《Linux那些事儿之我是USB》我是U盘(21)冬天来了,春天还会远吗(五)--总结

(1)
看完了get_transport()继续看get_protocol()和get_pipes().
  usb_stor_probe1 ()
{
        /* Get standard transport and protocol settings */                        
    get_transport(us);                                                        
    get_protocol(us); 
}
static void  get_protocol (struct us_data *us)
{
    case USB_SC_SCSI:
        us->protocol_name = "Transparent SCSI";
        us->proto_handler = usb_stor_transparent_scsi_command;
        break;
//根据us->subclass来判断.对于U盘来说,spec里边规定了,subclass是US_SC_SCSI,所以这里就是两句赋值语句.一个是令us的protocol_name为"Transparent SCSI",另一个是令us的proto_handler为usb_stor_transparent_scsi_command.后者又是一个函数指针,我们日后遇到这个函数。
}

int  usb_stor_probe2 (struct us_data *us)
{
  result =  get_pipes (us);
}


static int  get_pipes (struct us_data *us)
{
    struct usb_endpoint_descriptor *ep;
    struct usb_endpoint_descriptor *ep_in = NULL;
    struct usb_endpoint_descriptor *ep_out = NULL;
    struct usb_endpoint_descriptor *ep_int = NULL;

struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting;
//us->pusb_intf,在associate_dev中赋得值,故事中最开始提到interface(指针).而它的成员cur_altsetting,就是当前的setting,或者说设置.在讲associate_dev的时候也已经遇到过,是一个struct usb_host_interface的结构体指针.现在这里用另一个指针临时代替一下, altsetting.接下来会用到它的成员,desc和endpoint.

 for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
          ep = &altsetting->endpoint[i].desc;
//altsetting->endpoint[i].desc,对照struct usb_host_endpoint这个结构体的定义,可知,desc正是一个struct usb_endpoint_descriptor的变量.刚刚定义了四个这种结构体的指针,ep,ep_in,ep_out,ep_int,就是用来记录端点描述符的,ep_in用于bulk-in,ep_out用于bulk-out,ep_int用于记录中断端点(如果有的话).而ep,只是一个临时指针.

if ( usb_endpoint_xfer_bulk (ep)) {
            if ( usb_endpoint_dir_in (ep)) {
                if (!ep_in)
                    ep_in = ep;
            } else {
                if (!ep_out)
                    ep_out = ep;
            }
        }
        else if ( usb_endpoint_is_int_in (ep)) {
            if (!ep_int)
                ep_int = ep;
        }
}

struct  usb_host_interface  {
    struct  usb_interface_descriptor  desc;
    int extralen;
    unsigned char *extra;   /* Extra descriptors */
    /* array of desc.bNumEndpoint endpoints associated with this
     * interface setting.  these will be in no particular order.
     */
    struct  usb_host_endpoint  *endpoint;
    char *string;       /* iInterface string, if present */
};
struct  usb_host_endpoint  {
    struct  usb_endpoint_descriptor       desc;                                 
    struct usb_ss_ep_comp_descriptor    ss_ep_comp;                           
    struct list_head        urb_list;
    void                *hcpriv;
    struct ep_device        *ep_dev;    /* For sysfs info */                  
    unsigned char *extra;   /* Extra descriptors */                           
    int extralen;
    int enabled;    
}; 
//struct usb_host_interface,可以看到,它这两个成员,struct usb_interface_descriptor desc,和struct usb_host_endpoint *endpoint.其中,desc是interface的接口描述符,而endpoint这个指针记录的是几个endpoint,它们以数组的形式被存储,而endpoint指向数组头.
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct  usb_endpoint_descriptor  {
    __u8  bLength;
    __u8  bDescriptorType;   
    __u8   bEndpointAddress ;                                                   
    __u8   bmAttributes ;
    __le16 wMaxPacketSize;  
    __u8  bInterval;    
    /* NOTE:  these two are _only_ in audio endpoints. */
    /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
    __u8  bRefresh;
    __u8  bSynchAddress;
} __attribute__ ((packed));
#define USB_DT_ENDPOINT_SIZE        7
#define USB_DT_ENDPOINT_AUDIO_SIZE  9   /* Audio extension */


Flash memory---Flash是用来给用户存储数据的,而U盘中的Flash就相当于PC机中的硬盘,存储数据主要就靠它.那么除了给用户存储数据以外,设备自己还需要存储一些设备本身固有的东西,比如设备姓甚名谁?谁生产的?还有一些信息,比如该设备有几种配置,有几个接口,等等许多特性.

除了Flash memory外,还有一个做EEPROM,也是用来存储的, 而Flash是基于EEPROM技术发展起来的一种低成本的ROM产品. EEPROM和Flash相同,都是需要电擦除,但EEPROM可以按字节擦除,而不向Flash那样一次擦除一个block.

通常,USB设备里边,会有一个Flash芯片,会有一个EEPROM芯片,Flash给客户存储数据,而EEPROM用来存储设备本身的信息.

usb spec规定的,这种格式就是一个个的描述符的格式.设备描述符,配置描述符,接口描述符,端点描述符,以及其它一些某一些类别的设备特有的描述符,比如hub描述符.这些东西都是很规范的,尤其对于这四种标准的描述符,每个usb设备都是规规矩矩的支持的,所以usb core层可以用一段相同的代码把它们都给读出来,而不用再让我们设备驱动程序去自己读了。


/* USB_DT_INTERFACE: Interface descriptor */
struct  usb_interface_descriptor  {
    __u8  bLength;
    __u8  bDescriptorType;
    __u8   bInterfaceNumber ;
    __u8   bAlternateSetting ;
    __u8   bNumEndpoints ;
    __u8  bInterfaceClass;
    __u8  bInterfaceSubClass;
    __u8  bInterfaceProtocol;
    __u8  iInterface;
} __attribute__ ((packed));
#define USB_DT_INTERFACE_SIZE       9
bNumEndpoints就是接口描述符中的成员,表示这个接口有多少个端点,不过这其中不包括0号端点,0号端点是任何一个usb设备都必须是提供的,这个端点专门用于进行控制传输,即它是一个控制端点.

通常usb mass storage会有两个bulk端点,用于bulk传输,即所谓的批量传输.我们日常的读写U盘里的文件,就是属于批量传输,对于mass storage设备来说,bulk传输是它的主要工作方式,道理很简单,我们使用U盘就是用来读写文件的,谁没事天天去读它的这种描述符那种描述符呢,和这些描述符打交道无非就是为了帮助我们最终实现读写文件的工作,这才是每一个usb存储设备真正的使命.

struct  usb_endpoint_descriptor  {                                                  
    __u8   bmAttributes ;
} __attribute__ ((packed));
struct usb_endpoint_descriptor,它的成员中bmAttributes表示属性,总共8位,其中bit1和bit0共同称为Transfer Type,
传输类型,即00表示控制,01表示等时,10表示批量,11表示中断.
USB_ENDPOINT_XFERTYPE_MASK这个宏定义于include/linux/usb_ch9.h中:
#define USB_ENDPOINT_XFERTYPE_MASK      0x03    /* in bmAttributes */

static inline int  usb_endpoint_xfer_bulk (const struct usb_endpoint_descriptor *epd)
//判断端点传输类型  判断这个端点描述符描述的是不是一个Bulk端点
{
    return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
        USB_ENDPOINT_XFER_BULK);
}  
struct  usb_endpoint_descriptor  {  
    __u8   bEndpointAddress ;                                                   
} __attribute__ ((packed));

static inline int  usb_endpoint_dir_in (const struct usb_endpoint_descriptor *epd)
{
    return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
}
如果是Bulk端点,继续比较,struct usb_endpoint_descriptor中的一个成员bEndpointAddress,也是8个bit,或者说1个byte,其中bit7表示的是这个端点的方向,0表示OUT,1表示IN,OUT与IN是对主机而言.OUT就是从主机到设备,IN就是从设备到主机.而宏USB_DIR_IN仍然来自include/linux/usb_ch9.h
#define USB_DIR_OUT         0       /* to device */
#define USB_DIR_IN          0x80        /* to host */
#define USB_ENDPOINT_DIR_MASK       0x80   bit7

usb_endpoint_is_int_in - check if the endpoint is interrupt IN          
static inline int  usb_endpoint_is_int_in ( const struct usb_endpoint_descriptor *epd)
{
    return usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd);
}           
//为了让ep_in和ep_out指向该指的endpoint descriptor.
如果这个端点是中断端点,那么就让ep_int指向它.每一类usb其上面有多少端点有何种端点都是不确定的,都得遵守该类设备的规范,而usb mass storage的规范说了,一个usb mass storage设备至少应该有两个bulk端点,除此之外,那个控制端点显然是必须的,另外,可能会有一个中断端点,这种设备支持CBI协议,Control/Bulk/Interrupt协议.我们也说过了,U盘遵守的是Bulk-only协议,它不需要有中断端点.

总结:
下一节我们将开始最精彩的部分,它就是伟大的usb_stor_acquire_resources(),我们小结一下,此前我们花了很大的篇幅来为 usb_stor_acquire_resources() 做铺垫,那我们来回顾一下,究竟做了哪些事情?
首先我们从storage_probe出发,一共调用了五个函数,它们是 assocaite_dev,get_device_info,get_transport,get_protocol,get_pipes. 我们这样做的目的是什么?很简单,就是为了建立一个数据结构,它就是传说中的 struct us_data ,它的名字叫做us.我们把她建立了起来,为她申请了内存,为她的各个元素赋了值,目的就是为了让以后我们可以很好的利用她.这五个函数都不难,你一定也会写.难的是如何去定义struct us_data,别忘了这个数据结构是写代码的同志们专门为usb-storage模块而设计的.

所谓编程,无非就是数据结构加上算法.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux技术芯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值