mcfserial.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,892 行 · 第 1/4 页
C
1,892 行
/* Re-arm timer */ mcfrs_timer_struct.expires = jiffies + HZ/25; add_timer(&mcfrs_timer_struct);}#endif /* MCFPP_DCD0 *//* * 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() -> mcfrs_hangup() * */static void do_serial_hangup(void *private){ struct mcf_serial *info = (struct mcf_serial *) private; struct tty_struct *tty; tty = info->tty; if (!tty) return; tty_hangup(tty);}static int startup(struct mcf_serial * info){ volatile unsigned char *uartp; unsigned long flags; if (info->flags & ASYNC_INITIALIZED) return 0; if (!info->xmit_buf) { info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL); if (!info->xmit_buf) return -ENOMEM; } local_irq_save(flags);#ifdef SERIAL_DEBUG_OPEN printk("starting up ttyS%d (irq %d)...\n", info->line, info->irq);#endif /* * Reset UART, get it into known state... */ uartp = info->addr; uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ mcfrs_setsignals(info, 1, 1); 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 */ mcfrs_change_speed(info); /* * Lastly enable the UART transmitter and receiver, and * interrupt enables. */ info->imr = MCFUART_UIR_RXREADY; uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE; uartp[MCFUART_UIMR] = info->imr; info->flags |= ASYNC_INITIALIZED; local_irq_restore(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 mcf_serial * info){ volatile unsigned char *uartp; unsigned long flags; if (!(info->flags & ASYNC_INITIALIZED)) return;#ifdef SERIAL_DEBUG_OPEN printk("Shutting down serial port %d (irq %d)....\n", info->line, info->irq);#endif local_irq_save(flags); uartp = info->addr; uartp[MCFUART_UIMR] = 0; /* mask all interrupts */ uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) mcfrs_setsignals(info, 0, 0); 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 &= ~ASYNC_INITIALIZED; local_irq_restore(flags);}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void mcfrs_change_speed(struct mcf_serial *info){ volatile unsigned char *uartp; unsigned int baudclk, cflag; unsigned long flags; unsigned char mr1, mr2; int i;#ifdef CONFIG_M5272 unsigned int fraction;#endif if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (info->addr == 0) return;#if 0 printk("%s(%d): mcfrs_change_speed()\n", __FILE__, __LINE__);#endif i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; if (i < 1 || i > 4) info->tty->termios->c_cflag &= ~CBAUDEX; else i += 15; } if (i == 0) { mcfrs_setsignals(info, 0, -1); return; } /* compute the baudrate clock */#ifdef CONFIG_M5272 /* * For the MCF5272, also compute the baudrate fraction. */ baudclk = (MCF_BUSCLK / mcfrs_baud_table[i]) / 32; fraction = MCF_BUSCLK - (baudclk * 32 * mcfrs_baud_table[i]); fraction *= 16; fraction /= (32 * mcfrs_baud_table[i]);#else baudclk = ((MCF_BUSCLK / mcfrs_baud_table[i]) + 16) / 32;#endif info->baud = mcfrs_baud_table[i]; mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR; mr2 = 0; switch (cflag & CSIZE) { case CS5: mr1 |= MCFUART_MR1_CS5; break; case CS6: mr1 |= MCFUART_MR1_CS6; break; case CS7: mr1 |= MCFUART_MR1_CS7; break; case CS8: default: mr1 |= MCFUART_MR1_CS8; break; } if (cflag & PARENB) { if (cflag & CMSPAR) { if (cflag & PARODD) mr1 |= MCFUART_MR1_PARITYMARK; else mr1 |= MCFUART_MR1_PARITYSPACE; } else { if (cflag & PARODD) mr1 |= MCFUART_MR1_PARITYODD; else mr1 |= MCFUART_MR1_PARITYEVEN; } } else { mr1 |= MCFUART_MR1_PARITYNONE; } if (cflag & CSTOPB) mr2 |= MCFUART_MR2_STOP2; else mr2 |= MCFUART_MR2_STOP1; if (cflag & CRTSCTS) { mr1 |= MCFUART_MR1_RXRTS; mr2 |= MCFUART_MR2_TXCTS; } if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; else info->flags |= ASYNC_CHECK_CD; uartp = info->addr; local_irq_save(flags);#if 0 printk("%s(%d): mr1=%x mr2=%x baudclk=%x\n", __FILE__, __LINE__, mr1, mr2, baudclk);#endif /* Note: pg 12-16 of MCF5206e User's Manual states that a software reset should be performed prior to changing UMR1,2, UCSR, UACR, bit 7 */ uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETMRPTR; /* reset MR pointer */ uartp[MCFUART_UMR] = mr1; uartp[MCFUART_UMR] = mr2; uartp[MCFUART_UBG1] = (baudclk & 0xff00) >> 8; /* set msb byte */ uartp[MCFUART_UBG2] = (baudclk & 0xff); /* set lsb byte */#ifdef CONFIG_M5272 uartp[MCFUART_UFPD] = (fraction & 0xf); /* set fraction */#endif uartp[MCFUART_UCSR] = MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER; uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE; mcfrs_setsignals(info, 1, -1); local_irq_restore(flags); return;}static void mcfrs_flush_chars(struct tty_struct *tty){ volatile unsigned char *uartp; struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "mcfrs_flush_chars")) return; if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf) return; /* Enable transmitter */ local_irq_save(flags); uartp = info->addr; info->imr |= MCFUART_UIR_TXREADY; uartp[MCFUART_UIMR] = info->imr; local_irq_restore(flags);}static int mcfrs_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count){ volatile unsigned char *uartp; struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; unsigned long flags; int c, total = 0;#if 0 printk("%s(%d): mcfrs_write(tty=%x,from_user=%d,buf=%x,count=%d)\n", __FILE__, __LINE__, (int)tty, from_user, (int)buf, count);#endif if (serial_paranoia_check(info, tty->name, "mcfrs_write")) return 0; if (!tty || !info->xmit_buf) return 0; local_save_flags(flags); while (1) { local_irq_disable(); c = min(count, (int) min(((int)SERIAL_XMIT_SIZE) - info->xmit_cnt - 1, ((int)SERIAL_XMIT_SIZE) - info->xmit_head)); local_irq_restore(flags); if (c <= 0) break; if (from_user) { down(&mcfrs_tmp_buf_sem); if (copy_from_user(mcfrs_tmp_buf, buf, c)) return -EFAULT; local_irq_disable(); c = min(c, (int) min(((int)SERIAL_XMIT_SIZE) - info->xmit_cnt - 1, ((int)SERIAL_XMIT_SIZE) - info->xmit_head)); local_irq_restore(flags); memcpy(info->xmit_buf + info->xmit_head, mcfrs_tmp_buf, c); up(&mcfrs_tmp_buf_sem); } else memcpy(info->xmit_buf + info->xmit_head, buf, c); local_irq_disable(); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); info->xmit_cnt += c; local_irq_restore(flags); buf += c; count -= c; total += c; } local_irq_disable(); uartp = info->addr; info->imr |= MCFUART_UIR_TXREADY; uartp[MCFUART_UIMR] = info->imr; local_irq_restore(flags); return total;}static int mcfrs_write_room(struct tty_struct *tty){ struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; int ret; if (serial_paranoia_check(info, tty->name, "mcfrs_write_room")) return 0; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) ret = 0; return ret;}static int mcfrs_chars_in_buffer(struct tty_struct *tty){ struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->name, "mcfrs_chars_in_buffer")) return 0; return info->xmit_cnt;}static void mcfrs_flush_buffer(struct tty_struct *tty){ struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->name, "mcfrs_flush_buffer")) return; local_irq_save(flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; local_irq_restore(flags); tty_wakeup(tty);}/* * ------------------------------------------------------------ * mcfrs_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void mcfrs_throttle(struct tty_struct * tty){ struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;#ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("throttle %s: %d....\n", _tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->name, "mcfrs_throttle")) return; if (I_IXOFF(tty)) info->x_char = STOP_CHAR(tty); /* Turn off RTS line (do this atomic) */}static void mcfrs_unthrottle(struct tty_struct * tty){ struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;#ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("unthrottle %s: %d....\n", _tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->name, "mcfrs_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) */}/* * ------------------------------------------------------------ * mcfrs_ioctl() and friends * ------------------------------------------------------------ */static int get_serial_info(struct mcf_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 = (unsigned int) info->addr; 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; return copy_to_user(retinfo,&tmp,sizeof(*retinfo)) ? -EFAULT : 0;}static int set_serial_info(struct mcf_serial * info, struct serial_struct * new_info){ struct serial_struct new_serial; struct mcf_serial old_info; int retval = 0; if (!new_info) return -EFAULT; if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; old_info = *info; if (!capable(CAP_SYS_ADMIN)) { if ((new_serial.baud_base != info->baud_base) || (new_serial.type != info->type) || (new_serial.close_delay != info->close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != (info->flags & ~ASYNC_USR_MASK))) return -EPERM; info->flags = ((info->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_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..... */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?