skel框架.docx
- 文档编号:12740190
- 上传时间:2023-04-21
- 格式:DOCX
- 页数:17
- 大小:21.25KB
skel框架.docx
《skel框架.docx》由会员分享,可在线阅读,更多相关《skel框架.docx(17页珍藏版)》请在冰豆网上搜索。
skel框架
导读:
Usb_skeleton.c,是USB驱动的框架,适合USB驱动的初学者。
1.结构体
内核其实就是一坨坨的数据结构,加上一根根链表。
对于初学者,如果直接看USB驱动代码,大概会被那些名字相近的结构体弄得晕头转向,比如usb_host_interface和usb_interface,看着看着就把两个混淆了。
所以,在学习USB驱动之前,建议把相关结构体都拎出来看一下,其实,也就那么几个结构体在那装神弄鬼。
USBskeleton驱动中用到的主要字段已用蓝色标出:
endpoint:
structusb_host_endpoint{
structusb_endpoint_descriptor desc;
structlist_head urb_list;
void *hcpriv;
unsignedchar*extra;
intextralen;
};
structusb_endpoint_descriptor{
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress;
__u8 bmAttributes;
__le16wMaxPacketSize;
__u8 bInterval;
__u8 bRefresh;
__u8 bSynchAddress;
}__attribute__((packed));
bEndpointAddress,最高位用来判断传输方向:
#defineUSB_ENDPOINT_NUMBER_MASK 0x0f
#defineUSB_ENDPOINT_DIR_MASK 0x80
#defineUSB_DIR_OUT 0
#defineUSB_DIR_IN 0x80
bmAttributes,表示endpoint的类型:
#defineUSB_ENDPOINT_XFERTYPE_MASK0x03
#defineUSB_ENDPOINT_XFER_CONTROL 0
#defineUSB_ENDPOINT_XFER_ISOC 1
#defineUSB_ENDPOINT_XFER_BULK 2
#defineUSB_ENDPOINT_XFER_INT 3
bInterval,如果该endpoint是interrupt类型的(USB鼠标驱动就是该类型),那么bInterval就表示中断时间间隔,单位毫秒。
interface:
structusb_interface{
structusb_host_interface*altsetting;
structusb_host_interface*cur_altsetting;
unsignednum_altsetting;
intminor;
enumusb_interface_conditioncondition;
structdevicedev;
structclass_device*class_dev;
};
structusb_host_interface{
structusb_interface_descriptordesc;
structusb_host_endpoint*endpoint;
char*string;
unsignedchar*extra;
intextralen;
};
structusb_interface_descriptor{
__u8 bLength;
__u8 bDescriptorType;
__u8 bInterfaceNumber;
__u8 bAlternateSetting;
__u8 bNumEndpoints;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
__u8 iInterface;
}__attribute__((packed));
usb_device:
structusb_device{
int devnum;
char devpath[16];
enumusb_device_state state;
enumusb_device_speed speed;
structusb_tt*tt;
int ttport;
structsemaphoreserialize;
unsignedinttoggle[2];
structusb_device*parent;
structusb_bus*bus;
structusb_host_endpointep0;
structdevicedev;
structusb_device_descriptordescriptor;
structusb_host_config*config;
structusb_host_config*actconfig;
structusb_host_endpoint*ep_in[16];
structusb_host_endpoint*ep_out[16];
char**rawdescriptors;
inthave_langid;
intstring_langid;
char*product;
char*manufacturer;
char*serial;
structlist_headfilelist;
structdentry*usbfs_dentry;
intmaxchild;
structusb_device*children[USB_MAXCHILDREN];
};
usb_driver:
structusb_driver{
structmodule*owner;
constchar*name;
int(*probe)(structusb_interface*intf,
conststructusb_device_id*id);
void(*disconnect)(structusb_interface*intf);
int(*ioctl)(structusb_interface*intf,unsignedintcode,void*buf);
int(*suspend)(structusb_interface*intf,pm_message_tmessage);
int(*resume)(structusb_interface*intf);
conststructusb_device_id*id_table;
structdevice_driverdriver;
};
2.Init
先来看模块初始化函数,它仅仅完成一个功能,那就是注册USB驱动:
staticint__initusb_skel_init(void)
{
intresult;
result=usb_register(&skel_driver);
if(result)
err("usb_registerfailed.Errornumber%d",result);
returnresult;
}
其中,skel_driver如下:
staticstructusb_driverskel_driver={
.owner= THIS_MODULE,
.name= "skeleton",
.probe= skel_probe,
.disconnect=skel_disconnect,
.id_table= skel_table,
};
前面几个字段很好理解,这里就说下id_table。
先看skel_table的定义:
staticstructusb_device_idskel_table[]={
{USB_DEVICE(USB_SKEL_VENDOR_ID,USB_SKEL_PRODUCT_ID)},
{}
};
id_table用来告诉内核该模块支持的所有设备。
usb子系统通过设备的productionID和vendorID的组合或者设备的class、subclass跟protocol的组合来识别设备,并调用相关的驱动程序作处理。
不同设备的这些组合,当然是不一样的,这由USB协会统一管理、分配。
skeleton中,使用productionID和vendorID的组合来识别设备。
注意,还要使用MODULE_DEVICE_TABLE把这个id_table注册到系统中去:
MODULE_DEVICE_TABLE(usb,skel_table);
3.Probe
probe是usb子系统自动调用的一个函数,有USB设备连接到主机时,usb子系统会根据productionID和vendorID的组合或者设备的class、subclass跟protocol的组合(也就是根据id_table)来识别设备,并调用相应驱动程序的probe(探测)函数。
不同的USB驱动模块,会注册不同的id_table,比如现在有Usb_skeleton.c、Usb_driver1.c、Usb_driver2.c和Usb_driver3.c这么四个USB驱动模块,它们都会调用MODULE_DEVICE_TABLE(usb,xxx_table)。
这样,系统中就有四个id_table。
当一个USB设备连接到主机时,系统会从这四个id_table中,找到能够匹配该USB设备的id_table,并调用该id_table所属的USB驱动模块。
Probe代码很长,分段分析:
staticintskel_probe(structusb_interface*interface,conststructusb_device_id*id)
{
structusb_skel*dev=NULL;
structusb_host_interface*iface_desc;
structusb_endpoint_descriptor*endpoint;
size_tbuffer_size;
inti;
intretval=-ENOMEM;
dev=kmalloc(sizeof(*dev),GFP_KERNEL);
if(dev==NULL){
err("Outofmemory");
gotoerror;
}
memset(dev,0x00,sizeof(*dev));
kref_init(&dev->kref);
dev->udev=usb_get_dev(interface_to_usbdev(interface));
dev->interface=interface;
……
error:
if(dev)
kref_put(&dev->kref,skel_delete);
returnretval;
先介绍几个函数:
usb_get_dev和usb_put_dev分别是递增/递减usb_device的referencecount。
kref_init,初始化kref,并将其置设成1。
kref_get和kref_put分别递增/递减kref。
在初始化了一些资源之后,可以看到第一个关键的函数调用——interface_to_usbdev。
他从一个usb_interface来得到该接口所在设备的usb_device。
本来,要得到一个usb_device只要用interface_to_usbdev就够了,但因为要增加对该usb_device的引用计数,我们应该在做一个usb_get_dev的操作,来增加引用计数,并在释放设备时用usb_put_dev来减少引用计数。
这里要解释的是,usb_get_dev是对该usb_device的计数,并不是对本模块的计数,本模块的计数要由kref来维护。
所以,probe一开始就有初始化kref,kref_init(&dev->kref)。
事实上,kref_init操作不单只初始化kref,还将其置设成1。
所以在出错处理代码中有kref_put,它把kref的计数减1,如果kref计数已经为0,那么kref会被释放。
kref_put的第二个参数是一个函数指针,指向一个清理函数。
注意,该指针不能为空,或者kfree。
该函数会在最后一个对kref的引用释放时被调用。
iface_desc=interface->cur_altsetting;
for(i=0;i
endpoint=&iface_desc->endpoint[i].desc;
if(!
dev->bulk_in_endpointAddr&&
((endpoint->bEndpointAddress&USB_ENDPOINT_DIR_MASK)
==USB_DIR_IN)&&
((endpoint->bmAttributes&USB_ENDPOINT_XFERTYPE_MASK)
==USB_ENDPOINT_XFER_BULK)){
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("Couldnotallocatebulk_in_buffer");
gotoerror;
}
}
if(!
dev->bulk_out_endpointAddr&&
((endpoint->bEndpointAddress&USB_ENDPOINT_DIR_MASK)
==USB_DIR_OUT)&&
((endpoint->bmAttributes&USB_ENDPOINT_XFERTYPE_MASK)
==USB_ENDPOINT_XFER_BULK)){
dev->bulk_out_endpointAddr=endpoint->bEndpointAddress;
}
}
if(!
(dev->bulk_in_endpointAddr&&dev->bulk_out_endpointAddr)){
err("Couldnotfindbothbulk-inandbulk-outendpoints");
gotoerror;
}
上面这段函数,主要是通过usb_endpoint_descriptor里的信息,初始化dev(usb_skel类型)中的字段。
这里列一下各个结构体之间的关系,帮助大家理一下层次:
usb_interface->usb_host_interface->usb_host_endpoint->usb_endpoint_descriptor
usb_set_intfdata(interface,dev);
retval=usb_register_dev(interface,&skel_class);
if(retval){
err("Notabletogetaminorforthisdevice.");
usb_set_intfdata(interface,NULL);
gotoerror;
}
info("USBSkeletondevicenowattachedtoUSBSkel-%d",interface->minor);
return0;
usb_set_intfdata,把刚才初始化得到的dev(usb_skel类型)保存在usb_interface中,以便其他函数使用。
这样做是因为,dev是一个局部变量,其他函数没法获得,但其他函数(比如open)可以访问usb_interface,这样,也就可以访问usb_skel里的具体字段了。
如open函数中,dev=usb_get_intfdata(interface)。
下面讲一下usb_register_dev相关的内容。
一个USBinterface对应一种USB逻辑设备,比如鼠标、键盘、音频流。
所以,在USB范畴中,device一般就是指一个interface。
一个驱动只控制一个interface。
这样,usb_register_dev自然是注册一个interface,所以usb_register_dev的第一个参数是interface(usb_interface类型)。
接着介绍下skel_class:
staticstructusb_class_driverskel_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,
};
其中,skel_fops定义为:
staticstructfile_operationsskel_fops={
.owner= THIS_MODULE,
.read= skel_read,
.write= skel_write,
.open= skel_open,
.release=skel_release,
};
skel_fops是真正完成对设备IO操作的函数集。
usb_register_dev注册一次,获取一个次设备号。
该次设备号从usb_class_driver->minor_base开始分配。
usb_register_dev(interface,&skel_class),也就是说,一个usb_interface对应一个次设备号。
结合上面举的interface例子,可以知道,鼠标、键盘各自对应一个不同的次设备号。
4.Disconnect
当设备从主机拔出时,usb子系统会自动地调用disconnect,他做的事情不多,最重要的是注销class_driver(交还次设备号)和interface的data。
然后用kref_put(&dev->kref,skel_delete)进行清理。
staticvoidskel_disconnect(structusb_interface*interface)
{
structusb_skel*dev;
intminor=interface->minor;
lock_kernel();
dev=usb_get_intfdata(interface);
usb_set_intfdata(interface,NULL);
usb_deregister_dev(interface,&skel_class);
unlock_kernel();
kref_put(&dev->kref,skel_delete);
info("USBSkeleton#%dnowdisconnected",minor);
}
5.Open、Read、Write
skel_open、skel_read、skel_write,和我们通常见到的文件操作一样,当用户调用open、read或write这三个系统调用时,系统会分别调用这三个函数。
5.1Open
staticintskel_open(structinode*inode,structfile*file)
{
structusb_skel*dev;
structusb_interface*interface;
intsubminor;
intretval=0;
subminor=iminor(inode);
interface=usb_find_interface(&skel_driver,subminor);
if(!
interface){
err("%s-error,can'tfinddeviceforminor%d",
__FUNCTION__,subminor);
retval=-ENODEV;
gotoexit;
dev=usb_get_intfdata(interface);
if(!
dev){
retval=-ENODEV;
gotoexit;
kref_get(&dev->kref);
file->private_data=dev;
exit:
returnretval;
}
open函数很简单,主要是递增usb_skel的kref,并把该结构体存入file的private_data中,以便其他函数(如read、write)调用。
5.2Read
staticssize_tskel_read(structfile*file,char*buffer,size_tcount,loff_t*ppos)
{
structusb_skel*dev;
intretval=0;
intbytes_read;
dev=(structusb_ske
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- skel 框架