mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 07:51:31 -04:00
USB: cdc-acm: Add quirks for Yoga Book 9 14IAH10 INGENIC touchscreen
The Lenovo Yoga Book 9 14IAH10 (83KJ) has a composite USB device (17EF:6161) that controls both touchscreens via a CDC ACM interface. Interface 0 is a standard CDC ACM control interface, but interface 1 (the data interface) incorrectly declares vendor-specific class (0xFF) instead of USB_CLASS_CDC_DATA. cdc-acm rejects the device at probe with -EINVAL, leaving interface 0 unbound and EP 0x82 never polled. With no consumer polling EP 0x82, the firmware's watchdog fires every ~20 seconds and resets the USB bus, producing a continuous disconnect/ reconnect loop that prevents the touchscreens from ever initialising. Add two new quirk flags: VENDOR_CLASS_DATA_IFACE: Bypasses the bInterfaceClass check in acm_probe() that would otherwise reject the vendor-class data interface with -EINVAL. ALWAYS_POLL_CTRL: Submits the notification URB at probe() rather than waiting for a TTY open. This keeps EP 0x82 polled at all times, permanently suppressing the firmware watchdog. The URB is resubmitted after port_shutdown() and on system resume. SET_CONTROL_LINE_STATE (DTR|RTS) is sent at probe and after port_shutdown() to complete firmware handshake. Note: the firmware performs exactly 4 USB connect/disconnect cycles (~19 s each) on every cold boot before stabilising. This is a fixed firmware property; touch is available ~75-80 s after power-on. Signed-off-by: Dave Carey <carvsdriver@gmail.com> Cc: stable <stable@kernel.org> Tested-by: Dave Carey <carvsdriver@gmail.com> Acked-by: Oliver Neukum <oneukum@suse.com> Link: https://patch.msgid.link/20260402182950.389016-1-carvsdriver@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
609865ab3d
commit
f58752ebcb
@@ -114,6 +114,8 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value,
|
||||
int retval;
|
||||
|
||||
retval = usb_autopm_get_interface(acm->control);
|
||||
#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */
|
||||
#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
@@ -710,12 +712,14 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
|
||||
set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
|
||||
acm->control->needs_remote_wakeup = 1;
|
||||
|
||||
acm->ctrlurb->dev = acm->dev;
|
||||
retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL);
|
||||
if (retval) {
|
||||
dev_err(&acm->control->dev,
|
||||
"%s - usb_submit_urb(ctrl irq) failed\n", __func__);
|
||||
goto error_submit_urb;
|
||||
if (!(acm->quirks & ALWAYS_POLL_CTRL)) {
|
||||
acm->ctrlurb->dev = acm->dev;
|
||||
retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL);
|
||||
if (retval) {
|
||||
dev_err(&acm->control->dev,
|
||||
"%s - usb_submit_urb(ctrl irq) failed\n", __func__);
|
||||
goto error_submit_urb;
|
||||
}
|
||||
}
|
||||
|
||||
acm_tty_set_termios(tty, NULL);
|
||||
@@ -788,6 +792,14 @@ static void acm_port_shutdown(struct tty_port *port)
|
||||
|
||||
acm_unpoison_urbs(acm);
|
||||
|
||||
if (acm->quirks & ALWAYS_POLL_CTRL) {
|
||||
acm->ctrlurb->dev = acm->dev;
|
||||
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL))
|
||||
dev_dbg(&acm->control->dev,
|
||||
"ctrl polling restart failed after port close\n");
|
||||
/* port_shutdown() cleared DTR/RTS; restore them */
|
||||
acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS);
|
||||
}
|
||||
}
|
||||
|
||||
static void acm_tty_cleanup(struct tty_struct *tty)
|
||||
@@ -1328,6 +1340,9 @@ static int acm_probe(struct usb_interface *intf,
|
||||
dev_dbg(&intf->dev,
|
||||
"Your device has switched interfaces.\n");
|
||||
swap(control_interface, data_interface);
|
||||
} else if (quirks & VENDOR_CLASS_DATA_IFACE) {
|
||||
dev_dbg(&intf->dev,
|
||||
"Vendor-specific data interface class, continuing.\n");
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1522,6 +1537,9 @@ static int acm_probe(struct usb_interface *intf,
|
||||
acm->line.bDataBits = 8;
|
||||
acm_set_line(acm, &acm->line);
|
||||
|
||||
if (quirks & ALWAYS_POLL_CTRL)
|
||||
acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS);
|
||||
|
||||
if (!acm->combined_interfaces) {
|
||||
rv = usb_driver_claim_interface(&acm_driver, data_interface, acm);
|
||||
if (rv)
|
||||
@@ -1543,6 +1561,13 @@ static int acm_probe(struct usb_interface *intf,
|
||||
|
||||
dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
|
||||
|
||||
if (acm->quirks & ALWAYS_POLL_CTRL) {
|
||||
acm->ctrlurb->dev = acm->dev;
|
||||
if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL))
|
||||
dev_warn(&intf->dev,
|
||||
"failed to start persistent ctrl polling\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_release_data_interface:
|
||||
@@ -1669,7 +1694,7 @@ static int acm_resume(struct usb_interface *intf)
|
||||
|
||||
acm_unpoison_urbs(acm);
|
||||
|
||||
if (tty_port_initialized(&acm->port)) {
|
||||
if (tty_port_initialized(&acm->port) || (acm->quirks & ALWAYS_POLL_CTRL)) {
|
||||
rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
|
||||
|
||||
for (;;) {
|
||||
@@ -2016,6 +2041,20 @@ static const struct usb_device_id acm_ids[] = {
|
||||
/* CH343 supports CAP_BRK, but doesn't advertise it */
|
||||
{ USB_DEVICE(0x1a86, 0x55d3), .driver_info = MISSING_CAP_BRK, },
|
||||
|
||||
/*
|
||||
* Lenovo Yoga Book 9 14IAH10 (83KJ) — INGENIC 17EF:6161 touchscreen
|
||||
* composite device. The CDC ACM control interface (0) uses a standard
|
||||
* Union descriptor, but the data interface (1) is declared as vendor-
|
||||
* specific class (0xff) with no CDC data descriptors, so cdc-acm would
|
||||
* normally reject it. The firmware also requires continuous polling of
|
||||
* the notification endpoint (EP 0x82) to suppress a 20-second watchdog
|
||||
* reset; ALWAYS_POLL_CTRL keeps the ctrlurb active even when no TTY is
|
||||
* open. Match only the control interface by class to avoid probing the
|
||||
* vendor-specific data interface.
|
||||
*/
|
||||
{ USB_DEVICE_INTERFACE_CLASS(0x17ef, 0x6161, USB_CLASS_COMM),
|
||||
.driver_info = VENDOR_CLASS_DATA_IFACE | ALWAYS_POLL_CTRL },
|
||||
|
||||
/* control interfaces without any protocol set */
|
||||
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
|
||||
USB_CDC_PROTO_NONE) },
|
||||
|
||||
Reference in New Issue
Block a user