📄 trioserial.c
字号:
/* * This is the serial driver's generic interrupt routine */void rs_interrupta(int irq, void *dev_id, struct pt_regs * regs){ rs_interrupt(&trio_info[0]);}void rs_interruptb(int irq, void *dev_id, struct pt_regs * regs){ rs_interrupt(&trio_info[1]);}static void rs_interrupt(struct trio_serial *info){ u_32 status; status = info->uart->csr; if (status & US_TXRDY) { transmit_chars(info); } if (status & US_RXRDY){ receive_chars(info, status); } status_handle(info, status); if(!info->use_ints){ serialpoll.data = (void *)info; queue_task_irq_off(&serialpoll, &tq_timer); } return;}static void serpoll(void *data){ struct trio_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_serial);}static void do_softint(void *private_){ struct trio_serial *info = (struct trio_serial *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; 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); }}/* * 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 trio_serial *info = (struct trio_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. */ static void rs_timer(void){ panic("rs_timer called\n"); return;}static u_32 calcCD(u_32 br){ return(UART_CLOCK/br);}static void uart_init(struct trio_serial *info){ struct uart_regs* uart; if(info){ uart = info->uart; }else{ uart = uarts[0]; } uart->cr = US_RSTRX|US_RSTTX|US_RSTSTA|US_TXDIS|US_RXDIS; uart->mr = US_USCLKS(0)|US_CLK0|US_CHMODE(0)|US_NBSTOP(0)|US_PAR(4)|US_CHRL(3); uart->ier = 0; uart->idr = US_ALL_INTS; uart->brgr = calcCD(9600); uart->rtor = 100; // timeout = value * 4 * bit period uart->ttgr = 0; // no guard time uart->rpr = 0; uart->rcr = 0; uart->tpr = 0; uart->tcr = 0; uart->mc = 0;}static void uart_speed(struct trio_serial *info, unsigned cflag){ unsigned baud = info->baud; struct uart_regs *uart = info->uart; uart->cr = US_TXDIS|US_RXDIS; uart->ier = 0; uart->idr = US_ALL_INTS; uart->brgr = calcCD(baud); uart->rtor = 100; // timeout = value * 4 *bit period uart->ttgr = 0; // no guard time uart->rpr = 0; uart->rcr = 0; uart->tpr = 0; uart->tcr =0; uart->mc = 0; if (cflag != 0xffff){ uart->mr = US_USCLKS(0)|US_CLK0|US_CHMODE(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 } tx_start(uart, info->use_ints); start_rx(info);}static void wait_EOT(struct uart_regs *uart){ volatile u_32 status; volatile struct uart_regs* puart; puart = (volatile struct uart_regs*)uart; while(1){ status = puart->csr; if(status & US_TXRDY) break; }}static int startup(struct trio_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); 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(1, 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 trio_serial * info){ unsigned long flags; tx_disable(info->uart); rx_disable(info->uart); rx_stop(info->uart); /* 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 trio_serial *info){ unsigned cflag; int i; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; /* First disable the interrupts */ tx_stop(info->uart); rx_stop(info->uart); /* set the baudrate */ i = cflag & CBAUD; info->baud = baud_table[i]; uart_speed(info, cflag); start_rx(info); tx_start(info->uart, info->use_ints); return;}static void start_rx(struct trio_serial *info){ struct uart_regs *uart = info->uart; uart->rcr = (u_32)RX_SERIAL_SIZE; uart->rpr = (u_32)info->rx_buf; rx_start(uart, info->use_ints);}static void xmit_char(struct trio_serial *info, char ch){ prompt0 = ch; xmit_string(info, &prompt0, 1);}static void xmit_string(struct trio_serial *info, char *p, int len){ info->uart->tcr = (u_32)len; info->uart->tpr = (u_32)p; tx_start(info->uart, info->use_ints);}#if 0/* These are for receiving and sending characters under the kgdb * source level kernel debugger. */void putDebugChar(char kgdb_char){ struct sun_zschannel *chan = trio_kgdbchan; while((chan->control & Tx_BUF_EMP)==0) udelay(5); chan->data = kgdb_char;}char getDebugChar(void){ struct sun_zschannel *chan = trio_kgdbchan; while((chan->control & Rx_CH_AV)==0) barrier(); return chan->data;}#endif/* * Fair output driver allows a process to speak. */static void rs_fair_output( struct trio_serial *info){ int left; /* Output no more than that */ unsigned long flags; char c; if (info == 0) return; if (info->xmit_buf == 0) return; save_flags(flags); cli(); left = info->xmit_cnt; while (left != 0) { c = info->xmit_buf[info->xmit_tail]; info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1); info->xmit_cnt--; restore_flags(flags); rs_put_char(info, c); save_flags(flags); cli(); left = MIN(info->xmit_cnt, left-1); } /* Last character is being transmitted now (hopefully). */// udelay(20); wait_EOT(info->uart); restore_flags(flags); return;}/* * trio_console_print is registered for printk. */static int console_initialized = 0;static void init_console(void){ struct trio_serial *info; info = &trio_info[0]; memset(info, 0, sizeof(struct trio_serial));#if 0 info->uart = uarts[0];#else info->uart = (struct uart_regs*)USARTA_BASE;#endif info->tty = 0; info->irqmask = AIC_UA; info->irq = IRQ_USARTA; info->port = 1; info->use_ints = 0; info->is_cons = 1; console_initialized = 1;}void console_print_trio(const char *p){ char c; struct trio_serial *info; info = &trio_info[0];// if (!(info->flags & S_INITIALIZED)){ if(!console_initialized){ init_console(); uart_init(info); info->baud = 9600; uart_speed(info,0xffff); } 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 (!info->use_ints)// rs_fair_output(info); return;}static void rs_set_ldisc(struct tty_struct *tty){ struct trio_serial *info = (struct trio_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 trio_serial *info = (struct trio_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->uart,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->uart, info->use_ints); } if(!info->use_ints) wait_EOT(info->uart); /* 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 trio_serial *info = (struct trio_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; /*buf = "123456"; count = 6; printk("Writing '%s' to serial port\n", buf);*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -