// SPDX-License-Identifier: GPL-2.0
/*
* USB Raw Gadget driver.
* See Documentation/usb/raw-gadget.rst for more details.
*
* Andrey Konovalov <andreyknvl@gmail.com>
*/
#include <linux/compiler.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/kref.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/semaphore.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
#include <linux/usb/ch11.h>
#include <linux/usb/gadget.h>
#include <uapi/linux/usb/raw_gadget.h>
#define DRIVER_DESC "USB Raw Gadget"
#define DRIVER_NAME "raw-gadget"
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Andrey Konovalov");
MODULE_LICENSE("GPL");
/*----------------------------------------------------------------------*/
#define RAW_EVENT_QUEUE_SIZE 16
struct raw_event_queue {
/* See the comment in raw_event_queue_fetch() for locking details. */
spinlock_t lock;
struct semaphore sema;
struct usb_raw_event *events[RAW_EVENT_QUEUE_SIZE];
int size;
};
static void raw_event_queue_init(struct raw_event_queue *queue)
{
spin_lock_init(&queue->lock);
sema_init(&queue->sema, 0);
queue->size = 0;
}
static int raw_event_queue_add(struct raw_event_queue *queue,
enum usb_raw_event_type type, size_t length, const void *data)
{
unsigned long flags;
struct usb_raw_event *event;
spin_lock_irqsave(&queue->lock, flags);
if (WARN_ON(queue->size >= RAW_EVENT_QUEUE_SIZE)) {
spin_unlock_irqrestore(&queue->lock, flags);
return -ENOMEM;
}
event = kmalloc(sizeof(*event) + length, GFP_ATOMIC);
if (!event) {
spin_unlock_irqrestore(&queue->lock, flags);
return -ENOMEM;
}
event->type = type;
event->length = length;
if (event->length)
memcpy(&event->data[0], data, length);
queue->events[queue->size] = event;
queue->size++;
up(&queue->sema);
spin_unlock_irqrestore(&queue->lock, flags);
return 0;
}
static struct usb_raw_event *raw_event_queue_fetch(
struct raw_event_queue *queue)
{
unsigned long flags;
struct usb_raw_event *event;
/*
* This function can be called concurrently. We first check that
* there's at least one event queued by decrementing the semaphore,
* and then take the lock to protect queue struct fields.
*/
if (down_interruptible(&queue->sema))
return NULL;
spin_lock_irqsave(&queue->lock, flags);
if (WARN_ON(!queue->size))
return NULL;
event = queue->events[0];
queue->size--;
memmove(&queue->events[0], &queue->events[1],
queue->size * sizeof(queue->events[0]));
spin_unlock_irqrestore(&queue->lock, flags);
return event;
}
static void raw_event_queue_destroy(struct raw_event_queue *queue)
{
int i;
for (i = 0; i < queue->size; i++)
kfree(queue->events[i]);
queue->size = 0;
}
/*----------------------------------------------------------------------*/
struct raw_dev;
#define USB_RAW_MAX_ENDPOINTS 32
enum ep_state {
STATE_EP_DISABLED,
STATE_EP_ENABLED,
};
struct raw_ep {
struct raw_dev *dev;
enum ep_state state;
struct usb_ep *ep;
struct usb_request *req;
bool urb_queued;
bool disabling;
ssize_t status;
};
enum dev_state {
STATE_DEV_INVALID = 0,
STATE_DEV_OPENED,
STATE_DEV_INITIALIZED,
STATE_DEV_RUNNING,
STATE_DEV_CLOSED,
STATE_DEV_FAILED
};
struct raw_dev {
struct kref count;