diff mbox

[RFC/RFT] Reset bcm5974 into wellspring mode when it forgets

Message ID 1363271407.4853.52.camel@i7.infradead.org (mailing list archive)
State New, archived
Headers show

Commit Message

David Woodhouse March 14, 2013, 2:30 p.m. UTC
Occasionally the trackpad in my MacBookPro 8,3 stops working, spewing
 'bcm5974: bad trackpad package, length: 8'

I haven't been able to reproduce this on demand, but it's annoying when
it does happen. I'm *assuming* that it's somehow forgotten what mode
it's supposed to be in. The fix for this on suspend/resume was to switch
it back to Wellspring mode again, so let's try that...

Untested, because my trackpad seems to *know* when I'm actually running
a kernel with this patch, and doesn't ever misbehave. I've played with
removing the *normal* mode switch in bcm5974_start_traffic() but can't
get it to produce the 'bad trackpad package' message at all in that
case, so the rest function doesn't get invoked.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>

Comments

Henrik Rydberg March 16, 2013, 7:31 p.m. UTC | #1
Hi David,

> Occasionally the trackpad in my MacBookPro 8,3 stops working, spewing
>  'bcm5974: bad trackpad package, length: 8'

We have seen this on a few exemplars over the years. It could be a
hardware problem.

> I haven't been able to reproduce this on demand, but it's annoying when
> it does happen. I'm *assuming* that it's somehow forgotten what mode
> it's supposed to be in. The fix for this on suspend/resume was to switch
> it back to Wellspring mode again, so let's try that...

What do you mean by "fix for this on suspend/resume"? The driver
always returns to normal mode at suspend, and sets wellspring mode at
resume.

> Untested, because my trackpad seems to *know* when I'm actually running
> a kernel with this patch, and doesn't ever misbehave. I've played with
> removing the *normal* mode switch in bcm5974_start_traffic() but can't
> get it to produce the 'bad trackpad package' message at all in that
> case, so the rest function doesn't get invoked.

Being random, it really sounds like faulty hardware. As such, perhaps
the problem can be detected in the usb layer?

> 
> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
> 
> diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
> index 2baff1b..db0b3ab 100644
> --- a/drivers/input/mouse/bcm5974.c
> +++ b/drivers/input/mouse/bcm5974.c
> @@ -244,6 +244,7 @@ struct bcm5974 {
>  	struct bt_data *bt_data;	/* button transferred data */
>  	struct urb *tp_urb;		/* trackpad usb request block */
>  	u8 *tp_data;			/* trackpad transferred data */
> +	struct work_struct reset_work;	/* reset to wellspring mode */
>  	const struct tp_finger *index[MAX_FINGERS];	/* finger index data */
>  	struct input_mt_pos pos[MAX_FINGERS];		/* position array */
>  	int slots[MAX_FINGERS];				/* slot assignments */
> @@ -676,9 +677,14 @@ static void bcm5974_irq_trackpad(struct urb *urb)
>  	if (dev->tp_urb->actual_length == 2)
>  		goto exit;
>  
> -	if (report_tp_state(dev, dev->tp_urb->actual_length))
> +	if (report_tp_state(dev, dev->tp_urb->actual_length)) {
>  		dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
>  			dev->tp_urb->actual_length);
> +		if (dev->tp_urb->actual_length == 8) {
> +			/* Hm. Make sure it's in wellspring mode... */
> +			schedule_work(&dev->reset_work);
> +		}
> +	}
>  
>  exit:
>  	error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
> @@ -815,6 +821,14 @@ static int bcm5974_resume(struct usb_interface *iface)
>  	return error;
>  }
>  
> +static void bcm5974_mode_workfn(struct work_struct *work)
> +{
> +	struct bcm5974 *dev = container_of(work, struct bcm5974, reset_work);
> +
> +	dev_info(&dev->intf->dev, "Reset into wellspring mode...\n");
> +	bcm5974_wellspring_mode(dev, true);
> +}
> +

This looks racy.

>  static int bcm5974_probe(struct usb_interface *iface,
>  			 const struct usb_device_id *id)
>  {
> @@ -840,6 +854,7 @@ static int bcm5974_probe(struct usb_interface *iface,
>  	dev->input = input_dev;
>  	dev->cfg = *cfg;
>  	mutex_init(&dev->pm_mutex);
> +	INIT_WORK(&dev->reset_work, bcm5974_mode_workfn);
>  
>  	/* setup urbs */
>  	if (cfg->tp_type == TYPE1) {
> @@ -936,6 +951,7 @@ static void bcm5974_disconnect(struct usb_interface *iface)
>  				  dev->bt_data, dev->bt_urb->transfer_dma);
>  	usb_free_urb(dev->tp_urb);
>  	usb_free_urb(dev->bt_urb);
> +	cancel_work_sync(&dev->reset_work);
>  	kfree(dev);
>  }
>  

In general, It does not really make sense for the transaction mode to
change under our feet without anything in the usb layer knowing about
it. Maybe there is a reset state cycle which does not get handle
properly in the driver?

Thanks,
Henrik

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Woodhouse March 17, 2013, 2:50 p.m. UTC | #2
On Sat, 2013-03-16 at 20:31 +0100, Henrik Rydberg wrote:
> What do you mean by "fix for this on suspend/resume"? The driver
> always returns to normal mode at suspend, and sets wellspring mode at
> resume.

Yes, that's exactly what I mean. And in the early days when the driver
didn't have a resume method, users were seeing exactly this 'bad
trackpad package, length: 8' message on resume. Adding the
suspend/resume methods fixed that — and that's why I'm assuming that a
switch back into wellspring mode is going to be sufficient to fix what
I'm seeing too. Unloading and reloading the module certainly is.

> > +static void bcm5974_mode_workfn(struct work_struct *work)
> > +{
> > +	struct bcm5974 *dev = container_of(work, struct bcm5974, reset_work);
> > +
> > +	dev_info(&dev->intf->dev, "Reset into wellspring mode...\n");
> > +	bcm5974_wellspring_mode(dev, true);
> > +}
> > +
> 
> This looks racy.

Racy with what? Oh, I see... we should probably move the
cancel_work_sync() from bcm5974_disconnect() to bcm5974_pause_traffic()?

> In general, It does not really make sense for the transaction mode to
> change under our feet without anything in the usb layer knowing about
> it.

If hardware always made sense, the world would be a much better place :)

>  Maybe there is a reset state cycle which does not get handle
> properly in the driver?

I don't believe so. A USB reset would end up with the bcm5974_probe()
method being called again, and everything would work fine. The device
may have reset its mode, but the USB bus doesn't seem to notice
anything. When it happens, there are no USB messages; it just starts
spewing the 'bad trackpad package' messages.
Henrik Rydberg March 19, 2013, 10:20 p.m. UTC | #3
Hi David,

> On Sat, 2013-03-16 at 20:31 +0100, Henrik Rydberg wrote:
> > What do you mean by "fix for this on suspend/resume"? The driver
> > always returns to normal mode at suspend, and sets wellspring mode at
> > resume.
> 
> Yes, that's exactly what I mean. And in the early days when the driver
> didn't have a resume method, users were seeing exactly this 'bad
> trackpad package, length: 8' message on resume. Adding the
> suspend/resume methods fixed that — and that's why I'm assuming that a
> switch back into wellspring mode is going to be sufficient to fix what
> I'm seeing too. Unloading and reloading the module certainly is.

The driver always had suspend/resume, but it is true that the EFI
booting did create a problem with the reset_resume method, so we went
back to rebinding the device. In addition to that problem, there has
been reports in the past that seem to indicate a few exemplars of
faulty hardware.

> 
> > > +static void bcm5974_mode_workfn(struct work_struct *work)
> > > +{
> > > +	struct bcm5974 *dev = container_of(work, struct bcm5974, reset_work);
> > > +
> > > +	dev_info(&dev->intf->dev, "Reset into wellspring mode...\n");
> > > +	bcm5974_wellspring_mode(dev, true);
> > > +}
> > > +
> > 
> > This looks racy.
> 
> Racy with what? Oh, I see... we should probably move the
> cancel_work_sync() from bcm5974_disconnect() to bcm5974_pause_traffic()?

I was thinking about the work function entering the wellspring_mode
function without the pm lock, but your observation seems correct, too :-)

> > In general, It does not really make sense for the transaction mode to
> > change under our feet without anything in the usb layer knowing about
> > it.
> 
> If hardware always made sense, the world would be a much better place :)

But it would be good to know why your machine is special. As you say,
the patch is not really tested, so we don't even know if it will fix
the problem.

> >  Maybe there is a reset state cycle which does not get handle
> > properly in the driver?
> 
> I don't believe so. A USB reset would end up with the bcm5974_probe()
> method being called again, and everything would work fine. The device
> may have reset its mode, but the USB bus doesn't seem to notice
> anything. When it happens, there are no USB messages; it just starts
> spewing the 'bad trackpad package' messages.

Ok, if you manage to hit the problem with a second version of this
patch, I will take it.

Thanks,
Henrik
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index 2baff1b..db0b3ab 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -244,6 +244,7 @@  struct bcm5974 {
 	struct bt_data *bt_data;	/* button transferred data */
 	struct urb *tp_urb;		/* trackpad usb request block */
 	u8 *tp_data;			/* trackpad transferred data */
+	struct work_struct reset_work;	/* reset to wellspring mode */
 	const struct tp_finger *index[MAX_FINGERS];	/* finger index data */
 	struct input_mt_pos pos[MAX_FINGERS];		/* position array */
 	int slots[MAX_FINGERS];				/* slot assignments */
@@ -676,9 +677,14 @@  static void bcm5974_irq_trackpad(struct urb *urb)
 	if (dev->tp_urb->actual_length == 2)
 		goto exit;
 
-	if (report_tp_state(dev, dev->tp_urb->actual_length))
+	if (report_tp_state(dev, dev->tp_urb->actual_length)) {
 		dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
 			dev->tp_urb->actual_length);
+		if (dev->tp_urb->actual_length == 8) {
+			/* Hm. Make sure it's in wellspring mode... */
+			schedule_work(&dev->reset_work);
+		}
+	}
 
 exit:
 	error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
@@ -815,6 +821,14 @@  static int bcm5974_resume(struct usb_interface *iface)
 	return error;
 }
 
+static void bcm5974_mode_workfn(struct work_struct *work)
+{
+	struct bcm5974 *dev = container_of(work, struct bcm5974, reset_work);
+
+	dev_info(&dev->intf->dev, "Reset into wellspring mode...\n");
+	bcm5974_wellspring_mode(dev, true);
+}
+
 static int bcm5974_probe(struct usb_interface *iface,
 			 const struct usb_device_id *id)
 {
@@ -840,6 +854,7 @@  static int bcm5974_probe(struct usb_interface *iface,
 	dev->input = input_dev;
 	dev->cfg = *cfg;
 	mutex_init(&dev->pm_mutex);
+	INIT_WORK(&dev->reset_work, bcm5974_mode_workfn);
 
 	/* setup urbs */
 	if (cfg->tp_type == TYPE1) {
@@ -936,6 +951,7 @@  static void bcm5974_disconnect(struct usb_interface *iface)
 				  dev->bt_data, dev->bt_urb->transfer_dma);
 	usb_free_urb(dev->tp_urb);
 	usb_free_urb(dev->bt_urb);
+	cancel_work_sync(&dev->reset_work);
 	kfree(dev);
 }