📄 esp.c
字号:
tmp.flags = info->flags; tmp.xmit_fifo_size = 1024; tmp.baud_base = BASE_BAUD; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; tmp.custom_divisor = info->custom_divisor; tmp.hub6 = 0; if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0;}static int get_esp_config(struct esp_struct * info, struct hayes_esp_config * retinfo){ struct hayes_esp_config tmp; if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); tmp.rx_timeout = info->config.rx_timeout; tmp.rx_trigger = info->config.rx_trigger; tmp.tx_trigger = info->config.tx_trigger; tmp.flow_off = info->config.flow_off; tmp.flow_on = info->config.flow_on; tmp.pio_threshold = info->config.pio_threshold; tmp.dma_channel = (info->stat_flags & ESP_STAT_NEVER_DMA ? 0 : dma); return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;}static int set_serial_info(struct esp_struct * info, struct serial_struct * new_info){ struct serial_struct new_serial; struct esp_struct old_info; unsigned int change_irq; int retval = 0; struct esp_struct *current_async; if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; old_info = *info; if ((new_serial.type != PORT_16550A) || (new_serial.hub6) || (info->port != new_serial.port) || (new_serial.baud_base != BASE_BAUD) || (new_serial.irq > 15) || (new_serial.irq < 2) || (new_serial.irq == 6) || (new_serial.irq == 8) || (new_serial.irq == 13)) return -EINVAL; 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 * new_info){ struct hayes_esp_config new_config; unsigned int change_dma; int retval = 0; struct esp_struct *current_async; /* 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 */ current_async = ports; while (current_async) { esp_basic_init(current_async); current_async = current_async->next_port; } } else { /* DMA mode to PIO mode only */ if (info->count > 1) return -EBUSY; shutdown(info); info->stat_flags |= ESP_STAT_NEVER_DMA; esp_basic_init(info); } } 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; save_flags(flags); cli(); 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); restore_flags(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; save_flags(flags); cli(); 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); restore_flags(flags); } if (new_config.rx_timeout != info->config.rx_timeout) { unsigned long flags; info->config.rx_timeout = new_config.rx_timeout; save_flags(flags); cli(); 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); } restore_flags(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 *value){ unsigned char status; unsigned int result; cli(); serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT); status = serial_in(info, UART_ESI_STAT1); sti(); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); return put_user(result,value);}static int get_modem_info(struct esp_struct * info, unsigned int *value){ unsigned char control, status; unsigned int result; control = info->MCR; cli(); serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT); status = serial_in(info, UART_ESI_STAT2); 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); return put_user(result,value);}static int set_modem_info(struct esp_struct * info, unsigned int cmd, unsigned int *value){ unsigned int arg; if (get_user(arg, value)) return -EFAULT; switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS) info->MCR |= UART_MCR_RTS; if (arg & TIOCM_DTR) info->MCR |= UART_MCR_DTR; break; case TIOCMBIC: if (arg & TIOCM_RTS) info->MCR &= ~UART_MCR_RTS; if (arg & TIOCM_DTR) info->MCR &= ~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)); break; default: return -EINVAL; } cli(); 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); sti(); 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->device, "esp_break")) return; save_flags(flags); cli(); if (break_state == -1) { serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK); serial_out(info, UART_ESI_CMD2, 0x01); interruptible_sleep_on(&info->break_wait); } else { serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK); serial_out(info, UART_ESI_CMD2, 0x00); } restore_flags(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 *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) && (cmd != TIOCGHAYESESP) && (cmd != TIOCSHAYESESP)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } switch (cmd) { case TIOCMGET: 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: return get_serial_info(info, (struct serial_struct *) arg); case TIOCSSERIAL: return set_serial_info(info, (struct serial_struct *) arg); case TIOCSERCONFIG: /* do not reconfigure after initial configuration */ return 0; case TIOCSERGWILD: return put_user(0L, (unsigned long *) arg); case TIOCSERGETLSR: /* Get line status register */ return get_lsr_info(info, (unsigned int *) arg); 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: 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 (signal_pending(current)) 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: cli(); cnow = info->icount; sti(); p_cuser = (struct serial_icounter_struct *) arg; 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, (struct hayes_esp_config *)arg));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -