📄 serial_netarm.c
字号:
return;}/* Write a number of characters (polled) */static intrs_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count){ struct netarm_async_struct *info = (struct netarm_async_struct *)tty->driver_data; volatile netarm_serial_channel_t *regs; unsigned int *buf32; unsigned int *fifo; unsigned char *bytefifo; int acount; int scount = 0;#ifdef NAS_DEBUG_VERBOSE printk(" rs_write: called with %d chars\n", count);#endif if (serial_paranoia_check(info, tty->device, "rs_write")) return 0; if (!tty || !info->xmit.buf || !tmp_buf) return 0; regs = info->registers; fifo = (unsigned int *)&(regs->fifo); bytefifo = (unsigned char *)&(regs->fifo); /* count to align */ acount = (4 - (((unsigned int)buf) & 0x3)) & 0x03; if ( acount > count ) acount = count; count -= acount; /* word align */ if ( acount > 0 ) { while ( acount > 0 ) { NAS_TX_WAIT_RDY(regs); *bytefifo = *buf; buf++; acount--; scount++; } } buf32 = (unsigned int *)buf; while ( count > 3 ) { NAS_TX_WAIT_RDY(regs); *fifo = *buf32; buf32++; count -= 4; scount += 4; } if ( count > 0 ) { buf = (unsigned char *)buf32; while ( count > 0 ) { NAS_TX_WAIT_RDY(regs); *bytefifo = *buf; buf++; count--; scount++; } } return scount;}static intrs_write_room(struct tty_struct *tty){ struct netarm_async_struct *info = (struct netarm_async_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_write_room")) return 0; return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);}static int rs_chars_in_buffer(struct tty_struct *tty){ struct netarm_async_struct *info = (struct netarm_async_struct *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) return 0; return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);}/* not needed in polled output: */static void rs_flush_buffer(struct tty_struct *tty){ struct netarm_async_struct *info = (struct netarm_async_struct *)tty->driver_data; serial_paranoia_check(info, tty->device, "rs_flush_buffer"); return;}/* * This function is used to send a high-priority XON/XOFF character to * the device */static voidrs_send_xchar(struct tty_struct *tty, char ch){ rs_put_char( tty, ch );}/* * ------------------------------------------------------------ * rs_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static voidrs_throttle(struct tty_struct * tty){ struct netarm_async_struct *info = (struct netarm_async_struct *)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->device, "rs_throttle")) return; if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) info->registers->ctrl_a &= ~NETARM_SER_CTLA_RTS_EN;}static voidrs_unthrottle(struct tty_struct * tty){ struct netarm_async_struct *info = (struct netarm_async_struct *)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->device, "rs_unthrottle")) return; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else rs_send_xchar(tty, START_CHAR(tty)); } if (tty->termios->c_cflag & CRTSCTS) info->registers->ctrl_a |= NETARM_SER_CTLA_RTS_EN;}/* * ------------------------------------------------------------ * rs_ioctl() and friends * ------------------------------------------------------------ */static intget_serial_info(struct netarm_async_struct * info, struct serial_struct * retinfo){ struct serial_struct tmp; struct serial_state *state = info->state; if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); tmp.type = state->type; tmp.line = state->line; tmp.port = state->port; if (HIGH_BITS_OFFSET) tmp.port_high = state->port >> HIGH_BITS_OFFSET; else tmp.port_high = 0; tmp.irq = state->irq; tmp.flags = state->flags; tmp.xmit_fifo_size = state->xmit_fifo_size; tmp.baud_base = state->baud_base; tmp.close_delay = state->close_delay; tmp.closing_wait = state->closing_wait; tmp.custom_divisor = state->custom_divisor; tmp.hub6 = state->hub6; tmp.io_type = state->io_type; if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) return -EFAULT; return 0;}static intset_serial_info(struct netarm_async_struct * info, struct serial_struct * new_info){ struct serial_struct new_serial; struct serial_state old_state, *state; unsigned int i,change_irq,change_port; int retval = 0; unsigned long new_port; if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) return -EFAULT; state = info->state; old_state = *state; new_port = new_serial.port; if (HIGH_BITS_OFFSET) new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; change_irq = new_serial.irq != state->irq; change_port = (new_port != ((int) state->port)) || (new_serial.hub6 != state->hub6); if (!capable(CAP_SYS_ADMIN)) { if (change_irq || change_port || (new_serial.baud_base != state->baud_base) || (new_serial.type != state->type) || (new_serial.close_delay != state->close_delay) || (new_serial.xmit_fifo_size != state->xmit_fifo_size) || ((new_serial.flags & ~ASYNC_USR_MASK) != (state->flags & ~ASYNC_USR_MASK))) return -EPERM; state->flags = ((state->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); info->flags = ((info->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); state->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } new_serial.irq = irq_cannonicalize(new_serial.irq); if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || (new_serial.baud_base < 9600)|| (new_serial.type != PORT_NETARM)) { return -EINVAL; } if (new_serial.type == PORT_NETARM) new_serial.xmit_fifo_size = NETARM_SER_FIFO_SIZE; /* Make sure address is not already in use */ if (new_serial.type) { for (i = 0 ; i < NR_NAS_PORTS; i++) if ((state != &rs_table[i]) && (rs_table[i].port == new_port) && rs_table[i].type) return -EADDRINUSE; } if ((change_port || change_irq) && (state->count > 1)) return -EBUSY; /* * OK, past this point, all the error checking has been done. * At this point, we start making changes..... */ state->baud_base = new_serial.baud_base; state->flags = ((state->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | (info->flags & ASYNC_INTERNAL_FLAGS)); state->custom_divisor = new_serial.custom_divisor; state->close_delay = new_serial.close_delay * HZ/100; state->closing_wait = new_serial.closing_wait * HZ/100;#if (LINUX_VERSION_CODE > 0x20100) info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;#endif info->xmit_fifo_size = state->xmit_fifo_size = new_serial.xmit_fifo_size; if ((state->type != PORT_UNKNOWN) && state->port) { release_region(state->port,8); } state->type = new_serial.type; if (change_port || change_irq) { /* * We need to shutdown the serial port at the old * port/irq combination. */ shutdown(info); state->irq = new_serial.irq; info->port = state->port = new_port; } if ((state->type != PORT_UNKNOWN) && state->port) { request_region(state->port,8,"serial(set)"); } check_and_exit: if (!state->port || !state->type) return 0; if (info->flags & ASYNC_INITIALIZED) { if (((old_state.flags & ASYNC_SPD_MASK) != (state->flags & ASYNC_SPD_MASK)) || (old_state.custom_divisor != state->custom_divisor)) { if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; change_speed(info, 0); } } else 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 intget_lsr_info(struct netarm_async_struct * info, unsigned int *value){ unsigned char status; unsigned int result; unsigned long flags; save_flags(flags); cli(); status = info->registers->status_a; restore_flags(flags); result = ((status & NETARM_SER_STATA_TX_FULL) ? TIOCSER_TEMT : 0); /* * If we're about to load something into the transmit * register, we'll pretend the transmitter isn't empty to * avoid a race condition (depending on when the transmit * interrupt happens). */ if (info->x_char || ((CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) > 0) && !info->tty->stopped && !info->tty->hw_stopped)) result &= TIOCSER_TEMT; if (copy_to_user(value, &result, sizeof(int))) return -EFAULT; return 0;}static intdo_autoconfig(struct netarm_async_struct * info){ if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (info->state->count > 1) return -EBUSY; shutdown(info); autoconfig(info->state); return startup(info);}/* * rs_break() --- routine which turns the break handling on or off */static voidrs_break(struct tty_struct *tty, int break_state){ struct netarm_async_struct * info = (struct netarm_async_struct *)tty->driver_data; unsigned long flags, control; if (serial_paranoia_check(info, tty->device, "rs_break")) return; if (!CONFIGURED_NAS_PORT(info)) return; save_flags(flags); cli(); control = info->registers->ctrl_a; if (break_state == -1) control |= NETARM_SER_CTLA_BRK; else control &= ~NETARM_SER_CTLA_BRK; info->registers->ctrl_a = control; restore_flags(flags);}static intrs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ struct netarm_async_struct * info = (struct netarm_async_struct *)tty->driver_data; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct icount; unsigned long flags, control; if (serial_paranoia_check(info, tty->device, "rs_ioctl")) return -ENODEV; if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } switch (cmd) {/* case TIOCMGET: return get_modem_info(info, (unsigned int *) arg); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: return set_modem_info(info, cmd, (unsigned int *) arg);*/ case TIOCGSERIAL: return get_serial_info(info, (struct serial_struct *) arg); case TIOCSSERIAL: return set_serial_info(info, (struct serial_struct *) arg); case TIOCSERCONFIG: return do_autoconfig(info); case TIOCSERGETLSR: /* Get line status register */ return get_lsr_info(info, (unsigned int *) arg); case TIOCSERGSTRUCT: if (copy_to_user((struct netarm_async_struct *) arg, info, sizeof(struct netarm_async_struct))) return -EFAULT; return 0; /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT: save_flags(flags); cli(); /* note the counters on entry */ cprev = info->state->icount; restore_flags(flags); /* Force modem status interrupts on */ control = info->registers->ctrl_a; control |= NETARM_SER_CTLA_IE_RX_DCD | NETARM_SER_CTLA_IE_RX_RI | NETARM_SER_CTLA_IE_RX_DSR | NETARM_SER_CTLA_IE_TX_CTS; info->registers->ctrl_a = control; while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; save_flags(flags); cli(); cnow = info->state->icount; /* atomic copy */ restore_flags(flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { return 0; } cprev = cnow; } /* NOTREACHED */ /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -