Commit d52531b8 authored by Linus Torvalds's avatar Linus Torvalds

This removes the old Eicon ISDN driver.

The config entries etc were already removed earlier.
parent f69c2d56
/*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/fs.h>
#undef N_DATA
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include "adapter.h"
#include "uxio.h"
MODULE_DESCRIPTION("ISDN4Linux: Driver for Eicon Diva Server cards");
MODULE_AUTHOR("Armin Schindler");
MODULE_LICENSE("GPL");
void DivasInitDpc(void);
#ifdef MODULE
#include "idi.h"
void DIVA_DIDD_Write(DESCRIPTOR *, int);
EXPORT_SYMBOL_NOVERS(DIVA_DIDD_Read);
EXPORT_SYMBOL_NOVERS(DIVA_DIDD_Write);
EXPORT_SYMBOL_NOVERS(DivasPrintf);
#endif
int DivasCardsDiscover(void);
static int __init
divas_init(void)
{
printk(KERN_DEBUG "DIVA Server Driver - initialising\n");
printk(KERN_DEBUG "DIVA Server Driver - Version 2.0.16\n");
#if !defined(CONFIG_PCI)
printk(KERN_WARNING "CONFIG_PCI is not defined!\n");
return -ENODEV;
#endif
DivasInitDpc();
if (DivasCardsDiscover() < 0)
{
printk(KERN_WARNING "Divas: Not loaded\n");
return -ENODEV;
}
return 0;
}
static void __exit
divas_exit(void)
{
card_t *pCard;
word wCardIndex;
extern int Divas_major;
printk(KERN_DEBUG "DIVA Server Driver - unloading\n");
pCard = DivasCards;
for (wCardIndex = 0; wCardIndex < MAX_CARDS; wCardIndex++)
{
if ((pCard->hw) && (pCard->hw->in_use))
{
(*pCard->card_reset)(pCard);
UxIsrRemove(pCard->hw, pCard);
UxCardHandleFree(pCard->hw);
if(pCard->e_tbl != NULL)
{
kfree(pCard->e_tbl);
}
if(pCard->hw->card_type == DIA_CARD_TYPE_DIVA_SERVER_B)
{
release_region(pCard->hw->io_base,0x20);
release_region(pCard->hw->reset_base,0x80);
}
// If this is a 4BRI ...
if (pCard->hw->card_type == DIA_CARD_TYPE_DIVA_SERVER_Q)
{
// Skip over the next 3 virtual adapters
wCardIndex += 3;
// But free their handles
pCard++;
UxCardHandleFree(pCard->hw);
if(pCard->e_tbl != NULL)
{
kfree(pCard->e_tbl);
}
pCard++;
UxCardHandleFree(pCard->hw);
if(pCard->e_tbl != NULL)
{
kfree(pCard->e_tbl);
}
pCard++;
UxCardHandleFree(pCard->hw);
if(pCard->e_tbl != NULL)
{
kfree(pCard->e_tbl);
}
}
}
pCard++;
}
unregister_chrdev(Divas_major, "Divas");
}
module_init(divas_init);
module_exit(divas_exit);
#
# Config.in for Eicon active ISDN support
#
config ISDN_DRV_EICON
bool "Eicon active card support"
help
Say Y here if you have an Eicon active ISDN card. In order to use
this card, additional firmware is necessary, which has to be loaded
into the card using the eiconctrl utility which is part of the
latest isdn4k-utils package. Please read the file
<file:Documentation/isdn/README.eicon> for more information.
choice
prompt "Eicon active card support"
optional
depends on ISDN_DRV_EICON && ISDN && m
config ISDN_DRV_EICON_DIVAS
tristate "Eicon driver"
depends on PCI
help
Enable this option if you want the eicon driver as standalone
version with no interface to the ISDN4Linux isdn module. If you
say Y here, the eicon module only supports the Diva Server PCI
cards and will provide its own IDI interface. You should say N
here.
config ISDN_DRV_EICON_OLD
tristate "Legacy driver"
help
Say Y here to use your Eicon active ISDN card with ISDN4Linux
isdn module.
config ISDN_DRV_EICON_PCI
bool "Eicon PCI DIVA Server BRI/PRI/4BRI support"
depends on ISDN_DRV_EICON_OLD && PCI
help
Say Y here if you have an Eicon Diva Server (BRI/PRI/4BRI) ISDN
card. Please read <file:Documentation/isdn/README.eicon> for more
information.
config ISDN_DRV_EICON_ISA
bool "Eicon S,SX,SCOM,Quadro,S2M support"
depends on ISDN_DRV_EICON_OLD
help
Say Y here if you have an old-type Eicon active ISDN card. In order
to use this card, additional firmware is necessary, which has to be
loaded into the card using the eiconctrl utility which is part of
the latest isdn4k-utils package. Please read the file
<file:Documentation/isdn/README.eicon> for more information.
endchoice
# Makefile for the eicon ISDN device driver
# Each configuration option enables a list of files.
obj-$(CONFIG_ISDN_DRV_EICON_OLD) += eicon.o
obj-$(CONFIG_ISDN_DRV_EICON_DIVAS) += divas.o
# Multipart objects.
eicon-y := eicon_mod.o eicon_isa.o eicon_pci.o \
eicon_idi.o eicon_io.o
eicon-$(CONFIG_ISDN_DRV_EICON_PCI) += common.o idi.o bri.o pri.o log.o \
xlog.o kprintf.o fpga.o fourbri.o lincfg.o \
linchr.o linsys.o linio.o
divas-y := common.o idi.o bri.o pri.o log.o xlog.o \
kprintf.o fpga.o fourbri.o lincfg.o \
linchr.o linsys.o linio.o Divas_mod.o
/*
* Main internal include file for Diva Server driver
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.7
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#if !defined(ADAPTER_H)
#define ADAPTER_H
#include "sys.h"
#include "idi.h"
#include "divas.h"
#undef ID_MASK
#include "pc.h"
#define XMOREC 0x1f
#define XMOREF 0x20
#define XBUSY 0x40
#define RMORE 0x80
/* structure for all information we have to keep on a per */
/* adapater basis */
typedef struct adapter_s ADAPTER;
struct adapter_s {
void * io;
byte IdTable[256];
byte ReadyInt;
byte (* ram_in)(ADAPTER * a, void * adr);
word (* ram_inw)(ADAPTER * a, void * adr);
void (* ram_in_buffer)(ADAPTER * a, void * adr, void * P, word length);
void (* ram_look_ahead)(ADAPTER * a, PBUFFER * RBuffer, ENTITY * e);
void (* ram_out)(ADAPTER * a, void * adr, byte data);
void (* ram_outw)(ADAPTER * a, void * adr, word data);
void (* ram_out_buffer)(ADAPTER * a, void * adr, void * P, word length);
void (* ram_inc)(ADAPTER * a, void * adr);
};
typedef struct card card_t;
typedef int card_load_fn_t(card_t *card, dia_load_t *load);
typedef int card_config_fn_t(card_t *card, dia_config_t *config);
typedef int card_start_fn_t(card_t *card, byte *channels);
typedef int card_reset_fn_t(card_t *card);
typedef int card_mem_get_fn_t(card_t *card, mem_block_t *mem_block);
#define MAX_PENTITIES 256 /* Number of entities primary adapter */
#define MAX_ENTITIES 16 /* Number of entities standard adapter */
typedef struct e_info_s E_INFO;
struct e_info_s
{
ENTITY *e; /* entity pointer */
byte next; /* chaining index */
word assign_ref; /* assign reference */
};
/* DIVA card info (details hidden from user) */
typedef struct ux_diva_card_s ux_diva_card_t;
/* card info */
struct card
{
ADAPTER a; /* per-adapter information */
dia_card_t cfg; /* card configuration */
int state; /* State of the adapter */
dword serial_no; /* serial number */
int test_int_pend; /* set for interrupt testing */
ux_diva_card_t *hw; /* O/S-specific handle */
card_reset_fn_t *card_reset; /* call this to reset card */
card_load_fn_t *card_load; /* call this to load card */
card_config_fn_t *card_config; /* call this to config card */
card_start_fn_t *card_start; /* call this to start card */
card_mem_get_fn_t *card_mem_get; /* call this to get card memory */
E_INFO *e_tbl; /* table of ENTITY pointers */
byte e_head; /* list of active ENTITIES */
byte e_tail; /* list of active ENTITIES */
int e_count; /* # of active ENTITIES */
int e_max; /* total # of ENTITIES */
byte assign; /* assign queue entry */
PBUFFER RBuffer; /* Copy of receive lookahead buffer */
int log_types; /* bit-mask of active logs */
word xlog_offset; /* offset to XLOG buffer on card */
void (*out)(ADAPTER *a);
byte (*dpc)(ADAPTER * a);
byte (*test_int)(ADAPTER * a);
void (*clear_int)(ADAPTER * a);
void (*reset_int)(card_t *c);
int is_live;
int (*card_isr)(card_t *card);
int int_pend; /* interrupt pending */
long interrupt_reentered;
long dpc_reentered;
int set_xlog_request;
} ;
/* card information */
#define MAX_CARDS 20 /* max number of cards on a system */
extern
card_t DivasCards[];
extern
int DivasCardNext;
extern
dia_config_t DivasCardConfigs[];
extern
byte DivasFlavourConfig[];
/*------------------------------------------------------------------*/
/* public functions of IDI common code */
/*------------------------------------------------------------------*/
void DivasOut(ADAPTER * a);
byte DivasDpc(ADAPTER * a);
byte DivasTestInt(ADAPTER * a);
void DivasClearInt(ADAPTER * a);
/*------------------------------------------------------------------*/
/* public functions of configuration platform-specific code */
/*------------------------------------------------------------------*/
int DivasConfigGet(dia_card_t *card);
/*------------------------------------------------------------------*/
/* public functions of LOG related code */
/*------------------------------------------------------------------*/
void DivasXlogReq(int card_num);
int DivasXlogRetrieve(card_t *card);
void DivasLog(dia_log_t *log);
void DivasLogIdi(card_t *card, ENTITY *e, int request);
/*------------------------------------------------------------------*/
/* public functions to initialise cards for each type supported */
/*------------------------------------------------------------------*/
int DivasPriInit(card_t *card, dia_card_t *cfg);
int DivasBriInit(card_t *card, dia_card_t *cfg);
int Divas4BriInit(card_t *card, dia_card_t *cfg);
void DivasBriPatch(card_t *card);
/*------------------------------------------------------------------*/
/* public functions of log common code */
/*------------------------------------------------------------------*/
extern char *DivasLogFifoRead(void);
extern void DivasLogFifoWrite(char *entry, int length);
extern int DivasLogFifoEmpty(void);
extern int DivasLogFifoFull(void);
extern void DivasLogAdd(void *buffer, int length);
/*------------------------------------------------------------------*/
/* public functions of misc. platform-specific code */
/*------------------------------------------------------------------*/
int DivasDpcSchedule(void);
void DivasDoDpc(unsigned long);
int DivasScheduleRequestDpc(void);
/* table of IDI request functions */
extern
IDI_CALL DivasIdiRequest[];
/*
* initialisation entry point
*/
int DivasInit(void);
/*
* Get information on the number and type of cards present
*/
extern
int DivasCardsDiscover(void);
/*
* initialise a new card
*/
int DivasCardNew(dia_card_t *card);
/*
* configure specified card
*/
int DivasCardConfig(dia_config_t *config);
/*
* load specified binary code onto card
*/
int DivasCardLoad(dia_load_t *load);
/*
* start specified card running
*/
int DivasCardStart(int card_id);
/*
* ISR for card
* Returns 0 if specified card was interrupting
*/
int DivasIsr(void *arg);
/*
* Get number of active cards
*/
int DivasGetNum(void);
/*
* Get list of active cards
*/
int DivasGetList(dia_card_list_t *card_list);
/* definitions common to several card types */
#define DIVAS_SHARED_OFFSET (0x1000)
#endif /* ADAPTER_H */
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.8
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "eicon.h"
#include "sys.h"
#include "idi.h"
#include "divas.h"
#include "pc.h"
#include "pr_pc.h"
#include "dsp_defs.h"
#include "adapter.h"
#include "uxio.h"
#define PCI_BADDR0 0x10
#define PCI_BADDR1 0x14
#define PCI_BADDR2 0x18
#define DIVAS_SIGNATURE 0x4447
/* offset to start of MAINT area (used by xlog) */
#define DIVAS_MAINT_OFFSET 0xff00 /* value for BRI card */
#define PROTCAP_TELINDUS 0x1
#define PROTCAP_V90D 0x8
word GetProtFeatureValue(char *sw_id);
byte io_in(ADAPTER *a, void *adr);
word io_inw(ADAPTER *a, void *adr);
void io_in_buffer(ADAPTER *a, void *adr, void *P, word length);
void io_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e);
void io_out(ADAPTER *a, void *adr, byte data);
void io_outw(ADAPTER *a, void *adr, word data);
void io_out_buffer(ADAPTER *a, void *adr, void *P, word length);
void io_inc(ADAPTER *a, void *adr);
static int diva_server_bri_test_int(card_t *card);
static int bri_ISR (card_t* card);
#define PLX_IOBASE 0
#define DIVAS_IOBASE 1
#define REG_DATA 0x00
#define REG_ADDRLO 0x04
#define REG_ADDRHI 0x0C
#define REG_IOCTRL 0x10
#define M_PCI_RESET 0x10
byte UxCardPortIoIn(ux_diva_card_t *card, byte *base, int offset);
word UxCardPortIoInW(ux_diva_card_t *card, byte *base, int offset);
void UxCardPortIoOut(ux_diva_card_t *card, byte *base, int offset, byte);
void UxCardPortIoOutW(ux_diva_card_t *card, byte *base, int offset, word);
int DivasBRIInitPCI(card_t *card, dia_card_t *cfg);
static
int diva_server_bri_reset(card_t *card)
{
byte *DivasIOBase;
word i;
dword dwWait;
UxCardLog(0);
DPRINTF(("divas: resetting BRI adapter"));
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
UxCardPortIoOut(card->hw, DivasIOBase, REG_IOCTRL, 0);
for (i=0; i < 50000; i++)
;
UxCardPortIoOut(card->hw, DivasIOBase, REG_ADDRHI, 0);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 0);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_DATA , 0);
UxCardPortIoOut(card->hw, DivasIOBase, REG_ADDRHI, 0xFF);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 0x0000);
for (i=0; i<0x8000; i++)
{
UxCardPortIoOutW(card->hw, DivasIOBase, REG_DATA , 0);
}
for (dwWait=0; dwWait < 0x00FFFFFF; dwWait++)
;
UxCardMemDetach(card->hw, DivasIOBase);
return 0;
}
static
void diva_server_bri_reset_int(card_t *card)
{
byte *DivasIOBase = NULL;
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
UxCardPortIoOut(card->hw, DivasIOBase, REG_IOCTRL, 0x08);
UxCardMemDetach(card->hw, DivasIOBase);
return;
}
static
int diva_server_bri_start(card_t *card, byte *channels)
{
byte *DivasIOBase, *PLXIOBase;
word wSig = 0;
word i;
dword dwSerialNum;
byte bPLX9060 = FALSE;
DPRINTF(("divas: starting Diva Server BRI card"));
card->is_live = FALSE;
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
UxCardPortIoOut(card->hw, DivasIOBase, REG_ADDRHI, 0xFF);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 0x1E);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA , 0);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA , 0);
UxCardPortIoOut(card->hw, DivasIOBase, REG_IOCTRL, 0x08);
/* wait for signature to indicate card has started */
for (i = 0; i < 300; i++)
{
UxCardPortIoOut(card->hw, DivasIOBase, REG_ADDRHI, 0xFF);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 0x1E);
wSig = UxCardPortIoInW(card->hw, DivasIOBase, REG_DATA);
if (wSig == DIVAS_SIGNATURE)
{
DPRINTF(("divas: card started after %d ms", i * 10));
break;
}
UxPause(10);
}
if (wSig != DIVAS_SIGNATURE)
{
DPRINTF(("divas: card failed to start (Sig=0x%x)", wSig));
UxCardMemDetach(card->hw, DivasIOBase);
return -1;
}
card->is_live = TRUE;
UxCardPortIoOut(card->hw, DivasIOBase, REG_ADDRHI, 0xFF);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 0x3F6);
*channels = UxCardPortIoInW(card->hw, DivasIOBase, REG_DATA);
UxCardMemDetach(card->hw, DivasIOBase);
PLXIOBase = UxCardMemAttach(card->hw, PLX_IOBASE);
bPLX9060 = UxCardPortIoInW(card->hw, PLXIOBase, 0x6C) | UxCardPortIoInW(card->hw, PLXIOBase, 0x6E);
if (bPLX9060)
{
dwSerialNum = (UxCardPortIoInW(card->hw, PLXIOBase, 0x1E) << 16) |
(UxCardPortIoInW(card->hw, PLXIOBase, 0x22));
DPRINTF(("divas: PLX9060 in use. Serial number 0x%04X", dwSerialNum));
}
else
{
dwSerialNum = (UxCardPortIoInW(card->hw, PLXIOBase, 0x22) << 16) |
(UxCardPortIoInW(card->hw, PLXIOBase, 0x26));
DPRINTF(("divas: PLX9050 in use. Serial number 0x%04X", dwSerialNum));
}
UxCardMemDetach(card->hw, PLXIOBase);
card->serial_no = dwSerialNum;
diva_server_bri_test_int(card);
return 0;
}
static
int diva_server_bri_load(card_t *card, dia_load_t *load)
{
byte *DivasIOBase;
dword r3000_base;
dword dwAddr, dwLength, i;
word wTest, aWord;
DPRINTF(("divas: loading Diva Server BRI card"));
switch (load->code_type)
{
case DIA_CPU_CODE:
DPRINTF(("divas: loading RISC %s", &load->code[0x80]));
card->hw->features = GetProtFeatureValue((char *)&load->code[0x80]);
DPRINTF(("divas: features 0x%x", card->hw->features));
if (card->hw->features == 0xFFFF)
{
DPRINTF(("divas: invalid feature string failed load\n"));
return -1;
}
r3000_base = 0;
break;
case DIA_DSP_CODE:
DPRINTF(("divas: DSP code \"%s\"", load->code));
if ((card->hw->features) && (!(card->hw->features & PROTCAP_TELINDUS)))
{
DPRINTF(("divas: only Telindus style binaries supported"));
return -1;
}
if ((card->hw->features) && (card->hw->features & PROTCAP_V90D))
{
DPRINTF(("divas: V.90 DSP binary"));
r3000_base = (0xBF790000 + (((sizeof(dword) + (sizeof(t_dsp_download_desc)* DSP_MAX_DOWNLOAD_COUNT)) + 3) & 0xFFFFFFFC));
}
else
{
DPRINTF(("divas: non-V.90 DSP binary"));
r3000_base = (0xBF7A0000 + (((sizeof(dword) + (sizeof(t_dsp_download_desc)* DSP_MAX_DOWNLOAD_COUNT)) + 3) & 0xFFFFFFFC));
}
DPRINTF(("divas: loading at 0x%x", r3000_base));
break;
case DIA_TABLE_CODE:
DPRINTF(("divas: TABLE code"));
if ((card->hw->features) && (card->hw->features & PROTCAP_V90D))
{
r3000_base = 0xBF790000 + sizeof(dword);
}
else
{
r3000_base = 0xBF7A0000 + sizeof(dword);
}
break;
case DIA_DLOAD_CNT:
DPRINTF(("divas: COUNT code"));
if ((card->hw->features) && (card->hw->features & PROTCAP_V90D))
{
r3000_base = 0xBF790000;
}
else
{
r3000_base = 0xBF7A0000;
}
break;
default:
DPRINTF(("divas: unknown code type %d", load->code_type));
return -1;
break;
}
DPRINTF(("divas: Writing %d bytes to adapter, address 0x%x", load->length, r3000_base));
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
DPRINTF(("divas: Attached to 0x%04X", DivasIOBase));
dwLength = load->length;
for (i=0; i < dwLength; i++)
{
dwAddr = r3000_base + i;
UxCardPortIoOut(card->hw, DivasIOBase, REG_ADDRHI, dwAddr >> 16);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, dwAddr);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, load->code[i]);
}
DPRINTF(("divas: Verifying"));
for (i=0; i<dwLength; i++)
{
dwAddr = r3000_base + i;
UxCardPortIoOut(card->hw, DivasIOBase, REG_ADDRHI, dwAddr >> 16);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, dwAddr);
wTest = UxCardPortIoIn(card->hw, DivasIOBase, REG_DATA);
aWord = load->code[i];
if (wTest != aWord)
{
DPRINTF(("divas: load verify failed on byte %d", i));
DPRINTF(("divas: RAM 0x%x File 0x%x",wTest,aWord));
UxCardMemDetach(card->hw, DivasIOBase);
return -1;
}
}
DPRINTF(("divas: Loaded and verified. Detaching from adapter"));
UxCardMemDetach(card->hw, DivasIOBase);
UxCardLog(0);
return 0;
}
static
int diva_server_bri_config(card_t *card, dia_config_t *config)
{
byte *DivasIOBase, i;
DPRINTF(("divas: configuring Diva Server BRI card"));
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
UxCardPortIoOut(card->hw, DivasIOBase, REG_ADDRHI, 0xFF);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 8);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->tei);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 9);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->nt2);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 10);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->sig_flags);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 11);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->watchdog);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 12);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->permanent);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 13);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, 0);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 14);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->stable_l2);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 15);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->no_order_check);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 16);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, 0);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 17);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, 0);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 18);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->low_channel);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 19);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->prot_version);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 20);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->crc4);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 21);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, 0);
if ((card->hw->features) && (card->hw->features & PROTCAP_V90D))
{
DPRINTF(("divas: Signifying V.90"));
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 22);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, 4);
}
else
{
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 22);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, 0);
}
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 23);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, card->serial_no & 0xFF);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 24);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, (card->serial_no >> 8) & 0xFF);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 25);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, (card->serial_no >> 16) & 0xFF);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 26);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, 21);
for (i=0; i<32; i++)
{
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 32+i);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->terminal[0].oad[i]);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 64+i);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->terminal[0].osa[i]);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 96+i);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->terminal[0].spid[i]);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 128+i);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->terminal[1].oad[i]);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 160+i);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->terminal[1].osa[i]);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, 192+i);
UxCardPortIoOut(card->hw, DivasIOBase, REG_DATA, config->terminal[1].spid[i]);
}
UxCardMemDetach(card->hw, DivasIOBase);
return 0;
}
void DivasBriPatch(card_t *card)
{
dword PLXIOBase = 0;
dword DivasIOBase = 0;
PLXIOBase = card->cfg.reset_base;
DivasIOBase = card->cfg.io_base;
if(card->hw == NULL)
{
DPRINTF(("Divas: BRI PATCH (PLX chip) card->hw is null"));
return;
}
if (PLXIOBase == 0)
{
DPRINTF(("Divas: BRI (PLX chip) cannot be patched. The BRI adapter may"));
DPRINTF(("Divas: not function properly. If you do encounter problems,"));
DPRINTF(("Divas: ensure that your machine is using the latest BIOS."));
return;
}
DPRINTF(("Divas: PLX I/O Base 0x%x", PLXIOBase));
DPRINTF(("Divas: Divas I/O Base 0x%x", DivasIOBase));
if (PLXIOBase & 0x80)
{
dword dwSize, dwSerialNum, dwCmd;
boolean_t bPLX9060;
word wSerHi, wSerLo;
DPRINTF(("Divas: Patch required"));
dwCmd = 0;
UxPciConfigWrite(card->hw, 4, PCI_COMMAND, &dwCmd);
PLXIOBase &= ~0x80;
UxPciConfigWrite(card->hw, 4, PCI_BADDR1, &PLXIOBase);
dwSize = 0xFFFFFFFF;
UxPciConfigWrite(card->hw, 4, PCI_BADDR1, &dwSize);
UxPciConfigRead(card->hw, 4, PCI_BADDR1, &dwSize);
dwSize = (~ (dwSize & ~7)) + 1;
DivasIOBase = PLXIOBase + dwSize;
card->cfg.reset_base = PLXIOBase;
card->cfg.io_base = DivasIOBase;
UxPciConfigWrite(card->hw, 4, PCI_BADDR1, &card->cfg.reset_base);
UxPciConfigWrite(card->hw, 4, PCI_BADDR2, &card->cfg.io_base);
dwCmd = 5;
UxPciConfigWrite(card->hw, 4, PCI_COMMAND, &dwCmd);
bPLX9060 = UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x6C) |
UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x6E);
if (bPLX9060)
{
wSerHi = UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x1E);
wSerLo = UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x22);
dwSerialNum = (wSerHi << 16) | wSerLo;
UxCardLog(0);
}
else
{
wSerHi = UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x22);
wSerLo = UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x26);
dwSerialNum = (wSerHi << 16) | wSerLo;
UxCardLog(0);
}
}
else
{
word wSerHi, wSerLo;
boolean_t bPLX9060;
dword dwSerialNum;
DPRINTF(("divas: No patch required"));
bPLX9060 = UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x6C) |
UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x6E);
if (bPLX9060)
{
wSerHi = UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x1E);
wSerLo = UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x22);
dwSerialNum = (wSerHi << 16) | wSerLo;
}
else
{
wSerHi = UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x22);
wSerLo = UxCardPortIoInW(card->hw, (void *) card->cfg.reset_base, 0x26);
dwSerialNum = (wSerHi << 16) | wSerLo;
}
}
DPRINTF(("Divas: After patching:"));
DPRINTF(("Divas: PLX I/O Base 0x%x", PLXIOBase));
DPRINTF(("Divas: Divas I/O Base 0x%x", DivasIOBase));
}
#define TEST_INT_DIVAS_BRI 0x12
static
int diva_server_bri_test_int(card_t *card)
{
boolean_t bPLX9060 = FALSE;
byte *PLXIOBase = NULL, *DivasIOBase = NULL;
DPRINTF(("divas: test interrupt for Diva Server BRI card"));
PLXIOBase = UxCardMemAttach(card->hw, PLX_IOBASE);
bPLX9060 = UxCardPortIoInW(card->hw, PLXIOBase, 0x6C) || UxCardPortIoInW(card->hw, PLXIOBase, 0x6E);
if (bPLX9060)
{ /* PLX9060 */
UxCardPortIoOut(card->hw, PLXIOBase, 0x69, 0x09);
}
else
{ /* PLX9050 */
UxCardPortIoOut(card->hw, PLXIOBase, 0x4C, 0x41);
}
card->test_int_pend = TEST_INT_DIVAS_BRI;
UxCardMemDetach(card->hw, PLXIOBase);
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
UxCardPortIoOut(card->hw, DivasIOBase, REG_IOCTRL, 0x89);
UxCardMemDetach(card->hw, DivasIOBase);
return 0;
}
static
int diva_server_bri_mem_get(card_t *card, mem_block_t *mem_block)
{
dword user_addr = mem_block->addr;
word length = 0;
dword addr;
word i;
byte *DivasIOBase;
DPRINTF(("divas: Retrieving memory from 0x%x", user_addr));
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
addr = user_addr;
for (i=0; i < (16 * 8); i++)
{
addr = user_addr + i;
UxCardPortIoOut(card->hw, DivasIOBase, REG_ADDRHI, addr >> 16);
UxCardPortIoOutW(card->hw, DivasIOBase, REG_ADDRLO, (word) addr);
mem_block->data[i] = UxCardPortIoIn(card->hw, DivasIOBase, REG_DATA);
length++;
}
UxCardMemDetach(card->hw, DivasIOBase);
return length;
}
int DivasBriInit(card_t *card, dia_card_t *cfg)
{
DPRINTF(("divas: initialise Diva Server BRI card"));
if (DivasBRIInitPCI(card, cfg) == -1)
{
return -1;
}
card->card_reset = diva_server_bri_reset;
card->card_start = diva_server_bri_start;
card->card_load = diva_server_bri_load;
card->card_config = diva_server_bri_config;
card->reset_int = diva_server_bri_reset_int;
card->card_mem_get = diva_server_bri_mem_get;
card->xlog_offset = DIVAS_MAINT_OFFSET;
card->out = DivasOut;
card->test_int = DivasTestInt;
card->dpc = DivasDpc;
card->clear_int = DivasClearInt;
card->card_isr = bri_ISR;
card->a.ram_out = io_out;
card->a.ram_outw = io_outw;
card->a.ram_out_buffer = io_out_buffer;
card->a.ram_inc = io_inc;
card->a.ram_in = io_in;
card->a.ram_inw = io_inw;
card->a.ram_in_buffer = io_in_buffer;
card->a.ram_look_ahead = io_look_ahead;
return 0;
}
word GetProtFeatureValue(char *sw_id)
{
word features = 0;
while ((*sw_id) && (sw_id[0] != '['))
sw_id++;
if (sw_id == NULL)
{
DPRINTF(("divas: no feature string present"));
features = -1;
}
else
{
byte i, shifter;
sw_id += 3;
for (i=0, shifter=12; i<4; i++, shifter-=4)
{
if ((sw_id[i] >= '0') && (sw_id[i] <= '9'))
{
features |= (sw_id[i] - '0') << shifter;
}
else if ((sw_id[i] >= 'a') && (sw_id[i] <= 'f'))
{
features |= (sw_id[i] - 'a' + 10) << shifter;
}
else if ((sw_id[i] >= 'A') && (sw_id[i] <= 'F'))
{
features |= (sw_id[i] - 'A' + 10) << shifter;
}
else
{
DPRINTF(("divas: invalid feature string"));
return -1;
}
}
}
return features;
}
int bri_ISR (card_t* card)
{
int served = 0;
byte *DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
if (UxCardPortIoIn (card->hw, DivasIOBase, M_PCI_RESET) & 0x01)
{
served = 1;
card->int_pend += 1;
DivasDpcSchedule(); /* ISR DPC */
UxCardPortIoOut (card->hw, DivasIOBase, M_PCI_RESET, 0x08);
}
UxCardMemDetach(card->hw, DivasIOBase);
return (served != 0);
}
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.15
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "eicon.h"
#include "sys.h"
#include "idi.h"
#include "constant.h"
#include "divas.h"
#include "pc.h"
#include "pr_pc.h"
#include "uxio.h"
#define DIVAS_LOAD_CMD 0x02
#define DIVAS_START_CMD 0x03
#define DIVAS_IRQ_RESET 0xC18
#define DIVAS_IRQ_RESET_VAL 0xFE
#define TEST_INT_DIVAS 0x11
#define TEST_INT_DIVAS_BRI 0x12
#define TEST_INT_DIVAS_Q 0x13
#define DIVAS_RESET 0x81
#define DIVAS_LED1 0x04
#define DIVAS_LED2 0x08
#define DIVAS_LED3 0x20
#define DIVAS_LED4 0x40
#define DIVAS_SIGNATURE 0x4447
#define MP_PROTOCOL_ADDR 0xA0011000
#define PLX_IOBASE 0
#define DIVAS_IOBASE 1
typedef struct {
dword cmd;
dword addr;
dword len;
dword err;
dword live;
dword reserved[(0x1020>>2)-6];
dword signature;
byte data[1];
} diva_server_boot_t;
int DivasCardNext;
card_t DivasCards[MAX_CARDS];
dia_config_t *DivasConfig(card_t *, dia_config_t *);
static
DESCRIPTOR DIDD_Table[32];
void DIVA_DIDD_Read( DESCRIPTOR *table, int tablelength )
{
memset(table, 0, tablelength);
if (tablelength > sizeof(DIDD_Table))
tablelength = sizeof(DIDD_Table);
if(tablelength % sizeof(DESCRIPTOR)) {
tablelength /= sizeof(DESCRIPTOR);
tablelength *= sizeof(DESCRIPTOR);
}
if (tablelength > 0)
memcpy((void *)table, (void *)DIDD_Table, tablelength);
return;
}
void DIVA_DIDD_Write(DESCRIPTOR *table, int tablelength)
{
if (tablelength > sizeof(DIDD_Table))
tablelength = sizeof(DIDD_Table);
memcpy((void *)DIDD_Table, (void *)table, tablelength);
return;
}
static
void init_idi_tab(void)
{
DESCRIPTOR d[32];
memset(d, 0, sizeof(d));
d[0].type = IDI_DIMAINT; /* identify the DIMAINT entry */
d[0].channels = 0; /* zero channels associated with dimaint*/
d[0].features = 0; /* no features associated with dimaint */
d[0].request = (IDI_CALL) DivasPrintf;
DIVA_DIDD_Write(d, sizeof(d));
return;
}
/*
* I/O routines for memory mapped cards
*/
byte mem_in(ADAPTER *a, void *adr)
{
card_t *card = a->io;
unsigned char *b, *m;
byte value;
m = b = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
m += (unsigned int) adr;
value = UxCardMemIn(card->hw, m);
UxCardMemDetach(card->hw, b);
return value;
}
word mem_inw(ADAPTER *a, void *adr)
{
card_t *card = a->io;
unsigned char *b, *m;
word value;
m = b = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
m += (unsigned int) adr;
value = UxCardMemInW(card->hw, m);
UxCardMemDetach(card->hw, b);
return value;
}
void mem_in_buffer(ADAPTER *a, void *adr, void *P, word length)
{
card_t *card = a->io;
unsigned char *b, *m;
m = b = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
m += (unsigned int) adr;
UxCardMemInBuffer(card->hw, m, P, length);
UxCardMemDetach(card->hw, b);
return;
}
void mem_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e)
{
card_t *card = a->io;
unsigned char *b, *m;
m = b = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
m += (dword) &RBuffer->length;
card->RBuffer.length = UxCardMemInW(card->hw, m);
m = b;
m += (dword) &RBuffer->P;
UxCardMemInBuffer(card->hw, m, card->RBuffer.P, card->RBuffer.length);
e->RBuffer = (DBUFFER *) &card->RBuffer;
UxCardMemDetach(card->hw, b);
return;
}
void mem_out(ADAPTER *a, void *adr, byte data)
{
card_t *card = a->io;
unsigned char *b, *m;
m = b = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
m += (unsigned int) adr;
UxCardMemOut(card->hw, m, data);
UxCardMemDetach(card->hw, b);
return;
}
void mem_outw(ADAPTER *a, void *adr, word data)
{
card_t *card = a->io;
unsigned char *b, *m;
m = b = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
m += (unsigned int) adr;
UxCardMemOutW(card->hw, m, data);
UxCardMemDetach(card->hw, b);
return;
}
void mem_out_buffer(ADAPTER *a, void *adr, void *P, word length)
{
card_t *card = a->io;
unsigned char *b, *m;
m = b = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
m += (unsigned int) adr;
UxCardMemOutBuffer(card->hw, m, P, length);
UxCardMemDetach(card->hw, b);
return;
}
void mem_inc(ADAPTER *a, void *adr)
{
word value;
card_t *card = a->io;
unsigned char *b, *m;
m = b = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
m += (unsigned int) adr;
value = UxCardMemInW(card->hw, m);
value++;
UxCardMemOutW(card->hw, m, value);
UxCardMemDetach(card->hw, b);
return;
}
/*
* I/O routines for I/O mapped cards
*/
byte io_in(ADAPTER *a, void *adr)
{
card_t *card = a->io;
byte value;
byte *DivasIOBase = NULL;
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
value = UxCardIoIn(card->hw, DivasIOBase, adr);
UxCardMemDetach(card->hw, DivasIOBase);
return value;
}
word io_inw(ADAPTER *a, void *adr)
{
card_t *card = a->io;
word value;
byte *DivasIOBase = NULL;
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
value = UxCardIoInW(card->hw, DivasIOBase, adr);
UxCardMemDetach(card->hw, DivasIOBase);
return value;
}
void io_in_buffer(ADAPTER *a, void *adr, void *P, word length)
{
card_t *card = a->io;
byte *DivasIOBase = NULL;
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
UxCardIoInBuffer(card->hw, DivasIOBase, adr, P,length);
UxCardMemDetach(card->hw, DivasIOBase);
return;
}
void io_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e)
{
card_t *card = a->io;
byte *DivasIOBase = NULL;
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
card->RBuffer.length = UxCardIoInW(card->hw, DivasIOBase, (byte *) RBuffer);
UxCardIoInBuffer(card->hw, DivasIOBase, &RBuffer->P, card->RBuffer.P, card->RBuffer.length);
UxCardMemDetach(card->hw, DivasIOBase);
e->RBuffer = (DBUFFER *) &card->RBuffer;
return;
}
void io_out(ADAPTER *a, void *adr, byte data)
{
card_t *card = a->io;
byte *DivasIOBase = NULL;
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
UxCardIoOut(card->hw, DivasIOBase, adr, data);
UxCardMemDetach(card->hw, DivasIOBase);
return;
}
void io_outw(ADAPTER *a, void *adr, word data)
{
card_t *card = a->io;
byte *DivasIOBase = NULL;
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
UxCardIoOutW(card->hw, DivasIOBase, adr, data);
UxCardMemDetach(card->hw, DivasIOBase);
return;
}
void io_out_buffer(ADAPTER *a, void *adr, void *P, word length)
{
card_t *card = a->io;
byte *DivasIOBase = NULL;
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
UxCardIoOutBuffer(card->hw, DivasIOBase, adr, P, length);
UxCardMemDetach(card->hw, DivasIOBase);
return;
}
void io_inc(ADAPTER *a, void *adr)
{
word value;
card_t *card = a->io;
byte *DivasIOBase;
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
value = UxCardIoInW(card->hw, DivasIOBase, adr);
value++;
UxCardIoOutW(card->hw, DivasIOBase, adr, value);
UxCardMemDetach(card->hw, DivasIOBase);
return;
}
static
void test_int(card_t *card)
{
byte *shared, *DivasIOBase;
switch (card->test_int_pend)
{
case TEST_INT_DIVAS:
DPRINTF(("divas: test interrupt pending"));
shared = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
if (UxCardMemIn(card->hw, &shared[0x3FE]))
{
UxCardMemOut(card->hw,
&(((struct pr_ram *)shared)->RcOutput), 0);
UxCardMemDetach(card->hw, shared);
(*card->reset_int)(card);
shared = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
UxCardMemOut(card->hw, &shared[0x3FE], 0);
DPRINTF(("divas: test interrupt cleared"));
}
UxCardMemDetach(card->hw, shared);
card->test_int_pend = 0;
break;
case TEST_INT_DIVAS_BRI:
DPRINTF(("divas: BRI test interrupt pending"));
(*card->reset_int)(card);
DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
UxCardIoOutW(card->hw, DivasIOBase, (void *) 0x3FE, 0);
UxCardMemDetach(card->hw, DivasIOBase);
DPRINTF(("divas: test interrupt cleared"));
card->test_int_pend = 0;
break;
case TEST_INT_DIVAS_Q:
DPRINTF(("divas: 4BRI test interrupt pending"));
(*card->reset_int)(card);
card->test_int_pend = 0;
break;
default:
DPRINTF(("divas: unknown test interrupt pending"));
return;
}
return;
}
void card_isr (void *dev_id)
{
card_t *card = (card_t *) dev_id;
ADAPTER *a = &card->a;
int ipl;
if (card->test_int_pend)
{
ipl = UxCardLock(card->hw);
card->int_pend=0;
test_int(card);
UxCardUnlock(card->hw,ipl);
return;
}
if(card->card_isr)
{
(*(card->card_isr))(card);
}
else
{
ipl = UxCardLock(card->hw);
if ((card->test_int)(a))
{
(card->reset_int)(card);
}
UxCardUnlock(card->hw,ipl);
}
}
int DivasCardNew(dia_card_t *card_info)
{
card_t *card;
static boolean_t first_call = TRUE;
boolean_t NeedISRandReset = FALSE;
DPRINTF(("divas: new card "));
if (first_call)
{
first_call = FALSE;
init_idi_tab();
}
DivasConfigGet(card_info);
if (DivasCardNext == DIM(DivasCards))
{
KDPRINTF((KERN_WARNING "Divas: no space available for new card"));
return -1;
}
card = &DivasCards[DivasCardNext];
card->state = DIA_UNKNOWN;
card->cfg = *card_info;
card->a.io = card;
if (UxCardHandleGet(&card->hw, card_info))
{
KDPRINTF((KERN_WARNING "Divas: cannot get OS specific handle for card"));
return -1;
}
if (card_info->card_type == DIA_CARD_TYPE_DIVA_SERVER_B)
{
DivasBriPatch(card);
card_info->io_base = card->cfg.io_base;
}
switch (card_info->card_type)
{
case DIA_CARD_TYPE_DIVA_SERVER:
if (DivasPriInit(card, card_info))
{
return -1;
}
NeedISRandReset = TRUE;
break;
case DIA_CARD_TYPE_DIVA_SERVER_B:
if (DivasBriInit(card, card_info))
{
return -1;
}
NeedISRandReset = TRUE;
break;
case DIA_CARD_TYPE_DIVA_SERVER_Q:
if (Divas4BriInit(card, card_info))
{
return -1;
}
if (card_info->name[6] == '0')
{
NeedISRandReset = TRUE;
}
else // Need to set paramater for ISR anyway
{
card->hw->user_isr_arg = card;
card->hw->user_isr = card_isr;
}
break;
default:
KDPRINTF((KERN_WARNING "Divas: unsupported card type (%d)", card_info->card_type));
return -1;
}
if (NeedISRandReset)
{
if (UxIsrInstall(card->hw, card_isr, card))
{
KDPRINTF((KERN_WARNING "Divas: Install ISR failed (IRQ %d)", card->cfg.irq));
UxCardHandleFree(card->hw);
return -1;
}
if (card_info->card_type != DIA_CARD_TYPE_DIVA_SERVER_Q)
{
if ((*card->card_reset)(card))
{
KDPRINTF((KERN_WARNING "Divas: Adapter reset failed"));
return -1;
}
card->state = DIA_RESET;
}
NeedISRandReset = FALSE;
}
DivasCardNext++;
return 0;
}
void *get_card(int card_id)
{
int i;
for (i=0; i < DivasCardNext; i++)
{
if (DivasCards[i].cfg.card_id == card_id)
{
return(&DivasCards[i]);
}
}
DPRINTF(("divas: get_card() : no such card id (%d)", card_id));
return NULL;
}
int DivasCardConfig(dia_config_t *config)
{
card_t *card;
int status;
DPRINTF(("divas: configuring card"));
card = get_card(config->card_id);
if (!card)
{
return -1;
}
config = DivasConfig(card, config);
status = (*card->card_config)(card, config);
if (!status)
{
card->state = DIA_CONFIGURED;
}
return status;
}
int DivasCardLoad(dia_load_t *load)
{
card_t *card;
int status;
card = get_card(load->card_id);
if (!card)
{
return -1;
}
if (card->state == DIA_RUNNING)
{
(*card->card_reset)(card);
}
status = (*card->card_load)(card, load);
if (!status)
{
card->state = DIA_LOADED;
}
return status;
}
static int idi_register(card_t *card, byte channels)
{
DESCRIPTOR d[32];
int length, num_entities;
DPRINTF(("divas: registering card with IDI"));
num_entities = (channels > 2) ? MAX_PENTITIES : MAX_ENTITIES;
card->e_tbl = UxAlloc(sizeof(E_INFO) * num_entities);
if (!card->e_tbl)
{
KDPRINTF((KERN_WARNING "Divas: IDI register failed - no memory available"));
return -1;
}
memset(card->e_tbl, 0, sizeof(E_INFO) * num_entities);
card->e_max = num_entities;
DIVA_DIDD_Read(d, sizeof(d));
for(length=0; length < DIM(d); length++)
if (d[length].type == 0) break;
if (length >= DIM(d))
{
KDPRINTF((KERN_WARNING "Divas: IDI register failed - table full"));
return -1;
}
switch (card->cfg.card_type)
{
case DIA_CARD_TYPE_DIVA_SERVER:
d[length].type = IDI_ADAPTER_PR;
/* d[length].serial = card->serial_no; */
break;
case DIA_CARD_TYPE_DIVA_SERVER_B:
d[length].type = IDI_ADAPTER_MAESTRA;
/* d[length].serial = card->serial_no; */
break;
// 4BRI is treated as 4 BRI adapters
case DIA_CARD_TYPE_DIVA_SERVER_Q:
d[length].type = IDI_ADAPTER_MAESTRA;
/* d[length].serial = card->cfg.serial; */
}
d[length].features = 0;
d[length].features |= DI_FAX3|DI_MODEM|DI_POST|DI_V110|DI_V120;
if ( card->hw->features & PROTCAP_MANIF )
{
d[length].features |= DI_MANAGE ;
}
if ( card->hw->features & PROTCAP_V_42 )
{
d[length].features |= DI_V_42 ;
}
if ( card->hw->features & PROTCAP_EXTD_FAX )
{
d[length].features |= DI_EXTD_FAX ;
}
d[length].channels = channels;
d[length].request = DivasIdiRequest[card - DivasCards];
length++;
DIVA_DIDD_Write(d, sizeof(d));
return 0;
}
int DivasCardStart(int card_id)
{
card_t *card;
byte channels;
int status;
DPRINTF(("divas: starting card"));
card = get_card(card_id);
if (!card)
{
return -1;
}
status = (*card->card_start)(card, &channels);
if (status)
{
return status;
}
/* 4BRI == 4 x BRI so call idi_register 4 times each with 2 channels */
if (card->cfg.card_type == DIA_CARD_TYPE_DIVA_SERVER_Q)
{
int i;
card_t *FourBRISlave;
for (i=3; i >= 0; i--)
{
FourBRISlave = get_card(card_id - i); /* 0, 1, 2, 3 */
if (FourBRISlave)
{
idi_register(FourBRISlave, 2);
FourBRISlave->state = DIA_RUNNING;
}
}
card->serial_no = card->cfg.serial;
DPRINTF(("divas: card id %d (4BRI), serial no. 0x%x ready with %d channels",
card_id - 3, card->serial_no, (int) channels));
}
else
{
status = idi_register(card, channels);
if (!status)
{
card->state = DIA_RUNNING;
DPRINTF(("divas: card id %d, serial no. 0x%x ready with %d channels",
card_id, card->serial_no, (int) channels));
}
}
return status;
}
int DivasGetMem(mem_block_t *mem_block)
{
card_t *card;
word card_id = mem_block->card_id;
card = get_card(card_id);
if (!card)
{
return 0;
}
return (*card->card_mem_get)(card, mem_block);
}
/*
* Deleyed Procedure Call for handling interrupts from card
*/
void DivaDoCardDpc(card_t *card)
{
ADAPTER *a;
a = &card->a;
if(UxInterlockedIncrement(card->hw, &card->dpc_reentered) > 1)
{
return;
}
do{
if((*(card->test_int))(a))
{
(*(card->dpc))(a);
(*(card->clear_int))(a);
}
(*(card->out))(a);
}while(UxInterlockedDecrement(card->hw, &card->dpc_reentered));
}
void DivasDoDpc(unsigned long dummy)
{
card_t *card = DivasCards;
int i = DivasCardNext;
while(i--)
{
if (card->state == DIA_RUNNING)
DivaDoCardDpc(card);
card++;
}
}
/*
* DivasGetNum
* Returns the number of active adapters
*/
int DivasGetNum(void)
{
return(DivasCardNext);
}
/*
* DivasGetList
* Returns a list of active adapters
*/
int DivasGetList(dia_card_list_t *card_list)
{
int i;
memset(card_list, 0, sizeof(dia_card_list_t));
for(i = 0; i < DivasCardNext; i++)
{
card_list->card_type = DivasCards[i].cfg.card_type;
card_list->card_slot = DivasCards[i].cfg.slot;
card_list->state = DivasCards[i].state;
card_list++;
}
return 0;
}
/*
* control logging for specified card
*/
void DivasLog(dia_log_t *log)
{
card_t *card;
card = get_card(log->card_id);
if (!card)
{
return;
}
card->log_types = log->log_types;
return;
}
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.0
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
/*------------------------------------------------------------------*/
/* Q.931 information elements maximum length */
/* excluding the identifier, including the length field */
/*------------------------------------------------------------------*/
#define MAX_LEN_BC 13
#define MAX_LEN_LLC 19 /* ctr3 */
#define MAX_LEN_HLC 6 /* ctr3 */
#define MAX_LEN_UUI 200 /* Hicom USBS req */
#define MAX_LEN_NUM 24
#define MAX_LEN_DSP 83 /* ctr3 */
#define MAX_LEN_NI 4
#define MAX_LEN_PI 5
#define MAX_LEN_SIN 3
#define MAX_LEN_CST 4
#define MAX_LEN_SIG 2
#define MAX_LEN_SPID 32
#define MAX_LEN_EID 3
#define MAX_LEN_CHI 35 /* ctr3 */
#define MAX_LEN_CAU 33
#define MAX_LEN_FTY 130
#define MAX_LEN_KEY 83 /* ctr3 */
#define MAX_LEN_RSI 4
#define MAX_LEN_CAI 11
#define MAX_NUM_SPID 4
#define MAX_LEN_USERID 9
#define MAX_LEN_APPLID 5
#define MAX_LEN_NTTCIF 15
/*------------------------------------------------------------------*/
/* decision return values */
/*------------------------------------------------------------------*/
#define YES 1
#define NO 0
/*-------------------------------------------------------------------*/
/* w element coding */
/*-------------------------------------------------------------------*/
#define NTTCIF 0x01
#define BC 0x04
#define CAU 0x08
#define CAD 0x0c
#define CAI 0x10
#define CST 0x14
#define CHI 0x18
#define LLI 0x19
#define CHA 0x1a
#define FTY 0x1c
#define PI 0x1e
#define NFAC 0x20
#define TC 0x24
#define ATT_EID 0x26
#define NI 0x27
#define DSP 0x28
#define DT 0x29
#define KEY 0x2c
#define KP 0x2c
#define UID 0x2d
#define SIG 0x34
#define FI 0x39
#define SPID 0x3a
#define EID 0x3b
#define DSPF 0x3c
#define ECAD 0x4c
#define OAD 0x6c
#define OSA 0x6d
#define DAD 0x70
#define CPN 0x70
#define DSA 0x71
#define RDX 0x73
#define RAD 0x74
#define RDN 0x74
#define RSI 0x79
#define SCR 0x7A /* internal unscreened CPN */
#define MIE 0x7a /* internal management info element */
#define LLC 0x7c
#define HLC 0x7d
#define UUI 0x7e
#define ESC 0x7f
#define SHIFT 0x90
#define MORE 0xa0
#define CL 0xb0
/* information elements used on the spid interface */
#define SPID_CMD 0xc0
#define SPID_LINK 0x10
#define SPID_DN 0x70
#define SPID_BC 0x04
#define SPID_SWITCH 0x11
/*------------------------------------------------------------------*/
/* global configuration parameters, defined in exec.c */
/* these parameters are configured with program loading */
/*------------------------------------------------------------------*/
#define PROT_1TR6 0
#define PROT_ETSI 1
#define PROT_FRANC 2
#define PROT_BELG 3
#define PROT_SWED 4
#define PROT_NI 5
#define PROT_5ESS 6
#define PROT_JAPAN 7
#define PROT_ATEL 8
#define PROT_US 9
#define PROT_ITALY 10
#define PROT_TWAN 11
#define PROT_AUSTRAL 12
#define INIT_PROT_1TR6 0x80|PROT_1TR6
#define INIT_PROT_ETSI 0x80|PROT_ETSI
#define INIT_PROT_FRANC 0x80|PROT_FRANC
#define INIT_PROT_BELG 0x80|PROT_BELG
#define INIT_PROT_SWED 0x80|PROT_SWED
#define INIT_PROT_NI 0x80|PROT_NI
#define INIT_PROT_5ESS 0x80|PROT_5ESS
#define INIT_PROT_JAPAN 0x80|PROT_JAPAN
#define INIT_PROT_ATEL 0x80|PROT_ATEL
#define INIT_PROT_ITALY 0x80|PROT_ITALY
#define INIT_PROT_TWAN 0x80|PROT_TWAN
#define INIT_PROT_AUSTRAL 0x80|PROT_AUSTRAL
/* -----------------------------------------------------------**
** The PROTOCOL_FEATURE_STRING in feature.h (included **
** in prstart.sx and astart.sx) defines capabilities and **
** features of the actual protocol code. It's used as a bit **
** mask. **
** The following Bits are defined: **
** -----------------------------------------------------------*/
#define PROTCAP_TELINDUS 0x0001 /* Telindus Variant of protocol code */
#define PROTCAP_MANIF 0x0002 /* Management interface implemented */
#define PROTCAP_V_42 0x0004 /* V42 implemented */
#define PROTCAP_V90D 0x0008 /* V.90D (implies up to 384k DSP code) */
#define PROTCAP_EXTD_FAX 0x0010 /* Extended FAX (ECM, 2D, T6, Polling) */
#define PROTCAP_FREE4 0x0020 /* not used */
#define PROTCAP_FREE5 0x0040 /* not used */
#define PROTCAP_FREE6 0x0080 /* not used */
#define PROTCAP_FREE7 0x0100 /* not used */
#define PROTCAP_FREE8 0x0200 /* not used */
#define PROTCAP_FREE9 0x0400 /* not used */
#define PROTCAP_FREE10 0x0800 /* not used */
#define PROTCAP_FREE11 0x1000 /* not used */
#define PROTCAP_FREE12 0x2000 /* not used */
#define PROTCAP_FREE13 0x4000 /* not used */
#define PROTCAP_EXTENSION 0x8000 /* used for future extentions */
/*
* Include file for defining the kernel logger messages
* These definitions are shared between the klog driver and the
* klogd daemon process
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.0
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#if !defined(_KLOGMSG_H)
#define _KLOGMSG_H
/* define a type for a log entry */
#define KLOG_TEXT_MSG (0)
#define KLOG_XLOG_MSG (1)
#define KLOG_XTXT_MSG (2)
#define KLOG_IDI_REQ (4)
#define KLOG_IDI_CALLBACK (5)
#define KLOG_CAPI_MSG (6)
typedef struct
{
unsigned long time_stamp; /* in ms since last system boot */
int card; /* card number (-1 for all) */
unsigned int type; /* type of log message (0 is text) */
unsigned int length; /* message length (non-text messages only) */
unsigned short code; /* message code (non-text messages only) */
char buffer[110];/* text/data to log */
} klog_t;
void DivasLogAdd(void *buffer, int length);
#endif /* of _KLOGMSG_H */
/*
* External Diva Server driver include file
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.5
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#if !defined(DIVAS_H)
#define DIVAS_H
#include "sys.h"
/* IOCTL commands */
#define DIA_IOCTL_INIT (0)
#define DIA_IOCTL_LOAD (1)
#define DIA_IOCTL_CONFIG (2)
#define DIA_IOCTL_START (3)
#define DIA_IOCTL_GET_NUM (4)
#define DIA_IOCTL_GET_LIST (5)
#define DIA_IOCTL_LOG (6)
#define DIA_IOCTL_DETECT (7)
#define DIA_IOCTL_SPACE (8)
#define DIA_IOCTL_GET_MEM (9)
#define DIA_IOCTL_FLAVOUR (10)
#define DIA_IOCTL_XLOG_REQ (11)
/* Error codes */
#define XLOG_ERR_CARD_NUM (13)
#define XLOG_ERR_DONE (14)
#define XLOG_ERR_CMD (15)
#define XLOG_ERR_TIMEOUT (16)
#define XLOG_ERR_CARD_STATE (17)
#define XLOG_ERR_UNKNOWN (18)
#define XLOG_OK (0)
/* Adapter states */
#define DIA_UNKNOWN (0)
#define DIA_RESET (1)
#define DIA_LOADED (2)
#define DIA_CONFIGURED (3)
#define DIA_RUNNING (4)
/* Stucture for getting card specific information from active cad driver */
typedef struct
{
int card_type;
int card_slot;
int state;
} dia_card_list_t;
/* use following to select which logging to have active */
#define DIVAS_LOG_DEBUG (1 << 0)
#define DIVAS_LOG_XLOG (1 << 1)
#define DIVAS_LOG_IDI (1 << 2)
#define DIVAS_LOG_CAPI (1 << 3)
/* stucture for DIA_IOCTL_LOG to get information from adapter */
typedef struct
{
int card_id;
int log_types; /* bit mask of log types: use DIVAS_LOG_XXX */
} dia_log_t;
/* list of cards supported by this driver */
#define DIA_CARD_TYPE_DIVA_SERVER (0) /* Diva Server PRI */
#define DIA_CARD_TYPE_DIVA_SERVER_B (1) /* Diva Server BRI */
#define DIA_CARD_TYPE_DIVA_SERVER_Q (2) /* Diva Server 4-BRI */
/* bus types */
#define DIA_BUS_TYPE_ISA (0)
#define DIA_BUS_TYPE_ISA_PNP (1)
#define DIA_BUS_TYPE_PCI (2)
#define DIA_BUS_TYPE_MCA (3)
/* types of memory used (index for memory array below) */
#define DIVAS_RAM_MEMORY 0
#define DIVAS_REG_MEMORY 1
#define DIVAS_CFG_MEMORY 2
#define DIVAS_SHARED_MEMORY 3
#define DIVAS_CTL_MEMORY 4
/*
* card config information
* passed as parameter to DIA_IOCTL_INIT ioctl to initialise new card
*/
typedef struct
{
int card_id; /* unique id assigned to this card */
int card_type; /* use DIA_CARD_TYPE_xxx above */
int bus_type; /* use DIA_BUS_TYPE_xxx above */
struct pci_dev *pdev;
int slot; /* slot number in bus */
unsigned char irq; /* IRQ number */
int reset_base; /* Reset register for I/O mapped cards */
int io_base; /* I/O base for I/O mapped cards */
void *memory[5]; /* memory base addresses for memory mapped cards */
char name[9]; /* name of adapter */
int serial; /* serial number */
unsigned char int_priority; /* Interrupt priority */
} dia_card_t;
/*
* protocol configuration information
* passed as parameter to DIA_IOCTL_CONFIG ioctl to configure card
*/
typedef struct
{
int card_id; /* to identify particular card */
unsigned char tei;
unsigned char nt2;
unsigned char watchdog;
unsigned char permanent;
unsigned char x_interface;
unsigned char stable_l2;
unsigned char no_order_check;
unsigned char handset_type;
unsigned char sig_flags;
unsigned char low_channel;
unsigned char prot_version;
unsigned char crc4;
struct
{
unsigned char oad[32];
unsigned char osa[32];
unsigned char spid[32];
}terminal[2];
} dia_config_t;
/*
* code configuration
* passed as parameter to DIA_IOCTL_LOAD ioctl
* one of these ioctl per code file to load
*/
typedef struct
{
int card_id; /* card to load */
enum
{
DIA_CPU_CODE, /* CPU code */
DIA_DSP_CODE, /* DSP code */
DIA_CONT_CODE, /* continuation of code */
DIA_TABLE_CODE, /* code table */
DIA_DLOAD_CNT, /* number of downloads*/
DIA_FPGA_CODE
} code_type; /* code for CPU or DSP ? */
int length; /* length of code */
unsigned char *code; /* pointer (in user-space) to code */
} dia_load_t;
/*
* start configuration
* passed as parameter to DIA_IOCTL_START ioctl
*/
typedef struct
{
int card_id; /* card to start */
} dia_start_t;
/* used for retrieving memory from the card */
typedef struct {
word card_id;
dword addr;
byte data[16 * 8];
} mem_block_t;
/* DIVA Server specific addresses */
#define DIVAS_CPU_START_ADDR (0x0)
#define ORG_MAX_PROTOCOL_CODE_SIZE 0x000A0000
#define ORG_MAX_DSP_CODE_SIZE (0x000F0000 - ORG_MAX_PROTOCOL_CODE_SIZE)
#define ORG_DSP_CODE_BASE (0xBF7F0000 - ORG_MAX_DSP_CODE_SIZE)
#define DIVAS_DSP_START_ADDR (0xBF7A0000)
#define DIVAS_SHARED_OFFSET (0x1000)
#define MP_DSP_CODE_BASE 0xa03a0000
#define MQ_PROTCODE_OFFSET 0x100000
#define MQ_SM_OFFSET 0X0f0000
#define V90D_MAX_PROTOCOL_CODE_SIZE 0x00090000
#define V90D_MAX_DSP_CODE_SIZE (0x000F0000 - V90D_MAX_PROTOCOL_CODE_SIZE)
#define V90D_DSP_CODE_BASE (0xBF7F0000 - V90D_MAX_DSP_CODE_SIZE)
#define MQ_ORG_MAX_PROTOCOL_CODE_SIZE 0x000a0000 /* max 640K Protocol-Code */
#define MQ_ORG_MAX_DSP_CODE_SIZE 0x00050000 /* max 320K DSP-Code */
#define MQ_ORG_DSP_CODE_BASE (MQ_MAX_DSP_DOWNLOAD_ADDR \
- MQ_ORG_MAX_DSP_CODE_SIZE)
#define MQ_V90D_MAX_PROTOCOL_CODE_SIZE 0x00090000 /* max 576K Protocol-Code */
#define MQ_V90D_MAX_DSP_CODE_SIZE 0x00060000 /* max 384K DSP-Code if V.90D included */
#define MQ_MAX_DSP_DOWNLOAD_ADDR 0xa03f0000
#define MQ_V90D_DSP_CODE_BASE (MQ_MAX_DSP_DOWNLOAD_ADDR \
- MQ_V90D_MAX_DSP_CODE_SIZE)
#define ALIGNMENT_MASK_MAESTRA 0xfffffffc
#endif /* DIVAS_H */
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.0
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef DSP_DEFS_H_
#define DSP_DEFS_H_
#ifndef DSPDIDS_H_
#include "dspdids.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*---------------------------------------------------------------------------*/
#ifndef NULL
#define NULL 0
#endif
#ifndef TRUE
#define TRUE (0 == 0)
#endif
#ifndef FALSE
#define FALSE (0 != 0)
#endif
/*---------------------------------------------------------------------------*/
#define DSP_MEMORY_TYPE_EXTERNAL_DM 0
#define DSP_MEMORY_TYPE_EXTERNAL_PM 1
#define DSP_MEMORY_TYPE_INTERNAL_DM 2
#define DSP_MEMORY_TYPE_INTERNAL_PM 3
#define DSP_DOWNLOAD_FLAG_BOOTABLE 0x0001
#define DSP_DOWNLOAD_FLAG_2181 0x0002
#define DSP_DOWNLOAD_FLAG_TIMECRITICAL 0x0004
#define DSP_DOWNLOAD_FLAG_COMPAND 0x0008
#define DSP_MEMORY_BLOCK_COUNT 16
#define DSP_SEGMENT_PM_FLAG 0x0001
#define DSP_SEGMENT_SHARED_FLAG 0x0002
#define DSP_SEGMENT_EXTERNAL_DM DSP_MEMORY_TYPE_EXTERNAL_DM
#define DSP_SEGMENT_EXTERNAL_PM DSP_MEMORY_TYPE_EXTERNAL_PM
#define DSP_SEGMENT_INTERNAL_DM DSP_MEMORY_TYPE_INTERNAL_DM
#define DSP_SEGMENT_INTERNAL_PM DSP_MEMORY_TYPE_INTERNAL_PM
#define DSP_SEGMENT_FIRST_RELOCATABLE 4
#define DSP_DATA_BLOCK_PM_FLAG 0x0001
#define DSP_DATA_BLOCK_DWORD_FLAG 0x0002
#define DSP_DATA_BLOCK_RESOLVE_FLAG 0x0004
#define DSP_RELOC_NONE 0x00
#define DSP_RELOC_SEGMENT_MASK 0x3f
#define DSP_RELOC_TYPE_MASK 0xc0
#define DSP_RELOC_TYPE_0 0x00 /* relocation of address in DM word / high part of PM word */
#define DSP_RELOC_TYPE_1 0x40 /* relocation of address in low part of PM data word */
#define DSP_RELOC_TYPE_2 0x80 /* relocation of address in standard command */
#define DSP_RELOC_TYPE_3 0xc0 /* relocation of address in call/jump on flag in */
#define DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE 48
#define DSP_COMBIFILE_FORMAT_VERSION_BCD 0x0100
#define DSP_FILE_FORMAT_IDENTIFICATION_SIZE 48
#define DSP_FILE_FORMAT_VERSION_BCD 0x0100
typedef struct tag_dsp_combifile_header
{
char format_identification[DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE];
word format_version_bcd;
word header_size;
word combifile_description_size;
word directory_entries;
word directory_size;
word download_count;
word usage_mask_size;
} t_dsp_combifile_header;
typedef struct tag_dsp_combifile_directory_entry
{
word card_type_number;
word file_set_number;
} t_dsp_combifile_directory_entry;
typedef struct tag_dsp_file_header
{
char format_identification[DSP_FILE_FORMAT_IDENTIFICATION_SIZE];
word format_version_bcd;
word download_id;
word download_flags;
word required_processing_power;
word interface_channel_count;
word header_size;
word download_description_size;
word memory_block_table_size;
word memory_block_count;
word segment_table_size;
word segment_count;
word symbol_table_size;
word symbol_count;
word total_data_size_dm;
word data_block_count_dm;
word total_data_size_pm;
word data_block_count_pm;
} t_dsp_file_header;
typedef struct tag_dsp_memory_block_desc
{
word alias_memory_block;
word memory_type;
word address;
word size; /* DSP words */
} t_dsp_memory_block_desc;
typedef struct tag_dsp_segment_desc
{
word memory_block;
word attributes;
word base;
word size;
word alignment; /* ==0 -> no other legal start address than base */
} t_dsp_segment_desc;
typedef struct tag_dsp_symbol_desc
{
word symbol_id;
word segment;
word offset;
word size; /* DSP words */
} t_dsp_symbol_desc;
typedef struct tag_dsp_data_block_header
{
word attributes;
word segment;
word offset;
word size; /* DSP words */
} t_dsp_data_block_header;
typedef struct tag_dsp_download_desc /* be sure to keep native alignment for MAESTRA's */
{
word download_id;
word download_flags;
word required_processing_power;
word interface_channel_count;
word excess_header_size;
word memory_block_count;
word segment_count;
word symbol_count;
word data_block_count_dm;
word data_block_count_pm;
byte *p_excess_header_data;
char *p_download_description;
t_dsp_memory_block_desc *p_memory_block_table;
t_dsp_segment_desc *p_segment_table;
t_dsp_symbol_desc *p_symbol_table;
word *p_data_blocks_dm;
word *p_data_blocks_pm;
} t_dsp_download_desc;
#define DSP_DOWNLOAD_INDEX_KERNEL 0
#define DSP30TX_DOWNLOAD_INDEX_KERNEL 1
#define DSP30RX_DOWNLOAD_INDEX_KERNEL 2
#define DSP_MAX_DOWNLOAD_COUNT 35
#define DSP_DOWNLOAD_MAX_SEGMENTS 16
#define DSP_UDATA_REQUEST_RECONFIGURE 0
/*
parameters:
<word> reconfigure delay (in 8kHz samples)
<word> reconfigure code
<byte> reconfigure hdlc preamble flags
*/
#define DSP_RECONFIGURE_TX_FLAG 0x8000
#define DSP_RECONFIGURE_SHORT_TRAIN_FLAG 0x4000
#define DSP_RECONFIGURE_ECHO_PROTECT_FLAG 0x2000
#define DSP_RECONFIGURE_HDLC_FLAG 0x1000
#define DSP_RECONFIGURE_SYNC_FLAG 0x0800
#define DSP_RECONFIGURE_PROTOCOL_MASK 0x00ff
#define DSP_RECONFIGURE_IDLE 0
#define DSP_RECONFIGURE_V25 1
#define DSP_RECONFIGURE_V21_CH2 2
#define DSP_RECONFIGURE_V27_2400 3
#define DSP_RECONFIGURE_V27_4800 4
#define DSP_RECONFIGURE_V29_7200 5
#define DSP_RECONFIGURE_V29_9600 6
#define DSP_RECONFIGURE_V33_12000 7
#define DSP_RECONFIGURE_V33_14400 8
#define DSP_RECONFIGURE_V17_7200 9
#define DSP_RECONFIGURE_V17_9600 10
#define DSP_RECONFIGURE_V17_12000 11
#define DSP_RECONFIGURE_V17_14400 12
/*
data indications if transparent framer
<byte> data 0
<byte> data 1
...
data indications if HDLC framer
<byte> data 0
<byte> data 1
...
<byte> CRC 0
<byte> CRC 1
<byte> preamble flags
*/
#define DSP_UDATA_INDICATION_SYNC 0
/*
returns:
<word> time of sync (sampled from counter at 8kHz)
*/
#define DSP_UDATA_INDICATION_DCD_OFF 1
/*
returns:
<word> time of DCD off (sampled from counter at 8kHz)
*/
#define DSP_UDATA_INDICATION_DCD_ON 2
/*
returns:
<word> time of DCD on (sampled from counter at 8kHz)
<byte> connected norm
<word> connected options
<dword> connected speed (bit/s)
*/
#define DSP_UDATA_INDICATION_CTS_OFF 3
/*
returns:
<word> time of CTS off (sampled from counter at 8kHz)
*/
#define DSP_UDATA_INDICATION_CTS_ON 4
/*
returns:
<word> time of CTS on (sampled from counter at 8kHz)
<byte> connected norm
<word> connected options
<dword> connected speed (bit/s)
*/
#define DSP_CONNECTED_NORM_UNSPECIFIED 0
#define DSP_CONNECTED_NORM_V21 1
#define DSP_CONNECTED_NORM_V23 2
#define DSP_CONNECTED_NORM_V22 3
#define DSP_CONNECTED_NORM_V22_BIS 4
#define DSP_CONNECTED_NORM_V32_BIS 5
#define DSP_CONNECTED_NORM_V34 6
#define DSP_CONNECTED_NORM_V8 7
#define DSP_CONNECTED_NORM_BELL_212A 8
#define DSP_CONNECTED_NORM_BELL_103 9
#define DSP_CONNECTED_NORM_V29_LEASED_LINE 10
#define DSP_CONNECTED_NORM_V33_LEASED_LINE 11
#define DSP_CONNECTED_NORM_TFAST 12
#define DSP_CONNECTED_NORM_V21_CH2 13
#define DSP_CONNECTED_NORM_V27_TER 14
#define DSP_CONNECTED_NORM_V29 15
#define DSP_CONNECTED_NORM_V33 16
#define DSP_CONNECTED_NORM_V17 17
#define DSP_CONNECTED_OPTION_TRELLIS 0x0001
/*---------------------------------------------------------------------------*/
#ifdef __cplusplus
}
#endif
#endif
/*---------------------------------------------------------------------------*/
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.0
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef DSPDIDS_H_
#define DSPDIDS_H_
/*---------------------------------------------------------------------------*/
#define DSP_DID_INVALID 0
#define DSP_DID_DIVA 1
#define DSP_DID_DIVA_PRO 2
#define DSP_DID_DIVA_PRO_20 3
#define DSP_DID_DIVA_PRO_PCCARD 4
#define DSP_DID_DIVA_SERVER_BRI_1M 5
#define DSP_DID_DIVA_SERVER_BRI_2M 6
#define DSP_DID_DIVA_SERVER_PRI_2M_TX 7
#define DSP_DID_DIVA_SERVER_PRI_2M_RX 8
#define DSP_DID_DIVA_SERVER_PRI_30M 9
#define DSP_DID_TASK_HSCX 100
#define DSP_DID_TASK_HSCX_PRI_2M_TX 101
#define DSP_DID_TASK_HSCX_PRI_2M_RX 102
#define DSP_DID_TASK_V110KRNL 200
#define DSP_DID_OVERLAY_V1100 201
#define DSP_DID_OVERLAY_V1101 202
#define DSP_DID_OVERLAY_V1102 203
#define DSP_DID_OVERLAY_V1103 204
#define DSP_DID_OVERLAY_V1104 205
#define DSP_DID_OVERLAY_V1105 206
#define DSP_DID_OVERLAY_V1106 207
#define DSP_DID_OVERLAY_V1107 208
#define DSP_DID_OVERLAY_V1108 209
#define DSP_DID_OVERLAY_V1109 210
#define DSP_DID_TASK_V110_PRI_2M_TX 220
#define DSP_DID_TASK_V110_PRI_2M_RX 221
#define DSP_DID_TASK_MODEM 300
#define DSP_DID_TASK_FAX05 400
#define DSP_DID_TASK_VOICE 500
#define DSP_DID_TASK_TIKRNL81 600
#define DSP_DID_OVERLAY_DIAL 601
#define DSP_DID_OVERLAY_V22 602
#define DSP_DID_OVERLAY_V32 603
#define DSP_DID_OVERLAY_FSK 604
#define DSP_DID_OVERLAY_FAX 605
#define DSP_DID_OVERLAY_VXX 606
#define DSP_DID_OVERLAY_V8 607
#define DSP_DID_OVERLAY_INFO 608
#define DSP_DID_OVERLAY_V34 609
#define DSP_DID_OVERLAY_DFX 610
#define DSP_DID_PARTIAL_OVERLAY_DIAL 611
#define DSP_DID_PARTIAL_OVERLAY_FSK 612
#define DSP_DID_PARTIAL_OVERLAY_FAX 613
#define DSP_DID_TASK_TIKRNL05 700
/*---------------------------------------------------------------------------*/
#endif
/*---------------------------------------------------------------------------*/
/* $Id: eicon.h,v 1.1.4.1.2.3 2002/10/01 11:29:13 armin Exp $
*
* ISDN low-level module for Eicon active ISDN-Cards.
*
* Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
* Copyright 1998-2000 by Armin Schindler (mac@melware.de)
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef eicon_h
#define eicon_h
#include <linux/interrupt.h>
#define EICON_IOCTL_SETMMIO 0
#define EICON_IOCTL_GETMMIO 1
#define EICON_IOCTL_SETIRQ 2
#define EICON_IOCTL_GETIRQ 3
#define EICON_IOCTL_LOADBOOT 4
#define EICON_IOCTL_ADDCARD 5
#define EICON_IOCTL_GETTYPE 6
#define EICON_IOCTL_LOADPCI 7
#define EICON_IOCTL_LOADISA 8
#define EICON_IOCTL_GETVER 9
#define EICON_IOCTL_GETXLOG 10
#define EICON_IOCTL_MANIF 90
#define EICON_IOCTL_FREEIT 97
#define EICON_IOCTL_TEST 98
#define EICON_IOCTL_DEBUGVAR 99
#define EICON_IOCTL_DIA_OFFSET 100
/* Bus types */
#define EICON_BUS_ISA 1
#define EICON_BUS_MCA 2
#define EICON_BUS_PCI 3
/* Constants for describing Card-Type */
#define EICON_CTYPE_S 0
#define EICON_CTYPE_SX 1
#define EICON_CTYPE_SCOM 2
#define EICON_CTYPE_QUADRO 3
#define EICON_CTYPE_S2M 4
#define EICON_CTYPE_MAESTRA 5
#define EICON_CTYPE_MAESTRAQ 6
#define EICON_CTYPE_MAESTRAQ_U 7
#define EICON_CTYPE_MAESTRAP 8
#define EICON_CTYPE_ISABRI 0x10
#define EICON_CTYPE_ISAPRI 0x20
#define EICON_CTYPE_MASK 0x0f
#define EICON_CTYPE_QUADRO_NR(n) (n<<4)
#define MAX_HEADER_LEN 10
#define MAX_STATUS_BUFFER 150
/* Struct for adding new cards */
typedef struct eicon_cdef {
int membase;
int irq;
char id[10];
} eicon_cdef;
#define EICON_ISA_BOOT_MEMCHK 1
#define EICON_ISA_BOOT_NORMAL 2
/* Struct for downloading protocol via ioctl for ISA cards */
/* same struct for downloading protocol via ioctl for MCA cards */
typedef struct {
/* start-up parameters */
unsigned char tei;
unsigned char nt2;
unsigned char skip1;
unsigned char WatchDog;
unsigned char Permanent;
unsigned char XInterface;
unsigned char StableL2;
unsigned char NoOrderCheck;
unsigned char HandsetType;
unsigned char skip2;
unsigned char LowChannel;
unsigned char ProtVersion;
unsigned char Crc4;
unsigned char Loopback;
unsigned char oad[32];
unsigned char osa[32];
unsigned char spid[32];
unsigned char boot_opt;
unsigned long bootstrap_len;
unsigned long firmware_len;
unsigned char code[1]; /* Rest (bootstrap- and firmware code) will be allocated */
} eicon_isa_codebuf;
/* Data for downloading protocol via ioctl */
typedef union {
eicon_isa_codebuf isa;
eicon_isa_codebuf mca;
} eicon_codebuf;
/* Data for Management interface */
typedef struct {
int count;
int pos;
int length[50];
unsigned char data[700];
} eicon_manifbuf;
#define TRACE_OK (1)
#ifdef __KERNEL__
/* Kernel includes */
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <asm/io.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/ioport.h>
#include <linux/timer.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/pci.h>
#include <linux/isdn.h>
#include <linux/isdnif.h>
typedef struct {
__u16 length __attribute__ ((packed)); /* length of data/parameter field */
__u8 P[1]; /* data/parameter field */
} eicon_PBUFFER;
#include "eicon_isa.h"
#include "idi.h"
typedef struct {
__u16 NextReq __attribute__ ((packed)); /* pointer to next Req Buffer */
__u16 NextRc __attribute__ ((packed)); /* pointer to next Rc Buffer */
__u16 NextInd __attribute__ ((packed)); /* pointer to next Ind Buffer */
__u8 ReqInput __attribute__ ((packed)); /* number of Req Buffers sent */
__u8 ReqOutput __attribute__ ((packed)); /* number of Req Buffers returned */
__u8 ReqReserved __attribute__ ((packed));/*number of Req Buffers reserved */
__u8 Int __attribute__ ((packed)); /* ISDN-P interrupt */
__u8 XLock __attribute__ ((packed)); /* Lock field for arbitration */
__u8 RcOutput __attribute__ ((packed)); /* number of Rc buffers received */
__u8 IndOutput __attribute__ ((packed)); /* number of Ind buffers received */
__u8 IMask __attribute__ ((packed)); /* Interrupt Mask Flag */
__u8 Reserved1[2] __attribute__ ((packed)); /* reserved field, do not use */
__u8 ReadyInt __attribute__ ((packed)); /* request field for ready int */
__u8 Reserved2[12] __attribute__ ((packed)); /* reserved field, do not use */
__u8 InterfaceType __attribute__ ((packed)); /* interface type 1=16K */
__u16 Signature __attribute__ ((packed)); /* ISDN-P initialized ind */
__u8 B[1]; /* buffer space for Req,Ind and Rc */
} eicon_pr_ram;
/* Macro for delay via schedule() */
#define SLEEP(j) { \
set_current_state(TASK_UNINTERRUPTIBLE); \
schedule_timeout(j); \
}
typedef struct {
__u8 Req; /* pending request */
__u8 Rc; /* return code received */
__u8 Ind; /* indication received */
__u8 ReqCh; /* channel of current Req */
__u8 RcCh; /* channel of current Rc */
__u8 IndCh; /* channel of current Ind */
__u8 D3Id; /* ID used by this entity */
__u8 B2Id; /* ID used by this entity */
__u8 GlobalId; /* reserved field */
__u8 XNum; /* number of X-buffers */
__u8 RNum; /* number of R-buffers */
struct sk_buff_head X; /* X-buffer queue */
struct sk_buff_head R; /* R-buffer queue */
__u8 RNR; /* receive not ready flag */
__u8 complete; /* receive complete status */
__u8 busy; /* busy flag */
__u16 ref; /* saved reference */
} entity;
#define FAX_MAX_SCANLINE 256
typedef struct {
__u8 PrevObject;
__u8 NextObject;
__u8 abLine[FAX_MAX_SCANLINE];
__u8 abFrame[FAX_MAX_SCANLINE];
unsigned int LineLen;
unsigned int LineDataLen;
__u32 LineData;
unsigned int NullBytesPos;
__u8 NullByteExist;
int PageCount;
__u8 Dle;
__u8 Eop;
} eicon_ch_fax_buf;
typedef struct {
int No; /* Channel Number */
unsigned short fsm_state; /* Current D-Channel state */
unsigned short statectrl; /* State controling bits */
unsigned short eazmask; /* EAZ-Mask for this Channel */
int queued; /* User-Data Bytes in TX queue */
int pqueued; /* User-Data Packets in TX queue */
int waitq; /* User-Data Bytes in wait queue */
int waitpq; /* User-Data Bytes in packet queue */
struct sk_buff *tskb1; /* temp skb 1 */
struct sk_buff *tskb2; /* temp skb 2 */
unsigned char l2prot; /* Layer 2 protocol */
unsigned char l3prot; /* Layer 3 protocol */
#ifdef CONFIG_ISDN_TTY_FAX
T30_s *fax; /* pointer to fax data in LL */
eicon_ch_fax_buf fax2; /* fax related struct */
#endif
entity e; /* Native Entity */
ENTITY de; /* Divas D Entity */
ENTITY be; /* Divas B Entity */
char cpn[32]; /* remember cpn */
char oad[32]; /* remember oad */
char dsa[32]; /* remember dsa */
char osa[32]; /* remember osa */
unsigned char cause[2]; /* Last Cause */
unsigned char si1;
unsigned char si2;
unsigned char plan;
unsigned char screen;
} eicon_chan;
typedef struct {
eicon_chan *ptr;
} eicon_chan_ptr;
#include "eicon_pci.h"
#define EICON_FLAGS_RUNNING 1 /* Cards driver activated */
#define EICON_FLAGS_PVALID 2 /* Cards port is valid */
#define EICON_FLAGS_IVALID 4 /* Cards irq is valid */
#define EICON_FLAGS_MVALID 8 /* Cards membase is valid */
#define EICON_FLAGS_LOADED 8 /* Firmware loaded */
/* D-Channel states */
#define EICON_STATE_NULL 0
#define EICON_STATE_ICALL 1
#define EICON_STATE_OCALL 2
#define EICON_STATE_IWAIT 3
#define EICON_STATE_OWAIT 4
#define EICON_STATE_IBWAIT 5
#define EICON_STATE_OBWAIT 6
#define EICON_STATE_BWAIT 7
#define EICON_STATE_BHWAIT 8
#define EICON_STATE_BHWAIT2 9
#define EICON_STATE_DHWAIT 10
#define EICON_STATE_DHWAIT2 11
#define EICON_STATE_BSETUP 12
#define EICON_STATE_ACTIVE 13
#define EICON_STATE_ICALLW 14
#define EICON_STATE_LISTEN 15
#define EICON_STATE_WMCONN 16
#define EICON_MAX_QUEUE 2138
typedef union {
eicon_isa_card isa;
eicon_pci_card pci;
eicon_isa_card mca;
} eicon_hwif;
typedef struct {
__u8 ret;
__u8 id;
__u8 ch;
} eicon_ack;
typedef struct {
__u8 code;
__u8 id;
__u8 ch;
} eicon_req;
typedef struct {
__u8 ret;
__u8 id;
__u8 ch;
__u8 more;
} eicon_indhdr;
/*
* Per card driver data
*/
typedef struct eicon_card {
eicon_hwif hwif; /* Hardware dependent interface */
DESCRIPTOR *d; /* IDI Descriptor */
u_char ptype; /* Protocol type (1TR6 or Euro) */
u_char bus; /* Bustype (ISA, MCA, PCI) */
u_char type; /* Cardtype (EICON_CTYPE_...) */
struct eicon_card *qnext; /* Pointer to next quadro adapter */
int Feature; /* Protocol Feature Value */
struct eicon_card *next; /* Pointer to next device struct */
int myid; /* Driver-Nr. assigned by linklevel */
unsigned long flags; /* Statusflags */
struct sk_buff_head rcvq; /* Receive-Message queue */
struct sk_buff_head sndq; /* Send-Message queue */
struct sk_buff_head rackq; /* Req-Ack-Message queue */
struct sk_buff_head sackq; /* Data-Ack-Message queue */
struct sk_buff_head statq; /* Status-Message queue */
int statq_entries;
struct tasklet_struct snd_tq; /* Task struct for xmit bh */
struct tasklet_struct rcv_tq; /* Task struct for rcv bh */
struct tasklet_struct ack_tq; /* Task struct for ack bh */
eicon_chan* IdTable[256]; /* Table to find entity */
__u16 ref_in;
__u16 ref_out;
int nchannels; /* Number of B-Channels */
int ReadyInt; /* Ready Interrupt */
eicon_chan *bch; /* B-Channel status/control */
DBUFFER *dbuf; /* Dbuffer for Diva Server */
BUFFERS *sbuf; /* Buffer for Diva Server */
char *sbufp; /* Data Buffer for Diva Server */
isdn_if interface; /* Interface to upper layer */
char regname[35]; /* Drivers card name */
#ifdef CONFIG_MCA
int mca_slot; /* # of cards MCA slot */
int mca_io; /* MCA cards IO port */
#endif /* CONFIG_MCA */
} eicon_card;
#include "eicon_idi.h"
extern eicon_card *cards;
extern char *eicon_ctype_name[];
extern __inline__ void eicon_schedule_tx(eicon_card *card)
{
tasklet_schedule(&card->snd_tq);
}
extern __inline__ void eicon_schedule_rx(eicon_card *card)
{
tasklet_schedule(&card->rcv_tq);
}
extern __inline__ void eicon_schedule_ack(eicon_card *card)
{
tasklet_schedule(&card->ack_tq);
}
extern int eicon_addcard(int, int, int, char *, int);
extern void eicon_io_transmit(eicon_card *card);
extern irqreturn_t eicon_irq(int irq, void *dev_id, struct pt_regs *regs);
extern void eicon_io_rcv_dispatch(eicon_card *ccard);
extern void eicon_io_ack_dispatch(eicon_card *ccard);
#ifdef CONFIG_MCA
extern int eicon_mca_find_card(int, int, int, char *);
extern int eicon_mca_probe(int, int, int, int, char *);
extern int eicon_info(char *, int , void *);
#endif /* CONFIG_MCA */
extern ulong DebugVar;
extern void eicon_log(eicon_card * card, int level, const char *fmt, ...);
extern void eicon_putstatus(eicon_card * card, char * buf);
extern spinlock_t eicon_lock;
#endif /* __KERNEL__ */
#endif /* eicon_h */
/* $Id: eicon_dsp.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $
*
* ISDN lowlevel-module for Eicon active cards.
* DSP definitions
*
* Copyright 1999,2000 by Armin Schindler (mac@melware.de)
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef DSP_H
#define DSP_H
#include "dsp_defs.h"
#define DSP_UDATA_REQUEST_SWITCH_FRAMER 1
/*
parameters:
<byte> transmit framer type
<byte> receive framer type
*/
#define DSP_REQUEST_SWITCH_FRAMER_HDLC 0
#define DSP_REQUEST_SWITCH_FRAMER_TRANSPARENT 1
#define DSP_REQUEST_SWITCH_FRAMER_ASYNC 2
#define DSP_UDATA_REQUEST_CLEARDOWN 2
/*
parameters:
- none -
*/
#define DSP_UDATA_REQUEST_TX_CONFIRMATION_ON 3
/*
parameters:
- none -
*/
#define DSP_UDATA_REQUEST_TX_CONFIRMATION_OFF 4
/*
parameters:
- none -
*/
typedef struct eicon_dsp_ind {
__u16 time __attribute__ ((packed));
__u8 norm __attribute__ ((packed));
__u16 options __attribute__ ((packed));
__u32 speed __attribute__ ((packed));
__u16 delay __attribute__ ((packed));
__u32 txspeed __attribute__ ((packed));
__u32 rxspeed __attribute__ ((packed));
} eicon_dsp_ind;
#define DSP_CONNECTED_OPTION_V42_TRANS 0x0002
#define DSP_CONNECTED_OPTION_V42_LAPM 0x0004
#define DSP_CONNECTED_OPTION_SHORT_TRAIN 0x0008
#define DSP_CONNECTED_OPTION_TALKER_ECHO_PROTECT 0x0010
#define DSP_UDATA_INDICATION_DISCONNECT 5
/*
returns:
<byte> cause
*/
#define DSP_DISCONNECT_CAUSE_NONE 0x00
#define DSP_DISCONNECT_CAUSE_BUSY_TONE 0x01
#define DSP_DISCONNECT_CAUSE_CONGESTION_TONE 0x02
#define DSP_DISCONNECT_CAUSE_INCOMPATIBILITY 0x03
#define DSP_DISCONNECT_CAUSE_CLEARDOWN 0x04
#define DSP_DISCONNECT_CAUSE_TRAINING_TIMEOUT 0x05
#define DSP_UDATA_INDICATION_TX_CONFIRMATION 6
/*
returns:
<word> confirmation number
*/
#define DSP_UDATA_REQUEST_SEND_DTMF_DIGITS 16
/*
parameters:
<word> tone duration (ms)
<word> gap duration (ms)
<byte> digit 0 tone code
...
<byte> digit n tone code
*/
#define DSP_SEND_DTMF_DIGITS_HEADER_LENGTH 5
#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_697_HZ 0x00
#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_770_HZ 0x01
#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_852_HZ 0x02
#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_941_HZ 0x03
#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_MASK 0x03
#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1209_HZ 0x00
#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1336_HZ 0x04
#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1477_HZ 0x08
#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1633_HZ 0x0c
#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_MASK 0x0c
#define DSP_DTMF_DIGIT_TONE_CODE_0 0x07
#define DSP_DTMF_DIGIT_TONE_CODE_1 0x00
#define DSP_DTMF_DIGIT_TONE_CODE_2 0x04
#define DSP_DTMF_DIGIT_TONE_CODE_3 0x08
#define DSP_DTMF_DIGIT_TONE_CODE_4 0x01
#define DSP_DTMF_DIGIT_TONE_CODE_5 0x05
#define DSP_DTMF_DIGIT_TONE_CODE_6 0x09
#define DSP_DTMF_DIGIT_TONE_CODE_7 0x02
#define DSP_DTMF_DIGIT_TONE_CODE_8 0x06
#define DSP_DTMF_DIGIT_TONE_CODE_9 0x0a
#define DSP_DTMF_DIGIT_TONE_CODE_STAR 0x03
#define DSP_DTMF_DIGIT_TONE_CODE_HASHMARK 0x0b
#define DSP_DTMF_DIGIT_TONE_CODE_A 0x0c
#define DSP_DTMF_DIGIT_TONE_CODE_B 0x0d
#define DSP_DTMF_DIGIT_TONE_CODE_C 0x0e
#define DSP_DTMF_DIGIT_TONE_CODE_D 0x0f
#define DSP_UDATA_INDICATION_DTMF_DIGITS_SENT 16
/*
returns:
- none -
One indication will be sent for every request.
*/
#define DSP_UDATA_REQUEST_ENABLE_DTMF_RECEIVER 17
/*
parameters:
<word> tone duration (ms)
<word> gap duration (ms)
*/
typedef struct enable_dtmf_s {
__u16 tone;
__u16 gap;
} enable_dtmf_s;
#define DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER 18
/*
parameters:
- none -
*/
#define DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED 17
/*
returns:
<byte> digit 0 tone code
...
<byte> digit n tone code
*/
#define DSP_DTMF_DIGITS_RECEIVED_HEADER_LENGTH 1
#define DSP_UDATA_INDICATION_MODEM_CALLING_TONE 18
/*
returns:
- none -
*/
#define DSP_UDATA_INDICATION_FAX_CALLING_TONE 19
/*
returns:
- none -
*/
#define DSP_UDATA_INDICATION_ANSWER_TONE 20
/*
returns:
- none -
*/
/* ============= FAX ================ */
#define EICON_FAXID_LEN 20
typedef struct eicon_t30_s {
__u8 code;
__u8 rate;
__u8 resolution;
__u8 format;
__u8 pages_low;
__u8 pages_high;
__u8 atf;
__u8 control_bits_low;
__u8 control_bits_high;
__u8 feature_bits_low;
__u8 feature_bits_high;
__u8 universal_5;
__u8 universal_6;
__u8 universal_7;
__u8 station_id_len;
__u8 head_line_len;
__u8 station_id[EICON_FAXID_LEN];
/* __u8 head_line[]; */
} eicon_t30_s;
/* EDATA transmit messages */
#define EDATA_T30_DIS 0x01
#define EDATA_T30_FTT 0x02
#define EDATA_T30_MCF 0x03
/* EDATA receive messages */
#define EDATA_T30_DCS 0x81
#define EDATA_T30_TRAIN_OK 0x82
#define EDATA_T30_EOP 0x83
#define EDATA_T30_MPS 0x84
#define EDATA_T30_EOM 0x85
#define EDATA_T30_DTC 0x86
#define T30_FORMAT_SFF 0
#define T30_FORMAT_ASCII 1
#define T30_FORMAT_COUNT 2
#define T30_CONTROL_BIT_DISABLE_FINE 0x0001
#define T30_CONTROL_BIT_ENABLE_ECM 0x0002
#define T30_CONTROL_BIT_ECM_64_BYTES 0x0004
#define T30_CONTROL_BIT_ENABLE_2D_CODING 0x0008
#define T30_CONTROL_BIT_ENABLE_T6_CODING 0x0010
#define T30_CONTROL_BIT_ENABLE_UNCOMPR 0x0020
#define T30_CONTROL_BIT_ACCEPT_POLLING 0x0040
#define T30_CONTROL_BIT_REQUEST_POLLING 0x0080
#define T30_CONTROL_BIT_MORE_DOCUMENTS 0x0100
#define T30_CONTROL_BIT_ALL_FEATURES\
(T30_CONTROL_BIT_ENABLE_ECM | T30_CONTROL_BIT_ENABLE_2D_CODING |\
T30_CONTROL_BIT_ENABLE_T6_CODING | T30_CONTROL_BIT_ENABLE_UNCOMPR)
#define T30_FEATURE_BIT_FINE 0x0001
#define T30_FEATURE_BIT_ECM 0x0002
#define T30_FEATURE_BIT_ECM_64_BYTES 0x0004
#define T30_FEATURE_BIT_2D_CODING 0x0008
#define T30_FEATURE_BIT_T6_CODING 0x0010
#define T30_FEATURE_BIT_UNCOMPR_ENABLED 0x0020
#define T30_FEATURE_BIT_POLLING 0x0040
#define FAX_OBJECT_DOCU 1
#define FAX_OBJECT_PAGE 2
#define FAX_OBJECT_LINE 3
#define T4_EOL 0x800
#define T4_EOL_BITSIZE 12
#define T4_EOL_DWORD (T4_EOL << (32 - T4_EOL_BITSIZE))
#define T4_EOL_MASK_DWORD ((__u32) -1 << (32 - T4_EOL_BITSIZE))
#define SFF_LEN_FLD_SIZE 3
#define _DLE_ 0x10
#define _ETX_ 0x03
typedef struct eicon_sff_dochead {
__u32 id __attribute__ ((packed));
__u8 version __attribute__ ((packed));
__u8 reserved1 __attribute__ ((packed));
__u16 userinfo __attribute__ ((packed));
__u16 pagecount __attribute__ ((packed));
__u16 off1pagehead __attribute__ ((packed));
__u32 offnpagehead __attribute__ ((packed));
__u32 offdocend __attribute__ ((packed));
} eicon_sff_dochead;
typedef struct eicon_sff_pagehead {
__u8 pageheadid __attribute__ ((packed));
__u8 pageheadlen __attribute__ ((packed));
__u8 resvert __attribute__ ((packed));
__u8 reshoriz __attribute__ ((packed));
__u8 coding __attribute__ ((packed));
__u8 reserved2 __attribute__ ((packed));
__u16 linelength __attribute__ ((packed));
__u16 pagelength __attribute__ ((packed));
__u32 offprevpage __attribute__ ((packed));
__u32 offnextpage __attribute__ ((packed));
} eicon_sff_pagehead;
#endif /* DSP_H */
/* $Id: eicon_idi.c,v 1.1.4.1.2.4 2002/10/01 11:29:13 armin Exp $
*
* ISDN lowlevel-module for Eicon active cards.
* IDI interface
*
* Copyright 1998-2000 by Armin Schindler (mac@melware.de)
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
*
* Thanks to Deutsche Mailbox Saar-Lor-Lux GmbH
* for sponsoring and testing fax
* capabilities with Diva Server cards.
* (dor@deutschemailbox.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/config.h>
#include "eicon.h"
#include "eicon_idi.h"
#include "eicon_dsp.h"
#include "uxio.h"
#undef EICON_FULL_SERVICE_OKTETT
char *eicon_idi_revision = "$Revision: 1.1.4.1.2.4 $";
eicon_manifbuf *manbuf;
int eicon_idi_manage_assign(eicon_card *card);
int eicon_idi_manage_remove(eicon_card *card);
int idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer);
int
idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan)
{
int l = 0;
int tmp;
tmp = 0;
if (!signet) {
/* Signal Layer */
reqbuf->XBuffer.P[l++] = CAI;
reqbuf->XBuffer.P[l++] = 1;
reqbuf->XBuffer.P[l++] = 0;
reqbuf->XBuffer.P[l++] = KEY;
reqbuf->XBuffer.P[l++] = 3;
reqbuf->XBuffer.P[l++] = 'I';
reqbuf->XBuffer.P[l++] = '4';
reqbuf->XBuffer.P[l++] = 'L';
reqbuf->XBuffer.P[l++] = SHIFT|6;
reqbuf->XBuffer.P[l++] = SIN;
reqbuf->XBuffer.P[l++] = 2;
reqbuf->XBuffer.P[l++] = 0;
reqbuf->XBuffer.P[l++] = 0;
reqbuf->XBuffer.P[l++] = 0; /* end */
reqbuf->Req = ASSIGN;
reqbuf->ReqCh = 0;
reqbuf->ReqId = DSIG_ID;
reqbuf->XBuffer.length = l;
reqbuf->Reference = 0; /* Sig Entity */
}
else {
/* Network Layer */
reqbuf->XBuffer.P[l++] = CAI;
reqbuf->XBuffer.P[l++] = 1;
reqbuf->XBuffer.P[l++] = chan->e.D3Id;
reqbuf->XBuffer.P[l++] = LLC;
reqbuf->XBuffer.P[l++] = 2;
switch(chan->l2prot) {
case ISDN_PROTO_L2_V11096:
case ISDN_PROTO_L2_V11019:
case ISDN_PROTO_L2_V11038:
case ISDN_PROTO_L2_TRANS:
reqbuf->XBuffer.P[l++] = 2; /* transparent */
break;
case ISDN_PROTO_L2_X75I:
case ISDN_PROTO_L2_X75UI:
case ISDN_PROTO_L2_X75BUI:
reqbuf->XBuffer.P[l++] = 5; /* X.75 */
break;
case ISDN_PROTO_L2_MODEM:
if (chan->fsm_state == EICON_STATE_IWAIT)
reqbuf->XBuffer.P[l++] = 9; /* V.42 incoming */
else
reqbuf->XBuffer.P[l++] = 10; /* V.42 */
break;
case ISDN_PROTO_L2_HDLC:
case ISDN_PROTO_L2_FAX:
if (chan->fsm_state == EICON_STATE_IWAIT)
reqbuf->XBuffer.P[l++] = 3; /* autoconnect on incoming */
else
reqbuf->XBuffer.P[l++] = 2; /* transparent */
break;
default:
reqbuf->XBuffer.P[l++] = 1;
}
switch(chan->l3prot) {
case ISDN_PROTO_L3_FCLASS2:
#ifdef CONFIG_ISDN_TTY_FAX
reqbuf->XBuffer.P[l++] = 6;
reqbuf->XBuffer.P[l++] = NLC;
tmp = idi_fill_in_T30(chan, &reqbuf->XBuffer.P[l+1]);
reqbuf->XBuffer.P[l++] = tmp;
l += tmp;
break;
#endif
case ISDN_PROTO_L3_TRANS:
default:
reqbuf->XBuffer.P[l++] = 4;
}
reqbuf->XBuffer.P[l++] = 0; /* end */
reqbuf->Req = ASSIGN;
reqbuf->ReqCh = 0;
reqbuf->ReqId = NL_ID;
reqbuf->XBuffer.length = l;
reqbuf->Reference = 1; /* Net Entity */
}
return(0);
}
int
idi_put_req(eicon_REQ *reqbuf, int rq, int signet, int Ch)
{
reqbuf->Req = rq;
reqbuf->ReqCh = Ch;
reqbuf->ReqId = 1;
reqbuf->XBuffer.length = 1;
reqbuf->XBuffer.P[0] = 0;
reqbuf->Reference = signet;
return(0);
}
int
idi_put_suspend_req(eicon_REQ *reqbuf, eicon_chan *chan)
{
reqbuf->Req = SUSPEND;
reqbuf->ReqCh = 0;
reqbuf->ReqId = 1;
reqbuf->XBuffer.P[0] = CAI;
reqbuf->XBuffer.P[1] = 1;
reqbuf->XBuffer.P[2] = chan->No;
reqbuf->XBuffer.P[3] = 0;
reqbuf->XBuffer.length = 4;
reqbuf->Reference = 0; /* Sig Entity */
return(0);
}
int
idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan)
{
int l = 9;
reqbuf->Req = CALL_RES;
reqbuf->ReqCh = 0;
reqbuf->ReqId = 1;
reqbuf->XBuffer.P[0] = CAI;
reqbuf->XBuffer.P[1] = 6;
reqbuf->XBuffer.P[2] = 9;
reqbuf->XBuffer.P[3] = 0;
reqbuf->XBuffer.P[4] = 0;
reqbuf->XBuffer.P[5] = 0;
reqbuf->XBuffer.P[6] = 32;
reqbuf->XBuffer.P[7] = 0;
switch(chan->l2prot) {
case ISDN_PROTO_L2_X75I:
case ISDN_PROTO_L2_X75UI:
case ISDN_PROTO_L2_X75BUI:
case ISDN_PROTO_L2_HDLC:
reqbuf->XBuffer.P[1] = 1;
reqbuf->XBuffer.P[2] = 0x05;
l = 4;
break;
case ISDN_PROTO_L2_V11096:
reqbuf->XBuffer.P[2] = 0x0d;
reqbuf->XBuffer.P[3] = 5;
reqbuf->XBuffer.P[4] = 0;
break;
case ISDN_PROTO_L2_V11019:
reqbuf->XBuffer.P[2] = 0x0d;
reqbuf->XBuffer.P[3] = 6;
reqbuf->XBuffer.P[4] = 0;
break;
case ISDN_PROTO_L2_V11038:
reqbuf->XBuffer.P[2] = 0x0d;
reqbuf->XBuffer.P[3] = 7;
reqbuf->XBuffer.P[4] = 0;
break;
case ISDN_PROTO_L2_MODEM:
reqbuf->XBuffer.P[2] = 0x11;
reqbuf->XBuffer.P[3] = 7;
reqbuf->XBuffer.P[4] = 0;
reqbuf->XBuffer.P[5] = 0;
reqbuf->XBuffer.P[6] = 128;
reqbuf->XBuffer.P[7] = 0;
break;
case ISDN_PROTO_L2_FAX:
reqbuf->XBuffer.P[2] = 0x10;
reqbuf->XBuffer.P[3] = 0;
reqbuf->XBuffer.P[4] = 0;
reqbuf->XBuffer.P[5] = 0;
reqbuf->XBuffer.P[6] = 128;
reqbuf->XBuffer.P[7] = 0;
break;
case ISDN_PROTO_L2_TRANS:
switch(chan->l3prot) {
case ISDN_PROTO_L3_TRANSDSP:
reqbuf->XBuffer.P[2] = 22; /* DTMF, audio events on */
}
break;
}
reqbuf->XBuffer.P[8] = 0;
reqbuf->XBuffer.length = l;
reqbuf->Reference = 0; /* Sig Entity */
eicon_log(NULL, 8, "idi_req: Ch%d: Call_Res\n", chan->No);
return(0);
}
int
idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer)
{
struct sk_buff *skb;
struct sk_buff *skb2;
eicon_REQ *reqbuf;
eicon_chan_ptr *chan2;
skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
if ((!skb) || (!skb2)) {
eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in do_req()\n", chan->No);
if (skb)
dev_kfree_skb(skb);
if (skb2)
dev_kfree_skb(skb2);
return -ENOMEM;
}
chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
chan2->ptr = chan;
reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
eicon_log(card, 8, "idi_req: Ch%d: req %x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig");
if (layer) cmd |= 0x700;
switch(cmd) {
case ASSIGN:
case ASSIGN|0x700:
idi_assign_req(reqbuf, layer, chan);
break;
case REMOVE:
case REMOVE|0x700:
idi_put_req(reqbuf, REMOVE, layer, 0);
break;
case INDICATE_REQ:
idi_put_req(reqbuf, INDICATE_REQ, 0, 0);
break;
case HANGUP:
idi_put_req(reqbuf, HANGUP, 0, 0);
break;
case SUSPEND:
idi_put_suspend_req(reqbuf, chan);
break;
case RESUME:
idi_put_req(reqbuf, RESUME, 0 ,0);
break;
case REJECT:
idi_put_req(reqbuf, REJECT, 0 ,0);
break;
case CALL_ALERT:
idi_put_req(reqbuf, CALL_ALERT, 0, 0);
break;
case CALL_RES:
idi_call_res_req(reqbuf, chan);
break;
case CALL_HOLD:
idi_put_req(reqbuf, CALL_HOLD, 0, 0);
break;
case N_CONNECT|0x700:
idi_put_req(reqbuf, N_CONNECT, 1, 0);
break;
case N_CONNECT_ACK|0x700:
idi_put_req(reqbuf, N_CONNECT_ACK, 1, 0);
break;
case N_DISC|0x700:
idi_put_req(reqbuf, N_DISC, 1, chan->e.IndCh);
break;
case N_DISC_ACK|0x700:
idi_put_req(reqbuf, N_DISC_ACK, 1, chan->e.IndCh);
break;
default:
eicon_log(card, 1, "idi_req: Ch%d: Unknown request\n", chan->No);
dev_kfree_skb(skb);
dev_kfree_skb(skb2);
return(-1);
}
skb_queue_tail(&chan->e.X, skb);
skb_queue_tail(&card->sndq, skb2);
eicon_schedule_tx(card);
return(0);
}
int
eicon_idi_listen_req(eicon_card *card, eicon_chan *chan)
{
if ((!card) || (!chan))
return 1;
eicon_log(card, 16, "idi_req: Ch%d: Listen_Req eazmask=0x%x\n",chan->No, chan->eazmask);
if (!chan->e.D3Id) {
idi_do_req(card, chan, ASSIGN, 0);
}
if (chan->fsm_state == EICON_STATE_NULL) {
if (!(chan->statectrl & HAVE_CONN_REQ)) {
idi_do_req(card, chan, INDICATE_REQ, 0);
chan->fsm_state = EICON_STATE_LISTEN;
}
}
return(0);
}
unsigned char
idi_si2bc(int si1, int si2, char *bc, char *hlc)
{
hlc[0] = 0;
switch(si1) {
case 1:
bc[0] = 0x90; /* 3,1 kHz audio */
bc[1] = 0x90; /* 64 kbit/s */
bc[2] = 0xa3; /* G.711 A-law */
#ifdef EICON_FULL_SERVICE_OKTETT
if (si2 == 1) {
bc[0] = 0x80; /* Speech */
hlc[0] = 0x02; /* hlc len */
hlc[1] = 0x91; /* first hic */
hlc[2] = 0x81; /* Telephony */
}
#endif
return(3);
case 2:
bc[0] = 0x90; /* 3,1 kHz audio */
bc[1] = 0x90; /* 64 kbit/s */
bc[2] = 0xa3; /* G.711 A-law */
#ifdef EICON_FULL_SERVICE_OKTETT
if (si2 == 2) {
hlc[0] = 0x02; /* hlc len */
hlc[1] = 0x91; /* first hic */
hlc[2] = 0x84; /* Fax Gr.2/3 */
}
#endif
return(3);
case 5:
case 7:
default:
bc[0] = 0x88;
bc[1] = 0x90;
return(2);
}
return (0);
}
int
idi_hangup(eicon_card *card, eicon_chan *chan)
{
if ((!card) || (!chan))
return 1;
if ((chan->fsm_state == EICON_STATE_ACTIVE) ||
(chan->fsm_state == EICON_STATE_WMCONN)) {
if (chan->e.B2Id) idi_do_req(card, chan, N_DISC, 1);
}
if (chan->e.B2Id) idi_do_req(card, chan, REMOVE, 1);
if (chan->fsm_state != EICON_STATE_NULL) {
chan->statectrl |= WAITING_FOR_HANGUP;
idi_do_req(card, chan, HANGUP, 0);
chan->fsm_state = EICON_STATE_NULL;
}
eicon_log(card, 8, "idi_req: Ch%d: Hangup\n", chan->No);
#ifdef CONFIG_ISDN_TTY_FAX
chan->fax = 0;
#endif
return(0);
}
int
capipmsg(eicon_card *card, eicon_chan *chan, capi_msg *cm)
{
if ((cm->para[0] != 3) || (cm->para[1] != 0))
return -1;
if (cm->para[2] < 3)
return -1;
if (cm->para[4] != 0)
return -1;
switch(cm->para[3]) {
case 4: /* Suspend */
eicon_log(card, 8, "idi_req: Ch%d: Call Suspend\n", chan->No);
if (cm->para[5]) {
idi_do_req(card, chan, SUSPEND, 0);
} else {
idi_do_req(card, chan, CALL_HOLD, 0);
}
break;
case 5: /* Resume */
eicon_log(card, 8, "idi_req: Ch%d: Call Resume\n", chan->No);
idi_do_req(card, chan, RESUME, 0);
break;
}
return 0;
}
int
idi_connect_res(eicon_card *card, eicon_chan *chan)
{
if ((!card) || (!chan))
return 1;
chan->fsm_state = EICON_STATE_IWAIT;
/* check if old NetID has been removed */
if (chan->e.B2Id) {
eicon_log(card, 1, "eicon: Ch%d: old net_id %x still exist, removing.\n",
chan->No, chan->e.B2Id);
idi_do_req(card, chan, REMOVE, 1);
}
idi_do_req(card, chan, ASSIGN, 1);
idi_do_req(card, chan, CALL_RES, 0);
return(0);
}
int
idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
char *eazmsn, int si1, int si2)
{
int l = 0;
int i;
unsigned char tmp;
unsigned char *sub, *sp;
unsigned char bc[5];
unsigned char hlc[5];
struct sk_buff *skb;
struct sk_buff *skb2;
eicon_REQ *reqbuf;
eicon_chan_ptr *chan2;
if ((!card) || (!chan))
return 1;
skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
if ((!skb) || (!skb2)) {
eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in connect_req()\n", chan->No);
if (skb)
dev_kfree_skb(skb);
if (skb2)
dev_kfree_skb(skb2);
return -ENOMEM;
}
chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
chan2->ptr = chan;
reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
reqbuf->Req = CALL_REQ;
reqbuf->ReqCh = 0;
reqbuf->ReqId = 1;
sub = NULL;
sp = phone;
while (*sp) {
if (*sp == '.') {
sub = sp + 1;
*sp = 0;
} else
sp++;
}
reqbuf->XBuffer.P[l++] = CPN;
reqbuf->XBuffer.P[l++] = strlen(phone) + 1;
reqbuf->XBuffer.P[l++] = 0x81;
for(i=0; i<strlen(phone);i++)
reqbuf->XBuffer.P[l++] = phone[i] & 0x7f;
if (sub) {
reqbuf->XBuffer.P[l++] = DSA;
reqbuf->XBuffer.P[l++] = strlen(sub) + 2;
reqbuf->XBuffer.P[l++] = 0x80; /* NSAP coded */
reqbuf->XBuffer.P[l++] = 0x50; /* local IDI format */
while (*sub)
reqbuf->XBuffer.P[l++] = *sub++ & 0x7f;
}
sub = NULL;
sp = eazmsn;
while (*sp) {
if (*sp == '.') {
sub = sp + 1;
*sp = 0;
} else
sp++;
}
reqbuf->XBuffer.P[l++] = OAD;
reqbuf->XBuffer.P[l++] = strlen(eazmsn) + 2;
reqbuf->XBuffer.P[l++] = 0x01;
reqbuf->XBuffer.P[l++] = 0x80;
for(i=0; i<strlen(eazmsn);i++)
reqbuf->XBuffer.P[l++] = eazmsn[i] & 0x7f;
if (sub) {
reqbuf->XBuffer.P[l++] = OSA;
reqbuf->XBuffer.P[l++] = strlen(sub) + 2;
reqbuf->XBuffer.P[l++] = 0x80; /* NSAP coded */
reqbuf->XBuffer.P[l++] = 0x50; /* local IDI format */
while (*sub)
reqbuf->XBuffer.P[l++] = *sub++ & 0x7f;
}
if (si2 > 2) {
reqbuf->XBuffer.P[l++] = SHIFT|6;
reqbuf->XBuffer.P[l++] = SIN;
reqbuf->XBuffer.P[l++] = 2;
reqbuf->XBuffer.P[l++] = si1;
reqbuf->XBuffer.P[l++] = si2;
}
else if ((tmp = idi_si2bc(si1, si2, bc, hlc)) > 0) {
reqbuf->XBuffer.P[l++] = BC;
reqbuf->XBuffer.P[l++] = tmp;
for(i=0; i<tmp;i++)
reqbuf->XBuffer.P[l++] = bc[i];
if ((tmp=hlc[0])) {
reqbuf->XBuffer.P[l++] = HLC;
reqbuf->XBuffer.P[l++] = tmp;
for(i=1; i<=tmp;i++)
reqbuf->XBuffer.P[l++] = hlc[i];
}
}
reqbuf->XBuffer.P[l++] = CAI;
reqbuf->XBuffer.P[l++] = 6;
reqbuf->XBuffer.P[l++] = 0x09;
reqbuf->XBuffer.P[l++] = 0;
reqbuf->XBuffer.P[l++] = 0;
reqbuf->XBuffer.P[l++] = 0;
reqbuf->XBuffer.P[l++] = 32;
reqbuf->XBuffer.P[l++] = 0;
switch(chan->l2prot) {
case ISDN_PROTO_L2_X75I:
case ISDN_PROTO_L2_X75UI:
case ISDN_PROTO_L2_X75BUI:
case ISDN_PROTO_L2_HDLC:
reqbuf->XBuffer.P[l-6] = 5;
reqbuf->XBuffer.P[l-7] = 1;
l -= 5;
break;
case ISDN_PROTO_L2_V11096:
reqbuf->XBuffer.P[l-7] = 3;
reqbuf->XBuffer.P[l-6] = 0x0d;
reqbuf->XBuffer.P[l-5] = 5;
reqbuf->XBuffer.P[l-4] = 0;
l -= 3;
break;
case ISDN_PROTO_L2_V11019:
reqbuf->XBuffer.P[l-7] = 3;
reqbuf->XBuffer.P[l-6] = 0x0d;
reqbuf->XBuffer.P[l-5] = 6;
reqbuf->XBuffer.P[l-4] = 0;
l -= 3;
break;
case ISDN_PROTO_L2_V11038:
reqbuf->XBuffer.P[l-7] = 3;
reqbuf->XBuffer.P[l-6] = 0x0d;
reqbuf->XBuffer.P[l-5] = 7;
reqbuf->XBuffer.P[l-4] = 0;
l -= 3;
break;
case ISDN_PROTO_L2_MODEM:
reqbuf->XBuffer.P[l-6] = 0x11;
reqbuf->XBuffer.P[l-5] = 7;
reqbuf->XBuffer.P[l-4] = 0;
reqbuf->XBuffer.P[l-3] = 0;
reqbuf->XBuffer.P[l-2] = 128;
reqbuf->XBuffer.P[l-1] = 0;
break;
case ISDN_PROTO_L2_FAX:
reqbuf->XBuffer.P[l-6] = 0x10;
reqbuf->XBuffer.P[l-5] = 0;
reqbuf->XBuffer.P[l-4] = 0;
reqbuf->XBuffer.P[l-3] = 0;
reqbuf->XBuffer.P[l-2] = 128;
reqbuf->XBuffer.P[l-1] = 0;
break;
case ISDN_PROTO_L2_TRANS:
switch(chan->l3prot) {
case ISDN_PROTO_L3_TRANSDSP:
reqbuf->XBuffer.P[l-6] = 22; /* DTMF, audio events on */
}
break;
}
reqbuf->XBuffer.P[l++] = 0; /* end */
reqbuf->XBuffer.length = l;
reqbuf->Reference = 0; /* Sig Entity */
if (chan->statectrl & WAITING_FOR_HANGUP) {
/* If the line did not disconnect yet,
we have to delay this command */
eicon_log(card, 32, "idi_req: Ch%d: delaying conn_req\n", chan->No);
chan->statectrl |= HAVE_CONN_REQ;
chan->tskb1 = skb;
chan->tskb2 = skb2;
} else {
skb_queue_tail(&chan->e.X, skb);
skb_queue_tail(&card->sndq, skb2);
eicon_schedule_tx(card);
}
eicon_log(card, 8, "idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone);
return(0);
}
void
idi_IndParse(eicon_card *ccard, eicon_chan *chan, idi_ind_message *message, unsigned char *buffer, int len)
{
int i,j;
int pos = 0;
int codeset = 0;
int wlen = 0;
int lock = 0;
__u8 w;
__u16 code;
isdn_ctrl cmd;
memset(message, 0, sizeof(idi_ind_message));
if ((!len) || (!buffer[pos])) return;
while(pos <= len) {
w = buffer[pos++];
if (!w) return;
if (w & 0x80) {
wlen = 0;
}
else {
wlen = buffer[pos++];
}
if (pos > len) return;
if (lock & 0x80) lock &= 0x7f;
else codeset = lock;
if((w&0xf0) == SHIFT) {
codeset = w;
if(!(codeset & 0x08)) lock = codeset & 7;
codeset &= 7;
lock |= 0x80;
}
else {
if (w==ESC && wlen >=2) {
code = buffer[pos++]|0x800;
wlen--;
}
else code = w;
code |= (codeset<<8);
if (pos + wlen > len) {
eicon_log(ccard, 1, "idi_err: Ch%d: IElen %d of %x exceeds Ind_Length (+%d)\n", chan->No,
wlen, code, (pos + wlen) - len);
return;
}
switch(code) {
case OAD:
if (wlen > sizeof(message->oad)) {
pos += wlen;
break;
}
j = 1;
if (wlen) {
message->plan = buffer[pos++];
if (message->plan &0x80)
message->screen = 0;
else {
message->screen = buffer[pos++];
j = 2;
}
}
for(i=0; i < wlen-j; i++)
message->oad[i] = buffer[pos++];
eicon_log(ccard, 2, "idi_inf: Ch%d: OAD=(0x%02x,0x%02x) %s\n", chan->No,
message->plan, message->screen, message->oad);
break;
case RDN:
if (wlen > sizeof(message->rdn)) {
pos += wlen;
break;
}
j = 1;
if (wlen) {
if (!(buffer[pos++] & 0x80)) {
pos++;
j = 2;
}
}
for(i=0; i < wlen-j; i++)
message->rdn[i] = buffer[pos++];
eicon_log(ccard, 2, "idi_inf: Ch%d: RDN= %s\n", chan->No,
message->rdn);
break;
case CPN:
if (wlen > sizeof(message->cpn)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->cpn[i] = buffer[pos++];
eicon_log(ccard, 2, "idi_inf: Ch%d: CPN=(0x%02x) %s\n", chan->No,
(__u8)message->cpn[0], message->cpn + 1);
break;
case DSA:
if (wlen > sizeof(message->dsa)) {
pos += wlen;
break;
}
pos += 2;
for(i=0; i < wlen-2; i++)
message->dsa[i] = buffer[pos++];
eicon_log(ccard, 2, "idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa);
break;
case OSA:
if (wlen > sizeof(message->osa)) {
pos += wlen;
break;
}
pos += 2;
for(i=0; i < wlen-2; i++)
message->osa[i] = buffer[pos++];
eicon_log(ccard, 2, "idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa);
break;
case CAD:
pos += wlen;
eicon_log(ccard, 2, "idi_inf: Ch%d: Connected Address in ind, len:%x\n",
chan->No, wlen);
break;
case BC:
if (wlen > sizeof(message->bc)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->bc[i] = buffer[pos++];
eicon_log(ccard, 4, "idi_inf: Ch%d: BC = 0x%02x 0x%02x 0x%02x\n", chan->No,
message->bc[0],message->bc[1],message->bc[2]);
break;
case 0x800|BC:
if (wlen > sizeof(message->e_bc)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->e_bc[i] = buffer[pos++];
eicon_log(ccard, 4, "idi_inf: Ch%d: ESC/BC=%d\n", chan->No, message->bc[0]);
break;
case LLC:
if (wlen > sizeof(message->llc)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->llc[i] = buffer[pos++];
eicon_log(ccard, 4, "idi_inf: Ch%d: LLC=%d %d %d %d ...\n", chan->No, message->llc[0],
message->llc[1],message->llc[2],message->llc[3]);
break;
case HLC:
if (wlen > sizeof(message->hlc)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->hlc[i] = buffer[pos++];
eicon_log(ccard, 4, "idi_inf: Ch%d: HLC=%x %x %x %x %x ...\n", chan->No,
message->hlc[0], message->hlc[1],
message->hlc[2], message->hlc[3], message->hlc[4]);
break;
case DSP:
case 0x600|DSP:
if (wlen > sizeof(message->display)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->display[i] = buffer[pos++];
eicon_log(ccard, 4, "idi_inf: Ch%d: Display: %s\n", chan->No,
message->display);
break;
case 0x600|KEY:
if (wlen > sizeof(message->keypad)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->keypad[i] = buffer[pos++];
eicon_log(ccard, 4, "idi_inf: Ch%d: Keypad: %s\n", chan->No,
message->keypad);
break;
case NI:
case 0x600|NI:
if (wlen) {
switch(buffer[pos] & 127) {
case 0:
eicon_log(ccard, 4, "idi_inf: Ch%d: User suspended.\n", chan->No);
break;
case 1:
eicon_log(ccard, 4, "idi_inf: Ch%d: User resumed.\n", chan->No);
break;
case 2:
eicon_log(ccard, 4, "idi_inf: Ch%d: Bearer service change.\n", chan->No);
break;
default:
eicon_log(ccard, 4, "idi_inf: Ch%d: Unknown Notification %x.\n",
chan->No, buffer[pos] & 127);
}
pos += wlen;
}
break;
case PI:
case 0x600|PI:
if (wlen > 1) {
switch(buffer[pos+1] & 127) {
case 1:
eicon_log(ccard, 4, "idi_inf: Ch%d: Call is not end-to-end ISDN.\n", chan->No);
break;
case 2:
eicon_log(ccard, 4, "idi_inf: Ch%d: Destination address is non ISDN.\n", chan->No);
break;
case 3:
eicon_log(ccard, 4, "idi_inf: Ch%d: Origination address is non ISDN.\n", chan->No);
break;
case 4:
eicon_log(ccard, 4, "idi_inf: Ch%d: Call has returned to the ISDN.\n", chan->No);
break;
case 5:
eicon_log(ccard, 4, "idi_inf: Ch%d: Interworking has occurred.\n", chan->No);
break;
case 8:
eicon_log(ccard, 4, "idi_inf: Ch%d: In-band information available.\n", chan->No);
break;
default:
eicon_log(ccard, 4, "idi_inf: Ch%d: Unknown Progress %x.\n",
chan->No, buffer[pos+1] & 127);
}
}
pos += wlen;
break;
case CAU:
if (wlen > sizeof(message->cau)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->cau[i] = buffer[pos++];
memcpy(&chan->cause, &message->cau, 2);
eicon_log(ccard, 4, "idi_inf: Ch%d: CAU=%d %d\n", chan->No,
message->cau[0],message->cau[1]);
break;
case 0x800|CAU:
if (wlen > sizeof(message->e_cau)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->e_cau[i] = buffer[pos++];
eicon_log(ccard, 4, "idi_inf: Ch%d: ECAU=%d %d\n", chan->No,
message->e_cau[0],message->e_cau[1]);
break;
case 0x800|CHI:
if (wlen > sizeof(message->e_chi)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->e_chi[i] = buffer[pos++];
eicon_log(ccard, 4, "idi_inf: Ch%d: ESC/CHI=%d\n", chan->No,
message->e_cau[0]);
break;
case 0x800|0x7a:
pos ++;
message->e_mt=buffer[pos++];
eicon_log(ccard, 4, "idi_inf: Ch%d: EMT=0x%x\n", chan->No, message->e_mt);
break;
case DT:
if (wlen > sizeof(message->dt)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->dt[i] = buffer[pos++];
eicon_log(ccard, 4, "idi_inf: Ch%d: DT: %02d.%02d.%02d %02d:%02d:%02d\n", chan->No,
message->dt[2], message->dt[1], message->dt[0],
message->dt[3], message->dt[4], message->dt[5]);
break;
case 0x600|SIN:
if (wlen > sizeof(message->sin)) {
pos += wlen;
break;
}
for(i=0; i < wlen; i++)
message->sin[i] = buffer[pos++];
eicon_log(ccard, 2, "idi_inf: Ch%d: SIN=%d %d\n", chan->No,
message->sin[0],message->sin[1]);
break;
case 0x600|CPS:
eicon_log(ccard, 2, "idi_inf: Ch%d: Called Party Status in ind\n", chan->No);
pos += wlen;
break;
case 0x600|CIF:
for (i = 0; i < wlen; i++)
if (buffer[pos + i] != '0') break;
memcpy(&cmd.parm.num, &buffer[pos + i], wlen - i);
cmd.parm.num[wlen - i] = 0;
eicon_log(ccard, 2, "idi_inf: Ch%d: CIF=%s\n", chan->No, cmd.parm.num);
pos += wlen;
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_CINF;
cmd.arg = chan->No;
ccard->interface.statcallb(&cmd);
break;
case 0x600|DATE:
eicon_log(ccard, 2, "idi_inf: Ch%d: Date in ind\n", chan->No);
pos += wlen;
break;
case 0xa1:
eicon_log(ccard, 2, "idi_inf: Ch%d: Sending Complete in ind.\n", chan->No);
pos += wlen;
break;
case 0xe08:
case 0xe7a:
case 0xe04:
case 0xe00:
/* *** TODO *** */
case CHA:
/* Charge advice */
case FTY:
case 0x600|FTY:
case CHI:
case 0x800:
/* Not yet interested in this */
pos += wlen;
break;
case 0x880:
/* Managment Information Element */
if (!manbuf) {
eicon_log(ccard, 1, "idi_err: manbuf not allocated\n");
}
else {
memcpy(&manbuf->data[manbuf->pos], &buffer[pos], wlen);
manbuf->length[manbuf->count] = wlen;
manbuf->count++;
manbuf->pos += wlen;
}
pos += wlen;
break;
default:
pos += wlen;
eicon_log(ccard, 6, "idi_inf: Ch%d: unknown information element 0x%x in ind, len:%x\n",
chan->No, code, wlen);
}
}
}
}
void
idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *sin, unsigned char *si1, unsigned char *si2)
{
si1[0] = 0;
si2[0] = 0;
switch (bc[0] & 0x7f) {
case 0x00: /* Speech */
si1[0] = 1;
#ifdef EICON_FULL_SERVICE_OKTETT
si1[0] = sin[0];
si2[0] = sin[1];
#endif
break;
case 0x10: /* 3.1 Khz audio */
si1[0] = 1;
#ifdef EICON_FULL_SERVICE_OKTETT
si1[0] = sin[0];
si2[0] = sin[1];
#endif
break;
case 0x08: /* Unrestricted digital information */
si1[0] = 7;
si2[0] = sin[1];
break;
case 0x09: /* Restricted digital information */
si1[0] = 2;
break;
case 0x11:
/* Unrestr. digital information with
* tones/announcements ( or 7 kHz audio
*/
si1[0] = 3;
break;
case 0x18: /* Video */
si1[0] = 4;
break;
}
switch (bc[1] & 0x7f) {
case 0x40: /* packed mode */
si1[0] = 8;
break;
case 0x10: /* 64 kbit */
case 0x11: /* 2*64 kbit */
case 0x13: /* 384 kbit */
case 0x15: /* 1536 kbit */
case 0x17: /* 1920 kbit */
/* moderate = bc[1] & 0x7f; */
break;
}
}
/********************* FAX stuff ***************************/
#ifdef CONFIG_ISDN_TTY_FAX
int
idi_fill_in_T30(eicon_chan *chan, unsigned char *buffer)
{
eicon_t30_s *t30 = (eicon_t30_s *) buffer;
if (!chan->fax) {
eicon_log(NULL, 1,"idi_T30: fill_in with NULL fax struct, ERROR\n");
return 0;
}
memset(t30, 0, sizeof(eicon_t30_s));
t30->station_id_len = EICON_FAXID_LEN;
memcpy(&t30->station_id[0], &chan->fax->id[0], EICON_FAXID_LEN);
t30->resolution = chan->fax->resolution;
t30->rate = chan->fax->rate + 1; /* eicon rate starts with 1 */
t30->format = T30_FORMAT_SFF;
t30->pages_low = 0;
t30->pages_high = 0;
t30->atf = 1; /* optimised for AT+F command set */
t30->code = 0;
t30->feature_bits_low = 0;
t30->feature_bits_high = 0;
t30->control_bits_low = 0;
t30->control_bits_high = 0;
if (chan->fax->nbc) {
/* set compression by DCC value */
switch(chan->fax->compression) {
case (0): /* 1-D modified */
break;
case (1): /* 2-D modified Read */
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
break;
case (2): /* 2-D uncompressed */
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR;
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED;
t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
break;
case (3): /* 2-D modified Read */
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_T6_CODING;
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR;
t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED;
t30->feature_bits_low |= T30_FEATURE_BIT_T6_CODING;
t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
break;
}
} else {
/* set compression to best */
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_T6_CODING;
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_2D_CODING;
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_UNCOMPR;
t30->feature_bits_low |= T30_FEATURE_BIT_UNCOMPR_ENABLED;
t30->feature_bits_low |= T30_FEATURE_BIT_T6_CODING;
t30->feature_bits_low |= T30_FEATURE_BIT_2D_CODING;
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
}
switch(chan->fax->ecm) {
case (0): /* disable ECM */
break;
case (1):
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
t30->control_bits_low |= T30_CONTROL_BIT_ECM_64_BYTES;
t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
t30->feature_bits_low |= T30_FEATURE_BIT_ECM_64_BYTES;
break;
case (2):
t30->control_bits_low |= T30_CONTROL_BIT_ENABLE_ECM;
t30->feature_bits_low |= T30_FEATURE_BIT_ECM;
break;
}
if (DebugVar & 128) {
char st[40];
eicon_log(NULL, 128, "sT30:code = %x\n", t30->code);
eicon_log(NULL, 128, "sT30:rate = %x\n", t30->rate);
eicon_log(NULL, 128, "sT30:res = %x\n", t30->resolution);
eicon_log(NULL, 128, "sT30:format = %x\n", t30->format);
eicon_log(NULL, 128, "sT30:pages_low = %x\n", t30->pages_low);
eicon_log(NULL, 128, "sT30:pages_high = %x\n", t30->pages_high);
eicon_log(NULL, 128, "sT30:atf = %x\n", t30->atf);
eicon_log(NULL, 128, "sT30:control_bits_low = %x\n", t30->control_bits_low);
eicon_log(NULL, 128, "sT30:control_bits_high = %x\n", t30->control_bits_high);
eicon_log(NULL, 128, "sT30:feature_bits_low = %x\n", t30->feature_bits_low);
eicon_log(NULL, 128, "sT30:feature_bits_high = %x\n", t30->feature_bits_high);
//eicon_log(NULL, 128, "sT30:universal_5 = %x\n", t30->universal_5);
//eicon_log(NULL, 128, "sT30:universal_6 = %x\n", t30->universal_6);
//eicon_log(NULL, 128, "sT30:universal_7 = %x\n", t30->universal_7);
eicon_log(NULL, 128, "sT30:station_id_len = %x\n", t30->station_id_len);
eicon_log(NULL, 128, "sT30:head_line_len = %x\n", t30->head_line_len);
strlcpy(st, t30->station_id, t30->station_id_len + 1);
eicon_log(NULL, 128, "sT30:station_id = <%s>\n", st);
}
return(sizeof(eicon_t30_s));
}
/* send fax struct */
int
idi_send_edata(eicon_card *card, eicon_chan *chan)
{
struct sk_buff *skb;
struct sk_buff *skb2;
eicon_REQ *reqbuf;
eicon_chan_ptr *chan2;
if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) {
eicon_log(card, 1, "idi_snd: Ch%d: send edata on state %d !\n", chan->No, chan->fsm_state);
return -ENODEV;
}
eicon_log(card, 128, "idi_snd: Ch%d: edata (fax)\n", chan->No);
skb = alloc_skb(sizeof(eicon_REQ) + sizeof(eicon_t30_s), GFP_ATOMIC);
skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
if ((!skb) || (!skb2)) {
eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_edata()\n", chan->No);
if (skb)
dev_kfree_skb(skb);
if (skb2)
dev_kfree_skb(skb2);
return -ENOMEM;
}
chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
chan2->ptr = chan;
reqbuf = (eicon_REQ *)skb_put(skb, sizeof(eicon_t30_s) + sizeof(eicon_REQ));
reqbuf->Req = N_EDATA;
reqbuf->ReqCh = chan->e.IndCh;
reqbuf->ReqId = 1;
reqbuf->XBuffer.length = idi_fill_in_T30(chan, reqbuf->XBuffer.P);
reqbuf->Reference = 1; /* Net Entity */
skb_queue_tail(&chan->e.X, skb);
skb_queue_tail(&card->sndq, skb2);
eicon_schedule_tx(card);
return (0);
}
void
idi_parse_edata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len)
{
eicon_t30_s *p = (eicon_t30_s *)buffer;
int i;
if (DebugVar & 128) {
char st[40];
eicon_log(ccard, 128, "rT30:len %d , size %d\n", len, sizeof(eicon_t30_s));
eicon_log(ccard, 128, "rT30:code = %x\n", p->code);
eicon_log(ccard, 128, "rT30:rate = %x\n", p->rate);
eicon_log(ccard, 128, "rT30:res = %x\n", p->resolution);
eicon_log(ccard, 128, "rT30:format = %x\n", p->format);
eicon_log(ccard, 128, "rT30:pages_low = %x\n", p->pages_low);
eicon_log(ccard, 128, "rT30:pages_high = %x\n", p->pages_high);
eicon_log(ccard, 128, "rT30:atf = %x\n", p->atf);
eicon_log(ccard, 128, "rT30:control_bits_low = %x\n", p->control_bits_low);
eicon_log(ccard, 128, "rT30:control_bits_high = %x\n", p->control_bits_high);
eicon_log(ccard, 128, "rT30:feature_bits_low = %x\n", p->feature_bits_low);
eicon_log(ccard, 128, "rT30:feature_bits_high = %x\n", p->feature_bits_high);
//eicon_log(ccard, 128, "rT30:universal_5 = %x\n", p->universal_5);
//eicon_log(ccard, 128, "rT30:universal_6 = %x\n", p->universal_6);
//eicon_log(ccard, 128, "rT30:universal_7 = %x\n", p->universal_7);
eicon_log(ccard, 128, "rT30:station_id_len = %x\n", p->station_id_len);
eicon_log(ccard, 128, "rT30:head_line_len = %x\n", p->head_line_len);
strlcpy(st, p->station_id, p->station_id_len + 1);
eicon_log(ccard, 128, "rT30:station_id = <%s>\n", st);
}
if (!chan->fax) {
eicon_log(ccard, 1, "idi_edata: parse to NULL fax struct, ERROR\n");
return;
}
chan->fax->code = p->code;
i = (p->station_id_len < FAXIDLEN) ? p->station_id_len : (FAXIDLEN - 1);
memcpy(chan->fax->r_id, p->station_id, i);
chan->fax->r_id[i] = 0;
chan->fax->r_resolution = p->resolution;
chan->fax->r_rate = p->rate - 1;
chan->fax->r_binary = 0; /* no binary support */
chan->fax->r_width = 0;
chan->fax->r_length = 2;
chan->fax->r_scantime = 0;
chan->fax->r_compression = 0;
chan->fax->r_ecm = 0;
if (p->feature_bits_low & T30_FEATURE_BIT_2D_CODING) {
chan->fax->r_compression = 1;
if (p->feature_bits_low & T30_FEATURE_BIT_UNCOMPR_ENABLED) {
chan->fax->r_compression = 2;
}
}
if (p->feature_bits_low & T30_FEATURE_BIT_T6_CODING) {
chan->fax->r_compression = 3;
}
if (p->feature_bits_low & T30_FEATURE_BIT_ECM) {
chan->fax->r_ecm = 2;
if (p->feature_bits_low & T30_FEATURE_BIT_ECM_64_BYTES)
chan->fax->r_ecm = 1;
}
}
void
idi_fax_send_header(eicon_card *card, eicon_chan *chan, int header)
{
static __u16 wd2sff[] = {
1728, 2048, 2432, 1216, 864
};
static __u16 ln2sff[2][3] = {
{ 1143, 1401, 0 } , { 2287, 2802, 0 }
};
struct sk_buff *skb;
eicon_sff_dochead *doc;
eicon_sff_pagehead *page;
u_char *docp;
if (!chan->fax) {
eicon_log(card, 1, "idi_fax: send head with NULL fax struct, ERROR\n");
return;
}
if (header == 2) { /* DocHeader + PageHeader */
skb = alloc_skb(sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead), GFP_ATOMIC);
} else {
skb = alloc_skb(sizeof(eicon_sff_pagehead), GFP_ATOMIC);
}
if (!skb) {
eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in fax_send_header()\n", chan->No);
return;
}
if (header == 2) { /* DocHeader + PageHeader */
docp = skb_put(skb, sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead));
doc = (eicon_sff_dochead *) docp;
page = (eicon_sff_pagehead *) (docp + sizeof(eicon_sff_dochead));
memset(docp, 0,sizeof(eicon_sff_dochead) + sizeof(eicon_sff_pagehead));
doc->id = 0x66666653;
doc->version = 0x01;
doc->off1pagehead = sizeof(eicon_sff_dochead);
} else {
page = (eicon_sff_pagehead *)skb_put(skb, sizeof(eicon_sff_pagehead));
memset(page, 0, sizeof(eicon_sff_pagehead));
}
switch(header) {
case 1: /* PageHeaderEnd */
page->pageheadid = 254;
page->pageheadlen = 0;
break;
case 0: /* PageHeader */
case 2: /* DocHeader + PageHeader */
page->pageheadid = 254;
page->pageheadlen = sizeof(eicon_sff_pagehead) - 2;
page->resvert = chan->fax->resolution;
page->reshoriz = 0; /* always 203 dpi */
page->coding = 0; /* always 1D */
page->linelength = wd2sff[chan->fax->width];
page->pagelength = ln2sff[chan->fax->resolution][chan->fax->length];
eicon_log(card, 128, "sSFF-Head: linelength = %d\n", page->linelength);
eicon_log(card, 128, "sSFF-Head: pagelength = %d\n", page->pagelength);
break;
}
idi_send_data(card, chan, 0, skb, 0, 0);
}
void
idi_fax_cmd(eicon_card *card, eicon_chan *chan)
{
isdn_ctrl cmd;
if ((!card) || (!chan))
return;
if (!chan->fax) {
eicon_log(card, 1, "idi_fax: cmd with NULL fax struct, ERROR\n");
return;
}
switch (chan->fax->code) {
case ISDN_TTY_FAX_DT:
if (chan->fax->phase == ISDN_FAX_PHASE_B) {
idi_send_edata(card, chan);
break;
}
if (chan->fax->phase == ISDN_FAX_PHASE_D) {
idi_send_edata(card, chan);
break;
}
break;
case ISDN_TTY_FAX_DR:
if (chan->fax->phase == ISDN_FAX_PHASE_B) {
idi_send_edata(card, chan);
cmd.driver = card->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_CFR;
card->interface.statcallb(&cmd);
cmd.driver = card->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_RID;
card->interface.statcallb(&cmd);
/* telling 1-D compression */
chan->fax->r_compression = 0;
cmd.driver = card->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_DCS;
card->interface.statcallb(&cmd);
chan->fax2.NextObject = FAX_OBJECT_DOCU;
chan->fax2.PrevObject = FAX_OBJECT_DOCU;
break;
}
if (chan->fax->phase == ISDN_FAX_PHASE_D) {
idi_send_edata(card, chan);
break;
}
break;
case ISDN_TTY_FAX_ET:
switch(chan->fax->fet) {
case 0:
case 1:
idi_fax_send_header(card, chan, 0);
break;
case 2:
idi_fax_send_header(card, chan, 1);
break;
}
break;
}
}
void
idi_edata_rcveop(eicon_card *card, eicon_chan *chan)
{
isdn_ctrl cmd;
if (!chan->fax) {
eicon_log(card, 1, "idi_edata: rcveop with NULL fax struct, ERROR\n");
return;
}
cmd.driver = card->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_ET;
card->interface.statcallb(&cmd);
}
void
idi_reset_fax_stat(eicon_chan *chan)
{
chan->fax2.LineLen = 0;
chan->fax2.LineData = 0;
chan->fax2.LineDataLen = 0;
chan->fax2.NullByteExist = 0;
chan->fax2.Dle = 0;
chan->fax2.PageCount = 0;
chan->fax2.Eop = 0;
}
void
idi_edata_action(eicon_card *ccard, eicon_chan *chan, char *buffer, int len)
{
isdn_ctrl cmd;
if (!chan->fax) {
eicon_log(ccard, 1, "idi_edata: action with NULL fax struct, ERROR\n");
return;
}
if (chan->fax->direction == ISDN_TTY_FAX_CONN_OUT) {
idi_parse_edata(ccard, chan, buffer, len);
if (chan->fax->phase == ISDN_FAX_PHASE_A) {
idi_reset_fax_stat(chan);
chan->fsm_state = EICON_STATE_ACTIVE;
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_BCONN;
cmd.arg = chan->No;
strcpy(cmd.parm.num, "");
ccard->interface.statcallb(&cmd);
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_FCON;
ccard->interface.statcallb(&cmd);
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_RID;
ccard->interface.statcallb(&cmd);
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_DIS;
ccard->interface.statcallb(&cmd);
if (chan->fax->r_compression != 0) {
/* telling fake compression in second DIS message */
chan->fax->r_compression = 0;
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_DIS;
ccard->interface.statcallb(&cmd);
}
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_SENT; /* OK message */
ccard->interface.statcallb(&cmd);
} else
if (chan->fax->phase == ISDN_FAX_PHASE_D) {
if ((chan->fax->code == EDATA_T30_MCF) &&
(chan->fax->fet != 2)) {
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_PTS;
ccard->interface.statcallb(&cmd);
}
switch(chan->fax->fet) {
case 0: /* new page */
/* stay in phase D , wait on cmd +FDT */
break;
case 1: /* new document */
/* link-level switch to phase B */
break;
case 2: /* session end */
default:
/* send_edata produces error on some */
/* fax-machines here, so we don't */
/* idi_send_edata(ccard, chan); */
break;
}
}
}
if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) {
idi_parse_edata(ccard, chan, buffer, len);
if ((chan->fax->code == EDATA_T30_DCS) &&
(chan->fax->phase == ISDN_FAX_PHASE_A)) {
idi_reset_fax_stat(chan);
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_BCONN;
cmd.arg = chan->No;
strcpy(cmd.parm.num, "");
ccard->interface.statcallb(&cmd);
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_FCON_I;
ccard->interface.statcallb(&cmd);
} else
if ((chan->fax->code == EDATA_T30_TRAIN_OK) &&
(chan->fax->phase == ISDN_FAX_PHASE_A)) {
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_RID;
ccard->interface.statcallb(&cmd);
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_TRAIN_OK;
ccard->interface.statcallb(&cmd);
} else
if ((chan->fax->code == EDATA_T30_TRAIN_OK) &&
(chan->fax->phase == ISDN_FAX_PHASE_B)) {
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_TRAIN_OK;
ccard->interface.statcallb(&cmd);
} else
if (chan->fax->phase == ISDN_FAX_PHASE_C) {
switch(chan->fax->code) {
case EDATA_T30_TRAIN_OK:
idi_send_edata(ccard, chan);
break;
case EDATA_T30_MPS:
chan->fax->fet = 0;
idi_edata_rcveop(ccard, chan);
break;
case EDATA_T30_EOM:
chan->fax->fet = 1;
idi_edata_rcveop(ccard, chan);
break;
case EDATA_T30_EOP:
chan->fax->fet = 2;
idi_edata_rcveop(ccard, chan);
break;
}
}
}
}
void
fax_put_rcv(eicon_card *ccard, eicon_chan *chan, u_char *Data, int len)
{
struct sk_buff *skb;
skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC);
if (!skb) {
eicon_log(ccard, 1, "idi_err: Ch%d: alloc_skb failed in fax_put_rcv()\n", chan->No);
return;
}
skb_reserve(skb, MAX_HEADER_LEN);
memcpy(skb_put(skb, len), Data, len);
ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb);
}
void
idi_faxdata_rcv(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb)
{
eicon_OBJBUFFER InBuf;
eicon_OBJBUFFER LineBuf;
unsigned int Length = 0;
unsigned int aLength = 0;
unsigned int ObjectSize = 0;
unsigned int ObjHeadLen = 0;
unsigned int ObjDataLen = 0;
__u8 Recordtype;
__u8 PageHeaderLen;
__u8 Event;
eicon_sff_pagehead *ob_page;
__u16 Cl2Eol = 0x8000;
# define EVENT_NONE 0
# define EVENT_NEEDDATA 1
if (!chan->fax) {
eicon_log(ccard, 1, "idi_fax: rcvdata with NULL fax struct, ERROR\n");
return;
}
if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) {
InBuf.Data = skb->data;
InBuf.Size = skb->len;
InBuf.Len = 0;
InBuf.Next = InBuf.Data;
LineBuf.Data = chan->fax2.abLine;
LineBuf.Size = sizeof(chan->fax2.abLine);
LineBuf.Len = chan->fax2.LineLen;
LineBuf.Next = LineBuf.Data + LineBuf.Len;
Event = EVENT_NONE;
while (Event == EVENT_NONE) {
switch(chan->fax2.NextObject) {
case FAX_OBJECT_DOCU:
Length = LineBuf.Len + (InBuf.Size - InBuf.Len);
if (Length < sizeof(eicon_sff_dochead)) {
Event = EVENT_NEEDDATA;
break;
}
ObjectSize = sizeof(eicon_sff_dochead);
Length = ObjectSize;
if (LineBuf.Len < Length) {
Length -= LineBuf.Len;
LineBuf.Len = 0;
LineBuf.Next = LineBuf.Data;
InBuf.Len += Length;
InBuf.Next += Length;
} else {
LineBuf.Len -= Length;
LineBuf.Next = LineBuf.Data + LineBuf.Len;
memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len);
}
chan->fax2.PrevObject = FAX_OBJECT_DOCU;
chan->fax2.NextObject = FAX_OBJECT_PAGE;
break;
case FAX_OBJECT_PAGE:
Length = LineBuf.Len + (InBuf.Size - InBuf.Len);
if (Length < 2) {
Event = EVENT_NEEDDATA;
break;
}
if (LineBuf.Len == 0) {
*LineBuf.Next++ = *InBuf.Next++;
LineBuf.Len++;
InBuf.Len++;
}
if (LineBuf.Len == 1) {
*LineBuf.Next++ = *InBuf.Next++;
LineBuf.Len++;
InBuf.Len++;
}
PageHeaderLen = *(LineBuf.Data + 1);
ObjectSize = (PageHeaderLen == 0) ? 2 : sizeof(eicon_sff_pagehead);
if (Length < ObjectSize) {
Event = EVENT_NEEDDATA;
break;
}
Length = ObjectSize;
/* extract page dimensions */
if (LineBuf.Len < Length) {
aLength = Length - LineBuf.Len;
memcpy(LineBuf.Next, InBuf.Next, aLength);
LineBuf.Next += aLength;
InBuf.Next += aLength;
LineBuf.Len += aLength;
InBuf.Len += aLength;
}
if (Length > 2) {
ob_page = (eicon_sff_pagehead *)LineBuf.Data;
switch(ob_page->linelength) {
case 2048:
chan->fax->r_width = 1;
break;
case 2432:
chan->fax->r_width = 2;
break;
case 1216:
chan->fax->r_width = 3;
break;
case 864:
chan->fax->r_width = 4;
break;
case 1728:
default:
chan->fax->r_width = 0;
}
switch(ob_page->pagelength) {
case 1143:
case 2287:
chan->fax->r_length = 0;
break;
case 1401:
case 2802:
chan->fax->r_length = 1;
break;
default:
chan->fax->r_length = 2;
}
eicon_log(ccard, 128, "rSFF-Head: linelength = %d\n", ob_page->linelength);
eicon_log(ccard, 128, "rSFF-Head: pagelength = %d\n", ob_page->pagelength);
}
LineBuf.Len -= Length;
LineBuf.Next = LineBuf.Data + LineBuf.Len;
memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len);
chan->fax2.PrevObject = FAX_OBJECT_PAGE;
chan->fax2.NextObject = FAX_OBJECT_LINE;
break;
case FAX_OBJECT_LINE:
Length = LineBuf.Len + (InBuf.Size - InBuf.Len);
if (Length < 1) {
Event = EVENT_NEEDDATA;
break;
}
if (LineBuf.Len == 0) {
*LineBuf.Next++ = *InBuf.Next++;
LineBuf.Len++;
InBuf.Len++;
}
Recordtype = *LineBuf.Data;
if (Recordtype == 0) {
/* recordtype pixel row (2 byte length) */
ObjHeadLen = 3;
if (Length < ObjHeadLen) {
Event = EVENT_NEEDDATA;
break;
}
while (LineBuf.Len < ObjHeadLen) {
*LineBuf.Next++ = *InBuf.Next++;
LineBuf.Len++;
InBuf.Len++;
}
ObjDataLen = *((__u16*) (LineBuf.Data + 1));
ObjectSize = ObjHeadLen + ObjDataLen;
if (Length < ObjectSize) {
Event = EVENT_NEEDDATA;
break;
}
} else
if ((Recordtype >= 1) && (Recordtype <= 216)) {
/* recordtype pixel row (1 byte length) */
ObjHeadLen = 1;
ObjDataLen = Recordtype;
ObjectSize = ObjHeadLen + ObjDataLen;
if (Length < ObjectSize) {
Event = EVENT_NEEDDATA;
break;
}
} else
if ((Recordtype >= 217) && (Recordtype <= 253)) {
/* recordtype empty lines */
ObjHeadLen = 1;
ObjDataLen = 0;
ObjectSize = ObjHeadLen + ObjDataLen;
LineBuf.Len--;
LineBuf.Next = LineBuf.Data + LineBuf.Len;
memmove(LineBuf.Data, LineBuf.Data + 1, LineBuf.Len);
break;
} else
if (Recordtype == 254) {
/* recordtype page header */
chan->fax2.PrevObject = FAX_OBJECT_LINE;
chan->fax2.NextObject = FAX_OBJECT_PAGE;
break;
} else {
/* recordtype user information */
ObjHeadLen = 2;
if (Length < ObjHeadLen) {
Event = EVENT_NEEDDATA;
break;
}
while (LineBuf.Len < ObjHeadLen) {
*LineBuf.Next++ = *InBuf.Next++;
LineBuf.Len++;
InBuf.Len++;
}
ObjDataLen = *(LineBuf.Data + 1);
ObjectSize = ObjHeadLen + ObjDataLen;
if (ObjDataLen == 0) {
/* illegal line coding */
LineBuf.Len -= ObjHeadLen;
LineBuf.Next = LineBuf.Data + LineBuf.Len;
memmove(LineBuf.Data, LineBuf.Data + ObjHeadLen, LineBuf.Len);
break;
} else {
/* user information */
if (Length < ObjectSize) {
Event = EVENT_NEEDDATA;
break;
}
Length = ObjectSize;
if (LineBuf.Len < Length) {
Length -= LineBuf.Len;
LineBuf.Len = 0;
LineBuf.Next = LineBuf.Data;
InBuf.Len += Length;
InBuf.Next += Length;
} else {
LineBuf.Len -= Length;
LineBuf.Next = LineBuf.Data + LineBuf.Len;
memmove(LineBuf.Data, LineBuf.Data + Length, LineBuf.Len);
}
}
break;
}
Length = ObjectSize;
if (LineBuf.Len > ObjHeadLen) {
fax_put_rcv(ccard, chan, LineBuf.Data + ObjHeadLen,
(LineBuf.Len - ObjHeadLen));
}
Length -= LineBuf.Len;
LineBuf.Len = 0;
LineBuf.Next = LineBuf.Data;
if (Length > 0) {
fax_put_rcv(ccard, chan, InBuf.Next, Length);
InBuf.Len += Length;
InBuf.Next += Length;
}
fax_put_rcv(ccard, chan, (__u8 *)&Cl2Eol, sizeof(Cl2Eol));
break;
} /* end of switch (chan->fax2.NextObject) */
} /* end of while (Event==EVENT_NONE) */
if (InBuf.Len < InBuf.Size) {
Length = InBuf.Size - InBuf.Len;
if ((LineBuf.Len + Length) > LineBuf.Size) {
eicon_log(ccard, 1, "idi_fax: Ch%d: %d bytes dropping, small buffer\n", chan->No,
Length);
} else {
memcpy(LineBuf.Next, InBuf.Next, Length);
LineBuf.Len += Length;
}
}
chan->fax2.LineLen = LineBuf.Len;
} else { /* CONN_OUT */
/* On CONN_OUT we do not need incoming data, drop it */
/* maybe later for polling */
}
# undef EVENT_NONE
# undef EVENT_NEEDDATA
return;
}
int
idi_fax_send_outbuf(eicon_card *ccard, eicon_chan *chan, eicon_OBJBUFFER *OutBuf)
{
struct sk_buff *skb;
skb = alloc_skb(OutBuf->Len, GFP_ATOMIC);
if (!skb) {
eicon_log(ccard, 1, "idi_err: Ch%d: alloc_skb failed in fax_send_outbuf()\n", chan->No);
return(-1);
}
memcpy(skb_put(skb, OutBuf->Len), OutBuf->Data, OutBuf->Len);
OutBuf->Len = 0;
OutBuf->Next = OutBuf->Data;
return(idi_send_data(ccard, chan, 0, skb, 1, 0));
}
int
idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb)
{
isdn_ctrl cmd;
eicon_OBJBUFFER InBuf;
__u8 InData;
__u8 InMask;
eicon_OBJBUFFER OutBuf;
eicon_OBJBUFFER LineBuf;
__u32 LineData;
unsigned int LineDataLen;
__u8 Byte;
__u8 Event;
int ret = 1;
# define EVENT_NONE 0
# define EVENT_EOD 1
# define EVENT_EOL 2
# define EVENT_EOP 3
if ((!ccard) || (!chan))
return -1;
if (!chan->fax) {
eicon_log(ccard, 1, "idi_fax: senddata with NULL fax struct, ERROR\n");
return -1;
}
if (chan->fax->direction == ISDN_TTY_FAX_CONN_IN) {
/* Simply ignore any data written in data mode when receiving a fax. */
/* This is not completely correct because only XON's should come here. */
dev_kfree_skb(skb);
return 1;
}
if (chan->fax->phase != ISDN_FAX_PHASE_C) {
dev_kfree_skb(skb);
return 1;
}
if (chan->queued + skb->len > 1200)
return 0;
if (chan->pqueued > 1)
return 0;
InBuf.Data = skb->data;
InBuf.Size = skb->len;
InBuf.Len = 0;
InBuf.Next = InBuf.Data;
InData = 0;
InMask = 0;
LineBuf.Data = chan->fax2.abLine;
LineBuf.Size = sizeof(chan->fax2.abLine);
LineBuf.Len = chan->fax2.LineLen;
LineBuf.Next = LineBuf.Data + LineBuf.Len;
LineData = chan->fax2.LineData;
LineDataLen = chan->fax2.LineDataLen;
OutBuf.Data = chan->fax2.abFrame;
OutBuf.Size = sizeof(chan->fax2.abFrame);
OutBuf.Len = 0;
OutBuf.Next = OutBuf.Data;
Event = EVENT_NONE;
chan->fax2.Eop = 0;
for (;;) {
for (;;) {
if (InMask == 0) {
if (InBuf.Len >= InBuf.Size) {
Event = EVENT_EOD;
break;
}
if ((chan->fax2.Dle != _DLE_) && *InBuf.Next == _DLE_) {
chan->fax2.Dle = _DLE_;
InBuf.Next++;
InBuf.Len++;
if (InBuf.Len >= InBuf.Size) {
Event = EVENT_EOD;
break;
}
}
if (chan->fax2.Dle == _DLE_) {
chan->fax2.Dle = 0;
if (*InBuf.Next == _ETX_) {
Event = EVENT_EOP;
break;
} else
if (*InBuf.Next == _DLE_) {
/* do nothing */
} else {
eicon_log(ccard, 1,
"idi_err: Ch%d: unknown DLE escape %02x found\n",
chan->No, *InBuf.Next);
InBuf.Next++;
InBuf.Len++;
if (InBuf.Len >= InBuf.Size) {
Event = EVENT_EOD;
break;
}
}
}
InBuf.Len++;
InData = *InBuf.Next++;
InMask = (chan->fax->bor) ? 0x80 : 0x01;
}
while (InMask) {
LineData >>= 1;
LineDataLen++;
if (InData & InMask)
LineData |= 0x80000000;
if (chan->fax->bor)
InMask >>= 1;
else
InMask <<= 1;
if ((LineDataLen >= T4_EOL_BITSIZE) &&
((LineData & T4_EOL_MASK_DWORD) == T4_EOL_DWORD)) {
Event = EVENT_EOL;
if (LineDataLen > T4_EOL_BITSIZE) {
Byte = (__u8)
((LineData & ~T4_EOL_MASK_DWORD) >>
(32 - LineDataLen));
if (Byte == 0) {
if (! chan->fax2.NullByteExist) {
chan->fax2.NullBytesPos = LineBuf.Len;
chan->fax2.NullByteExist = 1;
}
} else {
chan->fax2.NullByteExist = 0;
}
if (LineBuf.Len < LineBuf.Size) {
*LineBuf.Next++ = Byte;
LineBuf.Len++;
}
}
LineDataLen = 0;
break;
}
if (LineDataLen >= T4_EOL_BITSIZE + 8) {
Byte = (__u8)
((LineData & ~T4_EOL_MASK_DWORD) >>
(32 - T4_EOL_BITSIZE - 8));
LineData &= T4_EOL_MASK_DWORD;
LineDataLen = T4_EOL_BITSIZE;
if (Byte == 0) {
if (! chan->fax2.NullByteExist) {
chan->fax2.NullBytesPos = LineBuf.Len;
chan->fax2.NullByteExist = 1;
}
} else {
chan->fax2.NullByteExist = 0;
}
if (LineBuf.Len < LineBuf.Size) {
*LineBuf.Next++ = Byte;
LineBuf.Len++;
}
}
}
if (Event != EVENT_NONE)
break;
}
if ((Event != EVENT_EOL) && (Event != EVENT_EOP))
break;
if ((Event == EVENT_EOP) && (LineDataLen > 0)) {
LineData >>= 32 - LineDataLen;
LineDataLen = 0;
while (LineData != 0) {
Byte = (__u8) LineData;
LineData >>= 8;
if (Byte == 0) {
if (! chan->fax2.NullByteExist) {
chan->fax2.NullBytesPos = LineBuf.Len;
chan->fax2.NullByteExist = 1;
}
} else {
chan->fax2.NullByteExist = 0;
}
if (LineBuf.Len < LineBuf.Size) {
*LineBuf.Next++ = Byte;
LineBuf.Len++;
}
}
}
if (chan->fax2.NullByteExist) {
if (chan->fax2.NullBytesPos == 0) {
LineBuf.Len = 0;
} else {
LineBuf.Len = chan->fax2.NullBytesPos + 1;
}
}
if (LineBuf.Len > 0) {
if (OutBuf.Len + LineBuf.Len + SFF_LEN_FLD_SIZE > OutBuf.Size) {
ret = idi_fax_send_outbuf(ccard, chan, &OutBuf);
}
if (LineBuf.Len <= 216) {
*OutBuf.Next++ = (__u8) LineBuf.Len;
OutBuf.Len++;
} else {
*OutBuf.Next++ = 0;
*((__u16 *) OutBuf.Next)++ = (__u16) LineBuf.Len;
OutBuf.Len += 3;
}
memcpy(OutBuf.Next, LineBuf.Data, LineBuf.Len);
OutBuf.Next += LineBuf.Len;
OutBuf.Len += LineBuf.Len;
}
LineBuf.Len = 0;
LineBuf.Next = LineBuf.Data;
chan->fax2.NullByteExist = 0;
if (Event == EVENT_EOP)
break;
Event = EVENT_NONE;
}
if (Event == EVENT_EOP) {
chan->fax2.Eop = 1;
chan->fax2.PageCount++;
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_EOP;
ccard->interface.statcallb(&cmd);
}
if (OutBuf.Len > 0) {
ret = idi_fax_send_outbuf(ccard, chan, &OutBuf);
}
chan->fax2.LineLen = LineBuf.Len;
chan->fax2.LineData = LineData;
chan->fax2.LineDataLen = LineDataLen;
# undef EVENT_NONE
# undef EVENT_EOD
# undef EVENT_EOL
# undef EVENT_EOP
if (ret >= 0)
dev_kfree_skb(skb);
if (ret == 0)
ret = 1;
return(ret);
}
void
idi_fax_hangup(eicon_card *ccard, eicon_chan *chan)
{
isdn_ctrl cmd;
if (!chan->fax) {
eicon_log(ccard, 1, "idi_fax: hangup with NULL fax struct, ERROR\n");
return;
}
if ((chan->fax->direction == ISDN_TTY_FAX_CONN_OUT) &&
(chan->fax->code == 0)) {
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_PTS;
ccard->interface.statcallb(&cmd);
}
if ((chan->fax->code > 1) && (chan->fax->code < 120))
chan->fax->code += 120;
eicon_log(ccard, 8, "idi_fax: Ch%d: Hangup (code=%d)\n", chan->No, chan->fax->code);
chan->fax->r_code = ISDN_TTY_FAX_HNG;
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
ccard->interface.statcallb(&cmd);
}
#endif /******** FAX ********/
int
idi_send_udata(eicon_card *card, eicon_chan *chan, int UReq, u_char *buffer, int len)
{
struct sk_buff *skb;
struct sk_buff *skb2;
eicon_REQ *reqbuf;
eicon_chan_ptr *chan2;
if ((chan->fsm_state == EICON_STATE_NULL) || (chan->fsm_state == EICON_STATE_LISTEN)) {
eicon_log(card, 1, "idi_snd: Ch%d: send udata on state %d !\n", chan->No, chan->fsm_state);
return -ENODEV;
}
eicon_log(card, 8, "idi_snd: Ch%d: udata 0x%x: %d %d %d %d\n", chan->No,
UReq, buffer[0], buffer[1], buffer[2], buffer[3]);
skb = alloc_skb(sizeof(eicon_REQ) + len + 1, GFP_ATOMIC);
skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
if ((!skb) || (!skb2)) {
eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_udata()\n", chan->No);
if (skb)
dev_kfree_skb(skb);
if (skb2)
dev_kfree_skb(skb2);
return -ENOMEM;
}
chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
chan2->ptr = chan;
reqbuf = (eicon_REQ *)skb_put(skb, 1 + len + sizeof(eicon_REQ));
reqbuf->Req = N_UDATA;
reqbuf->ReqCh = chan->e.IndCh;
reqbuf->ReqId = 1;
reqbuf->XBuffer.length = len + 1;
reqbuf->XBuffer.P[0] = UReq;
memcpy(&reqbuf->XBuffer.P[1], buffer, len);
reqbuf->Reference = 1; /* Net Entity */
skb_queue_tail(&chan->e.X, skb);
skb_queue_tail(&card->sndq, skb2);
eicon_schedule_tx(card);
return (0);
}
void
idi_audio_cmd(eicon_card *ccard, eicon_chan *chan, int cmd, u_char *value)
{
u_char buf[6];
struct enable_dtmf_s *dtmf_buf = (struct enable_dtmf_s *)buf;
if ((!ccard) || (!chan))
return;
memset(buf, 0, 6);
switch(cmd) {
case ISDN_AUDIO_SETDD:
if (value[0]) {
dtmf_buf->tone = (__u16) (value[1] * 5);
dtmf_buf->gap = (__u16) (value[1] * 5);
idi_send_udata(ccard, chan,
DSP_UDATA_REQUEST_ENABLE_DTMF_RECEIVER,
buf, 4);
} else {
idi_send_udata(ccard, chan,
DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER,
buf, 0);
}
break;
}
}
void
idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len)
{
isdn_ctrl cmd;
eicon_dsp_ind *p = (eicon_dsp_ind *) (&buffer[1]);
static char *connmsg[] =
{"", "V.21", "V.23", "V.22", "V.22bis", "V.32bis", "V.34",
"V.8", "Bell 212A", "Bell 103", "V.29 Leased", "V.33 Leased", "V.90",
"V.21 CH2", "V.27ter", "V.29", "V.33", "V.17", "V.32", "K56Flex",
"X2", "V.18", "V.18LH", "V.18HL", "V.21LH", "V.21HL",
"Bell 103LH", "Bell 103HL", "V.23", "V.23", "EDT 110",
"Baudot45", "Baudot47", "Baudot50", "DTMF" };
static u_char dtmf_code[] = {
'1','4','7','*','2','5','8','0','3','6','9','#','A','B','C','D'
};
if ((!ccard) || (!chan))
return;
switch (buffer[0]) {
case DSP_UDATA_INDICATION_SYNC:
eicon_log(ccard, 16, "idi_ind: Ch%d: UDATA_SYNC time %d\n", chan->No, p->time);
break;
case DSP_UDATA_INDICATION_DCD_OFF:
eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DCD_OFF time %d\n", chan->No, p->time);
break;
case DSP_UDATA_INDICATION_DCD_ON:
if ((chan->l2prot == ISDN_PROTO_L2_MODEM) &&
(chan->fsm_state == EICON_STATE_WMCONN)) {
chan->fsm_state = EICON_STATE_ACTIVE;
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_BCONN;
cmd.arg = chan->No;
if (p->norm > 34) {
sprintf(cmd.parm.num, "%d/(%d)", p->speed, p->norm);
} else {
sprintf(cmd.parm.num, "%d/%s", p->speed, connmsg[p->norm]);
}
ccard->interface.statcallb(&cmd);
}
eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DCD_ON time %d\n", chan->No, p->time);
eicon_log(ccard, 8, "idi_ind: Ch%d: %d %d %d %d\n", chan->No,
p->norm, p->options, p->speed, p->delay);
break;
case DSP_UDATA_INDICATION_CTS_OFF:
eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_CTS_OFF time %d\n", chan->No, p->time);
break;
case DSP_UDATA_INDICATION_CTS_ON:
eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_CTS_ON time %d\n", chan->No, p->time);
eicon_log(ccard, 8, "idi_ind: Ch%d: %d %d %d %d\n", chan->No,
p->norm, p->options, p->speed, p->delay);
break;
case DSP_UDATA_INDICATION_DISCONNECT:
eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]);
break;
case DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED:
eicon_log(ccard, 8, "idi_ind: Ch%d: UDATA_DTMF_REC '%c'\n", chan->No,
dtmf_code[buffer[1]]);
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_AUDIO;
cmd.parm.num[0] = ISDN_AUDIO_DTMF;
cmd.parm.num[1] = dtmf_code[buffer[1]];
cmd.arg = chan->No;
ccard->interface.statcallb(&cmd);
break;
default:
eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]);
}
}
void
eicon_parse_trace(eicon_card *ccard, unsigned char *buffer, int len)
{
int i,j,n;
int buflen = len * 3 + 30;
char *p;
struct trace_s {
unsigned long time;
unsigned short size;
unsigned short code;
unsigned char data[1];
} *q;
if (!(p = kmalloc(buflen, GFP_ATOMIC))) {
eicon_log(ccard, 1, "idi_err: Ch??: could not allocate trace buffer\n");
return;
}
memset(p, 0, buflen);
q = (struct trace_s *)buffer;
if (DebugVar & 512) {
if ((q->code == 3) || (q->code == 4)) {
n = (short) *(q->data);
if (n) {
j = sprintf(p, "DTRC:");
for (i = 0; i < n; i++) {
j += sprintf(p + j, "%02x ", q->data[i+2]);
}
j += sprintf(p + j, "\n");
}
}
} else {
j = sprintf(p, "XLOG: %lx %04x %04x ",
q->time, q->size, q->code);
for (i = 0; i < q->size; i++) {
j += sprintf(p + j, "%02x ", q->data[i]);
}
j += sprintf(p + j, "\n");
}
if (strlen(p))
eicon_putstatus(ccard, p);
kfree(p);
}
void
idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
{
int tmp;
char tnum[64];
int dlev;
int free_buff;
ulong flags;
struct sk_buff *skb2;
eicon_IND *ind = (eicon_IND *)skb->data;
eicon_chan *chan;
idi_ind_message message;
isdn_ctrl cmd;
if (!ccard) {
eicon_log(ccard, 1, "idi_err: Ch??: null card in handle_ind\n");
dev_kfree_skb(skb);
return;
}
if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
eicon_log(ccard, 1, "idi_err: Ch??: null chan in handle_ind\n");
dev_kfree_skb(skb);
return;
}
if ((ind->Ind != 8) && (ind->Ind != 0xc))
dlev = 144;
else
dlev = 128;
eicon_log(ccard, dlev, "idi_hdl: Ch%d: Ind=%x Id=%x Ch=%x MInd=%x MLen=%x Len=%x\n", chan->No,
ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
free_buff = 1;
/* Signal Layer */
if (chan->e.D3Id == ind->IndId) {
idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
switch(ind->Ind) {
case HANGUP:
eicon_log(ccard, 8, "idi_ind: Ch%d: Hangup\n", chan->No);
while((skb2 = skb_dequeue(&chan->e.X))) {
dev_kfree_skb(skb2);
}
spin_lock_irqsave(&eicon_lock, flags);
chan->queued = 0;
chan->pqueued = 0;
chan->waitq = 0;
chan->waitpq = 0;
spin_unlock_irqrestore(&eicon_lock, flags);
if (message.e_cau[0] & 0x7f) {
cmd.driver = ccard->myid;
cmd.arg = chan->No;
sprintf(cmd.parm.num,"E%02x%02x",
chan->cause[0]&0x7f, message.e_cau[0]&0x7f);
cmd.command = ISDN_STAT_CAUSE;
ccard->interface.statcallb(&cmd);
}
chan->cause[0] = 0;
if (((chan->fsm_state == EICON_STATE_ACTIVE) ||
(chan->fsm_state == EICON_STATE_WMCONN)) ||
((chan->l2prot == ISDN_PROTO_L2_FAX) &&
(chan->fsm_state == EICON_STATE_OBWAIT))) {
chan->fsm_state = EICON_STATE_NULL;
} else {
if (chan->e.B2Id)
idi_do_req(ccard, chan, REMOVE, 1);
chan->statectrl &= ~WAITING_FOR_HANGUP;
chan->statectrl &= ~IN_HOLD;
if (chan->statectrl & HAVE_CONN_REQ) {
eicon_log(ccard, 32, "idi_req: Ch%d: queueing delayed conn_req\n", chan->No);
chan->statectrl &= ~HAVE_CONN_REQ;
if ((chan->tskb1) && (chan->tskb2)) {
skb_queue_tail(&chan->e.X, chan->tskb1);
skb_queue_tail(&ccard->sndq, chan->tskb2);
eicon_schedule_tx(ccard);
}
chan->tskb1 = NULL;
chan->tskb2 = NULL;
} else {
chan->fsm_state = EICON_STATE_NULL;
cmd.driver = ccard->myid;
cmd.arg = chan->No;
cmd.command = ISDN_STAT_DHUP;
ccard->interface.statcallb(&cmd);
eicon_idi_listen_req(ccard, chan);
#ifdef CONFIG_ISDN_TTY_FAX
chan->fax = 0;
#endif
}
}
break;
case INDICATE_IND:
eicon_log(ccard, 8, "idi_ind: Ch%d: Indicate_Ind\n", chan->No);
if (chan->fsm_state != EICON_STATE_LISTEN) {
eicon_log(ccard, 1, "idi_err: Ch%d: Incoming call on wrong state (%d).\n",
chan->No, chan->fsm_state);
idi_do_req(ccard, chan, HANGUP, 0);
break;
}
chan->fsm_state = EICON_STATE_ICALL;
idi_bc2si(message.bc, message.hlc, message.sin, &chan->si1, &chan->si2);
strcpy(chan->cpn, message.cpn + 1);
strcpy(chan->oad, message.oad);
strcpy(chan->dsa, message.dsa);
strcpy(chan->osa, message.osa);
chan->plan = message.plan;
chan->screen = message.screen;
try_stat_icall_again:
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_ICALL;
cmd.arg = chan->No;
cmd.parm.setup.si1 = chan->si1;
cmd.parm.setup.si2 = chan->si2;
strcpy(tnum, chan->cpn);
if (strlen(chan->dsa)) {
strcat(tnum, ".");
strcat(tnum, chan->dsa);
}
tnum[ISDN_MSNLEN - 1] = 0;
strcpy(cmd.parm.setup.eazmsn, tnum);
strcpy(tnum, chan->oad);
if (strlen(chan->osa)) {
strcat(tnum, ".");
strcat(tnum, chan->osa);
}
tnum[ISDN_MSNLEN - 1] = 0;
strcpy(cmd.parm.setup.phone, tnum);
cmd.parm.setup.plan = chan->plan;
cmd.parm.setup.screen = chan->screen;
tmp = ccard->interface.statcallb(&cmd);
switch(tmp) {
case 0: /* no user responding */
idi_do_req(ccard, chan, HANGUP, 0);
chan->fsm_state = EICON_STATE_NULL;
break;
case 1: /* alert */
eicon_log(ccard, 8, "idi_req: Ch%d: Call Alert\n", chan->No);
if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_ICALLW)) {
chan->fsm_state = EICON_STATE_ICALL;
idi_do_req(ccard, chan, CALL_ALERT, 0);
}
break;
case 2: /* reject */
eicon_log(ccard, 8, "idi_req: Ch%d: Call Reject\n", chan->No);
idi_do_req(ccard, chan, REJECT, 0);
break;
case 3: /* incomplete number */
eicon_log(ccard, 8, "idi_req: Ch%d: Incomplete Number\n", chan->No);
chan->fsm_state = EICON_STATE_ICALLW;
break;
}
break;
case INFO_IND:
eicon_log(ccard, 8, "idi_ind: Ch%d: Info_Ind\n", chan->No);
if ((chan->fsm_state == EICON_STATE_ICALLW) &&
(message.cpn[0])) {
strcat(chan->cpn, message.cpn + 1);
goto try_stat_icall_again;
}
break;
case CALL_IND:
eicon_log(ccard, 8, "idi_ind: Ch%d: Call_Ind\n", chan->No);
if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_IWAIT)) {
chan->fsm_state = EICON_STATE_IBWAIT;
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_DCONN;
cmd.arg = chan->No;
ccard->interface.statcallb(&cmd);
switch(chan->l2prot) {
case ISDN_PROTO_L2_FAX:
#ifdef CONFIG_ISDN_TTY_FAX
if (chan->fax)
chan->fax->phase = ISDN_FAX_PHASE_A;
#endif
break;
case ISDN_PROTO_L2_MODEM:
/* do nothing, wait for connect */
break;
case ISDN_PROTO_L2_V11096:
case ISDN_PROTO_L2_V11019:
case ISDN_PROTO_L2_V11038:
case ISDN_PROTO_L2_TRANS:
idi_do_req(ccard, chan, N_CONNECT, 1);
break;
default:;
/* On most incoming calls we use automatic connect */
/* idi_do_req(ccard, chan, N_CONNECT, 1); */
}
} else {
if (chan->fsm_state != EICON_STATE_ACTIVE)
idi_hangup(ccard, chan);
}
break;
case CALL_CON:
eicon_log(ccard, 8, "idi_ind: Ch%d: Call_Con\n", chan->No);
if (chan->fsm_state == EICON_STATE_OCALL) {
/* check if old NetID has been removed */
if (chan->e.B2Id) {
eicon_log(ccard, 1, "eicon: Ch%d: old net_id %x still exist, removing.\n",
chan->No, chan->e.B2Id);
idi_do_req(ccard, chan, REMOVE, 1);
}
#ifdef CONFIG_ISDN_TTY_FAX
if (chan->l2prot == ISDN_PROTO_L2_FAX) {
if (chan->fax) {
chan->fax->phase = ISDN_FAX_PHASE_A;
} else {
eicon_log(ccard, 1, "idi_ind: Call_Con with NULL fax struct, ERROR\n");
idi_hangup(ccard, chan);
break;
}
}
#endif
chan->fsm_state = EICON_STATE_OBWAIT;
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_DCONN;
cmd.arg = chan->No;
ccard->interface.statcallb(&cmd);
idi_do_req(ccard, chan, ASSIGN, 1);
idi_do_req(ccard, chan, N_CONNECT, 1);
} else
idi_hangup(ccard, chan);
break;
case AOC_IND:
eicon_log(ccard, 8, "idi_ind: Ch%d: Advice of Charge\n", chan->No);
break;
case CALL_HOLD_ACK:
chan->statectrl |= IN_HOLD;
eicon_log(ccard, 8, "idi_ind: Ch%d: Call Hold Ack\n", chan->No);
break;
case SUSPEND_REJ:
eicon_log(ccard, 8, "idi_ind: Ch%d: Suspend Rejected\n", chan->No);
break;
case SUSPEND:
eicon_log(ccard, 8, "idi_ind: Ch%d: Suspend Ack\n", chan->No);
break;
case RESUME:
eicon_log(ccard, 8, "idi_ind: Ch%d: Resume Ack\n", chan->No);
break;
default:
eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED SigIndication 0x%02x\n", chan->No, ind->Ind);
}
}
/* Network Layer */
else if (chan->e.B2Id == ind->IndId) {
if (chan->No == ccard->nchannels) {
/* Management Indication */
if (ind->Ind == 0x04) { /* Trace_Ind */
eicon_parse_trace(ccard, ind->RBuffer.P, ind->RBuffer.length);
} else {
idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
chan->fsm_state = 1;
}
}
else
switch(ind->Ind) {
case N_CONNECT_ACK:
eicon_log(ccard, 16, "idi_ind: Ch%d: N_Connect_Ack\n", chan->No);
if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
chan->fsm_state = EICON_STATE_WMCONN;
break;
}
if (chan->l2prot == ISDN_PROTO_L2_FAX) {
#ifdef CONFIG_ISDN_TTY_FAX
chan->fsm_state = EICON_STATE_ACTIVE;
idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
if (chan->fax) {
if (chan->fax->phase == ISDN_FAX_PHASE_B) {
idi_fax_send_header(ccard, chan, 2);
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_DCS;
ccard->interface.statcallb(&cmd);
}
}
else {
eicon_log(ccard, 1, "idi_ind: N_Connect_Ack with NULL fax struct, ERROR\n");
}
#endif
break;
}
chan->fsm_state = EICON_STATE_ACTIVE;
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_BCONN;
cmd.arg = chan->No;
strcpy(cmd.parm.num, "64000");
ccard->interface.statcallb(&cmd);
break;
case N_CONNECT:
eicon_log(ccard, 16,"idi_ind: Ch%d: N_Connect\n", chan->No);
chan->e.IndCh = ind->IndCh;
if (chan->e.B2Id) idi_do_req(ccard, chan, N_CONNECT_ACK, 1);
if (chan->l2prot == ISDN_PROTO_L2_FAX) {
break;
}
if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
chan->fsm_state = EICON_STATE_WMCONN;
break;
}
chan->fsm_state = EICON_STATE_ACTIVE;
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_BCONN;
cmd.arg = chan->No;
strcpy(cmd.parm.num, "64000");
ccard->interface.statcallb(&cmd);
break;
case N_DISC:
eicon_log(ccard, 16, "idi_ind: Ch%d: N_Disc\n", chan->No);
if (chan->e.B2Id) {
while((skb2 = skb_dequeue(&chan->e.X))) {
dev_kfree_skb(skb2);
}
idi_do_req(ccard, chan, N_DISC_ACK, 1);
idi_do_req(ccard, chan, REMOVE, 1);
}
#ifdef CONFIG_ISDN_TTY_FAX
if ((chan->l2prot == ISDN_PROTO_L2_FAX) && (chan->fax)){
idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
idi_fax_hangup(ccard, chan);
}
#endif
chan->e.IndCh = 0;
spin_lock_irqsave(&eicon_lock, flags);
chan->queued = 0;
chan->pqueued = 0;
chan->waitq = 0;
chan->waitpq = 0;
spin_unlock_irqrestore(&eicon_lock, flags);
if (!(chan->statectrl & IN_HOLD)) {
idi_do_req(ccard, chan, HANGUP, 0);
}
if (chan->fsm_state == EICON_STATE_ACTIVE) {
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_BHUP;
cmd.arg = chan->No;
ccard->interface.statcallb(&cmd);
chan->fsm_state = EICON_STATE_NULL;
if (!(chan->statectrl & IN_HOLD)) {
chan->statectrl |= WAITING_FOR_HANGUP;
}
}
#ifdef CONFIG_ISDN_TTY_FAX
chan->fax = 0;
#endif
break;
case N_DISC_ACK:
eicon_log(ccard, 16, "idi_ind: Ch%d: N_Disc_Ack\n", chan->No);
#ifdef CONFIG_ISDN_TTY_FAX
if (chan->l2prot == ISDN_PROTO_L2_FAX) {
idi_parse_edata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
idi_fax_hangup(ccard, chan);
}
#endif
break;
case N_DATA_ACK:
eicon_log(ccard, 128, "idi_ind: Ch%d: N_Data_Ack\n", chan->No);
break;
case N_DATA:
skb_pull(skb, sizeof(eicon_IND) - 1);
eicon_log(ccard, 128, "idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len);
if (chan->l2prot == ISDN_PROTO_L2_FAX) {
#ifdef CONFIG_ISDN_TTY_FAX
idi_faxdata_rcv(ccard, chan, skb);
#endif
} else {
ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb);
free_buff = 0;
}
break;
case N_UDATA:
idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
break;
#ifdef CONFIG_ISDN_TTY_FAX
case N_EDATA:
idi_edata_action(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
break;
#endif
default:
eicon_log(ccard, 8, "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind);
}
}
else {
eicon_log(ccard, 1, "idi_ind: Ch%d: Ind is neither SIG nor NET !\n", chan->No);
}
if (free_buff)
dev_kfree_skb(skb);
}
int
idi_handle_ack_ok(eicon_card *ccard, eicon_chan *chan, eicon_RC *ack)
{
ulong flags;
isdn_ctrl cmd;
int tqueued = 0;
int twaitpq = 0;
if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) {
/* I don't know why this happens, should not ! */
/* just ignoring this RC */
eicon_log(ccard, 16, "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No,
ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id);
return 1;
}
/* Management Interface */
if (chan->No == ccard->nchannels) {
/* Managementinterface: changing state */
if (chan->e.Req != 0x02)
chan->fsm_state = 1;
}
/* Remove an Id */
if (chan->e.Req == REMOVE) {
if (ack->Reference != chan->e.ref) {
/* This should not happen anymore */
eicon_log(ccard, 16, "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No,
ack->Reference, chan->e.ref);
}
spin_lock_irqsave(&eicon_lock, flags);
ccard->IdTable[ack->RcId] = NULL;
if (!chan->e.ReqCh)
chan->e.D3Id = 0;
else
chan->e.B2Id = 0;
spin_unlock_irqrestore(&eicon_lock, flags);
eicon_log(ccard, 16, "idi_ack: Ch%d: Removed : Id=%x Ch=%d (%s)\n", chan->No,
ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig");
return 1;
}
/* Signal layer */
if (!chan->e.ReqCh) {
eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
ack->RcId, ack->RcCh, ack->Reference);
} else {
/* Network layer */
switch(chan->e.Req & 0x0f) {
case N_CONNECT:
chan->e.IndCh = ack->RcCh;
eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
ack->RcId, ack->RcCh, ack->Reference);
break;
case N_MDATA:
case N_DATA:
tqueued = chan->queued;
twaitpq = chan->waitpq;
if ((chan->e.Req & 0x0f) == N_DATA) {
spin_lock_irqsave(&eicon_lock, flags);
chan->waitpq = 0;
if(chan->pqueued)
chan->pqueued--;
spin_unlock_irqrestore(&eicon_lock, flags);
#ifdef CONFIG_ISDN_TTY_FAX
if (chan->l2prot == ISDN_PROTO_L2_FAX) {
if (((chan->queued - chan->waitq) < 1) &&
(chan->fax2.Eop)) {
chan->fax2.Eop = 0;
if (chan->fax) {
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_FAXIND;
cmd.arg = chan->No;
chan->fax->r_code = ISDN_TTY_FAX_SENT;
ccard->interface.statcallb(&cmd);
}
else {
eicon_log(ccard, 1, "idi_ack: Sent with NULL fax struct, ERROR\n");
}
}
}
#endif
}
spin_lock_irqsave(&eicon_lock, flags);
chan->queued -= chan->waitq;
if (chan->queued < 0) chan->queued = 0;
spin_unlock_irqrestore(&eicon_lock, flags);
if (((chan->e.Req & 0x0f) == N_DATA) && (tqueued)) {
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_BSENT;
cmd.arg = chan->No;
cmd.parm.length = twaitpq;
ccard->interface.statcallb(&cmd);
}
break;
default:
eicon_log(ccard, 16, "idi_ack: Ch%d: RC OK Id=%x Ch=%d (ref:%d)\n", chan->No,
ack->RcId, ack->RcCh, ack->Reference);
}
}
return 1;
}
void
idi_handle_ack(eicon_card *ccard, struct sk_buff *skb)
{
int j;
ulong flags;
eicon_RC *ack = (eicon_RC *)skb->data;
eicon_chan *chan;
isdn_ctrl cmd;
int dCh = -1;
if (!ccard) {
eicon_log(ccard, 1, "idi_err: Ch??: null card in handle_ack\n");
dev_kfree_skb(skb);
return;
}
spin_lock_irqsave(&eicon_lock, flags);
if ((chan = ccard->IdTable[ack->RcId]) != NULL)
dCh = chan->No;
spin_unlock_irqrestore(&eicon_lock, flags);
switch (ack->Rc) {
case OK_FC:
case N_FLOW_CONTROL:
case ASSIGN_RC:
eicon_log(ccard, 1, "idi_ack: Ch%d: unhandled RC 0x%x\n",
dCh, ack->Rc);
break;
case READY_INT:
case TIMER_INT:
/* we do nothing here */
break;
case OK:
if (!chan) {
eicon_log(ccard, 1, "idi_ack: Ch%d: OK on chan without Id\n", dCh);
break;
}
if (!idi_handle_ack_ok(ccard, chan, ack))
chan = NULL;
break;
case ASSIGN_OK:
if (chan) {
eicon_log(ccard, 1, "idi_ack: Ch%d: ASSIGN-OK on chan already assigned (%x,%x)\n",
chan->No, chan->e.D3Id, chan->e.B2Id);
}
spin_lock_irqsave(&eicon_lock, flags);
for(j = 0; j < ccard->nchannels + 1; j++) {
if ((ccard->bch[j].e.ref == ack->Reference) &&
(ccard->bch[j].e.Req == ASSIGN)) {
if (!ccard->bch[j].e.ReqCh)
ccard->bch[j].e.D3Id = ack->RcId;
else
ccard->bch[j].e.B2Id = ack->RcId;
ccard->IdTable[ack->RcId] = &ccard->bch[j];
chan = &ccard->bch[j];
break;
}
}
spin_unlock_irqrestore(&eicon_lock, flags);
eicon_log(ccard, 16, "idi_ack: Ch%d: Id %x assigned (%s)\n", j,
ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig");
if (j > ccard->nchannels) {
eicon_log(ccard, 24, "idi_ack: Ch??: ref %d not found for Id %d\n",
ack->Reference, ack->RcId);
}
break;
case OUT_OF_RESOURCES:
case UNKNOWN_COMMAND:
case WRONG_COMMAND:
case WRONG_ID:
case ADAPTER_DEAD:
case WRONG_CH:
case UNKNOWN_IE:
case WRONG_IE:
default:
if (!chan) {
eicon_log(ccard, 1, "idi_ack: Ch%d: Not OK !! on chan without Id\n", dCh);
break;
} else
switch (chan->e.Req) {
case 12: /* Alert */
eicon_log(ccard, 2, "eicon_err: Ch%d: Alert Not OK : Rc=%d Id=%x Ch=%d\n",
dCh, ack->Rc, ack->RcId, ack->RcCh);
break;
default:
if (dCh != ccard->nchannels)
eicon_log(ccard, 1, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n",
dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req);
}
if (dCh == ccard->nchannels) { /* Management */
chan->fsm_state = 2;
eicon_log(ccard, 8, "eicon_err: Ch%d: Ack Not OK !!: Rc=%d Id=%x Ch=%d Req=%d\n",
dCh, ack->Rc, ack->RcId, ack->RcCh, chan->e.Req);
} else if (dCh >= 0) {
/* any other channel */
/* card reports error: we hangup */
idi_hangup(ccard, chan);
cmd.driver = ccard->myid;
cmd.command = ISDN_STAT_DHUP;
cmd.arg = chan->No;
ccard->interface.statcallb(&cmd);
}
}
spin_lock_irqsave(&eicon_lock, flags);
if (chan) {
chan->e.ref = 0;
chan->e.busy = 0;
}
spin_unlock_irqrestore(&eicon_lock, flags);
dev_kfree_skb(skb);
eicon_schedule_tx(ccard);
}
int
idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que, int chk)
{
struct sk_buff *xmit_skb;
struct sk_buff *skb2;
eicon_REQ *reqbuf;
eicon_chan_ptr *chan2;
int len, plen = 0, offset = 0;
unsigned long flags;
if ((!card) || (!chan)) {
eicon_log(card, 1, "idi_err: Ch??: null card/chan in send_data\n");
return -1;
}
if (chan->fsm_state != EICON_STATE_ACTIVE) {
eicon_log(card, 1, "idi_snd: Ch%d: send bytes on state %d !\n", chan->No, chan->fsm_state);
return -ENODEV;
}
len = skb->len;
if (len > EICON_MAX_QUEUE) /* too much for the shared memory */
return -1;
if (!len)
return 0;
if ((chk) && (chan->pqueued > 1))
return 0;
eicon_log(card, 128, "idi_snd: Ch%d: %d bytes (Pqueue=%d)\n",
chan->No, len, chan->pqueued);
spin_lock_irqsave(&eicon_lock, flags);
while(offset < len) {
plen = ((len - offset) > 270) ? 270 : len - offset;
xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC);
skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
if ((!xmit_skb) || (!skb2)) {
spin_unlock_irqrestore(&eicon_lock, flags);
eicon_log(card, 1, "idi_err: Ch%d: alloc_skb failed in send_data()\n", chan->No);
if (xmit_skb)
dev_kfree_skb(xmit_skb);
if (skb2)
dev_kfree_skb(skb2);
return -ENOMEM;
}
chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
chan2->ptr = chan;
reqbuf = (eicon_REQ *)skb_put(xmit_skb, plen + sizeof(eicon_REQ));
if ((len - offset) > 270) {
reqbuf->Req = N_MDATA;
} else {
reqbuf->Req = N_DATA;
/* if (ack) reqbuf->Req |= N_D_BIT; */
}
reqbuf->ReqCh = chan->e.IndCh;
reqbuf->ReqId = 1;
memcpy(&reqbuf->XBuffer.P, skb->data + offset, plen);
reqbuf->XBuffer.length = plen;
reqbuf->Reference = 1; /* Net Entity */
skb_queue_tail(&chan->e.X, xmit_skb);
skb_queue_tail(&card->sndq, skb2);
offset += plen;
}
if (que) {
chan->queued += len;
chan->pqueued++;
}
spin_unlock_irqrestore(&eicon_lock, flags);
eicon_schedule_tx(card);
dev_kfree_skb(skb);
return len;
}
int
eicon_idi_manage_assign(eicon_card *card)
{
struct sk_buff *skb;
struct sk_buff *skb2;
eicon_REQ *reqbuf;
eicon_chan *chan;
eicon_chan_ptr *chan2;
chan = &(card->bch[card->nchannels]);
skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
if ((!skb) || (!skb2)) {
eicon_log(card, 1, "idi_err: alloc_skb failed in manage_assign()\n");
if (skb)
dev_kfree_skb(skb);
if (skb2)
dev_kfree_skb(skb2);
return -ENOMEM;
}
chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
chan2->ptr = chan;
reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
reqbuf->XBuffer.P[0] = 0;
reqbuf->Req = ASSIGN;
reqbuf->ReqCh = 0;
reqbuf->ReqId = MAN_ID;
reqbuf->XBuffer.length = 1;
reqbuf->Reference = 2; /* Man Entity */
skb_queue_tail(&chan->e.X, skb);
skb_queue_tail(&card->sndq, skb2);
eicon_schedule_tx(card);
return(0);
}
int
eicon_idi_manage_remove(eicon_card *card)
{
struct sk_buff *skb;
struct sk_buff *skb2;
eicon_REQ *reqbuf;
eicon_chan *chan;
eicon_chan_ptr *chan2;
chan = &(card->bch[card->nchannels]);
skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
if ((!skb) || (!skb2)) {
eicon_log(card, 1, "idi_err: alloc_skb failed in manage_remove()\n");
if (skb)
dev_kfree_skb(skb);
if (skb2)
dev_kfree_skb(skb2);
return -ENOMEM;
}
chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
chan2->ptr = chan;
reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
reqbuf->Req = REMOVE;
reqbuf->ReqCh = 0;
reqbuf->ReqId = 1;
reqbuf->XBuffer.length = 0;
reqbuf->Reference = 2; /* Man Entity */
skb_queue_tail(&chan->e.X, skb);
skb_queue_tail(&card->sndq, skb2);
eicon_schedule_tx(card);
return(0);
}
int
eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb)
{
int l = 0;
int ret = 0;
unsigned long timeout;
int i;
struct sk_buff *skb;
struct sk_buff *skb2;
eicon_REQ *reqbuf;
eicon_chan *chan;
eicon_chan_ptr *chan2;
chan = &(card->bch[card->nchannels]);
if (!(chan->e.D3Id)) {
chan->e.D3Id = 1;
while((skb2 = skb_dequeue(&chan->e.X)))
dev_kfree_skb(skb2);
chan->e.busy = 0;
if ((ret = eicon_idi_manage_assign(card))) {
chan->e.D3Id = 0;
return(ret);
}
timeout = jiffies + HZ / 2;
while (time_before(jiffies, timeout)) {
if (chan->e.B2Id) break;
SLEEP(10);
}
if (!chan->e.B2Id) {
chan->e.D3Id = 0;
return -EIO;
}
}
chan->fsm_state = 0;
if (!(manbuf = kmalloc(sizeof(eicon_manifbuf), GFP_KERNEL))) {
eicon_log(card, 1, "idi_err: alloc_manifbuf failed\n");
return -ENOMEM;
}
if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) {
kfree(manbuf);
return -EFAULT;
}
skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
if ((!skb) || (!skb2)) {
eicon_log(card, 1, "idi_err_manif: alloc_skb failed in manage()\n");
if (skb)
dev_kfree_skb(skb);
if (skb2)
dev_kfree_skb(skb2);
kfree(manbuf);
return -ENOMEM;
}
chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
chan2->ptr = chan;
reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
reqbuf->XBuffer.P[l++] = ESC;
reqbuf->XBuffer.P[l++] = 6;
reqbuf->XBuffer.P[l++] = 0x80;
for (i = 0; i < manbuf->length[0]; i++)
reqbuf->XBuffer.P[l++] = manbuf->data[i];
reqbuf->XBuffer.P[1] = manbuf->length[0] + 1;
reqbuf->XBuffer.P[l++] = 0;
reqbuf->Req = (manbuf->count) ? manbuf->count : MAN_READ;
reqbuf->ReqCh = 0;
reqbuf->ReqId = 1;
reqbuf->XBuffer.length = l;
reqbuf->Reference = 2; /* Man Entity */
skb_queue_tail(&chan->e.X, skb);
skb_queue_tail(&card->sndq, skb2);
manbuf->count = 0;
manbuf->pos = 0;
eicon_schedule_tx(card);
timeout = jiffies + HZ / 2;
while (time_before(jiffies, timeout)) {
if (chan->fsm_state) break;
SLEEP(10);
}
if ((!chan->fsm_state) || (chan->fsm_state == 2)) {
kfree(manbuf);
return -EIO;
}
if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) {
kfree(manbuf);
return -EFAULT;
}
kfree(manbuf);
return(0);
}
/* $Id: eicon_idi.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $
*
* ISDN lowlevel-module for the Eicon active cards.
* IDI-Interface
*
* Copyright 1998-2000 by Armin Schindler (mac@melware.de)
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef E_IDI_H
#define E_IDI_H
#include <linux/config.h>
#undef N_DATA
#undef ID_MASK
#include "pc.h"
#define AOC_IND 26 /* Advice of Charge */
#define PI 0x1e /* Progress Indicator */
#define NI 0x27 /* Notification Indicator */
#define CALL_HOLD 0x22
#define CALL_HOLD_ACK 0x24
/* defines for statectrl */
#define WAITING_FOR_HANGUP 0x01
#define HAVE_CONN_REQ 0x02
#define IN_HOLD 0x04
typedef struct {
char cpn[32];
char oad[32];
char dsa[32];
char osa[32];
__u8 plan;
__u8 screen;
__u8 sin[4];
__u8 chi[4];
__u8 e_chi[4];
__u8 bc[12];
__u8 e_bc[12];
__u8 llc[18];
__u8 hlc[5];
__u8 cau[4];
__u8 e_cau[2];
__u8 e_mt;
__u8 dt[6];
char display[83];
char keypad[35];
char rdn[32];
} idi_ind_message;
typedef struct {
__u16 next __attribute__ ((packed));
__u8 Req __attribute__ ((packed));
__u8 ReqId __attribute__ ((packed));
__u8 ReqCh __attribute__ ((packed));
__u8 Reserved1 __attribute__ ((packed));
__u16 Reference __attribute__ ((packed));
__u8 Reserved[8] __attribute__ ((packed));
eicon_PBUFFER XBuffer;
} eicon_REQ;
typedef struct {
__u16 next __attribute__ ((packed));
__u8 Rc __attribute__ ((packed));
__u8 RcId __attribute__ ((packed));
__u8 RcCh __attribute__ ((packed));
__u8 Reserved1 __attribute__ ((packed));
__u16 Reference __attribute__ ((packed));
__u8 Reserved2[8] __attribute__ ((packed));
} eicon_RC;
typedef struct {
__u16 next __attribute__ ((packed));
__u8 Ind __attribute__ ((packed));
__u8 IndId __attribute__ ((packed));
__u8 IndCh __attribute__ ((packed));
__u8 MInd __attribute__ ((packed));
__u16 MLength __attribute__ ((packed));
__u16 Reference __attribute__ ((packed));
__u8 RNR __attribute__ ((packed));
__u8 Reserved __attribute__ ((packed));
__u32 Ack __attribute__ ((packed));
eicon_PBUFFER RBuffer;
} eicon_IND;
typedef struct {
__u8 *Data;
unsigned int Size;
unsigned int Len;
__u8 *Next;
} eicon_OBJBUFFER;
extern int idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer);
extern int idi_hangup(eicon_card *card, eicon_chan *chan);
extern int idi_connect_res(eicon_card *card, eicon_chan *chan);
extern int eicon_idi_listen_req(eicon_card *card, eicon_chan *chan);
extern int idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
char *eazmsn, int si1, int si2);
extern void idi_handle_ack(eicon_card *card, struct sk_buff *skb);
extern void idi_handle_ind(eicon_card *card, struct sk_buff *skb);
extern int eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb);
extern int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb, int que, int chk);
extern void idi_audio_cmd(eicon_card *ccard, eicon_chan *chan, int cmd, u_char *value);
extern int capipmsg(eicon_card *card, eicon_chan *chan, capi_msg *cm);
#ifdef CONFIG_ISDN_TTY_FAX
extern void idi_fax_cmd(eicon_card *card, eicon_chan *chan);
extern int idi_faxdata_send(eicon_card *ccard, eicon_chan *chan, struct sk_buff *skb);
#endif
#endif
/* $Id: eicon_io.c,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $
*
* ISDN low-level module for Eicon active ISDN-Cards.
* Code for communicating with hardware.
*
* Copyright 1999,2000 by Armin Schindler (mac@melware.de)
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
* Thanks to Eicon Networks for
* documents, informations and hardware.
*
*/
#include <linux/config.h>
#include "eicon.h"
#include "uxio.h"
void
eicon_io_rcv_dispatch(eicon_card *ccard) {
ulong flags;
struct sk_buff *skb, *skb2, *skb_new;
eicon_IND *ind, *ind2, *ind_new;
eicon_chan *chan;
if (!ccard) {
eicon_log(ccard, 1, "eicon_err: NULL card in rcv_dispatch !\n");
return;
}
while((skb = skb_dequeue(&ccard->rcvq))) {
ind = (eicon_IND *)skb->data;
spin_lock_irqsave(&eicon_lock, flags);
if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
spin_unlock_irqrestore(&eicon_lock, flags);
if (DebugVar & 1) {
switch(ind->Ind) {
case N_DISC_ACK:
/* doesn't matter if this happens */
break;
default:
eicon_log(ccard, 1, "idi: Indication for unknown channel Ind=%d Id=%x\n", ind->Ind, ind->IndId);
eicon_log(ccard, 1, "idi_hdl: Ch??: Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n",
ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
}
}
dev_kfree_skb(skb);
continue;
}
spin_unlock_irqrestore(&eicon_lock, flags);
if (chan->e.complete) { /* check for rec-buffer chaining */
if (ind->MLength == ind->RBuffer.length) {
chan->e.complete = 1;
idi_handle_ind(ccard, skb);
continue;
}
else {
chan->e.complete = 0;
ind->Ind = ind->MInd;
skb_queue_tail(&chan->e.R, skb);
continue;
}
}
else {
if (!(skb2 = skb_dequeue(&chan->e.R))) {
chan->e.complete = 1;
eicon_log(ccard, 1, "eicon: buffer incomplete, but 0 in queue\n");
dev_kfree_skb(skb);
continue;
}
ind2 = (eicon_IND *)skb2->data;
skb_new = alloc_skb(((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length),
GFP_ATOMIC);
if (!skb_new) {
eicon_log(ccard, 1, "eicon_io: skb_alloc failed in rcv_dispatch()\n");
dev_kfree_skb(skb);
dev_kfree_skb(skb2);
continue;
}
ind_new = (eicon_IND *)skb_put(skb_new,
((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length));
ind_new->Ind = ind2->Ind;
ind_new->IndId = ind2->IndId;
ind_new->IndCh = ind2->IndCh;
ind_new->MInd = ind2->MInd;
ind_new->MLength = ind2->MLength;
ind_new->RBuffer.length = ind2->RBuffer.length + ind->RBuffer.length;
memcpy(&ind_new->RBuffer.P, &ind2->RBuffer.P, ind2->RBuffer.length);
memcpy((&ind_new->RBuffer.P)+ind2->RBuffer.length, &ind->RBuffer.P, ind->RBuffer.length);
dev_kfree_skb(skb);
dev_kfree_skb(skb2);
if (ind->MLength == ind->RBuffer.length) {
chan->e.complete = 2;
idi_handle_ind(ccard, skb_new);
continue;
}
else {
chan->e.complete = 0;
skb_queue_tail(&chan->e.R, skb_new);
continue;
}
}
}
}
void
eicon_io_ack_dispatch(eicon_card *ccard) {
struct sk_buff *skb;
if (!ccard) {
eicon_log(ccard, 1, "eicon_err: NULL card in ack_dispatch!\n");
return;
}
while((skb = skb_dequeue(&ccard->rackq))) {
idi_handle_ack(ccard, skb);
}
}
/*
* IO-Functions for ISA cards
*/
u8 ram_inb(eicon_card *card, void *adr) {
u32 addr = (u32) adr;
return(readb(addr));
}
u16 ram_inw(eicon_card *card, void *adr) {
u32 addr = (u32) adr;
return(readw(addr));
}
void ram_outb(eicon_card *card, void *adr, u8 data) {
u32 addr = (u32) adr;
writeb(data, addr);
}
void ram_outw(eicon_card *card, void *adr , u16 data) {
u32 addr = (u32) adr;
writew(data, addr);
}
void ram_copyfromcard(eicon_card *card, void *adrto, void *adr, int len) {
memcpy_fromio(adrto, adr, len);
}
void ram_copytocard(eicon_card *card, void *adrto, void *adr, int len) {
memcpy_toio(adrto, adr, len);
}
#ifdef CONFIG_ISDN_DRV_EICON_PCI
/*
* IDI-Callback function
*/
void
eicon_idi_callback(ENTITY *de)
{
eicon_card *ccard = (eicon_card *)de->R;
struct sk_buff *skb;
eicon_RC *ack;
eicon_IND *ind;
int len = 0;
if (de->complete == 255) {
/* Return Code */
skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
if (!skb) {
eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _idi_callback()\n");
} else {
ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC));
ack->Rc = de->Rc;
if (de->Rc == ASSIGN_OK) {
ack->RcId = de->Id;
de->user[1] = de->Id;
} else {
ack->RcId = de->user[1];
}
ack->RcCh = de->RcCh;
ack->Reference = de->user[0];
skb_queue_tail(&ccard->rackq, skb);
eicon_schedule_ack(ccard);
eicon_log(ccard, 128, "idi_cbk: Ch%d: Rc=%x Id=%x RLen=%x compl=%x\n",
de->user[0], de->Rc, ack->RcId, de->RLength, de->complete);
}
} else {
/* Indication */
if (de->complete) {
len = de->RLength;
} else {
len = 270;
if (de->RLength <= 270)
eicon_log(ccard, 1, "eicon_cbk: ind not complete but <= 270\n");
}
skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
if (!skb) {
eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _idi_callback()\n");
} else {
ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
ind->Ind = de->Ind;
ind->IndId = de->user[1];
ind->IndCh = de->IndCh;
ind->MInd = de->Ind;
ind->RBuffer.length = len;
ind->MLength = de->RLength;
memcpy(&ind->RBuffer.P, &de->RBuffer->P, len);
skb_queue_tail(&ccard->rcvq, skb);
eicon_schedule_rx(ccard);
eicon_log(ccard, 128, "idi_cbk: Ch%d: Ind=%x Id=%x RLen=%x compl=%x\n",
de->user[0], de->Ind, ind->IndId, de->RLength, de->complete);
}
}
de->RNum = 0;
de->RNR = 0;
de->Rc = 0;
de->Ind = 0;
}
#endif /* CONFIG_ISDN_DRV_EICON_PCI */
/*
* Transmit-Function
*/
void
eicon_io_transmit(eicon_card *ccard) {
eicon_isa_card *isa_card;
struct sk_buff *skb;
struct sk_buff *skb2;
unsigned long flags;
eicon_pr_ram *prram = 0;
eicon_isa_com *com = 0;
eicon_REQ *ReqOut = 0;
eicon_REQ *reqbuf = 0;
eicon_chan *chan;
eicon_chan_ptr *chan2;
int ReqCount;
int scom = 0;
int tmp = 0;
int tmpid = 0;
int quloop = 1;
int dlev = 0;
ENTITY *ep = 0;
isa_card = &ccard->hwif.isa;
if (!ccard) {
eicon_log(ccard, 1, "eicon_transmit: NULL card!\n");
return;
}
switch(ccard->type) {
#ifdef CONFIG_ISDN_DRV_EICON_ISA
case EICON_CTYPE_S:
case EICON_CTYPE_SX:
case EICON_CTYPE_SCOM:
case EICON_CTYPE_QUADRO:
scom = 1;
com = (eicon_isa_com *)isa_card->shmem;
break;
case EICON_CTYPE_S2M:
scom = 0;
prram = (eicon_pr_ram *)isa_card->shmem;
break;
#endif
#ifdef CONFIG_ISDN_DRV_EICON_PCI
case EICON_CTYPE_MAESTRAP:
scom = 2;
break;
case EICON_CTYPE_MAESTRAQ:
scom = 2;
break;
case EICON_CTYPE_MAESTRA:
scom = 2;
break;
#endif
default:
eicon_log(ccard, 1, "eicon_transmit: unsupported card-type!\n");
return;
}
ReqCount = 0;
if (!(skb2 = skb_dequeue(&ccard->sndq)))
quloop = 0;
while(quloop) {
spin_lock_irqsave(&eicon_lock, flags);
switch (scom) {
case 1:
if ((ram_inb(ccard, &com->Req)) || (ccard->ReadyInt)) {
if (!ccard->ReadyInt) {
tmp = ram_inb(ccard, &com->ReadyInt) + 1;
ram_outb(ccard, &com->ReadyInt, tmp);
ccard->ReadyInt++;
}
spin_unlock_irqrestore(&eicon_lock, flags);
skb_queue_head(&ccard->sndq, skb2);
eicon_log(ccard, 32, "eicon: transmit: Card not ready\n");
return;
}
break;
case 0:
if (!(ram_inb(ccard, &prram->ReqOutput) - ram_inb(ccard, &prram->ReqInput))) {
spin_unlock_irqrestore(&eicon_lock, flags);
skb_queue_head(&ccard->sndq, skb2);
eicon_log(ccard, 32, "eicon: transmit: Card not ready\n");
return;
}
break;
}
spin_unlock_irqrestore(&eicon_lock, flags);
chan2 = (eicon_chan_ptr *)skb2->data;
chan = chan2->ptr;
if (!chan->e.busy) {
if((skb = skb_dequeue(&chan->e.X))) {
reqbuf = (eicon_REQ *)skb->data;
if ((reqbuf->Reference) && (chan->e.B2Id == 0) && (reqbuf->ReqId & 0x1f)) {
eicon_log(ccard, 16, "eicon: transmit: error Id=0 on %d (Net)\n", chan->No);
} else {
spin_lock_irqsave(&eicon_lock, flags);
switch (scom) {
case 1:
ram_outw(ccard, &com->XBuffer.length, reqbuf->XBuffer.length);
ram_copytocard(ccard, &com->XBuffer.P, &reqbuf->XBuffer.P, reqbuf->XBuffer.length);
ram_outb(ccard, &com->ReqCh, reqbuf->ReqCh);
break;
case 0:
/* get address of next available request buffer */
ReqOut = (eicon_REQ *)&prram->B[ram_inw(ccard, &prram->NextReq)];
ram_outw(ccard, &ReqOut->XBuffer.length, reqbuf->XBuffer.length);
ram_copytocard(ccard, &ReqOut->XBuffer.P, &reqbuf->XBuffer.P, reqbuf->XBuffer.length);
ram_outb(ccard, &ReqOut->ReqCh, reqbuf->ReqCh);
ram_outb(ccard, &ReqOut->Req, reqbuf->Req);
break;
}
dlev = 160;
if (reqbuf->ReqId & 0x1f) { /* if this is no ASSIGN */
if (!reqbuf->Reference) { /* Signal Layer */
switch (scom) {
case 1:
ram_outb(ccard, &com->ReqId, chan->e.D3Id);
break;
case 0:
ram_outb(ccard, &ReqOut->ReqId, chan->e.D3Id);
break;
case 2:
ep = &chan->de;
break;
}
tmpid = chan->e.D3Id;
chan->e.ReqCh = 0;
}
else { /* Net Layer */
switch(scom) {
case 1:
ram_outb(ccard, &com->ReqId, chan->e.B2Id);
break;
case 0:
ram_outb(ccard, &ReqOut->ReqId, chan->e.B2Id);
break;
case 2:
ep = &chan->be;
break;
}
tmpid = chan->e.B2Id;
chan->e.ReqCh = 1;
if (((reqbuf->Req & 0x0f) == 0x08) ||
((reqbuf->Req & 0x0f) == 0x01)) { /* Send Data */
chan->waitq = reqbuf->XBuffer.length;
chan->waitpq += reqbuf->XBuffer.length;
dlev = 128;
}
}
} else { /* It is an ASSIGN */
switch(scom) {
case 1:
ram_outb(ccard, &com->ReqId, reqbuf->ReqId);
break;
case 0:
ram_outb(ccard, &ReqOut->ReqId, reqbuf->ReqId);
break;
case 2:
if (!reqbuf->Reference)
ep = &chan->de;
else
ep = &chan->be;
ep->Id = reqbuf->ReqId;
break;
}
tmpid = reqbuf->ReqId;
if (!reqbuf->Reference)
chan->e.ReqCh = 0;
else
chan->e.ReqCh = 1;
}
switch(scom) {
case 1:
chan->e.ref = ccard->ref_out++;
break;
case 0:
chan->e.ref = ram_inw(ccard, &ReqOut->Reference);
break;
case 2:
chan->e.ref = chan->No;
break;
}
chan->e.Req = reqbuf->Req;
ReqCount++;
switch (scom) {
case 1:
ram_outb(ccard, &com->Req, reqbuf->Req);
break;
case 0:
ram_outw(ccard, &prram->NextReq, ram_inw(ccard, &ReqOut->next));
break;
case 2:
#ifdef CONFIG_ISDN_DRV_EICON_PCI
if (!ep) break;
ep->callback = eicon_idi_callback;
ep->R = (BUFFERS *)ccard;
ep->user[0] = (word)chan->No;
ep->user[1] = (word)tmpid;
ep->XNum = 1;
ep->RNum = 0;
ep->RNR = 0;
ep->Rc = 0;
ep->Ind = 0;
ep->X->PLength = reqbuf->XBuffer.length;
memcpy(ep->X->P, &reqbuf->XBuffer.P, reqbuf->XBuffer.length);
ep->ReqCh = reqbuf->ReqCh;
ep->Req = reqbuf->Req;
#endif
break;
}
chan->e.busy = 1;
spin_unlock_irqrestore(&eicon_lock, flags);
eicon_log(ccard, dlev, "eicon: Req=%d Id=%x Ch=%d Len=%d Ref=%d\n",
reqbuf->Req, tmpid,
reqbuf->ReqCh, reqbuf->XBuffer.length,
chan->e.ref);
#ifdef CONFIG_ISDN_DRV_EICON_PCI
if (scom == 2) {
if (ep) {
ccard->d->request(ep);
if (ep->Rc)
eicon_idi_callback(ep);
}
}
#endif
}
dev_kfree_skb(skb);
}
dev_kfree_skb(skb2);
}
else {
skb_queue_tail(&ccard->sackq, skb2);
eicon_log(ccard, 128, "eicon: transmit: busy chan %d\n", chan->No);
}
switch(scom) {
case 1:
quloop = 0;
break;
case 0:
case 2:
if (!(skb2 = skb_dequeue(&ccard->sndq)))
quloop = 0;
break;
}
}
if (!scom)
ram_outb(ccard, &prram->ReqInput, (__u8)(ram_inb(ccard, &prram->ReqInput) + ReqCount));
while((skb = skb_dequeue(&ccard->sackq))) {
skb_queue_tail(&ccard->sndq, skb);
}
}
#ifdef CONFIG_ISDN_DRV_EICON_ISA
/*
* IRQ handler
*/
irqreturn_t
eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
eicon_card *ccard = (eicon_card *)dev_id;
eicon_isa_card *isa_card;
eicon_pr_ram *prram = 0;
eicon_isa_com *com = 0;
eicon_RC *RcIn;
eicon_IND *IndIn;
struct sk_buff *skb;
int Count = 0;
int Rc = 0;
int Ind = 0;
unsigned char *irqprobe = 0;
int scom = 0;
int tmp = 0;
int dlev = 0;
if (!ccard) {
eicon_log(ccard, 1, "eicon_irq: spurious interrupt %d\n", irq);
return IRQ_NONE;
}
if (ccard->type == EICON_CTYPE_QUADRO) {
tmp = 4;
while(tmp) {
com = (eicon_isa_com *)ccard->hwif.isa.shmem;
if ((readb(ccard->hwif.isa.intack))) { /* quadro found */
break;
}
ccard = ccard->qnext;
tmp--;
}
}
isa_card = &ccard->hwif.isa;
switch(ccard->type) {
case EICON_CTYPE_S:
case EICON_CTYPE_SX:
case EICON_CTYPE_SCOM:
case EICON_CTYPE_QUADRO:
scom = 1;
com = (eicon_isa_com *)isa_card->shmem;
irqprobe = &isa_card->irqprobe;
break;
case EICON_CTYPE_S2M:
scom = 0;
prram = (eicon_pr_ram *)isa_card->shmem;
irqprobe = &isa_card->irqprobe;
break;
default:
eicon_log(ccard, 1, "eicon_irq: unsupported card-type!\n");
return IRQ_NONE;
}
if (*irqprobe) {
switch(ccard->type) {
case EICON_CTYPE_S:
case EICON_CTYPE_SX:
case EICON_CTYPE_SCOM:
case EICON_CTYPE_QUADRO:
if (readb(isa_card->intack)) {
writeb(0, &com->Rc);
writeb(0, isa_card->intack);
}
(*irqprobe)++;
break;
case EICON_CTYPE_S2M:
if (readb(isa_card->intack)) {
writeb(0, &prram->RcOutput);
writeb(0, isa_card->intack);
}
(*irqprobe)++;
break;
}
return IRQ_HANDLED;
}
switch(ccard->type) {
case EICON_CTYPE_S:
case EICON_CTYPE_SX:
case EICON_CTYPE_SCOM:
case EICON_CTYPE_QUADRO:
case EICON_CTYPE_S2M:
if (!(readb(isa_card->intack))) { /* card did not interrupt */
eicon_log(ccard, 1, "eicon: IRQ: card reports no interrupt!\n");
return IRQ_NONE;
}
break;
}
if (scom) {
/* if a return code is available ... */
if ((tmp = ram_inb(ccard, &com->Rc))) {
eicon_RC *ack;
if (tmp == READY_INT) {
eicon_log(ccard, 64, "eicon: IRQ Rc=READY_INT\n");
if (ccard->ReadyInt) {
ccard->ReadyInt--;
ram_outb(ccard, &com->Rc, 0);
eicon_schedule_tx(ccard);
}
} else {
skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
if (!skb) {
eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n");
} else {
ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC));
ack->Rc = tmp;
ack->RcId = ram_inb(ccard, &com->RcId);
ack->RcCh = ram_inb(ccard, &com->RcCh);
ack->Reference = ccard->ref_in++;
eicon_log(ccard, 128, "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n",
tmp,ack->RcId,ack->RcCh,ack->Reference);
skb_queue_tail(&ccard->rackq, skb);
eicon_schedule_ack(ccard);
}
ram_outb(ccard, &com->Req, 0);
ram_outb(ccard, &com->Rc, 0);
}
} else {
/* if an indication is available ... */
if ((tmp = ram_inb(ccard, &com->Ind))) {
eicon_IND *ind;
int len = ram_inw(ccard, &com->RBuffer.length);
skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
if (!skb) {
eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n");
} else {
ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
ind->Ind = tmp;
ind->IndId = ram_inb(ccard, &com->IndId);
ind->IndCh = ram_inb(ccard, &com->IndCh);
ind->MInd = ram_inb(ccard, &com->MInd);
ind->MLength = ram_inw(ccard, &com->MLength);
ind->RBuffer.length = len;
if ((tmp == 1) || (tmp == 8))
dlev = 128;
else
dlev = 192;
eicon_log(ccard, dlev, "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n",
tmp,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len);
ram_copyfromcard(ccard, &ind->RBuffer.P, &com->RBuffer.P, len);
skb_queue_tail(&ccard->rcvq, skb);
eicon_schedule_rx(ccard);
}
ram_outb(ccard, &com->Ind, 0);
}
}
} else {
/* if return codes are available ... */
if((Count = ram_inb(ccard, &prram->RcOutput))) {
eicon_RC *ack;
/* get the buffer address of the first return code */
RcIn = (eicon_RC *)&prram->B[ram_inw(ccard, &prram->NextRc)];
/* for all return codes do ... */
while(Count--) {
if((Rc=ram_inb(ccard, &RcIn->Rc))) {
skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
if (!skb) {
eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n");
} else {
ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC));
ack->Rc = Rc;
ack->RcId = ram_inb(ccard, &RcIn->RcId);
ack->RcCh = ram_inb(ccard, &RcIn->RcCh);
ack->Reference = ram_inw(ccard, &RcIn->Reference);
eicon_log(ccard, 128, "eicon: IRQ Rc=%d Id=%x Ch=%d Ref=%d\n",
Rc,ack->RcId,ack->RcCh,ack->Reference);
skb_queue_tail(&ccard->rackq, skb);
eicon_schedule_ack(ccard);
}
ram_outb(ccard, &RcIn->Rc, 0);
}
/* get buffer address of next return code */
RcIn = (eicon_RC *)&prram->B[ram_inw(ccard, &RcIn->next)];
}
/* clear all return codes (no chaining!) */
ram_outb(ccard, &prram->RcOutput, 0);
}
/* if indications are available ... */
if((Count = ram_inb(ccard, &prram->IndOutput))) {
eicon_IND *ind;
/* get the buffer address of the first indication */
IndIn = (eicon_IND *)&prram->B[ram_inw(ccard, &prram->NextInd)];
/* for all indications do ... */
while(Count--) {
Ind = ram_inb(ccard, &IndIn->Ind);
if(Ind) {
int len = ram_inw(ccard, &IndIn->RBuffer.length);
skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
if (!skb) {
eicon_log(ccard, 1, "eicon_io: skb_alloc failed in _irq()\n");
} else {
ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
ind->Ind = Ind;
ind->IndId = ram_inb(ccard, &IndIn->IndId);
ind->IndCh = ram_inb(ccard, &IndIn->IndCh);
ind->MInd = ram_inb(ccard, &IndIn->MInd);
ind->MLength = ram_inw(ccard, &IndIn->MLength);
ind->RBuffer.length = len;
if ((Ind == 1) || (Ind == 8))
dlev = 128;
else
dlev = 192;
eicon_log(ccard, dlev, "eicon: IRQ Ind=%d Id=%x Ch=%d MInd=%d MLen=%d Len=%d\n",
Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len);
ram_copyfromcard(ccard, &ind->RBuffer.P, &IndIn->RBuffer.P, len);
skb_queue_tail(&ccard->rcvq, skb);
eicon_schedule_rx(ccard);
}
ram_outb(ccard, &IndIn->Ind, 0);
}
/* get buffer address of next indication */
IndIn = (eicon_IND *)&prram->B[ram_inw(ccard, &IndIn->next)];
}
ram_outb(ccard, &prram->IndOutput, 0);
}
}
/* clear interrupt */
switch(ccard->type) {
case EICON_CTYPE_QUADRO:
writeb(0, isa_card->intack);
writeb(0, &com[0x401]);
break;
case EICON_CTYPE_S:
case EICON_CTYPE_SX:
case EICON_CTYPE_SCOM:
case EICON_CTYPE_S2M:
writeb(0, isa_card->intack);
break;
}
return IRQ_HANDLED;
}
#endif
/* $Id: eicon_isa.c,v 1.1.4.1.2.3 2002/10/01 11:29:13 armin Exp $
*
* ISDN low-level module for Eicon active ISDN-Cards.
* Hardware-specific code for old ISA cards.
*
* Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
* Copyright 1998-2000 by Armin Schindler (mac@melware.de)
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/config.h>
#include "eicon.h"
#include "eicon_isa.h"
#define check_shmem check_region
#define release_shmem release_region
#define request_shmem request_region
char *eicon_isa_revision = "$Revision: 1.1.4.1.2.3 $";
#undef EICON_MCA_DEBUG
#ifdef CONFIG_ISDN_DRV_EICON_ISA
/* Mask for detecting invalid IRQ parameter */
static int eicon_isa_valid_irq[] = {
0x1c1c, /* 2, 3, 4, 10, 11, 12 (S)*/
0x1c1c, /* 2, 3, 4, 10, 11, 12 (SX) */
0x1cbc, /* 2, 3, 4, 5, 7, 10, 11, 12 (SCOM) */
0x1cbc, /* 2, 3, 4, 5, 6, 10, 11, 12 (Quadro) */
0x1cbc /* 2, 3, 4, 5, 7, 10, 11, 12 (S2M) */
};
static void
eicon_isa_release_shmem(eicon_isa_card *card) {
if (card->mvalid) {
iounmap(card->shmem);
release_mem_region(card->physmem, card->ramsize);
}
card->mvalid = 0;
}
static void
eicon_isa_release_irq(eicon_isa_card *card) {
if (!card->master)
return;
if (card->ivalid)
free_irq(card->irq, card);
card->ivalid = 0;
}
void
eicon_isa_release(eicon_isa_card *card) {
eicon_isa_release_irq(card);
eicon_isa_release_shmem(card);
}
void
eicon_isa_printpar(eicon_isa_card *card) {
switch (card->type) {
case EICON_CTYPE_S:
case EICON_CTYPE_SX:
case EICON_CTYPE_SCOM:
case EICON_CTYPE_QUADRO:
case EICON_CTYPE_S2M:
printk(KERN_INFO "Eicon %s at 0x%lx, irq %d.\n",
eicon_ctype_name[card->type],
card->physmem,
card->irq);
}
}
int
eicon_isa_find_card(int Mem, int Irq, char * Id)
{
int primary = 1;
unsigned long amem;
if (!strlen(Id))
return -1;
if (Mem == -1)
return -1;
/* Check for valid membase address */
if ((Mem < 0x0c0000) ||
(Mem > 0x0fc000) ||
(Mem & 0xfff)) {
printk(KERN_WARNING "eicon_isa: invalid membase 0x%x for %s\n",
Mem, Id);
return -1;
}
if (check_mem_region(Mem, RAMSIZE)) {
printk(KERN_WARNING "eicon_isa_boot: memory at 0x%x already in use.\n", Mem);
return -1;
}
amem = (unsigned long) ioremap(Mem, RAMSIZE);
writew(0x55aa, amem + 0x402);
if (readw(amem + 0x402) != 0x55aa) primary = 0;
writew(0, amem + 0x402);
if (readw(amem + 0x402) != 0) primary = 0;
printk(KERN_INFO "Eicon: Driver-ID: %s\n", Id);
if (primary) {
printk(KERN_INFO "Eicon: assuming pri card at 0x%x\n", Mem);
writeb(0, amem + 0x3ffe);
iounmap((unsigned char *)amem);
return EICON_CTYPE_ISAPRI;
} else {
printk(KERN_INFO "Eicon: assuming bri card at 0x%x\n", Mem);
writeb(0, amem + 0x400);
iounmap((unsigned char *)amem);
return EICON_CTYPE_ISABRI;
}
return -1;
}
int
eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb) {
int tmp;
unsigned long timeout;
eicon_isa_codebuf cbuf;
unsigned char *code;
eicon_isa_boot *boot;
if (copy_from_user(&cbuf, cb, sizeof(eicon_isa_codebuf)))
return -EFAULT;
/* Allocate code-buffer and copy code from userspace */
if (cbuf.bootstrap_len > 1024) {
printk(KERN_WARNING "eicon_isa_boot: Invalid startup-code size %ld\n",
cbuf.bootstrap_len);
return -EINVAL;
}
if (!(code = kmalloc(cbuf.bootstrap_len, GFP_KERNEL))) {
printk(KERN_WARNING "eicon_isa_boot: Couldn't allocate code buffer\n");
return -ENOMEM;
}
if (copy_from_user(code, &cb->code, cbuf.bootstrap_len)) {
kfree(code);
return -EFAULT;
}
if (card->type == EICON_CTYPE_ISAPRI)
card->ramsize = RAMSIZE_P;
else
card->ramsize = RAMSIZE;
if (check_mem_region(card->physmem, card->ramsize)) {
printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n",
card->physmem);
kfree(code);
return -EBUSY;
}
request_mem_region(card->physmem, card->ramsize, "Eicon ISA ISDN");
card->shmem = (eicon_isa_shmem *) ioremap(card->physmem, card->ramsize);
#ifdef EICON_MCA_DEBUG
printk(KERN_INFO "eicon_isa_boot: card->ramsize = %d.\n", card->ramsize);
#endif
card->mvalid = 1;
switch(card->type) {
case EICON_CTYPE_S:
case EICON_CTYPE_SX:
case EICON_CTYPE_SCOM:
case EICON_CTYPE_QUADRO:
case EICON_CTYPE_ISABRI:
card->intack = (__u8 *)card->shmem + INTACK;
card->startcpu = (__u8 *)card->shmem + STARTCPU;
card->stopcpu = (__u8 *)card->shmem + STOPCPU;
break;
case EICON_CTYPE_S2M:
case EICON_CTYPE_ISAPRI:
card->intack = (__u8 *)card->shmem + INTACK_P;
card->startcpu = (__u8 *)card->shmem + STARTCPU_P;
card->stopcpu = (__u8 *)card->shmem + STOPCPU_P;
break;
default:
printk(KERN_WARNING "eicon_isa_boot: Invalid card type %d\n", card->type);
eicon_isa_release_shmem(card);
kfree(code);
return -EINVAL;
}
/* clear any pending irq's */
readb(card->intack);
#ifdef CONFIG_MCA
if (MCA_bus) {
if (card->type == EICON_CTYPE_SCOM) {
outb_p(0,card->io+1);
}
else {
printk(KERN_WARNING "eicon_isa_boot: Card type not supported yet.\n");
eicon_isa_release_shmem(card);
return -EINVAL;
};
#ifdef EICON_MCA_DEBUG
printk(KERN_INFO "eicon_isa_boot: card->io = %x.\n", card->io);
printk(KERN_INFO "eicon_isa_boot: card->irq = %d.\n", (int)card->irq);
#endif
}
#else
/* set reset-line active */
writeb(0, card->stopcpu);
#endif /* CONFIG_MCA */
/* clear irq-requests */
writeb(0, card->intack);
readb(card->intack);
/* Copy code into card */
memcpy_toio(&card->shmem->c, code, cbuf.bootstrap_len);
/* Check for properly loaded code */
if (!check_signature((unsigned long)&card->shmem->c, code, 1020)) {
printk(KERN_WARNING "eicon_isa_boot: Could not load startup-code\n");
eicon_isa_release_shmem(card);
kfree(code);
return -EIO;
}
/* if 16k-ramsize, duplicate the reset-jump-code */
if (card->ramsize == RAMSIZE_P)
memcpy_toio((__u8 *)card->shmem + 0x3ff0, &code[0x3f0], 12);
kfree(code);
boot = &card->shmem->boot;
/* Delay 0.2 sec. */
SLEEP(HZ / 5);
/* Start CPU */
writeb(cbuf.boot_opt, &boot->ctrl);
#ifdef CONFIG_MCA
if (MCA_bus) {
outb_p(0, card->io);
}
#else
writeb(0, card->startcpu);
#endif /* CONFIG_MCA */
/* Delay 0.2 sec. */
SLEEP(HZ / 5);
timeout = jiffies + (HZ * 22);
while (time_before(jiffies, timeout)) {
if (readb(&boot->ctrl) == 0)
break;
SLEEP(10);
}
if (readb(&boot->ctrl) != 0) {
printk(KERN_WARNING "eicon_isa_boot: CPU test failed.\n");
#ifdef EICON_MCA_DEBUG
printk(KERN_INFO "eicon_isa_boot: &boot->ctrl = %d.\n",
readb(&boot->ctrl));
#endif
eicon_isa_release_shmem(card);
return -EIO;
}
/* Check for memory-test errors */
if (readw(&boot->ebit)) {
printk(KERN_WARNING "eicon_isa_boot: memory test failed (bit 0x%04x at 0x%08x)\n",
readw(&boot->ebit), readl(&boot->eloc));
eicon_isa_release_shmem(card);
return -EIO;
}
/* Check card type and memory size */
tmp = readb(&boot->card);
if ((tmp < 0) || (tmp > 4)) {
printk(KERN_WARNING "eicon_isa_boot: Type detect failed\n");
eicon_isa_release_shmem(card);
return -EIO;
}
card->type = tmp;
((eicon_card *)card->card)->type = tmp;
tmp = readb(&boot->msize);
if (tmp != 8 && tmp != 16 && tmp != 24 &&
tmp != 32 && tmp != 48 && tmp != 60) {
printk(KERN_WARNING "eicon_isa_boot: invalid memsize\n");
eicon_isa_release_shmem(card);
return -EIO;
}
printk(KERN_INFO "%s: startup-code loaded\n", eicon_ctype_name[card->type]);
if ((card->type == EICON_CTYPE_QUADRO) && (card->master)) {
tmp = eicon_addcard(card->type, card->physmem, card->irq,
((eicon_card *)card->card)->regname, 0);
printk(KERN_INFO "Eicon: %d adapters added\n", tmp);
}
return 0;
}
int
eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb) {
eicon_isa_boot *boot;
int tmp;
unsigned long timeout;
int j;
eicon_isa_codebuf cbuf;
unsigned char *code;
unsigned char *p;
if (copy_from_user(&cbuf, cb, sizeof(eicon_isa_codebuf)))
return -EFAULT;
if (!(code = kmalloc(cbuf.firmware_len, GFP_KERNEL))) {
printk(KERN_WARNING "eicon_isa_load: Couldn't allocate code buffer\n");
return -ENOMEM;
}
if (copy_from_user(code, &cb->code, cbuf.firmware_len)) {
kfree(code);
return -EFAULT;
}
boot = &card->shmem->boot;
if ((!card->ivalid) && card->master) {
card->irqprobe = 1;
/* Check for valid IRQ */
if ((card->irq < 0) || (card->irq > 15) ||
(!((1 << card->irq) & eicon_isa_valid_irq[card->type & 0x0f]))) {
printk(KERN_WARNING "eicon_isa_load: invalid irq: %d\n", card->irq);
eicon_isa_release_shmem(card);
kfree(code);
return -EINVAL;
}
/* Register irq */
if (!request_irq(card->irq, &eicon_irq, 0, "Eicon ISA ISDN", card))
card->ivalid = 1;
else {
printk(KERN_WARNING "eicon_isa_load: irq %d already in use.\n",
card->irq);
eicon_isa_release_shmem(card);
kfree(code);
return -EBUSY;
}
}
tmp = readb(&boot->msize);
if (tmp != 8 && tmp != 16 && tmp != 24 &&
tmp != 32 && tmp != 48 && tmp != 60) {
printk(KERN_WARNING "eicon_isa_load: invalid memsize\n");
eicon_isa_release_shmem(card);
return -EIO;
}
eicon_isa_printpar(card);
/* Download firmware */
printk(KERN_INFO "%s %dkB, loading firmware ...\n",
eicon_ctype_name[card->type],
tmp * 16);
tmp = cbuf.firmware_len >> 8;
p = code;
while (tmp--) {
memcpy_toio(&boot->b, p, 256);
writeb(1, &boot->ctrl);
timeout = jiffies + HZ / 10;
while (time_before(jiffies, timeout)) {
if (readb(&boot->ctrl) == 0)
break;
SLEEP(2);
}
if (readb(&boot->ctrl)) {
printk(KERN_WARNING "eicon_isa_load: download timeout at 0x%x\n", p-code);
eicon_isa_release(card);
kfree(code);
return -EIO;
}
p += 256;
}
kfree(code);
/* Initialize firmware parameters */
memcpy_toio(&card->shmem->c[8], &cbuf.tei, 14);
memcpy_toio(&card->shmem->c[32], &cbuf.oad, 96);
memcpy_toio(&card->shmem->c[128], &cbuf.oad, 96);
/* Start firmware, wait for signature */
writeb(2, &boot->ctrl);
timeout = jiffies + (5*HZ);
while (time_before(jiffies, timeout)) {
if (readw(&boot->signature) == 0x4447)
break;
SLEEP(2);
}
if (readw(&boot->signature) != 0x4447) {
printk(KERN_WARNING "eicon_isa_load: firmware selftest failed %04x\n",
readw(&boot->signature));
eicon_isa_release(card);
return -EIO;
}
card->channels = readb(&card->shmem->c[0x3f6]);
/* clear irq-requests, reset irq-count */
readb(card->intack);
writeb(0, card->intack);
if (card->master) {
card->irqprobe = 1;
/* Trigger an interrupt and check if it is delivered */
tmp = readb(&card->shmem->com.ReadyInt);
tmp ++;
writeb(tmp, &card->shmem->com.ReadyInt);
timeout = jiffies + HZ / 5;
while (time_before(jiffies, timeout)) {
if (card->irqprobe > 1)
break;
SLEEP(2);
}
if (card->irqprobe == 1) {
printk(KERN_WARNING "eicon_isa_load: IRQ # %d test failed\n", card->irq);
eicon_isa_release(card);
return -EIO;
}
}
#ifdef EICON_MCA_DEBUG
printk(KERN_INFO "eicon_isa_load: IRQ # %d test succeeded.\n", card->irq);
#endif
writeb(card->irq, &card->shmem->com.Int);
/* initializing some variables */
((eicon_card *)card->card)->ReadyInt = 0;
((eicon_card *)card->card)->ref_in = 1;
((eicon_card *)card->card)->ref_out = 1;
for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
for(j=0; j< (card->channels + 1); j++) {
((eicon_card *)card->card)->bch[j].e.busy = 0;
((eicon_card *)card->card)->bch[j].e.D3Id = 0;
((eicon_card *)card->card)->bch[j].e.B2Id = 0;
((eicon_card *)card->card)->bch[j].e.ref = 0;
((eicon_card *)card->card)->bch[j].e.Req = 0;
((eicon_card *)card->card)->bch[j].e.complete = 1;
((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
}
printk(KERN_INFO "Eicon: Supported channels: %d\n", card->channels);
printk(KERN_INFO "%s successfully started\n", eicon_ctype_name[card->type]);
/* Enable normal IRQ processing */
card->irqprobe = 0;
return 0;
}
#endif /* CONFIG_ISDN_DRV_EICON_ISA */
/* $Id: eicon_isa.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $
*
* ISDN low-level module for Eicon active ISDN-Cards.
*
* Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
* Copyright 1998-2000 by Armin Schindler (mac@melware.de)
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef eicon_isa_h
#define eicon_isa_h
#ifdef __KERNEL__
#include <linux/config.h>
/* Factory defaults for ISA-Cards */
#define EICON_ISA_MEMBASE 0xd0000
#define EICON_ISA_IRQ 3
/* shmem offset for Quadro parts */
#define EICON_ISA_QOFFSET 0x0800
typedef struct {
__u16 length __attribute__ ((packed)); /* length of data/parameter field */
__u8 P[270]; /* data/parameter field */
} eicon_scom_PBUFFER;
/* General communication buffer */
typedef struct {
__u8 Req; /* request register */
__u8 ReqId; /* request task/entity identification */
__u8 Rc; /* return code register */
__u8 RcId; /* return code task/entity identification */
__u8 Ind; /* Indication register */
__u8 IndId; /* Indication task/entity identification */
__u8 IMask; /* Interrupt Mask Flag */
__u8 RNR; /* Receiver Not Ready (set by PC) */
__u8 XLock; /* XBuffer locked Flag */
__u8 Int; /* ISDN interrupt */
__u8 ReqCh; /* Channel field for layer-3 Requests */
__u8 RcCh; /* Channel field for layer-3 Returncodes */
__u8 IndCh; /* Channel field for layer-3 Indications */
__u8 MInd; /* more data indication field */
__u16 MLength; /* more data total packet length */
__u8 ReadyInt; /* request field for ready interrupt */
__u8 Reserved[12]; /* reserved space */
__u8 IfType; /* 1 = 16k-Interface */
__u16 Signature __attribute__ ((packed)); /* ISDN adapter Signature */
eicon_scom_PBUFFER XBuffer; /* Transmit Buffer */
eicon_scom_PBUFFER RBuffer; /* Receive Buffer */
} eicon_isa_com;
/* struct for downloading firmware */
typedef struct {
__u8 ctrl;
__u8 card;
__u8 msize;
__u8 fill0;
__u16 ebit __attribute__ ((packed));
__u32 eloc __attribute__ ((packed));
__u8 reserved[20];
__u16 signature __attribute__ ((packed));
__u8 fill[224];
__u8 b[256];
} eicon_isa_boot;
/* Shared memory */
typedef union {
unsigned char c[0x400];
eicon_isa_com com;
eicon_isa_boot boot;
} eicon_isa_shmem;
/*
* card's description
*/
typedef struct {
int ramsize;
int irq; /* IRQ */
unsigned long physmem; /* physical memory address */
#ifdef CONFIG_MCA
int io; /* IO-port for MCA brand */
#endif /* CONFIG_MCA */
void* card;
eicon_isa_shmem* shmem; /* Shared-memory area */
unsigned char* intack; /* Int-Acknowledge */
unsigned char* stopcpu; /* Writing here stops CPU */
unsigned char* startcpu; /* Writing here starts CPU */
unsigned char type; /* card type */
int channels; /* No. of channels */
unsigned char irqprobe; /* Flag: IRQ-probing */
unsigned char mvalid; /* Flag: Memory is valid */
unsigned char ivalid; /* Flag: IRQ is valid */
unsigned char master; /* Flag: Card ist Quadro 1/4 */
} eicon_isa_card;
/* Offsets for special locations on standard cards */
#define INTACK 0x03fe
#define STOPCPU 0x0400
#define STARTCPU 0x0401
#define RAMSIZE 0x0400
/* Offsets for special location on PRI card */
#define INTACK_P 0x3ffc
#define STOPCPU_P 0x3ffe
#define STARTCPU_P 0x3fff
#define RAMSIZE_P 0x4000
extern int eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb);
extern int eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb);
extern void eicon_isa_release(eicon_isa_card *card);
extern void eicon_isa_printpar(eicon_isa_card *card);
extern void eicon_isa_transmit(eicon_isa_card *card);
extern int eicon_isa_find_card(int Mem, int Irq, char * Id);
#endif /* __KERNEL__ */
#endif /* eicon_isa_h */
/* $Id: eicon_mod.c,v 1.1.4.1.2.4 2002/10/01 11:29:13 armin Exp $
*
* ISDN lowlevel-module for Eicon active cards.
*
* Copyright 1997 by Fritz Elfert (fritz@isdn4linux.de)
* Copyright 1998-2000 by Armin Schindler (mac@melware.de)
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
* Thanks to Eicon Networks for
* documents, informations and hardware.
*
* Deutsche Mailbox Saar-Lor-Lux GmbH
* for sponsoring and testing fax
* capabilities with Diva Server cards.
* (dor@deutschemailbox.de)
*
*/
#define DRIVERNAME "Eicon active ISDN driver"
#define DRIVERRELEASE "2.0"
#define DRIVERPATCH ".16"
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#ifdef CONFIG_MCA
#include <linux/mca.h>
#include <linux/mca-legacy.h>
#endif /* CONFIG_MCA */
#include "eicon.h"
#include <linux/isdn/capicmd.h>
#undef N_DATA
#include "adapter.h"
#include "uxio.h"
#define INCLUDE_INLINE_FUNCS
static eicon_card *cards = (eicon_card *) NULL; /* glob. var , contains
start of card-list */
static char *eicon_revision = "$Revision: 1.1.4.1.2.4 $";
extern char *eicon_pci_revision;
extern char *eicon_isa_revision;
extern char *eicon_idi_revision;
extern int do_ioctl(struct inode *pDivasInode, struct file *pDivasFile,
unsigned int command, unsigned long arg);
extern void eicon_pci_init_conf(eicon_card *card);
#define EICON_CTRL_VERSION 2
ulong DebugVar;
spinlock_t eicon_lock;
DESCRIPTOR idi_d[32];
/* Parameters to be set by insmod */
#ifdef CONFIG_ISDN_DRV_EICON_ISA
static int membase = -1;
static int irq = -1;
#endif
static char *id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
MODULE_DESCRIPTION( "ISDN4Linux: Driver for Eicon active ISDN cards");
MODULE_AUTHOR( "Armin Schindler");
MODULE_LICENSE( "GPL");
MODULE_PARM_DESC(id, "ID-String of first card");
MODULE_PARM(id, "s");
#ifdef CONFIG_ISDN_DRV_EICON_ISA
MODULE_PARM_DESC(membase, "Base address of first ISA card");
MODULE_PARM_DESC(irq, "IRQ of first card");
MODULE_PARM(membase, "i");
MODULE_PARM(irq, "i");
#endif
char *eicon_ctype_name[] = {
"ISDN-S",
"ISDN-SX",
"ISDN-SCOM",
"ISDN-QUADRO",
"ISDN-S2M",
"DIVA Server BRI/PCI",
"DIVA Server 4BRI/PCI",
"DIVA Server 4BRI/PCI",
"DIVA Server PRI/PCI"
};
static char *
eicon_getrev(const char *revision)
{
char *rev;
char *p;
if ((p = strchr(revision, ':'))) {
rev = p + 2;
p = strchr(rev, '$');
*--p = 0;
} else rev = "?.??";
return rev;
}
static eicon_chan *
find_channel(eicon_card *card, int channel)
{
if ((channel >= 0) && (channel < card->nchannels))
return &(card->bch[channel]);
eicon_log(card, 1, "eicon: Invalid channel %d\n", channel);
return NULL;
}
#ifdef CONFIG_PCI
#ifdef CONFIG_ISDN_DRV_EICON_PCI
/*
* Find pcicard with given card number
*/
static inline eicon_card *
eicon_findnpcicard(int driverid)
{
eicon_card *p = cards;
while (p) {
if ((p->regname[strlen(p->regname)-1] == (driverid + '0')) &&
(p->bus == EICON_BUS_PCI))
return p;
p = p->next;
}
return (eicon_card *) 0;
}
#endif
#endif /* CONFIG_PCI */
static void
eicon_rcv_dispatch(unsigned long context)
{
struct eicon_card *card = (struct eicon_card *)context;
switch (card->bus) {
case EICON_BUS_ISA:
case EICON_BUS_MCA:
case EICON_BUS_PCI:
eicon_io_rcv_dispatch(card);
break;
default:
eicon_log(card, 1,
"eicon_rcv_dispatch: Illegal bustype %d\n", card->bus);
}
}
static void
eicon_ack_dispatch(unsigned long context)
{
struct eicon_card *card = (struct eicon_card *)context;
switch (card->bus) {
case EICON_BUS_ISA:
case EICON_BUS_MCA:
case EICON_BUS_PCI:
eicon_io_ack_dispatch(card);
break;
default:
eicon_log(card, 1,
"eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
}
}
static void
eicon_transmit(unsigned long context)
{
struct eicon_card *card = (struct eicon_card *)context;
switch (card->bus) {
case EICON_BUS_ISA:
case EICON_BUS_MCA:
case EICON_BUS_PCI:
eicon_io_transmit(card);
break;
default:
eicon_log(card, 1,
"eicon_transmit: Illegal bustype %d\n", card->bus);
}
}
static int
eicon_command(eicon_card * card, isdn_ctrl * c)
{
ulong a;
eicon_chan *chan;
eicon_cdef cdef;
#ifdef CONFIG_PCI
#ifdef CONFIG_ISDN_DRV_EICON_PCI
dia_start_t dstart;
int idi_length = 0;
#endif
#endif
isdn_ctrl cmd;
int ret = 0;
unsigned long flags;
eicon_log(card, 16, "eicon_cmd 0x%x with arg 0x%lx (0x%lx)\n",
c->command, c->arg, (ulong) *c->parm.num);
switch (c->command) {
case ISDN_CMD_IOCTL:
memcpy(&a, c->parm.num, sizeof(ulong));
switch (c->arg) {
case EICON_IOCTL_GETVER:
return(EICON_CTRL_VERSION);
case EICON_IOCTL_GETTYPE:
if (card->bus == EICON_BUS_PCI) {
if (copy_to_user((char *)a,
&card->hwif.pci.master,
sizeof(int)))
return -EFAULT;
}
return(card->type);
case EICON_IOCTL_GETMMIO:
switch (card->bus) {
case EICON_BUS_ISA:
case EICON_BUS_MCA:
return (int)card->hwif.isa.shmem;
default:
eicon_log(card, 1,
"eicon: Illegal BUS type %d\n",
card->bus);
ret = -ENODEV;
}
#ifdef CONFIG_ISDN_DRV_EICON_ISA
case EICON_IOCTL_SETMMIO:
if (card->flags & EICON_FLAGS_LOADED)
return -EBUSY;
switch (card->bus) {
case EICON_BUS_ISA:
if (eicon_isa_find_card(a,
card->hwif.isa.irq,
card->regname) < 0)
return -EFAULT;
card->hwif.isa.shmem = (eicon_isa_shmem *)a;
return 0;
case EICON_BUS_MCA:
#ifdef CONFIG_MCA
if (eicon_mca_find_card(
0, a,
card->hwif.isa.irq,
card->regname) < 0)
return -EFAULT;
card->hwif.isa.shmem = (eicon_isa_shmem *)a;
return 0;
#endif /* CONFIG_MCA */
default:
eicon_log(card, 1,
"eicon: Illegal BUS type %d\n",
card->bus);
ret = -ENODEV;
}
#endif
case EICON_IOCTL_GETIRQ:
switch (card->bus) {
case EICON_BUS_ISA:
case EICON_BUS_MCA:
return card->hwif.isa.irq;
default:
eicon_log(card, 1,
"eicon: Illegal BUS type %d\n",
card->bus);
ret = -ENODEV;
}
case EICON_IOCTL_SETIRQ:
if (card->flags & EICON_FLAGS_LOADED)
return -EBUSY;
if ((a < 2) || (a > 15))
return -EFAULT;
switch (card->bus) {
case EICON_BUS_ISA:
case EICON_BUS_MCA:
card->hwif.isa.irq = a;
return 0;
default:
eicon_log(card, 1,
"eicon: Illegal BUS type %d\n",
card->bus);
ret = -ENODEV;
}
#ifdef CONFIG_ISDN_DRV_EICON_ISA
case EICON_IOCTL_LOADBOOT:
if (card->flags & EICON_FLAGS_RUNNING)
return -EBUSY;
switch (card->bus) {
case EICON_BUS_ISA:
case EICON_BUS_MCA:
ret = eicon_isa_bootload(
&(card->hwif.isa),
&(((eicon_codebuf *)a)->isa));
break;
default:
eicon_log(card, 1,
"eicon: Illegal BUS type %d\n",
card->bus);
ret = -ENODEV;
}
return ret;
#endif
#ifdef CONFIG_ISDN_DRV_EICON_ISA
case EICON_IOCTL_LOADISA:
if (card->flags & EICON_FLAGS_RUNNING)
return -EBUSY;
switch (card->bus) {
case EICON_BUS_ISA:
case EICON_BUS_MCA:
ret = eicon_isa_load(
&(card->hwif.isa),
&(((eicon_codebuf *)a)->isa));
if (!ret) {
card->flags |= EICON_FLAGS_LOADED;
card->flags |= EICON_FLAGS_RUNNING;
if (card->hwif.isa.channels > 1) {
cmd.command = ISDN_STAT_ADDCH;
cmd.driver = card->myid;
cmd.arg = card->hwif.isa.channels - 1;
card->interface.statcallb(&cmd);
}
cmd.command = ISDN_STAT_RUN;
cmd.driver = card->myid;
cmd.arg = 0;
card->interface.statcallb(&cmd);
}
break;
default:
eicon_log(card, 1,
"eicon: Illegal BUS type %d\n",
card->bus);
ret = -ENODEV;
}
return ret;
#endif
case EICON_IOCTL_MANIF:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!card->d)
return -ENODEV;
if (!card->d->features & DI_MANAGE)
return -ENODEV;
ret = eicon_idi_manage(
card,
(eicon_manifbuf *)a);
return ret;
case EICON_IOCTL_GETXLOG:
return -ENODEV;
case EICON_IOCTL_ADDCARD:
if (copy_from_user(&cdef, (char *)a,
sizeof(cdef)))
return -EFAULT;
if (!(eicon_addcard(0, cdef.membase, cdef.irq, cdef.id, 0)))
return -EIO;
return 0;
case EICON_IOCTL_DEBUGVAR:
DebugVar = a;
eicon_log(card, 1, "Eicon: Debug Value set to %ld\n", DebugVar);
return 0;
case EICON_IOCTL_LOADPCI:
eicon_log(card, 1, "Eicon: Wrong version of load-utility,\n");
eicon_log(card, 1, "Eicon: re-compile eiconctrl !\n");
eicon_log(card, 1, "Eicon: Maybe update of utility is necessary !\n");
return -EINVAL;
default:
#ifdef CONFIG_PCI
#ifdef CONFIG_ISDN_DRV_EICON_PCI
if (c->arg < EICON_IOCTL_DIA_OFFSET)
return -EINVAL;
if (copy_from_user(&dstart, (char *)a,
sizeof(dstart)))
return -EFAULT;
if (!(card = eicon_findnpcicard(dstart.card_id)))
return -EINVAL;
ret = do_ioctl(NULL, NULL,
c->arg - EICON_IOCTL_DIA_OFFSET,
(unsigned long) a);
if (((c->arg - EICON_IOCTL_DIA_OFFSET)==DIA_IOCTL_START) && (!ret)) {
if (card->type != EICON_CTYPE_MAESTRAQ) {
DIVA_DIDD_Read(idi_d, sizeof(idi_d));
for(idi_length = 0; idi_length < 32; idi_length++) {
if (idi_d[idi_length].type == 0) break;
}
if ((idi_length < 1) || (idi_length >= 32)) {
eicon_log(card, 1, "eicon: invalid idi table length.\n");
break;
}
card->d = &idi_d[idi_length - 1];
card->flags |= EICON_FLAGS_LOADED;
card->flags |= EICON_FLAGS_RUNNING;
eicon_pci_init_conf(card);
if (card->d->channels > 1) {
cmd.command = ISDN_STAT_ADDCH;
cmd.driver = card->myid;
cmd.arg = card->d->channels - 1;
card->interface.statcallb(&cmd);
}
cmd.command = ISDN_STAT_RUN;
cmd.driver = card->myid;
cmd.arg = 0;
card->interface.statcallb(&cmd);
eicon_log(card, 1, "Eicon: %s started, %d channels (feat. 0x%x)\n",
(card->type == EICON_CTYPE_MAESTRA) ? "BRI" : "PRI",
card->d->channels, card->d->features);
} else {
int i;
DIVA_DIDD_Read(idi_d, sizeof(idi_d));
for(idi_length = 0; idi_length < 32; idi_length++)
if (idi_d[idi_length].type == 0) break;
if ((idi_length < 1) || (idi_length >= 32)) {
eicon_log(card, 1, "eicon: invalid idi table length.\n");
break;
}
for(i = 3; i >= 0; i--) {
if (!(card = eicon_findnpcicard(dstart.card_id - i)))
return -EINVAL;
card->flags |= EICON_FLAGS_LOADED;
card->flags |= EICON_FLAGS_RUNNING;
card->d = &idi_d[idi_length - (i+1)];
eicon_pci_init_conf(card);
if (card->d->channels > 1) {
cmd.command = ISDN_STAT_ADDCH;
cmd.driver = card->myid;
cmd.arg = card->d->channels - 1;
card->interface.statcallb(&cmd);
}
cmd.command = ISDN_STAT_RUN;
cmd.driver = card->myid;
cmd.arg = 0;
card->interface.statcallb(&cmd);
eicon_log(card, 1, "Eicon: %d/4BRI started, %d channels (feat. 0x%x)\n",
4-i, card->d->channels, card->d->features);
}
}
}
return ret;
#else
return -EINVAL;
#endif
#endif /* CONFIG_PCI */
}
break;
case ISDN_CMD_DIAL:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, c->arg & 0x1f)))
break;
spin_lock_irqsave(&eicon_lock, flags);
if ((chan->fsm_state != EICON_STATE_NULL) && (chan->fsm_state != EICON_STATE_LISTEN)) {
spin_unlock_irqrestore(&eicon_lock, flags);
eicon_log(card, 1, "Dial on channel %d with state %d\n",
chan->No, chan->fsm_state);
return -EBUSY;
}
chan->fsm_state = EICON_STATE_OCALL;
spin_unlock_irqrestore(&eicon_lock, flags);
ret = idi_connect_req(card, chan, c->parm.setup.phone,
c->parm.setup.eazmsn,
c->parm.setup.si1,
c->parm.setup.si2);
if (ret) {
cmd.driver = card->myid;
cmd.command = ISDN_STAT_DHUP;
cmd.arg &= 0x1f;
card->interface.statcallb(&cmd);
}
return ret;
case ISDN_CMD_ACCEPTD:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, c->arg & 0x1f)))
break;
if (chan->fsm_state == EICON_STATE_ICALL) {
idi_connect_res(card, chan);
}
return 0;
case ISDN_CMD_ACCEPTB:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
return 0;
case ISDN_CMD_HANGUP:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, c->arg & 0x1f)))
break;
idi_hangup(card, chan);
return 0;
case ISDN_CMD_SETEAZ:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, c->arg & 0x1f)))
break;
chan->eazmask = 0x3ff;
eicon_idi_listen_req(card, chan);
return 0;
case ISDN_CMD_CLREAZ:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, c->arg & 0x1f)))
break;
chan->eazmask = 0;
eicon_idi_listen_req(card, chan);
return 0;
case ISDN_CMD_SETL2:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, c->arg & 0x1f)))
break;
chan->l2prot = (c->arg >> 8);
return 0;
case ISDN_CMD_SETL3:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, c->arg & 0x1f)))
break;
chan->l3prot = (c->arg >> 8);
#ifdef CONFIG_ISDN_TTY_FAX
if (chan->l3prot == ISDN_PROTO_L3_FCLASS2) {
chan->fax = c->parm.fax;
eicon_log(card, 128, "idi_cmd: Ch%d: SETL3 struct fax=0x%x\n",chan->No, chan->fax);
}
#endif
return 0;
#ifdef CONFIG_ISDN_TTY_FAX
case ISDN_CMD_FAXCMD:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, c->arg & 0x1f)))
break;
if (!chan->fax)
break;
idi_fax_cmd(card, chan);
return 0;
#endif
case ISDN_CMD_AUDIO:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, c->arg & 0x1f)))
break;
idi_audio_cmd(card, chan, c->arg >> 8, c->parm.num);
return 0;
case CAPI_PUT_MESSAGE:
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, c->arg & 0x1f)))
break;
if (c->parm.cmsg.Length < 8)
break;
switch(c->parm.cmsg.Command) {
case CAPI_FACILITY:
if (c->parm.cmsg.Subcommand == CAPI_REQ)
return(capipmsg(card, chan, &c->parm.cmsg));
break;
case CAPI_MANUFACTURER:
default:
break;
}
return 0;
}
return -EINVAL;
}
/*
* Find card with given driverId
*/
static inline eicon_card *
eicon_findcard(int driverid)
{
eicon_card *p = cards;
while (p) {
if (p->myid == driverid)
return p;
p = p->next;
}
return (eicon_card *) 0;
}
/*
* Wrapper functions for interface to linklevel
*/
static int
if_command(isdn_ctrl * c)
{
eicon_card *card = eicon_findcard(c->driver);
if (card)
return (eicon_command(card, c));
printk(KERN_ERR
"eicon: if_command %d called with invalid driverId %d!\n",
c->command, c->driver);
return -ENODEV;
}
static int
if_writecmd(const u_char * buf, int len, int user, int id, int channel)
{
return (len);
}
static int
if_readstatus(u_char * buf, int len, int user, int id, int channel)
{
int count = 0;
int cnt = 0;
ulong flags = 0;
u_char *p = buf;
struct sk_buff *skb;
eicon_card *card = eicon_findcard(id);
if (card) {
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
spin_lock_irqsave(&eicon_lock, flags);
while((skb = skb_dequeue(&card->statq))) {
if ((skb->len + count) > len)
cnt = len - count;
else
cnt = skb->len;
if (user) {
spin_unlock_irqrestore(&eicon_lock, flags);
if (copy_to_user(p, skb->data, cnt))
return -EFAULT;
spin_lock_irqsave(&eicon_lock, flags);
}
else
memcpy(p, skb->data, cnt);
count += cnt;
p += cnt;
if (cnt == skb->len) {
dev_kfree_skb(skb);
if (card->statq_entries > 0)
card->statq_entries--;
} else {
skb_pull(skb, cnt);
skb_queue_head(&card->statq, skb);
spin_unlock_irqrestore(&eicon_lock, flags);
return count;
}
}
card->statq_entries = 0;
spin_unlock_irqrestore(&eicon_lock, flags);
return count;
}
printk(KERN_ERR
"eicon: if_readstatus called with invalid driverId!\n");
return 0;
}
static int
if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
{
eicon_card *card = eicon_findcard(id);
eicon_chan *chan;
int ret = 0;
int len;
len = skb->len;
if (card) {
if (!card->flags & EICON_FLAGS_RUNNING)
return -ENODEV;
if (!(chan = find_channel(card, channel)))
return -ENODEV;
if (chan->fsm_state == EICON_STATE_ACTIVE) {
#ifdef CONFIG_ISDN_TTY_FAX
if (chan->l2prot == ISDN_PROTO_L2_FAX) {
if ((ret = idi_faxdata_send(card, chan, skb)) > 0)
ret = len;
}
else
#endif
ret = idi_send_data(card, chan, ack, skb, 1, 1);
return (ret);
} else {
return -ENODEV;
}
}
printk(KERN_ERR
"eicon: if_sendbuf called with invalid driverId!\n");
return -ENODEV;
}
/* jiftime() copied from HiSax */
static inline int jiftime(char *s, long mark)
{
s += 8;
*s-- = '\0';
*s-- = mark % 10 + '0';
mark /= 10;
*s-- = mark % 10 + '0';
mark /= 10;
*s-- = '.';
*s-- = mark % 10 + '0';
mark /= 10;
*s-- = mark % 6 + '0';
mark /= 6;
*s-- = ':';
*s-- = mark % 10 + '0';
mark /= 10;
*s-- = mark % 10 + '0';
return(8);
}
void
eicon_putstatus(eicon_card * card, char * buf)
{
ulong flags;
int count;
isdn_ctrl cmd;
u_char *p;
struct sk_buff *skb;
if (!card) {
if (!(card = cards))
return;
}
spin_lock_irqsave(&eicon_lock, flags);
count = strlen(buf);
skb = alloc_skb(count, GFP_ATOMIC);
if (!skb) {
spin_unlock_irqrestore(&eicon_lock, flags);
printk(KERN_ERR "eicon: could not alloc skb in putstatus\n");
return;
}
p = skb_put(skb, count);
memcpy(p, buf, count);
skb_queue_tail(&card->statq, skb);
if (card->statq_entries >= MAX_STATUS_BUFFER) {
if ((skb = skb_dequeue(&card->statq))) {
count -= skb->len;
dev_kfree_skb(skb);
} else
count = 0;
} else
card->statq_entries++;
spin_unlock_irqrestore(&eicon_lock, flags);
if (count) {
cmd.command = ISDN_STAT_STAVAIL;
cmd.driver = card->myid;
cmd.arg = count;
card->interface.statcallb(&cmd);
}
}
/*
* Debug and Log
*/
void
eicon_log(eicon_card * card, int level, const char *fmt, ...)
{
va_list args;
char Line[160];
u_char *p;
if ((DebugVar & level) || (DebugVar & 256)) {
va_start(args, fmt);
if (DebugVar & level) {
if (DebugVar & 256) {
/* log-buffer */
p = Line;
p += jiftime(p, jiffies);
*p++ = 32;
p += vsprintf(p, fmt, args);
*p = 0;
eicon_putstatus(card, Line);
} else {
/* printk, syslogd */
vsprintf(Line, fmt, args);
printk(KERN_DEBUG "%s", Line);
}
}
va_end(args);
}
}
/*
* Allocate a new card-struct, initialize it
* link it into cards-list.
*/
static void
eicon_alloccard(int Type, int membase, int irq, char *id, int card_id)
{
int i;
int j;
int qloop;
#ifdef CONFIG_ISDN_DRV_EICON_ISA
char qid[5];
#endif
eicon_card *card;
qloop = (Type == EICON_CTYPE_QUADRO)?2:0;
for (i = 0; i <= qloop; i++) {
if (!(card = (eicon_card *) kmalloc(sizeof(eicon_card), GFP_KERNEL))) {
eicon_log(card, 1,
"eicon: (%s) Could not allocate card-struct.\n", id);
return;
}
memset((char *) card, 0, sizeof(eicon_card));
skb_queue_head_init(&card->sndq);
skb_queue_head_init(&card->rcvq);
skb_queue_head_init(&card->rackq);
skb_queue_head_init(&card->sackq);
skb_queue_head_init(&card->statq);
card->statq_entries = 0;
tasklet_init(&card->snd_tq, eicon_transmit, (unsigned long)card);
tasklet_init(&card->rcv_tq, eicon_rcv_dispatch, (unsigned long)card);
tasklet_init(&card->ack_tq, eicon_ack_dispatch, (unsigned long)card);
card->interface.owner = THIS_MODULE;
card->interface.maxbufsize = 4000;
card->interface.command = if_command;
card->interface.writebuf_skb = if_sendbuf;
card->interface.writecmd = if_writecmd;
card->interface.readstat = if_readstatus;
card->interface.features =
ISDN_FEATURE_L2_X75I |
ISDN_FEATURE_L2_HDLC |
ISDN_FEATURE_L2_TRANS |
ISDN_FEATURE_L3_TRANS |
ISDN_FEATURE_P_UNKNOWN;
card->interface.hl_hdrlen = 20;
card->ptype = ISDN_PTYPE_UNKNOWN;
strlcpy(card->interface.id, id, sizeof(card->interface.id));
card->myid = -1;
card->type = Type;
switch (Type) {
#ifdef CONFIG_ISDN_DRV_EICON_ISA
#ifdef CONFIG_MCA /* only needed for MCA */
case EICON_CTYPE_S:
case EICON_CTYPE_SX:
case EICON_CTYPE_SCOM:
if (MCA_bus) {
if (membase == -1)
membase = EICON_ISA_MEMBASE;
if (irq == -1)
irq = EICON_ISA_IRQ;
card->bus = EICON_BUS_MCA;
card->hwif.isa.card = (void *)card;
card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
card->hwif.isa.physmem = (unsigned long)membase;
card->hwif.isa.master = 1;
card->hwif.isa.irq = irq;
card->hwif.isa.type = Type;
card->nchannels = 2;
card->interface.channels = 1;
} else {
printk(KERN_WARNING
"eicon (%s): no MCA bus detected.\n",
card->interface.id);
kfree(card);
return;
}
break;
#endif /* CONFIG_MCA */
case EICON_CTYPE_QUADRO:
if (membase == -1)
membase = EICON_ISA_MEMBASE;
if (irq == -1)
irq = EICON_ISA_IRQ;
card->bus = EICON_BUS_ISA;
card->hwif.isa.card = (void *)card;
card->hwif.isa.shmem = (eicon_isa_shmem *)(membase + (i+1) * EICON_ISA_QOFFSET);
card->hwif.isa.physmem = (unsigned long)(membase + (i+1) * EICON_ISA_QOFFSET);
card->hwif.isa.master = 0;
strcpy(card->interface.id, id);
if (id[strlen(id) - 1] == 'a') {
card->interface.id[strlen(id) - 1] = 'a' + i + 1;
} else {
sprintf(qid, "_%c",'2' + i);
strcat(card->interface.id, qid);
}
printk(KERN_INFO "Eicon: Quadro: Driver-Id %s added.\n",
card->interface.id);
if (i == 0) {
eicon_card *p = cards;
while(p) {
if ((p->hwif.isa.master) && (p->hwif.isa.irq == irq)) {
p->qnext = card;
break;
}
p = p->next;
}
if (!p) {
eicon_log(card, 1, "eicon_alloccard: Quadro Master not found.\n");
kfree(card);
return;
}
} else {
cards->qnext = card;
}
card->hwif.isa.irq = irq;
card->hwif.isa.type = Type;
card->nchannels = 2;
card->interface.channels = 1;
break;
#endif
#ifdef CONFIG_PCI
#ifdef CONFIG_ISDN_DRV_EICON_PCI
case EICON_CTYPE_MAESTRA:
card->bus = EICON_BUS_PCI;
card->interface.features |=
ISDN_FEATURE_L2_V11096 |
ISDN_FEATURE_L2_V11019 |
ISDN_FEATURE_L2_V11038 |
ISDN_FEATURE_L2_MODEM |
ISDN_FEATURE_L2_FAX |
ISDN_FEATURE_L3_TRANSDSP |
ISDN_FEATURE_L3_FCLASS2;
card->hwif.pci.card = (void *)card;
card->hwif.pci.master = card_id;
card->hwif.pci.irq = irq;
card->hwif.pci.type = Type;
card->flags = 0;
card->nchannels = 2;
card->interface.channels = 1;
break;
case EICON_CTYPE_MAESTRAQ:
card->bus = EICON_BUS_PCI;
card->interface.features |=
ISDN_FEATURE_L2_V11096 |
ISDN_FEATURE_L2_V11019 |
ISDN_FEATURE_L2_V11038 |
ISDN_FEATURE_L2_MODEM |
ISDN_FEATURE_L2_FAX |
ISDN_FEATURE_L3_TRANSDSP |
ISDN_FEATURE_L3_FCLASS2;
card->hwif.pci.card = (void *)card;
card->hwif.pci.master = card_id;
card->hwif.pci.irq = irq;
card->hwif.pci.type = Type;
card->flags = 0;
card->nchannels = 2;
card->interface.channels = 1;
break;
case EICON_CTYPE_MAESTRAP:
card->bus = EICON_BUS_PCI;
card->interface.features |=
ISDN_FEATURE_L2_V11096 |
ISDN_FEATURE_L2_V11019 |
ISDN_FEATURE_L2_V11038 |
ISDN_FEATURE_L2_MODEM |
ISDN_FEATURE_L2_FAX |
ISDN_FEATURE_L3_TRANSDSP |
ISDN_FEATURE_L3_FCLASS2;
card->hwif.pci.card = (void *)card;
card->hwif.pci.master = card_id;
card->hwif.pci.irq = irq;
card->hwif.pci.type = Type;
card->flags = 0;
card->nchannels = 30;
card->interface.channels = 1;
break;
#endif
#endif
#ifdef CONFIG_ISDN_DRV_EICON_ISA
case EICON_CTYPE_ISABRI:
if (membase == -1)
membase = EICON_ISA_MEMBASE;
if (irq == -1)
irq = EICON_ISA_IRQ;
card->bus = EICON_BUS_ISA;
card->hwif.isa.card = (void *)card;
card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
card->hwif.isa.physmem = (unsigned long)membase;
card->hwif.isa.master = 1;
card->hwif.isa.irq = irq;
card->hwif.isa.type = Type;
card->nchannels = 2;
card->interface.channels = 1;
break;
case EICON_CTYPE_ISAPRI:
if (membase == -1)
membase = EICON_ISA_MEMBASE;
if (irq == -1)
irq = EICON_ISA_IRQ;
card->bus = EICON_BUS_ISA;
card->hwif.isa.card = (void *)card;
card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
card->hwif.isa.physmem = (unsigned long)membase;
card->hwif.isa.master = 1;
card->hwif.isa.irq = irq;
card->hwif.isa.type = Type;
card->nchannels = 30;
card->interface.channels = 1;
break;
#endif
default:
eicon_log(card, 1, "eicon_alloccard: Invalid type %d\n", Type);
kfree(card);
return;
}
if (!(card->bch = (eicon_chan *) kmalloc(sizeof(eicon_chan) * (card->nchannels + 1)
, GFP_KERNEL))) {
eicon_log(card, 1,
"eicon: (%s) Could not allocate bch-struct.\n", id);
kfree(card);
return;
}
for (j=0; j< (card->nchannels + 1); j++) {
memset((char *)&card->bch[j], 0, sizeof(eicon_chan));
card->bch[j].statectrl = 0;
card->bch[j].l2prot = ISDN_PROTO_L2_X75I;
card->bch[j].l3prot = ISDN_PROTO_L3_TRANS;
card->bch[j].e.D3Id = 0;
card->bch[j].e.B2Id = 0;
card->bch[j].e.Req = 0;
card->bch[j].No = j;
card->bch[j].tskb1 = NULL;
card->bch[j].tskb2 = NULL;
skb_queue_head_init(&card->bch[j].e.X);
skb_queue_head_init(&card->bch[j].e.R);
}
#ifdef CONFIG_ISDN_DRV_EICON_PCI
/* *** Diva Server *** */
if (!(card->dbuf = (DBUFFER *) kmalloc((sizeof(DBUFFER) * (card->nchannels + 1))*2
, GFP_KERNEL))) {
eicon_log(card, 1,
"eicon: (%s) Could not allocate DBUFFER-struct.\n", id);
kfree(card);
kfree(card->bch);
return;
}
if (!(card->sbuf = (BUFFERS *) kmalloc((sizeof(BUFFERS) * (card->nchannels + 1)) * 2, GFP_KERNEL))) {
eicon_log(card, 1,
"eicon: (%s) Could not allocate BUFFERS-struct.\n", id);
kfree(card);
kfree(card->bch);
kfree(card->dbuf);
return;
}
if (!(card->sbufp = (char *) kmalloc((270 * (card->nchannels + 1)) * 2, GFP_KERNEL))) {
eicon_log(card, 1,
"eicon: (%s) Could not allocate BUFFERSP-struct.\n", id);
kfree(card);
kfree(card->bch);
kfree(card->dbuf);
kfree(card->sbuf);
return;
}
for (j=0; j< (card->nchannels + 1); j++) {
memset((char *)&card->dbuf[j], 0, sizeof(DBUFFER));
card->bch[j].de.RBuffer = (DBUFFER *)&card->dbuf[j];
memset((char *)&card->dbuf[j+(card->nchannels+1)], 0, sizeof(BUFFERS));
card->bch[j].be.RBuffer = (DBUFFER *)&card->dbuf[j+(card->nchannels+1)];
memset((char *)&card->sbuf[j], 0, sizeof(BUFFERS));
card->bch[j].de.X = (BUFFERS *)&card->sbuf[j];
memset((char *)&card->sbuf[j+(card->nchannels+1)], 0, sizeof(BUFFERS));
card->bch[j].be.X = (BUFFERS *)&card->sbuf[j+(card->nchannels+1)];
memset((char *)&card->sbufp[j], 0, 270);
card->bch[j].de.X->P = (char *)&card->sbufp[j * 270];
memset((char *)&card->sbufp[j+(card->nchannels+1)], 0, 270);
card->bch[j].be.X->P = (char *)&card->sbufp[(j+(card->nchannels+1)) * 270];
}
/* *** */
#endif /* CONFIG_ISDN_DRV_EICON_PCI */
card->next = cards;
cards = card;
}
}
/*
* register card at linklevel
*/
static int
eicon_registercard(eicon_card * card)
{
switch (card->bus) {
#ifdef CONFIG_ISDN_DRV_EICON_ISA
case EICON_BUS_ISA:
/* TODO something to print */
break;
#ifdef CONFIG_MCA
case EICON_BUS_MCA:
eicon_isa_printpar(&card->hwif.isa);
break;
#endif /* CONFIG_MCA */
#endif
case EICON_BUS_PCI:
break;
default:
eicon_log(card, 1,
"eicon_registercard: Illegal BUS type %d\n",
card->bus);
return -1;
}
if (!register_isdn(&card->interface)) {
printk(KERN_WARNING
"eicon_registercard: Unable to register %s\n",
card->interface.id);
return -1;
}
card->myid = card->interface.channels;
sprintf(card->regname, "%s", card->interface.id);
return 0;
}
static void __exit
unregister_card(eicon_card * card)
{
isdn_ctrl cmd;
cmd.command = ISDN_STAT_UNLOAD;
cmd.driver = card->myid;
card->interface.statcallb(&cmd);
switch (card->bus) {
#ifdef CONFIG_ISDN_DRV_EICON_ISA
case EICON_BUS_ISA:
#ifdef CONFIG_MCA
case EICON_BUS_MCA:
#endif /* CONFIG_MCA */
eicon_isa_release(&card->hwif.isa);
break;
#endif
case EICON_BUS_PCI:
break;
default:
eicon_log(card, 1,
"eicon: Invalid BUS type %d\n",
card->bus);
break;
}
}
static void
eicon_freecard(eicon_card *card) {
int i;
for(i = 0; i < (card->nchannels + 1); i++) {
skb_queue_purge(&card->bch[i].e.X);
skb_queue_purge(&card->bch[i].e.R);
}
skb_queue_purge(&card->sndq);
skb_queue_purge(&card->rcvq);
skb_queue_purge(&card->rackq);
skb_queue_purge(&card->sackq);
skb_queue_purge(&card->statq);
#ifdef CONFIG_ISDN_DRV_EICON_PCI
kfree(card->sbufp);
kfree(card->sbuf);
kfree(card->dbuf);
#endif
kfree(card->bch);
kfree(card);
}
int
eicon_addcard(int Type, int membase, int irq, char *id, int card_id)
{
eicon_card *p;
eicon_card *q = NULL;
int registered;
int added = 0;
int failed = 0;
#ifdef CONFIG_ISDN_DRV_EICON_ISA
if (!Type) /* ISA */
if ((Type = eicon_isa_find_card(membase, irq, id)) < 0)
return 0;
#endif
eicon_alloccard(Type, membase, irq, id, card_id);
p = cards;
while (p) {
registered = 0;
if (!p->interface.statcallb) {
/* Not yet registered.
* Try to register and activate it.
*/
added++;
switch (p->bus) {
#ifdef CONFIG_ISDN_DRV_EICON_ISA
case EICON_BUS_ISA:
case EICON_BUS_MCA:
if (eicon_registercard(p))
break;
registered = 1;
break;
#endif
case EICON_BUS_PCI:
#ifdef CONFIG_PCI
#ifdef CONFIG_ISDN_DRV_EICON_PCI
if (eicon_registercard(p))
break;
registered = 1;
break;
#endif
#endif
default:
printk(KERN_ERR
"eicon: addcard: Invalid BUS type %d\n",
p->bus);
}
} else
/* Card already registered */
registered = 1;
if (registered) {
/* Init OK, next card ... */
q = p;
p = p->next;
} else {
/* registering failed, remove card from list, free memory */
printk(KERN_ERR
"eicon: Initialization of %s failed\n",
p->interface.id);
if (q) {
q->next = p->next;
eicon_freecard(p);
p = q->next;
} else {
cards = p->next;
eicon_freecard(p);
p = cards;
}
failed++;
}
}
return (added - failed);
}
static int __init
eicon_init(void)
{
int card_count = 0;
char tmprev[50];
DebugVar = 1;
eicon_lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
printk(KERN_INFO "%s Rev: ", DRIVERNAME);
strcpy(tmprev, eicon_revision);
printk("%s/", eicon_getrev(tmprev));
strcpy(tmprev, eicon_pci_revision);
#ifdef CONFIG_ISDN_DRV_EICON_PCI
printk("%s/", eicon_getrev(tmprev));
#else
printk("---/");
#endif
strcpy(tmprev, eicon_isa_revision);
#ifdef CONFIG_ISDN_DRV_EICON_ISA
printk("%s/", eicon_getrev(tmprev));
#else
printk("---/");
#endif
strcpy(tmprev, eicon_idi_revision);
printk("%s\n", eicon_getrev(tmprev));
printk(KERN_INFO "%s Release: %s%s\n", DRIVERNAME,
DRIVERRELEASE, DRIVERPATCH);
#ifdef CONFIG_ISDN_DRV_EICON_ISA
#ifdef CONFIG_MCA
/* Check if we have MCA-bus */
if (!MCA_bus)
{
printk(KERN_INFO
"eicon: No MCA bus, ISDN-interfaces not probed.\n");
} else {
eicon_log(NULL, 8,
"eicon_mca_find_card, irq=%d.\n",
irq);
if (!eicon_mca_find_card(0, membase, irq, id))
card_count++;
};
#else
card_count = eicon_addcard(0, membase, irq, id, 0);
#endif /* CONFIG_MCA */
#endif /* CONFIG_ISDN_DRV_EICON_ISA */
#ifdef CONFIG_PCI
#ifdef CONFIG_ISDN_DRV_EICON_PCI
DivasCardsDiscover();
card_count += eicon_pci_find_card(id);
#endif
#endif
if (!cards) {
#ifdef MODULE
#ifndef CONFIG_ISDN_DRV_EICON_PCI
#ifndef CONFIG_ISDN_DRV_EICON_ISA
printk(KERN_INFO "Eicon: Driver is neither ISA nor PCI compiled !\n");
printk(KERN_INFO "Eicon: Driver not loaded !\n");
#else
printk(KERN_INFO "Eicon: No cards defined, driver not loaded !\n");
#endif
#else
printk(KERN_INFO "Eicon: No PCI-cards found, driver not loaded !\n");
#endif
#endif /* MODULE */
return -ENODEV;
} else
printk(KERN_INFO "Eicon: %d card%s added\n", card_count,
(card_count>1)?"s":"");
return 0;
}
#ifdef CONFIG_ISDN_DRV_EICON_PCI
void DIVA_DIDD_Write(DESCRIPTOR *, int);
EXPORT_SYMBOL_NOVERS(DIVA_DIDD_Read);
EXPORT_SYMBOL_NOVERS(DIVA_DIDD_Write);
EXPORT_SYMBOL_NOVERS(DivasPrintf);
#else
int DivasCardNext;
card_t DivasCards[1];
#endif
static void __exit
eicon_exit(void)
{
#ifdef CONFIG_PCI
#ifdef CONFIG_ISDN_DRV_EICON_PCI
card_t *pCard;
word wCardIndex;
extern int Divas_major;
int iTmp = 0;
#endif
#endif
eicon_card *card = cards;
eicon_card *last;
while (card) {
#ifdef CONFIG_ISDN_DRV_EICON_ISA
#ifdef CONFIG_MCA
if (MCA_bus)
{
mca_mark_as_unused (card->mca_slot);
mca_set_adapter_procfn(card->mca_slot, NULL, NULL);
};
#endif /* CONFIG_MCA */
#endif
unregister_card(card);
card = card->next;
}
card = cards;
while (card) {
last = card;
card = card->next;
eicon_freecard(last);
}
#ifdef CONFIG_PCI
#ifdef CONFIG_ISDN_DRV_EICON_PCI
pCard = DivasCards;
for (wCardIndex = 0; wCardIndex < MAX_CARDS; wCardIndex++)
{
if ((pCard->hw) && (pCard->hw->in_use))
{
(*pCard->card_reset)(pCard);
UxIsrRemove(pCard->hw, pCard);
UxCardHandleFree(pCard->hw);
if(pCard->e_tbl != NULL)
{
kfree(pCard->e_tbl);
}
if(pCard->hw->card_type == DIA_CARD_TYPE_DIVA_SERVER_B)
{
release_region(pCard->hw->io_base,0x20);
release_region(pCard->hw->reset_base,0x80);
}
// If this is a 4BRI ...
if (pCard->hw->card_type == DIA_CARD_TYPE_DIVA_SERVER_Q)
{
// Skip over the next 3 virtual adapters
wCardIndex += 3;
// But free their handles
for (iTmp = 0; iTmp < 3; iTmp++)
{
pCard++;
UxCardHandleFree(pCard->hw);
if(pCard->e_tbl != NULL)
{
kfree(pCard->e_tbl);
}
}
}
}
pCard++;
}
unregister_chrdev(Divas_major, "Divas");
#endif
#endif /* CONFIG_PCI */
printk(KERN_INFO "%s unloaded\n", DRIVERNAME);
}
#ifndef MODULE
static int __init
eicon_setup(char *line)
{
int i, argc;
int ints[5];
char *str;
str = get_options(line, 4, ints);
argc = ints[0];
i = 1;
#ifdef CONFIG_ISDN_DRV_EICON_ISA
if (argc) {
membase = irq = -1;
if (argc) {
membase = ints[i];
i++;
argc--;
}
if (argc) {
irq = ints[i];
i++;
argc--;
}
if (strlen(str)) {
strcpy(id, str);
} else {
strcpy(id, "eicon");
}
printk(KERN_INFO "Eicon ISDN active driver setup (id=%s membase=0x%x irq=%d)\n",
id, membase, irq);
}
#else
printk(KERN_INFO "Eicon ISDN active driver setup\n");
#endif
return(1);
}
__setup("eicon=", eicon_setup);
#endif /* MODULE */
#ifdef CONFIG_ISDN_DRV_EICON_ISA
#ifdef CONFIG_MCA
struct eicon_mca_adapters_struct {
char * name;
int adf_id;
};
/* possible MCA-brands of eicon cards */
struct eicon_mca_adapters_struct eicon_mca_adapters[] = {
{ "ISDN-P/2 Adapter", 0x6abb },
{ "ISDN-[S|SX|SCOM]/2 Adapter", 0x6a93 },
{ "DIVA /MCA", 0x6336 },
{ NULL, 0 },
};
int eicon_mca_find_card(int type, /* type-idx of eicon-card */
int membase,
int irq,
char * id) /* name of eicon-isdn-dev */
{
int j, curr_slot = 0;
eicon_log(NULL, 8,
"eicon_mca_find_card type: %d, membase: %#x, irq %d \n",
type, membase, irq);
/* find a no-driver-assigned eicon card */
for (j=0; eicon_mca_adapters[j].adf_id != 0; j++)
{
for ( curr_slot=0; curr_slot<=MCA_MAX_SLOT_NR; curr_slot++)
{
curr_slot = mca_find_unused_adapter(
eicon_mca_adapters[j].adf_id, curr_slot);
if (curr_slot != MCA_NOTFOUND)
{
/* check if pre-set parameters match
these of the card, check cards memory */
if (!(int) eicon_mca_probe(curr_slot,
j,
membase,
irq,
id))
{
return 0;
/* means: adapter parms did match */
};
};
break;
/* MCA_NOTFOUND-branch: no matching adapter of
THIS flavor found, next flavor */
};
};
/* all adapter flavors checked without match, finito with: */
return -ENODEV;
};
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* stolen from 3c523.c/elmc_getinfo, ewe, 10.5.1999
*/
int eicon_info(char * buf, int slot, void *d)
{
int len = 0;
struct eicon_card *dev;
dev = (struct eicon_card *) d;
if (dev == NULL)
return len;
len += sprintf(buf+len, "eicon ISDN adapter, type %d.\n",dev->type);
len += sprintf(buf+len, "IRQ: %d\n", dev->hwif.isa.irq);
len += sprintf(buf+len, "MEMBASE: %#lx\n", (unsigned long)dev->hwif.isa.shmem);
return len;
};
int eicon_mca_probe(int slot, /* slot-nr where the card was detected */
int a_idx, /* idx-nr of probed card in eicon_mca_adapters */
int membase,
int irq,
char * id) /* name of eicon-isdn-dev */
{
unsigned char adf_pos0;
int cards_irq, cards_membase, cards_io;
int type = EICON_CTYPE_S;
int irq_array[]={0,3,4,2};
int irq_array1[]={3,4,0,0,2,10,11,12};
adf_pos0 = mca_read_stored_pos(slot,2);
eicon_log(NULL, 8,
"eicon_mca_probe irq=%d, membase=%d\n",
irq,
membase);
switch (a_idx) {
case 0: /* P/2-Adapter (== PRI/S2M ? ) */
cards_membase= 0xC0000+((adf_pos0>>4)*0x4000);
if (membase == -1) {
membase = cards_membase;
} else {
if (membase != cards_membase)
return -ENODEV;
};
cards_irq=irq_array[((adf_pos0 & 0xC)>>2)];
if (irq == -1) {
irq = cards_irq;
} else {
if (irq != cards_irq)
return -ENODEV;
};
cards_io= 0xC00 + ((adf_pos0>>4)*0x10);
type = EICON_CTYPE_ISAPRI;
break;
case 1: /* [S|SX|SCOM]/2 */
cards_membase= 0xC0000+((adf_pos0>>4)*0x2000);
if (membase == -1) {
membase = cards_membase;
} else {
if (membase != cards_membase)
return -ENODEV;
};
cards_irq=irq_array[((adf_pos0 & 0xC)>>2)];
if (irq == -1) {
irq = cards_irq;
} else {
if (irq != cards_irq)
return -ENODEV;
};
cards_io= 0xC00 + ((adf_pos0>>4)*0x10);
type = EICON_CTYPE_SCOM;
break;
case 2: /* DIVA/MCA */
cards_io = 0x200+ ((adf_pos0>>4)* 0x20);
cards_irq = irq_array1[(adf_pos0 & 0x7)];
if (irq == -1) {
irq = cards_irq;
} else {
if (irq != cards_irq)
return -ENODEV;
};
type = 0;
break;
default:
return -ENODEV;
};
/* matching membase & irq */
if ( 1 == eicon_addcard(type, membase, irq, id, 0)) {
mca_set_adapter_name(slot, eicon_mca_adapters[a_idx].name);
mca_set_adapter_procfn(slot, (MCA_ProcFn) eicon_info, cards);
mca_mark_as_used(slot);
cards->mca_slot = slot;
/* card->io noch setzen oder ?? */
cards->mca_io = cards_io;
cards->hwif.isa.io = cards_io;
/* reset card */
outb_p(0,cards_io+1);
eicon_log(NULL, 8, "eicon_addcard: successful for slot # %d.\n",
cards->mca_slot+1);
return 0 ; /* eicon_addcard added a card */
} else {
return -ENODEV;
};
};
#endif /* CONFIG_MCA */
#endif /* CONFIG_ISDN_DRV_EICON_ISA */
module_init(eicon_init);
module_exit(eicon_exit);
/* $Id: eicon_pci.c,v 1.1.4.1.2.3 2002/10/01 11:29:13 armin Exp $
*
* ISDN low-level module for Eicon active ISDN-Cards.
* Hardware-specific code for PCI cards.
*
* Copyright 1998-2000 by Armin Schindler (mac@melware.de)
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
* Thanks to Eicon Networks for
* documents, informations and hardware.
*
*/
#include <linux/config.h>
#include <linux/pci.h>
#include "eicon.h"
#include "eicon_pci.h"
#undef N_DATA
#include "adapter.h"
#include "uxio.h"
char *eicon_pci_revision = "$Revision: 1.1.4.1.2.3 $";
#ifdef CONFIG_PCI /* entire stuff is only for PCI */
#ifdef CONFIG_ISDN_DRV_EICON_PCI
int eicon_pci_find_card(char *ID)
{
int pci_cards = 0;
int card_id = 0;
int had_q = 0;
int ctype = 0;
char did[20];
card_t *pCard;
word wCardIndex;
pCard = DivasCards;
for (wCardIndex = 0; wCardIndex < MAX_CARDS; wCardIndex++)
{
if ((pCard->hw) && (pCard->hw->in_use))
{
switch(pCard->hw->card_type) {
case DIA_CARD_TYPE_DIVA_SERVER:
ctype = EICON_CTYPE_MAESTRAP;
card_id++;
had_q = 0;
break;
case DIA_CARD_TYPE_DIVA_SERVER_B:
ctype = EICON_CTYPE_MAESTRA;
card_id++;
had_q = 0;
break;
case DIA_CARD_TYPE_DIVA_SERVER_Q:
ctype = EICON_CTYPE_MAESTRAQ;
if (!had_q)
card_id++;
if (++had_q >=4)
had_q = 0;
break;
default:
printk(KERN_ERR "eicon_pci: unknown card type %d !\n",
pCard->hw->card_type);
goto err;
}
sprintf(did, "%s%d", (strlen(ID) < 1) ? "eicon":ID, pci_cards);
if ((!ctype) || (!(eicon_addcard(ctype, 0, pCard->hw->irq, did, card_id)))) {
printk(KERN_ERR "eicon_pci: Card could not be added !\n");
} else {
pci_cards++;
printk(KERN_INFO "%s: DriverID='%s' CardID=%d\n",
eicon_ctype_name[ctype], did, card_id);
}
err:;
}
pCard++;
}
return pci_cards;
}
void
eicon_pci_init_conf(eicon_card *card)
{
int j;
/* initializing some variables */
card->ReadyInt = 0;
for(j = 0; j < 256; j++)
card->IdTable[j] = NULL;
for(j = 0; j < (card->d->channels + 1); j++) {
card->bch[j].e.busy = 0;
card->bch[j].e.D3Id = 0;
card->bch[j].e.B2Id = 0;
card->bch[j].e.ref = 0;
card->bch[j].e.Req = 0;
card->bch[j].e.complete = 1;
card->bch[j].fsm_state = EICON_STATE_NULL;
}
}
#endif
#endif /* CONFIG_PCI */
/* $Id: eicon_pci.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $
*
* ISDN low-level module for Eicon active ISDN-Cards (PCI part).
*
* Copyright 1998-2000 by Armin Schindler (mac@melware.de)
* Copyright 1999,2000 Cytronics & Melware (info@melware.de)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef eicon_pci_h
#define eicon_pci_h
#ifdef __KERNEL__
/*
* card's description
*/
typedef struct {
int irq; /* IRQ */
int channels; /* No. of supported channels */
void* card;
unsigned char type; /* card type */
unsigned char master; /* Flag: Card is Quadro 1/4 */
} eicon_pci_card;
extern int eicon_pci_find_card(char *ID);
#endif /* __KERNEL__ */
#endif /* eicon_pci_h */
/*
* Diva Server 4BRI specific part of initialisation
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.7
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "sys.h"
#include "idi.h"
#include "divas.h"
#include "pc.h"
#include "pr_pc.h"
#include "dsp_defs.h"
#include "constant.h"
#include "adapter.h"
#include "uxio.h"
#define TEST_INT_DIVAS_Q 0x13
#define DIVAS_MAINT_OFFSET 0xff00 /* value for 4BRI card */
#define MQ_BOARD_DSP_OFFSET 0x00a00000
#define MQ_DSP1_ADDR_OFFSET 0x00000008
#define MQ_DSP_JUNK_OFFSET 0x00000400
#define MQ_DSP1_DATA_OFFSET 0x00000000
#define MQ_BOARD_ISAC_DSP_RESET 0x00800028
#define MQ_BREG_RISC 0x1200 /* RISC Reset */
#define MQ_ISAC_DSP_RESET 0x0028 /* ISAC and DSP reset address offset */
#define MQ_RISC_COLD_RESET_MASK 0x0001 /* RISC Cold reset */
#define MQ_RISC_WARM_RESET_MASK 0x0002 /* RISC Warm reset */
#define MQ_IRQ_REQ_ON 0x1
#define MQ_IRQ_REQ_OFF 0x0
#define MQ_BREG_IRQ_TEST 0x0608
#define PLX9054_INTCSR 0x69
#define PLX9054_INT_ENA 0x09
#define DIVAS_IOBASE 0x01
#define M_PCI_RESET 0x10
byte mem_in(ADAPTER *a, void *adr);
word mem_inw(ADAPTER *a, void *adr);
void mem_in_buffer(ADAPTER *a, void *adr, void *P, word length);
void mem_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e);
void mem_out(ADAPTER *a, void *adr, byte data);
void mem_outw(ADAPTER *a, void *adr, word data);
void mem_out_buffer(ADAPTER *a, void *adr, void *P, word length);
void mem_inc(ADAPTER *a, void *adr);
int Divas4BRIInitPCI(card_t *card, dia_card_t *cfg);
static int fourbri_ISR (card_t* card);
int FPGA_Download(word, dword, byte *, byte *, int);
extern byte FPGA_Bytes[];
extern void *get_card(int);
byte UxCardPortIoIn(ux_diva_card_t *card, byte *base, int offset);
void UxCardPortIoOut(ux_diva_card_t *card, byte *base, int offset, byte);
word GetProtFeatureValue(char *sw_id);
void memcp(byte *dst, byte *src, dword dwLen);
int memcm(byte *dst, byte *src, dword dwLen);
static int diva_server_4bri_reset(card_t *card)
{
byte *ctl;
DPRINTF(("divas: reset Diva Server 4BRI"));
ctl = UxCardMemAttach(card->hw, DIVAS_CTL_MEMORY);
/* stop RISC, DSP's and ISAC */
UxCardMemOut(card->hw, &ctl[MQ_BREG_RISC], 0);
UxCardMemOut(card->hw, &ctl[MQ_ISAC_DSP_RESET], 0);
UxCardMemDetach(card->hw, ctl);
return 0;
}
static int diva_server_4bri_config(card_t *card, dia_config_t *config)
{
byte *shared;
int i, j;
DPRINTF(("divas: configure Diva Server 4BRI"));
shared = (byte *) UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
for (i=0; i<256; i++)
{
UxCardMemOut(card->hw, &shared[i], 0);
}
UxCardMemOut(card->hw, &shared[ 8], config->tei);
UxCardMemOut(card->hw, &shared[ 9], config->nt2);
UxCardMemOut(card->hw, &shared[10], config->sig_flags);
UxCardMemOut(card->hw, &shared[11], config->watchdog);
UxCardMemOut(card->hw, &shared[12], config->permanent);
UxCardMemOut(card->hw, &shared[13], config->x_interface);
UxCardMemOut(card->hw, &shared[14], config->stable_l2);
UxCardMemOut(card->hw, &shared[15], config->no_order_check);
UxCardMemOut(card->hw, &shared[16], config->handset_type);
UxCardMemOut(card->hw, &shared[17], 0);
UxCardMemOut(card->hw, &shared[18], config->low_channel);
UxCardMemOut(card->hw, &shared[19], config->prot_version);
UxCardMemOut(card->hw, &shared[20], config->crc4);
if ((card->hw->features) && (card->hw->features & PROTCAP_V90D))
{
DPRINTF(("divas: Signifying V.90"));
UxCardMemOut(card->hw, &shared[22], 4);
}
else
{
UxCardMemOut(card->hw, &shared[22], 0);
}
for (i=0; i<2; i++)
{
for (j=0; j<32; j++)
{
UxCardMemOut(card->hw, &shared[32+(i*96)+j],config->terminal[i].oad[j]);
}
for (j=0; j<32; j++)
{
UxCardMemOut(card->hw, &shared[64+(i*96)+j],config->terminal[i].osa[j]);
}
for (j=0; j<32; j++)
{
UxCardMemOut(card->hw, &shared[96+(i*96)+j],config->terminal[i].spid[j]);
}
}
UxCardMemDetach(card->hw, shared);
return 0;
}
static
void diva_server_4bri_reset_int(card_t *card)
{
byte *ctl;
ctl = UxCardMemAttach(card->hw, DIVAS_CTL_MEMORY);
UxCardMemOut(card->hw, &ctl[MQ_BREG_IRQ_TEST], MQ_IRQ_REQ_OFF);
UxCardMemDetach(card->hw, ctl);
return;
}
static int diva_server_4bri_test_int(card_t *card)
{
byte *ctl, i;
byte *reg;
DPRINTF(("divas: test interrupt for Diva Server 4BRI"));
/* We get the last (dummy) adapter in so we need to go back to the first */
card = get_card(card->cfg.card_id - 3);
/* Enable interrupts on PLX chip */
reg = UxCardMemAttach(card->hw, DIVAS_REG_MEMORY);
UxCardPortIoOut(card->hw, reg, PLX9054_INTCSR, PLX9054_INT_ENA);
UxCardMemDetach(card->hw, reg);
/* Set the test interrupt flag */
card->test_int_pend = TEST_INT_DIVAS_Q;
/* Now to trigger the interrupt */
ctl = UxCardMemAttach(card->hw, DIVAS_CTL_MEMORY);
UxCardMemOut(card->hw, &ctl[MQ_BREG_IRQ_TEST], MQ_IRQ_REQ_ON);
UxCardMemDetach(card->hw, ctl);
for (i = 0; i < 50; i++)
{
if (!card->test_int_pend)
{
break;
}
UxPause(10);
}
if (card->test_int_pend)
{
DPRINTF(("active: timeout waiting for card to interrupt"));
return (-1);
}
return 0;
}
static void print_hdr(unsigned char *code, int offset)
{
unsigned char hdr[80];
int i;
i = 0;
while ((i < (DIM(hdr) -1)) &&
(code[offset + i] != '\0') &&
(code[offset + i] != '\r') &&
(code[offset + i] != '\n'))
{
hdr[i] = code[offset + i];
i++;
}
hdr[i] = '\0';
DPRINTF(("divas: loading %s", hdr));
}
static int diva_server_4bri_load(card_t *card, dia_load_t *load)
{
byte *pRAM=NULL;
int download_offset=0;
card_t *FirstCard;
byte sw_id[80];
DPRINTF(("divas: loading Diva Server 4BRI[%d]", load->card_id));
switch(load->code_type)
{
case DIA_CPU_CODE:
DPRINTF(("divas: RISC code"));
print_hdr(load->code, 0x80);
card->hw->features = GetProtFeatureValue((char *)&load->code[0x80]);
download_offset = 0; // Protocol code written to offset 0
pRAM = UxCardMemAttach(card->hw, DIVAS_RAM_MEMORY);
break;
case DIA_DSP_CODE:
DPRINTF(("divas: DSP code"));
print_hdr(load->code, 0x0);
FirstCard = get_card(load->card_id - 3);
if ((card->hw->features) && (card->hw->features & PROTCAP_V90D))
{
download_offset = MQ_V90D_DSP_CODE_BASE;
}
else
{
download_offset = MQ_ORG_DSP_CODE_BASE;
}
pRAM = UxCardMemAttach(FirstCard->hw, DIVAS_RAM_MEMORY);
download_offset += (((sizeof(dword) + (sizeof(t_dsp_download_desc)* DSP_MAX_DOWNLOAD_COUNT)) + 3) & 0xFFFFFFFC);
break;
case DIA_TABLE_CODE:
DPRINTF(("divas: TABLE code"));
FirstCard = get_card(load->card_id - 3);
if ((card->hw->features) && (card->hw->features & PROTCAP_V90D))
{
download_offset = MQ_V90D_DSP_CODE_BASE + sizeof(dword);
}
else
{
download_offset = MQ_ORG_DSP_CODE_BASE + sizeof(dword);
}
pRAM = UxCardMemAttach(FirstCard->hw, DIVAS_RAM_MEMORY);
break;
case DIA_CONT_CODE:
DPRINTF(("divas: continuation code"));
break;
case DIA_DLOAD_CNT:
DPRINTF(("divas: COUNT code"));
FirstCard = get_card(load->card_id - 3);
if ((card->hw->features) && (card->hw->features & PROTCAP_V90D))
{
download_offset = MQ_V90D_DSP_CODE_BASE;
}
else
{
download_offset = MQ_ORG_DSP_CODE_BASE;
}
pRAM = UxCardMemAttach(FirstCard->hw, DIVAS_RAM_MEMORY);
break;
case DIA_FPGA_CODE:
DPRINTF(("divas: 4BRI FPGA download - %d bytes", load->length));
if (FPGA_Download(IDI_ADAPTER_MAESTRAQ,
card->hw->io_base,
sw_id,
load->code,
load->length
) == -1)
{
DPRINTF(("divas: FPGA download failed"));
return -1;
}
/* NOW reset the 4BRI */
diva_server_4bri_reset(card);
return 0; // No need for anything further loading
default:
DPRINTF(("divas: unknown code type"));
return -1;
}
memcp(pRAM + (download_offset & 0x3FFFFF), load->code, load->length);
{
int mism_off;
if ((mism_off = memcm(pRAM + (download_offset & 0x3FFFFF), load->code, load->length)))
{
DPRINTF(("divas: memory mismatch at offset %d", mism_off));
UxCardMemDetach(card->hw, pRAM);
return -1;
}
}
UxCardMemDetach(card->hw, pRAM);
return 0;
}
static int diva_server_4bri_start(card_t *card, byte *channels)
{
byte *ctl;
byte *shared, i;
int adapter_num;
DPRINTF(("divas: start Diva Server 4BRI"));
*channels = 0;
card->is_live = FALSE;
ctl = UxCardMemAttach(card->hw, DIVAS_CTL_MEMORY);
UxCardMemOutW(card->hw, &ctl[MQ_BREG_RISC], MQ_RISC_COLD_RESET_MASK);
UxPause(2);
UxCardMemOutW(card->hw, &ctl[MQ_BREG_RISC], MQ_RISC_WARM_RESET_MASK | MQ_RISC_COLD_RESET_MASK);
UxPause(10);
UxCardMemDetach(card->hw, ctl);
shared = (byte *) UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
for ( i = 0 ; i < 300 ; ++i )
{
UxPause (10) ;
if ( UxCardMemInW(card->hw, &shared[0x1E]) == 0x4447 )
{
DPRINTF(("divas: Protocol startup time %d.%02d seconds",
(i / 100), (i % 100) ));
break;
}
}
if (i==300)
{
DPRINTF(("divas: Timeout starting card"));
DPRINTF(("divas: Signature == 0x%04X", UxCardMemInW(card->hw, &shared[0x1E])));
UxCardMemDetach(card->hw, shared);
return -1;
}
UxCardMemDetach(card->hw, shared);
for (adapter_num=3; adapter_num >= 0; adapter_num--)
{
card_t *qbri_card;
qbri_card = get_card(card->cfg.card_id - adapter_num);
if (qbri_card)
{
qbri_card->is_live = TRUE;
shared = UxCardMemAttach(qbri_card->hw, DIVAS_SHARED_MEMORY);
*channels += UxCardMemIn(qbri_card->hw, &shared[0x3F6]);
UxCardMemDetach(qbri_card->hw, shared);
}
else
{
DPRINTF(("divas: Couldn't get card info %d", card->cfg.card_id));
}
}
diva_server_4bri_test_int(card);
return 0;
}
static
int diva_server_4bri_mem_get(card_t *card, mem_block_t *mem_block)
{
byte *a;
byte *card_addr;
word length = 0;
int i;
a = UxCardMemAttach(card->hw, DIVAS_RAM_MEMORY);
card_addr = a;
card_addr += mem_block->addr;
for (i=0; i < sizeof(mem_block->data); i++)
{
mem_block->data[i] = UxCardMemIn(card->hw, card_addr);
card_addr++;
length++;
}
UxCardMemDetach(card->hw, a);
return length;
}
/*
* Initialise 4BRI specific entry points
*/
int Divas4BriInit(card_t *card, dia_card_t *cfg)
{
// byte sw_id[80];
// extern int FPGA_Done;
DPRINTF(("divas: initialise Diva Server 4BRI"));
if (Divas4BRIInitPCI(card, cfg) == -1)
{
return -1;
}
/* Need to download the FPGA */
/* if (!FPGA_Done)
{
int retVal;
retVal=FPGA_Download(IDI_ADAPTER_MAESTRAQ,
cfg->io_base,
sw_id,
FPGA_Bytes
);
if(retVal==-1)
{
DPRINTF(("divas: FPGA Download Failed"));
return -1;
}
FPGA_Done = 1;
} */
card->card_reset = diva_server_4bri_reset;
card->card_load = diva_server_4bri_load;
card->card_config = diva_server_4bri_config;
card->card_start = diva_server_4bri_start;
card->reset_int = diva_server_4bri_reset_int;
card->card_mem_get = diva_server_4bri_mem_get;
card->xlog_offset = DIVAS_MAINT_OFFSET;
card->out = DivasOut;
card->test_int = DivasTestInt;
card->dpc = DivasDpc;
card->clear_int = DivasClearInt;
card->card_isr = fourbri_ISR;
card->a.ram_out = mem_out;
card->a.ram_outw = mem_outw;
card->a.ram_out_buffer = mem_out_buffer;
card->a.ram_inc = mem_inc;
card->a.ram_in = mem_in;
card->a.ram_inw = mem_inw;
card->a.ram_in_buffer = mem_in_buffer;
card->a.ram_look_ahead = mem_look_ahead;
return 0;
}
void memcp(byte *dst, byte *src, dword dwLen)
{
while (dwLen)
{
*dst = *src;
dst++; src++;
dwLen--;
}
}
int memcm(byte *dst, byte *src, dword dwLen)
{
int offset = 0;
while (offset < dwLen)
{
if(*dst != *src)
return (offset+1);
offset++;
src++;
dst++;
}
return 0;
}
/*int fourbri_ISR (card_t* card)
{
int served = 0;
byte *DivasIOBase = UxCardMemAttach(card->hw, DIVAS_IOBASE);
if (UxCardPortIoIn (card->hw, DivasIOBase, M_PCI_RESET) & 0x01)
{
served = 1;
card->int_pend += 1;
DivasDpcSchedule();
UxCardPortIoOut (card->hw, DivasIOBase, M_PCI_RESET, 0x08);
}
UxCardMemDetach(card->hw, DivasIOBase);
return (served != 0);
}*/
static int fourbri_ISR (card_t* card)
{
byte *ctl;
card->int_pend += 1;
DivasDpcSchedule(); /* ISR DPC */
ctl = UxCardMemAttach(card->hw, DIVAS_CTL_MEMORY);
UxCardMemOut(card->hw, &ctl[MQ_BREG_IRQ_TEST], MQ_IRQ_REQ_OFF);
UxCardMemDetach(card->hw, ctl);
return (1);
}
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.2
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "sys.h"
#include "idi.h"
#include "uxio.h"
#define FPGA_PORT 0x6E
#define FPGA_DLOAD_BUFLEN 256
#define NAME_OFFSET 0x10
#define NAME_MAXLEN 12
#define DATE_OFFSET 0x2c
#define DATE_MAXLEN 10
word UxCardPortIoInW(ux_diva_card_t *card, byte *base, int offset);
void UxCardPortIoOutW(ux_diva_card_t *card, byte *base, int offset, word);
void UxPause(long int);
/*-------------------------------------------------------------------------*/
/* Loads the FPGA configuration file onto the hardware. */
/* Function returns 0 on success, else an error number. */
/* On success, an identifier string is returned in the buffer */
/* */
/* A buffer of FPGA_BUFSIZE, a handle to the already opened bitstream */
/* file and a file read function has to be provided by the operating */
/* system part. */
/* ----------------------------------------------------------------------- */
int FPGA_Download( word cardtype,
dword RegBase,
byte *strbuf,
byte FPGA_SRC[],
int FPGA_LEN
)
{
word i, j, k;
word baseval, Mask_PROGRAM, Mask_DONE, Mask_CCLK, Mask_DIN;
dword addr;
byte *pFPGA;
//--- check for legal cardtype
switch (cardtype)
{
case IDI_ADAPTER_MAESTRAQ:
addr = RegBase ; // address where to access FPGA
Mask_PROGRAM = 0x0001; // FPGA pins at address
Mask_DONE = 0x0002;
Mask_CCLK = 0x0100;
Mask_DIN = 0x0400;
baseval = 0x000d; // PROGRAM hi, CCLK lo, DIN lo by default
break;
default:
DPRINTF(("divas: FPGA Download ,Illegal Card"));
return -1; // illegal card
}
//--- generate id string from file content
for (j=NAME_OFFSET, k=0; j<(NAME_OFFSET+NAME_MAXLEN); j++, k++) //name
{
if (!FPGA_SRC[j]) break;
strbuf[k] = FPGA_SRC[j];
}
strbuf[k++] = ' ';
for (j=DATE_OFFSET; j<(DATE_OFFSET+DATE_MAXLEN); j++, k++) // date
{
if (!FPGA_SRC[j]) break;
strbuf[k] = FPGA_SRC[j];
}
strbuf[k] = 0;
DPRINTF(("divas: FPGA Download - %s", strbuf));
//--- prepare download, Pulse PROGRAM pin down.
UxCardPortIoOutW(NULL, (byte *) addr, FPGA_PORT, baseval &~Mask_PROGRAM); // PROGRAM low pulse
UxCardPortIoOutW(NULL, (byte *) addr, FPGA_PORT, baseval); // release
UxPause(50); // wait until FPGA finised internal memory clear
//--- check done pin, must be low
if (UxCardPortIoInW(NULL, (byte *) addr, FPGA_PORT) &Mask_DONE)
{
DPRINTF(("divas: FPGA_ERR_DONE_WRONG_LEVEL"));
return -1;
}
pFPGA = FPGA_SRC;
i = 0;
/* Move past the header */
while ((FPGA_SRC[i] != 0xFF) && (i < FPGA_LEN))
{
i++;
}
// We've hit the 0xFF so move on to the next byte
// i++;
DPRINTF(("divas: FPGA Code starts at offset %d", i));
//--- put data onto the FPGA
for (;i<FPGA_LEN; i++)
{
//--- put byte onto FPGA
for (j=0; j<8; j++)
{
if (FPGA_SRC[i] &(0x80>>j)) baseval |= Mask_DIN; // write a hi
else baseval &=~Mask_DIN; // write a lo
UxCardPortIoOutW(NULL, (byte *) addr, FPGA_PORT, baseval);
UxCardPortIoOutW(NULL, (byte *) addr, FPGA_PORT, baseval | Mask_CCLK); // set CCLK hi
UxCardPortIoOutW(NULL, (byte *) addr, FPGA_PORT, baseval); // set CCLK lo
}
}
//--- add some additional startup clock cycles and check done pin
for (i=0; i<5; i++)
{
UxCardPortIoOutW(NULL, (byte *) addr, FPGA_PORT, baseval | Mask_CCLK); // set CCLK hi
UxCardPortIoOutW(NULL, (byte *) addr, FPGA_PORT, baseval); // set CCLK lo
}
UxPause(100);
if (UxCardPortIoInW(NULL, (byte *) addr, FPGA_PORT) &Mask_DONE)
{
DPRINTF(("divas: FPGA download successful"));
}
else
{
DPRINTF(("divas: FPGA download failed - 0x%x", UxCardPortIoInW(NULL, (byte *) addr, FPGA_PORT)));
return -1;
}
return 0;
}
/*
* Core driver for Diva Server cards
* Implements the IDI interface
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.8
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "idi.h"
#include "adapter.h"
#include "pc.h"
#include "pr_pc.h"
#include "sys.h"
#include "uxio.h"
/* IDI request functions */
static void request(card_t *card, ENTITY *e);
static void req_0(ENTITY *e) { request(&DivasCards[ 0], e); }
static void req_1(ENTITY *e) { request(&DivasCards[ 1], e); }
static void req_2(ENTITY *e) { request(&DivasCards[ 2], e); }
static void req_3(ENTITY *e) { request(&DivasCards[ 3], e); }
static void req_4(ENTITY *e) { request(&DivasCards[ 4], e); }
static void req_5(ENTITY *e) { request(&DivasCards[ 5], e); }
static void req_6(ENTITY *e) { request(&DivasCards[ 6], e); }
static void req_7(ENTITY *e) { request(&DivasCards[ 7], e); }
static void req_8(ENTITY *e) { request(&DivasCards[ 8], e); }
static void req_9(ENTITY *e) { request(&DivasCards[ 9], e); }
static void req_10(ENTITY *e) { request(&DivasCards[10], e); }
static void req_11(ENTITY *e) { request(&DivasCards[11], e); }
static void req_12(ENTITY *e) { request(&DivasCards[12], e); }
static void req_13(ENTITY *e) { request(&DivasCards[13], e); }
static void req_14(ENTITY *e) { request(&DivasCards[14], e); }
static void req_15(ENTITY *e) { request(&DivasCards[15], e); }
IDI_CALL DivasIdiRequest[16] =
{
&req_0, &req_1, &req_2, &req_3,
&req_4, &req_5, &req_6, &req_7,
&req_8, &req_9, &req_10, &req_11,
&req_12, &req_13, &req_14, &req_15
};
#define PR_RAM ((struct pr_ram *)0)
#define RAM ((struct dual *)0)
/*------------------------------------------------------------------*/
/* local function prototypes */
/*------------------------------------------------------------------*/
static byte isdn_rc(ADAPTER *, byte, byte, byte, word);
static byte isdn_ind(ADAPTER *, byte, byte, byte, PBUFFER *, byte, word);
/*
* IDI related functions
*/
static
ENTITY *entity_ptr(ADAPTER *a, byte e_no)
{
card_t *card;
card = a->io;
return card->e_tbl[e_no].e;
}
static
void CALLBACK(ADAPTER *a, ENTITY *e)
{
card_t *card = a->io;
if (card->log_types & DIVAS_LOG_IDI)
{
DivasLogIdi(card, e, FALSE);
}
(*e->callback)(e);
}
static
void *PTR_P(ADAPTER *a, ENTITY *e, void *P)
{
return(P);
}
static
void *PTR_R(ADAPTER *a, ENTITY *e)
{
return((void*)e->R);
}
static
void *PTR_X(ADAPTER *a, ENTITY *e)
{
return((void*)e->X);
}
static
void free_entity(ADAPTER *a, byte e_no)
{
card_t *card;
int ipl;
card = a->io;
ipl = UxCardLock(card->hw);
card->e_tbl[e_no].e = NULL;
card->e_count--;
UxCardUnlock(card->hw, ipl);
return;
}
static
void assign_queue(ADAPTER * a, byte e_no, word ref)
{
card_t *card;
int ipl;
card = a->io;
ipl = UxCardLock(card->hw);
card->e_tbl[e_no].assign_ref = ref;
card->e_tbl[e_no].next = card->assign;
card->assign = e_no;
UxCardUnlock(card->hw, ipl);
return;
}
static
byte get_assign(ADAPTER *a, word ref)
{
card_t *card;
byte e_no;
int ipl;
card = a->io;
ipl = UxCardLock(card->hw);
e_no = (byte)card->assign;
while (e_no)
{
if (card->e_tbl[e_no].assign_ref == ref)
{
break;
}
e_no = card->e_tbl[e_no].next;
}
UxCardUnlock(card->hw, ipl);
return e_no;
}
static
void req_queue(ADAPTER * a, byte e_no)
{
card_t *card;
int ipl;
card = a->io;
ipl = UxCardLock(card->hw);
card->e_tbl[e_no].next = 0;
if (card->e_head)
{
card->e_tbl[card->e_tail].next = e_no;
card->e_tail = e_no;
}
else
{
card->e_head = e_no;
card->e_tail = e_no;
}
UxCardUnlock(card->hw, ipl);
return;
}
static
byte look_req(ADAPTER * a)
{
card_t *card;
card = a->io;
return(card->e_head);
}
static
void next_req(ADAPTER * a)
{
card_t *card;
int ipl;
card = a->io;
ipl = UxCardLock(card->hw);
card->e_head = card->e_tbl[card->e_head].next;
if (!card->e_head)
{
card->e_tail = 0;
}
UxCardUnlock(card->hw, ipl);
return;
}
/*
* IDI request function for active cards
*/
static
void request(card_t *card, ENTITY *e)
{
word *special_req;
int i;
int ipl;
if (card->log_types & DIVAS_LOG_IDI)
{
DivasLogIdi(card, e, TRUE);
}
if (!e->Req)
{
special_req = (word *) e;
switch (*special_req)
{
case REQ_REMOVE:
return;
case REQ_NAME:
for (i=0; i < DIM(card->cfg.name); i++)
{
((struct get_name_s *) e)->name[i] = card->cfg.name[i];
}
return;
case REQ_SERIAL:
case REQ_XLOG:
DPRINTF(("IDI: attempted REQ_SERIAL or REQ_XLOG"));
return;
default:
return;
}
}
ipl = UxCardLock(card->hw);
if (!(e->Id & 0x1f))
{
DPRINTF(("IDI: ASSIGN req"));
for (i = 1; i < card->e_max; i++)
{
if (!card->e_tbl[i].e)
{
break;
}
}
if (i == card->e_max)
{
DPRINTF(("IDI: request all ids in use (IDI req ignored)"));
UxCardUnlock(card->hw, ipl);
e->Rc = OUT_OF_RESOURCES;
return;
}
card->e_tbl[i].e = e;
card->e_count++;
e->No = (byte) i;
e->More = 0;
e->RCurrent = 0xff;
}
else
{
i = e->No;
}
if (e->More & XBUSY)
{
DPRINTF(("IDI: request - entity is busy"));
UxCardUnlock(card->hw, ipl);
return;
}
e->More |= XBUSY;
e->More &= ~ XMOREF;
e->XCurrent = 0;
e->XOffset = 0;
card->e_tbl[i].next = 0;
if(card->e_head)
{
card->e_tbl[card->e_tail].next = i;
card->e_tail = i;
}
else
{
card->e_head = i;
card->e_tail = i;
}
UxCardUnlock(card->hw, ipl);
DivasScheduleRequestDpc();
return;
}
static byte pr_ready(ADAPTER * a)
{
byte ReadyCount;
ReadyCount = (byte)(a->ram_in(a, &PR_RAM->ReqOutput) -
a->ram_in(a, &PR_RAM->ReqInput));
if(!ReadyCount) {
if(!a->ReadyInt) {
a->ram_inc(a, &PR_RAM->ReadyInt);
a->ReadyInt++;
}
}
return ReadyCount;
}
/*------------------------------------------------------------------*/
/* output function */
/*------------------------------------------------------------------*/
void DivasOut(ADAPTER * a)
{
byte e_no;
ENTITY * this = NULL;
BUFFERS *X;
word length;
word i;
word clength;
REQ * ReqOut;
byte more;
byte ReadyCount;
byte ReqCount;
byte Id;
/* while a request is pending ... */
e_no = look_req(a);
if(!e_no)
{
return;
}
ReadyCount = pr_ready(a);
if(!ReadyCount)
{
DPRINTF(("IDI: card not ready for next request"));
return;
}
ReqCount = 0;
while(e_no && ReadyCount) {
next_req(a);
this = entity_ptr(a, e_no);
#ifdef USE_EXTENDED_DEBUGS
if ( !this )
{
ISDN_ADAPTER *io = (ISDN_ADAPTER *)a->io ;
DBG_FTL(("!A%d ==> NULL entity ptr - try to ignore", (int)io->ANum))
e_no = look_req(a) ;
ReadyCount-- ;
continue ;
}
{
ISDN_ADAPTER *io = (ISDN_ADAPTER *)a->io ;
DPRINTF(("IDI: >A%d Id=0x%x Req=0x%x", io->ANum, this->Id, this->Req))
}
#else
DPRINTF(("IDI: >REQ=%x,Id=%x,Ch=%x",this->Req,this->Id,this->ReqCh));
#endif
/* get address of next available request buffer */
ReqOut = (REQ *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextReq)];
/* now copy the data from the current data buffer into the */
/* adapters request buffer */
length = 0;
i = this->XCurrent;
X = PTR_X(a,this);
while(i<this->XNum && length<270) {
clength = (word)(270-length);
if (clength > X[i].PLength-this->XOffset)
clength = X[i].PLength-this->XOffset;
a->ram_out_buffer(a,
&ReqOut->XBuffer.P[length],
PTR_P(a,this,&X[i].P[this->XOffset]),
clength);
length +=clength;
this->XOffset +=clength;
if(this->XOffset==X[i].PLength) {
this->XCurrent = (byte)++i;
this->XOffset = 0;
}
}
a->ram_outw(a, &ReqOut->XBuffer.length, length);
a->ram_out(a, &ReqOut->ReqId, this->Id);
a->ram_out(a, &ReqOut->ReqCh, this->ReqCh);
/* if it's a specific request (no ASSIGN) ... */
if(this->Id &0x1f) {
/* if buffers are left in the list of data buffers do */
/* do chaining (LL_MDATA, N_MDATA) */
this->More++;
if(i<this->XNum && this->MInd) {
a->ram_out(a, &ReqOut->Req, this->MInd);
more = TRUE;
}
else {
this->More |=XMOREF;
a->ram_out(a, &ReqOut->Req, this->Req);
more = FALSE;
}
/* if we did chaining, this entity is put back into the */
/* request queue */
if(more) {
req_queue(a,this->No);
}
}
/* else it's a ASSIGN */
else {
/* save the request code used for buffer chaining */
this->MInd = 0;
if (this->Id==BLLC_ID) this->MInd = LL_MDATA;
if (this->Id==NL_ID ||
this->Id==TASK_ID ||
this->Id==MAN_ID
) this->MInd = N_MDATA;
/* send the ASSIGN */
this->More |=XMOREF;
a->ram_out(a, &ReqOut->Req, this->Req);
/* save the reference of the ASSIGN */
assign_queue(a, this->No, a->ram_inw(a, &ReqOut->Reference));
}
a->ram_outw(a, &PR_RAM->NextReq, a->ram_inw(a, &ReqOut->next));
ReadyCount--;
ReqCount++;
e_no = look_req(a);
}
/* send the filled request buffers to the ISDN adapter */
a->ram_out(a, &PR_RAM->ReqInput,
(byte)(a->ram_in(a, &PR_RAM->ReqInput) + ReqCount));
/* if it is a 'unreturncoded' UREMOVE request, remove the */
/* Id from our table after sending the request */
if(this->Req==UREMOVE && this->Id) {
Id = this->Id;
e_no = a->IdTable[Id];
free_entity(a, e_no);
a->IdTable[Id] = 0;
this->Id = 0;
}
}
/*------------------------------------------------------------------*/
/* isdn interrupt handler */
/*------------------------------------------------------------------*/
byte DivasDpc(ADAPTER * a)
{
byte Count;
RC * RcIn;
IND * IndIn;
byte c;
byte RNRId;
byte Rc;
byte Ind;
/* if return codes are available ... */
if((Count = a->ram_in(a, &PR_RAM->RcOutput))) {
DPRINTF(("IDI: #Rc=%x",Count));
/* get the buffer address of the first return code */
RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextRc)];
/* for all return codes do ... */
while(Count--) {
if((Rc=a->ram_in(a, &RcIn->Rc))) {
/* call return code handler, if it is not our return code */
/* the handler returns 2 */
/* for all return codes we process, we clear the Rc field */
isdn_rc(a,
Rc,
a->ram_in(a, &RcIn->RcId),
a->ram_in(a, &RcIn->RcCh),
a->ram_inw(a, &RcIn->Reference));
a->ram_out(a, &RcIn->Rc, 0);
}
/* get buffer address of next return code */
RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &RcIn->next)];
}
/* clear all return codes (no chaining!) */
a->ram_out(a, &PR_RAM->RcOutput ,0);
/* call output function */
DivasOut(a);
}
/* clear RNR flag */
RNRId = 0;
/* if indications are available ... */
if((Count = a->ram_in(a, &PR_RAM->IndOutput))) {
DPRINTF(("IDI: #Ind=%x",Count));
/* get the buffer address of the first indication */
IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextInd)];
/* for all indications do ... */
while(Count--) {
/* if the application marks an indication as RNR, all */
/* indications from the same Id delivered in this interrupt */
/* are marked RNR */
if(RNRId && RNRId==a->ram_in(a, &IndIn->IndId)) {
a->ram_out(a, &IndIn->Ind, 0);
a->ram_out(a, &IndIn->RNR, TRUE);
}
else {
Ind = a->ram_in(a, &IndIn->Ind);
if(Ind) {
RNRId = 0;
/* call indication handler, a return value of 2 means chain */
/* a return value of 1 means RNR */
/* for all indications we process, we clear the Ind field */
c = isdn_ind(a,
Ind,
a->ram_in(a, &IndIn->IndId),
a->ram_in(a, &IndIn->IndCh),
&IndIn->RBuffer,
a->ram_in(a, &IndIn->MInd),
a->ram_inw(a, &IndIn->MLength));
if(c==1) {
DPRINTF(("IDI: RNR"));
a->ram_out(a, &IndIn->Ind, 0);
RNRId = a->ram_in(a, &IndIn->IndId);
a->ram_out(a, &IndIn->RNR, TRUE);
}
}
}
/* get buffer address of next indication */
IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &IndIn->next)];
}
a->ram_out(a, &PR_RAM->IndOutput, 0);
}
return FALSE;
}
byte DivasTestInt(ADAPTER * a)
{
return a->ram_in(a,(void *)0x3fe);
}
void DivasClearInt(ADAPTER * a)
{
a->ram_out(a,(void *)0x3fe,0);
}
/*------------------------------------------------------------------*/
/* return code handler */
/*------------------------------------------------------------------*/
static
byte isdn_rc(ADAPTER * a,
byte Rc,
byte Id,
byte Ch,
word Ref)
{
ENTITY * this;
byte e_no;
#ifdef USE_EXTENDED_DEBUGS
{
ISDN_ADAPTER *io = (ISDN_ADAPTER *)a->io ;
DPRINTF(("IDI: <A%d Id=0x%x Rc=0x%x", io->ANum, Id, Rc))
}
#else
DPRINTF(("IDI: <RC(Rc=%x,Id=%x,Ch=%x)",Rc,Id,Ch));
#endif
/* check for ready interrupt */
if(Rc==READY_INT) {
if(a->ReadyInt) {
a->ReadyInt--;
return 0;
}
return 2;
}
/* if we know this Id ... */
e_no = a->IdTable[Id];
if(e_no) {
this = entity_ptr(a,e_no);
this->RcCh = Ch;
/* if it is a return code to a REMOVE request, remove the */
/* Id from our table */
if(this->Req==REMOVE && Rc==OK) {
free_entity(a, e_no);
a->IdTable[Id] = 0;
this->Id = 0;
/**************************************************************/
if ((this->More & XMOREC) > 1) {
this->More &= ~XMOREC;
this->More |= 1;
DPRINTF(("isdn_rc, Id=%x, correct More on REMOVE", Id));
}
}
if (Rc==OK_FC) {
this->Rc = Rc;
this->More = (this->More & (~XBUSY | XMOREC)) | 1;
this->complete = 0xFF;
CALLBACK(a, this);
return 0;
}
if(this->More &XMOREC)
this->More--;
/* call the application callback function */
if(this->More &XMOREF && !(this->More &XMOREC)) {
this->Rc = Rc;
this->More &=~XBUSY;
this->complete=0xff;
CALLBACK(a, this);
}
return 0;
}
/* if it's an ASSIGN return code check if it's a return */
/* code to an ASSIGN request from us */
if((Rc &0xf0)==ASSIGN_RC) {
e_no = get_assign(a, Ref);
if(e_no) {
this = entity_ptr(a,e_no);
this->Id = Id;
/* call the application callback function */
this->Rc = Rc;
this->More &=~XBUSY;
this->complete=0xff;
CALLBACK(a, this);
if(Rc==ASSIGN_OK) {
a->IdTable[Id] = e_no;
}
else
{
free_entity(a, e_no);
a->IdTable[Id] = 0;
this->Id = 0;
}
return 1;
}
}
return 2;
}
/*------------------------------------------------------------------*/
/* indication handler */
/*------------------------------------------------------------------*/
static
byte isdn_ind(ADAPTER * a,
byte Ind,
byte Id,
byte Ch,
PBUFFER * RBuffer,
byte MInd,
word MLength)
{
ENTITY * this;
word clength;
word offset;
BUFFERS *R;
#ifdef USE_EXTENDED_DEBUGS
{
ISDN_ADAPTER *io = (ISDN_ADAPTER *)a->io ;
DPRINTF(("IDI: <A%d Id=0x%x Ind=0x%x", io->ANum, Id, Ind))
}
#else
DPRINTF(("IDI: <IND(Ind=%x,Id=%x,Ch=%x)",Ind,Id,Ch));
#endif
if(a->IdTable[Id]) {
this = entity_ptr(a,a->IdTable[Id]);
this->IndCh = Ch;
/* if the Receive More flag is not yet set, this is the */
/* first buffer of the packet */
if(this->RCurrent==0xff) {
/* check for receive buffer chaining */
if(Ind==this->MInd) {
this->complete = 0;
this->Ind = MInd;
}
else {
this->complete = 1;
this->Ind = Ind;
}
/* call the application callback function for the receive */
/* look ahead */
this->RLength = MLength;
a->ram_look_ahead(a, RBuffer, this);
this->RNum = 0;
CALLBACK(a, this);
/* map entity ptr, selector could be re-mapped by call to */
/* IDI from within callback */
this = entity_ptr(a,a->IdTable[Id]);
/* check for RNR */
if(this->RNR==1) {
this->RNR = 0;
return 1;
}
/* if no buffers are provided by the application, the */
/* application want to copy the data itself including */
/* N_MDATA/LL_MDATA chaining */
if(!this->RNR && !this->RNum) {
return 0;
}
/* if there is no RNR, set the More flag */
this->RCurrent = 0;
this->ROffset = 0;
}
if(this->RNR==2) {
if(Ind!=this->MInd) {
this->RCurrent = 0xff;
this->RNR = 0;
}
return 0;
}
/* if we have received buffers from the application, copy */
/* the data into these buffers */
offset = 0;
R = PTR_R(a,this);
do {
if(this->ROffset==R[this->RCurrent].PLength) {
this->ROffset = 0;
this->RCurrent++;
}
clength = a->ram_inw(a, &RBuffer->length)-offset;
if (clength > R[this->RCurrent].PLength-this->ROffset)
clength = R[this->RCurrent].PLength-this->ROffset;
if(R[this->RCurrent].P) {
a->ram_in_buffer(a,
&RBuffer->P[offset],
PTR_P(a,this,&R[this->RCurrent].P[this->ROffset]),
clength);
}
offset +=clength;
this->ROffset +=clength;
} while(offset<(a->ram_inw(a, &RBuffer->length)));
/* if it's the last buffer of the packet, call the */
/* application callback function for the receive complete */
/* call */
if(Ind!=this->MInd) {
R[this->RCurrent].PLength = this->ROffset;
if(this->ROffset) this->RCurrent++;
this->RNum = this->RCurrent;
this->RCurrent = 0xff;
this->Ind = Ind;
this->complete = 2;
CALLBACK(a, this);
}
return 0;
}
return 2;
}
/*
* External IDI interface
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.0
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#if !defined(IDI_H)
#define IDI_H
#include "sys.h"
/* typedefs for our data structures */
typedef struct get_name_s GET_NAME;
typedef struct entity_s ENTITY;
typedef struct buffers_s BUFFERS;
/* IDI request/callback function pointer */
typedef void (* IDI_CALL)(ENTITY *);
typedef struct {
word length; /* length of data/parameter field */
byte P[270]; /* data/parameter field */
} DBUFFER;
#define REQ_NAME 0x0100
#define BOARD_NAME_LENGTH 9
struct get_name_s {
word command; /* command = 0x0100 */
byte name[BOARD_NAME_LENGTH];
};
#define REQ_REMOVE 0x0000 /* pointer to word which is 0 */
#define REQ_SERIAL 0x0200
struct get_serial_s {
word command; /* command = 0x0200 */
dword serial; /* serial number */
};
#define REQ_POSTCALL 0x0300
struct postcall_s {
word command; /* command = 0x0300 */
word dummy; /* not used */
IDI_CALL callback; /* routine address to call back */
ENTITY *contxt; /* ptr to entity to use */
};
#define REQ_XLOG 0x0400 /* structure is card dependent/defined locally */
struct buffers_s {
word PLength;
byte *P;
};
struct entity_s {
byte Req; /* pending request */
byte Rc; /* return code received */
byte Ind; /* indication received */
byte ReqCh; /* channel of current Req */
byte RcCh; /* channel of current Rc */
byte IndCh; /* channel of current Ind */
byte Id; /* ID used by this entity */
byte GlobalId; /* reserved field */
byte XNum; /* number of X-buffers */
byte RNum; /* number of R-buffers */
BUFFERS *X; /* pointer to X-buffer list */
BUFFERS *R; /* pointer to R-buffer list */
word RLength; /* length of current R-data */
DBUFFER *RBuffer; /* buffer of current R-data */
byte RNR; /* receive not ready flag */
byte complete; /* receive complete status */
IDI_CALL callback;
word user[2];
/* fields used by the driver internally */
byte No; /* entity number */
byte reserved2; /* reserved field */
byte More; /* R/X More flags */
byte MInd; /* MDATA coding for this ID */
byte XCurrent; /* current transmit buffer */
byte RCurrent; /* current receive buffer */
word XOffset; /* offset in x-buffer */
word ROffset; /* offset in r-buffer */
};
typedef struct {
byte type;
byte channels;
word features;
/* dword serial; */
IDI_CALL request;
} DESCRIPTOR;
extern void DIVA_DIDD_Read(DESCRIPTOR *, int);
/* descriptor type field coding */
#define IDI_ADAPTER_S 1
#define IDI_ADAPTER_PR 2
#define IDI_ADAPTER_DIVA 3
#define IDI_ADAPTER_MAESTRA 4
#define IDI_ADAPTER_MAESTRAQ 5
#define IDI_ADAPTER_MAESTRAP 6
#define IDI_VADAPTER 0x40
#define IDI_DRIVER 0x80
#define IDI_DIMAINT 0xff
/* feature bit mask values */
#define DI_VOICE 0x0 /* obsolete define */
#define DI_FAX3 0x1
#define DI_MODEM 0x2
#define DI_POST 0x4
#define DI_V110 0x8
#define DI_V120 0x10
#define DI_POTS 0x20
#define DI_CODEC 0x40
#define DI_MANAGE 0x80
#define DI_V_42 0x0100
#define DI_EXTD_FAX 0x0200 /* Extended FAX (ECM, 2D, T.6, Polling) */
#endif /* IDI_H */
/*
* Source file for kernel interface to kernel log facility
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.3
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "eicon.h"
#include "sys.h"
#include <stdarg.h>
#include "divas.h"
#include "divalog.h"
#include "uxio.h"
void DivasPrintf(char *fmt, ...)
{
klog_t log; /* log entry buffer */
va_list argptr; /* pointer to additional args */
va_start(argptr, fmt);
/* clear log entry */
memset((void *) &log, 0, sizeof(klog_t));
log.card = -1;
log.type = KLOG_TEXT_MSG;
/* time stamp the entry */
log.time_stamp = UxTimeGet();
/* call vsprintf to format the user's information */
vsnprintf(log.buffer, DIM(log.buffer), fmt, argptr);
va_end(argptr);
/* send to the log streams driver and return */
DivasLogAdd(&log, sizeof(klog_t));
return;
}
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.9
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/fs.h>
#undef N_DATA /* Because we have our own definition */
#include <asm/io.h>
#include "sys.h"
#include "idi.h"
#include "constant.h"
#include "divas.h"
#undef ID_MASK
#include "pc.h"
#include "pr_pc.h"
#include "adapter.h"
#include "uxio.h"
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
struct file_operations Divas_fops;
int Divas_major;
extern int do_ioctl(struct inode *pDivasInode, struct file *pDivasFile,
unsigned int command, unsigned long arg);
extern unsigned int do_poll(struct file *pFile, struct poll_table_struct *pPollTable);
extern ssize_t do_read(struct file *pFile, char *pUserBuffer, size_t BufferSize, loff_t *pOffset);
extern int do_open(struct inode *, struct file *);
extern int do_release(struct inode *, struct file *);
int FPGA_Done=0;
int DivasCardsDiscover(void)
{
struct pci_dev *pdev = NULL;
word wNumCards = 0, wDeviceIndex = 0;
word PCItmp;
dword j, i;
unsigned int PCIserial;
dia_card_t Card;
byte *b;
while ((pdev = pci_find_device(PCI_VENDOR_ID_EICON,
PCI_DEVICE_ID_EICON_MAESTRAQ,
pdev))) {
dword dwRAM, dwDivasIOBase, dwCFG, dwCTL;
printk(KERN_DEBUG "Divas: DIVA Server 4BRI Found\n");
dwRAM = pci_resource_start(pdev, 2);
dwDivasIOBase = pci_resource_start(pdev, 1);
dwCFG = pci_resource_start(pdev, 0);
dwCTL = pci_resource_start(pdev, 3);
/* Retrieve the serial number */
pci_write_config_word(pdev, 0x4E, 0x00FC);
for (j=0, PCItmp=0; j<10000 && !PCItmp; j++) {
pci_read_config_word(pdev, 0x4E, &PCItmp);
PCItmp &= 0x8000; // extract done flag
}
pci_read_config_dword(pdev, 0x50, &PCIserial);
Card.memory[DIVAS_RAM_MEMORY] = ioremap(dwRAM, 0x400000);
Card.memory[DIVAS_CTL_MEMORY] = ioremap(dwCTL, 0x2000);
Card.memory[DIVAS_CFG_MEMORY] = ioremap(dwCFG, 0x100);
Card.io_base=dwDivasIOBase;
Card.card_type = DIA_CARD_TYPE_DIVA_SERVER_Q;
Card.bus_type = DIA_BUS_TYPE_PCI;
Card.pdev = pdev;
Card.irq = pdev->irq;
FPGA_Done = 0;
/* Create four virtual card structures as we want to treat
the 4Bri card as 4 Bri cards*/
for(i=0;i<4;i++) {
b=Card.memory[DIVAS_RAM_MEMORY];
b+=(MQ_PROTCODE_OFFSET) * (i==0?0:1);
DPRINTF(("divas: offset = 0x%x", i* MQ_PROTCODE_OFFSET));
Card.memory[DIVAS_RAM_MEMORY]=b;
b = Card.memory[DIVAS_RAM_MEMORY];
b += MQ_SM_OFFSET;
Card.memory[DIVAS_SHARED_MEMORY] = b;
Card.slot = -1;
sprintf(Card.name, "DIVASQ%ld", i);
Card.serial = PCIserial;
Card.card_id = wNumCards;
if (DivasCardNew(&Card) != 0) {
break;
}
wNumCards++;
}
}
pdev = NULL;
while ((pdev = pci_find_device(PCI_VENDOR_ID_EICON,
PCI_DEVICE_ID_EICON_MAESTRA,
pdev))) {
dword dwPLXIOBase, dwDivasIOBase;
printk(KERN_DEBUG "Divas: DIVA Server BRI (S/T) Found\n");
dwPLXIOBase = pci_resource_start(pdev, 1);
dwDivasIOBase = pci_resource_start(pdev, 2);
Card.card_id = wNumCards;
Card.card_type = DIA_CARD_TYPE_DIVA_SERVER_B;
Card.bus_type = DIA_BUS_TYPE_PCI;
Card.pdev = pdev;
Card.irq = pdev->irq;
Card.reset_base = dwPLXIOBase;
Card.io_base = dwDivasIOBase;
Card.slot = -1;
strcpy(Card.name, "DIVASB");
if (check_region(Card.io_base, 0x20)) {
printk(KERN_WARNING "Divas: DIVA I/O Base already in use 0x%x-0x%x\n", Card.io_base, Card.io_base + 0x1F);
}
if (check_region(Card.reset_base, 0x80)) {
printk(KERN_WARNING "Divas: PLX I/O Base already in use 0x%x-0x%x\n", Card.reset_base, Card.reset_base + 0x7F);
continue;
}
if (DivasCardNew(&Card) != 0) {
continue;
}
wNumCards++;
}
pdev = NULL;
while ((pdev = pci_find_device(PCI_VENDOR_ID_EICON,
PCI_DEVICE_ID_EICON_MAESTRAQ_U,
pdev))) {
dword dwPLXIOBase, dwDivasIOBase;
printk(KERN_DEBUG "Divas: DIVA Server BRI (U) Found\n");
dwPLXIOBase = pci_resource_start(pdev, 1);
dwDivasIOBase = pci_resource_start(pdev, 2);
Card.card_id = wNumCards;
Card.card_type = DIA_CARD_TYPE_DIVA_SERVER_B;
Card.bus_type = DIA_BUS_TYPE_PCI;
Card.pdev = pdev;
Card.irq = pdev->irq;
Card.reset_base = dwPLXIOBase;
Card.io_base = dwDivasIOBase;
Card.slot = -1;
strcpy(Card.name, "DIVASB");
if (check_region(Card.io_base, 0x20)) {
printk(KERN_WARNING "Divas: DIVA I/O Base already in use 0x%x-0x%x\n", Card.io_base, Card.io_base + 0x1F);
continue;
}
if (check_region(Card.reset_base, 0x80)) {
printk(KERN_WARNING "Divas: PLX I/O Base already in use 0x%x-0x%x\n", Card.reset_base, Card.reset_base + 0x7F);
continue;
}
if (DivasCardNew(&Card) != 0) {
continue;
}
wNumCards++;
}
wDeviceIndex = 0;
pdev = NULL;
while ((pdev = pci_find_device(PCI_VENDOR_ID_EICON,
PCI_DEVICE_ID_EICON_MAESTRAQ_U,
pdev))) {
dword dwRAM, dwREG, dwCFG;
printk(KERN_DEBUG "Divas: DIVA Server PRI Found\n");
dwRAM = pci_resource_start(pdev, 0);
dwREG = pci_resource_start(pdev, 2);
dwCFG = pci_resource_start(pdev, 4);
Card.memory[DIVAS_RAM_MEMORY] = ioremap(dwRAM, 0x10000);
Card.memory[DIVAS_REG_MEMORY] = ioremap(dwREG, 0x4000);
Card.memory[DIVAS_CFG_MEMORY] = ioremap(dwCFG, 0x1000);
Card.memory[DIVAS_SHARED_MEMORY] = Card.memory[DIVAS_RAM_MEMORY] + DIVAS_SHARED_OFFSET;
Card.card_id = wNumCards;
Card.card_type = DIA_CARD_TYPE_DIVA_SERVER;
Card.bus_type = DIA_BUS_TYPE_PCI;
Card.pdev = pdev;
Card.irq = pdev->irq;
Card.slot = -1;
strcpy(Card.name, "DIVASP");
if (DivasCardNew(&Card) != 0) {
continue;
}
wNumCards++;
}
printk(KERN_INFO "Divas: %d cards detected\n", wNumCards);
if(wNumCards == 0) {
return -1;
}
Divas_fops.ioctl = do_ioctl;
Divas_fops.poll = do_poll;
Divas_fops.read = do_read;
Divas_fops.open = do_open;
Divas_fops.release = do_release;
Divas_major = register_chrdev(0, "Divas", &Divas_fops);
if (Divas_major < 0) {
printk(KERN_WARNING "Divas: Unable to register character driver\n");
return -1;
}
return 0;
}
/* Error return -1 */
int DivasConfigGet(dia_card_t *card)
{
/* Retrieve Config from O/S? Not in Linux */
return 0;
}
dia_config_t *DivasConfig(card_t *card, dia_config_t *config)
{
/* If config retrieved from OS then copy the data into a dia_config_t structure here
and return the pointer here. If the config 'came from above' then just
return config;
*/
return config;
}
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.12
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/slab.h>
#undef N_DATA
#include "adapter.h"
#include "divas.h"
#include "divalog.h"
extern int DivasCardNext;
void UxPause(long ms);
int DivasGetMem(mem_block_t *);
#define DIA_IOCTL_UNLOCK 12
void UnlockDivas(void);
int do_ioctl(struct inode *pDivasInode, struct file *pDivasFile,
unsigned int command, unsigned long arg)
{
byte *pUserCards, card_i;
word wCardNum;
switch (command)
{
case DIA_IOCTL_CONFIG:
{
dia_config_t DivaConfig;
if (copy_from_user(&DivaConfig, (void *)arg, sizeof(dia_config_t)))
return -EFAULT;
DivasCardConfig(&DivaConfig);
return 0;
}
case DIA_IOCTL_DETECT:
pUserCards = (byte *) arg;
if (!verify_area(VERIFY_WRITE, pUserCards, 20))
{
if(__put_user(DivasCardNext, pUserCards++))
return -EFAULT;
for (card_i=1; card_i < 20; card_i++)
{
if(__put_user((byte) DivasCards[card_i - 1].cfg.card_type, pUserCards++))
return -EFAULT;
}
}
else return -EFAULT;
return 0;
case DIA_IOCTL_START:
{
dia_start_t DivaStart;
if (copy_from_user(&DivaStart, (void *)arg, sizeof(dia_start_t)))
return -EFAULT;
return DivasCardStart(DivaStart.card_id);
}
case DIA_IOCTL_FLAVOUR:
return 0;
case DIA_IOCTL_LOAD:
{
dia_load_t DivaLoad;
if(copy_from_user(&DivaLoad, (void *)arg, sizeof(dia_load_t)))
return -EFAULT;
if (!verify_area(VERIFY_READ, DivaLoad.code,DivaLoad.length))
{
if (DivasCardLoad(&DivaLoad))
{
printk(KERN_WARNING "Divas: Error loading DIVA Server adapter\n");
return -EINVAL;
}
return 0;
}
return -EFAULT;
}
case DIA_IOCTL_LOG:
{
dia_log_t DivaLog;
if (copy_from_user(&DivaLog, (void *) arg, sizeof(dia_log_t)))
return -EFAULT;
DivasLog(&DivaLog);
return 0;
}
case DIA_IOCTL_XLOG_REQ:
if(get_user(wCardNum, (word *) arg))
return -EFAULT;
DivasXlogReq(wCardNum);
return 0;
case DIA_IOCTL_GET_NUM:
if(put_user(DivasCardNext, (int *)arg))
return -EFAULT;
return 0;
case DIA_IOCTL_GET_LIST:
{
dia_card_list_t cards;
DPRINTF(("divas: DIA_IOCTL_GET_LIST"));
DivasGetList(&cards);
if(copy_to_user((void *)arg, &cards, sizeof(cards)))
return -EFAULT;
return 0;
}
case DIA_IOCTL_GET_MEM:
{
mem_block_t mem_block;
if (copy_from_user(&mem_block, (void *)arg, sizeof(mem_block_t)))
return -EFAULT;
DivasGetMem(&mem_block);
return 0;
}
case DIA_IOCTL_UNLOCK:
UnlockDivas();
return 0;
default:
return -EINVAL;
}
return -EINVAL;
}
unsigned int do_poll(struct file *pFile, struct poll_table_struct *pPollTable)
{
word wMask = 0;
if (!DivasLogFifoEmpty())
wMask |= POLLIN | POLLRDNORM;
return wMask;
}
ssize_t do_read(struct file *pFile, char *pUserBuffer, size_t BufferSize, loff_t *pOffset)
{
klog_t *pClientLogBuffer = (klog_t *) pUserBuffer;
klog_t *pHeadItem;
if (BufferSize < sizeof(klog_t))
return -EIO;
pHeadItem = (klog_t *) DivasLogFifoRead();
if (pHeadItem) {
if (copy_to_user(pClientLogBuffer, pHeadItem, sizeof(klog_t))) {
kfree(pHeadItem);
return -EFAULT;
}
kfree(pHeadItem);
return sizeof(klog_t);
}
return 0;
}
static int private_usage_count;
int do_open(struct inode *pInode, struct file *pFile)
{
MOD_INC_USE_COUNT;
#ifdef MODULE
private_usage_count++;
#endif
return 0;
}
int do_release(struct inode *pInode, struct file *pFile)
{
MOD_DEC_USE_COUNT;
#ifdef MODULE
private_usage_count--;
#endif
return 0;
}
void UnlockDivas(void)
{
while (private_usage_count > 0)
{
private_usage_count--;
MOD_DEC_USE_COUNT;
}
}
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.16
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#define N_DATA
#include <linux/config.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/smp_lock.h>
#include <asm/io.h>
#include <asm/system.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>
#undef N_DATA
#include "uxio.h"
static
int log_on=0;
int Divasdevflag = 0;
spinlock_t diva_lock = SPIN_LOCK_UNLOCKED;
static
ux_diva_card_t card_pool[MAX_CARDS];
void UxPause(long int ms)
{
unsigned long timeout = jiffies + ((ms * HZ) / 1000);
while (time_before(jiffies, timeout));
}
int UxCardHandleGet(ux_diva_card_t **card, dia_card_t *cfg)
{
int i;
ux_diva_card_t *c;
if (cfg->bus_type != DIA_BUS_TYPE_PCI)
{
DPRINTF(("divas hw: type not PCI (%d)", cfg->bus_type));
return -1;
}
for (i = 0; (i < DIM(card_pool)) && (card_pool[i].in_use); i++)
{
;
}
if (i == DIM(card_pool))
{
DPRINTF(("divas hw: card_pool exhausted"));
return -1;
}
c = *card = &card_pool[i];
switch (cfg->bus_type)
{
case DIA_BUS_TYPE_PCI:
c->pdev = cfg->pdev;
c->io_base = cfg->io_base;
c->reset_base = cfg->reset_base;
c->card_type = cfg->card_type;
c->mapped = NULL;
c->slot = cfg->slot;
c->irq = (int) cfg->irq;
c->pDRAM = cfg->memory[DIVAS_RAM_MEMORY];
c->pDEVICES = cfg->memory[DIVAS_REG_MEMORY];
c->pCONFIG = cfg->memory[DIVAS_CFG_MEMORY];
c->pSHARED = cfg->memory[DIVAS_SHARED_MEMORY];
c->pCONTROL = cfg->memory[DIVAS_CTL_MEMORY];
/* c->bus_type = DIA_BUS_TYPE_PCI;
c->bus_num = cfg->bus_num & 0x3f;
c->slot = cfg->slot;
c->irq = (int) cfg->irq;
c->int_priority = (int) cfg->int_priority;
c->card_type = cfg->card_type;
c->io_base = cfg->io_base;
c->reset_base = cfg->reset_base;
c->pDRAM = cfg->memory[DIVAS_RAM_MEMORY];
c->pDEVICES = cfg->memory[DIVAS_REG_MEMORY];
c->pCONFIG = cfg->memory[DIVAS_CFG_MEMORY];
c->pSHARED = cfg->memory[DIVAS_SHARED_MEMORY];
DPRINTF(("divas hw: pDRAM is 0x%x", c->pDRAM));
DPRINTF(("divas hw: pSHARED is 0x%x", c->pSHARED));
DPRINTF(("divas hw: pCONFIG is 0x%x", c->pCONFIG));
c->cm_key = cm_getbrdkey("Divas", cfg->card_id);*/
break;
default:
break;
}
c->in_use = TRUE;
return 0;
}
void UxCardHandleFree(ux_diva_card_t *card)
{
card->in_use = FALSE;
}
#define PLX_IOBASE 0
#define DIVAS_IOBASE 1
void *UxCardMemAttach(ux_diva_card_t *card, int id)
{
if (card->card_type == DIA_CARD_TYPE_DIVA_SERVER)
{
switch (id)
{
case DIVAS_SHARED_MEMORY:
card->mapped = card->pSHARED;
return card->pSHARED;
break;
case DIVAS_RAM_MEMORY:
card->mapped = card->pDRAM;
return card->pDRAM;
break;
case DIVAS_REG_MEMORY:
card->mapped = card->pDEVICES;
return card->pDEVICES;
break;
case DIVAS_CFG_MEMORY:
card->mapped = card->pCONFIG;
return card->pCONFIG;
break;
default:
ASSERT(FALSE);
card->mapped = NULL;
return (void *) 0;
}
}
else if (card->card_type == DIA_CARD_TYPE_DIVA_SERVER_B)
{
switch (id)
{
case PLX_IOBASE:
return (void *) card->reset_base;
break;
case DIVAS_IOBASE:
return (void *) card->io_base;
break;
default:
ASSERT(FALSE);
return 0;
}
}
else if (card->card_type == DIA_CARD_TYPE_DIVA_SERVER_Q)
{
switch (id)
{
case DIVAS_SHARED_MEMORY:
card->mapped = card->pSHARED;
return card->pSHARED;
break;
case DIVAS_RAM_MEMORY:
card->mapped = card->pDRAM;
return card->pDRAM;
break;
case DIVAS_REG_MEMORY:
card->mapped = (void *) card->io_base;
return (void *) card->io_base;
break;
case DIVAS_CTL_MEMORY:
card->mapped = card->pCONTROL;
return card->pCONTROL;
break;
default:
// ASSERT(FALSE);
DPRINTF(("divas: Trying to attach to mem %d", id));
card->mapped = NULL;
return (void *) 0;
}
} else
DPRINTF(("divas: Tried to attach to unknown card"));
/* Unknown card type */
return NULL;
}
void UxCardMemDetach(ux_diva_card_t *card, void *address)
{
return; // Just a place holder. No un-mapping done.
}
void UxCardLog(int turn_on)
{
log_on = turn_on;
}
/*
* Control Register I/O Routines to be performed on Attached I/O ports
*/
void UxCardPortIoOut(ux_diva_card_t *card, void *AttachedBase, int offset, byte the_byte)
{
word base = (word) (dword) AttachedBase;
base += offset;
outb(the_byte, base);
}
void UxCardPortIoOutW(ux_diva_card_t *card, void *AttachedBase, int offset, word the_word)
{
word base = (word) (dword) AttachedBase;
base += offset;
outw(the_word, base);
}
void UxCardPortIoOutD(ux_diva_card_t *card, void *AttachedBase, int offset, dword the_dword)
{
word base = (word) (dword) AttachedBase;
base += offset;
outl(the_dword, base);
}
byte UxCardPortIoIn(ux_diva_card_t *card, void *AttachedBase, int offset)
{
word base = (word) (dword) AttachedBase;
base += offset;
return inb(base);
}
word UxCardPortIoInW(ux_diva_card_t *card, void *AttachedBase, int offset)
{
word base = (word) (dword) AttachedBase;
base += offset;
return inw(base);
}
/*
* Memory mapped card I/O functions
*/
byte UxCardMemIn(ux_diva_card_t *card, void *address)
{
byte b;
volatile byte* t = (byte*)address;
b = *t;
if (log_on)
{
byte *a = address;
a -= (int) card->mapped;
DPRINTF(("divas hw: read 0x%02x from 0x%x (memory mapped)", b & 0xff, a));
}
return(b);
}
word UxCardMemInW(ux_diva_card_t *card, void *address)
{
word w;
volatile word* t = (word*)address;
w = *t;
if (log_on)
{
byte *a = address;
a -= (int) card->mapped;
DPRINTF(("divas hw: read 0x%04x from 0x%x (memory mapped)", w & 0xffff, a));
}
return (w);
}
dword UxCardMemInD(ux_diva_card_t *card, void *address)
{
dword dw;
volatile dword* t = (dword*)address;
dw = *t;
if (log_on)
{
byte *a = address;
a -= (int) card->mapped;
DPRINTF(("divas hw: read 0x%08x from 0x%x (memory mapped)", dw, a));
}
return (dw);
}
void UxCardMemInBuffer(ux_diva_card_t *card, void *address, void *buffer, int length)
{
volatile byte *pSource = address;
byte *pDest = buffer;
while (length--)
{
*pDest++ = *pSource++;
}
if (log_on)
{
byte *a = address;
a -= (int) card->mapped;
pDest = buffer;
DPRINTF(("divas hw: read %02x %02x %02x %02x %02x %02x %02x %02x from 0x%x (memory mapped)",
pDest[0] & 0xff, pDest[1] & 0xff, pDest[2] & 0xff, pDest[3] & 0xff,
pDest[4] & 0xff, pDest[5] & 0xff, pDest[6] & 0xff, pDest[7] & 0xff,
a));
}
return;
}
void UxCardMemOut(ux_diva_card_t *card, void *address, byte data)
{
volatile byte* t = (byte*)address;
if (log_on)
{
byte *a = address;
a -= (int) card->mapped;
DPRINTF(("divas hw: wrote 0x%02x to 0x%x (memory mapped)", data & 0xff, a));
}
*t = data;
return;
}
void UxCardMemOutW(ux_diva_card_t *card, void *address, word data)
{
volatile word* t = (word*)address;
if (log_on)
{
byte *a = address;
a -= (int) card->mapped;
DPRINTF(("divas hw: wrote 0x%04x to 0x%x (memory mapped)", data & 0xffff, a));
}
*t = data;
return;
}
void UxCardMemOutD(ux_diva_card_t *card, void *address, dword data)
{
volatile dword* t = (dword*)address;
if (log_on)
{
byte *a = address;
a -= (int) card->mapped;
DPRINTF(("divas hw: wrote 0x%08x to 0x%x (memory mapped)", data, a));
}
*t = data;
return;
}
void UxCardMemOutBuffer(ux_diva_card_t *card, void *address, void *buffer, int length)
{
byte *pSource = buffer;
byte *pDest = address;
while (length--)
{
*pDest++ = *pSource++;
}
if (log_on)
{
byte *a = address;
a -= (int) card->mapped;
pDest = buffer;
DPRINTF(("divas hw: wrote %02x %02x %02x %02x %02x %02x %02x %02x to 0x%x (memory mapped)",
pDest[0] & 0xff, pDest[1] & 0xff, pDest[2] & 0xff, pDest[3] & 0xff,
pDest[4] & 0xff, pDest[5] & 0xff, pDest[6] & 0xff, pDest[7] & 0xff,
a));
}
return;
}
/*
* Memory mapped card I/O functions
*/
byte UxCardIoIn(ux_diva_card_t *card, void *AttachedDivasIOBase, void *address)
{
byte the_byte;
outb(0xFF, card->io_base + 0xC);
outw((word) (dword) address, card->io_base + 4);
the_byte = inb(card->io_base);
if (log_on)
{
DPRINTF(("divas hw: read 0x%02x from 0x%x (I/O mapped)",
the_byte & 0xff, address));
}
return the_byte;
}
word UxCardIoInW(ux_diva_card_t *card, void *AttachedDivasIOBase, void *address)
{
word the_word;
outb(0xFF, card->io_base + 0xC);
outw((word) (dword) address, card->io_base + 4);
the_word = inw(card->io_base);
if (log_on)
{
DPRINTF(("divas hw: read 0x%04x from 0x%x (I/O mapped)",
the_word & 0xffff, address));
}
return the_word;
}
dword UxCardIoInD(ux_diva_card_t *card, void *AttachedDivasIOBase, void *address)
{
dword the_dword;
outb(0xFF, card->io_base + 0xC);
outw((word) (dword) address, card->io_base + 4);
the_dword = inl(card->io_base);
if (log_on)
{
DPRINTF(("divas hw: read 0x%08x from 0x%x (I/O mapped)",
the_dword, address));
}
return the_dword;
}
void UxCardIoInBuffer(ux_diva_card_t *card, void *AttachedDivasIOBase, void *address, void *buffer, int length)
{
byte *pSource = address;
byte *pDest = buffer;
if ((word) (dword) address & 0x1)
{
outb(0xFF, card->io_base + 0xC);
outw((word) (dword) pSource, card->io_base + 4);
*pDest = (byte) inb(card->io_base);
pDest++;
pSource++;
length--;
if (!length)
{
return;
}
}
outb(0xFF, card->io_base + 0xC);
outw((word) (dword) pSource, card->io_base + 4);
insw(card->io_base, (word *)pDest,length%2 ? (length+1)>>1 : length>>1);
if (log_on)
{
pDest = buffer;
DPRINTF(("divas hw: read %02x %02x %02x %02x %02x %02x %02x %02x from 0x%x (I/O mapped)",
pDest[0] & 0xff, pDest[1] & 0xff, pDest[2] & 0xff, pDest[3] & 0xff,
pDest[4] & 0xff, pDest[5] & 0xff, pDest[6] & 0xff, pDest[7] & 0xff,
address));
}
return;
}
/* Output */
void UxCardIoOut(ux_diva_card_t *card, void *AttachedDivasIOBase, void *address, byte data)
{
if (log_on)
{
DPRINTF(("divas hw: wrote 0x%02x to 0x%x (I/O mapped)",
data & 0xff, address));
}
outb(0xFF, card->io_base + 0xC);
outw((word) (dword) address, card->io_base + 4);
outb((byte) data & 0xFF, card->io_base);
return;
}
void UxCardIoOutW(ux_diva_card_t *card, void *AttachedDivasIOBase, void *address, word data)
{
if (log_on)
{
DPRINTF(("divas hw: wrote 0x%04x to 0x%x (I/O mapped)",
data & 0xffff, address));
}
outb(0xFF, card->io_base + 0xC);
outw((word) (dword) address, card->io_base + 4);
outw((word) data & 0xFFFF, card->io_base);
return;
}
void UxCardIoOutD(ux_diva_card_t *card, void *AttachedDivasIOBase, void *address, dword data)
{
if (log_on)
{
DPRINTF(("divas hw: wrote 0x%08x to 0x%x (I/O mapped)", data, address));
}
outb(0xFF, card->io_base + 0xC);
outw((word) (dword) address, card->io_base + 4);
outl((dword) data & 0xFFFFFFFF, card->io_base);
return;
}
void UxCardIoOutBuffer(ux_diva_card_t *card, void *AttachedDivasIOBase, void *address, void *buffer, int length)
{
byte *pSource = buffer;
byte *pDest = address;
if ((word) (dword) address & 1)
{
outb(0xFF, card->io_base + 0xC);
outw((word) (dword) pDest, card->io_base + 4);
outb(*pSource, card->io_base);
pSource++;
pDest++;
length--;
if (!length)
{
return;
}
}
outb(0xFF, card->io_base + 0xC);
outw((word) (dword) pDest, card->io_base + 4);
outsw(card->io_base, (word *)pSource, length%2 ? (length+1)>>1 : length>>1);
if (log_on)
{
pDest = buffer;
DPRINTF(("divas hw: wrote %02x %02x %02x %02x %02x %02x %02x %02x to 0x%x (I/O mapped)",
pDest[0] & 0xff, pDest[1] & 0xff, pDest[2] & 0xff, pDest[3] & 0xff,
pDest[4] & 0xff, pDest[5] & 0xff, pDest[6] & 0xff, pDest[7] & 0xff,
address));
}
return;
}
irqreturn_t Divasintr(int arg, void *unused, struct pt_regs *unused_regs)
{
int i;
card_t *card = NULL;
ux_diva_card_t *ux_ref = NULL;
for (i = 0; i < DivasCardNext; i++)
{
if (arg == DivasCards[i].cfg.irq)
{
card = &DivasCards[i];
ux_ref = card->hw;
if ((ux_ref) && (card->is_live))
{
(*ux_ref->user_isr)(ux_ref->user_isr_arg);
}
else
{
DPRINTF(("divas: ISR couldn't locate card"));
}
}
}
return IRQ_HANDLED;
}
int UxIsrInstall(ux_diva_card_t *card, isr_fn_t *isr_fn, void *isr_arg)
{
int result;
card->user_isr = isr_fn;
card->user_isr_arg = isr_arg;
result = request_irq(card->irq, Divasintr, SA_INTERRUPT | SA_SHIRQ, "Divas", (void *) isr_arg);
return result;
}
void UxIsrRemove(ux_diva_card_t *card, void *dev_id)
{
free_irq(card->irq, card->user_isr_arg);
}
void UxPciConfigWrite(ux_diva_card_t *card, int size, int offset, void *value)
{
switch (size) {
case sizeof(byte):
pci_write_config_byte(card->pdev, offset, * (byte *) value);
break;
case sizeof(word):
pci_write_config_word(card->pdev, offset, * (word *) value);
break;
case sizeof(dword):
pci_write_config_dword(card->pdev, offset, * (dword *) value);
break;
default:
printk(KERN_WARNING "Divas: Invalid size in UxPciConfigWrite\n");
}
}
void UxPciConfigRead(ux_diva_card_t *card, int size, int offset, void *value)
{
switch (size) {
case sizeof(byte):
pci_read_config_byte(card->pdev, offset, (byte *) value);
break;
case sizeof(word):
pci_read_config_word(card->pdev, offset, (word *) value);
break;
case sizeof(dword):
pci_read_config_dword(card->pdev, offset, (unsigned int *) value);
break;
default:
printk(KERN_WARNING "Divas: Invalid size in UxPciConfigRead\n");
}
}
void *UxAlloc(unsigned int size)
{
void *m;
m = kmalloc(size, GFP_ATOMIC);
return m;
}
void UxFree(void *ptr)
{
kfree(ptr);
}
long UxCardLock(ux_diva_card_t *card)
{
unsigned long flags;
spin_lock_irqsave(&diva_lock, flags);
return flags;
}
void UxCardUnlock(ux_diva_card_t *card, unsigned long ipl)
{
spin_unlock_irqrestore(&diva_lock, ipl);
}
dword UxTimeGet(void)
{
return jiffies;
}
long UxInterlockedIncrement(ux_diva_card_t *card, long *dst)
{
register volatile long *p;
register long ret;
int ipl;
p =dst;
ipl = UxCardLock(card);
*p += 1;
ret = *p;
UxCardUnlock(card,ipl);
return(ret);
}
long UxInterlockedDecrement(ux_diva_card_t *card, long *dst)
{
register volatile long *p;
register long ret;
int ipl;
p =dst;
ipl = UxCardLock(card);
*p -= 1;
ret = *p;
UxCardUnlock(card,ipl);
return(ret);
}
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.10
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/sched.h>
#undef N_DATA
#include <linux/workqueue.h>
#include <linux/smp.h>
struct pt_regs;
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include "sys.h"
#include "divas.h"
#include "adapter.h"
#include "divalog.h"
#include "uxio.h"
static struct tasklet_struct DivasTask;
int Divas4BRIInitPCI(card_t *card, dia_card_t *cfg)
{
/* Use UxPciConfigWrite routines to initialise PCI config space */
/* wPCIcommand = 0x03;
cm_write_devconfig16(CMKey, PCI_COMMAND, &wPCIcommand);
wPCIcommand = 0x280;
cm_write_devconfig16(CMKey, PCI_STATUS, &wPCIcommand);
bPCIcommand = 0x30;
cm_write_devconfig16(CMKey, PCI_STATUS, &wPCIcommand);
*/
return 0;
}
int DivasPRIInitPCI(card_t *card, dia_card_t *cfg)
{
/* Use UxPciConfigWrite routines to initialise PCI config space */
/* wPCIcommand = 0x03;
cm_write_devconfig16(CMKey, PCI_COMMAND, &wPCIcommand);
wPCIcommand = 0x280;
cm_write_devconfig16(CMKey, PCI_STATUS, &wPCIcommand);
bPCIcommand = 0x30;
cm_write_devconfig8(CMKey, PCI_LATENCY, &bPCIcommand);*/
return 0;
}
int DivasBRIInitPCI(card_t *card, dia_card_t *cfg)
{
/* Need to set these platform dependent values after patching */
card->hw->reset_base = card->cfg.reset_base;
card->hw->io_base = card->cfg.io_base;
request_region(card->hw->reset_base,0x80,"Divas");
request_region(card->hw->io_base,0x20,"Divas");
/* Same as for PRI */
return DivasPRIInitPCI(card, cfg);
}
/* ######################### Stubs of routines that are not done yet ################## */
/*void DivasLogIdi(card_t *card, ENTITY *e, int request)
{
}
*/
int DivasDpcSchedule(void)
{
tasklet_schedule(&DivasTask);
return 0;
}
int DivasScheduleRequestDpc(void)
{
tasklet_schedule(&DivasTask);
return 0;
}
void DivasInitDpc(void)
{
tasklet_init(&DivasTask, DivasDoDpc, 0);
}
void DivasLogAdd(void *buffer, int length)
{
static
boolean_t overflow = FALSE;
static
boolean_t busy = FALSE;
/* make sure we're not interrupting ourselves */
if (busy)
{
printk(KERN_DEBUG "Divas: Logging interrupting self !\n");
return;
}
busy = TRUE;
/* ignore call if daemon isn't running and we've reached limit */
if (DivasLogFifoFull())
{
if (!overflow)
{
printk(KERN_DEBUG "Divas: Trace buffer full\n");
overflow = TRUE;
}
busy = FALSE;
return;
}
DivasLogFifoWrite(buffer, length);
busy = FALSE;
return;
}
/* #################################################################################### */
/*
* Source file for diva log facility
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.5
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "sys.h"
#include "idi.h"
#include "divas.h"
#include "adapter.h"
#include "divalog.h"
#include "uxio.h"
/*Counter to monitor number of messages */
static int m_count;
#define MAX_BUFFERED_MSGS (1000)
/* Our Linked List Structure to hold message */
typedef struct klog_link{
klog_t klog;
struct klog_link *next;
}KNODE;
/* First & Last structures in list*/
KNODE *head;
KNODE *tail;
/*
* retrieve message from FIFO buffer
* returns NULL if buffer empty
* otherwise returns pointer to entry
*/
char *DivasLogFifoRead(void)
{
KNODE *old_head;
if(head==NULL)
{
/* Buffer Empty - No Messages */
return NULL;
}
m_count--;
/* Keep track of message to be read & increment to next message*/
old_head = head;
head = head->next;
/*Return ptr to Msg */
return((char *)old_head);
}
/*
* write message into FIFO buffer
*/
void DivasLogFifoWrite(char *entry, int length)
{
KNODE *new_klog;
if(head == NULL)
{
/* No Entries in Log */
tail=NULL;
m_count=0;
new_klog=UxAlloc(sizeof(KNODE));
if(new_klog==NULL)
{
return;
}
m_count++;
memset(new_klog, 0, sizeof(KNODE));
/* Set head & tail to point to the new Msg Struct */
head=tail=new_klog;
tail->next=NULL;
}
else
{
new_klog=UxAlloc(sizeof(KNODE));
if(new_klog==NULL)
{
return;
}
m_count++;
memset(new_klog, 0, sizeof(KNODE));
/* Let last Msg Struct point to new Msg Struct & inc tail */
tail->next=new_klog;
tail=new_klog;
tail->next=NULL;
}
if (length > sizeof(klog_t))
{
length = sizeof(klog_t);
}
memcpy(&tail->klog, entry, length);
return;
}
/*
* DivaslogFifoEmpty:return TRUE if FIFO buffer is empty,otherwise FALSE
*/
int DivasLogFifoEmpty(void)
{
return (m_count == 0);
}
/*
*DivasLogFifoFull:return TRUE if FIFO buffer is full,otherwise FALSE
*/
int DivasLogFifoFull(void)
{
return (m_count == MAX_BUFFERED_MSGS);
}
/*
* generate an IDI log entry
*/
void DivasLogIdi(card_t *card, ENTITY *e, int request)
{
klog_t klog;
memset(&klog, 0, sizeof(klog));
klog.time_stamp = UxTimeGet();
klog.length = sizeof(ENTITY) > sizeof(klog.buffer) ?
sizeof(klog.buffer) : sizeof(ENTITY);
klog.card = (int) (card - DivasCards);
klog.type = request ? KLOG_IDI_REQ : KLOG_IDI_CALLBACK;
klog.code = 0;
memcpy(klog.buffer, e, klog.length);
/* send to the log driver and return */
DivasLogAdd(&klog, sizeof(klog));
return;
}
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.2
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef PC_H_INCLUDED
#define PC_H_INCLUDED
/*------------------------------------------------------------------*/
/* buffer definition */
/*------------------------------------------------------------------*/
typedef struct {
word length; /* length of data/parameter field */
byte P[270]; /* data/parameter field */
} PBUFFER;
/*------------------------------------------------------------------*/
/* dual port ram structure */
/*------------------------------------------------------------------*/
struct dual
{
byte Req; /* request register */
byte ReqId; /* request task/entity identification */
byte Rc; /* return code register */
byte RcId; /* return code task/entity identification */
byte Ind; /* Indication register */
byte IndId; /* Indication task/entity identification */
byte IMask; /* Interrupt Mask Flag */
byte RNR; /* Receiver Not Ready (set by PC) */
byte XLock; /* XBuffer locked Flag */
byte Int; /* ISDN-S interrupt */
byte ReqCh; /* Channel field for layer-3 Requests */
byte RcCh; /* Channel field for layer-3 Returncodes */
byte IndCh; /* Channel field for layer-3 Indications */
byte MInd; /* more data indication field */
word MLength; /* more data total packet length */
byte ReadyInt; /* request field for ready interrupt */
byte SWReg; /* Software register for special purposes */
byte Reserved[11]; /* reserved space */
byte InterfaceType; /* interface type 1=16K interface */
word Signature; /* ISDN-S adapter Signature (GD) */
PBUFFER XBuffer; /* Transmit Buffer */
PBUFFER RBuffer; /* Receive Buffer */
};
/*------------------------------------------------------------------*/
/* SWReg Values (0 means no command) */
/*------------------------------------------------------------------*/
#define SWREG_DIE_WITH_LEDON 0x01
#define SWREG_HALT_CPU 0x02 /* Push CPU into a while(1) loop */
/*------------------------------------------------------------------*/
/* Id Fields Coding */
/*------------------------------------------------------------------*/
#define ID_MASK 0xe0 /* Mask for the ID field */
#define GL_ERR_ID 0x1f /* ID for error reporting on global requests*/
#define DSIG_ID 0x00 /* ID for D-channel signaling */
#define NL_ID 0x20 /* ID for network-layer access (B or D) */
#define BLLC_ID 0x60 /* ID for B-channel link level access */
#define TASK_ID 0x80 /* ID for dynamic user tasks */
#define TIMER_ID 0xa0 /* ID for timer task */
#define TEL_ID 0xc0 /* ID for telephone support */
#define MAN_ID 0xe0 /* ID for management */
/*------------------------------------------------------------------*/
/* ASSIGN and REMOVE requests are the same for all entities */
/*------------------------------------------------------------------*/
#define ASSIGN 0x01
#define UREMOVE 0xfe /* without returncode */
#define REMOVE 0xff
/*------------------------------------------------------------------*/
/* Timer Interrupt Task Interface */
/*------------------------------------------------------------------*/
#define ASSIGN_TIM 0x01
#define REMOVE_TIM 0xff
/*------------------------------------------------------------------*/
/* dynamic user task interface */
/*------------------------------------------------------------------*/
#define ASSIGN_TSK 0x01
#define REMOVE_TSK 0xff
#define LOAD 0xf0
#define RELOCATE 0xf1
#define START 0xf2
#define LOAD2 0xf3
#define RELOCATE2 0xf4
/*------------------------------------------------------------------*/
/* dynamic user task messages */
/*------------------------------------------------------------------*/
#define TSK_B2 0x0000
#define TSK_WAKEUP 0x2000
#define TSK_TIMER 0x4000
#define TSK_TSK 0x6000
#define TSK_PC 0xe000
/*------------------------------------------------------------------*/
/* LL management primitives */
/*------------------------------------------------------------------*/
#define ASSIGN_LL 1 /* assign logical link */
#define REMOVE_LL 0xff /* remove logical link */
/*------------------------------------------------------------------*/
/* LL service primitives */
/*------------------------------------------------------------------*/
#define LL_UDATA 1 /* link unit data request/indication */
#define LL_ESTABLISH 2 /* link establish request/indication */
#define LL_RELEASE 3 /* link release request/indication */
#define LL_DATA 4 /* data request/indication */
#define LL_LOCAL 5 /* switch to local operation (COM only) */
#define LL_DATA_PEND 5 /* data pending indication (SDLC SHM only) */
#define LL_REMOTE 6 /* switch to remote operation (COM only) */
#define LL_TEST 8 /* link test request */
#define LL_MDATA 9 /* more data request/indication */
#define LL_BUDATA 10 /* broadcast unit data request/indication */
#define LL_XID 12 /* XID command request/indication */
#define LL_XID_R 13 /* XID response request/indication */
/*------------------------------------------------------------------*/
/* NL service primitives */
/*------------------------------------------------------------------*/
#define N_MDATA 1 /* more data to come REQ/IND */
#define N_CONNECT 2 /* OSI N-CONNECT REQ/IND */
#define N_CONNECT_ACK 3 /* OSI N-CONNECT CON/RES */
#define N_DISC 4 /* OSI N-DISC REQ/IND */
#define N_DISC_ACK 5 /* OSI N-DISC CON/RES */
#define N_RESET 6 /* OSI N-RESET REQ/IND */
#define N_RESET_ACK 7 /* OSI N-RESET CON/RES */
#define N_DATA 8 /* OSI N-DATA REQ/IND */
#define N_EDATA 9 /* OSI N-EXPEDITED DATA REQ/IND */
#define N_UDATA 10 /* OSI D-UNIT-DATA REQ/IND */
#define N_BDATA 11 /* BROADCAST-DATA REQ/IND */
#define N_DATA_ACK 12 /* data ack ind for D-bit procedure */
#define N_EDATA_ACK 13 /* data ack ind for INTERRUPT */
#define N_Q_BIT 0x10 /* Q-bit for req/ind */
#define N_M_BIT 0x20 /* M-bit for req/ind */
#define N_D_BIT 0x40 /* D-bit for req/ind */
/*------------------------------------------------------------------*/
/* Signaling management primitives */
/*------------------------------------------------------------------*/
#define ASSIGN_SIG 1 /* assign signaling task */
#define UREMOVE_SIG 0xfe /* remove signaling task without returncode */
#define REMOVE_SIG 0xff /* remove signaling task */
/*------------------------------------------------------------------*/
/* Signaling service primitives */
/*------------------------------------------------------------------*/
#define CALL_REQ 1 /* call request */
#define CALL_CON 1 /* call confirmation */
#define CALL_IND 2 /* incoming call connected */
#define LISTEN_REQ 2 /* listen request */
#define HANGUP 3 /* hangup request/indication */
#define SUSPEND 4 /* call suspend request/confirm */
#define RESUME 5 /* call resume request/confirm */
#define SUSPEND_REJ 6 /* suspend rejected indication */
#define USER_DATA 8 /* user data for user to user signaling */
#define CONGESTION 9 /* network congestion indication */
#define INDICATE_REQ 10 /* request to indicate an incoming call */
#define INDICATE_IND 10 /* indicates that there is an incoming call */
#define CALL_RES 11 /* accept an incoming call */
#define CALL_ALERT 12 /* send ALERT for incoming call */
#define INFO_REQ 13 /* INFO request */
#define INFO_IND 13 /* INFO indication */
#define REJECT 14 /* reject an incoming call */
#define RESOURCES 15 /* reserve B-Channel hardware resources */
#define TEL_CTRL 16 /* Telephone control request/indication */
#define STATUS_REQ 17 /* Request D-State (returned in INFO_IND) */
#define FAC_REG_REQ 18 /* connection idependent fac registration */
#define FAC_REG_ACK 19 /* fac registration acknowledge */
#define FAC_REG_REJ 20 /* fac registration reject */
#define CALL_COMPLETE 21/* send a CALL_PROC for incoming call */
#define FACILITY_REQ 22 /* send a Facility Message type */
#define FACILITY_IND 22 /* Facility Message type indication */
#define SIG_CTRL 29 /* Control for signalling hardware */
#define DSP_CTRL 30 /* Control for DSPs */
#define LAW_REQ 31 /* Law config request for (returns info_i) */
/*------------------------------------------------------------------*/
/* management service primitives */
/*------------------------------------------------------------------*/
#define MAN_READ 2
#define MAN_WRITE 3
#define MAN_EXECUTE 4
#define MAN_EVENT_ON 5
#define MAN_EVENT_OFF 6
#define MAN_LOCK 7
#define MAN_UNLOCK 8
#define MAN_INFO_IND 2
#define MAN_EVENT_IND 3
#define MAN_TRACE_IND 4
#define MAN_ESC 0x80
/*------------------------------------------------------------------*/
/* return code coding */
/*------------------------------------------------------------------*/
#define UNKNOWN_COMMAND 0x01 /* unknown command */
#define WRONG_COMMAND 0x02 /* wrong command */
#define WRONG_ID 0x03 /* unknown task/entity id */
#define WRONG_CH 0x04 /* wrong task/entity id */
#define UNKNOWN_IE 0x05 /* unknown information el. */
#define WRONG_IE 0x06 /* wrong information el. */
#define OUT_OF_RESOURCES 0x07 /* ISDN-S card out of res. */
#define ADAPTER_DEAD 0x08 /* ISDN card CPU halted */
#define N_FLOW_CONTROL 0x10 /* Flow-Control, retry */
#define ASSIGN_RC 0xe0 /* ASSIGN acknowledgement */
#define ASSIGN_OK 0xef /* ASSIGN OK */
#define OK_FC 0xfc /* Flow-Control RC */
#define READY_INT 0xfd /* Ready interrupt */
#define TIMER_INT 0xfe /* timer interrupt */
#define OK 0xff /* command accepted */
/*------------------------------------------------------------------*/
/* information elements */
/*------------------------------------------------------------------*/
#define SHIFT 0x90 /* codeset shift */
#define MORE 0xa0 /* more data */
#define CL 0xb0 /* congestion level */
/* codeset 0 */
#define BC 0x04 /* Bearer Capability */
#define CAU 0x08 /* cause */
#define CAD 0x0c /* Connected address */
#define CAI 0x10 /* call identity */
#define CHI 0x18 /* channel identification */
#define LLI 0x19 /* logical link id */
#define CHA 0x1a /* charge advice */
#define DT 0x29 /* ETSI date/time */
#define KEY 0x2c /* keypad information element */
#define FTY 0x1c /* facility information element */
#define DSP 0x28 /* display */
#define OAD 0x6c /* origination address */
#define OSA 0x6d /* origination sub-address */
#define CPN 0x70 /* called party number */
#define DSA 0x71 /* destination sub-address */
#define RDX 0x73 /* redirected number extended */
#define RDN 0x74 /* redirected number */
#define LLC 0x7c /* low layer compatibility */
#define HLC 0x7d /* high layer compatibility */
#define UUI 0x7e /* user user information */
#define ESC 0x7f /* escape extension */
#define DLC 0x20 /* data link layer configuration */
#define NLC 0x21 /* network layer configuration */
/* codeset 6 */
#define SIN 0x01 /* service indicator */
#define CIF 0x02 /* charging information */
#define DATE 0x03 /* date */
#define CPS 0x07 /* called party status */
/*------------------------------------------------------------------*/
/* TEL_CTRL contents */
/*------------------------------------------------------------------*/
#define RING_ON 0x01
#define RING_OFF 0x02
#define HANDS_FREE_ON 0x03
#define HANDS_FREE_OFF 0x04
#define ON_HOOK 0x80
#define OFF_HOOK 0x90
#endif
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.0
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef PC_MAINT_H
#define PC_MAINT_H
#if !defined(MIPS_SCOM)
#define BUFFER_SZ 48
#define MAINT_OFFS 0x380
#else
#define BUFFER_SZ 128
#define MAINT_OFFS 0xff00
#endif
#define MIPS_BUFFER_SZ 128
#define MIPS_MAINT_OFFS 0xff00
#define DO_LOG 1
#define MEMR 2
#define MEMW 3
#define IOR 4
#define IOW 5
#define B1TEST 6
#define B2TEST 7
#define BTESTOFF 8
#define DSIG_STATS 9
#define B_CH_STATS 10
#define D_CH_STATS 11
#define BL1_STATS 12
#define BL1_STATS_C 13
#define GET_VERSION 14
#define OS_STATS 15
#define XLOG_SET_MASK 16
#define XLOG_GET_MASK 17
#define DSP_READ 20
#define DSP_WRITE 21
#define OK 0xff
#define MORE_EVENTS 0xfe
#define NO_EVENT 1
struct DSigStruc
{
byte Id;
byte uX;
byte listen;
byte active;
byte sin[3];
byte bc[6];
byte llc[6];
byte hlc[6];
byte oad[20];
};
struct BL1Struc {
dword cx_b1;
dword cx_b2;
dword cr_b1;
dword cr_b2;
dword px_b1;
dword px_b2;
dword pr_b1;
dword pr_b2;
word er_b1;
word er_b2;
};
struct L2Struc {
dword XTotal;
dword RTotal;
word XError;
word RError;
};
struct OSStruc {
word free_n;
};
typedef union
{
struct DSigStruc DSigStats;
struct BL1Struc BL1Stats;
struct L2Struc L2Stats;
struct OSStruc OSStats;
byte b[BUFFER_SZ];
word w[BUFFER_SZ>>1];
word l[BUFFER_SZ>>2]; /* word is wrong, do not use! Use 'd' instead. */
dword d[BUFFER_SZ>>2];
} BUFFER;
typedef union
{
struct DSigStruc DSigStats;
struct BL1Struc BL1Stats;
struct L2Struc L2Stats;
struct OSStruc OSStats;
byte b[MIPS_BUFFER_SZ];
word w[MIPS_BUFFER_SZ>>1];
word l[BUFFER_SZ>>2]; /* word is wrong, do not use! Use 'd' instead. */
dword d[MIPS_BUFFER_SZ>>2];
} MIPS_BUFFER;
#if !defined(MIPS_SCOM)
struct pc_maint
{
byte req;
byte rc;
byte *mem; /*far*/
short length;
word port;
byte fill[6];
BUFFER data;
};
#else
struct pc_maint
{
byte req;
byte rc;
byte reserved[2]; /* R3000 alignment ... */
byte far *mem;
short length;
word port;
byte fill[4]; /* data at offset 16 */
BUFFER data;
};
#endif
struct mi_pc_maint
{
byte req;
byte rc;
byte reserved[2]; /* R3000 alignment ... */
byte *mem; /*far*/
short length;
word port;
byte fill[4]; /* data at offset 16 */
MIPS_BUFFER data;
};
#endif /* PC_MAINT_H */
/*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.0
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#if !defined(PR_PC_H)
#define PR_PC_H
struct pr_ram {
word NextReq; /* pointer to next Req Buffer */
word NextRc; /* pointer to next Rc Buffer */
word NextInd; /* pointer to next Ind Buffer */
byte ReqInput; /* number of Req Buffers sent */
byte ReqOutput; /* number of Req Buffers returned */
byte ReqReserved; /* number of Req Buffers reserved */
byte Int; /* ISDN-P interrupt */
byte XLock; /* Lock field for arbitration */
byte RcOutput; /* number of Rc buffers received */
byte IndOutput; /* number of Ind buffers received */
byte IMask; /* Interrupt Mask Flag */
byte Reserved1[2]; /* reserved field, do not use */
byte ReadyInt; /* request field for ready interrupt */
byte Reserved2[12]; /* reserved field, do not use */
byte InterfaceType; /* interface type 1=16K interface */
word Signature; /* ISDN-P initialized indication */
byte B[1]; /* buffer space for Req,Ind and Rc */
};
typedef struct {
word next;
byte Req;
byte ReqId;
byte ReqCh;
byte Reserved1;
word Reference;
byte Reserved[8];
PBUFFER XBuffer;
} REQ;
typedef struct {
word next;
byte Rc;
byte RcId;
byte RcCh;
byte Reserved1;
word Reference;
byte Reserved2[8];
} RC;
typedef struct {
word next;
byte Ind;
byte IndId;
byte IndCh;
byte MInd;
word MLength;
word Reference;
byte RNR;
byte Reserved;
dword Ack;
PBUFFER RBuffer;
} IND;
#endif
/*
* Diva Server PRI specific part of initialisation
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.5
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "sys.h"
#include "idi.h"
#include "divas.h"
#include "pc.h"
#include "pr_pc.h"
#include "dsp_defs.h"
#include "adapter.h"
#include "uxio.h"
#define DIVAS_LOAD_CMD 0x02
#define DIVAS_START_CMD 0x03
#define DIVAS_IRQ_RESET 0xC18
#define DIVAS_IRQ_RESET_VAL 0xFE
#define TEST_INT_DIVAS 0x11
#define TEST_INT_DIVAS_BRI 0x12
#define DIVAS_RESET 0x81
#define DIVAS_LED1 0x04
#define DIVAS_LED2 0x08
#define DIVAS_LED3 0x20
#define DIVAS_LED4 0x40
#define DIVAS_RESET_REG 0x20
#define DIVAS_SIGNATURE 0x4447
/* offset to start of MAINT area (used by xlog) */
#define DIVAS_MAINT_OFFSET 0xef00 /* value for PRI card */
#define MP_PROTOCOL_ADDR 0xA0011000
#define MP_DSP_CODE_BASE 0xa03a0000
typedef struct {
dword cmd;
dword addr;
dword len;
dword err;
dword live;
dword reserved[(0x1020>>2)-6];
dword signature;
byte data[1];
} diva_server_boot_t;
byte mem_in(ADAPTER *a, void *adr);
word mem_inw(ADAPTER *a, void *adr);
void mem_in_buffer(ADAPTER *a, void *adr, void *P, word length);
void mem_look_ahead(ADAPTER *a, PBUFFER *RBuffer, ENTITY *e);
void mem_out(ADAPTER *a, void *adr, byte data);
void mem_outw(ADAPTER *a, void *adr, word data);
void mem_out_buffer(ADAPTER *a, void *adr, void *P, word length);
void mem_inc(ADAPTER *a, void *adr);
int DivasPRIInitPCI(card_t *card, dia_card_t *cfg);
static int pri_ISR (card_t* card);
static int diva_server_reset(card_t *card)
{
byte *reg;
diva_server_boot_t *boot = NULL;
dword live = 0;
int i = 0;
dword dwWait;
DPRINTF(("divas: reset Diva Server PRI"));
reg = UxCardMemAttach(card->hw, DIVAS_REG_MEMORY);
UxCardMemOut(card->hw, &reg[DIVAS_RESET_REG], DIVAS_RESET |
DIVAS_LED1 | DIVAS_LED2 | DIVAS_LED3 | DIVAS_LED4);
for (dwWait = 0x000fffff; dwWait; dwWait--)
;
UxCardMemOut(card->hw, &reg[DIVAS_RESET_REG], 0x00);
for (dwWait = 0x000fffff; dwWait; dwWait--)
;
UxCardMemDetach(card->hw, reg);
boot = UxCardMemAttach(card->hw, DIVAS_RAM_MEMORY);
UxCardMemOutD(card->hw, boot->reserved, 0);
live = UxCardMemInD(card->hw, &boot->live);
for (i=0; i<5; i++)
{
if (live != UxCardMemInD(card->hw, &boot->live))
{
break;
}
UxPause(10);
}
if (i == 5)
{
UxCardMemDetach(card->hw, boot);
DPRINTF(("divas: card is reset but CPU not running"));
return -1;
}
UxCardMemDetach(card->hw, boot);
DPRINTF(("divas: card reset after %d ms", i * 10));
return 0;
}
static int diva_server_config(card_t *card, dia_config_t *config)
{
byte *shared;
int i, j;
DPRINTF(("divas: configure Diva Server PRI"));
shared = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
UxCardLog(0);
for (i=0; i<256; i++)
{
UxCardMemOut(card->hw, &shared[i], 0);
}
UxCardMemOut(card->hw, &shared[ 8], config->tei);
UxCardMemOut(card->hw, &shared[ 9], config->nt2);
UxCardMemOut(card->hw, &shared[10], config->sig_flags);
UxCardMemOut(card->hw, &shared[11], config->watchdog);
UxCardMemOut(card->hw, &shared[12], config->permanent);
UxCardMemOut(card->hw, &shared[13], config->x_interface);
UxCardMemOut(card->hw, &shared[14], config->stable_l2);
UxCardMemOut(card->hw, &shared[15], config->no_order_check);
UxCardMemOut(card->hw, &shared[16], config->handset_type);
UxCardMemOut(card->hw, &shared[17], 0);
UxCardMemOut(card->hw, &shared[18], config->low_channel);
UxCardMemOut(card->hw, &shared[19], config->prot_version);
UxCardMemOut(card->hw, &shared[20], config->crc4);
for (i=0; i<2; i++)
{
for (j=0; j<32; j++)
{
UxCardMemOut(card->hw, &shared[32+(i*96)+j],config->terminal[i].oad[j]);
}
for (j=0; j<32; j++)
{
UxCardMemOut(card->hw, &shared[64+(i*96)+j],config->terminal[i].osa[j]);
}
for (j=0; j<32; j++)
{
UxCardMemOut(card->hw, &shared[96+(i*96)+j],config->terminal[i].spid[j]);
}
}
UxCardMemDetach(card->hw, shared);
return 0;
}
static
void diva_server_reset_int(card_t *card)
{
byte *cfg;
cfg = UxCardMemAttach(card->hw, DIVAS_CFG_MEMORY);
UxCardMemOutW(card->hw, &cfg[DIVAS_IRQ_RESET], DIVAS_IRQ_RESET_VAL);
UxCardMemOutW(card->hw, &cfg[DIVAS_IRQ_RESET + 2], 0);
UxCardMemDetach(card->hw, cfg);
return;
}
static int diva_server_test_int(card_t *card)
{
int i;
byte *shared;
byte req_int;
DPRINTF(("divas: test interrupt for Diva Server PRI"));
shared = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
UxCardMemIn(card->hw, &shared[0x3FE]);
UxCardMemOut(card->hw, &shared[0x3FE], 0);
UxCardMemIn(card->hw, &shared[0x3FE]);
UxCardMemDetach(card->hw, shared);
diva_server_reset_int(card);
shared = UxCardMemAttach(card->hw, DIVAS_SHARED_MEMORY);
card->test_int_pend = TEST_INT_DIVAS;
req_int = UxCardMemIn(card->hw, &(((struct pr_ram *)shared)->ReadyInt));
req_int++;
UxCardMemOut(card->hw, &(((struct pr_ram *)shared)->ReadyInt), req_int);
UxCardMemDetach(card->hw, shared);
UxCardLog(0);
for (i = 0; i < 50; i++)
{
if (!card->test_int_pend)
{
break;
}
UxPause(10);
}
if (card->test_int_pend)
{
DPRINTF(("active: timeout waiting for card to interrupt"));
return (-1);
}
return 0;
}
static void print_hdr(unsigned char *code, int offset)
{
unsigned char hdr[80];
int i;
i = 0;
while ((i < (DIM(hdr) -1)) &&
(code[offset + i] != '\0') &&
(code[offset + i] != '\r') &&
(code[offset + i] != '\n'))
{
hdr[i] = code[offset + i];
i++;
}
hdr[i] = '\0';
DPRINTF(("divas: loading %s", hdr));
}
static int diva_server_load(card_t *card, dia_load_t *load)
{
diva_server_boot_t *boot;
int i, offset, length;
dword cmd = 0;
DPRINTF(("divas: loading Diva Server PRI"));
boot = UxCardMemAttach(card->hw, DIVAS_RAM_MEMORY);
switch(load->code_type)
{
case DIA_CPU_CODE:
DPRINTF(("divas: RISC code"));
print_hdr(load->code, 0x80);
UxCardMemOutD(card->hw, &boot->addr, MP_PROTOCOL_ADDR);
break;
case DIA_DSP_CODE:
DPRINTF(("divas: DSP code"));
print_hdr(load->code, 0x0);
UxCardMemOutD(card->hw, &boot->addr,
(MP_DSP_CODE_BASE + (((sizeof(dword) +
(sizeof(t_dsp_download_desc) * DSP_MAX_DOWNLOAD_COUNT))
+ ~ALIGNMENT_MASK_MAESTRA) & ALIGNMENT_MASK_MAESTRA)));
break;
case DIA_TABLE_CODE:
DPRINTF(("divas: TABLE code"));
UxCardMemOutD(card->hw, &boot->addr,
(MP_DSP_CODE_BASE + sizeof(dword)));
break;
case DIA_CONT_CODE:
DPRINTF(("divas: continuation code"));
break;
case DIA_DLOAD_CNT:
DPRINTF(("divas: COUNT code"));
UxCardMemOutD(card->hw, &boot->addr, MP_DSP_CODE_BASE);
break;
default:
DPRINTF(("divas: unknown code type"));
UxCardMemDetach(card->hw, boot);
return -1;
}
UxCardLog(0);
offset = 0;
do
{
length = (load->length - offset >= 400) ? 400 : load->length - offset;
for (i=0; i<length; i++)
{
UxCardMemOut(card->hw, &boot->data[i], load->code[offset+i]);
}
for (i=0; i<length; i++)
{
if (load->code[offset + i] != UxCardMemIn(card->hw, &boot->data[i]))
{
UxCardMemDetach(card->hw, boot);
DPRINTF(("divas: card code block verify failed"));
return -1;
}
}
UxCardMemOutD(card->hw, &boot->len, (length + 3) / 4);
UxCardMemOutD(card->hw, &boot->cmd, DIVAS_LOAD_CMD);
for (i=0; i<50000; i++)
{
cmd = UxCardMemInD(card->hw, &boot->cmd);
if (!cmd)
{
break;
}
/*UxPause(1);*/
}
if (cmd)
{
DPRINTF(("divas: timeout waiting for card to ACK load (offset = %d)", offset));
UxCardMemDetach(card->hw, boot);
return -1;
}
offset += length;
} while (offset < load->length);
UxCardMemDetach(card->hw, boot);
DPRINTF(("divas: DIVA Server card loaded"));
return 0;
}
static int diva_server_start(card_t *card, byte *channels)
{
diva_server_boot_t *boot;
byte *ram;
int i;
dword signature = 0;
DPRINTF(("divas: start Diva Server PRI"));
card->is_live = FALSE;
boot = UxCardMemAttach(card->hw, DIVAS_RAM_MEMORY);
UxCardMemOutD(card->hw, &boot->addr, MP_PROTOCOL_ADDR);
UxCardMemOutD(card->hw, &boot->cmd, DIVAS_START_CMD);
UxCardLog(0);
for (i = 0; i < 300; i++)
{
signature = UxCardMemInD(card->hw, &boot->signature);
if ((signature >> 16) == DIVAS_SIGNATURE)
{
DPRINTF(("divas: started card after %d ms", i * 10));
break;
}
UxPause(10);
}
if ((signature >> 16) != DIVAS_SIGNATURE)
{
UxCardMemDetach(card->hw, boot);
DPRINTF(("divas: timeout waiting for card to run protocol code (sig = 0x%x)", signature));
return -1;
}
card->is_live = TRUE;
ram = (byte *) boot;
ram += DIVAS_SHARED_OFFSET;
*channels = UxCardMemIn(card->hw, &ram[0x3F6]);
card->serial_no = UxCardMemInD(card->hw, &ram[0x3F0]);
UxCardMemDetach(card->hw, boot);
if (diva_server_test_int(card))
{
DPRINTF(("divas: interrupt test failed"));
return -1;
}
DPRINTF(("divas: DIVA Server card started"));
return 0;
}
static
int diva_server_mem_get(card_t *card, mem_block_t *mem_block)
{
byte *a;
byte *card_addr;
word length = 0;
int i;
a = UxCardMemAttach(card->hw, DIVAS_RAM_MEMORY);
card_addr = a;
card_addr += mem_block->addr;
for (i=0; i < sizeof(mem_block->data); i++)
{
mem_block->data[i] = UxCardMemIn(card->hw, card_addr);
card_addr++;
length++;
}
UxCardMemDetach(card->hw, a);
return length;
}
/*
* Initialise PRI specific entry points
*/
int DivasPriInit(card_t *card, dia_card_t *cfg)
{
DPRINTF(("divas: initialise Diva Server PRI"));
if (DivasPRIInitPCI(card, cfg) == -1)
{
return -1;
}
card->card_reset = diva_server_reset;
card->card_load = diva_server_load;
card->card_config = diva_server_config;
card->card_start = diva_server_start;
card->reset_int = diva_server_reset_int;
card->card_mem_get = diva_server_mem_get;
card->xlog_offset = DIVAS_MAINT_OFFSET;
card->out = DivasOut;
card->test_int = DivasTestInt;
card->dpc = DivasDpc;
card->clear_int = DivasClearInt;
card->card_isr = pri_ISR;
card->a.ram_out = mem_out;
card->a.ram_outw = mem_outw;
card->a.ram_out_buffer = mem_out_buffer;
card->a.ram_inc = mem_inc;
card->a.ram_in = mem_in;
card->a.ram_inw = mem_inw;
card->a.ram_in_buffer = mem_in_buffer;
card->a.ram_look_ahead = mem_look_ahead;
return 0;
}
static int pri_ISR (card_t* card)
{
int served = 0;
byte* cfg = UxCardMemAttach(card->hw, DIVAS_CFG_MEMORY);
volatile unsigned long* isr = (unsigned long*)&cfg[DIVAS_IRQ_RESET];
register unsigned long val = *isr;
if (val & 0x80000000) /* our card had caused interrupt ??? */
{
served = 1;
card->int_pend += 1;
DivasDpcSchedule(); /* ISR DPC */
*isr = (unsigned long)~0x03E00000; /* Clear interrupt line */
}
UxCardMemDetach(card->hw, cfg);
return (served != 0);
}
/*
* Environment provided by system and miscellaneous definitions
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.2
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#if !defined(SYS_H)
#define SYS_H
/* abreviations for unsigned types */
typedef int boolean_t;
typedef unsigned char byte;
typedef unsigned long dword;
typedef unsigned short word;
/* abreviations for volatile types */
typedef volatile byte vbyte;
typedef volatile word vword;
typedef volatile dword vdword;
/* Booleans */
#if !defined(TRUE)
#define TRUE (1)
#define FALSE (0)
#endif
/* NULL pointer */
#if !defined(NULL)
#define NULL ((void *) 0)
#endif
/* Return the dimension of an array */
#if !defined(DIM)
#define DIM(array) (sizeof (array)/sizeof ((array)[0]))
#endif
/*
* Return the number of milliseconds since last boot
*/
extern dword UxTimeGet(void);
extern void DivasSprintf(char *buffer, char *format, ...);
extern void DivasPrintf(char *format, ...);
/* fatal errors, asserts and tracing */
void HwFatalErrorFrom(char *file, int line);
void HwFatalError(void);
/* void HwAssert(char *file, int line, char *condition); */
#include <linux/kernel.h>
#include <linux/string.h>
#define _PRINTK printk
#define _PRINTF DivasPrintf
void _PRINTF(char *format, ...);
#define PRINTF(arg_list) _PRINTF arg_list
#if defined DTRACE
# define DPRINTF(arg_list) _PRINTF arg_list
# define KDPRINTF(arg_list) _PRINTF arg_list ; _PRINTK arg_list ; _PRINTK("\n");
#else
# define DPRINTF(arg_list) (void)0
# define KDPRINTF(arg_list) _PRINTK arg_list ; _PRINTK("\n");
#endif
#if !defined(ASSERT)
#if defined DEBUG || defined DBG
# define HwFatalError() HwFatalErrorFrom(__FILE__, __LINE__)
# define ASSERT(cond) \
if (!(cond)) \
{ \
/* HwAssert(__FILE__, __LINE__, #cond);*/ \
}
#else
# define ASSERT(cond) ((void)0)
#endif
#endif /* !defined(ASSERT) */
#define TRACE (_PRINTF(__FILE__"@%d\n", __LINE__))
#endif /* SYS_H */
/*
* Interface to Unix specific code for performing card I/O
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.6
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#if !defined(UXIO_H)
#define UXIO_H
#include "sys.h"
#include "adapter.h"
struct pt_regs;
/* user callback, returns zero if interrupt was from this card */
typedef void isr_fn_t(void *);
struct ux_diva_card_s
{
word in_use;
int io_base;
int reset_base;
int card_type;
byte *mapped;
struct pci_dev *pdev;
int slot;
int irq;
byte *pDRAM;
byte *pDEVICES;
byte *pCONFIG;
byte *pSHARED;
byte *pCONTROL;
word features;
void *user_isr_arg;
isr_fn_t *user_isr;
};
/*
* Get a card handle to enable card to be accessed
*/
int UxCardHandleGet( ux_diva_card_t **card,
dia_card_t *cfg);
/*
* Free a card handle as no longer needed
*/
void UxCardHandleFree(ux_diva_card_t *card);
/*
* Lock and unlock access to a card
*/
long UxCardLock(ux_diva_card_t *card);
void UxCardUnlock(ux_diva_card_t *card, unsigned long ipl);
/*
* Set the mapping address for PCI cards
*/
int UxCardAddrMappingSet(ux_diva_card_t *card,
int id,
void *address,
int size);
/*
* Attach card to memory to enable it to be accessed
* Returns the mapped address
*/
void *UxCardMemAttach(ux_diva_card_t *card, int id);
/*
* map card out of memory after completion of access
*/
void UxCardMemDetach(ux_diva_card_t *card, void *address);
/*
* input functions for memory-mapped cards
*/
byte UxCardMemIn(ux_diva_card_t *card, void *address);
word UxCardMemInW(ux_diva_card_t *card, void *address);
dword UxCardMemInD(ux_diva_card_t *card, void *address);
void UxCardMemInBuffer( ux_diva_card_t *card,
void *address,
void *buffer,
int length);
/*
* output functions for memory-mapped cards
*/
void UxCardMemOut(ux_diva_card_t *card, void *address, byte data);
void UxCardMemOutW(ux_diva_card_t *card, void *address, word data);
void UxCardMemOutD(ux_diva_card_t *card, void *address, dword data);
void UxCardMemOutBuffer( ux_diva_card_t *card,
void *address,
void *buffer,
int length);
/*
* input functions for I/O-mapped cards
*/
byte UxCardIoIn(ux_diva_card_t *card, void *, void *address);
word UxCardIoInW(ux_diva_card_t *card, void *, void *address);
dword UxCardIoInD(ux_diva_card_t *card, void *, void *address);
void UxCardIoInBuffer( ux_diva_card_t *card,
void *, void *address,
void *buffer,
int length);
/*
* output functions for I/O-mapped cards
*/
void UxCardIoOut(ux_diva_card_t *card, void *, void *address, byte data);
void UxCardIoOutW(ux_diva_card_t *card, void *, void *address, word data);
void UxCardIoOutD(ux_diva_card_t *card, void *, void *address, dword data);
void UxCardIoOutBuffer( ux_diva_card_t *card,
void *, void *address,
void *buffer,
int length);
/*
* Get specified PCI config
*/
void UxPciConfigRead(ux_diva_card_t *card,
int size,
int offset,
void *value);
/*
* Set specified PCI config
*/
void UxPciConfigWrite(ux_diva_card_t *card,
int size,
int offset,
void *value);
/* allocate memory, returning NULL if none available */
void *UxAlloc(unsigned int size);
void UxFree(void *);
/*
* Pause for specified number of milli-seconds
*/
void UxPause(long ms);
/*
* Install an ISR for the specified card
*/
int UxIsrInstall(ux_diva_card_t *card, isr_fn_t *isr_fn, void *isr_arg);
/*
* Remove an ISR for the specified card
*/
void UxIsrRemove(ux_diva_card_t *card, void *);
/*
* DEBUG function to turn logging ON or OFF
*/
void UxCardLog(int turn_on);
long UxInterlockedIncrement(ux_diva_card_t *card, long *dst);
long UxInterlockedDecrement(ux_diva_card_t *card, long *dst);
#endif /* of UXIO_H */
/*
* Unix Eicon active card driver
* XLOG related functions
*
* Copyright (C) Eicon Technology Corporation, 2000.
*
* Eicon File Revision : 1.2
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "sys.h"
#include "idi.h"
#include "pc.h"
#include "pc_maint.h"
#include "divalog.h"
#include "adapter.h"
#include "uxio.h"
/*
* convert/copy XLOG info into a KLOG entry
*/
static
void xlog_to_klog(byte *b, int size, int card_num)
{
typedef struct
{
word code;
word time_hi;
word time_lo;
word xcode;
byte data[2];
} card_xlog_t;
card_xlog_t *x;
klog_t klog;
x = (card_xlog_t *) b;
memset(&klog, 0, sizeof(klog));
klog.time_stamp = (dword) x->time_hi;
klog.time_stamp = (klog.time_stamp << 16) | (dword) x->time_lo;
klog.length = size > sizeof(klog.buffer) ? sizeof(klog.buffer) : size;
klog.card = card_num;
if (x->code == 1)
{
klog.type = KLOG_XTXT_MSG;
klog.code = 0;
memcpy(klog.buffer, &x->xcode, klog.length);
}
else if (x->code == 2)
{
klog.type = KLOG_XLOG_MSG;
klog.code = x->xcode;
memcpy(klog.buffer, &x->data, klog.length);
}
else
{
char *c; int i;
klog.type = KLOG_TEXT_MSG;
klog.code = 0;
c = "divas: invalid xlog message code from card";
i = 0;
while (*c)
{
klog.buffer[i] = *c;
c++;
i++;
}
klog.buffer[i] = *c;
}
/* send to the log driver and return */
DivasLogAdd(&klog, sizeof(klog));
return;
}
/*
* send an XLOG request down to specified card
* if response available from previous request then read it
* if not then just send down new request, ready for next time
*/
void DivasXlogReq(int card_num)
{
card_t *card;
ADAPTER *a;
if ((card_num < 0) || (card_num > DivasCardNext))
{
DPRINTF(("xlog: invalid card number"));
return;
}
card = &DivasCards[card_num];
if (DivasXlogRetrieve(card))
{
return;
}
/* send down request for next time */
a = &card->a;
a->ram_out(a, (word *) (card->xlog_offset + 1), 0);
a->ram_out(a, (word *) (dword) (card->xlog_offset), DO_LOG);
return;
}
/*
* retrieve XLOG request from specified card
* returns non-zero if new request sent to card
*/
int DivasXlogRetrieve(card_t *card)
{
ADAPTER *a;
struct mi_pc_maint pcm;
a = &card->a;
/* get status of last request */
pcm.rc = a->ram_in(a, (word *)(card->xlog_offset + 1));
/* if nothing there from previous request, send down a new one */
if (pcm.rc == OK)
{
/* read in response */
a->ram_in_buffer(a, (word *) (dword) card->xlog_offset, &pcm, sizeof(pcm));
xlog_to_klog((byte *) &pcm.data, sizeof(pcm.data),
(int) (card - DivasCards));
}
/* if any response received from card, re-send request */
if (pcm.rc)
{
a->ram_out(a, (word *) (card->xlog_offset + 1), 0);
a->ram_out(a, (word *) (dword) (card->xlog_offset), DO_LOG);
return 1;
}
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment