主頁 > 軟體設計 > Linux驅動開發: 網路設備驅動開發

Linux驅動開發: 網路設備驅動開發

2021-09-03 18:40:10 軟體設計

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 的主要特點如下:

  1. 兼容 IEEE802.3 協議的以太網控制器
  2. 集成 MAC 和 10 BASE-T 物理層
  3. 支持全雙工和半雙工模式
  4. 資料沖突時可編程自動重發
  5. SPI 介面速度可達 10Mbps
  6. 8K 資料接收和發送雙埠 RAM
  7. 提供快速資料移動的內部 DMA 控制器
  8. 可配置的接收和發送緩沖區大小
  9. 兩個可編程 LED 輸出
  10. 帶7個中斷源的兩個中斷引腳
  11. TTL 電平輸入
  12. 提供多種封裝: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;

}

/*4. 啟動網路資料包傳輸的方法

當應用層的TCP/IP需要發送資料時,就呼叫該函式

*/

static netdev_tx_t tiny4412_ndo_start_xmit(struct sk_buff *skb,struct net_device *dev)

{

int len,i;

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; //記錄發送時間戳

printk("\n發送的資料:");

for(i=0;i<len;i++)

{

printk("%X ",data[i]);

}

printk("\n");

/* 設定硬體暫存器讓硬體將資料發出去 */

// xxx_hw_tx(data,len,dev);

return NETDEV_TX_OK; //這是個列舉狀態,

}

/*5. 設定MAC地址,對應的命令: ifconfig eth888 hw ether 00:AA:BB:CC:DD:EE */

static int tiny4412_set_mac_address(struct net_device *dev, void *addr)

{

struct sockaddr *address = addr;

memcpy(dev->dev_addr, address->sa_data, dev->addr_len);

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]);

return 0;

}

/*

以下函式在網卡的接收中斷中呼叫,用于讀取網卡里的資料,

讀取完畢之后,再通過netif_rx()函式上報到應用層

*/

static void data_rx(struct net_device* dev)

{

int length;

/*1. 讀取硬體網卡接收到的資料*/

//length = get_rev_len(...);

/*2. 分配新的套接字緩沖區*/

struct sk_buff *skb = dev_alloc_skb(length+NET_IP_ALIGN);

skb_reserve(skb, NET_IP_ALIGN); //對齊

skb->dev = dev;

/* 讀取硬體上接收到的資料 */

//skb_put(skb, length) //存放網卡里讀取資料的緩沖區地址

//ENC28J60_Read(length,skb_put(skb, length));

/* 獲取上層協議型別 */

skb->protocol = eth_type_trans(skb,dev);

/* 把資料包交給上層 */

netif_rx(skb);

/* 記錄接收時間戳 */

dev->last_rx = jiffies;

}

/*網路設備虛擬檔案操作集合*/

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,

};

static int __init Net_test_init(void)

{

/*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; //虛擬檔案操作集合

/*3. 隨機生成MAC地址*/

eth_hw_addr_random(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]);

/*注冊網路設備*/

register_netdev(tiny4412_net);

printk("網路設備注冊成功!\n");

return 0;

}

static void __exit Net_test_exit(void)

{

//注銷網路設備

unregister_netdev(tiny4412_net);

printk("網路設備注銷成功!\n");

}

module_init(Net_test_init);

module_exit(Net_test_exit);

MODULE_AUTHOR("xiaolong");

MODULE_LICENSE("GPL");

6.3 ENC28J60網卡驅動代碼

以下代碼,在上面的網路設備驅動模型里加入了ENC28J60驅動代碼,實作了完整的網卡驅動程式,

以下代碼中的ENC28J60驅動直接是使用模擬SPI時序,沒有使用SPI子系統,

由于測驗的ENC28J60網卡中斷無法正常產生,故使用內核定時器進行輪詢讀取網卡資料,讀取之后再上傳給應用層,

網卡驅動安裝后應用層測驗效果如下:

[root@XiaoLong /code]# insmod enc28j60_network_drv.ko //安裝網卡

[ 52.075000] 隨機生成的MAC地址如下:

[ 52.075000] 2E-2F-7-5-A0-DC

[ 52.100000] 網路設備初始化!

[ 52.100000] 網路設備注冊成功!

[root@XiaoLong /code]#

[root@XiaoLong /code]# ifconfig eth888 //查看網卡資訊

eth888 Link encap:Ethernet HWaddr 2E:2F:07:05:A0:DC

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)

[root@XiaoLong /code]# udhcpc -i eth888 //自動分配IP地址

udhcpc (v1.23.2) started

Setting IP address 0.0.0.0 on eth888

[ 92.310000] 網路設備打開成功!

Sending discover...

Sending select for 192.168.1.102...

Lease of 192.168.1.102 obtained, lease time 7200

Setting IP address 192.168.1.102 on eth888

Deleting routers

route: SIOCDELRT: No such process

Adding router 192.168.1.1

Recreating /etc/resolv.conf

Adding DNS server 192.168.1.1

[root@XiaoLong /code]# ifconfig //查看分配成功并設定成功的IP地址

eth888 Link encap:Ethernet HWaddr 2E:2F:07:05:A0:DC

inet addr:192.168.1.102 Bcast:192.168.1.255 Mask:255.255.255.0

inet6 addr: fe80::2c2f:7ff:fe05:a0dc/64 Scope:Link

UP BROADCAST RUNNING 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)

[root@XiaoLong /code]# ping 192.168.1.112 //ping局域網內的其他主機

PING 192.168.1.1 (192.168.1.1): 56 data bytes

64 bytes from 192.168.1.1: seq=0 ttl=64 time=12.099 ms

64 bytes from 192.168.1.1: seq=1 ttl=64 time=12.932 ms

64 bytes from 192.168.1.1: seq=2 ttl=64 time=9.035 ms

--- 192.168.1.1 ping statistics ---

3 packets transmitted, 3 packets received, 0% packet loss

round-trip min/avg/max = 9.035/11.355/12.932 ms

Enc28j60.h檔案代碼

#ifndef __ENC28J60_H

#define __ENC28J60_H

// ENC28J60 Control Registers

// Control register definitions are a combination of address,

// bank number, and Ethernet/MAC/PHY indicator bits.

// - Register address (bits 0-4)

// - Bank number (bits 5-6)

// - MAC/PHY indicator (bit 7)

#define ADDR_MASK 0x1F

#define BANK_MASK 0x60

#define SPRD_MASK 0x80

// All-bank registers

#define EIE 0x1B

#define EIR 0x1C

#define ESTAT 0x1D

#define ECON2 0x1E

#define ECON1 0x1F

// Bank 0 registers

#define ERDPTL (0x00|0x00)

#define ERDPTH (0x01|0x00)

#define EWRPTL (0x02|0x00)

#define EWRPTH (0x03|0x00)

#define ETXSTL (0x04|0x00)

#define ETXSTH (0x05|0x00)

#define ETXNDL (0x06|0x00)

#define ETXNDH (0x07|0x00)

#define ERXSTL (0x08|0x00)

#define ERXSTH (0x09|0x00)

#define ERXNDL (0x0A|0x00)

#define ERXNDH (0x0B|0x00)

//ERXWRPTH:ERXWRPTL 暫存器定義硬體向FIFO 中

//的哪個位置寫入其接收到的位元組, 指標是只讀的,在成

//功接收到一個資料包后,硬體會自動更新指標, 指標可

//用于判斷FIFO 內剩余空間的大小,

#define ERXRDPTL (0x0C|0x00)

#define ERXRDPTH (0x0D|0x00)

#define ERXWRPTL (0x0E|0x00)

#define ERXWRPTH (0x0F|0x00)

#define EDMASTL (0x10|0x00)

#define EDMASTH (0x11|0x00)

#define EDMANDL (0x12|0x00)

#define EDMANDH (0x13|0x00)

#define EDMADSTL (0x14|0x00)

#define EDMADSTH (0x15|0x00)

#define EDMACSL (0x16|0x00)

#define EDMACSH (0x17|0x00)

// Bank 1 registers

#define EHT0 (0x00|0x20)

#define EHT1 (0x01|0x20)

#define EHT2 (0x02|0x20)

#define EHT3 (0x03|0x20)

#define EHT4 (0x04|0x20)

#define EHT5 (0x05|0x20)

#define EHT6 (0x06|0x20)

#define EHT7 (0x07|0x20)

#define EPMM0 (0x08|0x20)

#define EPMM1 (0x09|0x20)

#define EPMM2 (0x0A|0x20)

#define EPMM3 (0x0B|0x20)

#define EPMM4 (0x0C|0x20)

#define EPMM5 (0x0D|0x20)

#define EPMM6 (0x0E|0x20)

#define EPMM7 (0x0F|0x20)

#define EPMCSL (0x10|0x20)

#define EPMCSH (0x11|0x20)

#define EPMOL (0x14|0x20)

#define EPMOH (0x15|0x20)

#define EWOLIE (0x16|0x20)

#define EWOLIR (0x17|0x20)

#define ERXFCON (0x18|0x20)

#define EPKTCNT (0x19|0x20)

// Bank 2 registers

#define MACON1 (0x00|0x40|0x80)

#define MACON2 (0x01|0x40|0x80)

#define MACON3 (0x02|0x40|0x80)

#define MACON4 (0x03|0x40|0x80)

#define MABBIPG (0x04|0x40|0x80)

#define MAIPGL (0x06|0x40|0x80)

#define MAIPGH (0x07|0x40|0x80)

#define MACLCON1 (0x08|0x40|0x80)

#define MACLCON2 (0x09|0x40|0x80)

#define MAMXFLL (0x0A|0x40|0x80)

#define MAMXFLH (0x0B|0x40|0x80)

#define MAPHSUP (0x0D|0x40|0x80)

#define MICON (0x11|0x40|0x80)

#define MICMD (0x12|0x40|0x80)

#define MIREGADR (0x14|0x40|0x80)

#define MIWRL (0x16|0x40|0x80)

#define MIWRH (0x17|0x40|0x80)

#define MIRDL (0x18|0x40|0x80)

#define MIRDH (0x19|0x40|0x80)

// Bank 3 registers

#define MAADR1 (0x00|0x60|0x80)

#define MAADR0 (0x01|0x60|0x80)

#define MAADR3 (0x02|0x60|0x80)

#define MAADR2 (0x03|0x60|0x80)

#define MAADR5 (0x04|0x60|0x80)

#define MAADR4 (0x05|0x60|0x80)

#define EBSTSD (0x06|0x60)

#define EBSTCON (0x07|0x60)

#define EBSTCSL (0x08|0x60)

#define EBSTCSH (0x09|0x60)

#define MISTAT (0x0A|0x60|0x80)

#define EREVID (0x12|0x60)

#define ECOCON (0x15|0x60)

#define EFLOCON (0x17|0x60)

#define EPAUSL (0x18|0x60)

#define EPAUSH (0x19|0x60)

// PHY registers

#define PHCON1 0x00

#define PHSTAT1 0x01

#define PHHID1 0x02

#define PHHID2 0x03

#define PHCON2 0x10

#define PHSTAT2 0x11

#define PHIE 0x12

#define PHIR 0x13

#define PHLCON 0x14

// ENC28J60 ERXFCON Register Bit Definitions

#define ERXFCON_UCEN 0x80

#define ERXFCON_ANDOR 0x40

#define ERXFCON_CRCEN 0x20

#define ERXFCON_PMEN 0x10

#define ERXFCON_MPEN 0x08

#define ERXFCON_HTEN 0x04

#define ERXFCON_MCEN 0x02

#define ERXFCON_BCEN 0x01

// ENC28J60 EIE Register Bit Definitions

#define EIE_INTIE 0x80

#define EIE_PKTIE 0x40

#define EIE_DMAIE 0x20

#define EIE_LINKIE 0x10

#define EIE_TXIE 0x08

#define EIE_WOLIE 0x04

#define EIE_TXERIE 0x02

#define EIE_RXERIE 0x01

// ENC28J60 EIR Register Bit Definitions

#define EIR_PKTIF 0x40

#define EIR_DMAIF 0x20

#define EIR_LINKIF 0x10

#define EIR_TXIF 0x08

#define EIR_WOLIF 0x04

#define EIR_TXERIF 0x02

#define EIR_RXERIF 0x01

// ENC28J60 ESTAT Register Bit Definitions

#define ESTAT_INT 0x80

#define ESTAT_LATECOL 0x10

#define ESTAT_RXBUSY 0x04

#define ESTAT_TXABRT 0x02

#define ESTAT_CLKRDY 0x01

// ENC28J60 ECON2 Register Bit Definitions

#define ECON2_AUTOINC 0x80

#define ECON2_PKTDEC 0x40

#define ECON2_PWRSV 0x20

#define ECON2_VRPS 0x08

// ENC28J60 ECON1 Register Bit Definitions

#define ECON1_TXRST 0x80

#define ECON1_RXRST 0x40

#define ECON1_DMAST 0x20

#define ECON1_CSUMEN 0x10

#define ECON1_TXRTS 0x08

#define ECON1_RXEN 0x04

#define ECON1_BSEL1 0x02

#define ECON1_BSEL0 0x01

// ENC28J60 MACON1 Register Bit Definitions

#define MACON1_LOOPBK 0x10

#define MACON1_TXPAUS 0x08

#define MACON1_RXPAUS 0x04

#define MACON1_PASSALL 0x02

#define MACON1_MARXEN 0x01

// ENC28J60 MACON2 Register Bit Definitions

#define MACON2_MARST 0x80

#define MACON2_RNDRST 0x40

#define MACON2_MARXRST 0x08

#define MACON2_RFUNRST 0x04

#define MACON2_MATXRST 0x02

#define MACON2_TFUNRST 0x01

// ENC28J60 MACON3 Register Bit Definitions

#define MACON3_PADCFG2 0x80

#define MACON3_PADCFG1 0x40

#define MACON3_PADCFG0 0x20

#define MACON3_TXCRCEN 0x10

#define MACON3_PHDRLEN 0x08

#define MACON3_HFRMLEN 0x04

#define MACON3_FRMLNEN 0x02

#define MACON3_FULDPX 0x01

// ENC28J60 MICMD Register Bit Definitions

#define MICMD_MIISCAN 0x02

#define MICMD_MIIRD 0x01

// ENC28J60 MISTAT Register Bit Definitions

#define MISTAT_NVALID 0x04

#define MISTAT_SCAN 0x02

#define MISTAT_BUSY 0x01

// ENC28J60 PHY PHCON1 Register Bit Definitions

#define PHCON1_PRST 0x8000

#define PHCON1_PLOOPBK 0x4000

#define PHCON1_PPWRSV 0x0800

#define PHCON1_PDPXMD 0x0100

// ENC28J60 PHY PHSTAT1 Register Bit Definitions

#define PHSTAT1_PFDPX 0x1000

#define PHSTAT1_PHDPX 0x0800

#define PHSTAT1_LLSTAT 0x0004

#define PHSTAT1_JBSTAT 0x0002

// ENC28J60 PHY PHCON2 Register Bit Definitions

#define PHCON2_FRCLINK 0x4000

#define PHCON2_TXDIS 0x2000

#define PHCON2_JABBER 0x0400

#define PHCON2_HDLDIS 0x0100

// ENC28J60 Packet Control Byte Bit Definitions

#define PKTCTRL_PHUGEEN 0x08

#define PKTCTRL_PPADEN 0x04

#define PKTCTRL_PCRCEN 0x02

#define PKTCTRL_POVERRIDE 0x01

// SPI operation codes

#define ENC28J60_READ_CTRL_REG 0x00

#define ENC28J60_READ_BUF_MEM 0x3A

#define ENC28J60_WRITE_CTRL_REG 0x40

#define ENC28J60_WRITE_BUF_MEM 0x7A

#define ENC28J60_BIT_FIELD_SET 0x80

#define ENC28J60_BIT_FIELD_CLR 0xA0

#define ENC28J60_SOFT_RESET 0xFF

// The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata

// buffer boundaries applied to internal 8K ram

// the entire available packet buffer space is allocated

//

// start with recbuf at 0/

#define RXSTART_INIT 0x0

// receive buffer end

#define RXSTOP_INIT (0x1FFF-1518-1)

// start TX buffer at 0x1FFF-0x0600, pace for one full ethernet frame (0~1518 bytes)

#define TXSTART_INIT (0x1FFF-1518)

// stp TX buffer at end of mem

#define TXSTOP_INIT 0x1FFF

// max frame length which the conroller will accept:

#define MAX_FRAMELEN 1518 // (note: maximum ethernet frame length would be 1518)

void ENC28J60_Reset(void);

u8 ENC28J60_Read_Op(u8 op,u8 addr);

void ENC28J60_Write_Op(u8 op,u8 addr,u8 data);

void ENC28J60_Read_Buf(u32 len,u8* data);

void ENC28J60_Write_Buf(u32 len,u8* data);

void ENC28J60_Set_Bank(u8 bank);

u8 ENC28J60_Read(u8 addr);

void ENC28J60_Write(u8 addr,u8 data);

void ENC28J60_PHY_Write(u8 addr,u32 data);

u8 ENC28J60_Init(u8* macaddr);

u8 ENC28J60_Get_EREVID(void);

void ENC28J60_Packet_Send(u32 len,u8* packet);

u32 ENC28J60_Packet_Receive(u32 maxlen,u8* packet);

#endif

Enc28j60.c檔案代碼:

#include <linux/init.h>

#include <linux/module.h>

#include <linux/netdevice.h>

#include <linux/etherdevice.h>

#include <linux/delay.h>

#include "enc28j60.h"

#include <linux/gpio.h>

#include <mach/gpio.h>

#include <plat/gpio-cfg.h>

#include <linux/delay.h>

#include <linux/workqueue.h>

#include <linux/delay.h>

#include <linux/interrupt.h>

#include <linux/irq.h>

#include <linux/timer.h>

/*

參考的網卡程式: cs89x0.c與Enc28j60.c

*/

/*

以下是ENC28J60驅動移植介面:

SPI0介面:

GPB_0--SCK

GPB_1--CS

GPB_2--MISO

GPB_3--MOSI

GPX1(0)--中斷

*/

static u32 ENC28J60_IRQ; //中斷編號

/*SPI底層硬體IO定義*/

#define Tiny4412_GPIO_SPI_SCK EXYNOS4_GPB(0)

#define Tiny4412_GPIO_SPI_CS EXYNOS4_GPB(1)

#define Tiny4412_GPIO_SPI_MISO EXYNOS4_GPB(2)

#define Tiny4412_GPIO_SPI_MOSI EXYNOS4_GPB(3)

#define ENC28J60_IRQ_NUMBER EXYNOS4_GPX1(0) /*Tiny4412開發板引出的IO口第9個IO口*/

#define ENC28J60_CS(x) if(x){gpio_set_value(Tiny4412_GPIO_SPI_CS,1);}else{gpio_set_value(Tiny4412_GPIO_SPI_CS,0);} //ENC28J60片選信號

#define ENC28J60_MOSI(x) if(x){gpio_set_value(Tiny4412_GPIO_SPI_MOSI,1);}else{gpio_set_value(Tiny4412_GPIO_SPI_MOSI,0);} //輸出

#define ENC28J60_MISO (gpio_get_value(Tiny4412_GPIO_SPI_MISO)) //輸入

#define ENC28J60_SCLK(x) if(x){gpio_set_value(Tiny4412_GPIO_SPI_SCK,1);}else{gpio_set_value(Tiny4412_GPIO_SPI_SCK,0);} //時鐘線

static u8 ENC28J60BANK;

static u32 NextPacketPtr;

static struct timer_list timer_date;

//網卡MAC地址,必須唯一

u8 ENC28J60_MacAddr[6]={0x04,0x02,0x35,0x00,0x00,0x01}; //MAC地址

static struct net_device *tiny4412_net=NULL; //網路設備指標結構

/*

函式功能:底層SPI介面收發一個位元組

說 明:模擬SPI時序,ENC28J60時鐘線空閑電平為低電平,在第一個下降沿采集資料

*/

u8 ENC28J60_SPI_ReadWriteOneByte(u8 tx_data)

{

u8 rx_data=0;

u8 i;

for(i=0;i<8;i++)

{

if(tx_data&0x80){ENC28J60_MOSI(1);}

else {ENC28J60_MOSI(0);}

tx_data<<=1;

{ENC28J60_SCLK(1); }

rx_data<<=1;

if(ENC28J60_MISO)rx_data|=0x01;

{ENC28J60_SCLK(0);}//第一個下降沿采集資料

}

return rx_data;

}

/*

函式功能:復位ENC28J60,包括SPI初始化/IO初始化等

MISO--->PA6----主機輸入

MOSI--->PA7----主機輸出

SCLK--->PA5----時鐘信號

CS----->PA4----片選

RESET-->PG15---復位

*/

void ENC28J60_Reset(void)

{

/*釋放GPIO*/

gpio_free(Tiny4412_GPIO_SPI_SCK);

gpio_free(Tiny4412_GPIO_SPI_CS);

gpio_free(Tiny4412_GPIO_SPI_MISO);

gpio_free(Tiny4412_GPIO_SPI_MOSI);

/*1. 配置GPIO模式*/

printk("%d\n",gpio_request(Tiny4412_GPIO_SPI_SCK, "Tiny4412_Tiny4412_SPI_SCK"));

printk("%d\n",gpio_request(Tiny4412_GPIO_SPI_CS, "Tiny4412_Tiny4412_SPI_CS"));

printk("%d\n",gpio_request(Tiny4412_GPIO_SPI_MISO, "Tiny4412_Tiny4412_SPI_MISO"));

printk("%d\n",gpio_request(Tiny4412_GPIO_SPI_MOSI, "Tiny4412_Tiny4412_SPI_MOSI"));

printk("%d\n",s3c_gpio_cfgpin(Tiny4412_GPIO_SPI_SCK, S3C_GPIO_OUTPUT));

printk("%d\n",s3c_gpio_cfgpin(Tiny4412_GPIO_SPI_CS, S3C_GPIO_OUTPUT));

printk("%d\n",s3c_gpio_cfgpin(Tiny4412_GPIO_SPI_MISO, S3C_GPIO_INPUT));

printk("%d\n",s3c_gpio_cfgpin(Tiny4412_GPIO_SPI_MOSI, S3C_GPIO_OUTPUT));

mdelay(100);

}

/*

函式功能:讀取ENC28J60暫存器(帶操作碼)

參 數:

op:操作碼

addr:暫存器地址/引數

返 回 值:讀到的資料

*/

u8 ENC28J60_Read_Op(u8 op,u8 addr)

{

u8 dat=0;

ENC28J60_CS(0);

dat=op|(addr&ADDR_MASK);

ENC28J60_SPI_ReadWriteOneByte(dat);

dat=ENC28J60_SPI_ReadWriteOneByte(0xFF);

//如果是讀取MAC/MII暫存器,則第二次讀到的資料才是正確的,見手冊29頁

if(addr&0x80)dat=ENC28J60_SPI_ReadWriteOneByte(0xFF);

ENC28J60_CS(1);

return dat;

}

/*

函式功能:讀取ENC28J60暫存器(帶操作碼)

參 數:

op:操作碼

addr:暫存器地址

data:引數

*/

void ENC28J60_Write_Op(u8 op,u8 addr,u8 data)

{

u8 dat = 0;

ENC28J60_CS(0);

dat=op|(addr&ADDR_MASK);

ENC28J60_SPI_ReadWriteOneByte(dat);

ENC28J60_SPI_ReadWriteOneByte(data);

ENC28J60_CS(1);

}

/*

函式功能:讀取ENC28J60接收快取資料

參 數:

len:要讀取的資料長度

data:輸出資料快取區(末尾自動添加結束符)

*/

void ENC28J60_Read_Buf(u32 len,u8* data)

{

ENC28J60_CS(0);

ENC28J60_SPI_ReadWriteOneByte(ENC28J60_READ_BUF_MEM);

while(len)

{

len--;

*data=(u8)ENC28J60_SPI_ReadWriteOneByte(0);

data++;

}

*data='\0';

ENC28J60_CS(1);

}

/*

函式功能:向ENC28J60寫發送快取資料

參 數:

len:要寫入的資料長度

data:資料快取區

*/

void ENC28J60_Write_Buf(u32 len,u8* data)

{

ENC28J60_CS(0);

ENC28J60_SPI_ReadWriteOneByte(ENC28J60_WRITE_BUF_MEM);

while(len)

{

len--;

ENC28J60_SPI_ReadWriteOneByte(*data);

data++;

}

ENC28J60_CS(1);

}

/*

函式功能:設定ENC28J60暫存器Bank

參 數:

ban:要設定的bank

*/

void ENC28J60_Set_Bank(u8 bank)

{

if((bank&BANK_MASK)!=ENC28J60BANK)//和當前bank不一致的時候,才設定

{

ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,ECON1,(ECON1_BSEL1|ECON1_BSEL0));

ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON1,(bank&BANK_MASK)>>5);

ENC28J60BANK=(bank&BANK_MASK);

}

}

/*

函式功能:讀取ENC28J60指定暫存器

參 數:addr:暫存器地址

返 回 值:讀到的資料

*/

u8 ENC28J60_Read(u8 addr)

{

ENC28J60_Set_Bank(addr);//設定BANK

return ENC28J60_Read_Op(ENC28J60_READ_CTRL_REG,addr);

}

/*

函式功能:向ENC28J60指定暫存器寫資料

參 數:

addr:暫存器地址

data:要寫入的資料

*/

void ENC28J60_Write(u8 addr,u8 data)

{

ENC28J60_Set_Bank(addr);

ENC28J60_Write_Op(ENC28J60_WRITE_CTRL_REG,addr,data);

}

/*

函式功能:向ENC28J60的PHY暫存器寫入資料

參 數:

addr:暫存器地址

data:要寫入的資料

*/

void ENC28J60_PHY_Write(u8 addr,u32 data)

{

u16 retry=0;

ENC28J60_Write(MIREGADR,addr); //設定PHY暫存器地址

ENC28J60_Write(MIWRL,data); //寫入資料

ENC28J60_Write(MIWRH,data>>8);

while((ENC28J60_Read(MISTAT)&MISTAT_BUSY)&&retry<0XFFF)retry++;//等待寫入PHY結束

}

/*

函式功能:初始化ENC28J60

參 數:macaddr:MAC地址

返 回 值:

0,初始化成功;

1,初始化失敗;

*/

u8 ENC28J60_Init(u8* macaddr)

{

u16 retry=0;

ENC28J60_Reset(); //復位底層引腳介面

ENC28J60_Write_Op(ENC28J60_SOFT_RESET,0,ENC28J60_SOFT_RESET);//軟體復位

while(!(ENC28J60_Read(ESTAT)&ESTAT_CLKRDY)&&retry<500)//等待時鐘穩定

{

retry++;

mdelay(1);

};

if(retry>=500)return 1;//ENC28J60初始化失敗

// do bank 0 stuff

// initialize receive buffer

// 16-bit transfers,must write low byte first

// set receive buffer start address 設定接識訓沖區地址 8K位元組容量

NextPacketPtr=RXSTART_INIT;

// Rx start

//接識訓沖器由一個硬體管理的回圈FIFO 緩沖器構成,

//暫存器對ERXSTH:ERXSTL 和ERXNDH:ERXNDL 作

//為指標,定義緩沖器的容量和其在存盤器中的位置,

//ERXST和ERXND指向的位元組均包含在FIFO緩沖器內,

//當從以太網介面接收資料位元組時,這些位元組被順序寫入

//接識訓沖器, 但是當寫入由ERXND 指向的存盤單元

//后,硬體會自動將接收的下一位元組寫入由ERXST 指向

//的存盤單元, 因此接收硬體將不會寫入FIFO 以外的單

//元,

//設定接收起始位元組

ENC28J60_Write(ERXSTL,RXSTART_INIT&0xFF);

ENC28J60_Write(ERXSTH,RXSTART_INIT>>8);

//ERXWRPTH:ERXWRPTL 暫存器定義硬體向FIFO 中

//的哪個位置寫入其接收到的位元組, 指標是只讀的,在成

//功接收到一個資料包后,硬體會自動更新指標, 指標可

//用于判斷FIFO 內剩余空間的大小 8K-1500,

//設定接收讀指標位元組

ENC28J60_Write(ERXRDPTL,RXSTART_INIT&0xFF);

ENC28J60_Write(ERXRDPTH,RXSTART_INIT>>8);

//設定接收結束位元組

ENC28J60_Write(ERXNDL,RXSTOP_INIT&0xFF);

ENC28J60_Write(ERXNDH,RXSTOP_INIT>>8);

//設定發送起始位元組

ENC28J60_Write(ETXSTL,TXSTART_INIT&0xFF);

ENC28J60_Write(ETXSTH,TXSTART_INIT>>8);

//設定發送結束位元組

ENC28J60_Write(ETXNDL,TXSTOP_INIT&0xFF);

ENC28J60_Write(ETXNDH,TXSTOP_INIT>>8);

// do bank 1 stuff,packet filter:

// For broadcast packets we allow only ARP packtets

// All other packets should be unicast only for our mac (MAADR)

//

// The pattern to match on is therefore

// Type ETH.DST

// ARP BROADCAST

// 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9

// in binary these poitions are:11 0000 0011 1111

// This is hex 303F->EPMM0=0x3f,EPMM1=0x30

//接收過濾器

//UCEN:單播過濾器使能位

//當ANDOR = 1 時://1 = 目標地址與本地MAC 地址不匹配的資料包將被丟棄

//0 = 禁止過濾器

//當ANDOR = 0 時://1 = 目標地址與本地MAC 地址匹配的資料包會被接受

//0 = 禁止過濾器

//CRCEN:后過濾器CRC 校驗使能位//1 = 所有CRC 無效的資料包都將被丟棄

//0 = 不考慮CRC 是否有效

//PMEN:格式匹配過濾器使能位

//當ANDOR = 1 時: //1 = 資料包必須符合格式匹配條件,否則將被丟棄

//0 = 禁止過濾器

//當ANDOR = 0 時: //1 = 符合格式匹配條件的資料包將被接受

//0 = 禁止過濾器

ENC28J60_Write(ERXFCON,ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN);

ENC28J60_Write(EPMM0,0x3f);

ENC28J60_Write(EPMM1,0x30);

ENC28J60_Write(EPMCSL,0xf9);

ENC28J60_Write(EPMCSH,0xf7);

// do bank 2 stuff

// enable MAC receive

//bit 0 MARXEN:MAC 接收使能位 //1 = 允許MAC 接收資料包

//0 = 禁止資料包接收

//bit 3 TXPAUS:暫停控制幀發送使能位 //1 = 允許MAC 發送暫停控制幀(用于全雙工模式下的流量控制)

//0 = 禁止暫停幀發送

//bit 2 RXPAUS:暫停控制幀接收使能位 //1 = 當接收到暫停控制幀時,禁止發送(正常操作)

//0 = 忽略接收到的暫停控制幀

ENC28J60_Write(MACON1,MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);

// bring MAC out of reset

//將MACON2 中的MARST 位清零,使MAC 退出復位狀態,

ENC28J60_Write(MACON2,0x00);

// enable automatic padding to 60bytes and CRC operations

//bit 7-5 PADCFG2:PACDFG0:自動填充和CRC 配置位

//111 = 用0 填充所有短幀至64 位元組長,并追加一個有效的CRC

//110 = 不自動填充短幀

//101 = MAC 自動檢測具有8100h 型別欄位的VLAN 協議幀,并自動填充到64 位元組長,如果不

//是VLAN 幀,則填充至60 位元組長,填充后還要追加一個有效的CRC

//100 = 不自動填充短幀

//011 = 用0 填充所有短幀至64 位元組長,并追加一個有效的CRC

//010 = 不自動填充短幀

//001 = 用0 填充所有短幀至60 位元組長,并追加一個有效的CRC

//000 = 不自動填充短幀

//bit 4 TXCRCEN:發送CRC 使能位 //1= 不管PADCFG如何,MAC都會在發送幀的末尾追加一個有效的CRC, 如果PADCFG規定要

//追加有效的CRC,則必須將TXCRCEN 置1,

//0 = MAC不會追加CRC, 檢查最后4 個位元組,如果不是有效的CRC 則報告給發送狀態向量,

//bit 0 FULDPX:MAC 全雙工使能位 //1 = MAC作業在全雙工模式下, PHCON1.PDPXMD 位必須置1,

//0 = MAC作業在半雙工模式下, PHCON1.PDPXMD 位必須清零,

ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN|MACON3_FULDPX);

// set inter-frame gap (non-back-to-back)

//配置非背對背包間間隔暫存器的低位元組

//MAIPGL, 大多數應用使用12h 編程該暫存器,

//如果使用半雙工模式,應編程非背對背包間間隔

//暫存器的高位元組MAIPGH, 大多數應用使用0Ch

//編程該暫存器,

ENC28J60_Write(MAIPGL,0x12);

ENC28J60_Write(MAIPGH,0x0C);

// set inter-frame gap (back-to-back)

//配置背對背包間間隔暫存器MABBIPG,當使用

//全雙工模式時,大多數應用使用15h 編程該寄存

//器,而使用半雙工模式時則使用12h 進行編程,

ENC28J60_Write(MABBIPG,0x15);

// Set the maximum packet size which the controller will accept

// Do not send packets longer than MAX_FRAMELEN:

// 最大幀長度 1500

ENC28J60_Write(MAMXFLL,MAX_FRAMELEN&0xFF);

ENC28J60_Write(MAMXFLH,MAX_FRAMELEN>>8);

// do bank 3 stuff

// write MAC address

// NOTE: MAC address in ENC28J60 is byte-backward

//設定MAC地址

ENC28J60_Write(MAADR5,macaddr[0]);

ENC28J60_Write(MAADR4,macaddr[1]);

ENC28J60_Write(MAADR3,macaddr[2]);

ENC28J60_Write(MAADR2,macaddr[3]);

ENC28J60_Write(MAADR1,macaddr[4]);

ENC28J60_Write(MAADR0,macaddr[5]);

//配置PHY為全雙工 LEDB為拉電流

ENC28J60_PHY_Write(PHCON1,PHCON1_PDPXMD);

// no loopback of transmitted frames 禁止環回

//HDLDIS:PHY 半雙工環回禁止位

//當PHCON1.PDPXMD = 1 或PHCON1.PLOOPBK = 1 時:

//此位可被忽略,

//當PHCON1.PDPXMD = 0 且PHCON1.PLOOPBK = 0 時: //1 = 要發送的資料僅通過雙絞線介面發出

//0 = 要發送的資料會環回到MAC 并通過雙絞線介面發出

ENC28J60_PHY_Write(PHCON2,PHCON2_HDLDIS);

// switch to bank 0

//ECON1 暫存器

//暫存器3-1 所示為ECON1 暫存器,它用于控制

//ENC28J60 的主要功能, ECON1 中包含接收使能、發

//送請求、DMA 控制和存盤區選擇位,

ENC28J60_Set_Bank(ECON1);

// enable interrutps

//EIE: 以太網中斷允許暫存器

//bit 7 INTIE: 全域INT 中斷允許位 //1 = 允許中斷事件驅動INT 引腳

//0 = 禁止所有INT 引腳的活動(引腳始終被驅動為高電平)

//bit 6 PKTIE: 接收資料包待處理中斷允許位 //1 = 允許接收資料包待處理中斷

//0 = 禁止接收資料包待處理中斷

ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE);

// enable packet reception

//bit 2 RXEN:接收使能位 //1 = 通過當前過濾器的資料包將被寫入接識訓沖器

//0 = 忽略所有接收的資料包

ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_RXEN);

if(ENC28J60_Read(MAADR5)== macaddr[0])return 0;//初始化成功

else return 1;

}

/*

函式功能:讀取EREVID

*/

u8 ENC28J60_Get_EREVID(void)

{

//在EREVID 內也存盤了版本資訊, EREVID 是一個只讀控

//制暫存器,包含一個5 位識別符號,用來標識器件特定硅片

//的版本號

return ENC28J60_Read(EREVID);

}

/*

函式功能:通過ENC28J60發送資料包到網路

參 數:

len :資料包大小

packet:資料包

*/

void ENC28J60_Packet_Send(u32 len,u8* packet)

{

//設定發送緩沖區地址寫指標入口

ENC28J60_Write(EWRPTL,TXSTART_INIT&0xFF);

ENC28J60_Write(EWRPTH,TXSTART_INIT>>8);

//設定TXND指標,以對應給定的資料包大小

ENC28J60_Write(ETXNDL,(TXSTART_INIT+len)&0xFF);

ENC28J60_Write(ETXNDH,(TXSTART_INIT+len)>>8);

//寫每包控制位元組(0x00表示使用macon3的設定)

ENC28J60_Write_Op(ENC28J60_WRITE_BUF_MEM,0,0x00);

//復制資料包到發送緩沖區

//printf("len:%d\r\n",len); //監視發送資料長度

ENC28J60_Write_Buf(len,packet);

//發送資料到網路

ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRTS);

//復位發送邏輯的問題,參見Rev. B4 Silicon Errata point 12.

if((ENC28J60_Read(EIR)&EIR_TXERIF))ENC28J60_Write_Op(ENC28J60_BIT_FIELD_CLR,ECON1,ECON1_TXRTS);

}

/*

函式功能:從網路獲取一個資料包內容

函式引數:

maxlen:資料包最大允許接收長度

packet:資料包快取區

返 回 值:收到的資料包長度(位元組)

*/

u32 ENC28J60_Packet_Receive(u32 maxlen,u8* packet)

{

u32 rxstat;

u32 len;

if(ENC28J60_Read(EPKTCNT)==0)return 0; //是否收到資料包?

//設定接識訓沖器讀指標

ENC28J60_Write(ERDPTL,(NextPacketPtr));

ENC28J60_Write(ERDPTH,(NextPacketPtr)>>8);

// 讀下一個包的指標

NextPacketPtr=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0);

NextPacketPtr|=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0)<<8;

//讀包的長度

len=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0);

len|=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0)<<8;

len-=4; //去掉CRC計數

//讀取接收狀態

rxstat=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0);

rxstat|=ENC28J60_Read_Op(ENC28J60_READ_BUF_MEM,0)<<8;

//限制接收長度

if (len>maxlen-1)len=maxlen-1;

//檢查CRC和符號錯誤

// ERXFCON.CRCEN為默認設定,一般我們不需要檢查.

if((rxstat&0x80)==0)len=0;//無效

else ENC28J60_Read_Buf(len,packet);//從接識訓沖器中復制資料包

//RX讀指標移動到下一個接收到的資料包的開始位置

//并釋放我們剛才讀出過的記憶體

ENC28J60_Write(ERXRDPTL,(NextPacketPtr));

ENC28J60_Write(ERXRDPTH,(NextPacketPtr)>>8);

//遞減資料包計數器標志我們已經得到了這個包

ENC28J60_Write_Op(ENC28J60_BIT_FIELD_SET,ECON2,ECON2_PKTDEC);

return(len);

}

/*--------------------------作業佇列、定時器、中斷服務函式---------------------------------------*/

static struct work_struct work_list;

/*

作業佇列處理函式

以下函式用于讀取網卡里的資料,

讀取完畢之后,再通過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);

}

/*

函式功能: 中斷服務函式

*/

irqreturn_t ENC28J60_irq_handler(int irq, void *dev)

{

schedule_work(&work_list);

return IRQ_HANDLED;

}

static void timer_function(unsigned long data)

{

/*共享作業佇列調度*/

schedule_work(&work_list);

/*修改定時器超時*/

mod_timer(&timer_date,jiffies+usecs_to_jiffies(100));

/*注明: ENC28J60的中斷不靈敏,就使用定時器輪詢彌補*/

}

/*----------------------------網路設備相關代碼--------------------------------------*/

/*1. 設備初始化呼叫,該函式在注冊成功后會呼叫一次,可以撰寫網卡初始化相關代碼*/

static int tiny4412_ndo_init(struct net_device * dev)

{

/*1. ENC28J60網卡初始化*/

u8 stat=ENC28J60_Init(ENC28J60_MacAddr);

if(stat)

{

printk("ENC28J60網卡初始化失敗!\r\n");

}

/*2. 獲取中斷編號*/

ENC28J60_IRQ=gpio_to_irq(ENC28J60_IRQ_NUMBER);

printk("ENC28J60_IRQ=%d\n",ENC28J60_IRQ);

/*3. 初始化作業佇列*/

INIT_WORK(&work_list,workqueue_function);

/*4. 注冊中斷*/

if(request_irq(ENC28J60_IRQ,ENC28J60_irq_handler,IRQ_TYPE_EDGE_FALLING,"ENC28J60_NET",NULL)!=0)

{

printk("ENC28J60中斷注冊失敗!\n");

}

/*使用定時器100ms*/

timer_date.expires=jiffies+usecs_to_jiffies(100);

timer_date.function=timer_function;

/*5. 初始化定時器*/

init_timer(&timer_date);

/*6. 添加定時器到內核并啟動*/

add_timer(&timer_date);

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;

}

/*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;

}

/*記錄發送時間戳*/

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. 設定MAC地址,對應的命令: ifconfig eth888 hw ether 00:AA:BB:CC:DD:EE */

static int tiny4412_set_mac_address(struct net_device *dev, void *addr)

{

struct sockaddr *address = addr;

memcpy(dev->dev_addr, address->sa_data, dev->addr_len);

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]);

//設定MAC地址

ENC28J60_Write(MAADR5,tiny4412_net->dev_addr[0]);

ENC28J60_Write(MAADR4,tiny4412_net->dev_addr[1]);

ENC28J60_Write(MAADR3,tiny4412_net->dev_addr[2]);

ENC28J60_Write(MAADR2,tiny4412_net->dev_addr[3]);

ENC28J60_Write(MAADR1,tiny4412_net->dev_addr[4]);

ENC28J60_Write(MAADR0,tiny4412_net->dev_addr[5]);

return 0;

}

/*網路設備虛擬檔案操作集合*/

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,

};

/*--------------------------驅動框架------------------------------------*/

static int __init Net_test_init(void)

{

/*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; //看門狗超時時間

/*3. 隨機生成MAC地址*/

eth_hw_addr_random(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];

/*注冊網路設備*/

register_netdev(tiny4412_net);

printk("網路設備注冊成功!\n");

return 0;

}

static void __exit Net_test_exit(void)

{

//注銷網路設備

unregister_netdev(tiny4412_net);

free_netdev(tiny4412_net);

/*1. 釋放GPIO口使用權*/

gpio_free(Tiny4412_GPIO_SPI_SCK);

gpio_free(Tiny4412_GPIO_SPI_CS);

gpio_free(Tiny4412_GPIO_SPI_MISO);

gpio_free(Tiny4412_GPIO_SPI_MOSI);

/*2. 釋放中斷號*/

free_irq(ENC28J60_IRQ,NULL);

/*3. 停止定時器*/

del_timer_sync(&timer_date);

/*4. 清除作業*/

cancel_work_sync(&work_list);

printk("網路設備注銷成功!\n");

}

module_init(Net_test_init);

module_exit(Net_test_exit);

MODULE_AUTHOR("xiaolong");

MODULE_LICENSE("GPL");

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/297115.html

標籤:其他

上一篇:WebSocket協議介紹

下一篇:基于nchan打造百萬用戶的聊天室

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more