2005-04-17 02:20:36 +04:00
/* $Id: act2000_isa.c,v 1.11.6.3 2001/09/23 22:24:32 kai Exp $
*
* ISDN lowlevel - module for the IBM ISDN - S0 Active 2000 ( ISA - Version ) .
*
* Author Fritz Elfert
* Copyright by Fritz Elfert < fritz @ isdn4linux . de >
2012-02-20 07:52:38 +04:00
*
2005-04-17 02:20:36 +04:00
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*
* Thanks to Friedemann Baitinger and IBM Germany
*
*/
# include "act2000.h"
# include "act2000_isa.h"
# include "capi.h"
/*
* Reset Controller , then try to read the Card ' s signature .
+ Return :
* 1 = Signature found .
* 0 = Signature not found .
*/
static int
act2000_isa_reset ( unsigned short portbase )
{
2012-02-20 07:52:38 +04:00
unsigned char reg ;
int i ;
int found ;
int serial = 0 ;
found = 0 ;
if ( ( reg = inb ( portbase + ISA_COR ) ) ! = 0xff ) {
outb ( reg | ISA_COR_RESET , portbase + ISA_COR ) ;
mdelay ( 10 ) ;
outb ( reg , portbase + ISA_COR ) ;
mdelay ( 10 ) ;
for ( i = 0 ; i < 16 ; i + + ) {
if ( inb ( portbase + ISA_ISR ) & ISA_ISR_SERIAL )
serial | = 0x10000 ;
serial > > = 1 ;
}
if ( serial = = ISA_SER_ID )
found + + ;
}
return found ;
2005-04-17 02:20:36 +04:00
}
int
act2000_isa_detect ( unsigned short portbase )
{
2012-02-20 07:52:38 +04:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
if ( request_region ( portbase , ACT2000_PORTLEN , " act2000isa " ) ) {
2012-02-20 07:52:38 +04:00
ret = act2000_isa_reset ( portbase ) ;
2005-04-17 02:20:36 +04:00
release_region ( portbase , ISA_REGION ) ;
}
2012-02-20 07:52:38 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
static irqreturn_t
2007-10-20 03:30:28 +04:00
act2000_isa_interrupt ( int dummy , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2012-02-20 07:52:38 +04:00
act2000_card * card = dev_id ;
u_char istatus ;
2005-04-17 02:20:36 +04:00
2012-02-20 07:52:38 +04:00
istatus = ( inb ( ISA_PORT_ISR ) & 0x07 ) ;
if ( istatus & ISA_ISR_OUT ) {
/* RX fifo has data */
2005-04-17 02:20:36 +04:00
istatus & = ISA_ISR_OUT_MASK ;
outb ( 0 , ISA_PORT_SIS ) ;
act2000_isa_receive ( card ) ;
outb ( ISA_SIS_INT , ISA_PORT_SIS ) ;
2012-02-20 07:52:38 +04:00
}
if ( istatus & ISA_ISR_ERR ) {
/* Error Interrupt */
2005-04-17 02:20:36 +04:00
istatus & = ISA_ISR_ERR_MASK ;
2012-02-20 07:52:38 +04:00
printk ( KERN_WARNING " act2000: errIRQ \n " ) ;
}
2005-04-17 02:20:36 +04:00
if ( istatus )
2007-10-20 03:30:28 +04:00
printk ( KERN_DEBUG " act2000: ?IRQ %d %02x \n " , card - > irq , istatus ) ;
2005-04-17 02:20:36 +04:00
return IRQ_HANDLED ;
}
static void
2012-02-20 07:52:38 +04:00
act2000_isa_select_irq ( act2000_card * card )
2005-04-17 02:20:36 +04:00
{
unsigned char reg ;
reg = ( inb ( ISA_PORT_COR ) & ~ ISA_COR_IRQOFF ) | ISA_COR_PERR ;
switch ( card - > irq ) {
2012-02-20 07:52:38 +04:00
case 3 :
reg = ISA_COR_IRQ03 ;
break ;
case 5 :
reg = ISA_COR_IRQ05 ;
break ;
case 7 :
reg = ISA_COR_IRQ07 ;
break ;
case 10 :
reg = ISA_COR_IRQ10 ;
break ;
case 11 :
reg = ISA_COR_IRQ11 ;
break ;
case 12 :
reg = ISA_COR_IRQ12 ;
break ;
case 15 :
reg = ISA_COR_IRQ15 ;
break ;
2005-04-17 02:20:36 +04:00
}
outb ( reg , ISA_PORT_COR ) ;
}
static void
2012-02-20 07:52:38 +04:00
act2000_isa_enable_irq ( act2000_card * card )
2005-04-17 02:20:36 +04:00
{
act2000_isa_select_irq ( card ) ;
/* Enable READ irq */
outb ( ISA_SIS_INT , ISA_PORT_SIS ) ;
}
/*
* Install interrupt handler , enable irq on card .
2007-10-20 01:22:55 +04:00
* If irq is - 1 , choose next free irq , else irq is given explicitly .
2005-04-17 02:20:36 +04:00
*/
int
2012-02-20 07:52:38 +04:00
act2000_isa_config_irq ( act2000_card * card , short irq )
2005-04-17 02:20:36 +04:00
{
2007-10-20 03:30:28 +04:00
int old_irq ;
2012-02-20 07:52:38 +04:00
if ( card - > flags & ACT2000_FLAGS_IVALID ) {
free_irq ( card - > irq , card ) ;
}
card - > flags & = ~ ACT2000_FLAGS_IVALID ;
outb ( ISA_COR_IRQOFF , ISA_PORT_COR ) ;
if ( ! irq )
return 0 ;
2005-04-17 02:20:36 +04:00
2007-10-20 03:30:28 +04:00
old_irq = card - > irq ;
card - > irq = irq ;
if ( request_irq ( irq , & act2000_isa_interrupt , 0 , card - > regname , card ) ) {
card - > irq = old_irq ;
2005-04-17 02:20:36 +04:00
card - > flags | = ACT2000_FLAGS_IVALID ;
2012-02-20 07:52:38 +04:00
printk ( KERN_WARNING
" act2000: Could not request irq %d \n " , irq ) ;
return - EBUSY ;
} else {
2005-04-17 02:20:36 +04:00
act2000_isa_select_irq ( card ) ;
2012-02-20 07:52:38 +04:00
/* Disable READ and WRITE irq */
outb ( 0 , ISA_PORT_SIS ) ;
outb ( 0 , ISA_PORT_SOS ) ;
}
return 0 ;
2005-04-17 02:20:36 +04:00
}
int
2012-02-20 07:52:38 +04:00
act2000_isa_config_port ( act2000_card * card , unsigned short portbase )
2005-04-17 02:20:36 +04:00
{
2012-02-20 07:52:38 +04:00
if ( card - > flags & ACT2000_FLAGS_PVALID ) {
release_region ( card - > port , ISA_REGION ) ;
card - > flags & = ~ ACT2000_FLAGS_PVALID ;
}
2005-04-17 02:20:36 +04:00
if ( request_region ( portbase , ACT2000_PORTLEN , card - > regname ) = = NULL )
return - EBUSY ;
else {
2012-02-20 07:52:38 +04:00
card - > port = portbase ;
card - > flags | = ACT2000_FLAGS_PVALID ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
}
/*
* Release ressources , used by an adaptor .
*/
void
2012-02-20 07:52:38 +04:00
act2000_isa_release ( act2000_card * card )
2005-04-17 02:20:36 +04:00
{
2012-02-20 07:52:38 +04:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2012-02-20 07:52:38 +04:00
spin_lock_irqsave ( & card - > lock , flags ) ;
if ( card - > flags & ACT2000_FLAGS_IVALID )
free_irq ( card - > irq , card ) ;
2006-10-06 23:04:35 +04:00
2012-02-20 07:52:38 +04:00
card - > flags & = ~ ACT2000_FLAGS_IVALID ;
if ( card - > flags & ACT2000_FLAGS_PVALID )
release_region ( card - > port , ISA_REGION ) ;
card - > flags & = ~ ACT2000_FLAGS_PVALID ;
spin_unlock_irqrestore ( & card - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
static int
2012-02-20 07:52:38 +04:00
act2000_isa_writeb ( act2000_card * card , u_char data )
2005-04-17 02:20:36 +04:00
{
2012-02-20 07:52:38 +04:00
u_char timeout = 40 ;
while ( timeout ) {
if ( inb ( ISA_PORT_SOS ) & ISA_SOS_READY ) {
outb ( data , ISA_PORT_SDO ) ;
return 0 ;
} else {
timeout - - ;
udelay ( 10 ) ;
}
}
return 1 ;
2005-04-17 02:20:36 +04:00
}
static int
2012-02-20 07:52:38 +04:00
act2000_isa_readb ( act2000_card * card , u_char * data )
2005-04-17 02:20:36 +04:00
{
2012-02-20 07:52:38 +04:00
u_char timeout = 40 ;
while ( timeout ) {
if ( inb ( ISA_PORT_SIS ) & ISA_SIS_READY ) {
* data = inb ( ISA_PORT_SDI ) ;
return 0 ;
} else {
timeout - - ;
udelay ( 10 ) ;
}
}
return 1 ;
2005-04-17 02:20:36 +04:00
}
void
act2000_isa_receive ( act2000_card * card )
{
u_char c ;
2012-02-20 07:52:38 +04:00
if ( test_and_set_bit ( ACT2000_LOCK_RX , ( void * ) & card - > ilock ) ! = 0 )
2005-04-17 02:20:36 +04:00
return ;
while ( ! act2000_isa_readb ( card , & c ) ) {
if ( card - > idat . isa . rcvidx < 8 ) {
2012-02-20 07:52:38 +04:00
card - > idat . isa . rcvhdr [ card - > idat . isa . rcvidx + + ] = c ;
2005-04-17 02:20:36 +04:00
if ( card - > idat . isa . rcvidx = = 8 ) {
int valid = actcapi_chkhdr ( card , ( actcapi_msghdr * ) & card - > idat . isa . rcvhdr ) ;
if ( valid ) {
card - > idat . isa . rcvlen = ( ( actcapi_msghdr * ) & card - > idat . isa . rcvhdr ) - > len ;
card - > idat . isa . rcvskb = dev_alloc_skb ( card - > idat . isa . rcvlen ) ;
if ( card - > idat . isa . rcvskb = = NULL ) {
card - > idat . isa . rcvignore = 1 ;
printk ( KERN_WARNING
" act2000_isa_receive: no memory \n " ) ;
test_and_clear_bit ( ACT2000_LOCK_RX , ( void * ) & card - > ilock ) ;
return ;
}
memcpy ( skb_put ( card - > idat . isa . rcvskb , 8 ) , card - > idat . isa . rcvhdr , 8 ) ;
card - > idat . isa . rcvptr = skb_put ( card - > idat . isa . rcvskb , card - > idat . isa . rcvlen - 8 ) ;
} else {
card - > idat . isa . rcvidx = 0 ;
printk ( KERN_WARNING
" act2000_isa_receive: Invalid CAPI msg \n " ) ;
{
2009-02-25 16:09:52 +03:00
int i ; __u8 * p ; __u8 * t ; __u8 tmp [ 30 ] ;
for ( i = 0 , p = ( __u8 * ) & card - > idat . isa . rcvhdr , t = tmp ; i < 8 ; i + + )
t + = sprintf ( t , " %02x " , * ( p + + ) ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING " act2000_isa_receive: %s \n " , tmp ) ;
}
}
}
} else {
if ( ! card - > idat . isa . rcvignore )
* card - > idat . isa . rcvptr + + = c ;
if ( + + card - > idat . isa . rcvidx > = card - > idat . isa . rcvlen ) {
if ( ! card - > idat . isa . rcvignore ) {
skb_queue_tail ( & card - > rcvq , card - > idat . isa . rcvskb ) ;
act2000_schedule_rx ( card ) ;
}
card - > idat . isa . rcvidx = 0 ;
card - > idat . isa . rcvlen = 8 ;
card - > idat . isa . rcvignore = 0 ;
card - > idat . isa . rcvskb = NULL ;
card - > idat . isa . rcvptr = card - > idat . isa . rcvhdr ;
}
}
}
if ( ! ( card - > flags & ACT2000_FLAGS_IVALID ) ) {
/* In polling mode, schedule myself */
if ( ( card - > idat . isa . rcvidx ) & &
( card - > idat . isa . rcvignore | |
( card - > idat . isa . rcvidx < card - > idat . isa . rcvlen ) ) )
act2000_schedule_poll ( card ) ;
}
test_and_clear_bit ( ACT2000_LOCK_RX , ( void * ) & card - > ilock ) ;
}
void
2012-02-20 07:52:38 +04:00
act2000_isa_send ( act2000_card * card )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
struct sk_buff * skb ;
actcapi_msg * msg ;
int l ;
2012-02-20 07:52:38 +04:00
if ( test_and_set_bit ( ACT2000_LOCK_TX , ( void * ) & card - > ilock ) ! = 0 )
2005-04-17 02:20:36 +04:00
return ;
while ( 1 ) {
spin_lock_irqsave ( & card - > lock , flags ) ;
if ( ! ( card - > sbuf ) ) {
if ( ( card - > sbuf = skb_dequeue ( & card - > sndq ) ) ) {
card - > ack_msg = card - > sbuf - > data ;
msg = ( actcapi_msg * ) card - > sbuf - > data ;
if ( ( msg - > hdr . cmd . cmd = = 0x86 ) & &
2012-02-20 07:52:38 +04:00
( msg - > hdr . cmd . subcmd = = 0 ) ) {
2005-04-17 02:20:36 +04:00
/* Save flags in message */
card - > need_b3ack = msg - > msg . data_b3_req . flags ;
msg - > msg . data_b3_req . flags = 0 ;
}
}
}
spin_unlock_irqrestore ( & card - > lock , flags ) ;
if ( ! ( card - > sbuf ) ) {
/* No more data to send */
test_and_clear_bit ( ACT2000_LOCK_TX , ( void * ) & card - > ilock ) ;
return ;
}
skb = card - > sbuf ;
l = 0 ;
while ( skb - > len ) {
if ( act2000_isa_writeb ( card , * ( skb - > data ) ) ) {
/* Fifo is full, but more data to send */
test_and_clear_bit ( ACT2000_LOCK_TX , ( void * ) & card - > ilock ) ;
/* Schedule myself */
act2000_schedule_tx ( card ) ;
return ;
}
skb_pull ( skb , 1 ) ;
l + + ;
}
msg = ( actcapi_msg * ) card - > ack_msg ;
if ( ( msg - > hdr . cmd . cmd = = 0x86 ) & &
2012-02-20 07:52:38 +04:00
( msg - > hdr . cmd . subcmd = = 0 ) ) {
2005-04-17 02:20:36 +04:00
/*
* If it ' s user data , reset data - ptr
* and put skb into ackq .
*/
skb - > data = card - > ack_msg ;
/* Restore flags in message */
msg - > msg . data_b3_req . flags = card - > need_b3ack ;
skb_queue_tail ( & card - > ackq , skb ) ;
} else
dev_kfree_skb ( skb ) ;
card - > sbuf = NULL ;
}
}
/*
* Get firmware ID , check for ' ISDN ' signature .
*/
static int
2012-02-20 07:52:38 +04:00
act2000_isa_getid ( act2000_card * card )
2005-04-17 02:20:36 +04:00
{
2012-02-20 07:52:38 +04:00
act2000_fwid fid ;
u_char * p = ( u_char * ) & fid ;
int count = 0 ;
while ( 1 ) {
if ( count > 510 )
return - EPROTO ;
if ( act2000_isa_readb ( card , p + + ) )
break ;
count + + ;
}
if ( count < = 20 ) {
printk ( KERN_WARNING " act2000: No Firmware-ID! \n " ) ;
return - ETIME ;
}
* p = ' \0 ' ;
fid . revlen [ 0 ] = ' \0 ' ;
if ( strcmp ( fid . isdn , " ISDN " ) ) {
printk ( KERN_WARNING " act2000: Wrong Firmware-ID! \n " ) ;
return - EPROTO ;
}
2005-04-17 02:20:36 +04:00
if ( ( p = strchr ( fid . revision , ' \n ' ) ) )
* p = ' \0 ' ;
2012-02-20 07:52:38 +04:00
printk ( KERN_INFO " act2000: Firmware-ID: %s \n " , fid . revision ) ;
2005-04-17 02:20:36 +04:00
if ( card - > flags & ACT2000_FLAGS_IVALID ) {
printk ( KERN_DEBUG " Enabling Interrupts ... \n " ) ;
act2000_isa_enable_irq ( card ) ;
}
2012-02-20 07:52:38 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Download microcode into card , check Firmware signature .
*/
int
2012-02-20 07:52:38 +04:00
act2000_isa_download ( act2000_card * card , act2000_ddef __user * cb )
2005-04-17 02:20:36 +04:00
{
2012-02-20 07:52:38 +04:00
unsigned int length ;
int l ;
int c ;
long timeout ;
u_char * b ;
u_char __user * p ;
u_char * buf ;
act2000_ddef cblock ;
if ( ! act2000_isa_reset ( card - > port ) )
return - ENXIO ;
msleep_interruptible ( 500 ) ;
if ( copy_from_user ( & cblock , cb , sizeof ( cblock ) ) )
return - EFAULT ;
length = cblock . length ;
p = cblock . buffer ;
if ( ! access_ok ( VERIFY_READ , p , length ) )
return - EFAULT ;
buf = kmalloc ( 1024 , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
timeout = 0 ;
while ( length ) {
l = ( length > 1024 ) ? 1024 : length ;
c = 0 ;
b = buf ;
if ( copy_from_user ( buf , p , l ) ) {
kfree ( buf ) ;
return - EFAULT ;
}
while ( c < l ) {
if ( act2000_isa_writeb ( card , * b + + ) ) {
printk ( KERN_WARNING
" act2000: loader timed out "
" len=%d c=%d \n " , length , c ) ;
kfree ( buf ) ;
return - ETIME ;
}
c + + ;
}
length - = l ;
p + = l ;
}
kfree ( buf ) ;
msleep_interruptible ( 500 ) ;
return ( act2000_isa_getid ( card ) ) ;
2005-04-17 02:20:36 +04:00
}