aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/rc/ir-raw.c7
-rw-r--r--drivers/media/rc/rc-main.c265
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)