V4L/DVB: IR/imon: protect ictx's kc and last_keycode w/spinlock
authorJarod Wilson <jarod@redhat.com>
Wed, 15 Sep 2010 18:56:03 +0000 (15:56 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Thu, 21 Oct 2010 03:06:07 +0000 (01:06 -0200)
Lest we get our keycodes wrong... Thus far, in practice, I've not found
it to actually matter, but its one of the issues raised in
https://bugzilla.kernel.org/show_bug.cgi?id=16351 that wasn't addressed
by converting to using native IR keydown/up functions.

Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/IR/imon.c

index d36fe7239782b001229495f369b64e9a292ef9ae..4b73b8eaf7dcc5a70fe918fb9fd83a00691c927b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   imon.c:   input and display driver for SoundGraph iMON IR/VFD/LCD
  *
- *   Copyright(C) 2009  Jarod Wilson <jarod@wilsonet.com>
+ *   Copyright(C) 2010  Jarod Wilson <jarod@wilsonet.com>
  *   Portions based on the original lirc_imon driver,
  *     Copyright(C) 2004  Venky Raju(dev@venky.ws)
  *
@@ -125,6 +125,7 @@ struct imon_context {
        struct input_dev *idev;         /* input device for panel & IR mouse */
        struct input_dev *touch;        /* input device for touchscreen */
 
+       spinlock_t kc_lock;             /* make sure we get keycodes right */
        u32 kc;                         /* current input keycode */
        u32 last_keycode;               /* last reported input keycode */
        u32 rc_scancode;                /* the computed remote scancode */
@@ -1210,6 +1211,9 @@ static bool imon_mouse_event(struct imon_context *ictx,
        u8 right_shift = 1;
        bool mouse_input = true;
        int dir = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ictx->kc_lock, flags);
 
        /* newer iMON device PAD or mouse button */
        if (ictx->product != 0xffdc && (buf[0] & 0x01) && len == 5) {
@@ -1241,6 +1245,8 @@ static bool imon_mouse_event(struct imon_context *ictx,
        } else
                mouse_input = false;
 
+       spin_unlock_irqrestore(&ictx->kc_lock, flags);
+
        if (mouse_input) {
                dev_dbg(ictx->dev, "sending mouse data via input subsystem\n");
 
@@ -1255,7 +1261,9 @@ static bool imon_mouse_event(struct imon_context *ictx,
                                         buf[1] >> right_shift & 0x1);
                }
                input_sync(ictx->idev);
+               spin_lock_irqsave(&ictx->kc_lock, flags);
                ictx->last_keycode = ictx->kc;
+               spin_unlock_irqrestore(&ictx->kc_lock, flags);
        }
 
        return mouse_input;
@@ -1278,6 +1286,7 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
        char rel_x = 0x00, rel_y = 0x00;
        u16 timeout, threshold;
        u32 scancode = KEY_RESERVED;
+       unsigned long flags;
 
        /*
         * The imon directional pad functions more like a touchpad. Bytes 3 & 4
@@ -1301,7 +1310,11 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
                                dir = stabilize((int)rel_x, (int)rel_y,
                                                timeout, threshold);
                                if (!dir) {
+                                       spin_lock_irqsave(&ictx->kc_lock,
+                                                         flags);
                                        ictx->kc = KEY_UNKNOWN;
+                                       spin_unlock_irqrestore(&ictx->kc_lock,
+                                                              flags);
                                        return;
                                }
                                buf[2] = dir & 0xFF;
@@ -1363,7 +1376,9 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
                        dir = stabilize((int)rel_x, (int)rel_y,
                                        timeout, threshold);
                        if (!dir) {
+                               spin_lock_irqsave(&ictx->kc_lock, flags);
                                ictx->kc = KEY_UNKNOWN;
+                               spin_unlock_irqrestore(&ictx->kc_lock, flags);
                                return;
                        }
                        buf[2] = dir & 0xFF;
@@ -1392,8 +1407,11 @@ static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
                }
        }
 
-       if (scancode)
+       if (scancode) {
+               spin_lock_irqsave(&ictx->kc_lock, flags);
                ictx->kc = imon_remote_key_lookup(ictx, scancode);
+               spin_unlock_irqrestore(&ictx->kc_lock, flags);
+       }
 }
 
 /**
@@ -1405,6 +1423,9 @@ static int imon_parse_press_type(struct imon_context *ictx,
                                 unsigned char *buf, u8 ktype)
 {
        int press_type = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ictx->kc_lock, flags);
 
        /* key release of 0x02XXXXXX key */
        if (ictx->kc == KEY_RESERVED && buf[0] == 0x02 && buf[3] == 0x00)
@@ -1437,6 +1458,8 @@ static int imon_parse_press_type(struct imon_context *ictx,
        else
                press_type = 1;
 
+       spin_unlock_irqrestore(&ictx->kc_lock, flags);
+
        return press_type;
 }
 
@@ -1449,6 +1472,7 @@ static void imon_incoming_packet(struct imon_context *ictx,
        int len = urb->actual_length;
        unsigned char *buf = urb->transfer_buffer;
        struct device *dev = ictx->dev;
+       unsigned long flags;
        u32 kc;
        bool norelease = false;
        int i;
@@ -1486,6 +1510,7 @@ static void imon_incoming_packet(struct imon_context *ictx,
                }
        }
 
+       spin_lock_irqsave(&ictx->kc_lock, flags);
        /* keyboard/mouse mode toggle button */
        if (kc == KEY_KEYBOARD && !ictx->release_code) {
                ictx->last_keycode = kc;
@@ -1493,6 +1518,7 @@ static void imon_incoming_packet(struct imon_context *ictx,
                        ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1;
                        dev_dbg(dev, "toggling to %s mode\n",
                                ictx->pad_mouse ? "mouse" : "keyboard");
+                       spin_unlock_irqrestore(&ictx->kc_lock, flags);
                        return;
                } else {
                        ictx->pad_mouse = 0;
@@ -1501,6 +1527,7 @@ static void imon_incoming_packet(struct imon_context *ictx,
        }
 
        ictx->kc = kc;
+       spin_unlock_irqrestore(&ictx->kc_lock, flags);
 
        /* send touchscreen events through input subsystem if touchpad data */
        if (ictx->display_type == IMON_DISPLAY_TYPE_VGA && len == 8 &&
@@ -1534,8 +1561,10 @@ static void imon_incoming_packet(struct imon_context *ictx,
        if (press_type < 0)
                goto not_input_data;
 
+       spin_lock_irqsave(&ictx->kc_lock, flags);
        if (ictx->kc == KEY_UNKNOWN)
                goto unknown_key;
+       spin_unlock_irqrestore(&ictx->kc_lock, flags);
 
        if (ktype != IMON_KEY_PANEL) {
                if (press_type == 0)
@@ -1543,33 +1572,43 @@ static void imon_incoming_packet(struct imon_context *ictx,
                else {
                        ir_keydown(ictx->rdev, ictx->rc_scancode,
                                   ictx->rc_toggle);
+                       spin_lock_irqsave(&ictx->kc_lock, flags);
                        ictx->last_keycode = ictx->kc;
+                       spin_unlock_irqrestore(&ictx->kc_lock, flags);
                }
                return;
        }
 
        /* Only panel type events left to process now */
+       spin_lock_irqsave(&ictx->kc_lock, flags);
+
        /* KEY_MUTE repeats from knob need to be suppressed */
        if (ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) {
                do_gettimeofday(&t);
                msec = tv2int(&t, &prev_time);
                prev_time = t;
-               if (msec < idev->rep[REP_DELAY])
+               if (msec < idev->rep[REP_DELAY]) {
+                       spin_unlock_irqrestore(&ictx->kc_lock, flags);
                        return;
+               }
        }
+       kc = ictx->kc;
+
+       spin_unlock_irqrestore(&ictx->kc_lock, flags);
 
-       input_report_key(idev, ictx->kc, press_type);
+       input_report_key(idev, kc, press_type);
        input_sync(idev);
 
        /* panel keys don't generate a release */
-       input_report_key(idev, ictx->kc, 0);
+       input_report_key(idev, kc, 0);
        input_sync(idev);
 
-       ictx->last_keycode = ictx->kc;
+       ictx->last_keycode = kc;
 
        return;
 
 unknown_key:
+       spin_unlock_irqrestore(&ictx->kc_lock, flags);
        dev_info(dev, "%s: unknown keypress, code 0x%llx\n", __func__,
                 (long long)scancode);
        return;
@@ -1927,6 +1966,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
        }
 
        mutex_init(&ictx->lock);
+       spin_lock_init(&ictx->kc_lock);
 
        mutex_lock(&ictx->lock);