Linux下的网络设备驱动(二)

发表于:2013-2-20 10:08

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:weiqing1981127    来源:51Testing软件测试网采编

  三、网络设备驱动分析

  我们先看看dm9000.c驱动的模块加载函数

static struct platform_driver dm9000_driver = {
       .driver     = {
              .name    = "dm9000",  //驱动名
              .owner    = THIS_MODULE,
              .pm  = &dm9000_drv_pm_ops, //电源相关操作
       },
       .probe   = dm9000_probe,  //探测函数
       .remove  = __devexit_p(dm9000_drv_remove),
};
static int __init  dm9000_init(void)
{
#if defined(CONFIG_ARCH_S3C2410)  //调整DM9000所用的位宽寄存器
       unsigned int oldval_bwscon = *(volatile unsigned int *)S3C2410_BWSCON;
       unsigned int oldval_bankcon4 = *(volatile unsigned int *)S3C2410_BANKCON4;
       *((volatile unsigned int *)S3C2410_BWSCON) =
                     (oldval_bwscon & ~(3<<16)) | S3C2410_BWSCON_DW4_16 | S3C2410_BWSCON_WS4 | S3C2410_BWSCON_ST4;
       *((volatile unsigned int *)S3C2410_BANKCON4) = 0x1f7c;
#endif
       printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION);
       return platform_driver_register(&dm9000_driver); //注册驱动
}

  下面我们重点关注下probe探测函数

static int __devinit dm9000_probe(struct platform_device *pdev)
{
       struct dm9000_plat_data *pdata = pdev->dev.platform_data;  //获取平台数据
       struct board_info *db;   //网卡私有数据
       struct net_device *ndev; //网络设备结构体
       const unsigned char *mac_src;
       int ret = 0;
       int iosize;
       int i;
       u32 id_val;
       //分配并初始化net_device,把board_info作为net_device的私有数据
       ndev = alloc_etherdev(sizeof(struct board_info)); 
       if (!ndev) {
              dev_err(&pdev->dev, "could not allocate device.\n");
              return -ENOMEM;
       }
       //设置继承关系,使pdev->dev作为ndev的parent
       SET_NETDEV_DEV(ndev, &pdev->dev);
       dev_dbg(&pdev->dev, "dm9000_probe()\n");
       db = netdev_priv(ndev);  //获取私有数据
       db->dev = &pdev->dev;  //设置父设备
       db->ndev = ndev;  
       spin_lock_init(&db->lock);  //初始化锁
       mutex_init(&db->addr_lock);  //初始化互斥锁
       INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);  //初始化工作队列
       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); //获取中断号
       if (db->addr_res == NULL || db->data_res == NULL ||
           db->irq_res == NULL) {
              dev_err(db->dev, "insufficient resources\n");
              ret = -ENOENT;
              goto out;
       }
       iosize = resource_size(db->addr_res); //地址寄存器大小
       db->addr_req = request_mem_region(db->addr_res->start, iosize,pdev->name);
       if (db->addr_req == NULL) {
              dev_err(db->dev, "cannot claim address reg area\n");
              ret = -EIO;
              goto out;
       }
       db->io_addr = ioremap(db->addr_res->start, iosize); //物理地址映射为虚拟地址
       if (db->io_addr == NULL) {
              dev_err(db->dev, "failed to ioremap address reg\n");
              ret = -EINVAL;
              goto out;
       }
       iosize = resource_size(db->data_res);
       db->data_req = request_mem_region(db->data_res->start, iosize, pdev->name);
       if (db->data_req == NULL) {
              dev_err(db->dev, "cannot claim data reg area\n");
              ret = -EIO;
              goto out;
       }
       db->io_data = ioremap(db->data_res->start, iosize);  //数据寄存器
       if (db->io_data == NULL) {
              dev_err(db->dev, "failed to ioremap data reg\n");
              ret = -EINVAL;
              goto out;
       }
       ndev->base_addr = (unsigned long)db->io_addr;   //地址寄存器
       ndev->irq       = db->irq_res->start;  //中断号
       dm9000_set_io(db, iosize);  //根据DM9000数据位宽设置读写数据帧的函数指针
       if (pdata != NULL) {
              if (pdata->flags & DM9000_PLATF_8BITONLY)
                     dm9000_set_io(db, 1);
              if (pdata->flags & DM9000_PLATF_16BITONLY) //根据平台数据重新设置数据位宽
                     dm9000_set_io(db, 2);  
              if (pdata->flags & DM9000_PLATF_32BITONLY)
                     dm9000_set_io(db, 4);
              if (pdata->inblk != NULL)  //如果平台数据定义了这些函数,则优先使用这些函数
                     db->inblk = pdata->inblk; //输入
              if (pdata->outblk != NULL)
                     db->outblk = pdata->outblk;  //输出
              if (pdata->dumpblk != NULL)
                     db->dumpblk = pdata->dumpblk;
              db->flags = pdata->flags;
       }
#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL
       db->flags |= DM9000_PLATF_SIMPLE_PHY;
#endif
       dm9000_reset(db);  //对DM9000进行复位
       for (i = 0; i < 8; i++) {  //读芯片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;
              if (id_val == DM9000_ID)
                     break;
              dev_err(db->dev, "read wrong id 0x%08x\n", id_val);
       }
       if (id_val != DM9000_ID) {  //判断是否为0x90000A46
              dev_err(db->dev, "wrong id: 0x%08x\n", id_val);
              ret = -ENODEV;
              goto out;
       }
       id_val = ior(db, DM9000_CHIPR);
       dev_dbg(db->dev, "dm9000 revision 0x%02x\n", id_val);
       switch (id_val) { //设置DM000的具体类型
       case CHIPR_DM9000A:
              db->type = TYPE_DM9000A;
              break;
       case CHIPR_DM9000B:
              db->type = TYPE_DM9000B;
              break;
       default:
              dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\n", id_val);
              db->type = TYPE_DM9000E;
       }
       if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {
              db->can_csum = 1;
              db->rx_csum = 1;
              ndev->features |= NETIF_F_IP_CSUM;
       }
       ether_setup(ndev);  //对net_device中部分成员进行初始化
       ndev->netdev_ops  = &dm9000_netdev_ops; // 网络操作方法
       ndev->watchdog_timeo  = msecs_to_jiffies(watchdog); //看门狗时间
       ndev->ethtool_ops  = &dm9000_ethtool_ops;   //使用户空间支持ethtool这一工具
       db->msg_enable       = NETIF_MSG_LINK;
       db->mii.phy_id_mask  = 0x1f;
       db->mii.reg_num_mask = 0x1f;
       db->mii.force_media  = 0;
       db->mii.full_duplex  = 0;
       db->mii.dev         = ndev;
       db->mii.mdio_read    = dm9000_phy_read;
       db->mii.mdio_write   = dm9000_phy_write;
       mac_src = "eeprom";
       for (i = 0; i < 6; i += 2)  //从E2PROM中读取MAC地址
              dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);
       if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
              mac_src = "platform data";
              memcpy(ndev->dev_addr, pdata->dev_addr, 6);  //从平台数据中读取MAC地址
       }
       if (!is_valid_ether_addr(ndev->dev_addr)) {
              mac_src = "chip";
              for (i = 0; i < 6; i++)
                     ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
       }
       memcpy(ndev->dev_addr, "\x08\x90\x90\x90\x90\x90", 6);  //直接给软MAC地址
       if (!is_valid_ether_addr(ndev->dev_addr))
              dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
                      "set using ifconfig\n", ndev->name);
       platform_set_drvdata(pdev, ndev); //将net_device作为平台数据
       ret = register_netdev(ndev);  //注册net_device
       if (ret == 0)
              printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",
                     ndev->name, dm9000_type_to_char(db->type),
                     db->io_addr, db->io_data, ndev->irq,
                     ndev->dev_addr, mac_src);
       return 0;
out:
       dev_err(db->dev, "not found (%d).\n", ret);
       dm9000_release_board(pdev, db);
       free_netdev(ndev);
       return ret;
}

41/41234>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号