xref: /linux/drivers/usb/gadget/legacy/raw_gadget.c (revision c771600c6af14749609b49565ffb4cac2959710d)
1f2c2e717SAndrey Konovalov // SPDX-License-Identifier: GPL-2.0
2f2c2e717SAndrey Konovalov /*
3f2c2e717SAndrey Konovalov  * USB Raw Gadget driver.
4f2c2e717SAndrey Konovalov  * See Documentation/usb/raw-gadget.rst for more details.
5f2c2e717SAndrey Konovalov  *
64c1934bdSAndrey Konovalov  * Copyright (c) 2020 Google, Inc.
74c1934bdSAndrey Konovalov  * Author: Andrey Konovalov <andreyknvl@gmail.com>
8f2c2e717SAndrey Konovalov  */
9f2c2e717SAndrey Konovalov 
10f2c2e717SAndrey Konovalov #include <linux/compiler.h>
1197df5e57SAndrey Konovalov #include <linux/ctype.h>
12f2c2e717SAndrey Konovalov #include <linux/debugfs.h>
13f2c2e717SAndrey Konovalov #include <linux/delay.h>
14f2d8c260SAlan Stern #include <linux/idr.h>
15f2c2e717SAndrey Konovalov #include <linux/kref.h>
16f2c2e717SAndrey Konovalov #include <linux/miscdevice.h>
17f2c2e717SAndrey Konovalov #include <linux/module.h>
18f2c2e717SAndrey Konovalov #include <linux/semaphore.h>
19f2c2e717SAndrey Konovalov #include <linux/sched.h>
20f2c2e717SAndrey Konovalov #include <linux/slab.h>
21f2c2e717SAndrey Konovalov #include <linux/uaccess.h>
22f2c2e717SAndrey Konovalov #include <linux/wait.h>
23f2c2e717SAndrey Konovalov 
24f2c2e717SAndrey Konovalov #include <linux/usb.h>
25f2c2e717SAndrey Konovalov #include <linux/usb/ch9.h>
26f2c2e717SAndrey Konovalov #include <linux/usb/ch11.h>
27f2c2e717SAndrey Konovalov #include <linux/usb/gadget.h>
28cf9f7a6eSAndrey Konovalov #include <linux/usb/composite.h>
29f2c2e717SAndrey Konovalov 
30f2c2e717SAndrey Konovalov #include <uapi/linux/usb/raw_gadget.h>
31f2c2e717SAndrey Konovalov 
32f2c2e717SAndrey Konovalov #define	DRIVER_DESC "USB Raw Gadget"
33f2c2e717SAndrey Konovalov #define DRIVER_NAME "raw-gadget"
34f2c2e717SAndrey Konovalov 
35f2c2e717SAndrey Konovalov MODULE_DESCRIPTION(DRIVER_DESC);
36f2c2e717SAndrey Konovalov MODULE_AUTHOR("Andrey Konovalov");
37f2c2e717SAndrey Konovalov MODULE_LICENSE("GPL");
38f2c2e717SAndrey Konovalov 
39f2c2e717SAndrey Konovalov /*----------------------------------------------------------------------*/
40f2c2e717SAndrey Konovalov 
41f2d8c260SAlan Stern static DEFINE_IDA(driver_id_numbers);
42f2d8c260SAlan Stern #define DRIVER_DRIVER_NAME_LENGTH_MAX	32
43f2d8c260SAlan Stern 
44f2c2e717SAndrey Konovalov #define RAW_EVENT_QUEUE_SIZE	16
45f2c2e717SAndrey Konovalov 
46f2c2e717SAndrey Konovalov struct raw_event_queue {
47f2c2e717SAndrey Konovalov 	/* See the comment in raw_event_queue_fetch() for locking details. */
48f2c2e717SAndrey Konovalov 	spinlock_t		lock;
49f2c2e717SAndrey Konovalov 	struct semaphore	sema;
50f2c2e717SAndrey Konovalov 	struct usb_raw_event	*events[RAW_EVENT_QUEUE_SIZE];
51f2c2e717SAndrey Konovalov 	int			size;
52f2c2e717SAndrey Konovalov };
53f2c2e717SAndrey Konovalov 
raw_event_queue_init(struct raw_event_queue * queue)54f2c2e717SAndrey Konovalov static void raw_event_queue_init(struct raw_event_queue *queue)
55f2c2e717SAndrey Konovalov {
56f2c2e717SAndrey Konovalov 	spin_lock_init(&queue->lock);
57f2c2e717SAndrey Konovalov 	sema_init(&queue->sema, 0);
58f2c2e717SAndrey Konovalov 	queue->size = 0;
59f2c2e717SAndrey Konovalov }
60f2c2e717SAndrey Konovalov 
raw_event_queue_add(struct raw_event_queue * queue,enum usb_raw_event_type type,size_t length,const void * data)61f2c2e717SAndrey Konovalov static int raw_event_queue_add(struct raw_event_queue *queue,
62f2c2e717SAndrey Konovalov 	enum usb_raw_event_type type, size_t length, const void *data)
63f2c2e717SAndrey Konovalov {
64f2c2e717SAndrey Konovalov 	unsigned long flags;
65f2c2e717SAndrey Konovalov 	struct usb_raw_event *event;
66f2c2e717SAndrey Konovalov 
67f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&queue->lock, flags);
68c3a383d8SAndrey Konovalov 	if (queue->size >= RAW_EVENT_QUEUE_SIZE) {
69f2c2e717SAndrey Konovalov 		spin_unlock_irqrestore(&queue->lock, flags);
70f2c2e717SAndrey Konovalov 		return -ENOMEM;
71f2c2e717SAndrey Konovalov 	}
72f2c2e717SAndrey Konovalov 	event = kmalloc(sizeof(*event) + length, GFP_ATOMIC);
73f2c2e717SAndrey Konovalov 	if (!event) {
74f2c2e717SAndrey Konovalov 		spin_unlock_irqrestore(&queue->lock, flags);
75f2c2e717SAndrey Konovalov 		return -ENOMEM;
76f2c2e717SAndrey Konovalov 	}
77f2c2e717SAndrey Konovalov 	event->type = type;
78f2c2e717SAndrey Konovalov 	event->length = length;
79f2c2e717SAndrey Konovalov 	if (event->length)
80f2c2e717SAndrey Konovalov 		memcpy(&event->data[0], data, length);
81f2c2e717SAndrey Konovalov 	queue->events[queue->size] = event;
82f2c2e717SAndrey Konovalov 	queue->size++;
83f2c2e717SAndrey Konovalov 	up(&queue->sema);
84f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&queue->lock, flags);
85f2c2e717SAndrey Konovalov 	return 0;
86f2c2e717SAndrey Konovalov }
87f2c2e717SAndrey Konovalov 
raw_event_queue_fetch(struct raw_event_queue * queue)88f2c2e717SAndrey Konovalov static struct usb_raw_event *raw_event_queue_fetch(
89f2c2e717SAndrey Konovalov 				struct raw_event_queue *queue)
90f2c2e717SAndrey Konovalov {
91fdd10499SAndrey Konovalov 	int ret;
92f2c2e717SAndrey Konovalov 	unsigned long flags;
93f2c2e717SAndrey Konovalov 	struct usb_raw_event *event;
94f2c2e717SAndrey Konovalov 
95f2c2e717SAndrey Konovalov 	/*
96f2c2e717SAndrey Konovalov 	 * This function can be called concurrently. We first check that
97f2c2e717SAndrey Konovalov 	 * there's at least one event queued by decrementing the semaphore,
98f2c2e717SAndrey Konovalov 	 * and then take the lock to protect queue struct fields.
99f2c2e717SAndrey Konovalov 	 */
100fdd10499SAndrey Konovalov 	ret = down_interruptible(&queue->sema);
101fdd10499SAndrey Konovalov 	if (ret)
102fdd10499SAndrey Konovalov 		return ERR_PTR(ret);
103f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&queue->lock, flags);
104fdd10499SAndrey Konovalov 	/*
105fdd10499SAndrey Konovalov 	 * queue->size must have the same value as queue->sema counter (before
106fdd10499SAndrey Konovalov 	 * the down_interruptible() call above), so this check is a fail-safe.
107fdd10499SAndrey Konovalov 	 */
108fdd10499SAndrey Konovalov 	if (WARN_ON(!queue->size)) {
109fdd10499SAndrey Konovalov 		spin_unlock_irqrestore(&queue->lock, flags);
110fdd10499SAndrey Konovalov 		return ERR_PTR(-ENODEV);
111fdd10499SAndrey Konovalov 	}
112f2c2e717SAndrey Konovalov 	event = queue->events[0];
113f2c2e717SAndrey Konovalov 	queue->size--;
114f2c2e717SAndrey Konovalov 	memmove(&queue->events[0], &queue->events[1],
115f2c2e717SAndrey Konovalov 			queue->size * sizeof(queue->events[0]));
116f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&queue->lock, flags);
117f2c2e717SAndrey Konovalov 	return event;
118f2c2e717SAndrey Konovalov }
119f2c2e717SAndrey Konovalov 
raw_event_queue_destroy(struct raw_event_queue * queue)120f2c2e717SAndrey Konovalov static void raw_event_queue_destroy(struct raw_event_queue *queue)
121f2c2e717SAndrey Konovalov {
122f2c2e717SAndrey Konovalov 	int i;
123f2c2e717SAndrey Konovalov 
124f2c2e717SAndrey Konovalov 	for (i = 0; i < queue->size; i++)
125f2c2e717SAndrey Konovalov 		kfree(queue->events[i]);
126f2c2e717SAndrey Konovalov 	queue->size = 0;
127f2c2e717SAndrey Konovalov }
128f2c2e717SAndrey Konovalov 
129f2c2e717SAndrey Konovalov /*----------------------------------------------------------------------*/
130f2c2e717SAndrey Konovalov 
131f2c2e717SAndrey Konovalov struct raw_dev;
132f2c2e717SAndrey Konovalov 
133f2c2e717SAndrey Konovalov enum ep_state {
134f2c2e717SAndrey Konovalov 	STATE_EP_DISABLED,
135f2c2e717SAndrey Konovalov 	STATE_EP_ENABLED,
136f2c2e717SAndrey Konovalov };
137f2c2e717SAndrey Konovalov 
138f2c2e717SAndrey Konovalov struct raw_ep {
139f2c2e717SAndrey Konovalov 	struct raw_dev		*dev;
140f2c2e717SAndrey Konovalov 	enum ep_state		state;
141f2c2e717SAndrey Konovalov 	struct usb_ep		*ep;
14297df5e57SAndrey Konovalov 	u8			addr;
143f2c2e717SAndrey Konovalov 	struct usb_request	*req;
144f2c2e717SAndrey Konovalov 	bool			urb_queued;
145f2c2e717SAndrey Konovalov 	bool			disabling;
146f2c2e717SAndrey Konovalov 	ssize_t			status;
147f2c2e717SAndrey Konovalov };
148f2c2e717SAndrey Konovalov 
149f2c2e717SAndrey Konovalov enum dev_state {
150f2c2e717SAndrey Konovalov 	STATE_DEV_INVALID = 0,
151f2c2e717SAndrey Konovalov 	STATE_DEV_OPENED,
152f2c2e717SAndrey Konovalov 	STATE_DEV_INITIALIZED,
1535f0b5f4dSSchspa Shi 	STATE_DEV_REGISTERING,
154f2c2e717SAndrey Konovalov 	STATE_DEV_RUNNING,
155f2c2e717SAndrey Konovalov 	STATE_DEV_CLOSED,
156f2c2e717SAndrey Konovalov 	STATE_DEV_FAILED
157f2c2e717SAndrey Konovalov };
158f2c2e717SAndrey Konovalov 
159f2c2e717SAndrey Konovalov struct raw_dev {
160f2c2e717SAndrey Konovalov 	struct kref			count;
161f2c2e717SAndrey Konovalov 	spinlock_t			lock;
162f2c2e717SAndrey Konovalov 
163f2c2e717SAndrey Konovalov 	const char			*udc_name;
164f2c2e717SAndrey Konovalov 	struct usb_gadget_driver	driver;
165f2c2e717SAndrey Konovalov 
166f2c2e717SAndrey Konovalov 	/* Reference to misc device: */
167f2c2e717SAndrey Konovalov 	struct device			*dev;
168f2c2e717SAndrey Konovalov 
169f2d8c260SAlan Stern 	/* Make driver names unique */
170f2d8c260SAlan Stern 	int				driver_id_number;
171f2d8c260SAlan Stern 
172f2c2e717SAndrey Konovalov 	/* Protected by lock: */
173f2c2e717SAndrey Konovalov 	enum dev_state			state;
174f2c2e717SAndrey Konovalov 	bool				gadget_registered;
175f2c2e717SAndrey Konovalov 	struct usb_gadget		*gadget;
176f2c2e717SAndrey Konovalov 	struct usb_request		*req;
177f2c2e717SAndrey Konovalov 	bool				ep0_in_pending;
178f2c2e717SAndrey Konovalov 	bool				ep0_out_pending;
179f2c2e717SAndrey Konovalov 	bool				ep0_urb_queued;
180f2c2e717SAndrey Konovalov 	ssize_t				ep0_status;
18197df5e57SAndrey Konovalov 	struct raw_ep			eps[USB_RAW_EPS_NUM_MAX];
18297df5e57SAndrey Konovalov 	int				eps_num;
183f2c2e717SAndrey Konovalov 
184f2c2e717SAndrey Konovalov 	struct completion		ep0_done;
185f2c2e717SAndrey Konovalov 	struct raw_event_queue		queue;
186f2c2e717SAndrey Konovalov };
187f2c2e717SAndrey Konovalov 
dev_new(void)188f2c2e717SAndrey Konovalov static struct raw_dev *dev_new(void)
189f2c2e717SAndrey Konovalov {
190f2c2e717SAndrey Konovalov 	struct raw_dev *dev;
191f2c2e717SAndrey Konovalov 
192f2c2e717SAndrey Konovalov 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
193f2c2e717SAndrey Konovalov 	if (!dev)
194f2c2e717SAndrey Konovalov 		return NULL;
195f2c2e717SAndrey Konovalov 	/* Matches kref_put() in raw_release(). */
196f2c2e717SAndrey Konovalov 	kref_init(&dev->count);
197f2c2e717SAndrey Konovalov 	spin_lock_init(&dev->lock);
198f2c2e717SAndrey Konovalov 	init_completion(&dev->ep0_done);
199f2c2e717SAndrey Konovalov 	raw_event_queue_init(&dev->queue);
200f2d8c260SAlan Stern 	dev->driver_id_number = -1;
201f2c2e717SAndrey Konovalov 	return dev;
202f2c2e717SAndrey Konovalov }
203f2c2e717SAndrey Konovalov 
dev_free(struct kref * kref)204f2c2e717SAndrey Konovalov static void dev_free(struct kref *kref)
205f2c2e717SAndrey Konovalov {
206f2c2e717SAndrey Konovalov 	struct raw_dev *dev = container_of(kref, struct raw_dev, count);
207f2c2e717SAndrey Konovalov 	int i;
208f2c2e717SAndrey Konovalov 
209f2c2e717SAndrey Konovalov 	kfree(dev->udc_name);
210f2c2e717SAndrey Konovalov 	kfree(dev->driver.udc_name);
211f2d8c260SAlan Stern 	kfree(dev->driver.driver.name);
212f2d8c260SAlan Stern 	if (dev->driver_id_number >= 0)
213f2d8c260SAlan Stern 		ida_free(&driver_id_numbers, dev->driver_id_number);
214f2c2e717SAndrey Konovalov 	if (dev->req) {
215f2c2e717SAndrey Konovalov 		if (dev->ep0_urb_queued)
216f2c2e717SAndrey Konovalov 			usb_ep_dequeue(dev->gadget->ep0, dev->req);
217f2c2e717SAndrey Konovalov 		usb_ep_free_request(dev->gadget->ep0, dev->req);
218f2c2e717SAndrey Konovalov 	}
219f2c2e717SAndrey Konovalov 	raw_event_queue_destroy(&dev->queue);
22097df5e57SAndrey Konovalov 	for (i = 0; i < dev->eps_num; i++) {
221c61769bdSAndrey Konovalov 		if (dev->eps[i].state == STATE_EP_DISABLED)
222f2c2e717SAndrey Konovalov 			continue;
223f2c2e717SAndrey Konovalov 		usb_ep_disable(dev->eps[i].ep);
224f2c2e717SAndrey Konovalov 		usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
225f2c2e717SAndrey Konovalov 		kfree(dev->eps[i].ep->desc);
226f2c2e717SAndrey Konovalov 		dev->eps[i].state = STATE_EP_DISABLED;
227f2c2e717SAndrey Konovalov 	}
228f2c2e717SAndrey Konovalov 	kfree(dev);
229f2c2e717SAndrey Konovalov }
230f2c2e717SAndrey Konovalov 
231f2c2e717SAndrey Konovalov /*----------------------------------------------------------------------*/
232f2c2e717SAndrey Konovalov 
raw_queue_event(struct raw_dev * dev,enum usb_raw_event_type type,size_t length,const void * data)233f2c2e717SAndrey Konovalov static int raw_queue_event(struct raw_dev *dev,
234f2c2e717SAndrey Konovalov 	enum usb_raw_event_type type, size_t length, const void *data)
235f2c2e717SAndrey Konovalov {
236f2c2e717SAndrey Konovalov 	int ret = 0;
237f2c2e717SAndrey Konovalov 	unsigned long flags;
238f2c2e717SAndrey Konovalov 
239f2c2e717SAndrey Konovalov 	ret = raw_event_queue_add(&dev->queue, type, length, data);
240f2c2e717SAndrey Konovalov 	if (ret < 0) {
241f2c2e717SAndrey Konovalov 		spin_lock_irqsave(&dev->lock, flags);
242f2c2e717SAndrey Konovalov 		dev->state = STATE_DEV_FAILED;
243f2c2e717SAndrey Konovalov 		spin_unlock_irqrestore(&dev->lock, flags);
244f2c2e717SAndrey Konovalov 	}
245f2c2e717SAndrey Konovalov 	return ret;
246f2c2e717SAndrey Konovalov }
247f2c2e717SAndrey Konovalov 
gadget_ep0_complete(struct usb_ep * ep,struct usb_request * req)248f2c2e717SAndrey Konovalov static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
249f2c2e717SAndrey Konovalov {
250f2c2e717SAndrey Konovalov 	struct raw_dev *dev = req->context;
251f2c2e717SAndrey Konovalov 	unsigned long flags;
252f2c2e717SAndrey Konovalov 
253f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
254f2c2e717SAndrey Konovalov 	if (req->status)
255f2c2e717SAndrey Konovalov 		dev->ep0_status = req->status;
256f2c2e717SAndrey Konovalov 	else
257f2c2e717SAndrey Konovalov 		dev->ep0_status = req->actual;
258f2c2e717SAndrey Konovalov 	if (dev->ep0_in_pending)
259f2c2e717SAndrey Konovalov 		dev->ep0_in_pending = false;
260f2c2e717SAndrey Konovalov 	else
261f2c2e717SAndrey Konovalov 		dev->ep0_out_pending = false;
262f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
263f2c2e717SAndrey Konovalov 
264f2c2e717SAndrey Konovalov 	complete(&dev->ep0_done);
265f2c2e717SAndrey Konovalov }
266f2c2e717SAndrey Konovalov 
get_ep_addr(const char * name)26797df5e57SAndrey Konovalov static u8 get_ep_addr(const char *name)
26897df5e57SAndrey Konovalov {
26997df5e57SAndrey Konovalov 	/* If the endpoint has fixed function (named as e.g. "ep12out-bulk"),
27097df5e57SAndrey Konovalov 	 * parse the endpoint address from its name. We deliberately use
27197df5e57SAndrey Konovalov 	 * deprecated simple_strtoul() function here, as the number isn't
27297df5e57SAndrey Konovalov 	 * followed by '\0' nor '\n'.
27397df5e57SAndrey Konovalov 	 */
27497df5e57SAndrey Konovalov 	if (isdigit(name[2]))
27597df5e57SAndrey Konovalov 		return simple_strtoul(&name[2], NULL, 10);
27697df5e57SAndrey Konovalov 	/* Otherwise the endpoint is configurable (named as e.g. "ep-a"). */
27797df5e57SAndrey Konovalov 	return USB_RAW_EP_ADDR_ANY;
27897df5e57SAndrey Konovalov }
27997df5e57SAndrey Konovalov 
gadget_bind(struct usb_gadget * gadget,struct usb_gadget_driver * driver)280f2c2e717SAndrey Konovalov static int gadget_bind(struct usb_gadget *gadget,
281f2c2e717SAndrey Konovalov 			struct usb_gadget_driver *driver)
282f2c2e717SAndrey Konovalov {
28397df5e57SAndrey Konovalov 	int ret = 0, i = 0;
284f2c2e717SAndrey Konovalov 	struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
285f2c2e717SAndrey Konovalov 	struct usb_request *req;
28697df5e57SAndrey Konovalov 	struct usb_ep *ep;
287f2c2e717SAndrey Konovalov 	unsigned long flags;
288f2c2e717SAndrey Konovalov 
289f2c2e717SAndrey Konovalov 	if (strcmp(gadget->name, dev->udc_name) != 0)
290f2c2e717SAndrey Konovalov 		return -ENODEV;
291f2c2e717SAndrey Konovalov 
292f2c2e717SAndrey Konovalov 	set_gadget_data(gadget, dev);
293f2c2e717SAndrey Konovalov 	req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
294f2c2e717SAndrey Konovalov 	if (!req) {
295f2c2e717SAndrey Konovalov 		dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
296f2c2e717SAndrey Konovalov 		set_gadget_data(gadget, NULL);
297f2c2e717SAndrey Konovalov 		return -ENOMEM;
298f2c2e717SAndrey Konovalov 	}
299f2c2e717SAndrey Konovalov 
300f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
301f2c2e717SAndrey Konovalov 	dev->req = req;
302f2c2e717SAndrey Konovalov 	dev->req->context = dev;
303f2c2e717SAndrey Konovalov 	dev->req->complete = gadget_ep0_complete;
304f2c2e717SAndrey Konovalov 	dev->gadget = gadget;
30597df5e57SAndrey Konovalov 	gadget_for_each_ep(ep, dev->gadget) {
30697df5e57SAndrey Konovalov 		dev->eps[i].ep = ep;
30797df5e57SAndrey Konovalov 		dev->eps[i].addr = get_ep_addr(ep->name);
30897df5e57SAndrey Konovalov 		dev->eps[i].state = STATE_EP_DISABLED;
30997df5e57SAndrey Konovalov 		i++;
31097df5e57SAndrey Konovalov 	}
31197df5e57SAndrey Konovalov 	dev->eps_num = i;
312f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
313f2c2e717SAndrey Konovalov 
314c3a383d8SAndrey Konovalov 	dev_dbg(&gadget->dev, "gadget connected\n");
31583e30f2bSZqiang 	ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
31683e30f2bSZqiang 	if (ret < 0) {
317c3a383d8SAndrey Konovalov 		dev_err(&gadget->dev, "failed to queue connect event\n");
31883e30f2bSZqiang 		set_gadget_data(gadget, NULL);
31983e30f2bSZqiang 		return ret;
32083e30f2bSZqiang 	}
32183e30f2bSZqiang 
322f2c2e717SAndrey Konovalov 	/* Matches kref_put() in gadget_unbind(). */
323f2c2e717SAndrey Konovalov 	kref_get(&dev->count);
324f2c2e717SAndrey Konovalov 	return ret;
325f2c2e717SAndrey Konovalov }
326f2c2e717SAndrey Konovalov 
gadget_unbind(struct usb_gadget * gadget)327f2c2e717SAndrey Konovalov static void gadget_unbind(struct usb_gadget *gadget)
328f2c2e717SAndrey Konovalov {
329f2c2e717SAndrey Konovalov 	struct raw_dev *dev = get_gadget_data(gadget);
330f2c2e717SAndrey Konovalov 
331f2c2e717SAndrey Konovalov 	set_gadget_data(gadget, NULL);
332f2c2e717SAndrey Konovalov 	/* Matches kref_get() in gadget_bind(). */
333f2c2e717SAndrey Konovalov 	kref_put(&dev->count, dev_free);
334f2c2e717SAndrey Konovalov }
335f2c2e717SAndrey Konovalov 
gadget_setup(struct usb_gadget * gadget,const struct usb_ctrlrequest * ctrl)336f2c2e717SAndrey Konovalov static int gadget_setup(struct usb_gadget *gadget,
337f2c2e717SAndrey Konovalov 			const struct usb_ctrlrequest *ctrl)
338f2c2e717SAndrey Konovalov {
339f2c2e717SAndrey Konovalov 	int ret = 0;
340f2c2e717SAndrey Konovalov 	struct raw_dev *dev = get_gadget_data(gadget);
341f2c2e717SAndrey Konovalov 	unsigned long flags;
342f2c2e717SAndrey Konovalov 
343f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
344f2c2e717SAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
345f2c2e717SAndrey Konovalov 		dev_err(&gadget->dev, "ignoring, device is not running\n");
346f2c2e717SAndrey Konovalov 		ret = -ENODEV;
347f2c2e717SAndrey Konovalov 		goto out_unlock;
348f2c2e717SAndrey Konovalov 	}
349f2c2e717SAndrey Konovalov 	if (dev->ep0_in_pending || dev->ep0_out_pending) {
350f2c2e717SAndrey Konovalov 		dev_dbg(&gadget->dev, "stalling, request already pending\n");
351f2c2e717SAndrey Konovalov 		ret = -EBUSY;
352f2c2e717SAndrey Konovalov 		goto out_unlock;
353f2c2e717SAndrey Konovalov 	}
354f2c2e717SAndrey Konovalov 	if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength)
355f2c2e717SAndrey Konovalov 		dev->ep0_in_pending = true;
356f2c2e717SAndrey Konovalov 	else
357f2c2e717SAndrey Konovalov 		dev->ep0_out_pending = true;
358f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
359f2c2e717SAndrey Konovalov 
360f2c2e717SAndrey Konovalov 	ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl);
361f2c2e717SAndrey Konovalov 	if (ret < 0)
362c3a383d8SAndrey Konovalov 		dev_err(&gadget->dev, "failed to queue control event\n");
363f2c2e717SAndrey Konovalov 	goto out;
364f2c2e717SAndrey Konovalov 
365f2c2e717SAndrey Konovalov out_unlock:
366f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
367f2c2e717SAndrey Konovalov out:
368cf9f7a6eSAndrey Konovalov 	if (ret == 0 && ctrl->wLength == 0) {
369cf9f7a6eSAndrey Konovalov 		/*
370cf9f7a6eSAndrey Konovalov 		 * Return USB_GADGET_DELAYED_STATUS as a workaround to stop
371cf9f7a6eSAndrey Konovalov 		 * some UDC drivers (e.g. dwc3) from automatically proceeding
372cf9f7a6eSAndrey Konovalov 		 * with the status stage for 0-length transfers.
373cf9f7a6eSAndrey Konovalov 		 * Should be removed once all UDC drivers are fixed to always
374cf9f7a6eSAndrey Konovalov 		 * delay the status stage until a response is queued to EP0.
375cf9f7a6eSAndrey Konovalov 		 */
376cf9f7a6eSAndrey Konovalov 		return USB_GADGET_DELAYED_STATUS;
377cf9f7a6eSAndrey Konovalov 	}
378f2c2e717SAndrey Konovalov 	return ret;
379f2c2e717SAndrey Konovalov }
380f2c2e717SAndrey Konovalov 
gadget_disconnect(struct usb_gadget * gadget)381c3a383d8SAndrey Konovalov static void gadget_disconnect(struct usb_gadget *gadget)
382c3a383d8SAndrey Konovalov {
383c3a383d8SAndrey Konovalov 	struct raw_dev *dev = get_gadget_data(gadget);
384c3a383d8SAndrey Konovalov 	int ret;
385c3a383d8SAndrey Konovalov 
386c3a383d8SAndrey Konovalov 	dev_dbg(&gadget->dev, "gadget disconnected\n");
387c3a383d8SAndrey Konovalov 	ret = raw_queue_event(dev, USB_RAW_EVENT_DISCONNECT, 0, NULL);
388c3a383d8SAndrey Konovalov 	if (ret < 0)
389c3a383d8SAndrey Konovalov 		dev_err(&gadget->dev, "failed to queue disconnect event\n");
390c3a383d8SAndrey Konovalov }
gadget_suspend(struct usb_gadget * gadget)391c3a383d8SAndrey Konovalov static void gadget_suspend(struct usb_gadget *gadget)
392c3a383d8SAndrey Konovalov {
393c3a383d8SAndrey Konovalov 	struct raw_dev *dev = get_gadget_data(gadget);
394c3a383d8SAndrey Konovalov 	int ret;
395c3a383d8SAndrey Konovalov 
396c3a383d8SAndrey Konovalov 	dev_dbg(&gadget->dev, "gadget suspended\n");
397c3a383d8SAndrey Konovalov 	ret = raw_queue_event(dev, USB_RAW_EVENT_SUSPEND, 0, NULL);
398c3a383d8SAndrey Konovalov 	if (ret < 0)
399c3a383d8SAndrey Konovalov 		dev_err(&gadget->dev, "failed to queue suspend event\n");
400c3a383d8SAndrey Konovalov }
gadget_resume(struct usb_gadget * gadget)401c3a383d8SAndrey Konovalov static void gadget_resume(struct usb_gadget *gadget)
402c3a383d8SAndrey Konovalov {
403c3a383d8SAndrey Konovalov 	struct raw_dev *dev = get_gadget_data(gadget);
404c3a383d8SAndrey Konovalov 	int ret;
405c3a383d8SAndrey Konovalov 
406c3a383d8SAndrey Konovalov 	dev_dbg(&gadget->dev, "gadget resumed\n");
407c3a383d8SAndrey Konovalov 	ret = raw_queue_event(dev, USB_RAW_EVENT_RESUME, 0, NULL);
408c3a383d8SAndrey Konovalov 	if (ret < 0)
409c3a383d8SAndrey Konovalov 		dev_err(&gadget->dev, "failed to queue resume event\n");
410c3a383d8SAndrey Konovalov }
gadget_reset(struct usb_gadget * gadget)411c3a383d8SAndrey Konovalov static void gadget_reset(struct usb_gadget *gadget)
412c3a383d8SAndrey Konovalov {
413c3a383d8SAndrey Konovalov 	struct raw_dev *dev = get_gadget_data(gadget);
414c3a383d8SAndrey Konovalov 	int ret;
415c3a383d8SAndrey Konovalov 
416c3a383d8SAndrey Konovalov 	dev_dbg(&gadget->dev, "gadget reset\n");
417c3a383d8SAndrey Konovalov 	ret = raw_queue_event(dev, USB_RAW_EVENT_RESET, 0, NULL);
418c3a383d8SAndrey Konovalov 	if (ret < 0)
419c3a383d8SAndrey Konovalov 		dev_err(&gadget->dev, "failed to queue reset event\n");
420c3a383d8SAndrey Konovalov }
421f2c2e717SAndrey Konovalov 
422f2c2e717SAndrey Konovalov /*----------------------------------------------------------------------*/
423f2c2e717SAndrey Konovalov 
424f2c2e717SAndrey Konovalov static struct miscdevice raw_misc_device;
425f2c2e717SAndrey Konovalov 
raw_open(struct inode * inode,struct file * fd)426f2c2e717SAndrey Konovalov static int raw_open(struct inode *inode, struct file *fd)
427f2c2e717SAndrey Konovalov {
428f2c2e717SAndrey Konovalov 	struct raw_dev *dev;
429f2c2e717SAndrey Konovalov 
430f2c2e717SAndrey Konovalov 	/* Nonblocking I/O is not supported yet. */
431f2c2e717SAndrey Konovalov 	if (fd->f_flags & O_NONBLOCK)
432f2c2e717SAndrey Konovalov 		return -EINVAL;
433f2c2e717SAndrey Konovalov 
434f2c2e717SAndrey Konovalov 	dev = dev_new();
435f2c2e717SAndrey Konovalov 	if (!dev)
436f2c2e717SAndrey Konovalov 		return -ENOMEM;
437f2c2e717SAndrey Konovalov 	fd->private_data = dev;
438f2c2e717SAndrey Konovalov 	dev->state = STATE_DEV_OPENED;
439f2c2e717SAndrey Konovalov 	dev->dev = raw_misc_device.this_device;
440f2c2e717SAndrey Konovalov 	return 0;
441f2c2e717SAndrey Konovalov }
442f2c2e717SAndrey Konovalov 
raw_release(struct inode * inode,struct file * fd)443f2c2e717SAndrey Konovalov static int raw_release(struct inode *inode, struct file *fd)
444f2c2e717SAndrey Konovalov {
445f2c2e717SAndrey Konovalov 	int ret = 0;
446f2c2e717SAndrey Konovalov 	struct raw_dev *dev = fd->private_data;
447f2c2e717SAndrey Konovalov 	unsigned long flags;
448f2c2e717SAndrey Konovalov 	bool unregister = false;
449f2c2e717SAndrey Konovalov 
450f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
451f2c2e717SAndrey Konovalov 	dev->state = STATE_DEV_CLOSED;
452f2c2e717SAndrey Konovalov 	if (!dev->gadget) {
453f2c2e717SAndrey Konovalov 		spin_unlock_irqrestore(&dev->lock, flags);
454f2c2e717SAndrey Konovalov 		goto out_put;
455f2c2e717SAndrey Konovalov 	}
456f2c2e717SAndrey Konovalov 	if (dev->gadget_registered)
457f2c2e717SAndrey Konovalov 		unregister = true;
458f2c2e717SAndrey Konovalov 	dev->gadget_registered = false;
459f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
460f2c2e717SAndrey Konovalov 
461f2c2e717SAndrey Konovalov 	if (unregister) {
462f2c2e717SAndrey Konovalov 		ret = usb_gadget_unregister_driver(&dev->driver);
463f2c2e717SAndrey Konovalov 		if (ret != 0)
464f2c2e717SAndrey Konovalov 			dev_err(dev->dev,
465f2c2e717SAndrey Konovalov 				"usb_gadget_unregister_driver() failed with %d\n",
466f2c2e717SAndrey Konovalov 				ret);
467f2c2e717SAndrey Konovalov 		/* Matches kref_get() in raw_ioctl_run(). */
468f2c2e717SAndrey Konovalov 		kref_put(&dev->count, dev_free);
469f2c2e717SAndrey Konovalov 	}
470f2c2e717SAndrey Konovalov 
471f2c2e717SAndrey Konovalov out_put:
472f2c2e717SAndrey Konovalov 	/* Matches dev_new() in raw_open(). */
473f2c2e717SAndrey Konovalov 	kref_put(&dev->count, dev_free);
474f2c2e717SAndrey Konovalov 	return ret;
475f2c2e717SAndrey Konovalov }
476f2c2e717SAndrey Konovalov 
477f2c2e717SAndrey Konovalov /*----------------------------------------------------------------------*/
478f2c2e717SAndrey Konovalov 
raw_ioctl_init(struct raw_dev * dev,unsigned long value)479f2c2e717SAndrey Konovalov static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
480f2c2e717SAndrey Konovalov {
481f2c2e717SAndrey Konovalov 	int ret = 0;
48290bc2af2SAlan Stern 	int driver_id_number;
483f2c2e717SAndrey Konovalov 	struct usb_raw_init arg;
484f2c2e717SAndrey Konovalov 	char *udc_driver_name;
485f2c2e717SAndrey Konovalov 	char *udc_device_name;
486f2d8c260SAlan Stern 	char *driver_driver_name;
487f2c2e717SAndrey Konovalov 	unsigned long flags;
488f2c2e717SAndrey Konovalov 
489068fbff4SDan Carpenter 	if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
490068fbff4SDan Carpenter 		return -EFAULT;
491f2c2e717SAndrey Konovalov 
492f2c2e717SAndrey Konovalov 	switch (arg.speed) {
493f2c2e717SAndrey Konovalov 	case USB_SPEED_UNKNOWN:
494f2c2e717SAndrey Konovalov 		arg.speed = USB_SPEED_HIGH;
495f2c2e717SAndrey Konovalov 		break;
496f2c2e717SAndrey Konovalov 	case USB_SPEED_LOW:
497f2c2e717SAndrey Konovalov 	case USB_SPEED_FULL:
498f2c2e717SAndrey Konovalov 	case USB_SPEED_HIGH:
499f2c2e717SAndrey Konovalov 	case USB_SPEED_SUPER:
500f2c2e717SAndrey Konovalov 		break;
501f2c2e717SAndrey Konovalov 	default:
502f2c2e717SAndrey Konovalov 		return -EINVAL;
503f2c2e717SAndrey Konovalov 	}
504f2c2e717SAndrey Konovalov 
50590bc2af2SAlan Stern 	driver_id_number = ida_alloc(&driver_id_numbers, GFP_KERNEL);
50690bc2af2SAlan Stern 	if (driver_id_number < 0)
50790bc2af2SAlan Stern 		return driver_id_number;
508f2d8c260SAlan Stern 
509f2d8c260SAlan Stern 	driver_driver_name = kmalloc(DRIVER_DRIVER_NAME_LENGTH_MAX, GFP_KERNEL);
510f2d8c260SAlan Stern 	if (!driver_driver_name) {
511f2d8c260SAlan Stern 		ret = -ENOMEM;
512f2d8c260SAlan Stern 		goto out_free_driver_id_number;
513f2d8c260SAlan Stern 	}
514f2d8c260SAlan Stern 	snprintf(driver_driver_name, DRIVER_DRIVER_NAME_LENGTH_MAX,
51590bc2af2SAlan Stern 				DRIVER_NAME ".%d", driver_id_number);
516f2d8c260SAlan Stern 
517f2c2e717SAndrey Konovalov 	udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
518f2d8c260SAlan Stern 	if (!udc_driver_name) {
519f2d8c260SAlan Stern 		ret = -ENOMEM;
520f2d8c260SAlan Stern 		goto out_free_driver_driver_name;
521f2d8c260SAlan Stern 	}
522f2c2e717SAndrey Konovalov 	ret = strscpy(udc_driver_name, &arg.driver_name[0],
523f2c2e717SAndrey Konovalov 				UDC_NAME_LENGTH_MAX);
524f2d8c260SAlan Stern 	if (ret < 0)
525f2d8c260SAlan Stern 		goto out_free_udc_driver_name;
526f2c2e717SAndrey Konovalov 	ret = 0;
527f2c2e717SAndrey Konovalov 
528f2c2e717SAndrey Konovalov 	udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
529f2c2e717SAndrey Konovalov 	if (!udc_device_name) {
530f2d8c260SAlan Stern 		ret = -ENOMEM;
531f2d8c260SAlan Stern 		goto out_free_udc_driver_name;
532f2c2e717SAndrey Konovalov 	}
533f2c2e717SAndrey Konovalov 	ret = strscpy(udc_device_name, &arg.device_name[0],
534f2c2e717SAndrey Konovalov 				UDC_NAME_LENGTH_MAX);
535f2d8c260SAlan Stern 	if (ret < 0)
536f2d8c260SAlan Stern 		goto out_free_udc_device_name;
537f2c2e717SAndrey Konovalov 	ret = 0;
538f2c2e717SAndrey Konovalov 
539f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
540f2c2e717SAndrey Konovalov 	if (dev->state != STATE_DEV_OPENED) {
541f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not opened\n");
542f2c2e717SAndrey Konovalov 		ret = -EINVAL;
543f2c2e717SAndrey Konovalov 		goto out_unlock;
544f2c2e717SAndrey Konovalov 	}
545f2c2e717SAndrey Konovalov 	dev->udc_name = udc_driver_name;
546f2c2e717SAndrey Konovalov 
547f2c2e717SAndrey Konovalov 	dev->driver.function = DRIVER_DESC;
548f2c2e717SAndrey Konovalov 	dev->driver.max_speed = arg.speed;
549f2c2e717SAndrey Konovalov 	dev->driver.setup = gadget_setup;
550f2c2e717SAndrey Konovalov 	dev->driver.disconnect = gadget_disconnect;
551f2c2e717SAndrey Konovalov 	dev->driver.bind = gadget_bind;
552f2c2e717SAndrey Konovalov 	dev->driver.unbind = gadget_unbind;
553f2c2e717SAndrey Konovalov 	dev->driver.suspend = gadget_suspend;
554f2c2e717SAndrey Konovalov 	dev->driver.resume = gadget_resume;
555f2c2e717SAndrey Konovalov 	dev->driver.reset = gadget_reset;
556f2d8c260SAlan Stern 	dev->driver.driver.name = driver_driver_name;
557f2c2e717SAndrey Konovalov 	dev->driver.udc_name = udc_device_name;
558f2c2e717SAndrey Konovalov 	dev->driver.match_existing_only = 1;
55990bc2af2SAlan Stern 	dev->driver_id_number = driver_id_number;
560f2c2e717SAndrey Konovalov 
561f2c2e717SAndrey Konovalov 	dev->state = STATE_DEV_INITIALIZED;
562f2d8c260SAlan Stern 	spin_unlock_irqrestore(&dev->lock, flags);
563f2d8c260SAlan Stern 	return ret;
564f2c2e717SAndrey Konovalov 
565f2c2e717SAndrey Konovalov out_unlock:
566f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
567f2d8c260SAlan Stern out_free_udc_device_name:
568f2d8c260SAlan Stern 	kfree(udc_device_name);
569f2d8c260SAlan Stern out_free_udc_driver_name:
570f2d8c260SAlan Stern 	kfree(udc_driver_name);
571f2d8c260SAlan Stern out_free_driver_driver_name:
572f2d8c260SAlan Stern 	kfree(driver_driver_name);
573f2d8c260SAlan Stern out_free_driver_id_number:
57490bc2af2SAlan Stern 	ida_free(&driver_id_numbers, driver_id_number);
575f2c2e717SAndrey Konovalov 	return ret;
576f2c2e717SAndrey Konovalov }
577f2c2e717SAndrey Konovalov 
raw_ioctl_run(struct raw_dev * dev,unsigned long value)578f2c2e717SAndrey Konovalov static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
579f2c2e717SAndrey Konovalov {
580f2c2e717SAndrey Konovalov 	int ret = 0;
581f2c2e717SAndrey Konovalov 	unsigned long flags;
582f2c2e717SAndrey Konovalov 
583f2c2e717SAndrey Konovalov 	if (value)
584f2c2e717SAndrey Konovalov 		return -EINVAL;
585f2c2e717SAndrey Konovalov 
586f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
587f2c2e717SAndrey Konovalov 	if (dev->state != STATE_DEV_INITIALIZED) {
588f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not initialized\n");
589f2c2e717SAndrey Konovalov 		ret = -EINVAL;
590f2c2e717SAndrey Konovalov 		goto out_unlock;
591f2c2e717SAndrey Konovalov 	}
5925f0b5f4dSSchspa Shi 	dev->state = STATE_DEV_REGISTERING;
593f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
594f2c2e717SAndrey Konovalov 
595af1969a2SAlan Stern 	ret = usb_gadget_register_driver(&dev->driver);
596f2c2e717SAndrey Konovalov 
597f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
598f2c2e717SAndrey Konovalov 	if (ret) {
599f2c2e717SAndrey Konovalov 		dev_err(dev->dev,
600af1969a2SAlan Stern 			"fail, usb_gadget_register_driver returned %d\n", ret);
601f2c2e717SAndrey Konovalov 		dev->state = STATE_DEV_FAILED;
602f2c2e717SAndrey Konovalov 		goto out_unlock;
603f2c2e717SAndrey Konovalov 	}
604f2c2e717SAndrey Konovalov 	dev->gadget_registered = true;
605f2c2e717SAndrey Konovalov 	dev->state = STATE_DEV_RUNNING;
606f2c2e717SAndrey Konovalov 	/* Matches kref_put() in raw_release(). */
607f2c2e717SAndrey Konovalov 	kref_get(&dev->count);
608f2c2e717SAndrey Konovalov 
609f2c2e717SAndrey Konovalov out_unlock:
610f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
611f2c2e717SAndrey Konovalov 	return ret;
612f2c2e717SAndrey Konovalov }
613f2c2e717SAndrey Konovalov 
raw_ioctl_event_fetch(struct raw_dev * dev,unsigned long value)614f2c2e717SAndrey Konovalov static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
615f2c2e717SAndrey Konovalov {
616f2c2e717SAndrey Konovalov 	struct usb_raw_event arg;
617f2c2e717SAndrey Konovalov 	unsigned long flags;
618f2c2e717SAndrey Konovalov 	struct usb_raw_event *event;
619f2c2e717SAndrey Konovalov 	uint32_t length;
620f2c2e717SAndrey Konovalov 
621068fbff4SDan Carpenter 	if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
622068fbff4SDan Carpenter 		return -EFAULT;
623f2c2e717SAndrey Konovalov 
624f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
625f2c2e717SAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
626f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not running\n");
627f2c2e717SAndrey Konovalov 		spin_unlock_irqrestore(&dev->lock, flags);
628f2c2e717SAndrey Konovalov 		return -EINVAL;
629f2c2e717SAndrey Konovalov 	}
630f2c2e717SAndrey Konovalov 	if (!dev->gadget) {
631f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, gadget is not bound\n");
632f2c2e717SAndrey Konovalov 		spin_unlock_irqrestore(&dev->lock, flags);
633f2c2e717SAndrey Konovalov 		return -EBUSY;
634f2c2e717SAndrey Konovalov 	}
635f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
636f2c2e717SAndrey Konovalov 
637f2c2e717SAndrey Konovalov 	event = raw_event_queue_fetch(&dev->queue);
638fdd10499SAndrey Konovalov 	if (PTR_ERR(event) == -EINTR) {
639f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
640f2c2e717SAndrey Konovalov 		return -EINTR;
641f2c2e717SAndrey Konovalov 	}
642fdd10499SAndrey Konovalov 	if (IS_ERR(event)) {
643fdd10499SAndrey Konovalov 		dev_err(&dev->gadget->dev, "failed to fetch event\n");
644fdd10499SAndrey Konovalov 		spin_lock_irqsave(&dev->lock, flags);
645fdd10499SAndrey Konovalov 		dev->state = STATE_DEV_FAILED;
646fdd10499SAndrey Konovalov 		spin_unlock_irqrestore(&dev->lock, flags);
647fdd10499SAndrey Konovalov 		return -ENODEV;
648fdd10499SAndrey Konovalov 	}
649f2c2e717SAndrey Konovalov 	length = min(arg.length, event->length);
650129aa973SZqiang 	if (copy_to_user((void __user *)value, event, sizeof(*event) + length)) {
651129aa973SZqiang 		kfree(event);
652068fbff4SDan Carpenter 		return -EFAULT;
653129aa973SZqiang 	}
654068fbff4SDan Carpenter 
655129aa973SZqiang 	kfree(event);
656068fbff4SDan Carpenter 	return 0;
657f2c2e717SAndrey Konovalov }
658f2c2e717SAndrey Konovalov 
raw_alloc_io_data(struct usb_raw_ep_io * io,void __user * ptr,bool get_from_user)659f2c2e717SAndrey Konovalov static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
660f2c2e717SAndrey Konovalov 				bool get_from_user)
661f2c2e717SAndrey Konovalov {
662f2c2e717SAndrey Konovalov 	void *data;
663f2c2e717SAndrey Konovalov 
664068fbff4SDan Carpenter 	if (copy_from_user(io, ptr, sizeof(*io)))
665068fbff4SDan Carpenter 		return ERR_PTR(-EFAULT);
66697df5e57SAndrey Konovalov 	if (io->ep >= USB_RAW_EPS_NUM_MAX)
667f2c2e717SAndrey Konovalov 		return ERR_PTR(-EINVAL);
668f2c2e717SAndrey Konovalov 	if (!usb_raw_io_flags_valid(io->flags))
669f2c2e717SAndrey Konovalov 		return ERR_PTR(-EINVAL);
670f2c2e717SAndrey Konovalov 	if (io->length > PAGE_SIZE)
671f2c2e717SAndrey Konovalov 		return ERR_PTR(-EINVAL);
672f2c2e717SAndrey Konovalov 	if (get_from_user)
673f2c2e717SAndrey Konovalov 		data = memdup_user(ptr + sizeof(*io), io->length);
674f2c2e717SAndrey Konovalov 	else {
675f2c2e717SAndrey Konovalov 		data = kmalloc(io->length, GFP_KERNEL);
676f2c2e717SAndrey Konovalov 		if (!data)
677f2c2e717SAndrey Konovalov 			data = ERR_PTR(-ENOMEM);
678f2c2e717SAndrey Konovalov 	}
679f2c2e717SAndrey Konovalov 	return data;
680f2c2e717SAndrey Konovalov }
681f2c2e717SAndrey Konovalov 
raw_process_ep0_io(struct raw_dev * dev,struct usb_raw_ep_io * io,void * data,bool in)682f2c2e717SAndrey Konovalov static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
683f2c2e717SAndrey Konovalov 				void *data, bool in)
684f2c2e717SAndrey Konovalov {
685f2c2e717SAndrey Konovalov 	int ret = 0;
686f2c2e717SAndrey Konovalov 	unsigned long flags;
687f2c2e717SAndrey Konovalov 
688f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
689f2c2e717SAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
690f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not running\n");
691f2c2e717SAndrey Konovalov 		ret = -EINVAL;
692f2c2e717SAndrey Konovalov 		goto out_unlock;
693f2c2e717SAndrey Konovalov 	}
694f2c2e717SAndrey Konovalov 	if (!dev->gadget) {
695f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, gadget is not bound\n");
696f2c2e717SAndrey Konovalov 		ret = -EBUSY;
697f2c2e717SAndrey Konovalov 		goto out_unlock;
698f2c2e717SAndrey Konovalov 	}
699f2c2e717SAndrey Konovalov 	if (dev->ep0_urb_queued) {
700f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
701f2c2e717SAndrey Konovalov 		ret = -EBUSY;
702f2c2e717SAndrey Konovalov 		goto out_unlock;
703f2c2e717SAndrey Konovalov 	}
704f2c2e717SAndrey Konovalov 	if ((in && !dev->ep0_in_pending) ||
705f2c2e717SAndrey Konovalov 			(!in && !dev->ep0_out_pending)) {
706f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
707f2c2e717SAndrey Konovalov 		ret = -EBUSY;
708f2c2e717SAndrey Konovalov 		goto out_unlock;
709f2c2e717SAndrey Konovalov 	}
710f2c2e717SAndrey Konovalov 	if (WARN_ON(in && dev->ep0_out_pending)) {
711f2c2e717SAndrey Konovalov 		ret = -ENODEV;
712f2c2e717SAndrey Konovalov 		dev->state = STATE_DEV_FAILED;
713e8033bdeSAndrey Konovalov 		goto out_unlock;
714f2c2e717SAndrey Konovalov 	}
715f2c2e717SAndrey Konovalov 	if (WARN_ON(!in && dev->ep0_in_pending)) {
716f2c2e717SAndrey Konovalov 		ret = -ENODEV;
717f2c2e717SAndrey Konovalov 		dev->state = STATE_DEV_FAILED;
718e8033bdeSAndrey Konovalov 		goto out_unlock;
719f2c2e717SAndrey Konovalov 	}
720f2c2e717SAndrey Konovalov 
721f2c2e717SAndrey Konovalov 	dev->req->buf = data;
722f2c2e717SAndrey Konovalov 	dev->req->length = io->length;
723f2c2e717SAndrey Konovalov 	dev->req->zero = usb_raw_io_flags_zero(io->flags);
724f2c2e717SAndrey Konovalov 	dev->ep0_urb_queued = true;
725f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
726f2c2e717SAndrey Konovalov 
727f2c2e717SAndrey Konovalov 	ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
728f2c2e717SAndrey Konovalov 	if (ret) {
729f2c2e717SAndrey Konovalov 		dev_err(&dev->gadget->dev,
730f2c2e717SAndrey Konovalov 				"fail, usb_ep_queue returned %d\n", ret);
731f2c2e717SAndrey Konovalov 		spin_lock_irqsave(&dev->lock, flags);
732e8033bdeSAndrey Konovalov 		goto out_queue_failed;
733f2c2e717SAndrey Konovalov 	}
734f2c2e717SAndrey Konovalov 
735f2c2e717SAndrey Konovalov 	ret = wait_for_completion_interruptible(&dev->ep0_done);
736f2c2e717SAndrey Konovalov 	if (ret) {
737f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "wait interrupted\n");
738f2c2e717SAndrey Konovalov 		usb_ep_dequeue(dev->gadget->ep0, dev->req);
739f2c2e717SAndrey Konovalov 		wait_for_completion(&dev->ep0_done);
740f2c2e717SAndrey Konovalov 		spin_lock_irqsave(&dev->lock, flags);
741e8033bdeSAndrey Konovalov 		if (dev->ep0_status == -ECONNRESET)
742e8033bdeSAndrey Konovalov 			dev->ep0_status = -EINTR;
743e8033bdeSAndrey Konovalov 		goto out_interrupted;
744f2c2e717SAndrey Konovalov 	}
745f2c2e717SAndrey Konovalov 
746f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
747f2c2e717SAndrey Konovalov 
748e8033bdeSAndrey Konovalov out_interrupted:
749e8033bdeSAndrey Konovalov 	ret = dev->ep0_status;
750e8033bdeSAndrey Konovalov out_queue_failed:
751f2c2e717SAndrey Konovalov 	dev->ep0_urb_queued = false;
752f2c2e717SAndrey Konovalov out_unlock:
753f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
754f2c2e717SAndrey Konovalov 	return ret;
755f2c2e717SAndrey Konovalov }
756f2c2e717SAndrey Konovalov 
raw_ioctl_ep0_write(struct raw_dev * dev,unsigned long value)757f2c2e717SAndrey Konovalov static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
758f2c2e717SAndrey Konovalov {
759f2c2e717SAndrey Konovalov 	int ret = 0;
760f2c2e717SAndrey Konovalov 	void *data;
761f2c2e717SAndrey Konovalov 	struct usb_raw_ep_io io;
762f2c2e717SAndrey Konovalov 
763f2c2e717SAndrey Konovalov 	data = raw_alloc_io_data(&io, (void __user *)value, true);
764f2c2e717SAndrey Konovalov 	if (IS_ERR(data))
765f2c2e717SAndrey Konovalov 		return PTR_ERR(data);
766f2c2e717SAndrey Konovalov 	ret = raw_process_ep0_io(dev, &io, data, true);
767f2c2e717SAndrey Konovalov 	kfree(data);
768f2c2e717SAndrey Konovalov 	return ret;
769f2c2e717SAndrey Konovalov }
770f2c2e717SAndrey Konovalov 
raw_ioctl_ep0_read(struct raw_dev * dev,unsigned long value)771f2c2e717SAndrey Konovalov static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
772f2c2e717SAndrey Konovalov {
773f2c2e717SAndrey Konovalov 	int ret = 0;
774f2c2e717SAndrey Konovalov 	void *data;
775f2c2e717SAndrey Konovalov 	struct usb_raw_ep_io io;
776f2c2e717SAndrey Konovalov 	unsigned int length;
777f2c2e717SAndrey Konovalov 
778f2c2e717SAndrey Konovalov 	data = raw_alloc_io_data(&io, (void __user *)value, false);
779f2c2e717SAndrey Konovalov 	if (IS_ERR(data))
780f2c2e717SAndrey Konovalov 		return PTR_ERR(data);
781f2c2e717SAndrey Konovalov 	ret = raw_process_ep0_io(dev, &io, data, false);
7826e507644SAndrey Konovalov 	if (ret < 0)
783068fbff4SDan Carpenter 		goto free;
784068fbff4SDan Carpenter 
785*b7d49096SSabyrzhan Tasbolatov 	length = min_t(unsigned int, io.length, ret);
786068fbff4SDan Carpenter 	if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
787068fbff4SDan Carpenter 		ret = -EFAULT;
7886e507644SAndrey Konovalov 	else
7896e507644SAndrey Konovalov 		ret = length;
790068fbff4SDan Carpenter free:
791f2c2e717SAndrey Konovalov 	kfree(data);
792f2c2e717SAndrey Konovalov 	return ret;
793f2c2e717SAndrey Konovalov }
794f2c2e717SAndrey Konovalov 
raw_ioctl_ep0_stall(struct raw_dev * dev,unsigned long value)795c61769bdSAndrey Konovalov static int raw_ioctl_ep0_stall(struct raw_dev *dev, unsigned long value)
796c61769bdSAndrey Konovalov {
797c61769bdSAndrey Konovalov 	int ret = 0;
798c61769bdSAndrey Konovalov 	unsigned long flags;
799c61769bdSAndrey Konovalov 
800c61769bdSAndrey Konovalov 	if (value)
801c61769bdSAndrey Konovalov 		return -EINVAL;
802c61769bdSAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
803c61769bdSAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
804c61769bdSAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not running\n");
805c61769bdSAndrey Konovalov 		ret = -EINVAL;
806c61769bdSAndrey Konovalov 		goto out_unlock;
807c61769bdSAndrey Konovalov 	}
808c61769bdSAndrey Konovalov 	if (!dev->gadget) {
809c61769bdSAndrey Konovalov 		dev_dbg(dev->dev, "fail, gadget is not bound\n");
810c61769bdSAndrey Konovalov 		ret = -EBUSY;
811c61769bdSAndrey Konovalov 		goto out_unlock;
812c61769bdSAndrey Konovalov 	}
813c61769bdSAndrey Konovalov 	if (dev->ep0_urb_queued) {
814c61769bdSAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
815c61769bdSAndrey Konovalov 		ret = -EBUSY;
816c61769bdSAndrey Konovalov 		goto out_unlock;
817c61769bdSAndrey Konovalov 	}
818c61769bdSAndrey Konovalov 	if (!dev->ep0_in_pending && !dev->ep0_out_pending) {
819c61769bdSAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "fail, no request pending\n");
820c61769bdSAndrey Konovalov 		ret = -EBUSY;
821c61769bdSAndrey Konovalov 		goto out_unlock;
822c61769bdSAndrey Konovalov 	}
823c61769bdSAndrey Konovalov 
824c61769bdSAndrey Konovalov 	ret = usb_ep_set_halt(dev->gadget->ep0);
825c61769bdSAndrey Konovalov 	if (ret < 0)
826c61769bdSAndrey Konovalov 		dev_err(&dev->gadget->dev,
827c61769bdSAndrey Konovalov 				"fail, usb_ep_set_halt returned %d\n", ret);
828c61769bdSAndrey Konovalov 
829c61769bdSAndrey Konovalov 	if (dev->ep0_in_pending)
830c61769bdSAndrey Konovalov 		dev->ep0_in_pending = false;
831c61769bdSAndrey Konovalov 	else
832c61769bdSAndrey Konovalov 		dev->ep0_out_pending = false;
833c61769bdSAndrey Konovalov 
834c61769bdSAndrey Konovalov out_unlock:
835c61769bdSAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
836c61769bdSAndrey Konovalov 	return ret;
837c61769bdSAndrey Konovalov }
838c61769bdSAndrey Konovalov 
raw_ioctl_ep_enable(struct raw_dev * dev,unsigned long value)839f2c2e717SAndrey Konovalov static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
840f2c2e717SAndrey Konovalov {
841f2c2e717SAndrey Konovalov 	int ret = 0, i;
842f2c2e717SAndrey Konovalov 	unsigned long flags;
843f2c2e717SAndrey Konovalov 	struct usb_endpoint_descriptor *desc;
84497df5e57SAndrey Konovalov 	struct raw_ep *ep;
8450d48aee6SWei Ming Chen 	bool ep_props_matched = false;
846f2c2e717SAndrey Konovalov 
847f2c2e717SAndrey Konovalov 	desc = memdup_user((void __user *)value, sizeof(*desc));
848f2c2e717SAndrey Konovalov 	if (IS_ERR(desc))
849f2c2e717SAndrey Konovalov 		return PTR_ERR(desc);
850f2c2e717SAndrey Konovalov 
851f2c2e717SAndrey Konovalov 	/*
852f2c2e717SAndrey Konovalov 	 * Endpoints with a maxpacket length of 0 can cause crashes in UDC
853f2c2e717SAndrey Konovalov 	 * drivers.
854f2c2e717SAndrey Konovalov 	 */
855f2c2e717SAndrey Konovalov 	if (usb_endpoint_maxp(desc) == 0) {
856f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n");
857f2c2e717SAndrey Konovalov 		kfree(desc);
858f2c2e717SAndrey Konovalov 		return -EINVAL;
859f2c2e717SAndrey Konovalov 	}
860f2c2e717SAndrey Konovalov 
861f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
862f2c2e717SAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
863f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not running\n");
864f2c2e717SAndrey Konovalov 		ret = -EINVAL;
865f2c2e717SAndrey Konovalov 		goto out_free;
866f2c2e717SAndrey Konovalov 	}
867f2c2e717SAndrey Konovalov 	if (!dev->gadget) {
868f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, gadget is not bound\n");
869f2c2e717SAndrey Konovalov 		ret = -EBUSY;
870f2c2e717SAndrey Konovalov 		goto out_free;
871f2c2e717SAndrey Konovalov 	}
872f2c2e717SAndrey Konovalov 
87397df5e57SAndrey Konovalov 	for (i = 0; i < dev->eps_num; i++) {
87497df5e57SAndrey Konovalov 		ep = &dev->eps[i];
87597df5e57SAndrey Konovalov 		if (ep->addr != usb_endpoint_num(desc) &&
87697df5e57SAndrey Konovalov 				ep->addr != USB_RAW_EP_ADDR_ANY)
877f2c2e717SAndrey Konovalov 			continue;
87897df5e57SAndrey Konovalov 		if (!usb_gadget_ep_match_desc(dev->gadget, ep->ep, desc, NULL))
879f2c2e717SAndrey Konovalov 			continue;
8800d48aee6SWei Ming Chen 		ep_props_matched = true;
8810d48aee6SWei Ming Chen 		if (ep->state != STATE_EP_DISABLED)
8820d48aee6SWei Ming Chen 			continue;
88397df5e57SAndrey Konovalov 		ep->ep->desc = desc;
88497df5e57SAndrey Konovalov 		ret = usb_ep_enable(ep->ep);
885f2c2e717SAndrey Konovalov 		if (ret < 0) {
886f2c2e717SAndrey Konovalov 			dev_err(&dev->gadget->dev,
887f2c2e717SAndrey Konovalov 				"fail, usb_ep_enable returned %d\n", ret);
888f2c2e717SAndrey Konovalov 			goto out_free;
889f2c2e717SAndrey Konovalov 		}
89097df5e57SAndrey Konovalov 		ep->req = usb_ep_alloc_request(ep->ep, GFP_ATOMIC);
89197df5e57SAndrey Konovalov 		if (!ep->req) {
892f2c2e717SAndrey Konovalov 			dev_err(&dev->gadget->dev,
893f2c2e717SAndrey Konovalov 				"fail, usb_ep_alloc_request failed\n");
89497df5e57SAndrey Konovalov 			usb_ep_disable(ep->ep);
895f2c2e717SAndrey Konovalov 			ret = -ENOMEM;
896f2c2e717SAndrey Konovalov 			goto out_free;
897f2c2e717SAndrey Konovalov 		}
89897df5e57SAndrey Konovalov 		ep->state = STATE_EP_ENABLED;
89997df5e57SAndrey Konovalov 		ep->ep->driver_data = ep;
900f2c2e717SAndrey Konovalov 		ret = i;
901f2c2e717SAndrey Konovalov 		goto out_unlock;
902f2c2e717SAndrey Konovalov 	}
903f2c2e717SAndrey Konovalov 
9040d48aee6SWei Ming Chen 	if (!ep_props_matched) {
9050d48aee6SWei Ming Chen 		dev_dbg(&dev->gadget->dev, "fail, bad endpoint descriptor\n");
9060d48aee6SWei Ming Chen 		ret = -EINVAL;
9070d48aee6SWei Ming Chen 	} else {
9080d48aee6SWei Ming Chen 		dev_dbg(&dev->gadget->dev, "fail, no endpoints available\n");
909f2c2e717SAndrey Konovalov 		ret = -EBUSY;
9100d48aee6SWei Ming Chen 	}
911f2c2e717SAndrey Konovalov 
912f2c2e717SAndrey Konovalov out_free:
913f2c2e717SAndrey Konovalov 	kfree(desc);
914f2c2e717SAndrey Konovalov out_unlock:
915f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
916f2c2e717SAndrey Konovalov 	return ret;
917f2c2e717SAndrey Konovalov }
918f2c2e717SAndrey Konovalov 
raw_ioctl_ep_disable(struct raw_dev * dev,unsigned long value)919f2c2e717SAndrey Konovalov static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
920f2c2e717SAndrey Konovalov {
921f2c2e717SAndrey Konovalov 	int ret = 0, i = value;
922f2c2e717SAndrey Konovalov 	unsigned long flags;
923f2c2e717SAndrey Konovalov 
924f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
925f2c2e717SAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
926f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not running\n");
927f2c2e717SAndrey Konovalov 		ret = -EINVAL;
928f2c2e717SAndrey Konovalov 		goto out_unlock;
929f2c2e717SAndrey Konovalov 	}
930f2c2e717SAndrey Konovalov 	if (!dev->gadget) {
931f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, gadget is not bound\n");
932f2c2e717SAndrey Konovalov 		ret = -EBUSY;
933f2c2e717SAndrey Konovalov 		goto out_unlock;
934f2c2e717SAndrey Konovalov 	}
93597df5e57SAndrey Konovalov 	if (i < 0 || i >= dev->eps_num) {
93697df5e57SAndrey Konovalov 		dev_dbg(dev->dev, "fail, invalid endpoint\n");
93797df5e57SAndrey Konovalov 		ret = -EBUSY;
93897df5e57SAndrey Konovalov 		goto out_unlock;
93997df5e57SAndrey Konovalov 	}
940c61769bdSAndrey Konovalov 	if (dev->eps[i].state == STATE_EP_DISABLED) {
941f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
942f2c2e717SAndrey Konovalov 		ret = -EINVAL;
943f2c2e717SAndrey Konovalov 		goto out_unlock;
944f2c2e717SAndrey Konovalov 	}
945f2c2e717SAndrey Konovalov 	if (dev->eps[i].disabling) {
946f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev,
947f2c2e717SAndrey Konovalov 				"fail, disable already in progress\n");
948f2c2e717SAndrey Konovalov 		ret = -EINVAL;
949f2c2e717SAndrey Konovalov 		goto out_unlock;
950f2c2e717SAndrey Konovalov 	}
951f2c2e717SAndrey Konovalov 	if (dev->eps[i].urb_queued) {
952f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev,
953f2c2e717SAndrey Konovalov 				"fail, waiting for urb completion\n");
954f2c2e717SAndrey Konovalov 		ret = -EINVAL;
955f2c2e717SAndrey Konovalov 		goto out_unlock;
956f2c2e717SAndrey Konovalov 	}
957f2c2e717SAndrey Konovalov 	dev->eps[i].disabling = true;
958f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
959f2c2e717SAndrey Konovalov 
960f2c2e717SAndrey Konovalov 	usb_ep_disable(dev->eps[i].ep);
961f2c2e717SAndrey Konovalov 
962f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
963f2c2e717SAndrey Konovalov 	usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
96497df5e57SAndrey Konovalov 	kfree(dev->eps[i].ep->desc);
965f2c2e717SAndrey Konovalov 	dev->eps[i].state = STATE_EP_DISABLED;
966f2c2e717SAndrey Konovalov 	dev->eps[i].disabling = false;
967f2c2e717SAndrey Konovalov 
968f2c2e717SAndrey Konovalov out_unlock:
969f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
970f2c2e717SAndrey Konovalov 	return ret;
971f2c2e717SAndrey Konovalov }
972f2c2e717SAndrey Konovalov 
raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev * dev,unsigned long value,bool set,bool halt)973c61769bdSAndrey Konovalov static int raw_ioctl_ep_set_clear_halt_wedge(struct raw_dev *dev,
974c61769bdSAndrey Konovalov 		unsigned long value, bool set, bool halt)
975c61769bdSAndrey Konovalov {
976c61769bdSAndrey Konovalov 	int ret = 0, i = value;
977c61769bdSAndrey Konovalov 	unsigned long flags;
978c61769bdSAndrey Konovalov 
979c61769bdSAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
980c61769bdSAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
981c61769bdSAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not running\n");
982c61769bdSAndrey Konovalov 		ret = -EINVAL;
983c61769bdSAndrey Konovalov 		goto out_unlock;
984c61769bdSAndrey Konovalov 	}
985c61769bdSAndrey Konovalov 	if (!dev->gadget) {
986c61769bdSAndrey Konovalov 		dev_dbg(dev->dev, "fail, gadget is not bound\n");
987c61769bdSAndrey Konovalov 		ret = -EBUSY;
988c61769bdSAndrey Konovalov 		goto out_unlock;
989c61769bdSAndrey Konovalov 	}
990c61769bdSAndrey Konovalov 	if (i < 0 || i >= dev->eps_num) {
991c61769bdSAndrey Konovalov 		dev_dbg(dev->dev, "fail, invalid endpoint\n");
992c61769bdSAndrey Konovalov 		ret = -EBUSY;
993c61769bdSAndrey Konovalov 		goto out_unlock;
994c61769bdSAndrey Konovalov 	}
995c61769bdSAndrey Konovalov 	if (dev->eps[i].state == STATE_EP_DISABLED) {
996c61769bdSAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
997c61769bdSAndrey Konovalov 		ret = -EINVAL;
998c61769bdSAndrey Konovalov 		goto out_unlock;
999c61769bdSAndrey Konovalov 	}
1000c61769bdSAndrey Konovalov 	if (dev->eps[i].disabling) {
1001c61769bdSAndrey Konovalov 		dev_dbg(&dev->gadget->dev,
1002c61769bdSAndrey Konovalov 				"fail, disable is in progress\n");
1003c61769bdSAndrey Konovalov 		ret = -EINVAL;
1004c61769bdSAndrey Konovalov 		goto out_unlock;
1005c61769bdSAndrey Konovalov 	}
1006c61769bdSAndrey Konovalov 	if (dev->eps[i].urb_queued) {
1007c61769bdSAndrey Konovalov 		dev_dbg(&dev->gadget->dev,
1008c61769bdSAndrey Konovalov 				"fail, waiting for urb completion\n");
1009c61769bdSAndrey Konovalov 		ret = -EINVAL;
1010c61769bdSAndrey Konovalov 		goto out_unlock;
1011c61769bdSAndrey Konovalov 	}
1012c61769bdSAndrey Konovalov 	if (usb_endpoint_xfer_isoc(dev->eps[i].ep->desc)) {
1013c61769bdSAndrey Konovalov 		dev_dbg(&dev->gadget->dev,
1014c61769bdSAndrey Konovalov 				"fail, can't halt/wedge ISO endpoint\n");
1015c61769bdSAndrey Konovalov 		ret = -EINVAL;
1016c61769bdSAndrey Konovalov 		goto out_unlock;
1017c61769bdSAndrey Konovalov 	}
1018c61769bdSAndrey Konovalov 
1019c61769bdSAndrey Konovalov 	if (set && halt) {
1020c61769bdSAndrey Konovalov 		ret = usb_ep_set_halt(dev->eps[i].ep);
1021c61769bdSAndrey Konovalov 		if (ret < 0)
1022c61769bdSAndrey Konovalov 			dev_err(&dev->gadget->dev,
1023c61769bdSAndrey Konovalov 				"fail, usb_ep_set_halt returned %d\n", ret);
1024c61769bdSAndrey Konovalov 	} else if (!set && halt) {
1025c61769bdSAndrey Konovalov 		ret = usb_ep_clear_halt(dev->eps[i].ep);
1026c61769bdSAndrey Konovalov 		if (ret < 0)
1027c61769bdSAndrey Konovalov 			dev_err(&dev->gadget->dev,
1028c61769bdSAndrey Konovalov 				"fail, usb_ep_clear_halt returned %d\n", ret);
1029c61769bdSAndrey Konovalov 	} else if (set && !halt) {
1030c61769bdSAndrey Konovalov 		ret = usb_ep_set_wedge(dev->eps[i].ep);
1031c61769bdSAndrey Konovalov 		if (ret < 0)
1032c61769bdSAndrey Konovalov 			dev_err(&dev->gadget->dev,
1033c61769bdSAndrey Konovalov 				"fail, usb_ep_set_wedge returned %d\n", ret);
1034c61769bdSAndrey Konovalov 	}
1035c61769bdSAndrey Konovalov 
1036c61769bdSAndrey Konovalov out_unlock:
1037c61769bdSAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
1038c61769bdSAndrey Konovalov 	return ret;
1039c61769bdSAndrey Konovalov }
1040c61769bdSAndrey Konovalov 
gadget_ep_complete(struct usb_ep * ep,struct usb_request * req)1041f2c2e717SAndrey Konovalov static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
1042f2c2e717SAndrey Konovalov {
1043f2c2e717SAndrey Konovalov 	struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data;
1044f2c2e717SAndrey Konovalov 	struct raw_dev *dev = r_ep->dev;
1045f2c2e717SAndrey Konovalov 	unsigned long flags;
1046f2c2e717SAndrey Konovalov 
1047f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
1048f2c2e717SAndrey Konovalov 	if (req->status)
1049f2c2e717SAndrey Konovalov 		r_ep->status = req->status;
1050f2c2e717SAndrey Konovalov 	else
1051f2c2e717SAndrey Konovalov 		r_ep->status = req->actual;
1052f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
1053f2c2e717SAndrey Konovalov 
1054f2c2e717SAndrey Konovalov 	complete((struct completion *)req->context);
1055f2c2e717SAndrey Konovalov }
1056f2c2e717SAndrey Konovalov 
raw_process_ep_io(struct raw_dev * dev,struct usb_raw_ep_io * io,void * data,bool in)1057f2c2e717SAndrey Konovalov static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
1058f2c2e717SAndrey Konovalov 				void *data, bool in)
1059f2c2e717SAndrey Konovalov {
1060f2c2e717SAndrey Konovalov 	int ret = 0;
1061f2c2e717SAndrey Konovalov 	unsigned long flags;
106297df5e57SAndrey Konovalov 	struct raw_ep *ep;
1063f2c2e717SAndrey Konovalov 	DECLARE_COMPLETION_ONSTACK(done);
1064f2c2e717SAndrey Konovalov 
1065f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
1066f2c2e717SAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
1067f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not running\n");
1068f2c2e717SAndrey Konovalov 		ret = -EINVAL;
1069f2c2e717SAndrey Konovalov 		goto out_unlock;
1070f2c2e717SAndrey Konovalov 	}
1071f2c2e717SAndrey Konovalov 	if (!dev->gadget) {
1072f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, gadget is not bound\n");
1073f2c2e717SAndrey Konovalov 		ret = -EBUSY;
1074f2c2e717SAndrey Konovalov 		goto out_unlock;
1075f2c2e717SAndrey Konovalov 	}
107697df5e57SAndrey Konovalov 	if (io->ep >= dev->eps_num) {
107797df5e57SAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "fail, invalid endpoint\n");
107897df5e57SAndrey Konovalov 		ret = -EINVAL;
107997df5e57SAndrey Konovalov 		goto out_unlock;
108097df5e57SAndrey Konovalov 	}
108197df5e57SAndrey Konovalov 	ep = &dev->eps[io->ep];
1082f2c2e717SAndrey Konovalov 	if (ep->state != STATE_EP_ENABLED) {
1083f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
1084f2c2e717SAndrey Konovalov 		ret = -EBUSY;
1085f2c2e717SAndrey Konovalov 		goto out_unlock;
1086f2c2e717SAndrey Konovalov 	}
1087f2c2e717SAndrey Konovalov 	if (ep->disabling) {
1088f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev,
1089f2c2e717SAndrey Konovalov 				"fail, endpoint is already being disabled\n");
1090f2c2e717SAndrey Konovalov 		ret = -EBUSY;
1091f2c2e717SAndrey Konovalov 		goto out_unlock;
1092f2c2e717SAndrey Konovalov 	}
1093f2c2e717SAndrey Konovalov 	if (ep->urb_queued) {
1094f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
1095f2c2e717SAndrey Konovalov 		ret = -EBUSY;
1096f2c2e717SAndrey Konovalov 		goto out_unlock;
1097f2c2e717SAndrey Konovalov 	}
1098292d2c82SJann Horn 	if (in != usb_endpoint_dir_in(ep->ep->desc)) {
1099f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
1100f2c2e717SAndrey Konovalov 		ret = -EINVAL;
1101f2c2e717SAndrey Konovalov 		goto out_unlock;
1102f2c2e717SAndrey Konovalov 	}
1103f2c2e717SAndrey Konovalov 
1104f2c2e717SAndrey Konovalov 	ep->dev = dev;
1105f2c2e717SAndrey Konovalov 	ep->req->context = &done;
1106f2c2e717SAndrey Konovalov 	ep->req->complete = gadget_ep_complete;
1107f2c2e717SAndrey Konovalov 	ep->req->buf = data;
1108f2c2e717SAndrey Konovalov 	ep->req->length = io->length;
1109f2c2e717SAndrey Konovalov 	ep->req->zero = usb_raw_io_flags_zero(io->flags);
1110f2c2e717SAndrey Konovalov 	ep->urb_queued = true;
1111f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
1112f2c2e717SAndrey Konovalov 
1113f2c2e717SAndrey Konovalov 	ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
1114f2c2e717SAndrey Konovalov 	if (ret) {
1115f2c2e717SAndrey Konovalov 		dev_err(&dev->gadget->dev,
1116f2c2e717SAndrey Konovalov 				"fail, usb_ep_queue returned %d\n", ret);
1117f2c2e717SAndrey Konovalov 		spin_lock_irqsave(&dev->lock, flags);
1118e8033bdeSAndrey Konovalov 		goto out_queue_failed;
1119f2c2e717SAndrey Konovalov 	}
1120f2c2e717SAndrey Konovalov 
1121f2c2e717SAndrey Konovalov 	ret = wait_for_completion_interruptible(&done);
1122f2c2e717SAndrey Konovalov 	if (ret) {
1123f2c2e717SAndrey Konovalov 		dev_dbg(&dev->gadget->dev, "wait interrupted\n");
1124f2c2e717SAndrey Konovalov 		usb_ep_dequeue(ep->ep, ep->req);
1125f2c2e717SAndrey Konovalov 		wait_for_completion(&done);
1126f2c2e717SAndrey Konovalov 		spin_lock_irqsave(&dev->lock, flags);
1127e8033bdeSAndrey Konovalov 		if (ep->status == -ECONNRESET)
1128e8033bdeSAndrey Konovalov 			ep->status = -EINTR;
1129e8033bdeSAndrey Konovalov 		goto out_interrupted;
1130f2c2e717SAndrey Konovalov 	}
1131f2c2e717SAndrey Konovalov 
1132f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
1133f2c2e717SAndrey Konovalov 
1134e8033bdeSAndrey Konovalov out_interrupted:
1135e8033bdeSAndrey Konovalov 	ret = ep->status;
1136e8033bdeSAndrey Konovalov out_queue_failed:
1137f2c2e717SAndrey Konovalov 	ep->urb_queued = false;
1138f2c2e717SAndrey Konovalov out_unlock:
1139f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
1140f2c2e717SAndrey Konovalov 	return ret;
1141f2c2e717SAndrey Konovalov }
1142f2c2e717SAndrey Konovalov 
raw_ioctl_ep_write(struct raw_dev * dev,unsigned long value)1143f2c2e717SAndrey Konovalov static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value)
1144f2c2e717SAndrey Konovalov {
1145f2c2e717SAndrey Konovalov 	int ret = 0;
1146f2c2e717SAndrey Konovalov 	char *data;
1147f2c2e717SAndrey Konovalov 	struct usb_raw_ep_io io;
1148f2c2e717SAndrey Konovalov 
1149f2c2e717SAndrey Konovalov 	data = raw_alloc_io_data(&io, (void __user *)value, true);
1150f2c2e717SAndrey Konovalov 	if (IS_ERR(data))
1151f2c2e717SAndrey Konovalov 		return PTR_ERR(data);
1152f2c2e717SAndrey Konovalov 	ret = raw_process_ep_io(dev, &io, data, true);
1153f2c2e717SAndrey Konovalov 	kfree(data);
1154f2c2e717SAndrey Konovalov 	return ret;
1155f2c2e717SAndrey Konovalov }
1156f2c2e717SAndrey Konovalov 
raw_ioctl_ep_read(struct raw_dev * dev,unsigned long value)1157f2c2e717SAndrey Konovalov static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value)
1158f2c2e717SAndrey Konovalov {
1159f2c2e717SAndrey Konovalov 	int ret = 0;
1160f2c2e717SAndrey Konovalov 	char *data;
1161f2c2e717SAndrey Konovalov 	struct usb_raw_ep_io io;
1162f2c2e717SAndrey Konovalov 	unsigned int length;
1163f2c2e717SAndrey Konovalov 
1164f2c2e717SAndrey Konovalov 	data = raw_alloc_io_data(&io, (void __user *)value, false);
1165f2c2e717SAndrey Konovalov 	if (IS_ERR(data))
1166f2c2e717SAndrey Konovalov 		return PTR_ERR(data);
1167f2c2e717SAndrey Konovalov 	ret = raw_process_ep_io(dev, &io, data, false);
11686e507644SAndrey Konovalov 	if (ret < 0)
1169068fbff4SDan Carpenter 		goto free;
1170068fbff4SDan Carpenter 
1171*b7d49096SSabyrzhan Tasbolatov 	length = min_t(unsigned int, io.length, ret);
1172068fbff4SDan Carpenter 	if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
1173068fbff4SDan Carpenter 		ret = -EFAULT;
11746e507644SAndrey Konovalov 	else
11756e507644SAndrey Konovalov 		ret = length;
1176068fbff4SDan Carpenter free:
1177f2c2e717SAndrey Konovalov 	kfree(data);
1178f2c2e717SAndrey Konovalov 	return ret;
1179f2c2e717SAndrey Konovalov }
1180f2c2e717SAndrey Konovalov 
raw_ioctl_configure(struct raw_dev * dev,unsigned long value)1181f2c2e717SAndrey Konovalov static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value)
1182f2c2e717SAndrey Konovalov {
1183f2c2e717SAndrey Konovalov 	int ret = 0;
1184f2c2e717SAndrey Konovalov 	unsigned long flags;
1185f2c2e717SAndrey Konovalov 
1186f2c2e717SAndrey Konovalov 	if (value)
1187f2c2e717SAndrey Konovalov 		return -EINVAL;
1188f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
1189f2c2e717SAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
1190f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not running\n");
1191f2c2e717SAndrey Konovalov 		ret = -EINVAL;
1192f2c2e717SAndrey Konovalov 		goto out_unlock;
1193f2c2e717SAndrey Konovalov 	}
1194f2c2e717SAndrey Konovalov 	if (!dev->gadget) {
1195f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, gadget is not bound\n");
1196f2c2e717SAndrey Konovalov 		ret = -EBUSY;
1197f2c2e717SAndrey Konovalov 		goto out_unlock;
1198f2c2e717SAndrey Konovalov 	}
1199f2c2e717SAndrey Konovalov 	usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED);
1200f2c2e717SAndrey Konovalov 
1201f2c2e717SAndrey Konovalov out_unlock:
1202f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
1203f2c2e717SAndrey Konovalov 	return ret;
1204f2c2e717SAndrey Konovalov }
1205f2c2e717SAndrey Konovalov 
raw_ioctl_vbus_draw(struct raw_dev * dev,unsigned long value)1206f2c2e717SAndrey Konovalov static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
1207f2c2e717SAndrey Konovalov {
1208f2c2e717SAndrey Konovalov 	int ret = 0;
1209f2c2e717SAndrey Konovalov 	unsigned long flags;
1210f2c2e717SAndrey Konovalov 
1211f2c2e717SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
1212f2c2e717SAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
1213f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not running\n");
1214f2c2e717SAndrey Konovalov 		ret = -EINVAL;
1215f2c2e717SAndrey Konovalov 		goto out_unlock;
1216f2c2e717SAndrey Konovalov 	}
1217f2c2e717SAndrey Konovalov 	if (!dev->gadget) {
1218f2c2e717SAndrey Konovalov 		dev_dbg(dev->dev, "fail, gadget is not bound\n");
1219f2c2e717SAndrey Konovalov 		ret = -EBUSY;
1220f2c2e717SAndrey Konovalov 		goto out_unlock;
1221f2c2e717SAndrey Konovalov 	}
1222f2c2e717SAndrey Konovalov 	usb_gadget_vbus_draw(dev->gadget, 2 * value);
1223f2c2e717SAndrey Konovalov 
1224f2c2e717SAndrey Konovalov out_unlock:
1225f2c2e717SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
1226f2c2e717SAndrey Konovalov 	return ret;
1227f2c2e717SAndrey Konovalov }
1228f2c2e717SAndrey Konovalov 
fill_ep_caps(struct usb_ep_caps * caps,struct usb_raw_ep_caps * raw_caps)122997df5e57SAndrey Konovalov static void fill_ep_caps(struct usb_ep_caps *caps,
123097df5e57SAndrey Konovalov 				struct usb_raw_ep_caps *raw_caps)
123197df5e57SAndrey Konovalov {
123297df5e57SAndrey Konovalov 	raw_caps->type_control = caps->type_control;
123397df5e57SAndrey Konovalov 	raw_caps->type_iso = caps->type_iso;
123497df5e57SAndrey Konovalov 	raw_caps->type_bulk = caps->type_bulk;
123597df5e57SAndrey Konovalov 	raw_caps->type_int = caps->type_int;
123697df5e57SAndrey Konovalov 	raw_caps->dir_in = caps->dir_in;
123797df5e57SAndrey Konovalov 	raw_caps->dir_out = caps->dir_out;
123897df5e57SAndrey Konovalov }
123997df5e57SAndrey Konovalov 
fill_ep_limits(struct usb_ep * ep,struct usb_raw_ep_limits * limits)124097df5e57SAndrey Konovalov static void fill_ep_limits(struct usb_ep *ep, struct usb_raw_ep_limits *limits)
124197df5e57SAndrey Konovalov {
124297df5e57SAndrey Konovalov 	limits->maxpacket_limit = ep->maxpacket_limit;
124397df5e57SAndrey Konovalov 	limits->max_streams = ep->max_streams;
124497df5e57SAndrey Konovalov }
124597df5e57SAndrey Konovalov 
raw_ioctl_eps_info(struct raw_dev * dev,unsigned long value)124697df5e57SAndrey Konovalov static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value)
124797df5e57SAndrey Konovalov {
124897df5e57SAndrey Konovalov 	int ret = 0, i;
124997df5e57SAndrey Konovalov 	unsigned long flags;
125097df5e57SAndrey Konovalov 	struct usb_raw_eps_info *info;
125197df5e57SAndrey Konovalov 	struct raw_ep *ep;
125297df5e57SAndrey Konovalov 
1253beb8518eSJulia Lawall 	info = kzalloc(sizeof(*info), GFP_KERNEL);
125497df5e57SAndrey Konovalov 	if (!info) {
125597df5e57SAndrey Konovalov 		ret = -ENOMEM;
125697df5e57SAndrey Konovalov 		goto out;
125797df5e57SAndrey Konovalov 	}
125897df5e57SAndrey Konovalov 
125997df5e57SAndrey Konovalov 	spin_lock_irqsave(&dev->lock, flags);
126097df5e57SAndrey Konovalov 	if (dev->state != STATE_DEV_RUNNING) {
126197df5e57SAndrey Konovalov 		dev_dbg(dev->dev, "fail, device is not running\n");
126297df5e57SAndrey Konovalov 		ret = -EINVAL;
126397df5e57SAndrey Konovalov 		spin_unlock_irqrestore(&dev->lock, flags);
126497df5e57SAndrey Konovalov 		goto out_free;
126597df5e57SAndrey Konovalov 	}
126697df5e57SAndrey Konovalov 	if (!dev->gadget) {
126797df5e57SAndrey Konovalov 		dev_dbg(dev->dev, "fail, gadget is not bound\n");
126897df5e57SAndrey Konovalov 		ret = -EBUSY;
126997df5e57SAndrey Konovalov 		spin_unlock_irqrestore(&dev->lock, flags);
127097df5e57SAndrey Konovalov 		goto out_free;
127197df5e57SAndrey Konovalov 	}
127297df5e57SAndrey Konovalov 
127397df5e57SAndrey Konovalov 	for (i = 0; i < dev->eps_num; i++) {
127497df5e57SAndrey Konovalov 		ep = &dev->eps[i];
127597df5e57SAndrey Konovalov 		strscpy(&info->eps[i].name[0], ep->ep->name,
127697df5e57SAndrey Konovalov 				USB_RAW_EP_NAME_MAX);
127797df5e57SAndrey Konovalov 		info->eps[i].addr = ep->addr;
127897df5e57SAndrey Konovalov 		fill_ep_caps(&ep->ep->caps, &info->eps[i].caps);
127997df5e57SAndrey Konovalov 		fill_ep_limits(ep->ep, &info->eps[i].limits);
128097df5e57SAndrey Konovalov 	}
128197df5e57SAndrey Konovalov 	ret = dev->eps_num;
128297df5e57SAndrey Konovalov 	spin_unlock_irqrestore(&dev->lock, flags);
128397df5e57SAndrey Konovalov 
128497df5e57SAndrey Konovalov 	if (copy_to_user((void __user *)value, info, sizeof(*info)))
128597df5e57SAndrey Konovalov 		ret = -EFAULT;
128697df5e57SAndrey Konovalov 
128797df5e57SAndrey Konovalov out_free:
128897df5e57SAndrey Konovalov 	kfree(info);
128997df5e57SAndrey Konovalov out:
129097df5e57SAndrey Konovalov 	return ret;
129197df5e57SAndrey Konovalov }
129297df5e57SAndrey Konovalov 
raw_ioctl(struct file * fd,unsigned int cmd,unsigned long value)1293f2c2e717SAndrey Konovalov static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
1294f2c2e717SAndrey Konovalov {
1295f2c2e717SAndrey Konovalov 	struct raw_dev *dev = fd->private_data;
1296f2c2e717SAndrey Konovalov 	int ret = 0;
1297f2c2e717SAndrey Konovalov 
1298f2c2e717SAndrey Konovalov 	if (!dev)
1299f2c2e717SAndrey Konovalov 		return -EBUSY;
1300f2c2e717SAndrey Konovalov 
1301f2c2e717SAndrey Konovalov 	switch (cmd) {
1302f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_INIT:
1303f2c2e717SAndrey Konovalov 		ret = raw_ioctl_init(dev, value);
1304f2c2e717SAndrey Konovalov 		break;
1305f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_RUN:
1306f2c2e717SAndrey Konovalov 		ret = raw_ioctl_run(dev, value);
1307f2c2e717SAndrey Konovalov 		break;
1308f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_EVENT_FETCH:
1309f2c2e717SAndrey Konovalov 		ret = raw_ioctl_event_fetch(dev, value);
1310f2c2e717SAndrey Konovalov 		break;
1311f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_EP0_WRITE:
1312f2c2e717SAndrey Konovalov 		ret = raw_ioctl_ep0_write(dev, value);
1313f2c2e717SAndrey Konovalov 		break;
1314f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_EP0_READ:
1315f2c2e717SAndrey Konovalov 		ret = raw_ioctl_ep0_read(dev, value);
1316f2c2e717SAndrey Konovalov 		break;
1317f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_EP_ENABLE:
1318f2c2e717SAndrey Konovalov 		ret = raw_ioctl_ep_enable(dev, value);
1319f2c2e717SAndrey Konovalov 		break;
1320f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_EP_DISABLE:
1321f2c2e717SAndrey Konovalov 		ret = raw_ioctl_ep_disable(dev, value);
1322f2c2e717SAndrey Konovalov 		break;
1323f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_EP_WRITE:
1324f2c2e717SAndrey Konovalov 		ret = raw_ioctl_ep_write(dev, value);
1325f2c2e717SAndrey Konovalov 		break;
1326f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_EP_READ:
1327f2c2e717SAndrey Konovalov 		ret = raw_ioctl_ep_read(dev, value);
1328f2c2e717SAndrey Konovalov 		break;
1329f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_CONFIGURE:
1330f2c2e717SAndrey Konovalov 		ret = raw_ioctl_configure(dev, value);
1331f2c2e717SAndrey Konovalov 		break;
1332f2c2e717SAndrey Konovalov 	case USB_RAW_IOCTL_VBUS_DRAW:
1333f2c2e717SAndrey Konovalov 		ret = raw_ioctl_vbus_draw(dev, value);
1334f2c2e717SAndrey Konovalov 		break;
133597df5e57SAndrey Konovalov 	case USB_RAW_IOCTL_EPS_INFO:
133697df5e57SAndrey Konovalov 		ret = raw_ioctl_eps_info(dev, value);
133797df5e57SAndrey Konovalov 		break;
1338c61769bdSAndrey Konovalov 	case USB_RAW_IOCTL_EP0_STALL:
1339c61769bdSAndrey Konovalov 		ret = raw_ioctl_ep0_stall(dev, value);
1340c61769bdSAndrey Konovalov 		break;
1341c61769bdSAndrey Konovalov 	case USB_RAW_IOCTL_EP_SET_HALT:
1342c61769bdSAndrey Konovalov 		ret = raw_ioctl_ep_set_clear_halt_wedge(
1343c61769bdSAndrey Konovalov 					dev, value, true, true);
1344c61769bdSAndrey Konovalov 		break;
1345c61769bdSAndrey Konovalov 	case USB_RAW_IOCTL_EP_CLEAR_HALT:
1346c61769bdSAndrey Konovalov 		ret = raw_ioctl_ep_set_clear_halt_wedge(
1347c61769bdSAndrey Konovalov 					dev, value, false, true);
1348c61769bdSAndrey Konovalov 		break;
1349c61769bdSAndrey Konovalov 	case USB_RAW_IOCTL_EP_SET_WEDGE:
1350c61769bdSAndrey Konovalov 		ret = raw_ioctl_ep_set_clear_halt_wedge(
1351c61769bdSAndrey Konovalov 					dev, value, true, false);
1352c61769bdSAndrey Konovalov 		break;
1353f2c2e717SAndrey Konovalov 	default:
1354f2c2e717SAndrey Konovalov 		ret = -EINVAL;
1355f2c2e717SAndrey Konovalov 	}
1356f2c2e717SAndrey Konovalov 
1357f2c2e717SAndrey Konovalov 	return ret;
1358f2c2e717SAndrey Konovalov }
1359f2c2e717SAndrey Konovalov 
1360f2c2e717SAndrey Konovalov /*----------------------------------------------------------------------*/
1361f2c2e717SAndrey Konovalov 
1362f2c2e717SAndrey Konovalov static const struct file_operations raw_fops = {
1363f2c2e717SAndrey Konovalov 	.open =			raw_open,
1364f2c2e717SAndrey Konovalov 	.unlocked_ioctl =	raw_ioctl,
1365f2c2e717SAndrey Konovalov 	.compat_ioctl =		raw_ioctl,
1366f2c2e717SAndrey Konovalov 	.release =		raw_release,
1367f2c2e717SAndrey Konovalov };
1368f2c2e717SAndrey Konovalov 
1369f2c2e717SAndrey Konovalov static struct miscdevice raw_misc_device = {
1370f2c2e717SAndrey Konovalov 	.minor = MISC_DYNAMIC_MINOR,
1371f2c2e717SAndrey Konovalov 	.name = DRIVER_NAME,
1372f2c2e717SAndrey Konovalov 	.fops = &raw_fops,
1373f2c2e717SAndrey Konovalov };
1374f2c2e717SAndrey Konovalov 
1375f2c2e717SAndrey Konovalov module_misc_device(raw_misc_device);
1376