Initial release
This commit is contained in:
parent
c190083506
commit
bd47cdf255
|
@ -0,0 +1,24 @@
|
|||
SUB_DIRS :=
|
||||
MOD_SUB_DIRS :=
|
||||
ALL_SUB_DIRS :=
|
||||
|
||||
L_OBJS :=
|
||||
LX_OBJS :=
|
||||
M_OBJS :=
|
||||
MX_OBJS :=
|
||||
O_OBJS :=
|
||||
OX_OBJS :=
|
||||
L_TARGET :=
|
||||
O_TARGET :=
|
||||
|
||||
ifeq ($(CONFIG_PROC_FS),y)
|
||||
ifeq ($(CONFIG_HYSDN),y)
|
||||
M_OBJS += hysdn.o
|
||||
O_TARGET += hysdn.o
|
||||
O_OBJS += hysdn_procconf.o hysdn_proclog.o boardergo.o hysdn_boot.o hysdn_sched.o hysdn_net.o
|
||||
OX_OBJS += hysdn_init.o
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(TOPDIR)/Rules.make
|
||||
|
|
@ -0,0 +1,464 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, specific routines for ergo type boards.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#define __NO_VERSION__
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.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 void
|
||||
ergo_interrupt(int intno, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
hysdn_card *card = dev_id; /* parameter from irq */
|
||||
tErgDpram *dpr;
|
||||
ulong flags;
|
||||
uchar volatile b;
|
||||
|
||||
if (!card)
|
||||
return; /* error -> spurious interrupt */
|
||||
if (!card->irq_enabled)
|
||||
return; /* other device interrupting or irq switched off */
|
||||
|
||||
save_flags(flags);
|
||||
cli(); /* no further irqs allowed */
|
||||
|
||||
if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) {
|
||||
restore_flags(flags); /* restore old state */
|
||||
return; /* 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) {
|
||||
queue_task(&card->irq_queue, &tq_immediate);
|
||||
mark_bh(IMMEDIATE_BH);
|
||||
}
|
||||
restore_flags(flags);
|
||||
} /* ergo_interrupt */
|
||||
|
||||
/******************************************************************************/
|
||||
/* ergo_irq_bh is the function called by the immediate kernel task list after */
|
||||
/* being activated with queue_task and no interrupts active. 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(hysdn_card * card)
|
||||
{
|
||||
tErgDpram *dpr;
|
||||
int again;
|
||||
ulong flags;
|
||||
|
||||
if (card->state != CARD_STATE_RUN)
|
||||
return; /* invalid call */
|
||||
|
||||
dpr = card->dpram; /* point to DPRAM */
|
||||
|
||||
save_flags(flags);
|
||||
cli();
|
||||
if (card->hw_lock) {
|
||||
restore_flags(flags); /* hardware currently unavailable */
|
||||
return;
|
||||
}
|
||||
card->hw_lock = 1; /* we now lock the hardware */
|
||||
|
||||
do {
|
||||
sti(); /* reenable other ints */
|
||||
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 */
|
||||
cli(); /* no further ints */
|
||||
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 */
|
||||
|
||||
restore_flags(flags);
|
||||
} /* ergo_irq_bh */
|
||||
|
||||
|
||||
/*********************************************************/
|
||||
/* stop the card (hardware reset) and disable interrupts */
|
||||
/*********************************************************/
|
||||
static void
|
||||
ergo_stopcard(hysdn_card * card)
|
||||
{
|
||||
ulong flags;
|
||||
uchar val;
|
||||
|
||||
hysdn_net_release(card); /* first release the net device if existing */
|
||||
save_flags(flags);
|
||||
cli();
|
||||
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 */
|
||||
|
||||
restore_flags(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)
|
||||
{
|
||||
ulong flags;
|
||||
|
||||
if (card->state != CARD_STATE_RUN) {
|
||||
card->err_log_state = ERRLOG_STATE_OFF; /* must be off */
|
||||
return;
|
||||
}
|
||||
save_flags(flags);
|
||||
cli();
|
||||
|
||||
if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) ||
|
||||
((card->err_log_state == ERRLOG_STATE_ON) && on)) {
|
||||
restore_flags(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 */
|
||||
|
||||
restore_flags(flags);
|
||||
queue_task(&card->irq_queue, &tq_immediate);
|
||||
mark_bh(IMMEDIATE_BH);
|
||||
} /* 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, uchar * buf, ulong offs)
|
||||
{
|
||||
uchar *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 */
|
||||
|
||||
sti();
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout((20 * HZ) / 1000); /* 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); /* successfull */
|
||||
} /* 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, uchar * buf, int len)
|
||||
{
|
||||
tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram;
|
||||
uchar *dst;
|
||||
uchar buflen;
|
||||
int nr_write;
|
||||
uchar tmp_rdptr;
|
||||
uchar 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 */
|
||||
sti();
|
||||
|
||||
/* 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 successfull 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. */
|
||||
ulong 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) ||
|
||||
((*(ulong *) dpr->ToPcBuf) != RDY_MAGIC))
|
||||
break; /* an error occured */
|
||||
|
||||
/* 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");
|
||||
|
||||
save_flags(flags);
|
||||
cli();
|
||||
|
||||
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 */
|
||||
|
||||
restore_flags(flags);
|
||||
if ((i = hysdn_net_create(card))) {
|
||||
ergo_stopcard(card);
|
||||
card->state = CARD_STATE_BOOTERR;
|
||||
return (i);
|
||||
}
|
||||
return (0); /* success */
|
||||
} /* data has arrived */
|
||||
sti();
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout((50 * HZ) / 1000); /* 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);
|
||||
vfree(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 (check_region(card->iobase + PCI9050_INTR_REG, 1) ||
|
||||
check_region(card->iobase + PCI9050_USER_IO, 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)))
|
||||
return (-1);
|
||||
|
||||
request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN");
|
||||
request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN");
|
||||
ergo_stopcard(card); /* disable interrupts */
|
||||
if (request_irq(card->irq, ergo_interrupt, SA_SHIRQ, "HYSDN", card)) {
|
||||
ergo_releasehardware(card); /* return the aquired 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;
|
||||
card->irq_queue.next = 0;
|
||||
card->irq_queue.sync = 0;
|
||||
card->irq_queue.data = card; /* init task queue for interrupt */
|
||||
card->irq_queue.routine = (void *) (void *) ergo_irq_bh;
|
||||
|
||||
return (0);
|
||||
} /* ergo_inithardware */
|
|
@ -0,0 +1,114 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, definitions for ergo type boards (buffers..).
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/isdn_compat.h>
|
||||
|
||||
/************************************************/
|
||||
/* 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 */ uchar ToHyBuf[ERG_TO_HY_BUF_SIZE];
|
||||
/*0E00 */ uchar ToPcBuf[ERG_TO_PC_BUF_SIZE];
|
||||
|
||||
/*1C00 */ uchar bSoftUart[SIZE_RSV_SOFT_UART];
|
||||
/* size 0x1B0 */
|
||||
|
||||
/*1DB0 *//* tErrLogEntry */ uchar volatile ErrLogMsg[64];
|
||||
/* size 64 bytes */
|
||||
/*1DB0 ulong ulErrType; */
|
||||
/*1DB4 ulong ulErrSubtype; */
|
||||
/*1DB8 ulong ucTextSize; */
|
||||
/*1DB9 ulong ucText[ERRLOG_TEXT_SIZE]; *//* ASCIIZ of len ucTextSize-1 */
|
||||
/*1DF0 */
|
||||
|
||||
/*1DF0 */ word volatile ToHyChannel;
|
||||
/*1DF2 */ word volatile ToHySize;
|
||||
/*1DF4 */ uchar volatile ToHyFlag;
|
||||
/* !=0: msg for Hy waiting */
|
||||
/*1DF5 */ uchar volatile ToPcFlag;
|
||||
/* !=0: msg for PC waiting */
|
||||
/*1DF6 */ word volatile ToPcChannel;
|
||||
/*1DF8 */ word volatile ToPcSize;
|
||||
/*1DFA */ uchar bRes1DBA[0x1E00 - 0x1DFA];
|
||||
/* 6 bytes */
|
||||
|
||||
/*1E00 */ uchar bRestOfEntryTbl[0x1F00 - 0x1E00];
|
||||
/*1F00 */ ulong TrapTable[62];
|
||||
/*1FF8 */ uchar bRes1FF8[0x1FFB - 0x1FF8];
|
||||
/* low part of reset vetor */
|
||||
/*1FFB */ uchar 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 */ uchar 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 */ uchar bRes1FFD;
|
||||
/*1FFE */ uchar ToPcInt;
|
||||
/* E1_intclear; on CHAMP2: E1_intset */
|
||||
/*1FFF */ uchar 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 */
|
|
@ -0,0 +1,417 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, specific routines for booting and pof handling.
|
||||
*
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#define __NO_VERSION__
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/malloc.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 {
|
||||
word Cryptor; /* for use with Decrypt function */
|
||||
word Nrecs; /* records remaining in file */
|
||||
uchar pof_state; /* actual state of read handler */
|
||||
uchar is_crypted; /* card data is crypted */
|
||||
int BufSize; /* actual number of bytes bufferd */
|
||||
int last_error; /* last occured error */
|
||||
word pof_recid; /* actual pof recid */
|
||||
ulong pof_reclen; /* total length of pof record data */
|
||||
ulong pof_recoffset; /* actual offset inside pof record */
|
||||
union {
|
||||
uchar 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 sucessive POF file chuncks. */
|
||||
/* */
|
||||
/* to be called at start of POF file reading, */
|
||||
/* before starting any decryption on any POF record. */
|
||||
/*****************************************************/
|
||||
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. */
|
||||
/***************************************************************/
|
||||
void
|
||||
DecryptBuf(struct boot_data *boot, int cnt)
|
||||
{
|
||||
uchar *bufp = boot->buf.BootBuf;
|
||||
|
||||
while (cnt--) {
|
||||
boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
|
||||
*bufp++ ^= (uchar) boot->Cryptor;
|
||||
}
|
||||
} /* DecryptBuf */
|
||||
|
||||
/********************************************************************************/
|
||||
/* pof_handle_data executes the required actions dependant on the active record */
|
||||
/* id. If successfull 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;
|
||||
uchar *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 */
|
||||
/* occured 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 = (word) (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 occured */
|
||||
|
||||
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, uchar ** 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 = kmalloc(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;
|
||||
memset(boot, 0, sizeof(struct boot_data));
|
||||
|
||||
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 occured. */
|
||||
/*********************************************************************************/
|
||||
int
|
||||
EvalSysrTokData(hysdn_card * card, uchar * 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--; /* dont 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 */
|
|
@ -0,0 +1,226 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, global definitions and exported vars and functions.
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/hysdn_if.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/tqueue.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/isdn_compat.h>
|
||||
|
||||
/****************************/
|
||||
/* storage type definitions */
|
||||
/****************************/
|
||||
#define uchar unsigned char
|
||||
#define uint unsigned int
|
||||
#define ulong unsigned long
|
||||
#define word unsigned short
|
||||
|
||||
#include "ince1pc.h"
|
||||
|
||||
/************************************************/
|
||||
/* 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 constant defines */
|
||||
/************************/
|
||||
#define PCI_VENDOR_ID_HYPERCOPE 0x1365
|
||||
#define PCI_DEVICE_ID_PLX 0x9050 /* all DPRAM cards use the same id */
|
||||
|
||||
/*****************************/
|
||||
/* sub ids determining cards */
|
||||
/*****************************/
|
||||
#define PCI_SUB_ID_OLD_ERGO 0x0104
|
||||
#define PCI_SUB_ID_ERGO 0x0106
|
||||
#define PCI_SUB_ID_METRO 0x0107
|
||||
#define PCI_SUB_ID_CHAMP2 0x0108
|
||||
#define PCI_SUB_ID_PLEXUS 0x0109
|
||||
|
||||
/***********************************/
|
||||
/* 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 */
|
||||
uchar bus; /* pci bus the card is connected to */
|
||||
uchar devfn; /* slot+function bit encoded */
|
||||
word subsysid; /* PCI subsystem id */
|
||||
uchar brdtype; /* type of card */
|
||||
uint bchans; /* number of available B-channels */
|
||||
uint faxchans; /* number of available fax-channels */
|
||||
uchar mac_addr[6]; /* MAC Address read from card */
|
||||
uint irq; /* interrupt number */
|
||||
uint iobase; /* IO-port base address */
|
||||
ulong plxbase; /* PLX memory base */
|
||||
ulong membase; /* DPRAM memory base */
|
||||
ulong 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 */
|
||||
uchar err_log_state; /* actual error log state of the card */
|
||||
ulong debug_flags; /* tells what should be debugged and where */
|
||||
void (*set_errlog_state) (struct HYSDN_CARD *, int);
|
||||
|
||||
/* interrupt handler + interrupt synchronisation */
|
||||
struct tq_struct irq_queue; /* interrupt task queue */
|
||||
uchar volatile irq_enabled; /* interrupt enabled if != 0 */
|
||||
uchar volatile hw_lock; /* hardware is currently locked -> no access */
|
||||
|
||||
/* boot process */
|
||||
void *boot; /* pointer to boot private data */
|
||||
int (*writebootimg) (struct HYSDN_CARD *, uchar *, ulong);
|
||||
int (*writebootseq) (struct HYSDN_CARD *, uchar *, int);
|
||||
int (*waitpofready) (struct HYSDN_CARD *);
|
||||
int (*testram) (struct HYSDN_CARD *);
|
||||
|
||||
/* scheduler for data transfer (only async parts) */
|
||||
uchar async_data[256]; /* async data to be sent (normally for config) */
|
||||
word volatile async_len; /* length of data to sent */
|
||||
word 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 *);
|
||||
} hysdn_card;
|
||||
|
||||
|
||||
/*****************/
|
||||
/* exported vars */
|
||||
/*****************/
|
||||
extern int cardmax; /* number of found cards */
|
||||
extern hysdn_card *card_root; /* pointer to first card */
|
||||
|
||||
|
||||
|
||||
/*************************/
|
||||
/* im/exported functions */
|
||||
/*************************/
|
||||
extern int printk(const char *fmt,...);
|
||||
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 put_log_buffer(hysdn_card *, char *); /* output log data */
|
||||
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 *, uchar **); /* open proc file for writing pof */
|
||||
extern int pof_write_buffer(hysdn_card *, int); /* write boot data to card */
|
||||
extern int EvalSysrTokData(hysdn_card *, uchar *, int); /* Check Sysready Token Data */
|
||||
|
||||
/* hysdn_sched.c */
|
||||
extern int hysdn_sched_tx(hysdn_card *, uchar *, word volatile *, word volatile *,
|
||||
word);
|
||||
extern int hysdn_sched_rx(hysdn_card *, uchar *, word, word);
|
||||
extern int hysdn_tx_cfgline(hysdn_card *, uchar *, word); /* send one cfg line */
|
||||
|
||||
/* hysdn_net.c */
|
||||
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 *, uchar *, word); /* rxed packet from network */
|
|
@ -0,0 +1,239 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, init functions.
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/malloc.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
|
||||
static char *hysdn_init_revision = "$Revision$";
|
||||
int cardmax; /* number of found cards */
|
||||
hysdn_card *card_root = NULL; /* pointer to first card */
|
||||
|
||||
/**********************************************/
|
||||
/* table assigning PCI-sub ids to board types */
|
||||
/* the last entry contains all 0 */
|
||||
/**********************************************/
|
||||
static struct {
|
||||
word subid; /* PCI sub id */
|
||||
uchar cardtyp; /* card type assigned */
|
||||
} pci_subid_map[] = {
|
||||
|
||||
{
|
||||
PCI_SUB_ID_METRO, BD_METRO
|
||||
},
|
||||
{
|
||||
PCI_SUB_ID_CHAMP2, BD_CHAMP2
|
||||
},
|
||||
{
|
||||
PCI_SUB_ID_ERGO, BD_ERGO
|
||||
},
|
||||
{
|
||||
PCI_SUB_ID_OLD_ERGO, BD_ERGO
|
||||
},
|
||||
{
|
||||
0, 0
|
||||
} /* terminating entry */
|
||||
};
|
||||
|
||||
/*********************************************************************/
|
||||
/* search_cards searches for available cards in the pci config data. */
|
||||
/* If a card is found, the card structure is allocated and the cards */
|
||||
/* ressources are reserved. cardmax is incremented. */
|
||||
/*********************************************************************/
|
||||
static void
|
||||
search_cards(void)
|
||||
{
|
||||
struct pci_dev *akt_pcidev = NULL;
|
||||
hysdn_card *card, *card_last;
|
||||
uchar irq;
|
||||
int i;
|
||||
|
||||
card_root = NULL;
|
||||
card_last = NULL;
|
||||
while ((akt_pcidev = pci_find_device(PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_PLX,
|
||||
akt_pcidev)) != NULL) {
|
||||
|
||||
if (!(card = kmalloc(sizeof(hysdn_card), GFP_KERNEL))) {
|
||||
printk(KERN_ERR "HYSDN: unable to alloc device mem \n");
|
||||
return;
|
||||
}
|
||||
memset(card, 0, sizeof(hysdn_card));
|
||||
card->myid = cardmax; /* set own id */
|
||||
card->bus = akt_pcidev->bus->number;
|
||||
card->devfn = akt_pcidev->devfn; /* slot + function */
|
||||
pcibios_read_config_word(card->bus, card->devfn, PCI_SUBSYSTEM_ID, &card->subsysid);
|
||||
pcibios_read_config_byte(card->bus, card->devfn, PCI_INTERRUPT_LINE, &irq);
|
||||
card->irq = irq;
|
||||
card->iobase = get_pcibase(akt_pcidev, PCI_REG_PLX_IO_BASE) & PCI_BASE_ADDRESS_IO_MASK;
|
||||
card->plxbase = get_pcibase(akt_pcidev, PCI_REG_PLX_MEM_BASE);
|
||||
card->membase = get_pcibase(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 */
|
||||
for (i = 0; pci_subid_map[i].subid; i++)
|
||||
if (pci_subid_map[i].subid == card->subsysid) {
|
||||
card->brdtype = pci_subid_map[i].cardtyp;
|
||||
break;
|
||||
}
|
||||
if (card->brdtype != BD_NONE) {
|
||||
if (ergo_inithardware(card)) {
|
||||
printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase);
|
||||
kfree(card);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
printk(KERN_WARNING "HYSDN: unknown card id 0x%04x\n", card->subsysid);
|
||||
kfree(card); /* release mem */
|
||||
continue;
|
||||
}
|
||||
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 */
|
||||
} /* device found */
|
||||
} /* search_cards */
|
||||
|
||||
/************************************************************************************/
|
||||
/* free_resources frees the acquired PCI resources and returns the allocated memory */
|
||||
/************************************************************************************/
|
||||
static void
|
||||
free_resources(void)
|
||||
{
|
||||
hysdn_card *card;
|
||||
|
||||
while (card_root) {
|
||||
card = card_root;
|
||||
if (card->releasehardware)
|
||||
card->releasehardware(card); /* free all hardware resources */
|
||||
card_root = card_root->next; /* remove card from chain */
|
||||
kfree(card); /* return mem */
|
||||
|
||||
} /* while card_root */
|
||||
} /* free_resources */
|
||||
|
||||
/**************************************************************************/
|
||||
/* stop_cards disables (hardware resets) all cards and disables interrupt */
|
||||
/**************************************************************************/
|
||||
static void
|
||||
stop_cards(void)
|
||||
{
|
||||
hysdn_card *card;
|
||||
|
||||
card = card_root; /* first in chain */
|
||||
while (card) {
|
||||
if (card->stopcard)
|
||||
card->stopcard(card);
|
||||
card = card->next; /* remove card from chain */
|
||||
} /* while card */
|
||||
} /* stop_cards */
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* 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. */
|
||||
/****************************************************************************/
|
||||
#ifdef CONFIG_MODULES
|
||||
|
||||
/******************************************************/
|
||||
/* 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 successfull */
|
||||
/* 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. */
|
||||
/****************************************************************************/
|
||||
int
|
||||
init_module(void)
|
||||
{
|
||||
char tmp[50];
|
||||
|
||||
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));
|
||||
if (!pci_present()) {
|
||||
printk(KERN_ERR "HYSDN: no PCI bus present, module not loaded\n");
|
||||
return (-1);
|
||||
}
|
||||
search_cards();
|
||||
printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax);
|
||||
|
||||
if (hysdn_procconf_init()) {
|
||||
free_resources(); /* proc file_sys not created */
|
||||
return (-1);
|
||||
}
|
||||
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 successfull 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. */
|
||||
/***********************************************************************/
|
||||
void
|
||||
cleanup_module(void)
|
||||
{
|
||||
|
||||
stop_cards();
|
||||
hysdn_procconf_release();
|
||||
free_resources();
|
||||
printk(KERN_NOTICE "HYSDN: module unloaded\n");
|
||||
} /* cleanup_module */
|
||||
|
||||
#endif /* CONFIG_MODULES */
|
|
@ -0,0 +1,324 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, net (ethernet type) handling routines.
|
||||
*
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This net module has been inspired by the skeleton driver from
|
||||
* Donald Becker (becker@CESDIS.gsfc.nasa.gov)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#define __NO_VERSION__
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.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"
|
||||
|
||||
/* store the actual version for log reporting */
|
||||
char *hysdn_net_revision = "$Revision$";
|
||||
|
||||
/****************************************************************************/
|
||||
/* 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 {
|
||||
struct device netdev; /* the network device */
|
||||
struct net_device_stats stats;
|
||||
/* additional vars may be added here */
|
||||
char dev_name[9]; /* our own device name */
|
||||
struct sk_buff *tx_skb; /* buffer for tx operation */
|
||||
}; /* net_local */
|
||||
|
||||
|
||||
/*****************************************************/
|
||||
/* Get the current statistics for this card. */
|
||||
/* This may be called with the card open or closed ! */
|
||||
/*****************************************************/
|
||||
static struct net_device_stats *
|
||||
net_get_stats(struct device *dev)
|
||||
{
|
||||
return (&((struct net_local *) dev)->stats);
|
||||
} /* net_device_stats */
|
||||
|
||||
/*********************************************************************/
|
||||
/* 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 device *dev)
|
||||
{
|
||||
struct in_device *in_dev;
|
||||
hysdn_card *card = dev->priv;
|
||||
int i;
|
||||
|
||||
dev->tbusy = 0; /* non busy state */
|
||||
dev->interrupt = 0;
|
||||
if (!dev->start)
|
||||
MOD_INC_USE_COUNT; /* increment only if device is down */
|
||||
dev->start = 1; /* and started */
|
||||
|
||||
/* Fill in the MAC-level header (if not already set) */
|
||||
if (!card->mac_addr[0]) {
|
||||
for (i = 0; i < ETH_ALEN - sizeof(ulong); 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(ulong)), &ifa->ifa_local, sizeof(ulong));
|
||||
}
|
||||
} else
|
||||
memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN);
|
||||
|
||||
return (0);
|
||||
} /* net_open */
|
||||
|
||||
/*********************************************************************/
|
||||
/* close/decativate the device. The device is not removed, but only */
|
||||
/* deactivated. */
|
||||
/*********************************************************************/
|
||||
static int
|
||||
net_close(struct device *dev)
|
||||
{
|
||||
|
||||
dev->tbusy = 1; /* we are busy */
|
||||
|
||||
if (dev->start)
|
||||
MOD_DEC_USE_COUNT; /* dec only if device has been active */
|
||||
|
||||
dev->start = 0; /* and not started */
|
||||
|
||||
return (0); /* success */
|
||||
} /* net_close */
|
||||
|
||||
/************************************/
|
||||
/* send a packet on this interface. */
|
||||
/************************************/
|
||||
static int
|
||||
net_send_packet(struct sk_buff *skb, struct device *dev)
|
||||
{
|
||||
struct net_local *lp = (struct net_local *) dev;
|
||||
|
||||
if (dev->tbusy) {
|
||||
/*
|
||||
* If we get here, some higher level has decided we are broken.
|
||||
* There should really be a "kick me" function call instead.
|
||||
* As ISDN may have higher timeouts than real ethernet 10s timeout
|
||||
*/
|
||||
int tickssofar = jiffies - dev->trans_start;
|
||||
if (tickssofar < (10000 * HZ) / 1000)
|
||||
return 1;
|
||||
printk(KERN_WARNING "%s: transmit timed out. \n", dev->name);
|
||||
dev->tbusy = 0;
|
||||
dev->trans_start = jiffies;
|
||||
}
|
||||
/*
|
||||
* Block a timer-based transmit from overlapping. This could better be
|
||||
* done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
|
||||
*/
|
||||
if (test_and_set_bit(0, (void *) &dev->tbusy) != 0)
|
||||
printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
|
||||
|
||||
else {
|
||||
lp->stats.tx_bytes += skb->len;
|
||||
dev->trans_start = jiffies;
|
||||
lp->tx_skb = skb; /* remember skb pointer */
|
||||
queue_task(&((hysdn_card *) dev->priv)->irq_queue, &tq_immediate);
|
||||
mark_bh(IMMEDIATE_BH);
|
||||
}
|
||||
|
||||
return (0); /* 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->tx_skb)
|
||||
dev_kfree_skb(lp->tx_skb); /* free tx pointer */
|
||||
lp->tx_skb = NULL; /* reset pointer */
|
||||
|
||||
lp->stats.tx_packets++;
|
||||
lp->netdev.tbusy = 0;
|
||||
mark_bh(NET_BH); /* Inform upper layers. */
|
||||
|
||||
} /* hysdn_tx_netack */
|
||||
|
||||
/*****************************************************/
|
||||
/* we got a packet from the network, go and queue it */
|
||||
/*****************************************************/
|
||||
void
|
||||
hysdn_rx_netpkt(hysdn_card * card, uchar * buf, word len)
|
||||
{
|
||||
struct net_local *lp = card->netif;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!lp)
|
||||
return; /* non existing device */
|
||||
|
||||
lp->stats.rx_bytes += len;
|
||||
|
||||
skb = dev_alloc_skb(len);
|
||||
if (skb == NULL) {
|
||||
printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
|
||||
lp->netdev.name);
|
||||
lp->stats.rx_dropped++;
|
||||
return;
|
||||
}
|
||||
skb->dev = &lp->netdev;
|
||||
|
||||
/* copy the data */
|
||||
memcpy(skb_put(skb, len), buf, len);
|
||||
|
||||
/* determine the used protocol */
|
||||
skb->protocol = eth_type_trans(skb, &lp->netdev);
|
||||
|
||||
netif_rx(skb);
|
||||
lp->stats.rx_packets++; /* adjust packet count */
|
||||
|
||||
} /* 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 */
|
||||
|
||||
return (lp->tx_skb); /* return packet pointer */
|
||||
} /* hysdn_tx_netget */
|
||||
|
||||
|
||||
/*******************************************/
|
||||
/* init function called by register device */
|
||||
/*******************************************/
|
||||
static int
|
||||
net_init(struct device *dev)
|
||||
{
|
||||
/* setup the function table */
|
||||
dev->open = net_open;
|
||||
dev->stop = net_close;
|
||||
dev->hard_start_xmit = net_send_packet;
|
||||
dev->get_stats = net_get_stats;
|
||||
|
||||
/* Fill in the fields of the device structure with ethernet values. */
|
||||
ether_setup(dev);
|
||||
|
||||
return (0); /* success */
|
||||
} /* net_init */
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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 device *dev;
|
||||
int i;
|
||||
|
||||
hysdn_net_release(card); /* release an existing net device */
|
||||
if ((dev = kmalloc(sizeof(struct net_local), GFP_KERNEL)) == NULL) {
|
||||
printk(KERN_WARNING "HYSDN: unable to allocate mem\n");
|
||||
if (card->debug_flags & LOG_NET_INIT)
|
||||
return (-ENOMEM);
|
||||
}
|
||||
memset(dev, 0, sizeof(struct net_local)); /* clean the structure */
|
||||
|
||||
/* initialise necessary or informing fields */
|
||||
dev->base_addr = card->iobase; /* IO address */
|
||||
dev->irq = card->irq; /* irq */
|
||||
dev->init = net_init; /* the init function of the device */
|
||||
dev->name = ((struct net_local *) dev)->dev_name; /* device name */
|
||||
if ((i = register_netdev(dev))) {
|
||||
printk(KERN_WARNING "HYSDN: unable to create network device\n");
|
||||
kfree(dev);
|
||||
return (i);
|
||||
}
|
||||
dev->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 device *dev = card->netif;
|
||||
|
||||
if (!dev)
|
||||
return (0); /* non existing */
|
||||
|
||||
card->netif = NULL; /* clear out pointer */
|
||||
dev->stop(dev); /* close the device */
|
||||
unregister_netdev(dev); /* release the device */
|
||||
kfree(dev); /* release the memory allocated */
|
||||
if (card->debug_flags & LOG_NET_INIT)
|
||||
hysdn_addlog(card, "network device deleted");
|
||||
|
||||
return (0); /* always successfull */
|
||||
} /* 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 device *dev = card->netif;
|
||||
|
||||
if (!dev)
|
||||
return ("-"); /* non existing */
|
||||
|
||||
return (dev->name);
|
||||
} /* hysdn_net_getname */
|
|
@ -0,0 +1,91 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, definitions used for handling pof-files.
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
/************************/
|
||||
/* 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 */ ulong Magic __attribute__((packed));
|
||||
/*04 */ ulong N_PofRecs __attribute__((packed));
|
||||
/*08 */
|
||||
} tPofFileHdr;
|
||||
|
||||
typedef struct PofRecHdr_tag { /* Pof record header */
|
||||
/*00 */ word PofRecId __attribute__((packed));
|
||||
/*02 */ ulong PofRecDataLen __attribute__((packed));
|
||||
/*06 */
|
||||
} tPofRecHdr;
|
||||
|
||||
typedef struct PofTimeStamp_tag {
|
||||
/*00 */ ulong UnixTime __attribute__((packed));
|
||||
/*04 */ uchar DateTimeText[0x28] __attribute__((packed));
|
||||
/* =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 */
|
|
@ -0,0 +1,494 @@
|
|||
/* $Id$
|
||||
|
||||
* 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 program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#define __NO_VERSION__
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
|
||||
static char *hysdn_procconf_revision = "$Revision$";
|
||||
|
||||
#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 80 /* 80 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 */
|
||||
uchar conf_line[CONF_LINE_LEN]; /* buffered conf line */
|
||||
word channel; /* active channel number */
|
||||
uchar *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 occured. */
|
||||
/***********************************************************************/
|
||||
static int
|
||||
process_line(struct conf_writedata *cnf)
|
||||
{
|
||||
uchar *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 */
|
||||
|
||||
/*************************/
|
||||
/* dummy file operations */
|
||||
/*************************/
|
||||
static loff_t
|
||||
hysdn_dummy_lseek(struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
return -ESPIPE;
|
||||
} /* hysdn_dummy_lseek */
|
||||
|
||||
/***********************************/
|
||||
/* 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 *buf, size_t count, loff_t * off)
|
||||
{
|
||||
struct conf_writedata *cnf;
|
||||
int i;
|
||||
uchar ch, *cp;
|
||||
|
||||
if (&file->f_pos != off) /* fs error check */
|
||||
return (-ESPIPE);
|
||||
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 occured -> 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 occured */
|
||||
}
|
||||
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 *buf, size_t count, loff_t * off)
|
||||
{
|
||||
char *cp;
|
||||
int i;
|
||||
|
||||
if (off != &file->f_pos) /* fs error check */
|
||||
return -ESPIPE;
|
||||
|
||||
if (file->f_mode & FMODE_READ) {
|
||||
if (!(cp = file->private_data))
|
||||
return (-EFAULT); /* should never happen */
|
||||
i = strlen(cp); /* get total string length */
|
||||
if (*off < i) {
|
||||
/* still bytes to transfer */
|
||||
cp += *off; /* point to desired data offset */
|
||||
i -= *off; /* remaining length */
|
||||
if (i > count)
|
||||
i = count; /* limit length to transfer */
|
||||
if (copy_to_user(buf, cp, i))
|
||||
return (-EFAULT); /* copy error */
|
||||
*off += i; /* adjust offset */
|
||||
} else
|
||||
return (0);
|
||||
} else
|
||||
return (-EPERM); /* no permission to read */
|
||||
|
||||
return (i);
|
||||
} /* 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;
|
||||
|
||||
MOD_INC_USE_COUNT; /* lock module */
|
||||
|
||||
/* now search the addressed card */
|
||||
card = card_root;
|
||||
while (card) {
|
||||
pd = card->procconf;
|
||||
if (pd->low_ino == (ino->i_ino & 0xFFFF))
|
||||
break;
|
||||
card = card->next; /* search next entry */
|
||||
}
|
||||
if (!card) {
|
||||
MOD_DEC_USE_COUNT; /* unlock module */
|
||||
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_uid, filep->f_gid, 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))) {
|
||||
MOD_DEC_USE_COUNT;
|
||||
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 = (char *) kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
|
||||
MOD_DEC_USE_COUNT;
|
||||
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%08x %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 ! */
|
||||
MOD_DEC_USE_COUNT; /* unlock module */
|
||||
return (-EPERM); /* no permission this time */
|
||||
}
|
||||
return (0);
|
||||
} /* 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;
|
||||
|
||||
/* search the addressed card */
|
||||
card = card_root;
|
||||
while (card) {
|
||||
pd = card->procconf;
|
||||
if (pd->low_ino == (ino->i_ino & 0xFFFF))
|
||||
break;
|
||||
card = card->next; /* search next entry */
|
||||
}
|
||||
if (!card) {
|
||||
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_uid, filep->f_gid, 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 */
|
||||
|
||||
if (filep->private_data)
|
||||
kfree(filep->private_data); /* release memory */
|
||||
}
|
||||
MOD_DEC_USE_COUNT; /* reduce usage count */
|
||||
return (retval);
|
||||
} /* hysdn_conf_close */
|
||||
|
||||
/******************************************************/
|
||||
/* table for conf filesystem functions defined above. */
|
||||
/******************************************************/
|
||||
static struct file_operations conf_fops =
|
||||
{
|
||||
hysdn_dummy_lseek,
|
||||
hysdn_conf_read,
|
||||
hysdn_conf_write,
|
||||
NULL, /* readdir */
|
||||
NULL, /* poll */
|
||||
NULL, /* ioctl */
|
||||
NULL, /* mmap */
|
||||
hysdn_conf_open,
|
||||
NULL, /* flush */
|
||||
hysdn_conf_close,
|
||||
NULL /* fsync */
|
||||
};
|
||||
|
||||
static struct inode_operations conf_inode_operations =
|
||||
{
|
||||
&conf_fops, /* log proc file-ops */
|
||||
NULL, /* create */
|
||||
NULL, /* lookup */
|
||||
NULL, /* link */
|
||||
NULL, /* unlink */
|
||||
NULL, /* symlink */
|
||||
NULL, /* mkdir */
|
||||
NULL, /* rmdir */
|
||||
NULL, /* mknod */
|
||||
NULL, /* rename */
|
||||
NULL, /* readlink */
|
||||
NULL, /* follow_link */
|
||||
NULL, /* readpage */
|
||||
NULL, /* writepage */
|
||||
NULL, /* bmap */
|
||||
NULL, /* truncate */
|
||||
NULL /* permission */
|
||||
};
|
||||
|
||||
/*****************************/
|
||||
/* 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;
|
||||
uchar conf_name[20];
|
||||
|
||||
hysdn_proc_entry = create_proc_entry(PROC_SUBDIR_NAME, S_IFDIR | S_IRUGO | S_IXUGO, 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 *) create_proc_entry(conf_name,
|
||||
S_IFREG | S_IRUGO | S_IWUSR,
|
||||
hysdn_proc_entry)) != NULL) {
|
||||
((struct proc_dir_entry *) card->procconf)->ops = &conf_inode_operations;
|
||||
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;
|
||||
uchar 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, proc_net);
|
||||
} /* hysdn_procfs_release */
|
|
@ -0,0 +1,505 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, /proc/net filesystem log functions.
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#define __NO_VERSION__
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
|
||||
static char *hysdn_procfs_revision = "$Revision$";
|
||||
|
||||
#define INFO_OUT_LEN 80 /* length of info line including lf */
|
||||
|
||||
/*************************************************/
|
||||
/* structure keeping ascii log for device output */
|
||||
/*************************************************/
|
||||
struct log_data {
|
||||
struct log_data *next;
|
||||
ulong 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 */
|
||||
#ifdef COMPAT_HAS_NEW_WAITQ
|
||||
wait_queue_head_t rd_queue;
|
||||
#else
|
||||
struct wait_queue *rd_queue; /* wait queue structure */
|
||||
#endif
|
||||
};
|
||||
|
||||
/********************************************/
|
||||
/* 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. */
|
||||
/********************************************/
|
||||
void
|
||||
put_log_buffer(hysdn_card * card, char *cp)
|
||||
{
|
||||
struct log_data *ib;
|
||||
struct procdata *pd = card->procfs;
|
||||
int flags;
|
||||
|
||||
if (!pd)
|
||||
return;
|
||||
if (!cp)
|
||||
return;
|
||||
if (!*cp)
|
||||
return;
|
||||
if (pd->if_used <= 0)
|
||||
return; /* no open file for read */
|
||||
|
||||
if (!(ib = (struct log_data *) 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 */
|
||||
save_flags(flags);
|
||||
cli();
|
||||
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 */
|
||||
restore_flags(flags);
|
||||
|
||||
/* delete old entrys */
|
||||
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 */
|
||||
wake_up_interruptible(&(pd->rd_queue)); /* announce new entry */
|
||||
} /* put_log_buffer */
|
||||
|
||||
|
||||
/*************************/
|
||||
/* dummy file operations */
|
||||
/*************************/
|
||||
static loff_t
|
||||
hysdn_dummy_lseek(struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
return -ESPIPE;
|
||||
} /* hysdn_dummy_lseek */
|
||||
|
||||
/**********************************/
|
||||
/* log file operations and tables */
|
||||
/**********************************/
|
||||
|
||||
/****************************************/
|
||||
/* write log file -> set log level bits */
|
||||
/****************************************/
|
||||
static ssize_t
|
||||
hysdn_log_write(struct file *file, const char *buf, size_t count, loff_t * off)
|
||||
{
|
||||
int retval;
|
||||
hysdn_card *card = (hysdn_card *) file->private_data;
|
||||
|
||||
if (&file->f_pos != off) /* fs error check */
|
||||
return (-ESPIPE);
|
||||
|
||||
if ((retval = pof_boot_write(card, buf, count)) < 0)
|
||||
retval = -EFAULT; /* an error occured */
|
||||
|
||||
return (retval);
|
||||
} /* hysdn_log_write */
|
||||
|
||||
/******************/
|
||||
/* read log file */
|
||||
/******************/
|
||||
static ssize_t
|
||||
hysdn_log_read(struct file *file, char *buf, size_t count, loff_t * off)
|
||||
{
|
||||
struct log_data *inf;
|
||||
int len;
|
||||
word ino;
|
||||
struct procdata *pd;
|
||||
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 */
|
||||
ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */
|
||||
card = card_root;
|
||||
while (card) {
|
||||
pd = card->procfs;
|
||||
if (pd->log->low_ino == ino)
|
||||
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 */
|
||||
(struct log_data **) 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;
|
||||
file->f_pos += 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;
|
||||
ulong flags;
|
||||
|
||||
MOD_INC_USE_COUNT; /* lock module */
|
||||
card = card_root;
|
||||
while (card) {
|
||||
pd = card->procfs;
|
||||
if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
|
||||
break;
|
||||
card = card->next; /* search next entry */
|
||||
}
|
||||
if (!card) {
|
||||
MOD_DEC_USE_COUNT; /* unlock module */
|
||||
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 -> boot pof data */
|
||||
if (pof_boot_open(card)) {
|
||||
MOD_DEC_USE_COUNT; /* unlock module */
|
||||
return (-EPERM); /* no permission this time */
|
||||
}
|
||||
} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
|
||||
|
||||
/* read access -> log/debug read */
|
||||
save_flags(flags);
|
||||
cli();
|
||||
pd->if_used++;
|
||||
if (pd->log_head)
|
||||
(struct log_data **) filep->private_data = &(pd->log_tail->next);
|
||||
else
|
||||
(struct log_data **) filep->private_data = &(pd->log_head);
|
||||
restore_flags(flags);
|
||||
|
||||
} else { /* simultaneous read/write access forbidden ! */
|
||||
MOD_DEC_USE_COUNT; /* unlock module */
|
||||
return (-EPERM); /* no permission this time */
|
||||
}
|
||||
return (0);
|
||||
} /* 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 flags, retval = 0;
|
||||
|
||||
|
||||
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
|
||||
/* write only access -> write debug completely written */
|
||||
retval = 0; /* success */
|
||||
} else {
|
||||
/* read access -> log/debug read, mark one further file as closed */
|
||||
|
||||
pd = NULL;
|
||||
save_flags(flags);
|
||||
cli();
|
||||
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->procfs;
|
||||
if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
|
||||
break;
|
||||
card = card->next; /* search next entry */
|
||||
}
|
||||
if (card)
|
||||
pd = card->procfs; /* pointer to procfs ctrl */
|
||||
}
|
||||
if (pd)
|
||||
pd->if_used--; /* decrement interface usage count by one */
|
||||
|
||||
while (inf) {
|
||||
inf->usage_cnt--; /* decrement usage count for buffers */
|
||||
inf = inf->next;
|
||||
}
|
||||
restore_flags(flags);
|
||||
|
||||
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 */
|
||||
|
||||
MOD_DEC_USE_COUNT;
|
||||
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;
|
||||
word ino;
|
||||
hysdn_card *card;
|
||||
struct procdata *pd;
|
||||
|
||||
if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
|
||||
return (mask); /* no polling for write supported */
|
||||
|
||||
/* we need to search the card */
|
||||
ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */
|
||||
card = card_root;
|
||||
while (card) {
|
||||
pd = card->procfs;
|
||||
if (pd->log->low_ino == ino)
|
||||
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 struct file_operations log_fops =
|
||||
{
|
||||
hysdn_dummy_lseek,
|
||||
hysdn_log_read,
|
||||
hysdn_log_write,
|
||||
NULL, /* readdir */
|
||||
hysdn_log_poll, /* poll */
|
||||
NULL, /*hysdn_log_ioctl, *//* ioctl */
|
||||
NULL, /* mmap */
|
||||
hysdn_log_open,
|
||||
NULL, /* flush */
|
||||
hysdn_log_close,
|
||||
NULL /* fsync */
|
||||
};
|
||||
|
||||
struct inode_operations log_inode_operations =
|
||||
{
|
||||
&log_fops, /* log proc file-ops */
|
||||
NULL, /* create */
|
||||
NULL, /* lookup */
|
||||
NULL, /* link */
|
||||
NULL, /* unlink */
|
||||
NULL, /* symlink */
|
||||
NULL, /* mkdir */
|
||||
NULL, /* rmdir */
|
||||
NULL, /* mknod */
|
||||
NULL, /* rename */
|
||||
NULL, /* readlink */
|
||||
NULL, /* follow_link */
|
||||
NULL, /* readpage */
|
||||
NULL, /* writepage */
|
||||
NULL, /* bmap */
|
||||
NULL, /* truncate */
|
||||
NULL /* permission */
|
||||
};
|
||||
|
||||
/*****************************************/
|
||||
/* Output info data to the cardinfo file */
|
||||
/*****************************************/
|
||||
static int
|
||||
info_read(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
|
||||
{
|
||||
char tmp[INFO_OUT_LEN * 11 + 2];
|
||||
int i;
|
||||
char *cp;
|
||||
hysdn_card *card;
|
||||
|
||||
sprintf(tmp, "id bus slot type irq iobase plx-mem dp-mem boot device");
|
||||
cp = tmp; /* start of string */
|
||||
while (*cp)
|
||||
cp++;
|
||||
while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
|
||||
*cp++ = ' ';
|
||||
*cp++ = '\n';
|
||||
|
||||
card = card_root; /* start of list */
|
||||
while (card) {
|
||||
sprintf(cp, "%d %3d %4d %4d %3d 0x%04x 0x%08x 0x%08x",
|
||||
card->myid,
|
||||
card->bus,
|
||||
PCI_SLOT(card->devfn),
|
||||
card->brdtype,
|
||||
card->irq,
|
||||
card->iobase,
|
||||
card->plxbase,
|
||||
card->membase);
|
||||
card = card->next;
|
||||
while (*cp)
|
||||
cp++;
|
||||
while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
|
||||
*cp++ = ' ';
|
||||
*cp++ = '\n';
|
||||
}
|
||||
|
||||
i = cp - tmp;
|
||||
*start = buffer;
|
||||
if (offset + length > i) {
|
||||
length = i - offset;
|
||||
*eof = 1;
|
||||
} else if (offset > i) {
|
||||
length = 0;
|
||||
*eof = 1;
|
||||
}
|
||||
cp = tmp + offset;
|
||||
|
||||
if (length > 0) {
|
||||
/* start_bh_atomic(); */
|
||||
memcpy(buffer, cp, length);
|
||||
/* end_bh_atomic(); */
|
||||
return length;
|
||||
}
|
||||
return 0;
|
||||
} /* info_read */
|
||||
|
||||
/*****************************/
|
||||
/* hysdn subdir in /proc/net */
|
||||
/*****************************/
|
||||
static struct proc_dir_entry *hysdn_proc_entry = NULL;
|
||||
static struct proc_dir_entry *hysdn_info_entry = NULL;
|
||||
|
||||
/***************************************************************************************/
|
||||
/* hysdn_procfs_init is called when the module is loaded and after the cards have been */
|
||||
/* detected. The needed proc dir and card entries are created. */
|
||||
/***************************************************************************************/
|
||||
int
|
||||
hysdn_procfs_init(void)
|
||||
{
|
||||
struct procdata *pd;
|
||||
hysdn_card *card;
|
||||
|
||||
hysdn_proc_entry = create_proc_entry(PROC_SUBDIR_NAME, S_IFDIR | S_IRUGO | S_IXUGO, proc_net);
|
||||
if (!hysdn_proc_entry) {
|
||||
printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
|
||||
return (-1);
|
||||
}
|
||||
hysdn_info_entry = create_proc_entry("cardinfo", 0, hysdn_proc_entry);
|
||||
if (hysdn_info_entry)
|
||||
hysdn_info_entry->read_proc = info_read; /* read info function */
|
||||
|
||||
/* create all cardlog proc entries */
|
||||
|
||||
card = card_root; /* start with first card */
|
||||
while (card) {
|
||||
if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
|
||||
memset(pd, 0, sizeof(struct procdata));
|
||||
|
||||
sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
|
||||
if ((pd->log = create_proc_entry(pd->log_name, S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry)) != NULL)
|
||||
pd->log->ops = &log_inode_operations; /* set new operations table */
|
||||
|
||||
#ifdef COMPAT_HAS_NEW_WAITQ
|
||||
init_waitqueue_head(&(pd->rd_queue));
|
||||
#endif
|
||||
|
||||
card->procfs = (void *) pd; /* remember procfs structure */
|
||||
}
|
||||
card = card->next; /* point to next card */
|
||||
}
|
||||
|
||||
printk(KERN_NOTICE "HYSDN: procfs Rev. %s initialised\n", hysdn_getrev(hysdn_procfs_revision));
|
||||
return (0);
|
||||
} /* hysdn_procfs_init */
|
||||
|
||||
/***************************************************************************************/
|
||||
/* hysdn_procfs_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_procfs_release(void)
|
||||
{
|
||||
struct procdata *pd;
|
||||
hysdn_card *card;
|
||||
|
||||
card = card_root; /* start with first card */
|
||||
while (card) {
|
||||
if ((pd = (struct procdata *) card->procfs) != NULL) {
|
||||
if (pd->log)
|
||||
remove_proc_entry(pd->log_name, hysdn_proc_entry);
|
||||
kfree(pd); /* release memory */
|
||||
}
|
||||
card = card->next; /* point to next card */
|
||||
}
|
||||
|
||||
remove_proc_entry("cardinfo", hysdn_proc_entry);
|
||||
remove_proc_entry(PROC_SUBDIR_NAME, proc_net);
|
||||
} /* hysdn_procfs_release */
|
|
@ -0,0 +1,497 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, /proc/net filesystem log functions.
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#define __NO_VERSION__
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
|
||||
static char *hysdn_proclog_revision = "$Revision$";
|
||||
|
||||
/* the proc subdir for the interface is defined in the procconf module */
|
||||
extern struct proc_dir_entry *hysdn_proc_entry;
|
||||
|
||||
/*************************************************/
|
||||
/* structure keeping ascii log for device output */
|
||||
/*************************************************/
|
||||
struct log_data {
|
||||
struct log_data *next;
|
||||
ulong 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 */
|
||||
uchar logtmp[LOG_MAX_LINELEN];
|
||||
#ifdef COMPAT_HAS_NEW_WAITQ
|
||||
wait_queue_head_t rd_queue;
|
||||
#else
|
||||
struct wait_queue *rd_queue; /* wait queue structure */
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**********************************************/
|
||||
/* 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. */
|
||||
/********************************************/
|
||||
void
|
||||
put_log_buffer(hysdn_card * card, char *cp)
|
||||
{
|
||||
struct log_data *ib;
|
||||
struct procdata *pd = card->proclog;
|
||||
int i, flags;
|
||||
|
||||
if (!pd)
|
||||
return;
|
||||
if (!cp)
|
||||
return;
|
||||
if (!*cp)
|
||||
return;
|
||||
if (pd->if_used <= 0)
|
||||
return; /* no open file for read */
|
||||
|
||||
if (!(ib = (struct log_data *) 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 */
|
||||
save_flags(flags);
|
||||
cli();
|
||||
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 */
|
||||
restore_flags(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 */
|
||||
|
||||
|
||||
/*************************/
|
||||
/* dummy file operations */
|
||||
/*************************/
|
||||
static loff_t
|
||||
hysdn_dummy_lseek(struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
return -ESPIPE;
|
||||
} /* hysdn_dummy_lseek */
|
||||
|
||||
/******************************/
|
||||
/* file operations and tables */
|
||||
/******************************/
|
||||
|
||||
/****************************************/
|
||||
/* write log file -> set log level bits */
|
||||
/****************************************/
|
||||
static ssize_t
|
||||
hysdn_log_write(struct file *file, const char *buf, size_t count, loff_t * off)
|
||||
{
|
||||
ulong u = 0;
|
||||
int found = 0;
|
||||
uchar *cp, valbuf[128];
|
||||
long base = 10;
|
||||
hysdn_card *card = (hysdn_card *) file->private_data;
|
||||
|
||||
if (&file->f_pos != off) /* fs error check */
|
||||
return (-ESPIPE);
|
||||
|
||||
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 *buf, size_t count, loff_t * off)
|
||||
{
|
||||
struct log_data *inf;
|
||||
int len;
|
||||
word ino;
|
||||
struct procdata *pd;
|
||||
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 */
|
||||
ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */
|
||||
card = card_root;
|
||||
while (card) {
|
||||
pd = card->proclog;
|
||||
if (pd->log->low_ino == ino)
|
||||
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 */
|
||||
(struct log_data **) 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;
|
||||
file->f_pos += 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;
|
||||
ulong flags;
|
||||
|
||||
MOD_INC_USE_COUNT; /* lock module */
|
||||
card = card_root;
|
||||
while (card) {
|
||||
pd = card->proclog;
|
||||
if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
|
||||
break;
|
||||
card = card->next; /* search next entry */
|
||||
}
|
||||
if (!card) {
|
||||
MOD_DEC_USE_COUNT; /* unlock module */
|
||||
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 */
|
||||
save_flags(flags);
|
||||
cli();
|
||||
pd->if_used++;
|
||||
if (pd->log_head)
|
||||
(struct log_data **) filep->private_data = &(pd->log_tail->next);
|
||||
else
|
||||
(struct log_data **) filep->private_data = &(pd->log_head);
|
||||
restore_flags(flags);
|
||||
} else { /* simultaneous read/write access forbidden ! */
|
||||
MOD_DEC_USE_COUNT; /* unlock module */
|
||||
return (-EPERM); /* no permission this time */
|
||||
}
|
||||
return (0);
|
||||
} /* 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 flags, retval = 0;
|
||||
|
||||
|
||||
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;
|
||||
save_flags(flags);
|
||||
cli();
|
||||
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->low_ino == (ino->i_ino & 0xFFFF))
|
||||
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;
|
||||
}
|
||||
restore_flags(flags);
|
||||
|
||||
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 */
|
||||
|
||||
MOD_DEC_USE_COUNT;
|
||||
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;
|
||||
word ino;
|
||||
hysdn_card *card;
|
||||
struct procdata *pd;
|
||||
|
||||
if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
|
||||
return (mask); /* no polling for write supported */
|
||||
|
||||
/* we need to search the card */
|
||||
ino = file->f_dentry->d_inode->i_ino & 0xFFFF; /* low-ino */
|
||||
card = card_root;
|
||||
while (card) {
|
||||
pd = card->proclog;
|
||||
if (pd->log->low_ino == ino)
|
||||
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 struct file_operations log_fops =
|
||||
{
|
||||
hysdn_dummy_lseek,
|
||||
hysdn_log_read,
|
||||
hysdn_log_write,
|
||||
NULL, /* readdir */
|
||||
hysdn_log_poll, /* poll */
|
||||
NULL,
|
||||
NULL, /* mmap */
|
||||
hysdn_log_open,
|
||||
NULL, /* flush */
|
||||
hysdn_log_close,
|
||||
NULL /* fsync */
|
||||
};
|
||||
|
||||
struct inode_operations log_inode_operations =
|
||||
{
|
||||
&log_fops, /* log proc file-ops */
|
||||
NULL, /* create */
|
||||
NULL, /* lookup */
|
||||
NULL, /* link */
|
||||
NULL, /* unlink */
|
||||
NULL, /* symlink */
|
||||
NULL, /* mkdir */
|
||||
NULL, /* rmdir */
|
||||
NULL, /* mknod */
|
||||
NULL, /* rename */
|
||||
NULL, /* readlink */
|
||||
NULL, /* follow_link */
|
||||
NULL, /* readpage */
|
||||
NULL, /* writepage */
|
||||
NULL, /* bmap */
|
||||
NULL, /* truncate */
|
||||
NULL /* permission */
|
||||
};
|
||||
|
||||
/***********************************************************************************/
|
||||
/* hysdn_proclog_init is called when the module is loaded after creating the cards */
|
||||
/* conf files.the cards have been */
|
||||
/***********************************************************************************/
|
||||
int
|
||||
hysdn_proclog_init(hysdn_card * card)
|
||||
{
|
||||
struct procdata *pd;
|
||||
|
||||
/* create a cardlog proc entry */
|
||||
|
||||
if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
|
||||
memset(pd, 0, sizeof(struct procdata));
|
||||
|
||||
sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
|
||||
if ((pd->log = create_proc_entry(pd->log_name, S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry)) != NULL)
|
||||
pd->log->ops = &log_inode_operations; /* set new operations table */
|
||||
|
||||
#ifdef COMPAT_HAS_NEW_WAITQ
|
||||
init_waitqueue_head(&(pd->rd_queue));
|
||||
#endif
|
||||
|
||||
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 */
|
|
@ -0,0 +1,199 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, scheduler routines for handling exchange card <-> pc.
|
||||
*
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#define __NO_VERSION__
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.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, uchar * buf, word len, word chan)
|
||||
{
|
||||
|
||||
switch (chan) {
|
||||
case CHAN_NDIS_DATA:
|
||||
hysdn_rx_netpkt(card, buf, len); /* give packet to network handler */
|
||||
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;
|
||||
|
||||
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, uchar * buf, word volatile *len, word volatile *chan, word 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 ((skb = hysdn_tx_netget(card)) != NULL) {
|
||||
if (skb->len <= maxlen) {
|
||||
memcpy(buf, skb->data, skb->len); /* copy the packet to the buffer */
|
||||
*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 */
|
||||
return (0); /* nothing to send */
|
||||
} /* hysdn_sched_tx */
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* send one config line to the card and return 0 if successfull, 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, uchar * line, word chan)
|
||||
{
|
||||
int cnt = 50; /* timeout intervalls */
|
||||
ulong flags;
|
||||
|
||||
if (card->debug_flags & LOG_SCHED_ASYN)
|
||||
hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1);
|
||||
|
||||
save_flags(flags);
|
||||
cli();
|
||||
while (card->async_busy) {
|
||||
sti();
|
||||
|
||||
if (card->debug_flags & LOG_SCHED_ASYN)
|
||||
hysdn_addlog(card, "async tx-cfg delayed");
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */
|
||||
if (!--cnt) {
|
||||
restore_flags(flags);
|
||||
return (-ERR_ASYNC_TIME); /* timed out */
|
||||
}
|
||||
cli();
|
||||
} /* wait for buffer to become free */
|
||||
|
||||
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 */
|
||||
queue_task(&card->irq_queue, &tq_immediate);
|
||||
mark_bh(IMMEDIATE_BH);
|
||||
sti();
|
||||
|
||||
if (card->debug_flags & LOG_SCHED_ASYN)
|
||||
hysdn_addlog(card, "async tx-cfg data queued");
|
||||
|
||||
cnt++; /* short delay */
|
||||
cli();
|
||||
|
||||
while (card->async_busy) {
|
||||
sti();
|
||||
|
||||
if (card->debug_flags & LOG_SCHED_ASYN)
|
||||
hysdn_addlog(card, "async tx-cfg waiting for tx-ready");
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */
|
||||
if (!--cnt) {
|
||||
restore_flags(flags);
|
||||
return (-ERR_ASYNC_TIME); /* timed out */
|
||||
}
|
||||
cli();
|
||||
} /* wait for buffer to become free again */
|
||||
|
||||
restore_flags(flags);
|
||||
|
||||
if (card->debug_flags & LOG_SCHED_ASYN)
|
||||
hysdn_addlog(card, "async tx-cfg data send");
|
||||
|
||||
return (0); /* line send correctly */
|
||||
} /* hysdn_tx_cfgline */
|
|
@ -0,0 +1,132 @@
|
|||
#ifndef __INCE1PC_H__
|
||||
#define __INCE1PC_H__
|
||||
|
||||
/****************************************************************************
|
||||
|
||||
FILE: ince1pc.h
|
||||
|
||||
AUTHOR: M.Steinkopf
|
||||
|
||||
PURPOSE: common definitions for both sides of the bus:
|
||||
- conventions both spoolers must know
|
||||
- channel numbers agreed upon
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
/* 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 uchar
|
||||
* 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 */ ulong ulErrType;
|
||||
|
||||
/*04 */ ulong ulErrSubtype;
|
||||
|
||||
/*08 */ uchar ucTextSize;
|
||||
|
||||
/*09 */ uchar 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 */ uchar Len;
|
||||
|
||||
/*01 */ volatile uchar RdPtr;
|
||||
|
||||
/*02 */ uchar WrPtr;
|
||||
|
||||
/*03 */ uchar 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__ */
|
|
@ -0,0 +1,45 @@
|
|||
/* $Id$
|
||||
|
||||
* Linux driver for HYSDN cards, ioctl definitions shared by hynetmgr and driver.
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Log$
|
||||
*
|
||||
*/
|
||||
|
||||
/****************/
|
||||
/* error values */
|
||||
/****************/
|
||||
#define ERR_NONE 0 /* no error occured */
|
||||
#define ERR_ALREADY_BOOT 1000 /* we are already booting */
|
||||
#define EPOF_BAD_MAGIC 1001 /* bad magic in POF header */
|
||||
#define ERR_BOARD_DPRAM 1002 /* board DPRAM failed */
|
||||
#define EPOF_INTERNAL 1003 /* internal POF handler error */
|
||||
#define EPOF_BAD_IMG_SIZE 1004 /* POF boot image size invalid */
|
||||
#define ERR_BOOTIMG_FAIL 1005 /* 1. stage boot image did not start */
|
||||
#define ERR_BOOTSEQ_FAIL 1006 /* 2. stage boot seq handshake timeout */
|
||||
#define ERR_POF_TIMEOUT 1007 /* timeout waiting for card pof ready */
|
||||
#define ERR_NOT_BOOTED 1008 /* operation only allowed when booted */
|
||||
#define ERR_CONF_LONG 1009 /* conf line is to long */
|
||||
#define ERR_INV_CHAN 1010 /* invalid channel number */
|
||||
#define ERR_ASYNC_TIME 1011 /* timeout sending async data */
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue