1*00a2430fSAndrzej Pietrasiewicz /* 2*00a2430fSAndrzej Pietrasiewicz * f_hid.c -- USB HID function driver 3*00a2430fSAndrzej Pietrasiewicz * 4*00a2430fSAndrzej Pietrasiewicz * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com> 5*00a2430fSAndrzej Pietrasiewicz * 6*00a2430fSAndrzej Pietrasiewicz * This program is free software; you can redistribute it and/or modify 7*00a2430fSAndrzej Pietrasiewicz * it under the terms of the GNU General Public License as published by 8*00a2430fSAndrzej Pietrasiewicz * the Free Software Foundation; either version 2 of the License, or 9*00a2430fSAndrzej Pietrasiewicz * (at your option) any later version. 10*00a2430fSAndrzej Pietrasiewicz */ 11*00a2430fSAndrzej Pietrasiewicz 12*00a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h> 13*00a2430fSAndrzej Pietrasiewicz #include <linux/module.h> 14*00a2430fSAndrzej Pietrasiewicz #include <linux/hid.h> 15*00a2430fSAndrzej Pietrasiewicz #include <linux/cdev.h> 16*00a2430fSAndrzej Pietrasiewicz #include <linux/mutex.h> 17*00a2430fSAndrzej Pietrasiewicz #include <linux/poll.h> 18*00a2430fSAndrzej Pietrasiewicz #include <linux/uaccess.h> 19*00a2430fSAndrzej Pietrasiewicz #include <linux/wait.h> 20*00a2430fSAndrzej Pietrasiewicz #include <linux/sched.h> 21*00a2430fSAndrzej Pietrasiewicz #include <linux/usb/g_hid.h> 22*00a2430fSAndrzej Pietrasiewicz 23*00a2430fSAndrzej Pietrasiewicz #include "u_f.h" 24*00a2430fSAndrzej Pietrasiewicz 25*00a2430fSAndrzej Pietrasiewicz static int major, minors; 26*00a2430fSAndrzej Pietrasiewicz static struct class *hidg_class; 27*00a2430fSAndrzej Pietrasiewicz 28*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 29*00a2430fSAndrzej Pietrasiewicz /* HID gadget struct */ 30*00a2430fSAndrzej Pietrasiewicz 31*00a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list { 32*00a2430fSAndrzej Pietrasiewicz struct usb_request *req; 33*00a2430fSAndrzej Pietrasiewicz unsigned int pos; 34*00a2430fSAndrzej Pietrasiewicz struct list_head list; 35*00a2430fSAndrzej Pietrasiewicz }; 36*00a2430fSAndrzej Pietrasiewicz 37*00a2430fSAndrzej Pietrasiewicz struct f_hidg { 38*00a2430fSAndrzej Pietrasiewicz /* configuration */ 39*00a2430fSAndrzej Pietrasiewicz unsigned char bInterfaceSubClass; 40*00a2430fSAndrzej Pietrasiewicz unsigned char bInterfaceProtocol; 41*00a2430fSAndrzej Pietrasiewicz unsigned short report_desc_length; 42*00a2430fSAndrzej Pietrasiewicz char *report_desc; 43*00a2430fSAndrzej Pietrasiewicz unsigned short report_length; 44*00a2430fSAndrzej Pietrasiewicz 45*00a2430fSAndrzej Pietrasiewicz /* recv report */ 46*00a2430fSAndrzej Pietrasiewicz struct list_head completed_out_req; 47*00a2430fSAndrzej Pietrasiewicz spinlock_t spinlock; 48*00a2430fSAndrzej Pietrasiewicz wait_queue_head_t read_queue; 49*00a2430fSAndrzej Pietrasiewicz unsigned int qlen; 50*00a2430fSAndrzej Pietrasiewicz 51*00a2430fSAndrzej Pietrasiewicz /* send report */ 52*00a2430fSAndrzej Pietrasiewicz struct mutex lock; 53*00a2430fSAndrzej Pietrasiewicz bool write_pending; 54*00a2430fSAndrzej Pietrasiewicz wait_queue_head_t write_queue; 55*00a2430fSAndrzej Pietrasiewicz struct usb_request *req; 56*00a2430fSAndrzej Pietrasiewicz 57*00a2430fSAndrzej Pietrasiewicz int minor; 58*00a2430fSAndrzej Pietrasiewicz struct cdev cdev; 59*00a2430fSAndrzej Pietrasiewicz struct usb_function func; 60*00a2430fSAndrzej Pietrasiewicz 61*00a2430fSAndrzej Pietrasiewicz struct usb_ep *in_ep; 62*00a2430fSAndrzej Pietrasiewicz struct usb_ep *out_ep; 63*00a2430fSAndrzej Pietrasiewicz }; 64*00a2430fSAndrzej Pietrasiewicz 65*00a2430fSAndrzej Pietrasiewicz static inline struct f_hidg *func_to_hidg(struct usb_function *f) 66*00a2430fSAndrzej Pietrasiewicz { 67*00a2430fSAndrzej Pietrasiewicz return container_of(f, struct f_hidg, func); 68*00a2430fSAndrzej Pietrasiewicz } 69*00a2430fSAndrzej Pietrasiewicz 70*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 71*00a2430fSAndrzej Pietrasiewicz /* Static descriptors */ 72*00a2430fSAndrzej Pietrasiewicz 73*00a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor hidg_interface_desc = { 74*00a2430fSAndrzej Pietrasiewicz .bLength = sizeof hidg_interface_desc, 75*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE, 76*00a2430fSAndrzej Pietrasiewicz /* .bInterfaceNumber = DYNAMIC */ 77*00a2430fSAndrzej Pietrasiewicz .bAlternateSetting = 0, 78*00a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 2, 79*00a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_HID, 80*00a2430fSAndrzej Pietrasiewicz /* .bInterfaceSubClass = DYNAMIC */ 81*00a2430fSAndrzej Pietrasiewicz /* .bInterfaceProtocol = DYNAMIC */ 82*00a2430fSAndrzej Pietrasiewicz /* .iInterface = DYNAMIC */ 83*00a2430fSAndrzej Pietrasiewicz }; 84*00a2430fSAndrzej Pietrasiewicz 85*00a2430fSAndrzej Pietrasiewicz static struct hid_descriptor hidg_desc = { 86*00a2430fSAndrzej Pietrasiewicz .bLength = sizeof hidg_desc, 87*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = HID_DT_HID, 88*00a2430fSAndrzej Pietrasiewicz .bcdHID = 0x0101, 89*00a2430fSAndrzej Pietrasiewicz .bCountryCode = 0x00, 90*00a2430fSAndrzej Pietrasiewicz .bNumDescriptors = 0x1, 91*00a2430fSAndrzej Pietrasiewicz /*.desc[0].bDescriptorType = DYNAMIC */ 92*00a2430fSAndrzej Pietrasiewicz /*.desc[0].wDescriptorLenght = DYNAMIC */ 93*00a2430fSAndrzej Pietrasiewicz }; 94*00a2430fSAndrzej Pietrasiewicz 95*00a2430fSAndrzej Pietrasiewicz /* High-Speed Support */ 96*00a2430fSAndrzej Pietrasiewicz 97*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { 98*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 99*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 100*00a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 101*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 102*00a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 103*00a2430fSAndrzej Pietrasiewicz .bInterval = 4, /* FIXME: Add this field in the 104*00a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 105*00a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 106*00a2430fSAndrzej Pietrasiewicz */ 107*00a2430fSAndrzej Pietrasiewicz }; 108*00a2430fSAndrzej Pietrasiewicz 109*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { 110*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 111*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 112*00a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT, 113*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 114*00a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 115*00a2430fSAndrzej Pietrasiewicz .bInterval = 4, /* FIXME: Add this field in the 116*00a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 117*00a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 118*00a2430fSAndrzej Pietrasiewicz */ 119*00a2430fSAndrzej Pietrasiewicz }; 120*00a2430fSAndrzej Pietrasiewicz 121*00a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hidg_hs_descriptors[] = { 122*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_interface_desc, 123*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_desc, 124*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, 125*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_hs_out_ep_desc, 126*00a2430fSAndrzej Pietrasiewicz NULL, 127*00a2430fSAndrzej Pietrasiewicz }; 128*00a2430fSAndrzej Pietrasiewicz 129*00a2430fSAndrzej Pietrasiewicz /* Full-Speed Support */ 130*00a2430fSAndrzej Pietrasiewicz 131*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { 132*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 133*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 134*00a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 135*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 136*00a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 137*00a2430fSAndrzej Pietrasiewicz .bInterval = 10, /* FIXME: Add this field in the 138*00a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 139*00a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 140*00a2430fSAndrzej Pietrasiewicz */ 141*00a2430fSAndrzej Pietrasiewicz }; 142*00a2430fSAndrzej Pietrasiewicz 143*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { 144*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 145*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 146*00a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT, 147*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 148*00a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 149*00a2430fSAndrzej Pietrasiewicz .bInterval = 10, /* FIXME: Add this field in the 150*00a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 151*00a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 152*00a2430fSAndrzej Pietrasiewicz */ 153*00a2430fSAndrzej Pietrasiewicz }; 154*00a2430fSAndrzej Pietrasiewicz 155*00a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hidg_fs_descriptors[] = { 156*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_interface_desc, 157*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_desc, 158*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, 159*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_fs_out_ep_desc, 160*00a2430fSAndrzej Pietrasiewicz NULL, 161*00a2430fSAndrzej Pietrasiewicz }; 162*00a2430fSAndrzej Pietrasiewicz 163*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 164*00a2430fSAndrzej Pietrasiewicz /* Char Device */ 165*00a2430fSAndrzej Pietrasiewicz 166*00a2430fSAndrzej Pietrasiewicz static ssize_t f_hidg_read(struct file *file, char __user *buffer, 167*00a2430fSAndrzej Pietrasiewicz size_t count, loff_t *ptr) 168*00a2430fSAndrzej Pietrasiewicz { 169*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = file->private_data; 170*00a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list *list; 171*00a2430fSAndrzej Pietrasiewicz struct usb_request *req; 172*00a2430fSAndrzej Pietrasiewicz unsigned long flags; 173*00a2430fSAndrzej Pietrasiewicz int ret; 174*00a2430fSAndrzej Pietrasiewicz 175*00a2430fSAndrzej Pietrasiewicz if (!count) 176*00a2430fSAndrzej Pietrasiewicz return 0; 177*00a2430fSAndrzej Pietrasiewicz 178*00a2430fSAndrzej Pietrasiewicz if (!access_ok(VERIFY_WRITE, buffer, count)) 179*00a2430fSAndrzej Pietrasiewicz return -EFAULT; 180*00a2430fSAndrzej Pietrasiewicz 181*00a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 182*00a2430fSAndrzej Pietrasiewicz 183*00a2430fSAndrzej Pietrasiewicz #define READ_COND (!list_empty(&hidg->completed_out_req)) 184*00a2430fSAndrzej Pietrasiewicz 185*00a2430fSAndrzej Pietrasiewicz /* wait for at least one buffer to complete */ 186*00a2430fSAndrzej Pietrasiewicz while (!READ_COND) { 187*00a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 188*00a2430fSAndrzej Pietrasiewicz if (file->f_flags & O_NONBLOCK) 189*00a2430fSAndrzej Pietrasiewicz return -EAGAIN; 190*00a2430fSAndrzej Pietrasiewicz 191*00a2430fSAndrzej Pietrasiewicz if (wait_event_interruptible(hidg->read_queue, READ_COND)) 192*00a2430fSAndrzej Pietrasiewicz return -ERESTARTSYS; 193*00a2430fSAndrzej Pietrasiewicz 194*00a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 195*00a2430fSAndrzej Pietrasiewicz } 196*00a2430fSAndrzej Pietrasiewicz 197*00a2430fSAndrzej Pietrasiewicz /* pick the first one */ 198*00a2430fSAndrzej Pietrasiewicz list = list_first_entry(&hidg->completed_out_req, 199*00a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list, list); 200*00a2430fSAndrzej Pietrasiewicz req = list->req; 201*00a2430fSAndrzej Pietrasiewicz count = min_t(unsigned int, count, req->actual - list->pos); 202*00a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 203*00a2430fSAndrzej Pietrasiewicz 204*00a2430fSAndrzej Pietrasiewicz /* copy to user outside spinlock */ 205*00a2430fSAndrzej Pietrasiewicz count -= copy_to_user(buffer, req->buf + list->pos, count); 206*00a2430fSAndrzej Pietrasiewicz list->pos += count; 207*00a2430fSAndrzej Pietrasiewicz 208*00a2430fSAndrzej Pietrasiewicz /* 209*00a2430fSAndrzej Pietrasiewicz * if this request is completely handled and transfered to 210*00a2430fSAndrzej Pietrasiewicz * userspace, remove its entry from the list and requeue it 211*00a2430fSAndrzej Pietrasiewicz * again. Otherwise, we will revisit it again upon the next 212*00a2430fSAndrzej Pietrasiewicz * call, taking into account its current read position. 213*00a2430fSAndrzej Pietrasiewicz */ 214*00a2430fSAndrzej Pietrasiewicz if (list->pos == req->actual) { 215*00a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 216*00a2430fSAndrzej Pietrasiewicz list_del(&list->list); 217*00a2430fSAndrzej Pietrasiewicz kfree(list); 218*00a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 219*00a2430fSAndrzej Pietrasiewicz 220*00a2430fSAndrzej Pietrasiewicz req->length = hidg->report_length; 221*00a2430fSAndrzej Pietrasiewicz ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL); 222*00a2430fSAndrzej Pietrasiewicz if (ret < 0) 223*00a2430fSAndrzej Pietrasiewicz return ret; 224*00a2430fSAndrzej Pietrasiewicz } 225*00a2430fSAndrzej Pietrasiewicz 226*00a2430fSAndrzej Pietrasiewicz return count; 227*00a2430fSAndrzej Pietrasiewicz } 228*00a2430fSAndrzej Pietrasiewicz 229*00a2430fSAndrzej Pietrasiewicz static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req) 230*00a2430fSAndrzej Pietrasiewicz { 231*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; 232*00a2430fSAndrzej Pietrasiewicz 233*00a2430fSAndrzej Pietrasiewicz if (req->status != 0) { 234*00a2430fSAndrzej Pietrasiewicz ERROR(hidg->func.config->cdev, 235*00a2430fSAndrzej Pietrasiewicz "End Point Request ERROR: %d\n", req->status); 236*00a2430fSAndrzej Pietrasiewicz } 237*00a2430fSAndrzej Pietrasiewicz 238*00a2430fSAndrzej Pietrasiewicz hidg->write_pending = 0; 239*00a2430fSAndrzej Pietrasiewicz wake_up(&hidg->write_queue); 240*00a2430fSAndrzej Pietrasiewicz } 241*00a2430fSAndrzej Pietrasiewicz 242*00a2430fSAndrzej Pietrasiewicz static ssize_t f_hidg_write(struct file *file, const char __user *buffer, 243*00a2430fSAndrzej Pietrasiewicz size_t count, loff_t *offp) 244*00a2430fSAndrzej Pietrasiewicz { 245*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = file->private_data; 246*00a2430fSAndrzej Pietrasiewicz ssize_t status = -ENOMEM; 247*00a2430fSAndrzej Pietrasiewicz 248*00a2430fSAndrzej Pietrasiewicz if (!access_ok(VERIFY_READ, buffer, count)) 249*00a2430fSAndrzej Pietrasiewicz return -EFAULT; 250*00a2430fSAndrzej Pietrasiewicz 251*00a2430fSAndrzej Pietrasiewicz mutex_lock(&hidg->lock); 252*00a2430fSAndrzej Pietrasiewicz 253*00a2430fSAndrzej Pietrasiewicz #define WRITE_COND (!hidg->write_pending) 254*00a2430fSAndrzej Pietrasiewicz 255*00a2430fSAndrzej Pietrasiewicz /* write queue */ 256*00a2430fSAndrzej Pietrasiewicz while (!WRITE_COND) { 257*00a2430fSAndrzej Pietrasiewicz mutex_unlock(&hidg->lock); 258*00a2430fSAndrzej Pietrasiewicz if (file->f_flags & O_NONBLOCK) 259*00a2430fSAndrzej Pietrasiewicz return -EAGAIN; 260*00a2430fSAndrzej Pietrasiewicz 261*00a2430fSAndrzej Pietrasiewicz if (wait_event_interruptible_exclusive( 262*00a2430fSAndrzej Pietrasiewicz hidg->write_queue, WRITE_COND)) 263*00a2430fSAndrzej Pietrasiewicz return -ERESTARTSYS; 264*00a2430fSAndrzej Pietrasiewicz 265*00a2430fSAndrzej Pietrasiewicz mutex_lock(&hidg->lock); 266*00a2430fSAndrzej Pietrasiewicz } 267*00a2430fSAndrzej Pietrasiewicz 268*00a2430fSAndrzej Pietrasiewicz count = min_t(unsigned, count, hidg->report_length); 269*00a2430fSAndrzej Pietrasiewicz status = copy_from_user(hidg->req->buf, buffer, count); 270*00a2430fSAndrzej Pietrasiewicz 271*00a2430fSAndrzej Pietrasiewicz if (status != 0) { 272*00a2430fSAndrzej Pietrasiewicz ERROR(hidg->func.config->cdev, 273*00a2430fSAndrzej Pietrasiewicz "copy_from_user error\n"); 274*00a2430fSAndrzej Pietrasiewicz mutex_unlock(&hidg->lock); 275*00a2430fSAndrzej Pietrasiewicz return -EINVAL; 276*00a2430fSAndrzej Pietrasiewicz } 277*00a2430fSAndrzej Pietrasiewicz 278*00a2430fSAndrzej Pietrasiewicz hidg->req->status = 0; 279*00a2430fSAndrzej Pietrasiewicz hidg->req->zero = 0; 280*00a2430fSAndrzej Pietrasiewicz hidg->req->length = count; 281*00a2430fSAndrzej Pietrasiewicz hidg->req->complete = f_hidg_req_complete; 282*00a2430fSAndrzej Pietrasiewicz hidg->req->context = hidg; 283*00a2430fSAndrzej Pietrasiewicz hidg->write_pending = 1; 284*00a2430fSAndrzej Pietrasiewicz 285*00a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC); 286*00a2430fSAndrzej Pietrasiewicz if (status < 0) { 287*00a2430fSAndrzej Pietrasiewicz ERROR(hidg->func.config->cdev, 288*00a2430fSAndrzej Pietrasiewicz "usb_ep_queue error on int endpoint %zd\n", status); 289*00a2430fSAndrzej Pietrasiewicz hidg->write_pending = 0; 290*00a2430fSAndrzej Pietrasiewicz wake_up(&hidg->write_queue); 291*00a2430fSAndrzej Pietrasiewicz } else { 292*00a2430fSAndrzej Pietrasiewicz status = count; 293*00a2430fSAndrzej Pietrasiewicz } 294*00a2430fSAndrzej Pietrasiewicz 295*00a2430fSAndrzej Pietrasiewicz mutex_unlock(&hidg->lock); 296*00a2430fSAndrzej Pietrasiewicz 297*00a2430fSAndrzej Pietrasiewicz return status; 298*00a2430fSAndrzej Pietrasiewicz } 299*00a2430fSAndrzej Pietrasiewicz 300*00a2430fSAndrzej Pietrasiewicz static unsigned int f_hidg_poll(struct file *file, poll_table *wait) 301*00a2430fSAndrzej Pietrasiewicz { 302*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = file->private_data; 303*00a2430fSAndrzej Pietrasiewicz unsigned int ret = 0; 304*00a2430fSAndrzej Pietrasiewicz 305*00a2430fSAndrzej Pietrasiewicz poll_wait(file, &hidg->read_queue, wait); 306*00a2430fSAndrzej Pietrasiewicz poll_wait(file, &hidg->write_queue, wait); 307*00a2430fSAndrzej Pietrasiewicz 308*00a2430fSAndrzej Pietrasiewicz if (WRITE_COND) 309*00a2430fSAndrzej Pietrasiewicz ret |= POLLOUT | POLLWRNORM; 310*00a2430fSAndrzej Pietrasiewicz 311*00a2430fSAndrzej Pietrasiewicz if (READ_COND) 312*00a2430fSAndrzej Pietrasiewicz ret |= POLLIN | POLLRDNORM; 313*00a2430fSAndrzej Pietrasiewicz 314*00a2430fSAndrzej Pietrasiewicz return ret; 315*00a2430fSAndrzej Pietrasiewicz } 316*00a2430fSAndrzej Pietrasiewicz 317*00a2430fSAndrzej Pietrasiewicz #undef WRITE_COND 318*00a2430fSAndrzej Pietrasiewicz #undef READ_COND 319*00a2430fSAndrzej Pietrasiewicz 320*00a2430fSAndrzej Pietrasiewicz static int f_hidg_release(struct inode *inode, struct file *fd) 321*00a2430fSAndrzej Pietrasiewicz { 322*00a2430fSAndrzej Pietrasiewicz fd->private_data = NULL; 323*00a2430fSAndrzej Pietrasiewicz return 0; 324*00a2430fSAndrzej Pietrasiewicz } 325*00a2430fSAndrzej Pietrasiewicz 326*00a2430fSAndrzej Pietrasiewicz static int f_hidg_open(struct inode *inode, struct file *fd) 327*00a2430fSAndrzej Pietrasiewicz { 328*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = 329*00a2430fSAndrzej Pietrasiewicz container_of(inode->i_cdev, struct f_hidg, cdev); 330*00a2430fSAndrzej Pietrasiewicz 331*00a2430fSAndrzej Pietrasiewicz fd->private_data = hidg; 332*00a2430fSAndrzej Pietrasiewicz 333*00a2430fSAndrzej Pietrasiewicz return 0; 334*00a2430fSAndrzej Pietrasiewicz } 335*00a2430fSAndrzej Pietrasiewicz 336*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 337*00a2430fSAndrzej Pietrasiewicz /* usb_function */ 338*00a2430fSAndrzej Pietrasiewicz 339*00a2430fSAndrzej Pietrasiewicz static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, 340*00a2430fSAndrzej Pietrasiewicz unsigned length) 341*00a2430fSAndrzej Pietrasiewicz { 342*00a2430fSAndrzej Pietrasiewicz return alloc_ep_req(ep, length, length); 343*00a2430fSAndrzej Pietrasiewicz } 344*00a2430fSAndrzej Pietrasiewicz 345*00a2430fSAndrzej Pietrasiewicz static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) 346*00a2430fSAndrzej Pietrasiewicz { 347*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = (struct f_hidg *) req->context; 348*00a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list *req_list; 349*00a2430fSAndrzej Pietrasiewicz unsigned long flags; 350*00a2430fSAndrzej Pietrasiewicz 351*00a2430fSAndrzej Pietrasiewicz req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC); 352*00a2430fSAndrzej Pietrasiewicz if (!req_list) 353*00a2430fSAndrzej Pietrasiewicz return; 354*00a2430fSAndrzej Pietrasiewicz 355*00a2430fSAndrzej Pietrasiewicz req_list->req = req; 356*00a2430fSAndrzej Pietrasiewicz 357*00a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 358*00a2430fSAndrzej Pietrasiewicz list_add_tail(&req_list->list, &hidg->completed_out_req); 359*00a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 360*00a2430fSAndrzej Pietrasiewicz 361*00a2430fSAndrzej Pietrasiewicz wake_up(&hidg->read_queue); 362*00a2430fSAndrzej Pietrasiewicz } 363*00a2430fSAndrzej Pietrasiewicz 364*00a2430fSAndrzej Pietrasiewicz static int hidg_setup(struct usb_function *f, 365*00a2430fSAndrzej Pietrasiewicz const struct usb_ctrlrequest *ctrl) 366*00a2430fSAndrzej Pietrasiewicz { 367*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 368*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev; 369*00a2430fSAndrzej Pietrasiewicz struct usb_request *req = cdev->req; 370*00a2430fSAndrzej Pietrasiewicz int status = 0; 371*00a2430fSAndrzej Pietrasiewicz __u16 value, length; 372*00a2430fSAndrzej Pietrasiewicz 373*00a2430fSAndrzej Pietrasiewicz value = __le16_to_cpu(ctrl->wValue); 374*00a2430fSAndrzej Pietrasiewicz length = __le16_to_cpu(ctrl->wLength); 375*00a2430fSAndrzej Pietrasiewicz 376*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x " 377*00a2430fSAndrzej Pietrasiewicz "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value); 378*00a2430fSAndrzej Pietrasiewicz 379*00a2430fSAndrzej Pietrasiewicz switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { 380*00a2430fSAndrzej Pietrasiewicz case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 381*00a2430fSAndrzej Pietrasiewicz | HID_REQ_GET_REPORT): 382*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "get_report\n"); 383*00a2430fSAndrzej Pietrasiewicz 384*00a2430fSAndrzej Pietrasiewicz /* send an empty report */ 385*00a2430fSAndrzej Pietrasiewicz length = min_t(unsigned, length, hidg->report_length); 386*00a2430fSAndrzej Pietrasiewicz memset(req->buf, 0x0, length); 387*00a2430fSAndrzej Pietrasiewicz 388*00a2430fSAndrzej Pietrasiewicz goto respond; 389*00a2430fSAndrzej Pietrasiewicz break; 390*00a2430fSAndrzej Pietrasiewicz 391*00a2430fSAndrzej Pietrasiewicz case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 392*00a2430fSAndrzej Pietrasiewicz | HID_REQ_GET_PROTOCOL): 393*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "get_protocol\n"); 394*00a2430fSAndrzej Pietrasiewicz goto stall; 395*00a2430fSAndrzej Pietrasiewicz break; 396*00a2430fSAndrzej Pietrasiewicz 397*00a2430fSAndrzej Pietrasiewicz case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 398*00a2430fSAndrzej Pietrasiewicz | HID_REQ_SET_REPORT): 399*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength); 400*00a2430fSAndrzej Pietrasiewicz goto stall; 401*00a2430fSAndrzej Pietrasiewicz break; 402*00a2430fSAndrzej Pietrasiewicz 403*00a2430fSAndrzej Pietrasiewicz case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 404*00a2430fSAndrzej Pietrasiewicz | HID_REQ_SET_PROTOCOL): 405*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "set_protocol\n"); 406*00a2430fSAndrzej Pietrasiewicz goto stall; 407*00a2430fSAndrzej Pietrasiewicz break; 408*00a2430fSAndrzej Pietrasiewicz 409*00a2430fSAndrzej Pietrasiewicz case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8 410*00a2430fSAndrzej Pietrasiewicz | USB_REQ_GET_DESCRIPTOR): 411*00a2430fSAndrzej Pietrasiewicz switch (value >> 8) { 412*00a2430fSAndrzej Pietrasiewicz case HID_DT_HID: 413*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n"); 414*00a2430fSAndrzej Pietrasiewicz length = min_t(unsigned short, length, 415*00a2430fSAndrzej Pietrasiewicz hidg_desc.bLength); 416*00a2430fSAndrzej Pietrasiewicz memcpy(req->buf, &hidg_desc, length); 417*00a2430fSAndrzej Pietrasiewicz goto respond; 418*00a2430fSAndrzej Pietrasiewicz break; 419*00a2430fSAndrzej Pietrasiewicz case HID_DT_REPORT: 420*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n"); 421*00a2430fSAndrzej Pietrasiewicz length = min_t(unsigned short, length, 422*00a2430fSAndrzej Pietrasiewicz hidg->report_desc_length); 423*00a2430fSAndrzej Pietrasiewicz memcpy(req->buf, hidg->report_desc, length); 424*00a2430fSAndrzej Pietrasiewicz goto respond; 425*00a2430fSAndrzej Pietrasiewicz break; 426*00a2430fSAndrzej Pietrasiewicz 427*00a2430fSAndrzej Pietrasiewicz default: 428*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "Unknown descriptor request 0x%x\n", 429*00a2430fSAndrzej Pietrasiewicz value >> 8); 430*00a2430fSAndrzej Pietrasiewicz goto stall; 431*00a2430fSAndrzej Pietrasiewicz break; 432*00a2430fSAndrzej Pietrasiewicz } 433*00a2430fSAndrzej Pietrasiewicz break; 434*00a2430fSAndrzej Pietrasiewicz 435*00a2430fSAndrzej Pietrasiewicz default: 436*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "Unknown request 0x%x\n", 437*00a2430fSAndrzej Pietrasiewicz ctrl->bRequest); 438*00a2430fSAndrzej Pietrasiewicz goto stall; 439*00a2430fSAndrzej Pietrasiewicz break; 440*00a2430fSAndrzej Pietrasiewicz } 441*00a2430fSAndrzej Pietrasiewicz 442*00a2430fSAndrzej Pietrasiewicz stall: 443*00a2430fSAndrzej Pietrasiewicz return -EOPNOTSUPP; 444*00a2430fSAndrzej Pietrasiewicz 445*00a2430fSAndrzej Pietrasiewicz respond: 446*00a2430fSAndrzej Pietrasiewicz req->zero = 0; 447*00a2430fSAndrzej Pietrasiewicz req->length = length; 448*00a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); 449*00a2430fSAndrzej Pietrasiewicz if (status < 0) 450*00a2430fSAndrzej Pietrasiewicz ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value); 451*00a2430fSAndrzej Pietrasiewicz return status; 452*00a2430fSAndrzej Pietrasiewicz } 453*00a2430fSAndrzej Pietrasiewicz 454*00a2430fSAndrzej Pietrasiewicz static void hidg_disable(struct usb_function *f) 455*00a2430fSAndrzej Pietrasiewicz { 456*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 457*00a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list *list, *next; 458*00a2430fSAndrzej Pietrasiewicz 459*00a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->in_ep); 460*00a2430fSAndrzej Pietrasiewicz hidg->in_ep->driver_data = NULL; 461*00a2430fSAndrzej Pietrasiewicz 462*00a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->out_ep); 463*00a2430fSAndrzej Pietrasiewicz hidg->out_ep->driver_data = NULL; 464*00a2430fSAndrzej Pietrasiewicz 465*00a2430fSAndrzej Pietrasiewicz list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { 466*00a2430fSAndrzej Pietrasiewicz list_del(&list->list); 467*00a2430fSAndrzej Pietrasiewicz kfree(list); 468*00a2430fSAndrzej Pietrasiewicz } 469*00a2430fSAndrzej Pietrasiewicz } 470*00a2430fSAndrzej Pietrasiewicz 471*00a2430fSAndrzej Pietrasiewicz static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 472*00a2430fSAndrzej Pietrasiewicz { 473*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev; 474*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 475*00a2430fSAndrzej Pietrasiewicz int i, status = 0; 476*00a2430fSAndrzej Pietrasiewicz 477*00a2430fSAndrzej Pietrasiewicz VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); 478*00a2430fSAndrzej Pietrasiewicz 479*00a2430fSAndrzej Pietrasiewicz if (hidg->in_ep != NULL) { 480*00a2430fSAndrzej Pietrasiewicz /* restart endpoint */ 481*00a2430fSAndrzej Pietrasiewicz if (hidg->in_ep->driver_data != NULL) 482*00a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->in_ep); 483*00a2430fSAndrzej Pietrasiewicz 484*00a2430fSAndrzej Pietrasiewicz status = config_ep_by_speed(f->config->cdev->gadget, f, 485*00a2430fSAndrzej Pietrasiewicz hidg->in_ep); 486*00a2430fSAndrzej Pietrasiewicz if (status) { 487*00a2430fSAndrzej Pietrasiewicz ERROR(cdev, "config_ep_by_speed FAILED!\n"); 488*00a2430fSAndrzej Pietrasiewicz goto fail; 489*00a2430fSAndrzej Pietrasiewicz } 490*00a2430fSAndrzej Pietrasiewicz status = usb_ep_enable(hidg->in_ep); 491*00a2430fSAndrzej Pietrasiewicz if (status < 0) { 492*00a2430fSAndrzej Pietrasiewicz ERROR(cdev, "Enable IN endpoint FAILED!\n"); 493*00a2430fSAndrzej Pietrasiewicz goto fail; 494*00a2430fSAndrzej Pietrasiewicz } 495*00a2430fSAndrzej Pietrasiewicz hidg->in_ep->driver_data = hidg; 496*00a2430fSAndrzej Pietrasiewicz } 497*00a2430fSAndrzej Pietrasiewicz 498*00a2430fSAndrzej Pietrasiewicz 499*00a2430fSAndrzej Pietrasiewicz if (hidg->out_ep != NULL) { 500*00a2430fSAndrzej Pietrasiewicz /* restart endpoint */ 501*00a2430fSAndrzej Pietrasiewicz if (hidg->out_ep->driver_data != NULL) 502*00a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->out_ep); 503*00a2430fSAndrzej Pietrasiewicz 504*00a2430fSAndrzej Pietrasiewicz status = config_ep_by_speed(f->config->cdev->gadget, f, 505*00a2430fSAndrzej Pietrasiewicz hidg->out_ep); 506*00a2430fSAndrzej Pietrasiewicz if (status) { 507*00a2430fSAndrzej Pietrasiewicz ERROR(cdev, "config_ep_by_speed FAILED!\n"); 508*00a2430fSAndrzej Pietrasiewicz goto fail; 509*00a2430fSAndrzej Pietrasiewicz } 510*00a2430fSAndrzej Pietrasiewicz status = usb_ep_enable(hidg->out_ep); 511*00a2430fSAndrzej Pietrasiewicz if (status < 0) { 512*00a2430fSAndrzej Pietrasiewicz ERROR(cdev, "Enable IN endpoint FAILED!\n"); 513*00a2430fSAndrzej Pietrasiewicz goto fail; 514*00a2430fSAndrzej Pietrasiewicz } 515*00a2430fSAndrzej Pietrasiewicz hidg->out_ep->driver_data = hidg; 516*00a2430fSAndrzej Pietrasiewicz 517*00a2430fSAndrzej Pietrasiewicz /* 518*00a2430fSAndrzej Pietrasiewicz * allocate a bunch of read buffers and queue them all at once. 519*00a2430fSAndrzej Pietrasiewicz */ 520*00a2430fSAndrzej Pietrasiewicz for (i = 0; i < hidg->qlen && status == 0; i++) { 521*00a2430fSAndrzej Pietrasiewicz struct usb_request *req = 522*00a2430fSAndrzej Pietrasiewicz hidg_alloc_ep_req(hidg->out_ep, 523*00a2430fSAndrzej Pietrasiewicz hidg->report_length); 524*00a2430fSAndrzej Pietrasiewicz if (req) { 525*00a2430fSAndrzej Pietrasiewicz req->complete = hidg_set_report_complete; 526*00a2430fSAndrzej Pietrasiewicz req->context = hidg; 527*00a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(hidg->out_ep, req, 528*00a2430fSAndrzej Pietrasiewicz GFP_ATOMIC); 529*00a2430fSAndrzej Pietrasiewicz if (status) 530*00a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s queue req --> %d\n", 531*00a2430fSAndrzej Pietrasiewicz hidg->out_ep->name, status); 532*00a2430fSAndrzej Pietrasiewicz } else { 533*00a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->out_ep); 534*00a2430fSAndrzej Pietrasiewicz hidg->out_ep->driver_data = NULL; 535*00a2430fSAndrzej Pietrasiewicz status = -ENOMEM; 536*00a2430fSAndrzej Pietrasiewicz goto fail; 537*00a2430fSAndrzej Pietrasiewicz } 538*00a2430fSAndrzej Pietrasiewicz } 539*00a2430fSAndrzej Pietrasiewicz } 540*00a2430fSAndrzej Pietrasiewicz 541*00a2430fSAndrzej Pietrasiewicz fail: 542*00a2430fSAndrzej Pietrasiewicz return status; 543*00a2430fSAndrzej Pietrasiewicz } 544*00a2430fSAndrzej Pietrasiewicz 545*00a2430fSAndrzej Pietrasiewicz const struct file_operations f_hidg_fops = { 546*00a2430fSAndrzej Pietrasiewicz .owner = THIS_MODULE, 547*00a2430fSAndrzej Pietrasiewicz .open = f_hidg_open, 548*00a2430fSAndrzej Pietrasiewicz .release = f_hidg_release, 549*00a2430fSAndrzej Pietrasiewicz .write = f_hidg_write, 550*00a2430fSAndrzej Pietrasiewicz .read = f_hidg_read, 551*00a2430fSAndrzej Pietrasiewicz .poll = f_hidg_poll, 552*00a2430fSAndrzej Pietrasiewicz .llseek = noop_llseek, 553*00a2430fSAndrzej Pietrasiewicz }; 554*00a2430fSAndrzej Pietrasiewicz 555*00a2430fSAndrzej Pietrasiewicz static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) 556*00a2430fSAndrzej Pietrasiewicz { 557*00a2430fSAndrzej Pietrasiewicz struct usb_ep *ep; 558*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 559*00a2430fSAndrzej Pietrasiewicz int status; 560*00a2430fSAndrzej Pietrasiewicz dev_t dev; 561*00a2430fSAndrzej Pietrasiewicz 562*00a2430fSAndrzej Pietrasiewicz /* allocate instance-specific interface IDs, and patch descriptors */ 563*00a2430fSAndrzej Pietrasiewicz status = usb_interface_id(c, f); 564*00a2430fSAndrzej Pietrasiewicz if (status < 0) 565*00a2430fSAndrzej Pietrasiewicz goto fail; 566*00a2430fSAndrzej Pietrasiewicz hidg_interface_desc.bInterfaceNumber = status; 567*00a2430fSAndrzej Pietrasiewicz 568*00a2430fSAndrzej Pietrasiewicz /* allocate instance-specific endpoints */ 569*00a2430fSAndrzej Pietrasiewicz status = -ENODEV; 570*00a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc); 571*00a2430fSAndrzej Pietrasiewicz if (!ep) 572*00a2430fSAndrzej Pietrasiewicz goto fail; 573*00a2430fSAndrzej Pietrasiewicz ep->driver_data = c->cdev; /* claim */ 574*00a2430fSAndrzej Pietrasiewicz hidg->in_ep = ep; 575*00a2430fSAndrzej Pietrasiewicz 576*00a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); 577*00a2430fSAndrzej Pietrasiewicz if (!ep) 578*00a2430fSAndrzej Pietrasiewicz goto fail; 579*00a2430fSAndrzej Pietrasiewicz ep->driver_data = c->cdev; /* claim */ 580*00a2430fSAndrzej Pietrasiewicz hidg->out_ep = ep; 581*00a2430fSAndrzej Pietrasiewicz 582*00a2430fSAndrzej Pietrasiewicz /* preallocate request and buffer */ 583*00a2430fSAndrzej Pietrasiewicz status = -ENOMEM; 584*00a2430fSAndrzej Pietrasiewicz hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); 585*00a2430fSAndrzej Pietrasiewicz if (!hidg->req) 586*00a2430fSAndrzej Pietrasiewicz goto fail; 587*00a2430fSAndrzej Pietrasiewicz 588*00a2430fSAndrzej Pietrasiewicz hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL); 589*00a2430fSAndrzej Pietrasiewicz if (!hidg->req->buf) 590*00a2430fSAndrzej Pietrasiewicz goto fail; 591*00a2430fSAndrzej Pietrasiewicz 592*00a2430fSAndrzej Pietrasiewicz /* set descriptor dynamic values */ 593*00a2430fSAndrzej Pietrasiewicz hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; 594*00a2430fSAndrzej Pietrasiewicz hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; 595*00a2430fSAndrzej Pietrasiewicz hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 596*00a2430fSAndrzej Pietrasiewicz hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 597*00a2430fSAndrzej Pietrasiewicz hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 598*00a2430fSAndrzej Pietrasiewicz hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 599*00a2430fSAndrzej Pietrasiewicz hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT; 600*00a2430fSAndrzej Pietrasiewicz hidg_desc.desc[0].wDescriptorLength = 601*00a2430fSAndrzej Pietrasiewicz cpu_to_le16(hidg->report_desc_length); 602*00a2430fSAndrzej Pietrasiewicz 603*00a2430fSAndrzej Pietrasiewicz hidg_hs_in_ep_desc.bEndpointAddress = 604*00a2430fSAndrzej Pietrasiewicz hidg_fs_in_ep_desc.bEndpointAddress; 605*00a2430fSAndrzej Pietrasiewicz hidg_hs_out_ep_desc.bEndpointAddress = 606*00a2430fSAndrzej Pietrasiewicz hidg_fs_out_ep_desc.bEndpointAddress; 607*00a2430fSAndrzej Pietrasiewicz 608*00a2430fSAndrzej Pietrasiewicz status = usb_assign_descriptors(f, hidg_fs_descriptors, 609*00a2430fSAndrzej Pietrasiewicz hidg_hs_descriptors, NULL); 610*00a2430fSAndrzej Pietrasiewicz if (status) 611*00a2430fSAndrzej Pietrasiewicz goto fail; 612*00a2430fSAndrzej Pietrasiewicz 613*00a2430fSAndrzej Pietrasiewicz mutex_init(&hidg->lock); 614*00a2430fSAndrzej Pietrasiewicz spin_lock_init(&hidg->spinlock); 615*00a2430fSAndrzej Pietrasiewicz init_waitqueue_head(&hidg->write_queue); 616*00a2430fSAndrzej Pietrasiewicz init_waitqueue_head(&hidg->read_queue); 617*00a2430fSAndrzej Pietrasiewicz INIT_LIST_HEAD(&hidg->completed_out_req); 618*00a2430fSAndrzej Pietrasiewicz 619*00a2430fSAndrzej Pietrasiewicz /* create char device */ 620*00a2430fSAndrzej Pietrasiewicz cdev_init(&hidg->cdev, &f_hidg_fops); 621*00a2430fSAndrzej Pietrasiewicz dev = MKDEV(major, hidg->minor); 622*00a2430fSAndrzej Pietrasiewicz status = cdev_add(&hidg->cdev, dev, 1); 623*00a2430fSAndrzej Pietrasiewicz if (status) 624*00a2430fSAndrzej Pietrasiewicz goto fail; 625*00a2430fSAndrzej Pietrasiewicz 626*00a2430fSAndrzej Pietrasiewicz device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor); 627*00a2430fSAndrzej Pietrasiewicz 628*00a2430fSAndrzej Pietrasiewicz return 0; 629*00a2430fSAndrzej Pietrasiewicz 630*00a2430fSAndrzej Pietrasiewicz fail: 631*00a2430fSAndrzej Pietrasiewicz ERROR(f->config->cdev, "hidg_bind FAILED\n"); 632*00a2430fSAndrzej Pietrasiewicz if (hidg->req != NULL) { 633*00a2430fSAndrzej Pietrasiewicz kfree(hidg->req->buf); 634*00a2430fSAndrzej Pietrasiewicz if (hidg->in_ep != NULL) 635*00a2430fSAndrzej Pietrasiewicz usb_ep_free_request(hidg->in_ep, hidg->req); 636*00a2430fSAndrzej Pietrasiewicz } 637*00a2430fSAndrzej Pietrasiewicz 638*00a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f); 639*00a2430fSAndrzej Pietrasiewicz return status; 640*00a2430fSAndrzej Pietrasiewicz } 641*00a2430fSAndrzej Pietrasiewicz 642*00a2430fSAndrzej Pietrasiewicz static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) 643*00a2430fSAndrzej Pietrasiewicz { 644*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 645*00a2430fSAndrzej Pietrasiewicz 646*00a2430fSAndrzej Pietrasiewicz device_destroy(hidg_class, MKDEV(major, hidg->minor)); 647*00a2430fSAndrzej Pietrasiewicz cdev_del(&hidg->cdev); 648*00a2430fSAndrzej Pietrasiewicz 649*00a2430fSAndrzej Pietrasiewicz /* disable/free request and end point */ 650*00a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->in_ep); 651*00a2430fSAndrzej Pietrasiewicz usb_ep_dequeue(hidg->in_ep, hidg->req); 652*00a2430fSAndrzej Pietrasiewicz kfree(hidg->req->buf); 653*00a2430fSAndrzej Pietrasiewicz usb_ep_free_request(hidg->in_ep, hidg->req); 654*00a2430fSAndrzej Pietrasiewicz 655*00a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f); 656*00a2430fSAndrzej Pietrasiewicz 657*00a2430fSAndrzej Pietrasiewicz kfree(hidg->report_desc); 658*00a2430fSAndrzej Pietrasiewicz kfree(hidg); 659*00a2430fSAndrzej Pietrasiewicz } 660*00a2430fSAndrzej Pietrasiewicz 661*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 662*00a2430fSAndrzej Pietrasiewicz /* Strings */ 663*00a2430fSAndrzej Pietrasiewicz 664*00a2430fSAndrzej Pietrasiewicz #define CT_FUNC_HID_IDX 0 665*00a2430fSAndrzej Pietrasiewicz 666*00a2430fSAndrzej Pietrasiewicz static struct usb_string ct_func_string_defs[] = { 667*00a2430fSAndrzej Pietrasiewicz [CT_FUNC_HID_IDX].s = "HID Interface", 668*00a2430fSAndrzej Pietrasiewicz {}, /* end of list */ 669*00a2430fSAndrzej Pietrasiewicz }; 670*00a2430fSAndrzej Pietrasiewicz 671*00a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings ct_func_string_table = { 672*00a2430fSAndrzej Pietrasiewicz .language = 0x0409, /* en-US */ 673*00a2430fSAndrzej Pietrasiewicz .strings = ct_func_string_defs, 674*00a2430fSAndrzej Pietrasiewicz }; 675*00a2430fSAndrzej Pietrasiewicz 676*00a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *ct_func_strings[] = { 677*00a2430fSAndrzej Pietrasiewicz &ct_func_string_table, 678*00a2430fSAndrzej Pietrasiewicz NULL, 679*00a2430fSAndrzej Pietrasiewicz }; 680*00a2430fSAndrzej Pietrasiewicz 681*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 682*00a2430fSAndrzej Pietrasiewicz /* usb_configuration */ 683*00a2430fSAndrzej Pietrasiewicz 684*00a2430fSAndrzej Pietrasiewicz int __init hidg_bind_config(struct usb_configuration *c, 685*00a2430fSAndrzej Pietrasiewicz struct hidg_func_descriptor *fdesc, int index) 686*00a2430fSAndrzej Pietrasiewicz { 687*00a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg; 688*00a2430fSAndrzej Pietrasiewicz int status; 689*00a2430fSAndrzej Pietrasiewicz 690*00a2430fSAndrzej Pietrasiewicz if (index >= minors) 691*00a2430fSAndrzej Pietrasiewicz return -ENOENT; 692*00a2430fSAndrzej Pietrasiewicz 693*00a2430fSAndrzej Pietrasiewicz /* maybe allocate device-global string IDs, and patch descriptors */ 694*00a2430fSAndrzej Pietrasiewicz if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) { 695*00a2430fSAndrzej Pietrasiewicz status = usb_string_id(c->cdev); 696*00a2430fSAndrzej Pietrasiewicz if (status < 0) 697*00a2430fSAndrzej Pietrasiewicz return status; 698*00a2430fSAndrzej Pietrasiewicz ct_func_string_defs[CT_FUNC_HID_IDX].id = status; 699*00a2430fSAndrzej Pietrasiewicz hidg_interface_desc.iInterface = status; 700*00a2430fSAndrzej Pietrasiewicz } 701*00a2430fSAndrzej Pietrasiewicz 702*00a2430fSAndrzej Pietrasiewicz /* allocate and initialize one new instance */ 703*00a2430fSAndrzej Pietrasiewicz hidg = kzalloc(sizeof *hidg, GFP_KERNEL); 704*00a2430fSAndrzej Pietrasiewicz if (!hidg) 705*00a2430fSAndrzej Pietrasiewicz return -ENOMEM; 706*00a2430fSAndrzej Pietrasiewicz 707*00a2430fSAndrzej Pietrasiewicz hidg->minor = index; 708*00a2430fSAndrzej Pietrasiewicz hidg->bInterfaceSubClass = fdesc->subclass; 709*00a2430fSAndrzej Pietrasiewicz hidg->bInterfaceProtocol = fdesc->protocol; 710*00a2430fSAndrzej Pietrasiewicz hidg->report_length = fdesc->report_length; 711*00a2430fSAndrzej Pietrasiewicz hidg->report_desc_length = fdesc->report_desc_length; 712*00a2430fSAndrzej Pietrasiewicz hidg->report_desc = kmemdup(fdesc->report_desc, 713*00a2430fSAndrzej Pietrasiewicz fdesc->report_desc_length, 714*00a2430fSAndrzej Pietrasiewicz GFP_KERNEL); 715*00a2430fSAndrzej Pietrasiewicz if (!hidg->report_desc) { 716*00a2430fSAndrzej Pietrasiewicz kfree(hidg); 717*00a2430fSAndrzej Pietrasiewicz return -ENOMEM; 718*00a2430fSAndrzej Pietrasiewicz } 719*00a2430fSAndrzej Pietrasiewicz 720*00a2430fSAndrzej Pietrasiewicz hidg->func.name = "hid"; 721*00a2430fSAndrzej Pietrasiewicz hidg->func.strings = ct_func_strings; 722*00a2430fSAndrzej Pietrasiewicz hidg->func.bind = hidg_bind; 723*00a2430fSAndrzej Pietrasiewicz hidg->func.unbind = hidg_unbind; 724*00a2430fSAndrzej Pietrasiewicz hidg->func.set_alt = hidg_set_alt; 725*00a2430fSAndrzej Pietrasiewicz hidg->func.disable = hidg_disable; 726*00a2430fSAndrzej Pietrasiewicz hidg->func.setup = hidg_setup; 727*00a2430fSAndrzej Pietrasiewicz 728*00a2430fSAndrzej Pietrasiewicz /* this could me made configurable at some point */ 729*00a2430fSAndrzej Pietrasiewicz hidg->qlen = 4; 730*00a2430fSAndrzej Pietrasiewicz 731*00a2430fSAndrzej Pietrasiewicz status = usb_add_function(c, &hidg->func); 732*00a2430fSAndrzej Pietrasiewicz if (status) 733*00a2430fSAndrzej Pietrasiewicz kfree(hidg); 734*00a2430fSAndrzej Pietrasiewicz 735*00a2430fSAndrzej Pietrasiewicz return status; 736*00a2430fSAndrzej Pietrasiewicz } 737*00a2430fSAndrzej Pietrasiewicz 738*00a2430fSAndrzej Pietrasiewicz int __init ghid_setup(struct usb_gadget *g, int count) 739*00a2430fSAndrzej Pietrasiewicz { 740*00a2430fSAndrzej Pietrasiewicz int status; 741*00a2430fSAndrzej Pietrasiewicz dev_t dev; 742*00a2430fSAndrzej Pietrasiewicz 743*00a2430fSAndrzej Pietrasiewicz hidg_class = class_create(THIS_MODULE, "hidg"); 744*00a2430fSAndrzej Pietrasiewicz 745*00a2430fSAndrzej Pietrasiewicz status = alloc_chrdev_region(&dev, 0, count, "hidg"); 746*00a2430fSAndrzej Pietrasiewicz if (!status) { 747*00a2430fSAndrzej Pietrasiewicz major = MAJOR(dev); 748*00a2430fSAndrzej Pietrasiewicz minors = count; 749*00a2430fSAndrzej Pietrasiewicz } 750*00a2430fSAndrzej Pietrasiewicz 751*00a2430fSAndrzej Pietrasiewicz return status; 752*00a2430fSAndrzej Pietrasiewicz } 753*00a2430fSAndrzej Pietrasiewicz 754*00a2430fSAndrzej Pietrasiewicz void ghid_cleanup(void) 755*00a2430fSAndrzej Pietrasiewicz { 756*00a2430fSAndrzej Pietrasiewicz if (major) { 757*00a2430fSAndrzej Pietrasiewicz unregister_chrdev_region(MKDEV(major, 0), minors); 758*00a2430fSAndrzej Pietrasiewicz major = minors = 0; 759*00a2430fSAndrzej Pietrasiewicz } 760*00a2430fSAndrzej Pietrasiewicz 761*00a2430fSAndrzej Pietrasiewicz class_destroy(hidg_class); 762*00a2430fSAndrzej Pietrasiewicz hidg_class = NULL; 763*00a2430fSAndrzej Pietrasiewicz } 764