2005-04-16 15:20:36 -07:00
/*
* Driver for ISAC - S and ISAC - SX
* ISDN Subscriber Access Controller for Terminals
*
* Author Kai Germaschewski
* Copyright 2001 by Kai Germaschewski < kai . germaschewski @ gmx . de >
* 2001 by Karsten Keil < keil @ isdn4linux . de >
*
* based upon Karsten Keil ' s original isac . c driver
*
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*
* Thanks to Wizard Computersysteme GmbH , Bremervoerde and
* SoHaNet Technology GmbH , Berlin
* for supporting the development of this driver
*/
/* TODO:
* specifically handle level vs edge triggered ?
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/netdevice.h>
# include "hisax_isac.h"
// debugging cruft
# define __debug_variable debug
# include "hisax_debug.h"
# ifdef CONFIG_HISAX_DEBUG
static int debug = 1 ;
module_param ( debug , int , 0 ) ;
static char * ISACVer [ ] = {
" 2086/2186 V1.1 " ,
" 2085 B1 " ,
" 2085 B2 " ,
" 2085 V2.3 "
} ;
# endif
MODULE_AUTHOR ( " Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de> " ) ;
MODULE_DESCRIPTION ( " ISAC/ISAC-SX driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define DBG_WARN 0x0001
# define DBG_IRQ 0x0002
# define DBG_L1M 0x0004
# define DBG_PR 0x0008
# define DBG_RFIFO 0x0100
# define DBG_RPACKET 0x0200
# define DBG_XFIFO 0x1000
# define DBG_XPACKET 0x2000
// we need to distinguish ISAC-S and ISAC-SX
# define TYPE_ISAC 0x00
# define TYPE_ISACSX 0x01
// registers etc.
# define ISAC_MASK 0x20
# define ISAC_ISTA 0x20
# define ISAC_ISTA_EXI 0x01
# define ISAC_ISTA_SIN 0x02
# define ISAC_ISTA_CISQ 0x04
# define ISAC_ISTA_XPR 0x10
# define ISAC_ISTA_RSC 0x20
# define ISAC_ISTA_RPF 0x40
# define ISAC_ISTA_RME 0x80
# define ISAC_STAR 0x21
# define ISAC_CMDR 0x21
# define ISAC_CMDR_XRES 0x01
# define ISAC_CMDR_XME 0x02
# define ISAC_CMDR_XTF 0x08
# define ISAC_CMDR_RRES 0x40
# define ISAC_CMDR_RMC 0x80
# define ISAC_EXIR 0x24
# define ISAC_EXIR_MOS 0x04
# define ISAC_EXIR_XDU 0x40
# define ISAC_EXIR_XMR 0x80
# define ISAC_ADF2 0x39
# define ISAC_SPCR 0x30
# define ISAC_ADF1 0x38
# define ISAC_CIR0 0x31
# define ISAC_CIX0 0x31
# define ISAC_CIR0_CIC0 0x02
# define ISAC_CIR0_CIC1 0x01
# define ISAC_CIR1 0x33
# define ISAC_CIX1 0x33
# define ISAC_STCR 0x37
# define ISAC_MODE 0x22
# define ISAC_RSTA 0x27
# define ISAC_RSTA_RDO 0x40
# define ISAC_RSTA_CRC 0x20
# define ISAC_RSTA_RAB 0x10
# define ISAC_RBCL 0x25
# define ISAC_RBCH 0x2A
# define ISAC_TIMR 0x23
# define ISAC_SQXR 0x3b
# define ISAC_MOSR 0x3a
# define ISAC_MOCR 0x3a
# define ISAC_MOR0 0x32
# define ISAC_MOX0 0x32
# define ISAC_MOR1 0x34
# define ISAC_MOX1 0x34
# define ISAC_RBCH_XAC 0x80
# define ISAC_CMD_TIM 0x0
# define ISAC_CMD_RES 0x1
# define ISAC_CMD_SSP 0x2
# define ISAC_CMD_SCP 0x3
# define ISAC_CMD_AR8 0x8
# define ISAC_CMD_AR10 0x9
# define ISAC_CMD_ARL 0xa
# define ISAC_CMD_DI 0xf
# define ISACSX_MASK 0x60
# define ISACSX_ISTA 0x60
# define ISACSX_ISTA_ICD 0x01
# define ISACSX_ISTA_CIC 0x10
# define ISACSX_MASKD 0x20
# define ISACSX_ISTAD 0x20
# define ISACSX_ISTAD_XDU 0x04
# define ISACSX_ISTAD_XMR 0x08
# define ISACSX_ISTAD_XPR 0x10
# define ISACSX_ISTAD_RFO 0x20
# define ISACSX_ISTAD_RPF 0x40
# define ISACSX_ISTAD_RME 0x80
# define ISACSX_CMDRD 0x21
# define ISACSX_CMDRD_XRES 0x01
# define ISACSX_CMDRD_XME 0x02
# define ISACSX_CMDRD_XTF 0x08
# define ISACSX_CMDRD_RRES 0x40
# define ISACSX_CMDRD_RMC 0x80
# define ISACSX_MODED 0x22
# define ISACSX_RBCLD 0x26
# define ISACSX_RSTAD 0x28
# define ISACSX_RSTAD_RAB 0x10
# define ISACSX_RSTAD_CRC 0x20
# define ISACSX_RSTAD_RDO 0x40
# define ISACSX_RSTAD_VFR 0x80
# define ISACSX_CIR0 0x2e
# define ISACSX_CIR0_CIC0 0x08
# define ISACSX_CIX0 0x2e
# define ISACSX_TR_CONF0 0x30
# define ISACSX_TR_CONF2 0x32
static struct Fsm l1fsm ;
enum {
ST_L1_RESET ,
ST_L1_F3_PDOWN ,
ST_L1_F3_PUP ,
ST_L1_F3_PEND_DEACT ,
ST_L1_F4 ,
ST_L1_F5 ,
ST_L1_F6 ,
ST_L1_F7 ,
ST_L1_F8 ,
} ;
# define L1_STATE_COUNT (ST_L1_F8+1)
static char * strL1State [ ] =
{
" ST_L1_RESET " ,
" ST_L1_F3_PDOWN " ,
" ST_L1_F3_PUP " ,
" ST_L1_F3_PEND_DEACT " ,
" ST_L1_F4 " ,
" ST_L1_F5 " ,
" ST_L1_F6 " ,
" ST_L1_F7 " ,
" ST_L1_F8 " ,
} ;
enum {
EV_PH_DR , // 0000
EV_PH_RES , // 0001
EV_PH_TMA , // 0010
EV_PH_SLD , // 0011
EV_PH_RSY , // 0100
EV_PH_DR6 , // 0101
EV_PH_EI , // 0110
EV_PH_PU , // 0111
EV_PH_AR , // 1000
EV_PH_9 , // 1001
EV_PH_ARL , // 1010
EV_PH_CVR , // 1011
EV_PH_AI8 , // 1100
EV_PH_AI10 , // 1101
EV_PH_AIL , // 1110
EV_PH_DC , // 1111
EV_PH_ACTIVATE_REQ ,
EV_PH_DEACTIVATE_REQ ,
EV_TIMER3 ,
} ;
# define L1_EVENT_COUNT (EV_TIMER3 + 1)
static char * strL1Event [ ] =
{
" EV_PH_DR " , // 0000
" EV_PH_RES " , // 0001
" EV_PH_TMA " , // 0010
" EV_PH_SLD " , // 0011
" EV_PH_RSY " , // 0100
" EV_PH_DR6 " , // 0101
" EV_PH_EI " , // 0110
" EV_PH_PU " , // 0111
" EV_PH_AR " , // 1000
" EV_PH_9 " , // 1001
" EV_PH_ARL " , // 1010
" EV_PH_CVR " , // 1011
" EV_PH_AI8 " , // 1100
" EV_PH_AI10 " , // 1101
" EV_PH_AIL " , // 1110
" EV_PH_DC " , // 1111
" EV_PH_ACTIVATE_REQ " ,
" EV_PH_DEACTIVATE_REQ " ,
" EV_TIMER3 " ,
} ;
static inline void D_L1L2 ( struct isac * isac , int pr , void * arg )
{
struct hisax_if * ifc = ( struct hisax_if * ) & isac - > hisax_d_if ;
DBG ( DBG_PR , " pr %#x " , pr ) ;
ifc - > l1l2 ( ifc , pr , arg ) ;
}
static void ph_command ( struct isac * isac , unsigned int command )
{
DBG ( DBG_L1M , " ph_command %#x " , command ) ;
switch ( isac - > type ) {
case TYPE_ISAC :
isac - > write_isac ( isac , ISAC_CIX0 , ( command < < 2 ) | 3 ) ;
break ;
case TYPE_ISACSX :
isac - > write_isac ( isac , ISACSX_CIX0 , ( command < < 4 ) | ( 7 < < 1 ) ) ;
break ;
}
}
// ----------------------------------------------------------------------
static void l1_di ( struct FsmInst * fi , int event , void * arg )
{
struct isac * isac = fi - > userdata ;
FsmChangeState ( fi , ST_L1_RESET ) ;
ph_command ( isac , ISAC_CMD_DI ) ;
}
static void l1_di_deact_ind ( struct FsmInst * fi , int event , void * arg )
{
struct isac * isac = fi - > userdata ;
FsmChangeState ( fi , ST_L1_RESET ) ;
D_L1L2 ( isac , PH_DEACTIVATE | INDICATION , NULL ) ;
ph_command ( isac , ISAC_CMD_DI ) ;
}
static void l1_go_f3pdown ( struct FsmInst * fi , int event , void * arg )
{
FsmChangeState ( fi , ST_L1_F3_PDOWN ) ;
}
static void l1_go_f3pend_deact_ind ( struct FsmInst * fi , int event , void * arg )
{
struct isac * isac = fi - > userdata ;
FsmChangeState ( fi , ST_L1_F3_PEND_DEACT ) ;
D_L1L2 ( isac , PH_DEACTIVATE | INDICATION , NULL ) ;
ph_command ( isac , ISAC_CMD_DI ) ;
}
static void l1_go_f3pend ( struct FsmInst * fi , int event , void * arg )
{
struct isac * isac = fi - > userdata ;
FsmChangeState ( fi , ST_L1_F3_PEND_DEACT ) ;
ph_command ( isac , ISAC_CMD_DI ) ;
}
static void l1_go_f4 ( struct FsmInst * fi , int event , void * arg )
{
FsmChangeState ( fi , ST_L1_F4 ) ;
}
static void l1_go_f5 ( struct FsmInst * fi , int event , void * arg )
{
FsmChangeState ( fi , ST_L1_F5 ) ;
}
static void l1_go_f6 ( struct FsmInst * fi , int event , void * arg )
{
FsmChangeState ( fi , ST_L1_F6 ) ;
}
static void l1_go_f6_deact_ind ( struct FsmInst * fi , int event , void * arg )
{
struct isac * isac = fi - > userdata ;
FsmChangeState ( fi , ST_L1_F6 ) ;
D_L1L2 ( isac , PH_DEACTIVATE | INDICATION , NULL ) ;
}
static void l1_go_f7_act_ind ( struct FsmInst * fi , int event , void * arg )
{
struct isac * isac = fi - > userdata ;
FsmDelTimer ( & isac - > timer , 0 ) ;
FsmChangeState ( fi , ST_L1_F7 ) ;
ph_command ( isac , ISAC_CMD_AR8 ) ;
D_L1L2 ( isac , PH_ACTIVATE | INDICATION , NULL ) ;
}
static void l1_go_f8 ( struct FsmInst * fi , int event , void * arg )
{
FsmChangeState ( fi , ST_L1_F8 ) ;
}
static void l1_go_f8_deact_ind ( struct FsmInst * fi , int event , void * arg )
{
struct isac * isac = fi - > userdata ;
FsmChangeState ( fi , ST_L1_F8 ) ;
D_L1L2 ( isac , PH_DEACTIVATE | INDICATION , NULL ) ;
}
static void l1_ar8 ( struct FsmInst * fi , int event , void * arg )
{
struct isac * isac = fi - > userdata ;
FsmRestartTimer ( & isac - > timer , TIMER3_VALUE , EV_TIMER3 , NULL , 2 ) ;
ph_command ( isac , ISAC_CMD_AR8 ) ;
}
static void l1_timer3 ( struct FsmInst * fi , int event , void * arg )
{
struct isac * isac = fi - > userdata ;
ph_command ( isac , ISAC_CMD_DI ) ;
D_L1L2 ( isac , PH_DEACTIVATE | INDICATION , NULL ) ;
}
// state machines according to data sheet PSB 2186 / 3186
static struct FsmNode L1FnList [ ] __initdata =
{
{ ST_L1_RESET , EV_PH_RES , l1_di } ,
{ ST_L1_RESET , EV_PH_EI , l1_di } ,
{ ST_L1_RESET , EV_PH_DC , l1_go_f3pdown } ,
{ ST_L1_RESET , EV_PH_AR , l1_go_f6 } ,
{ ST_L1_RESET , EV_PH_AI8 , l1_go_f7_act_ind } ,
{ ST_L1_F3_PDOWN , EV_PH_RES , l1_di } ,
{ ST_L1_F3_PDOWN , EV_PH_EI , l1_di } ,
{ ST_L1_F3_PDOWN , EV_PH_AR , l1_go_f6 } ,
{ ST_L1_F3_PDOWN , EV_PH_RSY , l1_go_f5 } ,
{ ST_L1_F3_PDOWN , EV_PH_PU , l1_go_f4 } ,
{ ST_L1_F3_PDOWN , EV_PH_AI8 , l1_go_f7_act_ind } ,
{ ST_L1_F3_PDOWN , EV_PH_ACTIVATE_REQ , l1_ar8 } ,
{ ST_L1_F3_PDOWN , EV_TIMER3 , l1_timer3 } ,
{ ST_L1_F3_PEND_DEACT , EV_PH_RES , l1_di } ,
{ ST_L1_F3_PEND_DEACT , EV_PH_EI , l1_di } ,
{ ST_L1_F3_PEND_DEACT , EV_PH_DC , l1_go_f3pdown } ,
{ ST_L1_F3_PEND_DEACT , EV_PH_RSY , l1_go_f5 } ,
{ ST_L1_F3_PEND_DEACT , EV_PH_AR , l1_go_f6 } ,
{ ST_L1_F3_PEND_DEACT , EV_PH_AI8 , l1_go_f7_act_ind } ,
{ ST_L1_F4 , EV_PH_RES , l1_di } ,
{ ST_L1_F4 , EV_PH_EI , l1_di } ,
{ ST_L1_F4 , EV_PH_RSY , l1_go_f5 } ,
{ ST_L1_F4 , EV_PH_AI8 , l1_go_f7_act_ind } ,
{ ST_L1_F4 , EV_TIMER3 , l1_timer3 } ,
{ ST_L1_F4 , EV_PH_DC , l1_go_f3pdown } ,
{ ST_L1_F5 , EV_PH_RES , l1_di } ,
{ ST_L1_F5 , EV_PH_EI , l1_di } ,
{ ST_L1_F5 , EV_PH_AR , l1_go_f6 } ,
{ ST_L1_F5 , EV_PH_AI8 , l1_go_f7_act_ind } ,
{ ST_L1_F5 , EV_TIMER3 , l1_timer3 } ,
{ ST_L1_F5 , EV_PH_DR , l1_go_f3pend } ,
{ ST_L1_F5 , EV_PH_DC , l1_go_f3pdown } ,
{ ST_L1_F6 , EV_PH_RES , l1_di } ,
{ ST_L1_F6 , EV_PH_EI , l1_di } ,
{ ST_L1_F6 , EV_PH_RSY , l1_go_f8 } ,
{ ST_L1_F6 , EV_PH_AI8 , l1_go_f7_act_ind } ,
{ ST_L1_F6 , EV_PH_DR6 , l1_go_f3pend } ,
{ ST_L1_F6 , EV_TIMER3 , l1_timer3 } ,
{ ST_L1_F6 , EV_PH_DC , l1_go_f3pdown } ,
{ ST_L1_F7 , EV_PH_RES , l1_di_deact_ind } ,
{ ST_L1_F7 , EV_PH_EI , l1_di_deact_ind } ,
{ ST_L1_F7 , EV_PH_AR , l1_go_f6_deact_ind } ,
{ ST_L1_F7 , EV_PH_RSY , l1_go_f8_deact_ind } ,
{ ST_L1_F7 , EV_PH_DR , l1_go_f3pend_deact_ind } ,
{ ST_L1_F8 , EV_PH_RES , l1_di } ,
{ ST_L1_F8 , EV_PH_EI , l1_di } ,
{ ST_L1_F8 , EV_PH_AR , l1_go_f6 } ,
{ ST_L1_F8 , EV_PH_DR , l1_go_f3pend } ,
{ ST_L1_F8 , EV_PH_AI8 , l1_go_f7_act_ind } ,
{ ST_L1_F8 , EV_TIMER3 , l1_timer3 } ,
{ ST_L1_F8 , EV_PH_DC , l1_go_f3pdown } ,
} ;
static void l1m_debug ( struct FsmInst * fi , char * fmt , . . . )
{
va_list args ;
char buf [ 256 ] ;
va_start ( args , fmt ) ;
2006-12-08 02:39:34 -08:00
vsnprintf ( buf , sizeof ( buf ) , fmt , args ) ;
2005-04-16 15:20:36 -07:00
DBG ( DBG_L1M , " %s " , buf ) ;
va_end ( args ) ;
}
static void isac_version ( struct isac * cs )
{
int val ;
val = cs - > read_isac ( cs , ISAC_RBCH ) ;
DBG ( 1 , " ISAC version (%x): %s " , val , ISACVer [ ( val > > 5 ) & 3 ] ) ;
}
static void isac_empty_fifo ( struct isac * isac , int count )
{
// this also works for isacsx, since
// CMDR(D) register works the same
u_char * ptr ;
DBG ( DBG_IRQ , " count %d " , count ) ;
if ( ( isac - > rcvidx + count ) > = MAX_DFRAME_LEN_L1 ) {
DBG ( DBG_WARN , " overrun %d " , isac - > rcvidx + count ) ;
isac - > write_isac ( isac , ISAC_CMDR , ISAC_CMDR_RMC ) ;
isac - > rcvidx = 0 ;
return ;
}
ptr = isac - > rcvbuf + isac - > rcvidx ;
isac - > rcvidx + = count ;
isac - > read_isac_fifo ( isac , ptr , count ) ;
isac - > write_isac ( isac , ISAC_CMDR , ISAC_CMDR_RMC ) ;
DBG_PACKET ( DBG_RFIFO , ptr , count ) ;
}
static void isac_fill_fifo ( struct isac * isac )
{
// this also works for isacsx, since
// CMDR(D) register works the same
int count ;
unsigned char cmd ;
u_char * ptr ;
2006-03-26 18:19:26 +02:00
BUG_ON ( ! isac - > tx_skb ) ;
2005-04-16 15:20:36 -07:00
count = isac - > tx_skb - > len ;
2006-03-26 18:19:26 +02:00
BUG_ON ( count < = 0 ) ;
2005-04-16 15:20:36 -07:00
DBG ( DBG_IRQ , " count %d " , count ) ;
if ( count > 0x20 ) {
count = 0x20 ;
cmd = ISAC_CMDR_XTF ;
} else {
cmd = ISAC_CMDR_XTF | ISAC_CMDR_XME ;
}
ptr = isac - > tx_skb - > data ;
skb_pull ( isac - > tx_skb , count ) ;
isac - > tx_cnt + = count ;
DBG_PACKET ( DBG_XFIFO , ptr , count ) ;
isac - > write_isac_fifo ( isac , ptr , count ) ;
isac - > write_isac ( isac , ISAC_CMDR , cmd ) ;
}
static void isac_retransmit ( struct isac * isac )
{
if ( ! isac - > tx_skb ) {
DBG ( DBG_WARN , " no skb " ) ;
return ;
}
skb_push ( isac - > tx_skb , isac - > tx_cnt ) ;
isac - > tx_cnt = 0 ;
}
static inline void isac_cisq_interrupt ( struct isac * isac )
{
unsigned char val ;
val = isac - > read_isac ( isac , ISAC_CIR0 ) ;
DBG ( DBG_IRQ , " CIR0 %#x " , val ) ;
if ( val & ISAC_CIR0_CIC0 ) {
DBG ( DBG_IRQ , " CODR0 %#x " , ( val > > 2 ) & 0xf ) ;
FsmEvent ( & isac - > l1m , ( val > > 2 ) & 0xf , NULL ) ;
}
if ( val & ISAC_CIR0_CIC1 ) {
val = isac - > read_isac ( isac , ISAC_CIR1 ) ;
DBG ( DBG_WARN , " ISAC CIR1 %#x " , val ) ;
}
}
static inline void isac_rme_interrupt ( struct isac * isac )
{
unsigned char val ;
int count ;
struct sk_buff * skb ;
val = isac - > read_isac ( isac , ISAC_RSTA ) ;
if ( ( val & ( ISAC_RSTA_RDO | ISAC_RSTA_CRC | ISAC_RSTA_RAB ) )
! = ISAC_RSTA_CRC ) {
DBG ( DBG_WARN , " RSTA %#x, dropped " , val ) ;
isac - > write_isac ( isac , ISAC_CMDR , ISAC_CMDR_RMC ) ;
goto out ;
}
count = isac - > read_isac ( isac , ISAC_RBCL ) & 0x1f ;
DBG ( DBG_IRQ , " RBCL %#x " , count ) ;
if ( count = = 0 )
count = 0x20 ;
isac_empty_fifo ( isac , count ) ;
count = isac - > rcvidx ;
if ( count < 1 ) {
DBG ( DBG_WARN , " count %d < 1 " , count ) ;
goto out ;
}
skb = alloc_skb ( count , GFP_ATOMIC ) ;
if ( ! skb ) {
DBG ( DBG_WARN , " no memory, dropping \n " ) ;
goto out ;
}
memcpy ( skb_put ( skb , count ) , isac - > rcvbuf , count ) ;
DBG_SKB ( DBG_RPACKET , skb ) ;
D_L1L2 ( isac , PH_DATA | INDICATION , skb ) ;
out :
isac - > rcvidx = 0 ;
}
static inline void isac_xpr_interrupt ( struct isac * isac )
{
if ( ! isac - > tx_skb )
return ;
if ( isac - > tx_skb - > len > 0 ) {
isac_fill_fifo ( isac ) ;
return ;
}
dev_kfree_skb_irq ( isac - > tx_skb ) ;
isac - > tx_cnt = 0 ;
isac - > tx_skb = NULL ;
D_L1L2 ( isac , PH_DATA | CONFIRM , NULL ) ;
}
static inline void isac_exi_interrupt ( struct isac * isac )
{
unsigned char val ;
val = isac - > read_isac ( isac , ISAC_EXIR ) ;
DBG ( 2 , " EXIR %#x " , val ) ;
if ( val & ISAC_EXIR_XMR ) {
DBG ( DBG_WARN , " ISAC XMR " ) ;
isac_retransmit ( isac ) ;
}
if ( val & ISAC_EXIR_XDU ) {
DBG ( DBG_WARN , " ISAC XDU " ) ;
isac_retransmit ( isac ) ;
}
if ( val & ISAC_EXIR_MOS ) { /* MOS */
DBG ( DBG_WARN , " MOS " ) ;
val = isac - > read_isac ( isac , ISAC_MOSR ) ;
DBG ( 2 , " ISAC MOSR %#x " , val ) ;
}
}
void isac_irq ( struct isac * isac )
{
unsigned char val ;
val = isac - > read_isac ( isac , ISAC_ISTA ) ;
DBG ( DBG_IRQ , " ISTA %#x " , val ) ;
if ( val & ISAC_ISTA_EXI ) {
DBG ( DBG_IRQ , " EXI " ) ;
isac_exi_interrupt ( isac ) ;
}
if ( val & ISAC_ISTA_XPR ) {
DBG ( DBG_IRQ , " XPR " ) ;
isac_xpr_interrupt ( isac ) ;
}
if ( val & ISAC_ISTA_RME ) {
DBG ( DBG_IRQ , " RME " ) ;
isac_rme_interrupt ( isac ) ;
}
if ( val & ISAC_ISTA_RPF ) {
DBG ( DBG_IRQ , " RPF " ) ;
isac_empty_fifo ( isac , 0x20 ) ;
}
if ( val & ISAC_ISTA_CISQ ) {
DBG ( DBG_IRQ , " CISQ " ) ;
isac_cisq_interrupt ( isac ) ;
}
if ( val & ISAC_ISTA_RSC ) {
DBG ( DBG_WARN , " RSC " ) ;
}
if ( val & ISAC_ISTA_SIN ) {
DBG ( DBG_WARN , " SIN " ) ;
}
isac - > write_isac ( isac , ISAC_MASK , 0xff ) ;
isac - > write_isac ( isac , ISAC_MASK , 0x00 ) ;
}
// ======================================================================
static inline void isacsx_cic_interrupt ( struct isac * isac )
{
unsigned char val ;
val = isac - > read_isac ( isac , ISACSX_CIR0 ) ;
DBG ( DBG_IRQ , " CIR0 %#x " , val ) ;
if ( val & ISACSX_CIR0_CIC0 ) {
DBG ( DBG_IRQ , " CODR0 %#x " , val > > 4 ) ;
FsmEvent ( & isac - > l1m , val > > 4 , NULL ) ;
}
}
static inline void isacsx_rme_interrupt ( struct isac * isac )
{
int count ;
struct sk_buff * skb ;
unsigned char val ;
val = isac - > read_isac ( isac , ISACSX_RSTAD ) ;
if ( ( val & ( ISACSX_RSTAD_VFR |
ISACSX_RSTAD_RDO |
ISACSX_RSTAD_CRC |
ISACSX_RSTAD_RAB ) )
! = ( ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC ) ) {
DBG ( DBG_WARN , " RSTAD %#x, dropped " , val ) ;
isac - > write_isac ( isac , ISACSX_CMDRD , ISACSX_CMDRD_RMC ) ;
goto out ;
}
count = isac - > read_isac ( isac , ISACSX_RBCLD ) & 0x1f ;
DBG ( DBG_IRQ , " RBCLD %#x " , count ) ;
if ( count = = 0 )
count = 0x20 ;
isac_empty_fifo ( isac , count ) ;
// strip trailing status byte
count = isac - > rcvidx - 1 ;
if ( count < 1 ) {
DBG ( DBG_WARN , " count %d < 1 " , count ) ;
goto out ;
}
skb = dev_alloc_skb ( count ) ;
if ( ! skb ) {
DBG ( DBG_WARN , " no memory, dropping " ) ;
goto out ;
}
memcpy ( skb_put ( skb , count ) , isac - > rcvbuf , count ) ;
DBG_SKB ( DBG_RPACKET , skb ) ;
D_L1L2 ( isac , PH_DATA | INDICATION , skb ) ;
out :
isac - > rcvidx = 0 ;
}
static inline void isacsx_xpr_interrupt ( struct isac * isac )
{
if ( ! isac - > tx_skb )
return ;
if ( isac - > tx_skb - > len > 0 ) {
isac_fill_fifo ( isac ) ;
return ;
}
dev_kfree_skb_irq ( isac - > tx_skb ) ;
isac - > tx_skb = NULL ;
isac - > tx_cnt = 0 ;
D_L1L2 ( isac , PH_DATA | CONFIRM , NULL ) ;
}
static inline void isacsx_icd_interrupt ( struct isac * isac )
{
unsigned char val ;
val = isac - > read_isac ( isac , ISACSX_ISTAD ) ;
DBG ( DBG_IRQ , " ISTAD %#x " , val ) ;
if ( val & ISACSX_ISTAD_XDU ) {
DBG ( DBG_WARN , " ISTAD XDU " ) ;
isac_retransmit ( isac ) ;
}
if ( val & ISACSX_ISTAD_XMR ) {
DBG ( DBG_WARN , " ISTAD XMR " ) ;
isac_retransmit ( isac ) ;
}
if ( val & ISACSX_ISTAD_XPR ) {
DBG ( DBG_IRQ , " ISTAD XPR " ) ;
isacsx_xpr_interrupt ( isac ) ;
}
if ( val & ISACSX_ISTAD_RFO ) {
DBG ( DBG_WARN , " ISTAD RFO " ) ;
isac - > write_isac ( isac , ISACSX_CMDRD , ISACSX_CMDRD_RMC ) ;
}
if ( val & ISACSX_ISTAD_RME ) {
DBG ( DBG_IRQ , " ISTAD RME " ) ;
isacsx_rme_interrupt ( isac ) ;
}
if ( val & ISACSX_ISTAD_RPF ) {
DBG ( DBG_IRQ , " ISTAD RPF " ) ;
isac_empty_fifo ( isac , 0x20 ) ;
}
}
void isacsx_irq ( struct isac * isac )
{
unsigned char val ;
val = isac - > read_isac ( isac , ISACSX_ISTA ) ;
DBG ( DBG_IRQ , " ISTA %#x " , val ) ;
if ( val & ISACSX_ISTA_ICD )
isacsx_icd_interrupt ( isac ) ;
if ( val & ISACSX_ISTA_CIC )
isacsx_cic_interrupt ( isac ) ;
}
void isac_init ( struct isac * isac )
{
isac - > tx_skb = NULL ;
isac - > l1m . fsm = & l1fsm ;
isac - > l1m . state = ST_L1_RESET ;
# ifdef CONFIG_HISAX_DEBUG
isac - > l1m . debug = 1 ;
# else
isac - > l1m . debug = 0 ;
# endif
isac - > l1m . userdata = isac ;
isac - > l1m . printdebug = l1m_debug ;
FsmInitTimer ( & isac - > l1m , & isac - > timer ) ;
}
void isac_setup ( struct isac * isac )
{
int val , eval ;
isac - > type = TYPE_ISAC ;
isac_version ( isac ) ;
ph_command ( isac , ISAC_CMD_RES ) ;
isac - > write_isac ( isac , ISAC_MASK , 0xff ) ;
isac - > mocr = 0xaa ;
if ( test_bit ( ISAC_IOM1 , & isac - > flags ) ) {
/* IOM 1 Mode */
isac - > write_isac ( isac , ISAC_ADF2 , 0x0 ) ;
isac - > write_isac ( isac , ISAC_SPCR , 0xa ) ;
isac - > write_isac ( isac , ISAC_ADF1 , 0x2 ) ;
isac - > write_isac ( isac , ISAC_STCR , 0x70 ) ;
isac - > write_isac ( isac , ISAC_MODE , 0xc9 ) ;
} else {
/* IOM 2 Mode */
if ( ! isac - > adf2 )
isac - > adf2 = 0x80 ;
isac - > write_isac ( isac , ISAC_ADF2 , isac - > adf2 ) ;
isac - > write_isac ( isac , ISAC_SQXR , 0x2f ) ;
isac - > write_isac ( isac , ISAC_SPCR , 0x00 ) ;
isac - > write_isac ( isac , ISAC_STCR , 0x70 ) ;
isac - > write_isac ( isac , ISAC_MODE , 0xc9 ) ;
isac - > write_isac ( isac , ISAC_TIMR , 0x00 ) ;
isac - > write_isac ( isac , ISAC_ADF1 , 0x00 ) ;
}
val = isac - > read_isac ( isac , ISAC_STAR ) ;
DBG ( 2 , " ISAC STAR %x " , val ) ;
val = isac - > read_isac ( isac , ISAC_MODE ) ;
DBG ( 2 , " ISAC MODE %x " , val ) ;
val = isac - > read_isac ( isac , ISAC_ADF2 ) ;
DBG ( 2 , " ISAC ADF2 %x " , val ) ;
val = isac - > read_isac ( isac , ISAC_ISTA ) ;
DBG ( 2 , " ISAC ISTA %x " , val ) ;
if ( val & 0x01 ) {
eval = isac - > read_isac ( isac , ISAC_EXIR ) ;
DBG ( 2 , " ISAC EXIR %x " , eval ) ;
}
val = isac - > read_isac ( isac , ISAC_CIR0 ) ;
DBG ( 2 , " ISAC CIR0 %x " , val ) ;
FsmEvent ( & isac - > l1m , ( val > > 2 ) & 0xf , NULL ) ;
isac - > write_isac ( isac , ISAC_MASK , 0x0 ) ;
// RESET Receiver and Transmitter
isac - > write_isac ( isac , ISAC_CMDR , ISAC_CMDR_XRES | ISAC_CMDR_RRES ) ;
}
void isacsx_setup ( struct isac * isac )
{
isac - > type = TYPE_ISACSX ;
// clear LDD
isac - > write_isac ( isac , ISACSX_TR_CONF0 , 0x00 ) ;
// enable transmitter
isac - > write_isac ( isac , ISACSX_TR_CONF2 , 0x00 ) ;
// transparent mode 0, RAC, stop/go
isac - > write_isac ( isac , ISACSX_MODED , 0xc9 ) ;
// all HDLC IRQ unmasked
isac - > write_isac ( isac , ISACSX_MASKD , 0x03 ) ;
// unmask ICD, CID IRQs
isac - > write_isac ( isac , ISACSX_MASK ,
~ ( ISACSX_ISTA_ICD | ISACSX_ISTA_CIC ) ) ;
}
void isac_d_l2l1 ( struct hisax_if * hisax_d_if , int pr , void * arg )
{
struct isac * isac = hisax_d_if - > priv ;
struct sk_buff * skb = arg ;
DBG ( DBG_PR , " pr %#x " , pr ) ;
switch ( pr ) {
case PH_ACTIVATE | REQUEST :
FsmEvent ( & isac - > l1m , EV_PH_ACTIVATE_REQ , NULL ) ;
break ;
case PH_DEACTIVATE | REQUEST :
FsmEvent ( & isac - > l1m , EV_PH_DEACTIVATE_REQ , NULL ) ;
break ;
case PH_DATA | REQUEST :
DBG ( DBG_PR , " PH_DATA REQUEST len %d " , skb - > len ) ;
DBG_SKB ( DBG_XPACKET , skb ) ;
if ( isac - > l1m . state ! = ST_L1_F7 ) {
DBG ( 1 , " L1 wrong state %d \n " , isac - > l1m . state ) ;
dev_kfree_skb ( skb ) ;
break ;
}
2006-03-26 18:19:26 +02:00
BUG_ON ( isac - > tx_skb ) ;
2005-04-16 15:20:36 -07:00
isac - > tx_skb = skb ;
isac_fill_fifo ( isac ) ;
break ;
}
}
static int __init hisax_isac_init ( void )
{
printk ( KERN_INFO " hisax_isac: ISAC-S/ISAC-SX ISDN driver v0.1.0 \n " ) ;
l1fsm . state_count = L1_STATE_COUNT ;
l1fsm . event_count = L1_EVENT_COUNT ;
l1fsm . strState = strL1State ;
l1fsm . strEvent = strL1Event ;
return FsmNew ( & l1fsm , L1FnList , ARRAY_SIZE ( L1FnList ) ) ;
}
static void __exit hisax_isac_exit ( void )
{
FsmFree ( & l1fsm ) ;
}
EXPORT_SYMBOL ( isac_init ) ;
EXPORT_SYMBOL ( isac_d_l2l1 ) ;
EXPORT_SYMBOL ( isacsx_setup ) ;
EXPORT_SYMBOL ( isacsx_irq ) ;
EXPORT_SYMBOL ( isac_setup ) ;
EXPORT_SYMBOL ( isac_irq ) ;
module_init ( hisax_isac_init ) ;
module_exit ( hisax_isac_exit ) ;