📄 synclink_cs.c
字号:
{ unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("tx_abort(%s)\n", info->device_name); spin_lock_irqsave(&info->lock,flags); if (info->tx_active && info->tx_count && info->params.mode == MGSL_MODE_HDLC) { /* clear data count so FIFO is not filled on next IRQ. * This results in underrun and abort transmission. */ info->tx_count = info->tx_put = info->tx_get = 0; info->tx_aborting = TRUE; } spin_unlock_irqrestore(&info->lock,flags); return 0;}static int set_rxenable(MGSLPC_INFO * info, int enable){ unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("set_rxenable(%s,%d)\n", info->device_name, enable); spin_lock_irqsave(&info->lock,flags); if (enable) { if (!info->rx_enabled) rx_start(info); } else { if (info->rx_enabled) rx_stop(info); } spin_unlock_irqrestore(&info->lock,flags); return 0;}/* wait for specified event to occur * * Arguments: info pointer to device instance data * mask pointer to bitmask of events to wait for * Return Value: 0 if successful and bit mask updated with * of events triggerred, * otherwise error code */static int wait_events(MGSLPC_INFO * info, int __user *mask_ptr){ unsigned long flags; int s; int rc=0; struct mgsl_icount cprev, cnow; int events; int mask; struct _input_signal_events oldsigs, newsigs; DECLARE_WAITQUEUE(wait, current); COPY_FROM_USER(rc,&mask, mask_ptr, sizeof(int)); if (rc) return -EFAULT; if (debug_level >= DEBUG_LEVEL_INFO) printk("wait_events(%s,%d)\n", info->device_name, mask); spin_lock_irqsave(&info->lock,flags); /* return immediately if state matches requested events */ get_signals(info); s = info->serial_signals; events = mask & ( ((s & SerialSignal_DSR) ? MgslEvent_DsrActive:MgslEvent_DsrInactive) + ((s & SerialSignal_DCD) ? MgslEvent_DcdActive:MgslEvent_DcdInactive) + ((s & SerialSignal_CTS) ? MgslEvent_CtsActive:MgslEvent_CtsInactive) + ((s & SerialSignal_RI) ? MgslEvent_RiActive :MgslEvent_RiInactive) ); if (events) { spin_unlock_irqrestore(&info->lock,flags); goto exit; } /* save current irq counts */ cprev = info->icount; oldsigs = info->input_signal_events; if ((info->params.mode == MGSL_MODE_HDLC) && (mask & MgslEvent_ExitHuntMode)) irq_enable(info, CHA, IRQ_EXITHUNT); set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&info->event_wait_q, &wait); spin_unlock_irqrestore(&info->lock,flags); for(;;) { schedule(); if (signal_pending(current)) { rc = -ERESTARTSYS; break; } /* get current irq counts */ spin_lock_irqsave(&info->lock,flags); cnow = info->icount; newsigs = info->input_signal_events; set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&info->lock,flags); /* if no change, wait aborted for some reason */ if (newsigs.dsr_up == oldsigs.dsr_up && newsigs.dsr_down == oldsigs.dsr_down && newsigs.dcd_up == oldsigs.dcd_up && newsigs.dcd_down == oldsigs.dcd_down && newsigs.cts_up == oldsigs.cts_up && newsigs.cts_down == oldsigs.cts_down && newsigs.ri_up == oldsigs.ri_up && newsigs.ri_down == oldsigs.ri_down && cnow.exithunt == cprev.exithunt && cnow.rxidle == cprev.rxidle) { rc = -EIO; break; } events = mask & ( (newsigs.dsr_up != oldsigs.dsr_up ? MgslEvent_DsrActive:0) + (newsigs.dsr_down != oldsigs.dsr_down ? MgslEvent_DsrInactive:0) + (newsigs.dcd_up != oldsigs.dcd_up ? MgslEvent_DcdActive:0) + (newsigs.dcd_down != oldsigs.dcd_down ? MgslEvent_DcdInactive:0) + (newsigs.cts_up != oldsigs.cts_up ? MgslEvent_CtsActive:0) + (newsigs.cts_down != oldsigs.cts_down ? MgslEvent_CtsInactive:0) + (newsigs.ri_up != oldsigs.ri_up ? MgslEvent_RiActive:0) + (newsigs.ri_down != oldsigs.ri_down ? MgslEvent_RiInactive:0) + (cnow.exithunt != cprev.exithunt ? MgslEvent_ExitHuntMode:0) + (cnow.rxidle != cprev.rxidle ? MgslEvent_IdleReceived:0) ); if (events) break; cprev = cnow; oldsigs = newsigs; } remove_wait_queue(&info->event_wait_q, &wait); set_current_state(TASK_RUNNING); if (mask & MgslEvent_ExitHuntMode) { spin_lock_irqsave(&info->lock,flags); if (!waitqueue_active(&info->event_wait_q)) irq_disable(info, CHA, IRQ_EXITHUNT); spin_unlock_irqrestore(&info->lock,flags); }exit: if (rc == 0) PUT_USER(rc, events, mask_ptr); return rc;}static int modem_input_wait(MGSLPC_INFO *info,int arg){ unsigned long flags; int rc; struct mgsl_icount cprev, cnow; DECLARE_WAITQUEUE(wait, current); /* save current irq counts */ spin_lock_irqsave(&info->lock,flags); cprev = info->icount; add_wait_queue(&info->status_event_wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&info->lock,flags); for(;;) { schedule(); if (signal_pending(current)) { rc = -ERESTARTSYS; break; } /* get new irq counts */ spin_lock_irqsave(&info->lock,flags); cnow = info->icount; set_current_state(TASK_INTERRUPTIBLE); spin_unlock_irqrestore(&info->lock,flags); /* if no change, wait aborted for some reason */ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) { rc = -EIO; break; } /* check for change in caller specified modem input */ 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)) { rc = 0; break; } cprev = cnow; } remove_wait_queue(&info->status_event_wait_q, &wait); set_current_state(TASK_RUNNING); return rc;}/* return the state of the serial control and status signals */static int tiocmget(struct tty_struct *tty, struct file *file){ MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; unsigned int result; unsigned long flags; spin_lock_irqsave(&info->lock,flags); get_signals(info); spin_unlock_irqrestore(&info->lock,flags); result = ((info->serial_signals & SerialSignal_RTS) ? TIOCM_RTS:0) + ((info->serial_signals & SerialSignal_DTR) ? TIOCM_DTR:0) + ((info->serial_signals & SerialSignal_DCD) ? TIOCM_CAR:0) + ((info->serial_signals & SerialSignal_RI) ? TIOCM_RNG:0) + ((info->serial_signals & SerialSignal_DSR) ? TIOCM_DSR:0) + ((info->serial_signals & SerialSignal_CTS) ? TIOCM_CTS:0); if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s tiocmget() value=%08X\n", __FILE__,__LINE__, info->device_name, result ); return result;}/* set modem control signals (DTR/RTS) */static int tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear){ MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s tiocmset(%x,%x)\n", __FILE__,__LINE__,info->device_name, set, clear); if (set & TIOCM_RTS) info->serial_signals |= SerialSignal_RTS; if (set & TIOCM_DTR) info->serial_signals |= SerialSignal_DTR; if (clear & TIOCM_RTS) info->serial_signals &= ~SerialSignal_RTS; if (clear & TIOCM_DTR) info->serial_signals &= ~SerialSignal_DTR; spin_lock_irqsave(&info->lock,flags); set_signals(info); spin_unlock_irqrestore(&info->lock,flags); return 0;}/* Set or clear transmit break condition * * Arguments: tty pointer to tty instance data * break_state -1=set break condition, 0=clear */static void mgslpc_break(struct tty_struct *tty, int break_state){ MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgslpc_break(%s,%d)\n", __FILE__,__LINE__, info->device_name, break_state); if (mgslpc_paranoia_check(info, tty->name, "mgslpc_break")) return; spin_lock_irqsave(&info->lock,flags); if (break_state == -1) set_reg_bits(info, CHA+DAFO, BIT6); else clear_reg_bits(info, CHA+DAFO, BIT6); spin_unlock_irqrestore(&info->lock,flags);}/* Service an IOCTL request * * Arguments: * * tty pointer to tty instance data * file pointer to associated file object for device * cmd IOCTL command code * arg command argument/context * * Return Value: 0 if success, otherwise error code */static int mgslpc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgslpc_ioctl %s cmd=%08X\n", __FILE__,__LINE__, info->device_name, cmd ); if (mgslpc_paranoia_check(info, tty->name, "mgslpc_ioctl")) return -ENODEV; if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } return ioctl_common(info, cmd, arg);}int ioctl_common(MGSLPC_INFO *info, unsigned int cmd, unsigned long arg){ int error; struct mgsl_icount cnow; /* kernel counter temps */ struct serial_icounter_struct __user *p_cuser; /* user space */ void __user *argp = (void __user *)arg; unsigned long flags; switch (cmd) { case MGSL_IOCGPARAMS: return get_params(info, argp); case MGSL_IOCSPARAMS: return set_params(info, argp); case MGSL_IOCGTXIDLE: return get_txidle(info, argp); case MGSL_IOCSTXIDLE: return set_txidle(info, (int)arg); case MGSL_IOCGIF: return get_interface(info, argp); case MGSL_IOCSIF: return set_interface(info,(int)arg); case MGSL_IOCTXENABLE: return set_txenable(info,(int)arg); case MGSL_IOCRXENABLE: return set_rxenable(info,(int)arg); case MGSL_IOCTXABORT: return tx_abort(info); case MGSL_IOCGSTATS: return get_stats(info, argp); case MGSL_IOCWAITEVENT: return wait_events(info, argp); case TIOCMIWAIT: return modem_input_wait(info,(int)arg); case TIOCGICOUNT: spin_lock_irqsave(&info->lock,flags); cnow = info->icount; spin_unlock_irqrestore(&info->lock,flags); p_cuser = argp; PUT_USER(error,cnow.cts, &p_cuser->cts); if (error) return error; PUT_USER(error,cnow.dsr, &p_cuser->dsr); if (error) return error; PUT_USER(error,cnow.rng, &p_cuser->rng); if (error) return error; PUT_USER(error,cnow.dcd, &p_cuser->dcd); if (error) return error; PUT_USER(error,cnow.rx, &p_cuser->rx); if (error) return error; PUT_USER(error,cnow.tx, &p_cuser->tx); if (error) return error; PUT_USER(error,cnow.frame, &p_cuser->frame); if (error) return error; PUT_USER(error,cnow.overrun, &p_cuser->overrun); if (error) return error; PUT_USER(error,cnow.parity, &p_cuser->parity); if (error) return error; PUT_USER(error,cnow.brk, &p_cuser->brk); if (error) return error; PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun); if (error) return error; return 0; default: return -ENOIOCTLCMD; } return 0;}/* Set new termios settings * * Arguments: * * tty pointer to tty structure * termios pointer to buffer to hold returned old termios */static void mgslpc_set_termios(struct tty_struct *tty, struct termios *old_termios){ MGSLPC_INFO *info = (MGSLPC_INFO *)tty->driver_data; unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgslpc_set_termios %s\n", __FILE__,__LINE__, tty->driver->name ); /* just return if nothing has changed */ if ((tty->termios->c_cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) return; mgslpc_change_params(info); /* Handle transition to B0 status */ if (old_termios->c_cflag & CBAUD && !(tty->termios->c_cflag & CBAUD)) { info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); spin_lock_irqsave(&info->lock,flags); set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && tty->termios->c_cflag & CBAUD) { info->serial_signals |= SerialSignal_DTR; if (!(tty->termios->c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { info->serial_signals |= SerialSignal_RTS; } spin_lock_irqsave(&info->lock,flags); set_signals(info); spin_unlock_irqrestore(&info->lock,flags); } /* Handle turning off CRTSCTS */ if (old_termios->c_cflag & CRTSCTS && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; tx_release(tty); }}static void mgslpc_close(struct tty_struct *tty, struct file * filp){ MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; if (mgslpc_paranoia_check(info, tty->name, "mgslpc_close")) return; if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgslpc_close(%s) entry, count=%d\n", __FILE__,__LINE__, info->device_name, info->count); if (!info->count) return; if (tty_hung_up_p(filp)) goto cleanup; if ((tty->count == 1) && (info->count != 1)) { /* * tty->count is 1 and the tty structure will be freed. * info->count should be one in this case. * if it's not, correct it so that the port is shutdown. */ printk("mgslpc_close: bad refcount; tty->count is 1, " "info->count is %d\n", info->count); info->count = 1; } info->count--; /* if at least one open remaining, leave hardware active */ if (info->count) goto cleanup; info->flags |= ASYNC_CLOSING; /* set tty->closing to notify line discipline to * only process XON/XOFF characters. Only the N_TTY * discipline appears to use this (ppp does not). */ tty->closing = 1; /* wait for transmit data to clear all layers */ if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) { if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgslpc_close(%s) calling tty_wait_until_sent\n", __FILE__,__LINE__, info->device_name ); tty_wait_until_sent(tty, info->closing_wait); } if (info->flags & ASYNC_INITIALIZED) mgslpc_wait_until_sent(tty, info->timeout); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); ldisc_flush_buff
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -