1706 lines
45 KiB
C
1706 lines
45 KiB
C
/* $Id$
|
|
*
|
|
* ISDN lowlevel-module for Eicon active cards.
|
|
*
|
|
* Copyright 1997 by Fritz Elfert (fritz@isdn4linux.de)
|
|
* Copyright 1998-2000 by Armin Schindler (mac@melware.de)
|
|
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
|
|
*
|
|
* Thanks to Eicon Networks for
|
|
* documents, informations and hardware.
|
|
*
|
|
* Deutsche Mailbox Saar-Lor-Lux GmbH
|
|
* for sponsoring and testing fax
|
|
* capabilities with Diva Server cards.
|
|
* (dor@deutschemailbox.de)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#define DRIVERNAME "Eicon active ISDN driver"
|
|
#define DRIVERRELEASE "2.0"
|
|
#define DRIVERPATCH ".16"
|
|
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#ifdef CONFIG_MCA
|
|
#include <linux/mca.h>
|
|
#endif /* CONFIG_MCA */
|
|
|
|
#include "eicon.h"
|
|
|
|
#include "../avmb1/capicmd.h" /* this should be moved in a common place */
|
|
|
|
#undef N_DATA
|
|
#include "adapter.h"
|
|
#include "uxio.h"
|
|
|
|
#define INCLUDE_INLINE_FUNCS
|
|
|
|
static eicon_card *cards = (eicon_card *) NULL; /* glob. var , contains
|
|
start of card-list */
|
|
|
|
static char *eicon_revision = "$Revision$";
|
|
|
|
extern char *eicon_pci_revision;
|
|
extern char *eicon_isa_revision;
|
|
extern char *eicon_idi_revision;
|
|
|
|
extern int do_ioctl(struct inode *pDivasInode, struct file *pDivasFile,
|
|
unsigned int command, unsigned long arg);
|
|
extern void eicon_pci_init_conf(eicon_card *card);
|
|
|
|
#ifdef MODULE
|
|
#define MOD_USE_COUNT (GET_USE_COUNT (&__this_module))
|
|
#endif
|
|
|
|
#define EICON_CTRL_VERSION 2
|
|
|
|
ulong DebugVar;
|
|
|
|
spinlock_t eicon_lock;
|
|
|
|
DESCRIPTOR idi_d[32];
|
|
|
|
/* Parameters to be set by insmod */
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
static int membase = -1;
|
|
static int irq = -1;
|
|
#endif
|
|
static char *id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
|
|
|
MODULE_DESCRIPTION( "Driver for Eicon active ISDN cards");
|
|
MODULE_AUTHOR( "Armin Schindler");
|
|
MODULE_SUPPORTED_DEVICE( "ISDN subsystem");
|
|
MODULE_PARM_DESC(id, "ID-String of first card");
|
|
MODULE_PARM(id, "s");
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
MODULE_PARM_DESC(membase, "Base address of first ISA card");
|
|
MODULE_PARM_DESC(irq, "IRQ of first card");
|
|
MODULE_PARM(membase, "i");
|
|
MODULE_PARM(irq, "i");
|
|
#endif
|
|
|
|
char *eicon_ctype_name[] = {
|
|
"ISDN-S",
|
|
"ISDN-SX",
|
|
"ISDN-SCOM",
|
|
"ISDN-QUADRO",
|
|
"ISDN-S2M",
|
|
"DIVA Server BRI/PCI",
|
|
"DIVA Server 4BRI/PCI",
|
|
"DIVA Server 4BRI/PCI",
|
|
"DIVA Server PRI/PCI"
|
|
};
|
|
|
|
static char *
|
|
eicon_getrev(const char *revision)
|
|
{
|
|
char *rev;
|
|
char *p;
|
|
if ((p = strchr(revision, ':'))) {
|
|
rev = p + 2;
|
|
p = strchr(rev, '$');
|
|
*--p = 0;
|
|
} else rev = "?.??";
|
|
return rev;
|
|
|
|
}
|
|
|
|
static eicon_chan *
|
|
find_channel(eicon_card *card, int channel)
|
|
{
|
|
if ((channel >= 0) && (channel < card->nchannels))
|
|
return &(card->bch[channel]);
|
|
eicon_log(card, 1, "eicon: Invalid channel %d\n", channel);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef CONFIG_PCI
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
/*
|
|
* Find pcicard with given card number
|
|
*/
|
|
static inline eicon_card *
|
|
eicon_findnpcicard(int driverid)
|
|
{
|
|
eicon_card *p = cards;
|
|
|
|
while (p) {
|
|
if ((p->regname[strlen(p->regname)-1] == (driverid + '0')) &&
|
|
(p->bus == EICON_BUS_PCI))
|
|
return p;
|
|
p = p->next;
|
|
}
|
|
return (eicon_card *) 0;
|
|
}
|
|
#endif
|
|
#endif /* CONFIG_PCI */
|
|
|
|
static void
|
|
eicon_rcv_dispatch(struct eicon_card *card)
|
|
{
|
|
switch (card->bus) {
|
|
case EICON_BUS_ISA:
|
|
case EICON_BUS_MCA:
|
|
case EICON_BUS_PCI:
|
|
eicon_io_rcv_dispatch(card);
|
|
break;
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
|
|
}
|
|
}
|
|
|
|
static void
|
|
eicon_ack_dispatch(struct eicon_card *card)
|
|
{
|
|
switch (card->bus) {
|
|
case EICON_BUS_ISA:
|
|
case EICON_BUS_MCA:
|
|
case EICON_BUS_PCI:
|
|
eicon_io_ack_dispatch(card);
|
|
break;
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
|
|
}
|
|
}
|
|
|
|
static void
|
|
eicon_transmit(struct eicon_card *card)
|
|
{
|
|
switch (card->bus) {
|
|
case EICON_BUS_ISA:
|
|
case EICON_BUS_MCA:
|
|
case EICON_BUS_PCI:
|
|
eicon_io_transmit(card);
|
|
break;
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon_transmit: Illegal bustype %d\n", card->bus);
|
|
}
|
|
}
|
|
|
|
static int
|
|
eicon_command(eicon_card * card, isdn_ctrl * c)
|
|
{
|
|
ulong a;
|
|
eicon_chan *chan;
|
|
eicon_cdef cdef;
|
|
#ifdef CONFIG_PCI
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
dia_start_t dstart;
|
|
int idi_length = 0;
|
|
#endif
|
|
#endif
|
|
isdn_ctrl cmd;
|
|
int ret = 0;
|
|
unsigned long flags;
|
|
|
|
eicon_log(card, 16, "eicon_cmd 0x%x with arg 0x%lx (0x%lx)\n",
|
|
c->command, c->arg, (ulong) *c->parm.num);
|
|
|
|
switch (c->command) {
|
|
case ISDN_CMD_IOCTL:
|
|
memcpy(&a, c->parm.num, sizeof(ulong));
|
|
switch (c->arg) {
|
|
case EICON_IOCTL_GETVER:
|
|
return(EICON_CTRL_VERSION);
|
|
case EICON_IOCTL_GETTYPE:
|
|
if (card->bus == EICON_BUS_PCI) {
|
|
copy_to_user((char *)a, &card->hwif.pci.master, sizeof(int));
|
|
}
|
|
return(card->type);
|
|
case EICON_IOCTL_GETMMIO:
|
|
switch (card->bus) {
|
|
case EICON_BUS_ISA:
|
|
case EICON_BUS_MCA:
|
|
return (int)card->hwif.isa.shmem;
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon: Illegal BUS type %d\n",
|
|
card->bus);
|
|
ret = -ENODEV;
|
|
}
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
case EICON_IOCTL_SETMMIO:
|
|
if (card->flags & EICON_FLAGS_LOADED)
|
|
return -EBUSY;
|
|
switch (card->bus) {
|
|
case EICON_BUS_ISA:
|
|
if (eicon_isa_find_card(a,
|
|
card->hwif.isa.irq,
|
|
card->regname) < 0)
|
|
return -EFAULT;
|
|
card->hwif.isa.shmem = (eicon_isa_shmem *)a;
|
|
return 0;
|
|
case EICON_BUS_MCA:
|
|
#if CONFIG_MCA
|
|
if (eicon_mca_find_card(
|
|
0, a,
|
|
card->hwif.isa.irq,
|
|
card->regname) < 0)
|
|
return -EFAULT;
|
|
card->hwif.isa.shmem = (eicon_isa_shmem *)a;
|
|
return 0;
|
|
#endif /* CONFIG_MCA */
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon: Illegal BUS type %d\n",
|
|
card->bus);
|
|
ret = -ENODEV;
|
|
}
|
|
#endif
|
|
case EICON_IOCTL_GETIRQ:
|
|
switch (card->bus) {
|
|
case EICON_BUS_ISA:
|
|
case EICON_BUS_MCA:
|
|
return card->hwif.isa.irq;
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon: Illegal BUS type %d\n",
|
|
card->bus);
|
|
ret = -ENODEV;
|
|
}
|
|
case EICON_IOCTL_SETIRQ:
|
|
if (card->flags & EICON_FLAGS_LOADED)
|
|
return -EBUSY;
|
|
if ((a < 2) || (a > 15))
|
|
return -EFAULT;
|
|
switch (card->bus) {
|
|
case EICON_BUS_ISA:
|
|
case EICON_BUS_MCA:
|
|
card->hwif.isa.irq = a;
|
|
return 0;
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon: Illegal BUS type %d\n",
|
|
card->bus);
|
|
ret = -ENODEV;
|
|
}
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
case EICON_IOCTL_LOADBOOT:
|
|
if (card->flags & EICON_FLAGS_RUNNING)
|
|
return -EBUSY;
|
|
switch (card->bus) {
|
|
case EICON_BUS_ISA:
|
|
case EICON_BUS_MCA:
|
|
ret = eicon_isa_bootload(
|
|
&(card->hwif.isa),
|
|
&(((eicon_codebuf *)a)->isa));
|
|
break;
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon: Illegal BUS type %d\n",
|
|
card->bus);
|
|
ret = -ENODEV;
|
|
}
|
|
return ret;
|
|
#endif
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
case EICON_IOCTL_LOADISA:
|
|
if (card->flags & EICON_FLAGS_RUNNING)
|
|
return -EBUSY;
|
|
switch (card->bus) {
|
|
case EICON_BUS_ISA:
|
|
case EICON_BUS_MCA:
|
|
ret = eicon_isa_load(
|
|
&(card->hwif.isa),
|
|
&(((eicon_codebuf *)a)->isa));
|
|
if (!ret) {
|
|
card->flags |= EICON_FLAGS_LOADED;
|
|
card->flags |= EICON_FLAGS_RUNNING;
|
|
if (card->hwif.isa.channels > 1) {
|
|
cmd.command = ISDN_STAT_ADDCH;
|
|
cmd.driver = card->myid;
|
|
cmd.arg = card->hwif.isa.channels - 1;
|
|
card->interface.statcallb(&cmd);
|
|
}
|
|
cmd.command = ISDN_STAT_RUN;
|
|
cmd.driver = card->myid;
|
|
cmd.arg = 0;
|
|
card->interface.statcallb(&cmd);
|
|
}
|
|
break;
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon: Illegal BUS type %d\n",
|
|
card->bus);
|
|
ret = -ENODEV;
|
|
}
|
|
return ret;
|
|
#endif
|
|
case EICON_IOCTL_MANIF:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!card->d)
|
|
return -ENODEV;
|
|
if (!card->d->features & DI_MANAGE)
|
|
return -ENODEV;
|
|
ret = eicon_idi_manage(
|
|
card,
|
|
(eicon_manifbuf *)a);
|
|
return ret;
|
|
|
|
case EICON_IOCTL_GETXLOG:
|
|
return -ENODEV;
|
|
|
|
case EICON_IOCTL_ADDCARD:
|
|
if ((ret = copy_from_user(&cdef, (char *)a, sizeof(cdef))))
|
|
return -EFAULT;
|
|
if (!(eicon_addcard(0, cdef.membase, cdef.irq, cdef.id, 0)))
|
|
return -EIO;
|
|
return 0;
|
|
case EICON_IOCTL_DEBUGVAR:
|
|
DebugVar = a;
|
|
eicon_log(card, 1, "Eicon: Debug Value set to %ld\n", DebugVar);
|
|
return 0;
|
|
#ifdef MODULE
|
|
case EICON_IOCTL_FREEIT:
|
|
while (MOD_USE_COUNT > 0) MOD_DEC_USE_COUNT;
|
|
MOD_INC_USE_COUNT;
|
|
return 0;
|
|
#endif
|
|
case EICON_IOCTL_LOADPCI:
|
|
eicon_log(card, 1, "Eicon: Wrong version of load-utility,\n");
|
|
eicon_log(card, 1, "Eicon: re-compile eiconctrl !\n");
|
|
eicon_log(card, 1, "Eicon: Maybe update of utility is necessary !\n");
|
|
return -EINVAL;
|
|
default:
|
|
#ifdef CONFIG_PCI
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
if (c->arg < EICON_IOCTL_DIA_OFFSET)
|
|
return -EINVAL;
|
|
if (copy_from_user(&dstart, (char *)a, sizeof(dstart)))
|
|
return -1;
|
|
if (!(card = eicon_findnpcicard(dstart.card_id)))
|
|
return -EINVAL;
|
|
ret = do_ioctl(NULL, NULL,
|
|
c->arg - EICON_IOCTL_DIA_OFFSET,
|
|
(unsigned long) a);
|
|
if (((c->arg - EICON_IOCTL_DIA_OFFSET)==DIA_IOCTL_START) && (!ret)) {
|
|
if (card->type != EICON_CTYPE_MAESTRAQ) {
|
|
DIVA_DIDD_Read(idi_d, sizeof(idi_d));
|
|
for(idi_length = 0; idi_length < 32; idi_length++) {
|
|
if (idi_d[idi_length].type == 0) break;
|
|
}
|
|
if ((idi_length < 1) || (idi_length >= 32)) {
|
|
eicon_log(card, 1, "eicon: invalid idi table length.\n");
|
|
break;
|
|
}
|
|
card->d = &idi_d[idi_length - 1];
|
|
card->flags |= EICON_FLAGS_LOADED;
|
|
card->flags |= EICON_FLAGS_RUNNING;
|
|
eicon_pci_init_conf(card);
|
|
if (card->d->channels > 1) {
|
|
cmd.command = ISDN_STAT_ADDCH;
|
|
cmd.driver = card->myid;
|
|
cmd.arg = card->d->channels - 1;
|
|
card->interface.statcallb(&cmd);
|
|
}
|
|
cmd.command = ISDN_STAT_RUN;
|
|
cmd.driver = card->myid;
|
|
cmd.arg = 0;
|
|
card->interface.statcallb(&cmd);
|
|
eicon_log(card, 1, "Eicon: %s started, %d channels (feat. 0x%x)\n",
|
|
(card->type == EICON_CTYPE_MAESTRA) ? "BRI" : "PRI",
|
|
card->d->channels, card->d->features);
|
|
} else {
|
|
int i;
|
|
DIVA_DIDD_Read(idi_d, sizeof(idi_d));
|
|
for(idi_length = 0; idi_length < 32; idi_length++)
|
|
if (idi_d[idi_length].type == 0) break;
|
|
if ((idi_length < 1) || (idi_length >= 32)) {
|
|
eicon_log(card, 1, "eicon: invalid idi table length.\n");
|
|
break;
|
|
}
|
|
for(i = 3; i >= 0; i--) {
|
|
if (!(card = eicon_findnpcicard(dstart.card_id - i)))
|
|
return -EINVAL;
|
|
|
|
card->flags |= EICON_FLAGS_LOADED;
|
|
card->flags |= EICON_FLAGS_RUNNING;
|
|
card->d = &idi_d[idi_length - (i+1)];
|
|
eicon_pci_init_conf(card);
|
|
if (card->d->channels > 1) {
|
|
cmd.command = ISDN_STAT_ADDCH;
|
|
cmd.driver = card->myid;
|
|
cmd.arg = card->d->channels - 1;
|
|
card->interface.statcallb(&cmd);
|
|
}
|
|
cmd.command = ISDN_STAT_RUN;
|
|
cmd.driver = card->myid;
|
|
cmd.arg = 0;
|
|
card->interface.statcallb(&cmd);
|
|
eicon_log(card, 1, "Eicon: %d/4BRI started, %d channels (feat. 0x%x)\n",
|
|
4-i, card->d->channels, card->d->features);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
#else
|
|
return -EINVAL;
|
|
#endif
|
|
#endif /* CONFIG_PCI */
|
|
}
|
|
break;
|
|
case ISDN_CMD_DIAL:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
spin_lock_irqsave(&eicon_lock, flags);
|
|
if ((chan->fsm_state != EICON_STATE_NULL) && (chan->fsm_state != EICON_STATE_LISTEN)) {
|
|
spin_unlock_irqrestore(&eicon_lock, flags);
|
|
eicon_log(card, 1, "Dial on channel %d with state %d\n",
|
|
chan->No, chan->fsm_state);
|
|
return -EBUSY;
|
|
}
|
|
chan->fsm_state = EICON_STATE_OCALL;
|
|
spin_unlock_irqrestore(&eicon_lock, flags);
|
|
|
|
ret = idi_connect_req(card, chan, c->parm.setup.phone,
|
|
c->parm.setup.eazmsn,
|
|
c->parm.setup.si1,
|
|
c->parm.setup.si2);
|
|
if (ret) {
|
|
cmd.driver = card->myid;
|
|
cmd.command = ISDN_STAT_DHUP;
|
|
cmd.arg &= 0x1f;
|
|
card->interface.statcallb(&cmd);
|
|
}
|
|
return ret;
|
|
case ISDN_CMD_ACCEPTD:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
if (chan->fsm_state == EICON_STATE_ICALL) {
|
|
idi_connect_res(card, chan);
|
|
}
|
|
return 0;
|
|
case ISDN_CMD_ACCEPTB:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
return 0;
|
|
case ISDN_CMD_HANGUP:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
idi_hangup(card, chan);
|
|
return 0;
|
|
case ISDN_CMD_SETEAZ:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
chan->eazmask = 0x3ff;
|
|
eicon_idi_listen_req(card, chan);
|
|
return 0;
|
|
case ISDN_CMD_CLREAZ:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
chan->eazmask = 0;
|
|
eicon_idi_listen_req(card, chan);
|
|
return 0;
|
|
case ISDN_CMD_SETL2:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
chan->l2prot = (c->arg >> 8);
|
|
return 0;
|
|
case ISDN_CMD_GETL2:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
return chan->l2prot;
|
|
case ISDN_CMD_SETL3:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
chan->l3prot = (c->arg >> 8);
|
|
#ifdef CONFIG_ISDN_TTY_FAX
|
|
if (chan->l3prot == ISDN_PROTO_L3_FCLASS2) {
|
|
chan->fax = c->parm.fax;
|
|
eicon_log(card, 128, "idi_cmd: Ch%d: SETL3 struct fax=0x%x\n",chan->No, chan->fax);
|
|
}
|
|
#endif
|
|
return 0;
|
|
case ISDN_CMD_GETL3:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
return chan->l3prot;
|
|
case ISDN_CMD_GETEAZ:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
eicon_log(card, 1, "eicon CMD_GETEAZ not implemented\n");
|
|
return 0;
|
|
case ISDN_CMD_SETSIL:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
eicon_log(card, 1, "eicon CMD_SETSIL not implemented\n");
|
|
return 0;
|
|
case ISDN_CMD_GETSIL:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
eicon_log(card, 1, "eicon CMD_GETSIL not implemented\n");
|
|
return 0;
|
|
case ISDN_CMD_LOCK:
|
|
MOD_INC_USE_COUNT;
|
|
return 0;
|
|
case ISDN_CMD_UNLOCK:
|
|
MOD_DEC_USE_COUNT;
|
|
return 0;
|
|
#ifdef CONFIG_ISDN_TTY_FAX
|
|
case ISDN_CMD_FAXCMD:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
if (!chan->fax)
|
|
break;
|
|
idi_fax_cmd(card, chan);
|
|
return 0;
|
|
#endif
|
|
case ISDN_CMD_AUDIO:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
idi_audio_cmd(card, chan, c->arg >> 8, c->parm.num);
|
|
return 0;
|
|
case CAPI_PUT_MESSAGE:
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, c->arg & 0x1f)))
|
|
break;
|
|
if (c->parm.cmsg.Length < 8)
|
|
break;
|
|
switch(c->parm.cmsg.Command) {
|
|
case CAPI_FACILITY:
|
|
if (c->parm.cmsg.Subcommand == CAPI_REQ)
|
|
return(capipmsg(card, chan, &c->parm.cmsg));
|
|
break;
|
|
case CAPI_MANUFACTURER:
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Find card with given driverId
|
|
*/
|
|
static inline eicon_card *
|
|
eicon_findcard(int driverid)
|
|
{
|
|
eicon_card *p = cards;
|
|
|
|
while (p) {
|
|
if (p->myid == driverid)
|
|
return p;
|
|
p = p->next;
|
|
}
|
|
return (eicon_card *) 0;
|
|
}
|
|
|
|
/*
|
|
* Wrapper functions for interface to linklevel
|
|
*/
|
|
static int
|
|
if_command(isdn_ctrl * c)
|
|
{
|
|
eicon_card *card = eicon_findcard(c->driver);
|
|
|
|
if (card)
|
|
return (eicon_command(card, c));
|
|
printk(KERN_ERR
|
|
"eicon: if_command %d called with invalid driverId %d!\n",
|
|
c->command, c->driver);
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int
|
|
if_writecmd(const u_char * buf, int len, int user, int id, int channel)
|
|
{
|
|
#if 0
|
|
/* Not yet used */
|
|
eicon_card *card = eicon_findcard(id);
|
|
|
|
if (card) {
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return (len);
|
|
return (len);
|
|
}
|
|
printk(KERN_ERR
|
|
"eicon: if_writecmd called with invalid driverId!\n");
|
|
#endif
|
|
return (len);
|
|
}
|
|
|
|
static int
|
|
if_readstatus(u_char * buf, int len, int user, int id, int channel)
|
|
{
|
|
int count = 0;
|
|
int cnt = 0;
|
|
ulong flags = 0;
|
|
u_char *p = buf;
|
|
struct sk_buff *skb;
|
|
|
|
eicon_card *card = eicon_findcard(id);
|
|
|
|
if (card) {
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
|
|
spin_lock_irqsave(&eicon_lock, flags);
|
|
while((skb = skb_dequeue(&card->statq))) {
|
|
|
|
if ((skb->len + count) > len)
|
|
cnt = len - count;
|
|
else
|
|
cnt = skb->len;
|
|
|
|
if (user)
|
|
copy_to_user(p, skb->data, cnt);
|
|
else
|
|
memcpy(p, skb->data, cnt);
|
|
|
|
count += cnt;
|
|
p += cnt;
|
|
|
|
if (cnt == skb->len) {
|
|
dev_kfree_skb(skb);
|
|
if (card->statq_entries > 0)
|
|
card->statq_entries--;
|
|
} else {
|
|
skb_pull(skb, cnt);
|
|
skb_queue_head(&card->statq, skb);
|
|
spin_unlock_irqrestore(&eicon_lock, flags);
|
|
return count;
|
|
}
|
|
}
|
|
card->statq_entries = 0;
|
|
spin_unlock_irqrestore(&eicon_lock, flags);
|
|
return count;
|
|
}
|
|
printk(KERN_ERR
|
|
"eicon: if_readstatus called with invalid driverId!\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
|
|
{
|
|
eicon_card *card = eicon_findcard(id);
|
|
eicon_chan *chan;
|
|
int ret = 0;
|
|
int len;
|
|
|
|
len = skb->len;
|
|
|
|
if (card) {
|
|
if (!card->flags & EICON_FLAGS_RUNNING)
|
|
return -ENODEV;
|
|
if (!(chan = find_channel(card, channel)))
|
|
return -ENODEV;
|
|
|
|
if (chan->fsm_state == EICON_STATE_ACTIVE) {
|
|
#ifdef CONFIG_ISDN_TTY_FAX
|
|
if (chan->l2prot == ISDN_PROTO_L2_FAX) {
|
|
if ((ret = idi_faxdata_send(card, chan, skb)) > 0)
|
|
ret = len;
|
|
}
|
|
else
|
|
#endif
|
|
ret = idi_send_data(card, chan, ack, skb, 1, 1);
|
|
return (ret);
|
|
} else {
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
printk(KERN_ERR
|
|
"eicon: if_sendbuf called with invalid driverId!\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* jiftime() copied from HiSax */
|
|
static inline int jiftime(char *s, long mark)
|
|
{
|
|
s += 8;
|
|
|
|
*s-- = '\0';
|
|
*s-- = mark % 10 + '0';
|
|
mark /= 10;
|
|
*s-- = mark % 10 + '0';
|
|
mark /= 10;
|
|
*s-- = '.';
|
|
*s-- = mark % 10 + '0';
|
|
mark /= 10;
|
|
*s-- = mark % 6 + '0';
|
|
mark /= 6;
|
|
*s-- = ':';
|
|
*s-- = mark % 10 + '0';
|
|
mark /= 10;
|
|
*s-- = mark % 10 + '0';
|
|
return(8);
|
|
}
|
|
|
|
void
|
|
eicon_putstatus(eicon_card * card, char * buf)
|
|
{
|
|
ulong flags;
|
|
int count;
|
|
isdn_ctrl cmd;
|
|
u_char *p;
|
|
struct sk_buff *skb;
|
|
|
|
if (!card) {
|
|
if (!(card = cards))
|
|
return;
|
|
}
|
|
|
|
spin_lock_irqsave(&eicon_lock, flags);
|
|
count = strlen(buf);
|
|
skb = alloc_skb(count, GFP_ATOMIC);
|
|
if (!skb) {
|
|
spin_unlock_irqrestore(&eicon_lock, flags);
|
|
printk(KERN_ERR "eicon: could not alloc skb in putstatus\n");
|
|
return;
|
|
}
|
|
p = skb_put(skb, count);
|
|
memcpy(p, buf, count);
|
|
|
|
skb_queue_tail(&card->statq, skb);
|
|
|
|
if (card->statq_entries >= MAX_STATUS_BUFFER) {
|
|
if ((skb = skb_dequeue(&card->statq))) {
|
|
count -= skb->len;
|
|
dev_kfree_skb(skb);
|
|
} else
|
|
count = 0;
|
|
} else
|
|
card->statq_entries++;
|
|
|
|
spin_unlock_irqrestore(&eicon_lock, flags);
|
|
if (count) {
|
|
cmd.command = ISDN_STAT_STAVAIL;
|
|
cmd.driver = card->myid;
|
|
cmd.arg = count;
|
|
card->interface.statcallb(&cmd);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Debug and Log
|
|
*/
|
|
void
|
|
eicon_log(eicon_card * card, int level, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char Line[160];
|
|
u_char *p;
|
|
|
|
|
|
if ((DebugVar & level) || (DebugVar & 256)) {
|
|
va_start(args, fmt);
|
|
|
|
if (DebugVar & level) {
|
|
if (DebugVar & 256) {
|
|
/* log-buffer */
|
|
p = Line;
|
|
p += jiftime(p, jiffies);
|
|
*p++ = 32;
|
|
p += vsprintf(p, fmt, args);
|
|
*p = 0;
|
|
eicon_putstatus(card, Line);
|
|
} else {
|
|
/* printk, syslogd */
|
|
vsprintf(Line, fmt, args);
|
|
printk(KERN_DEBUG "%s", Line);
|
|
}
|
|
}
|
|
|
|
va_end(args);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Allocate a new card-struct, initialize it
|
|
* link it into cards-list.
|
|
*/
|
|
static void
|
|
eicon_alloccard(int Type, int membase, int irq, char *id, int card_id)
|
|
{
|
|
int i;
|
|
int j;
|
|
int qloop;
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
char qid[5];
|
|
#endif
|
|
eicon_card *card;
|
|
|
|
qloop = (Type == EICON_CTYPE_QUADRO)?2:0;
|
|
for (i = 0; i <= qloop; i++) {
|
|
if (!(card = (eicon_card *) kmalloc(sizeof(eicon_card), GFP_KERNEL))) {
|
|
eicon_log(card, 1,
|
|
"eicon: (%s) Could not allocate card-struct.\n", id);
|
|
return;
|
|
}
|
|
memset((char *) card, 0, sizeof(eicon_card));
|
|
skb_queue_head_init(&card->sndq);
|
|
skb_queue_head_init(&card->rcvq);
|
|
skb_queue_head_init(&card->rackq);
|
|
skb_queue_head_init(&card->sackq);
|
|
skb_queue_head_init(&card->statq);
|
|
card->statq_entries = 0;
|
|
card->snd_tq.routine = (void *) (void *) eicon_transmit;
|
|
card->snd_tq.data = card;
|
|
card->rcv_tq.routine = (void *) (void *) eicon_rcv_dispatch;
|
|
card->rcv_tq.data = card;
|
|
card->ack_tq.routine = (void *) (void *) eicon_ack_dispatch;
|
|
card->ack_tq.data = card;
|
|
card->interface.maxbufsize = 4000;
|
|
card->interface.command = if_command;
|
|
card->interface.writebuf_skb = if_sendbuf;
|
|
card->interface.writecmd = if_writecmd;
|
|
card->interface.readstat = if_readstatus;
|
|
card->interface.features =
|
|
ISDN_FEATURE_L2_X75I |
|
|
ISDN_FEATURE_L2_HDLC |
|
|
ISDN_FEATURE_L2_TRANS |
|
|
ISDN_FEATURE_L3_TRANS |
|
|
ISDN_FEATURE_P_UNKNOWN;
|
|
card->interface.hl_hdrlen = 20;
|
|
card->ptype = ISDN_PTYPE_UNKNOWN;
|
|
strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
|
|
card->myid = -1;
|
|
card->type = Type;
|
|
switch (Type) {
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
#if CONFIG_MCA /* only needed for MCA */
|
|
case EICON_CTYPE_S:
|
|
case EICON_CTYPE_SX:
|
|
case EICON_CTYPE_SCOM:
|
|
if (MCA_bus) {
|
|
if (membase == -1)
|
|
membase = EICON_ISA_MEMBASE;
|
|
if (irq == -1)
|
|
irq = EICON_ISA_IRQ;
|
|
card->bus = EICON_BUS_MCA;
|
|
card->hwif.isa.card = (void *)card;
|
|
card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
|
|
card->hwif.isa.physmem = (unsigned long)membase;
|
|
card->hwif.isa.master = 1;
|
|
|
|
card->hwif.isa.irq = irq;
|
|
card->hwif.isa.type = Type;
|
|
card->nchannels = 2;
|
|
card->interface.channels = 1;
|
|
} else {
|
|
printk(KERN_WARNING
|
|
"eicon (%s): no MCA bus detected.\n",
|
|
card->interface.id);
|
|
kfree(card);
|
|
return;
|
|
}
|
|
break;
|
|
#endif /* CONFIG_MCA */
|
|
case EICON_CTYPE_QUADRO:
|
|
if (membase == -1)
|
|
membase = EICON_ISA_MEMBASE;
|
|
if (irq == -1)
|
|
irq = EICON_ISA_IRQ;
|
|
card->bus = EICON_BUS_ISA;
|
|
card->hwif.isa.card = (void *)card;
|
|
card->hwif.isa.shmem = (eicon_isa_shmem *)(membase + (i+1) * EICON_ISA_QOFFSET);
|
|
card->hwif.isa.physmem = (unsigned long)(membase + (i+1) * EICON_ISA_QOFFSET);
|
|
card->hwif.isa.master = 0;
|
|
strcpy(card->interface.id, id);
|
|
if (id[strlen(id) - 1] == 'a') {
|
|
card->interface.id[strlen(id) - 1] = 'a' + i + 1;
|
|
} else {
|
|
sprintf(qid, "_%c",'2' + i);
|
|
strcat(card->interface.id, qid);
|
|
}
|
|
printk(KERN_INFO "Eicon: Quadro: Driver-Id %s added.\n",
|
|
card->interface.id);
|
|
if (i == 0) {
|
|
eicon_card *p = cards;
|
|
while(p) {
|
|
if ((p->hwif.isa.master) && (p->hwif.isa.irq == irq)) {
|
|
p->qnext = card;
|
|
break;
|
|
}
|
|
p = p->next;
|
|
}
|
|
if (!p) {
|
|
eicon_log(card, 1, "eicon_alloccard: Quadro Master not found.\n");
|
|
kfree(card);
|
|
return;
|
|
}
|
|
} else {
|
|
cards->qnext = card;
|
|
}
|
|
card->hwif.isa.irq = irq;
|
|
card->hwif.isa.type = Type;
|
|
card->nchannels = 2;
|
|
card->interface.channels = 1;
|
|
break;
|
|
#endif
|
|
#ifdef CONFIG_PCI
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
case EICON_CTYPE_MAESTRA:
|
|
card->bus = EICON_BUS_PCI;
|
|
card->interface.features |=
|
|
ISDN_FEATURE_L2_V11096 |
|
|
ISDN_FEATURE_L2_V11019 |
|
|
ISDN_FEATURE_L2_V11038 |
|
|
ISDN_FEATURE_L2_MODEM |
|
|
ISDN_FEATURE_L2_FAX |
|
|
ISDN_FEATURE_L3_TRANSDSP |
|
|
ISDN_FEATURE_L3_FCLASS2;
|
|
card->hwif.pci.card = (void *)card;
|
|
card->hwif.pci.master = card_id;
|
|
card->hwif.pci.irq = irq;
|
|
card->hwif.pci.type = Type;
|
|
card->flags = 0;
|
|
card->nchannels = 2;
|
|
card->interface.channels = 1;
|
|
break;
|
|
|
|
case EICON_CTYPE_MAESTRAQ:
|
|
card->bus = EICON_BUS_PCI;
|
|
card->interface.features |=
|
|
ISDN_FEATURE_L2_V11096 |
|
|
ISDN_FEATURE_L2_V11019 |
|
|
ISDN_FEATURE_L2_V11038 |
|
|
ISDN_FEATURE_L2_MODEM |
|
|
ISDN_FEATURE_L2_FAX |
|
|
ISDN_FEATURE_L3_TRANSDSP |
|
|
ISDN_FEATURE_L3_FCLASS2;
|
|
card->hwif.pci.card = (void *)card;
|
|
card->hwif.pci.master = card_id;
|
|
card->hwif.pci.irq = irq;
|
|
card->hwif.pci.type = Type;
|
|
card->flags = 0;
|
|
card->nchannels = 2;
|
|
card->interface.channels = 1;
|
|
break;
|
|
|
|
case EICON_CTYPE_MAESTRAP:
|
|
card->bus = EICON_BUS_PCI;
|
|
card->interface.features |=
|
|
ISDN_FEATURE_L2_V11096 |
|
|
ISDN_FEATURE_L2_V11019 |
|
|
ISDN_FEATURE_L2_V11038 |
|
|
ISDN_FEATURE_L2_MODEM |
|
|
ISDN_FEATURE_L2_FAX |
|
|
ISDN_FEATURE_L3_TRANSDSP |
|
|
ISDN_FEATURE_L3_FCLASS2;
|
|
card->hwif.pci.card = (void *)card;
|
|
card->hwif.pci.master = card_id;
|
|
card->hwif.pci.irq = irq;
|
|
card->hwif.pci.type = Type;
|
|
card->flags = 0;
|
|
card->nchannels = 30;
|
|
card->interface.channels = 1;
|
|
break;
|
|
#endif
|
|
#endif
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
case EICON_CTYPE_ISABRI:
|
|
if (membase == -1)
|
|
membase = EICON_ISA_MEMBASE;
|
|
if (irq == -1)
|
|
irq = EICON_ISA_IRQ;
|
|
card->bus = EICON_BUS_ISA;
|
|
card->hwif.isa.card = (void *)card;
|
|
card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
|
|
card->hwif.isa.physmem = (unsigned long)membase;
|
|
card->hwif.isa.master = 1;
|
|
card->hwif.isa.irq = irq;
|
|
card->hwif.isa.type = Type;
|
|
card->nchannels = 2;
|
|
card->interface.channels = 1;
|
|
break;
|
|
case EICON_CTYPE_ISAPRI:
|
|
if (membase == -1)
|
|
membase = EICON_ISA_MEMBASE;
|
|
if (irq == -1)
|
|
irq = EICON_ISA_IRQ;
|
|
card->bus = EICON_BUS_ISA;
|
|
card->hwif.isa.card = (void *)card;
|
|
card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
|
|
card->hwif.isa.physmem = (unsigned long)membase;
|
|
card->hwif.isa.master = 1;
|
|
card->hwif.isa.irq = irq;
|
|
card->hwif.isa.type = Type;
|
|
card->nchannels = 30;
|
|
card->interface.channels = 1;
|
|
break;
|
|
#endif
|
|
default:
|
|
eicon_log(card, 1, "eicon_alloccard: Invalid type %d\n", Type);
|
|
kfree(card);
|
|
return;
|
|
}
|
|
if (!(card->bch = (eicon_chan *) kmalloc(sizeof(eicon_chan) * (card->nchannels + 1)
|
|
, GFP_KERNEL))) {
|
|
eicon_log(card, 1,
|
|
"eicon: (%s) Could not allocate bch-struct.\n", id);
|
|
kfree(card);
|
|
return;
|
|
}
|
|
for (j=0; j< (card->nchannels + 1); j++) {
|
|
memset((char *)&card->bch[j], 0, sizeof(eicon_chan));
|
|
card->bch[j].statectrl = 0;
|
|
card->bch[j].l2prot = ISDN_PROTO_L2_X75I;
|
|
card->bch[j].l3prot = ISDN_PROTO_L3_TRANS;
|
|
card->bch[j].e.D3Id = 0;
|
|
card->bch[j].e.B2Id = 0;
|
|
card->bch[j].e.Req = 0;
|
|
card->bch[j].No = j;
|
|
card->bch[j].tskb1 = NULL;
|
|
card->bch[j].tskb2 = NULL;
|
|
skb_queue_head_init(&card->bch[j].e.X);
|
|
skb_queue_head_init(&card->bch[j].e.R);
|
|
}
|
|
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
/* *** Diva Server *** */
|
|
if (!(card->dbuf = (DBUFFER *) kmalloc((sizeof(DBUFFER) * (card->nchannels + 1))*2
|
|
, GFP_KERNEL))) {
|
|
eicon_log(card, 1,
|
|
"eicon: (%s) Could not allocate DBUFFER-struct.\n", id);
|
|
kfree(card);
|
|
kfree(card->bch);
|
|
return;
|
|
}
|
|
if (!(card->sbuf = (BUFFERS *) kmalloc((sizeof(BUFFERS) * (card->nchannels + 1)) * 2, GFP_KERNEL))) {
|
|
eicon_log(card, 1,
|
|
"eicon: (%s) Could not allocate BUFFERS-struct.\n", id);
|
|
kfree(card);
|
|
kfree(card->bch);
|
|
kfree(card->dbuf);
|
|
return;
|
|
}
|
|
if (!(card->sbufp = (char *) kmalloc((270 * (card->nchannels + 1)) * 2, GFP_KERNEL))) {
|
|
eicon_log(card, 1,
|
|
"eicon: (%s) Could not allocate BUFFERSP-struct.\n", id);
|
|
kfree(card);
|
|
kfree(card->bch);
|
|
kfree(card->dbuf);
|
|
kfree(card->sbuf);
|
|
return;
|
|
}
|
|
for (j=0; j< (card->nchannels + 1); j++) {
|
|
memset((char *)&card->dbuf[j], 0, sizeof(DBUFFER));
|
|
card->bch[j].de.RBuffer = (DBUFFER *)&card->dbuf[j];
|
|
memset((char *)&card->dbuf[j+(card->nchannels+1)], 0, sizeof(BUFFERS));
|
|
card->bch[j].be.RBuffer = (DBUFFER *)&card->dbuf[j+(card->nchannels+1)];
|
|
|
|
memset((char *)&card->sbuf[j], 0, sizeof(BUFFERS));
|
|
card->bch[j].de.X = (BUFFERS *)&card->sbuf[j];
|
|
memset((char *)&card->sbuf[j+(card->nchannels+1)], 0, sizeof(BUFFERS));
|
|
card->bch[j].be.X = (BUFFERS *)&card->sbuf[j+(card->nchannels+1)];
|
|
|
|
memset((char *)&card->sbufp[j], 0, 270);
|
|
card->bch[j].de.X->P = (char *)&card->sbufp[j * 270];
|
|
memset((char *)&card->sbufp[j+(card->nchannels+1)], 0, 270);
|
|
card->bch[j].be.X->P = (char *)&card->sbufp[(j+(card->nchannels+1)) * 270];
|
|
}
|
|
/* *** */
|
|
#endif /* CONFIG_ISDN_DRV_EICON_PCI */
|
|
|
|
card->next = cards;
|
|
cards = card;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* register card at linklevel
|
|
*/
|
|
static int
|
|
eicon_registercard(eicon_card * card)
|
|
{
|
|
switch (card->bus) {
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
case EICON_BUS_ISA:
|
|
/* TODO something to print */
|
|
break;
|
|
#ifdef CONFIG_MCA
|
|
case EICON_BUS_MCA:
|
|
eicon_isa_printpar(&card->hwif.isa);
|
|
break;
|
|
#endif /* CONFIG_MCA */
|
|
#endif
|
|
case EICON_BUS_PCI:
|
|
break;
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon_registercard: Illegal BUS type %d\n",
|
|
card->bus);
|
|
return -1;
|
|
}
|
|
if (!register_isdn(&card->interface)) {
|
|
printk(KERN_WARNING
|
|
"eicon_registercard: Unable to register %s\n",
|
|
card->interface.id);
|
|
return -1;
|
|
}
|
|
card->myid = card->interface.channels;
|
|
sprintf(card->regname, "%s", card->interface.id);
|
|
return 0;
|
|
}
|
|
|
|
static void __exit
|
|
unregister_card(eicon_card * card)
|
|
{
|
|
isdn_ctrl cmd;
|
|
|
|
cmd.command = ISDN_STAT_UNLOAD;
|
|
cmd.driver = card->myid;
|
|
card->interface.statcallb(&cmd);
|
|
switch (card->bus) {
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
case EICON_BUS_ISA:
|
|
#ifdef CONFIG_MCA
|
|
case EICON_BUS_MCA:
|
|
#endif /* CONFIG_MCA */
|
|
eicon_isa_release(&card->hwif.isa);
|
|
break;
|
|
#endif
|
|
case EICON_BUS_PCI:
|
|
break;
|
|
default:
|
|
eicon_log(card, 1,
|
|
"eicon: Invalid BUS type %d\n",
|
|
card->bus);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
eicon_freecard(eicon_card *card) {
|
|
int i;
|
|
|
|
for(i = 0; i < (card->nchannels + 1); i++) {
|
|
skb_queue_purge(&card->bch[i].e.X);
|
|
skb_queue_purge(&card->bch[i].e.R);
|
|
}
|
|
skb_queue_purge(&card->sndq);
|
|
skb_queue_purge(&card->rcvq);
|
|
skb_queue_purge(&card->rackq);
|
|
skb_queue_purge(&card->sackq);
|
|
skb_queue_purge(&card->statq);
|
|
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
kfree(card->sbufp);
|
|
kfree(card->sbuf);
|
|
kfree(card->dbuf);
|
|
#endif
|
|
kfree(card->bch);
|
|
kfree(card);
|
|
}
|
|
|
|
int
|
|
eicon_addcard(int Type, int membase, int irq, char *id, int card_id)
|
|
{
|
|
eicon_card *p;
|
|
eicon_card *q = NULL;
|
|
int registered;
|
|
int added = 0;
|
|
int failed = 0;
|
|
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
if (!Type) /* ISA */
|
|
if ((Type = eicon_isa_find_card(membase, irq, id)) < 0)
|
|
return 0;
|
|
#endif
|
|
eicon_alloccard(Type, membase, irq, id, card_id);
|
|
p = cards;
|
|
while (p) {
|
|
registered = 0;
|
|
if (!p->interface.statcallb) {
|
|
/* Not yet registered.
|
|
* Try to register and activate it.
|
|
*/
|
|
added++;
|
|
switch (p->bus) {
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
case EICON_BUS_ISA:
|
|
case EICON_BUS_MCA:
|
|
if (eicon_registercard(p))
|
|
break;
|
|
registered = 1;
|
|
break;
|
|
#endif
|
|
case EICON_BUS_PCI:
|
|
#ifdef CONFIG_PCI
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
if (eicon_registercard(p))
|
|
break;
|
|
registered = 1;
|
|
break;
|
|
#endif
|
|
#endif
|
|
default:
|
|
printk(KERN_ERR
|
|
"eicon: addcard: Invalid BUS type %d\n",
|
|
p->bus);
|
|
}
|
|
} else
|
|
/* Card already registered */
|
|
registered = 1;
|
|
if (registered) {
|
|
/* Init OK, next card ... */
|
|
q = p;
|
|
p = p->next;
|
|
} else {
|
|
/* registering failed, remove card from list, free memory */
|
|
printk(KERN_ERR
|
|
"eicon: Initialization of %s failed\n",
|
|
p->interface.id);
|
|
if (q) {
|
|
q->next = p->next;
|
|
eicon_freecard(p);
|
|
p = q->next;
|
|
} else {
|
|
cards = p->next;
|
|
eicon_freecard(p);
|
|
p = cards;
|
|
}
|
|
failed++;
|
|
}
|
|
}
|
|
return (added - failed);
|
|
}
|
|
|
|
|
|
static int __init
|
|
eicon_init(void)
|
|
{
|
|
int card_count = 0;
|
|
char tmprev[50];
|
|
|
|
DebugVar = 1;
|
|
eicon_lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
|
|
|
|
printk(KERN_INFO "%s Rev: ", DRIVERNAME);
|
|
strcpy(tmprev, eicon_revision);
|
|
printk("%s/", eicon_getrev(tmprev));
|
|
strcpy(tmprev, eicon_pci_revision);
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
printk("%s/", eicon_getrev(tmprev));
|
|
#else
|
|
printk("---/");
|
|
#endif
|
|
strcpy(tmprev, eicon_isa_revision);
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
printk("%s/", eicon_getrev(tmprev));
|
|
#else
|
|
printk("---/");
|
|
#endif
|
|
strcpy(tmprev, eicon_idi_revision);
|
|
printk("%s\n", eicon_getrev(tmprev));
|
|
printk(KERN_INFO "%s Release: %s%s\n", DRIVERNAME,
|
|
DRIVERRELEASE, DRIVERPATCH);
|
|
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
#ifdef CONFIG_MCA
|
|
/* Check if we have MCA-bus */
|
|
if (!MCA_bus)
|
|
{
|
|
printk(KERN_INFO
|
|
"eicon: No MCA bus, ISDN-interfaces not probed.\n");
|
|
} else {
|
|
eicon_log(NULL, 8,
|
|
"eicon_mca_find_card, irq=%d.\n",
|
|
irq);
|
|
if (!eicon_mca_find_card(0, membase, irq, id))
|
|
card_count++;
|
|
};
|
|
#else
|
|
card_count = eicon_addcard(0, membase, irq, id, 0);
|
|
#endif /* CONFIG_MCA */
|
|
#endif /* CONFIG_ISDN_DRV_EICON_ISA */
|
|
|
|
#ifdef CONFIG_PCI
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
DivasCardsDiscover();
|
|
card_count += eicon_pci_find_card(id);
|
|
#endif
|
|
#endif
|
|
|
|
if (!cards) {
|
|
#ifdef MODULE
|
|
#ifndef CONFIG_ISDN_DRV_EICON_PCI
|
|
#ifndef CONFIG_ISDN_DRV_EICON_ISA
|
|
printk(KERN_INFO "Eicon: Driver is neither ISA nor PCI compiled !\n");
|
|
printk(KERN_INFO "Eicon: Driver not loaded !\n");
|
|
#else
|
|
printk(KERN_INFO "Eicon: No cards defined, driver not loaded !\n");
|
|
#endif
|
|
#else
|
|
printk(KERN_INFO "Eicon: No PCI-cards found, driver not loaded !\n");
|
|
#endif
|
|
#endif /* MODULE */
|
|
return -ENODEV;
|
|
|
|
} else
|
|
printk(KERN_INFO "Eicon: %d card%s added\n", card_count,
|
|
(card_count>1)?"s":"");
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
void DIVA_DIDD_Write(DESCRIPTOR *, int);
|
|
EXPORT_SYMBOL_NOVERS(DIVA_DIDD_Read);
|
|
EXPORT_SYMBOL_NOVERS(DIVA_DIDD_Write);
|
|
EXPORT_SYMBOL_NOVERS(DivasPrintf);
|
|
#else
|
|
int DivasCardNext;
|
|
card_t DivasCards[1];
|
|
#endif
|
|
|
|
static void __exit
|
|
eicon_exit(void)
|
|
{
|
|
#if CONFIG_PCI
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
card_t *pCard;
|
|
word wCardIndex;
|
|
extern int Divas_major;
|
|
int iTmp = 0;
|
|
#endif
|
|
#endif
|
|
|
|
eicon_card *card = cards;
|
|
eicon_card *last;
|
|
|
|
while (card) {
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
#ifdef CONFIG_MCA
|
|
if (MCA_bus)
|
|
{
|
|
mca_mark_as_unused (card->mca_slot);
|
|
mca_set_adapter_procfn(card->mca_slot, NULL, NULL);
|
|
};
|
|
#endif /* CONFIG_MCA */
|
|
#endif
|
|
unregister_card(card);
|
|
card = card->next;
|
|
}
|
|
card = cards;
|
|
while (card) {
|
|
last = card;
|
|
card = card->next;
|
|
eicon_freecard(last);
|
|
}
|
|
|
|
#if CONFIG_PCI
|
|
#ifdef CONFIG_ISDN_DRV_EICON_PCI
|
|
pCard = DivasCards;
|
|
for (wCardIndex = 0; wCardIndex < MAX_CARDS; wCardIndex++)
|
|
{
|
|
if ((pCard->hw) && (pCard->hw->in_use))
|
|
{
|
|
(*pCard->card_reset)(pCard);
|
|
|
|
UxIsrRemove(pCard->hw, pCard);
|
|
UxCardHandleFree(pCard->hw);
|
|
|
|
if(pCard->e_tbl != NULL)
|
|
{
|
|
kfree(pCard->e_tbl);
|
|
}
|
|
|
|
if(pCard->hw->card_type == DIA_CARD_TYPE_DIVA_SERVER_B)
|
|
{
|
|
release_region(pCard->hw->io_base,0x20);
|
|
release_region(pCard->hw->reset_base,0x80);
|
|
}
|
|
|
|
// If this is a 4BRI ...
|
|
if (pCard->hw->card_type == DIA_CARD_TYPE_DIVA_SERVER_Q)
|
|
{
|
|
// Skip over the next 3 virtual adapters
|
|
wCardIndex += 3;
|
|
|
|
// But free their handles
|
|
for (iTmp = 0; iTmp < 3; iTmp++)
|
|
{
|
|
pCard++;
|
|
UxCardHandleFree(pCard->hw);
|
|
|
|
if(pCard->e_tbl != NULL)
|
|
{
|
|
kfree(pCard->e_tbl);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pCard++;
|
|
}
|
|
unregister_chrdev(Divas_major, "Divas");
|
|
#endif
|
|
#endif /* CONFIG_PCI */
|
|
printk(KERN_INFO "%s unloaded\n", DRIVERNAME);
|
|
}
|
|
|
|
#ifndef MODULE
|
|
|
|
#ifdef COMPAT_HAS_NEW_SETUP
|
|
static int __init
|
|
eicon_setup(char *line)
|
|
{
|
|
int i, argc;
|
|
int ints[5];
|
|
char *str;
|
|
|
|
str = get_options(line, 4, ints);
|
|
#else
|
|
void
|
|
eicon_setup(char *str, int *ints)
|
|
{
|
|
int i, argc;
|
|
#endif
|
|
|
|
argc = ints[0];
|
|
i = 1;
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
if (argc) {
|
|
membase = irq = -1;
|
|
if (argc) {
|
|
membase = ints[i];
|
|
i++;
|
|
argc--;
|
|
}
|
|
if (argc) {
|
|
irq = ints[i];
|
|
i++;
|
|
argc--;
|
|
}
|
|
if (strlen(str)) {
|
|
strcpy(id, str);
|
|
} else {
|
|
strcpy(id, "eicon");
|
|
}
|
|
printk(KERN_INFO "Eicon ISDN active driver setup (id=%s membase=0x%x irq=%d)\n",
|
|
id, membase, irq);
|
|
}
|
|
#else
|
|
printk(KERN_INFO "Eicon ISDN active driver setup\n");
|
|
#endif
|
|
#ifdef COMPAT_HAS_NEW_SETUP
|
|
return(1);
|
|
}
|
|
__setup("eicon=", eicon_setup);
|
|
#else
|
|
}
|
|
#endif
|
|
|
|
#endif /* MODULE */
|
|
|
|
#ifdef CONFIG_ISDN_DRV_EICON_ISA
|
|
#ifdef CONFIG_MCA
|
|
|
|
struct eicon_mca_adapters_struct {
|
|
char * name;
|
|
int adf_id;
|
|
};
|
|
/* possible MCA-brands of eicon cards */
|
|
struct eicon_mca_adapters_struct eicon_mca_adapters[] = {
|
|
{ "ISDN-P/2 Adapter", 0x6abb },
|
|
{ "ISDN-[S|SX|SCOM]/2 Adapter", 0x6a93 },
|
|
{ "DIVA /MCA", 0x6336 },
|
|
{ NULL, 0 },
|
|
};
|
|
|
|
int eicon_mca_find_card(int type, /* type-idx of eicon-card */
|
|
int membase,
|
|
int irq,
|
|
char * id) /* name of eicon-isdn-dev */
|
|
{
|
|
int j, curr_slot = 0;
|
|
|
|
eicon_log(NULL, 8,
|
|
"eicon_mca_find_card type: %d, membase: %#x, irq %d \n",
|
|
type, membase, irq);
|
|
/* find a no-driver-assigned eicon card */
|
|
for (j=0; eicon_mca_adapters[j].adf_id != 0; j++)
|
|
{
|
|
for ( curr_slot=0; curr_slot<=MCA_MAX_SLOT_NR; curr_slot++)
|
|
{
|
|
curr_slot = mca_find_unused_adapter(
|
|
eicon_mca_adapters[j].adf_id, curr_slot);
|
|
if (curr_slot != MCA_NOTFOUND)
|
|
{
|
|
/* check if pre-set parameters match
|
|
these of the card, check cards memory */
|
|
if (!(int) eicon_mca_probe(curr_slot,
|
|
j,
|
|
membase,
|
|
irq,
|
|
id))
|
|
{
|
|
return 0;
|
|
/* means: adapter parms did match */
|
|
};
|
|
};
|
|
break;
|
|
/* MCA_NOTFOUND-branch: no matching adapter of
|
|
THIS flavor found, next flavor */
|
|
|
|
};
|
|
};
|
|
/* all adapter flavors checked without match, finito with: */
|
|
return ENODEV;
|
|
};
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
* stolen from 3c523.c/elmc_getinfo, ewe, 10.5.1999
|
|
*/
|
|
int eicon_info(char * buf, int slot, void *d)
|
|
{
|
|
int len = 0;
|
|
struct eicon_card *dev;
|
|
|
|
dev = (struct eicon_card *) d;
|
|
|
|
if (dev == NULL)
|
|
return len;
|
|
len += sprintf(buf+len, "eicon ISDN adapter, type %d.\n",dev->type);
|
|
len += sprintf(buf+len, "IRQ: %d\n", dev->hwif.isa.irq);
|
|
len += sprintf(buf+len, "MEMBASE: %#lx\n", (unsigned long)dev->hwif.isa.shmem);
|
|
|
|
return len;
|
|
};
|
|
|
|
int eicon_mca_probe(int slot, /* slot-nr where the card was detected */
|
|
int a_idx, /* idx-nr of probed card in eicon_mca_adapters */
|
|
int membase,
|
|
int irq,
|
|
char * id) /* name of eicon-isdn-dev */
|
|
{
|
|
unsigned char adf_pos0;
|
|
int cards_irq, cards_membase, cards_io;
|
|
int type = EICON_CTYPE_S;
|
|
int irq_array[]={0,3,4,2};
|
|
int irq_array1[]={3,4,0,0,2,10,11,12};
|
|
|
|
adf_pos0 = mca_read_stored_pos(slot,2);
|
|
eicon_log(NULL, 8,
|
|
"eicon_mca_probe irq=%d, membase=%d\n",
|
|
irq,
|
|
membase);
|
|
switch (a_idx) {
|
|
case 0: /* P/2-Adapter (== PRI/S2M ? ) */
|
|
cards_membase= 0xC0000+((adf_pos0>>4)*0x4000);
|
|
if (membase == -1) {
|
|
membase = cards_membase;
|
|
} else {
|
|
if (membase != cards_membase)
|
|
return ENODEV;
|
|
};
|
|
cards_irq=irq_array[((adf_pos0 & 0xC)>>2)];
|
|
if (irq == -1) {
|
|
irq = cards_irq;
|
|
} else {
|
|
if (irq != cards_irq)
|
|
return ENODEV;
|
|
};
|
|
cards_io= 0xC00 + ((adf_pos0>>4)*0x10);
|
|
type = EICON_CTYPE_ISAPRI;
|
|
break;
|
|
|
|
case 1: /* [S|SX|SCOM]/2 */
|
|
cards_membase= 0xC0000+((adf_pos0>>4)*0x2000);
|
|
if (membase == -1) {
|
|
membase = cards_membase;
|
|
} else {
|
|
if (membase != cards_membase)
|
|
return ENODEV;
|
|
};
|
|
cards_irq=irq_array[((adf_pos0 & 0xC)>>2)];
|
|
if (irq == -1) {
|
|
irq = cards_irq;
|
|
} else {
|
|
if (irq != cards_irq)
|
|
return ENODEV;
|
|
};
|
|
|
|
cards_io= 0xC00 + ((adf_pos0>>4)*0x10);
|
|
type = EICON_CTYPE_SCOM;
|
|
break;
|
|
|
|
case 2: /* DIVA/MCA */
|
|
cards_io = 0x200+ ((adf_pos0>>4)* 0x20);
|
|
cards_irq = irq_array1[(adf_pos0 & 0x7)];
|
|
if (irq == -1) {
|
|
irq = cards_irq;
|
|
} else {
|
|
if (irq != cards_irq)
|
|
return ENODEV;
|
|
};
|
|
type = 0;
|
|
break;
|
|
default:
|
|
return ENODEV;
|
|
};
|
|
/* matching membase & irq */
|
|
if ( 1 == eicon_addcard(type, membase, irq, id, 0)) {
|
|
mca_set_adapter_name(slot, eicon_mca_adapters[a_idx].name);
|
|
mca_set_adapter_procfn(slot, (MCA_ProcFn) eicon_info, cards);
|
|
|
|
mca_mark_as_used(slot);
|
|
cards->mca_slot = slot;
|
|
/* card->io noch setzen oder ?? */
|
|
cards->mca_io = cards_io;
|
|
cards->hwif.isa.io = cards_io;
|
|
/* reset card */
|
|
outb_p(0,cards_io+1);
|
|
|
|
eicon_log(NULL, 8, "eicon_addcard: successful for slot # %d.\n",
|
|
cards->mca_slot+1);
|
|
return 0 ; /* eicon_addcard added a card */
|
|
} else {
|
|
return ENODEV;
|
|
};
|
|
};
|
|
#endif /* CONFIG_MCA */
|
|
#endif /* CONFIG_ISDN_DRV_EICON_ISA */
|
|
|
|
module_init(eicon_init);
|
|
module_exit(eicon_exit);
|