blob: 005621d067f0d24f69ee22a7357aa5b1a9b311e9 [file] [log] [blame]
David Härdemandd3f6162010-04-15 18:46:35 -03001/* ir-sysfs.c - sysfs interface for RC devices (/sys/class/rc)
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -03002 *
Mauro Carvalho Chehab995187b2010-03-24 20:47:53 -03003 * Copyright (C) 2009-2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -03004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation version 2 of the License.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090015#include <linux/slab.h>
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -030016#include <linux/input.h>
17#include <linux/device.h>
Mauro Carvalho Chehab3f113e32010-04-08 15:10:27 -030018#include "ir-core-priv.h"
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -030019
20#define IRRCV_NUM_DEVICES 256
21
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -030022/* bit array to represent IR sysfs device number */
23static unsigned long ir_core_dev_number;
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -030024
Mauro Carvalho Chehabe202c152010-03-26 22:45:16 -030025/* class for /sys/class/rc */
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -030026static char *ir_devnode(struct device *dev, mode_t *mode)
27{
Mauro Carvalho Chehabe202c152010-03-26 22:45:16 -030028 return kasprintf(GFP_KERNEL, "rc/%s", dev_name(dev));
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -030029}
30
Mauro Carvalho Chehab995187b2010-03-24 20:47:53 -030031static struct class ir_input_class = {
Mauro Carvalho Chehabe202c152010-03-26 22:45:16 -030032 .name = "rc",
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -030033 .devnode = ir_devnode,
34};
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -030035
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -030036/**
David Härdeman667c9eb2010-06-13 17:29:31 -030037 * show_protocols() - shows the current IR protocol(s)
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -030038 * @d: the device descriptor
39 * @mattr: the device attribute struct (unused)
40 * @buf: a pointer to the output buffer
41 *
David Härdeman667c9eb2010-06-13 17:29:31 -030042 * This routine is a callback routine for input read the IR protocol type(s).
43 * it is trigged by reading /sys/class/rc/rc?/protocols.
44 * It returns the protocol names of supported protocols.
45 * Enabled protocols are printed in brackets.
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -030046 */
David Härdeman667c9eb2010-06-13 17:29:31 -030047static ssize_t show_protocols(struct device *d,
48 struct device_attribute *mattr, char *buf)
Mauro Carvalho Chehab53f87022009-12-14 02:16:36 -030049{
Mauro Carvalho Chehab53f87022009-12-14 02:16:36 -030050 struct ir_input_dev *ir_dev = dev_get_drvdata(d);
David Härdeman667c9eb2010-06-13 17:29:31 -030051 u64 allowed, enabled;
52 char *tmp = buf;
Mauro Carvalho Chehab53f87022009-12-14 02:16:36 -030053
David Härdeman667c9eb2010-06-13 17:29:31 -030054 if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) {
55 enabled = ir_dev->rc_tab.ir_type;
56 allowed = ir_dev->props->allowed_protos;
57 } else {
58 enabled = ir_dev->raw->enabled_protocols;
59 allowed = ir_raw_get_allowed_protocols();
60 }
Mauro Carvalho Chehab53f87022009-12-14 02:16:36 -030061
David Härdeman667c9eb2010-06-13 17:29:31 -030062 IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n",
63 (long long)allowed,
64 (long long)enabled);
Mauro Carvalho Chehab53f87022009-12-14 02:16:36 -030065
David Härdeman667c9eb2010-06-13 17:29:31 -030066 if (allowed & enabled & IR_TYPE_UNKNOWN)
67 tmp += sprintf(tmp, "[unknown] ");
68 else if (allowed & IR_TYPE_UNKNOWN)
69 tmp += sprintf(tmp, "unknown ");
70
71 if (allowed & enabled & IR_TYPE_RC5)
72 tmp += sprintf(tmp, "[rc5] ");
73 else if (allowed & IR_TYPE_RC5)
74 tmp += sprintf(tmp, "rc5 ");
75
76 if (allowed & enabled & IR_TYPE_NEC)
77 tmp += sprintf(tmp, "[nec] ");
78 else if (allowed & IR_TYPE_NEC)
79 tmp += sprintf(tmp, "nec ");
80
81 if (allowed & enabled & IR_TYPE_RC6)
82 tmp += sprintf(tmp, "[rc6] ");
83 else if (allowed & IR_TYPE_RC6)
84 tmp += sprintf(tmp, "rc6 ");
85
86 if (allowed & enabled & IR_TYPE_JVC)
87 tmp += sprintf(tmp, "[jvc] ");
88 else if (allowed & IR_TYPE_JVC)
89 tmp += sprintf(tmp, "jvc ");
90
91 if (allowed & enabled & IR_TYPE_SONY)
92 tmp += sprintf(tmp, "[sony] ");
93 else if (allowed & IR_TYPE_SONY)
94 tmp += sprintf(tmp, "sony ");
95
96 if (tmp != buf)
97 tmp--;
98 *tmp = '\n';
99 return tmp + 1 - buf;
Mauro Carvalho Chehab53f87022009-12-14 02:16:36 -0300100}
101
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -0300102/**
David Härdeman667c9eb2010-06-13 17:29:31 -0300103 * store_protocols() - changes the current IR protocol(s)
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -0300104 * @d: the device descriptor
105 * @mattr: the device attribute struct (unused)
106 * @buf: a pointer to the input buffer
107 * @len: length of the input buffer
108 *
109 * This routine is a callback routine for changing the IR protocol type.
David Härdeman667c9eb2010-06-13 17:29:31 -0300110 * It is trigged by writing to /sys/class/rc/rc?/protocols.
111 * Writing "+proto" will add a protocol to the list of enabled protocols.
112 * Writing "-proto" will remove a protocol from the list of enabled protocols.
113 * Writing "proto" will enable only "proto".
114 * Returns -EINVAL if an invalid protocol combination or unknown protocol name
115 * is used, otherwise @len.
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -0300116 */
David Härdeman667c9eb2010-06-13 17:29:31 -0300117static ssize_t store_protocols(struct device *d,
118 struct device_attribute *mattr,
119 const char *data,
120 size_t len)
Mauro Carvalho Chehab09b01b92009-12-14 02:46:42 -0300121{
122 struct ir_input_dev *ir_dev = dev_get_drvdata(d);
David Härdeman667c9eb2010-06-13 17:29:31 -0300123 bool enable, disable;
124 const char *tmp;
125 u64 type;
126 u64 mask;
127 int rc;
Mauro Carvalho Chehabeecee322009-12-14 02:56:15 -0300128 unsigned long flags;
Mauro Carvalho Chehab09b01b92009-12-14 02:46:42 -0300129
David Härdeman667c9eb2010-06-13 17:29:31 -0300130 tmp = skip_spaces(data);
131
132 if (*tmp == '+') {
133 enable = true;
134 disable = false;
135 tmp++;
136 } else if (*tmp == '-') {
137 enable = false;
138 disable = true;
139 tmp++;
140 } else {
141 enable = false;
142 disable = false;
Mauro Carvalho Chehabb320f802010-04-08 01:02:49 -0300143 }
Mauro Carvalho Chehab09b01b92009-12-14 02:46:42 -0300144
David Härdeman667c9eb2010-06-13 17:29:31 -0300145 if (!strncasecmp(tmp, "unknown", 7)) {
146 tmp += 7;
147 mask = IR_TYPE_UNKNOWN;
148 } else if (!strncasecmp(tmp, "rc5", 3)) {
149 tmp += 3;
150 mask = IR_TYPE_RC5;
151 } else if (!strncasecmp(tmp, "nec", 3)) {
152 tmp += 3;
153 mask = IR_TYPE_NEC;
154 } else if (!strncasecmp(tmp, "rc6", 3)) {
155 tmp += 3;
156 mask = IR_TYPE_RC6;
157 } else if (!strncasecmp(tmp, "jvc", 3)) {
158 tmp += 3;
159 mask = IR_TYPE_JVC;
160 } else if (!strncasecmp(tmp, "sony", 4)) {
161 tmp += 4;
162 mask = IR_TYPE_SONY;
163 } else {
Mauro Carvalho Chehabb320f802010-04-08 01:02:49 -0300164 IR_dprintk(1, "Unknown protocol\n");
Mauro Carvalho Chehab09b01b92009-12-14 02:46:42 -0300165 return -EINVAL;
166 }
167
David Härdeman667c9eb2010-06-13 17:29:31 -0300168 tmp = skip_spaces(tmp);
169 if (*tmp != '\0') {
170 IR_dprintk(1, "Invalid trailing characters\n");
Mauro Carvalho Chehab09b01b92009-12-14 02:46:42 -0300171 return -EINVAL;
172 }
173
David Härdeman667c9eb2010-06-13 17:29:31 -0300174 if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE)
175 type = ir_dev->rc_tab.ir_type;
176 else
177 type = ir_dev->raw->enabled_protocols;
Mauro Carvalho Chehab09b01b92009-12-14 02:46:42 -0300178
David Härdeman667c9eb2010-06-13 17:29:31 -0300179 if (enable)
180 type |= mask;
181 else if (disable)
182 type &= ~mask;
183 else
184 type = mask;
185
186 if (ir_dev->props && ir_dev->props->change_protocol) {
187 rc = ir_dev->props->change_protocol(ir_dev->props->priv,
188 type);
189 if (rc < 0) {
190 IR_dprintk(1, "Error setting protocols to 0x%llx\n",
191 (long long)type);
192 return -EINVAL;
193 }
194 }
195
196 if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) {
197 spin_lock_irqsave(&ir_dev->rc_tab.lock, flags);
198 ir_dev->rc_tab.ir_type = type;
199 spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags);
200 } else {
201 ir_dev->raw->enabled_protocols = type;
202 }
203
204
205 IR_dprintk(1, "Current protocol(s): 0x%llx\n",
206 (long long)type);
Mauro Carvalho Chehab09b01b92009-12-14 02:46:42 -0300207
208 return len;
209}
210
Mauro Carvalho Chehab9c89a182010-03-12 11:50:17 -0300211#define ADD_HOTPLUG_VAR(fmt, val...) \
212 do { \
213 int err = add_uevent_var(env, fmt, val); \
214 if (err) \
215 return err; \
216 } while (0)
217
David Härdeman667c9eb2010-06-13 17:29:31 -0300218static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
Mauro Carvalho Chehab9c89a182010-03-12 11:50:17 -0300219{
220 struct ir_input_dev *ir_dev = dev_get_drvdata(device);
221
222 if (ir_dev->rc_tab.name)
Mauro Carvalho Chehab3efaa062010-04-10 23:43:39 -0300223 ADD_HOTPLUG_VAR("NAME=%s", ir_dev->rc_tab.name);
Mauro Carvalho Chehab727e6252010-03-12 21:18:14 -0300224 if (ir_dev->driver_name)
Mauro Carvalho Chehab3efaa062010-04-10 23:43:39 -0300225 ADD_HOTPLUG_VAR("DRV_NAME=%s", ir_dev->driver_name);
Mauro Carvalho Chehab9c89a182010-03-12 11:50:17 -0300226
227 return 0;
228}
229
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -0300230/*
231 * Static device attribute struct with the sysfs attributes for IR's
232 */
David Härdeman667c9eb2010-06-13 17:29:31 -0300233static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR,
234 show_protocols, store_protocols);
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300235
David Härdeman667c9eb2010-06-13 17:29:31 -0300236static struct attribute *rc_dev_attrs[] = {
237 &dev_attr_protocols.attr,
Francesco Lavra9714d582009-12-29 15:48:04 -0300238 NULL,
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300239};
240
David Härdeman667c9eb2010-06-13 17:29:31 -0300241static struct attribute_group rc_dev_attr_grp = {
242 .attrs = rc_dev_attrs,
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300243};
244
David Härdeman667c9eb2010-06-13 17:29:31 -0300245static const struct attribute_group *rc_dev_attr_groups[] = {
246 &rc_dev_attr_grp,
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300247 NULL
248};
249
Mauro Carvalho Chehab626cf692010-04-06 23:21:46 -0300250static struct device_type rc_dev_type = {
David Härdeman667c9eb2010-06-13 17:29:31 -0300251 .groups = rc_dev_attr_groups,
252 .uevent = rc_dev_uevent,
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300253};
254
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -0300255/**
David Härdemande88f312010-03-28 18:38:49 -0300256 * ir_register_class() - creates the sysfs for /sys/class/rc/rc?
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -0300257 * @input_dev: the struct input_dev descriptor of the device
258 *
259 * This routine is used to register the syfs code for IR class
260 */
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300261int ir_register_class(struct input_dev *input_dev)
262{
263 int rc;
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300264 const char *path;
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300265 struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
266 int devno = find_first_zero_bit(&ir_core_dev_number,
267 IRRCV_NUM_DEVICES);
268
269 if (unlikely(devno < 0))
270 return devno;
271
David Härdeman667c9eb2010-06-13 17:29:31 -0300272 ir_dev->dev.type = &rc_dev_type;
Mauro Carvalho Chehab626cf692010-04-06 23:21:46 -0300273
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300274 ir_dev->dev.class = &ir_input_class;
275 ir_dev->dev.parent = input_dev->dev.parent;
David Härdemande88f312010-03-28 18:38:49 -0300276 dev_set_name(&ir_dev->dev, "rc%d", devno);
Mauro Carvalho Chehab9c89a182010-03-12 11:50:17 -0300277 dev_set_drvdata(&ir_dev->dev, ir_dev);
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300278 rc = device_register(&ir_dev->dev);
279 if (rc)
280 return rc;
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300281
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300282
283 input_dev->dev.parent = &ir_dev->dev;
284 rc = input_register_device(input_dev);
285 if (rc < 0) {
286 device_del(&ir_dev->dev);
287 return rc;
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300288 }
289
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300290 __module_get(THIS_MODULE);
291
Mauro Carvalho Chehab9c89a182010-03-12 11:50:17 -0300292 path = kobject_get_path(&ir_dev->dev.kobj, GFP_KERNEL);
293 printk(KERN_INFO "%s: %s as %s\n",
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300294 dev_name(&ir_dev->dev),
295 input_dev->name ? input_dev->name : "Unspecified device",
296 path ? path : "N/A");
297 kfree(path);
298
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300299 ir_dev->devno = devno;
300 set_bit(devno, &ir_core_dev_number);
301
302 return 0;
303};
304
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -0300305/**
306 * ir_unregister_class() - removes the sysfs for sysfs for
David Härdemande88f312010-03-28 18:38:49 -0300307 * /sys/class/rc/rc?
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -0300308 * @input_dev: the struct input_dev descriptor of the device
309 *
310 * This routine is used to unregister the syfs code for IR class
311 */
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300312void ir_unregister_class(struct input_dev *input_dev)
313{
314 struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300315
316 clear_bit(ir_dev->devno, &ir_core_dev_number);
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300317 input_unregister_device(input_dev);
318 device_del(&ir_dev->dev);
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300319
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300320 module_put(THIS_MODULE);
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300321}
322
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -0300323/*
Mauro Carvalho Chehabe202c152010-03-26 22:45:16 -0300324 * Init/exit code for the module. Basically, creates/removes /sys/class/rc
Mauro Carvalho Chehabd4b778d2009-12-14 02:55:03 -0300325 */
326
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300327static int __init ir_core_init(void)
328{
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300329 int rc = class_register(&ir_input_class);
330 if (rc) {
Mauro Carvalho Chehabe202c152010-03-26 22:45:16 -0300331 printk(KERN_ERR "ir_core: unable to register rc class\n");
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300332 return rc;
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300333 }
334
Mauro Carvalho Chehab02858ee2010-04-02 20:01:00 -0300335 /* Initialize/load the decoders/keymap code that will be used */
Mauro Carvalho Chehab995187b2010-03-24 20:47:53 -0300336 ir_raw_init();
337
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300338 return 0;
339}
340
341static void __exit ir_core_exit(void)
342{
Mauro Carvalho Chehab945cdfa2010-03-11 12:41:56 -0300343 class_unregister(&ir_input_class);
Mauro Carvalho Chehab4714eda2009-12-13 16:00:08 -0300344}
345
346module_init(ir_core_init);
347module_exit(ir_core_exit);