File: [Development] / linux-2.4-xfs / drivers / net / wan / 8253x / 8253xtty.c (download)
Revision 1.2, Thu Jan 20 13:59:19 2005 UTC (12 years, 8 months ago) by nathans.longdrop.melbourne.sgi.com
Branch: MAIN
CVS Tags: HEAD Changes since 1.1: +2 -8
lines
Merge up to 2.4.29.
Merge of 2.4.x-xfs-melb:linux:21231a by kenmcd.
|
/* -*- linux-c -*- */
/* $Id: 8253xtty.c,v 1.23 2002/02/10 22:17:25 martillo Exp $
* sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
*
* Modified by Francois Wautier 2000 (fw@auroratech.com)
*
* Extended extensively by Joachim Martillo 2001 (Telford002@aol.com)
* to provide synchronous/asynchronous TTY/Callout/character/network device
* capabilities.
*
* Modifications and extensions
* Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.
*
* 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.
*/
/* Standard in kernel modules */
#define DEFINE_VARIABLE
#include <linux/module.h> /* Specifically, a module */
#include <asm/io.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/mm.h>
#include <linux/version.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include "8253xctl.h"
#include "sp502.h"
DECLARE_TASK_QUEUE(tq_8253x_serial); /* this just initializes a list head called */
/* tq_8253x_serial*/
struct tty_driver sab8253x_serial_driver, sab8253x_callout_driver, sync_sab8253x_serial_driver;
int sab8253x_refcount;
/* Trace things on serial device, useful for console debugging: */
#undef SERIAL_LOG_DEVICE
#ifdef SERIAL_LOG_DEVICE
static void dprint_init(int tty);
#endif
static void sab8253x_change_speed(struct sab_port *port);
static struct tty_struct **sab8253x_tableASY = 0; /* make dynamic */
static struct tty_struct **sab8253x_tableCUA = 0; /* make dynamic */
static struct tty_struct **sab8253x_tableSYN = 0; /* make dynamic */
static struct termios **sab8253x_termios = 0 ;
static struct termios **sab8253x_termios_locked = 0;
#ifdef MODULE
#undef XCONFIG_SERIAL_CONSOLE /* leaving out CONFIG_SERIAL_CONSOLE for now */
#endif
#ifdef XCONFIG_SERIAL_CONSOLE /* not really implemented yet */
extern int serial_console;
struct console sab8253x_console;
int sab8253x_console_init(void);
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
char sab8253x_serial_version[16];
static void sab8253x_flush_to_ldisc(void *private_)
{
struct tty_struct *tty = (struct tty_struct *) private_;
unsigned char *cp;
char *fp;
int count;
struct sab_port *port;
struct sk_buff *skb;
if(tty)
{
port = (struct sab_port *)tty->driver_data; /* probably a silly check */
}
else
{
return;
}
if(!port)
{
return;
}
if (test_bit(TTY_DONT_FLIP, &tty->flags))
{
queue_task(&tty->flip.tqueue, &tq_timer);
return;
}
/* note that a hangup may have occurred -- perhaps should check for that */
port->DoingInterrupt = 1;
while(port->sab8253xc_rcvbuflist && (skb_queue_len(port->sab8253xc_rcvbuflist) > 0))
{
skb = skb_dequeue(port->sab8253xc_rcvbuflist);
count = skb->data_len;
cp = skb->data;
fp = skb->data + (count/2);
(*tty->ldisc.receive_buf)(tty, cp, fp, count/2);
dev_kfree_skb_any(skb);
}
port->DoingInterrupt = 0;
}
/* only used asynchronously */
static void inline sab8253x_tec_wait(struct sab_port *port)
{
int count = port->tec_timeout;
while((READB(port, star) & SAB82532_STAR_TEC) && --count)
{
udelay(1);
}
}
void sab8253x_start_tx(struct sab_port *port)
{
unsigned long flags;
register int count;
register int total;
register int offset;
char temporary[32];
register unsigned int slopspace;
register int sendsize;
unsigned int totaltransmit;
unsigned fifospace;
unsigned loadedcount;
struct tty_struct *tty = port->tty;
fifospace = port->xmit_fifo_size;
loadedcount = 0;
if(port->sabnext2.transmit == NULL)
{
return;
}
save_flags(flags);
cli();
while(count = port->sabnext2.transmit->Count, (count & OWNER) == OWN_SAB)
{
count &= ~OWN_SAB; /* OWN_SAB is really 0 but cannot guarantee in the future */
if(port->sabnext2.transmit->HostVaddr)
{
total = (port->sabnext2.transmit->HostVaddr->tail -
port->sabnext2.transmit->HostVaddr->data); /* packet size */
}
else
{
total = 0; /* the data is only in the crc/trailer */
}
if(tty && (tty->stopped || tty->hw_stopped))
{ /* works for frame that only has a trailer (crc) */
port->interrupt_mask1 |= SAB82532_IMR1_XPR;
WRITEB(port, imr1, port->interrupt_mask1);
restore_flags(flags); /* can't send */
return;
}
offset = (total - count); /* offset to data still to send */
port->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
WRITEB(port, imr1, port->interrupt_mask1);
port->all_sent = 0;
if(READB(port,star) & SAB82532_STAR_XFW)
{
if(count <= fifospace)
{
port->xmit_cnt = count;
slopspace = 0;
sendsize = 0;
if(port->sabnext2.transmit->sendcrc)
/* obviously should not happen for async but might use for
priority transmission */
{
slopspace = fifospace - count;
}
if(slopspace)
{
if(count)
{
memcpy(temporary, &port->sabnext2.transmit->HostVaddr->data[offset],
count);
}
sendsize = MIN(slopspace, (4 - port->sabnext2.transmit->crcindex));
/* how many bytes to send */
memcpy(&temporary[count],
&((unsigned char*)(&port->sabnext2.transmit->crc))
[port->sabnext2.transmit->crcindex],
sendsize);
port->sabnext2.transmit->crcindex += sendsize;
if(port->sabnext2.transmit->crcindex >= 4)
{
port->sabnext2.transmit->sendcrc = 0;
}
port->xmit_buf = temporary;
}
else
{
port->xmit_buf = /* set up wrifefifo variables */
&port->sabnext2.transmit->HostVaddr->data[offset];
}
port->xmit_cnt += sendsize;
count = 0;
}
else
{
count -= fifospace;
port->xmit_cnt = fifospace;
port->xmit_buf = /* set up wrifefifo variables */
&port->sabnext2.transmit->HostVaddr->data[offset];
}
port->xmit_tail= 0;
loadedcount = port->xmit_cnt;
(*port->writefifo)(port);
totaltransmit = Sab8253xCountTransmitDescriptors(port);
if((sab8253xt_listsize - totaltransmit) > 2)
{
sab8253x_sched_event(port, SAB8253X_EVENT_WRITE_WAKEUP);
}
if((sab8253xt_listsize - totaltransmit) > (sab8253xt_listsize/2))
{
port->buffergreedy = 0;
}
else
{
port->buffergreedy = 1;
}
port->xmit_buf = NULL; /* this var is used to indicate whether to call kfree */
fifospace -= loadedcount;
if ((count <= 0) && (port->sabnext2.transmit->sendcrc == 0))
{
port->sabnext2.transmit->Count = OWN_DRIVER;
#ifdef FREEININTERRUPT /* treat this routine as if taking place in interrupt */
if(port->sabnext2.transmit->HostVaddr)
{
skb_unlink(port->sabnext2.transmit->HostVaddr);
dev_kfree_skb_any(port->sabnext2.transmit->HostVaddr);
port->sabnext2.transmit->HostVaddr = 0; /* no skb */
}
port->sabnext2.transmit->crcindex = 0; /* no single byte */
#endif
port->sabnext2.transmit = port->sabnext2.transmit->VNext;
if((port->sabnext2.transmit->Count & OWNER) == OWN_SAB)
{
if(fifospace > 0)
{
continue; /* the only place where this code really loops */
}
if(fifospace < 0)
{
printk(KERN_ALERT "sab8253x: bad math in interrupt handler.\n");
}
port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
WRITEB(port, imr1, port->interrupt_mask1);
}
else
{
port->interrupt_mask1 |= SAB82532_IMR1_XPR;
WRITEB(port, imr1, port->interrupt_mask1);
}
sab8253x_cec_wait(port);
/* Issue a Transmit Frame command. */
WRITEB(port, cmdr, SAB82532_CMDR_XF);
/* This could be optimized to load from next skbuff */
/* SAB82532_CMDR_XF is the same as SAB82532_CMDR_XTF */
restore_flags(flags);
return;
}
sab8253x_cec_wait(port);
/* Issue a Transmit Frame command. */
WRITEB(port, cmdr, SAB82532_CMDR_XF); /* same as SAB82532_CMDR_XTF */
port->sabnext2.transmit->Count = (count|OWN_SAB);
}
port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
WRITEB(port, imr1, port->interrupt_mask1);
restore_flags(flags);
return;
}
/* The While loop only exits via return*/
/* we get here by skipping the loop */
port->interrupt_mask1 |= SAB82532_IMR1_XPR;
WRITEB(port, imr1, port->interrupt_mask1);
restore_flags(flags);
return;
}
/*
* ------------------------------------------------------------
* sab8253x_stop() and sab8253x_start()
*
* This routines are called before setting or resetting tty->stopped.
* They enable or disable transmitter interrupts, as necessary.
* ------------------------------------------------------------
*/
static void sab8253x_stop(struct tty_struct *tty)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
unsigned long flags;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_stop"))
{
return;
}
save_flags(flags);
cli(); /* maybe should turn off ALLS as well
but the stop flags are checked
so ALLS is probably harmless
and I have seen too much evil
associated with that interrupt*/
port->interrupt_mask1 |= SAB82532_IMR1_XPR;
WRITEB(port, imr1, port->interrupt_mask1);
restore_flags(flags);
}
static void sab8253x_start(struct tty_struct *tty)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_start"))
{
return;
}
sab8253x_start_tx(port);
}
/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver.
*/
/* no obvious changes for sync tty */
static void sab8253x_receive_chars(struct sab_port *port,
union sab8253x_irq_status *stat)
{
struct tty_struct *tty = port->tty;
unsigned char buf[32];
unsigned char reordered[32];
unsigned char status;
int free_fifo = 0;
int i, count = 0;
struct sk_buff *skb;
/* Read number of BYTES (Character + Status) available. */
if (stat->images[ISR0_IDX] & SAB82532_ISR0_RPF)
{
count = port->recv_fifo_size;
free_fifo++;
}
if (stat->images[ISR0_IDX] & SAB82532_ISR0_TCD)
{
count = READB(port,rbcl) & (port->recv_fifo_size - 1);
free_fifo++;
}
/* Issue a FIFO read command in case we where idle. */
if (stat->sreg.isr0 & SAB82532_ISR0_TIME)
{
sab8253x_cec_wait(port);
WRITEB(port, cmdr, SAB82532_CMDR_RFRD);
}
if (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO)
{ /* FIFO overflow */
free_fifo++;
}
/* Read the FIFO. */
(*port->readfifo)(port, buf, count);
/* Issue Receive Message Complete command. */
if (free_fifo)
{
sab8253x_cec_wait(port);
WRITEB(port, cmdr, SAB82532_CMDR_RMC);
}
#ifdef CONSOLE_SUPPORT
if (port->is_console)
{
wake_up(&keypress_wait);
}
#endif
if (!tty)
{
return;
}
if(!count)
{
return;
}
for(i = 0; i < count; i += 2)
{
reordered[i/2] = buf[i];
status = buf[i+1];
if (status & SAB82532_RSTAT_PE)
{
status = TTY_PARITY;
port->icount.parity++;
}
else if (status & SAB82532_RSTAT_FE)
{
status = TTY_FRAME;
port->icount.frame++;
}
else
{
status = TTY_NORMAL;
}
reordered[(count+i)/2] = status;
}
if(port->active2.receive == NULL)
{
return;
}
memcpy(port->active2.receive->HostVaddr->tail, reordered, count);
port->active2.receive->HostVaddr->tail += count;
port->active2.receive->HostVaddr->data_len = count;
port->active2.receive->HostVaddr->len = count;
if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL) /* use dev_alloc_skb because at int
there is header space but so what*/
{
port->icount.buf_overrun++;
port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; /* clear the buffer */
port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
port->active2.receive->HostVaddr->data_len = 0;
port->active2.receive->HostVaddr->len = 0;
}
else
{
skb_unlink(port->active2.receive->HostVaddr);
skb_queue_tail(port->sab8253xc_rcvbuflist, port->active2.receive->HostVaddr);
skb_queue_head(port->sab8253xbuflist, skb);
port->active2.receive->HostVaddr = skb;
port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
}
queue_task(&tty->flip.tqueue, &tq_timer);
}
static void sab8253x_transmit_chars(struct sab_port *port,
union sab8253x_irq_status *stat)
{
if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) /* got an all sent int? */
{
port->interrupt_mask1 |= SAB82532_IMR1_ALLS;
WRITEB(port, imr1, port->interrupt_mask1);
port->all_sent = 1; /* not much else to do */
} /* a very weird chip -- this int only indicates this int */
sab8253x_start_tx(port);
}
static void sab8253x_check_status(struct sab_port *port,
union sab8253x_irq_status *stat)
{
struct tty_struct *tty = port->tty;
int modem_change = 0;
mctlsig_t *sig;
struct sk_buff *skb;
if (!tty)
{
return;
}
if(port->active2.receive == NULL)
{
goto check_modem;
}
if (stat->images[ISR1_IDX] & SAB82532_ISR1_BRK)
{
#ifdef XCONFIG_SERIAL_CONSOLE
if (port->is_console)
{
batten_down_hatches(info); /* need to add this function */
return;
}
#endif
port->active2.receive->HostVaddr->tail[0] = 0;
port->active2.receive->HostVaddr->tail[1] = TTY_PARITY;
port->active2.receive->HostVaddr->tail += 2;
port->active2.receive->HostVaddr->data_len = 2;
port->active2.receive->HostVaddr->len = 2;
if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL)
{
port->icount.buf_overrun++;
port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data;
/* clear the buffer */
port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
port->active2.receive->HostVaddr->data_len = 0;
port->active2.receive->HostVaddr->len = 0;
}
else
{
skb_unlink(port->active2.receive->HostVaddr);
skb_queue_tail(port->sab8253xc_rcvbuflist, port->active2.receive->HostVaddr);
skb_queue_head(port->sab8253xbuflist, skb);
port->active2.receive->HostVaddr = skb;
port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
}
queue_task(&tty->flip.tqueue, &tq_timer);
port->icount.brk++;
}
if (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO)
{
port->active2.receive->HostVaddr->tail[0] = 0;
port->active2.receive->HostVaddr->tail[1] = TTY_PARITY;
port->active2.receive->HostVaddr->tail += 2;
port->active2.receive->HostVaddr->data_len = 2;
port->active2.receive->HostVaddr->len = 2;
if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL)
{
port->icount.buf_overrun++;
port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data;
/* clear the buffer */
port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
port->active2.receive->HostVaddr->data_len = 0;
port->active2.receive->HostVaddr->len = 0;
}
else
{
skb_unlink(port->active2.receive->HostVaddr);
skb_queue_tail(port->sab8253xc_rcvbuflist, port->active2.receive->HostVaddr);
skb_queue_head(port->sab8253xbuflist, skb);
port->active2.receive->HostVaddr = skb;
port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
}
queue_task(&tty->flip.tqueue, &tq_timer);
port->icount.overrun++;
}
check_modem:
/* Checking DCD */
sig = &port->dcd;
if (stat->images[sig->irq] & sig->irqmask)
{
sig->val = ISON(port,dcd);
port->icount.dcd++;
modem_change++;
}
/* Checking CTS */
sig = &port->cts;
if (stat->images[sig->irq] & sig->irqmask)
{
sig->val = ISON(port,cts);
port->icount.cts++;
modem_change++;
}
/* Checking DSR */
sig = &port->dsr;
if (stat->images[sig->irq] & sig->irqmask)
{
sig->val = ISON(port,dsr);
port->icount.dsr++;
modem_change++;
}
if (modem_change)
{
wake_up_interruptible(&port->delta_msr_wait); /* incase kernel proc level was waiting on modem change */
}
sig = &port->dcd;
if ((port->flags & FLAG8253X_CHECK_CD) &&
(stat->images[sig->irq] & sig->irqmask))
{
if (sig->val)
{
wake_up_interruptible(&port->open_wait); /* in case waiting in block_til_ready */
}
else if (!((port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
(port->flags & FLAG8253X_CALLOUT_NOHUP)))
{
MOD_INC_USE_COUNT; /* in case a close is already in progress
don't want structures to vanish during
late processing of hangup */
if (schedule_task(&port->tqueue_hangup) == 0)
{
MOD_DEC_USE_COUNT; /* task schedule failed */
}
}
}
sig = &port->cts;
if (port->flags & FLAG8253X_CTS_FLOW)
{
if (port->tty->hw_stopped)
{
if (sig->val)
{
port->tty->hw_stopped = 0;
sab8253x_sched_event(port, SAB8253X_EVENT_WRITE_WAKEUP);
sab8253x_start_tx(port);
}
}
else
{
if (!(sig->val))
{
port->tty->hw_stopped = 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
* sab8253x_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 sab8253x_sched_event(), and they get done here.
*/
/* The following routine is installed */
/* in the bottom half -- just search */
/* for the init_bh() call */
/* The logic: sab8253x_sched_event() */
/* enqueues the tqueue port entry on */
/* the tq_8253x_serial task list -- */
/* whenever the bottom half is run */
/* sab8253x_do_softint is invoked for */
/* every port that has invoked the bottom */
/* half via sab8253x_sched_event(). */
/* currently only a write wakeevent */
/* wakeup is scheduled -- to tell the */
/* tty driver to send more chars */
/* down to the serial driver.*/
static void sab8253x_do_serial_bh(void)
{
run_task_queue(&tq_8253x_serial);
}
/* I believe the reason for the */
/* bottom half processing below is */
/* the length of time needed to transfer */
/* characters to the TTY driver. */
static void sab8253x_do_softint(void *private_)
{
struct sab_port *port = (struct sab_port *)private_;
struct tty_struct *tty;
tty = port->tty;
if (!tty)
{
return;
}
port->DoingInterrupt = 1;
if (test_and_clear_bit(SAB8253X_EVENT_WRITE_WAKEUP, &port->event))
{
tty_wakeup(tty);
}
port->DoingInterrupt = 0;
}
/*
* This routine is called from the scheduler tqueue when the interrupt
* routine has signalled that a hangup has occurred. The path of
* hangup processing is:
*
* serial interrupt routine -> (scheduler tqueue) ->
* do_serial_hangup() -> tty->hangup() -> sab8253x_hangup()
*
*/
/* This logic takes place at kernel */
/* process context through the scheduler*/
/* schedule_task(tqueue_hangup) */
/* takes place in the interrupt handler*/
static void sab8253x_do_serial_hangup(void *private_)
{
struct sab_port *port = (struct sab_port *) private_;
struct tty_struct *tty;
tty = port->tty;
if (tty)
{
tty_hangup(tty);
}
MOD_DEC_USE_COUNT; /* in case busy waiting to unload module */
}
static void
sab8253x_init_line(struct sab_port *port)
{
unsigned char stat;
if(port->chip->c_cim)
{
if(port->chip->c_cim->ci_type == CIM_SP502)
{
aura_sp502_program(port, SP502_OFF_MODE);
}
}
/*
* Wait for any commands or immediate characters
*/
sab8253x_cec_wait(port);
sab8253x_tec_wait(port);
/*
* Clear the FIFO buffers.
*/
WRITEB(port, cmdr, SAB82532_CMDR_RRES);
sab8253x_cec_wait(port);
WRITEB(port, cmdr, SAB82532_CMDR_XRES);
/*
* Clear the interrupt registers.
*/
stat = READB(port, isr0);
stat = READB(port, isr1);
/*
* Now, initialize the UART
*/
WRITEB(port, ccr0, 0); /* power-down */
WRITEB(port, ccr0,
SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ | SAB82532_CCR0_SM_ASYNC);
WRITEB(port, ccr1,
SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7);
WRITEB(port, ccr2,
SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL | SAB82532_CCR2_TOE);
WRITEB(port, ccr3, 0);
WRITEB(port, ccr4,
SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG);
WRITEB(port, mode,
SAB82532_MODE_RTS | SAB82532_MODE_FCTS | SAB82532_MODE_RAC);
WRITEB(port, rfc,
SAB82532_RFC_DPS | SAB82532_RFC_RFDF);
switch (port->recv_fifo_size)
{
case 1:
SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_1);
break;
case 4:
SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_4);
break;
case 16:
SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_16);
break;
default:
port->recv_fifo_size = 32;
case 32:
SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_32);
break;
}
/* power-up */
SET_REG_BIT(port, ccr0, SAB82532_CCR0_PU);
if(port->chip->c_cim)
{
if(port->chip->c_cim->ci_type == CIM_SP502)
{
aura_sp502_program(port, port->sigmode);
}
}
}
static int sab8253x_startup(struct sab_port *port)
{
unsigned long flags;
int retval = 0;
save_flags(flags); cli();
if (port->flags & FLAG8253X_INITIALIZED)
{
goto errout;
}
port->msgbufindex = 0;
port->xmit_buf = NULL;
port->buffergreedy = 0;
if (!port->regs)
{
if (port->tty)
{
set_bit(TTY_IO_ERROR, &port->tty->flags);
}
retval = -ENODEV;
goto errout;
}
/*
* Initialize the Hardware
*/
sab8253x_init_line(port);
if (port->tty->termios->c_cflag & CBAUD)
{
/* Activate RTS */
RAISE(port,rts);
/* Activate DTR */
RAISE(port,dtr);
}
/*
* Initialize the modem signals values
*/
port->dcd.val=ISON(port,dcd);
port->cts.val=ISON(port,cts);
port->dsr.val=ISON(port,dsr);
/*
* Finally, enable interrupts
*/
port->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
SAB82532_IMR0_PLLA;
WRITEB(port, imr0, port->interrupt_mask0);
port->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_XOFF |
SAB82532_IMR1_TIN | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR;
WRITEB(port, imr1, port->interrupt_mask1);
port->all_sent = 1;
if (port->tty)
{
clear_bit(TTY_IO_ERROR, &port->tty->flags);
}
port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
/*
* and set the speed of the serial port
*/
sab8253x_change_speed(port);
port->flags |= FLAG8253X_INITIALIZED;
port->receive_chars = sab8253x_receive_chars;
port->transmit_chars = sab8253x_transmit_chars;
port->check_status = sab8253x_check_status;
port->receive_test = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF);
port->transmit_test = (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR);
port->check_status_test = SAB82532_ISR1_BRK;
restore_flags(flags);
return 0;
errout:
restore_flags(flags);
return retval;
}
/*
* 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 sab8253x_shutdown(struct sab_port *port)
{
unsigned long flags;
if (!(port->flags & FLAG8253X_INITIALIZED))
{
return;
}
save_flags(flags);
cli(); /* Disable interrupts */
/*
* clear delta_msr_wait queue to avoid mem leaks: we may free the irq
* here so the queue might never be waken up
*/
wake_up_interruptible(&port->delta_msr_wait); /* shutting down port modem status is pointless */
if (port->xmit_buf)
{
port->xmit_buf = NULL;
}
#ifdef XCONFIG_SERIAL_CONSOLE
if (port->is_console)
{
port->interrupt_mask0 =
SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
/*SAB82532_IMR0_TIME |*/
SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
WRITEB(port,imr0,port->interrupt_mask0);
port->interrupt_mask1 =
SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR;
WRITEB(port,imr1,port->interrupt_mask1);
if (port->tty)
{
set_bit(TTY_IO_ERROR, &port->tty->flags);
}
port->flags &= ~FLAG8253X_INITIALIZED;
restore_flags(flags);
return;
}
#endif
/* Disable Interrupts */
port->interrupt_mask0 = 0xff;
WRITEB(port, imr0, port->interrupt_mask0);
port->interrupt_mask1 = 0xff;
WRITEB(port, imr1, port->interrupt_mask1);
if (!port->tty || (port->tty->termios->c_cflag & HUPCL))
{
LOWER(port,rts);
LOWER(port,dtr);
}
/* Disable break condition */
CLEAR_REG_BIT(port,dafo,SAB82532_DAFO_XBRK);
/* Disable Receiver */
CLEAR_REG_BIT(port,mode,SAB82532_MODE_RAC);
/* Power Down */
CLEAR_REG_BIT(port,ccr0,SAB82532_CCR0_PU);
if (port->tty)
{
set_bit(TTY_IO_ERROR, &port->tty->flags);
}
port->flags &= ~FLAG8253X_INITIALIZED;
restore_flags(flags);
}
/*
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
*/
static void sab8253x_change_speed(struct sab_port *port)
{
unsigned long flags,baud;
tcflag_t cflag;
u8 dafo,ccr2=0,ccr4=0,ebrg=0,mode;
int i, bits;
#ifdef DEBUGGING
printk("Change speed! ");
#endif
if (!port->tty || !port->tty->termios)
{
#ifdef DEBUGGING
printk("NOT!\n");
#endif
return;
}
#ifdef DEBUGGING
printk(" for real.\n");
#endif
cflag = port->tty->termios->c_cflag;
/* Byte size and parity */
switch (cflag & CSIZE)
{
case CS5:
dafo = SAB82532_DAFO_CHL5;
bits = 7;
break;
case CS6:
dafo = SAB82532_DAFO_CHL6;
bits = 8;
break;
case CS7:
dafo = SAB82532_DAFO_CHL7;
bits = 9;
break;
default:
case CS8:
dafo = SAB82532_DAFO_CHL8;
bits = 10;
break;
}
if (cflag & CSTOPB)
{
dafo |= SAB82532_DAFO_STOP;
bits++;
}
if (cflag & PARENB)
{
dafo |= SAB82532_DAFO_PARE;
bits++;
}
if (cflag & PARODD)
{
#ifdef CMSPAR
if (cflag & CMSPAR)
dafo |= SAB82532_DAFO_PAR_MARK;
else
#endif
dafo |= SAB82532_DAFO_PAR_ODD;
}
else
{
#ifdef CMSPAR
if (cflag & CMSPAR)
dafo |= SAB82532_DAFO_PAR_SPACE;
else
#endif
dafo |= SAB82532_DAFO_PAR_EVEN;
}
/* Determine EBRG values based on the "encoded"baud rate */
i = cflag & CBAUD;
switch(i)
{
case B0:
baud=0;
break;
case B50:
baud=100;
break;
case B75:
baud=150;
break;
case B110:
baud=220;
break;
case B134:
baud=269;
break;
case B150:
baud=300;
break;
case B200:
baud=400;
break;
case B300:
baud=600;
break;
case B600:
baud=1200;
break;
case B1200:
baud=2400;
break;
case B1800:
baud=3600;
break;
case B2400:
baud=4800;
break;
case B4800:
baud=9600;
break;
case B9600:
baud=19200;
break;
case B19200:
baud=38400;
break;
case B38400:
if(port->custspeed)
{
baud=port->custspeed<<1;
}
else
{
baud=76800;
}
break;
case B57600:
baud=115200;
break;
#ifdef SKIPTHIS
case B76800:
baud=153600;
break;
case B153600:
baud=307200;
break;
#endif
case B230400:
baud=460800;
break;
case B460800:
baud=921600;
break;
case B115200:
default:
baud=230400;
break;
}
if(!sab8253x_baud(port,baud,&ebrg,&ccr2,&ccr4,&(port->baud)))
{
printk("Aurora Warning. baudrate %ld could not be set! Using 115200",baud);
baud=230400;
sab8253x_baud(port,baud,&ebrg,&ccr2,&ccr4,&(port->baud));
}
if (port->baud)
port->timeout = (port->xmit_fifo_size * HZ * bits) / port->baud;
else
port->timeout = 0;
port->timeout += HZ / 50; /* Add .02 seconds of slop */
/* CTS flow control flags */
if (cflag & CRTSCTS)
port->flags |= FLAG8253X_CTS_FLOW;
else
port->flags &= ~(FLAG8253X_CTS_FLOW);
if (cflag & CLOCAL)
port->flags &= ~(FLAG8253X_CHECK_CD);
else
port->flags |= FLAG8253X_CHECK_CD;
if (port->tty)
port->tty->hw_stopped = 0;
/*
* Set up parity check flag
* XXX: not implemented, yet.
*/
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
/*
* Characters to ignore
* XXX: not implemented, yet.
*/
/*
* !!! ignore all characters if CREAD is not set
* XXX: not implemented, yet.
*/
if ((cflag & CREAD) == 0)
port->ignore_status_mask |= SAB82532_ISR0_RPF |
/* SAB82532_ISR0_TIME |*/
SAB82532_ISR0_TCD ;
save_flags(flags);
cli();
sab8253x_cec_wait(port);
sab8253x_tec_wait(port);
WRITEB(port,dafo,dafo);
WRITEB(port,bgr,ebrg);
ccr2 |= READB(port,ccr2) & ~(0xc0);
WRITEB(port,ccr2,ccr2);
ccr4 |= READB(port,ccr4) & ~(SAB82532_CCR4_EBRG);
WRITEB(port,ccr4,ccr4);
if (port->flags & FLAG8253X_CTS_FLOW)
{
mode = READB(port,mode) & ~(SAB82532_MODE_RTS);
mode |= SAB82532_MODE_FRTS;
mode &= ~(SAB82532_MODE_FCTS);
}
else
{
mode = READB(port,mode) & ~(SAB82532_MODE_FRTS);
mode |= SAB82532_MODE_RTS;
mode |= SAB82532_MODE_FCTS;
}
WRITEB(port,mode,mode);
mode |= SAB82532_MODE_RAC;
WRITEB(port,mode,mode);
restore_flags(flags);
}
static void sab8253x_flush_chars(struct tty_struct *tty)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_flush_chars"))
{
return;
}
if ((Sab8253xCountTransmit(port) <= 0) || tty->stopped || tty->hw_stopped)
{
return;
}
sab8253x_start_tx(port);
}
static int sab8253x_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
struct sk_buff *skb = NULL;
int truelength = 0;
int do_queue = 1;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_write"))
{
return 0;
}
if(count == 0)
{
return 0;
}
if(port->active2.transmit == NULL)
{
return 0;
}
if((port->active2.transmit->Count & OWNER) == OWN_SAB)
{
sab8253x_start_tx(port);
return 0;
}
#ifndef FREEININTERRUPT
skb = port->active2.transmit->HostVaddr; /* current slot value */
if(port->buffergreedy == 0) /* are we avoiding buffer free's */
{ /* no */
if((skb != NULL) || /* not OWN_SAB from above */
(port->active2.transmit->crcindex != 0))
{
register RING_DESCRIPTOR *freeme;
freeme = port->active2.transmit;
do
{
if((freeme->crcindex == 0) && (freeme->HostVaddr == NULL))
{
break;
}
if(freeme->HostVaddr)
{
skb_unlink((struct sk_buff*)freeme->HostVaddr);
dev_kfree_skb_any((struct sk_buff*)freeme->HostVaddr);
freeme->HostVaddr = NULL;
}
freeme->sendcrc = 0;
freeme->crcindex = 0;
freeme = (RING_DESCRIPTOR*) freeme->VNext;
}
while((freeme->Count & OWNER) != OWN_SAB);
}
skb = NULL; /* buffer was freed */
}
if(skb != NULL) /* potentially useful */
{
truelength = (skb->end - skb->head);
if(truelength >= count)
{
skb->data = skb->head; /* this buffer is already queued */
skb->tail = skb->head;
do_queue = 0;
}
else
{
skb_unlink(skb);
dev_kfree_skb_any(skb);
skb = NULL;
port->active2.transmit->HostVaddr = NULL;
}
}
/* in all cases the following is allowed */
port->active2.transmit->sendcrc = 0;
port->active2.transmit->crcindex = 0;
#endif
if(skb == NULL)
{
if(port->DoingInterrupt)
{
skb = alloc_skb(count, GFP_ATOMIC);
}
else
{
skb = alloc_skb(count, GFP_KERNEL);
}
}
if(skb == NULL)
{
printk(KERN_ALERT "sab8253xt: no skbuffs available.\n");
return 0;
}
if(from_user)
{
copy_from_user(skb->data, buf, count);
}
else
{
memcpy(skb->data, buf, count);
}
skb->tail = (skb->data + count);
skb->data_len = count;
skb->len = count;
if(do_queue)
{
skb_queue_head(port->sab8253xbuflist, skb);
}
port->active2.transmit->HostVaddr = skb;
port->active2.transmit->sendcrc = 0;
port->active2.transmit->crcindex = 0;
port->active2.transmit->Count = (OWN_SAB|count);
port->active2.transmit = port->active2.transmit->VNext;
sab8253x_start_tx(port);
return count;
}
static int sab8253x_write_room(struct tty_struct *tty)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
if(sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_write_room"))
{
return 0;
}
if(port->active2.transmit == NULL)
{
return 0;
}
if((port->active2.transmit->Count & OWNER) == OWN_SAB)
{
return 0;
}
return ((sab8253xt_rbufsize) * /* really should not send buffs bigger than 32 I guess */
(sab8253xt_listsize -
Sab8253xCountTransmitDescriptors(port)));
}
static int sab8253x_chars_in_buffer(struct tty_struct *tty)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_chars_in_bufferS"))
{
return 0;
}
return Sab8253xCountTransmit(port);
}
/*
* This function is used to send a high-priority XON/XOFF character to
* the device
*/
static void sab8253x_send_xchar(struct tty_struct *tty, char ch)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
unsigned long flags;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_send_xchar"))
{
return;
}
save_flags(flags);
cli();
sab8253x_tec_wait(port);
WRITEB(port, tic, ch);
restore_flags(flags);
}
/*
* ------------------------------------------------------------
* sab8253x_throttle()
*
* This routine is called by the upper-layer tty layer to signal that
* incoming characters should be throttled.
* ------------------------------------------------------------
*/
static void sab8253x_throttle(struct tty_struct * tty)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_throttle"))
{
return;
}
if (I_IXOFF(tty))
{
sab8253x_send_xchar(tty, STOP_CHAR(tty));
}
}
static void sab8253x_unthrottle(struct tty_struct * tty)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_unthrottle"))
{
return;
}
if (I_IXOFF(tty))
{
if (port->x_char)
{
port->x_char = 0;
}
else
{
sab8253x_send_xchar(tty, START_CHAR(tty));
}
}
}
/*
* ------------------------------------------------------------
* sab8253x_ioctl() and friends
* ------------------------------------------------------------
*/
static int sab8253x_get_serial_info(struct sab_port *port,
struct serial_struct *retinfo)
{
struct serial_struct tmp;
if (!retinfo)
{
return -EFAULT;
}
memset(&tmp, 0, sizeof(tmp));
tmp.type = port->type;
tmp.line = port->line;
tmp.port = (unsigned long)port->regs;
tmp.irq = port->irq;
tmp.flags = port->flags;
tmp.xmit_fifo_size = port->xmit_fifo_size;
tmp.baud_base = 0;
tmp.close_delay = port->close_delay;
tmp.closing_wait = port->closing_wait;
tmp.custom_divisor = port->custom_divisor;
tmp.hub6 = 0;
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
{
return -EFAULT;
}
return 0;
}
static int sab8253x_set_serial_info(struct sab_port *port,
struct serial_struct *new_info)
{
return 0;
}
/*
* get_lsr_info - get line status register info
*
* Purpose: Let user call ioctl() to get info when the UART physically
* is emptied. On bus types like RS485, the transmitter must
* release the bus after transmitting. This must be done when
* the transmit shift register is empty, not be done when the
* transmit holding register is empty. This functionality
* allows an RS485 driver to be written in user space.
*/
static int sab8253x_get_lsr_info(struct sab_port * port, unsigned int *value)
{
unsigned int result;
result = (((Sab8253xCountTransmit(port) <= 0) && port->all_sent) ? TIOCSER_TEMT : 0);
return put_user(result, value);
}
static int sab8253x_get_modem_info(struct sab_port * port, unsigned int *value)
{
unsigned int result;
/* Using the cached values !! After all when changed int occurs
and the cache is updated */
result=
((port->dtr.val) ? TIOCM_DTR : 0)
| ((port->rts.val) ? TIOCM_RTS : 0)
| ((port->cts.val) ? TIOCM_CTS : 0)
| ((port->dsr.val) ? TIOCM_DSR : 0)
| ((port->dcd.val) ? TIOCM_CAR : 0);
return put_user(result,value);
}
static int sab8253x_set_modem_info(struct sab_port * port, unsigned int cmd,
unsigned int *value)
{
int error;
unsigned int arg;
unsigned long flags;
error = get_user(arg, value);
if (error)
{
return error;
}
save_flags(flags);
cli();
switch (cmd)
{
case TIOCMBIS:
if (arg & TIOCM_RTS)
{
RAISE(port, rts);
}
if (arg & TIOCM_DTR)
{
RAISE(port, dtr);
}
break;
case TIOCMBIC:
if (arg & TIOCM_RTS)
{
LOWER(port,rts);
}
if (arg & TIOCM_DTR)
{
LOWER(port,dtr);
}
break;
case TIOCMSET:
if (arg & TIOCM_RTS)
{
RAISE(port, rts);
}
else
{
LOWER(port,rts);
}
if (arg & TIOCM_DTR)
{
RAISE(port, dtr);
}
else
{
LOWER(port,dtr);
}
break;
default:
restore_flags(flags);
return -EINVAL;
}
restore_flags(flags);
return 0;
}
/*
* This routine sends a break character out the serial port.
*/
static void sab8253x_break(struct tty_struct *tty, int break_state)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
unsigned long flags;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_break"))
{
return;
}
if (!port->regs)
{
return;
}
save_flags(flags);
cli();
if (break_state == -1)
{
SET_REG_BIT(port,dafo,SAB82532_DAFO_XBRK);
}
else
{
CLEAR_REG_BIT(port,dafo,SAB82532_DAFO_XBRK);
}
restore_flags(flags);
}
static int sab8253x_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
int error;
unsigned int wordindex;
unsigned short *wordptr;
struct sab_port *port = (struct sab_port *)tty->driver_data;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */
SAB_BOARD *bptr;
unsigned long flags;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_ioctl"))
{
return -ENODEV;
}
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
(cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT))
{
if (tty->flags & (1 << TTY_IO_ERROR))
{
return -EIO;
}
}
switch (cmd)
{
case ATIS_IOCSPARAMS:
copy_from_user(&port->ccontrol, (struct channelcontrol*)arg , sizeof(struct channelcontrol));
break;
case ATIS_IOCGPARAMS:
copy_to_user((struct channelcontrol*) arg, &port->ccontrol, sizeof(struct channelcontrol));
break;
case ATIS_IOCSSPEED:
copy_from_user(&port->custspeed, (unsigned long*)arg , sizeof(unsigned long));
break;
case ATIS_IOCGSPEED:
copy_to_user((unsigned long*) arg, &port->custspeed, sizeof(unsigned long));
break;
case ATIS_IOCSSEP9050:
bptr = port->board;
if(bptr->b_type == BD_WANMCS)
{
return -EINVAL;
}
copy_from_user((unsigned char*) bptr->b_eprom, (unsigned char*) arg , sizeof(struct sep9050));
wordptr = (unsigned short*) bptr->b_eprom;
plx9050_eprom_cmd(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl,
NM93_WENCMD, NM93_WENADDR, 0);
for(wordindex = 0; wordindex < EPROM9050_SIZE; ++wordindex)
{
plx9050_eprom_cmd(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl,
NM93_WRITECMD,
wordindex, wordptr[wordindex]);
}
plx9050_eprom_cmd(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl,
NM93_WDSCMD, NM93_WDSADDR, 0);
break;
case ATIS_IOCGSEP9050:
bptr = port->board;
if(bptr->b_type == BD_WANMCS)
{
return -EINVAL;
}
if (!plx9050_eprom_read(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl,
(unsigned short*) bptr->b_eprom,
(unsigned char) 0, EPROM9050_SIZE))
{
printk(KERN_ALERT "auraXX20n: Could not read serial eprom.\n");
return -EIO;
}
copy_to_user((unsigned char*) arg, (unsigned char*) bptr->b_eprom, sizeof(struct sep9050));
break;
case TIOCGSOFTCAR:
return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
case TIOCSSOFTCAR:
error = get_user(arg, (unsigned int *) arg);
if (error)
{
return error;
}
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
return 0;
case TIOCMGET:
return sab8253x_get_modem_info(port, (unsigned int *) arg);
case TIOCMBIS:
case TIOCMBIC:
case TIOCMSET:
return sab8253x_set_modem_info(port, cmd, (unsigned int *) arg);
case TIOCGSERIAL:
return sab8253x_get_serial_info(port,
(struct serial_struct *) arg);
case TIOCSSERIAL:
return sab8253x_set_serial_info(port,
(struct serial_struct *) arg);
case TIOCSERGETLSR: /* Get line status register */
return sab8253x_get_lsr_info(port, (unsigned int *) arg);
case TIOCSERGSTRUCT:
if (copy_to_user((struct sab_port *) arg,
port, sizeof(struct sab_port)))
return -EFAULT;
return 0;
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was
*/
case TIOCMIWAIT:
save_flags(flags);
cli();
/* note the counters on entry */
cprev = port->icount;
restore_flags(flags);
while (1)
{
interruptible_sleep_on(&port->delta_msr_wait); /* waits for a modem signal change */
/* see if a signal did it */
if (signal_pending(current))
{
return -ERESTARTSYS;
}
save_flags(flags);
cli();
cnow = port->icount; /* atomic copy */
restore_flags(flags);
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
{
return -EIO; /* no change => error */
}
if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
{
return 0;
}
}
cprev = cnow;
}
/* NOTREACHED */
break;
/*
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
* Return: write counters to the user passed counter struct
* NB: both 1->0 and 0->1 transitions are counted except for
* RI where only 0->1 is counted.
*/
case TIOCGICOUNT:
save_flags(flags);
cli();
cnow = port->icount;
restore_flags(flags);
p_cuser = (struct serial_icounter_struct *) arg;
error = put_user(cnow.cts, &p_cuser->cts);
if (error)
{
return error;
}
error = put_user(cnow.dsr, &p_cuser->dsr);
if (error)
{
return error;
}
error = put_user(cnow.rng, &p_cuser->rng);
if (error)
{
return error;
}
error = put_user(cnow.dcd, &p_cuser->dcd);
if (error)
{
return error;
}
return 0;
case ATIS_IOCSSIGMODE:
if(port->chip->c_cim)
{
if(port->chip->c_cim->ci_type == CIM_SP502)
{
copy_from_user(&port->sigmode, (unsigned int*)arg , sizeof(unsigned int));
return 0;
}
}
return -EINVAL;
case ATIS_IOCGSIGMODE:
if(port->chip->c_cim)
{
if(port->chip->c_cim->ci_type == CIM_SP502)
{
copy_to_user((unsigned int*) arg, &port->sigmode, sizeof(unsigned int));
return 0;
}
}
return -EINVAL;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static void sab8253x_set_termios(struct tty_struct *tty,
struct termios *old_termios)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
if((tty->termios->c_cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag)))
{
return;
}
if(!port)
{
return;
}
sab8253x_change_speed(port);
/* Handle transition to B0 status */
if ((old_termios->c_cflag & CBAUD) &&
!(tty->termios->c_cflag & CBAUD))
{
LOWER(port,rts);
LOWER(port,dtr);
}
/* Handle transition away from B0 status */
if (!(old_termios->c_cflag & CBAUD) &&
(tty->termios->c_cflag & CBAUD))
{
RAISE(port, dtr);
if (!tty->hw_stopped ||
!(tty->termios->c_cflag & CRTSCTS))
{
RAISE(port, rts);
}
}
/* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS))
{
tty->hw_stopped = 0;
sab8253x_start(tty);
}
}
/*
* ------------------------------------------------------------
* sab8253x_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 sab8253x_close(struct tty_struct *tty, struct file * filp)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
unsigned long flags;
MOD_DEC_USE_COUNT;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_close"))
{
return;
}
if(port->open_type == OPEN_SYNC_NET)
{ /* port->tty field should already be NULL */
/* port count was not incremented */
return;
}
--(port->count); /* have a valid port */
if (tty_hung_up_p(filp))
{
if(port->count == 0) /* shutdown took place in hangup context */
{
port->open_type = OPEN_NOT;
}
else if(port->count < 0)
{
printk(KERN_ALERT "XX20: port->count went negative.\n");
port->count = 0;
port->open_type = OPEN_NOT;
}
return;
}
if (port->count < 0)
{
printk(KERN_ALERT "sab8253x_close: bad serial port count for ttys%d: %d\n",
port->line, port->count);
port->count = 0;
}
if (port->count)
{
return;
}
port->flags |= FLAG8253X_CLOSING;
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
*/
if (port->flags & FLAG8253X_NORMAL_ACTIVE)
{
port->normal_termios = *tty->termios;
}
if (port->flags & FLAG8253X_CALLOUT_ACTIVE)
{
port->callout_termios = *tty->termios;
}
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty->closing = 1;
if (port->closing_wait != SAB8253X_CLOSING_WAIT_NONE)
{
tty_wait_until_sent(tty, port->closing_wait); /* wait for drain */
}
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and turn off
* the receiver.
*/
save_flags(flags);
cli();
port->interrupt_mask0 |= SAB82532_IMR0_TCD;
WRITEB(port,imr0,port->interrupt_mask0);
CLEAR_REG_BIT(port,mode,SAB82532_MODE_RAC); /* ??????? */
restore_flags(flags);
if (port->flags & FLAG8253X_INITIALIZED)
{
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
sab8253x_wait_until_sent(tty, port->timeout);
}
sab8253x_shutdown(port); /* no more ints on port */
Sab8253xCleanUpTransceiveN(port); /* should be okay */
if (tty->driver.flush_buffer)
{
tty->driver.flush_buffer(tty);
}
tty_ldisc_flush(tty);
tty->closing = 0;
port->event = 0;
port->tty = 0;
if (port->blocked_open)
{
if (port->close_delay)
{
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(port->close_delay);
}
wake_up_interruptible(&port->open_wait); /* deal with open blocks */
}
if((port->flags & (FLAG8253X_CALLOUT_ACTIVE | FLAG8253X_NETWORK)) ==
(FLAG8253X_CALLOUT_ACTIVE | FLAG8253X_NETWORK) &&
port->dev)
{
port->flags &= ~(FLAG8253X_NORMAL_ACTIVE|FLAG8253X_CALLOUT_ACTIVE|
FLAG8253X_CLOSING); /* leave network set */
netif_carrier_off(port->dev);
port->open_type = OPEN_SYNC_NET;
sab8253x_startupN(port);
}
else
{
port->flags &= ~(FLAG8253X_NORMAL_ACTIVE|FLAG8253X_CALLOUT_ACTIVE|
FLAG8253X_CLOSING);
wake_up_interruptible(&port->close_wait);
port->open_type = OPEN_NOT;
}
}
/*
* sab8253x_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
static void sab8253x_hangup(struct tty_struct *tty)
{
struct sab_port *port = (struct sab_port *)tty->driver_data;
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_hangup"))
{
return;
}
#ifdef XCONFIG_SERIAL_CONSOLE
if (port->is_console)
{
return;
}
#endif
sab8253x_flush_buffer(tty);
if(port)
{
sab8253x_shutdown(port);
Sab8253xCleanUpTransceiveN(port); /* this logic is a bit contorted
Are we cleaning up the lists
because we are waking up a
blocked open? There is possibly
an order problem here perhaps the
open count should have increased in the
int handler so that it could decrease here*/
port->event = 0;
port->flags &= ~(FLAG8253X_NORMAL_ACTIVE|FLAG8253X_CALLOUT_ACTIVE);
port->tty = 0;
wake_up_interruptible(&port->open_wait); /* deal with blocking open */
}
}
/*
* ------------------------------------------------------------
* sab8253x_open() and friends
* ------------------------------------------------------------
*/
/*
* This routine is called whenever a serial port is opened. It
* enables interrupts for a serial port, linking in its async structure into
* the IRQ chain. It also performs the serial-specific
* initialization for the tty structure.
*/
static int sab8253x_open(struct tty_struct *tty, struct file * filp)
{
struct sab_port *port;
int retval, line;
int counter;
unsigned long flags;
MOD_INC_USE_COUNT;
line = MINOR(tty->device) - tty->driver.minor_start;
for(counter = 0, port = AuraPortRoot;
(counter < line) && (port != NULL);
++counter)
{
port = port->next;
}
if (!port)
{
printk(KERN_ALERT "sab8253x_open: can't find structure for line %d\n",
line);
return -ENODEV;
}
save_flags(flags); /* Need to protect the port->tty field */
cli();
if(port->tty == NULL)
{
port->tty = tty; /* may be a standard tty waiting on a call out device */
tty->flip.tqueue.routine = sab8253x_flush_to_ldisc;
}
tty->driver_data = port; /* but the tty devices are unique for each type of open */
if(port->function == FUNCTION_NA)
{ /* port 2 on 1020s and 1520s */
++(port->count);
restore_flags(flags);
return -ENODEV;
}
/* Check whether or not the port is open in SYNC mode */
if(port->open_type == OPEN_SYNC_NET)
{
if(port->dev && netif_carrier_ok(port->dev))
{
port->tty= NULL; /* Don't bother with open counting here
but make sure the tty field is NULL*/
restore_flags(flags);
return -EBUSY;
}
sab8253x_flush_buffer(tty); /* don't restore flags here */
sab8253x_shutdownN(port);
}
else if (port->open_type > OPEN_ASYNC) /* can't have a callout or async line
* if already open in some sync mode */
{
++(port->count);
restore_flags(flags);
return -EBUSY;
}
restore_flags(flags);
if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_open"))
{
++(port->count);
return -ENODEV;
}
#ifdef DEBUG_OPEN
printk("sab8253x_open %s%d, count = %d\n", tty->driver.name, port->line,
port->count);
#endif
/*
* If the port is in the middle of closing, bail out now.
*/
if (tty_hung_up_p(filp) ||
(port->flags & FLAG8253X_CLOSING))
{
if (port->flags & FLAG8253X_CLOSING)
{
interruptible_sleep_on(&port->close_wait);
}
#ifdef SERIAL_DO_RESTART
++(port->count);
return ((port->flags & FLAG8253X_HUP_NOTIFY) ?
-EAGAIN : -ERESTARTSYS);
#else
++(port->count);
return -EAGAIN;
#endif
}
if(Sab8253xSetUpLists(port))
{
++(port->count);
return -ENODEV;
}
if(Sab8253xInitDescriptors2(port, sab8253xt_listsize, sab8253xt_rbufsize))
{
++(port->count);
return -ENODEV;
}
retval = sab8253x_startup(port);
if (retval)
{
++(port->count);
return retval;
}
retval = sab8253x_block_til_ready(tty, filp, port);
++(port->count);
if (retval)
{
return retval;
}
port->tty = tty; /* may change here once through the block */
/* because now the port belongs to an new tty */
tty->flip.tqueue.routine = sab8253x_flush_to_ldisc; /* in case it was changed */
if(Sab8253xSetUpLists(port))
{
return -ENODEV;
}
if(Sab8253xInitDescriptors2(port, sab8253xt_listsize, sab8253xt_rbufsize))
{
Sab8253xCleanUpTransceiveN(port); /* the network functions should be okay -- only difference */
/* is the crc32 that is appended */
return -ENODEV;
}
/*
* Start up serial port
*/
retval = sab8253x_startup(port); /* just in case closing the cu dev
* shutdown the port (but left CD) */
if (retval)
{
return retval;
}
if ((port->count == 1) && /* first open */
(port->flags & FLAG8253X_SPLIT_TERMIOS))
{
if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
{
*tty->termios = port->normal_termios;
}
else
{
*tty->termios = port->callout_termios;
}
sab8253x_change_speed(port);
}
#ifdef XCONFIG_SERIAL_CONSOLE
if (sab8253x_console.cflag && sab8253x_console.index == line)
{
tty->termios->c_cflag = sab8253x_console.cflag;
sab8253x_console.cflag = 0;
sab8253x_change_speed(port);
}
#endif
port->session = current->session;
port->pgrp = current->pgrp;
port->open_type = OPEN_ASYNC;
return 0;
}
static char *signaling[] =
{
"OFF ",
"RS232",
"RS422",
"RS485",
"RS449",
"RS530",
"V.35 "
};
static int sab8253x_read_proc(char *page, char **start, off_t off, int count,
int *eof, void *data)
{
extern struct sab_port * AuraPortRoot;
struct sab_port *port = AuraPortRoot;
extern char *board_type[];
off_t begin = 0;
int len = 0;
int portno;
unsigned int typeno;
extern int sab8253x_rebootflag;
#ifdef FREEININTERRUPT
len += sprintf(page, "serinfo:2.01I driver:%s\n", sab8253x_serial_version);
#else
len += sprintf(page, "serinfo:2.01N driver:%s\n", sab8253x_serial_version);
#endif
if(sab8253x_rebootflag)
{
len += sprintf(page+len,
"WARNING: Found %d cards that required reprogramming. Reboot machine!.\n",
sab8253x_rebootflag);
}
len += sprintf(page+len, "TTY MAJOR = %d, CUA MAJOR = %d, STTY MAJOR = %d.\n",
sab8253x_serial_driver.major, sab8253x_callout_driver.major,
sync_sab8253x_serial_driver.major);
for (portno = 0; port != NULL; port = port->next, ++portno)
{
typeno = port->board->b_type;
if(typeno > BD_8520P)
{
typeno = 0;
}
len += sprintf(page+len,
"%d: port %d: %s: v%d: chip %d: ATI %s: bus %d: slot %d: %s: ",
sab8253x_serial_driver.minor_start + portno,
port->portno,
(port->chip->chip_type == ESCC2) ? "sab82532" : "sab82538",
port->type,
port->chip->c_chipno,
board_type[port->board->b_type],
port->board->b_dev.bus->number,
PCI_SLOT(port->board->b_dev.devfn),
aura_functionality[((port->function > FUNCTION_UN) ? FUNCTION_UN : port->function)]);
switch(port->open_type)
{
case OPEN_ASYNC:
len += sprintf(page+len, "openA");
break;
case OPEN_SYNC:
len += sprintf(page+len, "openS");
break;
case OPEN_SYNC_NET:
len += sprintf(page+len, "openN");
break;
case OPEN_SYNC_CHAR:
len += sprintf(page+len, "openC");
break;
case OPEN_NOT:
len += sprintf(page+len, "close");
break;
default:
len += sprintf(page+len, "open?");
break;
}
if(port->chip->c_cim)
{
if(port->chip->c_cim->ci_type == CIM_SP502)
{
len += sprintf(page+len, ": %s\n", signaling[port->sigmode]);
}
else
{
len += sprintf(page+len, ": NOPRG\n");
}
}
else
{
len += sprintf(page+len, ": NOPRG\n");
}
if (len+begin > off+count)
{
goto done;
}
if (len+begin < off)
{
begin += len;
len = 0;
}
}
*eof = 1;
done:
if (off >= len+begin)
{
return 0;
}
*start = page + (off-begin);
return ((count < begin+len-off) ? count : begin+len-off);
}
/*
* ---------------------------------------------------------------------
* sab8253x_init() and friends
*
* sab8253x_init() is called at boot-time to initialize the serial driver.
* ---------------------------------------------------------------------
*/
static void inline show_aurora_version(void)
{
char *revision = "$Revision: 1.23 $";
char *version, *p;
version = strchr(revision, ' ');
strcpy(sab8253x_serial_version, ++version);
p = strchr(sab8253x_serial_version, ' ');
*p = '\0';
printk("Aurora serial driver version %s\n", sab8253x_serial_version);
}
#ifndef MODULE
static int GetMinorStart(void)
{
struct tty_driver *ttydriver;
int minor_start = 0;
kdev_t device;
device = MKDEV(TTY_MAJOR, minor_start);
while(ttydriver = get_tty_driver(device), ttydriver != NULL)
{
minor_start += ttydriver->num;
device = MKDEV(TTY_MAJOR, minor_start);
}
return minor_start;
}
#endif
int finish_sab8253x_setup_ttydriver(void)
{
extern unsigned int NumSab8253xPorts;
sab8253x_tableASY = (struct tty_struct **) kmalloc(NumSab8253xPorts*sizeof(struct tty_struct *), GFP_KERNEL);
if(sab8253x_tableASY == NULL)
{
printk(KERN_ALERT "auraXX20: Could not allocate memory for sab8253x_tableASY.\n");
return -1;
}
memset(sab8253x_tableASY, 0, NumSab8253xPorts*sizeof(struct tty_struct *));
sab8253x_tableCUA = (struct tty_struct **) kmalloc(NumSab8253xPorts*sizeof(struct tty_struct *), GFP_KERNEL);
if(sab8253x_tableCUA == NULL)
{
printk(KERN_ALERT "auraXX20: Could not allocate memory for sab8253x_tableCUA.\n");
return -1;
}
memset(sab8253x_tableCUA, 0, NumSab8253xPorts*sizeof(struct tty_struct *));
sab8253x_tableSYN = (struct tty_struct **) kmalloc(NumSab8253xPorts*sizeof(struct tty_struct *), GFP_KERNEL);
if(sab8253x_tableSYN == NULL)
{
printk(KERN_ALERT "auraXX20: Could not allocate memory for sab8253x_tableSYN.\n");
return -1;
}
memset(sab8253x_tableSYN, 0, NumSab8253xPorts*sizeof(struct tty_struct *));
sab8253x_termios = (struct termios **) kmalloc(NumSab8253xPorts*sizeof(struct termios *), GFP_KERNEL);
if(sab8253x_termios == NULL)
{
printk(KERN_ALERT "auraXX20: Could not allocate memory for sab8253x_termios.\n");
return -1;
}
memset(sab8253x_termios, 0, NumSab8253xPorts*sizeof(struct termios *));
sab8253x_termios_locked = (struct termios **) kmalloc(NumSab8253xPorts*sizeof(struct termios *), GFP_KERNEL);
if(sab8253x_termios_locked == NULL)
{
printk(KERN_ALERT "auraXX20: Could not allocate memory for sab8253x_termios_locked.\n");
return -1;
}
memset(sab8253x_termios_locked, 0, NumSab8253xPorts*sizeof(struct termios *));
sync_sab8253x_serial_driver.num = sab8253x_callout_driver.num = sab8253x_serial_driver.num = NumSab8253xPorts;
sab8253x_serial_driver.table = sab8253x_tableASY;
sab8253x_callout_driver.table = sab8253x_tableCUA;
sync_sab8253x_serial_driver.table = sab8253x_tableSYN;
sync_sab8253x_serial_driver.termios = sab8253x_callout_driver.termios = sab8253x_serial_driver.termios =
sab8253x_termios;
sync_sab8253x_serial_driver.termios_locked = sab8253x_callout_driver.termios_locked =
sab8253x_serial_driver.termios_locked = sab8253x_termios_locked;
if (tty_register_driver(&sab8253x_serial_driver) < 0)
{
printk(KERN_ALERT "auraXX20: Could not register serial driver.\n");
return -1;
}
if (tty_register_driver(&sab8253x_callout_driver) < 0)
{
printk(KERN_ALERT "auraXX20: Could not register call out device.\n");
return -1;
}
if (tty_register_driver(&sync_sab8253x_serial_driver) < 0)
{
printk(KERN_ALERT "auraXX20: Could not register sync serial device.\n");
return -1;
}
return 0;
}
void sab8253x_setup_ttydriver(void)
{
#ifdef MODULE
extern int xx20_minorstart;
#endif
init_bh(AURORA_BH, sab8253x_do_serial_bh);
show_aurora_version();
/* Initialize the tty_driver structure */
memset(&sab8253x_serial_driver, 0, sizeof(struct tty_driver));
sab8253x_serial_driver.magic = TTY_DRIVER_MAGIC;
sab8253x_serial_driver.driver_name = "auraserial";
sab8253x_serial_driver.name = "ttyS";
sab8253x_serial_driver.major = TTY_MAJOR;
#ifdef MODULE
sab8253x_serial_driver.minor_start = xx20_minorstart;
#else
sab8253x_serial_driver.minor_start = GetMinorStart();
#endif
sab8253x_serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
sab8253x_serial_driver.subtype = SERIAL_TYPE_NORMAL;
sab8253x_serial_driver.init_termios = tty_std_termios;
sab8253x_serial_driver.init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
sab8253x_serial_driver.flags = TTY_DRIVER_REAL_RAW;
sab8253x_serial_driver.refcount = &sab8253x_refcount;
sab8253x_serial_driver.open = sab8253x_open;
sab8253x_serial_driver.close = sab8253x_close;
sab8253x_serial_driver.write = sab8253x_write;
sab8253x_serial_driver.put_char = NULL; /*sab8253x_put_char is evil.*/
sab8253x_serial_driver.flush_chars = sab8253x_flush_chars;
sab8253x_serial_driver.write_room = sab8253x_write_room;
sab8253x_serial_driver.chars_in_buffer = sab8253x_chars_in_buffer;
sab8253x_serial_driver.flush_buffer = sab8253x_flush_buffer;
sab8253x_serial_driver.ioctl = sab8253x_ioctl;
sab8253x_serial_driver.throttle = sab8253x_throttle;
sab8253x_serial_driver.unthrottle = sab8253x_unthrottle;
sab8253x_serial_driver.send_xchar = sab8253x_send_xchar;
sab8253x_serial_driver.set_termios = sab8253x_set_termios;
sab8253x_serial_driver.stop = sab8253x_stop;
sab8253x_serial_driver.start = sab8253x_start;
sab8253x_serial_driver.hangup = sab8253x_hangup;
sab8253x_serial_driver.break_ctl = sab8253x_break;
sab8253x_serial_driver.wait_until_sent = sab8253x_wait_until_sent;
sab8253x_serial_driver.read_proc = sab8253x_read_proc;
/*
* The callout device is just like normal device except for
* major number and the subtype code.
*/
sab8253x_callout_driver = sab8253x_serial_driver;
sab8253x_callout_driver.name = "cua";
sab8253x_callout_driver.major = TTYAUX_MAJOR;
sab8253x_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
sab8253x_callout_driver.read_proc = 0;
sab8253x_callout_driver.proc_entry = 0;
sync_sab8253x_serial_driver = sab8253x_serial_driver;
sync_sab8253x_serial_driver.name = "sttyS";
sync_sab8253x_serial_driver.major = 0;
sync_sab8253x_serial_driver.subtype = SERIAL_TYPE_SYNCTTY;
sync_sab8253x_serial_driver.open = sab8253x_openS;
sync_sab8253x_serial_driver.close = sab8253x_closeS;
sync_sab8253x_serial_driver.write = sab8253x_writeS;
sync_sab8253x_serial_driver.put_char = NULL; /*sab8253x_put_char logic is evil*/
sync_sab8253x_serial_driver.flush_chars = sab8253x_flush_charsS;
sync_sab8253x_serial_driver.write_room = sab8253x_write_room;
sync_sab8253x_serial_driver.chars_in_buffer = sab8253x_chars_in_buffer;
sync_sab8253x_serial_driver.flush_buffer = sab8253x_flush_buffer;
sync_sab8253x_serial_driver.ioctl = sab8253x_ioctl;
sync_sab8253x_serial_driver.throttle = sab8253x_throttleS;
sync_sab8253x_serial_driver.unthrottle = sab8253x_unthrottleS;
sync_sab8253x_serial_driver.send_xchar = sab8253x_send_xcharS;
sync_sab8253x_serial_driver.set_termios = sab8253x_set_termiosS;
sync_sab8253x_serial_driver.stop = sab8253x_stopS;
sync_sab8253x_serial_driver.start = sab8253x_startS;
sync_sab8253x_serial_driver.hangup = sab8253x_hangupS;
sync_sab8253x_serial_driver.break_ctl = sab8253x_breakS;
sync_sab8253x_serial_driver.wait_until_sent = sab8253x_wait_until_sent;
sync_sab8253x_serial_driver.read_proc = 0;
sync_sab8253x_serial_driver.proc_entry = 0;
}
void sab8253x_setup_ttyport(struct sab_port *p_port)
{
p_port->magic = SAB_MAGIC;
p_port->custom_divisor = 16;
p_port->close_delay = 5*HZ/10;
p_port->closing_wait = 30*HZ;
p_port->tec_timeout = SAB8253X_MAX_TEC_DELAY;
p_port->cec_timeout = SAB8253X_MAX_CEC_DELAY;
p_port->x_char = 0;
p_port->event = 0;
p_port->flags= FLAG8253X_BOOT_AUTOCONF | FLAG8253X_SKIP_TEST;
p_port->blocked_open = 0;
p_port->all_sent = 1; /* probably not needed */
p_port->tqueue.sync = 0; /* for later */
p_port->tqueue.routine = sab8253x_do_softint;
p_port->tqueue.data = p_port;
p_port->tqueue_hangup.sync = 0; /* for later */
p_port->tqueue_hangup.routine = sab8253x_do_serial_hangup;
p_port->tqueue_hangup.data = p_port;
p_port->callout_termios = sab8253x_callout_driver.init_termios;
p_port->normal_termios = sab8253x_serial_driver.init_termios; /* these are being shared */
/* between asynchronous and */
/* asynchronous ttys */
init_waitqueue_head(&p_port->open_wait);
init_waitqueue_head(&p_port->close_wait);
init_waitqueue_head(&p_port->delta_msr_wait);
init_waitqueue_head(&p_port->write_wait);
init_waitqueue_head(&p_port->read_wait);
p_port->count = 0; /* maybe not needed */
p_port->icount.cts = p_port->icount.dsr =
p_port->icount.rng = p_port->icount.dcd = 0;
p_port->cts.val = p_port->dsr.val =
p_port->dcd.val = 0;
p_port->icount.rx = p_port->icount.tx = 0;
p_port->icount.frame = p_port->icount.parity = 0;
p_port->icount.overrun = p_port->icount.brk = 0;
p_port->xmit_fifo_size = 32;
p_port->recv_fifo_size = 32;
p_port->xmit_buf = NULL;
p_port->receive_chars = sab8253x_receive_chars;
p_port->transmit_chars = sab8253x_transmit_chars;
p_port->check_status = sab8253x_check_status;
p_port->receive_test = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
SAB82532_ISR0_RFO | SAB82532_ISR0_RPF);
p_port->transmit_test = (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR);
p_port->check_status_test = SAB82532_ISR1_BRK;
/* put in default sync control channel values
* not needed for async -- I think these work
* for bisync*/
p_port->ccontrol.ccr0 = DEFAULT_CCR0;
p_port->ccontrol.ccr1 = DEFAULT_CCR1;
p_port->ccontrol.ccr2 = DEFAULT_CCR2;
p_port->ccontrol.ccr3 = DEFAULT_CCR3;
p_port->ccontrol.ccr4 = DEFAULT_CCR4;
p_port->ccontrol.mode = DEFAULT_MODE;
p_port->ccontrol.rlcr = DEFAULT_RLCR;
}
void sab8253x_cleanup_ttydriver(void)
{
unsigned long flags;
int e1, e2;
save_flags(flags);
cli();
if(sab8253x_tableASY)
kfree(sab8253x_tableASY);
if(sab8253x_tableCUA)
kfree(sab8253x_tableCUA);
if(sab8253x_tableSYN)
kfree(sab8253x_tableSYN);
if(sab8253x_termios)
kfree(sab8253x_termios);
if(sab8253x_termios_locked)
kfree(sab8253x_termios_locked);
remove_bh(AURORA_BH);
if ((e1 = tty_unregister_driver(&sab8253x_serial_driver)))
{
printk("SERIAL: failed to unregister serial driver (%d)\n",
e1);
}
if ((e2 = tty_unregister_driver(&sab8253x_callout_driver)))
{
printk("SERIAL: failed to unregister callout driver (%d)\n",
e2);
}
if ((e2 = tty_unregister_driver(&sync_sab8253x_serial_driver)))
{
printk("SERIAL: failed to unregister callout driver (%d)\n",
e2);
}
restore_flags(flags);
}
/* THE CODE BELOW HAS NOT YET BEEN MODIFIED!!!! FW */
#ifdef XCONFIG_SERIAL_CONSOLE
static inline void
sab8253x_console_putchar(struct sab8253x *info, char c)
{
unsigned long flags;
save_flags(flags); cli();
sab8253x_tec_wait(info);
WRITEB(port,tic,c);
restore_flags(flags);
}
static void
sab8253x_console_write(struct console *con, const char *s, unsigned n)
{
struct sab8253x *info;
int i;
info = sab8253x_chain;
for (i = con->index; i; i--) {
info = info->next;
if (!info)
return;
}
for (i = 0; i < n; i++) {
if (*s == '\n')
sab8253x_console_putchar(info, '\r');
sab8253x_console_putchar(info, *s++);
}
sab8253x_tec_wait(info);
}
static int
sab8253x_console_wait_key(struct console *con)
{
sleep_on(&keypress_wait);
return 0;
}
static kdev_t
sab8253x_console_device(struct console *con)
{
return MKDEV(TTY_MAJOR, 64 + con->index);
}
static int
sab8253x_console_setup(struct console *con, char *options)
{
struct sab8253x *info;
unsigned int ebrg;
tcflag_t cflag;
unsigned char dafo;
int i, bits;
unsigned long flags;
info = sab8253x_chain;
for (i = con->index; i; i--)
{
info = info->next;
if (!info)
return -ENODEV;
}
info->is_console = 1;
/*
* Initialize the hardware
*/
sab8253x_init_line(info);
/*
* Finally, enable interrupts
*/
info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
/*SAB82532_IMR0_TIME*/ | SAB82532_IMR0_PLLA/*| SAB82532_IMR0_CDSC*/;
WRITEB(port,imr0,info->interrupt_mask0);
info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
SAB82532_IMR1_XPR;
WRITEB(port,imr1,info->interrupt_mask1);
printk("Console: ttyS%d (SAB82532)\n", info->line);
sunserial_console_termios(con);
cflag = con->cflag;
/* Byte size and parity */
switch (cflag & CSIZE)
{
case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break;
case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break;
case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break;
/* Never happens, but GCC is too dumb to figure it out */
default: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
}
if (cflag & CSTOPB)
{
dafo |= SAB82532_DAFO_STOP;
bits++;
}
if (cflag & PARENB)
{
dafo |= SAB82532_DAFO_PARE;
bits++;
}
if (cflag & PARODD)
{
#ifdef CMSPAR
if (cflag & CMSPAR)
dafo |= SAB82532_DAFO_PAR_MARK;
else
#endif
dafo |= SAB82532_DAFO_PAR_ODD;
}
else
{
#ifdef CMSPAR
if (cflag & CMSPAR)
dafo |= SAB82532_DAFO_PAR_SPACE;
else
#endif
dafo |= SAB82532_DAFO_PAR_EVEN;
}
/* Determine EBRG values based on baud rate */
i = cflag & CBAUD;
if (i & CBAUDEX)
{
i &= ~(CBAUDEX);
if ((i < 1) || ((i + 15) >= NR_EBRG_VALUES))
cflag &= ~CBAUDEX;
else
i += 15;
}
ebrg = ebrg_tabl[i].n;
ebrg |= (ebrg_table[i].m << 6);
info->baud = ebrg_table[i].baud;
if (info->baud)
info->timeout = (info->xmit_fifo_size * HZ * bits) / info->baud;
else
info->timeout = 0;
info->timeout += HZ / 50; /* Add .02 seconds of slop */
/* CTS flow control flags */
if (cflag & CRTSCTS)
info->flags |= FLAG8253X_CTS_FLOW;
else
info->flags &= ~(FLAG8253X_CTS_FLOW);
if (cflag & CLOCAL)
info->flags &= ~(FLAG8253X_CHECK_CD);
else
info->flags |= FLAG8253X_CHECK_CD;
save_flags(flags); cli();
sab8253x_cec_wait(info);
sab8253x_tec_wait(info);
WRITEB(port,dafo,dafo);
WRITEB(port,bgr,ebrg & 0xff);
info->regs->rw.ccr2 &= ~(0xc0);
info->regs->rw.ccr2 |= (ebrg >> 2) & 0xc0;
if (info->flags & FLAG8253X_CTS_FLOW)
{
info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
info->regs->rw.mode |= SAB82532_MODE_FRTS;
info->regs->rw.mode &= ~(SAB82532_MODE_FCTS);
}
else
{
info->regs->rw.mode |= SAB82532_MODE_RTS;
info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
info->regs->rw.mode |= SAB82532_MODE_FCTS;
}
info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
info->regs->rw.mode |= SAB82532_MODE_RAC;
restore_flags(flags);
return 0;
}
static struct console sab8253x_console =
{
"ttyS",
sab8253x_console_write,
NULL,
sab8253x_console_device,
sab8253x_console_wait_key,
NULL,
sab8253x_console_setup,
CON_PRINTBUFFER,
-1,
0,
NULL
};
int sab8253x_console_init(void)
{
extern int con_is_present(void);
extern int su_console_registered;
if (con_is_present() || su_console_registered)
return 0;
if (!sab8253x_chain)
{
prom_printf("sab8253x_console_setup: can't get SAB8253X chain");
prom_halt();
}
sab8253x_console.index = serial_console - 1;
register_console(&sab8253x_console);
return 0;
}
#ifdef SERIAL_LOG_DEVICE
static int serial_log_device = 0;
static void
dprint_init(int tty)
{
serial_console = tty + 1;
sab8253x_console.index = tty;
sab8253x_console_setup(&sab8253x_console, "");
serial_console = 0;
serial_log_device = tty + 1;
}
int
dprintf(const char *fmt, ...)
{
static char buffer[4096];
va_list args;
int i;
if (!serial_log_device)
return 0;
va_start(args, fmt);
i = vsprintf(buffer, fmt, args);
va_end(args);
sab8253x_console.write(&sab8253x_console, buffer, i);
return i;
}
#endif /* SERIAL_LOG_DEVICE */
#endif /* XCONFIG_SERIAL_CONSOLE */