《Linux那些事儿之我是USB》我是U盘(18)冬天来了,春天还会远吗?(四)

来源:岁月联盟 编辑:exp 时间:2011-10-23

 

结束了get_device_info,我们继续沿着storage_probe一步一步地走下去。继续,这就是我们前面提到过的三个函数,get_transport、get_protocol和get_pipes。一旦结束了这三个函数,我们就将进入本故事的高潮部分。而在这之前,我们只能一个一个地来看。好在这几个函数虽然不短,但是真正有用的信息只有一点,所以可以很快看完。

 

993   /* Get the transport, protocol,and pipe settings */

 

994   result = get_transport(us);

 

995    if(result)

 

996         goto BadDevice;

 

997    result =get_protocol(us);

 

998    if(result)

 

999         goto BadDevice;

 

1000   result = get_pipes(us);

 

1001    if(result)

 

1002       goto BadDevice;

 

第1个,get_transport(us)。

 

557 static int get_transport(struct us_data *us)

 

558 {

 

559    switch(us->protocol) {

 

560    caseUS_PR_CB:

 

561        us->transport_name = "Control/Bulk";

 

562         us->transport = usb_stor_CB_transport;

 

563         us->transport_reset = usb_stor_CB_reset;

 

564         us->max_lun = 7;

 

565         break;

 

566

 

567  case US_PR_CBI:

 

568          us->transport_name ="Control/Bulk/Interrupt";

 

569         us->transport = usb_stor_CBI_transport;

 

570         us->transport_reset = usb_stor_CB_reset;

 

571          us->max_lun = 7;

 

572        break;

 

573

 

574   case US_PR_BULK:

 

575        us->transport_name = "Bulk";

 

576        us->transport = usb_stor_Bulk_transport;

 

577         us->transport_reset = usb_stor_Bulk_reset;

 

578          break;

 

579

 

580 #ifdef CONFIG_USB_STORAGE_USBAT

 

581    caseUS_PR_USBAT:

 

582        us->transport_name = "Shuttle USBAT";

 

583         us->transport = usbat_transport;

 

584         us->transport_reset = usb_stor_CB_reset;

 

585         us->max_lun = 1;

 

586         break;

 

587 #endif

 

588

 

589 #ifdef CONFIG_USB_STORAGE_SDDR09

 

590    caseUS_PR_EUSB_SDDR09:

 

591         us->transport_name ="EUSB/SDDR09";

 

592         us->transport = sddr09_transport;

 

593         us->transport_reset = usb_stor_CB_reset;

 

594         us->max_lun = 0;

 

595         break;

 

596 #endif

 

597

 

598 #ifdef CONFIG_USB_STORAGE_SDDR55

 

599   case US_PR_SDDR55:

 

600         us->transport_name = "SDDR55";

 

601        us->transport = sddr55_transport;

 

602         us->transport_reset = sddr55_reset;

 

603         us->max_lun = 0;

 

604         break;

 

605 #endif

 

606

 

607 #ifdef CONFIG_USB_STORAGE_DPCM

 

608    caseUS_PR_DPCM_USB:

 

609         us->transport_name ="Control/Bulk-EUSB/SDDR09";

 

610        us->transport = dpcm_transport;

 

611         us->transport_reset= usb_stor_CB_reset;

 

612         us->max_lun = 1;

 

613         break;

 

614 #endif

 

615

 

616 #ifdef CONFIG_USB_STORAGE_FREECOM

 

617   case US_PR_FREECOM:

 

618         us->transport_name = "Freecom";

 

619         us->transport = freecom_transport;

 

620          us->transport_reset =usb_stor_freecom_reset;

 

621        us->max_lun = 0;

 

622        break;

 

623 #endif

 

624

 

625 #ifdef CONFIG_USB_STORAGE_DATAFAB

 

626   case US_PR_DATAFAB:

 

627       us->transport_name  ="Datafab Bulk-Only";

 

628         us->transport = datafab_transport;

 

629         us->transport_reset = usb_stor_Bulk_reset;

 

630        us->max_lun = 1;

 

631        break;

 

632 #endif

 

633

 

634 #ifdef CONFIG_USB_STORAGE_JUMPSHOT

 

635   case US_PR_JUMPSHOT:

 

636        us->transport_name  ="Lexar Jumpshot Control/Bulk";

 

637         us->transport = jumpshot_transport;

 

638       us->transport_reset = usb_stor_Bulk_reset;

 

639        us->max_lun = 1;

 

640         break;

 

641 #endif

 

642

 

643 #ifdef CONFIG_USB_STORAGE_ALAUDA

 

644    case US_PR_ALAUDA:

 

645         us->transport_name  = "Alauda Control/Bulk";

 

646         us->transport = alauda_transport;

 

647         us->transport_reset = usb_stor_Bulk_reset;

 

648         us->max_lun = 1;

 

649        break;

 

650 #endif

 

651

 

652 #ifdef CONFIG_USB_STORAGE_KARMA

 

653    caseUS_PR_KARMA:

 

654        us->transport_name = "Rio Karma/Bulk";

 

655         us->transport = rio_karma_transport;

 

656         us->transport_reset = usb_stor_Bulk_reset;

 

657         break;

 

658 #endif

 

659

 

660   default:

 

661           return -EIO;

 

662   }

 

663     US_DEBUGP("Transport:%s/n", us->transport_name);

 

664

 

665  /* fix for single-lun devices */

 

666    if(us->flags & US_FL_SINGLE_LUN)

 

667        us->max_lun = 0;

 

668    return 0;

 

669 }

 

乍一看,这么长一段,不过明眼人一看就知道了,主要就是一个switch,选择语句,语法上来说很简单,所以我们看懂这段代码不难。只是,我想说的是,虽然这里做出一个选择不难,但是不同选择就意味着后来整个故事会有千差万别的结局,当鸟儿选择在两翼上系上黄金,就意味着它放弃展翅高飞;选择云天搏击,就意味着放弃身外的负累。

 

所以,此处,我们需要仔细地看清楚我们究竟选择了怎样一条路。很显然,前面我们已经说过,对于U盘,spec规定了,它就属于Bulk-only的传输方式,即它的us->protocol就是US_PR_BULK。这是我们刚刚在get_device_info中确定下来的。于是,在整个switch段落中,我们所执行的只是US_PR_BULK这一段,即us的transport_name被赋值为“Bulk”,transport被赋值为usb_stor_Bulk_transport,transport_reset被赋值为usb_stor_Bulk_reset。其中我们最需要记住的是,us的成员transport和transport_reset是两个函数指针。程序员们把这个称作“钩子”。这两个赋值我们需要牢记,日后我们一定会用到它们,因为这正是我们真正的数据传输时调用的东西。关于usb_stor_Bulk_*的这两个函数,到时候调用了再来看。现在只需知道,日后我们一定会回过来看这个赋值的。

 

580行到658行,不用多说了,这里全是与各种特定产品相关的一些编译开关,它们有一些自己定义一些传输函数,有些则共用通用的函数。

 

666行,判断us->flags,还记得我们在讲unusual_devs.h文件时说的flags吧,这里第一次用上了。有些设备设置了US_FL_SINGLE_LUN这个flag,就表明它是只有一个LUN的。像这样的设备挺多的,随便从unusual_devs.h中抓一个出来:

 

596 UNUSUAL_DEV(  0x054c, 0x002d, 0x0100, 0x0100,

 

597                "Sony",

 

598                "Memorystick MSAC-US1",

 

599                US_SC_DEVICE, US_PR_DEVICE, NULL,

 

600                US_FL_SINGLE_LUN ),

 

比如这个Sony的Memorystick。中文名叫“记忆棒”,大小就与口香糖一样,也是一种存储芯片。它是Sony公司推出的,广泛用于Sony的各种数码产品中,比如数码照相机、数码摄影机。

 

LUN就是Logical Unit Number。通常在谈到SCSI设备时不可避免要说起LUN。关于LUN,曾几何时,一位来自Novell的参与开发Linux内核中USB子系统的工程师这样对我说,一个LUN就是设备中的一个驱动。下面举例来说一下USB中引入LUN的目的。有些读卡器可以有多个插槽,比如有两个,其中一个支持CF卡,另一个支持SD卡,那么这种情况要区分这两个插槽里的设备,就得引入LUN有这个概念,即逻辑单元。很显然,像U盘这样简单的设备其LUN必然是一个。有时候,人们常把U盘中一个分区当做一个LUN,但是不应该这么理解。

 

知道了LUN以后,自然就可以知道US_FL_SINGLE_LUN是做什么了,这个flag的意义很明显,直截了当地告诉你,这个设备只有一个LUN,它不支持多个LUN。而max_lun又是什么意思?us中的成员max_lun等于一个设备所支持的最大LUN号。即如果一个设备支持四个LUNs,那么这四个LUN的编号就是0,1,2,3,而max_lun就是3。如果一个设备不用支持多个LUN,那么它的max_lun就是0。所以这里max_lun就是设为0。

 

另外一个需要注意的地方是,比较一下各个case语句会发现US_PR_BULK和其他的case不一样,其他的case下面都设置了us->max_lun,而对应于Bulk-Only协议的这个case,它没有设置us->max_lun,之所以不设,是因为这个值由设备说了算,必须向设备查询,这是Bulk-Only协议规定的。在drivers/usb/storage/transport.c中定义了一个usb_stor_Bulk_max_lun()函数,它将负责获取这个max lun。而我依然要声明一次,这个函数对我们U盘没有什么意义,这个值肯定是0,所以这个函数咱们就不去理睬了。

 

至此,get_transport()也结束了,和get_device_info()一样。我们目前所看到的这些函数都不得不面对现实,对它们来说,凋谢是最终的结果,盛开只是一个过程。而对我们来说,要到达终点,那么和这些函数狭路相逢,终不能幸免。然而,不管这部分代码有多么重要,也不过是我们整个长途旅程中,来去匆匆的转机站,无论停留多久,始终要离去坐另一班机。