1 /* fmv18x.c: A network device driver for the Fujitsu FMV-181/182/183/184.
2
3 Original: at1700.c (1993-94 by Donald Becker).
4 Copyright 1993 United States Government as represented by the
5 Director, National Security Agency.
6 The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
7 Center of Excellence in Space Data and Information Sciences
8 Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
9
10 Modified by Yutaka TAMIYA (tamy@flab.fujitsu.co.jp)
11 Copyright 1994 Fujitsu Laboratories Ltd.
12 Special thanks to:
13 Masayoshi UTAKA (utaka@ace.yk.fujitsu.co.jp)
14 for testing this driver.
15 H. NEGISHI (agy, negishi@sun45.psd.cs.fujitsu.co.jp)
16 for suggestion of some program modification.
17 Masahiro SEKIGUCHI <seki@sysrap.cs.fujitsu.co.jp>
18 for suggestion of some program modification.
19 Kazutoshi MORIOKA (morioka@aurora.oaks.cs.fujitsu.co.jp)
20 for testing this driver.
21
22 This software may be used and distributed according to the terms
23 of the GNU Public License, incorporated herein by reference.
24
25 This is a device driver for the Fujitsu FMV-181/182/183/184, which
26 is a straight-forward Fujitsu MB86965 implementation.
27
28 Sources:
29 at1700.c
30 The Fujitsu MB86965 datasheet.
31 The Fujitsu FMV-181/182 user's guide
32 */
33
34 static const char *version =
35 "fmv18x.c:v2.2.0 09/24/98 Yutaka TAMIYA (tamy@flab.fujitsu.co.jp)\n";
36
37 #include <linux/module.h>
38
39 #include <linux/kernel.h>
40 #include <linux/sched.h>
41 #include <linux/types.h>
42 #include <linux/fcntl.h>
43 #include <linux/interrupt.h>
44 #include <linux/ptrace.h>
45 #include <linux/ioport.h>
46 #include <linux/in.h>
47 #include <linux/malloc.h>
48 #include <linux/string.h>
49 #include <linux/init.h>
50 #include <asm/system.h>
51 #include <asm/bitops.h>
52 #include <asm/io.h>
53 #include <asm/dma.h>
54 #include <linux/errno.h>
55
56 #include <linux/netdevice.h>
57 #include <linux/etherdevice.h>
58 #include <linux/skbuff.h>
59 #include <linux/delay.h>
60
61 static int fmv18x_probe_list[] __initdata = {
62 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x300, 0x340, 0
63 };
64
65 /* use 0 for production, 1 for verification, >2 for debug */
66 #ifndef NET_DEBUG
67 #define NET_DEBUG 1
68 #endif
69 static unsigned int net_debug = NET_DEBUG;
70
71 typedef unsigned char uchar;
72
73 /* Information that need to be kept for each board. */
74 struct net_local {
75 struct net_device_stats stats;
76 long open_time; /* Useless example local info. */
77 uint tx_started:1; /* Number of packet on the Tx queue. */
78 uint tx_queue_ready:1; /* Tx queue is ready to be sent. */
79 uint rx_started:1; /* Packets are Rxing. */
80 uchar tx_queue; /* Number of packet on the Tx queue. */
81 ushort tx_queue_len; /* Current length of the Tx queue. */
82 };
83
84
85 /* Offsets from the base address. */
86 #define STATUS 0
87 #define TX_STATUS 0
88 #define RX_STATUS 1
89 #define TX_INTR 2 /* Bit-mapped interrupt enable registers. */
90 #define RX_INTR 3
91 #define TX_MODE 4
92 #define RX_MODE 5
93 #define CONFIG_0 6 /* Misc. configuration settings. */
94 #define CONFIG_1 7
95 /* Run-time register bank 2 definitions. */
96 #define DATAPORT 8 /* Word-wide DMA or programmed-I/O dataport. */
97 #define TX_START 10
98 #define COL16CNTL 11 /* Controll Reg for 16 collisions */
99 #define MODE13 13
100 /* Fujitsu FMV-18x Card Configuration */
101 #define FJ_STATUS0 0x10
102 #define FJ_STATUS1 0x11
103 #define FJ_CONFIG0 0x12
104 #define FJ_CONFIG1 0x13
105 #define FJ_MACADDR 0x14 /* 0x14 - 0x19 */
106 #define FJ_BUFCNTL 0x1A
107 #define FJ_BUFDATA 0x1C
108 #define FMV18X_IO_EXTENT 32
109
110 /* Index to functions, as function prototypes. */
111
112 extern int fmv18x_probe(struct net_device *dev);
113
114 static int fmv18x_probe1(struct net_device *dev, short ioaddr);
115 static int net_open(struct net_device *dev);
116 static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
117 static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
118 static void net_rx(struct net_device *dev);
119 static void net_timeout(struct net_device *dev);
120 static int net_close(struct net_device *dev);
121 static struct net_device_stats *net_get_stats(struct net_device *dev);
122 static void set_multicast_list(struct net_device *dev);
123
124
125 /* Check for a network adaptor of this type, and return '' iff one exists.
126 If dev->base_addr == 0, probe all likely locations.
127 If dev->base_addr == 1, always return failure.
128 If dev->base_addr == 2, allocate space for the device and return success
129 (detachable devices only).
130 */
131
132 int __init fmv18x_probe(struct net_device *dev)
133 {
134 int i;
135 int base_addr = dev->base_addr;
136
137 SET_MODULE_OWNER(dev);
138
139 if (base_addr > 0x1ff) /* Check a single specified location. */
140 return fmv18x_probe1(dev, base_addr);
141 else if (base_addr != 0) /* Don't probe at all. */
142 return -ENXIO;
143
144 for (i = 0; fmv18x_probe_list[i]; i++)
145 if (fmv18x_probe1(dev, fmv18x_probe_list[i]) == 0)
146 return 0;
147
148 return -ENODEV;
149 }
150
151 /* The Fujitsu datasheet suggests that the NIC be probed for by checking its
152 "signature", the default bit pattern after a reset. This *doesn't* work --
153 there is no way to reset the bus interface without a complete power-cycle!
154
155 It turns out that ATI came to the same conclusion I did: the only thing
156 that can be done is checking a few bits and then diving right into MAC
157 address check. */
158
159 static int __init fmv18x_probe1(struct net_device *dev, short ioaddr)
160 {
161 char irqmap[4] = {3, 7, 10, 15};
162 char irqmap_pnp[8] = {3, 4, 5, 7, 9, 10, 11, 15};
163 unsigned int i, irq, retval;
164
165 /* Resetting the chip doesn't reset the ISA interface, so don't bother.
166 That means we have to be careful with the register values we probe for.
167 */
168
169 if (!request_region(ioaddr, FMV18X_IO_EXTENT, dev->name))
170 return -EBUSY;
171
172 /* Check I/O address configuration and Fujitsu vendor code */
173 if (inb(ioaddr+FJ_MACADDR ) != 0x00
174 || inb(ioaddr+FJ_MACADDR+1) != 0x00
175 || inb(ioaddr+FJ_MACADDR+2) != 0x0e) {
176 retval = -ENODEV;
177 goto out;
178 }
179
180 /* Check PnP mode for FMV-183/184/183A/184A. */
181 /* This PnP routine is very poor. IO and IRQ should be known. */
182 if (inb(ioaddr + FJ_STATUS1) & 0x20) {
183 irq = dev->irq;
184 for (i = 0; i < 8; i++) {
185 if (irq == irqmap_pnp[i])
186 break;
187 }
188 if (i == 8) {
189 retval = -ENODEV;
190 goto out;
191 }
192 } else {
193 if (fmv18x_probe_list[inb(ioaddr + FJ_CONFIG0) & 0x07] != ioaddr)
194 return -ENODEV;
195 irq = irqmap[(inb(ioaddr + FJ_CONFIG0)>>6) & 0x03];
196 }
197
198 /* Snarf the interrupt vector now. */
199 retval = request_irq(irq, &net_interrupt, 0, dev->name, dev);
200 if (retval) {
201 printk ("FMV-18x found at %#3x, but it's unusable due to a conflict on"
202 "IRQ %d.\n", ioaddr, irq);
203 goto out;
204 }
205
206 printk("%s: FMV-18x found at %#3x, IRQ %d, address ", dev->name,
207 ioaddr, irq);
208
209 dev->base_addr = ioaddr;
210 dev->irq = irq;
211
212 for(i = 0; i < 6; i++) {
213 unsigned char val = inb(ioaddr + FJ_MACADDR + i);
214 printk("%02x", val);
215 dev->dev_addr[i] = val;
216 }
217
218 /* "FJ_STATUS0" 12 bit 0x0400 means use regular 100 ohm 10baseT signals,
219 rather than 150 ohm shielded twisted pair compensation.
220 0x0000 == auto-sense the interface
221 0x0800 == use TP interface
222 0x1800 == use coax interface
223 */
224 {
225 const char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2/5"};
226 ushort setup_value = inb(ioaddr + FJ_STATUS0);
227
228 switch( setup_value & 0x07 ){
229 case 0x01 /* 10base5 */:
230 case 0x02 /* 10base2 */: dev->if_port = 0x18; break;
231 case 0x04 /* 10baseT */: dev->if_port = 0x08; break;
232 default /* auto-sense*/: dev->if_port = 0x00; break;
233 }
234 printk(" %s interface.\n", porttype[(dev->if_port>>3) & 3]);
235 }
236
237 /* Initialize LAN Controller and LAN Card */
238 outb(0xda, ioaddr + CONFIG_0); /* Initialize LAN Controller */
239 outb(0x00, ioaddr + CONFIG_1); /* Stand by mode */
240 outb(0x00, ioaddr + FJ_CONFIG1); /* Disable IRQ of LAN Card */
241 outb(0x00, ioaddr + FJ_BUFCNTL); /* Reset ? I'm not sure (TAMIYA) */
242
243 /* wait for a while */
244 udelay(200);
245
246 /* Set the station address in bank zero. */
247 outb(0x00, ioaddr + CONFIG_1);
248 for (i = 0; i < 6; i++)
249 outb(dev->dev_addr[i], ioaddr + 8 + i);
250
251 /* Switch to bank 1 and set the multicast table to accept none. */
252 outb(0x04, ioaddr + CONFIG_1);
253 for (i = 0; i < 8; i++)
254 outb(0x00, ioaddr + 8 + i);
255
256 /* Switch to bank 2 and lock our I/O address. */
257 outb(0x08, ioaddr + CONFIG_1);
258 outb(dev->if_port, ioaddr + MODE13);
259 outb(0x00, ioaddr + COL16CNTL);
260
261 if (net_debug)
262 printk(version);
263
264 /* Initialize the device structure. */
265 dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
266 if (!dev->priv) {
267 retval = -ENOMEM;
268 goto out_irq;
269 }
270 memset(dev->priv, 0, sizeof(struct net_local));
271
272 dev->open = net_open;
273 dev->stop = net_close;
274 dev->hard_start_xmit = net_send_packet;
275 dev->tx_timeout = net_timeout;
276 dev->watchdog_timeo = HZ/10;
277 dev->get_stats = net_get_stats;
278 dev->set_multicast_list = set_multicast_list;
279
280 /* Fill in the fields of 'dev' with ethernet-generic values. */
281
282 ether_setup(dev);
283 return 0;
284
285 out_irq:
286 free_irq(irq, dev);
287 out:
288 release_region(ioaddr, FMV18X_IO_EXTENT);
289 return retval;
290 }
291
292
293 static int net_open(struct net_device *dev)
294 {
295 struct net_local *lp = (struct net_local *)dev->priv;
296 int ioaddr = dev->base_addr;
297
298 /* Set the configuration register 0 to 32K 100ns. byte-wide memory,
299 16 bit bus access, and two 4K Tx, enable the Rx and Tx. */
300 outb(0x5a, ioaddr + CONFIG_0);
301
302 /* Powerup and switch to register bank 2 for the run-time registers. */
303 outb(0xe8, ioaddr + CONFIG_1);
304
305 lp->tx_started = 0;
306 lp->tx_queue_ready = 1;
307 lp->rx_started = 0;
308 lp->tx_queue = 0;
309 lp->tx_queue_len = 0;
310
311 /* Clear Tx and Rx Status */
312 outb(0xff, ioaddr + TX_STATUS);
313 outb(0xff, ioaddr + RX_STATUS);
314 lp->open_time = jiffies;
315
316 netif_start_queue(dev);
317
318 /* Enable the IRQ of the LAN Card */
319 outb(0x80, ioaddr + FJ_CONFIG1);
320
321 /* Enable both Tx and Rx interrupts */
322 outw(0x8182, ioaddr+TX_INTR);
323
324 return 0;
325 }
326
327 static void net_timeout(struct net_device *dev)
328 {
329 struct net_local *lp = (struct net_local *)dev->priv;
330 int ioaddr = dev->base_addr;
331 unsigned long flags;
332
333
334 printk(KERN_WARNING "%s: transmit timed out with status %04x, %s?\n", dev->name,
335 htons(inw(ioaddr + TX_STATUS)),
336 inb(ioaddr + TX_STATUS) & 0x80
337 ? "IRQ conflict" : "network cable problem");
338 printk(KERN_WARNING "%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",
339 dev->name, htons(inw(ioaddr + 0)),
340 htons(inw(ioaddr + 2)), htons(inw(ioaddr + 4)),
341 htons(inw(ioaddr + 6)), htons(inw(ioaddr + 8)),
342 htons(inw(ioaddr +10)), htons(inw(ioaddr +12)),
343 htons(inw(ioaddr +14)));
344 printk(KERN_WARNING "eth card: %04x %04x\n",
345 htons(inw(ioaddr+FJ_STATUS0)),
346 htons(inw(ioaddr+FJ_CONFIG0)));
347 lp->stats.tx_errors++;
348 /* ToDo: We should try to restart the adaptor... */
349 save_flags(flags);
350 cli();
351
352 /* Initialize LAN Controller and LAN Card */
353 outb(0xda, ioaddr + CONFIG_0); /* Initialize LAN Controller */
354 outb(0x00, ioaddr + CONFIG_1); /* Stand by mode */
355 outb(0x00, ioaddr + FJ_CONFIG1); /* Disable IRQ of LAN Card */
356 outb(0x00, ioaddr + FJ_BUFCNTL); /* Reset ? I'm not sure */
357 net_open(dev);
358 restore_flags(flags);
359 }
360
361 static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
362 {
363 struct net_local *lp = (struct net_local *)dev->priv;
364 int ioaddr = dev->base_addr;
365 short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
366 unsigned char *buf = skb->data;
367
368 /* Block a transmit from overlapping. */
369
370 netif_stop_queue(dev);
371
372 if (length > ETH_FRAME_LEN) {
373 if (net_debug)
374 printk("%s: Attempting to send a large packet (%d bytes).\n",
375 dev->name, length);
376 return 1;
377 }
378 if (net_debug > 4)
379 printk("%s: Transmitting a packet of length %lu.\n", dev->name,
380 (unsigned long)skb->len);
381 /* We may not start transmitting unless we finish transferring
382 a packet into the Tx queue. During executing the following
383 codes we possibly catch a Tx interrupt. Thus we flag off
384 tx_queue_ready, so that we prevent the interrupt routine
385 (net_interrupt) to start transmitting. */
386 lp->tx_queue_ready = 0;
387 {
388 outw(length, ioaddr + DATAPORT);
389 outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
390 lp->tx_queue++;
391 lp->tx_queue_len += length + 2;
392 }
393 lp->tx_queue_ready = 1;
394 if (lp->tx_started == 0) {
395 /* If the Tx is idle, always trigger a transmit. */
396 outb(0x80 | lp->tx_queue, ioaddr + TX_START);
397 lp->tx_queue = 0;
398 lp->tx_queue_len = 0;
399 dev->trans_start = jiffies;
400 lp->tx_started = 1;
401 netif_wake_queue(dev);
402 } else if (lp->tx_queue_len < 4096 - 1502)
403 /* Yes, there is room for one more packet. */
404 netif_wake_queue(dev);
405
406 dev_kfree_skb(skb);
407 return 0;
408 }
409
410 /* The typical workload of the driver:
411 Handle the network interface interrupts. */
412 static void
413 net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
414 {
415 struct net_device *dev = dev_id;
416 struct net_local *lp;
417 int ioaddr, status;
418
419 ioaddr = dev->base_addr;
420 lp = (struct net_local *)dev->priv;
421 status = inw(ioaddr + TX_STATUS);
422 outw(status, ioaddr + TX_STATUS);
423
424 if (net_debug > 4)
425 printk("%s: Interrupt with status %04x.\n", dev->name, status);
426 if (lp->rx_started == 0 &&
427 (status & 0xff00 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) {
428 /* Got a packet(s).
429 We cannot execute net_rx more than once at the same time for
430 the same device. During executing net_rx, we possibly catch a
431 Tx interrupt. Thus we flag on rx_started, so that we prevent
432 the interrupt routine (net_interrupt) to dive into net_rx
433 again. */
434 lp->rx_started = 1;
435 outb(0x00, ioaddr + RX_INTR); /* Disable RX intr. */
436 net_rx(dev);
437 outb(0x81, ioaddr + RX_INTR); /* Enable RX intr. */
438 lp->rx_started = 0;
439 }
440 if (status & 0x00ff) {
441 if (status & 0x02) {
442 /* More than 16 collisions occurred */
443 if (net_debug > 4)
444 printk("%s: 16 Collision occur during Txing.\n", dev->name);
445 /* Cancel sending a packet. */
446 outb(0x03, ioaddr + COL16CNTL);
447 lp->stats.collisions++;
448 }
449 if (status & 0x82) {
450 lp->stats.tx_packets++;
451 if (lp->tx_queue && lp->tx_queue_ready) {
452 outb(0x80 | lp->tx_queue, ioaddr + TX_START);
453 lp->tx_queue = 0;
454 lp->tx_queue_len = 0;
455 dev->trans_start = jiffies;
456 netif_wake_queue(dev); /* Inform upper layers. */
457 } else {
458 lp->tx_started = 0;
459 netif_wake_queue(dev); /* Inform upper layers. */
460 }
461 }
462 }
463 return;
464 }
465
466 /* We have a good packet(s), get it/them out of the buffers. */
467 static void net_rx(struct net_device *dev)
468 {
469 struct net_local *lp = (struct net_local *)dev->priv;
470 int ioaddr = dev->base_addr;
471 int boguscount = 5;
472
473 while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
474 /* Clear PKT_RDY bit: by agy 19940922 */
475 /* outb(0x80, ioaddr + RX_STATUS); */
476 ushort status = inw(ioaddr + DATAPORT);
477
478 if (net_debug > 4)
479 printk("%s: Rxing packet mode %02x status %04x.\n",
480 dev->name, inb(ioaddr + RX_MODE), status);
481 #ifndef final_version
482 if (status == 0) {
483 outb(0x05, ioaddr + 14);
484 break;
485 }
486 #endif
487
488 if ((status & 0xF0) != 0x20) { /* There was an error. */
489 lp->stats.rx_errors++;
490 if (status & 0x08) lp->stats.rx_length_errors++;
491 if (status & 0x04) lp->stats.rx_frame_errors++;
492 if (status & 0x02) lp->stats.rx_crc_errors++;
493 if (status & 0x01) lp->stats.rx_over_errors++;
494 } else {
495 ushort pkt_len = inw(ioaddr + DATAPORT);
496 /* Malloc up new buffer. */
497 struct sk_buff *skb;
498
499 if (pkt_len > 1550) {
500 printk("%s: The FMV-18x claimed a very large packet, size %d.\n",
501 dev->name, pkt_len);
502 outb(0x05, ioaddr + 14);
503 lp->stats.rx_errors++;
504 break;
505 }
506 skb = dev_alloc_skb(pkt_len+3);
507 if (skb == NULL) {
508 printk("%s: Memory squeeze, dropping packet (len %d).\n",
509 dev->name, pkt_len);
510 outb(0x05, ioaddr + 14);
511 lp->stats.rx_dropped++;
512 break;
513 }
514 skb->dev = dev;
515 skb_reserve(skb,2);
516
517 insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
518
519 if (net_debug > 5) {
520 int i;
521 printk("%s: Rxed packet of length %d: ", dev->name, pkt_len);
522 for (i = 0; i < 14; i++)
523 printk(" %02x", skb->data[i]);
524 printk(".\n");
525 }
526
527 skb->protocol=eth_type_trans(skb, dev);
528 netif_rx(skb);
529 lp->stats.rx_packets++;
530 }
531 if (--boguscount <= 0)
532 break;
533 }
534
535 /* If any worth-while packets have been received, dev_rint()
536 has done a mark_bh(NET_BH) for us and will work on them
537 when we get to the bottom-half routine. */
538 {
539 int i;
540 for (i = 0; i < 20; i++) {
541 if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)
542 break;
543 (void)inw(ioaddr + DATAPORT); /* dummy status read */
544 outb(0x05, ioaddr + 14);
545 }
546
547 if (net_debug > 5 && i > 0)
548 printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",
549 dev->name, inb(ioaddr + RX_MODE), i);
550 }
551
552 return;
553 }
554
555 /* The inverse routine to net_open(). */
556 static int net_close(struct net_device *dev)
557 {
558 int ioaddr = dev->base_addr;
559
560 ((struct net_local *)dev->priv)->open_time = 0;
561
562 netif_stop_queue(dev);
563
564 /* Set configuration register 0 to disable Tx and Rx. */
565 outb(0xda, ioaddr + CONFIG_0);
566
567 /* Update the statistics -- ToDo. */
568
569 /* Power-down the chip. Green, green, green! */
570 outb(0x00, ioaddr + CONFIG_1);
571
572 /* Set the ethernet adaptor disable IRQ */
573 outb(0x00, ioaddr + FJ_CONFIG1);
574
575 return 0;
576 }
577
578 /* Get the current statistics. This may be called with the card open or
579 closed. */
580 static struct net_device_stats *net_get_stats(struct net_device *dev)
581 {
582 struct net_local *lp = (struct net_local *)dev->priv;
583 return &lp->stats;
584 }
585
586 /* Set or clear the multicast filter for this adaptor.
587 num_addrs == -1 Promiscuous mode, receive all packets
588 num_addrs == 0 Normal mode, clear multicast list
589 num_addrs > 0 Multicast mode, receive normal and MC packets, and do
590 best-effort filtering.
591 */
592
593 static void set_multicast_list(struct net_device *dev)
594 {
595 short ioaddr = dev->base_addr;
596 if (dev->mc_count || dev->flags&(IFF_PROMISC|IFF_ALLMULTI))
597 {
598 /*
599 * We must make the kernel realise we had to move
600 * into promisc mode or we start all out war on
601 * the cable. - AC
602 */
603 dev->flags|=IFF_PROMISC;
604
605 outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */
606 }
607 else
608 outb(2, ioaddr + RX_MODE); /* Disable promiscuous, use normal mode */
609 }
610
611 #ifdef MODULE
612 static struct net_device dev_fmv18x;
613 static int io = 0x220;
614 static int irq;
615
616 MODULE_PARM(io, "i");
617 MODULE_PARM(irq, "i");
618 MODULE_PARM(net_debug, "i");
619
620 int init_module(void)
621 {
622 if (io == 0)
623 printk("fmv18x: You should not use auto-probing with insmod!\n");
624 dev_fmv18x.base_addr = io;
625 dev_fmv18x.irq = irq;
626 dev_fmv18x.init = fmv18x_probe;
627 if (register_netdev(&dev_fmv18x) != 0) {
628 printk("fmv18x: register_netdev() returned non-zero.\n");
629 return -EIO;
630 }
631 return 0;
632 }
633
634 void
635 cleanup_module(void)
636 {
637 unregister_netdev(&dev_fmv18x);
638 kfree(dev_fmv18x.priv);
639 dev_fmv18x.priv = NULL;
640
641 /* If we don't do this, we can't re-insmod it later. */
642 free_irq(dev_fmv18x.irq, &dev_fmv18x);
643 release_region(dev_fmv18x.base_addr, FMV18X_IO_EXTENT);
644 }
645 #endif /* MODULE */
646
647 /*
648 * Local variables:
649 * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c fmv18x.c"
650 * version-control: t
651 * kept-new-versions: 5
652 * tab-width: 4
653 * c-indent-level: 4
654 * End:
655 */
656
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.