2005-04-17 02:20:36 +04:00
/* drivers/char/ser_a2232.c */
/* $Id: ser_a2232.c,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */
/* Linux serial driver for the Amiga A2232 board */
/* This driver is MAINTAINED. Before applying any changes, please contact
* the author .
*/
/* Copyright (c) 2000-2001 Enver Haase <ehaase@inf.fu-berlin.de>
* alias The A2232 driver project < A2232 @ gmx . net >
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
/***************************** Documentation ************************/
/*
* This driver is in EXPERIMENTAL state . That means I could not find
* someone with five A2232 boards with 35 ports running at 19200 bps
* at the same time and test the machine ' s behaviour .
* However , I know that you can performance - tweak this driver ( see
* the source code ) .
* One thing to consider is the time this driver consumes during the
* Amiga ' s vertical blank interrupt . Everything that is to be done
* _IS DONE_ when entering the vertical blank interrupt handler of
* this driver .
* However , it would be more sane to only do the job for only ONE card
* instead of ALL cards at a time ; or , more generally , to handle only
* SOME ports instead of ALL ports at a time .
* However , as long as no - one runs into problems I guess I shouldn ' t
* change the driver as it runs fine for me : ) .
*
* Version history of this file :
* 0.4 Resolved licensing issues .
* 0.3 Inclusion in the Linux / m68k tree , small fixes .
* 0.2 Added documentation , minor typo fixes .
* 0.1 Initial release .
*
* TO DO :
* - Handle incoming BREAK events . I guess " Stevens: Advanced
* Programming in the UNIX ( R ) Environment " is a good reference
* on what is to be done .
* - When installing as a module , don ' t simply ' printk ' text , but
* send it to the TTY used by the user .
*
* THANKS TO :
* - Jukka Marin ( 65 EC02 code ) .
* - The other NetBSD developers on whose A2232 driver I had a
* pretty close look . However , I didn ' t copy any code so it
* is okay to put my code under the GPL and include it into
* Linux .
*/
/***************************** End of Documentation *****************/
/***************************** Defines ******************************/
/*
* Enables experimental 115200 ( normal ) 230400 ( turbo ) baud rate .
* The A2232 specification states it can only operate at speeds up to
* 19200 bits per second , and I was not able to send a file via
* " sz " / " rz " and a null - modem cable from one A2232 port to another
* at 115200 bits per second .
* However , this might work for you .
*/
# undef A2232_SPEEDHACK
/*
* Default is not to use RTS / CTS so you could be talked to death .
*/
# define A2232_SUPPRESS_RTSCTS_WARNING
/************************* End of Defines ***************************/
/***************************** Includes *****************************/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/sched.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/tty.h>
# include <asm/setup.h>
# include <asm/amigaints.h>
# include <asm/amigahw.h>
# include <linux/zorro.h>
# include <asm/irq.h>
# include <asm/semaphore.h>
# include <linux/delay.h>
# include <linux/serial.h>
# include <linux/generic_serial.h>
# include "ser_a2232.h"
# include "ser_a2232fw.h"
/************************* End of Includes **************************/
/***************************** Prototypes ***************************/
/* The interrupt service routine */
static irqreturn_t a2232_vbl_inter ( int irq , void * data , struct pt_regs * fp ) ;
/* Initialize the port structures */
static void a2232_init_portstructs ( void ) ;
/* Initialize and register TTY drivers. */
/* returns 0 IFF successful */
static int a2232_init_drivers ( void ) ;
/* BEGIN GENERIC_SERIAL PROTOTYPES */
static void a2232_disable_tx_interrupts ( void * ptr ) ;
static void a2232_enable_tx_interrupts ( void * ptr ) ;
static void a2232_disable_rx_interrupts ( void * ptr ) ;
static void a2232_enable_rx_interrupts ( void * ptr ) ;
static int a2232_get_CD ( void * ptr ) ;
static void a2232_shutdown_port ( void * ptr ) ;
static int a2232_set_real_termios ( void * ptr ) ;
static int a2232_chars_in_buffer ( void * ptr ) ;
static void a2232_close ( void * ptr ) ;
static void a2232_hungup ( void * ptr ) ;
/* static void a2232_getserial (void *ptr, struct serial_struct *sp); */
/* END GENERIC_SERIAL PROTOTYPES */
/* Functions that the TTY driver struct expects */
static int a2232_ioctl ( struct tty_struct * tty , struct file * file ,
unsigned int cmd , unsigned long arg ) ;
static void a2232_throttle ( struct tty_struct * tty ) ;
static void a2232_unthrottle ( struct tty_struct * tty ) ;
static int a2232_open ( struct tty_struct * tty , struct file * filp ) ;
/************************* End of Prototypes ************************/
/***************************** Global variables *********************/
/*---------------------------------------------------------------------------
* Interface from generic_serial . c back here
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static struct real_driver a2232_real_driver = {
a2232_disable_tx_interrupts ,
a2232_enable_tx_interrupts ,
a2232_disable_rx_interrupts ,
a2232_enable_rx_interrupts ,
a2232_get_CD ,
a2232_shutdown_port ,
a2232_set_real_termios ,
a2232_chars_in_buffer ,
a2232_close ,
a2232_hungup ,
NULL /* a2232_getserial */
} ;
static void * a2232_driver_ID = & a2232_driver_ID ; // Some memory address WE own.
/* Ports structs */
static struct a2232_port a2232_ports [ MAX_A2232_BOARDS * NUMLINES ] ;
/* TTY driver structs */
static struct tty_driver * a2232_driver ;
/* nr of cards completely (all ports) and correctly configured */
static int nr_a2232 ;
/* zorro_dev structs for the A2232's */
static struct zorro_dev * zd_a2232 [ MAX_A2232_BOARDS ] ;
/***************************** End of Global variables **************/
/* Helper functions */
static inline volatile struct a2232memory * a2232mem ( unsigned int board )
{
return ( volatile struct a2232memory * ) ZTWO_VADDR ( zd_a2232 [ board ] - > resource . start ) ;
}
static inline volatile struct a2232status * a2232stat ( unsigned int board ,
unsigned int portonboard )
{
volatile struct a2232memory * mem = a2232mem ( board ) ;
return & ( mem - > Status [ portonboard ] ) ;
}
static inline void a2232_receive_char ( struct a2232_port * port , int ch , int err )
{
/* Mostly stolen from other drivers.
Maybe one could implement a more efficient version by not only
transferring one character at a time .
*/
struct tty_struct * tty = port - > gs . tty ;
if ( tty - > flip . count > = TTY_FLIPBUF_SIZE )
return ;
tty - > flip . count + + ;
#if 0
switch ( err ) {
case TTY_BREAK :
break ;
case TTY_PARITY :
break ;
case TTY_OVERRUN :
break ;
case TTY_FRAME :
break ;
}
# endif
* tty - > flip . flag_buf_ptr + + = err ;
* tty - > flip . char_buf_ptr + + = ch ;
tty_flip_buffer_push ( tty ) ;
}
/***************************** Functions ****************************/
/*** BEGIN OF REAL_DRIVER FUNCTIONS ***/
static void a2232_disable_tx_interrupts ( void * ptr )
{
struct a2232_port * port ;
volatile struct a2232status * stat ;
unsigned long flags ;
port = ptr ;
stat = a2232stat ( port - > which_a2232 , port - > which_port_on_a2232 ) ;
stat - > OutDisable = - 1 ;
/* Does this here really have to be? */
local_irq_save ( flags ) ;
port - > gs . flags & = ~ GS_TX_INTEN ;
local_irq_restore ( flags ) ;
}
static void a2232_enable_tx_interrupts ( void * ptr )
{
struct a2232_port * port ;
volatile struct a2232status * stat ;
unsigned long flags ;
port = ptr ;
stat = a2232stat ( port - > which_a2232 , port - > which_port_on_a2232 ) ;
stat - > OutDisable = 0 ;
/* Does this here really have to be? */
local_irq_save ( flags ) ;
port - > gs . flags | = GS_TX_INTEN ;
local_irq_restore ( flags ) ;
}
static void a2232_disable_rx_interrupts ( void * ptr )
{
struct a2232_port * port ;
port = ptr ;
port - > disable_rx = - 1 ;
}
static void a2232_enable_rx_interrupts ( void * ptr )
{
struct a2232_port * port ;
port = ptr ;
port - > disable_rx = 0 ;
}
static int a2232_get_CD ( void * ptr )
{
return ( ( struct a2232_port * ) ptr ) - > cd_status ;
}
static void a2232_shutdown_port ( void * ptr )
{
struct a2232_port * port ;
volatile struct a2232status * stat ;
unsigned long flags ;
port = ptr ;
stat = a2232stat ( port - > which_a2232 , port - > which_port_on_a2232 ) ;
local_irq_save ( flags ) ;
port - > gs . flags & = ~ GS_ACTIVE ;
if ( port - > gs . tty & & port - > gs . tty - > termios - > c_cflag & HUPCL ) {
/* Set DTR and RTS to Low, flush output.
The NetBSD driver " msc.c " does it this way . */
stat - > Command = ( ( stat - > Command & ~ A2232CMD_CMask ) |
A2232CMD_Close ) ;
stat - > OutFlush = - 1 ;
stat - > Setup = - 1 ;
}
local_irq_restore ( flags ) ;
/* After analyzing control flow, I think a2232_shutdown_port
is actually the last call from the system when at application
level someone issues a " echo Hello >>/dev/ttyY0 " .
Therefore I think the MOD_DEC_USE_COUNT should be here and
not in " a2232_close() " . See the comment in " sx.c " , too .
If you run into problems , compile this driver into the
kernel instead of compiling it as a module . */
}
static int a2232_set_real_termios ( void * ptr )
{
unsigned int cflag , baud , chsize , stopb , parity , softflow ;
int rate ;
int a2232_param , a2232_cmd ;
unsigned long flags ;
unsigned int i ;
struct a2232_port * port = ptr ;
volatile struct a2232status * status ;
volatile struct a2232memory * mem ;
if ( ! port - > gs . tty | | ! port - > gs . tty - > termios ) return 0 ;
status = a2232stat ( port - > which_a2232 , port - > which_port_on_a2232 ) ;
mem = a2232mem ( port - > which_a2232 ) ;
a2232_param = a2232_cmd = 0 ;
// get baud rate
baud = port - > gs . baud ;
if ( baud = = 0 ) {
/* speed == 0 -> drop DTR, do nothing else */
local_irq_save ( flags ) ;
// Clear DTR (and RTS... mhhh).
status - > Command = ( ( status - > Command & ~ A2232CMD_CMask ) |
A2232CMD_Close ) ;
status - > OutFlush = - 1 ;
status - > Setup = - 1 ;
local_irq_restore ( flags ) ;
return 0 ;
}
rate = A2232_BAUD_TABLE_NOAVAIL ;
for ( i = 0 ; i < A2232_BAUD_TABLE_NUM_RATES * 3 ; i + = 3 ) {
if ( a2232_baud_table [ i ] = = baud ) {
if ( mem - > Common . Crystal = = A2232_TURBO ) rate = a2232_baud_table [ i + 2 ] ;
else rate = a2232_baud_table [ i + 1 ] ;
}
}
if ( rate = = A2232_BAUD_TABLE_NOAVAIL ) {
printk ( " a2232: Board %d Port %d unsupported baud rate: %d baud. Using another. \n " , port - > which_a2232 , port - > which_port_on_a2232 , baud ) ;
// This is useful for both (turbo or normal) Crystal versions.
rate = A2232PARAM_B9600 ;
}
a2232_param | = rate ;
cflag = port - > gs . tty - > termios - > c_cflag ;
// get character size
chsize = cflag & CSIZE ;
switch ( chsize ) {
case CS8 : a2232_param | = A2232PARAM_8Bit ; break ;
case CS7 : a2232_param | = A2232PARAM_7Bit ; break ;
case CS6 : a2232_param | = A2232PARAM_6Bit ; break ;
case CS5 : a2232_param | = A2232PARAM_5Bit ; break ;
default : printk ( " a2232: Board %d Port %d unsupported character size: %d. Using 8 data bits. \n " ,
port - > which_a2232 , port - > which_port_on_a2232 , chsize ) ;
a2232_param | = A2232PARAM_8Bit ; break ;
}
// get number of stop bits
stopb = cflag & CSTOPB ;
if ( stopb ) { // two stop bits instead of one
printk ( " a2232: Board %d Port %d 2 stop bits unsupported. Using 1 stop bit. \n " ,
port - > which_a2232 , port - > which_port_on_a2232 ) ;
}
// Warn if RTS/CTS not wanted
if ( ! ( cflag & CRTSCTS ) ) {
# ifndef A2232_SUPPRESS_RTSCTS_WARNING
printk ( " a2232: Board %d Port %d cannot switch off firmware-implemented RTS/CTS hardware flow control. \n " ,
port - > which_a2232 , port - > which_port_on_a2232 ) ;
# endif
}
/* I think this is correct.
However , IXOFF means _input_ flow control and I wonder
if one should care about IXON _output_ flow control ,
too . If this makes problems , one should turn the A2232
firmware XON / XOFF " SoftFlow " flow control off and use
the conventional way of inserting START / STOP characters
by hand in throttle ( ) / unthrottle ( ) .
*/
softflow = ! ! ( port - > gs . tty - > termios - > c_iflag & IXOFF ) ;
// get Parity (Enabled/Disabled? If Enabled, Odd or Even?)
parity = cflag & ( PARENB | PARODD ) ;
if ( parity & PARENB ) {
if ( parity & PARODD ) {
a2232_cmd | = A2232CMD_OddParity ;
}
else {
a2232_cmd | = A2232CMD_EvenParity ;
}
}
else a2232_cmd | = A2232CMD_NoParity ;
/* Hmm. Maybe an own a2232_port structure
member would be cleaner ? */
if ( cflag & CLOCAL )
port - > gs . flags & = ~ ASYNC_CHECK_CD ;
else
port - > gs . flags | = ASYNC_CHECK_CD ;
/* Now we have all parameters and can go to set them: */
local_irq_save ( flags ) ;
status - > Param = a2232_param | A2232PARAM_RcvBaud ;
status - > Command = a2232_cmd | A2232CMD_Open | A2232CMD_Enable ;
status - > SoftFlow = softflow ;
status - > OutDisable = 0 ;
status - > Setup = - 1 ;
local_irq_restore ( flags ) ;
return 0 ;
}
static int a2232_chars_in_buffer ( void * ptr )
{
struct a2232_port * port ;
volatile struct a2232status * status ;
unsigned char ret ; /* we need modulo-256 arithmetics */
port = ptr ;
status = a2232stat ( port - > which_a2232 , port - > which_port_on_a2232 ) ;
# if A2232_IOBUFLEN != 256
# error "Re-Implement a2232_chars_in_buffer()!"
# endif
ret = ( status - > OutHead - status - > OutTail ) ;
return ret ;
}
static void a2232_close ( void * ptr )
{
a2232_disable_tx_interrupts ( ptr ) ;
a2232_disable_rx_interrupts ( ptr ) ;
/* see the comment in a2232_shutdown_port above. */
}
static void a2232_hungup ( void * ptr )
{
a2232_close ( ptr ) ;
}
/*** END OF REAL_DRIVER FUNCTIONS ***/
/*** BEGIN FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/
static int a2232_ioctl ( struct tty_struct * tty , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return - ENOIOCTLCMD ;
}
static void a2232_throttle ( struct tty_struct * tty )
{
/* Throttle: System cannot take another chars: Drop RTS or
send the STOP char or whatever .
The A2232 firmware does RTS / CTS anyway , and XON / XOFF
if switched on . So the only thing we can do at this
layer here is not taking any characters out of the
A2232 buffer any more . */
struct a2232_port * port = ( struct a2232_port * ) tty - > driver_data ;
port - > throttle_input = - 1 ;
}
static void a2232_unthrottle ( struct tty_struct * tty )
{
/* Unthrottle: dual to "throttle()" above. */
struct a2232_port * port = ( struct a2232_port * ) tty - > driver_data ;
port - > throttle_input = 0 ;
}
static int a2232_open ( struct tty_struct * tty , struct file * filp )
{
/* More or less stolen from other drivers. */
int line ;
int retval ;
struct a2232_port * port ;
line = tty - > index ;
port = & a2232_ports [ line ] ;
tty - > driver_data = port ;
port - > gs . tty = tty ;
port - > gs . count + + ;
retval = gs_init_port ( & port - > gs ) ;
if ( retval ) {
port - > gs . count - - ;
return retval ;
}
port - > gs . flags | = GS_ACTIVE ;
retval = gs_block_til_ready ( port , filp ) ;
if ( retval ) {
port - > gs . count - - ;
return retval ;
}
a2232_enable_rx_interrupts ( port ) ;
return 0 ;
}
/*** END OF FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/
static irqreturn_t a2232_vbl_inter ( int irq , void * data , struct pt_regs * fp )
{
# if A2232_IOBUFLEN != 256
# error "Re-Implement a2232_vbl_inter()!"
# endif
struct a2232_port * port ;
volatile struct a2232memory * mem ;
volatile struct a2232status * status ;
unsigned char newhead ;
unsigned char bufpos ; /* Must be unsigned char. We need the modulo-256 arithmetics */
unsigned char ncd , ocd , ccd ; /* names consistent with the NetBSD driver */
volatile u_char * ibuf , * cbuf , * obuf ;
int ch , err , n , p ;
for ( n = 0 ; n < nr_a2232 ; n + + ) { /* for every completely initialized A2232 board */
mem = a2232mem ( n ) ;
for ( p = 0 ; p < NUMLINES ; p + + ) { /* for every port on this board */
err = 0 ;
port = & a2232_ports [ n * NUMLINES + p ] ;
if ( port - > gs . flags & GS_ACTIVE ) { /* if the port is used */
status = a2232stat ( n , p ) ;
if ( ! port - > disable_rx & & ! port - > throttle_input ) { /* If input is not disabled */
newhead = status - > InHead ; /* 65EC02 write pointer */
bufpos = status - > InTail ;
/* check for input for this port */
if ( newhead ! = bufpos ) {
/* buffer for input chars/events */
ibuf = mem - > InBuf [ p ] ;
/* data types of bytes in ibuf */
cbuf = mem - > InCtl [ p ] ;
/* do for all chars */
while ( bufpos ! = newhead ) {
/* which type of input data? */
switch ( cbuf [ bufpos ] ) {
/* switch on input event (CD, BREAK, etc.) */
case A2232INCTL_EVENT :
switch ( ibuf [ bufpos + + ] ) {
case A2232EVENT_Break :
/* TODO: Handle BREAK signal */
break ;
/* A2232EVENT_CarrierOn and A2232EVENT_CarrierOff are
handled in a separate queue and should not occur here . */
case A2232EVENT_Sync :
printk ( " A2232: 65EC02 software sent SYNC event, don't know what to do. Ignoring. " ) ;
break ;
default :
printk ( " A2232: 65EC02 software broken, unknown event type %d occurred. \n " , ibuf [ bufpos - 1 ] ) ;
} /* event type switch */
break ;
case A2232INCTL_CHAR :
/* Receive incoming char */
a2232_receive_char ( port , ibuf [ bufpos ] , err ) ;
bufpos + + ;
break ;
default :
printk ( " A2232: 65EC02 software broken, unknown data type %d occurred. \n " , cbuf [ bufpos ] ) ;
bufpos + + ;
} /* switch on input data type */
} /* while there's something in the buffer */
status - > InTail = bufpos ; /* tell 65EC02 what we've read */
} /* if there was something in the buffer */
} /* If input is not disabled */
/* Now check if there's something to output */
obuf = mem - > OutBuf [ p ] ;
bufpos = status - > OutHead ;
while ( ( port - > gs . xmit_cnt > 0 ) & &
( ! port - > gs . tty - > stopped ) & &
( ! port - > gs . tty - > hw_stopped ) ) { /* While there are chars to transmit */
if ( ( ( bufpos + 1 ) & A2232_IOBUFLENMASK ) ! = status - > OutTail ) { /* If the A2232 buffer is not full */
ch = port - > gs . xmit_buf [ port - > gs . xmit_tail ] ; /* get the next char to transmit */
port - > gs . xmit_tail = ( port - > gs . xmit_tail + 1 ) & ( SERIAL_XMIT_SIZE - 1 ) ; /* modulo-addition for the gs.xmit_buf ring-buffer */
obuf [ bufpos + + ] = ch ; /* put it into the A2232 buffer */
port - > gs . xmit_cnt - - ;
}
else { /* If A2232 the buffer is full */
break ; /* simply stop filling it. */
}
}
status - > OutHead = bufpos ;
/* WakeUp if output buffer runs low */
if ( ( port - > gs . xmit_cnt < = port - > gs . wakeup_chars ) & & port - > gs . tty ) {
tty_wakeup ( port - > gs . tty ) ;
}
} // if the port is used
} // for every port on the board
/* Now check the CD message queue */
newhead = mem - > Common . CDHead ;
bufpos = mem - > Common . CDTail ;
if ( newhead ! = bufpos ) { /* There are CD events in queue */
ocd = mem - > Common . CDStatus ; /* get old status bits */
while ( newhead ! = bufpos ) { /* read all events */
ncd = mem - > CDBuf [ bufpos + + ] ; /* get one event */
ccd = ncd ^ ocd ; /* mask of changed lines */
ocd = ncd ; /* save new status bits */
for ( p = 0 ; p < NUMLINES ; p + + ) { /* for all ports */
if ( ccd & 1 ) { /* this one changed */
struct a2232_port * port = & a2232_ports [ n * 7 + p ] ;
port - > cd_status = ! ( ncd & 1 ) ; /* ncd&1 <=> CD is now off */
if ( ! ( port - > gs . flags & ASYNC_CHECK_CD ) )
; /* Don't report DCD changes */
else if ( port - > cd_status ) { // if DCD on: DCD went UP!
/* Are we blocking in open?*/
wake_up_interruptible ( & port - > gs . open_wait ) ;
}
else { // if DCD off: DCD went DOWN!
if ( port - > gs . tty )
tty_hangup ( port - > gs . tty ) ;
}
} // if CD changed for this port
ccd > > = 1 ;
ncd > > = 1 ; /* Shift bits for next line */
} // for every port
} // while CD events in queue
mem - > Common . CDStatus = ocd ; /* save new status */
mem - > Common . CDTail = bufpos ; /* remove events */
} // if events in CD queue
} // for every completely initialized A2232 board
return IRQ_HANDLED ;
}
static void a2232_init_portstructs ( void )
{
struct a2232_port * port ;
int i ;
for ( i = 0 ; i < MAX_A2232_BOARDS * NUMLINES ; i + + ) {
port = a2232_ports + i ;
port - > which_a2232 = i / NUMLINES ;
port - > which_port_on_a2232 = i % NUMLINES ;
port - > disable_rx = port - > throttle_input = port - > cd_status = 0 ;
port - > gs . magic = A2232_MAGIC ;
port - > gs . close_delay = HZ / 2 ;
port - > gs . closing_wait = 30 * HZ ;
port - > gs . rd = & a2232_real_driver ;
# ifdef NEW_WRITE_LOCKING
init_MUTEX ( & ( port - > gs . port_write_sem ) ) ;
# endif
init_waitqueue_head ( & port - > gs . open_wait ) ;
init_waitqueue_head ( & port - > gs . close_wait ) ;
}
}
static struct tty_operations a2232_ops = {
. open = a2232_open ,
. close = gs_close ,
. write = gs_write ,
. put_char = gs_put_char ,
. flush_chars = gs_flush_chars ,
. write_room = gs_write_room ,
. chars_in_buffer = gs_chars_in_buffer ,
. flush_buffer = gs_flush_buffer ,
. ioctl = a2232_ioctl ,
. throttle = a2232_throttle ,
. unthrottle = a2232_unthrottle ,
. set_termios = gs_set_termios ,
. stop = gs_stop ,
. start = gs_start ,
. hangup = gs_hangup ,
} ;
static int a2232_init_drivers ( void )
{
int error ;
a2232_driver = alloc_tty_driver ( NUMLINES * nr_a2232 ) ;
if ( ! a2232_driver )
return - ENOMEM ;
a2232_driver - > owner = THIS_MODULE ;
a2232_driver - > driver_name = " commodore_a2232 " ;
a2232_driver - > name = " ttyY " ;
a2232_driver - > major = A2232_NORMAL_MAJOR ;
a2232_driver - > type = TTY_DRIVER_TYPE_SERIAL ;
a2232_driver - > subtype = SERIAL_TYPE_NORMAL ;
a2232_driver - > init_termios = tty_std_termios ;
a2232_driver - > init_termios . c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL ;
a2232_driver - > flags = TTY_DRIVER_REAL_RAW ;
tty_set_operations ( a2232_driver , & a2232_ops ) ;
if ( ( error = tty_register_driver ( a2232_driver ) ) ) {
printk ( KERN_ERR " A2232: Couldn't register A2232 driver, error = %d \n " ,
error ) ;
put_tty_driver ( a2232_driver ) ;
return 1 ;
}
return 0 ;
}
static int __init a2232board_init ( void )
{
struct zorro_dev * z ;
unsigned int boardaddr ;
int bcount ;
short start ;
u_char * from ;
volatile u_char * to ;
volatile struct a2232memory * mem ;
# ifdef CONFIG_SMP
return - ENODEV ; /* This driver is not SMP aware. Is there an SMP ZorroII-bus-machine? */
# endif
if ( ! MACH_IS_AMIGA ) {
return - ENODEV ;
}
printk ( " Commodore A2232 driver initializing. \n " ) ; /* Say that we're alive. */
z = NULL ;
nr_a2232 = 0 ;
while ( ( z = zorro_find_device ( ZORRO_WILDCARD , z ) ) ) {
if ( ( z - > id ! = ZORRO_PROD_CBM_A2232_PROTOTYPE ) & &
( z - > id ! = ZORRO_PROD_CBM_A2232 ) ) {
continue ; // The board found was no A2232
}
if ( ! zorro_request_device ( z , " A2232 driver " ) )
continue ;
printk ( " Commodore A2232 found (#%d). \n " , nr_a2232 ) ;
zd_a2232 [ nr_a2232 ] = z ;
boardaddr = ZTWO_VADDR ( z - > resource . start ) ;
printk ( " Board is located at address 0x%x, size is 0x%x. \n " , boardaddr , ( unsigned int ) ( ( z - > resource . end + 1 ) - ( z - > resource . start ) ) ) ;
mem = ( volatile struct a2232memory * ) boardaddr ;
( void ) mem - > Enable6502Reset ; /* copy the code across to the board */
to = ( u_char * ) mem ; from = a2232_65EC02code ; bcount = sizeof ( a2232_65EC02code ) - 2 ;
start = * ( short * ) from ;
from + = sizeof ( start ) ;
to + = start ;
while ( bcount - - ) * to + + = * from + + ;
printk ( " 65EC02 software uploaded to the A2232 memory. \n " ) ;
mem - > Common . Crystal = A2232_UNKNOWN ; /* use automatic speed check */
/* start 6502 running */
( void ) mem - > ResetBoard ;
printk ( " A2232's 65EC02 CPU up and running. \n " ) ;
/* wait until speed detector has finished */
for ( bcount = 0 ; bcount < 2000 ; bcount + + ) {
udelay ( 1000 ) ;
if ( mem - > Common . Crystal )
break ;
}
printk ( ( mem - > Common . Crystal ? " A2232 oscillator crystal detected by 65EC02 software: " : " 65EC02 software could not determine A2232 oscillator crystal: " ) ) ;
switch ( mem - > Common . Crystal ) {
case A2232_UNKNOWN :
printk ( " Unknown crystal. \n " ) ;
break ;
case A2232_NORMAL :
printk ( " Normal crystal. \n " ) ;
break ;
case A2232_TURBO :
printk ( " Turbo crystal. \n " ) ;
break ;
default :
printk ( " 0x%x. Huh? \n " , mem - > Common . Crystal ) ;
}
nr_a2232 + + ;
}
2005-10-31 02:02:23 +03:00
printk ( " Total: %d A2232 boards initialized. \n " , nr_a2232 ) ; /* Some status report if no card was found */
2005-04-17 02:20:36 +04:00
a2232_init_portstructs ( ) ;
/*
a2232_init_drivers also registers the drivers . Must be here because all boards
have to be detected first .
*/
if ( a2232_init_drivers ( ) ) return - ENODEV ; // maybe we should use a different -Exxx?
request_irq ( IRQ_AMIGA_VERTB , a2232_vbl_inter , 0 , " A2232 serial VBL " , a2232_driver_ID ) ;
return 0 ;
}
static void __exit a2232board_exit ( void )
{
int i ;
for ( i = 0 ; i < nr_a2232 ; i + + ) {
zorro_release_device ( zd_a2232 [ i ] ) ;
}
tty_unregister_driver ( a2232_driver ) ;
put_tty_driver ( a2232_driver ) ;
free_irq ( IRQ_AMIGA_VERTB , a2232_driver_ID ) ;
}
module_init ( a2232board_init ) ;
module_exit ( a2232board_exit ) ;
MODULE_AUTHOR ( " Enver Haase " ) ;
MODULE_DESCRIPTION ( " Amiga A2232 multi-serial board driver " ) ;
MODULE_LICENSE ( " GPL " ) ;