?? serial.c
字號(hào):
if (recheck_count > 16)
printk("recheck_count = %d\n", recheck_count);
#endif
}
#ifdef ISR_HACK
serial_outp(info, UART_IER, 0);
serial_out(info, UART_IER, info->IER);
#endif
info = info->next_port;
if (!info && !done) {
info = IRQ_ports[irq];
done = 1;
if (pass_number++ > 64)
break; /* Prevent infinite loops */
}
}
if ((info = IRQ_ports[irq]) != NULL) {
#ifdef 0
do {
serial_outp(info, UART_IER, 0);
serial_out(info, UART_IER, info->IER);
info = info->next_port;
} while (info);
#endif
if (irq && !done_work)
IRQ_timer[irq] = jiffies + 1500;
else
IRQ_timer[irq] = jiffies + IRQ_timeout[irq];
IRQ_active |= 1 << irq;
}
figure_RS_timer();
}
/*
* -------------------------------------------------------------------
* Here ends the serial interrupt routines.
* -------------------------------------------------------------------
*/
/*
* 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);
}
}
}
}
/*
* This subroutine is called when the RS_TIMER goes off. It is used
* by the serial driver to run the rs_interrupt routine at certain
* intervals, either because a serial interrupt might have been lost,
* or because (in the case of IRQ=0) the serial port does not have an
* interrupt, and is being checked only via the timer interrupts.
*/
static void rs_timer(void)
{
int i, mask;
int timeout = 0;
for (i = 0, mask = 1; mask <= IRQ_active; i++, mask <<= 1) {
if ((mask & IRQ_active) && (IRQ_timer[i] <= jiffies)) {
IRQ_active &= ~mask;
cli();
#ifdef SERIAL_DEBUG_TIMER
printk("rs_timer: rs_interrupt(%d)...", i);
#endif
rs_interrupt(i);
sti();
}
if (mask & IRQ_active) {
if (!timeout || (IRQ_timer[i] < timeout))
timeout = IRQ_timer[i];
}
}
if (timeout) {
timer_table[RS_TIMER].expires = timeout;
timer_active |= 1 << RS_TIMER;
}
}
/*
* ---------------------------------------------------------------
* Low level utility subroutines for the serial driver: routines to
* figure out the appropriate timeout for an interrupt chain, routines
* to initialize and startup a serial port, and routines to shutdown a
* serial port. Useful stuff like that.
* ---------------------------------------------------------------
*/
/*
* Grab all interrupts in preparation for doing an automatic irq
* detection. dontgrab is a mask of irq's _not_ to grab. Returns a
* mask of irq's which were grabbed and should therefore be freed
* using free_all_interrupts().
*/
static int grab_all_interrupts(int dontgrab)
{
int irq_lines = 0;
int i, mask;
struct sigaction sa;
sa.sa_handler = rs_probe;
sa.sa_flags = (SA_INTERRUPT);
sa.sa_mask = 0;
sa.sa_restorer = NULL;
for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
if (!(mask & dontgrab) && !irqaction(i, &sa)) {
irq_lines |= mask;
}
}
return irq_lines;
}
/*
* Release all interrupts grabbed by grab_all_interrupts
*/
static void free_all_interrupts(int irq_lines)
{
int i;
for (i = 0; i < 16; i++) {
if (irq_lines & (1 << i))
free_irq(i);
}
}
/*
* This routine figures out the correct timeout for a particular IRQ.
* It uses the smallest timeout of all of the serial ports in a
* particular interrupt chain.
*/
static void figure_IRQ_timeout(int irq)
{
struct async_struct *info;
int timeout = 6000; /* 60 seconds === a long time :-) */
info = IRQ_ports[irq];
if (!info) {
IRQ_timeout[irq] = 6000;
return;
}
while (info) {
if (info->timeout < timeout)
timeout = info->timeout;
info = info->next_port;
}
if (!irq)
timeout = timeout / 2;
IRQ_timeout[irq] = timeout ? timeout : 1;
}
static int startup(struct async_struct * info, int get_irq)
{
unsigned short ICP;
unsigned long flags;
struct sigaction sa;
int retval;
if (info->flags & ASYNC_INITIALIZED)
return 0;
if (!info->port || !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
/*
* Allocate the IRQ if necessary
*/
if (get_irq && info->irq && !IRQ_ports[info->irq]) {
sa.sa_handler = rs_interrupt;
sa.sa_flags = (SA_INTERRUPT);
sa.sa_mask = 0;
sa.sa_restorer = NULL;
retval = irqaction(info->irq,&sa);
if (retval) {
restore_flags(flags);
return retval;
}
}
/*
* Clear the FIFO buffers and disable them
* (they will be reenabled in change_speed())
*/
if (info->type == PORT_16550A) {
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
info->xmit_fifo_size = 16;
} else
info->xmit_fifo_size = 1;
/*
* Clear the interrupt registers.
*/
(void)serial_inp(info, UART_LSR);
(void)serial_inp(info, UART_RX);
(void)serial_inp(info, UART_IIR);
(void)serial_inp(info, UART_MSR);
/*
* Now, initialize the UART
*/
serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
if (info->flags & ASYNC_FOURPORT)
serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
else
serial_outp(info, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
/*
* Finally, enable interrupts
*/
#ifdef ISR_HACK
info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
serial_outp(info, UART_IER, info->IER); /* enable interrupts */
#else
info->IER = (UART_IER_MSI | UART_IER_RLSI |
UART_IER_THRI | UART_IER_RDI);
serial_outp(info, UART_IER, info->IER); /* enable all intrs */
#endif
if (info->flags & ASYNC_FOURPORT) {
/* Enable interrupts on the AST Fourport board */
ICP = (info->port & 0xFE0) | 0x01F;
outb_p(0x80, ICP);
(void) inb_p(ICP);
}
/*
* And clear the interrupt registers again for luck.
*/
(void)serial_inp(info, UART_LSR);
(void)serial_inp(info, UART_RX);
(void)serial_inp(info, UART_IIR);
(void)serial_inp(info, UART_MSR);
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
/*
* Set up parity check flag
*/
if (info->tty && info->tty->termios && I_INPCK(info->tty))
info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
UART_LSR_FE | UART_LSR_PE);
else
info->read_status_mask = (UART_LSR_OE | UART_LSR_BI |
UART_LSR_FE);
/*
* Insert serial port into IRQ chain.
*/
info->prev_port = 0;
info->next_port = IRQ_ports[info->irq];
if (info->next_port)
info->next_port->prev_port = info;
IRQ_ports[info->irq] = info;
figure_IRQ_timeout(info->irq);
/*
* Set up serial timers...
*/
IRQ_active |= 1 << info->irq;
figure_RS_timer();
/*
* and set the speed of the serial port
*/
change_speed(info->line);
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 */
/*
* First unlink the serial port from the IRQ chain...
*/
if (info->next_port)
info->next_port->prev_port = info->prev_port;
if (info->prev_port)
info->prev_port->next_port = info->next_port;
else
IRQ_ports[info->irq] = info->next_port;
figure_IRQ_timeout(info->irq);
/*
* Free the IRQ, if necessary
*/
if (do_free_irq && info->irq && !IRQ_ports[info->irq])
free_irq(info->irq);
info->IER = 0;
serial_outp(info, UART_IER, 0x00); /* disable all intrs */
if (info->flags & ASYNC_FOURPORT) {
/* reset interrupts on the AST Fourport board */
(void) inb((info->port & 0xFE0) | 0x01F);
}
if (info->tty && !(info->tty->termios->c_cflag & HUPCL))
serial_outp(info, UART_MCR, UART_MCR_DTR);
else
/* reset DTR,RTS,OUT_2 */
serial_outp(info, UART_MCR, 0x00);
/* disable FIFO's */
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
(void)serial_in(info, UART_RX); /* read data port to reset things */
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~ASYNC_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 change_speed(unsigned int line)
{
struct async_struct * info;
unsigned short port;
int quot = 0;
unsigned cflag,cval,mcr,fcr;
int i;
if (line >= NR_PORTS)
return;
info = rs_table + line;
if (!info->tty || !info->tty->termios)
return;
cflag = info->tty->termios->c_cflag;
if (!(port = info->port))
return;
i = cflag & CBAUD;
if (i == 15) {
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
i += 1;
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
i += 2;
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
quot = info->custom_divisor;
}
if (quot) {
info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
info->baud_base) + 2;
} else if (baud_table[i] == 134) {
quot = (2*info->baud_base / 269);
info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
} else if (baud_table[i]) {
quot = info->baud_base / baud_table[i];
info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
} else {
quot = 0;
info->timeout = 0;
}
cli();
mcr = serial_in(info, UART_MCR);
if (quot) {
serial_out(info, UART_MCR, mcr | UART_MCR_DTR);
} else {
serial_out(info, UART_MCR, mcr & ~UART_MCR_DTR);
sti();
return;
}
sti();
/* byte size and parity */
cval = cflag & (CSIZE | CSTOPB);
cval >>= 4;
if (cflag & PARENB)
cval |= UART_LCR_PARITY;
if (!(cflag & PARODD))
cval |= UART_LCR_EPAR;
if (info->type == PORT_16550A) {
if ((info->baud_base / quot) < 2400)
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
else
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
} else
fcr = 0;
cli();
serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */
serial_outp(info, UART_LCR, cval); /* reset DLAB */
serial_outp(info, UART_FCR, fcr); /* set fcr */
sti();
}
/*
* ------------------------------------------------------------
* rs_write() and friends
* ------------------------------------------------------------
*/
/*
* This routine is used by rs_write to restart transmitter interrupts,
* which are disabled after we have a transmitter interrupt which went
* unacknowledged because we had run out of data to transmit.
*
* Note: this subroutine must be called with the interrupts *off*
*/
static inline void restart_port(struct async_struct *info)
{
struct tty_queue * queue;
int head, tail, count;
if (!info)
return;
if (serial_inp(info, UART_LSR) & UART_LSR_THRE) {
if (info->x_char) {
serial_outp(info, UART_TX, info->x_char);
info->x_char = 0;
} else {
queue = &info->tty->write_q;
head = queue->head;
tail = queue->tail;
count = info->xmit_fifo_size;
while (count--) {
if (tail == head)
break;
serial_outp(info, UART_TX, queue->buf[tail++]);
tail &= TTY_BUF_SIZE-1;
}
queue->tail = tail;
}
}
}
/*
* 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);
cli();
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -