📄 ftdi_sio.c
字号:
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; } /* have to make sure we don't overflow the buffer with tty_insert_flip_char's */ if (tty->flip.count+length > TTY_FLIPBUF_SIZE) { tty_flip_buffer_push(tty); need_flip = 0; if (tty->flip.count != 0) { /* flip didn't work, this happens when ftdi_process_read() is * called from ftdi_unthrottle, because TTY_DONT_FLIP is set */ dbg("%s - flip buffer push failed", __FUNCTION__); break; } } if (priv->rx_flags & THROTTLED) { dbg("%s - throttled", __FUNCTION__); break; } if (tty->ldisc.receive_room(tty)-tty->flip.count < 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 & CBAUD) == B0) { set_mctrl(port, TIOCM_DTR | TIOCM_RTS); } } /* Set flow control */ /* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */ if (cflag & CRTSCTS) { dbg("%s Setting to CRTSCTS flow control", __FUNCTION__); if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), FTDI_SIO_SET_FLOW_CTRL_REQUEST, FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, 0 , (FTDI_SIO_RTS_CTS_HS | priv->interface), buf, 0, WDR_TIMEOUT) < 0) { err("urb failed to set to rts/cts flow control"); } } else { /* * Xon/Xoff code * * Check the IXOFF status in the iflag component of the termios structure * if IXOFF is not set, the pre-xon/xoff code is executed. */ if (iflag & IXOFF) { dbg("%s request to enable xonxoff iflag=%04x",__FUNCTION__,iflag); // Try to enable the XON/XOFF on the ftdi_sio // Set the vstart and vstop -- could have been done up above where // a lot of other dereferencing is done but that would be very // inefficient as vstart and vstop are not always needed vstart=port->tty->termios->c_cc[VSTART]; vstop=port->tty->termios->c_cc[VSTOP]; urb_value=(vstop << 8) | (vstart); if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), FTDI_SIO_SET_FLOW_CTRL_REQUEST, FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, urb_value , (FTDI_SIO_XON_XOFF_HS | priv->interface), buf, 0, WDR_TIMEOUT) < 0) { err("urb failed to set to xon/xoff flow control"); } } else { /* else clause to only run if cfag ! CRTSCTS and iflag ! XOFF */ /* CHECKME Assuming XON/XOFF handled by tty stack - not by device */ dbg("%s Turning off hardware flow control", __FUNCTION__); 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("urb failed to clear flow control"); } } } return;} /* ftdi_termios */static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file){ struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned char buf[2]; int ret; dbg("%s TIOCMGET", __FUNCTION__); switch (priv->chip_type) { case SIO: /* Request the status from the device */ if ((ret = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0), FTDI_SIO_GET_MODEM_STATUS_REQUEST, FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -