diff options
Diffstat (limited to 'drivers/isdn/capi/kcapi.c')
-rw-r--r-- | drivers/isdn/capi/kcapi.c | 991 |
1 files changed, 991 insertions, 0 deletions
diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c new file mode 100644 index 00000000000..feec40cf590 --- /dev/null +++ b/drivers/isdn/capi/kcapi.c @@ -0,0 +1,991 @@ +/* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $ + * + * Kernel CAPI 2.0 Module + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#define CONFIG_AVMB1_COMPAT + +#include "kcapi.h" +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/skbuff.h> +#include <linux/workqueue.h> +#include <linux/capi.h> +#include <linux/kernelcapi.h> +#include <linux/init.h> +#include <linux/moduleparam.h> +#include <linux/delay.h> +#include <asm/uaccess.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#ifdef CONFIG_AVMB1_COMPAT +#include <linux/b1lli.h> +#endif + +static char *revision = "$Revision: 1.1.2.8 $"; + +/* ------------------------------------------------------------- */ + +static int showcapimsgs = 0; + +MODULE_DESCRIPTION("CAPI4Linux: kernel CAPI layer"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); +module_param(showcapimsgs, uint, 0); + +/* ------------------------------------------------------------- */ + +struct capi_notifier { + struct work_struct work; + unsigned int cmd; + u32 controller; + u16 applid; + u32 ncci; +}; + +/* ------------------------------------------------------------- */ + +static struct capi_version driver_version = {2, 0, 1, 1<<4}; +static char driver_serial[CAPI_SERIAL_LEN] = "0004711"; +static char capi_manufakturer[64] = "AVM Berlin"; + +#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) + +LIST_HEAD(capi_drivers); +DEFINE_RWLOCK(capi_drivers_list_lock); + +static DEFINE_RWLOCK(application_lock); +static DECLARE_MUTEX(controller_sem); + +struct capi20_appl *capi_applications[CAPI_MAXAPPL]; +struct capi_ctr *capi_cards[CAPI_MAXCONTR]; + +static int ncards; + +/* -------- controller ref counting -------------------------------------- */ + +static inline struct capi_ctr * +capi_ctr_get(struct capi_ctr *card) +{ + if (!try_module_get(card->owner)) + return NULL; + return card; +} + +static inline void +capi_ctr_put(struct capi_ctr *card) +{ + module_put(card->owner); +} + +/* ------------------------------------------------------------- */ + +static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr) +{ + if (contr - 1 >= CAPI_MAXCONTR) + return NULL; + + return capi_cards[contr - 1]; +} + +static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid) +{ + if (applid - 1 >= CAPI_MAXAPPL) + return NULL; + + return capi_applications[applid - 1]; +} + +/* -------- util functions ------------------------------------ */ + +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; +} + +/* ------------------------------------------------------------ */ + +static void register_appl(struct capi_ctr *card, u16 applid, capi_register_params *rparam) +{ + card = capi_ctr_get(card); + + if (card) + card->register_appl(card, applid, rparam); + else + printk(KERN_WARNING "%s: cannot get card resources\n", __FUNCTION__); +} + + +static void release_appl(struct capi_ctr *card, u16 applid) +{ + DBG("applid %#x", applid); + + card->release_appl(card, applid); + capi_ctr_put(card); +} + +/* -------- KCI_CONTRUP --------------------------------------- */ + +static void notify_up(u32 contr) +{ + struct capi_ctr *card = get_capi_ctr_by_nr(contr); + struct capi20_appl *ap; + u16 applid; + + if (showcapimsgs & 1) { + printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr); + } + if (!card) { + printk(KERN_WARNING "%s: invalid contr %d\n", __FUNCTION__, contr); + return; + } + for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { + ap = get_capi_appl_by_nr(applid); + if (!ap || ap->release_in_progress) continue; + register_appl(card, applid, &ap->rparam); + if (ap->callback && !ap->release_in_progress) + ap->callback(KCI_CONTRUP, contr, &card->profile); + } +} + +/* -------- KCI_CONTRDOWN ------------------------------------- */ + +static void notify_down(u32 contr) +{ + struct capi20_appl *ap; + u16 applid; + + if (showcapimsgs & 1) { + printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr); + } + + for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { + ap = get_capi_appl_by_nr(applid); + if (ap && ap->callback && !ap->release_in_progress) + ap->callback(KCI_CONTRDOWN, contr, NULL); + } +} + +static void notify_handler(void *data) +{ + struct capi_notifier *np = data; + + switch (np->cmd) { + case KCI_CONTRUP: + notify_up(np->controller); + break; + case KCI_CONTRDOWN: + notify_down(np->controller); + break; + } + + kfree(np); +} + +/* + * The notifier will result in adding/deleteing of devices. Devices can + * only removed in user process, not in bh. + */ +static int notify_push(unsigned int cmd, u32 controller, u16 applid, u32 ncci) +{ + struct capi_notifier *np = kmalloc(sizeof(*np), GFP_ATOMIC); + + if (!np) + return -ENOMEM; + + INIT_WORK(&np->work, notify_handler, np); + np->cmd = cmd; + np->controller = controller; + np->applid = applid; + np->ncci = ncci; + + schedule_work(&np->work); + return 0; +} + + +/* -------- Receiver ------------------------------------------ */ + +static void recv_handler(void *_ap) +{ + struct sk_buff *skb; + struct capi20_appl *ap = (struct capi20_appl *) _ap; + + if ((!ap) || (ap->release_in_progress)) + return; + + down(&ap->recv_sem); + while ((skb = skb_dequeue(&ap->recv_queue))) { + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND) + ap->nrecvdatapkt++; + else + ap->nrecvctlpkt++; + + ap->recv_message(ap, skb); + } + up(&ap->recv_sem); +} + +void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb) +{ + struct capi20_appl *ap; + int showctl = 0; + u8 cmd, subcmd; + unsigned long flags; + + if (card->cardstate != CARD_RUNNING) { + printk(KERN_INFO "kcapi: controller %d not active, got: %s", + card->cnr, capi_message2str(skb->data)); + goto error; + } + + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { + card->nrecvdatapkt++; + if (card->traceflag > 2) showctl |= 2; + } else { + card->nrecvctlpkt++; + if (card->traceflag) showctl |= 2; + } + showctl |= (card->traceflag & 1); + if (showctl & 2) { + if (showctl & 1) { + printk(KERN_DEBUG "kcapi: got [0x%lx] id#%d %s len=%u\n", + (unsigned long) card->cnr, + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), + CAPIMSG_LEN(skb->data)); + } else { + printk(KERN_DEBUG "kcapi: got [0x%lx] %s\n", + (unsigned long) card->cnr, + capi_message2str(skb->data)); + } + + } + + read_lock_irqsave(&application_lock, flags); + ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data)); + if ((!ap) || (ap->release_in_progress)) { + read_unlock_irqrestore(&application_lock, flags); + printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n", + CAPIMSG_APPID(skb->data), capi_message2str(skb->data)); + goto error; + } + skb_queue_tail(&ap->recv_queue, skb); + schedule_work(&ap->recv_work); + read_unlock_irqrestore(&application_lock, flags); + + return; + +error: + kfree_skb(skb); +} + +EXPORT_SYMBOL(capi_ctr_handle_message); + +void capi_ctr_ready(struct capi_ctr * card) +{ + card->cardstate = CARD_RUNNING; + + printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n", + card->cnr, card->name); + + notify_push(KCI_CONTRUP, card->cnr, 0, 0); +} + +EXPORT_SYMBOL(capi_ctr_ready); + +void capi_ctr_reseted(struct capi_ctr * card) +{ + u16 appl; + + DBG(""); + + if (card->cardstate == CARD_DETECTED) + return; + + card->cardstate = CARD_DETECTED; + + memset(card->manu, 0, sizeof(card->manu)); + memset(&card->version, 0, sizeof(card->version)); + memset(&card->profile, 0, sizeof(card->profile)); + memset(card->serial, 0, sizeof(card->serial)); + + for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { + struct capi20_appl *ap = get_capi_appl_by_nr(appl); + if (!ap || ap->release_in_progress) + continue; + + capi_ctr_put(card); + } + + printk(KERN_NOTICE "kcapi: card %d down.\n", card->cnr); + + notify_push(KCI_CONTRDOWN, card->cnr, 0, 0); +} + +EXPORT_SYMBOL(capi_ctr_reseted); + +void capi_ctr_suspend_output(struct capi_ctr *card) +{ + if (!card->blocked) { + printk(KERN_DEBUG "kcapi: card %d suspend\n", card->cnr); + card->blocked = 1; + } +} + +EXPORT_SYMBOL(capi_ctr_suspend_output); + +void capi_ctr_resume_output(struct capi_ctr *card) +{ + if (card->blocked) { + printk(KERN_DEBUG "kcapi: card %d resume\n", card->cnr); + card->blocked = 0; + } +} + +EXPORT_SYMBOL(capi_ctr_resume_output); + +/* ------------------------------------------------------------- */ + +int +attach_capi_ctr(struct capi_ctr *card) +{ + int i; + + down(&controller_sem); + + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (capi_cards[i] == NULL) + break; + } + if (i == CAPI_MAXCONTR) { + up(&controller_sem); + printk(KERN_ERR "kcapi: out of controller slots\n"); + return -EBUSY; + } + capi_cards[i] = card; + + up(&controller_sem); + + card->nrecvctlpkt = 0; + card->nrecvdatapkt = 0; + card->nsentctlpkt = 0; + card->nsentdatapkt = 0; + card->cnr = i + 1; + card->cardstate = CARD_DETECTED; + card->blocked = 0; + card->traceflag = showcapimsgs; + + sprintf(card->procfn, "capi/controllers/%d", card->cnr); + card->procent = create_proc_entry(card->procfn, 0, NULL); + if (card->procent) { + card->procent->read_proc = + (int (*)(char *,char **,off_t,int,int *,void *)) + card->ctr_read_proc; + card->procent->data = card; + } + + ncards++; + printk(KERN_NOTICE "kcapi: Controller %d: %s attached\n", + card->cnr, card->name); + return 0; +} + +EXPORT_SYMBOL(attach_capi_ctr); + +int detach_capi_ctr(struct capi_ctr *card) +{ + if (card->cardstate != CARD_DETECTED) + capi_ctr_reseted(card); + + ncards--; + + if (card->procent) { + remove_proc_entry(card->procfn, NULL); + card->procent = NULL; + } + capi_cards[card->cnr - 1] = NULL; + printk(KERN_NOTICE "kcapi: Controller %d: %s unregistered\n", + card->cnr, card->name); + + return 0; +} + +EXPORT_SYMBOL(detach_capi_ctr); + +void register_capi_driver(struct capi_driver *driver) +{ + unsigned long flags; + + write_lock_irqsave(&capi_drivers_list_lock, flags); + list_add_tail(&driver->list, &capi_drivers); + write_unlock_irqrestore(&capi_drivers_list_lock, flags); +} + +EXPORT_SYMBOL(register_capi_driver); + +void unregister_capi_driver(struct capi_driver *driver) +{ + unsigned long flags; + + write_lock_irqsave(&capi_drivers_list_lock, flags); + list_del(&driver->list); + write_unlock_irqrestore(&capi_drivers_list_lock, flags); +} + +EXPORT_SYMBOL(unregister_capi_driver); + +/* ------------------------------------------------------------- */ +/* -------- CAPI2.0 Interface ---------------------------------- */ +/* ------------------------------------------------------------- */ + +u16 capi20_isinstalled(void) +{ + int i; + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (capi_cards[i] && capi_cards[i]->cardstate == CARD_RUNNING) + return CAPI_NOERROR; + } + return CAPI_REGNOTINSTALLED; +} + +EXPORT_SYMBOL(capi20_isinstalled); + +u16 capi20_register(struct capi20_appl *ap) +{ + int i; + u16 applid; + unsigned long flags; + + DBG(""); + + if (ap->rparam.datablklen < 128) + return CAPI_LOGBLKSIZETOSMALL; + + write_lock_irqsave(&application_lock, flags); + + for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { + if (capi_applications[applid - 1] == NULL) + break; + } + if (applid > CAPI_MAXAPPL) { + write_unlock_irqrestore(&application_lock, flags); + return CAPI_TOOMANYAPPLS; + } + + ap->applid = applid; + capi_applications[applid - 1] = ap; + + ap->nrecvctlpkt = 0; + ap->nrecvdatapkt = 0; + ap->nsentctlpkt = 0; + ap->nsentdatapkt = 0; + ap->callback = NULL; + init_MUTEX(&ap->recv_sem); + skb_queue_head_init(&ap->recv_queue); + INIT_WORK(&ap->recv_work, recv_handler, (void *)ap); + ap->release_in_progress = 0; + + write_unlock_irqrestore(&application_lock, flags); + + down(&controller_sem); + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING) + continue; + register_appl(capi_cards[i], applid, &ap->rparam); + } + up(&controller_sem); + + if (showcapimsgs & 1) { + printk(KERN_DEBUG "kcapi: appl %d up\n", applid); + } + + return CAPI_NOERROR; +} + +EXPORT_SYMBOL(capi20_register); + +u16 capi20_release(struct capi20_appl *ap) +{ + int i; + unsigned long flags; + + DBG("applid %#x", ap->applid); + + write_lock_irqsave(&application_lock, flags); + ap->release_in_progress = 1; + capi_applications[ap->applid - 1] = NULL; + write_unlock_irqrestore(&application_lock, flags); + + down(&controller_sem); + for (i = 0; i < CAPI_MAXCONTR; i++) { + if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING) + continue; + release_appl(capi_cards[i], ap->applid); + } + up(&controller_sem); + + flush_scheduled_work(); + skb_queue_purge(&ap->recv_queue); + + if (showcapimsgs & 1) { + printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid); + } + + return CAPI_NOERROR; +} + +EXPORT_SYMBOL(capi20_release); + +u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb) +{ + struct capi_ctr *card; + int showctl = 0; + u8 cmd, subcmd; + + DBG("applid %#x", ap->applid); + + if (ncards == 0) + return CAPI_REGNOTINSTALLED; + if ((ap->applid == 0) || ap->release_in_progress) + return CAPI_ILLAPPNR; + if (skb->len < 12 + || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) + || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data))) + return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; + card = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data)); + if (!card || card->cardstate != CARD_RUNNING) { + card = get_capi_ctr_by_nr(1); // XXX why? + if (!card || card->cardstate != CARD_RUNNING) + return CAPI_REGNOTINSTALLED; + } + if (card->blocked) + return CAPI_SENDQUEUEFULL; + + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + if (cmd == CAPI_DATA_B3 && subcmd== CAPI_REQ) { + card->nsentdatapkt++; + ap->nsentdatapkt++; + if (card->traceflag > 2) showctl |= 2; + } else { + card->nsentctlpkt++; + ap->nsentctlpkt++; + if (card->traceflag) showctl |= 2; + } + showctl |= (card->traceflag & 1); + if (showctl & 2) { + if (showctl & 1) { + printk(KERN_DEBUG "kcapi: put [%#x] id#%d %s len=%u\n", + CAPIMSG_CONTROLLER(skb->data), + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), + CAPIMSG_LEN(skb->data)); + } else { + printk(KERN_DEBUG "kcapi: put [%#x] %s\n", + CAPIMSG_CONTROLLER(skb->data), + capi_message2str(skb->data)); + } + + } + return card->send_message(card, skb); +} + +EXPORT_SYMBOL(capi20_put_message); + +u16 capi20_get_manufacturer(u32 contr, u8 *buf) +{ + struct capi_ctr *card; + + if (contr == 0) { + strlcpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); + return CAPI_NOERROR; + } + card = get_capi_ctr_by_nr(contr); + if (!card || card->cardstate != CARD_RUNNING) + return CAPI_REGNOTINSTALLED; + strlcpy(buf, card->manu, CAPI_MANUFACTURER_LEN); + return CAPI_NOERROR; +} + +EXPORT_SYMBOL(capi20_get_manufacturer); + +u16 capi20_get_version(u32 contr, struct capi_version *verp) +{ + struct capi_ctr *card; + + if (contr == 0) { + *verp = driver_version; + return CAPI_NOERROR; + } + card = get_capi_ctr_by_nr(contr); + if (!card || card->cardstate != CARD_RUNNING) + return CAPI_REGNOTINSTALLED; + + memcpy((void *) verp, &card->version, sizeof(capi_version)); + return CAPI_NOERROR; +} + +EXPORT_SYMBOL(capi20_get_version); + +u16 capi20_get_serial(u32 contr, u8 *serial) +{ + struct capi_ctr *card; + + if (contr == 0) { + strlcpy(serial, driver_serial, CAPI_SERIAL_LEN); + return CAPI_NOERROR; + } + card = get_capi_ctr_by_nr(contr); + if (!card || card->cardstate != CARD_RUNNING) + return CAPI_REGNOTINSTALLED; + + strlcpy((void *) serial, card->serial, CAPI_SERIAL_LEN); + return CAPI_NOERROR; +} + +EXPORT_SYMBOL(capi20_get_serial); + +u16 capi20_get_profile(u32 contr, struct capi_profile *profp) +{ + struct capi_ctr *card; + + if (contr == 0) { + profp->ncontroller = ncards; + return CAPI_NOERROR; + } + card = get_capi_ctr_by_nr(contr); + if (!card || card->cardstate != CARD_RUNNING) + return CAPI_REGNOTINSTALLED; + + memcpy((void *) profp, &card->profile, + sizeof(struct capi_profile)); + return CAPI_NOERROR; +} + +EXPORT_SYMBOL(capi20_get_profile); + +#ifdef CONFIG_AVMB1_COMPAT +static int old_capi_manufacturer(unsigned int cmd, void __user *data) +{ + avmb1_loadandconfigdef ldef; + avmb1_extcarddef cdef; + avmb1_resetdef rdef; + capicardparams cparams; + struct capi_ctr *card; + struct capi_driver *driver = NULL; + capiloaddata ldata; + struct list_head *l; + unsigned long flags; + int retval; + + switch (cmd) { + case AVMB1_ADDCARD: + case AVMB1_ADDCARD_WITH_TYPE: + if (cmd == AVMB1_ADDCARD) { + if ((retval = copy_from_user(&cdef, data, + sizeof(avmb1_carddef)))) + return retval; + cdef.cardtype = AVM_CARDTYPE_B1; + } else { + if ((retval = copy_from_user(&cdef, data, + sizeof(avmb1_extcarddef)))) + return retval; + } + cparams.port = cdef.port; + cparams.irq = cdef.irq; + cparams.cardnr = cdef.cardnr; + + read_lock_irqsave(&capi_drivers_list_lock, flags); + switch (cdef.cardtype) { + case AVM_CARDTYPE_B1: + list_for_each(l, &capi_drivers) { + driver = list_entry(l, struct capi_driver, list); + if (strcmp(driver->name, "b1isa") == 0) + break; + } + break; + case AVM_CARDTYPE_T1: + list_for_each(l, &capi_drivers) { + driver = list_entry(l, struct capi_driver, list); + if (strcmp(driver->name, "t1isa") == 0) + break; + } + break; + default: + driver = NULL; + break; + } + if (!driver) { + read_unlock_irqrestore(&capi_drivers_list_lock, flags); + printk(KERN_ERR "kcapi: driver not loaded.\n"); + return -EIO; + } + if (!driver->add_card) { + read_unlock_irqrestore(&capi_drivers_list_lock, flags); + printk(KERN_ERR "kcapi: driver has no add card function.\n"); + return -EIO; + } + + retval = driver->add_card(driver, &cparams); + read_unlock_irqrestore(&capi_drivers_list_lock, flags); + return retval; + + case AVMB1_LOAD: + case AVMB1_LOAD_AND_CONFIG: + + if (cmd == AVMB1_LOAD) { + if (copy_from_user(&ldef, data, + sizeof(avmb1_loaddef))) + return -EFAULT; + ldef.t4config.len = 0; + ldef.t4config.data = NULL; + } else { + if (copy_from_user(&ldef, data, + sizeof(avmb1_loadandconfigdef))) + return -EFAULT; + } + card = get_capi_ctr_by_nr(ldef.contr); + card = capi_ctr_get(card); + if (!card) + return -ESRCH; + if (card->load_firmware == 0) { + printk(KERN_DEBUG "kcapi: load: no load function\n"); + return -ESRCH; + } + + if (ldef.t4file.len <= 0) { + printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len); + return -EINVAL; + } + if (ldef.t4file.data == 0) { + printk(KERN_DEBUG "kcapi: load: invalid parameter: dataptr is 0\n"); + return -EINVAL; + } + + ldata.firmware.user = 1; + ldata.firmware.data = ldef.t4file.data; + ldata.firmware.len = ldef.t4file.len; + ldata.configuration.user = 1; + ldata.configuration.data = ldef.t4config.data; + ldata.configuration.len = ldef.t4config.len; + + if (card->cardstate != CARD_DETECTED) { + printk(KERN_INFO "kcapi: load: contr=%d not in detect state\n", ldef.contr); + return -EBUSY; + } + card->cardstate = CARD_LOADING; + + retval = card->load_firmware(card, &ldata); + + if (retval) { + card->cardstate = CARD_DETECTED; + capi_ctr_put(card); + return retval; + } + + while (card->cardstate != CARD_RUNNING) { + + msleep_interruptible(100); /* 0.1 sec */ + + if (signal_pending(current)) { + capi_ctr_put(card); + return -EINTR; + } + } + capi_ctr_put(card); + return 0; + + case AVMB1_RESETCARD: + if (copy_from_user(&rdef, data, sizeof(avmb1_resetdef))) + return -EFAULT; + card = get_capi_ctr_by_nr(rdef.contr); + if (!card) + return -ESRCH; + + if (card->cardstate == CARD_DETECTED) + return 0; + + card->reset_ctr(card); + + while (card->cardstate > CARD_DETECTED) { + + msleep_interruptible(100); /* 0.1 sec */ + + if (signal_pending(current)) + return -EINTR; + } + return 0; + + } + return -EINVAL; +} +#endif + +int capi20_manufacturer(unsigned int cmd, void __user *data) +{ + struct capi_ctr *card; + + switch (cmd) { +#ifdef CONFIG_AVMB1_COMPAT + case AVMB1_LOAD: + case AVMB1_LOAD_AND_CONFIG: + case AVMB1_RESETCARD: + case AVMB1_GET_CARDINFO: + case AVMB1_REMOVECARD: + return old_capi_manufacturer(cmd, data); +#endif + case KCAPI_CMD_TRACE: + { + kcapi_flagdef fdef; + + if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef))) + return -EFAULT; + + card = get_capi_ctr_by_nr(fdef.contr); + if (!card) + return -ESRCH; + + card->traceflag = fdef.flag; + printk(KERN_INFO "kcapi: contr %d set trace=%d\n", + card->cnr, card->traceflag); + return 0; + } + case KCAPI_CMD_ADDCARD: + { + struct list_head *l; + struct capi_driver *driver = NULL; + capicardparams cparams; + kcapi_carddef cdef; + int retval; + + if ((retval = copy_from_user(&cdef, data, sizeof(cdef)))) + return retval; + + cparams.port = cdef.port; + cparams.irq = cdef.irq; + cparams.membase = cdef.membase; + cparams.cardnr = cdef.cardnr; + cparams.cardtype = 0; + cdef.driver[sizeof(cdef.driver)-1] = 0; + + list_for_each(l, &capi_drivers) { + driver = list_entry(l, struct capi_driver, list); + if (strcmp(driver->name, cdef.driver) == 0) + break; + } + if (driver == 0) { + printk(KERN_ERR "kcapi: driver \"%s\" not loaded.\n", + cdef.driver); + return -ESRCH; + } + + if (!driver->add_card) { + printk(KERN_ERR "kcapi: driver \"%s\" has no add card function.\n", cdef.driver); + return -EIO; + } + + return driver->add_card(driver, &cparams); + } + + default: + printk(KERN_ERR "kcapi: manufacturer command %d unknown.\n", + cmd); + break; + + } + return -EINVAL; +} + +EXPORT_SYMBOL(capi20_manufacturer); + +/* temporary hack */ +void capi20_set_callback(struct capi20_appl *ap, + void (*callback) (unsigned int cmd, __u32 contr, void *data)) +{ + ap->callback = callback; +} + +EXPORT_SYMBOL(capi20_set_callback); + +/* ------------------------------------------------------------- */ +/* -------- Init & Cleanup ------------------------------------- */ +/* ------------------------------------------------------------- */ + +/* + * init / exit functions + */ + +static int __init kcapi_init(void) +{ + char *p; + char rev[32]; + + kcapi_proc_init(); + + if ((p = strchr(revision, ':')) != 0 && p[1]) { + strlcpy(rev, p + 2, sizeof(rev)); + if ((p = strchr(rev, '$')) != 0 && p > rev) + *(p-1) = 0; + } else + strcpy(rev, "1.0"); + + printk(KERN_NOTICE "CAPI Subsystem Rev %s\n", rev); + + return 0; +} + +static void __exit kcapi_exit(void) +{ + kcapi_proc_exit(); + + /* make sure all notifiers are finished */ + flush_scheduled_work(); +} + +module_init(kcapi_init); +module_exit(kcapi_exit); |