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/hid-picolcd_fb.c | |
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/hid-picolcd_fb.c')
-rw-r--r-- | drivers/hid/hid-picolcd_fb.c | 41 |
1 files changed, 25 insertions, 16 deletions
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; |