From eb48d03335847781637e4fc6e86bcc88dd4cb0f0 Mon Sep 17 00:00:00 2001 From: Tom Knot Date: Thu, 10 May 2018 15:28:13 +0200 Subject: [PATCH] Fixed LINEDISCIPL return value and SYSFS counter byte order --- modules/unipi/Makefile | 4 +- modules/unipi/src/unipi_common.h | 2 +- modules/unipi/src/unipi_platform.c | 2 +- modules/unipi/src/unipi_spi.c | 2 +- modules/unipi/src/unipi_sysfs.c | 6 +- modules/unipi/src/unipi_tty.c | 2639 +--------------------------- modules/unipi/src/unipi_uart.c | 12 +- 7 files changed, 18 insertions(+), 2649 deletions(-) diff --git a/modules/unipi/Makefile b/modules/unipi/Makefile index 83eed53..e201691 100644 --- a/modules/unipi/Makefile +++ b/modules/unipi/Makefile @@ -3,7 +3,7 @@ # if necessary. SYMLINK_DIR_PATH = /run/kernel/neuron_spi -LINUX_DIR_PATH = /root/linux/4_9_41 +LINUX_DIR_PATH = /root/bokula/linux/ SRC_DIR_PATH = $(PWD)/src BIN_DIR_PATH = $(PWD)/bin @@ -41,7 +41,7 @@ KERNEL_MODULE_NAME = unipi obj-m += ${KERNEL_MODULE_NAME}.o unipi-objs := ${OBJ_FILES} -TARGET_PLC_PATH = tomunipi:/root/ +TARGET_PLC_PATH = tomunipi: .PHONY: default default: symlink ; diff --git a/modules/unipi/src/unipi_common.h b/modules/unipi/src/unipi_common.h index 3bac807..feb5c67 100644 --- a/modules/unipi/src/unipi_common.h +++ b/modules/unipi/src/unipi_common.h @@ -47,7 +47,7 @@ * Definitions * ***************/ -#define NEURONSPI_SCHED_REQUIRED 1 // Older kernels do not require sched/types to be specifically imported +#define NEURONSPI_SCHED_REQUIRED 0 // Older kernels do not require sched/types to be specifically imported #if NEURONSPI_SCHED_REQUIRED > 0 #include #endif diff --git a/modules/unipi/src/unipi_platform.c b/modules/unipi/src/unipi_platform.c index 7266711..4e252a5 100644 --- a/modules/unipi/src/unipi_platform.c +++ b/modules/unipi/src/unipi_platform.c @@ -1652,7 +1652,7 @@ int neuronspi_regmap_hw_gather_write(void *context, const void *reg, size_t reg_ int neuronspi_create_reg_starts(struct neuronspi_board_regstart_table *out_table, struct neuronspi_board_combination *board) { if (board->features.di_count > 0) { out_table->di_val_reg = neuronspi_find_reg_start(board, NEURONSPI_REGFUN_DI_READ); - out_table->di_counter_reg = neuronspi_find_reg_start(board, NEURONSPI_REGFUN_DI_COUNTER_UPPER); + out_table->di_counter_reg = neuronspi_find_reg_start(board, NEURONSPI_REGFUN_DI_COUNTER_LOWER); out_table->di_deboun_reg = neuronspi_find_reg_start(board, NEURONSPI_REGFUN_DI_DEBOUNCE); out_table->di_direct_reg = neuronspi_find_reg_start(board, NEURONSPI_REGFUN_DS_ENABLE); out_table->di_polar_reg = neuronspi_find_reg_start(board, NEURONSPI_REGFUN_DS_POLARITY); diff --git a/modules/unipi/src/unipi_spi.c b/modules/unipi/src/unipi_spi.c index b2fcdcf..4a2ce4e 100644 --- a/modules/unipi/src/unipi_spi.c +++ b/modules/unipi/src/unipi_spi.c @@ -1552,7 +1552,7 @@ static s32 __init neuronspi_init(void) if (neuronspi_invalidate_thread != NULL) { wake_up_process(neuronspi_invalidate_thread); } - //neuronspi_tty_init(); + neuronspi_tty_init(); return ret; } diff --git a/modules/unipi/src/unipi_sysfs.c b/modules/unipi/src/unipi_sysfs.c index 3bc08ba..de883e0 100644 --- a/modules/unipi/src/unipi_sysfs.c +++ b/modules/unipi/src/unipi_sysfs.c @@ -318,6 +318,7 @@ static ssize_t neuronspi_spi_gpio_di_show_counter(struct device *dev, struct dev { ssize_t ret = 0; u32 val = 0; + u32 val_upper = 0; struct neuronspi_di_driver *n_di; struct neuronspi_driver_data *n_spi; struct platform_device *plat = to_platform_device(dev); @@ -325,6 +326,8 @@ static ssize_t neuronspi_spi_gpio_di_show_counter(struct device *dev, struct dev n_spi = spi_get_drvdata(n_di->spi); if (n_spi && n_spi->combination_id != 0xFF && n_spi->reg_map) { regmap_read(n_spi->reg_map, n_spi->regstart_table->di_counter_reg + (2 * n_di->di_index), &val); + regmap_read(n_spi->reg_map, n_spi->regstart_table->di_counter_reg + 1 + (2 * n_di->di_index), &val_upper); + val |= val_upper << 16; ret = scnprintf(buf, 255, "%d\n", val); } return ret; @@ -342,7 +345,8 @@ static ssize_t neuronspi_spi_gpio_di_store_counter(struct device *dev, struct de err = kstrtouint(buf, 0, &val); if (err < 0) goto err_end; if (n_spi && n_spi->combination_id != 0xFF && n_spi->reg_map) { - regmap_write(n_spi->reg_map, n_spi->regstart_table->di_counter_reg + (2 * n_di->di_index), val); + regmap_write(n_spi->reg_map, n_spi->regstart_table->di_counter_reg + (2 * n_di->di_index), val & 0xFFFF); + regmap_write(n_spi->reg_map, n_spi->regstart_table->di_counter_reg + 1 + (2 * n_di->di_index), val >> 16); } err_end: return count; diff --git a/modules/unipi/src/unipi_tty.c b/modules/unipi/src/unipi_tty.c index 8cc65f4..222ae77 100644 --- a/modules/unipi/src/unipi_tty.c +++ b/modules/unipi/src/unipi_tty.c @@ -13,2659 +13,24 @@ */ #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 - -/* 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 - -/* - * Special byte codes used in the echo buffer to represent operations - * or special handling of characters. Bytes in the echo buffer that - * are not part of such special blocks are treated as normal character - * codes. - */ -#define ECHO_OP_START 0xff -#define ECHO_OP_MOVE_BACK_COL 0x80 -#define ECHO_OP_SET_CANON_COL 0x81 -#define ECHO_OP_ERASE_TAB 0x82 - -#define ECHO_COMMIT_WATERMARK 256 -#define ECHO_BLOCK 256 -#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32) - - -#undef N_TTY_TRACE -#ifdef N_TTY_TRACE -# define n_tty_trace(f, args...) trace_printk(f, ##args) -#else -# define n_tty_trace(f, args...) -#endif - -struct neuronspi_tty_data { - /* producer-published */ - size_t read_head; - size_t commit_head; - size_t canon_head; - size_t echo_head; - size_t echo_commit; - size_t echo_mark; - DECLARE_BITMAP(char_map, 256); - - /* private to n_tty_receive_overrun (single-threaded) */ - unsigned long overrun_time; - int num_overrun; - - /* non-atomic */ - bool no_room; - - /* must hold exclusive termios_rwsem to reset these */ - unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; - unsigned char push:1; - - /* shared by producer and consumer */ - char read_buf[N_TTY_BUF_SIZE]; - DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); - unsigned char echo_buf[N_TTY_BUF_SIZE]; - - /* consumer-published */ - size_t read_tail; - size_t line_start; - - /* protected by output lock */ - unsigned int column; - unsigned int canon_column; - size_t echo_tail; - - struct mutex atomic_read_lock; - struct mutex output_lock; -}; - -static DEFINE_SPINLOCK(redirect_lock); -static struct file *redirect; - -static inline struct tty_struct *file_tty(struct file *file) -{ - return ((struct tty_file_private *)file->private_data)->tty; -} - -static int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, - const char *routine) -{ -#ifdef TTY_PARANOIA_CHECK - if (!tty) { - pr_warn("(%d:%d): %s: NULL tty\n", - imajor(inode), iminor(inode), routine); - return 1; - } - if (tty->magic != TTY_MAGIC) { - pr_warn("(%d:%d): %s: bad magic number\n", - imajor(inode), iminor(inode), routine); - return 1; - } -#endif - return 0; -} - -//static ssize_t hung_up_tty_read(struct file *file, char __user *buf, -// size_t count, loff_t *ppos) -//{ -// return 0; -//} - -static ssize_t hung_up_tty_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - return -EIO; -} - -static void tty_write_unlock(struct tty_struct *tty) -{ - mutex_unlock(&tty->atomic_write_lock); - wake_up_interruptible_poll(&tty->write_wait, POLLOUT); -} - -static int tty_write_lock(struct tty_struct *tty, int ndelay) -{ - if (!mutex_trylock(&tty->atomic_write_lock)) { - if (ndelay) - return -EAGAIN; - if (mutex_lock_interruptible(&tty->atomic_write_lock)) - return -ERESTARTSYS; - } - return 0; -} - -static int is_ignored(int sig) -{ - return (sigismember(¤t->blocked, sig) || - current->sighand->action[sig-1].sa.sa_handler == SIG_IGN); -} - -int __tty_check_change(struct tty_struct *tty, int sig) -{ - unsigned long flags; - struct pid *pgrp, *tty_pgrp; - int ret = 0; - - if (current->signal->tty != tty) - return 0; - - rcu_read_lock(); - pgrp = task_pgrp(current); - - spin_lock_irqsave(&tty->ctrl_lock, flags); - tty_pgrp = tty->pgrp; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - - if (tty_pgrp && pgrp != tty->pgrp) { - if (is_ignored(sig)) { - if (sig == SIGTTIN) - ret = -EIO; - } //else if (is_current_pgrp_orphaned()) - //ret = -EIO; - else { - kill_pgrp(pgrp, sig, 1); - set_thread_flag(TIF_SIGPENDING); - ret = -ERESTARTSYS; - } - } - rcu_read_unlock(); - - if (!tty_pgrp) - tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig); - - return ret; -} - -static void tty_update_time(struct timespec *time) -{ - unsigned long sec = get_seconds(); - - /* - * We only care if the two values differ in anything other than the - * lower three bits (i.e every 8 seconds). If so, then we can update - * the time of the tty device, otherwise it could be construded as a - * security leak to let userspace know the exact timing of the tty. - */ - if ((sec ^ time->tv_sec) & ~7) - time->tv_sec = sec; -} - - -static inline ssize_t do_tty_write( - ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t), - struct tty_struct *tty, - struct file *file, - const char __user *buf, - size_t count) -{ - ssize_t ret, written = 0; - unsigned int chunk; - - ret = tty_write_lock(tty, file->f_flags & O_NDELAY); - if (ret < 0) - return ret; - - /* - * We chunk up writes into a temporary buffer. This - * simplifies low-level drivers immensely, since they - * don't have locking issues and user mode accesses. - * - * But if TTY_NO_WRITE_SPLIT is set, we should use a - * big chunk-size.. - * - * The default chunk-size is 2kB, because the NTTY - * layer has problems with bigger chunks. It will - * claim to be able to handle more characters than - * it actually does. - * - * FIXME: This can probably go away now except that 64K chunks - * are too likely to fail unless switched to vmalloc... - */ - chunk = 2048; - if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags)) - chunk = 65536; - if (count < chunk) - chunk = count; - - /* write_buf/write_cnt is protected by the atomic_write_lock mutex */ - if (tty->write_cnt < chunk) { - unsigned char *buf_chunk; - - if (chunk < 1024) - chunk = 1024; - - buf_chunk = kmalloc(chunk, GFP_KERNEL); - if (!buf_chunk) { - ret = -ENOMEM; - goto out; - } - kfree(tty->write_buf); - tty->write_cnt = chunk; - tty->write_buf = buf_chunk; - } - - /* Do the write .. */ - for (;;) { - size_t size = count; - if (size > chunk) - size = chunk; - ret = -EFAULT; - if (copy_from_user(tty->write_buf, buf, size)) - break; - ret = write(tty, file, tty->write_buf, size); - if (ret <= 0) - break; - written += ret; - buf += ret; - count -= ret; - if (!count) - break; - ret = -ERESTARTSYS; - if (signal_pending(current)) - break; - cond_resched(); - } - if (written) { - tty_update_time(&file_inode(file)->i_mtime); - ret = written; - } -out: - tty_write_unlock(tty); - return ret; -} - -static ssize_t tty_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct tty_struct *tty = file_tty(file); - struct tty_ldisc *ld; - ssize_t ret; - - if (tty_paranoia_check(tty, file_inode(file), "tty_write")) - return -EIO; - if (!tty || !tty->ops->write || tty_io_error(tty)) - return -EIO; - /* Short term debug to catch buggy drivers */ - if (tty->ops->write_room == NULL) - tty_err(tty, "missing write_room method\n"); - ld = tty_ldisc_ref_wait(tty); - if (!ld) - return hung_up_tty_write(file, buf, count, ppos); - if (!ld->ops->write) - ret = -EIO; - else - ret = do_tty_write(ld->ops->write, tty, file, buf, count); - tty_ldisc_deref(ld); - return ret; -} - - -const char *tty_driver_name(const struct tty_struct *tty) -{ - if (!tty || !tty->driver) - return ""; - return tty->driver->name; -} - -int 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 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; -} - -bool tty_buffer_restart_work(struct tty_port *port) -{ - return queue_work(system_unbound_wq, &port->buf.work); -} - -bool tty_buffer_cancel_work(struct tty_port *port) -{ - return cancel_work_sync(&port->buf.work); -} - -ssize_t redirected_tty_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct file *p = NULL; - - spin_lock(&redirect_lock); - if (redirect) - p = get_file(redirect); - spin_unlock(&redirect_lock); - - if (p) { - ssize_t res; - res = vfs_write(p, buf, count, &p->f_pos); - fput(p); - return res; - } - return tty_write(file, buf, count, ppos); -} - -void tty_buffer_flush_work(struct tty_port *port) -{ - flush_work(&port->buf.work); -} - -static inline size_t read_cnt(struct neuronspi_tty_data *ldata) -{ - return ldata->read_head - ldata->read_tail; -} - -static inline unsigned char read_buf(struct neuronspi_tty_data *ldata, size_t i) -{ - return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; -} - -static inline unsigned char *read_buf_addr(struct neuronspi_tty_data *ldata, size_t i) -{ - return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; -} - -static inline unsigned char echo_buf(struct neuronspi_tty_data *ldata, size_t i) -{ - return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; -} - -static inline unsigned char *echo_buf_addr(struct neuronspi_tty_data *ldata, size_t i) -{ - return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; -} - -static int tty_copy_to_user(struct tty_struct *tty, void __user *to, - size_t tail, size_t n) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - size_t size = N_TTY_BUF_SIZE - tail; - const void *from = read_buf_addr(ldata, tail); - int uncopied; - - if (n > size) { - tty_audit_add_data(tty, from, size); - uncopied = copy_to_user(to, from, size); - if (uncopied) - return uncopied; - to += size; - n -= size; - from = ldata->read_buf; - } - - tty_audit_add_data(tty, from, n); - return copy_to_user(to, from, n); -} - -/** - * n_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 - * n_tty_read()/consumer path: - * holds non-exclusive termios_rwsem - */ - -static void neuronspi_tty_kick_worker(struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - /* 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); - } -} - -static ssize_t chars_in_buffer(struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - ssize_t n = 0; - - if (!ldata->icanon) - n = ldata->commit_head - ldata->read_tail; - else - n = ldata->canon_head - ldata->read_tail; - return n; -} - -/** - * n_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 neuronspi_tty_write_wakeup(struct tty_struct *tty) -{ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - kill_fasync(&tty->fasync, SIGIO, POLL_OUT); -} - -static void neuronspi_tty_check_throttle(struct tty_struct *tty) -{ - struct neuronspi_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! - */ - if (ldata->icanon && ldata->canon_head == ldata->read_tail) - return; - - 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 = tty_throttle_safe(tty); - if (!throttled) - break; - } - __tty_set_flow_change(tty, 0); -} - -static void neuronspi_tty_check_unthrottle(struct tty_struct *tty) -{ - if (tty->driver->type == TTY_DRIVER_TYPE_PTY) { - if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) - return; - neuronspi_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; - neuronspi_tty_kick_worker(tty); - unthrottled = tty_unthrottle_safe(tty); - if (!unthrottled) - break; - } - __tty_set_flow_change(tty, 0); -} - -/** - * put_tty_queue - add character to tty - * @c: character - * @ldata: n_tty data - * - * Add a character to the tty read_buf queue. - * - * n_tty_receive_buf()/producer path: - * caller holds non-exclusive termios_rwsem - */ - -static inline void put_tty_queue(unsigned char c, struct neuronspi_tty_data *ldata) -{ - *read_buf_addr(ldata, ldata->read_head) = c; - ldata->read_head++; -} - -/** - * reset_buffer_flags - reset buffer state - * @tty: terminal to reset - * - * Reset the read buffer counters and clear the flags. - * Called from n_tty_open() and n_tty_flush_buffer(). - * - * Locking: caller holds exclusive termios_rwsem - * (or locking is not required) - */ - -static void reset_buffer_flags(struct neuronspi_tty_data *ldata) -{ - ldata->read_head = ldata->canon_head = ldata->read_tail = 0; - ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0; - ldata->commit_head = 0; - ldata->echo_mark = 0; - ldata->line_start = 0; - - ldata->erasing = 0; - bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); - ldata->push = 0; -} - -static void neuronspi_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); - } -} - -/** - * n_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 neuronspi_tty_flush_buffer(struct tty_struct *tty) -{ - printk(KERN_INFO "TTYFlushBuffer Called"); - down_write(&tty->termios_rwsem); - reset_buffer_flags(tty->disc_data); - neuronspi_tty_kick_worker(tty); - - if (tty->link) - neuronspi_tty_packet_mode_flush(tty); - up_write(&tty->termios_rwsem); -} - -/** - * is_utf8_continuation - utf8 multibyte check - * @c: byte to check - * - * Returns true if the utf8 character 'c' is a multibyte continuation - * character. We use this to correctly compute the on screen size - * of the character when printing - */ - -static inline int is_utf8_continuation(unsigned char c) -{ - return (c & 0xc0) == 0x80; -} - -/** - * is_continuation - multibyte check - * @c: byte to check - * - * Returns true if the utf8 character 'c' is a multibyte continuation - * character and the terminal is in unicode mode. - */ - -static inline int is_continuation(unsigned char c, struct tty_struct *tty) -{ - return I_IUTF8(tty) && is_utf8_continuation(c); -} - -/** - * do_output_char - output one character - * @c: character (or partial unicode symbol) - * @tty: terminal device - * @space: space available in tty driver write buffer - * - * This is a helper function that handles one output character - * (including special characters like TAB, CR, LF, etc.), - * doing OPOST processing and putting the results in the - * tty driver's write buffer. - * - * Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY - * and NLDLY. They simply aren't relevant in the world today. - * If you ever need them, add them here. - * - * Returns the number of bytes of buffer space used or -1 if - * no space left. - * - * Locking: should be called under the output_lock to protect - * the column state and space left in the buffer - */ - -static int do_output_char(unsigned char c, struct tty_struct *tty, int space) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - int spaces; - - if (!space) - return -1; - - switch (c) { - case '\n': - if (O_ONLRET(tty)) - ldata->column = 0; - if (O_ONLCR(tty)) { - if (space < 2) - return -1; - ldata->canon_column = ldata->column = 0; - tty->ops->write(tty, "\r\n", 2); - return 2; - } - ldata->canon_column = ldata->column; - break; - case '\r': - if (O_ONOCR(tty) && ldata->column == 0) - return 0; - if (O_OCRNL(tty)) { - c = '\n'; - if (O_ONLRET(tty)) - ldata->canon_column = ldata->column = 0; - break; - } - ldata->canon_column = ldata->column = 0; - break; - case '\t': - spaces = 8 - (ldata->column & 7); - if (O_TABDLY(tty) == XTABS) { - if (space < spaces) - return -1; - ldata->column += spaces; - tty->ops->write(tty, " ", spaces); - return spaces; - } - ldata->column += spaces; - break; - case '\b': - if (ldata->column > 0) - ldata->column--; - break; - default: - if (!iscntrl(c)) { - if (O_OLCUC(tty)) - c = toupper(c); - if (!is_continuation(c, tty)) - ldata->column++; - } - break; - } - - tty_put_char(tty, c); - return 1; -} - -/** - * process_output - output post processor - * @c: character (or partial unicode symbol) - * @tty: terminal device - * - * Output one character with OPOST processing. - * Returns -1 when the output device is full and the character - * must be retried. - * - * Locking: output_lock to protect column state and space left - * (also, this is called from n_tty_write under the - * tty layer write lock) - */ - -static int process_output(unsigned char c, struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - int space, retval; - - mutex_lock(&ldata->output_lock); - - space = tty_write_room(tty); - retval = do_output_char(c, tty, space); - - mutex_unlock(&ldata->output_lock); - if (retval < 0) - return -1; - else - return 0; -} - -/** - * process_output_block - block post processor - * @tty: terminal device - * @buf: character buffer - * @nr: number of bytes to output - * - * Output a block of characters with OPOST processing. - * Returns the number of characters output. - * - * This path is used to speed up block console writes, among other - * things when processing blocks of output data. It handles only - * the simple cases normally found and helps to generate blocks of - * symbols for the console driver and thus improve performance. - * - * Locking: output_lock to protect column state and space left - * (also, this is called from n_tty_write under the - * tty layer write lock) - */ - -static ssize_t process_output_block(struct tty_struct *tty, - const unsigned char *buf, unsigned int nr) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - int space; - int i; - const unsigned char *cp; - - mutex_lock(&ldata->output_lock); - - space = tty_write_room(tty); - if (!space) { - mutex_unlock(&ldata->output_lock); - return 0; - } - if (nr > space) - nr = space; - - for (i = 0, cp = buf; i < nr; i++, cp++) { - unsigned char c = *cp; - - switch (c) { - case '\n': - if (O_ONLRET(tty)) - ldata->column = 0; - if (O_ONLCR(tty)) - goto break_out; - ldata->canon_column = ldata->column; - break; - case '\r': - if (O_ONOCR(tty) && ldata->column == 0) - goto break_out; - if (O_OCRNL(tty)) - goto break_out; - ldata->canon_column = ldata->column = 0; - break; - case '\t': - goto break_out; - case '\b': - if (ldata->column > 0) - ldata->column--; - break; - default: - if (!iscntrl(c)) { - if (O_OLCUC(tty)) - goto break_out; - if (!is_continuation(c, tty)) - ldata->column++; - } - break; - } - } -break_out: - i = tty->ops->write(tty, buf, i); - - mutex_unlock(&ldata->output_lock); - return i; -} - -/** - * process_echoes - write pending echo characters - * @tty: terminal device - * - * Write previously buffered echo (and other ldisc-generated) - * characters to the tty. - * - * Characters generated by the ldisc (including echoes) need to - * be buffered because the driver's write buffer can fill during - * heavy program output. Echoing straight to the driver will - * often fail under these conditions, causing lost characters and - * resulting mismatches of ldisc state information. - * - * Since the ldisc state must represent the characters actually sent - * to the driver at the time of the write, operations like certain - * changes in column state are also saved in the buffer and executed - * here. - * - * A circular fifo buffer is used so that the most recent characters - * are prioritized. Also, when control characters are echoed with a - * prefixed "^", the pair is treated atomically and thus not separated. - * - * Locking: callers must hold output_lock - */ - -static size_t __process_echoes(struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - int space, old_space; - size_t tail; - unsigned char c; - - old_space = space = tty_write_room(tty); - - tail = ldata->echo_tail; - while (ldata->echo_commit != tail) { - c = echo_buf(ldata, tail); - if (c == ECHO_OP_START) { - unsigned char op; - int no_space_left = 0; - - /* - * If the buffer byte is the start of a multi-byte - * operation, get the next byte, which is either the - * op code or a control character value. - */ - op = echo_buf(ldata, tail + 1); - - switch (op) { - unsigned int num_chars, num_bs; - case ECHO_OP_ERASE_TAB: - num_chars = echo_buf(ldata, tail + 2); - /* - * Determine how many columns to go back - * in order to erase the tab. - * This depends on the number of columns - * used by other characters within the tab - * area. If this (modulo 8) count is from - * the start of input rather than from a - * previous tab, we offset by canon column. - * Otherwise, tab spacing is normal. - */ - if (!(num_chars & 0x80)) - num_chars += ldata->canon_column; - num_bs = 8 - (num_chars & 7); - - if (num_bs > space) { - no_space_left = 1; - break; - } - space -= num_bs; - while (num_bs--) { - tty_put_char(tty, '\b'); - if (ldata->column > 0) - ldata->column--; - } - tail += 3; - break; - - case ECHO_OP_SET_CANON_COL: - ldata->canon_column = ldata->column; - tail += 2; - break; - - case ECHO_OP_MOVE_BACK_COL: - if (ldata->column > 0) - ldata->column--; - tail += 2; - break; - - case ECHO_OP_START: - /* This is an escaped echo op start code */ - if (!space) { - no_space_left = 1; - break; - } - tty_put_char(tty, ECHO_OP_START); - ldata->column++; - space--; - tail += 2; - break; - - default: - /* - * If the op is not a special byte code, - * it is a ctrl char tagged to be echoed - * as "^X" (where X is the letter - * representing the control char). - * Note that we must ensure there is - * enough space for the whole ctrl pair. - * - */ - if (space < 2) { - no_space_left = 1; - break; - } - tty_put_char(tty, '^'); - tty_put_char(tty, op ^ 0100); - ldata->column += 2; - space -= 2; - tail += 2; - } - - if (no_space_left) - break; - } else { - if (O_OPOST(tty)) { - int retval = do_output_char(c, tty, space); - if (retval < 0) - break; - space -= retval; - } else { - if (!space) - break; - tty_put_char(tty, c); - space -= 1; - } - tail += 1; - } - } - - /* If the echo buffer is nearly full (so that the possibility exists - * of echo overrun before the next commit), then discard enough - * data at the tail to prevent a subsequent overrun */ - while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { - if (echo_buf(ldata, tail) == ECHO_OP_START) { - if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB) - tail += 3; - else - tail += 2; - } else - tail++; - } - - ldata->echo_tail = tail; - return old_space - space; -} - -static void commit_echoes(struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - size_t nr, old, echoed; - size_t head; - - head = ldata->echo_head; - ldata->echo_mark = head; - old = ldata->echo_commit - ldata->echo_tail; - - /* Process committed echoes if the accumulated # of bytes - * is over the threshold (and try again each time another - * block is accumulated) */ - nr = head - ldata->echo_tail; - if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK)) - return; - - mutex_lock(&ldata->output_lock); - ldata->echo_commit = head; - echoed = __process_echoes(tty); - mutex_unlock(&ldata->output_lock); - - if (echoed && tty->ops->flush_chars) - tty->ops->flush_chars(tty); -} - -static void process_echoes(struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - size_t echoed; - - if (ldata->echo_mark == ldata->echo_tail) - return; - - mutex_lock(&ldata->output_lock); - ldata->echo_commit = ldata->echo_mark; - echoed = __process_echoes(tty); - mutex_unlock(&ldata->output_lock); - - if (echoed && tty->ops->flush_chars) - tty->ops->flush_chars(tty); -} - -/* NB: echo_mark and echo_head should be equivalent here */ -static void flush_echoes(struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - if ((!L_ECHO(tty) && !L_ECHONL(tty)) || - ldata->echo_commit == ldata->echo_head) - return; - - mutex_lock(&ldata->output_lock); - ldata->echo_commit = ldata->echo_head; - __process_echoes(tty); - mutex_unlock(&ldata->output_lock); -} - -/** - * add_echo_byte - add a byte to the echo buffer - * @c: unicode byte to echo - * @ldata: n_tty data - * - * Add a character or operation byte to the echo buffer. - */ - -static inline void add_echo_byte(unsigned char c, struct neuronspi_tty_data *ldata) -{ - *echo_buf_addr(ldata, ldata->echo_head++) = c; -} - -/** - * echo_move_back_col - add operation to move back a column - * @ldata: n_tty data - * - * Add an operation to the echo buffer to move back one column. - */ - -static void echo_move_back_col(struct neuronspi_tty_data *ldata) -{ - add_echo_byte(ECHO_OP_START, ldata); - add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata); -} - -/** - * echo_set_canon_col - add operation to set the canon column - * @ldata: n_tty data - * - * Add an operation to the echo buffer to set the canon column - * to the current column. - */ - -static void echo_set_canon_col(struct neuronspi_tty_data *ldata) -{ - add_echo_byte(ECHO_OP_START, ldata); - add_echo_byte(ECHO_OP_SET_CANON_COL, ldata); -} - -/** - * echo_erase_tab - add operation to erase a tab - * @num_chars: number of character columns already used - * @after_tab: true if num_chars starts after a previous tab - * @ldata: n_tty data - * - * Add an operation to the echo buffer to erase a tab. - * - * Called by the eraser function, which knows how many character - * columns have been used since either a previous tab or the start - * of input. This information will be used later, along with - * canon column (if applicable), to go back the correct number - * of columns. - */ - -static void echo_erase_tab(unsigned int num_chars, int after_tab, - struct neuronspi_tty_data *ldata) -{ - add_echo_byte(ECHO_OP_START, ldata); - add_echo_byte(ECHO_OP_ERASE_TAB, ldata); - - /* We only need to know this modulo 8 (tab spacing) */ - num_chars &= 7; - - /* Set the high bit as a flag if num_chars is after a previous tab */ - if (after_tab) - num_chars |= 0x80; - - add_echo_byte(num_chars, ldata); -} - -/** - * echo_char_raw - echo a character raw - * @c: unicode byte to echo - * @tty: terminal device - * - * Echo user input back onto the screen. This must be called only when - * L_ECHO(tty) is true. Called from the driver receive_buf path. - * - * This variant does not treat control characters specially. - */ - -static void echo_char_raw(unsigned char c, struct neuronspi_tty_data *ldata) -{ - if (c == ECHO_OP_START) { - add_echo_byte(ECHO_OP_START, ldata); - add_echo_byte(ECHO_OP_START, ldata); - } else { - add_echo_byte(c, ldata); - } -} - -/** - * echo_char - echo a character - * @c: unicode byte to echo - * @tty: terminal device - * - * Echo user input back onto the screen. This must be called only when - * L_ECHO(tty) is true. Called from the driver receive_buf path. - * - * This variant tags control characters to be echoed as "^X" - * (where X is the letter representing the control char). - */ - -static void echo_char(unsigned char c, struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - if (c == ECHO_OP_START) { - add_echo_byte(ECHO_OP_START, ldata); - add_echo_byte(ECHO_OP_START, ldata); - } else { - if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') - add_echo_byte(ECHO_OP_START, ldata); - add_echo_byte(c, ldata); - } -} - -/** - * finish_erasing - complete erase - * @ldata: n_tty data - */ - -static inline void finish_erasing(struct neuronspi_tty_data *ldata) -{ - if (ldata->erasing) { - echo_char_raw('/', ldata); - ldata->erasing = 0; - } -} - -/** - * eraser - handle erase function - * @c: character input - * @tty: terminal device - * - * Perform erase and necessary output when an erase character is - * present in the stream from the driver layer. Handles the complexities - * of UTF-8 multibyte symbols. - * - * n_tty_receive_buf()/producer path: - * caller holds non-exclusive termios_rwsem - */ - -static void eraser(unsigned char c, struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - enum { ERASE, WERASE, KILL } kill_type; - size_t head; - size_t cnt; - int seen_alnums; - - if (ldata->read_head == ldata->canon_head) { - /* process_output('\a', tty); */ /* what do you think? */ - return; - } - if (c == ERASE_CHAR(tty)) - kill_type = ERASE; - else if (c == WERASE_CHAR(tty)) - kill_type = WERASE; - else { - if (!L_ECHO(tty)) { - ldata->read_head = ldata->canon_head; - return; - } - if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { - ldata->read_head = ldata->canon_head; - finish_erasing(ldata); - echo_char(KILL_CHAR(tty), tty); - /* Add a newline if ECHOK is on and ECHOKE is off. */ - if (L_ECHOK(tty)) - echo_char_raw('\n', ldata); - return; - } - kill_type = KILL; - } - - seen_alnums = 0; - while (ldata->read_head != ldata->canon_head) { - head = ldata->read_head; - - /* erase a single possibly multibyte character */ - do { - head--; - c = read_buf(ldata, head); - } while (is_continuation(c, tty) && head != ldata->canon_head); - - /* do not partially erase */ - if (is_continuation(c, tty)) - break; - - if (kill_type == WERASE) { - /* Equivalent to BSD's ALTWERASE. */ - if (isalnum(c) || c == '_') - seen_alnums++; - else if (seen_alnums) - break; - } - cnt = ldata->read_head - head; - ldata->read_head = head; - if (L_ECHO(tty)) { - if (L_ECHOPRT(tty)) { - if (!ldata->erasing) { - echo_char_raw('\\', ldata); - ldata->erasing = 1; - } - /* if cnt > 1, output a multi-byte character */ - echo_char(c, tty); - while (--cnt > 0) { - head++; - echo_char_raw(read_buf(ldata, head), ldata); - echo_move_back_col(ldata); - } - } else if (kill_type == ERASE && !L_ECHOE(tty)) { - echo_char(ERASE_CHAR(tty), tty); - } else if (c == '\t') { - unsigned int num_chars = 0; - int after_tab = 0; - size_t tail = ldata->read_head; - - /* - * Count the columns used for characters - * since the start of input or after a - * previous tab. - * This info is used to go back the correct - * number of columns. - */ - while (tail != ldata->canon_head) { - tail--; - c = read_buf(ldata, tail); - if (c == '\t') { - after_tab = 1; - break; - } else if (iscntrl(c)) { - if (L_ECHOCTL(tty)) - num_chars += 2; - } else if (!is_continuation(c, tty)) { - num_chars++; - } - } - echo_erase_tab(num_chars, after_tab, ldata); - } else { - if (iscntrl(c) && L_ECHOCTL(tty)) { - echo_char_raw('\b', ldata); - echo_char_raw(' ', ldata); - echo_char_raw('\b', ldata); - } - if (!iscntrl(c) || L_ECHOCTL(tty)) { - echo_char_raw('\b', ldata); - echo_char_raw(' ', ldata); - echo_char_raw('\b', ldata); - } - } - } - if (kill_type == ERASE) - break; - } - if (ldata->read_head == ldata->canon_head && L_ECHO(tty)) - finish_erasing(ldata); -} - -/** - * isig - handle the ISIG optio - * @sig: signal - * @tty: terminal - * - * Called when a signal is being sent due to terminal input. - * Called from the driver receive_buf path so serialized. - * - * Performs input and output flush if !NOFLSH. In this context, the echo - * buffer is 'output'. The signal is processed first to alert any current - * readers or writers to discontinue and exit their i/o loops. - * - * Locking: ctrl_lock - */ - -static void __isig(int sig, struct tty_struct *tty) -{ - struct pid *tty_pgrp = tty_get_pgrp(tty); - if (tty_pgrp) { - kill_pgrp(tty_pgrp, sig, 1); - put_pid(tty_pgrp); - } -} - -static void isig(int sig, struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - if (L_NOFLSH(tty)) { - /* signal only */ - __isig(sig, tty); - - } else { /* signal and flush */ - up_read(&tty->termios_rwsem); - down_write(&tty->termios_rwsem); - - __isig(sig, tty); - - /* clear echo buffer */ - mutex_lock(&ldata->output_lock); - ldata->echo_head = ldata->echo_tail = 0; - ldata->echo_mark = ldata->echo_commit = 0; - mutex_unlock(&ldata->output_lock); - - /* clear output buffer */ - tty_driver_flush_buffer(tty); - - /* clear input buffer */ - reset_buffer_flags(tty->disc_data); - - /* notify pty master of flush */ - if (tty->link) - neuronspi_tty_packet_mode_flush(tty); - - up_write(&tty->termios_rwsem); - down_read(&tty->termios_rwsem); - } -} - -/** - * n_tty_receive_break - handle break - * @tty: terminal - * - * An RS232 break event has been hit in the incoming bitstream. This - * can cause a variety of events depending upon the termios settings. - * - * n_tty_receive_buf()/producer path: - * caller holds non-exclusive termios_rwsem - * - * Note: may get exclusive termios_rwsem if flushing input buffer - */ - -static void neuronspi_tty_receive_break(struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - if (I_IGNBRK(tty)) - return; - if (I_BRKINT(tty)) { - isig(SIGINT, tty); - return; - } - if (I_PARMRK(tty)) { - put_tty_queue('\377', ldata); - put_tty_queue('\0', ldata); - } - put_tty_queue('\0', ldata); -} - -/** - * n_tty_receive_overrun - handle overrun reporting - * @tty: terminal - * - * Data arrived faster than we could process it. While the tty - * driver has flagged this the bits that were missed are gone - * forever. - * - * Called from the receive_buf path so single threaded. Does not - * need locking as num_overrun and overrun_time are function - * private. - */ - -static void neuronspi_tty_receive_overrun(struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - ldata->num_overrun++; - if (time_after(jiffies, ldata->overrun_time + HZ) || - time_after(ldata->overrun_time, jiffies)) { - tty_warn(tty, "%d input overrun(s)\n", ldata->num_overrun); - ldata->overrun_time = jiffies; - ldata->num_overrun = 0; - } -} - -/** - * n_tty_receive_parity_error - error notifier - * @tty: terminal device - * @c: character - * - * Process a parity error and queue the right data to indicate - * the error case if necessary. - * - * n_tty_receive_buf()/producer path: - * caller holds non-exclusive termios_rwsem - */ -static void neuronspi_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - if (I_INPCK(tty)) { - if (I_IGNPAR(tty)) - return; - if (I_PARMRK(tty)) { - put_tty_queue('\377', ldata); - put_tty_queue('\0', ldata); - put_tty_queue(c, ldata); - } else - put_tty_queue('\0', ldata); - } else - put_tty_queue(c, ldata); -} - -static void -neuronspi_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c) -{ - isig(signal, tty); - if (I_IXON(tty)) - start_tty(tty); - if (L_ECHO(tty)) { - echo_char(c, tty); - commit_echoes(tty); - } else - process_echoes(tty); - return; -} - -/** - * n_tty_receive_char - perform processing - * @tty: terminal device - * @c: character - * - * Process an individual character of input received from the driver. - * This is serialized with respect to itself by the rules for the - * driver above. - * - * n_tty_receive_buf()/producer path: - * caller holds non-exclusive termios_rwsem - * publishes canon_head if canonical mode is active - * - * Returns 1 if LNEXT was received, else returns 0 - */ - -static int -neuronspi_tty_receive_char_special(struct tty_struct *tty, unsigned char c) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - if (I_IXON(tty)) { - if (c == START_CHAR(tty)) { - start_tty(tty); - process_echoes(tty); - return 0; - } - if (c == STOP_CHAR(tty)) { - stop_tty(tty); - return 0; - } - } - - if (L_ISIG(tty)) { - if (c == INTR_CHAR(tty)) { - neuronspi_tty_receive_signal_char(tty, SIGINT, c); - return 0; - } else if (c == QUIT_CHAR(tty)) { - neuronspi_tty_receive_signal_char(tty, SIGQUIT, c); - return 0; - } else if (c == SUSP_CHAR(tty)) { - neuronspi_tty_receive_signal_char(tty, SIGTSTP, c); - return 0; - } - } - - if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { - start_tty(tty); - process_echoes(tty); - } - - if (c == '\r') { - if (I_IGNCR(tty)) - return 0; - if (I_ICRNL(tty)) - c = '\n'; - } else if (c == '\n' && I_INLCR(tty)) - c = '\r'; - - if (ldata->icanon) { - if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || - (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { - eraser(c, tty); - commit_echoes(tty); - return 0; - } - if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { - ldata->lnext = 1; - if (L_ECHO(tty)) { - finish_erasing(ldata); - if (L_ECHOCTL(tty)) { - echo_char_raw('^', ldata); - echo_char_raw('\b', ldata); - commit_echoes(tty); - } - } - return 1; - } - if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { - size_t tail = ldata->canon_head; - - finish_erasing(ldata); - echo_char(c, tty); - echo_char_raw('\n', ldata); - while (tail != ldata->read_head) { - echo_char(read_buf(ldata, tail), tty); - tail++; - } - commit_echoes(tty); - return 0; - } - if (c == '\n') { - if (L_ECHO(tty) || L_ECHONL(tty)) { - echo_char_raw('\n', ldata); - commit_echoes(tty); - } - goto handle_newline; - } - if (c == EOF_CHAR(tty)) { - c = __DISABLED_CHAR; - goto handle_newline; - } - if ((c == EOL_CHAR(tty)) || - (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { - /* - * XXX are EOL_CHAR and EOL2_CHAR echoed?!? - */ - if (L_ECHO(tty)) { - /* Record the column of first canon char. */ - if (ldata->canon_head == ldata->read_head) - echo_set_canon_col(ldata); - echo_char(c, tty); - commit_echoes(tty); - } - /* - * XXX does PARMRK doubling happen for - * EOL_CHAR and EOL2_CHAR? - */ - if (c == (unsigned char) '\377' && I_PARMRK(tty)) - put_tty_queue(c, ldata); - -handle_newline: - set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags); - put_tty_queue(c, ldata); - smp_store_release(&ldata->canon_head, ldata->read_head); - kill_fasync(&tty->fasync, SIGIO, POLL_IN); - wake_up_interruptible_poll(&tty->read_wait, POLLIN); - return 0; - } - } - - if (L_ECHO(tty)) { - finish_erasing(ldata); - if (c == '\n') - echo_char_raw('\n', ldata); - else { - /* Record the column of first canon char. */ - if (ldata->canon_head == ldata->read_head) - echo_set_canon_col(ldata); - echo_char(c, tty); - } - commit_echoes(tty); - } - - /* PARMRK doubling check */ - if (c == (unsigned char) '\377' && I_PARMRK(tty)) - put_tty_queue(c, ldata); - - put_tty_queue(c, ldata); - return 0; -} - -static inline void -neuronspi_tty_receive_char_inline(struct tty_struct *tty, unsigned char c) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { - start_tty(tty); - process_echoes(tty); - } - if (L_ECHO(tty)) { - finish_erasing(ldata); - /* Record the column of first canon char. */ - if (ldata->canon_head == ldata->read_head) - echo_set_canon_col(ldata); - echo_char(c, tty); - commit_echoes(tty); - } - /* PARMRK doubling check */ - if (c == (unsigned char) '\377' && I_PARMRK(tty)) - put_tty_queue(c, ldata); - put_tty_queue(c, ldata); -} - -static void neuronspi_tty_receive_char(struct tty_struct *tty, unsigned char c) -{ - neuronspi_tty_receive_char_inline(tty, c); -} - -static inline void -neuronspi_tty_receive_char_fast(struct tty_struct *tty, unsigned char c) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { - start_tty(tty); - process_echoes(tty); - } - if (L_ECHO(tty)) { - finish_erasing(ldata); - /* Record the column of first canon char. */ - if (ldata->canon_head == ldata->read_head) - echo_set_canon_col(ldata); - echo_char(c, tty); - commit_echoes(tty); - } - put_tty_queue(c, ldata); -} - -static void neuronspi_tty_receive_char_closing(struct tty_struct *tty, unsigned char c) -{ - if (I_ISTRIP(tty)) - c &= 0x7f; - if (I_IUCLC(tty) && L_IEXTEN(tty)) - c = tolower(c); - - if (I_IXON(tty)) { - if (c == STOP_CHAR(tty)) - stop_tty(tty); - else if (c == START_CHAR(tty) || - (tty->stopped && !tty->flow_stopped && I_IXANY(tty) && - c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && - c != SUSP_CHAR(tty))) { - start_tty(tty); - process_echoes(tty); - } - } -} - -static void -neuronspi_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag) -{ - switch (flag) { - case TTY_BREAK: - neuronspi_tty_receive_break(tty); - break; - case TTY_PARITY: - case TTY_FRAME: - neuronspi_tty_receive_parity_error(tty, c); - break; - case TTY_OVERRUN: - neuronspi_tty_receive_overrun(tty); - break; - default: - tty_err(tty, "unknown flag %d\n", flag); - break; - } -} - -static void -neuronspi_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - ldata->lnext = 0; - if (likely(flag == TTY_NORMAL)) { - if (I_ISTRIP(tty)) - c &= 0x7f; - if (I_IUCLC(tty) && L_IEXTEN(tty)) - c = tolower(c); - neuronspi_tty_receive_char(tty, c); - } else - neuronspi_tty_receive_char_flagged(tty, c, flag); -} - -static void -neuronspi_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - struct neuronspi_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; -} - -static void neuronspi_tty_receive_buf_raw(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - char flag = TTY_NORMAL; - - while (count--) { - if (fp) - flag = *fp++; - if (likely(flag == TTY_NORMAL)) - put_tty_queue(*cp++, ldata); - else - neuronspi_tty_receive_char_flagged(tty, *cp++, flag); - } -} - -static void neuronspi_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - char flag = TTY_NORMAL; - - while (count--) { - if (fp) - flag = *fp++; - if (likely(flag == TTY_NORMAL)) - neuronspi_tty_receive_char_closing(tty, *cp++); - } -} - -static void neuronspi_tty_receive_buf_standard(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - char flag = TTY_NORMAL; - - while (count--) { - if (fp) - flag = *fp++; - if (likely(flag == TTY_NORMAL)) { - unsigned char c = *cp++; - - if (I_ISTRIP(tty)) - c &= 0x7f; - if (I_IUCLC(tty) && L_IEXTEN(tty)) - c = tolower(c); - if (L_EXTPROC(tty)) { - put_tty_queue(c, ldata); - continue; - } - if (!test_bit(c, ldata->char_map)) - neuronspi_tty_receive_char_inline(tty, c); - else if (neuronspi_tty_receive_char_special(tty, c) && count) { - if (fp) - flag = *fp++; - neuronspi_tty_receive_char_lnext(tty, *cp++, flag); - count--; - } - } else - neuronspi_tty_receive_char_flagged(tty, *cp++, flag); - } -} - -static void neuronspi_tty_receive_buf_fast(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - char flag = TTY_NORMAL; - - while (count--) { - if (fp) - flag = *fp++; - if (likely(flag == TTY_NORMAL)) { - unsigned char c = *cp++; - - if (!test_bit(c, ldata->char_map)) - neuronspi_tty_receive_char_fast(tty, c); - else if (neuronspi_tty_receive_char_special(tty, c) && count) { - if (fp) - flag = *fp++; - neuronspi_tty_receive_char_lnext(tty, *cp++, flag); - count--; - } - } else - neuronspi_tty_receive_char_flagged(tty, *cp++, flag); - } -} - -static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty)); - - if (ldata->real_raw) - neuronspi_tty_receive_buf_real_raw(tty, cp, fp, count); - else if (ldata->raw || (L_EXTPROC(tty) && !preops)) - neuronspi_tty_receive_buf_raw(tty, cp, fp, count); - else if (tty->closing && !L_EXTPROC(tty)) - neuronspi_tty_receive_buf_closing(tty, cp, fp, count); - else { - if (ldata->lnext) { - char flag = TTY_NORMAL; - - if (fp) - flag = *fp++; - neuronspi_tty_receive_char_lnext(tty, *cp++, flag); - count--; - } - - if (!preops && !I_PARMRK(tty)) - neuronspi_tty_receive_buf_fast(tty, cp, fp, count); - else - neuronspi_tty_receive_buf_standard(tty, cp, fp, count); - - flush_echoes(tty); - if (tty->ops->flush_chars) - tty->ops->flush_chars(tty); - } - - if (ldata->icanon && !L_EXTPROC(tty)) - return; - - /* 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); - } -} - - -static int neuronspi_tty_open(struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata; - - /* Currently a malloc failure here can panic */ - ldata = vmalloc(sizeof(*ldata)); - if (!ldata) - goto err; - - ldata->overrun_time = jiffies; - mutex_init(&ldata->atomic_read_lock); - mutex_init(&ldata->output_lock); - - tty->disc_data = ldata; - reset_buffer_flags(tty->disc_data); - ldata->column = 0; - ldata->canon_column = 0; - ldata->num_overrun = 0; - ldata->no_room = 0; - ldata->lnext = 0; - tty->closing = 0; - /* indicate buffer work may resume */ - clear_bit(TTY_LDISC_HALTED, &tty->flags); - //n_tty_set_termios(tty, NULL); - tty_unthrottle(tty); - - return 0; -err: - return -ENOMEM; -} - -static void neuronspi_tty_close(struct tty_struct *tty) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - //if (tty->link) - //n_tty_packet_mode_flush(tty); - - vfree(ldata); - tty->disc_data = NULL; -} - -static inline int input_available_p(struct tty_struct *tty, int poll) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - int amt = poll && !TIME_CHAR(tty) && MIN_CHAR(tty) ? MIN_CHAR(tty) : 1; - - if (ldata->icanon && !L_EXTPROC(tty)) - return ldata->canon_head != ldata->read_tail; - else - 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 n_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 - * - * n_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 neuronspi_tty_data *ldata = tty->disc_data; - int retval; - size_t n; - bool is_eof; - 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; - is_eof = n == 1 && *from == EOF_CHAR(tty); - printk(KERN_INFO "NEURONSPI: ReadBufChar %x", *from); - tty_audit_add_data(tty, from, n); - smp_store_release(&ldata->read_tail, ldata->read_tail + n); - /* Turn single EOF into zero-length read */ - if (L_EXTPROC(tty) && ldata->icanon && is_eof && - (head == ldata->read_tail)) - n = 0; - *b += n; - *nr -= n; - } - return retval; -} - -/** - * canon_copy_from_read_buf - copy read data in canonical mode - * @tty: terminal device - * @b: user data - * @nr: size of data - * - * Helper function for n_tty_read. It is only called when ICANON is on; - * it copies one line of input up to and including the line-delimiting - * character into the user-space buffer. - * - * NB: When termios is changed from non-canonical to canonical mode and - * the read buffer contains data, n_tty_set_termios() simulates an EOF - * push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer. - * This causes data already processed as input to be immediately available - * as input although a newline has not been received. - * - * Called under the atomic_read_lock mutex - * - * n_tty_read()/consumer path: - * caller holds non-exclusive termios_rwsem - * read_tail published - */ - -static int canon_copy_from_read_buf(struct tty_struct *tty, - unsigned char __user **b, - size_t *nr) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - size_t n, size, more, c; - size_t eol; - size_t tail; - int ret, found = 0; - - /* N.B. avoid overrun if nr == 0 */ - if (!*nr) - return 0; - - n = min(*nr + 1, smp_load_acquire(&ldata->canon_head) - ldata->read_tail); - - tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); - size = min_t(size_t, tail + n, N_TTY_BUF_SIZE); - - n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n", - __func__, *nr, tail, n, size); - - eol = find_next_bit(ldata->read_flags, size, tail); - more = n - (size - tail); - if (eol == N_TTY_BUF_SIZE && more) { - /* scan wrapped without finding set bit */ - eol = find_next_bit(ldata->read_flags, more, 0); - found = eol != more; - } else - found = eol != size; - - n = eol - tail; - if (n > N_TTY_BUF_SIZE) - n += N_TTY_BUF_SIZE; - c = n + found; - - if (!found || read_buf(ldata, eol) != __DISABLED_CHAR) { - c = min(*nr, c); - n = c; - } - - n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n", - __func__, eol, found, n, c, tail, more); - - ret = tty_copy_to_user(tty, *b, tail, n); - if (ret) - return -EFAULT; - *b += n; - *nr -= n; - - if (found) - clear_bit(eol, ldata->read_flags); - smp_store_release(&ldata->read_tail, ldata->read_tail + c); - - if (found) { - if (!ldata->push) - ldata->line_start = ldata->read_tail; - else - ldata->push = 0; - tty_audit_push(); - } - return 0; -} - -extern ssize_t redirected_tty_write(struct file *, const char __user *, - size_t, loff_t *); - -/** - * job_control - check job control - * @tty: tty - * @file: file handle - * - * Perform job control management checks on this file/tty descriptor - * and if appropriate send any needed signals and return a negative - * error code if action should be taken. - * - * Locking: redirected write test is safe - * current->signal->tty check is safe - * ctrl_lock to safely reference tty->pgrp - */ - -static int job_control(struct tty_struct *tty, struct file *file) -{ - /* Job control check -- must be done at start and after - every sleep (POSIX.1 7.1.1.4). */ - /* NOTE: not yet done after every sleep pending a thorough - check of the logic of this change. -- jlc */ - /* don't stop on /dev/console */ - if (file->f_op->write == redirected_tty_write) - return 0; - - return __tty_check_change(tty, SIGTTIN); -} - -int neuronspi_tty_ioctl(struct tty_struct *tty_s, struct file *tty_f, unsigned int cmd, unsigned long arg) -{ - int err = 0; - printk(KERN_INFO "NEURONSPI: TTY IOCTL %d %ld", cmd, arg); - switch (cmd) { - case 0x5480: { - printk(KERN_INFO "NEURONSPI: m_Params.FrameSpaceChars (0x5480) %d\n", (u32)arg); - err = (s32)arg; - break; - } - case 0x5481: { - printk(KERN_INFO "NEURONSPI: m_Params.FrameSpaceTimeInUs (0x5481) %d\n", (u32)arg); - err = (s32)arg; - break; - } - default: { - err = n_tty_ioctl_helper(tty_s, tty_f, cmd, arg); - break; - } - } - return err; -} - -static int -neuronspi_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count, int flow) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - int room, n, rcvd = 0, overflow; - - down_read(&tty->termios_rwsem); - - while (1) { - /* - * When PARMRK is set, each input char may take up to 3 chars - * in the read buf; reduce the buffer space avail by 3x - * - * If we are doing input canonicalization, and there are no - * pending newlines, let characters through without limit, so - * that erase characters will be handled. Other excess - * characters will be beeped. - * - * paired with store in *_copy_from_read_buf() -- guarantees - * the consumer has loaded the data in read_buf up to the new - * read_tail (so this producer will not overwrite unread data) - */ - size_t tail = smp_load_acquire(&ldata->read_tail); - - room = N_TTY_BUF_SIZE - (ldata->read_head - tail); - if (I_PARMRK(tty)) - room = (room + 2) / 3; - room--; - if (room <= 0) { - overflow = ldata->icanon && ldata->canon_head == tail; - if (overflow && room < 0) - ldata->read_head--; - room = overflow; - ldata->no_room = flow && !room; - } else - overflow = 0; - - n = min(count, room); - if (!n) - break; - - /* ignore parity errors if handling overflow */ - if (!overflow || !fp || *fp != TTY_PARITY) - __receive_buf(tty, cp, fp, n); - cp += n; - if (fp) - fp += n; - count -= n; - rcvd += n; - } - - tty->receive_room = room; - - /* Unthrottle if handling overflow on pty */ - if (tty->driver->type == TTY_DRIVER_TYPE_PTY) { - if (overflow) { - tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); - tty_unthrottle_safe(tty); - __tty_set_flow_change(tty, 0); - } - } else - neuronspi_tty_check_throttle(tty); - - up_read(&tty->termios_rwsem); - - return rcvd; -} - -static void neuronspi_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - neuronspi_tty_receive_buf_common(tty, cp, fp, count, 0); - printk(KERN_INFO "NEURONSPI: Receive Buffer 1 called, count %d", count); -} - -static int neuronspi_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - printk(KERN_INFO "NEURONSPI: Receive Buffer 2 called, count %d", count); - return neuronspi_tty_receive_buf_common(tty, cp, fp, count, 1); -} - -/** - * n_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. - * - * n_tty_read()/consumer path: - * claims non-exclusive termios_rwsem - * publishes read_tail - */ - -static ssize_t neuronspi_tty_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t nr) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - unsigned char __user *b = buf; - DEFINE_WAIT_FUNC(wait, woken_wake_function); - int c; - int minimum, time; - ssize_t retval = 0; - long timeout; - int packet; - size_t tail; - - c = job_control(tty, file); - printk(KERN_INFO "TTYRead Called"); - if (c < 0) - return c; - - printk(KERN_INFO "TTYRead %d", (uint32_t)nr); - /* - * Internal serialization of reads. - */ - 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; - if (!ldata->icanon) { - 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); - 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; - 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; - } - } - - if (ldata->icanon && !L_EXTPROC(tty)) { - retval = canon_copy_from_read_buf(tty, &b, &nr); - if (retval) - break; - } else { - 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; - } - } - - neuronspi_tty_check_unthrottle(tty); - - if (b - buf >= minimum) - break; - if (time) - timeout = time; - } - if (tail != ldata->read_tail) - neuronspi_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; -} - -/** - * n_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 neuronspi_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); - int c; - ssize_t retval = 0; - - /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ - if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) { - retval = tty_check_change(tty); - if (retval) - return retval; - } - - down_read(&tty->termios_rwsem); - - /* Write out any echoed characters that are still pending */ - process_echoes(tty); - - 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; - } - if (O_OPOST(tty)) { - while (nr > 0) { - ssize_t num = process_output_block(tty, b, nr); - if (num < 0) { - if (num == -EAGAIN) - break; - retval = num; - goto break_out; - } - b += num; - nr -= num; - if (nr == 0) - break; - c = *b; - if (process_output(c, tty) < 0) - break; - b++; nr--; - } - if (tty->ops->flush_chars) - tty->ops->flush_chars(tty); - } else { - struct neuronspi_tty_data *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; -} - -/** - * n_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 neuronspi_tty_poll(struct tty_struct *tty, struct file *file, - poll_table *wait) -{ - unsigned int mask = 0; - printk(KERN_INFO "TTYPoll Called"); - 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); - 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 void neuronspi_tty_set_termios(struct tty_struct *tty, struct ktermios *old) -{ - struct neuronspi_tty_data *ldata = tty->disc_data; - - if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) { - bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); - ldata->line_start = ldata->read_tail; - if (!L_ICANON(tty) || !read_cnt(ldata)) { - ldata->canon_head = ldata->read_tail; - ldata->push = 0; - } else { - set_bit((ldata->read_head - 1) & (N_TTY_BUF_SIZE - 1), - ldata->read_flags); - ldata->canon_head = ldata->read_head; - ldata->push = 1; - } - ldata->commit_head = ldata->read_head; - ldata->erasing = 0; - ldata->lnext = 0; - } - - ldata->icanon = (L_ICANON(tty) != 0); - - if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || - I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || - I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || - I_PARMRK(tty)) { - bitmap_zero(ldata->char_map, 256); - - if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', ldata->char_map); - if (I_INLCR(tty)) - set_bit('\n', ldata->char_map); - - if (L_ICANON(tty)) { - set_bit(ERASE_CHAR(tty), ldata->char_map); - set_bit(KILL_CHAR(tty), ldata->char_map); - set_bit(EOF_CHAR(tty), ldata->char_map); - set_bit('\n', ldata->char_map); - set_bit(EOL_CHAR(tty), ldata->char_map); - if (L_IEXTEN(tty)) { - set_bit(WERASE_CHAR(tty), ldata->char_map); - set_bit(LNEXT_CHAR(tty), ldata->char_map); - set_bit(EOL2_CHAR(tty), ldata->char_map); - if (L_ECHO(tty)) - set_bit(REPRINT_CHAR(tty), - ldata->char_map); - } - } - if (I_IXON(tty)) { - set_bit(START_CHAR(tty), ldata->char_map); - set_bit(STOP_CHAR(tty), ldata->char_map); - } - if (L_ISIG(tty)) { - set_bit(INTR_CHAR(tty), ldata->char_map); - set_bit(QUIT_CHAR(tty), ldata->char_map); - set_bit(SUSP_CHAR(tty), ldata->char_map); - } - clear_bit(__DISABLED_CHAR, ldata->char_map); - ldata->raw = 0; - ldata->real_raw = 0; - } else { - ldata->raw = 1; - if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) && - (I_IGNPAR(tty) || !I_INPCK(tty)) && - (tty->driver->flags & TTY_DRIVER_REAL_RAW)) - ldata->real_raw = 1; - else - ldata->real_raw = 0; - } - /* - * 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); - process_echoes(tty); - } - - /* The termios change make the tty ready for I/O */ - wake_up_interruptible(&tty->write_wait); - wake_up_interruptible(&tty->read_wait); -} int neuronspi_tty_init() { int err; static struct tty_ldisc_ops neuronspi_tty_ldisc; - printk(KERN_INFO "TTYInit\n"); + printk(KERN_INFO "NEURONSPI TTYInit\n"); 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.ioctl = neuronspi_tty_ioctl; neuronspi_tty_ldisc.owner = THIS_MODULE; - neuronspi_tty_ldisc.open = neuronspi_tty_open; - neuronspi_tty_ldisc.close = neuronspi_tty_close; - neuronspi_tty_ldisc.flush_buffer = neuronspi_tty_flush_buffer, - neuronspi_tty_ldisc.read = neuronspi_tty_read, - neuronspi_tty_ldisc.write = neuronspi_tty_write, - neuronspi_tty_ldisc.ioctl = neuronspi_tty_ioctl, - neuronspi_tty_ldisc.set_termios = neuronspi_tty_set_termios, - neuronspi_tty_ldisc.poll = neuronspi_tty_poll, - neuronspi_tty_ldisc.receive_buf = neuronspi_tty_receive_buf, - neuronspi_tty_ldisc.write_wakeup = neuronspi_tty_write_wakeup, - neuronspi_tty_ldisc.receive_buf2 = neuronspi_tty_receive_buf2, err = tty_register_ldisc(N_PROFIBUS_FDL, &neuronspi_tty_ldisc); if (err) { printk(KERN_INFO "UniPi line discipline registration failed. (%d)", err); diff --git a/modules/unipi/src/unipi_uart.c b/modules/unipi/src/unipi_uart.c index 7007e1b..8c05b50 100644 --- a/modules/unipi/src/unipi_uart.c +++ b/modules/unipi/src/unipi_uart.c @@ -115,24 +115,24 @@ int neuronspi_uart_ioctl (struct uart_port *port, unsigned int ioctl_code, unsig return 1; } case 0x5480: { -#if NEURONSPI_DETAILED_DEBUG > 0 +//#if NEURONSPI_DETAILED_DEBUG > 0 printk(KERN_INFO "NEURONSPI: IOCTL 0x5480\n"); -#endif +//#endif write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_TIMEOUT_REGISTER, &inp_buf, &outp_buf, (ioctl_arg * 1000000) / n_port->baud); neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0); kfree(inp_buf); kfree(outp_buf); - return 1; + return 0; } case 0x5481: { -#if NEURONSPI_DETAILED_DEBUG > 0 +//#if NEURONSPI_DETAILED_DEBUG > 0 printk(KERN_INFO "NEURONSPI: IOCTL 0x5481\n"); -#endif +//#endif write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_TIMEOUT_REGISTER, &inp_buf, &outp_buf, ioctl_arg); neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0); kfree(inp_buf); kfree(outp_buf); - return 1; + return 0; } default: { return -ENOIOCTLCMD; -- 2.34.1