* 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)
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;
if (tmp != buf)
tmp--;
*tmp = '\n';
+
+ mutex_unlock(&dev->lock);
+
return tmp + 1 - buf;
}
* 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,
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) {
}
if (i == ARRAY_SIZE(proto_names)) {
IR_dprintk(1, "Unknown protocol: '%s'\n", tmp);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
count++;
}
if (!count) {
IR_dprintk(1, "Protocol not specified\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (dev->change_protocol) {
if (rc < 0) {
IR_dprintk(1, "Error setting protocols to 0x%llx\n",
(long long)type);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
}
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)
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;
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)
if (rc < 0)
goto out_input;
}
+ mutex_unlock(&dev->lock);
if (dev->change_protocol) {
rc = dev->change_protocol(dev, rc_map->rc_type);
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);
* @driver_name: name of the hardware driver which registered this device
* @map_name: name of the default keymap
* @rc_map: current scan/key table
+ * @lock: used to ensure we've filled in all protocol details before
+ * anyone can call show_protocols or store_protocols
* @devno: unique remote control device number
* @raw: additional data for raw pulse/space devices
* @input_dev: the input child device used to communicate events to userspace
- * @driver_type: specifies if protocol decoding is done in hardware or software
+ * @driver_type: specifies if protocol decoding is done in hardware or software
* @idle: used to keep track of RX state
* @allowed_protos: bitmask with the supported RC_TYPE_* protocols
* @scanmask: some hardware decoders are not capable of providing the full
struct input_id input_id;
char *driver_name;
const char *map_name;
- struct rc_map rc_map;
+ struct rc_map rc_map;
+ struct mutex lock;
unsigned long devno;
struct ir_raw_event_ctrl *raw;
struct input_dev *input_dev;