📄 serial.c
字号:
e100_enable_rxdma_irq(struct e100_serial *info) {#ifdef SERIAL_DEBUG_INTR printk("rxdma_irq(%d): 1\n",info->line);#endif *R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3);}/* the tx DMA uses only dma_descr interrupt */static inline voide100_disable_txdma_irq(struct e100_serial *info) {#ifdef SERIAL_DEBUG_INTR printk("txdma_irq(%d): 0\n",info->line);#endif *R_IRQ_MASK2_CLR = info->irq;}static inline voide100_enable_txdma_irq(struct e100_serial *info) {#ifdef SERIAL_DEBUG_INTR printk("txdma_irq(%d): 1\n",info->line);#endif *R_IRQ_MASK2_SET = info->irq;}#ifdef SERIAL_HANDLE_EARLY_ERRORS/* in order to detect and fix errors on the first byte we have to use the serial interrupts as well. */static inline voide100_disable_serial_data_irq(struct e100_serial *info) {#ifdef SERIAL_DEBUG_INTR printk("ser_irq(%d): 0\n",info->line);#endif *R_IRQ_MASK1_CLR = (1U << (8+2*info->line));}static inline voide100_enable_serial_data_irq(struct e100_serial *info) {#ifdef SERIAL_DEBUG_INTR printk("ser_irq(%d): 1\n",info->line); printk("**** %d = %d\n", (8+2*info->line), (1U << (8+2*info->line)));#endif *R_IRQ_MASK1_SET = (1U << (8+2*info->line));}#endif#if defined(CONFIG_ETRAX_RS485)/* Enable RS-485 mode on selected port. This is UGLY. */static inte100_enable_rs485(struct tty_struct *tty,struct rs485_control *r){ struct e100_serial * info = (struct e100_serial *)tty->driver_data;#if defined(CONFIG_ETRAX_RS485_ON_PA) *R_PORT_PA_DATA = port_pa_data_shadow |= (1 << rs485_pa_bit);#endif info->rs485.rts_on_send = 0x01 & r->rts_on_send; info->rs485.rts_after_sent = 0x01 & r->rts_after_sent; info->rs485.delay_rts_before_send = r->delay_rts_before_send; info->rs485.enabled = r->enabled; return 0;}static inte100_write_rs485(struct tty_struct *tty,struct rs485_write *r){ int stop_delay; int total, i; int max_j, delay_ms, bits; tcflag_t cflags; int size = (*r).outc_size; struct e100_serial * info = (struct e100_serial *)tty->driver_data; struct wait_queue wait = { current, NULL }; /* If we are in RS-485 mode, we need to toggle RTS and disable * the receiver before initiating a DMA transfer */ e100_rts(info, info->rs485.rts_on_send);#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) e100_disable_rx(info); e100_disable_rxdma_irq(info);#endif if (info->rs485.delay_rts_before_send > 0) { current->timeout = jiffies + (info->rs485.delay_rts_before_send * HZ)/1000; current->state = TASK_INTERRUPTIBLE; schedule(); current->timeout = 0; } total = rs_write(tty, 1, (*r).outc, (*r).outc_size); /* If we are in RS-485 mode the following things has to be done: * wait until DMA is ready * wait on transmit shift register * wait to toggle RTS * enable the receiver */ /* wait on transmit shift register */ /* All is sent, check if we should wait more before toggling rts */ /* calc. number of bits / data byte */ cflags = info->tty->termios->c_cflag; /* databits + startbit and 1 stopbit */ if ((cflags & CSIZE) == CS7) bits = 9; else bits = 10; if (cflags & CSTOPB) /* 2 stopbits ? */ bits++; if (cflags & PARENB) /* parity bit ? */ bits++; /* calc timeout */ delay_ms = ((bits * size * 1000) / info->baud) + 1; max_j = jiffies + (delay_ms * HZ)/1000 + 10; while (jiffies < max_j) { if (info->port[REG_STATUS] & IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) { for (i = 0; i < 100; i++) ; if (info->port[REG_STATUS] & IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) { /* ~25 for loops per usec */ stop_delay = 1000000 / info->baud; if (cflags & CSTOPB) stop_delay *= 2; udelay(stop_delay); break; } } } e100_rts(info, info->rs485.rts_after_sent); #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) e100_enable_rx(info); e100_enable_rxdma_irq(info);#endif return total;}#endif/* * ------------------------------------------------------------ * rs_stop() and rs_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ *//* FIXME - when are these used and what is the purpose ? * In rs_stop we probably just can block the transmit DMA ready irq * and in rs_start we re-enable it (and then the old one will come). */static void rs_stop(struct tty_struct *tty){}static void rs_start(struct tty_struct *tty){}/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines. All of the following * subroutines are declared as inline and are folded into * rs_interrupt(). They were separated out for readability's sake. * * Note: rs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off. People who may want to modify * rs_interrupt() should try to keep the interrupt handler as fast as * possible. After you are done making modifications, it is not a bad * idea to do: * * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c * * and look at the resulting assemble code in serial.s. * * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 * ----------------------------------------------------------------------- *//* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */static _INLINE_ void rs_sched_event(struct e100_serial *info, int event){ info->event |= 1 << event; queue_task(&info->tqueue, &tq_serial); mark_bh(SERIAL_BH);}/* The output DMA channel is free - use it to send as many chars as possible * NOTES: * We don't pay attention to info->x_char, which means if the TTY wants to * use XON/XOFF it will set info->x_char but we won't send any X char! * * To implement this, we'd just start a DMA send of 1 byte pointing at a * buffer containing the X char, and skip updating xmit. We'd also have to * check if the last sent char was the X char when we enter this function * the next time, to avoid updating xmit with the sent X value. */static void transmit_chars(struct e100_serial *info){ unsigned int c, sentl; struct etrax_dma_descr *descr;#ifdef CONFIG_SVINTO_SIM /* This will output too little if tail is not 0 always since * we don't reloop to send the other part. Anyway this SHOULD be a * no-op - transmit_chars would never really be called during sim * since rs_write does not write into the xmit buffer then. */ if (info->xmit.tail) printk("Error in serial.c:transmit_chars(), tail!=0\n"); if (info->xmit.head != info->xmit.tail) { SIMCOUT(info->xmit.buf + info->xmit.tail, CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)); info->xmit.head = info->xmit.tail; /* move back head */ info->tr_running = 0; } return;#endif /* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */ *info->oclrintradr = IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);#ifdef SERIAL_DEBUG_INTR if (info->line == SERIAL_DEBUG_LINE) printk("tc\n");#endif if (!info->tr_running) { /* weirdo... we shouldn't get here! */ printk("Achtung: transmit_chars with !tr_running\n"); return; } descr = &info->tr_descr; /* first get the amount of bytes sent during the last DMA transfer, and update xmit accordingly */ /* if the stop bit was not set, all data has been sent */ if (!(descr->status & d_stop)) { sentl = descr->sw_len; } else /* otherwise we find the amount of data sent here */ sentl = descr->hw_len; /* update stats */ info->icount.tx += sentl; /* update xmit buffer */ info->xmit.tail = (info->xmit.tail + sentl) & (SERIAL_XMIT_SIZE - 1); /* if there is only a few chars left in the buf, wake up the blocked write if any */ if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) < WAKEUP_CHARS) rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); /* find out the largest amount of consecutive bytes we want to send now */ c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); if (c <= 0) { /* our job here is done, don't schedule any new DMA transfer */ info->tr_running = 0;#if defined(CONFIG_ETRAX_RS485) /* Check if we should toggle RTS now */ if (info->rs485.enabled) { /* Make sure fifo is empty */ int in_fifo = 0; do { in_fifo = IO_EXTRACT(R_DMA_CH6_STATUS, avail, *info->ostatusadr); } while (in_fifo > 0); /* Any way to really check transmitter empty? (TEMT) */ /* Control RTS to set to RX mode */ e100_rts(info, info->rs485.rts_after_sent); #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) e100_enable_rx(info); e100_enable_rxdma_irq(info);#endif }#endif /* RS485 */ return; } /* ok we can schedule a dma send of c chars starting at info->xmit.tail */ /* set up the descriptor correctly for output */ descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */ descr->sw_len = c; descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail); descr->status = 0; *info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */ *info->ocmdadr = 1; /* dma command start -> R_DMAx_CMD */ /* DMA is now running (hopefully) */}static void start_transmit(struct e100_serial *info){#if 0 if (info->line == SERIAL_DEBUG_LINE) printk("x\n");#endif info->tr_descr.sw_len = 0; info->tr_descr.hw_len = 0; info->tr_descr.status = 0; info->tr_running = 1; transmit_chars(info);}#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMERstatic int serial_fast_timer_started = 0;static int serial_fast_timer_expired = 0;static void flush_timeout_function(unsigned long data);#define START_FLUSH_FAST_TIMER(info, string) {\ unsigned long timer_flags; \ save_flags(timer_flags); \ cli(); \ if (fast_timers[info->line].function == NULL) { \ serial_fast_timer_started++; \ TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \ TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \ start_one_shot_timer(&fast_timers[info->line], \ flush_timeout_function, \ (unsigned long)info, \ info->char_time_usec*4, \ string); \ } \ else { \ TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \ } \ restore_flags(timer_flags); \}#else#define START_FLUSH_FAST_TIMER(info, string)#endifstatic intadd_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag){ if (!CIRC_SPACE(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE)) return 0; info->recv.buf[info->recv.head] = data; info->flag_buf[info->recv.head] = flag; info->recv.head = (info->recv.head + 1) & (SERIAL_RECV_SIZE - 1); info->icount.rx++; return 1;}static _INLINE_ unsigned intcopy_descr_data(struct e100_serial *info, unsigned int recvl, unsigned char *buf){ unsigned int count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE); unsigned int length = 0; while (length < recvl && count) { if (length + count > recvl) count = recvl - length; memcpy(info->recv.buf + info->recv.head, buf + length, count); memset(info->flag_buf + info->recv.head, '\0', count); info->recv.head = (info->recv.head + count) & (SERIAL_RECV_SIZE - 1); length += count; count = CIRC_SPACE_TO_END(info->recv.head, info->recv.tail, SERIAL_RECV_SIZE); } if (length != recvl) { printk(__FUNCTION__ ": Buffer overflow! %d byte(s) did not fit.\n", recvl - length); PROCSTAT(ser_stat[info->line].overrun_cnt += recvl - length); } return length;}static _INLINE_ unsigned intcopy_all_descr_data(struct e100_serial *info){ struct etrax_dma_descr *descr; unsigned int recvl; unsigned int ret = 0; while (1) { descr = &info->rec_descr[info->cur_rec_descr]; if (descr == phys_to_virt(*info->idescradr)) break; if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS) info->cur_rec_descr = 0; /* find out how many bytes were read */ /* if the eop bit was not set, all data has been received */ if (!(descr->status & d_eop)) { recvl = descr->sw_len; } else { /* otherwise we find the amount of data received here */ recvl = descr->hw_len; } /* Reset the status information */ descr->status = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -