]>
Commit | Line | Data |
---|---|---|
dd3f616d | 1 | /* ir-sysfs.c - sysfs interface for RC devices (/sys/class/rc) |
4714eda8 | 2 | * |
995187be | 3 | * Copyright (C) 2009-2010 by Mauro Carvalho Chehab <mchehab@redhat.com> |
4714eda8 MCC |
4 | * |
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 | ||
5a0e3ad6 | 15 | #include <linux/slab.h> |
4714eda8 MCC |
16 | #include <linux/input.h> |
17 | #include <linux/device.h> | |
3f113e36 | 18 | #include "ir-core-priv.h" |
4714eda8 MCC |
19 | |
20 | #define IRRCV_NUM_DEVICES 256 | |
21 | ||
d4b778d3 MCC |
22 | /* bit array to represent IR sysfs device number */ |
23 | static unsigned long ir_core_dev_number; | |
4714eda8 | 24 | |
e202c15b | 25 | /* class for /sys/class/rc */ |
945cdfa2 MCC |
26 | static char *ir_devnode(struct device *dev, mode_t *mode) |
27 | { | |
e202c15b | 28 | return kasprintf(GFP_KERNEL, "rc/%s", dev_name(dev)); |
945cdfa2 MCC |
29 | } |
30 | ||
995187be | 31 | static struct class ir_input_class = { |
e202c15b | 32 | .name = "rc", |
945cdfa2 MCC |
33 | .devnode = ir_devnode, |
34 | }; | |
4714eda8 | 35 | |
4403b7b4 MCC |
36 | static struct { |
37 | u64 type; | |
38 | char *name; | |
39 | } proto_names[] = { | |
40 | { IR_TYPE_UNKNOWN, "unknown" }, | |
a9e55ea9 | 41 | { IR_TYPE_RC5, "rc-5" }, |
4403b7b4 | 42 | { IR_TYPE_NEC, "nec" }, |
a9e55ea9 | 43 | { IR_TYPE_RC6, "rc-6" }, |
4403b7b4 MCC |
44 | { IR_TYPE_JVC, "jvc" }, |
45 | { IR_TYPE_SONY, "sony" }, | |
7a569f52 | 46 | { IR_TYPE_RC5_SZ, "rc-5-sz" }, |
ca414698 | 47 | { IR_TYPE_LIRC, "lirc" }, |
4403b7b4 MCC |
48 | }; |
49 | ||
5f124797 MCC |
50 | #define PROTO_NONE "none" |
51 | ||
d4b778d3 | 52 | /** |
667c9ebe | 53 | * show_protocols() - shows the current IR protocol(s) |
d4b778d3 MCC |
54 | * @d: the device descriptor |
55 | * @mattr: the device attribute struct (unused) | |
56 | * @buf: a pointer to the output buffer | |
57 | * | |
667c9ebe DH |
58 | * This routine is a callback routine for input read the IR protocol type(s). |
59 | * it is trigged by reading /sys/class/rc/rc?/protocols. | |
60 | * It returns the protocol names of supported protocols. | |
61 | * Enabled protocols are printed in brackets. | |
d4b778d3 | 62 | */ |
667c9ebe DH |
63 | static ssize_t show_protocols(struct device *d, |
64 | struct device_attribute *mattr, char *buf) | |
53f87022 | 65 | { |
53f87022 | 66 | struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
667c9ebe DH |
67 | u64 allowed, enabled; |
68 | char *tmp = buf; | |
4403b7b4 | 69 | int i; |
667c9ebe | 70 | |
b4d752b3 MCC |
71 | /* Device is being removed */ |
72 | if (!ir_dev) | |
73 | return -EINVAL; | |
74 | ||
a08c7c68 | 75 | if (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_SCANCODE) { |
667c9ebe DH |
76 | enabled = ir_dev->rc_tab.ir_type; |
77 | allowed = ir_dev->props->allowed_protos; | |
a08c7c68 | 78 | } else if (ir_dev->raw) { |
667c9ebe DH |
79 | enabled = ir_dev->raw->enabled_protocols; |
80 | allowed = ir_raw_get_allowed_protocols(); | |
a08c7c68 BR |
81 | } else |
82 | return sprintf(tmp, "[builtin]\n"); | |
53f87022 | 83 | |
667c9ebe DH |
84 | IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n", |
85 | (long long)allowed, | |
86 | (long long)enabled); | |
87 | ||
4403b7b4 MCC |
88 | for (i = 0; i < ARRAY_SIZE(proto_names); i++) { |
89 | if (allowed & enabled & proto_names[i].type) | |
90 | tmp += sprintf(tmp, "[%s] ", proto_names[i].name); | |
91 | else if (allowed & proto_names[i].type) | |
92 | tmp += sprintf(tmp, "%s ", proto_names[i].name); | |
93 | } | |
667c9ebe DH |
94 | |
95 | if (tmp != buf) | |
96 | tmp--; | |
97 | *tmp = '\n'; | |
98 | return tmp + 1 - buf; | |
53f87022 MCC |
99 | } |
100 | ||
d4b778d3 | 101 | /** |
667c9ebe | 102 | * store_protocols() - changes the current IR protocol(s) |
d4b778d3 MCC |
103 | * @d: the device descriptor |
104 | * @mattr: the device attribute struct (unused) | |
105 | * @buf: a pointer to the input buffer | |
106 | * @len: length of the input buffer | |
107 | * | |
108 | * This routine is a callback routine for changing the IR protocol type. | |
667c9ebe DH |
109 | * It is trigged by writing to /sys/class/rc/rc?/protocols. |
110 | * Writing "+proto" will add a protocol to the list of enabled protocols. | |
111 | * Writing "-proto" will remove a protocol from the list of enabled protocols. | |
112 | * Writing "proto" will enable only "proto". | |
5f124797 | 113 | * Writing "none" will disable all protocols. |
667c9ebe DH |
114 | * Returns -EINVAL if an invalid protocol combination or unknown protocol name |
115 | * is used, otherwise @len. | |
d4b778d3 | 116 | */ |
667c9ebe DH |
117 | static ssize_t store_protocols(struct device *d, |
118 | struct device_attribute *mattr, | |
119 | const char *data, | |
120 | size_t len) | |
09b01b90 MCC |
121 | { |
122 | struct ir_input_dev *ir_dev = dev_get_drvdata(d); | |
667c9ebe DH |
123 | bool enable, disable; |
124 | const char *tmp; | |
125 | u64 type; | |
126 | u64 mask; | |
de8592bd | 127 | int rc, i, count = 0; |
eecee32a | 128 | unsigned long flags; |
667c9ebe | 129 | |
b4d752b3 MCC |
130 | /* Device is being removed */ |
131 | if (!ir_dev) | |
132 | return -EINVAL; | |
133 | ||
a08c7c68 | 134 | if (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_SCANCODE) |
de8592bd | 135 | type = ir_dev->rc_tab.ir_type; |
a08c7c68 | 136 | else if (ir_dev->raw) |
de8592bd | 137 | type = ir_dev->raw->enabled_protocols; |
a08c7c68 BR |
138 | else { |
139 | IR_dprintk(1, "Protocol switching not supported\n"); | |
140 | return -EINVAL; | |
141 | } | |
09b01b90 | 142 | |
de8592bd MCC |
143 | while ((tmp = strsep((char **) &data, " \n")) != NULL) { |
144 | if (!*tmp) | |
145 | break; | |
146 | ||
147 | if (*tmp == '+') { | |
148 | enable = true; | |
149 | disable = false; | |
150 | tmp++; | |
151 | } else if (*tmp == '-') { | |
152 | enable = false; | |
153 | disable = true; | |
154 | tmp++; | |
155 | } else { | |
156 | enable = false; | |
157 | disable = false; | |
158 | } | |
5f124797 | 159 | |
de8592bd MCC |
160 | if (!enable && !disable && !strncasecmp(tmp, PROTO_NONE, sizeof(PROTO_NONE))) { |
161 | tmp += sizeof(PROTO_NONE); | |
162 | mask = 0; | |
163 | count++; | |
164 | } else { | |
165 | for (i = 0; i < ARRAY_SIZE(proto_names); i++) { | |
166 | if (!strncasecmp(tmp, proto_names[i].name, strlen(proto_names[i].name))) { | |
167 | tmp += strlen(proto_names[i].name); | |
168 | mask = proto_names[i].type; | |
169 | break; | |
170 | } | |
5f124797 | 171 | } |
de8592bd MCC |
172 | if (i == ARRAY_SIZE(proto_names)) { |
173 | IR_dprintk(1, "Unknown protocol: '%s'\n", tmp); | |
174 | return -EINVAL; | |
175 | } | |
176 | count++; | |
5f124797 | 177 | } |
de8592bd MCC |
178 | |
179 | if (enable) | |
180 | type |= mask; | |
181 | else if (disable) | |
182 | type &= ~mask; | |
183 | else | |
184 | type = mask; | |
09b01b90 MCC |
185 | } |
186 | ||
de8592bd MCC |
187 | if (!count) { |
188 | IR_dprintk(1, "Protocol not specified\n"); | |
09b01b90 MCC |
189 | return -EINVAL; |
190 | } | |
191 | ||
667c9ebe DH |
192 | if (ir_dev->props && ir_dev->props->change_protocol) { |
193 | rc = ir_dev->props->change_protocol(ir_dev->props->priv, | |
194 | type); | |
195 | if (rc < 0) { | |
196 | IR_dprintk(1, "Error setting protocols to 0x%llx\n", | |
197 | (long long)type); | |
198 | return -EINVAL; | |
199 | } | |
200 | } | |
09b01b90 | 201 | |
a08c7c68 | 202 | if (ir_dev->props && ir_dev->props->driver_type == RC_DRIVER_SCANCODE) { |
667c9ebe DH |
203 | spin_lock_irqsave(&ir_dev->rc_tab.lock, flags); |
204 | ir_dev->rc_tab.ir_type = type; | |
205 | spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags); | |
206 | } else { | |
207 | ir_dev->raw->enabled_protocols = type; | |
208 | } | |
b320f80a | 209 | |
667c9ebe DH |
210 | IR_dprintk(1, "Current protocol(s): 0x%llx\n", |
211 | (long long)type); | |
b320f80a | 212 | |
667c9ebe | 213 | return len; |
b320f80a | 214 | } |
9c89a181 MCC |
215 | |
216 | #define ADD_HOTPLUG_VAR(fmt, val...) \ | |
217 | do { \ | |
218 | int err = add_uevent_var(env, fmt, val); \ | |
219 | if (err) \ | |
220 | return err; \ | |
221 | } while (0) | |
222 | ||
667c9ebe | 223 | static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env) |
9c89a181 MCC |
224 | { |
225 | struct ir_input_dev *ir_dev = dev_get_drvdata(device); | |
226 | ||
227 | if (ir_dev->rc_tab.name) | |
3efaa062 | 228 | ADD_HOTPLUG_VAR("NAME=%s", ir_dev->rc_tab.name); |
727e625c | 229 | if (ir_dev->driver_name) |
3efaa062 | 230 | ADD_HOTPLUG_VAR("DRV_NAME=%s", ir_dev->driver_name); |
9c89a181 MCC |
231 | |
232 | return 0; | |
233 | } | |
234 | ||
d4b778d3 MCC |
235 | /* |
236 | * Static device attribute struct with the sysfs attributes for IR's | |
237 | */ | |
667c9ebe DH |
238 | static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR, |
239 | show_protocols, store_protocols); | |
b320f80a | 240 | |
667c9ebe DH |
241 | static struct attribute *rc_dev_attrs[] = { |
242 | &dev_attr_protocols.attr, | |
9714d587 | 243 | NULL, |
4714eda8 MCC |
244 | }; |
245 | ||
667c9ebe DH |
246 | static struct attribute_group rc_dev_attr_grp = { |
247 | .attrs = rc_dev_attrs, | |
945cdfa2 MCC |
248 | }; |
249 | ||
667c9ebe DH |
250 | static const struct attribute_group *rc_dev_attr_groups[] = { |
251 | &rc_dev_attr_grp, | |
945cdfa2 MCC |
252 | NULL |
253 | }; | |
254 | ||
626cf697 | 255 | static struct device_type rc_dev_type = { |
667c9ebe DH |
256 | .groups = rc_dev_attr_groups, |
257 | .uevent = rc_dev_uevent, | |
945cdfa2 MCC |
258 | }; |
259 | ||
d4b778d3 | 260 | /** |
de88f31c | 261 | * ir_register_class() - creates the sysfs for /sys/class/rc/rc? |
d4b778d3 MCC |
262 | * @input_dev: the struct input_dev descriptor of the device |
263 | * | |
264 | * This routine is used to register the syfs code for IR class | |
265 | */ | |
4714eda8 MCC |
266 | int ir_register_class(struct input_dev *input_dev) |
267 | { | |
4714eda8 MCC |
268 | struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
269 | int devno = find_first_zero_bit(&ir_core_dev_number, | |
270 | IRRCV_NUM_DEVICES); | |
271 | ||
272 | if (unlikely(devno < 0)) | |
273 | return devno; | |
274 | ||
667c9ebe | 275 | ir_dev->dev.type = &rc_dev_type; |
58b3dd44 | 276 | ir_dev->devno = devno; |
626cf697 | 277 | |
945cdfa2 MCC |
278 | ir_dev->dev.class = &ir_input_class; |
279 | ir_dev->dev.parent = input_dev->dev.parent; | |
58b3dd44 | 280 | input_dev->dev.parent = &ir_dev->dev; |
de88f31c | 281 | dev_set_name(&ir_dev->dev, "rc%d", devno); |
9c89a181 | 282 | dev_set_drvdata(&ir_dev->dev, ir_dev); |
58b3dd44 ML |
283 | return device_register(&ir_dev->dev); |
284 | }; | |
285 | ||
286 | /** | |
287 | * ir_register_input - registers ir input device with input subsystem | |
288 | * @input_dev: the struct input_dev descriptor of the device | |
289 | */ | |
290 | ||
291 | int ir_register_input(struct input_dev *input_dev) | |
292 | { | |
293 | struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); | |
294 | int rc; | |
295 | const char *path; | |
945cdfa2 MCC |
296 | |
297 | ||
945cdfa2 MCC |
298 | rc = input_register_device(input_dev); |
299 | if (rc < 0) { | |
300 | device_del(&ir_dev->dev); | |
301 | return rc; | |
4714eda8 MCC |
302 | } |
303 | ||
945cdfa2 MCC |
304 | __module_get(THIS_MODULE); |
305 | ||
9c89a181 MCC |
306 | path = kobject_get_path(&ir_dev->dev.kobj, GFP_KERNEL); |
307 | printk(KERN_INFO "%s: %s as %s\n", | |
945cdfa2 MCC |
308 | dev_name(&ir_dev->dev), |
309 | input_dev->name ? input_dev->name : "Unspecified device", | |
310 | path ? path : "N/A"); | |
311 | kfree(path); | |
312 | ||
58b3dd44 | 313 | set_bit(ir_dev->devno, &ir_core_dev_number); |
4714eda8 | 314 | return 0; |
58b3dd44 | 315 | } |
4714eda8 | 316 | |
d4b778d3 MCC |
317 | /** |
318 | * ir_unregister_class() - removes the sysfs for sysfs for | |
de88f31c | 319 | * /sys/class/rc/rc? |
d4b778d3 MCC |
320 | * @input_dev: the struct input_dev descriptor of the device |
321 | * | |
322 | * This routine is used to unregister the syfs code for IR class | |
323 | */ | |
4714eda8 MCC |
324 | void ir_unregister_class(struct input_dev *input_dev) |
325 | { | |
326 | struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); | |
4714eda8 | 327 | |
b4d752b3 | 328 | input_set_drvdata(input_dev, NULL); |
4714eda8 | 329 | clear_bit(ir_dev->devno, &ir_core_dev_number); |
945cdfa2 MCC |
330 | input_unregister_device(input_dev); |
331 | device_del(&ir_dev->dev); | |
4714eda8 | 332 | |
945cdfa2 | 333 | module_put(THIS_MODULE); |
4714eda8 MCC |
334 | } |
335 | ||
d4b778d3 | 336 | /* |
e202c15b | 337 | * Init/exit code for the module. Basically, creates/removes /sys/class/rc |
d4b778d3 MCC |
338 | */ |
339 | ||
4714eda8 MCC |
340 | static int __init ir_core_init(void) |
341 | { | |
945cdfa2 MCC |
342 | int rc = class_register(&ir_input_class); |
343 | if (rc) { | |
e202c15b | 344 | printk(KERN_ERR "ir_core: unable to register rc class\n"); |
945cdfa2 | 345 | return rc; |
4714eda8 MCC |
346 | } |
347 | ||
02858eed | 348 | /* Initialize/load the decoders/keymap code that will be used */ |
995187be | 349 | ir_raw_init(); |
b378f43f | 350 | ir_rcmap_init(); |
995187be | 351 | |
4714eda8 MCC |
352 | return 0; |
353 | } | |
354 | ||
355 | static void __exit ir_core_exit(void) | |
356 | { | |
945cdfa2 | 357 | class_unregister(&ir_input_class); |
b378f43f | 358 | ir_rcmap_cleanup(); |
4714eda8 MCC |
359 | } |
360 | ||
361 | module_init(ir_core_init); | |
362 | module_exit(ir_core_exit); |