// SPDX-License-Identifier: GPL-2.0+
/*
* f_hid.c -- USB HID function driver
*
* Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hid.h>
#include <linux/idr.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/usb/g_hid.h>
#include "u_f.h"
#include "u_hid.h"
#define HIDG_MINORS 4
static int major, minors;
static struct class *hidg_class;
static DEFINE_IDA(hidg_ida);
static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
/*-------------------------------------------------------------------------*/
/* HID gadget struct */
struct f_hidg_req_list {
struct usb_request *req;
unsigned int pos;
struct list_head list;
};
struct f_hidg {
/* configuration */
unsigned char bInterfaceSubClass;
unsigned char bInterfaceProtocol;
unsigned char protocol;
unsigned char idle;
unsigned short report_desc_length;
char *report_desc;
unsigned short report_length;
/*
* use_out_ep - if true, the OUT Endpoint (interrupt out method)
* will be used to receive reports from the host
* using functions with the "intout" suffix.
* Otherwise, the OUT Endpoint will not be configured
* and the SETUP/SET_REPORT method ("ssreport" suffix)
* will be used to receive reports.
*/
bool use_out_ep;
/* recv report */
spinlock_t read_spinlock;
wait_queue_head_t read_queue;
/* recv report - interrupt out only (use_out_ep == 1) */
struct list_head completed_out_req;
unsigned int qlen;
/* recv report - setup set_report only (use_out_ep == 0) */
char *set_report_buf;
unsigned int set_report_length;
/* send report */
spinlock_t write_spinlock;
bool write_pending;
wait_queue_head_t write_queue;
struct usb_request *req;
struct device dev;
struct cdev cdev;
struct usb_function func;
struct usb_ep *in_ep;
struct usb_ep *out_ep;
};
static inline struct f_hidg *func_to_hidg(struct usb_function *f)
{
return container_of(f, struct f_hidg, func);
}
static void hidg_release(struct device *dev)
{
struct f_hidg *hidg = container_of(dev, struct f_hidg, dev);
kfree(hidg->set_report_buf);
kfree(hidg);
}
/*-------------------------------------------------------------------------*/
/* Static descriptors */
static struct usb_interface_descriptor hidg_interface_desc = {
.bLength = sizeof hidg_interface_desc,