16.5 USB OTG驱动
OTG是On-The-Go的缩写,是近年来发展的技术,2001年12月18日由USB Implementers Forum公布,主要应用于各种不同的设备或移动设备之间的联接,进行数据交换。
特别是Pad、移动电话、消费类设备。改变如数码相机、摄像机、打印机等设备之间多种不同制式连机器,多大7种制式的存储卡数据交换的不便。2014年左右开始在市场普及。
USB OTG标准在完全兼容USB 2.0标准的基础上,USB OTG允许设备既可作为主机,也可作为外设操作,OTG新增主机通令协议(HNP)和对话请求协议(SRP)。
在OTG中,初始主机设备称为A设备,外设称为B设备。可用电缆的连接方式来决定初始角色。两用设备使用新型Mini-AB插座,从而使Mini-A插头、Mini-B插头和Mini-AB插座增添了第5个引脚,以用于识别不同的电缆端点。Mini-A插头中的ID引脚接地,Mini-B插头中的ID引脚浮空。当OTG设备检测到接地的ID引脚时,表示默认的是A设备(主机),而检测到ID引脚浮空的设备则认为是B设备(外设)。系统一旦连接后,OTG的角色还可以更换,以采用新的HNP协议。而SRP允许B设备请求A设备打开VBUS电源并启动一次对话。一次OTG对话可通过A设备提供VBUS电源的时间来确定。
自Linux 2.6.9,OTG相关源代码已经被包含在内核中,新增的主要内容包括:
linux/usb/gadget.h
* struct usb_gadget - represents a usb slave device
* @work: (internal use) Workqueue to be used for sysfs_notify()
* @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
* @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.
* @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to
* MaxPacketSize.
* @xfer_isr_count: UI (transfer complete) interrupts count
* @usb_core_id: Identifies the usb core controlled by this usb_gadget.
* Used in case of more then one core operates concurrently.
* @bam2bam_func_enabled; Indicates function using bam2bam is enabled or not.
* @extra_buf_alloc: Extra allocation size for AXI prefetch so that out of
* boundary access is protected.
* @interrupt_num: Interrupt number for the underlying platform device.
*
* 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;
/* readonly to gadget driver */
const struct usb_gadget_ops *ops;
struct usb_ep *ep0;
struct list_head ep_list; /* of usb_ep */
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;
unsigned sg_supported:1;
unsigned is_otg:1; /* C语言中的位域结构体,is_otg占用1位,其值只能是0和1 */
unsigned is_a_peripheral:1;
unsigned b_hnp_enable:1;
unsigned a_hnp_support:1;
unsigned a_alt_hnp_support:1;
unsigned quirk_ep_out_aligned_size:1;
bool remote_wakeup;
u32 xfer_isr_count;
u8 usb_core_id;
bool l1_supported;
bool bam2bam_func_enabled;
u32 extra_buf_alloc;
int interrupt_num;
};
/**
* usb_gadget_vbus_connect - Notify controller that VBUS is powered
* @gadget:The device which now has VBUS power.
* Context: can sleep
*
* This call is used by a driver for an external transceiver (or GPIO)
* that detects a VBUS power session starting. Common responses include
* resuming the controller, activating the D+ (or D-) pullup to let the
* host detect that a USB device is attached, and starting to draw power
* (8mA or possibly more, especially after SET_CONFIGURATION).
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_vbus_connect(struct usb_gadget *gadget)
{
if (!gadget->ops->vbus_session)
return -EOPNOTSUPP;
return gadget->ops->vbus_session(gadget, 1);
}
/**
* usb_gadget_vbus_draw - constrain controller's VBUS power usage
* @gadget:The device whose VBUS usage is being described
* @mA:How much current to draw, in milliAmperes. This should be twice
* the value listed in the configuration descriptor bMaxPower field.
*
* This call is used by gadget drivers during SET_CONFIGURATION calls,
* reporting how much power the device may consume. For example, this
* could affect how quickly batteries are recharged.
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
{
if (!gadget->ops->vbus_draw)
return -EOPNOTSUPP;
return gadget->ops->vbus_draw(gadget, mA);
}
/**
* usb_gadget_vbus_disconnect - notify controller about VBUS session end
* @gadget:the device whose VBUS supply is being described
* Context: can sleep
*
* This call is used by a driver for an external transceiver (or GPIO)
* that detects a VBUS power session ending. Common responses include
* reversing everything done in usb_gadget_vbus_connect().
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_vbus_disconnect(struct usb_gadget *gadget)
{
if (!gadget->ops->vbus_session)
return -EOPNOTSUPP;
return gadget->ops->vbus_session(gadget, 0);
}
/**
* usb_gadget_connect - software-controlled connect to USB host
* @gadget:the peripheral being connected
*
* Enables the D+ (or potentially D-) pullup. The host will start
* enumerating this gadget when the pullup is active and a VBUS session
* is active (the link is powered). This pullup is always enabled unless
* usb_gadget_disconnect() has been used to disable it.
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_connect(struct usb_gadget *gadget)
{
if (!gadget->ops->pullup)
return -EOPNOTSUPP;
return gadget->ops->pullup(gadget, 1);
}
/**
* usb_gadget_disconnect - software-controlled disconnect from USB host
* @gadget:the peripheral being disconnected
*
* Disables the D+ (or potentially D-) pullup, which the host may see
* as a disconnect (when a VBUS session is active). Not all systems
* support software pullup controls.
*
* This routine may be used during the gadget driver bind() call to prevent
* the peripheral from ever being visible to the USB host, unless later
* usb_gadget_connect() is called. For example, user mode components may
* need to be activated before the system can talk to hosts.
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_disconnect(struct usb_gadget *gadget)
{
if (!gadget->ops->pullup)
return -EOPNOTSUPP;
return gadget->ops->pullup(gadget, 0);
}
/**
* usb_gadget_wakeup - tries to wake up the host connected to this gadget
* @gadget: controller used to wake up the host
*
* Returns zero on success, else negative error code if the hardware
* doesn't support such attempts, or its support has not been enabled
* by the usb host. Drivers must return device descriptors that report
* their ability to support this, or hosts won't enable it.
*
* This may also try to use SRP to wake the host and start enumeration,
* even if OTG isn't otherwise in use. OTG devices may also start
* remote wakeup even when hosts don't explicitly enable it.
*/
static inline int usb_gadget_wakeup(struct usb_gadget *gadget)
{
if (!gadget->ops->wakeup)
return -EOPNOTSUPP;
return gadget->ops->wakeup(gadget);
}
(2)Gadget驱动端添加的OTG相关属性和函数
如果gadget->is_otg属性为真,则增加一个OTG描述符;通过printk()、LED等方式报告HNP(主机通令协议)可用;当挂起开始时,通过用户界面报告HNP切换开始。
(3)主机侧添加的OTG相关属性和函数
在USB核心中新增了关于OTG设备枚举的信息:
linux/usb.h
struct usb_bus {
struct device *controller; /* host/master side hardware */
int busnum; /* Bus number (in order of reg) */
const char *bus_name; /* stable id (PCI slot_name etc) */
u8 uses_dma; /* Does the host controller use DMA? */
u8 uses_pio_for_control; /*
* Does the host controller use PIO
* for control transfers?
*/
u8 otg_port; /* 0, or number of OTG/HNP port */
unsigned is_b_host:1; /* true during some HNP roleswitches */
unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */
unsigned no_stop_on_short:1; /*
* Quirk: some controllers don't stop
* the ep queue on a short transfer
* with the URB_SHORT_NOT_OK flag set.
*/
unsigned no_sg_constraint:1; /* no sg constraint */
unsigned sg_tablesize; /* 0 or largest number of sg list entries */
int devnum_next; /* Next open device number in
* round-robin allocation */
struct usb_devmap devmap; /* device address allocation map */
struct usb_device *root_hub; /* Root hub */
struct usb_bus *hs_companion; /* Companion EHCI bus, if any */
struct list_head bus_list; /* list of busses */
struct mutex usb_address0_mutex; /* unaddressed device mutex */
int bandwidth_allocated; /* on this bus: how much of the time
* reserved for periodic (intr/iso)
* requests is used, on average?
* Units: microseconds/frame.
* Limits: Full/low speed reserve 90%,
* while high speed reserves 80%.
*/
int bandwidth_int_reqs; /* number of Interrupt requests */
int bandwidth_isoc_reqs; /* number of Isoc. requests */
unsigned resuming_ports; /* bit array: resuming root-hub ports */
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
struct mon_bus *mon_bus; /* non-null when associated */
int monitored; /* non-zero when monitored */
#endif
unsigned skip_resume:1; /* All USB devices are brought into full
* power state after system resume. It
* is desirable for some buses to keep
* their devices in suspend state even
* after system resume. The devices
* are resumed later when a remote
* wakeup is detected or an interface
* driver starts I/O.
*/
};
为了实现HNP需要的挂起/恢复,新增如下通用接口:
int usb_suspend_device(struct usb_device *dev, u32 state);
int usb_resume_device(struct usb_device *dev);
(4)新增OTG功能切换和协议的描述结构体usb_otg
linux/usb/otg.h
struct usb_otg {
u8 default_a;
struct usb_phy *phy;
struct usb_bus *host;
struct usb_gadget *gadget;
/* bind/unbind the host controller */
int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
/* bind/unbind the peripheral controller */
int (*set_peripheral)(struct usb_otg *otg, truct usb_gadget *gadget);
/* effective for A-peripheral, ignored for B devices */
int (*set_vbus)(struct usb_otg *otg, bool enabled);
/* for B devices only: start session with A-Host */
int (*start_srp)(struct usb_otg *otg);
/* start or continue HNP role switch */
int (*start_hnp)(struct usb_otg *otg);
};
备注:
usb_otg的代码一般在USB的phy端实现。
16.6 总结
USB驱动分为USB主机驱动和USB设备驱动,如果系统的USB主机控制器符合OHCI(Open Host Controller Interface)等标准,那主机驱动的绝大部分工作都可以沿用通用的代码。
对于一个USB设备,USB设备至少具备两重身份:首先USB设备是“USB”的,其次USB设备是“自己”的。USB设备是“USB”的,指USB设备挂接在USB总线上,其必须完成usb_driver的初始化和注册;USB设备是“自己”的,意味着本身可能是一个字符设备、tty设备、网络设备等,在USB设备驱动中也必须实现符合相应框架的代码。
USB设备驱动的自身设备驱动部分的读写等操作流程有其特殊性,以URB(USB请求块)来贯穿始终,一个URB的生命周期通常包含创建、初始化、提交和被USB核心及USB主机传递、完成后回调函数被调用的过程,在URB被驱动提交后,也可以被取消。
在UDC(USB设备控制器)和Gadget Function(UDC驱动之上的)侧,UDC关心底层的硬件操作,而Function(File Storage)驱动则只是利用通用的API,并通过usb_request与底层UDC驱动交互。