Import 2.3.25pre1
[davej-history.git] / drivers / net / hamradio / yam.c
blobef3e7d1620cdf1798fcc79753046037fa7910167
1 /*****************************************************************************/
3 /*
4 * yam.c -- YAM radio modem driver.
6 * Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr)
7 * Adapted from baycom.c driver written by Thomas Sailer (sailer@ife.ee.ethz.ch)
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 * Please note that the GPL allows you to use the driver, NOT the radio.
24 * In order to use the radio, you need a license from the communications
25 * authority of your country.
28 * History:
29 * 0.0 F1OAT 06.06.98 Begin of work with baycom.c source code V 0.3
30 * 0.1 F1OAT 07.06.98 Add timer polling routine for channel arbitration
31 * 0.2 F6FBB 08.06.98 Added delay after FPGA programming
32 * 0.3 F6FBB 29.07.98 Delayed PTT implementation for dupmode=2
33 * 0.4 F6FBB 30.07.98 Added TxTail, Slottime and Persistance
34 * 0.5 F6FBB 01.08.98 Shared IRQs, /proc/net and network statistics
35 * 0.6 F6FBB 25.08.98 Added 1200Bds format
36 * 0.7 F6FBB 12.09.98 Added to the kernel configuration
37 * 0.8 F6FBB 14.10.98 Fixed slottime/persistance timing bug
40 /*****************************************************************************/
42 #include <linux/config.h>
43 #include <linux/module.h>
44 #include <linux/types.h>
45 #include <linux/net.h>
46 #include <linux/in.h>
47 #include <linux/if.h>
48 #include <linux/malloc.h>
49 #include <linux/errno.h>
50 #include <asm/bitops.h>
51 #include <asm/io.h>
52 #include <asm/system.h>
53 #include <linux/interrupt.h>
54 #include <linux/ioport.h>
56 #include <linux/netdevice.h>
57 #include <linux/if_arp.h>
58 #include <linux/etherdevice.h>
59 #include <linux/skbuff.h>
60 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
61 /* prototypes for ax25_encapsulate and ax25_rebuild_header */
62 #include <net/ax25.h>
63 #endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
65 /* make genksyms happy */
66 #include <linux/ip.h>
67 #include <linux/udp.h>
68 #include <linux/tcp.h>
70 #include <linux/kernel.h>
71 #include <linux/proc_fs.h>
73 #include <linux/yam.h>
74 #include "yam9600.h"
75 #include "yam1200.h"
77 /* --------------------------------------------------------------------- */
80 * currently this module is supposed to support both module styles, i.e.
81 * the old one present up to about 2.1.9, and the new one functioning
82 * starting with 2.1.21. The reason is I have a kit allowing to compile
83 * this module also under 2.0.x which was requested by several people.
84 * This will go in 2.2
86 #include <linux/version.h>
88 #if LINUX_VERSION_CODE >= 0x20100
89 #include <asm/uaccess.h>
90 #else
91 #include <asm/segment.h>
92 #include <linux/mm.h>
94 #undef put_user
95 #undef get_user
97 #define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
98 #define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
100 extern inline int copy_from_user(void *to, const void *from, unsigned long n)
102 int i = verify_area(VERIFY_READ, from, n);
103 if (i)
104 return i;
105 memcpy_fromfs(to, from, n);
106 return 0;
109 extern inline int copy_to_user(void *to, const void *from, unsigned long n)
111 int i = verify_area(VERIFY_WRITE, to, n);
112 if (i)
113 return i;
114 memcpy_tofs(to, from, n);
115 return 0;
117 #endif
119 #if LINUX_VERSION_CODE < 0x20115
120 extern __inline__ void dev_init_buffers(struct net_device *dev)
122 int i;
123 for (i = 0; i < DEV_NUMBUFFS; i++) {
124 skb_queue_head_init(&dev->buffs[i]);
127 #endif
129 #if LINUX_VERSION_CODE >= 0x20123
130 #include <linux/init.h>
131 #else
132 #define __init
133 #define __initdata
134 #endif
136 /* --------------------------------------------------------------------- */
138 static const char yam_drvname[] = "yam";
139 static const char yam_drvinfo[] = KERN_INFO "YAM driver version 0.8 by F1OAT/F6FBB\n";
141 /* --------------------------------------------------------------------- */
143 #define YAM_9600 1
144 #define YAM_1200 2
146 #define NR_PORTS 4
147 #define YAM_MAGIC 0xF10A7654
149 /* Transmitter states */
151 #define TX_OFF 0
152 #define TX_HEAD 1
153 #define TX_DATA 2
154 #define TX_CRC1 3
155 #define TX_CRC2 4
156 #define TX_TAIL 5
158 #define YAM_MAX_FRAME 1024
160 #define DEFAULT_BITRATE 9600 /* bps */
161 #define DEFAULT_HOLDD 10 /* sec */
162 #define DEFAULT_TXD 300 /* ms */
163 #define DEFAULT_TXTAIL 10 /* ms */
164 #define DEFAULT_SLOT 100 /* ms */
165 #define DEFAULT_PERS 64 /* 0->255 */
167 struct yam_port {
168 int magic;
169 int bitrate;
170 int baudrate;
171 int iobase;
172 int irq;
173 int dupmode;
174 char name[16];
176 struct net_device dev;
178 /* Stats section */
180 #if LINUX_VERSION_CODE < 0x20119
181 struct enet_statistics stats;
182 #else
183 struct net_device_stats stats;
184 #endif
185 int nb_rxint;
186 int nb_mdint;
188 /* Parameters section */
190 int txd; /* tx delay */
191 int holdd; /* duplex ptt delay */
192 int txtail; /* txtail delay */
193 int slot; /* slottime */
194 int pers; /* persistence */
196 /* Tx section */
198 int tx_state;
199 int tx_count;
200 int slotcnt;
201 unsigned char tx_buf[YAM_MAX_FRAME];
202 int tx_len;
203 int tx_crcl, tx_crch;
204 struct sk_buff_head send_queue; /* Packets awaiting transmission */
206 /* Rx section */
208 int dcd;
209 unsigned char rx_buf[YAM_MAX_FRAME];
210 int rx_len;
211 int rx_crcl, rx_crch;
214 struct yam_mcs {
215 unsigned char bits[YAM_FPGA_SIZE];
216 int bitrate;
217 struct yam_mcs *next;
220 static struct yam_port yam_ports[NR_PORTS];
222 static struct yam_mcs *yam_data = NULL;
224 static unsigned irqs[16];
226 static char ax25_bcast[7] =
227 {'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
228 static char ax25_test[7] =
229 {'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
231 static struct timer_list yam_timer;
233 /* --------------------------------------------------------------------- */
235 #define RBR(iobase) (iobase+0)
236 #define THR(iobase) (iobase+0)
237 #define IER(iobase) (iobase+1)
238 #define IIR(iobase) (iobase+2)
239 #define FCR(iobase) (iobase+2)
240 #define LCR(iobase) (iobase+3)
241 #define MCR(iobase) (iobase+4)
242 #define LSR(iobase) (iobase+5)
243 #define MSR(iobase) (iobase+6)
244 #define SCR(iobase) (iobase+7)
245 #define DLL(iobase) (iobase+0)
246 #define DLM(iobase) (iobase+1)
248 #define YAM_EXTENT 8
250 /* Interrupt Identification Register Bit Masks */
251 #define IIR_NOPEND 1
252 #define IIR_MSR 0
253 #define IIR_TX 2
254 #define IIR_RX 4
255 #define IIR_LSR 6
256 #define IIR_TIMEOUT 12 /* Fifo mode only */
258 #define IIR_MASK 0x0F
260 /* Interrupt Enable Register Bit Masks */
261 #define IER_RX 1 /* enable rx interrupt */
262 #define IER_TX 2 /* enable tx interrupt */
263 #define IER_LSR 4 /* enable line status interrupts */
264 #define IER_MSR 8 /* enable modem status interrupts */
266 /* Modem Control Register Bit Masks */
267 #define MCR_DTR 0x01 /* DTR output */
268 #define MCR_RTS 0x02 /* RTS output */
269 #define MCR_OUT1 0x04 /* OUT1 output (not accessible in RS232) */
270 #define MCR_OUT2 0x08 /* Master Interrupt enable (must be set on PCs) */
271 #define MCR_LOOP 0x10 /* Loopback enable */
273 /* Modem Status Register Bit Masks */
274 #define MSR_DCTS 0x01 /* Delta CTS input */
275 #define MSR_DDSR 0x02 /* Delta DSR */
276 #define MSR_DRIN 0x04 /* Delta RI */
277 #define MSR_DDCD 0x08 /* Delta DCD */
278 #define MSR_CTS 0x10 /* CTS input */
279 #define MSR_DSR 0x20 /* DSR input */
280 #define MSR_RING 0x40 /* RI input */
281 #define MSR_DCD 0x80 /* DCD input */
283 /* line status register bit mask */
284 #define LSR_RXC 0x01
285 #define LSR_OE 0x02
286 #define LSR_PE 0x04
287 #define LSR_FE 0x08
288 #define LSR_BREAK 0x10
289 #define LSR_THRE 0x20
290 #define LSR_TSRE 0x40
292 /* Line Control Register Bit Masks */
293 #define LCR_DLAB 0x80
294 #define LCR_BREAK 0x40
295 #define LCR_PZERO 0x28
296 #define LCR_PEVEN 0x18
297 #define LCR_PODD 0x08
298 #define LCR_STOP1 0x00
299 #define LCR_STOP2 0x04
300 #define LCR_BIT5 0x00
301 #define LCR_BIT6 0x02
302 #define LCR_BIT7 0x01
303 #define LCR_BIT8 0x03
305 /* YAM Modem <-> UART Port mapping */
307 #define TX_RDY MSR_DCTS /* transmitter ready to send */
308 #define RX_DCD MSR_DCD /* carrier detect */
309 #define RX_FLAG MSR_RING /* hdlc flag received */
310 #define FPGA_DONE MSR_DSR /* FPGA is configured */
311 #define PTT_ON (MCR_RTS|MCR_OUT2) /* activate PTT */
312 #define PTT_OFF (MCR_DTR|MCR_OUT2) /* release PTT */
314 #define ENABLE_RXINT IER_RX /* enable uart rx interrupt during rx */
315 #define ENABLE_TXINT IER_MSR /* enable uart ms interrupt during tx */
316 #define ENABLE_RTXINT (IER_RX|IER_MSR) /* full duplex operations */
318 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
319 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
321 /*************************************************************************
322 * CRC Tables
323 ************************************************************************/
325 static const unsigned char chktabl[256] =
326 {0x00, 0x89, 0x12, 0x9b, 0x24, 0xad, 0x36, 0xbf, 0x48, 0xc1, 0x5a, 0xd3, 0x6c, 0xe5, 0x7e,
327 0xf7, 0x81, 0x08, 0x93, 0x1a, 0xa5, 0x2c, 0xb7, 0x3e, 0xc9, 0x40, 0xdb, 0x52, 0xed, 0x64,
328 0xff, 0x76, 0x02, 0x8b, 0x10, 0x99, 0x26, 0xaf, 0x34, 0xbd, 0x4a, 0xc3, 0x58, 0xd1, 0x6e,
329 0xe7, 0x7c, 0xf5, 0x83, 0x0a, 0x91, 0x18, 0xa7, 0x2e, 0xb5, 0x3c, 0xcb, 0x42, 0xd9, 0x50,
330 0xef, 0x66, 0xfd, 0x74, 0x04, 0x8d, 0x16, 0x9f, 0x20, 0xa9, 0x32, 0xbb, 0x4c, 0xc5, 0x5e,
331 0xd7, 0x68, 0xe1, 0x7a, 0xf3, 0x85, 0x0c, 0x97, 0x1e, 0xa1, 0x28, 0xb3, 0x3a, 0xcd, 0x44,
332 0xdf, 0x56, 0xe9, 0x60, 0xfb, 0x72, 0x06, 0x8f, 0x14, 0x9d, 0x22, 0xab, 0x30, 0xb9, 0x4e,
333 0xc7, 0x5c, 0xd5, 0x6a, 0xe3, 0x78, 0xf1, 0x87, 0x0e, 0x95, 0x1c, 0xa3, 0x2a, 0xb1, 0x38,
334 0xcf, 0x46, 0xdd, 0x54, 0xeb, 0x62, 0xf9, 0x70, 0x08, 0x81, 0x1a, 0x93, 0x2c, 0xa5, 0x3e,
335 0xb7, 0x40, 0xc9, 0x52, 0xdb, 0x64, 0xed, 0x76, 0xff, 0x89, 0x00, 0x9b, 0x12, 0xad, 0x24,
336 0xbf, 0x36, 0xc1, 0x48, 0xd3, 0x5a, 0xe5, 0x6c, 0xf7, 0x7e, 0x0a, 0x83, 0x18, 0x91, 0x2e,
337 0xa7, 0x3c, 0xb5, 0x42, 0xcb, 0x50, 0xd9, 0x66, 0xef, 0x74, 0xfd, 0x8b, 0x02, 0x99, 0x10,
338 0xaf, 0x26, 0xbd, 0x34, 0xc3, 0x4a, 0xd1, 0x58, 0xe7, 0x6e, 0xf5, 0x7c, 0x0c, 0x85, 0x1e,
339 0x97, 0x28, 0xa1, 0x3a, 0xb3, 0x44, 0xcd, 0x56, 0xdf, 0x60, 0xe9, 0x72, 0xfb, 0x8d, 0x04,
340 0x9f, 0x16, 0xa9, 0x20, 0xbb, 0x32, 0xc5, 0x4c, 0xd7, 0x5e, 0xe1, 0x68, 0xf3, 0x7a, 0x0e,
341 0x87, 0x1c, 0x95, 0x2a, 0xa3, 0x38, 0xb1, 0x46, 0xcf, 0x54, 0xdd, 0x62, 0xeb, 0x70, 0xf9,
342 0x8f, 0x06, 0x9d, 0x14, 0xab, 0x22, 0xb9, 0x30, 0xc7, 0x4e, 0xd5, 0x5c, 0xe3, 0x6a, 0xf1,
343 0x78};
344 static const unsigned char chktabh[256] =
345 {0x00, 0x11, 0x23, 0x32, 0x46, 0x57, 0x65, 0x74, 0x8c, 0x9d, 0xaf, 0xbe, 0xca, 0xdb, 0xe9,
346 0xf8, 0x10, 0x01, 0x33, 0x22, 0x56, 0x47, 0x75, 0x64, 0x9c, 0x8d, 0xbf, 0xae, 0xda, 0xcb,
347 0xf9, 0xe8, 0x21, 0x30, 0x02, 0x13, 0x67, 0x76, 0x44, 0x55, 0xad, 0xbc, 0x8e, 0x9f, 0xeb,
348 0xfa, 0xc8, 0xd9, 0x31, 0x20, 0x12, 0x03, 0x77, 0x66, 0x54, 0x45, 0xbd, 0xac, 0x9e, 0x8f,
349 0xfb, 0xea, 0xd8, 0xc9, 0x42, 0x53, 0x61, 0x70, 0x04, 0x15, 0x27, 0x36, 0xce, 0xdf, 0xed,
350 0xfc, 0x88, 0x99, 0xab, 0xba, 0x52, 0x43, 0x71, 0x60, 0x14, 0x05, 0x37, 0x26, 0xde, 0xcf,
351 0xfd, 0xec, 0x98, 0x89, 0xbb, 0xaa, 0x63, 0x72, 0x40, 0x51, 0x25, 0x34, 0x06, 0x17, 0xef,
352 0xfe, 0xcc, 0xdd, 0xa9, 0xb8, 0x8a, 0x9b, 0x73, 0x62, 0x50, 0x41, 0x35, 0x24, 0x16, 0x07,
353 0xff, 0xee, 0xdc, 0xcd, 0xb9, 0xa8, 0x9a, 0x8b, 0x84, 0x95, 0xa7, 0xb6, 0xc2, 0xd3, 0xe1,
354 0xf0, 0x08, 0x19, 0x2b, 0x3a, 0x4e, 0x5f, 0x6d, 0x7c, 0x94, 0x85, 0xb7, 0xa6, 0xd2, 0xc3,
355 0xf1, 0xe0, 0x18, 0x09, 0x3b, 0x2a, 0x5e, 0x4f, 0x7d, 0x6c, 0xa5, 0xb4, 0x86, 0x97, 0xe3,
356 0xf2, 0xc0, 0xd1, 0x29, 0x38, 0x0a, 0x1b, 0x6f, 0x7e, 0x4c, 0x5d, 0xb5, 0xa4, 0x96, 0x87,
357 0xf3, 0xe2, 0xd0, 0xc1, 0x39, 0x28, 0x1a, 0x0b, 0x7f, 0x6e, 0x5c, 0x4d, 0xc6, 0xd7, 0xe5,
358 0xf4, 0x80, 0x91, 0xa3, 0xb2, 0x4a, 0x5b, 0x69, 0x78, 0x0c, 0x1d, 0x2f, 0x3e, 0xd6, 0xc7,
359 0xf5, 0xe4, 0x90, 0x81, 0xb3, 0xa2, 0x5a, 0x4b, 0x79, 0x68, 0x1c, 0x0d, 0x3f, 0x2e, 0xe7,
360 0xf6, 0xc4, 0xd5, 0xa1, 0xb0, 0x82, 0x93, 0x6b, 0x7a, 0x48, 0x59, 0x2d, 0x3c, 0x0e, 0x1f,
361 0xf7, 0xe6, 0xd4, 0xc5, 0xb1, 0xa0, 0x92, 0x83, 0x7b, 0x6a, 0x58, 0x49, 0x3d, 0x2c, 0x1e,
362 0x0f};
364 /*************************************************************************
365 * FPGA functions
366 ************************************************************************/
368 static void delay(int ms)
370 unsigned long timeout = jiffies + ((ms * HZ) / 1000);
371 while (jiffies < timeout);
375 * reset FPGA
378 static void fpga_reset(int iobase)
380 outb(0, IER(iobase));
381 outb(LCR_DLAB | LCR_BIT5, LCR(iobase));
382 outb(1, DLL(iobase));
383 outb(0, DLM(iobase));
385 outb(LCR_BIT5, LCR(iobase));
386 inb(LSR(iobase));
387 inb(MSR(iobase));
388 /* turn off FPGA supply voltage */
389 outb(MCR_OUT1 | MCR_OUT2, MCR(iobase));
390 delay(100);
391 /* turn on FPGA supply voltage again */
392 outb(MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2, MCR(iobase));
393 delay(100);
397 * send one byte to FPGA
400 static int fpga_write(int iobase, unsigned char wrd)
402 unsigned char bit;
403 int k;
404 unsigned long timeout = jiffies + HZ / 10;
406 for (k = 0; k < 8; k++) {
407 bit = (wrd & 0x80) ? (MCR_RTS | MCR_DTR) : MCR_DTR;
408 outb(bit | MCR_OUT1 | MCR_OUT2, MCR(iobase));
409 wrd <<= 1;
410 outb(0xfc, THR(iobase));
411 while ((inb(LSR(iobase)) & LSR_TSRE) == 0)
412 if (jiffies > timeout)
413 return -1;
416 return 0;
419 #ifdef MODULE
420 static void free_mcs(void)
422 struct yam_mcs *p;
424 while (yam_data) {
425 p = yam_data;
426 yam_data = yam_data->next;
427 kfree(p);
430 #endif
432 static unsigned char *
433 add_mcs(unsigned char *bits, int bitrate)
435 struct yam_mcs *p;
437 /* If it already exists, replace the bit data */
438 p = yam_data;
439 while (p) {
440 if (p->bitrate == bitrate) {
441 memcpy(p->bits, bits, YAM_FPGA_SIZE);
442 return p->bits;
444 p = p->next;
447 /* Allocate a new mcs */
448 p = kmalloc(sizeof(struct yam_mcs), GFP_ATOMIC);
449 if (p == NULL) {
450 printk(KERN_WARNING "YAM: no memory to allocate mcs\n");
451 return NULL;
453 memcpy(p->bits, bits, YAM_FPGA_SIZE);
454 p->bitrate = bitrate;
455 p->next = yam_data;
456 yam_data = p;
458 return p->bits;
461 static unsigned char *get_mcs(int bitrate)
463 struct yam_mcs *p;
465 p = yam_data;
466 while (p) {
467 if (p->bitrate == bitrate)
468 return p->bits;
469 p = p->next;
472 /* Load predefined mcs data */
473 switch (bitrate) {
474 case 1200:
475 return add_mcs(bits_1200, bitrate);
476 default:
477 return add_mcs(bits_9600, bitrate);
482 * download bitstream to FPGA
483 * data is contained in bits[] array in fpgaconf.h
486 static int fpga_download(int iobase, int bitrate)
488 int i, rc;
489 unsigned char *pbits;
491 pbits = get_mcs(bitrate);
492 if (pbits == NULL)
493 return -1;
495 fpga_reset(iobase);
496 for (i = 0; i < YAM_FPGA_SIZE; i++) {
497 if (fpga_write(iobase, pbits[i])) {
498 printk("yam: error in write cycle\n");
499 return -1; /* write... */
503 fpga_write(iobase, 0xFF);
504 rc = inb(MSR(iobase)); /* check DONE signal */
506 /* Needed for some hardwares */
507 delay(50);
509 return (rc & MSR_DSR) ? 0 : -1;
513 /************************************************************************
514 * Serial port init
515 ************************************************************************/
517 static void yam_set_uart(struct net_device *dev)
519 struct yam_port *yp = (struct yam_port *) dev->priv;
520 int divisor = 115200 / yp->baudrate;
522 outb(0, IER(dev->base_addr));
523 outb(LCR_DLAB | LCR_BIT8, LCR(dev->base_addr));
524 outb(divisor, DLL(dev->base_addr));
525 outb(0, DLM(dev->base_addr));
526 outb(LCR_BIT8, LCR(dev->base_addr));
527 outb(PTT_OFF, MCR(dev->base_addr));
528 outb(0x00, FCR(dev->base_addr));
530 /* Flush pending irq */
532 inb(RBR(dev->base_addr));
533 inb(MSR(dev->base_addr));
535 /* Enable rx irq */
537 outb(ENABLE_RTXINT, IER(dev->base_addr));
541 /* --------------------------------------------------------------------- */
543 enum uart {
544 c_uart_unknown, c_uart_8250,
545 c_uart_16450, c_uart_16550, c_uart_16550A
548 static const char *uart_str[] =
549 {"unknown", "8250", "16450", "16550", "16550A"};
551 static enum uart yam_check_uart(unsigned int iobase)
553 unsigned char b1, b2, b3;
554 enum uart u;
555 enum uart uart_tab[] =
556 {c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A};
558 b1 = inb(MCR(iobase));
559 outb(b1 | 0x10, MCR(iobase)); /* loopback mode */
560 b2 = inb(MSR(iobase));
561 outb(0x1a, MCR(iobase));
562 b3 = inb(MSR(iobase)) & 0xf0;
563 outb(b1, MCR(iobase)); /* restore old values */
564 outb(b2, MSR(iobase));
565 if (b3 != 0x90)
566 return c_uart_unknown;
567 inb(RBR(iobase));
568 inb(RBR(iobase));
569 outb(0x01, FCR(iobase)); /* enable FIFOs */
570 u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
571 if (u == c_uart_16450) {
572 outb(0x5a, SCR(iobase));
573 b1 = inb(SCR(iobase));
574 outb(0xa5, SCR(iobase));
575 b2 = inb(SCR(iobase));
576 if ((b1 != 0x5a) || (b2 != 0xa5))
577 u = c_uart_8250;
579 return u;
582 /******************************************************************************
583 * Rx Section
584 ******************************************************************************/
585 static void inline
586 yam_rx_flag(struct net_device *dev, struct yam_port *yp)
588 if (yp->dcd && yp->rx_len >= 3 && yp->rx_len < YAM_MAX_FRAME) {
589 int pkt_len = yp->rx_len - 2 + 1; /* -CRC + kiss */
590 struct sk_buff *skb;
592 if ((yp->rx_crch & yp->rx_crcl) != 0xFF) {
593 /* Bad crc */
594 } else {
595 if (!(skb = dev_alloc_skb(pkt_len))) {
596 printk("%s: memory squeeze, dropping packet\n", dev->name);
597 ++yp->stats.rx_dropped;
598 } else {
599 unsigned char *cp;
600 skb->dev = dev;
601 cp = skb_put(skb, pkt_len);
602 *cp++ = 0; /* KISS kludge */
603 memcpy(cp, yp->rx_buf, pkt_len - 1);
604 skb->protocol = htons(ETH_P_AX25);
605 skb->mac.raw = skb->data;
606 netif_rx(skb);
607 ++yp->stats.rx_packets;
611 yp->rx_len = 0;
612 yp->rx_crcl = 0x21;
613 yp->rx_crch = 0xf3;
616 static void inline
617 yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsigned char rxb)
619 if (yp->rx_len < YAM_MAX_FRAME) {
620 unsigned char c = yp->rx_crcl;
621 yp->rx_crcl = (chktabl[c] ^ yp->rx_crch);
622 yp->rx_crch = (chktabh[c] ^ rxb);
623 yp->rx_buf[yp->rx_len++] = rxb;
627 /********************************************************************************
628 * TX Section
629 ********************************************************************************/
631 static void ptt_on(struct net_device *dev)
633 outb(PTT_ON, MCR(dev->base_addr));
636 static void ptt_off(struct net_device *dev)
638 outb(PTT_OFF, MCR(dev->base_addr));
641 static int yam_send_packet(struct sk_buff *skb, struct net_device *dev)
643 struct yam_port *yp = dev->priv;
645 if (skb == NULL) {
646 return 0;
648 skb_queue_tail(&yp->send_queue, skb);
649 dev->trans_start = jiffies;
650 return 0;
653 static void yam_start_tx(struct net_device *dev, struct yam_port *yp)
655 if ((yp->tx_state == TX_TAIL) || (yp->txd == 0))
656 yp->tx_count = 1;
657 else
658 yp->tx_count = (yp->bitrate * yp->txd) / 8000;
659 yp->tx_state = TX_HEAD;
660 ptt_on(dev);
663 static unsigned short random_seed;
665 static inline unsigned short random_num(void)
667 random_seed = 28629 * random_seed + 157;
668 return random_seed;
671 static void yam_arbitrate(struct net_device *dev)
673 struct yam_port *yp = dev->priv;
675 if (!yp || yp->magic != YAM_MAGIC
676 || yp->tx_state != TX_OFF || skb_queue_empty(&yp->send_queue)) {
677 return;
679 /* tx_state is TX_OFF and there is data to send */
681 if (yp->dupmode) {
682 /* Full duplex mode, don't wait */
683 yam_start_tx(dev, yp);
684 return;
686 if (yp->dcd) {
687 /* DCD on, wait slotime ... */
688 yp->slotcnt = yp->slot / 10;
689 return;
691 /* Is slottime passed ? */
692 if ((--yp->slotcnt) > 0)
693 return;
695 yp->slotcnt = yp->slot / 10;
697 /* is random > persist ? */
698 if ((random_num() % 256) > yp->pers)
699 return;
701 yam_start_tx(dev, yp);
704 static void yam_dotimer(unsigned long dummy)
706 int i;
708 for (i = 0; i < NR_PORTS; i++) {
709 struct net_device *dev = &yam_ports[i].dev;
710 if (dev->start)
711 yam_arbitrate(dev);
713 yam_timer.expires = jiffies + HZ / 100;
714 add_timer(&yam_timer);
717 static void yam_tx_byte(struct net_device *dev, struct yam_port *yp)
719 struct sk_buff *skb;
720 unsigned char b, temp;
722 switch (yp->tx_state) {
723 case TX_OFF:
724 break;
725 case TX_HEAD:
726 if (--yp->tx_count <= 0) {
727 if (!(skb = skb_dequeue(&yp->send_queue))) {
728 ptt_off(dev);
729 yp->tx_state = TX_OFF;
730 break;
732 yp->tx_state = TX_DATA;
733 if (skb->data[0] != 0) {
734 /* do_kiss_params(s, skb->data, skb->len); */
735 dev_kfree_skb(skb);
736 break;
738 yp->tx_len = skb->len - 1; /* strip KISS byte */
739 if (yp->tx_len >= YAM_MAX_FRAME || yp->tx_len < 2) {
740 dev_kfree_skb(skb);
741 break;
743 memcpy(yp->tx_buf, skb->data + 1, yp->tx_len);
744 dev_kfree_skb(skb);
745 yp->tx_count = 0;
746 yp->tx_crcl = 0x21;
747 yp->tx_crch = 0xf3;
748 yp->tx_state = TX_DATA;
750 break;
751 case TX_DATA:
752 b = yp->tx_buf[yp->tx_count++];
753 outb(b, THR(dev->base_addr));
754 temp = yp->tx_crcl;
755 yp->tx_crcl = chktabl[temp] ^ yp->tx_crch;
756 yp->tx_crch = chktabh[temp] ^ b;
757 if (yp->tx_count >= yp->tx_len) {
758 yp->tx_state = TX_CRC1;
760 break;
761 case TX_CRC1:
762 yp->tx_crch = chktabl[yp->tx_crcl] ^ yp->tx_crch;
763 yp->tx_crcl = chktabh[yp->tx_crcl] ^ chktabl[yp->tx_crch] ^ 0xff;
764 outb(yp->tx_crcl, THR(dev->base_addr));
765 yp->tx_state = TX_CRC2;
766 break;
767 case TX_CRC2:
768 outb(chktabh[yp->tx_crch] ^ 0xFF, THR(dev->base_addr));
769 if (skb_queue_empty(&yp->send_queue)) {
770 yp->tx_count = (yp->bitrate * yp->txtail) / 8000;
771 if (yp->dupmode == 2)
772 yp->tx_count += (yp->bitrate * yp->holdd) / 8;
773 if (yp->tx_count == 0)
774 yp->tx_count = 1;
775 yp->tx_state = TX_TAIL;
776 } else {
777 yp->tx_count = 1;
778 yp->tx_state = TX_HEAD;
780 ++yp->stats.tx_packets;
781 break;
782 case TX_TAIL:
783 if (--yp->tx_count <= 0) {
784 yp->tx_state = TX_OFF;
785 ptt_off(dev);
787 break;
791 /***********************************************************************************
792 * ISR routine
793 ************************************************************************************/
795 static void yam_interrupt(int irq, void *dev_id, struct pt_regs *regs)
797 struct net_device *dev;
798 struct yam_port *yp;
799 unsigned char iir;
800 int counter = 100;
801 int i;
803 sti();
805 for (i = 0; i < NR_PORTS; i++) {
806 yp = &yam_ports[i];
807 dev = &yp->dev;
809 if (!dev->start)
810 continue;
812 while ((iir = IIR_MASK & inb(IIR(dev->base_addr))) != IIR_NOPEND) {
813 unsigned char msr = inb(MSR(dev->base_addr));
814 unsigned char lsr = inb(LSR(dev->base_addr));
815 unsigned char rxb;
817 if (lsr & LSR_OE)
818 ++yp->stats.rx_fifo_errors;
820 yp->dcd = (msr & RX_DCD) ? 1 : 0;
822 if (--counter <= 0) {
823 printk("%s: too many irq iir=%d\n", dev->name, iir);
824 return;
826 if (msr & TX_RDY) {
827 ++yp->nb_mdint;
828 yam_tx_byte(dev, yp);
830 if (lsr & LSR_RXC) {
831 ++yp->nb_rxint;
832 rxb = inb(RBR(dev->base_addr));
833 if (msr & RX_FLAG)
834 yam_rx_flag(dev, yp);
835 else
836 yam_rx_byte(dev, yp, rxb);
842 static int yam_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
844 int len = 0;
845 int i;
846 off_t pos = 0;
847 off_t begin = 0;
849 cli();
851 for (i = 0; i < NR_PORTS; i++) {
852 if (yam_ports[i].iobase == 0 || yam_ports[i].irq == 0)
853 continue;
854 len += sprintf(buffer + len, "Device %s\n", yam_ports[i].name);
855 len += sprintf(buffer + len, " Up %d\n", yam_ports[i].dev.start);
856 len += sprintf(buffer + len, " Speed %u\n", yam_ports[i].bitrate);
857 len += sprintf(buffer + len, " IoBase 0x%x\n", yam_ports[i].iobase);
858 len += sprintf(buffer + len, " BaudRate %u\n", yam_ports[i].baudrate);
859 len += sprintf(buffer + len, " IRQ %u\n", yam_ports[i].irq);
860 len += sprintf(buffer + len, " TxState %u\n", yam_ports[i].tx_state);
861 len += sprintf(buffer + len, " Duplex %u\n", yam_ports[i].dupmode);
862 len += sprintf(buffer + len, " HoldDly %u\n", yam_ports[i].holdd);
863 len += sprintf(buffer + len, " TxDelay %u\n", yam_ports[i].txd);
864 len += sprintf(buffer + len, " TxTail %u\n", yam_ports[i].txtail);
865 len += sprintf(buffer + len, " SlotTime %u\n", yam_ports[i].slot);
866 len += sprintf(buffer + len, " Persist %u\n", yam_ports[i].pers);
867 len += sprintf(buffer + len, " TxFrames %lu\n", yam_ports[i].stats.tx_packets);
868 len += sprintf(buffer + len, " RxFrames %lu\n", yam_ports[i].stats.rx_packets);
869 len += sprintf(buffer + len, " TxInt %u\n", yam_ports[i].nb_mdint);
870 len += sprintf(buffer + len, " RxInt %u\n", yam_ports[i].nb_rxint);
871 len += sprintf(buffer + len, " RxOver %lu\n", yam_ports[i].stats.rx_fifo_errors);
872 len += sprintf(buffer + len, "\n");
874 pos = begin + len;
876 if (pos < offset) {
877 len = 0;
878 begin = pos;
880 if (pos > offset + length)
881 break;
884 sti();
886 *start = buffer + (offset - begin);
887 len -= (offset - begin);
889 if (len > length)
890 len = length;
892 return len;
895 #ifdef CONFIG_INET
896 #ifdef CONFIG_PROC_FS
897 #define yam_net_procfs_init() proc_net_create("yam",0,yam_net_get_info)
898 #define yam_net_procfs_remove() proc_net_remove("yam")
899 #else
900 #define yam_net_procfs_init()
901 #define yam_net_procfs_remove()
902 #endif /* CONFIG_PROC_FS */
903 #else
904 #define yam_net_procfs_init()
905 #define yam_net_procfs_remove()
906 #endif /* CONFIG_INET */
908 /* --------------------------------------------------------------------- */
910 #if LINUX_VERSION_CODE >= 0x20119
911 static struct net_device_stats *
912 yam_get_stats(struct net_device *dev)
913 #else
914 static struct enet_statistics *
915 yam_get_stats(struct net_device *dev)
916 #endif
918 struct yam_port *yp;
920 if (!dev || !dev->priv)
921 return NULL;
923 yp = (struct yam_port *) dev->priv;
924 if (yp->magic != YAM_MAGIC)
925 return NULL;
928 * Get the current statistics. This may be called with the
929 * card open or closed.
931 return &yp->stats;
934 /* --------------------------------------------------------------------- */
936 static int yam_open(struct net_device *dev)
938 struct yam_port *yp = (struct yam_port *) dev->priv;
939 enum uart u;
940 int i;
942 printk(KERN_INFO "Trying %s at iobase 0x%lx irq %u\n", dev->name, dev->base_addr, dev->irq);
944 if (!dev || !yp || !yp->bitrate)
945 return -ENXIO;
946 if (!dev->base_addr || dev->base_addr > 0x1000 - YAM_EXTENT ||
947 dev->irq < 2 || dev->irq > 15) {
948 return -ENXIO;
950 if (check_region(dev->base_addr, YAM_EXTENT)) {
951 printk("%s: cannot 0x%lx busy\n", dev->name, dev->base_addr);
952 return -EACCES;
954 if ((u = yam_check_uart(dev->base_addr)) == c_uart_unknown) {
955 printk("%s: cannot find uart type\n", dev->name);
956 return -EIO;
958 if (fpga_download(dev->base_addr, yp->bitrate)) {
959 printk("%s: cannot init FPGA\n", dev->name);
960 return -EIO;
962 outb(0, IER(dev->base_addr));
963 if (request_irq(dev->irq, yam_interrupt, SA_INTERRUPT | SA_SHIRQ, dev->name, NULL)) {
964 printk("%s: irq %d busy\n", dev->name, dev->irq);
965 return -EBUSY;
967 request_region(dev->base_addr, YAM_EXTENT, dev->name);
969 yam_set_uart(dev);
970 dev->start = 1;
971 yp->slotcnt = yp->slot / 10;
973 /* Reset overruns for all ports - FPGA programming makes overruns */
974 for (i = 0; i < NR_PORTS; i++) {
975 inb(LSR(yam_ports[i].dev.base_addr));
976 yam_ports[i].stats.rx_fifo_errors = 0;
979 printk(KERN_INFO "%s at iobase 0x%lx irq %u uart %s\n", dev->name, dev->base_addr, dev->irq,
980 uart_str[u]);
981 MOD_INC_USE_COUNT;
982 return 0;
985 /* --------------------------------------------------------------------- */
987 static int yam_close(struct net_device *dev)
989 struct sk_buff *skb;
990 struct yam_port *yp = (struct yam_port *) dev->priv;
992 if (!dev || !yp)
993 return -EINVAL;
995 * disable interrupts
997 outb(0, IER(dev->base_addr));
998 outb(1, MCR(dev->base_addr));
999 /* Remove IRQ handler if last */
1000 free_irq(dev->irq, NULL);
1001 release_region(dev->base_addr, YAM_EXTENT);
1002 dev->start = 0;
1003 dev->tbusy = 1;
1004 while ((skb = skb_dequeue(&yp->send_queue)))
1005 dev_kfree_skb(skb);
1007 printk(KERN_INFO "%s: close yam at iobase 0x%lx irq %u\n",
1008 yam_drvname, dev->base_addr, dev->irq);
1009 MOD_DEC_USE_COUNT;
1010 return 0;
1013 /* --------------------------------------------------------------------- */
1015 static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
1017 struct yam_port *yp = (struct yam_port *) dev->priv;
1018 struct yamdrv_ioctl_cfg yi;
1019 struct yamdrv_ioctl_mcs *ym;
1020 int ioctl_cmd;
1022 if (copy_from_user(&ioctl_cmd, ifr->ifr_data, sizeof(int)))
1023 return -EFAULT;
1025 if (yp == NULL || yp->magic != YAM_MAGIC)
1026 return -EINVAL;
1028 if (!suser())
1029 return -EPERM;
1031 if (cmd != SIOCDEVPRIVATE)
1032 return -EINVAL;
1034 switch (ioctl_cmd) {
1036 case SIOCYAMRESERVED:
1037 return -EINVAL; /* unused */
1039 case SIOCYAMSMCS:
1040 if (dev->start)
1041 return -EINVAL; /* Cannot change this parameter when up */
1042 ym = kmalloc(sizeof(struct yamdrv_ioctl_mcs), GFP_ATOMIC);
1043 ym->bitrate = 9600;
1044 if (copy_from_user(ym, ifr->ifr_data, sizeof(struct yamdrv_ioctl_mcs)))
1045 return -EFAULT;
1046 if (ym->bitrate > YAM_MAXBITRATE)
1047 return -EINVAL;
1048 add_mcs(ym->bits, ym->bitrate);
1049 kfree(ym);
1050 break;
1052 case SIOCYAMSCFG:
1053 if (copy_from_user(&yi, ifr->ifr_data, sizeof(struct yamdrv_ioctl_cfg)))
1054 return -EFAULT;
1056 if ((yi.cfg.mask & YAM_IOBASE) && dev->start)
1057 return -EINVAL; /* Cannot change this parameter when up */
1058 if ((yi.cfg.mask & YAM_IRQ) && dev->start)
1059 return -EINVAL; /* Cannot change this parameter when up */
1060 if ((yi.cfg.mask & YAM_BITRATE) && dev->start)
1061 return -EINVAL; /* Cannot change this parameter when up */
1062 if ((yi.cfg.mask & YAM_BAUDRATE) && dev->start)
1063 return -EINVAL; /* Cannot change this parameter when up */
1065 if (yi.cfg.mask & YAM_IOBASE) {
1066 yp->iobase = yi.cfg.iobase;
1067 dev->base_addr = yi.cfg.iobase;
1069 if (yi.cfg.mask & YAM_IRQ) {
1070 if (yi.cfg.irq > 15)
1071 return -EINVAL;
1072 yp->irq = yi.cfg.irq;
1073 dev->irq = yi.cfg.irq;
1075 if (yi.cfg.mask & YAM_BITRATE) {
1076 if (yi.cfg.bitrate > YAM_MAXBITRATE)
1077 return -EINVAL;
1078 yp->bitrate = yi.cfg.bitrate;
1080 if (yi.cfg.mask & YAM_BAUDRATE) {
1081 if (yi.cfg.baudrate > YAM_MAXBAUDRATE)
1082 return -EINVAL;
1083 yp->baudrate = yi.cfg.baudrate;
1085 if (yi.cfg.mask & YAM_MODE) {
1086 if (yi.cfg.mode > YAM_MAXMODE)
1087 return -EINVAL;
1088 yp->dupmode = yi.cfg.mode;
1090 if (yi.cfg.mask & YAM_HOLDDLY) {
1091 if (yi.cfg.holddly > YAM_MAXHOLDDLY)
1092 return -EINVAL;
1093 yp->holdd = yi.cfg.holddly;
1095 if (yi.cfg.mask & YAM_TXDELAY) {
1096 if (yi.cfg.txdelay > YAM_MAXTXDELAY)
1097 return -EINVAL;
1098 yp->txd = yi.cfg.txdelay;
1100 if (yi.cfg.mask & YAM_TXTAIL) {
1101 if (yi.cfg.txtail > YAM_MAXTXTAIL)
1102 return -EINVAL;
1103 yp->txtail = yi.cfg.txtail;
1105 if (yi.cfg.mask & YAM_PERSIST) {
1106 if (yi.cfg.persist > YAM_MAXPERSIST)
1107 return -EINVAL;
1108 yp->pers = yi.cfg.persist;
1110 if (yi.cfg.mask & YAM_SLOTTIME) {
1111 if (yi.cfg.slottime > YAM_MAXSLOTTIME)
1112 return -EINVAL;
1113 yp->slot = yi.cfg.slottime;
1114 yp->slotcnt = yp->slot / 10;
1116 break;
1118 case SIOCYAMGCFG:
1119 yi.cfg.mask = 0xffffffff;
1120 yi.cfg.iobase = yp->iobase;
1121 yi.cfg.irq = yp->irq;
1122 yi.cfg.bitrate = yp->bitrate;
1123 yi.cfg.baudrate = yp->baudrate;
1124 yi.cfg.mode = yp->dupmode;
1125 yi.cfg.txdelay = yp->txd;
1126 yi.cfg.holddly = yp->holdd;
1127 yi.cfg.txtail = yp->txtail;
1128 yi.cfg.persist = yp->pers;
1129 yi.cfg.slottime = yp->slot;
1130 if (copy_to_user(ifr->ifr_data, &yi, sizeof(struct yamdrv_ioctl_cfg)))
1131 return -EFAULT;
1132 break;
1134 default:
1135 return -EINVAL;
1139 return 0;
1142 /* --------------------------------------------------------------------- */
1144 static int yam_set_mac_address(struct net_device *dev, void *addr)
1146 struct sockaddr *sa = (struct sockaddr *) addr;
1148 /* addr is an AX.25 shifted ASCII mac address */
1149 memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
1150 return 0;
1153 /* --------------------------------------------------------------------- */
1155 static int yam_probe(struct net_device *dev)
1157 struct yam_port *yp;
1159 if (!dev)
1160 return -ENXIO;
1162 yp = (struct yam_port *) dev->priv;
1164 dev->open = yam_open;
1165 dev->stop = yam_close;
1166 dev->do_ioctl = yam_ioctl;
1167 dev->hard_start_xmit = yam_send_packet;
1168 dev->get_stats = yam_get_stats;
1170 dev_init_buffers(dev);
1171 skb_queue_head_init(&yp->send_queue);
1173 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
1174 dev->hard_header = ax25_encapsulate;
1175 dev->rebuild_header = ax25_rebuild_header;
1176 #else /* CONFIG_AX25 || CONFIG_AX25_MODULE */
1177 dev->hard_header = NULL;
1178 dev->rebuild_header = NULL;
1179 #endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
1181 dev->set_mac_address = yam_set_mac_address;
1183 dev->type = ARPHRD_AX25; /* AF_AX25 device */
1184 dev->hard_header_len = 73; /* We do digipeaters now */
1185 dev->mtu = 256; /* AX25 is the default */
1186 dev->addr_len = 7; /* sizeof an ax.25 address */
1187 memcpy(dev->broadcast, ax25_bcast, 7);
1188 memcpy(dev->dev_addr, ax25_test, 7);
1190 /* New style flags */
1191 dev->flags = 0;
1193 return 0;
1196 /* --------------------------------------------------------------------- */
1198 int __init yam_init(struct net_device *dev)
1200 int i;
1202 printk(yam_drvinfo);
1204 /* Clears the IRQ table */
1205 memset(irqs, 0, sizeof(irqs));
1206 memset(yam_ports, 0, sizeof(yam_ports));
1208 for (i = 0; i < NR_PORTS; i++) {
1209 sprintf(yam_ports[i].name, "yam%d", i);
1210 yam_ports[i].magic = YAM_MAGIC;
1211 yam_ports[i].bitrate = DEFAULT_BITRATE;
1212 yam_ports[i].baudrate = DEFAULT_BITRATE * 2;
1213 yam_ports[i].iobase = 0;
1214 yam_ports[i].irq = 0;
1215 yam_ports[i].dupmode = 0;
1216 yam_ports[i].holdd = DEFAULT_HOLDD;
1217 yam_ports[i].txd = DEFAULT_TXD;
1218 yam_ports[i].txtail = DEFAULT_TXTAIL;
1219 yam_ports[i].slot = DEFAULT_SLOT;
1220 yam_ports[i].pers = DEFAULT_PERS;
1222 dev = &yam_ports[i].dev;
1224 dev->priv = &yam_ports[i];
1225 dev->name = yam_ports[i].name;
1226 dev->base_addr = yam_ports[i].iobase;
1227 dev->irq = yam_ports[i].irq;
1228 dev->init = yam_probe;
1229 dev->if_port = 0;
1230 dev->start = 0;
1231 dev->tbusy = 1;
1233 if (register_netdev(dev)) {
1234 printk(KERN_WARNING "yam: cannot register net device %s\n", dev->name);
1235 return -ENXIO;
1239 yam_timer.function = yam_dotimer;
1240 yam_timer.expires = jiffies + HZ / 100;
1241 add_timer(&yam_timer);
1243 yam_net_procfs_init();
1245 /* do not keep this device */
1246 return 1;
1249 /* --------------------------------------------------------------------- */
1251 #ifdef MODULE
1254 * command line settable parameters
1257 #if LINUX_VERSION_CODE >= 0x20115
1259 MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr");
1260 MODULE_DESCRIPTION("Yam amateur radio modem driver");
1262 #endif
1264 int init_module(void)
1266 int ret = yam_init(NULL);
1268 return (ret == 1) ? 0 : ret;
1271 /* --------------------------------------------------------------------- */
1273 void cleanup_module(void)
1275 int i;
1277 del_timer(&yam_timer);
1278 for (i = 0; i < NR_PORTS; i++) {
1279 struct net_device *dev = &yam_ports[i].dev;
1280 if (!dev->priv)
1281 continue;
1282 if (dev->start)
1283 yam_close(dev);
1284 unregister_netdev(dev);
1286 free_mcs();
1287 yam_net_procfs_remove();
1290 #endif /* MODULE */
1291 /* --------------------------------------------------------------------- */