Mini2440 DM9000 驱动分析(三)
现在开始逐步分析dm9000驱动的probe方法
具体过程见代码中添加的注释
/*
* Search DM9000 board, allocate space and register it
*/
static int __devinit
dm9000_probe(struct platform_device *pdev)
{
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;
/* Init network device */
/* 为net_device分配内存,同时这里会为board_info留下了一席之地,追踪alloc_etherdev的实现就可以明了 */
ndev = alloc_etherdev(sizeof(struct board_info));
if (!ndev) {
dev_err(&pdev->dev, "could not allocate device.\n");
return -ENOMEM;
}
/* 这里使得ndev->dev.parent=&pdev->dev */
SET_NETDEV_DEV(ndev, &pdev->dev);
dev_dbg(&pdev->dev, "dm9000_probe()\n");
/* 上面已经说过,在为ndev申请内存的时候,同时也为board_info也暂留了一席之地,也也就是说申请的
实际内存大小是sizeof(net_device)+sizeof(board_info),下面把db指针指向这块特意申请的内存 */
/* setup board info structure */
db = netdev_priv(ndev);
/* 这里说明ndev->dev.parent=db->dev,也就是说board_info中的dev是ndev的父设备 */
db->dev = &pdev->dev;
/* board_info里面或有一个子结构就是net_device,这里也印证了net_device是board_info的一个子设备 */
db->ndev = ndev;
/* 初始化自旋锁 */
spin_lock_init(&db->lock);
/* 初始化互斥锁 */
mutex_init(&db->addr_lock);
/* 初始化等待队列 */
INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
/* 从这里开始获取addr,data寄存器的物理地址,还有irq的物理地址 */
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;
}
/* fill in parameters for net-dev structure */
ndev->base_addr = (unsigned long)db->io_addr;
ndev->irq = db->irq_res->start;
/* 这里通过判断寄存器地址所占的字节数来确定我们使用哪种通信方式
dm9000_set_io的具体实现在下面代码中给出 */
/* ensure at least we have a default set of IO routines */
dm9000_set_io(db, iosize);
/* 下面的方法用来检查系统是否已经默认的为我们设置好默认的通信方法,
如果系统有提供默认的通信方式设置,则使用系统提供的通信方法 */
/* check to see if anything is being over-ridden */
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_NCR寄存器是Network Control Register,将寄存器的第零为设为1,
表明Software reset and auto clear after 10us
dm9000_reset实现在下面给出 */
dm9000_reset(db);
/* try multiple times, DM9000 sometimes gets the read wrong */
for (i = 0; i < 8; i++) {
/* 读取VIDL寄存器的低八位数据,保存在id_val的7-0这八位 */
id_val = ior(db, DM9000_VIDL);
/* 读取VIDL寄存器的高八位数据,保存在id_val的15-8这八位 */
id_val |= (u32)ior(db, DM9000_VIDH) << 8;
/* 读取PIDL寄存器的低八位数据,保存在id_val的23-16这八位 */
id_val |= (u32)ior(db, DM9000_PIDL) << 16;
/* 读取PIDL寄存器的高八位数据,保存在id_val的31-24这八位 */
id_val |= (u32)ior(db, DM9000_PIDH) << 24;
/* 说明找到了我们相匹配的网卡设备id=0x90000A46 */
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 */
/* SHIPR寄存器的低八位保存着DM9000的type信息,这里读取寄存器中的值,获取到type信息 */
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;
}
/* dm9000a/b are capable of hardware checksum offload */
if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) {
db->can_csum = 1;
db->rx_csum = 1;
ndev->features |= NETIF_F_IP_CSUM;
}
/* from this point we assume that we have found a DM9000 */
/* driver system function */
/* 从这里开始对net_device和board_info两个结构,做必要的初始化 */
ether_setup(ndev);
ndev->netdev_ops = &dm9000_netdev_ops;
ndev->watchdog_timeo = msecs_to_jiffies(watchdog);
ndev->ethtool_ops = &dm9000_ethtool_ops;
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;
/* 下面的方法首先通过eeprom方式读取mac地址,
如果使用eeprom方式读取失败,这是会吧platform传入的mac地址作为网络的mac地址,
接着进行检查,如果platform没有给定默认的mac地址,
则会通过读取PAR寄存器所指向的连续6个地址,获取到mac的六位的值,
如果还是没有获取到mac地址,则通知用户通过ifconfig设置mac地址 */
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);
if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
mac_src = "platform data";
memcpy(ndev->dev_addr, pdata->dev_addr, 6);
}
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);
}
memcpy(ndev->dev_addr, "\x08\x90\x90\x90\x90\x90", 6);
if (!is_valid_ether_addr(ndev->dev_addr))
dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please "
"set using ifconfig\n", ndev->name);
/* 这里把net_device设置进入pdev,之后可以get到net_device进行使用,很重要*/
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;
}
dm9000_set_io
/* dm9000_set_io
*
* select the specified set of io routines to use with the
* device
*/
static void dm9000_set_io(struct board_info *db, int byte_width)
{
/* use the size of the data resource to work out what IO
* routines we want to use
*/
switch (byte_width) {
case 1:
db->dumpblk = dm9000_dumpblk_8bit;
db->outblk = dm9000_outblk_8bit;
db->inblk = dm9000_inblk_8bit;
break;
case 3:
dev_dbg(db->dev, ": 3 byte IO, falling back to 16bit\n");
case 2:
db->dumpblk = dm9000_dumpblk_16bit;
db->outblk = dm9000_outblk_16bit;
db->inblk = dm9000_inblk_16bit;
break;
case 4:
default:
db->dumpblk = dm9000_dumpblk_32bit;
db->outblk = dm9000_outblk_32bit;
db->inblk = dm9000_inblk_32bit;
break;
}
}
dm9000_reset
/* DM9000 network board routine ---------------------------- */
static void
dm9000_reset(board_info_t * db)
{
dev_dbg(db->dev, "resetting device\n");
/* RESET device */
writeb(DM9000_NCR, db->io_addr);
udelay(200);
writeb(NCR_RST, db->io_data);
udelay(200);
}
probe过程完成,接下来逐个分析driver中的其他方法实现