]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * This software may be used and distributed according to the terms | |
3 | * of the GNU General Public License, incorporated herein by reference. | |
4 | * | |
5 | */ | |
6 | ||
7 | #include <linux/module.h> | |
8 | #include <linux/init.h> | |
9 | #include <linux/interrupt.h> | |
10 | #include <linux/delay.h> | |
11 | #include "includes.h" | |
12 | #include "hardware.h" | |
13 | #include "card.h" | |
14 | ||
15 | MODULE_DESCRIPTION("ISDN4Linux: Driver for Spellcaster card"); | |
16 | MODULE_AUTHOR("Spellcaster Telecommunications Inc."); | |
17 | MODULE_LICENSE("GPL"); | |
18 | ||
19 | board *sc_adapter[MAX_CARDS]; | |
20 | int cinst; | |
21 | ||
22 | static char devname[] = "scX"; | |
e3ca5e76 | 23 | static const char version[] = "2.0b1"; |
1da177e4 | 24 | |
e3ca5e76 | 25 | static const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" }; |
1da177e4 LT |
26 | |
27 | /* insmod set parameters */ | |
28 | static unsigned int io[] = {0,0,0,0}; | |
29 | static unsigned char irq[] = {0,0,0,0}; | |
30 | static unsigned long ram[] = {0,0,0,0}; | |
31 | static int do_reset = 0; | |
32 | ||
33 | module_param_array(io, int, NULL, 0); | |
34 | module_param_array(irq, int, NULL, 0); | |
35 | module_param_array(ram, int, NULL, 0); | |
36 | module_param(do_reset, bool, 0); | |
37 | ||
1da177e4 LT |
38 | extern irqreturn_t interrupt_handler(int, void *, struct pt_regs *); |
39 | extern int sndpkt(int, int, int, struct sk_buff *); | |
40 | extern int command(isdn_ctrl *); | |
41 | extern int indicate_status(int, int, ulong, char*); | |
42 | extern int reset(int); | |
43 | ||
e3ca5e76 | 44 | static int identify_board(unsigned long, unsigned int); |
1da177e4 LT |
45 | |
46 | static int __init sc_init(void) | |
47 | { | |
48 | int b = -1; | |
49 | int i, j; | |
50 | int status = -ENODEV; | |
51 | ||
52 | unsigned long memsize = 0; | |
53 | unsigned long features = 0; | |
54 | isdn_if *interface; | |
55 | unsigned char channels; | |
56 | unsigned char pgport; | |
57 | unsigned long magic; | |
58 | int model; | |
59 | int last_base = IOBASE_MIN; | |
60 | int probe_exhasted = 0; | |
61 | ||
62 | #ifdef MODULE | |
63 | pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version); | |
64 | #else | |
65 | pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version); | |
66 | #endif | |
67 | pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n"); | |
68 | ||
69 | while(b++ < MAX_CARDS - 1) { | |
70 | pr_debug("Probing for adapter #%d\n", b); | |
71 | /* | |
72 | * Initialize reusable variables | |
73 | */ | |
74 | model = -1; | |
75 | magic = 0; | |
76 | channels = 0; | |
77 | pgport = 0; | |
78 | ||
79 | /* | |
80 | * See if we should probe for IO base | |
81 | */ | |
82 | pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b], | |
83 | io[b] == 0 ? "will" : "won't"); | |
84 | if(io[b]) { | |
85 | /* | |
86 | * No, I/O Base has been provided | |
87 | */ | |
88 | for (i = 0 ; i < MAX_IO_REGS - 1 ; i++) { | |
89 | if(!request_region(io[b] + i * 0x400, 1, "sc test")) { | |
90 | pr_debug("check_region for 0x%x failed\n", io[b] + i * 0x400); | |
91 | io[b] = 0; | |
92 | break; | |
93 | } else | |
94 | release_region(io[b] + i * 0x400, 1); | |
95 | } | |
96 | ||
97 | /* | |
98 | * Confirm the I/O Address with a test | |
99 | */ | |
100 | if(io[b] == 0) { | |
101 | pr_debug("I/O Address 0x%x is in use.\n"); | |
102 | continue; | |
103 | } | |
104 | ||
105 | outb(0x18, io[b] + 0x400 * EXP_PAGE0); | |
106 | if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { | |
107 | pr_debug("I/O Base 0x%x fails test\n"); | |
108 | continue; | |
109 | } | |
110 | } | |
111 | else { | |
112 | /* | |
113 | * Yes, probe for I/O Base | |
114 | */ | |
115 | if(probe_exhasted) { | |
116 | pr_debug("All probe addresses exhasted, skipping\n"); | |
117 | continue; | |
118 | } | |
119 | pr_debug("Probing for I/O...\n"); | |
120 | for (i = last_base ; i <= IOBASE_MAX ; i += IOBASE_OFFSET) { | |
121 | int found_io = 1; | |
122 | if (i == IOBASE_MAX) { | |
123 | probe_exhasted = 1; /* No more addresses to probe */ | |
124 | pr_debug("End of Probes\n"); | |
125 | } | |
126 | last_base = i + IOBASE_OFFSET; | |
127 | pr_debug(" checking 0x%x...", i); | |
128 | for ( j = 0 ; j < MAX_IO_REGS - 1 ; j++) { | |
129 | if(!request_region(i + j * 0x400, 1, "sc test")) { | |
130 | pr_debug("Failed\n"); | |
131 | found_io = 0; | |
132 | break; | |
133 | } else | |
134 | release_region(i + j * 0x400, 1); | |
135 | } | |
136 | ||
137 | if(found_io) { | |
138 | io[b] = i; | |
139 | outb(0x18, io[b] + 0x400 * EXP_PAGE0); | |
140 | if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) { | |
141 | pr_debug("Failed by test\n"); | |
142 | continue; | |
143 | } | |
144 | pr_debug("Passed\n"); | |
145 | break; | |
146 | } | |
147 | } | |
148 | if(probe_exhasted) { | |
149 | continue; | |
150 | } | |
151 | } | |
152 | ||
153 | /* | |
154 | * See if we should probe for shared RAM | |
155 | */ | |
156 | if(do_reset) { | |
157 | pr_debug("Doing a SAFE probe reset\n"); | |
158 | outb(0xFF, io[b] + RESET_OFFSET); | |
159 | msleep_interruptible(10000); | |
160 | } | |
161 | pr_debug("RAM Base for board %d is 0x%x, %s probe\n", b, ram[b], | |
162 | ram[b] == 0 ? "will" : "won't"); | |
163 | ||
164 | if(ram[b]) { | |
165 | /* | |
166 | * No, the RAM base has been provided | |
167 | * Just look for a signature and ID the | |
168 | * board model | |
169 | */ | |
170 | if(request_region(ram[b], SRAM_PAGESIZE, "sc test")) { | |
171 | pr_debug("request_region for RAM base 0x%x succeeded\n", ram[b]); | |
172 | model = identify_board(ram[b], io[b]); | |
173 | release_region(ram[b], SRAM_PAGESIZE); | |
174 | } | |
175 | } | |
176 | else { | |
177 | /* | |
178 | * Yes, probe for free RAM and look for | |
179 | * a signature and id the board model | |
180 | */ | |
181 | for (i = SRAM_MIN ; i < SRAM_MAX ; i += SRAM_PAGESIZE) { | |
182 | pr_debug("Checking RAM address 0x%x...\n", i); | |
183 | if(request_region(i, SRAM_PAGESIZE, "sc test")) { | |
184 | pr_debug(" check_region succeeded\n"); | |
185 | model = identify_board(i, io[b]); | |
186 | release_region(i, SRAM_PAGESIZE); | |
187 | if (model >= 0) { | |
188 | pr_debug(" Identified a %s\n", | |
189 | boardname[model]); | |
190 | ram[b] = i; | |
191 | break; | |
192 | } | |
193 | pr_debug(" Unidentifed or inaccessible\n"); | |
194 | continue; | |
195 | } | |
196 | pr_debug(" request failed\n"); | |
197 | } | |
198 | } | |
199 | /* | |
200 | * See if we found free RAM and the board model | |
201 | */ | |
202 | if(!ram[b] || model < 0) { | |
203 | /* | |
204 | * Nope, there was no place in RAM for the | |
205 | * board, or it couldn't be identified | |
206 | */ | |
207 | pr_debug("Failed to find an adapter at 0x%x\n", ram[b]); | |
208 | continue; | |
209 | } | |
210 | ||
211 | /* | |
212 | * Set the board's magic number, memory size and page register | |
213 | */ | |
214 | switch(model) { | |
215 | case PRI_BOARD: | |
216 | channels = 23; | |
217 | magic = 0x20000; | |
218 | memsize = 0x100000; | |
219 | features = PRI_FEATURES; | |
220 | break; | |
221 | ||
222 | case BRI_BOARD: | |
223 | case POTS_BOARD: | |
224 | channels = 2; | |
225 | magic = 0x60000; | |
226 | memsize = 0x10000; | |
227 | features = BRI_FEATURES; | |
228 | break; | |
229 | } | |
230 | switch(ram[b] >> 12 & 0x0F) { | |
231 | case 0x0: | |
232 | pr_debug("RAM Page register set to EXP_PAGE0\n"); | |
233 | pgport = EXP_PAGE0; | |
234 | break; | |
235 | ||
236 | case 0x4: | |
237 | pr_debug("RAM Page register set to EXP_PAGE1\n"); | |
238 | pgport = EXP_PAGE1; | |
239 | break; | |
240 | ||
241 | case 0x8: | |
242 | pr_debug("RAM Page register set to EXP_PAGE2\n"); | |
243 | pgport = EXP_PAGE2; | |
244 | break; | |
245 | ||
246 | case 0xC: | |
247 | pr_debug("RAM Page register set to EXP_PAGE3\n"); | |
248 | pgport = EXP_PAGE3; | |
249 | break; | |
250 | ||
251 | default: | |
252 | pr_debug("RAM base address doesn't fall on 16K boundary\n"); | |
253 | continue; | |
254 | } | |
255 | ||
256 | pr_debug("current IRQ: %d b: %d\n",irq[b],b); | |
257 | ||
258 | /* | |
259 | * Make sure we got an IRQ | |
260 | */ | |
261 | if(!irq[b]) { | |
262 | /* | |
263 | * No interrupt could be used | |
264 | */ | |
265 | pr_debug("Failed to acquire an IRQ line\n"); | |
266 | continue; | |
267 | } | |
268 | ||
269 | /* | |
270 | * Horray! We found a board, Make sure we can register | |
271 | * it with ISDN4Linux | |
272 | */ | |
273 | interface = kmalloc(sizeof(isdn_if), GFP_KERNEL); | |
274 | if (interface == NULL) { | |
275 | /* | |
276 | * Oops, can't malloc isdn_if | |
277 | */ | |
278 | continue; | |
279 | } | |
280 | memset(interface, 0, sizeof(isdn_if)); | |
281 | ||
282 | interface->owner = THIS_MODULE; | |
283 | interface->hl_hdrlen = 0; | |
284 | interface->channels = channels; | |
285 | interface->maxbufsize = BUFFER_SIZE; | |
286 | interface->features = features; | |
287 | interface->writebuf_skb = sndpkt; | |
288 | interface->writecmd = NULL; | |
289 | interface->command = command; | |
290 | strcpy(interface->id, devname); | |
291 | interface->id[2] = '0' + cinst; | |
292 | ||
293 | /* | |
294 | * Allocate the board structure | |
295 | */ | |
296 | sc_adapter[cinst] = kmalloc(sizeof(board), GFP_KERNEL); | |
297 | if (sc_adapter[cinst] == NULL) { | |
298 | /* | |
299 | * Oops, can't alloc memory for the board | |
300 | */ | |
301 | kfree(interface); | |
302 | continue; | |
303 | } | |
304 | memset(sc_adapter[cinst], 0, sizeof(board)); | |
305 | spin_lock_init(&sc_adapter[cinst]->lock); | |
306 | ||
307 | if(!register_isdn(interface)) { | |
308 | /* | |
309 | * Oops, couldn't register for some reason | |
310 | */ | |
311 | kfree(interface); | |
312 | kfree(sc_adapter[cinst]); | |
313 | continue; | |
314 | } | |
315 | ||
316 | sc_adapter[cinst]->card = interface; | |
317 | sc_adapter[cinst]->driverId = interface->channels; | |
318 | strcpy(sc_adapter[cinst]->devicename, interface->id); | |
319 | sc_adapter[cinst]->nChannels = channels; | |
320 | sc_adapter[cinst]->ramsize = memsize; | |
321 | sc_adapter[cinst]->shmem_magic = magic; | |
322 | sc_adapter[cinst]->shmem_pgport = pgport; | |
323 | sc_adapter[cinst]->StartOnReset = 1; | |
324 | ||
325 | /* | |
326 | * Allocate channels status structures | |
327 | */ | |
328 | sc_adapter[cinst]->channel = kmalloc(sizeof(bchan) * channels, GFP_KERNEL); | |
329 | if (sc_adapter[cinst]->channel == NULL) { | |
330 | /* | |
331 | * Oops, can't alloc memory for the channels | |
332 | */ | |
333 | indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */ | |
334 | kfree(interface); | |
335 | kfree(sc_adapter[cinst]); | |
336 | continue; | |
337 | } | |
338 | memset(sc_adapter[cinst]->channel, 0, sizeof(bchan) * channels); | |
339 | ||
340 | /* | |
341 | * Lock down the hardware resources | |
342 | */ | |
343 | sc_adapter[cinst]->interrupt = irq[b]; | |
344 | if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler, | |
345 | SA_INTERRUPT, interface->id, NULL)) | |
346 | { | |
347 | kfree(sc_adapter[cinst]->channel); | |
348 | indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */ | |
349 | kfree(interface); | |
350 | kfree(sc_adapter[cinst]); | |
351 | continue; | |
352 | ||
353 | } | |
354 | sc_adapter[cinst]->iobase = io[b]; | |
355 | for(i = 0 ; i < MAX_IO_REGS - 1 ; i++) { | |
356 | sc_adapter[cinst]->ioport[i] = io[b] + i * 0x400; | |
357 | request_region(sc_adapter[cinst]->ioport[i], 1, | |
358 | interface->id); | |
359 | pr_debug("Requesting I/O Port %#x\n", | |
360 | sc_adapter[cinst]->ioport[i]); | |
361 | } | |
362 | sc_adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2; | |
363 | request_region(sc_adapter[cinst]->ioport[IRQ_SELECT], 1, | |
364 | interface->id); | |
365 | pr_debug("Requesting I/O Port %#x\n", | |
366 | sc_adapter[cinst]->ioport[IRQ_SELECT]); | |
367 | sc_adapter[cinst]->rambase = ram[b]; | |
368 | request_region(sc_adapter[cinst]->rambase, SRAM_PAGESIZE, | |
369 | interface->id); | |
370 | ||
371 | pr_info(" %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n", | |
372 | sc_adapter[cinst]->devicename, | |
373 | sc_adapter[cinst]->driverId, | |
374 | boardname[model], channels, irq[b], io[b], ram[b]); | |
375 | ||
376 | /* | |
377 | * reset the adapter to put things in motion | |
378 | */ | |
379 | reset(cinst); | |
380 | ||
381 | cinst++; | |
382 | status = 0; | |
383 | } | |
384 | if (status) | |
385 | pr_info("Failed to find any adapters, driver unloaded\n"); | |
386 | return status; | |
387 | } | |
388 | ||
389 | static void __exit sc_exit(void) | |
390 | { | |
391 | int i, j; | |
392 | ||
393 | for(i = 0 ; i < cinst ; i++) { | |
394 | pr_debug("Cleaning up after adapter %d\n", i); | |
395 | /* | |
396 | * kill the timers | |
397 | */ | |
398 | del_timer(&(sc_adapter[i]->reset_timer)); | |
399 | del_timer(&(sc_adapter[i]->stat_timer)); | |
400 | ||
401 | /* | |
402 | * Tell I4L we're toast | |
403 | */ | |
404 | indicate_status(i, ISDN_STAT_STOP, 0, NULL); | |
405 | indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL); | |
406 | ||
407 | /* | |
408 | * Release shared RAM | |
409 | */ | |
410 | release_region(sc_adapter[i]->rambase, SRAM_PAGESIZE); | |
411 | ||
412 | /* | |
413 | * Release the IRQ | |
414 | */ | |
415 | FREE_IRQ(sc_adapter[i]->interrupt, NULL); | |
416 | ||
417 | /* | |
418 | * Reset for a clean start | |
419 | */ | |
420 | outb(0xFF, sc_adapter[i]->ioport[SFT_RESET]); | |
421 | ||
422 | /* | |
423 | * Release the I/O Port regions | |
424 | */ | |
425 | for(j = 0 ; j < MAX_IO_REGS - 1; j++) { | |
426 | release_region(sc_adapter[i]->ioport[j], 1); | |
427 | pr_debug("Releasing I/O Port %#x\n", | |
428 | sc_adapter[i]->ioport[j]); | |
429 | } | |
430 | release_region(sc_adapter[i]->ioport[IRQ_SELECT], 1); | |
431 | pr_debug("Releasing I/O Port %#x\n", | |
432 | sc_adapter[i]->ioport[IRQ_SELECT]); | |
433 | ||
434 | /* | |
435 | * Release any memory we alloced | |
436 | */ | |
437 | kfree(sc_adapter[i]->channel); | |
438 | kfree(sc_adapter[i]->card); | |
439 | kfree(sc_adapter[i]); | |
440 | } | |
441 | pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n"); | |
442 | } | |
443 | ||
e3ca5e76 | 444 | static int identify_board(unsigned long rambase, unsigned int iobase) |
1da177e4 LT |
445 | { |
446 | unsigned int pgport; | |
447 | unsigned long sig; | |
448 | DualPortMemory *dpm; | |
449 | RspMessage rcvmsg; | |
450 | ReqMessage sndmsg; | |
451 | HWConfig_pl hwci; | |
452 | int x; | |
453 | ||
454 | pr_debug("Attempting to identify adapter @ 0x%x io 0x%x\n", | |
455 | rambase, iobase); | |
456 | ||
457 | /* | |
458 | * Enable the base pointer | |
459 | */ | |
460 | outb(rambase >> 12, iobase + 0x2c00); | |
461 | ||
462 | switch(rambase >> 12 & 0x0F) { | |
463 | case 0x0: | |
464 | pgport = iobase + PG0_OFFSET; | |
465 | pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET); | |
466 | break; | |
467 | ||
468 | case 0x4: | |
469 | pgport = iobase + PG1_OFFSET; | |
470 | pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET); | |
471 | break; | |
472 | ||
473 | case 0x8: | |
474 | pgport = iobase + PG2_OFFSET; | |
475 | pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET); | |
476 | break; | |
477 | ||
478 | case 0xC: | |
479 | pgport = iobase + PG3_OFFSET; | |
480 | pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET); | |
481 | break; | |
482 | default: | |
483 | pr_debug("Invalid rambase 0x%lx\n", rambase); | |
484 | return -1; | |
485 | } | |
486 | ||
487 | /* | |
488 | * Try to identify a PRI card | |
489 | */ | |
490 | outb(PRI_BASEPG_VAL, pgport); | |
491 | msleep_interruptible(1000); | |
492 | sig = readl(rambase + SIG_OFFSET); | |
493 | pr_debug("Looking for a signature, got 0x%x\n", sig); | |
494 | if(sig == SIGNATURE) | |
495 | return PRI_BOARD; | |
496 | ||
497 | /* | |
498 | * Try to identify a PRI card | |
499 | */ | |
500 | outb(BRI_BASEPG_VAL, pgport); | |
501 | msleep_interruptible(1000); | |
502 | sig = readl(rambase + SIG_OFFSET); | |
503 | pr_debug("Looking for a signature, got 0x%x\n", sig); | |
504 | if(sig == SIGNATURE) | |
505 | return BRI_BOARD; | |
506 | ||
507 | return -1; | |
508 | ||
509 | /* | |
510 | * Try to spot a card | |
511 | */ | |
512 | sig = readl(rambase + SIG_OFFSET); | |
513 | pr_debug("Looking for a signature, got 0x%x\n", sig); | |
514 | if(sig != SIGNATURE) | |
515 | return -1; | |
516 | ||
517 | dpm = (DualPortMemory *) rambase; | |
518 | ||
519 | memset(&sndmsg, 0, MSG_LEN); | |
520 | sndmsg.msg_byte_cnt = 3; | |
521 | sndmsg.type = cmReqType1; | |
522 | sndmsg.class = cmReqClass0; | |
523 | sndmsg.code = cmReqHWConfig; | |
524 | memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN); | |
525 | outb(0, iobase + 0x400); | |
526 | pr_debug("Sent HWConfig message\n"); | |
527 | /* | |
528 | * Wait for the response | |
529 | */ | |
530 | x = 0; | |
531 | while((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) { | |
532 | set_current_state(TASK_INTERRUPTIBLE); | |
533 | schedule_timeout(1); | |
534 | x++; | |
535 | } | |
536 | if(x == 100) { | |
537 | pr_debug("Timeout waiting for response\n"); | |
538 | return -1; | |
539 | } | |
540 | ||
541 | memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN); | |
542 | pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status); | |
543 | memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl)); | |
544 | pr_debug("Hardware Config: Interface: %s, RAM Size: %d, Serial: %s\n" | |
545 | " Part: %s, Rev: %s\n", | |
546 | hwci.st_u_sense ? "S/T" : "U", hwci.ram_size, | |
547 | hwci.serial_no, hwci.part_no, hwci.rev_no); | |
548 | ||
549 | if(!strncmp(PRI_PARTNO, hwci.part_no, 6)) | |
550 | return PRI_BOARD; | |
551 | if(!strncmp(BRI_PARTNO, hwci.part_no, 6)) | |
552 | return BRI_BOARD; | |
553 | ||
554 | return -1; | |
555 | } | |
556 | ||
557 | module_init(sc_init); | |
558 | module_exit(sc_exit); |