dm9000驅動介紹

一. 硬件情況

DM9000在電路板上的連接中與編程相關的如下:
1)EECS拉高:16bit模式;
2)EECK拉高,INT連接到2440 EINT7:INT腳爲低時爲有效中斷信號,中斷線爲EINT7
3)cs連接到2440的nGCS2,CMD連接2440地址總線ADDR[2]:INDEX和DATA端口地址分別爲0x1000_0000和0x1000_0004。
知道上面這些信息已經足夠移植驅動了。
二. Dm9000驅動移植詳細過程
1.在arch/arm/mach-s3c2410/devs.c 中添加dm9000的platform_device。
static struct resource eievk_dm9000_resource[] = {
[0]= {
.start = 0x10000000, //this is based on EIEVK board
.end = 0x10000003,
.flags = IORESOURCE_MEM,
},
[1]={
.start = 0x10000004,
.end = 0x10000007,
.flags = IORESOURCE_MEM,
},
[2]={
.start = IRQ_EINT7,
.end = IRQ_EINT7,
.flags = IORESOURCE_IRQ,
}
};
static struct dm9000_plat_data eievk_dm9000_platdata ={
.flags = DM9000_PLATF_16BITONLY,//work in 16bit mode
};
struct platform_device eievk_dm9000_device = {
.name = "dm9000",
.id = -1,
.num_resources = 3,
.resource = eievk_dm9000_resource,
.dev = {
.platform_data = &eievk_dm9000_platdata,
}
};
EXPORT_SYMBOL(eievk_dm9000_device);
3.在arch/arm/mach-s3c2410/devs.h中 聲明平臺設備 eievk_dm9000_device :
extern struct platform_device eievk_dm9000_device;
4.在arm/arm/mach-s3c2410/mach-smdk2410.c中將eievk_dm9000_device添加到平臺設備列表中:
static struct platform_device *smdk2440_devices[] __initdata = {
& eievk_dm9000_device, //add dm9000
};
5.需要做兩方面的工作:設置芯片MAC地址,使能DM9000的中斷。
1)GPFCON (56000050) GPF7 [15:14] 置 10 ,功能設置爲EINT7 。
這可以用函數實現:s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF3_EINT7); 
2)EXTINT0 (56000088) [30:28] 置0 低電平觸發中斷 (復位默認爲全0,可能不必設)
3)外部中斷屏蔽寄存器。EINTMASK (560000a4) [7] 置0 以enable interrupt EINT7
4)全局中斷屏蔽寄存器 INTMASK (4A000008) [4] 置0 使能EINT4_7。
代碼修改:
在dm9000.C的開始添加如下定義:
static char net_mac_addr[]={0x00,0xe0,0x3d,0xf4,0xdd,0xf7};//MAC 
static void *extint0,*intmsk,*eintmsk; 
#define EINTMASK (0x560000a4) //外部中斷屏蔽 
#define EXTINT0 (0x56000088) //外部中斷方式
#define INTMSK (0x4A000008) //中斷屏蔽 
在dm9000.C中dm9000_probe(struct platform_device *pdev)中適當位置(設置MAC之後,register_netdevice()之前)添加如下代碼:
for(i=0;i
ndev->dev_addr=net_mac_addr;
}
extint0=ioremap_nocache(EXTINT0,4);// 
intmsk=ioremap_nocache(INTMSK,4); 
eintmsk=ioremap_nocache(EINTMASK ,4);
s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_EINT7); 
writel(readl(extint0)&0x8fffffff,extint0); //eint7 low level 
writel(readl(intmsk)&(~(1
writel(readl(extint0)&(~(1
iounmap(intmsk);
iounmap(extint0);
iounmap(eintmsk);
而platform_data則是設置給struct device dev;中的platform_data指針(void *)。這個指針內核並不使用,而是驅動自身來定義及使用。
比如說對於DM9000,對應的platform_data定義於include/linux/dm9000.H中。
struct dm9000_plat_data {
unsigned int flags;
/* allow replacement IO routines */
void (*inblk)(void __iomem *reg, void *data, int len);
void (*outblk)(void __iomem *reg, void *data, int len);
void (*dumpblk)(void __iomem *reg, int len);
};
OK,初始化完資源和platform_data,一個平臺設備就定義好了。把這個平臺設備變量的地址添加到資源列表中去。比如在2410平臺:
在arm/arm/mach-s3c2410/mach-smdk2410.c把設備地址添加到*smdk2410_devices[] __initdata 數組中去。
最後在arch/arm/mach-3sc2410/cpu.c 中初始化函數__init s3c_arch_init(void)會對smdk2410_devices[]每一個設備的指針ptr調
用platform_device_register(ptr)。主要是建立device的層次結構(建立sysfs入口),將設備佔用的資源添加到內核資源管理。接下
來看看platform_driver:
在probe中獲取資源,並且申請資源,最後映射到內核空間,把映射結果保存起來。
在net_device中的open函數裏,註冊中斷處理函數。
Platform_data的使用極爲靈活,首先platform_data結構不同設備之間沒有定論,一般可用來保存特定於設備的一些配置,操作等。
比如對於DM9000,可以存在按字節,按字訪問的不同模式,因此其platform_data定義成這樣:
struct dm9000_plat_data {
unsigned int flags;
/* allow replacement IO routines */
void (*inblk)(void __iomem *reg, void *data, int len);
void (*outblk)(void __iomem *reg, void *data, int len);
void (*dumpblk)(void __iomem *reg, int len);
};
其中flags是8/16位模式的選擇標誌,下面三個是在該模式下的IO存取函數。 
然後Dm9000驅動只使用了它的flags標誌,其餘的並不使用。
因爲對於網絡net_device,有一個叫着private_data的指針,在分配一個net_device的時候可以讓內核爲其開闢指定大小的內存。這
部分內存可以通過net_device訪問,而且內容也是驅動開發者自定義的。在DM9000的驅動中,net_devict的private_data使用了一個
叫board_info的結構體來包括更多設備相關的信息和操作。
dm9000_plat_data提供的內容也被包括進board_info。因此驅動只使用了初始時設置的flags,除此外dm9000_plat_data中的方法沒有
使用的必要。
從中得到的啓示:
Device 包含一個platform_data。
Net_device則包含一個private區域.
這樣既實現了設備模型的統一管理,又實現了保持不同設備的信息與方法的靈活性。
四. Dm9000驅動源碼的簡要分析。
1.定義並註冊DM9000 的 platform_device 。定義設備佔用資源和platform_data。(具體的見前面)
2.將platform_device添加到板子的設備列表中去,在系統初始化時註冊入內核。
3.在DM9000.C中,定義了dm9000的platform_driver。
static struct platform_driver dm9000_driver = {
.driver = {
.name = "dm9000",
.owner = THIS_MODULE,
},
.probe = dm9000_probe,
.remove = dm9000_drv_remove,
.suspend = dm9000_drv_suspend,
.resume = dm9000_drv_resume,
};
這裏面關健的東西是name和probe,remove。
4.在模塊初始化函數module_init(dm9000_init);中註冊dm9000_driver。
platform_driver_register(&dm9000_driver);
這將導致驅動的probe函數被調用。
5.驅動還定義了一個數據結構:board_info來記錄芯片的信息及操作。如統計信息,讀寫操作,佔用的IO地址資源,狀態。
6.模塊初始化函數最終將調用probe函數。這個函數完成的基本過程 :
1)獲取一個netdevice:
ndev = alloc_etherdev(sizeof (struct board_info));
2)獲取設備資源:
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
3)申請IO資源,映射到內核並保存映射地址:
db->addr_req = request_mem_region(db->addr_res->start, i,pdev->name);
db->data_req = request_mem_region(db->data_res->start, iosize,pdev->name);
db->io_addr = ioremap(db->addr_res->start, i);
db->io_data = ioremap(db->data_res->start, iosize);
這裏的db即是驅動自定義的board_info結構指針。伴隨ndev申請內存。
4)根據DM9000數據位寬設置 讀寫數據幀的函數指針。
Dm9000_set_io(db, iosize);
5)復位芯片:dm9000_reset(db); db中已經包含了詳細的芯片信息。
6)讀取芯片ID號並判斷是否爲0x90000A46。
7)初始化以太網ndev : ether_setup(ndev);
8)設置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->get_stats = &dm9000_get_stats;
ndev->set_multicast_list = &dm9000_hash_table;
9)添加一些打開中斷,設置MAC地址的操作在這裏。
10)將ndev記錄於平臺設備platform_dev中去。註冊ndev。
platform_set_drvdata(pdev, ndev); //pdev->dev->dev_driver_data=ndev.
ret = register_netdev(ndev);
這也是ndev與platform_dev建立聯繫的地方。
linux的設備模型負責的只是設備的管理(檢測,啓動,移除),而如何訪問這個設備的數據,比如說以字符流模
式,塊設備方式,網絡接口,則定義相應的cdev,gendisk,ndev,然後註冊到內核。所有的數據訪問工作都以這三種界面提供。
7.在系統啓動之後,配置eth0,這將引起ndev->open()調用.
Open(dev)流程:
申請中斷線:
request_irq(dev->irq, &dm9000_interrupt, IRQF_SHARED, dev->name, dev)
復位DM9000,初始化芯片的各個寄存器使其工作在適當的狀態。
設置timer(用於傳輸超時),
調用netif_start_queue(dev);使設備可以開始收發數據。
8.發送數據包:協議層用已經封裝好上層協議數據的skb_buffer調用dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)函數。
發送函數流程:
netif_stop_queue(dev); 暫停接口,使上層暫時不能發送數據。
正在發送中的數據計數加1。
如果只有當前包發送,寫指令,寫數據幀,發送包。
如果多於一包數據正在發送,當前幀不發送。
釋放skb。
重新使能接口:netif_wake_queue(dev);
發送結束,DM9000產生中斷,在中斷函數中讀取芯片相關寄存器判斷中斷原因,如果是發送結束,則遞減正發送包計數。並netif_wake_queue(dev);
9.接收過程:
網絡數據包到達,DM9000自動接收並存放在DM內部RAM中,產生中斷。在中斷處理中識別中斷原因並調用接收處理函數dm9000_rx(struct net_device *dev)。
dm9000_rx:
讀取芯片相關寄存器確認DM9000正確的收到一幀數據。
申請skb_buffer,將數據從DM9000中拷貝到skb_buffer中。設置skb->dev=nev,skb->protocol=eth_type_trans(skb, dev)。
然後把skb_buffer交給上層協議:netif_rx(skb);
最後更新接口統計信息:db->stats.rx_packets++; 收到包總數+1。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章