USB驱动分析3

USB驱动分析3
USB驱动分析3

USB驱动分析(三)

来源: ChinaUnix博客日期:2008.06.05 21:26(共有0条评论) 我要评论

需要注意的是,这些调试信息得是我们打开了编译选项CONFIG_USB_STORAGE_DEBUG 才有意义的,这里也看出来了,如果这个选项为0,那么这几个宏就什么也不干,因为它们被赋为空了.关于US_DEBUG系列的这几个宏,就讲这么多,之后再碰上,将直接过滤掉,不予多说. 关于prink和kmalloc,这两个函数也没有太多需要说的,对大多数人来讲,就把printk当成printf,把kmalloc当成malloc即可,只不过是这两个函数是专门用于内核代码中的.一个是打印一些东西,一个是申请内存空间.

931行呢?id_index干嘛用的?让我们在下节不见不散吧.Be there or be square!-孙楠如是说. storage_probe这个函数挺有意思的,总长度不足100行,但是干了许多事情,这就像足球场上的后腰,比如切尔西的马克莱莱,在场上并不起眼,但是却为整个团队做出了卓越的贡献.也有很多评论家说银河战舰皇家马德里这几年的衰落正是从赶走这个不起眼的马克莱莱开始的.

在讲id_index之前,我们继续贴storage_probe的代码:

943 init_MUTEX(&(us->dev_semaphore));

944 init_MUTEX_LOCKED(&(us->sema));

945 init_completion(&(us->notify));

946 init_waitqueue_head(&us->dev_reset_wait);

947 init_waitqueue_head(&us->scsi_scan_wait);

948 init_completion(&us->scsi_scan_done);

949

950 /* Associate the us_data structure with the USB device */

951 result = associate_dev(us, intf);

952 if (result)

953 goto BadDevice;

954

955 /*

956 * Get the unusual_devs entries and the descriptors

957 *

958 * id_index is calculated in the declaration to be the index number

959 * of the match from the usb_device_id table, so we can find the

960 * corresponding entry in the private table.

961 */

962 get_device_info(us, id_index);

storage_probe这个函数之所以短小,是因为它调用了大量的函数.所以,看起来短短一段代码,实际上却要花费我们读代码的人好几个小时,想想真是觉得不划算,难怪朱自清先生看到这个storage_probe函数的时候,不禁感慨,燕子去了,有再来的时候,杨柳枯了,有再青的时候,桃花谢了,有再开的时候,但是,聪明的你告诉我,我们读这段不足100行的函数花掉的时间为何一去不复返呢?

其实我们不知道id_index也不影响对后面问题的理解,甚至某种意义上来说,花太多笔墨去讲这个id_index有一点喧宾夺主的感觉,但是,有时候,有些事情,你明知它是无奈的事,无奈的心情,却还要无奈的面对,无奈的去选择,有时想无奈的逃避都不可能,因为我们都被无奈禁锢了.比如这里,注意到962行出现了一个get_device_info的函数,它的一个参数就是id_index,所以,我们别无选择,只能看看这个id_index究竟是干嘛的.

上节我们注意到id_index=id-storage_usb_ids,id我们知道,storage_probe函数的两个形参之一,

而storage_usb_ids,不是别人,正是我们曾经赋给usb_storage_driver的成员id_table的值.忘记了id_table的可以回去看.它实际上就是一张表格,告诉全世界我这个driver支持怎样的一些设备.storage_usb_ids同样来自drivers/usb/storage/usb.c中,

111 /* The entries in this table, except for final ones here

112 * (USB_MASS_STORAGE_CLASS and the empty entry), correspond,

113 * line for line with the entries of us_unsuaul_dev_list[].

114 */

115

116 #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \

117 vendorName, productName,useProtocol, useTransport, \

118 initFunction, flags) \

119 { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax) }

120

121 static struct usb_device_id storage_usb_ids [] = {

122

123 # include "unusual_devs.h"

124 #undef UNUSUAL_DEV

125 /* Control/Bulk transport for all SubClass values */

126 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CB) },

127 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CB) },

128 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CB) },

129 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CB) },

130 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CB) },

131 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CB) },

132

133 /* Control/Bulk/Interrupt transport for all SubClass values */

134 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CBI) },

135 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CBI) },

136 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CBI) },

137 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CBI) },

138 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CBI) },

139 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CBI) },

140

141 /* Bulk-only transport for all SubClass values */

142 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_BULK) },

143 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_BULK) },

144 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_BULK) },

145 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_BULK) },

146 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_BULK) },

147 #if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE) 148 { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) },

149 #endif

150

151 /* Terminating entry */

152 { }

153 };

注意到这是一个struct usb_device_id结构体的数组,所以即使我们用下半身思考也能知道,其中每一项必然是一个struct usb_device_id的结构体变量.我们先来看USB_INTERFACE_INFO这个咚咚,很显然这是一个宏,来自include/linux/usb.h,

473 /**

474 * USB_INTERFACE_INFO - macro used to describe a class of usb interfaces

475 * @cl: bInterfaceClass value

476 * @sc: bInterfaceSubClass value

477 * @pr: bInterfaceProtocol value

478 *

479 * This macro is used to create a struct usb_device_id that matches a

480 * specific class of interfaces.

481 */

482 #define USB_INTERFACE_INFO(cl,sc,pr) \

483 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), .bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)

每一个USB_INTERFACE_INFO就是构造一个struct usb_device_id的结构体变量,回顾一下我们之前给出的struct usb_device_id的定义,这里实际上就是为其中的四个元素赋了值,它们是match_flags,bInterfaceClass,bInterfaceSubClass,bInterfaceProtocol.这里不得不说的是,这个世界上有许许多多的usb设备,它们各有各的特点,为了区分它们,usb规范,或者说usb协议,把usb设备分成了很多类,然而每个类又分成子类,这很好理解,我们一个大学也是如此,先是分成很多个学院,比如我们复旦大学,信息学院,经济学院,管理学院,外文学院,等等.然后每个学院又被分为很多个系,比如信息学院,下面分了电子工程系,微电子系,计算机系,通信工程系,然后可能每个系下边又分了各个专业,usb协议也是这样干的,首先每个Interface属于一个Class,(为什么是把Interface分类,而不把Device分类?前面讲过了,在usb设备驱动中,不用再

提Device,因为每个设备驱动对应的是一种Interface,而不是一种Device),然后Class下面又分了SubClass,完了SubClass下面又按各种设备所遵循的不同的通信协议继续细分.usb协议里边为每一种Class,每一种SubClass,每一种Protocol定义一个数值,比如mass storage的Class 就是0x08,而这里USB_CLASS_MASS_STORAGE这个宏在include/linux/usb_ch9.h中定义,其值正是8.

我们拿第126行来举例,

{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CB) },

把这个宏展开,就是说定义了这么一个usb_device_id结构体变量,其match_flags=USB_DEVICE_ID_MATCH_INT_INFO,而bInterfaceClass=USB_CLASS_MASS_STORAGE,bInterfaceSubClass=US_SC_RBC,而bInterfaceProtocol=US_PR_CB.

USB_CLASS_MASS_STORAGE就不用再说了,咱们这个驱动程序所支持的每一种设备都是属于这个类,或者说这个Class.但是这个Class里边包含不同的SubClass,比如subclass 02为CD-ROM设备,04为软盘驱动器,06为通用SCSI类设备.而通信协议则主要有CBI协议和Bulk-Only协议.

像US_SC_RBC这些关于sub class的宏的定义是在文件drivers/usb/storage/protocol.h中:

47 /* Sub Classes */

48

49 #define US_SC_RBC 0x01 /* Typically, flash devices */

50 #define US_SC_8020 0x02 /* CD-ROM */

51 #define US_SC_QIC 0x03 /* QIC-157 Tapes */

52 #define US_SC_UFI 0x04 /* Floppy */

53 #define US_SC_8070 0x05 /* Removable media */

54 #define US_SC_SCSI 0x06 /* Transparent */

55 #define US_SC_ISD200 0x07 /* ISD200 ATA */

而像US_PR_CB这些关于传输协议的宏则在另一个文件中,drivers/usb/storage/transport.h

/* Protocols */

#define US_PR_CBI 0x00 /* Control/Bulk/Interrupt */

#define US_PR_CB 0x01 /* Control/Bulk w/o interrupt */

#define US_PR_BULK 0x50 /* bulk only */

这个文件中还定义了更多的协议,不过我们暂时只需要知道这三种,因为其她协议都是专门针对一些特殊设备的,在storage_usb_ids数组中使用宏USB_INTERFACE_INFO定义的usb_device_id都只是用的这三种协议.(US_PR_CBI和US_PR_CB这两种协议在usb协议中都唤作CBI,不过之间有点差别.)而对于一些特殊的设备,则还在unusual_devs.h文件中有专门的一些变量定义,我们暂且不去关注它们.

说了这许多,U盘属于其中的哪一种呢?usb协议中规定,U盘的Subclass是属于US_SC_SCSI 的.而其通信协议使用的是Bulk-Only的.显然这些东西我们后来都会用得上.

那么这里还有一个match_flag,它又是表示什么意思?USB_INTERFACE_INFO这个宏貌似把所有的设备的match_flag都给设成了USB_DEVICE_ID_MATCH_INT_INFO,这是为啥?这个宏来自include/linux/usb.h,

435 #define USB_DEVICE_ID_MATCH_INT_INFO \

436 (USB_DEVICE_ID_MA TCH_INT_CLASS | USB_DEVICE_ID_MA TCH_INT_SUBCLASS | USB_DEVICE_ID_MA TCH_INT_PROTOCOL)

match_flag这个咚咚是给usb core去用的,usb core负责给设备寻找适合她的driver,负责给driver寻找适合他的device,它所比较的就是struct usb_device_id的变量,而struct usb_device_id结构体中有许多成员,那么是不是一定要把每一个成员都给比较一下呢,其实没有必要那么细,差不多就行了,比如咱们这里,就是告诉usb core,你只要比较bInterfaceClass,bInterfaceSubClass,bInterfaceProtocol即可.include/linux/mod_devicetable.h中针对struct usb_device_id中的每一个要比较的项定义了一个宏:

121 /* Some useful macros to use to create struct usb_device_id */

122 #define USB_DEVICE_ID_MATCH_VENDOR 0x0001

123 #define USB_DEVICE_ID_MATCH_PRODUCT 0x0002

124 #define USB_DEVICE_ID_MATCH_DEV_LO 0x0004

125 #define USB_DEVICE_ID_MATCH_DEV_HI 0x0008

126 #define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010

127 #define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020

128 #define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040

129 #define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080

130 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100

131 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200

回去对比一下struct usb_device_id就知道这些宏是什么意思了.

然后我们再看storage_usb_ids中那个#include "unusual_devs.h",实际上这个文件也是在我们这个drivers/usb/storage/目录下面,它里边定义了一些特殊的设备,也是以struct usb_device_id 结构体变量的形式,这些设备或者是有一些别的设备不具备的特性,或者是他们遵循的通信协议有些与众不同,比如,它既不是Bulk-Only也不是CBI,像这些不按常理出牌的设备,写代码的同志们把他们单独给列了出来.整理在这个文件中.当然,从大的类来分,它们依然是属于usb mass storage这个类别的,否则也没必要放在这个目录下面了.

至此,我们可以知道storage_usb_ids这个表是怎么回事了.usb core为每个设备在这张表里查找,如果找到了某一行和这个设备相匹配,那么该行就是我们前面提到的那个storage_probe()的参数id.所以id_index=id-storage_usb_ids就是如其字面意义那样,在表中的编号.至于这个编号有什么用,那我们骑驴看唱本.总之,费这么大劲干了这么一件事,总是有它的意义的.

最后,总结陈词,这个所谓的花名册,就好比我们大学生申请国外学校,每个人都会事先搜集一大批学校名单,然后结合各方面,比如师资力量,是否牛的导师够多,比如经济实力,是否能给够多的奖学金,比如教授本人资历,是否获得过重大奖项,取得过何种成绩,比如学校声望, 是否是名校,综合这些来看最终确定一份名单,就是自己真正心仪的学校花名册.那么那个match_flags是什么意思呢,而有的同学嫌这样太麻烦,又或者他们可能只是需要一个能发学历的,像杨澜老公吴征所获得博士学位的那个巴林顿大学那样卖假文凭的学校也许就能满足他们了,那么他们就不必去评估那么多项,反正他们心中的match_flags就是能够和吴征成为校友就可以了,管它是否因为该学校被取缔以后他们也会像吴征那样痛失母校呢.

罗马不是一天建成的.在让U盘工作之前,其实我们的驱动作了很多准备工作.

我们继续跟着感觉走,storage_probe(),943行至948行,一系列的以init_*命名的函数在此刻被调用,这里涉及了一些锁机制,等待机制,不过只是初始化,暂且不理睬,到后面用到的时候再细说,不过请记住,这几行每一行都是有用的.后面自然会用得着.

此时,我们先往下走,951行associate_dev()和962行get_device_info(),这两个函数是我们目前需要看的,一个一个来.

先看associate_dev(),定义于drivers/usb/storage/usb.c,

429 /* Associate our private data with the USB device */

430 static int associate_dev(struct us_data *us, struct usb_interface *intf)

431 {

432 US_DEBUGP("-- %s\n", __FUNCTION__);

433

434 /* Fill in the device-related fields */

435 us->pusb_dev = interface_to_usbdev(intf);

436 us->pusb_intf = intf;

437 us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;

438 US_DEBUGP("V endor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n",

439 us->pusb_dev->descriptor.idVendor,

440 us->pusb_dev->descriptor.idProduct,

441 us->pusb_dev->descriptor.bcdDevice);

442 US_DEBUGP("Interface Subclass: 0x%02x, Protocol: 0x%02x\n",

443 intf->cur_altsetting->desc.bInterfaceSubClass,

444 intf->cur_altsetting->desc.bInterfaceProtocol);

445

446 /* Store our private data in the interface */

447 usb_set_intfdata(intf, us);

448

449 /* Allocate the device-related DMA-mapped buffers */

450 us->cr = usb_buffer_alloc(us->pusb_dev, sizeof(*us->cr),

451 GFP_KERNEL, &us->cr_dma);

452 if (!us->cr) {

453 US_DEBUGP("usb_ctrlrequest allocation failed\n");

454 return -ENOMEM;

455 }

456

457 us->iobuf = usb_buffer_alloc(us->pusb_dev, US_IOBUF_SIZE,

458 GFP_KERNEL, &us->iobuf_dma);

459 if (!us->iobuf) {

460 US_DEBUGP("I/O buffer allocation failed\n");

461 return -ENOMEM;

462 }

463 return 0;

464 }

我们首先来关注函数associate_dev的参数, struct us_data *us,传递给它的是us,这个不用多说了吧,此前刚刚为它申请了内存,并且初始化各成员为0. 这个us将一直陪伴我们走下去,直到我们的故事结束.所以其重要性不言而喻. struct usb_interface *intf,这个也不用说,storage_probe()函数传进来的两个参数之一.总之,此处郑重申明一次,struct us_data的结构体指针us,struct usb_interface结构体的指针intf,以及struct usb_device结构体和struct usb_device_id结构体在整个U盘驱动的故事中是唯一的,每次提到都是那个. 而以后我们会遇上的几个重要的数据结构,struct urb urb,struct scsi_cmnd srb这也非常重要,但是它们并不唯一,也许每次遇上都不一样,就像演戏一样.前边这几个数据结构的变量就像那些主角,而之后遇见的urb啊,srb啊,虽然频繁露面,但是只是群众演员,只不过这次是路人甲,下次是路人乙.

所以,以后我们将只说us,不再说struct us_data *us,struct usb_interface * intf也将只用intf来代替.

us之所以重要,是因为接下来很多函数都要用到它以及它的各个成员.实际上目前这个函数,associate_dev所做的事情就是为us的各成员赋值,毕竟此刻us和我们之前提到的那些struct usb_device啊,struct usb_interface啊,还没有一点关系.因而,这个函数,以及这之后的好几个函数都是为了给us的各成员赋上适当的值,之所以如此兴师动众去为它赋值,主要就是因为后面要利用它.所谓天下没有免费的午餐.

432行,本来无须多讲,因为只是一个debug语句,不过提一下__FUNCTION__这个"宏",gcc 2.95以后的版本支持这么一个冬冬,这个"宏"在编译的时候会被转换为函数名(字符串),这里自然就是"associate_dev"这么一个字符串,于是函数执行到这里就会打印一句话告诉世人我们执行到这个函数来了,这种做法显然会有利于咱们调试程序.不过这个冬冬实际上不是宏,因为预处理器对她一无所知.她的心只有编译器才懂.

435行,pusb_dev,就是point of usb device的意思.struct us_data中的一个成员,按照我们刚才约定的规矩,此刻我将说us的一个成员,us->pusb_dev= interface_to_usbdev(intf), interface_to_usbdev我们前面已经讲过,其含义正如字面表示的那样,把一个struct interface结构体的指针转换成一个struct usb_device的结构体指针.前面我们说过,struct usb_device对我们没有什么用,但是usb core层的一些函数要求使用这个参数,所以我们不得已而为止,正所谓人在江湖身不由己.

436行,把intf赋给us的pusb_intf.

437行,us的ifnum, 先看intf的cur_altsetting,这个容易令外行混淆.usb设备有一个configuration的概念,这个我们前面讲协议的时候说了,而这里又有一个setting,咋一看有些奇怪,这两个词不是一回事吗.这时候,就体现出外语水平了,上过新东方没上过新东方,背没背过俞敏洪的GRE红宝书,在这时候就体现出差距了.还是拿我们最熟悉的手机来打比方,configuration不说了,setting,一个手机可能各种配置都确定了,是振动还是铃声已经确定了,各种功能都确定了,但是声音的大小还可以变吧,通常手机的音量是一格一格的变动,大概也就5,6格,那么这个可以算一个setting吧.这里cur_altsetting就是表示的当前的这个setting,或者说设置.cur_altsetting是一个struct usb_host_interface的指针,这个结构体定义于include/linux/usb.h:

51 /* host-side wrapper for one interface setting's parsed descriptors */

52 struct usb_host_interface {

53 struct usb_interface_descriptor desc;

54

55 /* array of desc.bNumEndpoint endpoints associated with this

56 * interface setting. these will be in no particular order.

57 */

58 struct usb_host_endpoint *endpoint;

59

60 unsigned char *extra; /* Extra descriptors */

61 int extralen;

62 };

它的成员desc是一个struct usb_interface_descriptor结构体变量,这个结构体的定义是和usb 协议直接对应的,定义于include/linux/usb_ch9.h.(这里取名为"ch9"是因为这个文件很多东西对应于usb spec 2.0中的第九章,chapter 9.):

242 /* USB_DT_INTERFACE: Interface descriptor */

243 struct usb_interface_descriptor {

244 __u8 bLength;

245 __u8 bDescriptorType;

246

247 __u8 bInterfaceNumber;

248 __u8 bAlternateSetting;

249 __u8 bNumEndpoints;

250 __u8 bInterfaceClass;

251 __u8 bInterfaceSubClass;

252 __u8 bInterfaceProtocol;

253 __u8 iInterface;

254 } __attribute__ ((packed));

而其中我们这里提到的是bInterfaceNumber,一个设备可以有多个Interface,于是每一个Interface当然就得用个编号了,要不然咋区分啊?所有这些描述符里的冬冬都是出厂的时候就固化在设备里边的,而我们这里之所以可以用bInterfaceNumber来赋值,是因为usbcore在为设备初始化的时候就已经做足了这些功课.否则的话,我们真是寸步难行.

总之,us->ifnum就是这样,最终就是等于咱们眼下这个interface的编号.

438到444行就是两句调试语句,打印更多一些描述符信息,包括device描述符和interface描述符.

447行, usb_set_intfdata(),这其实是一个内联函数,她就一行代码,也是定义于include/linux/usb.h中:

138 static inline void usb_set_intfdata (struct usb_interface *intf, void *data)

139 {

140 dev_set_drvdata(&intf->dev, data);

141 }

有趣的是,dev_set_drvdata这个函数也是内联函数,也只有一行代码,她定义于include/linux/device.h中:

302 static inline void

303 dev_set_drvdata (struct device *dev, void *data)

304 {

305 dev->driver_data = data;

306 }

所以,结合来看,最终做的事情就是让&intf->dev->driver_data=data,即&intf->dev->driver_data=us.

再往下走,就是申请内存了,us->cr和us->iobuf都是指针,这里让它们指向两段内存空间,下面会用得到.需要注意的是usb_buffer_alloc(),这个函数是usbcore提供的,我们只管调用即可.从名字上就能知道它是用来申请内存的,第一个参数就是struct usb_device结构体的指针,所以我们要传递一个pusb_dev,第三个参数,GFP_KERNEL,是一个内存申请的flag,通常内存申请都用这个flag,除非是中断上下文,不能睡眠,那就得用GPF_ATOMIC,这里没那么多要求.第二个参数申请的buffer的大小,对于cr,传递的是sizeof(*us->cr),而对于iobuf,传递的是US_IOBUF_SIZE,这是一个宏,大小是64,是我们自己定义的,来自drivers/usb/storage/usb.h:

91 /*

92 * We provide a DMA-mapped I/O buffer for use with small USB transfers.

93 * It turns out that CB[I] needs a 12-byte buffer and Bulk-only needs a

94 * 31-byte buffer. But Freecom needs a 64-byte buffer, so that's the

95 * size we'll allocate.

96 */

97

98 #define US_IOBUF_SIZE 64 /* Size of the DMA-mapped I/O buffer */

而usb_buffer_alloc()的第四个参数有些意思了,第一次我们传递的是&us->cr_dma,第二次传递的是&us->iobuf_dma,这涉及到dma传输.这两个咚咚此前我们都没有赋过值,相反它们是在这个函数调用之后被赋上值的.cr_dma和iobuf_dma都是dma_addr_t类型的变量,这个数据类型是Linux内核中专门为dma传输而准备的.为了支持dma传输,usb_buffer_alloc不仅仅是申请了地址,并且建立了dma映射,cr_dma和iobuf_dma就是记录着cr和iobuf的dma地址.关于什么是cr,关于这些dma地址究竟有什么用,我们稍候就会遇到,那时候再讲也不迟.现在需要知道的就是usb_buffer_alloc申请的空间分别返回给了cr和iobuf.顺便提一下,用usb_buffer_alloc申请的内存空间需要用它的搭档usb_buffer_free()来释放.

452行和459行,每一次申请完内存就要检查成功与否,这是惯例.驱动程序能否驱动设备,关键就是看能否申请到内存空间,任何一处内存空间申请失败,整个驱动程序就没法正常工作.这就像如今找对象,谈婚姻,总是要看有没有房子.没有房子的话,那么基本上爱情也就没戏.然而现实中要拥有房子比计算机里分配内存却要难上许多,许多.可怜的我们这一代人,当我们不能挣钱的时候,房子是分配的,当我们能挣钱的时候,却发现房子已经买不起了.哭…

整个usb-storage模块的代码中,其最灵魂的部分在一个叫做usb_stor_control_thread()的函数中,而那也自然是我们整个故事的高潮.这个函数的调用有些特殊,我们是从usb_stor_acquire_resources()函数进入的,而后者我们即将遇到,它在整部戏中只出现过一次,即storage_probe中,行号为998的地方.然而在此之前,有四个函数挡在我们面前,它们就是get_device_info,get_transport,get_protocol,get_pipes.如我前面所说,两个人要走到一起,首先要了解彼此,这四个函数就是让driver去认识device的.这一点我们从名字上也能看出来.driver 需要知道device姓甚名谁,所以有了get_device_info,driver需要知道device喜欢用什么方式沟通,是用QQ还是用msn还是只用手机短信,如果是某一种,那么账号是多少,或者手机号是多少,写代码的人把这些工作分配给了get_transport,get_protocol,get_pipes.

实际上,这四个函数,加上之前刚说过的那个associate_dev(),是整个故事中最平淡最枯燥的部分,第一次读这部分代码总让人困惑,怎么没看见一点usb数据通信啊?完全没有看到usb host 和usb device是如何在交流的,这是usb吗?这一刻,这颗浮躁的心,在这个城市,迷失了.但是,我们知道,爱情,并非都与风花雪月有关,友情,并非总与疯斗打闹有关.这几个函数应该说是给后面做铺垫,红花总要有绿叶配,没有这段代码的铺垫,到了后面usb设备恐怕也无法正常工作吧.不过,一个利好消息是,这几个函数我们只会遇见这一次,它们在整个故事中就这么一次露脸的机会,像我们每个人的青春,只有一次,无法回头.和我们每个人的青春一样,都是绝版的.所以,让我们享受这段平淡无奇的代码吧.

get_device_info, 这个函数定义于drivers/usb/storage/usb.c中:

466 /* Get the unusual_devs entries and the string descriptors */

467 static void get_device_info(struct us_data *us, int id_index)

468 {

469 struct usb_device *dev = us->pusb_dev;

470 struct usb_interface_descriptor *idesc =

471 &us->pusb_intf->cur_altsetting->desc;

472 struct us_unusual_dev *unusual_dev = &us_unusual_dev_list[id_index];

473 struct usb_device_id *id = &storage_usb_ids[id_index];

474

475 /* Store the entries */

476 us->unusual_dev = unusual_dev;

477 us->subclass = (unusual_dev->useProtocol == US_SC_DEVICE) ?

478 idesc->bInterfaceSubClass :

479 unusual_dev->useProtocol;

480 us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ?

481 idesc->bInterfaceProtocol :

482 unusual_dev->useTransport;

483 us->flags = unusual_dev->flags;

484

485 /* Log a message if a non-generic unusual_dev entry contains an

486 * unnecessary subclass or protocol override. This may stimulate

487 * reports from users that will help us remove unneeded entries

488 * from the unusual_devs.h table.

489 */

490 if (id->idVendor || id->idProduct) {

491 static char *msgs[3] = {

492 "an unneeded SubClass entry",

493 "an unneeded Protocol entry",

494 "unneeded SubClass and Protocol entries"};

495 struct usb_device_descriptor *ddesc = &dev->descriptor;

496 int msg = -1;

497

498 if (unusual_dev->useProtocol != US_SC_DEVICE &&

499 us->subclass == idesc->bInterfaceSubClass)

500 msg += 1;

501 if (unusual_dev->useTransport != US_PR_DEVICE &&

502 us->protocol == idesc->bInterfaceProtocol)

503 msg += 2;

504 if (msg >= 0 && !(unusual_dev->flags & US_FL_NEED_OVERRIDE)) 505 printk(KERN_NOTICE USB_STORAGE "This device "

506 "(%04x,%04x,%04x S %02x P %02x)"

507 " has %s in unusual_devs.h\n"

508 " Please send a copy of this message to "

509 "\n]linux-usb-devel@https://www.360docs.net/doc/913061816.html,>\n[/email]

",

510 ddesc->idVendor, ddesc->idProduct,

511 ddesc->bcdDevice,

512 idesc->bInterfaceSubClass,

513 idesc->bInterfaceProtocol,

514 msgs[msg]);

515 }

516

517 /* Read the device's string descriptors */

518 if (dev->descriptor.iManufacturer)

519 usb_string(dev, dev->descriptor.iManufacturer,

520 us->vendor, sizeof(us->vendor));

521 if (dev->descriptor.iProduct)

522 usb_string(dev, dev->descriptor.iProduct,

523 us->product, sizeof(us->product));

524 if (dev->descriptor.iSerialNumber)

525 usb_string(dev, dev->descriptor.iSerialNumber,

526 us->serial, sizeof(us->serial));

527

528 /* Use the unusual_dev strings if the device didn't provide them */

529 if (strlen(us->vendor) == 0) {

530 if (unusual_dev->vendorName)

531 strlcpy(us->vendor, unusual_dev->vendorName,

532 sizeof(us->vendor));

533 else

534 strcpy(us->vendor, "Unknown");

535 }

536 if (strlen(us->product) == 0) {

537 if (unusual_dev->productName)

538 strlcpy(us->product, unusual_dev->productName,

539 sizeof(us->product));

540 else

541 strcpy(us->product, "Unknown");

542 }

543 if (strlen(us->serial) == 0)

544 strcpy(us->serial, "None");

545

546 US_DEBUGP("V endor: %s, Product: %s\n", us->vendor, us->product);

547 }

469行,不多说,dev还是那个dev.

470行,struct usb_interface_descriptor *idesc,这个也无须再说,之前那个associate_dev函数里已经介绍过这个结构体,而且整个故事就是针对一个interface的,一个interface就对应一个interface描述符.所以不管在哪里看到,她总还是她.

472行, struct us_unusual_dev,这个结构体是第一次出现,她定义于drivers/usb/storage/usb.h中,

55 /*

56 * Unusual device list definitions

57 */

58

59 struct us_unusual_dev {

60 const char* vendorName;

61 const char* productName;

62 __u8 useProtocol;

63 __u8 useTransport;

64 int (*initFunction)(struct us_data *);

65 unsigned int flags;

66 };

而等号右边的us_unusal_dev_list是一个数组,定义于drivers/usb/storage/usb.c: 157 /* This is the list of devices we recognize, along with their flag data */

158

159 /* The vendor name should be kept at eight characters or less, and

160 * the product name should be kept at 16 characters or less. If a device

161 * has the US_FL_FIX_INQUIRY flag, then the vendor and product names

162 * normally generated by a device thorugh the INQUIRY response will be

163 * taken from this list, and this is the reason for the above size

164 * restriction. However, if the flag is not present, then you

165 * are free to use as many characters as you like.

166 */

167

168 #undef UNUSUAL_DEV

169 #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ 170 vendor_name, product_name, use_protocol, use_transport, \ 171 init_function, Flags) \

172 { \

173 .vendorName = vendor_name, \

174 .productName = product_name, \

175 .useProtocol = use_protocol, \

176 .useTransport = use_transport, \

177 .initFunction = init_function, \

178 .flags = Flags, \

179 }

180

181 static struct us_unusual_dev us_unusual_dev_list[] = {

182 # include "unusual_devs.h"

183 # undef UNUSUAL_DEV

184 /* Control/Bulk transport for all SubClass values */

185 { .useProtocol = US_SC_RBC,

186 .useTransport = US_PR_CB},

187 { .useProtocol = US_SC_8020,

188 .useTransport = US_PR_CB},

189 { .useProtocol = US_SC_QIC,

190 .useTransport = US_PR_CB},

191 { .useProtocol = US_SC_UFI,

192 .useTransport = US_PR_CB},

193 { .useProtocol = US_SC_8070,

194 .useTransport = US_PR_CB},

195 { .useProtocol = US_SC_SCSI,

196 .useTransport = US_PR_CB},

197

198 /* Control/Bulk/Interrupt transport for all SubClass values */

199 { .useProtocol = US_SC_RBC,

200 .useTransport = US_PR_CBI},

201 { .useProtocol = US_SC_8020,

202 .useTransport = US_PR_CBI},

203 { .useProtocol = US_SC_QIC,

204 .useTransport = US_PR_CBI},

205 { .useProtocol = US_SC_UFI,

206 .useTransport = US_PR_CBI},

207 { .useProtocol = US_SC_8070,

208 .useTransport = US_PR_CBI},

209 { .useProtocol = US_SC_SCSI,

210 .useTransport = US_PR_CBI},

211

212 /* Bulk-only transport for all SubClass values */

213 { .useProtocol = US_SC_RBC,

214 .useTransport = US_PR_BULK},

215 { .useProtocol = US_SC_8020,

216 .useTransport = US_PR_BULK},

217 { .useProtocol = US_SC_QIC,

218 .useTransport = US_PR_BULK},

219 { .useProtocol = US_SC_UFI,

220 .useTransport = US_PR_BULK},

221 { .useProtocol = US_SC_8070,

222 .useTransport = US_PR_BULK},

223 #if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE) 224 { .useProtocol = US_SC_SCSI,

225 .useTransport = US_PR_BULK},

226 #endif

227

228 /* Terminating entry */

229 { NULL }

230 };

无可奈何花落去,似曾相识燕归来.这个数组看上去特别面熟对不对?可曾记得当初我们见过的那个storage_usb_ids,仔细对比一下会发现,这两个数组的元素个数完全一样,只不过,一个由是struct usb_device_id结构体构成的,另一个则是struct us_unusual_dev结构体构成的,其实这两个表格是一一对应的,它们也都定义于同一个文件中. 细心一点会注意到,UNUSUAL_DEV这个宏在这个文件里被定义了两次,这是干嘛用的?听仔细了,我们曾经提过unusual_devs.h这个文件,这个文件的作用是什么?假设没有这个文件,那么storage_usb_ids这张表就是用USB_INTERFACE_INFO这个宏定义了几种常规的usb mass storage的设备,比如,

{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CB) },

这一行就是定义一个设备的bInterfaceClass,bInterfaceSubClass,bInterfaceProtocol,也就是说一个usb interface只要其这三个项特点与这张表里定义的任何一行匹配,那么就可以把这个设备和这个驱动绑定,但是这三个条件毕竟只是规定了一些主流的或者说常规的设备的特点,这个世界上usb mass storage设备有许多种,林子大了,什么鸟都有.如果你的设备不符合这三个条件但是你仍然要用这个驱动怎么办?或者说你的设备不一般,在正式工作之前需要进行一些初始化的操作,你想自己提供一个初始化函数,那怎么办?伟大的Linux内核开发者们为你准备了一个文件,它就是让诸多usb mass storage设备厂家欢欣鼓舞的unusual_devs.h,有了它,厂家们不用再为自己的设备不被Linux内核支持而烦恼了.虽然我们的U盘基本上不属于这些另类设备,但作为一个有责任心的社会主义新青年,我们有必要为这个文件说两句.

我们打开unusual_devs.h吧,随便看一下,发现里边就是很多一个个UNUSUAL_DEV宏,每一行就是这么一个宏,毫无疑问它对应一种设备,我们从其中挑一个来看,比如挑一个三星的吧,过去在Intel的时候,前辈们会说,若不是当初我们对自己太自信了,这个世界上又怎么有三星的生存空间.说的是上世纪末,Intel决定提高flash产品的价格,而Nokia这个大客户不干了,它想找别人,一个叫三星的小公司鬼魅般的出现了,没有人相信这样一个小公司能够满足Nokia,可是,韩国人,韩国人的韧劲不仅仅是体现在足球场上.于是,世界上有了三星,Nokia养活了三星,而Intel,在flash这一领域失去了一个重要的客户,副CEO也为此引咎辞职了.而下面这个设备,正是来自三星的一个flash产品.

711 /* Submitted by Hartmut Wahl */]hwahl@hwahl.de>*/[/email]

712 UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001,

713 "Samsung",

714 "Digimax 410",

715 US_SC_DEVICE, US_PR_DEVICE, NULL,

716 US_FL_FIX_INQUIRY),

Digimax 410,熟悉数码相机的哥们儿大概对三星的这款410万像素3倍光学变焦的产品不会陌生,不过数码相机更新得很快,这款2002年推出的相机如今当然也算是很一般了,市场上卖的话也就1500以下,不过当时刚推出的时候也是3000到4000元的.ok,我们来看这一行是什么意思.

UNUSUAL_DEV这个宏被定义过两次,当然,#define了之后再下一次#define之前有一个#undef,否则就重复定义了.在storage_usb_ids之前,它的定义是

116 #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \

117 vendorName, productName,useProtocol, useTransport, \

118 initFunction, flags) \

119 { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax) }

USB_DEVICE_VER的定义在include/linux/usb.h中,

448 /**

449 * USB_DEVICE_VER - macro used to describe a specific usb device with a version range 450 * @vend: the 16 bit USB Vendor ID

451 * @prod: the 16 bit USB Product ID

452 * @lo: the bcdDevice_lo value

453 * @hi: the bcdDevice_hi value

454 *

455 * This macro is used to create a struct usb_device_id that matches a

456 * specific device, with a version range.

457 */

458 #define USB_DEVICE_VER(vend,prod,lo,hi) \

459 .match_flags = USB_DEVICE_ID_MA TCH_DEVICE_AND_VERSION, .idVendor = (vend), .idProduct = (prod), .bcdDevice_lo = (lo), .bcdDevice_hi = (hi)

所以这行最终出现在storage_usb_ids中的意思就是令match_flags为USB_DEVICE_ID_MA TCH_DEVICE_AND_VERSION,idVendor为0x0839,idProduct为0x000a,bcdDevice_lo为0x0001,bcdDevice_hi为0x0001.

而在us_unusal_dev_list这张表之前,UNUSUAL_DEV又被定义为:

168 #undef UNUSUAL_DEV

169 #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \

170 vendor_name, product_name, use_protocol, use_transport, \

171 init_function, Flags) \

172 { \

173 .vendorName = vendor_name, \

174 .productName = product_name, \

175 .useProtocol = use_protocol, \

176 .useTransport = use_transport, \

177 .initFunction = init_function, \

178 .flags = Flags, \

179 }

Ok.这样这个宏的意思又是令vendorName为"Samsung",令productName为"Digimax 410",而useProtocol为US_SC_DEVICE, useTransport为US_PR_DEVICE,initFunction为NULL,flag 为US_FL_FIX_INQUIRY.

看明白了吗?首先不去管各项的具体含义,至少我们看出来,针对同一个文件,我们使用两次定义UNUSUAL_DEV这个宏的方法,两次利用了它的不同元素,换言之,UNUSUAL_DEV这个宏本来可以设定10个参数,而storage_usb_ids中需要使用其中的前4个参数,同时us_unusual_dev_list中需要使用其中的后6个参数,所以在unusual_devs.h中定义的一行起了两个作用.我们注意到不管是storage_usb_ids数组还是us_unusual_dev_list,其中都通过这么一行把unusual_devs.h文件给包含了进来.storage_usb_ids中:

121 static struct usb_device_id storage_usb_ids [] = {

122

123 # include "unusual_devs.h"

124 #undef UNUSUAL_DEV

us_unusual_dev_list中:

181 static struct us_unusual_dev us_unusual_dev_list[] = {

182 # include "unusual_devs.h"

183 # undef UNUSUAL_DEV

而我们之所以使用两个数组的原因是,storage_usb_ids是提供给usb core的,它需要比较driver 和device从而确定设备是被这个driver所支持的,我们只需要比较四项就可以了,因为这四项已经足以确定一个设备了,厂商,产品,序列号.比较这些就够了,而us_unusual_dev_list这个数组中的元素是我们接下来的代码要用的,比如它用什么协议,它有什么初始化函数,所以我们使用了两个数组.而我们需要注意的是,这两个数组中元素的顺序是一样的,所以我们从storage_usb_ids中得到的id_index用于us_unusual_dev_list也是可以的,表示的还是同一个设备.而这也就是我们刚才在get_device_info中看到的.

472 struct us_unusual_dev *unusual_dev = &us_unusual_dev_list[id_index];

473 struct usb_device_id *id = &storage_usb_ids[id_index];

这样,unusual_dev和id就各取所需了.下面我们将会用到这两个指针.暂且不表.

总结陈词,最后具体解释一下这行为三星这款数码相机写的语句,

1. 关于match_flags,它的值是USB_DEVICE_ID_MA TCH_DEVICE_AND_VERSION,这是一个宏,它就告诉usb core,要比较这样几个方面,idVendor,idProduct,bcdDevice_lo,bcdDevice_hi,其中idVendor和下面的vendorName是对应的,而idProduct和下面的productName是对应的,业内为每家公司编一个号,这样便于管理,比如三星的编号就是0x0839,那么三星的产品中就会在其设备描述符中idVendor的烙上0x0839.而三星自己的每种产品也会有个编号,和Digimax 410对应的编号就是0x000a,而bcdDevice_lo和bcdDevice_hi共同组成一个具体设备的编号(device release number),bcd就意味着这个编号是二进制的格式.

2. vendorName和productName不用再说了, "Samsung"和"Digimax 410".

3. useProtocol为US_SC_DEVICE, useTransport为US_PR_DEVICE,这种情况就说明对于这种设备,它属于什么subclass,它使用什么通信协议,得从设备描述符里边去读取,它都写在那里边了.一会我们会看到我们的代码中会如何判断这个的.

4. initFunction等于NULL,这个很有意义的,这个函数就是设备的初始化函数,一般的设备都不需要这个函数,但是有些设备它偏要标新立异,它就告诉你,要用我的设备你必须先做一些初始化,于是它提供了一个函数,initFunction当然是一个函数指针,这里如果不为NULL的话,到时候就会被调用,以后我们会看到代码中对这个指针进行了判断.如果为空不理睬,否则就会执行.比如我们看下面两处,惠普的两个设备,它就提供了一个叫做init_8200e的初始化函数,

63 UNUSUAL_DEV( 0x03f0, 0x0207, 0x0001, 0x0001,

64 "HP",

65 "CD-Writer+ 8200e",

66 US_SC_8070, US_PR_SCM_A TAPI, init_8200e, 0),

67

68 UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001,

69 "HP",

70 "CD-Writer+ CD-4e",

71 US_SC_8070, US_PR_SCM_A TAPI, init_8200e, 0),

5. flag等于US_FL_FIX_INQUIRY,这个flag可以设为很多值,这个flag的存在本身就表示这个设备有些与众不同,因为一般的设备是不用这个flag的,有这个flag就表明这个设备可能在某些地方需要进行特殊的处理,所以今后在代码中我们会看到突然跳出一句,判断us->flag等于某个咚咚不,如果等于,就执行一些代码,如果不等于,那就不做任何事情.这个flag 的存在也使得我们可以方便处理一些设备的bug,比如正常的设备你问它吃了吗?它就回答吃了.可是不正常的设备可能就会根本不回答,或者回答北京房价真贵!于是对于这种设备,可能我们就需要一些专门的代码来对付.具体到这个US_FL_FIX_INQUIRY,这个flag这么一设置,就表明这个设备在接受到INQUIRY命令的时候会有一些异常的特征,所以以后我们会在代码里看到我们是如何处理它的.设置了这个flag的当然不只是三星的这款相机,别的设备也有可能设置的.

6. 既然明白了unusual_devs.h的作用,那么很显然的一个事情,如果一个厂家推出了一个新的设备,它有一些新的特征,而目前的设备驱动不足以完全支持它,那么厂家首先需要做的事情就是在unusual_devs.h中添加一个UNUSUAL_DEV来定义自己的设备,然后再看是否需要给内核打补丁以及如何打.因此这几年unusual_devs.h这个文件的长度也是慢慢在

增长。

从两张表得到了我们需要的冬冬,然后下面的代码就是围绕着这两个指针来展开了.(unusual_dev和id)

476行,把unusual_dev给记录在us里边,反正us里边也有这么一个成员.这样记录下来日后要用起来就方便了,因为us是贯穿整个故事的,所以访问他的成员很方便,随时都可以,但是us_unusual_dev_list以及storage_usb_ids这两张表这次之后就不会再用了.因为我们已经得到了我们想要的,所以我们就不用再去骚扰这两个数组了.

477至483行,给us的另外三个成员赋值,subclass,protocol,flags.比如我们的U盘,它属于主流设备,在us_unusual_dev_list列表中能找到它,其subclass是US_SC_SCSI,而protocol是Bulk-only,即这里用宏US_PR_BULK所代表的.(224行和225行.)关于US_SC_DEVICE和US_PR_DEVICE我们之前讲那个三星的数码相机的时候已经看到了,它就表示subclass和protocol得从设备的描述符里边读出来.这样做看起来很滑稽,因为三星完全可以把subclass 和protocol在UNUSUAL_DEV中写清楚,何必让我们再去读设备描述符呢.然而,我们可以想象,它这样的好处是定义一个UNUSUAL_DEV可以代表几种设备,即它可以让几个不同subclass的设备共用这么一个宏,或者几个不同protocol的设备共用这么一个宏.能省一点就省一点吧,这里体现了开源社区人们勤俭节约的高尚品德. 需要特别指出的是us->flags,对于U 盘来说,它当然没有什么flags需要设定,但是unusual_devs.h中的很多设备都设置了各种flags,稍后在代码中我们会看到,时不时我们就得判断一下是否某个flag设置了,通常是如果设置了,就要多执行某段代码,以满足某种要求.

490至515行,这是一段纯粹的调试代码,对我们理解usb没有任何意义的.这段代码检查unusual_devs.h,看是否这个文件定义了一行没有意义的句子.什么叫没有意义?我们刚才看见了,如果这个设备设了US_SC_DEVICE,那么其subclass将从描述符中读出来,如果不然,则让subclass=unusual_dev->useProtocol,但是如果后者又真的和描述符里读出来的一样,那么这个设备就没有必要把自己的useProtocol定义在unusual_devs.h中了,因为反正也可以从描述符里读出来.还不如和大众一样设为US_SC_DEVICE得了.就比如我们来看下面这行代表一个Sony的Memory Stick产品的代码:

371 UNUSUAL_DEV( 0x054c, 0x0069, 0x0000, 0x9999,

372 "Sony",

373 "Memorystick MSC-U03",

374 US_SC_UFI, US_PR_CB, NULL,

375 US_FL_SINGLE_LUN ),

我们看到其useProtocol这一栏里写了US_SC_UFI,这表明它自称是属于UFI这个subclass的,但是如果我们从它的描述符里边读出来也是这个,那就没有必要注明在这里了,这里直接写成US_SC_DEVICE好了.当然,总的来说这段代码有些傻.写代码的是希望能够更好的管理unusual_devs.h,希望它不要不断的增加,他总希望能够从这个文件里删除一些行,并且即使不能删除一行,也希望每一行都看上去整齐一点,让这个文件看上去更加小巧玲珑,更加精致.而不是无休的增加,不息的扩充.于是我们也只能对其良苦用心多一份理解吧.

518至526行,这里调用了一个来自usb core的函数,usb_string,这个函数的作用是获得一个设备的字符串描述符.咦?怎么跳出来一个叫做字符串描述符的冬冬?之前不是只讲了四种描述符吗?没错,设备描述符,配置描述符,接口描述符,端点描述符,这是每个设备都有的,而还有些描述符是可有可无的,字符串描述符就是这么一种情况,有的设备有,有的设备没有.又比如,hub,它就有一个hub描述符,这当然是一般的设备没有的.那么字符串描述符是干嘛的呢?有些东西模糊一些也未偿不是一件好事,看得太透彻了才知道很残酷.如果你一定要知道的话,

举个例子,我的机器里很多usb设备,有一个和lspci类似的命令,可以查看一下,这个命令就是lsusb.你也可以试一下,安装一个软件包usbutils,然后就可以使用这个命令.我们看: localhost:~/test # lsusb

Bus 004 Device 003: ID 0ea0:1001 Ours Technology, Inc.

Bus 004 Device 002: ID 04b4:6560 Cypress Semiconductor Corp. CY7C65640 USB-2.0 "TetraHub"

Bus 004 Device 001: ID 0000:0000

Bus 003 Device 001: ID 0000:0000

Bus 002 Device 002: ID 0624:0294 Avocent Corp.

Bus 002 Device 001: ID 0000:0000

Bus 001 Device 001: ID 0000:0000

看这个第二行, Cypress Semiconductor Corp., 这么一长串的东西,你说哪来的?是不是应该从设备里来?设备的那几个标准描述符,整个描述符的大小也不一定放得下这么一长串,所以,一些设备专门准备了一些字符串描述符(string descriptor).就用来记这些长串的东西,我们结合刚才的518行开始讲,如果设备描述符里边iManufacturer不为0,那么调用usb_string,这句话具体做了什么?就是根据iManufactuer的值得到公司名字,而iManufactuer的第一个字母i,就表示index,它记录的是真正的公司名字保存在哪一个字符串描述符中,因为字符串描述符可以有多个,那么必然就有个号码来区分,接下来几行,iProduct记录了产品名在第几个字符串描述符中,iSerialNumber记录了产品序列号在第几个字符串描述中,然后调用usb_string这个函数,就把真正的字符串描述符里的冬冬给记录了下来.我们看到,我们三次调用的时候分别传递了us->vendor,us->product,us->serial.这样函数调用结束之后,这三个里面就记录了必要的信息,于是以后我们就可以用了.

得到了us->vendor,us->product,us->serial,那么下面528直到547行就不需要多讲了,就是说如果得到的东西是空的,(得到的是空可以有两种可能,一个是设备根本就没提供这些字符串描述符,另一种情况是usb_string函数没能成功,但是这个函数不成功也无所谓,没影响.)那也没关系,毕竟这些信息我们可有可无,无非是打印出来给客户看看.如果unusual_dev里边有的话,那就拷贝过来,如果也没有,那没办法,设为Unknown.而序列号这个就索性置为None好了,最后US_DEBUGP把这些信息给打印出来,如果你打开了debug开关,那么你会在日志文件里看到这么一句话,在/var/log/messages里边.

至此,get_device_info这个函数就结束了他的使命.在usb storage这部戏里,他将不再出场.但我想说,对于usb storage这整个模块来说,主角配角不重要,每个函数都是画布上的一抹色彩.就像我们每一个人,不也是别人人生中的配角,但总是自己人生的主角吗?

DF12型手扶拖拉机变速驱动系统设计

本科毕业设计 题目:DF12型手扶拖拉机变速驱动系统设计 学院: 姓名: 学号: 专业:机械设计制造及其自动化 年级: 指导教师:

摘要 随着变型运输拖拉机和农用运输车的发展,原来依靠农村运输业发展起来的小四轮拖拉机逐步转向田间地头。然而,目前小四轮拖拉机田间作业能力差,又没有很多配套的农业机械,农忙季节短,致使大量小四轮拖拉机一年中作业时间短,被迫长期闲置着。这影响了农村专业户的作业效益,也造成了不应该有的资源浪费。针对这些情况,我们在原有小四轮拖拉机的基础上稍微作些改动,使它的功能延伸。譬如可在原来小四轮拖拉机的基础上,改变座位、方向盘、离合、油门、刹车的方位,把拖拉机变成倒开式,在变速箱后安装挖掘装置、铲运装置或装载装置而成。 本次毕业设计是对机械专业学生在毕业前的一次全面训练,目的在于巩固和扩大学生在校期间所学的基础知识和专业知识,训练学生综合运用所学知识分析和解决问题的能力。是培养、锻炼学生独立工作能力和创新精神之最佳手段。毕业设计要求每个学生在工作过程中,要独立思考,刻苦钻研,有所创造的分析、解决技术问题。通过毕业设计,使学生掌握改造方案的拟定、比较、分析及进行必要的计算。 关键字:齿轮操纵机构轴轴承锁定机构

DESING of a CERTAIN TYPE of TRACTOR GEARBOX With variant transport tractors and agricultural the development of carriage car, depends on rural transportation industry to develop the small four-wheel tractor to field edge of a field. However, at present, small four-wheel the tractor field work ability is poor, and not many ancillary agricultural machinery, busy season is short, resulting in a large number of small four-wheel tractor year short operation time, forced long-term idle. The influence of rural specialist work benefit, also cause should not be some waste of resources. In light of these circumstances, we in the original small four-wheel tractor based on slightly to make some changes, make it functional extension. For example, in the original small four-wheel tractor based on, change seats, steering wheel, clutch, throttle, brake position, the tractor into inverted open, in a gearbox installed after digging device, lifting device or a loading device. The graduation design is about mechanical speciality students before graduation and a comprehensive training, purpose is to consolidate and expand the students learn the basic knowledge and professional knowledge, training students' comprehensive use of the knowledge the ability to analyze and solve problems. Is training, training students the ability to work independently and the spirit of innovation is the best means. Graduation design requirements of each student in the course of the work, we need to think independently, study assiduously, create somewhat analysis, solving technical problems. Through the graduation project, so that students master the transformation plan formulation, comparison, analysis and necessary calculation. Key words:Gear Manipulation of body Axis Bearing Locking mechanism

DAQ数据采集卡快速使用指南

DAQ数据撷取卡快速使用指南 首先感谢您选购NI的DAQ产品,以下将简短地为您叙述快速安装与使用DAQ卡的步骤。 在安装DAQ的硬件之前,请您先确认是否安装了DAQ的驱动程序,基本上您的计算机必须有Measurement And Automation (MAX)来管理您所有的NI装置,另外您必须安装NI-DAQ软件,目前建议安装最新的版本(您可利用光盘安装或是上网下载最新版本驱动程序https://www.360docs.net/doc/913061816.html,/support点选Drivers and Updates),新版驱动程序可支持大多数NI的DAQ卡片,包含S、E、M系列以及USB接口产品。 在安装完成NI-DAQ之后,您可以在桌面上发现有MAX应用程序,此时您可以关闭计算机,进行硬件安装,将PCI或是PCMCIA接口的DAQ卡片插入并重新开机,开机之后操作系统会自行侦测到该装置,并且自动安装驱动程序,依照对话框的带领便能顺利完成安装程序。 安装程序完成后,建议您开启MAX在Device and interface选项中会有Traditional DAQ 以及DAQmx两个类别,那是依照您的卡片型号支持哪一种API而分类,一般而言,E系列卡片两种都支持,而M系列只支持DAQmx,S系列则不一定,在对应的Traditional DAQ 或DAQmx中找到您的DAQ卡片型号,然后建议您先进行校正以及测试。 您可参考https://www.360docs.net/doc/913061816.html,/support/daq/versions确认您硬件适用的版本 如何做校正与硬件测试:

若需校正硬件,请于MAX中,您所安装的卡片型号上按鼠标右键选择self-calibration 即可,系统会对DAQ卡以现在温度做一次校正。 若需测试硬件,请于MAX中,您所安装的卡片型号上按鼠标右键选择Test Panels,然后选择所要测试的项目,并且依照接脚图将讯号连接妥当即可测试,建议您分别测试AI、AO、DI以及Counter。 接脚图: 您可以在MAX中的DAQmx找到您所安装的卡片型号,并按鼠标右键,选择Device Pinout便可以依照接脚图去连接相关接法,进行量测。 接线模式(Input Configuration):接线模式一般有分为Differential、RSE、NRSE三种,其中以Differential最为准确,但此模式需要一次使用掉两个channel,一般而言,选择Differential

数字驱动 共赢未来

数字驱动共赢未来 一、“三元世界”正在加快形成 伴随着数字化的变革与智能化时代的到来,信息技术正在发生革命性的进步,数字建模、传感互联、虚拟全息、增强交互、人工智能等技术广泛应用,世界逐渐从原来的“二元世界”,即人类的意识世界和物理世界,进入到“三元世界”,数字世界逐步成为世界的新一极。“意识世界—数字世界—物理世界”相互交汇、相互作用、融合发展并产生新的变化。 在三元世界中,人脑是“意识世界”的核心,电脑是“数字世界”的核心。通过数字建模,人们可以将意识所想先行作用于数字世界中的数字虚体,可以不受时间和空间的限制进行设计、模拟和优化,可以不眠不休地进行超高速的运算、分析和推演,直到达到最优方案后再实施,这让人们能更高效、更低成本、更充分地实现意识世界的构想。数字世界中的数字虚体可以借助物联网等技术充分感知物理世界并形成实时的映射,再通过数字驱动的智能算法和程序来操控物理世界中的实体自主化运行。同时,物理世界也不断地将信息与数据反馈给数字世界,加速了数字世界的自我学习和进化演进,使其拥有了类似于人的感知和认知能力,为意识世界提供更智能化的服务,极大地解放了人类的劳动力,进一步提升了人类意识世界的想象力和创造力。进而让意识世界能更充分和全面地感知物理世界,更优化、创新地改造物理世界。可以说,“三元世界”的相互促进、共同进化、共生发展,让人们认识世界和改变世界的能力大大提升,成本大幅降低,进一步提高了人们对物理世界的改造效率,加快了人们对物理世界的改造进程。 二、传统电子招投标存在的不足 近年来,伴随着公共资源交易管理电子化的不断深入推进,电子交易平台的建设取得了很大的成就,公共资源交易平台更加便捷、高效、透明,随着新型技术不断地涌现,也给传统的电子招投标带来了新的发展机遇和影响。在新的数字化潮流背景下,纵观传统的电子招投标,操作过程中还存在以下几方面的不足。 1.电子化却未必真的方便快捷。电子评标的部分环节实质提升效率有限,依然是以阅读静态的文字和图片为主,评审方式劳动强度大,工作效率低,不能看到直观的效果,评审过程中需要依赖较强的个人专业素养和工程经验,评审效果主观性强,对评委管理较难,无法很好地避免招投标过程中的违法违规问题。 2.信息化却未必真的信息对称。标书文件的各个部分是彼此关联的,传统的电子招投标中缺乏将彼此关联的信息集成的平台,评审过程中相关因素的关联性无法保障,评审中彼此割裂,无法联动,如技术方案和相关措施的联系,场地布置等方案的评审等,均需要统筹关注和综合考虑,但是传统的手段在这方面难以达到理想的效果,这一不足在设计施工一体化(EPC或PPP)评审项目中更为突出。

顾客价值及其驱动因素

顾客价值及其驱动因素 企业竞争说到底可以归结为顾客之争——顾客份额和顾客知识之争,而企业拥有的唯一战略武器就是:创造和交付优异的顾客价值。 (一)顾客价值的层次性与动态性 Zeithaml在1988年指出,感知价值是主观的,随顾客的不同而不同。顾客对某一产品的期望价值不仅在不同顾客之间会所有差别,而且同一顾客在不同时间的期望价值也会不同。这表明顾客价值的性质及影响因素在顾客与公司交往的不同阶段可能会发生变化。换句话说,激发顾客最初购买某种产品的属性可能不同于顾客购买后使用过程中的价值标准,后者可能又不同于长期使用过程中的价值决定因素。此外,引发顾客离弃的缺陷,也并不必然发生在顾客在使用产品时对主导价值评价的标准上。类似地,Ravald在1996年做出了这样论述:“不同顾客具有不同的价值观念、需求、偏好和财务资源,而这些资源显然影响着顾客的感知价值”。事实上,在明确了顾客价值内涵的基础上,不难理解上述论断的科学性。例如,感知所得可能因顾客而异(如有的可能要数量,另一些要高质量,还有的要便利),付出也可能有所不同(如一些顾客只关心所付出的金钱,一些则关心所付出的时间和努力)。同时,顾客价值也可能因适用环境的不同而有所差异,顾客在不同时间对价值的评估可能有所不同,例如,在购买决策之前、实际购买过程之中和产品使用之后,顾客对价值的评估可能存在重大差异,因为在不同的时间阶段,顾客评判的标准可能会有所不同。在购买阶段,顾客需要比较不同的产品或服务,并选出自己最喜欢的;而在产品的使用中或之后,顾客更关心的是所选产品的效用。值得指出的是,这种现象实际上已得到证实:Gardial,Clemons,Woodruff,Schumann及Burns(1994)的研究表明:顾客在购买产品过程中对价值的感知与使用过程中或之后截然不同。即不同顾客可能有不同的价值感知,而同一顾客在不同时刻也会有不同的价值感知,即顾客价值具有明显的层次性和动态性。 后来,Flint等人在1997又进一步描述了顾客价值的动态特征,列出了能够改变顾客价值感知的一些“触发事件”(Trigger event);而Woodruff(1997)教授基于信息处理的认知逻辑,提出了顾客价值的层次模型;在对 Woodruff(1997)文章的一篇评论中,Parasueaman(1997)指出,随着顾客从第一次购买到短期顾客再到长期顾客的转变,他们的价值评价标准可能会变得越来越全面、抽象:第一次购买的顾客可能主要关注属性层次的标准,但是短期和长期顾客可能关注的是结果层次和全局层次的标准。他还进一步提出了一个系统监测模型,把顾客区分为初次顾客、短期顾客、长期顾客和离弃顾客4种基本类型,并形象地论述了各自的动态变化。在对上述研究进行总结与提升的基础上,图1描述了顾客价值的动态层次模型——随着时间的推移和与供应商关系的深化,构成顾客金字塔的、具有不同特征的不同顾客细分市场上的顾客对价值感知所表现出的动态层次性。该模型认为,顾客以途径—结果(means-end)模式形成期望价值,从最低一层开始,顾客首先会考虑产品的特定属性及其效能;在购买和使用产品时,顾客根据特定产品属性对实现期望结果的贡献,而形成一种期望和偏好,反映在顾客价值上就是使用和拥有价值(第二层);同时,顾客也会根据产品属性对实现自身目标和目的的贡献,形成对特定使用结果的期望(最

在LabVIEW中驱动数据采集卡的三种方法

在LabVIEW中驱动数据采集卡的三种方法 作者:EEFOCUS 文章来源:EDN China 一、引言 近年来,面向仪器的软件开发平台,如美国NI公司LabVIEW的成熟和商业化,使用者在配有专用或通用插卡式硬件和软件开发平台的个人计算机上,可按自己的需求,设计和组建各种测试分析仪器和测控系统。由于LabVIEW提供的是一种适应工程技术人员思维习惯的图形化编程语言,图形界面丰富,内含大量分析处理子程序,使用十分方便,个人仪器发展到了使用者也能设计,开发的新阶段。 鉴于是工程技术人员自己编制,调用软件来开发仪器功能,软件成了仪器的关键。故人们也称这类个人仪器为虚拟仪器,称这种主要由使用者自己设计,制造仪器的技术为虚拟仪器技术(Virtual Instrumentation Technology)。使用虚拟仪器技术,开发周期短、仪器成本低、界面友好、使用方便、可靠性高, 可赋于检测仪初步智能,能共享PC机丰富的软硬件资源,是当前仪器业发展的一个重要方面。 虚拟仪器的典型形式是在台式微机系统主板扩展槽中插入各类数据采集插卡,与微机外被测信号或仪器相连,组成测试与控制系统。但NI公司出售的,直接支持LabVIEW的插卡价格十分昂贵,严重限制着人们用LabVIEW来开发各种虚拟仪器系统。在LabVIEW中如何驱动其它低价位的数据采集插卡,成为了国内许多使用者面临的关键问题。 二、三种在LabVIEW中使用国产数据采集插卡的方法 笔者将近年来工程应用中总结出的三种在LabVIEW中驱动通用数据采集插卡的方法介绍如下。介绍中,以某市售8通道12位A/D插卡为例。设插卡基地址为base=0x100,在C语言中,选择信号通道ch的指令是_outp(base,ch),启动A/D的指令是_inp(base),采样量化后的12位二进制数的高4位存于base+2中,低8位存于base+3中。 1、直接用LabVIEW的In Port , Out Port图标编程 LabVIEW的Functions模板内Adevanced \ Memory中的In Port 、Out Port 图标,与_inp、_outp功能相同,因此可用它们画程序方框图, 设计该A/D插卡的驱动程序。N个通道扫描,各采集n点数据的LabVIEW程序方框图如图1所示。图中用LabVIEW的计时图标控制扫描速率。

数字驱动HR决策

数字驱动HR决策 数据分析原则之一,是以业务结果为导向。 美国田纳西州的孟菲斯市号称美国的“物流产业之都”,位于几条州级高速公路和几条东西铁路大动脉的交汇处,得天独厚的地理优势吸引了大批物流分销企业到此落户。其中,最著名的一家企业就是联邦快递(Fedex)。 但是,众多工业仓储企业蜂授拥而至,也随之引出一个问题;企业之间的人才竞争趋于白热化。 当地本来人才供应就比较有限,加上各家公司的薪资待遇差不多,有一段时间,很多企业都陷入了用工荒。于是,能否有效找到合适的仓储工人,成为各家企业管理者们最头疼的事情。 其中一家当地大型物流公司管理层把解决招聘作为公司首要任务,希望HR团队跳出传统工作模式,积极创新地解决招人难的问题。公司董事长向HR提出问题:从人员搜寻、招聘和入职阶段来看,公司现有的高绩效员工都有哪些共同特征? 为此,HR的招聘团队从数据分析(HR Analytics)角度开始,着手寻找HR整个工作在招聘渠道、招聘方式、挑选流程以及员工绩效绩效考核结果方面有哪些相关联的因素。 任何一个标准的数据分析流程都包括三个环节:输入、过程、输出。在本案例中,输出端是高绩效员工,可以根据HR信息系统里面每名员工最近一次的绩效评估结果予以识别。另一方面,来自招聘渠道的输入质量,也可以根据招聘系统里的人员数据来记录和识别。整个流程中的挑选和入职方式可以通过员工档案得到。

显然,涉及的所有人力数据已大量存在于组织中,只是缺乏关注。而这次做人力数据分析的目的,正是需要去发掘和利用这些宝贵的数据。 数据宝藏 数据分析发现了一些出乎人们意料的结果,导致后来整个招聘流程的大幅改革。通过对输入端的数据分析,获得了以下发现:大多数高绩效员工在申请岗位时,其现有公司到所申请公司的距离都比较近;大多数仓储岗位的应聘人员不愿意到离家远的地方工作;申请人主要通过公司大楼外的醒目标识或其他在职员工(而不是报纸或杂志的招聘广告)来了解岗位空缺;诱人的员工福利计划是促使申请人决定跳槽的关键因素。 通过对整个招聘流程的数据分析,又获得了以下发现: 优秀候选人更倾向于到现场应聘,而不只是通过在线填写一堆表格应聘;把候选人未来可能工作的场所作为招聘现场,比到公司以外的地方去展开招聘更吸引候选人;候选人通常在下午偏晚一些的时候才会提交他们的应聘资料,因为此时正是他们在其他公司交接班之际;最有效的面试官是生产线工人领班,而不是工人主管。 基于以上分析,该公司采取了以下措施来改进招聘工作: 首先,HR重新设计了招聘方案,主题是“到一个离家近而且福利好的公司工作是一种什么体验?”这个广告语出现在该工业区入口处一个醒目的大广告牌上。另外,还广泛张贴在工业区内的一些餐饮和零售店的公告栏里。 另外,HR重新设计了来公司的参访环节,辅之以现场面试,还能享受美味零食。参访时间安排在每周二和周四的下午3点到7点,方便每天处于交接班的候选人前来应聘。最后,招聘人员还特别为

电动车辆无级变速驱动系统、变速控制系统及方法与设计方案

一种电动车辆技术领域的电动车辆无级变速驱动系统、变速控制系统及方法,包括:第一驱动电机、行星轮系、第二驱动电机和第三输出机构,其中,行星轮系设有齿圈、行星支架、太阳轮和若干行星齿轮,行星齿轮周向均布在行星支架上,行星支架和齿圈之一与第二驱动电机的输出轴啮合、另一与第三输出机构啮合,齿圈与行星齿轮啮合,太阳轮与第一驱动电机输出端连接并与行星齿轮啮合。本技术采用行星减速机构,通过第二驱动电机带动行星支架或者齿圈的转动达到所需要的减速比,完成电动车的无级变速,实现换挡自动化。 技术要求 1.一种电动车辆无级变速驱动系统,其特征在于,包括:第一驱动电机、行星轮系、第二驱动电机和第三输出机构,其中:行星轮系设有齿圈、行星支架、太阳轮和若干行星齿轮,行星齿轮周向均布在行星支架上,行星支架和齿圈之一与第二驱动电机的输出轴啮合、另一与第三输出机构啮合,齿圈与行星齿轮啮合,太阳轮与第一驱动电机输出端连 接并与行星齿轮啮合。 2.根据权利要求1所述的电动车辆无级变速驱动系统,其特征是,所述的齿圈、行星支架和太阳轮均套设在主轴上。 3.根据权利要求1所述的电动车辆无级变速驱动系统,其特征是,所述第三输出机构设有轮轴,所述的轮轴与电动车辆的驱动轴固定连接。 4.根据权利要求2所述的电动车辆无级变速驱动系统,其特征是,所述的第一驱动电机包括转子总成和绕线定子,其中,转子总成设有转子内衬套,转子内衬套一端固定有太阳轮。 5.根据权利要求2所述的电动车辆无级变速驱动系统,其特征是,所述第一驱动电机的输出端与太阳轮通过减速机构相连,所述的减速机构为皮带、链条、齿轮传动机构中任意 一种。

6.一种应用于上述任一权利要求所述电动车辆无级变速驱动系统的变速控制系统,其特征在于,包括:行车电脑、制动传感器、变速传感器、第一驱动电机控制单元、第二驱动电机控制单元、第一转速传感器和第二转速传感器,其中, 制动传感器与行车电脑相连并输出制动信号; 变速传感器与行车电脑相连并输出加速信号或减速信号; 行车电脑与第一驱动电机控制单元、第二驱动电机控制单元相连并传输两驱动电机的控制信息; 第一转速传感器与行车电脑相连并输出第一驱动电机转速信息; 第二转速传感器与行车电脑相连并输出第二驱动电机转速信息。 7.根据权利要求6所述电动车辆无级变速驱动系统的变速控制系统,其特征在于,所述的变速控制系统还包括:第一电流传感器、第二电流传感器、第一电压传感器、第二电压传感器、第一扭矩传感器和第二扭矩传感器,其中, 第一扭矩传感器与行车电脑相连并输出第一驱动电机扭矩信息; 第二扭矩传感器与行车电脑相连并输出第二驱动电机扭矩信息; 第一电流传感器、第一电压传感器与第一驱动电机控制单元相连并分别输出第一驱动电机的输入电流和输入电压信息; 第二电流传感器、第二电压传感器与第二驱动电机控制单元相连并分别输出第二驱动电机的输入电流和输入电压信息。 8.一种基于权利要求6或7所述变速控制系统的电动车辆变速控制方法,其特征在于,包括以下步骤: S1,首先判断输入信号是否为制动信号,若为制动信号,则第一驱动电机和第二驱动电机停转,电动车辆停车,否则判断为变速信号;

研华数据采集卡USB 的安装和使用

基于Labview的研华数据采集卡的安装和使用数据采集卡型号:USB 4704,要求用labview采集研华的采集卡上的数据第一节研华设备管理器DAQNavi SDK安装 安装前的准备: 要求先安装好labview, 然后再进行以下安装 第一步: 安装研华的DAQ设备管理程序DAQNavi SDK包 1. 双击""文件,弹出安装对话框, 选择第1项“Update and DAQNavi”并点击“Next”: 点击“Next”:

如左上所示勾选,并点击“Next”: 点击“Next”,得如下图所示对话框,表示正在安装,请耐心等待。

耐心等待安装结束。安装结束后,选择操作系统上的“程序”,在程序列表中应该有“Advantech Automation”选项,点击该选项展开应有“DAQNavi”,如下图所示: 单击上图中的“Advantech Nagigator”选项,即可打开研华的设备管理器对话框,如下图所示,在这里,左侧的“Device”栏中列出了本机上连接的所有采集卡,可以对这些卡进行管理和测试,具体如何测试,请参照帮助文档。

第三二步.usb4704采集卡驱动安装 1. 双击“进行安装; 2. 安装完毕后,将采集卡与PC机相连(将usb数据线一端连上采集卡,另外一端连到计算机的USB口上),系统将自动安装采集卡的驱动,并识别采集卡。 3. 检查采集卡安装成功否 首先查看插在PC机上的采集卡上的灯是否呈绿色; 其次,打开“DAQNavi”,如下图所示,观察设备列表中是否显示出了“USB-4704” 第三步:在研华的设备列表中添加模拟卡(Demo Device) 若没有实际的采集卡,可以添加模拟卡进行模拟测试和数据采集编程练习 那么如何添加模拟卡呢? 如下图所示,点击“Advantech Automation”——〉DAQNavi ——〉Add Demo Device

数字证书驱动安装说明

数字证书驱动安装说明 驱动安装步骤: 第一步:在GDCA网站(https://www.360docs.net/doc/913061816.html,)或者网挂系统网站下载数字证书客户端普通版驱动(3.9版本以上)。 第二步:在安装包中,双击“Setup.exe”文件执行运行程序,进入程序安装主页面,如图1。 (图1) 第三步:在图1中,选择“安装GDCA数字证书客户端”选项,进入准备安装页面,如图2。 (图2) 第四步:单击“下一步”,进入用户信息页面,如图3。正确填写“用户姓名”和“单

位”,其他保持默认设置即可。 (图3) 第五步:单击“下一步”,进入到“自定义安装”页面,如图4。 (图4) 第六步:保持所有默认值设置,单击“下一步”,进入“请输入GDCA KEY序列号”页面,如图5。

(图5) 第七步:在“序列号”输入框输入已经办理的数字证书外壳上由字母和数字组成的8位序列码(如W807####)。单击“下一步”,进入“已做好安装程序的准备”页面,如图6。 (图6) 第八步:单击“安装”按钮,计算机自动进行安装。在自动安装过程中会弹出“请确认UKEY已经拔出!”页面,如图7。请在确定正在安装驱动程序的计算机上没有插入数字证书后,单击“继续”按钮。

(图7) 第九步:驱动程序安装完成后,自动弹出“安装完成”提示页面,如图8。单击“完成”按钮即可。 (图9) 第十步:在如图1所示的页面中,选择“退出”按钮退出“程序安装主页面”,并重新启动计算机。 第十一步:计算机重新启动后,插入数字证书,计算机会自动弹出提示信息,如图10所示。

(图10) 第十二步:打开IE浏览器(建议使用IE8版本),输入网上挂牌交易系统域名,在登录网页输入密码,并单击“登录”按钮。若出现如图11所示的提示信息,说明IE自动禁止了数字证书的加载。请查看浏览器是否有如图12所示的提示信息,重新进入登录页面,右键该提示信息并选择“允许运行该加载项”,装载完成后,输入密码进行登录即可。 (图11) (图12)

客户驱动与服务驱动相结合的软件营销模式探讨

客户驱动与服务驱动相结合的软件营销模式探讨 摘要:文章以短短不到5年的时间成长起来的奇虎360公司为例子,对客户驱动和服务驱动相结合的软件营销模式进行了探讨。文章认为传统的营销模式在互联网的冲击下,必然面临着重大的变革。当今软件营销的重点在于增加用户基数与服务客户。而客户驱动和服务驱动相结合的软件营销模式是进入互联网时代后软件营销模式发展的必然趋势。 关键词:客户驱动;服务驱动;软件;营销 随着网络的日益地普及,软件消费文化正在逐渐地发生着改变,而这种变化更是严重地冲击了软件行业的销售模式。在巨大的市场和文化的冲击之下,各个软件制造商、销售商纷纷开始采取战略措施来维护自身利益,开拓更大的发展空间。 从市场营销的角度看,可以将软件产品分为四类:消费类软件产品、嵌入式软件产品、系统软件产品和应用软件产品。文献认为只有第三类软件产品适于采用免费赠送、开放技术等营销战略。系统软件产品包括计算机操作系统软件、杀毒软件等,是保证计算机系统正常运行和基本应用的软件产品。这类软件产品是消费者在使用计算机时必不可少的,它在互联网的冲击下的营销模式会有怎样的改变? 1传统软件营销模式 试想一下10多年前,如果公司开发出了一套不错的通用型软件,会如何去销售?首选是将软件的版权卖给大公司,因为“品牌效应”与商业发展同行,站在巨人的肩膀上可以看得更远。可能公司也会考虑在各类媒体上花钱打广告,或者与连邦公司这样的通用软件连锁商谈判。或者公司会选择自己开发销售渠道,直接找报刊亭、书店或音像店谈判。那么7、8年前呢?如果软件足够优秀,可能公司会去和几大PC厂商谈判,因为那个年代“捆绑营销”的概念正蒸蒸日上。 传统通用软件的销售模式与其他产品的销售模式趋于一致,但当互联网时代席卷而来的时候,传统软件仅仅在分销渠道方面就面临翻天覆地的变化。 软件分销渠道,也称软件营销渠道或配销通路,指软件产品从开发者手中转至消费者所经过的各中间商连接起来形成的通道。它由位于起点的开发者和位于终点的消费者以及二者之间的中间商组成。

数字芯片的驱动能力详解

数字芯片的驱动能力详解 1.芯片驱动能力基本概念 芯片驱动能力,是指在额定电平下的最大输出电流;或者是在额定输出电流下的最大输出电压。具体解释如下。 当逻辑门输出端是低电平时,灌入逻辑门的电流称为灌电流,灌电流越大,输出端的低电平就越高。由三极管输出特性曲线也可以看出,灌电流越大,饱和压降越大,低电平越大。然而,逻辑门的低电平是有一定限制的,它有一个最大值UOLMAX。在逻辑门工作时,不允许超过这个数值,TTL逻辑门的规范规定UOLMAX ≤0.4。所以,灌电流有一个上限。 当逻辑门输出端是高电平时,逻辑门输出端的电流是从逻辑门中流出,这个电流称为拉电流。拉电流越大,输出端的高电平就越低。这是因为输出级三极管是有内阻的,内阻上的电压降会使输出电压下降。拉电流越大,输出端的高电平越低。然而,逻辑门的高电平是有一定限制的,它有一个最小值UOHMIN。在逻辑门工作时,不允许超过这个数值,TTL逻辑门的规范规定UOHMIN ≥2.4V。所以,拉电流也有一个上限。 可见,输出端的拉电流和灌电流都有一个上限,否则高电平输出时,拉电流会使输出电平低于UOHMIN;低电平输出时,灌电流会使输出电平高于UOLMAX。所以,拉电流与灌电流反映了输出驱动能力。(芯片的拉、灌电流参数值越大,意味着该芯片可以接更多的负载,因为,例如灌电流是负载给的,负载越多,被灌入的电流越大)。 2.怎么通过数字芯片的datasheet看其驱动能力 以时钟buffer FCT3807例,下图是从Pericom的FCT3807的datasheet截取的。 当其输出为高电平2.4V时,其输出电流为8mA,也就是拉电流为8mA。如果输出电流大于8mA,那么其输出电平就低于2.4V了,就不能称其输出高电平,所以可以说FCT3807输出高电平的驱动能力为8mA。 同样道理,FCT3807输出低电平的驱动能力为24mA。 3.怎么通过数字芯片的驱动能力来估算输出信号的过冲等指标 仍然以Pericom的FCT3807为例,其输出为高电平时的输出阻抗为: RH= (3.3V – 3V )/ 8mA = 37.5欧姆。 其输出为低电平时的输出阻抗为: RL= 0.3V / 24mA = 12.5欧姆。 从上面的计算可以看出,3807输出为高电平和输出为低电平时的驱动能力不一样,也就是输出阻抗不一样,所以用串联匹配的方法很难做到完全匹配,常常表现为overshoot-大

数字经济的新驱动要素

数字经济的新驱动要素 本文首发于微信公众号:网络智酷。文章内容属作者个人观点,不代表和讯网立场。投资者据此操作,风险请自担。 中国互联网协会副理事长、国家信息化专家咨询委员会委员高新民分享了对《中国“互联网+”数字经济指数(2017)》报告的看法并对数字经济与实体经济关系以及数字经济驱动力进行了阐述。以下是其发言实录: 高新民演讲视频 非常感谢腾讯研究院邀请我来参加这次活动,今天讲数字经济,我稍微讲一些自己个人的看法,也可能跟发表的指数有一些不一样的地方,也可能我不一定对,大家可以参照。 首先数字经济的概念慢慢得到了共识。数字经济的概念,在国际上有不同的解释,是一个有歧义性的东西,特别是我们国内现在用的比较多,在研究院的报告里面也都提到了。我们引用信息经济、网络经济、互联网经济、新经济、知识经济等等,当然还有数字经济。但是目前数字经济在国际上用的比较多一些,包括我们在杭州开的G20,对数字经济有一些倡议。在最近一带一路的会议上也提到在数字经济方面要开展一些合作,当然别的词也有。 实际上最早的概念是信息经济,没有计算机的背景。当时讲信息经济是在统计信息指数的时候,那个指数要素是什么?比如你订了多少报纸,一个月花了多少电话费,这都算信息经济的概念,那个时候根本没有数字计算机的概念。其实计算机出来以后还有两个阶段,一个是模拟计算机(Analogue computer),一个是数字计算机(Digital computer),数字计算机出现以后就数字化了。数字计算机出来的时候,尤其是PC出来以后,在80年代初期以后,以数字为基础的技术以及后来发展到网络,以数字计算机控制了网络为主的经济活动,一般当时就叫数字经济。 所以信息经济出现最早,后来是数字经济。数字经济是在数字计算机技术普及以后形成的信息经济,应该这样来理解。现在仍然有用信息经济的概念,与数字经济应该是一个概念。再后来就有了互联网,互联网出来就更加强调了互联网的支撑作用,出现在90年代以后(互联网的商业化是1991年),以前都是工业性的东西。 目前数字经济概念从80年代、90年代开始用,是跟互联网和数字计算机的应用开始有联系的,但是最近互联网发展以后,大数据的技术成熟开始应用,特别是在大数据基础上人工智能发展到新的阶段以后,也包括物联网产生大数据的支撑,大数据概念出现以后就变成了以数据驱动的经济概念。这个数字经济的概念,是目前国际上比较流行的数字经济的概念。 所以基本上可以有两个数字经济的概念:一个是基于数字计算机技术和网络的经济,包括数字网络支撑下的经济,是比较广义的;还有一种是当前大数据驱动的或者是数据驱动经济,这个叫数字经济,讲法是不一样的。现在我为什么要讲这个东西,G20里面讲了数字经济,是一个广义的数字经济,现在你们的报告也是用的这个,但是我们现在很多国际上知名的研究机构,比如说麦肯锡,讲数字金融和网络金融是讲窄义的东西,还有一些Govern Group,讲的是数字政府,是采用大数据驱动的概念。比如现在我们讲的电子政府,电子政府有四个阶段: 第一个阶段,E-government,就是我们讲的电子政务。 第二个阶段,Integrated government——整合型的电子政务,就是部门之间能够整合起来。 第三个阶段,Smart government——智能政府。 第四个阶段,Data driven government——数据驱动政府。 所以从它的概念里面看的话,它的数字经济和数字政府的概念是大数据驱动的概念,是最新的一个概念。我们在讲数字经济的时候,一定要把这个概念区分开,这两个概念我认为

数据采集卡PCI-8344A驱动说明书

PCI-8344A驱动1.2版说明 一、驱动适用范围 1. 适用于windows98,2K,XP系统 2. 编程适用于VC,VB,Delphi等决大多数编程语言 二、与上一个版本驱动的区别 1. 增加了一些错误号 2. 函数名普遍加了前缀“ZT8344A” 3. 废弃了用结构体传递参数的方式 三、驱动函数的参数说明 请以这个版本驱动中的《PCI8344A.h》文件中所述为准。 《PCI8344A.h》是一个纯文本文件,可用写字板或WORD打开。 推荐:如果用 VC 或 UltraEdit 打开,其中的注释及关键字会有不同的颜色, 从而有助于阅读。 四、连续AD采集的编程思路 1. 首先在程序初始化时调用 ZT8344A_OpenDevice 函数,用于打开设备,只调一次即可; 2. 调用 ZT8344A_DisableAD 函数,禁止AD 调用 ZT8344A_ClearHFifo 函数,清硬件缓冲区(HFIFO) 调用 ZT8344A_ClearSFifo 函数,清软件缓冲区(SFIFO) 调用 ZT8344A_OpenIRQ 函数,打开HFIFO半满中断 调用 ZT8344A_AIinit 函数,做一些AD初始化工作 3. 在一个循环中不断调用ZT8344A_GetSFifoDataCount 判断SFIFO中数据的个数, 申请一个数组,并把这个数组中传入 ZT8344A_AISFifo 用于接收数据, 把读出的数据保存到文件或直接显示, 注意:SFIFO的默认大小为 819200,用户要不断读数,使SFIFO有空间放入新的来自 HFIFO的数,如果SFIFO中的有效数据的个数接近 819200,会使整个AD过程停止。如果想重新采集,必须重复2—3步。 4. 调用 ZT8344A_CloseIRQ 函数,停止采集过程 5. 在程序退出前调用 ZT8344A_CloseDevice 函数 提示:1. 在这版驱动中,板卡的序号是从1开始的 2. 如果函数返回 -1,应该调用ZT8344A_ClearLastErr 函数得到错误号, 然后去《PCI8344A.h》文件中查找这个错误号对应的含义。 3. 一旦错误号不为0,如果想重新使函数正常工作,必须调用 ZT8344A_ClearLastErr 函数清除错误号。

数字证书驱动及客户端安装操作指引

数字证书驱动及客户端安装操作指引 广州市数字证书管理中心 二○一二年十月十日

目录 一、用户须知 (3) 二、安装要求 (3) 三、安装数字证书驱动程序 (5) 四、安装数字证书客户端 (9) 五、数字证书客户端功能介绍 (11) 1、证书注册功能 (11) 2、数字证书功能 (14) 3、系统设置功能 (26) 4、软证书功能 (28) 六、软件的卸载 (33)

一、用户须知 1、请到广州市数字证书管理中心网站下载数字证书客户端安装包:https://www.360docs.net/doc/913061816.html,/ 2、该压缩包包含以下三个文件 数字证书USB驱动程序; 数字证书客户端安装程序; 数字证书驱动及客户端安装操作指引。 在安装“数字证书USB驱动程序”及“数字证书客户端程序” 前请先查看“数字证书驱动及客户端安装操作指引”,然后再按照安装步骤先安装“数字证书USB驱动程序”,接着安装“数字证书客户端程序”。 3、目前数字证书客户端主要功能包括以下几部分: 数字证书注册 数字证书管理和测试 欠费提醒和控制 二、安装要求 操作系统版本: windows XP、windows2003、windows7(需兼容32位,暂不支持纯64位系统) 硬件要求: CPU:800MHZ或以上 内存:256M或以上 其中安装了windows7的用户请注意,进行安装前需要按以下步骤调节系统:

1、点击屏幕左下角“开始”; 2、在搜索栏输入UAC ,点击旁边的“搜索”按钮,如 图: 3、点击上方的“更改用户账号控制设置” 4、把滑动按钮拉到“从不通知”,如图:

5、点击“确定”后即可继续安装数字证书客户端。 6、待完成安装数字证书驱动程序和数字证书客户端后,按 照以上步骤把滑动条拉回原来位置。 三、安装数字证书驱动程序 注意:在安装驱动程序前如果已经插入了数字证书,请先拔出数字证书再进行安装。 (1)双击USB驱动包.exe,如下图:

研华数据采集卡USB的安装和使用

基于Labview的研华数据采集卡的安装和使用 数据采集卡型号: USB 4704,要求用labview采集研华的采集卡上的数据第一节研华设备管理器DAQNavi SDK安装 安装前的准备: 要求先安装好labview, 然后再进行以下安装 第一步: 安装研华的DAQ设备管理程序DAQNavi SDK包 1. 双击""文件,弹出安装对话框, 选择第1项“Update and DAQNavi”并点击“Next”: 点击“Next”:

如左上所示勾选,并点击“Next”: 点击“Next”,得如下图所示对话框,表示正在安装,请耐心等待。

耐心等待安装结束。安装结束后,选择操作系统上的“程序”,在程序列表中应该有“Advantech Automation”选项,点击该选项展开应有“DAQNavi”,如下图所示: 单击上图中的“Advantech Nagigator”选项,即可打开研华的设备管理器对话框,如下图所示,在这里,左侧的“Device”栏中列出了本机上连接的所有采集卡,可以对这些卡进行管理和测试,具体如何测试,请参照帮助文档。

第三二步.usb4704采集卡驱动安装 1. 双击“进行安装; 2. 安装完毕后,将采集卡与PC机相连(将usb数据线一端连上采集卡,另外一端连到计算机的USB口上),系统将自动安装采集卡的驱动,并识别采集卡。 3. 检查采集卡安装成功否 首先查看插在PC机上的采集卡上的灯是否呈绿色; 其次,打开“DAQNavi”,如下图所示,观察设备列表中是否显示出了“USB-4704” 第三步:在研华的设备列表中添加模拟卡(Demo Device) 若没有实际的采集卡,可以添加模拟卡进行模拟测试和数据采集编程练习 那么如何添加模拟卡呢 如下图所示,点击“Advantech Automation”——〉DAQNavi ——〉Add Demo Device

数字屏驱动板手册

HY-D70SD TFT控制板应用手册 目录 1.特点介绍 (2) 2.接口定义 (3) 3.安装尺寸 (4) 4.工作寄存器描述 (5) 5.工作寄存器配置说明 (6) 5.1背光控制 (6) 5.2行列地址写入 (6) 5.3行列地址增量方向 (6) 5.4读写数据通道 (6) 5.5复位和初始化 (6) 6.颜色配置说明 (7) 7.读写时序说明 (8) 7.1写行地址的时序 (8) 7.2写列地址的时序 (8) 7.3写显示数据的时序 (8) 7.4读显示数据的时序 (8) 7.5写其他命令寄存器时序 (9) 8.单片机硬件接口应用示例 (10) 9.单片机驱动程序应用示例 (10) 9.1示例代码(8051) (10)

1.特点介绍 HY-D70SD可控制5寸、7寸数字彩色TFT显示屏。本控制板采用16bit并行接口,最大支持800×480分辨率,65K色。为提高读写速度,简化控制程序,显示屏中每个点影射SD-RAM中的一个字。显示屏中的行列号与SD-RAM中的行列号一一对应,用户只需把数据连续写进SD-RAM中即可。对用户来讲,直接输入的是LCD上的点的坐标。 详细的程序编写请参考DEMO程序。对于800×480点阵彩色LCD,可储存32/64页显示内容。本控制板工作电压为5V,电流为100mA(仅指本控制板,不包括显示屏和背光)。 适配CPU:51,96,x86,DSP,ARM,AVR,PIC,MSP430等。 本控制板的响应速度很快,能达到200ns的读写周期。显示数据写入后,坐标地址自动增量。坐标地址支持向右或向下增量。32MB/64MB SD-RAM对应32页/64页显示缓冲。 TFT的驱动时序和电路都经过优化设计,保证色彩准确还原,显示稳定,杜绝闪烁或窜色,并提供LED背光驱动,亮度可从0(关闭)~16(全开)间调节。

数字工匠素养驱动数字创新发展

龙源期刊网 https://www.360docs.net/doc/913061816.html, 数字工匠素养驱动数字创新发展 作者:王洪艳 来源:《报刊荟萃(下)》2018年第06期 摘要:在数字经济时代下,探究培养数字工匠、提高数字素养、驱动数字创新、引领数 字创业方案。在当前职业现状采用新教学模式、新工科融合、重视校企培养、创新联盟、创业孵化等措施来完成短期内培养数字人才适用时代发展需求。 关键词:数字经济;数字工匠;数字创新 数字经济时代是继农耕经济和工业经济之后新经济形态,数字工匠开启了工业4.0和互联网+的起点,是中国制造2025数字化的新动能,助力新经济发展的基础支撑和保障。职业院校怎样才能为国家培养数字工匠、提高数字素养、驱动数字创新、引领创业值得探究。 一、数字时代培养数字工匠新举措 (一)坚持面向市场、服务发展、促进就业的方针,落实新发展理念,加快构建现代职业教育新体系 在数字时代制造领域升级对人才需求越来越高与职业院校生源基础差相违背的情况下,高职院校作为生产一线技术人才的培养基地,应针对智能时代新技术,新工艺的不断出现,及时合理地调整人才培养方案。制定数字工匠在校新工科专业交叉融合,引领创新、跨界发展改革实施方案。从本质上将传统学科转型、改造和升级,形成产学研一体的新学科教育。研究新工科重视专业交叉融合,拓展出智能时代需求的新工科。提高学生实战综合能力,真正为企业输送能解决实际问题的工匠。 (二)制定“数字工匠”连贯可行的培养模式和可行性培养体系 教育培养体系与时代发展相呼应,采用课堂虚拟教学、课后网络自学、产教融合、创新引领指导帮扶、培训联盟企业实战融为一体的高校创新创业培养体系[1]。坚持按照“推广技术,提升师资,课堂创新,竞赛强化,建立基地,合作培养,服务企业”的理念来制定职业学生培养方案。 虚拟现实是数字时代新的教学手段,它是计算机与多媒体教学之后重新改造人们学习方式的新技术,虚拟现实可以充分调动学习者的兴趣,学生们能进入到逼真的学习空间中,体验沉浸式的学习快乐,从而大幅度提高学习成效。职业教育的方法和手段要跟上技术的发展,教学方法与多元信息融合的交互式教学,高效快捷的先进教学手段是在短期内培养出高质量人才的最佳捷径 (三)建立校企、校内孵化基地,校企从合作与伙伴到共生与互助

相关文档
最新文档