diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/ni5010.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/net/ni5010.c')
-rw-r--r-- | drivers/net/ni5010.c | 812 |
1 files changed, 812 insertions, 0 deletions
diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c new file mode 100644 index 000000000000..2ab01a5d1d22 --- /dev/null +++ b/drivers/net/ni5010.c | |||
@@ -0,0 +1,812 @@ | |||
1 | /* ni5010.c: A network driver for the MiCom-Interlan NI5010 ethercard. | ||
2 | * | ||
3 | * Copyright 1996,1997 Jan-Pascal van Best and Andreas Mohr. | ||
4 | * | ||
5 | * This software may be used and distributed according to the terms | ||
6 | * of the GNU General Public License, incorporated herein by reference. | ||
7 | * | ||
8 | * The authors may be reached as: | ||
9 | * jvbest@wi.leidenuniv.nl a.mohr@mailto.de | ||
10 | * or by snail mail as | ||
11 | * Jan-Pascal van Best Andreas Mohr | ||
12 | * Klikspaanweg 58-4 Stauferstr. 6 | ||
13 | * 2324 LZ Leiden D-71272 Renningen | ||
14 | * The Netherlands Germany | ||
15 | * | ||
16 | * Sources: | ||
17 | * Donald Becker's "skeleton.c" | ||
18 | * Crynwr ni5010 packet driver | ||
19 | * | ||
20 | * Changes: | ||
21 | * v0.0: First test version | ||
22 | * v0.1: First working version | ||
23 | * v0.2: | ||
24 | * v0.3->v0.90: Now demand setting io and irq when loading as module | ||
25 | * 970430 v0.91: modified for Linux 2.1.14 | ||
26 | * v0.92: Implemented Andreas' (better) NI5010 probe | ||
27 | * 970503 v0.93: Fixed auto-irq failure on warm reboot (JB) | ||
28 | * 970623 v1.00: First kernel version (AM) | ||
29 | * 970814 v1.01: Added detection of onboard receive buffer size (AM) | ||
30 | * Bugs: | ||
31 | * - None known... | ||
32 | * - Note that you have to patch ifconfig for the new /proc/net/dev | ||
33 | * format. It gives incorrect stats otherwise. | ||
34 | * | ||
35 | * To do: | ||
36 | * Fix all bugs :-) | ||
37 | * Move some stuff to chipset_init() | ||
38 | * Handle xmt errors other than collisions | ||
39 | * Complete merge with Andreas' driver | ||
40 | * Implement ring buffers (Is this useful? You can't squeeze | ||
41 | * too many packet in a 2k buffer!) | ||
42 | * Implement DMA (Again, is this useful? Some docs says DMA is | ||
43 | * slower than programmed I/O) | ||
44 | * | ||
45 | * Compile with: | ||
46 | * gcc -O2 -fomit-frame-pointer -m486 -D__KERNEL__ \ | ||
47 | * -DMODULE -c ni5010.c | ||
48 | * | ||
49 | * Insert with e.g.: | ||
50 | * insmod ni5010.o io=0x300 irq=5 | ||
51 | */ | ||
52 | |||
53 | #include <linux/module.h> | ||
54 | #include <linux/kernel.h> | ||
55 | #include <linux/string.h> | ||
56 | #include <linux/errno.h> | ||
57 | #include <linux/ioport.h> | ||
58 | #include <linux/slab.h> | ||
59 | #include <linux/interrupt.h> | ||
60 | #include <linux/delay.h> | ||
61 | #include <linux/init.h> | ||
62 | #include <linux/bitops.h> | ||
63 | #include <asm/io.h> | ||
64 | #include <asm/dma.h> | ||
65 | |||
66 | #include <linux/netdevice.h> | ||
67 | #include <linux/etherdevice.h> | ||
68 | #include <linux/skbuff.h> | ||
69 | |||
70 | #include "ni5010.h" | ||
71 | |||
72 | static const char *boardname = "NI5010"; | ||
73 | static char *version = | ||
74 | "ni5010.c: v1.00 06/23/97 Jan-Pascal van Best and Andreas Mohr\n"; | ||
75 | |||
76 | /* bufsize_rcv == 0 means autoprobing */ | ||
77 | static unsigned int bufsize_rcv; | ||
78 | |||
79 | #define jumpered_interrupts /* IRQ line jumpered on board */ | ||
80 | #undef jumpered_dma /* No DMA used */ | ||
81 | #undef FULL_IODETECT /* Only detect in portlist */ | ||
82 | |||
83 | #ifndef FULL_IODETECT | ||
84 | /* A zero-terminated list of I/O addresses to be probed. */ | ||
85 | static unsigned int ports[] __initdata = | ||
86 | { 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0 }; | ||
87 | #endif | ||
88 | |||
89 | /* Use 0 for production, 1 for verification, >2 for debug */ | ||
90 | #ifndef NI5010_DEBUG | ||
91 | #define NI5010_DEBUG 0 | ||
92 | #endif | ||
93 | |||
94 | /* Information that needs to be kept for each board. */ | ||
95 | struct ni5010_local { | ||
96 | struct net_device_stats stats; | ||
97 | int o_pkt_size; | ||
98 | spinlock_t lock; | ||
99 | }; | ||
100 | |||
101 | /* Index to functions, as function prototypes. */ | ||
102 | |||
103 | static int ni5010_probe1(struct net_device *dev, int ioaddr); | ||
104 | static int ni5010_open(struct net_device *dev); | ||
105 | static int ni5010_send_packet(struct sk_buff *skb, struct net_device *dev); | ||
106 | static irqreturn_t ni5010_interrupt(int irq, void *dev_id, struct pt_regs *regs); | ||
107 | static void ni5010_rx(struct net_device *dev); | ||
108 | static void ni5010_timeout(struct net_device *dev); | ||
109 | static int ni5010_close(struct net_device *dev); | ||
110 | static struct net_device_stats *ni5010_get_stats(struct net_device *dev); | ||
111 | static void ni5010_set_multicast_list(struct net_device *dev); | ||
112 | static void reset_receiver(struct net_device *dev); | ||
113 | |||
114 | static int process_xmt_interrupt(struct net_device *dev); | ||
115 | #define tx_done(dev) 1 | ||
116 | static void hardware_send_packet(struct net_device *dev, char *buf, int length, int pad); | ||
117 | static void chipset_init(struct net_device *dev, int startp); | ||
118 | static void dump_packet(void *buf, int len); | ||
119 | static void ni5010_show_registers(struct net_device *dev); | ||
120 | |||
121 | static int io; | ||
122 | static int irq; | ||
123 | |||
124 | struct net_device * __init ni5010_probe(int unit) | ||
125 | { | ||
126 | struct net_device *dev = alloc_etherdev(sizeof(struct ni5010_local)); | ||
127 | int *port; | ||
128 | int err = 0; | ||
129 | |||
130 | if (!dev) | ||
131 | return ERR_PTR(-ENOMEM); | ||
132 | |||
133 | if (unit >= 0) { | ||
134 | sprintf(dev->name, "eth%d", unit); | ||
135 | netdev_boot_setup_check(dev); | ||
136 | io = dev->base_addr; | ||
137 | irq = dev->irq; | ||
138 | } | ||
139 | |||
140 | PRINTK2((KERN_DEBUG "%s: Entering ni5010_probe\n", dev->name)); | ||
141 | |||
142 | SET_MODULE_OWNER(dev); | ||
143 | |||
144 | if (io > 0x1ff) { /* Check a single specified location. */ | ||
145 | err = ni5010_probe1(dev, io); | ||
146 | } else if (io != 0) { /* Don't probe at all. */ | ||
147 | err = -ENXIO; | ||
148 | } else { | ||
149 | #ifdef FULL_IODETECT | ||
150 | for (io=0x200; io<0x400 && ni5010_probe1(dev, io) ; io+=0x20) | ||
151 | ; | ||
152 | if (io == 0x400) | ||
153 | err = -ENODEV; | ||
154 | |||
155 | #else | ||
156 | for (port = ports; *port && ni5010_probe1(dev, *port); port++) | ||
157 | ; | ||
158 | if (!*port) | ||
159 | err = -ENODEV; | ||
160 | #endif /* FULL_IODETECT */ | ||
161 | } | ||
162 | if (err) | ||
163 | goto out; | ||
164 | err = register_netdev(dev); | ||
165 | if (err) | ||
166 | goto out1; | ||
167 | return dev; | ||
168 | out1: | ||
169 | release_region(dev->base_addr, NI5010_IO_EXTENT); | ||
170 | out: | ||
171 | free_netdev(dev); | ||
172 | return ERR_PTR(err); | ||
173 | } | ||
174 | |||
175 | static inline int rd_port(int ioaddr) | ||
176 | { | ||
177 | inb(IE_RBUF); | ||
178 | return inb(IE_SAPROM); | ||
179 | } | ||
180 | |||
181 | static void __init trigger_irq(int ioaddr) | ||
182 | { | ||
183 | outb(0x00, EDLC_RESET); /* Clear EDLC hold RESET state */ | ||
184 | outb(0x00, IE_RESET); /* Board reset */ | ||
185 | outb(0x00, EDLC_XMASK); /* Disable all Xmt interrupts */ | ||
186 | outb(0x00, EDLC_RMASK); /* Disable all Rcv interrupt */ | ||
187 | outb(0xff, EDLC_XCLR); /* Clear all pending Xmt interrupts */ | ||
188 | outb(0xff, EDLC_RCLR); /* Clear all pending Rcv interrupts */ | ||
189 | /* | ||
190 | * Transmit packet mode: Ignore parity, Power xcvr, | ||
191 | * Enable loopback | ||
192 | */ | ||
193 | outb(XMD_IG_PAR | XMD_T_MODE | XMD_LBC, EDLC_XMODE); | ||
194 | outb(RMD_BROADCAST, EDLC_RMODE); /* Receive normal&broadcast */ | ||
195 | outb(XM_ALL, EDLC_XMASK); /* Enable all Xmt interrupts */ | ||
196 | udelay(50); /* FIXME: Necessary? */ | ||
197 | outb(MM_EN_XMT|MM_MUX, IE_MMODE); /* Start transmission */ | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * This is the real probe routine. Linux has a history of friendly device | ||
202 | * probes on the ISA bus. A good device probes avoids doing writes, and | ||
203 | * verifies that the correct device exists and functions. | ||
204 | */ | ||
205 | |||
206 | static int __init ni5010_probe1(struct net_device *dev, int ioaddr) | ||
207 | { | ||
208 | static unsigned version_printed; | ||
209 | struct ni5010_local *lp; | ||
210 | int i; | ||
211 | unsigned int data = 0; | ||
212 | int boguscount = 40; | ||
213 | int err = -ENODEV; | ||
214 | |||
215 | dev->base_addr = ioaddr; | ||
216 | dev->irq = irq; | ||
217 | |||
218 | if (!request_region(ioaddr, NI5010_IO_EXTENT, boardname)) | ||
219 | return -EBUSY; | ||
220 | |||
221 | /* | ||
222 | * This is no "official" probe method, I've rather tested which | ||
223 | * probe works best with my seven NI5010 cards | ||
224 | * (they have very different serial numbers) | ||
225 | * Suggestions or failure reports are very, very welcome ! | ||
226 | * But I think it is a relatively good probe method | ||
227 | * since it doesn't use any "outb" | ||
228 | * It should be nearly 100% reliable ! | ||
229 | * well-known WARNING: this probe method (like many others) | ||
230 | * will hang the system if a NE2000 card region is probed ! | ||
231 | * | ||
232 | * - Andreas | ||
233 | */ | ||
234 | |||
235 | PRINTK2((KERN_DEBUG "%s: entering ni5010_probe1(%#3x)\n", | ||
236 | dev->name, ioaddr)); | ||
237 | |||
238 | if (inb(ioaddr+0) == 0xff) | ||
239 | goto out; | ||
240 | |||
241 | while ( (rd_port(ioaddr) & rd_port(ioaddr) & rd_port(ioaddr) & | ||
242 | rd_port(ioaddr) & rd_port(ioaddr) & rd_port(ioaddr)) != 0xff) | ||
243 | { | ||
244 | if (boguscount-- == 0) | ||
245 | goto out; | ||
246 | } | ||
247 | |||
248 | PRINTK2((KERN_DEBUG "%s: I/O #1 passed!\n", dev->name)); | ||
249 | |||
250 | for (i=0; i<32; i++) | ||
251 | if ( (data = rd_port(ioaddr)) != 0xff) break; | ||
252 | if (data==0xff) | ||
253 | goto out; | ||
254 | |||
255 | PRINTK2((KERN_DEBUG "%s: I/O #2 passed!\n", dev->name)); | ||
256 | |||
257 | if ((data != SA_ADDR0) || (rd_port(ioaddr) != SA_ADDR1) || | ||
258 | (rd_port(ioaddr) != SA_ADDR2)) | ||
259 | goto out; | ||
260 | |||
261 | for (i=0; i<4; i++) | ||
262 | rd_port(ioaddr); | ||
263 | |||
264 | if ( (rd_port(ioaddr) != NI5010_MAGICVAL1) || | ||
265 | (rd_port(ioaddr) != NI5010_MAGICVAL2) ) | ||
266 | goto out; | ||
267 | |||
268 | PRINTK2((KERN_DEBUG "%s: I/O #3 passed!\n", dev->name)); | ||
269 | |||
270 | if (NI5010_DEBUG && version_printed++ == 0) | ||
271 | printk(KERN_INFO "%s", version); | ||
272 | |||
273 | printk("NI5010 ethercard probe at 0x%x: ", ioaddr); | ||
274 | |||
275 | dev->base_addr = ioaddr; | ||
276 | |||
277 | for (i=0; i<6; i++) { | ||
278 | outw(i, IE_GP); | ||
279 | printk("%2.2x ", dev->dev_addr[i] = inb(IE_SAPROM)); | ||
280 | } | ||
281 | |||
282 | PRINTK2((KERN_DEBUG "%s: I/O #4 passed!\n", dev->name)); | ||
283 | |||
284 | #ifdef jumpered_interrupts | ||
285 | if (dev->irq == 0xff) | ||
286 | ; | ||
287 | else if (dev->irq < 2) { | ||
288 | unsigned long irq_mask; | ||
289 | |||
290 | PRINTK2((KERN_DEBUG "%s: I/O #5 passed!\n", dev->name)); | ||
291 | |||
292 | irq_mask = probe_irq_on(); | ||
293 | trigger_irq(ioaddr); | ||
294 | mdelay(20); | ||
295 | dev->irq = probe_irq_off(irq_mask); | ||
296 | |||
297 | PRINTK2((KERN_DEBUG "%s: I/O #6 passed!\n", dev->name)); | ||
298 | |||
299 | if (dev->irq == 0) { | ||
300 | err = -EAGAIN; | ||
301 | printk(KERN_WARNING "%s: no IRQ found!\n", dev->name); | ||
302 | goto out; | ||
303 | } | ||
304 | PRINTK2((KERN_DEBUG "%s: I/O #7 passed!\n", dev->name)); | ||
305 | } else if (dev->irq == 2) { | ||
306 | dev->irq = 9; | ||
307 | } | ||
308 | #endif /* jumpered_irq */ | ||
309 | PRINTK2((KERN_DEBUG "%s: I/O #9 passed!\n", dev->name)); | ||
310 | |||
311 | /* DMA is not supported (yet?), so no use detecting it */ | ||
312 | lp = netdev_priv(dev); | ||
313 | |||
314 | spin_lock_init(&lp->lock); | ||
315 | |||
316 | PRINTK2((KERN_DEBUG "%s: I/O #10 passed!\n", dev->name)); | ||
317 | |||
318 | /* get the size of the onboard receive buffer | ||
319 | * higher addresses than bufsize are wrapped into real buffer | ||
320 | * i.e. data for offs. 0x801 is written to 0x1 with a 2K onboard buffer | ||
321 | */ | ||
322 | if (!bufsize_rcv) { | ||
323 | outb(1, IE_MMODE); /* Put Rcv buffer on system bus */ | ||
324 | outw(0, IE_GP); /* Point GP at start of packet */ | ||
325 | outb(0, IE_RBUF); /* set buffer byte 0 to 0 */ | ||
326 | for (i = 1; i < 0xff; i++) { | ||
327 | outw(i << 8, IE_GP); /* Point GP at packet size to be tested */ | ||
328 | outb(i, IE_RBUF); | ||
329 | outw(0x0, IE_GP); /* Point GP at start of packet */ | ||
330 | data = inb(IE_RBUF); | ||
331 | if (data == i) break; | ||
332 | } | ||
333 | bufsize_rcv = i << 8; | ||
334 | outw(0, IE_GP); /* Point GP at start of packet */ | ||
335 | outb(0, IE_RBUF); /* set buffer byte 0 to 0 again */ | ||
336 | } | ||
337 | printk("// bufsize rcv/xmt=%d/%d\n", bufsize_rcv, NI5010_BUFSIZE); | ||
338 | memset(dev->priv, 0, sizeof(struct ni5010_local)); | ||
339 | |||
340 | dev->open = ni5010_open; | ||
341 | dev->stop = ni5010_close; | ||
342 | dev->hard_start_xmit = ni5010_send_packet; | ||
343 | dev->get_stats = ni5010_get_stats; | ||
344 | dev->set_multicast_list = ni5010_set_multicast_list; | ||
345 | dev->tx_timeout = ni5010_timeout; | ||
346 | dev->watchdog_timeo = HZ/20; | ||
347 | |||
348 | dev->flags &= ~IFF_MULTICAST; /* Multicast doesn't work */ | ||
349 | |||
350 | /* Shut up the ni5010 */ | ||
351 | outb(0, EDLC_RMASK); /* Mask all receive interrupts */ | ||
352 | outb(0, EDLC_XMASK); /* Mask all xmit interrupts */ | ||
353 | outb(0xff, EDLC_RCLR); /* Kill all pending rcv interrupts */ | ||
354 | outb(0xff, EDLC_XCLR); /* Kill all pending xmt interrupts */ | ||
355 | |||
356 | printk(KERN_INFO "%s: NI5010 found at 0x%x, using IRQ %d", dev->name, ioaddr, dev->irq); | ||
357 | if (dev->dma) printk(" & DMA %d", dev->dma); | ||
358 | printk(".\n"); | ||
359 | |||
360 | printk(KERN_INFO "Join the NI5010 driver development team!\n"); | ||
361 | printk(KERN_INFO "Mail to a.mohr@mailto.de or jvbest@wi.leidenuniv.nl\n"); | ||
362 | return 0; | ||
363 | out: | ||
364 | release_region(dev->base_addr, NI5010_IO_EXTENT); | ||
365 | return err; | ||
366 | } | ||
367 | |||
368 | /* | ||
369 | * Open/initialize the board. This is called (in the current kernel) | ||
370 | * sometime after booting when the 'ifconfig' program is run. | ||
371 | * | ||
372 | * This routine should set everything up anew at each open, even | ||
373 | * registers that "should" only need to be set once at boot, so that | ||
374 | * there is non-reboot way to recover if something goes wrong. | ||
375 | */ | ||
376 | |||
377 | static int ni5010_open(struct net_device *dev) | ||
378 | { | ||
379 | int ioaddr = dev->base_addr; | ||
380 | int i; | ||
381 | |||
382 | PRINTK2((KERN_DEBUG "%s: entering ni5010_open()\n", dev->name)); | ||
383 | |||
384 | if (request_irq(dev->irq, &ni5010_interrupt, 0, boardname, dev)) { | ||
385 | printk(KERN_WARNING "%s: Cannot get irq %#2x\n", dev->name, dev->irq); | ||
386 | return -EAGAIN; | ||
387 | } | ||
388 | PRINTK3((KERN_DEBUG "%s: passed open() #1\n", dev->name)); | ||
389 | /* | ||
390 | * Always allocate the DMA channel after the IRQ, | ||
391 | * and clean up on failure. | ||
392 | */ | ||
393 | #ifdef jumpered_dma | ||
394 | if (request_dma(dev->dma, cardname)) { | ||
395 | printk(KERN_WARNING "%s: Cannot get dma %#2x\n", dev->name, dev->dma); | ||
396 | free_irq(dev->irq, NULL); | ||
397 | return -EAGAIN; | ||
398 | } | ||
399 | #endif /* jumpered_dma */ | ||
400 | |||
401 | PRINTK3((KERN_DEBUG "%s: passed open() #2\n", dev->name)); | ||
402 | /* Reset the hardware here. Don't forget to set the station address. */ | ||
403 | |||
404 | outb(RS_RESET, EDLC_RESET); /* Hold up EDLC_RESET while configing board */ | ||
405 | outb(0, IE_RESET); /* Hardware reset of ni5010 board */ | ||
406 | outb(XMD_LBC, EDLC_XMODE); /* Only loopback xmits */ | ||
407 | |||
408 | PRINTK3((KERN_DEBUG "%s: passed open() #3\n", dev->name)); | ||
409 | /* Set the station address */ | ||
410 | for(i = 0;i < 6; i++) { | ||
411 | outb(dev->dev_addr[i], EDLC_ADDR + i); | ||
412 | } | ||
413 | |||
414 | PRINTK3((KERN_DEBUG "%s: Initialising ni5010\n", dev->name)); | ||
415 | outb(0, EDLC_XMASK); /* No xmit interrupts for now */ | ||
416 | outb(XMD_IG_PAR | XMD_T_MODE | XMD_LBC, EDLC_XMODE); | ||
417 | /* Normal packet xmit mode */ | ||
418 | outb(0xff, EDLC_XCLR); /* Clear all pending xmit interrupts */ | ||
419 | outb(RMD_BROADCAST, EDLC_RMODE); | ||
420 | /* Receive broadcast and normal packets */ | ||
421 | reset_receiver(dev); /* Ready ni5010 for receiving packets */ | ||
422 | |||
423 | outb(0, EDLC_RESET); /* Un-reset the ni5010 */ | ||
424 | |||
425 | netif_start_queue(dev); | ||
426 | |||
427 | if (NI5010_DEBUG) ni5010_show_registers(dev); | ||
428 | |||
429 | PRINTK((KERN_DEBUG "%s: open successful\n", dev->name)); | ||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static void reset_receiver(struct net_device *dev) | ||
434 | { | ||
435 | int ioaddr = dev->base_addr; | ||
436 | |||
437 | PRINTK3((KERN_DEBUG "%s: resetting receiver\n", dev->name)); | ||
438 | outw(0, IE_GP); /* Receive packet at start of buffer */ | ||
439 | outb(0xff, EDLC_RCLR); /* Clear all pending rcv interrupts */ | ||
440 | outb(0, IE_MMODE); /* Put EDLC to rcv buffer */ | ||
441 | outb(MM_EN_RCV, IE_MMODE); /* Enable rcv */ | ||
442 | outb(0xff, EDLC_RMASK); /* Enable all rcv interrupts */ | ||
443 | } | ||
444 | |||
445 | static void ni5010_timeout(struct net_device *dev) | ||
446 | { | ||
447 | printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, | ||
448 | tx_done(dev) ? "IRQ conflict" : "network cable problem"); | ||
449 | /* Try to restart the adaptor. */ | ||
450 | /* FIXME: Give it a real kick here */ | ||
451 | chipset_init(dev, 1); | ||
452 | dev->trans_start = jiffies; | ||
453 | netif_wake_queue(dev); | ||
454 | } | ||
455 | |||
456 | static int ni5010_send_packet(struct sk_buff *skb, struct net_device *dev) | ||
457 | { | ||
458 | int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; | ||
459 | |||
460 | PRINTK2((KERN_DEBUG "%s: entering ni5010_send_packet\n", dev->name)); | ||
461 | |||
462 | /* | ||
463 | * Block sending | ||
464 | */ | ||
465 | |||
466 | netif_stop_queue(dev); | ||
467 | hardware_send_packet(dev, (unsigned char *)skb->data, skb->len, length-skb->len); | ||
468 | dev->trans_start = jiffies; | ||
469 | dev_kfree_skb (skb); | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | /* | ||
474 | * The typical workload of the driver: | ||
475 | * Handle the network interface interrupts. | ||
476 | */ | ||
477 | static irqreturn_t ni5010_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
478 | { | ||
479 | struct net_device *dev = dev_id; | ||
480 | struct ni5010_local *lp; | ||
481 | int ioaddr, status; | ||
482 | int xmit_was_error = 0; | ||
483 | |||
484 | PRINTK2((KERN_DEBUG "%s: entering ni5010_interrupt\n", dev->name)); | ||
485 | |||
486 | ioaddr = dev->base_addr; | ||
487 | lp = netdev_priv(dev); | ||
488 | |||
489 | spin_lock(&lp->lock); | ||
490 | status = inb(IE_ISTAT); | ||
491 | PRINTK3((KERN_DEBUG "%s: IE_ISTAT = %#02x\n", dev->name, status)); | ||
492 | |||
493 | if ((status & IS_R_INT) == 0) ni5010_rx(dev); | ||
494 | |||
495 | if ((status & IS_X_INT) == 0) { | ||
496 | xmit_was_error = process_xmt_interrupt(dev); | ||
497 | } | ||
498 | |||
499 | if ((status & IS_DMA_INT) == 0) { | ||
500 | PRINTK((KERN_DEBUG "%s: DMA complete (?)\n", dev->name)); | ||
501 | outb(0, IE_DMA_RST); /* Reset DMA int */ | ||
502 | } | ||
503 | |||
504 | if (!xmit_was_error) | ||
505 | reset_receiver(dev); | ||
506 | spin_unlock(&lp->lock); | ||
507 | return IRQ_HANDLED; | ||
508 | } | ||
509 | |||
510 | |||
511 | static void dump_packet(void *buf, int len) | ||
512 | { | ||
513 | int i; | ||
514 | |||
515 | printk(KERN_DEBUG "Packet length = %#4x\n", len); | ||
516 | for (i = 0; i < len; i++){ | ||
517 | if (i % 16 == 0) printk(KERN_DEBUG "%#4.4x", i); | ||
518 | if (i % 2 == 0) printk(" "); | ||
519 | printk("%2.2x", ((unsigned char *)buf)[i]); | ||
520 | if (i % 16 == 15) printk("\n"); | ||
521 | } | ||
522 | printk("\n"); | ||
523 | |||
524 | return; | ||
525 | } | ||
526 | |||
527 | /* We have a good packet, get it out of the buffer. */ | ||
528 | static void ni5010_rx(struct net_device *dev) | ||
529 | { | ||
530 | struct ni5010_local *lp = netdev_priv(dev); | ||
531 | int ioaddr = dev->base_addr; | ||
532 | unsigned char rcv_stat; | ||
533 | struct sk_buff *skb; | ||
534 | int i_pkt_size; | ||
535 | |||
536 | PRINTK2((KERN_DEBUG "%s: entering ni5010_rx()\n", dev->name)); | ||
537 | |||
538 | rcv_stat = inb(EDLC_RSTAT); | ||
539 | PRINTK3((KERN_DEBUG "%s: EDLC_RSTAT = %#2x\n", dev->name, rcv_stat)); | ||
540 | |||
541 | if ( (rcv_stat & RS_VALID_BITS) != RS_PKT_OK) { | ||
542 | PRINTK((KERN_INFO "%s: receive error.\n", dev->name)); | ||
543 | lp->stats.rx_errors++; | ||
544 | if (rcv_stat & RS_RUNT) lp->stats.rx_length_errors++; | ||
545 | if (rcv_stat & RS_ALIGN) lp->stats.rx_frame_errors++; | ||
546 | if (rcv_stat & RS_CRC_ERR) lp->stats.rx_crc_errors++; | ||
547 | if (rcv_stat & RS_OFLW) lp->stats.rx_fifo_errors++; | ||
548 | outb(0xff, EDLC_RCLR); /* Clear the interrupt */ | ||
549 | return; | ||
550 | } | ||
551 | |||
552 | outb(0xff, EDLC_RCLR); /* Clear the interrupt */ | ||
553 | |||
554 | i_pkt_size = inw(IE_RCNT); | ||
555 | if (i_pkt_size > ETH_FRAME_LEN || i_pkt_size < 10 ) { | ||
556 | PRINTK((KERN_DEBUG "%s: Packet size error, packet size = %#4.4x\n", | ||
557 | dev->name, i_pkt_size)); | ||
558 | lp->stats.rx_errors++; | ||
559 | lp->stats.rx_length_errors++; | ||
560 | return; | ||
561 | } | ||
562 | |||
563 | /* Malloc up new buffer. */ | ||
564 | skb = dev_alloc_skb(i_pkt_size + 3); | ||
565 | if (skb == NULL) { | ||
566 | printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); | ||
567 | lp->stats.rx_dropped++; | ||
568 | return; | ||
569 | } | ||
570 | |||
571 | skb->dev = dev; | ||
572 | skb_reserve(skb, 2); | ||
573 | |||
574 | /* Read packet into buffer */ | ||
575 | outb(MM_MUX, IE_MMODE); /* Rcv buffer to system bus */ | ||
576 | outw(0, IE_GP); /* Seek to beginning of packet */ | ||
577 | insb(IE_RBUF, skb_put(skb, i_pkt_size), i_pkt_size); | ||
578 | |||
579 | if (NI5010_DEBUG >= 4) | ||
580 | dump_packet(skb->data, skb->len); | ||
581 | |||
582 | skb->protocol = eth_type_trans(skb,dev); | ||
583 | netif_rx(skb); | ||
584 | dev->last_rx = jiffies; | ||
585 | lp->stats.rx_packets++; | ||
586 | lp->stats.rx_bytes += i_pkt_size; | ||
587 | |||
588 | PRINTK2((KERN_DEBUG "%s: Received packet, size=%#4.4x\n", | ||
589 | dev->name, i_pkt_size)); | ||
590 | |||
591 | } | ||
592 | |||
593 | static int process_xmt_interrupt(struct net_device *dev) | ||
594 | { | ||
595 | struct ni5010_local *lp = netdev_priv(dev); | ||
596 | int ioaddr = dev->base_addr; | ||
597 | int xmit_stat; | ||
598 | |||
599 | PRINTK2((KERN_DEBUG "%s: entering process_xmt_interrupt\n", dev->name)); | ||
600 | |||
601 | xmit_stat = inb(EDLC_XSTAT); | ||
602 | PRINTK3((KERN_DEBUG "%s: EDLC_XSTAT = %2.2x\n", dev->name, xmit_stat)); | ||
603 | |||
604 | outb(0, EDLC_XMASK); /* Disable xmit IRQ's */ | ||
605 | outb(0xff, EDLC_XCLR); /* Clear all pending xmit IRQ's */ | ||
606 | |||
607 | if (xmit_stat & XS_COLL){ | ||
608 | PRINTK((KERN_DEBUG "%s: collision detected, retransmitting\n", | ||
609 | dev->name)); | ||
610 | outw(NI5010_BUFSIZE - lp->o_pkt_size, IE_GP); | ||
611 | /* outb(0, IE_MMODE); */ /* xmt buf on sysbus FIXME: needed ? */ | ||
612 | outb(MM_EN_XMT | MM_MUX, IE_MMODE); | ||
613 | outb(XM_ALL, EDLC_XMASK); /* Enable xmt IRQ's */ | ||
614 | lp->stats.collisions++; | ||
615 | return 1; | ||
616 | } | ||
617 | |||
618 | /* FIXME: handle other xmt error conditions */ | ||
619 | |||
620 | lp->stats.tx_packets++; | ||
621 | lp->stats.tx_bytes += lp->o_pkt_size; | ||
622 | netif_wake_queue(dev); | ||
623 | |||
624 | PRINTK2((KERN_DEBUG "%s: sent packet, size=%#4.4x\n", | ||
625 | dev->name, lp->o_pkt_size)); | ||
626 | |||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | /* The inverse routine to ni5010_open(). */ | ||
631 | static int ni5010_close(struct net_device *dev) | ||
632 | { | ||
633 | int ioaddr = dev->base_addr; | ||
634 | |||
635 | PRINTK2((KERN_DEBUG "%s: entering ni5010_close\n", dev->name)); | ||
636 | #ifdef jumpered_interrupts | ||
637 | free_irq(dev->irq, NULL); | ||
638 | #endif | ||
639 | /* Put card in held-RESET state */ | ||
640 | outb(0, IE_MMODE); | ||
641 | outb(RS_RESET, EDLC_RESET); | ||
642 | |||
643 | netif_stop_queue(dev); | ||
644 | |||
645 | PRINTK((KERN_DEBUG "%s: %s closed down\n", dev->name, boardname)); | ||
646 | return 0; | ||
647 | |||
648 | } | ||
649 | |||
650 | /* Get the current statistics. This may be called with the card open or | ||
651 | closed. */ | ||
652 | static struct net_device_stats *ni5010_get_stats(struct net_device *dev) | ||
653 | { | ||
654 | struct ni5010_local *lp = netdev_priv(dev); | ||
655 | |||
656 | PRINTK2((KERN_DEBUG "%s: entering ni5010_get_stats\n", dev->name)); | ||
657 | |||
658 | if (NI5010_DEBUG) ni5010_show_registers(dev); | ||
659 | |||
660 | /* cli(); */ | ||
661 | /* Update the statistics from the device registers. */ | ||
662 | /* We do this in the interrupt handler */ | ||
663 | /* sti(); */ | ||
664 | |||
665 | return &lp->stats; | ||
666 | } | ||
667 | |||
668 | /* Set or clear the multicast filter for this adaptor. | ||
669 | num_addrs == -1 Promiscuous mode, receive all packets | ||
670 | num_addrs == 0 Normal mode, clear multicast list | ||
671 | num_addrs > 0 Multicast mode, receive normal and MC packets, and do | ||
672 | best-effort filtering. | ||
673 | */ | ||
674 | static void ni5010_set_multicast_list(struct net_device *dev) | ||
675 | { | ||
676 | short ioaddr = dev->base_addr; | ||
677 | |||
678 | PRINTK2((KERN_DEBUG "%s: entering set_multicast_list\n", dev->name)); | ||
679 | |||
680 | if (dev->flags&IFF_PROMISC || dev->flags&IFF_ALLMULTI) { | ||
681 | dev->flags |= IFF_PROMISC; | ||
682 | outb(RMD_PROMISC, EDLC_RMODE); /* Enable promiscuous mode */ | ||
683 | PRINTK((KERN_DEBUG "%s: Entering promiscuous mode\n", dev->name)); | ||
684 | } else if (dev->mc_list) { | ||
685 | /* Sorry, multicast not supported */ | ||
686 | PRINTK((KERN_DEBUG "%s: No multicast, entering broadcast mode\n", dev->name)); | ||
687 | outb(RMD_BROADCAST, EDLC_RMODE); | ||
688 | } else { | ||
689 | PRINTK((KERN_DEBUG "%s: Entering broadcast mode\n", dev->name)); | ||
690 | outb(RMD_BROADCAST, EDLC_RMODE); /* Disable promiscuous mode, use normal mode */ | ||
691 | } | ||
692 | } | ||
693 | |||
694 | static void hardware_send_packet(struct net_device *dev, char *buf, int length, int pad) | ||
695 | { | ||
696 | struct ni5010_local *lp = netdev_priv(dev); | ||
697 | int ioaddr = dev->base_addr; | ||
698 | unsigned long flags; | ||
699 | unsigned int buf_offs; | ||
700 | |||
701 | PRINTK2((KERN_DEBUG "%s: entering hardware_send_packet\n", dev->name)); | ||
702 | |||
703 | if (length > ETH_FRAME_LEN) { | ||
704 | PRINTK((KERN_WARNING "%s: packet too large, not possible\n", | ||
705 | dev->name)); | ||
706 | return; | ||
707 | } | ||
708 | |||
709 | if (NI5010_DEBUG) ni5010_show_registers(dev); | ||
710 | |||
711 | if (inb(IE_ISTAT) & IS_EN_XMT) { | ||
712 | PRINTK((KERN_WARNING "%s: sending packet while already transmitting, not possible\n", | ||
713 | dev->name)); | ||
714 | return; | ||
715 | } | ||
716 | |||
717 | if (NI5010_DEBUG > 3) dump_packet(buf, length); | ||
718 | |||
719 | buf_offs = NI5010_BUFSIZE - length - pad; | ||
720 | |||
721 | spin_lock_irqsave(&lp->lock, flags); | ||
722 | lp->o_pkt_size = length + pad; | ||
723 | |||
724 | outb(0, EDLC_RMASK); /* Mask all receive interrupts */ | ||
725 | outb(0, IE_MMODE); /* Put Xmit buffer on system bus */ | ||
726 | outb(0xff, EDLC_RCLR); /* Clear out pending rcv interrupts */ | ||
727 | |||
728 | outw(buf_offs, IE_GP); /* Point GP at start of packet */ | ||
729 | outsb(IE_XBUF, buf, length); /* Put data in buffer */ | ||
730 | while(pad--) | ||
731 | outb(0, IE_XBUF); | ||
732 | |||
733 | outw(buf_offs, IE_GP); /* Rewrite where packet starts */ | ||
734 | |||
735 | /* should work without that outb() (Crynwr used it) */ | ||
736 | /*outb(MM_MUX, IE_MMODE);*/ /* Xmt buffer to EDLC bus */ | ||
737 | outb(MM_EN_XMT | MM_MUX, IE_MMODE); /* Begin transmission */ | ||
738 | outb(XM_ALL, EDLC_XMASK); /* Cause interrupt after completion or fail */ | ||
739 | |||
740 | spin_unlock_irqrestore(&lp->lock, flags); | ||
741 | |||
742 | netif_wake_queue(dev); | ||
743 | |||
744 | if (NI5010_DEBUG) ni5010_show_registers(dev); | ||
745 | } | ||
746 | |||
747 | static void chipset_init(struct net_device *dev, int startp) | ||
748 | { | ||
749 | /* FIXME: Move some stuff here */ | ||
750 | PRINTK3((KERN_DEBUG "%s: doing NOTHING in chipset_init\n", dev->name)); | ||
751 | } | ||
752 | |||
753 | static void ni5010_show_registers(struct net_device *dev) | ||
754 | { | ||
755 | int ioaddr = dev->base_addr; | ||
756 | |||
757 | PRINTK3((KERN_DEBUG "%s: XSTAT %#2.2x\n", dev->name, inb(EDLC_XSTAT))); | ||
758 | PRINTK3((KERN_DEBUG "%s: XMASK %#2.2x\n", dev->name, inb(EDLC_XMASK))); | ||
759 | PRINTK3((KERN_DEBUG "%s: RSTAT %#2.2x\n", dev->name, inb(EDLC_RSTAT))); | ||
760 | PRINTK3((KERN_DEBUG "%s: RMASK %#2.2x\n", dev->name, inb(EDLC_RMASK))); | ||
761 | PRINTK3((KERN_DEBUG "%s: RMODE %#2.2x\n", dev->name, inb(EDLC_RMODE))); | ||
762 | PRINTK3((KERN_DEBUG "%s: XMODE %#2.2x\n", dev->name, inb(EDLC_XMODE))); | ||
763 | PRINTK3((KERN_DEBUG "%s: ISTAT %#2.2x\n", dev->name, inb(IE_ISTAT))); | ||
764 | } | ||
765 | |||
766 | #ifdef MODULE | ||
767 | static struct net_device *dev_ni5010; | ||
768 | |||
769 | MODULE_PARM(io, "i"); | ||
770 | MODULE_PARM(irq, "i"); | ||
771 | MODULE_PARM_DESC(io, "ni5010 I/O base address"); | ||
772 | MODULE_PARM_DESC(irq, "ni5010 IRQ number"); | ||
773 | |||
774 | int init_module(void) | ||
775 | { | ||
776 | PRINTK2((KERN_DEBUG "%s: entering init_module\n", boardname)); | ||
777 | /* | ||
778 | if(io <= 0 || irq == 0){ | ||
779 | printk(KERN_WARNING "%s: Autoprobing not allowed for modules.\n", boardname); | ||
780 | printk(KERN_WARNING "%s: Set symbols 'io' and 'irq'\n", boardname); | ||
781 | return -EINVAL; | ||
782 | } | ||
783 | */ | ||
784 | if (io <= 0){ | ||
785 | printk(KERN_WARNING "%s: Autoprobing for modules is hazardous, trying anyway..\n", boardname); | ||
786 | } | ||
787 | |||
788 | PRINTK2((KERN_DEBUG "%s: init_module irq=%#2x, io=%#3x\n", boardname, irq, io)); | ||
789 | dev_ni5010 = ni5010_probe(-1); | ||
790 | if (IS_ERR(dev_ni5010)) | ||
791 | return PTR_ERR(dev_ni5010); | ||
792 | return 0; | ||
793 | } | ||
794 | |||
795 | void cleanup_module(void) | ||
796 | { | ||
797 | PRINTK2((KERN_DEBUG "%s: entering cleanup_module\n", boardname)); | ||
798 | unregister_netdev(dev_ni5010); | ||
799 | release_region(dev_ni5010->base_addr, NI5010_IO_EXTENT); | ||
800 | free_netdev(dev_ni5010); | ||
801 | } | ||
802 | #endif /* MODULE */ | ||
803 | MODULE_LICENSE("GPL"); | ||
804 | |||
805 | /* | ||
806 | * Local variables: | ||
807 | * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c ni5010.c" | ||
808 | * version-control: t | ||
809 | * kept-new-versions: 5 | ||
810 | * tab-width: 4 | ||
811 | * End: | ||
812 | */ | ||