DM9000网卡移植及源码分析

网络框架

网络框架程序功能
Socketnet/ipv4/socket.c提供Socket通信API
传输层(TCP/UDP)net/ipv4/tcp.c udp.c提供TCP/UDP协议
网络层(IP)net/ipv4/ip_x.c网络层协议,增加IP包头
链路层(Ethernet)net/ethernet/eth.c链路层协议,增加DataLinkHeader
驱动dm9000.c定义设备类;设备树匹配检测;初始化空间及注册;实现设备文件操作;

设备树移植

image-20230911103355922

网卡芯片需要注意的引脚:

引脚网卡电路图对应丝印板子丝印
csBUF_Xm0cs1Xm0cs1
INTDM9000_IRQGPX0_6

参考文档:

kernel-4.9/Documentation/devicetree/bindings/net/davicom-dm9000.txt

ethernet@18000000 {
    compatible = "davicom,dm9000";             
    reg = <0x18000000 0x2 0x18000004 0x2>;           
    interrupt-parent = <&gpn>;
    interrupts = <7 4>;
    local-mac-address = [00 00 de ad be ef];
    davicom,no-eeprom;
    reset-gpios = <&gpf 12 GPIO_ACTIVE_LOW>;
    vcc-supply = <&eth0_power>;
}
  1. reg = <0x18000000 0x2 0x18000004 0x2>;

    • 在SROM控制器章节,将网卡看作是cpu内存的扩展,cs线找到对应cpu的地址

  2. interrupt-parent = <&gpn>;

    • 找到中断对应的GPIO,将该GPIO属性填入

  3. interrupts = <7 4>;

    • 7: 中断连接GPIO管脚号

    • 4: 触发方式

驱动源码分析

一、网卡驱动程序框架

在一个网络驱动程序中,一般都提供了一个platform_driver结构变量。

platform_driver结构包括了网卡驱动的相关操作函数,通过platform_driver_register()函数注册到内核设备驱动列表,提供以下函数:、

probe:加载网卡驱动的时候执行,主要用于初始化网卡硬件接口,设置网络接口函数;

remove:卸载网卡驱动的时候执行该函数,用于从系统中注销网络接口函数;

static struct platform_driver dm9000_driver = {
    .driver = {
        .name    = "dm9000",
        .pm  = &dm9000_drv_pm_ops,
        .of_match_table = of_match_ptr(dm9000_of_matches),
    },
    .probe   = dm9000_probe,
    .remove  = dm9000_drv_remove,
};

二、定义设备类

struct board_info {
​
    void __iomem        *io_addr;           //dm9000的寄存器 IO地址
    void __iomem        *io_data;           //dm9000的数据 IO地址
    u16      irq;                           //中断号
​
    u16     tx_pkt_cnt;                     //网卡SRAM中,待网络包的数目
    u16     queue_pkt_len;                  //SKB发送队列中的数据包个数
    u16     queue_start_addr;               //SKB发送队列的其实地址
    u8      phy_addr;                       //物理网卡地址
​
    struct device       *dev;               //统一设备模型
  
    //设备树里的资源
    struct resource     *addr_res; 
    struct resource     *data_res;
​
    struct mutex        addr_lock;          //互斥锁
    spinlock_t          lock;               //自旋锁
    struct delayed_work phy_poll;           //延迟工作队列
    struct net_device   *ndev;              //继承网络设备,结构体过于冗杂
    
    void (*inblk)(void __iomem *port, void *data, int length);      //从dm9000读一块数据
    void (*outblk)(void __iomem *port, void *data, int length);     //向dm9000写一块数据
};

在 board_info结构中,

  • io_addr和 io_data成员变量存放了控制寄存器和数据寄存器地址;

  • tx_pkt_cnt记录待发送数据包个数;

  • queue_pkt_len记录了发送队列中数据包个数;

  • queue_start_addr记录了数据包发送队列的起始地址;

  • phy_addr是网卡的物理地址;

三、驱动初始化 dm9000_probe

1. 获取设备资源

根据设备树,获取设备资源

power = devm_regulator_get(dev, "vcc");     //获取设备树中vcc(电源值)
​
ret = regulator_enable(power);              //电源使能
​
/* of_get_named_gpio_flags函数,获取设备树节点中reset-gpios(复位管脚)对应的GPIO管脚号 */
reset_gpios = of_get_named_gpio_flags(dev->of_node, "reset-gpios", 0, &flags);
​
/* 判断该gpio是否被占用 */
if (gpio_is_valid(reset_gpios)) {
    /*  
    *   申请gpio, 该函数申请GPIO后
    *   可以使用其他函数(gpio_direction_input、gpio_direction_output)来配置 GPIO 引脚的工作模式
    *   可以使用其他函数(gpio_set_value、gpio_get_value)来设置或获取 GPIO 引脚的电平状态
    */
    ret = devm_gpio_request_one(dev, reset_gpios, flags, "dm9000_reset");
    
    /* 设置复位值 */
    gpio_set_value(reset_gpios, 1);
}
​
/* 解析设备树信息 */
pdata = dm9000_parse_dt(&pdev->dev);

2. 分配board_info空间

分配board_info结构体占有的私有资源,在程序中使用 alloc_etherdev()函数分配网卡驱动使用的私有资源

/* 分配网络空间,并初始化 */
ndev = alloc_etherdev(sizeof(struct board_info));   //分配net_device的空间

3. 初始化board_info结构体

分配资源成功后,开始初始化 board_info结构,设置结构的值为0,然后初始化 spin_lock,spin_lock称做自旋锁,是内核中用于临界资源的一种结构。

初始化自旋锁以后,程序分配网络适配器 I/O地址寄存器、数据寄存器用到的内存,并且映射到内核空间。

/* 安装网络设备(把设备对象db里的ndev指向实体) */
db = netdev_priv(ndev);
​
/* 设备对象db赋初值 */
db->dev = &pdev->dev;
db->ndev = ndev;
​
/* 初始化对象db中的自旋锁和互斥锁 */
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);  //DM9000的寄存器地址
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);  //DM9000的数据地址
​
/* 获取中断资源 */
ndev->irq = platform_get_irq(pdev, 0);
​
/*
*   读取硬件地址需要将物理地址转换成寄存器的虚地址
*   1. 计算数据寄存器地址空间
*   2. 申请mem资源
*   3. 映射物理地址
*/
​
/* 地址寄存器初始化 */
iosize = resource_size(db->addr_res);
db->addr_req = request_mem_region(db->addr_res->start, iosize, pdev->name);
db->io_addr = ioremap(db->addr_res->start, iosize);
​
/* 数据寄存器初始化 */
iosize = resource_size(db->data_res);
db->data_req = request_mem_region(db->data_res->start, iosize, pdev->name);
db->io_data = ioremap(db->data_res->start, iosize);
​
/* 设置网络设备I/O地址 */
ndev->base_addr = (unsigned long)db->io_addr;
​
/* 该函数用于数据I/O的方法,io有8位、16位、32位,这里设置了默认的方法 */
dm9000_set_io(db, iosize);

4. 设置数据I/O回调函数

检查是否需要继承系统提供的函数。

初始化网络适配器数据结构后,需要设置网络设备用到的回调函数。

if (pdata != NULL) {
    /* 网卡数据总线有8位、16位、32位等等,将对应的方法绑定到db中 */
    if (pdata->flags & DM9000_PLATF_8BITONLY)
        dm9000_set_io(db, 1);   //8位
​
    if (pdata->flags & DM9000_PLATF_16BITONLY)
        dm9000_set_io(db, 2);   //16位
​
    if (pdata->flags & DM9000_PLATF_32BITONLY)
        dm9000_set_io(db, 4);   //32位
​
    /* 输入输出方法绑定 */
    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;
}

5. 检测ID值

设置DM9000网络适配器芯片ID

/*  网卡复位
*   1. 写(NCR_RST | NCR_MAC_LBK)值,到网卡复位寄存器地址(DM9000_NCR)
*       iow(db, DM9000_NCR, NCR_RST | NCR_MAC_LBK);     
*   2. 读取DM9000_NCR寄存器,看是否复位成功
*       if (ior(db, DM9000_NCR) & 1)                    
*           dev_err(db->dev, "dm9000 did not respond to first reset\n");
*/
dm9000_reset(db);
​
/* 多次读取设备的ID值,确认是DM9000设备 */
/* 如果网卡设备发生故障,先看是否读ID值错误read wrong id 0x%08x */
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);
}
/* 检查芯片 ID是否正确 */
if (id_val != DM9000_ID) {
    dev_err(db->dev, "wrong id: 0x%08x\n", id_val);
    ret = -ENODEV;
    goto out;
}

6. 注册勾连接口函数

设置网卡芯片用到的接口函数

    //设置网卡芯片的接口函数
    ndev->netdev_ops    = &dm9000_netdev_ops;
    ndev->watchdog_timeo    = msecs_to_jiffies(watchdog);
    ndev->ethtool_ops   = &dm9000_ethtool_ops;
​

7. 判断EEPROM值

设置回调函数后,设置MII接口,MII接口不是所有的处理器都支持,设置的目的是供支持MII接口的处理器使用。

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;     // MII方式读函数
db->mii.mdio_write   = dm9000_phy_write;    // MII方式写函数 
​
/* 网卡内部有一个eeprom,可以存储网卡的一些信息参数 */
mac_src = "eeprom";
​
/* 从连接的网卡设备EEPROM中读取节点地址 */
for (i = 0; i < 6; i += 2)
    dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i);
​
//验证EEPROM值是否正确
if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {
    mac_src = "platform data";
    memcpy(ndev->dev_addr, pdata->dev_addr, ETH_ALEN);
}
​
/* 第一次判断EEPROM值 */
if (!is_valid_ether_addr(ndev->dev_addr)) {
    /* 如果是不合法地址,则从DM9000内部寄存器中重新读取 MAC地址 */
    mac_src = "chip";
    for (i = 0; i < 6; i++)
        ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
}
/* 第二次判断EEPROM值 */
if (!is_valid_ether_addr(ndev->dev_addr)) {
    inv_mac_addr = true;
    eth_hw_addr_random(ndev);
    mac_src = "random";

8. 注册网络设备驱动

/* 注册网络设备驱动 */
platform_set_drvdata(pdev, ndev);
    
/* 注册网络设备 */
ret = register_netdev(ndev);
​
/* 成功后打印出来网卡的关键信息 */
if (ret == 0) {
    if (inv_mac_addr)
        dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please set using ip\n",
             ndev->name);
    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;

四、实现文件操作

网络设备的操作函数集,供上层APP调用,比如socket、ifconfig等

static const struct net_device_ops dm9000_netdev_ops = {
    .ndo_open       = dm9000_open,              //打开网卡设备(ifconfig时激活),该函数用于网卡软启动
    .ndo_stop       = dm9000_stop,              //停止网卡设备(ifdown命令设置网卡暂时停止)
    .ndo_start_xmit     = dm9000_start_xmit,    //发送网络包
    .ndo_tx_timeout     = dm9000_timeout,       //超时操作
    .ndo_set_rx_mode    = dm9000_hash_table,
    .ndo_do_ioctl       = dm9000_ioctl,
    .ndo_change_mtu     = eth_change_mtu,
    .ndo_set_features   = dm9000_set_features,
    .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
};

1. 打开网卡设备(dm9000_open)

/*  该函数用于初始化dm9000网卡
*   1. 复位dm9000网卡
*   2. 关闭网卡内部的中断
*   3. 设置网卡设备内部属性
*/
dm9000_init_dm9000(dev);
​
/* 4. 申请中断号 */
if (request_irq(dev->irq, dm9000_interrupt, irq_flags, dev->name, dev))
    return -EAGAIN;
    
/* 5. 打开网卡内部的中断,向中断寄存器写入值 */
dm9000_unmask_interrupts(db);
​
/* 6. 检测网卡媒介状态(Link网卡是否连接),调用netif_carrier_on载波侦听 */
mii_check_media(&db->mii, netif_msg_link(db), 1);
​
/* 7. 启动SKB发送队列,向网卡SRAM写数据 */
netif_start_queue(dev);
​
/* 8. 启动延迟工作队列 -> 周期性(以1为周期)调用db->phy_poll函数,该函数不断调用mii_check_media来检测网卡是否连接 */
schedule_delayed_work(&db->phy_poll, 1);
​

2. 关闭网卡设备(dm9000_stop)

    /* 1. 取消延迟工作队列 */
    cancel_delayed_work_sync(&db->phy_poll);
​
    /* 2. 停止数据包发送工作=队列 */
    netif_stop_queue(ndev);
    
    /* 3. 停止载波监听 */
    netif_carrier_off(ndev);
​
    /* 4. 释放中断号 */
    free_irq(ndev->irq, ndev);
​
    /* 5. 关闭网卡(向网卡设备相应寄存器中写值) */
    dm9000_shutdown(ndev);

3. 超时操作(dm9000_timeout)

DM9000网络驱动会使用一种定时机制或计数器来跟踪网络传输的时间。当发送或接收数据包时,会设置一个超时时间戳,然后在后续的处理中检查是否已经超过了这个时间戳,如果超过了就认为发生了超时操作。

db->in_timeout 是超时的锁状态,用来线程间互斥

/* 1. 自旋锁互斥保护 */
spin_lock_irqsave(&db->lock, flags);
db->in_timeout = 1;
reg_save = readb(db->io_addr);
​
/* 2. 停止发送队列 */
netif_stop_queue(dev);
/* 3. 重新初始化网卡(软重启复位网卡) */
dm9000_init_dm9000(dev);
/* 4. 取消中断屏蔽 */
dm9000_unmask_interrupts(db);
​
/* 5. 查找并返回指向该网络队列的指针 */
netif_trans_update(dev);
​
/* 6. 唤醒发送队列 */
netif_wake_queue(dev);
​
​
writeb(reg_save, db->io_addr);
db->in_timeout = 0;
/* 7. 关闭自旋锁互斥 */
spin_unlock_irqrestore(&db->lock, flags);

4. 发送网络包(dm9000_start_xmit)

网卡驱动程序需要向内核提供两个发送数据包的回调函数,一个用于发送数据包,一个用于数据包发送完毕后的处理。

​
/* 1. 发送条件检测,SRAM空间只能容纳一个包,当待发送包大于1时,返回BUSY */
if (db->tx_pkt_cnt > 1)
    return NETDEV_TX_BUSY;
​
/* 2. 自旋锁保护 */
spin_lock_irqsave(&db->lock, flags);
​
/* 3. 写初始化寄存器 */
writeb(DM9000_MWCMD, db->io_addr);
​
/* 4. 将skb中数据块写入网卡RAM中 */
(db->outblk)(db->io_data, skb->data, skb->len); 
dev->stats.tx_bytes += skb->len;
​
/* 5. 记录写入网卡SRAM中待发送数据包的数量 */
db->tx_pkt_cnt++;
​
/*  注:
*   a. 网卡的SRAM中,有两个buffer区域,buffer1存放当前发送数据,buffer2存放等待发送数据包(用tx_pkt_cnt记录数据包数量)
*   b. 判断当前待发送数据包数量,本来应该是0,加上这个数据包是1,如果不等于1,则说明状态错误
*/  
if (db->tx_pkt_cnt == 1) {
    /* 6. 启动网卡发送数据 */
    dm9000_send_packet(dev, skb->ip_summed, skb->len);
} else {
    /* 若网卡RAM不等于,则停止发送队列,不要往网卡RAM中写数据了  */
    db->queue_pkt_len = skb->len;
    db->queue_ip_summed = skb->ip_summed;
    netif_stop_queue(dev);  
}
​
/* 7. 释放互斥锁 */
spin_unlock_irqrestore(&db->lock, flags);
​
/* 8. 释放掉当前skb缓冲区,因为上面已经将该缓冲区的数据拷入到SRAM中了,并且释放掉不意 */
dev_consume_skb_any(skb);

5. 数据包发送后续处理函数(dm9000_tx_done)

数据包发送完毕后,会触发一个中断,内核会调用后续的处理函数 dm9000_tx_done()

if (tx_status & (NSR_TX2END | NSR_TX1END)) {
        /* 1. 数据包发送完成,要把当前数量减一 */
        db->tx_pkt_cnt--;
        dev->stats.tx_packets++;
​
        /* 2. 判断释放发送完毕,看db的发送标志 
        *   db->msg_enable & 0x0400
        */
        if (netif_msg_tx_done(db))
            dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);
​
        /* 3.如果数据包未发送完,立马启动发送 */
        if (db->tx_pkt_cnt > 0)
            dm9000_send_packet(dev, db->queue_ip_summed,
                       db->queue_pkt_len);
        /* 4. 如果RAM发送队列=0,则说明RAM缓存区空出来了,可以将下一个数据包放进来,唤醒发送队列 */
        netif_wake_queue(dev);
    }

6. 接收网络包(dm9000_rx)

收到DM9000接收数据包中断后被内核调用。dm9000_rx()函数使用了一个自定义的dm9000_rxhdr结构,该结构与DM9000网络控制器提供的数据包接收信息对应。

struct dm9000_rxhdr rxhdr;  //用于描述DM9000数据包结构体,即包头结构体
struct sk_buff *skb;
u8 rxbyte;                  //存放网络控制器状态
u8 *rdptr;                  //存放读取数据包
bool GoodPacket;
int RxLen;                  //存放接收数据包的长度
​
/*  while (rxbyte & DM9000_PKT_RDY); 
*   该循环会读取DM9000的DM9000_PKT_RDY,检测是否还有准备读取的数据包,如果没有则循环结束
*/
do {
    /* 1. 先读取第一个字节,如果不为DM9000_PKT_RDY,则说明数据包未准备好,则跳出循环 */
    ior(db, DM9000_MRCMDX);
​
    /* 2. 读取网络控制器状态 */
    rxbyte = readb(db->io_data);
​
    /* 3. 判断状态是否正确 */
    if (rxbyte & DM9000_PKT_ERR) {
        dev_warn(db->dev, "status check fail: %d\n", rxbyte);
        iow(db, DM9000_RCR, 0x00);  /* 状态错误时,停止网络控制器 */
        return;
    }
​
    /* 4. 检测是否有数据需要读取 */
    if (!(rxbyte & DM9000_PKT_RDY))
        return;
​
    /* A packet ready now  & Get status/length */
    GoodPacket = true;
    /* 5. 向控制器发起读命令 */
    writeb(DM9000_MRCMD, db->io_addr);
​
    /* 6.读取包头,网卡数据包的前四个字节(有效标志、接收状态、数据长度。 把他们存在结构体rxhdr中) */
    (db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr));
​
    RxLen = le16_to_cpu(rxhdr.RxLen);
​
    if (netif_msg_rx_status(db))
        dev_dbg(db->dev, "RX: status %02x, length %04x\n",
            rxhdr.RxStatus, RxLen);
​
    /* 7. 判断数据包是否小于64字节,正常情况小于64字节会填充到64字节,小于则是损坏的包 */
    if (RxLen < 0x40) {
        GoodPacket = false;
        if (netif_msg_rx_err(db))
            dev_dbg(db->dev, "RX: Bad Packet (runt)\n");
    }
​
    /* 8. 判断数据包是否超过1536字节,不能超过RAM大小 */
    if (RxLen > DM9000_PKT_MAX) {
        dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen);
    }
​
    /* 9. 检查接收状态是否出错 */
    /*
    *   RSR_FOE:    表示接收时发生了帧溢出错误。
    *   RSR_CE:     表示接收时发生了 CRC 错误。
    *   RSR_AE:     表示接收时发生了地址错误。
    *   RSR_PLE:    表示接收时发生了数据包长度错误。
    *   RSR_RWTO:   表示接收时发生了读等待超时错误。
    *   RSR_LCS:    表示链路状态改变。
    *   RSR_RF:     表示接收到了数据帧
    */
    if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |
                  RSR_PLE | RSR_RWTO |
                  RSR_LCS | RSR_RF)) {
        GoodPacket = false;
        //FIFO 错误
        if (rxhdr.RxStatus & RSR_FOE) {
            if (netif_msg_rx_err(db))
                dev_dbg(db->dev, "fifo error\n");
            dev->stats.rx_fifo_errors++;
        }
        //CRC 错误
        if (rxhdr.RxStatus & RSR_CE) {
            if (netif_msg_rx_err(db))
                dev_dbg(db->dev, "crc error\n");
            dev->stats.rx_crc_errors++;
        }
        //包长度错误
        if (rxhdr.RxStatus & RSR_RF) {
            if (netif_msg_rx_err(db))
                dev_dbg(db->dev, "length error\n");
            dev->stats.rx_length_errors++;
        }
    }

    /* 10.如果条件都满足,将网卡的RAM中数据包,拷到SK_BUFFER中,并通知上层协议栈取走SKB */
    /* 如果是一个好的数据包则分配skb内存,并且缓存足够时,将数据读入SK_BUFFER缓存中 */
    if (GoodPacket && ((skb = netdev_alloc_skb(dev, RxLen + 4)) != NULL)) {
        skb_reserve(skb, 2);
        rdptr = (u8 *) skb_put(skb, RxLen - 4);
​
        /* 从RX SRAM中读取接收到的数据包 */
        (db->inblk)(db->io_data, rdptr, RxLen);     // 将网卡内部RAM的数据包读取到SKB中
        dev->stats.rx_bytes += RxLen;               // 更新接收字节数统计
​
        /* 传递给上层协议栈 */
        skb->protocol = eth_type_trans(skb, dev);   //把SKB数据包丢给上层协议栈
​
        /* 这里通知上层协议栈取走解析后的SKB数据包 */
        netif_rx(skb);
        /* 增加接收数据包计数 */
        dev->stats.rx_packets++;
​
    } else {
        /* 需要丢弃数据包的数据 */
        (db->dumpblk)(db->io_data, RxLen); // 调用函数将数据包的数据从网卡内部RAM中丢弃
    }
} while (rxbyte & DM9000_PKT_RDY); 

7. 中断处理函数(dm9000_interrupt)

网络设备驱动需要提供中断处理函数和定时处理函数供内核使用。

  • 中断处理函数当网络控制器向CPU发出中断后,由内核中断处理函数调用。

  • 定时器处理函数是由内核的一个定时器周期的调用。

  • 网卡中断处理函数,有三个触发时机

    1. 收到数据包时

    1. 数据包发送完

    1. 连接状态改变

​
/* 1. 开启自旋锁 */
spin_lock_irqsave(&db->lock, flags);
​
/* 2. 保存当前中断寄存器的值 */
reg_save = readb(db->io_addr);
​
/* 3. 中断屏蔽(操作DM9000的IMR寄存器,屏蔽所有中断) */
dm9000_mask_interrupts(db); 
​
/* 4. 获取dm9000的中断状态寄存器(ISR),判断是什么操作触发了中断 */
int_status = ior(db, DM9000_ISR);   /* Got ISR */
iow(db, DM9000_ISR, int_status);    /* Clear ISR status */
​
if (netif_msg_intr(db))
    dev_dbg(db->dev, "interrupt status %02x\n", int_status);
​
/* 检测是否是接收数据时的中断 */
if (int_status & ISR_PRS)
    dm9000_rx(dev);
​
/* 检测是否数据包发送完成的中断 */
if (int_status & ISR_PTS)
    dm9000_tx_done(dev, db);
​
/* 检测是否连接状态发生的中断 */
if (db->type != TYPE_DM9000E) {
    if (int_status & ISR_LNKCHNG) {
        /* fire a link-change request */
        schedule_delayed_work(&db->phy_poll, 1);    //启动网卡连接检测
    }
}
​
/* 5. 解除中断屏蔽 */
dm9000_unmask_interrupts(db);
​
/* 6. 恢复中断处理前中断寄存器的值 */
writeb(reg_save, db->io_addr);
​
/* 7. 解除自旋锁 */
spin_unlock_irqrestore(&db->lock, flags);
​
return IRQ_HANDLED;

### 参考文章

【驱动】DM9000网卡驱动分析_dm9000q驱动_一口Linux的博客-CSDN博客

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值