在struct usb_driver中,.probe和.disconnect的原型如下:
int (*probe) (struct usb_interface *intf,const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
usb_device_id就不用多说了,看看struct usb_interface
/**
* struct usb_interface - what usb device drivers talk to
* @altsetting: array of interface structures, one for each alternate
* setting that may be selected.Each one includes a set of
* endpoint configurations.They will be in no particular order.可选的设置列表
* @cur_altsetting: the current altsetting.当前设置
* @num_altsetting: number of altsettings defined.接口具有可选设置的数量
* @intf_assoc: interface association descriptor接口关联的描述符
* @minor: the minor number assigned to this interface, if this次设备号
*interface is bound to a driver that uses the USB major number.
*If this interface does not use the USB major, this field should
*be unused.The driver should set this value in the probe()
*function of the driver, after it has been assigned a minor
*number from the USB core by calling usb_register_dev().
* @condition: binding state of the interface: not bound, binding接口绑定状态
*(in probe()), bound to a driver, or unbinding (in disconnect())
* @is_active: flag set when the interface is bound and not suspended.
* @sysfs_files_created: sysfs attributes exist
* @needs_remote_wakeup: flag set when the driver requires remote-wakeup
*capability during autosuspend.
* @needs_altsetting0: flag set when a set-interface request for altsetting 0
*has been deferred.
* @needs_binding: flag set when the driver should be re-probed or unbound
*following a reset or suspend operation it doesn't support.
* @dev: driver model's view of this device
* @usb_dev: if an interface is bound to the USB major, this will point
*to the sysfs representation for that device.
* @pm_usage_cnt: PM usage counter for this interface; autosuspend is not
*allowed unless the counter is 0.
*
* USB device drivers attach to interfaces on a physical device.Each
* interface encapsulates a single high level function, such as feeding
* an audio stream to a speaker or reporting a change in a volume control.
* Many USB devices only have one interface.The protocol used to talk to
* an interface's endpoints can be defined in a usb "class" specification,
* or by a product's vendor.The (default) control endpoint is part of
* every interface, but is never listed among the interface's descriptors.
*
* The driver that is bound to the interface can use standard driver model
* calls such as dev_get_drvdata() on the dev member of this structure.
*
* Each interface may have alternate settings.The initial configuration
* of a device sets altsetting 0, but the device driver can change
* that setting using usb_set_interface().Alternate settings are often
* used to control the use of periodic endpoints, such as by having
* different endpoints use different amounts of reserved USB bandwidth.
* All standards-conformant USB devices that use isochronous endpoints
* will use them in non-default settings.
*
* The USB specification says that alternate setting numbers must run from
* 0 to one less than the total number of alternate settings.But some
* devices manage to mess this up, and the structures aren't necessarily
* stored in numerical order anyhow.Use usb_altnum_to_altsetting() to
* look up an alternate setting in the altsetting array based on its number.
*/
struct usb_interface {
/* array of alternate settings for this interface,
* stored in no particular order */
struct usb_host_interface *altsetting;
struct usb_host_interface *cur_altsetting;/* the currently
* active alternate setting */
unsigned num_altsetting;/* number of alternate settings */
/* If there is an interface association descriptor then it will list
* the associated interfaces */
struct usb_interface_assoc_descriptor *intf_assoc;
int minor;/* minor number this interface is
* bound to */
enum usb_interface_condition condition;/* state of binding */
unsigned is_active:1;/* the interface is not suspended */
unsigned sysfs_files_created:1;/* the sysfs attributes exist */
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 */
struct device dev;/* interface specific device info */
struct device *usb_dev;
int pm_usage_cnt;/* usage counter for autosuspend */
};
#defineto_usb_interface(d) container_of(d, struct usb_interface, dev)
#defineinterface_to_usbdev(intf) \
container_of(intf->dev.parent, struct usb_device, dev)
对于USB设备驱动程序编写者来说,更为关键的是:intetface和endpoint。一个设备可以有几种配置,一个配置可以有几种接口,一个接口就对应一个USB设备驱动程序,因此写驱动程序时,直接面对的是接口,而不是设备。
接下来就开始探测函数了,如下:
static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_skel *dev;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
size_t buffer_size;
int i;
int retval = -ENOMEM;
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
err("Out of memory");
goto error;
}
kref_init(&dev->kref);
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
mutex_init(&dev->io_mutex);
spin_lock_init(&dev->err_lock);
init_usb_anchor(&dev->submitted);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
/* 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 &&
usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
buffer_size = le16_to_cpu(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 &&
usb_endpoint_is_bulk_out(endpoint)) {
/* 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;
}
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
/* 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;
}
/* let the user know what node this device is now attached to */
info("USB Skeleton device now attached to USBSkel-%d", interface->minor);
return 0;
error:
if (dev)
/* this frees allocated memory */
kref_put(&dev->kref, skel_delete);
return retval;
}
行数比较少,每次在Linux里面看到这么小的函数时,我都兴奋不已。还没高兴过来呢,就看到了一个新结构体struct usb_skel *dev;看看它的定义吧
/* Structure to hold all of our device specific stuff */
struct usb_skel {
struct usb_device*udev;/* the usb device for this device */
struct usb_interface*interface;/* the interface for this device */
unsigned char*bulk_in_buffer;/* the buffer to receive data */
size_tbulk_in_size;/* the size of the receive buffer */
__u8bulk_in_endpointAddr;/* the address of the bulk in endpoint */
__u8bulk_out_endpointAddr;/* the address of the bulk out endpoint */
interrors;/* the last request tanked */
intopen_count;/* count the number of openers */
spinlock_terr_lock;/* lock for errors */
struct krefkref;
struct mutexio_mutex;/* synchronize I/O with disconnect */
};
当在linux里面见过那么多庞大的数据结构之后,看到这个结构体,又禁不住高兴起来。还是直接看程序吧。
dev = kzalloc(sizeof(*dev), GFP_KERNEL);//申请内存并初始化为0
kref_init(&dev->kref); //设置原子变量kref->refcount为1
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);//初始化信号量
mutex_init(&dev->io_mutex);//初始化互斥体
spin_lock_init(&dev->err_lock);//初始化自旋锁
init_usb_anchor(&dev->submitted);
看下init_usb_anchor定义
struct usb_anchor {
struct list_head urb_list;
wait_queue_head_t wait;
spinlock_t lock;
};
static inline void init_usb_anchor(struct usb_anchor *anchor)
{
INIT_LIST_HEAD(&anchor->urb_list); //初始化urb_list链表
init_waitqueue_head(&anchor->wait); //初始化等待队列头
spin_lock_init(&anchor->lock);//初始化自旋锁
}
回到probe函数
dev->udev = usb_get_dev(interface_to_usbdev(interface));//usb dev引用计数加1
dev->interface = interface;// probe函数传进来的interface指针赋给usb_skel的dev
/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
iface_desc = interface->cur_altsetting;
到这里,就有必要介绍下结构体struct usb_host_interface,顺便介绍struct usb_host_endpoint
/**
* struct usb_host_endpoint - host-side endpoint descriptor and queueUSB主机端端点描述符和队列
* @desc: descriptor for this endpoint, wMaxPacketSize in native byteorder端点描述符
* @urb_list: urbs queued to this endpoint; maintained by usbcore该端点的urb_list队列,
* @hcpriv: for use by HCD; typically holds hardware dma queue head (QH)
*with one or more transfer descriptors (TDs) per urb
* @ep_dev: ep_device for sysfs info
* @extra: descriptors following this endpoint in the configuration
* @extralen: how many bytes of "extra" are valid
* @enabled: URBs may be submitted to this endpoint
*
* USB requests are always queued to a given endpoint, identified by a
* descriptor within an active interface in a given USB configuration.
*/
{
struct usb_endpoint_descriptordesc;
struct list_headurb_list;
void*hcpriv;
struct ep_device *ep_dev;/* For sysfs info */
unsigned char *extra;/* Extra descriptors */
int extralen;
int enabled;
};
/* host-side wrapper for one interface setting's parsed descriptors */
struct usb_host_interface {
struct usb_interface_descriptordesc;
/* 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 */
unsigned char *extra;/* Extra descriptors */
int extralen;
};
iface_desc = interface->cur_altsetting;//就是把当前的设置赋给主机接口端
回到probe函数,继续
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {//循环所有端点
endpoint = &iface_desc->endpoint[i].desc; //端点描述符复制
if (!dev->bulk_in_endpointAddr &&//如果in端点描述符地址非0
usb_endpoint_is_bulk_in(endpoint)) { //并且in端点确实存在
/* we found a bulk in endpoint */ //赋值相关并申请接收内存
buffer_size = le16_to_cpu(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 &&
usb_endpoint_is_bulk_out(endpoint)) {
/* 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;
}
只有in端点和out端点都存在,才会继续下去,否则返回失败。
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev); //储存为接口的私有数据
/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &skel_class);//注册usb设备
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(),与结构体skel_class了。
usb_register_dev的原型如下
/**
* usb_register_dev - register a USB device, and ask for a minor number
* @intf: pointer to the usb_interface that is being registered
* @class_driver: pointer to the usb_class_driver for this device
*
* This should be called by all USB drivers that use the USB major number.
* If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be
* dynamically allocated out of the list of available ones.If it is not
* enabled, the minor number will be based on the next available free minor,
* starting at the class_driver->minor_base.
*
* This function also creates a usb class device in the sysfs tree.
*
* usb_deregister_dev() must be called when the driver is done with
* the minor numbers given out by this function.
*
* Returns -EINVAL if something bad happens with trying to register a
* device, and 0 on success.
*/
int usb_register_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int retval = -EINVAL;
int minor_base = class_driver->minor_base;
int minor = 0;
char name[20];
char *temp;
#ifdef CONFIG_USB_DYNAMIC_MINORS
/*
* We don't care what the device tries to start at, we want to start
* at zero to pack the devices into the smallest available space with
* no holes in the minor range.
*/
minor_base = 0;
#endif
intf->minor = -1;
dbg ("looking for a minor, starting at %d", minor_base);
if (class_driver->fops == NULL)
goto exit;
down_write(&minor_rwsem);
for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) {
if (usb_minors[minor])
continue;
usb_minors[minor] = class_driver->fops;
retval = 0;
break;
}
up_write(&minor_rwsem);
if (retval)
goto exit;
retval = init_usb_class();
if (retval)
goto exit;
intf->minor = minor;
/* create a usb class device for this usb interface */
snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
temp = strrchr(name, '/');
if (temp && (temp[1] != '\0'))
++temp;
else
temp = name;
intf->usb_dev = device_create_drvdata(usb_class->class, &intf->dev,
MKDEV(USB_MAJOR, minor), NULL,
"%s", temp);
if (IS_ERR(intf->usb_dev)) {
down_write(&minor_rwsem);
usb_minors[intf->minor] = NULL;
up_write(&minor_rwsem);
retval = PTR_ERR(intf->usb_dev);
}
exit:
return retval;
}
这段函数比较简单,主要就是申请一个次设备号,然后初始化usb类,为usb接口创建一个usb类设备,注册并创建设备.
基本上,probe函数就讲解完了,总结下,就是根据usb_interface的成员寻找第一个批量输入和输出点,将端点地址、缓存区信息存入以usb骨架程序定义的usb_skel结构体,并将usb_skel实例的指针传入usb_set_intfdata()作为USB接口的私有数据,最后,它会注册并创建USB设备。
接下来,就轮到disconnect函数了,如下
static void skel_disconnect(struct usb_interface *interface)
{
struct usb_skel *dev;
int minor = interface->minor;
dev = usb_get_intfdata(interface); //得到私有数据接口指针
usb_set_intfdata(interface, NULL); //将传进来的interface私有数据设置为空
/* give back our minor */
usb_deregister_dev(interface, &skel_class); //释放次设备号,并注销设备
/* prevent more I/O from starting */
mutex_lock(&dev->io_mutex);
dev->interface = NULL;//将usb_skel的接口数据设置为空
mutex_unlock(&dev->io_mutex);
usb_kill_anchored_urbs(&dev->submitted); //释放urb相关
/* decrement our usage count */
kref_put(&dev->kref, skel_delete);//计数减1,并释放相关内存资源
info("USB Skeleton #%d now disconnected", minor);
}