From: Miroslav Ondra Date: Thu, 2 Aug 2018 10:03:41 +0000 (+0200) Subject: rewrite line discipline to be compatible with PARMRK X-Git-Url: http://git.graph-it.com/?a=commitdiff_plain;h=0d397c961f460bca4a6834ef5493683c75b56737;p=graphit%2Funipi-kernel.git rewrite line discipline to be compatible with PARMRK fixed ioctl timeout settings for 32b calling on 64bit system (compat_ioctl) --- diff --git a/modules/unipi/src/unipi_common.h b/modules/unipi/src/unipi_common.h index 20d2609..a09f806 100644 --- a/modules/unipi/src/unipi_common.h +++ b/modules/unipi/src/unipi_common.h @@ -41,7 +41,7 @@ #include #include #include -#include +//#include /*************** * Definitions * diff --git a/modules/unipi/src/unipi_spi.c b/modules/unipi/src/unipi_spi.c index 1dc4245..9557df7 100644 --- a/modules/unipi/src/unipi_spi.c +++ b/modules/unipi/src/unipi_spi.c @@ -1550,7 +1550,7 @@ static s32 __init neuronspi_init(void) if (neuronspi_invalidate_thread != NULL) { wake_up_process(neuronspi_invalidate_thread); } - neuronspi_tty_init(); + unipi_tty_init(); return ret; } diff --git a/modules/unipi/src/unipi_tty.c b/modules/unipi/src/unipi_tty.c index d26c26b..a18dbd0 100644 --- a/modules/unipi/src/unipi_tty.c +++ b/modules/unipi/src/unipi_tty.c @@ -1,39 +1,981 @@ /* - * UniPi PLC device driver - Copyright (C) 2018 UniPi Technology - * Author: Tomas Knot + * Implements line discpline for using with Neuron/Axon. * - * Based on the SC16IS7xx driver by Jon Ringle , - * which was in turn based on max310x.c, by Alexander Shiyan + * Derivated from n_tty.c --- implements the N_TTY line discipline. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This code used to be in tty_io.c, but things are getting hairy + * enough that it made sense to split things off. (The N_TTY + * processing has changed so much that it's hardly recognizable, + * anyway...) * + * Note that the open routine for N_TTY is guaranteed never to return + * an error. This is because Linux will fall back to setting a line + * to N_TTY if it can not switch to any other line discipline. + * + * Written by Theodore Ts'o, Copyright 1994. + * + * This file also contains code originally written by Linus Torvalds, + * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994. + * + * This file may be redistributed under the terms of the GNU General Public + * License. + * + * Reduced memory usage for older ARM systems - Russell King. + * + * 2000/01/20 Fixed SMP locking on put_tty_queue using bits of + * the patch by Andrew J. Kroll + * who actually finally proved there really was a race. + * + * 2002/03/18 Implemented unipi_tty_wakeup to send SIGIO POLL_OUTs to + * waiting writing processes-Sapan Bhatia . + * Also fixed a bug in BLOCKING mode where unipi_tty_write returns + * EAGAIN */ -#include "unipi_tty.h" +#include +#include +#include +#include +#include +#include #include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unipi_tty.h" + +/* number of characters left in xmit buffer before select has we have room */ +#define WAKEUP_CHARS 256 + +/* + * This defines the low- and high-watermarks for throttling and + * unthrottling the TTY driver. These watermarks are used for + * controlling the space in the read buffer. + */ +#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ +#define TTY_THRESHOLD_UNTHROTTLE 128 + + +// #undef UNIPI_TTY_TRACE +#ifdef NEURONSPI_DETAILED_DEBUG +# define unipi_tty_trace(f, args...) trace_printk(f, ##args) +#else +# define unipi_tty_trace(f, args...) +#endif + +struct unipi_tty_data { + /* producer-published */ + size_t read_head; + size_t commit_head; + //DECLARE_BITMAP(char_map, 256); -struct tty_ldisc_ops neuronspi_tty_ldisc; + /* private to unipi_tty_receive_overrun (single-threaded) */ + /* non-atomic */ + bool no_room; -int neuronspi_tty_init() + /* shared by producer and consumer */ + char read_buf[N_TTY_BUF_SIZE]; + + /* consumer-published */ + size_t read_tail; + + + struct mutex atomic_read_lock; + struct mutex output_lock; +}; + +static inline size_t read_cnt(struct unipi_tty_data *ldata) { - int err; -#if NEURONSPI_DETAILED_DEBUG > 0 - printk(KERN_INFO "UNIPISPI: TTY Init\n"); + return ldata->read_head - ldata->read_tail; +} + +static inline unsigned char read_buf(struct unipi_tty_data *ldata, size_t i) +{ + return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char *read_buf_addr(struct unipi_tty_data *ldata, size_t i) +{ + return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + + +/** + * tty_throttle_safe - flow control + * @tty: terminal + * + * Similar to tty_throttle() but will only attempt throttle + * if tty->flow_change is TTY_THROTTLE_SAFE. Prevents an accidental + * throttle due to race conditions when throttling is conditional + * on factors evaluated prior to throttling. + * + * Returns 0 if tty is throttled (or was already throttled) + */ + +int unipi_tty_throttle_safe(struct tty_struct *tty) +{ + int ret = 0; + + mutex_lock(&tty->throttle_mutex); + if (!tty_throttled(tty)) { + if (tty->flow_change != TTY_THROTTLE_SAFE) + ret = 1; + else { + set_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->throttle) + tty->ops->throttle(tty); + } + } + mutex_unlock(&tty->throttle_mutex); + + return ret; +} + +/** + * tty_unthrottle_safe - flow control + * @tty: terminal + * + * Similar to tty_unthrottle() but will only attempt unthrottle + * if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental + * unthrottle due to race conditions when unthrottling is conditional + * on factors evaluated prior to unthrottling. + * + * Returns 0 if tty is unthrottled (or was already unthrottled) + */ + +int unipi_tty_unthrottle_safe(struct tty_struct *tty) +{ + int ret = 0; + + mutex_lock(&tty->throttle_mutex); + if (tty_throttled(tty)) { + if (tty->flow_change != TTY_UNTHROTTLE_SAFE) + ret = 1; + else { + clear_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->unthrottle) + tty->ops->unthrottle(tty); + } + } + mutex_unlock(&tty->throttle_mutex); + + return ret; +} + + +/** + * unipi_tty_kick_worker - start input worker (if required) + * @tty: terminal + * + * Re-schedules the flip buffer work if it may have stopped + * + * Caller holds exclusive termios_rwsem + * or + * unipi_tty_read()/consumer path: + * holds non-exclusive termios_rwsem + */ + +static void unipi_tty_kick_worker(struct tty_struct *tty) +{ + struct unipi_tty_data *ldata = tty->disc_data; + struct tty_port *port = tty->port; + + /* Did the input worker stop? Restart it */ + if (unlikely(ldata->no_room)) { + ldata->no_room = 0; + + WARN_RATELIMIT(tty->port->itty == NULL, + "scheduling with invalid itty\n"); + /* see if ldisc has been killed - if so, this means that + * even though the ldisc has been halted and ->buf.work + * cancelled, ->buf.work is about to be rescheduled + */ + WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags), + "scheduling buffer work for halted ldisc\n"); + //tty_buffer_restart_work(tty->port); + queue_work(system_unbound_wq, &port->buf.work); + } +} + +static ssize_t chars_in_buffer(struct tty_struct *tty) +{ + struct unipi_tty_data *ldata = tty->disc_data; + ssize_t n = 0; + + n = ldata->commit_head - ldata->read_tail; + return n; +} + +/** + * unipi_tty_write_wakeup - asynchronous I/O notifier + * @tty: tty device + * + * Required for the ptys, serial driver etc. since processes + * that attach themselves to the master and rely on ASYNC + * IO must be woken up + */ + +static void unipi_tty_write_wakeup(struct tty_struct *tty) +{ + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Wakeup start."); + + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + kill_fasync(&tty->fasync, SIGIO, POLL_OUT); +} + +static void unipi_tty_check_throttle(struct tty_struct *tty) +{ + struct unipi_tty_data *ldata = tty->disc_data; + + /* + * Check the remaining room for the input canonicalization + * mode. We don't want to throttle the driver if we're in + * canonical mode and don't have a newline yet! + */ + + while (1) { + int throttled; + tty_set_flow_change(tty, TTY_THROTTLE_SAFE); + if (N_TTY_BUF_SIZE - read_cnt(ldata) >= TTY_THRESHOLD_THROTTLE) + break; + throttled = unipi_tty_throttle_safe(tty); + if (!throttled) + break; + } + __tty_set_flow_change(tty, 0); +} + +static void unipi_tty_check_unthrottle(struct tty_struct *tty) +{ + if (tty->driver->type == TTY_DRIVER_TYPE_PTY) { + if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + return; + unipi_tty_kick_worker(tty); + tty_wakeup(tty->link); + return; + } + + /* If there is enough space in the read buffer now, let the + * low-level driver know. We use chars_in_buffer() to + * check the buffer, as it now knows about canonical mode. + * Otherwise, if the driver is throttled and the line is + * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, + * we won't get any more characters. + */ + + while (1) { + int unthrottled; + tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); + if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + break; + unipi_tty_kick_worker(tty); + unthrottled = unipi_tty_unthrottle_safe(tty); + if (!unthrottled) + break; + } + __tty_set_flow_change(tty, 0); +} + +/** + * reset_buffer_flags - reset buffer state + * @tty: terminal to reset + * + * Reset the read buffer counters and clear the flags. + * Called from unipi_tty_open() and unipi_tty_flush_buffer(). + * + * Locking: caller holds exclusive termios_rwsem + * (or locking is not required) + */ + +static void reset_buffer_flags(struct unipi_tty_data *ldata) +{ + ldata->read_head = ldata->read_tail = 0; + ldata->commit_head = 0; + +} + +static void unipi_tty_packet_mode_flush(struct tty_struct *tty) +{ + unsigned long flags; + + if (tty->link->packet) { + spin_lock_irqsave(&tty->ctrl_lock, flags); + tty->ctrl_status |= TIOCPKT_FLUSHREAD; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + wake_up_interruptible(&tty->link->read_wait); + } +} + +/** + * unipi_tty_flush_buffer - clean input queue + * @tty: terminal device + * + * Flush the input buffer. Called when the tty layer wants the + * buffer flushed (eg at hangup) or when the N_TTY line discipline + * internally has to clean the pending queue (for example some signals). + * + * Holds termios_rwsem to exclude producer/consumer while + * buffer indices are reset. + * + * Locking: ctrl_lock, exclusive termios_rwsem + */ + +static void unipi_tty_flush_buffer(struct tty_struct *tty) +{ + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Flush start."); + + down_write(&tty->termios_rwsem); + reset_buffer_flags(tty->disc_data); + unipi_tty_kick_worker(tty); + + if (tty->link) + unipi_tty_packet_mode_flush(tty); + up_write(&tty->termios_rwsem); +} + + + +static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct unipi_tty_data *ldata = tty->disc_data; + size_t n, head; + + head = ldata->read_head & (N_TTY_BUF_SIZE - 1); + n = min_t(size_t, count, N_TTY_BUF_SIZE - head); + memcpy(read_buf_addr(ldata, head), cp, n); + ldata->read_head += n; + cp += n; + count -= n; + + head = ldata->read_head & (N_TTY_BUF_SIZE - 1); + n = min_t(size_t, count, N_TTY_BUF_SIZE - head); + memcpy(read_buf_addr(ldata, head), cp, n); + ldata->read_head += n; + + /* publish read_head to consumer */ + smp_store_release(&ldata->commit_head, ldata->read_head); + + if (read_cnt(ldata)) { + kill_fasync(&tty->fasync, SIGIO, POLL_IN); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); + } +} + +/** + * unipi_tty_receive_buf_common - process input + * @tty: device to receive input + * @cp: input chars + * @fp: flags for each char (if NULL, all chars are TTY_NORMAL) + * @count: number of input chars in @cp + * + * Called by the terminal driver when a block of characters has + * been received. This function must be called from soft contexts + * not from interrupt context. The driver is responsible for making + * calls one at a time and in order (or using flush_to_ldisc) + * + * Returns the # of input chars from @cp which were processed. + * + * In canonical mode, the maximum line length is 4096 chars (including + * the line termination char); lines longer than 4096 chars are + * truncated. After 4095 chars, input data is still processed but + * not stored. Overflow processing ensures the tty can always + * receive more input until at least one line can be read. + * + * In non-canonical mode, the read buffer will only accept 4095 chars; + * this provides the necessary space for a newline char if the input + * mode is switched to canonical. + * + * Note it is possible for the read buffer to _contain_ 4096 chars + * in non-canonical mode: the read buffer could already contain the + * maximum canon line of 4096 chars when the mode is switched to + * non-canonical. + * + * unipi_tty_receive_buf()/producer path: + * claims non-exclusive termios_rwsem + * publishes commit_head or canon_head + */ +static int +unipi_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count, int flow) +{ + struct unipi_tty_data *ldata = tty->disc_data; + int room, n, rcvd = 0; + + down_read(&tty->termios_rwsem); + + while (1) { + + size_t tail = smp_load_acquire(&ldata->read_tail); + + room = N_TTY_BUF_SIZE - (ldata->read_head - tail); + room--; + if (room <= 0) { + room = 0; + ldata->no_room = flow; + } + n = min(count, room); + if (!n) + break; + + __receive_buf(tty, cp, fp, n); + + cp += n; + count -= n; + rcvd += n; + } + + tty->receive_room = room; + + /* Unthrottle if handling overflow on pty */ + if (tty->driver->type == TTY_DRIVER_TYPE_PTY) { + } else + unipi_tty_check_throttle(tty); + + up_read(&tty->termios_rwsem); + + return rcvd; +} + +static void unipi_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Buf start."); + unipi_tty_receive_buf_common(tty, cp, fp, count, 0); +} + +static int unipi_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Buf2 start."); + return unipi_tty_receive_buf_common(tty, cp, fp, count, 1); +} + +/** + * unipi_tty_set_termios - termios data changed + * @tty: terminal + * @old: previous data + * + * Called by the tty layer when the user changes termios flags so + * that the line discipline can plan ahead. This function cannot sleep + * and is protected from re-entry by the tty layer. The user is + * guaranteed that this function will not be re-entered or in progress + * when the ldisc is closed. + * + * Locking: Caller holds tty->termios_rwsem + */ + +static void unipi_tty_set_termios(struct tty_struct *tty, struct ktermios *old) +{ + struct unipi_tty_data *ldata = tty->disc_data; + int lx=0; int ix=0; int ox=0; int cx = 0; + if (old) { + lx = old->c_lflag; + ix = old->c_iflag; + ox = old->c_oflag; + cx = old->c_cflag; + } + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Termios new:0x%x %x %x %x old:0x%x %x %x %x", \ + tty->termios.c_lflag, tty->termios.c_iflag, tty->termios.c_oflag, tty->termios.c_cflag,\ + lx, ix, ox, cx); + if (!old || (old->c_lflag ^ tty->termios.c_lflag) & (ICANON | EXTPROC)) { + ldata->commit_head = ldata->read_head; + } + + /* + * Fix tty hang when I_IXON(tty) is cleared, but the tty + * been stopped by STOP_CHAR(tty) before it. + */ + if (!I_IXON(tty) && old && (old->c_iflag & IXON) && !tty->flow_stopped) { + start_tty(tty); + } + + /* The termios change make the tty ready for I/O */ + wake_up_interruptible(&tty->write_wait); + wake_up_interruptible(&tty->read_wait); +} + +/** + * unipi_tty_close - close the ldisc for this tty + * @tty: device + * + * Called from the terminal layer when this line discipline is + * being shut down, either because of a close or becsuse of a + * discipline change. The function will not be called while other + * ldisc methods are in progress. + */ + +static void unipi_tty_close(struct tty_struct *tty) +{ + struct unipi_tty_data *ldata = tty->disc_data; + + if (tty->link) + unipi_tty_packet_mode_flush(tty); + + vfree(ldata); + tty->disc_data = NULL; + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Close OK."); +} + +/** + * unipi_tty_open - open an ldisc + * @tty: terminal to open + * + * Called when this line discipline is being attached to the + * terminal device. Can sleep. Called serialized so that no + * other events will occur in parallel. No further open will occur + * until a close. + */ + +static int unipi_tty_open(struct tty_struct *tty) +{ + struct unipi_tty_data *ldata; + + /* Currently a malloc failure here can panic */ + ldata = vmalloc(sizeof(*ldata)); + if (!ldata) + goto err; + + mutex_init(&ldata->atomic_read_lock); + mutex_init(&ldata->output_lock); + + tty->disc_data = ldata; + reset_buffer_flags(tty->disc_data); + ldata->no_room = 0; + tty->closing = 0; + /* indicate buffer work may resume */ + clear_bit(TTY_LDISC_HALTED, &tty->flags); + unipi_tty_set_termios(tty, NULL); + tty_unthrottle(tty); + + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Open OK."); + + return 0; +err: + return -ENOMEM; +} + +static inline int input_available_p(struct tty_struct *tty, int poll) +{ + struct unipi_tty_data *ldata = tty->disc_data; + int amt = poll && !TIME_CHAR(tty) && MIN_CHAR(tty) ? MIN_CHAR(tty) : 1; + + return ldata->commit_head - ldata->read_tail >= amt; +} + +/** + * copy_from_read_buf - copy read data directly + * @tty: terminal device + * @b: user data + * @nr: size of data + * + * Helper function to speed up unipi_tty_read. It is only called when + * ICANON is off; it copies characters straight from the tty queue to + * user space directly. It can be profitably called twice; once to + * drain the space from the tail pointer to the (physical) end of the + * buffer, and once to drain the space from the (physical) beginning of + * the buffer to head pointer. + * + * Called under the ldata->atomic_read_lock sem + * + * unipi_tty_read()/consumer path: + * caller holds non-exclusive termios_rwsem + * read_tail published + */ + +static int copy_from_read_buf(struct tty_struct *tty, + unsigned char __user **b, + size_t *nr) + +{ + struct unipi_tty_data *ldata = tty->disc_data; + int retval; + size_t n; + size_t head = smp_load_acquire(&ldata->commit_head); + size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); + + retval = 0; + n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail); + n = min(*nr, n); + if (n) { + const unsigned char *from = read_buf_addr(ldata, tail); + retval = copy_to_user(*b, from, n); + n -= retval; + //tty_audit_add_data(tty, from, n); + smp_store_release(&ldata->read_tail, ldata->read_tail + n); + *b += n; + *nr -= n; + } + return retval; +} + + + +/** + * unipi_tty_read - read function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Perform reads for the line discipline. We are guaranteed that the + * line discipline will not be closed under us but we may get multiple + * parallel readers and must handle this ourselves. We may also get + * a hangup. Always called in user context, may sleep. + * + * This code must be sure never to sleep through a hangup. + * + * unipi_tty_read()/consumer path: + * claims non-exclusive termios_rwsem + * publishes read_tail + */ + +static ssize_t unipi_tty_read(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr) +{ + struct unipi_tty_data *ldata = tty->disc_data; + struct tty_port *port = tty->port; + unsigned char __user *b = buf; + DEFINE_WAIT_FUNC(wait, woken_wake_function); + int minimum, time; + ssize_t retval = 0; + long timeout; + int packet; + size_t tail; + + /* + * Internal serialization of reads. + */ + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Read start."); + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&ldata->atomic_read_lock)) + return -EAGAIN; + } else { + if (mutex_lock_interruptible(&ldata->atomic_read_lock)) + return -ERESTARTSYS; + } + + down_read(&tty->termios_rwsem); + + minimum = time = 0; + timeout = MAX_SCHEDULE_TIMEOUT; + + minimum = MIN_CHAR(tty); + if (minimum) { + time = (HZ / 10) * TIME_CHAR(tty); + } else { + timeout = (HZ / 10) * TIME_CHAR(tty); + minimum = 1; + } + + packet = tty->packet; + tail = ldata->read_tail; + + add_wait_queue(&tty->read_wait, &wait); + while (nr) { + /* First test for status change. */ + if (packet && tty->link->ctrl_status) { + unsigned char cs; + if (b != buf) + break; + spin_lock_irq(&tty->link->ctrl_lock); + cs = tty->link->ctrl_status; + tty->link->ctrl_status = 0; + spin_unlock_irq(&tty->link->ctrl_lock); + if (put_user(cs, b)) { + retval = -EFAULT; + break; + } + b++; + nr--; + break; + } + + if (!input_available_p(tty, 0)) { + up_read(&tty->termios_rwsem); + //tty_buffer_flush_work(tty->port); + flush_work(&port->buf.work); + down_read(&tty->termios_rwsem); + if (!input_available_p(tty, 0)) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { + retval = -EIO; + break; + } + if (tty_hung_up_p(file)) + break; + /* + * Abort readers for ttys which never actually + * get hung up. See __tty_hangup(). + */ +#ifdef TTY_HUPPING + if (test_bit(TTY_HUPPING, &tty->flags)) + break; #endif - memset(&neuronspi_tty_ldisc, 0, sizeof(neuronspi_tty_ldisc)); - n_tty_inherit_ops(&neuronspi_tty_ldisc); - neuronspi_tty_ldisc.magic = TTY_LDISC_MAGIC; - neuronspi_tty_ldisc.name = "unipi_tty"; - neuronspi_tty_ldisc.owner = THIS_MODULE; - err = tty_register_ldisc(N_PROFIBUS_FDL, &neuronspi_tty_ldisc); + if (!timeout) + break; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + up_read(&tty->termios_rwsem); + + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, + timeout); + + down_read(&tty->termios_rwsem); + continue; + } + } + + { + int uncopied; + + /* Deal with packet mode. */ + if (packet && b == buf) { + if (put_user(TIOCPKT_DATA, b)) { + retval = -EFAULT; + break; + } + b++; + nr--; + } + + uncopied = copy_from_read_buf(tty, &b, &nr); + uncopied += copy_from_read_buf(tty, &b, &nr); + if (uncopied) { + retval = -EFAULT; + break; + } + } + + unipi_tty_check_unthrottle(tty); + + if (b - buf >= minimum) + break; + if (time) + timeout = time; + } + if (tail != ldata->read_tail) + unipi_tty_kick_worker(tty); + up_read(&tty->termios_rwsem); + + remove_wait_queue(&tty->read_wait, &wait); + mutex_unlock(&ldata->atomic_read_lock); + + if (b - buf) + retval = b - buf; + + return retval; +} + +/** + * unipi_tty_write - write function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Write function of the terminal device. This is serialized with + * respect to other write callers but not to termios changes, reads + * and other such events. Since the receive code will echo characters, + * thus calling driver write methods, the output_lock is used in + * the output processing functions called here as well as in the + * echo processing function to protect the column state and space + * left in the buffer. + * + * This code must be sure never to sleep through a hangup. + * + * Locking: output_lock to protect column state and space left + * (note that the process_output*() functions take this + * lock themselves) + */ + +static ssize_t unipi_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) +{ + const unsigned char *b = buf; + DEFINE_WAIT_FUNC(wait, woken_wake_function); + struct unipi_tty_data *ldata; + int c; + ssize_t retval = 0; + + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Write start."); + down_read(&tty->termios_rwsem); + + add_wait_queue(&tty->write_wait, &wait); + while (1) { + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) { + retval = -EIO; + break; + } + + ldata = tty->disc_data; + + while (nr > 0) { + mutex_lock(&ldata->output_lock); + c = tty->ops->write(tty, b, nr); + mutex_unlock(&ldata->output_lock); + if (c < 0) { + retval = c; + goto break_out; + } + if (!c) + break; + b += c; + nr -= c; + } + + if (!nr) + break; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + up_read(&tty->termios_rwsem); + + wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); + + down_read(&tty->termios_rwsem); + } +break_out: + remove_wait_queue(&tty->write_wait, &wait); + if (nr && tty->fasync) + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + up_read(&tty->termios_rwsem); + return (b - buf) ? b - buf : retval; +} + +/** + * unipi_tty_poll - poll method for N_TTY + * @tty: terminal device + * @file: file accessing it + * @wait: poll table + * + * Called when the line discipline is asked to poll() for data or + * for special events. This code is not serialized with respect to + * other events save open/close. + * + * This code must be sure never to sleep through a hangup. + * Called without the kernel lock held - fine + */ + +static unsigned int unipi_tty_poll(struct tty_struct *tty, struct file *file, + poll_table *wait) +{ + unsigned int mask = 0; + struct tty_port *port = tty->port; + + //unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Poll start."); + poll_wait(file, &tty->read_wait, wait); + poll_wait(file, &tty->write_wait, wait); + if (input_available_p(tty, 1)) + mask |= POLLIN | POLLRDNORM; + else { + //tty_buffer_flush_work(tty->port); + flush_work(&port->buf.work); + if (input_available_p(tty, 1)) + mask |= POLLIN | POLLRDNORM; + } + if (tty->packet && tty->link->ctrl_status) + mask |= POLLPRI | POLLIN | POLLRDNORM; + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + mask |= POLLHUP; + if (tty_hung_up_p(file)) + mask |= POLLHUP; + if (tty->ops->write && !tty_is_writelocked(tty) && + tty_chars_in_buffer(tty) < WAKEUP_CHARS && + tty_write_room(tty) > 0) + mask |= POLLOUT | POLLWRNORM; + return mask; +} + + +static int unipi_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct unipi_tty_data *ldata = tty->disc_data; + int retval; + + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Ioctl start. cmd=%x", cmd); + switch (cmd) { + case TIOCOUTQ: + return put_user(tty_chars_in_buffer(tty), (int __user *) arg); + case TIOCINQ: + down_write(&tty->termios_rwsem); + retval = read_cnt(ldata); + up_write(&tty->termios_rwsem); + return put_user(retval, (unsigned int __user *) arg); + default: + return n_tty_ioctl_helper(tty, file, cmd, arg); + } +} + +static long unipi_tty_compat_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int retval; + + unipi_tty_trace(KERN_INFO "UNIPI_LDISC: Compat_ioctl start. cmd=%x", cmd); + switch (cmd) { + case 0x5481: + case 0x5480: + if (tty->ops->ioctl) { + retval = tty->ops->ioctl(tty, cmd, (unsigned long) compat_ptr(arg)); + if (retval != -ENOIOCTLCMD) + return retval; + } + //return tty_mode_ioctl(tty, file, cmd, (unsigned long) compat_ptr(arg)); + default: + return n_tty_compat_ioctl_helper(tty, file, cmd, arg); + } +} + + +static struct tty_ldisc_ops unipi_tty_ops = { + .magic = TTY_LDISC_MAGIC, + .owner = THIS_MODULE, + .name = "unipi_tty", + .open = unipi_tty_open, + .close = unipi_tty_close, + .flush_buffer = unipi_tty_flush_buffer, + .read = unipi_tty_read, + .write = unipi_tty_write, + .ioctl = unipi_tty_ioctl, + .compat_ioctl = unipi_tty_compat_ioctl, + .set_termios = unipi_tty_set_termios, + .poll = unipi_tty_poll, + .receive_buf = unipi_tty_receive_buf, + .write_wakeup = unipi_tty_write_wakeup, + .receive_buf2 = unipi_tty_receive_buf2, +}; + + + +int __init unipi_tty_init(void) +{ + int err; + unipi_tty_trace(KERN_INFO "UNIPISPI: TTY Init\n"); + err = tty_register_ldisc(N_PROFIBUS_FDL, &unipi_tty_ops); if (err) { printk(KERN_INFO "UNIPISPI: UniPi line discipline registration failed. (%d)", err); return err; diff --git a/modules/unipi/src/unipi_tty.h b/modules/unipi/src/unipi_tty.h index 1d98f19..371f133 100644 --- a/modules/unipi/src/unipi_tty.h +++ b/modules/unipi/src/unipi_tty.h @@ -17,6 +17,6 @@ #include "unipi_common.h" -int neuronspi_tty_init(void); +int unipi_tty_init(void); #endif /* MODULES_UNIPI_SRC_UNIPI_TTY_H_ */ diff --git a/modules/unipi/src/unipi_uart.c b/modules/unipi/src/unipi_uart.c index 48b6b8e..d0b875d 100644 --- a/modules/unipi/src/unipi_uart.c +++ b/modules/unipi/src/unipi_uart.c @@ -67,7 +67,7 @@ void neuronspi_uart_set_ldisc(struct uart_port *port, struct ktermios *kterm) spi = neuronspi_s_dev[n_port->dev_index]; n_spi = spi_get_drvdata(spi); #if NEURONSPI_DETAILED_DEBUG > 0 - printk(KERN_INFO "UNIPISPI: PROFIBUS discipline set\n"); + printk(KERN_INFO "UNIPISPI: line discipline set\n"); #endif write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_LDISC_REGISTER, &inp_buf, &outp_buf, kterm->c_line); neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0); @@ -124,7 +124,9 @@ int neuronspi_uart_ioctl (struct uart_port *port, unsigned int ioctl_code, unsig printk(KERN_INFO "UNIPISPI: IOCTL 0x5481\n"); #endif write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_TIMEOUT_REGISTER, &inp_buf, &outp_buf, (ioctl_arg * 1000000) / n_port->baud); - printk(KERN_INFO "UNIPISPI: val_upper: %x, val_lower: %x", inp_buf[10], inp_buf[11]); +#if NEURONSPI_DETAILED_DEBUG > 0 + printk(KERN_INFO "UNIPISPI: val_upper: 0x%x, val_lower: 0x%x", inp_buf[10], inp_buf[11]); +#endif neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0); kfree(inp_buf); kfree(outp_buf); @@ -135,7 +137,9 @@ int neuronspi_uart_ioctl (struct uart_port *port, unsigned int ioctl_code, unsig printk(KERN_INFO "UNIPISPI: IOCTL 0x5480\n"); #endif write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_TIMEOUT_REGISTER, &inp_buf, &outp_buf, ioctl_arg * 10); - printk(KERN_INFO "UNIPISPI: val_upper: %x, val_lower: %x", inp_buf[10], inp_buf[11]); +#if NEURONSPI_DETAILED_DEBUG > 0 + printk(KERN_INFO "UNIPISPI: val_upper: 0x%x, val_lower: 0x%x", inp_buf[10], inp_buf[11]); +#endif neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0); kfree(inp_buf); kfree(outp_buf); @@ -153,26 +157,27 @@ void neuronspi_uart_set_termios(struct uart_port *port, struct ktermios *termios { struct neuronspi_port *n_port; n_port = to_neuronspi_port(port, port); - if (old && old->c_iflag && old->c_iflag != termios->c_iflag) { + #if NEURONSPI_DETAILED_DEBUG > 0 - printk(KERN_INFO "UNIPISPI: c_iflag termios:%d\n", termios->c_iflag); -#endif + if (old && old->c_iflag && old->c_iflag != termios->c_iflag) { + printk(KERN_INFO "UNIPI_UART: c_iflag termios:%d\n", termios->c_iflag); } -#if NEURONSPI_DETAILED_DEBUG > 0 - printk(KERN_DEBUG "UNIPISPI: TERMIOS Set, p:%d, c_cflag:%x\n", port->line, termios->c_cflag); + printk(KERN_INFO "UNIPI_UART: Termios port:%d new:0x%x %x %x %x", port->line,\ + termios->c_lflag, termios->c_iflag, \ + termios->c_oflag, termios->c_cflag); #endif neuronspi_spi_uart_set_cflag(neuronspi_s_dev[n_port->dev_index], n_port->dev_port, termios->c_cflag); if (old && termios && (old->c_iflag & PARMRK) != (termios->c_iflag & PARMRK)) { neuronspi_uart_set_iflags(port, termios->c_iflag); +#if 0 + /* No more required to disable PARMRK */ if (termios->c_iflag & PARMRK) { termios->c_iflag &= ~PARMRK; } - } - if (old && termios && old->c_line != termios->c_line) { - if (termios->c_line == N_PROFIBUS_FDL) { -#if NEURONSPI_DETAILED_DEBUG > 0 - printk(KERN_INFO "UNIPISPI: Line Discipline change\n"); #endif + } + if (termios) { + if (!old || old->c_line != termios->c_line) { neuronspi_uart_set_ldisc(port, termios); } }