📄 serial.c
字号:
info->hub6 = new_serial.hub6; } if(info->type != PORT_UNKNOWN) request_region(info->port,8,"serial(set)"); 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); } else retval = startup(info); return retval;}/* * 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 get_lsr_info(struct async_struct * info, unsigned int *value){ unsigned char status; unsigned int result; cli(); status = serial_in(info, UART_LSR); sti(); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); put_user(result,value); return 0;}static int get_modem_info(struct async_struct * info, unsigned int *value){ unsigned char control, status; unsigned int result; control = info->MCR; cli(); 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_user(result,value); return 0;}static int set_modem_info(struct async_struct * info, unsigned int cmd, unsigned int *value){ int error; unsigned int arg; error = verify_area(VERIFY_READ, value, sizeof(int)); if (error) return error; arg = get_user(value); switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS) { info->MCR |= UART_MCR_RTS; info->MCR_noint |= UART_MCR_RTS; } if (arg & TIOCM_DTR) { info->MCR |= UART_MCR_DTR; info->MCR_noint |= UART_MCR_DTR; } break; case TIOCMBIC: if (arg & TIOCM_RTS) { info->MCR &= ~UART_MCR_RTS; info->MCR_noint &= ~UART_MCR_RTS; } if (arg & TIOCM_DTR) { info->MCR &= ~UART_MCR_DTR; info->MCR_noint &= ~UART_MCR_DTR; } break; case TIOCMSET: info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR)) | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); info->MCR_noint = ((info->MCR_noint & ~(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, info->MCR); 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); cli(); autoconfig(info); sti(); retval = startup(info); 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+HZ/10; while (timeout >= jiffies) ; rs_triggered = 0; /* Reset after letting things settle */ timeout = jiffies+HZ/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 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[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[info->irq]) return -EINVAL; multi = &rs_multiport[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[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;}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 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); 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 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); 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); 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; 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); /* * 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)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -