Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: serial: pl2303: account for deficits of clones

There are apparently incomplete clones of the HXD type chip in use.
Those return -EPIPE on GET_LINE_REQUEST and BREAK_REQUEST. Avoid
flooding the kernel log with those errors. Detect them during startup
and then use the line_settings cache instead of GET_LINE_REQUEST. Signal
missing break support via -ENOTTY.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
[ johan: fix macro prefix, drop oom error message ]
Signed-off-by: Johan Hovold <johan@kernel.org>

authored by

Jan Kiszka and committed by
Johan Hovold
40827729 59b723cd

+37 -1
+37 -1
drivers/usb/serial/pl2303.c
··· 31 31 #define PL2303_QUIRK_UART_STATE_IDX0 BIT(0) 32 32 #define PL2303_QUIRK_LEGACY BIT(1) 33 33 #define PL2303_QUIRK_ENDPOINT_HACK BIT(2) 34 + #define PL2303_QUIRK_NO_BREAK_GETLINE BIT(3) 34 35 35 36 static const struct usb_device_id id_table[] = { 36 37 { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID), ··· 468 467 return -ENODEV; 469 468 } 470 469 470 + static bool pl2303_is_hxd_clone(struct usb_serial *serial) 471 + { 472 + struct usb_device *udev = serial->dev; 473 + unsigned char *buf; 474 + int ret; 475 + 476 + buf = kmalloc(7, GFP_KERNEL); 477 + if (!buf) 478 + return false; 479 + 480 + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 481 + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, 482 + 0, 0, buf, 7, 100); 483 + 484 + kfree(buf); 485 + 486 + return ret == -EPIPE; 487 + } 488 + 471 489 static int pl2303_startup(struct usb_serial *serial) 472 490 { 473 491 struct pl2303_serial_private *spriv; ··· 508 488 spriv->type = &pl2303_type_data[type]; 509 489 spriv->quirks = (unsigned long)usb_get_serial_data(serial); 510 490 spriv->quirks |= spriv->type->quirks; 491 + 492 + if (type == TYPE_HXD && pl2303_is_hxd_clone(serial)) 493 + spriv->quirks |= PL2303_QUIRK_NO_BREAK_GETLINE; 511 494 512 495 usb_set_serial_data(serial, spriv); 513 496 ··· 748 725 static int pl2303_get_line_request(struct usb_serial_port *port, 749 726 unsigned char buf[7]) 750 727 { 751 - struct usb_device *udev = port->serial->dev; 728 + struct usb_serial *serial = port->serial; 729 + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); 730 + struct usb_device *udev = serial->dev; 752 731 int ret; 732 + 733 + if (spriv->quirks & PL2303_QUIRK_NO_BREAK_GETLINE) { 734 + struct pl2303_private *priv = usb_get_serial_port_data(port); 735 + 736 + memcpy(buf, priv->line_settings, 7); 737 + return 0; 738 + } 753 739 754 740 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 755 741 GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, ··· 1096 1064 static int pl2303_set_break(struct usb_serial_port *port, bool enable) 1097 1065 { 1098 1066 struct usb_serial *serial = port->serial; 1067 + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); 1099 1068 u16 state; 1100 1069 int result; 1070 + 1071 + if (spriv->quirks & PL2303_QUIRK_NO_BREAK_GETLINE) 1072 + return -ENOTTY; 1101 1073 1102 1074 if (enable) 1103 1075 state = BREAK_ON;