📄 esp.c
字号:
info->irq);#endif spin_lock_irqsave(&info->lock, flags); /* * 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 */ /* DMA lock is higher priority always */ 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 = NULL; } } if (info->xmit_buf) { free_page((unsigned long) info->xmit_buf); info->xmit_buf = NULL; } 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; spin_unlock_irqrestore(&info->lock, 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 */ 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; spin_lock_irqsave(&info->lock, flags); /* 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); spin_unlock_irqrestore(&info->lock, 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->name, "rs_put_char")) return; if (!info->xmit_buf) return; spin_lock_irqsave(&info->lock, flags); if (info->xmit_cnt < ESP_XMIT_SIZE - 1) { info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= ESP_XMIT_SIZE-1; info->xmit_cnt++; } spin_unlock_irqrestore(&info->lock, 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->name, "rs_flush_chars")) return; spin_lock_irqsave(&info->lock, flags); if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf) goto out; 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); }out: spin_unlock_irqrestore(&info->lock, flags);}static int rs_write(struct tty_struct * tty, 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->name, "rs_write")) return 0; if (!info->xmit_buf) return 0; 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; 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; } spin_lock_irqsave(&info->lock, flags); 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); } spin_unlock_irqrestore(&info->lock, flags); return ret;}static int rs_write_room(struct tty_struct *tty){ struct esp_struct *info = (struct esp_struct *)tty->driver_data; int ret; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_write_room")) return 0; spin_lock_irqsave(&info->lock, flags); ret = ESP_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) ret = 0; spin_unlock_irqrestore(&info->lock, flags); 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->name, "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; unsigned long flags; if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) return; spin_lock_irqsave(&info->lock, flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; spin_unlock_irqrestore(&info->lock, flags); tty_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; unsigned long flags;#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->name, "rs_throttle")) return; spin_lock_irqsave(&info->lock, flags); 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); spin_unlock_irqrestore(&info->lock, flags);}static void rs_unthrottle(struct tty_struct * tty){ struct esp_struct *info = (struct esp_struct *)tty->driver_data; unsigned long flags;#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->name, "rs_unthrottle")) return; spin_lock_irqsave(&info->lock, flags); 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); spin_unlock_irqrestore(&info->lock, flags);}/* * ------------------------------------------------------------ * rs_ioctl() and friends * ------------------------------------------------------------ */static int get_serial_info(struct esp_struct * info, struct serial_struct __user *retinfo){ struct serial_struct tmp; memset(&tmp, 0, sizeof(tmp)); tmp.type = PORT_16550A; tmp.line = info->line; tmp.port = info->port; tmp.irq = info->irq; 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 __user *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 __user *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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -