OTG与gadget学习(二)

      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学习(二)_第1张图片

     一开始,像上面这幅图,在OTG/Gadget驱动下嵌入UVC驱动。但这样要使用内核的UVC的话就需要外部做切换。

OTG与gadget学习(二)_第2张图片

那就换下,在UVC驱动下嵌入OTG/Gadget驱动,这样,在UVC内部切换,对于正常UVC和OTG输出的切换就方便了。

      下面就是要了解内核的UVC框架了,然后试着修改看看能不能实现上面这种想法。


      (后来看了一下,其实不用改,直接在原来的基础上就可以了。在uvc_driver.c中,uvc_probe()中添加mygadget_init()的调用,同时将要用到的信息赋值给全局变量;uvc_disconnect()中调用mygadget_remove即可。

你可能感兴趣的:(OTG与gadget学习(二))