File: /usr/src/linux/drivers/isdn/avmb1/b1.c

1     /*
2      * $Id: b1.c,v 1.20.6.6 2001/05/17 21:15:33 kai Exp $
3      * 
4      * Common module for AVM B1 cards.
5      * 
6      * (c) Copyright 1999 by Carsten Paeth (calle@calle.in-berlin.de)
7      *
8      */
9     
10     #include <linux/module.h>
11     #include <linux/kernel.h>
12     #include <linux/skbuff.h>
13     #include <linux/delay.h>
14     #include <linux/mm.h>
15     #include <linux/interrupt.h>
16     #include <linux/ioport.h>
17     #include <linux/capi.h>
18     #include <linux/kernelcapi.h>
19     #include <asm/io.h>
20     #include <linux/init.h>
21     #include <asm/uaccess.h>
22     #include <linux/netdevice.h>
23     #include "capilli.h"
24     #include "avmcard.h"
25     #include "capicmd.h"
26     #include "capiutil.h"
27     
28     static char *revision = "$Revision: 1.20.6.6 $";
29     
30     /* ------------------------------------------------------------- */
31     
32     MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
33     
34     /* ------------------------------------------------------------- */
35     
36     int b1_irq_table[16] =
37     {0,
38      0,
39      0,
40      192,				/* irq 3 */
41      32,				/* irq 4 */
42      160,				/* irq 5 */
43      96,				/* irq 6 */
44      224,				/* irq 7 */
45      0,
46      64,				/* irq 9 */
47      80,				/* irq 10 */
48      208,				/* irq 11 */
49      48,				/* irq 12 */
50      0,
51      0,
52      112,				/* irq 15 */
53     };
54     
55     /* ------------------------------------------------------------- */
56     
57     int b1_detect(unsigned int base, enum avmcardtype cardtype)
58     {
59     	int onoff, i;
60     
61     	/*
62     	 * Statusregister 0000 00xx 
63     	 */
64     	if ((inb(base + B1_INSTAT) & 0xfc)
65     	    || (inb(base + B1_OUTSTAT) & 0xfc))
66     		return 1;
67     	/*
68     	 * Statusregister 0000 001x 
69     	 */
70     	b1outp(base, B1_INSTAT, 0x2);	/* enable irq */
71     	/* b1outp(base, B1_OUTSTAT, 0x2); */
72     	if ((inb(base + B1_INSTAT) & 0xfe) != 0x2
73     	    /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */)
74     		return 2;
75     	/*
76     	 * Statusregister 0000 000x 
77     	 */
78     	b1outp(base, B1_INSTAT, 0x0);	/* disable irq */
79     	b1outp(base, B1_OUTSTAT, 0x0);
80     	if ((inb(base + B1_INSTAT) & 0xfe)
81     	    || (inb(base + B1_OUTSTAT) & 0xfe))
82     		return 3;
83             
84     	for (onoff = !0, i= 0; i < 10 ; i++) {
85     		b1_set_test_bit(base, cardtype, onoff);
86     		if (b1_get_test_bit(base, cardtype) != onoff)
87     		   return 4;
88     		onoff = !onoff;
89     	}
90     
91     	if (cardtype == avm_m1)
92     	   return 0;
93     
94             if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01)
95     	   return 5;
96     
97     	return 0;
98     }
99     
100     void b1_getrevision(avmcard *card)
101     {
102         card->class = inb(card->port + B1_ANALYSE);
103         card->revision = inb(card->port + B1_REVISION);
104     }
105     
106     int b1_load_t4file(avmcard *card, capiloaddatapart * t4file)
107     {
108     	unsigned char buf[256];
109     	unsigned char *dp;
110     	int i, left, retval;
111     	unsigned int base = card->port;
112     
113     	dp = t4file->data;
114     	left = t4file->len;
115     	while (left > sizeof(buf)) {
116     		if (t4file->user) {
117     			retval = copy_from_user(buf, dp, sizeof(buf));
118     			if (retval)
119     				return -EFAULT;
120     		} else {
121     			memcpy(buf, dp, sizeof(buf));
122     		}
123     		for (i = 0; i < sizeof(buf); i++)
124     			if (b1_save_put_byte(base, buf[i]) < 0) {
125     				printk(KERN_ERR "%s: corrupted firmware file ?\n",
126     						card->name);
127     				return -EIO;
128     			}
129     		left -= sizeof(buf);
130     		dp += sizeof(buf);
131     	}
132     	if (left) {
133     		if (t4file->user) {
134     			retval = copy_from_user(buf, dp, left);
135     			if (retval)
136     				return -EFAULT;
137     		} else {
138     			memcpy(buf, dp, left);
139     		}
140     		for (i = 0; i < left; i++)
141     			if (b1_save_put_byte(base, buf[i]) < 0) {
142     				printk(KERN_ERR "%s: corrupted firmware file ?\n",
143     						card->name);
144     				return -EIO;
145     			}
146     	}
147     	return 0;
148     }
149     
150     int b1_load_config(avmcard *card, capiloaddatapart * config)
151     {
152     	unsigned char buf[256];
153     	unsigned char *dp;
154     	unsigned int base = card->port;
155     	int i, j, left, retval;
156     
157     	dp = config->data;
158     	left = config->len;
159     	if (left) {
160     		b1_put_byte(base, SEND_CONFIG);
161             	b1_put_word(base, 1);
162     		b1_put_byte(base, SEND_CONFIG);
163             	b1_put_word(base, left);
164     	}
165     	while (left > sizeof(buf)) {
166     		if (config->user) {
167     			retval = copy_from_user(buf, dp, sizeof(buf));
168     			if (retval)
169     				return -EFAULT;
170     		} else {
171     			memcpy(buf, dp, sizeof(buf));
172     		}
173     		for (i = 0; i < sizeof(buf); ) {
174     			b1_put_byte(base, SEND_CONFIG);
175     			for (j=0; j < 4; j++) {
176     				b1_put_byte(base, buf[i++]);
177     			}
178     		}
179     		left -= sizeof(buf);
180     		dp += sizeof(buf);
181     	}
182     	if (left) {
183     		if (config->user) {
184     			retval = copy_from_user(buf, dp, left);
185     			if (retval)
186     				return -EFAULT;
187     		} else {
188     			memcpy(buf, dp, left);
189     		}
190     		for (i = 0; i < left; ) {
191     			b1_put_byte(base, SEND_CONFIG);
192     			for (j=0; j < 4; j++) {
193     				if (i < left)
194     					b1_put_byte(base, buf[i++]);
195     				else
196     					b1_put_byte(base, 0);
197     			}
198     		}
199     	}
200     	return 0;
201     }
202     
203     int b1_loaded(avmcard *card)
204     {
205     	unsigned int base = card->port;
206     	unsigned long stop;
207     	unsigned char ans;
208     	unsigned long tout = 2;
209     
210     	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
211     		if (b1_tx_empty(base))
212     			break;
213     	}
214     	if (!b1_tx_empty(base)) {
215     		printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n",
216     				card->name);
217     		return 0;
218     	}
219     	b1_put_byte(base, SEND_POLL);
220     	for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
221     		if (b1_rx_full(base)) {
222     			if ((ans = b1_get_byte(base)) == RECEIVE_POLL) {
223     				return 1;
224     			}
225     			printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n",
226     					card->name, ans);
227     			return 0;
228     		}
229     	}
230     	printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name);
231     	return 0;
232     }
233     
234     /* ------------------------------------------------------------- */
235     
236     int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
237     {
238     	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
239     	avmcard *card = cinfo->card;
240     	unsigned int port = card->port;
241     	unsigned long flags;
242     	int retval;
243     
244     	b1_reset(port);
245     
246     	if ((retval = b1_load_t4file(card, &data->firmware))) {
247     		b1_reset(port);
248     		printk(KERN_ERR "%s: failed to load t4file!!\n",
249     					card->name);
250     		return retval;
251     	}
252     
253     	b1_disable_irq(port);
254     
255     	if (data->configuration.len > 0 && data->configuration.data) {
256     		if ((retval = b1_load_config(card, &data->configuration))) {
257     			b1_reset(port);
258     			printk(KERN_ERR "%s: failed to load config!!\n",
259     					card->name);
260     			return retval;
261     		}
262     	}
263     
264     	if (!b1_loaded(card)) {
265     		printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
266     		return -EIO;
267     	}
268     
269     	save_flags(flags);
270     	cli();
271     	b1_setinterrupt(port, card->irq, card->cardtype);
272     	b1_put_byte(port, SEND_INIT);
273     	b1_put_word(port, CAPI_MAXAPPL);
274     	b1_put_word(port, AVM_NCCI_PER_CHANNEL*2);
275     	b1_put_word(port, ctrl->cnr - 1);
276     	restore_flags(flags);
277     
278     	return 0;
279     }
280     
281     void b1_reset_ctr(struct capi_ctr *ctrl)
282     {
283     	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
284     	avmcard *card = cinfo->card;
285     	unsigned int port = card->port;
286     
287     	b1_reset(port);
288     	b1_reset(port);
289     
290     	memset(cinfo->version, 0, sizeof(cinfo->version));
291     	ctrl->reseted(ctrl);
292     }
293     
294     void b1_register_appl(struct capi_ctr *ctrl,
295     				__u16 appl,
296     				capi_register_params *rp)
297     {
298     	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
299     	avmcard *card = cinfo->card;
300     	unsigned int port = card->port;
301     	unsigned long flags;
302     	int nconn, want = rp->level3cnt;
303     
304     	if (want > 0) nconn = want;
305     	else nconn = ctrl->profile.nbchannel * -want;
306     	if (nconn == 0) nconn = ctrl->profile.nbchannel;
307     
308     	save_flags(flags);
309     	cli();
310     	b1_put_byte(port, SEND_REGISTER);
311     	b1_put_word(port, appl);
312     	b1_put_word(port, 1024 * (nconn+1));
313     	b1_put_word(port, nconn);
314     	b1_put_word(port, rp->datablkcnt);
315     	b1_put_word(port, rp->datablklen);
316     	restore_flags(flags);
317     
318     	ctrl->appl_registered(ctrl, appl);
319     }
320     
321     void b1_release_appl(struct capi_ctr *ctrl, __u16 appl)
322     {
323     	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
324     	avmcard *card = cinfo->card;
325     	unsigned int port = card->port;
326     	unsigned long flags;
327     
328     	save_flags(flags);
329     	cli();
330     	b1_put_byte(port, SEND_RELEASE);
331     	b1_put_word(port, appl);
332     	restore_flags(flags);
333     }
334     
335     void b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
336     {
337     	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
338     	avmcard *card = cinfo->card;
339     	unsigned int port = card->port;
340     	unsigned long flags;
341     	__u16 len = CAPIMSG_LEN(skb->data);
342     	__u8 cmd = CAPIMSG_COMMAND(skb->data);
343     	__u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
344     
345     	save_flags(flags);
346     	cli();
347     	if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
348     		__u16 dlen = CAPIMSG_DATALEN(skb->data);
349     		b1_put_byte(port, SEND_DATA_B3_REQ);
350     		b1_put_slice(port, skb->data, len);
351     		b1_put_slice(port, skb->data + len, dlen);
352     	} else {
353     		b1_put_byte(port, SEND_MESSAGE);
354     		b1_put_slice(port, skb->data, len);
355     	}
356     	restore_flags(flags);
357     	dev_kfree_skb_any(skb);
358     }
359     
360     /* ------------------------------------------------------------- */
361     
362     void b1_parse_version(avmctrl_info *cinfo)
363     {
364     	struct capi_ctr *ctrl = cinfo->capi_ctrl;
365     	avmcard *card = cinfo->card;
366     	capi_profile *profp;
367     	__u8 *dversion;
368     	__u8 flag;
369     	int i, j;
370     
371     	for (j = 0; j < AVM_MAXVERSION; j++)
372     		cinfo->version[j] = "\0\0" + 1;
373     	for (i = 0, j = 0;
374     	     j < AVM_MAXVERSION && i < cinfo->versionlen;
375     	     j++, i += cinfo->versionbuf[i] + 1)
376     		cinfo->version[j] = &cinfo->versionbuf[i + 1];
377     
378     	strncpy(ctrl->serial, cinfo->version[VER_SERIAL], CAPI_SERIAL_LEN);
379     	memcpy(&ctrl->profile, cinfo->version[VER_PROFILE],sizeof(capi_profile));
380     	strncpy(ctrl->manu, "AVM GmbH", CAPI_MANUFACTURER_LEN);
381     	dversion = cinfo->version[VER_DRIVER];
382     	ctrl->version.majorversion = 2;
383     	ctrl->version.minorversion = 0;
384     	ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4);
385     	ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf);
386     	ctrl->version.minormanuversion = (dversion[3] - '0') << 4;
387     	ctrl->version.minormanuversion |=
388     			(dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf);
389     
390     	profp = &ctrl->profile;
391     
392     	flag = ((__u8 *)(profp->manu))[1];
393     	switch (flag) {
394     	case 0: if (cinfo->version[VER_CARDTYPE])
395     	           strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]);
396     	        else strcpy(cinfo->cardname, "B1");
397     		break;
398     	case 3: strcpy(cinfo->cardname,"PCMCIA B"); break;
399     	case 4: strcpy(cinfo->cardname,"PCMCIA M1"); break;
400     	case 5: strcpy(cinfo->cardname,"PCMCIA M2"); break;
401     	case 6: strcpy(cinfo->cardname,"B1 V3.0"); break;
402     	case 7: strcpy(cinfo->cardname,"B1 PCI"); break;
403     	default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break;
404             }
405             printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n",
406     				card->name, ctrl->cnr, cinfo->cardname);
407     
408             flag = ((__u8 *)(profp->manu))[3];
409             if (flag)
410     		printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n",
411     			card->name,
412     			ctrl->cnr,
413     			(flag & 0x01) ? " DSS1" : "",
414     			(flag & 0x02) ? " CT1" : "",
415     			(flag & 0x04) ? " VN3" : "",
416     			(flag & 0x08) ? " NI1" : "",
417     			(flag & 0x10) ? " AUSTEL" : "",
418     			(flag & 0x20) ? " ESS" : "",
419     			(flag & 0x40) ? " 1TR6" : ""
420     			);
421     
422             flag = ((__u8 *)(profp->manu))[5];
423     	if (flag)
424     		printk(KERN_NOTICE "%s: card %d Linetype:%s%s%s%s\n",
425     			card->name,
426     			ctrl->cnr,
427     			(flag & 0x01) ? " point to point" : "",
428     			(flag & 0x02) ? " point to multipoint" : "",
429     			(flag & 0x08) ? " leased line without D-channel" : "",
430     			(flag & 0x04) ? " leased line with D-channel" : ""
431     			);
432     }
433     
434     /* ------------------------------------------------------------- */
435     
436     void b1_handle_interrupt(avmcard * card)
437     {
438     	avmctrl_info *cinfo = &card->ctrlinfo[0];
439     	struct capi_ctr *ctrl = cinfo->capi_ctrl;
440     	unsigned char b1cmd;
441     	struct sk_buff *skb;
442     
443     	unsigned ApplId;
444     	unsigned MsgLen;
445     	unsigned DataB3Len;
446     	unsigned NCCI;
447     	unsigned WindowSize;
448     
449     	if (!b1_rx_full(card->port))
450     	   return;
451     
452     	b1cmd = b1_get_byte(card->port);
453     
454     	switch (b1cmd) {
455     
456     	case RECEIVE_DATA_B3_IND:
457     
458     		ApplId = (unsigned) b1_get_word(card->port);
459     		MsgLen = b1_get_slice(card->port, card->msgbuf);
460     		DataB3Len = b1_get_slice(card->port, card->databuf);
461     
462     		if (MsgLen < 30) { /* not CAPI 64Bit */
463     			memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
464     			MsgLen = 30;
465     			CAPIMSG_SETLEN(card->msgbuf, 30);
466     		}
467     		if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
468     			printk(KERN_ERR "%s: incoming packet dropped\n",
469     					card->name);
470     		} else {
471     			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
472     			memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
473     			ctrl->handle_capimsg(ctrl, ApplId, skb);
474     		}
475     		break;
476     
477     	case RECEIVE_MESSAGE:
478     
479     		ApplId = (unsigned) b1_get_word(card->port);
480     		MsgLen = b1_get_slice(card->port, card->msgbuf);
481     		if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
482     			printk(KERN_ERR "%s: incoming packet dropped\n",
483     					card->name);
484     		} else {
485     			memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
486     			ctrl->handle_capimsg(ctrl, ApplId, skb);
487     		}
488     		break;
489     
490     	case RECEIVE_NEW_NCCI:
491     
492     		ApplId = b1_get_word(card->port);
493     		NCCI = b1_get_word(card->port);
494     		WindowSize = b1_get_word(card->port);
495     
496     		ctrl->new_ncci(ctrl, ApplId, NCCI, WindowSize);
497     
498     		break;
499     
500     	case RECEIVE_FREE_NCCI:
501     
502     		ApplId = b1_get_word(card->port);
503     		NCCI = b1_get_word(card->port);
504     
505     		if (NCCI != 0xffffffff)
506     			ctrl->free_ncci(ctrl, ApplId, NCCI);
507     		else ctrl->appl_released(ctrl, ApplId);
508     		break;
509     
510     	case RECEIVE_START:
511     	   	/* b1_put_byte(card->port, SEND_POLLACK); */
512     		ctrl->resume_output(ctrl);
513     		break;
514     
515     	case RECEIVE_STOP:
516     		ctrl->suspend_output(ctrl);
517     		break;
518     
519     	case RECEIVE_INIT:
520     
521     		cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf);
522     		b1_parse_version(cinfo);
523     		printk(KERN_INFO "%s: %s-card (%s) now active\n",
524     		       card->name,
525     		       cinfo->version[VER_CARDTYPE],
526     		       cinfo->version[VER_DRIVER]);
527     		ctrl->ready(ctrl);
528     		break;
529     
530     	case RECEIVE_TASK_READY:
531     		ApplId = (unsigned) b1_get_word(card->port);
532     		MsgLen = b1_get_slice(card->port, card->msgbuf);
533     		card->msgbuf[MsgLen] = 0;
534     		while (    MsgLen > 0
535     		       && (   card->msgbuf[MsgLen-1] == '\n'
536     			   || card->msgbuf[MsgLen-1] == '\r')) {
537     			card->msgbuf[MsgLen-1] = 0;
538     			MsgLen--;
539     		}
540     		printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
541     				card->name, ApplId, card->msgbuf);
542     		break;
543     
544     	case RECEIVE_DEBUGMSG:
545     		MsgLen = b1_get_slice(card->port, card->msgbuf);
546     		card->msgbuf[MsgLen] = 0;
547     		while (    MsgLen > 0
548     		       && (   card->msgbuf[MsgLen-1] == '\n'
549     			   || card->msgbuf[MsgLen-1] == '\r')) {
550     			card->msgbuf[MsgLen-1] = 0;
551     			MsgLen--;
552     		}
553     		printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
554     		break;
555     
556     	case 0xff:
557     		printk(KERN_ERR "%s: card removed ?\n", card->name);
558     		return;
559     	default:
560     		printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
561     				card->name, b1cmd);
562     		return;
563     	}
564     }
565     
566     /* ------------------------------------------------------------- */
567     int b1ctl_read_proc(char *page, char **start, off_t off,
568             		int count, int *eof, struct capi_ctr *ctrl)
569     {
570     	avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
571     	avmcard *card = cinfo->card;
572     	__u8 flag;
573     	int len = 0;
574     	char *s;
575     
576     	len += sprintf(page+len, "%-16s %s\n", "name", card->name);
577     	len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
578     	len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
579     	switch (card->cardtype) {
580     	case avm_b1isa: s = "B1 ISA"; break;
581     	case avm_b1pci: s = "B1 PCI"; break;
582     	case avm_b1pcmcia: s = "B1 PCMCIA"; break;
583     	case avm_m1: s = "M1"; break;
584     	case avm_m2: s = "M2"; break;
585     	case avm_t1isa: s = "T1 ISA (HEMA)"; break;
586     	case avm_t1pci: s = "T1 PCI"; break;
587     	case avm_c4: s = "C4"; break;
588     	case avm_c2: s = "C2"; break;
589     	default: s = "???"; break;
590     	}
591     	len += sprintf(page+len, "%-16s %s\n", "type", s);
592     	if (card->cardtype == avm_t1isa)
593     	   len += sprintf(page+len, "%-16s %d\n", "cardnr", card->cardnr);
594     	if ((s = cinfo->version[VER_DRIVER]) != 0)
595     	   len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
596     	if ((s = cinfo->version[VER_CARDTYPE]) != 0)
597     	   len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
598     	if ((s = cinfo->version[VER_SERIAL]) != 0)
599     	   len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
600     
601     	if (card->cardtype != avm_m1) {
602             	flag = ((__u8 *)(ctrl->profile.manu))[3];
603             	if (flag)
604     			len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
605     			"protocol",
606     			(flag & 0x01) ? " DSS1" : "",
607     			(flag & 0x02) ? " CT1" : "",
608     			(flag & 0x04) ? " VN3" : "",
609     			(flag & 0x08) ? " NI1" : "",
610     			(flag & 0x10) ? " AUSTEL" : "",
611     			(flag & 0x20) ? " ESS" : "",
612     			(flag & 0x40) ? " 1TR6" : ""
613     			);
614     	}
615     	if (card->cardtype != avm_m1) {
616             	flag = ((__u8 *)(ctrl->profile.manu))[5];
617     		if (flag)
618     			len += sprintf(page+len, "%-16s%s%s%s%s\n",
619     			"linetype",
620     			(flag & 0x01) ? " point to point" : "",
621     			(flag & 0x02) ? " point to multipoint" : "",
622     			(flag & 0x08) ? " leased line without D-channel" : "",
623     			(flag & 0x04) ? " leased line with D-channel" : ""
624     			);
625     	}
626     	len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
627     
628     	if (off+count >= len)
629     	   *eof = 1;
630     	if (len < off)
631                return 0;
632     	*start = page + off;
633     	return ((count < len-off) ? count : len-off);
634     }
635     
636     /* ------------------------------------------------------------- */
637     
638     EXPORT_SYMBOL(b1_irq_table);
639     
640     EXPORT_SYMBOL(b1_detect);
641     EXPORT_SYMBOL(b1_getrevision);
642     EXPORT_SYMBOL(b1_load_t4file);
643     EXPORT_SYMBOL(b1_load_config);
644     EXPORT_SYMBOL(b1_loaded);
645     EXPORT_SYMBOL(b1_load_firmware);
646     EXPORT_SYMBOL(b1_reset_ctr);
647     EXPORT_SYMBOL(b1_register_appl);
648     EXPORT_SYMBOL(b1_release_appl);
649     EXPORT_SYMBOL(b1_send_message);
650     
651     EXPORT_SYMBOL(b1_parse_version);
652     EXPORT_SYMBOL(b1_handle_interrupt);
653     
654     EXPORT_SYMBOL(b1ctl_read_proc);
655     
656     static int __init b1_init(void)
657     {
658     	char *p;
659     	char rev[32];
660     
661     	if ((p = strchr(revision, ':')) != 0 && p[1]) {
662     		strncpy(rev, p + 2, sizeof(rev));
663     		rev[sizeof(rev)-1] = 0;
664     		if ((p = strchr(rev, '$')) != 0 && p > rev)
665     		   *(p-1) = 0;
666     	} else
667     		strcpy(rev, "1.0");
668     
669     	printk(KERN_INFO "b1: revision %s\n", rev);
670     
671     	return 0;
672     }
673     
674     static void __exit b1_exit(void)
675     {
676     }
677     
678     module_init(b1_init);
679     module_exit(b1_exit);
680