100a2430fSAndrzej Pietrasiewicz /* 200a2430fSAndrzej Pietrasiewicz * f_hid.c -- USB HID function driver 300a2430fSAndrzej Pietrasiewicz * 400a2430fSAndrzej Pietrasiewicz * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com> 500a2430fSAndrzej Pietrasiewicz * 600a2430fSAndrzej Pietrasiewicz * This program is free software; you can redistribute it and/or modify 700a2430fSAndrzej Pietrasiewicz * it under the terms of the GNU General Public License as published by 800a2430fSAndrzej Pietrasiewicz * the Free Software Foundation; either version 2 of the License, or 900a2430fSAndrzej Pietrasiewicz * (at your option) any later version. 1000a2430fSAndrzej Pietrasiewicz */ 1100a2430fSAndrzej Pietrasiewicz 1200a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h> 1300a2430fSAndrzej Pietrasiewicz #include <linux/module.h> 1400a2430fSAndrzej Pietrasiewicz #include <linux/hid.h> 1500a2430fSAndrzej Pietrasiewicz #include <linux/cdev.h> 1600a2430fSAndrzej Pietrasiewicz #include <linux/mutex.h> 1700a2430fSAndrzej Pietrasiewicz #include <linux/poll.h> 1800a2430fSAndrzej Pietrasiewicz #include <linux/uaccess.h> 1900a2430fSAndrzej Pietrasiewicz #include <linux/wait.h> 2000a2430fSAndrzej Pietrasiewicz #include <linux/sched.h> 2100a2430fSAndrzej Pietrasiewicz #include <linux/usb/g_hid.h> 2200a2430fSAndrzej Pietrasiewicz 2300a2430fSAndrzej Pietrasiewicz #include "u_f.h" 2400a2430fSAndrzej Pietrasiewicz 2500a2430fSAndrzej Pietrasiewicz static int major, minors; 2600a2430fSAndrzej Pietrasiewicz static struct class *hidg_class; 2700a2430fSAndrzej Pietrasiewicz 2800a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 2900a2430fSAndrzej Pietrasiewicz /* HID gadget struct */ 3000a2430fSAndrzej Pietrasiewicz 3100a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list { 3200a2430fSAndrzej Pietrasiewicz struct usb_request *req; 3300a2430fSAndrzej Pietrasiewicz unsigned int pos; 3400a2430fSAndrzej Pietrasiewicz struct list_head list; 3500a2430fSAndrzej Pietrasiewicz }; 3600a2430fSAndrzej Pietrasiewicz 3700a2430fSAndrzej Pietrasiewicz struct f_hidg { 3800a2430fSAndrzej Pietrasiewicz /* configuration */ 3900a2430fSAndrzej Pietrasiewicz unsigned char bInterfaceSubClass; 4000a2430fSAndrzej Pietrasiewicz unsigned char bInterfaceProtocol; 4100a2430fSAndrzej Pietrasiewicz unsigned short report_desc_length; 4200a2430fSAndrzej Pietrasiewicz char *report_desc; 4300a2430fSAndrzej Pietrasiewicz unsigned short report_length; 4400a2430fSAndrzej Pietrasiewicz 4500a2430fSAndrzej Pietrasiewicz /* recv report */ 4600a2430fSAndrzej Pietrasiewicz struct list_head completed_out_req; 4700a2430fSAndrzej Pietrasiewicz spinlock_t spinlock; 4800a2430fSAndrzej Pietrasiewicz wait_queue_head_t read_queue; 4900a2430fSAndrzej Pietrasiewicz unsigned int qlen; 5000a2430fSAndrzej Pietrasiewicz 5100a2430fSAndrzej Pietrasiewicz /* send report */ 5200a2430fSAndrzej Pietrasiewicz struct mutex lock; 5300a2430fSAndrzej Pietrasiewicz bool write_pending; 5400a2430fSAndrzej Pietrasiewicz wait_queue_head_t write_queue; 5500a2430fSAndrzej Pietrasiewicz struct usb_request *req; 5600a2430fSAndrzej Pietrasiewicz 5700a2430fSAndrzej Pietrasiewicz int minor; 5800a2430fSAndrzej Pietrasiewicz struct cdev cdev; 5900a2430fSAndrzej Pietrasiewicz struct usb_function func; 6000a2430fSAndrzej Pietrasiewicz 6100a2430fSAndrzej Pietrasiewicz struct usb_ep *in_ep; 6200a2430fSAndrzej Pietrasiewicz struct usb_ep *out_ep; 6300a2430fSAndrzej Pietrasiewicz }; 6400a2430fSAndrzej Pietrasiewicz 6500a2430fSAndrzej Pietrasiewicz static inline struct f_hidg *func_to_hidg(struct usb_function *f) 6600a2430fSAndrzej Pietrasiewicz { 6700a2430fSAndrzej Pietrasiewicz return container_of(f, struct f_hidg, func); 6800a2430fSAndrzej Pietrasiewicz } 6900a2430fSAndrzej Pietrasiewicz 7000a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 7100a2430fSAndrzej Pietrasiewicz /* Static descriptors */ 7200a2430fSAndrzej Pietrasiewicz 7300a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor hidg_interface_desc = { 7400a2430fSAndrzej Pietrasiewicz .bLength = sizeof hidg_interface_desc, 7500a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE, 7600a2430fSAndrzej Pietrasiewicz /* .bInterfaceNumber = DYNAMIC */ 7700a2430fSAndrzej Pietrasiewicz .bAlternateSetting = 0, 7800a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 2, 7900a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_HID, 8000a2430fSAndrzej Pietrasiewicz /* .bInterfaceSubClass = DYNAMIC */ 8100a2430fSAndrzej Pietrasiewicz /* .bInterfaceProtocol = DYNAMIC */ 8200a2430fSAndrzej Pietrasiewicz /* .iInterface = DYNAMIC */ 8300a2430fSAndrzej Pietrasiewicz }; 8400a2430fSAndrzej Pietrasiewicz 8500a2430fSAndrzej Pietrasiewicz static struct hid_descriptor hidg_desc = { 8600a2430fSAndrzej Pietrasiewicz .bLength = sizeof hidg_desc, 8700a2430fSAndrzej Pietrasiewicz .bDescriptorType = HID_DT_HID, 8800a2430fSAndrzej Pietrasiewicz .bcdHID = 0x0101, 8900a2430fSAndrzej Pietrasiewicz .bCountryCode = 0x00, 9000a2430fSAndrzej Pietrasiewicz .bNumDescriptors = 0x1, 9100a2430fSAndrzej Pietrasiewicz /*.desc[0].bDescriptorType = DYNAMIC */ 9200a2430fSAndrzej Pietrasiewicz /*.desc[0].wDescriptorLenght = DYNAMIC */ 9300a2430fSAndrzej Pietrasiewicz }; 9400a2430fSAndrzej Pietrasiewicz 9500a2430fSAndrzej Pietrasiewicz /* High-Speed Support */ 9600a2430fSAndrzej Pietrasiewicz 9700a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { 9800a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 9900a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 10000a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 10100a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 10200a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 10300a2430fSAndrzej Pietrasiewicz .bInterval = 4, /* FIXME: Add this field in the 10400a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 10500a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 10600a2430fSAndrzej Pietrasiewicz */ 10700a2430fSAndrzej Pietrasiewicz }; 10800a2430fSAndrzej Pietrasiewicz 10900a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { 11000a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 11100a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 11200a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT, 11300a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 11400a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 11500a2430fSAndrzej Pietrasiewicz .bInterval = 4, /* FIXME: Add this field in the 11600a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 11700a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 11800a2430fSAndrzej Pietrasiewicz */ 11900a2430fSAndrzej Pietrasiewicz }; 12000a2430fSAndrzej Pietrasiewicz 12100a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hidg_hs_descriptors[] = { 12200a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_interface_desc, 12300a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_desc, 12400a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, 12500a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_hs_out_ep_desc, 12600a2430fSAndrzej Pietrasiewicz NULL, 12700a2430fSAndrzej Pietrasiewicz }; 12800a2430fSAndrzej Pietrasiewicz 12900a2430fSAndrzej Pietrasiewicz /* Full-Speed Support */ 13000a2430fSAndrzej Pietrasiewicz 13100a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { 13200a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 13300a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 13400a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 13500a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 13600a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 13700a2430fSAndrzej Pietrasiewicz .bInterval = 10, /* FIXME: Add this field in the 13800a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 13900a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 14000a2430fSAndrzej Pietrasiewicz */ 14100a2430fSAndrzej Pietrasiewicz }; 14200a2430fSAndrzej Pietrasiewicz 14300a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { 14400a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 14500a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 14600a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT, 14700a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 14800a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 14900a2430fSAndrzej Pietrasiewicz .bInterval = 10, /* FIXME: Add this field in the 15000a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 15100a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 15200a2430fSAndrzej Pietrasiewicz */ 15300a2430fSAndrzej Pietrasiewicz }; 15400a2430fSAndrzej Pietrasiewicz 15500a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hidg_fs_descriptors[] = { 15600a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_interface_desc, 15700a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_desc, 15800a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, 15900a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_fs_out_ep_desc, 16000a2430fSAndrzej Pietrasiewicz NULL, 16100a2430fSAndrzej Pietrasiewicz }; 16200a2430fSAndrzej Pietrasiewicz 16300a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 16400a2430fSAndrzej Pietrasiewicz /* Char Device */ 16500a2430fSAndrzej Pietrasiewicz 16600a2430fSAndrzej Pietrasiewicz static ssize_t f_hidg_read(struct file *file, char __user *buffer, 16700a2430fSAndrzej Pietrasiewicz size_t count, loff_t *ptr) 16800a2430fSAndrzej Pietrasiewicz { 16900a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = file->private_data; 17000a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list *list; 17100a2430fSAndrzej Pietrasiewicz struct usb_request *req; 17200a2430fSAndrzej Pietrasiewicz unsigned long flags; 17300a2430fSAndrzej Pietrasiewicz int ret; 17400a2430fSAndrzej Pietrasiewicz 17500a2430fSAndrzej Pietrasiewicz if (!count) 17600a2430fSAndrzej Pietrasiewicz return 0; 17700a2430fSAndrzej Pietrasiewicz 17800a2430fSAndrzej Pietrasiewicz if (!access_ok(VERIFY_WRITE, buffer, count)) 17900a2430fSAndrzej Pietrasiewicz return -EFAULT; 18000a2430fSAndrzej Pietrasiewicz 18100a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 18200a2430fSAndrzej Pietrasiewicz 18300a2430fSAndrzej Pietrasiewicz #define READ_COND (!list_empty(&hidg->completed_out_req)) 18400a2430fSAndrzej Pietrasiewicz 18500a2430fSAndrzej Pietrasiewicz /* wait for at least one buffer to complete */ 18600a2430fSAndrzej Pietrasiewicz while (!READ_COND) { 18700a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 18800a2430fSAndrzej Pietrasiewicz if (file->f_flags & O_NONBLOCK) 18900a2430fSAndrzej Pietrasiewicz return -EAGAIN; 19000a2430fSAndrzej Pietrasiewicz 19100a2430fSAndrzej Pietrasiewicz if (wait_event_interruptible(hidg->read_queue, READ_COND)) 19200a2430fSAndrzej Pietrasiewicz return -ERESTARTSYS; 19300a2430fSAndrzej Pietrasiewicz 19400a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 19500a2430fSAndrzej Pietrasiewicz } 19600a2430fSAndrzej Pietrasiewicz 19700a2430fSAndrzej Pietrasiewicz /* pick the first one */ 19800a2430fSAndrzej Pietrasiewicz list = list_first_entry(&hidg->completed_out_req, 19900a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list, list); 20000a2430fSAndrzej Pietrasiewicz req = list->req; 20100a2430fSAndrzej Pietrasiewicz count = min_t(unsigned int, count, req->actual - list->pos); 20200a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 20300a2430fSAndrzej Pietrasiewicz 20400a2430fSAndrzej Pietrasiewicz /* copy to user outside spinlock */ 20500a2430fSAndrzej Pietrasiewicz count -= copy_to_user(buffer, req->buf + list->pos, count); 20600a2430fSAndrzej Pietrasiewicz list->pos += count; 20700a2430fSAndrzej Pietrasiewicz 20800a2430fSAndrzej Pietrasiewicz /* 20900a2430fSAndrzej Pietrasiewicz * if this request is completely handled and transfered to 21000a2430fSAndrzej Pietrasiewicz * userspace, remove its entry from the list and requeue it 21100a2430fSAndrzej Pietrasiewicz * again. Otherwise, we will revisit it again upon the next 21200a2430fSAndrzej Pietrasiewicz * call, taking into account its current read position. 21300a2430fSAndrzej Pietrasiewicz */ 21400a2430fSAndrzej Pietrasiewicz if (list->pos == req->actual) { 21500a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 21600a2430fSAndrzej Pietrasiewicz list_del(&list->list); 21700a2430fSAndrzej Pietrasiewicz kfree(list); 21800a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 21900a2430fSAndrzej Pietrasiewicz 22000a2430fSAndrzej Pietrasiewicz req->length = hidg->report_length; 22100a2430fSAndrzej Pietrasiewicz ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL); 22200a2430fSAndrzej Pietrasiewicz if (ret < 0) 22300a2430fSAndrzej Pietrasiewicz return ret; 22400a2430fSAndrzej Pietrasiewicz } 22500a2430fSAndrzej Pietrasiewicz 22600a2430fSAndrzej Pietrasiewicz return count; 22700a2430fSAndrzej Pietrasiewicz } 22800a2430fSAndrzej Pietrasiewicz 22900a2430fSAndrzej Pietrasiewicz static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req) 23000a2430fSAndrzej Pietrasiewicz { 23100a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; 23200a2430fSAndrzej Pietrasiewicz 23300a2430fSAndrzej Pietrasiewicz if (req->status != 0) { 23400a2430fSAndrzej Pietrasiewicz ERROR(hidg->func.config->cdev, 23500a2430fSAndrzej Pietrasiewicz "End Point Request ERROR: %d\n", req->status); 23600a2430fSAndrzej Pietrasiewicz } 23700a2430fSAndrzej Pietrasiewicz 23800a2430fSAndrzej Pietrasiewicz hidg->write_pending = 0; 23900a2430fSAndrzej Pietrasiewicz wake_up(&hidg->write_queue); 24000a2430fSAndrzej Pietrasiewicz } 24100a2430fSAndrzej Pietrasiewicz 24200a2430fSAndrzej Pietrasiewicz static ssize_t f_hidg_write(struct file *file, const char __user *buffer, 24300a2430fSAndrzej Pietrasiewicz size_t count, loff_t *offp) 24400a2430fSAndrzej Pietrasiewicz { 24500a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = file->private_data; 24600a2430fSAndrzej Pietrasiewicz ssize_t status = -ENOMEM; 24700a2430fSAndrzej Pietrasiewicz 24800a2430fSAndrzej Pietrasiewicz if (!access_ok(VERIFY_READ, buffer, count)) 24900a2430fSAndrzej Pietrasiewicz return -EFAULT; 25000a2430fSAndrzej Pietrasiewicz 25100a2430fSAndrzej Pietrasiewicz mutex_lock(&hidg->lock); 25200a2430fSAndrzej Pietrasiewicz 25300a2430fSAndrzej Pietrasiewicz #define WRITE_COND (!hidg->write_pending) 25400a2430fSAndrzej Pietrasiewicz 25500a2430fSAndrzej Pietrasiewicz /* write queue */ 25600a2430fSAndrzej Pietrasiewicz while (!WRITE_COND) { 25700a2430fSAndrzej Pietrasiewicz mutex_unlock(&hidg->lock); 25800a2430fSAndrzej Pietrasiewicz if (file->f_flags & O_NONBLOCK) 25900a2430fSAndrzej Pietrasiewicz return -EAGAIN; 26000a2430fSAndrzej Pietrasiewicz 26100a2430fSAndrzej Pietrasiewicz if (wait_event_interruptible_exclusive( 26200a2430fSAndrzej Pietrasiewicz hidg->write_queue, WRITE_COND)) 26300a2430fSAndrzej Pietrasiewicz return -ERESTARTSYS; 26400a2430fSAndrzej Pietrasiewicz 26500a2430fSAndrzej Pietrasiewicz mutex_lock(&hidg->lock); 26600a2430fSAndrzej Pietrasiewicz } 26700a2430fSAndrzej Pietrasiewicz 26800a2430fSAndrzej Pietrasiewicz count = min_t(unsigned, count, hidg->report_length); 26900a2430fSAndrzej Pietrasiewicz status = copy_from_user(hidg->req->buf, buffer, count); 27000a2430fSAndrzej Pietrasiewicz 27100a2430fSAndrzej Pietrasiewicz if (status != 0) { 27200a2430fSAndrzej Pietrasiewicz ERROR(hidg->func.config->cdev, 27300a2430fSAndrzej Pietrasiewicz "copy_from_user error\n"); 27400a2430fSAndrzej Pietrasiewicz mutex_unlock(&hidg->lock); 27500a2430fSAndrzej Pietrasiewicz return -EINVAL; 27600a2430fSAndrzej Pietrasiewicz } 27700a2430fSAndrzej Pietrasiewicz 27800a2430fSAndrzej Pietrasiewicz hidg->req->status = 0; 27900a2430fSAndrzej Pietrasiewicz hidg->req->zero = 0; 28000a2430fSAndrzej Pietrasiewicz hidg->req->length = count; 28100a2430fSAndrzej Pietrasiewicz hidg->req->complete = f_hidg_req_complete; 28200a2430fSAndrzej Pietrasiewicz hidg->req->context = hidg; 28300a2430fSAndrzej Pietrasiewicz hidg->write_pending = 1; 28400a2430fSAndrzej Pietrasiewicz 28500a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC); 28600a2430fSAndrzej Pietrasiewicz if (status < 0) { 28700a2430fSAndrzej Pietrasiewicz ERROR(hidg->func.config->cdev, 28800a2430fSAndrzej Pietrasiewicz "usb_ep_queue error on int endpoint %zd\n", status); 28900a2430fSAndrzej Pietrasiewicz hidg->write_pending = 0; 29000a2430fSAndrzej Pietrasiewicz wake_up(&hidg->write_queue); 29100a2430fSAndrzej Pietrasiewicz } else { 29200a2430fSAndrzej Pietrasiewicz status = count; 29300a2430fSAndrzej Pietrasiewicz } 29400a2430fSAndrzej Pietrasiewicz 29500a2430fSAndrzej Pietrasiewicz mutex_unlock(&hidg->lock); 29600a2430fSAndrzej Pietrasiewicz 29700a2430fSAndrzej Pietrasiewicz return status; 29800a2430fSAndrzej Pietrasiewicz } 29900a2430fSAndrzej Pietrasiewicz 30000a2430fSAndrzej Pietrasiewicz static unsigned int f_hidg_poll(struct file *file, poll_table *wait) 30100a2430fSAndrzej Pietrasiewicz { 30200a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = file->private_data; 30300a2430fSAndrzej Pietrasiewicz unsigned int ret = 0; 30400a2430fSAndrzej Pietrasiewicz 30500a2430fSAndrzej Pietrasiewicz poll_wait(file, &hidg->read_queue, wait); 30600a2430fSAndrzej Pietrasiewicz poll_wait(file, &hidg->write_queue, wait); 30700a2430fSAndrzej Pietrasiewicz 30800a2430fSAndrzej Pietrasiewicz if (WRITE_COND) 30900a2430fSAndrzej Pietrasiewicz ret |= POLLOUT | POLLWRNORM; 31000a2430fSAndrzej Pietrasiewicz 31100a2430fSAndrzej Pietrasiewicz if (READ_COND) 31200a2430fSAndrzej Pietrasiewicz ret |= POLLIN | POLLRDNORM; 31300a2430fSAndrzej Pietrasiewicz 31400a2430fSAndrzej Pietrasiewicz return ret; 31500a2430fSAndrzej Pietrasiewicz } 31600a2430fSAndrzej Pietrasiewicz 31700a2430fSAndrzej Pietrasiewicz #undef WRITE_COND 31800a2430fSAndrzej Pietrasiewicz #undef READ_COND 31900a2430fSAndrzej Pietrasiewicz 32000a2430fSAndrzej Pietrasiewicz static int f_hidg_release(struct inode *inode, struct file *fd) 32100a2430fSAndrzej Pietrasiewicz { 32200a2430fSAndrzej Pietrasiewicz fd->private_data = NULL; 32300a2430fSAndrzej Pietrasiewicz return 0; 32400a2430fSAndrzej Pietrasiewicz } 32500a2430fSAndrzej Pietrasiewicz 32600a2430fSAndrzej Pietrasiewicz static int f_hidg_open(struct inode *inode, struct file *fd) 32700a2430fSAndrzej Pietrasiewicz { 32800a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = 32900a2430fSAndrzej Pietrasiewicz container_of(inode->i_cdev, struct f_hidg, cdev); 33000a2430fSAndrzej Pietrasiewicz 33100a2430fSAndrzej Pietrasiewicz fd->private_data = hidg; 33200a2430fSAndrzej Pietrasiewicz 33300a2430fSAndrzej Pietrasiewicz return 0; 33400a2430fSAndrzej Pietrasiewicz } 33500a2430fSAndrzej Pietrasiewicz 33600a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 33700a2430fSAndrzej Pietrasiewicz /* usb_function */ 33800a2430fSAndrzej Pietrasiewicz 33900a2430fSAndrzej Pietrasiewicz static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, 34000a2430fSAndrzej Pietrasiewicz unsigned length) 34100a2430fSAndrzej Pietrasiewicz { 34200a2430fSAndrzej Pietrasiewicz return alloc_ep_req(ep, length, length); 34300a2430fSAndrzej Pietrasiewicz } 34400a2430fSAndrzej Pietrasiewicz 34500a2430fSAndrzej Pietrasiewicz static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) 34600a2430fSAndrzej Pietrasiewicz { 34700a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = (struct f_hidg *) req->context; 34800a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list *req_list; 34900a2430fSAndrzej Pietrasiewicz unsigned long flags; 35000a2430fSAndrzej Pietrasiewicz 35100a2430fSAndrzej Pietrasiewicz req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC); 35200a2430fSAndrzej Pietrasiewicz if (!req_list) 35300a2430fSAndrzej Pietrasiewicz return; 35400a2430fSAndrzej Pietrasiewicz 35500a2430fSAndrzej Pietrasiewicz req_list->req = req; 35600a2430fSAndrzej Pietrasiewicz 35700a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 35800a2430fSAndrzej Pietrasiewicz list_add_tail(&req_list->list, &hidg->completed_out_req); 35900a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 36000a2430fSAndrzej Pietrasiewicz 36100a2430fSAndrzej Pietrasiewicz wake_up(&hidg->read_queue); 36200a2430fSAndrzej Pietrasiewicz } 36300a2430fSAndrzej Pietrasiewicz 36400a2430fSAndrzej Pietrasiewicz static int hidg_setup(struct usb_function *f, 36500a2430fSAndrzej Pietrasiewicz const struct usb_ctrlrequest *ctrl) 36600a2430fSAndrzej Pietrasiewicz { 36700a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 36800a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev; 36900a2430fSAndrzej Pietrasiewicz struct usb_request *req = cdev->req; 37000a2430fSAndrzej Pietrasiewicz int status = 0; 37100a2430fSAndrzej Pietrasiewicz __u16 value, length; 37200a2430fSAndrzej Pietrasiewicz 37300a2430fSAndrzej Pietrasiewicz value = __le16_to_cpu(ctrl->wValue); 37400a2430fSAndrzej Pietrasiewicz length = __le16_to_cpu(ctrl->wLength); 37500a2430fSAndrzej Pietrasiewicz 37600a2430fSAndrzej Pietrasiewicz VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x " 37700a2430fSAndrzej Pietrasiewicz "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value); 37800a2430fSAndrzej Pietrasiewicz 37900a2430fSAndrzej Pietrasiewicz switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { 38000a2430fSAndrzej Pietrasiewicz case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 38100a2430fSAndrzej Pietrasiewicz | HID_REQ_GET_REPORT): 38200a2430fSAndrzej Pietrasiewicz VDBG(cdev, "get_report\n"); 38300a2430fSAndrzej Pietrasiewicz 38400a2430fSAndrzej Pietrasiewicz /* send an empty report */ 38500a2430fSAndrzej Pietrasiewicz length = min_t(unsigned, length, hidg->report_length); 38600a2430fSAndrzej Pietrasiewicz memset(req->buf, 0x0, length); 38700a2430fSAndrzej Pietrasiewicz 38800a2430fSAndrzej Pietrasiewicz goto respond; 38900a2430fSAndrzej Pietrasiewicz break; 39000a2430fSAndrzej Pietrasiewicz 39100a2430fSAndrzej Pietrasiewicz case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 39200a2430fSAndrzej Pietrasiewicz | HID_REQ_GET_PROTOCOL): 39300a2430fSAndrzej Pietrasiewicz VDBG(cdev, "get_protocol\n"); 39400a2430fSAndrzej Pietrasiewicz goto stall; 39500a2430fSAndrzej Pietrasiewicz break; 39600a2430fSAndrzej Pietrasiewicz 39700a2430fSAndrzej Pietrasiewicz case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 39800a2430fSAndrzej Pietrasiewicz | HID_REQ_SET_REPORT): 39900a2430fSAndrzej Pietrasiewicz VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength); 40000a2430fSAndrzej Pietrasiewicz goto stall; 40100a2430fSAndrzej Pietrasiewicz break; 40200a2430fSAndrzej Pietrasiewicz 40300a2430fSAndrzej Pietrasiewicz case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 40400a2430fSAndrzej Pietrasiewicz | HID_REQ_SET_PROTOCOL): 40500a2430fSAndrzej Pietrasiewicz VDBG(cdev, "set_protocol\n"); 40600a2430fSAndrzej Pietrasiewicz goto stall; 40700a2430fSAndrzej Pietrasiewicz break; 40800a2430fSAndrzej Pietrasiewicz 40900a2430fSAndrzej Pietrasiewicz case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8 41000a2430fSAndrzej Pietrasiewicz | USB_REQ_GET_DESCRIPTOR): 41100a2430fSAndrzej Pietrasiewicz switch (value >> 8) { 41200a2430fSAndrzej Pietrasiewicz case HID_DT_HID: 41300a2430fSAndrzej Pietrasiewicz VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n"); 41400a2430fSAndrzej Pietrasiewicz length = min_t(unsigned short, length, 41500a2430fSAndrzej Pietrasiewicz hidg_desc.bLength); 41600a2430fSAndrzej Pietrasiewicz memcpy(req->buf, &hidg_desc, length); 41700a2430fSAndrzej Pietrasiewicz goto respond; 41800a2430fSAndrzej Pietrasiewicz break; 41900a2430fSAndrzej Pietrasiewicz case HID_DT_REPORT: 42000a2430fSAndrzej Pietrasiewicz VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n"); 42100a2430fSAndrzej Pietrasiewicz length = min_t(unsigned short, length, 42200a2430fSAndrzej Pietrasiewicz hidg->report_desc_length); 42300a2430fSAndrzej Pietrasiewicz memcpy(req->buf, hidg->report_desc, length); 42400a2430fSAndrzej Pietrasiewicz goto respond; 42500a2430fSAndrzej Pietrasiewicz break; 42600a2430fSAndrzej Pietrasiewicz 42700a2430fSAndrzej Pietrasiewicz default: 42800a2430fSAndrzej Pietrasiewicz VDBG(cdev, "Unknown descriptor request 0x%x\n", 42900a2430fSAndrzej Pietrasiewicz value >> 8); 43000a2430fSAndrzej Pietrasiewicz goto stall; 43100a2430fSAndrzej Pietrasiewicz break; 43200a2430fSAndrzej Pietrasiewicz } 43300a2430fSAndrzej Pietrasiewicz break; 43400a2430fSAndrzej Pietrasiewicz 43500a2430fSAndrzej Pietrasiewicz default: 43600a2430fSAndrzej Pietrasiewicz VDBG(cdev, "Unknown request 0x%x\n", 43700a2430fSAndrzej Pietrasiewicz ctrl->bRequest); 43800a2430fSAndrzej Pietrasiewicz goto stall; 43900a2430fSAndrzej Pietrasiewicz break; 44000a2430fSAndrzej Pietrasiewicz } 44100a2430fSAndrzej Pietrasiewicz 44200a2430fSAndrzej Pietrasiewicz stall: 44300a2430fSAndrzej Pietrasiewicz return -EOPNOTSUPP; 44400a2430fSAndrzej Pietrasiewicz 44500a2430fSAndrzej Pietrasiewicz respond: 44600a2430fSAndrzej Pietrasiewicz req->zero = 0; 44700a2430fSAndrzej Pietrasiewicz req->length = length; 44800a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); 44900a2430fSAndrzej Pietrasiewicz if (status < 0) 45000a2430fSAndrzej Pietrasiewicz ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value); 45100a2430fSAndrzej Pietrasiewicz return status; 45200a2430fSAndrzej Pietrasiewicz } 45300a2430fSAndrzej Pietrasiewicz 45400a2430fSAndrzej Pietrasiewicz static void hidg_disable(struct usb_function *f) 45500a2430fSAndrzej Pietrasiewicz { 45600a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 45700a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list *list, *next; 45800a2430fSAndrzej Pietrasiewicz 45900a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->in_ep); 46000a2430fSAndrzej Pietrasiewicz hidg->in_ep->driver_data = NULL; 46100a2430fSAndrzej Pietrasiewicz 46200a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->out_ep); 46300a2430fSAndrzej Pietrasiewicz hidg->out_ep->driver_data = NULL; 46400a2430fSAndrzej Pietrasiewicz 46500a2430fSAndrzej Pietrasiewicz list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { 46600a2430fSAndrzej Pietrasiewicz list_del(&list->list); 46700a2430fSAndrzej Pietrasiewicz kfree(list); 46800a2430fSAndrzej Pietrasiewicz } 46900a2430fSAndrzej Pietrasiewicz } 47000a2430fSAndrzej Pietrasiewicz 47100a2430fSAndrzej Pietrasiewicz static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 47200a2430fSAndrzej Pietrasiewicz { 47300a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev; 47400a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 47500a2430fSAndrzej Pietrasiewicz int i, status = 0; 47600a2430fSAndrzej Pietrasiewicz 47700a2430fSAndrzej Pietrasiewicz VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); 47800a2430fSAndrzej Pietrasiewicz 47900a2430fSAndrzej Pietrasiewicz if (hidg->in_ep != NULL) { 48000a2430fSAndrzej Pietrasiewicz /* restart endpoint */ 48100a2430fSAndrzej Pietrasiewicz if (hidg->in_ep->driver_data != NULL) 48200a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->in_ep); 48300a2430fSAndrzej Pietrasiewicz 48400a2430fSAndrzej Pietrasiewicz status = config_ep_by_speed(f->config->cdev->gadget, f, 48500a2430fSAndrzej Pietrasiewicz hidg->in_ep); 48600a2430fSAndrzej Pietrasiewicz if (status) { 48700a2430fSAndrzej Pietrasiewicz ERROR(cdev, "config_ep_by_speed FAILED!\n"); 48800a2430fSAndrzej Pietrasiewicz goto fail; 48900a2430fSAndrzej Pietrasiewicz } 49000a2430fSAndrzej Pietrasiewicz status = usb_ep_enable(hidg->in_ep); 49100a2430fSAndrzej Pietrasiewicz if (status < 0) { 49200a2430fSAndrzej Pietrasiewicz ERROR(cdev, "Enable IN endpoint FAILED!\n"); 49300a2430fSAndrzej Pietrasiewicz goto fail; 49400a2430fSAndrzej Pietrasiewicz } 49500a2430fSAndrzej Pietrasiewicz hidg->in_ep->driver_data = hidg; 49600a2430fSAndrzej Pietrasiewicz } 49700a2430fSAndrzej Pietrasiewicz 49800a2430fSAndrzej Pietrasiewicz 49900a2430fSAndrzej Pietrasiewicz if (hidg->out_ep != NULL) { 50000a2430fSAndrzej Pietrasiewicz /* restart endpoint */ 50100a2430fSAndrzej Pietrasiewicz if (hidg->out_ep->driver_data != NULL) 50200a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->out_ep); 50300a2430fSAndrzej Pietrasiewicz 50400a2430fSAndrzej Pietrasiewicz status = config_ep_by_speed(f->config->cdev->gadget, f, 50500a2430fSAndrzej Pietrasiewicz hidg->out_ep); 50600a2430fSAndrzej Pietrasiewicz if (status) { 50700a2430fSAndrzej Pietrasiewicz ERROR(cdev, "config_ep_by_speed FAILED!\n"); 50800a2430fSAndrzej Pietrasiewicz goto fail; 50900a2430fSAndrzej Pietrasiewicz } 51000a2430fSAndrzej Pietrasiewicz status = usb_ep_enable(hidg->out_ep); 51100a2430fSAndrzej Pietrasiewicz if (status < 0) { 51200a2430fSAndrzej Pietrasiewicz ERROR(cdev, "Enable IN endpoint FAILED!\n"); 51300a2430fSAndrzej Pietrasiewicz goto fail; 51400a2430fSAndrzej Pietrasiewicz } 51500a2430fSAndrzej Pietrasiewicz hidg->out_ep->driver_data = hidg; 51600a2430fSAndrzej Pietrasiewicz 51700a2430fSAndrzej Pietrasiewicz /* 51800a2430fSAndrzej Pietrasiewicz * allocate a bunch of read buffers and queue them all at once. 51900a2430fSAndrzej Pietrasiewicz */ 52000a2430fSAndrzej Pietrasiewicz for (i = 0; i < hidg->qlen && status == 0; i++) { 52100a2430fSAndrzej Pietrasiewicz struct usb_request *req = 52200a2430fSAndrzej Pietrasiewicz hidg_alloc_ep_req(hidg->out_ep, 52300a2430fSAndrzej Pietrasiewicz hidg->report_length); 52400a2430fSAndrzej Pietrasiewicz if (req) { 52500a2430fSAndrzej Pietrasiewicz req->complete = hidg_set_report_complete; 52600a2430fSAndrzej Pietrasiewicz req->context = hidg; 52700a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(hidg->out_ep, req, 52800a2430fSAndrzej Pietrasiewicz GFP_ATOMIC); 52900a2430fSAndrzej Pietrasiewicz if (status) 53000a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s queue req --> %d\n", 53100a2430fSAndrzej Pietrasiewicz hidg->out_ep->name, status); 53200a2430fSAndrzej Pietrasiewicz } else { 53300a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->out_ep); 53400a2430fSAndrzej Pietrasiewicz hidg->out_ep->driver_data = NULL; 53500a2430fSAndrzej Pietrasiewicz status = -ENOMEM; 53600a2430fSAndrzej Pietrasiewicz goto fail; 53700a2430fSAndrzej Pietrasiewicz } 53800a2430fSAndrzej Pietrasiewicz } 53900a2430fSAndrzej Pietrasiewicz } 54000a2430fSAndrzej Pietrasiewicz 54100a2430fSAndrzej Pietrasiewicz fail: 54200a2430fSAndrzej Pietrasiewicz return status; 54300a2430fSAndrzej Pietrasiewicz } 54400a2430fSAndrzej Pietrasiewicz 54500a2430fSAndrzej Pietrasiewicz const struct file_operations f_hidg_fops = { 54600a2430fSAndrzej Pietrasiewicz .owner = THIS_MODULE, 54700a2430fSAndrzej Pietrasiewicz .open = f_hidg_open, 54800a2430fSAndrzej Pietrasiewicz .release = f_hidg_release, 54900a2430fSAndrzej Pietrasiewicz .write = f_hidg_write, 55000a2430fSAndrzej Pietrasiewicz .read = f_hidg_read, 55100a2430fSAndrzej Pietrasiewicz .poll = f_hidg_poll, 55200a2430fSAndrzej Pietrasiewicz .llseek = noop_llseek, 55300a2430fSAndrzej Pietrasiewicz }; 55400a2430fSAndrzej Pietrasiewicz 55500a2430fSAndrzej Pietrasiewicz static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) 55600a2430fSAndrzej Pietrasiewicz { 55700a2430fSAndrzej Pietrasiewicz struct usb_ep *ep; 55800a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 559*63406087SAndrzej Pietrasiewicz struct device *device; 56000a2430fSAndrzej Pietrasiewicz int status; 56100a2430fSAndrzej Pietrasiewicz dev_t dev; 56200a2430fSAndrzej Pietrasiewicz 56300a2430fSAndrzej Pietrasiewicz /* allocate instance-specific interface IDs, and patch descriptors */ 56400a2430fSAndrzej Pietrasiewicz status = usb_interface_id(c, f); 56500a2430fSAndrzej Pietrasiewicz if (status < 0) 56600a2430fSAndrzej Pietrasiewicz goto fail; 56700a2430fSAndrzej Pietrasiewicz hidg_interface_desc.bInterfaceNumber = status; 56800a2430fSAndrzej Pietrasiewicz 56900a2430fSAndrzej Pietrasiewicz /* allocate instance-specific endpoints */ 57000a2430fSAndrzej Pietrasiewicz status = -ENODEV; 57100a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc); 57200a2430fSAndrzej Pietrasiewicz if (!ep) 57300a2430fSAndrzej Pietrasiewicz goto fail; 57400a2430fSAndrzej Pietrasiewicz ep->driver_data = c->cdev; /* claim */ 57500a2430fSAndrzej Pietrasiewicz hidg->in_ep = ep; 57600a2430fSAndrzej Pietrasiewicz 57700a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); 57800a2430fSAndrzej Pietrasiewicz if (!ep) 57900a2430fSAndrzej Pietrasiewicz goto fail; 58000a2430fSAndrzej Pietrasiewicz ep->driver_data = c->cdev; /* claim */ 58100a2430fSAndrzej Pietrasiewicz hidg->out_ep = ep; 58200a2430fSAndrzej Pietrasiewicz 58300a2430fSAndrzej Pietrasiewicz /* preallocate request and buffer */ 58400a2430fSAndrzej Pietrasiewicz status = -ENOMEM; 58500a2430fSAndrzej Pietrasiewicz hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); 58600a2430fSAndrzej Pietrasiewicz if (!hidg->req) 58700a2430fSAndrzej Pietrasiewicz goto fail; 58800a2430fSAndrzej Pietrasiewicz 58900a2430fSAndrzej Pietrasiewicz hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL); 59000a2430fSAndrzej Pietrasiewicz if (!hidg->req->buf) 59100a2430fSAndrzej Pietrasiewicz goto fail; 59200a2430fSAndrzej Pietrasiewicz 59300a2430fSAndrzej Pietrasiewicz /* set descriptor dynamic values */ 59400a2430fSAndrzej Pietrasiewicz hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; 59500a2430fSAndrzej Pietrasiewicz hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; 59600a2430fSAndrzej Pietrasiewicz hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 59700a2430fSAndrzej Pietrasiewicz hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 59800a2430fSAndrzej Pietrasiewicz hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 59900a2430fSAndrzej Pietrasiewicz hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 60000a2430fSAndrzej Pietrasiewicz hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT; 60100a2430fSAndrzej Pietrasiewicz hidg_desc.desc[0].wDescriptorLength = 60200a2430fSAndrzej Pietrasiewicz cpu_to_le16(hidg->report_desc_length); 60300a2430fSAndrzej Pietrasiewicz 60400a2430fSAndrzej Pietrasiewicz hidg_hs_in_ep_desc.bEndpointAddress = 60500a2430fSAndrzej Pietrasiewicz hidg_fs_in_ep_desc.bEndpointAddress; 60600a2430fSAndrzej Pietrasiewicz hidg_hs_out_ep_desc.bEndpointAddress = 60700a2430fSAndrzej Pietrasiewicz hidg_fs_out_ep_desc.bEndpointAddress; 60800a2430fSAndrzej Pietrasiewicz 60900a2430fSAndrzej Pietrasiewicz status = usb_assign_descriptors(f, hidg_fs_descriptors, 61000a2430fSAndrzej Pietrasiewicz hidg_hs_descriptors, NULL); 61100a2430fSAndrzej Pietrasiewicz if (status) 61200a2430fSAndrzej Pietrasiewicz goto fail; 61300a2430fSAndrzej Pietrasiewicz 61400a2430fSAndrzej Pietrasiewicz mutex_init(&hidg->lock); 61500a2430fSAndrzej Pietrasiewicz spin_lock_init(&hidg->spinlock); 61600a2430fSAndrzej Pietrasiewicz init_waitqueue_head(&hidg->write_queue); 61700a2430fSAndrzej Pietrasiewicz init_waitqueue_head(&hidg->read_queue); 61800a2430fSAndrzej Pietrasiewicz INIT_LIST_HEAD(&hidg->completed_out_req); 61900a2430fSAndrzej Pietrasiewicz 62000a2430fSAndrzej Pietrasiewicz /* create char device */ 62100a2430fSAndrzej Pietrasiewicz cdev_init(&hidg->cdev, &f_hidg_fops); 62200a2430fSAndrzej Pietrasiewicz dev = MKDEV(major, hidg->minor); 62300a2430fSAndrzej Pietrasiewicz status = cdev_add(&hidg->cdev, dev, 1); 62400a2430fSAndrzej Pietrasiewicz if (status) 625d12a8727SPavitrakumar Managutte goto fail_free_descs; 62600a2430fSAndrzej Pietrasiewicz 627*63406087SAndrzej Pietrasiewicz device = device_create(hidg_class, NULL, dev, NULL, 628*63406087SAndrzej Pietrasiewicz "%s%d", "hidg", hidg->minor); 629*63406087SAndrzej Pietrasiewicz if (IS_ERR(device)) { 630*63406087SAndrzej Pietrasiewicz status = PTR_ERR(device); 631*63406087SAndrzej Pietrasiewicz goto del; 632*63406087SAndrzej Pietrasiewicz } 63300a2430fSAndrzej Pietrasiewicz 63400a2430fSAndrzej Pietrasiewicz return 0; 635*63406087SAndrzej Pietrasiewicz del: 636*63406087SAndrzej Pietrasiewicz cdev_del(&hidg->cdev); 637d12a8727SPavitrakumar Managutte fail_free_descs: 638d12a8727SPavitrakumar Managutte usb_free_all_descriptors(f); 63900a2430fSAndrzej Pietrasiewicz fail: 64000a2430fSAndrzej Pietrasiewicz ERROR(f->config->cdev, "hidg_bind FAILED\n"); 64100a2430fSAndrzej Pietrasiewicz if (hidg->req != NULL) { 64200a2430fSAndrzej Pietrasiewicz kfree(hidg->req->buf); 64300a2430fSAndrzej Pietrasiewicz if (hidg->in_ep != NULL) 64400a2430fSAndrzej Pietrasiewicz usb_ep_free_request(hidg->in_ep, hidg->req); 64500a2430fSAndrzej Pietrasiewicz } 64600a2430fSAndrzej Pietrasiewicz 64700a2430fSAndrzej Pietrasiewicz return status; 64800a2430fSAndrzej Pietrasiewicz } 64900a2430fSAndrzej Pietrasiewicz 65000a2430fSAndrzej Pietrasiewicz static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) 65100a2430fSAndrzej Pietrasiewicz { 65200a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 65300a2430fSAndrzej Pietrasiewicz 65400a2430fSAndrzej Pietrasiewicz device_destroy(hidg_class, MKDEV(major, hidg->minor)); 65500a2430fSAndrzej Pietrasiewicz cdev_del(&hidg->cdev); 65600a2430fSAndrzej Pietrasiewicz 65700a2430fSAndrzej Pietrasiewicz /* disable/free request and end point */ 65800a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->in_ep); 65900a2430fSAndrzej Pietrasiewicz usb_ep_dequeue(hidg->in_ep, hidg->req); 66000a2430fSAndrzej Pietrasiewicz kfree(hidg->req->buf); 66100a2430fSAndrzej Pietrasiewicz usb_ep_free_request(hidg->in_ep, hidg->req); 66200a2430fSAndrzej Pietrasiewicz 66300a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f); 66400a2430fSAndrzej Pietrasiewicz 66500a2430fSAndrzej Pietrasiewicz kfree(hidg->report_desc); 66600a2430fSAndrzej Pietrasiewicz kfree(hidg); 66700a2430fSAndrzej Pietrasiewicz } 66800a2430fSAndrzej Pietrasiewicz 66900a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 67000a2430fSAndrzej Pietrasiewicz /* Strings */ 67100a2430fSAndrzej Pietrasiewicz 67200a2430fSAndrzej Pietrasiewicz #define CT_FUNC_HID_IDX 0 67300a2430fSAndrzej Pietrasiewicz 67400a2430fSAndrzej Pietrasiewicz static struct usb_string ct_func_string_defs[] = { 67500a2430fSAndrzej Pietrasiewicz [CT_FUNC_HID_IDX].s = "HID Interface", 67600a2430fSAndrzej Pietrasiewicz {}, /* end of list */ 67700a2430fSAndrzej Pietrasiewicz }; 67800a2430fSAndrzej Pietrasiewicz 67900a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings ct_func_string_table = { 68000a2430fSAndrzej Pietrasiewicz .language = 0x0409, /* en-US */ 68100a2430fSAndrzej Pietrasiewicz .strings = ct_func_string_defs, 68200a2430fSAndrzej Pietrasiewicz }; 68300a2430fSAndrzej Pietrasiewicz 68400a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *ct_func_strings[] = { 68500a2430fSAndrzej Pietrasiewicz &ct_func_string_table, 68600a2430fSAndrzej Pietrasiewicz NULL, 68700a2430fSAndrzej Pietrasiewicz }; 68800a2430fSAndrzej Pietrasiewicz 68900a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 69000a2430fSAndrzej Pietrasiewicz /* usb_configuration */ 69100a2430fSAndrzej Pietrasiewicz 69200a2430fSAndrzej Pietrasiewicz int __init hidg_bind_config(struct usb_configuration *c, 69300a2430fSAndrzej Pietrasiewicz struct hidg_func_descriptor *fdesc, int index) 69400a2430fSAndrzej Pietrasiewicz { 69500a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg; 69600a2430fSAndrzej Pietrasiewicz int status; 69700a2430fSAndrzej Pietrasiewicz 69800a2430fSAndrzej Pietrasiewicz if (index >= minors) 69900a2430fSAndrzej Pietrasiewicz return -ENOENT; 70000a2430fSAndrzej Pietrasiewicz 70100a2430fSAndrzej Pietrasiewicz /* maybe allocate device-global string IDs, and patch descriptors */ 70200a2430fSAndrzej Pietrasiewicz if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) { 70300a2430fSAndrzej Pietrasiewicz status = usb_string_id(c->cdev); 70400a2430fSAndrzej Pietrasiewicz if (status < 0) 70500a2430fSAndrzej Pietrasiewicz return status; 70600a2430fSAndrzej Pietrasiewicz ct_func_string_defs[CT_FUNC_HID_IDX].id = status; 70700a2430fSAndrzej Pietrasiewicz hidg_interface_desc.iInterface = status; 70800a2430fSAndrzej Pietrasiewicz } 70900a2430fSAndrzej Pietrasiewicz 71000a2430fSAndrzej Pietrasiewicz /* allocate and initialize one new instance */ 71100a2430fSAndrzej Pietrasiewicz hidg = kzalloc(sizeof *hidg, GFP_KERNEL); 71200a2430fSAndrzej Pietrasiewicz if (!hidg) 71300a2430fSAndrzej Pietrasiewicz return -ENOMEM; 71400a2430fSAndrzej Pietrasiewicz 71500a2430fSAndrzej Pietrasiewicz hidg->minor = index; 71600a2430fSAndrzej Pietrasiewicz hidg->bInterfaceSubClass = fdesc->subclass; 71700a2430fSAndrzej Pietrasiewicz hidg->bInterfaceProtocol = fdesc->protocol; 71800a2430fSAndrzej Pietrasiewicz hidg->report_length = fdesc->report_length; 71900a2430fSAndrzej Pietrasiewicz hidg->report_desc_length = fdesc->report_desc_length; 72000a2430fSAndrzej Pietrasiewicz hidg->report_desc = kmemdup(fdesc->report_desc, 72100a2430fSAndrzej Pietrasiewicz fdesc->report_desc_length, 72200a2430fSAndrzej Pietrasiewicz GFP_KERNEL); 72300a2430fSAndrzej Pietrasiewicz if (!hidg->report_desc) { 72400a2430fSAndrzej Pietrasiewicz kfree(hidg); 72500a2430fSAndrzej Pietrasiewicz return -ENOMEM; 72600a2430fSAndrzej Pietrasiewicz } 72700a2430fSAndrzej Pietrasiewicz 72800a2430fSAndrzej Pietrasiewicz hidg->func.name = "hid"; 72900a2430fSAndrzej Pietrasiewicz hidg->func.strings = ct_func_strings; 73000a2430fSAndrzej Pietrasiewicz hidg->func.bind = hidg_bind; 73100a2430fSAndrzej Pietrasiewicz hidg->func.unbind = hidg_unbind; 73200a2430fSAndrzej Pietrasiewicz hidg->func.set_alt = hidg_set_alt; 73300a2430fSAndrzej Pietrasiewicz hidg->func.disable = hidg_disable; 73400a2430fSAndrzej Pietrasiewicz hidg->func.setup = hidg_setup; 73500a2430fSAndrzej Pietrasiewicz 73600a2430fSAndrzej Pietrasiewicz /* this could me made configurable at some point */ 73700a2430fSAndrzej Pietrasiewicz hidg->qlen = 4; 73800a2430fSAndrzej Pietrasiewicz 73900a2430fSAndrzej Pietrasiewicz status = usb_add_function(c, &hidg->func); 74000a2430fSAndrzej Pietrasiewicz if (status) 74100a2430fSAndrzej Pietrasiewicz kfree(hidg); 74200a2430fSAndrzej Pietrasiewicz 74300a2430fSAndrzej Pietrasiewicz return status; 74400a2430fSAndrzej Pietrasiewicz } 74500a2430fSAndrzej Pietrasiewicz 74600a2430fSAndrzej Pietrasiewicz int __init ghid_setup(struct usb_gadget *g, int count) 74700a2430fSAndrzej Pietrasiewicz { 74800a2430fSAndrzej Pietrasiewicz int status; 74900a2430fSAndrzej Pietrasiewicz dev_t dev; 75000a2430fSAndrzej Pietrasiewicz 75100a2430fSAndrzej Pietrasiewicz hidg_class = class_create(THIS_MODULE, "hidg"); 75206529407SAndrzej Pietrasiewicz if (IS_ERR(hidg_class)) { 75306529407SAndrzej Pietrasiewicz hidg_class = NULL; 75406529407SAndrzej Pietrasiewicz return PTR_ERR(hidg_class); 75506529407SAndrzej Pietrasiewicz } 75600a2430fSAndrzej Pietrasiewicz 75700a2430fSAndrzej Pietrasiewicz status = alloc_chrdev_region(&dev, 0, count, "hidg"); 75800a2430fSAndrzej Pietrasiewicz if (!status) { 75900a2430fSAndrzej Pietrasiewicz major = MAJOR(dev); 76000a2430fSAndrzej Pietrasiewicz minors = count; 76100a2430fSAndrzej Pietrasiewicz } 76200a2430fSAndrzej Pietrasiewicz 76300a2430fSAndrzej Pietrasiewicz return status; 76400a2430fSAndrzej Pietrasiewicz } 76500a2430fSAndrzej Pietrasiewicz 76600a2430fSAndrzej Pietrasiewicz void ghid_cleanup(void) 76700a2430fSAndrzej Pietrasiewicz { 76800a2430fSAndrzej Pietrasiewicz if (major) { 76900a2430fSAndrzej Pietrasiewicz unregister_chrdev_region(MKDEV(major, 0), minors); 77000a2430fSAndrzej Pietrasiewicz major = minors = 0; 77100a2430fSAndrzej Pietrasiewicz } 77200a2430fSAndrzej Pietrasiewicz 77300a2430fSAndrzej Pietrasiewicz class_destroy(hidg_class); 77400a2430fSAndrzej Pietrasiewicz hidg_class = NULL; 77500a2430fSAndrzej Pietrasiewicz } 776