📄 68328serial.c
字号:
m68328_uart *uart = &uart_addr[info->line]; unsigned long flags; uart->ustcnt = 0; /* All off! */ if (!(info->flags & S_INITIALIZED)) return; 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);}struct { int divisor, prescale;}#ifndef CONFIG_M68VZ328 hw_baud_table[18] = { {0,0}, /* 0 */ {0,0}, /* 50 */ {0,0}, /* 75 */ {0,0}, /* 110 */ {0,0}, /* 134 */ {0,0}, /* 150 */ {0,0}, /* 200 */ {7,0x26}, /* 300 */ {6,0x26}, /* 600 */ {5,0x26}, /* 1200 */ {0,0}, /* 1800 */ {4,0x26}, /* 2400 */ {3,0x26}, /* 4800 */ {2,0x26}, /* 9600 */ {1,0x26}, /* 19200 */ {0,0x26}, /* 38400 */ {1,0x38}, /* 57600 */ {0,0x38}, /* 115200 */};#else hw_baud_table[18] = { {0,0}, /* 0 */ {0,0}, /* 50 */ {0,0}, /* 75 */ {0,0}, /* 110 */ {0,0}, /* 134 */ {0,0}, /* 150 */ {0,0}, /* 200 */ {0,0}, /* 300 */ {7,0x26}, /* 600 */ {6,0x26}, /* 1200 */ {0,0}, /* 1800 */ {5,0x26}, /* 2400 */ {4,0x26}, /* 4800 */ {3,0x26}, /* 9600 */ {2,0x26}, /* 19200 */ {1,0x26}, /* 38400 */ {0,0x26}, /* 57600 */ {1,0x38}, /* 115200 */}; #endif/* rate = 1036800 / ((65 - prescale) * (1<<divider)) *//* * 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 m68k_serial *info){ m68328_uart *uart = &uart_addr[info->line]; unsigned short port; unsigned short ustcnt; unsigned cflag; int i; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (!(port = info->port)) return; ustcnt = uart->ustcnt; uart->ustcnt = ustcnt & ~USTCNT_TXEN; i = cflag & CBAUD; if (i & CBAUDEX) { i = (i & ~CBAUDEX) + B38400; } info->baud = baud_table[i]; uart->ubaud = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) | PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale); ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7); if ((cflag & CSIZE) == CS8) ustcnt |= USTCNT_8_7; if (cflag & CSTOPB) ustcnt |= USTCNT_STOP; if (cflag & PARENB) ustcnt |= USTCNT_PARITYEN; if (cflag & PARODD) ustcnt |= USTCNT_ODD_EVEN; #ifdef CONFIG_68328_SERIAL_RTS_CTS if (cflag & CRTSCTS) { uart->utx.w &= ~ UTX_NOCTS; } else { uart->utx.w |= UTX_NOCTS; }#endif ustcnt |= USTCNT_TXEN; uart->ustcnt = ustcnt; return;}/* * Fair output driver allows a process to speak. */static void rs_fair_output(void){ int left; /* Output no more than that */ unsigned long flags; struct m68k_serial *info = &m68k_soft[0]; 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(c); save_flags(flags); cli(); left = MIN(info->xmit_cnt, left-1); } /* Last character is being transmitted now (hopefully). */ udelay(5); restore_flags(flags); return;}/* * m68k_console_print is registered for printk. */void console_print_68328(const char *p){ char c; while((c=*(p++)) != 0) { if(c == '\n') rs_put_char('\r'); rs_put_char(c); } /* Comment this if you want to have a strict interrupt-driven output */ rs_fair_output(); return;}static void rs_set_ldisc(struct tty_struct *tty){ struct m68k_serial *info = (struct m68k_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 m68k_serial *info = (struct m68k_serial *)tty->driver_data; m68328_uart *uart = &uart_addr[info->line]; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) return;#ifndef USE_INTS for(;;) {#endif if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf) return; /* Enable transmitter */ save_flags(flags); cli();#ifdef USE_INTS uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK;#else uart->ustcnt |= USTCNT_TXEN;#endif#ifdef USE_INTS if (uart->utx.w & UTX_TX_AVAIL) {#else if (1) {#endif /* Send char */ uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); info->xmit_cnt--; }#ifndef USE_INTS while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5); }#endif 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 m68k_serial *info = (struct m68k_serial *)tty->driver_data; m68328_uart *uart = &uart_addr[info->line]; 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); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); up(&tmp_buf_sem); } else memcpy(info->xmit_buf + info->xmit_head, buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); info->xmit_cnt += c; restore_flags(flags); buf += c; count -= c; total += c; } if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { /* Enable transmitter */ cli(); #ifndef USE_INTS while(info->xmit_cnt) {#endif uart->ustcnt |= USTCNT_TXEN;#ifdef USE_INTS uart->ustcnt |= USTCNT_TX_INTR_MASK;#else while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5);#endif if (uart->utx.w & UTX_TX_AVAIL) { uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); info->xmit_cnt--; }#ifndef USE_INTS }#endif restore_flags(flags); } restore_flags(flags); return total;}static int rs_write_room(struct tty_struct *tty){ struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; int ret; if (serial_paranoia_check(info, tty->device, "rs_write_room")) return 0; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) ret = 0; return ret;}static int rs_chars_in_buffer(struct tty_struct *tty){ struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) return 0; return info->xmit_cnt;}static void rs_flush_buffer(struct tty_struct *tty){ struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) return; cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; sti(); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty);}/* * ------------------------------------------------------------ * rs_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void rs_throttle(struct tty_struct * tty){ struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_throttle")) return; if (I_IXOFF(tty)) info->x_char = STOP_CHAR(tty); /* Turn off RTS line (do this atomic) */}static void rs_unthrottle(struct tty_struct * tty){ struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) return; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else info->x_char = START_CHAR(tty); } /* Assert RTS line (do this atomic) */}/* * ------------------------------------------------------------ * rs_ioctl() and friends * ------------------------------------------------------------ */static int get_serial_info(struct m68k_serial * info, struct serial_struct * retinfo){ struct serial_struct tmp; if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); tmp.type = info->type; tmp.line = info->line; tmp.port = info->port; tmp.irq = info->irq; tmp.flags = info->flags; tmp.baud_base = info->baud_base; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; tmp.custom_divisor = info->custom_divisor; copy_to_user(retinfo,&tmp,sizeof(*retinfo)); return 0;}static int set_serial_info(struct m68k_serial * info, struct serial_struct * new_info){ struct serial_struct new_serial; struct m68k_serial old_info; int retval = 0; if (!new_info) return -EFAULT; copy_from_user(&new_serial,new_info,sizeof(new_serial)); old_info = *info; if (!suser()) { if ((new_serial.baud_base != info->baud_base) || (new_serial.type != info->type) || (new_serial.close_delay != info->close_delay) || ((new_serial.flags & ~S_USR_MASK) != (info->flags & ~S_USR_MASK))) return -EPERM; info->flags = ((info->flags & ~S_USR_MASK) | (new_serial.flags & S_USR_MASK)); info->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } if (info->count > 1) return -EBUSY; /* * OK, past this point, all the error checking has been done. * At this point, we start making changes..... */ info->baud_base = new_serial.baud_base; info->flags = ((info->flags & ~S_FLAGS) | (new_serial.flags & S_FLAGS)); info->type = new_serial.type; info->close_delay = new_serial.close_delay; info->closing_wait = new_serial.closing_wait;check_and_exit: retval = startup(info); return retval;}/* * get_lsr_info - get line status register info * * Purpose: Let user call ioctl() to get info when the UART physically * is emptied. On bus types like RS485, the transmitter must * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */static int get_lsr_info(struct m68k_serial * info, unsigned int *value){#ifdef CONFIG_68328_SERIAL_RTS_CTS m68328_uart *uart = &uart_addr[info->line];#endif unsigned char status; cli();#ifdef CONFIG_68328_SERIAL_RTS_CTS status = (uart->utx.w & UTX_CTS_STAT) ? 1 : 0;#else status = 0;#endif sti(); put_user(status,value); return 0;}/* * This routine sends a break character out the serial port. */static void send_break( struct m68k_serial * info, int duration){ m68328_uart *uart = &uart_addr[info->line]; unsigned long flags; if (!info->port) return; current->state = TASK_INTERRUPTIBLE; save_flags(flags); cli();#ifdef USE_INTS uart->utx.w |= UTX_SEND_BREAK; schedule_timeout(duration); uart->utx.w &= ~UTX_SEND_BREAK;#endif restore_flags(flags);}static int rs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ int error; struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; int retval; if (serial_paranoia_check(info, tty->device, "rs_ioctl")) return -ENODEV; if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } switch (cmd) { case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); if (!arg) send_break(info, HZ/4); /* 1/4 second */ return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); send_break(info, arg ? arg*(HZ/10) : HZ/4); return 0; case TIOCGSOFTCAR: error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); if (error) return error; put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); return 0; case TIOCSSOFTCAR: get_user(arg, (unsigned long *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return 0; case TIOCGSERIAL: error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct)); if (error) return error; return get_serial_info(info, (struct serial_struct *) arg); case TIOCSSERIAL: return set_serial_info(info, (struct serial_struct *) arg); case TIOCSERGETLSR: /* Get line status register */ error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -