Linux驱动修炼之道-DM9000A网卡驱动框架源码分析(中)

努力成为linux kernel hacker的人李万鹏原创作品,为梦而战。转载请标明出处

http://blog.youkuaiyun.com/woshixingaaa/archive/2011/06/18/6552910.aspx

网络设备的初始化:

通过模块的加载函数看出DM9000A的驱动是以平台驱动的形式注册进内核的,下边是模块的加载函数:

static int __init dm9000_init(void) { printk(KERN_INFO "%s Ethernet Driver, V%s/n", CARDNAME, DRV_VERSION); return platform_driver_register(&dm9000_driver); }

下边是平台驱动结构体:

static struct platform_driver dm9000_driver = { .driver = { .name = "dm9000", .owner = THIS_MODULE, }, .probe = dm9000_probe, .remove = __devexit_p(dm9000_drv_remove), .suspend = dm9000_drv_suspend, .resume = dm9000_drv_resume, };

下面来分析probe函数,用来执行分配的内核函数是alloc_netdev,函数原型是:

struct net_device *alloc_netdev(int sizeof_priv, const char *name, void (*setup)(struct net_device*));
这里的sizeof_priv是驱动程序私有数据区的大小;这个区成员和net_device结构一同分配给网络设备。实际上,他们都处于一大块内存中,但是驱动程序不需要知道这些。name是接口的名字,其在用户空间可见;这个名字可以使用类似printf中%d的格式,内核将用下一个可用的接口号替代%d,最后,setup是一个初始化函数,用来设置net_device结构剩余的部分。网络子系统对alloc_netdev,为不同种类的接口封装了许多函数。最常用的是alloc_etherdev,它定义在linux/etherdevice.h中:

struct net_device *alloc_etherdev(int sizeof_priv);

该函数使用eth%d的形式指定分配给网络设备的名字。它提供了自己的初始化函数(ether_setup),用正确的值为以太网设备设置net_device中的许多成员。那么在DM9000A中这个私有数据成员是什么呢,看下边的结构:

/* Structure/enum declaration ------------------------------- */ typedef struct board_info { void __iomem *io_addr; /* Register I/O base address */ void __iomem *io_data; /* Data I/O address */ u16 irq; /* IRQ */ u16 tx_pkt_cnt; u16 queue_pkt_len; u16 queue_start_addr; u16 dbug_cnt; u8 io_mode; /* 0:word, 2:byte */ u8 phy_addr; u8 imr_all; unsigned int flags; unsigned int in_suspend :1; int debug_level; enum dm9000_type type; void (*inblk)(void __iomem *port, void *data, int length); void (*outblk)(void __iomem *port, void *data, int length); void (*dumpblk)(void __iomem *port, int length); struct device *dev; /* parent device */ struct resource *addr_res; /* resources found */ struct resource *data_res; struct resource *addr_req; /* resources requested */ struct resource *data_req; struct resource *irq_res; struct mutex addr_lock; /* phy and eeprom access lock */ struct delayed_work phy_poll; struct net_device *ndev; spinlock_t lock; struct mii_if_info mii; u32 msg_enable; } board_info_t;

这个struct board_info就是那个私有数据,用来保存芯片相关的一些私有信息。

下面是probe函数的实现:

/* * Search DM9000 board, allocate space and register it */ static int __devinit dm9000_probe(struct platform_device *pdev) { /*获得平台数据,这个应该在platform_device那边指定了*/ struct dm9000_plat_data *pdata = pdev->dev.platform_data; struct board_info *db; /* Point a board information structure */ struct net_device *ndev; const unsigned char *mac_src; int ret = 0; int iosize; int i; u32 id_val; /*分配以太网的网络设备*/ ndev = alloc_etherdev(sizeof(struct board_info)); if (!ndev) { dev_err(&pdev->dev, "could not allocate device./n"); return -ENOMEM; } /*#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))*/ SET_NETDEV_DEV(ndev, &pdev->dev); dev_dbg(&pdev->dev, "dm9000_probe()/n"); /*设置struct board_info为ndev的私有数据*/ db = netdev_priv(ndev); memset(db, 0, sizeof(*db)); db->dev = &pdev->dev; db->ndev = ndev; spin_lock_init(&db->lock); mutex_init(&db->addr_lock); /*提交一个任务给一个工作队列,你需要填充一个work_struct结构db->phy_poll*/ INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work); /*获取IO内存和中断资源*/ 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; } /*映射到内核,并获得IO内存的虚拟地址,ioremap完成页表的建立,不同于vmalloc,但是,它实际上不分配内存*/ iosize = res_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 = res_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; /*设置默认的IO函数*/ dm9000_set_io(db, iosize); /*如果平台数据不为空*/ if (pdata != NULL) { /* check to see if the driver wants to over-ride the * default IO width */ 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); /* check to see if there are any IO routine * over-rides */ 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复位*/ dm9000_reset(db); /*读取Vendor ID Register,Product ID Register中的值,与0x90000A46比较,如果相等,则说明是DM9000*/ /* try multiple times, DM9000 sometimes gets the read wrong */ for (i = 0; i < 8; i++) { 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) { dev_err(db->dev, "wrong id: 0x%08x/n", id_val); ret = -ENODEV; goto out; } /* Identify what type of DM9000 we are working on */ /*读取Chip Revision Register中的值*/ id_val = ior(db, DM9000_CHIPR); dev_dbg(db->dev, "dm9000 revision 0x%02x/n", id_val); switch (id_val) { 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; } /* from this point we assume that we have found a DM9000 */ /* driver system function */ /*设置部分net_device字段*/ 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->set_multicast_list = &dm9000_hash_table; /*对ethtool支持的相关声明可在<linux/ethtool.h>中找到。它的核心是一个ethtool_ops类型的结构,里边包含一个全部的24个不同的方法来支持ethtool*/ ndev->ethtool_ops = &dm9000_ethtool_ops; ndev->do_ioctl = &dm9000_ioctl; #ifdef CONFIG_NET_POLL_CONTROLLER ndev->poll_controller = &dm9000_poll_controller; #endif 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地址的源是eeprom*/ mac_src = "eeprom"; /* try reading the node address from the attached EEPROM */ for (i = 0; i < 6; i += 2) dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i); /*如果从eeprom中读取的地址无效,并且私有数据不为空,从platform_device的私有数据中获取dev_addr*/ if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) { mac_src = "platform data"; memcpy(ndev->dev_addr, pdata->dev_addr, 6); } /*如果地址依然无效,从PAR:物理地址(MAC)寄存器(Physical Address Register)中读取*/ if (!is_valid_ether_addr(ndev->dev_addr)) { /* try reading from mac */ mac_src = "chip"; for (i = 0; i < 6; i++) ndev->dev_addr[i] = ior(db, i+DM9000_PAR); } /*查看以太网网卡设备地址是否有效*/ if (!is_valid_ether_addr(ndev->dev_addr)) dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please " "set using ifconfig/n", ndev->name); /*将ndev保存到pdev->dev->driver_data中*/ platform_set_drvdata(pdev, ndev); /*一切都初始化好后,注册网络设备*/ ret = register_netdev(ndev); 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; }

下边看看挂起和唤醒函数:

挂起函数完成了设置挂起标志,并没有真正把设备移除而只是设置了移除标志,复位PHY,停止PHY,禁止所有中断,禁止接受引脚。

static int dm9000_drv_suspend(struct platform_device *dev, pm_message_t state) { struct net_device *ndev = platform_get_drvdata(dev); board_info_t *db; if (ndev) { db = netdev_priv(ndev); db->in_suspend = 1; if (netif_running(ndev)) { netif_device_detach(ndev); dm9000_shutdown(ndev); } } return 0; }

唤醒函数完成了复位dm9000,初始化dm9000,标记设备为attached,清除挂起标志。

static int dm9000_drv_resume(struct platform_device *dev) { struct net_device *ndev = platform_get_drvdata(dev); board_info_t *db = netdev_priv(ndev); if (ndev) { if (netif_running(ndev)) { dm9000_reset(db); dm9000_init_dm9000(ndev); netif_device_attach(ndev); } db->in_suspend = 0; } return 0; }

网络设备的打开与释放:

首先来看这个open函数:

static int dm9000_open(struct net_device *dev) { board_info_t *db = netdev_priv(dev); unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK; if (netif_msg_ifup(db)) dev_dbg(db->dev, "enabling %s/n", dev->name); /* If there is no IRQ type specified, default to something that * may work, and tell the user that this is a problem */ if (irqflags == IRQF_TRIGGER_NONE) dev_warn(db->dev, "WARNING: no IRQ resource flags set./n"); irqflags |= IRQF_SHARED; /*注册中断处理函数*/ if (request_irq(dev->irq, &dm9000_interrupt, irqflags, dev->name, dev)) return -EAGAIN; /* Initialize DM9000 board */ /*复位DM9000*/ dm9000_reset(db); /*初始化DM9000的寄存器*/ dm9000_init_dm9000(dev); /* Init driver variable */ db->dbug_cnt = 0; /*检查链路载波状况*/ mii_check_media(&db->mii, netif_msg_link(db), 1); /*启动发送队列*/ netif_start_queue(dev); /*之前在probe函数中调用 INIT_DELAYED_WORK初始化了工作队列,并关联了一个操作函数dm9000_poll_work(),此时运行dm9000_schedule_poll来调用这个函数*/ dm9000_schedule_poll(db); return 0; }

然后是stop函数:

static int dm9000_stop(struct net_device *ndev) { board_info_t *db = netdev_priv(ndev); if (netif_msg_ifdown(db)) dev_dbg(db->dev, "shutting down %s/n", ndev->name); /*杀死延时工作队列phy_poll*/ cancel_delayed_work_sync(&db->phy_poll); /*停止发送队列*/ netif_stop_queue(ndev); /*通知内核链路失去连接*/ netif_carrier_off(ndev); /* free interrupt */ free_irq(ndev->irq, ndev); /*关闭DM9000*/ dm9000_shutdown(ndev); return 0; }
复位PHY,停止PHY,禁止所有中断,禁止接收引脚。

static void dm9000_shutdown(struct net_device *dev) { board_info_t *db = netdev_priv(dev); /* RESET device */ dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */ iow(db, DM9000_GPR, 0x01); /* Power-Down PHY */ iow(db, DM9000_IMR, IMR_PAR); /* Disable all interrupt */ iow(db, DM9000_RCR, 0x00); /* Disable RX */ }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值