📄 dz.c
字号:
dz_out (info, DZ_LPR, info->cflags); if (info->xmit_buf) { /* free Tx buffer */ free_page ((unsigned long)info->xmit_buf); info->xmit_buf = 0; } if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { tmp = dz_in (info, DZ_TCR); if (tmp & DZ_MODEM_DTR) { tmp &= ~DZ_MODEM_DTR; dz_out (info, DZ_TCR, tmp); } } if (info->tty) set_bit (TTY_IO_ERROR, &info->tty->flags); info->is_initialized = 0; 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_flags (flags); cli (); 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_flags (flags); cli (); 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_flags (flags); cli (); 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_flags (flags); cli (); 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));}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; copy_from_user (&new_serial, new_info, sizeof(new_serial)); old_info = *info; if (!suser()) 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_flags (flags); cli(); 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: error = get_user (arg, (unsigned long *)arg); if (error) return error; 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)); if (error) return error; else return get_lsr_info (info, (unsigned int *)arg); case TIOCSERGSTRUCT: error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(struct dz_serial)); if (error) return error; copy_to_user((struct dz_serial *)arg, info, sizeof(struct dz_serial)); return 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_flags (flags); cli(); 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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -