aboutsummaryrefslogtreecommitdiff
path: root/drivers/isdn/sc
diff options
context:
space:
mode:
authorLinus Torvalds2005-04-16 15:20:36 -0700
committerLinus Torvalds2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/sc
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/isdn/sc')
-rw-r--r--drivers/isdn/sc/Kconfig12
-rw-r--r--drivers/isdn/sc/Makefile10
-rw-r--r--drivers/isdn/sc/card.h101
-rw-r--r--drivers/isdn/sc/command.c441
-rw-r--r--drivers/isdn/sc/debug.c46
-rw-r--r--drivers/isdn/sc/debug.h19
-rw-r--r--drivers/isdn/sc/event.c69
-rw-r--r--drivers/isdn/sc/hardware.h110
-rw-r--r--drivers/isdn/sc/includes.h18
-rw-r--r--drivers/isdn/sc/init.c571
-rw-r--r--drivers/isdn/sc/interrupt.c260
-rw-r--r--drivers/isdn/sc/ioctl.c601
-rw-r--r--drivers/isdn/sc/message.c241
-rw-r--r--drivers/isdn/sc/message.h245
-rw-r--r--drivers/isdn/sc/packet.c231
-rw-r--r--drivers/isdn/sc/scioc.h105
-rw-r--r--drivers/isdn/sc/shmem.c143
-rw-r--r--drivers/isdn/sc/timer.c147
18 files changed, 3370 insertions, 0 deletions
diff --git a/drivers/isdn/sc/Kconfig b/drivers/isdn/sc/Kconfig
new file mode 100644
index 000000000000..5346e33d816c
--- /dev/null
+++ b/drivers/isdn/sc/Kconfig
@@ -0,0 +1,12 @@
+#
+# Config.in for Spellcaster ISDN driver
+#
+config ISDN_DRV_SC
+ tristate "Spellcaster support"
+ depends on ISDN_I4L && 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/drivers/isdn/sc/Makefile b/drivers/isdn/sc/Makefile
new file mode 100644
index 000000000000..9cc474cd0c44
--- /dev/null
+++ b/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 debug.o packet.o command.o event.o \
+ ioctl.o interrupt.o message.o timer.o
diff --git a/drivers/isdn/sc/card.h b/drivers/isdn/sc/card.h
new file mode 100644
index 000000000000..8e44928cdf1c
--- /dev/null
+++ b/drivers/isdn/sc/card.h
@@ -0,0 +1,101 @@
+/* $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 "message.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;
+
+#endif /* CARD_H */
diff --git a/drivers/isdn/sc/command.c b/drivers/isdn/sc/command.c
new file mode 100644
index 000000000000..b2c4eac7cef5
--- /dev/null
+++ b/drivers/isdn/sc/command.c
@@ -0,0 +1,441 @@
+/* $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"
+
+int dial(int card, unsigned long channel, setup_parm setup);
+int hangup(int card, unsigned long channel);
+int answer(int card, unsigned long channel);
+int clreaz(int card, unsigned long channel);
+int seteaz(int card, unsigned long channel, char *);
+int setl2(int card, unsigned long arg);
+int setl3(int card, unsigned long arg);
+int acceptb(int card, unsigned long channel);
+
+extern int cinst;
+extern board *sc_adapter[];
+
+extern int sc_ioctl(int, scs_ioctl *);
+extern int setup_buffers(int, int, unsigned int);
+extern int indicate_status(int, int,ulong,char*);
+extern void check_reset(unsigned long);
+extern int send_and_receive(int, unsigned int, unsigned char, unsigned char,
+ unsigned char, unsigned char, unsigned char, unsigned char *,
+ RspMessage *, int);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int, unsigned int, unsigned int *);
+extern inline void pullphone(char *, char *);
+
+#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;
+ }
+
+ pr_debug("%s: Received %s command from Link Layer\n",
+ sc_adapter[card]->devicename, commands[cmd->command]);
+
+ /*
+ * 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%x\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;
+}
+
+/*
+ * Confirm our ability to communicate with the board. This test assumes no
+ * other message activity is present
+ */
+int loopback(int card)
+{
+
+ int status;
+ static char testmsg[] = "Test Message";
+ RspMessage rspmsg;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ pr_debug("%s: Sending loopback message\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Send the loopback message to confirm that memory transfer is
+ * operational
+ */
+ status = send_and_receive(card, CMPID, cmReqType1,
+ cmReqClass0,
+ cmReqMsgLpbk,
+ 0,
+ (unsigned char) strlen(testmsg),
+ (unsigned char *)testmsg,
+ &rspmsg, SAR_TIMEOUT);
+
+
+ if (!status) {
+ pr_debug("%s: Loopback message successfully sent\n",
+ sc_adapter[card]->devicename);
+ if(strcmp(rspmsg.msg_data.byte_array, testmsg)) {
+ pr_debug("%s: Loopback return != sent\n",
+ sc_adapter[card]->devicename);
+ return -EIO;
+ }
+ return 0;
+ }
+ else {
+ pr_debug("%s: Send loopback message failed\n",
+ sc_adapter[card]->devicename);
+ return -EIO;
+ }
+
+}
+
+/*
+ * 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;
+}
+
+
+int loadproc(int card, char *data)
+{
+ return -1;
+}
+
+
+/*
+ * Dials the number passed in
+ */
+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 %d\n",
+ sc_adapter[card]->devicename, Phone, channel+1);
+
+ return status;
+}
+
+/*
+ * Answer an incoming call
+ */
+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, BUFFER_SIZE)) {
+ hangup(card, channel+1);
+ return -ENOBUFS;
+ }
+
+ indicate_status(card, ISDN_STAT_BCONN,channel,NULL);
+ pr_debug("%s: Answered incoming call on channel %s\n",
+ sc_adapter[card]->devicename, channel+1);
+ return 0;
+}
+
+/*
+ * Hangup up the call on specified channel
+ */
+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 %d\n",
+ sc_adapter[card]->devicename, channel+1);
+ return status;
+}
+
+/*
+ * Set the layer 2 protocol (X.25, HDLC, Raw)
+ */
+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;
+ pr_debug("%s: Level 2 protocol for channel %d set to %s from %d\n",
+ sc_adapter[card]->devicename, channel+1,
+ l2protos[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
+ */
+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;
+ pr_debug("%s: Level 3 protocol for channel %d set to %s\n",
+ sc_adapter[card]->devicename, channel+1, l3protos[protocol]);
+ return 0;
+}
+
+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, BUFFER_SIZE))
+ {
+ hangup(card, channel+1);
+ return -ENOBUFS;
+ }
+
+ pr_debug("%s: B-Channel connection accepted on channel %d\n",
+ sc_adapter[card]->devicename, channel+1);
+ indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
+ return 0;
+}
+
+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 %d\n",
+ sc_adapter[card]->devicename, arg+1);
+ return 0;
+}
+
+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 %d 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 = 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/drivers/isdn/sc/debug.c b/drivers/isdn/sc/debug.c
new file mode 100644
index 000000000000..1a992a75868b
--- /dev/null
+++ b/drivers/isdn/sc/debug.c
@@ -0,0 +1,46 @@
+/* $Id: debug.c,v 1.5.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 <linux/kernel.h>
+#include <linux/string.h>
+
+int dbg_level = 0;
+static char dbg_funcname[255];
+
+void dbg_endfunc(void)
+{
+ if (dbg_level) {
+ printk("<-- Leaving function %s\n", dbg_funcname);
+ strcpy(dbg_funcname, "");
+ }
+}
+
+void dbg_func(char *func)
+{
+ strcpy(dbg_funcname, func);
+ if(dbg_level)
+ printk("--> Entering function %s\n", dbg_funcname);
+}
+
+inline void pullphone(char *dn, char *str)
+{
+ int i = 0;
+
+ while(dn[i] != ',')
+ str[i] = dn[i], i++;
+ str[i] = 0x0;
+}
diff --git a/drivers/isdn/sc/debug.h b/drivers/isdn/sc/debug.h
new file mode 100644
index 000000000000..e9db96ede4b2
--- /dev/null
+++ b/drivers/isdn/sc/debug.h
@@ -0,0 +1,19 @@
+/* $Id: debug.h,v 1.2.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
+ */
+
+#define REQUEST_IRQ(a,b,c,d,e) request_irq(a,b,c,d,e)
+#define FREE_IRQ(a,b) free_irq(a,b)
diff --git a/drivers/isdn/sc/event.c b/drivers/isdn/sc/event.c
new file mode 100644
index 000000000000..5b8c7c1a7663
--- /dev/null
+++ b/drivers/isdn/sc/event.c
@@ -0,0 +1,69 @@
+/* $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"
+
+extern int cinst;
+extern board *sc_adapter[];
+
+#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;
+
+ pr_debug("%s: Indicating event %s on Channel %d\n",
+ sc_adapter[card]->devicename, events[event-256], Channel);
+ 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:
+ strcpy(cmd.parm.num, Data);
+ }
+ }
+
+ cmd.command = event;
+ cmd.driver = sc_adapter[card]->driverId;
+ cmd.arg = Channel;
+ return sc_adapter[card]->card->statcallb(&cmd);
+}
diff --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h
new file mode 100644
index 000000000000..9e6d5302bf8e
--- /dev/null
+++ b/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 /* Ammount 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/drivers/isdn/sc/includes.h b/drivers/isdn/sc/includes.h
new file mode 100644
index 000000000000..4611da6e9231
--- /dev/null
+++ b/drivers/isdn/sc/includes.h
@@ -0,0 +1,18 @@
+/*
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/version.h>
+#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>
+#include "debug.h"
diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c
new file mode 100644
index 000000000000..efefedea37b9
--- /dev/null
+++ b/drivers/isdn/sc/init.c
@@ -0,0 +1,571 @@
+/*
+ * 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 "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";
+const char version[] = "2.0b1";
+
+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 int do_reset = 0;
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(ram, int, NULL, 0);
+module_param(do_reset, bool, 0);
+
+static int sup_irq[] = { 11, 10, 9, 5, 12, 14, 7, 3, 4, 6 };
+#define MAX_IRQS 10
+
+extern irqreturn_t interrupt_handler(int, void *, struct pt_regs *);
+extern int sndpkt(int, int, int, struct sk_buff *);
+extern int command(isdn_ctrl *);
+extern int indicate_status(int, int, ulong, char*);
+extern int reset(int);
+
+int identify_board(unsigned long, unsigned int);
+
+int irq_supported(int irq_x)
+{
+ int i;
+ for(i=0 ; i < MAX_IRQS ; i++) {
+ if(sup_irq[i] == irq_x)
+ return 1;
+ }
+ return 0;
+}
+
+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("check_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 0x%x is in use.\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");
+ continue;
+ }
+ }
+ else {
+ /*
+ * Yes, probe for I/O Base
+ */
+ if(probe_exhasted) {
+ pr_debug("All probe addresses exhasted, 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%x, %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%x 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(" check_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(" Unidentifed 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%x\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 = kmalloc(sizeof(isdn_if), GFP_KERNEL);
+ if (interface == NULL) {
+ /*
+ * Oops, can't malloc isdn_if
+ */
+ continue;
+ }
+ memset(interface, 0, sizeof(isdn_if));
+
+ 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] = kmalloc(sizeof(board), GFP_KERNEL);
+ if (sc_adapter[cinst] == NULL) {
+ /*
+ * Oops, can't alloc memory for the board
+ */
+ kfree(interface);
+ continue;
+ }
+ memset(sc_adapter[cinst], 0, sizeof(board));
+ 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 = kmalloc(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;
+ }
+ memset(sc_adapter[cinst]->channel, 0, sizeof(bchan) * channels);
+
+ /*
+ * Lock down the hardware resources
+ */
+ sc_adapter[cinst]->interrupt = irq[b];
+ if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler,
+ SA_INTERRUPT, interface->id, NULL))
+ {
+ 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(&(sc_adapter[i]->reset_timer));
+ del_timer(&(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");
+}
+
+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%x 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%x\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%x\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%x\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) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(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: %d, 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/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c
new file mode 100644
index 000000000000..e5e164aca7fa
--- /dev/null
+++ b/drivers/isdn/sc/interrupt.c
@@ -0,0 +1,260 @@
+/* $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>
+
+extern int indicate_status(int, int, ulong, char *);
+extern void check_phystat(unsigned long);
+extern int receivemessage(int, RspMessage *);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int, unsigned int, unsigned int *);
+extern void rcvpkt(int, RspMessage *);
+
+extern int cinst;
+extern board *sc_adapter[];
+
+int get_card_from_irq(int irq)
+{
+ int i;
+
+ for(i = 0 ; i < cinst ; i++) {
+ if(sc_adapter[i]->interrupt == irq)
+ return i;
+ }
+ return -1;
+}
+
+/*
+ *
+ */
+irqreturn_t interrupt_handler(int interrupt, void *cardptr, struct pt_regs *regs)
+{
+
+ RspMessage rcvmsg;
+ int channel;
+ int card;
+
+ card = get_card_from_irq(interrupt);
+
+ 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%x\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)
+ {
+ pr_debug("%s: Got Incoming Call\n",
+ sc_adapter[card]->devicename);
+ strcpy(setup.phone,&(rcvmsg.msg_data.byte_array[4]));
+ strcpy(setup.eazmsn,
+ sc_adapter[card]->channel[rcvmsg.phy_link_no-1].dn);
+ 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)){
+ strcpy(sc_adapter[card]->channel[rcvmsg.phy_link_no-1].dn,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/drivers/isdn/sc/ioctl.c b/drivers/isdn/sc/ioctl.c
new file mode 100644
index 000000000000..1371a990416a
--- /dev/null
+++ b/drivers/isdn/sc/ioctl.c
@@ -0,0 +1,601 @@
+/*
+ * 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"
+
+extern int indicate_status(int, int, unsigned long, char *);
+extern int startproc(int);
+extern int loadproc(int, char *record);
+extern int reset(int);
+extern int send_and_receive(int, unsigned int, unsigned char,unsigned char,
+ unsigned char,unsigned char,
+ unsigned char, unsigned char *, RspMessage *, int);
+
+extern board *sc_adapter[];
+
+
+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;
+ 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, sizeof(srec))) {
+ kfree(rcvmsg);
+ kfree(srec);
+ return -EFAULT;
+ }
+
+ status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc,
+ 0, sizeof(srec), 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:
+ {
+ 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 = kmalloc(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(rcvmsg);
+ return status;
+ }
+ strcpy(spid, rcvmsg->msg_data.byte_array);
+
+ /*
+ * 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);
+
+ spid = kmalloc(SCIOC_SPIDSIZE, GFP_KERNEL);
+ if(!spid) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+
+ /*
+ * Get the spid from user space
+ */
+ if (copy_from_user(spid, data->dataptr, SCIOC_SPIDSIZE)) {
+ kfree(rcvmsg);
+ return -EFAULT;
+ }
+
+ 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 = kmalloc(SCIOC_DNSIZE, GFP_KERNEL);
+ if (!dn) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+ strcpy(dn, rcvmsg->msg_data.byte_array);
+ 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);
+
+ dn = kmalloc(SCIOC_DNSIZE, GFP_KERNEL);
+ if (!dn) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+ /*
+ * Get the spid from user space
+ */
+ if (copy_from_user(dn, data->dataptr, SCIOC_DNSIZE)) {
+ kfree(rcvmsg);
+ kfree(dn);
+ return -EFAULT;
+ }
+
+ 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 = kmalloc (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;
+}
+
+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/drivers/isdn/sc/message.c b/drivers/isdn/sc/message.c
new file mode 100644
index 000000000000..ca204da3257d
--- /dev/null
+++ b/drivers/isdn/sc/message.c
@@ -0,0 +1,241 @@
+/* $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 "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+extern board *sc_adapter[];
+extern unsigned int cinst;
+
+/*
+ * Obligatory function prototypes
+ */
+extern int indicate_status(int,ulong,char*);
+extern int scm_command(isdn_ctrl *);
+
+
+/*
+ * 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) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(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/drivers/isdn/sc/message.h b/drivers/isdn/sc/message.h
new file mode 100644
index 000000000000..8eb15e7306b2
--- /dev/null
+++ b/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/drivers/isdn/sc/packet.c b/drivers/isdn/sc/packet.c
new file mode 100644
index 000000000000..8e3fac3ba1a1
--- /dev/null
+++ b/drivers/isdn/sc/packet.c
@@ -0,0 +1,231 @@
+/* $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"
+
+extern board *sc_adapter[];
+extern unsigned int cinst;
+
+extern int get_card_from_id(int);
+extern int indicate_status(int, int,ulong, char*);
+extern void memcpy_toshmem(int, void *, const void *, size_t);
+extern void memcpy_fromshmem(int, void *, const void *, size_t);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int, unsigned int, unsigned int *);
+
+int sndpkt(int devId, int channel, 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%x 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%x\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%x 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%x\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%x 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%x 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%x 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;
+}
+
+int print_skb(int card,char *skb_p, int len){
+ int i,data;
+ pr_debug("%s: data at 0x%x len: 0x%x\n", sc_adapter[card]->devicename,
+ skb_p,len);
+ for(i=1;i<=len;i++,skb_p++){
+ data = (int) (0xff & (*skb_p));
+ pr_debug("%s: data = 0x%x", sc_adapter[card]->devicename,data);
+ if(!(i%4))
+ pr_debug(" ");
+ if(!(i%32))
+ pr_debug("\n");
+ }
+ pr_debug("\n");
+ return 0;
+}
+
diff --git a/drivers/isdn/sc/scioc.h b/drivers/isdn/sc/scioc.h
new file mode 100644
index 000000000000..d08e650c7b6a
--- /dev/null
+++ b/drivers/isdn/sc/scioc.h
@@ -0,0 +1,105 @@
+/*
+ * 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;
diff --git a/drivers/isdn/sc/shmem.c b/drivers/isdn/sc/shmem.c
new file mode 100644
index 000000000000..7bc2dfad0775
--- /dev/null
+++ b/drivers/isdn/sc/shmem.c
@@ -0,0 +1,143 @@
+/* $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"
+
+/*
+ * Main adapter array
+ */
+extern board *sc_adapter[];
+extern int cinst;
+
+/*
+ *
+ */
+void memcpy_toshmem(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) 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(sc_adapter[card]->rambase +
+ ((unsigned long) dest % 0x4000), 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 %d bytes from %#x to %#x\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); */
+}
+
+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);
+}
diff --git a/drivers/isdn/sc/timer.c b/drivers/isdn/sc/timer.c
new file mode 100644
index 000000000000..710d0f47ca35
--- /dev/null
+++ b/drivers/isdn/sc/timer.c
@@ -0,0 +1,147 @@
+/* $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"
+
+extern board *sc_adapter[];
+
+extern void flushreadfifo(int);
+extern int startproc(int);
+extern int indicate_status(int, int, unsigned long, char *);
+extern int sendmessage(int, unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int, unsigned int, unsigned int *);
+
+
+/*
+ * Write the proper values into the I/O ports following a reset
+ */
+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 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 %d 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);
+}
+
+/*
+ * When in trace mode, this callback is used to swap the working shared
+ * RAM page to the trace page(s) and process all received messages. It
+ * must be called often enough to get all of the messages out of RAM before
+ * it loops around.
+ * Trace messages are \n terminated strings.
+ * We output the messages in 64 byte chunks through readstat. Each chunk
+ * is scanned for a \n followed by a time stamp. If the timerstamp is older
+ * than the current time, scanning stops and the page and offset are recorded
+ * as the starting point the next time the trace timer is called. The final
+ * step is to restore the working page and reset the timer.
+ */
+void trace_timer(unsigned long data)
+{
+ /* not implemented */
+}