📄 esp.c
字号:
change_irq = new_serial.irq != info->irq; if (change_irq && (info->line % 8)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) { if (change_irq || (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; } else { if (new_serial.irq == 2) new_serial.irq = 9; if (change_irq) { current_async = ports; while (current_async) { if ((current_async->line >= info->line) && (current_async->line < (info->line + 8))) { if (current_async == info) { if (current_async->count > 1) return -EBUSY; } else if (current_async->count) return -EBUSY; } current_async = current_async->next_port; } } /* * OK, past this point, all the error checking has been done. * At this point, we start making changes..... */ info->flags = ((info->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->custom_divisor = new_serial.custom_divisor; info->close_delay = new_serial.close_delay * HZ/100; info->closing_wait = new_serial.closing_wait * HZ/100; if (change_irq) { /* * We need to shutdown the serial port at the old * port/irq combination. */ shutdown(info); current_async = ports; while (current_async) { if ((current_async->line >= info->line) && (current_async->line < (info->line + 8))) current_async->irq = new_serial.irq; current_async = current_async->next_port; } serial_out(info, UART_ESI_CMD1, ESI_SET_ENH_IRQ); if (info->irq == 9) serial_out(info, UART_ESI_CMD2, 0x02); else serial_out(info, UART_ESI_CMD2, info->irq); } } if (info->flags & ASYNC_INITIALIZED) { if (((old_info.flags & ASYNC_SPD_MASK) != (info->flags & ASYNC_SPD_MASK)) || (old_info.custom_divisor != info->custom_divisor)) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; change_speed(info); } } else retval = startup(info); return retval;}static int set_esp_config(struct esp_struct * info, struct hayes_esp_config __user * new_info){ struct hayes_esp_config new_config; unsigned int change_dma; int retval = 0; struct esp_struct *current_async; unsigned long flags; /* Perhaps a non-sysadmin user should be able to do some of these */ /* operations. I haven't decided yet. */ if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (copy_from_user(&new_config, new_info, sizeof(new_config))) return -EFAULT; if ((new_config.flow_on >= new_config.flow_off) || (new_config.rx_trigger < 1) || (new_config.tx_trigger < 1) || (new_config.flow_off < 1) || (new_config.flow_on < 1) || (new_config.rx_trigger > 1023) || (new_config.tx_trigger > 1023) || (new_config.flow_off > 1023) || (new_config.flow_on > 1023) || (new_config.pio_threshold < 0) || (new_config.pio_threshold > 1024)) return -EINVAL; if ((new_config.dma_channel != 1) && (new_config.dma_channel != 3)) new_config.dma_channel = 0; if (info->stat_flags & ESP_STAT_NEVER_DMA) change_dma = new_config.dma_channel; else change_dma = (new_config.dma_channel != dma); if (change_dma) { if (new_config.dma_channel) { /* PIO mode to DMA mode transition OR */ /* change current DMA channel */ current_async = ports; while (current_async) { if (current_async == info) { if (current_async->count > 1) return -EBUSY; } else if (current_async->count) return -EBUSY; current_async = current_async->next_port; } shutdown(info); dma = new_config.dma_channel; info->stat_flags &= ~ESP_STAT_NEVER_DMA; /* all ports must use the same DMA channel */ spin_lock_irqsave(&info->lock, flags); current_async = ports; while (current_async) { esp_basic_init(current_async); current_async = current_async->next_port; } spin_unlock_irqrestore(&info->lock, flags); } else { /* DMA mode to PIO mode only */ if (info->count > 1) return -EBUSY; shutdown(info); spin_lock_irqsave(&info->lock, flags); info->stat_flags |= ESP_STAT_NEVER_DMA; esp_basic_init(info); spin_unlock_irqrestore(&info->lock, flags); } } info->config.pio_threshold = new_config.pio_threshold; if ((new_config.flow_off != info->config.flow_off) || (new_config.flow_on != info->config.flow_on)) { unsigned long flags; info->config.flow_off = new_config.flow_off; info->config.flow_on = new_config.flow_on; spin_lock_irqsave(&info->lock, flags); serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_LVL); serial_out(info, UART_ESI_CMD2, new_config.flow_off >> 8); serial_out(info, UART_ESI_CMD2, new_config.flow_off); serial_out(info, UART_ESI_CMD2, new_config.flow_on >> 8); serial_out(info, UART_ESI_CMD2, new_config.flow_on); spin_unlock_irqrestore(&info->lock, flags); } if ((new_config.rx_trigger != info->config.rx_trigger) || (new_config.tx_trigger != info->config.tx_trigger)) { unsigned long flags; info->config.rx_trigger = new_config.rx_trigger; info->config.tx_trigger = new_config.tx_trigger; spin_lock_irqsave(&info->lock, flags); serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER); serial_out(info, UART_ESI_CMD2, new_config.rx_trigger >> 8); serial_out(info, UART_ESI_CMD2, new_config.rx_trigger); serial_out(info, UART_ESI_CMD2, new_config.tx_trigger >> 8); serial_out(info, UART_ESI_CMD2, new_config.tx_trigger); spin_unlock_irqrestore(&info->lock, flags); } if (new_config.rx_timeout != info->config.rx_timeout) { unsigned long flags; info->config.rx_timeout = new_config.rx_timeout; spin_lock_irqsave(&info->lock, flags); if (info->IER & UART_IER_RDI) { serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT); serial_out(info, UART_ESI_CMD2, new_config.rx_timeout); } spin_unlock_irqrestore(&info->lock, flags); } if (!(info->flags & ASYNC_INITIALIZED)) 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 esp_struct * info, unsigned int __user *value){ unsigned char status; unsigned int result; unsigned long flags; spin_lock_irqsave(&info->lock, flags); serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT); status = serial_in(info, UART_ESI_STAT1); spin_unlock_irqrestore(&info->lock, flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); return put_user(result,value);}static int esp_tiocmget(struct tty_struct *tty, struct file *file){ struct esp_struct * info = (struct esp_struct *)tty->driver_data; unsigned char control, status; unsigned long flags; if (serial_paranoia_check(info, tty->name, __FUNCTION__)) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; control = info->MCR; spin_lock_irqsave(&info->lock, flags); serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT); status = serial_in(info, UART_ESI_STAT2); spin_unlock_irqrestore(&info->lock, flags); return ((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);}static int esp_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear){ struct esp_struct * info = (struct esp_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, __FUNCTION__)) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; spin_lock_irqsave(&info->lock, flags); if (set & TIOCM_RTS) info->MCR |= UART_MCR_RTS; if (set & TIOCM_DTR) info->MCR |= UART_MCR_DTR; if (clear & TIOCM_RTS) info->MCR &= ~UART_MCR_RTS; if (clear & TIOCM_DTR) info->MCR &= ~UART_MCR_DTR; serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART); serial_out(info, UART_ESI_CMD2, UART_MCR); serial_out(info, UART_ESI_CMD2, info->MCR); spin_unlock_irqrestore(&info->lock, flags); return 0;}/* * rs_break() --- routine which turns the break handling on or off */static void esp_break(struct tty_struct *tty, int break_state){ struct esp_struct * info = (struct esp_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "esp_break")) return; if (break_state == -1) { spin_lock_irqsave(&info->lock, flags); serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK); serial_out(info, UART_ESI_CMD2, 0x01); spin_unlock_irqrestore(&info->lock, flags); /* FIXME - new style wait needed here */ interruptible_sleep_on(&info->break_wait); } else { spin_lock_irqsave(&info->lock, flags); serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK); serial_out(info, UART_ESI_CMD2, 0x00); spin_unlock_irqrestore(&info->lock, flags); }}static int rs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ struct esp_struct * info = (struct esp_struct *)tty->driver_data; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct __user *p_cuser; /* user space */ void __user *argp = (void __user *)arg; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_ioctl")) return -ENODEV; if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT) && (cmd != TIOCGHAYESESP) && (cmd != TIOCSHAYESESP)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } switch (cmd) { case TIOCGSERIAL: return get_serial_info(info, argp); case TIOCSSERIAL: return set_serial_info(info, argp); case TIOCSERCONFIG: /* do not reconfigure after initial configuration */ return 0; case TIOCSERGWILD: return put_user(0L, (unsigned long __user *)argp); case TIOCSERGETLSR: /* Get line status register */ return get_lsr_info(info, argp); case TIOCSERSWILD: if (!capable(CAP_SYS_ADMIN)) return -EPERM; return 0; /* * 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: spin_lock_irqsave(&info->lock, flags); cprev = info->icount; /* note the counters on entry */ spin_unlock_irqrestore(&info->lock, flags); while (1) { /* FIXME: convert to new style wakeup */ interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; spin_lock_irqsave(&info->lock, flags); cnow = info->icount; /* atomic copy */ spin_unlock_irqrestore(&info->lock, flags); 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: spin_lock_irqsave(&info->lock, flags); cnow = info->icount; spin_unlock_irqrestore(&info->lock, flags); p_cuser = argp; if (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 -EFAULT; return 0; case TIOCGHAYESESP: return get_esp_config(info, argp); case TIOCSHAYESESP: return set_esp_config(info, argp); default: return -ENOIOCTLCMD; } return 0;}static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios){ struct esp_struct *info = (struct esp_struct *)tty->driver_data; unsigned long flags; change_speed(info); spin_lock_irqsave(&info->lock, flags); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) { info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART); serial_out(info, UART_ESI_CMD2, UART_MCR); serial_out(info, UART_ESI_CMD2, info->MCR); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) { info->MCR |= (UART_MCR_DTR | UART_MCR_RTS); serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART); serial_out(info, UART_ESI_CMD2, UART_MCR); serial_out(info, UART_ESI_CMD2, info->MCR); } spin_unlock_irqrestore(&info->lock, flags); /* Handle turning of CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { rs_start(tty); }}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -