Commit | Line | Data |
---|---|---|
b4e30a9e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a56960e8 HV |
2 | /* |
3 | * cec-core.c - HDMI Consumer Electronics Control framework - Core | |
4 | * | |
5 | * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | |
a56960e8 HV |
6 | */ |
7 | ||
8 | #include <linux/errno.h> | |
9 | #include <linux/init.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/kmod.h> | |
13 | #include <linux/slab.h> | |
14 | #include <linux/mm.h> | |
15 | #include <linux/string.h> | |
16 | #include <linux/types.h> | |
17 | ||
18 | #include "cec-priv.h" | |
19 | ||
20 | #define CEC_NUM_DEVICES 256 | |
21 | #define CEC_NAME "cec" | |
22 | ||
23 | int cec_debug; | |
24 | module_param_named(debug, cec_debug, int, 0644); | |
25 | MODULE_PARM_DESC(debug, "debug level (0-2)"); | |
26 | ||
db07c5ca HV |
27 | static bool debug_phys_addr; |
28 | module_param(debug_phys_addr, bool, 0644); | |
29 | MODULE_PARM_DESC(debug_phys_addr, "add CEC_CAP_PHYS_ADDR if set"); | |
30 | ||
a56960e8 HV |
31 | static dev_t cec_dev_t; |
32 | ||
33 | /* Active devices */ | |
34 | static DEFINE_MUTEX(cec_devnode_lock); | |
35 | static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES); | |
36 | ||
37 | static struct dentry *top_cec_dir; | |
38 | ||
39 | /* dev to cec_devnode */ | |
40 | #define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev) | |
41 | ||
42 | int cec_get_device(struct cec_devnode *devnode) | |
43 | { | |
44 | /* | |
45 | * Check if the cec device is available. This needs to be done with | |
2ab25d35 | 46 | * the devnode->lock held to prevent an open/unregister race: |
a56960e8 HV |
47 | * without the lock, the device could be unregistered and freed between |
48 | * the devnode->registered check and get_device() calls, leading to | |
49 | * a crash. | |
50 | */ | |
2ab25d35 | 51 | mutex_lock(&devnode->lock); |
a56960e8 HV |
52 | /* |
53 | * return ENXIO if the cec device has been removed | |
54 | * already or if it is not registered anymore. | |
55 | */ | |
56 | if (!devnode->registered) { | |
2ab25d35 | 57 | mutex_unlock(&devnode->lock); |
a56960e8 HV |
58 | return -ENXIO; |
59 | } | |
60 | /* and increase the device refcount */ | |
61 | get_device(&devnode->dev); | |
2ab25d35 | 62 | mutex_unlock(&devnode->lock); |
a56960e8 HV |
63 | return 0; |
64 | } | |
65 | ||
66 | void cec_put_device(struct cec_devnode *devnode) | |
67 | { | |
a56960e8 | 68 | put_device(&devnode->dev); |
a56960e8 HV |
69 | } |
70 | ||
71 | /* Called when the last user of the cec device exits. */ | |
72 | static void cec_devnode_release(struct device *cd) | |
73 | { | |
74 | struct cec_devnode *devnode = to_cec_devnode(cd); | |
75 | ||
76 | mutex_lock(&cec_devnode_lock); | |
a56960e8 HV |
77 | /* Mark device node number as free */ |
78 | clear_bit(devnode->minor, cec_devnode_nums); | |
a56960e8 | 79 | mutex_unlock(&cec_devnode_lock); |
2ab25d35 | 80 | |
a56960e8 HV |
81 | cec_delete_adapter(to_cec_adapter(devnode)); |
82 | } | |
83 | ||
84 | static struct bus_type cec_bus_type = { | |
85 | .name = CEC_NAME, | |
86 | }; | |
87 | ||
88 | /* | |
89 | * Register a cec device node | |
90 | * | |
91 | * The registration code assigns minor numbers and registers the new device node | |
92 | * with the kernel. An error is returned if no free minor number can be found, | |
93 | * or if the registration of the device node fails. | |
94 | * | |
95 | * Zero is returned on success. | |
96 | * | |
97 | * Note that if the cec_devnode_register call fails, the release() callback of | |
98 | * the cec_devnode structure is *not* called, so the caller is responsible for | |
99 | * freeing any data. | |
100 | */ | |
101 | static int __must_check cec_devnode_register(struct cec_devnode *devnode, | |
102 | struct module *owner) | |
103 | { | |
104 | int minor; | |
105 | int ret; | |
106 | ||
a56960e8 HV |
107 | /* Part 1: Find a free minor number */ |
108 | mutex_lock(&cec_devnode_lock); | |
109 | minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0); | |
110 | if (minor == CEC_NUM_DEVICES) { | |
111 | mutex_unlock(&cec_devnode_lock); | |
112 | pr_err("could not get a free minor\n"); | |
113 | return -ENFILE; | |
114 | } | |
115 | ||
116 | set_bit(minor, cec_devnode_nums); | |
117 | mutex_unlock(&cec_devnode_lock); | |
118 | ||
119 | devnode->minor = minor; | |
120 | devnode->dev.bus = &cec_bus_type; | |
121 | devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor); | |
122 | devnode->dev.release = cec_devnode_release; | |
a56960e8 HV |
123 | dev_set_name(&devnode->dev, "cec%d", devnode->minor); |
124 | device_initialize(&devnode->dev); | |
125 | ||
126 | /* Part 2: Initialize and register the character device */ | |
127 | cdev_init(&devnode->cdev, &cec_devnode_fops); | |
a56960e8 | 128 | devnode->cdev.owner = owner; |
7dfccff1 | 129 | kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor); |
a56960e8 | 130 | |
354cf003 | 131 | devnode->registered = true; |
857313e5 LG |
132 | ret = cdev_device_add(&devnode->cdev, &devnode->dev); |
133 | if (ret) { | |
354cf003 | 134 | devnode->registered = false; |
857313e5 | 135 | pr_err("%s: cdev_device_add failed\n", __func__); |
a56960e8 HV |
136 | goto clr_bit; |
137 | } | |
138 | ||
a56960e8 HV |
139 | return 0; |
140 | ||
a56960e8 | 141 | clr_bit: |
2ab25d35 | 142 | mutex_lock(&cec_devnode_lock); |
a56960e8 | 143 | clear_bit(devnode->minor, cec_devnode_nums); |
2ab25d35 | 144 | mutex_unlock(&cec_devnode_lock); |
a56960e8 HV |
145 | return ret; |
146 | } | |
147 | ||
148 | /* | |
149 | * Unregister a cec device node | |
150 | * | |
151 | * This unregisters the passed device. Future open calls will be met with | |
152 | * errors. | |
153 | * | |
154 | * This function can safely be called if the device node has never been | |
155 | * registered or has already been unregistered. | |
156 | */ | |
c8959a39 | 157 | static void cec_devnode_unregister(struct cec_adapter *adap) |
a56960e8 | 158 | { |
c8959a39 | 159 | struct cec_devnode *devnode = &adap->devnode; |
a56960e8 HV |
160 | struct cec_fh *fh; |
161 | ||
2ab25d35 HV |
162 | mutex_lock(&devnode->lock); |
163 | ||
a56960e8 | 164 | /* Check if devnode was never registered or already unregistered */ |
2ab25d35 HV |
165 | if (!devnode->registered || devnode->unregistered) { |
166 | mutex_unlock(&devnode->lock); | |
a56960e8 | 167 | return; |
2ab25d35 | 168 | } |
a56960e8 | 169 | |
a56960e8 HV |
170 | list_for_each_entry(fh, &devnode->fhs, list) |
171 | wake_up_interruptible(&fh->wait); | |
a56960e8 HV |
172 | |
173 | devnode->registered = false; | |
174 | devnode->unregistered = true; | |
2ab25d35 HV |
175 | mutex_unlock(&devnode->lock); |
176 | ||
c8959a39 HV |
177 | mutex_lock(&adap->lock); |
178 | __cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false); | |
179 | __cec_s_log_addrs(adap, NULL, false); | |
180 | mutex_unlock(&adap->lock); | |
181 | ||
857313e5 | 182 | cdev_device_del(&devnode->cdev, &devnode->dev); |
a56960e8 HV |
183 | put_device(&devnode->dev); |
184 | } | |
185 | ||
e94c3281 | 186 | #ifdef CONFIG_CEC_NOTIFIER |
e3a93adc HV |
187 | static void cec_cec_notify(struct cec_adapter *adap, u16 pa) |
188 | { | |
189 | cec_s_phys_addr(adap, pa, false); | |
190 | } | |
191 | ||
192 | void cec_register_cec_notifier(struct cec_adapter *adap, | |
193 | struct cec_notifier *notifier) | |
194 | { | |
c8959a39 | 195 | if (WARN_ON(!cec_is_registered(adap))) |
e3a93adc HV |
196 | return; |
197 | ||
198 | adap->notifier = notifier; | |
199 | cec_notifier_register(adap->notifier, adap, cec_cec_notify); | |
200 | } | |
201 | EXPORT_SYMBOL_GPL(cec_register_cec_notifier); | |
202 | #endif | |
203 | ||
9ca400c1 HV |
204 | #ifdef CONFIG_DEBUG_FS |
205 | static ssize_t cec_error_inj_write(struct file *file, | |
206 | const char __user *ubuf, size_t count, loff_t *ppos) | |
207 | { | |
208 | struct seq_file *sf = file->private_data; | |
209 | struct cec_adapter *adap = sf->private; | |
210 | char *buf; | |
211 | char *line; | |
212 | char *p; | |
213 | ||
214 | buf = memdup_user_nul(ubuf, min_t(size_t, PAGE_SIZE, count)); | |
215 | if (IS_ERR(buf)) | |
216 | return PTR_ERR(buf); | |
217 | p = buf; | |
98c1ce0c | 218 | while (p && *p) { |
9ca400c1 HV |
219 | p = skip_spaces(p); |
220 | line = strsep(&p, "\n"); | |
221 | if (!*line || *line == '#') | |
222 | continue; | |
223 | if (!adap->ops->error_inj_parse_line(adap, line)) { | |
98c1ce0c MCC |
224 | kfree(buf); |
225 | return -EINVAL; | |
9ca400c1 HV |
226 | } |
227 | } | |
228 | kfree(buf); | |
229 | return count; | |
230 | } | |
231 | ||
232 | static int cec_error_inj_show(struct seq_file *sf, void *unused) | |
233 | { | |
234 | struct cec_adapter *adap = sf->private; | |
235 | ||
236 | return adap->ops->error_inj_show(adap, sf); | |
237 | } | |
238 | ||
239 | static int cec_error_inj_open(struct inode *inode, struct file *file) | |
240 | { | |
241 | return single_open(file, cec_error_inj_show, inode->i_private); | |
242 | } | |
243 | ||
244 | static const struct file_operations cec_error_inj_fops = { | |
245 | .open = cec_error_inj_open, | |
246 | .write = cec_error_inj_write, | |
247 | .read = seq_read, | |
248 | .llseek = seq_lseek, | |
249 | .release = single_release, | |
250 | }; | |
251 | #endif | |
252 | ||
a56960e8 HV |
253 | struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, |
254 | void *priv, const char *name, u32 caps, | |
f51e8080 | 255 | u8 available_las) |
a56960e8 HV |
256 | { |
257 | struct cec_adapter *adap; | |
258 | int res; | |
259 | ||
32a847f9 DM |
260 | /* |
261 | * Disable this capability until the connector info public API | |
262 | * is ready. | |
263 | */ | |
264 | caps &= ~CEC_CAP_CONNECTOR_INFO; | |
5f2c467c | 265 | #ifndef CONFIG_MEDIA_CEC_RC |
ee044f5b HV |
266 | caps &= ~CEC_CAP_RC; |
267 | #endif | |
268 | ||
a56960e8 HV |
269 | if (WARN_ON(!caps)) |
270 | return ERR_PTR(-EINVAL); | |
271 | if (WARN_ON(!ops)) | |
272 | return ERR_PTR(-EINVAL); | |
273 | if (WARN_ON(!available_las || available_las > CEC_MAX_LOG_ADDRS)) | |
274 | return ERR_PTR(-EINVAL); | |
275 | adap = kzalloc(sizeof(*adap), GFP_KERNEL); | |
276 | if (!adap) | |
277 | return ERR_PTR(-ENOMEM); | |
c0decac1 | 278 | strscpy(adap->name, name, sizeof(adap->name)); |
a56960e8 | 279 | adap->phys_addr = CEC_PHYS_ADDR_INVALID; |
28e11b15 | 280 | adap->cec_pin_is_high = true; |
a56960e8 HV |
281 | adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0; |
282 | adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE; | |
283 | adap->capabilities = caps; | |
db07c5ca HV |
284 | if (debug_phys_addr) |
285 | adap->capabilities |= CEC_CAP_PHYS_ADDR; | |
f902c1e9 | 286 | adap->needs_hpd = caps & CEC_CAP_NEEDS_HPD; |
a56960e8 HV |
287 | adap->available_log_addrs = available_las; |
288 | adap->sequence = 0; | |
289 | adap->ops = ops; | |
290 | adap->priv = priv; | |
291 | memset(adap->phys_addrs, 0xff, sizeof(adap->phys_addrs)); | |
292 | mutex_init(&adap->lock); | |
293 | INIT_LIST_HEAD(&adap->transmit_queue); | |
294 | INIT_LIST_HEAD(&adap->wait_queue); | |
295 | init_waitqueue_head(&adap->kthread_waitq); | |
296 | ||
333ef6bd HV |
297 | /* adap->devnode initialization */ |
298 | INIT_LIST_HEAD(&adap->devnode.fhs); | |
299 | mutex_init(&adap->devnode.lock); | |
300 | ||
a56960e8 HV |
301 | adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name); |
302 | if (IS_ERR(adap->kthread)) { | |
303 | pr_err("cec-%s: kernel_thread() failed\n", name); | |
304 | res = PTR_ERR(adap->kthread); | |
305 | kfree(adap); | |
306 | return ERR_PTR(res); | |
307 | } | |
308 | ||
5f2c467c | 309 | #ifdef CONFIG_MEDIA_CEC_RC |
a56960e8 HV |
310 | if (!(caps & CEC_CAP_RC)) |
311 | return adap; | |
312 | ||
a56960e8 | 313 | /* Prepare the RC input device */ |
0f7499fd | 314 | adap->rc = rc_allocate_device(RC_DRIVER_SCANCODE); |
a56960e8 HV |
315 | if (!adap->rc) { |
316 | pr_err("cec-%s: failed to allocate memory for rc_dev\n", | |
317 | name); | |
318 | kthread_stop(adap->kthread); | |
319 | kfree(adap); | |
320 | return ERR_PTR(-ENOMEM); | |
321 | } | |
322 | ||
a56960e8 | 323 | snprintf(adap->input_phys, sizeof(adap->input_phys), |
557c97b5 | 324 | "%s/input0", adap->name); |
a56960e8 | 325 | |
557c97b5 | 326 | adap->rc->device_name = adap->name; |
a56960e8 HV |
327 | adap->rc->input_phys = adap->input_phys; |
328 | adap->rc->input_id.bustype = BUS_CEC; | |
329 | adap->rc->input_id.vendor = 0; | |
330 | adap->rc->input_id.product = 0; | |
331 | adap->rc->input_id.version = 1; | |
a56960e8 | 332 | adap->rc->driver_name = CEC_NAME; |
6d741bfe | 333 | adap->rc->allowed_protocols = RC_PROTO_BIT_CEC; |
a56960e8 HV |
334 | adap->rc->priv = adap; |
335 | adap->rc->map_name = RC_MAP_CEC; | |
28492256 | 336 | adap->rc->timeout = MS_TO_NS(550); |
a56960e8 HV |
337 | #endif |
338 | return adap; | |
339 | } | |
340 | EXPORT_SYMBOL_GPL(cec_allocate_adapter); | |
341 | ||
f51e8080 HV |
342 | int cec_register_adapter(struct cec_adapter *adap, |
343 | struct device *parent) | |
a56960e8 HV |
344 | { |
345 | int res; | |
346 | ||
347 | if (IS_ERR_OR_NULL(adap)) | |
348 | return 0; | |
349 | ||
f51e8080 HV |
350 | if (WARN_ON(!parent)) |
351 | return -EINVAL; | |
352 | ||
353 | adap->owner = parent->driver->owner; | |
354 | adap->devnode.dev.parent = parent; | |
355 | ||
5f2c467c | 356 | #ifdef CONFIG_MEDIA_CEC_RC |
a56960e8 | 357 | if (adap->capabilities & CEC_CAP_RC) { |
43c0c039 | 358 | adap->rc->dev.parent = parent; |
a56960e8 HV |
359 | res = rc_register_device(adap->rc); |
360 | ||
361 | if (res) { | |
362 | pr_err("cec-%s: failed to prepare input device\n", | |
363 | adap->name); | |
364 | rc_free_device(adap->rc); | |
365 | adap->rc = NULL; | |
366 | return res; | |
367 | } | |
368 | } | |
369 | #endif | |
370 | ||
371 | res = cec_devnode_register(&adap->devnode, adap->owner); | |
372 | if (res) { | |
5f2c467c | 373 | #ifdef CONFIG_MEDIA_CEC_RC |
a56960e8 HV |
374 | /* Note: rc_unregister also calls rc_free */ |
375 | rc_unregister_device(adap->rc); | |
376 | adap->rc = NULL; | |
377 | #endif | |
378 | return res; | |
379 | } | |
380 | ||
381 | dev_set_drvdata(&adap->devnode.dev, adap); | |
20249f84 | 382 | #ifdef CONFIG_DEBUG_FS |
a56960e8 HV |
383 | if (!top_cec_dir) |
384 | return 0; | |
385 | ||
386 | adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev), top_cec_dir); | |
387 | if (IS_ERR_OR_NULL(adap->cec_dir)) { | |
388 | pr_warn("cec-%s: Failed to create debugfs dir\n", adap->name); | |
389 | return 0; | |
390 | } | |
391 | adap->status_file = debugfs_create_devm_seqfile(&adap->devnode.dev, | |
392 | "status", adap->cec_dir, cec_adap_status); | |
393 | if (IS_ERR_OR_NULL(adap->status_file)) { | |
394 | pr_warn("cec-%s: Failed to create status file\n", adap->name); | |
395 | debugfs_remove_recursive(adap->cec_dir); | |
396 | adap->cec_dir = NULL; | |
9ca400c1 | 397 | return 0; |
a56960e8 | 398 | } |
9ca400c1 HV |
399 | if (!adap->ops->error_inj_show || !adap->ops->error_inj_parse_line) |
400 | return 0; | |
401 | adap->error_inj_file = debugfs_create_file("error-inj", 0644, | |
402 | adap->cec_dir, adap, | |
403 | &cec_error_inj_fops); | |
404 | if (IS_ERR_OR_NULL(adap->error_inj_file)) | |
405 | pr_warn("cec-%s: Failed to create error-inj file\n", | |
406 | adap->name); | |
a56960e8 HV |
407 | #endif |
408 | return 0; | |
409 | } | |
410 | EXPORT_SYMBOL_GPL(cec_register_adapter); | |
411 | ||
412 | void cec_unregister_adapter(struct cec_adapter *adap) | |
413 | { | |
414 | if (IS_ERR_OR_NULL(adap)) | |
415 | return; | |
416 | ||
5f2c467c | 417 | #ifdef CONFIG_MEDIA_CEC_RC |
a56960e8 HV |
418 | /* Note: rc_unregister also calls rc_free */ |
419 | rc_unregister_device(adap->rc); | |
420 | adap->rc = NULL; | |
421 | #endif | |
422 | debugfs_remove_recursive(adap->cec_dir); | |
e94c3281 | 423 | #ifdef CONFIG_CEC_NOTIFIER |
e3a93adc HV |
424 | if (adap->notifier) |
425 | cec_notifier_unregister(adap->notifier); | |
426 | #endif | |
c8959a39 | 427 | cec_devnode_unregister(adap); |
a56960e8 HV |
428 | } |
429 | EXPORT_SYMBOL_GPL(cec_unregister_adapter); | |
430 | ||
431 | void cec_delete_adapter(struct cec_adapter *adap) | |
432 | { | |
433 | if (IS_ERR_OR_NULL(adap)) | |
434 | return; | |
a56960e8 HV |
435 | kthread_stop(adap->kthread); |
436 | if (adap->kthread_config) | |
437 | kthread_stop(adap->kthread_config); | |
e6259b5f HV |
438 | if (adap->ops->adap_free) |
439 | adap->ops->adap_free(adap); | |
5f2c467c | 440 | #ifdef CONFIG_MEDIA_CEC_RC |
d8eddb15 | 441 | rc_free_device(adap->rc); |
a56960e8 HV |
442 | #endif |
443 | kfree(adap); | |
444 | } | |
445 | EXPORT_SYMBOL_GPL(cec_delete_adapter); | |
446 | ||
447 | /* | |
448 | * Initialise cec for linux | |
449 | */ | |
450 | static int __init cec_devnode_init(void) | |
451 | { | |
ed10b4e0 | 452 | int ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES, CEC_NAME); |
a56960e8 | 453 | |
a56960e8 HV |
454 | if (ret < 0) { |
455 | pr_warn("cec: unable to allocate major\n"); | |
456 | return ret; | |
457 | } | |
458 | ||
20249f84 | 459 | #ifdef CONFIG_DEBUG_FS |
a56960e8 HV |
460 | top_cec_dir = debugfs_create_dir("cec", NULL); |
461 | if (IS_ERR_OR_NULL(top_cec_dir)) { | |
462 | pr_warn("cec: Failed to create debugfs cec dir\n"); | |
463 | top_cec_dir = NULL; | |
464 | } | |
465 | #endif | |
466 | ||
467 | ret = bus_register(&cec_bus_type); | |
468 | if (ret < 0) { | |
469 | unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES); | |
470 | pr_warn("cec: bus_register failed\n"); | |
471 | return -EIO; | |
472 | } | |
473 | ||
474 | return 0; | |
475 | } | |
476 | ||
477 | static void __exit cec_devnode_exit(void) | |
478 | { | |
479 | debugfs_remove_recursive(top_cec_dir); | |
480 | bus_unregister(&cec_bus_type); | |
481 | unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES); | |
482 | } | |
483 | ||
484 | subsys_initcall(cec_devnode_init); | |
485 | module_exit(cec_devnode_exit) | |
486 | ||
487 | MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>"); | |
488 | MODULE_DESCRIPTION("Device node registration for cec drivers"); | |
489 | MODULE_LICENSE("GPL"); |