16-DM9000网卡驱动设计分析
16.9.2 DM9000网卡驱动设计分析
DM9000网卡驱动位于内核源代码的drivers/net/dm9000.c,它基于平台驱动架构,代码清单16.19抽取了它的主干。其核心工作是实现了前文所述net_device结构体中的hard_start_xmit、open、stop、set_multicast_list、do_ioctl、tx_timeout等成员函数并借助中断辅助进行网络数据包的收发,另外它也实现了ethtool_ops中的成员函数。
代码清单16.19 DM9000网卡驱动
1 / Structure/enum 定义 ------------------------------- /
2 typedef struct board_info {
3
4 void __iomem io_addr; / 寄存器I/O基地址*/
5 void __iomem io_data; / 数据I/O基地址 */
6 u16 irq; / IRQ /
7
8 u16 tx_pkt_cnt;
9 ...
10 } board_info_t;
11
12 static int dm9000_ioctl(struct net_device dev, struct ifreq req, int cmd){...}
13
14 static const struct ethtool_ops dm9000_ethtool_ops = {
15 .get_drvinfo = dm9000_get_drvinfo,
16 .get_settings = dm9000_get_settings,
17 .set_settings = dm9000_set_settings,
18 .get_msglevel = dm9000_get_msglevel,
19 .set_msglevel = dm9000_set_msglevel,
20 .nway_reset = dm9000_nway_reset,
21 .get_link = dm9000_get_link,
22 .get_eeprom_len = dm9000_get_eeprom_len,
23 .get_eeprom = dm9000_get_eeprom,
24 .set_eeprom = dm9000_set_eeprom,
25 };
26
27 / 设置DM9000多播地址 /
28 static void dm9000_hash_table(struct net_device *dev)
29
30 / 看门狗超时,网络层将调用该函数 /
31 static void dm9000_timeout(struct net_device *dev)
32 {
33 ...
34 netif_stop_queue(dev);
35 netif_wake_queue(dev);
36 ...
37 }
38
39 static int dm9000_start_xmit(struct sk_buff skb, struct net_device dev)
40 {
41 ...
42 / 将发送数据包移至DM9000的TX RAM /
43 writeb(DM9000_MWCMD, db->io_addr);
44
45 (db->outblk)(db->io_data, skb->data, skb->len);
46 dev->stats.tx_bytes += skb->len;
47 ...
48 }
49
50 / 数据发送完成 /
51 static void dm9000_tx_done(struct net_device dev, board_info_t db)
52 {
53 ...
54 netif_wake_queue(dev);
55 }
56
57 / 接收数据并传递给上层 /
58 static void
59 dm9000_rx(struct net_device *dev)
60 {
61 ...
62 netif_rx(skb);
63 dev->stats.rx_packets++;
64 ...
65 }
66
67 static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
68 {
69 ...
70 return IRQ_HANDLED;
71 }
72
73 / 打开网卡接口 /
74 static int dm9000_open(struct net_device *dev)
75 {
76 ..
77 netif_start_queue(dev);
78 ...
79 return 0;
80 }
81 / 从phyxcer读一个word /
82 static int dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg){...};
83 / 向phyxcer写一个word /
84 static void
85 dm9000_phy_write(struct net_device *dev,
86 int phyaddr_unused, int reg, int value){...}
87
88 static int __devinit
89 dm9000_probe(struct platform_device *pdev)
90 {
91 ...
92
93 / Init network device /
94 ndev = alloc_etherdev(sizeof(struct board_info));
95
96 SET_NETDEV_DEV(ndev, &pdev->dev);
97
98 ether_setup(ndev);
99
100 ndev->open = &dm9000_open;
101 ndev->hard_start_xmit = &dm9000_start_xmit;
102 ndev->tx_timeout = &dm9000_timeout;
103 ndev->stop = &dm9000_stop;
104 ndev->set_multicast_list = &dm9000_hash_table;
105 ndev->ethtool_ops = &dm9000_ethtool_ops;
106 ndev->do_ioctl = &dm9000_ioctl;
107
108 #ifdef CONFIG_NET_POLL_CONTROLLER
109 ndev->poll_controller = &dm9000_poll_controller;
110 #endif
111
112 db->msg_enable = NETIF_MSG_LINK;
113 ...
114 db->mii.mdio_read = dm9000_phy_read;
115 db->mii.mdio_write = dm9000_phy_write;
116
117 platform_set_drvdata(pdev, ndev);
118 ret = register_netdev(ndev);
119
120 ...
121 }
122
123 static int __devexit
124 dm9000_drv_remove(struct platform_device *pdev)
125 {
126 struct net_device *ndev = platform_get_drvdata(pdev);
127
128 platform_set_drvdata(pdev, NULL);
129
130 unregister_netdev(ndev);
131 free_netdev(ndev); / free device structure /
132
133 return 0;
134 }
135
136 static struct platform_driver dm9000_driver = {
137 .driver = {
138 .name = "dm9000",
139 .owner = THIS_MODULE,
140 },
141 .probe = dm9000_probe,
142 .remove = __devexit_p(dm9000_drv_remove),
143 };
144
145 static int __init dm9000_init(void)
146 {
147 return platform_driver_register(&dm9000_driver);
148 }
149
150 static void __exit dm9000_cleanup(void)
151 {
152 platform_driver_unregister(&dm9000_driver);
153 }
154
155 module_init(dm9000_init);
156 module_exit(dm9000_cleanup);
DM9000驱动的实现与具体CPU无关,在将该驱动移植到特定电路板时,只需要在板文件中为板上DM9000对应平台设备的寄存器和数据基地址进行赋值,并指定正确的IRQ资源,代码16.20给出了LDD6410开发板的板文件中对DM9000添加的内容。
代码清单16.20 LDD6410板文件中的DM9000的平台设备
1 static struct resource ldd6410_dm9000_resource[] = {
2 [0] = {
3 .start = 0x18000000,
4 .end = 0x18000000 + 3,
5 .flags = IORESOURCE_MEM
6 },
7 [1] = {
8 .start = 0x18000000 + 0x4,
9 .end = 0x18000000 + 0x7,
10 .flags = IORESOURCE_MEM
11 },
12 [2] = {
13 .start = IRQ_EINT(7),
14 .end = IRQ_EINT(7),
15 .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
16 }
17
18 };
19
20 static struct dm9000_plat_data ldd6410_dm9000__platdata = {
21 .flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO__EEPROM,
22 .dev_addr = { 0x0, 0x16, 0xd4, 0x9f, 0xed, 0xa4 },
23 };
24
25 static struct platform__device ldd6410_dm9000 = {
26 .name = "dm9000",
27 .id = 0,
28 .num_resources = ARRAY_SIZE(ldd6410_dm9000_resource),
29 .resource = ldd6410_dm9000_resource,
30 .dev = {
31 .platform_data = &ldd6410_dm9000_platdata,
32 }
33 };
另外,LDD6410开发板的U-BOOT也全面支持DM9000,这使得我们可以在U-BOOT中运行网络命令或通过tftp下载Linux内核运行,例如:
执行ping:
LDD6410 # ping 192.168.1.111
dm9000 i/o: 0x18000300, id: 0x90000a46
MAC: 00:40:5c:26:0a:5b
operating at 100M full duplex mode host 192.168.1.111 is alive
LDD6410 #
下载zImage:
LDD6410 # tftp 0xc0008000 zImage
dm9000 i/o: 0x18000300, id: 0x90000a46
MAC: 00:40:5c:26:0a:5b
operating at 100M full duplex mode
TFTP from server 192.168.1.111; our IP address is 192.168.1.20
Filename 'zImage'.
Load address: 0xc0008000
Loading: #################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
########################################################
done
Bytes transferred = 2279916 (22c9ec hex)
启动内核:
LDD6410 # bootm 0xc0008000
Boot with zImage
Starting kernel ...