1265 lines
31 KiB
C
1265 lines
31 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
* CAPI 2.0 Module for AVM B1-card.
|
|
*
|
|
* (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
|
|
*
|
|
* $Log$
|
|
* Revision 1.13 1999/01/05 18:29:31 he
|
|
* merged remaining schedule_timeout() changes from 2.1.127
|
|
*
|
|
* Revision 1.12 1998/10/25 14:38:58 fritz
|
|
* Backported from MIPS (Cobalt).
|
|
*
|
|
* Revision 1.11 1998/03/29 16:05:58 calle
|
|
* changes from 2.0 tree merged.
|
|
*
|
|
* Revision 1.4.2.18 1998/03/20 20:34:37 calle
|
|
* port valid check now only for T1, because of the PCI and PCMCIA cards.
|
|
*
|
|
* Revision 1.4.2.17 1998/03/20 14:38:17 calle
|
|
* capidrv: prepared state machines for suspend/resume/hold
|
|
* capidrv: fix bug in state machine if B1/T1 is out of nccis
|
|
* b1capi: changed some errno returns.
|
|
* b1capi: detect if you try to add same T1 to different io address.
|
|
* b1capi: change number of nccis depending on number of channels.
|
|
* b1lli: cosmetics
|
|
*
|
|
* Revision 1.4.2.16 1998/03/20 09:01:08 calle
|
|
* Changes capi_register handling to get full support for 30 bchannels.
|
|
*
|
|
* Revision 1.4.2.15 1998/03/18 17:43:26 calle
|
|
* T1 with fastlink, bugfix for multicontroller support in capidrv.c
|
|
*
|
|
* Revision 1.4.2.14 1998/03/04 17:33:47 calle
|
|
* Changes for T1.
|
|
*
|
|
* Revision 1.4.2.13 1998/02/27 15:40:41 calle
|
|
* T1 running with slow link. bugfix in capi_release.
|
|
*
|
|
* Revision 1.4.2.12 1998/02/24 17:58:25 calle
|
|
* changes for T1.
|
|
*
|
|
* Revision 1.10 1998/02/13 07:09:10 calle
|
|
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
|
|
*
|
|
* Revision 1.9 1998/01/31 11:14:39 calle
|
|
* merged changes to 2.0 tree, prepare 2.1.82 to work.
|
|
*
|
|
* Revision 1.8 1997/12/10 20:00:46 calle
|
|
* get changes from 2.0 version
|
|
*
|
|
* Revision 1.4.2.5 1997/12/07 19:59:54 calle
|
|
* more changes for M1/T1/B1 + config
|
|
*
|
|
* Revision 1.4.2.4 1997/11/26 16:57:20 calle
|
|
* more changes for B1/M1/T1.
|
|
*
|
|
* Revision 1.7 1997/10/19 14:45:40 calle
|
|
* fixed capi_get_version.
|
|
*
|
|
* Revision 1.6 1997/10/01 09:21:09 fritz
|
|
* Removed old compatibility stuff for 2.0.X kernels.
|
|
* From now on, this code is for 2.1.X ONLY!
|
|
* Old stuff is still in the separate branch.
|
|
*
|
|
* Revision 1.5 1997/07/12 08:22:26 calle
|
|
* Correct bug in CARD_NR macro, so now more than one card will work.
|
|
* Allow card reset, even if card is in running state.
|
|
*
|
|
*
|
|
* Revision 1.4 1997/05/27 15:17:45 fritz
|
|
* Added changes for recent 2.1.x kernels:
|
|
* changed return type of isdn_close
|
|
* queue_task_* -> queue_task
|
|
* clear/set_bit -> test_and_... where apropriate.
|
|
* changed type of hard_header_cache parameter.
|
|
*
|
|
* Revision 1.3 1997/05/18 09:24:09 calle
|
|
* added verbose disconnect reason reporting to avmb1.
|
|
* some fixes in capi20 interface.
|
|
* changed info messages for B1-PCI
|
|
*
|
|
* Revision 1.2 1997/03/05 21:20:41 fritz
|
|
* Removed include of config.h (mkdep stated this is unneded).
|
|
*
|
|
* Revision 1.1 1997/03/04 21:50:27 calle
|
|
* Frirst version in isdn4linux
|
|
*
|
|
* Revision 2.2 1997/02/12 09:31:39 calle
|
|
* new version
|
|
*
|
|
* Revision 1.1 1997/01/31 10:32:20 calle
|
|
* Initial revision
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <asm/segment.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/tqueue.h>
|
|
#include <linux/capi.h>
|
|
#include <linux/b1lli.h>
|
|
#include <linux/kernelcapi.h>
|
|
#include "compat.h"
|
|
#include "capicmd.h"
|
|
#include "capiutil.h"
|
|
|
|
static char *revision = "$Revision$";
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
int showcapimsgs = 0; /* used in lli.c */
|
|
int loaddebug = 0;
|
|
|
|
MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
|
|
MODULE_PARM(showcapimsgs, "0-5i");
|
|
MODULE_PARM(loaddebug, "0-1i");
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
struct msgidqueue {
|
|
struct msgidqueue *next;
|
|
__u16 msgid;
|
|
};
|
|
|
|
typedef struct avmb1_ncci {
|
|
struct avmb1_ncci *next;
|
|
__u16 applid;
|
|
__u32 ncci;
|
|
__u32 winsize;
|
|
struct msgidqueue *msgidqueue;
|
|
struct msgidqueue *msgidlast;
|
|
struct msgidqueue *msgidfree;
|
|
struct msgidqueue msgidpool[CAPI_MAXDATAWINDOW];
|
|
} avmb1_ncci;
|
|
|
|
typedef struct avmb1_appl {
|
|
__u16 applid;
|
|
capi_register_params rparam;
|
|
int releasing;
|
|
__u32 param;
|
|
void (*signal) (__u16 applid, __u32 param);
|
|
struct sk_buff_head recv_queue;
|
|
struct avmb1_ncci *nccilist;
|
|
} avmb1_appl;
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
static struct capi_version driver_version = {2, 0, 1, 1<<4};
|
|
static char driver_serial[CAPI_SERIAL_LEN] = "4711";
|
|
static char capi_manufakturer[64] = "AVM Berlin";
|
|
|
|
#define APPL(a) (&applications[(a)-1])
|
|
#define VALID_APPLID(a) ((a) && (a) <= CAPI_MAXAPPL && APPL(a)->applid == a)
|
|
#define APPL_IS_FREE(a) (APPL(a)->applid == 0)
|
|
#define APPL_MARK_FREE(a) do{ APPL(a)->applid=0; MOD_DEC_USE_COUNT; }while(0);
|
|
#define APPL_MARK_USED(a) do{ APPL(a)->applid=(a); MOD_INC_USE_COUNT; }while(0);
|
|
|
|
#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f)
|
|
|
|
#define VALID_CARD(c) ((c) > 0 && (c) <= CAPI_MAXCONTR)
|
|
#define CARD(c) (&cards[(c)-1])
|
|
#define CARDNR(cp) (((cp)-cards)+1)
|
|
|
|
static avmb1_appl applications[CAPI_MAXAPPL];
|
|
static avmb1_card cards[CAPI_MAXCONTR];
|
|
static int ncards = 0;
|
|
static struct sk_buff_head recv_queue;
|
|
static struct capi_interface_user *capi_users = 0;
|
|
static long notify_up_set = 0;
|
|
static long notify_down_set = 0;
|
|
|
|
static struct tq_struct tq_state_notify;
|
|
static struct tq_struct tq_recv_notify;
|
|
|
|
/* -------- util functions ------------------------------------ */
|
|
|
|
static char *cardtype2str(int cardtype)
|
|
{
|
|
switch (cardtype) {
|
|
default:
|
|
case AVM_CARDTYPE_B1: return "B1-ISA";
|
|
case AVM_CARDTYPE_B1PCI: return "B1-PCI";
|
|
case AVM_CARDTYPE_M1: return "M1";
|
|
case AVM_CARDTYPE_M2: return "M2";
|
|
case AVM_CARDTYPE_T1: return "T1";
|
|
}
|
|
}
|
|
|
|
static inline int capi_cmd_valid(__u8 cmd)
|
|
{
|
|
switch (cmd) {
|
|
case CAPI_ALERT:
|
|
case CAPI_CONNECT:
|
|
case CAPI_CONNECT_ACTIVE:
|
|
case CAPI_CONNECT_B3_ACTIVE:
|
|
case CAPI_CONNECT_B3:
|
|
case CAPI_CONNECT_B3_T90_ACTIVE:
|
|
case CAPI_DATA_B3:
|
|
case CAPI_DISCONNECT_B3:
|
|
case CAPI_DISCONNECT:
|
|
case CAPI_FACILITY:
|
|
case CAPI_INFO:
|
|
case CAPI_LISTEN:
|
|
case CAPI_MANUFACTURER:
|
|
case CAPI_RESET_B3:
|
|
case CAPI_SELECT_B_PROTOCOL:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline int capi_subcmd_valid(__u8 subcmd)
|
|
{
|
|
switch (subcmd) {
|
|
case CAPI_REQ:
|
|
case CAPI_CONF:
|
|
case CAPI_IND:
|
|
case CAPI_RESP:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------- NCCI Handling ------------------------------------- */
|
|
|
|
static inline void mq_init(avmb1_ncci * np)
|
|
{
|
|
int i;
|
|
np->msgidqueue = 0;
|
|
np->msgidlast = 0;
|
|
memset(np->msgidpool, 0, sizeof(np->msgidpool));
|
|
np->msgidfree = &np->msgidpool[0];
|
|
for (i = 1; i < np->winsize; i++) {
|
|
np->msgidpool[i].next = np->msgidfree;
|
|
np->msgidfree = &np->msgidpool[i];
|
|
}
|
|
}
|
|
|
|
static inline int mq_enqueue(avmb1_ncci * np, __u16 msgid)
|
|
{
|
|
struct msgidqueue *mq;
|
|
if ((mq = np->msgidfree) == 0)
|
|
return 0;
|
|
np->msgidfree = mq->next;
|
|
mq->msgid = msgid;
|
|
mq->next = 0;
|
|
if (np->msgidlast)
|
|
np->msgidlast->next = mq;
|
|
np->msgidlast = mq;
|
|
if (!np->msgidqueue)
|
|
np->msgidqueue = mq;
|
|
return 1;
|
|
}
|
|
|
|
static inline int mq_dequeue(avmb1_ncci * np, __u16 msgid)
|
|
{
|
|
struct msgidqueue **pp;
|
|
for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) {
|
|
if ((*pp)->msgid == msgid) {
|
|
struct msgidqueue *mq = *pp;
|
|
*pp = mq->next;
|
|
if (mq == np->msgidlast)
|
|
np->msgidlast = 0;
|
|
mq->next = np->msgidfree;
|
|
np->msgidfree = mq;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void avmb1_handle_new_ncci(avmb1_card * card,
|
|
__u16 appl, __u32 ncci, __u32 winsize)
|
|
{
|
|
avmb1_ncci *np;
|
|
if (!VALID_APPLID(appl)) {
|
|
printk(KERN_ERR "avmb1_handle_new_ncci: illegal appl %d\n", appl);
|
|
return;
|
|
}
|
|
if ((np = (avmb1_ncci *) kmalloc(sizeof(avmb1_ncci), GFP_ATOMIC)) == 0) {
|
|
printk(KERN_ERR "avmb1_handle_new_ncci: alloc failed ncci 0x%x\n", ncci);
|
|
return;
|
|
}
|
|
if (winsize > CAPI_MAXDATAWINDOW) {
|
|
printk(KERN_ERR "avmb1_handle_new_ncci: winsize %d too big, set to %d\n",
|
|
winsize, CAPI_MAXDATAWINDOW);
|
|
winsize = CAPI_MAXDATAWINDOW;
|
|
}
|
|
np->applid = appl;
|
|
np->ncci = ncci;
|
|
np->winsize = winsize;
|
|
mq_init(np);
|
|
np->next = APPL(appl)->nccilist;
|
|
APPL(appl)->nccilist = np;
|
|
printk(KERN_INFO "b1capi: appl %d ncci 0x%x up\n", appl, ncci);
|
|
|
|
}
|
|
|
|
void avmb1_handle_free_ncci(avmb1_card * card,
|
|
__u16 appl, __u32 ncci)
|
|
{
|
|
if (!VALID_APPLID(appl)) {
|
|
printk(KERN_ERR "avmb1_handle_free_ncci: illegal appl %d\n", appl);
|
|
return;
|
|
}
|
|
if (ncci != 0xffffffff) {
|
|
avmb1_ncci **pp;
|
|
for (pp = &APPL(appl)->nccilist; *pp; pp = &(*pp)->next) {
|
|
if ((*pp)->ncci == ncci) {
|
|
avmb1_ncci *np = *pp;
|
|
*pp = np->next;
|
|
kfree(np);
|
|
printk(KERN_INFO "b1capi: appl %d ncci 0x%x down\n", appl, ncci);
|
|
return;
|
|
}
|
|
}
|
|
printk(KERN_ERR "avmb1_handle_free_ncci: ncci 0x%x not found\n", ncci);
|
|
} else {
|
|
avmb1_ncci **pp, **nextpp;
|
|
for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) {
|
|
if (NCCI2CTRL((*pp)->ncci) == card->cnr) {
|
|
avmb1_ncci *np = *pp;
|
|
*pp = np->next;
|
|
printk(KERN_INFO "b1capi: appl %d ncci 0x%x down!\n", appl, np->ncci);
|
|
kfree(np);
|
|
nextpp = pp;
|
|
} else {
|
|
nextpp = &(*pp)->next;
|
|
}
|
|
}
|
|
APPL(appl)->releasing--;
|
|
if (APPL(appl)->releasing <= 0) {
|
|
APPL(appl)->signal = 0;
|
|
APPL_MARK_FREE(appl);
|
|
printk(KERN_INFO "b1capi: appl %d down\n", appl);
|
|
}
|
|
}
|
|
}
|
|
|
|
static avmb1_ncci *find_ncci(avmb1_appl * app, __u32 ncci)
|
|
{
|
|
avmb1_ncci *np;
|
|
for (np = app->nccilist; np; np = np->next) {
|
|
if (np->ncci == ncci)
|
|
return np;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* -------- Receiver ------------------------------------------ */
|
|
|
|
|
|
static void recv_handler(void *dummy)
|
|
{
|
|
struct sk_buff *skb;
|
|
|
|
while ((skb = skb_dequeue(&recv_queue)) != 0) {
|
|
__u16 appl = CAPIMSG_APPID(skb->data);
|
|
struct avmb1_ncci *np;
|
|
if (!VALID_APPLID(appl)) {
|
|
printk(KERN_ERR "b1capi: recv_handler: applid %d ? (%s)\n",
|
|
appl, capi_message2str(skb->data));
|
|
kfree_skb(skb);
|
|
continue;
|
|
}
|
|
if (APPL(appl)->signal == 0) {
|
|
printk(KERN_ERR "b1capi: recv_handler: applid %d has no signal function\n",
|
|
appl);
|
|
kfree_skb(skb);
|
|
continue;
|
|
}
|
|
if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
|
|
&& CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF
|
|
&& (np = find_ncci(APPL(appl), CAPIMSG_NCCI(skb->data))) != 0
|
|
&& mq_dequeue(np, CAPIMSG_MSGID(skb->data)) == 0) {
|
|
printk(KERN_ERR "b1capi: msgid %hu ncci 0x%x not on queue\n",
|
|
CAPIMSG_MSGID(skb->data), np->ncci);
|
|
}
|
|
skb_queue_tail(&APPL(appl)->recv_queue, skb);
|
|
(APPL(appl)->signal) (APPL(appl)->applid, APPL(appl)->param);
|
|
}
|
|
}
|
|
|
|
|
|
void avmb1_handle_capimsg(avmb1_card * card, __u16 appl, struct sk_buff *skb)
|
|
{
|
|
if (card->cardstate != CARD_RUNNING) {
|
|
printk(KERN_INFO "b1capi: controller %d not active, got: %s",
|
|
card->cnr, capi_message2str(skb->data));
|
|
goto error;
|
|
return;
|
|
}
|
|
skb_queue_tail(&recv_queue, skb);
|
|
queue_task(&tq_recv_notify, &tq_immediate);
|
|
mark_bh(IMMEDIATE_BH);
|
|
return;
|
|
|
|
error:
|
|
kfree_skb(skb);
|
|
}
|
|
|
|
void avmb1_interrupt(int interrupt, void *devptr, struct pt_regs *regs)
|
|
{
|
|
avmb1_card *card;
|
|
|
|
card = (avmb1_card *) devptr;
|
|
|
|
if (!card) {
|
|
printk(KERN_WARNING "avmb1_interrupt: wrong device\n");
|
|
return;
|
|
}
|
|
if (card->interrupt) {
|
|
printk(KERN_ERR "avmb1_interrupt: reentering interrupt hander\n");
|
|
return;
|
|
}
|
|
|
|
card->interrupt = 1;
|
|
|
|
B1_handle_interrupt(card);
|
|
|
|
card->interrupt = 0;
|
|
}
|
|
|
|
/* -------- Notifier ------------------------------------------ */
|
|
|
|
static void notify_up(__u16 contr)
|
|
{
|
|
struct capi_interface_user *p;
|
|
|
|
printk(KERN_NOTICE "b1capi: notify up contr %d\n", contr);
|
|
for (p = capi_users; p; p = p->next) {
|
|
if (p->callback)
|
|
(*p->callback) (KCI_CONTRUP, contr,
|
|
(capi_profile *)
|
|
CARD(contr)->version[VER_PROFILE]);
|
|
}
|
|
}
|
|
|
|
static void notify_down(__u16 contr)
|
|
{
|
|
struct capi_interface_user *p;
|
|
printk(KERN_NOTICE "b1capi: notify down contr %d\n", contr);
|
|
for (p = capi_users; p; p = p->next) {
|
|
if (p->callback)
|
|
(*p->callback) (KCI_CONTRDOWN, contr, 0);
|
|
}
|
|
}
|
|
|
|
static void notify_handler(void *dummy)
|
|
{
|
|
__u16 contr;
|
|
|
|
for (contr=1; VALID_CARD(contr); contr++)
|
|
if (test_and_clear_bit(contr, ¬ify_up_set))
|
|
notify_up(contr);
|
|
for (contr=1; VALID_CARD(contr); contr++)
|
|
if (test_and_clear_bit(contr, ¬ify_down_set))
|
|
notify_down(contr);
|
|
}
|
|
|
|
/* -------- card ready callback ------------------------------- */
|
|
|
|
|
|
void avmb1_card_ready(avmb1_card * card)
|
|
{
|
|
struct capi_profile *profp =
|
|
(struct capi_profile *)card->version[VER_PROFILE];
|
|
char *dversion = card->version[VER_DRIVER];
|
|
__u16 appl;
|
|
char *cardname, cname[20];
|
|
__u32 flag;
|
|
int nbchan = profp->nbchannel;
|
|
|
|
card->cversion.majorversion = 2;
|
|
card->cversion.minorversion = 0;
|
|
card->cversion.majormanuversion = (((dversion[0] - '0') & 0xf) << 4);
|
|
card->cversion.majormanuversion |= ((dversion[2] - '0') & 0xf);
|
|
card->cversion.minormanuversion = (dversion[3] - '0') << 4;
|
|
card->cversion.minormanuversion |=
|
|
(dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf);
|
|
card->cardstate = CARD_RUNNING;
|
|
|
|
for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
|
|
if (VALID_APPLID(appl) && !APPL(appl)->releasing) {
|
|
int nconn, want = APPL(appl)->rparam.level3cnt;
|
|
|
|
if (want > 0) nconn = want;
|
|
else nconn = nbchan * -want;
|
|
if (nconn == 0) nconn = nbchan;
|
|
|
|
B1_send_register(card->port, appl,
|
|
1024 * (nconn+1), nconn,
|
|
APPL(appl)->rparam.datablkcnt,
|
|
APPL(appl)->rparam.datablklen);
|
|
}
|
|
}
|
|
|
|
set_bit(CARDNR(card), ¬ify_up_set);
|
|
queue_task(&tq_state_notify, &tq_scheduler);
|
|
|
|
flag = ((__u8 *)(profp->manu))[1];
|
|
switch (flag) {
|
|
case 0: cardname = cardtype2str(card->cardtype); break;
|
|
case 3: cardname = "PCMCIA B"; break;
|
|
case 4: cardname = "PCMCIA M1"; break;
|
|
case 5: cardname = "PCMCIA M2"; break;
|
|
case 6: cardname = "B1 V3.0"; break;
|
|
case 7: cardname = "B1 PCI"; break;
|
|
default: cardname = cname; break;
|
|
sprintf(cname, "AVM?%u", (unsigned int)flag);
|
|
break;
|
|
}
|
|
printk(KERN_NOTICE "b1capi: card %d \"%s\" ready.\n",
|
|
CARDNR(card), cardname);
|
|
flag = ((__u8 *)(profp->manu))[3];
|
|
if (flag)
|
|
printk(KERN_NOTICE "b1capi: card %d Protocol:%s%s%s%s%s%s%s\n",
|
|
CARDNR(card),
|
|
(flag & 0x01) ? " DSS1" : "",
|
|
(flag & 0x02) ? " CT1" : "",
|
|
(flag & 0x04) ? " VN3" : "",
|
|
(flag & 0x08) ? " NI1" : "",
|
|
(flag & 0x10) ? " AUSTEL" : "",
|
|
(flag & 0x20) ? " ESS" : "",
|
|
(flag & 0x40) ? " 1TR6" : ""
|
|
);
|
|
flag = ((__u8 *)(profp->manu))[5];
|
|
if (flag)
|
|
printk(KERN_NOTICE "b1capi: card %d Linetype:%s%s%s%s\n",
|
|
CARDNR(card),
|
|
(flag & 0x01) ? " point to point" : "",
|
|
(flag & 0x02) ? " point to multipoint" : "",
|
|
(flag & 0x08) ? " leased line without D-channel" : "",
|
|
(flag & 0x04) ? " leased line with D-channel" : ""
|
|
);
|
|
}
|
|
|
|
static void avmb1_card_down(avmb1_card * card, int notify)
|
|
{
|
|
__u16 appl;
|
|
|
|
card->cardstate = CARD_DETECTED;
|
|
|
|
for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
|
|
avmb1_ncci **pp, **nextpp;
|
|
for (pp = &APPL(appl)->nccilist; *pp; pp = nextpp) {
|
|
if (NCCI2CTRL((*pp)->ncci) == card->cnr) {
|
|
avmb1_ncci *np = *pp;
|
|
*pp = np->next;
|
|
printk(KERN_INFO "b1capi: appl %d ncci 0x%x forced down!\n", appl, np->ncci);
|
|
kfree(np);
|
|
nextpp = pp;
|
|
} else {
|
|
nextpp = &(*pp)->next;
|
|
}
|
|
}
|
|
}
|
|
set_bit(CARDNR(card), ¬ify_down_set);
|
|
queue_task(&tq_state_notify, &tq_scheduler);
|
|
printk(KERN_NOTICE "b1capi: card %d down.\n", CARDNR(card));
|
|
}
|
|
|
|
/* ------------------------------------------------------------- */
|
|
|
|
|
|
int avmb1_registercard(int port, int irq, int cardtype, int allocio)
|
|
{
|
|
struct avmb1_card *card;
|
|
int irqval,i;
|
|
|
|
|
|
for (i=0; i < CAPI_MAXCONTR && cards[i].cardstate != CARD_FREE; i++) ;
|
|
|
|
if (i == CAPI_MAXCONTR) {
|
|
printk(KERN_ERR "b1capi: out of controller slots\n");
|
|
return -ENFILE;
|
|
}
|
|
|
|
card = &cards[i];
|
|
memset(card, 0, sizeof(avmb1_card));
|
|
sprintf(card->name, "avmb1-%d", CARDNR(card));
|
|
|
|
if (allocio)
|
|
request_region(port, AVMB1_PORTLEN, card->name);
|
|
|
|
if ((irqval = request_irq(irq, avmb1_interrupt,
|
|
SA_SHIRQ, card->name, card)) != 0) {
|
|
printk(KERN_ERR "b1capi: unable to get IRQ %d (irqval=%d).\n",
|
|
irq, irqval);
|
|
release_region(port, AVMB1_PORTLEN);
|
|
return -EBUSY;
|
|
}
|
|
|
|
card->cardstate = CARD_DETECTED;
|
|
ncards++;
|
|
card->cnr = CARDNR(card);
|
|
card->port = port;
|
|
card->irq = irq;
|
|
card->cardtype = cardtype;
|
|
return card->cnr;
|
|
}
|
|
|
|
int avmb1_addcard(int port, int irq, int cardtype)
|
|
{
|
|
return avmb1_registercard(port, irq, cardtype, 1);
|
|
}
|
|
|
|
int avmb1_detectcard(int port, int irq, int cardtype)
|
|
{
|
|
int rc;
|
|
|
|
if (!B1_valid_irq(irq, cardtype)) {
|
|
printk(KERN_WARNING "b1capi: irq %d not valid for %s-card.\n",
|
|
irq, cardtype2str(cardtype));
|
|
return -EINVAL;
|
|
}
|
|
if (!B1_valid_port(port, cardtype)) {
|
|
printk(KERN_WARNING "b1capi: port 0x%x not valid for %s-card.\n",
|
|
port, cardtype2str(cardtype));
|
|
return -EINVAL;
|
|
}
|
|
B1_reset(port);
|
|
if ((rc = B1_detect(port, cardtype)) != 0) {
|
|
printk(KERN_NOTICE "b1capi: NO %s-card at 0x%x (%d)\n",
|
|
cardtype2str(cardtype), port, rc);
|
|
return -EIO;
|
|
}
|
|
B1_reset(port);
|
|
switch (cardtype) {
|
|
default:
|
|
case AVM_CARDTYPE_M1:
|
|
case AVM_CARDTYPE_M2:
|
|
case AVM_CARDTYPE_B1:
|
|
case AVM_CARDTYPE_B1PCI:
|
|
printk(KERN_NOTICE "b1capi: AVM-%s-Controller detected at 0x%x\n", cardtype2str(cardtype), port);
|
|
break;
|
|
case AVM_CARDTYPE_T1:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int avmb1_probecard(int port, int irq, int cardtype)
|
|
{
|
|
if (check_region(port, AVMB1_PORTLEN)) {
|
|
printk(KERN_WARNING
|
|
"b1capi: ports 0x%03x-0x%03x in use.\n",
|
|
port, port + AVMB1_PORTLEN);
|
|
return -EBUSY;
|
|
}
|
|
return avmb1_detectcard(port, irq, cardtype);
|
|
}
|
|
|
|
int avmb1_unregistercard(int cnr, int freeio)
|
|
{
|
|
avmb1_card * card;
|
|
if (!VALID_CARD(cnr))
|
|
return -ESRCH;
|
|
card = CARD(cnr);
|
|
|
|
if (card->cardstate == CARD_FREE)
|
|
return -ESRCH;
|
|
if (card->cardstate == CARD_RUNNING)
|
|
avmb1_card_down(card, freeio);
|
|
|
|
if (card->cardstate != CARD_FREE)
|
|
if (card->cardtype == AVM_CARDTYPE_T1)
|
|
T1_reset(card->port);
|
|
|
|
free_irq(card->irq, card);
|
|
if (freeio)
|
|
release_region(card->port, AVMB1_PORTLEN);
|
|
card->cardstate = CARD_FREE;
|
|
return 0;
|
|
}
|
|
|
|
int avmb1_resetcard(int cnr)
|
|
{
|
|
avmb1_card * card;
|
|
|
|
if (!VALID_CARD(cnr))
|
|
return -ESRCH;
|
|
card = CARD(cnr);
|
|
if (card->cardstate == CARD_FREE)
|
|
return -ESRCH;
|
|
|
|
if (card->cardstate == CARD_RUNNING)
|
|
avmb1_card_down(card, 0);
|
|
|
|
B1_reset(card->port);
|
|
B1_reset(card->port);
|
|
|
|
card->cardstate = CARD_DETECTED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ------------------------------------------------------------- */
|
|
/* -------- CAPI2.0 Interface ---------------------------------- */
|
|
/* ------------------------------------------------------------- */
|
|
|
|
static int capi_installed(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < CAPI_MAXCONTR; i++) {
|
|
if (cards[i].cardstate == CARD_RUNNING)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static __u16 capi_register(capi_register_params * rparam, __u16 * applidp)
|
|
{
|
|
int nconn, want = rparam->level3cnt;
|
|
int i;
|
|
int appl;
|
|
|
|
if (rparam->datablklen < 128)
|
|
return CAPI_LOGBLKSIZETOSMALL;
|
|
|
|
for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
|
|
if (APPL_IS_FREE(appl))
|
|
break;
|
|
}
|
|
if (appl > CAPI_MAXAPPL)
|
|
return CAPI_TOOMANYAPPLS;
|
|
|
|
APPL_MARK_USED(appl);
|
|
skb_queue_head_init(&APPL(appl)->recv_queue);
|
|
|
|
memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params));
|
|
|
|
for (i = 0; i < CAPI_MAXCONTR; i++) {
|
|
struct capi_profile *profp =
|
|
(struct capi_profile *)cards[i].version[VER_PROFILE];
|
|
|
|
if (cards[i].cardstate != CARD_RUNNING)
|
|
continue;
|
|
|
|
if (want > 0) nconn = want;
|
|
else nconn = profp->nbchannel * -want;
|
|
if (nconn == 0) nconn = profp->nbchannel;
|
|
|
|
B1_send_register(cards[i].port, appl,
|
|
1024 * (nconn+1), nconn,
|
|
APPL(appl)->rparam.datablkcnt,
|
|
APPL(appl)->rparam.datablklen);
|
|
}
|
|
*applidp = appl;
|
|
printk(KERN_INFO "b1capi: appl %d up\n", appl);
|
|
|
|
return CAPI_NOERROR;
|
|
}
|
|
|
|
static __u16 capi_release(__u16 applid)
|
|
{
|
|
struct sk_buff *skb;
|
|
int i;
|
|
|
|
if (!VALID_APPLID(applid) || APPL(applid)->releasing)
|
|
return CAPI_ILLAPPNR;
|
|
while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0)
|
|
kfree_skb(skb);
|
|
for (i = 0; i < CAPI_MAXCONTR; i++) {
|
|
if (cards[i].cardstate != CARD_RUNNING) {
|
|
continue;
|
|
}
|
|
APPL(applid)->releasing++;
|
|
B1_send_release(cards[i].port, applid);
|
|
}
|
|
if (APPL(applid)->releasing <= 0) {
|
|
APPL(applid)->signal = 0;
|
|
APPL_MARK_FREE(applid);
|
|
printk(KERN_INFO "b1capi: appl %d down\n", applid);
|
|
}
|
|
return CAPI_NOERROR;
|
|
}
|
|
|
|
static __u16 capi_put_message(__u16 applid, struct sk_buff *skb)
|
|
{
|
|
avmb1_ncci *np;
|
|
int contr;
|
|
if (ncards == 0)
|
|
return CAPI_REGNOTINSTALLED;
|
|
if (!VALID_APPLID(applid))
|
|
return CAPI_ILLAPPNR;
|
|
if (skb->len < 12
|
|
|| !capi_cmd_valid(CAPIMSG_COMMAND(skb->data))
|
|
|| !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data)))
|
|
return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
|
|
contr = CAPIMSG_CONTROLLER(skb->data);
|
|
if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING) {
|
|
contr = 1;
|
|
if (CARD(contr)->cardstate != CARD_RUNNING)
|
|
return CAPI_REGNOTINSTALLED;
|
|
}
|
|
if (CARD(contr)->blocked)
|
|
return CAPI_SENDQUEUEFULL;
|
|
|
|
if ( CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3
|
|
&& CAPIMSG_SUBCOMMAND(skb->data) == CAPI_REQ
|
|
&& (np = find_ncci(APPL(applid), CAPIMSG_NCCI(skb->data))) != 0
|
|
&& mq_enqueue(np, CAPIMSG_MSGID(skb->data)) == 0)
|
|
return CAPI_SENDQUEUEFULL;
|
|
|
|
B1_send_message(CARD(contr)->port, skb);
|
|
return CAPI_NOERROR;
|
|
}
|
|
|
|
static __u16 capi_get_message(__u16 applid, struct sk_buff **msgp)
|
|
{
|
|
struct sk_buff *skb;
|
|
|
|
if (!VALID_APPLID(applid))
|
|
return CAPI_ILLAPPNR;
|
|
if ((skb = skb_dequeue(&APPL(applid)->recv_queue)) == 0)
|
|
return CAPI_RECEIVEQUEUEEMPTY;
|
|
*msgp = skb;
|
|
return CAPI_NOERROR;
|
|
}
|
|
|
|
static __u16 capi_set_signal(__u16 applid,
|
|
void (*signal) (__u16 applid, __u32 param),
|
|
__u32 param)
|
|
{
|
|
if (!VALID_APPLID(applid))
|
|
return CAPI_ILLAPPNR;
|
|
APPL(applid)->signal = signal;
|
|
APPL(applid)->param = param;
|
|
return CAPI_NOERROR;
|
|
}
|
|
|
|
static __u16 capi_get_manufacturer(__u16 contr, __u8 buf[CAPI_MANUFACTURER_LEN])
|
|
{
|
|
if (contr == 0) {
|
|
strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
|
|
return CAPI_NOERROR;
|
|
}
|
|
if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING)
|
|
return 0x2002;
|
|
|
|
strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
|
|
return CAPI_NOERROR;
|
|
}
|
|
|
|
static __u16 capi_get_version(__u16 contr, struct capi_version *verp)
|
|
{
|
|
if (contr == 0) {
|
|
*verp = driver_version;
|
|
return CAPI_NOERROR;
|
|
}
|
|
if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING)
|
|
return 0x2002;
|
|
|
|
memcpy((void *) verp, &CARD(contr)->cversion,
|
|
sizeof(capi_version));
|
|
return CAPI_NOERROR;
|
|
}
|
|
|
|
static __u16 capi_get_serial(__u16 contr, __u8 serial[CAPI_SERIAL_LEN])
|
|
{
|
|
if (contr == 0) {
|
|
strncpy(serial, driver_serial, 8);
|
|
return CAPI_NOERROR;
|
|
}
|
|
if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING)
|
|
return 0x2002;
|
|
|
|
memcpy((void *) serial, CARD(contr)->version[VER_SERIAL],
|
|
CAPI_SERIAL_LEN);
|
|
serial[CAPI_SERIAL_LEN - 1] = 0;
|
|
return CAPI_NOERROR;
|
|
}
|
|
|
|
static __u16 capi_get_profile(__u16 contr, struct capi_profile *profp)
|
|
{
|
|
if (contr == 0) {
|
|
profp->ncontroller = ncards;
|
|
return CAPI_NOERROR;
|
|
}
|
|
if (!VALID_CARD(contr) || CARD(contr)->cardstate != CARD_RUNNING)
|
|
return 0x2002;
|
|
|
|
memcpy((void *) profp, CARD(contr)->version[VER_PROFILE],
|
|
sizeof(struct capi_profile));
|
|
return CAPI_NOERROR;
|
|
}
|
|
|
|
static int capi_manufacturer(unsigned int cmd, void *data)
|
|
{
|
|
unsigned long flags;
|
|
avmb1_loadandconfigdef ldef;
|
|
avmb1_extcarddef cdef;
|
|
avmb1_resetdef rdef;
|
|
avmb1_getdef gdef;
|
|
avmb1_card *card;
|
|
int rc;
|
|
|
|
switch (cmd) {
|
|
case AVMB1_ADDCARD:
|
|
case AVMB1_ADDCARD_WITH_TYPE:
|
|
if (cmd == AVMB1_ADDCARD) {
|
|
if ((rc = copy_from_user((void *) &cdef, data,
|
|
sizeof(avmb1_carddef))))
|
|
return rc;
|
|
cdef.cardtype = AVM_CARDTYPE_B1;
|
|
} else {
|
|
if ((rc = copy_from_user((void *) &cdef, data,
|
|
sizeof(avmb1_extcarddef))))
|
|
return rc;
|
|
}
|
|
|
|
if ((rc = avmb1_probecard(cdef.port, cdef.irq, cdef.cardtype)) != 0)
|
|
return rc;
|
|
|
|
if (cdef.cardtype == AVM_CARDTYPE_T1) {
|
|
int i;
|
|
for (i=0; i < CAPI_MAXCONTR; i++) {
|
|
if ( cards[i].cardstate != CARD_FREE
|
|
&& cards[i].cardtype == AVM_CARDTYPE_T1
|
|
&& cards[i].cardnr == cdef.cardnr) {
|
|
printk(KERN_ERR
|
|
"b1capi: T1-HEMA-card-%d already at 0x%x\n",
|
|
cdef.cardnr, cards[i].port);
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
rc = T1_detectandinit(cdef.port,cdef.irq,cdef.cardnr);
|
|
if (rc) {
|
|
printk(KERN_NOTICE "b1capi: NO T1-HEMA-card-%d at 0x%x (%d)\n",
|
|
cdef.cardnr, cdef.port, rc);
|
|
return -EIO;
|
|
}
|
|
printk(KERN_NOTICE "b1capi: T1-HEMA-card-%d at 0x%x\n",
|
|
cdef.cardnr, cdef.port);
|
|
}
|
|
|
|
rc = avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype);
|
|
if (rc < 0)
|
|
return rc;
|
|
/* don't want to change interface t
|
|
addcard/probecard/registercard */
|
|
if (cdef.cardtype == AVM_CARDTYPE_T1) {
|
|
int i;
|
|
for (i=0; i < CAPI_MAXCONTR; i++) {
|
|
if (cards[i].cnr == rc) {
|
|
cards[i].cardnr = cdef.cardnr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
|
|
case AVMB1_LOAD:
|
|
case AVMB1_LOAD_AND_CONFIG:
|
|
|
|
if (cmd == AVMB1_LOAD) {
|
|
if ((rc = copy_from_user((void *) &ldef, data,
|
|
sizeof(avmb1_loaddef))))
|
|
return rc;
|
|
ldef.t4config.len = 0;
|
|
ldef.t4config.data = 0;
|
|
} else {
|
|
if ((rc = copy_from_user((void *) &ldef, data,
|
|
sizeof(avmb1_loadandconfigdef))))
|
|
return rc;
|
|
}
|
|
if (!VALID_CARD(ldef.contr))
|
|
return -ESRCH;
|
|
|
|
if (ldef.t4file.len <= 0) {
|
|
printk(KERN_DEBUG "b1capi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
card = CARD(ldef.contr);
|
|
save_flags(flags);
|
|
cli();
|
|
if (card->cardstate != CARD_DETECTED) {
|
|
restore_flags(flags);
|
|
if (loaddebug)
|
|
printk(KERN_DEBUG "b1capi: load: contr=%d not in detect state\n", ldef.contr);
|
|
return -EBUSY;
|
|
}
|
|
card->cardstate = CARD_LOADING;
|
|
restore_flags(flags);
|
|
|
|
if (loaddebug) {
|
|
printk(KERN_DEBUG "b1capi: load: reseting contr %d\n",
|
|
ldef.contr);
|
|
}
|
|
|
|
B1_reset(card->port);
|
|
|
|
if (loaddebug) {
|
|
printk(KERN_DEBUG "b1capi: loading contr %d\n",
|
|
ldef.contr);
|
|
}
|
|
|
|
if ((rc = B1_load_t4file(card->port, &ldef.t4file))) {
|
|
B1_reset(card->port);
|
|
printk(KERN_ERR "b1capi: failed to load t4file!!\n");
|
|
card->cardstate = CARD_DETECTED;
|
|
return rc;
|
|
}
|
|
|
|
B1_disable_irq(card->port);
|
|
|
|
if (ldef.t4config.len > 0) { /* load config */
|
|
if (loaddebug) {
|
|
printk(KERN_DEBUG "b1capi: loading config to contr %d\n",
|
|
ldef.contr);
|
|
}
|
|
if ((rc = B1_load_config(card->port, &ldef.t4config))) {
|
|
B1_reset(card->port);
|
|
printk(KERN_ERR "b1capi: failed to load config!!\n");
|
|
card->cardstate = CARD_DETECTED;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
if (loaddebug) {
|
|
printk(KERN_DEBUG "b1capi: load: ready contr %d: checking\n",
|
|
ldef.contr);
|
|
}
|
|
|
|
if (!B1_loaded(card->port)) {
|
|
card->cardstate = CARD_DETECTED;
|
|
printk(KERN_ERR "b1capi: failed to load t4file.\n");
|
|
return -EIO;
|
|
}
|
|
/*
|
|
* enable interrupt
|
|
*/
|
|
|
|
card->cardstate = CARD_INITSTATE;
|
|
save_flags(flags);
|
|
cli();
|
|
B1_setinterrupt(card->port, card->irq, card->cardtype);
|
|
restore_flags(flags);
|
|
|
|
if (loaddebug) {
|
|
printk(KERN_DEBUG "b1capi: load: irq enabled contr %d\n",
|
|
ldef.contr);
|
|
}
|
|
|
|
/*
|
|
* init card
|
|
*/
|
|
if (card->cardtype == AVM_CARDTYPE_T1)
|
|
B1_send_init(card->port, AVM_NAPPS,
|
|
AVM_NNCCI_PER_CHANNEL*30,
|
|
card->cnr - 1);
|
|
else
|
|
B1_send_init(card->port, AVM_NAPPS,
|
|
AVM_NNCCI_PER_CHANNEL*2,
|
|
card->cnr - 1);
|
|
|
|
if (loaddebug) {
|
|
printk(KERN_DEBUG "b1capi: load: waiting for init reply contr %d\n",
|
|
ldef.contr);
|
|
}
|
|
|
|
while (card->cardstate != CARD_RUNNING) {
|
|
|
|
current->state = TASK_INTERRUPTIBLE;
|
|
schedule_timeout(HZ/10); /* 0.1 sec */
|
|
|
|
if (signal_pending(current))
|
|
return -EINTR;
|
|
}
|
|
return 0;
|
|
|
|
case AVMB1_RESETCARD:
|
|
if ((rc = copy_from_user((void *) &rdef, data,
|
|
sizeof(avmb1_resetdef))))
|
|
return rc;
|
|
|
|
return avmb1_resetcard(rdef.contr);
|
|
|
|
case AVMB1_GET_CARDINFO:
|
|
if ((rc = copy_from_user((void *) &gdef, data,
|
|
sizeof(avmb1_getdef))))
|
|
return rc;
|
|
|
|
if (!VALID_CARD(gdef.contr))
|
|
return -ESRCH;
|
|
|
|
card = CARD(gdef.contr);
|
|
|
|
gdef.cardstate = card->cardstate;
|
|
gdef.cardtype = card->cardtype;
|
|
|
|
if ((rc = copy_to_user(data, (void *) &gdef,
|
|
sizeof(avmb1_getdef))))
|
|
return rc;
|
|
|
|
return 0;
|
|
case AVMB1_REMOVECARD:
|
|
if ((rc = copy_from_user((void *) &rdef, data,
|
|
sizeof(avmb1_resetdef))))
|
|
return rc;
|
|
if (!VALID_CARD(rdef.contr))
|
|
return -ESRCH;
|
|
|
|
card = CARD(rdef.contr);
|
|
|
|
if (card->cardstate != CARD_DETECTED)
|
|
return -EBUSY;
|
|
|
|
return avmb1_unregistercard(rdef.contr, 1);
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
struct capi_interface avmb1_interface =
|
|
{
|
|
capi_installed,
|
|
capi_register,
|
|
capi_release,
|
|
capi_put_message,
|
|
capi_get_message,
|
|
capi_set_signal,
|
|
capi_get_manufacturer,
|
|
capi_get_version,
|
|
capi_get_serial,
|
|
capi_get_profile,
|
|
capi_manufacturer
|
|
};
|
|
|
|
/* ------------------------------------------------------------- */
|
|
/* -------- Exported Functions --------------------------------- */
|
|
/* ------------------------------------------------------------- */
|
|
|
|
struct capi_interface *attach_capi_interface(struct capi_interface_user *userp)
|
|
{
|
|
struct capi_interface_user *p;
|
|
|
|
for (p = capi_users; p; p = p->next) {
|
|
if (p == userp) {
|
|
printk(KERN_ERR "b1capi: double attach from %s\n",
|
|
userp->name);
|
|
return 0;
|
|
}
|
|
}
|
|
userp->next = capi_users;
|
|
capi_users = userp;
|
|
MOD_INC_USE_COUNT;
|
|
printk(KERN_NOTICE "b1capi: %s attached\n", userp->name);
|
|
|
|
return &avmb1_interface;
|
|
}
|
|
|
|
int detach_capi_interface(struct capi_interface_user *userp)
|
|
{
|
|
struct capi_interface_user **pp;
|
|
|
|
for (pp = &capi_users; *pp; pp = &(*pp)->next) {
|
|
if (*pp == userp) {
|
|
*pp = userp->next;
|
|
userp->next = 0;
|
|
MOD_DEC_USE_COUNT;
|
|
printk(KERN_NOTICE "b1capi: %s detached\n", userp->name);
|
|
return 0;
|
|
}
|
|
}
|
|
printk(KERN_ERR "b1capi: double detach from %s\n", userp->name);
|
|
return -1;
|
|
}
|
|
|
|
/* ------------------------------------------------------------- */
|
|
/* -------- Init & Cleanup ------------------------------------- */
|
|
/* ------------------------------------------------------------- */
|
|
|
|
EXPORT_SYMBOL(attach_capi_interface);
|
|
EXPORT_SYMBOL(detach_capi_interface);
|
|
EXPORT_SYMBOL(avmb1_addcard);
|
|
EXPORT_SYMBOL(avmb1_probecard);
|
|
EXPORT_SYMBOL(avmb1_registercard);
|
|
EXPORT_SYMBOL(avmb1_unregistercard);
|
|
EXPORT_SYMBOL(avmb1_resetcard);
|
|
EXPORT_SYMBOL(avmb1_detectcard);
|
|
|
|
|
|
/*
|
|
* init / exit functions
|
|
*/
|
|
|
|
#ifdef MODULE
|
|
#define avmb1_init init_module
|
|
#endif
|
|
|
|
int avmb1_init(void)
|
|
{
|
|
char *p;
|
|
char rev[10];
|
|
|
|
skb_queue_head_init(&recv_queue);
|
|
/* init_bh(CAPI_BH, do_capi_bh); */
|
|
|
|
tq_state_notify.routine = notify_handler;
|
|
tq_state_notify.data = 0;
|
|
|
|
tq_recv_notify.routine = recv_handler;
|
|
tq_recv_notify.data = 0;
|
|
|
|
|
|
if ((p = strchr(revision, ':'))) {
|
|
strcpy(rev, p + 1);
|
|
p = strchr(rev, '$');
|
|
*p = 0;
|
|
} else
|
|
strcpy(rev, " ??? ");
|
|
|
|
#ifdef MODULE
|
|
printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: loaded\n", rev);
|
|
#else
|
|
printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: started\n", rev);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#ifdef MODULE
|
|
void cleanup_module(void)
|
|
{
|
|
char rev[10];
|
|
char *p;
|
|
int i;
|
|
|
|
if ((p = strchr(revision, ':'))) {
|
|
strcpy(rev, p + 1);
|
|
p = strchr(rev, '$');
|
|
*p = 0;
|
|
} else {
|
|
strcpy(rev, " ??? ");
|
|
}
|
|
|
|
for (i = 0; i < CAPI_MAXCONTR; i++) {
|
|
if (cards[i].cardstate != CARD_FREE) {
|
|
/*
|
|
* disable card
|
|
*/
|
|
B1_disable_irq(cards[i].port);
|
|
avmb1_resetcard(i+1);
|
|
/*
|
|
* free kernel resources
|
|
*/
|
|
avmb1_unregistercard(i+1, 1);
|
|
}
|
|
}
|
|
schedule(); /* execute queued tasks .... */
|
|
printk(KERN_NOTICE "AVM-B1-CAPI-driver Rev%s: unloaded\n", rev);
|
|
}
|
|
#endif
|