2001-08-02 14:55:45 +00:00
|
|
|
/* $Id$
|
|
|
|
|
2001-11-14 10:41:26 +00:00
|
|
|
* hfc_pci.c low level driver for CCD's hfc-pci based cards
|
2001-08-02 14:55:45 +00:00
|
|
|
*
|
|
|
|
* Author Werner Cornelius (werner@isdn4linux.de)
|
|
|
|
* based on existing driver for CCD hfc ISA cards
|
|
|
|
* type approval valid for HFC-S PCI A based card
|
|
|
|
*
|
|
|
|
* Copyright 1999 by Werner Cornelius (werner@isdn-development.de)
|
2001-11-14 10:41:26 +00:00
|
|
|
* Copyright 2001 by Karsten Keil (keil@isdn4linux.de)
|
2001-08-02 14:55:45 +00:00
|
|
|
*
|
|
|
|
* 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>
|
2002-07-08 12:27:45 +00:00
|
|
|
|
2005-12-08 18:32:53 +00:00
|
|
|
#include "channel.h"
|
2001-08-02 14:55:45 +00:00
|
|
|
#include "hfc_pci.h"
|
2003-07-21 12:44:46 +00:00
|
|
|
#include "layer1.h"
|
2001-08-02 14:55:45 +00:00
|
|
|
#include "debug.h"
|
2002-07-08 12:27:45 +00:00
|
|
|
#include <linux/isdn_compat.h>
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2002-07-07 21:20:17 +00:00
|
|
|
#define HFC_INFO(txt) printk(KERN_DEBUG txt)
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
extern const char *CardType[];
|
|
|
|
|
2002-07-08 12:27:45 +00:00
|
|
|
static const char *hfcpci_revision = "$Revision$";
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
/* table entry in the PCI devices list */
|
|
|
|
typedef struct {
|
|
|
|
int vendor_id;
|
|
|
|
int device_id;
|
|
|
|
char *vendor_name;
|
|
|
|
char *card_name;
|
|
|
|
} PCI_ENTRY;
|
|
|
|
|
|
|
|
#define NT_T1_COUNT 20 /* number of 3.125ms interrupts for G2 timeout */
|
|
|
|
#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */
|
|
|
|
#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */
|
|
|
|
|
2001-09-30 17:09:23 +00:00
|
|
|
#ifndef PCI_VENDOR_ID_CCD
|
2001-08-02 14:55:45 +00:00
|
|
|
#define PCI_VENDOR_ID_CCD 0x1397
|
|
|
|
#define PCI_DEVICE_ID_CCD_2BD0 0x2BD0
|
|
|
|
#define PCI_DEVICE_ID_CCD_B000 0xB000
|
|
|
|
#define PCI_DEVICE_ID_CCD_B006 0xB006
|
|
|
|
#define PCI_DEVICE_ID_CCD_B007 0xB007
|
|
|
|
#define PCI_DEVICE_ID_CCD_B008 0xB008
|
|
|
|
#define PCI_DEVICE_ID_CCD_B009 0xB009
|
|
|
|
#define PCI_DEVICE_ID_CCD_B00A 0xB00A
|
|
|
|
#define PCI_DEVICE_ID_CCD_B00B 0xB00B
|
|
|
|
#define PCI_DEVICE_ID_CCD_B00C 0xB00C
|
|
|
|
#define PCI_DEVICE_ID_CCD_B100 0xB100
|
|
|
|
|
|
|
|
#define PCI_VENDOR_ID_ASUSTEK 0x1043
|
|
|
|
#define PCI_DEVICE_ID_ASUSTEK_0675 0x0675
|
|
|
|
|
|
|
|
#define PCI_VENDOR_ID_BERKOM 0x0871
|
|
|
|
#define PCI_DEVICE_ID_BERKOM_A1T 0xFFA1
|
|
|
|
#define PCI_DEVICE_ID_BERKOM_T_CONCEPT 0xFFA2
|
|
|
|
|
|
|
|
#define PCI_VENDOR_ID_ANIGMA 0x1051
|
|
|
|
#define PCI_DEVICE_ID_ANIGMA_MC145575 0x0100
|
|
|
|
|
|
|
|
#define PCI_VENDOR_ID_ZOLTRIX 0x15b0
|
|
|
|
#define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2BD0
|
|
|
|
|
|
|
|
#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_E 0x0070
|
|
|
|
#define PCI_DEVICE_ID_DIGI_DF_M_E 0x0071
|
|
|
|
#define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A 0x0072
|
|
|
|
#define PCI_DEVICE_ID_DIGI_DF_M_A 0x0073
|
|
|
|
|
|
|
|
#define PCI_VENDOR_ID_ABOCOM 0x13D1
|
|
|
|
#define PCI_DEVICE_ID_ABOCOM_2BD1 0x2BD1
|
2001-09-30 17:09:23 +00:00
|
|
|
#endif
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-04-08 13:39:58 +00:00
|
|
|
#ifndef PCI_VENDOR_ID_SITECOM
|
|
|
|
#define PCI_VENDOR_ID_SITECOM 0x182D
|
2005-11-07 14:55:30 +00:00
|
|
|
#define PCI_DEVICE_ID_SITECOM_DC105V2 0x3069
|
2005-04-08 13:39:58 +00:00
|
|
|
#endif
|
|
|
|
|
2006-03-02 14:39:35 +00:00
|
|
|
/* new device IDs, obsolete when include/linux/pci_ids.h will be updated */
|
|
|
|
#ifndef PCI_DEVICE_ID_CCD_B700
|
|
|
|
#define PCI_DEVICE_ID_CCD_B700 0xb700
|
|
|
|
#endif
|
|
|
|
#ifndef PCI_DEVICE_ID_CCD_B701
|
|
|
|
#define PCI_DEVICE_ID_CCD_B701 0xb701
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2001-08-02 14:55:45 +00:00
|
|
|
static const PCI_ENTRY id_list[] =
|
|
|
|
{
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, "CCD/Billion/Asuscom", "2BD0"},
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, "Billion", "B000"},
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, "Billion", "B006"},
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, "Billion", "B007"},
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, "Billion", "B008"},
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, "Billion", "B009"},
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, "Billion", "B00A"},
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, "Billion", "B00B"},
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, "Billion", "B00C"},
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, "Seyeon", "B100"},
|
2006-03-02 14:39:35 +00:00
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B700, "Primux II S0", "B700"},
|
|
|
|
{PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B701, "Primux II S0 NT", "B701"},
|
2001-08-02 14:55:45 +00:00
|
|
|
{PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, "Abocom/Magitek", "2BD1"},
|
|
|
|
{PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, "Asuscom/Askey", "675"},
|
|
|
|
{PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, "German telekom", "T-Concept"},
|
|
|
|
{PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, "German telekom", "A1T"},
|
|
|
|
{PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, "Motorola MC145575", "MC145575"},
|
|
|
|
{PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, "Zoltrix", "2BD0"},
|
|
|
|
{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,"Digi International", "Digi DataFire Micro V IOM2 (Europe)"},
|
|
|
|
{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,"Digi International", "Digi DataFire Micro V (Europe)"},
|
|
|
|
{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,"Digi International", "Digi DataFire Micro V IOM2 (North America)"},
|
|
|
|
{PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,"Digi International", "Digi DataFire Micro V (North America)"},
|
2005-11-07 14:55:30 +00:00
|
|
|
{PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2,"Sitecom Connectivity", "DC-105 ISDN TA"},
|
2001-08-02 14:55:45 +00:00
|
|
|
{0, 0, NULL, NULL},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct hfcPCI_hw {
|
2003-07-27 11:14:19 +00:00
|
|
|
unsigned char cirm;
|
|
|
|
unsigned char ctmt;
|
|
|
|
unsigned char clkdel;
|
|
|
|
unsigned char states;
|
|
|
|
unsigned char conn;
|
|
|
|
unsigned char mst_m;
|
|
|
|
unsigned char int_m1;
|
|
|
|
unsigned char int_m2;
|
|
|
|
unsigned char sctrl;
|
|
|
|
unsigned char sctrl_r;
|
|
|
|
unsigned char sctrl_e;
|
|
|
|
unsigned char trm;
|
|
|
|
unsigned char fifo_en;
|
|
|
|
unsigned char bswapped;
|
|
|
|
unsigned char nt_mode;
|
|
|
|
int nt_timer;
|
|
|
|
struct pci_dev *dev;
|
|
|
|
unsigned char *pci_io; /* start of PCI IO memory */
|
2005-12-11 17:26:34 +00:00
|
|
|
dma_addr_t dmahandle;
|
2003-07-27 11:14:19 +00:00
|
|
|
void *fifos; /* FIFO memory */
|
|
|
|
int last_bfifo_cnt[2]; /* marker saving last b-fifo frame count */
|
|
|
|
struct timer_list timer;
|
2001-08-02 14:55:45 +00:00
|
|
|
};
|
|
|
|
|
2001-11-14 10:41:26 +00:00
|
|
|
typedef struct hfcPCI_hw hfcPCI_hw_t;
|
|
|
|
|
2001-09-29 20:05:01 +00:00
|
|
|
#define SPIN_DEBUG
|
2001-11-14 10:41:26 +00:00
|
|
|
#define HFC_CFG_MASTER 1
|
|
|
|
#define HFC_CFG_SLAVE 2
|
|
|
|
#define HFC_CFG_PCM 3
|
|
|
|
#define HFC_CFG_2HFC 4
|
|
|
|
#define HFC_CFG_SLAVEHFC 5
|
2002-07-07 21:20:17 +00:00
|
|
|
#define HFC_CFG_NEG_F0 6
|
|
|
|
#define HFC_CFG_SW_DD_DU 7
|
2001-09-29 20:05:01 +00:00
|
|
|
|
2001-08-02 14:55:45 +00:00
|
|
|
typedef struct _hfc_pci {
|
2004-06-17 12:31:14 +00:00
|
|
|
struct list_head list;
|
|
|
|
u_char subtyp;
|
|
|
|
u_char chanlimit;
|
|
|
|
u_long cfg;
|
|
|
|
u_int irq;
|
|
|
|
u_int irqcnt;
|
|
|
|
hfcPCI_hw_t hw;
|
2005-11-07 14:53:57 +00:00
|
|
|
spinlock_t lock;
|
2005-12-08 18:32:53 +00:00
|
|
|
channel_t dch;
|
|
|
|
channel_t bch[2];
|
2001-08-02 14:55:45 +00:00
|
|
|
} hfc_pci_t;
|
|
|
|
|
|
|
|
/* Interface functions */
|
2005-11-09 16:54:53 +00:00
|
|
|
static void
|
|
|
|
enable_hwirq(hfc_pci_t *hc)
|
|
|
|
{
|
|
|
|
hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE;
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
disable_hwirq(hfc_pci_t *hc)
|
|
|
|
{
|
|
|
|
hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE);
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
|
|
|
|
}
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
/******************************************/
|
|
|
|
/* free hardware resources used by driver */
|
|
|
|
/******************************************/
|
|
|
|
void
|
|
|
|
release_io_hfcpci(hfc_pci_t *hc)
|
|
|
|
{
|
2005-11-09 16:54:53 +00:00
|
|
|
hc->hw.int_m2 = 0; /* interrupt output off ! */
|
|
|
|
disable_hwirq(hc);
|
2003-07-27 11:14:19 +00:00
|
|
|
Write_hfc(hc, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */
|
|
|
|
mdelay(10); /* Timeout 10ms */
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.cirm = 0; /* Reset Off */
|
|
|
|
Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
|
2003-07-27 11:14:19 +00:00
|
|
|
pci_write_config_word(hc->hw.dev, PCI_COMMAND, 0); /* disable memory mapped ports + busmaster */
|
2001-08-02 14:55:45 +00:00
|
|
|
del_timer(&hc->hw.timer);
|
2005-12-11 17:26:34 +00:00
|
|
|
pci_free_consistent(hc->hw.dev, 0x8000, hc->hw.fifos, hc->hw.dmahandle);
|
2002-09-17 12:17:21 +00:00
|
|
|
iounmap((void *)hc->hw.pci_io);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/********************************************************************************/
|
|
|
|
/* function called to reset the HFC PCI chip. A complete software reset of chip */
|
|
|
|
/* and fifos is done. */
|
|
|
|
/********************************************************************************/
|
|
|
|
static void
|
|
|
|
reset_hfcpci(hfc_pci_t *hc)
|
|
|
|
{
|
2001-11-16 01:49:21 +00:00
|
|
|
u_char val;
|
2002-04-29 23:26:30 +00:00
|
|
|
int cnt = 0;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2002-07-07 21:20:17 +00:00
|
|
|
HFC_INFO("reset_hfcpci: entered\n");
|
2002-04-29 23:26:30 +00:00
|
|
|
val = Read_hfc(hc, HFCPCI_CHIP_ID);
|
|
|
|
printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val);
|
2003-07-27 11:14:19 +00:00
|
|
|
pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */
|
2005-11-09 16:54:53 +00:00
|
|
|
disable_hwirq(hc);
|
2003-07-27 11:14:19 +00:00
|
|
|
pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER); /* enable memory ports + busmaster */
|
2002-04-29 23:26:30 +00:00
|
|
|
val = Read_hfc(hc, HFCPCI_STATUS);
|
|
|
|
printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val);
|
2003-07-27 11:14:19 +00:00
|
|
|
hc->hw.cirm = HFCPCI_RESET; /* Reset On */
|
2001-08-02 14:55:45 +00:00
|
|
|
Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
|
|
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
2003-07-27 11:14:19 +00:00
|
|
|
mdelay(10); /* Timeout 10ms */
|
|
|
|
hc->hw.cirm = 0; /* Reset Off */
|
2001-08-02 14:55:45 +00:00
|
|
|
Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
|
2001-11-19 14:54:00 +00:00
|
|
|
val = Read_hfc(hc, HFCPCI_STATUS);
|
2002-04-29 23:26:30 +00:00
|
|
|
printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val);
|
|
|
|
while (cnt < 50000) { /* max 50000 us */
|
|
|
|
udelay(5);
|
|
|
|
cnt += 5;
|
|
|
|
val = Read_hfc(hc, HFCPCI_STATUS);
|
|
|
|
if (!(val & 2))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt);
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
hc->hw.fifo_en = 0x30; /* only D fifos enabled */
|
|
|
|
|
|
|
|
hc->hw.bswapped = 0; /* no exchange */
|
|
|
|
hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER;
|
|
|
|
hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */
|
|
|
|
hc->hw.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */
|
|
|
|
hc->hw.sctrl_r = 0;
|
|
|
|
hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE; /* S/T Auto awake */
|
2002-07-07 21:20:17 +00:00
|
|
|
hc->hw.mst_m = 0;
|
2001-11-14 10:41:26 +00:00
|
|
|
if (test_bit(HFC_CFG_MASTER, &hc->cfg))
|
2002-07-07 21:20:17 +00:00
|
|
|
hc->hw.mst_m |= HFCPCI_MASTER; /* HFC Master Mode */
|
|
|
|
if (test_bit(HFC_CFG_NEG_F0, &hc->cfg))
|
|
|
|
hc->hw.mst_m |= HFCPCI_F0_NEGATIV;
|
2001-08-02 14:55:45 +00:00
|
|
|
if (hc->hw.nt_mode) {
|
|
|
|
hc->hw.clkdel = CLKDEL_NT; /* ST-Bit delay for NT-Mode */
|
|
|
|
hc->hw.sctrl |= SCTRL_MODE_NT; /* NT-MODE */
|
|
|
|
hc->hw.states = 1; /* G1 */
|
|
|
|
} else {
|
|
|
|
hc->hw.clkdel = CLKDEL_TE; /* ST-Bit delay for TE-Mode */
|
|
|
|
hc->hw.states = 2; /* F2 */
|
|
|
|
}
|
|
|
|
Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
|
|
|
|
Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
|
|
|
|
Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel);
|
|
|
|
Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e);
|
|
|
|
Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
|
|
|
|
|
|
|
|
hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC |
|
|
|
|
HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER;
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
|
|
|
|
|
|
|
|
/* Clear already pending ints */
|
|
|
|
if (Read_hfc(hc, HFCPCI_INT_S1));
|
|
|
|
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states);
|
|
|
|
udelay(10);
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, hc->hw.states);
|
|
|
|
|
|
|
|
Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
|
|
|
|
Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
|
|
|
|
Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
|
|
|
|
|
|
|
|
/* Init GCI/IOM2 in master mode */
|
|
|
|
/* Slots 0 and 1 are set for B-chan 1 and 2 */
|
|
|
|
/* D- and monitor/CI channel are not enabled */
|
|
|
|
/* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */
|
|
|
|
/* STIO2 is used as data input, B1+B2 from IOM->ST */
|
|
|
|
/* ST B-channel send disabled -> continous 1s */
|
|
|
|
/* The IOM slots are always enabled */
|
2002-07-07 21:20:17 +00:00
|
|
|
if (test_bit(HFC_CFG_PCM, &hc->cfg)) {
|
|
|
|
/* set data flow directions: connect B1,B2: HFC to/from PCM */
|
|
|
|
hc->hw.conn = 0x09;
|
|
|
|
} else {
|
|
|
|
hc->hw.conn = 0x36; /* set data flow directions */
|
|
|
|
if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) {
|
2002-07-08 12:53:57 +00:00
|
|
|
Write_hfc(hc, HFCPCI_B1_SSL, 0xC0);
|
|
|
|
Write_hfc(hc, HFCPCI_B2_SSL, 0xC1);
|
|
|
|
Write_hfc(hc, HFCPCI_B1_RSL, 0xC0);
|
|
|
|
Write_hfc(hc, HFCPCI_B2_RSL, 0xC1);
|
2002-07-07 21:20:17 +00:00
|
|
|
} else {
|
|
|
|
Write_hfc(hc, HFCPCI_B1_SSL, 0x80);
|
|
|
|
Write_hfc(hc, HFCPCI_B2_SSL, 0x81);
|
|
|
|
Write_hfc(hc, HFCPCI_B1_RSL, 0x80);
|
|
|
|
Write_hfc(hc, HFCPCI_B2_RSL, 0x81);
|
|
|
|
}
|
|
|
|
}
|
2001-08-02 14:55:45 +00:00
|
|
|
Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
|
2001-11-16 01:49:21 +00:00
|
|
|
val = Read_hfc(hc, HFCPCI_INT_S2);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************/
|
|
|
|
/* Timer function called when kernel timer expires */
|
|
|
|
/***************************************************/
|
|
|
|
static void
|
|
|
|
hfcpci_Timer(hfc_pci_t *hc)
|
|
|
|
{
|
|
|
|
hc->hw.timer.expires = jiffies + 75;
|
|
|
|
/* WD RESET */
|
|
|
|
/* WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80);
|
|
|
|
add_timer(&hc->hw.timer);
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/************************************************/
|
|
|
|
/* select a b-channel entry matching and active */
|
|
|
|
/************************************************/
|
2005-12-08 18:32:53 +00:00
|
|
|
static channel_t *
|
2001-08-02 14:55:45 +00:00
|
|
|
Sel_BCS(hfc_pci_t *hc, int channel)
|
|
|
|
{
|
2005-12-08 18:32:53 +00:00
|
|
|
if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) &&
|
|
|
|
(hc->bch[0].channel & channel))
|
2001-08-02 14:55:45 +00:00
|
|
|
return (&hc->bch[0]);
|
2005-12-08 18:32:53 +00:00
|
|
|
else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) &&
|
|
|
|
(hc->bch[1].channel & channel))
|
2001-08-02 14:55:45 +00:00
|
|
|
return (&hc->bch[1]);
|
|
|
|
else
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************/
|
|
|
|
/* clear the desired B-channel rx fifo */
|
|
|
|
/***************************************/
|
|
|
|
static void hfcpci_clear_fifo_rx(hfc_pci_t *hc, int fifo)
|
|
|
|
{ u_char fifo_state;
|
|
|
|
bzfifo_type *bzr;
|
|
|
|
|
|
|
|
if (fifo) {
|
|
|
|
bzr = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b2;
|
|
|
|
fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX;
|
|
|
|
} else {
|
|
|
|
bzr = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b1;
|
|
|
|
fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX;
|
|
|
|
}
|
|
|
|
if (fifo_state)
|
|
|
|
hc->hw.fifo_en ^= fifo_state;
|
|
|
|
Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
|
|
|
|
hc->hw.last_bfifo_cnt[fifo] = 0;
|
|
|
|
bzr->f1 = MAX_B_FRAMES;
|
|
|
|
bzr->f2 = bzr->f1; /* init F pointers to remain constant */
|
2005-12-11 17:26:34 +00:00
|
|
|
bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1);
|
|
|
|
bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16(le16_to_cpu(bzr->za[MAX_B_FRAMES].z1));
|
2001-08-02 14:55:45 +00:00
|
|
|
if (fifo_state)
|
|
|
|
hc->hw.fifo_en |= fifo_state;
|
|
|
|
Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************/
|
|
|
|
/* clear the desired B-channel tx fifo */
|
|
|
|
/***************************************/
|
|
|
|
static void hfcpci_clear_fifo_tx(hfc_pci_t *hc, int fifo)
|
|
|
|
{ u_char fifo_state;
|
|
|
|
bzfifo_type *bzt;
|
|
|
|
|
|
|
|
if (fifo) {
|
|
|
|
bzt = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b2;
|
|
|
|
fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX;
|
|
|
|
} else {
|
|
|
|
bzt = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b1;
|
|
|
|
fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX;
|
|
|
|
}
|
|
|
|
if (fifo_state)
|
|
|
|
hc->hw.fifo_en ^= fifo_state;
|
|
|
|
Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
|
2001-09-29 20:05:01 +00:00
|
|
|
if (hc->bch[fifo].debug & L1_DEB_HSCX)
|
2005-12-11 17:26:34 +00:00
|
|
|
mISDN_debugprint(&hc->bch[fifo].inst,
|
|
|
|
"hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x) state(%x)",
|
|
|
|
fifo, bzt->f1, bzt->f2,
|
|
|
|
le16_to_cpu(bzt->za[MAX_B_FRAMES].z1),
|
|
|
|
le16_to_cpu(bzt->za[MAX_B_FRAMES].z2),
|
|
|
|
fifo_state);
|
2001-09-29 20:05:01 +00:00
|
|
|
bzt->f2 = MAX_B_FRAMES;
|
2002-09-16 22:03:51 +00:00
|
|
|
bzt->f1 = bzt->f2; /* init F pointers to remain constant */
|
2005-12-11 17:26:34 +00:00
|
|
|
bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1);
|
|
|
|
bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16(le16_to_cpu(bzt->za[MAX_B_FRAMES].z1 - 1));
|
2001-08-02 14:55:45 +00:00
|
|
|
if (fifo_state)
|
|
|
|
hc->hw.fifo_en |= fifo_state;
|
|
|
|
Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
|
2001-09-29 20:05:01 +00:00
|
|
|
if (hc->bch[fifo].debug & L1_DEB_HSCX)
|
2005-12-11 17:26:34 +00:00
|
|
|
mISDN_debugprint(&hc->bch[fifo].inst,
|
|
|
|
"hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)",
|
|
|
|
fifo, bzt->f1, bzt->f2,
|
|
|
|
le16_to_cpu(bzt->za[MAX_B_FRAMES].z1),
|
|
|
|
le16_to_cpu(bzt->za[MAX_B_FRAMES].z2));
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************/
|
|
|
|
/* read a complete B-frame out of the buffer */
|
|
|
|
/*********************************************/
|
2005-12-08 18:32:53 +00:00
|
|
|
static void
|
|
|
|
hfcpci_empty_fifo(channel_t *bch, bzfifo_type * bz, u_char * bdata, int count)
|
2001-08-02 14:55:45 +00:00
|
|
|
{
|
|
|
|
u_char *ptr, *ptr1, new_f2;
|
2001-11-02 23:28:21 +00:00
|
|
|
int total, maxlen, new_z2;
|
2001-08-02 14:55:45 +00:00
|
|
|
z_type *zp;
|
|
|
|
|
|
|
|
if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "hfcpci_empty_fifo");
|
2001-08-02 14:55:45 +00:00
|
|
|
zp = &bz->za[bz->f2]; /* point to Z-Regs */
|
2005-12-11 17:26:34 +00:00
|
|
|
new_z2 = le16_to_cpu(zp->z2) + count; /* new position in fifo */
|
2001-08-02 14:55:45 +00:00
|
|
|
if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
|
|
|
|
new_z2 -= B_FIFO_SIZE; /* buffer wrap */
|
|
|
|
new_f2 = (bz->f2 + 1) & MAX_B_FRAMES;
|
|
|
|
if ((count > MAX_DATA_SIZE + 3) || (count < 4) ||
|
2005-12-11 17:26:34 +00:00
|
|
|
(*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) {
|
2001-08-02 14:55:45 +00:00
|
|
|
if (bch->debug & L1_DEB_WARN)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count);
|
2001-08-02 14:55:45 +00:00
|
|
|
#ifdef ERROR_STATISTIC
|
|
|
|
bch->err_inv++;
|
|
|
|
#endif
|
2005-12-11 17:26:34 +00:00
|
|
|
bz->za[new_f2].z2 = cpu_to_le16(new_z2);
|
2001-08-02 14:55:45 +00:00
|
|
|
bz->f2 = new_f2; /* next buffer */
|
2005-12-08 18:32:53 +00:00
|
|
|
} else if (!(bch->rx_skb = alloc_stack_skb(count - 3, bch->up_headerlen)))
|
2001-08-02 14:55:45 +00:00
|
|
|
printk(KERN_WARNING "HFCPCI: receive out of memory\n");
|
|
|
|
else {
|
|
|
|
total = count;
|
|
|
|
count -= 3;
|
2005-12-08 18:32:53 +00:00
|
|
|
ptr = skb_put(bch->rx_skb, count);
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-12-11 17:26:34 +00:00
|
|
|
if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL)
|
2001-08-02 14:55:45 +00:00
|
|
|
maxlen = count; /* complete transfer */
|
|
|
|
else
|
2005-12-11 17:26:34 +00:00
|
|
|
maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(zp->z2); /* maximum */
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-12-11 17:26:34 +00:00
|
|
|
ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL); /* start of data */
|
2001-08-02 14:55:45 +00:00
|
|
|
memcpy(ptr, ptr1, maxlen); /* copy data */
|
|
|
|
count -= maxlen;
|
|
|
|
|
|
|
|
if (count) { /* rest remaining */
|
|
|
|
ptr += maxlen;
|
|
|
|
ptr1 = bdata; /* start of buffer */
|
|
|
|
memcpy(ptr, ptr1, count); /* rest */
|
|
|
|
}
|
2005-12-11 17:26:34 +00:00
|
|
|
bz->za[new_f2].z2 = cpu_to_le16(new_z2);
|
2001-08-02 14:55:45 +00:00
|
|
|
bz->f2 = new_f2; /* next buffer */
|
2005-12-08 18:32:53 +00:00
|
|
|
queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, bch->rx_skb);
|
|
|
|
bch->rx_skb = NULL;
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************/
|
|
|
|
/* D-channel receive procedure */
|
|
|
|
/*******************************/
|
|
|
|
static
|
|
|
|
int
|
|
|
|
receive_dmsg(hfc_pci_t *hc)
|
|
|
|
{
|
2005-12-08 18:32:53 +00:00
|
|
|
channel_t *dch = &hc->dch;
|
2001-08-02 14:55:45 +00:00
|
|
|
int maxlen;
|
|
|
|
int rcnt, total;
|
|
|
|
int count = 5;
|
|
|
|
u_char *ptr, *ptr1;
|
|
|
|
dfifo_type *df;
|
|
|
|
z_type *zp;
|
|
|
|
|
|
|
|
df = &((fifo_area *) (hc->hw.fifos))->d_chan.d_rx;
|
|
|
|
while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) {
|
|
|
|
zp = &df->za[df->f2 & D_FREG_MASK];
|
2005-12-11 17:26:34 +00:00
|
|
|
rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2);
|
2001-08-02 14:55:45 +00:00
|
|
|
if (rcnt < 0)
|
|
|
|
rcnt += D_FIFO_SIZE;
|
|
|
|
rcnt++;
|
|
|
|
if (dch->debug & L1_DEB_ISAC)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&dch->inst, "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
|
2005-12-11 17:26:34 +00:00
|
|
|
df->f1, df->f2,
|
|
|
|
le16_to_cpu(zp->z1),
|
|
|
|
le16_to_cpu(zp->z2),
|
|
|
|
rcnt);
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) ||
|
2005-12-11 17:26:34 +00:00
|
|
|
(df->data[le16_to_cpu(zp->z1)])) {
|
2001-08-02 14:55:45 +00:00
|
|
|
if (dch->debug & L1_DEB_WARN)
|
2005-12-11 17:26:34 +00:00
|
|
|
mISDN_debugprint(&dch->inst,
|
|
|
|
"empty_fifo hfcpci paket inv. len %d or crc %d",
|
|
|
|
rcnt,
|
|
|
|
df->data[le16_to_cpu(zp->z1)]);
|
2001-08-02 14:55:45 +00:00
|
|
|
#ifdef ERROR_STATISTIC
|
|
|
|
cs->err_rx++;
|
|
|
|
#endif
|
|
|
|
df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */
|
2005-12-11 17:26:34 +00:00
|
|
|
df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16((zp->z2 + rcnt) & (D_FIFO_SIZE - 1));
|
2005-12-08 18:32:53 +00:00
|
|
|
} else if ((dch->rx_skb = alloc_stack_skb(rcnt - 3, dch->up_headerlen))) {
|
2001-08-02 14:55:45 +00:00
|
|
|
total = rcnt;
|
|
|
|
rcnt -= 3;
|
2005-12-08 18:32:53 +00:00
|
|
|
ptr = skb_put(dch->rx_skb, rcnt);
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-12-11 17:26:34 +00:00
|
|
|
if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE)
|
2001-08-02 14:55:45 +00:00
|
|
|
maxlen = rcnt; /* complete transfer */
|
|
|
|
else
|
2005-12-11 17:26:34 +00:00
|
|
|
maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2); /* maximum */
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-12-11 17:26:34 +00:00
|
|
|
ptr1 = df->data + le16_to_cpu(zp->z2); /* start of data */
|
2001-08-02 14:55:45 +00:00
|
|
|
memcpy(ptr, ptr1, maxlen); /* copy data */
|
|
|
|
rcnt -= maxlen;
|
|
|
|
|
|
|
|
if (rcnt) { /* rest remaining */
|
|
|
|
ptr += maxlen;
|
|
|
|
ptr1 = df->data; /* start of buffer */
|
|
|
|
memcpy(ptr, ptr1, rcnt); /* rest */
|
|
|
|
}
|
|
|
|
df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */
|
2005-12-11 17:26:34 +00:00
|
|
|
df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16((le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1));
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
if (dch->debug & L1_DEB_ISAC_FIFO) {
|
2005-12-08 18:32:53 +00:00
|
|
|
char *t = dch->log;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-12-08 18:32:53 +00:00
|
|
|
count = dch->rx_skb->len;
|
|
|
|
ptr = dch->rx_skb->data;
|
2001-08-02 14:55:45 +00:00
|
|
|
t += sprintf(t, "hfcD_empty_fifo cnt %d", count);
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_QuickHex(t, ptr, count);
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_debugprint(&dch->inst, dch->log);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_queueup_newhead(&dch->inst, 0, PH_DATA_IND, MISDN_ID_ANY, dch->rx_skb);
|
|
|
|
dch->rx_skb = NULL;
|
2001-08-02 14:55:45 +00:00
|
|
|
} else
|
|
|
|
printk(KERN_WARNING "HFC-PCI: D receive out of memory\n");
|
|
|
|
}
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************************************/
|
|
|
|
/* check for transparent receive data and read max one threshold size if avail */
|
|
|
|
/*******************************************************************************/
|
|
|
|
int
|
2005-12-08 18:32:53 +00:00
|
|
|
hfcpci_empty_fifo_trans(channel_t *bch, bzfifo_type * bz, u_char * bdata)
|
2001-08-02 14:55:45 +00:00
|
|
|
{
|
|
|
|
unsigned short *z1r, *z2r;
|
|
|
|
int new_z2, fcnt, maxlen;
|
|
|
|
u_char *ptr, *ptr1;
|
|
|
|
|
|
|
|
z1r = &bz->za[MAX_B_FRAMES].z1; /* pointer to z reg */
|
|
|
|
z2r = z1r + 1;
|
|
|
|
|
2005-12-11 17:26:34 +00:00
|
|
|
if (!(fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r)))
|
2001-08-02 14:55:45 +00:00
|
|
|
return (0); /* no data avail */
|
|
|
|
|
|
|
|
if (fcnt <= 0)
|
|
|
|
fcnt += B_FIFO_SIZE; /* bytes actually buffered */
|
|
|
|
if (fcnt > HFCPCI_BTRANS_THRESHOLD)
|
|
|
|
fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */
|
|
|
|
|
2005-12-11 17:26:34 +00:00
|
|
|
new_z2 = le16_to_cpu(*z2r) + fcnt; /* new position in fifo */
|
2001-08-02 14:55:45 +00:00
|
|
|
if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
|
|
|
|
new_z2 -= B_FIFO_SIZE; /* buffer wrap */
|
|
|
|
|
2005-12-08 18:32:53 +00:00
|
|
|
if (!(bch->rx_skb = alloc_stack_skb(fcnt, bch->up_headerlen)))
|
2001-08-02 14:55:45 +00:00
|
|
|
printk(KERN_WARNING "HFCPCI: receive out of memory\n");
|
|
|
|
else {
|
2005-12-08 18:32:53 +00:00
|
|
|
ptr = skb_put(bch->rx_skb, fcnt);
|
2005-12-11 17:26:34 +00:00
|
|
|
if (le16_to_cpu(*z2r) + fcnt <= B_FIFO_SIZE + B_SUB_VAL)
|
2001-08-02 14:55:45 +00:00
|
|
|
maxlen = fcnt; /* complete transfer */
|
|
|
|
else
|
2005-12-11 17:26:34 +00:00
|
|
|
maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r); /* maximum */
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-12-11 17:26:34 +00:00
|
|
|
ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL); /* start of data */
|
2001-08-02 14:55:45 +00:00
|
|
|
memcpy(ptr, ptr1, maxlen); /* copy data */
|
|
|
|
fcnt -= maxlen;
|
|
|
|
|
|
|
|
if (fcnt) { /* rest remaining */
|
|
|
|
ptr += maxlen;
|
|
|
|
ptr1 = bdata; /* start of buffer */
|
|
|
|
memcpy(ptr, ptr1, fcnt); /* rest */
|
|
|
|
}
|
2005-12-08 18:32:53 +00:00
|
|
|
queue_ch_frame(bch, INDICATION, MISDN_ID_ANY, bch->rx_skb);
|
|
|
|
bch->rx_skb = NULL;
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
|
2005-12-11 17:26:34 +00:00
|
|
|
*z2r = cpu_to_le16(new_z2); /* new position */
|
2001-08-02 14:55:45 +00:00
|
|
|
return (1);
|
|
|
|
} /* hfcpci_empty_fifo_trans */
|
|
|
|
|
|
|
|
/**********************************/
|
|
|
|
/* B-channel main receive routine */
|
|
|
|
/**********************************/
|
|
|
|
void
|
2005-12-08 18:32:53 +00:00
|
|
|
main_rec_hfcpci(channel_t *bch)
|
2001-08-02 14:55:45 +00:00
|
|
|
{
|
2005-05-07 21:04:11 +00:00
|
|
|
hfc_pci_t *hc = bch->hw;
|
2001-08-02 14:55:45 +00:00
|
|
|
int rcnt, real_fifo;
|
|
|
|
int receive, count = 5;
|
|
|
|
bzfifo_type *bz;
|
|
|
|
u_char *bdata;
|
|
|
|
z_type *zp;
|
|
|
|
|
|
|
|
|
2001-11-14 10:41:26 +00:00
|
|
|
if ((bch->channel & 2) && (!hc->hw.bswapped)) {
|
2001-08-02 14:55:45 +00:00
|
|
|
bz = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b2;
|
|
|
|
bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.rxdat_b2;
|
|
|
|
real_fifo = 1;
|
|
|
|
} else {
|
|
|
|
bz = &((fifo_area *) (hc->hw.fifos))->b_chans.rxbz_b1;
|
|
|
|
bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.rxdat_b1;
|
|
|
|
real_fifo = 0;
|
|
|
|
}
|
|
|
|
Begin:
|
|
|
|
count--;
|
|
|
|
if (bz->f1 != bz->f2) {
|
|
|
|
if (bch->debug & L1_DEB_HSCX)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "hfcpci rec ch(%x) f1(%d) f2(%d)",
|
2001-08-02 14:55:45 +00:00
|
|
|
bch->channel, bz->f1, bz->f2);
|
|
|
|
zp = &bz->za[bz->f2];
|
|
|
|
|
2005-12-11 17:26:34 +00:00
|
|
|
rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2);
|
2001-08-02 14:55:45 +00:00
|
|
|
if (rcnt < 0)
|
|
|
|
rcnt += B_FIFO_SIZE;
|
|
|
|
rcnt++;
|
|
|
|
if (bch->debug & L1_DEB_HSCX)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)",
|
2005-12-11 17:26:34 +00:00
|
|
|
bch->channel, le16_to_cpu(zp->z1), le16_to_cpu(zp->z2), rcnt);
|
2005-12-08 18:32:53 +00:00
|
|
|
hfcpci_empty_fifo(bch, bz, bdata, rcnt);
|
2001-08-02 14:55:45 +00:00
|
|
|
rcnt = bz->f1 - bz->f2;
|
|
|
|
if (rcnt < 0)
|
|
|
|
rcnt += MAX_B_FRAMES + 1;
|
|
|
|
if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) {
|
|
|
|
rcnt = 0;
|
|
|
|
hfcpci_clear_fifo_rx(hc, real_fifo);
|
|
|
|
}
|
|
|
|
hc->hw.last_bfifo_cnt[real_fifo] = rcnt;
|
|
|
|
if (rcnt > 1)
|
|
|
|
receive = 1;
|
|
|
|
else
|
|
|
|
receive = 0;
|
2005-12-08 18:32:53 +00:00
|
|
|
} else if (test_bit(FLG_TRANSPARENT, &bch->Flags))
|
2001-08-02 14:55:45 +00:00
|
|
|
receive = hfcpci_empty_fifo_trans(bch, bz, bdata);
|
|
|
|
else
|
|
|
|
receive = 0;
|
|
|
|
if (count && receive)
|
|
|
|
goto Begin;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************/
|
|
|
|
/* D-channel send routine */
|
|
|
|
/**************************/
|
|
|
|
static void
|
|
|
|
hfcpci_fill_dfifo(hfc_pci_t *hc)
|
|
|
|
{
|
2005-12-08 18:32:53 +00:00
|
|
|
channel_t *dch = &hc->dch;
|
2001-08-02 14:55:45 +00:00
|
|
|
int fcnt;
|
|
|
|
int count, new_z1, maxlen;
|
|
|
|
dfifo_type *df;
|
|
|
|
u_char *src, *dst, new_f1;
|
|
|
|
|
|
|
|
if ((dch->debug & L1_DEB_ISAC) && !(dch->debug & L1_DEB_ISAC_FIFO))
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&dch->inst, "hfcpci_fill_dfifo");
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-12-08 18:32:53 +00:00
|
|
|
if (!dch->tx_skb)
|
|
|
|
return;
|
|
|
|
count = dch->tx_skb->len - dch->tx_idx;
|
2001-08-02 14:55:45 +00:00
|
|
|
if (count <= 0)
|
|
|
|
return;
|
|
|
|
df = &((fifo_area *) (hc->hw.fifos))->d_chan.d_tx;
|
|
|
|
|
|
|
|
if (dch->debug & L1_DEB_ISAC_FIFO)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo f1(%d) f2(%d) z1(f1)(%x)",
|
2001-08-02 14:55:45 +00:00
|
|
|
df->f1, df->f2,
|
2005-12-11 17:26:34 +00:00
|
|
|
le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1));
|
2001-08-02 14:55:45 +00:00
|
|
|
fcnt = df->f1 - df->f2; /* frame count actually buffered */
|
|
|
|
if (fcnt < 0)
|
|
|
|
fcnt += (MAX_D_FRAMES + 1); /* if wrap around */
|
|
|
|
if (fcnt > (MAX_D_FRAMES - 1)) {
|
|
|
|
if (dch->debug & L1_DEB_ISAC)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo more as 14 frames");
|
2001-08-02 14:55:45 +00:00
|
|
|
#ifdef ERROR_STATISTIC
|
|
|
|
cs->err_tx++;
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* now determine free bytes in FIFO buffer */
|
2005-12-11 17:26:34 +00:00
|
|
|
maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1;
|
2001-08-02 14:55:45 +00:00
|
|
|
if (maxlen <= 0)
|
|
|
|
maxlen += D_FIFO_SIZE; /* count now contains available bytes */
|
|
|
|
|
|
|
|
if (dch->debug & L1_DEB_ISAC)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo count(%ld/%d)",
|
2001-08-02 14:55:45 +00:00
|
|
|
count, maxlen);
|
|
|
|
if (count > maxlen) {
|
|
|
|
if (dch->debug & L1_DEB_ISAC)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&dch->inst, "hfcpci_fill_Dfifo no fifo mem");
|
2001-08-02 14:55:45 +00:00
|
|
|
return;
|
|
|
|
}
|
2005-12-11 17:26:34 +00:00
|
|
|
new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) & (D_FIFO_SIZE - 1);
|
2001-08-02 14:55:45 +00:00
|
|
|
new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1);
|
2005-12-08 18:32:53 +00:00
|
|
|
src = dch->tx_skb->data + dch->tx_idx; /* source pointer */
|
2005-12-11 17:26:34 +00:00
|
|
|
dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1);
|
|
|
|
maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); /* end fifo */
|
2001-08-02 14:55:45 +00:00
|
|
|
if (maxlen > count)
|
|
|
|
maxlen = count; /* limit size */
|
|
|
|
memcpy(dst, src, maxlen); /* first copy */
|
|
|
|
|
|
|
|
count -= maxlen; /* remaining bytes */
|
|
|
|
if (count) {
|
|
|
|
dst = df->data; /* start of buffer */
|
|
|
|
src += maxlen; /* new position */
|
|
|
|
memcpy(dst, src, count);
|
|
|
|
}
|
2005-12-11 17:26:34 +00:00
|
|
|
df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); /* for next buffer */
|
|
|
|
df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); /* new pos actual buffer */
|
2001-08-02 14:55:45 +00:00
|
|
|
df->f1 = new_f1; /* next frame */
|
|
|
|
if (dch->debug & L1_DEB_ISAC_FIFO) {
|
2005-12-08 18:32:53 +00:00
|
|
|
char *t = dch->log;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-12-08 18:32:53 +00:00
|
|
|
count = dch->tx_skb->len - dch->tx_idx;
|
|
|
|
src = dch->tx_skb->data + dch->tx_idx;
|
2001-08-02 14:55:45 +00:00
|
|
|
t += sprintf(t, "hfcD_fill_fifo cnt %d", count);
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_QuickHex(t, src, count);
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_debugprint(&dch->inst, dch->log);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2005-12-08 18:32:53 +00:00
|
|
|
dch->tx_idx = dch->tx_skb->len;
|
2001-08-02 14:55:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**************************/
|
|
|
|
/* B-channel send routine */
|
|
|
|
/**************************/
|
|
|
|
static void
|
2005-12-08 18:32:53 +00:00
|
|
|
hfcpci_fill_fifo(channel_t *bch)
|
2001-08-02 14:55:45 +00:00
|
|
|
{
|
2005-05-07 21:04:11 +00:00
|
|
|
hfc_pci_t *hc = bch->hw;
|
2003-06-21 21:39:54 +00:00
|
|
|
int maxlen, fcnt;
|
2001-08-02 14:55:45 +00:00
|
|
|
int count, new_z1;
|
|
|
|
bzfifo_type *bz;
|
|
|
|
u_char *bdata;
|
|
|
|
u_char new_f1, *src, *dst;
|
|
|
|
unsigned short *z1t, *z2t;
|
|
|
|
|
|
|
|
if ((bch->debug & L1_DEB_HSCX) && !(bch->debug & L1_DEB_HSCX_FIFO))
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "%s", __FUNCTION__);
|
2005-12-08 18:32:53 +00:00
|
|
|
if ((!bch->tx_skb) || bch->tx_skb->len <= 0)
|
2001-08-02 14:55:45 +00:00
|
|
|
return;
|
2005-12-08 18:32:53 +00:00
|
|
|
count = bch->tx_skb->len - bch->tx_idx;
|
2001-11-14 10:41:26 +00:00
|
|
|
if ((bch->channel & 2) && (!hc->hw.bswapped)) {
|
2001-08-02 14:55:45 +00:00
|
|
|
bz = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b2;
|
|
|
|
bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.txdat_b2;
|
|
|
|
} else {
|
|
|
|
bz = &((fifo_area *) (hc->hw.fifos))->b_chans.txbz_b1;
|
|
|
|
bdata = ((fifo_area *) (hc->hw.fifos))->b_chans.txdat_b1;
|
|
|
|
}
|
|
|
|
|
2005-12-08 18:32:53 +00:00
|
|
|
if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
|
2001-08-02 14:55:45 +00:00
|
|
|
z1t = &bz->za[MAX_B_FRAMES].z1;
|
|
|
|
z2t = z1t + 1;
|
|
|
|
if (bch->debug & L1_DEB_HSCX)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo_trans ch(%x) cnt(%d) z1(%x) z2(%x)",
|
2005-12-11 17:26:34 +00:00
|
|
|
bch->channel, count, le16_to_cpu(*z1t), le16_to_cpu(*z2t));
|
|
|
|
fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t);
|
2001-08-02 14:55:45 +00:00
|
|
|
if (fcnt <= 0)
|
|
|
|
fcnt += B_FIFO_SIZE; /* fcnt contains available bytes in fifo */
|
|
|
|
fcnt = B_FIFO_SIZE - fcnt; /* remaining bytes to send */
|
|
|
|
next_t_frame:
|
|
|
|
if (fcnt < (2 * HFCPCI_BTRANS_THRESHOLD)) {
|
2005-12-08 18:32:53 +00:00
|
|
|
count = bch->tx_skb->len - bch->tx_idx;
|
|
|
|
if (count >= B_FIFO_SIZE - fcnt)
|
|
|
|
count = B_FIFO_SIZE - fcnt -1;
|
|
|
|
if (count <= 0)
|
|
|
|
return;
|
|
|
|
/* data is suitable for fifo */
|
2005-12-11 17:26:34 +00:00
|
|
|
new_z1 = le16_to_cpu(*z1t) + count; /* new buffer Position */
|
2005-12-08 18:32:53 +00:00
|
|
|
if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
|
|
|
|
new_z1 -= B_FIFO_SIZE; /* buffer wrap */
|
|
|
|
src = bch->tx_skb->data + bch->tx_idx; /* source pointer */
|
2005-12-11 17:26:34 +00:00
|
|
|
dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL);
|
|
|
|
maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); /* end of fifo */
|
2005-12-08 18:32:53 +00:00
|
|
|
if (bch->debug & L1_DEB_HSCX_FIFO)
|
|
|
|
mISDN_debugprint(&bch->inst, "hfcpci_FFt fcnt(%d) maxl(%d) nz1(%x) dst(%p)",
|
|
|
|
fcnt, maxlen, new_z1, dst);
|
|
|
|
fcnt += count;
|
|
|
|
bch->tx_idx += count;
|
|
|
|
if (maxlen > count)
|
|
|
|
maxlen = count; /* limit size */
|
|
|
|
memcpy(dst, src, maxlen); /* first copy */
|
|
|
|
count -= maxlen; /* remaining bytes */
|
|
|
|
if (count) {
|
|
|
|
dst = bdata; /* start of buffer */
|
|
|
|
src += maxlen; /* new position */
|
|
|
|
memcpy(dst, src, count);
|
|
|
|
}
|
2005-12-11 17:26:34 +00:00
|
|
|
*z1t = cpu_to_le16(new_z1); /* now send data */
|
2005-12-08 18:32:53 +00:00
|
|
|
if (bch->tx_idx < bch->tx_skb->len)
|
|
|
|
return;
|
|
|
|
dev_kfree_skb(bch->tx_skb);
|
|
|
|
bch->tx_idx = 0;
|
|
|
|
if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
|
|
|
|
bch->tx_skb = bch->next_skb;
|
|
|
|
if (bch->tx_skb) {
|
|
|
|
mISDN_head_t *hh = mISDN_HEAD_P(bch->tx_skb);
|
2005-05-10 14:18:13 +00:00
|
|
|
bch->next_skb = NULL;
|
2005-12-08 18:32:53 +00:00
|
|
|
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
|
|
|
|
queue_ch_frame(bch, CONFIRM, hh->dinfo, NULL);
|
2001-08-02 14:55:45 +00:00
|
|
|
goto next_t_frame;
|
2005-10-27 19:23:39 +00:00
|
|
|
} else {
|
2005-12-08 18:32:53 +00:00
|
|
|
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
printk(KERN_WARNING "hfcB tx irq TX_NEXT without skb\n");
|
2005-10-27 19:23:39 +00:00
|
|
|
}
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2005-12-08 18:32:53 +00:00
|
|
|
bch->tx_skb = NULL;
|
|
|
|
test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (bch->debug & L1_DEB_HSCX)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)",
|
2002-09-16 23:49:38 +00:00
|
|
|
__FUNCTION__, bch->channel, bz->f1, bz->f2, bz->za[bz->f1].z1);
|
2001-08-02 14:55:45 +00:00
|
|
|
fcnt = bz->f1 - bz->f2; /* frame count actually buffered */
|
|
|
|
if (fcnt < 0)
|
|
|
|
fcnt += (MAX_B_FRAMES + 1); /* if wrap around */
|
|
|
|
if (fcnt > (MAX_B_FRAMES - 1)) {
|
|
|
|
if (bch->debug & L1_DEB_HSCX)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "hfcpci_fill_Bfifo more as 14 frames");
|
2001-08-02 14:55:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* now determine free bytes in FIFO buffer */
|
2005-12-11 17:26:34 +00:00
|
|
|
maxlen = le16_to_cpu(bz->za[bz->f2].z2) - le16_to_cpu(bz->za[bz->f1].z1) - 1;
|
2001-08-02 14:55:45 +00:00
|
|
|
if (maxlen <= 0)
|
|
|
|
maxlen += B_FIFO_SIZE; /* count now contains available bytes */
|
|
|
|
|
|
|
|
if (bch->debug & L1_DEB_HSCX)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo ch(%x) count(%ld/%d),%lx",
|
2001-08-02 14:55:45 +00:00
|
|
|
bch->channel, count,
|
|
|
|
maxlen, current->state);
|
|
|
|
|
|
|
|
if (maxlen < count) {
|
|
|
|
if (bch->debug & L1_DEB_HSCX)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "hfcpci_fill_fifo no fifo mem");
|
2001-08-02 14:55:45 +00:00
|
|
|
return;
|
|
|
|
}
|
2005-12-11 17:26:34 +00:00
|
|
|
new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count; /* new buffer Position */
|
2001-08-02 14:55:45 +00:00
|
|
|
if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
|
|
|
|
new_z1 -= B_FIFO_SIZE; /* buffer wrap */
|
|
|
|
|
|
|
|
new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES);
|
2005-12-08 18:32:53 +00:00
|
|
|
src = bch->tx_skb->data + bch->tx_idx; /* source pointer */
|
2005-12-11 17:26:34 +00:00
|
|
|
dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL);
|
|
|
|
maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1); /* end fifo */
|
2001-08-02 14:55:45 +00:00
|
|
|
if (maxlen > count)
|
|
|
|
maxlen = count; /* limit size */
|
|
|
|
memcpy(dst, src, maxlen); /* first copy */
|
|
|
|
|
|
|
|
count -= maxlen; /* remaining bytes */
|
|
|
|
if (count) {
|
|
|
|
dst = bdata; /* start of buffer */
|
|
|
|
src += maxlen; /* new position */
|
|
|
|
memcpy(dst, src, count);
|
|
|
|
}
|
2005-12-11 17:26:34 +00:00
|
|
|
bz->za[new_f1].z1 = cpu_to_le16(new_z1); /* for next buffer */
|
2001-08-02 14:55:45 +00:00
|
|
|
bz->f1 = new_f1; /* next frame */
|
2005-12-08 18:32:53 +00:00
|
|
|
dev_kfree_skb(bch->tx_skb);
|
|
|
|
bch->tx_idx = 0;
|
|
|
|
if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
|
|
|
|
bch->tx_skb = bch->next_skb;
|
|
|
|
if (bch->tx_skb) {
|
|
|
|
mISDN_head_t *hh = mISDN_HEAD_P(bch->tx_skb);
|
|
|
|
bch->next_skb = NULL;
|
|
|
|
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
|
|
|
|
queue_ch_frame(bch, CONFIRM, hh->dinfo, NULL);
|
|
|
|
} else {
|
|
|
|
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
|
|
|
|
test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
|
|
|
|
printk(KERN_WARNING "hfcB tx irq TX_NEXT without skb\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bch->tx_skb = NULL;
|
|
|
|
test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
|
|
|
|
}
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
|
2001-09-30 17:10:25 +00:00
|
|
|
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-05-10 14:18:13 +00:00
|
|
|
/***************************/
|
|
|
|
/* handle L1 state changes */
|
|
|
|
/***************************/
|
|
|
|
|
|
|
|
static void
|
2005-12-08 18:32:53 +00:00
|
|
|
ph_state_change(channel_t *dch)
|
2005-05-10 14:18:13 +00:00
|
|
|
{
|
|
|
|
hfc_pci_t *hc = dch->inst.privat;
|
|
|
|
u_int prim = PH_SIGNAL | INDICATION;
|
|
|
|
u_int para = 0;
|
|
|
|
|
|
|
|
if (!hc->hw.nt_mode) {
|
|
|
|
if (dch->debug)
|
|
|
|
printk(KERN_DEBUG "%s: TE newstate %x\n",
|
2005-12-08 18:32:53 +00:00
|
|
|
__FUNCTION__, dch->state);
|
|
|
|
switch (dch->state) {
|
2005-05-10 14:18:13 +00:00
|
|
|
case (0):
|
|
|
|
prim = PH_CONTROL | INDICATION;
|
|
|
|
para = HW_RESET;
|
|
|
|
break;
|
|
|
|
case (3):
|
|
|
|
prim = PH_CONTROL | INDICATION;
|
|
|
|
para = HW_DEACTIVATE;
|
|
|
|
break;
|
|
|
|
case (5):
|
|
|
|
case (8):
|
|
|
|
para = ANYSIGNAL;
|
|
|
|
break;
|
|
|
|
case (6):
|
|
|
|
para = INFO2;
|
|
|
|
break;
|
|
|
|
case (7):
|
|
|
|
para = INFO4_P8;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (dch->debug)
|
|
|
|
printk(KERN_DEBUG "%s: NT newstate %x\n",
|
2005-12-08 18:32:53 +00:00
|
|
|
__FUNCTION__, dch->state);
|
|
|
|
switch (dch->state) {
|
2005-05-10 14:18:13 +00:00
|
|
|
case (2):
|
|
|
|
if (hc->hw.nt_timer < 0) {
|
|
|
|
hc->hw.nt_timer = 0;
|
|
|
|
hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
|
|
|
|
/* Clear already pending ints */
|
|
|
|
if (Read_hfc(hc, HFCPCI_INT_S1));
|
|
|
|
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE);
|
|
|
|
udelay(10);
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, 4);
|
2005-12-08 18:32:53 +00:00
|
|
|
dch->state = 4;
|
2005-05-10 14:18:13 +00:00
|
|
|
} else {
|
|
|
|
hc->hw.int_m1 |= HFCPCI_INTS_TIMER;
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
|
|
|
|
hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER;
|
|
|
|
hc->hw.ctmt |= HFCPCI_TIM3_125;
|
|
|
|
Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
|
|
|
|
Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
|
|
|
|
hc->hw.nt_timer = NT_T1_COUNT;
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); /* allow G2 -> G3 transition */
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
case (1):
|
|
|
|
prim = PH_DEACTIVATE | INDICATION;
|
|
|
|
para = 0;
|
|
|
|
hc->hw.nt_timer = 0;
|
|
|
|
hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
|
2005-12-08 18:32:53 +00:00
|
|
|
test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
|
2005-05-10 14:18:13 +00:00
|
|
|
break;
|
|
|
|
case (4):
|
|
|
|
hc->hw.nt_timer = 0;
|
|
|
|
hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
|
|
|
|
return;
|
|
|
|
case (3):
|
|
|
|
prim = PH_ACTIVATE | INDICATION;
|
|
|
|
para = 0;
|
|
|
|
hc->hw.nt_timer = 0;
|
|
|
|
hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
|
2005-12-08 18:32:53 +00:00
|
|
|
test_and_set_bit(FLG_ACTIVE, &dch->Flags);
|
2005-05-10 14:18:13 +00:00
|
|
|
break;
|
|
|
|
default:
|
2005-11-17 09:40:39 +00:00
|
|
|
return;
|
2005-05-10 14:18:13 +00:00
|
|
|
}
|
2005-11-17 09:40:39 +00:00
|
|
|
mISDN_queue_data(&dch->inst, dch->inst.id | MSG_BROADCAST,
|
2005-12-08 18:32:53 +00:00
|
|
|
MGR_SHORTSTATUS | INDICATION, test_bit(FLG_ACTIVE, &dch->Flags) ?
|
2005-11-17 09:40:39 +00:00
|
|
|
SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED,
|
|
|
|
0, NULL, 0);
|
2005-05-10 14:18:13 +00:00
|
|
|
}
|
|
|
|
mISDN_queue_data(&dch->inst, FLG_MSG_UP, prim, para, 0, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2001-08-02 14:55:45 +00:00
|
|
|
/*********************/
|
|
|
|
/* Interrupt handler */
|
|
|
|
/*********************/
|
2005-12-08 18:32:53 +00:00
|
|
|
static inline void
|
|
|
|
tx_irq(channel_t *chan)
|
|
|
|
{
|
|
|
|
if (chan->tx_skb && chan->tx_idx < chan->tx_skb->len) {
|
|
|
|
if (test_bit(FLG_DCHANNEL, &chan->Flags))
|
|
|
|
hfcpci_fill_dfifo(chan->hw);
|
|
|
|
if (test_bit(FLG_BCHANNEL, &chan->Flags))
|
|
|
|
hfcpci_fill_fifo(chan);
|
|
|
|
} else {
|
|
|
|
if (chan->tx_skb)
|
|
|
|
dev_kfree_skb(chan->tx_skb);
|
|
|
|
chan->tx_idx = 0;
|
|
|
|
if (test_bit(FLG_TX_NEXT, &chan->Flags)) {
|
|
|
|
chan->tx_skb = chan->next_skb;
|
|
|
|
if (chan->tx_skb) {
|
|
|
|
mISDN_head_t *hh = mISDN_HEAD_P(chan->tx_skb);
|
|
|
|
chan->next_skb = NULL;
|
|
|
|
test_and_clear_bit(FLG_TX_NEXT, &chan->Flags);
|
|
|
|
queue_ch_frame(chan, CONFIRM, hh->dinfo, NULL);
|
2006-03-01 17:26:01 +00:00
|
|
|
if (test_bit(FLG_DCHANNEL, &chan->Flags))
|
|
|
|
hfcpci_fill_dfifo(chan->hw);
|
|
|
|
if (test_bit(FLG_BCHANNEL, &chan->Flags))
|
|
|
|
hfcpci_fill_fifo(chan);
|
2005-12-08 18:32:53 +00:00
|
|
|
} else {
|
2006-03-01 17:26:01 +00:00
|
|
|
printk(KERN_WARNING "hfc tx irq TX_NEXT without skb\n");
|
2005-12-08 18:32:53 +00:00
|
|
|
test_and_clear_bit(FLG_TX_NEXT, &chan->Flags);
|
|
|
|
test_and_clear_bit(FLG_TX_BUSY, &chan->Flags);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
test_and_clear_bit(FLG_TX_BUSY, &chan->Flags);
|
|
|
|
chan->tx_skb = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-07-27 11:14:19 +00:00
|
|
|
static irqreturn_t
|
2001-08-02 14:55:45 +00:00
|
|
|
hfcpci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
hfc_pci_t *hc = dev_id;
|
|
|
|
u_char exval;
|
2005-12-08 18:32:53 +00:00
|
|
|
channel_t *bch;
|
2001-08-02 14:55:45 +00:00
|
|
|
u_char val, stat;
|
|
|
|
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_lock(&hc->lock);
|
2003-06-21 21:39:54 +00:00
|
|
|
if (!(hc->hw.int_m2 & 0x08)) {
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock(&hc->lock);
|
2003-07-27 11:14:19 +00:00
|
|
|
return IRQ_NONE; /* not initialised */
|
2003-06-21 21:39:54 +00:00
|
|
|
}
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
if (HFCPCI_ANYINT & (stat = Read_hfc(hc, HFCPCI_STATUS))) {
|
|
|
|
val = Read_hfc(hc, HFCPCI_INT_S1);
|
|
|
|
if (hc->dch.debug & L1_DEB_ISAC)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&hc->dch.inst, "HFC-PCI: stat(%02x) s1(%02x)",
|
2001-11-19 14:54:00 +00:00
|
|
|
stat, val);
|
2003-06-21 21:39:54 +00:00
|
|
|
} else {
|
|
|
|
/* shared */
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock(&hc->lock);
|
2003-07-27 11:14:19 +00:00
|
|
|
return IRQ_NONE;
|
2003-06-21 21:39:54 +00:00
|
|
|
}
|
2003-07-27 11:14:19 +00:00
|
|
|
hc->irqcnt++;
|
2003-06-21 21:39:54 +00:00
|
|
|
|
2001-08-02 14:55:45 +00:00
|
|
|
if (hc->dch.debug & L1_DEB_ISAC)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&hc->dch.inst, "HFC-PCI irq %x", val);
|
2001-08-02 14:55:45 +00:00
|
|
|
val &= hc->hw.int_m1;
|
|
|
|
if (val & 0x40) { /* state machine irq */
|
|
|
|
exval = Read_hfc(hc, HFCPCI_STATES) & 0xf;
|
|
|
|
if (hc->dch.debug & L1_DEB_ISAC)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&hc->dch.inst, "ph_state chg %d->%d",
|
2005-12-08 18:32:53 +00:00
|
|
|
hc->dch.state, exval);
|
|
|
|
hc->dch.state = exval;
|
2005-05-10 14:18:13 +00:00
|
|
|
ph_state_change(&hc->dch);
|
2001-08-02 14:55:45 +00:00
|
|
|
val &= ~0x40;
|
|
|
|
}
|
|
|
|
if (val & 0x80) { /* timer irq */
|
|
|
|
if (hc->hw.nt_mode) {
|
|
|
|
if ((--hc->hw.nt_timer) < 0)
|
2005-05-10 14:18:13 +00:00
|
|
|
ph_state_change(&hc->dch);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
val &= ~0x80;
|
|
|
|
Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
|
|
|
|
}
|
2003-06-21 21:39:54 +00:00
|
|
|
if (val & 0x08) {
|
|
|
|
if (!(bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1))) {
|
|
|
|
if (hc->dch.debug)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x08 IRQ");
|
2003-06-21 21:39:54 +00:00
|
|
|
} else
|
|
|
|
main_rec_hfcpci(bch);
|
|
|
|
}
|
|
|
|
if (val & 0x10) {
|
|
|
|
// if (hc->logecho)
|
|
|
|
// receive_emsg(hc);
|
|
|
|
// else
|
|
|
|
if (!(bch = Sel_BCS(hc, 2))) {
|
|
|
|
if (hc->dch.debug)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x10 IRQ");
|
2003-06-21 21:39:54 +00:00
|
|
|
} else
|
|
|
|
main_rec_hfcpci(bch);
|
|
|
|
}
|
|
|
|
if (val & 0x01) {
|
|
|
|
if (!(bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1))) {
|
|
|
|
if (hc->dch.debug)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x01 IRQ");
|
2005-12-08 18:32:53 +00:00
|
|
|
} else
|
|
|
|
tx_irq(bch);
|
2003-06-21 21:39:54 +00:00
|
|
|
}
|
|
|
|
if (val & 0x02) {
|
|
|
|
if (!(bch = Sel_BCS(hc, 2))) {
|
|
|
|
if (hc->dch.debug)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&hc->dch.inst, "hfcpci spurious 0x02 IRQ");
|
2005-12-08 18:32:53 +00:00
|
|
|
} else
|
|
|
|
tx_irq(bch);
|
2003-06-21 21:39:54 +00:00
|
|
|
}
|
|
|
|
if (val & 0x20) { /* receive dframe */
|
|
|
|
receive_dmsg(hc);
|
|
|
|
}
|
|
|
|
if (val & 0x04) { /* dframe transmitted */
|
2005-12-08 18:32:53 +00:00
|
|
|
if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags))
|
|
|
|
del_timer(&hc->dch.timer);
|
|
|
|
tx_irq(&hc->dch);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock(&hc->lock);
|
2003-07-27 11:14:19 +00:00
|
|
|
return IRQ_HANDLED;
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************/
|
|
|
|
/* timer callback for D-chan busy resolution. Currently no function */
|
|
|
|
/********************************************************************/
|
|
|
|
static void
|
|
|
|
hfcpci_dbusy_timer(hfc_pci_t *hc)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************/
|
|
|
|
/* activate/deactivate hardware for selected channels and mode */
|
|
|
|
/***************************************************************/
|
|
|
|
static int
|
2005-12-08 18:32:53 +00:00
|
|
|
mode_hfcpci(channel_t *bch, int bc, int protocol)
|
2001-08-02 14:55:45 +00:00
|
|
|
{
|
2005-05-07 21:04:11 +00:00
|
|
|
hfc_pci_t *hc = bch->hw;
|
2001-08-02 14:55:45 +00:00
|
|
|
int fifo2;
|
2001-11-14 10:41:26 +00:00
|
|
|
u_char rx_slot = 0, tx_slot = 0, pcm_mode;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
if (bch->debug & L1_DEB_HSCX)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "HFCPCI bchannel protocol %x-->%x ch %x-->%x",
|
2005-12-08 18:32:53 +00:00
|
|
|
bch->state, protocol, bch->channel, bc);
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
fifo2 = bc;
|
2001-11-14 10:41:26 +00:00
|
|
|
pcm_mode = (bc>>24) & 0xff;
|
|
|
|
if (pcm_mode) { /* PCM SLOT USE */
|
2002-07-07 21:20:17 +00:00
|
|
|
if (!test_bit(HFC_CFG_PCM, &hc->cfg))
|
2002-09-16 23:49:38 +00:00
|
|
|
printk(KERN_WARNING "%s: pcm channel id without HFC_CFG_PCM\n",
|
|
|
|
__FUNCTION__);
|
2001-11-14 10:41:26 +00:00
|
|
|
rx_slot = (bc>>8) & 0xff;
|
|
|
|
tx_slot = (bc>>16) & 0xff;
|
|
|
|
bc = bc & 0xff;
|
2002-07-08 13:02:19 +00:00
|
|
|
} else if (test_bit(HFC_CFG_PCM, &hc->cfg) && (protocol > ISDN_PID_NONE))
|
2002-09-16 23:49:38 +00:00
|
|
|
printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n",
|
|
|
|
__FUNCTION__);
|
2001-08-02 14:55:45 +00:00
|
|
|
if (hc->chanlimit > 1) {
|
|
|
|
hc->hw.bswapped = 0; /* B1 and B2 normal mode */
|
|
|
|
hc->hw.sctrl_e &= ~0x80;
|
|
|
|
} else {
|
2001-11-14 10:41:26 +00:00
|
|
|
if (bc & 2) {
|
2001-08-02 14:55:45 +00:00
|
|
|
if (protocol != ISDN_PID_NONE) {
|
|
|
|
hc->hw.bswapped = 1; /* B1 and B2 exchanged */
|
|
|
|
hc->hw.sctrl_e |= 0x80;
|
|
|
|
} else {
|
|
|
|
hc->hw.bswapped = 0; /* B1 and B2 normal mode */
|
|
|
|
hc->hw.sctrl_e &= ~0x80;
|
|
|
|
}
|
2001-11-14 10:41:26 +00:00
|
|
|
fifo2 = 1;
|
2001-08-02 14:55:45 +00:00
|
|
|
} else {
|
|
|
|
hc->hw.bswapped = 0; /* B1 and B2 normal mode */
|
|
|
|
hc->hw.sctrl_e &= ~0x80;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (protocol) {
|
|
|
|
case (-1): /* used for init */
|
2005-12-08 18:32:53 +00:00
|
|
|
bch->state = -1;
|
2001-08-02 14:55:45 +00:00
|
|
|
bch->channel = bc;
|
|
|
|
case (ISDN_PID_NONE):
|
2005-12-08 18:32:53 +00:00
|
|
|
if (bch->state == ISDN_PID_NONE) {
|
2001-08-02 14:55:45 +00:00
|
|
|
return(0);
|
|
|
|
}
|
2001-11-14 10:41:26 +00:00
|
|
|
if (bc & 2) {
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.sctrl &= ~SCTRL_B2_ENA;
|
|
|
|
hc->hw.sctrl_r &= ~SCTRL_B2_ENA;
|
|
|
|
} else {
|
|
|
|
hc->hw.sctrl &= ~SCTRL_B1_ENA;
|
|
|
|
hc->hw.sctrl_r &= ~SCTRL_B1_ENA;
|
|
|
|
}
|
2001-11-14 10:41:26 +00:00
|
|
|
if (fifo2 & 2) {
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2;
|
|
|
|
hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
|
|
|
|
} else {
|
|
|
|
hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1;
|
|
|
|
hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
|
|
|
|
}
|
2003-11-09 11:37:40 +00:00
|
|
|
#ifdef REVERSE_BITORDER
|
2001-11-14 10:41:26 +00:00
|
|
|
if (bch->channel & 2)
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.cirm &= 0x7f;
|
|
|
|
else
|
|
|
|
hc->hw.cirm &= 0xbf;
|
2003-11-09 11:37:40 +00:00
|
|
|
#endif
|
2005-12-08 18:32:53 +00:00
|
|
|
bch->state = ISDN_PID_NONE;
|
2001-08-02 14:55:45 +00:00
|
|
|
bch->channel = bc;
|
2005-12-08 18:32:53 +00:00
|
|
|
test_and_clear_bit(FLG_HDLC, &bch->Flags);
|
|
|
|
test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
break;
|
|
|
|
case (ISDN_PID_L1_B_64TRANS):
|
2005-12-08 18:32:53 +00:00
|
|
|
bch->state = protocol;
|
2001-08-02 14:55:45 +00:00
|
|
|
bch->channel = bc;
|
2001-11-26 17:19:26 +00:00
|
|
|
hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0);
|
|
|
|
hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0);
|
2001-11-14 10:41:26 +00:00
|
|
|
if (bc & 2) {
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.sctrl |= SCTRL_B2_ENA;
|
|
|
|
hc->hw.sctrl_r |= SCTRL_B2_ENA;
|
2003-11-09 11:37:40 +00:00
|
|
|
#ifdef REVERSE_BITORDER
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.cirm |= 0x80;
|
2003-11-09 11:37:40 +00:00
|
|
|
#endif
|
2001-08-02 14:55:45 +00:00
|
|
|
} else {
|
|
|
|
hc->hw.sctrl |= SCTRL_B1_ENA;
|
|
|
|
hc->hw.sctrl_r |= SCTRL_B1_ENA;
|
2003-11-09 11:37:40 +00:00
|
|
|
#ifdef REVERSE_BITORDER
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.cirm |= 0x40;
|
2003-11-09 11:37:40 +00:00
|
|
|
#endif
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2001-11-14 10:41:26 +00:00
|
|
|
if (fifo2 & 2) {
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.fifo_en |= HFCPCI_FIFOEN_B2;
|
|
|
|
hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
|
|
|
|
hc->hw.ctmt |= 2;
|
|
|
|
hc->hw.conn &= ~0x18;
|
|
|
|
} else {
|
|
|
|
hc->hw.fifo_en |= HFCPCI_FIFOEN_B1;
|
|
|
|
hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
|
|
|
|
hc->hw.ctmt |= 1;
|
|
|
|
hc->hw.conn &= ~0x03;
|
|
|
|
}
|
2005-12-08 18:32:53 +00:00
|
|
|
test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
break;
|
|
|
|
case (ISDN_PID_L1_B_64HDLC):
|
2005-12-08 18:32:53 +00:00
|
|
|
bch->state = protocol;
|
2001-08-02 14:55:45 +00:00
|
|
|
bch->channel = bc;
|
2001-11-26 17:19:26 +00:00
|
|
|
hfcpci_clear_fifo_rx(hc, (fifo2 & 2)?1:0);
|
|
|
|
hfcpci_clear_fifo_tx(hc, (fifo2 & 2)?1:0);
|
2001-11-14 10:41:26 +00:00
|
|
|
if (bc & 2) {
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.sctrl |= SCTRL_B2_ENA;
|
|
|
|
hc->hw.sctrl_r |= SCTRL_B2_ENA;
|
|
|
|
} else {
|
|
|
|
hc->hw.sctrl |= SCTRL_B1_ENA;
|
|
|
|
hc->hw.sctrl_r |= SCTRL_B1_ENA;
|
|
|
|
}
|
2001-11-14 10:41:26 +00:00
|
|
|
if (fifo2 & 2) {
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.last_bfifo_cnt[1] = 0;
|
|
|
|
hc->hw.fifo_en |= HFCPCI_FIFOEN_B2;
|
|
|
|
hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
|
|
|
|
hc->hw.ctmt &= ~2;
|
|
|
|
hc->hw.conn &= ~0x18;
|
|
|
|
} else {
|
|
|
|
hc->hw.last_bfifo_cnt[0] = 0;
|
|
|
|
hc->hw.fifo_en |= HFCPCI_FIFOEN_B1;
|
|
|
|
hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
|
|
|
|
hc->hw.ctmt &= ~1;
|
|
|
|
hc->hw.conn &= ~0x03;
|
|
|
|
}
|
2005-12-08 18:32:53 +00:00
|
|
|
test_and_set_bit(FLG_HDLC, &bch->Flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
break;
|
|
|
|
default:
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "prot not known %x", protocol);
|
2001-08-02 14:55:45 +00:00
|
|
|
return(-ENOPROTOOPT);
|
|
|
|
}
|
2002-07-07 21:20:17 +00:00
|
|
|
if (test_bit(HFC_CFG_PCM, &hc->cfg)) {
|
2002-07-08 12:33:04 +00:00
|
|
|
if ((protocol == ISDN_PID_NONE) ||
|
|
|
|
(protocol == -1)) { /* init case */
|
2001-11-14 10:41:26 +00:00
|
|
|
rx_slot = 0;
|
|
|
|
tx_slot = 0;
|
|
|
|
} else {
|
2002-07-07 21:20:17 +00:00
|
|
|
if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) {
|
|
|
|
rx_slot |= 0xC0;
|
|
|
|
tx_slot |= 0xC0;
|
|
|
|
} else {
|
|
|
|
rx_slot |= 0x80;
|
|
|
|
tx_slot |= 0x80;
|
|
|
|
}
|
2001-11-14 10:41:26 +00:00
|
|
|
}
|
|
|
|
if (bc & 2) {
|
|
|
|
hc->hw.conn &= 0xc7;
|
|
|
|
hc->hw.conn |= 0x08;
|
2002-09-16 23:49:38 +00:00
|
|
|
printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n",
|
|
|
|
__FUNCTION__, tx_slot);
|
|
|
|
printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n",
|
|
|
|
__FUNCTION__, rx_slot);
|
2001-11-14 10:41:26 +00:00
|
|
|
Write_hfc(hc, HFCPCI_B2_SSL, tx_slot);
|
|
|
|
Write_hfc(hc, HFCPCI_B2_RSL, rx_slot);
|
|
|
|
} else {
|
|
|
|
hc->hw.conn &= 0xf8;
|
|
|
|
hc->hw.conn |= 0x01;
|
2002-09-16 23:49:38 +00:00
|
|
|
printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n",
|
|
|
|
__FUNCTION__, tx_slot);
|
|
|
|
printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n",
|
|
|
|
__FUNCTION__, rx_slot);
|
2001-11-14 10:41:26 +00:00
|
|
|
Write_hfc(hc, HFCPCI_B1_SSL, tx_slot);
|
|
|
|
Write_hfc(hc, HFCPCI_B1_RSL, rx_slot);
|
|
|
|
}
|
|
|
|
}
|
2001-08-02 14:55:45 +00:00
|
|
|
Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e);
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
|
|
|
|
Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
|
|
|
|
Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
|
|
|
|
Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
|
|
|
|
Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
|
|
|
|
Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
|
2003-11-09 11:37:40 +00:00
|
|
|
#ifdef REVERSE_BITORDER
|
2001-08-02 14:55:45 +00:00
|
|
|
Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
|
2003-11-09 11:37:40 +00:00
|
|
|
#endif
|
2001-08-02 14:55:45 +00:00
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
2003-11-13 13:01:55 +00:00
|
|
|
static int
|
2005-12-08 18:32:53 +00:00
|
|
|
set_hfcpci_rxtest(channel_t *bch, int protocol, struct sk_buff *skb)
|
2003-11-13 13:01:55 +00:00
|
|
|
{
|
2005-05-07 21:04:11 +00:00
|
|
|
hfc_pci_t *hc = bch->hw;
|
2003-11-13 13:01:55 +00:00
|
|
|
int *chan = (int *)skb->data;
|
|
|
|
|
|
|
|
if (skb->len <4) {
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "HFCPCI rxtest no channel parameter");
|
2003-11-13 13:01:55 +00:00
|
|
|
return(-EINVAL);
|
|
|
|
}
|
|
|
|
if (bch->debug & L1_DEB_HSCX)
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x",
|
2005-12-08 18:32:53 +00:00
|
|
|
bch->state, protocol, bch->channel, *chan);
|
2003-11-13 13:01:55 +00:00
|
|
|
if (bch->channel != *chan) {
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "HFCPCI rxtest wrong channel parameter %x/%x",
|
2003-11-13 13:01:55 +00:00
|
|
|
bch->channel, *chan);
|
|
|
|
return(-EINVAL);
|
|
|
|
}
|
|
|
|
switch (protocol) {
|
|
|
|
case (ISDN_PID_L1_B_64TRANS):
|
2005-12-08 18:32:53 +00:00
|
|
|
bch->state = protocol;
|
2003-11-13 13:01:55 +00:00
|
|
|
hfcpci_clear_fifo_rx(hc, (*chan & 2)?1:0);
|
|
|
|
if (*chan & 2) {
|
|
|
|
hc->hw.sctrl_r |= SCTRL_B2_ENA;
|
|
|
|
hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX;
|
|
|
|
hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
|
|
|
|
hc->hw.ctmt |= 2;
|
|
|
|
hc->hw.conn &= ~0x18;
|
|
|
|
#ifdef REVERSE_BITORDER
|
|
|
|
hc->hw.cirm |= 0x80;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
hc->hw.sctrl_r |= SCTRL_B1_ENA;
|
|
|
|
hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX;
|
|
|
|
hc->hw.int_m1 |= HFCPCI_INTS_B1REC;
|
|
|
|
hc->hw.ctmt |= 1;
|
|
|
|
hc->hw.conn &= ~0x03;
|
|
|
|
#ifdef REVERSE_BITORDER
|
|
|
|
hc->hw.cirm |= 0x40;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case (ISDN_PID_L1_B_64HDLC):
|
2005-12-08 18:32:53 +00:00
|
|
|
bch->state = protocol;
|
2003-11-13 13:01:55 +00:00
|
|
|
hfcpci_clear_fifo_rx(hc, (*chan & 2)?1:0);
|
|
|
|
if (*chan & 2) {
|
|
|
|
hc->hw.sctrl_r |= SCTRL_B2_ENA;
|
|
|
|
hc->hw.last_bfifo_cnt[1] = 0;
|
|
|
|
hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX;
|
|
|
|
hc->hw.int_m1 |= HFCPCI_INTS_B2REC;
|
|
|
|
hc->hw.ctmt &= ~2;
|
|
|
|
hc->hw.conn &= ~0x18;
|
|
|
|
} else {
|
|
|
|
hc->hw.sctrl_r |= SCTRL_B1_ENA;
|
|
|
|
hc->hw.last_bfifo_cnt[0] = 0;
|
|
|
|
hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX;
|
|
|
|
hc->hw.int_m1 |= HFCPCI_INTS_B1REC;
|
|
|
|
hc->hw.ctmt &= ~1;
|
|
|
|
hc->hw.conn &= ~0x03;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_debugprint(&bch->inst, "prot not known %x", protocol);
|
2003-11-13 13:01:55 +00:00
|
|
|
return(-ENOPROTOOPT);
|
|
|
|
}
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
|
|
|
|
Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
|
|
|
|
Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r);
|
|
|
|
Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt);
|
|
|
|
Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
|
|
|
|
#ifdef REVERSE_BITORDER
|
|
|
|
Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm);
|
|
|
|
#endif
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
2005-12-08 18:32:53 +00:00
|
|
|
/*************************************/
|
|
|
|
/* Layer 1 D-channel hardware access */
|
|
|
|
/*************************************/
|
2001-08-02 14:55:45 +00:00
|
|
|
static int
|
2005-12-08 18:32:53 +00:00
|
|
|
hfc_dmsg(channel_t *dch, struct sk_buff *skb)
|
2001-08-02 14:55:45 +00:00
|
|
|
{
|
2005-12-08 18:32:53 +00:00
|
|
|
hfc_pci_t *hc = dch->hw;
|
|
|
|
int ret = 0;
|
|
|
|
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
2005-11-07 14:53:57 +00:00
|
|
|
u_long flags;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2005-12-08 18:32:53 +00:00
|
|
|
if (hh->prim == (PH_SIGNAL | REQUEST)) {
|
|
|
|
spin_lock_irqsave(dch->inst.hwlock, flags);
|
|
|
|
if ((hh->dinfo == INFO3_P8) || (hh->dinfo == INFO3_P10)) {
|
|
|
|
if (test_bit(HFC_CFG_MASTER, &hc->cfg))
|
|
|
|
hc->hw.mst_m |= HFCPCI_MASTER;
|
|
|
|
Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
|
|
|
|
} else
|
|
|
|
ret = -EINVAL;
|
|
|
|
spin_unlock_irqrestore(dch->inst.hwlock, flags);
|
|
|
|
} else if (hh->prim == (PH_CONTROL | REQUEST)) {
|
|
|
|
spin_lock_irqsave(dch->inst.hwlock, flags);
|
|
|
|
if (hh->dinfo == HW_RESET) {
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); /* HFC ST 3 */
|
|
|
|
udelay(6);
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, 3); /* HFC ST 2 */
|
|
|
|
if (test_bit(HFC_CFG_MASTER, &hc->cfg))
|
|
|
|
hc->hw.mst_m |= HFCPCI_MASTER;
|
|
|
|
Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION);
|
|
|
|
spin_unlock_irqrestore(dch->inst.hwlock, flags);
|
|
|
|
skb_trim(skb, 0);
|
|
|
|
return(mISDN_queueup_newhead(&dch->inst, 0, PH_CONTROL | INDICATION,
|
|
|
|
HW_POWERUP, skb));
|
|
|
|
// l1_msg(hc, HW_POWERUP | CONFIRM, NULL);
|
|
|
|
} else if (hh->dinfo == HW_DEACTIVATE) {
|
|
|
|
hc->hw.mst_m &= ~HFCPCI_MASTER;
|
|
|
|
Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
|
|
|
|
if (dch->next_skb) {
|
|
|
|
dev_kfree_skb(dch->next_skb);
|
|
|
|
dch->next_skb = NULL;
|
|
|
|
}
|
|
|
|
test_and_clear_bit(FLG_TX_NEXT, &dch->Flags);
|
|
|
|
test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
|
|
|
|
if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
|
|
|
|
del_timer(&dch->timer);
|
|
|
|
} else if (hh->dinfo == HW_POWERUP) {
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION);
|
|
|
|
} else if ((hh->dinfo & HW_TESTLOOP) == HW_TESTLOOP) {
|
|
|
|
u_char slot;
|
|
|
|
if (1 & hh->dinfo) {
|
|
|
|
if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
|
|
|
|
slot = 0xC0;
|
|
|
|
else
|
|
|
|
slot = 0x80;
|
|
|
|
printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n",
|
|
|
|
__FUNCTION__, slot);
|
|
|
|
Write_hfc(hc, HFCPCI_B1_SSL, slot);
|
|
|
|
Write_hfc(hc, HFCPCI_B1_RSL, slot);
|
|
|
|
hc->hw.conn = (hc->hw.conn & ~7) | 1;
|
|
|
|
Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
|
|
|
|
}
|
|
|
|
if (2 & hh->dinfo) {
|
|
|
|
if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg))
|
|
|
|
slot = 0xC1;
|
|
|
|
else
|
|
|
|
slot = 0x81;
|
|
|
|
printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n",
|
|
|
|
__FUNCTION__, slot);
|
|
|
|
Write_hfc(hc, HFCPCI_B2_SSL, slot);
|
|
|
|
Write_hfc(hc, HFCPCI_B2_RSL, slot);
|
|
|
|
hc->hw.conn = (hc->hw.conn & ~0x38) | 0x08;
|
|
|
|
Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
|
|
|
|
}
|
|
|
|
if (3 & hh->dinfo)
|
|
|
|
hc->hw.trm |= 0x80; /* enable IOM-loop */
|
|
|
|
else
|
|
|
|
hc->hw.trm &= 0x7f; /* disable IOM-loop */
|
|
|
|
Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
|
|
|
|
} else {
|
|
|
|
if (dch->debug & L1_DEB_WARN)
|
|
|
|
mISDN_debugprint(&dch->inst, "%s: unknown ctrl %x",
|
|
|
|
__FUNCTION__, hh->dinfo);
|
|
|
|
ret = -EINVAL;
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2005-12-08 18:32:53 +00:00
|
|
|
spin_unlock_irqrestore(dch->inst.hwlock, flags);
|
|
|
|
} else if (hh->prim == (PH_ACTIVATE | REQUEST)) {
|
|
|
|
if (hc->hw.nt_mode) {
|
|
|
|
spin_lock_irqsave(dch->inst.hwlock, flags);
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0); /* G0 */
|
|
|
|
udelay(6);
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1); /* G1 */
|
|
|
|
udelay(6);
|
|
|
|
if (test_bit(HFC_CFG_MASTER, &hc->cfg))
|
|
|
|
hc->hw.mst_m |= HFCPCI_MASTER;
|
|
|
|
Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
|
|
|
|
udelay(6);
|
|
|
|
Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION | 1);
|
|
|
|
spin_unlock_irqrestore(dch->inst.hwlock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
} else {
|
2005-12-08 18:32:53 +00:00
|
|
|
if (dch->debug & L1_DEB_WARN)
|
|
|
|
mISDN_debugprint(&dch->inst, "%s: PH_ACTIVATE none NT mode",
|
|
|
|
__FUNCTION__);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
} else if (hh->prim == (PH_DEACTIVATE | REQUEST)) {
|
|
|
|
if (hc->hw.nt_mode) {
|
|
|
|
spin_lock_irqsave(dch->inst.hwlock, flags);
|
|
|
|
hc->hw.mst_m &= ~HFCPCI_MASTER;
|
|
|
|
Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
|
|
|
|
if (dch->next_skb) {
|
|
|
|
dev_kfree_skb(dch->next_skb);
|
|
|
|
dch->next_skb = NULL;
|
|
|
|
}
|
|
|
|
test_and_clear_bit(FLG_TX_NEXT, &dch->Flags);
|
|
|
|
test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
|
|
|
|
if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
|
|
|
|
del_timer(&dch->timer);
|
2005-05-07 21:04:11 +00:00
|
|
|
#ifdef FIXME
|
2005-12-08 18:32:53 +00:00
|
|
|
if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
|
|
|
|
dchannel_sched_event(&hc->dch, D_CLEARBUSY);
|
2005-05-07 21:04:11 +00:00
|
|
|
#endif
|
2005-12-08 18:32:53 +00:00
|
|
|
spin_unlock_irqrestore(dch->inst.hwlock, flags);
|
|
|
|
} else {
|
|
|
|
if (dch->debug & L1_DEB_WARN)
|
|
|
|
mISDN_debugprint(&dch->inst, "%s: PH_DEACTIVATE none NT mode",
|
|
|
|
__FUNCTION__);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
} else if ((hh->prim & MISDN_CMD_MASK) == MGR_SHORTSTATUS) {
|
|
|
|
u_int temp = hh->dinfo & SSTATUS_ALL;
|
|
|
|
if (hc->hw.nt_mode && /* if TE mode ignore */
|
|
|
|
(temp == SSTATUS_ALL || temp == SSTATUS_L1)) {
|
|
|
|
if (hh->dinfo & SSTATUS_BROADCAST_BIT)
|
|
|
|
temp = dch->inst.id | MSG_BROADCAST;
|
|
|
|
else
|
|
|
|
temp = hh->addr | FLG_MSG_TARGET;
|
2002-05-01 01:00:40 +00:00
|
|
|
skb_trim(skb, 0);
|
2005-12-08 18:32:53 +00:00
|
|
|
hh->dinfo = test_bit(FLG_ACTIVE, &dch->Flags) ?
|
|
|
|
SSTATUS_L1_ACTIVATED : SSTATUS_L1_DEACTIVATED;
|
|
|
|
hh->prim = MGR_SHORTSTATUS | CONFIRM;
|
|
|
|
return(mISDN_queue_message(&dch->inst, temp, skb));
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2005-12-08 18:32:53 +00:00
|
|
|
ret = -EOPNOTSUPP;
|
|
|
|
} else {
|
|
|
|
if (dch->debug & L1_DEB_WARN)
|
|
|
|
mISDN_debugprint(&dch->inst, "%s: unknown prim %x",
|
|
|
|
__FUNCTION__, hh->prim);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
if (!ret)
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*************************************/
|
|
|
|
/* Layer 1 B-channel hardware access */
|
|
|
|
/*************************************/
|
|
|
|
static int
|
|
|
|
hfc_bmsg(channel_t *bch, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
|
|
|
int ret = -EINVAL;
|
|
|
|
u_long flags;
|
|
|
|
|
|
|
|
if ((hh->prim == (PH_ACTIVATE | REQUEST)) ||
|
2001-08-02 14:55:45 +00:00
|
|
|
(hh->prim == (DL_ESTABLISH | REQUEST))) {
|
2005-12-08 18:32:53 +00:00
|
|
|
if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) {
|
|
|
|
spin_lock_irqsave(bch->inst.hwlock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
ret = mode_hfcpci(bch, bch->channel,
|
|
|
|
bch->inst.pid.protocol[1]);
|
2005-12-08 18:32:53 +00:00
|
|
|
if (bch->inst.pid.protocol[2] == ISDN_PID_L2_B_TRANS)
|
|
|
|
test_and_set_bit(FLG_L2DATA, &bch->Flags);
|
|
|
|
spin_unlock_irqrestore(bch->inst.hwlock, flags);
|
|
|
|
} else
|
|
|
|
ret = 0;
|
2002-05-01 01:00:40 +00:00
|
|
|
skb_trim(skb, 0);
|
2005-12-08 18:32:53 +00:00
|
|
|
return(mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, ret, skb));
|
2001-08-02 14:55:45 +00:00
|
|
|
} else if ((hh->prim == (PH_DEACTIVATE | REQUEST)) ||
|
|
|
|
(hh->prim == (DL_RELEASE | REQUEST)) ||
|
2005-05-30 16:42:28 +00:00
|
|
|
((hh->prim == (PH_CONTROL | REQUEST) && (hh->dinfo == HW_DEACTIVATE)))) {
|
2005-12-08 18:32:53 +00:00
|
|
|
spin_lock_irqsave(bch->inst.hwlock, flags);
|
|
|
|
if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
|
2001-08-02 14:55:45 +00:00
|
|
|
dev_kfree_skb(bch->next_skb);
|
|
|
|
bch->next_skb = NULL;
|
|
|
|
}
|
2005-12-08 18:32:53 +00:00
|
|
|
if (bch->tx_skb) {
|
|
|
|
dev_kfree_skb(bch->tx_skb);
|
|
|
|
bch->tx_skb = NULL;
|
|
|
|
}
|
|
|
|
bch->tx_idx = 0;
|
|
|
|
if (bch->rx_skb) {
|
|
|
|
dev_kfree_skb(bch->rx_skb);
|
|
|
|
bch->rx_skb = NULL;
|
|
|
|
}
|
|
|
|
test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
|
2002-07-07 21:20:17 +00:00
|
|
|
mode_hfcpci(bch, bch->channel, ISDN_PID_NONE);
|
2005-12-08 18:32:53 +00:00
|
|
|
test_and_clear_bit(FLG_L2DATA, &bch->Flags);
|
|
|
|
test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
|
|
|
|
spin_unlock_irqrestore(bch->inst.hwlock, flags);
|
2002-05-01 01:00:40 +00:00
|
|
|
skb_trim(skb, 0);
|
2005-12-08 18:32:53 +00:00
|
|
|
if (hh->prim != (PH_CONTROL | REQUEST))
|
|
|
|
if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, 0, skb))
|
2001-08-02 14:55:45 +00:00
|
|
|
return(0);
|
|
|
|
ret = 0;
|
2003-11-13 13:01:55 +00:00
|
|
|
} else if (hh->prim == (PH_CONTROL | REQUEST)) {
|
2005-12-08 18:32:53 +00:00
|
|
|
spin_lock_irqsave(bch->inst.hwlock, flags);
|
2003-11-13 13:01:55 +00:00
|
|
|
if (hh->dinfo == HW_TESTRX_RAW) {
|
|
|
|
ret = set_hfcpci_rxtest(bch, ISDN_PID_L1_B_64TRANS, skb);
|
|
|
|
} else if (hh->dinfo == HW_TESTRX_HDLC) {
|
|
|
|
ret = set_hfcpci_rxtest(bch, ISDN_PID_L1_B_64HDLC, skb);
|
|
|
|
} else if (hh->dinfo == HW_TESTRX_OFF) {
|
|
|
|
mode_hfcpci(bch, bch->channel, ISDN_PID_NONE);
|
|
|
|
ret = 0;
|
|
|
|
} else
|
|
|
|
ret = -EINVAL;
|
2005-12-08 18:32:53 +00:00
|
|
|
spin_unlock_irqrestore(bch->inst.hwlock, flags);
|
2003-11-13 13:01:55 +00:00
|
|
|
if (!ret) {
|
|
|
|
skb_trim(skb, 0);
|
2005-12-08 18:32:53 +00:00
|
|
|
if (!mISDN_queueup_newhead(&bch->inst, 0, hh->prim | CONFIRM, hh->dinfo, skb))
|
2003-11-13 13:01:55 +00:00
|
|
|
return(0);
|
|
|
|
}
|
2001-08-02 14:55:45 +00:00
|
|
|
} else {
|
2002-09-16 23:49:38 +00:00
|
|
|
printk(KERN_WARNING "%s: unknown prim(%x)\n",
|
|
|
|
__FUNCTION__, hh->prim);
|
2005-12-08 18:32:53 +00:00
|
|
|
ret = -EAGAIN;
|
|
|
|
}
|
|
|
|
if (!ret)
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
/******************************/
|
|
|
|
/* Layer2 -> Layer 1 Transfer */
|
|
|
|
/******************************/
|
|
|
|
static int
|
|
|
|
hfcpci_l2l1(mISDNinstance_t *inst, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
channel_t *chan = container_of(inst, channel_t, inst);
|
|
|
|
int ret = -EINVAL;
|
|
|
|
mISDN_head_t *hh = mISDN_HEAD_P(skb);
|
|
|
|
u_long flags;
|
|
|
|
|
|
|
|
if ((hh->prim == PH_DATA_REQ) ||
|
|
|
|
(hh->prim == (DL_DATA | REQUEST))) {
|
|
|
|
spin_lock_irqsave(inst->hwlock, flags);
|
|
|
|
ret = channel_senddata(chan, hh->dinfo, skb);
|
|
|
|
if (ret > 0) { /* direct TX */
|
|
|
|
if (test_bit(FLG_DCHANNEL, &chan->Flags))
|
|
|
|
hfcpci_fill_dfifo(chan->hw);
|
|
|
|
if (test_bit(FLG_BCHANNEL, &chan->Flags))
|
|
|
|
hfcpci_fill_fifo(chan);
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(inst->hwlock, flags);
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
if (test_bit(FLG_DCHANNEL, &chan->Flags)) {
|
|
|
|
ret = hfc_dmsg(chan, skb);
|
|
|
|
if (ret != -EAGAIN)
|
|
|
|
return(ret);
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
if (test_bit(FLG_BCHANNEL, &chan->Flags)) {
|
|
|
|
ret = hfc_bmsg(chan, skb);
|
|
|
|
if (ret != -EAGAIN)
|
|
|
|
return(ret);
|
2001-08-02 14:55:45 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
if (!ret)
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/********************************/
|
|
|
|
/* called for card init message */
|
|
|
|
/********************************/
|
|
|
|
|
|
|
|
void
|
|
|
|
inithfcpci(hfc_pci_t *hc)
|
|
|
|
{
|
2002-07-07 21:20:17 +00:00
|
|
|
HFC_INFO("inithfcpci: entered\n");
|
2005-12-08 18:32:53 +00:00
|
|
|
hc->dch.timer.function = (void *) hfcpci_dbusy_timer;
|
|
|
|
hc->dch.timer.data = (long) &hc->dch;
|
|
|
|
init_timer(&hc->dch.timer);
|
2001-09-29 20:05:01 +00:00
|
|
|
hc->chanlimit = 2;
|
2001-11-14 10:41:26 +00:00
|
|
|
mode_hfcpci(&hc->bch[0], 1, -1);
|
|
|
|
mode_hfcpci(&hc->bch[1], 2, -1);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int init_card(hfc_pci_t *hc)
|
|
|
|
{
|
2003-07-27 11:14:19 +00:00
|
|
|
int cnt = 3;
|
2005-11-07 14:53:57 +00:00
|
|
|
u_long flags;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2002-07-07 21:20:17 +00:00
|
|
|
HFC_INFO("init_card: entered\n");
|
|
|
|
|
2005-11-09 16:54:53 +00:00
|
|
|
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_lock_irqsave(&hc->lock, flags);
|
2005-11-09 16:54:53 +00:00
|
|
|
disable_hwirq(hc);
|
|
|
|
spin_unlock_irqrestore(&hc->lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
if (request_irq(hc->irq, hfcpci_interrupt, SA_SHIRQ, "HFC PCI", hc)) {
|
2005-11-07 14:53:57 +00:00
|
|
|
printk(KERN_WARNING "mISDN: couldn't get interrupt %d\n", hc->irq);
|
2001-08-02 14:55:45 +00:00
|
|
|
return(-EIO);
|
|
|
|
}
|
2005-11-09 16:54:53 +00:00
|
|
|
spin_lock_irqsave(&hc->lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
while (cnt) {
|
|
|
|
inithfcpci(hc);
|
2001-11-16 01:49:21 +00:00
|
|
|
/* Finally enable IRQ output
|
|
|
|
* this is only allowed, if an IRQ routine is allready
|
|
|
|
* established for this HFC, so don't do that earlier
|
|
|
|
*/
|
2005-11-09 16:54:53 +00:00
|
|
|
enable_hwirq(hc);
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock_irqrestore(&hc->lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
/* Timeout 80ms */
|
|
|
|
current->state = TASK_UNINTERRUPTIBLE;
|
|
|
|
schedule_timeout((80*HZ)/1000);
|
|
|
|
printk(KERN_INFO "HFC PCI: IRQ %d count %d\n",
|
2003-07-27 11:14:19 +00:00
|
|
|
hc->irq, hc->irqcnt);
|
2001-08-02 14:55:45 +00:00
|
|
|
/* now switch timer interrupt off */
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_lock_irqsave(&hc->lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
|
|
|
|
/* reinit mode reg */
|
|
|
|
Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
|
2003-07-27 11:14:19 +00:00
|
|
|
if (!hc->irqcnt) {
|
2001-08-02 14:55:45 +00:00
|
|
|
printk(KERN_WARNING
|
|
|
|
"HFC PCI: IRQ(%d) getting no interrupts during init %d\n",
|
|
|
|
hc->irq, 4 - cnt);
|
|
|
|
if (cnt == 1) {
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock_irqrestore(&hc->lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
return (-EIO);
|
|
|
|
} else {
|
|
|
|
reset_hfcpci(hc);
|
|
|
|
cnt--;
|
|
|
|
}
|
|
|
|
} else {
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock_irqrestore(&hc->lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock_irqrestore(&hc->lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
return(-EIO);
|
|
|
|
}
|
|
|
|
|
2001-11-14 10:41:26 +00:00
|
|
|
static int
|
|
|
|
SelFreeBChannel(hfc_pci_t *hc, channel_info_t *ci)
|
|
|
|
{
|
2005-12-08 18:32:53 +00:00
|
|
|
channel_t *bch;
|
2004-06-17 12:31:14 +00:00
|
|
|
hfc_pci_t *hfc;
|
|
|
|
mISDNstack_t *bst;
|
|
|
|
u_int cnr;
|
|
|
|
struct list_head *head;
|
2001-11-14 10:41:26 +00:00
|
|
|
|
|
|
|
if (!ci)
|
|
|
|
return(-EINVAL);
|
|
|
|
ci->st.p = NULL;
|
|
|
|
cnr=0;
|
2004-06-17 12:31:14 +00:00
|
|
|
bst = hc->dch.inst.st;
|
|
|
|
if (list_empty(&bst->childlist)) {
|
|
|
|
if ((bst->id & FLG_CLONE_STACK) &&
|
|
|
|
(bst->childlist.prev != &bst->childlist)) {
|
|
|
|
head = bst->childlist.prev;
|
|
|
|
} else {
|
|
|
|
printk(KERN_ERR "%s: invalid empty childlist (no clone) stid(%x) childlist(%p<-%p->%p)\n",
|
|
|
|
__FUNCTION__, bst->id, bst->childlist.prev, &bst->childlist, bst->childlist.next);
|
|
|
|
return(-EINVAL);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
head = &bst->childlist;
|
|
|
|
list_for_each_entry(bst, head, list) {
|
2005-05-07 21:04:11 +00:00
|
|
|
if (!bst->mgr) {
|
2001-11-14 10:41:26 +00:00
|
|
|
int_errtxt("no mgr st(%p)", bst);
|
|
|
|
return(-EINVAL);
|
|
|
|
}
|
2005-12-08 18:32:53 +00:00
|
|
|
bch = container_of(bst->mgr, channel_t, inst);
|
2005-05-07 21:04:11 +00:00
|
|
|
hfc = bst->mgr->privat;
|
2001-11-14 10:41:26 +00:00
|
|
|
if (!hfc) {
|
|
|
|
int_errtxt("no mgr->data st(%p)", bst);
|
|
|
|
return(-EINVAL);
|
|
|
|
}
|
|
|
|
bch = &hfc->bch[cnr & 1];
|
|
|
|
if (!(ci->channel & (~CHANNEL_NUMBER))) {
|
|
|
|
/* only number is set */
|
2001-12-02 13:08:08 +00:00
|
|
|
if ((ci->channel & 0x3) == (cnr + 1)) {
|
2005-12-08 18:32:53 +00:00
|
|
|
if (test_bit(FLG_ACTIVE, &bch->Flags))
|
2001-11-14 10:41:26 +00:00
|
|
|
return(-EBUSY);
|
|
|
|
bch->channel = (cnr & 1) ? 2 : 1;
|
|
|
|
ci->st.p = bst;
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
} else if ((ci->channel & (~CHANNEL_NUMBER)) == 0x00a18300) {
|
2005-12-08 18:32:53 +00:00
|
|
|
if (!test_bit(FLG_ACTIVE, &bch->Flags)) {
|
2001-11-14 10:41:26 +00:00
|
|
|
ci->st.p = bst;
|
|
|
|
bch->channel = (cnr & 1) ? 2 : 1;
|
|
|
|
bch->channel |= CHANNEL_EXT_PCM;
|
|
|
|
bch->channel |= (ci->channel & 0x1f) << 16;
|
|
|
|
bch->channel |= (ci->channel & 0x1f) << 8;
|
|
|
|
ci->st.p = bst;
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cnr++;
|
|
|
|
}
|
|
|
|
return(-EBUSY);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MAX_CARDS 8
|
|
|
|
#define MODULE_PARM_T "1-8i"
|
2001-08-02 14:55:45 +00:00
|
|
|
static int HFC_cnt;
|
2005-05-18 14:19:34 +00:00
|
|
|
static uint protocol[MAX_CARDS];
|
|
|
|
static uint layermask[MAX_CARDS];
|
|
|
|
static uint debug;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2003-07-21 12:00:05 +00:00
|
|
|
static mISDNobject_t HFC_obj;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
#ifdef MODULE
|
|
|
|
MODULE_AUTHOR("Karsten Keil");
|
2002-09-16 23:49:38 +00:00
|
|
|
#ifdef MODULE_LICENSE
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
#endif
|
2005-05-18 14:19:34 +00:00
|
|
|
module_param (debug, uint, 0);
|
|
|
|
MODULE_PARM_DESC (debug, "hfcpci debug mask");
|
2005-11-07 14:55:30 +00:00
|
|
|
#ifdef OLD_MODULE_PARAM_ARRAY
|
|
|
|
static int protocol_cnt;
|
|
|
|
module_param_array(protocol, uint, protocol_cnt, 0);
|
|
|
|
#else
|
2005-05-18 14:19:34 +00:00
|
|
|
module_param_array(protocol, uint, NULL, 0);
|
2005-11-07 14:55:30 +00:00
|
|
|
#endif
|
2005-05-18 14:19:34 +00:00
|
|
|
MODULE_PARM_DESC (protocol, "hfcpci protcol (DSS1 := 2)");
|
2002-07-07 21:20:17 +00:00
|
|
|
|
|
|
|
/* short description of protocol
|
|
|
|
* protocol=<p1>[,p2,p3...]
|
|
|
|
*
|
|
|
|
* Values:
|
|
|
|
* the value has following structure
|
|
|
|
* <bit 3 - 0> D-channel protocol id
|
|
|
|
* <bit 15 - 4> Flags for special features
|
|
|
|
* <bit 31 - 16> Spare (set to 0)
|
|
|
|
*
|
|
|
|
* D-channel protocol ids
|
|
|
|
* 1 1TR6 (not released yet)
|
|
|
|
* 2 DSS1
|
|
|
|
*
|
|
|
|
* Feature Flags
|
|
|
|
* bit 4 0x0010 Net side stack (NT mode)
|
|
|
|
* bit 5 0x0020 point to point line
|
|
|
|
* bit 6 0x0040 PCM slave mode
|
|
|
|
* bit 7 0x0080 use negativ frame pulse
|
|
|
|
* bit 8 0x0100 use setting from the previous HFC driver and add channels to
|
|
|
|
* the previous stack, used for the second chip in 2 chip setups
|
|
|
|
* bit 9 0x0200 switch DD/DU interface
|
|
|
|
* bit 10 - 15 reserved
|
|
|
|
*/
|
2005-11-07 14:55:30 +00:00
|
|
|
#ifdef OLD_MODULE_PARAM_ARRAY
|
|
|
|
static int layermask_cnt;
|
|
|
|
module_param_array(layermask, uint, layermask_cnt, 0);
|
|
|
|
#else
|
2005-05-18 14:19:34 +00:00
|
|
|
module_param_array(layermask, uint, NULL, 0);
|
2005-11-07 14:55:30 +00:00
|
|
|
#endif
|
2005-05-18 14:19:34 +00:00
|
|
|
MODULE_PARM_DESC(layermask, "hfcpci layer mask");
|
2001-08-02 14:55:45 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
static char HFCName[] = "HFC_PCI";
|
|
|
|
|
|
|
|
/* this variable is used as card index when more than one cards are present */
|
|
|
|
static struct pci_dev *dev_hfcpci = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
setup_hfcpci(hfc_pci_t *hc)
|
|
|
|
{
|
|
|
|
char tmp[64];
|
|
|
|
int i=0;
|
|
|
|
struct pci_dev *tmp_hfcpci = NULL;
|
2005-12-11 17:26:34 +00:00
|
|
|
void *buffer;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
strcpy(tmp, hfcpci_revision);
|
2003-07-21 12:00:05 +00:00
|
|
|
printk(KERN_INFO "mISDN: HFC-PCI driver Rev. %s\n", mISDN_getrev(tmp));
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.cirm = 0;
|
2005-12-08 18:32:53 +00:00
|
|
|
hc->dch.state = 0;
|
2001-08-02 14:55:45 +00:00
|
|
|
while (id_list[i].vendor_id) {
|
|
|
|
tmp_hfcpci = pci_find_device(id_list[i].vendor_id,
|
|
|
|
id_list[i].device_id, dev_hfcpci);
|
|
|
|
i++;
|
|
|
|
if (tmp_hfcpci) {
|
|
|
|
if (pci_enable_device(tmp_hfcpci))
|
|
|
|
continue;
|
|
|
|
pci_set_master(tmp_hfcpci);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tmp_hfcpci) {
|
|
|
|
i--;
|
|
|
|
dev_hfcpci = tmp_hfcpci; /* old device */
|
2003-07-27 11:14:19 +00:00
|
|
|
hc->hw.dev = dev_hfcpci;
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->irq = dev_hfcpci->irq;
|
|
|
|
if (!hc->irq) {
|
|
|
|
printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
|
|
|
|
return (1);
|
|
|
|
}
|
2001-09-30 17:09:23 +00:00
|
|
|
hc->hw.pci_io = (char *) get_pcibase(dev_hfcpci, 1);
|
2003-07-21 12:00:05 +00:00
|
|
|
printk(KERN_INFO "mISDN: HFC-PCI card manufacturer: %s card name: %s\n", id_list[i].vendor_name, id_list[i].card_name);
|
2001-08-02 14:55:45 +00:00
|
|
|
} else {
|
2003-10-24 21:27:28 +00:00
|
|
|
printk(KERN_WARNING "HFC-PCI: No more PCI cards found\n");
|
2001-08-02 14:55:45 +00:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
if (!hc->hw.pci_io) {
|
|
|
|
printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
/* Allocate memory for FIFOS */
|
2005-12-11 17:26:34 +00:00
|
|
|
/* the memory needs to be on a 32k boundary within the first 4G */
|
|
|
|
pci_set_dma_mask(dev_hfcpci, 0xFFFF8000);
|
|
|
|
buffer = pci_alloc_consistent(dev_hfcpci, 0x8000, &hc->hw.dmahandle);
|
|
|
|
/* We silently assume the address is okay if nonzero */
|
|
|
|
if(!buffer) {
|
2001-08-02 14:55:45 +00:00
|
|
|
printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2005-12-11 17:26:34 +00:00
|
|
|
hc->hw.fifos = buffer;
|
|
|
|
pci_write_config_dword(hc->hw.dev, 0x80, (u_int) cpu_to_le32((unsigned int)virt_to_bus(hc->hw.fifos)));
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256);
|
|
|
|
printk(KERN_INFO
|
2005-05-18 14:19:34 +00:00
|
|
|
"HFC-PCI: defined at mem %#lx fifo %#lx(%#lx) IRQ %d HZ %d\n",
|
|
|
|
(u_long) hc->hw.pci_io, (u_long) hc->hw.fifos,
|
|
|
|
(u_long) virt_to_bus(hc->hw.fifos),
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->irq, HZ);
|
2003-07-27 11:14:19 +00:00
|
|
|
pci_write_config_word(hc->hw.dev, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */
|
2005-11-09 16:54:53 +00:00
|
|
|
hc->hw.int_m2 = 0;
|
|
|
|
disable_hwirq(hc);
|
2001-08-02 14:55:45 +00:00
|
|
|
hc->hw.int_m1 = 0;
|
|
|
|
Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
|
|
|
|
/* At this point the needed PCI config is done */
|
|
|
|
/* fifos are still not enabled */
|
|
|
|
hc->hw.timer.function = (void *) hfcpci_Timer;
|
|
|
|
hc->hw.timer.data = (long) hc;
|
|
|
|
init_timer(&hc->hw.timer);
|
|
|
|
reset_hfcpci(hc);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
release_card(hfc_pci_t *hc) {
|
2005-11-07 14:53:57 +00:00
|
|
|
u_long flags;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
free_irq(hc->irq, hc);
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_lock_irqsave(&hc->lock, flags);
|
2001-11-14 10:41:26 +00:00
|
|
|
mode_hfcpci(&hc->bch[0], 1, ISDN_PID_NONE);
|
|
|
|
mode_hfcpci(&hc->bch[1], 2, ISDN_PID_NONE);
|
2005-12-08 18:32:53 +00:00
|
|
|
if (hc->dch.timer.function != NULL) {
|
|
|
|
del_timer(&hc->dch.timer);
|
|
|
|
hc->dch.timer.function = NULL;
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
release_io_hfcpci(hc);
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_freechannel(&hc->bch[1]);
|
|
|
|
mISDN_freechannel(&hc->bch[0]);
|
|
|
|
mISDN_freechannel(&hc->dch);
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock_irqrestore(&hc->lock, flags);
|
2003-08-01 22:15:53 +00:00
|
|
|
HFC_obj.ctrl(&hc->dch.inst, MGR_UNREGLAYER | REQUEST, NULL);
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_lock_irqsave(&HFC_obj.lock, flags);
|
2004-06-17 12:31:14 +00:00
|
|
|
list_del(&hc->list);
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock_irqrestore(&HFC_obj.lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
kfree(hc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
HFC_manager(void *data, u_int prim, void *arg) {
|
2004-06-17 12:31:14 +00:00
|
|
|
hfc_pci_t *card;
|
2003-07-21 12:00:05 +00:00
|
|
|
mISDNinstance_t *inst = data;
|
2001-08-02 14:55:45 +00:00
|
|
|
struct sk_buff *skb;
|
|
|
|
int channel = -1;
|
2005-11-07 14:53:57 +00:00
|
|
|
u_long flags;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
|
|
|
if (!data) {
|
2004-01-29 00:53:13 +00:00
|
|
|
MGR_HASPROTOCOL_HANDLER(prim,arg,&HFC_obj)
|
2002-09-16 23:49:38 +00:00
|
|
|
printk(KERN_ERR "%s: no data prim %x arg %p\n",
|
|
|
|
__FUNCTION__, prim, arg);
|
2001-08-02 14:55:45 +00:00
|
|
|
return(-EINVAL);
|
|
|
|
}
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_lock_irqsave(&HFC_obj.lock, flags);
|
2004-06-17 12:31:14 +00:00
|
|
|
list_for_each_entry(card, &HFC_obj.ilist, list) {
|
2001-08-02 14:55:45 +00:00
|
|
|
if (&card->dch.inst == inst) {
|
|
|
|
channel = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (&card->bch[0].inst == inst) {
|
|
|
|
channel = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (&card->bch[1].inst == inst) {
|
|
|
|
inst = &card->bch[1].inst;
|
|
|
|
channel = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock_irqrestore(&HFC_obj.lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
if (channel<0) {
|
2002-09-16 23:49:38 +00:00
|
|
|
printk(KERN_ERR "%s: no channel data %p prim %x arg %p\n",
|
|
|
|
__FUNCTION__, data, prim, arg);
|
2001-08-02 14:55:45 +00:00
|
|
|
return(-EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(prim) {
|
|
|
|
case MGR_REGLAYER | CONFIRM:
|
2003-08-01 22:15:53 +00:00
|
|
|
if (channel == 2)
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_setpara(&card->dch, &inst->st->para);
|
2003-08-01 22:15:53 +00:00
|
|
|
else
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_setpara(&card->bch[channel], &inst->st->para);
|
2001-08-02 14:55:45 +00:00
|
|
|
break;
|
|
|
|
case MGR_UNREGLAYER | REQUEST:
|
2005-12-08 18:32:53 +00:00
|
|
|
if ((skb = create_link_skb(PH_CONTROL | REQUEST,
|
|
|
|
HW_DEACTIVATE, 0, NULL, 0))) {
|
|
|
|
if (hfcpci_l2l1(inst, skb))
|
|
|
|
dev_kfree_skb(skb);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2003-08-01 22:15:53 +00:00
|
|
|
HFC_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
|
|
|
break;
|
|
|
|
case MGR_CLRSTPARA | INDICATION:
|
|
|
|
arg = NULL;
|
|
|
|
case MGR_ADDSTPARA | INDICATION:
|
|
|
|
if (channel == 2)
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_setpara(&card->dch, arg);
|
2003-08-01 22:15:53 +00:00
|
|
|
else
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_setpara(&card->bch[channel], arg);
|
2001-08-02 14:55:45 +00:00
|
|
|
break;
|
|
|
|
case MGR_RELEASE | INDICATION:
|
2003-08-01 22:15:53 +00:00
|
|
|
if (channel == 2) {
|
|
|
|
release_card(card);
|
2001-08-02 14:55:45 +00:00
|
|
|
} else {
|
2003-08-01 22:15:53 +00:00
|
|
|
HFC_obj.refcnt--;
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
break;
|
2005-05-07 21:04:11 +00:00
|
|
|
#ifdef FIXME
|
2001-08-02 14:55:45 +00:00
|
|
|
case MGR_CONNECT | REQUEST:
|
2004-01-26 22:21:32 +00:00
|
|
|
return(mISDN_ConnectIF(inst, arg));
|
2001-08-02 14:55:45 +00:00
|
|
|
case MGR_SETIF | REQUEST:
|
|
|
|
case MGR_SETIF | INDICATION:
|
|
|
|
if (channel==2)
|
2004-01-26 22:21:32 +00:00
|
|
|
return(mISDN_SetIF(inst, arg, prim, HFCD_l1hw, NULL,
|
2001-08-02 14:55:45 +00:00
|
|
|
&card->dch));
|
|
|
|
else
|
2004-01-26 22:21:32 +00:00
|
|
|
return(mISDN_SetIF(inst, arg, prim, hfcpci_l2l1, NULL,
|
2001-08-02 14:55:45 +00:00
|
|
|
&card->bch[channel]));
|
|
|
|
case MGR_DISCONNECT | REQUEST:
|
|
|
|
case MGR_DISCONNECT | INDICATION:
|
2004-01-26 22:21:32 +00:00
|
|
|
return(mISDN_DisConnectIF(inst, arg));
|
2005-05-07 21:04:11 +00:00
|
|
|
#endif
|
2001-11-14 10:41:26 +00:00
|
|
|
case MGR_SELCHANNEL | REQUEST:
|
|
|
|
if (channel != 2) {
|
2002-09-16 23:49:38 +00:00
|
|
|
printk(KERN_WARNING "%s: selchannel not dinst\n",
|
|
|
|
__FUNCTION__);
|
2001-11-14 10:41:26 +00:00
|
|
|
return(-EINVAL);
|
|
|
|
}
|
|
|
|
return(SelFreeBChannel(card, arg));
|
2005-05-13 10:41:48 +00:00
|
|
|
case MGR_SETSTACK | INDICATION:
|
2001-08-02 14:55:45 +00:00
|
|
|
if ((channel!=2) && (inst->pid.global == 2)) {
|
2005-05-07 21:04:11 +00:00
|
|
|
if ((skb = create_link_skb(PH_ACTIVATE | REQUEST, 0, 0, NULL, 0))) {
|
|
|
|
if (hfcpci_l2l1(inst, skb))
|
2001-08-02 14:55:45 +00:00
|
|
|
dev_kfree_skb(skb);
|
|
|
|
}
|
|
|
|
if (inst->pid.protocol[2] == ISDN_PID_L2_B_TRANS)
|
2005-05-07 21:04:11 +00:00
|
|
|
mISDN_queue_data(inst, FLG_MSG_UP, DL_ESTABLISH | INDICATION,
|
2001-08-02 14:55:45 +00:00
|
|
|
0, 0, NULL, 0);
|
|
|
|
else
|
2005-05-07 21:04:11 +00:00
|
|
|
mISDN_queue_data(inst, FLG_MSG_UP, PH_ACTIVATE | INDICATION,
|
2001-08-02 14:55:45 +00:00
|
|
|
0, 0, NULL, 0);
|
|
|
|
}
|
|
|
|
break;
|
2004-01-29 00:53:13 +00:00
|
|
|
PRIM_NOT_HANDLED(MGR_CTRLREADY | INDICATION);
|
|
|
|
PRIM_NOT_HANDLED(MGR_GLOBALOPT | REQUEST);
|
2001-08-02 14:55:45 +00:00
|
|
|
default:
|
2002-09-16 23:49:38 +00:00
|
|
|
printk(KERN_WARNING "%s: prim %x not handled\n",
|
|
|
|
__FUNCTION__, prim);
|
2001-08-02 14:55:45 +00:00
|
|
|
return(-EINVAL);
|
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
2003-07-27 11:14:19 +00:00
|
|
|
static int __init HFC_init(void)
|
2001-08-02 14:55:45 +00:00
|
|
|
{
|
2001-11-14 10:41:26 +00:00
|
|
|
int err,i;
|
|
|
|
hfc_pci_t *card, *prev;
|
2003-07-21 12:00:05 +00:00
|
|
|
mISDN_pid_t pid;
|
|
|
|
mISDNstack_t *dst;
|
2005-11-07 14:53:57 +00:00
|
|
|
u_long flags;
|
2001-08-02 14:55:45 +00:00
|
|
|
|
2004-01-11 13:58:50 +00:00
|
|
|
#ifdef MODULE
|
|
|
|
HFC_obj.owner = THIS_MODULE;
|
|
|
|
#endif
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_lock_init(&HFC_obj.lock);
|
2004-06-17 12:31:14 +00:00
|
|
|
INIT_LIST_HEAD(&HFC_obj.ilist);
|
2001-08-02 14:55:45 +00:00
|
|
|
HFC_obj.name = HFCName;
|
|
|
|
HFC_obj.own_ctrl = HFC_manager;
|
|
|
|
HFC_obj.DPROTO.protocol[0] = ISDN_PID_L0_TE_S0 |
|
|
|
|
ISDN_PID_L0_NT_S0;
|
2001-09-29 20:05:01 +00:00
|
|
|
HFC_obj.DPROTO.protocol[1] = ISDN_PID_L1_NT_S0;
|
2001-08-02 14:55:45 +00:00
|
|
|
HFC_obj.BPROTO.protocol[1] = ISDN_PID_L1_B_64TRANS |
|
|
|
|
ISDN_PID_L1_B_64HDLC;
|
2001-09-29 20:05:01 +00:00
|
|
|
HFC_obj.BPROTO.protocol[2] = ISDN_PID_L2_B_TRANS |
|
|
|
|
ISDN_PID_L2_B_RAWDEV;
|
2003-07-21 12:00:05 +00:00
|
|
|
if ((err = mISDN_register(&HFC_obj))) {
|
2001-08-02 14:55:45 +00:00
|
|
|
printk(KERN_ERR "Can't register HFC PCI error(%d)\n", err);
|
|
|
|
return(err);
|
|
|
|
}
|
|
|
|
while (HFC_cnt < MAX_CARDS) {
|
|
|
|
if (!(card = kmalloc(sizeof(hfc_pci_t), GFP_ATOMIC))) {
|
|
|
|
printk(KERN_ERR "No kmem for HFCcard\n");
|
2003-07-21 12:00:05 +00:00
|
|
|
mISDN_unregister(&HFC_obj);
|
2001-08-02 14:55:45 +00:00
|
|
|
return(-ENOMEM);
|
|
|
|
}
|
|
|
|
memset(card, 0, sizeof(hfc_pci_t));
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_lock_irqsave(&HFC_obj.lock, flags);
|
2004-06-17 12:31:14 +00:00
|
|
|
list_add_tail(&card->list, &HFC_obj.ilist);
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock_irqrestore(&HFC_obj.lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
card->dch.debug = debug;
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_lock_init(&card->lock);
|
|
|
|
card->dch.inst.hwlock = &card->lock;
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_init_instance(&card->dch.inst, &HFC_obj, card, hfcpci_l2l1);
|
2001-08-02 14:55:45 +00:00
|
|
|
card->dch.inst.pid.layermask = ISDN_LAYER(0);
|
|
|
|
sprintf(card->dch.inst.name, "HFC%d", HFC_cnt+1);
|
2005-12-08 18:32:53 +00:00
|
|
|
err = mISDN_initchannel(&card->dch, MSK_INIT_DCHANNEL, MAX_DFRAME_LEN_L1);
|
2005-12-21 10:09:43 +00:00
|
|
|
card->dch.hw = card;
|
2005-12-08 18:32:53 +00:00
|
|
|
if (err) {
|
|
|
|
mISDN_unregister(&HFC_obj);
|
|
|
|
return(err);
|
|
|
|
}
|
2001-08-02 14:55:45 +00:00
|
|
|
for (i=0; i<2; i++) {
|
2001-11-14 10:41:26 +00:00
|
|
|
card->bch[i].channel = i + 1;
|
2005-05-10 14:18:13 +00:00
|
|
|
mISDN_init_instance(&card->bch[i].inst, &HFC_obj, card, hfcpci_l2l1);
|
2001-08-02 14:55:45 +00:00
|
|
|
card->bch[i].inst.pid.layermask = ISDN_LAYER(0);
|
2005-11-07 14:53:57 +00:00
|
|
|
card->bch[i].inst.hwlock = &card->lock;
|
2001-08-02 14:55:45 +00:00
|
|
|
card->bch[i].debug = debug;
|
|
|
|
sprintf(card->bch[i].inst.name, "%s B%d",
|
|
|
|
card->dch.inst.name, i+1);
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_initchannel(&card->bch[i], MSK_INIT_BCHANNEL, MAX_DATA_MEM);
|
2005-12-21 10:09:43 +00:00
|
|
|
card->bch[i].hw = card;
|
2005-05-07 21:04:11 +00:00
|
|
|
#ifdef FIXME
|
2001-09-29 20:05:01 +00:00
|
|
|
if (card->bch[i].dev) {
|
|
|
|
card->bch[i].dev->wport.pif.func =
|
|
|
|
hfcpci_l2l1;
|
|
|
|
card->bch[i].dev->wport.pif.fdata =
|
|
|
|
&card->bch[i];
|
|
|
|
}
|
2005-05-07 21:04:11 +00:00
|
|
|
#endif
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2001-11-14 10:41:26 +00:00
|
|
|
if (protocol[HFC_cnt] == 0x100) {
|
2004-06-17 12:31:14 +00:00
|
|
|
if (card->list.prev == &HFC_obj.ilist)
|
|
|
|
prev = NULL;
|
|
|
|
else
|
|
|
|
prev = list_entry(card->list.prev, hfc_pci_t, list);
|
2001-11-14 10:41:26 +00:00
|
|
|
|
|
|
|
if (!prev) {
|
|
|
|
int_errtxt("card(%d) no previous HFC",
|
|
|
|
HFC_cnt);
|
|
|
|
if (!HFC_cnt)
|
2003-07-21 12:00:05 +00:00
|
|
|
mISDN_unregister(&HFC_obj);
|
2001-11-14 10:41:26 +00:00
|
|
|
else
|
|
|
|
err = 0;
|
|
|
|
return(err);
|
|
|
|
}
|
|
|
|
i = HFC_cnt - 1;
|
|
|
|
test_and_set_bit(HFC_CFG_2HFC, &prev->cfg);
|
|
|
|
test_and_set_bit(HFC_CFG_2HFC, &card->cfg);
|
|
|
|
test_and_set_bit(HFC_CFG_SLAVEHFC, &card->cfg);
|
|
|
|
} else {
|
|
|
|
prev = NULL;
|
|
|
|
i = HFC_cnt;
|
|
|
|
}
|
2004-01-26 22:21:32 +00:00
|
|
|
mISDN_set_dchannel_pid(&pid, protocol[i], layermask[i]);
|
2001-11-14 10:41:26 +00:00
|
|
|
test_and_set_bit(HFC_CFG_MASTER, &card->cfg);
|
|
|
|
if (protocol[i] & 0x10) {
|
|
|
|
card->dch.inst.pid.protocol[0] = ISDN_PID_L0_NT_S0;
|
|
|
|
card->dch.inst.pid.protocol[1] = ISDN_PID_L1_NT_S0;
|
|
|
|
pid.protocol[0] = ISDN_PID_L0_NT_S0;
|
|
|
|
pid.protocol[1] = ISDN_PID_L1_NT_S0;
|
|
|
|
card->dch.inst.pid.layermask |= ISDN_LAYER(1);
|
|
|
|
pid.layermask |= ISDN_LAYER(1);
|
|
|
|
if (layermask[i] & ISDN_LAYER(2))
|
|
|
|
pid.protocol[2] = ISDN_PID_L2_LAPD_NET;
|
|
|
|
card->hw.nt_mode = 1;
|
|
|
|
} else {
|
|
|
|
card->dch.inst.pid.protocol[0] = ISDN_PID_L0_TE_S0;
|
|
|
|
card->hw.nt_mode = 0;
|
|
|
|
}
|
|
|
|
if (protocol[i] & 0x40) {
|
|
|
|
if (pid.layermask & ISDN_LAYER(3))
|
|
|
|
pid.protocol[3] |= ISDN_PID_L3_DF_EXTCID;
|
|
|
|
test_and_set_bit(HFC_CFG_PCM, &card->cfg);
|
|
|
|
test_and_set_bit(HFC_CFG_SLAVE, &card->cfg);
|
|
|
|
test_and_clear_bit(HFC_CFG_MASTER, &card->cfg);
|
|
|
|
}
|
2002-07-07 21:20:17 +00:00
|
|
|
if (protocol[i] & 0x80) {
|
|
|
|
test_and_set_bit(HFC_CFG_NEG_F0, &card->cfg);
|
|
|
|
}
|
|
|
|
if (protocol[i] & 0x200) {
|
|
|
|
test_and_set_bit(HFC_CFG_SW_DD_DU, &card->cfg);
|
|
|
|
}
|
2001-08-02 14:55:45 +00:00
|
|
|
printk(KERN_DEBUG "HFC card %p dch %p bch1 %p bch2 %p\n",
|
|
|
|
card, &card->dch, &card->bch[0], &card->bch[1]);
|
|
|
|
if (setup_hfcpci(card)) {
|
|
|
|
err = 0;
|
2005-12-08 18:32:53 +00:00
|
|
|
mISDN_freechannel(&card->dch);
|
|
|
|
mISDN_freechannel(&card->bch[1]);
|
|
|
|
mISDN_freechannel(&card->bch[0]);
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_lock_irqsave(&HFC_obj.lock, flags);
|
2004-06-17 12:31:14 +00:00
|
|
|
list_del(&card->list);
|
2005-11-07 14:53:57 +00:00
|
|
|
spin_unlock_irqrestore(&HFC_obj.lock, flags);
|
2001-08-02 14:55:45 +00:00
|
|
|
kfree(card);
|
|
|
|
if (!HFC_cnt) {
|
2003-07-21 12:00:05 +00:00
|
|
|
mISDN_unregister(&HFC_obj);
|
2001-08-02 14:55:45 +00:00
|
|
|
err = -ENODEV;
|
|
|
|
} else
|
|
|
|
printk(KERN_INFO "HFC %d cards installed\n",
|
|
|
|
HFC_cnt);
|
|
|
|
return(err);
|
|
|
|
}
|
2005-11-09 16:54:53 +00:00
|
|
|
card->dch.inst.class_dev.dev = &card->hw.dev->dev;
|
2001-08-02 14:55:45 +00:00
|
|
|
HFC_cnt++;
|
2001-11-14 10:41:26 +00:00
|
|
|
if (prev) {
|
|
|
|
dst = prev->dch.inst.st;
|
|
|
|
} else {
|
|
|
|
if ((err = HFC_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST,
|
|
|
|
&card->dch.inst))) {
|
|
|
|
printk(KERN_ERR "MGR_ADDSTACK REQUEST dch err(%d)\n", err);
|
|
|
|
release_card(card);
|
|
|
|
if (!HFC_cnt)
|
2003-07-21 12:00:05 +00:00
|
|
|
mISDN_unregister(&HFC_obj);
|
2001-11-14 10:41:26 +00:00
|
|
|
else
|
|
|
|
err = 0;
|
|
|
|
return(err);
|
|
|
|
}
|
|
|
|
dst = card->dch.inst.st;
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2005-05-30 16:42:28 +00:00
|
|
|
HFC_obj.ctrl(dst, MGR_STOPSTACK | REQUEST, NULL);
|
2001-11-14 10:41:26 +00:00
|
|
|
for (i = 0; i < 2; i++) {
|
2005-11-09 16:54:53 +00:00
|
|
|
card->bch[i].inst.class_dev.dev = &card->hw.dev->dev;
|
2001-11-14 10:41:26 +00:00
|
|
|
if ((err = HFC_obj.ctrl(dst,
|
|
|
|
MGR_NEWSTACK | REQUEST, &card->bch[i].inst))) {
|
2001-08-02 14:55:45 +00:00
|
|
|
printk(KERN_ERR "MGR_ADDSTACK bchan error %d\n", err);
|
|
|
|
HFC_obj.ctrl(card->dch.inst.st,
|
|
|
|
MGR_DELSTACK | REQUEST, NULL);
|
|
|
|
if (!HFC_cnt)
|
2003-07-21 12:00:05 +00:00
|
|
|
mISDN_unregister(&HFC_obj);
|
2001-08-02 14:55:45 +00:00
|
|
|
else
|
|
|
|
err = 0;
|
|
|
|
return(err);
|
|
|
|
}
|
2001-11-14 10:41:26 +00:00
|
|
|
}
|
|
|
|
if (protocol[HFC_cnt] != 0x100) { /* next not second HFC */
|
|
|
|
if ((err = HFC_obj.ctrl(dst, MGR_SETSTACK | REQUEST,
|
|
|
|
&pid))) {
|
|
|
|
printk(KERN_ERR "MGR_SETSTACK REQUEST dch err(%d)\n",
|
|
|
|
err);
|
|
|
|
HFC_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL);
|
|
|
|
if (!HFC_cnt)
|
2003-07-21 12:00:05 +00:00
|
|
|
mISDN_unregister(&HFC_obj);
|
2001-11-14 10:41:26 +00:00
|
|
|
else
|
|
|
|
err = 0;
|
|
|
|
return(err);
|
|
|
|
}
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
if ((err = init_card(card))) {
|
2001-11-14 10:41:26 +00:00
|
|
|
HFC_obj.ctrl(dst, MGR_DELSTACK | REQUEST, NULL);
|
2001-08-02 14:55:45 +00:00
|
|
|
if (!HFC_cnt)
|
2003-07-21 12:00:05 +00:00
|
|
|
mISDN_unregister(&HFC_obj);
|
2001-08-02 14:55:45 +00:00
|
|
|
else
|
|
|
|
err = 0;
|
|
|
|
return(err);
|
|
|
|
}
|
2005-05-30 16:42:28 +00:00
|
|
|
HFC_obj.ctrl(dst, MGR_STARTSTACK | REQUEST, NULL);
|
2003-08-01 22:15:53 +00:00
|
|
|
HFC_obj.ctrl(dst, MGR_CTRLREADY | INDICATION, NULL);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
|
|
|
printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MODULE
|
2003-07-27 11:14:19 +00:00
|
|
|
static void __exit HFC_cleanup(void)
|
2001-08-02 14:55:45 +00:00
|
|
|
{
|
2004-06-17 12:31:14 +00:00
|
|
|
hfc_pci_t *card, *next;
|
|
|
|
int err;
|
|
|
|
|
2003-07-21 12:00:05 +00:00
|
|
|
if ((err = mISDN_unregister(&HFC_obj))) {
|
2001-08-02 14:55:45 +00:00
|
|
|
printk(KERN_ERR "Can't unregister HFC PCI error(%d)\n", err);
|
|
|
|
}
|
2004-06-17 12:31:14 +00:00
|
|
|
list_for_each_entry_safe(card, next, &HFC_obj.ilist, list) {
|
2001-08-02 14:55:45 +00:00
|
|
|
printk(KERN_ERR "HFC PCI card struct not empty refs %d\n",
|
|
|
|
HFC_obj.refcnt);
|
2004-06-17 12:31:14 +00:00
|
|
|
release_card(card);
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2003-07-27 11:14:19 +00:00
|
|
|
return;
|
2001-08-02 14:55:45 +00:00
|
|
|
}
|
2003-07-27 11:14:19 +00:00
|
|
|
|
|
|
|
module_init(HFC_init);
|
|
|
|
module_exit(HFC_cleanup);
|
2001-08-02 14:55:45 +00:00
|
|
|
#endif
|