linux 网络驱动

谨以此文纪念过往的岁月

一.前言
在linux中网络驱动也是一个大头,如何去理解网络驱动是作为一个linux驱动工程师必备的技能。不过同样的设备,在不同人的手中会有不同的效果,其原因就在于驱动的好与否。

二.设备注册
学习网络的驱动与学习普通cdev驱动一样,都是学习其模板,然后再创造学习。在学习网络驱动过程中,我们忽略其对硬件的具体操作,这样会更具有通用性。以dm9000A为例。网络驱动亦如usb驱动一样,其内核将许多工作都完成了。DM9000A认采用了platform

bus的办法来实现设备与驱动的匹配。在前文中也说,其设备所属的设备类型与设备的类型并没有关系,就如usb总线一下,其usb键盘属于cdev,而usb

storage则属于block设备,但是他们都是usb总线设备。网络设备也是一样的。所以这里的设备采用platform总线反而更加的直观。对于platform设备的注册和驱动的注册咱们就不说了。不过在这之前还需要说明一点东西,即是内存映射,该映射非彼映射。以s3c6410和DM9000A为例,DM9000A的data和address线连接在cpu的srom1的接口。在里面就需要理解一个宏定义
#define S3C64XX_PA_DM9000    (0x18000000)和#define DM9000_CMD    0X04
第一个就是DM9000的物理地址为什么是0x18000000,因为dm9000的data和addr是连接在srom1的接口,而srom1的起始地址为0x18000000,这里是用于外设的地址。
第二个是cmd地址为什么是0x04,因为dm9000的cmd与data区分的那个引脚连接在srom1的addr[2]上,故cmd的地址为0x04,如果学过数电这个就很好懂的。
我们就从设备的探测开始来开始我们的网络驱动之旅。
2.1 设备探测
在网络设备探测中,其各个不同的设备的硬件初始化是不同的,不过其本质还是一样一样的。
void dm9000_probe(struct platform_device *pdev)
{
            struct net_device *ndev;  --这个东西可是核心
            ndev = alloc_etherdev(sizeof (struct private_data)); --后面的参数其实是ndev的私用数据
  
         SET_NETDEV_DEV(ndev, &pdev->dev);            --#define
SET_NETDEV_DEV(net, pdev)    ((net)->dev.parent = (pdev))
设置ndev的父设备为pdev
            ether_setup(ndev);    --函数的核心是初始化ndev的成员。
            --下面是设置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;     --设置组播地址。
 
        db->msg_enable       = NETIF_MSG_LINK;
        db->mii.phy_id_mask  = 0x1f;                   --这个就是mii接口
        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;
        platform_set_drvdata(pdev, ndev);              --将ndev设置为pdev的私用函数,留着以后卸载时用
        register_netdev(ndev);                         --注册ndev
}
probe中对于ndev的操作分为三部 开辟ndev -> 初始化ndev ->注册ndev,有没有发现这个过程怎么这么类似于其他的驱动模型呢。这里面的核心是ndev
2.2 网络设备的打开和释放
在open中,往往都会去申请中断,对硬件进行复位,并且激活设备发送队列,以dm9000_open为例
static int dm9000_open(struct net_device *dev)
{
    if (request_irq(dev->irq, &dm9000_interrupt, DM9000_IRQ_FLAGS, dev->name, dev)) --申请中断资源
        return -EAGAIN;
        
    mii_check_media(&db->mii, netif_msg_link(db), 1);  --其核心在于此,检测mii的接口状态
    netif_start_queue(dev);             --激活设备发送队列

return 0;
}
在close中则相反,主要是资源的释放和停止设备发送队列。
static int dm9000_stop(struct net_device *ndev)
{
    board_info_t *db = (board_info_t *) ndev->priv;
    netif_stop_queue(ndev);
    netif_carrier_off(ndev);

free_irq(ndev->irq, ndev);

return 0;
}
2.3 数据发送
将从上层传入的数据发送的media中。
static int dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    unsigned long flags;
    board_info_t *db = (board_info_t *) dev->priv;

if (db->tx_pkt_cnt > 1)         --包数不超过2个
        return 1;

spin_lock_irqsave(&db->lock, flags);
    writeb(DM9000_MWCMD, db->io_addr);
    (db->outblk)(db->io_data, skb->data, skb->len);
    dev->stats.tx_bytes += skb->len;
    db->tx_pkt_cnt++;
    if (db->tx_pkt_cnt == 1) {
        iow(db, DM9000_TXPLL, skb->len & 0xff);
        iow(db, DM9000_TXPLH, (skb->len >> 8) & 0xff);

iow(db, DM9000_TCR, TCR_TXREQ);

dev->trans_start = jiffies;    --保存时间戳
    } else {
        db->queue_pkt_len = skb->len;
        netif_stop_queue(dev);
    }

spin_unlock_irqrestore(&db->lock, flags);
    dev_kfree_skb(skb);

return 0;
}

2.4 中断处理
对于media每次接送或发送完成一帧数据后都会产生一个中断,根据中断flags来判断是发送完成还是接受完成。
static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
{
    if (int_status & ISR_PRS)     --接受数据中断。
        dm9000_rx(dev);
    if (int_status & ISR_PTS)
        dm9000_tx_done(dev, db);
}

static void dm9000_tx_done(struct net_device *dev, board_info_t * db)
{
        netif_wake_queue(dev);  --唤醒等待队列
}

2.5 数据接受
对于数据接受而言,其实就是将数据从media的缓冲区读出,然后提交给上层。读取数据的真正核心是下面的代码
static void dm9000_rx(struct net_device *dev)
{
    skb = dev_alloc_skb(RxLen + 4));    --分配一个skb
    skb_reserve(skb, 2);                --保留2两个字节
    rdptr = (u8 *) skb_put(skb, RxLen - 4);  --硬件读取数据
    (db->inblk)(db->io_data, rdptr, RxLen);
    dev->stats.rx_bytes += RxLen;
    skb->protocol = eth_type_trans(skb, dev); --获取上册协议类型
    netif_rx(skb);                     --向上层提交数据包
    dev->stats.rx_packets++;
}

2.6 在驱动中会设定一个定时器
该定时器的功能就是定时查询mii的状态。

三.总结
网络驱动的核心在于ndev和skb,需要好好去理解。
这次的网络驱动学习的很是粗糙,因为网络驱动是一个很大的部分,需要慢慢去细嚼其中的每一点。前途是光明的,不过道路是曲折的。

linux 网络设备驱动的更多相关文章

  1. Linux网络设备驱动架構學習(三)

    Linux网络设备驱动架構學習(三) 接下來會從以下幾個方面介紹網絡設備驅動的編寫流程: 1.網絡設備的註冊與註銷 2.網絡設備的初始化 3.網絡設備的打開與釋放 4.網絡數據發送流程 5.網絡數據接 ...

  2. linux网络设备驱动

    Linux网络设备驱动 Linux网络驱动程序的体系结构可划分为4个层次.Linux内核源代码中提供了网络设备接口及以网络子系统的上层的代码,移植特定网络硬件的驱动程序的主要工作就是完成设备驱动功能层 ...

  3. Linux网络设备驱动架構學習(二)

    Linux网络设备驱动架構學習(二) 接下來會從以下幾個方面介紹網絡設備驅動的編寫流程: 1.網絡設備的註冊與註銷 2.網絡設備的初始化 3.網絡設備的打開與釋放 4.網絡數據發送流程 5.網絡數據接 ...

  4. Linux网络设备驱动(一) _驱动模型

    Linux素来以其强大的网络功能著名,同时, 网络设备也作为三大设备之一, 成为Linux驱动学习中必不可少的设备类型, 此外, 由于历史原因, Linux并没有强制对网络设备贯彻其"一切皆 ...

  5. Linux网络设备驱动架构

    Linux网络设备驱动程序体系结构分为四层:网络协议接口层.网络设备接口层.提供实际功能的设备驱动层以及网络设备与媒介层. (1)网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议是AR ...

  6. Linux网络设备驱动 _驱动模型

    Linux素来以其强大的网络功能著名,同时, 网络设备也作为三大设备之一, 成为Linux驱动学习中必不可少的设备类型, 此外, 由于历史原因, Linux并没有强制对网络设备贯彻其"一切皆 ...

  7. Linux 网络设备驱动开发(一) —— linux内核网络分层结构

    Preface Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程,又 ...

  8. 《linux设备驱动开发详解》笔记——14 linux网络设备驱动

    14.1 网络设备驱动结构 网络协议接口层:硬件无关,标准收发函数dev_queue_xmit()和netif_rx();  注意,netif_rx是将接收到的数据给上层,有时也在驱动收到数据以后调用 ...

  9. Linux网络设备驱动的实现

    结论: 1.对一个设备驱动而言,主要从两个方面进行着手,一个是控制流,一个是数据流. 控制流就是如何控制网络设备,数据流则说的是报文的收发流程. 2.网络设备可以是Linux服务器的网卡,也可以是嵌入 ...

随机推荐

  1. js封装插件【组件】三种方式,含es6新特性。

    1.先来说一下我使用到的es6的Object.assign.在jq里合并对象用的是extend方法,用来处理默认参数和传入参数做合并.es6里为我们提供了Object.assign,但是ie下全部撂倒 ...

  2. 【51nod】2026 Gcd and Lcm

    题解 话说LOJ说我今天宜学数论= =看到小迪学了杜教筛去蹭了一波小迪做的题 标解的杜教筛的函数不懂啊,怎么推的毫无思路= = 所以写了个复杂度稍微高一点的?? 首先,我们发现f是个积性函数,那么我们 ...

  3. centos6.5 下安装mysql5.7

    http://blog.csdn.net/cryhelyxx/article/details/49757217 按步骤一路执行下去. 以下是补充: linux下,在mysql正常运行的情况下,输入my ...

  4. 洛谷P1088 火星人 [STL]

    题目传送门 火星人 格式难调,题面就不放了. 分析: 这道题目不得不又让人感叹,还是$STL$大法好!!! $C++$的$algorithm$库中自带有$next\_permutation()$和$p ...

  5. Bzoj[Usaco2018 Feb]5194 Snow Boots(线段树)

    Description 到冬天了,这意味着下雪了!从农舍到牛棚的路上有N块地砖,方便起见编号为1…N,第i块地砖上积了fi英尺的雪 .在Farmer John的农舍的地窖中,总共有B双靴子,编号为1… ...

  6. java泛型理解。代码更明了。

    泛型数据java基础,但真正理解需要悉心品尝.毕竟在工作中用到的是在是太多了. 不要以为new ArrayList<>这就是泛型,这只能属于会使用. 在工作中,相对于现有的项目源码的数据库 ...

  7. [BZOJ4423][AMPPZ2013]Bytehattan(对偶图+并查集)

    建出对偶图,删除一条边时将两边的格子连边.一条边两端连通当且仅当两边的格子不连通,直接并查集处理即可. #include<cstdio> #include<algorithm> ...

  8. Android:Date、String、Long三种日期类型之间的相互转换

    源地址:http://blog.csdn.net/wangyanguiyiyang date类型转换为String类型: // formatType格式为yyyy-MM-dd HH:mm:ss//yy ...

  9. [TC6194]AllWoundUp

    [TC6194]AllWoundUp 题目大意: 有\(A\)和\(B\)两个人.\(A\)在平面上游走,\(B\)会一直盯着\(A\)看,站在\(x\)轴某个位置上不动,并随着\(A\)的运动旋转身 ...

  10. pat 打印沙漏

    本题要求你写个程序把给定的符号打印成沙漏的形状.例如给定17个“*”,要求按下列格式打印 ***** *** * *** ***** 所谓“沙漏形状”,是指每行输出奇数个符号:各行符号中心对齐:相邻两 ...