?? serial.c
字號:
if (!info || !info->tty || !(info->flags & ASYNC_INITIALIZED)) {
sti();
return;
}
restart_port(info);
info->IER = (UART_IER_MSI | UART_IER_RLSI |
UART_IER_THRI | UART_IER_RDI);
#ifdef ISR_HACK
serial_out(info, UART_IER, info->IER);
#endif
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).
* ------------------------------------------------------------
*/
static void rs_throttle(struct tty_struct * tty, int status)
{
struct async_struct *info;
unsigned char mcr;
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 {
mcr = serial_inp(info, UART_MCR);
mcr &= ~UART_MCR_RTS;
serial_out(info, UART_MCR, mcr);
}
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 {
mcr = serial_in(info, UART_MCR);
mcr |= UART_MCR_RTS;
serial_out(info, UART_MCR, mcr);
}
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;
memset(&tmp, 0, sizeof(tmp));
tmp.type = info->type;
tmp.line = info->line;
tmp.port = info->port;
tmp.irq = info->irq;
tmp.flags = info->flags;
tmp.baud_base = info->baud_base;
tmp.close_delay = info->close_delay;
tmp.custom_divisor = info->custom_divisor;
tmp.hub6 = info->hub6;
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;
unsigned int i,change_irq,change_port;
int retval;
struct sigaction sa;
if (!new_info)
return -EFAULT;
memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
old_info = *info;
change_irq = new_serial.irq != info->irq;
change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6);
if (!suser()) {
if (change_irq || change_port ||
(new_serial.baud_base != info->baud_base) ||
(new_serial.type != info->type) ||
(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;
}
if (new_serial.irq == 2)
new_serial.irq = 9;
if ((new_serial.irq > 15) || (new_serial.port > 0xffff) ||
(new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) {
return -EINVAL;
}
/* Make sure address is not already in use */
for (i = 0 ; i < NR_PORTS; i++)
if ((info != &rs_table[i]) &&
(rs_table[i].port == new_serial.port) && rs_table[i].type)
return -EADDRINUSE;
/*
* If necessary, first we try to grab the new IRQ for serial
* interrupts. (We have to do this early, since we may get an
* error trying to do this.)
*/
if (new_serial.port && new_serial.type && new_serial.irq &&
(change_irq || !(info->flags & ASYNC_INITIALIZED))) {
if (!IRQ_ports[new_serial.irq]) {
sa.sa_handler = rs_interrupt;
sa.sa_flags = (SA_INTERRUPT);
sa.sa_mask = 0;
sa.sa_restorer = NULL;
retval = irqaction(new_serial.irq,&sa);
if (retval)
return retval;
}
}
if ((change_port || change_irq) && (info->count > 1))
return -EBUSY;
/*
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
info->baud_base = new_serial.baud_base;
info->flags = ((info->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
info->custom_divisor = new_serial.custom_divisor;
info->type = new_serial.type;
info->close_delay = new_serial.close_delay;
if (change_port || change_irq) {
/*
* We need to shutdown the serial port at the old
* port/irq combination.
*/
shutdown(info, change_irq);
info->irq = new_serial.irq;
info->port = new_serial.port;
info->hub6 = new_serial.hub6;
}
check_and_exit:
if (!info->port || !info->type)
return 0;
if (info->flags & ASYNC_INITIALIZED) {
if (((old_info.flags & ASYNC_SPD_MASK) !=
(info->flags & ASYNC_SPD_MASK)) ||
(old_info.custom_divisor != info->custom_divisor))
change_speed(info->line);
} else
(void) startup(info, 0);
return 0;
}
static int get_modem_info(struct async_struct * info, unsigned int *value)
{
unsigned char control, status;
unsigned int result;
cli();
control = serial_in(info, UART_MCR);
status = serial_in(info, UART_MSR);
sti();
result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
| ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
| ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
| ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
| ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
| ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
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 char control;
unsigned int arg = get_fs_long((unsigned long *) value);
cli();
control = serial_in(info, UART_MCR);
sti();
switch (cmd) {
case TIOCMBIS:
if (arg & TIOCM_RTS)
control |= UART_MCR_RTS;
if (arg & TIOCM_DTR)
control |= UART_MCR_DTR;
break;
case TIOCMBIC:
if (arg & TIOCM_RTS)
control &= ~UART_MCR_RTS;
if (arg & TIOCM_DTR)
control &= ~UART_MCR_DTR;
break;
case TIOCMSET:
control = (control & ~(UART_MCR_RTS | UART_MCR_DTR))
| ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
| ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0);
break;
default:
return -EINVAL;
}
cli();
serial_out(info, UART_MCR, control);
sti();
return 0;
}
static int do_autoconfig(struct async_struct * info)
{
int retval;
if (!suser())
return -EPERM;
if (info->count > 1)
return -EBUSY;
shutdown(info, 1);
cli();
autoconfig(info);
sti();
retval = startup(info, 1);
if (retval)
return retval;
return 0;
}
/*
* This routine sends a break character out the serial port.
*/
static void send_break( struct async_struct * info, int duration)
{
if (!info->port)
return;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + duration;
cli();
serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
schedule();
serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
sti();
}
/*
* This routine returns a bitfield of "wild interrupts". Basically,
* any unclaimed interrupts which is flapping around.
*/
static int check_wild_interrupts(int doprint)
{
int i, mask;
int wild_interrupts = 0;
int irq_lines;
unsigned long timeout;
unsigned long flags;
/* Turn on interrupts (they may be off) */
save_flags(flags); sti();
irq_lines = grab_all_interrupts(0);
/*
* Delay for 0.1 seconds -- we use a busy loop since this may
* occur during the bootup sequence
*/
timeout = jiffies+10;
while (timeout >= jiffies)
;
rs_triggered = 0; /* Reset after letting things settle */
timeout = jiffies+10;
while (timeout >= jiffies)
;
for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
if ((rs_triggered & (1 << i)) &&
(irq_lines & (1 << i))) {
wild_interrupts |= mask;
if (doprint)
printk("Wild interrupt? (IRQ %d)\n", i);
}
}
free_all_interrupts(irq_lines);
restore_flags(flags);
return wild_interrupts;
}
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;
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);
case TIOCSERGWILD:
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(int));
if (error)
return error;
put_fs_long(rs_wild_int_mask, (unsigned long *) arg);
return 0;
case TIOCSERSWILD:
if (!suser())
return -EPERM;
rs_wild_int_mask = get_fs_long((unsigned long *) arg);
if (rs_wild_int_mask < 0)
rs_wild_int_mask = check_wild_interrupts(0);
return 0;
default:
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)];
change_speed(DEV_TO_SL(tty->line));
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);
if (I_INPCK(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);
}
/*
* ------------------------------------------------------------
* 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;
#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);
cli();
/*
* Make sure the UART transmitter has completely drained; this
* is especially important if there is a transmit FIFO!
*/
if (!(serial_inp(info, UART_LSR) & UART_LSR_THRE)) {
rs_start(tty); /* Make sure THRI interrupt enabled */
interruptible_sleep_on(&info->xmit_wait);
}
sti();
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);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -