Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c500c971 authored by Jiri Slaby's avatar Jiri Slaby Committed by Jiri Kosina
Browse files

HID: hid, make parsing event driven



Next step for complete hid bus, this patch includes:
- call parser either from probe or from hid-core if there is no probe.
- add ll_driver structure and centralize some stuff there (open, close...)
- split and merge usb_hid_configure and hid_probe into several functions
  to allow hooks/fixes between them

Signed-off-by: default avatarJiri Slaby <jslaby@suse.cz>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 85cdaf52
Loading
Loading
Loading
Loading
+17 −7
Original line number Diff line number Diff line
@@ -648,6 +648,9 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
		hid_parser_reserved
	};

	if (device->driver->report_fixup)
		device->driver->report_fixup(device, start, size);

	device->rdesc = kmalloc(size, GFP_KERNEL);
	if (device->rdesc == NULL)
		return -ENOMEM;
@@ -1152,15 +1155,20 @@ static int hid_device_probe(struct device *dev)
	int ret = 0;

	if (!hdev->driver) {
		if (hdrv->probe) {
			ret = -ENODEV;

		id = hid_match_id(hdev, hdrv->id_table);
			if (id)
		if (id == NULL)
			return -ENODEV;

		hdev->driver = hdrv;
		if (hdrv->probe) {
			ret = hdrv->probe(hdev, id);
		}
		} else { /* default probe */
			ret = hid_parse(hdev);
			if (!ret)
			hdev->driver = hdrv;
				ret = hid_hw_start(hdev);
		}
		if (ret)
			hdev->driver = NULL;
	}
	return ret;
}
@@ -1173,6 +1181,8 @@ static int hid_device_remove(struct device *dev)
	if (hdrv) {
		if (hdrv->remove)
			hdrv->remove(hdev);
		else /* default remove */
			hid_hw_stop(hdev);
		hdev->driver = NULL;
	}

+17 −3
Original line number Diff line number Diff line
@@ -390,6 +390,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
	if (ret)
		goto mapped;

	if (device->driver->input_mapping) {
		int ret = device->driver->input_mapping(device, hidinput, field,
				usage, &bit, &max);
		if (ret > 0)
			goto mapped;
		if (ret < 0)
			goto ignore;
	}

	switch (usage->hid & HID_USAGE_PAGE) {

		case HID_UP_UNDEFINED:
@@ -755,6 +764,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
	}

mapped:
	if (device->driver->input_mapped && device->driver->input_mapped(device,
				hidinput, field, usage, &bit, &max) < 0)
		goto ignore;

	if (device->quirks & HID_QUIRK_MIGHTYMOUSE) {
		if (usage->hid == HID_GD_Z)
			map_rel(REL_HWHEEL);
@@ -961,14 +974,14 @@ static int hidinput_open(struct input_dev *dev)
{
	struct hid_device *hid = input_get_drvdata(dev);

	return hid->hid_open(hid);
	return hid->ll_driver->open(hid);
}

static void hidinput_close(struct input_dev *dev)
{
	struct hid_device *hid = input_get_drvdata(dev);

	hid->hid_close(hid);
	hid->ll_driver->close(hid);
}

/*
@@ -1019,7 +1032,8 @@ int hidinput_connect(struct hid_device *hid)
				}

				input_set_drvdata(input_dev, hid);
				input_dev->event = hid->hidinput_input_event;
				input_dev->event =
					hid->ll_driver->hidinput_input_event;
				input_dev->open = hidinput_open;
				input_dev->close = hidinput_close;
				input_dev->setkeycode = hidinput_setkeycode;
+3 −3
Original line number Diff line number Diff line
@@ -181,7 +181,7 @@ static int hidraw_open(struct inode *inode, struct file *file)

	dev = hidraw_table[minor];
	if (!dev->open++)
		dev->hid->hid_open(dev->hid);
		dev->hid->ll_driver->open(dev->hid);

out_unlock:
	spin_unlock(&minors_lock);
@@ -207,7 +207,7 @@ static int hidraw_release(struct inode * inode, struct file * file)
	dev = hidraw_table[minor];
	if (!dev->open--) {
		if (list->hidraw->exist)
			dev->hid->hid_close(dev->hid);
			dev->hid->ll_driver->close(dev->hid);
		else
			kfree(list->hidraw);
	}
@@ -367,7 +367,7 @@ void hidraw_disconnect(struct hid_device *hid)
	device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));

	if (hidraw->open) {
		hid->hid_close(hid);
		hid->ll_driver->close(hid);
		wake_up_interruptible(&hidraw->wait);
	} else {
		kfree(hidraw);
+191 −139
Original line number Diff line number Diff line
@@ -701,17 +701,84 @@ static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum)
	kfree(buf);
}

static struct hid_device *usb_hid_configure(struct usb_interface *intf)
static int usbhid_start_finish(struct hid_device *hid)
{
	struct usb_interface *intf = to_usb_interface(hid->dev.parent);
	char path[64], *type;
	unsigned int i;

	usbhid_init_reports(hid);
	hid_dump_device(hid);
	if (hid->quirks & HID_QUIRK_RESET_LEDS)
		usbhid_set_leds(hid);

	if (!hidinput_connect(hid))
		hid->claimed |= HID_CLAIMED_INPUT;
	if (!hiddev_connect(hid))
		hid->claimed |= HID_CLAIMED_HIDDEV;
	if (!hidraw_connect(hid))
		hid->claimed |= HID_CLAIMED_HIDRAW;

	if (!hid->claimed) {
		printk(KERN_ERR "HID device claimed by neither input, hiddev "
				"nor hidraw\n");
		return -ENODEV;
	}

	if ((hid->claimed & HID_CLAIMED_INPUT))
		hid_ff_init(hid);

	if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
		hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
			intf->cur_altsetting->desc.bInterfaceNumber);

	printk(KERN_INFO);

	if (hid->claimed & HID_CLAIMED_INPUT)
		printk("input");
	if ((hid->claimed & HID_CLAIMED_INPUT) &&
			((hid->claimed & HID_CLAIMED_HIDDEV) ||
				hid->claimed & HID_CLAIMED_HIDRAW))
		printk(",");
	if (hid->claimed & HID_CLAIMED_HIDDEV)
		printk("hiddev%d", hid->minor);
	if ((hid->claimed & HID_CLAIMED_INPUT) &&
			(hid->claimed & HID_CLAIMED_HIDDEV) &&
			(hid->claimed & HID_CLAIMED_HIDRAW))
		printk(",");
	if (hid->claimed & HID_CLAIMED_HIDRAW)
		printk("hidraw%d", ((struct hidraw *)hid->hidraw)->minor);

	type = "Device";
	for (i = 0; i < hid->maxcollection; i++) {
		if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
		    (hid->collection[i].usage & HID_USAGE_PAGE) ==
						HID_UP_GENDESK &&
		    (hid->collection[i].usage & 0xffff) <
						ARRAY_SIZE(hid_types)) {
			type = hid_types[hid->collection[i].usage & 0xffff];
			break;
		}
	}

	usb_make_path(interface_to_usbdev(intf), path, 63);

	printk(": USB HID v%x.%02x %s [%s] on %s\n",
		hid->version >> 8, hid->version & 0xff, type, hid->name, path);

	return 0;
}

static int usbhid_parse(struct hid_device *hid)
{
	struct usb_interface *intf = to_usb_interface(hid->dev.parent);
	struct usb_host_interface *interface = intf->cur_altsetting;
	struct usb_device *dev = interface_to_usbdev (intf);
	struct hid_descriptor *hdesc;
	struct hid_device *hid;
	u32 quirks = 0;
	unsigned int insize = 0, rsize = 0;
	unsigned int rsize = 0;
	char *rdesc;
	int n, len;
	struct usbhid_device *usbhid;
	int ret, n;

	quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
			le16_to_cpu(dev->descriptor.idProduct));
@@ -725,40 +792,44 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
	}

	if (quirks & HID_QUIRK_IGNORE)
		return NULL;
		return -ENODEV;

	if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
		(interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
			return NULL;

			return -ENODEV;

	if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
	    (!interface->desc.bNumEndpoints ||
	     usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
		dbg_hid("class descriptor not present\n");
		return NULL;
		return -ENODEV;
	}

	hid->version = le16_to_cpu(hdesc->bcdHID);
	hid->country = hdesc->bCountryCode;

	for (n = 0; n < hdesc->bNumDescriptors; n++)
		if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
			rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);

	if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
		dbg_hid("weird size of report descriptor (%u)\n", rsize);
		return NULL;
		return -EINVAL;
	}

	if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
		dbg_hid("couldn't allocate rdesc memory\n");
		return NULL;
		return -ENOMEM;
	}

	hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0);

	if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
	ret = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber,
			HID_DT_REPORT, rdesc, rsize);
	if (ret < 0) {
		dbg_hid("reading report descriptor failed\n");
		kfree(rdesc);
		return NULL;
		goto err;
	}

	usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),
@@ -770,24 +841,36 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
		dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
	dbg_hid_line("\n");

	hid = hid_allocate_device();
	if (IS_ERR(hid)) {
	ret = hid_parse_report(hid, rdesc, rsize);
	kfree(rdesc);
		return NULL;
	}

	if (hid_parse_report(hid, rdesc, n)) {
	if (ret) {
		dbg_hid("parsing report descriptor failed\n");
		hid_destroy_device(hid);
		kfree(rdesc);
		return NULL;
		goto err;
	}

	kfree(rdesc);
	hid->quirks = quirks;

	if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
		goto fail_no_usbhid;
	return 0;
err:
	return ret;
}

static int usbhid_start(struct hid_device *hid)
{
	struct usb_interface *intf = to_usb_interface(hid->dev.parent);
	struct usb_host_interface *interface = intf->cur_altsetting;
	struct usb_device *dev = interface_to_usbdev(intf);
	struct usbhid_device *usbhid;
	unsigned int n, insize = 0;
	int ret;

	WARN_ON(hid->driver_data);

	usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL);
	if (usbhid == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	hid->driver_data = usbhid;
	usbhid->hid = hid;
@@ -805,27 +888,12 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
	if (insize > HID_MAX_BUFFER_SIZE)
		insize = HID_MAX_BUFFER_SIZE;

	if (hid_alloc_buffers(dev, hid))
	if (hid_alloc_buffers(dev, hid)) {
		ret = -ENOMEM;
		goto fail;

	hid->name[0] = 0;

	if (dev->manufacturer)
		strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));

	if (dev->product) {
		if (dev->manufacturer)
			strlcat(hid->name, " ", sizeof(hid->name));
		strlcat(hid->name, dev->product, sizeof(hid->name));
	}

	if (!strlen(hid->name))
		snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
			 le16_to_cpu(dev->descriptor.idVendor),
			 le16_to_cpu(dev->descriptor.idProduct));

	for (n = 0; n < interface->desc.bNumEndpoints; n++) {

		struct usb_endpoint_descriptor *endpoint;
		int pipe;
		int interval;
@@ -837,7 +905,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
		interval = endpoint->bInterval;

		/* Some vendors give fullspeed interval on highspeed devides */
		if (quirks & HID_QUIRK_FULLSPEED_INTERVAL  &&
		if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
		    dev->speed == USB_SPEED_HIGH) {
			interval = fls(endpoint->bInterval*8);
			printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
@@ -848,6 +916,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
		if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
			interval = hid_mousepoll_interval;

		ret = -ENOMEM;
		if (usb_endpoint_dir_in(endpoint)) {
			if (usbhid->urbin)
				continue;
@@ -873,6 +942,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)

	if (!usbhid->urbin) {
		err_hid("couldn't find an input interrupt endpoint");
		ret = -ENODEV;
		goto fail;
	}

@@ -884,44 +954,26 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
	spin_lock_init(&usbhid->outlock);
	spin_lock_init(&usbhid->ctrllock);

	hid->version = le16_to_cpu(hdesc->bcdHID);
	hid->country = hdesc->bCountryCode;
	hid->dev.parent = &intf->dev;
	usbhid->intf = intf;
	usbhid->ifnum = interface->desc.bInterfaceNumber;

	hid->bus = BUS_USB;
	hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
	hid->product = le16_to_cpu(dev->descriptor.idProduct);

	usb_make_path(dev, hid->phys, sizeof(hid->phys));
	strlcat(hid->phys, "/input", sizeof(hid->phys));
	len = strlen(hid->phys);
	if (len < sizeof(hid->phys) - 1)
		snprintf(hid->phys + len, sizeof(hid->phys) - len,
			 "%d", intf->altsetting[0].desc.bInterfaceNumber);

	if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
		hid->uniq[0] = 0;

	usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
	if (!usbhid->urbctrl)
	if (!usbhid->urbctrl) {
		ret = -ENOMEM;
		goto fail;
	}

	usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
			     usbhid->ctrlbuf, 1, hid_ctrl, hid);
	usbhid->urbctrl->setup_dma = usbhid->cr_dma;
	usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
	usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
	hid->hidinput_input_event = usb_hidinput_input_event;
	hid->hid_open = usbhid_open;
	hid->hid_close = usbhid_close;
#ifdef CONFIG_USB_HIDDEV
	hid->hiddev_hid_event = hiddev_hid_event;
	hid->hiddev_report_event = hiddev_report_event;
#endif
	hid->hid_output_raw_report = usbhid_output_raw_report;
	return hid;

	ret = usbhid_start_finish(hid);
	if (ret)
		goto fail;

	return 0;

fail:
	usb_free_urb(usbhid->urbin);
@@ -929,24 +981,18 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
	usb_free_urb(usbhid->urbctrl);
	hid_free_buffers(dev, hid);
	kfree(usbhid);
fail_no_usbhid:
	hid_destroy_device(hid);

	return NULL;
err:
	return ret;
}

static void hid_disconnect(struct usb_interface *intf)
static void usbhid_stop(struct hid_device *hid)
{
	struct hid_device *hid = usb_get_intfdata (intf);
	struct usbhid_device *usbhid;
	struct usbhid_device *usbhid = hid->driver_data;

	if (!hid)
	if (WARN_ON(!usbhid))
		return;

	usbhid = hid->driver_data;

	spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
	usb_set_intfdata(intf, NULL);
	set_bit(HID_DISCONNECTED, &usbhid->iofl);
	spin_unlock_irq(&usbhid->inlock);
	usb_kill_urb(usbhid->urbin);
@@ -963,93 +1009,99 @@ static void hid_disconnect(struct usb_interface *intf)
	if (hid->claimed & HID_CLAIMED_HIDRAW)
		hidraw_disconnect(hid);

	hid->claimed = 0;

	usb_free_urb(usbhid->urbin);
	usb_free_urb(usbhid->urbctrl);
	usb_free_urb(usbhid->urbout);

	hid_free_buffers(hid_to_usb_dev(hid), hid);
	kfree(usbhid);
	hid_destroy_device(hid);
	hid->driver_data = NULL;
}

static struct hid_ll_driver usb_hid_driver = {
	.parse = usbhid_parse,
	.start = usbhid_start,
	.stop = usbhid_stop,
	.open = usbhid_open,
	.close = usbhid_close,
	.hidinput_input_event = usb_hidinput_input_event,
};

static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
	struct usb_device *dev = interface_to_usbdev(intf);
	struct hid_device *hid;
	char path[64];
	int i, ret;
	char *c;
	size_t len;
	int ret;

	dbg_hid("HID probe called for ifnum %d\n",
			intf->altsetting->desc.bInterfaceNumber);

	if (!(hid = usb_hid_configure(intf)))
		return -ENODEV;

	usbhid_init_reports(hid);
	hid_dump_device(hid);
	if (hid->quirks & HID_QUIRK_RESET_LEDS)
		usbhid_set_leds(hid);

	if (!hidinput_connect(hid))
		hid->claimed |= HID_CLAIMED_INPUT;
	if (!hiddev_connect(hid))
		hid->claimed |= HID_CLAIMED_HIDDEV;
	if (!hidraw_connect(hid))
		hid->claimed |= HID_CLAIMED_HIDRAW;
	hid = hid_allocate_device();
	if (IS_ERR(hid))
		return PTR_ERR(hid);

	usb_set_intfdata(intf, hid);
	hid->ll_driver = &usb_hid_driver;
	hid->hid_output_raw_report = usbhid_output_raw_report;
#ifdef CONFIG_USB_HIDDEV
	hid->hiddev_hid_event = hiddev_hid_event;
	hid->hiddev_report_event = hiddev_report_event;
#endif
	hid->dev.parent = &intf->dev;
	hid->bus = BUS_USB;
	hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
	hid->product = le16_to_cpu(dev->descriptor.idProduct);
	hid->name[0] = 0;

	if (!hid->claimed) {
		printk ("HID device claimed by neither input, hiddev nor hidraw\n");
		hid_disconnect(intf);
		return -ENODEV;
	}

	if ((hid->claimed & HID_CLAIMED_INPUT))
		hid_ff_init(hid);

	if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
		hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
			intf->cur_altsetting->desc.bInterfaceNumber);

	printk(KERN_INFO);

	if (hid->claimed & HID_CLAIMED_INPUT)
		printk("input");
	if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||
				hid->claimed & HID_CLAIMED_HIDRAW))
		printk(",");
	if (hid->claimed & HID_CLAIMED_HIDDEV)
		printk("hiddev%d", hid->minor);
	if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&
			(hid->claimed & HID_CLAIMED_HIDRAW))
		printk(",");
	if (hid->claimed & HID_CLAIMED_HIDRAW)
		printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);
	if (dev->manufacturer)
		strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));

	c = "Device";
	for (i = 0; i < hid->maxcollection; i++) {
		if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
		    (hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
		    (hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
			c = hid_types[hid->collection[i].usage & 0xffff];
			break;
		}
	if (dev->product) {
		if (dev->manufacturer)
			strlcat(hid->name, " ", sizeof(hid->name));
		strlcat(hid->name, dev->product, sizeof(hid->name));
	}

	usb_make_path(interface_to_usbdev(intf), path, 63);
	if (!strlen(hid->name))
		snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
			 le16_to_cpu(dev->descriptor.idVendor),
			 le16_to_cpu(dev->descriptor.idProduct));

	printk(": USB HID v%x.%02x %s [%s] on %s\n",
		hid->version >> 8, hid->version & 0xff, c, hid->name, path);
	usb_make_path(dev, hid->phys, sizeof(hid->phys));
	strlcat(hid->phys, "/input", sizeof(hid->phys));
	len = strlen(hid->phys);
	if (len < sizeof(hid->phys) - 1)
		snprintf(hid->phys + len, sizeof(hid->phys) - len,
			 "%d", intf->altsetting[0].desc.bInterfaceNumber);

	if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
		hid->uniq[0] = 0;

	ret = hid_add_device(hid);
	if (ret) {
		dev_err(&intf->dev, "can't add hid device: %d\n", ret);
		hid_disconnect(intf);
		goto err;
	}

	return 0;
err:
	hid_destroy_device(hid);
	return ret;
}

static void hid_disconnect(struct usb_interface *intf)
{
	struct hid_device *hid = usb_get_intfdata(intf);

	if (WARN_ON(!hid))
		return;

	hid_destroy_device(hid);
}

static int hid_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct hid_device *hid = usb_get_intfdata (intf);
+99 −5
Original line number Diff line number Diff line
@@ -419,6 +419,7 @@ struct hid_control_fifo {
#define HID_CLAIMED_HIDRAW	4

#define HID_STAT_ADDED		1
#define HID_STAT_PARSED		2

#define HID_CTRL_RUNNING	1
#define HID_OUT_RUNNING		2
@@ -435,6 +436,7 @@ struct hid_input {
};

struct hid_driver;
struct hid_ll_driver;

struct hid_device {							/* device report descriptor */
	__u8 *rdesc;
@@ -452,6 +454,7 @@ struct hid_device { /* device report descriptor */

	struct device dev;						/* device */
	struct hid_driver *driver;
	struct hid_ll_driver *ll_driver;

	unsigned int status;						/* see STAT flags above */
	unsigned claimed;						/* Claimed by hidinput, hiddev? */
@@ -471,11 +474,6 @@ struct hid_device { /* device report descriptor */

	__s32 delayed_value;						/* For A4 Tech mice hwheel quirk */

	/* device-specific function pointers */
	int (*hidinput_input_event) (struct input_dev *, unsigned int, unsigned int, int);
	int (*hid_open) (struct hid_device *);
	void (*hid_close) (struct hid_device *);

	/* hiddev event handler */
	void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field,
				  struct hid_usage *, __s32);
@@ -561,9 +559,22 @@ struct hid_usage_id {
 * @raw_event: if report in report_table, this hook is called (NULL means nop)
 * @usage_table: on which events to call event (NULL means all)
 * @event: if usage in usage_table, this hook is called (NULL means nop)
 * @report_fixup: called before report descriptor parsing (NULL means nop)
 * @input_mapping: invoked on input registering before mapping an usage
 * @input_mapped: invoked on input registering after mapping an usage
 *
 * raw_event and event should return 0 on no action performed, 1 when no
 * further processing should be done and negative on error
 *
 * input_mapping shall return a negative value to completely ignore this usage
 * (e.g. doubled or invalid usage), zero to continue with parsing of this
 * usage by generic code (no special handling needed) or positive to skip
 * generic parsing (needed special handling which was done in the hook already)
 * input_mapped shall return negative to inform the layer that this usage
 * should not be considered for further processing or zero to notify that
 * no processing was performed and should be done in a generic manner
 * Both these functions may be NULL which means the same behavior as returning
 * zero from them.
 */
struct hid_driver {
	char *name;
@@ -578,10 +589,43 @@ struct hid_driver {
	const struct hid_usage_id *usage_table;
	int (*event)(struct hid_device *hdev, struct hid_field *field,
			struct hid_usage *usage, __s32 value);

	void (*report_fixup)(struct hid_device *hdev, __u8 *buf,
			unsigned int size);

	int (*input_mapping)(struct hid_device *hdev,
			struct hid_input *hidinput, struct hid_field *field,
			struct hid_usage *usage, unsigned long **bit, int *max);
	int (*input_mapped)(struct hid_device *hdev,
			struct hid_input *hidinput, struct hid_field *field,
			struct hid_usage *usage, unsigned long **bit, int *max);
/* private: */
	struct device_driver driver;
};

/**
 * hid_ll_driver - low level driver callbacks
 * @start: called on probe to start the device
 * @stop: called on remove
 * @open: called by input layer on open
 * @close: called by input layer on close
 * @hidinput_input_event: event input event (e.g. ff or leds)
 * @parse: this method is called only once to parse the device data,
 *	   shouldn't allocate anything to not leak memory
 */
struct hid_ll_driver {
	int (*start)(struct hid_device *hdev);
	void (*stop)(struct hid_device *hdev);

	int (*open)(struct hid_device *hdev);
	void (*close)(struct hid_device *hdev);

	int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
			unsigned int code, int value);

	int (*parse)(struct hid_device *hdev);
};

/* Applications from HID Usage Tables 4/8/99 Version 1.1 */
/* We ignore a few input applications that are not widely used */
#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002))
@@ -618,6 +662,56 @@ void hid_output_report(struct hid_report *report, __u8 *data);
struct hid_device *hid_allocate_device(void);
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);

/**
 * hid_parse - parse HW reports
 *
 * @hdev: hid device
 *
 * Call this from probe after you set up the device (if needed). Your
 * report_fixup will be called (if non-NULL) after reading raw report from
 * device before passing it to hid layer for real parsing.
 */
static inline int __must_check hid_parse(struct hid_device *hdev)
{
	int ret;

	if (hdev->status & HID_STAT_PARSED)
		return 0;

	ret = hdev->ll_driver->parse(hdev);
	if (!ret)
		hdev->status |= HID_STAT_PARSED;

	return ret;
}

/**
 * hid_hw_start - start underlaying HW
 *
 * @hdev: hid device
 *
 * Call this in probe function *after* hid_parse. This will setup HW buffers
 * and start the device (if not deffered to device open). hid_hw_stop must be
 * called if this was successfull.
 */
static inline int __must_check hid_hw_start(struct hid_device *hdev)
{
	return hdev->ll_driver->start(hdev);
}

/**
 * hid_hw_stop - stop underlaying HW
 *
 * @hdev: hid device
 *
 * This is usually called from remove function or from probe when something
 * failed and hid_hw_start was called already.
 */
static inline void hid_hw_stop(struct hid_device *hdev)
{
	hdev->ll_driver->stop(hdev);
}

void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
		int interrupt);

Loading