一、Usb 驱动关键结构体:
1、usb设备
1.1、usb_device
usb_device保存一个USB设备的信息,包括设备地址,设备描述符,配置描述符等。
每个usb设备是由一个usb_device对象来描述的。
struct usb_device {
int devnum; //usb设备在一条usb总线上的编号
char devpath[16]; //用于消息的设备ID字符串
u32 route; //用于xHCI的树拓扑十六进制字符串
enum usb_device_state state;//usb设备状态
enum usb_device_speed speed;//设备速度
unsigned int rx_lanes;//使用的rx线路数量
unsigned int tx_lanes;//使用的tx线路数量
enum usb_ssp_rate ssp_rate;// SuperSpeed + phy信令速率和车道数
struct usb_tt *tt; //如果一个高速设备里有这么一个TT,那么就可以连接低速/全速设备,如不然,那低速/全速设备没法用,只能连接到OHCI/UHCI那边出来的hub口里。
int ttport;
unsigned int toggle[2];
struct usb_device *parent; //父设备指针
struct usb_bus *bus; //设备所在总线
struct usb_host_endpoint ep0;//在创建usb_device时初始化端点0
struct device dev; //通用设备接口
struct usb_device_descriptor descriptor; //设备描述符
struct usb_host_bos *bos;
struct usb_host_config *config; //设备拥有的所有配置
struct usb_host_config *actconfig; //设备正在使用的配置
struct usb_host_endpoint *ep_in[16]; //输入端点
struct usb_host_endpoint *ep_out[16]; //输出端点
char **rawdescriptors; //配置的原始描述符
unsigned short bus_mA;
u8 portnum; //hub 端口号
u8 level; //usb设备树的级联级别
u8 devaddr;
unsigned can_submit : 1;
unsigned persist_enabled : 1;
unsigned have_langid : 1;
unsigned authorized : 1;
unsigned authenticated : 1;
unsigned wusb : 1;
unsigned lpm_capable : 1;
unsigned lpm_devinit_allow : 1;
unsigned usb2_hw_lpm_capable : 1;
unsigned usb2_hw_lpm_besl_capable : 1;
unsigned usb2_hw_lpm_enabled : 1;
unsigned usb2_hw_lpm_allowed : 1;
unsigned usb3_lpm_u1_enabled : 1;
unsigned usb3_lpm_u2_enabled : 1;
int string_langid;
/* static strings from the device */
char *product; //分别用来保存产品、厂商和序列号对应的字符串描述符信息
char *manufacturer;
char *serial;
struct list_head filelist;
int maxchild;
u32 quirks;
atomic_t urbnum;
unsigned long active_duration;
#ifdef CONFIG_PM
unsigned long connect_time;
unsigned do_remote_wakeup : 1;
unsigned reset_resume : 1;
unsigned port_is_suspended : 1;
#endif
struct wusb_dev *wusb_dev;
int slot_id;
struct usb2_lpm_parameters l1_params;
struct usb3_lpm_parameters u1_params;
struct usb3_lpm_parameters u2_params;
unsigned lpm_disable_count;
u16 hub_delay;
unsigned use_generic_driver : 1;
};
端点0:被称为控制端点,是一个数据双向传输的端点,其作用是对设备枚举和对设备进行一些基本的控 制功能,在usb_device对象创建时就需要被初始化。
1.2、usb_device_descriptor
保存usb设备描述符的结构体,通过usb_get_configuration函数创建并初始化。
struct usb_device_descriptor {
__u8 bLength; //本描述符的size
__u8 bDescriptorType; //描述符的类型,这里是设备描述符DEVICE
__le16 bcdUSB; //指明usb的版本,比如usb2.0
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
__u8 bMaxPacketSize0; //端点0对应的最大包大小
__le16 idVendor; //厂家ID
__le16 idProduct; //产品ID
__le16 bcdDevice; //设备出厂的编号
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber;
__u8 bNumConfigurations; //可能的配置数量
}
2、usb配置
2.1、usb_host_config
描述usb配置的结构体
struct usb_host_config {
struct usb_config_descriptor desc; //配置描述符
char *string; /* iConfiguration string, if present */
/* List of any Interface Association Descriptors in this
* configuration. */
struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
/* the interfaces associated with this configuration,
* stored in no particular order */
struct usb_interface *interface[USB_MAXINTERFACES]; //包含的接口
/* Interface information available even when this is not the
* active configuration */
struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
unsigned char *extra; /* Extra descriptors */
int extralen;
};
2.2、usb_config_descriptor
usb配置描述符
struct usb_config_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 wTotalLength;
__u8 bNumInterfaces;
__u8 bConfigurationValue;
__u8 iConfiguration;
__u8 bmAttributes;
__u8 bMaxPower;
} __attribute__ ((packed));
3、usb接口
每个usb接口对应一种设备功能,绑定一个usb驱动。
3.1、usb_interface
usb接口结构体
struct usb_interface {
/* array of alternate settings for this interface,
* stored in no particular order */
struct usb_host_interface *altsetting; /* 包含所有可用于该接口的可选设置的接口结构数组。每个 struct usb_host_interface 包含一套端点配置(即struct usb_host_endpoint结构所定义的端点配置。这些接口结构没有特别的顺序。*/
struct usb_host_interface *cur_altsetting; /* 指向altsetting内部的指针,表示当前激活的接口配置*/
unsigned num_altsetting; /* 可选设置的数量*/
/* If there is an interface association descriptor then it will list
* the associated interfaces */
struct usb_interface_assoc_descriptor *intf_assoc;
int minor; /* 如果绑定到这个接口的 USB 驱动使用 USB 主设备号, 这个变量包含由 USB 核心分配给接口的次设备号. 这只在一个成功的调用 usb_register_dev后才有效。*/
enum usb_interface_condition condition; /* state of binding */
unsigned sysfs_files_created : 1; /* the sysfs attributes exist */
unsigned ep_devs_created : 1; /* endpoint "devices" exist */
unsigned unregistering : 1; /* unregistration is in progress */
unsigned needs_remote_wakeup : 1; /* driver requires remote wakeup */
unsigned needs_altsetting0 : 1; /* switch to altsetting 0 is pending */
unsigned needs_binding : 1; /* needs delayed unbind/rebind */
unsigned resetting_device : 1; /* true: bandwidth alloc after reset */
unsigned authorized : 1; /* used for interface authorization */
struct device dev; /* interface specific device info */
struct device *usb_dev;
struct work_struct reset_ws; /* for resets in atomic context */
};
3.2、usb_host_interface
cur_altsetting成员的结构体是usb_host_interface,如下:
/* host-side wrapper for one interface setting's parsed descriptors */
struct usb_host_interface {
struct usb_interface_descriptor desc;//当前被激活的接口描述符
int extralen;
unsigned char *extra; /* 额外的描述符 */
/* array of desc.bNumEndpoints endpoints associated with this
* interface setting. these will be in no particular order.
*/
struct usb_host_endpoint *endpoint; /* 这个接口的所有端点结构体的联合数组*/
char *string;/* 接口描述字符串 */
};
3.3、usb_interface_descriptor
接口描述符结构体
struct usb_interface_descriptor {
__u8 bLength; //描述符的长度
__u8 bDescriptorType; //描述符类型的编号
__u8 bInterfaceNumber; //接口的编号
__u8 bAlternateSetting;
__u8 bNumEndpoints; //要使用的端点个数,表示有多少个端点描述符(不包括端点0)
__u8 bInterfaceClass; //接口的类型
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
} __attribute__((packed));
4、usb端点
USB 通讯的最基本形式是通过一个称为端点的东西。一个USB端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道。
一个 USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:
控制CONTROL
控 制端点被用来控制对 USB 设备的不同部分访问. 通常用作配置设备、获取设备信息、发送命令到设备或获取设备状态报告。这些端点通常较小。每个 USB 设备都有一个控制端点称为"端点 0", 被 USB 核心用来在插入时配置设备。USB协议保证总有足够的带宽留给控制端点传送数据到设备.
中断INTERRUPT
每当 USB 主机向设备请求数据时,中断端点以固定的速率传送小量的数据。此为USB 键盘和鼠标的主要的数据传送方法。它还用以传送数据到 USB 设备来控制设备。通常不用来传送大量数据。USB协议保证总有足够的带宽留给中断端点传送数据到设备.
批量BULK
批 量端点用以传送大量数据。这些端点常比中断端点大得多. 它们普遍用于不能有任何数据丢失的数据。USB 协议不保证传输在特定时间范围内完成。如果总线上没有足够的空间来发送整个BULK包,它被分为多个包进行传输。这些端点普遍用于打印机、USB Mass Storage和USB网络设备上。
等时ISOCHRONOUS
等时端点也批量传送大量数据, 但是这个数据不被保证能送达。这些端点用在可以处理数据丢失的设备中,并且更多依赖于保持持续的数据流。如音频和视频设备等等。
控制和批量端点用于异步数据传送,而中断和同步端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据,USB 核心为它们保留了相应的带宽。
4.1、usb_host_endpoint
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc; //端点描述符
struct usb_ss_ep_comp_descriptor ss_ep_comp; //超快速端点描述符
struct usb_ssp_isoc_ep_comp_descriptor ssp_isoc_ep_comp;
struct list_head urb_list; //本端口对应的urb链表
void *hcpriv;
struct ep_device *ep_dev; /* For sysfs info */
unsigned char *extra; /* Extra descriptors */
int extralen;
int enabled; //使能的话urb才能被提交到此端口
int streams;
};
4.2、usb_endpoint_descriptor
usb端点描述符
struct usb_endpoint_descriptor {
__u8 bLength; //描述符的长度
__u8 bDescriptorType;//描述符类型的编号
__u8 bEndpointAddress; //端点编号,比如端点1,就是1
__u8 bmAttributes; //端点的属性, 比如中断传输类型,输入类型
__le16 wMaxPacketSize; //一个端点的最大包大小,
__u8 bInterval; //间隔时间,用在中断传输上,比如间隔时间查询鼠标的数据
/* NOTE: 仅在音频端点中使用*/
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__((packed));
5、usb_driver
usb_driver保存客户驱动信息,包括驱动名称,以及驱动提供给USB内核使用的函数指针等。
USB接口驱动必须提供name,probe(),disconnect()和id_table。其他的可以自行配置。
//描述usb驱动的结构体
struct usb_driver {
const char *name; //独有的驱动名称
int (*probe)(struct usb_interface *intf,
const struct usb_device_id *id);
//驱动的probe函数,查看驱动是否能够管理设备上的一个独特的接口,如果能,probe返回0,如果不能,返回-ENODEV
void (*disconnect)(struct usb_interface *intf);
//如果接口不能被访问就调用这个函数,常常因为设备没有连接或者驱动模块没有加载。
int (*unlocked_ioctl)(struct usb_interface *intf, unsigned int code,void *buf);
//使设备暴露信息到用户空间
int (*suspend)(struct usb_interface *intf, pm_message_t message);
//系统禁用设备时使用
int (*resume)(struct usb_interface *intf);
//系统重新调用设备时使用
int (*reset_resume)(struct usb_interface *intf);
//挂起的设备被复位时调用
int (*pre_reset)(struct usb_interface *intf);
//当设备要复位时,被usb_reset_device()调用
int (*post_reset)(struct usb_interface *intf);
//设备复位后,被usb_reset_device()函数调用
const struct usb_device_id *id_table;
//描述了这个usb驱动所支持的usb设备列表,
//USB驱动使用ID table支持热插拔。输出参数到MODULE_DEVICE_TABLE().这是必须配置的,除非你的驱动probe函数永远不会调用
const struct attribute_group **dev_groups;
struct usb_dynids dynids;
//用来保存设备id的链表
struct usbdrv_wrap drvwrap;
//驱动模型核心结构体的封装
unsigned int no_dynamic_id : 1;
//配置成1,USB核心将不会允许动态id被加入到驱动(通过阻止系统文件的创建)
unsigned int supports_autosuspend : 1;
//配置成1,USB核心允许自动挂起驱动
unsigned int disable_hub_initiated_lpm : 1;
unsigned int soft_unbind : 1;
};
const struct usb_device_id *id_table: 它指向一个usb_device_id数组,,usb_device_id结构体包含有USB设备的制造商ID,产品ID,产品版本,设备类,接口类等信息及其要匹配标志成员match_flags。当usb核心检测到某个设备的属性和id_table中的所携带的信息一致时,这个驱动程序的probe函数就会被执行。
6、URB(Universal Request Block)
USB请求块(URB)是USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构,USB设备中的每个端点都处理一个URB队列。
urb 以一种异步的方式同一个特定USB设备的特定端点发送或接受数据。一个 USB 设备驱动可根据驱动的需要,分配多个 urb 给一个端点或重用单个 urb 给多个不同的端点。设备中的每个端点都处理一个 urb 队列, 所以多个 urb 可在队列清空之前被发送到相同的端点。
struct urb {
/* private: usb core and host controller only fields in the urb */
struct kref kref; /*URB 引用计数 */
int unlinked; /* unlink error code */
void *hcpriv; /* host控制器的私有数据 */
atomic_t use_count; /* 当前提交计数 */
atomic_t reject; /* 提交失败计数 */
/* public: documented fields in the urb that can be used by drivers */
struct list_head urb_list; /* list head for use by the urb's
* current owner */
struct list_head anchor_list; /* the URB may be anchored */
struct usb_anchor *anchor;
struct usb_device *dev; /* 指向这个 urb 要发送的目标 struct usb_device 的指针,这个变量必须在这个 urb 被发送到 USB 核心之前被 USB 驱动初始化 */
struct usb_host_endpoint *ep; /* (internal) 指向端点的指针 */
unsigned int pipe; /* 这个 urb 所要发送到的特定struct usb_device的端点消息 */
unsigned int stream_id; /* (in) stream ID */
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* 传输设置(in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区指针。为了主机控制器驱动正确访问这个缓冲, 它必须使用 kmalloc 调用来创建, 不是在堆栈或者静态内存中。 对控制端点, 这个缓冲区用于数据中转 */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
struct scatterlist *sg; /* (in) scatter gather buffer list */
int num_mapped_sgs; /* (internal) mapped sg entries */
int num_sgs; /* (in) number of entries in the sg list */
u32 transfer_buffer_length; /* 指向的缓冲区大小。如果这是 0, 传送缓冲没有被 USB 核心所使用 */
u32 actual_length; /* 当这个 urb 完成后, 该变量被设置为这个 urb (对于 OUT urb)发送或(对于 IN urb)接受数据的真实长度.对于 IN urb, 必须是用此变量而非 transfer_buffer_length , 因为接收的数据可能比整个缓冲小 */
unsigned char *setup_packet; /* 指向控制urb的设置数据包指针 */
dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
int start_frame; /* (modify) start frame (ISO) */
int number_of_packets; /* (in) number of ISO packets */
int interval; /* (modify) transfer interval
* (INT/ISO) */
int error_count; /* (return) number of ISO errors */
void *context; /* (in) context for completion */
usb_complete_t complete; /* 结束处理例程函数指针, 当 urb 被完全传送或发生错误,它将被 USB 核心调用. 此函数检查这个 urb, 并决定释放它或重新提交给另一个传输中 */
struct usb_iso_packet_descriptor iso_frame_desc[];/* (in) ISO ONLY */
};
struct urb结构不能静态构建,必须使用usb_alloc_urb函数构建,使用usb_free_urb函数释放。
//* @iso_packets:该urb的iso包数
//* @mem_flags:要分配的内存类型
//* 如果成功,该函数返回一个URB结构体指针,否则返回0.
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
{
struct urb *urb;
urb = kmalloc(struct_size(urb, iso_frame_desc, iso_packets),
mem_flags);
if (!urb)
return NULL;
usb_init_urb(urb);
return urb;
}
void usb_free_urb(struct urb *urb);
usb client driver---->usb core---->usb host controller---->usb设备(响应后原路返回)
一个urb的生命循环如下:
(1)被创建;
(2)被分配给一个特定 USB 设备的特定端点;
(3)被提交给 USB 核心;
(4)被 USB 核心提交给特定设备的特定 USB 主机控制器驱动;
(5)被 USB 主机控制器驱动处理, 并传送到设备;
(6)以上操作完成后,USB主机控制器驱动通知 USB 设备驱动。
urb 也可被提交它的驱动在任何时间取消;如果设备被移除,urb 可以被USB核心取消。
二、usb设备启动流程
三、usb驱动启动流程函数分析
- hub_port_connect
static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
u16 portchange)
{
...
udev = usb_alloc_dev(hdev, hdev->bus, port1); //(1)注册一个usb_device,然后会放在usb总线上
choose_devnum(udev); //(2)给新的设备分配一个地址编号
status = hub_port_init(hub, udev, port1, i); //(3)初始化端口,与USB设备建立连接
status = usb_new_device(udev); //(4)创建USB设备,与USB设备驱动连接
...
}
- usb_alloc_dev:创建并初始化usb_device结构体
struct usb_device *
usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
{
struct usb_device *dev; //分配usb_device
...
dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配内存
... ...
device_initialize(&dev->dev); //初始化
dev->dev.bus = &usb_bus_type; //设置bus成员
dev->dev.type = &usb_device_type; //设置device成员
... ...
return dev; //返回usb_device
-
choose_devnum:选择设备编号,在devnum_next和128之间寻找一个空设备编号
设备号被用作usbfs中的文件名,对于USB-2.0总线,它们也被用作设备地址,但在USB-3.0总线地址控制器硬件分配,通常与设备号不相同。
static void choose_devnum(struct usb_device *udev)
{
int devnum;
struct usb_bus *bus = udev->bus;
/* be safe when more hub events are proceed in parallel */
mutex_lock(&bus->devnum_next_mutex);
if (udev->wusb) {
devnum = udev->portnum + 1;
BUG_ON(test_bit(devnum, bus->devmap.devicemap));
} else {
/* Try to allocate the next devnum beginning at
* bus->devnum_next. */
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
bus->devnum_next); //在bus->devnum_next~128区间中,循环查找下一个非0(没有设备)的编号
if (devnum >= 128) //若编号大于等于128,说明没有找到空余的地址编号,从头开始找
devnum = find_next_zero_bit(bus->devmap.devicemap,
128, 1);
bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1); //设置下次寻址的区间+1
}
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap); //设置位
udev->devnum = devnum;
}
mutex_unlock(&bus->devnum_next_mutex);
}
- hub_port_init:设置地址,获取设备描述符
//重置设备,(重新)分配地址,获取设备描述符。
//返回USB_STATE_ADDRESS中的设备,错误除外。
//如果调用的是已经存在的设备(作为usb_reset_and_verify_device),调用者必须拥有设备锁和端口锁。
//对于无法访问的新检测到的设备,通过任何全局指针,它不需要锁定设备,但仍然需要锁定端口。
static int
hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
int retry_counter)
{
... ...
for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
retval = hub_set_address(udev, devnum); //设置地址,告诉USB设备新的地址编号
if (retval >= 0)
break;
msleep(200);
}
... ...
retval = usb_get_device_descriptor(udev, 8); //获得USB设备描述符前8个字节
... ...
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); //重新获取设备描述符信息
}
- usb_new_device:获取usb设备配置,将新创建的设备添加进dev链表中,寻找与之匹配的驱动
int usb_new_device(struct usb_device *udev)
{
err = usb_enumerate_device(udev); /* 获取设备的描述符(枚举) */
... ...
err = device_add(&udev->dev); /*将设备添加进dev链表中,并为其匹配相应的驱动*/
}
usb_enumerate_device(udev):内部调用usb_get_configuration函数创建并填充一个usb_config_descriptor类型的结构体。usb_enumerate_device函数使用该结构体中的数据填充usb_device中的相关字段。
device_add:将设备添加到对应的bus的dev链表中,并调用bus_probe_device为设备寻找相应的驱动
int device_add(struct device *dev)
{
... ...
error = bus_add_device(dev); /*将设备添加到对应的bus设备列表中*/
... ...
bus_probe_device(dev); /*为设备匹配相应的驱动*/
... ...
}
如果device_add调用成功,在移除设备时应该调用device_del函数。
如果device_add调用失败,则使用put_device函数删除引用计数
/**
* bus_probe_device - probe drivers for a new device
* @dev: device to probe
*
* - Automatically probe for a driver if the bus allows it.
*/
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
struct subsys_interface *sif;
if (!bus)
return;
if (bus->p->drivers_autoprobe)
device_initial_probe(dev); //调用__device_attach
mutex_lock(&bus->p->mutex);
list_for_each_entry(sif, &bus->p->interfaces, node)
if (sif->add_dev)
sif->add_dev(dev, sif);
mutex_unlock(&bus->p->mutex);
}
void device_initial_probe(struct device *dev)
{
__device_attach(dev, true);
}
static int __device_attach(struct device *dev, bool allow_async)
{
... ...
if (dev->p->dead) {
goto out_unlock;
} else if (dev->driver) {
if (device_is_bound(dev)) {
ret = 1;
goto out_unlock;
}
ret = device_bind_driver(dev); //如果dev->driver存在,则将设备与驱动绑定
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
struct device_attach_data data = {
.dev = dev,
.check_async = allow_async,
.want_async = false,
};
if (dev->parent)
pm_runtime_get_sync(dev->parent);
ret = bus_for_each_drv(dev->bus, NULL, &data,
__device_attach_driver); //遍历总线上的driver并调用__device_attach_driver
... ...
}
int device_bind_driver(struct device *dev)
{
int ret;
ret = driver_sysfs_add(dev);
if (!ret) {
device_links_force_bind(dev);
driver_bound(dev);
}
else if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
return ret;
}
__device_attach_driver:成功返回一个小于零的数,失败则返回0.
static int __device_attach_driver(struct device_driver *drv, void *_data)
{
... ...
/*判断driver和device是否匹配*/
ret = driver_match_device(drv, dev);
... ...
/*若匹配,则调用driver_probe_device来probe driver*/
ret = driver_probe_device(drv, dev);
... ...
}
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
/*bus->match:platform bus的match函数---platform_match*/
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
static int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int trigger_count = atomic_read(&deferred_trigger_count);
int ret;
atomic_inc(&probe_count);
ret = __driver_probe_device(drv, dev); //调用really_probe函数
if (ret == -EPROBE_DEFER || ret == EPROBE_DEFER) {
driver_deferred_probe_add(dev);
/*
* Did a trigger occur while probing? Need to re-trigger if yes
*/
if (trigger_count != atomic_read(&deferred_trigger_count) &&
!defer_all_probes)
driver_deferred_probe_trigger();
}
atomic_dec(&probe_count);
wake_up_all(&probe_waitqueue);
return ret;
}
static int __driver_probe_device(struct device_driver *drv, struct device *dev)
{
... ...
//具体执行probe
ret = really_probe(dev, drv);
... ...
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
... ...
/*对于挂载在PCI/PCIe总线上的设备来说,在调用call_driver_probe 的时候就会通过ret = drv->probe(dev)调用到对应的设备驱动的probe函数。*/
ret = call_driver_probe(dev, drv);
... ...
driver_bound(dev); //实际将device加入驱动的设备链表
... ...
}
static int call_driver_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
/*调用驱动总线所属的probe函数*/
if (dev->bus->probe)
ret = dev->bus->probe(dev);
/*调用驱动中的probe函数*/
else if (drv->probe)
ret = drv->probe(dev);
switch (ret) {
case 0:
break;
case -EPROBE_DEFER:
/* Driver requested deferred probing */
dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name);
break;
case -ENODEV:
case -ENXIO:
pr_debug("%s: probe of %s rejects match %d\n",
drv->name, dev_name(dev), ret);
break;
default:
/* driver matched but the probe failed */
pr_warn("%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
break;
}
return ret;
}