// 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/workqueue.h>#include <linux/usb/func_utils.h>#include <linux/usb/g_hid.h>#include <uapi/linux/usb/g_hid.h> #include "u_hid.h" #define HIDG_MINORS 4 /* * Most operating systems seem to allow for 5000ms timeout, we will allow * userspace half that time to respond before we return an empty report. */#define GET_REPORT_TIMEOUT_MS 2500 static int major, minors; static const struct class hidg_class = { .name = "hidg",}; static DEFINE_IDA(hidg_ida);static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */ struct report_entry { struct usb_hidg_report report_data; struct list_head node;}; /*-------------------------------------------------------------------------*//* 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; unsigned char interval; bool interval_user_set; /* * 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; bool disabled; /* 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; /* get report */ struct usb_request *get_req; struct usb_hidg_report get_report; bool get_report_returned; int get_report_req_report_id; int get_report_req_report_length; spinlock_t get_report_spinlock; wait_queue_head_t get_queue; /* Waiting for userspace response */ wait_queue_head_t get_id_queue; /* Get ID came in */ struct work_struct work; struct workqueue_struct *workqueue; struct list_head report_list; 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->report_desc); kfree(hidg->set_report_buf); kfree(hidg);} /*-------------------------------------------------------------------------*//* Static descriptors */ static struct usb_interface_descriptor hidg_interface_desc = { .bLength = sizeof hidg_interface_desc, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ .bAlternateSetting = 0, /* .bNumEndpoints = DYNAMIC (depends on use_out_ep) */ .bInterfaceClass = USB_CLASS_HID, /* .bInterfaceSubClass = DYNAMIC */ /* .bInterfaceProtocol = DYNAMIC */ /* .iInterface = DYNAMIC */}; static struct hid_descriptor hidg_desc = { .bLength = sizeof hidg_desc, .bDescriptorType = HID_DT_HID, .bcdHID = cpu_to_le16(0x0101), .bCountryCode = 0x00, .bNumDescriptors = 0x1, /*.rpt_desc.bDescriptorType = DYNAMIC */ /*.rpt_desc.wDescriptorLength = DYNAMIC */}; /* Super-Speed Support */ static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /*.bInterval = DYNAMIC */}; static struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = { .bLength = sizeof(hidg_ss_in_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */ /* .wBytesPerInterval = DYNAMIC */}; static struct usb_endpoint_descriptor hidg_ss_out_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /*.bInterval = DYNAMIC */}; static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = { .bLength = sizeof(hidg_ss_out_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */ /* .wBytesPerInterval = DYNAMIC */}; static struct usb_descriptor_header *hidg_ss_descriptors_intout[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_ss_in_ep_desc, (struct usb_descriptor_header *)&hidg_ss_in_comp_desc, (struct usb_descriptor_header *)&hidg_ss_out_ep_desc, (struct usb_descriptor_header *)&hidg_ss_out_comp_desc, NULL,}; static struct usb_descriptor_header *hidg_ss_descriptors_ssreport[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_ss_in_ep_desc, (struct usb_descriptor_header *)&hidg_ss_in_comp_desc, NULL,}; /* High-Speed Support */ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /* .bInterval = DYNAMIC */}; static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /*.bInterval = DYNAMIC */}; static struct usb_descriptor_header *hidg_hs_descriptors_intout[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, (struct usb_descriptor_header *)&hidg_hs_out_ep_desc, NULL,}; static struct usb_descriptor_header *hidg_hs_descriptors_ssreport[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, NULL,}; /* Full-Speed Support */ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /*.bInterval = DYNAMIC */}; static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /*.bInterval = DYNAMIC */}; static struct usb_descriptor_header *hidg_fs_descriptors_intout[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, (struct usb_descriptor_header *)&hidg_fs_out_ep_desc, NULL,}; static struct usb_descriptor_header *hidg_fs_descriptors_ssreport[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, NULL,}; /*-------------------------------------------------------------------------*//* Strings */ #define CT_FUNC_HID_IDX 0 static struct usb_string ct_func_string_defs[] = { [CT_FUNC_HID_IDX].s = "HID Interface", {}, /* end of list */}; static struct usb_gadget_strings ct_func_string_table = { .language = 0x0409, /* en-US */ .strings = ct_func_string_defs,}; static struct usb_gadget_strings *ct_func_strings[] = { &ct_func_string_table, NULL,}; /*-------------------------------------------------------------------------*//* Char Device */ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer, size_t count, loff_t *ptr){ struct f_hidg *hidg = file->private_data; struct f_hidg_req_list *list; struct usb_request *req; unsigned long flags; int ret; if (!count) return 0; spin_lock_irqsave(&hidg->read_spinlock, flags); #define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req) || hidg->disabled) /* wait for at least one buffer to complete */ while (!READ_COND_INTOUT) { spin_unlock_irqrestore(&hidg->read_spinlock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(hidg->read_queue, READ_COND_INTOUT)) return -ERESTARTSYS; spin_lock_irqsave(&hidg->read_spinlock, flags); } if (hidg->disabled) { spin_unlock_irqrestore(&hidg->read_spinlock, flags); return -ESHUTDOWN; } /* pick the first one */ list = list_first_entry(&hidg->completed_out_req, struct f_hidg_req_list, list); /* * Remove this from list to protect it from beign free() * while host disables our function */ list_del(&list->list); req = list->req; count = min_t(unsigned int, count, req->actual - list->pos); spin_unlock_irqrestore(&hidg->read_spinlock, flags); /* copy to user outside spinlock */ count -= copy_to_user(buffer, req->buf + list->pos, count); list->pos += count; /* * if this request is completely handled and transfered to * userspace, remove its entry from the list and requeue it * again. Otherwise, we will revisit it again upon the next * call, taking into account its current read position. */ if (list->pos == req->actual) { kfree(list); req->length = hidg->report_length; ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL); if (ret < 0) { free_ep_req(hidg->out_ep, req); return ret; } } else { spin_lock_irqsave(&hidg->read_spinlock, flags); list_add(&list->list, &hidg->completed_out_req); spin_unlock_irqrestore(&hidg->read_spinlock, flags); wake_up(&hidg->read_queue); } return count;} #define READ_COND_SSREPORT (hidg->set_report_buf != NULL || hidg->disabled) static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer, size_t count, loff_t *ptr){ struct f_hidg *hidg = file->private_data; char *tmp_buf = NULL; unsigned long flags; if (!count) return 0; spin_lock_irqsave(&hidg->read_spinlock, flags); while (!READ_COND_SSREPORT) { spin_unlock_irqrestore(&hidg->read_spinlock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(hidg->read_queue, READ_COND_SSREPORT)) return -ERESTARTSYS; spin_lock_irqsave(&hidg->read_spinlock, flags); } count = min_t(unsigned int, count, hidg->set_report_length); tmp_buf = hidg->set_report_buf; hidg->set_report_buf = NULL; spin_unlock_irqrestore(&hidg->read_spinlock, flags); if (tmp_buf != NULL) { count -= copy_to_user(buffer, tmp_buf, count); kfree(tmp_buf); } else { count = -ENOMEM; } wake_up(&hidg->read_queue); return count;} static ssize_t f_hidg_read(struct file *file, char __user *buffer, size_t count, loff_t *ptr){ struct f_hidg *hidg = file->private_data; if (hidg->use_out_ep) return f_hidg_intout_read(file, buffer, count, ptr); else return f_hidg_ssreport_read(file, buffer, count, ptr);} static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req){ struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; unsigned long flags; if (req->status != 0) { ERROR(hidg->func.config->cdev, "End Point Request ERROR: %d\n", req->status); } spin_lock_irqsave(&hidg->write_spinlock, flags); hidg->write_pending = 0; spin_unlock_irqrestore(&hidg->write_spinlock, flags); wake_up(&hidg->write_queue);} static ssize_t f_hidg_write(struct file *file, const char __user *buffer, size_t count, loff_t *offp){ struct f_hidg *hidg = file->private_data; struct usb_request *req; unsigned long flags; ssize_t status = -ENOMEM; spin_lock_irqsave(&hidg->write_spinlock, flags); if (!hidg->req) { spin_unlock_irqrestore(&hidg->write_spinlock, flags); return -ESHUTDOWN; } #define WRITE_COND (!hidg->write_pending)try_again: /* write queue */ while (!WRITE_COND) { spin_unlock_irqrestore(&hidg->write_spinlock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible_exclusive( hidg->write_queue, WRITE_COND)) return -ERESTARTSYS; spin_lock_irqsave(&hidg->write_spinlock, flags); } hidg->write_pending = 1; req = hidg->req; count = min_t(unsigned, count, hidg->report_length); spin_unlock_irqrestore(&hidg->write_spinlock, flags); if (!req) { ERROR(hidg->func.config->cdev, "hidg->req is NULL\n"); status = -ESHUTDOWN; goto release_write_pending; } status = copy_from_user(req->buf, buffer, count); if (status != 0) { ERROR(hidg->func.config->cdev, "copy_from_user error\n"); status = -EINVAL; goto release_write_pending; } Lines 1–500 of 1700. Load more
// 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/workqueue.h> #include <linux/usb/func_utils.h> #include <linux/usb/g_hid.h> #include <uapi/linux/usb/g_hid.h> #include "u_hid.h" #define HIDG_MINORS 4 /* * Most operating systems seem to allow for 5000ms timeout, we will allow * userspace half that time to respond before we return an empty report. */ #define GET_REPORT_TIMEOUT_MS 2500 static int major, minors; static const struct class hidg_class = { .name = "hidg", }; static DEFINE_IDA(hidg_ida); static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */ struct report_entry { struct usb_hidg_report report_data; struct list_head node; }; /*-------------------------------------------------------------------------*/ /* 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; unsigned char interval; bool interval_user_set; /* * 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; bool disabled; /* 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; /* get report */ struct usb_request *get_req; struct usb_hidg_report get_report; bool get_report_returned; int get_report_req_report_id; int get_report_req_report_length; spinlock_t get_report_spinlock; wait_queue_head_t get_queue; /* Waiting for userspace response */ wait_queue_head_t get_id_queue; /* Get ID came in */ struct work_struct work; struct workqueue_struct *workqueue; struct list_head report_list; 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->report_desc); kfree(hidg->set_report_buf); kfree(hidg); } /*-------------------------------------------------------------------------*/ /* Static descriptors */ static struct usb_interface_descriptor hidg_interface_desc = { .bLength = sizeof hidg_interface_desc, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ .bAlternateSetting = 0, /* .bNumEndpoints = DYNAMIC (depends on use_out_ep) */ .bInterfaceClass = USB_CLASS_HID, /* .bInterfaceSubClass = DYNAMIC */ /* .bInterfaceProtocol = DYNAMIC */ /* .iInterface = DYNAMIC */ }; static struct hid_descriptor hidg_desc = { .bLength = sizeof hidg_desc, .bDescriptorType = HID_DT_HID, .bcdHID = cpu_to_le16(0x0101), .bCountryCode = 0x00, .bNumDescriptors = 0x1, /*.rpt_desc.bDescriptorType = DYNAMIC */ /*.rpt_desc.wDescriptorLength = DYNAMIC */ }; /* Super-Speed Support */ static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /*.bInterval = DYNAMIC */ }; static struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = { .bLength = sizeof(hidg_ss_in_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */ /* .wBytesPerInterval = DYNAMIC */ }; static struct usb_endpoint_descriptor hidg_ss_out_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /*.bInterval = DYNAMIC */ }; static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = { .bLength = sizeof(hidg_ss_out_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, /* .bMaxBurst = 0, */ /* .bmAttributes = 0, */ /* .wBytesPerInterval = DYNAMIC */ }; static struct usb_descriptor_header *hidg_ss_descriptors_intout[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_ss_in_ep_desc, (struct usb_descriptor_header *)&hidg_ss_in_comp_desc, (struct usb_descriptor_header *)&hidg_ss_out_ep_desc, (struct usb_descriptor_header *)&hidg_ss_out_comp_desc, NULL, }; static struct usb_descriptor_header *hidg_ss_descriptors_ssreport[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_ss_in_ep_desc, (struct usb_descriptor_header *)&hidg_ss_in_comp_desc, NULL, }; /* High-Speed Support */ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /* .bInterval = DYNAMIC */ }; static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /*.bInterval = DYNAMIC */ }; static struct usb_descriptor_header *hidg_hs_descriptors_intout[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, (struct usb_descriptor_header *)&hidg_hs_out_ep_desc, NULL, }; static struct usb_descriptor_header *hidg_hs_descriptors_ssreport[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, NULL, }; /* Full-Speed Support */ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /*.bInterval = DYNAMIC */ }; static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ /*.bInterval = DYNAMIC */ }; static struct usb_descriptor_header *hidg_fs_descriptors_intout[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, (struct usb_descriptor_header *)&hidg_fs_out_ep_desc, NULL, }; static struct usb_descriptor_header *hidg_fs_descriptors_ssreport[] = { (struct usb_descriptor_header *)&hidg_interface_desc, (struct usb_descriptor_header *)&hidg_desc, (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, NULL, }; /*-------------------------------------------------------------------------*/ /* Strings */ #define CT_FUNC_HID_IDX 0 static struct usb_string ct_func_string_defs[] = { [CT_FUNC_HID_IDX].s = "HID Interface", {}, /* end of list */ }; static struct usb_gadget_strings ct_func_string_table = { .language = 0x0409, /* en-US */ .strings = ct_func_string_defs, }; static struct usb_gadget_strings *ct_func_strings[] = { &ct_func_string_table, NULL, }; /*-------------------------------------------------------------------------*/ /* Char Device */ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer, size_t count, loff_t *ptr) { struct f_hidg *hidg = file->private_data; struct f_hidg_req_list *list; struct usb_request *req; unsigned long flags; int ret; if (!count) return 0; spin_lock_irqsave(&hidg->read_spinlock, flags); #define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req) || hidg->disabled) /* wait for at least one buffer to complete */ while (!READ_COND_INTOUT) { spin_unlock_irqrestore(&hidg->read_spinlock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(hidg->read_queue, READ_COND_INTOUT)) return -ERESTARTSYS; spin_lock_irqsave(&hidg->read_spinlock, flags); } if (hidg->disabled) { spin_unlock_irqrestore(&hidg->read_spinlock, flags); return -ESHUTDOWN; } /* pick the first one */ list = list_first_entry(&hidg->completed_out_req, struct f_hidg_req_list, list); /* * Remove this from list to protect it from beign free() * while host disables our function */ list_del(&list->list); req = list->req; count = min_t(unsigned int, count, req->actual - list->pos); spin_unlock_irqrestore(&hidg->read_spinlock, flags); /* copy to user outside spinlock */ count -= copy_to_user(buffer, req->buf + list->pos, count); list->pos += count; /* * if this request is completely handled and transfered to * userspace, remove its entry from the list and requeue it * again. Otherwise, we will revisit it again upon the next * call, taking into account its current read position. */ if (list->pos == req->actual) { kfree(list); req->length = hidg->report_length; ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL); if (ret < 0) { free_ep_req(hidg->out_ep, req); return ret; } } else { spin_lock_irqsave(&hidg->read_spinlock, flags); list_add(&list->list, &hidg->completed_out_req); spin_unlock_irqrestore(&hidg->read_spinlock, flags); wake_up(&hidg->read_queue); } return count; } #define READ_COND_SSREPORT (hidg->set_report_buf != NULL || hidg->disabled) static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer, size_t count, loff_t *ptr) { struct f_hidg *hidg = file->private_data; char *tmp_buf = NULL; unsigned long flags; if (!count) return 0; spin_lock_irqsave(&hidg->read_spinlock, flags); while (!READ_COND_SSREPORT) { spin_unlock_irqrestore(&hidg->read_spinlock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(hidg->read_queue, READ_COND_SSREPORT)) return -ERESTARTSYS; spin_lock_irqsave(&hidg->read_spinlock, flags); } count = min_t(unsigned int, count, hidg->set_report_length); tmp_buf = hidg->set_report_buf; hidg->set_report_buf = NULL; spin_unlock_irqrestore(&hidg->read_spinlock, flags); if (tmp_buf != NULL) { count -= copy_to_user(buffer, tmp_buf, count); kfree(tmp_buf); } else { count = -ENOMEM; } wake_up(&hidg->read_queue); return count; } static ssize_t f_hidg_read(struct file *file, char __user *buffer, size_t count, loff_t *ptr) { struct f_hidg *hidg = file->private_data; if (hidg->use_out_ep) return f_hidg_intout_read(file, buffer, count, ptr); else return f_hidg_ssreport_read(file, buffer, count, ptr); } static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req) { struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; unsigned long flags; if (req->status != 0) { ERROR(hidg->func.config->cdev, "End Point Request ERROR: %d\n", req->status); } spin_lock_irqsave(&hidg->write_spinlock, flags); hidg->write_pending = 0; spin_unlock_irqrestore(&hidg->write_spinlock, flags); wake_up(&hidg->write_queue); } static ssize_t f_hidg_write(struct file *file, const char __user *buffer, size_t count, loff_t *offp) { struct f_hidg *hidg = file->private_data; struct usb_request *req; unsigned long flags; ssize_t status = -ENOMEM; spin_lock_irqsave(&hidg->write_spinlock, flags); if (!hidg->req) { spin_unlock_irqrestore(&hidg->write_spinlock, flags); return -ESHUTDOWN; } #define WRITE_COND (!hidg->write_pending) try_again: /* write queue */ while (!WRITE_COND) { spin_unlock_irqrestore(&hidg->write_spinlock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible_exclusive( hidg->write_queue, WRITE_COND)) return -ERESTARTSYS; spin_lock_irqsave(&hidg->write_spinlock, flags); } hidg->write_pending = 1; req = hidg->req; count = min_t(unsigned, count, hidg->report_length); spin_unlock_irqrestore(&hidg->write_spinlock, flags); if (!req) { ERROR(hidg->func.config->cdev, "hidg->req is NULL\n"); status = -ESHUTDOWN; goto release_write_pending; } status = copy_from_user(req->buf, buffer, count); if (status != 0) { ERROR(hidg->func.config->cdev, "copy_from_user error\n"); status = -EINVAL; goto release_write_pending; }