diff options
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/rc/ir-raw.c | 7 | ||||
-rw-r--r-- | drivers/media/rc/rc-main.c | 265 |
2 files changed, 151 insertions, 121 deletions
diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 763c9d131d0f..c08718e17671 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -240,6 +240,12 @@ ir_raw_get_allowed_protocols(void) return protocols; } +static int change_protocol(struct rc_dev *dev, u64 *rc_type) +{ + /* the caller will update dev->enabled_protocols */ + return 0; +} + /* * Used to (un)register raw event clients */ @@ -257,6 +263,7 @@ int ir_raw_event_register(struct rc_dev *dev) dev->raw->dev = dev; rc_set_enabled_protocols(dev, ~0); + dev->change_protocol = change_protocol; rc = kfifo_alloc(&dev->raw->kfifo, sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE, GFP_KERNEL); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 9abcf77246fa..d5bfe6442459 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -830,7 +830,7 @@ struct rc_filter_attribute { /** * show_protocols() - shows the current/wakeup 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). @@ -856,20 +856,21 @@ static ssize_t show_protocols(struct device *device, mutex_lock(&dev->lock); - enabled = dev->enabled_protocols[fattr->type]; - if (dev->driver_type == RC_DRIVER_SCANCODE || - fattr->type == RC_FILTER_WAKEUP) - allowed = dev->allowed_protocols[fattr->type]; - else if (dev->raw) - allowed = ir_raw_get_allowed_protocols(); - else { - mutex_unlock(&dev->lock); - return -ENODEV; + if (fattr->type == RC_FILTER_NORMAL) { + enabled = dev->enabled_protocols[RC_FILTER_NORMAL]; + if (dev->raw) + allowed = ir_raw_get_allowed_protocols(); + else + allowed = dev->allowed_protocols[RC_FILTER_NORMAL]; + } else { + enabled = dev->enabled_protocols[RC_FILTER_WAKEUP]; + allowed = dev->allowed_protocols[RC_FILTER_WAKEUP]; } - IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n", - (long long)allowed, - (long long)enabled); + mutex_unlock(&dev->lock); + + IR_dprintk(1, "%s: allowed - 0x%llx, enabled - 0x%llx\n", + __func__, (long long)allowed, (long long)enabled); for (i = 0; i < ARRAY_SIZE(proto_names); i++) { if (allowed & enabled & proto_names[i].type) @@ -885,62 +886,29 @@ static ssize_t show_protocols(struct device *device, tmp--; *tmp = '\n'; - mutex_unlock(&dev->lock); - return tmp + 1 - buf; } /** - * store_protocols() - changes the current/wakeup IR protocol(s) - * @device: the device descriptor - * @mattr: the device attribute struct (unused) - * @buf: a pointer to the input buffer - * @len: length of the input buffer + * parse_protocol_change() - parses a protocol change request + * @protocols: pointer to the bitmask of current protocols + * @buf: pointer to the buffer with a list of changes * - * This routine is for changing the IR protocol type. - * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]protocols. - * Writing "+proto" will add a protocol to the list of enabled protocols. - * Writing "-proto" will remove a protocol from the list of enabled protocols. + * Writing "+proto" will add a protocol to the protocol mask. + * Writing "-proto" will remove a protocol from protocol mask. * Writing "proto" will enable only "proto". * 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. + * Returns the number of changes performed or a negative error code. */ -static ssize_t store_protocols(struct device *device, - struct device_attribute *mattr, - const char *data, - size_t len) +static int parse_protocol_change(u64 *protocols, const char *buf) { - struct rc_dev *dev = to_rc_dev(device); - struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr); - bool enable, disable; const char *tmp; - u64 old_type, type; + unsigned count = 0; + bool enable, disable; u64 mask; - int rc, i, count = 0; - ssize_t ret; - int (*change_protocol)(struct rc_dev *dev, u64 *rc_type); - int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter); - struct rc_scancode_filter local_filter, *filter; - - /* Device is being removed */ - if (!dev) - return -EINVAL; - - mutex_lock(&dev->lock); - - if (dev->driver_type != RC_DRIVER_SCANCODE && !dev->raw) { - IR_dprintk(1, "Protocol switching not supported\n"); - ret = -EINVAL; - goto out; - } - old_type = dev->enabled_protocols[fattr->type]; - type = old_type; + int i; - while ((tmp = strsep((char **) &data, " \n")) != NULL) { + while ((tmp = strsep((char **)&buf, " \n")) != NULL) { if (!*tmp) break; @@ -966,76 +934,124 @@ static ssize_t store_protocols(struct device *device, if (i == ARRAY_SIZE(proto_names)) { IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); - ret = -EINVAL; - goto out; + return -EINVAL; } count++; if (enable) - type |= mask; + *protocols |= mask; else if (disable) - type &= ~mask; + *protocols &= ~mask; else - type = mask; + *protocols = mask; } if (!count) { IR_dprintk(1, "Protocol not specified\n"); - ret = -EINVAL; + return -EINVAL; + } + + return count; +} + +/** + * store_protocols() - changes the current/wakeup IR protocol(s) + * @device: the device descriptor + * @mattr: the device attribute struct + * @buf: a pointer to the input buffer + * @len: length of the input buffer + * + * This routine is for changing the IR protocol type. + * It is trigged by writing to /sys/class/rc/rc?/[wakeup_]protocols. + * See parse_protocol_change() for the valid commands. + * Returns @len on success or a negative error code. + * + * 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, + const char *buf, size_t len) +{ + struct rc_dev *dev = to_rc_dev(device); + struct rc_filter_attribute *fattr = to_rc_filter_attr(mattr); + u64 *current_protocols; + int (*change_protocol)(struct rc_dev *dev, u64 *rc_type); + struct rc_scancode_filter *filter; + int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter); + u64 old_protocols, new_protocols; + ssize_t rc; + + /* Device is being removed */ + if (!dev) + return -EINVAL; + + if (fattr->type == RC_FILTER_NORMAL) { + IR_dprintk(1, "Normal protocol change requested\n"); + current_protocols = &dev->enabled_protocols[RC_FILTER_NORMAL]; + change_protocol = dev->change_protocol; + filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + set_filter = dev->s_filter; + } else { + IR_dprintk(1, "Wakeup protocol change requested\n"); + current_protocols = &dev->enabled_protocols[RC_FILTER_WAKEUP]; + change_protocol = dev->change_wakeup_protocol; + filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + set_filter = dev->s_wakeup_filter; + } + + if (!change_protocol) { + IR_dprintk(1, "Protocol switching not supported\n"); + return -EINVAL; + } + + mutex_lock(&dev->lock); + + old_protocols = *current_protocols; + new_protocols = old_protocols; + rc = parse_protocol_change(&new_protocols, buf); + if (rc < 0) + goto out; + + rc = change_protocol(dev, &new_protocols); + if (rc < 0) { + IR_dprintk(1, "Error setting protocols to 0x%llx\n", + (long long)new_protocols); goto out; } - change_protocol = (fattr->type == RC_FILTER_NORMAL) - ? dev->change_protocol : dev->change_wakeup_protocol; - if (change_protocol) { - rc = change_protocol(dev, &type); - if (rc < 0) { - IR_dprintk(1, "Error setting protocols to 0x%llx\n", - (long long)type); - ret = -EINVAL; - goto out; - } + if (new_protocols == old_protocols) { + rc = len; + goto out; } - dev->enabled_protocols[fattr->type] = type; - IR_dprintk(1, "Current protocol(s): 0x%llx\n", - (long long)type); + *current_protocols = new_protocols; + IR_dprintk(1, "Protocols changed to 0x%llx\n", (long long)new_protocols); /* * If the protocol is changed the filter needs updating. * Try setting the same filter with the new protocol (if any). * Fall back to clearing the filter. */ - filter = &dev->scancode_filters[fattr->type]; - set_filter = (fattr->type == RC_FILTER_NORMAL) - ? dev->s_filter : dev->s_wakeup_filter; - - if (set_filter && old_type != type && filter->mask) { - local_filter = *filter; - if (!type) { - /* no protocol => clear filter */ - ret = -1; - } else { - /* hardware filtering => try setting, otherwise clear */ - ret = set_filter(dev, &local_filter); - } - if (ret < 0) { - /* clear the filter */ - local_filter.data = 0; - local_filter.mask = 0; - set_filter(dev, &local_filter); - } + if (set_filter && filter->mask) { + if (new_protocols) + rc = set_filter(dev, filter); + else + rc = -1; - /* commit the new filter */ - *filter = local_filter; + if (rc < 0) { + filter->data = 0; + filter->mask = 0; + set_filter(dev, filter); + } } - ret = len; + rc = len; out: mutex_unlock(&dev->lock); - return ret; + return rc; } /** @@ -1061,20 +1077,23 @@ static ssize_t show_filter(struct device *device, { struct rc_dev *dev = to_rc_dev(device); struct rc_filter_attribute *fattr = to_rc_filter_attr(attr); + struct rc_scancode_filter *filter; u32 val; /* Device is being removed */ if (!dev) return -EINVAL; + if (fattr->type == RC_FILTER_NORMAL) + filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + else + filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + mutex_lock(&dev->lock); - if ((fattr->type == RC_FILTER_NORMAL && !dev->s_filter) || - (fattr->type == RC_FILTER_WAKEUP && !dev->s_wakeup_filter)) - val = 0; - else if (fattr->mask) - val = dev->scancode_filters[fattr->type].mask; + if (fattr->mask) + val = filter->mask; else - val = dev->scancode_filters[fattr->type].data; + val = filter->data; mutex_unlock(&dev->lock); return sprintf(buf, "%#x\n", val); @@ -1101,15 +1120,15 @@ static ssize_t show_filter(struct device *device, */ static ssize_t store_filter(struct device *device, struct device_attribute *attr, - const char *buf, - size_t count) + const char *buf, size_t len) { struct rc_dev *dev = to_rc_dev(device); struct rc_filter_attribute *fattr = to_rc_filter_attr(attr); - struct rc_scancode_filter local_filter, *filter; + struct rc_scancode_filter new_filter, *filter; int ret; unsigned long val; int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter); + u64 *enabled_protocols; /* Device is being removed */ if (!dev) @@ -1119,38 +1138,42 @@ static ssize_t store_filter(struct device *device, if (ret < 0) return ret; - /* Can the scancode filter be set? */ - set_filter = (fattr->type == RC_FILTER_NORMAL) ? dev->s_filter : - dev->s_wakeup_filter; + if (fattr->type == RC_FILTER_NORMAL) { + set_filter = dev->s_filter; + enabled_protocols = &dev->enabled_protocols[RC_FILTER_NORMAL]; + filter = &dev->scancode_filters[RC_FILTER_NORMAL]; + } else { + set_filter = dev->s_wakeup_filter; + enabled_protocols = &dev->enabled_protocols[RC_FILTER_WAKEUP]; + filter = &dev->scancode_filters[RC_FILTER_WAKEUP]; + } + if (!set_filter) return -EINVAL; mutex_lock(&dev->lock); - /* Tell the driver about the new filter */ - filter = &dev->scancode_filters[fattr->type]; - local_filter = *filter; + new_filter = *filter; if (fattr->mask) - local_filter.mask = val; + new_filter.mask = val; else - local_filter.data = val; + new_filter.data = val; - if (!dev->enabled_protocols[fattr->type] && local_filter.mask) { + if (!*enabled_protocols && val) { /* refuse to set a filter unless a protocol is enabled */ ret = -EINVAL; goto unlock; } - ret = set_filter(dev, &local_filter); + ret = set_filter(dev, &new_filter); if (ret < 0) goto unlock; - /* Success, commit the new filter */ - *filter = local_filter; + *filter = new_filter; unlock: mutex_unlock(&dev->lock); - return (ret < 0) ? ret : count; + return (ret < 0) ? ret : len; } static void rc_dev_release(struct device *device) |