home *** CD-ROM | disk | FTP | other *** search
-
- /*
- * drivers/char/atari_serial.c: Common source file for all Atari serial ports
- *
- * Copyright 1994 Roman Hodek
- * EMail: rnhodek@cip.informatik.uni-erlangen.de (Internet)
- * or: Roman_Hodek@n.maus.de (MausNet, NO mail > 16 KB!)
- *
- * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file README.legal in the main directory of this archive
- * for more details.
- *
- */
-
- /*
- * Notes and Design Goals:
- * -----------------------
- * The PC serial drivers can rely on the fact that all the serial
- * hardware is very similar to program for all ports. Unfortunately,
- * this is not true for the Atari. Here it is nearly the other way
- * round: All ports need different treatment for the low-level stuff.
- *
- * For this reason, I split the serial driver code into a
- * port-independent part (atari_serial.c) and port-specific parts
- * (atari_*.c). The first manages all what can be done without
- * accessing the hardware directly, i.e. interfacing with the
- * high-level tty drivers, wait queues, managing the existing ports
- * and the like. The latter do the actual hardware programming and are
- * accessed by the hardware-independant part by a "switch" structure,
- * that contains pointers to functions for specific tasks. See the
- * comment before the definition of the SERIALSWITCH structure in
- * atari_serial.h for more details.
- *
- * Despite its name, the port-independant code should be usable by
- * other machines than Atari, too, if there are similar circumstances
- * with different serial port hardware. Feel free to use it, but
- * please inform me if you have to do changes to it. I'll try to keep
- * it really device-independant.
- *
- */
-
-
- #include <linux/types.h>
- #include <linux/sched.h>
- #include <linux/timer.h>
- #include <linux/errno.h>
- #include <linux/string.h>
- #include <linux/bootinfo.h>
- #include <linux/tty.h>
- #include <linux/termios.h>
- #include <linux/major.h>
- #include <linux/fcntl.h>
-
- #include <asm/bitops.h>
- #include <asm/segment.h>
-
- #include "atari_serial.h"
- #include "atari_SCC.h"
- #include "atari_MFPser.h"
-
-
-
- struct async_struct rs_table[ NR_PORTS ];
- char rs_event[ (NR_PORTS + 7) / 8 ];
-
-
-
- /***************************** Prototypes *****************************/
-
- static inline void handle_rs_break( struct async_struct *info );
- static void do_softint( void *unused );
- static int startup( struct async_struct * info, int get_irq );
- static void shutdown( struct async_struct * info, int do_free_irq );
- static int get_serial_info( struct async_struct * info, struct
- serial_struct * retinfo );
- static int set_serial_info( struct async_struct * info, struct
- serial_struct * new_info );
- static int get_modem_info( struct async_struct * info, unsigned int *value
- );
- static int set_modem_info( struct async_struct * info, unsigned int cmd,
- unsigned int *value );
- static void send_break( struct async_struct * info, int duration );
- static int rs_ioctl( struct tty_struct *tty, struct file * file, unsigned
- int cmd, unsigned long arg );
- static void rs_set_termios( struct tty_struct *tty, struct termios
- *old_termios );
- static void rs_close( struct tty_struct *tty, struct file * filp );
- static int block_til_ready( struct tty_struct *tty, struct file * filp,
- struct async_struct *info );
-
- /************************* End of Prototypes **************************/
-
-
-
- /*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------ */
-
- void rs_stop( struct tty_struct *tty )
-
- {
- struct async_struct *info;
-
- info = rs_table + DEV_TO_SL(tty->line);
-
- if (info->flags & ASYNC_CLOSING) {
- tty->stopped = 0;
- tty->hw_stopped = 0;
- return;
- }
-
- if (info->sw->enab_tx_int)
- info->sw->enab_tx_int( info, 0 );
- }
-
- void rs_start( struct tty_struct *tty )
-
- {
- struct async_struct *info;
-
- info = rs_table + DEV_TO_SL(tty->line);
-
- if (info->sw->enab_tx_int) {
- info->sw->enab_tx_int( info, 1 );
- }
- }
-
-
- /*
- * This routine is called when we receive a break on a serial line.
- * It is executed out of the software interrupt routine.
- */
-
- static inline void handle_rs_break( struct async_struct *info )
-
- {
- if (info->flags & ASYNC_SAK)
- do_SAK(info->tty);
-
- if (!I_IGNBRK(info->tty) && I_BRKINT(info->tty)) {
- flush_input(info->tty);
- flush_output(info->tty);
- if (info->tty->pgrp > 0)
- kill_pg(info->tty->pgrp, SIGINT, 1 );
- }
- }
-
- /*
- * This routine is used to handle the "bottom half" processing for the
- * serial driver, known also the "software interrupt" processing.
- * This processing is done at the kernel interrupt level, after the
- * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
- * is where time-consuming activities which can not be done in the
- * interrupt driver proper are done; the interrupt driver schedules
- * them using rs_sched_event(), and they get done here.
- */
- static void do_softint( void *unused )
-
- {
- int i;
- struct async_struct *info;
-
- for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
- if (clear_bit(i, rs_event)) {
- if (!info->tty)
- continue;
- if (clear_bit(RS_EVENT_READ_PROCESS, &info->event)) {
- TTY_READ_FLUSH(info->tty);
- }
- if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
- wake_up_interruptible(&info->tty->write_q.proc_list);
- }
- if (clear_bit(RS_EVENT_HANGUP, &info->event)) {
- tty_hangup(info->tty);
- wake_up_interruptible(&info->open_wait);
- info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
- }
- if (clear_bit(RS_EVENT_BREAK, &info->event))
- handle_rs_break(info);
- if (clear_bit(RS_EVENT_OPEN_WAKEUP, &info->event)) {
- wake_up_interruptible(&info->open_wait);
- }
- }
- }
- }
-
- static int startup( struct async_struct * info, int get_irq )
-
- {
- unsigned long flags;
-
- if (info->flags & ASYNC_INITIALIZED)
- return 0;
-
- if (!info->base || !info->type) {
- if (info->tty)
- set_bit(TTY_IO_ERROR, &info->tty->flags);
- return 0;
- }
-
- save_flags(flags); cli();
-
- #ifdef SERIAL_DEBUG_OPEN
- printk("starting up ttys%d (irq %d)...", info->line, info->irq);
- #endif
-
- info->sw->init( info );
-
- if (info->tty)
- clear_bit(TTY_IO_ERROR, &info->tty->flags);
-
- /*
- * Set the speed and other port parameters.
- */
- info->sw->change_speed( info );
-
- info->flags |= ASYNC_INITIALIZED;
- restore_flags(flags);
- return 0;
- }
-
- /*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-
- static void shutdown( struct async_struct * info, int do_free_irq )
-
- {
- unsigned long flags;
-
- if (!(info->flags & ASYNC_INITIALIZED))
- return;
-
- #ifdef SERIAL_DEBUG_OPEN
- printk("Shutting down serial port %d (irq %d)....", info->line,
- info->irq);
- #endif
-
- save_flags(flags); cli(); /* Disable interrupts */
-
- info->sw->deinit( info, info->tty &&
- !(info->tty->termios->c_cflag & HUPCL) );
-
- if (info->tty)
- set_bit(TTY_IO_ERROR, &info->tty->flags);
-
- info->flags &= ~ASYNC_INITIALIZED;
- restore_flags(flags);
- }
-
-
- /*
- * ------------------------------------------------------------
- * rs_write() and friends
- * ------------------------------------------------------------
- */
-
- /*
- * This routine gets called when tty_write has put something into
- * the write_queue.
- */
-
- void rs_write( struct tty_struct * tty )
-
- { struct async_struct *info;
-
- if (!tty || tty->stopped || tty->hw_stopped)
- return;
- info = rs_table + DEV_TO_SL(tty->line);
-
- if (!info || !info->tty || !(info->flags & ASYNC_INITIALIZED))
- return;
-
- cli();
- info->sw->restart( info, rs_get_char_from_queue( info ) );
- if (info->sw->enab_tx_int)
- info->sw->enab_tx_int( info, 1 );
- sti();
- }
-
-
- /*
- * ------------------------------------------------------------
- * rs_throttle()
- *
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled (and that the throttle
- * should be released).
- * ------------------------------------------------------------
- */
-
- void rs_throttle( struct tty_struct * tty, int status )
-
- {
- struct async_struct *info;
- unsigned long flags;
-
- save_flags(flags); cli();
- #if SERIAL_DEBUG_THROTTLE
- printk("throttle tty%d: %d (%d, %d)....\n", DEV_TO_SL(tty->line),
- status, LEFT(&tty->read_q), LEFT(&tty->secondary));
- #endif
-
- switch( status ) {
-
- case TTY_THROTTLE_RQ_FULL:
- info = rs_table + DEV_TO_SL(tty->line);
- if (I_IXOFF(tty))
- info->x_char = STOP_CHAR(tty);
- else if (C_CRTSCTS(tty))
- info->sw->throttle( info, status );
- break;
-
- case TTY_THROTTLE_RQ_AVAIL:
- info = rs_table + DEV_TO_SL(tty->line);
- if (I_IXOFF(tty)) {
- if (info->x_char)
- info->x_char = 0;
- else
- info->x_char = START_CHAR(tty);
- } else if (C_CRTSCTS(tty))
- info->sw->throttle( info, status );
- break;
- }
-
- restore_flags(flags);
- }
-
- /*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
- static int get_serial_info( struct async_struct * info,
- struct serial_struct * retinfo )
-
- {
- struct serial_struct tmp;
-
- if (!retinfo)
- return -EFAULT;
-
- /* Set port-independant data */
- memset(&tmp, 0, sizeof(tmp));
- tmp.type = info->type;
- tmp.line = info->line;
- tmp.port = 0; /* meaningless for non-Intel */
- tmp.irq = 0; /* meaningless for Atari */
- tmp.flags = info->flags;
- tmp.close_delay = info->close_delay;
- tmp.hub6 = 0; /* meaningless for Atari */
-
- /* At least baud_base and costum_divisor set by port-specific
- * function
- */
- info->sw->get_serial_info( info, &tmp );
-
- memcpy_tofs( retinfo, &tmp, sizeof(*retinfo) );
- return 0;
- }
-
-
- static int set_serial_info( struct async_struct * info,
- struct serial_struct * new_info )
-
- {
- struct serial_struct new_serial;
- struct async_struct old_info;
-
- if (!new_info) return( -EFAULT );
- memcpy_fromfs( &new_serial, new_info, sizeof(new_serial) );
- old_info = *info;
-
- /* The fields type, line, port, irq, xmit_fifo_size, baud_base and
- * hub6 of new_info are silently ignored for the Atari, since they
- * are meaningless or cannot be changed, resp. Is this right or
- * should an error be returned?
- */
-
- /* If a new custom divisor is to be set, let it check by the
- * hardware specific code first!
- */
- if (new_serial.custom_divisor != info->custom_divisor) {
- if (info->sw->check_custom_divisor( info, new_serial.custom_divisor ))
- return( -EINVAL );
- }
-
- if (!suser()) {
- if ((new_serial.close_delay != info->close_delay) ||
- ((new_serial.flags & ~ASYNC_USR_MASK) !=
- (info->flags & ~ASYNC_USR_MASK))) return( -EPERM );
-
- info->flags = ((info->flags & ~ASYNC_USR_MASK) |
- (new_serial.flags & ASYNC_USR_MASK));
- info->custom_divisor = new_serial.custom_divisor;
- goto check_and_exit;
- }
-
- /*
- * OK, past this point, all the error checking has been done.
- * At this point, we start making changes.....
- */
-
- info->flags = ((info->flags & ~ASYNC_FLAGS) |
- (new_serial.flags & ASYNC_FLAGS));
- info->custom_divisor = new_serial.custom_divisor;
- info->close_delay = new_serial.close_delay;
-
- check_and_exit:
- if (info->flags & ASYNC_INITIALIZED) {
-
- if (((old_info.flags & ASYNC_SPD_MASK) !=
- (info->flags & ASYNC_SPD_MASK)) ||
- (old_info.custom_divisor != info->custom_divisor))
- info->sw->change_speed( info );
- }
- else
- (void)startup( info, 0 );
-
- return 0;
- }
-
- static int get_modem_info( struct async_struct * info,
- unsigned int *value )
-
- {
- unsigned int result;
-
- result = info->sw->get_modem_info( info );
- put_fs_long( result, (unsigned long *) value );
-
- return 0;
- }
-
-
- static int set_modem_info( struct async_struct * info, unsigned int cmd,
- unsigned int *value )
-
- { unsigned arg = get_fs_long((unsigned long *) value);
- int new_dtr = -1, new_rts = -1;
-
- switch( cmd ) {
-
- case TIOCMBIS:
- if (arg & TIOCM_DTR) new_dtr = 1;
- if (arg & TIOCM_RTS) new_rts = 1;
- break;
-
- case TIOCMBIC:
- if (arg & TIOCM_DTR) new_dtr = 0;
- if (arg & TIOCM_RTS) new_rts = 0;
- break;
-
- case TIOCMSET:
- new_dtr = !!(arg & TIOCM_DTR);
- new_rts = !!(arg & TIOCM_RTS);
- break;
-
- default:
- return( -EINVAL );
- }
-
- return( info->sw->set_modem_info( info, new_dtr, new_rts ) );
- }
-
-
- /*
- * This routine sends a break out the serial port.
- */
-
- static void send_break( struct async_struct * info, int duration )
-
- {
- if (!info->base) return;
-
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + duration;
- cli();
- info->sw->set_break( info, 1 );
- schedule();
- info->sw->set_break( info, 0 );
- sti();
- }
-
- static int rs_ioctl( struct tty_struct *tty, struct file * file,
- unsigned int cmd, unsigned long arg )
-
- {
- int error, line;
- struct async_struct * info;
-
- line = DEV_TO_SL(tty->line);
- if (line < 0 || line >= NR_PORTS)
- return -ENODEV;
- info = rs_table + line;
- if (!info->base) return( -ENODEV );
-
- switch( cmd ) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- if (!arg)
- send_break(info, HZ/4); /* 1/4 second */
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- return 0;
- case TIOCGSOFTCAR:
- error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
- if (error)
- return error;
- put_fs_long(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg);
- return 0;
- case TIOCSSOFTCAR:
- arg = get_fs_long((unsigned long *) arg);
- tty->termios->c_cflag =
- ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0));
- return 0;
- case TIOCMGET:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned int));
- if (error)
- return error;
- return get_modem_info(info, (unsigned int *) arg);
- case TIOCMBIS:
- case TIOCMBIC:
- case TIOCMSET:
- return set_modem_info(info, cmd, (unsigned int *) arg);
- case TIOCGSERIAL:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct serial_struct));
- if (error)
- return error;
- return get_serial_info(info, (struct serial_struct *) arg);
- case TIOCSSERIAL:
- return set_serial_info(info, (struct serial_struct *) arg);
- case TIOCSERCONFIG:
- /* return do_autoconfig(info); */
- return( 0 );
-
- default:
- if (info->sw->ioctl)
- return( info->sw->ioctl( tty, file, info, cmd, arg ) );
- else
- return( -EINVAL );
- }
- return 0;
- }
-
-
- static void rs_set_termios( struct tty_struct *tty,
- struct termios *old_termios )
-
- {
- struct async_struct *info;
-
- if (tty->termios->c_cflag == old_termios->c_cflag)
- return;
-
- info = &rs_table[DEV_TO_SL(tty->line)];
-
- info->sw->change_speed( info );
-
- if ((old_termios->c_cflag & CRTSCTS) &&
- !(tty->termios->c_cflag & CRTSCTS)) {
- tty->hw_stopped = 0;
- rs_write(tty);
- }
-
- if (!(old_termios->c_cflag & CLOCAL) &&
- (tty->termios->c_cflag & CLOCAL))
- wake_up_interruptible(&info->open_wait);
- }
-
-
- /*
- * ------------------------------------------------------------
- * rs_close()
- *
- * This routine is called when the serial port gets closed. First, we
- * wait for the last remaining data to be sent. Then, we unlink its
- * async structure from the interrupt chain if necessary, and we free
- * that IRQ if nothing is left in the chain.
- * ------------------------------------------------------------
- */
-
- static void rs_close( struct tty_struct *tty, struct file * filp )
-
- {
- struct async_struct * info;
- int line;
-
- if (tty_hung_up_p(filp)) return;
-
- line = DEV_TO_SL(tty->line);
- if ((line < 0) || (line >= NR_PORTS)) return;
- info = rs_table + line;
- if (!info->base) return;
-
- #ifdef SERIAL_DEBUG_OPEN
- printk("rs_close ttys%d, count = %d\n", info->line, info->count);
- #endif
-
- if ((tty->count == 1) && (info->count != 1)) {
- /*
- * Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. Info->count should always
- * be one in these conditions. If it's greater than
- * one, we've got real problems, since it means the
- * serial port won't be shutdown.
- */
- printk("rs_close: bad serial port count; tty->count is 1, "
- "info->count is %d\n", info->count);
- info->count = 1;
- }
- if (--info->count < 0) {
- printk("rs_close: bad serial port count for ttys%d: %d\n",
- info->line, info->count);
- info->count = 0;
- }
- if (info->count)
- return;
- info->flags |= ASYNC_CLOSING;
- /*
- * Save the termios structure, since this port may have
- * separate termios for callout and dialin.
- */
- if (info->flags & ASYNC_NORMAL_ACTIVE)
- info->normal_termios = *tty->termios;
- if (info->flags & ASYNC_CALLOUT_ACTIVE)
- info->callout_termios = *tty->termios;
- tty->stopped = 0; /* Force flush to succeed */
- tty->hw_stopped = 0;
- if (info->flags & ASYNC_INITIALIZED) {
- rs_start(tty);
- wait_until_sent(tty, 6000); /* 60 seconds timeout */
- } else
- flush_output(tty);
- flush_input(tty);
-
- shutdown(info, 1);
- clear_bit(line, rs_event);
- info->event = 0;
- info->tty = 0;
- if (info->blocked_open) {
- if (info->close_delay) {
- tty->count++; /* avoid race condition */
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + info->close_delay;
- schedule();
- tty->count--;
- }
- wake_up_interruptible(&info->open_wait);
- }
- info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING);
- wake_up_interruptible(&info->close_wait);
- }
-
-
- /*
- * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-
- void rs_hangup( struct tty_struct *tty )
-
- {
- struct async_struct * info;
- int line;
-
- line = DEV_TO_SL(tty->line);
- if ((line < 0) || (line >= NR_PORTS)) return;
- info = rs_table + line;
- if (!info->base) return;
-
- shutdown(info, 1);
- clear_bit(line, rs_event);
- info->event = 0;
- info->count = 0;
- info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
- info->tty = 0;
- wake_up_interruptible(&info->open_wait);
- }
-
-
- /*
- * ------------------------------------------------------------
- * rs_open() and friends
- * ------------------------------------------------------------
- */
-
- static int block_til_ready( struct tty_struct *tty, struct file * filp,
- struct async_struct *info )
-
- {
- struct wait_queue wait = { current, NULL };
- int retval;
- int do_clocal = C_CLOCAL(tty);
-
- /*
- * If the device is in the middle of being closed, then block
- * until it's done, and then try again.
- */
- if (info->flags & ASYNC_CLOSING) {
- interruptible_sleep_on(&info->close_wait);
- #ifdef SERIAL_DO_RESTART
- if (info->flags & ASYNC_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
- #else
- return -EAGAIN;
- #endif
- }
-
- /*
- * If this is a callout device, then just make sure the normal
- * device isn't being used.
- */
- if (MAJOR(filp->f_rdev) == TTYAUX_MAJOR) {
- if (info->flags & ASYNC_NORMAL_ACTIVE)
- return -EBUSY;
- if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
- (info->flags & ASYNC_SESSION_LOCKOUT) &&
- (info->session != current->session))
- return -EBUSY;
- if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
- (info->flags & ASYNC_PGRP_LOCKOUT) &&
- (info->pgrp != current->pgrp))
- return -EBUSY;
- info->flags |= ASYNC_CALLOUT_ACTIVE;
- return 0;
- }
-
- /*
- * If non-blocking mode is set, then make the check up front
- * and then exit.
- */
- if (filp->f_flags & O_NONBLOCK) {
- if (info->flags & ASYNC_CALLOUT_ACTIVE)
- return -EBUSY;
- info->flags |= ASYNC_NORMAL_ACTIVE;
- return 0;
- }
-
- /*
- * Block waiting for the carrier detect and the line to become
- * free (i.e., not in use by the callout). While we are in
- * this loop, info->count is dropped by one, so that
- * rs_close() knows when to free things. We restore it upon
- * exit, either normal or abnormal.
- */
- retval = 0;
- add_wait_queue(&info->open_wait, &wait);
- #ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready before block: ttys%d, count = %d\n",
- info->line, info->count);
- #endif
- info->count--;
- info->blocked_open++;
- while (1) {
- cli();
- if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
- info->sw->set_modem_info( info, 1, 1 );
- sti();
- current->state = TASK_INTERRUPTIBLE;
- if (tty_hung_up_p(filp) ||
- !(info->flags & ASYNC_INITIALIZED)) {
- #ifdef SERIAL_DO_RESTART
- if (info->flags & ASYNC_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
- #else
- retval = -EAGAIN;
- #endif
- break;
- }
- if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
- !(info->flags & ASYNC_CLOSING) &&
- (do_clocal || (info->sw->get_modem_info( info ) & TIOCM_CAR)))
- break;
- if (current->signal & ~current->blocked) {
- retval = -ERESTARTSYS;
- break;
- }
- #ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready blocking: ttys%d, count = %d\n",
- info->line, info->count);
- #endif
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&info->open_wait, &wait);
- if (!tty_hung_up_p(filp))
- info->count++;
- info->blocked_open--;
- #ifdef SERIAL_DEBUG_OPEN
- printk("block_til_ready after blocking: ttys%d, count = %d\n",
- info->line, info->count);
- #endif
- if (retval)
- return retval;
- info->flags |= ASYNC_NORMAL_ACTIVE;
- return 0;
- }
-
-
- /*
- * This routine is called whenever a serial port is opened.
- */
-
- int rs_open( struct tty_struct *tty, struct file * filp )
-
- {
- struct async_struct *info;
- int retval, line;
-
- line = DEV_TO_SL(tty->line);
- if ((line < 0) || (line >= NR_PORTS))
- return( -ENODEV );
- info = rs_table + line;
- if (!info->base)
- return( -ENODEV );
-
- #ifdef SERIAL_DEBUG_OPEN
- printk("rs_open ttyS%d, count = %d\n", info->line, info->count);
- #endif
- info->count++;
- info->tty = tty;
-
- tty->write = rs_write;
- tty->close = rs_close;
- tty->ioctl = rs_ioctl;
- tty->throttle = rs_throttle;
- tty->set_termios = rs_set_termios;
- tty->stop = rs_stop;
- tty->start = rs_start;
- tty->hangup = rs_hangup;
-
- if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
- if (MAJOR(filp->f_rdev) == TTY_MAJOR)
- *tty->termios = info->normal_termios;
- else
- *tty->termios = info->callout_termios;
- }
-
- /*
- * Start up serial port
- */
- if ((retval = startup(info, 1))) return retval;
-
- retval = block_til_ready(tty, filp, info);
- if (retval) {
- #ifdef SERIAL_DEBUG_OPEN
- printk( "rs_open returning after block_til_ready with %d\n", retval );
- #endif
- return retval;
- }
-
- info->session = current->session;
- info->pgrp = current->pgrp;
-
- #ifdef SERIAL_DEBUG_OPEN
- printk("rs_open ttyS%d successfull...", info->line);
- #endif
- return 0;
- }
-
-
-
- /*
- * Boot-time initialization code: Init the serial ports present on
- * this machine type.
- *
- * Current HW Port to minor/device name mapping:
- *
- * TT | Falcon || minor | name
- * -----------------------+---------------------------++-------+-------
- * SCC B (Modem2) | SCC B (Modem) || 64 | ttyS0
- * SCC A (Serial2/LAN) | SCC A (LAN) || 65 | ttyS1
- * ST-MFP port (Modem1) | -- || 66 | ttyS2
- * TT-MFP port (Serial1) | ST-MFP port (no TOS name) || 67 | ttyS3
- *
- * The background of this mapping is that I wanted to assign the same
- * minors to ports with the same capabilities. Staring with the MFPs
- * would leave a hole in the first position on the Falcon!
- *
- * Should Serial2/LAN on the TT be split into two devices, selecting thus
- * the connector used? Serial2 would be unassigned on the Falcon, and
- * if one is open, opening the other would block.
- *
- */
-
- long rs_init( long kmem_start )
-
- { int i;
- struct async_struct *info;
-
- if (boot_info.machtype != MACH_ATARI) return( kmem_start );
-
- memset( &rs_event, 0, sizeof(rs_event) );
- bh_base[SERIAL_BH].routine = do_softint;
-
- /* Initialize the async_struct's */
- for( i = 0, info = rs_table; i < NR_PORTS; i++, info++ ) {
- info->line = i;
- info->base = NULL;
- info->tty = 0;
- info->type = PORT_UNKNOWN;
- info->custom_divisor = 0;
- info->close_delay = 50;
- info->x_char = 0;
- info->event = 0;
- info->count = 0;
- info->blocked_open = 0;
- memset(&info->callout_termios, 0, sizeof(struct termios));
- memset(&info->normal_termios, 0, sizeof(struct termios));
- info->open_wait = 0;
- info->xmit_wait = 0;
- info->close_wait = 0;
- }
-
- /* Now initialize the ports themselves */
- switch( boot_info.bi_atari.model ) {
-
- case ATARI_TT:
- atari_init_SCC(
- &rs_table[0],
- SCC_NORM, /* type */
- 1, /* Channel B */
- (void *)&tt_mfp.par_dt_reg, 3, 0/* RI */
- );
- atari_init_SCC(
- &rs_table[1],
- SCC_DMA, /* type */
- 0, /* Channel A */
- 0, 0, 0 /* no RI */
- );
- atari_init_MFPser(
- &rs_table[2],
- MFP_CTRL, /* type */
- 0, /* ST-MFP */
- (void *)&mfp.par_dt_reg, 6, 0 /* RI */
- );
- atari_init_MFPser(
- &rs_table[3],
- MFP_BARE, /* type */
- 1, /* TT_MFP */
- 0, 0, 0 /* no RI */
- );
- break;
-
- case ATARI_FALCON:
- atari_init_SCC(
- &rs_table[0],
- SCC_NORM, /* type */
- 1, /* Channel B */
- (void *)&mfp.par_dt_reg, 6, 0 /* RI */
- );
- atari_init_SCC(
- &rs_table[1],
- SCC_NORM, /* type */
- 0, /* Channel A */
- 0, 0, 0 /* no RI */
- );
- atari_init_MFPser(
- &rs_table[3],
- MFP_BARE, /* type */
- 0, /* ST-MFP */
- 0, 0, 0 /* no RI */
- );
- break;
-
- }
-
- return( kmem_start );
- }
-