예제 #1
0
static int hid_pid_erase(struct input_dev *dev, int id)
{
	struct hid_device *hid = dev->private;
	struct hid_ff_pid *pid = hid->ff_private;
	struct hid_field *field;
	unsigned long flags;
	int ret;

	if (!CHECK_OWNERSHIP(id, pid))
		return -EACCES;

	/* Find report */
	field = hid_find_field_by_usage(hid, HID_UP_PID | FF_PID_USAGE_BLOCK_FREE,
					HID_OUTPUT_REPORT);
	if (!field) {
		dev_err(&hid->dev->dev, "couldn't find report\n");
		return -EIO;
	}

	ret = hid_set_field(field, 0, pid->effects[id].device_id);
	if (ret) {
		dev_err(&hid->dev->dev, "couldn't set field\n");
		return ret;
	}

	hid_submit_report(hid, field->report, USB_DIR_OUT);

	spin_lock_irqsave(&pid->lock, flags);
	hid_pid_ctrl_playback(hid, pid->effects + id, 0);
	pid->effects[id].flags = 0;
	spin_unlock_irqrestore(&pid->lock, flags);

	return 0;
}
예제 #2
0
/*
 * "ioctl" file op
 */
static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	struct hiddev_list *list = file->private_data;
	struct hiddev *hiddev = list->hiddev;
	struct hid_device *hid = hiddev->hid;
	struct usb_device *dev = hid->dev;
	struct hiddev_collection_info cinfo;
	struct hiddev_report_info rinfo;
	struct hiddev_field_info finfo;
	struct hiddev_usage_ref_multi *uref_multi=NULL;
	struct hiddev_usage_ref *uref;
	struct hiddev_devinfo dinfo;
	struct hid_report *report;
	struct hid_field *field;
	void __user *user_arg = (void __user *)arg;
	int i;

	if (!hiddev->exist)
		return -EIO;

	switch (cmd) {

	case HIDIOCGVERSION:
		return put_user(HID_VERSION, (int __user *)arg);

	case HIDIOCAPPLICATION:
		if (arg < 0 || arg >= hid->maxapplication)
			return -EINVAL;

		for (i = 0; i < hid->maxcollection; i++)
			if (hid->collection[i].type ==
			    HID_COLLECTION_APPLICATION && arg-- == 0)
				break;

		if (i == hid->maxcollection)
			return -EINVAL;

		return hid->collection[i].usage;

	case HIDIOCGDEVINFO:
		dinfo.bustype = BUS_USB;
		dinfo.busnum = dev->bus->busnum;
		dinfo.devnum = dev->devnum;
		dinfo.ifnum = hid->ifnum;
		dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
		dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
		dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
		dinfo.num_applications = hid->maxapplication;
		if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
			return -EFAULT;

		return 0;

	case HIDIOCGFLAG:
		if (put_user(list->flags, (int __user *)arg))
			return -EFAULT;

		return 0;

	case HIDIOCSFLAG:
		{
			int newflags;
			if (get_user(newflags, (int __user *)arg))
				return -EFAULT;

			if ((newflags & ~HIDDEV_FLAGS) != 0 ||
			    ((newflags & HIDDEV_FLAG_REPORT) != 0 &&
			     (newflags & HIDDEV_FLAG_UREF) == 0))
				return -EINVAL;

			list->flags = newflags;

			return 0;
		}

	case HIDIOCGSTRING:
		{
			int idx, len;
			char *buf;

			if (get_user(idx, (int __user *)arg))
				return -EFAULT;

			if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
				return -ENOMEM;

			if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
				kfree(buf);
				return -EINVAL;
			}

			if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
				kfree(buf);
				return -EFAULT;
			}

			kfree(buf);

			return len;
		}

	case HIDIOCINITREPORT:
		hid_init_reports(hid);

		return 0;

	case HIDIOCGREPORT:
		if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
			return -EFAULT;

		if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
			return -EINVAL;

		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
			return -EINVAL;

		hid_submit_report(hid, report, USB_DIR_IN);
		hid_wait_io(hid);

		return 0;

	case HIDIOCSREPORT:
		if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
			return -EFAULT;

		if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
			return -EINVAL;

		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
			return -EINVAL;

		hid_submit_report(hid, report, USB_DIR_OUT);
		hid_wait_io(hid);

		return 0;

	case HIDIOCGREPORTINFO:
		if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
			return -EFAULT;

		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
			return -EINVAL;

		rinfo.num_fields = report->maxfield;

		if (copy_to_user(user_arg, &rinfo, sizeof(rinfo)))
			return -EFAULT;

		return 0;

	case HIDIOCGFIELDINFO:
		if (copy_from_user(&finfo, user_arg, sizeof(finfo)))
			return -EFAULT;
		rinfo.report_type = finfo.report_type;
		rinfo.report_id = finfo.report_id;
		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
			return -EINVAL;

		if (finfo.field_index >= report->maxfield)
			return -EINVAL;

		field = report->field[finfo.field_index];
		memset(&finfo, 0, sizeof(finfo));
		finfo.report_type = rinfo.report_type;
		finfo.report_id = rinfo.report_id;
		finfo.field_index = field->report_count - 1;
		finfo.maxusage = field->maxusage;
		finfo.flags = field->flags;
		finfo.physical = field->physical;
		finfo.logical = field->logical;
		finfo.application = field->application;
		finfo.logical_minimum = field->logical_minimum;
		finfo.logical_maximum = field->logical_maximum;
		finfo.physical_minimum = field->physical_minimum;
		finfo.physical_maximum = field->physical_maximum;
		finfo.unit_exponent = field->unit_exponent;
		finfo.unit = field->unit;

		if (copy_to_user(user_arg, &finfo, sizeof(finfo)))
			return -EFAULT;

		return 0;

	case HIDIOCGUCODE:
		uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
		if (!uref_multi)
			return -ENOMEM;
		uref = &uref_multi->uref;
		if (copy_from_user(uref, user_arg, sizeof(*uref)))
			goto fault;

		rinfo.report_type = uref->report_type;
		rinfo.report_id = uref->report_id;
		if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
			goto inval;

		if (uref->field_index >= report->maxfield)
			goto inval;

		field = report->field[uref->field_index];
		if (uref->usage_index >= field->maxusage)
			goto inval;

		uref->usage_code = field->usage[uref->usage_index].hid;

		if (copy_to_user(user_arg, uref, sizeof(*uref)))
			goto fault;

		kfree(uref_multi);
		return 0;

	case HIDIOCGUSAGE:
	case HIDIOCSUSAGE:
	case HIDIOCGUSAGES:
	case HIDIOCSUSAGES:
	case HIDIOCGCOLLECTIONINDEX:
		uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
		if (!uref_multi)
			return -ENOMEM;
		uref = &uref_multi->uref;
		if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
			if (copy_from_user(uref_multi, user_arg,
					   sizeof(*uref_multi)))
				goto fault;
		} else {
			if (copy_from_user(uref, user_arg, sizeof(*uref)))
				goto fault;
		}

		if (cmd != HIDIOCGUSAGE &&
		    cmd != HIDIOCGUSAGES &&
		    uref->report_type == HID_REPORT_TYPE_INPUT)
			goto inval;

		if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
			field = hiddev_lookup_usage(hid, uref);
			if (field == NULL)
				goto inval;
		} else {
			rinfo.report_type = uref->report_type;
			rinfo.report_id = uref->report_id;
			if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
				goto inval;

			if (uref->field_index >= report->maxfield)
				goto inval;

			field = report->field[uref->field_index];

			if (cmd == HIDIOCGCOLLECTIONINDEX) {
				if (uref->usage_index >= field->maxusage)
					goto inval;
			} else if (uref->usage_index >= field->report_count)
				goto inval;

			else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
				 (uref_multi->num_values > HID_MAX_MULTI_USAGES ||
				  uref->usage_index + uref_multi->num_values >= field->report_count))
				goto inval;
			}

		switch (cmd) {
			case HIDIOCGUSAGE:
				uref->value = field->value[uref->usage_index];
				if (copy_to_user(user_arg, uref, sizeof(*uref)))
					goto fault;
				goto goodreturn;

			case HIDIOCSUSAGE:
				field->value[uref->usage_index] = uref->value;
				goto goodreturn;

			case HIDIOCGCOLLECTIONINDEX:
				kfree(uref_multi);
				return field->usage[uref->usage_index].collection_index;
			case HIDIOCGUSAGES:
				for (i = 0; i < uref_multi->num_values; i++)
					uref_multi->values[i] =
					    field->value[uref->usage_index + i];
				if (copy_to_user(user_arg, uref_multi,
						 sizeof(*uref_multi)))
					goto fault;
				goto goodreturn;
			case HIDIOCSUSAGES:
				for (i = 0; i < uref_multi->num_values; i++)
					field->value[uref->usage_index + i] =
					    uref_multi->values[i];
				goto goodreturn;
		}

goodreturn:
		kfree(uref_multi);
		return 0;
fault:
		kfree(uref_multi);
		return -EFAULT;
inval:
		kfree(uref_multi);
		return -EINVAL;

	case HIDIOCGCOLLECTIONINFO:
		if (copy_from_user(&cinfo, user_arg, sizeof(cinfo)))
			return -EFAULT;

		if (cinfo.index >= hid->maxcollection)
			return -EINVAL;

		cinfo.type = hid->collection[cinfo.index].type;
		cinfo.usage = hid->collection[cinfo.index].usage;
		cinfo.level = hid->collection[cinfo.index].level;

		if (copy_to_user(user_arg, &cinfo, sizeof(cinfo)))
			return -EFAULT;
		return 0;

	default:

		if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
			return -EINVAL;

		if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
			int len;
			if (!hid->name)
				return 0;
			len = strlen(hid->name) + 1;
			if (len > _IOC_SIZE(cmd))
				 len = _IOC_SIZE(cmd);
			return copy_to_user(user_arg, hid->name, len) ?
				-EFAULT : len;
		}

		if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
			int len;
			if (!hid->phys)
				return 0;
			len = strlen(hid->phys) + 1;
			if (len > _IOC_SIZE(cmd))
				len = _IOC_SIZE(cmd);
			return copy_to_user(user_arg, hid->phys, len) ?
				-EFAULT : len;
		}
	}
	return -EINVAL;
}