URL
https://opencores.org/ocsvn/or1k_old/or1k_old/trunk
Subversion Repositories or1k_old
[/] [or1k_old/] [trunk/] [uclinux/] [uClinux-2.0.x/] [drivers/] [isdn/] [avmb1/] [b1capi.c] - Rev 1782
Compare with Previous | Blame | View Log
/* * $Id: b1capi.c,v 1.1.1.1 2001-09-10 07:44:18 simons Exp $ * * CAPI 2.0 Module for AVM B1-card. * * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de) * * $Log: not supported by cvs2svn $ * Revision 1.1.1.1 2001/07/02 17:58:32 simons * Initial revision * * Revision 1.4.2.19 1998/10/25 14:36:14 fritz * Backported from MIPS (Cobalt). * * 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.4.2.11 1998/01/27 16:12:49 calle * Support for PCMCIA B1/M1/M2 ready. * * Revision 1.4.2.10 1998/01/26 14:53:30 calle * interface change for pcmcia cards * * Revision 1.4.2.9 1998/01/23 16:49:27 calle * added functions for pcmcia cards, * avmb1_addcard returns now the controller number. * * Revision 1.4.2.8 1998/01/16 14:04:15 calle * Decoding of manufacturer part of capi_profile, now show linetype and * protocol if possible. * * Revision 1.4.2.7 1998/01/15 15:33:34 calle * print cardtype, d2 protocol and linetype after load. * * Revision 1.4.2.6 1997/12/08 06:58:41 calle * correct typo. * * 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.4.2.3 1997/11/26 10:46:52 calle * prepared for M1 (Mobile) and T1 (PMX) cards. * prepared to set configuration after load to support other D-channel * protocols, point-to-point and leased lines. * * Revision 1.4.2.2 1997/10/19 14:44:36 calle * fixed capi_get_version. * * Revision 1.4.2.1 1997/07/12 08:18:59 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: 1.1.1.1 $"; /* ------------------------------------------------------------- */ int showcapimsgs = 0; /* used in lli.c */ int loaddebug = 0; #ifdef HAS_NEW_SYMTAB MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>"); MODULE_PARM(showcapimsgs, "0-5i"); MODULE_PARM(loaddebug, "0-1i"); #endif /* ------------------------------------------------------------- */ 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"; 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, FREE_READ); continue; } if (APPL(appl)->signal == 0) { printk(KERN_ERR "b1capi: recv_handler: applid %d has no signal function\n", appl); kfree_skb(skb, FREE_READ); 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, FREE_READ); } 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: 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, FREE_READ); 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->timeout = jiffies + HZ / 10; /* 0.1 sec */ current->state = TASK_INTERRUPTIBLE; schedule(); if (current->signal & ~current->blocked) 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 ------------------------------------- */ /* ------------------------------------------------------------- */ #ifdef HAS_NEW_SYMTAB 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); #else static struct symbol_table capidev_syms = { #include <linux/symtab_begin.h> X(attach_capi_interface), X(detach_capi_interface), X(avmb1_addcard), X(avmb1_probecard), X(avmb1_registercard), X(avmb1_unregistercard), X(avmb1_resetcard), X(avmb1_detectcard), #include <linux/symtab_end.h> }; #endif /* * init / exit functions */ #ifdef MODULE #define avmb1_init init_module #endif int avmb1_init(void) { char *p; char rev[10]; #ifndef HAS_NEW_SYMTAB /* No symbols to export, hide all symbols */ register_symtab(&capidev_syms); #endif 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