summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/isdn/sc
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/isdn/sc')
-rw-r--r--kernel/drivers/isdn/sc/Kconfig8
-rw-r--r--kernel/drivers/isdn/sc/Makefile10
-rw-r--r--kernel/drivers/isdn/sc/card.h131
-rw-r--r--kernel/drivers/isdn/sc/command.c363
-rw-r--r--kernel/drivers/isdn/sc/event.c68
-rw-r--r--kernel/drivers/isdn/sc/hardware.h110
-rw-r--r--kernel/drivers/isdn/sc/includes.h16
-rw-r--r--kernel/drivers/isdn/sc/init.c549
-rw-r--r--kernel/drivers/isdn/sc/interrupt.c247
-rw-r--r--kernel/drivers/isdn/sc/ioctl.c582
-rw-r--r--kernel/drivers/isdn/sc/message.c230
-rw-r--r--kernel/drivers/isdn/sc/message.h245
-rw-r--r--kernel/drivers/isdn/sc/packet.c204
-rw-r--r--kernel/drivers/isdn/sc/scioc.h110
-rw-r--r--kernel/drivers/isdn/sc/shmem.c138
-rw-r--r--kernel/drivers/isdn/sc/timer.c122
16 files changed, 3133 insertions, 0 deletions
diff --git a/kernel/drivers/isdn/sc/Kconfig b/kernel/drivers/isdn/sc/Kconfig
new file mode 100644
index 000000000..7469863a7
--- /dev/null
+++ b/kernel/drivers/isdn/sc/Kconfig
@@ -0,0 +1,8 @@
+config ISDN_DRV_SC
+ tristate "Spellcaster support"
+ depends on ISA
+ help
+ This enables support for the Spellcaster BRI ISDN boards. This
+ driver currently builds only in a modularized version.
+ To build it, choose M here: the module will be called sc.
+ See <file:Documentation/isdn/README.sc> for more information.
diff --git a/kernel/drivers/isdn/sc/Makefile b/kernel/drivers/isdn/sc/Makefile
new file mode 100644
index 000000000..0f2b7d602
--- /dev/null
+++ b/kernel/drivers/isdn/sc/Makefile
@@ -0,0 +1,10 @@
+# Makefile for the sc ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_SC) += sc.o
+
+# Multipart objects.
+
+sc-y := shmem.o init.o packet.o command.o event.o \
+ ioctl.o interrupt.o message.o timer.o
diff --git a/kernel/drivers/isdn/sc/card.h b/kernel/drivers/isdn/sc/card.h
new file mode 100644
index 000000000..3da69ee43
--- /dev/null
+++ b/kernel/drivers/isdn/sc/card.h
@@ -0,0 +1,131 @@
+/* $Id: card.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Driver parameters for SpellCaster ISA ISDN adapters
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#ifndef CARD_H
+#define CARD_H
+
+/*
+ * We need these if they're not already included
+ */
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/isdnif.h>
+#include <linux/irqreturn.h>
+#include "message.h"
+#include "scioc.h"
+
+/*
+ * Amount of time to wait for a reset to complete
+ */
+#define CHECKRESET_TIME msecs_to_jiffies(4000)
+
+/*
+ * Amount of time between line status checks
+ */
+#define CHECKSTAT_TIME msecs_to_jiffies(8000)
+
+/*
+ * The maximum amount of time to wait for a message response
+ * to arrive. Use exclusively by send_and_receive
+ */
+#define SAR_TIMEOUT msecs_to_jiffies(10000)
+
+/*
+ * Macro to determine is a card id is valid
+ */
+#define IS_VALID_CARD(x) ((x >= 0) && (x <= cinst))
+
+/*
+ * Per channel status and configuration
+ */
+typedef struct {
+ int l2_proto;
+ int l3_proto;
+ char dn[50];
+ unsigned long first_sendbuf; /* Offset of first send buffer */
+ unsigned int num_sendbufs; /* Number of send buffers */
+ unsigned int free_sendbufs; /* Number of free sendbufs */
+ unsigned int next_sendbuf; /* Next sequential buffer */
+ char eazlist[50]; /* Set with SETEAZ */
+ char sillist[50]; /* Set with SETSIL */
+ int eazclear; /* Don't accept calls if TRUE */
+} bchan;
+
+/*
+ * Everything you want to know about the adapter ...
+ */
+typedef struct {
+ int model;
+ int driverId; /* LL Id */
+ char devicename[20]; /* The device name */
+ isdn_if *card; /* ISDN4Linux structure */
+ bchan *channel; /* status of the B channels */
+ char nChannels; /* Number of channels */
+ unsigned int interrupt; /* Interrupt number */
+ int iobase; /* I/O Base address */
+ int ioport[MAX_IO_REGS]; /* Index to I/O ports */
+ int shmem_pgport; /* port for the exp mem page reg. */
+ int shmem_magic; /* adapter magic number */
+ unsigned int rambase; /* Shared RAM base address */
+ unsigned int ramsize; /* Size of shared memory */
+ RspMessage async_msg; /* Async response message */
+ int want_async_messages; /* Snoop the Q ? */
+ unsigned char seq_no; /* Next send seq. number */
+ struct timer_list reset_timer; /* Check reset timer */
+ struct timer_list stat_timer; /* Check startproc timer */
+ unsigned char nphystat; /* Latest PhyStat info */
+ unsigned char phystat; /* Last PhyStat info */
+ HWConfig_pl hwconfig; /* Hardware config info */
+ char load_ver[11]; /* CommManage Version string */
+ char proc_ver[11]; /* CommEngine Version */
+ int StartOnReset; /* Indicates startproc after reset */
+ int EngineUp; /* Indicates CommEngine Up */
+ int trace_mode; /* Indicate if tracing is on */
+ spinlock_t lock; /* local lock */
+} board;
+
+
+extern board *sc_adapter[];
+extern int cinst;
+
+void memcpy_toshmem(int card, void *dest, const void *src, size_t n);
+void memcpy_fromshmem(int card, void *dest, const void *src, size_t n);
+int get_card_from_id(int driver);
+int indicate_status(int card, int event, ulong Channel, char *Data);
+irqreturn_t interrupt_handler(int interrupt, void *cardptr);
+int sndpkt(int devId, int channel, int ack, struct sk_buff *data);
+void rcvpkt(int card, RspMessage *rcvmsg);
+int command(isdn_ctrl *cmd);
+int reset(int card);
+int startproc(int card);
+int send_and_receive(int card, unsigned int procid, unsigned char type,
+ unsigned char class, unsigned char code,
+ unsigned char link, unsigned char data_len,
+ unsigned char *data, RspMessage *mesgdata, int timeout);
+void flushreadfifo(int card);
+int sendmessage(int card, unsigned int procid, unsigned int type,
+ unsigned int class, unsigned int code, unsigned int link,
+ unsigned int data_len, unsigned int *data);
+int receivemessage(int card, RspMessage *rspmsg);
+int sc_ioctl(int card, scs_ioctl *data);
+int setup_buffers(int card, int c);
+void sc_check_reset(unsigned long data);
+void check_phystat(unsigned long data);
+
+#endif /* CARD_H */
diff --git a/kernel/drivers/isdn/sc/command.c b/kernel/drivers/isdn/sc/command.c
new file mode 100644
index 000000000..4a4e66152
--- /dev/null
+++ b/kernel/drivers/isdn/sc/command.c
@@ -0,0 +1,363 @@
+/* $Id: command.c,v 1.4.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include <linux/module.h>
+#include "includes.h" /* This must be first */
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include "scioc.h"
+
+static int dial(int card, unsigned long channel, setup_parm setup);
+static int hangup(int card, unsigned long channel);
+static int answer(int card, unsigned long channel);
+static int clreaz(int card, unsigned long channel);
+static int seteaz(int card, unsigned long channel, char *);
+static int setl2(int card, unsigned long arg);
+static int setl3(int card, unsigned long arg);
+static int acceptb(int card, unsigned long channel);
+
+#ifdef DEBUG
+/*
+ * Translate command codes to strings
+ */
+static char *commands[] = { "ISDN_CMD_IOCTL",
+ "ISDN_CMD_DIAL",
+ "ISDN_CMD_ACCEPTB",
+ "ISDN_CMD_ACCEPTB",
+ "ISDN_CMD_HANGUP",
+ "ISDN_CMD_CLREAZ",
+ "ISDN_CMD_SETEAZ",
+ NULL,
+ NULL,
+ NULL,
+ "ISDN_CMD_SETL2",
+ NULL,
+ "ISDN_CMD_SETL3",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, };
+
+/*
+ * Translates ISDN4Linux protocol codes to strings for debug messages
+ */
+static char *l3protos[] = { "ISDN_PROTO_L3_TRANS" };
+static char *l2protos[] = { "ISDN_PROTO_L2_X75I",
+ "ISDN_PROTO_L2_X75UI",
+ "ISDN_PROTO_L2_X75BUI",
+ "ISDN_PROTO_L2_HDLC",
+ "ISDN_PROTO_L2_TRANS" };
+#endif
+
+int get_card_from_id(int driver)
+{
+ int i;
+
+ for (i = 0; i < cinst; i++) {
+ if (sc_adapter[i]->driverId == driver)
+ return i;
+ }
+ return -ENODEV;
+}
+
+/*
+ * command
+ */
+
+int command(isdn_ctrl *cmd)
+{
+ int card;
+
+ card = get_card_from_id(cmd->driver);
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ /*
+ * Dispatch the command
+ */
+ switch (cmd->command) {
+ case ISDN_CMD_IOCTL:
+ {
+ unsigned long cmdptr;
+ scs_ioctl ioc;
+
+ memcpy(&cmdptr, cmd->parm.num, sizeof(unsigned long));
+ if (copy_from_user(&ioc, (scs_ioctl __user *)cmdptr,
+ sizeof(scs_ioctl))) {
+ pr_debug("%s: Failed to verify user space 0x%lx\n",
+ sc_adapter[card]->devicename, cmdptr);
+ return -EFAULT;
+ }
+ return sc_ioctl(card, &ioc);
+ }
+ case ISDN_CMD_DIAL:
+ return dial(card, cmd->arg, cmd->parm.setup);
+ case ISDN_CMD_HANGUP:
+ return hangup(card, cmd->arg);
+ case ISDN_CMD_ACCEPTD:
+ return answer(card, cmd->arg);
+ case ISDN_CMD_ACCEPTB:
+ return acceptb(card, cmd->arg);
+ case ISDN_CMD_CLREAZ:
+ return clreaz(card, cmd->arg);
+ case ISDN_CMD_SETEAZ:
+ return seteaz(card, cmd->arg, cmd->parm.num);
+ case ISDN_CMD_SETL2:
+ return setl2(card, cmd->arg);
+ case ISDN_CMD_SETL3:
+ return setl3(card, cmd->arg);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * start the onboard firmware
+ */
+int startproc(int card)
+{
+ int status;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ /*
+ * send start msg
+ */
+ status = sendmessage(card, CMPID, cmReqType2,
+ cmReqClass0,
+ cmReqStartProc,
+ 0, 0, NULL);
+ pr_debug("%s: Sent startProc\n", sc_adapter[card]->devicename);
+
+ return status;
+}
+
+
+/*
+ * Dials the number passed in
+ */
+static int dial(int card, unsigned long channel, setup_parm setup)
+{
+ int status;
+ char Phone[48];
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ /*extract ISDN number to dial from eaz/msn string*/
+ strcpy(Phone, setup.phone);
+
+ /*send the connection message*/
+ status = sendmessage(card, CEPID, ceReqTypePhy,
+ ceReqClass1,
+ ceReqPhyConnect,
+ (unsigned char)channel + 1,
+ strlen(Phone),
+ (unsigned int *)Phone);
+
+ pr_debug("%s: Dialing %s on channel %lu\n",
+ sc_adapter[card]->devicename, Phone, channel + 1);
+
+ return status;
+}
+
+/*
+ * Answer an incoming call
+ */
+static int answer(int card, unsigned long channel)
+{
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ if (setup_buffers(card, channel + 1)) {
+ hangup(card, channel + 1);
+ return -ENOBUFS;
+ }
+
+ indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
+ pr_debug("%s: Answered incoming call on channel %lu\n",
+ sc_adapter[card]->devicename, channel + 1);
+ return 0;
+}
+
+/*
+ * Hangup up the call on specified channel
+ */
+static int hangup(int card, unsigned long channel)
+{
+ int status;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ status = sendmessage(card, CEPID, ceReqTypePhy,
+ ceReqClass1,
+ ceReqPhyDisconnect,
+ (unsigned char)channel + 1,
+ 0,
+ NULL);
+ pr_debug("%s: Sent HANGUP message to channel %lu\n",
+ sc_adapter[card]->devicename, channel + 1);
+ return status;
+}
+
+/*
+ * Set the layer 2 protocol (X.25, HDLC, Raw)
+ */
+static int setl2(int card, unsigned long arg)
+{
+ int status = 0;
+ int protocol, channel;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+ protocol = arg >> 8;
+ channel = arg & 0xff;
+ sc_adapter[card]->channel[channel].l2_proto = protocol;
+
+ /*
+ * check that the adapter is also set to the correct protocol
+ */
+ pr_debug("%s: Sending GetFrameFormat for channel %d\n",
+ sc_adapter[card]->devicename, channel + 1);
+ status = sendmessage(card, CEPID, ceReqTypeCall,
+ ceReqClass0,
+ ceReqCallGetFrameFormat,
+ (unsigned char)channel + 1,
+ 1,
+ (unsigned int *)protocol);
+ if (status)
+ return status;
+ return 0;
+}
+
+/*
+ * Set the layer 3 protocol
+ */
+static int setl3(int card, unsigned long channel)
+{
+ int protocol = channel >> 8;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ sc_adapter[card]->channel[channel].l3_proto = protocol;
+ return 0;
+}
+
+static int acceptb(int card, unsigned long channel)
+{
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ if (setup_buffers(card, channel + 1))
+ {
+ hangup(card, channel + 1);
+ return -ENOBUFS;
+ }
+
+ pr_debug("%s: B-Channel connection accepted on channel %lu\n",
+ sc_adapter[card]->devicename, channel + 1);
+ indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
+ return 0;
+}
+
+static int clreaz(int card, unsigned long arg)
+{
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ strcpy(sc_adapter[card]->channel[arg].eazlist, "");
+ sc_adapter[card]->channel[arg].eazclear = 1;
+ pr_debug("%s: EAZ List cleared for channel %lu\n",
+ sc_adapter[card]->devicename, arg + 1);
+ return 0;
+}
+
+static int seteaz(int card, unsigned long arg, char *num)
+{
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ strcpy(sc_adapter[card]->channel[arg].eazlist, num);
+ sc_adapter[card]->channel[arg].eazclear = 0;
+ pr_debug("%s: EAZ list for channel %lu set to: %s\n",
+ sc_adapter[card]->devicename, arg + 1,
+ sc_adapter[card]->channel[arg].eazlist);
+ return 0;
+}
+
+int reset(int card)
+{
+ unsigned long flags;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ indicate_status(card, ISDN_STAT_STOP, 0, NULL);
+
+ if (sc_adapter[card]->EngineUp) {
+ del_timer(&sc_adapter[card]->stat_timer);
+ }
+
+ sc_adapter[card]->EngineUp = 0;
+
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+ init_timer(&sc_adapter[card]->reset_timer);
+ sc_adapter[card]->reset_timer.function = sc_check_reset;
+ sc_adapter[card]->reset_timer.data = card;
+ sc_adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME;
+ add_timer(&sc_adapter[card]->reset_timer);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+
+ outb(0x1, sc_adapter[card]->ioport[SFT_RESET]);
+
+ pr_debug("%s: Adapter Reset\n", sc_adapter[card]->devicename);
+ return 0;
+}
+
+void flushreadfifo(int card)
+{
+ while (inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA)
+ inb(sc_adapter[card]->ioport[FIFO_READ]);
+}
diff --git a/kernel/drivers/isdn/sc/event.c b/kernel/drivers/isdn/sc/event.c
new file mode 100644
index 000000000..833d96c2c
--- /dev/null
+++ b/kernel/drivers/isdn/sc/event.c
@@ -0,0 +1,68 @@
+/* $Id: event.c,v 1.4.8.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+#ifdef DEBUG
+static char *events[] = { "ISDN_STAT_STAVAIL",
+ "ISDN_STAT_ICALL",
+ "ISDN_STAT_RUN",
+ "ISDN_STAT_STOP",
+ "ISDN_STAT_DCONN",
+ "ISDN_STAT_BCONN",
+ "ISDN_STAT_DHUP",
+ "ISDN_STAT_BHUP",
+ "ISDN_STAT_CINF",
+ "ISDN_STAT_LOAD",
+ "ISDN_STAT_UNLOAD",
+ "ISDN_STAT_BSENT",
+ "ISDN_STAT_NODCH",
+ "ISDN_STAT_ADDCH",
+ "ISDN_STAT_CAUSE" };
+#endif
+
+int indicate_status(int card, int event, ulong Channel, char *Data)
+{
+ isdn_ctrl cmd;
+
+#ifdef DEBUG
+ pr_debug("%s: Indicating event %s on Channel %d\n",
+ sc_adapter[card]->devicename, events[event - 256], Channel);
+#endif
+ if (Data != NULL) {
+ pr_debug("%s: Event data: %s\n", sc_adapter[card]->devicename,
+ Data);
+ switch (event) {
+ case ISDN_STAT_BSENT:
+ memcpy(&cmd.parm.length, Data, sizeof(cmd.parm.length));
+ break;
+ case ISDN_STAT_ICALL:
+ memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup));
+ break;
+ default:
+ strlcpy(cmd.parm.num, Data, sizeof(cmd.parm.num));
+ }
+ }
+
+ cmd.command = event;
+ cmd.driver = sc_adapter[card]->driverId;
+ cmd.arg = Channel;
+ return sc_adapter[card]->card->statcallb(&cmd);
+}
diff --git a/kernel/drivers/isdn/sc/hardware.h b/kernel/drivers/isdn/sc/hardware.h
new file mode 100644
index 000000000..81fbe7870
--- /dev/null
+++ b/kernel/drivers/isdn/sc/hardware.h
@@ -0,0 +1,110 @@
+/*
+ * Hardware specific macros, defines and structures
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef HARDWARE_H
+#define HARDWARE_H
+
+#include <asm/param.h> /* For HZ */
+
+/*
+ * General hardware parameters common to all ISA adapters
+ */
+
+#define MAX_CARDS 4 /* The maximum number of cards to
+ control or probe for. */
+
+#define SIGNATURE 0x87654321 /* Board reset signature */
+#define SIG_OFFSET 0x1004 /* Where to find signature in shared RAM */
+#define TRACE_OFFSET 0x1008 /* Trace enable word offset in shared RAM */
+#define BUFFER_OFFSET 0x1800 /* Beginning of buffers */
+
+/* I/O Port parameters */
+#define IOBASE_MIN 0x180 /* Lowest I/O port address */
+#define IOBASE_MAX 0x3C0 /* Highest I/O port address */
+#define IOBASE_OFFSET 0x20 /* Inter-board I/O port gap used during
+ probing */
+#define FIFORD_OFFSET 0x0
+#define FIFOWR_OFFSET 0x400
+#define FIFOSTAT_OFFSET 0x1000
+#define RESET_OFFSET 0x2800
+#define PG0_OFFSET 0x3000 /* Offset from I/O Base for Page 0 register */
+#define PG1_OFFSET 0x3400 /* Offset from I/O Base for Page 1 register */
+#define PG2_OFFSET 0x3800 /* Offset from I/O Base for Page 2 register */
+#define PG3_OFFSET 0x3C00 /* Offset from I/O Base for Page 3 register */
+
+#define FIFO_READ 0 /* FIFO Read register */
+#define FIFO_WRITE 1 /* FIFO Write rgister */
+#define LO_ADDR_PTR 2 /* Extended RAM Low Addr Pointer */
+#define HI_ADDR_PTR 3 /* Extended RAM High Addr Pointer */
+#define NOT_USED_1 4
+#define FIFO_STATUS 5 /* FIFO Status Register */
+#define NOT_USED_2 6
+#define MEM_OFFSET 7
+#define SFT_RESET 10 /* Reset Register */
+#define EXP_BASE 11 /* Shared RAM Base address */
+#define EXP_PAGE0 12 /* Shared RAM Page0 register */
+#define EXP_PAGE1 13 /* Shared RAM Page1 register */
+#define EXP_PAGE2 14 /* Shared RAM Page2 register */
+#define EXP_PAGE3 15 /* Shared RAM Page3 register */
+#define IRQ_SELECT 16 /* IRQ selection register */
+#define MAX_IO_REGS 17 /* Total number of I/O ports */
+
+/* FIFO register values */
+#define RF_HAS_DATA 0x01 /* fifo has data */
+#define RF_QUART_FULL 0x02 /* fifo quarter full */
+#define RF_HALF_FULL 0x04 /* fifo half full */
+#define RF_NOT_FULL 0x08 /* fifo not full */
+#define WF_HAS_DATA 0x10 /* fifo has data */
+#define WF_QUART_FULL 0x20 /* fifo quarter full */
+#define WF_HALF_FULL 0x40 /* fifo half full */
+#define WF_NOT_FULL 0x80 /* fifo not full */
+
+/* Shared RAM parameters */
+#define SRAM_MIN 0xC0000 /* Lowest host shared RAM address */
+#define SRAM_MAX 0xEFFFF /* Highest host shared RAM address */
+#define SRAM_PAGESIZE 0x4000 /* Size of one RAM page (16K) */
+
+/* Shared RAM buffer parameters */
+#define BUFFER_SIZE 0x800 /* The size of a buffer in bytes */
+#define BUFFER_BASE BUFFER_OFFSET /* Offset from start of shared RAM
+ where buffer start */
+#define BUFFERS_MAX 16 /* Maximum number of send/receive
+ buffers per channel */
+#define HDLC_PROTO 0x01 /* Frame Format for Layer 2 */
+
+#define BRI_BOARD 0
+#define POTS_BOARD 1
+#define PRI_BOARD 2
+
+/*
+ * Specific hardware parameters for the DataCommute/BRI
+ */
+#define BRI_CHANNELS 2 /* Number of B channels */
+#define BRI_BASEPG_VAL 0x98
+#define BRI_MAGIC 0x60000 /* Magic Number */
+#define BRI_MEMSIZE 0x10000 /* Amount of RAM (64K) */
+#define BRI_PARTNO "72-029"
+#define BRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
+/*
+ * Specific hardware parameters for the DataCommute/PRI
+ */
+#define PRI_CHANNELS 23 /* Number of B channels */
+#define PRI_BASEPG_VAL 0x88
+#define PRI_MAGIC 0x20000 /* Magic Number */
+#define PRI_MEMSIZE 0x100000 /* Amount of RAM (1M) */
+#define PRI_PARTNO "72-030"
+#define PRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
+
+/*
+ * Some handy macros
+ */
+
+/* Determine if a channel number is valid for the adapter */
+#define IS_VALID_CHANNEL(y, x) ((x > 0) && (x <= sc_adapter[y]->channels))
+
+#endif
diff --git a/kernel/drivers/isdn/sc/includes.h b/kernel/drivers/isdn/sc/includes.h
new file mode 100644
index 000000000..4766e5b77
--- /dev/null
+++ b/kernel/drivers/isdn/sc/includes.h
@@ -0,0 +1,16 @@
+/*
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
diff --git a/kernel/drivers/isdn/sc/init.c b/kernel/drivers/isdn/sc/init.c
new file mode 100644
index 000000000..3597ef47b
--- /dev/null
+++ b/kernel/drivers/isdn/sc/init.c
@@ -0,0 +1,549 @@
+/*
+ * 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/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include "includes.h"
+#include "hardware.h"
+#include "card.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for Spellcaster card");
+MODULE_AUTHOR("Spellcaster Telecommunications Inc.");
+MODULE_LICENSE("GPL");
+
+board *sc_adapter[MAX_CARDS];
+int cinst;
+
+static char devname[] = "scX";
+static const char version[] = "2.0b1";
+
+static const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" };
+
+/* insmod set parameters */
+static unsigned int io[] = {0, 0, 0, 0};
+static unsigned char irq[] = {0, 0, 0, 0};
+static unsigned long ram[] = {0, 0, 0, 0};
+static bool do_reset;
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, byte, NULL, 0);
+module_param_array(ram, long, NULL, 0);
+module_param(do_reset, bool, 0);
+
+static int identify_board(unsigned long, unsigned int);
+
+static int __init sc_init(void)
+{
+ int b = -1;
+ int i, j;
+ int status = -ENODEV;
+
+ unsigned long memsize = 0;
+ unsigned long features = 0;
+ isdn_if *interface;
+ unsigned char channels;
+ unsigned char pgport;
+ unsigned long magic;
+ int model;
+ int last_base = IOBASE_MIN;
+ int probe_exhasted = 0;
+
+#ifdef MODULE
+ pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version);
+#else
+ pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version);
+#endif
+ pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n");
+
+ while (b++ < MAX_CARDS - 1) {
+ pr_debug("Probing for adapter #%d\n", b);
+ /*
+ * Initialize reusable variables
+ */
+ model = -1;
+ magic = 0;
+ channels = 0;
+ pgport = 0;
+
+ /*
+ * See if we should probe for IO base
+ */
+ pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b],
+ io[b] == 0 ? "will" : "won't");
+ if (io[b]) {
+ /*
+ * No, I/O Base has been provided
+ */
+ for (i = 0; i < MAX_IO_REGS - 1; i++) {
+ if (!request_region(io[b] + i * 0x400, 1, "sc test")) {
+ pr_debug("request_region for 0x%x failed\n", io[b] + i * 0x400);
+ io[b] = 0;
+ break;
+ } else
+ release_region(io[b] + i * 0x400, 1);
+ }
+
+ /*
+ * Confirm the I/O Address with a test
+ */
+ if (io[b] == 0) {
+ pr_debug("I/O Address invalid.\n");
+ continue;
+ }
+
+ outb(0x18, io[b] + 0x400 * EXP_PAGE0);
+ if (inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
+ pr_debug("I/O Base 0x%x fails test\n",
+ io[b] + 0x400 * EXP_PAGE0);
+ continue;
+ }
+ } else {
+ /*
+ * Yes, probe for I/O Base
+ */
+ if (probe_exhasted) {
+ pr_debug("All probe addresses exhausted, skipping\n");
+ continue;
+ }
+ pr_debug("Probing for I/O...\n");
+ for (i = last_base; i <= IOBASE_MAX; i += IOBASE_OFFSET) {
+ int found_io = 1;
+ if (i == IOBASE_MAX) {
+ probe_exhasted = 1; /* No more addresses to probe */
+ pr_debug("End of Probes\n");
+ }
+ last_base = i + IOBASE_OFFSET;
+ pr_debug(" checking 0x%x...", i);
+ for (j = 0; j < MAX_IO_REGS - 1; j++) {
+ if (!request_region(i + j * 0x400, 1, "sc test")) {
+ pr_debug("Failed\n");
+ found_io = 0;
+ break;
+ } else
+ release_region(i + j * 0x400, 1);
+ }
+
+ if (found_io) {
+ io[b] = i;
+ outb(0x18, io[b] + 0x400 * EXP_PAGE0);
+ if (inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
+ pr_debug("Failed by test\n");
+ continue;
+ }
+ pr_debug("Passed\n");
+ break;
+ }
+ }
+ if (probe_exhasted) {
+ continue;
+ }
+ }
+
+ /*
+ * See if we should probe for shared RAM
+ */
+ if (do_reset) {
+ pr_debug("Doing a SAFE probe reset\n");
+ outb(0xFF, io[b] + RESET_OFFSET);
+ msleep_interruptible(10000);
+ }
+ pr_debug("RAM Base for board %d is 0x%lx, %s probe\n", b,
+ ram[b], ram[b] == 0 ? "will" : "won't");
+
+ if (ram[b]) {
+ /*
+ * No, the RAM base has been provided
+ * Just look for a signature and ID the
+ * board model
+ */
+ if (request_region(ram[b], SRAM_PAGESIZE, "sc test")) {
+ pr_debug("request_region for RAM base 0x%lx succeeded\n", ram[b]);
+ model = identify_board(ram[b], io[b]);
+ release_region(ram[b], SRAM_PAGESIZE);
+ }
+ } else {
+ /*
+ * Yes, probe for free RAM and look for
+ * a signature and id the board model
+ */
+ for (i = SRAM_MIN; i < SRAM_MAX; i += SRAM_PAGESIZE) {
+ pr_debug("Checking RAM address 0x%x...\n", i);
+ if (request_region(i, SRAM_PAGESIZE, "sc test")) {
+ pr_debug(" request_region succeeded\n");
+ model = identify_board(i, io[b]);
+ release_region(i, SRAM_PAGESIZE);
+ if (model >= 0) {
+ pr_debug(" Identified a %s\n",
+ boardname[model]);
+ ram[b] = i;
+ break;
+ }
+ pr_debug(" Unidentified or inaccessible\n");
+ continue;
+ }
+ pr_debug(" request failed\n");
+ }
+ }
+ /*
+ * See if we found free RAM and the board model
+ */
+ if (!ram[b] || model < 0) {
+ /*
+ * Nope, there was no place in RAM for the
+ * board, or it couldn't be identified
+ */
+ pr_debug("Failed to find an adapter at 0x%lx\n", ram[b]);
+ continue;
+ }
+
+ /*
+ * Set the board's magic number, memory size and page register
+ */
+ switch (model) {
+ case PRI_BOARD:
+ channels = 23;
+ magic = 0x20000;
+ memsize = 0x100000;
+ features = PRI_FEATURES;
+ break;
+
+ case BRI_BOARD:
+ case POTS_BOARD:
+ channels = 2;
+ magic = 0x60000;
+ memsize = 0x10000;
+ features = BRI_FEATURES;
+ break;
+ }
+ switch (ram[b] >> 12 & 0x0F) {
+ case 0x0:
+ pr_debug("RAM Page register set to EXP_PAGE0\n");
+ pgport = EXP_PAGE0;
+ break;
+
+ case 0x4:
+ pr_debug("RAM Page register set to EXP_PAGE1\n");
+ pgport = EXP_PAGE1;
+ break;
+
+ case 0x8:
+ pr_debug("RAM Page register set to EXP_PAGE2\n");
+ pgport = EXP_PAGE2;
+ break;
+
+ case 0xC:
+ pr_debug("RAM Page register set to EXP_PAGE3\n");
+ pgport = EXP_PAGE3;
+ break;
+
+ default:
+ pr_debug("RAM base address doesn't fall on 16K boundary\n");
+ continue;
+ }
+
+ pr_debug("current IRQ: %d b: %d\n", irq[b], b);
+
+ /*
+ * Make sure we got an IRQ
+ */
+ if (!irq[b]) {
+ /*
+ * No interrupt could be used
+ */
+ pr_debug("Failed to acquire an IRQ line\n");
+ continue;
+ }
+
+ /*
+ * Horray! We found a board, Make sure we can register
+ * it with ISDN4Linux
+ */
+ interface = kzalloc(sizeof(isdn_if), GFP_KERNEL);
+ if (interface == NULL) {
+ /*
+ * Oops, can't malloc isdn_if
+ */
+ continue;
+ }
+
+ interface->owner = THIS_MODULE;
+ interface->hl_hdrlen = 0;
+ interface->channels = channels;
+ interface->maxbufsize = BUFFER_SIZE;
+ interface->features = features;
+ interface->writebuf_skb = sndpkt;
+ interface->writecmd = NULL;
+ interface->command = command;
+ strcpy(interface->id, devname);
+ interface->id[2] = '0' + cinst;
+
+ /*
+ * Allocate the board structure
+ */
+ sc_adapter[cinst] = kzalloc(sizeof(board), GFP_KERNEL);
+ if (sc_adapter[cinst] == NULL) {
+ /*
+ * Oops, can't alloc memory for the board
+ */
+ kfree(interface);
+ continue;
+ }
+ spin_lock_init(&sc_adapter[cinst]->lock);
+
+ if (!register_isdn(interface)) {
+ /*
+ * Oops, couldn't register for some reason
+ */
+ kfree(interface);
+ kfree(sc_adapter[cinst]);
+ continue;
+ }
+
+ sc_adapter[cinst]->card = interface;
+ sc_adapter[cinst]->driverId = interface->channels;
+ strcpy(sc_adapter[cinst]->devicename, interface->id);
+ sc_adapter[cinst]->nChannels = channels;
+ sc_adapter[cinst]->ramsize = memsize;
+ sc_adapter[cinst]->shmem_magic = magic;
+ sc_adapter[cinst]->shmem_pgport = pgport;
+ sc_adapter[cinst]->StartOnReset = 1;
+
+ /*
+ * Allocate channels status structures
+ */
+ sc_adapter[cinst]->channel = kzalloc(sizeof(bchan) * channels, GFP_KERNEL);
+ if (sc_adapter[cinst]->channel == NULL) {
+ /*
+ * Oops, can't alloc memory for the channels
+ */
+ indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */
+ kfree(interface);
+ kfree(sc_adapter[cinst]);
+ continue;
+ }
+
+ /*
+ * Lock down the hardware resources
+ */
+ sc_adapter[cinst]->interrupt = irq[b];
+ if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler,
+ 0, interface->id,
+ (void *)(unsigned long) cinst)) {
+ kfree(sc_adapter[cinst]->channel);
+ indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */
+ kfree(interface);
+ kfree(sc_adapter[cinst]);
+ continue;
+
+ }
+ sc_adapter[cinst]->iobase = io[b];
+ for (i = 0; i < MAX_IO_REGS - 1; i++) {
+ sc_adapter[cinst]->ioport[i] = io[b] + i * 0x400;
+ request_region(sc_adapter[cinst]->ioport[i], 1,
+ interface->id);
+ pr_debug("Requesting I/O Port %#x\n",
+ sc_adapter[cinst]->ioport[i]);
+ }
+ sc_adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2;
+ request_region(sc_adapter[cinst]->ioport[IRQ_SELECT], 1,
+ interface->id);
+ pr_debug("Requesting I/O Port %#x\n",
+ sc_adapter[cinst]->ioport[IRQ_SELECT]);
+ sc_adapter[cinst]->rambase = ram[b];
+ request_region(sc_adapter[cinst]->rambase, SRAM_PAGESIZE,
+ interface->id);
+
+ pr_info(" %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n",
+ sc_adapter[cinst]->devicename,
+ sc_adapter[cinst]->driverId,
+ boardname[model], channels, irq[b], io[b], ram[b]);
+
+ /*
+ * reset the adapter to put things in motion
+ */
+ reset(cinst);
+
+ cinst++;
+ status = 0;
+ }
+ if (status)
+ pr_info("Failed to find any adapters, driver unloaded\n");
+ return status;
+}
+
+static void __exit sc_exit(void)
+{
+ int i, j;
+
+ for (i = 0; i < cinst; i++) {
+ pr_debug("Cleaning up after adapter %d\n", i);
+ /*
+ * kill the timers
+ */
+ del_timer_sync(&(sc_adapter[i]->reset_timer));
+ del_timer_sync(&(sc_adapter[i]->stat_timer));
+
+ /*
+ * Tell I4L we're toast
+ */
+ indicate_status(i, ISDN_STAT_STOP, 0, NULL);
+ indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL);
+
+ /*
+ * Release shared RAM
+ */
+ release_region(sc_adapter[i]->rambase, SRAM_PAGESIZE);
+
+ /*
+ * Release the IRQ
+ */
+ free_irq(sc_adapter[i]->interrupt, NULL);
+
+ /*
+ * Reset for a clean start
+ */
+ outb(0xFF, sc_adapter[i]->ioport[SFT_RESET]);
+
+ /*
+ * Release the I/O Port regions
+ */
+ for (j = 0; j < MAX_IO_REGS - 1; j++) {
+ release_region(sc_adapter[i]->ioport[j], 1);
+ pr_debug("Releasing I/O Port %#x\n",
+ sc_adapter[i]->ioport[j]);
+ }
+ release_region(sc_adapter[i]->ioport[IRQ_SELECT], 1);
+ pr_debug("Releasing I/O Port %#x\n",
+ sc_adapter[i]->ioport[IRQ_SELECT]);
+
+ /*
+ * Release any memory we alloced
+ */
+ kfree(sc_adapter[i]->channel);
+ kfree(sc_adapter[i]->card);
+ kfree(sc_adapter[i]);
+ }
+ pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n");
+}
+
+static int identify_board(unsigned long rambase, unsigned int iobase)
+{
+ unsigned int pgport;
+ unsigned long sig;
+ DualPortMemory *dpm;
+ RspMessage rcvmsg;
+ ReqMessage sndmsg;
+ HWConfig_pl hwci;
+ int x;
+
+ pr_debug("Attempting to identify adapter @ 0x%lx io 0x%x\n",
+ rambase, iobase);
+
+ /*
+ * Enable the base pointer
+ */
+ outb(rambase >> 12, iobase + 0x2c00);
+
+ switch (rambase >> 12 & 0x0F) {
+ case 0x0:
+ pgport = iobase + PG0_OFFSET;
+ pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET);
+ break;
+
+ case 0x4:
+ pgport = iobase + PG1_OFFSET;
+ pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET);
+ break;
+
+ case 0x8:
+ pgport = iobase + PG2_OFFSET;
+ pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET);
+ break;
+
+ case 0xC:
+ pgport = iobase + PG3_OFFSET;
+ pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET);
+ break;
+ default:
+ pr_debug("Invalid rambase 0x%lx\n", rambase);
+ return -1;
+ }
+
+ /*
+ * Try to identify a PRI card
+ */
+ outb(PRI_BASEPG_VAL, pgport);
+ msleep_interruptible(1000);
+ sig = readl(rambase + SIG_OFFSET);
+ pr_debug("Looking for a signature, got 0x%lx\n", sig);
+ if (sig == SIGNATURE)
+ return PRI_BOARD;
+
+ /*
+ * Try to identify a PRI card
+ */
+ outb(BRI_BASEPG_VAL, pgport);
+ msleep_interruptible(1000);
+ sig = readl(rambase + SIG_OFFSET);
+ pr_debug("Looking for a signature, got 0x%lx\n", sig);
+ if (sig == SIGNATURE)
+ return BRI_BOARD;
+
+ return -1;
+
+ /*
+ * Try to spot a card
+ */
+ sig = readl(rambase + SIG_OFFSET);
+ pr_debug("Looking for a signature, got 0x%lx\n", sig);
+ if (sig != SIGNATURE)
+ return -1;
+
+ dpm = (DualPortMemory *) rambase;
+
+ memset(&sndmsg, 0, MSG_LEN);
+ sndmsg.msg_byte_cnt = 3;
+ sndmsg.type = cmReqType1;
+ sndmsg.class = cmReqClass0;
+ sndmsg.code = cmReqHWConfig;
+ memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN);
+ outb(0, iobase + 0x400);
+ pr_debug("Sent HWConfig message\n");
+ /*
+ * Wait for the response
+ */
+ x = 0;
+ while ((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) {
+ schedule_timeout_interruptible(1);
+ x++;
+ }
+ if (x == 100) {
+ pr_debug("Timeout waiting for response\n");
+ return -1;
+ }
+
+ memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN);
+ pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status);
+ memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl));
+ pr_debug("Hardware Config: Interface: %s, RAM Size: %ld, Serial: %s\n"
+ " Part: %s, Rev: %s\n",
+ hwci.st_u_sense ? "S/T" : "U", hwci.ram_size,
+ hwci.serial_no, hwci.part_no, hwci.rev_no);
+
+ if (!strncmp(PRI_PARTNO, hwci.part_no, 6))
+ return PRI_BOARD;
+ if (!strncmp(BRI_PARTNO, hwci.part_no, 6))
+ return BRI_BOARD;
+
+ return -1;
+}
+
+module_init(sc_init);
+module_exit(sc_exit);
diff --git a/kernel/drivers/isdn/sc/interrupt.c b/kernel/drivers/isdn/sc/interrupt.c
new file mode 100644
index 000000000..e80cc76bc
--- /dev/null
+++ b/kernel/drivers/isdn/sc/interrupt.c
@@ -0,0 +1,247 @@
+/* $Id: interrupt.c,v 1.4.8.3 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include <linux/interrupt.h>
+
+/*
+ *
+ */
+irqreturn_t interrupt_handler(int dummy, void *card_inst)
+{
+
+ RspMessage rcvmsg;
+ int channel;
+ int card = (int)(unsigned long) card_inst;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return IRQ_NONE;
+ }
+
+ pr_debug("%s: Entered Interrupt handler\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Pull all of the waiting messages off the response queue
+ */
+ while (!receivemessage(card, &rcvmsg)) {
+ /*
+ * Push the message to the adapter structure for
+ * send_and_receive to snoop
+ */
+ if (sc_adapter[card]->want_async_messages)
+ memcpy(&(sc_adapter[card]->async_msg),
+ &rcvmsg, sizeof(RspMessage));
+
+ channel = (unsigned int) rcvmsg.phy_link_no;
+
+ /*
+ * Trap Invalid request messages
+ */
+ if (IS_CM_MESSAGE(rcvmsg, 0, 0, Invalid)) {
+ pr_debug("%s: Invalid request Message, rsp_status = %d\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.rsp_status);
+ break;
+ }
+
+ /*
+ * Check for a linkRead message
+ */
+ if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read))
+ {
+ pr_debug("%s: Received packet 0x%x bytes long at 0x%lx\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.msg_data.response.msg_len,
+ rcvmsg.msg_data.response.buff_offset);
+ rcvpkt(card, &rcvmsg);
+ continue;
+
+ }
+
+ /*
+ * Handle a write acknoledgement
+ */
+ if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Write)) {
+ pr_debug("%s: Packet Send ACK on channel %d\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.phy_link_no);
+ sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].free_sendbufs++;
+ continue;
+ }
+
+ /*
+ * Handle a connection message
+ */
+ if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Connect))
+ {
+ unsigned int callid;
+ setup_parm setup;
+ pr_debug("%s: Connect message: line %d: status %d: cause 0x%x\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.phy_link_no,
+ rcvmsg.rsp_status,
+ rcvmsg.msg_data.byte_array[2]);
+
+ memcpy(&callid, rcvmsg.msg_data.byte_array, sizeof(int));
+ if (callid >= 0x8000 && callid <= 0xFFFF)
+ {
+ pr_debug("%s: Got Dial-Out Rsp\n",
+ sc_adapter[card]->devicename);
+ indicate_status(card, ISDN_STAT_DCONN,
+ (unsigned long)rcvmsg.phy_link_no - 1, NULL);
+
+ }
+ else if (callid >= 0x0000 && callid <= 0x7FFF)
+ {
+ int len;
+
+ pr_debug("%s: Got Incoming Call\n",
+ sc_adapter[card]->devicename);
+ len = strlcpy(setup.phone, &(rcvmsg.msg_data.byte_array[4]),
+ sizeof(setup.phone));
+ if (len >= sizeof(setup.phone))
+ continue;
+ len = strlcpy(setup.eazmsn,
+ sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].dn,
+ sizeof(setup.eazmsn));
+ if (len >= sizeof(setup.eazmsn))
+ continue;
+ setup.si1 = 7;
+ setup.si2 = 0;
+ setup.plan = 0;
+ setup.screen = 0;
+
+ indicate_status(card, ISDN_STAT_ICALL, (unsigned long)rcvmsg.phy_link_no - 1, (char *)&setup);
+ indicate_status(card, ISDN_STAT_DCONN, (unsigned long)rcvmsg.phy_link_no - 1, NULL);
+ }
+ continue;
+ }
+
+ /*
+ * Handle a disconnection message
+ */
+ if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Disconnect))
+ {
+ pr_debug("%s: disconnect message: line %d: status %d: cause 0x%x\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.phy_link_no,
+ rcvmsg.rsp_status,
+ rcvmsg.msg_data.byte_array[2]);
+
+ indicate_status(card, ISDN_STAT_BHUP, (unsigned long)rcvmsg.phy_link_no - 1, NULL);
+ indicate_status(card, ISDN_STAT_DHUP, (unsigned long)rcvmsg.phy_link_no - 1, NULL);
+ continue;
+
+ }
+
+ /*
+ * Handle a startProc engine up message
+ */
+ if (IS_CM_MESSAGE(rcvmsg, 5, 0, MiscEngineUp)) {
+ pr_debug("%s: Received EngineUp message\n",
+ sc_adapter[card]->devicename);
+ sc_adapter[card]->EngineUp = 1;
+ sendmessage(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, 1, 0, NULL);
+ sendmessage(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber, 2, 0, NULL);
+ init_timer(&sc_adapter[card]->stat_timer);
+ sc_adapter[card]->stat_timer.function = check_phystat;
+ sc_adapter[card]->stat_timer.data = card;
+ sc_adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME;
+ add_timer(&sc_adapter[card]->stat_timer);
+ continue;
+ }
+
+ /*
+ * Start proc response
+ */
+ if (IS_CM_MESSAGE(rcvmsg, 2, 0, StartProc)) {
+ pr_debug("%s: StartProc Response Status %d\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.rsp_status);
+ continue;
+ }
+
+ /*
+ * Handle a GetMyNumber Rsp
+ */
+ if (IS_CE_MESSAGE(rcvmsg, Call, 0, GetMyNumber)) {
+ strlcpy(sc_adapter[card]->channel[rcvmsg.phy_link_no - 1].dn,
+ rcvmsg.msg_data.byte_array,
+ sizeof(rcvmsg.msg_data.byte_array));
+ continue;
+ }
+
+ /*
+ * PhyStatus response
+ */
+ if (IS_CE_MESSAGE(rcvmsg, Phy, 2, Status)) {
+ unsigned int b1stat, b2stat;
+
+ /*
+ * Covert the message data to the adapter->phystat code
+ */
+ b1stat = (unsigned int) rcvmsg.msg_data.byte_array[0];
+ b2stat = (unsigned int) rcvmsg.msg_data.byte_array[1];
+
+ sc_adapter[card]->nphystat = (b2stat >> 8) | b1stat; /* endian?? */
+ pr_debug("%s: PhyStat is 0x%2x\n",
+ sc_adapter[card]->devicename,
+ sc_adapter[card]->nphystat);
+ continue;
+ }
+
+
+ /*
+ * Handle a GetFramFormat
+ */
+ if (IS_CE_MESSAGE(rcvmsg, Call, 0, GetFrameFormat)) {
+ if (rcvmsg.msg_data.byte_array[0] != HDLC_PROTO) {
+ unsigned int proto = HDLC_PROTO;
+ /*
+ * Set board format to HDLC if it wasn't already
+ */
+ pr_debug("%s: current frame format: 0x%x, will change to HDLC\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.msg_data.byte_array[0]);
+ sendmessage(card, CEPID, ceReqTypeCall,
+ ceReqClass0,
+ ceReqCallSetFrameFormat,
+ (unsigned char)channel + 1,
+ 1, &proto);
+ }
+ continue;
+ }
+
+ /*
+ * Hmm...
+ */
+ pr_debug("%s: Received unhandled message (%d,%d,%d) link %d\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.type, rcvmsg.class, rcvmsg.code,
+ rcvmsg.phy_link_no);
+
+ } /* while */
+
+ pr_debug("%s: Exiting Interrupt Handler\n",
+ sc_adapter[card]->devicename);
+ return IRQ_HANDLED;
+}
diff --git a/kernel/drivers/isdn/sc/ioctl.c b/kernel/drivers/isdn/sc/ioctl.c
new file mode 100644
index 000000000..e63983aa1
--- /dev/null
+++ b/kernel/drivers/isdn/sc/ioctl.c
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include "scioc.h"
+
+static int GetStatus(int card, boardInfo *);
+
+/*
+ * Process private IOCTL messages (typically from scctrl)
+ */
+int sc_ioctl(int card, scs_ioctl *data)
+{
+ int status;
+ RspMessage *rcvmsg;
+ char *spid;
+ char *dn;
+ char switchtype;
+ char speed;
+
+ rcvmsg = kmalloc(sizeof(RspMessage), GFP_KERNEL);
+ if (!rcvmsg)
+ return -ENOMEM;
+
+ switch (data->command) {
+ case SCIOCRESET: /* Perform a hard reset of the adapter */
+ {
+ pr_debug("%s: SCIOCRESET: ioctl received\n",
+ sc_adapter[card]->devicename);
+ sc_adapter[card]->StartOnReset = 0;
+ kfree(rcvmsg);
+ return reset(card);
+ }
+
+ case SCIOCLOAD:
+ {
+ char *srec;
+
+ srec = kmalloc(SCIOC_SRECSIZE, GFP_KERNEL);
+ if (!srec) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+ pr_debug("%s: SCIOLOAD: ioctl received\n",
+ sc_adapter[card]->devicename);
+ if (sc_adapter[card]->EngineUp) {
+ pr_debug("%s: SCIOCLOAD: command failed, LoadProc while engine running.\n",
+ sc_adapter[card]->devicename);
+ kfree(rcvmsg);
+ kfree(srec);
+ return -1;
+ }
+
+ /*
+ * Get the SRec from user space
+ */
+ if (copy_from_user(srec, data->dataptr, SCIOC_SRECSIZE)) {
+ kfree(rcvmsg);
+ kfree(srec);
+ return -EFAULT;
+ }
+
+ status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc,
+ 0, SCIOC_SRECSIZE, srec, rcvmsg, SAR_TIMEOUT);
+ kfree(rcvmsg);
+ kfree(srec);
+
+ if (status) {
+ pr_debug("%s: SCIOCLOAD: command failed, status = %d\n",
+ sc_adapter[card]->devicename, status);
+ return -1;
+ }
+ else {
+ pr_debug("%s: SCIOCLOAD: command successful\n",
+ sc_adapter[card]->devicename);
+ return 0;
+ }
+ }
+
+ case SCIOCSTART:
+ {
+ kfree(rcvmsg);
+ pr_debug("%s: SCIOSTART: ioctl received\n",
+ sc_adapter[card]->devicename);
+ if (sc_adapter[card]->EngineUp) {
+ pr_debug("%s: SCIOCSTART: command failed, engine already running.\n",
+ sc_adapter[card]->devicename);
+ return -1;
+ }
+
+ sc_adapter[card]->StartOnReset = 1;
+ startproc(card);
+ return 0;
+ }
+
+ case SCIOCSETSWITCH:
+ {
+ pr_debug("%s: SCIOSETSWITCH: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Get the switch type from user space
+ */
+ if (copy_from_user(&switchtype, data->dataptr, sizeof(char))) {
+ kfree(rcvmsg);
+ return -EFAULT;
+ }
+
+ pr_debug("%s: SCIOCSETSWITCH: setting switch type to %d\n",
+ sc_adapter[card]->devicename,
+ switchtype);
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallSetSwitchType,
+ 0, sizeof(char), &switchtype, rcvmsg, SAR_TIMEOUT);
+ if (!status && !(rcvmsg->rsp_status)) {
+ pr_debug("%s: SCIOCSETSWITCH: command successful\n",
+ sc_adapter[card]->devicename);
+ kfree(rcvmsg);
+ return 0;
+ }
+ else {
+ pr_debug("%s: SCIOCSETSWITCH: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ return status;
+ }
+ }
+
+ case SCIOCGETSWITCH:
+ {
+ pr_debug("%s: SCIOGETSWITCH: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Get the switch type from the board
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+ ceReqCallGetSwitchType, 0, 0, NULL, rcvmsg, SAR_TIMEOUT);
+ if (!status && !(rcvmsg->rsp_status)) {
+ pr_debug("%s: SCIOCGETSWITCH: command successful\n",
+ sc_adapter[card]->devicename);
+ }
+ else {
+ pr_debug("%s: SCIOCGETSWITCH: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ return status;
+ }
+
+ switchtype = rcvmsg->msg_data.byte_array[0];
+
+ /*
+ * Package the switch type and send to user space
+ */
+ if (copy_to_user(data->dataptr, &switchtype,
+ sizeof(char))) {
+ kfree(rcvmsg);
+ return -EFAULT;
+ }
+
+ kfree(rcvmsg);
+ return 0;
+ }
+
+ case SCIOCGETSPID:
+ {
+ pr_debug("%s: SCIOGETSPID: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ spid = kzalloc(SCIOC_SPIDSIZE, GFP_KERNEL);
+ if (!spid) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+ /*
+ * Get the spid from the board
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetSPID,
+ data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ pr_debug("%s: SCIOCGETSPID: command successful\n",
+ sc_adapter[card]->devicename);
+ } else {
+ pr_debug("%s: SCIOCGETSPID: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(spid);
+ kfree(rcvmsg);
+ return status;
+ }
+ strlcpy(spid, rcvmsg->msg_data.byte_array, SCIOC_SPIDSIZE);
+
+ /*
+ * Package the switch type and send to user space
+ */
+ if (copy_to_user(data->dataptr, spid, SCIOC_SPIDSIZE)) {
+ kfree(spid);
+ kfree(rcvmsg);
+ return -EFAULT;
+ }
+
+ kfree(spid);
+ kfree(rcvmsg);
+ return 0;
+ }
+
+ case SCIOCSETSPID:
+ {
+ pr_debug("%s: DCBIOSETSPID: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Get the spid from user space
+ */
+ spid = memdup_user(data->dataptr, SCIOC_SPIDSIZE);
+ if (IS_ERR(spid)) {
+ kfree(rcvmsg);
+ return PTR_ERR(spid);
+ }
+
+ pr_debug("%s: SCIOCSETSPID: setting channel %d spid to %s\n",
+ sc_adapter[card]->devicename, data->channel, spid);
+ status = send_and_receive(card, CEPID, ceReqTypeCall,
+ ceReqClass0, ceReqCallSetSPID, data->channel,
+ strlen(spid), spid, rcvmsg, SAR_TIMEOUT);
+ if (!status && !(rcvmsg->rsp_status)) {
+ pr_debug("%s: SCIOCSETSPID: command successful\n",
+ sc_adapter[card]->devicename);
+ kfree(rcvmsg);
+ kfree(spid);
+ return 0;
+ }
+ else {
+ pr_debug("%s: SCIOCSETSPID: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ kfree(spid);
+ return status;
+ }
+ }
+
+ case SCIOCGETDN:
+ {
+ pr_debug("%s: SCIOGETDN: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Get the dn from the board
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber,
+ data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ pr_debug("%s: SCIOCGETDN: command successful\n",
+ sc_adapter[card]->devicename);
+ }
+ else {
+ pr_debug("%s: SCIOCGETDN: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ return status;
+ }
+
+ dn = kzalloc(SCIOC_DNSIZE, GFP_KERNEL);
+ if (!dn) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+ strlcpy(dn, rcvmsg->msg_data.byte_array, SCIOC_DNSIZE);
+ kfree(rcvmsg);
+
+ /*
+ * Package the dn and send to user space
+ */
+ if (copy_to_user(data->dataptr, dn, SCIOC_DNSIZE)) {
+ kfree(dn);
+ return -EFAULT;
+ }
+ kfree(dn);
+ return 0;
+ }
+
+ case SCIOCSETDN:
+ {
+ pr_debug("%s: SCIOSETDN: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Get the spid from user space
+ */
+ dn = memdup_user(data->dataptr, SCIOC_DNSIZE);
+ if (IS_ERR(dn)) {
+ kfree(rcvmsg);
+ return PTR_ERR(dn);
+ }
+
+ pr_debug("%s: SCIOCSETDN: setting channel %d dn to %s\n",
+ sc_adapter[card]->devicename, data->channel, dn);
+ status = send_and_receive(card, CEPID, ceReqTypeCall,
+ ceReqClass0, ceReqCallSetMyNumber, data->channel,
+ strlen(dn), dn, rcvmsg, SAR_TIMEOUT);
+ if (!status && !(rcvmsg->rsp_status)) {
+ pr_debug("%s: SCIOCSETDN: command successful\n",
+ sc_adapter[card]->devicename);
+ kfree(rcvmsg);
+ kfree(dn);
+ return 0;
+ }
+ else {
+ pr_debug("%s: SCIOCSETDN: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ kfree(dn);
+ return status;
+ }
+ }
+
+ case SCIOCTRACE:
+
+ pr_debug("%s: SCIOTRACE: ioctl received\n",
+ sc_adapter[card]->devicename);
+/* sc_adapter[card]->trace = !sc_adapter[card]->trace;
+ pr_debug("%s: SCIOCTRACE: tracing turned %s\n",
+ sc_adapter[card]->devicename,
+ sc_adapter[card]->trace ? "ON" : "OFF"); */
+ break;
+
+ case SCIOCSTAT:
+ {
+ boardInfo *bi;
+
+ pr_debug("%s: SCIOSTAT: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ bi = kzalloc(sizeof(boardInfo), GFP_KERNEL);
+ if (!bi) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+
+ kfree(rcvmsg);
+ GetStatus(card, bi);
+
+ if (copy_to_user(data->dataptr, bi, sizeof(boardInfo))) {
+ kfree(bi);
+ return -EFAULT;
+ }
+
+ kfree(bi);
+ return 0;
+ }
+
+ case SCIOCGETSPEED:
+ {
+ pr_debug("%s: SCIOGETSPEED: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Get the speed from the board
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+ ceReqCallGetCallType, data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
+ if (!status && !(rcvmsg->rsp_status)) {
+ pr_debug("%s: SCIOCGETSPEED: command successful\n",
+ sc_adapter[card]->devicename);
+ }
+ else {
+ pr_debug("%s: SCIOCGETSPEED: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ return status;
+ }
+
+ speed = rcvmsg->msg_data.byte_array[0];
+
+ kfree(rcvmsg);
+
+ /*
+ * Package the switch type and send to user space
+ */
+
+ if (copy_to_user(data->dataptr, &speed, sizeof(char)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case SCIOCSETSPEED:
+ pr_debug("%s: SCIOCSETSPEED: ioctl received\n",
+ sc_adapter[card]->devicename);
+ break;
+
+ case SCIOCLOOPTST:
+ pr_debug("%s: SCIOCLOOPTST: ioctl received\n",
+ sc_adapter[card]->devicename);
+ break;
+
+ default:
+ kfree(rcvmsg);
+ return -1;
+ }
+
+ kfree(rcvmsg);
+ return 0;
+}
+
+static int GetStatus(int card, boardInfo *bi)
+{
+ RspMessage rcvmsg;
+ int i, status;
+
+ /*
+ * Fill in some of the basic info about the board
+ */
+ bi->modelid = sc_adapter[card]->model;
+ strcpy(bi->serial_no, sc_adapter[card]->hwconfig.serial_no);
+ strcpy(bi->part_no, sc_adapter[card]->hwconfig.part_no);
+ bi->iobase = sc_adapter[card]->iobase;
+ bi->rambase = sc_adapter[card]->rambase;
+ bi->irq = sc_adapter[card]->interrupt;
+ bi->ramsize = sc_adapter[card]->hwconfig.ram_size;
+ bi->interface = sc_adapter[card]->hwconfig.st_u_sense;
+ strcpy(bi->load_ver, sc_adapter[card]->load_ver);
+ strcpy(bi->proc_ver, sc_adapter[card]->proc_ver);
+
+ /*
+ * Get the current PhyStats and LnkStats
+ */
+ status = send_and_receive(card, CEPID, ceReqTypePhy, ceReqClass2,
+ ceReqPhyStatus, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ if (sc_adapter[card]->model < PRI_BOARD) {
+ bi->l1_status = rcvmsg.msg_data.byte_array[2];
+ for (i = 0; i < BRI_CHANNELS; i++)
+ bi->status.bristats[i].phy_stat =
+ rcvmsg.msg_data.byte_array[i];
+ }
+ else {
+ bi->l1_status = rcvmsg.msg_data.byte_array[0];
+ bi->l2_status = rcvmsg.msg_data.byte_array[1];
+ for (i = 0; i < PRI_CHANNELS; i++)
+ bi->status.pristats[i].phy_stat =
+ rcvmsg.msg_data.byte_array[i + 2];
+ }
+ }
+
+ /*
+ * Get the call types for each channel
+ */
+ for (i = 0; i < sc_adapter[card]->nChannels; i++) {
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+ ceReqCallGetCallType, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ if (sc_adapter[card]->model == PRI_BOARD) {
+ bi->status.pristats[i].call_type =
+ rcvmsg.msg_data.byte_array[0];
+ }
+ else {
+ bi->status.bristats[i].call_type =
+ rcvmsg.msg_data.byte_array[0];
+ }
+ }
+ }
+
+ /*
+ * If PRI, get the call states and service states for each channel
+ */
+ if (sc_adapter[card]->model == PRI_BOARD) {
+ /*
+ * Get the call states
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
+ ceReqPhyChCallState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ for (i = 0; i < PRI_CHANNELS; i++)
+ bi->status.pristats[i].call_state =
+ rcvmsg.msg_data.byte_array[i];
+ }
+
+ /*
+ * Get the service states
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
+ ceReqPhyChServState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ for (i = 0; i < PRI_CHANNELS; i++)
+ bi->status.pristats[i].serv_state =
+ rcvmsg.msg_data.byte_array[i];
+ }
+
+ /*
+ * Get the link stats for the channels
+ */
+ for (i = 1; i <= PRI_CHANNELS; i++) {
+ status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+ ceReqLnkGetStats, i, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ bi->status.pristats[i - 1].link_stats.tx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[0];
+ bi->status.pristats[i - 1].link_stats.tx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[4];
+ bi->status.pristats[i - 1].link_stats.rx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[8];
+ bi->status.pristats[i - 1].link_stats.rx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[12];
+ }
+ }
+
+ /*
+ * Link stats for the D channel
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+ ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
+ bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
+ bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
+ bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
+ }
+
+ return 0;
+ }
+
+ /*
+ * If BRI or POTS, Get SPID, DN and call types for each channel
+ */
+
+ /*
+ * Get the link stats for the channels
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+ ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
+ bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
+ bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
+ bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
+ bi->status.bristats[0].link_stats.tx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[16];
+ bi->status.bristats[0].link_stats.tx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[20];
+ bi->status.bristats[0].link_stats.rx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[24];
+ bi->status.bristats[0].link_stats.rx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[28];
+ bi->status.bristats[1].link_stats.tx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[32];
+ bi->status.bristats[1].link_stats.tx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[36];
+ bi->status.bristats[1].link_stats.rx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[40];
+ bi->status.bristats[1].link_stats.rx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[44];
+ }
+
+ /*
+ * Get the SPIDs
+ */
+ for (i = 0; i < BRI_CHANNELS; i++) {
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+ ceReqCallGetSPID, i + 1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status)
+ strcpy(bi->status.bristats[i].spid, rcvmsg.msg_data.byte_array);
+ }
+
+ /*
+ * Get the DNs
+ */
+ for (i = 0; i < BRI_CHANNELS; i++) {
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+ ceReqCallGetMyNumber, i + 1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status)
+ strcpy(bi->status.bristats[i].dn, rcvmsg.msg_data.byte_array);
+ }
+
+ return 0;
+}
diff --git a/kernel/drivers/isdn/sc/message.c b/kernel/drivers/isdn/sc/message.c
new file mode 100644
index 000000000..9679a1902
--- /dev/null
+++ b/kernel/drivers/isdn/sc/message.c
@@ -0,0 +1,230 @@
+/* $Id: message.c,v 1.5.8.2 2001/09/23 22:24:59 kai Exp $
+ *
+ * functions for sending and receiving control messages
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+#include <linux/sched.h>
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+/*
+ * receive a message from the board
+ */
+int receivemessage(int card, RspMessage *rspmsg)
+{
+ DualPortMemory *dpm;
+ unsigned long flags;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: Entered receivemessage\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * See if there are messages waiting
+ */
+ if (inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) {
+ /*
+ * Map in the DPM to the base page and copy the message
+ */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+ outb((sc_adapter[card]->shmem_magic >> 14) | 0x80,
+ sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+ dpm = (DualPortMemory *) sc_adapter[card]->rambase;
+ memcpy_fromio(rspmsg, &(dpm->rsp_queue[dpm->rsp_tail]),
+ MSG_LEN);
+ dpm->rsp_tail = (dpm->rsp_tail + 1) % MAX_MESSAGES;
+ inb(sc_adapter[card]->ioport[FIFO_READ]);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+ /*
+ * Tell the board that the message is received
+ */
+ pr_debug("%s: Received Message seq:%d pid:%d time:%d cmd:%d "
+ "cnt:%d (type,class,code):(%d,%d,%d) "
+ "link:%d stat:0x%x\n",
+ sc_adapter[card]->devicename,
+ rspmsg->sequence_no,
+ rspmsg->process_id,
+ rspmsg->time_stamp,
+ rspmsg->cmd_sequence_no,
+ rspmsg->msg_byte_cnt,
+ rspmsg->type,
+ rspmsg->class,
+ rspmsg->code,
+ rspmsg->phy_link_no,
+ rspmsg->rsp_status);
+
+ return 0;
+ }
+ return -ENOMSG;
+}
+
+/*
+ * send a message to the board
+ */
+int sendmessage(int card,
+ unsigned int procid,
+ unsigned int type,
+ unsigned int class,
+ unsigned int code,
+ unsigned int link,
+ unsigned int data_len,
+ unsigned int *data)
+{
+ DualPortMemory *dpm;
+ ReqMessage sndmsg;
+ unsigned long flags;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure we only send CEPID messages when the engine is up
+ * and CMPID messages when it is down
+ */
+ if (sc_adapter[card]->EngineUp && procid == CMPID) {
+ pr_debug("%s: Attempt to send CM message with engine up\n",
+ sc_adapter[card]->devicename);
+ return -ESRCH;
+ }
+
+ if (!sc_adapter[card]->EngineUp && procid == CEPID) {
+ pr_debug("%s: Attempt to send CE message with engine down\n",
+ sc_adapter[card]->devicename);
+ return -ESRCH;
+ }
+
+ memset(&sndmsg, 0, MSG_LEN);
+ sndmsg.msg_byte_cnt = 4;
+ sndmsg.type = type;
+ sndmsg.class = class;
+ sndmsg.code = code;
+ sndmsg.phy_link_no = link;
+
+ if (data_len > 0) {
+ if (data_len > MSG_DATA_LEN)
+ data_len = MSG_DATA_LEN;
+ memcpy(&(sndmsg.msg_data), data, data_len);
+ sndmsg.msg_byte_cnt = data_len + 8;
+ }
+
+ sndmsg.process_id = procid;
+ sndmsg.sequence_no = sc_adapter[card]->seq_no++ % 256;
+
+ /*
+ * wait for an empty slot in the queue
+ */
+ while (!(inb(sc_adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL))
+ udelay(1);
+
+ /*
+ * Disable interrupts and map in shared memory
+ */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+ outb((sc_adapter[card]->shmem_magic >> 14) | 0x80,
+ sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+ dpm = (DualPortMemory *) sc_adapter[card]->rambase; /* Fix me */
+ memcpy_toio(&(dpm->req_queue[dpm->req_head]), &sndmsg, MSG_LEN);
+ dpm->req_head = (dpm->req_head + 1) % MAX_MESSAGES;
+ outb(sndmsg.sequence_no, sc_adapter[card]->ioport[FIFO_WRITE]);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+
+ pr_debug("%s: Sent Message seq:%d pid:%d time:%d "
+ "cnt:%d (type,class,code):(%d,%d,%d) "
+ "link:%d\n ",
+ sc_adapter[card]->devicename,
+ sndmsg.sequence_no,
+ sndmsg.process_id,
+ sndmsg.time_stamp,
+ sndmsg.msg_byte_cnt,
+ sndmsg.type,
+ sndmsg.class,
+ sndmsg.code,
+ sndmsg.phy_link_no);
+
+ return 0;
+}
+
+int send_and_receive(int card,
+ unsigned int procid,
+ unsigned char type,
+ unsigned char class,
+ unsigned char code,
+ unsigned char link,
+ unsigned char data_len,
+ unsigned char *data,
+ RspMessage *mesgdata,
+ int timeout)
+{
+ int retval;
+ int tries;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -EINVAL;
+ }
+
+ sc_adapter[card]->want_async_messages = 1;
+ retval = sendmessage(card, procid, type, class, code, link,
+ data_len, (unsigned int *) data);
+
+ if (retval) {
+ pr_debug("%s: SendMessage failed in SAR\n",
+ sc_adapter[card]->devicename);
+ sc_adapter[card]->want_async_messages = 0;
+ return -EIO;
+ }
+
+ tries = 0;
+ /* wait for the response */
+ while (tries < timeout) {
+ schedule_timeout_interruptible(1);
+
+ pr_debug("SAR waiting..\n");
+
+ /*
+ * See if we got our message back
+ */
+ if ((sc_adapter[card]->async_msg.type == type) &&
+ (sc_adapter[card]->async_msg.class == class) &&
+ (sc_adapter[card]->async_msg.code == code) &&
+ (sc_adapter[card]->async_msg.phy_link_no == link)) {
+
+ /*
+ * Got it!
+ */
+ pr_debug("%s: Got ASYNC message\n",
+ sc_adapter[card]->devicename);
+ memcpy(mesgdata, &(sc_adapter[card]->async_msg),
+ sizeof(RspMessage));
+ sc_adapter[card]->want_async_messages = 0;
+ return 0;
+ }
+
+ tries++;
+ }
+
+ pr_debug("%s: SAR message timeout\n", sc_adapter[card]->devicename);
+ sc_adapter[card]->want_async_messages = 0;
+ return -ETIME;
+}
diff --git a/kernel/drivers/isdn/sc/message.h b/kernel/drivers/isdn/sc/message.h
new file mode 100644
index 000000000..5e6f4a5c1
--- /dev/null
+++ b/kernel/drivers/isdn/sc/message.h
@@ -0,0 +1,245 @@
+/* $Id: message.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * structures, macros and defines useful for sending
+ * messages to the adapter
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+/*
+ * Board message macros, defines and structures
+ */
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#define MAX_MESSAGES 32 /* Maximum messages that can be
+ queued */
+#define MSG_DATA_LEN 48 /* Maximum size of message payload */
+#define MSG_LEN 64 /* Size of a message */
+#define CMPID 0 /* Loader message process ID */
+#define CEPID 64 /* Firmware message process ID */
+
+/*
+ * Macro to determine if a message is a loader message
+ */
+#define IS_CM_MESSAGE(mesg, tx, cx, dx) \
+ ((mesg.type == cmRspType##tx) \
+ && (mesg.class == cmRspClass##cx) \
+ && (mesg.code == cmRsp##dx))
+
+/*
+ * Macro to determine if a message is a firmware message
+ */
+#define IS_CE_MESSAGE(mesg, tx, cx, dx) \
+ ((mesg.type == ceRspType##tx) \
+ && (mesg.class == ceRspClass##cx) \
+ && (mesg.code == ceRsp##tx##dx))
+
+/*
+ * Loader Request and Response Messages
+ */
+
+/* message types */
+#define cmReqType1 1
+#define cmReqType2 2
+#define cmRspType0 0
+#define cmRspType1 1
+#define cmRspType2 2
+#define cmRspType5 5
+
+/* message classes */
+#define cmReqClass0 0
+#define cmRspClass0 0
+
+/* message codes */
+#define cmReqHWConfig 1 /* 1,0,1 */
+#define cmReqMsgLpbk 2 /* 1,0,2 */
+#define cmReqVersion 3 /* 1,0,3 */
+#define cmReqLoadProc 1 /* 2,0,1 */
+#define cmReqStartProc 2 /* 2,0,2 */
+#define cmReqReadMem 6 /* 2,0,6 */
+#define cmRspHWConfig cmReqHWConfig
+#define cmRspMsgLpbk cmReqMsgLpbk
+#define cmRspVersion cmReqVersion
+#define cmRspLoadProc cmReqLoadProc
+#define cmRspStartProc cmReqStartProc
+#define cmRspReadMem cmReqReadMem
+#define cmRspMiscEngineUp 1 /* 5,0,1 */
+#define cmRspInvalid 0 /* 0,0,0 */
+
+
+/*
+ * Firmware Request and Response Messages
+ */
+
+/* message types */
+#define ceReqTypePhy 1
+#define ceReqTypeLnk 2
+#define ceReqTypeCall 3
+#define ceReqTypeStat 1
+#define ceRspTypeErr 0
+#define ceRspTypePhy ceReqTypePhy
+#define ceRspTypeLnk ceReqTypeLnk
+#define ceRspTypeCall ceReqTypeCall
+#define ceRspTypeStat ceReqTypeStat
+
+/* message classes */
+#define ceReqClass0 0
+#define ceReqClass1 1
+#define ceReqClass2 2
+#define ceReqClass3 3
+#define ceRspClass0 ceReqClass0
+#define ceRspClass1 ceReqClass1
+#define ceRspClass2 ceReqClass2
+#define ceRspClass3 ceReqClass3
+
+/* message codes (B) = BRI only, (P) = PRI only, (V) = POTS only */
+#define ceReqPhyProcInfo 1 /* 1,0,1 */
+#define ceReqPhyConnect 1 /* 1,1,1 */
+#define ceReqPhyDisconnect 2 /* 1,1,2 */
+#define ceReqPhySetParams 3 /* 1,1,3 (P) */
+#define ceReqPhyGetParams 4 /* 1,1,4 (P) */
+#define ceReqPhyStatus 1 /* 1,2,1 */
+#define ceReqPhyAcfaStatus 2 /* 1,2,2 (P) */
+#define ceReqPhyChCallState 3 /* 1,2,3 (P) */
+#define ceReqPhyChServState 4 /* 1,2,4 (P) */
+#define ceReqPhyRLoopBack 1 /* 1,3,1 */
+#define ceRspPhyProcInfo ceReqPhyProcInfo
+#define ceRspPhyConnect ceReqPhyConnect
+#define ceRspPhyDisconnect ceReqPhyDisconnect
+#define ceRspPhySetParams ceReqPhySetParams
+#define ceRspPhyGetParams ceReqPhyGetParams
+#define ceRspPhyStatus ceReqPhyStatus
+#define ceRspPhyAcfaStatus ceReqPhyAcfaStatus
+#define ceRspPhyChCallState ceReqPhyChCallState
+#define ceRspPhyChServState ceReqPhyChServState
+#define ceRspPhyRLoopBack ceReqphyRLoopBack
+#define ceReqLnkSetParam 1 /* 2,0,1 */
+#define ceReqLnkGetParam 2 /* 2,0,2 */
+#define ceReqLnkGetStats 3 /* 2,0,3 */
+#define ceReqLnkWrite 1 /* 2,1,1 */
+#define ceReqLnkRead 2 /* 2,1,2 */
+#define ceReqLnkFlush 3 /* 2,1,3 */
+#define ceReqLnkWrBufTrc 4 /* 2,1,4 */
+#define ceReqLnkRdBufTrc 5 /* 2,1,5 */
+#define ceRspLnkSetParam ceReqLnkSetParam
+#define ceRspLnkGetParam ceReqLnkGetParam
+#define ceRspLnkGetStats ceReqLnkGetStats
+#define ceRspLnkWrite ceReqLnkWrite
+#define ceRspLnkRead ceReqLnkRead
+#define ceRspLnkFlush ceReqLnkFlush
+#define ceRspLnkWrBufTrc ceReqLnkWrBufTrc
+#define ceRspLnkRdBufTrc ceReqLnkRdBufTrc
+#define ceReqCallSetSwitchType 1 /* 3,0,1 */
+#define ceReqCallGetSwitchType 2 /* 3,0,2 */
+#define ceReqCallSetFrameFormat 3 /* 3,0,3 */
+#define ceReqCallGetFrameFormat 4 /* 3,0,4 */
+#define ceReqCallSetCallType 5 /* 3,0,5 */
+#define ceReqCallGetCallType 6 /* 3,0,6 */
+#define ceReqCallSetSPID 7 /* 3,0,7 (!P) */
+#define ceReqCallGetSPID 8 /* 3,0,8 (!P) */
+#define ceReqCallSetMyNumber 9 /* 3,0,9 (!P) */
+#define ceReqCallGetMyNumber 10 /* 3,0,10 (!P) */
+#define ceRspCallSetSwitchType ceReqCallSetSwitchType
+#define ceRspCallGetSwitchType ceReqCallSetSwitchType
+#define ceRspCallSetFrameFormat ceReqCallSetFrameFormat
+#define ceRspCallGetFrameFormat ceReqCallGetFrameFormat
+#define ceRspCallSetCallType ceReqCallSetCallType
+#define ceRspCallGetCallType ceReqCallGetCallType
+#define ceRspCallSetSPID ceReqCallSetSPID
+#define ceRspCallGetSPID ceReqCallGetSPID
+#define ceRspCallSetMyNumber ceReqCallSetMyNumber
+#define ceRspCallGetMyNumber ceReqCallGetMyNumber
+#define ceRspStatAcfaStatus 2
+#define ceRspStat
+#define ceRspErrError 0 /* 0,0,0 */
+
+/*
+ * Call Types
+ */
+#define CALLTYPE_64K 0
+#define CALLTYPE_56K 1
+#define CALLTYPE_SPEECH 2
+#define CALLTYPE_31KHZ 3
+
+/*
+ * Link Level data contains a pointer to and the length of
+ * a buffer in shared RAM. Used by LnkRead and LnkWrite message
+ * types. Part of RspMsgStruct and ReqMsgStruct.
+ */
+typedef struct {
+ unsigned long buff_offset;
+ unsigned short msg_len;
+} LLData;
+
+
+/*
+ * Message payload template for an HWConfig message
+ */
+typedef struct {
+ char st_u_sense;
+ char powr_sense;
+ char sply_sense;
+ unsigned char asic_id;
+ long ram_size;
+ char serial_no[13];
+ char part_no[13];
+ char rev_no[2];
+} HWConfig_pl;
+
+/*
+ * A Message
+ */
+struct message {
+ unsigned char sequence_no;
+ unsigned char process_id;
+ unsigned char time_stamp;
+ unsigned char cmd_sequence_no; /* Rsp messages only */
+ unsigned char reserved1[3];
+ unsigned char msg_byte_cnt;
+ unsigned char type;
+ unsigned char class;
+ unsigned char code;
+ unsigned char phy_link_no;
+ unsigned char rsp_status; /* Rsp messages only */
+ unsigned char reseved2[3];
+ union {
+ unsigned char byte_array[MSG_DATA_LEN];
+ LLData response;
+ HWConfig_pl HWCresponse;
+ } msg_data;
+};
+
+typedef struct message ReqMessage; /* Request message */
+typedef struct message RspMessage; /* Response message */
+
+/*
+ * The first 5010 bytes of shared memory contain the message queues,
+ * indexes and other data. This structure is its template
+ */
+typedef struct {
+ volatile ReqMessage req_queue[MAX_MESSAGES];
+ volatile RspMessage rsp_queue[MAX_MESSAGES];
+ volatile unsigned char req_head;
+ volatile unsigned char req_tail;
+ volatile unsigned char rsp_head;
+ volatile unsigned char rsp_tail;
+ volatile unsigned long signature;
+ volatile unsigned long trace_enable;
+ volatile unsigned char reserved[4];
+} DualPortMemory;
+
+#endif
diff --git a/kernel/drivers/isdn/sc/packet.c b/kernel/drivers/isdn/sc/packet.c
new file mode 100644
index 000000000..244695708
--- /dev/null
+++ b/kernel/drivers/isdn/sc/packet.c
@@ -0,0 +1,204 @@
+/* $Id: packet.c,v 1.5.8.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+int sndpkt(int devId, int channel, int ack, struct sk_buff *data)
+{
+ LLData ReqLnkWrite;
+ int status;
+ int card;
+ unsigned long len;
+
+ card = get_card_from_id(devId);
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ pr_debug("%s: sndpkt: frst = 0x%lx nxt = %d f = %d n = %d\n",
+ sc_adapter[card]->devicename,
+ sc_adapter[card]->channel[channel].first_sendbuf,
+ sc_adapter[card]->channel[channel].next_sendbuf,
+ sc_adapter[card]->channel[channel].free_sendbufs,
+ sc_adapter[card]->channel[channel].num_sendbufs);
+
+ if (!sc_adapter[card]->channel[channel].free_sendbufs) {
+ pr_debug("%s: out of TX buffers\n",
+ sc_adapter[card]->devicename);
+ return -EINVAL;
+ }
+
+ if (data->len > BUFFER_SIZE) {
+ pr_debug("%s: data overflows buffer size (data > buffer)\n",
+ sc_adapter[card]->devicename);
+ return -EINVAL;
+ }
+
+ ReqLnkWrite.buff_offset = sc_adapter[card]->channel[channel].next_sendbuf *
+ BUFFER_SIZE + sc_adapter[card]->channel[channel].first_sendbuf;
+ ReqLnkWrite.msg_len = data->len; /* sk_buff size */
+ pr_debug("%s: writing %d bytes to buffer offset 0x%lx\n",
+ sc_adapter[card]->devicename,
+ ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset);
+ memcpy_toshmem(card, (char *)ReqLnkWrite.buff_offset, data->data, ReqLnkWrite.msg_len);
+
+ /*
+ * sendmessage
+ */
+ pr_debug("%s: sndpkt size=%d, buf_offset=0x%lx buf_indx=%d\n",
+ sc_adapter[card]->devicename,
+ ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset,
+ sc_adapter[card]->channel[channel].next_sendbuf);
+
+ status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite,
+ channel + 1, sizeof(LLData), (unsigned int *)&ReqLnkWrite);
+ len = data->len;
+ if (status) {
+ pr_debug("%s: failed to send packet, status = %d\n",
+ sc_adapter[card]->devicename, status);
+ return -1;
+ }
+ else {
+ sc_adapter[card]->channel[channel].free_sendbufs--;
+ sc_adapter[card]->channel[channel].next_sendbuf =
+ ++sc_adapter[card]->channel[channel].next_sendbuf ==
+ sc_adapter[card]->channel[channel].num_sendbufs ? 0 :
+ sc_adapter[card]->channel[channel].next_sendbuf;
+ pr_debug("%s: packet sent successfully\n", sc_adapter[card]->devicename);
+ dev_kfree_skb(data);
+ indicate_status(card, ISDN_STAT_BSENT, channel, (char *)&len);
+ }
+ return len;
+}
+
+void rcvpkt(int card, RspMessage *rcvmsg)
+{
+ LLData newll;
+ struct sk_buff *skb;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("invalid param: %d is not a valid card id\n", card);
+ return;
+ }
+
+ switch (rcvmsg->rsp_status) {
+ case 0x01:
+ case 0x02:
+ case 0x70:
+ pr_debug("%s: error status code: 0x%x\n",
+ sc_adapter[card]->devicename, rcvmsg->rsp_status);
+ return;
+ case 0x00:
+ if (!(skb = dev_alloc_skb(rcvmsg->msg_data.response.msg_len))) {
+ printk(KERN_WARNING "%s: rcvpkt out of memory, dropping packet\n",
+ sc_adapter[card]->devicename);
+ return;
+ }
+ skb_put(skb, rcvmsg->msg_data.response.msg_len);
+ pr_debug("%s: getting data from offset: 0x%lx\n",
+ sc_adapter[card]->devicename,
+ rcvmsg->msg_data.response.buff_offset);
+ memcpy_fromshmem(card,
+ skb_put(skb, rcvmsg->msg_data.response.msg_len),
+ (char *)rcvmsg->msg_data.response.buff_offset,
+ rcvmsg->msg_data.response.msg_len);
+ sc_adapter[card]->card->rcvcallb_skb(sc_adapter[card]->driverId,
+ rcvmsg->phy_link_no - 1, skb);
+
+ case 0x03:
+ /*
+ * Recycle the buffer
+ */
+ pr_debug("%s: buffer size : %d\n",
+ sc_adapter[card]->devicename, BUFFER_SIZE);
+/* memset_shmem(card, rcvmsg->msg_data.response.buff_offset, 0, BUFFER_SIZE); */
+ newll.buff_offset = rcvmsg->msg_data.response.buff_offset;
+ newll.msg_len = BUFFER_SIZE;
+ pr_debug("%s: recycled buffer at offset 0x%lx size %d\n",
+ sc_adapter[card]->devicename,
+ newll.buff_offset, newll.msg_len);
+ sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
+ rcvmsg->phy_link_no, sizeof(LLData), (unsigned int *)&newll);
+ }
+
+}
+
+int setup_buffers(int card, int c)
+{
+ unsigned int nBuffers, i, cBase;
+ unsigned int buffer_size;
+ LLData RcvBuffOffset;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ /*
+ * Calculate the buffer offsets (send/recv/send/recv)
+ */
+ pr_debug("%s: setting up channel buffer space in shared RAM\n",
+ sc_adapter[card]->devicename);
+ buffer_size = BUFFER_SIZE;
+ nBuffers = ((sc_adapter[card]->ramsize - BUFFER_BASE) / buffer_size) / 2;
+ nBuffers = nBuffers > BUFFERS_MAX ? BUFFERS_MAX : nBuffers;
+ pr_debug("%s: calculating buffer space: %d buffers, %d big\n",
+ sc_adapter[card]->devicename,
+ nBuffers, buffer_size);
+ if (nBuffers < 2) {
+ pr_debug("%s: not enough buffer space\n",
+ sc_adapter[card]->devicename);
+ return -1;
+ }
+ cBase = (nBuffers * buffer_size) * (c - 1);
+ pr_debug("%s: channel buffer offset from shared RAM: 0x%x\n",
+ sc_adapter[card]->devicename, cBase);
+ sc_adapter[card]->channel[c - 1].first_sendbuf = BUFFER_BASE + cBase;
+ sc_adapter[card]->channel[c - 1].num_sendbufs = nBuffers / 2;
+ sc_adapter[card]->channel[c - 1].free_sendbufs = nBuffers / 2;
+ sc_adapter[card]->channel[c - 1].next_sendbuf = 0;
+ pr_debug("%s: send buffer setup complete: first=0x%lx n=%d f=%d, nxt=%d\n",
+ sc_adapter[card]->devicename,
+ sc_adapter[card]->channel[c - 1].first_sendbuf,
+ sc_adapter[card]->channel[c - 1].num_sendbufs,
+ sc_adapter[card]->channel[c - 1].free_sendbufs,
+ sc_adapter[card]->channel[c - 1].next_sendbuf);
+
+ /*
+ * Prep the receive buffers
+ */
+ pr_debug("%s: adding %d RecvBuffers:\n",
+ sc_adapter[card]->devicename, nBuffers / 2);
+ for (i = 0; i < nBuffers / 2; i++) {
+ RcvBuffOffset.buff_offset =
+ ((sc_adapter[card]->channel[c - 1].first_sendbuf +
+ (nBuffers / 2) * buffer_size) + (buffer_size * i));
+ RcvBuffOffset.msg_len = buffer_size;
+ pr_debug("%s: adding RcvBuffer #%d offset=0x%lx sz=%d bufsz:%d\n",
+ sc_adapter[card]->devicename,
+ i + 1, RcvBuffOffset.buff_offset,
+ RcvBuffOffset.msg_len, buffer_size);
+ sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
+ c, sizeof(LLData), (unsigned int *)&RcvBuffOffset);
+ }
+ return 0;
+}
diff --git a/kernel/drivers/isdn/sc/scioc.h b/kernel/drivers/isdn/sc/scioc.h
new file mode 100644
index 000000000..a50e14377
--- /dev/null
+++ b/kernel/drivers/isdn/sc/scioc.h
@@ -0,0 +1,110 @@
+#ifndef __ISDN_SC_SCIOC_H__
+#define __ISDN_SC_SCIOC_H__
+
+/*
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * IOCTL Command Codes
+ */
+#define SCIOCLOAD 0x01 /* Load a firmware record */
+#define SCIOCRESET 0x02 /* Perform hard reset */
+#define SCIOCDEBUG 0x03 /* Set debug level */
+#define SCIOCREV 0x04 /* Get driver revision(s) */
+#define SCIOCSTART 0x05 /* Start the firmware */
+#define SCIOCGETSWITCH 0x06 /* Get switch type */
+#define SCIOCSETSWITCH 0x07 /* Set switch type */
+#define SCIOCGETSPID 0x08 /* Get channel SPID */
+#define SCIOCSETSPID 0x09 /* Set channel SPID */
+#define SCIOCGETDN 0x0A /* Get channel DN */
+#define SCIOCSETDN 0x0B /* Set channel DN */
+#define SCIOCTRACE 0x0C /* Toggle trace mode */
+#define SCIOCSTAT 0x0D /* Get line status */
+#define SCIOCGETSPEED 0x0E /* Set channel speed */
+#define SCIOCSETSPEED 0x0F /* Set channel speed */
+#define SCIOCLOOPTST 0x10 /* Perform loopback test */
+
+typedef struct {
+ int device;
+ int channel;
+ unsigned long command;
+ void __user *dataptr;
+} scs_ioctl;
+
+/* Size of strings */
+#define SCIOC_SPIDSIZE 49
+#define SCIOC_DNSIZE SCIOC_SPIDSIZE
+#define SCIOC_REVSIZE SCIOC_SPIDSIZE
+#define SCIOC_SRECSIZE 49
+
+typedef struct {
+ unsigned long tx_good;
+ unsigned long tx_bad;
+ unsigned long rx_good;
+ unsigned long rx_bad;
+} ChLinkStats;
+
+typedef struct {
+ char spid[49];
+ char dn[49];
+ char call_type;
+ char phy_stat;
+ ChLinkStats link_stats;
+} BRIStat;
+
+typedef BRIStat POTStat;
+
+typedef struct {
+ char call_type;
+ char call_state;
+ char serv_state;
+ char phy_stat;
+ ChLinkStats link_stats;
+} PRIStat;
+
+typedef char PRIInfo;
+typedef char BRIInfo;
+typedef char POTInfo;
+
+
+typedef struct {
+ char acfa_nos;
+ char acfa_ais;
+ char acfa_los;
+ char acfa_rra;
+ char acfa_slpp;
+ char acfa_slpn;
+ char acfa_fsrf;
+} ACFAStat;
+
+typedef struct {
+ unsigned char modelid;
+ char serial_no[13];
+ char part_no[13];
+ char load_ver[11];
+ char proc_ver[11];
+ int iobase;
+ long rambase;
+ char irq;
+ long ramsize;
+ char interface;
+ char switch_type;
+ char l1_status;
+ char l2_status;
+ ChLinkStats dch_stats;
+ ACFAStat AcfaStats;
+ union {
+ PRIStat pristats[23];
+ BRIStat bristats[2];
+ POTStat potsstats[2];
+ } status;
+ union {
+ PRIInfo priinfo;
+ BRIInfo briinfo;
+ POTInfo potsinfo;
+ } info;
+} boardInfo;
+
+#endif /* __ISDN_SC_SCIOC_H__ */
diff --git a/kernel/drivers/isdn/sc/shmem.c b/kernel/drivers/isdn/sc/shmem.c
new file mode 100644
index 000000000..d24506ceb
--- /dev/null
+++ b/kernel/drivers/isdn/sc/shmem.c
@@ -0,0 +1,138 @@
+/* $Id: shmem.c,v 1.2.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * Card functions implementing ISDN4Linux functionality
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h" /* This must be first */
+#include "hardware.h"
+#include "card.h"
+
+/*
+ *
+ */
+void memcpy_toshmem(int card, void *dest, const void *src, size_t n)
+{
+ unsigned long flags;
+ unsigned char ch;
+ unsigned long dest_rem = ((unsigned long) dest) % 0x4000;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return;
+ }
+
+ if (n > SRAM_PAGESIZE)
+ return;
+
+ /*
+ * determine the page to load from the address
+ */
+ ch = (unsigned long) dest / SRAM_PAGESIZE;
+ pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch);
+ /*
+ * Block interrupts and load the page
+ */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+
+ outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+ sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+ memcpy_toio((void __iomem *)(sc_adapter[card]->rambase + dest_rem), src, n);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+ pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename,
+ ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80);
+ pr_debug("%s: copying %zu bytes from %#lx to %#lx\n",
+ sc_adapter[card]->devicename, n,
+ (unsigned long) src,
+ sc_adapter[card]->rambase + ((unsigned long) dest % 0x4000));
+}
+
+/*
+ * Reverse of above
+ */
+void memcpy_fromshmem(int card, void *dest, const void *src, size_t n)
+{
+ unsigned long flags;
+ unsigned char ch;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return;
+ }
+
+ if (n > SRAM_PAGESIZE) {
+ return;
+ }
+
+ /*
+ * determine the page to load from the address
+ */
+ ch = (unsigned long) src / SRAM_PAGESIZE;
+ pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch);
+
+
+ /*
+ * Block interrupts and load the page
+ */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+
+ outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+ sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+ memcpy_fromio(dest, (void *)(sc_adapter[card]->rambase +
+ ((unsigned long) src % 0x4000)), n);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+ pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename,
+ ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80);
+/* pr_debug("%s: copying %d bytes from %#x to %#x\n",
+ sc_adapter[card]->devicename, n,
+ sc_adapter[card]->rambase + ((unsigned long) src %0x4000), (unsigned long) dest); */
+}
+
+#if 0
+void memset_shmem(int card, void *dest, int c, size_t n)
+{
+ unsigned long flags;
+ unsigned char ch;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return;
+ }
+
+ if (n > SRAM_PAGESIZE) {
+ return;
+ }
+
+ /*
+ * determine the page to load from the address
+ */
+ ch = (unsigned long) dest / SRAM_PAGESIZE;
+ pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename, ch);
+
+ /*
+ * Block interrupts and load the page
+ */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+
+ outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+ sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+ memset_io(sc_adapter[card]->rambase +
+ ((unsigned long) dest % 0x4000), c, n);
+ pr_debug("%s: set page to %#x\n", sc_adapter[card]->devicename,
+ ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+}
+#endif /* 0 */
diff --git a/kernel/drivers/isdn/sc/timer.c b/kernel/drivers/isdn/sc/timer.c
new file mode 100644
index 000000000..6fbac2230
--- /dev/null
+++ b/kernel/drivers/isdn/sc/timer.c
@@ -0,0 +1,122 @@
+/* $Id: timer.c,v 1.3.6.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+
+/*
+ * Write the proper values into the I/O ports following a reset
+ */
+static void setup_ports(int card)
+{
+
+ outb((sc_adapter[card]->rambase >> 12), sc_adapter[card]->ioport[EXP_BASE]);
+
+ /* And the IRQ */
+ outb((sc_adapter[card]->interrupt | 0x80),
+ sc_adapter[card]->ioport[IRQ_SELECT]);
+}
+
+/*
+ * Timed function to check the status of a previous reset
+ * Must be very fast as this function runs in the context of
+ * an interrupt handler.
+ *
+ * Setup the ioports for the board that were cleared by the reset.
+ * Then, check to see if the signate has been set. Next, set the
+ * signature to a known value and issue a startproc if needed.
+ */
+void sc_check_reset(unsigned long data)
+{
+ unsigned long flags;
+ unsigned long sig;
+ int card = (unsigned int) data;
+
+ pr_debug("%s: check_timer timer called\n",
+ sc_adapter[card]->devicename);
+
+ /* Setup the io ports */
+ setup_ports(card);
+
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+ outb(sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport],
+ (sc_adapter[card]->shmem_magic >> 14) | 0x80);
+ sig = (unsigned long) *((unsigned long *)(sc_adapter[card]->rambase + SIG_OFFSET));
+
+ /* check the signature */
+ if (sig == SIGNATURE) {
+ flushreadfifo(card);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+ /* See if we need to do a startproc */
+ if (sc_adapter[card]->StartOnReset)
+ startproc(card);
+ } else {
+ pr_debug("%s: No signature yet, waiting another %lu jiffies.\n",
+ sc_adapter[card]->devicename, CHECKRESET_TIME);
+ mod_timer(&sc_adapter[card]->reset_timer, jiffies + CHECKRESET_TIME);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+ }
+}
+
+/*
+ * Timed function to check the status of a previous reset
+ * Must be very fast as this function runs in the context of
+ * an interrupt handler.
+ *
+ * Send check sc_adapter->phystat to see if the channels are up
+ * If they are, tell ISDN4Linux that the board is up. If not,
+ * tell IADN4Linux that it is up. Always reset the timer to
+ * fire again (endless loop).
+ */
+void check_phystat(unsigned long data)
+{
+ unsigned long flags;
+ int card = (unsigned int) data;
+
+ pr_debug("%s: Checking status...\n", sc_adapter[card]->devicename);
+ /*
+ * check the results of the last PhyStat and change only if
+ * has changed drastically
+ */
+ if (sc_adapter[card]->nphystat && !sc_adapter[card]->phystat) { /* All is well */
+ pr_debug("PhyStat transition to RUN\n");
+ pr_info("%s: Switch contacted, transmitter enabled\n",
+ sc_adapter[card]->devicename);
+ indicate_status(card, ISDN_STAT_RUN, 0, NULL);
+ }
+ else if (!sc_adapter[card]->nphystat && sc_adapter[card]->phystat) { /* All is not well */
+ pr_debug("PhyStat transition to STOP\n");
+ pr_info("%s: Switch connection lost, transmitter disabled\n",
+ sc_adapter[card]->devicename);
+
+ indicate_status(card, ISDN_STAT_STOP, 0, NULL);
+ }
+
+ sc_adapter[card]->phystat = sc_adapter[card]->nphystat;
+
+ /* Reinitialize the timer */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+ mod_timer(&sc_adapter[card]->stat_timer, jiffies + CHECKSTAT_TIME);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+
+ /* Send a new cePhyStatus message */
+ sendmessage(card, CEPID, ceReqTypePhy, ceReqClass2,
+ ceReqPhyStatus, 0, 0, NULL);
+}