Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
629 | giacomo | 1 | /* netdrv_init.c: Initialization for network devices. */ |
2 | /* |
||
3 | Written 1993,1994,1995 by Donald Becker. |
||
4 | |||
5 | The author may be reached as becker@cesdis.gsfc.nasa.gov or |
||
6 | C/O Center of Excellence in Space Data and Information Sciences |
||
7 | Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771 |
||
8 | |||
9 | This file contains the initialization for the "pl14+" style ethernet |
||
10 | drivers. It should eventually replace most of drivers/net/Space.c. |
||
11 | It's primary advantage is that it's able to allocate low-memory buffers. |
||
12 | A secondary advantage is that the dangerous NE*000 netcards can reserve |
||
13 | their I/O port region before the SCSI probes start. |
||
14 | |||
15 | Modifications/additions by Bjorn Ekwall <bj0rn@blox.se>: |
||
16 | ethdev_index[MAX_ETH_CARDS] |
||
17 | register_netdev() / unregister_netdev() |
||
18 | |||
19 | Modifications by Wolfgang Walter |
||
20 | Use dev_close cleanly so we always shut things down tidily. |
||
21 | |||
22 | Changed 29/10/95, Alan Cox to pass sockaddr's around for mac addresses. |
||
23 | |||
24 | 14/06/96 - Paul Gortmaker: Add generic eth_change_mtu() function. |
||
25 | |||
26 | August 12, 1996 - Lawrence V. Stefani: Added fddi_change_mtu() and |
||
27 | fddi_setup() functions. |
||
28 | Sept. 10, 1996 - Lawrence V. Stefani: Increased hard_header_len to |
||
29 | include 3 pad bytes. |
||
30 | */ |
||
31 | |||
32 | #include <linux/config.h> |
||
33 | #include <linux/kernel.h> |
||
34 | #include <linux/sched.h> |
||
35 | #include <linux/types.h> |
||
36 | /* #include <linux/fs.h> */ |
||
37 | #include <linux/malloc.h> |
||
38 | #include <linux/if_ether.h> |
||
39 | #include <linux/string.h> |
||
40 | #include <linux/netdevice.h> |
||
41 | #include <linux/etherdevice.h> |
||
42 | /* #include <linux/fddidevice.h> |
||
43 | //#include <linux/trdevice.h> */ |
||
44 | #include <linux/if_arp.h> |
||
45 | #ifdef CONFIG_NET_ALIAS |
||
46 | #include <linux/net_alias.h> |
||
47 | #endif |
||
48 | |||
49 | #include <string.h> |
||
50 | |||
51 | /* The network devices currently exist only in the socket namespace, so these |
||
52 | entries are unused. The only ones that make sense are |
||
53 | open start the ethercard |
||
54 | close stop the ethercard |
||
55 | ioctl To get statistics, perhaps set the interface port (AUI, BNC, etc.) |
||
56 | One can also imagine getting raw packets using |
||
57 | read & write |
||
58 | but this is probably better handled by a raw packet socket. |
||
59 | |||
60 | Given that almost all of these functions are handled in the current |
||
61 | socket-based scheme, putting ethercard devices in /dev/ seems pointless. |
||
62 | |||
63 | [Removed all support for /dev network devices. When someone adds |
||
64 | streams then by magic we get them, but otherwise they are un-needed |
||
65 | and a space waste] |
||
66 | */ |
||
67 | |||
68 | /* The list of used and available "eth" slots (for "eth0", "eth1", etc.) */ |
||
69 | #define MAX_ETH_CARDS 16 /* same as the number if irq's in irq2dev[] */ |
||
70 | static struct device *ethdev_index[MAX_ETH_CARDS]; |
||
71 | |||
72 | |||
73 | /* Fill in the fields of the device structure with ethernet-generic values. |
||
74 | |||
75 | If no device structure is passed, a new one is constructed, complete with |
||
76 | a SIZEOF_PRIVATE private data area. |
||
77 | |||
78 | If an empty string area is passed as dev->name, or a new structure is made, |
||
79 | a new name string is constructed. The passed string area should be 8 bytes |
||
80 | long. |
||
81 | */ |
||
82 | |||
83 | struct device * |
||
84 | init_etherdev(struct device *dev, int sizeof_priv) |
||
85 | { |
||
86 | int new_device = 0; |
||
87 | int i; |
||
88 | |||
89 | /* Use an existing correctly named device in Space.c:dev_base. */ |
||
90 | if (dev == NULL) { |
||
91 | int alloc_size = sizeof(struct device) + sizeof("eth%d ") |
||
92 | + sizeof_priv + 3; |
||
93 | struct device *cur_dev; |
||
94 | char pname[8]; /* Putative name for the device. */ |
||
95 | |||
96 | for (i = 0; i < MAX_ETH_CARDS; ++i) |
||
97 | if (ethdev_index[i] == NULL) { |
||
98 | sprintf(pname, "eth%d", i); |
||
99 | for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next) |
||
100 | if (strcmp(pname, cur_dev->name) == 0) { |
||
101 | dev = cur_dev; |
||
102 | dev->init = NULL; |
||
103 | sizeof_priv = (sizeof_priv + 3) & ~3; |
||
104 | dev->priv = sizeof_priv |
||
105 | ? kmalloc(sizeof_priv, GFP_KERNEL) |
||
106 | : NULL; |
||
107 | if (dev->priv) memset(dev->priv, 0, sizeof_priv); |
||
108 | goto found; |
||
109 | } |
||
110 | } |
||
111 | |||
112 | alloc_size &= ~3; /* Round to dword boundary. */ |
||
113 | |||
114 | dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL); |
||
115 | memset(dev, 0, alloc_size); |
||
116 | if (sizeof_priv) |
||
117 | dev->priv = (void *) (dev + 1); |
||
118 | dev->name = sizeof_priv + (char *)(dev + 1); |
||
119 | new_device = 1; |
||
120 | } |
||
121 | |||
122 | found: /* From the double loop above. */ |
||
123 | |||
124 | if (dev->name && |
||
125 | ((dev->name[0] == '\0') || (dev->name[0] == ' '))) { |
||
126 | for (i = 0; i < MAX_ETH_CARDS; ++i) |
||
127 | if (ethdev_index[i] == NULL) { |
||
128 | sprintf(dev->name, "eth%d", i); |
||
129 | ethdev_index[i] = dev; |
||
130 | break; |
||
131 | } |
||
132 | } |
||
133 | |||
134 | ether_setup(dev); /* Hmmm, should this be called here? */ |
||
135 | |||
136 | if (new_device) { |
||
137 | /* Append the device to the device queue. */ |
||
138 | struct device **old_devp = &dev_base; |
||
139 | while ((*old_devp)->next) |
||
140 | old_devp = & (*old_devp)->next; |
||
141 | (*old_devp)->next = dev; |
||
142 | dev->next = 0; |
||
143 | } |
||
144 | return dev; |
||
145 | } |
||
146 | |||
147 | |||
148 | static int eth_mac_addr(struct device *dev, void *p) |
||
149 | { |
||
150 | struct sockaddr *addr=p; |
||
151 | if(dev->start) |
||
152 | return -EBUSY; |
||
153 | memcpy(dev->dev_addr, addr->sa_data,dev->addr_len); |
||
154 | return 0; |
||
155 | } |
||
156 | |||
157 | static int eth_change_mtu(struct device *dev, int new_mtu) |
||
158 | { |
||
159 | if ((new_mtu < 68) || (new_mtu > 1500)) |
||
160 | return -EINVAL; |
||
161 | dev->mtu = new_mtu; |
||
162 | return 0; |
||
163 | } |
||
164 | |||
165 | #ifdef CONFIG_FDDI |
||
166 | |||
167 | static int fddi_change_mtu(struct device *dev, int new_mtu) |
||
168 | { |
||
169 | if ((new_mtu < FDDI_K_SNAP_HLEN) || (new_mtu > FDDI_K_SNAP_DLEN)) |
||
170 | return(-EINVAL); |
||
171 | dev->mtu = new_mtu; |
||
172 | return(0); |
||
173 | } |
||
174 | |||
175 | #endif |
||
176 | |||
177 | void ether_setup(struct device *dev) |
||
178 | { |
||
179 | int i; |
||
180 | /* Fill in the fields of the device structure with ethernet-generic values. |
||
181 | This should be in a common file instead of per-driver. */ |
||
182 | for (i = 0; i < DEV_NUMBUFFS; i++) |
||
183 | skb_queue_head_init(&dev->buffs[i]); |
||
184 | |||
185 | /* register boot-defined "eth" devices */ |
||
186 | if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) { |
||
187 | /* i = simple_strtoul(dev->name + 3, NULL, 0); */ |
||
188 | if (ethdev_index[i] == NULL) { |
||
189 | ethdev_index[i] = dev; |
||
190 | } |
||
191 | else if (dev != ethdev_index[i]) { |
||
192 | /* Really shouldn't happen! */ |
||
193 | printk(KERN_ERR "ether_setup: Ouch! Someone else took %s\n", |
||
194 | dev->name); |
||
195 | } |
||
196 | } |
||
197 | |||
198 | dev->change_mtu = eth_change_mtu; |
||
199 | /* dev->hard_header = eth_header; */ |
||
200 | dev->rebuild_header = (void *)((void *)(eth_rebuild_header)); |
||
201 | dev->set_mac_address = eth_mac_addr; |
||
202 | dev->header_cache_bind = (void *)((void *)(eth_header_cache_bind)); |
||
203 | dev->header_cache_update= (void *)((void *)(eth_header_cache_update)); |
||
204 | |||
205 | dev->type = ARPHRD_ETHER; |
||
206 | dev->hard_header_len = ETH_HLEN; |
||
207 | dev->mtu = 1500; /* eth_mtu */ |
||
208 | dev->addr_len = ETH_ALEN; |
||
209 | dev->tx_queue_len = 100; /* Ethernet wants good queues */ |
||
210 | |||
211 | memset(dev->broadcast,0xFF, ETH_ALEN); |
||
212 | |||
213 | /* New-style flags. */ |
||
214 | dev->flags = IFF_BROADCAST|IFF_MULTICAST; |
||
215 | dev->family = AF_INET; |
||
216 | dev->pa_addr = 0; |
||
217 | dev->pa_brdaddr = 0; |
||
218 | dev->pa_mask = 0; |
||
219 | dev->pa_alen = 4; |
||
220 | } |
||
221 | |||
222 | #ifdef CONFIG_TR |
||
223 | |||
224 | void tr_setup(struct device *dev) |
||
225 | { |
||
226 | int i; |
||
227 | /* Fill in the fields of the device structure with ethernet-generic values. |
||
228 | This should be in a common file instead of per-driver. */ |
||
229 | for (i = 0; i < DEV_NUMBUFFS; i++) |
||
230 | skb_queue_head_init(&dev->buffs[i]); |
||
231 | |||
232 | dev->hard_header = tr_header; |
||
233 | dev->rebuild_header = tr_rebuild_header; |
||
234 | |||
235 | dev->type = ARPHRD_IEEE802; |
||
236 | dev->hard_header_len = TR_HLEN; |
||
237 | dev->mtu = 2000; /* bug in fragmenter...*/ |
||
238 | dev->addr_len = TR_ALEN; |
||
239 | dev->tx_queue_len = 100; /* Long queues on tr */ |
||
240 | |||
241 | memset(dev->broadcast,0xFF, TR_ALEN); |
||
242 | |||
243 | /* New-style flags. */ |
||
244 | dev->flags = IFF_BROADCAST; |
||
245 | dev->family = AF_INET; |
||
246 | dev->pa_addr = 0; |
||
247 | dev->pa_brdaddr = 0; |
||
248 | dev->pa_mask = 0; |
||
249 | dev->pa_alen = 4; |
||
250 | } |
||
251 | |||
252 | #endif |
||
253 | |||
254 | #ifdef CONFIG_FDDI |
||
255 | |||
256 | void fddi_setup(struct device *dev) |
||
257 | { |
||
258 | int i; |
||
259 | |||
260 | /* |
||
261 | * Fill in the fields of the device structure with FDDI-generic values. |
||
262 | * This should be in a common file instead of per-driver. |
||
263 | */ |
||
264 | for (i=0; i < DEV_NUMBUFFS; i++) |
||
265 | skb_queue_head_init(&dev->buffs[i]); |
||
266 | |||
267 | dev->change_mtu = fddi_change_mtu; |
||
268 | dev->hard_header = fddi_header; |
||
269 | dev->rebuild_header = fddi_rebuild_header; |
||
270 | |||
271 | dev->type = ARPHRD_FDDI; |
||
272 | dev->hard_header_len = FDDI_K_SNAP_HLEN+3; /* Assume 802.2 SNAP hdr len + 3 pad bytes */ |
||
273 | dev->mtu = FDDI_K_SNAP_DLEN; /* Assume max payload of 802.2 SNAP frame */ |
||
274 | dev->addr_len = FDDI_K_ALEN; |
||
275 | dev->tx_queue_len = 100; /* Long queues on FDDI */ |
||
276 | |||
277 | memset(dev->broadcast, 0xFF, FDDI_K_ALEN); |
||
278 | |||
279 | /* New-style flags */ |
||
280 | dev->flags = IFF_BROADCAST | IFF_MULTICAST; |
||
281 | dev->family = AF_INET; |
||
282 | dev->pa_addr = 0; |
||
283 | dev->pa_brdaddr = 0; |
||
284 | dev->pa_mask = 0; |
||
285 | dev->pa_alen = 4; |
||
286 | return; |
||
287 | } |
||
288 | |||
289 | #endif |
||
290 | |||
291 | int ether_config(struct device *dev, struct ifmap *map) |
||
292 | { |
||
293 | if (map->mem_start != (u_long)(-1)) |
||
294 | dev->mem_start = map->mem_start; |
||
295 | if (map->mem_end != (u_long)(-1)) |
||
296 | dev->mem_end = map->mem_end; |
||
297 | if (map->base_addr != (u_short)(-1)) |
||
298 | dev->base_addr = map->base_addr; |
||
299 | if (map->irq != (u_char)(-1)) |
||
300 | dev->irq = map->irq; |
||
301 | if (map->dma != (u_char)(-1)) |
||
302 | dev->dma = map->dma; |
||
303 | if (map->port != (u_char)(-1)) |
||
304 | dev->if_port = map->port; |
||
305 | return 0; |
||
306 | } |
||
307 | |||
308 | int register_netdev(struct device *dev) |
||
309 | { |
||
310 | struct device *d = dev_base; |
||
311 | unsigned long flags; |
||
312 | int i=MAX_ETH_CARDS; |
||
313 | |||
314 | save_flags(flags); |
||
315 | cli(); |
||
316 | |||
317 | if (dev && dev->init) { |
||
318 | if (dev->name && |
||
319 | ((dev->name[0] == '\0') || (dev->name[0] == ' '))) { |
||
320 | for (i = 0; i < MAX_ETH_CARDS; ++i) |
||
321 | if (ethdev_index[i] == NULL) { |
||
322 | sprintf(dev->name, "eth%d", i); |
||
323 | printk(KERN_INFO "Loading device '%s'...\n", dev->name); |
||
324 | ethdev_index[i] = dev; |
||
325 | break; |
||
326 | } |
||
327 | } |
||
328 | |||
329 | sti(); /* device probes assume interrupts enabled */ |
||
330 | if (dev->init(dev) != 0) { |
||
331 | if (i < MAX_ETH_CARDS) ethdev_index[i] = NULL; |
||
332 | restore_flags(flags); |
||
333 | return -EIO; |
||
334 | } |
||
335 | cli(); |
||
336 | |||
337 | /* Add device to end of chain */ |
||
338 | if (dev_base) { |
||
339 | while (d->next) |
||
340 | d = d->next; |
||
341 | d->next = dev; |
||
342 | } |
||
343 | else |
||
344 | dev_base = dev; |
||
345 | dev->next = NULL; |
||
346 | } |
||
347 | restore_flags(flags); |
||
348 | return 0; |
||
349 | } |
||
350 | |||
351 | void unregister_netdev(struct device *dev) |
||
352 | { |
||
353 | struct device *d = dev_base; |
||
354 | unsigned long flags; |
||
355 | int i; |
||
356 | |||
357 | save_flags(flags); |
||
358 | cli(); |
||
359 | |||
360 | if (dev == NULL) |
||
361 | { |
||
362 | printk("was NULL\n"); |
||
363 | restore_flags(flags); |
||
364 | return; |
||
365 | } |
||
366 | /* else */ |
||
367 | if (dev->start) |
||
368 | printk("ERROR '%s' busy and not MOD_IN_USE.\n", dev->name); |
||
369 | |||
370 | /* |
||
371 | * must jump over main_device+aliases |
||
372 | * avoid alias devices unregistration so that only |
||
373 | * net_alias module manages them |
||
374 | */ |
||
375 | #ifdef CONFIG_NET_ALIAS |
||
376 | if (dev_base == dev) |
||
377 | dev_base = net_alias_nextdev(dev); |
||
378 | else |
||
379 | { |
||
380 | while(d && (net_alias_nextdev(d) != dev)) /* skip aliases */ |
||
381 | d = net_alias_nextdev(d); |
||
382 | |||
383 | if (d && (net_alias_nextdev(d) == dev)) |
||
384 | { |
||
385 | /* |
||
386 | * Critical: Bypass by consider devices as blocks (maindev+aliases) |
||
387 | */ |
||
388 | net_alias_nextdev_set(d, net_alias_nextdev(dev)); |
||
389 | } |
||
390 | #else |
||
391 | if (dev_base == dev) |
||
392 | dev_base = dev->next; |
||
393 | else |
||
394 | { |
||
395 | while (d && (d->next != dev)) |
||
396 | d = d->next; |
||
397 | |||
398 | if (d && (d->next == dev)) |
||
399 | { |
||
400 | d->next = dev->next; |
||
401 | } |
||
402 | #endif |
||
403 | else |
||
404 | { |
||
405 | printk("unregister_netdev: '%s' not found\n", dev->name); |
||
406 | restore_flags(flags); |
||
407 | return; |
||
408 | } |
||
409 | } |
||
410 | for (i = 0; i < MAX_ETH_CARDS; ++i) |
||
411 | { |
||
412 | if (ethdev_index[i] == dev) |
||
413 | { |
||
414 | ethdev_index[i] = NULL; |
||
415 | break; |
||
416 | } |
||
417 | } |
||
418 | |||
419 | restore_flags(flags); |
||
420 | |||
421 | /* |
||
422 | * You can i.e use a interfaces in a route though it is not up. |
||
423 | * We call close_dev (which is changed: it will down a device even if |
||
424 | * dev->flags==0 (but it will not call dev->stop if IFF_UP |
||
425 | * is not set). |
||
426 | * This will call notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev), |
||
427 | * dev_mc_discard(dev), .... |
||
428 | */ |
||
429 | |||
430 | /* I Disable this 4 now... |
||
431 | dev_close(dev); |
||
432 | */ |
||
433 | } |
||
434 | |||
435 | |||
436 | /* |
||
437 | * Local variables: |
||
438 | * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c net_init.c" |
||
439 | * version-control: t |
||
440 | * kept-new-versions: 5 |
||
441 | * tab-width: 4 |
||
442 | * End: |
||
443 | */ |