blob: 421ac5f856994731fc7a0f3752c32dcebda15bf1 [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{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 if (netif_running(dev))
Eric Dumazet807540b2010-09-23 05:40:09 +00001326 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327
1328 /* for now, you can't change the MTU! */
Eric Dumazet807540b2010-09-23 05:40:09 +00001329 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330}
1331
Adrian Bunk7665a082005-09-09 23:17:28 -07001332static int sdla_set_config(struct net_device *dev, struct ifmap *map)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333{
1334 struct frad_local *flp;
1335 int i;
1336 char byte;
1337 unsigned base;
1338 int err = -EINVAL;
1339
Wang Chen8f15ea42008-11-12 23:38:36 -08001340 flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341
1342 if (flp->initialized)
Eric Dumazet807540b2010-09-23 05:40:09 +00001343 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001345 for(i=0; i < ARRAY_SIZE(valid_port); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 if (valid_port[i] == map->base_addr)
1347 break;
1348
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001349 if (i == ARRAY_SIZE(valid_port))
Eric Dumazet807540b2010-09-23 05:40:09 +00001350 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351
1352 if (!request_region(map->base_addr, SDLA_IO_EXTENTS, dev->name)){
Joe Perches86fb0cc2011-06-26 19:01:31 +00001353 pr_warn("io-port 0x%04lx in use\n", dev->base_addr);
Eric Dumazet807540b2010-09-23 05:40:09 +00001354 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 }
1356 base = map->base_addr;
1357
1358 /* test for card types, S502A, S502E, S507, S508 */
1359 /* these tests shut down the card completely, so clear the state */
1360 flp->type = SDLA_UNKNOWN;
1361 flp->state = 0;
1362
1363 for(i=1;i<SDLA_IO_EXTENTS;i++)
1364 if (inb(base + i) != 0xFF)
1365 break;
1366
1367 if (i == SDLA_IO_EXTENTS) {
1368 outb(SDLA_HALT, base + SDLA_REG_Z80_CONTROL);
1369 if ((inb(base + SDLA_S502_STS) & 0x0F) == 0x08) {
1370 outb(SDLA_S502E_INTACK, base + SDLA_REG_CONTROL);
1371 if ((inb(base + SDLA_S502_STS) & 0x0F) == 0x0C) {
1372 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1373 flp->type = SDLA_S502E;
1374 goto got_type;
1375 }
1376 }
1377 }
1378
1379 for(byte=inb(base),i=0;i<SDLA_IO_EXTENTS;i++)
1380 if (inb(base + i) != byte)
1381 break;
1382
1383 if (i == SDLA_IO_EXTENTS) {
1384 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1385 if ((inb(base + SDLA_S502_STS) & 0x7E) == 0x30) {
1386 outb(SDLA_S507_ENABLE, base + SDLA_REG_CONTROL);
1387 if ((inb(base + SDLA_S502_STS) & 0x7E) == 0x32) {
1388 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1389 flp->type = SDLA_S507;
1390 goto got_type;
1391 }
1392 }
1393 }
1394
1395 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1396 if ((inb(base + SDLA_S508_STS) & 0x3F) == 0x00) {
1397 outb(SDLA_S508_INTEN, base + SDLA_REG_CONTROL);
1398 if ((inb(base + SDLA_S508_STS) & 0x3F) == 0x10) {
1399 outb(SDLA_HALT, base + SDLA_REG_CONTROL);
1400 flp->type = SDLA_S508;
1401 goto got_type;
1402 }
1403 }
1404
1405 outb(SDLA_S502A_HALT, base + SDLA_REG_CONTROL);
1406 if (inb(base + SDLA_S502_STS) == 0x40) {
1407 outb(SDLA_S502A_START, base + SDLA_REG_CONTROL);
1408 if (inb(base + SDLA_S502_STS) == 0x40) {
1409 outb(SDLA_S502A_INTEN, base + SDLA_REG_CONTROL);
1410 if (inb(base + SDLA_S502_STS) == 0x44) {
1411 outb(SDLA_S502A_START, base + SDLA_REG_CONTROL);
1412 flp->type = SDLA_S502A;
1413 goto got_type;
1414 }
1415 }
1416 }
1417
Joe Perches86fb0cc2011-06-26 19:01:31 +00001418 netdev_notice(dev, "Unknown card type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 err = -ENODEV;
1420 goto fail;
1421
1422got_type:
1423 switch(base) {
1424 case 0x270:
1425 case 0x280:
1426 case 0x380:
1427 case 0x390:
1428 if (flp->type != SDLA_S508 && flp->type != SDLA_S507)
1429 goto fail;
1430 }
1431
1432 switch (map->irq) {
1433 case 2:
1434 if (flp->type != SDLA_S502E)
1435 goto fail;
1436 break;
1437
1438 case 10:
1439 case 11:
1440 case 12:
1441 case 15:
1442 case 4:
1443 if (flp->type != SDLA_S508 && flp->type != SDLA_S507)
1444 goto fail;
1445 break;
1446 case 3:
1447 case 5:
1448 case 7:
1449 if (flp->type == SDLA_S502A)
1450 goto fail;
1451 break;
1452
1453 default:
1454 goto fail;
1455 }
1456
1457 err = -EAGAIN;
Joe Perchesa0607fd2009-11-18 23:29:17 -08001458 if (request_irq(dev->irq, sdla_isr, 0, dev->name, dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 goto fail;
1460
1461 if (flp->type == SDLA_S507) {
1462 switch(dev->irq) {
1463 case 3:
1464 flp->state = SDLA_S507_IRQ3;
1465 break;
1466 case 4:
1467 flp->state = SDLA_S507_IRQ4;
1468 break;
1469 case 5:
1470 flp->state = SDLA_S507_IRQ5;
1471 break;
1472 case 7:
1473 flp->state = SDLA_S507_IRQ7;
1474 break;
1475 case 10:
1476 flp->state = SDLA_S507_IRQ10;
1477 break;
1478 case 11:
1479 flp->state = SDLA_S507_IRQ11;
1480 break;
1481 case 12:
1482 flp->state = SDLA_S507_IRQ12;
1483 break;
1484 case 15:
1485 flp->state = SDLA_S507_IRQ15;
1486 break;
1487 }
1488 }
1489
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001490 for(i=0; i < ARRAY_SIZE(valid_mem); i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 if (valid_mem[i] == map->mem_start)
1492 break;
1493
1494 err = -EINVAL;
Alejandro Martinez Ruize9edda62007-10-15 03:37:43 +02001495 if (i == ARRAY_SIZE(valid_mem))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 goto fail2;
1497
1498 if (flp->type == SDLA_S502A && (map->mem_start & 0xF000) >> 12 == 0x0E)
1499 goto fail2;
1500
1501 if (flp->type != SDLA_S507 && map->mem_start >> 16 == 0x0B)
1502 goto fail2;
1503
1504 if (flp->type == SDLA_S507 && map->mem_start >> 16 == 0x0D)
1505 goto fail2;
1506
1507 byte = flp->type != SDLA_S508 ? SDLA_8K_WINDOW : 0;
1508 byte |= (map->mem_start & 0xF000) >> (12 + (flp->type == SDLA_S508 ? 1 : 0));
1509 switch(flp->type) {
1510 case SDLA_S502A:
1511 case SDLA_S502E:
1512 switch (map->mem_start >> 16) {
1513 case 0x0A:
1514 byte |= SDLA_S502_SEG_A;
1515 break;
1516 case 0x0C:
1517 byte |= SDLA_S502_SEG_C;
1518 break;
1519 case 0x0D:
1520 byte |= SDLA_S502_SEG_D;
1521 break;
1522 case 0x0E:
1523 byte |= SDLA_S502_SEG_E;
1524 break;
1525 }
1526 break;
1527 case SDLA_S507:
1528 switch (map->mem_start >> 16) {
1529 case 0x0A:
1530 byte |= SDLA_S507_SEG_A;
1531 break;
1532 case 0x0B:
1533 byte |= SDLA_S507_SEG_B;
1534 break;
1535 case 0x0C:
1536 byte |= SDLA_S507_SEG_C;
1537 break;
1538 case 0x0E:
1539 byte |= SDLA_S507_SEG_E;
1540 break;
1541 }
1542 break;
1543 case SDLA_S508:
1544 switch (map->mem_start >> 16) {
1545 case 0x0A:
1546 byte |= SDLA_S508_SEG_A;
1547 break;
1548 case 0x0C:
1549 byte |= SDLA_S508_SEG_C;
1550 break;
1551 case 0x0D:
1552 byte |= SDLA_S508_SEG_D;
1553 break;
1554 case 0x0E:
1555 byte |= SDLA_S508_SEG_E;
1556 break;
1557 }
1558 break;
1559 }
1560
1561 /* set the memory bits, and enable access */
1562 outb(byte, base + SDLA_REG_PC_WINDOW);
1563
1564 switch(flp->type)
1565 {
1566 case SDLA_S502E:
1567 flp->state = SDLA_S502E_ENABLE;
1568 break;
1569 case SDLA_S507:
1570 flp->state |= SDLA_MEMEN;
1571 break;
1572 case SDLA_S508:
1573 flp->state = SDLA_MEMEN;
1574 break;
1575 }
1576 outb(flp->state, base + SDLA_REG_CONTROL);
1577
1578 dev->irq = map->irq;
1579 dev->base_addr = base;
1580 dev->mem_start = map->mem_start;
1581 dev->mem_end = dev->mem_start + 0x2000;
1582 flp->initialized = 1;
1583 return 0;
1584
1585fail2:
1586 free_irq(map->irq, dev);
1587fail:
1588 release_region(base, SDLA_IO_EXTENTS);
1589 return err;
1590}
1591
Stephen Hemmingerac995332009-03-26 15:11:25 +00001592static const struct net_device_ops sdla_netdev_ops = {
1593 .ndo_open = sdla_open,
1594 .ndo_stop = sdla_close,
1595 .ndo_do_ioctl = sdla_ioctl,
1596 .ndo_set_config = sdla_set_config,
1597 .ndo_start_xmit = sdla_transmit,
1598 .ndo_change_mtu = sdla_change_mtu,
1599};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600
1601static void setup_sdla(struct net_device *dev)
1602{
Wang Chen8f15ea42008-11-12 23:38:36 -08001603 struct frad_local *flp = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
1605 netdev_boot_setup_check(dev);
1606
Stephen Hemmingerac995332009-03-26 15:11:25 +00001607 dev->netdev_ops = &sdla_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 dev->flags = 0;
1609 dev->type = 0xFFFF;
1610 dev->hard_header_len = 0;
1611 dev->addr_len = 0;
1612 dev->mtu = SDLA_MAX_MTU;
1613
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 flp->activate = sdla_activate;
1615 flp->deactivate = sdla_deactivate;
1616 flp->assoc = sdla_assoc;
1617 flp->deassoc = sdla_deassoc;
1618 flp->dlci_conf = sdla_dlci_conf;
1619
1620 init_timer(&flp->timer);
1621 flp->timer.expires = 1;
1622 flp->timer.data = (unsigned long) dev;
1623 flp->timer.function = sdla_poll;
1624}
1625
1626static struct net_device *sdla;
1627
1628static int __init init_sdla(void)
1629{
1630 int err;
1631
1632 printk("%s.\n", version);
1633
Tom Gundersenc835a672014-07-14 16:37:24 +02001634 sdla = alloc_netdev(sizeof(struct frad_local), "sdla0",
1635 NET_NAME_UNKNOWN, setup_sdla);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 if (!sdla)
1637 return -ENOMEM;
1638
1639 err = register_netdev(sdla);
1640 if (err)
1641 free_netdev(sdla);
1642
1643 return err;
1644}
1645
1646static void __exit exit_sdla(void)
1647{
Wang Chen8f15ea42008-11-12 23:38:36 -08001648 struct frad_local *flp = netdev_priv(sdla);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
1650 unregister_netdev(sdla);
1651 if (flp->initialized) {
1652 free_irq(sdla->irq, sdla);
1653 release_region(sdla->base_addr, SDLA_IO_EXTENTS);
1654 }
1655 del_timer_sync(&flp->timer);
1656 free_netdev(sdla);
1657}
1658
1659MODULE_LICENSE("GPL");
1660
1661module_init(init_sdla);
1662module_exit(exit_sdla);