diff options
author | Bruno Prémont <bonbons@linux-vserver.org> | 2012-07-30 15:38:57 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2012-08-15 04:13:26 -0400 |
commit | a93ab8494873a88622bf74be861a93f875643524 (patch) | |
tree | 19f76ccf334ffb78b387b10c047df61f44d8fa07 /drivers/hid | |
parent | baacf9c5d2e631e7c940527ad670c8d4b019da81 (diff) |
HID: picoLCD: Improve unplug handling
Stop earlier attempting to submit new reports/URBs (though locking and
usbhid still prevents to bail out early enough to not produce multiple
hid-picolcd 0003:04D8:C002.0003: usb_submit_urb(out) failed: -19
messages in kernel log.
Strengthen framebuffer removal to be less racy.
Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/hid-picolcd_backlight.c | 3 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_core.c | 21 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_fb.c | 41 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_lcd.c | 3 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_leds.c | 3 |
5 files changed, 46 insertions, 25 deletions
diff --git a/drivers/hid/hid-picolcd_backlight.c b/drivers/hid/hid-picolcd_backlight.c index cd1ccd7205cd..b91f30945f9c 100644 --- a/drivers/hid/hid-picolcd_backlight.c +++ b/drivers/hid/hid-picolcd_backlight.c | |||
@@ -45,7 +45,8 @@ static int picolcd_set_brightness(struct backlight_device *bdev) | |||
45 | data->lcd_power = bdev->props.power; | 45 | data->lcd_power = bdev->props.power; |
46 | spin_lock_irqsave(&data->lock, flags); | 46 | spin_lock_irqsave(&data->lock, flags); |
47 | hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); | 47 | hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); |
48 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | 48 | if (!(data->status & PICOLCD_FAILED)) |
49 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
49 | spin_unlock_irqrestore(&data->lock, flags); | 50 | spin_unlock_irqrestore(&data->lock, flags); |
50 | return 0; | 51 | return 0; |
51 | } | 52 | } |
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index 8d5d341adeab..2d7ef688d66e 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c | |||
@@ -105,12 +105,17 @@ struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev, | |||
105 | hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0); | 105 | hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0); |
106 | k++; | 106 | k++; |
107 | } | 107 | } |
108 | data->pending = work; | 108 | if (data->status & PICOLCD_FAILED) { |
109 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | 109 | kfree(work); |
110 | spin_unlock_irqrestore(&data->lock, flags); | 110 | work = NULL; |
111 | wait_for_completion_interruptible_timeout(&work->ready, HZ*2); | 111 | } else { |
112 | spin_lock_irqsave(&data->lock, flags); | 112 | data->pending = work; |
113 | data->pending = NULL; | 113 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); |
114 | spin_unlock_irqrestore(&data->lock, flags); | ||
115 | wait_for_completion_interruptible_timeout(&work->ready, HZ*2); | ||
116 | spin_lock_irqsave(&data->lock, flags); | ||
117 | data->pending = NULL; | ||
118 | } | ||
114 | spin_unlock_irqrestore(&data->lock, flags); | 119 | spin_unlock_irqrestore(&data->lock, flags); |
115 | mutex_unlock(&data->mutex); | 120 | mutex_unlock(&data->mutex); |
116 | return work; | 121 | return work; |
@@ -235,6 +240,10 @@ int picolcd_reset(struct hid_device *hdev) | |||
235 | 240 | ||
236 | /* perform the reset */ | 241 | /* perform the reset */ |
237 | hid_set_field(report->field[0], 0, 1); | 242 | hid_set_field(report->field[0], 0, 1); |
243 | if (data->status & PICOLCD_FAILED) { | ||
244 | spin_unlock_irqrestore(&data->lock, flags); | ||
245 | return -ENODEV; | ||
246 | } | ||
238 | usbhid_submit_report(hdev, report, USB_DIR_OUT); | 247 | usbhid_submit_report(hdev, report, USB_DIR_OUT); |
239 | spin_unlock_irqrestore(&data->lock, flags); | 248 | spin_unlock_irqrestore(&data->lock, flags); |
240 | 249 | ||
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index 602786c18b72..4d8e22c73249 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c | |||
@@ -226,13 +226,16 @@ int picolcd_fb_reset(struct picolcd_data *data, int clear) | |||
226 | } | 226 | } |
227 | 227 | ||
228 | /* Update fb_vbitmap from the screen_base and send changed tiles to device */ | 228 | /* Update fb_vbitmap from the screen_base and send changed tiles to device */ |
229 | static void picolcd_fb_update(struct picolcd_data *data) | 229 | static void picolcd_fb_update(struct fb_info *info) |
230 | { | 230 | { |
231 | int chip, tile, n; | 231 | int chip, tile, n; |
232 | unsigned long flags; | 232 | unsigned long flags; |
233 | struct picolcd_data *data; | ||
233 | 234 | ||
235 | mutex_lock(&info->lock); | ||
236 | data = info->par; | ||
234 | if (!data) | 237 | if (!data) |
235 | return; | 238 | goto out; |
236 | 239 | ||
237 | spin_lock_irqsave(&data->lock, flags); | 240 | spin_lock_irqsave(&data->lock, flags); |
238 | if (!(data->status & PICOLCD_READY_FB)) { | 241 | if (!(data->status & PICOLCD_READY_FB)) { |
@@ -256,17 +259,31 @@ static void picolcd_fb_update(struct picolcd_data *data) | |||
256 | data->fb_bitmap, data->fb_bpp, chip, tile) || | 259 | data->fb_bitmap, data->fb_bpp, chip, tile) || |
257 | data->fb_force) { | 260 | data->fb_force) { |
258 | n += 2; | 261 | n += 2; |
259 | if (data->status & PICOLCD_FAILED) | ||
260 | return; /* device lost! */ | ||
261 | if (n >= HID_OUTPUT_FIFO_SIZE / 2) { | 262 | if (n >= HID_OUTPUT_FIFO_SIZE / 2) { |
263 | mutex_unlock(&info->lock); | ||
262 | usbhid_wait_io(data->hdev); | 264 | usbhid_wait_io(data->hdev); |
265 | mutex_lock(&info->lock); | ||
266 | data = info->par; | ||
267 | if (!data) | ||
268 | goto out; | ||
269 | spin_lock_irqsave(&data->lock, flags); | ||
270 | if (data->status & PICOLCD_FAILED) { | ||
271 | spin_unlock_irqrestore(&data->lock, flags); | ||
272 | goto out; | ||
273 | } | ||
274 | spin_unlock_irqrestore(&data->lock, flags); | ||
263 | n = 0; | 275 | n = 0; |
264 | } | 276 | } |
265 | picolcd_fb_send_tile(data->hdev, chip, tile); | 277 | picolcd_fb_send_tile(data->hdev, chip, tile); |
266 | } | 278 | } |
267 | data->fb_force = false; | 279 | data->fb_force = false; |
268 | if (n) | 280 | if (n) { |
281 | mutex_unlock(&info->lock); | ||
269 | usbhid_wait_io(data->hdev); | 282 | usbhid_wait_io(data->hdev); |
283 | return; | ||
284 | } | ||
285 | out: | ||
286 | mutex_unlock(&info->lock); | ||
270 | } | 287 | } |
271 | 288 | ||
272 | /* Stub to call the system default and update the image on the picoLCD */ | 289 | /* Stub to call the system default and update the image on the picoLCD */ |
@@ -327,17 +344,12 @@ static int picolcd_fb_blank(int blank, struct fb_info *info) | |||
327 | 344 | ||
328 | static void picolcd_fb_destroy(struct fb_info *info) | 345 | static void picolcd_fb_destroy(struct fb_info *info) |
329 | { | 346 | { |
330 | struct picolcd_data *data; | ||
331 | |||
332 | /* make sure no work is deferred */ | 347 | /* make sure no work is deferred */ |
333 | cancel_delayed_work_sync(&info->deferred_work); | 348 | fb_deferred_io_cleanup(info); |
334 | data = info->par; | ||
335 | info->par = NULL; | ||
336 | if (data) | ||
337 | data->fb_info = NULL; | ||
338 | 349 | ||
339 | vfree((u8 *)info->fix.smem_start); | 350 | vfree((u8 *)info->fix.smem_start); |
340 | framebuffer_release(info); | 351 | framebuffer_release(info); |
352 | printk(KERN_DEBUG "picolcd_fb_destroy(%p)\n", info); | ||
341 | } | 353 | } |
342 | 354 | ||
343 | static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | 355 | static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) |
@@ -425,7 +437,7 @@ static struct fb_ops picolcdfb_ops = { | |||
425 | /* Callback from deferred IO workqueue */ | 437 | /* Callback from deferred IO workqueue */ |
426 | static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist) | 438 | static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist) |
427 | { | 439 | { |
428 | picolcd_fb_update(info->par); | 440 | picolcd_fb_update(info); |
429 | } | 441 | } |
430 | 442 | ||
431 | static const struct fb_deferred_io picolcd_fb_defio = { | 443 | static const struct fb_deferred_io picolcd_fb_defio = { |
@@ -582,10 +594,7 @@ void picolcd_exit_framebuffer(struct picolcd_data *data) | |||
582 | return; | 594 | return; |
583 | 595 | ||
584 | device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); | 596 | device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); |
585 | mutex_lock(&info->lock); | ||
586 | fb_deferred_io_cleanup(info); | ||
587 | info->par = NULL; | 597 | info->par = NULL; |
588 | mutex_unlock(&info->lock); | ||
589 | unregister_framebuffer(info); | 598 | unregister_framebuffer(info); |
590 | data->fb_vbitmap = NULL; | 599 | data->fb_vbitmap = NULL; |
591 | data->fb_bitmap = NULL; | 600 | data->fb_bitmap = NULL; |
diff --git a/drivers/hid/hid-picolcd_lcd.c b/drivers/hid/hid-picolcd_lcd.c index 64a067fc837c..2d0ddc5ac65f 100644 --- a/drivers/hid/hid-picolcd_lcd.c +++ b/drivers/hid/hid-picolcd_lcd.c | |||
@@ -47,7 +47,8 @@ static int picolcd_set_contrast(struct lcd_device *ldev, int contrast) | |||
47 | data->lcd_contrast = contrast & 0x0ff; | 47 | data->lcd_contrast = contrast & 0x0ff; |
48 | spin_lock_irqsave(&data->lock, flags); | 48 | spin_lock_irqsave(&data->lock, flags); |
49 | hid_set_field(report->field[0], 0, data->lcd_contrast); | 49 | hid_set_field(report->field[0], 0, data->lcd_contrast); |
50 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | 50 | if (!(data->status & PICOLCD_FAILED)) |
51 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
51 | spin_unlock_irqrestore(&data->lock, flags); | 52 | spin_unlock_irqrestore(&data->lock, flags); |
52 | return 0; | 53 | return 0; |
53 | } | 54 | } |
diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c index 5573cd8b240b..28cb6a4f9634 100644 --- a/drivers/hid/hid-picolcd_leds.c +++ b/drivers/hid/hid-picolcd_leds.c | |||
@@ -54,7 +54,8 @@ void picolcd_leds_set(struct picolcd_data *data) | |||
54 | 54 | ||
55 | spin_lock_irqsave(&data->lock, flags); | 55 | spin_lock_irqsave(&data->lock, flags); |
56 | hid_set_field(report->field[0], 0, data->led_state); | 56 | hid_set_field(report->field[0], 0, data->led_state); |
57 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | 57 | if (!(data->status & PICOLCD_FAILED)) |
58 | usbhid_submit_report(data->hdev, report, USB_DIR_OUT); | ||
58 | spin_unlock_irqrestore(&data->lock, flags); | 59 | spin_unlock_irqrestore(&data->lock, flags); |
59 | } | 60 | } |
60 | 61 | ||