brintos

brintos / linux-shallow public Read only

0
0
Text · 15.4 KiB · c887f48 Raw
691 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * HID raw devices, giving access to raw HID events.4 *5 * In comparison to hiddev, this device does not process the6 * hid events at all (no parsing, no lookups). This lets applications7 * to work on raw hid events as they want to, and avoids a need to8 * use a transport-specific userspace libhid/libusb libraries.9 *10 *  Copyright (c) 2007-2014 Jiri Kosina11 */12 13 14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt15 16#include <linux/fs.h>17#include <linux/module.h>18#include <linux/errno.h>19#include <linux/kernel.h>20#include <linux/init.h>21#include <linux/cdev.h>22#include <linux/poll.h>23#include <linux/device.h>24#include <linux/major.h>25#include <linux/slab.h>26#include <linux/hid.h>27#include <linux/mutex.h>28#include <linux/sched/signal.h>29#include <linux/string.h>30 31#include <linux/hidraw.h>32 33static int hidraw_major;34static struct cdev hidraw_cdev;35static const struct class hidraw_class = {36	.name = "hidraw",37};38static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];39static DECLARE_RWSEM(minors_rwsem);40 41static inline bool hidraw_is_revoked(struct hidraw_list *list)42{43	return list->revoked;44}45 46static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)47{48	struct hidraw_list *list = file->private_data;49	int ret = 0, len;50	DECLARE_WAITQUEUE(wait, current);51 52	if (hidraw_is_revoked(list))53		return -ENODEV;54 55	mutex_lock(&list->read_mutex);56 57	while (ret == 0) {58		if (list->head == list->tail) {59			add_wait_queue(&list->hidraw->wait, &wait);60			set_current_state(TASK_INTERRUPTIBLE);61 62			while (list->head == list->tail) {63				if (signal_pending(current)) {64					ret = -ERESTARTSYS;65					break;66				}67				if (!list->hidraw->exist) {68					ret = -EIO;69					break;70				}71				if (file->f_flags & O_NONBLOCK) {72					ret = -EAGAIN;73					break;74				}75 76				/* allow O_NONBLOCK to work well from other threads */77				mutex_unlock(&list->read_mutex);78				schedule();79				mutex_lock(&list->read_mutex);80				set_current_state(TASK_INTERRUPTIBLE);81			}82 83			set_current_state(TASK_RUNNING);84			remove_wait_queue(&list->hidraw->wait, &wait);85		}86 87		if (ret)88			goto out;89 90		len = list->buffer[list->tail].len > count ?91			count : list->buffer[list->tail].len;92 93		if (list->buffer[list->tail].value) {94			if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {95				ret = -EFAULT;96				goto out;97			}98			ret = len;99		}100 101		kfree(list->buffer[list->tail].value);102		list->buffer[list->tail].value = NULL;103		list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);104	}105out:106	mutex_unlock(&list->read_mutex);107	return ret;108}109 110/*111 * The first byte of the report buffer is expected to be a report number.112 */113static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)114{115	unsigned int minor = iminor(file_inode(file));116	struct hid_device *dev;117	__u8 *buf;118	int ret = 0;119 120	lockdep_assert_held(&minors_rwsem);121 122	if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {123		ret = -ENODEV;124		goto out;125	}126 127	dev = hidraw_table[minor]->hid;128 129	if (count > HID_MAX_BUFFER_SIZE) {130		hid_warn(dev, "pid %d passed too large report\n",131			 task_pid_nr(current));132		ret = -EINVAL;133		goto out;134	}135 136	if (count < 2) {137		hid_warn(dev, "pid %d passed too short report\n",138			 task_pid_nr(current));139		ret = -EINVAL;140		goto out;141	}142 143	buf = memdup_user(buffer, count);144	if (IS_ERR(buf)) {145		ret = PTR_ERR(buf);146		goto out;147	}148 149	if ((report_type == HID_OUTPUT_REPORT) &&150	    !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) {151		ret = __hid_hw_output_report(dev, buf, count, (u64)(long)file, false);152		/*153		 * compatibility with old implementation of USB-HID and I2C-HID:154		 * if the device does not support receiving output reports,155		 * on an interrupt endpoint, fallback to SET_REPORT HID command.156		 */157		if (ret != -ENOSYS)158			goto out_free;159	}160 161	ret = __hid_hw_raw_request(dev, buf[0], buf, count, report_type,162				   HID_REQ_SET_REPORT, (u64)(long)file, false);163 164out_free:165	kfree(buf);166out:167	return ret;168}169 170static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)171{172	struct hidraw_list *list = file->private_data;173	ssize_t ret;174	down_read(&minors_rwsem);175	if (hidraw_is_revoked(list))176		ret = -ENODEV;177	else178		ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);179	up_read(&minors_rwsem);180	return ret;181}182 183 184/*185 * This function performs a Get_Report transfer over the control endpoint186 * per section 7.2.1 of the HID specification, version 1.1.  The first byte187 * of buffer is the report number to request, or 0x0 if the device does not188 * use numbered reports. The report_type parameter can be HID_FEATURE_REPORT189 * or HID_INPUT_REPORT.190 */191static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)192{193	unsigned int minor = iminor(file_inode(file));194	struct hid_device *dev;195	__u8 *buf;196	int ret = 0, len;197	unsigned char report_number;198 199	lockdep_assert_held(&minors_rwsem);200 201	if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {202		ret = -ENODEV;203		goto out;204	}205 206	dev = hidraw_table[minor]->hid;207 208	if (!dev->ll_driver->raw_request) {209		ret = -ENODEV;210		goto out;211	}212 213	if (count > HID_MAX_BUFFER_SIZE) {214		hid_warn(dev, "pid %d passed too large report\n",215			task_pid_nr(current));216		ret = -EINVAL;217		goto out;218	}219 220	if (count < 2) {221		hid_warn(dev, "pid %d passed too short report\n",222			task_pid_nr(current));223		ret = -EINVAL;224		goto out;225	}226 227	buf = kmalloc(count, GFP_KERNEL);228	if (!buf) {229		ret = -ENOMEM;230		goto out;231	}232 233	/*234	 * Read the first byte from the user. This is the report number,235	 * which is passed to hid_hw_raw_request().236	 */237	if (copy_from_user(&report_number, buffer, 1)) {238		ret = -EFAULT;239		goto out_free;240	}241 242	ret = __hid_hw_raw_request(dev, report_number, buf, count, report_type,243				   HID_REQ_GET_REPORT, (u64)(long)file, false);244 245	if (ret < 0)246		goto out_free;247 248	len = (ret < count) ? ret : count;249 250	if (copy_to_user(buffer, buf, len)) {251		ret = -EFAULT;252		goto out_free;253	}254 255	ret = len;256 257out_free:258	kfree(buf);259out:260	return ret;261}262 263static __poll_t hidraw_poll(struct file *file, poll_table *wait)264{265	struct hidraw_list *list = file->private_data;266	__poll_t mask = EPOLLOUT | EPOLLWRNORM; /* hidraw is always writable */267 268	poll_wait(file, &list->hidraw->wait, wait);269	if (list->head != list->tail)270		mask |= EPOLLIN | EPOLLRDNORM;271	if (!list->hidraw->exist || hidraw_is_revoked(list))272		mask |= EPOLLERR | EPOLLHUP;273	return mask;274}275 276static int hidraw_open(struct inode *inode, struct file *file)277{278	unsigned int minor = iminor(inode);279	struct hidraw *dev;280	struct hidraw_list *list;281	unsigned long flags;282	int err = 0;283 284	if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {285		err = -ENOMEM;286		goto out;287	}288 289	/*290	 * Technically not writing to the hidraw_table but a write lock is291	 * required to protect the device refcount. This is symmetrical to292	 * hidraw_release().293	 */294	down_write(&minors_rwsem);295	if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {296		err = -ENODEV;297		goto out_unlock;298	}299 300	dev = hidraw_table[minor];301	if (!dev->open++) {302		err = hid_hw_power(dev->hid, PM_HINT_FULLON);303		if (err < 0) {304			dev->open--;305			goto out_unlock;306		}307 308		err = hid_hw_open(dev->hid);309		if (err < 0) {310			hid_hw_power(dev->hid, PM_HINT_NORMAL);311			dev->open--;312			goto out_unlock;313		}314	}315 316	list->hidraw = hidraw_table[minor];317	mutex_init(&list->read_mutex);318	spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);319	list_add_tail(&list->node, &hidraw_table[minor]->list);320	spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);321	file->private_data = list;322out_unlock:323	up_write(&minors_rwsem);324out:325	if (err < 0)326		kfree(list);327	return err;328 329}330 331static int hidraw_fasync(int fd, struct file *file, int on)332{333	struct hidraw_list *list = file->private_data;334 335	if (hidraw_is_revoked(list))336		return -ENODEV;337 338	return fasync_helper(fd, file, on, &list->fasync);339}340 341static void drop_ref(struct hidraw *hidraw, int exists_bit)342{343	if (exists_bit) {344		hidraw->exist = 0;345		if (hidraw->open) {346			hid_hw_close(hidraw->hid);347			wake_up_interruptible(&hidraw->wait);348		}349		device_destroy(&hidraw_class,350			       MKDEV(hidraw_major, hidraw->minor));351	} else {352		--hidraw->open;353	}354	if (!hidraw->open) {355		if (!hidraw->exist) {356			hidraw_table[hidraw->minor] = NULL;357			kfree(hidraw);358		} else {359			/* close device for last reader */360			hid_hw_close(hidraw->hid);361			hid_hw_power(hidraw->hid, PM_HINT_NORMAL);362		}363	}364}365 366static int hidraw_release(struct inode * inode, struct file * file)367{368	unsigned int minor = iminor(inode);369	struct hidraw_list *list = file->private_data;370	unsigned long flags;371 372	down_write(&minors_rwsem);373 374	spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);375	while (list->tail != list->head) {376		kfree(list->buffer[list->tail].value);377		list->buffer[list->tail].value = NULL;378		list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);379	}380	list_del(&list->node);381	spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);382	kfree(list);383 384	drop_ref(hidraw_table[minor], 0);385 386	up_write(&minors_rwsem);387	return 0;388}389 390static int hidraw_revoke(struct hidraw_list *list)391{392	list->revoked = true;393 394	return 0;395}396 397static long hidraw_ioctl(struct file *file, unsigned int cmd,398							unsigned long arg)399{400	struct inode *inode = file_inode(file);401	unsigned int minor = iminor(inode);402	long ret = 0;403	struct hidraw *dev;404	struct hidraw_list *list = file->private_data;405	void __user *user_arg = (void __user*) arg;406 407	down_read(&minors_rwsem);408	dev = hidraw_table[minor];409	if (!dev || !dev->exist || hidraw_is_revoked(list)) {410		ret = -ENODEV;411		goto out;412	}413 414	switch (cmd) {415		case HIDIOCGRDESCSIZE:416			if (put_user(dev->hid->rsize, (int __user *)arg))417				ret = -EFAULT;418			break;419 420		case HIDIOCGRDESC:421			{422				__u32 len;423 424				if (get_user(len, (int __user *)arg))425					ret = -EFAULT;426				else if (len > HID_MAX_DESCRIPTOR_SIZE - 1)427					ret = -EINVAL;428				else if (copy_to_user(user_arg + offsetof(429					struct hidraw_report_descriptor,430					value[0]),431					dev->hid->rdesc,432					min(dev->hid->rsize, len)))433					ret = -EFAULT;434				break;435			}436		case HIDIOCGRAWINFO:437			{438				struct hidraw_devinfo dinfo;439 440				dinfo.bustype = dev->hid->bus;441				dinfo.vendor = dev->hid->vendor;442				dinfo.product = dev->hid->product;443				if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))444					ret = -EFAULT;445				break;446			}447		case HIDIOCREVOKE:448			{449				if (user_arg)450					ret = -EINVAL;451				else452					ret = hidraw_revoke(list);453				break;454			}455		default:456			{457				struct hid_device *hid = dev->hid;458				if (_IOC_TYPE(cmd) != 'H') {459					ret = -EINVAL;460					break;461				}462 463				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSFEATURE(0))) {464					int len = _IOC_SIZE(cmd);465					ret = hidraw_send_report(file, user_arg, len, HID_FEATURE_REPORT);466					break;467				}468				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGFEATURE(0))) {469					int len = _IOC_SIZE(cmd);470					ret = hidraw_get_report(file, user_arg, len, HID_FEATURE_REPORT);471					break;472				}473 474				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSINPUT(0))) {475					int len = _IOC_SIZE(cmd);476					ret = hidraw_send_report(file, user_arg, len, HID_INPUT_REPORT);477					break;478				}479				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGINPUT(0))) {480					int len = _IOC_SIZE(cmd);481					ret = hidraw_get_report(file, user_arg, len, HID_INPUT_REPORT);482					break;483				}484 485				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCSOUTPUT(0))) {486					int len = _IOC_SIZE(cmd);487					ret = hidraw_send_report(file, user_arg, len, HID_OUTPUT_REPORT);488					break;489				}490				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGOUTPUT(0))) {491					int len = _IOC_SIZE(cmd);492					ret = hidraw_get_report(file, user_arg, len, HID_OUTPUT_REPORT);493					break;494				}495 496				/* Begin Read-only ioctls. */497				if (_IOC_DIR(cmd) != _IOC_READ) {498					ret = -EINVAL;499					break;500				}501 502				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) {503					int len = strlen(hid->name) + 1;504					if (len > _IOC_SIZE(cmd))505						len = _IOC_SIZE(cmd);506					ret = copy_to_user(user_arg, hid->name, len) ?507						-EFAULT : len;508					break;509				}510 511				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) {512					int len = strlen(hid->phys) + 1;513					if (len > _IOC_SIZE(cmd))514						len = _IOC_SIZE(cmd);515					ret = copy_to_user(user_arg, hid->phys, len) ?516						-EFAULT : len;517					break;518				}519 520				if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWUNIQ(0))) {521					int len = strlen(hid->uniq) + 1;522					if (len > _IOC_SIZE(cmd))523						len = _IOC_SIZE(cmd);524					ret = copy_to_user(user_arg, hid->uniq, len) ?525						-EFAULT : len;526					break;527				}528			}529 530		ret = -ENOTTY;531	}532out:533	up_read(&minors_rwsem);534	return ret;535}536 537static const struct file_operations hidraw_ops = {538	.owner =        THIS_MODULE,539	.read =         hidraw_read,540	.write =        hidraw_write,541	.poll =         hidraw_poll,542	.open =         hidraw_open,543	.release =      hidraw_release,544	.unlocked_ioctl = hidraw_ioctl,545	.fasync =	hidraw_fasync,546	.compat_ioctl   = compat_ptr_ioctl,547	.llseek =	noop_llseek,548};549 550int hidraw_report_event(struct hid_device *hid, u8 *data, int len)551{552	struct hidraw *dev = hid->hidraw;553	struct hidraw_list *list;554	int ret = 0;555	unsigned long flags;556 557	spin_lock_irqsave(&dev->list_lock, flags);558	list_for_each_entry(list, &dev->list, node) {559		int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);560 561		if (hidraw_is_revoked(list) || new_head == list->tail)562			continue;563 564		if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) {565			ret = -ENOMEM;566			break;567		}568		list->buffer[list->head].len = len;569		list->head = new_head;570		kill_fasync(&list->fasync, SIGIO, POLL_IN);571	}572	spin_unlock_irqrestore(&dev->list_lock, flags);573 574	wake_up_interruptible(&dev->wait);575	return ret;576}577EXPORT_SYMBOL_GPL(hidraw_report_event);578 579int hidraw_connect(struct hid_device *hid)580{581	int minor, result;582	struct hidraw *dev;583 584	/* we accept any HID device, all applications */585 586	dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);587	if (!dev)588		return -ENOMEM;589 590	result = -EINVAL;591 592	down_write(&minors_rwsem);593 594	for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {595		if (hidraw_table[minor])596			continue;597		hidraw_table[minor] = dev;598		result = 0;599		break;600	}601 602	if (result) {603		up_write(&minors_rwsem);604		kfree(dev);605		goto out;606	}607 608	dev->dev = device_create(&hidraw_class, &hid->dev, MKDEV(hidraw_major, minor),609				 NULL, "%s%d", "hidraw", minor);610 611	if (IS_ERR(dev->dev)) {612		hidraw_table[minor] = NULL;613		up_write(&minors_rwsem);614		result = PTR_ERR(dev->dev);615		kfree(dev);616		goto out;617	}618 619	init_waitqueue_head(&dev->wait);620	spin_lock_init(&dev->list_lock);621	INIT_LIST_HEAD(&dev->list);622 623	dev->hid = hid;624	dev->minor = minor;625 626	dev->exist = 1;627	hid->hidraw = dev;628 629	up_write(&minors_rwsem);630out:631	return result;632 633}634EXPORT_SYMBOL_GPL(hidraw_connect);635 636void hidraw_disconnect(struct hid_device *hid)637{638	struct hidraw *hidraw = hid->hidraw;639 640	down_write(&minors_rwsem);641 642	drop_ref(hidraw, 1);643 644	up_write(&minors_rwsem);645}646EXPORT_SYMBOL_GPL(hidraw_disconnect);647 648int __init hidraw_init(void)649{650	int result;651	dev_t dev_id;652 653	result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,654			HIDRAW_MAX_DEVICES, "hidraw");655	if (result < 0) {656		pr_warn("can't get major number\n");657		goto out;658	}659 660	hidraw_major = MAJOR(dev_id);661 662	result = class_register(&hidraw_class);663	if (result)664		goto error_cdev;665 666        cdev_init(&hidraw_cdev, &hidraw_ops);667	result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);668	if (result < 0)669		goto error_class;670 671	pr_info("raw HID events driver (C) Jiri Kosina\n");672out:673	return result;674 675error_class:676	class_unregister(&hidraw_class);677error_cdev:678	unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);679	goto out;680}681 682void hidraw_exit(void)683{684	dev_t dev_id = MKDEV(hidraw_major, 0);685 686	cdev_del(&hidraw_cdev);687	class_unregister(&hidraw_class);688	unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);689 690}691