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