Linux驱动开发: 网络设备驱动开发
Posted DS小龙哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动开发: 网络设备驱动开发相关的知识,希望对你有一定的参考价值。
Linux内核版本: 3.5
一、Linux下网络相关命令
1.1 ifconfig命令:设置网卡IP地址
功能
ifconfig用于查看和更改网络接口的地址和参数,包括IP地址、网络掩码、广播地址,使用权限是超级用户。
语法:fconfig -interface [options] address
主要参数
-interface | 指定的网络接口名,如eth0和eth1。 |
up | 激活指定的网络接口卡。 |
down | 关闭指定的网络接口。 |
broadcast address | 设置接口的广播地址。 |
pointopoint | 启用点对点方式。 |
address | 设置指定接口设备的IP地址。 |
netmask address | 设置接口的子网掩码。 |
ifconfig是用来设置和配置网卡的命令行工具。为了手工配置网络,这是一个必须掌握的命令。使用该命令的好处是无须重新启动机器。要赋给eth0接口IP地址207.164.186.2,并且马上激活它,使用下面命令:
#fconfig eth0 210.34.6.89 netmask 255.255.255.128 broadcast 210.34.6.127 |
该命令的作用是设置网卡eth0的IP地址、网络掩码和网络的本地广播地址。若运行不带任何参数的ifconfig命令,这个命令将显示机器所有激活接口的信息。带有“-a”参数的命令则显示所有接口的信息,包括没有激活的接口。注意,用ifconfig命令配置的网络设备参数,机器重新启动以后将会丢失。
查看网卡的IP地址信息
# ifconfig //查看当前已经启动的网卡信息 # ifconfig -a //查看所有网卡的信息。包含未启动的网卡。 # ifconfig eth0 //查看eth0网卡的信息 |
关闭与启动网卡
# ifconfig eth0 up //激活名称为eth0的网卡 # ifconfig eth0 down //关闭名称为eth0的网卡 |
修改网卡MAC地址
修改网卡MAC地址 首先必须关闭网卡设备:ifconfig eth0 down 修改MAC地址:ifconfig eth0 hw ether 00:AA:BB:CC:DD:EE 重新启用网卡:ifconfig eht0 up 这样网卡的MAC地址就更改完成了。每张网卡的MAC地址是惟一,但不是不能修改的,只要保证在网络中的MAC地址的惟一性就可以了。 |
在一张网卡上绑定多个IP地址
在Linux下,可以使用ifconfig方便地绑定多个IP地址到一张网卡。
例如,eth0接口的原有IP地址为192.168.0 .254,可以执行下面命令:
ifconfig eth0:0 192.168.0.253 netmask 255.255.255.0 ifconfig eth0:1 192.168.0.252 netmask 255.255.255.0 ...... |
1.2 ping命令
功能:ping检测主机网络接口状态,使用权限是所有用户。
语法:ping [-dfnqrRv][-c][-i][-I][-l][-p][-s][-t] IP地址
主要参数
-d | 使用Socket的SO_DEBUG功能。 |
-c | 设置完成要求回应的次数。 |
-f | 极限检测。 |
-i | 指定收发信息的间隔秒数。 |
-I | 网络界面使用指定的网络界面送出数据包。 |
-l | 前置载入,设置在送出要求信息之前,先行发出的数据包。 |
-n | 只输出数值。 |
-p | 设置填满数据包的范本样式。 |
-q | 不显示指令执行过程,开头和结尾的相关信息除外。 |
-r | 忽略普通的Routing Table,直接将数据包送到远端主机上。 |
-R | 记录路由过程。 |
-s | 设置数据包的大小。 |
-t | 设置存活数值TTL的大小。 |
-v | 详细显示指令的执行过程。 |
ping命令是使用最多的网络指令,通常我们使用它检测网络是否连通,它使用ICMP协议。但是有时会有这样的情况,我们可以浏览器查看一个网页,但是却无法ping通,这是因为一些网站处于安全考虑安装了防火墙。
使用实例
# ping 192.168.11.123 |
1.3 网卡启动与关闭
除了使用ifconfig配置之外,也可以使用ifup、ifdown命令来实现。
# ifup eth0 //开启eth0网卡 # ifdown eth0 //关闭eth0网卡 |
二、查看内核已经支持的网卡驱动
进入到内核配置菜单目录下:
[root@wbyq linux-3.5]# make menuconfig
Device Drivers ---> [*] Network device support ---> ……………………………….. USB Network Adapters ---> //支持的USB网卡设备 <*> USB Pegasus/Pegasus-II based ethernet device support < > USB RTL8150 based ethernet device support (EXPERIMENTAL) <*> ASIX AX88xxx Based USB 2.0 Ethernet Adapters <*> Davicom DM9601 based USB 1.1 10/100 ethernet devices <*> Davicom DM9620 USB2.0 Fast Ethernet devices (开发板本身的自带网卡) < > SMSC LAN75XX based USB 2.0 gigabit ethernet devices < > SMSC LAN95XX based USB 2.0 10/100 ethernet devices < > GeneSys GL620USB-A based cables < > Prolific PL-2301/2302/25A1 based cables < > MosChip MCS7830 based Ethernet adapters |
三、移植ENC28J60网卡驱动
3.1 ENC28J60芯片介绍
ENC28J60 是带有行业标准串行外设接口(Serial Peripheral Interface,SPI)的独立以太网 控制器。它可作为任何配备有 SPI 的控制器的以太网接口。ENC28J60 符合 IEEE 802.3 的全部规范,采用了一系列包过滤机制以对传入数据包进行限制。 它还提供了一个内部 DMA 模块, 以实现快速数据吞吐和硬件支持的 IP 校验和计算。 与主控制器的通信通过两个中断引脚和 SPI 实现,数据传输速率高达 10 Mb/s。两个专用的引脚用于连接 LED,进行网络活动状态指示。ENC28J60 总共只有 28 脚,提供 QFN/TF。
ENC28J60 的主要特点如下:
- 兼容 IEEE802.3 协议的以太网控制器
- 集成 MAC 和 10 BASE-T 物理层
- 支持全双工和半双工模式
- 数据冲突时可编程自动重发
- SPI 接口速度可达 10Mbps
- 8K 数据接收和发送双端口 RAM
- 提供快速数据移动的内部 DMA 控制器
- 可配置的接收和发送缓冲区大小
- 两个可编程 LED 输出
- 带7个中断源的两个中断引脚
- TTL 电平输入
- 提供多种封装:SOIC/SSOP/SPDIP/QFN 等。
ENC28J60 的典型应用电路如下图:
ENC28J60 由七个主要功能模块组成:
1) SPI 接口,充当主控制器和 ENC28J60 之间通信通道。
2) 控制寄存器,用于控制和监视 ENC28J60。
3) 双端口 RAM 缓冲器,用于接收和发送数据包。
4) 判优器,当 DMA、发送和接收模块发出请求时对 RAM 缓冲器的访问进行控制。
5) 总线接口,对通过 SPI 接收的数据和命令进行解析。
6) MAC(Medium Access Control)模块,实现符合 IEEE 802.3 标准的 MAC 逻辑。
7) PHY(物理层)模块,对双绞线上的模拟数据进行编码和译码。
ENC28J60 还包括其他支持模块,诸如振荡器、片内稳压器、电平变换器(提供可以接受 5V 电压的 I/O 引脚)和系统控制逻辑。
引脚功能说明:
3.2 ENC28J60以太网模块介绍
ENC28J60 网络模块采用 ENC28J60 作为主芯片,单芯片即可实现以太网接入, 利用该模块,基本上只要是个单片机,就可以实现以太网连接。
模块实物图如下:
模块的主要引脚功能:
其中 GND 和 V3.3 用于给模块供电,MISO/MOSI/SCK 用于 SPI 通信,CS 是片选信号,INT 为中断输出引脚,RST 为模块复位信号。
3.3 查看内核已经支持的网卡源码
在内核linux-3.5/drivers/net/ethernet源码目录下可以查看已经支持的网卡源码。
ENC28J60网卡源码就存放在: /linux-3.5/drivers/net/ethernet/microchip目录下
[root@wbyq microchip]# pwd /work/Tiny4412/linux-3.5/drivers/net/ethernet/microchip [root@wbyq microchip]# ls enc28j60.c enc28j60_hw.h Kconfig Makefile |
3.4 配置内核SPI总线设备端
ENC28J60使用的是SPI总线通信,先查看内核SPI总线板级注册是否支持。
进入到内核配置菜单: [root@wbyq linux-3.5]# make menuconfig
Device Drivers ---> [*] SPI support ---> <*> Samsung S3C64XX series type SPI [*] Samsung S3C64XX Channel 0 Support. /选中SP0总线支持*/ |
(使用的测试开发板是友善之臂的Tiny4412开发板)
因为开发板引出的SPI接口只有SPI0,所以只能配置SPI0总线。
1. 修改SPI0总线板级注册信息
打开开发板底层板级配置文件:
[root@wbyq linux-3.5]# vim arch/arm/mach-exynos/mach-tiny4412.c +1449 |
2. 修改SPI设备端名称:
1447 static struct spi_board_info spi0_board_info[] __initdata = { 1448 { 1449 .modalias = "spidev_enc28j60", /*修改设备端名称*/ 1450 .platform_data = NULL, 1451 .max_speed_hz = 10*1000*1000, 1452 .bus_num = 0, 1453 .chip_select = 0, 1454 .mode = SPI_MODE_0, 1455 .controller_data = &spi0_csi[0], 1456 } 1457 }; |
SPI子系统匹配使用的是平台设备模型,驱动端与设备端的名称需要一致。
3. 修改完以上两步配置之后,再重新编译内核,烧写内核。
3.5 修改ENC28J60驱动代码
将/drivers/net/ethernet/microchip目录下的ENC28J60源码复制出来,单独修改。
1. 编写Makefile文件,负责编译成模块。
2. 修改ENC28J60驱动源码里的名称与SPI总线设备端保持一致。
3. 修改驱动端的probe函数,增加对SPI模式配置与中断号获取,正常情况下可以直接在SPI设备端直接修改,驱动端直接获取信息即可。
static int __devinit enc28j60_probe(struct spi_device *spi) { spi->irq=gpio_to_irq(EXYNOS4_GPX3(2)); /*获取中断号*/ /*配置SPI模式*/ spi->bits_per_word = 8; spi->mode = SPI_MODE_1; spi->max_speed_hz=50000;/*1*100000; //50000*/ if(spi_setup(spi)<0)//配置 { printk("SPI配置失败!\\n"); } …………………………. } |
除了修改以上信息之外,其他信息不用修改,直接编译驱动安装即可。
3.6 驱动安装测试
[root@XiaoLong /code]# insmod enc28j60.ko [ 31.640000] SPI Probe函数匹配成功,SPI总线编号: 0 [ 31.640000] spidev_enc28j60 spi0.0: spidev_enc28j60 Ethernet driver 1.01 loaded [ 31.655000] spi->irq=442 [ 31.710000] net eth1: spidev_enc28j60 driver registered [root@XiaoLong /code]# ifconfig -a eth0 Link encap:Ethernet HWaddr 00:00:FF:FF:00:00 inet addr:192.168.10.123 Bcast:192.168.10.255 Mask:255.255.255.0 inet6 addr: fe80::200:ffff:feff:0/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:2841 errors:0 dropped:0 overruns:0 frame:0 TX packets:1641 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:2695524 (2.5 MiB) TX bytes:295408 (288.4 KiB) eth1 Link encap:Ethernet HWaddr CE:89:65:5A:91:93 //新生成的网卡名称 BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) Interrupt:186 ip6tnl0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 NOARP MTU:1452 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) sit0 Link encap:IPv6-in-IPv4 NOARP MTU:1480 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) [root@XiaoLong /code]# ifconfig eth1 192.168.1.100 //设置网卡IP地址 [ 76.460000] net eth1: link down [ 76.460000] net eth1: multicast mode [ 76.460000] net eth1: multicast mode [ 76.460000] net eth1: multicast mode [ 76.460000] IPv6: ADDRCONF(NETDEV_UP): eth1: link is not ready [root@XiaoLong /code]# udhcpc -i eth1 //自动获取IP地址 |
四、网络设备相关API函数介绍
4.1 动态分配net_device结构
#define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1) |
函数参数:分配的空间大小。如果自己没有定义自己的结构体,就直接填sizeof(struct net_device)
函数返回值:执行成功返回申请的空间地址。
空间分配的函数还有一个alloc_netdev()函数。
alloc_etherdev()是alloc_netdev()针对以太网的"快捷"函数
4.2 释放net_device结构
void free_netdev(struct net_device *dev) |
该函数用于释放alloc_etherdev分配的net_device结构体,与alloc_etherdev成对使用。
4.3 注册网络设备
int register_netdev(struct net_device *dev) |
函数形参:网络设备信息struct net_device
函数返回值:执行成功返回0。
struct net_device结构体原型如下:
struct net_device { char name[IFNAMSIZ]; /*网卡名字,ifconfig查看的名称*/ unsigned long mem_end; /* shared mem end */ unsigned long mem_start; /* shared mem start */ 这两个变量描述设备与内核通信所用到的内存边界。它们由设备驱动初始化,并且只能被设备驱动访问;高层协议不需要关心这块内存。 unsigned long base_addr; /* 存放网络设备基地址,就是物理地址,用来将设备映射到内存空间*/ unsigned int irq; /*设备中断号。它可以被多个设备共享。设备驱动调用request_irq来分配这个值,并调用free_irq来释放它*/
const struct net_device_ops *netdev_ops; //网络设备的虚拟文件操作集合,很重要的结构。 const struct ethtool_ops *ethtool_ops; //可选的设备操作 unsigned char *dev_addr; /*MAC地址*/ unsigned char broadcast[MAX_ADDR_LEN]; /*广播地址 */ unsigned long last_rx; /*最后收到数据包的时间,用于判断超时*/ unsigned char if_port; /*接口的端口类型。*/ unsigned char dma; /* DMA channel */ /* 设备所使用的DMA通道。为获取和释放一个DMA通道,内核在kernel/dma.c中定义了两个函数request_dma和free_dma。为了在获取dma通道后,启用或者停止dma通道,内核定义了两个函数enable_dma和disable_dma。这两个函数的实现与体系结构相关,所以在include/asm-architecture下有相关的文件(例如include/asm-i386)。这些函数被ISA设备使用;PCI设备不使用这些函数,它们使用其他函数。并不是所有的设备都可以使用dma,因为有些总线不支持dma。*/ unsigned long trans_start; /* 数据包发送的起始时间-jiffies表示 */ int watchdog_timeo; /* 被 by dev_watchdog()函数使用,用于定义超时 */ struct timer_list watchdog_timer; /* 看门狗定时器*/ }; |
const struct net_device_ops 网络设备虚拟文件操作集合:
struct net_device_ops { /*初始化注册网络设备的时候调用*/ int (*ndo_init)(struct net_device *dev);
/*释放设备的时候调用*/ void (*ndo_uninit)(struct net_device *dev); /*打开网络接口,对应ifconfig up命令,编写网络设备硬件初始化的相关代码*/ int (*ndo_open)(struct net_device *dev); /*关闭网络设备,对应ifconfig down命令,实现的内容与OPEN相反*/ int (*ndo_stop)(struct net_device *dev); /*启动网络数据包传输的方法*,返回值必须返回NETDEV_TX_OK, NETDEV_TX_BUSY / netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb,struct net_device *dev); /*网络数据包没有在规定的时间内发送出去,产生超时事件时的处理函数,它应当处理超时问题,并恢复报文发送*/ void (*ndo_tx_timeout) (struct net_device *dev); ……………………省略……………………………. } |
分配net_device结构体之后初始化示例
/*1. 分配及初始化net_device对象,参数:私有数据大小(单位:字节数)*/ tiny4412_net=alloc_etherdev(sizeof(struct net_device)); /*2. net结构体赋值*/ strcpy(tiny4412_net->name, "eth888");//网络设备的名称,使用ifconfig -a可以查看到。 tiny4412_net->netdev_ops=&netdev_ops_test; //虚拟文件操作集合 tiny4412_net->if_port = IF_PORT_10BASET; //协议规范 tiny4412_net->watchdog_timeo = 4 * HZ; //看门狗超时时间 |
4.4 注销网络设备
void unregister_netdev(struct net_device *dev) |
功能:注销网络设备
参数:注销的网络设备结构体
4.5 随机生成MAC地址
void eth_hw_addr_random(struct net_device *dev) |
该函数使用软件方式随机生成一个MAC地址,并给传入的net_device 结构体内部成员dev_addr赋值。
示例:
/*随机生成MAC地址*/ eth_hw_addr_random(tiny4412_net); // struct net_device *tiny4412_net; printk("随机生成的MAC地址如下:\\n"); printk("%X-%X-%X-%X-%X-%X\\n", tiny4412_net->dev_addr[0], tiny4412_net->dev_addr[1], tiny4412_net->dev_addr[2], tiny4412_net->dev_addr[3], tiny4412_net->dev_addr[4], tiny4412_net->dev_addr[5]); ENC28J60_MacAddr[0]=tiny4412_net->dev_addr[0]; ENC28J60_MacAddr[1]=tiny4412_net->dev_addr[1]; ENC28J60_MacAddr[2]=tiny4412_net->dev_addr[2]; ENC28J60_MacAddr[3]=tiny4412_net->dev_addr[3]; ENC28J60_MacAddr[4]=tiny4412_net->dev_addr[4]; ENC28J60_MacAddr[5]=tiny4412_net->dev_addr[5]; |
4.6 以太网最小一帧数据长度定义
#define ETH_ALEN 6 //定义了以太网接口的MAC地址的长度为6个字节 #define ETH_HLAN 14 //定义了以太网帧的头长度为14个字节 #define ETH_ZLEN 60 //定义了以太网帧的最小长度为 ETH_ZLEN + ETH_FCS_LEN = 64个字节 #define ETH_DATA_LEN 1500 //定义了以太网帧的最大负载为1500个字节 #define ETH_FRAME_LEN 1514 //定义了以太网正的最大长度为ETH_DATA_LEN + ETH_FCS_LEN = 1518个字节 #define ETH_FCS_LEN 4 //定义了以太网帧的CRC值占4个字节 |
使用网卡发送数据时,如何发现发送的实际数据小于以太网规定的最小长度,需要进行补齐:
static netdev_tx_t tiny4412_ndo_start_xmit(struct sk_buff *skb,struct net_device *dev) { int len; char *data, shortpkt[ETH_ZLEN];
/* 获得有效数据指针和长度 */ data = skb->data; /*获取将要发送出去的数据指针*/ len = skb->len; /*获取将要发送出去的数据长度*/
if(len < ETH_ZLEN) { /* 如果帧长小于以太网帧最小长度,补0 */ memset(shortpkt,0,ETH_ZLEN); memcpy(shortpkt,skb->data,skb->len); len = ETH_ZLEN; data = shortpkt; } ………………省略…………………………….. } |
4.7 分配新的套接字缓冲区
struct sk_buff *dev_alloc_skb(unsigned int length) |
该函数用于分配新的套接字缓冲区,用于存放即将上报给上层(TCP/IP协议层)的网络数据。
示例:
/*从ENC28J60的寄存器里读取接收到的数据*/ length=ENC28J60_Packet_Receive(1518,Enc28j60_Rx_Buff); if(length<=0) { return; } /*分配新的套接字缓冲区*/ struct sk_buff *skb = dev_alloc_skb(length+NET_IP_ALIGN); skb_reserve(skb, NET_IP_ALIGN); //对齐 skb->dev = tiny4412_net; /*将硬件上接收到的数据拷贝到sk_buff里*/ memcpy(skb_put(skb, length),Enc28j60_Rx_Buff,length); 说明: skb_put(skb, length)返回sk_buff数据缓冲区首地址,保存即将上报给应用层的数据。 参考: smsc-ircc2.c文件1461行 |
4.8 获取数据包的协议ID
eth_type_trans(struct sk_buff *skb, struct net_device *dev) |
从网卡里读取到数据包之后,可以通过该函数获取数据包的协议类型。
示例:
/*将硬件上接收到的数据拷贝到sk_buff里*/ memcpy(skb_put(skb, length),Enc28j60_Rx_Buff,length); // Enc28j60_Rx_Buff是网卡收到的实际数据
/* 获取上层协议类型 */ skb->protocol = eth_type_trans(skb,tiny4412_net); |
1.5 网络设备框架介绍
5.1 网络设备驱动框图
5.2 ndo_start_xmit函数接口代码编写示例
/*启动网络数据包传输的方法*/ static netdev_tx_t tiny4412_ndo_start_xmit(struct sk_buff *skb,struct net_device *dev) { int len; char *data, shortpkt[ETH_ZLEN];
/* 获得有效数据指针和长度 */ data = skb->data; /*获取将要发送出去的数据指针*/ len = skb->len; /*获取将要发送出去的数据长度*/
if(len < ETH_ZLEN) { /* 如果帧长小于以太网帧最小长度,补0 */ memset(shortpkt,0,ETH_ZLEN); memcpy(shortpkt,skb->data,skb->len); len = ETH_ZLEN; data = shortpkt; }
/*记录发送时间戳*/ dev->trans_start = jiffies;
/* 设置硬件寄存器让硬件将数据发出去 */ ENC28J60_Packet_Send(len,data);
/*释放skb*/ dev_kfree_skb(skb);
/*更新统计信息:记录发送的包数量*/ dev->stats.tx_packets++;
/*更新统计信息:记录发送的字节数量*/ dev->stats.tx_bytes += skb->len;
return NETDEV_TX_OK; //这是个枚举状态。 } |
5.3 通过netif_rx函数上报数据代码编写示例
/* 工作队列处理函数 以下函数用于读取网卡里的数据。 读取完毕之后,再通过netif_rx()函数上报到应用层 */ u8 Enc28j60_Rx_Buff[1518]; /*ENC28J60最大可接收的字节*/ static void workqueue_function(struct work_struct *work) { int length; /*从ENC28J60的寄存器里读取接收到的数据*/ length=ENC28J60_Packet_Receive(1518,Enc28j60_Rx_Buff); if(length<=0) { return; } /*2. 分配新的套接字缓冲区*/ struct sk_buff *skb = dev_alloc_skb(length+NET_IP_ALIGN); skb_reserve(skb, NET_IP_ALIGN); //对齐 skb->dev = tiny4412_net; /*将硬件上接收到的数据拷贝到sk_buff里*/ memcpy(skb_put(skb, length),Enc28j60_Rx_Buff,length);
/* 获取上层协议类型 */ skb->protocol = eth_type_trans(skb,tiny4412_net); /* 记录接收时间戳 */ tiny4412_net->last_rx = jiffies;
/*接收的数据包*/ tiny4412_net->stats.rx_packets++;
/*接收的字节数量*/ tiny4412_net->stats.rx_bytes += skb->len;
/* 把数据包交给上层 */ netif_rx(skb); } |
六、 网络设备驱动框架代码
6.1 网络设备驱动编程步骤
1. 调用alloc_etherdev函数,分配net_device对象
2. 对返回的net_device结构指针进行初始化赋值,比如:网卡名称,MAC地址,文件操作集合等等。
如果网卡没有固定的MAC地址,可以通过eth_hw_addr_random函数随机生成。
3. 网络设备文件操作集合实现的接口如下:
static struct net_device_ops netdev_ops_test= { .ndo_open = tiny4412_ndo_open, .ndo_stop = tiny4412_ndo_stop, .ndo_start_xmit = tiny4412_ndo_start_xmit, /*该函数负责接收应用层的数据,并通过网卡发出去*/ .ndo_init = tiny4412_ndo_init, .ndo_set_mac_address= tiny4412_set_mac_address, }; |
4. 调用register_netdev函数完成网络设备注册。
注销函数: unregister_netdev
5. 网卡收到数据通过netif_rx函数上传给应用层
6.2 网络设备驱动框架代码
以下代码是一个网络设备驱动模型,演示了网卡如何获取上层应用程序传递下来的数据并发送出去,网卡接收到数据如何传递给上层应用程序。
#include <linux/init.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> static struct net_device *tiny4412_net=NULL; //网络设备指针结构 /*1. 设备初始化调用,该函数在注册成功后会调用一次,可以编写网卡初始化相关代码*/ static int tiny4412_ndo_init(struct net_device * dev) { printk("网络设备初始化!\\n"); return 0; } /*2. 打开网络接口,对应ifconfig up命令,编写网络设备硬件初始化的相关代码*/ static int tiny4412_ndo_open(struct net_device *dev) { printk("网络设备打开成功!\\n"); return 0; } /*3. 关闭网络设备,对应ifconfig down命令,实现的内容与OPEN相反*/ static int tiny4412_ndo_stop(struct net_device *dev) { printk("网络设备关闭成功!\\n"); return 0; } <以上是关于Linux驱动开发: 网络设备驱动开发的主要内容,如果未能解决你的问题,请参考以下文章 |