add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
#
# Config.in for HYSDN ISDN driver
#
config HYSDN
tristate "Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)"
depends on m && PROC_FS && PCI
help
Say Y here if you have one of Hypercope's active PCI ISDN cards
Champ, Ergo and Metro. You will then get a module called hysdn.
Please read the file <file:Documentation/isdn/README.hysdn> for more
information.
config HYSDN_CAPI
bool "HYSDN CAPI 2.0 support"
depends on HYSDN && ISDN_CAPI
help
Say Y here if you like to use Hypercope's CAPI 2.0 interface.

View File

@@ -0,0 +1,11 @@
# Makefile for the hysdn ISDN device driver
# Each configuration option enables a list of files.
obj-$(CONFIG_HYSDN) += hysdn.o
# Multipart objects.
hysdn-y := hysdn_procconf.o hysdn_proclog.o boardergo.o \
hysdn_boot.o hysdn_sched.o hysdn_net.o hysdn_init.o
hysdn-$(CONFIG_HYSDN_CAPI) += hycapi.o

View File

@@ -0,0 +1,445 @@
/* $Id: boardergo.c,v 1.5.6.7 2001/11/06 21:58:19 kai Exp $
*
* Linux driver for HYSDN cards, specific routines for ergo type boards.
*
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
* Copyright 1999 by Werner Cornelius (werner@titro.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
* As all Linux supported cards Champ2, Ergo and Metro2/4 use the same
* DPRAM interface and layout with only minor differences all related
* stuff is done here, not in separate modules.
*
*/
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <asm/io.h>
#include "hysdn_defs.h"
#include "boardergo.h"
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
/***************************************************/
/* The cards interrupt handler. Called from system */
/***************************************************/
static irqreturn_t
ergo_interrupt(int intno, void *dev_id)
{
hysdn_card *card = dev_id; /* parameter from irq */
tErgDpram *dpr;
unsigned long flags;
unsigned char volatile b;
if (!card)
return IRQ_NONE; /* error -> spurious interrupt */
if (!card->irq_enabled)
return IRQ_NONE; /* other device interrupting or irq switched off */
spin_lock_irqsave(&card->hysdn_lock, flags); /* no further irqs allowed */
if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) {
spin_unlock_irqrestore(&card->hysdn_lock, flags); /* restore old state */
return IRQ_NONE; /* no interrupt requested by E1 */
}
/* clear any pending ints on the board */
dpr = card->dpram;
b = dpr->ToPcInt; /* clear for ergo */
b |= dpr->ToPcIntMetro; /* same for metro */
b |= dpr->ToHyInt; /* and for champ */
/* start kernel task immediately after leaving all interrupts */
if (!card->hw_lock)
schedule_work(&card->irq_queue);
spin_unlock_irqrestore(&card->hysdn_lock, flags);
return IRQ_HANDLED;
} /* ergo_interrupt */
/******************************************************************************/
/* ergo_irq_bh will be called as part of the kernel clearing its shared work */
/* queue sometime after a call to schedule_work has been made passing our */
/* work_struct. This task is the only one handling data transfer from or to */
/* the card after booting. The task may be queued from everywhere */
/* (interrupts included). */
/******************************************************************************/
static void
ergo_irq_bh(struct work_struct *ugli_api)
{
hysdn_card * card = container_of(ugli_api, hysdn_card, irq_queue);
tErgDpram *dpr;
int again;
unsigned long flags;
if (card->state != CARD_STATE_RUN)
return; /* invalid call */
dpr = card->dpram; /* point to DPRAM */
spin_lock_irqsave(&card->hysdn_lock, flags);
if (card->hw_lock) {
spin_unlock_irqrestore(&card->hysdn_lock, flags); /* hardware currently unavailable */
return;
}
card->hw_lock = 1; /* we now lock the hardware */
do {
again = 0; /* assume loop not to be repeated */
if (!dpr->ToHyFlag) {
/* we are able to send a buffer */
if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel,
ERG_TO_HY_BUF_SIZE)) {
dpr->ToHyFlag = 1; /* enable tx */
again = 1; /* restart loop */
}
} /* we are able to send a buffer */
if (dpr->ToPcFlag) {
/* a message has arrived for us, handle it */
if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) {
dpr->ToPcFlag = 0; /* we worked the data */
again = 1; /* restart loop */
}
} /* a message has arrived for us */
if (again) {
dpr->ToHyInt = 1;
dpr->ToPcInt = 1; /* interrupt to E1 for all cards */
} else
card->hw_lock = 0; /* free hardware again */
} while (again); /* until nothing more to do */
spin_unlock_irqrestore(&card->hysdn_lock, flags);
} /* ergo_irq_bh */
/*********************************************************/
/* stop the card (hardware reset) and disable interrupts */
/*********************************************************/
static void
ergo_stopcard(hysdn_card * card)
{
unsigned long flags;
unsigned char val;
hysdn_net_release(card); /* first release the net device if existing */
#ifdef CONFIG_HYSDN_CAPI
hycapi_capi_stop(card);
#endif /* CONFIG_HYSDN_CAPI */
spin_lock_irqsave(&card->hysdn_lock, flags);
val = bytein(card->iobase + PCI9050_INTR_REG); /* get actual value */
val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1); /* mask irq */
byteout(card->iobase + PCI9050_INTR_REG, val);
card->irq_enabled = 0;
byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET); /* reset E1 processor */
card->state = CARD_STATE_UNUSED;
card->err_log_state = ERRLOG_STATE_OFF; /* currently no log active */
spin_unlock_irqrestore(&card->hysdn_lock, flags);
} /* ergo_stopcard */
/**************************************************************************/
/* enable or disable the cards error log. The event is queued if possible */
/**************************************************************************/
static void
ergo_set_errlog_state(hysdn_card * card, int on)
{
unsigned long flags;
if (card->state != CARD_STATE_RUN) {
card->err_log_state = ERRLOG_STATE_OFF; /* must be off */
return;
}
spin_lock_irqsave(&card->hysdn_lock, flags);
if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) ||
((card->err_log_state == ERRLOG_STATE_ON) && on)) {
spin_unlock_irqrestore(&card->hysdn_lock, flags);
return; /* nothing to do */
}
if (on)
card->err_log_state = ERRLOG_STATE_START; /* request start */
else
card->err_log_state = ERRLOG_STATE_STOP; /* request stop */
spin_unlock_irqrestore(&card->hysdn_lock, flags);
schedule_work(&card->irq_queue);
} /* ergo_set_errlog_state */
/******************************************/
/* test the cards RAM and return 0 if ok. */
/******************************************/
static const char TestText[36] = "This Message is filler, why read it";
static int
ergo_testram(hysdn_card * card)
{
tErgDpram *dpr = card->dpram;
memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable)); /* clear all Traps */
dpr->ToHyInt = 1; /* E1 INTR state forced */
memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
sizeof(TestText));
if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
sizeof(TestText)))
return (-1);
memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
sizeof(TestText));
if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
sizeof(TestText)))
return (-1);
return (0);
} /* ergo_testram */
/*****************************************************************************/
/* this function is intended to write stage 1 boot image to the cards buffer */
/* this is done in two steps. First the 1024 hi-words are written (offs=0), */
/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the */
/* PCI-write-buffers flushed and the card is taken out of reset. */
/* The function then waits for a reaction of the E1 processor or a timeout. */
/* Negative return values are interpreted as errors. */
/*****************************************************************************/
static int
ergo_writebootimg(struct HYSDN_CARD *card, unsigned char *buf,
unsigned long offs)
{
unsigned char *dst;
tErgDpram *dpram;
int cnt = (BOOT_IMG_SIZE >> 2); /* number of words to move and swap (byte order!) */
if (card->debug_flags & LOG_POF_CARD)
hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs);
dst = card->dpram; /* pointer to start of DPRAM */
dst += (offs + ERG_DPRAM_FILL_SIZE); /* offset in the DPRAM */
while (cnt--) {
*dst++ = *(buf + 1); /* high byte */
*dst++ = *buf; /* low byte */
dst += 2; /* point to next longword */
buf += 2; /* buffer only filled with words */
}
/* if low words (offs = 2) have been written, clear the rest of the DPRAM, */
/* flush the PCI-write-buffer and take the E1 out of reset */
if (offs) {
memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE); /* fill the DPRAM still not cleared */
dpram = card->dpram; /* get pointer to dpram structure */
dpram->ToHyNoDpramErrLog = 0xFF; /* write a dpram register */
while (!dpram->ToHyNoDpramErrLog); /* reread volatile register to flush PCI */
byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN); /* start E1 processor */
/* the interrupts are still masked */
msleep_interruptible(20); /* Timeout 20ms */
if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) {
if (card->debug_flags & LOG_POF_CARD)
hysdn_addlog(card, "ERGO: write bootldr no answer");
return (-ERR_BOOTIMG_FAIL);
}
} /* start_boot_img */
return (0); /* successful */
} /* ergo_writebootimg */
/********************************************************************************/
/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */
/* using the boot spool mechanism. If everything works fine 0 is returned. In */
/* case of errors a negative error value is returned. */
/********************************************************************************/
static int
ergo_writebootseq(struct HYSDN_CARD *card, unsigned char *buf, int len)
{
tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram;
unsigned char *dst;
unsigned char buflen;
int nr_write;
unsigned char tmp_rdptr;
unsigned char wr_mirror;
int i;
if (card->debug_flags & LOG_POF_CARD)
hysdn_addlog(card, "ERGO: write boot seq len=%d ", len);
dst = sp->Data; /* point to data in spool structure */
buflen = sp->Len; /* maximum len of spooled data */
wr_mirror = sp->WrPtr; /* only once read */
/* try until all bytes written or error */
i = 0x1000; /* timeout value */
while (len) {
/* first determine the number of bytes that may be buffered */
do {
tmp_rdptr = sp->RdPtr; /* first read the pointer */
i--; /* decrement timeout */
} while (i && (tmp_rdptr != sp->RdPtr)); /* wait for stable pointer */
if (!i) {
if (card->debug_flags & LOG_POF_CARD)
hysdn_addlog(card, "ERGO: write boot seq timeout");
return (-ERR_BOOTSEQ_FAIL); /* value not stable -> timeout */
}
if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0)
nr_write += buflen; /* now we got number of free bytes - 1 in buffer */
if (!nr_write)
continue; /* no free bytes in buffer */
if (nr_write > len)
nr_write = len; /* limit if last few bytes */
i = 0x1000; /* reset timeout value */
/* now we know how much bytes we may put in the puffer */
len -= nr_write; /* we savely could adjust len before output */
while (nr_write--) {
*(dst + wr_mirror) = *buf++; /* output one byte */
if (++wr_mirror >= buflen)
wr_mirror = 0;
sp->WrPtr = wr_mirror; /* announce the next byte to E1 */
} /* while (nr_write) */
} /* while (len) */
return (0);
} /* ergo_writebootseq */
/***********************************************************************************/
/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */
/* boot process. If the process has been successful 0 is returned otherwise a */
/* negative error code is returned. */
/***********************************************************************************/
static int
ergo_waitpofready(struct HYSDN_CARD *card)
{
tErgDpram *dpr = card->dpram; /* pointer to DPRAM structure */
int timecnt = 10000 / 50; /* timeout is 10 secs max. */
unsigned long flags;
int msg_size;
int i;
if (card->debug_flags & LOG_POF_CARD)
hysdn_addlog(card, "ERGO: waiting for pof ready");
while (timecnt--) {
/* wait until timeout */
if (dpr->ToPcFlag) {
/* data has arrived */
if ((dpr->ToPcChannel != CHAN_SYSTEM) ||
(dpr->ToPcSize < MIN_RDY_MSG_SIZE) ||
(dpr->ToPcSize > MAX_RDY_MSG_SIZE) ||
((*(unsigned long *) dpr->ToPcBuf) != RDY_MAGIC))
break; /* an error occurred */
/* Check for additional data delivered during SysReady */
msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE;
if (msg_size > 0)
if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size))
break;
if (card->debug_flags & LOG_POF_RECORD)
hysdn_addlog(card, "ERGO: pof boot success");
spin_lock_irqsave(&card->hysdn_lock, flags);
card->state = CARD_STATE_RUN; /* now card is running */
/* enable the cards interrupt */
byteout(card->iobase + PCI9050_INTR_REG,
bytein(card->iobase + PCI9050_INTR_REG) |
(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1));
card->irq_enabled = 1; /* we are ready to receive interrupts */
dpr->ToPcFlag = 0; /* reset data indicator */
dpr->ToHyInt = 1;
dpr->ToPcInt = 1; /* interrupt to E1 for all cards */
spin_unlock_irqrestore(&card->hysdn_lock, flags);
if ((hynet_enable & (1 << card->myid))
&& (i = hysdn_net_create(card)))
{
ergo_stopcard(card);
card->state = CARD_STATE_BOOTERR;
return (i);
}
#ifdef CONFIG_HYSDN_CAPI
if((i = hycapi_capi_create(card))) {
printk(KERN_WARNING "HYSDN: failed to create capi-interface.\n");
}
#endif /* CONFIG_HYSDN_CAPI */
return (0); /* success */
} /* data has arrived */
msleep_interruptible(50); /* Timeout 50ms */
} /* wait until timeout */
if (card->debug_flags & LOG_POF_CARD)
hysdn_addlog(card, "ERGO: pof boot ready timeout");
return (-ERR_POF_TIMEOUT);
} /* ergo_waitpofready */
/************************************************************************************/
/* release the cards hardware. Before releasing do a interrupt disable and hardware */
/* reset. Also unmap dpram. */
/* Use only during module release. */
/************************************************************************************/
static void
ergo_releasehardware(hysdn_card * card)
{
ergo_stopcard(card); /* first stop the card if not already done */
free_irq(card->irq, card); /* release interrupt */
release_region(card->iobase + PCI9050_INTR_REG, 1); /* release all io ports */
release_region(card->iobase + PCI9050_USER_IO, 1);
iounmap(card->dpram);
card->dpram = NULL; /* release shared mem */
} /* ergo_releasehardware */
/*********************************************************************************/
/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */
/* value is returned. */
/* Use only during module init. */
/*********************************************************************************/
int
ergo_inithardware(hysdn_card * card)
{
if (!request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN"))
return (-1);
if (!request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN")) {
release_region(card->iobase + PCI9050_INTR_REG, 1);
return (-1); /* ports already in use */
}
card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1;
if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE))) {
release_region(card->iobase + PCI9050_INTR_REG, 1);
release_region(card->iobase + PCI9050_USER_IO, 1);
return (-1);
}
ergo_stopcard(card); /* disable interrupts */
if (request_irq(card->irq, ergo_interrupt, IRQF_SHARED, "HYSDN", card)) {
ergo_releasehardware(card); /* return the acquired hardware */
return (-1);
}
/* success, now setup the function pointers */
card->stopcard = ergo_stopcard;
card->releasehardware = ergo_releasehardware;
card->testram = ergo_testram;
card->writebootimg = ergo_writebootimg;
card->writebootseq = ergo_writebootseq;
card->waitpofready = ergo_waitpofready;
card->set_errlog_state = ergo_set_errlog_state;
INIT_WORK(&card->irq_queue, ergo_irq_bh);
spin_lock_init(&card->hysdn_lock);
return (0);
} /* ergo_inithardware */

View File

@@ -0,0 +1,100 @@
/* $Id: boardergo.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
*
* Linux driver for HYSDN cards, definitions for ergo type boards (buffers..).
*
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
* Copyright 1999 by Werner Cornelius (werner@titro.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
/************************************************/
/* defines for the dual port memory of the card */
/************************************************/
#define ERG_DPRAM_PAGE_SIZE 0x2000 /* DPRAM occupies a 8K page */
#define BOOT_IMG_SIZE 4096
#define ERG_DPRAM_FILL_SIZE (ERG_DPRAM_PAGE_SIZE - BOOT_IMG_SIZE)
#define ERG_TO_HY_BUF_SIZE 0x0E00 /* 3072 bytes buffer size to card */
#define ERG_TO_PC_BUF_SIZE 0x0E00 /* 3072 bytes to PC, too */
/* following DPRAM layout copied from OS2-driver boarderg.h */
typedef struct ErgDpram_tag {
/*0000 */ unsigned char ToHyBuf[ERG_TO_HY_BUF_SIZE];
/*0E00 */ unsigned char ToPcBuf[ERG_TO_PC_BUF_SIZE];
/*1C00 */ unsigned char bSoftUart[SIZE_RSV_SOFT_UART];
/* size 0x1B0 */
/*1DB0 *//* tErrLogEntry */ unsigned char volatile ErrLogMsg[64];
/* size 64 bytes */
/*1DB0 unsigned long ulErrType; */
/*1DB4 unsigned long ulErrSubtype; */
/*1DB8 unsigned long ucTextSize; */
/*1DB9 unsigned long ucText[ERRLOG_TEXT_SIZE]; *//* ASCIIZ of len ucTextSize-1 */
/*1DF0 */
/*1DF0 */ unsigned short volatile ToHyChannel;
/*1DF2 */ unsigned short volatile ToHySize;
/*1DF4 */ unsigned char volatile ToHyFlag;
/* !=0: msg for Hy waiting */
/*1DF5 */ unsigned char volatile ToPcFlag;
/* !=0: msg for PC waiting */
/*1DF6 */ unsigned short volatile ToPcChannel;
/*1DF8 */ unsigned short volatile ToPcSize;
/*1DFA */ unsigned char bRes1DBA[0x1E00 - 0x1DFA];
/* 6 bytes */
/*1E00 */ unsigned char bRestOfEntryTbl[0x1F00 - 0x1E00];
/*1F00 */ unsigned long TrapTable[62];
/*1FF8 */ unsigned char bRes1FF8[0x1FFB - 0x1FF8];
/* low part of reset vetor */
/*1FFB */ unsigned char ToPcIntMetro;
/* notes:
* - metro has 32-bit boot ram - accessing
* ToPcInt and ToHyInt would be the same;
* so we moved ToPcInt to 1FFB.
* Because on the PC side both vars are
* readonly (reseting on int from E1 to PC),
* we can read both vars on both cards
* without destroying anything.
* - 1FFB is the high byte of the reset vector,
* so E1 side should NOT change this byte
* when writing!
*/
/*1FFC */ unsigned char volatile ToHyNoDpramErrLog;
/* note: ToHyNoDpramErrLog is used to inform
* boot loader, not to use DPRAM based
* ErrLog; when DOS driver is rewritten
* this becomes obsolete
*/
/*1FFD */ unsigned char bRes1FFD;
/*1FFE */ unsigned char ToPcInt;
/* E1_intclear; on CHAMP2: E1_intset */
/*1FFF */ unsigned char ToHyInt;
/* E1_intset; on CHAMP2: E1_intclear */
} tErgDpram;
/**********************************************/
/* PCI9050 controller local register offsets: */
/* copied from boarderg.c */
/**********************************************/
#define PCI9050_INTR_REG 0x4C /* Interrupt register */
#define PCI9050_USER_IO 0x51 /* User I/O register */
/* bitmask for PCI9050_INTR_REG: */
#define PCI9050_INTR_REG_EN1 0x01 /* 1= enable (def.), 0= disable */
#define PCI9050_INTR_REG_POL1 0x02 /* 1= active high (def.), 0= active low */
#define PCI9050_INTR_REG_STAT1 0x04 /* 1= intr. active, 0= intr. not active (def.) */
#define PCI9050_INTR_REG_ENPCI 0x40 /* 1= PCI interrupts enable (def.) */
/* bitmask for PCI9050_USER_IO: */
#define PCI9050_USER_IO_EN3 0x02 /* 1= disable , 0= enable (def.) */
#define PCI9050_USER_IO_DIR3 0x04 /* 1= output (def.), 0= input */
#define PCI9050_USER_IO_DAT3 0x08 /* 1= high (def.) , 0= low */
#define PCI9050_E1_RESET ( PCI9050_USER_IO_DIR3) /* 0x04 */
#define PCI9050_E1_RUN (PCI9050_USER_IO_DAT3|PCI9050_USER_IO_DIR3) /* 0x0C */

View File

@@ -0,0 +1,798 @@
/* $Id: hycapi.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
*
* Linux driver for HYSDN cards, CAPI2.0-Interface.
*
* Author Ulrich Albrecht <u.albrecht@hypercope.de> for Hypercope GmbH
* Copyright 2000 by Hypercope GmbH
*
* 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/signal.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#define VER_DRIVER 0
#define VER_CARDTYPE 1
#define VER_HWID 2
#define VER_SERIAL 3
#define VER_OPTION 4
#define VER_PROTO 5
#define VER_PROFILE 6
#define VER_CAPI 7
#include "hysdn_defs.h"
#include <linux/kernelcapi.h>
static char hycapi_revision[]="$Revision: 1.8.6.4 $";
unsigned int hycapi_enable = 0xffffffff;
module_param(hycapi_enable, uint, 0);
typedef struct _hycapi_appl {
unsigned int ctrl_mask;
capi_register_params rp;
struct sk_buff *listen_req[CAPI_MAXCONTR];
} hycapi_appl;
static hycapi_appl hycapi_applications[CAPI_MAXAPPL];
static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
static inline int _hycapi_appCheck(int app_id, int ctrl_no)
{
if((ctrl_no <= 0) || (ctrl_no > CAPI_MAXCONTR) || (app_id <= 0) ||
(app_id > CAPI_MAXAPPL))
{
printk(KERN_ERR "HYCAPI: Invalid request app_id %d for controller %d", app_id, ctrl_no);
return -1;
}
return ((hycapi_applications[app_id-1].ctrl_mask & (1 << (ctrl_no-1))) != 0);
}
/******************************
Kernel-Capi callback reset_ctr
******************************/
static void
hycapi_reset_ctr(struct capi_ctr *ctrl)
{
hycapictrl_info *cinfo = ctrl->driverdata;
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "HYCAPI hycapi_reset_ctr\n");
#endif
capilib_release(&cinfo->ncci_head);
capi_ctr_down(ctrl);
}
/******************************
Kernel-Capi callback remove_ctr
******************************/
static void
hycapi_remove_ctr(struct capi_ctr *ctrl)
{
int i;
hycapictrl_info *cinfo = NULL;
hysdn_card *card = NULL;
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "HYCAPI hycapi_remove_ctr\n");
#endif
cinfo = (hycapictrl_info *)(ctrl->driverdata);
if(!cinfo) {
printk(KERN_ERR "No hycapictrl_info set!");
return;
}
card = cinfo->card;
capi_ctr_suspend_output(ctrl);
for(i=0; i<CAPI_MAXAPPL;i++) {
if(hycapi_applications[i].listen_req[ctrl->cnr-1]) {
kfree_skb(hycapi_applications[i].listen_req[ctrl->cnr-1]);
hycapi_applications[i].listen_req[ctrl->cnr-1] = NULL;
}
}
detach_capi_ctr(ctrl);
ctrl->driverdata = NULL;
kfree(card->hyctrlinfo);
card->hyctrlinfo = NULL;
}
/***********************************************************
Queue a CAPI-message to the controller.
***********************************************************/
static void
hycapi_sendmsg_internal(struct capi_ctr *ctrl, struct sk_buff *skb)
{
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
hysdn_card *card = cinfo->card;
spin_lock_irq(&cinfo->lock);
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_send_message\n");
#endif
cinfo->skbs[cinfo->in_idx++] = skb; /* add to buffer list */
if (cinfo->in_idx >= HYSDN_MAX_CAPI_SKB)
cinfo->in_idx = 0; /* wrap around */
cinfo->sk_count++; /* adjust counter */
if (cinfo->sk_count >= HYSDN_MAX_CAPI_SKB) {
/* inform upper layers we're full */
printk(KERN_ERR "HYSDN Card%d: CAPI-buffer overrun!\n",
card->myid);
capi_ctr_suspend_output(ctrl);
}
cinfo->tx_skb = skb;
spin_unlock_irq(&cinfo->lock);
schedule_work(&card->irq_queue);
}
/***********************************************************
hycapi_register_internal
Send down the CAPI_REGISTER-Command to the controller.
This functions will also be used if the adapter has been rebooted to
re-register any applications in the private list.
************************************************************/
static void
hycapi_register_internal(struct capi_ctr *ctrl, __u16 appl,
capi_register_params *rp)
{
char ExtFeatureDefaults[] = "49 /0/0/0/0,*/1,*/2,*/3,*/4,*/5,*/6,*/7,*/8,*/9,*";
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
hysdn_card *card = cinfo->card;
struct sk_buff *skb;
__u16 len;
__u8 _command = 0xa0, _subcommand = 0x80;
__u16 MessageNumber = 0x0000;
__u16 MessageBufferSize = 0;
int slen = strlen(ExtFeatureDefaults);
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_register_appl\n");
#endif
MessageBufferSize = rp->level3cnt * rp->datablkcnt * rp->datablklen;
len = CAPI_MSG_BASELEN + 8 + slen + 1;
if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
card->myid);
return;
}
memcpy(skb_put(skb,sizeof(__u16)), &len, sizeof(__u16));
memcpy(skb_put(skb,sizeof(__u16)), &appl, sizeof(__u16));
memcpy(skb_put(skb,sizeof(__u8)), &_command, sizeof(_command));
memcpy(skb_put(skb,sizeof(__u8)), &_subcommand, sizeof(_subcommand));
memcpy(skb_put(skb,sizeof(__u16)), &MessageNumber, sizeof(__u16));
memcpy(skb_put(skb,sizeof(__u16)), &MessageBufferSize, sizeof(__u16));
memcpy(skb_put(skb,sizeof(__u16)), &(rp->level3cnt), sizeof(__u16));
memcpy(skb_put(skb,sizeof(__u16)), &(rp->datablkcnt), sizeof(__u16));
memcpy(skb_put(skb,sizeof(__u16)), &(rp->datablklen), sizeof(__u16));
memcpy(skb_put(skb,slen), ExtFeatureDefaults, slen);
hycapi_applications[appl-1].ctrl_mask |= (1 << (ctrl->cnr-1));
hycapi_send_message(ctrl, skb);
}
/************************************************************
hycapi_restart_internal
After an adapter has been rebootet, re-register all applications and
send a LISTEN_REQ (if there has been such a thing )
*************************************************************/
static void hycapi_restart_internal(struct capi_ctr *ctrl)
{
int i;
struct sk_buff *skb;
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_WARNING "HYSDN: hycapi_restart_internal");
#endif
for(i=0; i<CAPI_MAXAPPL; i++) {
if(_hycapi_appCheck(i+1, ctrl->cnr) == 1) {
hycapi_register_internal(ctrl, i+1,
&hycapi_applications[i].rp);
if(hycapi_applications[i].listen_req[ctrl->cnr-1]) {
skb = skb_copy(hycapi_applications[i].listen_req[ctrl->cnr-1], GFP_ATOMIC);
hycapi_sendmsg_internal(ctrl, skb);
}
}
}
}
/*************************************************************
Register an application.
Error-checking is done for CAPI-compliance.
The application is recorded in the internal list.
*************************************************************/
static void
hycapi_register_appl(struct capi_ctr *ctrl, __u16 appl,
capi_register_params *rp)
{
int MaxLogicalConnections = 0, MaxBDataBlocks = 0, MaxBDataLen = 0;
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
hysdn_card *card = cinfo->card;
int chk = _hycapi_appCheck(appl, ctrl->cnr);
if(chk < 0) {
return;
}
if(chk == 1) {
printk(KERN_INFO "HYSDN: apl %d already registered\n", appl);
return;
}
MaxBDataBlocks = rp->datablkcnt > CAPI_MAXDATAWINDOW ? CAPI_MAXDATAWINDOW : rp->datablkcnt;
rp->datablkcnt = MaxBDataBlocks;
MaxBDataLen = rp->datablklen < 1024 ? 1024 : rp->datablklen ;
rp->datablklen = MaxBDataLen;
MaxLogicalConnections = rp->level3cnt;
if (MaxLogicalConnections < 0) {
MaxLogicalConnections = card->bchans * -MaxLogicalConnections;
}
if (MaxLogicalConnections == 0) {
MaxLogicalConnections = card->bchans;
}
rp->level3cnt = MaxLogicalConnections;
memcpy(&hycapi_applications[appl-1].rp,
rp, sizeof(capi_register_params));
}
/*********************************************************************
hycapi_release_internal
Send down a CAPI_RELEASE to the controller.
*********************************************************************/
static void hycapi_release_internal(struct capi_ctr *ctrl, __u16 appl)
{
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
hysdn_card *card = cinfo->card;
struct sk_buff *skb;
__u16 len;
__u8 _command = 0xa1, _subcommand = 0x80;
__u16 MessageNumber = 0x0000;
capilib_release_appl(&cinfo->ncci_head, appl);
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_release_appl\n");
#endif
len = CAPI_MSG_BASELEN;
if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
card->myid);
return;
}
memcpy(skb_put(skb,sizeof(__u16)), &len, sizeof(__u16));
memcpy(skb_put(skb,sizeof(__u16)), &appl, sizeof(__u16));
memcpy(skb_put(skb,sizeof(__u8)), &_command, sizeof(_command));
memcpy(skb_put(skb,sizeof(__u8)), &_subcommand, sizeof(_subcommand));
memcpy(skb_put(skb,sizeof(__u16)), &MessageNumber, sizeof(__u16));
hycapi_send_message(ctrl, skb);
hycapi_applications[appl-1].ctrl_mask &= ~(1 << (ctrl->cnr-1));
}
/******************************************************************
hycapi_release_appl
Release the application from the internal list an remove it's
registration at controller-level
******************************************************************/
static void
hycapi_release_appl(struct capi_ctr *ctrl, __u16 appl)
{
int chk;
chk = _hycapi_appCheck(appl, ctrl->cnr);
if(chk<0) {
printk(KERN_ERR "HYCAPI: Releasing invalid appl %d on controller %d\n", appl, ctrl->cnr);
return;
}
if(hycapi_applications[appl-1].listen_req[ctrl->cnr-1]) {
kfree_skb(hycapi_applications[appl-1].listen_req[ctrl->cnr-1]);
hycapi_applications[appl-1].listen_req[ctrl->cnr-1] = NULL;
}
if(chk == 1)
{
hycapi_release_internal(ctrl, appl);
}
}
/**************************************************************
Kill a single controller.
**************************************************************/
int hycapi_capi_release(hysdn_card *card)
{
hycapictrl_info *cinfo = card->hyctrlinfo;
struct capi_ctr *ctrl;
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_capi_release\n");
#endif
if(cinfo) {
ctrl = &cinfo->capi_ctrl;
hycapi_remove_ctr(ctrl);
}
return 0;
}
/**************************************************************
hycapi_capi_stop
Stop CAPI-Output on a card. (e.g. during reboot)
***************************************************************/
int hycapi_capi_stop(hysdn_card *card)
{
hycapictrl_info *cinfo = card->hyctrlinfo;
struct capi_ctr *ctrl;
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_capi_stop\n");
#endif
if(cinfo) {
ctrl = &cinfo->capi_ctrl;
/* ctrl->suspend_output(ctrl); */
capi_ctr_down(ctrl);
}
return 0;
}
/***************************************************************
hycapi_send_message
Send a message to the controller.
Messages are parsed for their Command/Subcommand-type, and appropriate
action's are performed.
Note that we have to muck around with a 64Bit-DATA_REQ as there are
firmware-releases that do not check the MsgLen-Indication!
***************************************************************/
static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
{
__u16 appl_id;
int _len, _len2;
__u8 msghead[64];
hycapictrl_info *cinfo = ctrl->driverdata;
u16 retval = CAPI_NOERROR;
appl_id = CAPIMSG_APPID(skb->data);
switch(_hycapi_appCheck(appl_id, ctrl->cnr))
{
case 0:
/* printk(KERN_INFO "Need to register\n"); */
hycapi_register_internal(ctrl,
appl_id,
&(hycapi_applications[appl_id-1].rp));
break;
case 1:
break;
default:
printk(KERN_ERR "HYCAPI: Controller mixup!\n");
retval = CAPI_ILLAPPNR;
goto out;
}
switch(CAPIMSG_CMD(skb->data)) {
case CAPI_DISCONNECT_B3_RESP:
capilib_free_ncci(&cinfo->ncci_head, appl_id,
CAPIMSG_NCCI(skb->data));
break;
case CAPI_DATA_B3_REQ:
_len = CAPIMSG_LEN(skb->data);
if (_len > 22) {
_len2 = _len - 22;
skb_copy_from_linear_data(skb, msghead, 22);
skb_copy_to_linear_data_offset(skb, _len2,
msghead, 22);
skb_pull(skb, _len2);
CAPIMSG_SETLEN(skb->data, 22);
retval = capilib_data_b3_req(&cinfo->ncci_head,
CAPIMSG_APPID(skb->data),
CAPIMSG_NCCI(skb->data),
CAPIMSG_MSGID(skb->data));
}
break;
case CAPI_LISTEN_REQ:
if(hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1])
{
kfree_skb(hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1]);
hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1] = NULL;
}
if (!(hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1] = skb_copy(skb, GFP_ATOMIC)))
{
printk(KERN_ERR "HYSDN: memory squeeze in private_listen\n");
}
break;
default:
break;
}
out:
if (retval == CAPI_NOERROR)
hycapi_sendmsg_internal(ctrl, skb);
else
dev_kfree_skb_any(skb);
return retval;
}
/*********************************************************************
hycapi_read_proc
Informations provided in the /proc/capi-entries.
*********************************************************************/
static int hycapi_read_proc(char *page, char **start, off_t off,
int count, int *eof, struct capi_ctr *ctrl)
{
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
hysdn_card *card = cinfo->card;
int len = 0;
char *s;
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_read_proc\n");
#endif
len += sprintf(page+len, "%-16s %s\n", "name", cinfo->cardname);
len += sprintf(page+len, "%-16s 0x%x\n", "io", card->iobase);
len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
switch (card->brdtype) {
case BD_PCCARD: s = "HYSDN Hycard"; break;
case BD_ERGO: s = "HYSDN Ergo2"; break;
case BD_METRO: s = "HYSDN Metro4"; break;
case BD_CHAMP2: s = "HYSDN Champ2"; break;
case BD_PLEXUS: s = "HYSDN Plexus30"; break;
default: s = "???"; break;
}
len += sprintf(page+len, "%-16s %s\n", "type", s);
if ((s = cinfo->version[VER_DRIVER]) != NULL)
len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
if ((s = cinfo->version[VER_SERIAL]) != NULL)
len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
if (off+count >= len)
*eof = 1;
if (len < off)
return 0;
*start = page + off;
return ((count < len-off) ? count : len-off);
}
/**************************************************************
hycapi_load_firmware
This does NOT load any firmware, but the callback somehow is needed
on capi-interface registration.
**************************************************************/
static int hycapi_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
{
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_load_firmware\n");
#endif
return 0;
}
static char *hycapi_procinfo(struct capi_ctr *ctrl)
{
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_proc_info\n");
#endif
if (!cinfo)
return "";
sprintf(cinfo->infobuf, "%s %s 0x%x %d %s",
cinfo->cardname[0] ? cinfo->cardname : "-",
cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
cinfo->card ? cinfo->card->iobase : 0x0,
cinfo->card ? cinfo->card->irq : 0,
hycapi_revision
);
return cinfo->infobuf;
}
/******************************************************************
hycapi_rx_capipkt
Receive a capi-message.
All B3_DATA_IND are converted to 64K-extension compatible format.
New nccis are created if necessary.
*******************************************************************/
void
hycapi_rx_capipkt(hysdn_card * card, unsigned char *buf, unsigned short len)
{
struct sk_buff *skb;
hycapictrl_info *cinfo = card->hyctrlinfo;
struct capi_ctr *ctrl;
__u16 ApplId;
__u16 MsgLen, info;
__u16 len2, CapiCmd;
__u32 CP64[2] = {0,0};
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_rx_capipkt\n");
#endif
if(!cinfo) {
return;
}
ctrl = &cinfo->capi_ctrl;
if(len < CAPI_MSG_BASELEN) {
printk(KERN_ERR "HYSDN Card%d: invalid CAPI-message, length %d!\n",
card->myid, len);
return;
}
MsgLen = CAPIMSG_LEN(buf);
ApplId = CAPIMSG_APPID(buf);
CapiCmd = CAPIMSG_CMD(buf);
if((CapiCmd == CAPI_DATA_B3_IND) && (MsgLen < 30)) {
len2 = len + (30 - MsgLen);
if (!(skb = alloc_skb(len2, GFP_ATOMIC))) {
printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
card->myid);
return;
}
memcpy(skb_put(skb, MsgLen), buf, MsgLen);
memcpy(skb_put(skb, 2*sizeof(__u32)), CP64, 2* sizeof(__u32));
memcpy(skb_put(skb, len - MsgLen), buf + MsgLen,
len - MsgLen);
CAPIMSG_SETLEN(skb->data, 30);
} else {
if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
card->myid);
return;
}
memcpy(skb_put(skb, len), buf, len);
}
switch(CAPIMSG_CMD(skb->data))
{
case CAPI_CONNECT_B3_CONF:
/* Check info-field for error-indication: */
info = CAPIMSG_U16(skb->data, 12);
switch(info)
{
case 0:
capilib_new_ncci(&cinfo->ncci_head, ApplId, CAPIMSG_NCCI(skb->data),
hycapi_applications[ApplId-1].rp.datablkcnt);
break;
case 0x0001:
printk(KERN_ERR "HYSDN Card%d: NCPI not supported by current "
"protocol. NCPI ignored.\n", card->myid);
break;
case 0x2001:
printk(KERN_ERR "HYSDN Card%d: Message not supported in"
" current state\n", card->myid);
break;
case 0x2002:
printk(KERN_ERR "HYSDN Card%d: invalid PLCI\n", card->myid);
break;
case 0x2004:
printk(KERN_ERR "HYSDN Card%d: out of NCCI\n", card->myid);
break;
case 0x3008:
printk(KERN_ERR "HYSDN Card%d: NCPI not supported\n",
card->myid);
break;
default:
printk(KERN_ERR "HYSDN Card%d: Info in CONNECT_B3_CONF: %d\n",
card->myid, info);
break;
}
break;
case CAPI_CONNECT_B3_IND:
capilib_new_ncci(&cinfo->ncci_head, ApplId,
CAPIMSG_NCCI(skb->data),
hycapi_applications[ApplId-1].rp.datablkcnt);
break;
case CAPI_DATA_B3_CONF:
capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
CAPIMSG_NCCI(skb->data),
CAPIMSG_MSGID(skb->data));
break;
default:
break;
}
capi_ctr_handle_message(ctrl, ApplId, skb);
}
/******************************************************************
hycapi_tx_capiack
Internally acknowledge a msg sent. This will remove the msg from the
internal queue.
*******************************************************************/
void hycapi_tx_capiack(hysdn_card * card)
{
hycapictrl_info *cinfo = card->hyctrlinfo;
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_tx_capiack\n");
#endif
if(!cinfo) {
return;
}
spin_lock_irq(&cinfo->lock);
kfree_skb(cinfo->skbs[cinfo->out_idx]); /* free skb */
cinfo->skbs[cinfo->out_idx++] = NULL;
if (cinfo->out_idx >= HYSDN_MAX_CAPI_SKB)
cinfo->out_idx = 0; /* wrap around */
if (cinfo->sk_count-- == HYSDN_MAX_CAPI_SKB) /* dec usage count */
capi_ctr_resume_output(&cinfo->capi_ctrl);
spin_unlock_irq(&cinfo->lock);
}
/***************************************************************
hycapi_tx_capiget(hysdn_card *card)
This is called when polling for messages to SEND.
****************************************************************/
struct sk_buff *
hycapi_tx_capiget(hysdn_card *card)
{
hycapictrl_info *cinfo = card->hyctrlinfo;
if(!cinfo) {
return (struct sk_buff *)NULL;
}
if (!cinfo->sk_count)
return (struct sk_buff *)NULL; /* nothing available */
return (cinfo->skbs[cinfo->out_idx]); /* next packet to send */
}
/**********************************************************
int hycapi_init()
attach the capi-driver to the kernel-capi.
***********************************************************/
int hycapi_init(void)
{
int i;
for(i=0;i<CAPI_MAXAPPL;i++) {
memset(&(hycapi_applications[i]), 0, sizeof(hycapi_appl));
}
return(0);
}
/**************************************************************
hycapi_cleanup(void)
detach the capi-driver to the kernel-capi. Actually this should
free some more ressources. Do that later.
**************************************************************/
void
hycapi_cleanup(void)
{
}
/********************************************************************
hycapi_capi_create(hysdn_card *card)
Attach the card with its capi-ctrl.
*********************************************************************/
static void hycapi_fill_profile(hysdn_card *card)
{
hycapictrl_info *cinfo = NULL;
struct capi_ctr *ctrl = NULL;
cinfo = card->hyctrlinfo;
if(!cinfo) return;
ctrl = &cinfo->capi_ctrl;
strcpy(ctrl->manu, "Hypercope");
ctrl->version.majorversion = 2;
ctrl->version.minorversion = 0;
ctrl->version.majormanuversion = 3;
ctrl->version.minormanuversion = 2;
ctrl->profile.ncontroller = card->myid;
ctrl->profile.nbchannel = card->bchans;
ctrl->profile.goptions = GLOBAL_OPTION_INTERNAL_CONTROLLER |
GLOBAL_OPTION_B_CHANNEL_OPERATION;
ctrl->profile.support1 = B1_PROT_64KBIT_HDLC |
(card->faxchans ? B1_PROT_T30 : 0) |
B1_PROT_64KBIT_TRANSPARENT;
ctrl->profile.support2 = B2_PROT_ISO7776 |
(card->faxchans ? B2_PROT_T30 : 0) |
B2_PROT_TRANSPARENT;
ctrl->profile.support3 = B3_PROT_TRANSPARENT |
B3_PROT_T90NL |
(card->faxchans ? B3_PROT_T30 : 0) |
(card->faxchans ? B3_PROT_T30EXT : 0) |
B3_PROT_ISO8208;
}
int
hycapi_capi_create(hysdn_card *card)
{
hycapictrl_info *cinfo = NULL;
struct capi_ctr *ctrl = NULL;
int retval;
#ifdef HYCAPI_PRINTFNAMES
printk(KERN_NOTICE "hycapi_capi_create\n");
#endif
if((hycapi_enable & (1 << card->myid)) == 0) {
return 1;
}
if (!card->hyctrlinfo) {
cinfo = kzalloc(sizeof(hycapictrl_info), GFP_ATOMIC);
if (!cinfo) {
printk(KERN_WARNING "HYSDN: no memory for capi-ctrl.\n");
return -ENOMEM;
}
card->hyctrlinfo = cinfo;
cinfo->card = card;
spin_lock_init(&cinfo->lock);
INIT_LIST_HEAD(&cinfo->ncci_head);
switch (card->brdtype) {
case BD_PCCARD: strcpy(cinfo->cardname,"HYSDN Hycard"); break;
case BD_ERGO: strcpy(cinfo->cardname,"HYSDN Ergo2"); break;
case BD_METRO: strcpy(cinfo->cardname,"HYSDN Metro4"); break;
case BD_CHAMP2: strcpy(cinfo->cardname,"HYSDN Champ2"); break;
case BD_PLEXUS: strcpy(cinfo->cardname,"HYSDN Plexus30"); break;
default: strcpy(cinfo->cardname,"HYSDN ???"); break;
}
ctrl = &cinfo->capi_ctrl;
ctrl->driver_name = "hycapi";
ctrl->driverdata = cinfo;
ctrl->register_appl = hycapi_register_appl;
ctrl->release_appl = hycapi_release_appl;
ctrl->send_message = hycapi_send_message;
ctrl->load_firmware = hycapi_load_firmware;
ctrl->reset_ctr = hycapi_reset_ctr;
ctrl->procinfo = hycapi_procinfo;
ctrl->ctr_read_proc = hycapi_read_proc;
strcpy(ctrl->name, cinfo->cardname);
ctrl->owner = THIS_MODULE;
retval = attach_capi_ctr(ctrl);
if (retval) {
printk(KERN_ERR "hycapi: attach controller failed.\n");
return -EBUSY;
}
/* fill in the blanks: */
hycapi_fill_profile(card);
capi_ctr_ready(ctrl);
} else {
/* resume output on stopped ctrl */
ctrl = &card->hyctrlinfo->capi_ctrl;
hycapi_fill_profile(card);
capi_ctr_ready(ctrl);
hycapi_restart_internal(ctrl);
/* ctrl->resume_output(ctrl); */
}
return 0;
}

View File

@@ -0,0 +1,398 @@
/* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $
*
* Linux driver for HYSDN cards
* specific routines for booting and pof handling
*
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
* Copyright 1999 by Werner Cornelius (werner@titro.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include "hysdn_defs.h"
#include "hysdn_pof.h"
/********************************/
/* defines for pof read handler */
/********************************/
#define POF_READ_FILE_HEAD 0
#define POF_READ_TAG_HEAD 1
#define POF_READ_TAG_DATA 2
/************************************************************/
/* definition of boot specific data area. This data is only */
/* needed during boot and so allocated dynamically. */
/************************************************************/
struct boot_data {
unsigned short Cryptor; /* for use with Decrypt function */
unsigned short Nrecs; /* records remaining in file */
unsigned char pof_state;/* actual state of read handler */
unsigned char is_crypted;/* card data is crypted */
int BufSize; /* actual number of bytes bufferd */
int last_error; /* last occurred error */
unsigned short pof_recid;/* actual pof recid */
unsigned long pof_reclen;/* total length of pof record data */
unsigned long pof_recoffset;/* actual offset inside pof record */
union {
unsigned char BootBuf[BOOT_BUF_SIZE];/* buffer as byte count */
tPofRecHdr PofRecHdr; /* header for actual record/chunk */
tPofFileHdr PofFileHdr; /* header from POF file */
tPofTimeStamp PofTime; /* time information */
} buf;
};
/*****************************************************/
/* start decryption of successive POF file chuncks. */
/* */
/* to be called at start of POF file reading, */
/* before starting any decryption on any POF record. */
/*****************************************************/
static void
StartDecryption(struct boot_data *boot)
{
boot->Cryptor = CRYPT_STARTTERM;
} /* StartDecryption */
/***************************************************************/
/* decrypt complete BootBuf */
/* NOTE: decryption must be applied to all or none boot tags - */
/* to HI and LO boot loader and (all) seq tags, because */
/* global Cryptor is started for whole POF. */
/***************************************************************/
static void
DecryptBuf(struct boot_data *boot, int cnt)
{
unsigned char *bufp = boot->buf.BootBuf;
while (cnt--) {
boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
*bufp++ ^= (unsigned char)boot->Cryptor;
}
} /* DecryptBuf */
/********************************************************************************/
/* pof_handle_data executes the required actions dependent on the active record */
/* id. If successful 0 is returned, a negative value shows an error. */
/********************************************************************************/
static int
pof_handle_data(hysdn_card * card, int datlen)
{
struct boot_data *boot = card->boot; /* pointer to boot specific data */
long l;
unsigned char *imgp;
int img_len;
/* handle the different record types */
switch (boot->pof_recid) {
case TAG_TIMESTMP:
if (card->debug_flags & LOG_POF_RECORD)
hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
break;
case TAG_CBOOTDTA:
DecryptBuf(boot, datlen); /* we need to encrypt the buffer */
case TAG_BOOTDTA:
if (card->debug_flags & LOG_POF_RECORD)
hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
(boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
datlen, boot->pof_recoffset);
if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
boot->last_error = EPOF_BAD_IMG_SIZE; /* invalid length */
return (boot->last_error);
}
imgp = boot->buf.BootBuf; /* start of buffer */
img_len = datlen; /* maximum length to transfer */
l = POF_BOOT_LOADER_OFF_IN_PAGE -
(boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
if (l > 0) {
/* buffer needs to be truncated */
imgp += l; /* advance pointer */
img_len -= l; /* adjust len */
}
/* at this point no special handling for data wrapping over buffer */
/* is necessary, because the boot image always will be adjusted to */
/* match a page boundary inside the buffer. */
/* The buffer for the boot image on the card is filled in 2 cycles */
/* first the 1024 hi-words are put in the buffer, then the low 1024 */
/* word are handled in the same way with different offset. */
if (img_len > 0) {
/* data available for copy */
if ((boot->last_error =
card->writebootimg(card, imgp,
(boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
return (boot->last_error);
}
break; /* end of case boot image hi/lo */
case TAG_CABSDATA:
DecryptBuf(boot, datlen); /* we need to encrypt the buffer */
case TAG_ABSDATA:
if (card->debug_flags & LOG_POF_RECORD)
hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
(boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
datlen, boot->pof_recoffset);
if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen) < 0))
return (boot->last_error); /* error writing data */
if (boot->pof_recoffset + datlen >= boot->pof_reclen)
return (card->waitpofready(card)); /* data completely spooled, wait for ready */
break; /* end of case boot seq data */
default:
if (card->debug_flags & LOG_POF_RECORD)
hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
datlen, boot->pof_recoffset);
break; /* simply skip record */
} /* switch boot->pof_recid */
return (0);
} /* pof_handle_data */
/******************************************************************************/
/* pof_write_buffer is called when the buffer has been filled with the needed */
/* number of data bytes. The number delivered is additionally supplied for */
/* verification. The functions handles the data and returns the needed number */
/* of bytes for the next action. If the returned value is 0 or less an error */
/* occurred and booting must be aborted. */
/******************************************************************************/
int
pof_write_buffer(hysdn_card * card, int datlen)
{
struct boot_data *boot = card->boot; /* pointer to boot specific data */
if (!boot)
return (-EFAULT); /* invalid call */
if (boot->last_error < 0)
return (boot->last_error); /* repeated error */
if (card->debug_flags & LOG_POF_WRITE)
hysdn_addlog(card, "POF write: got %d bytes ", datlen);
switch (boot->pof_state) {
case POF_READ_FILE_HEAD:
if (card->debug_flags & LOG_POF_WRITE)
hysdn_addlog(card, "POF write: checking file header");
if (datlen != sizeof(tPofFileHdr)) {
boot->last_error = -EPOF_INTERNAL;
break;
}
if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
boot->last_error = -EPOF_BAD_MAGIC;
break;
}
/* Setup the new state and vars */
boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */
boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
boot->last_error = sizeof(tPofRecHdr); /* new length */
break;
case POF_READ_TAG_HEAD:
if (card->debug_flags & LOG_POF_WRITE)
hysdn_addlog(card, "POF write: checking tag header");
if (datlen != sizeof(tPofRecHdr)) {
boot->last_error = -EPOF_INTERNAL;
break;
}
boot->pof_recid = boot->buf.PofRecHdr.PofRecId; /* actual pof recid */
boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen; /* total length */
boot->pof_recoffset = 0; /* no starting offset */
if (card->debug_flags & LOG_POF_RECORD)
hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
boot->pof_recid, boot->pof_reclen);
boot->pof_state = POF_READ_TAG_DATA; /* now start with tag data */
if (boot->pof_reclen < BOOT_BUF_SIZE)
boot->last_error = boot->pof_reclen; /* limit size */
else
boot->last_error = BOOT_BUF_SIZE; /* maximum */
if (!boot->last_error) { /* no data inside record */
boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
boot->last_error = sizeof(tPofRecHdr); /* new length */
}
break;
case POF_READ_TAG_DATA:
if (card->debug_flags & LOG_POF_WRITE)
hysdn_addlog(card, "POF write: getting tag data");
if (datlen != boot->last_error) {
boot->last_error = -EPOF_INTERNAL;
break;
}
if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
return (boot->last_error); /* an error occurred */
boot->pof_recoffset += datlen;
if (boot->pof_recoffset >= boot->pof_reclen) {
boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
boot->last_error = sizeof(tPofRecHdr); /* new length */
} else {
if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
boot->last_error = boot->pof_reclen - boot->pof_recoffset; /* limit size */
else
boot->last_error = BOOT_BUF_SIZE; /* maximum */
}
break;
default:
boot->last_error = -EPOF_INTERNAL; /* unknown state */
break;
} /* switch (boot->pof_state) */
return (boot->last_error);
} /* pof_write_buffer */
/*******************************************************************************/
/* pof_write_open is called when an open for boot on the cardlog device occurs. */
/* The function returns the needed number of bytes for the next operation. If */
/* the returned number is less or equal 0 an error specified by this code */
/* occurred. Additionally the pointer to the buffer data area is set on success */
/*******************************************************************************/
int
pof_write_open(hysdn_card * card, unsigned char **bufp)
{
struct boot_data *boot; /* pointer to boot specific data */
if (card->boot) {
if (card->debug_flags & LOG_POF_OPEN)
hysdn_addlog(card, "POF open: already opened for boot");
return (-ERR_ALREADY_BOOT); /* boot already active */
}
/* error no mem available */
if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) {
if (card->debug_flags & LOG_MEM_ERR)
hysdn_addlog(card, "POF open: unable to allocate mem");
return (-EFAULT);
}
card->boot = boot;
card->state = CARD_STATE_BOOTING;
card->stopcard(card); /* first stop the card */
if (card->testram(card)) {
if (card->debug_flags & LOG_POF_OPEN)
hysdn_addlog(card, "POF open: DPRAM test failure");
boot->last_error = -ERR_BOARD_DPRAM;
card->state = CARD_STATE_BOOTERR; /* show boot error */
return (boot->last_error);
}
boot->BufSize = 0; /* Buffer is empty */
boot->pof_state = POF_READ_FILE_HEAD; /* read file header */
StartDecryption(boot); /* if POF File should be encrypted */
if (card->debug_flags & LOG_POF_OPEN)
hysdn_addlog(card, "POF open: success");
*bufp = boot->buf.BootBuf; /* point to buffer */
return (sizeof(tPofFileHdr));
} /* pof_write_open */
/********************************************************************************/
/* pof_write_close is called when an close of boot on the cardlog device occurs. */
/* The return value must be 0 if everything has happened as desired. */
/********************************************************************************/
int
pof_write_close(hysdn_card * card)
{
struct boot_data *boot = card->boot; /* pointer to boot specific data */
if (!boot)
return (-EFAULT); /* invalid call */
card->boot = NULL; /* no boot active */
kfree(boot);
if (card->state == CARD_STATE_RUN)
card->set_errlog_state(card, 1); /* activate error log */
if (card->debug_flags & LOG_POF_OPEN)
hysdn_addlog(card, "POF close: success");
return (0);
} /* pof_write_close */
/*********************************************************************************/
/* EvalSysrTokData checks additional records delivered with the Sysready Message */
/* when POF has been booted. A return value of 0 is used if no error occurred. */
/*********************************************************************************/
int
EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len)
{
u_char *p;
u_char crc;
if (card->debug_flags & LOG_POF_RECORD)
hysdn_addlog(card, "SysReady Token data length %d", len);
if (len < 2) {
hysdn_addlog(card, "SysReady Token Data to short");
return (1);
}
for (p = cp, crc = 0; p < (cp + len - 2); p++)
if ((crc & 0x80))
crc = (((u_char) (crc << 1)) + 1) + *p;
else
crc = ((u_char) (crc << 1)) + *p;
crc = ~crc;
if (crc != *(cp + len - 1)) {
hysdn_addlog(card, "SysReady Token Data invalid CRC");
return (1);
}
len--; /* don't check CRC byte */
while (len > 0) {
if (*cp == SYSR_TOK_END)
return (0); /* End of Token stream */
if (len < (*(cp + 1) + 2)) {
hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
return (1);
}
switch (*cp) {
case SYSR_TOK_B_CHAN: /* 1 */
if (*(cp + 1) != 1)
return (1); /* length invalid */
card->bchans = *(cp + 2);
break;
case SYSR_TOK_FAX_CHAN: /* 2 */
if (*(cp + 1) != 1)
return (1); /* length invalid */
card->faxchans = *(cp + 2);
break;
case SYSR_TOK_MAC_ADDR: /* 3 */
if (*(cp + 1) != 6)
return (1); /* length invalid */
memcpy(card->mac_addr, cp + 2, 6);
break;
default:
hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
break;
}
len -= (*(cp + 1) + 2); /* adjust len */
cp += (*(cp + 1) + 2); /* and pointer */
}
hysdn_addlog(card, "no end token found");
return (1);
} /* EvalSysrTokData */

View File

@@ -0,0 +1,284 @@
/* $Id: hysdn_defs.h,v 1.5.6.3 2001/09/23 22:24:54 kai Exp $
*
* Linux driver for HYSDN cards
* global definitions and exported vars and functions.
*
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
* Copyright 1999 by Werner Cornelius (werner@titro.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef HYSDN_DEFS_H
#define HYSDN_DEFS_H
#include <linux/hysdn_if.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include "ince1pc.h"
#ifdef CONFIG_HYSDN_CAPI
#include <linux/capi.h>
#include <linux/isdn/capicmd.h>
#include <linux/isdn/capiutil.h>
#include <linux/isdn/capilli.h>
/***************************/
/* CAPI-Profile values. */
/***************************/
#define GLOBAL_OPTION_INTERNAL_CONTROLLER 0x0001
#define GLOBAL_OPTION_EXTERNAL_CONTROLLER 0x0002
#define GLOBAL_OPTION_HANDSET 0x0004
#define GLOBAL_OPTION_DTMF 0x0008
#define GLOBAL_OPTION_SUPPL_SERVICES 0x0010
#define GLOBAL_OPTION_CHANNEL_ALLOCATION 0x0020
#define GLOBAL_OPTION_B_CHANNEL_OPERATION 0x0040
#define B1_PROT_64KBIT_HDLC 0x0001
#define B1_PROT_64KBIT_TRANSPARENT 0x0002
#define B1_PROT_V110_ASYNCH 0x0004
#define B1_PROT_V110_SYNCH 0x0008
#define B1_PROT_T30 0x0010
#define B1_PROT_64KBIT_INV_HDLC 0x0020
#define B1_PROT_56KBIT_TRANSPARENT 0x0040
#define B2_PROT_ISO7776 0x0001
#define B2_PROT_TRANSPARENT 0x0002
#define B2_PROT_SDLC 0x0004
#define B2_PROT_LAPD 0x0008
#define B2_PROT_T30 0x0010
#define B2_PROT_PPP 0x0020
#define B2_PROT_TRANSPARENT_IGNORE_B1_FRAMING_ERRORS 0x0040
#define B3_PROT_TRANSPARENT 0x0001
#define B3_PROT_T90NL 0x0002
#define B3_PROT_ISO8208 0x0004
#define B3_PROT_X25_DCE 0x0008
#define B3_PROT_T30 0x0010
#define B3_PROT_T30EXT 0x0020
#define HYSDN_MAXVERSION 8
/* Number of sendbuffers in CAPI-queue */
#define HYSDN_MAX_CAPI_SKB 20
#endif /* CONFIG_HYSDN_CAPI*/
/************************************************/
/* constants and bits for debugging/log outputs */
/************************************************/
#define LOG_MAX_LINELEN 120
#define DEB_OUT_SYSLOG 0x80000000 /* output to syslog instead of proc fs */
#define LOG_MEM_ERR 0x00000001 /* log memory errors like kmalloc failure */
#define LOG_POF_OPEN 0x00000010 /* log pof open and close activities */
#define LOG_POF_RECORD 0x00000020 /* log pof record parser */
#define LOG_POF_WRITE 0x00000040 /* log detailed pof write operation */
#define LOG_POF_CARD 0x00000080 /* log pof related card functions */
#define LOG_CNF_LINE 0x00000100 /* all conf lines are put to procfs */
#define LOG_CNF_DATA 0x00000200 /* non comment conf lines are shown with channel */
#define LOG_CNF_MISC 0x00000400 /* additional conf line debug outputs */
#define LOG_SCHED_ASYN 0x00001000 /* debug schedulers async tx routines */
#define LOG_PROC_OPEN 0x00100000 /* open and close from procfs are logged */
#define LOG_PROC_ALL 0x00200000 /* all actions from procfs are logged */
#define LOG_NET_INIT 0x00010000 /* network init and deinit logging */
#define DEF_DEB_FLAGS 0x7fff000f /* everything is logged to procfs */
/**********************************/
/* proc filesystem name constants */
/**********************************/
#define PROC_SUBDIR_NAME "hysdn"
#define PROC_CONF_BASENAME "cardconf"
#define PROC_LOG_BASENAME "cardlog"
/***********************************/
/* PCI 32 bit parms for IO and MEM */
/***********************************/
#define PCI_REG_PLX_MEM_BASE 0
#define PCI_REG_PLX_IO_BASE 1
#define PCI_REG_MEMORY_BASE 3
/**************/
/* card types */
/**************/
#define BD_NONE 0U
#define BD_PERFORMANCE 1U
#define BD_VALUE 2U
#define BD_PCCARD 3U
#define BD_ERGO 4U
#define BD_METRO 5U
#define BD_CHAMP2 6U
#define BD_PLEXUS 7U
/******************************************************/
/* defined states for cards shown by reading cardconf */
/******************************************************/
#define CARD_STATE_UNUSED 0 /* never been used or booted */
#define CARD_STATE_BOOTING 1 /* booting is in progress */
#define CARD_STATE_BOOTERR 2 /* a previous boot was aborted */
#define CARD_STATE_RUN 3 /* card is active */
/*******************************/
/* defines for error_log_state */
/*******************************/
#define ERRLOG_STATE_OFF 0 /* error log is switched off, nothing to do */
#define ERRLOG_STATE_ON 1 /* error log is switched on, wait for data */
#define ERRLOG_STATE_START 2 /* start error logging */
#define ERRLOG_STATE_STOP 3 /* stop error logging */
/*******************************/
/* data structure for one card */
/*******************************/
typedef struct HYSDN_CARD {
/* general variables for the cards */
int myid; /* own driver card id */
unsigned char bus; /* pci bus the card is connected to */
unsigned char devfn; /* slot+function bit encoded */
unsigned short subsysid;/* PCI subsystem id */
unsigned char brdtype; /* type of card */
unsigned int bchans; /* number of available B-channels */
unsigned int faxchans; /* number of available fax-channels */
unsigned char mac_addr[6];/* MAC Address read from card */
unsigned int irq; /* interrupt number */
unsigned int iobase; /* IO-port base address */
unsigned long plxbase; /* PLX memory base */
unsigned long membase; /* DPRAM memory base */
unsigned long memend; /* DPRAM memory end */
void *dpram; /* mapped dpram */
int state; /* actual state of card -> CARD_STATE_** */
struct HYSDN_CARD *next; /* pointer to next card */
/* data areas for the /proc file system */
void *proclog; /* pointer to proclog filesystem specific data */
void *procconf; /* pointer to procconf filesystem specific data */
/* debugging and logging */
unsigned char err_log_state;/* actual error log state of the card */
unsigned long debug_flags;/* tells what should be debugged and where */
void (*set_errlog_state) (struct HYSDN_CARD *, int);
/* interrupt handler + interrupt synchronisation */
struct work_struct irq_queue; /* interrupt task queue */
unsigned char volatile irq_enabled;/* interrupt enabled if != 0 */
unsigned char volatile hw_lock;/* hardware is currently locked -> no access */
/* boot process */
void *boot; /* pointer to boot private data */
int (*writebootimg) (struct HYSDN_CARD *, unsigned char *, unsigned long);
int (*writebootseq) (struct HYSDN_CARD *, unsigned char *, int);
int (*waitpofready) (struct HYSDN_CARD *);
int (*testram) (struct HYSDN_CARD *);
/* scheduler for data transfer (only async parts) */
unsigned char async_data[256];/* async data to be sent (normally for config) */
unsigned short volatile async_len;/* length of data to sent */
unsigned short volatile async_channel;/* channel number for async transfer */
int volatile async_busy; /* flag != 0 sending in progress */
int volatile net_tx_busy; /* a network packet tx is in progress */
/* network interface */
void *netif; /* pointer to network structure */
/* init and deinit stopcard for booting, too */
void (*stopcard) (struct HYSDN_CARD *);
void (*releasehardware) (struct HYSDN_CARD *);
spinlock_t hysdn_lock;
#ifdef CONFIG_HYSDN_CAPI
struct hycapictrl_info {
char cardname[32];
spinlock_t lock;
int versionlen;
char versionbuf[1024];
char *version[HYSDN_MAXVERSION];
char infobuf[128]; /* for function procinfo */
struct HYSDN_CARD *card;
struct capi_ctr capi_ctrl;
struct sk_buff *skbs[HYSDN_MAX_CAPI_SKB];
int in_idx, out_idx; /* indexes to buffer ring */
int sk_count; /* number of buffers currently in ring */
struct sk_buff *tx_skb; /* buffer for tx operation */
struct list_head ncci_head;
} *hyctrlinfo;
#endif /* CONFIG_HYSDN_CAPI */
} hysdn_card;
#ifdef CONFIG_HYSDN_CAPI
typedef struct hycapictrl_info hycapictrl_info;
#endif /* CONFIG_HYSDN_CAPI */
/*****************/
/* exported vars */
/*****************/
extern hysdn_card *card_root; /* pointer to first card */
/*************************/
/* im/exported functions */
/*************************/
extern char *hysdn_getrev(const char *);
/* hysdn_procconf.c */
extern int hysdn_procconf_init(void); /* init proc config filesys */
extern void hysdn_procconf_release(void); /* deinit proc config filesys */
/* hysdn_proclog.c */
extern int hysdn_proclog_init(hysdn_card *); /* init proc log entry */
extern void hysdn_proclog_release(hysdn_card *); /* deinit proc log entry */
extern void hysdn_addlog(hysdn_card *, char *,...); /* output data to log */
extern void hysdn_card_errlog(hysdn_card *, tErrLogEntry *, int); /* output card log */
/* boardergo.c */
extern int ergo_inithardware(hysdn_card * card); /* get hardware -> module init */
/* hysdn_boot.c */
extern int pof_write_close(hysdn_card *); /* close proc file after writing pof */
extern int pof_write_open(hysdn_card *, unsigned char **); /* open proc file for writing pof */
extern int pof_write_buffer(hysdn_card *, int); /* write boot data to card */
extern int EvalSysrTokData(hysdn_card *, unsigned char *, int); /* Check Sysready Token Data */
/* hysdn_sched.c */
extern int hysdn_sched_tx(hysdn_card *, unsigned char *,
unsigned short volatile *, unsigned short volatile *,
unsigned short);
extern int hysdn_sched_rx(hysdn_card *, unsigned char *, unsigned short,
unsigned short);
extern int hysdn_tx_cfgline(hysdn_card *, unsigned char *,
unsigned short); /* send one cfg line */
/* hysdn_net.c */
extern unsigned int hynet_enable;
extern char *hysdn_net_revision;
extern int hysdn_net_create(hysdn_card *); /* create a new net device */
extern int hysdn_net_release(hysdn_card *); /* delete the device */
extern char *hysdn_net_getname(hysdn_card *); /* get name of net interface */
extern void hysdn_tx_netack(hysdn_card *); /* acknowledge a packet tx */
extern struct sk_buff *hysdn_tx_netget(hysdn_card *); /* get next network packet */
extern void hysdn_rx_netpkt(hysdn_card *, unsigned char *,
unsigned short); /* rxed packet from network */
#ifdef CONFIG_HYSDN_CAPI
extern unsigned int hycapi_enable;
extern int hycapi_capi_create(hysdn_card *); /* create a new capi device */
extern int hycapi_capi_release(hysdn_card *); /* delete the device */
extern int hycapi_capi_stop(hysdn_card *card); /* suspend */
extern void hycapi_rx_capipkt(hysdn_card * card, unsigned char * buf,
unsigned short len);
extern void hycapi_tx_capiack(hysdn_card * card);
extern struct sk_buff *hycapi_tx_capiget(hysdn_card *card);
extern int hycapi_init(void);
extern void hycapi_cleanup(void);
#endif /* CONFIG_HYSDN_CAPI */
#endif /* HYSDN_DEFS_H */

View File

@@ -0,0 +1,237 @@
/* $Id: hysdn_init.c,v 1.6.6.6 2001/09/23 22:24:54 kai Exp $
*
* Linux driver for HYSDN cards, init functions.
*
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
* Copyright 1999 by Werner Cornelius (werner@titro.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include "hysdn_defs.h"
static struct pci_device_id hysdn_pci_tbl[] = {
{ PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_METRO, 0, 0, BD_METRO },
{ PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2, 0, 0, BD_CHAMP2 },
{ PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_ERGO, 0, 0, BD_ERGO },
{ PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO, 0, 0, BD_ERGO },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(pci, hysdn_pci_tbl);
MODULE_DESCRIPTION("ISDN4Linux: Driver for HYSDN cards");
MODULE_AUTHOR("Werner Cornelius");
MODULE_LICENSE("GPL");
static char *hysdn_init_revision = "$Revision: 1.6.6.6 $";
static int cardmax; /* number of found cards */
hysdn_card *card_root = NULL; /* pointer to first card */
static hysdn_card *card_last = NULL; /* pointer to first card */
/****************************************************************************/
/* The module startup and shutdown code. Only compiled when used as module. */
/* Using the driver as module is always advisable, because the booting */
/* image becomes smaller and the driver code is only loaded when needed. */
/* Additionally newer versions may be activated without rebooting. */
/****************************************************************************/
/******************************************************/
/* extract revision number from string for log output */
/******************************************************/
char *
hysdn_getrev(const char *revision)
{
char *rev;
char *p;
if ((p = strchr(revision, ':'))) {
rev = p + 2;
p = strchr(rev, '$');
*--p = 0;
} else
rev = "???";
return rev;
}
/****************************************************************************/
/* init_module is called once when the module is loaded to do all necessary */
/* things like autodetect... */
/* If the return value of this function is 0 the init has been successful */
/* and the module is added to the list in /proc/modules, otherwise an error */
/* is assumed and the module will not be kept in memory. */
/****************************************************************************/
static int __devinit hysdn_pci_init_one(struct pci_dev *akt_pcidev,
const struct pci_device_id *ent)
{
hysdn_card *card;
int rc;
rc = pci_enable_device(akt_pcidev);
if (rc)
return rc;
if (!(card = kzalloc(sizeof(hysdn_card), GFP_KERNEL))) {
printk(KERN_ERR "HYSDN: unable to alloc device mem \n");
rc = -ENOMEM;
goto err_out;
}
card->myid = cardmax; /* set own id */
card->bus = akt_pcidev->bus->number;
card->devfn = akt_pcidev->devfn; /* slot + function */
card->subsysid = akt_pcidev->subsystem_device;
card->irq = akt_pcidev->irq;
card->iobase = pci_resource_start(akt_pcidev, PCI_REG_PLX_IO_BASE);
card->plxbase = pci_resource_start(akt_pcidev, PCI_REG_PLX_MEM_BASE);
card->membase = pci_resource_start(akt_pcidev, PCI_REG_MEMORY_BASE);
card->brdtype = BD_NONE; /* unknown */
card->debug_flags = DEF_DEB_FLAGS; /* set default debug */
card->faxchans = 0; /* default no fax channels */
card->bchans = 2; /* and 2 b-channels */
card->brdtype = ent->driver_data;
if (ergo_inithardware(card)) {
printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase);
rc = -EBUSY;
goto err_out_card;
}
cardmax++;
card->next = NULL; /*end of chain */
if (card_last)
card_last->next = card; /* pointer to next card */
else
card_root = card;
card_last = card; /* new chain end */
pci_set_drvdata(akt_pcidev, card);
return 0;
err_out_card:
kfree(card);
err_out:
pci_disable_device(akt_pcidev);
return rc;
}
static void __devexit hysdn_pci_remove_one(struct pci_dev *akt_pcidev)
{
hysdn_card *card = pci_get_drvdata(akt_pcidev);
pci_set_drvdata(akt_pcidev, NULL);
if (card->stopcard)
card->stopcard(card);
#ifdef CONFIG_HYSDN_CAPI
hycapi_capi_release(card);
#endif
if (card->releasehardware)
card->releasehardware(card); /* free all hardware resources */
if (card == card_root) {
card_root = card_root->next;
if (!card_root)
card_last = NULL;
} else {
hysdn_card *tmp = card_root;
while (tmp) {
if (tmp->next == card)
tmp->next = card->next;
card_last = tmp;
tmp = tmp->next;
}
}
kfree(card);
pci_disable_device(akt_pcidev);
}
static struct pci_driver hysdn_pci_driver = {
.name = "hysdn",
.id_table = hysdn_pci_tbl,
.probe = hysdn_pci_init_one,
.remove = __devexit_p(hysdn_pci_remove_one),
};
static int hysdn_have_procfs;
static int __init
hysdn_init(void)
{
char tmp[50];
int rc;
strcpy(tmp, hysdn_init_revision);
printk(KERN_NOTICE "HYSDN: module Rev: %s loaded\n", hysdn_getrev(tmp));
strcpy(tmp, hysdn_net_revision);
printk(KERN_NOTICE "HYSDN: network interface Rev: %s \n", hysdn_getrev(tmp));
rc = pci_register_driver(&hysdn_pci_driver);
if (rc)
return rc;
printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax);
if (!hysdn_procconf_init())
hysdn_have_procfs = 1;
#ifdef CONFIG_HYSDN_CAPI
if(cardmax > 0) {
if(hycapi_init()) {
printk(KERN_ERR "HYCAPI: init failed\n");
if (hysdn_have_procfs)
hysdn_procconf_release();
pci_unregister_driver(&hysdn_pci_driver);
return -ESPIPE;
}
}
#endif /* CONFIG_HYSDN_CAPI */
return 0; /* no error */
} /* init_module */
/***********************************************************************/
/* cleanup_module is called when the module is released by the kernel. */
/* The routine is only called if init_module has been successful and */
/* the module counter has a value of 0. Otherwise this function will */
/* not be called. This function must release all resources still allo- */
/* cated as after the return from this function the module code will */
/* be removed from memory. */
/***********************************************************************/
static void __exit
hysdn_exit(void)
{
if (hysdn_have_procfs)
hysdn_procconf_release();
pci_unregister_driver(&hysdn_pci_driver);
#ifdef CONFIG_HYSDN_CAPI
hycapi_cleanup();
#endif /* CONFIG_HYSDN_CAPI */
printk(KERN_NOTICE "HYSDN: module unloaded\n");
} /* cleanup_module */
module_init(hysdn_init);
module_exit(hysdn_exit);

View File

@@ -0,0 +1,329 @@
/* $Id: hysdn_net.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
*
* Linux driver for HYSDN cards, net (ethernet type) handling routines.
*
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
* Copyright 1999 by Werner Cornelius (werner@titro.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
* This net module has been inspired by the skeleton driver from
* Donald Becker (becker@CESDIS.gsfc.nasa.gov)
*
*/
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/inetdevice.h>
#include "hysdn_defs.h"
unsigned int hynet_enable = 0xffffffff;
module_param(hynet_enable, uint, 0);
/* store the actual version for log reporting */
char *hysdn_net_revision = "$Revision: 1.8.6.4 $";
#define MAX_SKB_BUFFERS 20 /* number of buffers for keeping TX-data */
/****************************************************************************/
/* structure containing the complete network data. The structure is aligned */
/* in a way that both, the device and statistics are kept inside it. */
/* for proper access, the device structure MUST be the first var/struct */
/* inside the definition. */
/****************************************************************************/
struct net_local {
/* Tx control lock. This protects the transmit buffer ring
* state along with the "tx full" state of the driver. This
* means all netif_queue flow control actions are protected
* by this lock as well.
*/
struct net_device *dev;
spinlock_t lock;
struct sk_buff *skbs[MAX_SKB_BUFFERS]; /* pointers to tx-skbs */
int in_idx, out_idx; /* indexes to buffer ring */
int sk_count; /* number of buffers currently in ring */
}; /* net_local */
/*********************************************************************/
/* Open/initialize the board. This is called (in the current kernel) */
/* sometime after booting when the 'ifconfig' program is run. */
/* This routine should set everything up anew at each open, even */
/* registers that "should" only need to be set once at boot, so that */
/* there is non-reboot way to recover if something goes wrong. */
/*********************************************************************/
static int
net_open(struct net_device *dev)
{
struct in_device *in_dev;
hysdn_card *card = dev->ml_priv;
int i;
netif_start_queue(dev); /* start tx-queueing */
/* Fill in the MAC-level header (if not already set) */
if (!card->mac_addr[0]) {
for (i = 0; i < ETH_ALEN; i++)
dev->dev_addr[i] = 0xfc;
if ((in_dev = dev->ip_ptr) != NULL) {
struct in_ifaddr *ifa = in_dev->ifa_list;
if (ifa != NULL)
memcpy(dev->dev_addr + (ETH_ALEN - sizeof(ifa->ifa_local)), &ifa->ifa_local, sizeof(ifa->ifa_local));
}
} else
memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN);
return (0);
} /* net_open */
/*******************************************/
/* flush the currently occupied tx-buffers */
/* must only be called when device closed */
/*******************************************/
static void
flush_tx_buffers(struct net_local *nl)
{
while (nl->sk_count) {
dev_kfree_skb(nl->skbs[nl->out_idx++]); /* free skb */
if (nl->out_idx >= MAX_SKB_BUFFERS)
nl->out_idx = 0; /* wrap around */
nl->sk_count--;
}
} /* flush_tx_buffers */
/*********************************************************************/
/* close/decativate the device. The device is not removed, but only */
/* deactivated. */
/*********************************************************************/
static int
net_close(struct net_device *dev)
{
netif_stop_queue(dev); /* disable queueing */
flush_tx_buffers((struct net_local *) dev);
return (0); /* success */
} /* net_close */
/************************************/
/* send a packet on this interface. */
/* new style for kernel >= 2.3.33 */
/************************************/
static netdev_tx_t
net_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct net_local *lp = (struct net_local *) dev;
spin_lock_irq(&lp->lock);
lp->skbs[lp->in_idx++] = skb; /* add to buffer list */
if (lp->in_idx >= MAX_SKB_BUFFERS)
lp->in_idx = 0; /* wrap around */
lp->sk_count++; /* adjust counter */
dev->trans_start = jiffies;
/* If we just used up the very last entry in the
* TX ring on this device, tell the queueing
* layer to send no more.
*/
if (lp->sk_count >= MAX_SKB_BUFFERS)
netif_stop_queue(dev);
/* When the TX completion hw interrupt arrives, this
* is when the transmit statistics are updated.
*/
spin_unlock_irq(&lp->lock);
if (lp->sk_count <= 3) {
schedule_work(&((hysdn_card *) dev->ml_priv)->irq_queue);
}
return NETDEV_TX_OK; /* success */
} /* net_send_packet */
/***********************************************************************/
/* acknowlegde a packet send. The network layer will be informed about */
/* completion */
/***********************************************************************/
void
hysdn_tx_netack(hysdn_card * card)
{
struct net_local *lp = card->netif;
if (!lp)
return; /* non existing device */
if (!lp->sk_count)
return; /* error condition */
lp->dev->stats.tx_packets++;
lp->dev->stats.tx_bytes += lp->skbs[lp->out_idx]->len;
dev_kfree_skb(lp->skbs[lp->out_idx++]); /* free skb */
if (lp->out_idx >= MAX_SKB_BUFFERS)
lp->out_idx = 0; /* wrap around */
if (lp->sk_count-- == MAX_SKB_BUFFERS) /* dec usage count */
netif_start_queue((struct net_device *) lp);
} /* hysdn_tx_netack */
/*****************************************************/
/* we got a packet from the network, go and queue it */
/*****************************************************/
void
hysdn_rx_netpkt(hysdn_card * card, unsigned char *buf, unsigned short len)
{
struct net_local *lp = card->netif;
struct net_device *dev = lp->dev;
struct sk_buff *skb;
if (!lp)
return; /* non existing device */
dev->stats.rx_bytes += len;
skb = dev_alloc_skb(len);
if (skb == NULL) {
printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
dev->name);
dev->stats.rx_dropped++;
return;
}
/* copy the data */
memcpy(skb_put(skb, len), buf, len);
/* determine the used protocol */
skb->protocol = eth_type_trans(skb, dev);
dev->stats.rx_packets++; /* adjust packet count */
netif_rx(skb);
} /* hysdn_rx_netpkt */
/*****************************************************/
/* return the pointer to a network packet to be send */
/*****************************************************/
struct sk_buff *
hysdn_tx_netget(hysdn_card * card)
{
struct net_local *lp = card->netif;
if (!lp)
return (NULL); /* non existing device */
if (!lp->sk_count)
return (NULL); /* nothing available */
return (lp->skbs[lp->out_idx]); /* next packet to send */
} /* hysdn_tx_netget */
static const struct net_device_ops hysdn_netdev_ops = {
.ndo_open = net_open,
.ndo_stop = net_close,
.ndo_start_xmit = net_send_packet,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
/*****************************************************************************/
/* hysdn_net_create creates a new net device for the given card. If a device */
/* already exists, it will be deleted and created a new one. The return value */
/* 0 announces success, else a negative error code will be returned. */
/*****************************************************************************/
int
hysdn_net_create(hysdn_card * card)
{
struct net_device *dev;
int i;
struct net_local *lp;
if(!card) {
printk(KERN_WARNING "No card-pt in hysdn_net_create!\n");
return (-ENOMEM);
}
hysdn_net_release(card); /* release an existing net device */
dev = alloc_etherdev(sizeof(struct net_local));
if (!dev) {
printk(KERN_WARNING "HYSDN: unable to allocate mem\n");
return (-ENOMEM);
}
lp = netdev_priv(dev);
lp->dev = dev;
dev->netdev_ops = &hysdn_netdev_ops;
spin_lock_init(&((struct net_local *) dev)->lock);
/* initialise necessary or informing fields */
dev->base_addr = card->iobase; /* IO address */
dev->irq = card->irq; /* irq */
dev->netdev_ops = &hysdn_netdev_ops;
if ((i = register_netdev(dev))) {
printk(KERN_WARNING "HYSDN: unable to create network device\n");
free_netdev(dev);
return (i);
}
dev->ml_priv = card; /* remember pointer to own data structure */
card->netif = dev; /* setup the local pointer */
if (card->debug_flags & LOG_NET_INIT)
hysdn_addlog(card, "network device created");
return (0); /* and return success */
} /* hysdn_net_create */
/***************************************************************************/
/* hysdn_net_release deletes the net device for the given card. The return */
/* value 0 announces success, else a negative error code will be returned. */
/***************************************************************************/
int
hysdn_net_release(hysdn_card * card)
{
struct net_device *dev = card->netif;
if (!dev)
return (0); /* non existing */
card->netif = NULL; /* clear out pointer */
net_close(dev);
flush_tx_buffers((struct net_local *) dev); /* empty buffers */
unregister_netdev(dev); /* release the device */
free_netdev(dev); /* release the memory allocated */
if (card->debug_flags & LOG_NET_INIT)
hysdn_addlog(card, "network device deleted");
return (0); /* always successful */
} /* hysdn_net_release */
/*****************************************************************************/
/* hysdn_net_getname returns a pointer to the name of the network interface. */
/* if the interface is not existing, a "-" is returned. */
/*****************************************************************************/
char *
hysdn_net_getname(hysdn_card * card)
{
struct net_device *dev = card->netif;
if (!dev)
return ("-"); /* non existing */
return (dev->name);
} /* hysdn_net_getname */

View File

@@ -0,0 +1,78 @@
/* $Id: hysdn_pof.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
*
* Linux driver for HYSDN cards, definitions used for handling pof-files.
*
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
* Copyright 1999 by Werner Cornelius (werner@titro.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
/************************/
/* POF specific defines */
/************************/
#define BOOT_BUF_SIZE 0x1000 /* =4096, maybe moved to other h file */
#define CRYPT_FEEDTERM 0x8142
#define CRYPT_STARTTERM 0x81a5
/* max. timeout time in seconds
* from end of booting to POF is ready
*/
#define POF_READY_TIME_OUT_SEC 10
/**********************************/
/* defines for 1.stage boot image */
/**********************************/
/* the POF file record containing the boot loader image
* has 2 pages a 16KB:
* 1. page contains the high 16-bit part of the 32-bit E1 words
* 2. page contains the low 16-bit part of the 32-bit E1 words
*
* In each 16KB page we assume the start of the boot loader code
* in the highest 2KB part (at offset 0x3800);
* the rest (0x0000..0x37FF) is assumed to contain 0 bytes.
*/
#define POF_BOOT_LOADER_PAGE_SIZE 0x4000 /* =16384U */
#define POF_BOOT_LOADER_TOTAL_SIZE (2U*POF_BOOT_LOADER_PAGE_SIZE)
#define POF_BOOT_LOADER_CODE_SIZE 0x0800 /* =2KB =2048U */
/* offset in boot page, where loader code may start */
/* =0x3800= 14336U */
#define POF_BOOT_LOADER_OFF_IN_PAGE (POF_BOOT_LOADER_PAGE_SIZE-POF_BOOT_LOADER_CODE_SIZE)
/*--------------------------------------POF file record structs------------*/
typedef struct PofFileHdr_tag { /* Pof file header */
/*00 */ unsigned long Magic __attribute__((packed));
/*04 */ unsigned long N_PofRecs __attribute__((packed));
/*08 */
} tPofFileHdr;
typedef struct PofRecHdr_tag { /* Pof record header */
/*00 */ unsigned short PofRecId __attribute__((packed));
/*02 */ unsigned long PofRecDataLen __attribute__((packed));
/*06 */
} tPofRecHdr;
typedef struct PofTimeStamp_tag {
/*00 */ unsigned long UnixTime __attribute__((packed));
/*04 */ unsigned char DateTimeText[0x28];
/* =40 */
/*2C */
} tPofTimeStamp;
/* tPofFileHdr.Magic value: */
#define TAGFILEMAGIC 0x464F501AUL
/* tPofRecHdr.PofRecId values: */
#define TAG_ABSDATA 0x1000 /* abs. data */
#define TAG_BOOTDTA 0x1001 /* boot data */
#define TAG_COMMENT 0x0020
#define TAG_SYSCALL 0x0021
#define TAG_FLOWCTRL 0x0022
#define TAG_TIMESTMP 0x0010 /* date/time stamp of version */
#define TAG_CABSDATA 0x1100 /* crypted abs. data */
#define TAG_CBOOTDTA 0x1101 /* crypted boot data */

View File

@@ -0,0 +1,432 @@
/* $Id: hysdn_procconf.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
*
* Linux driver for HYSDN cards, /proc/net filesystem dir and conf functions.
*
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
*
* Copyright 1999 by Werner Cornelius (werner@titro.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/cred.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/pci.h>
#include <linux/smp_lock.h>
#include <net/net_namespace.h>
#include "hysdn_defs.h"
static char *hysdn_procconf_revision = "$Revision: 1.8.6.4 $";
#define INFO_OUT_LEN 80 /* length of info line including lf */
/********************************************************/
/* defines and data structure for conf write operations */
/********************************************************/
#define CONF_STATE_DETECT 0 /* waiting for detect */
#define CONF_STATE_CONF 1 /* writing config data */
#define CONF_STATE_POF 2 /* writing pof data */
#define CONF_LINE_LEN 255 /* 255 chars max */
struct conf_writedata {
hysdn_card *card; /* card the device is connected to */
int buf_size; /* actual number of bytes in the buffer */
int needed_size; /* needed size when reading pof */
int state; /* actual interface states from above constants */
unsigned char conf_line[CONF_LINE_LEN]; /* buffered conf line */
unsigned short channel; /* active channel number */
unsigned char *pof_buffer; /* buffer when writing pof */
};
/***********************************************************************/
/* process_line parses one config line and transfers it to the card if */
/* necessary. */
/* if the return value is negative an error occurred. */
/***********************************************************************/
static int
process_line(struct conf_writedata *cnf)
{
unsigned char *cp = cnf->conf_line;
int i;
if (cnf->card->debug_flags & LOG_CNF_LINE)
hysdn_addlog(cnf->card, "conf line: %s", cp);
if (*cp == '-') { /* option */
cp++; /* point to option char */
if (*cp++ != 'c')
return (0); /* option unknown or used */
i = 0; /* start value for channel */
while ((*cp <= '9') && (*cp >= '0'))
i = i * 10 + *cp++ - '0'; /* get decimal number */
if (i > 65535) {
if (cnf->card->debug_flags & LOG_CNF_MISC)
hysdn_addlog(cnf->card, "conf channel invalid %d", i);
return (-ERR_INV_CHAN); /* invalid channel */
}
cnf->channel = i & 0xFFFF; /* set new channel number */
return (0); /* success */
} /* option */
if (*cp == '*') { /* line to send */
if (cnf->card->debug_flags & LOG_CNF_DATA)
hysdn_addlog(cnf->card, "conf chan=%d %s", cnf->channel, cp);
return (hysdn_tx_cfgline(cnf->card, cnf->conf_line + 1,
cnf->channel)); /* send the line without * */
} /* line to send */
return (0);
} /* process_line */
/***********************************/
/* conf file operations and tables */
/***********************************/
/****************************************************/
/* write conf file -> boot or send cfg line to card */
/****************************************************/
static ssize_t
hysdn_conf_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
{
struct conf_writedata *cnf;
int i;
unsigned char ch, *cp;
if (!count)
return (0); /* nothing to handle */
if (!(cnf = file->private_data))
return (-EFAULT); /* should never happen */
if (cnf->state == CONF_STATE_DETECT) { /* auto detect cnf or pof data */
if (copy_from_user(&ch, buf, 1)) /* get first char for detect */
return (-EFAULT);
if (ch == 0x1A) {
/* we detected a pof file */
if ((cnf->needed_size = pof_write_open(cnf->card, &cnf->pof_buffer)) <= 0)
return (cnf->needed_size); /* an error occurred -> exit */
cnf->buf_size = 0; /* buffer is empty */
cnf->state = CONF_STATE_POF; /* new state */
} else {
/* conf data has been detected */
cnf->buf_size = 0; /* buffer is empty */
cnf->state = CONF_STATE_CONF; /* requested conf data write */
if (cnf->card->state != CARD_STATE_RUN)
return (-ERR_NOT_BOOTED);
cnf->conf_line[CONF_LINE_LEN - 1] = 0; /* limit string length */
cnf->channel = 4098; /* default channel for output */
}
} /* state was auto detect */
if (cnf->state == CONF_STATE_POF) { /* pof write active */
i = cnf->needed_size - cnf->buf_size; /* bytes still missing for write */
if (i <= 0)
return (-EINVAL); /* size error handling pof */
if (i < count)
count = i; /* limit requested number of bytes */
if (copy_from_user(cnf->pof_buffer + cnf->buf_size, buf, count))
return (-EFAULT); /* error while copying */
cnf->buf_size += count;
if (cnf->needed_size == cnf->buf_size) {
cnf->needed_size = pof_write_buffer(cnf->card, cnf->buf_size); /* write data */
if (cnf->needed_size <= 0) {
cnf->card->state = CARD_STATE_BOOTERR; /* show boot error */
return (cnf->needed_size); /* an error occurred */
}
cnf->buf_size = 0; /* buffer is empty again */
}
}
/* pof write active */
else { /* conf write active */
if (cnf->card->state != CARD_STATE_RUN) {
if (cnf->card->debug_flags & LOG_CNF_MISC)
hysdn_addlog(cnf->card, "cnf write denied -> not booted");
return (-ERR_NOT_BOOTED);
}
i = (CONF_LINE_LEN - 1) - cnf->buf_size; /* bytes available in buffer */
if (i > 0) {
/* copy remaining bytes into buffer */
if (count > i)
count = i; /* limit transfer */
if (copy_from_user(cnf->conf_line + cnf->buf_size, buf, count))
return (-EFAULT); /* error while copying */
i = count; /* number of chars in buffer */
cp = cnf->conf_line + cnf->buf_size;
while (i) {
/* search for end of line */
if ((*cp < ' ') && (*cp != 9))
break; /* end of line found */
cp++;
i--;
} /* search for end of line */
if (i) {
/* delimiter found */
*cp++ = 0; /* string termination */
count -= (i - 1); /* subtract remaining bytes from count */
while ((i) && (*cp < ' ') && (*cp != 9)) {
i--; /* discard next char */
count++; /* mark as read */
cp++; /* next char */
}
cnf->buf_size = 0; /* buffer is empty after transfer */
if ((i = process_line(cnf)) < 0) /* handle the line */
count = i; /* return the error */
}
/* delimiter found */
else {
cnf->buf_size += count; /* add chars to string */
if (cnf->buf_size >= CONF_LINE_LEN - 1) {
if (cnf->card->debug_flags & LOG_CNF_MISC)
hysdn_addlog(cnf->card, "cnf line too long %d chars pos %d", cnf->buf_size, count);
return (-ERR_CONF_LONG);
}
} /* not delimited */
}
/* copy remaining bytes into buffer */
else {
if (cnf->card->debug_flags & LOG_CNF_MISC)
hysdn_addlog(cnf->card, "cnf line too long");
return (-ERR_CONF_LONG);
}
} /* conf write active */
return (count);
} /* hysdn_conf_write */
/*******************************************/
/* read conf file -> output card info data */
/*******************************************/
static ssize_t
hysdn_conf_read(struct file *file, char __user *buf, size_t count, loff_t *off)
{
char *cp;
if (!(file->f_mode & FMODE_READ))
return -EPERM; /* no permission to read */
if (!(cp = file->private_data))
return -EFAULT; /* should never happen */
return simple_read_from_buffer(buf, count, off, cp, strlen(cp));
} /* hysdn_conf_read */
/******************/
/* open conf file */
/******************/
static int
hysdn_conf_open(struct inode *ino, struct file *filep)
{
hysdn_card *card;
struct proc_dir_entry *pd;
struct conf_writedata *cnf;
char *cp, *tmp;
/* now search the addressed card */
lock_kernel();
card = card_root;
while (card) {
pd = card->procconf;
if (pd == PDE(ino))
break;
card = card->next; /* search next entry */
}
if (!card) {
unlock_kernel();
return (-ENODEV); /* device is unknown/invalid */
}
if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x",
filep->f_cred->fsuid, filep->f_cred->fsgid,
filep->f_mode);
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
/* write only access -> write boot file or conf line */
if (!(cnf = kmalloc(sizeof(struct conf_writedata), GFP_KERNEL))) {
unlock_kernel();
return (-EFAULT);
}
cnf->card = card;
cnf->buf_size = 0; /* nothing buffered */
cnf->state = CONF_STATE_DETECT; /* start auto detect */
filep->private_data = cnf;
} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
/* read access -> output card info data */
if (!(tmp = kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
unlock_kernel();
return (-EFAULT); /* out of memory */
}
filep->private_data = tmp; /* start of string */
/* first output a headline */
sprintf(tmp, "id bus slot type irq iobase dp-mem b-chans fax-chans state device");
cp = tmp; /* start of string */
while (*cp)
cp++;
while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
*cp++ = ' ';
*cp++ = '\n';
/* and now the data */
sprintf(cp, "%d %3d %4d %4d %3d 0x%04x 0x%08lx %7d %9d %3d %s",
card->myid,
card->bus,
PCI_SLOT(card->devfn),
card->brdtype,
card->irq,
card->iobase,
card->membase,
card->bchans,
card->faxchans,
card->state,
hysdn_net_getname(card));
while (*cp)
cp++;
while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
*cp++ = ' ';
*cp++ = '\n';
*cp = 0; /* end of string */
} else { /* simultaneous read/write access forbidden ! */
unlock_kernel();
return (-EPERM); /* no permission this time */
}
unlock_kernel();
return nonseekable_open(ino, filep);
} /* hysdn_conf_open */
/***************************/
/* close a config file. */
/***************************/
static int
hysdn_conf_close(struct inode *ino, struct file *filep)
{
hysdn_card *card;
struct conf_writedata *cnf;
int retval = 0;
struct proc_dir_entry *pd;
lock_kernel();
/* search the addressed card */
card = card_root;
while (card) {
pd = card->procconf;
if (pd == PDE(ino))
break;
card = card->next; /* search next entry */
}
if (!card) {
unlock_kernel();
return (-ENODEV); /* device is unknown/invalid */
}
if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x",
filep->f_cred->fsuid, filep->f_cred->fsgid,
filep->f_mode);
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
/* write only access -> write boot file or conf line */
if (filep->private_data) {
cnf = filep->private_data;
if (cnf->state == CONF_STATE_POF)
retval = pof_write_close(cnf->card); /* close the pof write */
kfree(filep->private_data); /* free allocated memory for buffer */
} /* handle write private data */
} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
/* read access -> output card info data */
kfree(filep->private_data); /* release memory */
}
unlock_kernel();
return (retval);
} /* hysdn_conf_close */
/******************************************************/
/* table for conf filesystem functions defined above. */
/******************************************************/
static const struct file_operations conf_fops =
{
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = hysdn_conf_read,
.write = hysdn_conf_write,
.open = hysdn_conf_open,
.release = hysdn_conf_close,
};
/*****************************/
/* hysdn subdir in /proc/net */
/*****************************/
struct proc_dir_entry *hysdn_proc_entry = NULL;
/*******************************************************************************/
/* hysdn_procconf_init is called when the module is loaded and after the cards */
/* have been detected. The needed proc dir and card config files are created. */
/* The log init is called at last. */
/*******************************************************************************/
int
hysdn_procconf_init(void)
{
hysdn_card *card;
unsigned char conf_name[20];
hysdn_proc_entry = proc_mkdir(PROC_SUBDIR_NAME, init_net.proc_net);
if (!hysdn_proc_entry) {
printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
return (-1);
}
card = card_root; /* point to first card */
while (card) {
sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
if ((card->procconf = (void *) proc_create(conf_name,
S_IFREG | S_IRUGO | S_IWUSR,
hysdn_proc_entry,
&conf_fops)) != NULL) {
hysdn_proclog_init(card); /* init the log file entry */
}
card = card->next; /* next entry */
}
printk(KERN_NOTICE "HYSDN: procfs Rev. %s initialised\n", hysdn_getrev(hysdn_procconf_revision));
return (0);
} /* hysdn_procconf_init */
/*************************************************************************************/
/* hysdn_procconf_release is called when the module is unloaded and before the cards */
/* resources are released. The module counter is assumed to be 0 ! */
/*************************************************************************************/
void
hysdn_procconf_release(void)
{
hysdn_card *card;
unsigned char conf_name[20];
card = card_root; /* start with first card */
while (card) {
sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
if (card->procconf)
remove_proc_entry(conf_name, hysdn_proc_entry);
hysdn_proclog_release(card); /* init the log file entry */
card = card->next; /* point to next card */
}
remove_proc_entry(PROC_SUBDIR_NAME, init_net.proc_net);
}

View File

@@ -0,0 +1,434 @@
/* $Id: hysdn_proclog.c,v 1.9.6.3 2001/09/23 22:24:54 kai Exp $
*
* Linux driver for HYSDN cards, /proc/net filesystem log functions.
*
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
* Copyright 1999 by Werner Cornelius (werner@titro.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include "hysdn_defs.h"
/* the proc subdir for the interface is defined in the procconf module */
extern struct proc_dir_entry *hysdn_proc_entry;
static void put_log_buffer(hysdn_card * card, char *cp);
/*************************************************/
/* structure keeping ascii log for device output */
/*************************************************/
struct log_data {
struct log_data *next;
unsigned long usage_cnt;/* number of files still to work */
void *proc_ctrl; /* pointer to own control procdata structure */
char log_start[2]; /* log string start (final len aligned by size) */
};
/**********************************************/
/* structure holding proc entrys for one card */
/**********************************************/
struct procdata {
struct proc_dir_entry *log; /* log entry */
char log_name[15]; /* log filename */
struct log_data *log_head, *log_tail; /* head and tail for queue */
int if_used; /* open count for interface */
int volatile del_lock; /* lock for delete operations */
unsigned char logtmp[LOG_MAX_LINELEN];
wait_queue_head_t rd_queue;
};
/**********************************************/
/* log function for cards error log interface */
/**********************************************/
void
hysdn_card_errlog(hysdn_card * card, tErrLogEntry * logp, int maxsize)
{
char buf[ERRLOG_TEXT_SIZE + 40];
sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText);
put_log_buffer(card, buf); /* output the string */
} /* hysdn_card_errlog */
/***************************************************/
/* Log function using format specifiers for output */
/***************************************************/
void
hysdn_addlog(hysdn_card * card, char *fmt,...)
{
struct procdata *pd = card->proclog;
char *cp;
va_list args;
if (!pd)
return; /* log structure non existent */
cp = pd->logtmp;
cp += sprintf(cp, "HYSDN: card %d ", card->myid);
va_start(args, fmt);
cp += vsprintf(cp, fmt, args);
va_end(args);
*cp++ = '\n';
*cp = 0;
if (card->debug_flags & DEB_OUT_SYSLOG)
printk(KERN_INFO "%s", pd->logtmp);
else
put_log_buffer(card, pd->logtmp);
} /* hysdn_addlog */
/********************************************/
/* put an log buffer into the log queue. */
/* This buffer will be kept until all files */
/* opened for read got the contents. */
/* Flushes buffers not longer in use. */
/********************************************/
static void
put_log_buffer(hysdn_card * card, char *cp)
{
struct log_data *ib;
struct procdata *pd = card->proclog;
int i;
unsigned long flags;
if (!pd)
return;
if (!cp)
return;
if (!*cp)
return;
if (pd->if_used <= 0)
return; /* no open file for read */
if (!(ib = kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
return; /* no memory */
strcpy(ib->log_start, cp); /* set output string */
ib->next = NULL;
ib->proc_ctrl = pd; /* point to own control structure */
spin_lock_irqsave(&card->hysdn_lock, flags);
ib->usage_cnt = pd->if_used;
if (!pd->log_head)
pd->log_head = ib; /* new head */
else
pd->log_tail->next = ib; /* follows existing messages */
pd->log_tail = ib; /* new tail */
i = pd->del_lock++; /* get lock state */
spin_unlock_irqrestore(&card->hysdn_lock, flags);
/* delete old entrys */
if (!i)
while (pd->log_head->next) {
if ((pd->log_head->usage_cnt <= 0) &&
(pd->log_head->next->usage_cnt <= 0)) {
ib = pd->log_head;
pd->log_head = pd->log_head->next;
kfree(ib);
} else
break;
} /* pd->log_head->next */
pd->del_lock--; /* release lock level */
wake_up_interruptible(&(pd->rd_queue)); /* announce new entry */
} /* put_log_buffer */
/******************************/
/* file operations and tables */
/******************************/
/****************************************/
/* write log file -> set log level bits */
/****************************************/
static ssize_t
hysdn_log_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
{
unsigned long u = 0;
int found = 0;
unsigned char *cp, valbuf[128];
long base = 10;
hysdn_card *card = (hysdn_card *) file->private_data;
if (count > (sizeof(valbuf) - 1))
count = sizeof(valbuf) - 1; /* limit length */
if (copy_from_user(valbuf, buf, count))
return (-EFAULT); /* copy failed */
valbuf[count] = 0; /* terminating 0 */
cp = valbuf;
if ((count > 2) && (valbuf[0] == '0') && (valbuf[1] == 'x')) {
cp += 2; /* pointer after hex modifier */
base = 16;
}
/* scan the input for debug flags */
while (*cp) {
if ((*cp >= '0') && (*cp <= '9')) {
found = 1;
u *= base; /* adjust to next digit */
u += *cp++ - '0';
continue;
}
if (base != 16)
break; /* end of number */
if ((*cp >= 'a') && (*cp <= 'f')) {
found = 1;
u *= base; /* adjust to next digit */
u += *cp++ - 'a' + 10;
continue;
}
break; /* terminated */
}
if (found) {
card->debug_flags = u; /* remember debug flags */
hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags);
}
return (count);
} /* hysdn_log_write */
/******************/
/* read log file */
/******************/
static ssize_t
hysdn_log_read(struct file *file, char __user *buf, size_t count, loff_t * off)
{
struct log_data *inf;
int len;
struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
struct procdata *pd = NULL;
hysdn_card *card;
if (!*((struct log_data **) file->private_data)) {
if (file->f_flags & O_NONBLOCK)
return (-EAGAIN);
/* sorry, but we need to search the card */
card = card_root;
while (card) {
pd = card->proclog;
if (pd->log == pde)
break;
card = card->next; /* search next entry */
}
if (card)
interruptible_sleep_on(&(pd->rd_queue));
else
return (-EAGAIN);
}
if (!(inf = *((struct log_data **) file->private_data)))
return (0);
inf->usage_cnt--; /* new usage count */
file->private_data = &inf->next; /* next structure */
if ((len = strlen(inf->log_start)) <= count) {
if (copy_to_user(buf, inf->log_start, len))
return -EFAULT;
*off += len;
return (len);
}
return (0);
} /* hysdn_log_read */
/******************/
/* open log file */
/******************/
static int
hysdn_log_open(struct inode *ino, struct file *filep)
{
hysdn_card *card;
struct procdata *pd = NULL;
unsigned long flags;
lock_kernel();
card = card_root;
while (card) {
pd = card->proclog;
if (pd->log == PDE(ino))
break;
card = card->next; /* search next entry */
}
if (!card) {
unlock_kernel();
return (-ENODEV); /* device is unknown/invalid */
}
filep->private_data = card; /* remember our own card */
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
/* write only access -> write log level only */
} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
/* read access -> log/debug read */
spin_lock_irqsave(&card->hysdn_lock, flags);
pd->if_used++;
if (pd->log_head)
filep->private_data = &pd->log_tail->next;
else
filep->private_data = &pd->log_head;
spin_unlock_irqrestore(&card->hysdn_lock, flags);
} else { /* simultaneous read/write access forbidden ! */
unlock_kernel();
return (-EPERM); /* no permission this time */
}
unlock_kernel();
return nonseekable_open(ino, filep);
} /* hysdn_log_open */
/*******************************************************************************/
/* close a cardlog file. If the file has been opened for exclusive write it is */
/* assumed as pof data input and the pof loader is noticed about. */
/* Otherwise file is handled as log output. In this case the interface usage */
/* count is decremented and all buffers are noticed of closing. If this file */
/* was the last one to be closed, all buffers are freed. */
/*******************************************************************************/
static int
hysdn_log_close(struct inode *ino, struct file *filep)
{
struct log_data *inf;
struct procdata *pd;
hysdn_card *card;
int retval = 0;
lock_kernel();
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
/* write only access -> write debug level written */
retval = 0; /* success */
} else {
/* read access -> log/debug read, mark one further file as closed */
pd = NULL;
inf = *((struct log_data **) filep->private_data); /* get first log entry */
if (inf)
pd = (struct procdata *) inf->proc_ctrl; /* still entries there */
else {
/* no info available -> search card */
card = card_root;
while (card) {
pd = card->proclog;
if (pd->log == PDE(ino))
break;
card = card->next; /* search next entry */
}
if (card)
pd = card->proclog; /* pointer to procfs log */
}
if (pd)
pd->if_used--; /* decrement interface usage count by one */
while (inf) {
inf->usage_cnt--; /* decrement usage count for buffers */
inf = inf->next;
}
if (pd)
if (pd->if_used <= 0) /* delete buffers if last file closed */
while (pd->log_head) {
inf = pd->log_head;
pd->log_head = pd->log_head->next;
kfree(inf);
}
} /* read access */
unlock_kernel();
return (retval);
} /* hysdn_log_close */
/*************************************************/
/* select/poll routine to be able using select() */
/*************************************************/
static unsigned int
hysdn_log_poll(struct file *file, poll_table * wait)
{
unsigned int mask = 0;
struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
hysdn_card *card;
struct procdata *pd = NULL;
if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
return (mask); /* no polling for write supported */
/* we need to search the card */
card = card_root;
while (card) {
pd = card->proclog;
if (pd->log == pde)
break;
card = card->next; /* search next entry */
}
if (!card)
return (mask); /* card not found */
poll_wait(file, &(pd->rd_queue), wait);
if (*((struct log_data **) file->private_data))
mask |= POLLIN | POLLRDNORM;
return mask;
} /* hysdn_log_poll */
/**************************************************/
/* table for log filesystem functions defined above. */
/**************************************************/
static const struct file_operations log_fops =
{
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = hysdn_log_read,
.write = hysdn_log_write,
.poll = hysdn_log_poll,
.open = hysdn_log_open,
.release = hysdn_log_close,
};
/***********************************************************************************/
/* hysdn_proclog_init is called when the module is loaded after creating the cards */
/* conf files. */
/***********************************************************************************/
int
hysdn_proclog_init(hysdn_card * card)
{
struct procdata *pd;
/* create a cardlog proc entry */
if ((pd = kzalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
pd->log = proc_create(pd->log_name,
S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry,
&log_fops);
init_waitqueue_head(&(pd->rd_queue));
card->proclog = (void *) pd; /* remember procfs structure */
}
return (0);
} /* hysdn_proclog_init */
/************************************************************************************/
/* hysdn_proclog_release is called when the module is unloaded and before the cards */
/* conf file is released */
/* The module counter is assumed to be 0 ! */
/************************************************************************************/
void
hysdn_proclog_release(hysdn_card * card)
{
struct procdata *pd;
if ((pd = (struct procdata *) card->proclog) != NULL) {
if (pd->log)
remove_proc_entry(pd->log_name, hysdn_proc_entry);
kfree(pd); /* release memory */
card->proclog = NULL;
}
} /* hysdn_proclog_release */

View File

@@ -0,0 +1,197 @@
/* $Id: hysdn_sched.c,v 1.5.6.4 2001/11/06 21:58:19 kai Exp $
*
* Linux driver for HYSDN cards
* scheduler routines for handling exchange card <-> pc.
*
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
* Copyright 1999 by Werner Cornelius (werner@titro.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/io.h>
#include "hysdn_defs.h"
/*****************************************************************************/
/* hysdn_sched_rx is called from the cards handler to announce new data is */
/* available from the card. The routine has to handle the data and return */
/* with a nonzero code if the data could be worked (or even thrown away), if */
/* no room to buffer the data is available a zero return tells the card */
/* to keep the data until later. */
/*****************************************************************************/
int
hysdn_sched_rx(hysdn_card *card, unsigned char *buf, unsigned short len,
unsigned short chan)
{
switch (chan) {
case CHAN_NDIS_DATA:
if (hynet_enable & (1 << card->myid)) {
/* give packet to network handler */
hysdn_rx_netpkt(card, buf, len);
}
break;
case CHAN_ERRLOG:
hysdn_card_errlog(card, (tErrLogEntry *) buf, len);
if (card->err_log_state == ERRLOG_STATE_ON)
card->err_log_state = ERRLOG_STATE_START; /* start new fetch */
break;
#ifdef CONFIG_HYSDN_CAPI
case CHAN_CAPI:
/* give packet to CAPI handler */
if (hycapi_enable & (1 << card->myid)) {
hycapi_rx_capipkt(card, buf, len);
}
break;
#endif /* CONFIG_HYSDN_CAPI */
default:
printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len);
break;
} /* switch rx channel */
return (1); /* always handled */
} /* hysdn_sched_rx */
/*****************************************************************************/
/* hysdn_sched_tx is called from the cards handler to announce that there is */
/* room in the tx-buffer to the card and data may be sent if needed. */
/* If the routine wants to send data it must fill buf, len and chan with the */
/* appropriate data and return a nonzero value. With a zero return no new */
/* data to send is assumed. maxlen specifies the buffer size available for */
/* sending. */
/*****************************************************************************/
int
hysdn_sched_tx(hysdn_card *card, unsigned char *buf,
unsigned short volatile *len, unsigned short volatile *chan,
unsigned short maxlen)
{
struct sk_buff *skb;
if (card->net_tx_busy) {
card->net_tx_busy = 0; /* reset flag */
hysdn_tx_netack(card); /* acknowledge packet send */
} /* a network packet has completely been transferred */
/* first of all async requests are handled */
if (card->async_busy) {
if (card->async_len <= maxlen) {
memcpy(buf, card->async_data, card->async_len);
*len = card->async_len;
*chan = card->async_channel;
card->async_busy = 0; /* reset request */
return (1);
}
card->async_busy = 0; /* in case of length error */
} /* async request */
if ((card->err_log_state == ERRLOG_STATE_START) &&
(maxlen >= ERRLOG_CMD_REQ_SIZE)) {
strcpy(buf, ERRLOG_CMD_REQ); /* copy the command */
*len = ERRLOG_CMD_REQ_SIZE; /* buffer length */
*chan = CHAN_ERRLOG; /* and channel */
card->err_log_state = ERRLOG_STATE_ON; /* new state is on */
return (1); /* tell that data should be send */
} /* error log start and able to send */
if ((card->err_log_state == ERRLOG_STATE_STOP) &&
(maxlen >= ERRLOG_CMD_STOP_SIZE)) {
strcpy(buf, ERRLOG_CMD_STOP); /* copy the command */
*len = ERRLOG_CMD_STOP_SIZE; /* buffer length */
*chan = CHAN_ERRLOG; /* and channel */
card->err_log_state = ERRLOG_STATE_OFF; /* new state is off */
return (1); /* tell that data should be send */
} /* error log start and able to send */
/* now handle network interface packets */
if ((hynet_enable & (1 << card->myid)) &&
(skb = hysdn_tx_netget(card)) != NULL)
{
if (skb->len <= maxlen) {
/* copy the packet to the buffer */
skb_copy_from_linear_data(skb, buf, skb->len);
*len = skb->len;
*chan = CHAN_NDIS_DATA;
card->net_tx_busy = 1; /* we are busy sending network data */
return (1); /* go and send the data */
} else
hysdn_tx_netack(card); /* aknowledge packet -> throw away */
} /* send a network packet if available */
#ifdef CONFIG_HYSDN_CAPI
if( ((hycapi_enable & (1 << card->myid))) &&
((skb = hycapi_tx_capiget(card)) != NULL) )
{
if (skb->len <= maxlen) {
skb_copy_from_linear_data(skb, buf, skb->len);
*len = skb->len;
*chan = CHAN_CAPI;
hycapi_tx_capiack(card);
return (1); /* go and send the data */
}
}
#endif /* CONFIG_HYSDN_CAPI */
return (0); /* nothing to send */
} /* hysdn_sched_tx */
/*****************************************************************************/
/* send one config line to the card and return 0 if successful, otherwise a */
/* negative error code. */
/* The function works with timeouts perhaps not giving the greatest speed */
/* sending the line, but this should be meaningless beacuse only some lines */
/* are to be sent and this happens very seldom. */
/*****************************************************************************/
int
hysdn_tx_cfgline(hysdn_card *card, unsigned char *line, unsigned short chan)
{
int cnt = 50; /* timeout intervalls */
unsigned long flags;
if (card->debug_flags & LOG_SCHED_ASYN)
hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1);
while (card->async_busy) {
if (card->debug_flags & LOG_SCHED_ASYN)
hysdn_addlog(card, "async tx-cfg delayed");
msleep_interruptible(20); /* Timeout 20ms */
if (!--cnt)
return (-ERR_ASYNC_TIME); /* timed out */
} /* wait for buffer to become free */
spin_lock_irqsave(&card->hysdn_lock, flags);
strcpy(card->async_data, line);
card->async_len = strlen(line) + 1;
card->async_channel = chan;
card->async_busy = 1; /* request transfer */
/* now queue the task */
schedule_work(&card->irq_queue);
spin_unlock_irqrestore(&card->hysdn_lock, flags);
if (card->debug_flags & LOG_SCHED_ASYN)
hysdn_addlog(card, "async tx-cfg data queued");
cnt++; /* short delay */
while (card->async_busy) {
if (card->debug_flags & LOG_SCHED_ASYN)
hysdn_addlog(card, "async tx-cfg waiting for tx-ready");
msleep_interruptible(20); /* Timeout 20ms */
if (!--cnt)
return (-ERR_ASYNC_TIME); /* timed out */
} /* wait for buffer to become free again */
if (card->debug_flags & LOG_SCHED_ASYN)
hysdn_addlog(card, "async tx-cfg data send");
return (0); /* line send correctly */
} /* hysdn_tx_cfgline */

View File

@@ -0,0 +1,134 @@
/*
* Linux driver for HYSDN cards
* common definitions for both sides of the bus:
* - conventions both spoolers must know
* - channel numbers agreed upon
*
* Author M. Steinkopf
* Copyright 1999 by M. Steinkopf
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef __INCE1PC_H__
#define __INCE1PC_H__
/* basic scalar definitions have same meanning,
* but their declaration location depends on environment
*/
/*--------------------------------------channel numbers---------------------*/
#define CHAN_SYSTEM 0x0001 /* system channel (spooler to spooler) */
#define CHAN_ERRLOG 0x0005 /* error logger */
#define CHAN_CAPI 0x0064 /* CAPI interface */
#define CHAN_NDIS_DATA 0x1001 /* NDIS data transfer */
/*--------------------------------------POF ready msg-----------------------*/
/* NOTE: after booting POF sends system ready message to PC: */
#define RDY_MAGIC 0x52535953UL /* 'SYSR' reversed */
#define RDY_MAGIC_SIZE 4 /* size in bytes */
#define MAX_N_TOK_BYTES 255
#define MIN_RDY_MSG_SIZE RDY_MAGIC_SIZE
#define MAX_RDY_MSG_SIZE (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
#define SYSR_TOK_END 0
#define SYSR_TOK_B_CHAN 1 /* nr. of B-Channels; DataLen=1; def: 2 */
#define SYSR_TOK_FAX_CHAN 2 /* nr. of FAX Channels; DataLen=1; def: 0 */
#define SYSR_TOK_MAC_ADDR 3 /* MAC-Address; DataLen=6; def: auto */
#define SYSR_TOK_ESC 255 /* undefined data size yet */
/* default values, if not corrected by token: */
#define SYSR_TOK_B_CHAN_DEF 2 /* assume 2 B-Channels */
#define SYSR_TOK_FAX_CHAN_DEF 1 /* assume 1 FAX Channel */
/* syntax of new SYSR token stream:
* channel: CHAN_SYSTEM
* msgsize: MIN_RDY_MSG_SIZE <= x <= MAX_RDY_MSG_SIZE
* RDY_MAGIC_SIZE <= x <= (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
* msg : 0 1 2 3 {4 5 6 ..}
* S Y S R MAX_N_TOK_BYTES bytes of TokenStream
*
* TokenStream := empty
* | {NonEndTokenChunk} EndToken RotlCRC
* NonEndTokenChunk:= NonEndTokenId DataLen [Data]
* NonEndTokenId := 0x01 .. 0xFE 1 BYTE
* DataLen := 0x00 .. 0xFF 1 BYTE
* Data := DataLen bytes
* EndToken := 0x00
* RotlCRC := special 1 byte CRC over all NonEndTokenChunk bytes
* s. RotlCRC algorithm
*
* RotlCRC algorithm:
* ucSum= 0 1 unsigned char
* for all NonEndTokenChunk bytes:
* ROTL(ucSum,1) rotate left by 1
* ucSum += Char; add current byte with swap around
* RotlCRC= ~ucSum; invert all bits for result
*
* note:
* - for 16-bit FIFO add padding 0 byte to achieve even token data bytes!
*/
/*--------------------------------------error logger------------------------*/
/* note: pof needs final 0 ! */
#define ERRLOG_CMD_REQ "ERRLOG ON"
#define ERRLOG_CMD_REQ_SIZE 10 /* with final 0 byte ! */
#define ERRLOG_CMD_STOP "ERRLOG OFF"
#define ERRLOG_CMD_STOP_SIZE 11 /* with final 0 byte ! */
#define ERRLOG_ENTRY_SIZE 64 /* sizeof(tErrLogEntry) */
/* remaining text size = 55 */
#define ERRLOG_TEXT_SIZE (ERRLOG_ENTRY_SIZE-2*4-1)
typedef struct ErrLogEntry_tag {
/*00 */ unsigned long ulErrType;
/*04 */ unsigned long ulErrSubtype;
/*08 */ unsigned char ucTextSize;
/*09 */ unsigned char ucText[ERRLOG_TEXT_SIZE];
/* ASCIIZ of len ucTextSize-1 */
/*40 */
} tErrLogEntry;
#if defined(__TURBOC__)
#if sizeof(tErrLogEntry) != ERRLOG_ENTRY_SIZE
#error size of tErrLogEntry != ERRLOG_ENTRY_SIZE
#endif /* */
#endif /* */
/*--------------------------------------DPRAM boot spooler------------------*/
/* this is the struture used between pc and
* hyperstone to exchange boot data
*/
#define DPRAM_SPOOLER_DATA_SIZE 0x20
typedef struct DpramBootSpooler_tag {
/*00 */ unsigned char Len;
/*01 */ volatile unsigned char RdPtr;
/*02 */ unsigned char WrPtr;
/*03 */ unsigned char Data[DPRAM_SPOOLER_DATA_SIZE];
/*23 */
} tDpramBootSpooler;
#define DPRAM_SPOOLER_MIN_SIZE 5 /* Len+RdPtr+Wrptr+2*data */
#define DPRAM_SPOOLER_DEF_SIZE 0x23 /* current default size */
/*--------------------------------------HYCARD/ERGO DPRAM SoftUart----------*/
/* at DPRAM offset 0x1C00: */
#define SIZE_RSV_SOFT_UART 0x1B0 /* 432 bytes reserved for SoftUart */
#endif /* __INCE1PC_H__ */