📄 dz.c
字号:
restore_flags (flags);}/* * ------------------------------------------------------------------- * change_speed () * * set the baud rate. * ------------------------------------------------------------------- */static void change_speed (struct dz_serial *info){ unsigned long flags; unsigned cflag; int baud; if (!info->tty || !info->tty->termios) return; save_and_cli(flags); info->cflags = info->line; cflag = info->tty->termios->c_cflag; switch (cflag & CSIZE) { case CS5: info->cflags |= DZ_CS5; break; case CS6: info->cflags |= DZ_CS6; break; case CS7: info->cflags |= DZ_CS7; break; case CS8: default: info->cflags |= DZ_CS8; } if (cflag & CSTOPB) info->cflags |= DZ_CSTOPB; if (cflag & PARENB) info->cflags |= DZ_PARENB; if (cflag & PARODD) info->cflags |= DZ_PARODD; baud = tty_get_baud_rate(info->tty); switch (baud) { case 50: info->cflags |= DZ_B50; break; case 75: info->cflags |= DZ_B75; break; case 110: info->cflags |= DZ_B110; break; case 134: info->cflags |= DZ_B134; break; case 150: info->cflags |= DZ_B150; break; case 300: info->cflags |= DZ_B300; break; case 600: info->cflags |= DZ_B600; break; case 1200: info->cflags |= DZ_B1200; break; case 1800: info->cflags |= DZ_B1800; break; case 2000: info->cflags |= DZ_B2000; break; case 2400: info->cflags |= DZ_B2400; break; case 3600: info->cflags |= DZ_B3600; break; case 4800: info->cflags |= DZ_B4800; break; case 7200: info->cflags |= DZ_B7200; break; case 9600: default: info->cflags |= DZ_B9600; } info->cflags |= DZ_RXENAB; dz_out(info, DZ_LPR, info->cflags); /* setup accept flag */ info->read_status_mask = DZ_OERR; if (I_INPCK(info->tty)) info->read_status_mask |= (DZ_FERR | DZ_PERR); /* characters to ignore */ info->ignore_status_mask = 0; if (I_IGNPAR(info->tty)) info->ignore_status_mask |= (DZ_FERR | DZ_PERR); restore_flags(flags);}/* * ------------------------------------------------------------------- * dz_flush_char () * * Flush the buffer. * ------------------------------------------------------------------- */static void dz_flush_chars (struct tty_struct *tty){ struct dz_serial *info = (struct dz_serial *)tty->driver_data; unsigned long flags; if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf) return; save_and_cli(flags); dz_start (info->tty); restore_flags(flags);}/* * ------------------------------------------------------------------- * dz_write () * * main output routine. * ------------------------------------------------------------------- */static int dz_write (struct tty_struct *tty, int from_user, const unsigned char *buf, int count){ struct dz_serial *info = (struct dz_serial *)tty->driver_data; unsigned long flags; int c, ret = 0; if (!tty ) return ret; if (!info->xmit_buf) return ret; if (!tmp_buf) tmp_buf = tmp_buffer; if (from_user) { down (&tmp_buf_sem); while (1) { c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head)); if (c <= 0) break; c -= copy_from_user (tmp_buf, buf, c); if (!c) { if (!ret) ret = -EFAULT; break; } save_and_cli(flags); c = MIN(c, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); info->xmit_head = ((info->xmit_head + c) & (DZ_XMIT_SIZE - 1)); info->xmit_cnt += c; restore_flags(flags); buf += c; count -= c; ret += c; } up(&tmp_buf_sem); } else { while (1) { save_and_cli(flags); c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head)); if (c <= 0) { restore_flags (flags); break; } memcpy(info->xmit_buf + info->xmit_head, buf, c); info->xmit_head = ((info->xmit_head + c) & (DZ_XMIT_SIZE-1)); info->xmit_cnt += c; restore_flags(flags); buf += c; count -= c; ret += c; } } if (info->xmit_cnt) { if (!tty->stopped) { if (!tty->hw_stopped) { dz_start (info->tty); } } } return ret;}/* * ------------------------------------------------------------------- * dz_write_room () * * compute the amount of space available for writing. * ------------------------------------------------------------------- */static int dz_write_room (struct tty_struct *tty){ struct dz_serial *info = (struct dz_serial *)tty->driver_data; int ret; ret = DZ_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) ret = 0; return ret;}/* * ------------------------------------------------------------------- * dz_chars_in_buffer () * * compute the amount of char left to be transmitted * ------------------------------------------------------------------- */static int dz_chars_in_buffer (struct tty_struct *tty){ struct dz_serial *info = (struct dz_serial *)tty->driver_data; return info->xmit_cnt;}/* * ------------------------------------------------------------------- * dz_flush_buffer () * * Empty the output buffer * ------------------------------------------------------------------- */static void dz_flush_buffer (struct tty_struct *tty){ struct dz_serial *info = (struct dz_serial *)tty->driver_data; 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);}/* * ------------------------------------------------------------ * dz_throttle () and dz_unthrottle () * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled (or not). * ------------------------------------------------------------ */static void dz_throttle (struct tty_struct *tty){ struct dz_serial *info = (struct dz_serial *)tty->driver_data; if (I_IXOFF(tty)) info->x_char = STOP_CHAR(tty);}static void dz_unthrottle (struct tty_struct *tty){ struct dz_serial *info = (struct dz_serial *)tty->driver_data; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else info->x_char = START_CHAR(tty); }}static void dz_send_xchar (struct tty_struct *tty, char ch){ struct dz_serial *info = (struct dz_serial *)tty->driver_data; info->x_char = ch; if (ch) dz_start(info->tty);}/* * ------------------------------------------------------------ * rs_ioctl () and friends * ------------------------------------------------------------ */static int get_serial_info(struct dz_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 = SERIAL; tmp.flags = info->flags; tmp.baud_base = info->baud_base; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;}static int set_serial_info (struct dz_serial *info, struct serial_struct *new_info){ struct serial_struct new_serial; struct dz_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)) return -EPERM; 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->type = new_serial.type; info->close_delay = new_serial.close_delay; info->closing_wait = new_serial.closing_wait; 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 dz_serial *info, unsigned int *value){ unsigned short status = dz_in (info, DZ_LPR); return put_user (status, value);}/* * This routine sends a break character out the serial port. */static void send_break (struct dz_serial *info, int duration){ unsigned long flags; unsigned short tmp, mask; if (!info->port) return; mask = 1 << info->line; tmp = dz_in (info, DZ_TCR); tmp |= mask; current->state = TASK_INTERRUPTIBLE; save_and_cli(flags); dz_out(info, DZ_TCR, tmp); schedule_timeout(duration); tmp &= ~mask; dz_out(info, DZ_TCR, tmp); restore_flags(flags);}static int dz_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ int error; struct dz_serial * info = (struct dz_serial *)tty->driver_data; int retval; 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: if (get_user (arg, (unsigned long *)arg)) return -EFAULT; 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 */ return get_lsr_info (info, (unsigned int *)arg); case TIOCSERGSTRUCT: return copy_to_user((struct dz_serial *)arg, info, sizeof(struct dz_serial)) ? -EFAULT : 0; default: return -ENOIOCTLCMD; } return 0;}static void dz_set_termios (struct tty_struct *tty, struct termios *old_termios){ struct dz_serial *info = (struct dz_serial *)tty->driver_data; if (tty->termios->c_cflag == old_termios->c_cflag) return; change_speed (info); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; dz_start(tty); }}/* * ------------------------------------------------------------ * dz_close() * * This routine is called when the serial port gets closed. First, we * wait for the last remaining data to be sent. Then, we turn off * the transmit enable and receive enable flags. * ------------------------------------------------------------ */static void dz_close(struct tty_struct *tty, struct file *filp){ struct dz_serial * info = (struct dz_serial *)tty->driver_data; unsigned long flags; if (!info) return; save_and_cli(flags); if (tty_hung_up_p(filp)) { restore_flags(flags); return; } if ((tty->count == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty structure * will be freed. Info->count should always be one in these * conditions. If it's greater than one, we've got real * problems, since it means the serial port won't be shutdown. */ printk("dz_close: bad serial port count; tty->count is 1, " "info->count is %d\n", info->count); info->count = 1; } if (--info->count < 0) { printk("ds_close: bad serial port count for ttyS%02d: %d\n", info->line, info->count);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -