/*
* Davicom DM9000 Fast Ethernet driver for Linux.
* Copyright (C) 1997 Sten Wang
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* (C) Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
*
* Additional updates, Copyright:
* Ben Dooks <ben@simtec.co.uk>
* Sascha Hauer <s.hauer@pengutronix.de>
*/
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/ethtool.h>
#include <linux/dm9000.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/io.h>
#include "dm9000.h"
/* Board/System/Debug information/definition ---------------- */
#define DM9000_PHY 0x40 /* PHY address 0x01 */
#define CARDNAME "dm9000"
#define DRV_VERSION "1.31"
/*
* Transmit timeout, default 5 seconds.
*/
static int watchdog = 5000;
module_param(watchdog, int, 0400);
MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
/*
* Debug messages level
*/
static int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "dm9000 debug level (0-4)");
/* DM9000 register address locking.
*
* The DM9000 uses an address register to control where data written
* to the data register goes. This means that the address register
* must be preserved over interrupts or similar calls.
*
* During interrupt and other critical calls, a spinlock is used to
* protect the system, but the calls themselves save the address
* in the address register in case they are interrupting another
* access to the device.
*
* For general accesses a lock is provided so that calls which are
* allowed to sleep are serialised so that the address register does
* not need to be saved. This lock also serves to serialise access
* to the EEPROM and PHY access registers which are shared between
* these two devices.
*/
/* The driver supports the original DM9000E, and now the two newer
* devices, DM9000A and DM9000B.
*/
enum dm9000_type {
TYPE_DM9000E, /* original DM9000 */
TYPE_DM9000A,
TYPE_DM9000B
};
/* debug code */
#define dm9000_dbg(db, lev, msg...) do { \
if ((lev) < debug) { \
dev_dbg(db->dev, msg); \
} \
} while (0)
static inline struct board_info *to_dm9000_board(struct net_device *dev)
{
return netdev_priv(dev);
}
/* Structure/enum declaration ------------------------------- */
struct board_info {
struct resource *addr_res; /* resources found */
struct resource *data_res;
struct resource *irq_res;
struct resource *addr_req; /* resources requested */
struct resource *data_req;
u16 irq; /* IRQ */
void __iomem *io_addr; /* Register I/O base address */
void __iomem *io_data; /* Data I/O address */
/**
* addr_res/data_res/irq_res 是在probe中通过调用platform_get_resource获取到的板级信息
* addr_req/data_req 是在probe中通过调用request_mem_region申请到的资源
* irq的值从irq_res->start中获取
* io_addr/io_data 申请到资源后,要通过ioremap映射到虚拟地址上才能操作,最后通过iounmap释放
*/
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);
/**
* 有了可操作的虚拟内存地址以后,封装一系列函数对内存区域进行读写操作
* inblk从网卡读数据到驱动程序
* outblk把数据从驱动程序写入到网卡
* dumpblk把垃圾数据从网卡中取出,丢弃
*/
enum dm9000_type type;
struct device *dev; /* parent device */
struct mutex addr_lock; /* phy and eeprom access lock */
spinlock_t lock;
struct net_device *ndev;
struct delayed_work phy_poll;
struct mii_if_info mii;
u32 msg_enable;
/**
* type: 网卡类型 dm9000a/dm9000b/dm9000e
* dev: 赋值为(struct platform_device)->dev, 注释是parent device,这是什么意思?
* 这个值我理解为触发这个驱动执行probe函数的设备的结构体指针
* addr_lock: 读写eeprom和phy时需要用到这个锁
* lock: 读写寄存器时用的锁,避免同时读写同一个寄存器
* ndev: 绑定的网卡设备
* phy_poll: delay_work,绑定dm9000_poll_work,进行硬件载波检查
* mii: (Medium Independent Interface), ethtool/ioctl功能的支持
* msg_enable: ethtool eth0打印出来的,Current message level的值
*/
int irq_wake;
u32 wake_state;
/**
* irq_wake: 赋值为platform_get_irq(pdev, 1), dm9000会产生两类中断,一类是正常的收发包中断
* 另一类是这个wakeup中断,以支持wol功能, dm9000_wol_interrupt中断处理函数打印唤醒的原因,应该是系统起来后再执行
* wol就是Wake-up On LAN, 现在许多新的PC机(尤其是主板集成网卡的机器)都支持远程唤醒功能
* 通过远程唤醒,可以方便管理员实现计算机的自动开启。当然被远程唤醒的主机需要具备以下条件:
* 主板(和网卡)支持WOL功能,有些机器还需要在BIOS中设置开启该功能;
* 主机在关闭状态时需要是插电的,即计算机的主板和网卡为通电状态(此时整个计算机的用电量很小),才能够监听到网络中对自己的“唤醒数据包”;
* 管理员需要记下被唤醒主机网卡的MAC地址(物理地址),这样才能够通过相应的方法唤醒该主机
* wake_state: ethtool eth0打印出来的: Supports Wake-on: pumbg
* Wake-on: g
*/
unsigned int flags;
unsigned int in_timeout:1;
unsigned int in_suspend:1;
unsigned int wake_supported:1;
/**
* in_timeout: 标记位,标记是否发包超时
* in_suspend: 标记位,标记是否处于休眠状态
* wake_supported: 标记位,标记是否支持wol
* flags: 设备的flags,比如是否处于混杂模式(IFF_PROMISC),是否有eeprom(DM9000_PLATF_NO_EEPROM),可见flag可以是通用的,也可以是设备独有的
*/
u16 tx_pkt_cnt;
u16 queue_pkt_len;
u16 queue_start_addr;
u16 queue_ip_summed;
int ip_summed;
u16 dbug_cnt;
u8 io_mode; /* 0:word, 2:byte */
u8 phy_addr;
u8 imr_all;
/**
* tx_pkt_cnt: 当前有几个包待发送,dm9000最多缓存两个包
* queue_pkt_len: 放在队列里的那个待发送包的长度(skb->len)
* queue_start_addr: 预留字段,暂时没用
* queue_ip_summed: 放在队列里的那个待发送包的ip_summed值
* ip_summed: 上一次发送的包的ip_summed值, 与当前要发送的包的ip_summed值对比,检测是不是同一个包的不同分片
* dbug_cnt: 预留字段,暂时没用
* io_mode: I/O mode, see DM9000 Application Notes V1.22 Jun 11, 2004 page 9
* The interrupt status register ISR (REG_FE) can help us to check the I/O mode setting
* 0 0 16-bit mode
* 1 0 8-bit mode
* phy_addr: 预留字段,暂时没用
* imr_all: 在恢复中断中使用,
*/
};
delay work流程如下:
1: probe函数
INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);
设置(struct board_info)->phy_poll.work.work_func_t = dm9000_poll_work
类似schedule_delayed_work(&db->phy_poll, 1);设置的时间到期后,dm9000_poll_work函数会被执行
2: open函数
schedule_delayed_work(&db->phy_poll, 1);
3: dm9000_poll_work函数
if (netif_running(ndev))
dm9000_schedule_poll(db);
4: dm9000_schedule_poll(struct board_info *db)
if (db->type == TYPE_DM9000E)
schedule_delayed_work(&db->phy_poll, HZ * 2);
如果设备是dm9000e,且设备在运行, 那么每两秒跳到第三步执行
如果设备不是dm9000e,那么只有在中断发生时, 在dm9000_interrupt函数
if (db->type != TYPE_DM9000E) {
if (int_status & ISR_LNKCHNG) {
/* fire a link-change request */
schedule_delayed_work(&db->phy_poll, 1);
}
}
然后跳到第三步执行
最后附一张我理解的dm9000流程图