This patch adds support for two files in sysfs controlling the wakeup (scan)codes that are written to hardware. * /sys/class/rc/rc?/wakeup_protocols: This file has the same syntax as rc protocols file (/sys/class/rc/rc?/protocols). When read it displays the wakeup protocols the underlying IR driver supports. Protocol can be changed by writing it to the file. The active protocol defines how actual wakeup_code is to be interpreted. Note: Only one protocol can be active at a time. * /sys/class/rc/rc?/wakeup_codes: This file contains the currently active wakeup (scan)code(s). New values can be written to activate a new wakeup code. The contents of the wakeup_code file are simply white space separated values. Note: Protocol "other" has a special meaning: if activated then wakeup_codes file will contain raw IR samples. Positive values represent pulses and negative values spaces. How to read: cat /sys/class/rc/rc?/wakeup_codes How to write: echo "rc-6" > /sys/class/rc/rc?/wakeup_protocols echo "0xd1ab" > /sys/class/rc/rc?/wakeup_codes Signed-off-by: Antti Seppälä <a.seppala@xxxxxxxxx> --- drivers/media/rc/rc-main.c | 179 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 165 insertions(+), 14 deletions(-) diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 02e2f38..cde17e2 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -797,7 +797,7 @@ static struct { /** * show_protocols() - shows the current IR protocol(s) * @device: the device descriptor - * @mattr: the device attribute struct (unused) + * @mattr: the device attribute struct * @buf: a pointer to the output buffer * * This routine is a callback routine for input read the IR protocol type(s). @@ -822,14 +822,19 @@ static ssize_t show_protocols(struct device *device, mutex_lock(&dev->lock); - enabled = dev->enabled_protocols; - if (dev->driver_type == RC_DRIVER_SCANCODE) - allowed = dev->allowed_protos; - else if (dev->raw) - allowed = ir_raw_get_allowed_protocols(); - else { - mutex_unlock(&dev->lock); - return -ENODEV; + if (strcmp(mattr->attr.name, "wakeup_protocols") == 0) { + enabled = dev->enabled_wake_protos; + allowed = dev->allowed_wake_protos; + } else { + enabled = dev->enabled_protocols; + if (dev->driver_type == RC_DRIVER_SCANCODE) + allowed = dev->allowed_protos; + else if (dev->raw) + allowed = ir_raw_get_allowed_protocols(); + else { + mutex_unlock(&dev->lock); + return -ENODEV; + } } IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n", @@ -858,7 +863,7 @@ static ssize_t show_protocols(struct device *device, /** * store_protocols() - changes the current IR protocol(s) * @device: the device descriptor - * @mattr: the device attribute struct (unused) + * @mattr: the device attribute struct * @buf: a pointer to the input buffer * @len: length of the input buffer * @@ -884,7 +889,7 @@ static ssize_t store_protocols(struct device *device, const char *tmp; u64 type; u64 mask; - int rc, i, count = 0; + int rc, i, wake = 0, count = 0, enablecount = 0; ssize_t ret; /* Device is being removed */ @@ -898,7 +903,13 @@ static ssize_t store_protocols(struct device *device, ret = -EINVAL; goto out; } - type = dev->enabled_protocols; + + if (strcmp(mattr->attr.name, "wakeup_protocols") == 0) { + wake = 1; + type = dev->enabled_wake_protos; + } else { + type = dev->enabled_protocols; + } while ((tmp = strsep((char **) &data, " \n")) != NULL) { if (!*tmp) @@ -920,6 +931,8 @@ static ssize_t store_protocols(struct device *device, for (i = 0; i < ARRAY_SIZE(proto_names); i++) { if (!strcasecmp(tmp, proto_names[i].name)) { mask = proto_names[i].type; + if (wake && (enable || (!enable && !disable))) + enablecount++; break; } } @@ -946,7 +959,7 @@ static ssize_t store_protocols(struct device *device, goto out; } - if (dev->change_protocol) { + if (dev->change_protocol && !wake) { rc = dev->change_protocol(dev, &type); if (rc < 0) { IR_dprintk(1, "Error setting protocols to 0x%llx\n", @@ -956,7 +969,16 @@ static ssize_t store_protocols(struct device *device, } } - dev->enabled_protocols = type; + if (wake && enablecount > 1) { + IR_dprintk(1, "Only one wake protocol can be enabled " + "at a time\n"); + ret = -EINVAL; + goto out; + } else if (wake) { + dev->enabled_wake_protos = type; + } else { + dev->enabled_protocols = type; + } IR_dprintk(1, "Current protocol(s): 0x%llx\n", (long long)type); @@ -967,6 +989,121 @@ out: return ret; } +/** + * show_wakeup_codes() - shows the current IR wake code(s) + * @device: the device descriptor + * @mattr: the device attribute struct (unused) + * @buf: a pointer to the output buffer + * + * This routine is a callback routine for input read the IR wake code(s). + * it is trigged by reading /sys/class/rc/rc?/wakeup_codes. + * It returns the currently active IR wake code or empty buffer if wake + * code is not active. + * + * dev->lock is taken to guard against races between device + * registration, store_wakeup_codes and show_wakeup_codes. + */ +static ssize_t show_wakeup_codes(struct device *device, + struct device_attribute *mattr, char *buf) +{ + int ret, pos = 0; + struct rc_wakeup_code *code, *next; + LIST_HEAD(wakeup_code_list); + struct rc_dev *dev = to_rc_dev(device); + + if (!dev || !dev->s_wakeup_codes) + return -ENODEV; + + mutex_lock(&dev->lock); + + ret = dev->s_wakeup_codes(dev, &wakeup_code_list, 0); + + list_for_each_entry_safe(code, next, &wakeup_code_list, list_item) { + if (dev->enabled_wake_protos & RC_BIT_OTHER) + pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", + code->value); + else + pos += scnprintf(buf + pos, PAGE_SIZE - pos, "0x%x ", + code->value); + list_del(&code->list_item); + kfree(code); + } + pos += scnprintf(buf + pos, PAGE_SIZE - pos, "\n"); + + mutex_unlock(&dev->lock); + + if (ret < 0) + return ret; + + return pos; +} + +/** + * store_wakeup_codes() - changes the current IR wake code(s) + * @device: the device descriptor + * @mattr: the device attribute struct (unused) + * @buf: a pointer to the input buffer + * @len: length of the input buffer + * + * This routine is for changing the IR wake code. + * It is trigged by writing to /sys/class/rc/rc?/wakeup_codes. + * Writing bytes separated by white space will pass them to the hardware. + * Writing "" (empty) will clear active wake code. + * Returns -EINVAL if too many values or invalid values were used + * otherwise @len. + * + * dev->lock is taken to guard against races between device + * registration, store_wakeup_codes and show_wakeup_codes. + */ +static ssize_t store_wakeup_codes(struct device *device, + struct device_attribute *mattr, + const char *data, + size_t len) +{ + int ret = 0; + char *tmp; + long value; + struct rc_wakeup_code *code, *next; + LIST_HEAD(wakeup_code_list); + struct rc_dev *dev = to_rc_dev(device); + + if (!dev || !dev->s_wakeup_codes) + return -ENODEV; + + mutex_lock(&dev->lock); + + while ((tmp = strsep((char **) &data, " ,\t\n")) != NULL) { + if (!*tmp) + break; + + ret = kstrtol(tmp, 0, &value); + if (ret < 0) { + IR_dprintk(1, "Error parsing value of %s", tmp); + break; + } + + code = kmalloc(sizeof(struct rc_wakeup_code), GFP_KERNEL); + if (!code) { + ret = -ENOMEM; + break; + } + code->value = value; + list_add_tail(&code->list_item, &wakeup_code_list); + } + + if (!ret) + ret = dev->s_wakeup_codes(dev, &wakeup_code_list, 1); + + list_for_each_entry_safe(code, next, &wakeup_code_list, list_item) { + list_del(&code->list_item); + kfree(code); + } + + mutex_unlock(&dev->lock); + + return ret ? ret : len; +} + static void rc_dev_release(struct device *device) { } @@ -998,6 +1135,10 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env) */ static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR, show_protocols, store_protocols); +static DEVICE_ATTR(wakeup_protocols, S_IRUGO | S_IWUSR, + show_protocols, store_protocols); +static DEVICE_ATTR(wakeup_codes, S_IRUGO | S_IWUSR, + show_wakeup_codes, store_wakeup_codes); static struct attribute *rc_dev_attrs[] = { &dev_attr_protocols.attr, @@ -1175,6 +1316,16 @@ int rc_register_device(struct rc_dev *dev) dev->enabled_protocols = rc_type; } + /* Create sysfs entry only if device has wake code support */ + if (dev->s_wakeup_codes) { + rc = device_create_file(&dev->dev, &dev_attr_wakeup_codes); + if (rc < 0) + goto out_raw; + rc = device_create_file(&dev->dev, &dev_attr_wakeup_protocols); + if (rc < 0) + goto out_raw; + } + mutex_unlock(&dev->lock); IR_dprintk(1, "Registered rc%ld (driver: %s, remote: %s, mode %s)\n", -- 1.8.3.2 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html