mISDN/drivers/isdn/hardware/mISDN/w6692.c

1545 lines
41 KiB
C
Executable File

/* $Id$
* w6692.c low level driver for CCD's hfc-pci based cards
*
* Author Karsten Keil <kkeil@suse.de>
* based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include "dchannel.h"
#include "bchannel.h"
#include "layer1.h"
#include "helper.h"
#include "debug.h"
#include "w6692.h"
#include <linux/isdn_compat.h>
#define SPIN_DEBUG
#define LOCK_STATISTIC
#include "hw_lock.h"
extern const char *CardType[];
const char *w6692_rev = "$Revision$";
#define DBUSY_TIMER_VALUE 80
typedef struct _w6692_bc {
struct timer_list timer;
u_char b_mode;
} w6692_bc;
typedef struct _w6692pci {
struct _w6692pci *prev;
struct _w6692pci *next;
void *pdev;
u_int irq;
u_int irqcnt;
u_int addr;
int pots;
mISDN_HWlock_t lock;
u_char imask;
u_char pctl;
u_char xaddr;
u_char xdata;
w6692_bc wbc[2];
dchannel_t dch;
bchannel_t bch[2];
} w6692pci;
static int lock_dev(void *data, int nowait)
{
register mISDN_HWlock_t *lock = &((w6692pci *)data)->lock;
return(lock_HW(lock, nowait));
}
static void unlock_dev(void *data)
{
register mISDN_HWlock_t *lock = &((w6692pci *)data)->lock;
unlock_HW(lock);
}
static __inline__ u_char
ReadW6692(w6692pci *card, u_char offset)
{
return (inb(card->addr + offset));
}
static __inline__ void
WriteW6692(w6692pci *card, u_char offset, u_char value)
{
outb(value, card->addr + offset);
}
static __inline__ u_char
ReadW6692B(w6692pci *card, int bchan, u_char offset)
{
return (inb(card->addr + (bchan ? 0x40 : 0) + offset));
}
static __inline__ void
WriteW6692B(w6692pci *card, int bchan, u_char offset, u_char value)
{
outb(value, card->addr + (bchan ? 0x40 : 0) + offset);
}
static char *W6692Ver[] __initdata =
{"W6692 V00", "W6692 V01", "W6692 V10",
"W6692 V11"};
static void
W6692Version(w6692pci *card, char *s)
{
int val;
val = ReadW6692(card, W_D_RBCH);
printk(KERN_INFO "%s Winbond W6692 version (%x): %s\n", s, val, W6692Ver[(val >> 6) & 3]);
}
static void
ph_command(w6692pci *card, u_char command)
{
if (card->dch.debug & L1_DEB_ISAC)
debugprint(&card->dch.inst, "ph_command %x", command);
WriteW6692(card, W_CIX, command);
}
static void
W6692_new_ph(dchannel_t *dch)
{
u_int prim = PH_SIGNAL | INDICATION;
u_int para = 0;
mISDNif_t *upif = &dch->inst.up;
if (dch->debug)
printk(KERN_DEBUG "%s: event %lx\n", __FUNCTION__, dch->event);
if (!test_and_clear_bit(D_L1STATECHANGE, &dch->event))
return;
switch (dch->ph_state) {
case W_L1CMD_RST:
dch->inst.lock(dch->inst.data, 0);
ph_command(dch->hw, W_L1CMD_DRC);
dch->inst.unlock(dch->inst.data);
prim = PH_CONTROL | INDICATION;
para = HW_RESET;
while(upif) {
if_link(upif, prim, para, 0, NULL, 0);
upif = upif->next;
}
upif = &dch->inst.up;
/* fall trough */
case W_L1IND_CD:
prim = PH_CONTROL | CONFIRM;
para = HW_DEACTIVATE;
break;
case W_L1IND_DRD:
prim = PH_CONTROL | INDICATION;
para = HW_DEACTIVATE;
break;
case W_L1IND_CE:
prim = PH_CONTROL | INDICATION;
para = HW_POWERUP;
break;
case W_L1IND_LD:
para = ANYSIGNAL;
break;
case W_L1IND_ARD:
para = INFO2;
break;
case W_L1IND_AI8:
para = INFO4_P8;
break;
case W_L1IND_AI10:
para = INFO4_P10;
break;
default:
return;
}
while(upif) {
if_link(upif, prim, para, 0, NULL, 0);
upif = upif->next;
}
}
static void
W6692_empty_Dfifo(w6692pci *card, int count)
{
dchannel_t *dch = &card->dch;
u_char *ptr;
if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO))
debugprint(&dch->inst, "empty_Dfifo");
if (!dch->rx_skb) {
if (!(dch->rx_skb = alloc_stack_skb(MAX_DFRAME_LEN_L1, dch->up_headerlen))) {
printk(KERN_WARNING "mISDN: D receive out of memory\n");
WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
return;
}
}
if ((dch->rx_skb->len + count) >= MAX_DFRAME_LEN_L1) {
if (dch->debug & L1_DEB_WARN)
debugprint(&dch->inst, "empty_Dfifo overrun %d",
dch->rx_skb->len + count);
WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
return;
}
ptr = skb_put(dch->rx_skb, count);
insb(card->addr + W_D_RFIFO, ptr, count);
WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
if (dch->debug & L1_DEB_ISAC_FIFO) {
char *t = dch->dlog;
t += sprintf(t, "empty_Dfifo cnt %d", count);
QuickHex(t, ptr, count);
debugprint(&dch->inst, dch->dlog);
}
}
static void
W6692_fill_Dfifo(w6692pci *card)
{
dchannel_t *dch = &card->dch;
int count;
u_char *ptr;
u_char cmd = W_D_CMDR_XMS;
if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO))
debugprint(&dch->inst, "fill_Dfifo");
count = dch->tx_len - dch->tx_idx;
if (count <= 0)
return;
if (count > 32) {
count = 32;
} else
cmd |= W_D_CMDR_XME;
ptr = dch->tx_buf + dch->tx_idx;
dch->tx_idx += count;
outsb(card->addr + W_D_XFIFO, ptr, count);
WriteW6692(card, W_D_CMDR, cmd);
if (test_and_set_bit(FLG_DBUSY_TIMER, &dch->DFlags)) {
debugprint(&dch->inst, "fill_Dfifo dbusytimer running");
del_timer(&dch->dbusytimer);
}
init_timer(&dch->dbusytimer);
dch->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
add_timer(&dch->dbusytimer);
if (dch->debug & L1_DEB_ISAC_FIFO) {
char *t = dch->dlog;
t += sprintf(t, "fill_Dfifo cnt %d", count);
QuickHex(t, ptr, count);
debugprint(&dch->inst, dch->dlog);
}
}
static void
d_retransmit(w6692pci *card)
{
dchannel_t *dch = &card->dch;
if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
del_timer(&dch->dbusytimer);
if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
dchannel_sched_event(dch, D_CLEARBUSY);
if (test_bit(FLG_TX_BUSY, &dch->DFlags)) {
/* Restart frame */
dch->tx_idx = 0;
W6692_fill_Dfifo(card);
} else {
printk(KERN_WARNING "mISDN: w6692 XDU no TX_BUSY\n");
debugprint(&dch->inst, "XDU no TX_BUSY");
if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) {
if (dch->next_skb) {
dch->tx_len = dch->next_skb->len;
memcpy(dch->tx_buf,
dch->next_skb->data,
dch->tx_len);
dch->tx_idx = 0;
W6692_fill_Dfifo(card);
dchannel_sched_event(dch, D_XMTBUFREADY);
} else {
printk(KERN_WARNING "w6692 xdu irq TX_NEXT without skb\n");
}
}
}
}
static void
handle_rxD(w6692pci *card) {
u_char stat;
int count;
stat = ReadW6692(card, W_D_RSTA);
if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {
if (stat & W_D_RSTA_RDOV) {
if (card->dch.debug & L1_DEB_WARN)
debugprint(&card->dch.inst, "D-channel RDOV");
#ifdef ERROR_STATISTIC
card->dch.err_rx++;
#endif
}
if (stat & W_D_RSTA_CRCE) {
if (card->dch.debug & L1_DEB_WARN)
debugprint(&card->dch.inst, "D-channel CRC error");
#ifdef ERROR_STATISTIC
card->dch.err_crc++;
#endif
}
if (stat & W_D_RSTA_RMB) {
if (card->dch.debug & L1_DEB_WARN)
debugprint(&card->dch.inst, "D-channel ABORT");
#ifdef ERROR_STATISTIC
card->dch.err_rx++;
#endif
}
if (card->dch.rx_skb)
dev_kfree_skb(card->dch.rx_skb);
WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);
} else {
count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1);
if (count == 0)
count = W_D_FIFO_THRESH;
W6692_empty_Dfifo(card, count);
if (card->dch.rx_skb) {
skb_queue_tail(&card->dch.rqueue, card->dch.rx_skb);
}
}
card->dch.rx_skb = NULL;
dchannel_sched_event(&card->dch, D_RCVBUFREADY);
}
static void
handle_txD(w6692pci *card) {
register dchannel_t *dch = &card->dch;
if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
del_timer(&dch->dbusytimer);
if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
dchannel_sched_event(dch, D_CLEARBUSY);
if (dch->tx_idx < dch->tx_len) {
W6692_fill_Dfifo(card);
} else {
if (test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags)) {
if (dch->next_skb) {
dch->tx_len = dch->next_skb->len;
memcpy(dch->tx_buf,
dch->next_skb->data, dch->tx_len);
dch->tx_idx = 0;
W6692_fill_Dfifo(card);
dchannel_sched_event(dch, D_XMTBUFREADY);
} else {
printk(KERN_WARNING "w6692 txD irq TX_NEXT without skb\n");
test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
}
} else
test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
}
}
static void
handle_statusD(w6692pci *card) {
register dchannel_t *dch = &card->dch;
u_char exval, v1, cir;
exval = ReadW6692(card, W_D_EXIR);
if (card->dch.debug & L1_DEB_ISAC)
debugprint(&card->dch.inst, "D_EXIR %02x", exval);
if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) { /* Transmit underrun/collision */
if (card->dch.debug & L1_DEB_WARN)
debugprint(&card->dch.inst, "D-channel underrun/collision");
#ifdef ERROR_STATISTIC
dch->err_tx++;
#endif
d_retransmit(card);
}
if (exval & W_D_EXI_RDOV) { /* RDOV */
if (card->dch.debug & L1_DEB_WARN)
debugprint(&card->dch.inst, "D-channel RDOV");
WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST);
}
if (exval & W_D_EXI_TIN2) /* TIN2 - never */
if (card->dch.debug & L1_DEB_WARN)
debugprint(&card->dch.inst, "spurious TIN2 interrupt");
if (exval & W_D_EXI_MOC) { /* MOC - not supported */
v1 = ReadW6692(card, W_MOSR);
if (card->dch.debug & L1_DEB_ISAC) {
debugprint(&card->dch.inst, "spurious MOC interrupt");
debugprint(&card->dch.inst, "MOSR %02x", v1);
}
}
if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */
cir = ReadW6692(card, W_CIR);
if (card->dch.debug & L1_DEB_ISAC)
debugprint(&card->dch.inst, "ISC CIR=0x%02X", cir);
if (cir & W_CIR_ICC) {
v1 = cir & W_CIR_COD_MASK;
if (card->dch.debug & L1_DEB_ISAC)
debugprint(&card->dch.inst, "ph_state_change %x -> %x",
dch->ph_state, v1);
dch->ph_state = v1;
dchannel_sched_event(dch, D_L1STATECHANGE);
}
if (cir & W_CIR_SCC) {
v1 = ReadW6692(card, W_SQR);
if (card->dch.debug & L1_DEB_ISAC)
debugprint(&card->dch.inst, "SCC SQR=0x%02X", v1);
}
}
if (exval & W_D_EXI_WEXP) {
if (card->dch.debug & L1_DEB_WARN)
debugprint(&card->dch.inst, "spurious WEXP interrupt!");
}
if (exval & W_D_EXI_TEXP) {
if (card->dch.debug & L1_DEB_WARN)
debugprint(&card->dch.inst, "spurious TEXP interrupt!");
}
}
static void
W6692_empty_Bfifo(bchannel_t *bch, int count)
{
u_char *ptr;
w6692pci *card = bch->inst.data;
if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
debugprint(&bch->inst, "empty_Bfifo %d", count);
if (bch->rx_idx + count > MAX_DATA_MEM) {
if (bch->debug & L1_DEB_WARN)
debugprint(&bch->inst, "empty_Bfifo incoming packet too large");
WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
bch->rx_idx = 0;
return;
}
ptr = bch->rx_buf + bch->rx_idx;
bch->rx_idx += count;
insb(card->addr + W_B_RFIFO + (bch->channel ? 0x40 : 0), ptr, count);
WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
if (bch->debug & L1_DEB_HSCX_FIFO) {
char *t = bch->blog;
t += sprintf(t, "empty_Bfifo B%d cnt %d", bch->channel, count);
QuickHex(t, ptr, count);
debugprint(&bch->inst, bch->blog);
}
}
static void
W6692_fill_Bfifo(bchannel_t *bch)
{
w6692pci *card = bch->inst.data;
int count;
u_char *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS;
if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
debugprint(&bch->inst, "%s", __FUNCTION__);
count = bch->tx_len - bch->tx_idx;
if (count <= 0)
return;
ptr = bch->tx_buf + bch->tx_idx;
if (count > W_B_FIFO_THRESH) {
count = W_B_FIFO_THRESH;
} else {
if (bch->protocol != ISDN_PID_L1_B_64TRANS)
cmd |= W_B_CMDR_XME;
}
if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
debugprint(&bch->inst, "%s: %d/%d", __FUNCTION__,
count, bch->tx_idx);
bch->tx_idx += count;
outsb(card->addr + W_B_XFIFO + (bch->channel ? 0x40 : 0), ptr, count);
WriteW6692B(card, bch->channel, W_B_CMDR, cmd);
if (bch->debug & L1_DEB_HSCX_FIFO) {
char *t = bch->blog;
t += sprintf(t, "fill_Bfifo B%d cnt %d",
bch->channel, count);
QuickHex(t, ptr, count);
debugprint(&bch->inst, bch->blog);
}
}
static int
setvolume(bchannel_t *bch, int mic, struct sk_buff *skb)
{
w6692pci *card = bch->inst.data;
u16 *vol = (u16 *)skb->data;
u_char val;
if ((card->pots == 0) || (bch->protocol != ISDN_PID_L1_B_64TRANS))
return(-ENODEV);
if (skb->len < 2)
return(-EINVAL);
if (*vol > 7)
return(-EINVAL);
val = *vol & 7;
val = 7 - val;
if (mic) {
val <<= 3;
card->xaddr &= 0xc7;
} else {
card->xaddr &= 0xf8;
}
card->xaddr |= val;
WriteW6692(card, W_XADDR, card->xaddr);
return(0);
}
static int
enable_pots(bchannel_t *bch)
{
w6692_bc *bhw = bch->hw;
w6692pci *card = bch->inst.data;
if ((card->pots == 0) || (bch->protocol != ISDN_PID_L1_B_64TRANS))
return(-ENODEV);
bhw->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0;
WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode);
WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
card->pctl |= (bch->channel ? W_PCTL_PCX : 0);
WriteW6692(card, W_PCTL, card->pctl);
return(0);
}
static int
disable_pots(bchannel_t *bch)
{
w6692_bc *bhw = bch->hw;
w6692pci *card = bch->inst.data;
if (card->pots == 0)
return(-ENODEV);
bhw->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0);
WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode);
WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST);
return(0);
}
static int
mode_w6692(bchannel_t *bch, int bc, int protocol)
{
w6692pci *card = bch->inst.data;
w6692_bc *bhw = bch->hw;
if (bch->debug & L1_DEB_HSCX)
debugprint(&bch->inst, "B%d protocol %x-->%x ch %d-->%d",
bch->channel, bch->protocol, protocol, bch->channel, bc);
switch (protocol) {
case (-1): /* used for init */
bch->protocol = -1;
bch->channel = bc;
case (ISDN_PID_NONE):
if (bch->protocol == ISDN_PID_NONE)
break;
bch->protocol = ISDN_PID_NONE;
if (card->pots && (bhw->b_mode & W_B_MODE_EPCM))
disable_pots(bch);
bhw->b_mode = 0;
WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode);
WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
break;
case (ISDN_PID_L1_B_64TRANS):
bch->protocol = protocol;
bhw->b_mode = W_B_MODE_MMS;
WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode);
WriteW6692B(card, bch->channel, W_B_EXIM, 0);
WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST);
bch_sched_event(bch, B_XMTBUFREADY);
break;
case (ISDN_PID_L1_B_64HDLC):
bch->protocol = protocol;
bhw->b_mode = W_B_MODE_ITF;
WriteW6692B(card, bch->channel, W_B_MODE, bhw->b_mode);
WriteW6692B(card, bch->channel, W_B_ADM1, 0xff);
WriteW6692B(card, bch->channel, W_B_ADM2, 0xff);
WriteW6692B(card, bch->channel, W_B_EXIM, 0);
WriteW6692B(card, bch->channel, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT | W_B_CMDR_XRST);
bch_sched_event(bch, B_XMTBUFREADY);
break;
default:
debugprint(&bch->inst, "prot not known %x", protocol);
return(-ENOPROTOOPT);
}
return(0);
}
static void
send_next(bchannel_t *bch)
{
if (bch->tx_idx < bch->tx_len)
W6692_fill_Bfifo(bch);
else {
bch->tx_idx = 0;
if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
if (bch->next_skb) {
bch->tx_len = bch->next_skb->len;
memcpy(bch->tx_buf, bch->next_skb->data, bch->tx_len);
W6692_fill_Bfifo(bch);
} else {
bch->tx_len = 0;
printk(KERN_WARNING "W6692 tx irq TX_NEXT without skb\n");
test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
}
} else {
bch->tx_len = 0;
test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
}
bch_sched_event(bch, B_XMTBUFREADY);
}
}
static void
W6692B_interrupt(w6692pci *card, int ch)
{
bchannel_t *bch = &card->bch[ch];
int count;
u_char stat, star = 0;
struct sk_buff *skb;
stat = ReadW6692B(card, ch, W_B_EXIR);
if (bch->debug & L1_DEB_HSCX)
debugprint(&bch->inst, "ch%d stat %#x", bch->channel, stat);
if (stat & W_B_EXI_RME) {
star = ReadW6692B(card, ch, W_B_STAR);
if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {
if ((star & W_B_STAR_RDOV) && (bch->protocol != ISDN_PID_NONE)) {
if (bch->debug & L1_DEB_WARN)
debugprint(&bch->inst, "B%d RDOV protocol=%x",
ch +1, bch->protocol);
#ifdef ERROR_STATISTIC
bch->err_rdo++;
#endif
}
if ((star & W_B_STAR_CRCE) && (bch->protocol == ISDN_PID_L1_B_64HDLC)) {
if (bch->debug & L1_DEB_WARN)
debugprint(&bch->inst, "B%d CRC error", ch +1);
#ifdef ERROR_STATISTIC
bch->err_crc++;
#endif
}
if ((star & W_B_STAR_RMB) && (bch->protocol == ISDN_PID_L1_B_64HDLC)) {
if (bch->debug & L1_DEB_WARN)
debugprint(&bch->inst, "B%d message abort", ch +1);
#ifdef ERROR_STATISTIC
bch->err_inv++;
#endif
}
WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
} else {
count = ReadW6692B(card, ch, W_B_RBCL) & (W_B_FIFO_THRESH - 1);
if (count == 0)
count = W_B_FIFO_THRESH;
W6692_empty_Bfifo(bch, count);
if (bch->rx_idx > 0) {
if (bch->debug & L1_DEB_HSCX)
debugprint(&bch->inst, "Bchan Frame %d", bch->rx_idx);
if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen)))
printk(KERN_WARNING "Bchan receive out of memory\n");
else {
memcpy(skb_put(skb, bch->rx_idx), bch->rx_buf, bch->rx_idx);
skb_queue_tail(&bch->rqueue, skb);
}
bch_sched_event(bch, B_RCVBUFREADY);
}
}
bch->rx_idx = 0;
}
if (stat & W_B_EXI_RMR) {
if (!(stat & W_B_EXI_RME))
star = ReadW6692B(card, ch, W_B_STAR);
if (star & W_B_STAR_RDOV) {
if (bch->debug & L1_DEB_WARN)
debugprint(&bch->inst, "B%d RDOV protocol=%x",
ch +1, bch->protocol);
#ifdef ERROR_STATISTIC
bch->err_rdo++;
#endif
WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
} else {
W6692_empty_Bfifo(bch, W_B_FIFO_THRESH);
if ((bch->protocol == ISDN_PID_L1_B_64TRANS) && (bch->rx_idx > 0)) {
/* receive audio data */
if (bch->debug & L1_DEB_HSCX)
debugprint(&bch->inst, "Bchan Frame %d", bch->rx_idx);
if (!(skb = alloc_stack_skb(bch->rx_idx, bch->up_headerlen)))
printk(KERN_WARNING "Bchan receive out of memory\n");
else {
memcpy(skb_put(skb, bch->rx_idx), bch->rx_buf, bch->rx_idx);
skb_queue_tail(&bch->rqueue, skb);
}
bch_sched_event(bch, B_RCVBUFREADY);
bch->rx_idx = 0;
}
}
}
if (stat & W_B_EXI_RDOV) {
if (!(star & W_B_STAR_RDOV)) { /* only if it is not handled yet */
if (bch->debug & L1_DEB_WARN)
debugprint(&bch->inst, "B%d RDOV IRQ protocol=%x",
ch +1, bch->protocol);
#ifdef ERROR_STATISTIC
bch->err_rdo++;
#endif
WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
}
}
if (stat & W_B_EXI_XFR) {
if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) {
star = ReadW6692B(card, ch, W_B_STAR);
if (bch->debug & L1_DEB_HSCX)
debugprint(&bch->inst, "B%d star %02x", ch +1, star);
}
if (star & W_B_STAR_XDOW) {
if (bch->debug & L1_DEB_WARN)
debugprint(&bch->inst, "B%d XDOW protocol=%x",
ch +1, bch->protocol);
#ifdef ERROR_STATISTIC
bch->err_xdu++;
#endif
WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
/* resend */
if (bch->tx_len) {
if (bch->protocol != ISDN_PID_L1_B_64TRANS)
bch->tx_idx = 0;
}
}
send_next(bch);
if (stat & W_B_EXI_XDUN)
return; /* handle XDOW only once */
}
if (stat & W_B_EXI_XDUN) {
if (bch->debug & L1_DEB_WARN)
debugprint(&bch->inst, "B%d XDUN protocol=%x",
ch +1, bch->protocol);
#ifdef ERROR_STATISTIC
bch->err_xdu++;
#endif
WriteW6692B(card, ch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
/* resend */
if (bch->tx_len) {
if (bch->protocol != ISDN_PID_L1_B_64TRANS)
bch->tx_idx = 0;
}
send_next(bch);
}
}
static irqreturn_t
w6692_interrupt(int intno, void *dev_id, struct pt_regs *regs)
{
w6692pci *card = dev_id;
u_long flags;
u_char ista;
spin_lock_irqsave(&card->lock.lock, flags);
#ifdef SPIN_DEBUG
card->lock.spin_adr = (void *)0x2001;
#endif
ista = ReadW6692(card, W_ISTA);
if ((ista | card->imask) == card->imask) {
/* possible a shared IRQ reqest */
#ifdef SPIN_DEBUG
card->lock.spin_adr = NULL;
#endif
spin_unlock_irqrestore(&card->lock.lock, flags);
return IRQ_NONE;
}
card->irqcnt++;
if (test_and_set_bit(STATE_FLAG_BUSY, &card->lock.state)) {
printk(KERN_ERR "%s: STATE_FLAG_BUSY allready activ, should never happen state:%lx\n",
__FUNCTION__, card->lock.state);
#ifdef SPIN_DEBUG
printk(KERN_ERR "%s: previous lock:%p\n",
__FUNCTION__, card->lock.busy_adr);
#endif
#ifdef LOCK_STATISTIC
card->lock.irq_fail++;
#endif
} else {
#ifdef LOCK_STATISTIC
card->lock.irq_ok++;
#endif
#ifdef SPIN_DEBUG
card->lock.busy_adr = w6692_interrupt;
#endif
}
test_and_set_bit(STATE_FLAG_INIRQ, &card->lock.state);
#ifdef SPIN_DEBUG
card->lock.spin_adr = NULL;
#endif
spin_unlock_irqrestore(&card->lock.lock, flags);
/* Begin IRQ handler */
if (card->dch.debug & L1_DEB_ISAC)
debugprint(&card->dch.inst, "ista %02x", ista);
ista &= ~card->imask;
if (ista & W_INT_B1_EXI)
W6692B_interrupt(card, 0);
if (ista & W_INT_B2_EXI)
W6692B_interrupt(card, 1);
if (ista & W_INT_D_RME)
handle_rxD(card);
if (ista & W_INT_D_RMR)
W6692_empty_Dfifo(card, W_D_FIFO_THRESH);
if (ista & W_INT_D_XFR)
handle_txD(card);
if (ista & W_INT_D_EXI)
handle_statusD(card);
if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */
debugprint(&card->dch.inst, "W6692 spurious XINT!");
/* End IRQ Handler */
spin_lock_irqsave(&card->lock.lock, flags);
#ifdef SPIN_DEBUG
card->lock.spin_adr = (void *)0x2002;
#endif
if (!test_and_clear_bit(STATE_FLAG_INIRQ, &card->lock.state)) {
}
if (!test_and_clear_bit(STATE_FLAG_BUSY, &card->lock.state)) {
printk(KERN_ERR "%s: STATE_FLAG_BUSY not locked state(%lx)\n",
__FUNCTION__, card->lock.state);
}
#ifdef SPIN_DEBUG
card->lock.busy_adr = NULL;
card->lock.spin_adr = NULL;
#endif
spin_unlock_irqrestore(&card->lock.lock, flags);
return IRQ_HANDLED;
}
static void
dbusy_timer_handler(dchannel_t *dch)
{
w6692pci *card = dch->hw;
int rbch, star;
if (test_bit(FLG_DBUSY_TIMER, &dch->DFlags)) {
if (dch->inst.lock(dch->inst.data, 1)) {
dch->dbusytimer.expires = jiffies + 1;
add_timer(&dch->dbusytimer);
return;
}
rbch = ReadW6692(card, W_D_RBCH);
star = ReadW6692(card, W_D_STAR);
if (dch->debug)
debugprint(&dch->inst, "D-Channel Busy RBCH %02x STAR %02x",
rbch, star);
if (star & W_D_STAR_XBZ) { /* D-Channel Busy */
test_and_set_bit(FLG_L1_DBUSY, &dch->DFlags);
} else {
/* discard frame; reset transceiver */
test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags);
if (dch->tx_idx) {
dch->tx_idx = 0;
} else {
printk(KERN_WARNING "mISDN: W6692 D-Channel Busy no tx_idx\n");
debugprint(&dch->inst, "D-Channel Busy no tx_idx");
}
/* Transmitter reset */
WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST); /* Transmitter reset */
}
dch->inst.unlock(dch->inst.data);
}
}
void initW6692(w6692pci *card)
{
u_char val;
card->dch.hw_bh = W6692_new_ph;
card->dch.dbusytimer.function = (void *) dbusy_timer_handler;
card->dch.dbusytimer.data = (u_long) &card->dch;
init_timer(&card->dch.dbusytimer);
mode_w6692(&card->bch[0], 0, -1);
mode_w6692(&card->bch[1], 1, -1);
WriteW6692(card, W_D_CTL, 0x00);
WriteW6692(card, W_IMASK, 0xff);
WriteW6692(card, W_D_SAM, 0xff);
WriteW6692(card, W_D_TAM, 0xff);
WriteW6692(card, W_D_MODE, W_D_MODE_RACT);
card->dch.ph_state = W_L1CMD_RST;
ph_command(card, W_L1CMD_RST);
ph_command(card, W_L1CMD_ECK);
/* Reenable all IRQ */
card->imask = 0x18;
WriteW6692(card, W_IMASK, card->imask);
WriteW6692(card, W_D_EXIM, 0x00);
WriteW6692B(card, 0, W_B_EXIM, 0);
WriteW6692B(card, 1, W_B_EXIM, 0);
/* Reset D-chan receiver and transmitter */
WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);
/* Reset B-chan receiver and transmitter */
WriteW6692B(card, 0, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
WriteW6692B(card, 1, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
/* enable peripheral */
card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 | W_PCTL_OE1 | W_PCTL_OE0;
if (card->pots) {
card->xaddr = 0x00; /* all sw off */
card->xdata = 0x06; /* LED off / POWER UP / ALAW */
WriteW6692(card, W_PCTL, card->pctl);
WriteW6692(card, W_XADDR, card->xaddr);
WriteW6692(card, W_XDATA, card->xdata);
val = ReadW6692(card, W_XADDR);
if (card->dch.debug & L1_DEB_ISAC)
debugprint(&card->dch.inst, "W_XADDR: %02x", val);
}
}
static void reset_w6692(w6692pci *card)
{
WriteW6692(card, W_D_CTL, W_D_CTL_SRST);
mdelay(10);
WriteW6692(card, W_D_CTL, 0);
}
static int init_card(w6692pci *card)
{
int cnt = 3;
unsigned long flags;
save_flags(flags);
lock_dev(card, 0);
if (request_irq(card->irq, w6692_interrupt, SA_SHIRQ,
"w6692", card)) {
printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n",
card->irq);
unlock_dev(card);
return(-EIO);
}
while (cnt) {
initW6692(card);
/* RESET Receiver and Transmitter */
unlock_dev(card);
sti();
/* Timeout 10ms */
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout((10*HZ)/1000);
restore_flags(flags);
printk(KERN_INFO "w6692: IRQ %d count %d\n",
card->irq, card->irqcnt);
if (!card->irqcnt) {
printk(KERN_WARNING
"w6692: IRQ(%d) getting no interrupts during init %d\n",
card->irq, 4 - cnt);
if (cnt == 1) {
return (-EIO);
} else {
reset_w6692(card);
cnt--;
}
} else {
return(0);
}
lock_dev(card, 0);
}
unlock_dev(card);
return(-EIO);
}
#define MAX_CARDS 4
#define MODULE_PARM_T "1-4i"
static int w6692_cnt;
static u_int protocol[MAX_CARDS];
static int layermask[MAX_CARDS];
static mISDNobject_t w6692;
static int debug;
static int pots[MAX_CARDS];
#ifdef MODULE
MODULE_AUTHOR("Karsten Keil");
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif
MODULE_PARM(debug, "1i");
MODULE_PARM(pots, MODULE_PARM_T);
MODULE_PARM(protocol, MODULE_PARM_T);
MODULE_PARM(layermask, MODULE_PARM_T);
#endif
/******************************/
/* Layer2 -> Layer 1 Transfer */
/******************************/
static int
w6692_l2l1B(mISDNif_t *hif, struct sk_buff *skb)
{
bchannel_t *bch;
int ret = -EINVAL;
mISDN_head_t *hh;
if (!hif || !skb)
return(ret);
hh = mISDN_HEAD_P(skb);
bch = hif->fdata;
if ((hh->prim == PH_DATA_REQ) ||
(hh->prim == (DL_DATA | REQUEST))) {
if (bch->next_skb) {
printk(KERN_WARNING "%s: next_skb exist ERROR\n",
__FUNCTION__);
return(-EBUSY);
}
bch->inst.lock(bch->inst.data, 0);
if (test_and_set_bit(BC_FLG_TX_BUSY, &bch->Flag)) {
test_and_set_bit(BC_FLG_TX_NEXT, &bch->Flag);
bch->next_skb = skb;
bch->inst.unlock(bch->inst.data);
return(0);
} else {
bch->tx_len = skb->len;
memcpy(bch->tx_buf, skb->data, bch->tx_len);
bch->tx_idx = 0;
W6692_fill_Bfifo(bch);
bch->inst.unlock(bch->inst.data);
if ((bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
&& bch->dev)
hif = &bch->dev->rport.pif;
else
hif = &bch->inst.up;
skb_trim(skb, 0);
return(if_newhead(hif, hh->prim | CONFIRM,
hh->dinfo, skb));
}
} else if ((hh->prim == (PH_ACTIVATE | REQUEST)) ||
(hh->prim == (DL_ESTABLISH | REQUEST))) {
if (test_and_set_bit(BC_FLG_ACTIV, &bch->Flag))
ret = 0;
else {
bch->inst.lock(bch->inst.data, 0);
ret = mode_w6692(bch, bch->channel, bch->inst.pid.protocol[1]);
bch->inst.unlock(bch->inst.data);
}
if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
if (bch->dev)
if_link(&bch->dev->rport.pif,
hh->prim | CONFIRM, 0, 0, NULL, 0);
skb_trim(skb, 0);
return(if_newhead(&bch->inst.up, hh->prim | CONFIRM, ret, skb));
} else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) ||
(hh->prim == (DL_RELEASE | REQUEST)) ||
(hh->prim == (MGR_DISCONNECT | REQUEST))) {
bch->inst.lock(bch->inst.data, 0);
if (test_and_clear_bit(BC_FLG_TX_NEXT, &bch->Flag)) {
dev_kfree_skb(bch->next_skb);
bch->next_skb = NULL;
}
test_and_clear_bit(BC_FLG_TX_BUSY, &bch->Flag);
mode_w6692(bch, bch->channel, ISDN_PID_NONE);
test_and_clear_bit(BC_FLG_ACTIV, &bch->Flag);
bch->inst.unlock(bch->inst.data);
skb_trim(skb, 0);
if (hh->prim != (MGR_DISCONNECT | REQUEST)) {
if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_RAWDEV)
if (bch->dev)
if_link(&bch->dev->rport.pif,
hh->prim | CONFIRM, 0, 0, NULL, 0);
if (!if_newhead(&bch->inst.up, hh->prim | CONFIRM, 0, skb))
return(0);
}
ret = 0;
} else if (hh->prim == (PH_CONTROL | REQUEST)) {
ret = 0;
bch->inst.lock(bch->inst.data, 0);
if (hh->dinfo == HW_POTS_ON) {
ret = enable_pots(bch);
} else if (hh->dinfo == HW_POTS_OFF) {
ret = disable_pots(bch);
} else if (hh->dinfo == HW_POTS_SETMICVOL) {
ret = setvolume(bch, 1, skb);
} else if (hh->dinfo == HW_POTS_SETSPKVOL) {
ret = setvolume(bch, 0, skb);
} else
ret = -EINVAL;
bch->inst.unlock(bch->inst.data);
} else {
printk(KERN_WARNING "%s: unknown prim(%x)\n",
__FUNCTION__, hh->prim);
}
if (!ret)
dev_kfree_skb(skb);
return(ret);
}
static int
w6692_l1hwD(mISDNif_t *hif, struct sk_buff *skb)
{
dchannel_t *dch;
int ret = -EINVAL;
mISDN_head_t *hh;
if (!hif || !skb)
return(ret);
hh = mISDN_HEAD_P(skb);
dch = hif->fdata;
ret = 0;
if (hh->prim == PH_DATA_REQ) {
if (dch->next_skb) {
debugprint(&dch->inst, "w6692 l2l1 next_skb exist this shouldn't happen");
return(-EBUSY);
}
dch->inst.lock(dch->inst.data,0);
if (test_and_set_bit(FLG_TX_BUSY, &dch->DFlags)) {
test_and_set_bit(FLG_TX_NEXT, &dch->DFlags);
dch->next_skb = skb;
dch->inst.unlock(dch->inst.data);
return(0);
} else {
dch->tx_len = skb->len;
memcpy(dch->tx_buf, skb->data, dch->tx_len);
dch->tx_idx = 0;
W6692_fill_Dfifo(dch->hw);
dch->inst.unlock(dch->inst.data);
return(if_newhead(&dch->inst.up, PH_DATA_CNF,
DINFO_SKB, skb));
}
} else if (hh->prim == (PH_SIGNAL | REQUEST)) {
dch->inst.lock(dch->inst.data,0);
if (hh->dinfo == INFO3_P8)
ph_command(dch->hw, W_L1CMD_AR8);
else if (hh->dinfo == INFO3_P10)
ph_command(dch->hw, W_L1CMD_AR10);
else
ret = -EINVAL;
dch->inst.unlock(dch->inst.data);
} else if (hh->prim == (PH_CONTROL | REQUEST)) {
dch->inst.lock(dch->inst.data,0);
if (hh->dinfo == HW_RESET) {
if (dch->ph_state != W_L1IND_DRD)
ph_command(dch->hw, W_L1CMD_RST);
ph_command(dch->hw, W_L1CMD_ECK);
} else if (hh->dinfo == HW_POWERUP) {
ph_command(dch->hw, W_L1CMD_ECK);
} else if (hh->dinfo == HW_DEACTIVATE) {
discard_queue(&dch->rqueue);
if (dch->next_skb) {
dev_kfree_skb(dch->next_skb);
dch->next_skb = NULL;
}
test_and_clear_bit(FLG_TX_NEXT, &dch->DFlags);
test_and_clear_bit(FLG_TX_BUSY, &dch->DFlags);
if (test_and_clear_bit(FLG_DBUSY_TIMER, &dch->DFlags))
del_timer(&dch->dbusytimer);
if (test_and_clear_bit(FLG_L1_DBUSY, &dch->DFlags))
dchannel_sched_event(dch, D_CLEARBUSY);
} else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) {
u_char val = 0;
if (1 & hh->dinfo)
val |= 0x0c;
if (2 & hh->dinfo)
val |= 0x3;
/* !!! not implemented yet */
} else {
if (dch->debug & L1_DEB_WARN)
debugprint(&dch->inst, "w6692_l1hw unknown ctrl %x",
hh->dinfo);
ret = -EINVAL;
}
dch->inst.unlock(dch->inst.data);
} else {
if (dch->debug & L1_DEB_WARN)
debugprint(&dch->inst, "w6692_l1hw unknown prim %x",
hh->prim);
ret = -EINVAL;
}
if (!ret)
dev_kfree_skb(skb);
return(ret);
}
int __init
setup_w6692(w6692pci *card)
{
if (!request_region(card->addr, 256, "w6692")) {
printk(KERN_WARNING
"mISDN: %s config port %x-%x already in use\n",
"w6692",
card->addr,
card->addr + 255);
return(-EIO);
}
W6692Version(card, "W6692:");
printk(KERN_INFO "W6692 ISTA=0x%X\n", ReadW6692(card, W_ISTA));
printk(KERN_INFO "W6692 IMASK=0x%X\n", ReadW6692(card, W_IMASK));
printk(KERN_INFO "W6692 D_EXIR=0x%X\n", ReadW6692(card, W_D_EXIR));
printk(KERN_INFO "W6692 D_EXIM=0x%X\n", ReadW6692(card, W_D_EXIM));
printk(KERN_INFO "W6692 D_RSTA=0x%X\n", ReadW6692(card, W_D_RSTA));
return (0);
}
static void
release_card(w6692pci *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);
/* disable all IRQ */
WriteW6692(card, W_IMASK, 0xff);
free_irq(card->irq, card);
mode_w6692(&card->bch[0], 0, ISDN_PID_NONE);
mode_w6692(&card->bch[1], 1, ISDN_PID_NONE);
release_region(card->addr, 256);
free_bchannel(&card->bch[1]);
free_bchannel(&card->bch[0]);
free_dchannel(&card->dch);
w6692.ctrl(&card->dch.inst, MGR_UNREGLAYER | REQUEST, NULL);
REMOVE_FROM_LISTBASE(card, ((w6692pci *)w6692.ilist));
unlock_dev(card);
pci_disable_device(card->pdev);
pci_set_drvdata(card->pdev, NULL);
kfree(card);
}
static int
w6692_manager(void *data, u_int prim, void *arg) {
w6692pci *card = w6692.ilist;
mISDNinstance_t *inst = data;
struct sk_buff *skb;
int channel = -1;
if (debug & 0x10000)
printk(KERN_DEBUG "%s: data(%p) prim(%x) arg(%p)\n",
__FUNCTION__, data, prim, arg);
if (!data) {
MGR_HASPROTOCOL_HANDLER(prim,arg,&w6692)
printk(KERN_ERR "%s: no data prim %x arg %p\n",
__FUNCTION__, 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_WARNING "%s: no channel data %p prim %x arg %p\n",
__FUNCTION__, data, prim, arg);
return(-EINVAL);
}
switch(prim) {
case MGR_REGLAYER | CONFIRM:
if (channel == 2)
dch_set_para(&card->dch, &inst->st->para);
else
bch_set_para(&card->bch[channel], &inst->st->para);
break;
case MGR_UNREGLAYER | REQUEST:
if (channel == 2) {
inst->down.fdata = &card->dch;
if ((skb = create_link_skb(PH_CONTROL | REQUEST,
HW_DEACTIVATE, 0, NULL, 0))) {
if (w6692_l1hwD(&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 (w6692_l2l1B(&inst->down, skb))
dev_kfree_skb(skb);
}
}
w6692.ctrl(inst->up.peer, MGR_DISCONNECT | REQUEST, &inst->up);
w6692.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
break;
case MGR_CLRSTPARA | INDICATION:
arg = NULL;
case MGR_ADDSTPARA | INDICATION:
if (channel == 2)
dch_set_para(&card->dch, arg);
else
bch_set_para(&card->bch[channel], arg);
break;
case MGR_RELEASE | INDICATION:
if (channel == 2) {
release_card(card);
} else {
w6692.refcnt--;
}
break;
case MGR_CONNECT | REQUEST:
return(ConnectIF(inst, arg));
case MGR_SETIF | REQUEST:
case MGR_SETIF | INDICATION:
if (channel==2)
return(SetIF(inst, arg, prim, w6692_l1hwD, NULL,
&card->dch));
else
return(SetIF(inst, arg, prim, w6692_l2l1B, NULL,
&card->bch[channel]));
break;
case MGR_DISCONNECT | REQUEST:
case MGR_DISCONNECT | INDICATION:
return(DisConnectIF(inst, arg));
case MGR_SETSTACK | CONFIRM:
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 (w6692_l2l1B(&inst->down, skb))
dev_kfree_skb(skb);
}
if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS)
if_link(&inst->up, DL_ESTABLISH | INDICATION,
0, 0, NULL, 0);
else
if_link(&inst->up, PH_ACTIVATE | INDICATION,
0, 0, NULL, 0);
}
break;
case MGR_GLOBALOPT | REQUEST:
if (arg) {
/* FIXME: detect cards with HEADSET */
u_int *gopt = arg;
*gopt = GLOBALOPT_INTERNAL_CTRL |
GLOBALOPT_EXTERNAL_EQUIPMENT |
GLOBALOPT_HANDSET;
} else
return(-EINVAL);
break;
PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION);
default:
printk(KERN_WARNING "%s: prim %x not handled\n",
__FUNCTION__, prim);
return(-EINVAL);
}
return(0);
}
static int __devinit setup_instance(w6692pci *card)
{
int i, err;
mISDN_pid_t pid;
APPEND_TO_LIST(card, ((w6692pci *)w6692.ilist));
card->dch.debug = debug;
lock_HW_init(&card->lock);
card->dch.inst.lock = lock_dev;
card->dch.inst.unlock = unlock_dev;
card->dch.inst.pid.layermask = ISDN_LAYER(0);
card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0;
init_mISDNinstance(&card->dch.inst, &w6692, card);
sprintf(card->dch.inst.name, "W6692_%d", w6692_cnt+1);
set_dchannel_pid(&pid, protocol[w6692_cnt], layermask[w6692_cnt]);
init_dchannel(&card->dch);
card->dch.hw = card;
for (i=0; i<2; i++) {
card->bch[i].channel = i;
init_mISDNinstance(&card->bch[i].inst, &w6692, card);
card->bch[i].inst.pid.layermask = ISDN_LAYER(0);
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]);
card->bch[i].hw = &card->wbc[i];
}
printk(KERN_DEBUG "w6692 card %p dch %p bch1 %p bch2 %p\n",
card, &card->dch, &card->bch[0], &card->bch[1]);
err = setup_w6692(card);
if (err) {
free_dchannel(&card->dch);
free_bchannel(&card->bch[1]);
free_bchannel(&card->bch[0]);
REMOVE_FROM_LISTBASE(card, ((w6692pci *)w6692.ilist));
kfree(card);
return(err);
}
card->pots = pots[w6692_cnt];
w6692_cnt++;
err = w6692.ctrl(NULL, MGR_NEWSTACK | REQUEST, &card->dch.inst);
if (err) {
release_card(card);
return(err);
}
for (i=0; i<2; i++) {
err = w6692.ctrl(card->dch.inst.st, MGR_NEWSTACK | REQUEST, &card->bch[i].inst);
if (err) {
printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err);
w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
return(err);
}
}
err = w6692.ctrl(card->dch.inst.st, MGR_SETSTACK | REQUEST, &pid);
if (err) {
printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n", err);
w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
return(err);
}
err = init_card(card);
if (err) {
w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
return(err);
}
w6692.ctrl(card->dch.inst.st, MGR_CTRLREADY | INDICATION, NULL);
printk(KERN_INFO "w6692 %d cards installed\n", w6692_cnt);
return(0);
}
static int __devinit w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int err = -ENOMEM;
w6692pci *card;
if (!(card = kmalloc(sizeof(w6692pci), GFP_ATOMIC))) {
printk(KERN_ERR "No kmem for w6692 card\n");
return(err);
}
memset(card, 0, sizeof(w6692pci));
card->pdev = pdev;
err = pci_enable_device(pdev);
if (err) {
kfree(card);
return(err);
}
printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n",
(char *) ent->driver_data, pdev->slot_name);
card->addr = pci_resource_start(pdev, 1);
card->irq = pdev->irq;
pci_set_drvdata(pdev, card);
err = setup_instance(card);
return(err);
}
static void __devexit w6692_remove_pci(struct pci_dev *pdev)
{
w6692pci *card = pci_get_drvdata(pdev);
if (card)
w6692.ctrl(card->dch.inst.st, MGR_DELSTACK | REQUEST, NULL);
else
printk(KERN_WARNING "%s: drvdata allready removed\n", __FUNCTION__);
}
/* table entry in the PCI devices list */
typedef struct {
int vendor_id;
int device_id;
char *vendor_name;
char *card_name;
} PCI_ENTRY;
static const PCI_ENTRY id_list[] =
{
{PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, "Dynalink/AsusCom", "IS64PH"},
{PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, "Winbond", "W6692"},
{0, 0, NULL, NULL}
};
static struct pci_device_id w6692_ids[] __devinitdata = {
{ PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, PCI_ANY_ID, PCI_ANY_ID,
0, 0, (unsigned long) "Dynalink/AsusCom IS64PH" },
{ PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, PCI_ANY_ID, PCI_ANY_ID,
0, 0, (unsigned long) "Winbond W6692" },
{ }
};
MODULE_DEVICE_TABLE(pci, w6692_ids);
static struct pci_driver w6692_driver = {
name: "w6692",
probe: w6692_probe,
remove: __devexit_p(w6692_remove_pci),
id_table: w6692_ids,
};
static char W6692Name[] = "W6692";
static int __init w6692_init(void)
{
int err;
printk(KERN_INFO "Winbond W6692 PCI driver Rev. %s\n", mISDN_getrev(w6692_rev));
SET_MODULE_OWNER(&w6692);
w6692.name = W6692Name;
w6692.own_ctrl = w6692_manager;
w6692.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0;
w6692.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS |
ISDN_PID_L1_B_64HDLC;
w6692.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS;
w6692.prev = NULL;
w6692.next = NULL;
if ((err = mISDN_register(&w6692))) {
printk(KERN_ERR "Can't register Winbond W6692 PCI error(%d)\n", err);
return(err);
}
err = pci_register_driver(&w6692_driver);
if (err < 0)
goto out;
if (err == 0) {
err = -ENODEV;
pci_unregister_driver(&w6692_driver);
goto out;
}
return 0;
out:
mISDN_unregister(&w6692);
return err;
}
static void __exit w6692_cleanup(void)
{
int err;
if ((err = mISDN_unregister(&w6692))) {
printk(KERN_ERR "Can't unregister Winbond W6692 PCI error(%d)\n", err);
}
while(w6692.ilist) {
printk(KERN_ERR "Winbond W6692 PCI card struct not empty refs %d\n",
w6692.refcnt);
release_card(w6692.ilist);
}
pci_unregister_driver(&w6692_driver);
}
module_init(w6692_init);
module_exit(w6692_cleanup);