USB: serial: cp210x: add support for line-status events
Subsystems (2)
Johan Hovold
maintainercover USB: serial: cp210x: add support for line-status events
This series add supports for line-status events that specifically can be used to detect parity errors.
The implementation relies on the device's event-insertion mode, which can also be used to detect line-breaks and modem status changes. But as this doesn't seem to work as expected on CP2102, support has been left unimplemented for now.
Included are also a couple of cleanups.
Johan
Changes since RFC [1]:
- drop sysrq support which relies on break detection
- use 0xec (instead of 0xff) as escape character
[1] https://lore.kernel.org/r/20200703150104.GE3453@localhost
Johan Hovold (5):
USB: serial: cp210x: disable interface on errors in open
USB: serial: cp210x: add support for line-status events
USB: serial: cp210x: add support for TIOCGICOUNT
USB: serial: cp210x: drop unnecessary packed attributes
USB: serial: cp210x: use in-kernel types in port data
drivers/usb/serial/cp210x.c | 209 ++++++++++++++++++++++++++++++++++-- 1 file changed, 201 insertions(+), 8 deletions(-)
Signature · 1 line
2.26.2
Johan Hovold
maintainerPATCH 1/5 USB: serial: cp210x: disable interface on errors in open
Try to disable the serial interface in the unlikely event that generic open() fails.
--- drivers/usb/serial/cp210x.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
drivers/usb/serial/cp210x.c +10 -1
@@ -824,7 +824,16 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)if (tty)cp210x_change_speed (tty, port,NULL ); -return usb_serial_generic_open (tty, port); + result =usb_serial_generic_open (tty, port); +if (result) + goto err_disable; + +return 0 ; + +err_disable: +cp210x_write_u16_reg (port,CP210X_IFC_ENABLE ,UART_DISABLE ); + +return result; }static void cp210x_close (struct usb_serial_port *port)
Signature · 1 line
2.26.2
Johan Hovold
maintainerPATCH 2/5 USB: serial: cp210x: add support for line-status events
In reply to Johan HovoldAdd support for line-status events that specifically can be used to detect and report parity errors.
Enable the device's event-insertion mode whenever input-parity checking is requested. This will insert line and modem status events into the data stream.
Note that modem-status changes appear to be buffered until a character is received (at least on CP2102) and support is therefore left unimplemented.
On at least one type of these chips (CP2102), line breaks are not reported as expected either (regardless of whether SERIAL_BREAK_CHAR is set) so do not enable event-mode when !IGNBRK is requested for now.
--- drivers/usb/serial/cp210x.c | 189 +++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 3 deletions(-)
drivers/usb/serial/cp210x.c +186 -3
@@ -50,6 +50,9 @@ static void cp210x_release(struct usb_serial *);static int cp210x_port_probe (struct usb_serial_port *);static int cp210x_port_remove (struct usb_serial_port *);static void cp210x_dtr_rts (struct usb_serial_port *p,int on); +static void cp210x_process_read_urb (struct urb *urb); +static void cp210x_enable_event_mode (struct usb_serial_port *port); +static void cp210x_disable_event_mode (struct usb_serial_port *port);static const struct usb_device_id id_table[] = { {USB_DEVICE (0x045B ,0x0053 ) },/* Renesas RX610 RX-Stick */
@@ -253,9 +256,21 @@ struct cp210x_serial_private {bool use_actual_rate; }; +enum cp210x_event_state { +ES_DATA , +ES_ESCAPE , +ES_LSR , +ES_LSR_DATA_0 , +ES_LSR_DATA_1 , +ES_MSR +}; +struct cp210x_port_private {__u8 bInterfaceNumber;bool has_swapped_line_ctl; +bool event_mode; +enum cp210x_event_state event_state; +u8 lsr; };static struct usb_serial_driver cp210x_device = {
@@ -281,7 +296,8 @@ static struct usb_serial_driver cp210x_device = { .release = cp210x_release, .port_probe = cp210x_port_probe, .port_remove = cp210x_port_remove, - .dtr_rts = cp210x_dtr_rts + .dtr_rts = cp210x_dtr_rts, + .process_read_urb = cp210x_process_read_urb, };static struct usb_serial_driver *const serial_drivers[] = {
@@ -403,6 +419,15 @@ struct cp210x_comm_status {*/ #define PURGE_ALL 0x000f +/* CP210X_EMBED_EVENTS */ +#define CP210X_ESCCHAR 0xec + +#define CP210X_LSR_OVERRUN BIT(1) +#define CP210X_LSR_PARITY BIT(2) +#define CP210X_LSR_FRAME BIT(3) +#define CP210X_LSR_BREAK BIT(4) + +/* CP210X_GET_FLOW/CP210X_SET_FLOW read/write these 0x10 bytes */ struct cp210x_flow_ctl {__le32 ulControlHandshake;
@@ -809,6 +834,7 @@ static int cp210x_get_line_ctl(struct usb_serial_port *port, u16 *ctl)static int cp210x_open (struct tty_struct *tty,struct usb_serial_port *port) { +struct cp210x_port_private *port_priv =usb_get_serial_port_data (port);int result; result =cp210x_write_u16_reg (port,CP210X_IFC_ENABLE ,UART_ENABLE );
@@ -820,10 +846,14 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port)/* Configure the termios structure */ cp210x_get_termios (tty, port); -/* The baud rate must be initialised on cp2104 */ -if (tty) +if (tty) { +/* The baud rate must be initialised on cp2104 */ cp210x_change_speed (tty, port,NULL ); +if (I_INPCK (tty)) +cp210x_enable_event_mode (port); + } + result =usb_serial_generic_open (tty, port);if (result) goto err_disable;
@@ -832,18 +862,128 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) err_disable:cp210x_write_u16_reg (port,CP210X_IFC_ENABLE ,UART_DISABLE ); + port_priv->event_mode = false;return result; }static void cp210x_close (struct usb_serial_port *port) { +struct cp210x_port_private *port_priv =usb_get_serial_port_data (port); +usb_serial_generic_close (port);/* Clear both queues; cp2108 needs this to avoid an occasional hang */ cp210x_write_u16_reg (port,CP210X_PURGE ,PURGE_ALL );cp210x_write_u16_reg (port,CP210X_IFC_ENABLE ,UART_DISABLE ); + +/* Disabling the interface disables event-insertion mode. */ + port_priv->event_mode = false; +} + +static void cp210x_process_lsr (struct usb_serial_port *port,unsigned char lsr,char *flag) +{ +if (lsr &CP210X_LSR_BREAK ) { + port->icount.brk++; + *flag =TTY_BREAK ; + }else if (lsr &CP210X_LSR_PARITY ) { + port->icount.parity++; + *flag =TTY_PARITY ; + }else if (lsr &CP210X_LSR_FRAME ) { + port->icount.frame++; + *flag =TTY_FRAME ; + } + +if (lsr &CP210X_LSR_OVERRUN ) { + port->icount.overrun++; +tty_insert_flip_char (&port->port,0 ,TTY_OVERRUN ); + } +} + +static bool cp210x_process_char (struct usb_serial_port *port,unsigned char *ch,char *flag) +{ +struct cp210x_port_private *port_priv =usb_get_serial_port_data (port); + +switch (port_priv->event_state) { +case ES_DATA : +if (*ch ==CP210X_ESCCHAR ) { + port_priv->event_state =ES_ESCAPE ; +break ; + } +return false; +case ES_ESCAPE : +switch (*ch) { +case 0 : +dev_dbg (&port->dev,"%s - escape char\n" , __func__); + *ch =CP210X_ESCCHAR ; + port_priv->event_state =ES_DATA ; +return false; +case 1 : + port_priv->event_state =ES_LSR_DATA_0 ; +break ; +case 2 : + port_priv->event_state =ES_LSR ; +break ; +case 3 : + port_priv->event_state =ES_MSR ; +break ; +default : +dev_err (&port->dev,"malformed event 0x%02x\n" , *ch); + port_priv->event_state =ES_DATA ; +break ; + } +break ; +case ES_LSR_DATA_0 : + port_priv->lsr = *ch; + port_priv->event_state =ES_LSR_DATA_1 ; +break ; +case ES_LSR_DATA_1 : +dev_dbg (&port->dev,"%s - lsr = 0x%02x, data = 0x%02x\n" , + __func__, port_priv->lsr, *ch); +cp210x_process_lsr (port, port_priv->lsr, flag); + port_priv->event_state =ES_DATA ; +return false; +case ES_LSR : +dev_dbg (&port->dev,"%s - lsr = 0x%02x\n" , __func__, *ch); + port_priv->lsr = *ch; +cp210x_process_lsr (port, port_priv->lsr, flag); + port_priv->event_state =ES_DATA ; +break ; +case ES_MSR : +dev_dbg (&port->dev,"%s - msr = 0x%02x\n" , __func__, *ch); +/* unimplemented */ + port_priv->event_state =ES_DATA ; +break ; + } + +return true; +} + +static void cp210x_process_read_urb (struct urb *urb) +{ +struct usb_serial_port *port = urb->context; +struct cp210x_port_private *port_priv =usb_get_serial_port_data (port); +unsigned char *ch = urb->transfer_buffer; +char flag; +int i; + +if (!urb->actual_length) +return ; + +if (port_priv->event_mode) { +for (i =0 ; i < urb->actual_length; i++, ch++) { + flag =TTY_NORMAL ; + +if (cp210x_process_char (port, ch, &flag)) +continue ; + +tty_insert_flip_char (&port->port, *ch, flag); + } + }else { +tty_insert_flip_string (&port->port, ch, urb->actual_length); + } +tty_flip_buffer_push (&port->port); }/*
@@ -1176,6 +1316,41 @@ static void cp210x_change_speed(struct tty_struct *tty,tty_encode_baud_rate (tty, baud, baud); } +static void cp210x_enable_event_mode (struct usb_serial_port *port) +{ +struct cp210x_port_private *port_priv =usb_get_serial_port_data (port); +int ret; + +if (port_priv->event_mode) +return ; + + port_priv->event_state =ES_DATA ; + port_priv->event_mode = true; + + ret =cp210x_write_u16_reg (port,CP210X_EMBED_EVENTS ,CP210X_ESCCHAR ); +if (ret) { +dev_err (&port->dev,"failed to enable events: %d\n" , ret); + port_priv->event_mode = false; + } +} + +static void cp210x_disable_event_mode (struct usb_serial_port *port) +{ +struct cp210x_port_private *port_priv =usb_get_serial_port_data (port); +int ret; + +if (!port_priv->event_mode) +return ; + + ret =cp210x_write_u16_reg (port,CP210X_EMBED_EVENTS ,0 ); +if (ret) { +dev_err (&port->dev,"failed to disable events: %d\n" , ret); +return ; + } + + port_priv->event_mode = false; +} +static void cp210x_set_termios (struct tty_struct *tty,struct usb_serial_port *port,struct ktermios *old_termios) {
@@ -1298,6 +1473,14 @@ static void cp210x_set_termios(struct tty_struct *tty, sizeof(flow_ctl)); } +/* +* Enable event-insertion mode only if input parity checking is +* enabled for now. +*/ +if (I_INPCK (tty)) +cp210x_enable_event_mode (port); +else +cp210x_disable_event_mode (port); } static int cp210x_tiocmset(struct tty_struct *tty,
Signature · 1 line
2.26.2
Johan Hovold
maintainerPATCH 3/5 USB: serial: cp210x: add support for TIOCGICOUNT
In reply to Johan HovoldEnable TIOCGICOUNT to allow reading out the (unused) interrupt counters and error statistics.
Note that modem-status events are currently left unimplemented as they appear to be buffered on at least CP2102 and therefore cannot be used to implement TIOCMIWAIT.
--- drivers/usb/serial/cp210x.c | 1 + 1 file changed, 1 insertion(+)
drivers/usb/serial/cp210x.c +1 -0
@@ -291,6 +291,7 @@ static struct usb_serial_driver cp210x_device = { .unthrottle = usb_serial_generic_unthrottle, .tiocmget = cp210x_tiocmget, .tiocmset = cp210x_tiocmset, + .get_icount = usb_serial_generic_get_icount, .attach = cp210x_attach, .disconnect = cp210x_disconnect, .release = cp210x_release,
Signature · 1 line
2.26.2
Johan Hovold
maintainerPATCH 4/5 USB: serial: cp210x: drop unnecessary packed attributes
In reply to Johan HovoldDrop unnecessary packed attributes from structs without padding.
--- drivers/usb/serial/cp210x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/usb/serial/cp210x.c +3 -3
@@ -435,7 +435,7 @@ struct cp210x_flow_ctl {__le32 ulFlowReplace;__le32 ulXonLimit;__le32 ulXoffLimit; -} __packed; +};/* cp210x_flow_ctl::ulControlHandshake */ #define CP210X_SERIAL_DTR_MASK GENMASK(1, 0)
@@ -469,7 +469,7 @@ struct cp210x_flow_ctl {struct cp210x_pin_mode {u8 eci;u8 sci; -} __packed; +};#define CP210X_PIN_MODE_MODEM 0#define CP210X_PIN_MODE_GPIO BIT(0)
@@ -532,7 +532,7 @@ struct cp210x_single_port_config {struct cp210x_gpio_write {u8 mask;u8 state; -} __packed; +};/* * Helper to get interface number when we only have struct usb_serial.
Signature · 1 line
2.26.2
Johan Hovold
maintainerPATCH 5/5 USB: serial: cp210x: use in-kernel types in port data
In reply to Johan HovoldThe port data is not exported to user space so use the in-kernel u8 type.
--- drivers/usb/serial/cp210x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/usb/serial/cp210x.c +1 -1
@@ -266,7 +266,7 @@ enum cp210x_event_state { };struct cp210x_port_private { -__u8 bInterfaceNumber; +u8 bInterfaceNumber;bool has_swapped_line_ctl;bool event_mode;enum cp210x_event_state event_state;
Signature · 1 line
2.26.2
Quoted 10 lines · Johan Hovold
This series add supports for line-status events that specifically can be used to detect parity errors.
The implementation relies on the device's event-insertion mode, which can also be used to detect line-breaks and modem status changes. But as this doesn't seem to work as expected on CP2102, support has been left unimplemented for now.
Included are also a couple of cleanups.
Quoted 7 lines
Johan
Changes since RFC [1]:
- drop sysrq support which relies on break detection
- use0xec(instead of0xff) as escape character[1] https://lore.kernel.org/r/20200703150104.GE3453@localhost
Johan Hovold
maintainerQuoted 13 lines · Greg Kroah-Hartman
Quoted 12 lines · Johan Hovold
This series add supports for line-status events that specifically can be used to detect parity errors.
The implementation relies on the device's event-insertion mode, which can also be used to detect line-breaks and modem status changes. But as this doesn't seem to work as expected on CP2102, support has been left unimplemented for now.
Included are also a couple of cleanups.
Johan
Quoted 8 lines
Quoted 5 lines
Changes since RFC [1]:
- drop sysrq support which relies on break detection
- use0xec(instead of0xff) as escape character[1] https://lore.kernel.org/r/20200703150104.GE3453@localhost
Reviewed-by Greg Kroah-Hartman <[email protected]>
Thanks. Now applied.
Johan