diff options
Diffstat (limited to 'drivers/isdn/hardware/avm')
-rw-r--r-- | drivers/isdn/hardware/avm/Kconfig | 66 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/Makefile | 11 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/avm_cs.c | 510 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/avmcard.h | 585 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/b1.c | 814 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/b1dma.c | 980 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/b1isa.c | 245 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/b1pci.c | 417 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/b1pcmcia.c | 224 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/c4.c | 1310 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/t1isa.c | 596 | ||||
-rw-r--r-- | drivers/isdn/hardware/avm/t1pci.c | 260 |
12 files changed, 6018 insertions, 0 deletions
diff --git a/drivers/isdn/hardware/avm/Kconfig b/drivers/isdn/hardware/avm/Kconfig new file mode 100644 index 0000000..29a32a8 --- /dev/null +++ b/drivers/isdn/hardware/avm/Kconfig @@ -0,0 +1,66 @@ +# +# ISDN AVM drivers +# + +menu "Active AVM cards" + depends on NET && ISDN && ISDN_CAPI!=n + +config CAPI_AVM + bool "Support AVM cards" + help + Enable support for AVM active ISDN cards. + +config ISDN_DRV_AVMB1_B1ISA + tristate "AVM B1 ISA support" + depends on CAPI_AVM && ISDN_CAPI && ISA + help + Enable support for the ISA version of the AVM B1 card. + +config ISDN_DRV_AVMB1_B1PCI + tristate "AVM B1 PCI support" + depends on CAPI_AVM && ISDN_CAPI && PCI + help + Enable support for the PCI version of the AVM B1 card. + +config ISDN_DRV_AVMB1_B1PCIV4 + bool "AVM B1 PCI V4 support" + depends on ISDN_DRV_AVMB1_B1PCI + help + Enable support for the V4 version of AVM B1 PCI card. + +config ISDN_DRV_AVMB1_T1ISA + tristate "AVM T1/T1-B ISA support" + depends on CAPI_AVM && ISDN_CAPI && ISA + help + Enable support for the AVM T1 T1B card. + Note: This is a PRI card and handle 30 B-channels. + +config ISDN_DRV_AVMB1_B1PCMCIA + tristate "AVM B1/M1/M2 PCMCIA support" + depends on CAPI_AVM && ISDN_CAPI + help + Enable support for the PCMCIA version of the AVM B1 card. + +config ISDN_DRV_AVMB1_AVM_CS + tristate "AVM B1/M1/M2 PCMCIA cs module" + depends on ISDN_DRV_AVMB1_B1PCMCIA && PCMCIA + help + Enable the PCMCIA client driver for the AVM B1/M1/M2 + PCMCIA cards. + +config ISDN_DRV_AVMB1_T1PCI + tristate "AVM T1/T1-B PCI support" + depends on CAPI_AVM && ISDN_CAPI && PCI + help + Enable support for the AVM T1 T1B card. + Note: This is a PRI card and handle 30 B-channels. + +config ISDN_DRV_AVMB1_C4 + tristate "AVM C4/C2 support" + depends on CAPI_AVM && ISDN_CAPI && PCI + help + Enable support for the AVM C4/C2 PCI cards. + These cards handle 4/2 BRI ISDN lines (8/4 channels). + +endmenu + diff --git a/drivers/isdn/hardware/avm/Makefile b/drivers/isdn/hardware/avm/Makefile new file mode 100644 index 0000000..b540e8f --- /dev/null +++ b/drivers/isdn/hardware/avm/Makefile @@ -0,0 +1,11 @@ +# Makefile for the AVM ISDN device drivers + +# Each configuration option enables a list of files. + +obj-$(CONFIG_ISDN_DRV_AVMB1_B1ISA) += b1isa.o b1.o +obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCI) += b1pci.o b1.o b1dma.o +obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCMCIA) += b1pcmcia.o b1.o +obj-$(CONFIG_ISDN_DRV_AVMB1_AVM_CS) += avm_cs.o +obj-$(CONFIG_ISDN_DRV_AVMB1_T1ISA) += t1isa.o b1.o +obj-$(CONFIG_ISDN_DRV_AVMB1_T1PCI) += t1pci.o b1.o b1dma.o +obj-$(CONFIG_ISDN_DRV_AVMB1_C4) += c4.o b1.o diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c new file mode 100644 index 0000000..dc00c85 --- /dev/null +++ b/drivers/isdn/hardware/avm/avm_cs.c @@ -0,0 +1,510 @@ +/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $ + * + * A PCMCIA client driver for AVM B1/M1/M2 + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/tty.h> +#include <linux/serial.h> +#include <linux/major.h> +#include <asm/io.h> +#include <asm/system.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ciscode.h> +#include <pcmcia/ds.h> +#include <pcmcia/cisreg.h> + +#include <linux/skbuff.h> +#include <linux/capi.h> +#include <linux/b1lli.h> +#include <linux/b1pcmcia.h> + +/*====================================================================*/ + +MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/*====================================================================*/ + +/* + The event() function is this driver's Card Services event handler. + It will be called by Card Services when an appropriate card status + event is received. The config() and release() entry points are + used to configure or release a socket, in response to card insertion + and ejection events. They are invoked from the skeleton event + handler. +*/ + +static void avmcs_config(dev_link_t *link); +static void avmcs_release(dev_link_t *link); +static int avmcs_event(event_t event, int priority, + event_callback_args_t *args); + +/* + The attach() and detach() entry points are used to create and destroy + "instances" of the driver, where each instance represents everything + needed to manage one actual PCMCIA card. +*/ + +static dev_link_t *avmcs_attach(void); +static void avmcs_detach(dev_link_t *); + +/* + The dev_info variable is the "key" that is used to match up this + device driver with appropriate cards, through the card configuration + database. +*/ + +static dev_info_t dev_info = "avm_cs"; + +/* + A linked list of "instances" of the skeleton device. Each actual + PCMCIA card corresponds to one device instance, and is described + by one dev_link_t structure (defined in ds.h). + + You may not want to use a linked list for this -- for example, the + memory card driver uses an array of dev_link_t pointers, where minor + device numbers are used to derive the corresponding array index. +*/ + +static dev_link_t *dev_list = NULL; + +/* + A dev_link_t structure has fields for most things that are needed + to keep track of a socket, but there will usually be some device + specific information that also needs to be kept track of. The + 'priv' pointer in a dev_link_t structure can be used to point to + a device-specific private data structure, like this. + + A driver needs to provide a dev_node_t structure for each device + on a card. In some cases, there is only one device per card (for + example, ethernet cards, modems). In other cases, there may be + many actual or logical devices (SCSI adapters, memory cards with + multiple partitions). The dev_node_t structures need to be kept + in a linked list starting at the 'dev' field of a dev_link_t + structure. We allocate them in the card's private data structure, + because they generally can't be allocated dynamically. +*/ + +typedef struct local_info_t { + dev_node_t node; +} local_info_t; + +/*====================================================================== + + avmcs_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + + The dev_link structure is initialized, but we don't actually + configure the card at this point -- we wait until we receive a + card insertion event. + +======================================================================*/ + +static dev_link_t *avmcs_attach(void) +{ + client_reg_t client_reg; + dev_link_t *link; + local_info_t *local; + int ret; + + /* Initialize the dev_link_t structure */ + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + if (!link) + goto err; + memset(link, 0, sizeof(struct dev_link_t)); + + /* The io structure describes IO port mapping */ + link->io.NumPorts1 = 16; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.NumPorts2 = 0; + + /* Interrupt setup */ + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; + + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + + /* General socket configuration */ + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + /* Allocate space for private device-specific data */ + local = kmalloc(sizeof(local_info_t), GFP_KERNEL); + if (!local) + goto err_kfree; + memset(local, 0, sizeof(local_info_t)); + link->priv = local; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &avmcs_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = pcmcia_register_client(&link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + avmcs_detach(link); + goto err; + } + return link; + + err_kfree: + kfree(link); + err: + return NULL; +} /* avmcs_attach */ + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void avmcs_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + /* + If the device is currently configured and active, we won't + actually delete it yet. Instead, it is marked so that when + the release() function is called, that will trigger a proper + detach(). + */ + if (link->state & DEV_CONFIG) { + link->state |= DEV_STALE_LINK; + return; + } + + /* Break the link with Card Services */ + if (link->handle) + pcmcia_deregister_client(link->handle); + + /* Unlink device structure, free pieces */ + *linkp = link->next; + if (link->priv) { + kfree(link->priv); + } + kfree(link); + +} /* avmcs_detach */ + +/*====================================================================== + + avmcs_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ethernet device available to the system. + +======================================================================*/ + +static int get_tuple(client_handle_t handle, tuple_t *tuple, + cisparse_t *parse) +{ + int i = pcmcia_get_tuple_data(handle, tuple); + if (i != CS_SUCCESS) return i; + return pcmcia_parse_tuple(handle, tuple, parse); +} + +static int first_tuple(client_handle_t handle, tuple_t *tuple, + cisparse_t *parse) +{ + int i = pcmcia_get_first_tuple(handle, tuple); + if (i != CS_SUCCESS) return i; + return get_tuple(handle, tuple, parse); +} + +static int next_tuple(client_handle_t handle, tuple_t *tuple, + cisparse_t *parse) +{ + int i = pcmcia_get_next_tuple(handle, tuple); + if (i != CS_SUCCESS) return i; + return get_tuple(handle, tuple, parse); +} + +static void avmcs_config(dev_link_t *link) +{ + client_handle_t handle; + tuple_t tuple; + cisparse_t parse; + cistpl_cftable_entry_t *cf = &parse.cftable_entry; + local_info_t *dev; + int i; + u_char buf[64]; + char devname[128]; + int cardtype; + int (*addcard)(unsigned int port, unsigned irq); + + handle = link->handle; + dev = link->priv; + + /* + This reads the card's CONFIG tuple to find its configuration + registers. + */ + do { + tuple.DesiredTuple = CISTPL_CONFIG; + i = pcmcia_get_first_tuple(handle, &tuple); + if (i != CS_SUCCESS) break; + tuple.TupleData = buf; + tuple.TupleDataMax = 64; + tuple.TupleOffset = 0; + i = pcmcia_get_tuple_data(handle, &tuple); + if (i != CS_SUCCESS) break; + i = pcmcia_parse_tuple(handle, &tuple, &parse); + if (i != CS_SUCCESS) break; + link->conf.ConfigBase = parse.config.base; + } while (0); + if (i != CS_SUCCESS) { + cs_error(link->handle, ParseTuple, i); + link->state &= ~DEV_CONFIG_PENDING; + return; + } + + /* Configure card */ + link->state |= DEV_CONFIG; + + do { + + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = 254; + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_VERS_1; + + devname[0] = 0; + if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) { + strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1], + sizeof(devname)); + } + /* + * find IO port + */ + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleOffset = 0; tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + i = first_tuple(handle, &tuple, &parse); + while (i == CS_SUCCESS) { + if (cf->io.nwin > 0) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.NumPorts1 = cf->io.win[0].len; + link->io.NumPorts2 = 0; + printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n", + link->io.BasePort1, + link->io.BasePort1+link->io.NumPorts1-1); + i = pcmcia_request_io(link->handle, &link->io); + if (i == CS_SUCCESS) goto found_port; + } + i = next_tuple(handle, &tuple, &parse); + } + +found_port: + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIO, i); + break; + } + + /* + * allocate an interrupt line + */ + i = pcmcia_request_irq(link->handle, &link->irq); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestIRQ, i); + pcmcia_release_io(link->handle, &link->io); + break; + } + + /* + * configure the PCMCIA socket + */ + i = pcmcia_request_configuration(link->handle, &link->conf); + if (i != CS_SUCCESS) { + cs_error(link->handle, RequestConfiguration, i); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); + break; + } + + } while (0); + + /* At this point, the dev_node_t structure(s) should be + initialized and arranged in a linked list at link->dev. */ + + if (devname[0]) { + char *s = strrchr(devname, ' '); + if (!s) + s = devname; + else s++; + strcpy(dev->node.dev_name, s); + if (strcmp("M1", s) == 0) { + cardtype = AVM_CARDTYPE_M1; + } else if (strcmp("M2", s) == 0) { + cardtype = AVM_CARDTYPE_M2; + } else { + cardtype = AVM_CARDTYPE_B1; + } + } else { + strcpy(dev->node.dev_name, "b1"); + cardtype = AVM_CARDTYPE_B1; + } + + dev->node.major = 64; + dev->node.minor = 0; + link->dev = &dev->node; + + link->state &= ~DEV_CONFIG_PENDING; + /* If any step failed, release any partially configured state */ + if (i != 0) { + avmcs_release(link); + return; + } + + + switch (cardtype) { + case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break; + case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break; + default: + case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break; + } + if ((i = (*addcard)(link->io.BasePort1, link->irq.AssignedIRQ)) < 0) { + printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n", + dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ); + avmcs_release(link); + return; + } + dev->node.minor = i; + +} /* avmcs_config */ + +/*====================================================================== + + After a card is removed, avmcs_release() will unregister the net + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. + +======================================================================*/ + +static void avmcs_release(dev_link_t *link) +{ + b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ); + + /* Unlink the device chain */ + link->dev = NULL; + + /* Don't bother checking to see if these succeed or not */ + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); + link->state &= ~DEV_CONFIG; + + if (link->state & DEV_STALE_LINK) + avmcs_detach(link); + +} /* avmcs_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the net drivers from trying + to talk to the card any more. + + When a CARD_REMOVAL event is received, we immediately set a flag + to block future accesses to this device. All the functions that + actually access the device should check this flag to make sure + the card is still present. + +======================================================================*/ + +static int avmcs_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) + avmcs_release(link); + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + avmcs_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + pcmcia_release_configuration(link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) + pcmcia_request_configuration(link->handle, &link->conf); + break; + } + return 0; +} /* avmcs_event */ + +static struct pcmcia_driver avmcs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "avm_cs", + }, + .attach = avmcs_attach, + .detach = avmcs_detach, +}; + +static int __init avmcs_init(void) +{ + return pcmcia_register_driver(&avmcs_driver); +} + +static void __exit avmcs_exit(void) +{ + pcmcia_unregister_driver(&avmcs_driver); + BUG_ON(dev_list != NULL); +} + +module_init(avmcs_init); +module_exit(avmcs_exit); diff --git a/drivers/isdn/hardware/avm/avmcard.h b/drivers/isdn/hardware/avm/avmcard.h new file mode 100644 index 0000000..296d6a6 --- /dev/null +++ b/drivers/isdn/hardware/avm/avmcard.h @@ -0,0 +1,585 @@ +/* $Id: avmcard.h,v 1.1.4.1.2.1 2001/12/21 15:00:17 kai Exp $ + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#ifndef _AVMCARD_H_ +#define _AVMCARD_H_ + +#include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/interrupt.h> + +#define AVMB1_PORTLEN 0x1f +#define AVM_MAXVERSION 8 +#define AVM_NCCI_PER_CHANNEL 4 + +/* + * Versions + */ + +#define VER_DRIVER 0 +#define VER_CARDTYPE 1 +#define VER_HWID 2 +#define VER_SERIAL 3 +#define VER_OPTION 4 +#define VER_PROTO 5 +#define VER_PROFILE 6 +#define VER_CAPI 7 + +enum avmcardtype { + avm_b1isa, + avm_b1pci, + avm_b1pcmcia, + avm_m1, + avm_m2, + avm_t1isa, + avm_t1pci, + avm_c4, + avm_c2 +}; + +typedef struct avmcard_dmabuf { + long size; + u8 *dmabuf; + dma_addr_t dmaaddr; +} avmcard_dmabuf; + +typedef struct avmcard_dmainfo { + u32 recvlen; + avmcard_dmabuf recvbuf; + + avmcard_dmabuf sendbuf; + struct sk_buff_head send_queue; + + struct pci_dev *pcidev; +} avmcard_dmainfo; + +typedef struct avmctrl_info { + char cardname[32]; + + int versionlen; + char versionbuf[1024]; + char *version[AVM_MAXVERSION]; + + char infobuf[128]; /* for function procinfo */ + + struct avmcard *card; + struct capi_ctr capi_ctrl; + + struct list_head ncci_head; +} avmctrl_info; + +typedef struct avmcard { + char name[32]; + + spinlock_t lock; + unsigned int port; + unsigned irq; + unsigned long membase; + enum avmcardtype cardtype; + unsigned char revision; + unsigned char class; + int cardnr; /* for t1isa */ + + char msgbuf[128]; /* capimsg msg part */ + char databuf[2048]; /* capimsg data part */ + + void __iomem *mbase; + volatile u32 csr; + avmcard_dmainfo *dma; + + struct avmctrl_info *ctrlinfo; + + u_int nr_controllers; + u_int nlogcontr; + struct list_head list; +} avmcard; + +extern int b1_irq_table[16]; + +/* + * LLI Messages to the ISDN-ControllerISDN Controller + */ + +#define SEND_POLL 0x72 /* + * after load <- RECEIVE_POLL + */ +#define SEND_INIT 0x11 /* + * first message <- RECEIVE_INIT + * int32 NumApplications int32 + * NumNCCIs int32 BoardNumber + */ +#define SEND_REGISTER 0x12 /* + * register an application int32 + * ApplIDId int32 NumMessages + * int32 NumB3Connections int32 + * NumB3Blocks int32 B3Size + * + * AnzB3Connection != 0 && + * AnzB3Blocks >= 1 && B3Size >= 1 + */ +#define SEND_RELEASE 0x14 /* + * deregister an application int32 + * ApplID + */ +#define SEND_MESSAGE 0x15 /* + * send capi-message int32 length + * capi-data ... + */ +#define SEND_DATA_B3_REQ 0x13 /* + * send capi-data-message int32 + * MsgLength capi-data ... int32 + * B3Length data .... + */ + +#define SEND_CONFIG 0x21 /* + */ + +#define SEND_POLLACK 0x73 /* T1 Watchdog */ + +/* + * LLI Messages from the ISDN-ControllerISDN Controller + */ + +#define RECEIVE_POLL 0x32 /* + * <- after SEND_POLL + */ +#define RECEIVE_INIT 0x27 /* + * <- after SEND_INIT int32 length + * byte total length b1struct board + * driver revision b1struct card + * type b1struct reserved b1struct + * serial number b1struct driver + * capability b1struct d-channel + * protocol b1struct CAPI-2.0 + * profile b1struct capi version + */ +#define RECEIVE_MESSAGE 0x21 /* + * <- after SEND_MESSAGE int32 + * AppllID int32 Length capi-data + * .... + */ +#define RECEIVE_DATA_B3_IND 0x22 /* + * received data int32 AppllID + * int32 Length capi-data ... + * int32 B3Length data ... + */ +#define RECEIVE_START 0x23 /* + * Handshake + */ +#define RECEIVE_STOP 0x24 /* + * Handshake + */ +#define RECEIVE_NEW_NCCI 0x25 /* + * int32 AppllID int32 NCCI int32 + * WindowSize + */ +#define RECEIVE_FREE_NCCI 0x26 /* + * int32 AppllID int32 NCCI + */ +#define RECEIVE_RELEASE 0x26 /* + * int32 AppllID int32 0xffffffff + */ +#define RECEIVE_TASK_READY 0x31 /* + * int32 tasknr + * int32 Length Taskname ... + */ +#define RECEIVE_DEBUGMSG 0x71 /* + * int32 Length message + * + */ +#define RECEIVE_POLLDWORD 0x75 /* t1pci in dword mode */ + +#define WRITE_REGISTER 0x00 +#define READ_REGISTER 0x01 + +/* + * port offsets + */ + +#define B1_READ 0x00 +#define B1_WRITE 0x01 +#define B1_INSTAT 0x02 +#define B1_OUTSTAT 0x03 +#define B1_ANALYSE 0x04 +#define B1_REVISION 0x05 +#define B1_RESET 0x10 + + +#define B1_STAT0(cardtype) ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l) +#define B1_STAT1(cardtype) (0x80E00000l) + +/* ---------------------------------------------------------------- */ + +static inline unsigned char b1outp(unsigned int base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); + return inb(base + B1_ANALYSE); +} + + +static inline int b1_rx_full(unsigned int base) +{ + return inb(base + B1_INSTAT) & 0x1; +} + +static inline unsigned char b1_get_byte(unsigned int base) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + while (!b1_rx_full(base) && time_before(jiffies, stop)); + if (b1_rx_full(base)) + return inb(base + B1_READ); + printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base); + return 0; +} + +static inline unsigned int b1_get_word(unsigned int base) +{ + unsigned int val = 0; + val |= b1_get_byte(base); + val |= (b1_get_byte(base) << 8); + val |= (b1_get_byte(base) << 16); + val |= (b1_get_byte(base) << 24); + return val; +} + +static inline int b1_tx_empty(unsigned int base) +{ + return inb(base + B1_OUTSTAT) & 0x1; +} + +static inline void b1_put_byte(unsigned int base, unsigned char val) +{ + while (!b1_tx_empty(base)); + b1outp(base, B1_WRITE, val); +} + +static inline int b1_save_put_byte(unsigned int base, unsigned char val) +{ + unsigned long stop = jiffies + 2 * HZ; + while (!b1_tx_empty(base) && time_before(jiffies,stop)); + if (!b1_tx_empty(base)) return -1; + b1outp(base, B1_WRITE, val); + return 0; +} + +static inline void b1_put_word(unsigned int base, unsigned int val) +{ + b1_put_byte(base, val & 0xff); + b1_put_byte(base, (val >> 8) & 0xff); + b1_put_byte(base, (val >> 16) & 0xff); + b1_put_byte(base, (val >> 24) & 0xff); +} + +static inline unsigned int b1_get_slice(unsigned int base, + unsigned char *dp) +{ + unsigned int len, i; + + len = i = b1_get_word(base); + while (i-- > 0) *dp++ = b1_get_byte(base); + return len; +} + +static inline void b1_put_slice(unsigned int base, + unsigned char *dp, unsigned int len) +{ + unsigned i = len; + b1_put_word(base, i); + while (i-- > 0) + b1_put_byte(base, *dp++); +} + +static void b1_wr_reg(unsigned int base, + unsigned int reg, + unsigned int value) +{ + b1_put_byte(base, WRITE_REGISTER); + b1_put_word(base, reg); + b1_put_word(base, value); +} + +static inline unsigned int b1_rd_reg(unsigned int base, + unsigned int reg) +{ + b1_put_byte(base, READ_REGISTER); + b1_put_word(base, reg); + return b1_get_word(base); + +} + +static inline void b1_reset(unsigned int base) +{ + b1outp(base, B1_RESET, 0); + mdelay(55 * 2); /* 2 TIC's */ + + b1outp(base, B1_RESET, 1); + mdelay(55 * 2); /* 2 TIC's */ + + b1outp(base, B1_RESET, 0); + mdelay(55 * 2); /* 2 TIC's */ +} + +static inline unsigned char b1_disable_irq(unsigned int base) +{ + return b1outp(base, B1_INSTAT, 0x00); +} + +/* ---------------------------------------------------------------- */ + +static inline void b1_set_test_bit(unsigned int base, + enum avmcardtype cardtype, + int onoff) +{ + b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20); +} + +static inline int b1_get_test_bit(unsigned int base, + enum avmcardtype cardtype) +{ + return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0; +} + +/* ---------------------------------------------------------------- */ + +#define T1_FASTLINK 0x00 +#define T1_SLOWLINK 0x08 + +#define T1_READ B1_READ +#define T1_WRITE B1_WRITE +#define T1_INSTAT B1_INSTAT +#define T1_OUTSTAT B1_OUTSTAT +#define T1_IRQENABLE 0x05 +#define T1_FIFOSTAT 0x06 +#define T1_RESETLINK 0x10 +#define T1_ANALYSE 0x11 +#define T1_IRQMASTER 0x12 +#define T1_IDENT 0x17 +#define T1_RESETBOARD 0x1f + +#define T1F_IREADY 0x01 +#define T1F_IHALF 0x02 +#define T1F_IFULL 0x04 +#define T1F_IEMPTY 0x08 +#define T1F_IFLAGS 0xF0 + +#define T1F_OREADY 0x10 +#define T1F_OHALF 0x20 +#define T1F_OEMPTY 0x40 +#define T1F_OFULL 0x80 +#define T1F_OFLAGS 0xF0 + +/* there are HEMA cards with 1k and 4k FIFO out */ +#define FIFO_OUTBSIZE 256 +#define FIFO_INPBSIZE 512 + +#define HEMA_VERSION_ID 0 +#define HEMA_PAL_ID 0 + +static inline void t1outp(unsigned int base, + unsigned short offset, + unsigned char value) +{ + outb(value, base + offset); +} + +static inline unsigned char t1inp(unsigned int base, + unsigned short offset) +{ + return inb(base + offset); +} + +static inline int t1_isfastlink(unsigned int base) +{ + return (inb(base + T1_IDENT) & ~0x82) == 1; +} + +static inline unsigned char t1_fifostatus(unsigned int base) +{ + return inb(base + T1_FIFOSTAT); +} + +static inline unsigned int t1_get_slice(unsigned int base, + unsigned char *dp) +{ + unsigned int len, i; +#ifdef FASTLINK_DEBUG + unsigned wcnt = 0, bcnt = 0; +#endif + + len = i = b1_get_word(base); + if (t1_isfastlink(base)) { + int status; + while (i > 0) { + status = t1_fifostatus(base) & (T1F_IREADY|T1F_IHALF); + if (i >= FIFO_INPBSIZE) status |= T1F_IFULL; + + switch (status) { + case T1F_IREADY|T1F_IHALF|T1F_IFULL: + insb(base+B1_READ, dp, FIFO_INPBSIZE); + dp += FIFO_INPBSIZE; + i -= FIFO_INPBSIZE; +#ifdef FASTLINK_DEBUG + wcnt += FIFO_INPBSIZE; +#endif + break; + case T1F_IREADY|T1F_IHALF: + insb(base+B1_READ,dp, i); +#ifdef FASTLINK_DEBUG + wcnt += i; +#endif + dp += i; + i = 0; + if (i == 0) + break; + /* fall through */ + default: + *dp++ = b1_get_byte(base); + i--; +#ifdef FASTLINK_DEBUG + bcnt++; +#endif + break; + } + } +#ifdef FASTLINK_DEBUG + if (wcnt) + printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n", + base, len, wcnt, bcnt); +#endif + } else { + while (i-- > 0) + *dp++ = b1_get_byte(base); + } + return len; +} + +static inline void t1_put_slice(unsigned int base, + unsigned char *dp, unsigned int len) +{ + unsigned i = len; + b1_put_word(base, i); + if (t1_isfastlink(base)) { + int status; + while (i > 0) { + status = t1_fifostatus(base) & (T1F_OREADY|T1F_OHALF); + if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY; + switch (status) { + case T1F_OREADY|T1F_OHALF|T1F_OEMPTY: + outsb(base+B1_WRITE, dp, FIFO_OUTBSIZE); + dp += FIFO_OUTBSIZE; + i -= FIFO_OUTBSIZE; + break; + case T1F_OREADY|T1F_OHALF: + outsb(base+B1_WRITE, dp, i); + dp += i; + i = 0; + break; + default: + b1_put_byte(base, *dp++); + i--; + break; + } + } + } else { + while (i-- > 0) + b1_put_byte(base, *dp++); + } +} + +static inline void t1_disable_irq(unsigned int base) +{ + t1outp(base, T1_IRQMASTER, 0x00); +} + +static inline void t1_reset(unsigned int base) +{ + /* reset T1 Controller */ + b1_reset(base); + /* disable irq on HEMA */ + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_OUTSTAT, 0x00); + t1outp(base, T1_IRQMASTER, 0x00); + /* reset HEMA board configuration */ + t1outp(base, T1_RESETBOARD, 0xf); +} + +static inline void b1_setinterrupt(unsigned int base, unsigned irq, + enum avmcardtype cardtype) +{ + switch (cardtype) { + case avm_t1isa: + t1outp(base, B1_INSTAT, 0x00); + t1outp(base, B1_INSTAT, 0x02); + t1outp(base, T1_IRQMASTER, 0x08); + break; + case avm_b1isa: + b1outp(base, B1_INSTAT, 0x00); + b1outp(base, B1_RESET, b1_irq_table[irq]); + b1outp(base, B1_INSTAT, 0x02); + break; + default: + case avm_m1: + case avm_m2: + case avm_b1pci: + b1outp(base, B1_INSTAT, 0x00); + b1outp(base, B1_RESET, 0xf0); + b1outp(base, B1_INSTAT, 0x02); + break; + case avm_c4: + case avm_t1pci: + b1outp(base, B1_RESET, 0xf0); + break; + } +} + +/* b1.c */ +avmcard *b1_alloc_card(int nr_controllers); +void b1_free_card(avmcard *card); +int b1_detect(unsigned int base, enum avmcardtype cardtype); +void b1_getrevision(avmcard *card); +int b1_load_t4file(avmcard *card, capiloaddatapart * t4file); +int b1_load_config(avmcard *card, capiloaddatapart * config); +int b1_loaded(avmcard *card); + +int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data); +void b1_reset_ctr(struct capi_ctr *ctrl); +void b1_register_appl(struct capi_ctr *ctrl, u16 appl, + capi_register_params *rp); +void b1_release_appl(struct capi_ctr *ctrl, u16 appl); +u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); +void b1_parse_version(avmctrl_info *card); +irqreturn_t b1_interrupt(int interrupt, void *devptr, struct pt_regs *regs); + +int b1ctl_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl); + +avmcard_dmainfo *avmcard_dma_alloc(char *name, struct pci_dev *, + long rsize, long ssize); +void avmcard_dma_free(avmcard_dmainfo *); + +/* b1dma.c */ +int b1pciv4_detect(avmcard *card); +int t1pci_detect(avmcard *card); +void b1dma_reset(avmcard *card); +irqreturn_t b1dma_interrupt(int interrupt, void *devptr, struct pt_regs *regs); + +int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data); +void b1dma_reset_ctr(struct capi_ctr *ctrl); +void b1dma_remove_ctr(struct capi_ctr *ctrl); +void b1dma_register_appl(struct capi_ctr *ctrl, + u16 appl, + capi_register_params *rp); +void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl); +u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); +int b1dmactl_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl); + +#endif /* _AVMCARD_H_ */ diff --git a/drivers/isdn/hardware/avm/b1.c b/drivers/isdn/hardware/avm/b1.c new file mode 100644 index 0000000..0c7061d --- /dev/null +++ b/drivers/isdn/hardware/avm/b1.c @@ -0,0 +1,814 @@ +/* $Id: b1.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ + * + * Common module for AVM B1 cards. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <linux/kernelcapi.h> +#include <asm/io.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <linux/netdevice.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> + +static char *revision = "$Revision: 1.1.2.2 $"; + +/* ------------------------------------------------------------- */ + +MODULE_DESCRIPTION("CAPI4Linux: Common support for active AVM cards"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +int b1_irq_table[16] = +{0, + 0, + 0, + 192, /* irq 3 */ + 32, /* irq 4 */ + 160, /* irq 5 */ + 96, /* irq 6 */ + 224, /* irq 7 */ + 0, + 64, /* irq 9 */ + 80, /* irq 10 */ + 208, /* irq 11 */ + 48, /* irq 12 */ + 0, + 0, + 112, /* irq 15 */ +}; + +/* ------------------------------------------------------------- */ + +avmcard *b1_alloc_card(int nr_controllers) +{ + avmcard *card; + avmctrl_info *cinfo; + int i; + + card = kmalloc(sizeof(*card), GFP_KERNEL); + if (!card) + return NULL; + + memset(card, 0, sizeof(*card)); + + cinfo = kmalloc(sizeof(*cinfo) * nr_controllers, GFP_KERNEL); + if (!cinfo) { + kfree(card); + return NULL; + } + memset(cinfo, 0, sizeof(*cinfo) * nr_controllers); + + card->ctrlinfo = cinfo; + for (i = 0; i < nr_controllers; i++) { + INIT_LIST_HEAD(&cinfo[i].ncci_head); + cinfo[i].card = card; + } + spin_lock_init(&card->lock); + card->nr_controllers = nr_controllers; + + return card; +} + +/* ------------------------------------------------------------- */ + +void b1_free_card(avmcard *card) +{ + kfree(card->ctrlinfo); + kfree(card); +} + +/* ------------------------------------------------------------- */ + +int b1_detect(unsigned int base, enum avmcardtype cardtype) +{ + int onoff, i; + + /* + * Statusregister 0000 00xx + */ + if ((inb(base + B1_INSTAT) & 0xfc) + || (inb(base + B1_OUTSTAT) & 0xfc)) + return 1; + /* + * Statusregister 0000 001x + */ + b1outp(base, B1_INSTAT, 0x2); /* enable irq */ + /* b1outp(base, B1_OUTSTAT, 0x2); */ + if ((inb(base + B1_INSTAT) & 0xfe) != 0x2 + /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */) + return 2; + /* + * Statusregister 0000 000x + */ + b1outp(base, B1_INSTAT, 0x0); /* disable irq */ + b1outp(base, B1_OUTSTAT, 0x0); + if ((inb(base + B1_INSTAT) & 0xfe) + || (inb(base + B1_OUTSTAT) & 0xfe)) + return 3; + + for (onoff = !0, i= 0; i < 10 ; i++) { + b1_set_test_bit(base, cardtype, onoff); + if (b1_get_test_bit(base, cardtype) != onoff) + return 4; + onoff = !onoff; + } + + if (cardtype == avm_m1) + return 0; + + if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01) + return 5; + + return 0; +} + +void b1_getrevision(avmcard *card) +{ + card->class = inb(card->port + B1_ANALYSE); + card->revision = inb(card->port + B1_REVISION); +} + +#define FWBUF_SIZE 256 +int b1_load_t4file(avmcard *card, capiloaddatapart * t4file) +{ + unsigned char buf[FWBUF_SIZE]; + unsigned char *dp; + int i, left; + unsigned int base = card->port; + + dp = t4file->data; + left = t4file->len; + while (left > FWBUF_SIZE) { + if (t4file->user) { + if (copy_from_user(buf, dp, FWBUF_SIZE)) + return -EFAULT; + } else { + memcpy(buf, dp, FWBUF_SIZE); + } + for (i = 0; i < FWBUF_SIZE; i++) + if (b1_save_put_byte(base, buf[i]) < 0) { + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); + return -EIO; + } + left -= FWBUF_SIZE; + dp += FWBUF_SIZE; + } + if (left) { + if (t4file->user) { + if (copy_from_user(buf, dp, left)) + return -EFAULT; + } else { + memcpy(buf, dp, left); + } + for (i = 0; i < left; i++) + if (b1_save_put_byte(base, buf[i]) < 0) { + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); + return -EIO; + } + } + return 0; +} + +int b1_load_config(avmcard *card, capiloaddatapart * config) +{ + unsigned char buf[FWBUF_SIZE]; + unsigned char *dp; + unsigned int base = card->port; + int i, j, left; + + dp = config->data; + left = config->len; + if (left) { + b1_put_byte(base, SEND_CONFIG); + b1_put_word(base, 1); + b1_put_byte(base, SEND_CONFIG); + b1_put_word(base, left); + } + while (left > FWBUF_SIZE) { + if (config->user) { + if (copy_from_user(buf, dp, FWBUF_SIZE)) + return -EFAULT; + } else { + memcpy(buf, dp, FWBUF_SIZE); + } + for (i = 0; i < FWBUF_SIZE; ) { + b1_put_byte(base, SEND_CONFIG); + for (j=0; j < 4; j++) { + b1_put_byte(base, buf[i++]); + } + } + left -= FWBUF_SIZE; + dp += FWBUF_SIZE; + } + if (left) { + if (config->user) { + if (copy_from_user(buf, dp, left)) + return -EFAULT; + } else { + memcpy(buf, dp, left); + } + for (i = 0; i < left; ) { + b1_put_byte(base, SEND_CONFIG); + for (j=0; j < 4; j++) { + if (i < left) + b1_put_byte(base, buf[i++]); + else + b1_put_byte(base, 0); + } + } + } + return 0; +} + +int b1_loaded(avmcard *card) +{ + unsigned int base = card->port; + unsigned long stop; + unsigned char ans; + unsigned long tout = 2; + + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_tx_empty(base)) + break; + } + if (!b1_tx_empty(base)) { + printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n", + card->name); + return 0; + } + b1_put_byte(base, SEND_POLL); + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_rx_full(base)) { + if ((ans = b1_get_byte(base)) == RECEIVE_POLL) { + return 1; + } + printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n", + card->name, ans); + return 0; + } + } + printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name); + return 0; +} + +/* ------------------------------------------------------------- */ + +int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + int retval; + + b1_reset(port); + + if ((retval = b1_load_t4file(card, &data->firmware))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + b1_disable_irq(port); + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(card, &data->configuration))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1_loaded(card)) { + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + spin_lock_irqsave(&card->lock, flags); + b1_setinterrupt(port, card->irq, card->cardtype); + b1_put_byte(port, SEND_INIT); + b1_put_word(port, CAPI_MAXAPPL); + b1_put_word(port, AVM_NCCI_PER_CHANNEL*2); + b1_put_word(port, ctrl->cnr - 1); + spin_unlock_irqrestore(&card->lock, flags); + + return 0; +} + +void b1_reset_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + memset(cinfo->version, 0, sizeof(cinfo->version)); + capilib_release(&cinfo->ncci_head); + capi_ctr_reseted(ctrl); +} + +void b1_register_appl(struct capi_ctr *ctrl, + u16 appl, + capi_register_params *rp) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + int nconn, want = rp->level3cnt; + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel; + + spin_lock_irqsave(&card->lock, flags); + b1_put_byte(port, SEND_REGISTER); + b1_put_word(port, appl); + b1_put_word(port, 1024 * (nconn+1)); + b1_put_word(port, nconn); + b1_put_word(port, rp->datablkcnt); + b1_put_word(port, rp->datablklen); + spin_unlock_irqrestore(&card->lock, flags); +} + +void b1_release_appl(struct capi_ctr *ctrl, u16 appl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + + capilib_release_appl(&cinfo->ncci_head, appl); + + spin_lock_irqsave(&card->lock, flags); + b1_put_byte(port, SEND_RELEASE); + b1_put_word(port, appl); + spin_unlock_irqrestore(&card->lock, flags); +} + +u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + u16 len = CAPIMSG_LEN(skb->data); + u8 cmd = CAPIMSG_COMMAND(skb->data); + u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); + u16 dlen, retval; + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + retval = capilib_data_b3_req(&cinfo->ncci_head, + CAPIMSG_APPID(skb->data), + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + if (retval != CAPI_NOERROR) + return retval; + + dlen = CAPIMSG_DATALEN(skb->data); + + spin_lock_irqsave(&card->lock, flags); + b1_put_byte(port, SEND_DATA_B3_REQ); + b1_put_slice(port, skb->data, len); + b1_put_slice(port, skb->data + len, dlen); + spin_unlock_irqrestore(&card->lock, flags); + } else { + spin_lock_irqsave(&card->lock, flags); + b1_put_byte(port, SEND_MESSAGE); + b1_put_slice(port, skb->data, len); + spin_unlock_irqrestore(&card->lock, flags); + } + + dev_kfree_skb_any(skb); + return CAPI_NOERROR; +} + +/* ------------------------------------------------------------- */ + +void b1_parse_version(avmctrl_info *cinfo) +{ + struct capi_ctr *ctrl = &cinfo->capi_ctrl; + avmcard *card = cinfo->card; + capi_profile *profp; + u8 *dversion; + u8 flag; + int i, j; + + for (j = 0; j < AVM_MAXVERSION; j++) + cinfo->version[j] = "\0\0" + 1; + for (i = 0, j = 0; + j < AVM_MAXVERSION && i < cinfo->versionlen; + j++, i += cinfo->versionbuf[i] + 1) + cinfo->version[j] = &cinfo->versionbuf[i + 1]; + + strlcpy(ctrl->serial, cinfo->version[VER_SERIAL], sizeof(ctrl->serial)); + memcpy(&ctrl->profile, cinfo->version[VER_PROFILE],sizeof(capi_profile)); + strlcpy(ctrl->manu, "AVM GmbH", sizeof(ctrl->manu)); + dversion = cinfo->version[VER_DRIVER]; + ctrl->version.majorversion = 2; + ctrl->version.minorversion = 0; + ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4); + ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf); + ctrl->version.minormanuversion = (dversion[3] - '0') << 4; + ctrl->version.minormanuversion |= + (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf); + + profp = &ctrl->profile; + + flag = ((u8 *)(profp->manu))[1]; + switch (flag) { + case 0: if (cinfo->version[VER_CARDTYPE]) + strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]); + else strcpy(cinfo->cardname, "B1"); + break; + case 3: strcpy(cinfo->cardname,"PCMCIA B"); break; + case 4: strcpy(cinfo->cardname,"PCMCIA M1"); break; + case 5: strcpy(cinfo->cardname,"PCMCIA M2"); break; + case 6: strcpy(cinfo->cardname,"B1 V3.0"); break; + case 7: strcpy(cinfo->cardname,"B1 PCI"); break; + default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break; + } + printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n", + card->name, ctrl->cnr, cinfo->cardname); + + flag = ((u8 *)(profp->manu))[3]; + if (flag) + printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n", + card->name, + ctrl->cnr, + (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 "%s: card %d Linetype:%s%s%s%s\n", + card->name, + ctrl->cnr, + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); +} + +/* ------------------------------------------------------------- */ + +irqreturn_t b1_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card = devptr; + avmctrl_info *cinfo = &card->ctrlinfo[0]; + struct capi_ctr *ctrl = &cinfo->capi_ctrl; + unsigned char b1cmd; + struct sk_buff *skb; + + unsigned ApplId; + unsigned MsgLen; + unsigned DataB3Len; + unsigned NCCI; + unsigned WindowSize; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + if (!b1_rx_full(card->port)) { + spin_unlock_irqrestore(&card->lock, flags); + return IRQ_NONE; + } + + b1cmd = b1_get_byte(card->port); + + switch (b1cmd) { + + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + DataB3Len = b1_get_slice(card->port, card->databuf); + spin_unlock_irqrestore(&card->lock, flags); + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf+MsgLen, 0, 30-MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + spin_unlock_irqrestore(&card->lock, flags); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) + capilib_data_b3_conf(&cinfo->ncci_head, ApplId, + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + WindowSize = b1_get_word(card->port); + spin_unlock_irqrestore(&card->lock, flags); + + capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + spin_unlock_irqrestore(&card->lock, flags); + + if (NCCI != 0xffffffff) + capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI); + + break; + + case RECEIVE_START: + /* b1_put_byte(card->port, SEND_POLLACK); */ + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_resume_output(ctrl); + break; + + case RECEIVE_STOP: + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf); + spin_unlock_irqrestore(&card->lock, flags); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + capi_ctr_ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = b1_get_slice(card->port, card->msgbuf); + spin_unlock_irqrestore(&card->lock, flags); + card->msgbuf[MsgLen] = 0; + while ( MsgLen > 0 + && ( card->msgbuf[MsgLen-1] == '\n' + || card->msgbuf[MsgLen-1] == '\r')) { + card->msgbuf[MsgLen-1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = b1_get_slice(card->port, card->msgbuf); + spin_unlock_irqrestore(&card->lock, flags); + card->msgbuf[MsgLen] = 0; + while ( MsgLen > 0 + && ( card->msgbuf[MsgLen-1] == '\n' + || card->msgbuf[MsgLen-1] == '\r')) { + card->msgbuf[MsgLen-1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + case 0xff: + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "%s: card removed ?\n", card->name); + return IRQ_NONE; + default: + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", + card->name, b1cmd); + return IRQ_HANDLED; + } + return IRQ_HANDLED; +} + +/* ------------------------------------------------------------- */ +int b1ctl_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + u8 flag; + int len = 0; + char *s; + + len += sprintf(page+len, "%-16s %s\n", "name", card->name); + len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port); + len += sprintf(page+len, "%-16s %d\n", "irq", card->irq); + switch (card->cardtype) { + case avm_b1isa: s = "B1 ISA"; break; + case avm_b1pci: s = "B1 PCI"; break; + case avm_b1pcmcia: s = "B1 PCMCIA"; break; + case avm_m1: s = "M1"; break; + case avm_m2: s = "M2"; break; + case avm_t1isa: s = "T1 ISA (HEMA)"; break; + case avm_t1pci: s = "T1 PCI"; break; + case avm_c4: s = "C4"; break; + case avm_c2: s = "C2"; break; + default: s = "???"; break; + } + len += sprintf(page+len, "%-16s %s\n", "type", s); + if (card->cardtype == avm_t1isa) + len += sprintf(page+len, "%-16s %d\n", "cardnr", card->cardnr); + if ((s = cinfo->version[VER_DRIVER]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); + if ((s = cinfo->version[VER_CARDTYPE]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); + if ((s = cinfo->version[VER_SERIAL]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_serial", s); + + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[3]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n", + "protocol", + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + } + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[5]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s\n", + "linetype", + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); + } + len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname); + + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +} + +/* ------------------------------------------------------------- */ + +#ifdef CONFIG_PCI + +avmcard_dmainfo * +avmcard_dma_alloc(char *name, struct pci_dev *pdev, long rsize, long ssize) +{ + avmcard_dmainfo *p; + void *buf; + + p = kmalloc(sizeof(avmcard_dmainfo), GFP_KERNEL); + if (!p) { + printk(KERN_WARNING "%s: no memory.\n", name); + goto err; + } + memset(p, 0, sizeof(avmcard_dmainfo)); + + p->recvbuf.size = rsize; + buf = pci_alloc_consistent(pdev, rsize, &p->recvbuf.dmaaddr); + if (!buf) { + printk(KERN_WARNING "%s: allocation of receive dma buffer failed.\n", name); + goto err_kfree; + } + p->recvbuf.dmabuf = buf; + + p->sendbuf.size = ssize; + buf = pci_alloc_consistent(pdev, ssize, &p->sendbuf.dmaaddr); + if (!buf) { + printk(KERN_WARNING "%s: allocation of send dma buffer failed.\n", name); + goto err_free_consistent; + } + + p->sendbuf.dmabuf = buf; + skb_queue_head_init(&p->send_queue); + + return p; + + err_free_consistent: + pci_free_consistent(p->pcidev, p->recvbuf.size, + p->recvbuf.dmabuf, p->recvbuf.dmaaddr); + err_kfree: + kfree(p); + err: + return NULL; +} + +void avmcard_dma_free(avmcard_dmainfo *p) +{ + pci_free_consistent(p->pcidev, p->recvbuf.size, + p->recvbuf.dmabuf, p->recvbuf.dmaaddr); + pci_free_consistent(p->pcidev, p->sendbuf.size, + p->sendbuf.dmabuf, p->sendbuf.dmaaddr); + skb_queue_purge(&p->send_queue); + kfree(p); +} + +EXPORT_SYMBOL(avmcard_dma_alloc); +EXPORT_SYMBOL(avmcard_dma_free); + +#endif + +EXPORT_SYMBOL(b1_irq_table); + +EXPORT_SYMBOL(b1_alloc_card); +EXPORT_SYMBOL(b1_free_card); +EXPORT_SYMBOL(b1_detect); +EXPORT_SYMBOL(b1_getrevision); +EXPORT_SYMBOL(b1_load_t4file); +EXPORT_SYMBOL(b1_load_config); +EXPORT_SYMBOL(b1_loaded); +EXPORT_SYMBOL(b1_load_firmware); +EXPORT_SYMBOL(b1_reset_ctr); +EXPORT_SYMBOL(b1_register_appl); +EXPORT_SYMBOL(b1_release_appl); +EXPORT_SYMBOL(b1_send_message); + +EXPORT_SYMBOL(b1_parse_version); +EXPORT_SYMBOL(b1_interrupt); + +EXPORT_SYMBOL(b1ctl_read_proc); + +static int __init b1_init(void) +{ + char *p; + char rev[32]; + + if ((p = strchr(revision, ':')) != 0 && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != 0 && p > rev) + *(p-1) = 0; + } else + strcpy(rev, "1.0"); + + printk(KERN_INFO "b1: revision %s\n", rev); + + return 0; +} + +static void __exit b1_exit(void) +{ +} + +module_init(b1_init); +module_exit(b1_exit); diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c new file mode 100644 index 0000000..55bed00 --- /dev/null +++ b/drivers/isdn/hardware/avm/b1dma.c @@ -0,0 +1,980 @@ +/* $Id: b1dma.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $ + * + * Common module for AVM B1 cards that support dma with AMCC + * + * Copyright 2000 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <linux/kernelcapi.h> +#include <asm/io.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <linux/netdevice.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> + +static char *revision = "$Revision: 1.1.2.3 $"; + +#undef CONFIG_B1DMA_DEBUG + +/* ------------------------------------------------------------- */ + +MODULE_DESCRIPTION("CAPI4Linux: DMA support for active AVM cards"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +static int suppress_pollack = 0; +MODULE_PARM(suppress_pollack, "0-1i"); + +/* ------------------------------------------------------------- */ + +static void b1dma_dispatch_tx(avmcard *card); + +/* ------------------------------------------------------------- */ + +/* S5933 */ + +#define AMCC_RXPTR 0x24 +#define AMCC_RXLEN 0x28 +#define AMCC_TXPTR 0x2c +#define AMCC_TXLEN 0x30 + +#define AMCC_INTCSR 0x38 +# define EN_READ_TC_INT 0x00008000L +# define EN_WRITE_TC_INT 0x00004000L +# define EN_TX_TC_INT EN_READ_TC_INT +# define EN_RX_TC_INT EN_WRITE_TC_INT +# define AVM_FLAG 0x30000000L + +# define ANY_S5933_INT 0x00800000L +# define READ_TC_INT 0x00080000L +# define WRITE_TC_INT 0x00040000L +# define TX_TC_INT READ_TC_INT +# define RX_TC_INT WRITE_TC_INT +# define MASTER_ABORT_INT 0x00100000L +# define TARGET_ABORT_INT 0x00200000L +# define BUS_MASTER_INT 0x00200000L +# define ALL_INT 0x000C0000L + +#define AMCC_MCSR 0x3c +# define A2P_HI_PRIORITY 0x00000100L +# define EN_A2P_TRANSFERS 0x00000400L +# define P2A_HI_PRIORITY 0x00001000L +# define EN_P2A_TRANSFERS 0x00004000L +# define RESET_A2P_FLAGS 0x04000000L +# define RESET_P2A_FLAGS 0x02000000L + +/* ------------------------------------------------------------- */ + +static inline void b1dma_writel(avmcard *card, u32 value, int off) +{ + writel(value, card->mbase + off); +} + +static inline u32 b1dma_readl(avmcard *card, int off) +{ + return readl(card->mbase + off); +} + +/* ------------------------------------------------------------- */ + +static inline int b1dma_tx_empty(unsigned int port) +{ + return inb(port + 0x03) & 0x1; +} + +static inline int b1dma_rx_full(unsigned int port) +{ + return inb(port + 0x02) & 0x1; +} + +static int b1dma_tolink(avmcard *card, void *buf, unsigned int len) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + unsigned char *s = (unsigned char *)buf; + while (len--) { + while ( !b1dma_tx_empty(card->port) + && time_before(jiffies, stop)); + if (!b1dma_tx_empty(card->port)) + return -1; + t1outp(card->port, 0x01, *s++); + } + return 0; +} + +static int b1dma_fromlink(avmcard *card, void *buf, unsigned int len) +{ + unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */ + unsigned char *s = (unsigned char *)buf; + while (len--) { + while ( !b1dma_rx_full(card->port) + && time_before(jiffies, stop)); + if (!b1dma_rx_full(card->port)) + return -1; + *s++ = t1inp(card->port, 0x00); + } + return 0; +} + +static int WriteReg(avmcard *card, u32 reg, u8 val) +{ + u8 cmd = 0x00; + if ( b1dma_tolink(card, &cmd, 1) == 0 + && b1dma_tolink(card, ®, 4) == 0) { + u32 tmp = val; + return b1dma_tolink(card, &tmp, 4); + } + return -1; +} + +static u8 ReadReg(avmcard *card, u32 reg) +{ + u8 cmd = 0x01; + if ( b1dma_tolink(card, &cmd, 1) == 0 + && b1dma_tolink(card, ®, 4) == 0) { + u32 tmp; + if (b1dma_fromlink(card, &tmp, 4) == 0) + return (u8)tmp; + } + return 0xff; +} + +/* ------------------------------------------------------------- */ + +static inline void _put_byte(void **pp, u8 val) +{ + u8 *s = *pp; + *s++ = val; + *pp = s; +} + +static inline void _put_word(void **pp, u32 val) +{ + u8 *s = *pp; + *s++ = val & 0xff; + *s++ = (val >> 8) & 0xff; + *s++ = (val >> 16) & 0xff; + *s++ = (val >> 24) & 0xff; + *pp = s; +} + +static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len) +{ + unsigned i = len; + _put_word(pp, i); + while (i-- > 0) + _put_byte(pp, *dp++); +} + +static inline u8 _get_byte(void **pp) +{ + u8 *s = *pp; + u8 val; + val = *s++; + *pp = s; + return val; +} + +static inline u32 _get_word(void **pp) +{ + u8 *s = *pp; + u32 val; + val = *s++; + val |= (*s++ << 8); + val |= (*s++ << 16); + val |= (*s++ << 24); + *pp = s; + return val; +} + +static inline u32 _get_slice(void **pp, unsigned char *dp) +{ + unsigned int len, i; + + len = i = _get_word(pp); + while (i-- > 0) *dp++ = _get_byte(pp); + return len; +} + +/* ------------------------------------------------------------- */ + +void b1dma_reset(avmcard *card) +{ + card->csr = 0x0; + b1dma_writel(card, card->csr, AMCC_INTCSR); + b1dma_writel(card, 0, AMCC_MCSR); + b1dma_writel(card, 0, AMCC_RXLEN); + b1dma_writel(card, 0, AMCC_TXLEN); + + t1outp(card->port, 0x10, 0x00); + t1outp(card->port, 0x07, 0x00); + + b1dma_writel(card, 0, AMCC_MCSR); + mdelay(10); + b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */ + mdelay(10); + b1dma_writel(card, 0, AMCC_MCSR); + if (card->cardtype == avm_t1pci) + mdelay(42); + else + mdelay(10); +} + +/* ------------------------------------------------------------- */ + +static int b1dma_detect(avmcard *card) +{ + b1dma_writel(card, 0, AMCC_MCSR); + mdelay(10); + b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */ + mdelay(10); + b1dma_writel(card, 0, AMCC_MCSR); + mdelay(42); + + b1dma_writel(card, 0, AMCC_RXLEN); + b1dma_writel(card, 0, AMCC_TXLEN); + card->csr = 0x0; + b1dma_writel(card, card->csr, AMCC_INTCSR); + + if (b1dma_readl(card, AMCC_MCSR) != 0x000000E6) + return 1; + + b1dma_writel(card, 0xffffffff, AMCC_RXPTR); + b1dma_writel(card, 0xffffffff, AMCC_TXPTR); + if ( b1dma_readl(card, AMCC_RXPTR) != 0xfffffffc + || b1dma_readl(card, AMCC_TXPTR) != 0xfffffffc) + return 2; + + b1dma_writel(card, 0x0, AMCC_RXPTR); + b1dma_writel(card, 0x0, AMCC_TXPTR); + if ( b1dma_readl(card, AMCC_RXPTR) != 0x0 + || b1dma_readl(card, AMCC_TXPTR) != 0x0) + return 3; + + t1outp(card->port, 0x10, 0x00); + t1outp(card->port, 0x07, 0x00); + + t1outp(card->port, 0x02, 0x02); + t1outp(card->port, 0x03, 0x02); + + if ( (t1inp(card->port, 0x02) & 0xFE) != 0x02 + || t1inp(card->port, 0x3) != 0x03) + return 4; + + t1outp(card->port, 0x02, 0x00); + t1outp(card->port, 0x03, 0x00); + + if ( (t1inp(card->port, 0x02) & 0xFE) != 0x00 + || t1inp(card->port, 0x3) != 0x01) + return 5; + + return 0; +} + +int t1pci_detect(avmcard *card) +{ + int ret; + + if ((ret = b1dma_detect(card)) != 0) + return ret; + + /* Transputer test */ + + if ( WriteReg(card, 0x80001000, 0x11) != 0 + || WriteReg(card, 0x80101000, 0x22) != 0 + || WriteReg(card, 0x80201000, 0x33) != 0 + || WriteReg(card, 0x80301000, 0x44) != 0) + return 6; + + if ( ReadReg(card, 0x80001000) != 0x11 + || ReadReg(card, 0x80101000) != 0x22 + || ReadReg(card, 0x80201000) != 0x33 + || ReadReg(card, 0x80301000) != 0x44) + return 7; + + if ( WriteReg(card, 0x80001000, 0x55) != 0 + || WriteReg(card, 0x80101000, 0x66) != 0 + || WriteReg(card, 0x80201000, 0x77) != 0 + || WriteReg(card, 0x80301000, 0x88) != 0) + return 8; + + if ( ReadReg(card, 0x80001000) != 0x55 + || ReadReg(card, 0x80101000) != 0x66 + || ReadReg(card, 0x80201000) != 0x77 + || ReadReg(card, 0x80301000) != 0x88) + return 9; + + return 0; +} + +int b1pciv4_detect(avmcard *card) +{ + int ret, i; + + if ((ret = b1dma_detect(card)) != 0) + return ret; + + for (i=0; i < 5 ; i++) { + if (WriteReg(card, 0x80A00000, 0x21) != 0) + return 6; + if ((ReadReg(card, 0x80A00000) & 0x01) != 0x01) + return 7; + } + for (i=0; i < 5 ; i++) { + if (WriteReg(card, 0x80A00000, 0x20) != 0) + return 8; + if ((ReadReg(card, 0x80A00000) & 0x01) != 0x00) + return 9; + } + + return 0; +} + +static void b1dma_queue_tx(avmcard *card, struct sk_buff *skb) +{ + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + skb_queue_tail(&card->dma->send_queue, skb); + + if (!(card->csr & EN_TX_TC_INT)) { + b1dma_dispatch_tx(card); + b1dma_writel(card, card->csr, AMCC_INTCSR); + } + + spin_unlock_irqrestore(&card->lock, flags); +} + +/* ------------------------------------------------------------- */ + +static void b1dma_dispatch_tx(avmcard *card) +{ + avmcard_dmainfo *dma = card->dma; + struct sk_buff *skb; + u8 cmd, subcmd; + u16 len; + u32 txlen; + void *p; + + skb = skb_dequeue(&dma->send_queue); + + len = CAPIMSG_LEN(skb->data); + + if (len) { + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + p = dma->sendbuf.dmabuf; + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + u16 dlen = CAPIMSG_DATALEN(skb->data); + _put_byte(&p, SEND_DATA_B3_REQ); + _put_slice(&p, skb->data, len); + _put_slice(&p, skb->data + len, dlen); + } else { + _put_byte(&p, SEND_MESSAGE); + _put_slice(&p, skb->data, len); + } + txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf; +#ifdef CONFIG_B1DMA_DEBUG + printk(KERN_DEBUG "tx: put msg len=%d\n", txlen); +#endif + } else { + txlen = skb->len-2; +#ifdef CONFIG_B1DMA_POLLDEBUG + if (skb->data[2] == SEND_POLLACK) + printk(KERN_INFO "%s: send ack\n", card->name); +#endif +#ifdef CONFIG_B1DMA_DEBUG + printk(KERN_DEBUG "tx: put 0x%x len=%d\n", + skb->data[2], txlen); +#endif + memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2); + } + txlen = (txlen + 3) & ~3; + + b1dma_writel(card, dma->sendbuf.dmaaddr, AMCC_TXPTR); + b1dma_writel(card, txlen, AMCC_TXLEN); + + card->csr |= EN_TX_TC_INT; + + dev_kfree_skb_any(skb); +} + +/* ------------------------------------------------------------- */ + +static void queue_pollack(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(3, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost poll ack\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_POLLACK); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + b1dma_queue_tx(card, skb); +} + +/* ------------------------------------------------------------- */ + +static void b1dma_handle_rx(avmcard *card) +{ + avmctrl_info *cinfo = &card->ctrlinfo[0]; + avmcard_dmainfo *dma = card->dma; + struct capi_ctr *ctrl = &cinfo->capi_ctrl; + struct sk_buff *skb; + void *p = dma->recvbuf.dmabuf+4; + u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize; + u8 b1cmd = _get_byte(&p); + +#ifdef CONFIG_B1DMA_DEBUG + printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen); +#endif + + switch (b1cmd) { + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + DataB3Len = _get_slice(&p, card->databuf); + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf+MsgLen, 0, 30-MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) + capilib_data_b3_conf(&cinfo->ncci_head, ApplId, + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + WindowSize = _get_word(&p); + + capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + + if (NCCI != 0xffffffff) + capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI); + + break; + + case RECEIVE_START: +#ifdef CONFIG_B1DMA_POLLDEBUG + printk(KERN_INFO "%s: receive poll\n", card->name); +#endif + if (!suppress_pollack) + queue_pollack(card); + capi_ctr_resume_output(ctrl); + break; + + case RECEIVE_STOP: + capi_ctr_suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + cinfo->versionlen = _get_slice(&p, cinfo->versionbuf); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + capi_ctr_ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + card->msgbuf[MsgLen] = 0; + while ( MsgLen > 0 + && ( card->msgbuf[MsgLen-1] == '\n' + || card->msgbuf[MsgLen-1] == '\r')) { + card->msgbuf[MsgLen-1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = _get_slice(&p, card->msgbuf); + card->msgbuf[MsgLen] = 0; + while ( MsgLen > 0 + && ( card->msgbuf[MsgLen-1] == '\n' + || card->msgbuf[MsgLen-1] == '\r')) { + card->msgbuf[MsgLen-1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + default: + printk(KERN_ERR "%s: b1dma_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } +} + +/* ------------------------------------------------------------- */ + +static void b1dma_handle_interrupt(avmcard *card) +{ + u32 status; + u32 newcsr; + + spin_lock(&card->lock); + + status = b1dma_readl(card, AMCC_INTCSR); + if ((status & ANY_S5933_INT) == 0) { + spin_unlock(&card->lock); + return; + } + + newcsr = card->csr | (status & ALL_INT); + if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT; + if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT; + b1dma_writel(card, newcsr, AMCC_INTCSR); + + if ((status & RX_TC_INT) != 0) { + struct avmcard_dmainfo *dma = card->dma; + u32 rxlen; + if (card->dma->recvlen == 0) { + rxlen = b1dma_readl(card, AMCC_RXLEN); + if (rxlen == 0) { + dma->recvlen = *((u32 *)dma->recvbuf.dmabuf); + rxlen = (dma->recvlen + 3) & ~3; + b1dma_writel(card, dma->recvbuf.dmaaddr+4, AMCC_RXPTR); + b1dma_writel(card, rxlen, AMCC_RXLEN); +#ifdef CONFIG_B1DMA_DEBUG + } else { + printk(KERN_ERR "%s: rx not complete (%d).\n", + card->name, rxlen); +#endif + } + } else { + spin_unlock(&card->lock); + b1dma_handle_rx(card); + dma->recvlen = 0; + spin_lock(&card->lock); + b1dma_writel(card, dma->recvbuf.dmaaddr, AMCC_RXPTR); + b1dma_writel(card, 4, AMCC_RXLEN); + } + } + + if ((status & TX_TC_INT) != 0) { + if (skb_queue_empty(&card->dma->send_queue)) + card->csr &= ~EN_TX_TC_INT; + else + b1dma_dispatch_tx(card); + } + b1dma_writel(card, card->csr, AMCC_INTCSR); + + spin_unlock(&card->lock); +} + +irqreturn_t b1dma_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card = devptr; + + b1dma_handle_interrupt(card); + return IRQ_HANDLED; +} + +/* ------------------------------------------------------------- */ + +static int b1dma_loaded(avmcard *card) +{ + unsigned long stop; + unsigned char ans; + unsigned long tout = 2; + unsigned int base = card->port; + + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_tx_empty(base)) + break; + } + if (!b1_tx_empty(base)) { + printk(KERN_ERR "%s: b1dma_loaded: tx err, corrupted t4 file ?\n", + card->name); + return 0; + } + b1_put_byte(base, SEND_POLLACK); + for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) { + if (b1_rx_full(base)) { + if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) { + return 1; + } + printk(KERN_ERR "%s: b1dma_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans); + return 0; + } + } + printk(KERN_ERR "%s: b1dma_loaded: firmware not running\n", card->name); + return 0; +} + +/* ------------------------------------------------------------- */ + +static void b1dma_send_init(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(15, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_INIT); + _put_word(&p, CAPI_MAXAPPL); + _put_word(&p, AVM_NCCI_PER_CHANNEL*30); + _put_word(&p, card->cardnr - 1); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + b1dma_queue_tx(card, skb); +} + +int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + int retval; + + b1dma_reset(card); + + if ((retval = b1_load_t4file(card, &data->firmware))) { + b1dma_reset(card); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(card, &data->configuration))) { + b1dma_reset(card); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1dma_loaded(card)) { + b1dma_reset(card); + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + card->csr = AVM_FLAG; + b1dma_writel(card, card->csr, AMCC_INTCSR); + b1dma_writel(card, EN_A2P_TRANSFERS|EN_P2A_TRANSFERS|A2P_HI_PRIORITY| + P2A_HI_PRIORITY|RESET_A2P_FLAGS|RESET_P2A_FLAGS, + AMCC_MCSR); + t1outp(card->port, 0x07, 0x30); + t1outp(card->port, 0x10, 0xF0); + + card->dma->recvlen = 0; + b1dma_writel(card, card->dma->recvbuf.dmaaddr, AMCC_RXPTR); + b1dma_writel(card, 4, AMCC_RXLEN); + card->csr |= EN_RX_TC_INT; + b1dma_writel(card, card->csr, AMCC_INTCSR); + + b1dma_send_init(card); + + return 0; +} + +void b1dma_reset_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + b1dma_reset(card); + spin_unlock_irqrestore(&card->lock, flags); + + memset(cinfo->version, 0, sizeof(cinfo->version)); + capilib_release(&cinfo->ncci_head); + capi_ctr_reseted(ctrl); +} + +/* ------------------------------------------------------------- */ + +void b1dma_register_appl(struct capi_ctr *ctrl, + u16 appl, + capi_register_params *rp) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + int want = rp->level3cnt; + int nconn; + void *p; + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel; + + skb = alloc_skb(23, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_REGISTER); + _put_word(&p, appl); + _put_word(&p, 1024 * (nconn+1)); + _put_word(&p, nconn); + _put_word(&p, rp->datablkcnt); + _put_word(&p, rp->datablklen); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + b1dma_queue_tx(card, skb); +} + +/* ------------------------------------------------------------- */ + +void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + void *p; + + capilib_release_appl(&cinfo->ncci_head, appl); + + skb = alloc_skb(7, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost release appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_RELEASE); + _put_word(&p, appl); + + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + b1dma_queue_tx(card, skb); +} + +/* ------------------------------------------------------------- */ + +u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + u16 retval = CAPI_NOERROR; + + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { + retval = capilib_data_b3_req(&cinfo->ncci_head, + CAPIMSG_APPID(skb->data), + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + } + if (retval == CAPI_NOERROR) + b1dma_queue_tx(card, skb); + + return retval; +} + +/* ------------------------------------------------------------- */ + +int b1dmactl_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + u8 flag; + int len = 0; + char *s; + u32 txoff, txlen, rxoff, rxlen, csr; + unsigned long flags; + + len += sprintf(page+len, "%-16s %s\n", "name", card->name); + len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port); + len += sprintf(page+len, "%-16s %d\n", "irq", card->irq); + len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase); + switch (card->cardtype) { + case avm_b1isa: s = "B1 ISA"; break; + case avm_b1pci: s = "B1 PCI"; break; + case avm_b1pcmcia: s = "B1 PCMCIA"; break; + case avm_m1: s = "M1"; break; + case avm_m2: s = "M2"; break; + case avm_t1isa: s = "T1 ISA (HEMA)"; break; + case avm_t1pci: s = "T1 PCI"; break; + case avm_c4: s = "C4"; break; + case avm_c2: s = "C2"; break; + default: s = "???"; break; + } + len += sprintf(page+len, "%-16s %s\n", "type", s); + if ((s = cinfo->version[VER_DRIVER]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); + if ((s = cinfo->version[VER_CARDTYPE]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); + if ((s = cinfo->version[VER_SERIAL]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_serial", s); + + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[3]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n", + "protocol", + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + } + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[5]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s\n", + "linetype", + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); + } + len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname); + + + spin_lock_irqsave(&card->lock, flags); + + txoff = (dma_addr_t)b1dma_readl(card, AMCC_TXPTR)-card->dma->sendbuf.dmaaddr; + txlen = b1dma_readl(card, AMCC_TXLEN); + + rxoff = (dma_addr_t)b1dma_readl(card, AMCC_RXPTR)-card->dma->recvbuf.dmaaddr; + rxlen = b1dma_readl(card, AMCC_RXLEN); + + csr = b1dma_readl(card, AMCC_INTCSR); + + spin_unlock_irqrestore(&card->lock, flags); + + len += sprintf(page+len, "%-16s 0x%lx\n", + "csr (cached)", (unsigned long)card->csr); + len += sprintf(page+len, "%-16s 0x%lx\n", + "csr", (unsigned long)csr); + len += sprintf(page+len, "%-16s %lu\n", + "txoff", (unsigned long)txoff); + len += sprintf(page+len, "%-16s %lu\n", + "txlen", (unsigned long)txlen); + len += sprintf(page+len, "%-16s %lu\n", + "rxoff", (unsigned long)rxoff); + len += sprintf(page+len, "%-16s %lu\n", + "rxlen", (unsigned long)rxlen); + + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +} + +/* ------------------------------------------------------------- */ + +EXPORT_SYMBOL(b1dma_reset); +EXPORT_SYMBOL(t1pci_detect); +EXPORT_SYMBOL(b1pciv4_detect); +EXPORT_SYMBOL(b1dma_interrupt); + +EXPORT_SYMBOL(b1dma_load_firmware); +EXPORT_SYMBOL(b1dma_reset_ctr); +EXPORT_SYMBOL(b1dma_register_appl); +EXPORT_SYMBOL(b1dma_release_appl); +EXPORT_SYMBOL(b1dma_send_message); +EXPORT_SYMBOL(b1dmactl_read_proc); + +int b1dma_init(void) +{ + char *p; + char rev[32]; + + 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_INFO "b1dma: revision %s\n", rev); + + return 0; +} + +void b1dma_exit(void) +{ +} + +module_init(b1dma_init); +module_exit(b1dma_exit); diff --git a/drivers/isdn/hardware/avm/b1isa.c b/drivers/isdn/hardware/avm/b1isa.c new file mode 100644 index 0000000..38bd4df --- /dev/null +++ b/drivers/isdn/hardware/avm/b1isa.c @@ -0,0 +1,245 @@ +/* $Id: b1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $ + * + * Module for AVM B1 ISA-card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +/* ------------------------------------------------------------- */ + +static char *revision = "$Revision: 1.1.2.3 $"; + +/* ------------------------------------------------------------- */ + +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 ISA card"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +static void b1isa_remove(struct pci_dev *pdev) +{ + avmctrl_info *cinfo = pci_get_drvdata(pdev); + avmcard *card; + + if (!cinfo) + return; + + card = cinfo->card; + + b1_reset(card->port); + b1_reset(card->port); + + detach_capi_ctr(&cinfo->capi_ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + b1_free_card(card); +} + +/* ------------------------------------------------------------- */ + +static char *b1isa_procinfo(struct capi_ctr *ctrl); + +static int b1isa_probe(struct pci_dev *pdev) +{ + avmctrl_info *cinfo; + avmcard *card; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "b1isa: no memory.\n"); + retval = -ENOMEM; + goto err; + } + + cinfo = card->ctrlinfo; + + card->port = pci_resource_start(pdev, 0); + card->irq = pdev->irq; + card->cardtype = avm_b1isa; + sprintf(card->name, "b1isa-%x", card->port); + + if ( card->port != 0x150 && card->port != 0x250 + && card->port != 0x300 && card->port != 0x340) { + printk(KERN_WARNING "b1isa: invalid port 0x%x.\n", card->port); + retval = -EINVAL; + goto err_free; + } + if (b1_irq_table[card->irq & 0xf] == 0) { + printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq); + retval = -EINVAL; + goto err_free; + } + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_WARNING "b1isa: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free; + } + retval = request_irq(card->irq, b1_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq); + goto err_release_region; + } + b1_reset(card->port); + if ((retval = b1_detect(card->port, card->cardtype)) != 0) { + printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n", + card->port, retval); + retval = -ENODEV; + goto err_free_irq; + } + b1_reset(card->port); + b1_getrevision(card); + + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "b1isa"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1_register_appl; + cinfo->capi_ctrl.release_appl = b1_release_appl; + cinfo->capi_ctrl.send_message = b1_send_message; + cinfo->capi_ctrl.load_firmware = b1_load_firmware; + cinfo->capi_ctrl.reset_ctr = b1_reset_ctr; + cinfo->capi_ctrl.procinfo = b1isa_procinfo; + cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "b1isa: attach controller failed.\n"); + goto err_free_irq; + } + + printk(KERN_INFO "b1isa: AVM B1 ISA at i/o %#x, irq %d, revision %d\n", + card->port, card->irq, card->revision); + + pci_set_drvdata(pdev, cinfo); + return 0; + + err_free_irq: + free_irq(card->irq, card); + err_release_region: + release_region(card->port, AVMB1_PORTLEN); + err_free: + b1_free_card(card); + err: + return retval; +} + +static char *b1isa_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->revision : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +#define MAX_CARDS 4 +static struct pci_dev isa_dev[MAX_CARDS]; +static int io[MAX_CARDS]; +static int irq[MAX_CARDS]; + +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i"); +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); + +static int b1isa_add_card(struct capi_driver *driver, capicardparams *data) +{ + int i; + + for (i = 0; i < MAX_CARDS; i++) { + if (isa_dev[i].resource[0].start) + continue; + + isa_dev[i].resource[0].start = data->port; + isa_dev[i].irq = data->irq; + + if (b1isa_probe(&isa_dev[i]) == 0) + return 0; + } + return -ENODEV; +} + +static struct capi_driver capi_driver_b1isa = { + .name = "b1isa", + .revision = "1.0", + .add_card = b1isa_add_card, +}; + +static int __init b1isa_init(void) +{ + char *p; + char rev[32]; + int i; + + if ((p = strchr(revision, ':')) != 0 && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != 0 && p > rev) + *(p-1) = 0; + } else + strcpy(rev, "1.0"); + + for (i = 0; i < MAX_CARDS; i++) { + if (!io[i]) + break; + + isa_dev[i].resource[0].start = io[i]; + isa_dev[i].irq = irq[i]; + + if (b1isa_probe(&isa_dev[i]) != 0) + return -ENODEV; + } + + strlcpy(capi_driver_b1isa.revision, rev, 32); + register_capi_driver(&capi_driver_b1isa); + printk(KERN_INFO "b1isa: revision %s\n", rev); + + return 0; +} + +static void __exit b1isa_exit(void) +{ + int i; + + for (i = 0; i < MAX_CARDS; i++) { + if (!io[i]) + break; + + b1isa_remove(&isa_dev[i]); + } + unregister_capi_driver(&capi_driver_b1isa); +} + +module_init(b1isa_init); +module_exit(b1isa_exit); diff --git a/drivers/isdn/hardware/avm/b1pci.c b/drivers/isdn/hardware/avm/b1pci.c new file mode 100644 index 0000000..5435a6c --- /dev/null +++ b/drivers/isdn/hardware/avm/b1pci.c @@ -0,0 +1,417 @@ +/* $Id: b1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ + * + * Module for AVM B1 PCI-card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/capi.h> +#include <asm/io.h> +#include <linux/init.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +/* ------------------------------------------------------------- */ + +static char *revision = "$Revision: 1.1.2.2 $"; + +/* ------------------------------------------------------------- */ + +static struct pci_device_id b1pci_pci_tbl[] = { + { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, PCI_ANY_ID, PCI_ANY_ID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(pci, b1pci_pci_tbl); +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 PCI card"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +static char *b1pci_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->revision : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +static int b1pci_probe(struct capicardparams *p, struct pci_dev *pdev) +{ + avmcard *card; + avmctrl_info *cinfo; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "b1pci: no memory.\n"); + retval = -ENOMEM; + goto err; + } + + cinfo = card->ctrlinfo; + sprintf(card->name, "b1pci-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->cardtype = avm_b1pci; + + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free; + } + b1_reset(card->port); + retval = b1_detect(card->port, card->cardtype); + if (retval) { + printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n", + card->port, retval); + retval = -ENODEV; + goto err_release_region; + } + b1_reset(card->port); + b1_getrevision(card); + + retval = request_irq(card->irq, b1_interrupt, SA_SHIRQ, card->name, card); + if (retval) { + printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq); + retval = -EBUSY; + goto err_release_region; + } + + cinfo->capi_ctrl.driver_name = "b1pci"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1_register_appl; + cinfo->capi_ctrl.release_appl = b1_release_appl; + cinfo->capi_ctrl.send_message = b1_send_message; + cinfo->capi_ctrl.load_firmware = b1_load_firmware; + cinfo->capi_ctrl.reset_ctr = b1_reset_ctr; + cinfo->capi_ctrl.procinfo = b1pci_procinfo; + cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc; + strcpy(cinfo->capi_ctrl.name, card->name); + cinfo->capi_ctrl.owner = THIS_MODULE; + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "b1pci: attach controller failed.\n"); + goto err_free_irq; + } + + if (card->revision >= 4) { + printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, revision %d (no dma)\n", + card->port, card->irq, card->revision); + } else { + printk(KERN_INFO "b1pci: AVM B1 PCI at i/o %#x, irq %d, revision %d\n", + card->port, card->irq, card->revision); + } + + pci_set_drvdata(pdev, card); + return 0; + + err_free_irq: + free_irq(card->irq, card); + err_release_region: + release_region(card->port, AVMB1_PORTLEN); + err_free: + b1_free_card(card); + err: + return retval; +} + +static void b1pci_remove(struct pci_dev *pdev) +{ + avmcard *card = pci_get_drvdata(pdev); + avmctrl_info *cinfo = card->ctrlinfo; + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + detach_capi_ctr(&cinfo->capi_ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + b1_free_card(card); +} + +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 +/* ------------------------------------------------------------- */ + +static char *b1pciv4_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx r%d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->membase : 0, + cinfo->card ? cinfo->card->revision : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +static int b1pciv4_probe(struct capicardparams *p, struct pci_dev *pdev) +{ + avmcard *card; + avmctrl_info *cinfo; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "b1pci: no memory.\n"); + retval = -ENOMEM; + goto err; + } + + card->dma = avmcard_dma_alloc("b1pci", pdev, 2048+128, 2048+128); + if (!card->dma) { + printk(KERN_WARNING "b1pci: dma alloc.\n"); + retval = -ENOMEM; + goto err_free; + } + + cinfo = card->ctrlinfo; + sprintf(card->name, "b1pciv4-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->membase = p->membase; + card->cardtype = avm_b1pci; + + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free_dma; + } + + card->mbase = ioremap(card->membase, 64); + if (!card->mbase) { + printk(KERN_NOTICE "b1pci: can't remap memory at 0x%lx\n", + card->membase); + retval = -ENOMEM; + goto err_release_region; + } + + b1dma_reset(card); + + retval = b1pciv4_detect(card); + if (retval) { + printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n", + card->port, retval); + retval = -ENODEV; + goto err_unmap; + } + b1dma_reset(card); + b1_getrevision(card); + + retval = request_irq(card->irq, b1dma_interrupt, SA_SHIRQ, card->name, card); + if (retval) { + printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", + card->irq); + retval = -EBUSY; + goto err_unmap; + } + + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "b1pciv4"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1dma_register_appl; + cinfo->capi_ctrl.release_appl = b1dma_release_appl; + cinfo->capi_ctrl.send_message = b1dma_send_message; + cinfo->capi_ctrl.load_firmware = b1dma_load_firmware; + cinfo->capi_ctrl.reset_ctr = b1dma_reset_ctr; + cinfo->capi_ctrl.procinfo = b1pciv4_procinfo; + cinfo->capi_ctrl.ctr_read_proc = b1dmactl_read_proc; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "b1pci: attach controller failed.\n"); + goto err_free_irq; + } + card->cardnr = cinfo->capi_ctrl.cnr; + + printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, mem %#lx, revision %d (dma)\n", + card->port, card->irq, card->membase, card->revision); + + pci_set_drvdata(pdev, card); + return 0; + + err_free_irq: + free_irq(card->irq, card); + err_unmap: + iounmap(card->mbase); + err_release_region: + release_region(card->port, AVMB1_PORTLEN); + err_free_dma: + avmcard_dma_free(card->dma); + err_free: + b1_free_card(card); + err: + return retval; + +} + +static void b1pciv4_remove(struct pci_dev *pdev) +{ + avmcard *card = pci_get_drvdata(pdev); + avmctrl_info *cinfo = card->ctrlinfo; + + b1dma_reset(card); + + detach_capi_ctr(&cinfo->capi_ctrl); + free_irq(card->irq, card); + iounmap(card->mbase); + release_region(card->port, AVMB1_PORTLEN); + avmcard_dma_free(card->dma); + b1_free_card(card); +} + +#endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */ + +static int __devinit b1pci_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct capicardparams param; + int retval; + + if (pci_enable_device(pdev) < 0) { + printk(KERN_ERR "b1pci: failed to enable AVM-B1\n"); + return -ENODEV; + } + param.irq = pdev->irq; + + if (pci_resource_start(pdev, 2)) { /* B1 PCI V4 */ +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + pci_set_master(pdev); +#endif + param.membase = pci_resource_start(pdev, 0); + param.port = pci_resource_start(pdev, 2); + + printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n", + param.port, param.irq, param.membase); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + retval = b1pciv4_probe(¶m, pdev); +#else + retval = b1pci_probe(¶m, pdev); +#endif + if (retval != 0) { + printk(KERN_ERR "b1pci: no AVM-B1 V4 at i/o %#x, irq %d, mem %#x detected\n", + param.port, param.irq, param.membase); + } + } else { + param.membase = 0; + param.port = pci_resource_start(pdev, 1); + + printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n", + param.port, param.irq); + retval = b1pci_probe(¶m, pdev); + if (retval != 0) { + printk(KERN_ERR "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n", + param.port, param.irq); + } + } + return retval; +} + +static void __devexit b1pci_pci_remove(struct pci_dev *pdev) +{ +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + avmcard *card = pci_get_drvdata(pdev); + + if (card->dma) + b1pciv4_remove(pdev); + else + b1pci_remove(pdev); +#else + b1pci_remove(pdev); +#endif +} + +static struct pci_driver b1pci_pci_driver = { + .name = "b1pci", + .id_table = b1pci_pci_tbl, + .probe = b1pci_pci_probe, + .remove = __devexit_p(b1pci_pci_remove), +}; + +static struct capi_driver capi_driver_b1pci = { + .name = "b1pci", + .revision = "1.0", +}; +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 +static struct capi_driver capi_driver_b1pciv4 = { + .name = "b1pciv4", + .revision = "1.0", +}; +#endif + +static int __init b1pci_init(void) +{ + char *p; + char rev[32]; + int err; + + if ((p = strchr(revision, ':')) != 0 && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != 0 && p > rev) + *(p-1) = 0; + } else + strcpy(rev, "1.0"); + + + err = pci_register_driver(&b1pci_pci_driver); + if (!err) { + strlcpy(capi_driver_b1pci.revision, rev, 32); + register_capi_driver(&capi_driver_b1pci); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + strlcpy(capi_driver_b1pciv4.revision, rev, 32); + register_capi_driver(&capi_driver_b1pciv4); +#endif + printk(KERN_INFO "b1pci: revision %s\n", rev); + } + return err; +} + +static void __exit b1pci_exit(void) +{ + unregister_capi_driver(&capi_driver_b1pci); +#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4 + unregister_capi_driver(&capi_driver_b1pciv4); +#endif + pci_unregister_driver(&b1pci_pci_driver); +} + +module_init(b1pci_init); +module_exit(b1pci_exit); diff --git a/drivers/isdn/hardware/avm/b1pcmcia.c b/drivers/isdn/hardware/avm/b1pcmcia.c new file mode 100644 index 0000000..9746cc5 --- /dev/null +++ b/drivers/isdn/hardware/avm/b1pcmcia.c @@ -0,0 +1,224 @@ +/* $Id: b1pcmcia.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ + * + * Module for AVM B1/M1/M2 PCMCIA-card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/capi.h> +#include <linux/b1pcmcia.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +/* ------------------------------------------------------------- */ + +static char *revision = "$Revision: 1.1.2.2 $"; + +/* ------------------------------------------------------------- */ + +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM PCMCIA cards"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + + b1_reset(port); + b1_reset(port); + + detach_capi_ctr(ctrl); + free_irq(card->irq, card); + b1_free_card(card); +} + +/* ------------------------------------------------------------- */ + +static LIST_HEAD(cards); + +static char *b1pcmcia_procinfo(struct capi_ctr *ctrl); + +static int b1pcmcia_add_card(unsigned int port, unsigned irq, + enum avmcardtype cardtype) +{ + avmctrl_info *cinfo; + avmcard *card; + char *cardname; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "b1pcmcia: no memory.\n"); + retval = -ENOMEM; + goto err; + } + cinfo = card->ctrlinfo; + + switch (cardtype) { + case avm_m1: sprintf(card->name, "m1-%x", port); break; + case avm_m2: sprintf(card->name, "m2-%x", port); break; + default: sprintf(card->name, "b1pcmcia-%x", port); break; + } + card->port = port; + card->irq = irq; + card->cardtype = cardtype; + + retval = request_irq(card->irq, b1_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n", + card->irq); + retval = -EBUSY; + goto err_free; + } + b1_reset(card->port); + if ((retval = b1_detect(card->port, card->cardtype)) != 0) { + printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n", + card->port, retval); + retval = -ENODEV; + goto err_free_irq; + } + b1_reset(card->port); + b1_getrevision(card); + + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "b1pcmcia"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1_register_appl; + cinfo->capi_ctrl.release_appl = b1_release_appl; + cinfo->capi_ctrl.send_message = b1_send_message; + cinfo->capi_ctrl.load_firmware = b1_load_firmware; + cinfo->capi_ctrl.reset_ctr = b1_reset_ctr; + cinfo->capi_ctrl.procinfo = b1pcmcia_procinfo; + cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "b1pcmcia: attach controller failed.\n"); + goto err_free_irq; + } + switch (cardtype) { + case avm_m1: cardname = "M1"; break; + case avm_m2: cardname = "M2"; break; + default : cardname = "B1 PCMCIA"; break; + } + + printk(KERN_INFO "b1pcmcia: AVM %s at i/o %#x, irq %d, revision %d\n", + cardname, card->port, card->irq, card->revision); + + list_add(&card->list, &cards); + return cinfo->capi_ctrl.cnr; + + err_free_irq: + free_irq(card->irq, card); + err_free: + b1_free_card(card); + err: + return retval; +} + +/* ------------------------------------------------------------- */ + +static char *b1pcmcia_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->revision : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +int b1pcmcia_addcard_b1(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(port, irq, avm_b1pcmcia); +} + +int b1pcmcia_addcard_m1(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(port, irq, avm_m1); +} + +int b1pcmcia_addcard_m2(unsigned int port, unsigned irq) +{ + return b1pcmcia_add_card(port, irq, avm_m2); +} + +int b1pcmcia_delcard(unsigned int port, unsigned irq) +{ + struct list_head *l; + avmcard *card; + + list_for_each(l, &cards) { + card = list_entry(l, avmcard, list); + if (card->port == port && card->irq == irq) { + b1pcmcia_remove_ctr(&card->ctrlinfo[0].capi_ctrl); + return 0; + } + } + return -ESRCH; +} + +EXPORT_SYMBOL(b1pcmcia_addcard_b1); +EXPORT_SYMBOL(b1pcmcia_addcard_m1); +EXPORT_SYMBOL(b1pcmcia_addcard_m2); +EXPORT_SYMBOL(b1pcmcia_delcard); + +static struct capi_driver capi_driver_b1pcmcia = { + .name = "b1pcmcia", + .revision = "1.0", +}; + +static int __init b1pcmcia_init(void) +{ + char *p; + char rev[32]; + + if ((p = strchr(revision, ':')) != 0 && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != 0 && p > rev) + *(p-1) = 0; + } else + strcpy(rev, "1.0"); + + strlcpy(capi_driver_b1pcmcia.revision, rev, 32); + register_capi_driver(&capi_driver_b1pcmcia); + printk(KERN_INFO "b1pci: revision %s\n", rev); + + return 0; +} + +static void __exit b1pcmcia_exit(void) +{ + unregister_capi_driver(&capi_driver_b1pcmcia); +} + +module_init(b1pcmcia_init); +module_exit(b1pcmcia_exit); diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c new file mode 100644 index 0000000..fa6b93b --- /dev/null +++ b/drivers/isdn/hardware/avm/c4.c @@ -0,0 +1,1310 @@ +/* $Id: c4.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ + * + * Module for AVM C4 & C2 card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/capi.h> +#include <linux/kernelcapi.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/netdevice.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +#undef CONFIG_C4_DEBUG +#undef CONFIG_C4_POLLDEBUG + +/* ------------------------------------------------------------- */ + +static char *revision = "$Revision: 1.1.2.2 $"; + +/* ------------------------------------------------------------- */ + +static int suppress_pollack; + +static struct pci_device_id c4_pci_tbl[] = { + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, 0, 0, (unsigned long)4 }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C2, 0, 0, (unsigned long)2 }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(pci, c4_pci_tbl); +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM C2/C4 cards"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); +MODULE_PARM(suppress_pollack, "0-1i"); + +/* ------------------------------------------------------------- */ + +static void c4_dispatch_tx(avmcard *card); + +/* ------------------------------------------------------------- */ + +#define DC21285_DRAM_A0MR 0x40000000 +#define DC21285_DRAM_A1MR 0x40004000 +#define DC21285_DRAM_A2MR 0x40008000 +#define DC21285_DRAM_A3MR 0x4000C000 + +#define CAS_OFFSET 0x88 + +#define DC21285_ARMCSR_BASE 0x42000000 + +#define PCI_OUT_INT_STATUS 0x30 +#define PCI_OUT_INT_MASK 0x34 +#define MAILBOX_0 0x50 +#define MAILBOX_1 0x54 +#define MAILBOX_2 0x58 +#define MAILBOX_3 0x5C +#define DOORBELL 0x60 +#define DOORBELL_SETUP 0x64 + +#define CHAN_1_CONTROL 0x90 +#define CHAN_2_CONTROL 0xB0 +#define DRAM_TIMING 0x10C +#define DRAM_ADDR_SIZE_0 0x110 +#define DRAM_ADDR_SIZE_1 0x114 +#define DRAM_ADDR_SIZE_2 0x118 +#define DRAM_ADDR_SIZE_3 0x11C +#define SA_CONTROL 0x13C +#define XBUS_CYCLE 0x148 +#define XBUS_STROBE 0x14C +#define DBELL_PCI_MASK 0x150 +#define DBELL_SA_MASK 0x154 + +#define SDRAM_SIZE 0x1000000 + +/* ------------------------------------------------------------- */ + +#define MBOX_PEEK_POKE MAILBOX_0 + +#define DBELL_ADDR 0x01 +#define DBELL_DATA 0x02 +#define DBELL_RNWR 0x40 +#define DBELL_INIT 0x80 + +/* ------------------------------------------------------------- */ + +#define MBOX_UP_ADDR MAILBOX_0 +#define MBOX_UP_LEN MAILBOX_1 +#define MBOX_DOWN_ADDR MAILBOX_2 +#define MBOX_DOWN_LEN MAILBOX_3 + +#define DBELL_UP_HOST 0x00000100 +#define DBELL_UP_ARM 0x00000200 +#define DBELL_DOWN_HOST 0x00000400 +#define DBELL_DOWN_ARM 0x00000800 +#define DBELL_RESET_HOST 0x40000000 +#define DBELL_RESET_ARM 0x80000000 + +/* ------------------------------------------------------------- */ + +#define DRAM_TIMING_DEF 0x001A01A5 +#define DRAM_AD_SZ_DEF0 0x00000045 +#define DRAM_AD_SZ_NULL 0x00000000 + +#define SA_CTL_ALLRIGHT 0x64AA0271 + +#define INIT_XBUS_CYCLE 0x100016DB +#define INIT_XBUS_STROBE 0xF1F1F1F1 + +/* ------------------------------------------------------------- */ + +#define RESET_TIMEOUT (15*HZ) /* 15 sec */ +#define PEEK_POKE_TIMEOUT (HZ/10) /* 0.1 sec */ + +/* ------------------------------------------------------------- */ + +#define c4outmeml(addr, value) writel(value, addr) +#define c4inmeml(addr) readl(addr) +#define c4outmemw(addr, value) writew(value, addr) +#define c4inmemw(addr) readw(addr) +#define c4outmemb(addr, value) writeb(value, addr) +#define c4inmemb(addr) readb(addr) + +/* ------------------------------------------------------------- */ + +static inline int wait_for_doorbell(avmcard *card, unsigned long t) +{ + unsigned long stop; + + stop = jiffies + t; + while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) { + if (!time_before(jiffies, stop)) + return -1; + mb(); + } + return 0; +} + +static int c4_poke(avmcard *card, unsigned long off, unsigned long value) +{ + + if (wait_for_doorbell(card, HZ/10) < 0) + return -1; + + c4outmeml(card->mbase+MBOX_PEEK_POKE, off); + c4outmeml(card->mbase+DOORBELL, DBELL_ADDR); + + if (wait_for_doorbell(card, HZ/10) < 0) + return -1; + + c4outmeml(card->mbase+MBOX_PEEK_POKE, value); + c4outmeml(card->mbase+DOORBELL, DBELL_DATA | DBELL_ADDR); + + return 0; +} + +static int c4_peek(avmcard *card, unsigned long off, unsigned long *valuep) +{ + if (wait_for_doorbell(card, HZ/10) < 0) + return -1; + + c4outmeml(card->mbase+MBOX_PEEK_POKE, off); + c4outmeml(card->mbase+DOORBELL, DBELL_RNWR | DBELL_ADDR); + + if (wait_for_doorbell(card, HZ/10) < 0) + return -1; + + *valuep = c4inmeml(card->mbase+MBOX_PEEK_POKE); + + return 0; +} + +/* ------------------------------------------------------------- */ + +static int c4_load_t4file(avmcard *card, capiloaddatapart * t4file) +{ + u32 val; + unsigned char *dp; + u_int left; + u32 loadoff = 0; + + dp = t4file->data; + left = t4file->len; + while (left >= sizeof(u32)) { + if (t4file->user) { + if (copy_from_user(&val, dp, sizeof(val))) + return -EFAULT; + } else { + memcpy(&val, dp, sizeof(val)); + } + if (c4_poke(card, loadoff, val)) { + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); + return -EIO; + } + left -= sizeof(u32); + dp += sizeof(u32); + loadoff += sizeof(u32); + } + if (left) { + val = 0; + if (t4file->user) { + if (copy_from_user(&val, dp, left)) + return -EFAULT; + } else { + memcpy(&val, dp, left); + } + if (c4_poke(card, loadoff, val)) { + printk(KERN_ERR "%s: corrupted firmware file ?\n", + card->name); + return -EIO; + } + } + return 0; +} + +/* ------------------------------------------------------------- */ + +static inline void _put_byte(void **pp, u8 val) +{ + u8 *s = *pp; + *s++ = val; + *pp = s; +} + +static inline void _put_word(void **pp, u32 val) +{ + u8 *s = *pp; + *s++ = val & 0xff; + *s++ = (val >> 8) & 0xff; + *s++ = (val >> 16) & 0xff; + *s++ = (val >> 24) & 0xff; + *pp = s; +} + +static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len) +{ + unsigned i = len; + _put_word(pp, i); + while (i-- > 0) + _put_byte(pp, *dp++); +} + +static inline u8 _get_byte(void **pp) +{ + u8 *s = *pp; + u8 val; + val = *s++; + *pp = s; + return val; +} + +static inline u32 _get_word(void **pp) +{ + u8 *s = *pp; + u32 val; + val = *s++; + val |= (*s++ << 8); + val |= (*s++ << 16); + val |= (*s++ << 24); + *pp = s; + return val; +} + +static inline u32 _get_slice(void **pp, unsigned char *dp) +{ + unsigned int len, i; + + len = i = _get_word(pp); + while (i-- > 0) *dp++ = _get_byte(pp); + return len; +} + +/* ------------------------------------------------------------- */ + +static void c4_reset(avmcard *card) +{ + unsigned long stop; + + c4outmeml(card->mbase+DOORBELL, DBELL_RESET_ARM); + + stop = jiffies + HZ*10; + while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) { + if (!time_before(jiffies, stop)) + return; + c4outmeml(card->mbase+DOORBELL, DBELL_ADDR); + mb(); + } + + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0); + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0); +} + +/* ------------------------------------------------------------- */ + +static int c4_detect(avmcard *card) +{ + unsigned long stop, dummy; + + c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c); + if (c4inmeml(card->mbase+PCI_OUT_INT_MASK) != 0x0c) + return 1; + + c4outmeml(card->mbase+DOORBELL, DBELL_RESET_ARM); + + stop = jiffies + HZ*10; + while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) { + if (!time_before(jiffies, stop)) + return 2; + c4outmeml(card->mbase+DOORBELL, DBELL_ADDR); + mb(); + } + + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0); + c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0); + + c4outmeml(card->mbase+MAILBOX_0, 0x55aa55aa); + if (c4inmeml(card->mbase+MAILBOX_0) != 0x55aa55aa) return 3; + + c4outmeml(card->mbase+MAILBOX_0, 0xaa55aa55); + if (c4inmeml(card->mbase+MAILBOX_0) != 0xaa55aa55) return 4; + + if (c4_poke(card, DC21285_ARMCSR_BASE+DBELL_SA_MASK, 0)) return 5; + if (c4_poke(card, DC21285_ARMCSR_BASE+DBELL_PCI_MASK, 0)) return 6; + if (c4_poke(card, DC21285_ARMCSR_BASE+SA_CONTROL, SA_CTL_ALLRIGHT)) + return 7; + if (c4_poke(card, DC21285_ARMCSR_BASE+XBUS_CYCLE, INIT_XBUS_CYCLE)) + return 8; + if (c4_poke(card, DC21285_ARMCSR_BASE+XBUS_STROBE, INIT_XBUS_STROBE)) + return 8; + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, 0)) return 9; + + mdelay(1); + + if (c4_peek(card, DC21285_DRAM_A0MR, &dummy)) return 10; + if (c4_peek(card, DC21285_DRAM_A1MR, &dummy)) return 11; + if (c4_peek(card, DC21285_DRAM_A2MR, &dummy)) return 12; + if (c4_peek(card, DC21285_DRAM_A3MR, &dummy)) return 13; + + if (c4_poke(card, DC21285_DRAM_A0MR+CAS_OFFSET, 0)) return 14; + if (c4_poke(card, DC21285_DRAM_A1MR+CAS_OFFSET, 0)) return 15; + if (c4_poke(card, DC21285_DRAM_A2MR+CAS_OFFSET, 0)) return 16; + if (c4_poke(card, DC21285_DRAM_A3MR+CAS_OFFSET, 0)) return 17; + + mdelay(1); + + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, DRAM_TIMING_DEF)) + return 18; + + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_0,DRAM_AD_SZ_DEF0)) + return 19; + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_1,DRAM_AD_SZ_NULL)) + return 20; + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_2,DRAM_AD_SZ_NULL)) + return 21; + if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_3,DRAM_AD_SZ_NULL)) + return 22; + + /* Transputer test */ + + if ( c4_poke(card, 0x000000, 0x11111111) + || c4_poke(card, 0x400000, 0x22222222) + || c4_poke(card, 0x800000, 0x33333333) + || c4_poke(card, 0xC00000, 0x44444444)) + return 23; + + if ( c4_peek(card, 0x000000, &dummy) || dummy != 0x11111111 + || c4_peek(card, 0x400000, &dummy) || dummy != 0x22222222 + || c4_peek(card, 0x800000, &dummy) || dummy != 0x33333333 + || c4_peek(card, 0xC00000, &dummy) || dummy != 0x44444444) + return 24; + + if ( c4_poke(card, 0x000000, 0x55555555) + || c4_poke(card, 0x400000, 0x66666666) + || c4_poke(card, 0x800000, 0x77777777) + || c4_poke(card, 0xC00000, 0x88888888)) + return 25; + + if ( c4_peek(card, 0x000000, &dummy) || dummy != 0x55555555 + || c4_peek(card, 0x400000, &dummy) || dummy != 0x66666666 + || c4_peek(card, 0x800000, &dummy) || dummy != 0x77777777 + || c4_peek(card, 0xC00000, &dummy) || dummy != 0x88888888) + return 26; + + return 0; +} + +/* ------------------------------------------------------------- */ + +static void c4_dispatch_tx(avmcard *card) +{ + avmcard_dmainfo *dma = card->dma; + struct sk_buff *skb; + u8 cmd, subcmd; + u16 len; + u32 txlen; + void *p; + + + if (card->csr & DBELL_DOWN_ARM) { /* tx busy */ + return; + } + + skb = skb_dequeue(&dma->send_queue); + if (!skb) { +#ifdef CONFIG_C4_DEBUG + printk(KERN_DEBUG "%s: tx underrun\n", card->name); +#endif + return; + } + + len = CAPIMSG_LEN(skb->data); + + if (len) { + cmd = CAPIMSG_COMMAND(skb->data); + subcmd = CAPIMSG_SUBCOMMAND(skb->data); + + p = dma->sendbuf.dmabuf; + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + u16 dlen = CAPIMSG_DATALEN(skb->data); + _put_byte(&p, SEND_DATA_B3_REQ); + _put_slice(&p, skb->data, len); + _put_slice(&p, skb->data + len, dlen); + } else { + _put_byte(&p, SEND_MESSAGE); + _put_slice(&p, skb->data, len); + } + txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf; +#ifdef CONFIG_C4_DEBUG + printk(KERN_DEBUG "%s: tx put msg len=%d\n", card->name, txlen); +#endif + } else { + txlen = skb->len-2; +#ifdef CONFIG_C4_POLLDEBUG + if (skb->data[2] == SEND_POLLACK) + printk(KERN_INFO "%s: ack to c4\n", card->name); +#endif +#ifdef CONFIG_C4_DEBUG + printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n", + card->name, skb->data[2], txlen); +#endif + memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2); + } + txlen = (txlen + 3) & ~3; + + c4outmeml(card->mbase+MBOX_DOWN_ADDR, dma->sendbuf.dmaaddr); + c4outmeml(card->mbase+MBOX_DOWN_LEN, txlen); + + card->csr |= DBELL_DOWN_ARM; + + c4outmeml(card->mbase+DOORBELL, DBELL_DOWN_ARM); + + dev_kfree_skb_any(skb); +} + +/* ------------------------------------------------------------- */ + +static void queue_pollack(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(3, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost poll ack\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_POLLACK); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); +} + +/* ------------------------------------------------------------- */ + +static void c4_handle_rx(avmcard *card) +{ + avmcard_dmainfo *dma = card->dma; + struct capi_ctr *ctrl; + avmctrl_info *cinfo; + struct sk_buff *skb; + void *p = dma->recvbuf.dmabuf; + u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize; + u8 b1cmd = _get_byte(&p); + u32 cidx; + + +#ifdef CONFIG_C4_DEBUG + printk(KERN_DEBUG "%s: rx 0x%x len=%lu\n", card->name, + b1cmd, (unsigned long)dma->recvlen); +#endif + + switch (b1cmd) { + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + DataB3Len = _get_slice(&p, card->databuf); + cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr; + if (cidx >= card->nlogcontr) cidx = 0; + ctrl = &card->ctrlinfo[cidx].capi_ctrl; + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf+MsgLen, 0, 30-MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr; + if (cidx >= card->nlogcontr) cidx = 0; + cinfo = &card->ctrlinfo[cidx]; + ctrl = &card->ctrlinfo[cidx].capi_ctrl; + + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) + capilib_data_b3_conf(&cinfo->ncci_head, ApplId, + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + WindowSize = _get_word(&p); + cidx = (NCCI&0x7f) - card->cardnr; + if (cidx >= card->nlogcontr) cidx = 0; + + capilib_new_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = _get_word(&p); + NCCI = _get_word(&p); + + if (NCCI != 0xffffffff) { + cidx = (NCCI&0x7f) - card->cardnr; + if (cidx >= card->nlogcontr) cidx = 0; + capilib_free_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI); + } + break; + + case RECEIVE_START: +#ifdef CONFIG_C4_POLLDEBUG + printk(KERN_INFO "%s: poll from c4\n", card->name); +#endif + if (!suppress_pollack) + queue_pollack(card); + for (cidx=0; cidx < card->nr_controllers; cidx++) { + ctrl = &card->ctrlinfo[cidx].capi_ctrl; + capi_ctr_resume_output(ctrl); + } + break; + + case RECEIVE_STOP: + for (cidx=0; cidx < card->nr_controllers; cidx++) { + ctrl = &card->ctrlinfo[cidx].capi_ctrl; + capi_ctr_suspend_output(ctrl); + } + break; + + case RECEIVE_INIT: + + cidx = card->nlogcontr; + if (cidx >= card->nr_controllers) { + printk(KERN_ERR "%s: card with %d controllers ??\n", + card->name, cidx+1); + break; + } + card->nlogcontr++; + cinfo = &card->ctrlinfo[cidx]; + ctrl = &cinfo->capi_ctrl; + cinfo->versionlen = _get_slice(&p, cinfo->versionbuf); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + capi_ctr_ready(&cinfo->capi_ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) _get_word(&p); + MsgLen = _get_slice(&p, card->msgbuf); + card->msgbuf[MsgLen] = 0; + while ( MsgLen > 0 + && ( card->msgbuf[MsgLen-1] == '\n' + || card->msgbuf[MsgLen-1] == '\r')) { + card->msgbuf[MsgLen-1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = _get_slice(&p, card->msgbuf); + card->msgbuf[MsgLen] = 0; + while ( MsgLen > 0 + && ( card->msgbuf[MsgLen-1] == '\n' + || card->msgbuf[MsgLen-1] == '\r')) { + card->msgbuf[MsgLen-1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + default: + printk(KERN_ERR "%s: c4_interrupt: 0x%x ???\n", + card->name, b1cmd); + return; + } +} + +/* ------------------------------------------------------------- */ + +static irqreturn_t c4_handle_interrupt(avmcard *card) +{ + unsigned long flags; + u32 status; + + spin_lock_irqsave(&card->lock, flags); + status = c4inmeml(card->mbase+DOORBELL); + + if (status & DBELL_RESET_HOST) { + u_int i; + c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c); + spin_unlock_irqrestore(&card->lock, flags); + if (card->nlogcontr == 0) + return IRQ_HANDLED; + printk(KERN_ERR "%s: unexpected reset\n", card->name); + for (i=0; i < card->nr_controllers; i++) { + avmctrl_info *cinfo = &card->ctrlinfo[i]; + memset(cinfo->version, 0, sizeof(cinfo->version)); + capilib_release(&cinfo->ncci_head); + capi_ctr_reseted(&cinfo->capi_ctrl); + } + card->nlogcontr = 0; + return IRQ_HANDLED; + } + + status &= (DBELL_UP_HOST | DBELL_DOWN_HOST); + if (!status) { + spin_unlock_irqrestore(&card->lock, flags); + return IRQ_HANDLED; + } + c4outmeml(card->mbase+DOORBELL, status); + + if ((status & DBELL_UP_HOST) != 0) { + card->dma->recvlen = c4inmeml(card->mbase+MBOX_UP_LEN); + c4outmeml(card->mbase+MBOX_UP_LEN, 0); + c4_handle_rx(card); + card->dma->recvlen = 0; + c4outmeml(card->mbase+MBOX_UP_LEN, card->dma->recvbuf.size); + c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM); + } + + if ((status & DBELL_DOWN_HOST) != 0) { + card->csr &= ~DBELL_DOWN_ARM; + c4_dispatch_tx(card); + } else if (card->csr & DBELL_DOWN_HOST) { + if (c4inmeml(card->mbase+MBOX_DOWN_LEN) == 0) { + card->csr &= ~DBELL_DOWN_ARM; + c4_dispatch_tx(card); + } + } + spin_unlock_irqrestore(&card->lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t c4_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card = devptr; + + return c4_handle_interrupt(card); +} + +/* ------------------------------------------------------------- */ + +static void c4_send_init(avmcard *card) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(15, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_INIT); + _put_word(&p, CAPI_MAXAPPL); + _put_word(&p, AVM_NCCI_PER_CHANNEL*30); + _put_word(&p, card->cardnr - 1); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); +} + +static int queue_sendconfigword(avmcard *card, u32 val) +{ + struct sk_buff *skb; + void *p; + + skb = alloc_skb(3+4, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, send config\n", + card->name); + return -ENOMEM; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_CONFIG); + _put_word(&p, val); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + c4_dispatch_tx(card); + return 0; +} + +static int queue_sendconfig(avmcard *card, char cval[4]) +{ + struct sk_buff *skb; + unsigned long flags; + void *p; + + skb = alloc_skb(3+4, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, send config\n", + card->name); + return -ENOMEM; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_CONFIG); + _put_byte(&p, cval[0]); + _put_byte(&p, cval[1]); + _put_byte(&p, cval[2]); + _put_byte(&p, cval[3]); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + + spin_lock_irqsave(&card->lock, flags); + c4_dispatch_tx(card); + spin_unlock_irqrestore(&card->lock, flags); + return 0; +} + +static int c4_send_config(avmcard *card, capiloaddatapart * config) +{ + u8 val[4]; + unsigned char *dp; + u_int left; + int retval; + + if ((retval = queue_sendconfigword(card, 1)) != 0) + return retval; + if ((retval = queue_sendconfigword(card, config->len)) != 0) + return retval; + + dp = config->data; + left = config->len; + while (left >= sizeof(u32)) { + if (config->user) { + if (copy_from_user(val, dp, sizeof(val))) + return -EFAULT; + } else { + memcpy(val, dp, sizeof(val)); + } + if ((retval = queue_sendconfig(card, val)) != 0) + return retval; + left -= sizeof(val); + dp += sizeof(val); + } + if (left) { + memset(val, 0, sizeof(val)); + if (config->user) { + if (copy_from_user(&val, dp, left)) + return -EFAULT; + } else { + memcpy(&val, dp, left); + } + if ((retval = queue_sendconfig(card, val)) != 0) + return retval; + } + + return 0; +} + +static int c4_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + int retval; + + if ((retval = c4_load_t4file(card, &data->firmware))) { + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + c4_reset(card); + return retval; + } + + card->csr = 0; + c4outmeml(card->mbase+MBOX_UP_LEN, 0); + c4outmeml(card->mbase+MBOX_DOWN_LEN, 0); + c4outmeml(card->mbase+DOORBELL, DBELL_INIT); + mdelay(1); + c4outmeml(card->mbase+DOORBELL, + DBELL_UP_HOST | DBELL_DOWN_HOST | DBELL_RESET_HOST); + + c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x08); + + card->dma->recvlen = 0; + c4outmeml(card->mbase+MBOX_UP_ADDR, card->dma->recvbuf.dmaaddr); + c4outmeml(card->mbase+MBOX_UP_LEN, card->dma->recvbuf.size); + c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM); + + if (data->configuration.len > 0 && data->configuration.data) { + retval = c4_send_config(card, &data->configuration); + if (retval) { + printk(KERN_ERR "%s: failed to set config!!\n", + card->name); + c4_reset(card); + return retval; + } + } + + c4_send_init(card); + + return 0; +} + + +void c4_reset_ctr(struct capi_ctr *ctrl) +{ + avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card; + avmctrl_info *cinfo; + u_int i; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + c4_reset(card); + + spin_unlock_irqrestore(&card->lock, flags); + + for (i=0; i < card->nr_controllers; i++) { + cinfo = &card->ctrlinfo[i]; + memset(cinfo->version, 0, sizeof(cinfo->version)); + capi_ctr_reseted(&cinfo->capi_ctrl); + } + card->nlogcontr = 0; +} + +static void c4_remove(struct pci_dev *pdev) +{ + avmcard *card = pci_get_drvdata(pdev); + avmctrl_info *cinfo; + u_int i; + + if (!card) + return; + + c4_reset(card); + + for (i=0; i < card->nr_controllers; i++) { + cinfo = &card->ctrlinfo[i]; + detach_capi_ctr(&cinfo->capi_ctrl); + } + + free_irq(card->irq, card); + iounmap(card->mbase); + release_region(card->port, AVMB1_PORTLEN); + avmcard_dma_free(card->dma); + pci_set_drvdata(pdev, NULL); + b1_free_card(card); +} + +/* ------------------------------------------------------------- */ + + +void c4_register_appl(struct capi_ctr *ctrl, + u16 appl, + capi_register_params *rp) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + struct sk_buff *skb; + int want = rp->level3cnt; + unsigned long flags; + int nconn; + void *p; + + if (ctrl->cnr == card->cardnr) { + + if (want > 0) nconn = want; + else nconn = ctrl->profile.nbchannel * 4 * -want; + if (nconn == 0) nconn = ctrl->profile.nbchannel * 4; + + skb = alloc_skb(23, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost register appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_REGISTER); + _put_word(&p, appl); + _put_word(&p, 1024 * (nconn+1)); + _put_word(&p, nconn); + _put_word(&p, rp->datablkcnt); + _put_word(&p, rp->datablklen); + skb_put(skb, (u8 *)p - (u8 *)skb->data); + + skb_queue_tail(&card->dma->send_queue, skb); + + spin_lock_irqsave(&card->lock, flags); + c4_dispatch_tx(card); + spin_unlock_irqrestore(&card->lock, flags); + } +} + +/* ------------------------------------------------------------- */ + +void c4_release_appl(struct capi_ctr *ctrl, u16 appl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned long flags; + struct sk_buff *skb; + void *p; + + capilib_release_appl(&cinfo->ncci_head, appl); + + if (ctrl->cnr == card->cardnr) { + skb = alloc_skb(7, GFP_ATOMIC); + if (!skb) { + printk(KERN_CRIT "%s: no memory, lost release appl.\n", + card->name); + return; + } + p = skb->data; + _put_byte(&p, 0); + _put_byte(&p, 0); + _put_byte(&p, SEND_RELEASE); + _put_word(&p, appl); + + skb_put(skb, (u8 *)p - (u8 *)skb->data); + skb_queue_tail(&card->dma->send_queue, skb); + spin_lock_irqsave(&card->lock, flags); + c4_dispatch_tx(card); + spin_unlock_irqrestore(&card->lock, flags); + } +} + +/* ------------------------------------------------------------- */ + + +static u16 c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + u16 retval = CAPI_NOERROR; + unsigned long flags; + + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { + retval = capilib_data_b3_req(&cinfo->ncci_head, + CAPIMSG_APPID(skb->data), + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + } + if (retval == CAPI_NOERROR) { + skb_queue_tail(&card->dma->send_queue, skb); + spin_lock_irqsave(&card->lock, flags); + c4_dispatch_tx(card); + spin_unlock_irqrestore(&card->lock, flags); + } + return retval; +} + +/* ------------------------------------------------------------- */ + +static char *c4_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->membase : 0 + ); + return cinfo->infobuf; +} + +static int c4_read_proc(char *page, char **start, off_t off, + int count, int *eof, struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + u8 flag; + int len = 0; + char *s; + + len += sprintf(page+len, "%-16s %s\n", "name", card->name); + len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port); + len += sprintf(page+len, "%-16s %d\n", "irq", card->irq); + len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase); + switch (card->cardtype) { + case avm_b1isa: s = "B1 ISA"; break; + case avm_b1pci: s = "B1 PCI"; break; + case avm_b1pcmcia: s = "B1 PCMCIA"; break; + case avm_m1: s = "M1"; break; + case avm_m2: s = "M2"; break; + case avm_t1isa: s = "T1 ISA (HEMA)"; break; + case avm_t1pci: s = "T1 PCI"; break; + case avm_c4: s = "C4"; break; + case avm_c2: s = "C2"; break; + default: s = "???"; break; + } + len += sprintf(page+len, "%-16s %s\n", "type", s); + if ((s = cinfo->version[VER_DRIVER]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_driver", s); + if ((s = cinfo->version[VER_CARDTYPE]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s); + if ((s = cinfo->version[VER_SERIAL]) != 0) + len += sprintf(page+len, "%-16s %s\n", "ver_serial", s); + + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[3]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n", + "protocol", + (flag & 0x01) ? " DSS1" : "", + (flag & 0x02) ? " CT1" : "", + (flag & 0x04) ? " VN3" : "", + (flag & 0x08) ? " NI1" : "", + (flag & 0x10) ? " AUSTEL" : "", + (flag & 0x20) ? " ESS" : "", + (flag & 0x40) ? " 1TR6" : "" + ); + } + if (card->cardtype != avm_m1) { + flag = ((u8 *)(ctrl->profile.manu))[5]; + if (flag) + len += sprintf(page+len, "%-16s%s%s%s%s\n", + "linetype", + (flag & 0x01) ? " point to point" : "", + (flag & 0x02) ? " point to multipoint" : "", + (flag & 0x08) ? " leased line without D-channel" : "", + (flag & 0x04) ? " leased line with D-channel" : "" + ); + } + len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname); + + if (off+count >= len) + *eof = 1; + if (len < off) + return 0; + *start = page + off; + return ((count < len-off) ? count : len-off); +} + +/* ------------------------------------------------------------- */ + +static int c4_add_card(struct capicardparams *p, struct pci_dev *dev, + int nr_controllers) +{ + avmcard *card; + avmctrl_info *cinfo; + int retval; + int i; + + card = b1_alloc_card(nr_controllers); + if (!card) { + printk(KERN_WARNING "c4: no memory.\n"); + retval = -ENOMEM; + goto err; + } + card->dma = avmcard_dma_alloc("c4", dev, 2048+128, 2048+128); + if (!card->dma) { + printk(KERN_WARNING "c4: no memory.\n"); + retval = -ENOMEM; + goto err_free; + } + + sprintf(card->name, "c%d-%x", nr_controllers, p->port); + card->port = p->port; + card->irq = p->irq; + card->membase = p->membase; + card->cardtype = (nr_controllers == 4) ? avm_c4 : avm_c2; + + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_WARNING "c4: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free_dma; + } + + card->mbase = ioremap(card->membase, 128); + if (card->mbase == 0) { + printk(KERN_NOTICE "c4: can't remap memory at 0x%lx\n", + card->membase); + retval = -EIO; + goto err_release_region; + } + + retval = c4_detect(card); + if (retval != 0) { + printk(KERN_NOTICE "c4: NO card at 0x%x error(%d)\n", + card->port, retval); + retval = -EIO; + goto err_unmap; + } + c4_reset(card); + + retval = request_irq(card->irq, c4_interrupt, SA_SHIRQ, card->name, card); + if (retval) { + printk(KERN_ERR "c4: unable to get IRQ %d.\n",card->irq); + retval = -EBUSY; + goto err_unmap; + } + + for (i=0; i < nr_controllers ; i++) { + cinfo = &card->ctrlinfo[i]; + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "c4"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = c4_register_appl; + cinfo->capi_ctrl.release_appl = c4_release_appl; + cinfo->capi_ctrl.send_message = c4_send_message; + cinfo->capi_ctrl.load_firmware = c4_load_firmware; + cinfo->capi_ctrl.reset_ctr = c4_reset_ctr; + cinfo->capi_ctrl.procinfo = c4_procinfo; + cinfo->capi_ctrl.ctr_read_proc = c4_read_proc; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "c4: attach controller failed (%d).\n", i); + for (i--; i >= 0; i--) { + cinfo = &card->ctrlinfo[i]; + detach_capi_ctr(&cinfo->capi_ctrl); + } + goto err_free_irq; + } + if (i == 0) + card->cardnr = cinfo->capi_ctrl.cnr; + } + + printk(KERN_INFO "c4: AVM C%d at i/o %#x, irq %d, mem %#lx\n", + nr_controllers, card->port, card->irq, + card->membase); + pci_set_drvdata(dev, card); + return 0; + + err_free_irq: + free_irq(card->irq, card); + err_unmap: + iounmap(card->mbase); + err_release_region: + release_region(card->port, AVMB1_PORTLEN); + err_free_dma: + avmcard_dma_free(card->dma); + err_free: + b1_free_card(card); + err: + return retval; +} + +/* ------------------------------------------------------------- */ + +static int __devinit c4_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + int nr = ent->driver_data; + int retval = 0; + struct capicardparams param; + + if (pci_enable_device(dev) < 0) { + printk(KERN_ERR "c4: failed to enable AVM-C%d\n", nr); + return -ENODEV; + } + pci_set_master(dev); + + param.port = pci_resource_start(dev, 1); + param.irq = dev->irq; + param.membase = pci_resource_start(dev, 0); + + printk(KERN_INFO "c4: PCI BIOS reports AVM-C%d at i/o %#x, irq %d, mem %#x\n", + nr, param.port, param.irq, param.membase); + + retval = c4_add_card(¶m, dev, nr); + if (retval != 0) { + printk(KERN_ERR "c4: no AVM-C%d at i/o %#x, irq %d detected, mem %#x\n", + nr, param.port, param.irq, param.membase); + return -ENODEV; + } + return 0; +} + +static struct pci_driver c4_pci_driver = { + .name = "c4", + .id_table = c4_pci_tbl, + .probe = c4_probe, + .remove = c4_remove, +}; + +static struct capi_driver capi_driver_c2 = { + .name = "c2", + .revision = "1.0", +}; + +static struct capi_driver capi_driver_c4 = { + .name = "c4", + .revision = "1.0", +}; + +static int __init c4_init(void) +{ + char *p; + char rev[32]; + int err; + + if ((p = strchr(revision, ':')) != 0 && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != 0 && p > rev) + *(p-1) = 0; + } else + strcpy(rev, "1.0"); + + err = pci_register_driver(&c4_pci_driver); + if (!err) { + strlcpy(capi_driver_c2.revision, rev, 32); + register_capi_driver(&capi_driver_c2); + strlcpy(capi_driver_c4.revision, rev, 32); + register_capi_driver(&capi_driver_c4); + printk(KERN_INFO "c4: revision %s\n", rev); + } + return err; +} + +static void __exit c4_exit(void) +{ + unregister_capi_driver(&capi_driver_c2); + unregister_capi_driver(&capi_driver_c4); + pci_unregister_driver(&c4_pci_driver); +} + +module_init(c4_init); +module_exit(c4_exit); diff --git a/drivers/isdn/hardware/avm/t1isa.c b/drivers/isdn/hardware/avm/t1isa.c new file mode 100644 index 0000000..cb9d9ce --- /dev/null +++ b/drivers/isdn/hardware/avm/t1isa.c @@ -0,0 +1,596 @@ +/* $Id: t1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $ + * + * Module for AVM T1 HEMA-card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/capi.h> +#include <linux/netdevice.h> +#include <linux/kernelcapi.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +/* ------------------------------------------------------------- */ + +static char *revision = "$Revision: 1.1.2.3 $"; + +/* ------------------------------------------------------------- */ + +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 HEMA ISA card"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +static int hema_irq_table[16] = +{0, + 0, + 0, + 0x80, /* irq 3 */ + 0, + 0x90, /* irq 5 */ + 0, + 0xA0, /* irq 7 */ + 0, + 0xB0, /* irq 9 */ + 0xC0, /* irq 10 */ + 0xD0, /* irq 11 */ + 0xE0, /* irq 12 */ + 0, + 0, + 0xF0, /* irq 15 */ +}; + +static int t1_detectandinit(unsigned int base, unsigned irq, int cardnr) +{ + unsigned char cregs[8]; + unsigned char reverse_cardnr; + unsigned char dummy; + int i; + + reverse_cardnr = ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1) + | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3); + cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf); + cregs[1] = 0x00; /* fast & slow link connected to CON1 */ + cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */ + cregs[3] = 0; + cregs[4] = 0x11; /* zero wait state */ + cregs[5] = hema_irq_table[irq & 0xf]; + cregs[6] = 0; + cregs[7] = 0; + + /* + * no one else should use the ISA bus in this moment, + * but no function there to prevent this :-( + * save_flags(flags); cli(); + */ + + /* board reset */ + t1outp(base, T1_RESETBOARD, 0xf); + mdelay(100); + dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */ + + /* write config */ + dummy = (base >> 4) & 0xff; + for (i=1;i<=0xf;i++) t1outp(base, i, dummy); + t1outp(base, HEMA_PAL_ID & 0xf, dummy); + t1outp(base, HEMA_PAL_ID >> 4, cregs[0]); + for(i=1;i<7;i++) t1outp(base, 0, cregs[i]); + t1outp(base, ((base >> 4)) & 0x3, cregs[7]); + /* restore_flags(flags); */ + + mdelay(100); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 0); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0); + mdelay(10); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 1); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1); + mdelay(100); + t1outp(base, T1_FASTLINK+T1_RESETLINK, 0); + t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0); + mdelay(10); + t1outp(base, T1_FASTLINK+T1_ANALYSE, 0); + mdelay(5); + t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0); + + if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */ + return 1; + if (t1inp(base, T1_FASTLINK+T1_INSTAT) != 0x0) /* rx empty */ + return 2; + if (t1inp(base, T1_FASTLINK+T1_IRQENABLE) != 0x0) + return 3; + if ((t1inp(base, T1_FASTLINK+T1_FIFOSTAT) & 0xf0) != 0x70) + return 4; + if ((t1inp(base, T1_FASTLINK+T1_IRQMASTER) & 0x0e) != 0) + return 5; + if ((t1inp(base, T1_FASTLINK+T1_IDENT) & 0x7d) != 1) + return 6; + if (t1inp(base, T1_SLOWLINK+T1_OUTSTAT) != 0x1) /* tx empty */ + return 7; + if ((t1inp(base, T1_SLOWLINK+T1_IRQMASTER) & 0x0e) != 0) + return 8; + if ((t1inp(base, T1_SLOWLINK+T1_IDENT) & 0x7d) != 0) + return 9; + return 0; +} + +static irqreturn_t t1isa_interrupt(int interrupt, void *devptr, struct pt_regs *regs) +{ + avmcard *card = devptr; + avmctrl_info *cinfo = &card->ctrlinfo[0]; + struct capi_ctr *ctrl = &cinfo->capi_ctrl; + unsigned char b1cmd; + struct sk_buff *skb; + + unsigned ApplId; + unsigned MsgLen; + unsigned DataB3Len; + unsigned NCCI; + unsigned WindowSize; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + + while (b1_rx_full(card->port)) { + + b1cmd = b1_get_byte(card->port); + + switch (b1cmd) { + + case RECEIVE_DATA_B3_IND: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + DataB3Len = t1_get_slice(card->port, card->databuf); + spin_unlock_irqrestore(&card->lock, flags); + + if (MsgLen < 30) { /* not CAPI 64Bit */ + memset(card->msgbuf+MsgLen, 0, 30-MsgLen); + MsgLen = 30; + CAPIMSG_SETLEN(card->msgbuf, 30); + } + if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len); + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_MESSAGE: + + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + spin_unlock_irqrestore(&card->lock, flags); + if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) { + printk(KERN_ERR "%s: incoming packet dropped\n", + card->name); + } else { + memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen); + if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3) + capilib_data_b3_conf(&cinfo->ncci_head, ApplId, + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + + capi_ctr_handle_message(ctrl, ApplId, skb); + } + break; + + case RECEIVE_NEW_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + WindowSize = b1_get_word(card->port); + spin_unlock_irqrestore(&card->lock, flags); + + capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize); + + break; + + case RECEIVE_FREE_NCCI: + + ApplId = b1_get_word(card->port); + NCCI = b1_get_word(card->port); + spin_unlock_irqrestore(&card->lock, flags); + + if (NCCI != 0xffffffff) + capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI); + + break; + + case RECEIVE_START: + b1_put_byte(card->port, SEND_POLLACK); + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_resume_output(ctrl); + break; + + case RECEIVE_STOP: + spin_unlock_irqrestore(&card->lock, flags); + capi_ctr_suspend_output(ctrl); + break; + + case RECEIVE_INIT: + + cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf); + spin_unlock_irqrestore(&card->lock, flags); + b1_parse_version(cinfo); + printk(KERN_INFO "%s: %s-card (%s) now active\n", + card->name, + cinfo->version[VER_CARDTYPE], + cinfo->version[VER_DRIVER]); + capi_ctr_ready(ctrl); + break; + + case RECEIVE_TASK_READY: + ApplId = (unsigned) b1_get_word(card->port); + MsgLen = t1_get_slice(card->port, card->msgbuf); + spin_unlock_irqrestore(&card->lock, flags); + card->msgbuf[MsgLen] = 0; + while ( MsgLen > 0 + && ( card->msgbuf[MsgLen-1] == '\n' + || card->msgbuf[MsgLen-1] == '\r')) { + card->msgbuf[MsgLen-1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: task %d \"%s\" ready.\n", + card->name, ApplId, card->msgbuf); + break; + + case RECEIVE_DEBUGMSG: + MsgLen = t1_get_slice(card->port, card->msgbuf); + spin_unlock_irqrestore(&card->lock, flags); + card->msgbuf[MsgLen] = 0; + while ( MsgLen > 0 + && ( card->msgbuf[MsgLen-1] == '\n' + || card->msgbuf[MsgLen-1] == '\r')) { + card->msgbuf[MsgLen-1] = 0; + MsgLen--; + } + printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf); + break; + + + case 0xff: + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "%s: card reseted ?\n", card->name); + return IRQ_HANDLED; + default: + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n", + card->name, b1cmd); + return IRQ_NONE; + } + } + return IRQ_HANDLED; +} + +/* ------------------------------------------------------------- */ + +static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + int retval; + + t1_disable_irq(port); + b1_reset(port); + + if ((retval = b1_load_t4file(card, &data->firmware))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load t4file!!\n", + card->name); + return retval; + } + + if (data->configuration.len > 0 && data->configuration.data) { + if ((retval = b1_load_config(card, &data->configuration))) { + b1_reset(port); + printk(KERN_ERR "%s: failed to load config!!\n", + card->name); + return retval; + } + } + + if (!b1_loaded(card)) { + printk(KERN_ERR "%s: failed to load t4file.\n", card->name); + return -EIO; + } + + spin_lock_irqsave(&card->lock, flags); + b1_setinterrupt(port, card->irq, card->cardtype); + b1_put_byte(port, SEND_INIT); + b1_put_word(port, CAPI_MAXAPPL); + b1_put_word(port, AVM_NCCI_PER_CHANNEL*30); + b1_put_word(port, ctrl->cnr - 1); + spin_unlock_irqrestore(&card->lock, flags); + + return 0; +} + +void t1isa_reset_ctr(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + + t1_disable_irq(port); + b1_reset(port); + b1_reset(port); + + memset(cinfo->version, 0, sizeof(cinfo->version)); + capilib_release(&cinfo->ncci_head); + capi_ctr_reseted(ctrl); +} + +static void t1isa_remove(struct pci_dev *pdev) +{ + avmctrl_info *cinfo = pci_get_drvdata(pdev); + avmcard *card; + + if (!cinfo) + return; + + card = cinfo->card; + + t1_disable_irq(card->port); + b1_reset(card->port); + b1_reset(card->port); + t1_reset(card->port); + + detach_capi_ctr(&cinfo->capi_ctrl); + free_irq(card->irq, card); + release_region(card->port, AVMB1_PORTLEN); + b1_free_card(card); +} + +/* ------------------------------------------------------------- */ + +static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb); +static char *t1isa_procinfo(struct capi_ctr *ctrl); + +static int t1isa_probe(struct pci_dev *pdev, int cardnr) +{ + avmctrl_info *cinfo; + avmcard *card; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "t1isa: no memory.\n"); + retval = -ENOMEM; + goto err; + } + + cinfo = card->ctrlinfo; + card->port = pci_resource_start(pdev, 0); + card->irq = pdev->irq; + card->cardtype = avm_t1isa; + card->cardnr = cardnr; + sprintf(card->name, "t1isa-%x", card->port); + + if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) { + printk(KERN_WARNING "t1isa: invalid port 0x%x.\n", card->port); + retval = -EINVAL; + goto err_free; + } + if (hema_irq_table[card->irq & 0xf] == 0) { + printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq); + retval = -EINVAL; + goto err_free; + } + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_INFO "t1isa: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free; + } + retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card); + if (retval) { + printk(KERN_INFO "t1isa: unable to get IRQ %d.\n", card->irq); + retval = -EBUSY; + goto err_release_region; + } + + if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) { + printk(KERN_INFO "t1isa: NO card at 0x%x (%d)\n", + card->port, retval); + retval = -ENODEV; + goto err_free_irq; + } + t1_disable_irq(card->port); + b1_reset(card->port); + + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "t1isa"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1_register_appl; + cinfo->capi_ctrl.release_appl = b1_release_appl; + cinfo->capi_ctrl.send_message = t1isa_send_message; + cinfo->capi_ctrl.load_firmware = t1isa_load_firmware; + cinfo->capi_ctrl.reset_ctr = t1isa_reset_ctr; + cinfo->capi_ctrl.procinfo = t1isa_procinfo; + cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_INFO "t1isa: attach controller failed.\n"); + goto err_free_irq; + } + + printk(KERN_INFO "t1isa: AVM T1 ISA at i/o %#x, irq %d, card %d\n", + card->port, card->irq, card->cardnr); + + pci_set_drvdata(pdev, cinfo); + return 0; + + err_free_irq: + free_irq(card->irq, card); + err_release_region: + release_region(card->port, AVMB1_PORTLEN); + err_free: + b1_free_card(card); + err: + return retval; +} + +static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + avmcard *card = cinfo->card; + unsigned int port = card->port; + unsigned long flags; + u16 len = CAPIMSG_LEN(skb->data); + u8 cmd = CAPIMSG_COMMAND(skb->data); + u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data); + u16 dlen, retval; + + if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) { + retval = capilib_data_b3_req(&cinfo->ncci_head, + CAPIMSG_APPID(skb->data), + CAPIMSG_NCCI(skb->data), + CAPIMSG_MSGID(skb->data)); + if (retval != CAPI_NOERROR) + return retval; + + dlen = CAPIMSG_DATALEN(skb->data); + + spin_lock_irqsave(&card->lock, flags); + b1_put_byte(port, SEND_DATA_B3_REQ); + t1_put_slice(port, skb->data, len); + t1_put_slice(port, skb->data + len, dlen); + spin_unlock_irqrestore(&card->lock, flags); + } else { + + spin_lock_irqsave(&card->lock, flags); + b1_put_byte(port, SEND_MESSAGE); + t1_put_slice(port, skb->data, len); + spin_unlock_irqrestore(&card->lock, flags); + } + + dev_kfree_skb_any(skb); + return CAPI_NOERROR; +} +/* ------------------------------------------------------------- */ + +static char *t1isa_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d %d", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->cardnr : 0 + ); + return cinfo->infobuf; +} + + +/* ------------------------------------------------------------- */ + +#define MAX_CARDS 4 +static struct pci_dev isa_dev[MAX_CARDS]; +static int io[MAX_CARDS]; +static int irq[MAX_CARDS]; +static int cardnr[MAX_CARDS]; + +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_CARDS) "i"); +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_CARDS) "i"); +MODULE_PARM(cardnr, "1-" __MODULE_STRING(MAX_CARDS) "i"); +MODULE_PARM_DESC(io, "I/O base address(es)"); +MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); +MODULE_PARM_DESC(cardnr, "Card number(s) (as jumpered)"); + +static int t1isa_add_card(struct capi_driver *driver, capicardparams *data) +{ + int i; + + for (i = 0; i < MAX_CARDS; i++) { + if (isa_dev[i].resource[0].start) + continue; + + isa_dev[i].resource[0].start = data->port; + isa_dev[i].irq = data->irq; + + if (t1isa_probe(&isa_dev[i], data->cardnr) == 0) + return 0; + } + return -ENODEV; +} + +static struct capi_driver capi_driver_t1isa = { + .name = "t1isa", + .revision = "1.0", + .add_card = t1isa_add_card, +}; + +static int __init t1isa_init(void) +{ + char rev[32]; + char *p; + int i; + + if ((p = strchr(revision, ':')) != 0 && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != 0 && p > rev) + *(p-1) = 0; + } else + strcpy(rev, "1.0"); + + for (i = 0; i < MAX_CARDS; i++) { + if (!io[i]) + break; + + isa_dev[i].resource[0].start = io[i]; + isa_dev[i].irq = irq[i]; + + if (t1isa_probe(&isa_dev[i], cardnr[i]) != 0) + return -ENODEV; + } + + strlcpy(capi_driver_t1isa.revision, rev, 32); + register_capi_driver(&capi_driver_t1isa); + printk(KERN_INFO "t1isa: revision %s\n", rev); + + return 0; +} + +static void __exit t1isa_exit(void) +{ + int i; + + for (i = 0; i < MAX_CARDS; i++) { + if (!io[i]) + break; + + t1isa_remove(&isa_dev[i]); + } +} + +module_init(t1isa_init); +module_exit(t1isa_exit); diff --git a/drivers/isdn/hardware/avm/t1pci.c b/drivers/isdn/hardware/avm/t1pci.c new file mode 100644 index 0000000..2ceec8e --- /dev/null +++ b/drivers/isdn/hardware/avm/t1pci.c @@ -0,0 +1,260 @@ +/* $Id: t1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $ + * + * Module for AVM T1 PCI-card. + * + * Copyright 1999 by Carsten Paeth <calle@calle.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/capi.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/isdn/capicmd.h> +#include <linux/isdn/capiutil.h> +#include <linux/isdn/capilli.h> +#include "avmcard.h" + +#undef CONFIG_T1PCI_DEBUG +#undef CONFIG_T1PCI_POLLDEBUG + +/* ------------------------------------------------------------- */ +static char *revision = "$Revision: 1.1.2.2 $"; +/* ------------------------------------------------------------- */ + +static struct pci_device_id t1pci_pci_tbl[] = { + { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_T1, PCI_ANY_ID, PCI_ANY_ID }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(pci, t1pci_pci_tbl); +MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 PCI card"); +MODULE_AUTHOR("Carsten Paeth"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------- */ + +static char *t1pci_procinfo(struct capi_ctr *ctrl); + +static int t1pci_add_card(struct capicardparams *p, struct pci_dev *pdev) +{ + avmcard *card; + avmctrl_info *cinfo; + int retval; + + card = b1_alloc_card(1); + if (!card) { + printk(KERN_WARNING "t1pci: no memory.\n"); + retval = -ENOMEM; + goto err; + } + + card->dma = avmcard_dma_alloc("t1pci", pdev, 2048+128, 2048+128); + if (!card->dma) { + printk(KERN_WARNING "t1pci: no memory.\n"); + retval = -ENOMEM; + goto err_free; + } + + cinfo = card->ctrlinfo; + sprintf(card->name, "t1pci-%x", p->port); + card->port = p->port; + card->irq = p->irq; + card->membase = p->membase; + card->cardtype = avm_t1pci; + + if (!request_region(card->port, AVMB1_PORTLEN, card->name)) { + printk(KERN_WARNING "t1pci: ports 0x%03x-0x%03x in use.\n", + card->port, card->port + AVMB1_PORTLEN); + retval = -EBUSY; + goto err_free_dma; + } + + card->mbase = ioremap(card->membase, 64); + if (!card->mbase) { + printk(KERN_NOTICE "t1pci: can't remap memory at 0x%lx\n", + card->membase); + retval = -EIO; + goto err_release_region; + } + + b1dma_reset(card); + + retval = t1pci_detect(card); + if (retval != 0) { + if (retval < 6) + printk(KERN_NOTICE "t1pci: NO card at 0x%x (%d)\n", + card->port, retval); + else + printk(KERN_NOTICE "t1pci: card at 0x%x, but cable not connected or T1 has no power (%d)\n", + card->port, retval); + retval = -EIO; + goto err_unmap; + } + b1dma_reset(card); + + retval = request_irq(card->irq, b1dma_interrupt, SA_SHIRQ, card->name, card); + if (retval) { + printk(KERN_ERR "t1pci: unable to get IRQ %d.\n", card->irq); + retval = -EBUSY; + goto err_unmap; + } + + cinfo->capi_ctrl.owner = THIS_MODULE; + cinfo->capi_ctrl.driver_name = "t1pci"; + cinfo->capi_ctrl.driverdata = cinfo; + cinfo->capi_ctrl.register_appl = b1dma_register_appl; + cinfo->capi_ctrl.release_appl = b1dma_release_appl; + cinfo->capi_ctrl.send_message = b1dma_send_message; + cinfo->capi_ctrl.load_firmware = b1dma_load_firmware; + cinfo->capi_ctrl.reset_ctr = b1dma_reset_ctr; + cinfo->capi_ctrl.procinfo = t1pci_procinfo; + cinfo->capi_ctrl.ctr_read_proc = b1dmactl_read_proc; + strcpy(cinfo->capi_ctrl.name, card->name); + + retval = attach_capi_ctr(&cinfo->capi_ctrl); + if (retval) { + printk(KERN_ERR "t1pci: attach controller failed.\n"); + retval = -EBUSY; + goto err_free_irq; + } + card->cardnr = cinfo->capi_ctrl.cnr; + + printk(KERN_INFO "t1pci: AVM T1 PCI at i/o %#x, irq %d, mem %#lx\n", + card->port, card->irq, card->membase); + + pci_set_drvdata(pdev, card); + return 0; + + err_free_irq: + free_irq(card->irq, card); + err_unmap: + iounmap(card->mbase); + err_release_region: + release_region(card->port, AVMB1_PORTLEN); + err_free_dma: + avmcard_dma_free(card->dma); + err_free: + b1_free_card(card); + err: + return retval; +} + +/* ------------------------------------------------------------- */ + +static void t1pci_remove(struct pci_dev *pdev) +{ + avmcard *card = pci_get_drvdata(pdev); + avmctrl_info *cinfo = card->ctrlinfo; + + b1dma_reset(card); + + detach_capi_ctr(&cinfo->capi_ctrl); + free_irq(card->irq, card); + iounmap(card->mbase); + release_region(card->port, AVMB1_PORTLEN); + avmcard_dma_free(card->dma); + b1_free_card(card); +} + +/* ------------------------------------------------------------- */ + +static char *t1pci_procinfo(struct capi_ctr *ctrl) +{ + avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata); + + if (!cinfo) + return ""; + sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx", + cinfo->cardname[0] ? cinfo->cardname : "-", + cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-", + cinfo->card ? cinfo->card->port : 0x0, + cinfo->card ? cinfo->card->irq : 0, + cinfo->card ? cinfo->card->membase : 0 + ); + return cinfo->infobuf; +} + +/* ------------------------------------------------------------- */ + +static int __devinit t1pci_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + struct capicardparams param; + int retval; + + if (pci_enable_device(dev) < 0) { + printk(KERN_ERR "t1pci: failed to enable AVM-T1-PCI\n"); + return -ENODEV; + } + pci_set_master(dev); + + param.port = pci_resource_start(dev, 1); + param.irq = dev->irq; + param.membase = pci_resource_start(dev, 0); + + printk(KERN_INFO "t1pci: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n", + param.port, param.irq, param.membase); + + retval = t1pci_add_card(¶m, dev); + if (retval != 0) { + printk(KERN_ERR "t1pci: no AVM-T1-PCI at i/o %#x, irq %d detected, mem %#x\n", + param.port, param.irq, param.membase); + return -ENODEV; + } + return 0; +} + +static struct pci_driver t1pci_pci_driver = { + .name = "t1pci", + .id_table = t1pci_pci_tbl, + .probe = t1pci_probe, + .remove = t1pci_remove, +}; + +static struct capi_driver capi_driver_t1pci = { + .name = "t1pci", + .revision = "1.0", +}; + +static int __init t1pci_init(void) +{ + char *p; + char rev[32]; + int err; + + if ((p = strchr(revision, ':')) != 0 && p[1]) { + strlcpy(rev, p + 2, 32); + if ((p = strchr(rev, '$')) != 0 && p > rev) + *(p-1) = 0; + } else + strcpy(rev, "1.0"); + + err = pci_register_driver(&t1pci_pci_driver); + if (!err) { + strlcpy(capi_driver_t1pci.revision, rev, 32); + register_capi_driver(&capi_driver_t1pci); + printk(KERN_INFO "t1pci: revision %s\n", rev); + } + return err; +} + +static void __exit t1pci_exit(void) +{ + unregister_capi_driver(&capi_driver_t1pci); + pci_unregister_driver(&t1pci_pci_driver); +} + +module_init(t1pci_init); +module_exit(t1pci_exit); |