📄 esp.c
字号:
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * Set up the tty->alt_speed kludge */ if (info->tty) { 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; } /* * set the speed of the serial port */ change_speed(info); info->flags |= ASYNC_INITIALIZED; retval = 0;out: restore_flags(flags); return retval;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */static void shutdown(struct esp_struct * info){ unsigned long flags, f; if (!(info->flags & ASYNC_INITIALIZED)) return;#ifdef SERIAL_DEBUG_OPEN printk("Shutting down serial port %d (irq %d)....", info->line, info->irq);#endif save_flags(flags); cli(); /* Disable interrupts */ /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be waken up */ wake_up_interruptible(&info->delta_msr_wait); wake_up_interruptible(&info->break_wait); /* stop a DMA transfer on the port being closed */ if (info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) { f=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); release_dma_lock(f); dma_bytes = 0; } /* * Free the IRQ */ free_irq(info->irq, info); if (dma_buffer) { struct esp_struct *current_port = ports; while (current_port) { if ((current_port != info) && (current_port->flags & ASYNC_INITIALIZED)) break; current_port = current_port->next_port; } if (!current_port) { free_dma(dma); free_pages((unsigned long)dma_buffer, get_order(DMA_BUFFER_SZ)); dma_buffer = 0; } } if (info->xmit_buf) { free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } info->IER = 0; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, 0x00); if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); info->MCR &= ~UART_MCR_OUT2; 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); if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags);}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void change_speed(struct esp_struct *info){ unsigned short port; int quot = 0; unsigned cflag,cval; int baud, bits; unsigned char flow1 = 0, flow2 = 0; unsigned long flags; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; port = info->port; /* byte size and parity */ switch (cflag & CSIZE) { case CS5: cval = 0x00; bits = 7; break; case CS6: cval = 0x01; bits = 8; break; case CS7: cval = 0x02; bits = 9; break; case CS8: cval = 0x03; bits = 10; break; default: cval = 0x00; bits = 7; break; } if (cflag & CSTOPB) { cval |= 0x04; bits++; } if (cflag & PARENB) { cval |= UART_LCR_PARITY; bits++; } if (!(cflag & PARODD)) cval |= UART_LCR_EPAR;#ifdef CMSPAR if (cflag & CMSPAR) cval |= UART_LCR_SPAR;#endif baud = tty_get_baud_rate(info->tty); if (baud == 38400 && ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2*BASE_BAUD / 269); else if (baud) quot = BASE_BAUD / baud; } /* If the quotient is ever zero, default to 9600 bps */ if (!quot) quot = BASE_BAUD / 9600; info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50); /* CTS flow control flag and modem status interrupts */ /* info->IER &= ~UART_IER_MSI; */ if (cflag & CRTSCTS) { info->flags |= ASYNC_CTS_FLOW; /* info->IER |= UART_IER_MSI; */ flow1 = 0x04; flow2 = 0x10; } else info->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; else { info->flags |= ASYNC_CHECK_CD; /* info->IER |= UART_IER_MSI; */ } /* * Set up parity check flag */#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (I_INPCK(info->tty)) info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) info->read_status_mask |= UART_LSR_BI; info->ignore_status_mask = 0;#if 0 /* This should be safe, but for some broken bits of hardware... */ if (I_IGNPAR(info->tty)) { info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_PE | UART_LSR_FE; }#endif if (I_IGNBRK(info->tty)) { info->ignore_status_mask |= UART_LSR_BI; info->read_status_mask |= UART_LSR_BI; /* * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support). */ if (I_IGNPAR(info->tty)) { info->ignore_status_mask |= UART_LSR_OE | \ UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_OE | \ UART_LSR_PE | UART_LSR_FE; } } if (I_IXOFF(info->tty)) flow1 |= 0x81; save_flags(flags); cli(); /* set baud */ serial_out(info, UART_ESI_CMD1, ESI_SET_BAUD); serial_out(info, UART_ESI_CMD2, quot >> 8); serial_out(info, UART_ESI_CMD2, quot & 0xff); /* set data bits, parity, etc. */ serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART); serial_out(info, UART_ESI_CMD2, UART_LCR); serial_out(info, UART_ESI_CMD2, cval); /* Enable flow control */ serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_CNTL); serial_out(info, UART_ESI_CMD2, flow1); serial_out(info, UART_ESI_CMD2, flow2); /* set flow control characters (XON/XOFF only) */ if (I_IXOFF(info->tty)) { serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_CHARS); serial_out(info, UART_ESI_CMD2, START_CHAR(info->tty)); serial_out(info, UART_ESI_CMD2, STOP_CHAR(info->tty)); serial_out(info, UART_ESI_CMD2, 0x10); serial_out(info, UART_ESI_CMD2, 0x21); switch (cflag & CSIZE) { case CS5: serial_out(info, UART_ESI_CMD2, 0x1f); break; case CS6: serial_out(info, UART_ESI_CMD2, 0x3f); break; case CS7: case CS8: serial_out(info, UART_ESI_CMD2, 0x7f); break; default: serial_out(info, UART_ESI_CMD2, 0xff); break; } } /* Set high/low water */ serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_LVL); serial_out(info, UART_ESI_CMD2, info->config.flow_off >> 8); serial_out(info, UART_ESI_CMD2, info->config.flow_off); serial_out(info, UART_ESI_CMD2, info->config.flow_on >> 8); serial_out(info, UART_ESI_CMD2, info->config.flow_on); restore_flags(flags);}static void rs_put_char(struct tty_struct *tty, unsigned char ch){ struct esp_struct *info = (struct esp_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_put_char")) return; if (!tty || !info->xmit_buf) return; save_flags(flags); cli(); if (info->xmit_cnt >= ESP_XMIT_SIZE - 1) { restore_flags(flags); return; } info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= ESP_XMIT_SIZE-1; info->xmit_cnt++; restore_flags(flags);}static void rs_flush_chars(struct tty_struct *tty){ struct esp_struct *info = (struct esp_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) return; if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf) return; save_flags(flags); cli(); if (!(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); } restore_flags(flags);}static int rs_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count){ int c, t, ret = 0; struct esp_struct *info = (struct esp_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_write")) return 0; if (!tty || !info->xmit_buf || !tmp_buf) return 0; if (from_user) down(&tmp_buf_sem); while (1) { /* Thanks to R. Wolff for suggesting how to do this with */ /* interrupts enabled */ c = count; t = ESP_XMIT_SIZE - info->xmit_cnt - 1; if (t < c) c = t; t = ESP_XMIT_SIZE - info->xmit_head; if (t < c) c = t; if (c <= 0) break; if (from_user) { c -= copy_from_user(tmp_buf, buf, c); if (!c) { if (!ret) ret = -EFAULT; break; } memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); } else memcpy(info->xmit_buf + info->xmit_head, buf, c); info->xmit_head = (info->xmit_head + c) & (ESP_XMIT_SIZE-1); info->xmit_cnt += c; buf += c; count -= c; ret += c; } if (from_user) up(&tmp_buf_sem); save_flags(flags); cli(); if (info->xmit_cnt && !tty->stopped && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); } restore_flags(flags); return ret;}static int rs_write_room(struct tty_struct *tty){ struct esp_struct *info = (struct esp_struct *)tty->driver_data; int ret; if (serial_paranoia_check(info, tty->device, "rs_write_room")) return 0; ret = ESP_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) ret = 0; return ret;}static int rs_chars_in_buffer(struct tty_struct *tty){ struct esp_struct *info = (struct esp_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) return 0; return info->xmit_cnt;}static void rs_flush_buffer(struct tty_struct *tty){ struct esp_struct *info = (struct esp_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) return; cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; sti(); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty);}/* * ------------------------------------------------------------ * rs_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void rs_throttle(struct tty_struct * tty){ struct esp_struct *info = (struct esp_struct *)tty->driver_data;#ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("throttle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->device, "rs_throttle")) return; cli(); info->IER &= ~UART_IER_RDI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT); serial_out(info, UART_ESI_CMD2, 0x00); sti();}static void rs_unthrottle(struct tty_struct * tty){ struct esp_struct *info = (struct esp_struct *)tty->driver_data;#ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("unthrottle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) return; cli(); info->IER |= UART_IER_RDI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT); serial_out(info, UART_ESI_CMD2, info->config.rx_timeout); sti();}/* * ------------------------------------------------------------ * rs_ioctl() and friends * ------------------------------------------------------------ */static int get_serial_info(struct esp_struct * info, struct serial_struct * retinfo){ struct serial_struct tmp; if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); tmp.type = PORT_16550A; tmp.line = info->line; tmp.port = info->port; tmp.irq = info->irq;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -