【转】 Linux那些事儿之我是U盘(26)第一次亲密接触(二)

时间:2022-12-02 14:34:05

对于设备驱动程序而言,控制传输要做的事情很简单,usb core提交一个urb,这个urb中间包含了一个命令,或者说控制请求,因为命令更适合于我们后来要讲的某个重要的概念.这里我们要发送的就是GET MAX LUN.我们调用了一个函数, usb_stor_control_msg,其作用从名字上也可以看出,发送控制信息,即控制请求.做一件事情要遵守一件事情的规矩,发送控制信息必须按照一定的格式,发出去了人家才能看得懂.就像你要给远方的恋人寄一封信,你要在信封上按基本格式填写一些东西,你的信才能被寄出去对吗.

我们结合usb_stor_control_msg函数本身以及我们调用它的时候其具体上下文包括实际传递给它的参数来读这段代码,看看这个格式究竟是如何的.首先第一个参数us,这个不用多说了吧.星星还是那个星星,月亮也还是那个月亮,山也还是那座山,梁也还是那到梁,us自然总还是那个us.us中有一个成员,叫做cr,struct us_data的定义:

149         struct usb_ctrlrequest  *cr;             /* control requests     */

她是struct usb_ctrlrequest结构指针, usb规范规定了一个控制请求的格式为一个8个字节的数据包,struct usb_ctrlrequest正是专门为此而准备的8个字节的一个变量,所以控制传输中总会用到她.她的定义在include/linux/usb_ch9.h:

     86 /**
     87  * struct usb_ctrlrequest - SETUP data for a USB device control request
     88  * @bRequestType: matches the USB bmRequestType field
     89  * @bRequest: matches the USB bRequest field
     90  * @wValue: matches the USB wValue field (le16 byte order)
     91  * @wIndex: matches the USB wIndex field (le16 byte order)
     92  * @wLength: matches the USB wLength field (le16 byte order)
     93  *
     94  * This structure is used to send control requests to a USB device.  It matches
     95  * the different fields of the USB 2.0 Spec section 9.3, table 9-2.  See the
     96  * USB spec for a fuller description of the different fields, and what they are
     97  * used for.
     98  *
     99  * Note that the driver for any interface can issue control requests.
    100  * For most devices, interfaces don't coordinate with each other, so
    101  * such requests may be made at any time.
    102  */
    103 struct usb_ctrlrequest {
    104         __u8 bRequestType;
    105         __u8 bRequest;
    106         __le16 wValue;
    107         __le16 wIndex;
    108         __le16 wLength;
    109 } __attribute__ ((packed));

 不过需要说明一点,usb spec,不叫cr,而叫setup packet,struct urb里边就有这么一个名字一样的成员,

799         unsigned char *setup_packet;    /* (in) setup packet (control only) */

因为在Linuxusb的世界里一个通用的数据结构是urb,很多函数都是以urb为参数的,所以稍后我们实际上还是会让urb中的setup_packet指针指向这个usb_ctrlrequest结构体的.毕竟我们最终要提交的就是urb,而不是cr.并且cr只是出现在控制传输,urb却是四大传输都要用到的.

Ok,usb mass storage bulk only transport协议里面,规定的很清楚,要发送GET MAX LUN请求,必须把bmRequestType设置为device to host,class,interface,同时把bRequest设置为254(FEh),即咱们这里的0xfe,wValue设置为0,wIndex设置为接口的编号,最后wLength设置为1.

  需要解释一下,什么叫bmRequestType设置device to host,class,interface?实际上,usb 2.0规范里面指出,所有的usb设备都会响应主机的一些请求,这些请求必须来自主机,通过设备的默认控制管道发出.(0号端点所对应的那个管道)这些请求,或者说这些request,是通过控制传输来实现的,请求以及请求所需的参数都是通过setup packet来发送的.主机负责建立好这个setup packet.(也就是咱们刚才的那个cr,后来的setup_packet.)每个setup packet包含8bytes.她们的含义如下:

  byte0: bmRequestType,注意,在刚才咱们代码中数据结构struct ctrlrequest里边是写的bRequestType,但是她们对应的是相同的冬冬.而之所以usb协议里写成bmRequestType,是因为她实际上又是一个位图(m表示map),也就是说,尽管她只有1byte,但是她仍然是当作8位来用.她的8位的含义是:

        D7: 控制传输数据传输的方向,0表示方向为主机到设备,1表示方向为设备到主机.(有时控制传输除了发命令阶段以外,可能还有数据传输阶段,此处表示的是在数据传输那个阶段的传输方向.)

        D6...5: 请求的类型,0称为标准类型(Standard),1称为Class,2称为Vendor,3则是保留的,不能使用.

        D4...0: 接收者,0表示设备,1表示接口,2表示端点,3表示其它,4...31都是保留的,不能使用.

        所以,bmRequestType被设device to host,class,interface,表示,数据传输阶段传输方向是设备到主机,请求的类型是Class,意思是,请求类型可以有好几种,首先usb spec本身定义了很多种标准请求,所有的usb设备都必须支持这些标准请求,或者或标准request,另一方面,一类设备或者厂商自己也可以定义自己的额外的一些请求,显然这里get max lun就是usb mass storage这个类,或者说这个class所定义的请求,因此,请求类型设置为class,request接收者可以是设备,也可以是接口,还可以是端点,get max lun这个命令来说,她是针对一个u盘的,所以她的接收者应该是interface.

  byte1: bRequest,1byte,指定了是哪个请求.每一个请求都有一个编号,咱们这里是GET MAX LUN,其编号是FEh.

  byte2...3: wValue,2bytes,不同请求有不同的值,咱们这里刚才已经说了,必须为0.

  byte4...5: wIndex,2bytes,不同请求有不同的值,咱们这里要求被设置为是interface number.

  byte6...7: wLength,2bytes,如果接下来有数据传输阶段,则这个值表征了数据传输阶段传输多少个bytes,没啥好说的,这个值在GET MAX LUN请求中被规定为1,也就是说返回1byte即可.

  结合函数调用的实参和函数的定义,可见,对于这个cr来说,bRequest被赋值为US_BULK_GET_MAX_LUN,bRequestType被赋值为USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE,wValue被设为0,wIndex被设置为us->ifnum, 推开记忆的门,我们发现在associate_dev()函数中,us->ifnum被赋值为intf->cur_altsetting->desc.bInterfaceNumber,即她确实对应了interface number. wLength被赋值为1.设置好cr之后,就可以把她传递给urbsetup_packet.对比上面的usb_stor_control_msg()函数中第232,和下面函数usb_fill_control_urb()的定义,即可看出urbsetup_packet指针指向了这个cr.具体来看usb_fill_control_urb()函数,这个函数定义于include/linux/usb.h:

    812 /**
    813  * usb_fill_control_urb - initializes a control urb
    814  * @urb: pointer to the urb to initialize.
    815  * @dev: pointer to the struct usb_device for this urb.
    816  * @pipe: the endpoint pipe
    817  * @setup_packet: pointer to the setup_packet buffer
    818  * @transfer_buffer: pointer to the transfer buffer
    819  * @buffer_length: length of the transfer buffer
    820  * @complete: pointer to the usb_complete_t function
    821  * @context: what to set the urb context to.
    822  *
    823  * Initializes a control urb with the proper information needed to submit
    824  * it to a device.
    825  */
    826 static inline void usb_fill_control_urb (struct urb *urb,
    827                                          struct usb_device *dev,
    828                                          unsigned int pipe,
    829                                          unsigned char *setup_packet,
    830                                          void *transfer_buffer,
    831                                          int buffer_length,
    832                                          usb_complete_t complete,
    833                                          void *context)
    834 {
    835         spin_lock_init(&urb->lock);
    836         urb->dev = dev;
    837         urb->pipe = pipe;
    838         urb->setup_packet = setup_packet;
    839         urb->transfer_buffer = transfer_buffer;
    840         urb->transfer_buffer_length = buffer_length;
    841         urb->complete = complete;
    842         urb->context = context;
    843 }

  很显然,她就是为特定的pipe填充一个urb(最初urb申请的时候被初始化为0了嘛不是).对比函数调用和函数定义,可知, 这个pipe被设为了us->recv_ctrl_pipe,即接收控制管道,也就是说专门为设备向主机发送数据而设置的管道.而这个urb就是us->current_urb,并且除了urb->setup_packet(unsigned char类型的指针)指向了us->cr之外,urb->transfer_buffer(void指针)指向了us->iobuf,urb->transfer_buffer_length(int类型)被赋值为1,urb->complete(usb_complete_t类型)被赋值为usb_stor_blocking_completion.

  此处特意提一下usb_complete_t类型,include/linux/usb.h,有这么一行,

    612 typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
 
这里用了typedef来简化声明,不熟悉typedef功能的可以去查一下,typedef的强大使得以下两种声明作用相同:

  一种是:

  void (*func1)(struct urb *,struct pt_regs *);

  void (*func2)(struct urb *,struct pt_regs *);

  void (*func3)(struct urb *,struct pt_regs *); 

  另一种是:

  typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
  usb_complete_t func1;

  usb_complete_t func2;

  usb_complete_t func3;

  看出来了吧,如果要声明很多个函数指针,她们的参数又都很复杂,那么显然使用typedef一次,就可以一劳永逸了,以后声明就很简单了.所以,咱们也就知道实际上,urb中的complete是一个函数指针,她被设置为指向函数usb_stor_blocking_completion().关于usb_stor_blocking_completion()咱们暂且不提,等到用的时候再说.

  看完了usb_fill_control_urb,咱们再回到usb_stor_control_msg()函数,接下来是下一个函数,usb_stor_msg_common(),注意了,usb_fill_control_urb这个函数只填充了urb中的几个元素,struct urb里面包含了很多东西,不过有一些设置是共同的,所以下面用usb_stor_msg_common()函数来设置,可以看出给这个函数传递的参数只有两个,一个就是us,另一个是timeout(传给她的值是HZ),我们继续进入到这个函数中来把她看个清清楚楚明明白白真真切切.天空收容每一片云彩,不论其美丑,所以天空宽阔无边;大地拥抱每一寸土地,不论其贫富,所以大地广袤无垠;海洋接纳每一条河流,不论其大小,所以海洋广阔无边;而这个函数支持各种传输,作为usb-storage模块,无论其采用何种传输方式,无论传输数据量的多少,最终都一定要调用这个函数,所以我们必须承认这个函数很伟大.

鉴于这个函数的重要性,它适用于各种信息的传送,而不仅仅是控制传送,日后在bulk传输中我们还将遇上它,我们将在下一节专门来分析.但是在看这个函数之前,有些事情必须要心里有数,那就是作为设备驱动程序,只需要提交一个urb就可以了,剩下的事情usb core会去处理,有了结果它会通知我们.而提交urb,usb core为我们准备了一个函数,usb_submit_urb()不管我们使用什么传输方式,我们都只要调用这个函数即可,在此之前,我们需要做的只是准备好这么一个urb,urb中各相关的成员填充好,然后就ok.而这usb_stor_msg_common正是这样做的.而显然,不同的传输方式其填写urb的方式也不同.

最后我们需要指出一点,这里我们的cr是一个指针,有同志会问这个指针申请过内存了吗?答案是肯定的,忆往昔峥嵘岁月,曾几何时,我们在函数associate_dev()中就见到了us->cr,并且用usb_buffer_alloc为其申请了内存,当时我们就讲过的.