linux usb-skeleton,Linux USB驱动程序(3)----usb-skeleton.c分析

在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);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值