diff options
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/rc/rc-main.c | 47 |
1 files changed, 41 insertions, 6 deletions
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index a2706648e36..0d4fcd911b8 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -749,6 +749,9 @@ static struct { * it is trigged by reading /sys/class/rc/rc?/protocols. * It returns the protocol names of supported protocols. * Enabled protocols are printed in brackets. + * + * dev->lock is taken to guard against races between device + * registration, store_protocols and show_protocols. */ static ssize_t show_protocols(struct device *device, struct device_attribute *mattr, char *buf) @@ -762,6 +765,8 @@ static ssize_t show_protocols(struct device *device, if (!dev) return -EINVAL; + mutex_lock(&dev->lock); + if (dev->driver_type == RC_DRIVER_SCANCODE) { enabled = dev->rc_map.rc_type; allowed = dev->allowed_protos; @@ -784,6 +789,9 @@ static ssize_t show_protocols(struct device *device, if (tmp != buf) tmp--; *tmp = '\n'; + + mutex_unlock(&dev->lock); + return tmp + 1 - buf; } @@ -802,6 +810,9 @@ static ssize_t show_protocols(struct device *device, * Writing "none" will disable all protocols. * Returns -EINVAL if an invalid protocol combination or unknown protocol name * is used, otherwise @len. + * + * dev->lock is taken to guard against races between device + * registration, store_protocols and show_protocols. */ static ssize_t store_protocols(struct device *device, struct device_attribute *mattr, @@ -815,18 +826,22 @@ static ssize_t store_protocols(struct device *device, u64 mask; int rc, i, count = 0; unsigned long flags; + ssize_t ret; /* Device is being removed */ if (!dev) return -EINVAL; + mutex_lock(&dev->lock); + if (dev->driver_type == RC_DRIVER_SCANCODE) type = dev->rc_map.rc_type; else if (dev->raw) type = dev->raw->enabled_protocols; else { IR_dprintk(1, "Protocol switching not supported\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } while ((tmp = strsep((char **) &data, " \n")) != NULL) { @@ -860,7 +875,8 @@ static ssize_t store_protocols(struct device *device, } if (i == ARRAY_SIZE(proto_names)) { IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); - return -EINVAL; + ret = -EINVAL; + goto out; } count++; } @@ -875,7 +891,8 @@ static ssize_t store_protocols(struct device *device, if (!count) { IR_dprintk(1, "Protocol not specified\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } if (dev->change_protocol) { @@ -883,7 +900,8 @@ static ssize_t store_protocols(struct device *device, if (rc < 0) { IR_dprintk(1, "Error setting protocols to 0x%llx\n", (long long)type); - return -EINVAL; + ret = -EINVAL; + goto out; } } @@ -898,7 +916,11 @@ static ssize_t store_protocols(struct device *device, IR_dprintk(1, "Current protocol(s): 0x%llx\n", (long long)type); - return len; + ret = len; + +out: + mutex_unlock(&dev->lock); + return ret; } static void rc_dev_release(struct device *device) @@ -974,6 +996,7 @@ struct rc_dev *rc_allocate_device(void) spin_lock_init(&dev->rc_map.lock); spin_lock_init(&dev->keylock); + mutex_init(&dev->lock); setup_timer(&dev->timer_keyup, ir_timer_keyup, (unsigned long)dev); dev->dev.type = &rc_dev_type; @@ -1019,12 +1042,21 @@ int rc_register_device(struct rc_dev *dev) if (dev->close) dev->input_dev->close = ir_close; + /* + * Take the lock here, as the device sysfs node will appear + * when device_add() is called, which may trigger an ir-keytable udev + * rule, which will in turn call show_protocols and access either + * dev->rc_map.rc_type or dev->raw->enabled_protocols before it has + * been initialized. + */ + mutex_lock(&dev->lock); + dev->devno = (unsigned long)(atomic_inc_return(&devno) - 1); dev_set_name(&dev->dev, "rc%ld", dev->devno); dev_set_drvdata(&dev->dev, dev); rc = device_add(&dev->dev); if (rc) - return rc; + goto out_unlock; rc = ir_setkeytable(dev, rc_map); if (rc) @@ -1058,6 +1090,7 @@ int rc_register_device(struct rc_dev *dev) if (rc < 0) goto out_input; } + mutex_unlock(&dev->lock); if (dev->change_protocol) { rc = dev->change_protocol(dev, rc_map->rc_type); @@ -1083,6 +1116,8 @@ out_table: ir_free_table(&dev->rc_map); out_dev: device_del(&dev->dev); +out_unlock: + mutex_unlock(&dev->lock); return rc; } EXPORT_SYMBOL_GPL(rc_register_device); |