在之前章节描述的 struct usb_driver 结构中, 驱动指定 2 个 USB 核心在合适的时候 调用的函数. 探测函数被调用, 当设备被安装时, USB 核心认为这个驱动应当处理; 探测 函数应当进行检查传递给它的关于设备的信息, 并且决定是否驱动真正合适那个设备. 去 连接函数被调用当驱动应当不再控制设备, 由于某些理由, 并且可做清理.

探测和去连接函数回调都在 USB 集线器内核线程上下文中被调用, 因此它们中睡眠是合 法的. 但是, 建议如果有可能大部分工作应当在设备被用户打开时完成. 为了保持 USB 探测时间为最小. 这是因为 USB 核心处理 USB 设备的添加和去除在一个线程中, 因此任 何慢设备驱动可导致 USB 设备探测时间慢下来并且用户可注意到.

在探测函数回调中, USB 驱动应当初始化任何它可能使用来管理 USB 设备的本地结构. 它还应当保存任何它需要的关于设备的信息到本地结构, 因为在此时做这些通常更容易. 作为一个例子, USB 驱动常常想为设备探测端点地址和缓冲大小是什么, 因为和设备通讯 需要它们. 这里是一些例子代码, 它探测 BULK 类型的 IN 和 OUT 端点, 并且保存一些 关于它们的信息在一个本地设备结构中:

/* set up the endpoint information */

/* use only the first bulk-in and bulk-out endpoints */ iface_desc = interface->cur_altsetting;

for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)

{

endpoint = &iface_desc->endpoint[i].desc; if (!dev->bulk_in_endpointAddr &&

(endpoint->bEndpointAddress & USB_DIR_IN) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)

== USB_ENDPOINT_XFER_BULK)) { /* we found a bulk in endpoint */ buffer_size = endpoint->wMaxPacketSize;

dev->bulk_in_size = buffer_size;

dev->bulk_in_endpointAddr
= endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(buffer_size,
GFP_KERNEL); if (!dev->bulk_in_buffer) {

err("Could
not allocate bulk_in_buffer"); goto error;

}

}

if
(!dev->bulk_out_endpointAddr &&

!(endpoint->bEndpointAddress
& USB_DIR_IN) && ((endpoint->bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK)

==
USB_ENDPOINT_XFER_BULK)) { /* we found a bulk out endpoint */

dev->bulk_out_endpointAddr
= endpoint->bEndpointAddress;

}

}

if
(!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr))

{

err("Could
not find both bulk-in and bulk-out endpoints"); goto error;

}

这块代码首先循环在这个接口中出现的每个端点, 并且分配一个本地指针到端点结构来使 它之后容易存取:

for (i = 0; i <
iface_desc->desc.bNumEndpoints; ++i) { endpoint =
&iface_desc->endpoint[i].desc;

那么,
在我们有了一个端点后, 我们还没有发现一个块 IN 类型端点, 我们看是否这个端 点的方向是 IN. 那个可被测试通过看是否位掩码 USB_DIR_IN 被包含在
bEndpointAddress 端点变量中. 如果这是真, 我们决定是否端点类型是块, 通过使用 USB_ENDPOINT_XFERTYPE_MASK 位掩码首先掩去
bmAttributes 变量, 并且接着检查是否 它匹配值 USB_ENDPOINT_XFER_BULK:

if
(!dev->bulk_in_endpointAddr &&

(endpoint->bEndpointAddress
& USB_DIR_IN) &&

((endpoint->bmAttributes
& USB_ENDPOINT_XFERTYPE_MASK)== USB_ENDPOINT_XFER_BULK))

{

如果所有的这些检查都是真, 驱动知道它发现了正确的端点类型, 并且可保存关于端点的信息到本地结
构中, 它后来将需要这些信息和它通讯.

/*
we found a bulk in endpoint */ buffer_size = endpoint->wMaxPacketSize;
dev->bulk_in_size = buffer_size;

dev->bulk_in_endpointAddr
= endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(buffer_size,
GFP_KERNEL); if (!dev->bulk_in_buffer)

{

err("Could
not allocate bulk_in_buffer"); goto error;

}

300

因为 USB 驱动需要获取在设备的生命周期后期和这个 struct usb_interface 关联的本 地数据结构,
函数 usb_set_intfdata 可被调用:

/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);

这个函数接受一个指向任何数据类型的指针, 并且保存它到 struct usb_interface 结构 为后面的存取.
为获取这个数据, 函数 usb_get_intfdata 应当被调用:

struct
usb_skel *dev;

struct
usb_interface *interface; int subminor;

int
retval = 0;

subminor
= iminor(inode);

interface
= usb_find_interface(&skel_driver, subminor); if (!interface)

{

err
("%s - error, can't find device for minor %d",

FUNCTION ,
subminor); retval = -ENODEV;

goto
exit;

}

dev
= usb_get_intfdata(interface); if (!dev)

{

retval = -ENODEV; goto exit;

}

usb_get_intfdata 常常被调用, 在 USB 驱动的 open 函数和在去连接函数. 感谢这 2 个函数, USB 驱动不需要保持一个静态指针数组来保存单个设备结构为系统中所有当前的
设备. 对设备信息的非直接引用允许一个无限数目的设备被任何 USB 驱动支持.

如果 USB 驱动没有和另一种处理用户和设备交互的子系统(例如 input, tty, video, 等 待)关联, 驱动可使用 USB 主编号为了使用传统的和用户空间之间的字符驱动接口. 为此, USB 驱动必须在探测函数中调用 usb_register_dev 函数, 当它想注册一个设备到 USB 核心. 确认设备和驱动处于正确的状态, 来处理一个想在调用这个函数时尽快存取这个设 备的用户.

/*
we can register the device now, as it is ready */ retval =
usb_register_dev(interface, &skel_class); if (retval)

{

/*
something prevented us from registering this driver */ err("Not able to
get a minor for this device."); usb_set_intfdata(interface, NULL);

goto
error;

}

usb_register_dev 函数要求一个指向
struct usb_interface 的指针和指向 struct usb_class_driver 的指针. struct
-usb_class_driver 用来定义许多不同的参数, 当注 册一个次编号 USB 驱动要 USB 核心知道这些参数. 这个结构包括下列变量:.

char *name

sysfs
用来描述设备的名子. 一个前导路径名, 如果存在, 只用在 devfs 并且本 书不涉及. 如果设备号需要在这个名子中, 字符 %d 应当在名子串中. 例如,
位创 建 devfs 名子 usb/foo1 和 sysfs 类名 foo1, 名子串应当设置为 usb/foo%d.

struct
file_operations *fops;

指向 struct file_operations 的结构的指针, 这个驱动已定义来注册为字符设备. 这个结构的更多信息见第
3 章.

mode_t mode;

给这个驱动的要被创建的 devfs 文件的模式; 否则不使用.
这个变量的典型设置 是值 S_IRUSR 和 值 S_IWUSR 的结合, 它将只提供这个设备文件的拥有者读和写 存取.

int minor_base;

这是给这个驱动安排的次编号的开始. 所有和这个驱动相关的设备被创建为从这个
值开始的唯一的, 递增的次编号. 只有 16 个设备被允许在任何时刻和这个驱动关 联, 除非 CONFIG_USB_DYNAMIC_MINORS 配置选项被打开. 如果这样, 忽略这个变 量, 并且这个设备的所有的次编号被以先来先服务的方式分配. 建议打开了这个选 项的系统使用一个程序例如 udev 来关联系统中的设备节点, 因为一个静态的

/dev 树不会正确工作.

当 USB 设备断开, 所有的关联到这个设备的资源应当被清除, 如果可能. 在此时, 如果
usb_register_dev 已被在探测函数中调用来分配一个 USB 设备的次编号, 函数 usb_deregister_dev 必须被调用来将次编号给回 USB 核心.

在断开函数中,
也重要的是从接口获取之前调用 usb_set_intfdata 所设置的数据. 接着 设置数据指针在 struct us_interface 结构为 NULL
来阻止在不正确存取数据中的任何 进一步的错误.

static
void skel_disconnect(struct usb_interface *interface)

{

struct
usb_skel *dev;

int
minor = interface->minor;

/*
prevent skel_open() from racing skel_disconnect( ) */ lock_kernel();

dev
= usb_get_intfdata(interface); usb_set_intfdata(interface, NULL);

/*
give back our minor */ usb_deregister_dev(interface, &skel_class);

unlock_kernel();
/* decrement our usage count */

kref_put(&dev->kref,
skel_delete);

info("USB
Skeleton #%d now disconnected", minor);

}

注意在之前代码片段中的调用
lock_kernel. 它获取了 bigkernel 锁, 以至于 disconnect 回调不会遇到一个竞争情况, 在使用 open 调用试图获取一个指向正确接口
数据结构的指针. 因为 open 在 bigkernel 锁获取情况下被调用, 如果 disconnect 也 获取同一个锁, 只有驱动的一部分可存取并且接着设置接口数据指针.

就在
disconnect 函数为一个 USB 设备被调用, 所有的当前在被传送的 urb 可被 USB 核心取消, 因此驱动不必明确为这些 urb 调用
usb_kill_urb. 如果一个驱动试图提交一 个 urb 给 USB 设备, 在调用 usb_submit_urb 被断开之后, 这个任务会失败, 错误值
为-EPIPE.

Linux 内核探测和去连接的细节的更多相关文章

  1. Linux 内核协议栈之TCP连接关闭

    Close行为: 当应用程序在调用close()函数关闭TCP连接时,Linux内核的默认行为是将套接口发送队列里的原有数据(比如之前残留的数据)以及新加入 的数据(比如函数close()产生的FIN ...

  2. Linux内核源代码的学习过程转换完成细节

    linux中的进程是个最主要的概念,进程从执行队列到開始执行有两个開始的地方, 一个就是switch_to宏中的标号1:"1:/t",//仅仅要不是新创建的进程,差点儿都是从上面的 ...

  3. Linux 内核sysfs 文件系统符号连接

    sysfs 文件系统有通常的树结构, 反映它代表的 kobjects 的层次组织. 但是内核中对象 间的关系常常比那个更加复杂. 例如, 一个 sysfs 子树 (/sys/devices )代表所有 ...

  4. Linux 内核注册一个 USB 驱动

    所有 USB 驱动必须创建的主要结构是 struct usb_driver. 这个结构必须被 USB 驱动填 充并且包含多个函数回调和变量, 来向 USB 核心代码描述 USB 驱动: struct ...

  5. 从Linux内核升级的必要性说开去

    Linux内核更新超级频繁,可是有必要时刻升级吗?个人感觉没有必要,可是你要时刻关注新特性列表,然后把自己的内核升级到离最新版本号差一两个月公布的版本号而不是最新版本号.以保证稳定性,由于一两个月的时 ...

  6. Tomcat 调优之从 Linux 内核源码层面看 Tcp backlog

    前两天看到一群里在讨论 Tomcat 参数调优,看到不止一个人说通过 accept-count 来配置线程池大小,我笑了笑,看来其实很多人并不太了解我们用的最多的 WebServer Tomcat,这 ...

  7. linux 内核协助的探测

    Linux 内核提供了一个低级设施来探测中断号. 它只为非共享中断, 但是大部分能够在共 享中断状态工作的硬件提供了更好的方法来尽量发现配置的中断号.这个设施包括 2 个函 数, 在<linux ...

  8. KSM剖析——Linux 内核中的内存去耦合

    简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通 ...

  9. 放开Linux内核对用户进程可打开文件数和TCP连接的限制

    一. 检查linux内核uname -alsb_release -a 二. 用户进程可打开文件数限制1) vim /etc/security/limits.conf*       -      nof ...

随机推荐

  1. BZOJ4241历史研究题解

    题目连接 很显然可以想到分块,用f[i][j]表示块i到块j的ans,然后发现答案一定是f[i][j] 或者其他在边角出现的数字 我们在记下g[i][j]从开头到块i中的数字j出现的次数 这样就每一次 ...

  2. css面试题总结(转)

    转自此网页http://www.cnblogs.com/YangqinCao/p/5721810.html. 1.两栏布局,左边栏宽度固定,适应父元素高度变化 首先分析两栏布局, 两栏布局两种常见方法 ...

  3. iOS 通知观察者的被调函数不一定运行在主线程

    Tony in iOS | 08/08/2013 iOS 通知观察者的被调函数不一定运行在主线程 今天修复Bug时候发现的一个小细节,记录下. 问题描述 事情是这样的:我在A视图(UITableVie ...

  4. 在 Linux 启动或重启时执行命令与脚本

    有时可能会需要在重启时或者每次系统启动时运行某些命令或者脚本.我们要怎样做呢?本文中我们就对此进行讨论. 我们会用两种方法来描述如何在 CentOS/RHEL 以及 Ubuntu 系统上做到重启或者系 ...

  5. django之请求方法

    Http1.0定义了三种请求方法:GET,POST和HEAD方法 Http1.1新增了五种请求方式:OPTIONS,PUT,DELETE,TRACE和CONNECT方法 ----get        ...

  6. 权重衰减(weight decay)与学习率衰减(learning rate decay)

    本文链接:https://blog.csdn.net/program_developer/article/details/80867468“微信公众号” 1. 权重衰减(weight decay)L2 ...

  7. C++笔记:面向对象编程(Handle类)

    句柄类 句柄类的出现是为了解决用户使用指针时须要控制指针的载入和释放的问题. 用指针訪问对象非常easy出现悬垂指针或者内存泄漏的问题. 为了解决这些问题,有很多方法能够使用,句柄类就是当中之中的一个 ...

  8. 2019-2-2-VisualStudio-扩展开发-添加菜单

    title author date CreateTime categories VisualStudio 扩展开发 添加菜单 lindexi 2019-02-02 15:35:18 +0800 201 ...

  9. Streamy 使用RDBMS

  10. oralce函数 next_day(d1[,c1])

    [功能]:返回日期d1在下周,星期几(参数c1)的日期 [参数]:d1日期型,c1为字符型(参数),c1默认为j(即当前日期) [参数表]:c1对应:星期一,星期二,星期三……星期日 [返回]:日期 ...