📄 serial_atmel.c
字号:
/* * This is the serial driver's generic interrupt routine */static void rs_interrupta(int irq, void *dev_id, struct pt_regs *regs){ rs_interrupt(&atmel_info[0]);}static void rs_interruptb(int irq, void *dev_id, struct pt_regs *regs){ rs_interrupt(&atmel_info[1]);}static void rs_interrupt(struct atmel_serial *info){ unsigned long status; status = info->usart->csr; if (status & (US_ENDRX | US_TIMEOUT)) { receive_chars(info, status); } if (status & (US_TXEMPTY)) { transmit_chars(info); } status_handle(info, status);#ifdef US_RTS if (!info->cts_state) { if (info->tty->flip.count < TTY_FLIPBUF_SIZE - RX_SERIAL_SIZE) { atmel_cts_on(info); } }#endif if (!info->use_ints) { serialpoll.data = (void *) info; queue_task(&serialpoll, &tq_timer); } return;}static void serpoll(void *data){ struct atmel_serial *info = data; rs_interrupt(info);}/* * ------------------------------------------------------------------- * 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_atmel_serial);}static void do_softint(void *private_){ struct atmel_serial *info = (struct atmel_serial *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return;#if 0 // FIXME - CHECK if (clear_bit(RS_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); }#endif}/* * 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() -> rs_hangup() * */static void do_serial_hangup(void *private_){ struct atmel_serial *info = (struct atmel_serial *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; tty_hangup(tty);}/* * This subroutine is called when the RS_TIMER goes off. It is used * by the serial driver to handle ports that do not have an interrupt * (irq=0). This doesn't work at all for 16450's, as a sun has a Z8530. */#if 0static void rs_timer(void){ panic("rs_timer called\n"); return;}#endifstatic unsigned long calcCD(unsigned long br){ return (UART_CLOCK / br);}static void uart_init(struct atmel_serial *info){ volatile struct atmel_usart_regs *uart; if (info) { uart = info->usart; } else { uart = usarts[0]; } /* Reset the USART */ uart->cr = US_TXDIS | US_RXDIS | US_RSTTX | US_RSTRX; /* clear Rx receive and Tx sent counters */ uart->rcr = 0; uart->tcr = 0; /* Disable interrups till we say we want them */ tx_disable(info->usart); rx_disable(info->usart); /* Set the serial port into a safe sane state */ uart->mr = US_USCLKS(0) | US_CLK0 | US_CHMODE(0) | US_NBSTOP(0) | US_PAR(4) | US_CHRL(3);#ifndef FORCE_57600 uart->brgr = calcCD(9600);#else uart->brgr = calcCD(57600);#endif uart->rtor = 20; // timeout = value * 4 *bit period uart->ttgr = 0; // no guard time uart->rcr = 0; uart->rpr = 0; uart->tcr = 0; uart->tpr = 0;#ifdef US_RTS uart->mc = 0; #endif}/* It is the responsibilty of whoever calls this function to be sure * that that have called * tx_stop(uart); rx_stop(uart); * before calling the function. Failure to do this will cause messy * things to happen. You have been warned. */static void uart_speed(struct atmel_serial *info, unsigned cflag){ unsigned baud = info->baud; volatile struct atmel_usart_regs *uart = info->usart; // disable tx and rx uart->cr = US_TXDIS | US_RXDIS; // disable interrupts tx_disable(uart); rx_disable(uart); #ifndef FORCE_57600 uart->brgr = calcCD(baud);#else uart->brgr = calcCD(57600);#endif/* FIXME */#if 0 /* probably not needed */ uart->US_RTOR = 20; // timeout = value * 4 *bit period uart->US_TTGR = 0; // no guard time uart->US_RPR = 0; uart->US_RCR = 0; uart->US_TPR = 0; uart->US_TCR = 0;#endif/* FIXME */#if 0 uart->mc = 0; if (cflag != 0xffff) { uart->mr = US_USCLKS(0) | US_CLK0 | US_CHMODE(0) | US_NBSTOP(0) | US_PAR(0); if ((cflag & CSIZE) == CS8) uart->mr |= US_CHRL(3); // 8 bit char else uart->mr |= US_CHRL(2); // 7 bit char if (cflag & CSTOPB) uart->mr |= US_NBSTOP(2); // 2 stop bits if (!(cflag & PARENB)) uart->mr |= US_PAR(4); // parity disabled else if (cflag & PARODD) uart->mr |= US_PAR(1); // odd parity }#endif /* FIXME */#if 0 // enable tx and rx uart->cr = US_TXEN | US_RXEN; // enable interrupts tx_enable(); rx_enable();#endif tx_start(uart, info->use_ints); start_rx(info);}static void wait_EOT(volatile struct atmel_usart_regs *uart){ // make sure tx is enabled uart->cr = US_TXEN; // wait until all chars sent FIXME - is this sane ? while (1) { if (uart->csr & US_TXEMPTY) break; }}static int startup(struct atmel_serial *info){ unsigned long flags; if (info->flags & S_INITIALIZED) return 0; if (!info->xmit_buf) { info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL); if (!info->xmit_buf) return -ENOMEM; } if (!info->rx_buf) { //info->rx_buf = (unsigned char *) get_free_page(GFP_KERNEL); //info->rx_buf = rx_buf1; if (!info->rx_buf) return -ENOMEM; } save_flags(flags); cli();#ifdef SERIAL_DEBUG_OPEN printk("starting up ttyS%d (irq %d)...\n", info->line, info->irq);#endif /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * and set the speed of the serial port */ uart_init(info); //set_ints_mode(0, info); change_speed(info); info->flags |= S_INITIALIZED; restore_flags(flags); return 0;}/* * 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 atmel_serial *info){ unsigned long flags; tx_disable(info->usart); rx_disable(info->usart); rx_stop(info->usart); /* All off! */ if (!(info->flags & S_INITIALIZED)) return;#ifdef SERIAL_DEBUG_OPEN printk("Shutting down serial port %d (irq %d)....\n", info->line, info->irq);#endif save_flags(flags); cli(); /* Disable interrupts */ if (info->xmit_buf) { free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~S_INITIALIZED; restore_flags(flags);}/* rate = 1036800 / ((65 - prescale) * (1<<divider)) */static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 0};/* * 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 atmel_serial *info){ unsigned cflag; int i; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; // disable tx and rx info->usart->cr = US_TXDIS | US_RXDIS; /* First disable the interrupts */ tx_stop(info->usart); rx_stop(info->usart); /* set the baudrate */ i = cflag & CBAUD; info->baud = baud_table[i]; uart_speed(info, cflag); tx_start(info->usart, info->use_ints); rx_start(info->usart, info->use_ints); // enable tx and rx info->usart->cr = US_TXEN | US_RXEN; return;}static void start_rx(struct atmel_serial *info){ volatile struct atmel_usart_regs *uart = info->usart; rx_stop(uart);/* FIXME - rehnberg if (info->rx_buf == rx_buf1) { info->rx_buf = rx_buf2; } else { info->rx_buf = rx_buf1; }*/ uart->rpr = (unsigned long) info->rx_buf; uart->rcr = (unsigned long) RX_SERIAL_SIZE; rx_start(uart, info->use_ints);}static void xmit_char(struct atmel_serial *info, char ch){ prompt0 = ch; xmit_string(info, &prompt0, 1);}static void xmit_string(struct atmel_serial *info, char *p, int len){ info->usart->tcr = 0; info->usart->tpr = (unsigned long) p; info->usart->tcr = (unsigned long) len; tx_start(info->usart, info->use_ints);}/* * atmel_console_print is registered for printk. */int atmel_console_initialized;static void init_console(struct atmel_serial *info){ memset(info, 0, sizeof(struct atmel_serial));#ifdef CONFIG_SWAP_ATMEL_PORTS info->usart = (volatile struct atmel_usart_regs *) AT91_USART1_BASE; info->irqmask = AIC_URT1; info->irq = IRQ_USART1;#else info->usart = (volatile struct atmel_usart_regs *) AT91_USART0_BASE; info->irqmask = 1<<IRQ_USART0; info->irq = IRQ_USART0;#endif info->tty = 0; info->port = 0; info->use_ints = 0; info->cts_state = 1; info->is_cons = 1; atmel_console_initialized = 1;}void console_print_atmel(const char *p){ char c; struct atmel_serial *info;#ifdef CONFIG_SWAP_ATMEL_PORTS info = &atmel_info[1];#else info = &atmel_info[0];#endif if (!atmel_console_initialized) { init_console(info); uart_init(info); info->baud = 9600; tx_stop(info->usart); rx_stop(info->usart); uart_speed(info, 0xffff); tx_start(info->usart, info->use_ints); rx_start(info->usart, info->use_ints); } while ((c = *(p++)) != 0) { if (c == '\n') rs_put_char(info, '\r'); rs_put_char(info, c); } /* Comment this if you want to have a strict interrupt-driven output */#if 0 if (!info->use_ints) rs_fair_output(info);#endif return;}static void rs_set_ldisc(struct tty_struct *tty){ struct atmel_serial *info = (struct atmel_serial *) tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_set_ldisc")) return; info->is_cons = (tty->termios->c_line == N_TTY); printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off");}static void rs_flush_chars(struct tty_struct *tty){ struct atmel_serial *info = (struct atmel_serial *) tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) return; if (!info->use_ints) { for (;;) { if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf) return; /* Enable transmitter */ save_flags(flags); cli(); tx_start(info->usart, info->use_ints); } } else { if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf) return; /* Enable transmitter */ save_flags(flags); cli(); tx_start(info->usart, info->use_ints); } if (!info->use_ints) wait_EOT(info->usart); /* Send char */ xmit_char(info, info->xmit_buf[info->xmit_tail++]); info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1); info->xmit_cnt--; restore_flags(flags);}extern void console_printn(const char *b, int count);static int rs_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count){ int c, total = 0; struct atmel_serial *info = (struct atmel_serial *) tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_write")) return 0; if (!tty || !info->xmit_buf) return 0; save_flags(flags); while (1) { cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) break; if (from_user) { down(&tmp_buf_sem); copy_from_user(tmp_buf, buf, c); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); up(&tmp_buf_sem); } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -