|
@@ -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);
|