linux usb usbip驅動詳解(三)

usbip協議很簡單,總共有4對命令:

OP_REQ_DEVLIST
OP_REP_DEVLIST

OP_REQ_IMPORT
OP_REP_IMPORT

USBIP_CMD_SUBMIT
USBIP_RET_SUBMIT

USBIP_CMD_UNLINK
USBIP_RET_UNLINK

分爲控制命令和數據傳輸命令兩大類。控制命令主要是用來list設備或者在attach前獲取導出設備的信息。

下面是列舉(list)已導出的usb設備(export)信息的時序圖(usbip list -r的流程)

                                                                              (圖一)

下面是傳輸usb數據時的命令時序圖(usbip attach的流程):

                                                                   (圖二)

在C/S模式中基本都是client發出請求,譬如當client在“usbip attach -r 192.168.100.191 -b 2-1.1”前,進行了“usbip list -r 192.168.100.191”,用於詢問host端能導出多少個設備以及設備信息,usbip-host支持最多導出8個usb設備(譬如我可以共享6個U盤和1個鼠標+1個鍵盤等)。

一) OP_REQ_DEVLIST命令;向server獲取設備信息:

(方向:vhci-hcd->usbip-host)

字段1:描述usbip版本,WORD

字段2:命令id,WORD,用於標識OP_REQ_DEVLIST這條命令

字段3:保留字,DWORD

 

二) OP_REP_DEVLIST命令;爲上一條命令的迴應。

(方向:usbip-host->vhci-hcd):

       看該回應的協議字段可以明顯知道,其實就是usb的“標準設備描述符”和“標準接口描述符”的一部分(沒有了端點數等),具體意義不用多說,搞usb的都能理解,詳情的話請參考《USB2.0官方協議規範》的第九章(CH9)。無論OP_REQ_DEVLIST還是OP_REP_DEVLIST的前8個字節(綠色框)意義類似,結構體在tool/usb/usbip/libsrc/usbip_common.h處定義,說明這對命令是應用程序傳輸的,不是在驅動傳輸,而且通過閱讀usbip工具代碼可以知道,tcp的創建和建立連接均在應用程序usbip工具上創建,至於驅動需要使用socket時,是採取將socket的描述符傳入內核,內核利用這個句柄,就能直接利用這個已打開的socket進行通信,無需再在內核建立連接之類的,linux內核會使用kernel_sendmsg和kernel_recvmsg發送和接收tcp數據。

//公共的
/* Common header for all the kinds of PDUs. */
struct op_common {
	uint16_t version;

#define OP_REQUEST	(0x80 << 8)
#define OP_REPLY	(0x00 << 8)
	uint16_t code;

	/* status codes defined in usbip_common.h */
	uint32_t status; /* op_code status (for reply) */

} __attribute__((packed));
//迴應特有的
#define SYSFS_PATH_MAX		256
#define SYSFS_BUS_ID_SIZE	32
struct op_devlist_reply {
	uint32_t ndev;
	/* followed by reply_extra[] */
} __attribute__((packed));

struct usbip_usb_device {
	char path[SYSFS_PATH_MAX];
	char busid[SYSFS_BUS_ID_SIZE];

	uint32_t busnum;
	uint32_t devnum;
	uint32_t speed;

	uint16_t idVendor;
	uint16_t idProduct;
	uint16_t bcdDevice;

	uint8_t bDeviceClass;
	uint8_t bDeviceSubClass;
	uint8_t bDeviceProtocol;
	uint8_t bConfigurationValue;
	uint8_t bNumConfigurations;
	uint8_t bNumInterfaces;
} __attribute__((packed));

struct usbip_usb_interface {
	uint8_t bInterfaceClass;
	uint8_t bInterfaceSubClass;
	uint8_t bInterfaceProtocol;
	uint8_t padding;	/* alignment */
} __attribute__((packed));

struct op_devinfo_reply {
	struct usbip_usb_device udev;
	struct usbip_usb_interface uinf[];
} __attribute__((packed));

      其中 0x08偏移處代表server端導出多少個usb設備,如果Number of exported devices爲0,就結束了,client不會再發後面的內容,如果導出多個設備,每個設備的信息都是下面的信息,即有多組struct op_devinfo_reply數據,同理,後面0x143偏移處也有一個bNumInterfaces,如果爲0,就沒有struct usbip_usb_interface的內容,否則每一組是struct usbip_usb_interface,多個就有多組struct usbip_usb_interface數據。

static int get_exported_devices(char *host, int sockfd)函數就利用socket獲取設備描述符信息和配置描述符信息的

該命令在運行“usbip list -r 192.168.100.191”時交互。

 

三) OP_REQ_IMPORT命令;請求attach一個遠程usb設備

(方向:vhci-hcd->usbip-host):

前8個字節不用多說,是公共頭,偏移 8處是一個描述busid總線號的字符串,有32個字節空間。


四) OP_REP_IMPORT,迴應請求attach一個遠程usb設備

(方向:usbip-host->vhci-hcd):

OP_REP_DEVLIST內容差不多,只是沒有了“導出usb設備數量”和“接口描述符”信息。只有“設備描述符”信息,看代碼一目瞭然:

struct op_import_reply {
	struct usbip_usb_device udev;
//	struct usbip_usb_interface uinf[];
} __attribute__((packed));

static int query_import_device(int sockfd, char *busid)函數就是通過socket獲取並解析OP_REP_IMPORT命令內容。

該命令在運行“usbip attach -r 192.168.100.191 -b 2-1.1”時交互。交互完後,即可進行usb URB消息的傳輸(圖二)。而後面兩組命令:

USBIP_CMD_SUBMIT
USBIP_RET_SUBMIT


USBIP_CMD_UNLINK
USBIP_RET_UNLINK
,專門用於usb通信用途,其中USBIP_CMD_UNLINKUSBIP_RET_UNLINK是在usb接口驅動(如U盤驅動、者hid鍵鼠驅動、usb-skeleton.c等)出現異常時,或者需要終止usb通信等時(接口驅動調用usb_kill_urb()),就會生成unlink命令,用於異常處理,以及內核回收urb對象。而USBIP_CMD_SUBMITUSBIP_RET_SUBMIT則是當usb接口驅動調用usb_submit_urb()時生成的命令,用於正常usb設備通信。

 

五) USBIP_CMD_SUBMIT命令,提交一個URB。

(方向:vhci-hcd->usbip-host) 

transfer_flags取值參考:

六) USBIP_RET_SUBMIT(方向:usbip-host->vhci-hcd),對提交的URB進行回覆,對應於linux URB對象註冊的“complete完成回調函數”返回。這得益於usb_submit_urb()時異步的,直接返回,不等待底層主機控制器完成usb數據的傳輸,直到“完成回調函數”的回調,即代表數據已經傳輸給usb設備了,得到了U盤等設備的狀態返回status,如果異常,會有相應的處理,等下一篇文章會詳細分析usbip驅動代碼。

urb的狀態:
usb_submit_urb的完成函數中通過URB結構體的status成員可以獲知其原因,如0表示傳輸成功,
-ENOENT表示被usb_kill_urb()殺死,
-ECONNRESET表示被usb_unlink_urb()殺死,
-EPROTO表示傳輸中發生了bitstuff錯誤或者硬件未能及時收到響應數據包,
-ENODEV表示USB設備已被移除,
-EXDEV表示等時傳輸僅完成了一部分等。

      下文有內核結構struct urb的描述,可關注該結構體的typedef void (*usb_complete_t)(struct urb *);完成回調函數指針和int status; 狀態值的英文註釋,幫助我們理解。

 

上面兩條命令的字段意義理解需要閱讀linux內核usb子系統的重量級對象URB:

/**
 * struct urb - USB Request Block
 * @urb_list: For use by current owner of the URB.
 * @anchor_list: membership in the list of an anchor
 * @anchor: to anchor URBs to a common mooring
 * @ep: Points to the endpoint's data structure.  Will eventually
 *	replace @pipe.
 * @pipe: Holds endpoint number, direction, type, and more.
 *	Create these values with the eight macros available;
 *	usb_{snd,rcv}TYPEpipe(dev,endpoint), where the TYPE is "ctrl"
 *	(control), "bulk", "int" (interrupt), or "iso" (isochronous).
 *	For example usb_sndbulkpipe() or usb_rcvintpipe().  Endpoint
 *	numbers range from zero to fifteen.  Note that "in" endpoint two
 *	is a different endpoint (and pipe) from "out" endpoint two.
 *	The current configuration controls the existence, type, and
 *	maximum packet size of any given endpoint.
 * @stream_id: the endpoint's stream ID for bulk streams
 * @dev: Identifies the USB device to perform the request.
 * @status: This is read in non-iso completion functions to get the
 *	status of the particular request.  ISO requests only use it
 *	to tell whether the URB was unlinked; detailed status for
 *	each frame is in the fields of the iso_frame-desc.
 * @transfer_flags: A variety of flags may be used to affect how URB
 *	submission, unlinking, or operation are handled.  Different
 *	kinds of URB can use different flags.
 * @transfer_buffer:  This identifies the buffer to (or from) which the I/O
 *	request will be performed unless URB_NO_TRANSFER_DMA_MAP is set
 *	(however, do not leave garbage in transfer_buffer even then).
 *	This buffer must be suitable for DMA; allocate it with
 *	kmalloc() or equivalent.  For transfers to "in" endpoints, contents
 *	of this buffer will be modified.  This buffer is used for the data
 *	stage of control transfers.
 * @transfer_dma: When transfer_flags includes URB_NO_TRANSFER_DMA_MAP,
 *	the device driver is saying that it provided this DMA address,
 *	which the host controller driver should use in preference to the
 *	transfer_buffer.
 * @sg: scatter gather buffer list, the buffer size of each element in
 * 	the list (except the last) must be divisible by the endpoint's
 * 	max packet size if no_sg_constraint isn't set in 'struct usb_bus'
 * @num_mapped_sgs: (internal) number of mapped sg entries
 * @num_sgs: number of entries in the sg list
 * @transfer_buffer_length: How big is transfer_buffer.  The transfer may
 *	be broken up into chunks according to the current maximum packet
 *	size for the endpoint, which is a function of the configuration
 *	and is encoded in the pipe.  When the length is zero, neither
 *	transfer_buffer nor transfer_dma is used.
 * @actual_length: This is read in non-iso completion functions, and
 *	it tells how many bytes (out of transfer_buffer_length) were
 *	transferred.  It will normally be the same as requested, unless
 *	either an error was reported or a short read was performed.
 *	The URB_SHORT_NOT_OK transfer flag may be used to make such
 *	short reads be reported as errors.
 * @setup_packet: Only used for control transfers, this points to eight bytes
 *	of setup data.  Control transfers always start by sending this data
 *	to the device.  Then transfer_buffer is read or written, if needed.
 * @setup_dma: DMA pointer for the setup packet.  The caller must not use
 *	this field; setup_packet must point to a valid buffer.
 * @start_frame: Returns the initial frame for isochronous transfers.
 * @number_of_packets: Lists the number of ISO transfer buffers.
 * @interval: Specifies the polling interval for interrupt or isochronous
 *	transfers.  The units are frames (milliseconds) for full and low
 *	speed devices, and microframes (1/8 millisecond) for highspeed
 *	and SuperSpeed devices.
 * @error_count: Returns the number of ISO transfers that reported errors.
 * @context: For use in completion functions.  This normally points to
 *	request-specific driver context.
 * @complete: Completion handler. This URB is passed as the parameter to the
 *	completion function.  The completion function may then do what
 *	it likes with the URB, including resubmitting or freeing it.
 * @iso_frame_desc: Used to provide arrays of ISO transfer buffers and to
 *	collect the transfer status for each buffer.
 *
 * This structure identifies USB transfer requests.  URBs must be allocated by
 * calling usb_alloc_urb() and freed with a call to usb_free_urb().
 * Initialization may be done using various usb_fill_*_urb() functions.  URBs
 * are submitted using usb_submit_urb(), and pending requests may be canceled
 * using usb_unlink_urb() or usb_kill_urb().
 *
 * Data Transfer Buffers:
 *
 * Normally drivers provide I/O buffers allocated with kmalloc() or otherwise
 * taken from the general page pool.  That is provided by transfer_buffer
 * (control requests also use setup_packet), and host controller drivers
 * perform a dma mapping (and unmapping) for each buffer transferred.  Those
 * mapping operations can be expensive on some platforms (perhaps using a dma
 * bounce buffer or talking to an IOMMU),
 * although they're cheap on commodity x86 and ppc hardware.
 *
 * Alternatively, drivers may pass the URB_NO_TRANSFER_DMA_MAP transfer flag,
 * which tells the host controller driver that no such mapping is needed for
 * the transfer_buffer since
 * the device driver is DMA-aware.  For example, a device driver might
 * allocate a DMA buffer with usb_alloc_coherent() or call usb_buffer_map().
 * When this transfer flag is provided, host controller drivers will
 * attempt to use the dma address found in the transfer_dma
 * field rather than determining a dma address themselves.
 *
 * Note that transfer_buffer must still be set if the controller
 * does not support DMA (as indicated by bus.uses_dma) and when talking
 * to root hub. If you have to trasfer between highmem zone and the device
 * on such controller, create a bounce buffer or bail out with an error.
 * If transfer_buffer cannot be set (is in highmem) and the controller is DMA
 * capable, assign NULL to it, so that usbmon knows not to use the value.
 * The setup_packet must always be set, so it cannot be located in highmem.
 *
 * Initialization:
 *
 * All URBs submitted must initialize the dev, pipe, transfer_flags (may be
 * zero), and complete fields.  All URBs must also initialize
 * transfer_buffer and transfer_buffer_length.  They may provide the
 * URB_SHORT_NOT_OK transfer flag, indicating that short reads are
 * to be treated as errors; that flag is invalid for write requests.
 *
 * Bulk URBs may
 * use the URB_ZERO_PACKET transfer flag, indicating that bulk OUT transfers
 * should always terminate with a short packet, even if it means adding an
 * extra zero length packet.
 *
 * Control URBs must provide a valid pointer in the setup_packet field.
 * Unlike the transfer_buffer, the setup_packet may not be mapped for DMA
 * beforehand.
 *
 * Interrupt URBs must provide an interval, saying how often (in milliseconds
 * or, for highspeed devices, 125 microsecond units)
 * to poll for transfers.  After the URB has been submitted, the interval
 * field reflects how the transfer was actually scheduled.
 * The polling interval may be more frequent than requested.
 * For example, some controllers have a maximum interval of 32 milliseconds,
 * while others support intervals of up to 1024 milliseconds.
 * Isochronous URBs also have transfer intervals.  (Note that for isochronous
 * endpoints, as well as high speed interrupt endpoints, the encoding of
 * the transfer interval in the endpoint descriptor is logarithmic.
 * Device drivers must convert that value to linear units themselves.)
 *
 * If an isochronous endpoint queue isn't already running, the host
 * controller will schedule a new URB to start as soon as bandwidth
 * utilization allows.  If the queue is running then a new URB will be
 * scheduled to start in the first transfer slot following the end of the
 * preceding URB, if that slot has not already expired.  If the slot has
 * expired (which can happen when IRQ delivery is delayed for a long time),
 * the scheduling behavior depends on the URB_ISO_ASAP flag.  If the flag
 * is clear then the URB will be scheduled to start in the expired slot,
 * implying that some of its packets will not be transferred; if the flag
 * is set then the URB will be scheduled in the first unexpired slot,
 * breaking the queue's synchronization.  Upon URB completion, the
 * start_frame field will be set to the (micro)frame number in which the
 * transfer was scheduled.  Ranges for frame counter values are HC-specific
 * and can go from as low as 256 to as high as 65536 frames.
 *
 * Isochronous URBs have a different data transfer model, in part because
 * the quality of service is only "best effort".  Callers provide specially
 * allocated URBs, with number_of_packets worth of iso_frame_desc structures
 * at the end.  Each such packet is an individual ISO transfer.  Isochronous
 * URBs are normally queued, submitted by drivers to arrange that
 * transfers are at least double buffered, and then explicitly resubmitted
 * in completion handlers, so
 * that data (such as audio or video) streams at as constant a rate as the
 * host controller scheduler can support.
 *
 * Completion Callbacks:
 *
 * The completion callback is made in_interrupt(), and one of the first
 * things that a completion handler should do is check the status field.
 * The status field is provided for all URBs.  It is used to report
 * unlinked URBs, and status for all non-ISO transfers.  It should not
 * be examined before the URB is returned to the completion handler.
 *
 * The context field is normally used to link URBs back to the relevant
 * driver or request state.
 *
 * When the completion callback is invoked for non-isochronous URBs, the
 * actual_length field tells how many bytes were transferred.  This field
 * is updated even when the URB terminated with an error or was unlinked.
 *
 * ISO transfer status is reported in the status and actual_length fields
 * of the iso_frame_desc array, and the number of errors is reported in
 * error_count.  Completion callbacks for ISO transfers will normally
 * (re)submit URBs to ensure a constant transfer rate.
 *
 * Note that even fields marked "public" should not be touched by the driver
 * when the urb is owned by the hcd, that is, since the call to
 * usb_submit_urb() till the entry into the completion routine.
 */
struct urb {
	/* private: usb core and host controller only fields in the urb */
	struct kref kref;		/* reference count of the URB */
	void *hcpriv;			/* private data for host controller */
	atomic_t use_count;		/* concurrent submissions counter */
	atomic_t reject;		/* submissions will fail */
	int unlinked;			/* unlink error code */

	/* public: documented fields in the urb that can be used by drivers */
	struct list_head urb_list;	/* list head for use by the urb's
					 * current owner */
	struct list_head anchor_list;	/* the URB may be anchored */
	struct usb_anchor *anchor;
	struct usb_device *dev;		/* (in) pointer to associated device */
	struct usb_host_endpoint *ep;	/* (internal) pointer to endpoint */
	unsigned int pipe;		/* (in) pipe information */
	unsigned int stream_id;		/* (in) stream ID */
	int status;			/* (return) non-ISO status */
	unsigned int transfer_flags;	/* (in) URB_SHORT_NOT_OK | ...*/
	void *transfer_buffer;		/* (in) associated data buffer */
	dma_addr_t transfer_dma;	/* (in) dma addr for transfer_buffer */
	struct scatterlist *sg;		/* (in) scatter gather buffer list */
	int num_mapped_sgs;		/* (internal) mapped sg entries */
	int num_sgs;			/* (in) number of entries in the sg list */
	u32 transfer_buffer_length;	/* (in) data buffer length */
	u32 actual_length;		/* (return) actual transfer length */
	unsigned char *setup_packet;	/* (in) setup packet (control only) */
	dma_addr_t setup_dma;		/* (in) dma addr for setup_packet */
	int start_frame;		/* (modify) start frame (ISO) */
	int number_of_packets;		/* (in) number of ISO packets */
	int interval;			/* (modify) transfer interval
					 * (INT/ISO) */
	int error_count;		/* (return) number of ISO errors */
	void *context;			/* (in) context for completion */
	usb_complete_t complete;	/* (in) completion routine */
	struct usb_iso_packet_descriptor iso_frame_desc[0];
					/* (in) ISO ONLY */
};

linux內核的大牛們對內核重要的結構體都會有一段詳細的註釋,urb也不例外,註釋比代碼長。

對於USBIP_CMD_SUBMIT命令重點關注這幾個(in)變量:

pipe
transfer_flags
transfer_buffer_length
setup_packet
transfer_buffer

以及complete完成函數的理解。

      能從pipe變量裏得到協議裏的方向direction和端點號ep,從transfer_flags得到命令裏的transfer_flags字段,從transfer_buffer_length得到發送數據長度,setup_packet是當pipe指定爲0號端口時(控制傳輸)使用,長度爲8字節,不是控制傳輸階段要填寫全0,從transfer_buffer得到URB data數據。命令的其他字段是關於ISO等時傳輸相關的。

而對於USBIP_RET_SUBMIT命令重點關注以下幾個變量:

pipe(in)
status(return)
actual_length(return)
setup_packet(in)
transfer_buffer(in)

      能從pipe變量裏得到協議裏的方向direction和端點號ep,status得到U盤設備處理後的狀態反饋,譬如error碼或success等,actual_length代表U盤返回的數據長度,transfer_buffer則是U盤返回的數據。setup_packet爲控制傳輸數據。

七八)USBIP_CMD_UNLINK和USBIP_RET_UNLINK命令

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章