📄 ftdi_sio.c
字号:
static int ftdi_chars_in_buffer (struct usb_serial_port *port){ /* ftdi_chars_in_buffer */ struct ftdi_private *priv = usb_get_serial_port_data(port); int buffered; unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); spin_lock_irqsave(&priv->tx_lock, flags); buffered = (int)priv->tx_outstanding_bytes; spin_unlock_irqrestore(&priv->tx_lock, flags); if (buffered < 0) { err("%s outstanding tx bytes is negative!", __FUNCTION__); buffered = 0; } return buffered;} /* ftdi_chars_in_buffer */static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs){ /* ftdi_read_bulk_callback */ struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct tty_struct *tty; struct ftdi_private *priv; if (urb->number_of_packets > 0) { err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__, urb->transfer_buffer_length, urb->actual_length, urb->number_of_packets ); err("%s transfer_flags %x ", __FUNCTION__,urb->transfer_flags ); } dbg("%s - port %d", __FUNCTION__, port->number); if (port->open_count <= 0) return; tty = port->tty; if (!tty) { dbg("%s - bad tty pointer - exiting",__FUNCTION__); return; } priv = usb_get_serial_port_data(port); if (!priv) { dbg("%s - bad port private data pointer - exiting", __FUNCTION__); return; } if (urb != port->read_urb) { err("%s - Not my urb!", __FUNCTION__); } if (urb->status) { /* This will happen at close every time so it is a dbg not an err */ dbg("(this is ok on close) nonzero read bulk status received: %d", urb->status); return; } ftdi_process_read(port);} /* ftdi_read_bulk_callback */static void ftdi_process_read (void *param){ /* ftdi_process_read */ struct usb_serial_port *port = (struct usb_serial_port*)param; struct urb *urb; struct tty_struct *tty; struct ftdi_private *priv; char error_flag; unsigned char *data; int i; int result; int need_flip; int packet_offset; unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); if (port->open_count <= 0) return; tty = port->tty; if (!tty) { dbg("%s - bad tty pointer - exiting",__FUNCTION__); return; } priv = usb_get_serial_port_data(port); if (!priv) { dbg("%s - bad port private data pointer - exiting", __FUNCTION__); return; } urb = port->read_urb; if (!urb) { dbg("%s - bad read_urb pointer - exiting", __FUNCTION__); return; } data = urb->transfer_buffer; if (priv->rx_processed) { dbg("%s - already processed: %d bytes, %d remain", __FUNCTION__, priv->rx_processed, urb->actual_length - priv->rx_processed); } else { /* The first two bytes of every read packet are status */ if (urb->actual_length > 2) { usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); } else { dbg("Status only: %03oo %03oo",data[0],data[1]); } } /* TO DO -- check for hung up line and handle appropriately: */ /* send hangup */ /* See acm.c - you do a tty_hangup - eg tty_hangup(tty) */ /* if CD is dropped and the line is not CLOCAL then we should hangup */ need_flip = 0; for (packet_offset = priv->rx_processed; packet_offset < urb->actual_length; packet_offset += PKTSZ) { int length; /* Compare new line status to the old one, signal if different */ /* N.B. packet may be processed more than once, but differences * are only processed once. */ if (priv != NULL) { char new_status = data[packet_offset+0] & FTDI_STATUS_B0_MASK; if (new_status != priv->prev_status) { priv->diff_status |= new_status ^ priv->prev_status; wake_up_interruptible(&priv->delta_msr_wait); priv->prev_status = new_status; } } length = min(PKTSZ, urb->actual_length-packet_offset)-2; if (length < 0) { err("%s - bad packet length: %d", __FUNCTION__, length+2); length = 0; } if (priv->rx_flags & THROTTLED) { dbg("%s - throttled", __FUNCTION__); break; } if (tty_buffer_request_room(tty, length) < length) { /* break out & wait for throttling/unthrottling to happen */ dbg("%s - receive room low", __FUNCTION__); break; } /* Handle errors and break */ error_flag = TTY_NORMAL; /* Although the device uses a bitmask and hence can have multiple */ /* errors on a packet - the order here sets the priority the */ /* error is returned to the tty layer */ if ( data[packet_offset+1] & FTDI_RS_OE ) { error_flag = TTY_OVERRUN; dbg("OVERRRUN error"); } if ( data[packet_offset+1] & FTDI_RS_BI ) { error_flag = TTY_BREAK; dbg("BREAK received"); } if ( data[packet_offset+1] & FTDI_RS_PE ) { error_flag = TTY_PARITY; dbg("PARITY error"); } if ( data[packet_offset+1] & FTDI_RS_FE ) { error_flag = TTY_FRAME; dbg("FRAMING error"); } if (length > 0) { for (i = 2; i < length+2; i++) { /* Note that the error flag is duplicated for every character received since we don't know which character it applied to */ tty_insert_flip_char(tty, data[packet_offset+i], error_flag); } need_flip = 1; }#ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW /* if a parity error is detected you get status packets forever until a character is sent without a parity error. This doesn't work well since the application receives a never ending stream of bad data - even though new data hasn't been sent. Therefore I (bill) have taken this out. However - this might make sense for framing errors and so on so I am leaving the code in for now. */ else { if (error_flag != TTY_NORMAL){ dbg("error_flag is not normal"); /* In this case it is just status - if that is an error send a bad character */ if(tty->flip.count >= TTY_FLIPBUF_SIZE) { tty_flip_buffer_push(tty); } tty_insert_flip_char(tty, 0xff, error_flag); need_flip = 1; } }#endif } /* "for(packet_offset=0..." */ /* Low latency */ if (need_flip) { tty_flip_buffer_push(tty); } if (packet_offset < urb->actual_length) { /* not completely processed - record progress */ priv->rx_processed = packet_offset; dbg("%s - incomplete, %d bytes processed, %d remain", __FUNCTION__, packet_offset, urb->actual_length - packet_offset); /* check if we were throttled while processing */ spin_lock_irqsave(&priv->rx_lock, flags); if (priv->rx_flags & THROTTLED) { priv->rx_flags |= ACTUALLY_THROTTLED; spin_unlock_irqrestore(&priv->rx_lock, flags); dbg("%s - deferring remainder until unthrottled", __FUNCTION__); return; } spin_unlock_irqrestore(&priv->rx_lock, flags); /* if the port is closed stop trying to read */ if (port->open_count > 0){ /* delay processing of remainder */ schedule_delayed_work(&priv->rx_work, 1); } else { dbg("%s - port is closed", __FUNCTION__); } return; } /* urb is completely processed */ priv->rx_processed = 0; /* if the port is closed stop trying to read */ if (port->open_count > 0){ /* Continue trying to always read */ usb_fill_bulk_urb(port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, ftdi_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); } return;} /* ftdi_process_read */static void ftdi_break_ctl( struct usb_serial_port *port, int break_state ){ struct ftdi_private *priv = usb_get_serial_port_data(port); __u16 urb_value = 0; char buf[1]; /* break_state = -1 to turn on break, and 0 to turn off break */ /* see drivers/char/tty_io.c to see it used */ /* last_set_data_urb_value NEVER has the break bit set in it */ if (break_state) { urb_value = priv->last_set_data_urb_value | FTDI_SIO_SET_BREAK; } else { urb_value = priv->last_set_data_urb_value; } if (usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, urb_value , priv->interface, buf, 0, WDR_TIMEOUT) < 0) { err("%s FAILED to enable/disable break state (state was %d)", __FUNCTION__,break_state); } dbg("%s break state is %d - urb is %d", __FUNCTION__,break_state, urb_value); }/* old_termios contains the original termios settings and tty->termios contains * the new setting to be used * WARNING: set_termios calls this with old_termios in kernel space */static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_termios){ /* ftdi_termios */ struct usb_device *dev = port->serial->dev; unsigned int cflag = port->tty->termios->c_cflag; struct ftdi_private *priv = usb_get_serial_port_data(port); __u16 urb_value; /* will hold the new flags */ char buf[1]; /* Perhaps I should dynamically alloc this? */ // Added for xon/xoff support unsigned int iflag = port->tty->termios->c_iflag; unsigned char vstop; unsigned char vstart; dbg("%s", __FUNCTION__); /* Force baud rate if this device requires it, unless it is set to B0. */ if (priv->force_baud && ((port->tty->termios->c_cflag & CBAUD) != B0)) { dbg("%s: forcing baud rate for this device", __FUNCTION__); port->tty->termios->c_cflag &= ~CBAUD; port->tty->termios->c_cflag |= priv->force_baud; } /* Force RTS-CTS if this device requires it. */ if (priv->force_rtscts) { dbg("%s: forcing rtscts for this device", __FUNCTION__); port->tty->termios->c_cflag |= CRTSCTS; } cflag = port->tty->termios->c_cflag; /* FIXME -For this cut I don't care if the line is really changing or not - so just do the change regardless - should be able to compare old_termios and tty->termios */ /* NOTE These routines can get interrupted by ftdi_sio_read_bulk_callback - need to examine what this means - don't see any problems yet */ /* Set number of data bits, parity, stop bits */ urb_value = 0; urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 : FTDI_SIO_SET_DATA_STOP_BITS_1); urb_value |= (cflag & PARENB ? (cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD : FTDI_SIO_SET_DATA_PARITY_EVEN) : FTDI_SIO_SET_DATA_PARITY_NONE); if (cflag & CSIZE) { switch (cflag & CSIZE) { case CS5: urb_value |= 5; dbg("Setting CS5"); break; case CS6: urb_value |= 6; dbg("Setting CS6"); break; case CS7: urb_value |= 7; dbg("Setting CS7"); break; case CS8: urb_value |= 8; dbg("Setting CS8"); break; default: err("CSIZE was set but not CS5-CS8"); } } /* This is needed by the break command since it uses the same command - but is * or'ed with this value */ priv->last_set_data_urb_value = urb_value; if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, urb_value , priv->interface, buf, 0, WDR_SHORT_TIMEOUT) < 0) { err("%s FAILED to set databits/stopbits/parity", __FUNCTION__); } /* Now do the baudrate */ if ((cflag & CBAUD) == B0 ) { /* Disable flow control */ if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), FTDI_SIO_SET_FLOW_CTRL_REQUEST, FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, 0, priv->interface, buf, 0, WDR_TIMEOUT) < 0) { err("%s error from disable flowcontrol urb", __FUNCTION__); } /* Drop RTS and DTR */ clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); } else { /* set the baudrate determined before */ if (change_speed(port)) { err("%s urb failed to set baudrate", __FUNCTION__); } /* Ensure RTS and DTR are raised when baudrate changed from 0 */ if ((old_termios->c_cflag & CB
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -