Chinaunix首页 | 论坛 | 博客
  • 博客访问: 559268
  • 博文数量: 105
  • 博客积分: 3274
  • 博客等级: 中校
  • 技术积分: 1161
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-21 12:14
文章分类

全部博文(105)

文章存档

2011年(1)

2010年(104)

分类: LINUX

2010-09-02 08:27:37

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 */

};

#define    to_usb_interface(d) container_of(d, struct usb_interface, dev)

#define    interface_to_usbdev(intf) \

       container_of(intf->dev.parent, struct usb_device, dev)

对于USB设备驱动程序编写者来说,更为关键的是:intetfaceendpoint。一个设备可以有几种配置,一个配置可以有几种接口,一个接口就对应一个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_t                    bulk_in_size;         /* the size of the receive buffer */

       __u8                     bulk_in_endpointAddr;  /* the address of the bulk in endpoint */

       __u8                     bulk_out_endpointAddr;       /* the address of the bulk out endpoint */

       int                 errors;                   /* the last request tanked */

       int                 open_count;           /* count the number of openers */

       spinlock_t              err_lock;        /* lock for errors */

       struct kref              kref;

       struct mutex           io_mutex;              /* synchronize I/O with disconnect */

};

当在linux里面见过那么多庞大的数据结构之后,看到这个结构体,又禁不住高兴起来。还是直接看程序吧。

dev = kzalloc(sizeof(*dev), GFP_KERNEL);  // 申请内存并初始化为0

kref_init(&dev->kref); //设置原子变量kref->refcount1

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 queue  USB主机端端点描述符和队列

 * @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_descriptor      desc;

       struct list_head              urb_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_descriptor      desc;

 

       /* 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);

}

阅读(3612) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2011-01-06 20:04:09

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com