Webcam使用的是usb_composite_driver,这个是gadget功能层的,也就是g_webcam.ko是模拟出一个模拟(信号)摄像头,但是看了代码以及实际操作后发现跟OTG没有关系。作者的意思也好像是要通过上层应用程序来使用g_webcam.ko产生的/dev/videoX,自己去实现usb webcam。
但现在要做USB webcam,也要学习OTG、GADGET。通过APP来实现,数据走的路径较长。有想法:去完成一个UVC驱动,同时包含OTG/GADGET驱动,这样,就相当于做个中继。PC USB HOST端要过的数据,首先给驱动,驱动通过UVC/V4L2去访问摄像头,然后将返回的数据回传给PC。
因此,首先从OTG这边开始。看了下,之前的g_file_storage.ko使用的是usb_gadget_driver。同时之前测试成功也说明有OTG部分。下面就仿照它来写个OTG/GADGET驱动。先给出代码,因为是仿照学习,所以代码乱,也没有自己的思路,都是拷贝、修改的。usb设备描述符这些信息,先将真实USB摄像头上的设备描述符的内容填写上去测试。编译生成mygadget.ko,加载驱动,接上OTG线,电脑端会发现设备。
同时也能正确识别是CAM类。(当然)
第一步,走到这算是完成了,能够让OTG使用起来,并获取到设备描述符信息。
部分输出信息:
[ 102.401713] *** s3c_udc_irq : GINTSTS=0x54888028(on state WAIT_FOR_SETUP), GINTMSK : 0x800c3800, DAINT : 0x10000, DAINTMSK : 0x10001
[ 102.401824] *** process_ep_out_intr: EP OUT interrupt : DAINT = 0x10000
[ 102.401879] EP0-OUT : DOEPINT = 0x8
[ 102.402935] SETUP packet(transaction) arrived
[ 102.407354] s3c_handle_ep0: WAIT_FOR_SETUP
[ 102.411428] nuke: ep0-control c09120d8
[ 102.415156] s3c_fifo_read: bytes=8, ep_index=0
[ 102.419576] s3c_ep0_setup: bRequestType = 0x80(IN), bRequest = 0x6 wLength = 0x40, wValue = 0x100, wIndex= 0x0
[ 102.429539] s3c_ep0_setup: *** USB_REQ_GET_DESCRIPTOR
[ 102.434565] s3c_ep0_setup: usb_ctrlrequest will be passed to fsg_setup()
[ 102.441239] mygadget_setup: ctrl->wLength=64
[ 102.445484] mygadget_setup: ep0-setup, length 8:
[ 102.450087] mygadget_standard_setup_req: USB_REQ_GET_DESCRIPTOR
[ 102.455970] mygadget_standard_setup_req: USB_DT_DEVICE
...
[ 102.981476] *** s3c_udc_irq : GINTSTS=0x54888028(on state WAIT_FOR_SETUP), GINTMSK : 0x800c3800, DAINT : 0x10000, DAINTMSK : 0x10001
[ 102.994801] *** process_ep_out_intr: EP OUT interrupt : DAINT = 0x10000
[ 103.001387] EP0-OUT : DOEPINT = 0x8
[ 103.004939] SETUP packet(transaction) arrived
[ 103.009358] s3c_handle_ep0: WAIT_FOR_SETUP
[ 103.013432] nuke: ep0-control c09120d8
[ 103.017159] s3c_fifo_read: bytes=8, ep_index=0
[ 103.021580] s3c_ep0_setup: bRequestType = 0x80(IN), bRequest = 0x6 wLength = 0xff, wValue = 0x200, wIndex= 0x0
[ 103.031543] s3c_ep0_setup: *** USB_REQ_GET_DESCRIPTOR
[ 103.036569] s3c_ep0_setup: usb_ctrlrequest will be passed to fsg_setup()
[ 103.043242] mygadget_setup: ctrl->wLength=255
[ 103.047574] mygadget_setup: ep0-setup, length 8:
[ 103.052174] mygadget_standard_setup_req: USB_REQ_GET_DESCRIPTOR
[ 103.058060] mygadget_standard_setup_req: USB_DT_CONFIG
...
[ 103.213456] *** s3c_udc_irq : GINTSTS=0x54888028(on state WAIT_FOR_SETUP), GINTMSK : 0x800c3800, DAINT : 0x10000, DAINTMSK : 0x10001
[ 103.226781] *** process_ep_out_intr: EP OUT interrupt : DAINT = 0x10000
[ 103.233367] EP0-OUT : DOEPINT = 0x8
[ 103.236918] SETUP packet(transaction) arrived
[ 103.241338] s3c_handle_ep0: WAIT_FOR_SETUP
[ 103.245412] nuke: ep0-control c09120d8
[ 103.249138] s3c_fifo_read: bytes=8, ep_index=0
[ 103.253560] s3c_ep0_setup: bRequestType = 0x80(IN), bRequest = 0x6 wLength = 0xff, wValue = 0x300, wIndex= 0x0
[ 103.263523] s3c_ep0_setup: *** USB_REQ_GET_DESCRIPTOR
[ 103.268549] s3c_ep0_setup: usb_ctrlrequest will be passed to fsg_setup()
[ 103.275222] mygadget_setup: ctrl->wLength=255
[ 103.279554] mygadget_setup: ep0-setup, length 8:
[ 103.284154] mygadget_standard_setup_req: USB_REQ_GET_DESCRIPTOR
[ 103.290039] mygadget_standard_setup_req: USB_DT_STRING
...
后面还有两三个USB_DT_STRING,可能STRING这部分有问题,也可能是正常现象。
mygadget.c:
#include <linux/kernel.h> #include <linux/device.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/composite.h> #include <linux/freezer.h> #include <linux/kthread.h> #include <asm/uaccess.h> #include "composite.c" #include "usbstring.c" #include "config.c" #include "epautoconf.c" #define DEBUG #undef DBG #ifdef DEBUG #define DBG(x...) printk(x) #else #define DBG(x...) do { } while (0) #endif /* Big enough to hold our biggest descriptor */ #define EP0_BUFSIZE 256 #define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ /* Bulk-only class specific requests */ #define USB_BULK_RESET_REQUEST 0xff #define USB_BULK_GET_MAX_LUN_REQUEST 0xfe /* atomic_bitflags */ #define REGISTERED 0 #define IGNORE_BULK_OUT 1 #define SUSPENDED 2 #define DRIVER_DESC "My Gadget" #define DRIVER_NAME "mygadget" #define DRIVER_VERSION "1 2015/10/26" enum { MYGADGET_MANUFACTURER = 1, MYGADGET_PRODUCT, MYGADGET_SERIAL, MYGADGET_CONFIG, MYGADGET_INTERFACE }; static char mygadget_string_manufacturer[64]; static const char mygadget_string_product[] = DRIVER_DESC; static const char mygadget_string_config[] = "Self-powered"; static const char mygadget_string_interface[] = DRIVER_NAME; typedef enum { /* This one isn't used anywhere */ MYGADGET_STATE_COMMAND_PHASE = -10, MYGADGET_STATE_DATA_PHASE, MYGADGET_STATE_STATUS_PHASE, MYGADGET_STATE_IDLE = 0, MYGADGET_STATE_ABORT_BULK_OUT, MYGADGET_STATE_RESET, MYGADGET_STATE_INTERFACE_CHANGE, MYGADGET_STATE_CONFIG_CHANGE, MYGADGET_STATE_DISCONNECT, MYGADGET_STATE_EXIT, MYGADGET_STATE_TERMINATED } MYGADGET_STATE; struct mygadget_dev { struct usb_gadget *gadget; struct usb_request *ep0req; // For control responses unsigned int ep0_req_tag; const char *ep0req_name; spinlock_t lock; struct task_struct *thread_task; struct completion thread_notifier; MYGADGET_STATE state; int running; unsigned long atomic_bitflags; unsigned char config, new_config; struct usb_request *request; struct usb_ep *bulk_in; struct usb_ep *bulk_out; }; static struct mygadget_dev *gMygadget; /* 0. device desc */ static struct usb_device_descriptor mygadget_dev_desc = { .bLength = sizeof(struct usb_device_descriptor), .bDescriptorType = USB_DT_DEVICE, .bcdUSB = cpu_to_le16(0x0200), .bDeviceClass = USB_CLASS_MISC, /* The next three values can be overridden by module parameters */ .idVendor = cpu_to_le16(0x1bcf), .idProduct = cpu_to_le16(0x8002), .bcdDevice = cpu_to_le16(0x0004), .iManufacturer = MYGADGET_MANUFACTURER, .iProduct = MYGADGET_PRODUCT, .iSerialNumber = MYGADGET_CONFIG, .bNumConfigurations = 1, }; /* 1. config desc */ static struct usb_config_descriptor mygadget_config_desc = { .bLength = sizeof(mygadget_config_desc), .bDescriptorType = USB_DT_CONFIG, /* wTotalLength computed by usb_gadget_config_buf() */ .bNumInterfaces = 1, .bConfigurationValue = 1, .iConfiguration = MYGADGET_CONFIG, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, }; /* 2. interface desc There is only one interface. */ static struct usb_interface_descriptor mygadget_intf_desc = { .bLength = sizeof(struct usb_interface_descriptor), .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 2, /* Adjusted during mygadget_bind() */ .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, /* Adjusted during mygadget_bind() */ .bInterfaceProtocol = 0, /* Adjusted during mygadget_bind() */ .iInterface = 1, }; static struct usb_otg_descriptor mygadget_otg_desc = { .bLength = sizeof(struct usb_otg_descriptor), .bDescriptorType = USB_DT_OTG, .bmAttributes = USB_OTG_SRP, }; /* * Three full-speed endpoint descriptors: bulk-in, bulk-out, and * interrupt-in. */ static struct usb_endpoint_descriptor mygadget_fs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, /* wMaxPacketSize set by autoconfiguration */ }; static struct usb_endpoint_descriptor mygadget_fs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, /* wMaxPacketSize set by autoconfiguration */ }; #if 0 static struct usb_endpoint_descriptor mygadget_fs_intr_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = cpu_to_le16(2), .bInterval = 32, /* frames -> 32 ms */ }; #endif static struct usb_descriptor_header *mygadget_fs_function[] = { (struct usb_descriptor_header *) &mygadget_otg_desc, (struct usb_descriptor_header *) &mygadget_intf_desc, (struct usb_descriptor_header *) &mygadget_fs_bulk_in_desc, (struct usb_descriptor_header *) &mygadget_fs_bulk_out_desc, //(struct usb_descriptor_header *) &fsg_fs_intr_in_desc, NULL, }; /* * USB 2.0 devices need to expose both high speed and full speed * descriptors, unless they only run at full speed. * * That means alternate endpoint descriptors (bigger packets) * and a "device qualifier" ... plus more construction options * for the configuration descriptor. */ static struct usb_endpoint_descriptor mygadget_hs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }; static struct usb_endpoint_descriptor mygadget_hs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), .bInterval = 1, /* NAK every 1 uframe */ }; #if 0 static struct usb_endpoint_descriptor fsg_hs_intr_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = cpu_to_le16(2), .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ }; #endif static struct usb_descriptor_header *mygadget_hs_function[] = { (struct usb_descriptor_header *) &mygadget_otg_desc, (struct usb_descriptor_header *) &mygadget_intf_desc, (struct usb_descriptor_header *) &mygadget_hs_bulk_in_desc, (struct usb_descriptor_header *) &mygadget_hs_bulk_out_desc, //(struct usb_descriptor_header *) &fsg_hs_intr_in_desc, NULL, }; static struct usb_qualifier_descriptor mygadget_dev_qualifier = { .bLength = sizeof(mygadget_dev_qualifier), .bDescriptorType = USB_DT_DEVICE_QUALIFIER, .bcdUSB = cpu_to_le16(0x0200), .bDeviceClass = USB_CLASS_MISC, .bNumConfigurations = 1, }; static void mygadget_raise_exception(struct mygadget_dev *mydev, MYGADGET_STATE state) { unsigned long flags; /* Do nothing if a higher-priority exception is already in progress. * If a lower-or-equal priority exception is in progress, preempt it * and notify the main thread by sending it a signal. */ spin_lock_irqsave(&mydev->lock, flags); if (mydev->state <= state) { //mydev->exception_req_tag = mydev->ep0_req_tag; mydev->state = state; if (mydev->thread_task) send_sig_info(SIGUSR1, SEND_SIG_FORCED, mydev->thread_task); } spin_unlock_irqrestore(&mydev->lock, flags); } static void __exit mygadget_unbind(struct usb_gadget *gadget) { struct mygadget_dev *mydev = get_gadget_data(gadget); struct usb_request *req = mydev->ep0req; printk("%s\n", __func__); clear_bit(REGISTERED, &mydev->atomic_bitflags); /* If the thread isn't already dead, tell it to exit now */ if (mydev->state != MYGADGET_STATE_TERMINATED) { mygadget_raise_exception(mydev, MYGADGET_STATE_EXIT); wait_for_completion(&mydev->thread_notifier); /* The cleanup routine waits for this completion also */ complete(&mydev->thread_notifier); } /* Free the request and buffer for endpoint 0 */ if(req) { kfree(req->buf); usb_ep_free_request(gadget->ep0, req); } } static void mygadget_disconnect(struct usb_gadget *gadget) { struct mygadget_dev *mydev = get_gadget_data(gadget); DBG("%s: disconnect or port reset\n", __func__); mygadget_raise_exception(mydev, MYGADGET_STATE_DISCONNECT); } static int mygadget_class_setup_req(struct mygadget_dev *dev, const struct usb_ctrlrequest *ctrl) { int value = -EOPNOTSUPP; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); if (!dev->config) return value; /* Handle Bulk-only class-specific requests */ switch (ctrl->bRequest) { case USB_BULK_RESET_REQUEST: DBG("%s: USB_BULK_RESET_REQUEST\n", __func__); if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; if (w_index != 0 || w_value != 0) { value = -EDOM; break; } /* Raise an exception to stop the current operation * and reinitialize our state. */ DBG("%s: bulk reset request\n", __func__); mygadget_raise_exception(dev, MYGADGET_STATE_RESET); value = DELAYED_STATUS; break; case USB_BULK_GET_MAX_LUN_REQUEST: DBG("%s: USB_BULK_GET_MAX_LUN_REQUEST\n", __func__); if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) break; if (w_index != 0 || w_value != 0) { value = -EDOM; break; } VDBG(dev, "get max LUN\n"); value = 1; break; } if (value == -EOPNOTSUPP) VDBG(dev, "unknown class-specific control req " "%02x.%02x v%04x i%04x l%u\n", ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue), w_index, w_length); return value; } static struct usb_string mygadget_strings[] = { { MYGADGET_MANUFACTURER, mygadget_string_manufacturer }, { MYGADGET_PRODUCT, mygadget_string_product }, { MYGADGET_SERIAL, "" }, { MYGADGET_CONFIG, mygadget_string_config }, { MYGADGET_INTERFACE, mygadget_string_interface }, { } }; static struct usb_gadget_strings mygadget_stringtab = { .language = 0x0409, /* en-us */ .strings = mygadget_strings, }; /* * Config descriptors must agree with the code that sets configurations * and with code managing interfaces and their altsettings. They must * also handle different speeds and other-speed requests. */ static int mygadget_pop_config_buf(struct usb_gadget *gadget, u8 *buf, u8 type, unsigned index) { enum usb_device_speed speed = gadget->speed; int len; const struct usb_descriptor_header **function; if (index > 0) return -EINVAL; if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG) speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH ? (const struct usb_descriptor_header **)mygadget_hs_function : (const struct usb_descriptor_header **)mygadget_fs_function; /* for now, don't advertise srp-only devices */ if (!gadget_is_otg(gadget)) function++; len = usb_gadget_config_buf(&mygadget_config_desc, buf, EP0_BUFSIZE, function); ((struct usb_config_descriptor *) buf)->bDescriptorType = type; return len; } /* Ep0 standard request handlers. These always run in_irq. */ static int mygadget_standard_setup_req(struct mygadget_dev *dev, const struct usb_ctrlrequest *ctrl) { struct usb_request *req = dev->ep0req; int value = -EOPNOTSUPP; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); /* Usually this just stores reply data in the pre-allocated ep0 buffer, * but config change events will also reconfigure hardware. */ switch (ctrl->bRequest) { case USB_REQ_GET_DESCRIPTOR: DBG("%s: USB_REQ_GET_DESCRIPTOR\n", __func__); if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) break; switch (w_value >> 8) { case USB_DT_DEVICE: DBG("%s: USB_DT_DEVICE\n", __func__); VDBG(dev, "get device descriptor\n"); value = sizeof(mygadget_dev_desc); memcpy(req->buf, &mygadget_dev_desc, value); break; case USB_DT_DEVICE_QUALIFIER: DBG("%s: USB_DT_DEVICE_QUALIFIER\n", __func__); VDBG(dev, "get device qualifier\n"); if (!gadget_is_dualspeed(dev->gadget)) break; value = sizeof(mygadget_dev_qualifier); memcpy(req->buf, &mygadget_dev_qualifier, value); break; case USB_DT_OTHER_SPEED_CONFIG: DBG("%s: USB_DT_OTHER_SPEED_CONFIG\n", __func__); VDBG(dev, "get other-speed config descriptor\n"); if (!gadget_is_dualspeed(dev->gadget)) break; goto get_config; case USB_DT_CONFIG: DBG("%s: USB_DT_CONFIG\n", __func__); VDBG(dev, "get configuration descriptor\n"); get_config: value = mygadget_pop_config_buf(dev->gadget, req->buf, w_value >> 8, w_value & 0xff); break; case USB_DT_STRING: DBG("%s: USB_DT_STRING\n", __func__); VDBG(dev, "get string descriptor\n"); /* wIndex == language code */ value = usb_gadget_get_string(&mygadget_stringtab, w_value & 0xff, req->buf); break; } break; /* One config, two speeds */ case USB_REQ_SET_CONFIGURATION: DBG("%s: USB_REQ_SET_CONFIGURATION\n", __func__); if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) break; VDBG(dev, "set configuration\n"); if (w_value == 1 || w_value == 0) { dev->new_config = w_value; /* Raise an exception to wipe out previous transaction * state (queued bufs, etc) and set the new config. */ mygadget_raise_exception(dev, MYGADGET_STATE_CONFIG_CHANGE); value = DELAYED_STATUS; } break; case USB_REQ_GET_CONFIGURATION: DBG("%s: USB_REQ_GET_CONFIGURATION\n", __func__); if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) break; VDBG(dev, "get configuration\n"); *(u8 *) req->buf = dev->config; value = 1; break; case USB_REQ_SET_INTERFACE: DBG("%s: USB_REQ_SET_INTERFACE\n", __func__); if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | USB_RECIP_INTERFACE)) break; if (dev->config && w_index == 0) { /* Raise an exception to wipe out previous transaction * state (queued bufs, etc) and install the new * interface altsetting. */ mygadget_raise_exception(dev, MYGADGET_STATE_INTERFACE_CHANGE); value = DELAYED_STATUS; } break; case USB_REQ_GET_INTERFACE: DBG("%s: USB_REQ_GET_INTERFACE\n", __func__); if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE)) break; if (!dev->config) break; if (w_index != 0) { value = -EDOM; break; } VDBG(dev, "get interface\n"); *(u8 *) req->buf = 0; value = 1; break; default: DBG("%s: default\n", __func__); VDBG(dev, "unknown control req %02x.%02x v%04x i%04x l%u\n", ctrl->bRequestType, ctrl->bRequest, w_value, w_index, le16_to_cpu(ctrl->wLength)); } return value; } static int ep0_queue(struct mygadget_dev *dev) { int rc; rc = usb_ep_queue(dev->gadget->ep0, dev->ep0req, GFP_ATOMIC); if (rc != 0 && rc != -ESHUTDOWN) { /* We can't do much more than wait for a reset */ WARNING(dev, "error in submission: %s --> %d\n", dev->gadget->ep0->name, rc); } return rc; } static int mygadget_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) { struct mygadget_dev *mydev = get_gadget_data(gadget); int ret = 0; int w_length = le16_to_cpu(ctrl->wLength); printk("%s: ctrl->wLength=%d\n", __func__, w_length); ++mydev->ep0_req_tag; // Record arrival of a new request mydev->ep0req->context = NULL; mydev->ep0req->length = 0; if(sizeof(*ctrl) < 512) { DBG("%s: ep0-setup, length %u:\n", __func__, sizeof(*ctrl)); print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, (u8*)ctrl, sizeof(*ctrl), 0); } if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) ret = mygadget_class_setup_req(mydev, ctrl); else ret = mygadget_standard_setup_req(mydev, ctrl); /* Respond with data/status or defer until later? */ if (ret >= 0 && ret != DELAYED_STATUS) { ret = min(ret, w_length); mydev->ep0req->length = ret; mydev->ep0req->zero = ret < w_length; mydev->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? "ep0-in" : "ep0-out"); ret = ep0_queue(mydev); } return ret; } static void mygadget_suspend(struct usb_gadget *gadget) { struct mygadget_dev *mydev = get_gadget_data(gadget); printk("%s\n", __func__); set_bit(SUSPENDED, &mydev->atomic_bitflags); } static void mygadget_resume(struct usb_gadget *gadget) { struct mygadget_dev *mydev = get_gadget_data(gadget); printk("%s\n", __func__); set_bit(SUSPENDED, &mydev->atomic_bitflags); } static struct usb_gadget_driver mygadget_driver = { #ifdef CONFIG_USB_GADGET_DUALSPEED .speed = USB_SPEED_HIGH, #else .speed = USB_SPEED_FULL, #endif .function = (char *) mygadget_string_product, .unbind = mygadget_unbind, .disconnect = mygadget_disconnect, .setup = mygadget_setup, .suspend = mygadget_suspend, .resume = mygadget_resume, .driver = { .name = "MyGadget", .owner = THIS_MODULE, // .release = ... // .suspend = ... // .resume = ... }, }; typedef void (*mygadget_routine_t)(struct mygadget_dev *); static void mygadget_ep0_complete(struct usb_ep *ep, struct usb_request *req) { struct mygadget_dev *mydev = ep->driver_data; DBG("%s\n", __func__); if(req->actual > 0 && req->actual < 512) { DBG("actual: %u, len: %u\n", req->actual, req->length); print_hex_dump(KERN_NOTICE, "", DUMP_PREFIX_OFFSET, 16, 1, req->buf, req->actual, 0); } if(req->status || req->actual != req->length) DBG("%s --> %d, %u/%u\n", __func__, req->status, req->actual, req->length); if(req->status == -ECONNRESET) // Request was cancelled usb_ep_fifo_flush(ep); /* call the func that req->context point */ if(req->status == 0 && req->context) ((mygadget_routine_t) (req->context))(mydev); } /* * Change our operational configuration. This code must agree with the code * that returns config descriptors, and with interface altsetting code. * * It's also responsible for power management interactions. Some * configurations might not work with our current power sources. * For now we just assume the gadget is always self-powered. */ static int mygadget_do_set_config(struct mygadget_dev *mydev, unsigned char config) { int rc = 0; /* Disable the single interface */ if(mydev->config != 0) { DBG("%s: reset config\n", __func__); mydev->config = 0; //rc = do_set_interface(mydev, -1); } /* Enable the interface */ if(config != 0) { mydev->config = config; /* if ((rc = do_set_interface(mydev, 0)) != 0) mydev->config = 0; // Reset on errors else { char *speed; switch (mydev->gadget->speed) { case USB_SPEED_LOW: speed = "low"; break; case USB_SPEED_FULL: speed = "full"; break; case USB_SPEED_HIGH: speed = "high"; break; default: speed = "?"; break; } DBG("%s: %s speed config #%d\n", __func__, speed, mydev->config); } */ } return rc; } static void mygadget_handle_exception(struct mygadget_dev *dev) { int sig; siginfo_t info; MYGADGET_STATE old_state; /* Clear the existing signals. Anything but SIGUSR1 is converted * into a high-priority EXIT exception. */ while(1) { sig = dequeue_signal_lock(current, ¤t->blocked, &info); if(!sig) break; if(sig != SIGUSR1) { if(dev->state < MYGADGET_STATE_EXIT) DBG("Main thread exiting on signal\n"); mygadget_raise_exception(dev, MYGADGET_STATE_EXIT); } } /* Cancel all the pending transfers */ /* for (i = 0; i < FSG_NUM_BUFFERS; ++i) { bh = &dev->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(dev->bulk_in, bh->inreq); if (bh->outreq_busy) usb_ep_dequeue(dev->bulk_out, bh->outreq); } */ /* Wait until everything is idle */ /* while(1) { num_active = dev->intreq_busy; for (i = 0; i < FSG_NUM_BUFFERS; ++i) { bh = &dev->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } if (num_active == 0) break; if (sleep_thread(dev)) return; } */ /* Clear out the controller's fifos */ //if (dev->bulk_in_enabled) usb_ep_fifo_flush(dev->bulk_in); //if (dev->bulk_out_enabled) usb_ep_fifo_flush(dev->bulk_out); /* Reset the I/O buffer states and pointers, the SCSI * state, and the exception. Then invoke the handler. */ spin_lock_irq(&dev->lock); /* for (i = 0; i < FSG_NUM_BUFFERS; ++i) { bh = &dev->buffhds[i]; bh->state = BUF_STATE_EMPTY; } fsg->next_buffhd_to_fill = dev->next_buffhd_to_drain = &dev->buffhds[0]; exception_req_tag = dev->exception_req_tag; new_config = dev->new_config; */ old_state = dev->state; if (old_state == MYGADGET_STATE_ABORT_BULK_OUT) dev->state = MYGADGET_STATE_STATUS_PHASE; else { dev->state = MYGADGET_STATE_IDLE; } spin_unlock_irq(&dev->lock); /* Carry out any extra actions required for the exception */ switch (old_state) { case MYGADGET_STATE_ABORT_BULK_OUT: DBG("%s: ABORT_BULK_OUT\n", __func__); //send_status(dev); spin_lock_irq(&dev->lock); if (dev->state == MYGADGET_STATE_STATUS_PHASE) dev->state = MYGADGET_STATE_IDLE; spin_unlock_irq(&dev->lock); break; case MYGADGET_STATE_RESET: DBG("%s: RESET\n", __func__); /* In case we were forced against our will to halt a * bulk endpoint, clear the halt now. (The SuperH UDC * requires this.) */ if (test_and_clear_bit(IGNORE_BULK_OUT, &dev->atomic_bitflags)) usb_ep_clear_halt(dev->bulk_in); //if (dev->ep0_req_tag == exception_req_tag) // ep0_queue(dev); // Complete the status stage break; case MYGADGET_STATE_INTERFACE_CHANGE: DBG("%s: INTERFACE_CHANGE\n", __func__); /* rc = do_set_interface(dev, 0); if (dev->ep0_req_tag != exception_req_tag) break; if (rc != 0) // STALL on errors fsg_set_halt(dev, dev->ep0); else // Complete the status stage ep0_queue(dev); */ break; case MYGADGET_STATE_CONFIG_CHANGE: DBG("%s: CONFIG_CHANGE\n", __func__); //rc = mygadget_do_set_config(dev, new_config); //if (dev->ep0_req_tag != exception_req_tag) // break; //if (rc != 0) // STALL on errors // fsg_set_halt(dev, dev->ep0); //else // Complete the status stage // ep0_queue(dev); break; case MYGADGET_STATE_DISCONNECT: mygadget_do_set_config(dev, 0); // Unconfigured state break; case MYGADGET_STATE_EXIT: case MYGADGET_STATE_TERMINATED: DBG("%s: EXIT / TERMINATED\n", __func__); mygadget_do_set_config(dev, 0); // Free resources spin_lock_irq(&dev->lock); dev->state = MYGADGET_STATE_TERMINATED; // Stop the thread spin_unlock_irq(&dev->lock); break; default: break; } } static int mygadget_sleep_thread(struct mygadget_dev *mydev) { int rc = 0; /* Wait until a signal arrives or we are woken up */ while(1) { try_to_freeze(); set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) { rc = -EINTR; break; } if (mydev->running) break; schedule(); } __set_current_state(TASK_RUNNING); mydev->running = 0; return rc; } static int mygadget_thread(void *arg) { struct mygadget_dev *mydev = (struct mygadget_dev *)arg; printk("%s\n", __func__); allow_signal(SIGINT); allow_signal(SIGTERM); allow_signal(SIGKILL); allow_signal(SIGUSR1); /* Allow the thread to be frozen */ set_freezable(); /* Arrange for userspace references to be interpreted as kernel * pointers. That way we can pass a kernel pointer to a routine * that expects a __user pointer and it will work okay. */ set_fs(get_ds()); /* The main loop */ while (mydev->state != MYGADGET_STATE_TERMINATED) { if(mydev->state > MYGADGET_STATE_IDLE || signal_pending(current)){ DBG("%s handle exception\n", __func__); mygadget_handle_exception(mydev); continue; } DBG("%s running\n", __func__); if (!mydev->running) { mygadget_sleep_thread(mydev); continue; } /* if (get_next_command(mydev)) continue; spin_lock_irq(&mydev->lock); if(mydev->state <= MYGADGET_STATE_IDLE) mydev->state = MYGADGET_STATE_DATA_PHASE; spin_unlock_irq(&mydev->lock); */ spin_lock_irq(&mydev->lock); if(mydev->state <= MYGADGET_STATE_IDLE) mydev->state = MYGADGET_STATE_IDLE; spin_unlock_irq(&mydev->lock); } spin_lock_irq(&mydev->lock); mydev->thread_task = NULL; spin_unlock_irq(&mydev->lock); /* Unregister the driver iff the thread hasn't already done so */ if (test_and_clear_bit(REGISTERED, &gMygadget->atomic_bitflags)) usb_gadget_unregister_driver(&mygadget_driver); /* Let the unbind and cleanup routines know the thread has exited */ complete_and_exit(&mydev->thread_notifier, 0); } /* Bind函数是功能层需要实现与设备层关联的重要函数 */ static int __init mygadget_bind(struct usb_gadget *gadget) { int ret = 0; struct mygadget_dev *mydev = gMygadget; struct usb_ep *ep = NULL; printk("%s\n", __func__); mydev->gadget = gadget; set_gadget_data(gadget, mydev); gadget->ep0->driver_data = mydev; /* Find all the endpoints we will use */ usb_ep_autoconfig_reset(gadget); ep = usb_ep_autoconfig(gadget, &mygadget_fs_bulk_in_desc); if(!ep) goto autoconf_fail; ep->driver_data = mydev; // claim the endpoint mydev->bulk_in = ep; ep = usb_ep_autoconfig(gadget, &mygadget_fs_bulk_out_desc); if(!ep) goto autoconf_fail; ep->driver_data = mydev; // claim the endpoint mydev->bulk_out = ep; /* Fix up the descriptors */ mygadget_dev_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; mygadget_dev_desc.idVendor = cpu_to_le16(0x1bcf); mygadget_dev_desc.idProduct = cpu_to_le16(0x8002); mygadget_dev_desc.bcdDevice = cpu_to_le16(0x0004); mygadget_intf_desc.bNumEndpoints = 2; mygadget_intf_desc.bInterfaceSubClass = 1; mygadget_intf_desc.bInterfaceProtocol = 0; if (gadget_is_dualspeed(gadget)) { mygadget_hs_function[3] = NULL; /* Assume ep0 uses the same maxpacket value for both speeds */ mygadget_dev_qualifier.bMaxPacketSize0 = gadget->ep0->maxpacket; /* Assume endpoint addresses are the same for both speeds */ mygadget_hs_bulk_in_desc.bEndpointAddress = mygadget_fs_bulk_in_desc.bEndpointAddress; mygadget_hs_bulk_out_desc.bEndpointAddress = mygadget_fs_bulk_out_desc.bEndpointAddress; } /* Allocate the request and buffer for endpoint 0 */ mydev->ep0req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); if(!mydev->ep0req) goto error; mydev->ep0req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); if (!mydev->ep0req->buf) goto error; mydev->ep0req->complete = mygadget_ep0_complete; if(gadget_is_otg(gadget)) mygadget_otg_desc.bmAttributes |= USB_OTG_HNP; /* This should reflect the actual gadget power source */ usb_gadget_set_selfpowered(gadget); /* kernel thread */ mydev->thread_task = kthread_create(mygadget_thread, mydev, "mygadget"); if (IS_ERR(mydev->thread_task)) { ret = PTR_ERR(mydev->thread_task); goto error; } DBG("I/O thread pid: %d\n", task_pid_nr(mydev->thread_task)); set_bit(REGISTERED, &mydev->atomic_bitflags); /* Tell the thread to start working */ wake_up_process(mydev->thread_task); return 0; autoconf_fail: DBG("unable to autoconfigure all endpoints\n"); ret = -ENOTSUPP; error: mydev->state = MYGADGET_STATE_TERMINATED; // The thread is dead mygadget_unbind(gadget); complete(&mydev->thread_notifier); return ret; } static int __init mygadget_init(void) { int rc; printk("%s\n", __func__); gMygadget = kzalloc(sizeof(struct mygadget_dev), GFP_KERNEL); if (!gMygadget) return -ENOMEM; spin_lock_init(&gMygadget->lock); init_completion(&gMygadget->thread_notifier); if((rc = usb_gadget_probe_driver(&mygadget_driver, mygadget_bind)) != 0) { kfree(gMygadget); gMygadget = NULL; } return rc; } static void __exit mygadget_remove(void) { printk("%s\n", __func__); /* Unregister the driver iff the thread hasn't already done so */ if (test_and_clear_bit(REGISTERED, &gMygadget->atomic_bitflags)) usb_gadget_unregister_driver(&mygadget_driver); /* Wait for the thread to finish up */ wait_for_completion(&gMygadget->thread_notifier); kfree(gMygadget); gMygadget = NULL; } module_init(mygadget_init); module_exit(mygadget_remove); MODULE_AUTHOR("fuchch"); MODULE_LICENSE("GPL");
接下来,原本是想继续修改下去,希望先能显示出一张图片来,可是这样的话就需要了解USB cam的IOCTRL过程,同时也发现了设备描述符上需要大量修改。如此得先了解UVC。
一开始,像上面这幅图,在OTG/Gadget驱动下嵌入UVC驱动。但这样要使用内核的UVC的话就需要外部做切换。
那就换下,在UVC驱动下嵌入OTG/Gadget驱动,这样,在UVC内部切换,对于正常UVC和OTG输出的切换就方便了。
下面就是要了解内核的UVC框架了,然后试着修改看看能不能实现上面这种想法。
(后来看了一下,其实不用改,直接在原来的基础上就可以了。在uvc_driver.c中,uvc_probe()中添加mygadget_init()的调用,同时将要用到的信息赋值给全局变量;uvc_disconnect()中调用mygadget_remove即可。)