blob: c8531612eea950b873593ba88740f809448fd7b7 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * SDLA An implementation of a driver for the Sangoma S502/S508 series
3 * multi-protocol PC interface card. Initial offering is with
4 * the DLCI driver, providing Frame Relay support for linux.
5 *
6 * Global definitions for the Frame relay interface.
7 *
8 * Version: @(#)sdla.c 0.30 12 Sep 1996
9 *
10 * Credits: Sangoma Technologies, for the use of 2 cards for an extended
11 * period of time.
12 * David Mandelstam <dm@sangoma.com> for getting me started on
13 * this project, and incentive to complete it.
14 * Gene Kozen <74604.152@compuserve.com> for providing me with
15 * important information about the cards.
16 *
17 * Author: Mike McLagan <mike.mclagan@linux.org>
18 *
19 * Changes:
20 * 0.15 Mike McLagan Improved error handling, packet dropping
21 * 0.20 Mike McLagan New transmit/receive flags for config
22 * If in FR mode, don't accept packets from
23 * non DLCI devices.
24 * 0.25 Mike McLagan Fixed problem with rejecting packets
25 * from non DLCI devices.
26 * 0.30 Mike McLagan Fixed kernel panic when used with modified
27 * ifconfig
28 *
29 * This program is free software; you can redistribute it and/or
30 * modify it under the terms of the GNU General Public License
31 * as published by the Free Software Foundation; either version
32 * 2 of the License, or (at your option) any later version.
33 */
34
Joe Perches86fb0cc2011-06-26 19:01:31 +000035#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/module.h>
38#include <linux/kernel.h>
39#include <linux/types.h>
40#include <linux/fcntl.h>
41#include <linux/interrupt.h>
42#include <linux/ptrace.h>
43#include <linux/ioport.h>
44#include <linux/in.h>
45#include <linux/slab.h>
46#include <linux/string.h>
47#include <linux/timer.h>
48#include <linux/errno.h>
49#include <linux/init.h>
50#include <linux/netdevice.h>
51#include <linux/skbuff.h>
52#include <linux/if_arp.h>
53#include <linux/if_frad.h>
54#include <linux/sdla.h>
55#include <linux/bitops.h>
56
57#include <asm/system.h>
58#include <asm/io.h>
59#include <asm/dma.h>
60#include <asm/uaccess.h>
61
62static const char* version = "SDLA driver v0.30, 12 Sep 1996, mike.mclagan@linux.org";
63
Randy Dunlap96ebb922006-06-25 05:48:37 -070064static unsigned int valid_port[] = { 0x250, 0x270, 0x280, 0x300, 0x350, 0x360, 0x380, 0x390};
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
Randy Dunlap96ebb922006-06-25 05:48:37 -070066static unsigned int valid_mem[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 0xA0000, 0xA2000, 0xA4000, 0xA6000, 0xA8000, 0xAA000, 0xAC000, 0xAE000,
68 0xB0000, 0xB2000, 0xB4000, 0xB6000, 0xB8000, 0xBA000, 0xBC000, 0xBE000,
69 0xC0000, 0xC2000, 0xC4000, 0xC6000, 0xC8000, 0xCA000, 0xCC000, 0xCE000,
70 0xD0000, 0xD2000, 0xD4000, 0xD6000, 0xD8000, 0xDA000, 0xDC000, 0xDE000,
71 0xE0000, 0xE2000, 0xE4000, 0xE6000, 0xE8000, 0xEA000, 0xEC000, 0xEE000};
72
73static DEFINE_SPINLOCK(sdla_lock);
74
75/*********************************************************
76 *
77 * these are the core routines that access the card itself
78 *
79 *********************************************************/
80
81#define SDLA_WINDOW(dev,addr) outb((((addr) >> 13) & 0x1F), (dev)->base_addr + SDLA_REG_Z80_WINDOW)
82
83static void __sdla_read(struct net_device *dev, int addr, void *buf, short len)
84{
85 char *temp;
86 const void *base;
87 int offset, bytes;
88
89 temp = buf;
90 while(len)
91 {
92 offset = addr & SDLA_ADDR_MASK;
93 bytes = offset + len > SDLA_WINDOW_SIZE ? SDLA_WINDOW_SIZE - offset : len;
94 base = (const void *) (dev->mem_start + offset);
95
96 SDLA_WINDOW(dev, addr);
97 memcpy(temp, base, bytes);
98
99 addr += bytes;
100 temp += bytes;
101 len -= bytes;
102 }
103}
104
105static void sdla_read(struct net_device *dev, int addr, void *buf, short len)
106{
107 unsigned long flags;
108 spin_lock_irqsave(&sdla_lock, flags);
109 __sdla_read(dev, addr, buf, len);
110 spin_unlock_irqrestore(&sdla_lock, flags);
111}
112
113static void __sdla_write(struct net_device *dev, int addr,
114 const void *buf, short len)
115{
116 const char *temp;
117 void *base;
118 int offset, bytes;
119
120 temp = buf;
121 while(len)
122 {
123 offset = addr & SDLA_ADDR_MASK;
124 bytes = offset + len > SDLA_WINDOW_SIZE ? SDLA_WINDOW_SIZE - offset : len;
125 base = (void *) (dev->mem_start + offset);
126
127 SDLA_WINDOW(dev, addr);
128 memcpy(base, temp, bytes);
129
130 addr += bytes;
131 temp += bytes;
132 len -= bytes;
133 }
134}
135
136static void sdla_write(struct net_device *dev, int addr,
137 const void *buf, short len)
138{
139 unsigned long flags;
140
141 spin_lock_irqsave(&sdla_lock, flags);
142 __sdla_write(dev, addr, buf, len);
143 spin_unlock_irqrestore(&sdla_lock, flags);
144}
145
146
147static void sdla_clear(struct net_device *dev)
148{
149 unsigned long flags;
150 char *base;
151 int len, addr, bytes;
152
153 len = 65536;
154 addr = 0;
155 bytes = SDLA_WINDOW_SIZE;
156 base = (void *) dev->mem_start;
157
158 spin_lock_irqsave(&sdla_lock, flags);
159 while(len)
160 {
161 SDLA_WINDOW(dev, addr);
162 memset(base, 0, bytes);
163
164 addr += bytes;
165 len -= bytes;
166 }
167 spin_unlock_irqrestore(&sdla_lock, flags);
168
169}
170
171static char sdla_byte(struct net_device *dev, int addr)
172{
173 unsigned long flags;
174 char byte, *temp;
175
176 temp = (void *) (dev->mem_start + (addr & SDLA_ADDR_MASK));
177
178 spin_lock_irqsave(&sdla_lock, flags);
179 SDLA_WINDOW(dev, addr);
180 byte = *temp;
181 spin_unlock_irqrestore(&sdla_lock, flags);
182
Eric Dumazet807540b2010-09-23 05:40:09 +0000183 return byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184}
185
Adrian Bunk7665a082005-09-09 23:17:28 -0700186static void sdla_stop(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187{
188 struct frad_local *flp;
189
Wang Chen8f15ea42008-11-12 23:38:36 -0800190 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 switch(flp->type)
192 {
193 case SDLA_S502A:
194 outb(SDLA_S502A_HALT, dev->base_addr + SDLA_REG_CONTROL);
195 flp->state = SDLA_HALT;
196 break;
197 case SDLA_S502E:
198 outb(SDLA_HALT, dev->base_addr + SDLA_REG_Z80_CONTROL);
199 outb(SDLA_S502E_ENABLE, dev->base_addr + SDLA_REG_CONTROL);
200 flp->state = SDLA_S502E_ENABLE;
201 break;
202 case SDLA_S507:
203 flp->state &= ~SDLA_CPUEN;
204 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
205 break;
206 case SDLA_S508:
207 flp->state &= ~SDLA_CPUEN;
208 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
209 break;
210 }
211}
212
Adrian Bunk7665a082005-09-09 23:17:28 -0700213static void sdla_start(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214{
215 struct frad_local *flp;
216
Wang Chen8f15ea42008-11-12 23:38:36 -0800217 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 switch(flp->type)
219 {
220 case SDLA_S502A:
221 outb(SDLA_S502A_NMI, dev->base_addr + SDLA_REG_CONTROL);
222 outb(SDLA_S502A_START, dev->base_addr + SDLA_REG_CONTROL);
223 flp->state = SDLA_S502A_START;
224 break;
225 case SDLA_S502E:
226 outb(SDLA_S502E_CPUEN, dev->base_addr + SDLA_REG_Z80_CONTROL);
227 outb(0x00, dev->base_addr + SDLA_REG_CONTROL);
228 flp->state = 0;
229 break;
230 case SDLA_S507:
231 flp->state |= SDLA_CPUEN;
232 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
233 break;
234 case SDLA_S508:
235 flp->state |= SDLA_CPUEN;
236 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
237 break;
238 }
239}
240
241/****************************************************
242 *
243 * this is used for the S502A/E cards to determine
244 * the speed of the onboard CPU. Calibration is
245 * necessary for the Frame Relay code uploaded
246 * later. Incorrect results cause timing problems
247 * with link checks & status messages
248 *
249 ***************************************************/
250
Adrian Bunk7665a082005-09-09 23:17:28 -0700251static int sdla_z80_poll(struct net_device *dev, int z80_addr, int jiffs, char resp1, char resp2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252{
253 unsigned long start, done, now;
254 char resp, *temp;
255
256 start = now = jiffies;
257 done = jiffies + jiffs;
258
259 temp = (void *)dev->mem_start;
260 temp += z80_addr & SDLA_ADDR_MASK;
261
262 resp = ~resp1;
263 while (time_before(jiffies, done) && (resp != resp1) && (!resp2 || (resp != resp2)))
264 {
265 if (jiffies != now)
266 {
267 SDLA_WINDOW(dev, z80_addr);
268 now = jiffies;
269 resp = *temp;
270 }
271 }
Eric Dumazet807540b2010-09-23 05:40:09 +0000272 return time_before(jiffies, done) ? jiffies - start : -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273}
274
275/* constants for Z80 CPU speed */
276#define Z80_READY '1' /* Z80 is ready to begin */
277#define LOADER_READY '2' /* driver is ready to begin */
278#define Z80_SCC_OK '3' /* SCC is on board */
279#define Z80_SCC_BAD '4' /* SCC was not found */
280
281static int sdla_cpuspeed(struct net_device *dev, struct ifreq *ifr)
282{
283 int jiffs;
284 char data;
285
286 sdla_start(dev);
287 if (sdla_z80_poll(dev, 0, 3*HZ, Z80_READY, 0) < 0)
Eric Dumazet807540b2010-09-23 05:40:09 +0000288 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290 data = LOADER_READY;
291 sdla_write(dev, 0, &data, 1);
292
293 if ((jiffs = sdla_z80_poll(dev, 0, 8*HZ, Z80_SCC_OK, Z80_SCC_BAD)) < 0)
Eric Dumazet807540b2010-09-23 05:40:09 +0000294 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 sdla_stop(dev);
297 sdla_read(dev, 0, &data, 1);
298
299 if (data == Z80_SCC_BAD)
300 {
301 printk("%s: SCC bad\n", dev->name);
Eric Dumazet807540b2010-09-23 05:40:09 +0000302 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 }
304
305 if (data != Z80_SCC_OK)
Eric Dumazet807540b2010-09-23 05:40:09 +0000306 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
308 if (jiffs < 165)
309 ifr->ifr_mtu = SDLA_CPU_16M;
310 else if (jiffs < 220)
311 ifr->ifr_mtu = SDLA_CPU_10M;
312 else if (jiffs < 258)
313 ifr->ifr_mtu = SDLA_CPU_8M;
314 else if (jiffs < 357)
315 ifr->ifr_mtu = SDLA_CPU_7M;
316 else if (jiffs < 467)
317 ifr->ifr_mtu = SDLA_CPU_5M;
318 else
319 ifr->ifr_mtu = SDLA_CPU_3M;
320
Eric Dumazet807540b2010-09-23 05:40:09 +0000321 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322}
323
324/************************************************
325 *
326 * Direct interaction with the Frame Relay code
327 * starts here.
328 *
329 ************************************************/
330
331struct _dlci_stat
332{
Jan Blunck6a878182006-01-08 01:05:07 -0800333 short dlci;
334 char flags;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000335} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337struct _frad_stat
338{
339 char flags;
340 struct _dlci_stat dlcis[SDLA_MAX_DLCI];
341};
342
343static void sdla_errors(struct net_device *dev, int cmd, int dlci, int ret, int len, void *data)
344{
345 struct _dlci_stat *pstatus;
346 short *pdlci;
347 int i;
348 char *state, line[30];
349
350 switch (ret)
351 {
352 case SDLA_RET_MODEM:
353 state = data;
354 if (*state & SDLA_MODEM_DCD_LOW)
Joe Perches86fb0cc2011-06-26 19:01:31 +0000355 netdev_info(dev, "Modem DCD unexpectedly low!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 if (*state & SDLA_MODEM_CTS_LOW)
Joe Perches86fb0cc2011-06-26 19:01:31 +0000357 netdev_info(dev, "Modem CTS unexpectedly low!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 /* I should probably do something about this! */
359 break;
360
361 case SDLA_RET_CHANNEL_OFF:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000362 netdev_info(dev, "Channel became inoperative!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 /* same here */
364 break;
365
366 case SDLA_RET_CHANNEL_ON:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000367 netdev_info(dev, "Channel became operative!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 /* same here */
369 break;
370
371 case SDLA_RET_DLCI_STATUS:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000372 netdev_info(dev, "Status change reported by Access Node\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 len /= sizeof(struct _dlci_stat);
374 for(pstatus = data, i=0;i < len;i++,pstatus++)
375 {
376 if (pstatus->flags & SDLA_DLCI_NEW)
377 state = "new";
378 else if (pstatus->flags & SDLA_DLCI_DELETED)
379 state = "deleted";
380 else if (pstatus->flags & SDLA_DLCI_ACTIVE)
381 state = "active";
382 else
383 {
384 sprintf(line, "unknown status: %02X", pstatus->flags);
385 state = line;
386 }
Joe Perches86fb0cc2011-06-26 19:01:31 +0000387 netdev_info(dev, "DLCI %i: %s\n",
388 pstatus->dlci, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 /* same here */
390 }
391 break;
392
393 case SDLA_RET_DLCI_UNKNOWN:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000394 netdev_info(dev, "Received unknown DLCIs:");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 len /= sizeof(short);
396 for(pdlci = data,i=0;i < len;i++,pdlci++)
Joe Perches86fb0cc2011-06-26 19:01:31 +0000397 pr_cont(" %i", *pdlci);
398 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 break;
400
401 case SDLA_RET_TIMEOUT:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000402 netdev_err(dev, "Command timed out!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 break;
404
405 case SDLA_RET_BUF_OVERSIZE:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000406 netdev_info(dev, "Bc/CIR overflow, acceptable size is %i\n",
407 len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 break;
409
410 case SDLA_RET_BUF_TOO_BIG:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000411 netdev_info(dev, "Buffer size over specified max of %i\n",
412 len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 break;
414
415 case SDLA_RET_CHANNEL_INACTIVE:
416 case SDLA_RET_DLCI_INACTIVE:
417 case SDLA_RET_CIR_OVERFLOW:
418 case SDLA_RET_NO_BUFS:
419 if (cmd == SDLA_INFORMATION_WRITE)
420 break;
421
422 default:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000423 netdev_dbg(dev, "Cmd 0x%02X generated return code 0x%02X\n",
424 cmd, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 /* Further processing could be done here */
426 break;
427 }
428}
429
430static int sdla_cmd(struct net_device *dev, int cmd, short dlci, short flags,
431 void *inbuf, short inlen, void *outbuf, short *outlen)
432{
433 static struct _frad_stat status;
434 struct frad_local *flp;
435 struct sdla_cmd *cmd_buf;
436 unsigned long pflags;
437 unsigned long jiffs;
438 int ret, waiting, len;
439 long window;
440
Wang Chen8f15ea42008-11-12 23:38:36 -0800441 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 window = flp->type == SDLA_S508 ? SDLA_508_CMD_BUF : SDLA_502_CMD_BUF;
443 cmd_buf = (struct sdla_cmd *)(dev->mem_start + (window & SDLA_ADDR_MASK));
444 ret = 0;
445 len = 0;
446 jiffs = jiffies + HZ; /* 1 second is plenty */
447
448 spin_lock_irqsave(&sdla_lock, pflags);
449 SDLA_WINDOW(dev, window);
450 cmd_buf->cmd = cmd;
451 cmd_buf->dlci = dlci;
452 cmd_buf->flags = flags;
453
454 if (inbuf)
455 memcpy(cmd_buf->data, inbuf, inlen);
456
457 cmd_buf->length = inlen;
458
459 cmd_buf->opp_flag = 1;
460 spin_unlock_irqrestore(&sdla_lock, pflags);
461
462 waiting = 1;
463 len = 0;
464 while (waiting && time_before_eq(jiffies, jiffs))
465 {
466 if (waiting++ % 3)
467 {
468 spin_lock_irqsave(&sdla_lock, pflags);
469 SDLA_WINDOW(dev, window);
470 waiting = ((volatile int)(cmd_buf->opp_flag));
471 spin_unlock_irqrestore(&sdla_lock, pflags);
472 }
473 }
474
475 if (!waiting)
476 {
477
478 spin_lock_irqsave(&sdla_lock, pflags);
479 SDLA_WINDOW(dev, window);
480 ret = cmd_buf->retval;
481 len = cmd_buf->length;
482 if (outbuf && outlen)
483 {
484 *outlen = *outlen >= len ? len : *outlen;
485
486 if (*outlen)
487 memcpy(outbuf, cmd_buf->data, *outlen);
488 }
489
490 /* This is a local copy that's used for error handling */
491 if (ret)
492 memcpy(&status, cmd_buf->data, len > sizeof(status) ? sizeof(status) : len);
493
494 spin_unlock_irqrestore(&sdla_lock, pflags);
495 }
496 else
497 ret = SDLA_RET_TIMEOUT;
498
499 if (ret != SDLA_RET_OK)
500 sdla_errors(dev, cmd, dlci, ret, len, &status);
501
Eric Dumazet807540b2010-09-23 05:40:09 +0000502 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503}
504
505/***********************************************
506 *
507 * these functions are called by the DLCI driver
508 *
509 ***********************************************/
510
511static int sdla_reconfig(struct net_device *dev);
512
Adrian Bunk7665a082005-09-09 23:17:28 -0700513static int sdla_activate(struct net_device *slave, struct net_device *master)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514{
515 struct frad_local *flp;
516 int i;
517
Wang Chen8f15ea42008-11-12 23:38:36 -0800518 flp = netdev_priv(slave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519
520 for(i=0;i<CONFIG_DLCI_MAX;i++)
521 if (flp->master[i] == master)
522 break;
523
524 if (i == CONFIG_DLCI_MAX)
Eric Dumazet807540b2010-09-23 05:40:09 +0000525 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
527 flp->dlci[i] = abs(flp->dlci[i]);
528
529 if (netif_running(slave) && (flp->config.station == FRAD_STATION_NODE))
530 sdla_cmd(slave, SDLA_ACTIVATE_DLCI, 0, 0, &flp->dlci[i], sizeof(short), NULL, NULL);
531
Eric Dumazet807540b2010-09-23 05:40:09 +0000532 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533}
534
Adrian Bunk7665a082005-09-09 23:17:28 -0700535static int sdla_deactivate(struct net_device *slave, struct net_device *master)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536{
537 struct frad_local *flp;
538 int i;
539
Wang Chen8f15ea42008-11-12 23:38:36 -0800540 flp = netdev_priv(slave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
542 for(i=0;i<CONFIG_DLCI_MAX;i++)
543 if (flp->master[i] == master)
544 break;
545
546 if (i == CONFIG_DLCI_MAX)
Eric Dumazet807540b2010-09-23 05:40:09 +0000547 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
549 flp->dlci[i] = -abs(flp->dlci[i]);
550
551 if (netif_running(slave) && (flp->config.station == FRAD_STATION_NODE))
552 sdla_cmd(slave, SDLA_DEACTIVATE_DLCI, 0, 0, &flp->dlci[i], sizeof(short), NULL, NULL);
553
Eric Dumazet807540b2010-09-23 05:40:09 +0000554 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555}
556
Adrian Bunk7665a082005-09-09 23:17:28 -0700557static int sdla_assoc(struct net_device *slave, struct net_device *master)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558{
559 struct frad_local *flp;
560 int i;
561
562 if (master->type != ARPHRD_DLCI)
Eric Dumazet807540b2010-09-23 05:40:09 +0000563 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
Wang Chen8f15ea42008-11-12 23:38:36 -0800565 flp = netdev_priv(slave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
567 for(i=0;i<CONFIG_DLCI_MAX;i++)
568 {
569 if (!flp->master[i])
570 break;
571 if (abs(flp->dlci[i]) == *(short *)(master->dev_addr))
Eric Dumazet807540b2010-09-23 05:40:09 +0000572 return -EADDRINUSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 }
574
575 if (i == CONFIG_DLCI_MAX)
Eric Dumazet807540b2010-09-23 05:40:09 +0000576 return -EMLINK; /* #### Alan: Comments on this ?? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
578
579 flp->master[i] = master;
580 flp->dlci[i] = -*(short *)(master->dev_addr);
581 master->mtu = slave->mtu;
582
583 if (netif_running(slave)) {
584 if (flp->config.station == FRAD_STATION_CPE)
585 sdla_reconfig(slave);
586 else
587 sdla_cmd(slave, SDLA_ADD_DLCI, 0, 0, master->dev_addr, sizeof(short), NULL, NULL);
588 }
589
Eric Dumazet807540b2010-09-23 05:40:09 +0000590 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591}
592
Adrian Bunk7665a082005-09-09 23:17:28 -0700593static int sdla_deassoc(struct net_device *slave, struct net_device *master)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
595 struct frad_local *flp;
596 int i;
597
Wang Chen8f15ea42008-11-12 23:38:36 -0800598 flp = netdev_priv(slave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
600 for(i=0;i<CONFIG_DLCI_MAX;i++)
601 if (flp->master[i] == master)
602 break;
603
604 if (i == CONFIG_DLCI_MAX)
Eric Dumazet807540b2010-09-23 05:40:09 +0000605 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
607 flp->master[i] = NULL;
608 flp->dlci[i] = 0;
609
610
611 if (netif_running(slave)) {
612 if (flp->config.station == FRAD_STATION_CPE)
613 sdla_reconfig(slave);
614 else
615 sdla_cmd(slave, SDLA_DELETE_DLCI, 0, 0, master->dev_addr, sizeof(short), NULL, NULL);
616 }
617
Eric Dumazet807540b2010-09-23 05:40:09 +0000618 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619}
620
Adrian Bunk7665a082005-09-09 23:17:28 -0700621static int sdla_dlci_conf(struct net_device *slave, struct net_device *master, int get)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622{
623 struct frad_local *flp;
624 struct dlci_local *dlp;
625 int i;
626 short len, ret;
627
Wang Chen8f15ea42008-11-12 23:38:36 -0800628 flp = netdev_priv(slave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
630 for(i=0;i<CONFIG_DLCI_MAX;i++)
631 if (flp->master[i] == master)
632 break;
633
634 if (i == CONFIG_DLCI_MAX)
Eric Dumazet807540b2010-09-23 05:40:09 +0000635 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
Wang Chen8f15ea42008-11-12 23:38:36 -0800637 dlp = netdev_priv(master);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
639 ret = SDLA_RET_OK;
640 len = sizeof(struct dlci_conf);
641 if (netif_running(slave)) {
642 if (get)
643 ret = sdla_cmd(slave, SDLA_READ_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0,
644 NULL, 0, &dlp->config, &len);
645 else
646 ret = sdla_cmd(slave, SDLA_SET_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0,
647 &dlp->config, sizeof(struct dlci_conf) - 4 * sizeof(short), NULL, NULL);
648 }
649
Eric Dumazet807540b2010-09-23 05:40:09 +0000650 return ret == SDLA_RET_OK ? 0 : -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651}
652
653/**************************
654 *
655 * now for the Linux driver
656 *
657 **************************/
658
659/* NOTE: the DLCI driver deals with freeing the SKB!! */
Stephen Hemmingerd71a6742009-08-31 19:50:47 +0000660static netdev_tx_t sdla_transmit(struct sk_buff *skb,
Stephen Hemminger38482422009-09-04 05:33:46 +0000661 struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662{
663 struct frad_local *flp;
664 int ret, addr, accept, i;
665 short size;
666 unsigned long flags;
667 struct buf_entry *pbuf;
668
Wang Chen8f15ea42008-11-12 23:38:36 -0800669 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 ret = 0;
671 accept = 1;
672
673 netif_stop_queue(dev);
674
675 /*
676 * stupid GateD insists on setting up the multicast router thru us
677 * and we're ill equipped to handle a non Frame Relay packet at this
678 * time!
679 */
680
681 accept = 1;
682 switch (dev->type)
683 {
684 case ARPHRD_FRAD:
685 if (skb->dev->type != ARPHRD_DLCI)
686 {
Joe Perches86fb0cc2011-06-26 19:01:31 +0000687 netdev_warn(dev, "Non DLCI device, type %i, tried to send on FRAD module\n",
688 skb->dev->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 accept = 0;
690 }
691 break;
692 default:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000693 netdev_warn(dev, "unknown firmware type 0x%04X\n",
694 dev->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 accept = 0;
696 break;
697 }
698 if (accept)
699 {
700 /* this is frame specific, but till there's a PPP module, it's the default */
701 switch (flp->type)
702 {
703 case SDLA_S502A:
704 case SDLA_S502E:
705 ret = sdla_cmd(dev, SDLA_INFORMATION_WRITE, *(short *)(skb->dev->dev_addr), 0, skb->data, skb->len, NULL, NULL);
706 break;
707 case SDLA_S508:
708 size = sizeof(addr);
709 ret = sdla_cmd(dev, SDLA_INFORMATION_WRITE, *(short *)(skb->dev->dev_addr), 0, NULL, skb->len, &addr, &size);
710 if (ret == SDLA_RET_OK)
711 {
712
713 spin_lock_irqsave(&sdla_lock, flags);
714 SDLA_WINDOW(dev, addr);
715 pbuf = (void *)(((int) dev->mem_start) + (addr & SDLA_ADDR_MASK));
716 __sdla_write(dev, pbuf->buf_addr, skb->data, skb->len);
717 SDLA_WINDOW(dev, addr);
718 pbuf->opp_flag = 1;
719 spin_unlock_irqrestore(&sdla_lock, flags);
720 }
721 break;
722 }
Stephen Hemminger38482422009-09-04 05:33:46 +0000723
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 switch (ret)
725 {
726 case SDLA_RET_OK:
Stephen Hemmingerac995332009-03-26 15:11:25 +0000727 dev->stats.tx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 break;
729
730 case SDLA_RET_CIR_OVERFLOW:
731 case SDLA_RET_BUF_OVERSIZE:
732 case SDLA_RET_NO_BUFS:
Stephen Hemmingerac995332009-03-26 15:11:25 +0000733 dev->stats.tx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 break;
735
736 default:
Stephen Hemmingerac995332009-03-26 15:11:25 +0000737 dev->stats.tx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 break;
739 }
740 }
741 netif_wake_queue(dev);
742 for(i=0;i<CONFIG_DLCI_MAX;i++)
743 {
744 if(flp->master[i]!=NULL)
745 netif_wake_queue(flp->master[i]);
746 }
Stephen Hemminger38482422009-09-04 05:33:46 +0000747
748 dev_kfree_skb(skb);
Stephen Hemmingerd71a6742009-08-31 19:50:47 +0000749 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750}
751
752static void sdla_receive(struct net_device *dev)
753{
754 struct net_device *master;
755 struct frad_local *flp;
756 struct dlci_local *dlp;
757 struct sk_buff *skb;
758
759 struct sdla_cmd *cmd;
760 struct buf_info *pbufi;
761 struct buf_entry *pbuf;
762
763 unsigned long flags;
764 int i=0, received, success, addr, buf_base, buf_top;
765 short dlci, len, len2, split;
766
Wang Chen8f15ea42008-11-12 23:38:36 -0800767 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 success = 1;
769 received = addr = buf_top = buf_base = 0;
770 len = dlci = 0;
771 skb = NULL;
772 master = NULL;
773 cmd = NULL;
774 pbufi = NULL;
775 pbuf = NULL;
776
777 spin_lock_irqsave(&sdla_lock, flags);
778
779 switch (flp->type)
780 {
781 case SDLA_S502A:
782 case SDLA_S502E:
783 cmd = (void *) (dev->mem_start + (SDLA_502_RCV_BUF & SDLA_ADDR_MASK));
784 SDLA_WINDOW(dev, SDLA_502_RCV_BUF);
785 success = cmd->opp_flag;
786 if (!success)
787 break;
788
789 dlci = cmd->dlci;
790 len = cmd->length;
791 break;
792
793 case SDLA_S508:
794 pbufi = (void *) (dev->mem_start + (SDLA_508_RXBUF_INFO & SDLA_ADDR_MASK));
795 SDLA_WINDOW(dev, SDLA_508_RXBUF_INFO);
796 pbuf = (void *) (dev->mem_start + ((pbufi->rse_base + flp->buffer * sizeof(struct buf_entry)) & SDLA_ADDR_MASK));
797 success = pbuf->opp_flag;
798 if (!success)
799 break;
800
801 buf_top = pbufi->buf_top;
802 buf_base = pbufi->buf_base;
803 dlci = pbuf->dlci;
804 len = pbuf->length;
805 addr = pbuf->buf_addr;
806 break;
807 }
808
809 /* common code, find the DLCI and get the SKB */
810 if (success)
811 {
812 for (i=0;i<CONFIG_DLCI_MAX;i++)
813 if (flp->dlci[i] == dlci)
814 break;
815
816 if (i == CONFIG_DLCI_MAX)
817 {
Joe Perches86fb0cc2011-06-26 19:01:31 +0000818 netdev_notice(dev, "Received packet from invalid DLCI %i, ignoring\n",
819 dlci);
Stephen Hemmingerac995332009-03-26 15:11:25 +0000820 dev->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 success = 0;
822 }
823 }
824
825 if (success)
826 {
827 master = flp->master[i];
828 skb = dev_alloc_skb(len + sizeof(struct frhdr));
829 if (skb == NULL)
830 {
Joe Perches86fb0cc2011-06-26 19:01:31 +0000831 netdev_notice(dev, "Memory squeeze, dropping packet\n");
Stephen Hemmingerac995332009-03-26 15:11:25 +0000832 dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 success = 0;
834 }
835 else
836 skb_reserve(skb, sizeof(struct frhdr));
837 }
838
839 /* pick up the data */
840 switch (flp->type)
841 {
842 case SDLA_S502A:
843 case SDLA_S502E:
844 if (success)
845 __sdla_read(dev, SDLA_502_RCV_BUF + SDLA_502_DATA_OFS, skb_put(skb,len), len);
846
847 SDLA_WINDOW(dev, SDLA_502_RCV_BUF);
848 cmd->opp_flag = 0;
849 break;
850
851 case SDLA_S508:
852 if (success)
853 {
854 /* is this buffer split off the end of the internal ring buffer */
855 split = addr + len > buf_top + 1 ? len - (buf_top - addr + 1) : 0;
856 len2 = len - split;
857
858 __sdla_read(dev, addr, skb_put(skb, len2), len2);
859 if (split)
860 __sdla_read(dev, buf_base, skb_put(skb, split), split);
861 }
862
863 /* increment the buffer we're looking at */
864 SDLA_WINDOW(dev, SDLA_508_RXBUF_INFO);
865 flp->buffer = (flp->buffer + 1) % pbufi->rse_num;
866 pbuf->opp_flag = 0;
867 break;
868 }
869
870 if (success)
871 {
Stephen Hemmingerac995332009-03-26 15:11:25 +0000872 dev->stats.rx_packets++;
Wang Chen8f15ea42008-11-12 23:38:36 -0800873 dlp = netdev_priv(master);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 (*dlp->receive)(skb, master);
875 }
876
877 spin_unlock_irqrestore(&sdla_lock, flags);
878}
879
Jeff Garzik28fc1f52007-10-29 05:46:16 -0400880static irqreturn_t sdla_isr(int dummy, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881{
882 struct net_device *dev;
883 struct frad_local *flp;
884 char byte;
885
886 dev = dev_id;
887
Jeff Garzikc31f28e2006-10-06 14:56:04 -0400888 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
890 if (!flp->initialized)
891 {
Joe Perches86fb0cc2011-06-26 19:01:31 +0000892 netdev_warn(dev, "irq %d for uninitialized device\n", dev->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 return IRQ_NONE;
894 }
895
896 byte = sdla_byte(dev, flp->type == SDLA_S508 ? SDLA_508_IRQ_INTERFACE : SDLA_502_IRQ_INTERFACE);
897 switch (byte)
898 {
899 case SDLA_INTR_RX:
900 sdla_receive(dev);
901 break;
902
903 /* the command will get an error return, which is processed above */
904 case SDLA_INTR_MODEM:
905 case SDLA_INTR_STATUS:
906 sdla_cmd(dev, SDLA_READ_DLC_STATUS, 0, 0, NULL, 0, NULL, NULL);
907 break;
908
909 case SDLA_INTR_TX:
910 case SDLA_INTR_COMPLETE:
911 case SDLA_INTR_TIMER:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000912 netdev_warn(dev, "invalid irq flag 0x%02X\n", byte);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 break;
914 }
915
916 /* the S502E requires a manual acknowledgement of the interrupt */
917 if (flp->type == SDLA_S502E)
918 {
919 flp->state &= ~SDLA_S502E_INTACK;
920 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
921 flp->state |= SDLA_S502E_INTACK;
922 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
923 }
924
925 /* this clears the byte, informing the Z80 we're done */
926 byte = 0;
927 sdla_write(dev, flp->type == SDLA_S508 ? SDLA_508_IRQ_INTERFACE : SDLA_502_IRQ_INTERFACE, &byte, sizeof(byte));
928 return IRQ_HANDLED;
929}
930
931static void sdla_poll(unsigned long device)
932{
933 struct net_device *dev;
934 struct frad_local *flp;
935
936 dev = (struct net_device *) device;
Wang Chen8f15ea42008-11-12 23:38:36 -0800937 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
939 if (sdla_byte(dev, SDLA_502_RCV_BUF))
940 sdla_receive(dev);
941
942 flp->timer.expires = 1;
943 add_timer(&flp->timer);
944}
945
946static int sdla_close(struct net_device *dev)
947{
948 struct frad_local *flp;
949 struct intr_info intr;
950 int len, i;
951 short dlcis[CONFIG_DLCI_MAX];
952
Wang Chen8f15ea42008-11-12 23:38:36 -0800953 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
955 len = 0;
956 for(i=0;i<CONFIG_DLCI_MAX;i++)
957 if (flp->dlci[i])
958 dlcis[len++] = abs(flp->dlci[i]);
959 len *= 2;
960
961 if (flp->config.station == FRAD_STATION_NODE)
962 {
963 for(i=0;i<CONFIG_DLCI_MAX;i++)
964 if (flp->dlci[i] > 0)
965 sdla_cmd(dev, SDLA_DEACTIVATE_DLCI, 0, 0, dlcis, len, NULL, NULL);
966 sdla_cmd(dev, SDLA_DELETE_DLCI, 0, 0, &flp->dlci[i], sizeof(flp->dlci[i]), NULL, NULL);
967 }
968
969 memset(&intr, 0, sizeof(intr));
970 /* let's start up the reception */
971 switch(flp->type)
972 {
973 case SDLA_S502A:
974 del_timer(&flp->timer);
975 break;
976
977 case SDLA_S502E:
978 sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(char) + sizeof(short), NULL, NULL);
979 flp->state &= ~SDLA_S502E_INTACK;
980 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
981 break;
982
983 case SDLA_S507:
984 break;
985
986 case SDLA_S508:
987 sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(struct intr_info), NULL, NULL);
988 flp->state &= ~SDLA_S508_INTEN;
989 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
990 break;
991 }
992
993 sdla_cmd(dev, SDLA_DISABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
994
995 netif_stop_queue(dev);
996
Eric Dumazet807540b2010-09-23 05:40:09 +0000997 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998}
999
1000struct conf_data {
1001 struct frad_conf config;
1002 short dlci[CONFIG_DLCI_MAX];
1003};
1004
1005static int sdla_open(struct net_device *dev)
1006{
1007 struct frad_local *flp;
1008 struct dlci_local *dlp;
1009 struct conf_data data;
1010 struct intr_info intr;
1011 int len, i;
1012 char byte;
1013
Wang Chen8f15ea42008-11-12 23:38:36 -08001014 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015
1016 if (!flp->initialized)
Eric Dumazet807540b2010-09-23 05:40:09 +00001017 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018
1019 if (!flp->configured)
Eric Dumazet807540b2010-09-23 05:40:09 +00001020 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021
1022 /* time to send in the configuration */
1023 len = 0;
1024 for(i=0;i<CONFIG_DLCI_MAX;i++)
1025 if (flp->dlci[i])
1026 data.dlci[len++] = abs(flp->dlci[i]);
1027 len *= 2;
1028
1029 memcpy(&data.config, &flp->config, sizeof(struct frad_conf));
1030 len += sizeof(struct frad_conf);
1031
1032 sdla_cmd(dev, SDLA_DISABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
1033 sdla_cmd(dev, SDLA_SET_DLCI_CONFIGURATION, 0, 0, &data, len, NULL, NULL);
1034
1035 if (flp->type == SDLA_S508)
1036 flp->buffer = 0;
1037
1038 sdla_cmd(dev, SDLA_ENABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
1039
1040 /* let's start up the reception */
1041 memset(&intr, 0, sizeof(intr));
1042 switch(flp->type)
1043 {
1044 case SDLA_S502A:
1045 flp->timer.expires = 1;
1046 add_timer(&flp->timer);
1047 break;
1048
1049 case SDLA_S502E:
1050 flp->state |= SDLA_S502E_ENABLE;
1051 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
1052 flp->state |= SDLA_S502E_INTACK;
1053 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
1054 byte = 0;
1055 sdla_write(dev, SDLA_502_IRQ_INTERFACE, &byte, sizeof(byte));
1056 intr.flags = SDLA_INTR_RX | SDLA_INTR_STATUS | SDLA_INTR_MODEM;
1057 sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(char) + sizeof(short), NULL, NULL);
1058 break;
1059
1060 case SDLA_S507:
1061 break;
1062
1063 case SDLA_S508:
1064 flp->state |= SDLA_S508_INTEN;
1065 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
1066 byte = 0;
1067 sdla_write(dev, SDLA_508_IRQ_INTERFACE, &byte, sizeof(byte));
1068 intr.flags = SDLA_INTR_RX | SDLA_INTR_STATUS | SDLA_INTR_MODEM;
1069 intr.irq = dev->irq;
1070 sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(struct intr_info), NULL, NULL);
1071 break;
1072 }
1073
1074 if (flp->config.station == FRAD_STATION_CPE)
1075 {
1076 byte = SDLA_ICS_STATUS_ENQ;
1077 sdla_cmd(dev, SDLA_ISSUE_IN_CHANNEL_SIGNAL, 0, 0, &byte, sizeof(byte), NULL, NULL);
1078 }
1079 else
1080 {
1081 sdla_cmd(dev, SDLA_ADD_DLCI, 0, 0, data.dlci, len - sizeof(struct frad_conf), NULL, NULL);
1082 for(i=0;i<CONFIG_DLCI_MAX;i++)
1083 if (flp->dlci[i] > 0)
1084 sdla_cmd(dev, SDLA_ACTIVATE_DLCI, 0, 0, &flp->dlci[i], 2*sizeof(flp->dlci[i]), NULL, NULL);
1085 }
1086
1087 /* configure any specific DLCI settings */
1088 for(i=0;i<CONFIG_DLCI_MAX;i++)
1089 if (flp->dlci[i])
1090 {
Wang Chen8f15ea42008-11-12 23:38:36 -08001091 dlp = netdev_priv(flp->master[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 if (dlp->configured)
1093 sdla_cmd(dev, SDLA_SET_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0, &dlp->config, sizeof(struct dlci_conf), NULL, NULL);
1094 }
1095
1096 netif_start_queue(dev);
1097
Eric Dumazet807540b2010-09-23 05:40:09 +00001098 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099}
1100
1101static int sdla_config(struct net_device *dev, struct frad_conf __user *conf, int get)
1102{
1103 struct frad_local *flp;
1104 struct conf_data data;
1105 int i;
1106 short size;
1107
1108 if (dev->type == 0xFFFF)
Eric Dumazet807540b2010-09-23 05:40:09 +00001109 return -EUNATCH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110
Wang Chen8f15ea42008-11-12 23:38:36 -08001111 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112
1113 if (!get)
1114 {
1115 if (netif_running(dev))
Eric Dumazet807540b2010-09-23 05:40:09 +00001116 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117
1118 if(copy_from_user(&data.config, conf, sizeof(struct frad_conf)))
1119 return -EFAULT;
1120
1121 if (data.config.station & ~FRAD_STATION_NODE)
Eric Dumazet807540b2010-09-23 05:40:09 +00001122 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123
1124 if (data.config.flags & ~FRAD_VALID_FLAGS)
Eric Dumazet807540b2010-09-23 05:40:09 +00001125 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126
1127 if ((data.config.kbaud < 0) ||
1128 ((data.config.kbaud > 128) && (flp->type != SDLA_S508)))
Eric Dumazet807540b2010-09-23 05:40:09 +00001129 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130
1131 if (data.config.clocking & ~(FRAD_CLOCK_INT | SDLA_S508_PORT_RS232))
Eric Dumazet807540b2010-09-23 05:40:09 +00001132 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133
1134 if ((data.config.mtu < 0) || (data.config.mtu > SDLA_MAX_MTU))
Eric Dumazet807540b2010-09-23 05:40:09 +00001135 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136
1137 if ((data.config.T391 < 5) || (data.config.T391 > 30))
Eric Dumazet807540b2010-09-23 05:40:09 +00001138 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139
1140 if ((data.config.T392 < 5) || (data.config.T392 > 30))
Eric Dumazet807540b2010-09-23 05:40:09 +00001141 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142
1143 if ((data.config.N391 < 1) || (data.config.N391 > 255))
Eric Dumazet807540b2010-09-23 05:40:09 +00001144 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145
1146 if ((data.config.N392 < 1) || (data.config.N392 > 10))
Eric Dumazet807540b2010-09-23 05:40:09 +00001147 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148
1149 if ((data.config.N393 < 1) || (data.config.N393 > 10))
Eric Dumazet807540b2010-09-23 05:40:09 +00001150 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151
1152 memcpy(&flp->config, &data.config, sizeof(struct frad_conf));
1153 flp->config.flags |= SDLA_DIRECT_RECV;
1154
1155 if (flp->type == SDLA_S508)
1156 flp->config.flags |= SDLA_TX70_RX30;
1157
1158 if (dev->mtu != flp->config.mtu)
1159 {
1160 /* this is required to change the MTU */
1161 dev->mtu = flp->config.mtu;
1162 for(i=0;i<CONFIG_DLCI_MAX;i++)
1163 if (flp->master[i])
1164 flp->master[i]->mtu = flp->config.mtu;
1165 }
1166
1167 flp->config.mtu += sizeof(struct frhdr);
1168
1169 /* off to the races! */
1170 if (!flp->configured)
1171 sdla_start(dev);
1172
1173 flp->configured = 1;
1174 }
1175 else
1176 {
1177 /* no sense reading if the CPU isn't started */
1178 if (netif_running(dev))
1179 {
1180 size = sizeof(data);
1181 if (sdla_cmd(dev, SDLA_READ_DLCI_CONFIGURATION, 0, 0, NULL, 0, &data, &size) != SDLA_RET_OK)
Eric Dumazet807540b2010-09-23 05:40:09 +00001182 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 }
1184 else
1185 if (flp->configured)
1186 memcpy(&data.config, &flp->config, sizeof(struct frad_conf));
1187 else
1188 memset(&data.config, 0, sizeof(struct frad_conf));
1189
1190 memcpy(&flp->config, &data.config, sizeof(struct frad_conf));
1191 data.config.flags &= FRAD_VALID_FLAGS;
1192 data.config.mtu -= data.config.mtu > sizeof(struct frhdr) ? sizeof(struct frhdr) : data.config.mtu;
1193 return copy_to_user(conf, &data.config, sizeof(struct frad_conf))?-EFAULT:0;
1194 }
1195
Eric Dumazet807540b2010-09-23 05:40:09 +00001196 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197}
1198
1199static int sdla_xfer(struct net_device *dev, struct sdla_mem __user *info, int read)
1200{
1201 struct sdla_mem mem;
1202 char *temp;
1203
1204 if(copy_from_user(&mem, info, sizeof(mem)))
1205 return -EFAULT;
1206
1207 if (read)
1208 {
Yoann Padioleaudd00cc42007-07-19 01:49:03 -07001209 temp = kzalloc(mem.len, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 if (!temp)
Eric Dumazet807540b2010-09-23 05:40:09 +00001211 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 sdla_read(dev, mem.addr, temp, mem.len);
1213 if(copy_to_user(mem.data, temp, mem.len))
1214 {
1215 kfree(temp);
1216 return -EFAULT;
1217 }
1218 kfree(temp);
1219 }
1220 else
1221 {
Julia Lawallc146fc92010-05-21 22:20:26 +00001222 temp = memdup_user(mem.data, mem.len);
1223 if (IS_ERR(temp))
1224 return PTR_ERR(temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 sdla_write(dev, mem.addr, temp, mem.len);
1226 kfree(temp);
1227 }
Eric Dumazet807540b2010-09-23 05:40:09 +00001228 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229}
1230
1231static int sdla_reconfig(struct net_device *dev)
1232{
1233 struct frad_local *flp;
1234 struct conf_data data;
1235 int i, len;
1236
Wang Chen8f15ea42008-11-12 23:38:36 -08001237 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238
1239 len = 0;
1240 for(i=0;i<CONFIG_DLCI_MAX;i++)
1241 if (flp->dlci[i])
1242 data.dlci[len++] = flp->dlci[i];
1243 len *= 2;
1244
1245 memcpy(&data, &flp->config, sizeof(struct frad_conf));
1246 len += sizeof(struct frad_conf);
1247
1248 sdla_cmd(dev, SDLA_DISABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
1249 sdla_cmd(dev, SDLA_SET_DLCI_CONFIGURATION, 0, 0, &data, len, NULL, NULL);
1250 sdla_cmd(dev, SDLA_ENABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
1251
Eric Dumazet807540b2010-09-23 05:40:09 +00001252 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253}
1254
1255static int sdla_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
1256{
1257 struct frad_local *flp;
1258
1259 if(!capable(CAP_NET_ADMIN))
1260 return -EPERM;
1261
Wang Chen8f15ea42008-11-12 23:38:36 -08001262 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
1264 if (!flp->initialized)
Eric Dumazet807540b2010-09-23 05:40:09 +00001265 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266
1267 switch (cmd)
1268 {
1269 case FRAD_GET_CONF:
1270 case FRAD_SET_CONF:
Eric Dumazet807540b2010-09-23 05:40:09 +00001271 return sdla_config(dev, ifr->ifr_data, cmd == FRAD_GET_CONF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272
1273 case SDLA_IDENTIFY:
1274 ifr->ifr_flags = flp->type;
1275 break;
1276
1277 case SDLA_CPUSPEED:
Eric Dumazet807540b2010-09-23 05:40:09 +00001278 return sdla_cpuspeed(dev, ifr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
1280/* ==========================================================
1281NOTE: This is rather a useless action right now, as the
1282 current driver does not support protocols other than
1283 FR. However, Sangoma has modules for a number of
1284 other protocols in the works.
1285============================================================*/
1286 case SDLA_PROTOCOL:
1287 if (flp->configured)
Eric Dumazet807540b2010-09-23 05:40:09 +00001288 return -EALREADY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289
1290 switch (ifr->ifr_flags)
1291 {
1292 case ARPHRD_FRAD:
1293 dev->type = ifr->ifr_flags;
1294 break;
1295 default:
Eric Dumazet807540b2010-09-23 05:40:09 +00001296 return -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 }
1298 break;
1299
1300 case SDLA_CLEARMEM:
1301 sdla_clear(dev);
1302 break;
1303
1304 case SDLA_WRITEMEM:
1305 case SDLA_READMEM:
1306 if(!capable(CAP_SYS_RAWIO))
1307 return -EPERM;
Eric Dumazet807540b2010-09-23 05:40:09 +00001308 return sdla_xfer(dev, ifr->ifr_data, cmd == SDLA_READMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
1310 case SDLA_START:
1311 sdla_start(dev);
1312 break;
1313
1314 case SDLA_STOP:
1315 sdla_stop(dev);
1316 break;
1317
1318 default:
Eric Dumazet807540b2010-09-23 05:40:09 +00001319 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 }
Eric Dumazet807540b2010-09-23 05:40:09 +00001321 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322}
1323
Adrian Bunk7665a082005-09-09 23:17:28 -07001324static int sdla_change_mtu(struct net_device *dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325{
1326 struct frad_local *flp;
1327
Wang Chen8f15ea42008-11-12 23:38:36 -08001328 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329
1330 if (netif_running(dev))
Eric Dumazet807540b2010-09-23 05:40:09 +00001331 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332
1333 /* for now, you can't change the MTU! */
Eric Dumazet807540b2010-09-23 05:40:09 +00001334 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335}
1336
Adrian Bunk7665a082005-09-09 23:17:28 -07001337static int sdla_set_config(struct net_device *dev, struct ifmap *map)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338{
1339 struct frad_local *flp;
1340 int i;
1341 char byte;
1342 unsigned base;
1343 int err = -EINVAL;
1344
Wang Chen8f15ea42008-11-12 23:38:36 -08001345 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346
1347 if (flp->initialized)
Eric Dumazet807540b2010-09-23 05:40:09 +00001348 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001350 for(i=0; i < ARRAY_SIZE(valid_port); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 if (valid_port[i] == map->base_addr)
1352 break;
1353
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001354 if (i == ARRAY_SIZE(valid_port))
Eric Dumazet807540b2010-09-23 05:40:09 +00001355 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356
1357 if (!request_region(map->base_addr, SDLA_IO_EXTENTS, dev->name)){
Joe Perches86fb0cc2011-06-26 19:01:31 +00001358 pr_warn("io-port 0x%04lx in use\n", dev->base_addr);
Eric Dumazet807540b2010-09-23 05:40:09 +00001359 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 }
1361 base = map->base_addr;
1362
1363 /* test for card types, S502A, S502E, S507, S508 */
1364 /* these tests shut down the card completely, so clear the state */
1365 flp->type = SDLA_UNKNOWN;
1366 flp->state = 0;
1367
1368 for(i=1;i<SDLA_IO_EXTENTS;i++)
1369 if (inb(base + i) != 0xFF)
1370 break;
1371
1372 if (i == SDLA_IO_EXTENTS) {
1373 outb(SDLA_HALT, base + SDLA_REG_Z80_CONTROL);
1374 if ((inb(base + SDLA_S502_STS) & 0x0F) == 0x08) {
1375 outb(SDLA_S502E_INTACK, base + SDLA_REG_CONTROL);
1376 if ((inb(base + SDLA_S502_STS) & 0x0F) == 0x0C) {
1377 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1378 flp->type = SDLA_S502E;
1379 goto got_type;
1380 }
1381 }
1382 }
1383
1384 for(byte=inb(base),i=0;i<SDLA_IO_EXTENTS;i++)
1385 if (inb(base + i) != byte)
1386 break;
1387
1388 if (i == SDLA_IO_EXTENTS) {
1389 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1390 if ((inb(base + SDLA_S502_STS) & 0x7E) == 0x30) {
1391 outb(SDLA_S507_ENABLE, base + SDLA_REG_CONTROL);
1392 if ((inb(base + SDLA_S502_STS) & 0x7E) == 0x32) {
1393 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1394 flp->type = SDLA_S507;
1395 goto got_type;
1396 }
1397 }
1398 }
1399
1400 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1401 if ((inb(base + SDLA_S508_STS) & 0x3F) == 0x00) {
1402 outb(SDLA_S508_INTEN, base + SDLA_REG_CONTROL);
1403 if ((inb(base + SDLA_S508_STS) & 0x3F) == 0x10) {
1404 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1405 flp->type = SDLA_S508;
1406 goto got_type;
1407 }
1408 }
1409
1410 outb(SDLA_S502A_HALT, base + SDLA_REG_CONTROL);
1411 if (inb(base + SDLA_S502_STS) == 0x40) {
1412 outb(SDLA_S502A_START, base + SDLA_REG_CONTROL);
1413 if (inb(base + SDLA_S502_STS) == 0x40) {
1414 outb(SDLA_S502A_INTEN, base + SDLA_REG_CONTROL);
1415 if (inb(base + SDLA_S502_STS) == 0x44) {
1416 outb(SDLA_S502A_START, base + SDLA_REG_CONTROL);
1417 flp->type = SDLA_S502A;
1418 goto got_type;
1419 }
1420 }
1421 }
1422
Joe Perches86fb0cc2011-06-26 19:01:31 +00001423 netdev_notice(dev, "Unknown card type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 err = -ENODEV;
1425 goto fail;
1426
1427got_type:
1428 switch(base) {
1429 case 0x270:
1430 case 0x280:
1431 case 0x380:
1432 case 0x390:
1433 if (flp->type != SDLA_S508 && flp->type != SDLA_S507)
1434 goto fail;
1435 }
1436
1437 switch (map->irq) {
1438 case 2:
1439 if (flp->type != SDLA_S502E)
1440 goto fail;
1441 break;
1442
1443 case 10:
1444 case 11:
1445 case 12:
1446 case 15:
1447 case 4:
1448 if (flp->type != SDLA_S508 && flp->type != SDLA_S507)
1449 goto fail;
1450 break;
1451 case 3:
1452 case 5:
1453 case 7:
1454 if (flp->type == SDLA_S502A)
1455 goto fail;
1456 break;
1457
1458 default:
1459 goto fail;
1460 }
1461
1462 err = -EAGAIN;
Joe Perchesa0607fd2009-11-18 23:29:17 -08001463 if (request_irq(dev->irq, sdla_isr, 0, dev->name, dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 goto fail;
1465
1466 if (flp->type == SDLA_S507) {
1467 switch(dev->irq) {
1468 case 3:
1469 flp->state = SDLA_S507_IRQ3;
1470 break;
1471 case 4:
1472 flp->state = SDLA_S507_IRQ4;
1473 break;
1474 case 5:
1475 flp->state = SDLA_S507_IRQ5;
1476 break;
1477 case 7:
1478 flp->state = SDLA_S507_IRQ7;
1479 break;
1480 case 10:
1481 flp->state = SDLA_S507_IRQ10;
1482 break;
1483 case 11:
1484 flp->state = SDLA_S507_IRQ11;
1485 break;
1486 case 12:
1487 flp->state = SDLA_S507_IRQ12;
1488 break;
1489 case 15:
1490 flp->state = SDLA_S507_IRQ15;
1491 break;
1492 }
1493 }
1494
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001495 for(i=0; i < ARRAY_SIZE(valid_mem); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 if (valid_mem[i] == map->mem_start)
1497 break;
1498
1499 err = -EINVAL;
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001500 if (i == ARRAY_SIZE(valid_mem))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 goto fail2;
1502
1503 if (flp->type == SDLA_S502A && (map->mem_start & 0xF000) >> 12 == 0x0E)
1504 goto fail2;
1505
1506 if (flp->type != SDLA_S507 && map->mem_start >> 16 == 0x0B)
1507 goto fail2;
1508
1509 if (flp->type == SDLA_S507 && map->mem_start >> 16 == 0x0D)
1510 goto fail2;
1511
1512 byte = flp->type != SDLA_S508 ? SDLA_8K_WINDOW : 0;
1513 byte |= (map->mem_start & 0xF000) >> (12 + (flp->type == SDLA_S508 ? 1 : 0));
1514 switch(flp->type) {
1515 case SDLA_S502A:
1516 case SDLA_S502E:
1517 switch (map->mem_start >> 16) {
1518 case 0x0A:
1519 byte |= SDLA_S502_SEG_A;
1520 break;
1521 case 0x0C:
1522 byte |= SDLA_S502_SEG_C;
1523 break;
1524 case 0x0D:
1525 byte |= SDLA_S502_SEG_D;
1526 break;
1527 case 0x0E:
1528 byte |= SDLA_S502_SEG_E;
1529 break;
1530 }
1531 break;
1532 case SDLA_S507:
1533 switch (map->mem_start >> 16) {
1534 case 0x0A:
1535 byte |= SDLA_S507_SEG_A;
1536 break;
1537 case 0x0B:
1538 byte |= SDLA_S507_SEG_B;
1539 break;
1540 case 0x0C:
1541 byte |= SDLA_S507_SEG_C;
1542 break;
1543 case 0x0E:
1544 byte |= SDLA_S507_SEG_E;
1545 break;
1546 }
1547 break;
1548 case SDLA_S508:
1549 switch (map->mem_start >> 16) {
1550 case 0x0A:
1551 byte |= SDLA_S508_SEG_A;
1552 break;
1553 case 0x0C:
1554 byte |= SDLA_S508_SEG_C;
1555 break;
1556 case 0x0D:
1557 byte |= SDLA_S508_SEG_D;
1558 break;
1559 case 0x0E:
1560 byte |= SDLA_S508_SEG_E;
1561 break;
1562 }
1563 break;
1564 }
1565
1566 /* set the memory bits, and enable access */
1567 outb(byte, base + SDLA_REG_PC_WINDOW);
1568
1569 switch(flp->type)
1570 {
1571 case SDLA_S502E:
1572 flp->state = SDLA_S502E_ENABLE;
1573 break;
1574 case SDLA_S507:
1575 flp->state |= SDLA_MEMEN;
1576 break;
1577 case SDLA_S508:
1578 flp->state = SDLA_MEMEN;
1579 break;
1580 }
1581 outb(flp->state, base + SDLA_REG_CONTROL);
1582
1583 dev->irq = map->irq;
1584 dev->base_addr = base;
1585 dev->mem_start = map->mem_start;
1586 dev->mem_end = dev->mem_start + 0x2000;
1587 flp->initialized = 1;
1588 return 0;
1589
1590fail2:
1591 free_irq(map->irq, dev);
1592fail:
1593 release_region(base, SDLA_IO_EXTENTS);
1594 return err;
1595}
1596
Stephen Hemmingerac995332009-03-26 15:11:25 +00001597static const struct net_device_ops sdla_netdev_ops = {
1598 .ndo_open = sdla_open,
1599 .ndo_stop = sdla_close,
1600 .ndo_do_ioctl = sdla_ioctl,
1601 .ndo_set_config = sdla_set_config,
1602 .ndo_start_xmit = sdla_transmit,
1603 .ndo_change_mtu = sdla_change_mtu,
1604};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605
1606static void setup_sdla(struct net_device *dev)
1607{
Wang Chen8f15ea42008-11-12 23:38:36 -08001608 struct frad_local *flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609
1610 netdev_boot_setup_check(dev);
1611
Stephen Hemmingerac995332009-03-26 15:11:25 +00001612 dev->netdev_ops = &sdla_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 dev->flags = 0;
1614 dev->type = 0xFFFF;
1615 dev->hard_header_len = 0;
1616 dev->addr_len = 0;
1617 dev->mtu = SDLA_MAX_MTU;
1618
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 flp->activate = sdla_activate;
1620 flp->deactivate = sdla_deactivate;
1621 flp->assoc = sdla_assoc;
1622 flp->deassoc = sdla_deassoc;
1623 flp->dlci_conf = sdla_dlci_conf;
1624
1625 init_timer(&flp->timer);
1626 flp->timer.expires = 1;
1627 flp->timer.data = (unsigned long) dev;
1628 flp->timer.function = sdla_poll;
1629}
1630
1631static struct net_device *sdla;
1632
1633static int __init init_sdla(void)
1634{
1635 int err;
1636
1637 printk("%s.\n", version);
1638
1639 sdla = alloc_netdev(sizeof(struct frad_local), "sdla0", setup_sdla);
1640 if (!sdla)
1641 return -ENOMEM;
1642
1643 err = register_netdev(sdla);
1644 if (err)
1645 free_netdev(sdla);
1646
1647 return err;
1648}
1649
1650static void __exit exit_sdla(void)
1651{
Wang Chen8f15ea42008-11-12 23:38:36 -08001652 struct frad_local *flp = netdev_priv(sdla);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
1654 unregister_netdev(sdla);
1655 if (flp->initialized) {
1656 free_irq(sdla->irq, sdla);
1657 release_region(sdla->base_addr, SDLA_IO_EXTENTS);
1658 }
1659 del_timer_sync(&flp->timer);
1660 free_netdev(sdla);
1661}
1662
1663MODULE_LICENSE("GPL");
1664
1665module_init(init_sdla);
1666module_exit(exit_sdla);