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

Configure Feed

Select the types of activity you want to include in your feed.

Input: bcm5974 - recover from failed mode switch

Mode switches sent before control response are ignored. This results in
an unresponsive trackpad and "bcm5974: bad trackpad package, length: 8"
repeated in logs.

On receiving unknown 8-byte packets, assume that mode switch was ignored
and schedule an asynchronous mode reset. The reset will switch the
device to normal mode, wait, then switch back to wellspring mode.

Signed-off-by: Liam Mitchell <mitchell.liam@gmail.com>
Link: https://lore.kernel.org/linux-input/CAOQ1CL4+DP1TuLAGNsz5GdFBTHvnTg=5q=Dr2Z1OQc6RXydSYA@mail.gmail.com/
Acked-by: Henrik Rydberg <rydberg@bitmath.org>
Link: https://patch.msgid.link/20260213-bcm5974-reset-v2-1-1837851336b0@gmail.com
Cc: stable@vger.kernel.org
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Liam Mitchell and committed by
Dmitry Torokhov
fc1e8a6f 273a171d

+41 -1
+41 -1
drivers/input/mouse/bcm5974.c
··· 286 286 const struct tp_finger *index[MAX_FINGERS]; /* finger index data */ 287 287 struct input_mt_pos pos[MAX_FINGERS]; /* position array */ 288 288 int slots[MAX_FINGERS]; /* slot assignments */ 289 + struct work_struct mode_reset_work; 290 + unsigned long last_mode_reset; 289 291 }; 290 292 291 293 /* trackpad finger block data, le16-aligned */ ··· 698 696 return retval; 699 697 } 700 698 699 + /* 700 + * Mode switches sent before the control response are ignored. 701 + * Fixing this state requires switching to normal mode and waiting 702 + * about 1ms before switching back to wellspring mode. 703 + */ 704 + static void bcm5974_mode_reset_work(struct work_struct *work) 705 + { 706 + struct bcm5974 *dev = container_of(work, struct bcm5974, mode_reset_work); 707 + int error; 708 + 709 + guard(mutex)(&dev->pm_mutex); 710 + dev->last_mode_reset = jiffies; 711 + 712 + error = bcm5974_wellspring_mode(dev, false); 713 + if (error) { 714 + dev_err(&dev->intf->dev, "reset to normal mode failed\n"); 715 + return; 716 + } 717 + 718 + fsleep(1000); 719 + 720 + error = bcm5974_wellspring_mode(dev, true); 721 + if (error) 722 + dev_err(&dev->intf->dev, "mode switch after reset failed\n"); 723 + } 724 + 701 725 static void bcm5974_irq_button(struct urb *urb) 702 726 { 703 727 struct bcm5974 *dev = urb->context; ··· 780 752 if (dev->tp_urb->actual_length == 2) 781 753 goto exit; 782 754 783 - if (report_tp_state(dev, dev->tp_urb->actual_length)) 755 + if (report_tp_state(dev, dev->tp_urb->actual_length)) { 784 756 dprintk(1, "bcm5974: bad trackpad package, length: %d\n", 785 757 dev->tp_urb->actual_length); 758 + 759 + /* 760 + * Receiving a HID packet means we aren't in wellspring mode. 761 + * If we haven't tried a reset in the last second, try now. 762 + */ 763 + if (dev->tp_urb->actual_length == 8 && 764 + time_after(jiffies, dev->last_mode_reset + msecs_to_jiffies(1000))) { 765 + schedule_work(&dev->mode_reset_work); 766 + } 767 + } 786 768 787 769 exit: 788 770 error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC); ··· 944 906 dev->intf = iface; 945 907 dev->input = input_dev; 946 908 dev->cfg = *cfg; 909 + INIT_WORK(&dev->mode_reset_work, bcm5974_mode_reset_work); 947 910 mutex_init(&dev->pm_mutex); 948 911 949 912 /* setup urbs */ ··· 1037 998 { 1038 999 struct bcm5974 *dev = usb_get_intfdata(iface); 1039 1000 1001 + disable_work_sync(&dev->mode_reset_work); 1040 1002 usb_set_intfdata(iface, NULL); 1041 1003 1042 1004 input_unregister_device(dev->input);