2005-04-17 02:20:36 +04:00
/*****************************************************************
*
* Filename : donauboe . c
* Version : 2.17
* Description : Driver for the Toshiba OBOE ( or type - O or 701 )
* FIR Chipset , also supports the DONAUOBOE ( type - DO
* or d01 ) FIR chipset which as far as I know is
* register compatible .
* Documentation : http : //libxg.free.fr/irda/lib-irda.html
* Status : Experimental .
* Author : James McKenzie < james @ fishsoup . dhs . org >
* Created at : Sat May 8 12 : 35 : 27 1999
* Modified : Paul Bristow < paul . bristow @ technologist . com >
* Modified : Mon Nov 11 19 : 10 : 05 1999
* Modified : James McKenzie < james @ fishsoup . dhs . org >
* Modified : Thu Mar 16 12 : 49 : 00 2000 ( Substantial rewrite )
* Modified : Sat Apr 29 00 : 23 : 03 2000 ( Added DONAUOBOE support )
* Modified : Wed May 24 23 : 45 : 02 2000 ( Fixed chipio_t structure )
* Modified : 2.13 Christian Gennerat < christian . gennerat @ polytechnique . org >
* Modified : 2.13 dim jan 07 21 : 57 : 39 2001 ( tested with kernel 2.4 & irnet / ppp )
* Modified : 2.14 Christian Gennerat < christian . gennerat @ polytechnique . org >
* Modified : 2.14 lun fev 05 17 : 55 : 59 2001 ( adapted to patch - 2.4 .1 - pre8 - irda1 )
* Modified : 2.15 Martin Lucina < mato @ kotelna . sk >
* Modified : 2.15 Fri Jun 21 20 : 40 : 59 2002 ( sync with 2.4 .18 , substantial fixes )
* Modified : 2.16 Martin Lucina < mato @ kotelna . sk >
* Modified : 2.16 Sat Jun 22 18 : 54 : 29 2002 ( fix freeregion , default to verbose )
* Modified : 2.17 Christian Gennerat < christian . gennerat @ polytechnique . org >
* Modified : 2.17 jeu sep 12 08 : 50 : 20 2002 ( save_flags ( ) ; cli ( ) ; replaced by spinlocks )
* Modified : 2.18 Christian Gennerat < christian . gennerat @ polytechnique . org >
* Modified : 2.18 ven jan 10 03 : 14 : 16 2003 Change probe default options
*
* Copyright ( c ) 1999 James McKenzie , All Rights Reserved .
*
* 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 of
* the License , or ( at your option ) any later version .
*
* Neither James McKenzie nor Cambridge University admit liability nor
* provide warranty for any of this software . This material is
* provided " AS-IS " and at no charge .
*
* Applicable Models : Libretto 100 / 110 CT and many more .
* Toshiba refers to this chip as the type - O IR port ,
* or the type - DO IR port .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Look at toshoboe.h (currently in include/net/irda) for details of */
/* Where to get documentation on the chip */
static char * rcsid =
" $Id: donauboe.c V2.18 ven jan 10 03:14:16 2003$ " ;
/* See below for a description of the logic in this driver */
/* User servicable parts */
/* USE_PROBE Create the code which probes the chip and does a few tests */
/* do_probe module parameter Enable this code */
/* Probe code is very useful for understanding how the hardware works */
/* Use it with various combinations of TT_LEN, RX_LEN */
/* Strongly recomended, disable if the probe fails on your machine */
/* and send me <james@fishsoup.dhs.org> the output of dmesg */
# define USE_PROBE 1
# undef USE_PROBE
/* Trace Transmit ring, interrupts, Receive ring or not ? */
# define PROBE_VERBOSE 1
/* Debug option, examine sent and received raw data */
/* Irdadump is better, but does not see all packets. enable it if you want. */
# undef DUMP_PACKETS
/* MIR mode has not been tested. Some behaviour is different */
/* Seems to work against an Ericsson R520 for me. -Martin */
# define USE_MIR
/* Schedule back to back hardware transmits wherever possible, otherwise */
/* we need an interrupt for every frame, unset if oboe works for a bit and */
/* then hangs */
# define OPTIMIZE_TX
/* Set the number of slots in the rings */
/* If you get rx/tx fifo overflows at high bitrates, you can try increasing */
/* these */
# define RING_SIZE (OBOE_RING_SIZE_RX8 | OBOE_RING_SIZE_TX8)
# define TX_SLOTS 8
# define RX_SLOTS 8
/* Less user servicable parts below here */
/* Test, Transmit and receive buffer sizes, adjust at your peril */
/* remarks: nfs usually needs 1k blocks */
/* remarks: in SIR mode, CRC is received, -> RX_LEN=TX_LEN+2 */
/* remarks: test accepts large blocks. Standard is 0x80 */
/* When TT_LEN > RX_LEN (SIR mode) data is stored in successive slots. */
/* When 3 or more slots are needed for each test packet, */
/* data received in the first slots is overwritten, even */
/* if OBOE_CTL_RX_HW_OWNS is not set, without any error! */
# define TT_LEN 0x80
# define TX_LEN 0xc00
# define RX_LEN 0xc04
/* Real transmitted length (SIR mode) is about 14+(2%*TX_LEN) more */
/* long than user-defined length (see async_wrap_skb) and is less then 4K */
/* Real received length is (max RX_LEN) differs from user-defined */
/* length only b the CRC (2 or 4 bytes) */
# define BUF_SAFETY 0x7a
# define RX_BUF_SZ (RX_LEN)
# define TX_BUF_SZ (TX_LEN+BUF_SAFETY)
/* Logic of the netdev part of this driver */
/* The RX ring is filled with buffers, when a packet arrives */
/* it is DMA'd into the buffer which is marked used and RxDone called */
/* RxDone forms an skb (and checks the CRC if in SIR mode) and ships */
/* the packet off upstairs */
/* The transmitter on the oboe chip can work in one of two modes */
/* for each ring->tx[] the transmitter can either */
/* a) transmit the packet, leave the trasmitter enabled and proceed to */
/* the next ring */
/* OR */
/* b) transmit the packet, switch off the transmitter and issue TxDone */
/* All packets are entered into the ring in mode b), if the ring was */
/* empty the transmitter is started. */
/* If OPTIMIZE_TX is defined then in TxDone if the ring contains */
/* more than one packet, all but the last are set to mode a) [HOWEVER */
/* the hardware may not notice this, this is why we start in mode b) ] */
/* then restart the transmitter */
/* If OPTIMIZE_TX is not defined then we just restart the transmitter */
/* if the ring isn't empty */
/* Speed changes are delayed until the TxRing is empty */
/* mtt is handled by generating packets with bad CRCs, before the data */
/* TODO: */
/* check the mtt works ok */
/* finish the watchdog */
/* No user servicable parts below here */
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/skbuff.h>
# include <linux/netdevice.h>
# include <linux/ioport.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/pci.h>
# include <linux/rtnetlink.h>
# include <asm/system.h>
# include <asm/io.h>
# include <net/irda/wrapper.h>
# include <net/irda/irda.h>
//#include <net/irda/irmod.h>
//#include <net/irda/irlap_frame.h>
# include <net/irda/irda_device.h>
# include <net/irda/crc.h>
# include "donauboe.h"
# define INB(port) inb_p(port)
# define OUTB(val,port) outb_p(val,port)
# define OUTBP(val,port) outb_p(val,port)
# define PROMPT OUTB(OBOE_PROMPT_BIT,OBOE_PROMPT);
# if PROBE_VERBOSE
# define PROBE_DEBUG(args...) (printk (args))
# else
# define PROBE_DEBUG(args...) ;
# endif
/* Set the DMA to be byte at a time */
# define CONFIG0H_DMA_OFF OBOE_CONFIG0H_RCVANY
# define CONFIG0H_DMA_ON_NORX CONFIG0H_DMA_OFF| OBOE_CONFIG0H_ENDMAC
# define CONFIG0H_DMA_ON CONFIG0H_DMA_ON_NORX | OBOE_CONFIG0H_ENRX
static struct pci_device_id toshoboe_pci_tbl [ ] = {
{ PCI_VENDOR_ID_TOSHIBA , PCI_DEVICE_ID_FIR701 , PCI_ANY_ID , PCI_ANY_ID , } ,
{ PCI_VENDOR_ID_TOSHIBA , PCI_DEVICE_ID_FIRD01 , PCI_ANY_ID , PCI_ANY_ID , } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( pci , toshoboe_pci_tbl ) ;
# define DRIVER_NAME "toshoboe"
static char * driver_name = DRIVER_NAME ;
static int max_baud = 4000000 ;
# ifdef USE_PROBE
static int do_probe = 0 ;
# endif
/**********************************************************************/
static int
toshoboe_checkfcs ( unsigned char * buf , int len )
{
int i ;
union
{
__u16 value ;
__u8 bytes [ 2 ] ;
}
fcs ;
fcs . value = INIT_FCS ;
for ( i = 0 ; i < len ; + + i )
fcs . value = irda_fcs ( fcs . value , * ( buf + + ) ) ;
return ( fcs . value = = GOOD_FCS ) ;
}
/***********************************************************************/
/* Generic chip handling code */
# ifdef DUMP_PACKETS
static unsigned char dump [ 50 ] ;
static void
_dumpbufs ( unsigned char * data , int len , char tete )
{
int i , j ;
char head = tete ;
for ( i = 0 ; i < len ; i + = 16 ) {
for ( j = 0 ; j < 16 & & i + j < len ; j + + ) { sprintf ( & dump [ 3 * j ] , " %02x. " , data [ i + j ] ) ; }
dump [ 3 * j ] = 0 ;
IRDA_DEBUG ( 2 , " %c%s \n " , head , dump ) ;
head = ' + ' ;
}
}
# endif
# ifdef USE_PROBE
/* Dump the registers */
static void
toshoboe_dumpregs ( struct toshoboe_cb * self )
{
__u32 ringbase ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
ringbase = INB ( OBOE_RING_BASE0 ) < < 10 ;
ringbase | = INB ( OBOE_RING_BASE1 ) < < 18 ;
ringbase | = INB ( OBOE_RING_BASE2 ) < < 26 ;
printk ( KERN_ERR DRIVER_NAME " : Register dump: \n " ) ;
printk ( KERN_ERR " Interrupts: Tx:%d Rx:%d TxUnder:%d RxOver:%d Sip:%d \n " ,
self - > int_tx , self - > int_rx , self - > int_txunder , self - > int_rxover ,
self - > int_sip ) ;
printk ( KERN_ERR " RX %02x TX %02x RingBase %08x \n " ,
INB ( OBOE_RXSLOT ) , INB ( OBOE_TXSLOT ) , ringbase ) ;
printk ( KERN_ERR " RING_SIZE %02x IER %02x ISR %02x \n " ,
INB ( OBOE_RING_SIZE ) , INB ( OBOE_IER ) , INB ( OBOE_ISR ) ) ;
printk ( KERN_ERR " CONFIG1 %02x STATUS %02x \n " ,
INB ( OBOE_CONFIG1 ) , INB ( OBOE_STATUS ) ) ;
printk ( KERN_ERR " CONFIG0 %02x%02x ENABLE %02x%02x \n " ,
INB ( OBOE_CONFIG0H ) , INB ( OBOE_CONFIG0L ) ,
INB ( OBOE_ENABLEH ) , INB ( OBOE_ENABLEL ) ) ;
printk ( KERN_ERR " NEW_PCONFIG %02x%02x CURR_PCONFIG %02x%02x \n " ,
INB ( OBOE_NEW_PCONFIGH ) , INB ( OBOE_NEW_PCONFIGL ) ,
INB ( OBOE_CURR_PCONFIGH ) , INB ( OBOE_CURR_PCONFIGL ) ) ;
printk ( KERN_ERR " MAXLEN %02x%02x RXCOUNT %02x%02x \n " ,
INB ( OBOE_MAXLENH ) , INB ( OBOE_MAXLENL ) ,
INB ( OBOE_RXCOUNTL ) , INB ( OBOE_RXCOUNTH ) ) ;
if ( self - > ring )
{
int i ;
ringbase = virt_to_bus ( self - > ring ) ;
printk ( KERN_ERR " Ring at %08x: \n " , ringbase ) ;
printk ( KERN_ERR " RX: " ) ;
for ( i = 0 ; i < RX_SLOTS ; + + i )
printk ( " (%d,%02x) " , self - > ring - > rx [ i ] . len , self - > ring - > rx [ i ] . control ) ;
printk ( " \n " ) ;
printk ( KERN_ERR " TX: " ) ;
for ( i = 0 ; i < RX_SLOTS ; + + i )
printk ( " (%d,%02x) " , self - > ring - > tx [ i ] . len , self - > ring - > tx [ i ] . control ) ;
printk ( " \n " ) ;
}
}
# endif
/*Don't let the chip look at memory */
static void
toshoboe_disablebm ( struct toshoboe_cb * self )
{
__u8 command ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
pci_read_config_byte ( self - > pdev , PCI_COMMAND , & command ) ;
command & = ~ PCI_COMMAND_MASTER ;
pci_write_config_byte ( self - > pdev , PCI_COMMAND , command ) ;
}
/* Shutdown the chip and point the taskfile reg somewhere else */
static void
toshoboe_stopchip ( struct toshoboe_cb * self )
{
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
/*Disable interrupts */
OUTB ( 0x0 , OBOE_IER ) ;
/*Disable DMA, Disable Rx, Disable Tx */
OUTB ( CONFIG0H_DMA_OFF , OBOE_CONFIG0H ) ;
/*Disable SIR MIR FIR, Tx and Rx */
OUTB ( 0x00 , OBOE_ENABLEH ) ;
/*Point the ring somewhere safe */
OUTB ( 0x3f , OBOE_RING_BASE2 ) ;
OUTB ( 0xff , OBOE_RING_BASE1 ) ;
OUTB ( 0xff , OBOE_RING_BASE0 ) ;
OUTB ( RX_LEN > > 8 , OBOE_MAXLENH ) ;
OUTB ( RX_LEN & 0xff , OBOE_MAXLENL ) ;
/*Acknoledge any pending interrupts */
OUTB ( 0xff , OBOE_ISR ) ;
/*Why */
OUTB ( OBOE_ENABLEH_PHYANDCLOCK , OBOE_ENABLEH ) ;
/*switch it off */
OUTB ( OBOE_CONFIG1_OFF , OBOE_CONFIG1 ) ;
toshoboe_disablebm ( self ) ;
}
/* Transmitter initialization */
static void
toshoboe_start_DMA ( struct toshoboe_cb * self , int opts )
{
OUTB ( 0x0 , OBOE_ENABLEH ) ;
OUTB ( CONFIG0H_DMA_ON | opts , OBOE_CONFIG0H ) ;
OUTB ( OBOE_ENABLEH_PHYANDCLOCK , OBOE_ENABLEH ) ;
PROMPT ;
}
/*Set the baud rate */
static void
toshoboe_setbaud ( struct toshoboe_cb * self )
{
__u16 pconfig = 0 ;
__u8 config0l = 0 ;
IRDA_DEBUG ( 2 , " %s(%d/%d) \n " , __FUNCTION__ , self - > speed , self - > io . speed ) ;
switch ( self - > speed )
{
case 2400 :
case 4800 :
case 9600 :
case 19200 :
case 38400 :
case 57600 :
case 115200 :
# ifdef USE_MIR
case 1152000 :
# endif
case 4000000 :
break ;
default :
printk ( KERN_ERR DRIVER_NAME " : switch to unsupported baudrate %d \n " ,
self - > speed ) ;
return ;
}
switch ( self - > speed )
{
/* For SIR the preamble is done by adding XBOFs */
/* to the packet */
/* set to filtered SIR mode, filter looks for BOF and EOF */
case 2400 :
pconfig | = 47 < < OBOE_PCONFIG_BAUDSHIFT ;
pconfig | = 25 < < OBOE_PCONFIG_WIDTHSHIFT ;
break ;
case 4800 :
pconfig | = 23 < < OBOE_PCONFIG_BAUDSHIFT ;
pconfig | = 25 < < OBOE_PCONFIG_WIDTHSHIFT ;
break ;
case 9600 :
pconfig | = 11 < < OBOE_PCONFIG_BAUDSHIFT ;
pconfig | = 25 < < OBOE_PCONFIG_WIDTHSHIFT ;
break ;
case 19200 :
pconfig | = 5 < < OBOE_PCONFIG_BAUDSHIFT ;
pconfig | = 25 < < OBOE_PCONFIG_WIDTHSHIFT ;
break ;
case 38400 :
pconfig | = 2 < < OBOE_PCONFIG_BAUDSHIFT ;
pconfig | = 25 < < OBOE_PCONFIG_WIDTHSHIFT ;
break ;
case 57600 :
pconfig | = 1 < < OBOE_PCONFIG_BAUDSHIFT ;
pconfig | = 25 < < OBOE_PCONFIG_WIDTHSHIFT ;
break ;
case 115200 :
pconfig | = 0 < < OBOE_PCONFIG_BAUDSHIFT ;
pconfig | = 25 < < OBOE_PCONFIG_WIDTHSHIFT ;
break ;
default :
/*Set to packet based reception */
OUTB ( RX_LEN > > 8 , OBOE_MAXLENH ) ;
OUTB ( RX_LEN & 0xff , OBOE_MAXLENL ) ;
break ;
}
switch ( self - > speed )
{
case 2400 :
case 4800 :
case 9600 :
case 19200 :
case 38400 :
case 57600 :
case 115200 :
config0l = OBOE_CONFIG0L_ENSIR ;
if ( self - > async )
{
/*Set to character based reception */
/*System will lock if MAXLEN=0 */
/*so have to be careful */
OUTB ( 0x01 , OBOE_MAXLENH ) ;
OUTB ( 0x01 , OBOE_MAXLENL ) ;
OUTB ( 0x00 , OBOE_MAXLENH ) ;
}
else
{
/*Set to packet based reception */
config0l | = OBOE_CONFIG0L_ENSIRF ;
OUTB ( RX_LEN > > 8 , OBOE_MAXLENH ) ;
OUTB ( RX_LEN & 0xff , OBOE_MAXLENL ) ;
}
break ;
# ifdef USE_MIR
/* MIR mode */
/* Set for 16 bit CRC and enable MIR */
/* Preamble now handled by the chip */
case 1152000 :
pconfig | = 0 < < OBOE_PCONFIG_BAUDSHIFT ;
pconfig | = 8 < < OBOE_PCONFIG_WIDTHSHIFT ;
pconfig | = 1 < < OBOE_PCONFIG_PREAMBLESHIFT ;
config0l = OBOE_CONFIG0L_CRC16 | OBOE_CONFIG0L_ENMIR ;
break ;
# endif
/* FIR mode */
/* Set for 32 bit CRC and enable FIR */
/* Preamble handled by the chip */
case 4000000 :
pconfig | = 0 < < OBOE_PCONFIG_BAUDSHIFT ;
/* Documentation says 14, but toshiba use 15 in their drivers */
pconfig | = 15 < < OBOE_PCONFIG_PREAMBLESHIFT ;
config0l = OBOE_CONFIG0L_ENFIR ;
break ;
}
/* Copy into new PHY config buffer */
OUTBP ( pconfig > > 8 , OBOE_NEW_PCONFIGH ) ;
OUTB ( pconfig & 0xff , OBOE_NEW_PCONFIGL ) ;
OUTB ( config0l , OBOE_CONFIG0L ) ;
/* Now make OBOE copy from new PHY to current PHY */
OUTB ( 0x0 , OBOE_ENABLEH ) ;
OUTB ( OBOE_ENABLEH_PHYANDCLOCK , OBOE_ENABLEH ) ;
PROMPT ;
/* speed change executed */
self - > new_speed = 0 ;
self - > io . speed = self - > speed ;
}
/*Let the chip look at memory */
static void
toshoboe_enablebm ( struct toshoboe_cb * self )
{
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
pci_set_master ( self - > pdev ) ;
}
/*setup the ring */
static void
toshoboe_initring ( struct toshoboe_cb * self )
{
int i ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
for ( i = 0 ; i < TX_SLOTS ; + + i )
{
self - > ring - > tx [ i ] . len = 0 ;
self - > ring - > tx [ i ] . control = 0x00 ;
self - > ring - > tx [ i ] . address = virt_to_bus ( self - > tx_bufs [ i ] ) ;
}
for ( i = 0 ; i < RX_SLOTS ; + + i )
{
self - > ring - > rx [ i ] . len = RX_LEN ;
self - > ring - > rx [ i ] . len = 0 ;
self - > ring - > rx [ i ] . address = virt_to_bus ( self - > rx_bufs [ i ] ) ;
self - > ring - > rx [ i ] . control = OBOE_CTL_RX_HW_OWNS ;
}
}
static void
toshoboe_resetptrs ( struct toshoboe_cb * self )
{
/* Can reset pointers by twidling DMA */
OUTB ( 0x0 , OBOE_ENABLEH ) ;
OUTBP ( CONFIG0H_DMA_OFF , OBOE_CONFIG0H ) ;
OUTB ( OBOE_ENABLEH_PHYANDCLOCK , OBOE_ENABLEH ) ;
self - > rxs = inb_p ( OBOE_RXSLOT ) & OBOE_SLOT_MASK ;
self - > txs = inb_p ( OBOE_TXSLOT ) & OBOE_SLOT_MASK ;
}
/* Called in locked state */
static void
toshoboe_initptrs ( struct toshoboe_cb * self )
{
/* spin_lock_irqsave(self->spinlock, flags); */
/* save_flags (flags); */
/* Can reset pointers by twidling DMA */
toshoboe_resetptrs ( self ) ;
OUTB ( 0x0 , OBOE_ENABLEH ) ;
OUTB ( CONFIG0H_DMA_ON , OBOE_CONFIG0H ) ;
OUTB ( OBOE_ENABLEH_PHYANDCLOCK , OBOE_ENABLEH ) ;
self - > txpending = 0 ;
/* spin_unlock_irqrestore(self->spinlock, flags); */
/* restore_flags (flags); */
}
/* Wake the chip up and get it looking at the rings */
/* Called in locked state */
static void
toshoboe_startchip ( struct toshoboe_cb * self )
{
__u32 physaddr ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
toshoboe_initring ( self ) ;
toshoboe_enablebm ( self ) ;
OUTBP ( OBOE_CONFIG1_RESET , OBOE_CONFIG1 ) ;
OUTBP ( OBOE_CONFIG1_ON , OBOE_CONFIG1 ) ;
/* Stop the clocks */
OUTB ( 0 , OBOE_ENABLEH ) ;
/*Set size of rings */
OUTB ( RING_SIZE , OBOE_RING_SIZE ) ;
/*Acknoledge any pending interrupts */
OUTB ( 0xff , OBOE_ISR ) ;
/*Enable ints */
OUTB ( OBOE_INT_TXDONE | OBOE_INT_RXDONE |
OBOE_INT_TXUNDER | OBOE_INT_RXOVER | OBOE_INT_SIP , OBOE_IER ) ;
/*Acknoledge any pending interrupts */
OUTB ( 0xff , OBOE_ISR ) ;
/*Set the maximum packet length to 0xfff (4095) */
OUTB ( RX_LEN > > 8 , OBOE_MAXLENH ) ;
OUTB ( RX_LEN & 0xff , OBOE_MAXLENL ) ;
/*Shutdown DMA */
OUTB ( CONFIG0H_DMA_OFF , OBOE_CONFIG0H ) ;
/*Find out where the rings live */
physaddr = virt_to_bus ( self - > ring ) ;
IRDA_ASSERT ( ( physaddr & 0x3ff ) = = 0 ,
printk ( KERN_ERR DRIVER_NAME " ring not correctly aligned \n " ) ;
return ; ) ;
OUTB ( ( physaddr > > 10 ) & 0xff , OBOE_RING_BASE0 ) ;
OUTB ( ( physaddr > > 18 ) & 0xff , OBOE_RING_BASE1 ) ;
OUTB ( ( physaddr > > 26 ) & 0x3f , OBOE_RING_BASE2 ) ;
/*Enable DMA controler in byte mode and RX */
OUTB ( CONFIG0H_DMA_ON , OBOE_CONFIG0H ) ;
/* Start up the clocks */
OUTB ( OBOE_ENABLEH_PHYANDCLOCK , OBOE_ENABLEH ) ;
/*set to sensible speed */
self - > speed = 9600 ;
toshoboe_setbaud ( self ) ;
toshoboe_initptrs ( self ) ;
}
static void
toshoboe_isntstuck ( struct toshoboe_cb * self )
{
}
static void
toshoboe_checkstuck ( struct toshoboe_cb * self )
{
unsigned long flags ;
if ( 0 )
{
spin_lock_irqsave ( & self - > spinlock , flags ) ;
/* This will reset the chip completely */
printk ( KERN_ERR DRIVER_NAME " : Resetting chip \n " ) ;
toshoboe_stopchip ( self ) ;
toshoboe_startchip ( self ) ;
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
}
}
/*Generate packet of about mtt us long */
static int
toshoboe_makemttpacket ( struct toshoboe_cb * self , void * buf , int mtt )
{
int xbofs ;
xbofs = ( ( int ) ( mtt / 100 ) ) * ( int ) ( self - > speed ) ;
xbofs = xbofs / 80000 ; /*Eight bits per byte, and mtt is in us*/
xbofs + + ;
IRDA_DEBUG ( 2 , DRIVER_NAME
" : generated mtt of %d bytes for %d us at %d baud \n "
, xbofs , mtt , self - > speed ) ;
if ( xbofs > TX_LEN )
{
printk ( KERN_ERR DRIVER_NAME " : wanted %d bytes MTT but TX_LEN is %d \n " ,
xbofs , TX_LEN ) ;
xbofs = TX_LEN ;
}
/*xbofs will do for SIR, MIR and FIR,SIR mode doesn't generate a checksum anyway */
memset ( buf , XBOF , xbofs ) ;
return xbofs ;
}
static int toshoboe_invalid_dev ( int irq )
{
printk ( KERN_WARNING DRIVER_NAME " : irq %d for unknown device. \n " , irq ) ;
return 1 ;
}
# ifdef USE_PROBE
/***********************************************************************/
/* Probe code */
static void
toshoboe_dumptx ( struct toshoboe_cb * self )
{
int i ;
PROBE_DEBUG ( KERN_WARNING " TX: " ) ;
for ( i = 0 ; i < RX_SLOTS ; + + i )
PROBE_DEBUG ( " (%d,%02x) " , self - > ring - > tx [ i ] . len , self - > ring - > tx [ i ] . control ) ;
PROBE_DEBUG ( " [%d] \n " , self - > speed ) ;
}
static void
toshoboe_dumprx ( struct toshoboe_cb * self , int score )
{
int i ;
PROBE_DEBUG ( " %d \n RX: " , score ) ;
for ( i = 0 ; i < RX_SLOTS ; + + i )
PROBE_DEBUG ( " (%d,%02x) " , self - > ring - > rx [ i ] . len , self - > ring - > rx [ i ] . control ) ;
PROBE_DEBUG ( " \n " ) ;
}
static inline int
stuff_byte ( __u8 byte , __u8 * buf )
{
switch ( byte )
{
case BOF : /* FALLTHROUGH */
case EOF : /* FALLTHROUGH */
case CE :
/* Insert transparently coded */
buf [ 0 ] = CE ; /* Send link escape */
buf [ 1 ] = byte ^ IRDA_TRANS ; /* Complement bit 5 */
return 2 ;
/* break; */
default :
/* Non-special value, no transparency required */
buf [ 0 ] = byte ;
return 1 ;
/* break; */
}
}
static irqreturn_t
toshoboe_probeinterrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
struct toshoboe_cb * self = ( struct toshoboe_cb * ) dev_id ;
__u8 irqstat ;
if ( self = = NULL & & toshoboe_invalid_dev ( irq ) )
return IRQ_NONE ;
irqstat = INB ( OBOE_ISR ) ;
/* was it us */
if ( ! ( irqstat & OBOE_INT_MASK ) )
return IRQ_NONE ;
/* Ack all the interrupts */
OUTB ( irqstat , OBOE_ISR ) ;
if ( irqstat & OBOE_INT_TXDONE )
{
int txp ;
self - > int_tx + + ;
PROBE_DEBUG ( " T " ) ;
txp = INB ( OBOE_TXSLOT ) & OBOE_SLOT_MASK ;
if ( self - > ring - > tx [ txp ] . control & OBOE_CTL_TX_HW_OWNS )
{
self - > int_tx + = 100 ;
PROBE_DEBUG ( " S " ) ;
toshoboe_start_DMA ( self , OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP ) ;
}
}
if ( irqstat & OBOE_INT_RXDONE ) {
self - > int_rx + + ;
PROBE_DEBUG ( " R " ) ; }
if ( irqstat & OBOE_INT_TXUNDER ) {
self - > int_txunder + + ;
PROBE_DEBUG ( " U " ) ; }
if ( irqstat & OBOE_INT_RXOVER ) {
self - > int_rxover + + ;
PROBE_DEBUG ( " O " ) ; }
if ( irqstat & OBOE_INT_SIP ) {
self - > int_sip + + ;
PROBE_DEBUG ( " I " ) ; }
return IRQ_HANDLED ;
}
static int
toshoboe_maketestpacket ( unsigned char * buf , int badcrc , int fir )
{
int i ;
int len = 0 ;
union
{
__u16 value ;
__u8 bytes [ 2 ] ;
}
fcs ;
if ( fir )
{
memset ( buf , 0 , TT_LEN ) ;
return ( TT_LEN ) ;
}
fcs . value = INIT_FCS ;
memset ( buf , XBOF , 10 ) ;
len + = 10 ;
buf [ len + + ] = BOF ;
for ( i = 0 ; i < TT_LEN ; + + i )
{
len + = stuff_byte ( i , buf + len ) ;
fcs . value = irda_fcs ( fcs . value , i ) ;
}
len + = stuff_byte ( fcs . bytes [ 0 ] ^ badcrc , buf + len ) ;
len + = stuff_byte ( fcs . bytes [ 1 ] ^ badcrc , buf + len ) ;
buf [ len + + ] = EOF ;
len + + ;
return len ;
}
static int
toshoboe_probefail ( struct toshoboe_cb * self , char * msg )
{
printk ( KERN_ERR DRIVER_NAME " probe(%d) failed %s \n " , self - > speed , msg ) ;
toshoboe_dumpregs ( self ) ;
toshoboe_stopchip ( self ) ;
free_irq ( self - > io . irq , ( void * ) self ) ;
return 0 ;
}
static int
toshoboe_numvalidrcvs ( struct toshoboe_cb * self )
{
int i , ret = 0 ;
for ( i = 0 ; i < RX_SLOTS ; + + i )
if ( ( self - > ring - > rx [ i ] . control & 0xe0 ) = = 0 )
ret + + ;
return ret ;
}
static int
toshoboe_numrcvs ( struct toshoboe_cb * self )
{
int i , ret = 0 ;
for ( i = 0 ; i < RX_SLOTS ; + + i )
if ( ! ( self - > ring - > rx [ i ] . control & OBOE_CTL_RX_HW_OWNS ) )
ret + + ;
return ret ;
}
static int
toshoboe_probe ( struct toshoboe_cb * self )
{
int i , j , n ;
# ifdef USE_MIR
int bauds [ ] = { 9600 , 115200 , 4000000 , 1152000 } ;
# else
int bauds [ ] = { 9600 , 115200 , 4000000 } ;
# endif
unsigned long flags ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
if ( request_irq ( self - > io . irq , toshoboe_probeinterrupt ,
self - > io . irqflags , " toshoboe " , ( void * ) self ) )
{
printk ( KERN_ERR DRIVER_NAME " : probe failed to allocate irq %d \n " ,
self - > io . irq ) ;
return 0 ;
}
/* test 1: SIR filter and back to back */
for ( j = 0 ; j < ( sizeof ( bauds ) / sizeof ( int ) ) ; + + j )
{
int fir = ( j > 1 ) ;
toshoboe_stopchip ( self ) ;
spin_lock_irqsave ( & self - > spinlock , flags ) ;
/*Address is already setup */
toshoboe_startchip ( self ) ;
self - > int_rx = self - > int_tx = 0 ;
self - > speed = bauds [ j ] ;
toshoboe_setbaud ( self ) ;
toshoboe_initptrs ( self ) ;
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
self - > ring - > tx [ self - > txs ] . control =
/* (FIR only) OBOE_CTL_TX_SIP needed for switching to next slot */
/* MIR: all received data is stored in one slot */
( fir ) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
: OBOE_CTL_TX_HW_OWNS ;
self - > ring - > tx [ self - > txs ] . len =
toshoboe_maketestpacket ( self - > tx_bufs [ self - > txs ] , 0 , fir ) ;
self - > txs + + ;
self - > txs % = TX_SLOTS ;
self - > ring - > tx [ self - > txs ] . control =
( fir ) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_SIP
: OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ;
self - > ring - > tx [ self - > txs ] . len =
toshoboe_maketestpacket ( self - > tx_bufs [ self - > txs ] , 0 , fir ) ;
self - > txs + + ;
self - > txs % = TX_SLOTS ;
self - > ring - > tx [ self - > txs ] . control =
( fir ) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
: OBOE_CTL_TX_HW_OWNS ;
self - > ring - > tx [ self - > txs ] . len =
toshoboe_maketestpacket ( self - > tx_bufs [ self - > txs ] , 0 , fir ) ;
self - > txs + + ;
self - > txs % = TX_SLOTS ;
self - > ring - > tx [ self - > txs ] . control =
( fir ) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
| OBOE_CTL_TX_SIP | OBOE_CTL_TX_BAD_CRC
: OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ;
self - > ring - > tx [ self - > txs ] . len =
toshoboe_maketestpacket ( self - > tx_bufs [ self - > txs ] , 0 , fir ) ;
self - > txs + + ;
self - > txs % = TX_SLOTS ;
toshoboe_dumptx ( self ) ;
/* Turn on TX and RX and loopback */
toshoboe_start_DMA ( self , OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP ) ;
i = 0 ;
n = fir ? 1 : 4 ;
while ( toshoboe_numvalidrcvs ( self ) ! = n )
{
if ( i > 4800 )
return toshoboe_probefail ( self , " filter test " ) ;
udelay ( ( 9600 * ( TT_LEN + 16 ) ) / self - > speed ) ;
i + + ;
}
n = fir ? 203 : 102 ;
while ( ( toshoboe_numrcvs ( self ) ! = self - > int_rx ) | | ( self - > int_tx ! = n ) )
{
if ( i > 4800 )
return toshoboe_probefail ( self , " interrupt test " ) ;
udelay ( ( 9600 * ( TT_LEN + 16 ) ) / self - > speed ) ;
i + + ;
}
toshoboe_dumprx ( self , i ) ;
}
/* test 2: SIR in char at a time */
toshoboe_stopchip ( self ) ;
self - > int_rx = self - > int_tx = 0 ;
spin_lock_irqsave ( & self - > spinlock , flags ) ;
toshoboe_startchip ( self ) ;
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
self - > async = 1 ;
self - > speed = 115200 ;
toshoboe_setbaud ( self ) ;
self - > ring - > tx [ self - > txs ] . control =
OBOE_CTL_TX_RTCENTX | OBOE_CTL_TX_HW_OWNS ;
self - > ring - > tx [ self - > txs ] . len = 4 ;
( ( unsigned char * ) self - > tx_bufs [ self - > txs ] ) [ 0 ] = ' f ' ;
( ( unsigned char * ) self - > tx_bufs [ self - > txs ] ) [ 1 ] = ' i ' ;
( ( unsigned char * ) self - > tx_bufs [ self - > txs ] ) [ 2 ] = ' s ' ;
( ( unsigned char * ) self - > tx_bufs [ self - > txs ] ) [ 3 ] = ' h ' ;
toshoboe_dumptx ( self ) ;
toshoboe_start_DMA ( self , OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP ) ;
i = 0 ;
while ( toshoboe_numvalidrcvs ( self ) ! = 4 )
{
if ( i > 100 )
return toshoboe_probefail ( self , " Async test " ) ;
udelay ( 100 ) ;
i + + ;
}
while ( ( toshoboe_numrcvs ( self ) ! = self - > int_rx ) | | ( self - > int_tx ! = 1 ) )
{
if ( i > 100 )
return toshoboe_probefail ( self , " Async interrupt test " ) ;
udelay ( 100 ) ;
i + + ;
}
toshoboe_dumprx ( self , i ) ;
self - > async = 0 ;
self - > speed = 9600 ;
toshoboe_setbaud ( self ) ;
toshoboe_stopchip ( self ) ;
free_irq ( self - > io . irq , ( void * ) self ) ;
printk ( KERN_WARNING DRIVER_NAME " : Self test passed ok \n " ) ;
return 1 ;
}
# endif
/******************************************************************/
/* Netdev style code */
/* Transmit something */
static int
toshoboe_hard_xmit ( struct sk_buff * skb , struct net_device * dev )
{
struct toshoboe_cb * self ;
__s32 speed ;
int mtt , len , ctl ;
unsigned long flags ;
struct irda_skb_cb * cb = ( struct irda_skb_cb * ) skb - > cb ;
self = ( struct toshoboe_cb * ) dev - > priv ;
IRDA_ASSERT ( self ! = NULL , return 0 ; ) ;
IRDA_DEBUG ( 1 , " %s.tx:%x(%x)%x \n " , __FUNCTION__
, skb - > len , self - > txpending , INB ( OBOE_ENABLEH ) ) ;
if ( ! cb - > magic ) {
IRDA_DEBUG ( 2 , " %s.Not IrLAP:%x \n " , __FUNCTION__ , cb - > magic ) ;
# ifdef DUMP_PACKETS
_dumpbufs ( skb - > data , skb - > len , ' > ' ) ;
# endif
}
/* change speed pending, wait for its execution */
if ( self - > new_speed )
return - EBUSY ;
/* device stopped (apm) wait for restart */
if ( self - > stopped )
return - EBUSY ;
toshoboe_checkstuck ( self ) ;
dev - > trans_start = jiffies ;
/* Check if we need to change the speed */
/* But not now. Wait after transmission if mtt not required */
speed = irda_get_next_speed ( skb ) ;
if ( ( speed ! = self - > io . speed ) & & ( speed ! = - 1 ) )
{
spin_lock_irqsave ( & self - > spinlock , flags ) ;
if ( self - > txpending | | skb - > len )
{
self - > new_speed = speed ;
IRDA_DEBUG ( 1 , " %s: Queued TxDone scheduled speed change %d \n " ,
__FUNCTION__ , speed ) ;
/* if no data, that's all! */
if ( ! skb - > len )
{
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
dev_kfree_skb ( skb ) ;
return 0 ;
}
/* True packet, go on, but */
/* do not accept anything before change speed execution */
netif_stop_queue ( dev ) ;
/* ready to process TxDone interrupt */
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
}
else
{
/* idle and no data, change speed now */
self - > speed = speed ;
toshoboe_setbaud ( self ) ;
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
dev_kfree_skb ( skb ) ;
return 0 ;
}
}
if ( ( mtt = irda_get_mtt ( skb ) ) )
{
/* This is fair since the queue should be empty anyway */
spin_lock_irqsave ( & self - > spinlock , flags ) ;
if ( self - > txpending )
{
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
return - EBUSY ;
}
/* If in SIR mode we need to generate a string of XBOFs */
/* In MIR and FIR we need to generate a string of data */
/* which we will add a wrong checksum to */
mtt = toshoboe_makemttpacket ( self , self - > tx_bufs [ self - > txs ] , mtt ) ;
IRDA_DEBUG ( 1 , " %s.mtt:%x(%x)%d \n " , __FUNCTION__
, skb - > len , mtt , self - > txpending ) ;
if ( mtt )
{
self - > ring - > tx [ self - > txs ] . len = mtt & 0xfff ;
ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ;
if ( INB ( OBOE_ENABLEH ) & OBOE_ENABLEH_FIRON )
{
ctl | = OBOE_CTL_TX_BAD_CRC | OBOE_CTL_TX_SIP ;
}
# ifdef USE_MIR
else if ( INB ( OBOE_ENABLEH ) & OBOE_ENABLEH_MIRON )
{
ctl | = OBOE_CTL_TX_BAD_CRC ;
}
# endif
self - > ring - > tx [ self - > txs ] . control = ctl ;
OUTB ( 0x0 , OBOE_ENABLEH ) ;
/* It is only a timer. Do not send mtt packet outside! */
toshoboe_start_DMA ( self , OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP ) ;
self - > txpending + + ;
self - > txs + + ;
self - > txs % = TX_SLOTS ;
}
else
{
printk ( KERN_ERR DRIVER_NAME " : problem with mtt packet - ignored \n " ) ;
}
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
}
# ifdef DUMP_PACKETS
dumpbufs ( skb - > data , skb - > len , ' > ' ) ;
# endif
spin_lock_irqsave ( & self - > spinlock , flags ) ;
if ( self - > ring - > tx [ self - > txs ] . control & OBOE_CTL_TX_HW_OWNS )
{
IRDA_DEBUG ( 0 , " %s.ful:%x(%x)%x \n " , __FUNCTION__
, skb - > len , self - > ring - > tx [ self - > txs ] . control , self - > txpending ) ;
toshoboe_start_DMA ( self , OBOE_CONFIG0H_ENTX ) ;
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
return - EBUSY ;
}
if ( INB ( OBOE_ENABLEH ) & OBOE_ENABLEH_SIRON )
{
len = async_wrap_skb ( skb , self - > tx_bufs [ self - > txs ] , TX_BUF_SZ ) ;
}
else
{
len = skb - > len ;
memcpy ( self - > tx_bufs [ self - > txs ] , skb - > data , len ) ;
}
self - > ring - > tx [ self - > txs ] . len = len & 0x0fff ;
/*Sometimes the HW doesn't see us assert RTCENTX in the interrupt code */
/*later this plays safe, we garuntee the last packet to be transmitted */
/*has RTCENTX set */
ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ;
if ( INB ( OBOE_ENABLEH ) & OBOE_ENABLEH_FIRON )
{
ctl | = OBOE_CTL_TX_SIP ;
}
self - > ring - > tx [ self - > txs ] . control = ctl ;
/* If transmitter is idle start in one-shot mode */
if ( ! self - > txpending )
toshoboe_start_DMA ( self , OBOE_CONFIG0H_ENTX ) ;
self - > txpending + + ;
self - > txs + + ;
self - > txs % = TX_SLOTS ;
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
dev_kfree_skb ( skb ) ;
return 0 ;
}
/*interrupt handler */
static irqreturn_t
toshoboe_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
{
struct toshoboe_cb * self = ( struct toshoboe_cb * ) dev_id ;
__u8 irqstat ;
struct sk_buff * skb = NULL ;
if ( self = = NULL & & toshoboe_invalid_dev ( irq ) )
return IRQ_NONE ;
irqstat = INB ( OBOE_ISR ) ;
/* was it us */
if ( ! ( irqstat & OBOE_INT_MASK ) )
return IRQ_NONE ;
/* Ack all the interrupts */
OUTB ( irqstat , OBOE_ISR ) ;
toshoboe_isntstuck ( self ) ;
/* Txdone */
if ( irqstat & OBOE_INT_TXDONE )
{
int txp , txpc ;
int i ;
txp = self - > txpending ;
self - > txpending = 0 ;
for ( i = 0 ; i < TX_SLOTS ; + + i )
{
if ( self - > ring - > tx [ i ] . control & OBOE_CTL_TX_HW_OWNS )
self - > txpending + + ;
}
IRDA_DEBUG ( 1 , " %s.txd(%x)%x/%x \n " , __FUNCTION__
, irqstat , txp , self - > txpending ) ;
txp = INB ( OBOE_TXSLOT ) & OBOE_SLOT_MASK ;
/* Got anything queued ? start it together */
if ( self - > ring - > tx [ txp ] . control & OBOE_CTL_TX_HW_OWNS )
{
txpc = txp ;
# ifdef OPTIMIZE_TX
while ( self - > ring - > tx [ txpc ] . control & OBOE_CTL_TX_HW_OWNS )
{
txp = txpc ;
txpc + + ;
txpc % = TX_SLOTS ;
self - > stats . tx_packets + + ;
if ( self - > ring - > tx [ txpc ] . control & OBOE_CTL_TX_HW_OWNS )
self - > ring - > tx [ txp ] . control & = ~ OBOE_CTL_TX_RTCENTX ;
}
self - > stats . tx_packets - - ;
# else
self - > stats . tx_packets + + ;
# endif
toshoboe_start_DMA ( self , OBOE_CONFIG0H_ENTX ) ;
}
if ( ( ! self - > txpending ) & & ( self - > new_speed ) )
{
self - > speed = self - > new_speed ;
IRDA_DEBUG ( 1 , " %s: Executed TxDone scheduled speed change %d \n " ,
__FUNCTION__ , self - > speed ) ;
toshoboe_setbaud ( self ) ;
}
/* Tell network layer that we want more frames */
if ( ! self - > new_speed )
netif_wake_queue ( self - > netdev ) ;
}
if ( irqstat & OBOE_INT_RXDONE )
{
while ( ! ( self - > ring - > rx [ self - > rxs ] . control & OBOE_CTL_RX_HW_OWNS ) )
{
int len = self - > ring - > rx [ self - > rxs ] . len ;
skb = NULL ;
IRDA_DEBUG ( 3 , " %s.rcv:%x(%x) \n " , __FUNCTION__
, len , self - > ring - > rx [ self - > rxs ] . control ) ;
# ifdef DUMP_PACKETS
dumpbufs ( self - > rx_bufs [ self - > rxs ] , len , ' < ' ) ;
# endif
if ( self - > ring - > rx [ self - > rxs ] . control = = 0 )
{
__u8 enable = INB ( OBOE_ENABLEH ) ;
/* In SIR mode we need to check the CRC as this */
/* hasn't been done by the hardware */
if ( enable & OBOE_ENABLEH_SIRON )
{
if ( ! toshoboe_checkfcs ( self - > rx_bufs [ self - > rxs ] , len ) )
len = 0 ;
/*Trim off the CRC */
if ( len > 1 )
len - = 2 ;
else
len = 0 ;
IRDA_DEBUG ( 1 , " %s.SIR:%x(%x) \n " , __FUNCTION__ , len , enable ) ;
}
# ifdef USE_MIR
else if ( enable & OBOE_ENABLEH_MIRON )
{
if ( len > 1 )
len - = 2 ;
else
len = 0 ;
IRDA_DEBUG ( 2 , " %s.MIR:%x(%x) \n " , __FUNCTION__ , len , enable ) ;
}
# endif
else if ( enable & OBOE_ENABLEH_FIRON )
{
if ( len > 3 )
len - = 4 ; /*FIXME: check this */
else
len = 0 ;
IRDA_DEBUG ( 1 , " %s.FIR:%x(%x) \n " , __FUNCTION__ , len , enable ) ;
}
else
IRDA_DEBUG ( 0 , " %s.?IR:%x(%x) \n " , __FUNCTION__ , len , enable ) ;
if ( len )
{
skb = dev_alloc_skb ( len + 1 ) ;
if ( skb )
{
skb_reserve ( skb , 1 ) ;
skb_put ( skb , len ) ;
memcpy ( skb - > data , self - > rx_bufs [ self - > rxs ] , len ) ;
self - > stats . rx_packets + + ;
skb - > dev = self - > netdev ;
skb - > mac . raw = skb - > data ;
skb - > protocol = htons ( ETH_P_IRDA ) ;
}
else
{
printk ( KERN_INFO
" %s(), memory squeeze, dropping frame. \n " ,
__FUNCTION__ ) ;
}
}
}
else
{
/* TODO: =========================================== */
/* if OBOE_CTL_RX_LENGTH, our buffers are too small */
/* (MIR or FIR) data is lost. */
/* (SIR) data is splitted in several slots. */
/* we have to join all the received buffers received */
/*in a large buffer before checking CRC. */
IRDA_DEBUG ( 0 , " %s.err:%x(%x) \n " , __FUNCTION__
, len , self - > ring - > rx [ self - > rxs ] . control ) ;
}
self - > ring - > rx [ self - > rxs ] . len = 0x0 ;
self - > ring - > rx [ self - > rxs ] . control = OBOE_CTL_RX_HW_OWNS ;
self - > rxs + + ;
self - > rxs % = RX_SLOTS ;
if ( skb )
netif_rx ( skb ) ;
}
}
if ( irqstat & OBOE_INT_TXUNDER )
{
printk ( KERN_WARNING DRIVER_NAME " : tx fifo underflow \n " ) ;
}
if ( irqstat & OBOE_INT_RXOVER )
{
printk ( KERN_WARNING DRIVER_NAME " : rx fifo overflow \n " ) ;
}
/* This must be useful for something... */
if ( irqstat & OBOE_INT_SIP )
{
self - > int_sip + + ;
IRDA_DEBUG ( 1 , " %s.sip:%x(%x)%x \n " , __FUNCTION__
, self - > int_sip , irqstat , self - > txpending ) ;
}
return IRQ_HANDLED ;
}
static int
toshoboe_net_open ( struct net_device * dev )
{
struct toshoboe_cb * self ;
unsigned long flags ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
IRDA_ASSERT ( dev ! = NULL , return - 1 ; ) ;
self = ( struct toshoboe_cb * ) dev - > priv ;
IRDA_ASSERT ( self ! = NULL , return 0 ; ) ;
if ( self - > async )
return - EBUSY ;
if ( self - > stopped )
return 0 ;
if ( request_irq ( self - > io . irq , toshoboe_interrupt ,
SA_SHIRQ | SA_INTERRUPT , dev - > name , ( void * ) self ) )
{
return - EAGAIN ;
}
spin_lock_irqsave ( & self - > spinlock , flags ) ;
toshoboe_startchip ( self ) ;
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
/* Ready to play! */
netif_start_queue ( dev ) ;
/*
* Open new IrLAP layer instance , now that everything should be
* initialized properly
*/
self - > irlap = irlap_open ( dev , & self - > qos , driver_name ) ;
self - > irdad = 1 ;
return 0 ;
}
static int
toshoboe_net_close ( struct net_device * dev )
{
struct toshoboe_cb * self ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
IRDA_ASSERT ( dev ! = NULL , return - 1 ; ) ;
self = ( struct toshoboe_cb * ) dev - > priv ;
/* Stop device */
netif_stop_queue ( dev ) ;
/* Stop and remove instance of IrLAP */
if ( self - > irlap )
irlap_close ( self - > irlap ) ;
self - > irlap = NULL ;
self - > irdad = 0 ;
free_irq ( self - > io . irq , ( void * ) self ) ;
if ( ! self - > stopped )
{
toshoboe_stopchip ( self ) ;
}
return 0 ;
}
/*
* Function toshoboe_net_ioctl ( dev , rq , cmd )
*
* Process IOCTL commands for this device
*
*/
static int
toshoboe_net_ioctl ( struct net_device * dev , struct ifreq * rq , int cmd )
{
struct if_irda_req * irq = ( struct if_irda_req * ) rq ;
struct toshoboe_cb * self ;
unsigned long flags ;
int ret = 0 ;
IRDA_ASSERT ( dev ! = NULL , return - 1 ; ) ;
self = dev - > priv ;
IRDA_ASSERT ( self ! = NULL , return - 1 ; ) ;
IRDA_DEBUG ( 5 , " %s(), %s, (cmd=0x%X) \n " , __FUNCTION__ , dev - > name , cmd ) ;
/* Disable interrupts & save flags */
spin_lock_irqsave ( & self - > spinlock , flags ) ;
switch ( cmd )
{
case SIOCSBANDWIDTH : /* Set bandwidth */
/* This function will also be used by IrLAP to change the
* speed , so we still must allow for speed change within
* interrupt context .
*/
IRDA_DEBUG ( 1 , " %s(BANDWIDTH), %s, (%X/%ld \n " , __FUNCTION__
, dev - > name , INB ( OBOE_STATUS ) , irq - > ifr_baudrate ) ;
2005-11-08 20:41:13 +03:00
if ( ! in_interrupt ( ) & & ! capable ( CAP_NET_ADMIN ) ) {
ret = - EPERM ;
goto out ;
}
2005-04-17 02:20:36 +04:00
/* self->speed=irq->ifr_baudrate; */
/* toshoboe_setbaud(self); */
/* Just change speed once - inserted by Paul Bristow */
self - > new_speed = irq - > ifr_baudrate ;
break ;
case SIOCSMEDIABUSY : /* Set media busy */
IRDA_DEBUG ( 1 , " %s(MEDIABUSY), %s, (%X/%x) \n " , __FUNCTION__
, dev - > name , INB ( OBOE_STATUS ) , capable ( CAP_NET_ADMIN ) ) ;
2005-11-08 20:41:13 +03:00
if ( ! capable ( CAP_NET_ADMIN ) ) {
ret = - EPERM ;
goto out ;
}
2005-04-17 02:20:36 +04:00
irda_device_set_media_busy ( self - > netdev , TRUE ) ;
break ;
case SIOCGRECEIVING : /* Check if we are receiving right now */
irq - > ifr_receiving = ( INB ( OBOE_STATUS ) & OBOE_STATUS_RXBUSY ) ? 1 : 0 ;
IRDA_DEBUG ( 3 , " %s(RECEIVING), %s, (%X/%x) \n " , __FUNCTION__
, dev - > name , INB ( OBOE_STATUS ) , irq - > ifr_receiving ) ;
break ;
default :
IRDA_DEBUG ( 1 , " %s(?), %s, (cmd=0x%X) \n " , __FUNCTION__ , dev - > name , cmd ) ;
ret = - EOPNOTSUPP ;
}
2005-11-08 20:41:13 +03:00
out :
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
return ret ;
}
MODULE_DESCRIPTION ( " Toshiba OBOE IrDA Device Driver " ) ;
MODULE_AUTHOR ( " James McKenzie <james@fishsoup.dhs.org> " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( max_baud , int , 0 ) ;
MODULE_PARM_DESC ( max_baud , " Maximum baud rate " ) ;
# ifdef USE_PROBE
module_param ( do_probe , bool , 0 ) ;
MODULE_PARM_DESC ( do_probe , " Enable/disable chip probing and self-test " ) ;
# endif
static void
toshoboe_close ( struct pci_dev * pci_dev )
{
int i ;
struct toshoboe_cb * self = ( struct toshoboe_cb * ) pci_get_drvdata ( pci_dev ) ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
IRDA_ASSERT ( self ! = NULL , return ; ) ;
if ( ! self - > stopped )
{
toshoboe_stopchip ( self ) ;
}
release_region ( self - > io . fir_base , self - > io . fir_ext ) ;
for ( i = 0 ; i < TX_SLOTS ; + + i )
{
kfree ( self - > tx_bufs [ i ] ) ;
self - > tx_bufs [ i ] = NULL ;
}
for ( i = 0 ; i < RX_SLOTS ; + + i )
{
kfree ( self - > rx_bufs [ i ] ) ;
self - > rx_bufs [ i ] = NULL ;
}
unregister_netdev ( self - > netdev ) ;
kfree ( self - > ringbuf ) ;
self - > ringbuf = NULL ;
self - > ring = NULL ;
free_netdev ( self - > netdev ) ;
}
static int
toshoboe_open ( struct pci_dev * pci_dev , const struct pci_device_id * pdid )
{
struct toshoboe_cb * self ;
struct net_device * dev ;
int i = 0 ;
int ok = 0 ;
int err ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
if ( ( err = pci_enable_device ( pci_dev ) ) )
return err ;
dev = alloc_irdadev ( sizeof ( struct toshoboe_cb ) ) ;
if ( dev = = NULL )
{
printk ( KERN_ERR DRIVER_NAME " : can't allocate memory for "
" IrDA control block \n " ) ;
return - ENOMEM ;
}
self = dev - > priv ;
self - > netdev = dev ;
self - > pdev = pci_dev ;
self - > base = pci_resource_start ( pci_dev , 0 ) ;
self - > io . fir_base = self - > base ;
self - > io . fir_ext = OBOE_IO_EXTENT ;
self - > io . irq = pci_dev - > irq ;
self - > io . irqflags = SA_SHIRQ | SA_INTERRUPT ;
self - > speed = self - > io . speed = 9600 ;
self - > async = 0 ;
/* Lock the port that we need */
if ( NULL = = request_region ( self - > io . fir_base , self - > io . fir_ext , driver_name ) )
{
printk ( KERN_ERR DRIVER_NAME " : can't get iobase of 0x%03x \n "
, self - > io . fir_base ) ;
err = - EBUSY ;
goto freeself ;
}
spin_lock_init ( & self - > spinlock ) ;
irda_init_max_qos_capabilies ( & self - > qos ) ;
self - > qos . baud_rate . bits = 0 ;
if ( max_baud > = 2400 )
self - > qos . baud_rate . bits | = IR_2400 ;
/*if (max_baud>=4800) idev->qos.baud_rate.bits|=IR_4800; */
if ( max_baud > = 9600 )
self - > qos . baud_rate . bits | = IR_9600 ;
if ( max_baud > = 19200 )
self - > qos . baud_rate . bits | = IR_19200 ;
if ( max_baud > = 115200 )
self - > qos . baud_rate . bits | = IR_115200 ;
# ifdef USE_MIR
if ( max_baud > = 1152000 )
{
self - > qos . baud_rate . bits | = IR_1152000 ;
}
# endif
if ( max_baud > = 4000000 )
{
self - > qos . baud_rate . bits | = ( IR_4000000 < < 8 ) ;
}
/*FIXME: work this out... */
self - > qos . min_turn_time . bits = 0xff ;
irda_qos_bits_to_value ( & self - > qos ) ;
/* Allocate twice the size to guarantee alignment */
self - > ringbuf = ( void * ) kmalloc ( OBOE_RING_LEN < < 1 , GFP_KERNEL ) ;
if ( ! self - > ringbuf )
{
printk ( KERN_ERR DRIVER_NAME " : can't allocate DMA buffers \n " ) ;
err = - ENOMEM ;
goto freeregion ;
}
# if (BITS_PER_LONG == 64)
# error broken on 64-bit: casts pointer to 32-bit, and then back to pointer.
# endif
/*We need to align the taskfile on a taskfile size boundary */
{
unsigned long addr ;
addr = ( __u32 ) self - > ringbuf ;
addr & = ~ ( OBOE_RING_LEN - 1 ) ;
addr + = OBOE_RING_LEN ;
self - > ring = ( struct OboeRing * ) addr ;
}
memset ( self - > ring , 0 , OBOE_RING_LEN ) ;
self - > io . mem_base = ( __u32 ) self - > ring ;
ok = 1 ;
for ( i = 0 ; i < TX_SLOTS ; + + i )
{
self - > tx_bufs [ i ] = kmalloc ( TX_BUF_SZ , GFP_KERNEL ) ;
if ( ! self - > tx_bufs [ i ] )
ok = 0 ;
}
for ( i = 0 ; i < RX_SLOTS ; + + i )
{
self - > rx_bufs [ i ] = kmalloc ( RX_BUF_SZ , GFP_KERNEL ) ;
if ( ! self - > rx_bufs [ i ] )
ok = 0 ;
}
if ( ! ok )
{
printk ( KERN_ERR DRIVER_NAME " : can't allocate rx/tx buffers \n " ) ;
err = - ENOMEM ;
goto freebufs ;
}
# ifdef USE_PROBE
if ( do_probe )
if ( ! toshoboe_probe ( self ) )
{
err = - ENODEV ;
goto freebufs ;
}
# endif
SET_MODULE_OWNER ( dev ) ;
SET_NETDEV_DEV ( dev , & pci_dev - > dev ) ;
dev - > hard_start_xmit = toshoboe_hard_xmit ;
dev - > open = toshoboe_net_open ;
dev - > stop = toshoboe_net_close ;
dev - > do_ioctl = toshoboe_net_ioctl ;
err = register_netdev ( dev ) ;
if ( err )
{
printk ( KERN_ERR DRIVER_NAME " : register_netdev() failed \n " ) ;
err = - ENOMEM ;
goto freebufs ;
}
printk ( KERN_INFO " IrDA: Registered device %s \n " , dev - > name ) ;
pci_set_drvdata ( pci_dev , self ) ;
printk ( KERN_INFO DRIVER_NAME " : Using multiple tasks, version %s \n " , rcsid ) ;
return 0 ;
freebufs :
for ( i = 0 ; i < TX_SLOTS ; + + i )
2005-10-29 00:53:13 +04:00
kfree ( self - > tx_bufs [ i ] ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < RX_SLOTS ; + + i )
2005-10-29 00:53:13 +04:00
kfree ( self - > rx_bufs [ i ] ) ;
2005-04-17 02:20:36 +04:00
kfree ( self - > ringbuf ) ;
freeregion :
release_region ( self - > io . fir_base , self - > io . fir_ext ) ;
freeself :
free_netdev ( dev ) ;
return err ;
}
static int
toshoboe_gotosleep ( struct pci_dev * pci_dev , pm_message_t crap )
{
struct toshoboe_cb * self = ( struct toshoboe_cb * ) pci_get_drvdata ( pci_dev ) ;
unsigned long flags ;
int i = 10 ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
if ( ! self | | self - > stopped )
return 0 ;
if ( ( ! self - > irdad ) & & ( ! self - > async ) )
return 0 ;
/* Flush all packets */
while ( ( i - - ) & & ( self - > txpending ) )
udelay ( 10000 ) ;
spin_lock_irqsave ( & self - > spinlock , flags ) ;
toshoboe_stopchip ( self ) ;
self - > stopped = 1 ;
self - > txpending = 0 ;
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
return 0 ;
}
static int
toshoboe_wakeup ( struct pci_dev * pci_dev )
{
struct toshoboe_cb * self = ( struct toshoboe_cb * ) pci_get_drvdata ( pci_dev ) ;
unsigned long flags ;
IRDA_DEBUG ( 4 , " %s() \n " , __FUNCTION__ ) ;
if ( ! self | | ! self - > stopped )
return 0 ;
if ( ( ! self - > irdad ) & & ( ! self - > async ) )
return 0 ;
spin_lock_irqsave ( & self - > spinlock , flags ) ;
toshoboe_startchip ( self ) ;
self - > stopped = 0 ;
netif_wake_queue ( self - > netdev ) ;
spin_unlock_irqrestore ( & self - > spinlock , flags ) ;
return 0 ;
}
static struct pci_driver donauboe_pci_driver = {
. name = " donauboe " ,
. id_table = toshoboe_pci_tbl ,
. probe = toshoboe_open ,
. remove = toshoboe_close ,
. suspend = toshoboe_gotosleep ,
. resume = toshoboe_wakeup
} ;
static int __init
donauboe_init ( void )
{
2006-03-21 06:00:27 +03:00
return pci_register_driver ( & donauboe_pci_driver ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit
donauboe_cleanup ( void )
{
pci_unregister_driver ( & donauboe_pci_driver ) ;
}
module_init ( donauboe_init ) ;
module_exit ( donauboe_cleanup ) ;