📄 esp.c
字号:
sti(); for (i = 0; i < space_avail - 1; i += 2) { outw(*((unsigned short *)(pio_buf->data + i)), info->port + UART_ESI_TX); } if (space_avail & 0x0001) serial_out(info, UART_ESI_TX, pio_buf->data[space_avail - 1]); cli(); if (info->xmit_cnt) { serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND); serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL); space_avail = serial_in(info, UART_ESI_STAT1) << 8; space_avail |= serial_in(info, UART_ESI_STAT2); if (space_avail > info->xmit_cnt) space_avail = info->xmit_cnt; } } if (info->xmit_cnt < WAKEUP_CHARS) { rs_sched_event(info, ESP_EVENT_WRITE_WAKEUP);#ifdef SERIAL_DEBUG_INTR printk("THRE...");#endif if (info->xmit_cnt <= 0) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); } } release_pio_buffer(pio_buf);}static _INLINE_ void transmit_chars_dma(struct esp_struct *info, int num_bytes){ unsigned long flags; dma_bytes = num_bytes; if (info->xmit_tail + dma_bytes <= ESP_XMIT_SIZE) { memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]), dma_bytes); } else { int i = ESP_XMIT_SIZE - info->xmit_tail; memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]), i); memcpy(&(dma_buffer[i]), info->xmit_buf, dma_bytes - i); } info->xmit_cnt -= dma_bytes; info->xmit_tail = (info->xmit_tail + dma_bytes) & (ESP_XMIT_SIZE - 1); if (info->xmit_cnt < WAKEUP_CHARS) { rs_sched_event(info, ESP_EVENT_WRITE_WAKEUP);#ifdef SERIAL_DEBUG_INTR printk("THRE...");#endif if (info->xmit_cnt <= 0) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); } } info->stat_flags |= ESP_STAT_DMA_TX; flags=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); set_dma_mode(dma, DMA_MODE_WRITE); set_dma_addr(dma, virt_to_bus(dma_buffer)); set_dma_count(dma, dma_bytes); enable_dma(dma); release_dma_lock(flags); serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX);}static _INLINE_ void transmit_chars_dma_done(struct esp_struct *info){ int num_bytes; unsigned long flags; flags=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); num_bytes = dma_bytes - get_dma_residue(dma); info->icount.tx += dma_bytes; release_dma_lock(flags); if (dma_bytes != num_bytes) { dma_bytes -= num_bytes; memmove(dma_buffer, dma_buffer + num_bytes, dma_bytes); flags=claim_dma_lock(); disable_dma(dma); clear_dma_ff(dma); set_dma_mode(dma, DMA_MODE_WRITE); set_dma_addr(dma, virt_to_bus(dma_buffer)); set_dma_count(dma, dma_bytes); enable_dma(dma); release_dma_lock(flags); serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX); } else { dma_bytes = 0; info->stat_flags &= ~ESP_STAT_DMA_TX; }}static _INLINE_ void check_modem_status(struct esp_struct *info){ int status; serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT); status = serial_in(info, UART_ESI_STAT2); if (status & UART_MSR_ANY_DELTA) { /* update input line counters */ if (status & UART_MSR_TERI) info->icount.rng++; if (status & UART_MSR_DDSR) info->icount.dsr++; if (status & UART_MSR_DDCD) info->icount.dcd++; if (status & UART_MSR_DCTS) info->icount.cts++; wake_up_interruptible(&info->delta_msr_wait); } if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttys%d CD now %s...", info->line, (status & UART_MSR_DCD) ? "on" : "off");#endif if (status & UART_MSR_DCD) wake_up_interruptible(&info->open_wait); else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_CALLOUT_NOHUP))) {#ifdef SERIAL_DEBUG_OPEN printk("scheduling hangup...");#endif MOD_INC_USE_COUNT; if (schedule_task(&info->tqueue_hangup) == 0) MOD_DEC_USE_COUNT; } }}/* * This is the serial driver's interrupt routine */static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs){ struct esp_struct * info; unsigned err_status; unsigned int scratch;#ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_single(%d)...", irq);#endif info = (struct esp_struct *)dev_id; err_status = 0; scratch = serial_in(info, UART_ESI_SID); cli(); if (!info->tty) { sti(); return; } if (scratch & 0x04) { /* error */ serial_out(info, UART_ESI_CMD1, ESI_GET_ERR_STAT); err_status = serial_in(info, UART_ESI_STAT1); serial_in(info, UART_ESI_STAT2); if (err_status & 0x01) info->stat_flags |= ESP_STAT_RX_TIMEOUT; if (err_status & 0x20) /* UART status */ check_modem_status(info); if (err_status & 0x80) /* Start break */ wake_up_interruptible(&info->break_wait); } if ((scratch & 0x88) || /* DMA completed or timed out */ (err_status & 0x1c) /* receive error */) { if (info->stat_flags & ESP_STAT_DMA_RX) receive_chars_dma_done(info, err_status); else if (info->stat_flags & ESP_STAT_DMA_TX) transmit_chars_dma_done(info); } if (!(info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) && ((scratch & 0x01) || (info->stat_flags & ESP_STAT_RX_TIMEOUT)) && (info->IER & UART_IER_RDI)) { int num_bytes; serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND); serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL); num_bytes = serial_in(info, UART_ESI_STAT1) << 8; num_bytes |= serial_in(info, UART_ESI_STAT2); if (num_bytes > (TTY_FLIPBUF_SIZE - info->tty->flip.count)) num_bytes = TTY_FLIPBUF_SIZE - info->tty->flip.count; if (num_bytes) { if (dma_bytes || (info->stat_flags & ESP_STAT_USE_PIO) || (num_bytes <= info->config.pio_threshold)) receive_chars_pio(info, num_bytes); else receive_chars_dma(info, num_bytes); } } if (!(info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) && (scratch & 0x02) && (info->IER & UART_IER_THRI)) { if ((info->xmit_cnt <= 0) || info->tty->stopped) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); } else { int num_bytes; serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND); serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL); num_bytes = serial_in(info, UART_ESI_STAT1) << 8; num_bytes |= serial_in(info, UART_ESI_STAT2); if (num_bytes > info->xmit_cnt) num_bytes = info->xmit_cnt; if (num_bytes) { if (dma_bytes || (info->stat_flags & ESP_STAT_USE_PIO) || (num_bytes <= info->config.pio_threshold)) transmit_chars_pio(info, num_bytes); else transmit_chars_dma(info, num_bytes); } } } info->last_active = jiffies;#ifdef SERIAL_DEBUG_INTR printk("end.\n");#endif sti();}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_sched_event(), and they get done here. */static void do_serial_bh(void){ run_task_queue(&tq_esp);}static void do_softint(void *private_){ struct esp_struct *info = (struct esp_struct *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; if (test_and_clear_bit(ESP_EVENT_WRITE_WAKEUP, &info->event)) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); }}/* * This routine is called from the scheduler tqueue when the interrupt * routine has signalled that a hangup has occurred. The path of * hangup processing is: * * serial interrupt routine -> (scheduler tqueue) -> * do_serial_hangup() -> tty->hangup() -> esp_hangup() * */static void do_serial_hangup(void *private_){ struct esp_struct *info = (struct esp_struct *) private_; struct tty_struct *tty; tty = info->tty; if (tty) tty_hangup(tty); MOD_DEC_USE_COUNT;}/* * --------------------------------------------------------------- * Low level utility subroutines for the serial driver: routines to * figure out the appropriate timeout for an interrupt chain, routines * to initialize and startup a serial port, and routines to shutdown a * serial port. Useful stuff like that. * --------------------------------------------------------------- */static _INLINE_ void esp_basic_init(struct esp_struct * info){ /* put ESPC in enhanced mode */ serial_out(info, UART_ESI_CMD1, ESI_SET_MODE); if (info->stat_flags & ESP_STAT_NEVER_DMA) serial_out(info, UART_ESI_CMD2, 0x01); else serial_out(info, UART_ESI_CMD2, 0x31); /* disable interrupts for now */ serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, 0x00); /* set interrupt and DMA channel */ serial_out(info, UART_ESI_CMD1, ESI_SET_IRQ); if (info->stat_flags & ESP_STAT_NEVER_DMA) serial_out(info, UART_ESI_CMD2, 0x01); else serial_out(info, UART_ESI_CMD2, (dma << 4) | 0x01); serial_out(info, UART_ESI_CMD1, ESI_SET_ENH_IRQ); if (info->line % 8) /* secondary port */ serial_out(info, UART_ESI_CMD2, 0x0d); /* shared */ else if (info->irq == 9) serial_out(info, UART_ESI_CMD2, 0x02); else serial_out(info, UART_ESI_CMD2, info->irq); /* set error status mask (check this) */ serial_out(info, UART_ESI_CMD1, ESI_SET_ERR_MASK); if (info->stat_flags & ESP_STAT_NEVER_DMA) serial_out(info, UART_ESI_CMD2, 0xa1); else serial_out(info, UART_ESI_CMD2, 0xbd); serial_out(info, UART_ESI_CMD2, 0x00); /* set DMA timeout */ serial_out(info, UART_ESI_CMD1, ESI_SET_DMA_TMOUT); serial_out(info, UART_ESI_CMD2, 0xff); /* set FIFO trigger levels */ serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER); serial_out(info, UART_ESI_CMD2, info->config.rx_trigger >> 8); serial_out(info, UART_ESI_CMD2, info->config.rx_trigger); serial_out(info, UART_ESI_CMD2, info->config.tx_trigger >> 8); serial_out(info, UART_ESI_CMD2, info->config.tx_trigger); /* Set clock scaling and wait states */ serial_out(info, UART_ESI_CMD1, ESI_SET_PRESCALAR); serial_out(info, UART_ESI_CMD2, 0x04 | ESPC_SCALE); /* set reinterrupt pacing */ serial_out(info, UART_ESI_CMD1, ESI_SET_REINTR); serial_out(info, UART_ESI_CMD2, 0xff);}static int startup(struct esp_struct * info){ unsigned long flags; int retval=0; unsigned int num_chars; save_flags(flags); cli(); if (info->flags & ASYNC_INITIALIZED) goto out; if (!info->xmit_buf) { info->xmit_buf = (unsigned char *)get_free_page(GFP_KERNEL); retval = -ENOMEM; if (!info->xmit_buf) goto out; }#ifdef SERIAL_DEBUG_OPEN printk("starting up ttys%d (irq %d)...", info->line, info->irq);#endif /* Flush the RX buffer. Using the ESI flush command may cause */ /* wild interrupts, so read all the data instead. */ serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND); serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL); num_chars = serial_in(info, UART_ESI_STAT1) << 8; num_chars |= serial_in(info, UART_ESI_STAT2); while (num_chars > 1) { inw(info->port + UART_ESI_RX); num_chars -= 2; } if (num_chars) serial_in(info, UART_ESI_RX); /* set receive character timeout */ serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT); serial_out(info, UART_ESI_CMD2, info->config.rx_timeout); /* clear all flags except the "never DMA" flag */ info->stat_flags &= ESP_STAT_NEVER_DMA; if (info->stat_flags & ESP_STAT_NEVER_DMA) info->stat_flags |= ESP_STAT_USE_PIO; /* * Allocate the IRQ */ retval = request_irq(info->irq, rs_interrupt_single, SA_SHIRQ, "esp serial", info); if (retval) { if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); retval = 0; } goto out; } if (!(info->stat_flags & ESP_STAT_USE_PIO) && !dma_buffer) { dma_buffer = (char *)__get_dma_pages( GFP_KERNEL, get_order(DMA_BUFFER_SZ)); /* use PIO mode if DMA buf/chan cannot be allocated */ if (!dma_buffer) info->stat_flags |= ESP_STAT_USE_PIO; else if (request_dma(dma, "esp serial")) { free_pages((unsigned long)dma_buffer, get_order(DMA_BUFFER_SZ)); dma_buffer = 0; info->stat_flags |= ESP_STAT_USE_PIO; } } info->MCR = UART_MCR_DTR | UART_MCR_RTS | 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); /* * Finally, enable interrupts */ /* info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; */ info->IER = UART_IER_RLSI | UART_IER_RDI | UART_IER_DMA_TMOUT | UART_IER_DMA_TC; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -