📄 esp.c
字号:
ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); } } release_pio_buffer(pio_buf);}/* Caller must hold info->lock */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, isa_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, isa_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 {#ifdef SERIAL_DEBUG_OPEN printk("scheduling hangup...");#endif schedule_work(&info->tqueue_hangup); } }}/* * This is the serial driver's interrupt routine */static irqreturn_t rs_interrupt_single(int irq, void *dev_id){ 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); spin_lock(&info->lock); if (!info->tty) { spin_unlock(&info->lock); return IRQ_NONE; } 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); num_bytes = tty_buffer_request_room(info->tty, num_bytes); 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 spin_unlock(&info->lock); return IRQ_HANDLED;}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- */static void do_softint(struct work_struct *work){ struct esp_struct *info = container_of(work, struct esp_struct, tqueue); struct tty_struct *tty; tty = info->tty; if (!tty) return; if (test_and_clear_bit(ESP_EVENT_WRITE_WAKEUP, &info->event)) { tty_wakeup(tty); }}/* * 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(struct work_struct *work){ struct esp_struct *info = container_of(work, struct esp_struct, tqueue_hangup); struct tty_struct *tty; tty = info->tty; if (tty) tty_hangup(tty);}/* * --------------------------------------------------------------- * 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. * * Caller should hold lock * --------------------------------------------------------------- */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; spin_lock_irqsave(&info->lock, flags); if (info->flags & ASYNC_INITIALIZED) goto out; if (!info->xmit_buf) { info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_ATOMIC); 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; spin_unlock_irqrestore(&info->lock, flags); /* * Allocate the IRQ */ retval = request_irq(info->irq, rs_interrupt_single, IRQF_SHARED, "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_unlocked; } 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 = NULL; info->stat_flags |= ESP_STAT_USE_PIO; } } info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; spin_lock_irqsave(&info->lock, flags); 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); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; spin_unlock_irqrestore(&info->lock, flags); /* * 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; return 0;out: spin_unlock_irqrestore(&info->lock, flags);out_unlocked: 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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -