brintos

brintos / linux-shallow public Read only

0
0
Text · 11.1 KiB · 48282d2 Raw
444 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * cec-core.c - HDMI Consumer Electronics Control framework - Core4 *5 * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.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	25621#define CEC_NAME	"cec"22 23/*24 * 400 ms is the time it takes for one 16 byte message to be25 * transferred and 5 is the maximum number of retries. Add26 * another 100 ms as a margin. So if the transmit doesn't27 * finish before that time something is really wrong and we28 * have to time out.29 *30 * This is a sign that something it really wrong and a warning31 * will be issued.32 */33#define CEC_XFER_TIMEOUT_MS (5 * 400 + 100)34 35int cec_debug;36module_param_named(debug, cec_debug, int, 0644);37MODULE_PARM_DESC(debug, "debug level (0-2)");38 39static bool debug_phys_addr;40module_param(debug_phys_addr, bool, 0644);41MODULE_PARM_DESC(debug_phys_addr, "add CEC_CAP_PHYS_ADDR if set");42 43static dev_t cec_dev_t;44 45/* Active devices */46static DEFINE_MUTEX(cec_devnode_lock);47static DECLARE_BITMAP(cec_devnode_nums, CEC_NUM_DEVICES);48 49static struct dentry *top_cec_dir;50 51/* dev to cec_devnode */52#define to_cec_devnode(cd) container_of(cd, struct cec_devnode, dev)53 54/* Called when the last user of the cec device exits. */55static void cec_devnode_release(struct device *cd)56{57	struct cec_devnode *devnode = to_cec_devnode(cd);58 59	mutex_lock(&cec_devnode_lock);60	/* Mark device node number as free */61	clear_bit(devnode->minor, cec_devnode_nums);62	mutex_unlock(&cec_devnode_lock);63 64	cec_delete_adapter(to_cec_adapter(devnode));65}66 67static const struct bus_type cec_bus_type = {68	.name = CEC_NAME,69};70 71/*72 * Register a cec device node73 *74 * The registration code assigns minor numbers and registers the new device node75 * with the kernel. An error is returned if no free minor number can be found,76 * or if the registration of the device node fails.77 *78 * Zero is returned on success.79 *80 * Note that if the cec_devnode_register call fails, the release() callback of81 * the cec_devnode structure is *not* called, so the caller is responsible for82 * freeing any data.83 */84static int __must_check cec_devnode_register(struct cec_devnode *devnode,85					     struct module *owner)86{87	int minor;88	int ret;89 90	/* Part 1: Find a free minor number */91	mutex_lock(&cec_devnode_lock);92	minor = find_first_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES);93	if (minor == CEC_NUM_DEVICES) {94		mutex_unlock(&cec_devnode_lock);95		pr_err("could not get a free minor\n");96		return -ENFILE;97	}98 99	set_bit(minor, cec_devnode_nums);100	mutex_unlock(&cec_devnode_lock);101 102	devnode->minor = minor;103	devnode->dev.bus = &cec_bus_type;104	devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor);105	devnode->dev.release = cec_devnode_release;106	dev_set_name(&devnode->dev, "cec%d", devnode->minor);107	device_initialize(&devnode->dev);108 109	/* Part 2: Initialize and register the character device */110	cdev_init(&devnode->cdev, &cec_devnode_fops);111	devnode->cdev.owner = owner;112	kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor);113 114	devnode->registered = true;115	ret = cdev_device_add(&devnode->cdev, &devnode->dev);116	if (ret) {117		devnode->registered = false;118		pr_err("%s: cdev_device_add failed\n", __func__);119		goto clr_bit;120	}121 122	return 0;123 124clr_bit:125	mutex_lock(&cec_devnode_lock);126	clear_bit(devnode->minor, cec_devnode_nums);127	mutex_unlock(&cec_devnode_lock);128	return ret;129}130 131/*132 * Unregister a cec device node133 *134 * This unregisters the passed device. Future open calls will be met with135 * errors.136 *137 * This function can safely be called if the device node has never been138 * registered or has already been unregistered.139 */140static void cec_devnode_unregister(struct cec_adapter *adap)141{142	struct cec_devnode *devnode = &adap->devnode;143	struct cec_fh *fh;144 145	mutex_lock(&devnode->lock);146 147	/* Check if devnode was never registered or already unregistered */148	if (!devnode->registered || devnode->unregistered) {149		mutex_unlock(&devnode->lock);150		return;151	}152	devnode->registered = false;153	devnode->unregistered = true;154 155	mutex_lock(&devnode->lock_fhs);156	list_for_each_entry(fh, &devnode->fhs, list)157		wake_up_interruptible(&fh->wait);158	mutex_unlock(&devnode->lock_fhs);159 160	mutex_unlock(&devnode->lock);161 162	mutex_lock(&adap->lock);163	__cec_s_phys_addr(adap, CEC_PHYS_ADDR_INVALID, false);164	__cec_s_log_addrs(adap, NULL, false);165	// Disable the adapter (since adap->devnode.unregistered is true)166	cec_adap_enable(adap);167	mutex_unlock(&adap->lock);168 169	cdev_device_del(&devnode->cdev, &devnode->dev);170	put_device(&devnode->dev);171}172 173#ifdef CONFIG_DEBUG_FS174static ssize_t cec_error_inj_write(struct file *file,175	const char __user *ubuf, size_t count, loff_t *ppos)176{177	struct seq_file *sf = file->private_data;178	struct cec_adapter *adap = sf->private;179	char *buf;180	char *line;181	char *p;182 183	buf = memdup_user_nul(ubuf, min_t(size_t, PAGE_SIZE, count));184	if (IS_ERR(buf))185		return PTR_ERR(buf);186	p = buf;187	while (p && *p) {188		p = skip_spaces(p);189		line = strsep(&p, "\n");190		if (!*line || *line == '#')191			continue;192		if (!call_op(adap, error_inj_parse_line, line)) {193			kfree(buf);194			return -EINVAL;195		}196	}197	kfree(buf);198	return count;199}200 201static int cec_error_inj_show(struct seq_file *sf, void *unused)202{203	struct cec_adapter *adap = sf->private;204 205	return call_op(adap, error_inj_show, sf);206}207 208static int cec_error_inj_open(struct inode *inode, struct file *file)209{210	return single_open(file, cec_error_inj_show, inode->i_private);211}212 213static const struct file_operations cec_error_inj_fops = {214	.open = cec_error_inj_open,215	.write = cec_error_inj_write,216	.read = seq_read,217	.llseek = seq_lseek,218	.release = single_release,219};220#endif221 222struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,223					 void *priv, const char *name, u32 caps,224					 u8 available_las)225{226	struct cec_adapter *adap;227	int res;228 229#ifndef CONFIG_MEDIA_CEC_RC230	caps &= ~CEC_CAP_RC;231#endif232 233	if (WARN_ON(!caps))234		return ERR_PTR(-EINVAL);235	if (WARN_ON(!ops))236		return ERR_PTR(-EINVAL);237	if (WARN_ON(!available_las || available_las > CEC_MAX_LOG_ADDRS))238		return ERR_PTR(-EINVAL);239	adap = kzalloc(sizeof(*adap), GFP_KERNEL);240	if (!adap)241		return ERR_PTR(-ENOMEM);242	strscpy(adap->name, name, sizeof(adap->name));243	adap->phys_addr = CEC_PHYS_ADDR_INVALID;244	adap->cec_pin_is_high = true;245	adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0;246	adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE;247	adap->capabilities = caps | CEC_CAP_REPLY_VENDOR_ID;248	if (debug_phys_addr)249		adap->capabilities |= CEC_CAP_PHYS_ADDR;250	adap->needs_hpd = caps & CEC_CAP_NEEDS_HPD;251	adap->available_log_addrs = available_las;252	adap->sequence = 0;253	adap->ops = ops;254	adap->priv = priv;255	mutex_init(&adap->lock);256	INIT_LIST_HEAD(&adap->transmit_queue);257	INIT_LIST_HEAD(&adap->wait_queue);258	init_waitqueue_head(&adap->kthread_waitq);259 260	/* adap->devnode initialization */261	INIT_LIST_HEAD(&adap->devnode.fhs);262	mutex_init(&adap->devnode.lock_fhs);263	mutex_init(&adap->devnode.lock);264 265	adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name);266	if (IS_ERR(adap->kthread)) {267		pr_err("cec-%s: kernel_thread() failed\n", name);268		res = PTR_ERR(adap->kthread);269		kfree(adap);270		return ERR_PTR(res);271	}272 273#ifdef CONFIG_MEDIA_CEC_RC274	if (!(caps & CEC_CAP_RC))275		return adap;276 277	/* Prepare the RC input device */278	adap->rc = rc_allocate_device(RC_DRIVER_SCANCODE);279	if (!adap->rc) {280		pr_err("cec-%s: failed to allocate memory for rc_dev\n",281		       name);282		kthread_stop(adap->kthread);283		kfree(adap);284		return ERR_PTR(-ENOMEM);285	}286 287	snprintf(adap->input_phys, sizeof(adap->input_phys),288		 "%s/input0", adap->name);289 290	adap->rc->device_name = adap->name;291	adap->rc->input_phys = adap->input_phys;292	adap->rc->input_id.bustype = BUS_CEC;293	adap->rc->input_id.vendor = 0;294	adap->rc->input_id.product = 0;295	adap->rc->input_id.version = 1;296	adap->rc->driver_name = CEC_NAME;297	adap->rc->allowed_protocols = RC_PROTO_BIT_CEC;298	adap->rc->priv = adap;299	adap->rc->map_name = RC_MAP_CEC;300	adap->rc->timeout = MS_TO_US(550);301#endif302	return adap;303}304EXPORT_SYMBOL_GPL(cec_allocate_adapter);305 306int cec_register_adapter(struct cec_adapter *adap,307			 struct device *parent)308{309	int res;310 311	if (IS_ERR_OR_NULL(adap))312		return 0;313 314	if (WARN_ON(!parent))315		return -EINVAL;316 317	adap->owner = parent->driver->owner;318	adap->devnode.dev.parent = parent;319	if (!adap->xfer_timeout_ms)320		adap->xfer_timeout_ms = CEC_XFER_TIMEOUT_MS;321 322#ifdef CONFIG_MEDIA_CEC_RC323	if (adap->capabilities & CEC_CAP_RC) {324		adap->rc->dev.parent = parent;325		res = rc_register_device(adap->rc);326 327		if (res) {328			pr_err("cec-%s: failed to prepare input device\n",329			       adap->name);330			rc_free_device(adap->rc);331			adap->rc = NULL;332			return res;333		}334	}335#endif336 337	res = cec_devnode_register(&adap->devnode, adap->owner);338	if (res) {339#ifdef CONFIG_MEDIA_CEC_RC340		/* Note: rc_unregister also calls rc_free */341		rc_unregister_device(adap->rc);342		adap->rc = NULL;343#endif344		return res;345	}346 347	dev_set_drvdata(&adap->devnode.dev, adap);348#ifdef CONFIG_DEBUG_FS349	if (!top_cec_dir)350		return 0;351 352	adap->cec_dir = debugfs_create_dir(dev_name(&adap->devnode.dev),353					   top_cec_dir);354 355	debugfs_create_devm_seqfile(&adap->devnode.dev, "status", adap->cec_dir,356				    cec_adap_status);357 358	if (!adap->ops->error_inj_show || !adap->ops->error_inj_parse_line)359		return 0;360	debugfs_create_file("error-inj", 0644, adap->cec_dir, adap,361			    &cec_error_inj_fops);362#endif363	return 0;364}365EXPORT_SYMBOL_GPL(cec_register_adapter);366 367void cec_unregister_adapter(struct cec_adapter *adap)368{369	if (IS_ERR_OR_NULL(adap))370		return;371 372#ifdef CONFIG_MEDIA_CEC_RC373	/* Note: rc_unregister also calls rc_free */374	rc_unregister_device(adap->rc);375	adap->rc = NULL;376#endif377	debugfs_remove_recursive(adap->cec_dir);378#ifdef CONFIG_CEC_NOTIFIER379	cec_notifier_cec_adap_unregister(adap->notifier, adap);380#endif381	cec_devnode_unregister(adap);382}383EXPORT_SYMBOL_GPL(cec_unregister_adapter);384 385void cec_delete_adapter(struct cec_adapter *adap)386{387	if (IS_ERR_OR_NULL(adap))388		return;389	if (adap->kthread_config)390		kthread_stop(adap->kthread_config);391	kthread_stop(adap->kthread);392	if (adap->ops->adap_free)393		adap->ops->adap_free(adap);394#ifdef CONFIG_MEDIA_CEC_RC395	rc_free_device(adap->rc);396#endif397	kfree(adap);398}399EXPORT_SYMBOL_GPL(cec_delete_adapter);400 401/*402 *	Initialise cec for linux403 */404static int __init cec_devnode_init(void)405{406	int ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES, CEC_NAME);407 408	if (ret < 0) {409		pr_warn("cec: unable to allocate major\n");410		return ret;411	}412 413#ifdef CONFIG_DEBUG_FS414	top_cec_dir = debugfs_create_dir("cec", NULL);415	if (IS_ERR_OR_NULL(top_cec_dir)) {416		pr_warn("cec: Failed to create debugfs cec dir\n");417		top_cec_dir = NULL;418	}419#endif420 421	ret = bus_register(&cec_bus_type);422	if (ret < 0) {423		unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);424		pr_warn("cec: bus_register failed\n");425		return -EIO;426	}427 428	return 0;429}430 431static void __exit cec_devnode_exit(void)432{433	debugfs_remove_recursive(top_cec_dir);434	bus_unregister(&cec_bus_type);435	unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);436}437 438subsys_initcall(cec_devnode_init);439module_exit(cec_devnode_exit)440 441MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");442MODULE_DESCRIPTION("Device node registration for cec drivers");443MODULE_LICENSE("GPL");444