📄 serial.c
字号:
restore_flags(flags); return wild_interrupts;}static int get_multiport_struct(struct async_struct * info, struct serial_multiport_struct *retinfo){ struct serial_multiport_struct ret; struct rs_multiport_struct *multi; multi = &rs_multiport[IRQMASK(info->irq)]; ret.port_monitor = multi->port_monitor; ret.port1 = multi->port1; ret.mask1 = multi->mask1; ret.match1 = multi->match1; ret.port2 = multi->port2; ret.mask2 = multi->mask2; ret.match2 = multi->match2; ret.port3 = multi->port3; ret.mask3 = multi->mask3; ret.match3 = multi->match3; ret.port4 = multi->port4; ret.mask4 = multi->mask4; ret.match4 = multi->match4; ret.irq = info->irq; memcpy_tofs(retinfo,&ret,sizeof(*retinfo)); return 0; }static int set_multiport_struct(struct async_struct * info, struct serial_multiport_struct *in_multi){ struct serial_multiport_struct new_multi; struct rs_multiport_struct *multi; int was_multi, now_multi; int retval; void (*handler)(int, void *, struct pt_regs *); if (!suser()) return -EPERM; if (!in_multi) return -EFAULT; memcpy_fromfs(&new_multi, in_multi, sizeof(struct serial_multiport_struct)); if (new_multi.irq != info->irq || info->irq == 0 || !IRQ_ports[IRQMASK(info->irq)]) return -EINVAL; multi = &rs_multiport[IRQMASK(info->irq)]; was_multi = (multi->port1 != 0); multi->port_monitor = new_multi.port_monitor; if (multi->port1) release_region(multi->port1,1); multi->port1 = new_multi.port1; multi->mask1 = new_multi.mask1; multi->match1 = new_multi.match1; if (multi->port1) request_region(multi->port1,1,"serial(multiport1)"); if (multi->port2) release_region(multi->port2,1); multi->port2 = new_multi.port2; multi->mask2 = new_multi.mask2; multi->match2 = new_multi.match2; if (multi->port2) request_region(multi->port2,1,"serial(multiport2)"); if (multi->port3) release_region(multi->port3,1); multi->port3 = new_multi.port3; multi->mask3 = new_multi.mask3; multi->match3 = new_multi.match3; if (multi->port3) request_region(multi->port3,1,"serial(multiport3)"); if (multi->port4) release_region(multi->port4,1); multi->port4 = new_multi.port4; multi->mask4 = new_multi.mask4; multi->match4 = new_multi.match4; if (multi->port4) request_region(multi->port4,1,"serial(multiport4)"); now_multi = (multi->port1 != 0); if (IRQ_ports[IRQMASK(info->irq)]->next_port && (was_multi != now_multi)) { free_irq(info->irq, NULL); if (now_multi) handler = rs_interrupt_multi; else handler = rs_interrupt; retval = request_irq(info->irq, handler, IRQ_T(info), "serial", NULL); if (retval) { printk("Couldn't reallocate serial interrupt " "driver!!\n"); } } return 0;}#endif /* CONFIG_COLDFIRE */static int rs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ int error; struct async_struct * info = (struct async_struct *)tty->driver_data; int retval; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct *p_cuser; /* user space */ if (serial_paranoia_check(info, tty->device, "rs_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 TIOCSBRK: /* Turn break on, unconditionally */ retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); rs_break(info,-1); return 0; case TIOCCBRK: /* Turn break off, unconditionally */ retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); rs_break(info,0); return 0; case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); if (!arg) send_break(info, HZ/4); /* 1/4 second */ return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); 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: error = verify_area(VERIFY_READ, (void *) arg,sizeof(long)); if (error) return error; 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: error = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct)); if (error) return error; return set_serial_info(info, (struct serial_struct *) arg); case TIOCSERCONFIG: return do_autoconfig(info);#ifndef CONFIG_COLDFIRE 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;#endif /* CONFIG_COLDFIRE */ case TIOCSERGETLSR: /* Get line status register */ error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int)); if (error) return error; else return get_lsr_info(info, (unsigned int *) arg);#ifndef CONFIG_COLDFIRE case TIOCSERSWILD: if (!suser()) return -EPERM; error = verify_area(VERIFY_READ, (void *) arg,sizeof(long)); if (error) return error; rs_wild_int_mask = get_fs_long((unsigned long *) arg); if (rs_wild_int_mask < 0) rs_wild_int_mask = check_wild_interrupts(0);#endif /* CONFIG_COLDFIRE */ return 0; case TIOCSERGSTRUCT: error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct async_struct)); if (error) return error; memcpy_tofs((struct async_struct *) arg, info, sizeof(struct async_struct)); return 0; #ifndef CONFIG_COLDFIRE case TIOCSERGETMULTI: error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_multiport_struct)); if (error) return error; return get_multiport_struct(info, (struct serial_multiport_struct *) arg); case TIOCSERSETMULTI: error = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_multiport_struct)); if (error) return error; return set_multiport_struct(info, (struct serial_multiport_struct *) arg);#endif /* CONFIG_COLDFIRE */ /* * 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: cli(); cprev = info->icount; /* note the counters on entry */ sti(); while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (current->signal & ~current->blocked) return -ERESTARTSYS; cli(); cnow = info->icount; /* atomic copy */ sti(); 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 */ /* * 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: error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_icounter_struct)); if (error) return error; cli(); cnow = info->icount; sti(); p_cuser = (struct serial_icounter_struct *) arg; put_user(cnow.cts, &p_cuser->cts); put_user(cnow.dsr, &p_cuser->dsr); put_user(cnow.rng, &p_cuser->rng); put_user(cnow.dcd, &p_cuser->dcd); return 0; default: return -ENOIOCTLCMD; } return 0;}static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios){ struct async_struct *info = (struct async_struct *)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; change_speed(info); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; rs_start(tty); }#if 0 /* * No need to wake up processes in open wait, since they * sample the CLOCAL flag once, and don't recheck it. * XXX It's not clear whether the current behavior is correct * or not. Hence, this may change..... */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) wake_up_interruptible(&info->open_wait);#endif}/* * ------------------------------------------------------------ * 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 = (struct async_struct *)tty->driver_data; unsigned long flags; unsigned long timeout; if (!info || serial_paranoia_check(info, tty->device, "rs_close")) return; save_flags(flags); cli(); if (tty_hung_up_p(filp)) { DBG_CNT("before DEC-hung"); MOD_DEC_USE_COUNT; restore_flags(flags); return; } #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttys%d, count = %d\n", info->line, info->count);#endif if ((tty->count == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk("rs_close: bad serial port count; tty->count is 1, " "info->count is %d\n", info->count); info->count = 1; } if (--info->count < 0) { printk("rs_close: bad serial port count for ttys%d: %d\n", info->line, info->count); info->count = 0; } if (info->count) { DBG_CNT("before DEC-2"); MOD_DEC_USE_COUNT; restore_flags(flags); 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; /* * 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 (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, info->closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the * interrupt driver to stop checking the data ready bit in the * line status register. */ info->IER &= ~UART_IER_RLSI; info->read_status_mask &= ~UART_LSR_DR; if (info->flags & ASYNC_INITIALIZED) { serial_out(info, UART_IER, info->IER); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ timeout = jiffies+HZ; while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) { current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + info->timeout; schedule(); if (jiffies > timeout) break; } } shutdown(info); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); tty->closing = 0; info->event = 0; info->tty = 0; if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + info->close_delay; schedule(); } wake_up_interruptible(&info->open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); MOD_DEC_USE_COUNT; restore_flags(flags);}/* * rs_hangup() --- called by tty_hangup() when a hangup is signaled. */void rs_hangup(struct tty_struct *tty){ struct async_struct * info = (struct async_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_hangup")) return; rs_flush_buffer(tty); shutdown(info); info->event = 0; info->count = 0; info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); info->tty = 0; wake_up_interruptible(&info->open_wa
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -