1*00a2430fSAndrzej Pietrasiewicz /* 2*00a2430fSAndrzej Pietrasiewicz * uvc_gadget.c -- USB Video Class Gadget driver 3*00a2430fSAndrzej Pietrasiewicz * 4*00a2430fSAndrzej Pietrasiewicz * Copyright (C) 2009-2010 5*00a2430fSAndrzej Pietrasiewicz * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 6*00a2430fSAndrzej Pietrasiewicz * 7*00a2430fSAndrzej Pietrasiewicz * This program is free software; you can redistribute it and/or modify 8*00a2430fSAndrzej Pietrasiewicz * it under the terms of the GNU General Public License as published by 9*00a2430fSAndrzej Pietrasiewicz * the Free Software Foundation; either version 2 of the License, or 10*00a2430fSAndrzej Pietrasiewicz * (at your option) any later version. 11*00a2430fSAndrzej Pietrasiewicz */ 12*00a2430fSAndrzej Pietrasiewicz 13*00a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h> 14*00a2430fSAndrzej Pietrasiewicz #include <linux/device.h> 15*00a2430fSAndrzej Pietrasiewicz #include <linux/errno.h> 16*00a2430fSAndrzej Pietrasiewicz #include <linux/fs.h> 17*00a2430fSAndrzej Pietrasiewicz #include <linux/list.h> 18*00a2430fSAndrzej Pietrasiewicz #include <linux/mutex.h> 19*00a2430fSAndrzej Pietrasiewicz #include <linux/string.h> 20*00a2430fSAndrzej Pietrasiewicz #include <linux/usb/ch9.h> 21*00a2430fSAndrzej Pietrasiewicz #include <linux/usb/gadget.h> 22*00a2430fSAndrzej Pietrasiewicz #include <linux/usb/video.h> 23*00a2430fSAndrzej Pietrasiewicz #include <linux/vmalloc.h> 24*00a2430fSAndrzej Pietrasiewicz #include <linux/wait.h> 25*00a2430fSAndrzej Pietrasiewicz 26*00a2430fSAndrzej Pietrasiewicz #include <media/v4l2-dev.h> 27*00a2430fSAndrzej Pietrasiewicz #include <media/v4l2-event.h> 28*00a2430fSAndrzej Pietrasiewicz 29*00a2430fSAndrzej Pietrasiewicz #include "uvc.h" 30*00a2430fSAndrzej Pietrasiewicz 31*00a2430fSAndrzej Pietrasiewicz unsigned int uvc_gadget_trace_param; 32*00a2430fSAndrzej Pietrasiewicz 33*00a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 34*00a2430fSAndrzej Pietrasiewicz 35*00a2430fSAndrzej Pietrasiewicz /* module parameters specific to the Video streaming endpoint */ 36*00a2430fSAndrzej Pietrasiewicz static unsigned int streaming_interval = 1; 37*00a2430fSAndrzej Pietrasiewicz module_param(streaming_interval, uint, S_IRUGO|S_IWUSR); 38*00a2430fSAndrzej Pietrasiewicz MODULE_PARM_DESC(streaming_interval, "1 - 16"); 39*00a2430fSAndrzej Pietrasiewicz 40*00a2430fSAndrzej Pietrasiewicz static unsigned int streaming_maxpacket = 1024; 41*00a2430fSAndrzej Pietrasiewicz module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR); 42*00a2430fSAndrzej Pietrasiewicz MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)"); 43*00a2430fSAndrzej Pietrasiewicz 44*00a2430fSAndrzej Pietrasiewicz static unsigned int streaming_maxburst; 45*00a2430fSAndrzej Pietrasiewicz module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR); 46*00a2430fSAndrzej Pietrasiewicz MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)"); 47*00a2430fSAndrzej Pietrasiewicz 48*00a2430fSAndrzej Pietrasiewicz /* -------------------------------------------------------------------------- 49*00a2430fSAndrzej Pietrasiewicz * Function descriptors 50*00a2430fSAndrzej Pietrasiewicz */ 51*00a2430fSAndrzej Pietrasiewicz 52*00a2430fSAndrzej Pietrasiewicz /* string IDs are assigned dynamically */ 53*00a2430fSAndrzej Pietrasiewicz 54*00a2430fSAndrzej Pietrasiewicz #define UVC_STRING_CONTROL_IDX 0 55*00a2430fSAndrzej Pietrasiewicz #define UVC_STRING_STREAMING_IDX 1 56*00a2430fSAndrzej Pietrasiewicz 57*00a2430fSAndrzej Pietrasiewicz static struct usb_string uvc_en_us_strings[] = { 58*00a2430fSAndrzej Pietrasiewicz [UVC_STRING_CONTROL_IDX].s = "UVC Camera", 59*00a2430fSAndrzej Pietrasiewicz [UVC_STRING_STREAMING_IDX].s = "Video Streaming", 60*00a2430fSAndrzej Pietrasiewicz { } 61*00a2430fSAndrzej Pietrasiewicz }; 62*00a2430fSAndrzej Pietrasiewicz 63*00a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings uvc_stringtab = { 64*00a2430fSAndrzej Pietrasiewicz .language = 0x0409, /* en-us */ 65*00a2430fSAndrzej Pietrasiewicz .strings = uvc_en_us_strings, 66*00a2430fSAndrzej Pietrasiewicz }; 67*00a2430fSAndrzej Pietrasiewicz 68*00a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *uvc_function_strings[] = { 69*00a2430fSAndrzej Pietrasiewicz &uvc_stringtab, 70*00a2430fSAndrzej Pietrasiewicz NULL, 71*00a2430fSAndrzej Pietrasiewicz }; 72*00a2430fSAndrzej Pietrasiewicz 73*00a2430fSAndrzej Pietrasiewicz #define UVC_INTF_VIDEO_CONTROL 0 74*00a2430fSAndrzej Pietrasiewicz #define UVC_INTF_VIDEO_STREAMING 1 75*00a2430fSAndrzej Pietrasiewicz 76*00a2430fSAndrzej Pietrasiewicz #define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */ 77*00a2430fSAndrzej Pietrasiewicz 78*00a2430fSAndrzej Pietrasiewicz static struct usb_interface_assoc_descriptor uvc_iad __initdata = { 79*00a2430fSAndrzej Pietrasiewicz .bLength = sizeof(uvc_iad), 80*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, 81*00a2430fSAndrzej Pietrasiewicz .bFirstInterface = 0, 82*00a2430fSAndrzej Pietrasiewicz .bInterfaceCount = 2, 83*00a2430fSAndrzej Pietrasiewicz .bFunctionClass = USB_CLASS_VIDEO, 84*00a2430fSAndrzej Pietrasiewicz .bFunctionSubClass = UVC_SC_VIDEO_INTERFACE_COLLECTION, 85*00a2430fSAndrzej Pietrasiewicz .bFunctionProtocol = 0x00, 86*00a2430fSAndrzej Pietrasiewicz .iFunction = 0, 87*00a2430fSAndrzej Pietrasiewicz }; 88*00a2430fSAndrzej Pietrasiewicz 89*00a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor uvc_control_intf __initdata = { 90*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_INTERFACE_SIZE, 91*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE, 92*00a2430fSAndrzej Pietrasiewicz .bInterfaceNumber = UVC_INTF_VIDEO_CONTROL, 93*00a2430fSAndrzej Pietrasiewicz .bAlternateSetting = 0, 94*00a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 1, 95*00a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_VIDEO, 96*00a2430fSAndrzej Pietrasiewicz .bInterfaceSubClass = UVC_SC_VIDEOCONTROL, 97*00a2430fSAndrzej Pietrasiewicz .bInterfaceProtocol = 0x00, 98*00a2430fSAndrzej Pietrasiewicz .iInterface = 0, 99*00a2430fSAndrzej Pietrasiewicz }; 100*00a2430fSAndrzej Pietrasiewicz 101*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uvc_control_ep __initdata = { 102*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 103*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 104*00a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 105*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_INT, 106*00a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), 107*00a2430fSAndrzej Pietrasiewicz .bInterval = 8, 108*00a2430fSAndrzej Pietrasiewicz }; 109*00a2430fSAndrzej Pietrasiewicz 110*00a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp __initdata = { 111*00a2430fSAndrzej Pietrasiewicz .bLength = sizeof(uvc_ss_control_comp), 112*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 113*00a2430fSAndrzej Pietrasiewicz /* The following 3 values can be tweaked if necessary. */ 114*00a2430fSAndrzej Pietrasiewicz .bMaxBurst = 0, 115*00a2430fSAndrzej Pietrasiewicz .bmAttributes = 0, 116*00a2430fSAndrzej Pietrasiewicz .wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), 117*00a2430fSAndrzej Pietrasiewicz }; 118*00a2430fSAndrzej Pietrasiewicz 119*00a2430fSAndrzej Pietrasiewicz static struct uvc_control_endpoint_descriptor uvc_control_cs_ep __initdata = { 120*00a2430fSAndrzej Pietrasiewicz .bLength = UVC_DT_CONTROL_ENDPOINT_SIZE, 121*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_CS_ENDPOINT, 122*00a2430fSAndrzej Pietrasiewicz .bDescriptorSubType = UVC_EP_INTERRUPT, 123*00a2430fSAndrzej Pietrasiewicz .wMaxTransferSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE), 124*00a2430fSAndrzej Pietrasiewicz }; 125*00a2430fSAndrzej Pietrasiewicz 126*00a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor uvc_streaming_intf_alt0 __initdata = { 127*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_INTERFACE_SIZE, 128*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE, 129*00a2430fSAndrzej Pietrasiewicz .bInterfaceNumber = UVC_INTF_VIDEO_STREAMING, 130*00a2430fSAndrzej Pietrasiewicz .bAlternateSetting = 0, 131*00a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 0, 132*00a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_VIDEO, 133*00a2430fSAndrzej Pietrasiewicz .bInterfaceSubClass = UVC_SC_VIDEOSTREAMING, 134*00a2430fSAndrzej Pietrasiewicz .bInterfaceProtocol = 0x00, 135*00a2430fSAndrzej Pietrasiewicz .iInterface = 0, 136*00a2430fSAndrzej Pietrasiewicz }; 137*00a2430fSAndrzej Pietrasiewicz 138*00a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor uvc_streaming_intf_alt1 __initdata = { 139*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_INTERFACE_SIZE, 140*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE, 141*00a2430fSAndrzej Pietrasiewicz .bInterfaceNumber = UVC_INTF_VIDEO_STREAMING, 142*00a2430fSAndrzej Pietrasiewicz .bAlternateSetting = 1, 143*00a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 1, 144*00a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_VIDEO, 145*00a2430fSAndrzej Pietrasiewicz .bInterfaceSubClass = UVC_SC_VIDEOSTREAMING, 146*00a2430fSAndrzej Pietrasiewicz .bInterfaceProtocol = 0x00, 147*00a2430fSAndrzej Pietrasiewicz .iInterface = 0, 148*00a2430fSAndrzej Pietrasiewicz }; 149*00a2430fSAndrzej Pietrasiewicz 150*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = { 151*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 152*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 153*00a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 154*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_SYNC_ASYNC 155*00a2430fSAndrzej Pietrasiewicz | USB_ENDPOINT_XFER_ISOC, 156*00a2430fSAndrzej Pietrasiewicz /* The wMaxPacketSize and bInterval values will be initialized from 157*00a2430fSAndrzej Pietrasiewicz * module parameters. 158*00a2430fSAndrzej Pietrasiewicz */ 159*00a2430fSAndrzej Pietrasiewicz }; 160*00a2430fSAndrzej Pietrasiewicz 161*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = { 162*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 163*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 164*00a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 165*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_SYNC_ASYNC 166*00a2430fSAndrzej Pietrasiewicz | USB_ENDPOINT_XFER_ISOC, 167*00a2430fSAndrzej Pietrasiewicz /* The wMaxPacketSize and bInterval values will be initialized from 168*00a2430fSAndrzej Pietrasiewicz * module parameters. 169*00a2430fSAndrzej Pietrasiewicz */ 170*00a2430fSAndrzej Pietrasiewicz }; 171*00a2430fSAndrzej Pietrasiewicz 172*00a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = { 173*00a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE, 174*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT, 175*00a2430fSAndrzej Pietrasiewicz 176*00a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN, 177*00a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_SYNC_ASYNC 178*00a2430fSAndrzej Pietrasiewicz | USB_ENDPOINT_XFER_ISOC, 179*00a2430fSAndrzej Pietrasiewicz /* The wMaxPacketSize and bInterval values will be initialized from 180*00a2430fSAndrzej Pietrasiewicz * module parameters. 181*00a2430fSAndrzej Pietrasiewicz */ 182*00a2430fSAndrzej Pietrasiewicz }; 183*00a2430fSAndrzej Pietrasiewicz 184*00a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = { 185*00a2430fSAndrzej Pietrasiewicz .bLength = sizeof(uvc_ss_streaming_comp), 186*00a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 187*00a2430fSAndrzej Pietrasiewicz /* The bMaxBurst, bmAttributes and wBytesPerInterval values will be 188*00a2430fSAndrzej Pietrasiewicz * initialized from module parameters. 189*00a2430fSAndrzej Pietrasiewicz */ 190*00a2430fSAndrzej Pietrasiewicz }; 191*00a2430fSAndrzej Pietrasiewicz 192*00a2430fSAndrzej Pietrasiewicz static const struct usb_descriptor_header * const uvc_fs_streaming[] = { 193*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &uvc_streaming_intf_alt1, 194*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &uvc_fs_streaming_ep, 195*00a2430fSAndrzej Pietrasiewicz NULL, 196*00a2430fSAndrzej Pietrasiewicz }; 197*00a2430fSAndrzej Pietrasiewicz 198*00a2430fSAndrzej Pietrasiewicz static const struct usb_descriptor_header * const uvc_hs_streaming[] = { 199*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &uvc_streaming_intf_alt1, 200*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &uvc_hs_streaming_ep, 201*00a2430fSAndrzej Pietrasiewicz NULL, 202*00a2430fSAndrzej Pietrasiewicz }; 203*00a2430fSAndrzej Pietrasiewicz 204*00a2430fSAndrzej Pietrasiewicz static const struct usb_descriptor_header * const uvc_ss_streaming[] = { 205*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &uvc_streaming_intf_alt1, 206*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &uvc_ss_streaming_ep, 207*00a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &uvc_ss_streaming_comp, 208*00a2430fSAndrzej Pietrasiewicz NULL, 209*00a2430fSAndrzej Pietrasiewicz }; 210*00a2430fSAndrzej Pietrasiewicz 211*00a2430fSAndrzej Pietrasiewicz /* -------------------------------------------------------------------------- 212*00a2430fSAndrzej Pietrasiewicz * Control requests 213*00a2430fSAndrzej Pietrasiewicz */ 214*00a2430fSAndrzej Pietrasiewicz 215*00a2430fSAndrzej Pietrasiewicz static void 216*00a2430fSAndrzej Pietrasiewicz uvc_function_ep0_complete(struct usb_ep *ep, struct usb_request *req) 217*00a2430fSAndrzej Pietrasiewicz { 218*00a2430fSAndrzej Pietrasiewicz struct uvc_device *uvc = req->context; 219*00a2430fSAndrzej Pietrasiewicz struct v4l2_event v4l2_event; 220*00a2430fSAndrzej Pietrasiewicz struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; 221*00a2430fSAndrzej Pietrasiewicz 222*00a2430fSAndrzej Pietrasiewicz if (uvc->event_setup_out) { 223*00a2430fSAndrzej Pietrasiewicz uvc->event_setup_out = 0; 224*00a2430fSAndrzej Pietrasiewicz 225*00a2430fSAndrzej Pietrasiewicz memset(&v4l2_event, 0, sizeof(v4l2_event)); 226*00a2430fSAndrzej Pietrasiewicz v4l2_event.type = UVC_EVENT_DATA; 227*00a2430fSAndrzej Pietrasiewicz uvc_event->data.length = req->actual; 228*00a2430fSAndrzej Pietrasiewicz memcpy(&uvc_event->data.data, req->buf, req->actual); 229*00a2430fSAndrzej Pietrasiewicz v4l2_event_queue(uvc->vdev, &v4l2_event); 230*00a2430fSAndrzej Pietrasiewicz } 231*00a2430fSAndrzej Pietrasiewicz } 232*00a2430fSAndrzej Pietrasiewicz 233*00a2430fSAndrzej Pietrasiewicz static int 234*00a2430fSAndrzej Pietrasiewicz uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) 235*00a2430fSAndrzej Pietrasiewicz { 236*00a2430fSAndrzej Pietrasiewicz struct uvc_device *uvc = to_uvc(f); 237*00a2430fSAndrzej Pietrasiewicz struct v4l2_event v4l2_event; 238*00a2430fSAndrzej Pietrasiewicz struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; 239*00a2430fSAndrzej Pietrasiewicz 240*00a2430fSAndrzej Pietrasiewicz /* printk(KERN_INFO "setup request %02x %02x value %04x index %04x %04x\n", 241*00a2430fSAndrzej Pietrasiewicz * ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue), 242*00a2430fSAndrzej Pietrasiewicz * le16_to_cpu(ctrl->wIndex), le16_to_cpu(ctrl->wLength)); 243*00a2430fSAndrzej Pietrasiewicz */ 244*00a2430fSAndrzej Pietrasiewicz 245*00a2430fSAndrzej Pietrasiewicz if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) { 246*00a2430fSAndrzej Pietrasiewicz INFO(f->config->cdev, "invalid request type\n"); 247*00a2430fSAndrzej Pietrasiewicz return -EINVAL; 248*00a2430fSAndrzej Pietrasiewicz } 249*00a2430fSAndrzej Pietrasiewicz 250*00a2430fSAndrzej Pietrasiewicz /* Stall too big requests. */ 251*00a2430fSAndrzej Pietrasiewicz if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE) 252*00a2430fSAndrzej Pietrasiewicz return -EINVAL; 253*00a2430fSAndrzej Pietrasiewicz 254*00a2430fSAndrzej Pietrasiewicz memset(&v4l2_event, 0, sizeof(v4l2_event)); 255*00a2430fSAndrzej Pietrasiewicz v4l2_event.type = UVC_EVENT_SETUP; 256*00a2430fSAndrzej Pietrasiewicz memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req)); 257*00a2430fSAndrzej Pietrasiewicz v4l2_event_queue(uvc->vdev, &v4l2_event); 258*00a2430fSAndrzej Pietrasiewicz 259*00a2430fSAndrzej Pietrasiewicz return 0; 260*00a2430fSAndrzej Pietrasiewicz } 261*00a2430fSAndrzej Pietrasiewicz 262*00a2430fSAndrzej Pietrasiewicz void uvc_function_setup_continue(struct uvc_device *uvc) 263*00a2430fSAndrzej Pietrasiewicz { 264*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = uvc->func.config->cdev; 265*00a2430fSAndrzej Pietrasiewicz 266*00a2430fSAndrzej Pietrasiewicz usb_composite_setup_continue(cdev); 267*00a2430fSAndrzej Pietrasiewicz } 268*00a2430fSAndrzej Pietrasiewicz 269*00a2430fSAndrzej Pietrasiewicz static int 270*00a2430fSAndrzej Pietrasiewicz uvc_function_get_alt(struct usb_function *f, unsigned interface) 271*00a2430fSAndrzej Pietrasiewicz { 272*00a2430fSAndrzej Pietrasiewicz struct uvc_device *uvc = to_uvc(f); 273*00a2430fSAndrzej Pietrasiewicz 274*00a2430fSAndrzej Pietrasiewicz INFO(f->config->cdev, "uvc_function_get_alt(%u)\n", interface); 275*00a2430fSAndrzej Pietrasiewicz 276*00a2430fSAndrzej Pietrasiewicz if (interface == uvc->control_intf) 277*00a2430fSAndrzej Pietrasiewicz return 0; 278*00a2430fSAndrzej Pietrasiewicz else if (interface != uvc->streaming_intf) 279*00a2430fSAndrzej Pietrasiewicz return -EINVAL; 280*00a2430fSAndrzej Pietrasiewicz else 281*00a2430fSAndrzej Pietrasiewicz return uvc->state == UVC_STATE_STREAMING ? 1 : 0; 282*00a2430fSAndrzej Pietrasiewicz } 283*00a2430fSAndrzej Pietrasiewicz 284*00a2430fSAndrzej Pietrasiewicz static int 285*00a2430fSAndrzej Pietrasiewicz uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) 286*00a2430fSAndrzej Pietrasiewicz { 287*00a2430fSAndrzej Pietrasiewicz struct uvc_device *uvc = to_uvc(f); 288*00a2430fSAndrzej Pietrasiewicz struct v4l2_event v4l2_event; 289*00a2430fSAndrzej Pietrasiewicz struct uvc_event *uvc_event = (void *)&v4l2_event.u.data; 290*00a2430fSAndrzej Pietrasiewicz int ret; 291*00a2430fSAndrzej Pietrasiewicz 292*00a2430fSAndrzej Pietrasiewicz INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt); 293*00a2430fSAndrzej Pietrasiewicz 294*00a2430fSAndrzej Pietrasiewicz if (interface == uvc->control_intf) { 295*00a2430fSAndrzej Pietrasiewicz if (alt) 296*00a2430fSAndrzej Pietrasiewicz return -EINVAL; 297*00a2430fSAndrzej Pietrasiewicz 298*00a2430fSAndrzej Pietrasiewicz if (uvc->state == UVC_STATE_DISCONNECTED) { 299*00a2430fSAndrzej Pietrasiewicz memset(&v4l2_event, 0, sizeof(v4l2_event)); 300*00a2430fSAndrzej Pietrasiewicz v4l2_event.type = UVC_EVENT_CONNECT; 301*00a2430fSAndrzej Pietrasiewicz uvc_event->speed = f->config->cdev->gadget->speed; 302*00a2430fSAndrzej Pietrasiewicz v4l2_event_queue(uvc->vdev, &v4l2_event); 303*00a2430fSAndrzej Pietrasiewicz 304*00a2430fSAndrzej Pietrasiewicz uvc->state = UVC_STATE_CONNECTED; 305*00a2430fSAndrzej Pietrasiewicz } 306*00a2430fSAndrzej Pietrasiewicz 307*00a2430fSAndrzej Pietrasiewicz return 0; 308*00a2430fSAndrzej Pietrasiewicz } 309*00a2430fSAndrzej Pietrasiewicz 310*00a2430fSAndrzej Pietrasiewicz if (interface != uvc->streaming_intf) 311*00a2430fSAndrzej Pietrasiewicz return -EINVAL; 312*00a2430fSAndrzej Pietrasiewicz 313*00a2430fSAndrzej Pietrasiewicz /* TODO 314*00a2430fSAndrzej Pietrasiewicz if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep)) 315*00a2430fSAndrzej Pietrasiewicz return alt ? -EINVAL : 0; 316*00a2430fSAndrzej Pietrasiewicz */ 317*00a2430fSAndrzej Pietrasiewicz 318*00a2430fSAndrzej Pietrasiewicz switch (alt) { 319*00a2430fSAndrzej Pietrasiewicz case 0: 320*00a2430fSAndrzej Pietrasiewicz if (uvc->state != UVC_STATE_STREAMING) 321*00a2430fSAndrzej Pietrasiewicz return 0; 322*00a2430fSAndrzej Pietrasiewicz 323*00a2430fSAndrzej Pietrasiewicz if (uvc->video.ep) 324*00a2430fSAndrzej Pietrasiewicz usb_ep_disable(uvc->video.ep); 325*00a2430fSAndrzej Pietrasiewicz 326*00a2430fSAndrzej Pietrasiewicz memset(&v4l2_event, 0, sizeof(v4l2_event)); 327*00a2430fSAndrzej Pietrasiewicz v4l2_event.type = UVC_EVENT_STREAMOFF; 328*00a2430fSAndrzej Pietrasiewicz v4l2_event_queue(uvc->vdev, &v4l2_event); 329*00a2430fSAndrzej Pietrasiewicz 330*00a2430fSAndrzej Pietrasiewicz uvc->state = UVC_STATE_CONNECTED; 331*00a2430fSAndrzej Pietrasiewicz return 0; 332*00a2430fSAndrzej Pietrasiewicz 333*00a2430fSAndrzej Pietrasiewicz case 1: 334*00a2430fSAndrzej Pietrasiewicz if (uvc->state != UVC_STATE_CONNECTED) 335*00a2430fSAndrzej Pietrasiewicz return 0; 336*00a2430fSAndrzej Pietrasiewicz 337*00a2430fSAndrzej Pietrasiewicz if (uvc->video.ep) { 338*00a2430fSAndrzej Pietrasiewicz ret = config_ep_by_speed(f->config->cdev->gadget, 339*00a2430fSAndrzej Pietrasiewicz &(uvc->func), uvc->video.ep); 340*00a2430fSAndrzej Pietrasiewicz if (ret) 341*00a2430fSAndrzej Pietrasiewicz return ret; 342*00a2430fSAndrzej Pietrasiewicz usb_ep_enable(uvc->video.ep); 343*00a2430fSAndrzej Pietrasiewicz } 344*00a2430fSAndrzej Pietrasiewicz 345*00a2430fSAndrzej Pietrasiewicz memset(&v4l2_event, 0, sizeof(v4l2_event)); 346*00a2430fSAndrzej Pietrasiewicz v4l2_event.type = UVC_EVENT_STREAMON; 347*00a2430fSAndrzej Pietrasiewicz v4l2_event_queue(uvc->vdev, &v4l2_event); 348*00a2430fSAndrzej Pietrasiewicz return USB_GADGET_DELAYED_STATUS; 349*00a2430fSAndrzej Pietrasiewicz 350*00a2430fSAndrzej Pietrasiewicz default: 351*00a2430fSAndrzej Pietrasiewicz return -EINVAL; 352*00a2430fSAndrzej Pietrasiewicz } 353*00a2430fSAndrzej Pietrasiewicz } 354*00a2430fSAndrzej Pietrasiewicz 355*00a2430fSAndrzej Pietrasiewicz static void 356*00a2430fSAndrzej Pietrasiewicz uvc_function_disable(struct usb_function *f) 357*00a2430fSAndrzej Pietrasiewicz { 358*00a2430fSAndrzej Pietrasiewicz struct uvc_device *uvc = to_uvc(f); 359*00a2430fSAndrzej Pietrasiewicz struct v4l2_event v4l2_event; 360*00a2430fSAndrzej Pietrasiewicz 361*00a2430fSAndrzej Pietrasiewicz INFO(f->config->cdev, "uvc_function_disable\n"); 362*00a2430fSAndrzej Pietrasiewicz 363*00a2430fSAndrzej Pietrasiewicz memset(&v4l2_event, 0, sizeof(v4l2_event)); 364*00a2430fSAndrzej Pietrasiewicz v4l2_event.type = UVC_EVENT_DISCONNECT; 365*00a2430fSAndrzej Pietrasiewicz v4l2_event_queue(uvc->vdev, &v4l2_event); 366*00a2430fSAndrzej Pietrasiewicz 367*00a2430fSAndrzej Pietrasiewicz uvc->state = UVC_STATE_DISCONNECTED; 368*00a2430fSAndrzej Pietrasiewicz } 369*00a2430fSAndrzej Pietrasiewicz 370*00a2430fSAndrzej Pietrasiewicz /* -------------------------------------------------------------------------- 371*00a2430fSAndrzej Pietrasiewicz * Connection / disconnection 372*00a2430fSAndrzej Pietrasiewicz */ 373*00a2430fSAndrzej Pietrasiewicz 374*00a2430fSAndrzej Pietrasiewicz void 375*00a2430fSAndrzej Pietrasiewicz uvc_function_connect(struct uvc_device *uvc) 376*00a2430fSAndrzej Pietrasiewicz { 377*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = uvc->func.config->cdev; 378*00a2430fSAndrzej Pietrasiewicz int ret; 379*00a2430fSAndrzej Pietrasiewicz 380*00a2430fSAndrzej Pietrasiewicz if ((ret = usb_function_activate(&uvc->func)) < 0) 381*00a2430fSAndrzej Pietrasiewicz INFO(cdev, "UVC connect failed with %d\n", ret); 382*00a2430fSAndrzej Pietrasiewicz } 383*00a2430fSAndrzej Pietrasiewicz 384*00a2430fSAndrzej Pietrasiewicz void 385*00a2430fSAndrzej Pietrasiewicz uvc_function_disconnect(struct uvc_device *uvc) 386*00a2430fSAndrzej Pietrasiewicz { 387*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = uvc->func.config->cdev; 388*00a2430fSAndrzej Pietrasiewicz int ret; 389*00a2430fSAndrzej Pietrasiewicz 390*00a2430fSAndrzej Pietrasiewicz if ((ret = usb_function_deactivate(&uvc->func)) < 0) 391*00a2430fSAndrzej Pietrasiewicz INFO(cdev, "UVC disconnect failed with %d\n", ret); 392*00a2430fSAndrzej Pietrasiewicz } 393*00a2430fSAndrzej Pietrasiewicz 394*00a2430fSAndrzej Pietrasiewicz /* -------------------------------------------------------------------------- 395*00a2430fSAndrzej Pietrasiewicz * USB probe and disconnect 396*00a2430fSAndrzej Pietrasiewicz */ 397*00a2430fSAndrzej Pietrasiewicz 398*00a2430fSAndrzej Pietrasiewicz static int 399*00a2430fSAndrzej Pietrasiewicz uvc_register_video(struct uvc_device *uvc) 400*00a2430fSAndrzej Pietrasiewicz { 401*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = uvc->func.config->cdev; 402*00a2430fSAndrzej Pietrasiewicz struct video_device *video; 403*00a2430fSAndrzej Pietrasiewicz 404*00a2430fSAndrzej Pietrasiewicz /* TODO reference counting. */ 405*00a2430fSAndrzej Pietrasiewicz video = video_device_alloc(); 406*00a2430fSAndrzej Pietrasiewicz if (video == NULL) 407*00a2430fSAndrzej Pietrasiewicz return -ENOMEM; 408*00a2430fSAndrzej Pietrasiewicz 409*00a2430fSAndrzej Pietrasiewicz video->v4l2_dev = &uvc->v4l2_dev; 410*00a2430fSAndrzej Pietrasiewicz video->fops = &uvc_v4l2_fops; 411*00a2430fSAndrzej Pietrasiewicz video->release = video_device_release; 412*00a2430fSAndrzej Pietrasiewicz strlcpy(video->name, cdev->gadget->name, sizeof(video->name)); 413*00a2430fSAndrzej Pietrasiewicz 414*00a2430fSAndrzej Pietrasiewicz uvc->vdev = video; 415*00a2430fSAndrzej Pietrasiewicz video_set_drvdata(video, uvc); 416*00a2430fSAndrzej Pietrasiewicz 417*00a2430fSAndrzej Pietrasiewicz return video_register_device(video, VFL_TYPE_GRABBER, -1); 418*00a2430fSAndrzej Pietrasiewicz } 419*00a2430fSAndrzej Pietrasiewicz 420*00a2430fSAndrzej Pietrasiewicz #define UVC_COPY_DESCRIPTOR(mem, dst, desc) \ 421*00a2430fSAndrzej Pietrasiewicz do { \ 422*00a2430fSAndrzej Pietrasiewicz memcpy(mem, desc, (desc)->bLength); \ 423*00a2430fSAndrzej Pietrasiewicz *(dst)++ = mem; \ 424*00a2430fSAndrzej Pietrasiewicz mem += (desc)->bLength; \ 425*00a2430fSAndrzej Pietrasiewicz } while (0); 426*00a2430fSAndrzej Pietrasiewicz 427*00a2430fSAndrzej Pietrasiewicz #define UVC_COPY_DESCRIPTORS(mem, dst, src) \ 428*00a2430fSAndrzej Pietrasiewicz do { \ 429*00a2430fSAndrzej Pietrasiewicz const struct usb_descriptor_header * const *__src; \ 430*00a2430fSAndrzej Pietrasiewicz for (__src = src; *__src; ++__src) { \ 431*00a2430fSAndrzej Pietrasiewicz memcpy(mem, *__src, (*__src)->bLength); \ 432*00a2430fSAndrzej Pietrasiewicz *dst++ = mem; \ 433*00a2430fSAndrzej Pietrasiewicz mem += (*__src)->bLength; \ 434*00a2430fSAndrzej Pietrasiewicz } \ 435*00a2430fSAndrzej Pietrasiewicz } while (0) 436*00a2430fSAndrzej Pietrasiewicz 437*00a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header ** __init 438*00a2430fSAndrzej Pietrasiewicz uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) 439*00a2430fSAndrzej Pietrasiewicz { 440*00a2430fSAndrzej Pietrasiewicz struct uvc_input_header_descriptor *uvc_streaming_header; 441*00a2430fSAndrzej Pietrasiewicz struct uvc_header_descriptor *uvc_control_header; 442*00a2430fSAndrzej Pietrasiewicz const struct uvc_descriptor_header * const *uvc_control_desc; 443*00a2430fSAndrzej Pietrasiewicz const struct uvc_descriptor_header * const *uvc_streaming_cls; 444*00a2430fSAndrzej Pietrasiewicz const struct usb_descriptor_header * const *uvc_streaming_std; 445*00a2430fSAndrzej Pietrasiewicz const struct usb_descriptor_header * const *src; 446*00a2430fSAndrzej Pietrasiewicz struct usb_descriptor_header **dst; 447*00a2430fSAndrzej Pietrasiewicz struct usb_descriptor_header **hdr; 448*00a2430fSAndrzej Pietrasiewicz unsigned int control_size; 449*00a2430fSAndrzej Pietrasiewicz unsigned int streaming_size; 450*00a2430fSAndrzej Pietrasiewicz unsigned int n_desc; 451*00a2430fSAndrzej Pietrasiewicz unsigned int bytes; 452*00a2430fSAndrzej Pietrasiewicz void *mem; 453*00a2430fSAndrzej Pietrasiewicz 454*00a2430fSAndrzej Pietrasiewicz switch (speed) { 455*00a2430fSAndrzej Pietrasiewicz case USB_SPEED_SUPER: 456*00a2430fSAndrzej Pietrasiewicz uvc_control_desc = uvc->desc.ss_control; 457*00a2430fSAndrzej Pietrasiewicz uvc_streaming_cls = uvc->desc.ss_streaming; 458*00a2430fSAndrzej Pietrasiewicz uvc_streaming_std = uvc_ss_streaming; 459*00a2430fSAndrzej Pietrasiewicz break; 460*00a2430fSAndrzej Pietrasiewicz 461*00a2430fSAndrzej Pietrasiewicz case USB_SPEED_HIGH: 462*00a2430fSAndrzej Pietrasiewicz uvc_control_desc = uvc->desc.fs_control; 463*00a2430fSAndrzej Pietrasiewicz uvc_streaming_cls = uvc->desc.hs_streaming; 464*00a2430fSAndrzej Pietrasiewicz uvc_streaming_std = uvc_hs_streaming; 465*00a2430fSAndrzej Pietrasiewicz break; 466*00a2430fSAndrzej Pietrasiewicz 467*00a2430fSAndrzej Pietrasiewicz case USB_SPEED_FULL: 468*00a2430fSAndrzej Pietrasiewicz default: 469*00a2430fSAndrzej Pietrasiewicz uvc_control_desc = uvc->desc.fs_control; 470*00a2430fSAndrzej Pietrasiewicz uvc_streaming_cls = uvc->desc.fs_streaming; 471*00a2430fSAndrzej Pietrasiewicz uvc_streaming_std = uvc_fs_streaming; 472*00a2430fSAndrzej Pietrasiewicz break; 473*00a2430fSAndrzej Pietrasiewicz } 474*00a2430fSAndrzej Pietrasiewicz 475*00a2430fSAndrzej Pietrasiewicz /* Descriptors layout 476*00a2430fSAndrzej Pietrasiewicz * 477*00a2430fSAndrzej Pietrasiewicz * uvc_iad 478*00a2430fSAndrzej Pietrasiewicz * uvc_control_intf 479*00a2430fSAndrzej Pietrasiewicz * Class-specific UVC control descriptors 480*00a2430fSAndrzej Pietrasiewicz * uvc_control_ep 481*00a2430fSAndrzej Pietrasiewicz * uvc_control_cs_ep 482*00a2430fSAndrzej Pietrasiewicz * uvc_ss_control_comp (for SS only) 483*00a2430fSAndrzej Pietrasiewicz * uvc_streaming_intf_alt0 484*00a2430fSAndrzej Pietrasiewicz * Class-specific UVC streaming descriptors 485*00a2430fSAndrzej Pietrasiewicz * uvc_{fs|hs}_streaming 486*00a2430fSAndrzej Pietrasiewicz */ 487*00a2430fSAndrzej Pietrasiewicz 488*00a2430fSAndrzej Pietrasiewicz /* Count descriptors and compute their size. */ 489*00a2430fSAndrzej Pietrasiewicz control_size = 0; 490*00a2430fSAndrzej Pietrasiewicz streaming_size = 0; 491*00a2430fSAndrzej Pietrasiewicz bytes = uvc_iad.bLength + uvc_control_intf.bLength 492*00a2430fSAndrzej Pietrasiewicz + uvc_control_ep.bLength + uvc_control_cs_ep.bLength 493*00a2430fSAndrzej Pietrasiewicz + uvc_streaming_intf_alt0.bLength; 494*00a2430fSAndrzej Pietrasiewicz 495*00a2430fSAndrzej Pietrasiewicz if (speed == USB_SPEED_SUPER) { 496*00a2430fSAndrzej Pietrasiewicz bytes += uvc_ss_control_comp.bLength; 497*00a2430fSAndrzej Pietrasiewicz n_desc = 6; 498*00a2430fSAndrzej Pietrasiewicz } else { 499*00a2430fSAndrzej Pietrasiewicz n_desc = 5; 500*00a2430fSAndrzej Pietrasiewicz } 501*00a2430fSAndrzej Pietrasiewicz 502*00a2430fSAndrzej Pietrasiewicz for (src = (const struct usb_descriptor_header **)uvc_control_desc; 503*00a2430fSAndrzej Pietrasiewicz *src; ++src) { 504*00a2430fSAndrzej Pietrasiewicz control_size += (*src)->bLength; 505*00a2430fSAndrzej Pietrasiewicz bytes += (*src)->bLength; 506*00a2430fSAndrzej Pietrasiewicz n_desc++; 507*00a2430fSAndrzej Pietrasiewicz } 508*00a2430fSAndrzej Pietrasiewicz for (src = (const struct usb_descriptor_header **)uvc_streaming_cls; 509*00a2430fSAndrzej Pietrasiewicz *src; ++src) { 510*00a2430fSAndrzej Pietrasiewicz streaming_size += (*src)->bLength; 511*00a2430fSAndrzej Pietrasiewicz bytes += (*src)->bLength; 512*00a2430fSAndrzej Pietrasiewicz n_desc++; 513*00a2430fSAndrzej Pietrasiewicz } 514*00a2430fSAndrzej Pietrasiewicz for (src = uvc_streaming_std; *src; ++src) { 515*00a2430fSAndrzej Pietrasiewicz bytes += (*src)->bLength; 516*00a2430fSAndrzej Pietrasiewicz n_desc++; 517*00a2430fSAndrzej Pietrasiewicz } 518*00a2430fSAndrzej Pietrasiewicz 519*00a2430fSAndrzej Pietrasiewicz mem = kmalloc((n_desc + 1) * sizeof(*src) + bytes, GFP_KERNEL); 520*00a2430fSAndrzej Pietrasiewicz if (mem == NULL) 521*00a2430fSAndrzej Pietrasiewicz return NULL; 522*00a2430fSAndrzej Pietrasiewicz 523*00a2430fSAndrzej Pietrasiewicz hdr = mem; 524*00a2430fSAndrzej Pietrasiewicz dst = mem; 525*00a2430fSAndrzej Pietrasiewicz mem += (n_desc + 1) * sizeof(*src); 526*00a2430fSAndrzej Pietrasiewicz 527*00a2430fSAndrzej Pietrasiewicz /* Copy the descriptors. */ 528*00a2430fSAndrzej Pietrasiewicz UVC_COPY_DESCRIPTOR(mem, dst, &uvc_iad); 529*00a2430fSAndrzej Pietrasiewicz UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_intf); 530*00a2430fSAndrzej Pietrasiewicz 531*00a2430fSAndrzej Pietrasiewicz uvc_control_header = mem; 532*00a2430fSAndrzej Pietrasiewicz UVC_COPY_DESCRIPTORS(mem, dst, 533*00a2430fSAndrzej Pietrasiewicz (const struct usb_descriptor_header **)uvc_control_desc); 534*00a2430fSAndrzej Pietrasiewicz uvc_control_header->wTotalLength = cpu_to_le16(control_size); 535*00a2430fSAndrzej Pietrasiewicz uvc_control_header->bInCollection = 1; 536*00a2430fSAndrzej Pietrasiewicz uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf; 537*00a2430fSAndrzej Pietrasiewicz 538*00a2430fSAndrzej Pietrasiewicz UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep); 539*00a2430fSAndrzej Pietrasiewicz if (speed == USB_SPEED_SUPER) 540*00a2430fSAndrzej Pietrasiewicz UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp); 541*00a2430fSAndrzej Pietrasiewicz 542*00a2430fSAndrzej Pietrasiewicz UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep); 543*00a2430fSAndrzej Pietrasiewicz UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0); 544*00a2430fSAndrzej Pietrasiewicz 545*00a2430fSAndrzej Pietrasiewicz uvc_streaming_header = mem; 546*00a2430fSAndrzej Pietrasiewicz UVC_COPY_DESCRIPTORS(mem, dst, 547*00a2430fSAndrzej Pietrasiewicz (const struct usb_descriptor_header**)uvc_streaming_cls); 548*00a2430fSAndrzej Pietrasiewicz uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size); 549*00a2430fSAndrzej Pietrasiewicz uvc_streaming_header->bEndpointAddress = uvc->video.ep->address; 550*00a2430fSAndrzej Pietrasiewicz 551*00a2430fSAndrzej Pietrasiewicz UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std); 552*00a2430fSAndrzej Pietrasiewicz 553*00a2430fSAndrzej Pietrasiewicz *dst = NULL; 554*00a2430fSAndrzej Pietrasiewicz return hdr; 555*00a2430fSAndrzej Pietrasiewicz } 556*00a2430fSAndrzej Pietrasiewicz 557*00a2430fSAndrzej Pietrasiewicz static void 558*00a2430fSAndrzej Pietrasiewicz uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) 559*00a2430fSAndrzej Pietrasiewicz { 560*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = c->cdev; 561*00a2430fSAndrzej Pietrasiewicz struct uvc_device *uvc = to_uvc(f); 562*00a2430fSAndrzej Pietrasiewicz 563*00a2430fSAndrzej Pietrasiewicz INFO(cdev, "uvc_function_unbind\n"); 564*00a2430fSAndrzej Pietrasiewicz 565*00a2430fSAndrzej Pietrasiewicz video_unregister_device(uvc->vdev); 566*00a2430fSAndrzej Pietrasiewicz v4l2_device_unregister(&uvc->v4l2_dev); 567*00a2430fSAndrzej Pietrasiewicz uvc->control_ep->driver_data = NULL; 568*00a2430fSAndrzej Pietrasiewicz uvc->video.ep->driver_data = NULL; 569*00a2430fSAndrzej Pietrasiewicz 570*00a2430fSAndrzej Pietrasiewicz uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id = 0; 571*00a2430fSAndrzej Pietrasiewicz usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); 572*00a2430fSAndrzej Pietrasiewicz kfree(uvc->control_buf); 573*00a2430fSAndrzej Pietrasiewicz 574*00a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f); 575*00a2430fSAndrzej Pietrasiewicz 576*00a2430fSAndrzej Pietrasiewicz kfree(uvc); 577*00a2430fSAndrzej Pietrasiewicz } 578*00a2430fSAndrzej Pietrasiewicz 579*00a2430fSAndrzej Pietrasiewicz static int __init 580*00a2430fSAndrzej Pietrasiewicz uvc_function_bind(struct usb_configuration *c, struct usb_function *f) 581*00a2430fSAndrzej Pietrasiewicz { 582*00a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = c->cdev; 583*00a2430fSAndrzej Pietrasiewicz struct uvc_device *uvc = to_uvc(f); 584*00a2430fSAndrzej Pietrasiewicz unsigned int max_packet_mult; 585*00a2430fSAndrzej Pietrasiewicz unsigned int max_packet_size; 586*00a2430fSAndrzej Pietrasiewicz struct usb_ep *ep; 587*00a2430fSAndrzej Pietrasiewicz int ret = -EINVAL; 588*00a2430fSAndrzej Pietrasiewicz 589*00a2430fSAndrzej Pietrasiewicz INFO(cdev, "uvc_function_bind\n"); 590*00a2430fSAndrzej Pietrasiewicz 591*00a2430fSAndrzej Pietrasiewicz /* Sanity check the streaming endpoint module parameters. 592*00a2430fSAndrzej Pietrasiewicz */ 593*00a2430fSAndrzej Pietrasiewicz streaming_interval = clamp(streaming_interval, 1U, 16U); 594*00a2430fSAndrzej Pietrasiewicz streaming_maxpacket = clamp(streaming_maxpacket, 1U, 3072U); 595*00a2430fSAndrzej Pietrasiewicz streaming_maxburst = min(streaming_maxburst, 15U); 596*00a2430fSAndrzej Pietrasiewicz 597*00a2430fSAndrzej Pietrasiewicz /* Fill in the FS/HS/SS Video Streaming specific descriptors from the 598*00a2430fSAndrzej Pietrasiewicz * module parameters. 599*00a2430fSAndrzej Pietrasiewicz * 600*00a2430fSAndrzej Pietrasiewicz * NOTE: We assume that the user knows what they are doing and won't 601*00a2430fSAndrzej Pietrasiewicz * give parameters that their UDC doesn't support. 602*00a2430fSAndrzej Pietrasiewicz */ 603*00a2430fSAndrzej Pietrasiewicz if (streaming_maxpacket <= 1024) { 604*00a2430fSAndrzej Pietrasiewicz max_packet_mult = 1; 605*00a2430fSAndrzej Pietrasiewicz max_packet_size = streaming_maxpacket; 606*00a2430fSAndrzej Pietrasiewicz } else if (streaming_maxpacket <= 2048) { 607*00a2430fSAndrzej Pietrasiewicz max_packet_mult = 2; 608*00a2430fSAndrzej Pietrasiewicz max_packet_size = streaming_maxpacket / 2; 609*00a2430fSAndrzej Pietrasiewicz } else { 610*00a2430fSAndrzej Pietrasiewicz max_packet_mult = 3; 611*00a2430fSAndrzej Pietrasiewicz max_packet_size = streaming_maxpacket / 3; 612*00a2430fSAndrzej Pietrasiewicz } 613*00a2430fSAndrzej Pietrasiewicz 614*00a2430fSAndrzej Pietrasiewicz uvc_fs_streaming_ep.wMaxPacketSize = min(streaming_maxpacket, 1023U); 615*00a2430fSAndrzej Pietrasiewicz uvc_fs_streaming_ep.bInterval = streaming_interval; 616*00a2430fSAndrzej Pietrasiewicz 617*00a2430fSAndrzej Pietrasiewicz uvc_hs_streaming_ep.wMaxPacketSize = max_packet_size; 618*00a2430fSAndrzej Pietrasiewicz uvc_hs_streaming_ep.wMaxPacketSize |= ((max_packet_mult - 1) << 11); 619*00a2430fSAndrzej Pietrasiewicz uvc_hs_streaming_ep.bInterval = streaming_interval; 620*00a2430fSAndrzej Pietrasiewicz 621*00a2430fSAndrzej Pietrasiewicz uvc_ss_streaming_ep.wMaxPacketSize = max_packet_size; 622*00a2430fSAndrzej Pietrasiewicz uvc_ss_streaming_ep.bInterval = streaming_interval; 623*00a2430fSAndrzej Pietrasiewicz uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; 624*00a2430fSAndrzej Pietrasiewicz uvc_ss_streaming_comp.bMaxBurst = streaming_maxburst; 625*00a2430fSAndrzej Pietrasiewicz uvc_ss_streaming_comp.wBytesPerInterval = 626*00a2430fSAndrzej Pietrasiewicz max_packet_size * max_packet_mult * streaming_maxburst; 627*00a2430fSAndrzej Pietrasiewicz 628*00a2430fSAndrzej Pietrasiewicz /* Allocate endpoints. */ 629*00a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); 630*00a2430fSAndrzej Pietrasiewicz if (!ep) { 631*00a2430fSAndrzej Pietrasiewicz INFO(cdev, "Unable to allocate control EP\n"); 632*00a2430fSAndrzej Pietrasiewicz goto error; 633*00a2430fSAndrzej Pietrasiewicz } 634*00a2430fSAndrzej Pietrasiewicz uvc->control_ep = ep; 635*00a2430fSAndrzej Pietrasiewicz ep->driver_data = uvc; 636*00a2430fSAndrzej Pietrasiewicz 637*00a2430fSAndrzej Pietrasiewicz if (gadget_is_superspeed(c->cdev->gadget)) 638*00a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep, 639*00a2430fSAndrzej Pietrasiewicz &uvc_ss_streaming_comp); 640*00a2430fSAndrzej Pietrasiewicz else if (gadget_is_dualspeed(cdev->gadget)) 641*00a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep); 642*00a2430fSAndrzej Pietrasiewicz else 643*00a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); 644*00a2430fSAndrzej Pietrasiewicz 645*00a2430fSAndrzej Pietrasiewicz if (!ep) { 646*00a2430fSAndrzej Pietrasiewicz INFO(cdev, "Unable to allocate streaming EP\n"); 647*00a2430fSAndrzej Pietrasiewicz goto error; 648*00a2430fSAndrzej Pietrasiewicz } 649*00a2430fSAndrzej Pietrasiewicz uvc->video.ep = ep; 650*00a2430fSAndrzej Pietrasiewicz ep->driver_data = uvc; 651*00a2430fSAndrzej Pietrasiewicz 652*00a2430fSAndrzej Pietrasiewicz uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address; 653*00a2430fSAndrzej Pietrasiewicz uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; 654*00a2430fSAndrzej Pietrasiewicz uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; 655*00a2430fSAndrzej Pietrasiewicz 656*00a2430fSAndrzej Pietrasiewicz /* Allocate interface IDs. */ 657*00a2430fSAndrzej Pietrasiewicz if ((ret = usb_interface_id(c, f)) < 0) 658*00a2430fSAndrzej Pietrasiewicz goto error; 659*00a2430fSAndrzej Pietrasiewicz uvc_iad.bFirstInterface = ret; 660*00a2430fSAndrzej Pietrasiewicz uvc_control_intf.bInterfaceNumber = ret; 661*00a2430fSAndrzej Pietrasiewicz uvc->control_intf = ret; 662*00a2430fSAndrzej Pietrasiewicz 663*00a2430fSAndrzej Pietrasiewicz if ((ret = usb_interface_id(c, f)) < 0) 664*00a2430fSAndrzej Pietrasiewicz goto error; 665*00a2430fSAndrzej Pietrasiewicz uvc_streaming_intf_alt0.bInterfaceNumber = ret; 666*00a2430fSAndrzej Pietrasiewicz uvc_streaming_intf_alt1.bInterfaceNumber = ret; 667*00a2430fSAndrzej Pietrasiewicz uvc->streaming_intf = ret; 668*00a2430fSAndrzej Pietrasiewicz 669*00a2430fSAndrzej Pietrasiewicz /* Copy descriptors */ 670*00a2430fSAndrzej Pietrasiewicz f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL); 671*00a2430fSAndrzej Pietrasiewicz if (gadget_is_dualspeed(cdev->gadget)) 672*00a2430fSAndrzej Pietrasiewicz f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); 673*00a2430fSAndrzej Pietrasiewicz if (gadget_is_superspeed(c->cdev->gadget)) 674*00a2430fSAndrzej Pietrasiewicz f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); 675*00a2430fSAndrzej Pietrasiewicz 676*00a2430fSAndrzej Pietrasiewicz /* Preallocate control endpoint request. */ 677*00a2430fSAndrzej Pietrasiewicz uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); 678*00a2430fSAndrzej Pietrasiewicz uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL); 679*00a2430fSAndrzej Pietrasiewicz if (uvc->control_req == NULL || uvc->control_buf == NULL) { 680*00a2430fSAndrzej Pietrasiewicz ret = -ENOMEM; 681*00a2430fSAndrzej Pietrasiewicz goto error; 682*00a2430fSAndrzej Pietrasiewicz } 683*00a2430fSAndrzej Pietrasiewicz 684*00a2430fSAndrzej Pietrasiewicz uvc->control_req->buf = uvc->control_buf; 685*00a2430fSAndrzej Pietrasiewicz uvc->control_req->complete = uvc_function_ep0_complete; 686*00a2430fSAndrzej Pietrasiewicz uvc->control_req->context = uvc; 687*00a2430fSAndrzej Pietrasiewicz 688*00a2430fSAndrzej Pietrasiewicz /* Avoid letting this gadget enumerate until the userspace server is 689*00a2430fSAndrzej Pietrasiewicz * active. 690*00a2430fSAndrzej Pietrasiewicz */ 691*00a2430fSAndrzej Pietrasiewicz if ((ret = usb_function_deactivate(f)) < 0) 692*00a2430fSAndrzej Pietrasiewicz goto error; 693*00a2430fSAndrzej Pietrasiewicz 694*00a2430fSAndrzej Pietrasiewicz if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) { 695*00a2430fSAndrzej Pietrasiewicz printk(KERN_INFO "v4l2_device_register failed\n"); 696*00a2430fSAndrzej Pietrasiewicz goto error; 697*00a2430fSAndrzej Pietrasiewicz } 698*00a2430fSAndrzej Pietrasiewicz 699*00a2430fSAndrzej Pietrasiewicz /* Initialise video. */ 700*00a2430fSAndrzej Pietrasiewicz ret = uvc_video_init(&uvc->video); 701*00a2430fSAndrzej Pietrasiewicz if (ret < 0) 702*00a2430fSAndrzej Pietrasiewicz goto error; 703*00a2430fSAndrzej Pietrasiewicz 704*00a2430fSAndrzej Pietrasiewicz /* Register a V4L2 device. */ 705*00a2430fSAndrzej Pietrasiewicz ret = uvc_register_video(uvc); 706*00a2430fSAndrzej Pietrasiewicz if (ret < 0) { 707*00a2430fSAndrzej Pietrasiewicz printk(KERN_INFO "Unable to register video device\n"); 708*00a2430fSAndrzej Pietrasiewicz goto error; 709*00a2430fSAndrzej Pietrasiewicz } 710*00a2430fSAndrzej Pietrasiewicz 711*00a2430fSAndrzej Pietrasiewicz return 0; 712*00a2430fSAndrzej Pietrasiewicz 713*00a2430fSAndrzej Pietrasiewicz error: 714*00a2430fSAndrzej Pietrasiewicz v4l2_device_unregister(&uvc->v4l2_dev); 715*00a2430fSAndrzej Pietrasiewicz if (uvc->vdev) 716*00a2430fSAndrzej Pietrasiewicz video_device_release(uvc->vdev); 717*00a2430fSAndrzej Pietrasiewicz 718*00a2430fSAndrzej Pietrasiewicz if (uvc->control_ep) 719*00a2430fSAndrzej Pietrasiewicz uvc->control_ep->driver_data = NULL; 720*00a2430fSAndrzej Pietrasiewicz if (uvc->video.ep) 721*00a2430fSAndrzej Pietrasiewicz uvc->video.ep->driver_data = NULL; 722*00a2430fSAndrzej Pietrasiewicz 723*00a2430fSAndrzej Pietrasiewicz if (uvc->control_req) { 724*00a2430fSAndrzej Pietrasiewicz usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); 725*00a2430fSAndrzej Pietrasiewicz kfree(uvc->control_buf); 726*00a2430fSAndrzej Pietrasiewicz } 727*00a2430fSAndrzej Pietrasiewicz 728*00a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f); 729*00a2430fSAndrzej Pietrasiewicz return ret; 730*00a2430fSAndrzej Pietrasiewicz } 731*00a2430fSAndrzej Pietrasiewicz 732*00a2430fSAndrzej Pietrasiewicz /* -------------------------------------------------------------------------- 733*00a2430fSAndrzej Pietrasiewicz * USB gadget function 734*00a2430fSAndrzej Pietrasiewicz */ 735*00a2430fSAndrzej Pietrasiewicz 736*00a2430fSAndrzej Pietrasiewicz /** 737*00a2430fSAndrzej Pietrasiewicz * uvc_bind_config - add a UVC function to a configuration 738*00a2430fSAndrzej Pietrasiewicz * @c: the configuration to support the UVC instance 739*00a2430fSAndrzej Pietrasiewicz * Context: single threaded during gadget setup 740*00a2430fSAndrzej Pietrasiewicz * 741*00a2430fSAndrzej Pietrasiewicz * Returns zero on success, else negative errno. 742*00a2430fSAndrzej Pietrasiewicz * 743*00a2430fSAndrzej Pietrasiewicz * Caller must have called @uvc_setup(). Caller is also responsible for 744*00a2430fSAndrzej Pietrasiewicz * calling @uvc_cleanup() before module unload. 745*00a2430fSAndrzej Pietrasiewicz */ 746*00a2430fSAndrzej Pietrasiewicz int __init 747*00a2430fSAndrzej Pietrasiewicz uvc_bind_config(struct usb_configuration *c, 748*00a2430fSAndrzej Pietrasiewicz const struct uvc_descriptor_header * const *fs_control, 749*00a2430fSAndrzej Pietrasiewicz const struct uvc_descriptor_header * const *ss_control, 750*00a2430fSAndrzej Pietrasiewicz const struct uvc_descriptor_header * const *fs_streaming, 751*00a2430fSAndrzej Pietrasiewicz const struct uvc_descriptor_header * const *hs_streaming, 752*00a2430fSAndrzej Pietrasiewicz const struct uvc_descriptor_header * const *ss_streaming) 753*00a2430fSAndrzej Pietrasiewicz { 754*00a2430fSAndrzej Pietrasiewicz struct uvc_device *uvc; 755*00a2430fSAndrzej Pietrasiewicz int ret = 0; 756*00a2430fSAndrzej Pietrasiewicz 757*00a2430fSAndrzej Pietrasiewicz /* TODO Check if the USB device controller supports the required 758*00a2430fSAndrzej Pietrasiewicz * features. 759*00a2430fSAndrzej Pietrasiewicz */ 760*00a2430fSAndrzej Pietrasiewicz if (!gadget_is_dualspeed(c->cdev->gadget)) 761*00a2430fSAndrzej Pietrasiewicz return -EINVAL; 762*00a2430fSAndrzej Pietrasiewicz 763*00a2430fSAndrzej Pietrasiewicz uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); 764*00a2430fSAndrzej Pietrasiewicz if (uvc == NULL) 765*00a2430fSAndrzej Pietrasiewicz return -ENOMEM; 766*00a2430fSAndrzej Pietrasiewicz 767*00a2430fSAndrzej Pietrasiewicz uvc->state = UVC_STATE_DISCONNECTED; 768*00a2430fSAndrzej Pietrasiewicz 769*00a2430fSAndrzej Pietrasiewicz /* Validate the descriptors. */ 770*00a2430fSAndrzej Pietrasiewicz if (fs_control == NULL || fs_control[0] == NULL || 771*00a2430fSAndrzej Pietrasiewicz fs_control[0]->bDescriptorSubType != UVC_VC_HEADER) 772*00a2430fSAndrzej Pietrasiewicz goto error; 773*00a2430fSAndrzej Pietrasiewicz 774*00a2430fSAndrzej Pietrasiewicz if (ss_control == NULL || ss_control[0] == NULL || 775*00a2430fSAndrzej Pietrasiewicz ss_control[0]->bDescriptorSubType != UVC_VC_HEADER) 776*00a2430fSAndrzej Pietrasiewicz goto error; 777*00a2430fSAndrzej Pietrasiewicz 778*00a2430fSAndrzej Pietrasiewicz if (fs_streaming == NULL || fs_streaming[0] == NULL || 779*00a2430fSAndrzej Pietrasiewicz fs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) 780*00a2430fSAndrzej Pietrasiewicz goto error; 781*00a2430fSAndrzej Pietrasiewicz 782*00a2430fSAndrzej Pietrasiewicz if (hs_streaming == NULL || hs_streaming[0] == NULL || 783*00a2430fSAndrzej Pietrasiewicz hs_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) 784*00a2430fSAndrzej Pietrasiewicz goto error; 785*00a2430fSAndrzej Pietrasiewicz 786*00a2430fSAndrzej Pietrasiewicz if (ss_streaming == NULL || ss_streaming[0] == NULL || 787*00a2430fSAndrzej Pietrasiewicz ss_streaming[0]->bDescriptorSubType != UVC_VS_INPUT_HEADER) 788*00a2430fSAndrzej Pietrasiewicz goto error; 789*00a2430fSAndrzej Pietrasiewicz 790*00a2430fSAndrzej Pietrasiewicz uvc->desc.fs_control = fs_control; 791*00a2430fSAndrzej Pietrasiewicz uvc->desc.ss_control = ss_control; 792*00a2430fSAndrzej Pietrasiewicz uvc->desc.fs_streaming = fs_streaming; 793*00a2430fSAndrzej Pietrasiewicz uvc->desc.hs_streaming = hs_streaming; 794*00a2430fSAndrzej Pietrasiewicz uvc->desc.ss_streaming = ss_streaming; 795*00a2430fSAndrzej Pietrasiewicz 796*00a2430fSAndrzej Pietrasiewicz /* String descriptors are global, we only need to allocate string IDs 797*00a2430fSAndrzej Pietrasiewicz * for the first UVC function. UVC functions beyond the first (if any) 798*00a2430fSAndrzej Pietrasiewicz * will reuse the same IDs. 799*00a2430fSAndrzej Pietrasiewicz */ 800*00a2430fSAndrzej Pietrasiewicz if (uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id == 0) { 801*00a2430fSAndrzej Pietrasiewicz ret = usb_string_ids_tab(c->cdev, uvc_en_us_strings); 802*00a2430fSAndrzej Pietrasiewicz if (ret) 803*00a2430fSAndrzej Pietrasiewicz goto error; 804*00a2430fSAndrzej Pietrasiewicz uvc_iad.iFunction = 805*00a2430fSAndrzej Pietrasiewicz uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; 806*00a2430fSAndrzej Pietrasiewicz uvc_control_intf.iInterface = 807*00a2430fSAndrzej Pietrasiewicz uvc_en_us_strings[UVC_STRING_CONTROL_IDX].id; 808*00a2430fSAndrzej Pietrasiewicz ret = uvc_en_us_strings[UVC_STRING_STREAMING_IDX].id; 809*00a2430fSAndrzej Pietrasiewicz uvc_streaming_intf_alt0.iInterface = ret; 810*00a2430fSAndrzej Pietrasiewicz uvc_streaming_intf_alt1.iInterface = ret; 811*00a2430fSAndrzej Pietrasiewicz } 812*00a2430fSAndrzej Pietrasiewicz 813*00a2430fSAndrzej Pietrasiewicz /* Register the function. */ 814*00a2430fSAndrzej Pietrasiewicz uvc->func.name = "uvc"; 815*00a2430fSAndrzej Pietrasiewicz uvc->func.strings = uvc_function_strings; 816*00a2430fSAndrzej Pietrasiewicz uvc->func.bind = uvc_function_bind; 817*00a2430fSAndrzej Pietrasiewicz uvc->func.unbind = uvc_function_unbind; 818*00a2430fSAndrzej Pietrasiewicz uvc->func.get_alt = uvc_function_get_alt; 819*00a2430fSAndrzej Pietrasiewicz uvc->func.set_alt = uvc_function_set_alt; 820*00a2430fSAndrzej Pietrasiewicz uvc->func.disable = uvc_function_disable; 821*00a2430fSAndrzej Pietrasiewicz uvc->func.setup = uvc_function_setup; 822*00a2430fSAndrzej Pietrasiewicz 823*00a2430fSAndrzej Pietrasiewicz ret = usb_add_function(c, &uvc->func); 824*00a2430fSAndrzej Pietrasiewicz if (ret) 825*00a2430fSAndrzej Pietrasiewicz kfree(uvc); 826*00a2430fSAndrzej Pietrasiewicz 827*00a2430fSAndrzej Pietrasiewicz return ret; 828*00a2430fSAndrzej Pietrasiewicz 829*00a2430fSAndrzej Pietrasiewicz error: 830*00a2430fSAndrzej Pietrasiewicz kfree(uvc); 831*00a2430fSAndrzej Pietrasiewicz return ret; 832*00a2430fSAndrzej Pietrasiewicz } 833*00a2430fSAndrzej Pietrasiewicz 834*00a2430fSAndrzej Pietrasiewicz module_param_named(trace, uvc_gadget_trace_param, uint, S_IRUGO|S_IWUSR); 835*00a2430fSAndrzej Pietrasiewicz MODULE_PARM_DESC(trace, "Trace level bitmask"); 836*00a2430fSAndrzej Pietrasiewicz 837