为了与主机端驱动设备的USB Device Driver概念进行区别,将在外围器件中运行的驱动程序称为USB Gadget Driver。其中,Host端驱动设备的驱动程序是master或者client driver,设备端gadget driver是slave或者function driver。
Gadget Driver和USB Host端驱动程序类似,都是使用请求队列来对I/O包进行缓冲,这些请求可以被提交和取消。它们的结构、消息和常量的定义也和USB技术规范第九章的内容一致。同时也是通过bind和unbind将driver与device建立关系。
USB_Gadget对象
/**
* struct usb_gadget - represents a usb slave device
* @work: (internal use) Workqueue to be used for sysfs_notify()
* @udc: struct usb_udc pointer for this gadget
* @ops: Function pointers used to access hardware-specific operations.
* @ep0: Endpoint zero, used when reading or writing responses to
* driver setup() requests
* @ep_list: List of other endpoints supported by the device.
* @speed: Speed of current connection to USB host.
* @max_speed: Maximal speed the UDC can handle. UDC must support this
* and all slower speeds.
* @state: the state we are now (attached, suspended, configured, etc)
* @name: Identifies the controller hardware type. Used in diagnostics
* and sometimes configuration.
* @dev: Driver model state for this abstract device.
* @out_epnum: last used out ep number
* @in_epnum: last used in ep number
* @otg_caps: OTG capabilities of this gadget.
* @sg_supported: true if we can handle scatter-gather
* @is_otg: True if the USB device port uses a Mini-AB jack, so that the
* gadget driver must provide a USB OTG descriptor.
* @is_a_peripheral: False unless is_otg, the "A" end of a USB cable
* is in the Mini-AB jack, and HNP has been used to switch roles
* so that the "A" device currently acts as A-Peripheral, not A-Host.
* @a_hnp_support: OTG device feature flag, indicating that the A-Host
* supports HNP at this port.
* @a_alt_hnp_support: OTG device feature flag, indicating that the A-Host
* only supports HNP on a different root port.
* @b_hnp_enable: OTG device feature flag, indicating that the A-Host
* enabled HNP support.
* @hnp_polling_support: OTG device feature flag, indicating if the OTG device
* in peripheral mode can support HNP polling.
* @host_request_flag: OTG device feature flag, indicating if A-Peripheral
* or B-Peripheral wants to take host role.
* @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to
* MaxPacketSize.
* @is_selfpowered: if the gadget is self-powered.
* @deactivated: True if gadget is deactivated - in deactivated state it cannot
* be connected.
* @connected: True if gadget is connected.
*
* Gadgets have a mostly-portable "gadget driver" implementing device
* functions, handling all usb configurations and interfaces. Gadget
* drivers talk to hardware-specific code indirectly, through ops vectors.
* That insulates the gadget driver from hardware details, and packages
* the hardware endpoints through generic i/o queues. The "usb_gadget"
* and "usb_ep" interfaces provide that insulation from the hardware.
*
* Except for the driver data, all fields in this structure are
* read-only to the gadget driver. That driver data is part of the
* "driver model" infrastructure in 2.6 (and later) kernels, and for
* earlier systems is grouped in a similar structure that's not known
* to the rest of the kernel.
*
* Values of the three OTG device feature flags are updated before the
* setup() call corresponding to USB_REQ_SET_CONFIGURATION, and before
* driver suspend() calls. They are valid only when is_otg, and when the
* device is acting as a B-Peripheral (so is_a_peripheral is false).
*/
struct usb_gadget {
struct work_struct work;
struct usb_udc *udc;
/* readonly to gadget driver */
const struct usb_gadget_ops *ops; //Gadget设备操作函数集
struct usb_ep *ep0;//控制端点,只对setup包响应
struct list_head ep_list;/* of usb_ep *///将设备的所有端点连成链表,ep0不在其中
enum usb_device_speed speed; //高速、全速和低速
enum usb_device_speed max_speed;
enum usb_device_state state;
const char *name; //器件名称
struct device dev; //内核设备模型使用
unsigned out_epnum;
unsigned in_epnum;
struct usb_otg_caps *otg_caps;
unsigned sg_supported:1;
unsigned is_otg:1;
unsigned is_a_peripheral:1;
unsigned b_hnp_enable:1;
unsigned a_hnp_support:1;
unsigned a_alt_hnp_support:1;
unsigned hnp_polling_support:1;
unsigned host_request_flag:1;
unsigned quirk_ep_out_aligned_size:1;
unsigned quirk_altset_not_supp:1;
unsigned quirk_stall_not_supp:1;
unsigned quirk_zlp_not_supp:1;
unsigned is_selfpowered:1;
unsigned deactivated:1;
unsigned connected:1;
};
Gadget器件操作函数集
操作UDC硬件的API,但操作端点的函数由端点操作函数集完成
/* the rest of the api to the controller hardware: device operations,
* which don't involve endpoints (or i/o).
*/
struct usb_gadget_ops {
int (*get_frame)(struct usb_gadget *);
int (*wakeup)(struct usb_gadget *);
int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
int (*vbus_session) (struct usb_gadget *, int is_active);
int (*vbus_draw) (struct usb_gadget *, unsigned mA);
int (*pullup) (struct usb_gadget *, int is_on);
int (*ioctl)(struct usb_gadget *,
unsigned code, unsigned long param);
void (*get_config_params)(struct usb_dcd_config_params *);
int (*udc_start)(struct usb_gadget *,
struct usb_gadget_driver *);
int (*udc_stop)(struct usb_gadget *);
struct usb_ep *(*match_ep)(struct usb_gadget *,
struct usb_endpoint_descriptor *,
struct usb_ss_ep_comp_descriptor *);
};
USB Gadget driver对象
/*-------------------------------------------------------------------------*/
/**
* struct usb_gadget_driver - driver for usb 'slave' devices
* @function: String describing the gadget's function
* @max_speed: Highest speed the driver handles.
* @setup: Invoked for ep0 control requests that aren't handled by
* the hardware level driver. Most calls must be handled by
* the gadget driver, including descriptor and configuration
* management. The 16 bit members of the setup data are in
* USB byte order. Called in_interrupt; this may not sleep. Driver
* queues a response to ep0, or returns negative to stall.
* @disconnect: Invoked after all transfers have been stopped,
* when the host is disconnected. May be called in_interrupt; this
* may not sleep. Some devices can't detect disconnect, so this might
* not be called except as part of controller shutdown.
* @bind: the driver's bind callback
* @unbind: Invoked when the driver is unbound from a gadget,
* usually from rmmod (after a disconnect is reported).
* Called in a context that permits sleeping.
* @suspend: Invoked on USB suspend. May be called in_interrupt.
* @resume: Invoked on USB resume. May be called in_interrupt.
* @reset: Invoked on USB bus reset. It is mandatory for all gadget drivers
* and should be called in_interrupt.
* @driver: Driver model state for this driver.
* @udc_name: A name of UDC this driver should be bound to. If udc_name is NULL,
* this driver will be bound to any available UDC.
* @pending: UDC core private data used for deferred probe of this driver.
* @match_existing_only: If udc is not found, return an error and don't add this
* gadget driver to list of pending driver
*
* Devices are disabled till a gadget driver successfully bind()s, which
* means the driver will handle setup() requests needed to enumerate (and
* meet "chapter 9" requirements) then do some useful work.
*
* If gadget->is_otg is true, the gadget driver must provide an OTG
* descriptor during enumeration, or else fail the bind() call. In such
* cases, no USB traffic may flow until both bind() returns without
* having called usb_gadget_disconnect(), and the USB host stack has
* initialized.
*
* Drivers use hardware-specific knowledge to configure the usb hardware.
* endpoint addressing is only one of several hardware characteristics that
* are in descriptors the ep0 implementation returns from setup() calls.
*
* Except for ep0 implementation, most driver code shouldn't need change to
* run on top of different usb controllers. It'll use endpoints set up by
* that ep0 implementation.
*
* The usb controller driver handles a few standard usb requests. Those
* include set_address, and feature flags for devices, interfaces, and
* endpoints (the get_status, set_feature, and clear_feature requests).
*
* Accordingly, the driver's setup() callback must always implement all
* get_descriptor requests, returning at least a device descriptor and
* a configuration descriptor. Drivers must make sure the endpoint
* descriptors match any hardware constraints. Some hardware also constrains
* other descriptors. (The pxa250 allows only configurations 1, 2, or 3).
*
* The driver's setup() callback must also implement set_configuration,
* and should also implement set_interface, get_configuration, and
* get_interface. Setting a configuration (or interface) is where
* endpoints should be activated or (config 0) shut down.
*
* (Note that only the default control endpoint is supported. Neither
* hosts nor devices generally support control traffic except to ep0.)
*
* Most devices will ignore USB suspend/resume operations, and so will
* not provide those callbacks. However, some may need to change modes
* when the host is not longer directing those activities. For example,
* local controls (buttons, dials, etc) may need to be re-enabled since
* the (remote) host can't do that any longer; or an error state might
* be cleared, to make the device behave identically whether or not
* power is maintained.
*/
struct usb_gadget_driver {
char *function; //驱动名称
enum usb_device_speed max_speed; //USB设备速度类型
int (*bind)(struct usb_gadget *gadget,
struct usb_gadget_driver *driver); //将驱动和设备绑定,一般在驱动注册时调用
void (*unbind)(struct usb_gadget *);//卸载驱动时调用,rmmod时调用
int (*setup)(struct usb_gadget *,
const struct usb_ctrlrequest *); //处理ep0的控制请求,在中断中调用,不能睡眠
void (*disconnect)(struct usb_gadget *); //可能在中断中调用不能睡眠
void (*suspend)(struct usb_gadget *); //电源管理模式相关,设备挂起
void (*resume)(struct usb_gadget *); //电源管理模式相关,设备恢复
void (*reset)(struct usb_gadget *);
/* FIXME support safe rmmod */
struct device_driver driver; //内核设备管理使用
char *udc_name;
struct list_head pending;
unsigned match_existing_only:1;
};
描述一个I/O请求
struct usb_ep;
/**
* struct usb_request - describes one i/o request
* @buf: Buffer used for data. Always provide this; some controllers
* only use PIO, or don't use DMA for some endpoints.
* @dma: DMA address corresponding to 'buf'. If you don't set this
* field, and the usb controller needs one, it is responsible
* for mapping and unmapping the buffer.
* @sg: a scatterlist for SG-capable controllers.
* @num_sgs: number of SG entries
* @num_mapped_sgs: number of SG entries mapped to DMA (internal)
* @length: Length of that data
* @stream_id: The stream id, when USB3.0 bulk streams are being used
* @no_interrupt: If true, hints that no completion irq is needed.
* Helpful sometimes with deep request queues that are handled
* directly by DMA controllers.
* @zero: If true, when writing data, makes the last packet be "short"
* by adding a zero length packet as needed;
* @short_not_ok: When reading data, makes short packets be
* treated as errors (queue stops advancing till cleanup).
* @complete: Function called when request completes, so this request and
* its buffer may be re-used. The function will always be called with
* interrupts disabled, and it must not sleep.
* Reads terminate with a short packet, or when the buffer fills,
* whichever comes first. When writes terminate, some data bytes
* will usually still be in flight (often in a hardware fifo).
* Errors (for reads or writes) stop the queue from advancing
* until the completion function returns, so that any transfers
* invalidated by the error may first be dequeued.
* @context: For use by the completion callback
* @list: For use by the gadget driver.
* @status: Reports completion code, zero or a negative errno.
* Normally, faults block the transfer queue from advancing until
* the completion callback returns.
* Code "-ESHUTDOWN" indicates completion caused by device disconnect,
* or when the driver disabled the endpoint.
* @actual: Reports bytes transferred to/from the buffer. For reads (OUT
* transfers) this may be less than the requested length. If the
* short_not_ok flag is set, short reads are treated as errors
* even when status otherwise indicates successful completion.
* Note that for writes (IN transfers) some data bytes may still
* reside in a device-side FIFO when the request is reported as
* complete.
*
* These are allocated/freed through the endpoint they're used with. The
* hardware's driver can add extra per-request data to the memory it returns,
* which often avoids separate memory allocations (potential failures),
* later when the request is queued.
*
* Request flags affect request handling, such as whether a zero length
* packet is written (the "zero" flag), whether a short read should be
* treated as an error (blocking request queue advance, the "short_not_ok"
* flag), or hinting that an interrupt is not required (the "no_interrupt"
* flag, for use with deep request queues).
*
* Bulk endpoints can use any size buffers, and can also be used for interrupt
* transfers. interrupt-only endpoints can be much less functional.
*
* NOTE: this is analogous to 'struct urb' on the host side, except that
* it's thinner and promotes more pre-allocation.
*/
struct usb_request {
void *buf; //数据缓存区
unsigned length; //数据长度
dma_addr_t dma; //与buf关联的DMA地址,DMA传输时使用
struct scatterlist *sg;
unsigned num_sgs;
unsigned num_mapped_sgs;
unsigned stream_id:16;
unsigned no_interrupt:1; //当为true时,表示没有完成函数,则通过中断通知传输完成,这个由DMA控制器直接控制
unsigned zero:1; //当输出的最后一个数据包不够长度是是否填充0
unsigned short_not_ok:1; //当接收的数据不够指定长度时,是否报错
void (*complete)(struct usb_ep *ep,
struct usb_request *req); //请求完成函数
void *context; //被completion回调函数使用
struct list_head list; //被Gadget Driver使用,插入队列
int status; //返回完成结果,0表示成功
unsigned actual; //实际传输的数据长度
};
端点
/**
* struct usb_ep - device side representation of USB endpoint
* @name:identifier for the endpoint, such as "ep-a" or "ep9in-bulk"
* @ops: Function pointers used to access hardware-specific operations.
* @ep_list:the gadget's ep_list holds all of its endpoints
* @caps:The structure describing types and directions supported by endoint.
* @maxpacket:The maximum packet size used on this endpoint. The initial
* value can sometimes be reduced (hardware allowing), according to
* the endpoint descriptor used to configure the endpoint.
* @maxpacket_limit:The maximum packet size value which can be handled by this
* endpoint. It's set once by UDC driver when endpoint is initialized, and
* should not be changed. Should not be confused with maxpacket.
* @max_streams: The maximum number of streams supported
* by this EP (0 - 16, actual number is 2^n)
* @mult: multiplier, 'mult' value for SS Isoc EPs
* @maxburst: the maximum number of bursts supported by this EP (for usb3)
* @driver_data:for use by the gadget driver.
* @address: used to identify the endpoint when finding descriptor that
* matches connection speed
* @desc: endpoint descriptor. This pointer is set before the endpoint is
* enabled and remains valid until the endpoint is disabled.
* @comp_desc: In case of SuperSpeed support, this is the endpoint companion
* descriptor that is used to configure the endpoint
*
* the bus controller driver lists all the general purpose endpoints in
* gadget->ep_list. the control endpoint (gadget->ep0) is not in that list,
* and is accessed only in response to a driver setup() callback.
*/
struct usb_ep {
void *driver_data; //端点私有数据
const char *name; //端点名称
const struct usb_ep_ops *ops; //端点操作函数集
struct list_head ep_list; //Gadget设备建立所有端点的链表
struct usb_ep_caps caps;
bool claimed;
bool enabled;
unsigned maxpacket:16; //这个端点使用的最大包长度
unsigned maxpacket_limit:16;
unsigned max_streams:16;
unsigned mult:2;
unsigned maxburst:5;
u8 address;
const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
};
端点操作函数集
struct usb_gadget;
struct usb_gadget_driver;
struct usb_udc;
/* the rest of the api to the controller hardware: device operations,
* which don't involve endpoints (or i/o).
*/
struct usb_gadget_ops {
int (*get_frame)(struct usb_gadget *);
int (*wakeup)(struct usb_gadget *);
int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
int (*vbus_session) (struct usb_gadget *, int is_active);
int (*vbus_draw) (struct usb_gadget *, unsigned mA);
int (*pullup) (struct usb_gadget *, int is_on);
int (*ioctl)(struct usb_gadget *,
unsigned code, unsigned long param);
void (*get_config_params)(struct usb_dcd_config_params *);
int (*udc_start)(struct usb_gadget *,
struct usb_gadget_driver *);
int (*udc_stop)(struct usb_gadget *);
struct usb_ep *(*match_ep)(struct usb_gadget *,
struct usb_endpoint_descriptor *,
struct usb_ss_ep_comp_descriptor *);
};
字符串结构
/*-------------------------------------------------------------------------*/
/* utility to simplify dealing with string descriptors */
/**
* struct usb_string - wraps a C string and its USB id
* @id:the (nonzero) ID for this string
* @s:the string, in UTF-8 encoding
*
* If you're using usb_gadget_get_string(), use this to wrap a string
* together with its ID.
*/
struct usb_string {
u8 id;
const char *s;
};
/**
* struct usb_gadget_strings - a set of USB strings in a given language
* @language:identifies the strings' language (0x0409 for en-us)
* @strings:array of strings with their ids
*
* If you're using usb_gadget_get_string(), use this to wrap all the
* strings for a given language.
*/
struct usb_gadget_strings {
u16 language; /* 0x0409 for en-us */
struct usb_string *strings;
};
UDC层主要数据结构,以S3C2410为例,在drivers/usb/gadget/s3c2410_udc.c
和s3c2410_udc.h
文件中。
下面的结构基本上每个UDC驱动程序都会实现,但具体实现的细节又不太相同。但万变不离其宗,宗就是上面介绍的基本gadget驱动数据结构,基本上UDC驱动程序自己实现的数据结构都是都这些基本数据结构的二次封装。
设备结构
struct s3c2410_udc {
spinlock_t lock;
struct s3c2410_ep ep[S3C2410_ENDPOINTS];
int address;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct s3c2410_request fifo_req;
u8 fifo_buf[EP_FIFO_SIZE];
u16 devstatus;
u32 port_status;
int ep0state;
unsigned got_irq : 1;
unsigned req_std : 1;
unsigned req_config : 1;
unsigned req_pending : 1;
u8 vbus;
struct dentry *regs_info;
};
程序中对这个结构的初始化:
/*---------------------------------------------------------------------------*/
static struct s3c2410_udc memory = {
.gadget = {
.ops = &s3c2410_ops,
.ep0 = &memory.ep[0].ep,
.name = gadget_name,
.dev = {
.init_name = "gadget",
},
},
/* control endpoint */
.ep[0] = {
.num = 0,
.ep = {
.name = ep0name,
.ops = &s3c2410_ep_ops,
.maxpacket = EP0_FIFO_SIZE,
.caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL,
USB_EP_CAPS_DIR_ALL),
},
.dev = &memory,
},
/* first group of endpoints */
.ep[1] = {
.num = 1,
.ep = {
.name = "ep1-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
.caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
USB_EP_CAPS_DIR_ALL),
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 1,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.ep[2] = {
.num = 2,
.ep = {
.name = "ep2-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
.caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
USB_EP_CAPS_DIR_ALL),
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 2,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.ep[3] = {
.num = 3,
.ep = {
.name = "ep3-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
.caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
USB_EP_CAPS_DIR_ALL),
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 3,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.ep[4] = {
.num = 4,
.ep = {
.name = "ep4-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
.caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK,
USB_EP_CAPS_DIR_ALL),
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 4,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
}
};
不同的UDC,自定义的数据结构不同。但一般都有这样一个数据结构来表示UDC设备,对usb_gadget设备对象进行封装,并包含设备的所有端点。
端点结构
struct s3c2410_ep {
struct list_head queue;
unsigned long last_io; /* jiffies timestamp */
struct usb_gadget *gadget;
struct s3c2410_udc *dev;
struct usb_ep ep; //封装的struct usb_ep结构
u8 num;
unsigned short fifo_size;
u8 bEndpointAddress;
u8 bmAttributes;
unsigned halted : 1;
unsigned already_seen : 1;
unsigned setup_stage : 1;
};
对usb_ep
结构进行封装,并有一个queue队列来对该端口上的request进行排队。
Request结构
struct s3c2410_request {
struct list_head queue; /* ep's requests */
struct usb_request req;
};
对usb_request
进行封装,queue变量进行队列排队。
UDC驱动是作为platform driver向platform子系统注册的,因此UDC驱动首先就需要实现struct platform_driver
结构中的函数成员:
static struct platform_driver udc_driver_24x0 = {
.driver = {
.name = "s3c24x0-usbgadget",
},
.probe = s3c2410_udc_probe, //驱动和设备绑定
.remove = s3c2410_udc_remove, //支持热插拔的设备移除
.suspend = s3c2410_udc_suspend, //电源管理相关,挂起设备
.resume = s3c2410_udc_resume, //电源管理相关,恢复设备
.id_table = s3c_udc_ids, //驱动和设备匹配信息
};
其中以s3c2410_udc_probe
和s3c2410_udc_remove
函数最为重要,s3c2410_udc_probe
函数实现驱动和设备的匹配绑定,并分配资源;而s3c2410_udc_remove
函数实现资源的释放。
/*
* probe - binds to the platform device
*/
static int s3c2410_udc_probe(struct platform_device *pdev)
{
struct s3c2410_udc *udc = &memory; //s3c2410的UDC设备,在其中对usb_gadget设备对象和端点等进行了初始化
struct device *dev = &pdev->dev;
int retval;
int irq;
dev_dbg(dev, "%s()\n", __func__);
usb_bus_clock = clk_get(NULL, "usb-bus-gadget"); //获取总线时钟并使能
if (IS_ERR(usb_bus_clock)) {
dev_err(dev, "failed to get usb bus clock source\n");
return PTR_ERR(usb_bus_clock);
}
clk_prepare_enable(usb_bus_clock);
udc_clock = clk_get(NULL, "usb-device"); //获取设备时钟并使能
if (IS_ERR(udc_clock)) {
dev_err(dev, "failed to get udc clock source\n");
return PTR_ERR(udc_clock);
}
clk_prepare_enable(udc_clock);
mdelay(10);
dev_dbg(dev, "got and enabled clocks\n");
if (strncmp(pdev->name, "s3c2440", 7) == 0) {
dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n");
memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
}
spin_lock_init(&udc->lock); //获取设备时钟并使能
udc_info = dev_get_platdata(&pdev->dev);
rsrc_start = S3C2410_PA_USBDEV; //s3c2410 UDC端口起始地址
rsrc_len = S3C24XX_SZ_USBDEV; //s3c2410端口地址长度
if (!request_mem_region(rsrc_start, rsrc_len, gadget_name)) //申请端口资源
return -EBUSY;
base_addr = ioremap(rsrc_start, rsrc_len); //端口映射
if (!base_addr) {
retval = -ENOMEM;
goto err_mem;
}
the_controller = udc;
platform_set_drvdata(pdev, udc); //驱动和设备绑定,在platform_device结构中保存udc设备对象
/*重新初始化设备*/
s3c2410_udc_disable(udc);
s3c2410_udc_reinit(udc);
/* irq setup after old hardware state is cleaned up */
/*申请中断,并绑定中断函数,中断函数是UDC功能驱动的入口函数*/
retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
0, gadget_name, udc);
if (retval != 0) {
dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
retval = -EBUSY;
goto err_map;
}
dev_dbg(dev, "got irq %i\n", IRQ_USBD);
if (udc_info && udc_info->vbus_pin > 0) {
retval = gpio_request(udc_info->vbus_pin, "udc vbus");
if (retval < 0) {
dev_err(dev, "cannot claim vbus pin\n");
goto err_int;
}
irq = gpio_to_irq(udc_info->vbus_pin);
if (irq < 0) {
dev_err(dev, "no irq for gpio vbus pin\n");
retval = irq;
goto err_gpio_claim;
}
retval = request_irq(irq, s3c2410_udc_vbus_irq,
IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING | IRQF_SHARED,
gadget_name, udc);
if (retval != 0) {
dev_err(dev, "can't get vbus irq %d, err %d\n",
irq, retval);
retval = -EBUSY;
goto err_gpio_claim;
}
dev_dbg(dev, "got irq %i\n", irq);
} else {
udc->vbus = 1;
}
if (udc_info && !udc_info->udc_command &&
gpio_is_valid(udc_info->pullup_pin)) {
retval = gpio_request_one(udc_info->pullup_pin,
udc_info->vbus_pin_inverted ?
GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW,
"udc pullup");
if (retval)
goto err_vbus_irq;
}
retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (retval)
goto err_add_udc;
if (s3c2410_udc_debugfs_root) { //创建虚拟文件debugfs
udc->regs_info = debugfs_create_file("registers", S_IRUGO,
s3c2410_udc_debugfs_root,
udc, &s3c2410_udc_debugfs_fops);
if (!udc->regs_info)
dev_warn(dev, "debugfs file creation failed\n");
}
dev_dbg(dev, "probe ok\n");
return 0;
err_add_udc:
if (udc_info && !udc_info->udc_command &&
gpio_is_valid(udc_info->pullup_pin))
gpio_free(udc_info->pullup_pin);
err_vbus_irq:
if (udc_info && udc_info->vbus_pin > 0)
free_irq(gpio_to_irq(udc_info->vbus_pin), udc);
err_gpio_claim:
if (udc_info && udc_info->vbus_pin > 0)
gpio_free(udc_info->vbus_pin);
err_int:
free_irq(IRQ_USBD, udc);
err_map:
iounmap(base_addr);
err_mem:
release_mem_region(rsrc_start, rsrc_len);
return retval;
}
从s3c2410_udc_probe
函数可以看出,probe函数主要完成的就是将platform_device
设备对象和UDC设备对象建立关系,UDC设备和驱动的一些初始化工作,并申请驱动所需的资源,若端口区间、中断号等,并将中断函数和中断号绑定。这个中断处理函数非常重要,对这个设备的所有操作都将从中断函数入口
/*
* s3c2410_udc_remove
*/
static int s3c2410_udc_remove(struct platform_device *pdev)
{
struct s3c2410_udc *udc = platform_get_drvdata(pdev); //获取UDC设备对象,在probe函数中绑定的
unsigned int irq;
dev_dbg(&pdev->dev, "%s()\n", __func__);
if (udc->driver) //设备的驱动usb_gadget_driver对象,说明设备正在使用
return -EBUSY;
usb_del_gadget_udc(&udc->gadget);
debugfs_remove(udc->regs_info); //移除debugfs文件系统中建立的文件
if (udc_info && !udc_info->udc_command &&
gpio_is_valid(udc_info->pullup_pin))
gpio_free(udc_info->pullup_pin);
if (udc_info && udc_info->vbus_pin > 0) {
irq = gpio_to_irq(udc_info->vbus_pin);
free_irq(irq, udc);
}
free_irq(IRQ_USBD, udc); //释放中断
iounmap(base_addr); /*释放端口资源*/
release_mem_region(rsrc_start, rsrc_len);
/*释放时钟*/
if (!IS_ERR(udc_clock) && udc_clock != NULL) {
clk_disable_unprepare(udc_clock);
clk_put(udc_clock);
udc_clock = NULL;
}
if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
clk_disable_unprepare(usb_bus_clock);
clk_put(usb_bus_clock);
usb_bus_clock = NULL;
}
dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);
return 0;
}
可以看出,remove函数基本上是probe函数的逆操作,将probe函数中申请的资源释放掉。
UDC驱动程序还需要为上层实现usb_gadget_register_driver
和usb_gadget_unregister_driver
两个gadget drive
r注册接口,这两个函数将实现gadget driver
和udc driver
绑定。
中断函数
中断处理函数是UDC驱动层的核心函数,由于UDC是从设备,主机端是控制端,所有的操作都是主机端发起的,所以中断处理函数是UDC驱动层的核心函数。
/*
* s3c2410_udc_irq - interrupt handler
*/
static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
{
struct s3c2410_udc *dev = _dev; //UDC设备对象
int usb_status;
int usbd_status;
int pwr_reg;
int ep0csr;
int i;
u32 idx, idx2;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
/* Driver connected ? */
if (!dev->driver) { //还没有和驱动绑定,清除中断
/* Clear interrupts */
udc_write(udc_read(S3C2410_UDC_USB_INT_REG),
S3C2410_UDC_USB_INT_REG);
udc_write(udc_read(S3C2410_UDC_EP_INT_REG),
S3C2410_UDC_EP_INT_REG);
}
/* Save index */
idx = udc_read(S3C2410_UDC_INDEX_REG); //这是哪个端点产生中断的索引号
/* Read status registers */
usb_status = udc_read(S3C2410_UDC_USB_INT_REG); //UDC设备状态
usbd_status = udc_read(S3C2410_UDC_EP_INT_REG); //UDC产生中断的端点状态
pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n",
usb_status, usbd_status, pwr_reg, ep0csr);
/*
* Now, handle interrupts. There's two types :
* - Reset, Resume, Suspend coming -> usb_int_reg
* - EP -> ep_int_reg
* 开始中断的实际处理,这里的中断只有两种类型:
* UDC设备中断: 重置、挂起和恢复
* 端点中断
*/
/* RESET */
/* UDC设备RESET操作处理 */
if (usb_status & S3C2410_UDC_USBINT_RESET) {
/* two kind of reset :
* - reset start -> pwr reg = 8
* - reset end -> pwr reg = 0
**/
dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",
ep0csr, pwr_reg);
dev->gadget.speed = USB_SPEED_UNKNOWN;
udc_write(0x00, S3C2410_UDC_INDEX_REG);
udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3,
S3C2410_UDC_MAXP_REG);
dev->address = 0;
dev->ep0state = EP0_IDLE;
dev->gadget.speed = USB_SPEED_FULL;
/* clear interrupt */
udc_write(S3C2410_UDC_USBINT_RESET,
S3C2410_UDC_USB_INT_REG);
udc_write(idx, S3C2410_UDC_INDEX_REG);
spin_unlock_irqrestore(&dev->lock, flags);
return IRQ_HANDLED;
}
/* RESUME */
/* UDC设备RESUME操作处理 */
if (usb_status & S3C2410_UDC_USBINT_RESUME) {
dprintk(DEBUG_NORMAL, "USB resume\n");
/* clear interrupt */
udc_write(S3C2410_UDC_USBINT_RESUME,
S3C2410_UDC_USB_INT_REG);
if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver
&& dev->driver->resume)
dev->driver->resume(&dev->gadget);
}
/* SUSPEND */
if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {
dprintk(DEBUG_NORMAL, "USB suspend\n");
/* clear interrupt */
udc_write(S3C2410_UDC_USBINT_SUSPEND,
S3C2410_UDC_USB_INT_REG);
if (dev->gadget.speed != USB_SPEED_UNKNOWN
&& dev->driver
&& dev->driver->suspend)
dev->driver->suspend(&dev->gadget);
dev->ep0state = EP0_IDLE;
}
/* EP */
/* control traffic */
/* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
* generate an interrupt
*/
/* 下面就是端点中断得处理 */
/* 首先是控制端点(端点0)的中断处理*/
if (usbd_status & S3C2410_UDC_INT_EP0) {
dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
/* Clear the interrupt bit by setting it to 1 */
udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
s3c2410_udc_handle_ep0(dev);
}
/* endpoint data transfers */
/* 其他端点,就是数据传输的处理*/
for (i = 1; i < S3C2410_ENDPOINTS; i++) { //遍历所有端点,找出中断的端点
u32 tmp = 1 << i;
if (usbd_status & tmp) {
dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
/* Clear the interrupt bit by setting it to 1 */
udc_write(tmp, S3C2410_UDC_EP_INT_REG);
s3c2410_udc_handle_ep(&dev->ep[i]);
}
}
/* what else causes this interrupt? a receive! who is it? */
if (!usb_status && !usbd_status && !pwr_reg && !ep0csr) {
for (i = 1; i < S3C2410_ENDPOINTS; i++) {
idx2 = udc_read(S3C2410_UDC_INDEX_REG);
udc_write(i, S3C2410_UDC_INDEX_REG);
if (udc_read(S3C2410_UDC_OUT_CSR1_REG) & 0x1)
s3c2410_udc_handle_ep(&dev->ep[i]);
/* restore index */
udc_write(idx2, S3C2410_UDC_INDEX_REG);
}
}
dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);
/* Restore old index */
udc_write(idx, S3C2410_UDC_INDEX_REG);
spin_unlock_irqrestore(&dev->lock, flags);
return IRQ_HANDLED;
}
端点操作函数
端点操作函数是UDC驱动的基础,因为大部分的动作其实都是和端点相关的,如数据传输等。
首先来看中断函数中涉及的两个函数,一个是端点0的处理函数,一个是其他端点的处理函数。
static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
{
u32 ep0csr;
struct s3c2410_ep *ep = &dev->ep[0];
struct s3c2410_request *req;
struct usb_ctrlrequest crq;
if (list_empty(&ep->queue))
req = NULL;
else
req = list_entry(ep->queue.next, struct s3c2410_request, queue);
/* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to
* S3C2410_UDC_EP0_CSR_REG when index is zero */
udc_write(0, S3C2410_UDC_INDEX_REG);
ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n",
ep0csr, ep0states[dev->ep0state]);
/* clear stall status */
if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
s3c2410_udc_nuke(dev, ep, -EPIPE);
dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
s3c2410_udc_clear_ep0_sst(base_addr);
dev->ep0state = EP0_IDLE;
return;
}
/* clear setup end */
if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {
dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
s3c2410_udc_nuke(dev, ep, 0);
s3c2410_udc_clear_ep0_se(base_addr);
dev->ep0state = EP0_IDLE;
}
switch (dev->ep0state) {
case EP0_IDLE:
s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr);
break;
case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req)
s3c2410_udc_write_fifo(ep, req);
break;
case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req)
s3c2410_udc_read_fifo(ep, req);
break;
case EP0_END_XFER:
dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
dev->ep0state = EP0_IDLE;
break;
case EP0_STALL:
dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
dev->ep0state = EP0_IDLE;
break;
}
}
其他端点的处理函数,主要是数据发送和接收
/*
* handle_ep - Manage I/O endpoints
*/
static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
{
struct s3c2410_request *req;
int is_in = ep->bEndpointAddress & USB_DIR_IN;
u32 ep_csr1;
u32 idx;
if (likely(!list_empty(&ep->queue)))
req = list_entry(ep->queue.next,
struct s3c2410_request, queue);
else
req = NULL;
idx = ep->bEndpointAddress & 0x7F;
if (is_in) {
udc_write(idx, S3C2410_UDC_INDEX_REG);
ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",
idx, ep_csr1, req ? 1 : 0);
if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
dprintk(DEBUG_VERBOSE, "st\n");
udc_write(idx, S3C2410_UDC_INDEX_REG);
udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL,
S3C2410_UDC_IN_CSR1_REG);
return;
}
if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
s3c2410_udc_write_fifo(ep, req);
} else {
udc_write(idx, S3C2410_UDC_INDEX_REG);
ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1);
if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
udc_write(idx, S3C2410_UDC_INDEX_REG);
udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL,
S3C2410_UDC_OUT_CSR1_REG);
return;
}
if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
s3c2410_udc_read_fifo(ep, req);
}
}
端点操作函数集:端点的基本操作函数
static const struct usb_ep_ops s3c2410_ep_ops = {
.enable = s3c2410_udc_ep_enable, //端点使能
.disable = s3c2410_udc_ep_disable, //关闭端点
.alloc_request = s3c2410_udc_alloc_request, //分配一个请求
.free_request = s3c2410_udc_free_request, //释放请求
.queue = s3c2410_udc_queue, //向端点提交一个请求
.dequeue = s3c2410_udc_dequeue, //从端点请求队列中删除一个请求
.set_halt = s3c2410_udc_set_halt,
};
主要分析queue这个函数,因为上层主要和这个函数打交道,接收或发送数据都需要对应的端点队列提交请求
/*
* s3c2410_udc_queue
*/
static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
gfp_t gfp_flags)
{
struct s3c2410_request *req = to_s3c2410_req(_req);
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
struct s3c2410_udc *dev;
u32 ep_csr = 0;
int fifo_count = 0;
unsigned long flags;
if (unlikely(!_ep || (!ep->ep.desc && ep->ep.name != ep0name))) {
dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
return -EINVAL;
}
dev = ep->dev;
if (unlikely(!dev->driver
|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
return -ESHUTDOWN;
}
local_irq_save(flags);
if (unlikely(!_req || !_req->complete
|| !_req->buf || !list_empty(&req->queue))) {
if (!_req)
dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
else {
dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n",
__func__, !_req->complete, !_req->buf,
!list_empty(&req->queue));
}
local_irq_restore(flags);
return -EINVAL;
}
_req->status = -EINPROGRESS;
_req->actual = 0;
dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n",
__func__, ep->bEndpointAddress, _req->length);
if (ep->bEndpointAddress) {
udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);
ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN)
? S3C2410_UDC_IN_CSR1_REG
: S3C2410_UDC_OUT_CSR1_REG);
fifo_count = s3c2410_udc_fifo_count_out();
} else {
udc_write(0, S3C2410_UDC_INDEX_REG);
ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
fifo_count = s3c2410_udc_fifo_count_out();
}
/* kickstart this i/o queue? */
if (list_empty(&ep->queue) && !ep->halted) {
if (ep->bEndpointAddress == 0 /* ep0 */) {
switch (dev->ep0state) {
case EP0_IN_DATA_PHASE:
if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY)
&& s3c2410_udc_write_fifo(ep,
req)) {
dev->ep0state = EP0_IDLE;
req = NULL;
}
break;
case EP0_OUT_DATA_PHASE:
if ((!_req->length)
|| ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
&& s3c2410_udc_read_fifo(ep,
req))) {
dev->ep0state = EP0_IDLE;
req = NULL;
}
break;
default:
local_irq_restore(flags);
return -EL2HLT;
}
} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
&& (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY))
&& s3c2410_udc_write_fifo(ep, req)) {
req = NULL;
} else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
&& fifo_count
&& s3c2410_udc_read_fifo(ep, req)) {
req = NULL;
}
}
/* pio or dma irq handler advances the queue. */
if (likely(req))
list_add_tail(&req->queue, &ep->queue);
local_irq_restore(flags);
dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);
return 0;
}
UDC驱动中还有一个重要函数,在数据传输或接收完成,即一个请求完成之后,会调用这个请求的完成函数来通知上层驱动。
/*------------------------- I/O ----------------------------------*/
/*
* s3c2410_udc_done
*/
static void s3c2410_udc_done(struct s3c2410_ep *ep,
struct s3c2410_request *req, int status)
{
unsigned halted = ep->halted;
list_del_init(&req->queue);
if (likely(req->req.status == -EINPROGRESS))
req->req.status = status;
else
status = req->req.status;
ep->halted = 1;
usb_gadget_giveback_request(&ep->ep, &req->req);
ep->halted = halted;
}
UDC设备驱动层的源码就分析得差不多了,其他很多函数都是操作寄存器,与UDC设备密切相关,但总的来说完成的功能都是一致的。可以发现,在UDC设备驱动层主要需要做以下几个工作:
1. 对usb_gadget
、usb_ep
、usb_request
三个标准数据结构进行封装,根据自己UDC的一些设备特性,设计对应的自己的数据结构;
2. 实现platform_driver
数据结构中的函数,将UDC设备驱动向platform系统进行注册;
3. 实现usb_gadget_ops
函数集,这些函数主要是操作UDC设备的一些特性(针对设备);
4. 实现usb_ep_ops
函数集,这些函数主要是操作端点的功能,如请求分配和提交等(针对端点);
5. 实现UDC设备的中断处理函数,这个函数基本上就是UDC设备驱动的核心;
这一层是可选的,介于UDC驱动层和Gadget功能层之间。主要源码在composite.c和composite.h文件中,设备层其实和硬件无关,主要实现一些通用性的代码,减少gadget功能层的代码重复工作。Gadget设备层其中承上启下的作用,联系Gadget功能层和UDC驱动层。
将composite源码独立出来,还为复合设备的实现提供了一个通用性的框架。复合设备是指在一个配置描述符中支持多个功能,或者支持多个配置的设备中,每个配置都有一个不同的功能。如一个设备同时支持网络和存储,一个设备同时支持键盘和鼠标功能等。
Gadget设备层的主要数据结构:
usb_function
结构体
/**
* struct usb_function - describes one function of a configuration
* @name: For diagnostics, identifies the function.
* @strings: tables of strings, keyed by identifiers assigned during bind()
* and by language IDs provided in control requests
* @fs_descriptors: Table of full (or low) speed descriptors, using interface and
* string identifiers assigned during @bind(). If this pointer is null,
* the function will not be available at full speed (or at low speed).
* @hs_descriptors: Table of high speed descriptors, using interface and
* string identifiers assigned during @bind(). If this pointer is null,
* the function will not be available at high speed.
* @ss_descriptors: Table of super speed descriptors, using interface and
* string identifiers assigned during @bind(). If this
* pointer is null after initiation, the function will not
* be available at super speed.
* @ssp_descriptors: Table of super speed plus descriptors, using
* interface and string identifiers assigned during @bind(). If
* this pointer is null after initiation, the function will not
* be available at super speed plus.
* @config: assigned when @usb_add_function() is called; this is the
* configuration with which this function is associated.
* @os_desc_table: Table of (interface id, os descriptors) pairs. The function
* can expose more than one interface. If an interface is a member of
* an IAD, only the first interface of IAD has its entry in the table.
* @os_desc_n: Number of entries in os_desc_table
* @bind: Before the gadget can register, all of its functions bind() to the
* available resources including string and interface identifiers used
* in interface or class descriptors; endpoints; I/O buffers; and so on.
* @unbind: Reverses @bind; called as a side effect of unregistering the
* driver which added this function.
* @free_func: free the struct usb_function.
* @mod: (internal) points to the module that created this structure.
* @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
* initialize usb_ep.driver data at this time (when it is used).
* Note that setting an interface to its current altsetting resets
* interface state, and that all interfaces have a disabled state.
* @get_alt: Returns the active altsetting. If this is not provided,
* then only altsetting zero is supported.
* @disable: (REQUIRED) Indicates the function should be disabled. Reasons
* include host resetting or reconfiguring the gadget, and disconnection.
* @setup: Used for interface-specific control requests.
* @req_match: Tests if a given class request can be handled by this function.
* @suspend: Notifies functions when the host stops sending USB traffic.
* @resume: Notifies functions when the host restarts USB traffic.
* @get_status: Returns function status as a reply to
* GetStatus() request when the recipient is Interface.
* @func_suspend: callback to be called when
* SetFeature(FUNCTION_SUSPEND) is reseived
*
* A single USB function uses one or more interfaces, and should in most
* cases support operation at both full and high speeds. Each function is
* associated by @usb_add_function() with a one configuration; that function
* causes @bind() to be called so resources can be allocated as part of
* setting up a gadget driver. Those resources include endpoints, which
* should be allocated using @usb_ep_autoconfig().
*
* To support dual speed operation, a function driver provides descriptors
* for both high and full speed operation. Except in rare cases that don't
* involve bulk endpoints, each speed needs different endpoint descriptors.
*
* Function drivers choose their own strategies for managing instance data.
* The simplest strategy just declares it "static', which means the function
* can only be activated once. If the function needs to be exposed in more
* than one configuration at a given speed, it needs to support multiple
* usb_function structures (one for each configuration).
*
* A more complex strategy might encapsulate a @usb_function structure inside
* a driver-specific instance structure to allows multiple activations. An
* example of multiple activations might be a CDC ACM function that supports
* two or more distinct instances within the same configuration, providing
* several independent logical data links to a USB host.
*/
struct usb_function {
const char *name; //功能名称
struct usb_gadget_strings **strings; //string数组,通过bind中分配的id访问
struct usb_descriptor_header **fs_descriptors;//全速的描述符表,用于bind中分配的接口描述符和string描述符
struct usb_descriptor_header **hs_descriptors;//高速描述符表
struct usb_descriptor_header **ss_descriptors;
struct usb_descriptor_header **ssp_descriptors;
struct usb_configuration *config; //调用usb_add_function()函数赋值,是这个功能关联的配置描述符
struct usb_os_desc_table *os_desc_table;
unsigned os_desc_n;
/* REVISIT: bind() functions can be marked __init, which
* makes trouble for section mismatch analysis. See if
* we can't restructure things to avoid mismatching.
* Related: unbind() may kfree() but bind() won't...
*/
/* configuration management: bind/unbind */
int (*bind)(struct usb_configuration *,
struct usb_function *);
void (*unbind)(struct usb_configuration *,
struct usb_function *);
void (*free_func)(struct usb_function *f);
struct module *mod;
/* runtime state management */
int (*set_alt)(struct usb_function *,
unsigned interface, unsigned alt); /*重新配置altsetting,*/
int (*get_alt)(struct usb_function *,
unsigned interface); /*获取当前altsetting*/
void (*disable)(struct usb_function *); /*关闭功能*/
int (*setup)(struct usb_function *,
const struct usb_ctrlrequest *); /*接口相关的控制处理*/
bool (*req_match)(struct usb_function *,
const struct usb_ctrlrequest *);
void (*suspend)(struct usb_function *); /*电源管理相关的挂起功能*/
void (*resume)(struct usb_function *); /*电源管理相关的恢复功能*/
/* USB 3.0 additions */
int (*get_status)(struct usb_function *);
int (*func_suspend)(struct usb_function *,
u8 suspend_opt);
/* private: */
/* internals */
struct list_head list;
DECLARE_BITMAP(endpoints, 32);
const struct usb_function_instance *fi;
unsigned int bind_deactivated:1;
};
usb_configuration
结构体
/**
* struct usb_configuration - represents one gadget configuration
* @label: For diagnostics, describes the configuration.
* @strings: Tables of strings, keyed by identifiers assigned during @bind()
* and by language IDs provided in control requests.
* @descriptors: Table of descriptors preceding all function descriptors.
* Examples include OTG and vendor-specific descriptors.
* @unbind: Reverses @bind; called as a side effect of unregistering the
* driver which added this configuration.
* @setup: Used to delegate control requests that aren't handled by standard
* device infrastructure or directed at a specific interface.
* @bConfigurationValue: Copied into configuration descriptor.
* @iConfiguration: Copied into configuration descriptor.
* @bmAttributes: Copied into configuration descriptor.
* @MaxPower: Power consumtion in mA. Used to compute bMaxPower in the
* configuration descriptor after considering the bus speed.
* @cdev: assigned by @usb_add_config() before calling @bind(); this is
* the device associated with this configuration.
*
* Configurations are building blocks for gadget drivers structured around
* function drivers. Simple USB gadgets require only one function and one
* configuration, and handle dual-speed hardware by always providing the same
* functionality. Slightly more complex gadgets may have more than one
* single-function configuration at a given speed; or have configurations
* that only work at one speed.
*
* Composite devices are, by definition, ones with configurations which
* include more than one function.
*
* The lifecycle of a usb_configuration includes allocation, initialization
* of the fields described above, and calling @usb_add_config() to set up
* internal data and bind it to a specific device. The configuration's
* @bind() method is then used to initialize all the functions and then
* call @usb_add_function() for them.
*
* Those functions would normally be independent of each other, but that's
* not mandatory. CDC WMC devices are an example where functions often
* depend on other functions, with some functions subsidiary to others.
* Such interdependency may be managed in any way, so long as all of the
* descriptors complete by the time the composite driver returns from
* its bind() routine.
* usb_configuration 表示一个Gadget配置
*/
struct usb_configuration {
const char *label; //配置名称
struct usb_gadget_strings **strings; //字符串表
const struct usb_descriptor_header **descriptors; //功能描述符表
/* REVISIT: bind() functions can be marked __init, which
* makes trouble for section mismatch analysis. See if
* we can't restructure things to avoid mismatching...
*/
/* configuration management: unbind/setup */
void (*unbind)(struct usb_configuration *);
int (*setup)(struct usb_configuration *,
const struct usb_ctrlrequest *);
/* fields in the config descriptor */
/*用来赋值配置描述符*/
u8 bConfigurationValue;
u8 iConfiguration;
u8 bmAttributes;
u16 MaxPower;
struct usb_composite_dev *cdev; /*和composite设备关联,在usb_add_config函数中设置*/
/* private: */
/* internals */
struct list_head list;
struct list_head functions; //功能链表
u8 next_interface_id;
unsigned superspeed:1;
unsigned highspeed:1;
unsigned fullspeed:1;
unsigned superspeed_plus:1;
struct usb_function *interface[MAX_CONFIG_INTERFACES];
};
usb_composite_driver
结构体
/**
* struct usb_composite_driver - groups configurations into a gadget
* @name: For diagnostics, identifies the driver.
* @dev: Template descriptor for the device, including default device
* identifiers.
* @strings: tables of strings, keyed by identifiers assigned during @bind
* and language IDs provided in control requests. Note: The first entries
* are predefined. The first entry that may be used is
* USB_GADGET_FIRST_AVAIL_IDX
* @max_speed: Highest speed the driver supports.
* @needs_serial: set to 1 if the gadget needs userspace to provide
* a serial number. If one is not provided, warning will be printed.
* @bind: (REQUIRED) Used to allocate resources that are shared across the
* whole device, such as string IDs, and add its configurations using
* @usb_add_config(). This may fail by returning a negative errno
* value; it should return zero on successful initialization.
* @unbind: Reverses @bind; called as a side effect of unregistering
* this driver.
* @disconnect: optional driver disconnect method
* @suspend: Notifies when the host stops sending USB traffic,
* after function notifications
* @resume: Notifies configuration when the host restarts USB traffic,
* before function notifications
* @gadget_driver: Gadget driver controlling this driver
*
* Devices default to reporting self powered operation. Devices which rely
* on bus powered operation should report this in their @bind method.
*
* Before returning from @bind, various fields in the template descriptor
* may be overridden. These include the idVendor/idProduct/bcdDevice values
* normally to bind the appropriate host side driver, and the three strings
* (iManufacturer, iProduct, iSerialNumber) normally used to provide user
* meaningful device identifiers. (The strings will not be defined unless
* they are defined in @dev and @strings.) The correct ep0 maxpacket size
* is also reported, as defined by the underlying controller driver.
*/
struct usb_composite_driver {
const char *name; //驱动名称
const struct usb_device_descriptor *dev; //设备描述符
struct usb_gadget_strings **strings; //字符串表
enum usb_device_speed max_speed;
unsigned needs_serial:1;
int (*bind)(struct usb_composite_dev *cdev);
int (*unbind)(struct usb_composite_dev *);
void (*disconnect)(struct usb_composite_dev *);
/* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
struct usb_gadget_driver gadget_driver;
};
usb_composite_dev
结构体
/**
* struct usb_composite_device - represents one composite usb gadget
* @gadget: read-only, abstracts the gadget's usb peripheral controller
* @req: used for control responses; buffer is pre-allocated
* @os_desc_req: used for OS descriptors responses; buffer is pre-allocated
* @config: the currently active configuration
* @qw_sign: qwSignature part of the OS string
* @b_vendor_code: bMS_VendorCode part of the OS string
* @use_os_string: false by default, interested gadgets set it
* @os_desc_config: the configuration to be used with OS descriptors
* @setup_pending: true when setup request is queued but not completed
* @os_desc_pending: true when os_desc request is queued but not completed
*
* One of these devices is allocated and initialized before the
* associated device driver's bind() is called.
*
* OPEN ISSUE: it appears that some WUSB devices will need to be
* built by combining a normal (wired) gadget with a wireless one.
* This revision of the gadget framework should probably try to make
* sure doing that won't hurt too much.
*
* One notion for how to handle Wireless USB devices involves:
* (a) a second gadget here, discovery mechanism TBD, but likely
* needing separate "register/unregister WUSB gadget" calls;
* (b) updates to usb_gadget to include flags "is it wireless",
* "is it wired", plus (presumably in a wrapper structure)
* bandgroup and PHY info;
* (c) presumably a wireless_ep wrapping a usb_ep, and reporting
* wireless-specific parameters like maxburst and maxsequence;
* (d) configurations that are specific to wireless links;
* (e) function drivers that understand wireless configs and will
* support wireless for (additional) function instances;
* (f) a function to support association setup (like CBAF), not
* necessarily requiring a wireless adapter;
* (g) composite device setup that can create one or more wireless
* configs, including appropriate association setup support;
* (h) more, TBD.
*/
struct usb_composite_dev {
struct usb_gadget *gadget; //关联的gadget
struct usb_request *req; //用于控制响应,提前分配的
struct usb_request *os_desc_req;
struct usb_configuration *config; //当前配置
/* OS String is a custom (yet popular) extension to the USB standard. */
u8 qw_sign[OS_STRING_QW_SIGN_LEN];
u8 b_vendor_code;
struct usb_configuration *os_desc_config;
unsigned int use_os_string:1;
/* private: */
/* internals */
unsigned int suspended:1;
struct usb_device_descriptor desc;
struct list_head configs; //配置链表
struct list_head gstrings;
struct usb_composite_driver *driver;
u8 next_string_id;
char *def_manufacturer;
/* the gadget driver won't enable the data pullup
* while the deactivation count is nonzero.
*/
unsigned deactivations;
/* the composite driver won't complete the control transfer's
* data/status stages till delayed_status is zero.
*/
int delayed_status;
/* protects deactivations and delayed_status counts*/
spinlock_t lock;
unsigned setup_pending:1;
unsigned os_desc_pending:1;
};
其中重要的函数:
composite_bind
函数
static int composite_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *gdriver)
{
struct usb_composite_dev *cdev;
struct usb_composite_driver *composite = to_cdriver(gdriver);
int status = -ENOMEM;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
if (!cdev)
return status;
spin_lock_init(&cdev->lock);
cdev->gadget = gadget;
set_gadget_data(gadget, cdev);
INIT_LIST_HEAD(&cdev->configs);
INIT_LIST_HEAD(&cdev->gstrings);
status = composite_dev_prepare(composite, cdev);
if (status)
goto fail;
/* composite gadget needs to assign strings for whole device (like
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
status = composite->bind(cdev);
if (status < 0)
goto fail;
if (cdev->use_os_string) {
status = composite_os_desc_req_prepare(cdev, gadget->ep0);
if (status)
goto fail;
}
update_unchanged_dev_desc(&cdev->desc, composite->dev);
/* has userspace failed to provide a serial number? */
if (composite->needs_serial && !cdev->desc.iSerialNumber)
WARNING(cdev, "userspace failed to provide iSerialNumber\n");
INFO(cdev, "%s ready\n", composite->name);
return 0;
fail:
__composite_unbind(gadget, false);
return status;
}
composite_bind
函数主要完成UDC驱动层gadget设备和设备层composite设备关系的建立,分配控制端点0的请求数据结构,设置设备描述符,并调用功能层的bind函数分配功能层所需要的资源等工作。
composite_unbind
函数
static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
/* composite_disconnect() must already have been called
* by the underlying peripheral controller driver!
* so there's no i/o concurrency that could affect the
* state protected by cdev->lock.
*/
WARN_ON(cdev->config);
while (!list_empty(&cdev->configs)) {
struct usb_configuration *c;
c = list_first_entry(&cdev->configs,
struct usb_configuration, list);
remove_config(cdev, c);
}
if (cdev->driver->unbind && unbind_driver)
cdev->driver->unbind(cdev);
composite_dev_cleanup(cdev);
kfree(cdev->def_manufacturer);
kfree(cdev);
set_gadget_data(gadget, NULL);
}
static void composite_unbind(struct usb_gadget *gadget)
{
__composite_unbind(gadget, true);
}
Unbind函数完成bind函数中分配的资源,并遍历所有配置和各个配置下的功能,调用其对应得unbind函数来释放资源。
在配置中增加一个功能,每个配置初始化后都必须至少有一个功能。
usb_add_function
函数
/**
* usb_add_function() - add a function to a configuration
* @config: the configuration
* @function: the function being added
* Context: single threaded during gadget setup
*
* After initialization, each configuration must have one or more
* functions added to it. Adding a function involves calling its @bind()
* method to allocate resources such as interface and string identifiers
* and endpoints.
*
* This function returns the value of the function's bind(), which is
* zero for success else a negative errno value.
*/
int usb_add_function(struct usb_configuration *config,
struct usb_function *function)
{
int value = -EINVAL;
DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
function->name, function,
config->label, config);
if (!function->set_alt || !function->disable)
goto done;
function->config = config;
list_add_tail(&function->list, &config->functions);
if (function->bind_deactivated) {
value = usb_function_deactivate(function);
if (value)
goto done;
}
/* REVISIT *require* function->bind? */
if (function->bind) {
value = function->bind(config, function);
if (value < 0) {
list_del(&function->list);
function->config = NULL;
}
} else
value = 0;
/* We allow configurations that don't work at both speeds.
* If we run into a lowspeed Linux system, treat it the same
* as full speed ... it's the function drivers that will need
* to avoid bulk and ISO transfers.
*/
if (!config->fullspeed && function->fs_descriptors)
config->fullspeed = true;
if (!config->highspeed && function->hs_descriptors)
config->highspeed = true;
if (!config->superspeed && function->ss_descriptors)
config->superspeed = true;
if (!config->superspeed_plus && function->ssp_descriptors)
config->superspeed_plus = true;
done:
if (value)
DBG(config->cdev, "adding '%s'/%p --> %d\n",
function->name, function, value);
return value;
}
为设备增加一个配置,这个实现原理和在配置下增加一个功能类似
usb_add_config
函数:
/**
* usb_add_config() - add a configuration to a device.
* @cdev: wraps the USB gadget
* @config: the configuration, with bConfigurationValue assigned
* @bind: the configuration's bind function
* Context: single threaded during gadget setup
*
* One of the main tasks of a composite @bind() routine is to
* add each of the configurations it supports, using this routine.
*
* This function returns the value of the configuration's @bind(), which
* is zero for success else a negative errno value. Binding configurations
* assigns global resources including string IDs, and per-configuration
* resources such as interface IDs and endpoints.
*/
int usb_add_config(struct usb_composite_dev *cdev,
struct usb_configuration *config,
int (*bind)(struct usb_configuration *))
{
int status = -EINVAL;
if (!bind)
goto done;
DBG(cdev, "adding config #%u '%s'/%p\n",
config->bConfigurationValue,
config->label, config);
status = usb_add_config_only(cdev, config);
if (status)
goto done;
status = bind(config);
if (status < 0) {
while (!list_empty(&config->functions)) {
struct usb_function *f;
f = list_first_entry(&config->functions,
struct usb_function, list);
list_del(&f->list);
if (f->unbind) {
DBG(cdev, "unbind function '%s'/%p\n",
f->name, f);
f->unbind(config, f);
/* may free memory for "f" */
}
}
list_del(&config->list);
config->cdev = NULL;
} else {
unsigned i;
DBG(cdev, "cfg %d/%p speeds:%s%s%s%s\n",
config->bConfigurationValue, config,
config->superspeed_plus ? " superplus" : "",
config->superspeed ? " super" : "",
config->highspeed ? " high" : "",
config->fullspeed
? (gadget_is_dualspeed(cdev->gadget)
? " full"
: " full/low")
: "");
for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
struct usb_function *f = config->interface[i];
if (!f)
continue;
DBG(cdev, " interface %d = %s/%p\n",
i, f->name, f);
}
}
/* set_alt(), or next bind(), sets up ep->claimed as needed */
usb_ep_autoconfig_reset(cdev->gadget);
done:
if (status)
DBG(cdev, "added config '%s'/%u --> %d\n", config->label,
config->bConfigurationValue, status);
return status;
}
设置一个配置
set_config
函数
static int set_config(struct usb_composite_dev *cdev,
const struct usb_ctrlrequest *ctrl, unsigned number)
{
struct usb_gadget *gadget = cdev->gadget;
struct usb_configuration *c = NULL;
int result = -EINVAL;
unsigned power = gadget_is_otg(gadget) ? 8 : 100;
int tmp;
if (number) {
list_for_each_entry(c, &cdev->configs, list) {
if (c->bConfigurationValue == number) {
/*
* We disable the FDs of the previous
* configuration only if the new configuration
* is a valid one
*/
if (cdev->config)
reset_config(cdev);
result = 0;
break;
}
}
if (result < 0)
goto done;
} else { /* Zero configuration value - need to reset the config */
if (cdev->config)
reset_config(cdev);
result = 0;
}
INFO(cdev, "%s config #%d: %s\n",
usb_speed_string(gadget->speed),
number, c ? c->label : "unconfigured");
if (!c)
goto done;
usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
cdev->config = c;
/* Initialize all interfaces by setting them to altsetting zero. */
for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
struct usb_function *f = c->interface[tmp];
struct usb_descriptor_header **descriptors;
if (!f)
break;
/*
* Record which endpoints are used by the function. This is used
* to dispatch control requests targeted at that endpoint to the
* function's setup callback instead of the current
* configuration's setup callback.
*/
descriptors = function_descriptors(f, gadget->speed);
for (; *descriptors; ++descriptors) {
struct usb_endpoint_descriptor *ep;
int addr;
if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
continue;
ep = (struct usb_endpoint_descriptor *)*descriptors;
addr = ((ep->bEndpointAddress & 0x80) >> 3)
| (ep->bEndpointAddress & 0x0f);
set_bit(addr, f->endpoints);
}
result = f->set_alt(f, tmp, 0);
if (result < 0) {
DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
tmp, f->name, f, result);
reset_config(cdev);
goto done;
}
if (result == USB_GADGET_DELAYED_STATUS) {
DBG(cdev,
"%s: interface %d (%s) requested delayed status\n",
__func__, tmp, f->name);
cdev->delayed_status++;
DBG(cdev, "delayed_status count %d\n",
cdev->delayed_status);
}
}
/* when we return, be sure our power usage is valid */
power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW;
done:
usb_gadget_vbus_draw(gadget, power);
if (result >= 0 && cdev->delayed_status)
result = USB_GADGET_DELAYED_STATUS;
return result;
}
setup函数完成了ep0所需要处理的而底层不能处理的功能,由于这一个层实现了这个函数,所以功能层就不需要关注于USB协议的这些事务性处理,而重点放在需要实现的功能上
composite_setup
函数:
/*
* The setup() callback implements all the ep0 functionality that's
* not handled lower down, in hardware or the hardware driver(like
* device and endpoint feature flags, and their status). It's all
* housekeeping for the gadget function we're implementing. Most of
* the work is in config and function specific setup.
*/
int
composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
int status = 0;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u8 intf = w_index & 0xFF;
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
struct usb_function *f = NULL;
u8 endp;
/* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion
* when we delegate to it.
*/
req->zero = 0;
req->context = cdev;
req->complete = composite_setup_complete;
req->length = 0;
gadget->ep0->driver_data = cdev;
/*
* Don't let non-standard requests match any of the cases below
* by accident.
*/
if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
goto unknown;
switch (ctrl->bRequest) {
/* we handle all standard USB descriptors */
case USB_REQ_GET_DESCRIPTOR:
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
switch (w_value >> 8) {
case USB_DT_DEVICE:
cdev->desc.bNumConfigurations =
count_configs(cdev, USB_DT_DEVICE);
cdev->desc.bMaxPacketSize0 =
cdev->gadget->ep0->maxpacket;
if (gadget_is_superspeed(gadget)) {
if (gadget->speed >= USB_SPEED_SUPER) {
cdev->desc.bcdUSB = cpu_to_le16(0x0310);
cdev->desc.bMaxPacketSize0 = 9;
} else {
cdev->desc.bcdUSB = cpu_to_le16(0x0210);
}
} else {
cdev->desc.bcdUSB = cpu_to_le16(0x0200);
}
value = min(w_length, (u16) sizeof cdev->desc);
memcpy(req->buf, &cdev->desc, value);
break;
case USB_DT_DEVICE_QUALIFIER:
if (!gadget_is_dualspeed(gadget) ||
gadget->speed >= USB_SPEED_SUPER)
break;
device_qual(cdev);
value = min_t(int, w_length,
sizeof(struct usb_qualifier_descriptor));
break;
case USB_DT_OTHER_SPEED_CONFIG:
if (!gadget_is_dualspeed(gadget) ||
gadget->speed >= USB_SPEED_SUPER)
break;
/* FALLTHROUGH */
case USB_DT_CONFIG:
value = config_desc(cdev, w_value);
if (value >= 0)
value = min(w_length, (u16) value);
break;
case USB_DT_STRING:
value = get_string(cdev, req->buf,
w_index, w_value & 0xff);
if (value >= 0)
value = min(w_length, (u16) value);
break;
case USB_DT_BOS:
if (gadget_is_superspeed(gadget)) {
value = bos_desc(cdev);
value = min(w_length, (u16) value);
}
break;
case USB_DT_OTG:
if (gadget_is_otg(gadget)) {
struct usb_configuration *config;
int otg_desc_len = 0;
if (cdev->config)
config = cdev->config;
else
config = list_first_entry(
&cdev->configs,
struct usb_configuration, list);
if (!config)
goto done;
if (gadget->otg_caps &&
(gadget->otg_caps->otg_rev >= 0x0200))
otg_desc_len += sizeof(
struct usb_otg20_descriptor);
else
otg_desc_len += sizeof(
struct usb_otg_descriptor);
value = min_t(int, w_length, otg_desc_len);
memcpy(req->buf, config->descriptors[0], value);
}
break;
}
break;
/* any number of configs can work */
case USB_REQ_SET_CONFIGURATION:
if (ctrl->bRequestType != 0)
goto unknown;
if (gadget_is_otg(gadget)) {
if (gadget->a_hnp_support)
DBG(cdev, "HNP available\n");
else if (gadget->a_alt_hnp_support)
DBG(cdev, "HNP on another port\n");
else
VDBG(cdev, "HNP inactive\n");
}
spin_lock(&cdev->lock);
value = set_config(cdev, ctrl, w_value);
spin_unlock(&cdev->lock);
break;
case USB_REQ_GET_CONFIGURATION:
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
if (cdev->config)
*(u8 *)req->buf = cdev->config->bConfigurationValue;
else
*(u8 *)req->buf = 0;
value = min(w_length, (u16) 1);
break;
/* function drivers must handle get/set altsetting; if there's
* no get() method, we know only altsetting zero works.
*/
case USB_REQ_SET_INTERFACE:
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
if (w_value && !f->set_alt)
break;
value = f->set_alt(f, w_index, w_value);
if (value == USB_GADGET_DELAYED_STATUS) {
DBG(cdev,
"%s: interface %d (%s) requested delayed status\n",
__func__, intf, f->name);
cdev->delayed_status++;
DBG(cdev, "delayed_status count %d\n",
cdev->delayed_status);
}
break;
case USB_REQ_GET_INTERFACE:
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
goto unknown;
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
/* lots of interfaces only need altsetting zero... */
value = f->get_alt ? f->get_alt(f, w_index) : 0;
if (value < 0)
break;
*((u8 *)req->buf) = value;
value = min(w_length, (u16) 1);
break;
case USB_REQ_GET_STATUS:
if (gadget_is_otg(gadget) && gadget->hnp_polling_support &&
(w_index == OTG_STS_SELECTOR)) {
if (ctrl->bRequestType != (USB_DIR_IN |
USB_RECIP_DEVICE))
goto unknown;
*((u8 *)req->buf) = gadget->host_request_flag;
value = 1;
break;
}
/*
* USB 3.0 additions:
* Function driver should handle get_status request. If such cb
* wasn't supplied we respond with default value = 0
* Note: function driver should supply such cb only for the
* first interface of the function
*/
if (!gadget_is_superspeed(gadget))
goto unknown;
if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
goto unknown;
value = 2; /* This is the length of the get_status reply */
put_unaligned_le16(0, req->buf);
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
status = f->get_status ? f->get_status(f) : 0;
if (status < 0)
break;
put_unaligned_le16(status & 0x0000ffff, req->buf);
break;
/*
* Function drivers should handle SetFeature/ClearFeature
* (FUNCTION_SUSPEND) request. function_suspend cb should be supplied
* only for the first interface of the function
*/
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
if (!gadget_is_superspeed(gadget))
goto unknown;
if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE))
goto unknown;
switch (w_value) {
case USB_INTRF_FUNC_SUSPEND:
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
value = 0;
if (f->func_suspend)
value = f->func_suspend(f, w_index >> 8);
if (value < 0) {
ERROR(cdev,
"func_suspend() returned error %d\n",
value);
value = 0;
}
break;
}
break;
default:
unknown:
/*
* OS descriptors handling
*/
if (cdev->use_os_string && cdev->os_desc_config &&
(ctrl->bRequestType & USB_TYPE_VENDOR) &&
ctrl->bRequest == cdev->b_vendor_code) {
struct usb_request *req;
struct usb_configuration *os_desc_cfg;
u8 *buf;
int interface;
int count = 0;
req = cdev->os_desc_req;
req->context = cdev;
req->complete = composite_setup_complete;
buf = req->buf;
os_desc_cfg = cdev->os_desc_config;
memset(buf, 0, w_length);
buf[5] = 0x01;
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_DEVICE:
if (w_index != 0x4 || (w_value >> 8))
break;
buf[6] = w_index;
if (w_length == 0x10) {
/* Number of ext compat interfaces */
count = count_ext_compat(os_desc_cfg);
buf[8] = count;
count *= 24; /* 24 B/ext compat desc */
count += 16; /* header */
put_unaligned_le32(count, buf);
value = w_length;
} else {
/* "extended compatibility ID"s */
count = count_ext_compat(os_desc_cfg);
buf[8] = count;
count *= 24; /* 24 B/ext compat desc */
count += 16; /* header */
put_unaligned_le32(count, buf);
buf += 16;
fill_ext_compat(os_desc_cfg, buf);
value = w_length;
}
break;
case USB_RECIP_INTERFACE:
if (w_index != 0x5 || (w_value >> 8))
break;
interface = w_value & 0xFF;
buf[6] = w_index;
if (w_length == 0x0A) {
count = count_ext_prop(os_desc_cfg,
interface);
put_unaligned_le16(count, buf + 8);
count = len_ext_prop(os_desc_cfg,
interface);
put_unaligned_le32(count, buf);
value = w_length;
} else {
count = count_ext_prop(os_desc_cfg,
interface);
put_unaligned_le16(count, buf + 8);
count = len_ext_prop(os_desc_cfg,
interface);
put_unaligned_le32(count, buf);
buf += 10;
value = fill_ext_prop(os_desc_cfg,
interface, buf);
if (value < 0)
return value;
value = w_length;
}
break;
}
if (value >= 0) {
req->length = value;
req->context = cdev;
req->zero = value < w_length;
value = composite_ep0_queue(cdev, req,
GFP_ATOMIC);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
composite_setup_complete(gadget->ep0,
req);
}
}
return value;
}
VDBG(cdev,
"non-core control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
/* functions always handle their interfaces and endpoints...
* punt other recipients (other, WUSB, ...) to the current
* configuration code.
*
* REVISIT it could make sense to let the composite device
* take such requests too, if that's ever needed: to work
* in config 0, etc.
*/
if (cdev->config) {
list_for_each_entry(f, &cdev->config->functions, list)
if (f->req_match && f->req_match(f, ctrl))
goto try_fun_setup;
f = NULL;
}
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
break;
case USB_RECIP_ENDPOINT:
endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
list_for_each_entry(f, &cdev->config->functions, list) {
if (test_bit(endp, f->endpoints))
break;
}
if (&f->list == &cdev->config->functions)
f = NULL;
break;
}
try_fun_setup:
if (f && f->setup)
value = f->setup(f, ctrl);
else {
struct usb_configuration *c;
c = cdev->config;
if (!c)
goto done;
/* try current config's setup */
if (c->setup) {
value = c->setup(c, ctrl);
goto done;
}
/* try the only function in the current config */
if (!list_is_singular(&c->functions))
goto done;
f = list_first_entry(&c->functions, struct usb_function,
list);
if (f->setup)
value = f->setup(f, ctrl);
}
goto done;
}
/* respond with data transfer before status phase? */
if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
req->length = value;
req->context = cdev;
req->zero = value < w_length;
value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
composite_setup_complete(gadget->ep0, req);
}
} else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
WARN(cdev,
"%s: Delayed status not supported for w_length != 0",
__func__);
}
done:
/* device either stalls (value < 0) or reports success */
return value;
}
Gadget功能层完成USB设备的具体功能,其表现的形式各不相同,如键盘、鼠标、存储和网卡等等。功能层不仅涉及到Gadget驱动相关的内容,还涉及到其功能相关的内核子系统。如存储还涉及到内核存储子系统,网卡还涉及到网络驱动子系统。因此,Gadget功能的代码非常复杂。
未完待续。。。。