linux下usb驱动配置文件,Linux USB驱动框架分析(2)

19:dev->udev = usb_get_dev(interface_to_usbdev(interface));

20:dev->interface = interface;

在17行,我们对usb_skel中的kref结构体进行初始化,设置这个引用计数器的值,这个值用来说明对模块的引用次数,初始化函数如下:

点击(此处)折叠或打开

/***

* kref_init - initialize object.

* @kref: object in question.

*/

void kref_init(struct kref *kref)

{

atomic_set(&kref->refcount,1);

smp_mb();

}

这个函数不仅用来初始化kref,而且用原子的方式设置refcount的值为1。所以在出错处理代码中有kref_put,它把kref的计数减1,如果kref计数已经为0,那么kref会被释放,kref的释放会在下面分析。

在初始化了一些资源之后,可以看到第一个关键的函数调用——interface_to_usbdev。他同uo一个usb_interface来得到该接口所在设备的设备描述结构。本来,要得到一个usb_device只要用interface_to_usbdev就够了,但因为要增加对该usb_device的引用计数,我们应该在做一个usb_get_dev的操作,来增加引用计数,并在释放设备时用usb_put_dev来减少引用计数。

其中对interface_to_usbdev(interface)函数的定义如下:

点击(此处)折叠或打开

#define    interface_to_usbdev(intf) \

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

这个函数是通过将usb接口的引用计数,通过container函数,指向usb_devices结构。对于container_of函数,参见http://blog.csdn.net/yinkaizhong/article/details/4093795

21-55:对我们自定义的usb_skel各个状态跟资源作初始化。这部分工作的任务主要是向usb_skel注册该usb设备的端点。这里可能要补充以下一些关于usb_interface_descriptor的知识,但因为内核源码对该结构体的注释不多,所以只能靠个人猜测。在一个usb_host_interface结构里面有一个usb_interface_descriptor叫做desc的成员,他应该是用于描述该interface的一些属性,其中bNumEndpoints是一个8位(b for byte)的数字,他代表了该接口的端点数。probe然后遍历所有的端点,检查他们的类型跟方向,注册到usb_skel中。

56-58:向系统注册一些以后会用的的信息。首先我们来说明一下usb_set_intfdata(),他向内核注册一个data,这个data的结构可以是任意的,这段程序向内核注册了一个usb_skel结构。就是我们刚刚看到的被初始化的那个。之所以要把他注册,是因为我们定义的usb_skel结构不是全局变量,其他的函数需要使用的时候,可以后用usb_get_intfdata来得到。我们可以再内核源码中找到对它的定义:(/linux/usb.h)

点击(此处)折叠或打开

static inline void usb_set_intfdata(struct usb_interface *intf, void *data)

{

dev_set_drvdata(&intf->dev, data);

}

这个内联函数又调用了dev_set_drvdata(&intf->dev, data)

我们接着找这个函数的定义:()

点击(此处)折叠或打开

static inline unsigned int dev_set_drvdata(struct device *dev, void *data)

{

dev->driver_data = data;;

}这下我们就明白,我们是把dev的设备信息保存到了interface->dev->driver_data中。在以后使用的时候,只需要调用usb_get_intfdata就可以得到dev的信息。

59-67:我们向这个interface注册一个skel_class结构。这个结构又是什么?我们就来看看这到底是个什么东西:

点击(此处)折叠或打开

static struct usb_class_driver skel_class = {

.name = "usb/skel%d",

.fops = &skel_fops,

.mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,

.minor_base = USB_SKEL_MINOR_BASE,

};

它其实是一个系统定义的结构,里面包含了一名字、一个文件操作结构体还有一个次设备号的基准值。事实上它才是定义真正完成对设备IO操作的函数。所以他的核心内容应该是skel_fops。其原形如下:

点击(此处)折叠或打开

static struct file_operations skel_fops = {

.owner =    THIS_MODULE,

.read =        skel_read,

.write =    skel_write,

.open =        skel_open,

.release =    skel_release,

};

这是我们比较常见的file_operations函数。这个文件操作结构中定义了对设备的读写、打开、释放(USB设备通常使用这个术语release)。他们都是函数指针,分别指向skel_read、skel_write、skel_open、skel_release这四个函数,这四个函数应该有开发人员自己实现。

prode函数最后部分是对错误的相关处理。此时我们看到了:

kref_put(&dev->kref, skel_delete);它把kref的计数减1,如果kref计数已经为0,那么kref会被释放。kref_put的第二个参数是一个函数指针,指向一个清理函数。注意,该指针不能为空,或者kfree。该函数会在最后一个对kref的引用释放时被调用(如果我的理解不准确,请指正)。下面是内核源码中的一段注释及代码:

点击(此处)折叠或打开

/**

* kref_put - decrement refcount for object.

* @kref: object.

* @release: pointer to the function that will clean up the object when the

* last reference to the object is released.

* This pointer is required, and it is not acceptable to pass kfree

* in as this function.

*

* Decrement the refcount, and if 0, call release().

* Return 1 if the object was removed, otherwise return 0. Beware, if this

* function returns 0, you still can not count on the kref from remaining in

* memory. Only use the return value if you want to see if the kref is now

* gone, not present.

*/

int kref_put(struct kref *kref, void (*release)(struct kref *kref))

{

WARN_ON(release == NULL);

WARN_ON(release == (void (*)(struct kref *))kfree);

/*

* if current count is one, we are the last user and can release object

* right now, avoiding an atomic operation on 'refcount'

*/

if ((atomic_read(&kref->refcount) == 1) ||

(atomic_dec_and_test(&kref->refcount))) {

release(kref);

return 1;

}

return 0;

}当我们执行打开操作时,我们要增加kref的计数,我们可以用kref_get,来完成。所有对struct kref的操作都有内核代码确保其原子性。

(2)我们继续来看skel_disconnect函数:

点击(此处)折叠或打开

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

}当设备被拔出集线器时,usb子系统会自动地调用disconnect,他做的事情不多,最重要的是注销class_driver(交还次设备号)和interface的data:

dev = usb_get_intfdata(interface);

usb_set_intfdata(interface, NULL);

/* give back our minor */

usb_deregister_dev(interface, &skel_class);

然后他会用kref_put(&dev->kref, skel_delete)进行清理,kref_put的细节参见前文。

到目前为止,我们已经分析完usb子系统要求的各个主要操作,下一部分我们在讨论一下对USB设备的IO操作。

接下来我们进行IO操作的分析。

(1)open函数:

点击(此处)折叠或打开

static int skel_open(struct inode *inode, struct file *file)

{

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); //获取注册到接口的usb——skel数据。

if (!dev) {

retval = -ENODEV;

goto exit;

}

/* increment our usage count for the device */

kref_get(&dev->kref); //对usb_skel模块的使用计数

/* save our object in the file's private structure */

file->private_data = dev; //保存usb_skel结构的数据以便read,write函数使用

exit:

return retval;

}open函数很简单。主要是递增usb_skel的kref,并把该结构体存入file的private_data中,以便其他函数(如read、write)调用。

在open函数中有个函数:

interface = usb_find_interface(&skel_driver, subminor);

我们在/linux/usb.c中找到它的定义,在/driver/usb/core/usb.c中找到对它的源码:

点击(此处)折叠或打开

/**

* usb_find_interface - find usb_interface pointer for driver and device

* @drv: the driver whose current configuration is considered

* @minor: the minor number of the desired device

*

* This walks the bus device list and returns a pointer to the interface

* with the matching minor and driver. Note, this only works for devices

* that share the USB major number.

*/

struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)

{

struct find_interface_arg argb;

struct device *dev;

argb.minor = minor;

argb.drv = &drv->drvwrap.driver;

dev = bus_find_device(&usb_bus_type, NULL, &argb, __find_interface);

/* Drop reference count from bus_find_device */

put_device(dev);

return dev ? to_usb_interface(dev) : NULL;

}

EXPORT_SYMBOL_GPL(usb_find_interface);由于在open函数中,无法得到在prode函数中注册的设备的详细信息,而我们只能得到注册到接口的次设备号(通过subminor = iminor(inode);)以及我们注册的驱动skel_driver。

通过这个函数,遍历总线设备列表,查找和我们的驱动与次设备号匹配的interface指针。

我们定义了find_interface_arg的结构argb,这个结构如下:

点击(此处)折叠或打开

struct find_interface_arg {

int minor;

struct device_driver *drv;

};

我们把传入参数的相关内容传递整合到这个结构体中,如15,16所示。18行就是通过迭代查找设备,寻找我们需要的那个接口设备。

(2)release函数

点击(此处)折叠或打开

static int skel_release(struct inode *inode, struct file *file)

{

struct usb_skel *dev;

dev = (struct usb_skel *)file->private_data;

if (dev == NULL)

return -ENODEV;

/* decrement the count on our device */

kref_put(&dev->kref, skel_delete);

return 0;

}

release函数做一些清理工作,主要是把对设备的引用计数减1,如果等于零的话,调用skel_delete函数。

点击(此处)折叠或打开

#define to_skel_dev(d) container_of(d, struct usb_skel, kref)

static struct usb_driver skel_driver;

static void skel_delete(struct kref *kref)

{

struct usb_skel *dev = to_skel_dev(kref);

usb_put_dev(dev->udev);

kfree (dev->bulk_in_buffer);

kfree (dev);

}5-12行,当引用计数的值(kref->recount)为0值,就会调用这个skel_delete函数,做相应的清理工作。

(3)write函数

说到usb子系统的IO操作,不得不说usb request block,简称urb。事实上,可以打一个这样的比喻,usb总线就像一条高速公路,货物、人流之类的可以看成是系统与设备交互的数据,而urb就可以看成是汽车。在一开始对USB规范细节的介绍,我们就说过USB的endpoint有4种不同类型,也就是说能在这条高速公路上流动的数据就有四种。但是这对汽车是没有要求的,所以urb可以运载四种数据,不过你要先告诉司机你要运什么,目的地是什么。我们现在就看看struct urb的具体内容。它的内容很多,为了不让我的理解误导各位,大家最好还是看一看内核源码的注释,具体内容参见源码树下include/linux/usb.h。

在这里我们重点介绍程序中出现的几个关键字段:

struct usb_device  *dev

urb所发送的目标设备。

unsigned int pipe

一个管道号码,该管道记录了目标设备的端点以及管道的类型。每个管道只有一种类型和一个方向,它与他的目标设备的端点相对应,我们可以通过以下几个函数来获得管道号并设置管道类型:

unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备的指定端点设置为一个控制OUT端点。

unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备的指定端点设置为一个控制IN端点。

unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备的指定端点设置为一个批量OUT端点。

unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备的指定端点设置为一个批量OUT端点。

unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备的指定端点设置为一个中断OUT端点。

unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备的指定端点设置为一个中断OUT端点。

unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备的指定端点设置为一个等时OUT端点。

unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)

把指定USB设备的指定端点设置为一个等时OUT端点。

unsigned int transfer_flags

当不使用DMA时,应该transfer_flags |= URB_NO_TRANSFER_DMA_MAP(按照代码的理解,希望没有错)。

int status

当一个urb把数据送到设备时,这个urb会由系统返回给驱动程序,并调用驱动程序的urb完成回调函数处理。这时,status记录了这次数据传输的有关状态,例如传送成功与否。成功的话会是0。

要能够运货当然首先要有车,所以第一步当然要创建urb:

struct urb *usb_alloc_urb(int isoc_packets, int mem_flags);

第一个参数是等时包的数量,如果不是乘载等时包,应该为0,第二个参数与kmalloc的标志相同。

要释放一个urb可以用:

void usb_free_urb(struct urb *urb);

要承载数据,还要告诉司机目的地信息跟要运的货物,对于不同的数据,系统提供了不同的函数,对于中断urb,我们用

void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,

void *transfer_buffer, int buffer_length,

usb_complete_t complete, void *context, int interval);

这里要解释一下,transfer_buffer是一个要送/收的数据的缓冲,buffer_length是它的长度,complete是urb完成回调函数的入口,context由用户定义,可能会在回调函数中使用的数据,interval就是urb被调度的间隔。

对于批量urb和控制urb,我们用:

void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,

void *transfer_buffer, int buffer_length, usb_complete_t complete,

void *context);

void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,

unsigned char* setup_packet,void *transfer_buffer,

int buffer_length, usb_complete_t complete,void *context);

控制包有一个特殊参数setup_packet,它指向即将被发送到端点的设置数据报的数据。

对于等时urb,系统没有专门的fill函数,只能对各urb字段显示赋值。

有了汽车,有了司机,下一步就是要开始运货了,我们可以用下面的函数来提交urb

int usb_submit_urb(struct urb *urb, int mem_flags);

mem_flags有几种:GFP_ATOMIC、GFP_NOIO、GFP_KERNEL,通常在中断上下文环境我们会用GFP_ATOMIC。

当我们的卡车运货之后,系统会把它调回来,并调用urb完成回调函数,并把这辆车作为函数传递给驱动程序。我们应该在回调函数里面检查status字段,以确定数据的成功传输与否。下面是用urb来传送数据的细节。

/* initialize the urb properly */

usb_fill_bulk_urb(urb, dev->udev,

usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),

buf, writesize, skel_write_bulk_callback, dev);

urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

/* send the data out the bulk port */

retval = usb_submit_urb(urb, GFP_KERNEL);

这里skel_write_bulk_callback就是一个完成回调函数,而他做的主要事情就是检查数据传输状态和释放urb:

dev = (struct usb_skel *)urb->context;

/* sync/async unlink faults aren't errors */

if (urb->status && !(urb->status = = -ENOENT || urb->status == -ECONNRESET || urb->status = = -ESHUTDOWN)) {

dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);

}

/* free up our allocated buffer */

usb_buffer_free(urb->dev, urb->transfer_buffer_length,

urb->transfer_buffer, urb->transfer_dma);

(4)read函数

点击(此处)折叠或打开

static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)

{

struct usb_skel *dev;

int retval = 0;

dev = (struct usb_skel *)file->private_data; //获取设备的信息

/* do a blocking bulk read to get data from the device */

retval = usb_bulk_msg(dev->udev,

usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),

dev->bulk_in_buffer,

min(dev->bulk_in_size, count),

&count, HZ*10);

/* if the read was successful, copy the data to userspace */

if (!retval) {

if (copy_to_user(buffer, dev->bulk_in_buffer, count))

retval = -EFAULT;

else

retval = count;

}

return retval;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值