blob: de3bbf43fc5ac14f41ee9d76eeacb1f059eeaab3 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070057#include <asm/io.h>
58#include <asm/dma.h>
59#include <asm/uaccess.h>
60
61static const char* version = "SDLA driver v0.30, 12 Sep 1996, mike.mclagan@linux.org";
62
Randy Dunlap96ebb922006-06-25 05:48:37 -070063static unsigned int valid_port[] = { 0x250, 0x270, 0x280, 0x300, 0x350, 0x360, 0x380, 0x390};
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
Randy Dunlap96ebb922006-06-25 05:48:37 -070065static unsigned int valid_mem[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 0xA0000, 0xA2000, 0xA4000, 0xA6000, 0xA8000, 0xAA000, 0xAC000, 0xAE000,
67 0xB0000, 0xB2000, 0xB4000, 0xB6000, 0xB8000, 0xBA000, 0xBC000, 0xBE000,
68 0xC0000, 0xC2000, 0xC4000, 0xC6000, 0xC8000, 0xCA000, 0xCC000, 0xCE000,
69 0xD0000, 0xD2000, 0xD4000, 0xD6000, 0xD8000, 0xDA000, 0xDC000, 0xDE000,
70 0xE0000, 0xE2000, 0xE4000, 0xE6000, 0xE8000, 0xEA000, 0xEC000, 0xEE000};
71
72static DEFINE_SPINLOCK(sdla_lock);
73
74/*********************************************************
75 *
76 * these are the core routines that access the card itself
77 *
78 *********************************************************/
79
80#define SDLA_WINDOW(dev,addr) outb((((addr) >> 13) & 0x1F), (dev)->base_addr + SDLA_REG_Z80_WINDOW)
81
82static void __sdla_read(struct net_device *dev, int addr, void *buf, short len)
83{
84 char *temp;
85 const void *base;
86 int offset, bytes;
87
88 temp = buf;
89 while(len)
90 {
91 offset = addr & SDLA_ADDR_MASK;
92 bytes = offset + len > SDLA_WINDOW_SIZE ? SDLA_WINDOW_SIZE - offset : len;
93 base = (const void *) (dev->mem_start + offset);
94
95 SDLA_WINDOW(dev, addr);
96 memcpy(temp, base, bytes);
97
98 addr += bytes;
99 temp += bytes;
100 len -= bytes;
101 }
102}
103
104static void sdla_read(struct net_device *dev, int addr, void *buf, short len)
105{
106 unsigned long flags;
107 spin_lock_irqsave(&sdla_lock, flags);
108 __sdla_read(dev, addr, buf, len);
109 spin_unlock_irqrestore(&sdla_lock, flags);
110}
111
112static void __sdla_write(struct net_device *dev, int addr,
113 const void *buf, short len)
114{
115 const char *temp;
116 void *base;
117 int offset, bytes;
118
119 temp = buf;
120 while(len)
121 {
122 offset = addr & SDLA_ADDR_MASK;
123 bytes = offset + len > SDLA_WINDOW_SIZE ? SDLA_WINDOW_SIZE - offset : len;
124 base = (void *) (dev->mem_start + offset);
125
126 SDLA_WINDOW(dev, addr);
127 memcpy(base, temp, bytes);
128
129 addr += bytes;
130 temp += bytes;
131 len -= bytes;
132 }
133}
134
135static void sdla_write(struct net_device *dev, int addr,
136 const void *buf, short len)
137{
138 unsigned long flags;
139
140 spin_lock_irqsave(&sdla_lock, flags);
141 __sdla_write(dev, addr, buf, len);
142 spin_unlock_irqrestore(&sdla_lock, flags);
143}
144
145
146static void sdla_clear(struct net_device *dev)
147{
148 unsigned long flags;
149 char *base;
150 int len, addr, bytes;
151
152 len = 65536;
153 addr = 0;
154 bytes = SDLA_WINDOW_SIZE;
155 base = (void *) dev->mem_start;
156
157 spin_lock_irqsave(&sdla_lock, flags);
158 while(len)
159 {
160 SDLA_WINDOW(dev, addr);
161 memset(base, 0, bytes);
162
163 addr += bytes;
164 len -= bytes;
165 }
166 spin_unlock_irqrestore(&sdla_lock, flags);
167
168}
169
170static char sdla_byte(struct net_device *dev, int addr)
171{
172 unsigned long flags;
173 char byte, *temp;
174
175 temp = (void *) (dev->mem_start + (addr & SDLA_ADDR_MASK));
176
177 spin_lock_irqsave(&sdla_lock, flags);
178 SDLA_WINDOW(dev, addr);
179 byte = *temp;
180 spin_unlock_irqrestore(&sdla_lock, flags);
181
Eric Dumazet807540b2010-09-23 05:40:09 +0000182 return byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183}
184
Adrian Bunk7665a082005-09-09 23:17:28 -0700185static void sdla_stop(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186{
187 struct frad_local *flp;
188
Wang Chen8f15ea42008-11-12 23:38:36 -0800189 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 switch(flp->type)
191 {
192 case SDLA_S502A:
193 outb(SDLA_S502A_HALT, dev->base_addr + SDLA_REG_CONTROL);
194 flp->state = SDLA_HALT;
195 break;
196 case SDLA_S502E:
197 outb(SDLA_HALT, dev->base_addr + SDLA_REG_Z80_CONTROL);
198 outb(SDLA_S502E_ENABLE, dev->base_addr + SDLA_REG_CONTROL);
199 flp->state = SDLA_S502E_ENABLE;
200 break;
201 case SDLA_S507:
202 flp->state &= ~SDLA_CPUEN;
203 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
204 break;
205 case SDLA_S508:
206 flp->state &= ~SDLA_CPUEN;
207 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
208 break;
209 }
210}
211
Adrian Bunk7665a082005-09-09 23:17:28 -0700212static void sdla_start(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213{
214 struct frad_local *flp;
215
Wang Chen8f15ea42008-11-12 23:38:36 -0800216 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 switch(flp->type)
218 {
219 case SDLA_S502A:
220 outb(SDLA_S502A_NMI, dev->base_addr + SDLA_REG_CONTROL);
221 outb(SDLA_S502A_START, dev->base_addr + SDLA_REG_CONTROL);
222 flp->state = SDLA_S502A_START;
223 break;
224 case SDLA_S502E:
225 outb(SDLA_S502E_CPUEN, dev->base_addr + SDLA_REG_Z80_CONTROL);
226 outb(0x00, dev->base_addr + SDLA_REG_CONTROL);
227 flp->state = 0;
228 break;
229 case SDLA_S507:
230 flp->state |= SDLA_CPUEN;
231 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
232 break;
233 case SDLA_S508:
234 flp->state |= SDLA_CPUEN;
235 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
236 break;
237 }
238}
239
240/****************************************************
241 *
242 * this is used for the S502A/E cards to determine
243 * the speed of the onboard CPU. Calibration is
244 * necessary for the Frame Relay code uploaded
245 * later. Incorrect results cause timing problems
246 * with link checks & status messages
247 *
248 ***************************************************/
249
Adrian Bunk7665a082005-09-09 23:17:28 -0700250static int sdla_z80_poll(struct net_device *dev, int z80_addr, int jiffs, char resp1, char resp2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251{
252 unsigned long start, done, now;
253 char resp, *temp;
254
255 start = now = jiffies;
256 done = jiffies + jiffs;
257
258 temp = (void *)dev->mem_start;
259 temp += z80_addr & SDLA_ADDR_MASK;
260
261 resp = ~resp1;
262 while (time_before(jiffies, done) && (resp != resp1) && (!resp2 || (resp != resp2)))
263 {
264 if (jiffies != now)
265 {
266 SDLA_WINDOW(dev, z80_addr);
267 now = jiffies;
268 resp = *temp;
269 }
270 }
Eric Dumazet807540b2010-09-23 05:40:09 +0000271 return time_before(jiffies, done) ? jiffies - start : -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272}
273
274/* constants for Z80 CPU speed */
275#define Z80_READY '1' /* Z80 is ready to begin */
276#define LOADER_READY '2' /* driver is ready to begin */
277#define Z80_SCC_OK '3' /* SCC is on board */
278#define Z80_SCC_BAD '4' /* SCC was not found */
279
280static int sdla_cpuspeed(struct net_device *dev, struct ifreq *ifr)
281{
282 int jiffs;
283 char data;
284
285 sdla_start(dev);
286 if (sdla_z80_poll(dev, 0, 3*HZ, Z80_READY, 0) < 0)
Eric Dumazet807540b2010-09-23 05:40:09 +0000287 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
289 data = LOADER_READY;
290 sdla_write(dev, 0, &data, 1);
291
292 if ((jiffs = sdla_z80_poll(dev, 0, 8*HZ, Z80_SCC_OK, Z80_SCC_BAD)) < 0)
Eric Dumazet807540b2010-09-23 05:40:09 +0000293 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
295 sdla_stop(dev);
296 sdla_read(dev, 0, &data, 1);
297
298 if (data == Z80_SCC_BAD)
299 {
300 printk("%s: SCC bad\n", dev->name);
Eric Dumazet807540b2010-09-23 05:40:09 +0000301 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 }
303
304 if (data != Z80_SCC_OK)
Eric Dumazet807540b2010-09-23 05:40:09 +0000305 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
307 if (jiffs < 165)
308 ifr->ifr_mtu = SDLA_CPU_16M;
309 else if (jiffs < 220)
310 ifr->ifr_mtu = SDLA_CPU_10M;
311 else if (jiffs < 258)
312 ifr->ifr_mtu = SDLA_CPU_8M;
313 else if (jiffs < 357)
314 ifr->ifr_mtu = SDLA_CPU_7M;
315 else if (jiffs < 467)
316 ifr->ifr_mtu = SDLA_CPU_5M;
317 else
318 ifr->ifr_mtu = SDLA_CPU_3M;
319
Eric Dumazet807540b2010-09-23 05:40:09 +0000320 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321}
322
323/************************************************
324 *
325 * Direct interaction with the Frame Relay code
326 * starts here.
327 *
328 ************************************************/
329
330struct _dlci_stat
331{
Jan Blunck6a878182006-01-08 01:05:07 -0800332 short dlci;
333 char flags;
Eric Dumazetba2d3582010-06-02 18:10:09 +0000334} __packed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336struct _frad_stat
337{
338 char flags;
339 struct _dlci_stat dlcis[SDLA_MAX_DLCI];
340};
341
342static void sdla_errors(struct net_device *dev, int cmd, int dlci, int ret, int len, void *data)
343{
344 struct _dlci_stat *pstatus;
345 short *pdlci;
346 int i;
347 char *state, line[30];
348
349 switch (ret)
350 {
351 case SDLA_RET_MODEM:
352 state = data;
353 if (*state & SDLA_MODEM_DCD_LOW)
Joe Perches86fb0cc2011-06-26 19:01:31 +0000354 netdev_info(dev, "Modem DCD unexpectedly low!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 if (*state & SDLA_MODEM_CTS_LOW)
Joe Perches86fb0cc2011-06-26 19:01:31 +0000356 netdev_info(dev, "Modem CTS unexpectedly low!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 /* I should probably do something about this! */
358 break;
359
360 case SDLA_RET_CHANNEL_OFF:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000361 netdev_info(dev, "Channel became inoperative!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 /* same here */
363 break;
364
365 case SDLA_RET_CHANNEL_ON:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000366 netdev_info(dev, "Channel became operative!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 /* same here */
368 break;
369
370 case SDLA_RET_DLCI_STATUS:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000371 netdev_info(dev, "Status change reported by Access Node\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 len /= sizeof(struct _dlci_stat);
373 for(pstatus = data, i=0;i < len;i++,pstatus++)
374 {
375 if (pstatus->flags & SDLA_DLCI_NEW)
376 state = "new";
377 else if (pstatus->flags & SDLA_DLCI_DELETED)
378 state = "deleted";
379 else if (pstatus->flags & SDLA_DLCI_ACTIVE)
380 state = "active";
381 else
382 {
383 sprintf(line, "unknown status: %02X", pstatus->flags);
384 state = line;
385 }
Joe Perches86fb0cc2011-06-26 19:01:31 +0000386 netdev_info(dev, "DLCI %i: %s\n",
387 pstatus->dlci, state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 /* same here */
389 }
390 break;
391
392 case SDLA_RET_DLCI_UNKNOWN:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000393 netdev_info(dev, "Received unknown DLCIs:");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 len /= sizeof(short);
395 for(pdlci = data,i=0;i < len;i++,pdlci++)
Joe Perches86fb0cc2011-06-26 19:01:31 +0000396 pr_cont(" %i", *pdlci);
397 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 break;
399
400 case SDLA_RET_TIMEOUT:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000401 netdev_err(dev, "Command timed out!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 break;
403
404 case SDLA_RET_BUF_OVERSIZE:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000405 netdev_info(dev, "Bc/CIR overflow, acceptable size is %i\n",
406 len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 break;
408
409 case SDLA_RET_BUF_TOO_BIG:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000410 netdev_info(dev, "Buffer size over specified max of %i\n",
411 len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 break;
413
414 case SDLA_RET_CHANNEL_INACTIVE:
415 case SDLA_RET_DLCI_INACTIVE:
416 case SDLA_RET_CIR_OVERFLOW:
417 case SDLA_RET_NO_BUFS:
418 if (cmd == SDLA_INFORMATION_WRITE)
419 break;
420
421 default:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000422 netdev_dbg(dev, "Cmd 0x%02X generated return code 0x%02X\n",
423 cmd, ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 /* Further processing could be done here */
425 break;
426 }
427}
428
429static int sdla_cmd(struct net_device *dev, int cmd, short dlci, short flags,
430 void *inbuf, short inlen, void *outbuf, short *outlen)
431{
432 static struct _frad_stat status;
433 struct frad_local *flp;
434 struct sdla_cmd *cmd_buf;
435 unsigned long pflags;
436 unsigned long jiffs;
437 int ret, waiting, len;
438 long window;
439
Wang Chen8f15ea42008-11-12 23:38:36 -0800440 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 window = flp->type == SDLA_S508 ? SDLA_508_CMD_BUF : SDLA_502_CMD_BUF;
442 cmd_buf = (struct sdla_cmd *)(dev->mem_start + (window & SDLA_ADDR_MASK));
443 ret = 0;
444 len = 0;
445 jiffs = jiffies + HZ; /* 1 second is plenty */
446
447 spin_lock_irqsave(&sdla_lock, pflags);
448 SDLA_WINDOW(dev, window);
449 cmd_buf->cmd = cmd;
450 cmd_buf->dlci = dlci;
451 cmd_buf->flags = flags;
452
453 if (inbuf)
454 memcpy(cmd_buf->data, inbuf, inlen);
455
456 cmd_buf->length = inlen;
457
458 cmd_buf->opp_flag = 1;
459 spin_unlock_irqrestore(&sdla_lock, pflags);
460
461 waiting = 1;
462 len = 0;
463 while (waiting && time_before_eq(jiffies, jiffs))
464 {
465 if (waiting++ % 3)
466 {
467 spin_lock_irqsave(&sdla_lock, pflags);
468 SDLA_WINDOW(dev, window);
469 waiting = ((volatile int)(cmd_buf->opp_flag));
470 spin_unlock_irqrestore(&sdla_lock, pflags);
471 }
472 }
473
474 if (!waiting)
475 {
476
477 spin_lock_irqsave(&sdla_lock, pflags);
478 SDLA_WINDOW(dev, window);
479 ret = cmd_buf->retval;
480 len = cmd_buf->length;
481 if (outbuf && outlen)
482 {
483 *outlen = *outlen >= len ? len : *outlen;
484
485 if (*outlen)
486 memcpy(outbuf, cmd_buf->data, *outlen);
487 }
488
489 /* This is a local copy that's used for error handling */
490 if (ret)
491 memcpy(&status, cmd_buf->data, len > sizeof(status) ? sizeof(status) : len);
492
493 spin_unlock_irqrestore(&sdla_lock, pflags);
494 }
495 else
496 ret = SDLA_RET_TIMEOUT;
497
498 if (ret != SDLA_RET_OK)
499 sdla_errors(dev, cmd, dlci, ret, len, &status);
500
Eric Dumazet807540b2010-09-23 05:40:09 +0000501 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502}
503
504/***********************************************
505 *
506 * these functions are called by the DLCI driver
507 *
508 ***********************************************/
509
510static int sdla_reconfig(struct net_device *dev);
511
Adrian Bunk7665a082005-09-09 23:17:28 -0700512static int sdla_activate(struct net_device *slave, struct net_device *master)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513{
514 struct frad_local *flp;
515 int i;
516
Wang Chen8f15ea42008-11-12 23:38:36 -0800517 flp = netdev_priv(slave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
519 for(i=0;i<CONFIG_DLCI_MAX;i++)
520 if (flp->master[i] == master)
521 break;
522
523 if (i == CONFIG_DLCI_MAX)
Eric Dumazet807540b2010-09-23 05:40:09 +0000524 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
526 flp->dlci[i] = abs(flp->dlci[i]);
527
528 if (netif_running(slave) && (flp->config.station == FRAD_STATION_NODE))
529 sdla_cmd(slave, SDLA_ACTIVATE_DLCI, 0, 0, &flp->dlci[i], sizeof(short), NULL, NULL);
530
Eric Dumazet807540b2010-09-23 05:40:09 +0000531 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532}
533
Adrian Bunk7665a082005-09-09 23:17:28 -0700534static int sdla_deactivate(struct net_device *slave, struct net_device *master)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{
536 struct frad_local *flp;
537 int i;
538
Wang Chen8f15ea42008-11-12 23:38:36 -0800539 flp = netdev_priv(slave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
541 for(i=0;i<CONFIG_DLCI_MAX;i++)
542 if (flp->master[i] == master)
543 break;
544
545 if (i == CONFIG_DLCI_MAX)
Eric Dumazet807540b2010-09-23 05:40:09 +0000546 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
548 flp->dlci[i] = -abs(flp->dlci[i]);
549
550 if (netif_running(slave) && (flp->config.station == FRAD_STATION_NODE))
551 sdla_cmd(slave, SDLA_DEACTIVATE_DLCI, 0, 0, &flp->dlci[i], sizeof(short), NULL, NULL);
552
Eric Dumazet807540b2010-09-23 05:40:09 +0000553 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554}
555
Adrian Bunk7665a082005-09-09 23:17:28 -0700556static int sdla_assoc(struct net_device *slave, struct net_device *master)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557{
558 struct frad_local *flp;
559 int i;
560
561 if (master->type != ARPHRD_DLCI)
Eric Dumazet807540b2010-09-23 05:40:09 +0000562 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
Wang Chen8f15ea42008-11-12 23:38:36 -0800564 flp = netdev_priv(slave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
566 for(i=0;i<CONFIG_DLCI_MAX;i++)
567 {
568 if (!flp->master[i])
569 break;
570 if (abs(flp->dlci[i]) == *(short *)(master->dev_addr))
Eric Dumazet807540b2010-09-23 05:40:09 +0000571 return -EADDRINUSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 }
573
574 if (i == CONFIG_DLCI_MAX)
Eric Dumazet807540b2010-09-23 05:40:09 +0000575 return -EMLINK; /* #### Alan: Comments on this ?? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
577
578 flp->master[i] = master;
579 flp->dlci[i] = -*(short *)(master->dev_addr);
580 master->mtu = slave->mtu;
581
582 if (netif_running(slave)) {
583 if (flp->config.station == FRAD_STATION_CPE)
584 sdla_reconfig(slave);
585 else
586 sdla_cmd(slave, SDLA_ADD_DLCI, 0, 0, master->dev_addr, sizeof(short), NULL, NULL);
587 }
588
Eric Dumazet807540b2010-09-23 05:40:09 +0000589 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590}
591
Adrian Bunk7665a082005-09-09 23:17:28 -0700592static int sdla_deassoc(struct net_device *slave, struct net_device *master)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593{
594 struct frad_local *flp;
595 int i;
596
Wang Chen8f15ea42008-11-12 23:38:36 -0800597 flp = netdev_priv(slave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
599 for(i=0;i<CONFIG_DLCI_MAX;i++)
600 if (flp->master[i] == master)
601 break;
602
603 if (i == CONFIG_DLCI_MAX)
Eric Dumazet807540b2010-09-23 05:40:09 +0000604 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
606 flp->master[i] = NULL;
607 flp->dlci[i] = 0;
608
609
610 if (netif_running(slave)) {
611 if (flp->config.station == FRAD_STATION_CPE)
612 sdla_reconfig(slave);
613 else
614 sdla_cmd(slave, SDLA_DELETE_DLCI, 0, 0, master->dev_addr, sizeof(short), NULL, NULL);
615 }
616
Eric Dumazet807540b2010-09-23 05:40:09 +0000617 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618}
619
Adrian Bunk7665a082005-09-09 23:17:28 -0700620static int sdla_dlci_conf(struct net_device *slave, struct net_device *master, int get)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621{
622 struct frad_local *flp;
623 struct dlci_local *dlp;
624 int i;
625 short len, ret;
626
Wang Chen8f15ea42008-11-12 23:38:36 -0800627 flp = netdev_priv(slave);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
629 for(i=0;i<CONFIG_DLCI_MAX;i++)
630 if (flp->master[i] == master)
631 break;
632
633 if (i == CONFIG_DLCI_MAX)
Eric Dumazet807540b2010-09-23 05:40:09 +0000634 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
Wang Chen8f15ea42008-11-12 23:38:36 -0800636 dlp = netdev_priv(master);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
638 ret = SDLA_RET_OK;
639 len = sizeof(struct dlci_conf);
640 if (netif_running(slave)) {
641 if (get)
642 ret = sdla_cmd(slave, SDLA_READ_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0,
643 NULL, 0, &dlp->config, &len);
644 else
645 ret = sdla_cmd(slave, SDLA_SET_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0,
646 &dlp->config, sizeof(struct dlci_conf) - 4 * sizeof(short), NULL, NULL);
647 }
648
Eric Dumazet807540b2010-09-23 05:40:09 +0000649 return ret == SDLA_RET_OK ? 0 : -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650}
651
652/**************************
653 *
654 * now for the Linux driver
655 *
656 **************************/
657
658/* NOTE: the DLCI driver deals with freeing the SKB!! */
Stephen Hemmingerd71a6742009-08-31 19:50:47 +0000659static netdev_tx_t sdla_transmit(struct sk_buff *skb,
Stephen Hemminger38482422009-09-04 05:33:46 +0000660 struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661{
662 struct frad_local *flp;
663 int ret, addr, accept, i;
664 short size;
665 unsigned long flags;
666 struct buf_entry *pbuf;
667
Wang Chen8f15ea42008-11-12 23:38:36 -0800668 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 ret = 0;
670 accept = 1;
671
672 netif_stop_queue(dev);
673
674 /*
675 * stupid GateD insists on setting up the multicast router thru us
676 * and we're ill equipped to handle a non Frame Relay packet at this
677 * time!
678 */
679
680 accept = 1;
681 switch (dev->type)
682 {
683 case ARPHRD_FRAD:
684 if (skb->dev->type != ARPHRD_DLCI)
685 {
Joe Perches86fb0cc2011-06-26 19:01:31 +0000686 netdev_warn(dev, "Non DLCI device, type %i, tried to send on FRAD module\n",
687 skb->dev->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 accept = 0;
689 }
690 break;
691 default:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000692 netdev_warn(dev, "unknown firmware type 0x%04X\n",
693 dev->type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 accept = 0;
695 break;
696 }
697 if (accept)
698 {
699 /* this is frame specific, but till there's a PPP module, it's the default */
700 switch (flp->type)
701 {
702 case SDLA_S502A:
703 case SDLA_S502E:
704 ret = sdla_cmd(dev, SDLA_INFORMATION_WRITE, *(short *)(skb->dev->dev_addr), 0, skb->data, skb->len, NULL, NULL);
705 break;
706 case SDLA_S508:
707 size = sizeof(addr);
708 ret = sdla_cmd(dev, SDLA_INFORMATION_WRITE, *(short *)(skb->dev->dev_addr), 0, NULL, skb->len, &addr, &size);
709 if (ret == SDLA_RET_OK)
710 {
711
712 spin_lock_irqsave(&sdla_lock, flags);
713 SDLA_WINDOW(dev, addr);
714 pbuf = (void *)(((int) dev->mem_start) + (addr & SDLA_ADDR_MASK));
715 __sdla_write(dev, pbuf->buf_addr, skb->data, skb->len);
716 SDLA_WINDOW(dev, addr);
717 pbuf->opp_flag = 1;
718 spin_unlock_irqrestore(&sdla_lock, flags);
719 }
720 break;
721 }
Stephen Hemminger38482422009-09-04 05:33:46 +0000722
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 switch (ret)
724 {
725 case SDLA_RET_OK:
Stephen Hemmingerac995332009-03-26 15:11:25 +0000726 dev->stats.tx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 break;
728
729 case SDLA_RET_CIR_OVERFLOW:
730 case SDLA_RET_BUF_OVERSIZE:
731 case SDLA_RET_NO_BUFS:
Stephen Hemmingerac995332009-03-26 15:11:25 +0000732 dev->stats.tx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 break;
734
735 default:
Stephen Hemmingerac995332009-03-26 15:11:25 +0000736 dev->stats.tx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 break;
738 }
739 }
740 netif_wake_queue(dev);
741 for(i=0;i<CONFIG_DLCI_MAX;i++)
742 {
743 if(flp->master[i]!=NULL)
744 netif_wake_queue(flp->master[i]);
745 }
Stephen Hemminger38482422009-09-04 05:33:46 +0000746
747 dev_kfree_skb(skb);
Stephen Hemmingerd71a6742009-08-31 19:50:47 +0000748 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749}
750
751static void sdla_receive(struct net_device *dev)
752{
753 struct net_device *master;
754 struct frad_local *flp;
755 struct dlci_local *dlp;
756 struct sk_buff *skb;
757
758 struct sdla_cmd *cmd;
759 struct buf_info *pbufi;
760 struct buf_entry *pbuf;
761
762 unsigned long flags;
763 int i=0, received, success, addr, buf_base, buf_top;
764 short dlci, len, len2, split;
765
Wang Chen8f15ea42008-11-12 23:38:36 -0800766 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 success = 1;
768 received = addr = buf_top = buf_base = 0;
769 len = dlci = 0;
770 skb = NULL;
771 master = NULL;
772 cmd = NULL;
773 pbufi = NULL;
774 pbuf = NULL;
775
776 spin_lock_irqsave(&sdla_lock, flags);
777
778 switch (flp->type)
779 {
780 case SDLA_S502A:
781 case SDLA_S502E:
782 cmd = (void *) (dev->mem_start + (SDLA_502_RCV_BUF & SDLA_ADDR_MASK));
783 SDLA_WINDOW(dev, SDLA_502_RCV_BUF);
784 success = cmd->opp_flag;
785 if (!success)
786 break;
787
788 dlci = cmd->dlci;
789 len = cmd->length;
790 break;
791
792 case SDLA_S508:
793 pbufi = (void *) (dev->mem_start + (SDLA_508_RXBUF_INFO & SDLA_ADDR_MASK));
794 SDLA_WINDOW(dev, SDLA_508_RXBUF_INFO);
795 pbuf = (void *) (dev->mem_start + ((pbufi->rse_base + flp->buffer * sizeof(struct buf_entry)) & SDLA_ADDR_MASK));
796 success = pbuf->opp_flag;
797 if (!success)
798 break;
799
800 buf_top = pbufi->buf_top;
801 buf_base = pbufi->buf_base;
802 dlci = pbuf->dlci;
803 len = pbuf->length;
804 addr = pbuf->buf_addr;
805 break;
806 }
807
808 /* common code, find the DLCI and get the SKB */
809 if (success)
810 {
811 for (i=0;i<CONFIG_DLCI_MAX;i++)
812 if (flp->dlci[i] == dlci)
813 break;
814
815 if (i == CONFIG_DLCI_MAX)
816 {
Joe Perches86fb0cc2011-06-26 19:01:31 +0000817 netdev_notice(dev, "Received packet from invalid DLCI %i, ignoring\n",
818 dlci);
Stephen Hemmingerac995332009-03-26 15:11:25 +0000819 dev->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 success = 0;
821 }
822 }
823
824 if (success)
825 {
826 master = flp->master[i];
827 skb = dev_alloc_skb(len + sizeof(struct frhdr));
828 if (skb == NULL)
829 {
Joe Perches86fb0cc2011-06-26 19:01:31 +0000830 netdev_notice(dev, "Memory squeeze, dropping packet\n");
Stephen Hemmingerac995332009-03-26 15:11:25 +0000831 dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 success = 0;
833 }
834 else
835 skb_reserve(skb, sizeof(struct frhdr));
836 }
837
838 /* pick up the data */
839 switch (flp->type)
840 {
841 case SDLA_S502A:
842 case SDLA_S502E:
843 if (success)
844 __sdla_read(dev, SDLA_502_RCV_BUF + SDLA_502_DATA_OFS, skb_put(skb,len), len);
845
846 SDLA_WINDOW(dev, SDLA_502_RCV_BUF);
847 cmd->opp_flag = 0;
848 break;
849
850 case SDLA_S508:
851 if (success)
852 {
853 /* is this buffer split off the end of the internal ring buffer */
854 split = addr + len > buf_top + 1 ? len - (buf_top - addr + 1) : 0;
855 len2 = len - split;
856
857 __sdla_read(dev, addr, skb_put(skb, len2), len2);
858 if (split)
859 __sdla_read(dev, buf_base, skb_put(skb, split), split);
860 }
861
862 /* increment the buffer we're looking at */
863 SDLA_WINDOW(dev, SDLA_508_RXBUF_INFO);
864 flp->buffer = (flp->buffer + 1) % pbufi->rse_num;
865 pbuf->opp_flag = 0;
866 break;
867 }
868
869 if (success)
870 {
Stephen Hemmingerac995332009-03-26 15:11:25 +0000871 dev->stats.rx_packets++;
Wang Chen8f15ea42008-11-12 23:38:36 -0800872 dlp = netdev_priv(master);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 (*dlp->receive)(skb, master);
874 }
875
876 spin_unlock_irqrestore(&sdla_lock, flags);
877}
878
Jeff Garzik28fc1f52007-10-29 05:46:16 -0400879static irqreturn_t sdla_isr(int dummy, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880{
881 struct net_device *dev;
882 struct frad_local *flp;
883 char byte;
884
885 dev = dev_id;
886
Jeff Garzikc31f28e2006-10-06 14:56:04 -0400887 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
889 if (!flp->initialized)
890 {
Joe Perches86fb0cc2011-06-26 19:01:31 +0000891 netdev_warn(dev, "irq %d for uninitialized device\n", dev->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 return IRQ_NONE;
893 }
894
895 byte = sdla_byte(dev, flp->type == SDLA_S508 ? SDLA_508_IRQ_INTERFACE : SDLA_502_IRQ_INTERFACE);
896 switch (byte)
897 {
898 case SDLA_INTR_RX:
899 sdla_receive(dev);
900 break;
901
902 /* the command will get an error return, which is processed above */
903 case SDLA_INTR_MODEM:
904 case SDLA_INTR_STATUS:
905 sdla_cmd(dev, SDLA_READ_DLC_STATUS, 0, 0, NULL, 0, NULL, NULL);
906 break;
907
908 case SDLA_INTR_TX:
909 case SDLA_INTR_COMPLETE:
910 case SDLA_INTR_TIMER:
Joe Perches86fb0cc2011-06-26 19:01:31 +0000911 netdev_warn(dev, "invalid irq flag 0x%02X\n", byte);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 break;
913 }
914
915 /* the S502E requires a manual acknowledgement of the interrupt */
916 if (flp->type == SDLA_S502E)
917 {
918 flp->state &= ~SDLA_S502E_INTACK;
919 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
920 flp->state |= SDLA_S502E_INTACK;
921 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
922 }
923
924 /* this clears the byte, informing the Z80 we're done */
925 byte = 0;
926 sdla_write(dev, flp->type == SDLA_S508 ? SDLA_508_IRQ_INTERFACE : SDLA_502_IRQ_INTERFACE, &byte, sizeof(byte));
927 return IRQ_HANDLED;
928}
929
930static void sdla_poll(unsigned long device)
931{
932 struct net_device *dev;
933 struct frad_local *flp;
934
935 dev = (struct net_device *) device;
Wang Chen8f15ea42008-11-12 23:38:36 -0800936 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 if (sdla_byte(dev, SDLA_502_RCV_BUF))
939 sdla_receive(dev);
940
941 flp->timer.expires = 1;
942 add_timer(&flp->timer);
943}
944
945static int sdla_close(struct net_device *dev)
946{
947 struct frad_local *flp;
948 struct intr_info intr;
949 int len, i;
950 short dlcis[CONFIG_DLCI_MAX];
951
Wang Chen8f15ea42008-11-12 23:38:36 -0800952 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953
954 len = 0;
955 for(i=0;i<CONFIG_DLCI_MAX;i++)
956 if (flp->dlci[i])
957 dlcis[len++] = abs(flp->dlci[i]);
958 len *= 2;
959
960 if (flp->config.station == FRAD_STATION_NODE)
961 {
962 for(i=0;i<CONFIG_DLCI_MAX;i++)
963 if (flp->dlci[i] > 0)
964 sdla_cmd(dev, SDLA_DEACTIVATE_DLCI, 0, 0, dlcis, len, NULL, NULL);
965 sdla_cmd(dev, SDLA_DELETE_DLCI, 0, 0, &flp->dlci[i], sizeof(flp->dlci[i]), NULL, NULL);
966 }
967
968 memset(&intr, 0, sizeof(intr));
969 /* let's start up the reception */
970 switch(flp->type)
971 {
972 case SDLA_S502A:
973 del_timer(&flp->timer);
974 break;
975
976 case SDLA_S502E:
977 sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(char) + sizeof(short), NULL, NULL);
978 flp->state &= ~SDLA_S502E_INTACK;
979 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
980 break;
981
982 case SDLA_S507:
983 break;
984
985 case SDLA_S508:
986 sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(struct intr_info), NULL, NULL);
987 flp->state &= ~SDLA_S508_INTEN;
988 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
989 break;
990 }
991
992 sdla_cmd(dev, SDLA_DISABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
993
994 netif_stop_queue(dev);
995
Eric Dumazet807540b2010-09-23 05:40:09 +0000996 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997}
998
999struct conf_data {
1000 struct frad_conf config;
1001 short dlci[CONFIG_DLCI_MAX];
1002};
1003
1004static int sdla_open(struct net_device *dev)
1005{
1006 struct frad_local *flp;
1007 struct dlci_local *dlp;
1008 struct conf_data data;
1009 struct intr_info intr;
1010 int len, i;
1011 char byte;
1012
Wang Chen8f15ea42008-11-12 23:38:36 -08001013 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014
1015 if (!flp->initialized)
Eric Dumazet807540b2010-09-23 05:40:09 +00001016 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
1018 if (!flp->configured)
Eric Dumazet807540b2010-09-23 05:40:09 +00001019 return -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
1021 /* time to send in the configuration */
1022 len = 0;
1023 for(i=0;i<CONFIG_DLCI_MAX;i++)
1024 if (flp->dlci[i])
1025 data.dlci[len++] = abs(flp->dlci[i]);
1026 len *= 2;
1027
1028 memcpy(&data.config, &flp->config, sizeof(struct frad_conf));
1029 len += sizeof(struct frad_conf);
1030
1031 sdla_cmd(dev, SDLA_DISABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
1032 sdla_cmd(dev, SDLA_SET_DLCI_CONFIGURATION, 0, 0, &data, len, NULL, NULL);
1033
1034 if (flp->type == SDLA_S508)
1035 flp->buffer = 0;
1036
1037 sdla_cmd(dev, SDLA_ENABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
1038
1039 /* let's start up the reception */
1040 memset(&intr, 0, sizeof(intr));
1041 switch(flp->type)
1042 {
1043 case SDLA_S502A:
1044 flp->timer.expires = 1;
1045 add_timer(&flp->timer);
1046 break;
1047
1048 case SDLA_S502E:
1049 flp->state |= SDLA_S502E_ENABLE;
1050 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
1051 flp->state |= SDLA_S502E_INTACK;
1052 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
1053 byte = 0;
1054 sdla_write(dev, SDLA_502_IRQ_INTERFACE, &byte, sizeof(byte));
1055 intr.flags = SDLA_INTR_RX | SDLA_INTR_STATUS | SDLA_INTR_MODEM;
1056 sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(char) + sizeof(short), NULL, NULL);
1057 break;
1058
1059 case SDLA_S507:
1060 break;
1061
1062 case SDLA_S508:
1063 flp->state |= SDLA_S508_INTEN;
1064 outb(flp->state, dev->base_addr + SDLA_REG_CONTROL);
1065 byte = 0;
1066 sdla_write(dev, SDLA_508_IRQ_INTERFACE, &byte, sizeof(byte));
1067 intr.flags = SDLA_INTR_RX | SDLA_INTR_STATUS | SDLA_INTR_MODEM;
1068 intr.irq = dev->irq;
1069 sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(struct intr_info), NULL, NULL);
1070 break;
1071 }
1072
1073 if (flp->config.station == FRAD_STATION_CPE)
1074 {
1075 byte = SDLA_ICS_STATUS_ENQ;
1076 sdla_cmd(dev, SDLA_ISSUE_IN_CHANNEL_SIGNAL, 0, 0, &byte, sizeof(byte), NULL, NULL);
1077 }
1078 else
1079 {
1080 sdla_cmd(dev, SDLA_ADD_DLCI, 0, 0, data.dlci, len - sizeof(struct frad_conf), NULL, NULL);
1081 for(i=0;i<CONFIG_DLCI_MAX;i++)
1082 if (flp->dlci[i] > 0)
1083 sdla_cmd(dev, SDLA_ACTIVATE_DLCI, 0, 0, &flp->dlci[i], 2*sizeof(flp->dlci[i]), NULL, NULL);
1084 }
1085
1086 /* configure any specific DLCI settings */
1087 for(i=0;i<CONFIG_DLCI_MAX;i++)
1088 if (flp->dlci[i])
1089 {
Wang Chen8f15ea42008-11-12 23:38:36 -08001090 dlp = netdev_priv(flp->master[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 if (dlp->configured)
1092 sdla_cmd(dev, SDLA_SET_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0, &dlp->config, sizeof(struct dlci_conf), NULL, NULL);
1093 }
1094
1095 netif_start_queue(dev);
1096
Eric Dumazet807540b2010-09-23 05:40:09 +00001097 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098}
1099
1100static int sdla_config(struct net_device *dev, struct frad_conf __user *conf, int get)
1101{
1102 struct frad_local *flp;
1103 struct conf_data data;
1104 int i;
1105 short size;
1106
1107 if (dev->type == 0xFFFF)
Eric Dumazet807540b2010-09-23 05:40:09 +00001108 return -EUNATCH;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109
Wang Chen8f15ea42008-11-12 23:38:36 -08001110 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111
1112 if (!get)
1113 {
1114 if (netif_running(dev))
Eric Dumazet807540b2010-09-23 05:40:09 +00001115 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116
1117 if(copy_from_user(&data.config, conf, sizeof(struct frad_conf)))
1118 return -EFAULT;
1119
1120 if (data.config.station & ~FRAD_STATION_NODE)
Eric Dumazet807540b2010-09-23 05:40:09 +00001121 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122
1123 if (data.config.flags & ~FRAD_VALID_FLAGS)
Eric Dumazet807540b2010-09-23 05:40:09 +00001124 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125
1126 if ((data.config.kbaud < 0) ||
1127 ((data.config.kbaud > 128) && (flp->type != SDLA_S508)))
Eric Dumazet807540b2010-09-23 05:40:09 +00001128 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129
1130 if (data.config.clocking & ~(FRAD_CLOCK_INT | SDLA_S508_PORT_RS232))
Eric Dumazet807540b2010-09-23 05:40:09 +00001131 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132
1133 if ((data.config.mtu < 0) || (data.config.mtu > SDLA_MAX_MTU))
Eric Dumazet807540b2010-09-23 05:40:09 +00001134 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135
1136 if ((data.config.T391 < 5) || (data.config.T391 > 30))
Eric Dumazet807540b2010-09-23 05:40:09 +00001137 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138
1139 if ((data.config.T392 < 5) || (data.config.T392 > 30))
Eric Dumazet807540b2010-09-23 05:40:09 +00001140 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141
1142 if ((data.config.N391 < 1) || (data.config.N391 > 255))
Eric Dumazet807540b2010-09-23 05:40:09 +00001143 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144
1145 if ((data.config.N392 < 1) || (data.config.N392 > 10))
Eric Dumazet807540b2010-09-23 05:40:09 +00001146 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147
1148 if ((data.config.N393 < 1) || (data.config.N393 > 10))
Eric Dumazet807540b2010-09-23 05:40:09 +00001149 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150
1151 memcpy(&flp->config, &data.config, sizeof(struct frad_conf));
1152 flp->config.flags |= SDLA_DIRECT_RECV;
1153
1154 if (flp->type == SDLA_S508)
1155 flp->config.flags |= SDLA_TX70_RX30;
1156
1157 if (dev->mtu != flp->config.mtu)
1158 {
1159 /* this is required to change the MTU */
1160 dev->mtu = flp->config.mtu;
1161 for(i=0;i<CONFIG_DLCI_MAX;i++)
1162 if (flp->master[i])
1163 flp->master[i]->mtu = flp->config.mtu;
1164 }
1165
1166 flp->config.mtu += sizeof(struct frhdr);
1167
1168 /* off to the races! */
1169 if (!flp->configured)
1170 sdla_start(dev);
1171
1172 flp->configured = 1;
1173 }
1174 else
1175 {
1176 /* no sense reading if the CPU isn't started */
1177 if (netif_running(dev))
1178 {
1179 size = sizeof(data);
1180 if (sdla_cmd(dev, SDLA_READ_DLCI_CONFIGURATION, 0, 0, NULL, 0, &data, &size) != SDLA_RET_OK)
Eric Dumazet807540b2010-09-23 05:40:09 +00001181 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 }
1183 else
1184 if (flp->configured)
1185 memcpy(&data.config, &flp->config, sizeof(struct frad_conf));
1186 else
1187 memset(&data.config, 0, sizeof(struct frad_conf));
1188
1189 memcpy(&flp->config, &data.config, sizeof(struct frad_conf));
1190 data.config.flags &= FRAD_VALID_FLAGS;
1191 data.config.mtu -= data.config.mtu > sizeof(struct frhdr) ? sizeof(struct frhdr) : data.config.mtu;
1192 return copy_to_user(conf, &data.config, sizeof(struct frad_conf))?-EFAULT:0;
1193 }
1194
Eric Dumazet807540b2010-09-23 05:40:09 +00001195 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196}
1197
1198static int sdla_xfer(struct net_device *dev, struct sdla_mem __user *info, int read)
1199{
1200 struct sdla_mem mem;
1201 char *temp;
1202
1203 if(copy_from_user(&mem, info, sizeof(mem)))
1204 return -EFAULT;
1205
1206 if (read)
1207 {
Yoann Padioleaudd00cc42007-07-19 01:49:03 -07001208 temp = kzalloc(mem.len, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 if (!temp)
Eric Dumazet807540b2010-09-23 05:40:09 +00001210 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 sdla_read(dev, mem.addr, temp, mem.len);
1212 if(copy_to_user(mem.data, temp, mem.len))
1213 {
1214 kfree(temp);
1215 return -EFAULT;
1216 }
1217 kfree(temp);
1218 }
1219 else
1220 {
Julia Lawallc146fc92010-05-21 22:20:26 +00001221 temp = memdup_user(mem.data, mem.len);
1222 if (IS_ERR(temp))
1223 return PTR_ERR(temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 sdla_write(dev, mem.addr, temp, mem.len);
1225 kfree(temp);
1226 }
Eric Dumazet807540b2010-09-23 05:40:09 +00001227 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228}
1229
1230static int sdla_reconfig(struct net_device *dev)
1231{
1232 struct frad_local *flp;
1233 struct conf_data data;
1234 int i, len;
1235
Wang Chen8f15ea42008-11-12 23:38:36 -08001236 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237
1238 len = 0;
1239 for(i=0;i<CONFIG_DLCI_MAX;i++)
1240 if (flp->dlci[i])
1241 data.dlci[len++] = flp->dlci[i];
1242 len *= 2;
1243
1244 memcpy(&data, &flp->config, sizeof(struct frad_conf));
1245 len += sizeof(struct frad_conf);
1246
1247 sdla_cmd(dev, SDLA_DISABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
1248 sdla_cmd(dev, SDLA_SET_DLCI_CONFIGURATION, 0, 0, &data, len, NULL, NULL);
1249 sdla_cmd(dev, SDLA_ENABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
1250
Eric Dumazet807540b2010-09-23 05:40:09 +00001251 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252}
1253
1254static int sdla_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
1255{
1256 struct frad_local *flp;
1257
1258 if(!capable(CAP_NET_ADMIN))
1259 return -EPERM;
1260
Wang Chen8f15ea42008-11-12 23:38:36 -08001261 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262
1263 if (!flp->initialized)
Eric Dumazet807540b2010-09-23 05:40:09 +00001264 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
1266 switch (cmd)
1267 {
1268 case FRAD_GET_CONF:
1269 case FRAD_SET_CONF:
Eric Dumazet807540b2010-09-23 05:40:09 +00001270 return sdla_config(dev, ifr->ifr_data, cmd == FRAD_GET_CONF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271
1272 case SDLA_IDENTIFY:
1273 ifr->ifr_flags = flp->type;
1274 break;
1275
1276 case SDLA_CPUSPEED:
Eric Dumazet807540b2010-09-23 05:40:09 +00001277 return sdla_cpuspeed(dev, ifr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278
1279/* ==========================================================
1280NOTE: This is rather a useless action right now, as the
1281 current driver does not support protocols other than
1282 FR. However, Sangoma has modules for a number of
1283 other protocols in the works.
1284============================================================*/
1285 case SDLA_PROTOCOL:
1286 if (flp->configured)
Eric Dumazet807540b2010-09-23 05:40:09 +00001287 return -EALREADY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
1289 switch (ifr->ifr_flags)
1290 {
1291 case ARPHRD_FRAD:
1292 dev->type = ifr->ifr_flags;
1293 break;
1294 default:
Eric Dumazet807540b2010-09-23 05:40:09 +00001295 return -ENOPROTOOPT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 }
1297 break;
1298
1299 case SDLA_CLEARMEM:
1300 sdla_clear(dev);
1301 break;
1302
1303 case SDLA_WRITEMEM:
1304 case SDLA_READMEM:
1305 if(!capable(CAP_SYS_RAWIO))
1306 return -EPERM;
Eric Dumazet807540b2010-09-23 05:40:09 +00001307 return sdla_xfer(dev, ifr->ifr_data, cmd == SDLA_READMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
1309 case SDLA_START:
1310 sdla_start(dev);
1311 break;
1312
1313 case SDLA_STOP:
1314 sdla_stop(dev);
1315 break;
1316
1317 default:
Eric Dumazet807540b2010-09-23 05:40:09 +00001318 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 }
Eric Dumazet807540b2010-09-23 05:40:09 +00001320 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321}
1322
Adrian Bunk7665a082005-09-09 23:17:28 -07001323static int sdla_change_mtu(struct net_device *dev, int new_mtu)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324{
1325 struct frad_local *flp;
1326
Wang Chen8f15ea42008-11-12 23:38:36 -08001327 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
1329 if (netif_running(dev))
Eric Dumazet807540b2010-09-23 05:40:09 +00001330 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331
1332 /* for now, you can't change the MTU! */
Eric Dumazet807540b2010-09-23 05:40:09 +00001333 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334}
1335
Adrian Bunk7665a082005-09-09 23:17:28 -07001336static int sdla_set_config(struct net_device *dev, struct ifmap *map)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337{
1338 struct frad_local *flp;
1339 int i;
1340 char byte;
1341 unsigned base;
1342 int err = -EINVAL;
1343
Wang Chen8f15ea42008-11-12 23:38:36 -08001344 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345
1346 if (flp->initialized)
Eric Dumazet807540b2010-09-23 05:40:09 +00001347 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001349 for(i=0; i < ARRAY_SIZE(valid_port); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 if (valid_port[i] == map->base_addr)
1351 break;
1352
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001353 if (i == ARRAY_SIZE(valid_port))
Eric Dumazet807540b2010-09-23 05:40:09 +00001354 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
1356 if (!request_region(map->base_addr, SDLA_IO_EXTENTS, dev->name)){
Joe Perches86fb0cc2011-06-26 19:01:31 +00001357 pr_warn("io-port 0x%04lx in use\n", dev->base_addr);
Eric Dumazet807540b2010-09-23 05:40:09 +00001358 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 }
1360 base = map->base_addr;
1361
1362 /* test for card types, S502A, S502E, S507, S508 */
1363 /* these tests shut down the card completely, so clear the state */
1364 flp->type = SDLA_UNKNOWN;
1365 flp->state = 0;
1366
1367 for(i=1;i<SDLA_IO_EXTENTS;i++)
1368 if (inb(base + i) != 0xFF)
1369 break;
1370
1371 if (i == SDLA_IO_EXTENTS) {
1372 outb(SDLA_HALT, base + SDLA_REG_Z80_CONTROL);
1373 if ((inb(base + SDLA_S502_STS) & 0x0F) == 0x08) {
1374 outb(SDLA_S502E_INTACK, base + SDLA_REG_CONTROL);
1375 if ((inb(base + SDLA_S502_STS) & 0x0F) == 0x0C) {
1376 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1377 flp->type = SDLA_S502E;
1378 goto got_type;
1379 }
1380 }
1381 }
1382
1383 for(byte=inb(base),i=0;i<SDLA_IO_EXTENTS;i++)
1384 if (inb(base + i) != byte)
1385 break;
1386
1387 if (i == SDLA_IO_EXTENTS) {
1388 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1389 if ((inb(base + SDLA_S502_STS) & 0x7E) == 0x30) {
1390 outb(SDLA_S507_ENABLE, base + SDLA_REG_CONTROL);
1391 if ((inb(base + SDLA_S502_STS) & 0x7E) == 0x32) {
1392 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1393 flp->type = SDLA_S507;
1394 goto got_type;
1395 }
1396 }
1397 }
1398
1399 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1400 if ((inb(base + SDLA_S508_STS) & 0x3F) == 0x00) {
1401 outb(SDLA_S508_INTEN, base + SDLA_REG_CONTROL);
1402 if ((inb(base + SDLA_S508_STS) & 0x3F) == 0x10) {
1403 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1404 flp->type = SDLA_S508;
1405 goto got_type;
1406 }
1407 }
1408
1409 outb(SDLA_S502A_HALT, base + SDLA_REG_CONTROL);
1410 if (inb(base + SDLA_S502_STS) == 0x40) {
1411 outb(SDLA_S502A_START, base + SDLA_REG_CONTROL);
1412 if (inb(base + SDLA_S502_STS) == 0x40) {
1413 outb(SDLA_S502A_INTEN, base + SDLA_REG_CONTROL);
1414 if (inb(base + SDLA_S502_STS) == 0x44) {
1415 outb(SDLA_S502A_START, base + SDLA_REG_CONTROL);
1416 flp->type = SDLA_S502A;
1417 goto got_type;
1418 }
1419 }
1420 }
1421
Joe Perches86fb0cc2011-06-26 19:01:31 +00001422 netdev_notice(dev, "Unknown card type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 err = -ENODEV;
1424 goto fail;
1425
1426got_type:
1427 switch(base) {
1428 case 0x270:
1429 case 0x280:
1430 case 0x380:
1431 case 0x390:
1432 if (flp->type != SDLA_S508 && flp->type != SDLA_S507)
1433 goto fail;
1434 }
1435
1436 switch (map->irq) {
1437 case 2:
1438 if (flp->type != SDLA_S502E)
1439 goto fail;
1440 break;
1441
1442 case 10:
1443 case 11:
1444 case 12:
1445 case 15:
1446 case 4:
1447 if (flp->type != SDLA_S508 && flp->type != SDLA_S507)
1448 goto fail;
1449 break;
1450 case 3:
1451 case 5:
1452 case 7:
1453 if (flp->type == SDLA_S502A)
1454 goto fail;
1455 break;
1456
1457 default:
1458 goto fail;
1459 }
1460
1461 err = -EAGAIN;
Joe Perchesa0607fd2009-11-18 23:29:17 -08001462 if (request_irq(dev->irq, sdla_isr, 0, dev->name, dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 goto fail;
1464
1465 if (flp->type == SDLA_S507) {
1466 switch(dev->irq) {
1467 case 3:
1468 flp->state = SDLA_S507_IRQ3;
1469 break;
1470 case 4:
1471 flp->state = SDLA_S507_IRQ4;
1472 break;
1473 case 5:
1474 flp->state = SDLA_S507_IRQ5;
1475 break;
1476 case 7:
1477 flp->state = SDLA_S507_IRQ7;
1478 break;
1479 case 10:
1480 flp->state = SDLA_S507_IRQ10;
1481 break;
1482 case 11:
1483 flp->state = SDLA_S507_IRQ11;
1484 break;
1485 case 12:
1486 flp->state = SDLA_S507_IRQ12;
1487 break;
1488 case 15:
1489 flp->state = SDLA_S507_IRQ15;
1490 break;
1491 }
1492 }
1493
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001494 for(i=0; i < ARRAY_SIZE(valid_mem); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 if (valid_mem[i] == map->mem_start)
1496 break;
1497
1498 err = -EINVAL;
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001499 if (i == ARRAY_SIZE(valid_mem))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 goto fail2;
1501
1502 if (flp->type == SDLA_S502A && (map->mem_start & 0xF000) >> 12 == 0x0E)
1503 goto fail2;
1504
1505 if (flp->type != SDLA_S507 && map->mem_start >> 16 == 0x0B)
1506 goto fail2;
1507
1508 if (flp->type == SDLA_S507 && map->mem_start >> 16 == 0x0D)
1509 goto fail2;
1510
1511 byte = flp->type != SDLA_S508 ? SDLA_8K_WINDOW : 0;
1512 byte |= (map->mem_start & 0xF000) >> (12 + (flp->type == SDLA_S508 ? 1 : 0));
1513 switch(flp->type) {
1514 case SDLA_S502A:
1515 case SDLA_S502E:
1516 switch (map->mem_start >> 16) {
1517 case 0x0A:
1518 byte |= SDLA_S502_SEG_A;
1519 break;
1520 case 0x0C:
1521 byte |= SDLA_S502_SEG_C;
1522 break;
1523 case 0x0D:
1524 byte |= SDLA_S502_SEG_D;
1525 break;
1526 case 0x0E:
1527 byte |= SDLA_S502_SEG_E;
1528 break;
1529 }
1530 break;
1531 case SDLA_S507:
1532 switch (map->mem_start >> 16) {
1533 case 0x0A:
1534 byte |= SDLA_S507_SEG_A;
1535 break;
1536 case 0x0B:
1537 byte |= SDLA_S507_SEG_B;
1538 break;
1539 case 0x0C:
1540 byte |= SDLA_S507_SEG_C;
1541 break;
1542 case 0x0E:
1543 byte |= SDLA_S507_SEG_E;
1544 break;
1545 }
1546 break;
1547 case SDLA_S508:
1548 switch (map->mem_start >> 16) {
1549 case 0x0A:
1550 byte |= SDLA_S508_SEG_A;
1551 break;
1552 case 0x0C:
1553 byte |= SDLA_S508_SEG_C;
1554 break;
1555 case 0x0D:
1556 byte |= SDLA_S508_SEG_D;
1557 break;
1558 case 0x0E:
1559 byte |= SDLA_S508_SEG_E;
1560 break;
1561 }
1562 break;
1563 }
1564
1565 /* set the memory bits, and enable access */
1566 outb(byte, base + SDLA_REG_PC_WINDOW);
1567
1568 switch(flp->type)
1569 {
1570 case SDLA_S502E:
1571 flp->state = SDLA_S502E_ENABLE;
1572 break;
1573 case SDLA_S507:
1574 flp->state |= SDLA_MEMEN;
1575 break;
1576 case SDLA_S508:
1577 flp->state = SDLA_MEMEN;
1578 break;
1579 }
1580 outb(flp->state, base + SDLA_REG_CONTROL);
1581
1582 dev->irq = map->irq;
1583 dev->base_addr = base;
1584 dev->mem_start = map->mem_start;
1585 dev->mem_end = dev->mem_start + 0x2000;
1586 flp->initialized = 1;
1587 return 0;
1588
1589fail2:
1590 free_irq(map->irq, dev);
1591fail:
1592 release_region(base, SDLA_IO_EXTENTS);
1593 return err;
1594}
1595
Stephen Hemmingerac995332009-03-26 15:11:25 +00001596static const struct net_device_ops sdla_netdev_ops = {
1597 .ndo_open = sdla_open,
1598 .ndo_stop = sdla_close,
1599 .ndo_do_ioctl = sdla_ioctl,
1600 .ndo_set_config = sdla_set_config,
1601 .ndo_start_xmit = sdla_transmit,
1602 .ndo_change_mtu = sdla_change_mtu,
1603};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
1605static void setup_sdla(struct net_device *dev)
1606{
Wang Chen8f15ea42008-11-12 23:38:36 -08001607 struct frad_local *flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608
1609 netdev_boot_setup_check(dev);
1610
Stephen Hemmingerac995332009-03-26 15:11:25 +00001611 dev->netdev_ops = &sdla_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 dev->flags = 0;
1613 dev->type = 0xFFFF;
1614 dev->hard_header_len = 0;
1615 dev->addr_len = 0;
1616 dev->mtu = SDLA_MAX_MTU;
1617
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 flp->activate = sdla_activate;
1619 flp->deactivate = sdla_deactivate;
1620 flp->assoc = sdla_assoc;
1621 flp->deassoc = sdla_deassoc;
1622 flp->dlci_conf = sdla_dlci_conf;
1623
1624 init_timer(&flp->timer);
1625 flp->timer.expires = 1;
1626 flp->timer.data = (unsigned long) dev;
1627 flp->timer.function = sdla_poll;
1628}
1629
1630static struct net_device *sdla;
1631
1632static int __init init_sdla(void)
1633{
1634 int err;
1635
1636 printk("%s.\n", version);
1637
1638 sdla = alloc_netdev(sizeof(struct frad_local), "sdla0", setup_sdla);
1639 if (!sdla)
1640 return -ENOMEM;
1641
1642 err = register_netdev(sdla);
1643 if (err)
1644 free_netdev(sdla);
1645
1646 return err;
1647}
1648
1649static void __exit exit_sdla(void)
1650{
Wang Chen8f15ea42008-11-12 23:38:36 -08001651 struct frad_local *flp = netdev_priv(sdla);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652
1653 unregister_netdev(sdla);
1654 if (flp->initialized) {
1655 free_irq(sdla->irq, sdla);
1656 release_region(sdla->base_addr, SDLA_IO_EXTENTS);
1657 }
1658 del_timer_sync(&flp->timer);
1659 free_netdev(sdla);
1660}
1661
1662MODULE_LICENSE("GPL");
1663
1664module_init(init_sdla);
1665module_exit(exit_sdla);