复杂设备.docx
- 文档编号:4287461
- 上传时间:2022-11-28
- 格式:DOCX
- 页数:12
- 大小:53.62KB
复杂设备.docx
《复杂设备.docx》由会员分享,可在线阅读,更多相关《复杂设备.docx(12页珍藏版)》请在冰豆网上搜索。
复杂设备
这里所说的复杂设备驱动涉及到PCI、USB、网络设备、块设备等(严格意义而言,这些设备在概念上并不并列,例如与块设备并列的是字符设备,而PCI、USB设备等都可能属于字符设备),这些设备的驱动中又涉及到一些与特定设备类型相关的较为复杂的数据结构和程序结构。
本文将不对这些设备驱动的细节进行过多的介绍,仅仅进行轻描淡写的叙述。
PCI是ThePeripheralComponentInterconnect-Bus的缩写,CPU使用PCI桥chipset与PCI设备通信,PCI桥chipset处理了PCI子系统与内存子系统间的所有数据交互,PCI设备完全被从内存子系统分离出来。
下图呈现了PCI子系统的原理:
每个PCI设备都有一个256字节的设备配置块,其中前64字节作为设备的ID和基本配置信息,Linux中提供了一组函数来处理PCI配置块。
在PCI设备能得以使用前,Linux驱动程序需要从PCI设备配置块中的信息决定设备的特定参数,进行相关设置以便能正确操作该PCI设备。
一般的PCI设备初始化函数处理流程为:
(1)检查内核是否支持PCI-Bios;
(2)检查设备是否存在,获得设备的配置信息;
1~2这两步的例子如下:
intpcidata_read_proc(char*buf,char**start,off_toffset,intlen,int*eof,void*data)
{
inti,pos=0;
intbus,devfn;
if(!
pcibios_present())
returnsprintf(buf,"NoPCIbiospresent\n");
/*
*Thiscodeisderivedfrom"drivers/pci/pci.c".Thismeansthat
*theGPLappliestothissourcefileandcreditisduetothe
*originalauthors(DrewEckhardt,FredericPotter,David
*Mosberger-Tang)
*/
for(bus=0;!
bus;bus++)
{
/*onlybus0:
-)*/
for(devfn=0;devfn<0x100&&pos { structpci_dev*dev=NULL; dev=pci_find_slot(bus,devfn); if(! dev) continue; /*Ok,we'vefoundadevice,copyitscfgspacetothebuffer*/ for(i=0;i<256;i+=sizeof(u32),pos+=sizeof(u32))pci_read_config_dword(dev,i,(u32*)(buf+pos)); pci_release_device(dev);/*2.0compatibility*/ } } *eof=1; returnpos; } 其中使用的pci_find_slot()函数定义为: structpci_dev*pci_find_slot(unsignedintbus, unsignedintdevfn) { structpci_dev*pptr=kmalloc(sizeof(*pptr),GFP_KERNEL); intindex=0; unsignedshortvendor; intret; if(! pptr)returnNULL; pptr->index=index;/*0*/ ret=pcibios_read_config_word(bus,devfn,PCI_VENDOR_ID,&vendor); if(ret/*==PCIBIOS_DEVICE_NOT_FOUNDorwhatevererror*/ ||vendor==0xffff||vendor==0x0000){ kfree(pptr);returnNULL; } printk("ok(%i,%i%x)\n",bus,devfn,vendor); /*fillotherfields*/ pptr->bus=bus; pptr->devfn=devfn; pcibios_read_config_word(pptr->bus,pptr->devfn,PCI_VENDOR_ID,&pptr->vendor); pcibios_read_config_word(pptr->bus,pptr->devfn,PCI_DEVICE_ID,&pptr->device); returnpptr; } (3)根据设备的配置信息申请I/O空间及IRQ资源; (4)注册设备。 USB设备的驱动主要处理probe(探测)、disconnect(断开)函数及usb_device_id(设备信息)数据结构,如: staticstructusb_device_idsample_id_table[]= { { USB_INTERFACE_INFO(3,1,1),driver_info: (unsignedlong)"keyboard" }, { USB_INTERFACE_INFO(3,1,2),driver_info: (unsignedlong)"mouse" } , { 0,/*nomorematches*/ } }; staticstructusb_driversample_usb_driver= { name: "sample",probe: sample_probe,disconnect: sample_disconnect,id_table: sample_id_table, }; 当一个USB设备从系统拔掉后,设备驱动程序的disconnect函数会自动被调用,在执行了disconnect函数后,所有为USB设备分配的数据结构,内存空间都会被释放: staticvoidsample_disconnect(structusb_device*udev,void*clientdata) { /*theclientdataisthesample_devicewepassedoriginally*/ structsample_device*sample=clientdata; /*removetheURB,removetheinputdevice,freememory*/ usb_unlink_urb(&sample->urb); kfree(sample); printk(KERN_INFO"sample: USB%sdisconnected\n",sample->name); /* *hereyoumightMOD_DEC_USE_COUNT,butonlyifyouincrement *thecountinsample_probe()below */ return; } 当驱动程序向子系统注册后,插入一个新的USB设备后总是要自动进入probe函数。 驱动程序会为这个新加入系统的设备向内部的数据结构建立一个新的实例。 通常情况下,probe函数执行一些功能来检测新加入的USB设备硬件中的生产厂商和产品定义以及设备所属的类或子类定义是否与驱动程序相符,若相符,再比较接口的数目与本驱动程序支持设备的接口数目是否相符。 一般在probe函数中也会解析USB设备的说明,从而确认新加入的USB设备会使用这个驱动程序: staticvoid*sample_probe(structusb_device*udev,unsignedintifnum, conststructusb_device_id*id) { /* *Theprobeprocedureisprettystandard.Devicematchinghasalready *beenperformedbasedontheid_tablestructure(definedlater) */ structusb_interface*iface; structusb_interface_descriptor*interface; structusb_endpoint_descriptor*endpoint; structsample_device*sample; printk(KERN_INFO"usbsample: probecalledfor%sdevice\n",(char*)id->driver_info/*"mouse"or"keyboard"*/); iface=&udev->actconfig->interface[ifnum]; interface=&iface->altsetting[iface->act_altsetting]; if(interface->bNumEndpoints! =1)returnNULL; endpoint=interface->endpoint+0; if(! (endpoint->bEndpointAddress&0x80))returnNULL; if((endpoint->bmAttributes&3)! =3)returnNULL; usb_set_protocol(udev,interface->bInterfaceNumber,0); usb_set_idle(udev,interface->bInterfaceNumber,0,0); /*allocateandzeroanewdatastructureforthenewdevice*/ sample=kmalloc(sizeof(structsample_device),GFP_KERNEL); if(! sample)returnNULL;/*failure*/ memset(sample,0,sizeof(*sample)); sample->name=(char*)id->driver_info; /*filltheURBdatastructureusingtheFILL_INT_URBmacro*/ { intpipe=usb_rcvintpipe(udev,endpoint->bEndpointAddress); intmaxp=usb_maxpacket(udev,pipe,usb_pipeout(pipe)); if(maxp>8)maxp=8;sample->maxp=maxp;/*rememberforlater*/ FILL_INT_URB(&sample->urb,udev,pipe,sample->data,maxp, sample_irq,sample,endpoint->bInterval); } /*registertheURBwithintheUSBsubsystem*/ if(usb_submit_urb(&sample->urb)){ kfree(sample); returnNULL; } /*announceyourself*/ printk(KERN_INFO"usbsample: probesuccessfulfor%s(maxpis%i)\n",sample->name,sample->maxp); /* *hereyoumightMOD_INC_USE_COUNT;ifyoudo,you'llneedtounplug *thedeviceorthedevicesbeforebeingabletounloadthemodule */ /*andreturnthenewstructure*/ returnsample; } 在网络设备驱动的编写中,我们特别关心的就是数据的收、发及中断。 网络设备驱动程序的层次如下: 网络设备接收到报文后将其传入上层: /* *Receiveapacket: retrieve,encapsulateandpassovertoupperlevels */ voidsnull_rx(structnet_device*dev,intlen,unsignedchar*buf) { structsk_buff*skb; structsnull_priv*priv=(structsnull_priv*)dev->priv; /* *Thepackethasbeenretrievedfromthetransmission *medium.Buildanskbaroundit,soupperlayerscanhandleit */ skb=dev_alloc_skb(len+2); if(! skb){ printk("snullrx: lowonmem-packetdropped\n"); priv->stats.rx_dropped++; return; } skb_reserve(skb,2);/*alignIPon16Bboundary*/ memcpy(skb_put(skb,len),buf,len); /*Writemetadata,andthenpasstothereceivelevel*/ skb->dev=dev; skb->protocol=eth_type_trans(skb,dev); skb->ip_summed=CHECKSUM_UNNECESSARY;/*don'tcheckit*/ priv->stats.rx_packets++; #ifndefLINUX_20 priv->stats.rx_bytes+=len; #endif netif_rx(skb); return; } 在中断到来时接收报文信息: voidsnull_interrupt(intirq,void*dev_id,structpt_regs*regs) { intstatusword; structsnull_priv*priv; /* *Asusual,checkthe"device"pointerforsharedhandlers. *Thenassign"structdevice*dev" */ structnet_device*dev=(structnet_device*)dev_id; /*...andcheckwithhwifit'sreallyours*/ if(! dev/*paranoid*/)return; /*Lockthedevice*/ priv=(structsnull_priv*)dev->priv; spin_lock(&priv->lock); /*retrievestatusword: realnetdevicesuseI/Oinstructions*/ statusword=priv->status; if(statusword&SNULL_RX_INTR){ /*sendittosnull_rxforhandling*/ snull_rx(dev,priv->rx_packetlen,priv->rx_packetdata); } if(statusword&SNULL_TX_INTR){ /*atransmissionisover: freetheskb*/ priv->stats.tx_packets++; priv->stats.tx_bytes+=priv->tx_packetlen; dev_kfree_skb(priv->skb); } /*Unlockthedeviceandwearedone*/ spin_unlock(&priv->lock); return; } 而发送报文则分为两个层次,一个层次是内核调用,一个层次完成真正的硬件上的发送: /* *Transmitapacket(calledbythekernel) */ intsnull_tx(structsk_buff*skb,structnet_device*dev) { intlen; char*data; structsnull_priv*priv=(structsnull_priv*)dev->priv; #ifndefLINUX_24 if(dev->tbusy||skb==NULL){ PDEBUG("tintfor%p,tbusy%ld,skb%p\n",dev,dev->tbusy,skb); snull_tx_timeout(dev); if(skb==NULL) return0; } #endif len=skb->len ETH_ZLEN: skb->len; data=skb->data; dev->trans_start=jiffies;/*savethetimestamp*/ /*Remembertheskb,sowecanfreeitatinterrupttime*/ priv->skb=skb; /*actualdeliverofdataisdevice-specific,andnotshownhere*/ snull_hw_tx(data,len,dev); return0;/*Oursimpledevicecannotfail*/ } /* *Transmitapacket(lowlevelinterface) */ voidsnull_hw_tx(char*buf,intlen,structnet_device*dev) { /* *Thisfunctiondealswithhwdetails.Thisinterfaceloops *backthepackettotheothersnullinterface(ifany). *Inotherwords,thisfunctionimplementsthesnullbehaviour, *whileallotherproceduresareratherdevice-independent */ structiphdr*ih; structnet_device*dest; structsnull_priv*priv; u32*saddr,*daddr; /*Iamparanoid.Ain'tI? */ if(len printk("snull: Hmm...packettooshort(%ioctets)\n",len); return; } if(0){/*enablethisconditionaltolookatthedata*/ inti; PDEBUG("lenis%i\n"KERN_DEBUG"data: ",len); for(i=14;i printk("%02x",buf[i]&0xff); printk("\n"); } /* *Ethhdris14bytes,butthekernelarrangesforiphdr *tobealigned(i.e.,ethhdrisunaligned) */ ih=(structiphdr*)(buf+sizeof(structethhdr)); saddr=&ih->saddr; daddr=&ih->daddr; ((u8*)saddr)[2]^=1;/*changethethirdoctet(classC)*/ ((u8*)daddr)[2]^=1; ih->check=0;/*andrebuildthechecksum(ipneedsit)*/ ih->check=ip_fast_csum((unsignedchar*)ih,ih->ihl); if(dev==snull_devs) PDEBUGG("%08x: %05i-->%08x: %05i\n",ntohl(ih->saddr),ntohs(((structtcphdr*)(ih+1))->source), ntohl(ih->daddr),ntohs(((structtcphdr*)(ih+1))->dest)); else PDEBUGG("%08x: %05i<--%08x: %05i\n", ntohl(ih->daddr),ntoh
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 复杂 设备
![提示](https://static.bdocx.com/images/bang_tan.gif)