162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * f_hid.c -- USB HID function driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/hid.h>
1162306a36Sopenharmony_ci#include <linux/idr.h>
1262306a36Sopenharmony_ci#include <linux/cdev.h>
1362306a36Sopenharmony_ci#include <linux/mutex.h>
1462306a36Sopenharmony_ci#include <linux/poll.h>
1562306a36Sopenharmony_ci#include <linux/uaccess.h>
1662306a36Sopenharmony_ci#include <linux/wait.h>
1762306a36Sopenharmony_ci#include <linux/sched.h>
1862306a36Sopenharmony_ci#include <linux/usb/g_hid.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "u_f.h"
2162306a36Sopenharmony_ci#include "u_hid.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define HIDG_MINORS	4
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int major, minors;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const struct class hidg_class = {
2862306a36Sopenharmony_ci	.name = "hidg",
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic DEFINE_IDA(hidg_ida);
3262306a36Sopenharmony_cistatic DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
3562306a36Sopenharmony_ci/*                            HID gadget struct                            */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct f_hidg_req_list {
3862306a36Sopenharmony_ci	struct usb_request	*req;
3962306a36Sopenharmony_ci	unsigned int		pos;
4062306a36Sopenharmony_ci	struct list_head 	list;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct f_hidg {
4462306a36Sopenharmony_ci	/* configuration */
4562306a36Sopenharmony_ci	unsigned char			bInterfaceSubClass;
4662306a36Sopenharmony_ci	unsigned char			bInterfaceProtocol;
4762306a36Sopenharmony_ci	unsigned char			protocol;
4862306a36Sopenharmony_ci	unsigned char			idle;
4962306a36Sopenharmony_ci	unsigned short			report_desc_length;
5062306a36Sopenharmony_ci	char				*report_desc;
5162306a36Sopenharmony_ci	unsigned short			report_length;
5262306a36Sopenharmony_ci	/*
5362306a36Sopenharmony_ci	 * use_out_ep - if true, the OUT Endpoint (interrupt out method)
5462306a36Sopenharmony_ci	 *              will be used to receive reports from the host
5562306a36Sopenharmony_ci	 *              using functions with the "intout" suffix.
5662306a36Sopenharmony_ci	 *              Otherwise, the OUT Endpoint will not be configured
5762306a36Sopenharmony_ci	 *              and the SETUP/SET_REPORT method ("ssreport" suffix)
5862306a36Sopenharmony_ci	 *              will be used to receive reports.
5962306a36Sopenharmony_ci	 */
6062306a36Sopenharmony_ci	bool				use_out_ep;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* recv report */
6362306a36Sopenharmony_ci	spinlock_t			read_spinlock;
6462306a36Sopenharmony_ci	wait_queue_head_t		read_queue;
6562306a36Sopenharmony_ci	/* recv report - interrupt out only (use_out_ep == 1) */
6662306a36Sopenharmony_ci	struct list_head		completed_out_req;
6762306a36Sopenharmony_ci	unsigned int			qlen;
6862306a36Sopenharmony_ci	/* recv report - setup set_report only (use_out_ep == 0) */
6962306a36Sopenharmony_ci	char				*set_report_buf;
7062306a36Sopenharmony_ci	unsigned int			set_report_length;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* send report */
7362306a36Sopenharmony_ci	spinlock_t			write_spinlock;
7462306a36Sopenharmony_ci	bool				write_pending;
7562306a36Sopenharmony_ci	wait_queue_head_t		write_queue;
7662306a36Sopenharmony_ci	struct usb_request		*req;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	struct device			dev;
7962306a36Sopenharmony_ci	struct cdev			cdev;
8062306a36Sopenharmony_ci	struct usb_function		func;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	struct usb_ep			*in_ep;
8362306a36Sopenharmony_ci	struct usb_ep			*out_ep;
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic inline struct f_hidg *func_to_hidg(struct usb_function *f)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	return container_of(f, struct f_hidg, func);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void hidg_release(struct device *dev)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	struct f_hidg *hidg = container_of(dev, struct f_hidg, dev);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	kfree(hidg->report_desc);
9662306a36Sopenharmony_ci	kfree(hidg->set_report_buf);
9762306a36Sopenharmony_ci	kfree(hidg);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
10162306a36Sopenharmony_ci/*                           Static descriptors                            */
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic struct usb_interface_descriptor hidg_interface_desc = {
10462306a36Sopenharmony_ci	.bLength		= sizeof hidg_interface_desc,
10562306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_INTERFACE,
10662306a36Sopenharmony_ci	/* .bInterfaceNumber	= DYNAMIC */
10762306a36Sopenharmony_ci	.bAlternateSetting	= 0,
10862306a36Sopenharmony_ci	/* .bNumEndpoints	= DYNAMIC (depends on use_out_ep) */
10962306a36Sopenharmony_ci	.bInterfaceClass	= USB_CLASS_HID,
11062306a36Sopenharmony_ci	/* .bInterfaceSubClass	= DYNAMIC */
11162306a36Sopenharmony_ci	/* .bInterfaceProtocol	= DYNAMIC */
11262306a36Sopenharmony_ci	/* .iInterface		= DYNAMIC */
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic struct hid_descriptor hidg_desc = {
11662306a36Sopenharmony_ci	.bLength			= sizeof hidg_desc,
11762306a36Sopenharmony_ci	.bDescriptorType		= HID_DT_HID,
11862306a36Sopenharmony_ci	.bcdHID				= cpu_to_le16(0x0101),
11962306a36Sopenharmony_ci	.bCountryCode			= 0x00,
12062306a36Sopenharmony_ci	.bNumDescriptors		= 0x1,
12162306a36Sopenharmony_ci	/*.desc[0].bDescriptorType	= DYNAMIC */
12262306a36Sopenharmony_ci	/*.desc[0].wDescriptorLenght	= DYNAMIC */
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci/* Super-Speed Support */
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hidg_ss_in_ep_desc = {
12862306a36Sopenharmony_ci	.bLength		= USB_DT_ENDPOINT_SIZE,
12962306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_ENDPOINT,
13062306a36Sopenharmony_ci	.bEndpointAddress	= USB_DIR_IN,
13162306a36Sopenharmony_ci	.bmAttributes		= USB_ENDPOINT_XFER_INT,
13262306a36Sopenharmony_ci	/*.wMaxPacketSize	= DYNAMIC */
13362306a36Sopenharmony_ci	.bInterval		= 4, /* FIXME: Add this field in the
13462306a36Sopenharmony_ci				      * HID gadget configuration?
13562306a36Sopenharmony_ci				      * (struct hidg_func_descriptor)
13662306a36Sopenharmony_ci				      */
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = {
14062306a36Sopenharmony_ci	.bLength                = sizeof(hidg_ss_in_comp_desc),
14162306a36Sopenharmony_ci	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* .bMaxBurst           = 0, */
14462306a36Sopenharmony_ci	/* .bmAttributes        = 0, */
14562306a36Sopenharmony_ci	/* .wBytesPerInterval   = DYNAMIC */
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hidg_ss_out_ep_desc = {
14962306a36Sopenharmony_ci	.bLength		= USB_DT_ENDPOINT_SIZE,
15062306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_ENDPOINT,
15162306a36Sopenharmony_ci	.bEndpointAddress	= USB_DIR_OUT,
15262306a36Sopenharmony_ci	.bmAttributes		= USB_ENDPOINT_XFER_INT,
15362306a36Sopenharmony_ci	/*.wMaxPacketSize	= DYNAMIC */
15462306a36Sopenharmony_ci	.bInterval		= 4, /* FIXME: Add this field in the
15562306a36Sopenharmony_ci				      * HID gadget configuration?
15662306a36Sopenharmony_ci				      * (struct hidg_func_descriptor)
15762306a36Sopenharmony_ci				      */
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = {
16162306a36Sopenharmony_ci	.bLength                = sizeof(hidg_ss_out_comp_desc),
16262306a36Sopenharmony_ci	.bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* .bMaxBurst           = 0, */
16562306a36Sopenharmony_ci	/* .bmAttributes        = 0, */
16662306a36Sopenharmony_ci	/* .wBytesPerInterval   = DYNAMIC */
16762306a36Sopenharmony_ci};
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic struct usb_descriptor_header *hidg_ss_descriptors_intout[] = {
17062306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_interface_desc,
17162306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_desc,
17262306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_ss_in_ep_desc,
17362306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_ss_in_comp_desc,
17462306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_ss_out_ep_desc,
17562306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_ss_out_comp_desc,
17662306a36Sopenharmony_ci	NULL,
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic struct usb_descriptor_header *hidg_ss_descriptors_ssreport[] = {
18062306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_interface_desc,
18162306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_desc,
18262306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_ss_in_ep_desc,
18362306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_ss_in_comp_desc,
18462306a36Sopenharmony_ci	NULL,
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/* High-Speed Support */
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
19062306a36Sopenharmony_ci	.bLength		= USB_DT_ENDPOINT_SIZE,
19162306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_ENDPOINT,
19262306a36Sopenharmony_ci	.bEndpointAddress	= USB_DIR_IN,
19362306a36Sopenharmony_ci	.bmAttributes		= USB_ENDPOINT_XFER_INT,
19462306a36Sopenharmony_ci	/*.wMaxPacketSize	= DYNAMIC */
19562306a36Sopenharmony_ci	.bInterval		= 4, /* FIXME: Add this field in the
19662306a36Sopenharmony_ci				      * HID gadget configuration?
19762306a36Sopenharmony_ci				      * (struct hidg_func_descriptor)
19862306a36Sopenharmony_ci				      */
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
20262306a36Sopenharmony_ci	.bLength		= USB_DT_ENDPOINT_SIZE,
20362306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_ENDPOINT,
20462306a36Sopenharmony_ci	.bEndpointAddress	= USB_DIR_OUT,
20562306a36Sopenharmony_ci	.bmAttributes		= USB_ENDPOINT_XFER_INT,
20662306a36Sopenharmony_ci	/*.wMaxPacketSize	= DYNAMIC */
20762306a36Sopenharmony_ci	.bInterval		= 4, /* FIXME: Add this field in the
20862306a36Sopenharmony_ci				      * HID gadget configuration?
20962306a36Sopenharmony_ci				      * (struct hidg_func_descriptor)
21062306a36Sopenharmony_ci				      */
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic struct usb_descriptor_header *hidg_hs_descriptors_intout[] = {
21462306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_interface_desc,
21562306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_desc,
21662306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
21762306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_hs_out_ep_desc,
21862306a36Sopenharmony_ci	NULL,
21962306a36Sopenharmony_ci};
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic struct usb_descriptor_header *hidg_hs_descriptors_ssreport[] = {
22262306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_interface_desc,
22362306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_desc,
22462306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
22562306a36Sopenharmony_ci	NULL,
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/* Full-Speed Support */
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
23162306a36Sopenharmony_ci	.bLength		= USB_DT_ENDPOINT_SIZE,
23262306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_ENDPOINT,
23362306a36Sopenharmony_ci	.bEndpointAddress	= USB_DIR_IN,
23462306a36Sopenharmony_ci	.bmAttributes		= USB_ENDPOINT_XFER_INT,
23562306a36Sopenharmony_ci	/*.wMaxPacketSize	= DYNAMIC */
23662306a36Sopenharmony_ci	.bInterval		= 10, /* FIXME: Add this field in the
23762306a36Sopenharmony_ci				       * HID gadget configuration?
23862306a36Sopenharmony_ci				       * (struct hidg_func_descriptor)
23962306a36Sopenharmony_ci				       */
24062306a36Sopenharmony_ci};
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
24362306a36Sopenharmony_ci	.bLength		= USB_DT_ENDPOINT_SIZE,
24462306a36Sopenharmony_ci	.bDescriptorType	= USB_DT_ENDPOINT,
24562306a36Sopenharmony_ci	.bEndpointAddress	= USB_DIR_OUT,
24662306a36Sopenharmony_ci	.bmAttributes		= USB_ENDPOINT_XFER_INT,
24762306a36Sopenharmony_ci	/*.wMaxPacketSize	= DYNAMIC */
24862306a36Sopenharmony_ci	.bInterval		= 10, /* FIXME: Add this field in the
24962306a36Sopenharmony_ci				       * HID gadget configuration?
25062306a36Sopenharmony_ci				       * (struct hidg_func_descriptor)
25162306a36Sopenharmony_ci				       */
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic struct usb_descriptor_header *hidg_fs_descriptors_intout[] = {
25562306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_interface_desc,
25662306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_desc,
25762306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
25862306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_fs_out_ep_desc,
25962306a36Sopenharmony_ci	NULL,
26062306a36Sopenharmony_ci};
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic struct usb_descriptor_header *hidg_fs_descriptors_ssreport[] = {
26362306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_interface_desc,
26462306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_desc,
26562306a36Sopenharmony_ci	(struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
26662306a36Sopenharmony_ci	NULL,
26762306a36Sopenharmony_ci};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
27062306a36Sopenharmony_ci/*                                 Strings                                 */
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci#define CT_FUNC_HID_IDX	0
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic struct usb_string ct_func_string_defs[] = {
27562306a36Sopenharmony_ci	[CT_FUNC_HID_IDX].s	= "HID Interface",
27662306a36Sopenharmony_ci	{},			/* end of list */
27762306a36Sopenharmony_ci};
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic struct usb_gadget_strings ct_func_string_table = {
28062306a36Sopenharmony_ci	.language	= 0x0409,	/* en-US */
28162306a36Sopenharmony_ci	.strings	= ct_func_string_defs,
28262306a36Sopenharmony_ci};
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic struct usb_gadget_strings *ct_func_strings[] = {
28562306a36Sopenharmony_ci	&ct_func_string_table,
28662306a36Sopenharmony_ci	NULL,
28762306a36Sopenharmony_ci};
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
29062306a36Sopenharmony_ci/*                              Char Device                                */
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
29362306a36Sopenharmony_ci				  size_t count, loff_t *ptr)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct f_hidg *hidg = file->private_data;
29662306a36Sopenharmony_ci	struct f_hidg_req_list *list;
29762306a36Sopenharmony_ci	struct usb_request *req;
29862306a36Sopenharmony_ci	unsigned long flags;
29962306a36Sopenharmony_ci	int ret;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (!count)
30262306a36Sopenharmony_ci		return 0;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	spin_lock_irqsave(&hidg->read_spinlock, flags);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req))
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* wait for at least one buffer to complete */
30962306a36Sopenharmony_ci	while (!READ_COND_INTOUT) {
31062306a36Sopenharmony_ci		spin_unlock_irqrestore(&hidg->read_spinlock, flags);
31162306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK)
31262306a36Sopenharmony_ci			return -EAGAIN;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		if (wait_event_interruptible(hidg->read_queue, READ_COND_INTOUT))
31562306a36Sopenharmony_ci			return -ERESTARTSYS;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		spin_lock_irqsave(&hidg->read_spinlock, flags);
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* pick the first one */
32162306a36Sopenharmony_ci	list = list_first_entry(&hidg->completed_out_req,
32262306a36Sopenharmony_ci				struct f_hidg_req_list, list);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/*
32562306a36Sopenharmony_ci	 * Remove this from list to protect it from beign free()
32662306a36Sopenharmony_ci	 * while host disables our function
32762306a36Sopenharmony_ci	 */
32862306a36Sopenharmony_ci	list_del(&list->list);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	req = list->req;
33162306a36Sopenharmony_ci	count = min_t(unsigned int, count, req->actual - list->pos);
33262306a36Sopenharmony_ci	spin_unlock_irqrestore(&hidg->read_spinlock, flags);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* copy to user outside spinlock */
33562306a36Sopenharmony_ci	count -= copy_to_user(buffer, req->buf + list->pos, count);
33662306a36Sopenharmony_ci	list->pos += count;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	/*
33962306a36Sopenharmony_ci	 * if this request is completely handled and transfered to
34062306a36Sopenharmony_ci	 * userspace, remove its entry from the list and requeue it
34162306a36Sopenharmony_ci	 * again. Otherwise, we will revisit it again upon the next
34262306a36Sopenharmony_ci	 * call, taking into account its current read position.
34362306a36Sopenharmony_ci	 */
34462306a36Sopenharmony_ci	if (list->pos == req->actual) {
34562306a36Sopenharmony_ci		kfree(list);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		req->length = hidg->report_length;
34862306a36Sopenharmony_ci		ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL);
34962306a36Sopenharmony_ci		if (ret < 0) {
35062306a36Sopenharmony_ci			free_ep_req(hidg->out_ep, req);
35162306a36Sopenharmony_ci			return ret;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci	} else {
35462306a36Sopenharmony_ci		spin_lock_irqsave(&hidg->read_spinlock, flags);
35562306a36Sopenharmony_ci		list_add(&list->list, &hidg->completed_out_req);
35662306a36Sopenharmony_ci		spin_unlock_irqrestore(&hidg->read_spinlock, flags);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci		wake_up(&hidg->read_queue);
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	return count;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci#define READ_COND_SSREPORT (hidg->set_report_buf != NULL)
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer,
36762306a36Sopenharmony_ci				    size_t count, loff_t *ptr)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct f_hidg *hidg = file->private_data;
37062306a36Sopenharmony_ci	char *tmp_buf = NULL;
37162306a36Sopenharmony_ci	unsigned long flags;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (!count)
37462306a36Sopenharmony_ci		return 0;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	spin_lock_irqsave(&hidg->read_spinlock, flags);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	while (!READ_COND_SSREPORT) {
37962306a36Sopenharmony_ci		spin_unlock_irqrestore(&hidg->read_spinlock, flags);
38062306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK)
38162306a36Sopenharmony_ci			return -EAGAIN;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		if (wait_event_interruptible(hidg->read_queue, READ_COND_SSREPORT))
38462306a36Sopenharmony_ci			return -ERESTARTSYS;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		spin_lock_irqsave(&hidg->read_spinlock, flags);
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	count = min_t(unsigned int, count, hidg->set_report_length);
39062306a36Sopenharmony_ci	tmp_buf = hidg->set_report_buf;
39162306a36Sopenharmony_ci	hidg->set_report_buf = NULL;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	spin_unlock_irqrestore(&hidg->read_spinlock, flags);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (tmp_buf != NULL) {
39662306a36Sopenharmony_ci		count -= copy_to_user(buffer, tmp_buf, count);
39762306a36Sopenharmony_ci		kfree(tmp_buf);
39862306a36Sopenharmony_ci	} else {
39962306a36Sopenharmony_ci		count = -ENOMEM;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	wake_up(&hidg->read_queue);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return count;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic ssize_t f_hidg_read(struct file *file, char __user *buffer,
40862306a36Sopenharmony_ci			   size_t count, loff_t *ptr)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct f_hidg *hidg = file->private_data;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (hidg->use_out_ep)
41362306a36Sopenharmony_ci		return f_hidg_intout_read(file, buffer, count, ptr);
41462306a36Sopenharmony_ci	else
41562306a36Sopenharmony_ci		return f_hidg_ssreport_read(file, buffer, count, ptr);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
42162306a36Sopenharmony_ci	unsigned long flags;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (req->status != 0) {
42462306a36Sopenharmony_ci		ERROR(hidg->func.config->cdev,
42562306a36Sopenharmony_ci			"End Point Request ERROR: %d\n", req->status);
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	spin_lock_irqsave(&hidg->write_spinlock, flags);
42962306a36Sopenharmony_ci	hidg->write_pending = 0;
43062306a36Sopenharmony_ci	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
43162306a36Sopenharmony_ci	wake_up(&hidg->write_queue);
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic ssize_t f_hidg_write(struct file *file, const char __user *buffer,
43562306a36Sopenharmony_ci			    size_t count, loff_t *offp)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct f_hidg *hidg  = file->private_data;
43862306a36Sopenharmony_ci	struct usb_request *req;
43962306a36Sopenharmony_ci	unsigned long flags;
44062306a36Sopenharmony_ci	ssize_t status = -ENOMEM;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	spin_lock_irqsave(&hidg->write_spinlock, flags);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	if (!hidg->req) {
44562306a36Sopenharmony_ci		spin_unlock_irqrestore(&hidg->write_spinlock, flags);
44662306a36Sopenharmony_ci		return -ESHUTDOWN;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci#define WRITE_COND (!hidg->write_pending)
45062306a36Sopenharmony_citry_again:
45162306a36Sopenharmony_ci	/* write queue */
45262306a36Sopenharmony_ci	while (!WRITE_COND) {
45362306a36Sopenharmony_ci		spin_unlock_irqrestore(&hidg->write_spinlock, flags);
45462306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK)
45562306a36Sopenharmony_ci			return -EAGAIN;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci		if (wait_event_interruptible_exclusive(
45862306a36Sopenharmony_ci				hidg->write_queue, WRITE_COND))
45962306a36Sopenharmony_ci			return -ERESTARTSYS;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		spin_lock_irqsave(&hidg->write_spinlock, flags);
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	hidg->write_pending = 1;
46562306a36Sopenharmony_ci	req = hidg->req;
46662306a36Sopenharmony_ci	count  = min_t(unsigned, count, hidg->report_length);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (!req) {
47162306a36Sopenharmony_ci		ERROR(hidg->func.config->cdev, "hidg->req is NULL\n");
47262306a36Sopenharmony_ci		status = -ESHUTDOWN;
47362306a36Sopenharmony_ci		goto release_write_pending;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	status = copy_from_user(req->buf, buffer, count);
47762306a36Sopenharmony_ci	if (status != 0) {
47862306a36Sopenharmony_ci		ERROR(hidg->func.config->cdev,
47962306a36Sopenharmony_ci			"copy_from_user error\n");
48062306a36Sopenharmony_ci		status = -EINVAL;
48162306a36Sopenharmony_ci		goto release_write_pending;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	spin_lock_irqsave(&hidg->write_spinlock, flags);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	/* when our function has been disabled by host */
48762306a36Sopenharmony_ci	if (!hidg->req) {
48862306a36Sopenharmony_ci		free_ep_req(hidg->in_ep, req);
48962306a36Sopenharmony_ci		/*
49062306a36Sopenharmony_ci		 * TODO
49162306a36Sopenharmony_ci		 * Should we fail with error here?
49262306a36Sopenharmony_ci		 */
49362306a36Sopenharmony_ci		goto try_again;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	req->status   = 0;
49762306a36Sopenharmony_ci	req->zero     = 0;
49862306a36Sopenharmony_ci	req->length   = count;
49962306a36Sopenharmony_ci	req->complete = f_hidg_req_complete;
50062306a36Sopenharmony_ci	req->context  = hidg;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (!hidg->in_ep->enabled) {
50562306a36Sopenharmony_ci		ERROR(hidg->func.config->cdev, "in_ep is disabled\n");
50662306a36Sopenharmony_ci		status = -ESHUTDOWN;
50762306a36Sopenharmony_ci		goto release_write_pending;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC);
51162306a36Sopenharmony_ci	if (status < 0)
51262306a36Sopenharmony_ci		goto release_write_pending;
51362306a36Sopenharmony_ci	else
51462306a36Sopenharmony_ci		status = count;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	return status;
51762306a36Sopenharmony_cirelease_write_pending:
51862306a36Sopenharmony_ci	spin_lock_irqsave(&hidg->write_spinlock, flags);
51962306a36Sopenharmony_ci	hidg->write_pending = 0;
52062306a36Sopenharmony_ci	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	wake_up(&hidg->write_queue);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return status;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic __poll_t f_hidg_poll(struct file *file, poll_table *wait)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct f_hidg	*hidg  = file->private_data;
53062306a36Sopenharmony_ci	__poll_t	ret = 0;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	poll_wait(file, &hidg->read_queue, wait);
53362306a36Sopenharmony_ci	poll_wait(file, &hidg->write_queue, wait);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (WRITE_COND)
53662306a36Sopenharmony_ci		ret |= EPOLLOUT | EPOLLWRNORM;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	if (hidg->use_out_ep) {
53962306a36Sopenharmony_ci		if (READ_COND_INTOUT)
54062306a36Sopenharmony_ci			ret |= EPOLLIN | EPOLLRDNORM;
54162306a36Sopenharmony_ci	} else {
54262306a36Sopenharmony_ci		if (READ_COND_SSREPORT)
54362306a36Sopenharmony_ci			ret |= EPOLLIN | EPOLLRDNORM;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	return ret;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci#undef WRITE_COND
55062306a36Sopenharmony_ci#undef READ_COND_SSREPORT
55162306a36Sopenharmony_ci#undef READ_COND_INTOUT
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic int f_hidg_release(struct inode *inode, struct file *fd)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	fd->private_data = NULL;
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int f_hidg_open(struct inode *inode, struct file *fd)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct f_hidg *hidg =
56262306a36Sopenharmony_ci		container_of(inode->i_cdev, struct f_hidg, cdev);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	fd->private_data = hidg;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	return 0;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
57062306a36Sopenharmony_ci/*                                usb_function                             */
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
57362306a36Sopenharmony_ci						    unsigned length)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	return alloc_ep_req(ep, length);
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic void hidg_intout_complete(struct usb_ep *ep, struct usb_request *req)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct f_hidg *hidg = (struct f_hidg *) req->context;
58162306a36Sopenharmony_ci	struct usb_composite_dev *cdev = hidg->func.config->cdev;
58262306a36Sopenharmony_ci	struct f_hidg_req_list *req_list;
58362306a36Sopenharmony_ci	unsigned long flags;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	switch (req->status) {
58662306a36Sopenharmony_ci	case 0:
58762306a36Sopenharmony_ci		req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
58862306a36Sopenharmony_ci		if (!req_list) {
58962306a36Sopenharmony_ci			ERROR(cdev, "Unable to allocate mem for req_list\n");
59062306a36Sopenharmony_ci			goto free_req;
59162306a36Sopenharmony_ci		}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		req_list->req = req;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		spin_lock_irqsave(&hidg->read_spinlock, flags);
59662306a36Sopenharmony_ci		list_add_tail(&req_list->list, &hidg->completed_out_req);
59762306a36Sopenharmony_ci		spin_unlock_irqrestore(&hidg->read_spinlock, flags);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		wake_up(&hidg->read_queue);
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci	default:
60262306a36Sopenharmony_ci		ERROR(cdev, "Set report failed %d\n", req->status);
60362306a36Sopenharmony_ci		fallthrough;
60462306a36Sopenharmony_ci	case -ECONNABORTED:		/* hardware forced ep reset */
60562306a36Sopenharmony_ci	case -ECONNRESET:		/* request dequeued */
60662306a36Sopenharmony_ci	case -ESHUTDOWN:		/* disconnect from host */
60762306a36Sopenharmony_cifree_req:
60862306a36Sopenharmony_ci		free_ep_req(ep, req);
60962306a36Sopenharmony_ci		return;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void hidg_ssreport_complete(struct usb_ep *ep, struct usb_request *req)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct f_hidg *hidg = (struct f_hidg *)req->context;
61662306a36Sopenharmony_ci	struct usb_composite_dev *cdev = hidg->func.config->cdev;
61762306a36Sopenharmony_ci	char *new_buf = NULL;
61862306a36Sopenharmony_ci	unsigned long flags;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (req->status != 0 || req->buf == NULL || req->actual == 0) {
62162306a36Sopenharmony_ci		ERROR(cdev,
62262306a36Sopenharmony_ci		      "%s FAILED: status=%d, buf=%p, actual=%d\n",
62362306a36Sopenharmony_ci		      __func__, req->status, req->buf, req->actual);
62462306a36Sopenharmony_ci		return;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	spin_lock_irqsave(&hidg->read_spinlock, flags);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	new_buf = krealloc(hidg->set_report_buf, req->actual, GFP_ATOMIC);
63062306a36Sopenharmony_ci	if (new_buf == NULL) {
63162306a36Sopenharmony_ci		spin_unlock_irqrestore(&hidg->read_spinlock, flags);
63262306a36Sopenharmony_ci		return;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci	hidg->set_report_buf = new_buf;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	hidg->set_report_length = req->actual;
63762306a36Sopenharmony_ci	memcpy(hidg->set_report_buf, req->buf, req->actual);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	spin_unlock_irqrestore(&hidg->read_spinlock, flags);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	wake_up(&hidg->read_queue);
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int hidg_setup(struct usb_function *f,
64562306a36Sopenharmony_ci		const struct usb_ctrlrequest *ctrl)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	struct f_hidg			*hidg = func_to_hidg(f);
64862306a36Sopenharmony_ci	struct usb_composite_dev	*cdev = f->config->cdev;
64962306a36Sopenharmony_ci	struct usb_request		*req  = cdev->req;
65062306a36Sopenharmony_ci	int status = 0;
65162306a36Sopenharmony_ci	__u16 value, length;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	value	= __le16_to_cpu(ctrl->wValue);
65462306a36Sopenharmony_ci	length	= __le16_to_cpu(ctrl->wLength);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	VDBG(cdev,
65762306a36Sopenharmony_ci	     "%s crtl_request : bRequestType:0x%x bRequest:0x%x Value:0x%x\n",
65862306a36Sopenharmony_ci	     __func__, ctrl->bRequestType, ctrl->bRequest, value);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
66162306a36Sopenharmony_ci	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
66262306a36Sopenharmony_ci		  | HID_REQ_GET_REPORT):
66362306a36Sopenharmony_ci		VDBG(cdev, "get_report\n");
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		/* send an empty report */
66662306a36Sopenharmony_ci		length = min_t(unsigned, length, hidg->report_length);
66762306a36Sopenharmony_ci		memset(req->buf, 0x0, length);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		goto respond;
67062306a36Sopenharmony_ci		break;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
67362306a36Sopenharmony_ci		  | HID_REQ_GET_PROTOCOL):
67462306a36Sopenharmony_ci		VDBG(cdev, "get_protocol\n");
67562306a36Sopenharmony_ci		length = min_t(unsigned int, length, 1);
67662306a36Sopenharmony_ci		((u8 *) req->buf)[0] = hidg->protocol;
67762306a36Sopenharmony_ci		goto respond;
67862306a36Sopenharmony_ci		break;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
68162306a36Sopenharmony_ci		  | HID_REQ_GET_IDLE):
68262306a36Sopenharmony_ci		VDBG(cdev, "get_idle\n");
68362306a36Sopenharmony_ci		length = min_t(unsigned int, length, 1);
68462306a36Sopenharmony_ci		((u8 *) req->buf)[0] = hidg->idle;
68562306a36Sopenharmony_ci		goto respond;
68662306a36Sopenharmony_ci		break;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
68962306a36Sopenharmony_ci		  | HID_REQ_SET_REPORT):
69062306a36Sopenharmony_ci		VDBG(cdev, "set_report | wLength=%d\n", ctrl->wLength);
69162306a36Sopenharmony_ci		if (hidg->use_out_ep)
69262306a36Sopenharmony_ci			goto stall;
69362306a36Sopenharmony_ci		req->complete = hidg_ssreport_complete;
69462306a36Sopenharmony_ci		req->context  = hidg;
69562306a36Sopenharmony_ci		goto respond;
69662306a36Sopenharmony_ci		break;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
69962306a36Sopenharmony_ci		  | HID_REQ_SET_PROTOCOL):
70062306a36Sopenharmony_ci		VDBG(cdev, "set_protocol\n");
70162306a36Sopenharmony_ci		if (value > HID_REPORT_PROTOCOL)
70262306a36Sopenharmony_ci			goto stall;
70362306a36Sopenharmony_ci		length = 0;
70462306a36Sopenharmony_ci		/*
70562306a36Sopenharmony_ci		 * We assume that programs implementing the Boot protocol
70662306a36Sopenharmony_ci		 * are also compatible with the Report Protocol
70762306a36Sopenharmony_ci		 */
70862306a36Sopenharmony_ci		if (hidg->bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
70962306a36Sopenharmony_ci			hidg->protocol = value;
71062306a36Sopenharmony_ci			goto respond;
71162306a36Sopenharmony_ci		}
71262306a36Sopenharmony_ci		goto stall;
71362306a36Sopenharmony_ci		break;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
71662306a36Sopenharmony_ci		  | HID_REQ_SET_IDLE):
71762306a36Sopenharmony_ci		VDBG(cdev, "set_idle\n");
71862306a36Sopenharmony_ci		length = 0;
71962306a36Sopenharmony_ci		hidg->idle = value >> 8;
72062306a36Sopenharmony_ci		goto respond;
72162306a36Sopenharmony_ci		break;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
72462306a36Sopenharmony_ci		  | USB_REQ_GET_DESCRIPTOR):
72562306a36Sopenharmony_ci		switch (value >> 8) {
72662306a36Sopenharmony_ci		case HID_DT_HID:
72762306a36Sopenharmony_ci		{
72862306a36Sopenharmony_ci			struct hid_descriptor hidg_desc_copy = hidg_desc;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci			VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
73162306a36Sopenharmony_ci			hidg_desc_copy.desc[0].bDescriptorType = HID_DT_REPORT;
73262306a36Sopenharmony_ci			hidg_desc_copy.desc[0].wDescriptorLength =
73362306a36Sopenharmony_ci				cpu_to_le16(hidg->report_desc_length);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci			length = min_t(unsigned short, length,
73662306a36Sopenharmony_ci						   hidg_desc_copy.bLength);
73762306a36Sopenharmony_ci			memcpy(req->buf, &hidg_desc_copy, length);
73862306a36Sopenharmony_ci			goto respond;
73962306a36Sopenharmony_ci			break;
74062306a36Sopenharmony_ci		}
74162306a36Sopenharmony_ci		case HID_DT_REPORT:
74262306a36Sopenharmony_ci			VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
74362306a36Sopenharmony_ci			length = min_t(unsigned short, length,
74462306a36Sopenharmony_ci						   hidg->report_desc_length);
74562306a36Sopenharmony_ci			memcpy(req->buf, hidg->report_desc, length);
74662306a36Sopenharmony_ci			goto respond;
74762306a36Sopenharmony_ci			break;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		default:
75062306a36Sopenharmony_ci			VDBG(cdev, "Unknown descriptor request 0x%x\n",
75162306a36Sopenharmony_ci				 value >> 8);
75262306a36Sopenharmony_ci			goto stall;
75362306a36Sopenharmony_ci			break;
75462306a36Sopenharmony_ci		}
75562306a36Sopenharmony_ci		break;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	default:
75862306a36Sopenharmony_ci		VDBG(cdev, "Unknown request 0x%x\n",
75962306a36Sopenharmony_ci			 ctrl->bRequest);
76062306a36Sopenharmony_ci		goto stall;
76162306a36Sopenharmony_ci		break;
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cistall:
76562306a36Sopenharmony_ci	return -EOPNOTSUPP;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cirespond:
76862306a36Sopenharmony_ci	req->zero = 0;
76962306a36Sopenharmony_ci	req->length = length;
77062306a36Sopenharmony_ci	status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
77162306a36Sopenharmony_ci	if (status < 0)
77262306a36Sopenharmony_ci		ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value);
77362306a36Sopenharmony_ci	return status;
77462306a36Sopenharmony_ci}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_cistatic void hidg_disable(struct usb_function *f)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	struct f_hidg *hidg = func_to_hidg(f);
77962306a36Sopenharmony_ci	struct f_hidg_req_list *list, *next;
78062306a36Sopenharmony_ci	unsigned long flags;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	usb_ep_disable(hidg->in_ep);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (hidg->out_ep) {
78562306a36Sopenharmony_ci		usb_ep_disable(hidg->out_ep);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		spin_lock_irqsave(&hidg->read_spinlock, flags);
78862306a36Sopenharmony_ci		list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
78962306a36Sopenharmony_ci			free_ep_req(hidg->out_ep, list->req);
79062306a36Sopenharmony_ci			list_del(&list->list);
79162306a36Sopenharmony_ci			kfree(list);
79262306a36Sopenharmony_ci		}
79362306a36Sopenharmony_ci		spin_unlock_irqrestore(&hidg->read_spinlock, flags);
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	spin_lock_irqsave(&hidg->write_spinlock, flags);
79762306a36Sopenharmony_ci	if (!hidg->write_pending) {
79862306a36Sopenharmony_ci		free_ep_req(hidg->in_ep, hidg->req);
79962306a36Sopenharmony_ci		hidg->write_pending = 1;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	hidg->req = NULL;
80362306a36Sopenharmony_ci	spin_unlock_irqrestore(&hidg->write_spinlock, flags);
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct usb_composite_dev		*cdev = f->config->cdev;
80962306a36Sopenharmony_ci	struct f_hidg				*hidg = func_to_hidg(f);
81062306a36Sopenharmony_ci	struct usb_request			*req_in = NULL;
81162306a36Sopenharmony_ci	unsigned long				flags;
81262306a36Sopenharmony_ci	int i, status = 0;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (hidg->in_ep != NULL) {
81762306a36Sopenharmony_ci		/* restart endpoint */
81862306a36Sopenharmony_ci		usb_ep_disable(hidg->in_ep);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		status = config_ep_by_speed(f->config->cdev->gadget, f,
82162306a36Sopenharmony_ci					    hidg->in_ep);
82262306a36Sopenharmony_ci		if (status) {
82362306a36Sopenharmony_ci			ERROR(cdev, "config_ep_by_speed FAILED!\n");
82462306a36Sopenharmony_ci			goto fail;
82562306a36Sopenharmony_ci		}
82662306a36Sopenharmony_ci		status = usb_ep_enable(hidg->in_ep);
82762306a36Sopenharmony_ci		if (status < 0) {
82862306a36Sopenharmony_ci			ERROR(cdev, "Enable IN endpoint FAILED!\n");
82962306a36Sopenharmony_ci			goto fail;
83062306a36Sopenharmony_ci		}
83162306a36Sopenharmony_ci		hidg->in_ep->driver_data = hidg;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		req_in = hidg_alloc_ep_req(hidg->in_ep, hidg->report_length);
83462306a36Sopenharmony_ci		if (!req_in) {
83562306a36Sopenharmony_ci			status = -ENOMEM;
83662306a36Sopenharmony_ci			goto disable_ep_in;
83762306a36Sopenharmony_ci		}
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	if (hidg->use_out_ep && hidg->out_ep != NULL) {
84162306a36Sopenharmony_ci		/* restart endpoint */
84262306a36Sopenharmony_ci		usb_ep_disable(hidg->out_ep);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		status = config_ep_by_speed(f->config->cdev->gadget, f,
84562306a36Sopenharmony_ci					    hidg->out_ep);
84662306a36Sopenharmony_ci		if (status) {
84762306a36Sopenharmony_ci			ERROR(cdev, "config_ep_by_speed FAILED!\n");
84862306a36Sopenharmony_ci			goto free_req_in;
84962306a36Sopenharmony_ci		}
85062306a36Sopenharmony_ci		status = usb_ep_enable(hidg->out_ep);
85162306a36Sopenharmony_ci		if (status < 0) {
85262306a36Sopenharmony_ci			ERROR(cdev, "Enable OUT endpoint FAILED!\n");
85362306a36Sopenharmony_ci			goto free_req_in;
85462306a36Sopenharmony_ci		}
85562306a36Sopenharmony_ci		hidg->out_ep->driver_data = hidg;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci		/*
85862306a36Sopenharmony_ci		 * allocate a bunch of read buffers and queue them all at once.
85962306a36Sopenharmony_ci		 */
86062306a36Sopenharmony_ci		for (i = 0; i < hidg->qlen && status == 0; i++) {
86162306a36Sopenharmony_ci			struct usb_request *req =
86262306a36Sopenharmony_ci					hidg_alloc_ep_req(hidg->out_ep,
86362306a36Sopenharmony_ci							  hidg->report_length);
86462306a36Sopenharmony_ci			if (req) {
86562306a36Sopenharmony_ci				req->complete = hidg_intout_complete;
86662306a36Sopenharmony_ci				req->context  = hidg;
86762306a36Sopenharmony_ci				status = usb_ep_queue(hidg->out_ep, req,
86862306a36Sopenharmony_ci						      GFP_ATOMIC);
86962306a36Sopenharmony_ci				if (status) {
87062306a36Sopenharmony_ci					ERROR(cdev, "%s queue req --> %d\n",
87162306a36Sopenharmony_ci						hidg->out_ep->name, status);
87262306a36Sopenharmony_ci					free_ep_req(hidg->out_ep, req);
87362306a36Sopenharmony_ci				}
87462306a36Sopenharmony_ci			} else {
87562306a36Sopenharmony_ci				status = -ENOMEM;
87662306a36Sopenharmony_ci				goto disable_out_ep;
87762306a36Sopenharmony_ci			}
87862306a36Sopenharmony_ci		}
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	if (hidg->in_ep != NULL) {
88262306a36Sopenharmony_ci		spin_lock_irqsave(&hidg->write_spinlock, flags);
88362306a36Sopenharmony_ci		hidg->req = req_in;
88462306a36Sopenharmony_ci		hidg->write_pending = 0;
88562306a36Sopenharmony_ci		spin_unlock_irqrestore(&hidg->write_spinlock, flags);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci		wake_up(&hidg->write_queue);
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci	return 0;
89062306a36Sopenharmony_cidisable_out_ep:
89162306a36Sopenharmony_ci	if (hidg->out_ep)
89262306a36Sopenharmony_ci		usb_ep_disable(hidg->out_ep);
89362306a36Sopenharmony_cifree_req_in:
89462306a36Sopenharmony_ci	if (req_in)
89562306a36Sopenharmony_ci		free_ep_req(hidg->in_ep, req_in);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cidisable_ep_in:
89862306a36Sopenharmony_ci	if (hidg->in_ep)
89962306a36Sopenharmony_ci		usb_ep_disable(hidg->in_ep);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cifail:
90262306a36Sopenharmony_ci	return status;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic const struct file_operations f_hidg_fops = {
90662306a36Sopenharmony_ci	.owner		= THIS_MODULE,
90762306a36Sopenharmony_ci	.open		= f_hidg_open,
90862306a36Sopenharmony_ci	.release	= f_hidg_release,
90962306a36Sopenharmony_ci	.write		= f_hidg_write,
91062306a36Sopenharmony_ci	.read		= f_hidg_read,
91162306a36Sopenharmony_ci	.poll		= f_hidg_poll,
91262306a36Sopenharmony_ci	.llseek		= noop_llseek,
91362306a36Sopenharmony_ci};
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_cistatic int hidg_bind(struct usb_configuration *c, struct usb_function *f)
91662306a36Sopenharmony_ci{
91762306a36Sopenharmony_ci	struct usb_ep		*ep;
91862306a36Sopenharmony_ci	struct f_hidg		*hidg = func_to_hidg(f);
91962306a36Sopenharmony_ci	struct usb_string	*us;
92062306a36Sopenharmony_ci	int			status;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	/* maybe allocate device-global string IDs, and patch descriptors */
92362306a36Sopenharmony_ci	us = usb_gstrings_attach(c->cdev, ct_func_strings,
92462306a36Sopenharmony_ci				 ARRAY_SIZE(ct_func_string_defs));
92562306a36Sopenharmony_ci	if (IS_ERR(us))
92662306a36Sopenharmony_ci		return PTR_ERR(us);
92762306a36Sopenharmony_ci	hidg_interface_desc.iInterface = us[CT_FUNC_HID_IDX].id;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	/* allocate instance-specific interface IDs, and patch descriptors */
93062306a36Sopenharmony_ci	status = usb_interface_id(c, f);
93162306a36Sopenharmony_ci	if (status < 0)
93262306a36Sopenharmony_ci		goto fail;
93362306a36Sopenharmony_ci	hidg_interface_desc.bInterfaceNumber = status;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	/* allocate instance-specific endpoints */
93662306a36Sopenharmony_ci	status = -ENODEV;
93762306a36Sopenharmony_ci	ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc);
93862306a36Sopenharmony_ci	if (!ep)
93962306a36Sopenharmony_ci		goto fail;
94062306a36Sopenharmony_ci	hidg->in_ep = ep;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	hidg->out_ep = NULL;
94362306a36Sopenharmony_ci	if (hidg->use_out_ep) {
94462306a36Sopenharmony_ci		ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
94562306a36Sopenharmony_ci		if (!ep)
94662306a36Sopenharmony_ci			goto fail;
94762306a36Sopenharmony_ci		hidg->out_ep = ep;
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/* used only if use_out_ep == 1 */
95162306a36Sopenharmony_ci	hidg->set_report_buf = NULL;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	/* set descriptor dynamic values */
95462306a36Sopenharmony_ci	hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
95562306a36Sopenharmony_ci	hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
95662306a36Sopenharmony_ci	hidg_interface_desc.bNumEndpoints = hidg->use_out_ep ? 2 : 1;
95762306a36Sopenharmony_ci	hidg->protocol = HID_REPORT_PROTOCOL;
95862306a36Sopenharmony_ci	hidg->idle = 1;
95962306a36Sopenharmony_ci	hidg_ss_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
96062306a36Sopenharmony_ci	hidg_ss_in_comp_desc.wBytesPerInterval =
96162306a36Sopenharmony_ci				cpu_to_le16(hidg->report_length);
96262306a36Sopenharmony_ci	hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
96362306a36Sopenharmony_ci	hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
96462306a36Sopenharmony_ci	hidg_ss_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
96562306a36Sopenharmony_ci	hidg_ss_out_comp_desc.wBytesPerInterval =
96662306a36Sopenharmony_ci				cpu_to_le16(hidg->report_length);
96762306a36Sopenharmony_ci	hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
96862306a36Sopenharmony_ci	hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
96962306a36Sopenharmony_ci	/*
97062306a36Sopenharmony_ci	 * We can use hidg_desc struct here but we should not relay
97162306a36Sopenharmony_ci	 * that its content won't change after returning from this function.
97262306a36Sopenharmony_ci	 */
97362306a36Sopenharmony_ci	hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
97462306a36Sopenharmony_ci	hidg_desc.desc[0].wDescriptorLength =
97562306a36Sopenharmony_ci		cpu_to_le16(hidg->report_desc_length);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	hidg_hs_in_ep_desc.bEndpointAddress =
97862306a36Sopenharmony_ci		hidg_fs_in_ep_desc.bEndpointAddress;
97962306a36Sopenharmony_ci	hidg_hs_out_ep_desc.bEndpointAddress =
98062306a36Sopenharmony_ci		hidg_fs_out_ep_desc.bEndpointAddress;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	hidg_ss_in_ep_desc.bEndpointAddress =
98362306a36Sopenharmony_ci		hidg_fs_in_ep_desc.bEndpointAddress;
98462306a36Sopenharmony_ci	hidg_ss_out_ep_desc.bEndpointAddress =
98562306a36Sopenharmony_ci		hidg_fs_out_ep_desc.bEndpointAddress;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	if (hidg->use_out_ep)
98862306a36Sopenharmony_ci		status = usb_assign_descriptors(f,
98962306a36Sopenharmony_ci			hidg_fs_descriptors_intout,
99062306a36Sopenharmony_ci			hidg_hs_descriptors_intout,
99162306a36Sopenharmony_ci			hidg_ss_descriptors_intout,
99262306a36Sopenharmony_ci			hidg_ss_descriptors_intout);
99362306a36Sopenharmony_ci	else
99462306a36Sopenharmony_ci		status = usb_assign_descriptors(f,
99562306a36Sopenharmony_ci			hidg_fs_descriptors_ssreport,
99662306a36Sopenharmony_ci			hidg_hs_descriptors_ssreport,
99762306a36Sopenharmony_ci			hidg_ss_descriptors_ssreport,
99862306a36Sopenharmony_ci			hidg_ss_descriptors_ssreport);
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	if (status)
100162306a36Sopenharmony_ci		goto fail;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	spin_lock_init(&hidg->write_spinlock);
100462306a36Sopenharmony_ci	hidg->write_pending = 1;
100562306a36Sopenharmony_ci	hidg->req = NULL;
100662306a36Sopenharmony_ci	spin_lock_init(&hidg->read_spinlock);
100762306a36Sopenharmony_ci	init_waitqueue_head(&hidg->write_queue);
100862306a36Sopenharmony_ci	init_waitqueue_head(&hidg->read_queue);
100962306a36Sopenharmony_ci	INIT_LIST_HEAD(&hidg->completed_out_req);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	/* create char device */
101262306a36Sopenharmony_ci	cdev_init(&hidg->cdev, &f_hidg_fops);
101362306a36Sopenharmony_ci	status = cdev_device_add(&hidg->cdev, &hidg->dev);
101462306a36Sopenharmony_ci	if (status)
101562306a36Sopenharmony_ci		goto fail_free_descs;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	return 0;
101862306a36Sopenharmony_cifail_free_descs:
101962306a36Sopenharmony_ci	usb_free_all_descriptors(f);
102062306a36Sopenharmony_cifail:
102162306a36Sopenharmony_ci	ERROR(f->config->cdev, "hidg_bind FAILED\n");
102262306a36Sopenharmony_ci	if (hidg->req != NULL)
102362306a36Sopenharmony_ci		free_ep_req(hidg->in_ep, hidg->req);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	return status;
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic inline int hidg_get_minor(void)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	int ret;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL);
103362306a36Sopenharmony_ci	if (ret >= HIDG_MINORS) {
103462306a36Sopenharmony_ci		ida_simple_remove(&hidg_ida, ret);
103562306a36Sopenharmony_ci		ret = -ENODEV;
103662306a36Sopenharmony_ci	}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	return ret;
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic inline struct f_hid_opts *to_f_hid_opts(struct config_item *item)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	return container_of(to_config_group(item), struct f_hid_opts,
104462306a36Sopenharmony_ci			    func_inst.group);
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistatic void hid_attr_release(struct config_item *item)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	struct f_hid_opts *opts = to_f_hid_opts(item);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	usb_put_function_instance(&opts->func_inst);
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_cistatic struct configfs_item_operations hidg_item_ops = {
105562306a36Sopenharmony_ci	.release	= hid_attr_release,
105662306a36Sopenharmony_ci};
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci#define F_HID_OPT(name, prec, limit)					\
105962306a36Sopenharmony_cistatic ssize_t f_hid_opts_##name##_show(struct config_item *item, char *page)\
106062306a36Sopenharmony_ci{									\
106162306a36Sopenharmony_ci	struct f_hid_opts *opts = to_f_hid_opts(item);			\
106262306a36Sopenharmony_ci	int result;							\
106362306a36Sopenharmony_ci									\
106462306a36Sopenharmony_ci	mutex_lock(&opts->lock);					\
106562306a36Sopenharmony_ci	result = sprintf(page, "%d\n", opts->name);			\
106662306a36Sopenharmony_ci	mutex_unlock(&opts->lock);					\
106762306a36Sopenharmony_ci									\
106862306a36Sopenharmony_ci	return result;							\
106962306a36Sopenharmony_ci}									\
107062306a36Sopenharmony_ci									\
107162306a36Sopenharmony_cistatic ssize_t f_hid_opts_##name##_store(struct config_item *item,	\
107262306a36Sopenharmony_ci					 const char *page, size_t len)	\
107362306a36Sopenharmony_ci{									\
107462306a36Sopenharmony_ci	struct f_hid_opts *opts = to_f_hid_opts(item);			\
107562306a36Sopenharmony_ci	int ret;							\
107662306a36Sopenharmony_ci	u##prec num;							\
107762306a36Sopenharmony_ci									\
107862306a36Sopenharmony_ci	mutex_lock(&opts->lock);					\
107962306a36Sopenharmony_ci	if (opts->refcnt) {						\
108062306a36Sopenharmony_ci		ret = -EBUSY;						\
108162306a36Sopenharmony_ci		goto end;						\
108262306a36Sopenharmony_ci	}								\
108362306a36Sopenharmony_ci									\
108462306a36Sopenharmony_ci	ret = kstrtou##prec(page, 0, &num);				\
108562306a36Sopenharmony_ci	if (ret)							\
108662306a36Sopenharmony_ci		goto end;						\
108762306a36Sopenharmony_ci									\
108862306a36Sopenharmony_ci	if (num > limit) {						\
108962306a36Sopenharmony_ci		ret = -EINVAL;						\
109062306a36Sopenharmony_ci		goto end;						\
109162306a36Sopenharmony_ci	}								\
109262306a36Sopenharmony_ci	opts->name = num;						\
109362306a36Sopenharmony_ci	ret = len;							\
109462306a36Sopenharmony_ci									\
109562306a36Sopenharmony_ciend:									\
109662306a36Sopenharmony_ci	mutex_unlock(&opts->lock);					\
109762306a36Sopenharmony_ci	return ret;							\
109862306a36Sopenharmony_ci}									\
109962306a36Sopenharmony_ci									\
110062306a36Sopenharmony_ciCONFIGFS_ATTR(f_hid_opts_, name)
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ciF_HID_OPT(subclass, 8, 255);
110362306a36Sopenharmony_ciF_HID_OPT(protocol, 8, 255);
110462306a36Sopenharmony_ciF_HID_OPT(no_out_endpoint, 8, 1);
110562306a36Sopenharmony_ciF_HID_OPT(report_length, 16, 65535);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cistatic ssize_t f_hid_opts_report_desc_show(struct config_item *item, char *page)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct f_hid_opts *opts = to_f_hid_opts(item);
111062306a36Sopenharmony_ci	int result;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	mutex_lock(&opts->lock);
111362306a36Sopenharmony_ci	result = opts->report_desc_length;
111462306a36Sopenharmony_ci	memcpy(page, opts->report_desc, opts->report_desc_length);
111562306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	return result;
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic ssize_t f_hid_opts_report_desc_store(struct config_item *item,
112162306a36Sopenharmony_ci					    const char *page, size_t len)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci	struct f_hid_opts *opts = to_f_hid_opts(item);
112462306a36Sopenharmony_ci	int ret = -EBUSY;
112562306a36Sopenharmony_ci	char *d;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	mutex_lock(&opts->lock);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	if (opts->refcnt)
113062306a36Sopenharmony_ci		goto end;
113162306a36Sopenharmony_ci	if (len > PAGE_SIZE) {
113262306a36Sopenharmony_ci		ret = -ENOSPC;
113362306a36Sopenharmony_ci		goto end;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci	d = kmemdup(page, len, GFP_KERNEL);
113662306a36Sopenharmony_ci	if (!d) {
113762306a36Sopenharmony_ci		ret = -ENOMEM;
113862306a36Sopenharmony_ci		goto end;
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci	kfree(opts->report_desc);
114162306a36Sopenharmony_ci	opts->report_desc = d;
114262306a36Sopenharmony_ci	opts->report_desc_length = len;
114362306a36Sopenharmony_ci	opts->report_desc_alloc = true;
114462306a36Sopenharmony_ci	ret = len;
114562306a36Sopenharmony_ciend:
114662306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
114762306a36Sopenharmony_ci	return ret;
114862306a36Sopenharmony_ci}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ciCONFIGFS_ATTR(f_hid_opts_, report_desc);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_cistatic ssize_t f_hid_opts_dev_show(struct config_item *item, char *page)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	struct f_hid_opts *opts = to_f_hid_opts(item);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	return sprintf(page, "%d:%d\n", major, opts->minor);
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ciCONFIGFS_ATTR_RO(f_hid_opts_, dev);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_cistatic struct configfs_attribute *hid_attrs[] = {
116262306a36Sopenharmony_ci	&f_hid_opts_attr_subclass,
116362306a36Sopenharmony_ci	&f_hid_opts_attr_protocol,
116462306a36Sopenharmony_ci	&f_hid_opts_attr_no_out_endpoint,
116562306a36Sopenharmony_ci	&f_hid_opts_attr_report_length,
116662306a36Sopenharmony_ci	&f_hid_opts_attr_report_desc,
116762306a36Sopenharmony_ci	&f_hid_opts_attr_dev,
116862306a36Sopenharmony_ci	NULL,
116962306a36Sopenharmony_ci};
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_cistatic const struct config_item_type hid_func_type = {
117262306a36Sopenharmony_ci	.ct_item_ops	= &hidg_item_ops,
117362306a36Sopenharmony_ci	.ct_attrs	= hid_attrs,
117462306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
117562306a36Sopenharmony_ci};
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cistatic inline void hidg_put_minor(int minor)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	ida_simple_remove(&hidg_ida, minor);
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic void hidg_free_inst(struct usb_function_instance *f)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	struct f_hid_opts *opts;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	opts = container_of(f, struct f_hid_opts, func_inst);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	mutex_lock(&hidg_ida_lock);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	hidg_put_minor(opts->minor);
119162306a36Sopenharmony_ci	if (ida_is_empty(&hidg_ida))
119262306a36Sopenharmony_ci		ghid_cleanup();
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	mutex_unlock(&hidg_ida_lock);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	if (opts->report_desc_alloc)
119762306a36Sopenharmony_ci		kfree(opts->report_desc);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	kfree(opts);
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic struct usb_function_instance *hidg_alloc_inst(void)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	struct f_hid_opts *opts;
120562306a36Sopenharmony_ci	struct usb_function_instance *ret;
120662306a36Sopenharmony_ci	int status = 0;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
120962306a36Sopenharmony_ci	if (!opts)
121062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
121162306a36Sopenharmony_ci	mutex_init(&opts->lock);
121262306a36Sopenharmony_ci	opts->func_inst.free_func_inst = hidg_free_inst;
121362306a36Sopenharmony_ci	ret = &opts->func_inst;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	mutex_lock(&hidg_ida_lock);
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	if (ida_is_empty(&hidg_ida)) {
121862306a36Sopenharmony_ci		status = ghid_setup(NULL, HIDG_MINORS);
121962306a36Sopenharmony_ci		if (status)  {
122062306a36Sopenharmony_ci			ret = ERR_PTR(status);
122162306a36Sopenharmony_ci			kfree(opts);
122262306a36Sopenharmony_ci			goto unlock;
122362306a36Sopenharmony_ci		}
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	opts->minor = hidg_get_minor();
122762306a36Sopenharmony_ci	if (opts->minor < 0) {
122862306a36Sopenharmony_ci		ret = ERR_PTR(opts->minor);
122962306a36Sopenharmony_ci		kfree(opts);
123062306a36Sopenharmony_ci		if (ida_is_empty(&hidg_ida))
123162306a36Sopenharmony_ci			ghid_cleanup();
123262306a36Sopenharmony_ci		goto unlock;
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci	config_group_init_type_name(&opts->func_inst.group, "", &hid_func_type);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ciunlock:
123762306a36Sopenharmony_ci	mutex_unlock(&hidg_ida_lock);
123862306a36Sopenharmony_ci	return ret;
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_cistatic void hidg_free(struct usb_function *f)
124262306a36Sopenharmony_ci{
124362306a36Sopenharmony_ci	struct f_hidg *hidg;
124462306a36Sopenharmony_ci	struct f_hid_opts *opts;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	hidg = func_to_hidg(f);
124762306a36Sopenharmony_ci	opts = container_of(f->fi, struct f_hid_opts, func_inst);
124862306a36Sopenharmony_ci	put_device(&hidg->dev);
124962306a36Sopenharmony_ci	mutex_lock(&opts->lock);
125062306a36Sopenharmony_ci	--opts->refcnt;
125162306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
125262306a36Sopenharmony_ci}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_cistatic void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct f_hidg *hidg = func_to_hidg(f);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	cdev_device_del(&hidg->cdev, &hidg->dev);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	usb_free_all_descriptors(f);
126162306a36Sopenharmony_ci}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistatic struct usb_function *hidg_alloc(struct usb_function_instance *fi)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	struct f_hidg *hidg;
126662306a36Sopenharmony_ci	struct f_hid_opts *opts;
126762306a36Sopenharmony_ci	int ret;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	/* allocate and initialize one new instance */
127062306a36Sopenharmony_ci	hidg = kzalloc(sizeof(*hidg), GFP_KERNEL);
127162306a36Sopenharmony_ci	if (!hidg)
127262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	opts = container_of(fi, struct f_hid_opts, func_inst);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	mutex_lock(&opts->lock);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	device_initialize(&hidg->dev);
127962306a36Sopenharmony_ci	hidg->dev.release = hidg_release;
128062306a36Sopenharmony_ci	hidg->dev.class = &hidg_class;
128162306a36Sopenharmony_ci	hidg->dev.devt = MKDEV(major, opts->minor);
128262306a36Sopenharmony_ci	ret = dev_set_name(&hidg->dev, "hidg%d", opts->minor);
128362306a36Sopenharmony_ci	if (ret)
128462306a36Sopenharmony_ci		goto err_unlock;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	hidg->bInterfaceSubClass = opts->subclass;
128762306a36Sopenharmony_ci	hidg->bInterfaceProtocol = opts->protocol;
128862306a36Sopenharmony_ci	hidg->report_length = opts->report_length;
128962306a36Sopenharmony_ci	hidg->report_desc_length = opts->report_desc_length;
129062306a36Sopenharmony_ci	if (opts->report_desc) {
129162306a36Sopenharmony_ci		hidg->report_desc = kmemdup(opts->report_desc,
129262306a36Sopenharmony_ci					    opts->report_desc_length,
129362306a36Sopenharmony_ci					    GFP_KERNEL);
129462306a36Sopenharmony_ci		if (!hidg->report_desc) {
129562306a36Sopenharmony_ci			ret = -ENOMEM;
129662306a36Sopenharmony_ci			goto err_put_device;
129762306a36Sopenharmony_ci		}
129862306a36Sopenharmony_ci	}
129962306a36Sopenharmony_ci	hidg->use_out_ep = !opts->no_out_endpoint;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	++opts->refcnt;
130262306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	hidg->func.name    = "hid";
130562306a36Sopenharmony_ci	hidg->func.bind    = hidg_bind;
130662306a36Sopenharmony_ci	hidg->func.unbind  = hidg_unbind;
130762306a36Sopenharmony_ci	hidg->func.set_alt = hidg_set_alt;
130862306a36Sopenharmony_ci	hidg->func.disable = hidg_disable;
130962306a36Sopenharmony_ci	hidg->func.setup   = hidg_setup;
131062306a36Sopenharmony_ci	hidg->func.free_func = hidg_free;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	/* this could be made configurable at some point */
131362306a36Sopenharmony_ci	hidg->qlen	   = 4;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	return &hidg->func;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_cierr_put_device:
131862306a36Sopenharmony_ci	put_device(&hidg->dev);
131962306a36Sopenharmony_cierr_unlock:
132062306a36Sopenharmony_ci	mutex_unlock(&opts->lock);
132162306a36Sopenharmony_ci	return ERR_PTR(ret);
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
132562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
132662306a36Sopenharmony_ciMODULE_AUTHOR("Fabien Chouteau");
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ciint ghid_setup(struct usb_gadget *g, int count)
132962306a36Sopenharmony_ci{
133062306a36Sopenharmony_ci	int status;
133162306a36Sopenharmony_ci	dev_t dev;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	status = class_register(&hidg_class);
133462306a36Sopenharmony_ci	if (status)
133562306a36Sopenharmony_ci		return status;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	status = alloc_chrdev_region(&dev, 0, count, "hidg");
133862306a36Sopenharmony_ci	if (status) {
133962306a36Sopenharmony_ci		class_unregister(&hidg_class);
134062306a36Sopenharmony_ci		return status;
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	major = MAJOR(dev);
134462306a36Sopenharmony_ci	minors = count;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	return 0;
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_civoid ghid_cleanup(void)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	if (major) {
135262306a36Sopenharmony_ci		unregister_chrdev_region(MKDEV(major, 0), minors);
135362306a36Sopenharmony_ci		major = minors = 0;
135462306a36Sopenharmony_ci	}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	class_unregister(&hidg_class);
135762306a36Sopenharmony_ci}
1358