软件频道>程序开发>JavaVBVCDelphiC/C++Web开发微软专栏移动数据库程序人生软件工程|Java 运行时监控
您现在的位置: 天极网 > 开发频道 > Linux设备驱动编程之复杂设备驱动
全文

Linux设备驱动编程之复杂设备驱动

2006-11-01 11:32作者:宋宝华出处:天极开发责任编辑:方舟
  (4)注册设备。

  USB设备的驱动主要处理probe(探测)、disconnect(断开)函数及usb_device_id(设备信息)数据结构,如:

static struct usb_device_id sample_id_table[] =
{
 {
  USB_INTERFACE_INFO(3, 1, 1), driver_info: (unsigned long)"keyboard"
 } ,
 {
  USB_INTERFACE_INFO(3, 1, 2), driver_info: (unsigned long)"mouse"
 }
 ,
 {
  0, /* no more matches */
 }
};

static struct usb_driver sample_usb_driver =
{
 name: "sample", probe: sample_probe, disconnect: sample_disconnect, id_table:
 sample_id_table,
};

  当一个USB 设备从系统拔掉后,设备驱动程序的disconnect 函数会自动被调用,在执行了disconnect 函数后,所有为USB 设备分配的数据结构,内存空间都会被释放:

static void sample_disconnect(struct usb_device *udev, void *clientdata)
{
 /* the clientdata is the sample_device we passed originally */
 struct sample_device *sample = clientdata;

 /* remove the URB, remove the input device, free memory */
 usb_unlink_urb(&sample->urb);
 kfree(sample);
 printk(KERN_INFO "sample: USB %s disconnected\n", sample->name);

 /*
 * here you might MOD_DEC_USE_COUNT, but only if you increment
 * the count in sample_probe() below
 */
 return;
}

  当驱动程序向子系统注册后,插入一个新的USB设备后总是要自动进入probe函数。驱动程序会为这个新加入系统的设备向内部的数据结构建立一个新的实例。通常情况下,probe 函数执行一些功能来检测新加入的USB 设备硬件中的生产厂商和产品定义以及设备所属的类或子类定义是否与驱动程序相符,若相符,再比较接口的数目与本驱动程序支持设备的接口数目是否相符。一般在probe 函数中也会解析USB 设备的说明,从而确认新加入的USB 设备会使用这个驱动程序:

static void *sample_probe(struct usb_device *udev, unsigned int ifnum,
const struct usb_device_id *id)
{
 /*
 * The probe procedure is pretty standard. Device matching has already
 * been performed based on the id_table structure (defined later)
 */
 struct usb_interface *iface;
 struct usb_interface_descriptor *interface;
 struct usb_endpoint_descriptor *endpoint;
 struct sample_device *sample;

 printk(KERN_INFO "usbsample: probe called for %s device\n",(char *)id->driver_info /* "mouse" or "keyboard" */ );

 iface = &udev->actconfig->interface[ifnum];
 interface = &iface->altsetting[iface->act_altsetting];

 if (interface->bNumEndpoints != 1) return NULL;

 endpoint = interface->endpoint + 0;
 if (!(endpoint->bEndpointAddress & 0x80)) return NULL;
 if ((endpoint->bmAttributes & 3) != 3) return NULL;

 usb_set_protocol(udev, interface->bInterfaceNumber, 0);
 usb_set_idle(udev, interface->bInterfaceNumber, 0, 0);

 /* allocate and zero a new data structure for the new device */
 sample = kmalloc(sizeof(struct sample_device), GFP_KERNEL);
 if (!sample) return NULL; /* failure */
 memset(sample, 0, sizeof(*sample));
 sample->name = (char *)id->driver_info;

 /* fill the URB data structure using the FILL_INT_URB macro */
 {
  int pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
  int maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));

  if (maxp > 8) maxp = 8; sample->maxp = maxp; /* remember for later */
  FILL_INT_URB(&sample->urb, udev, pipe, sample->data, maxp,
  sample_irq, sample, endpoint->bInterval);
 }

 /* register the URB within the USB subsystem */
 if (usb_submit_urb(&sample->urb)) {
  kfree(sample);
  return NULL;
 }
 /* announce yourself */
 printk(KERN_INFO "usbsample: probe successful for %s (maxp is %i)\n",sample->name, sample->maxp);

 /*
 * here you might MOD_INC_USE_COUNT; if you do, you'll need to unplug
 * the device or the devices before being able to unload the module
 */

 /* and return the new structure */
 return sample;
}

  在网络设备驱动的编写中,我们特别关心的就是数据的收、发及中断。网络设备驱动程序的层次如下:


  网络设备接收到报文后将其传入上层:

/*
* Receive a packet: retrieve, encapsulate and pass over to upper levels
*/
void snull_rx(struct net_device *dev, int len, unsigned char *buf)
{
 struct sk_buff *skb;
 struct snull_priv *priv = (struct snull_priv *) dev->priv;

 /*
 * The packet has been retrieved from the transmission
 * medium. Build an skb around it, so upper layers can handle it
 */
 skb = dev_alloc_skb(len+2);
 if (!skb) {
  printk("snull rx: low on mem - packet dropped\n");
  priv->stats.rx_dropped++;
  return;
 }
 skb_reserve(skb, 2); /* align IP on 16B boundary */
 memcpy(skb_put(skb, len), buf, len);

 /* Write metadata, and then pass to the receive level */
 skb->dev = dev;
 skb->protocol = eth_type_trans(skb, dev);
 skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
 priv->stats.rx_packets++;
 #ifndef LINUX_20
  priv->stats.rx_bytes += len;
 #endif
 netif_rx(skb);
 return;
}

  在中断到来时接收报文信息:

void snull_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
 int statusword;
 struct snull_priv *priv;
 /*
 * As usual, check the "device" pointer for shared handlers.
 * Then assign "struct device *dev"
 */
 struct net_device *dev = (struct net_device *)dev_id;
 /* ... and check with hw if it's really ours */

 if (!dev /*paranoid*/ ) return;

 /* Lock the device */
 priv = (struct snull_priv *) dev->priv;
 spin_lock(&priv->lock);

 /* retrieve statusword: real netdevices use I/O instructions */
 statusword = priv->status;
 if (statusword & SNULL_RX_INTR) {
  /* send it to snull_rx for handling */
  snull_rx(dev, priv->rx_packetlen, priv->rx_packetdata);
 }
 if (statusword & SNULL_TX_INTR) {
  /* a transmission is over: free the skb */
  priv->stats.tx_packets++;
  priv->stats.tx_bytes += priv->tx_packetlen;
  dev_kfree_skb(priv->skb);
 }

 /* Unlock the device and we are done */
 spin_unlock(&priv->lock);
 return;
}

  而发送报文则分为两个层次,一个层次是内核调用,一个层次完成真正的硬件上的发送:

/*
* Transmit a packet (called by the kernel)
*/
int snull_tx(struct sk_buff *skb, struct net_device *dev)
{
 int len;
 char *data;
 struct snull_priv *priv = (struct snull_priv *) dev->priv;

 #ifndef LINUX_24
 if (dev->tbusy || skb == NULL) {
  PDEBUG("tint for %p, tbusy %ld, skb %p\n", dev, dev->tbusy, skb);
  snull_tx_timeout (dev);
  if (skb == NULL)
   return 0;
 }
 #endif

 len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
 data = skb->data;
 dev->trans_start = jiffies; /* save the timestamp */

 /* Remember the skb, so we can free it at interrupt time */
 priv->skb = skb;

 /* actual deliver of data is device-specific, and not shown here */
 snull_hw_tx(data, len, dev);

 return 0; /* Our simple device can not fail */
}

/*
* Transmit a packet (low level interface)
*/
void snull_hw_tx(char *buf, int len, struct net_device *dev)
{
 /*
 * This function deals with hw details. This interface loops
 * back the packet to the other snull interface (if any).
 * In other words, this function implements the snull behaviour,
 * while all other procedures are rather device-independent
 */
 struct iphdr *ih;
 struct net_device *dest;
 struct snull_priv *priv;
 u32 *saddr, *daddr;

 /* I am paranoid. Ain't I? */
 if (len < sizeof(struct ethhdr) + sizeof(struct iphdr)) {
  printk("snull: Hmm... packet too short (%i octets)\n",len);
  return;
 }

 if (0) { /* enable this conditional to look at the data */
  int i;
  PDEBUG("len is %i\n" KERN_DEBUG "data:",len);
  for (i=14 ; i<len; i++)
   printk(" %02x",buf[i]&0xff);
   printk("\n");
 }
 /*
 * Ethhdr is 14 bytes, but the kernel arranges for iphdr
 * to be aligned (i.e., ethhdr is unaligned)
 */
 ih = (struct iphdr *)(buf+sizeof(struct ethhdr));
 saddr = &ih->saddr;
 daddr = &ih->daddr;

 ((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
 ((u8 *)daddr)[2] ^= 1;

 ih->check = 0; /* and rebuild the checksum (ip needs it) */
 ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);

 if (dev == snull_devs)
  PDEBUGG("%08x:%05i --> %08x:%05i\n",ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source),
ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest));
 else
  PDEBUGG("%08x:%05i <-- %08x:%05i\n",
  ntohl(ih->daddr),ntohs(((struct tcphdr *)(ih+1))->dest),
  ntohl(ih->saddr),ntohs(((struct tcphdr *)(ih+1))->source));

  /*
  * Ok, now the packet is ready for transmission: first simulate a
  * receive interrupt on the twin device, then a
  * transmission-done on the transmitting device
  */
  dest = snull_devs + (dev==snull_devs ? 1 : 0);
  priv = (struct snull_priv *) dest->priv;
  priv->status = SNULL_RX_INTR;
  priv->rx_packetlen = len;
  priv->rx_packetdata = buf;
  snull_interrupt(0, dest, NULL);

  priv = (struct snull_priv *) dev->priv;
  priv->status = SNULL_TX_INTR;
  priv->tx_packetlen = len;
  priv->tx_packetdata = buf;
  if (lockup && ((priv->stats.tx_packets + 1) % lockup) == 0) {
   /* Simulate a dropped transmit interrupt */
   netif_stop_queue(dev);
   PDEBUG("Simulate lockup at %ld, txp %ld\n", jiffies,(unsigned long) priv->stats.tx_packets);
  }
  else
   snull_interrupt(0, dev, NULL);
 }

  块设备也以与字符设备register_chrdev、unregister_ chrdev 函数类似的方法进行设备的注册与释放。但是,register_chrdev使用一个向 file_operations 结构的指针,而register_blkdev 则使用 block_device_operations 结构的指针,其中定义的open、release 和 ioctl 方法和字符设备的对应方法相同,但未定义 read 或者 write 操作。这是因为,所有涉及到块设备的 I/O 通常由系统进行缓冲处理。

相关搜索:
关注此文读者还看过
热门关注
特别推荐
关于我们|About us|网站律师|天极服务|电子杂志|RSS订阅|加入我们|网站地图
TMG
Copyright (C) 1999-2009 Chinabyte.com, All Rights Reserved 版权所有 天极网络
商务联系、网站内容、合作建议:010-82657868
版权声明 在线提交意见反馈 Powered by 天极内容管理平台CMS4i
经营性网站备案信息 网警备案 中国网站排名
天极传媒:天极网|比特网|IT专家网|IT商网|52PK游戏网|IT分众