📄 serial.c
字号:
}
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;
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))
serial_out(info, UART_MCR,
serial_inp(info, UART_MCR) |
(UART_MCR_DTR | UART_MCR_RTS));
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 || (serial_in(info, UART_MSR) &
UART_MSR_DCD)))
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. It
* enables interrupts for a serial port, linking in its async structure into
* the IRQ chain. It also performs the serial-speicific
* initalization for the tty structure.
*/
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;
#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
*/
retval = startup(info, 1);
if (retval)
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 successful...", info->line);
#endif
return 0;
}
/*
* ---------------------------------------------------------------------
* rs_init() and friends
*
* rs_init() is called at boot-time to initialize the serial driver.
* ---------------------------------------------------------------------
*/
/*
* This routine prints out the appropriate serial driver version
* number, and identifies which options were configured into this
* driver.
*/
static void show_serial_version(void)
{
printk("Serial driver version 3.99a with");
#ifdef CONFIG_AST_FOURPORT
printk(" AST_FOURPORT");
#define SERIAL_OPT
#endif
#ifdef CONFIG_ACCENT_ASYNC
printk(" ACCENT_ASYNC");
#define SERIAL_OPT
#endif
#ifdef CONFIG_HUB6
printk(" HUB-6");
#define SERIAL_OPT
#endif
#ifdef CONFIG_AUTO_IRQ
printk (" AUTO_IRQ");
#define SERIAL_OPT
#endif
#ifdef SERIAL_OPT
printk(" enabled\n");
#else
printk(" no serial options enabled\n");
#endif
#undef SERIAL_OPT
}
/*
* This routine is called by do_auto_irq(); it attempts to determine
* which interrupt a serial port is configured to use. It is not
* fool-proof, but it works a large part of the time.
*/
static int get_auto_irq(struct async_struct *info)
{
unsigned char save_MCR, save_IER, save_ICP=0;
unsigned short ICP=0, port = info->port;
unsigned long timeout;
/*
* Enable interrupts and see who answers
*/
rs_irq_triggered = 0;
cli();
save_IER = serial_inp(info, UART_IER);
save_MCR = serial_inp(info, UART_MCR);
if (info->flags & ASYNC_FOURPORT) {
serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
ICP = (port & 0xFE0) | 0x01F;
save_ICP = inb_p(ICP);
outb_p(0x80, ICP);
(void) inb_p(ICP);
} else {
serial_outp(info, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
}
sti();
/*
* Next, 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);
timeout = jiffies+2;
while (timeout >= jiffies) {
if (rs_irq_triggered)
break;
}
/*
* Now check to see if we got any business, and clean up.
*/
cli();
serial_outp(info, UART_IER, save_IER);
serial_outp(info, UART_MCR, save_MCR);
if (info->flags & ASYNC_FOURPORT)
outb_p(save_ICP, ICP);
sti();
return(rs_irq_triggered);
}
/*
* Calls get_auto_irq() multiple times, to make sure we don't get
* faked out by random interrupts
*/
static int do_auto_irq(struct async_struct * info)
{
unsigned port = info->port;
int irq_lines = 0;
int irq_try_1 = 0, irq_try_2 = 0;
int retries;
unsigned long flags;
if (!port)
return 0;
/* Turn on interrupts (they may be off) */
save_flags(flags); sti();
irq_lines = grab_all_interrupts(rs_wild_int_mask);
for (retries = 0; retries < 5; retries++) {
if (!irq_try_1)
irq_try_1 = get_auto_irq(info);
if (!irq_try_2)
irq_try_2 = get_auto_irq(info);
if (irq_try_1 && irq_try_2) {
if (irq_try_1 == irq_try_2)
break;
irq_try_1 = irq_try_2 = 0;
}
}
restore_flags(flags);
free_all_interrupts(irq_lines);
return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
}
/*
* This routine is called by rs_init() to initialize a specific serial
* port. It determines what type of UART ship this serial port is
* using: 8250, 16450, 16550, 16550A. The important question is
* whether or not this UART is a 16550A or not, since this will
* determine whether or not we can use its FIFO features or not.
*/
static void autoconfig(struct async_struct * info)
{
unsigned char status1, status2, scratch, scratch2;
unsigned port = info->port;
unsigned long flags;
info->type = PORT_UNKNOWN;
if (!port)
return;
save_flags(flags); cli();
/*
* Do a simple existence test first; if we fail this, there's
* no point trying anything else.
*/
scratch = serial_inp(info, UART_IER);
serial_outp(info, UART_IER, 0);
scratch2 = serial_inp(info, UART_IER);
serial_outp(info, UART_IER, scratch);
if (scratch2) {
restore_flags(flags);
return; /* We failed; there's nothing here */
}
/*
* Check to see if a UART is really there. Certain broken
* internal modems based on the Rockwell chipset fail this
* test, because they apparently don't implement the loopback
* test mode. So this test is skipped on the COM 1 through
* COM 4 ports. This *should* be safe, since no board
* manufactucturer would be stupid enough to design a board
* that conflicts with COM 1-4 --- we hope!
*/
if (!(info->flags & ASYNC_SKIP_TEST)) {
scratch = serial_inp(info, UART_MCR);
serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
scratch2 = serial_inp(info, UART_MSR);
serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
status1 = serial_inp(info, UART_MSR) & 0xF0;
serial_outp(info, UART_MCR, scratch);
serial_outp(info, UART_MSR, scratch2);
if (status1 != 0x90) {
restore_flags(flags);
return;
}
}
/*
* If the AUTO_IRQ flag is set, try to do the automatic IRQ
* detection.
*/
if (info->flags & ASYNC_AUTO_IRQ)
info->irq = do_auto_irq(info);
serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(info, UART_IIR) >> 6;
info->xmit_fifo_size = 1;
switch (scratch) {
case 0:
info->type = PORT_16450;
break;
case 1:
info->type = PORT_UNKNOWN;
break;
case 2:
info->type = PORT_16550;
break;
case 3:
info->type = PORT_16550A;
info->xmit_fifo_size = 16;
break;
}
if (info->type == PORT_16450) {
scratch = serial_in(info, UART_SCR);
serial_outp(info, UART_SCR, 0xa5);
status1 = serial_in(info, UART_SCR);
serial_outp(info, UART_SCR, 0x5a);
status2 = serial_in(info, UART_SCR);
serial_outp(info, UART_SCR, scratch);
if ((status1 != 0xa5) || (status2 != 0x5a))
info->type = PORT_8250;
}
/*
* Reset the UART.
*/
serial_outp(info, UART_MCR, 0x00);
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
(void)serial_in(info, UART_RX);
restore_flags(flags);
}
/*
* The serial driver boot-time initialization code!
*/
long rs_init(long kmem_start)
{
int i;
struct async_struct * info;
memset(&rs_event, 0, sizeof(rs_event));
bh_base[SERIAL_BH].routine = do_softint;
timer_table[RS_TIMER].fn = rs_timer;
timer_table[RS_TIMER].expires = 0;
IRQ_active = 0;
#ifdef CONFIG_AUTO_IRQ
rs_wild_int_mask = check_wild_interrupts(1);
#endif
for (i = 0; i < 16; i++) {
IRQ_ports[i] = 0;
IRQ_timeout[i] = 0;
}
show_serial_version();
for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
info->line = i;
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;
info->next_port = 0;
info->prev_port = 0;
if (info->irq == 2)
info->irq = 9;
if (!(info->flags & ASYNC_BOOT_AUTOCONF))
continue;
autoconfig(info);
if (info->type == PORT_UNKNOWN)
continue;
printk("tty%02d%s at 0x%04x (irq = %d)", info->line,
(info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
info->port, info->irq);
switch (info->type) {
case PORT_8250:
printk(" is a 8250\n");
break;
case PORT_16450:
printk(" is a 16450\n");
break;
case PORT_16550:
printk(" is a 16550\n");
break;
case PORT_16550A:
printk(" is a 16550A\n");
break;
default:
printk("\n");
break;
}
}
return kmem_start;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -