959 lines
24 KiB
C
959 lines
24 KiB
C
/* $Id$
|
|
*
|
|
* sedl_fax.c low level stuff for Sedlbauer Speedfax + cards
|
|
*
|
|
* Copyright (C) 2000,2001 Karsten Keil (kkeil@suse.de)
|
|
*
|
|
* Author Karsten Keil (kkeil@suse.de)
|
|
*
|
|
*
|
|
* Thanks to Sedlbauer AG for informations
|
|
* Marcus Niemann
|
|
* Edgar Toernig
|
|
*
|
|
* This file is (c) under GNU PUBLIC LICENSE
|
|
*
|
|
*/
|
|
|
|
/* Supported cards:
|
|
* Card: Chip: Configuration: Comment:
|
|
* ---------------------------------------------------------------------
|
|
* Speed Fax+ ISAC_ISAR ISAPNP Full analog support
|
|
* Speed Fax+ ISAC_ISAR PCI PNP Full analog support
|
|
*
|
|
* Important:
|
|
* For the sedlbauer speed fax+ to work properly you have to download
|
|
* the firmware onto the card.
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/module.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/delay.h>
|
|
#include <asm/semaphore.h>
|
|
#include "mISDN_dch.h"
|
|
#include "mISDN_bch.h"
|
|
#include "isac.h"
|
|
#include "isar.h"
|
|
#include "mISDNl1.h"
|
|
#include "helper.h"
|
|
#include "debug.h"
|
|
|
|
#define SPIN_DEBUG
|
|
#define LOCK_STATISTIC
|
|
#include "hw_lock.h"
|
|
|
|
extern const char *CardType[];
|
|
|
|
const char *Sedlfax_revision = "$Revision$";
|
|
|
|
const char *Sedlbauer_Types[] =
|
|
{"None", "speed fax+", "speed fax+ pyramid", "speed fax+ pci"};
|
|
|
|
#ifndef PCI_VENDOR_ID_TIGERJET
|
|
#define PCI_VENDOR_ID_TIGERJET 0xe159
|
|
#endif
|
|
#ifndef PCI_DEVICE_ID_TIGERJET_100
|
|
#define PCI_DEVICE_ID_TIGERJET_100 0x0002
|
|
#endif
|
|
#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51
|
|
#define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54
|
|
#define PCI_SUB_ID_SEDLBAUER 0x01
|
|
|
|
#define SEDL_SPEEDFAX_ISA 1
|
|
#define SEDL_SPEEDFAX_PYRAMID 2
|
|
#define SEDL_SPEEDFAX_PCI 3
|
|
|
|
#define byteout(addr,val) outb(val,addr)
|
|
#define bytein(addr) inb(addr)
|
|
|
|
#define SEDL_ISA_ISAC 4
|
|
#define SEDL_ISA_ISAR 6
|
|
#define SEDL_ISA_ADR 8
|
|
#define SEDL_ISA_RESET_ON 10
|
|
#define SEDL_ISA_RESET_OFF 12
|
|
|
|
#define SEDL_PCI_ADR 0xc8
|
|
#define SEDL_PCI_ISAC 0xd0
|
|
#define SEDL_PCI_ISAR 0xe0
|
|
|
|
/* TIGER 100 Registers */
|
|
|
|
#define TIGER_RESET_ADDR 0x00
|
|
#define TIGER_EXTERN_RESET_ON 0x01
|
|
#define TIGER_EXTERN_RESET_OFF 0x00
|
|
#define TIGER_AUX_CTRL 0x02
|
|
#define TIGER_AUX_DATA 0x03
|
|
#define TIGER_AUX_IRQMASK 0x05
|
|
#define TIGER_AUX_STATUS 0x07
|
|
|
|
/* Tiger AUX BITs */
|
|
#define SEDL_AUX_IOMASK 0xdd /* 1 and 5 are inputs */
|
|
#define SEDL_ISAR_RESET_BIT_OFF 0x00
|
|
#define SEDL_ISAR_RESET_BIT_ON 0x01
|
|
#define SEDL_TIGER_IRQ_BIT 0x02
|
|
#define SEDL_ISAR_PCI_LED1_BIT 0x08
|
|
#define SEDL_ISAR_PCI_LED2_BIT 0x10
|
|
|
|
#define SEDL_PCI_RESET_ON (SEDL_ISAR_RESET_BIT_ON)
|
|
#define SEDL_PCI_RESET_OFF (SEDL_ISAR_PCI_LED1_BIT | SEDL_ISAR_PCI_LED2_BIT)
|
|
|
|
|
|
#define SEDL_RESET 0x3 /* same as DOS driver */
|
|
|
|
/* data struct */
|
|
|
|
typedef struct _sedl_fax {
|
|
struct _sedl_fax *prev;
|
|
struct _sedl_fax *next;
|
|
u_char subtyp;
|
|
u_int irq;
|
|
u_int cfg;
|
|
u_int addr;
|
|
u_int isac;
|
|
u_int isar;
|
|
mISDN_HWlock_t lock;
|
|
isar_reg_t ir;
|
|
isac_chip_t isac_hw;
|
|
isar_hw_t isar_hw[2];
|
|
dchannel_t dch;
|
|
bchannel_t bch[2];
|
|
} sedl_fax;
|
|
|
|
static int lock_dev(void *data, int nowait)
|
|
{
|
|
register mISDN_HWlock_t *lock = &((sedl_fax *)data)->lock;
|
|
|
|
return(lock_HW(lock, nowait));
|
|
}
|
|
|
|
static void unlock_dev(void *data)
|
|
{
|
|
register mISDN_HWlock_t *lock = &((sedl_fax *)data)->lock;
|
|
|
|
unlock_HW(lock);
|
|
}
|
|
|
|
static inline u_char
|
|
readreg(unsigned int ale, unsigned int adr, u_char off)
|
|
{
|
|
byteout(ale, off);
|
|
return (bytein(adr));
|
|
}
|
|
|
|
static inline void
|
|
readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
|
|
{
|
|
byteout(ale, off);
|
|
insb(adr, data, size);
|
|
}
|
|
|
|
|
|
static inline void
|
|
writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
|
|
{
|
|
byteout(ale, off);
|
|
byteout(adr, data);
|
|
}
|
|
|
|
static inline void
|
|
writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
|
|
{
|
|
byteout(ale, off);
|
|
outsb(adr, data, size);
|
|
}
|
|
|
|
/* Interface functions */
|
|
|
|
static u_char
|
|
ReadISAC(void *p, u_char offset)
|
|
{
|
|
return (readreg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, offset));
|
|
}
|
|
|
|
static void
|
|
WriteISAC(void *p, u_char offset, u_char value)
|
|
{
|
|
writereg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, offset, value);
|
|
}
|
|
|
|
static void
|
|
ReadISACfifo(void *p, u_char * data, int size)
|
|
{
|
|
readfifo(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, 0, data, size);
|
|
}
|
|
|
|
static void
|
|
WriteISACfifo(void *p, u_char * data, int size)
|
|
{
|
|
writefifo(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isac, 0, data, size);
|
|
}
|
|
|
|
/* ISAR access routines
|
|
* mode = 0 access with IRQ on
|
|
* mode = 1 access with IRQ off
|
|
* mode = 2 access with IRQ off and using last offset
|
|
*/
|
|
|
|
static u_char
|
|
ReadISAR(void *p, int mode, u_char offset)
|
|
{
|
|
if (mode == 0)
|
|
return (readreg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset));
|
|
else if (mode == 1)
|
|
byteout(((sedl_fax *)p)->addr, offset);
|
|
return(bytein(((sedl_fax *)p)->isar));
|
|
}
|
|
|
|
static void
|
|
WriteISAR(void *p, int mode, u_char offset, u_char value)
|
|
{
|
|
if (mode == 0)
|
|
writereg(((sedl_fax *)p)->addr, ((sedl_fax *)p)->isar, offset, value);
|
|
else {
|
|
if (mode == 1)
|
|
byteout(((sedl_fax *)p)->addr, offset);
|
|
byteout(((sedl_fax *)p)->isar, value);
|
|
}
|
|
}
|
|
|
|
inline void
|
|
do_sedl_interrupt(sedl_fax *sf)
|
|
{
|
|
u_char val;
|
|
int cnt = 8;
|
|
|
|
val = readreg(sf->addr, sf->isar, ISAR_IRQBIT);
|
|
Start_ISAR:
|
|
if (val & ISAR_IRQSTA)
|
|
isar_int_main(&sf->bch[0]);
|
|
val = readreg(sf->addr, sf->isac, ISAC_ISTA);
|
|
Start_ISAC:
|
|
if (val)
|
|
ISAC_interrupt(&sf->dch, val);
|
|
val = readreg(sf->addr, sf->isar, ISAR_IRQBIT);
|
|
if ((val & ISAR_IRQSTA) && cnt) {
|
|
cnt--;
|
|
if (sf->dch.debug & L1_DEB_HSCX)
|
|
printk(KERN_DEBUG "ISAR IntStat after IntRoutine cpu%d\n",
|
|
smp_processor_id());
|
|
goto Start_ISAR;
|
|
}
|
|
val = readreg(sf->addr, sf->isac, ISAC_ISTA);
|
|
if (val && cnt) {
|
|
cnt--;
|
|
if (sf->dch.debug & L1_DEB_ISAC)
|
|
printk(KERN_DEBUG "ISAC IntStat after IntRoutine cpu%d\n",
|
|
smp_processor_id());
|
|
goto Start_ISAC;
|
|
}
|
|
if (!cnt)
|
|
if (sf->dch.debug & L1_DEB_ISAC)
|
|
printk(KERN_DEBUG "Sedlbauer IRQ LOOP\n");
|
|
writereg(sf->addr, sf->isar, ISAR_IRQBIT, 0);
|
|
writereg(sf->addr, sf->isac, ISAC_MASK, 0xFF);
|
|
writereg(sf->addr, sf->isac, ISAC_MASK, 0x0);
|
|
writereg(sf->addr, sf->isar, ISAR_IRQBIT, ISAR_IRQMSK);
|
|
}
|
|
|
|
static void
|
|
speedfax_isa_interrupt(int intno, void *dev_id, struct pt_regs *regs)
|
|
{
|
|
sedl_fax *sf = dev_id;
|
|
u_long flags;
|
|
|
|
spin_lock_irqsave(&sf->lock.lock, flags);
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.spin_adr = (void *)0x2001;
|
|
#endif
|
|
if (test_and_set_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
|
|
printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%x\n",
|
|
__FUNCTION__, sf->lock.state);
|
|
#ifdef SPIN_DEBUG
|
|
printk(KERN_ERR "%s: previous lock:%p\n",
|
|
__FUNCTION__, sf->lock.busy_adr);
|
|
#endif
|
|
#ifdef LOCK_STATISTIC
|
|
sf->lock.irq_fail++;
|
|
#endif
|
|
} else {
|
|
#ifdef LOCK_STATISTIC
|
|
sf->lock.irq_ok++;
|
|
#endif
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.busy_adr = speedfax_isa_interrupt;
|
|
#endif
|
|
}
|
|
|
|
test_and_set_bit(STATE_FLAG_INIRQ, &sf->lock.state);
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.spin_adr = NULL;
|
|
#endif
|
|
spin_unlock_irqrestore(&sf->lock.lock, flags);
|
|
do_sedl_interrupt(sf);
|
|
spin_lock_irqsave(&sf->lock.lock, flags);
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.spin_adr = (void *)0x2002;
|
|
#endif
|
|
if (!test_and_clear_bit(STATE_FLAG_INIRQ, &sf->lock.state)) {
|
|
}
|
|
if (!test_and_clear_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
|
|
printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%x)\n",
|
|
__FUNCTION__, sf->lock.state);
|
|
}
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.busy_adr = NULL;
|
|
sf->lock.spin_adr = NULL;
|
|
#endif
|
|
spin_unlock_irqrestore(&sf->lock.lock, flags);
|
|
}
|
|
|
|
static void
|
|
speedfax_pci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
|
|
{
|
|
sedl_fax *sf = dev_id;
|
|
u_long flags;
|
|
u_char val;
|
|
|
|
spin_lock_irqsave(&sf->lock.lock, flags);
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.spin_adr = (void *)0x3001;
|
|
#endif
|
|
val = bytein(sf->cfg + TIGER_AUX_STATUS);
|
|
if (val & SEDL_TIGER_IRQ_BIT) { /* for us or shared ? */
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.spin_adr = NULL;
|
|
#endif
|
|
spin_unlock_irqrestore(&sf->lock.lock, flags);
|
|
return; /* shared */
|
|
}
|
|
if (test_and_set_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
|
|
printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%x\n",
|
|
__FUNCTION__, sf->lock.state);
|
|
#ifdef SPIN_DEBUG
|
|
printk(KERN_ERR "%s: previous lock:%p\n",
|
|
__FUNCTION__, sf->lock.busy_adr);
|
|
#endif
|
|
#ifdef LOCK_STATISTIC
|
|
sf->lock.irq_fail++;
|
|
#endif
|
|
} else {
|
|
#ifdef LOCK_STATISTIC
|
|
sf->lock.irq_ok++;
|
|
#endif
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.busy_adr = speedfax_pci_interrupt;
|
|
#endif
|
|
}
|
|
|
|
test_and_set_bit(STATE_FLAG_INIRQ, &sf->lock.state);
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.spin_adr= NULL;
|
|
#endif
|
|
spin_unlock_irqrestore(&sf->lock.lock, flags);
|
|
do_sedl_interrupt(sf);
|
|
spin_lock_irqsave(&sf->lock.lock, flags);
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.spin_adr = (void *)0x3002;
|
|
#endif
|
|
if (!test_and_clear_bit(STATE_FLAG_INIRQ, &sf->lock.state)) {
|
|
}
|
|
if (!test_and_clear_bit(STATE_FLAG_BUSY, &sf->lock.state)) {
|
|
printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%x)\n",
|
|
__FUNCTION__, sf->lock.state);
|
|
}
|
|
#ifdef SPIN_DEBUG
|
|
sf->lock.busy_adr = NULL;
|
|
sf->lock.spin_adr = NULL;
|
|
#endif
|
|
spin_unlock_irqrestore(&sf->lock.lock, flags);
|
|
}
|
|
|
|
void
|
|
release_sedlbauer(sedl_fax *sf)
|
|
{
|
|
int bytecnt = 256;
|
|
|
|
if (sf->subtyp == SEDL_SPEEDFAX_ISA)
|
|
bytecnt = 16;
|
|
else
|
|
byteout(sf->cfg + TIGER_AUX_IRQMASK, 0);
|
|
if (sf->cfg)
|
|
release_region(sf->cfg, bytecnt);
|
|
}
|
|
|
|
static void
|
|
reset_speedfax(sedl_fax *sf)
|
|
{
|
|
|
|
printk(KERN_INFO "Sedlbauer: resetting card\n");
|
|
|
|
if (sf->subtyp == SEDL_SPEEDFAX_ISA) {
|
|
byteout(sf->cfg + SEDL_ISA_RESET_ON, SEDL_RESET);
|
|
mdelay(1);
|
|
byteout(sf->cfg + SEDL_ISA_RESET_OFF, 0);
|
|
mdelay(1);
|
|
} else {
|
|
byteout(sf->cfg + TIGER_RESET_ADDR, TIGER_EXTERN_RESET_ON);
|
|
byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_ON);
|
|
mdelay(1);
|
|
byteout(sf->cfg + TIGER_RESET_ADDR, TIGER_EXTERN_RESET_OFF);
|
|
byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_OFF);
|
|
mdelay(1);
|
|
}
|
|
}
|
|
|
|
static int init_card(sedl_fax *sf)
|
|
{
|
|
int irq_cnt, cnt = 3;
|
|
u_int shared = SA_SHIRQ;
|
|
void *irq_func = speedfax_pci_interrupt;
|
|
|
|
if (sf->subtyp == SEDL_SPEEDFAX_ISA) {
|
|
irq_func = speedfax_isa_interrupt;
|
|
shared = 0;
|
|
}
|
|
irq_cnt = kstat_irqs(sf->irq);
|
|
printk(KERN_INFO "%s: IRQ %d count %d cpu%d\n",
|
|
sf->dch.inst.name, sf->irq, irq_cnt, smp_processor_id());
|
|
lock_dev(sf, 0);
|
|
if (request_irq(sf->irq, irq_func, shared, "speedfax", sf)) {
|
|
printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n",
|
|
sf->irq);
|
|
unlock_dev(sf);
|
|
return(-EIO);
|
|
}
|
|
while (cnt) {
|
|
int ret;
|
|
|
|
ISAC_clear_pending_ints(&sf->dch);
|
|
if ((ret=ISAC_init(&sf->dch))) {
|
|
printk(KERN_WARNING "mISDN: ISAC_init failed with %d\n", ret);
|
|
break;
|
|
}
|
|
init_isar(&sf->bch[0]);
|
|
init_isar(&sf->bch[1]);
|
|
if (sf->subtyp != SEDL_SPEEDFAX_ISA)
|
|
byteout(sf->cfg + TIGER_AUX_IRQMASK, SEDL_TIGER_IRQ_BIT);
|
|
WriteISAC(sf, ISAC_MASK, 0);
|
|
WriteISAR(sf, 0, ISAR_IRQBIT, ISAR_IRQMSK);
|
|
/* RESET Receiver and Transmitter */
|
|
WriteISAC(sf, ISAC_CMDR, 0x41);
|
|
unlock_dev(sf);
|
|
current->state = TASK_UNINTERRUPTIBLE;
|
|
/* Timeout 10ms */
|
|
schedule_timeout((10*HZ)/1000);
|
|
printk(KERN_INFO "%s: IRQ %d count %d cpu%d\n",
|
|
sf->dch.inst.name, sf->irq, kstat_irqs(sf->irq), smp_processor_id());
|
|
if (kstat_irqs(sf->irq) == irq_cnt) {
|
|
printk(KERN_WARNING
|
|
"Sedlbauer speedfax: IRQ(%d) getting no interrupts during init %d\n",
|
|
sf->irq, 4 - cnt);
|
|
if (cnt == 1) {
|
|
return (-EIO);
|
|
} else {
|
|
lock_dev(sf, 0);
|
|
reset_speedfax(sf);
|
|
cnt--;
|
|
}
|
|
} else {
|
|
return(0);
|
|
}
|
|
}
|
|
unlock_dev(sf);
|
|
return(-EIO);
|
|
}
|
|
|
|
|
|
#define MAX_CARDS 4
|
|
#define MODULE_PARM_T "1-4i"
|
|
static int sedl_cnt;
|
|
static mISDNobject_t speedfax;
|
|
static int debug;
|
|
static u_int protocol[MAX_CARDS];
|
|
static u_int io[MAX_CARDS];
|
|
static u_int irq[MAX_CARDS];
|
|
static int layermask[MAX_CARDS];
|
|
static int cfg_idx;
|
|
|
|
#ifdef MODULE
|
|
MODULE_AUTHOR("Karsten Keil");
|
|
#ifdef MODULE_LICENSE
|
|
MODULE_LICENSE("GPL");
|
|
#endif
|
|
MODULE_PARM(debug, "1i");
|
|
MODULE_PARM(io, MODULE_PARM_T);
|
|
MODULE_PARM(protocol, MODULE_PARM_T);
|
|
MODULE_PARM(irq, MODULE_PARM_T);
|
|
MODULE_PARM(layermask, MODULE_PARM_T);
|
|
#define Speedfax_init init_module
|
|
#endif
|
|
|
|
static char SpeedfaxName[] = "Speedfax";
|
|
|
|
static struct pci_dev *dev_sedl;
|
|
static int pci_finished_lookup;
|
|
|
|
int
|
|
setup_speedfax(sedl_fax *sf, u_int io_cfg, u_int irq_cfg)
|
|
{
|
|
int bytecnt, ver;
|
|
char tmp[64];
|
|
u16 sub_vendor_id, sub_id;
|
|
|
|
strcpy(tmp, Sedlfax_revision);
|
|
printk(KERN_INFO "mISDN: Sedlbauer speedfax driver Rev. %s\n", mISDN_getrev(tmp));
|
|
|
|
sf->subtyp = 0;
|
|
bytecnt = 16;
|
|
/* Probe for Sedlbauer speedfax pci */
|
|
#if CONFIG_PCI
|
|
if (pci_present() && !pci_finished_lookup) {
|
|
while ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET,
|
|
PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) {
|
|
sf->irq = dev_sedl->irq;
|
|
sf->cfg = pci_resource_start_io(dev_sedl, 0);
|
|
pci_get_sub_vendor(dev_sedl,sub_vendor_id);
|
|
pci_get_sub_system(dev_sedl,sub_id);
|
|
printk(KERN_INFO "Sedlbauer: PCI subvendor:%x subid %x\n",
|
|
sub_vendor_id, sub_id);
|
|
printk(KERN_INFO "Sedlbauer: PCI base adr %#x\n",
|
|
sf->cfg);
|
|
if (sub_id != PCI_SUB_ID_SEDLBAUER) {
|
|
printk(KERN_WARNING "Sedlbauer: unknown sub id %#x\n", sub_id);
|
|
continue;
|
|
}
|
|
if (!sf->irq) {
|
|
printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n");
|
|
continue;
|
|
}
|
|
if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PYRAMID) {
|
|
sf->subtyp = SEDL_SPEEDFAX_PYRAMID;
|
|
} else if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PCI) {
|
|
sf->subtyp = SEDL_SPEEDFAX_PCI;
|
|
} else {
|
|
printk(KERN_WARNING "Sedlbauer: unknown sub vendor id %#x\n",
|
|
sub_vendor_id);
|
|
continue;
|
|
}
|
|
if (pci_enable_device(dev_sedl))
|
|
continue;
|
|
bytecnt = 256;
|
|
break;
|
|
}
|
|
if (!dev_sedl) {
|
|
pci_finished_lookup = 1;
|
|
printk(KERN_INFO "Sedlbauer: No more PCI cards found\n");
|
|
}
|
|
}
|
|
#else
|
|
printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n");
|
|
#endif /* CONFIG_PCI */
|
|
if (!sf->subtyp) { /* OK no PCI found now check for an ISA card */
|
|
if ((!io_cfg) || (!irq_cfg)) {
|
|
if (!sedl_cnt)
|
|
printk(KERN_WARNING
|
|
"Sedlbauer: No io/irq for ISA card\n");
|
|
return(1);
|
|
}
|
|
sf->cfg = io_cfg;
|
|
sf->irq = irq_cfg;
|
|
sf->subtyp = SEDL_SPEEDFAX_ISA;
|
|
bytecnt = 16;
|
|
}
|
|
|
|
if (check_region(sf->cfg, bytecnt)) {
|
|
printk(KERN_WARNING
|
|
"mISDN: %s config port %x-%x already in use\n",
|
|
Sedlbauer_Types[sf->subtyp],
|
|
sf->cfg,
|
|
sf->cfg + bytecnt);
|
|
return (1);
|
|
} else {
|
|
request_region(sf->cfg, bytecnt, "sedlbauer speedfax+");
|
|
}
|
|
|
|
printk(KERN_INFO
|
|
"Sedlbauer: defined at 0x%x-0x%x IRQ %d\n",
|
|
sf->cfg,
|
|
sf->cfg + bytecnt,
|
|
sf->irq);
|
|
|
|
printk(KERN_INFO "Sedlbauer: %s detected\n",
|
|
Sedlbauer_Types[sf->subtyp]);
|
|
|
|
sf->dch.read_reg = &ReadISAC;
|
|
sf->dch.write_reg = &WriteISAC;
|
|
sf->dch.read_fifo = &ReadISACfifo;
|
|
sf->dch.write_fifo = &WriteISACfifo;
|
|
sf->dch.hw = &sf->isac_hw;
|
|
if (sf->subtyp != SEDL_SPEEDFAX_ISA) {
|
|
sf->addr = sf->cfg + SEDL_PCI_ADR;
|
|
sf->isac = sf->cfg + SEDL_PCI_ISAC;
|
|
sf->isar = sf->cfg + SEDL_PCI_ISAR;
|
|
byteout(sf->cfg + TIGER_RESET_ADDR, 0xff);
|
|
mdelay(1);
|
|
byteout(sf->cfg + TIGER_RESET_ADDR, 0x00);
|
|
mdelay(1);
|
|
byteout(sf->cfg + TIGER_AUX_CTRL, SEDL_AUX_IOMASK);
|
|
byteout(sf->cfg + TIGER_AUX_IRQMASK, 0);
|
|
byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_ON);
|
|
mdelay(1);
|
|
byteout(sf->cfg + TIGER_AUX_DATA, SEDL_PCI_RESET_OFF);
|
|
mdelay(1);
|
|
} else {
|
|
sf->addr = sf->cfg + SEDL_ISA_ADR;
|
|
sf->isac = sf->cfg + SEDL_ISA_ISAC;
|
|
sf->isar = sf->cfg + SEDL_ISA_ISAR;
|
|
}
|
|
sf->isar_hw[0].reg = &sf->ir;
|
|
sf->isar_hw[1].reg = &sf->ir;
|
|
sf->bch[0].hw = &sf->isar_hw[0];
|
|
sf->bch[1].hw = &sf->isar_hw[1];
|
|
sf->bch[0].Read_Reg = &ReadISAR;
|
|
sf->bch[0].Write_Reg = &WriteISAR;
|
|
sf->bch[1].Read_Reg = &ReadISAR;
|
|
sf->bch[1].Write_Reg = &WriteISAR;
|
|
lock_dev(sf, 0);
|
|
#ifdef SPIN_DEBUG
|
|
printk(KERN_ERR "spin_lock_adr=%p now(%p)\n", &sf->lock.spin_adr, sf->lock.spin_adr);
|
|
printk(KERN_ERR "busy_lock_adr=%p now(%p)\n", &sf->lock.busy_adr, sf->lock.busy_adr);
|
|
#endif
|
|
writereg(sf->addr, sf->isar, ISAR_IRQBIT, 0);
|
|
writereg(sf->addr, sf->isac, ISAC_MASK, 0xFF);
|
|
ver = ISARVersion(&sf->bch[0], "Sedlbauer:");
|
|
unlock_dev(sf);
|
|
if (ver < 0) {
|
|
printk(KERN_WARNING
|
|
"Sedlbauer: wrong ISAR version (ret = %d)\n", ver);
|
|
release_sedlbauer(sf);
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
release_card(sedl_fax *card) {
|
|
|
|
#ifdef LOCK_STATISTIC
|
|
printk(KERN_INFO "try_ok(%d) try_wait(%d) try_mult(%d) try_inirq(%d)\n",
|
|
card->lock.try_ok, card->lock.try_wait, card->lock.try_mult, card->lock.try_inirq);
|
|
printk(KERN_INFO "irq_ok(%d) irq_fail(%d)\n",
|
|
card->lock.irq_ok, card->lock.irq_fail);
|
|
#endif
|
|
lock_dev(card, 0);
|
|
free_irq(card->irq, card);
|
|
free_isar(&card->bch[1]);
|
|
free_isar(&card->bch[0]);
|
|
ISAC_free(&card->dch);
|
|
WriteISAR(card, 0, ISAR_IRQBIT, 0);
|
|
WriteISAC(card, ISAC_MASK, 0xFF);
|
|
reset_speedfax(card);
|
|
WriteISAR(card, 0, ISAR_IRQBIT, 0);
|
|
WriteISAC(card, ISAC_MASK, 0xFF);
|
|
release_sedlbauer(card);
|
|
free_bchannel(&card->bch[1]);
|
|
free_bchannel(&card->bch[0]);
|
|
free_dchannel(&card->dch);
|
|
REMOVE_FROM_LISTBASE(card, ((sedl_fax *)speedfax.ilist));
|
|
unlock_dev(card);
|
|
kfree(card);
|
|
sedl_cnt--;
|
|
speedfax.refcnt--;
|
|
}
|
|
|
|
static int
|
|
speedfax_manager(void *data, u_int prim, void *arg) {
|
|
sedl_fax *card = speedfax.ilist;
|
|
mISDNinstance_t *inst=data;
|
|
int channel = -1;
|
|
struct sk_buff *skb;
|
|
|
|
printk(KERN_DEBUG "%s: data:%p prim:%x arg:%p\n",
|
|
__FUNCTION__, data, prim, arg);
|
|
if (!data) {
|
|
printk(KERN_ERR "speedfax_manager no data prim %x arg %p\n",
|
|
prim, arg);
|
|
return(-EINVAL);
|
|
}
|
|
while(card) {
|
|
if (&card->dch.inst == inst) {
|
|
channel = 2;
|
|
break;
|
|
}
|
|
if (&card->bch[0].inst == inst) {
|
|
channel = 0;
|
|
break;
|
|
}
|
|
if (&card->bch[1].inst == inst) {
|
|
channel = 1;
|
|
break;
|
|
}
|
|
card = card->next;
|
|
}
|
|
if (channel<0) {
|
|
printk(KERN_ERR "speedfax_manager no channel data %p prim %x arg %p\n",
|
|
data, prim, arg);
|
|
return(-EINVAL);
|
|
}
|
|
switch(prim) {
|
|
case MGR_REGLAYER | CONFIRM:
|
|
if (!card) {
|
|
printk(KERN_WARNING "speedfax_manager no card found\n");
|
|
return(-ENODEV);
|
|
}
|
|
break;
|
|
case MGR_UNREGLAYER | REQUEST:
|
|
if (!card) {
|
|
printk(KERN_WARNING "speedfax_manager no card found\n");
|
|
return(-ENODEV);
|
|
} else {
|
|
if (channel == 2) {
|
|
inst->down.fdata = &card->dch;
|
|
if ((skb = create_link_skb(PH_CONTROL | REQUEST,
|
|
HW_DEACTIVATE, 0, NULL, 0))) {
|
|
if (ISAC_l1hw(&inst->down, skb))
|
|
dev_kfree_skb(skb);
|
|
}
|
|
} else {
|
|
inst->down.fdata = &card->bch[channel];
|
|
if ((skb = create_link_skb(MGR_DISCONNECT | REQUEST,
|
|
0, 0, NULL, 0))) {
|
|
if (isar_down(&inst->down, skb))
|
|
dev_kfree_skb(skb);
|
|
}
|
|
|
|
}
|
|
speedfax.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST,
|
|
&inst->up);
|
|
speedfax.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
|
}
|
|
break;
|
|
case MGR_RELEASE | INDICATION:
|
|
if (!card) {
|
|
printk(KERN_WARNING "speedfax_manager no card found\n");
|
|
return(-ENODEV);
|
|
} else {
|
|
if (channel == 2) {
|
|
release_card(card);
|
|
} else {
|
|
speedfax.refcnt--;
|
|
}
|
|
}
|
|
break;
|
|
case MGR_CONNECT | REQUEST:
|
|
if (!card) {
|
|
printk(KERN_WARNING "%s: connect request failed\n",
|
|
__FUNCTION__);
|
|
return(-ENODEV);
|
|
}
|
|
return(ConnectIF(inst, arg));
|
|
break;
|
|
case MGR_SETIF | REQUEST:
|
|
case MGR_SETIF | INDICATION:
|
|
if (!card) {
|
|
printk(KERN_WARNING "%s: setif failed\n", __FUNCTION__);
|
|
return(-ENODEV);
|
|
}
|
|
if (channel==2)
|
|
return(SetIF(inst, arg, prim, ISAC_l1hw, NULL,
|
|
&card->dch));
|
|
else
|
|
return(SetIF(inst, arg, prim, isar_down, NULL,
|
|
&card->bch[channel]));
|
|
case MGR_DISCONNECT | REQUEST:
|
|
case MGR_DISCONNECT | INDICATION:
|
|
if (!card) {
|
|
printk(KERN_WARNING "speedfax_manager del interface request failed\n");
|
|
return(-ENODEV);
|
|
}
|
|
return(DisConnectIF(inst, arg));
|
|
break;
|
|
case MGR_LOADFIRM | REQUEST:
|
|
if (!card) {
|
|
printk(KERN_WARNING "speedfax_manager MGR_LOADFIRM no card\n");
|
|
return(-ENODEV);
|
|
} else {
|
|
struct firm {
|
|
int len;
|
|
void *data;
|
|
} *firm = arg;
|
|
|
|
if (!arg)
|
|
return(-EINVAL);
|
|
return(isar_load_firmware(&card->bch[0], firm->data, firm->len));
|
|
}
|
|
case MGR_SETSTACK | CONFIRM:
|
|
if (!card) {
|
|
printk(KERN_WARNING "%s: setstack failed\n", __FUNCTION__);
|
|
return(-ENODEV);
|
|
}
|
|
if ((channel!=2) && (inst->pid.global == 2)) {
|
|
inst->down.fdata = &card->bch[channel];
|
|
if ((skb = create_link_skb(PH_ACTIVATE | REQUEST,
|
|
0, 0, NULL, 0))) {
|
|
if (isar_down(&inst->down, skb))
|
|
dev_kfree_skb(skb);
|
|
}
|
|
if ((inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS) ||
|
|
(inst->pid.protocol[2] == ISDN_PID_L2_B_TRANSDTMF))
|
|
if_link(&inst->up, DL_ESTABLISH | INDICATION,
|
|
0, 0, NULL, 0);
|
|
else
|
|
if_link(&inst->up, PH_ACTIVATE | INDICATION,
|
|
0, 0, NULL, 0);
|
|
}
|
|
break;
|
|
default:
|
|
printk(KERN_WARNING "speedfax_manager prim %x not handled\n", prim);
|
|
return(-EINVAL);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int
|
|
Speedfax_init(void)
|
|
{
|
|
int err,i;
|
|
sedl_fax *card;
|
|
mISDN_pid_t pid;
|
|
|
|
SET_MODULE_OWNER(&speedfax);
|
|
speedfax.name = SpeedfaxName;
|
|
speedfax.own_ctrl = speedfax_manager;
|
|
speedfax.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0;
|
|
speedfax.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS |
|
|
// ISDN_PID_L1_B_TRANS_TT |
|
|
// ISDN_PID_L1_B_TRANS_TTR |
|
|
// ISDN_PID_L1_B_TRANS_TTS |
|
|
ISDN_PID_L1_B_64HDLC;
|
|
speedfax.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS |
|
|
ISDN_PID_L2_B_TRANSDTMF;
|
|
speedfax.prev = NULL;
|
|
speedfax.next = NULL;
|
|
|
|
if ((err = mISDN_register(&speedfax))) {
|
|
printk(KERN_ERR "Can't register Speedfax error(%d)\n", err);
|
|
return(err);
|
|
}
|
|
while (sedl_cnt < MAX_CARDS) {
|
|
if (!(card = kmalloc(sizeof(sedl_fax), GFP_ATOMIC))) {
|
|
printk(KERN_ERR "No kmem for card\n");
|
|
mISDN_unregister(&speedfax);
|
|
return(-ENOMEM);
|
|
}
|
|
memset(card, 0, sizeof(sedl_fax));
|
|
APPEND_TO_LIST(card, ((sedl_fax *)speedfax.ilist));
|
|
card->dch.debug = debug;
|
|
card->dch.inst.obj = &speedfax;
|
|
lock_HW_init(&card->lock);
|
|
card->dch.inst.lock = lock_dev;
|
|
card->dch.inst.unlock = unlock_dev;
|
|
card->dch.inst.data = card;
|
|
card->dch.inst.up.owner = &card->dch.inst;
|
|
card->dch.inst.down.owner = &card->dch.inst;
|
|
speedfax.ctrl(NULL, MGR_DISCONNECT | REQUEST,
|
|
&card->dch.inst.down);
|
|
set_dchannel_pid(&pid, protocol[sedl_cnt], layermask[sedl_cnt]);
|
|
sprintf(card->dch.inst.name, "SFax%d", sedl_cnt+1);
|
|
init_dchannel(&card->dch);
|
|
for (i=0; i<2; i++) {
|
|
card->bch[i].channel = i;
|
|
card->bch[i].inst.obj = &speedfax;
|
|
card->bch[i].inst.data = card;
|
|
card->bch[i].inst.pid.layermask = 0;
|
|
card->bch[i].inst.up.owner = &card->bch[i].inst;
|
|
card->bch[i].inst.down.owner = &card->bch[i].inst;
|
|
card->bch[i].inst.down.fdata = &card->bch[i];
|
|
speedfax.ctrl(NULL, MGR_DISCONNECT | REQUEST,
|
|
&card->bch[i].inst.down);
|
|
card->bch[i].inst.lock = lock_dev;
|
|
card->bch[i].inst.unlock = unlock_dev;
|
|
card->bch[i].debug = debug;
|
|
sprintf(card->bch[i].inst.name, "%s B%d",
|
|
card->dch.inst.name, i+1);
|
|
init_bchannel(&card->bch[i]);
|
|
}
|
|
printk(KERN_DEBUG "sfax card %p dch %p bch1 %p bch2 %p\n",
|
|
card, &card->dch, &card->bch[0], &card->bch[1]);
|
|
if (setup_speedfax(card, io[cfg_idx], irq[cfg_idx])) {
|
|
err = 0;
|
|
free_dchannel(&card->dch);
|
|
free_bchannel(&card->bch[1]);
|
|
free_bchannel(&card->bch[0]);
|
|
REMOVE_FROM_LISTBASE(card, ((sedl_fax *)speedfax.ilist));
|
|
kfree(card);
|
|
card = NULL;
|
|
if (!sedl_cnt) {
|
|
mISDN_unregister(&speedfax);
|
|
err = -ENODEV;
|
|
} else
|
|
printk(KERN_INFO "sedlfax %d cards installed\n",
|
|
sedl_cnt);
|
|
return(err);
|
|
}
|
|
if (card->subtyp == SEDL_SPEEDFAX_ISA)
|
|
cfg_idx++;
|
|
sedl_cnt++;
|
|
if ((err = speedfax.ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst))) {
|
|
printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", err);
|
|
release_card(card);
|
|
if (!sedl_cnt)
|
|
mISDN_unregister(&speedfax);
|
|
else
|
|
err = 0;
|
|
return(err);
|
|
}
|
|
for (i=0; i<2; i++) {
|
|
if ((err = speedfax.ctrl(card->dch.inst.st,
|
|
MGR_NEWSTACK | REQUEST, &card->bch[i].inst))) {
|
|
printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err);
|
|
speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
|
|
if (!sedl_cnt)
|
|
mISDN_unregister(&speedfax);
|
|
else
|
|
err = 0;
|
|
return(err);
|
|
}
|
|
}
|
|
if ((err = speedfax.ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid))) {
|
|
printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err);
|
|
speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
|
|
if (!sedl_cnt)
|
|
mISDN_unregister(&speedfax);
|
|
else
|
|
err = 0;
|
|
return(err);
|
|
}
|
|
if ((err = init_card(card))) {
|
|
speedfax.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
|
|
if (!sedl_cnt)
|
|
mISDN_unregister(&speedfax);
|
|
else
|
|
err = 0;
|
|
return(err);
|
|
}
|
|
}
|
|
printk(KERN_INFO "sedlfax %d cards installed\n", sedl_cnt);
|
|
return(0);
|
|
}
|
|
|
|
#ifdef MODULE
|
|
int
|
|
cleanup_module(void)
|
|
{
|
|
int err;
|
|
if ((err = mISDN_unregister(&speedfax))) {
|
|
printk(KERN_ERR "Can't unregister Speedfax PCI error(%d)\n", err);
|
|
return(err);
|
|
}
|
|
while(speedfax.ilist) {
|
|
printk(KERN_ERR "Speedfax PCI card struct not empty refs %d\n",
|
|
speedfax.refcnt);
|
|
release_card(speedfax.ilist);
|
|
}
|
|
return(0);
|
|
}
|
|
#endif
|