2005-04-17 02:20:36 +04:00
/* bionet.c BioNet-100 device driver for linux68k.
*
* Version : @ ( # ) bionet . c 1.0 02 / 06 / 96
*
* Author : Hartmut Laue < laue @ ifk - mp . uni - kiel . de >
* and Torsten Narjes < narjes @ ifk - mp . uni - kiel . de >
*
* Little adaptions for integration into pl7 by Roman Hodek
*
* Some changes in bionet_poll_rx by Karl - Heinz Lohner
*
What is it ?
- - - - - - - - - - - -
This driver controls the BIONET - 100 LAN - Adapter which connects
an ATARI ST / TT via the ACSI - port to an Ethernet - based network .
This version can be compiled as a loadable module ( See the
compile command at the bottom of this file ) .
At load time , you can optionally set the debugging level and the
fastest response time on the command line of ' insmod ' .
' bionet_debug '
controls the amount of diagnostic messages :
0 : no messages
> 0 : see code for meaning of printed messages
' bionet_min_poll_time ' ( always > = 1 )
gives the time ( in jiffies ) between polls . Low values
increase the system load ( beware ! )
When loaded , a net device with the name ' bio0 ' becomes available ,
which can be controlled with the usual ' ifconfig ' command .
It is possible to compile this driver into the kernel like other
( net ) drivers . For this purpose , some source files ( e . g . config - files
makefiles , Space . c ) must be changed accordingly . ( You may refer to
other drivers how to do it . ) In this case , the device will be detected
at boot time and ( probably ) appear as ' eth0 ' .
This code is based on several sources :
- The driver code for a parallel port ethernet adapter by
Donald Becker ( see file ' atp . c ' from the PC linux distribution )
- The ACSI code by Roman Hodek for the ATARI - ACSI harddisk support
and DMA handling .
- Very limited information about moving packets in and out of the
BIONET - adapter from the TCP package for TOS by BioData GmbH .
Theory of Operation
- - - - - - - - - - - - - - - - - - -
Because the ATARI DMA port is usually shared between several
devices ( eg . harddisk , floppy ) we cannot block the ACSI bus
while waiting for interrupts . Therefore we use a polling mechanism
to fetch packets from the adapter . For the same reason , we send
packets without checking that the previous packet has been sent to
the LAN . We rely on the higher levels of the networking code to detect
missing packets and resend them .
Before we access the ATARI DMA controller , we check if another
process is using the DMA . If not , we lock the DMA , perform one or
more packet transfers and unlock the DMA before returning .
We do not use ' stdma_lock ' unconditionally because it is unclear
if the networking code can be set to sleep , which will happen if
another ( possibly slow ) device is using the DMA controller .
The polling is done via timer interrupts which periodically
' simulate ' an interrupt from the Ethernet adapter . The time ( in jiffies )
between polls varies depending on an estimate of the net activity .
The allowed range is given by the variable ' bionet_min_poll_time '
for the lower ( fastest ) limit and the constant ' MAX_POLL_TIME '
for the higher ( slowest ) limit .
Whenever a packet arrives , we switch to fastest response by setting
the polling time to its lowest limit . If the following poll fails ,
because no packets have arrived , we increase the time for the next
poll . When the net activity is low , the polling time effectively
stays at its maximum value , resulting in the lowest load for the
machine .
*/
# define MAX_POLL_TIME 10
static char version [ ] =
" bionet.c:v1.0 06-feb-96 (c) Hartmut Laue. \n " ;
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/jiffies.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/in.h>
# include <linux/slab.h>
# include <linux/string.h>
# include <linux/delay.h>
# include <linux/timer.h>
# include <linux/init.h>
# include <linux/bitops.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <asm/setup.h>
# include <asm/pgtable.h>
# include <asm/system.h>
# include <asm/io.h>
# include <asm/dma.h>
# include <asm/atarihw.h>
# include <asm/atariints.h>
# include <asm/atari_acsi.h>
# include <asm/atari_stdma.h>
/* use 0 for production, 1 for verification, >2 for debug
*/
# ifndef NET_DEBUG
# define NET_DEBUG 0
# endif
/*
* Global variable ' bionet_debug ' . Can be set at load time by ' insmod '
*/
unsigned int bionet_debug = NET_DEBUG ;
MODULE_PARM ( bionet_debug , " i " ) ;
MODULE_PARM_DESC ( bionet_debug , " bionet debug level (0-2) " ) ;
MODULE_LICENSE ( " GPL " ) ;
static unsigned int bionet_min_poll_time = 2 ;
/* Information that need to be kept for each board.
*/
struct net_local {
struct net_device_stats stats ;
long open_time ; /* for debugging */
int poll_time ; /* polling time varies with net load */
} ;
static struct nic_pkt_s { /* packet format */
unsigned char status ;
unsigned char dummy ;
unsigned char l_lo , l_hi ;
unsigned char buffer [ 3000 ] ;
} * nic_packet ;
unsigned char * phys_nic_packet ;
/* Index to functions, as function prototypes.
*/
static int bionet_open ( struct net_device * dev ) ;
static int bionet_send_packet ( struct sk_buff * skb , struct net_device * dev ) ;
static void bionet_poll_rx ( struct net_device * ) ;
static int bionet_close ( struct net_device * dev ) ;
static struct net_device_stats * net_get_stats ( struct net_device * dev ) ;
static void bionet_tick ( unsigned long ) ;
2005-09-10 00:10:40 +04:00
static DEFINE_TIMER ( bionet_timer , bionet_tick , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
# define STRAM_ADDR(a) (((a) & 0xff000000) == 0)
/* The following routines access the ethernet board connected to the
* ACSI port via the st_dma chip .
*/
# define NODE_ADR 0x60
# define C_READ 8
# define C_WRITE 0x0a
# define C_GETEA 0x0f
# define C_SETCR 0x0e
static int
sendcmd ( unsigned int a0 , unsigned int mod , unsigned int cmd ) {
unsigned int c ;
dma_wd . dma_mode_status = ( mod | ( ( a0 ) ? 2 : 0 ) | 0x88 ) ;
dma_wd . fdc_acces_seccount = cmd ;
dma_wd . dma_mode_status = ( mod | 0x8a ) ;
if ( ! acsi_wait_for_IRQ ( HZ / 2 ) ) /* wait for cmd ack */
return - 1 ; /* timeout */
c = dma_wd . fdc_acces_seccount ;
return ( c & 0xff ) ;
}
static void
set_status ( int cr ) {
sendcmd ( 0 , 0x100 , NODE_ADR | C_SETCR ) ; /* CMD: SET CR */
sendcmd ( 1 , 0x100 , cr ) ;
dma_wd . dma_mode_status = 0x80 ;
}
static int
get_status ( unsigned char * adr ) {
int i , c ;
DISABLE_IRQ ( ) ;
c = sendcmd ( 0 , 0x00 , NODE_ADR | C_GETEA ) ; /* CMD: GET ETH ADR*/
if ( c < 0 ) goto gsend ;
/* now read status bytes */
for ( i = 0 ; i < 6 ; i + + ) {
dma_wd . fdc_acces_seccount = 0 ; /* request next byte */
if ( ! acsi_wait_for_IRQ ( HZ / 2 ) ) { /* wait for cmd ack */
c = - 1 ;
goto gsend ; /* timeout */
}
c = dma_wd . fdc_acces_seccount ;
* adr + + = ( unsigned char ) c ;
}
c = 1 ;
gsend :
dma_wd . dma_mode_status = 0x80 ;
return c ;
}
static irqreturn_t
bionet_intr ( int irq , void * data , struct pt_regs * fp ) {
return IRQ_HANDLED ;
}
static int
get_frame ( unsigned long paddr , int odd ) {
int c ;
unsigned long flags ;
DISABLE_IRQ ( ) ;
local_irq_save ( flags ) ;
dma_wd . dma_mode_status = 0x9a ;
dma_wd . dma_mode_status = 0x19a ;
dma_wd . dma_mode_status = 0x9a ;
dma_wd . fdc_acces_seccount = 0x04 ; /* sector count (was 5) */
dma_wd . dma_lo = ( unsigned char ) paddr ;
paddr > > = 8 ;
dma_wd . dma_md = ( unsigned char ) paddr ;
paddr > > = 8 ;
dma_wd . dma_hi = ( unsigned char ) paddr ;
local_irq_restore ( flags ) ;
c = sendcmd ( 0 , 0x00 , NODE_ADR | C_READ ) ; /* CMD: READ */
if ( c < 128 ) goto rend ;
/* now read block */
c = sendcmd ( 1 , 0x00 , odd ) ; /* odd flag for address shift */
dma_wd . dma_mode_status = 0x0a ;
if ( ! acsi_wait_for_IRQ ( 100 ) ) { /* wait for DMA to complete */
c = - 1 ;
goto rend ;
}
dma_wd . dma_mode_status = 0x8a ;
dma_wd . dma_mode_status = 0x18a ;
dma_wd . dma_mode_status = 0x8a ;
c = dma_wd . fdc_acces_seccount ;
dma_wd . dma_mode_status = 0x88 ;
c = dma_wd . fdc_acces_seccount ;
c = 1 ;
rend :
dma_wd . dma_mode_status = 0x80 ;
udelay ( 40 ) ;
acsi_wait_for_noIRQ ( 20 ) ;
return c ;
}
static int
hardware_send_packet ( unsigned long paddr , int cnt ) {
unsigned int c ;
unsigned long flags ;
DISABLE_IRQ ( ) ;
local_irq_save ( flags ) ;
dma_wd . dma_mode_status = 0x19a ;
dma_wd . dma_mode_status = 0x9a ;
dma_wd . dma_mode_status = 0x19a ;
dma_wd . dma_lo = ( unsigned char ) paddr ;
paddr > > = 8 ;
dma_wd . dma_md = ( unsigned char ) paddr ;
paddr > > = 8 ;
dma_wd . dma_hi = ( unsigned char ) paddr ;
dma_wd . fdc_acces_seccount = 0x4 ; /* sector count */
local_irq_restore ( flags ) ;
c = sendcmd ( 0 , 0x100 , NODE_ADR | C_WRITE ) ; /* CMD: WRITE */
c = sendcmd ( 1 , 0x100 , cnt & 0xff ) ;
c = sendcmd ( 1 , 0x100 , cnt > > 8 ) ;
/* now write block */
dma_wd . dma_mode_status = 0x10a ; /* DMA enable */
if ( ! acsi_wait_for_IRQ ( 100 ) ) /* wait for DMA to complete */
goto end ;
dma_wd . dma_mode_status = 0x19a ; /* DMA disable ! */
c = dma_wd . fdc_acces_seccount ;
end :
c = sendcmd ( 1 , 0x100 , 0 ) ;
c = sendcmd ( 1 , 0x100 , 0 ) ;
dma_wd . dma_mode_status = 0x180 ;
udelay ( 40 ) ;
acsi_wait_for_noIRQ ( 20 ) ;
return ( c & 0x02 ) ;
}
/* Check for a network adaptor of this type, and return '0' if one exists.
*/
struct net_device * __init bionet_probe ( int unit )
{
struct net_device * dev ;
unsigned char station_addr [ 6 ] ;
static unsigned version_printed ;
static int no_more_found ; /* avoid "Probing for..." printed 4 times */
int i ;
int err ;
if ( ! MACH_IS_ATARI | | no_more_found )
return ERR_PTR ( - ENODEV ) ;
dev = alloc_etherdev ( sizeof ( struct net_local ) ) ;
if ( ! dev )
return ERR_PTR ( - ENOMEM ) ;
if ( unit > = 0 ) {
sprintf ( dev - > name , " eth%d " , unit ) ;
netdev_boot_setup_check ( dev ) ;
}
SET_MODULE_OWNER ( dev ) ;
printk ( " Probing for BioNet 100 Adapter... \n " ) ;
stdma_lock ( bionet_intr , NULL ) ;
i = get_status ( station_addr ) ; /* Read the station address PROM. */
ENABLE_IRQ ( ) ;
stdma_release ( ) ;
/* Check the first three octets of the S.A. for the manufactor's code.
*/
if ( i < 0
| | station_addr [ 0 ] ! = ' B '
| | station_addr [ 1 ] ! = ' I '
| | station_addr [ 2 ] ! = ' O ' ) {
no_more_found = 1 ;
printk ( " No BioNet 100 found. \n " ) ;
free_netdev ( dev ) ;
return ERR_PTR ( - ENODEV ) ;
}
if ( bionet_debug > 0 & & version_printed + + = = 0 )
printk ( version ) ;
printk ( " %s: %s found, eth-addr: %02x-%02x-%02x:%02x-%02x-%02x. \n " ,
dev - > name , " BioNet 100 " ,
station_addr [ 0 ] , station_addr [ 1 ] , station_addr [ 2 ] ,
station_addr [ 3 ] , station_addr [ 4 ] , station_addr [ 5 ] ) ;
/* Initialize the device structure. */
nic_packet = ( struct nic_pkt_s * ) acsi_buffer ;
phys_nic_packet = ( unsigned char * ) phys_acsi_buffer ;
if ( bionet_debug > 0 ) {
printk ( " nic_packet at 0x%p, phys at 0x%p \n " ,
nic_packet , phys_nic_packet ) ;
}
dev - > open = bionet_open ;
dev - > stop = bionet_close ;
dev - > hard_start_xmit = bionet_send_packet ;
dev - > get_stats = net_get_stats ;
/* Fill in the fields of the device structure with ethernet-generic
* values . This should be in a common file instead of per - driver .
*/
for ( i = 0 ; i < ETH_ALEN ; i + + ) {
#if 0
dev - > broadcast [ i ] = 0xff ;
# endif
dev - > dev_addr [ i ] = station_addr [ i ] ;
}
err = register_netdev ( dev ) ;
if ( ! err )
return dev ;
free_netdev ( dev ) ;
return ERR_PTR ( err ) ;
}
/* Open/initialize the board. This is called (in the current kernel)
sometime after booting when the ' ifconfig ' program is run .
This routine should set everything up anew at each open , even
registers that " should " only need to be set once at boot , so that
there is non - reboot way to recover if something goes wrong .
*/
static int
bionet_open ( struct net_device * dev ) {
struct net_local * lp = netdev_priv ( dev ) ;
if ( bionet_debug > 0 )
printk ( " bionet_open \n " ) ;
stdma_lock ( bionet_intr , NULL ) ;
/* Reset the hardware here.
*/
set_status ( 4 ) ;
lp - > open_time = 0 ; /*jiffies*/
lp - > poll_time = MAX_POLL_TIME ;
dev - > tbusy = 0 ;
dev - > interrupt = 0 ;
dev - > start = 1 ;
stdma_release ( ) ;
bionet_timer . data = ( long ) dev ;
bionet_timer . expires = jiffies + lp - > poll_time ;
add_timer ( & bionet_timer ) ;
return 0 ;
}
static int
bionet_send_packet ( struct sk_buff * skb , struct net_device * dev ) {
struct net_local * lp = netdev_priv ( dev ) ;
unsigned long flags ;
/* Block a timer-based transmit from overlapping. This could better be
* done with atomic_swap ( 1 , dev - > tbusy ) , but set_bit ( ) works as well .
*/
local_irq_save ( flags ) ;
if ( stdma_islocked ( ) ) {
local_irq_restore ( flags ) ;
lp - > stats . tx_errors + + ;
}
else {
int length = ETH_ZLEN < skb - > len ? skb - > len : ETH_ZLEN ;
unsigned long buf = virt_to_phys ( skb - > data ) ;
int stat ;
stdma_lock ( bionet_intr , NULL ) ;
local_irq_restore ( flags ) ;
if ( ! STRAM_ADDR ( buf + length - 1 ) ) {
memcpy ( nic_packet - > buffer , skb - > data , length ) ;
buf = ( unsigned long ) & ( ( struct nic_pkt_s * ) phys_nic_packet ) - > buffer ;
}
if ( bionet_debug > 1 ) {
u_char * data = nic_packet - > buffer , * p ;
int i ;
printk ( " %s: TX pkt type 0x%4x from " , dev - > name ,
( ( u_short * ) data ) [ 6 ] ) ;
for ( p = & data [ 6 ] , i = 0 ; i < 6 ; i + + )
printk ( " %02x%s " , * p + + , i ! = 5 ? " : " : " " ) ;
printk ( " to " ) ;
for ( p = data , i = 0 ; i < 6 ; i + + )
printk ( " %02x%s " , * p + + , i ! = 5 ? " : " : " " " \n " ) ;
printk ( " %s: " , dev - > name ) ;
printk ( " data %02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x "
" %02x%02x%02x%02x len %d \n " ,
data [ 12 ] , data [ 13 ] , data [ 14 ] , data [ 15 ] , data [ 16 ] , data [ 17 ] , data [ 18 ] , data [ 19 ] ,
data [ 20 ] , data [ 21 ] , data [ 22 ] , data [ 23 ] , data [ 24 ] , data [ 25 ] , data [ 26 ] , data [ 27 ] ,
data [ 28 ] , data [ 29 ] , data [ 30 ] , data [ 31 ] , data [ 32 ] , data [ 33 ] ,
length ) ;
}
dma_cache_maintenance ( buf , length , 1 ) ;
stat = hardware_send_packet ( buf , length ) ;
ENABLE_IRQ ( ) ;
stdma_release ( ) ;
dev - > trans_start = jiffies ;
dev - > tbusy = 0 ;
lp - > stats . tx_packets + + ;
lp - > stats . tx_bytes + = length ;
}
dev_kfree_skb ( skb ) ;
return 0 ;
}
/* We have a good packet(s), get it/them out of the buffers.
*/
static void
bionet_poll_rx ( struct net_device * dev ) {
struct net_local * lp = netdev_priv ( dev ) ;
int boguscount = 10 ;
int pkt_len , status ;
unsigned long flags ;
local_irq_save ( flags ) ;
/* ++roman: Take care at locking the ST-DMA... This must be done with ints
* off , since otherwise an int could slip in between the question and the
* locking itself , and then we ' d go to sleep . . . And locking itself is
* necessary to keep the floppy_change timer from working with ST - DMA
* registers . */
if ( stdma_islocked ( ) ) {
local_irq_restore ( flags ) ;
return ;
}
stdma_lock ( bionet_intr , NULL ) ;
DISABLE_IRQ ( ) ;
local_irq_restore ( flags ) ;
if ( lp - > poll_time < MAX_POLL_TIME ) lp - > poll_time + + ;
while ( boguscount - - ) {
status = get_frame ( ( unsigned long ) phys_nic_packet , 0 ) ;
if ( status = = 0 ) break ;
/* Good packet... */
dma_cache_maintenance ( ( unsigned long ) phys_nic_packet , 1520 , 0 ) ;
pkt_len = ( nic_packet - > l_hi < < 8 ) | nic_packet - > l_lo ;
lp - > poll_time = bionet_min_poll_time ; /* fast poll */
if ( pkt_len > = 60 & & pkt_len < = 1520 ) {
/* ^^^^ war 1514 KHL */
/* Malloc up new buffer.
*/
struct sk_buff * skb = dev_alloc_skb ( pkt_len + 2 ) ;
if ( skb = = NULL ) {
printk ( " %s: Memory squeeze, dropping packet. \n " ,
dev - > name ) ;
lp - > stats . rx_dropped + + ;
break ;
}
skb - > dev = dev ;
skb_reserve ( skb , 2 ) ; /* 16 Byte align */
skb_put ( skb , pkt_len ) ; /* make room */
/* 'skb->data' points to the start of sk_buff data area.
*/
memcpy ( skb - > data , nic_packet - > buffer , pkt_len ) ;
skb - > protocol = eth_type_trans ( skb , dev ) ;
netif_rx ( skb ) ;
dev - > last_rx = jiffies ;
lp - > stats . rx_packets + + ;
lp - > stats . rx_bytes + = pkt_len ;
/* If any worth-while packets have been received, dev_rint()
has done a mark_bh ( INET_BH ) for us and will work on them
when we get to the bottom - half routine .
*/
if ( bionet_debug > 1 ) {
u_char * data = nic_packet - > buffer , * p ;
int i ;
printk ( " %s: RX pkt type 0x%4x from " , dev - > name ,
( ( u_short * ) data ) [ 6 ] ) ;
for ( p = & data [ 6 ] , i = 0 ; i < 6 ; i + + )
printk ( " %02x%s " , * p + + , i ! = 5 ? " : " : " " ) ;
printk ( " to " ) ;
for ( p = data , i = 0 ; i < 6 ; i + + )
printk ( " %02x%s " , * p + + , i ! = 5 ? " : " : " " " \n " ) ;
printk ( " %s: " , dev - > name ) ;
printk ( " data %02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x "
" %02x%02x%02x%02x len %d \n " ,
data [ 12 ] , data [ 13 ] , data [ 14 ] , data [ 15 ] , data [ 16 ] , data [ 17 ] , data [ 18 ] , data [ 19 ] ,
data [ 20 ] , data [ 21 ] , data [ 22 ] , data [ 23 ] , data [ 24 ] , data [ 25 ] , data [ 26 ] , data [ 27 ] ,
data [ 28 ] , data [ 29 ] , data [ 30 ] , data [ 31 ] , data [ 32 ] , data [ 33 ] ,
pkt_len ) ;
}
}
else {
printk ( " Packet has wrong length: %04d bytes \n " , pkt_len ) ;
lp - > stats . rx_errors + + ;
}
}
stdma_release ( ) ;
ENABLE_IRQ ( ) ;
return ;
}
/* bionet_tick: called by bionet_timer. Reads packets from the adapter,
* passes them to the higher layers and restarts the timer .
*/
static void
bionet_tick ( unsigned long data ) {
struct net_device * dev = ( struct net_device * ) data ;
struct net_local * lp = netdev_priv ( dev ) ;
if ( bionet_debug > 0 & & ( lp - > open_time + + & 7 ) = = 8 )
printk ( " bionet_tick: %ld \n " , lp - > open_time ) ;
if ( ! stdma_islocked ( ) ) bionet_poll_rx ( dev ) ;
bionet_timer . expires = jiffies + lp - > poll_time ;
add_timer ( & bionet_timer ) ;
}
/* The inverse routine to bionet_open().
*/
static int
bionet_close ( struct net_device * dev ) {
struct net_local * lp = netdev_priv ( dev ) ;
if ( bionet_debug > 0 )
printk ( " bionet_close, open_time=%ld \n " , lp - > open_time ) ;
del_timer ( & bionet_timer ) ;
stdma_lock ( bionet_intr , NULL ) ;
set_status ( 0 ) ;
lp - > open_time = 0 ;
dev - > tbusy = 1 ;
dev - > start = 0 ;
stdma_release ( ) ;
return 0 ;
}
/* Get the current statistics.
This may be called with the card open or closed .
*/
static struct net_device_stats * net_get_stats ( struct net_device * dev )
{
struct net_local * lp = netdev_priv ( dev ) ;
return & lp - > stats ;
}
# ifdef MODULE
static struct net_device * bio_dev ;
int init_module ( void )
{
bio_dev = bionet_probe ( - 1 ) ;
if ( IS_ERR ( bio_dev ) )
return PTR_ERR ( bio_dev ) ;
return 0 ;
}
void cleanup_module ( void )
{
unregister_netdev ( bio_dev ) ;
free_netdev ( bio_dev ) ;
}
# endif /* MODULE */
/* Local variables:
* compile - command : " gcc -D__KERNEL__ -I/usr/src/linux/include
- b m68k - linuxaout - Wall - Wstrict - prototypes - O2
- fomit - frame - pointer - pipe - DMODULE - I . . / . . / net / inet - c bionet . c "
* version - control : t
* kept - new - versions : 5
* tab - width : 8
* End :
*/