【Linux高级驱动】如何分析并移植网卡驱动

时间:2021-03-23 16:35:45

dm9000的驱动分析

m9000_init
 platform_driver_register( &dm9000_driver);
dm9000_probe
  /*获取平台数据*/
  struct dm9000_plat_data *pdata = pdev - >dev.platform_data;
  /*表示一个网络设备*/
  struct net_device *ndev;
  /*为网络设备分配空间*/
 ndev = alloc_etherdev( sizeof( struct board_info));
  /*获取资源*/
 db - >addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 db - >data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 db - >irq_res   = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
  /*映射地址端口*/
 db - >io_addr = ioremap(db - >addr_res - >start, iosize);
  /*映射数据端口*/
 db - >io_data = ioremap(db - >data_res - >start, iosize);
  /*硬件相关的操作*/
  /*1.设置读写操作函数*/
 dm9000_set_io(db, 2);
   case 2 :
  db - >dumpblk = dm9000_dumpblk_16bit;
  db - >outblk   = dm9000_outblk_16bit;
  db - >inblk    = dm9000_inblk_16bit;
   break;
 
  /*2.复位 */
 dm9000_reset(db);
  /*3.读dm9000的ID号 */
 id_val   = ior(db, DM9000_VIDL);
 id_val |= (u32)ior(db, DM9000_VIDH) << 8;
 id_val |= (u32)ior(db, DM9000_PIDL) << 16;
 id_val |= (u32)ior(db, DM9000_PIDH) << 24;
  /* 获取芯片型号 */
 id_val = ior(db, DM9000_CHIPR);
 ether_setup(ndev);
  /*设置操作方法*/
 ndev - >netdev_ops   = &dm9000_netdev_ops;
  /*注册网络设备*/
 register_netdev(ndev);    //register_chrdev

 

cs8900a网卡驱动分析

/*分配一个网络设备*/
struct net_device *dev = alloc_etherdev( sizeof( struct net_local));
dev - >irq = irq;
dev - >base_addr = io;
cs89x0_probe1(dev, io, 1);
  /*识别芯片*/
  /*操作方法的设置*/
 dev - >netdev_ops = &net_ops;
  /*注册网络设备*/
 register_netdev(dev);

static const struct net_device_ops dm9000_netdev_ops = {
 .ndo_open     = dm9000_open,    //必须的
 .ndo_stop     = dm9000_stop,    //必须的
 .ndo_start_xmit    = dm9000_start_xmit, //必须的
 .ndo_tx_timeout    = dm9000_timeout,   //必须的
 .ndo_set_multicast_list = dm9000_hash_table,
 .ndo_do_ioctl    = dm9000_ioctl,
 .ndo_change_mtu    = eth_change_mtu,
 .ndo_validate_addr   = eth_validate_addr,
 .ndo_set_mac_address = eth_mac_addr,
# ifdef CONFIG_NET_POLL_CONTROLLER
 .ndo_poll_controller = dm9000_poll_controller,
# endif
};

 

网卡驱动的数据接收发送流程?

初始化设备

dm9000_open( struct net_device *dev)
{
   if (request_irq(dev - >irq, dm9000_interrupt, irqflags, dev - >name, dev))
   return -EAGAIN;
   /* Initialize DM9000 board */
  dm9000_reset(db);
     /* RESET device */
    writeb(DM9000_NCR, db - >io_addr);
    udelay( 200);
    writeb(NCR_RST, db - >io_data);
    udelay( 200);
   /* dm9000的初始化,芯片厂商会支持 */
  dm9000_init_dm9000(dev);
  
   /* 启动发送队列 */
  netif_start_queue(dev);
}

数据接收流程

dm9000_interrupt( int irq, void *dev_id)
  /* Save previous register address */
 reg_save = readb(db - >io_addr);
  /* Disable all interrupts */
 iow(db, DM9000_IMR, IMR_PAR);
  /* Got DM9000 interrupt status */
 int_status = ior(db, DM9000_ISR); /* Got ISR */
 iow(db, DM9000_ISR, int_status); /* Clear ISR status */
  /* Received the coming packet 读中断*/
  if (int_status & ISR_PRS)
  dm9000_rx(dev);
   
   ior(db, DM9000_MRCMDX); /* Dummy read 地址不自动增加*/
   rxbyte = readb(db - >io_data);
    if (rxbyte & DM9000_PKT_ERR) {
    dev_warn(db - >dev, "status check fail: %d\n", rxbyte);
    iow(db, DM9000_RCR, 0x00);   /* Stop Device */
    iow(db, DM9000_ISR, IMR_PAR); /* Stop INT request */
     return;
   }
   
   writeb(DM9000_MRCMD, db - >io_addr);   /*地址自动增加的*/
    /* 读取状态信息 */
   (db - >inblk)(db - >io_data, &rxhdr, sizeof(rxhdr)); //dm9000_inblk_16bit
   RxLen = le16_to_cpu(rxhdr.RxLen);
    /*数据的状态判断*/
    if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |
          RSR_PLE | RSR_RWTO |
          RSR_LCS | RSR_RF)) {
    struct sk_buff *skb;
   skb = dev_alloc_skb(RxLen + 4))    /*分配一个sk_buff数据包*/
   skb_reserve(skb, 2);   //data指针和tail指针同时下移  
   rdptr = (u8 *) skb_put(skb, RxLen - 4);
   (db - >inblk)(db - >io_data, rdptr, RxLen);   //dm9000_inblk_16bit   //读真正的有效数据(MAC头,TCP头,IP头,网络数据)
   dev - >stats.rx_bytes += RxLen;
    /* Pass to upper layer,去掉MAC头 */
   skb - >protocol = eth_type_trans(skb, dev);
    /* 将数据上报到上层 */
   netif_rx(skb);
   dev - >stats.rx_packets ++;

数据发送流程

sk_buff
dm9000_start_xmit
  /* 将数据写入到DM9000的SRAM中 */
  /* Move data to DM9000 TX RAM */
 writeb(DM9000_MWCMD, db - >io_addr);     //设置为自动增加
  //dm9000_outblk_16bit
 (db - >outblk)(db - >io_data, skb - >data, skb - >len);    //dm9000_outblk_16bit
 dev - >stats.tx_bytes += skb - >len;
 db - >tx_pkt_cnt ++;
  /*设置发送属性*/
 dm9000_send_packet( struct net_device *dev,
           int ip_summed,
          u16 pkt_len)
   /*指定数据包的长度*/
  iow(dm, DM9000_TXPLL, pkt_len);
  iow(dm, DM9000_TXPLH, pkt_len >> 8);
   /*启动发送:数据发送完成,产生中断*/
  iow(dm, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
 netif_stop_queue(dev);
 
dm9000_interrupt( int irq, void *dev_id)
  /* Save previous register address */
 reg_save = readb(db - >io_addr);    //让他不自动增加
  /* 关中断 */
 iow(db, DM9000_IMR, IMR_PAR);
 
  /*获取中断状态*/
 int_status = ior(db, DM9000_ISR); /* Got ISR */
 iow(db, DM9000_ISR, int_status); /* Clear ISR status */
  /* Trnasmit Interrupt check,数据发送完成 */
  if (int_status & ISR_PTS)
  dm9000_tx_done(dev, db);
   db - >tx_pkt_cnt --;
   dev - >stats.tx_packets ++;
    if (db - >tx_pkt_cnt > 0)
    dm9000_send_packet(dev, db - >queue_ip_summed,db - >queue_pkt_len);
      /*指定数据包的长度*/
     iow(dm, DM9000_TXPLL, pkt_len);
     iow(dm, DM9000_TXPLH, pkt_len >> 8);
      /*启动发送:数据发送完成,产生中断*/
     iow(dm, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */

   netif_wake_queue(dev);
  /* Re-enable interrupt mask */
 iow(db, DM9000_IMR, db - >imr_all);
  /* Restore previous register address */
 writeb(reg_save, db - >io_addr);   //恢复为自动增加

 

怎么写网卡驱动

1.cs89x0.c

1.1 分配一个net_device结构体

     alloc_etherdev

1.2 设置

    dev - >open        = net_open;
    dev - >stop        = net_close;
    dev - >tx_timeout      = net_timeout;
    dev - >watchdog_timeo = HZ;
    dev - >hard_start_xmit     = net_send_packet;
    dev - >get_stats       = net_get_stats;
    dev - >set_multicast_list = set_multicast_list;
    dev - >set_mac_address     = set_mac_address;

1.3 注册

    register_netdev

2. DM9000.c

2.1 分配一个net_device结构体
    ndev = alloc_etherdev(sizeof (struct board_info));
2.2 设置
   

    ether_setup(ndev);
    ndev - >open        = &dm9000_open;
    ndev - >hard_start_xmit     = &dm9000_start_xmit;
    ndev - >tx_timeout          = &dm9000_timeout;
    ndev - >watchdog_timeo = msecs_to_jiffies(watchdog);
    ndev - >stop        = &dm9000_stop;
    ndev - >get_stats       = &dm9000_get_stats;
    ndev - >set_multicast_list = &dm9000_hash_table;

2.3 注册
    ret = register_netdev(ndev);

任何设备的核心都是收发数据

1. 发数据:
   上层要发送数据时,构造一个sk_buff,然后调用net_device的hard_start_xmit来发送

2. 收数据:
   网卡收到数据后,发生中断
   在中断服务程序里:
   从硬件上读出数据,然后构造一个sk_buff,上报:
a. 分配一个sk_buff结构体:
    dev_alloc_skb
b. 使用硬件上得到数据填充这个结构体
c. 上报:netif_rx

测试方法

1. 编译/安装驱动 farsight_net_1.c

   ifconfig fs_net0 up
   ifconfig fs_net0 192. 188. 1. 1
   ping 192. 188. 1. 1 成功,证明ping自己的话,不经过硬件
   ping 192. 188. 1. 2 多次调用fsnet_hard_start_tx 
   PING 192. 188. 1. 2 ( 192. 188. 1. 2) : 56 data bytes
   fsnet_hard_start_tx 
   fsnet_hard_start_tx    
   再次ifconfig发现fs_net0的rx/tx都是0


2. 编译/安装驱动 farsight_net_2.c: 添加统计信息

3. 编译/安装驱动 farsight_net_3.c: 设MAC地址
   ifconfig fs_net0
   ifconfig         可以看到MAC地址

4. 编译/安装驱动 farsight_net_4.c: 构造ping的返回包
   ifconfig fs_net0 up
   ifconfig fs_net0 192.188.1.1
   ping 192.188.1.2                成功

 

怎么移植网卡驱动

    网卡基本上都是内存接口(ram-like)
    1. 根据原理图确定访问地址, 在驱动里修改相应项
    2. 为了能通过这些地址访问网卡,对于2410还要设置memory controller
   比如设置位宽、时间参数
    3. 根据原理图确定中断号, 在驱动里修改相应项(包括中断号、中断触发方式(高/低有效))

 

 @成鹏致远

(blogs:http://lcw.cnblogs.com

(emailwwwlllll@126.com)

(qq552158509