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> 15*cb382536SAndrzej Pietrasiewicz #include <linux/idr.h> 1600a2430fSAndrzej Pietrasiewicz #include <linux/cdev.h> 1700a2430fSAndrzej Pietrasiewicz #include <linux/mutex.h> 1800a2430fSAndrzej Pietrasiewicz #include <linux/poll.h> 1900a2430fSAndrzej Pietrasiewicz #include <linux/uaccess.h> 2000a2430fSAndrzej Pietrasiewicz #include <linux/wait.h> 2100a2430fSAndrzej Pietrasiewicz #include <linux/sched.h> 2200a2430fSAndrzej Pietrasiewicz #include <linux/usb/g_hid.h> 2300a2430fSAndrzej Pietrasiewicz 2400a2430fSAndrzej Pietrasiewicz #include "u_f.h" 25*cb382536SAndrzej Pietrasiewicz #include "u_hid.h" 26*cb382536SAndrzej Pietrasiewicz 27*cb382536SAndrzej Pietrasiewicz #define HIDG_MINORS 4 2800a2430fSAndrzej Pietrasiewicz 2900a2430fSAndrzej Pietrasiewicz static int major, minors; 3000a2430fSAndrzej Pietrasiewicz static struct class *hidg_class; 31*cb382536SAndrzej Pietrasiewicz #ifndef USBF_HID_INCLUDED 32*cb382536SAndrzej Pietrasiewicz static DEFINE_IDA(hidg_ida); 33*cb382536SAndrzej Pietrasiewicz static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */ 34*cb382536SAndrzej Pietrasiewicz #endif 3500a2430fSAndrzej Pietrasiewicz 3600a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 3700a2430fSAndrzej Pietrasiewicz /* HID gadget struct */ 3800a2430fSAndrzej Pietrasiewicz 3900a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list { 4000a2430fSAndrzej Pietrasiewicz struct usb_request *req; 4100a2430fSAndrzej Pietrasiewicz unsigned int pos; 4200a2430fSAndrzej Pietrasiewicz struct list_head list; 4300a2430fSAndrzej Pietrasiewicz }; 4400a2430fSAndrzej Pietrasiewicz 4500a2430fSAndrzej Pietrasiewicz struct f_hidg { 4600a2430fSAndrzej Pietrasiewicz /* configuration */ 4700a2430fSAndrzej Pietrasiewicz unsigned char bInterfaceSubClass; 4800a2430fSAndrzej Pietrasiewicz unsigned char bInterfaceProtocol; 4900a2430fSAndrzej Pietrasiewicz unsigned short report_desc_length; 5000a2430fSAndrzej Pietrasiewicz char *report_desc; 5100a2430fSAndrzej Pietrasiewicz unsigned short report_length; 5200a2430fSAndrzej Pietrasiewicz 5300a2430fSAndrzej Pietrasiewicz /* recv report */ 5400a2430fSAndrzej Pietrasiewicz struct list_head completed_out_req; 5500a2430fSAndrzej Pietrasiewicz spinlock_t spinlock; 5600a2430fSAndrzej Pietrasiewicz wait_queue_head_t read_queue; 5700a2430fSAndrzej Pietrasiewicz unsigned int qlen; 5800a2430fSAndrzej Pietrasiewicz 5900a2430fSAndrzej Pietrasiewicz /* send report */ 6000a2430fSAndrzej Pietrasiewicz struct mutex lock; 6100a2430fSAndrzej Pietrasiewicz bool write_pending; 6200a2430fSAndrzej Pietrasiewicz wait_queue_head_t write_queue; 6300a2430fSAndrzej Pietrasiewicz struct usb_request *req; 6400a2430fSAndrzej Pietrasiewicz 6500a2430fSAndrzej Pietrasiewicz int minor; 6600a2430fSAndrzej Pietrasiewicz struct cdev cdev; 6700a2430fSAndrzej Pietrasiewicz struct usb_function func; 6800a2430fSAndrzej Pietrasiewicz 6900a2430fSAndrzej Pietrasiewicz struct usb_ep *in_ep; 7000a2430fSAndrzej Pietrasiewicz struct usb_ep *out_ep; 7100a2430fSAndrzej Pietrasiewicz }; 7200a2430fSAndrzej Pietrasiewicz 7300a2430fSAndrzej Pietrasiewicz static inline struct f_hidg *func_to_hidg(struct usb_function *f) 7400a2430fSAndrzej Pietrasiewicz { 7500a2430fSAndrzej Pietrasiewicz return container_of(f, struct f_hidg, func); 7600a2430fSAndrzej Pietrasiewicz } 7700a2430fSAndrzej Pietrasiewicz 7800a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 7900a2430fSAndrzej Pietrasiewicz /* Static descriptors */ 8000a2430fSAndrzej Pietrasiewicz 8100a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor hidg_interface_desc = { 8200a2430fSAndrzej Pietrasiewicz .bLength = sizeof hidg_interface_desc, 8300a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE, 8400a2430fSAndrzej Pietrasiewicz /* .bInterfaceNumber = DYNAMIC */ 8500a2430fSAndrzej Pietrasiewicz .bAlternateSetting = 0, 8600a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 2, 8700a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_HID, 8800a2430fSAndrzej Pietrasiewicz /* .bInterfaceSubClass = DYNAMIC */ 8900a2430fSAndrzej Pietrasiewicz /* .bInterfaceProtocol = DYNAMIC */ 9000a2430fSAndrzej Pietrasiewicz /* .iInterface = DYNAMIC */ 9100a2430fSAndrzej Pietrasiewicz }; 9200a2430fSAndrzej Pietrasiewicz 9300a2430fSAndrzej Pietrasiewicz static struct hid_descriptor hidg_desc = { 9400a2430fSAndrzej Pietrasiewicz .bLength = sizeof hidg_desc, 9500a2430fSAndrzej Pietrasiewicz .bDescriptorType = HID_DT_HID, 9600a2430fSAndrzej Pietrasiewicz .bcdHID = 0x0101, 9700a2430fSAndrzej Pietrasiewicz .bCountryCode = 0x00, 9800a2430fSAndrzej Pietrasiewicz .bNumDescriptors = 0x1, 9900a2430fSAndrzej Pietrasiewicz /*.desc[0].bDescriptorType = DYNAMIC */ 10000a2430fSAndrzej Pietrasiewicz /*.desc[0].wDescriptorLenght = DYNAMIC */ 10100a2430fSAndrzej Pietrasiewicz }; 10200a2430fSAndrzej Pietrasiewicz 10300a2430fSAndrzej Pietrasiewicz /* High-Speed Support */ 10400a2430fSAndrzej Pietrasiewicz 10500a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { 10600a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 10700a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 10800a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 10900a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 11000a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 11100a2430fSAndrzej Pietrasiewicz .bInterval = 4, /* FIXME: Add this field in the 11200a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 11300a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 11400a2430fSAndrzej Pietrasiewicz */ 11500a2430fSAndrzej Pietrasiewicz }; 11600a2430fSAndrzej Pietrasiewicz 11700a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { 11800a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 11900a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 12000a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT, 12100a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 12200a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 12300a2430fSAndrzej Pietrasiewicz .bInterval = 4, /* FIXME: Add this field in the 12400a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 12500a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 12600a2430fSAndrzej Pietrasiewicz */ 12700a2430fSAndrzej Pietrasiewicz }; 12800a2430fSAndrzej Pietrasiewicz 12900a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hidg_hs_descriptors[] = { 13000a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_interface_desc, 13100a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_desc, 13200a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, 13300a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_hs_out_ep_desc, 13400a2430fSAndrzej Pietrasiewicz NULL, 13500a2430fSAndrzej Pietrasiewicz }; 13600a2430fSAndrzej Pietrasiewicz 13700a2430fSAndrzej Pietrasiewicz /* Full-Speed Support */ 13800a2430fSAndrzej Pietrasiewicz 13900a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { 14000a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 14100a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 14200a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 14300a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 14400a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 14500a2430fSAndrzej Pietrasiewicz .bInterval = 10, /* FIXME: Add this field in the 14600a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 14700a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 14800a2430fSAndrzej Pietrasiewicz */ 14900a2430fSAndrzej Pietrasiewicz }; 15000a2430fSAndrzej Pietrasiewicz 15100a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { 15200a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 15300a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 15400a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT, 15500a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 15600a2430fSAndrzej Pietrasiewicz /*.wMaxPacketSize = DYNAMIC */ 15700a2430fSAndrzej Pietrasiewicz .bInterval = 10, /* FIXME: Add this field in the 15800a2430fSAndrzej Pietrasiewicz * HID gadget configuration? 15900a2430fSAndrzej Pietrasiewicz * (struct hidg_func_descriptor) 16000a2430fSAndrzej Pietrasiewicz */ 16100a2430fSAndrzej Pietrasiewicz }; 16200a2430fSAndrzej Pietrasiewicz 16300a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hidg_fs_descriptors[] = { 16400a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_interface_desc, 16500a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_desc, 16600a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, 16700a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *)&hidg_fs_out_ep_desc, 16800a2430fSAndrzej Pietrasiewicz NULL, 16900a2430fSAndrzej Pietrasiewicz }; 17000a2430fSAndrzej Pietrasiewicz 17100a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 172*cb382536SAndrzej Pietrasiewicz /* Strings */ 173*cb382536SAndrzej Pietrasiewicz 174*cb382536SAndrzej Pietrasiewicz #define CT_FUNC_HID_IDX 0 175*cb382536SAndrzej Pietrasiewicz 176*cb382536SAndrzej Pietrasiewicz static struct usb_string ct_func_string_defs[] = { 177*cb382536SAndrzej Pietrasiewicz [CT_FUNC_HID_IDX].s = "HID Interface", 178*cb382536SAndrzej Pietrasiewicz {}, /* end of list */ 179*cb382536SAndrzej Pietrasiewicz }; 180*cb382536SAndrzej Pietrasiewicz 181*cb382536SAndrzej Pietrasiewicz static struct usb_gadget_strings ct_func_string_table = { 182*cb382536SAndrzej Pietrasiewicz .language = 0x0409, /* en-US */ 183*cb382536SAndrzej Pietrasiewicz .strings = ct_func_string_defs, 184*cb382536SAndrzej Pietrasiewicz }; 185*cb382536SAndrzej Pietrasiewicz 186*cb382536SAndrzej Pietrasiewicz static struct usb_gadget_strings *ct_func_strings[] = { 187*cb382536SAndrzej Pietrasiewicz &ct_func_string_table, 188*cb382536SAndrzej Pietrasiewicz NULL, 189*cb382536SAndrzej Pietrasiewicz }; 190*cb382536SAndrzej Pietrasiewicz 191*cb382536SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 19200a2430fSAndrzej Pietrasiewicz /* Char Device */ 19300a2430fSAndrzej Pietrasiewicz 19400a2430fSAndrzej Pietrasiewicz static ssize_t f_hidg_read(struct file *file, char __user *buffer, 19500a2430fSAndrzej Pietrasiewicz size_t count, loff_t *ptr) 19600a2430fSAndrzej Pietrasiewicz { 19700a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = file->private_data; 19800a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list *list; 19900a2430fSAndrzej Pietrasiewicz struct usb_request *req; 20000a2430fSAndrzej Pietrasiewicz unsigned long flags; 20100a2430fSAndrzej Pietrasiewicz int ret; 20200a2430fSAndrzej Pietrasiewicz 20300a2430fSAndrzej Pietrasiewicz if (!count) 20400a2430fSAndrzej Pietrasiewicz return 0; 20500a2430fSAndrzej Pietrasiewicz 20600a2430fSAndrzej Pietrasiewicz if (!access_ok(VERIFY_WRITE, buffer, count)) 20700a2430fSAndrzej Pietrasiewicz return -EFAULT; 20800a2430fSAndrzej Pietrasiewicz 20900a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 21000a2430fSAndrzej Pietrasiewicz 21100a2430fSAndrzej Pietrasiewicz #define READ_COND (!list_empty(&hidg->completed_out_req)) 21200a2430fSAndrzej Pietrasiewicz 21300a2430fSAndrzej Pietrasiewicz /* wait for at least one buffer to complete */ 21400a2430fSAndrzej Pietrasiewicz while (!READ_COND) { 21500a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 21600a2430fSAndrzej Pietrasiewicz if (file->f_flags & O_NONBLOCK) 21700a2430fSAndrzej Pietrasiewicz return -EAGAIN; 21800a2430fSAndrzej Pietrasiewicz 21900a2430fSAndrzej Pietrasiewicz if (wait_event_interruptible(hidg->read_queue, READ_COND)) 22000a2430fSAndrzej Pietrasiewicz return -ERESTARTSYS; 22100a2430fSAndrzej Pietrasiewicz 22200a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 22300a2430fSAndrzej Pietrasiewicz } 22400a2430fSAndrzej Pietrasiewicz 22500a2430fSAndrzej Pietrasiewicz /* pick the first one */ 22600a2430fSAndrzej Pietrasiewicz list = list_first_entry(&hidg->completed_out_req, 22700a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list, list); 22800a2430fSAndrzej Pietrasiewicz req = list->req; 22900a2430fSAndrzej Pietrasiewicz count = min_t(unsigned int, count, req->actual - list->pos); 23000a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 23100a2430fSAndrzej Pietrasiewicz 23200a2430fSAndrzej Pietrasiewicz /* copy to user outside spinlock */ 23300a2430fSAndrzej Pietrasiewicz count -= copy_to_user(buffer, req->buf + list->pos, count); 23400a2430fSAndrzej Pietrasiewicz list->pos += count; 23500a2430fSAndrzej Pietrasiewicz 23600a2430fSAndrzej Pietrasiewicz /* 23700a2430fSAndrzej Pietrasiewicz * if this request is completely handled and transfered to 23800a2430fSAndrzej Pietrasiewicz * userspace, remove its entry from the list and requeue it 23900a2430fSAndrzej Pietrasiewicz * again. Otherwise, we will revisit it again upon the next 24000a2430fSAndrzej Pietrasiewicz * call, taking into account its current read position. 24100a2430fSAndrzej Pietrasiewicz */ 24200a2430fSAndrzej Pietrasiewicz if (list->pos == req->actual) { 24300a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 24400a2430fSAndrzej Pietrasiewicz list_del(&list->list); 24500a2430fSAndrzej Pietrasiewicz kfree(list); 24600a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 24700a2430fSAndrzej Pietrasiewicz 24800a2430fSAndrzej Pietrasiewicz req->length = hidg->report_length; 24900a2430fSAndrzej Pietrasiewicz ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL); 25000a2430fSAndrzej Pietrasiewicz if (ret < 0) 25100a2430fSAndrzej Pietrasiewicz return ret; 25200a2430fSAndrzej Pietrasiewicz } 25300a2430fSAndrzej Pietrasiewicz 25400a2430fSAndrzej Pietrasiewicz return count; 25500a2430fSAndrzej Pietrasiewicz } 25600a2430fSAndrzej Pietrasiewicz 25700a2430fSAndrzej Pietrasiewicz static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req) 25800a2430fSAndrzej Pietrasiewicz { 25900a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; 26000a2430fSAndrzej Pietrasiewicz 26100a2430fSAndrzej Pietrasiewicz if (req->status != 0) { 26200a2430fSAndrzej Pietrasiewicz ERROR(hidg->func.config->cdev, 26300a2430fSAndrzej Pietrasiewicz "End Point Request ERROR: %d\n", req->status); 26400a2430fSAndrzej Pietrasiewicz } 26500a2430fSAndrzej Pietrasiewicz 26600a2430fSAndrzej Pietrasiewicz hidg->write_pending = 0; 26700a2430fSAndrzej Pietrasiewicz wake_up(&hidg->write_queue); 26800a2430fSAndrzej Pietrasiewicz } 26900a2430fSAndrzej Pietrasiewicz 27000a2430fSAndrzej Pietrasiewicz static ssize_t f_hidg_write(struct file *file, const char __user *buffer, 27100a2430fSAndrzej Pietrasiewicz size_t count, loff_t *offp) 27200a2430fSAndrzej Pietrasiewicz { 27300a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = file->private_data; 27400a2430fSAndrzej Pietrasiewicz ssize_t status = -ENOMEM; 27500a2430fSAndrzej Pietrasiewicz 27600a2430fSAndrzej Pietrasiewicz if (!access_ok(VERIFY_READ, buffer, count)) 27700a2430fSAndrzej Pietrasiewicz return -EFAULT; 27800a2430fSAndrzej Pietrasiewicz 27900a2430fSAndrzej Pietrasiewicz mutex_lock(&hidg->lock); 28000a2430fSAndrzej Pietrasiewicz 28100a2430fSAndrzej Pietrasiewicz #define WRITE_COND (!hidg->write_pending) 28200a2430fSAndrzej Pietrasiewicz 28300a2430fSAndrzej Pietrasiewicz /* write queue */ 28400a2430fSAndrzej Pietrasiewicz while (!WRITE_COND) { 28500a2430fSAndrzej Pietrasiewicz mutex_unlock(&hidg->lock); 28600a2430fSAndrzej Pietrasiewicz if (file->f_flags & O_NONBLOCK) 28700a2430fSAndrzej Pietrasiewicz return -EAGAIN; 28800a2430fSAndrzej Pietrasiewicz 28900a2430fSAndrzej Pietrasiewicz if (wait_event_interruptible_exclusive( 29000a2430fSAndrzej Pietrasiewicz hidg->write_queue, WRITE_COND)) 29100a2430fSAndrzej Pietrasiewicz return -ERESTARTSYS; 29200a2430fSAndrzej Pietrasiewicz 29300a2430fSAndrzej Pietrasiewicz mutex_lock(&hidg->lock); 29400a2430fSAndrzej Pietrasiewicz } 29500a2430fSAndrzej Pietrasiewicz 29600a2430fSAndrzej Pietrasiewicz count = min_t(unsigned, count, hidg->report_length); 29700a2430fSAndrzej Pietrasiewicz status = copy_from_user(hidg->req->buf, buffer, count); 29800a2430fSAndrzej Pietrasiewicz 29900a2430fSAndrzej Pietrasiewicz if (status != 0) { 30000a2430fSAndrzej Pietrasiewicz ERROR(hidg->func.config->cdev, 30100a2430fSAndrzej Pietrasiewicz "copy_from_user error\n"); 30200a2430fSAndrzej Pietrasiewicz mutex_unlock(&hidg->lock); 30300a2430fSAndrzej Pietrasiewicz return -EINVAL; 30400a2430fSAndrzej Pietrasiewicz } 30500a2430fSAndrzej Pietrasiewicz 30600a2430fSAndrzej Pietrasiewicz hidg->req->status = 0; 30700a2430fSAndrzej Pietrasiewicz hidg->req->zero = 0; 30800a2430fSAndrzej Pietrasiewicz hidg->req->length = count; 30900a2430fSAndrzej Pietrasiewicz hidg->req->complete = f_hidg_req_complete; 31000a2430fSAndrzej Pietrasiewicz hidg->req->context = hidg; 31100a2430fSAndrzej Pietrasiewicz hidg->write_pending = 1; 31200a2430fSAndrzej Pietrasiewicz 31300a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC); 31400a2430fSAndrzej Pietrasiewicz if (status < 0) { 31500a2430fSAndrzej Pietrasiewicz ERROR(hidg->func.config->cdev, 31600a2430fSAndrzej Pietrasiewicz "usb_ep_queue error on int endpoint %zd\n", status); 31700a2430fSAndrzej Pietrasiewicz hidg->write_pending = 0; 31800a2430fSAndrzej Pietrasiewicz wake_up(&hidg->write_queue); 31900a2430fSAndrzej Pietrasiewicz } else { 32000a2430fSAndrzej Pietrasiewicz status = count; 32100a2430fSAndrzej Pietrasiewicz } 32200a2430fSAndrzej Pietrasiewicz 32300a2430fSAndrzej Pietrasiewicz mutex_unlock(&hidg->lock); 32400a2430fSAndrzej Pietrasiewicz 32500a2430fSAndrzej Pietrasiewicz return status; 32600a2430fSAndrzej Pietrasiewicz } 32700a2430fSAndrzej Pietrasiewicz 32800a2430fSAndrzej Pietrasiewicz static unsigned int f_hidg_poll(struct file *file, poll_table *wait) 32900a2430fSAndrzej Pietrasiewicz { 33000a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = file->private_data; 33100a2430fSAndrzej Pietrasiewicz unsigned int ret = 0; 33200a2430fSAndrzej Pietrasiewicz 33300a2430fSAndrzej Pietrasiewicz poll_wait(file, &hidg->read_queue, wait); 33400a2430fSAndrzej Pietrasiewicz poll_wait(file, &hidg->write_queue, wait); 33500a2430fSAndrzej Pietrasiewicz 33600a2430fSAndrzej Pietrasiewicz if (WRITE_COND) 33700a2430fSAndrzej Pietrasiewicz ret |= POLLOUT | POLLWRNORM; 33800a2430fSAndrzej Pietrasiewicz 33900a2430fSAndrzej Pietrasiewicz if (READ_COND) 34000a2430fSAndrzej Pietrasiewicz ret |= POLLIN | POLLRDNORM; 34100a2430fSAndrzej Pietrasiewicz 34200a2430fSAndrzej Pietrasiewicz return ret; 34300a2430fSAndrzej Pietrasiewicz } 34400a2430fSAndrzej Pietrasiewicz 34500a2430fSAndrzej Pietrasiewicz #undef WRITE_COND 34600a2430fSAndrzej Pietrasiewicz #undef READ_COND 34700a2430fSAndrzej Pietrasiewicz 34800a2430fSAndrzej Pietrasiewicz static int f_hidg_release(struct inode *inode, struct file *fd) 34900a2430fSAndrzej Pietrasiewicz { 35000a2430fSAndrzej Pietrasiewicz fd->private_data = NULL; 35100a2430fSAndrzej Pietrasiewicz return 0; 35200a2430fSAndrzej Pietrasiewicz } 35300a2430fSAndrzej Pietrasiewicz 35400a2430fSAndrzej Pietrasiewicz static int f_hidg_open(struct inode *inode, struct file *fd) 35500a2430fSAndrzej Pietrasiewicz { 35600a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = 35700a2430fSAndrzej Pietrasiewicz container_of(inode->i_cdev, struct f_hidg, cdev); 35800a2430fSAndrzej Pietrasiewicz 35900a2430fSAndrzej Pietrasiewicz fd->private_data = hidg; 36000a2430fSAndrzej Pietrasiewicz 36100a2430fSAndrzej Pietrasiewicz return 0; 36200a2430fSAndrzej Pietrasiewicz } 36300a2430fSAndrzej Pietrasiewicz 36400a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 36500a2430fSAndrzej Pietrasiewicz /* usb_function */ 36600a2430fSAndrzej Pietrasiewicz 36700a2430fSAndrzej Pietrasiewicz static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, 36800a2430fSAndrzej Pietrasiewicz unsigned length) 36900a2430fSAndrzej Pietrasiewicz { 37000a2430fSAndrzej Pietrasiewicz return alloc_ep_req(ep, length, length); 37100a2430fSAndrzej Pietrasiewicz } 37200a2430fSAndrzej Pietrasiewicz 37300a2430fSAndrzej Pietrasiewicz static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) 37400a2430fSAndrzej Pietrasiewicz { 37500a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = (struct f_hidg *) req->context; 37600a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list *req_list; 37700a2430fSAndrzej Pietrasiewicz unsigned long flags; 37800a2430fSAndrzej Pietrasiewicz 37900a2430fSAndrzej Pietrasiewicz req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC); 38000a2430fSAndrzej Pietrasiewicz if (!req_list) 38100a2430fSAndrzej Pietrasiewicz return; 38200a2430fSAndrzej Pietrasiewicz 38300a2430fSAndrzej Pietrasiewicz req_list->req = req; 38400a2430fSAndrzej Pietrasiewicz 38500a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&hidg->spinlock, flags); 38600a2430fSAndrzej Pietrasiewicz list_add_tail(&req_list->list, &hidg->completed_out_req); 38700a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&hidg->spinlock, flags); 38800a2430fSAndrzej Pietrasiewicz 38900a2430fSAndrzej Pietrasiewicz wake_up(&hidg->read_queue); 39000a2430fSAndrzej Pietrasiewicz } 39100a2430fSAndrzej Pietrasiewicz 39200a2430fSAndrzej Pietrasiewicz static int hidg_setup(struct usb_function *f, 39300a2430fSAndrzej Pietrasiewicz const struct usb_ctrlrequest *ctrl) 39400a2430fSAndrzej Pietrasiewicz { 39500a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 39600a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev; 39700a2430fSAndrzej Pietrasiewicz struct usb_request *req = cdev->req; 39800a2430fSAndrzej Pietrasiewicz int status = 0; 39900a2430fSAndrzej Pietrasiewicz __u16 value, length; 40000a2430fSAndrzej Pietrasiewicz 40100a2430fSAndrzej Pietrasiewicz value = __le16_to_cpu(ctrl->wValue); 40200a2430fSAndrzej Pietrasiewicz length = __le16_to_cpu(ctrl->wLength); 40300a2430fSAndrzej Pietrasiewicz 40400a2430fSAndrzej Pietrasiewicz VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x " 40500a2430fSAndrzej Pietrasiewicz "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value); 40600a2430fSAndrzej Pietrasiewicz 40700a2430fSAndrzej Pietrasiewicz switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { 40800a2430fSAndrzej Pietrasiewicz case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 40900a2430fSAndrzej Pietrasiewicz | HID_REQ_GET_REPORT): 41000a2430fSAndrzej Pietrasiewicz VDBG(cdev, "get_report\n"); 41100a2430fSAndrzej Pietrasiewicz 41200a2430fSAndrzej Pietrasiewicz /* send an empty report */ 41300a2430fSAndrzej Pietrasiewicz length = min_t(unsigned, length, hidg->report_length); 41400a2430fSAndrzej Pietrasiewicz memset(req->buf, 0x0, length); 41500a2430fSAndrzej Pietrasiewicz 41600a2430fSAndrzej Pietrasiewicz goto respond; 41700a2430fSAndrzej Pietrasiewicz break; 41800a2430fSAndrzej Pietrasiewicz 41900a2430fSAndrzej Pietrasiewicz case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 42000a2430fSAndrzej Pietrasiewicz | HID_REQ_GET_PROTOCOL): 42100a2430fSAndrzej Pietrasiewicz VDBG(cdev, "get_protocol\n"); 42200a2430fSAndrzej Pietrasiewicz goto stall; 42300a2430fSAndrzej Pietrasiewicz break; 42400a2430fSAndrzej Pietrasiewicz 42500a2430fSAndrzej Pietrasiewicz case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 42600a2430fSAndrzej Pietrasiewicz | HID_REQ_SET_REPORT): 42700a2430fSAndrzej Pietrasiewicz VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength); 42800a2430fSAndrzej Pietrasiewicz goto stall; 42900a2430fSAndrzej Pietrasiewicz break; 43000a2430fSAndrzej Pietrasiewicz 43100a2430fSAndrzej Pietrasiewicz case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 43200a2430fSAndrzej Pietrasiewicz | HID_REQ_SET_PROTOCOL): 43300a2430fSAndrzej Pietrasiewicz VDBG(cdev, "set_protocol\n"); 43400a2430fSAndrzej Pietrasiewicz goto stall; 43500a2430fSAndrzej Pietrasiewicz break; 43600a2430fSAndrzej Pietrasiewicz 43700a2430fSAndrzej Pietrasiewicz case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8 43800a2430fSAndrzej Pietrasiewicz | USB_REQ_GET_DESCRIPTOR): 43900a2430fSAndrzej Pietrasiewicz switch (value >> 8) { 44000a2430fSAndrzej Pietrasiewicz case HID_DT_HID: 44100a2430fSAndrzej Pietrasiewicz VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n"); 44200a2430fSAndrzej Pietrasiewicz length = min_t(unsigned short, length, 44300a2430fSAndrzej Pietrasiewicz hidg_desc.bLength); 44400a2430fSAndrzej Pietrasiewicz memcpy(req->buf, &hidg_desc, length); 44500a2430fSAndrzej Pietrasiewicz goto respond; 44600a2430fSAndrzej Pietrasiewicz break; 44700a2430fSAndrzej Pietrasiewicz case HID_DT_REPORT: 44800a2430fSAndrzej Pietrasiewicz VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n"); 44900a2430fSAndrzej Pietrasiewicz length = min_t(unsigned short, length, 45000a2430fSAndrzej Pietrasiewicz hidg->report_desc_length); 45100a2430fSAndrzej Pietrasiewicz memcpy(req->buf, hidg->report_desc, length); 45200a2430fSAndrzej Pietrasiewicz goto respond; 45300a2430fSAndrzej Pietrasiewicz break; 45400a2430fSAndrzej Pietrasiewicz 45500a2430fSAndrzej Pietrasiewicz default: 45600a2430fSAndrzej Pietrasiewicz VDBG(cdev, "Unknown descriptor request 0x%x\n", 45700a2430fSAndrzej Pietrasiewicz value >> 8); 45800a2430fSAndrzej Pietrasiewicz goto stall; 45900a2430fSAndrzej Pietrasiewicz break; 46000a2430fSAndrzej Pietrasiewicz } 46100a2430fSAndrzej Pietrasiewicz break; 46200a2430fSAndrzej Pietrasiewicz 46300a2430fSAndrzej Pietrasiewicz default: 46400a2430fSAndrzej Pietrasiewicz VDBG(cdev, "Unknown request 0x%x\n", 46500a2430fSAndrzej Pietrasiewicz ctrl->bRequest); 46600a2430fSAndrzej Pietrasiewicz goto stall; 46700a2430fSAndrzej Pietrasiewicz break; 46800a2430fSAndrzej Pietrasiewicz } 46900a2430fSAndrzej Pietrasiewicz 47000a2430fSAndrzej Pietrasiewicz stall: 47100a2430fSAndrzej Pietrasiewicz return -EOPNOTSUPP; 47200a2430fSAndrzej Pietrasiewicz 47300a2430fSAndrzej Pietrasiewicz respond: 47400a2430fSAndrzej Pietrasiewicz req->zero = 0; 47500a2430fSAndrzej Pietrasiewicz req->length = length; 47600a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); 47700a2430fSAndrzej Pietrasiewicz if (status < 0) 47800a2430fSAndrzej Pietrasiewicz ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value); 47900a2430fSAndrzej Pietrasiewicz return status; 48000a2430fSAndrzej Pietrasiewicz } 48100a2430fSAndrzej Pietrasiewicz 48200a2430fSAndrzej Pietrasiewicz static void hidg_disable(struct usb_function *f) 48300a2430fSAndrzej Pietrasiewicz { 48400a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 48500a2430fSAndrzej Pietrasiewicz struct f_hidg_req_list *list, *next; 48600a2430fSAndrzej Pietrasiewicz 48700a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->in_ep); 48800a2430fSAndrzej Pietrasiewicz hidg->in_ep->driver_data = NULL; 48900a2430fSAndrzej Pietrasiewicz 49000a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->out_ep); 49100a2430fSAndrzej Pietrasiewicz hidg->out_ep->driver_data = NULL; 49200a2430fSAndrzej Pietrasiewicz 49300a2430fSAndrzej Pietrasiewicz list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { 49400a2430fSAndrzej Pietrasiewicz list_del(&list->list); 49500a2430fSAndrzej Pietrasiewicz kfree(list); 49600a2430fSAndrzej Pietrasiewicz } 49700a2430fSAndrzej Pietrasiewicz } 49800a2430fSAndrzej Pietrasiewicz 49900a2430fSAndrzej Pietrasiewicz static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 50000a2430fSAndrzej Pietrasiewicz { 50100a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev; 50200a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 50300a2430fSAndrzej Pietrasiewicz int i, status = 0; 50400a2430fSAndrzej Pietrasiewicz 50500a2430fSAndrzej Pietrasiewicz VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); 50600a2430fSAndrzej Pietrasiewicz 50700a2430fSAndrzej Pietrasiewicz if (hidg->in_ep != NULL) { 50800a2430fSAndrzej Pietrasiewicz /* restart endpoint */ 50900a2430fSAndrzej Pietrasiewicz if (hidg->in_ep->driver_data != NULL) 51000a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->in_ep); 51100a2430fSAndrzej Pietrasiewicz 51200a2430fSAndrzej Pietrasiewicz status = config_ep_by_speed(f->config->cdev->gadget, f, 51300a2430fSAndrzej Pietrasiewicz hidg->in_ep); 51400a2430fSAndrzej Pietrasiewicz if (status) { 51500a2430fSAndrzej Pietrasiewicz ERROR(cdev, "config_ep_by_speed FAILED!\n"); 51600a2430fSAndrzej Pietrasiewicz goto fail; 51700a2430fSAndrzej Pietrasiewicz } 51800a2430fSAndrzej Pietrasiewicz status = usb_ep_enable(hidg->in_ep); 51900a2430fSAndrzej Pietrasiewicz if (status < 0) { 52000a2430fSAndrzej Pietrasiewicz ERROR(cdev, "Enable IN endpoint FAILED!\n"); 52100a2430fSAndrzej Pietrasiewicz goto fail; 52200a2430fSAndrzej Pietrasiewicz } 52300a2430fSAndrzej Pietrasiewicz hidg->in_ep->driver_data = hidg; 52400a2430fSAndrzej Pietrasiewicz } 52500a2430fSAndrzej Pietrasiewicz 52600a2430fSAndrzej Pietrasiewicz 52700a2430fSAndrzej Pietrasiewicz if (hidg->out_ep != NULL) { 52800a2430fSAndrzej Pietrasiewicz /* restart endpoint */ 52900a2430fSAndrzej Pietrasiewicz if (hidg->out_ep->driver_data != NULL) 53000a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->out_ep); 53100a2430fSAndrzej Pietrasiewicz 53200a2430fSAndrzej Pietrasiewicz status = config_ep_by_speed(f->config->cdev->gadget, f, 53300a2430fSAndrzej Pietrasiewicz hidg->out_ep); 53400a2430fSAndrzej Pietrasiewicz if (status) { 53500a2430fSAndrzej Pietrasiewicz ERROR(cdev, "config_ep_by_speed FAILED!\n"); 53600a2430fSAndrzej Pietrasiewicz goto fail; 53700a2430fSAndrzej Pietrasiewicz } 53800a2430fSAndrzej Pietrasiewicz status = usb_ep_enable(hidg->out_ep); 53900a2430fSAndrzej Pietrasiewicz if (status < 0) { 54000a2430fSAndrzej Pietrasiewicz ERROR(cdev, "Enable IN endpoint FAILED!\n"); 54100a2430fSAndrzej Pietrasiewicz goto fail; 54200a2430fSAndrzej Pietrasiewicz } 54300a2430fSAndrzej Pietrasiewicz hidg->out_ep->driver_data = hidg; 54400a2430fSAndrzej Pietrasiewicz 54500a2430fSAndrzej Pietrasiewicz /* 54600a2430fSAndrzej Pietrasiewicz * allocate a bunch of read buffers and queue them all at once. 54700a2430fSAndrzej Pietrasiewicz */ 54800a2430fSAndrzej Pietrasiewicz for (i = 0; i < hidg->qlen && status == 0; i++) { 54900a2430fSAndrzej Pietrasiewicz struct usb_request *req = 55000a2430fSAndrzej Pietrasiewicz hidg_alloc_ep_req(hidg->out_ep, 55100a2430fSAndrzej Pietrasiewicz hidg->report_length); 55200a2430fSAndrzej Pietrasiewicz if (req) { 55300a2430fSAndrzej Pietrasiewicz req->complete = hidg_set_report_complete; 55400a2430fSAndrzej Pietrasiewicz req->context = hidg; 55500a2430fSAndrzej Pietrasiewicz status = usb_ep_queue(hidg->out_ep, req, 55600a2430fSAndrzej Pietrasiewicz GFP_ATOMIC); 55700a2430fSAndrzej Pietrasiewicz if (status) 55800a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s queue req --> %d\n", 55900a2430fSAndrzej Pietrasiewicz hidg->out_ep->name, status); 56000a2430fSAndrzej Pietrasiewicz } else { 56100a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->out_ep); 56200a2430fSAndrzej Pietrasiewicz hidg->out_ep->driver_data = NULL; 56300a2430fSAndrzej Pietrasiewicz status = -ENOMEM; 56400a2430fSAndrzej Pietrasiewicz goto fail; 56500a2430fSAndrzej Pietrasiewicz } 56600a2430fSAndrzej Pietrasiewicz } 56700a2430fSAndrzej Pietrasiewicz } 56800a2430fSAndrzej Pietrasiewicz 56900a2430fSAndrzej Pietrasiewicz fail: 57000a2430fSAndrzej Pietrasiewicz return status; 57100a2430fSAndrzej Pietrasiewicz } 57200a2430fSAndrzej Pietrasiewicz 57300a2430fSAndrzej Pietrasiewicz const struct file_operations f_hidg_fops = { 57400a2430fSAndrzej Pietrasiewicz .owner = THIS_MODULE, 57500a2430fSAndrzej Pietrasiewicz .open = f_hidg_open, 57600a2430fSAndrzej Pietrasiewicz .release = f_hidg_release, 57700a2430fSAndrzej Pietrasiewicz .write = f_hidg_write, 57800a2430fSAndrzej Pietrasiewicz .read = f_hidg_read, 57900a2430fSAndrzej Pietrasiewicz .poll = f_hidg_poll, 58000a2430fSAndrzej Pietrasiewicz .llseek = noop_llseek, 58100a2430fSAndrzej Pietrasiewicz }; 58200a2430fSAndrzej Pietrasiewicz 583*cb382536SAndrzej Pietrasiewicz static int hidg_bind(struct usb_configuration *c, struct usb_function *f) 58400a2430fSAndrzej Pietrasiewicz { 58500a2430fSAndrzej Pietrasiewicz struct usb_ep *ep; 58600a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 58763406087SAndrzej Pietrasiewicz struct device *device; 58800a2430fSAndrzej Pietrasiewicz int status; 58900a2430fSAndrzej Pietrasiewicz dev_t dev; 59000a2430fSAndrzej Pietrasiewicz 591*cb382536SAndrzej Pietrasiewicz /* maybe allocate device-global string IDs, and patch descriptors */ 592*cb382536SAndrzej Pietrasiewicz if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) { 593*cb382536SAndrzej Pietrasiewicz status = usb_string_id(c->cdev); 594*cb382536SAndrzej Pietrasiewicz if (status < 0) 595*cb382536SAndrzej Pietrasiewicz return status; 596*cb382536SAndrzej Pietrasiewicz ct_func_string_defs[CT_FUNC_HID_IDX].id = status; 597*cb382536SAndrzej Pietrasiewicz hidg_interface_desc.iInterface = status; 598*cb382536SAndrzej Pietrasiewicz } 599*cb382536SAndrzej Pietrasiewicz 60000a2430fSAndrzej Pietrasiewicz /* allocate instance-specific interface IDs, and patch descriptors */ 60100a2430fSAndrzej Pietrasiewicz status = usb_interface_id(c, f); 60200a2430fSAndrzej Pietrasiewicz if (status < 0) 60300a2430fSAndrzej Pietrasiewicz goto fail; 60400a2430fSAndrzej Pietrasiewicz hidg_interface_desc.bInterfaceNumber = status; 60500a2430fSAndrzej Pietrasiewicz 60600a2430fSAndrzej Pietrasiewicz /* allocate instance-specific endpoints */ 60700a2430fSAndrzej Pietrasiewicz status = -ENODEV; 60800a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc); 60900a2430fSAndrzej Pietrasiewicz if (!ep) 61000a2430fSAndrzej Pietrasiewicz goto fail; 61100a2430fSAndrzej Pietrasiewicz ep->driver_data = c->cdev; /* claim */ 61200a2430fSAndrzej Pietrasiewicz hidg->in_ep = ep; 61300a2430fSAndrzej Pietrasiewicz 61400a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); 61500a2430fSAndrzej Pietrasiewicz if (!ep) 61600a2430fSAndrzej Pietrasiewicz goto fail; 61700a2430fSAndrzej Pietrasiewicz ep->driver_data = c->cdev; /* claim */ 61800a2430fSAndrzej Pietrasiewicz hidg->out_ep = ep; 61900a2430fSAndrzej Pietrasiewicz 62000a2430fSAndrzej Pietrasiewicz /* preallocate request and buffer */ 62100a2430fSAndrzej Pietrasiewicz status = -ENOMEM; 62200a2430fSAndrzej Pietrasiewicz hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); 62300a2430fSAndrzej Pietrasiewicz if (!hidg->req) 62400a2430fSAndrzej Pietrasiewicz goto fail; 62500a2430fSAndrzej Pietrasiewicz 62600a2430fSAndrzej Pietrasiewicz hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL); 62700a2430fSAndrzej Pietrasiewicz if (!hidg->req->buf) 62800a2430fSAndrzej Pietrasiewicz goto fail; 62900a2430fSAndrzej Pietrasiewicz 63000a2430fSAndrzej Pietrasiewicz /* set descriptor dynamic values */ 63100a2430fSAndrzej Pietrasiewicz hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; 63200a2430fSAndrzej Pietrasiewicz hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; 63300a2430fSAndrzej Pietrasiewicz hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 63400a2430fSAndrzej Pietrasiewicz hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 63500a2430fSAndrzej Pietrasiewicz hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 63600a2430fSAndrzej Pietrasiewicz hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); 63700a2430fSAndrzej Pietrasiewicz hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT; 63800a2430fSAndrzej Pietrasiewicz hidg_desc.desc[0].wDescriptorLength = 63900a2430fSAndrzej Pietrasiewicz cpu_to_le16(hidg->report_desc_length); 64000a2430fSAndrzej Pietrasiewicz 64100a2430fSAndrzej Pietrasiewicz hidg_hs_in_ep_desc.bEndpointAddress = 64200a2430fSAndrzej Pietrasiewicz hidg_fs_in_ep_desc.bEndpointAddress; 64300a2430fSAndrzej Pietrasiewicz hidg_hs_out_ep_desc.bEndpointAddress = 64400a2430fSAndrzej Pietrasiewicz hidg_fs_out_ep_desc.bEndpointAddress; 64500a2430fSAndrzej Pietrasiewicz 64600a2430fSAndrzej Pietrasiewicz status = usb_assign_descriptors(f, hidg_fs_descriptors, 64700a2430fSAndrzej Pietrasiewicz hidg_hs_descriptors, NULL); 64800a2430fSAndrzej Pietrasiewicz if (status) 64900a2430fSAndrzej Pietrasiewicz goto fail; 65000a2430fSAndrzej Pietrasiewicz 65100a2430fSAndrzej Pietrasiewicz mutex_init(&hidg->lock); 65200a2430fSAndrzej Pietrasiewicz spin_lock_init(&hidg->spinlock); 65300a2430fSAndrzej Pietrasiewicz init_waitqueue_head(&hidg->write_queue); 65400a2430fSAndrzej Pietrasiewicz init_waitqueue_head(&hidg->read_queue); 65500a2430fSAndrzej Pietrasiewicz INIT_LIST_HEAD(&hidg->completed_out_req); 65600a2430fSAndrzej Pietrasiewicz 65700a2430fSAndrzej Pietrasiewicz /* create char device */ 65800a2430fSAndrzej Pietrasiewicz cdev_init(&hidg->cdev, &f_hidg_fops); 65900a2430fSAndrzej Pietrasiewicz dev = MKDEV(major, hidg->minor); 66000a2430fSAndrzej Pietrasiewicz status = cdev_add(&hidg->cdev, dev, 1); 66100a2430fSAndrzej Pietrasiewicz if (status) 662d12a8727SPavitrakumar Managutte goto fail_free_descs; 66300a2430fSAndrzej Pietrasiewicz 66463406087SAndrzej Pietrasiewicz device = device_create(hidg_class, NULL, dev, NULL, 66563406087SAndrzej Pietrasiewicz "%s%d", "hidg", hidg->minor); 66663406087SAndrzej Pietrasiewicz if (IS_ERR(device)) { 66763406087SAndrzej Pietrasiewicz status = PTR_ERR(device); 66863406087SAndrzej Pietrasiewicz goto del; 66963406087SAndrzej Pietrasiewicz } 67000a2430fSAndrzej Pietrasiewicz 67100a2430fSAndrzej Pietrasiewicz return 0; 67263406087SAndrzej Pietrasiewicz del: 67363406087SAndrzej Pietrasiewicz cdev_del(&hidg->cdev); 674d12a8727SPavitrakumar Managutte fail_free_descs: 675d12a8727SPavitrakumar Managutte usb_free_all_descriptors(f); 67600a2430fSAndrzej Pietrasiewicz fail: 67700a2430fSAndrzej Pietrasiewicz ERROR(f->config->cdev, "hidg_bind FAILED\n"); 67800a2430fSAndrzej Pietrasiewicz if (hidg->req != NULL) { 67900a2430fSAndrzej Pietrasiewicz kfree(hidg->req->buf); 68000a2430fSAndrzej Pietrasiewicz if (hidg->in_ep != NULL) 68100a2430fSAndrzej Pietrasiewicz usb_ep_free_request(hidg->in_ep, hidg->req); 68200a2430fSAndrzej Pietrasiewicz } 68300a2430fSAndrzej Pietrasiewicz 68400a2430fSAndrzej Pietrasiewicz return status; 68500a2430fSAndrzej Pietrasiewicz } 68600a2430fSAndrzej Pietrasiewicz 687*cb382536SAndrzej Pietrasiewicz #ifdef USBF_HID_INCLUDED 68800a2430fSAndrzej Pietrasiewicz static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) 68900a2430fSAndrzej Pietrasiewicz { 69000a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 69100a2430fSAndrzej Pietrasiewicz 69200a2430fSAndrzej Pietrasiewicz device_destroy(hidg_class, MKDEV(major, hidg->minor)); 69300a2430fSAndrzej Pietrasiewicz cdev_del(&hidg->cdev); 69400a2430fSAndrzej Pietrasiewicz 69500a2430fSAndrzej Pietrasiewicz /* disable/free request and end point */ 69600a2430fSAndrzej Pietrasiewicz usb_ep_disable(hidg->in_ep); 69700a2430fSAndrzej Pietrasiewicz usb_ep_dequeue(hidg->in_ep, hidg->req); 69800a2430fSAndrzej Pietrasiewicz kfree(hidg->req->buf); 69900a2430fSAndrzej Pietrasiewicz usb_ep_free_request(hidg->in_ep, hidg->req); 70000a2430fSAndrzej Pietrasiewicz 70100a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f); 70200a2430fSAndrzej Pietrasiewicz 70300a2430fSAndrzej Pietrasiewicz kfree(hidg->report_desc); 70400a2430fSAndrzej Pietrasiewicz kfree(hidg); 70500a2430fSAndrzej Pietrasiewicz } 70600a2430fSAndrzej Pietrasiewicz 70700a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 70800a2430fSAndrzej Pietrasiewicz /* usb_configuration */ 70900a2430fSAndrzej Pietrasiewicz int __init hidg_bind_config(struct usb_configuration *c, 71000a2430fSAndrzej Pietrasiewicz struct hidg_func_descriptor *fdesc, int index) 71100a2430fSAndrzej Pietrasiewicz { 71200a2430fSAndrzej Pietrasiewicz struct f_hidg *hidg; 71300a2430fSAndrzej Pietrasiewicz int status; 71400a2430fSAndrzej Pietrasiewicz 71500a2430fSAndrzej Pietrasiewicz if (index >= minors) 71600a2430fSAndrzej Pietrasiewicz return -ENOENT; 71700a2430fSAndrzej Pietrasiewicz 71800a2430fSAndrzej Pietrasiewicz /* allocate and initialize one new instance */ 71900a2430fSAndrzej Pietrasiewicz hidg = kzalloc(sizeof *hidg, GFP_KERNEL); 72000a2430fSAndrzej Pietrasiewicz if (!hidg) 72100a2430fSAndrzej Pietrasiewicz return -ENOMEM; 72200a2430fSAndrzej Pietrasiewicz 72300a2430fSAndrzej Pietrasiewicz hidg->minor = index; 72400a2430fSAndrzej Pietrasiewicz hidg->bInterfaceSubClass = fdesc->subclass; 72500a2430fSAndrzej Pietrasiewicz hidg->bInterfaceProtocol = fdesc->protocol; 72600a2430fSAndrzej Pietrasiewicz hidg->report_length = fdesc->report_length; 72700a2430fSAndrzej Pietrasiewicz hidg->report_desc_length = fdesc->report_desc_length; 72800a2430fSAndrzej Pietrasiewicz hidg->report_desc = kmemdup(fdesc->report_desc, 72900a2430fSAndrzej Pietrasiewicz fdesc->report_desc_length, 73000a2430fSAndrzej Pietrasiewicz GFP_KERNEL); 73100a2430fSAndrzej Pietrasiewicz if (!hidg->report_desc) { 73200a2430fSAndrzej Pietrasiewicz kfree(hidg); 73300a2430fSAndrzej Pietrasiewicz return -ENOMEM; 73400a2430fSAndrzej Pietrasiewicz } 73500a2430fSAndrzej Pietrasiewicz 73600a2430fSAndrzej Pietrasiewicz hidg->func.name = "hid"; 73700a2430fSAndrzej Pietrasiewicz hidg->func.strings = ct_func_strings; 73800a2430fSAndrzej Pietrasiewicz hidg->func.bind = hidg_bind; 73900a2430fSAndrzej Pietrasiewicz hidg->func.unbind = hidg_unbind; 74000a2430fSAndrzej Pietrasiewicz hidg->func.set_alt = hidg_set_alt; 74100a2430fSAndrzej Pietrasiewicz hidg->func.disable = hidg_disable; 74200a2430fSAndrzej Pietrasiewicz hidg->func.setup = hidg_setup; 74300a2430fSAndrzej Pietrasiewicz 74400a2430fSAndrzej Pietrasiewicz /* this could me made configurable at some point */ 74500a2430fSAndrzej Pietrasiewicz hidg->qlen = 4; 74600a2430fSAndrzej Pietrasiewicz 74700a2430fSAndrzej Pietrasiewicz status = usb_add_function(c, &hidg->func); 74800a2430fSAndrzej Pietrasiewicz if (status) 74900a2430fSAndrzej Pietrasiewicz kfree(hidg); 75000a2430fSAndrzej Pietrasiewicz 75100a2430fSAndrzej Pietrasiewicz return status; 75200a2430fSAndrzej Pietrasiewicz } 75300a2430fSAndrzej Pietrasiewicz 754*cb382536SAndrzej Pietrasiewicz #else 755*cb382536SAndrzej Pietrasiewicz 756*cb382536SAndrzej Pietrasiewicz static inline int hidg_get_minor(void) 757*cb382536SAndrzej Pietrasiewicz { 758*cb382536SAndrzej Pietrasiewicz int ret; 759*cb382536SAndrzej Pietrasiewicz 760*cb382536SAndrzej Pietrasiewicz ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL); 761*cb382536SAndrzej Pietrasiewicz 762*cb382536SAndrzej Pietrasiewicz return ret; 763*cb382536SAndrzej Pietrasiewicz } 764*cb382536SAndrzej Pietrasiewicz 765*cb382536SAndrzej Pietrasiewicz static inline void hidg_put_minor(int minor) 766*cb382536SAndrzej Pietrasiewicz { 767*cb382536SAndrzej Pietrasiewicz ida_simple_remove(&hidg_ida, minor); 768*cb382536SAndrzej Pietrasiewicz } 769*cb382536SAndrzej Pietrasiewicz 770*cb382536SAndrzej Pietrasiewicz static void hidg_free_inst(struct usb_function_instance *f) 771*cb382536SAndrzej Pietrasiewicz { 772*cb382536SAndrzej Pietrasiewicz struct f_hid_opts *opts; 773*cb382536SAndrzej Pietrasiewicz 774*cb382536SAndrzej Pietrasiewicz opts = container_of(f, struct f_hid_opts, func_inst); 775*cb382536SAndrzej Pietrasiewicz 776*cb382536SAndrzej Pietrasiewicz mutex_lock(&hidg_ida_lock); 777*cb382536SAndrzej Pietrasiewicz 778*cb382536SAndrzej Pietrasiewicz hidg_put_minor(opts->minor); 779*cb382536SAndrzej Pietrasiewicz if (idr_is_empty(&hidg_ida.idr)) 780*cb382536SAndrzej Pietrasiewicz ghid_cleanup(); 781*cb382536SAndrzej Pietrasiewicz 782*cb382536SAndrzej Pietrasiewicz mutex_unlock(&hidg_ida_lock); 783*cb382536SAndrzej Pietrasiewicz 784*cb382536SAndrzej Pietrasiewicz if (opts->report_desc_alloc) 785*cb382536SAndrzej Pietrasiewicz kfree(opts->report_desc); 786*cb382536SAndrzej Pietrasiewicz 787*cb382536SAndrzej Pietrasiewicz kfree(opts); 788*cb382536SAndrzej Pietrasiewicz } 789*cb382536SAndrzej Pietrasiewicz 790*cb382536SAndrzej Pietrasiewicz static struct usb_function_instance *hidg_alloc_inst(void) 791*cb382536SAndrzej Pietrasiewicz { 792*cb382536SAndrzej Pietrasiewicz struct f_hid_opts *opts; 793*cb382536SAndrzej Pietrasiewicz struct usb_function_instance *ret; 794*cb382536SAndrzej Pietrasiewicz int status = 0; 795*cb382536SAndrzej Pietrasiewicz 796*cb382536SAndrzej Pietrasiewicz opts = kzalloc(sizeof(*opts), GFP_KERNEL); 797*cb382536SAndrzej Pietrasiewicz if (!opts) 798*cb382536SAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 799*cb382536SAndrzej Pietrasiewicz 800*cb382536SAndrzej Pietrasiewicz opts->func_inst.free_func_inst = hidg_free_inst; 801*cb382536SAndrzej Pietrasiewicz ret = &opts->func_inst; 802*cb382536SAndrzej Pietrasiewicz 803*cb382536SAndrzej Pietrasiewicz mutex_lock(&hidg_ida_lock); 804*cb382536SAndrzej Pietrasiewicz 805*cb382536SAndrzej Pietrasiewicz if (idr_is_empty(&hidg_ida.idr)) { 806*cb382536SAndrzej Pietrasiewicz status = ghid_setup(NULL, HIDG_MINORS); 807*cb382536SAndrzej Pietrasiewicz if (status) { 808*cb382536SAndrzej Pietrasiewicz ret = ERR_PTR(status); 809*cb382536SAndrzej Pietrasiewicz kfree(opts); 810*cb382536SAndrzej Pietrasiewicz goto unlock; 811*cb382536SAndrzej Pietrasiewicz } 812*cb382536SAndrzej Pietrasiewicz } 813*cb382536SAndrzej Pietrasiewicz 814*cb382536SAndrzej Pietrasiewicz opts->minor = hidg_get_minor(); 815*cb382536SAndrzej Pietrasiewicz if (opts->minor < 0) { 816*cb382536SAndrzej Pietrasiewicz ret = ERR_PTR(opts->minor); 817*cb382536SAndrzej Pietrasiewicz kfree(opts); 818*cb382536SAndrzej Pietrasiewicz if (idr_is_empty(&hidg_ida.idr)) 819*cb382536SAndrzej Pietrasiewicz ghid_cleanup(); 820*cb382536SAndrzej Pietrasiewicz } 821*cb382536SAndrzej Pietrasiewicz 822*cb382536SAndrzej Pietrasiewicz unlock: 823*cb382536SAndrzej Pietrasiewicz mutex_unlock(&hidg_ida_lock); 824*cb382536SAndrzej Pietrasiewicz return ret; 825*cb382536SAndrzej Pietrasiewicz } 826*cb382536SAndrzej Pietrasiewicz 827*cb382536SAndrzej Pietrasiewicz static void hidg_free(struct usb_function *f) 828*cb382536SAndrzej Pietrasiewicz { 829*cb382536SAndrzej Pietrasiewicz struct f_hidg *hidg; 830*cb382536SAndrzej Pietrasiewicz 831*cb382536SAndrzej Pietrasiewicz hidg = func_to_hidg(f); 832*cb382536SAndrzej Pietrasiewicz kfree(hidg->report_desc); 833*cb382536SAndrzej Pietrasiewicz kfree(hidg); 834*cb382536SAndrzej Pietrasiewicz } 835*cb382536SAndrzej Pietrasiewicz 836*cb382536SAndrzej Pietrasiewicz static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) 837*cb382536SAndrzej Pietrasiewicz { 838*cb382536SAndrzej Pietrasiewicz struct f_hidg *hidg = func_to_hidg(f); 839*cb382536SAndrzej Pietrasiewicz 840*cb382536SAndrzej Pietrasiewicz device_destroy(hidg_class, MKDEV(major, hidg->minor)); 841*cb382536SAndrzej Pietrasiewicz cdev_del(&hidg->cdev); 842*cb382536SAndrzej Pietrasiewicz 843*cb382536SAndrzej Pietrasiewicz /* disable/free request and end point */ 844*cb382536SAndrzej Pietrasiewicz usb_ep_disable(hidg->in_ep); 845*cb382536SAndrzej Pietrasiewicz usb_ep_dequeue(hidg->in_ep, hidg->req); 846*cb382536SAndrzej Pietrasiewicz kfree(hidg->req->buf); 847*cb382536SAndrzej Pietrasiewicz usb_ep_free_request(hidg->in_ep, hidg->req); 848*cb382536SAndrzej Pietrasiewicz 849*cb382536SAndrzej Pietrasiewicz usb_free_all_descriptors(f); 850*cb382536SAndrzej Pietrasiewicz } 851*cb382536SAndrzej Pietrasiewicz 852*cb382536SAndrzej Pietrasiewicz struct usb_function *hidg_alloc(struct usb_function_instance *fi) 853*cb382536SAndrzej Pietrasiewicz { 854*cb382536SAndrzej Pietrasiewicz struct f_hidg *hidg; 855*cb382536SAndrzej Pietrasiewicz struct f_hid_opts *opts; 856*cb382536SAndrzej Pietrasiewicz 857*cb382536SAndrzej Pietrasiewicz /* allocate and initialize one new instance */ 858*cb382536SAndrzej Pietrasiewicz hidg = kzalloc(sizeof(*hidg), GFP_KERNEL); 859*cb382536SAndrzej Pietrasiewicz if (!hidg) 860*cb382536SAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 861*cb382536SAndrzej Pietrasiewicz 862*cb382536SAndrzej Pietrasiewicz opts = container_of(fi, struct f_hid_opts, func_inst); 863*cb382536SAndrzej Pietrasiewicz 864*cb382536SAndrzej Pietrasiewicz hidg->minor = opts->minor; 865*cb382536SAndrzej Pietrasiewicz hidg->bInterfaceSubClass = opts->subclass; 866*cb382536SAndrzej Pietrasiewicz hidg->bInterfaceProtocol = opts->protocol; 867*cb382536SAndrzej Pietrasiewicz hidg->report_length = opts->report_length; 868*cb382536SAndrzej Pietrasiewicz hidg->report_desc_length = opts->report_desc_length; 869*cb382536SAndrzej Pietrasiewicz if (opts->report_desc) { 870*cb382536SAndrzej Pietrasiewicz hidg->report_desc = kmemdup(opts->report_desc, 871*cb382536SAndrzej Pietrasiewicz opts->report_desc_length, 872*cb382536SAndrzej Pietrasiewicz GFP_KERNEL); 873*cb382536SAndrzej Pietrasiewicz if (!hidg->report_desc) { 874*cb382536SAndrzej Pietrasiewicz kfree(hidg); 875*cb382536SAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM); 876*cb382536SAndrzej Pietrasiewicz } 877*cb382536SAndrzej Pietrasiewicz } 878*cb382536SAndrzej Pietrasiewicz 879*cb382536SAndrzej Pietrasiewicz hidg->func.name = "hid"; 880*cb382536SAndrzej Pietrasiewicz hidg->func.strings = ct_func_strings; 881*cb382536SAndrzej Pietrasiewicz hidg->func.bind = hidg_bind; 882*cb382536SAndrzej Pietrasiewicz hidg->func.unbind = hidg_unbind; 883*cb382536SAndrzej Pietrasiewicz hidg->func.set_alt = hidg_set_alt; 884*cb382536SAndrzej Pietrasiewicz hidg->func.disable = hidg_disable; 885*cb382536SAndrzej Pietrasiewicz hidg->func.setup = hidg_setup; 886*cb382536SAndrzej Pietrasiewicz hidg->func.free_func = hidg_free; 887*cb382536SAndrzej Pietrasiewicz 888*cb382536SAndrzej Pietrasiewicz /* this could me made configurable at some point */ 889*cb382536SAndrzej Pietrasiewicz hidg->qlen = 4; 890*cb382536SAndrzej Pietrasiewicz 891*cb382536SAndrzej Pietrasiewicz return &hidg->func; 892*cb382536SAndrzej Pietrasiewicz } 893*cb382536SAndrzej Pietrasiewicz 894*cb382536SAndrzej Pietrasiewicz DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc); 895*cb382536SAndrzej Pietrasiewicz MODULE_LICENSE("GPL"); 896*cb382536SAndrzej Pietrasiewicz MODULE_AUTHOR("Fabien Chouteau"); 897*cb382536SAndrzej Pietrasiewicz 898*cb382536SAndrzej Pietrasiewicz #endif 899*cb382536SAndrzej Pietrasiewicz 900*cb382536SAndrzej Pietrasiewicz int ghid_setup(struct usb_gadget *g, int count) 90100a2430fSAndrzej Pietrasiewicz { 90200a2430fSAndrzej Pietrasiewicz int status; 90300a2430fSAndrzej Pietrasiewicz dev_t dev; 90400a2430fSAndrzej Pietrasiewicz 90500a2430fSAndrzej Pietrasiewicz hidg_class = class_create(THIS_MODULE, "hidg"); 90606529407SAndrzej Pietrasiewicz if (IS_ERR(hidg_class)) { 90706529407SAndrzej Pietrasiewicz hidg_class = NULL; 90806529407SAndrzej Pietrasiewicz return PTR_ERR(hidg_class); 90906529407SAndrzej Pietrasiewicz } 91000a2430fSAndrzej Pietrasiewicz 91100a2430fSAndrzej Pietrasiewicz status = alloc_chrdev_region(&dev, 0, count, "hidg"); 91200a2430fSAndrzej Pietrasiewicz if (!status) { 91300a2430fSAndrzej Pietrasiewicz major = MAJOR(dev); 91400a2430fSAndrzej Pietrasiewicz minors = count; 91500a2430fSAndrzej Pietrasiewicz } 91600a2430fSAndrzej Pietrasiewicz 91700a2430fSAndrzej Pietrasiewicz return status; 91800a2430fSAndrzej Pietrasiewicz } 91900a2430fSAndrzej Pietrasiewicz 92000a2430fSAndrzej Pietrasiewicz void ghid_cleanup(void) 92100a2430fSAndrzej Pietrasiewicz { 92200a2430fSAndrzej Pietrasiewicz if (major) { 92300a2430fSAndrzej Pietrasiewicz unregister_chrdev_region(MKDEV(major, 0), minors); 92400a2430fSAndrzej Pietrasiewicz major = minors = 0; 92500a2430fSAndrzej Pietrasiewicz } 92600a2430fSAndrzej Pietrasiewicz 92700a2430fSAndrzej Pietrasiewicz class_destroy(hidg_class); 92800a2430fSAndrzej Pietrasiewicz hidg_class = NULL; 92900a2430fSAndrzej Pietrasiewicz } 930