From 8fcbaa2b7f5b70dba9ed1c7f91d0a270ce752e2c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:27 +0200 Subject: TTY: devpts, don't care about TTY in devpts_get_tty The goal is to stop setting and using tty->driver_data in devpts code. It should be used solely by the driver's code, pty in this case. First, here we remove TTY from devpts_get_tty and rename it to devpts_get_priv. Note we do not remove type safety, we just shift the [implicit] (void *) cast one layer up. index was unused in devpts_get_tty, so remove that from the prototype too. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- include/linux/devpts_fs.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index 5ce0e5fd712e..de635a5505ea 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -21,8 +21,8 @@ int devpts_new_index(struct inode *ptmx_inode); void devpts_kill_index(struct inode *ptmx_inode, int idx); /* mknod in devpts */ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); -/* get tty structure */ -struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number); +/* get private structure */ +void *devpts_get_priv(struct inode *pts_inode); /* unlink */ void devpts_pty_kill(struct tty_struct *tty); @@ -36,8 +36,7 @@ static inline int devpts_pty_new(struct inode *ptmx_inode, { return -EINVAL; } -static inline struct tty_struct *devpts_get_tty(struct inode *pts_inode, - int number) +static inline void *devpts_get_priv(struct inode *pts_inode) { return NULL; } -- cgit v1.2.3 From 162b97cfa21f816f39ede1944f2a4220e3cf8969 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:28 +0200 Subject: TTY: devpts, return created inode from devpts_pty_new The goal is to stop setting and using tty->driver_data in devpts code. It should be used solely by the driver's code, pty in this case. For the cleanup of layering, we will need the inode created in devpts_pty_new to be stored into slave's driver_data. So we convert devpts_pty_new to return the inode or an ERR_PTR-encoded error in case of failure. The move of 'inode = new_inode(sb);' from declarators to the code is only cosmetical, but it makes the code easier to read. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 7 +++++-- fs/devpts/inode.c | 12 ++++++------ include/linux/devpts_fs.h | 8 ++++---- 3 files changed, 15 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 65f767154d12..9985b451e937 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -614,6 +614,7 @@ static const struct tty_operations pty_unix98_ops = { static int ptmx_open(struct inode *inode, struct file *filp) { struct tty_struct *tty; + struct inode *slave_inode; int retval; int index; @@ -650,9 +651,11 @@ static int ptmx_open(struct inode *inode, struct file *filp) tty_add_file(tty, filp); - retval = devpts_pty_new(inode, tty->link); - if (retval) + slave_inode = devpts_pty_new(inode, tty->link); + if (IS_ERR(slave_inode)) { + retval = PTR_ERR(slave_inode); goto err_release; + } retval = ptm_driver->ops->open(tty, filp); if (retval) diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 47965807884d..ec3bab716c05 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -545,7 +545,7 @@ void devpts_kill_index(struct inode *ptmx_inode, int idx) mutex_unlock(&allocated_ptys_lock); } -int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) +struct inode *devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) { /* tty layer puts index from devpts_new_index() in here */ int number = tty->index; @@ -553,19 +553,19 @@ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) dev_t device = MKDEV(driver->major, driver->minor_start+number); struct dentry *dentry; struct super_block *sb = pts_sb_from_inode(ptmx_inode); - struct inode *inode = new_inode(sb); + struct inode *inode; struct dentry *root = sb->s_root; struct pts_fs_info *fsi = DEVPTS_SB(sb); struct pts_mount_opts *opts = &fsi->mount_opts; - int ret = 0; char s[12]; /* We're supposed to be given the slave end of a pty */ BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY); BUG_ON(driver->subtype != PTY_TYPE_SLAVE); + inode = new_inode(sb); if (!inode) - return -ENOMEM; + return ERR_PTR(-ENOMEM); inode->i_ino = number + 3; inode->i_uid = opts->setuid ? opts->uid : current_fsuid(); @@ -585,12 +585,12 @@ int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) fsnotify_create(root->d_inode, dentry); } else { iput(inode); - ret = -ENOMEM; + inode = ERR_PTR(-ENOMEM); } mutex_unlock(&root->d_inode->i_mutex); - return ret; + return inode; } void *devpts_get_priv(struct inode *pts_inode) diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index de635a5505ea..4ca846f16fe5 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -20,7 +20,7 @@ int devpts_new_index(struct inode *ptmx_inode); void devpts_kill_index(struct inode *ptmx_inode, int idx); /* mknod in devpts */ -int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); +struct inode *devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); /* get private structure */ void *devpts_get_priv(struct inode *pts_inode); /* unlink */ @@ -31,10 +31,10 @@ void devpts_pty_kill(struct tty_struct *tty); /* Dummy stubs in the no-pty case */ static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; } static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { } -static inline int devpts_pty_new(struct inode *ptmx_inode, - struct tty_struct *tty) +static inline struct inode *devpts_pty_new(struct inode *ptmx_inode, + struct tty_struct *tty) { - return -EINVAL; + return ERR_PTR(-EINVAL); } static inline void *devpts_get_priv(struct inode *pts_inode) { -- cgit v1.2.3 From f11afb61247016162aa92225a337c1575556c9d9 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:29 +0200 Subject: TTY: devpts, do not set driver_data The goal is to stop setting and using tty->driver_data in devpts code. It should be used solely by the driver's code, pty in this case. Now driver_data are managed only in the pty driver. devpts_pty_new is switched to accept what we used to dig out of tty_struct, i.e. device node number and index. This also removes a note about driver_data being set outside of the driver. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 10 +++++----- fs/devpts/inode.c | 21 ++++++--------------- include/linux/devpts_fs.h | 9 +++++---- 3 files changed, 16 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 9985b451e937..559e5b27941a 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -4,9 +4,6 @@ * Added support for a Unix98-style ptmx device. * -- C. Scott Ananian , 14-Jan-1998 * - * When reading this code see also fs/devpts. In particular note that the - * driver_data field is used by the devpts side as a binding to the devpts - * inode. */ #include @@ -59,7 +56,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) #ifdef CONFIG_UNIX98_PTYS if (tty->driver == ptm_driver) { mutex_lock(&devpts_mutex); - devpts_pty_kill(tty->link); + devpts_pty_kill(tty->link->driver_data); mutex_unlock(&devpts_mutex); } #endif @@ -651,7 +648,9 @@ static int ptmx_open(struct inode *inode, struct file *filp) tty_add_file(tty, filp); - slave_inode = devpts_pty_new(inode, tty->link); + slave_inode = devpts_pty_new(inode, + MKDEV(UNIX98_PTY_SLAVE_MAJOR, index), index, + tty->link); if (IS_ERR(slave_inode)) { retval = PTR_ERR(slave_inode); goto err_release; @@ -662,6 +661,7 @@ static int ptmx_open(struct inode *inode, struct file *filp) goto err_release; tty_unlock(tty); + tty->link->driver_data = slave_inode; return 0; err_release: tty_unlock(tty); diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index ec3bab716c05..7a20d673bb8a 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -545,12 +545,9 @@ void devpts_kill_index(struct inode *ptmx_inode, int idx) mutex_unlock(&allocated_ptys_lock); } -struct inode *devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) +struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, + void *priv) { - /* tty layer puts index from devpts_new_index() in here */ - int number = tty->index; - struct tty_driver *driver = tty->driver; - dev_t device = MKDEV(driver->major, driver->minor_start+number); struct dentry *dentry; struct super_block *sb = pts_sb_from_inode(ptmx_inode); struct inode *inode; @@ -559,23 +556,18 @@ struct inode *devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) struct pts_mount_opts *opts = &fsi->mount_opts; char s[12]; - /* We're supposed to be given the slave end of a pty */ - BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY); - BUG_ON(driver->subtype != PTY_TYPE_SLAVE); - inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); - inode->i_ino = number + 3; + inode->i_ino = index + 3; inode->i_uid = opts->setuid ? opts->uid : current_fsuid(); inode->i_gid = opts->setgid ? opts->gid : current_fsgid(); inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; init_special_inode(inode, S_IFCHR|opts->mode, device); - inode->i_private = tty; - tty->driver_data = inode; + inode->i_private = priv; - sprintf(s, "%d", number); + sprintf(s, "%d", index); mutex_lock(&root->d_inode->i_mutex); @@ -613,9 +605,8 @@ void *devpts_get_priv(struct inode *pts_inode) return priv; } -void devpts_pty_kill(struct tty_struct *tty) +void devpts_pty_kill(struct inode *inode) { - struct inode *inode = tty->driver_data; struct super_block *sb = pts_sb_from_inode(inode); struct dentry *root = sb->s_root; struct dentry *dentry; diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index 4ca846f16fe5..251a2090a554 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -20,11 +20,12 @@ int devpts_new_index(struct inode *ptmx_inode); void devpts_kill_index(struct inode *ptmx_inode, int idx); /* mknod in devpts */ -struct inode *devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); +struct inode *devpts_pty_new(struct inode *ptmx_inode, dev_t device, int index, + void *priv); /* get private structure */ void *devpts_get_priv(struct inode *pts_inode); /* unlink */ -void devpts_pty_kill(struct tty_struct *tty); +void devpts_pty_kill(struct inode *inode); #else @@ -32,7 +33,7 @@ void devpts_pty_kill(struct tty_struct *tty); static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; } static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { } static inline struct inode *devpts_pty_new(struct inode *ptmx_inode, - struct tty_struct *tty) + dev_t device, int index, void *priv) { return ERR_PTR(-EINVAL); } @@ -40,7 +41,7 @@ static inline void *devpts_get_priv(struct inode *pts_inode) { return NULL; } -static inline void devpts_pty_kill(struct tty_struct *tty) { } +static inline void devpts_pty_kill(struct inode *inode) { } #endif -- cgit v1.2.3 From 6c633f27ccf783e9a782b84e34aeaeb7949a3359 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:37 +0200 Subject: TTY: audit, stop accessing tty->icount This is a private member of n_tty. Stop accessing it. Instead, take is as an argument. This is needed to allow clean switch of the private members to a separate private structure of n_tty. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 5 +++-- drivers/tty/tty_audit.c | 15 ++++++++------- include/linux/tty.h | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index ceae0744cbb8..3ebab0cfceb2 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -76,7 +76,7 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, unsigned char __user *ptr) { - tty_audit_add_data(tty, &x, 1); + tty_audit_add_data(tty, &x, 1, tty->icanon); return put_user(x, ptr); } @@ -1644,7 +1644,8 @@ static int copy_from_read_buf(struct tty_struct *tty, n -= retval; is_eof = n == 1 && tty->read_buf[tty->read_tail] == EOF_CHAR(tty); - tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n); + tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n, + tty->icanon); spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); tty->read_cnt -= n; diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index b0b39b823ccf..6953dc82850c 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -23,7 +23,7 @@ struct tty_audit_buf { }; static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor, - int icanon) + unsigned icanon) { struct tty_audit_buf *buf; @@ -239,7 +239,8 @@ int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid) * if TTY auditing is disabled or out of memory. Otherwise, return a new * reference to the buffer. */ -static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty) +static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty, + unsigned icanon) { struct tty_audit_buf *buf, *buf2; @@ -257,7 +258,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty) buf2 = tty_audit_buf_alloc(tty->driver->major, tty->driver->minor_start + tty->index, - tty->icanon); + icanon); if (buf2 == NULL) { audit_log_lost("out of memory in TTY auditing"); return NULL; @@ -287,7 +288,7 @@ static struct tty_audit_buf *tty_audit_buf_get(struct tty_struct *tty) * Audit @data of @size from @tty, if necessary. */ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, - size_t size) + size_t size, unsigned icanon) { struct tty_audit_buf *buf; int major, minor; @@ -299,7 +300,7 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, && tty->driver->subtype == PTY_TYPE_MASTER) return; - buf = tty_audit_buf_get(tty); + buf = tty_audit_buf_get(tty, icanon); if (!buf) return; @@ -307,11 +308,11 @@ void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, major = tty->driver->major; minor = tty->driver->minor_start + tty->index; if (buf->major != major || buf->minor != minor - || buf->icanon != tty->icanon) { + || buf->icanon != icanon) { tty_audit_buf_push_current(buf); buf->major = major; buf->minor = minor; - buf->icanon = tty->icanon; + buf->icanon = icanon; } do { size_t run; diff --git a/include/linux/tty.h b/include/linux/tty.h index f0b4eb47297c..f02712da5d85 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -535,7 +535,7 @@ extern void n_tty_inherit_ops(struct tty_ldisc_ops *ops); /* tty_audit.c */ #ifdef CONFIG_AUDIT extern void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, - size_t size); + size_t size, unsigned icanon); extern void tty_audit_exit(void); extern void tty_audit_fork(struct signal_struct *sig); extern void tty_audit_tiocsti(struct tty_struct *tty, char ch); @@ -544,7 +544,7 @@ extern int tty_audit_push_task(struct task_struct *tsk, kuid_t loginuid, u32 sessionid); #else static inline void tty_audit_add_data(struct tty_struct *tty, - unsigned char *data, size_t size) + unsigned char *data, size_t size, unsigned icanon) { } static inline void tty_audit_tiocsti(struct tty_struct *tty, char ch) -- cgit v1.2.3 From 53c5ee2cfb4dadc4f5c24fe671e2fbfc034c875e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:39 +0200 Subject: TTY: move ldisc data from tty_struct: simple members Here we start moving all the n_tty related bits from tty_struct to the newly defined n_tty_data struct in n_tty proper. In this patch primitive members and bits are moved. The rest will be done per-partes in the next patches. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 160 ++++++++++++++++++++++++++++++--------------------- drivers/tty/tty_io.c | 1 - include/linux/tty.h | 5 -- 3 files changed, 93 insertions(+), 73 deletions(-) (limited to 'include') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 3d1594e10d00..bd775a7c0629 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -74,13 +74,20 @@ #define ECHO_OP_ERASE_TAB 0x82 struct n_tty_data { - char dummy; + unsigned int column; + unsigned long overrun_time; + int num_overrun; + + unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; + unsigned char echo_overrun:1; }; static inline int tty_put_user(struct tty_struct *tty, unsigned char x, unsigned char __user *ptr) { - tty_audit_add_data(tty, &x, 1, tty->icanon); + struct n_tty_data *ldata = tty->disc_data; + + tty_audit_add_data(tty, &x, 1, ldata->icanon); return put_user(x, ptr); } @@ -96,6 +103,7 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, static void n_tty_set_room(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int left; int old_left; @@ -115,7 +123,7 @@ static void n_tty_set_room(struct tty_struct *tty) * characters will be beeped. */ if (left <= 0) - left = tty->icanon && !tty->canon_data; + left = ldata->icanon && !tty->canon_data; old_left = tty->receive_room; tty->receive_room = left; @@ -183,6 +191,7 @@ static void check_unthrottle(struct tty_struct *tty) static void reset_buffer_flags(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; spin_lock_irqsave(&tty->read_lock, flags); @@ -190,10 +199,10 @@ static void reset_buffer_flags(struct tty_struct *tty) spin_unlock_irqrestore(&tty->read_lock, flags); mutex_lock(&tty->echo_lock); - tty->echo_pos = tty->echo_cnt = tty->echo_overrun = 0; + tty->echo_pos = tty->echo_cnt = ldata->echo_overrun = 0; mutex_unlock(&tty->echo_lock); - tty->canon_head = tty->canon_data = tty->erasing = 0; + tty->canon_head = tty->canon_data = ldata->erasing = 0; memset(&tty->read_flags, 0, sizeof tty->read_flags); n_tty_set_room(tty); } @@ -239,11 +248,12 @@ static void n_tty_flush_buffer(struct tty_struct *tty) static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; ssize_t n = 0; spin_lock_irqsave(&tty->read_lock, flags); - if (!tty->icanon) { + if (!ldata->icanon) { n = tty->read_cnt; } else if (tty->canon_data) { n = (tty->canon_head > tty->read_tail) ? @@ -305,6 +315,7 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty) static int do_output_char(unsigned char c, struct tty_struct *tty, int space) { + struct n_tty_data *ldata = tty->disc_data; int spaces; if (!space) @@ -313,48 +324,48 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) switch (c) { case '\n': if (O_ONLRET(tty)) - tty->column = 0; + ldata->column = 0; if (O_ONLCR(tty)) { if (space < 2) return -1; - tty->canon_column = tty->column = 0; + tty->canon_column = ldata->column = 0; tty->ops->write(tty, "\r\n", 2); return 2; } - tty->canon_column = tty->column; + tty->canon_column = ldata->column; break; case '\r': - if (O_ONOCR(tty) && tty->column == 0) + if (O_ONOCR(tty) && ldata->column == 0) return 0; if (O_OCRNL(tty)) { c = '\n'; if (O_ONLRET(tty)) - tty->canon_column = tty->column = 0; + tty->canon_column = ldata->column = 0; break; } - tty->canon_column = tty->column = 0; + tty->canon_column = ldata->column = 0; break; case '\t': - spaces = 8 - (tty->column & 7); + spaces = 8 - (ldata->column & 7); if (O_TABDLY(tty) == XTABS) { if (space < spaces) return -1; - tty->column += spaces; + ldata->column += spaces; tty->ops->write(tty, " ", spaces); return spaces; } - tty->column += spaces; + ldata->column += spaces; break; case '\b': - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; break; default: if (!iscntrl(c)) { if (O_OLCUC(tty)) c = toupper(c); if (!is_continuation(c, tty)) - tty->column++; + ldata->column++; } break; } @@ -415,6 +426,7 @@ static int process_output(unsigned char c, struct tty_struct *tty) static ssize_t process_output_block(struct tty_struct *tty, const unsigned char *buf, unsigned int nr) { + struct n_tty_data *ldata = tty->disc_data; int space; int i; const unsigned char *cp; @@ -435,30 +447,30 @@ static ssize_t process_output_block(struct tty_struct *tty, switch (c) { case '\n': if (O_ONLRET(tty)) - tty->column = 0; + ldata->column = 0; if (O_ONLCR(tty)) goto break_out; - tty->canon_column = tty->column; + tty->canon_column = ldata->column; break; case '\r': - if (O_ONOCR(tty) && tty->column == 0) + if (O_ONOCR(tty) && ldata->column == 0) goto break_out; if (O_OCRNL(tty)) goto break_out; - tty->canon_column = tty->column = 0; + tty->canon_column = ldata->column = 0; break; case '\t': goto break_out; case '\b': - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; break; default: if (!iscntrl(c)) { if (O_OLCUC(tty)) goto break_out; if (!is_continuation(c, tty)) - tty->column++; + ldata->column++; } break; } @@ -498,6 +510,7 @@ break_out: static void process_echoes(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int space, nr; unsigned char c; unsigned char *cp, *buf_end; @@ -559,22 +572,22 @@ static void process_echoes(struct tty_struct *tty) space -= num_bs; while (num_bs--) { tty_put_char(tty, '\b'); - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; } cp += 3; nr -= 3; break; case ECHO_OP_SET_CANON_COL: - tty->canon_column = tty->column; + tty->canon_column = ldata->column; cp += 2; nr -= 2; break; case ECHO_OP_MOVE_BACK_COL: - if (tty->column > 0) - tty->column--; + if (ldata->column > 0) + ldata->column--; cp += 2; nr -= 2; break; @@ -586,7 +599,7 @@ static void process_echoes(struct tty_struct *tty) break; } tty_put_char(tty, ECHO_OP_START); - tty->column++; + ldata->column++; space--; cp += 2; nr -= 2; @@ -608,7 +621,7 @@ static void process_echoes(struct tty_struct *tty) } tty_put_char(tty, '^'); tty_put_char(tty, op ^ 0100); - tty->column += 2; + ldata->column += 2; space -= 2; cp += 2; nr -= 2; @@ -641,14 +654,14 @@ static void process_echoes(struct tty_struct *tty) if (nr == 0) { tty->echo_pos = 0; tty->echo_cnt = 0; - tty->echo_overrun = 0; + ldata->echo_overrun = 0; } else { int num_processed = tty->echo_cnt - nr; tty->echo_pos += num_processed; tty->echo_pos &= N_TTY_BUF_SIZE - 1; tty->echo_cnt = nr; if (num_processed > 0) - tty->echo_overrun = 0; + ldata->echo_overrun = 0; } mutex_unlock(&tty->echo_lock); @@ -670,6 +683,7 @@ static void process_echoes(struct tty_struct *tty) static void add_echo_byte(unsigned char c, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int new_byte_pos; if (tty->echo_cnt == N_TTY_BUF_SIZE) { @@ -695,7 +709,7 @@ static void add_echo_byte(unsigned char c, struct tty_struct *tty) } tty->echo_pos &= N_TTY_BUF_SIZE - 1; - tty->echo_overrun = 1; + ldata->echo_overrun = 1; } else { new_byte_pos = tty->echo_pos + tty->echo_cnt; new_byte_pos &= N_TTY_BUF_SIZE - 1; @@ -845,9 +859,10 @@ static void echo_char(unsigned char c, struct tty_struct *tty) static inline void finish_erasing(struct tty_struct *tty) { - if (tty->erasing) { + struct n_tty_data *ldata = tty->disc_data; + if (ldata->erasing) { echo_char_raw('/', tty); - tty->erasing = 0; + ldata->erasing = 0; } } @@ -865,6 +880,7 @@ static inline void finish_erasing(struct tty_struct *tty) static void eraser(unsigned char c, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; enum { ERASE, WERASE, KILL } kill_type; int head, seen_alnums, cnt; unsigned long flags; @@ -932,9 +948,9 @@ static void eraser(unsigned char c, struct tty_struct *tty) spin_unlock_irqrestore(&tty->read_lock, flags); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { - if (!tty->erasing) { + if (!ldata->erasing) { echo_char_raw('\\', tty); - tty->erasing = 1; + ldata->erasing = 1; } /* if cnt > 1, output a multi-byte character */ echo_char(c, tty); @@ -1056,16 +1072,17 @@ static inline void n_tty_receive_break(struct tty_struct *tty) static inline void n_tty_receive_overrun(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; char buf[64]; - tty->num_overrun++; - if (time_before(tty->overrun_time, jiffies - HZ) || - time_after(tty->overrun_time, jiffies)) { + ldata->num_overrun++; + if (time_after(jiffies, ldata->overrun_time + HZ) || + time_after(ldata->overrun_time, jiffies)) { printk(KERN_WARNING "%s: %d input overrun(s)\n", tty_name(tty, buf), - tty->num_overrun); - tty->overrun_time = jiffies; - tty->num_overrun = 0; + ldata->num_overrun); + ldata->overrun_time = jiffies; + ldata->num_overrun = 0; } } @@ -1105,10 +1122,11 @@ static inline void n_tty_receive_parity_error(struct tty_struct *tty, static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; int parmrk; - if (tty->raw) { + if (ldata->raw) { put_tty_queue(c, tty); return; } @@ -1147,8 +1165,8 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) * handle specially, do shortcut processing to speed things * up. */ - if (!test_bit(c, tty->process_char_map) || tty->lnext) { - tty->lnext = 0; + if (!test_bit(c, tty->process_char_map) || ldata->lnext) { + ldata->lnext = 0; parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { /* beep if no space */ @@ -1222,7 +1240,7 @@ send_signal: } else if (c == '\n' && I_INLCR(tty)) c = '\r'; - if (tty->icanon) { + if (ldata->icanon) { if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { eraser(c, tty); @@ -1230,7 +1248,7 @@ send_signal: return; } if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { - tty->lnext = 1; + ldata->lnext = 1; if (L_ECHO(tty)) { finish_erasing(tty); if (L_ECHOCTL(tty)) { @@ -1373,13 +1391,14 @@ static void n_tty_write_wakeup(struct tty_struct *tty) static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { + struct n_tty_data *ldata = tty->disc_data; const unsigned char *p; char *f, flags = TTY_NORMAL; int i; char buf[64]; unsigned long cpuflags; - if (tty->real_raw) { + if (ldata->real_raw) { spin_lock_irqsave(&tty->read_lock, cpuflags); i = min(N_TTY_BUF_SIZE - tty->read_cnt, N_TTY_BUF_SIZE - tty->read_head); @@ -1427,7 +1446,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, n_tty_set_room(tty); - if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || + if ((!ldata->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) @@ -1471,6 +1490,7 @@ int is_ignored(int sig) static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { + struct n_tty_data *ldata = tty->disc_data; int canon_change = 1; if (old) @@ -1479,16 +1499,16 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) memset(&tty->read_flags, 0, sizeof tty->read_flags); tty->canon_head = tty->read_tail; tty->canon_data = 0; - tty->erasing = 0; + ldata->erasing = 0; } if (canon_change && !L_ICANON(tty) && tty->read_cnt) wake_up_interruptible(&tty->read_wait); - tty->icanon = (L_ICANON(tty) != 0); + ldata->icanon = (L_ICANON(tty) != 0); if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { - tty->raw = 1; - tty->real_raw = 1; + ldata->raw = 1; + ldata->real_raw = 1; n_tty_set_room(tty); return; } @@ -1531,16 +1551,16 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) set_bit(SUSP_CHAR(tty), tty->process_char_map); } clear_bit(__DISABLED_CHAR, tty->process_char_map); - tty->raw = 0; - tty->real_raw = 0; + ldata->raw = 0; + ldata->real_raw = 0; } else { - tty->raw = 1; + 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)) - tty->real_raw = 1; + ldata->real_raw = 1; else - tty->real_raw = 0; + ldata->real_raw = 0; } n_tty_set_room(tty); /* The termios change make the tty ready for I/O */ @@ -1589,6 +1609,8 @@ static int n_tty_open(struct tty_struct *tty) if (!ldata) goto err; + ldata->overrun_time = jiffies; + /* These are ugly. Currently a malloc failure here can panic */ tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); @@ -1598,7 +1620,7 @@ static int n_tty_open(struct tty_struct *tty) tty->disc_data = ldata; reset_buffer_flags(tty); tty_unthrottle(tty); - tty->column = 0; + ldata->column = 0; n_tty_set_termios(tty, NULL); tty->minimum_to_wake = 1; tty->closing = 0; @@ -1614,8 +1636,10 @@ err: static inline int input_available_p(struct tty_struct *tty, int amt) { + struct n_tty_data *ldata = tty->disc_data; + tty_flush_to_ldisc(tty); - if (tty->icanon && !L_EXTPROC(tty)) { + if (ldata->icanon && !L_EXTPROC(tty)) { if (tty->canon_data) return 1; } else if (tty->read_cnt >= (amt ? amt : 1)) @@ -1646,6 +1670,7 @@ static int copy_from_read_buf(struct tty_struct *tty, size_t *nr) { + struct n_tty_data *ldata = tty->disc_data; int retval; size_t n; unsigned long flags; @@ -1662,12 +1687,12 @@ static int copy_from_read_buf(struct tty_struct *tty, is_eof = n == 1 && tty->read_buf[tty->read_tail] == EOF_CHAR(tty); tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n, - tty->icanon); + ldata->icanon); spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); tty->read_cnt -= n; /* Turn single EOF into zero-length read */ - if (L_EXTPROC(tty) && tty->icanon && is_eof && !tty->read_cnt) + if (L_EXTPROC(tty) && ldata->icanon && is_eof && !tty->read_cnt) n = 0; spin_unlock_irqrestore(&tty->read_lock, flags); *b += n; @@ -1736,6 +1761,7 @@ static int job_control(struct tty_struct *tty, struct file *file) static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr) { + struct n_tty_data *ldata = tty->disc_data; unsigned char __user *b = buf; DECLARE_WAITQUEUE(wait, current); int c; @@ -1753,7 +1779,7 @@ do_it_again: minimum = time = 0; timeout = MAX_SCHEDULE_TIMEOUT; - if (!tty->icanon) { + if (!ldata->icanon) { time = (HZ / 10) * TIME_CHAR(tty); minimum = MIN_CHAR(tty); if (minimum) { @@ -1846,7 +1872,7 @@ do_it_again: nr--; } - if (tty->icanon && !L_EXTPROC(tty)) { + if (ldata->icanon && !L_EXTPROC(tty)) { /* N.B. avoid overrun if nr == 0 */ spin_lock_irqsave(&tty->read_lock, flags); while (nr && tty->read_cnt) { diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e835a5b8d089..67b024ca16ec 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2932,7 +2932,6 @@ void initialize_tty_struct(struct tty_struct *tty, tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; - tty->overrun_time = jiffies; tty_buffer_init(tty); mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); diff --git a/include/linux/tty.h b/include/linux/tty.h index f02712da5d85..de590cec973b 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -270,13 +270,8 @@ struct tty_struct { * historical reasons, this is included in the tty structure. * Mostly locked by the BKL. */ - unsigned int column; - unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; unsigned char closing:1; - unsigned char echo_overrun:1; unsigned short minimum_to_wake; - unsigned long overrun_time; - int num_overrun; unsigned long process_char_map[256/(8*sizeof(unsigned long))]; char *read_buf; int read_head; -- cgit v1.2.3 From 3fe780b379fac2e1eeb5907ee7c864756ce7ec83 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:40 +0200 Subject: TTY: move ldisc data from tty_struct: bitmaps Here we move bitmaps and use DECLARE_BITMAP to declare them in the new structure. And instead of memset, we use bitmap_zero as it is more appropriate. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 52 ++++++++++++++++++++++++++++------------------------ include/linux/tty.h | 2 -- 2 files changed, 28 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index bd775a7c0629..702dd4adbdc9 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -80,6 +80,9 @@ struct n_tty_data { unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; unsigned char echo_overrun:1; + + DECLARE_BITMAP(process_char_map, 256); + DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); }; static inline int tty_put_user(struct tty_struct *tty, unsigned char x, @@ -203,7 +206,7 @@ static void reset_buffer_flags(struct tty_struct *tty) mutex_unlock(&tty->echo_lock); tty->canon_head = tty->canon_data = ldata->erasing = 0; - memset(&tty->read_flags, 0, sizeof tty->read_flags); + bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); n_tty_set_room(tty); } @@ -1165,7 +1168,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) * handle specially, do shortcut processing to speed things * up. */ - if (!test_bit(c, tty->process_char_map) || ldata->lnext) { + if (!test_bit(c, ldata->process_char_map) || ldata->lnext) { ldata->lnext = 0; parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { @@ -1321,7 +1324,7 @@ send_signal: handle_newline: spin_lock_irqsave(&tty->read_lock, flags); - set_bit(tty->read_head, tty->read_flags); + set_bit(tty->read_head, ldata->read_flags); put_tty_queue_nolock(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; @@ -1496,7 +1499,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) if (old) canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON; if (canon_change) { - memset(&tty->read_flags, 0, sizeof tty->read_flags); + bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); tty->canon_head = tty->read_tail; tty->canon_data = 0; ldata->erasing = 0; @@ -1516,41 +1519,41 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || I_PARMRK(tty)) { - memset(tty->process_char_map, 0, 256/8); + bitmap_zero(ldata->process_char_map, 256); if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', tty->process_char_map); + set_bit('\r', ldata->process_char_map); if (I_INLCR(tty)) - set_bit('\n', tty->process_char_map); + set_bit('\n', ldata->process_char_map); if (L_ICANON(tty)) { - set_bit(ERASE_CHAR(tty), tty->process_char_map); - set_bit(KILL_CHAR(tty), tty->process_char_map); - set_bit(EOF_CHAR(tty), tty->process_char_map); - set_bit('\n', tty->process_char_map); - set_bit(EOL_CHAR(tty), tty->process_char_map); + set_bit(ERASE_CHAR(tty), ldata->process_char_map); + set_bit(KILL_CHAR(tty), ldata->process_char_map); + set_bit(EOF_CHAR(tty), ldata->process_char_map); + set_bit('\n', ldata->process_char_map); + set_bit(EOL_CHAR(tty), ldata->process_char_map); if (L_IEXTEN(tty)) { set_bit(WERASE_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); set_bit(LNEXT_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); set_bit(EOL2_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), - tty->process_char_map); + ldata->process_char_map); } } if (I_IXON(tty)) { - set_bit(START_CHAR(tty), tty->process_char_map); - set_bit(STOP_CHAR(tty), tty->process_char_map); + set_bit(START_CHAR(tty), ldata->process_char_map); + set_bit(STOP_CHAR(tty), ldata->process_char_map); } if (L_ISIG(tty)) { - set_bit(INTR_CHAR(tty), tty->process_char_map); - set_bit(QUIT_CHAR(tty), tty->process_char_map); - set_bit(SUSP_CHAR(tty), tty->process_char_map); + set_bit(INTR_CHAR(tty), ldata->process_char_map); + set_bit(QUIT_CHAR(tty), ldata->process_char_map); + set_bit(SUSP_CHAR(tty), ldata->process_char_map); } - clear_bit(__DISABLED_CHAR, tty->process_char_map); + clear_bit(__DISABLED_CHAR, ldata->process_char_map); ldata->raw = 0; ldata->real_raw = 0; } else { @@ -1879,7 +1882,7 @@ do_it_again: int eol; eol = test_and_clear_bit(tty->read_tail, - tty->read_flags); + ldata->read_flags); c = tty->read_buf[tty->read_tail]; tty->read_tail = ((tty->read_tail+1) & (N_TTY_BUF_SIZE-1)); @@ -2105,6 +2108,7 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, static unsigned long inq_canon(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int nr, head, tail; if (!tty->canon_data) @@ -2114,7 +2118,7 @@ static unsigned long inq_canon(struct tty_struct *tty) nr = (head - tail) & (N_TTY_BUF_SIZE-1); /* Skip EOF-chars.. */ while (head != tail) { - if (test_bit(tail, tty->read_flags) && + if (test_bit(tail, ldata->read_flags) && tty->read_buf[tail] == __DISABLED_CHAR) nr--; tail = (tail+1) & (N_TTY_BUF_SIZE-1); diff --git a/include/linux/tty.h b/include/linux/tty.h index de590cec973b..2161e6b5a94c 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -272,12 +272,10 @@ struct tty_struct { */ unsigned char closing:1; unsigned short minimum_to_wake; - unsigned long process_char_map[256/(8*sizeof(unsigned long))]; char *read_buf; int read_head; int read_tail; int read_cnt; - unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))]; unsigned char *echo_buf; unsigned int echo_pos; unsigned int echo_cnt; -- cgit v1.2.3 From ba2e68ac6157004ee4922fb39ebd9459bbae883e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:41 +0200 Subject: TTY: move ldisc data from tty_struct: read_* and echo_* and canon_* stuff All the ring-buffers... Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 260 +++++++++++++++++++++++++++------------------------- include/linux/tty.h | 10 -- 2 files changed, 137 insertions(+), 133 deletions(-) (limited to 'include') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 702dd4adbdc9..4794537a50ff 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -83,6 +83,19 @@ struct n_tty_data { DECLARE_BITMAP(process_char_map, 256); DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); + + char *read_buf; + int read_head; + int read_tail; + int read_cnt; + + unsigned char *echo_buf; + unsigned int echo_pos; + unsigned int echo_cnt; + + int canon_data; + unsigned long canon_head; + unsigned int canon_column; }; static inline int tty_put_user(struct tty_struct *tty, unsigned char x, @@ -110,14 +123,14 @@ static void n_tty_set_room(struct tty_struct *tty) int left; int old_left; - /* tty->read_cnt is not read locked ? */ + /* ldata->read_cnt is not read locked ? */ if (I_PARMRK(tty)) { /* Multiply read_cnt by 3, since each byte might take up to * three times as many spaces when PARMRK is set (depending on * its flags, e.g. parity error). */ - left = N_TTY_BUF_SIZE - tty->read_cnt * 3 - 1; + left = N_TTY_BUF_SIZE - ldata->read_cnt * 3 - 1; } else - left = N_TTY_BUF_SIZE - tty->read_cnt - 1; + left = N_TTY_BUF_SIZE - ldata->read_cnt - 1; /* * If we are doing input canonicalization, and there are no @@ -126,7 +139,7 @@ static void n_tty_set_room(struct tty_struct *tty) * characters will be beeped. */ if (left <= 0) - left = ldata->icanon && !tty->canon_data; + left = ldata->icanon && !ldata->canon_data; old_left = tty->receive_room; tty->receive_room = left; @@ -137,10 +150,12 @@ static void n_tty_set_room(struct tty_struct *tty) static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) { - if (tty->read_cnt < N_TTY_BUF_SIZE) { - tty->read_buf[tty->read_head] = c; - tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1); - tty->read_cnt++; + struct n_tty_data *ldata = tty->disc_data; + + if (ldata->read_cnt < N_TTY_BUF_SIZE) { + ldata->read_buf[ldata->read_head] = c; + ldata->read_head = (ldata->read_head + 1) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt++; } } @@ -198,14 +213,14 @@ static void reset_buffer_flags(struct tty_struct *tty) unsigned long flags; spin_lock_irqsave(&tty->read_lock, flags); - tty->read_head = tty->read_tail = tty->read_cnt = 0; + ldata->read_head = ldata->read_tail = ldata->read_cnt = 0; spin_unlock_irqrestore(&tty->read_lock, flags); mutex_lock(&tty->echo_lock); - tty->echo_pos = tty->echo_cnt = ldata->echo_overrun = 0; + ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0; mutex_unlock(&tty->echo_lock); - tty->canon_head = tty->canon_data = ldata->erasing = 0; + ldata->canon_head = ldata->canon_data = ldata->erasing = 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); n_tty_set_room(tty); } @@ -257,11 +272,11 @@ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) spin_lock_irqsave(&tty->read_lock, flags); if (!ldata->icanon) { - n = tty->read_cnt; - } else if (tty->canon_data) { - n = (tty->canon_head > tty->read_tail) ? - tty->canon_head - tty->read_tail : - tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail); + n = ldata->read_cnt; + } else if (ldata->canon_data) { + n = (ldata->canon_head > ldata->read_tail) ? + ldata->canon_head - ldata->read_tail : + ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail); } spin_unlock_irqrestore(&tty->read_lock, flags); return n; @@ -331,11 +346,11 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) if (O_ONLCR(tty)) { if (space < 2) return -1; - tty->canon_column = ldata->column = 0; + ldata->canon_column = ldata->column = 0; tty->ops->write(tty, "\r\n", 2); return 2; } - tty->canon_column = ldata->column; + ldata->canon_column = ldata->column; break; case '\r': if (O_ONOCR(tty) && ldata->column == 0) @@ -343,10 +358,10 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) if (O_OCRNL(tty)) { c = '\n'; if (O_ONLRET(tty)) - tty->canon_column = ldata->column = 0; + ldata->canon_column = ldata->column = 0; break; } - tty->canon_column = ldata->column = 0; + ldata->canon_column = ldata->column = 0; break; case '\t': spaces = 8 - (ldata->column & 7); @@ -453,14 +468,14 @@ static ssize_t process_output_block(struct tty_struct *tty, ldata->column = 0; if (O_ONLCR(tty)) goto break_out; - tty->canon_column = ldata->column; + 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; - tty->canon_column = ldata->column = 0; + ldata->canon_column = ldata->column = 0; break; case '\t': goto break_out; @@ -518,7 +533,7 @@ static void process_echoes(struct tty_struct *tty) unsigned char c; unsigned char *cp, *buf_end; - if (!tty->echo_cnt) + if (!ldata->echo_cnt) return; mutex_lock(&tty->output_lock); @@ -526,9 +541,9 @@ static void process_echoes(struct tty_struct *tty) space = tty_write_room(tty); - buf_end = tty->echo_buf + N_TTY_BUF_SIZE; - cp = tty->echo_buf + tty->echo_pos; - nr = tty->echo_cnt; + buf_end = ldata->echo_buf + N_TTY_BUF_SIZE; + cp = ldata->echo_buf + ldata->echo_pos; + nr = ldata->echo_cnt; while (nr > 0) { c = *cp; if (c == ECHO_OP_START) { @@ -565,7 +580,7 @@ static void process_echoes(struct tty_struct *tty) * Otherwise, tab spacing is normal. */ if (!(num_chars & 0x80)) - num_chars += tty->canon_column; + num_chars += ldata->canon_column; num_bs = 8 - (num_chars & 7); if (num_bs > space) { @@ -583,7 +598,7 @@ static void process_echoes(struct tty_struct *tty) break; case ECHO_OP_SET_CANON_COL: - tty->canon_column = ldata->column; + ldata->canon_column = ldata->column; cp += 2; nr -= 2; break; @@ -655,14 +670,14 @@ static void process_echoes(struct tty_struct *tty) } if (nr == 0) { - tty->echo_pos = 0; - tty->echo_cnt = 0; + ldata->echo_pos = 0; + ldata->echo_cnt = 0; ldata->echo_overrun = 0; } else { - int num_processed = tty->echo_cnt - nr; - tty->echo_pos += num_processed; - tty->echo_pos &= N_TTY_BUF_SIZE - 1; - tty->echo_cnt = nr; + int num_processed = ldata->echo_cnt - nr; + ldata->echo_pos += num_processed; + ldata->echo_pos &= N_TTY_BUF_SIZE - 1; + ldata->echo_cnt = nr; if (num_processed > 0) ldata->echo_overrun = 0; } @@ -689,37 +704,37 @@ static void add_echo_byte(unsigned char c, struct tty_struct *tty) struct n_tty_data *ldata = tty->disc_data; int new_byte_pos; - if (tty->echo_cnt == N_TTY_BUF_SIZE) { + if (ldata->echo_cnt == N_TTY_BUF_SIZE) { /* Circular buffer is already at capacity */ - new_byte_pos = tty->echo_pos; + new_byte_pos = ldata->echo_pos; /* * Since the buffer start position needs to be advanced, * be sure to step by a whole operation byte group. */ - if (tty->echo_buf[tty->echo_pos] == ECHO_OP_START) { - if (tty->echo_buf[(tty->echo_pos + 1) & + if (ldata->echo_buf[ldata->echo_pos] == ECHO_OP_START) { + if (ldata->echo_buf[(ldata->echo_pos + 1) & (N_TTY_BUF_SIZE - 1)] == ECHO_OP_ERASE_TAB) { - tty->echo_pos += 3; - tty->echo_cnt -= 2; + ldata->echo_pos += 3; + ldata->echo_cnt -= 2; } else { - tty->echo_pos += 2; - tty->echo_cnt -= 1; + ldata->echo_pos += 2; + ldata->echo_cnt -= 1; } } else { - tty->echo_pos++; + ldata->echo_pos++; } - tty->echo_pos &= N_TTY_BUF_SIZE - 1; + ldata->echo_pos &= N_TTY_BUF_SIZE - 1; ldata->echo_overrun = 1; } else { - new_byte_pos = tty->echo_pos + tty->echo_cnt; + new_byte_pos = ldata->echo_pos + ldata->echo_cnt; new_byte_pos &= N_TTY_BUF_SIZE - 1; - tty->echo_cnt++; + ldata->echo_cnt++; } - tty->echo_buf[new_byte_pos] = c; + ldata->echo_buf[new_byte_pos] = c; } /** @@ -889,7 +904,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) unsigned long flags; /* FIXME: locking needed ? */ - if (tty->read_head == tty->canon_head) { + if (ldata->read_head == ldata->canon_head) { /* process_output('\a', tty); */ /* what do you think? */ return; } @@ -900,17 +915,17 @@ static void eraser(unsigned char c, struct tty_struct *tty) else { if (!L_ECHO(tty)) { spin_lock_irqsave(&tty->read_lock, flags); - tty->read_cnt -= ((tty->read_head - tty->canon_head) & + ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & (N_TTY_BUF_SIZE - 1)); - tty->read_head = tty->canon_head; + ldata->read_head = ldata->canon_head; spin_unlock_irqrestore(&tty->read_lock, flags); return; } if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { spin_lock_irqsave(&tty->read_lock, flags); - tty->read_cnt -= ((tty->read_head - tty->canon_head) & + ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & (N_TTY_BUF_SIZE - 1)); - tty->read_head = tty->canon_head; + ldata->read_head = ldata->canon_head; spin_unlock_irqrestore(&tty->read_lock, flags); finish_erasing(tty); echo_char(KILL_CHAR(tty), tty); @@ -924,14 +939,14 @@ static void eraser(unsigned char c, struct tty_struct *tty) seen_alnums = 0; /* FIXME: Locking ?? */ - while (tty->read_head != tty->canon_head) { - head = tty->read_head; + while (ldata->read_head != ldata->canon_head) { + head = ldata->read_head; /* erase a single possibly multibyte character */ do { head = (head - 1) & (N_TTY_BUF_SIZE-1); - c = tty->read_buf[head]; - } while (is_continuation(c, tty) && head != tty->canon_head); + c = ldata->read_buf[head]; + } while (is_continuation(c, tty) && head != ldata->canon_head); /* do not partially erase */ if (is_continuation(c, tty)) @@ -944,10 +959,10 @@ static void eraser(unsigned char c, struct tty_struct *tty) else if (seen_alnums) break; } - cnt = (tty->read_head - head) & (N_TTY_BUF_SIZE-1); + cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1); spin_lock_irqsave(&tty->read_lock, flags); - tty->read_head = head; - tty->read_cnt -= cnt; + ldata->read_head = head; + ldata->read_cnt -= cnt; spin_unlock_irqrestore(&tty->read_lock, flags); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { @@ -959,7 +974,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) echo_char(c, tty); while (--cnt > 0) { head = (head+1) & (N_TTY_BUF_SIZE-1); - echo_char_raw(tty->read_buf[head], tty); + echo_char_raw(ldata->read_buf[head], tty); echo_move_back_col(tty); } } else if (kill_type == ERASE && !L_ECHOE(tty)) { @@ -967,7 +982,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) } else if (c == '\t') { unsigned int num_chars = 0; int after_tab = 0; - unsigned long tail = tty->read_head; + unsigned long tail = ldata->read_head; /* * Count the columns used for characters @@ -976,9 +991,9 @@ static void eraser(unsigned char c, struct tty_struct *tty) * This info is used to go back the correct * number of columns. */ - while (tail != tty->canon_head) { + while (tail != ldata->canon_head) { tail = (tail-1) & (N_TTY_BUF_SIZE-1); - c = tty->read_buf[tail]; + c = ldata->read_buf[tail]; if (c == '\t') { after_tab = 1; break; @@ -1006,7 +1021,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) if (kill_type == ERASE) break; } - if (tty->read_head == tty->canon_head && L_ECHO(tty)) + if (ldata->read_head == ldata->canon_head && L_ECHO(tty)) finish_erasing(tty); } @@ -1171,7 +1186,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) if (!test_bit(c, ldata->process_char_map) || ldata->lnext) { ldata->lnext = 0; parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { + if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { /* beep if no space */ if (L_ECHO(tty)) process_output('\a', tty); @@ -1180,7 +1195,7 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) if (L_ECHO(tty)) { finish_erasing(tty); /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) + if (ldata->canon_head == ldata->read_head) echo_set_canon_col(tty); echo_char(c, tty); process_echoes(tty); @@ -1264,20 +1279,20 @@ send_signal: } if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { - unsigned long tail = tty->canon_head; + unsigned long tail = ldata->canon_head; finish_erasing(tty); echo_char(c, tty); echo_char_raw('\n', tty); - while (tail != tty->read_head) { - echo_char(tty->read_buf[tail], tty); + while (tail != ldata->read_head) { + echo_char(ldata->read_buf[tail], tty); tail = (tail+1) & (N_TTY_BUF_SIZE-1); } process_echoes(tty); return; } if (c == '\n') { - if (tty->read_cnt >= N_TTY_BUF_SIZE) { + if (ldata->read_cnt >= N_TTY_BUF_SIZE) { if (L_ECHO(tty)) process_output('\a', tty); return; @@ -1289,9 +1304,9 @@ send_signal: goto handle_newline; } if (c == EOF_CHAR(tty)) { - if (tty->read_cnt >= N_TTY_BUF_SIZE) + if (ldata->read_cnt >= N_TTY_BUF_SIZE) return; - if (tty->canon_head != tty->read_head) + if (ldata->canon_head != ldata->read_head) set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; @@ -1300,7 +1315,7 @@ send_signal: (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) { + if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk)) { if (L_ECHO(tty)) process_output('\a', tty); return; @@ -1310,7 +1325,7 @@ send_signal: */ if (L_ECHO(tty)) { /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) + if (ldata->canon_head == ldata->read_head) echo_set_canon_col(tty); echo_char(c, tty); process_echoes(tty); @@ -1324,10 +1339,10 @@ send_signal: handle_newline: spin_lock_irqsave(&tty->read_lock, flags); - set_bit(tty->read_head, ldata->read_flags); + set_bit(ldata->read_head, ldata->read_flags); put_tty_queue_nolock(c, tty); - tty->canon_head = tty->read_head; - tty->canon_data++; + ldata->canon_head = ldata->read_head; + ldata->canon_data++; spin_unlock_irqrestore(&tty->read_lock, flags); kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) @@ -1337,7 +1352,7 @@ handle_newline: } parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { + if (ldata->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { /* beep if no space */ if (L_ECHO(tty)) process_output('\a', tty); @@ -1349,7 +1364,7 @@ handle_newline: echo_char_raw('\n', tty); else { /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) + if (ldata->canon_head == ldata->read_head) echo_set_canon_col(tty); echo_char(c, tty); } @@ -1403,21 +1418,21 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, if (ldata->real_raw) { spin_lock_irqsave(&tty->read_lock, cpuflags); - i = min(N_TTY_BUF_SIZE - tty->read_cnt, - N_TTY_BUF_SIZE - tty->read_head); + i = min(N_TTY_BUF_SIZE - ldata->read_cnt, + N_TTY_BUF_SIZE - ldata->read_head); i = min(count, i); - memcpy(tty->read_buf + tty->read_head, cp, i); - tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); - tty->read_cnt += i; + memcpy(ldata->read_buf + ldata->read_head, cp, i); + ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt += i; cp += i; count -= i; - i = min(N_TTY_BUF_SIZE - tty->read_cnt, - N_TTY_BUF_SIZE - tty->read_head); + i = min(N_TTY_BUF_SIZE - ldata->read_cnt, + N_TTY_BUF_SIZE - ldata->read_head); i = min(count, i); - memcpy(tty->read_buf + tty->read_head, cp, i); - tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); - tty->read_cnt += i; + memcpy(ldata->read_buf + ldata->read_head, cp, i); + ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt += i; spin_unlock_irqrestore(&tty->read_lock, cpuflags); } else { for (i = count, p = cp, f = fp; i; i--, p++) { @@ -1449,7 +1464,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, n_tty_set_room(tty); - if ((!ldata->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || + if ((!ldata->icanon && (ldata->read_cnt >= tty->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) @@ -1500,12 +1515,12 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON; if (canon_change) { bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); - tty->canon_head = tty->read_tail; - tty->canon_data = 0; + ldata->canon_head = ldata->read_tail; + ldata->canon_data = 0; ldata->erasing = 0; } - if (canon_change && !L_ICANON(tty) && tty->read_cnt) + if (canon_change && !L_ICANON(tty) && ldata->read_cnt) wake_up_interruptible(&tty->read_wait); ldata->icanon = (L_ICANON(tty) != 0); @@ -1586,11 +1601,9 @@ static void n_tty_close(struct tty_struct *tty) struct n_tty_data *ldata = tty->disc_data; n_tty_flush_buffer(tty); - kfree(tty->read_buf); - kfree(tty->echo_buf); + kfree(ldata->read_buf); + kfree(ldata->echo_buf); kfree(ldata); - tty->read_buf = NULL; - tty->echo_buf = NULL; tty->disc_data = NULL; } @@ -1615,9 +1628,9 @@ static int n_tty_open(struct tty_struct *tty) ldata->overrun_time = jiffies; /* These are ugly. Currently a malloc failure here can panic */ - tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - if (!tty->read_buf || !tty->echo_buf) + ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + ldata->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); + if (!ldata->read_buf || !ldata->echo_buf) goto err_free_bufs; tty->disc_data = ldata; @@ -1630,8 +1643,8 @@ static int n_tty_open(struct tty_struct *tty) return 0; err_free_bufs: - kfree(tty->read_buf); - kfree(tty->echo_buf); + kfree(ldata->read_buf); + kfree(ldata->echo_buf); kfree(ldata); err: return -ENOMEM; @@ -1643,9 +1656,9 @@ static inline int input_available_p(struct tty_struct *tty, int amt) tty_flush_to_ldisc(tty); if (ldata->icanon && !L_EXTPROC(tty)) { - if (tty->canon_data) + if (ldata->canon_data) return 1; - } else if (tty->read_cnt >= (amt ? amt : 1)) + } else if (ldata->read_cnt >= (amt ? amt : 1)) return 1; return 0; @@ -1681,21 +1694,21 @@ static int copy_from_read_buf(struct tty_struct *tty, retval = 0; spin_lock_irqsave(&tty->read_lock, flags); - n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail); + n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail); n = min(*nr, n); spin_unlock_irqrestore(&tty->read_lock, flags); if (n) { - retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n); + retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n); n -= retval; is_eof = n == 1 && - tty->read_buf[tty->read_tail] == EOF_CHAR(tty); - tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n, + ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty); + tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n, ldata->icanon); spin_lock_irqsave(&tty->read_lock, flags); - tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); - tty->read_cnt -= n; + ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1); + ldata->read_cnt -= n; /* Turn single EOF into zero-length read */ - if (L_EXTPROC(tty) && ldata->icanon && is_eof && !tty->read_cnt) + if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt) n = 0; spin_unlock_irqrestore(&tty->read_lock, flags); *b += n; @@ -1878,22 +1891,22 @@ do_it_again: if (ldata->icanon && !L_EXTPROC(tty)) { /* N.B. avoid overrun if nr == 0 */ spin_lock_irqsave(&tty->read_lock, flags); - while (nr && tty->read_cnt) { + while (nr && ldata->read_cnt) { int eol; - eol = test_and_clear_bit(tty->read_tail, + eol = test_and_clear_bit(ldata->read_tail, ldata->read_flags); - c = tty->read_buf[tty->read_tail]; - tty->read_tail = ((tty->read_tail+1) & + c = ldata->read_buf[ldata->read_tail]; + ldata->read_tail = ((ldata->read_tail+1) & (N_TTY_BUF_SIZE-1)); - tty->read_cnt--; + ldata->read_cnt--; if (eol) { /* this test should be redundant: * we shouldn't be reading data if * canon_data is 0 */ - if (--tty->canon_data < 0) - tty->canon_data = 0; + if (--ldata->canon_data < 0) + ldata->canon_data = 0; } spin_unlock_irqrestore(&tty->read_lock, flags); @@ -2111,15 +2124,15 @@ static unsigned long inq_canon(struct tty_struct *tty) struct n_tty_data *ldata = tty->disc_data; int nr, head, tail; - if (!tty->canon_data) + if (!ldata->canon_data) return 0; - head = tty->canon_head; - tail = tty->read_tail; + head = ldata->canon_head; + tail = ldata->read_tail; nr = (head - tail) & (N_TTY_BUF_SIZE-1); /* Skip EOF-chars.. */ while (head != tail) { if (test_bit(tail, ldata->read_flags) && - tty->read_buf[tail] == __DISABLED_CHAR) + ldata->read_buf[tail] == __DISABLED_CHAR) nr--; tail = (tail+1) & (N_TTY_BUF_SIZE-1); } @@ -2129,6 +2142,7 @@ static unsigned long inq_canon(struct tty_struct *tty) static int n_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { + struct n_tty_data *ldata = tty->disc_data; int retval; switch (cmd) { @@ -2136,7 +2150,7 @@ static int n_tty_ioctl(struct tty_struct *tty, struct file *file, return put_user(tty_chars_in_buffer(tty), (int __user *) arg); case TIOCINQ: /* FIXME: Locking */ - retval = tty->read_cnt; + retval = ldata->read_cnt; if (L_ICANON(tty)) retval = inq_canon(tty); return put_user(retval, (unsigned int __user *) arg); diff --git a/include/linux/tty.h b/include/linux/tty.h index 2161e6b5a94c..226cf20e0150 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -272,16 +272,6 @@ struct tty_struct { */ unsigned char closing:1; unsigned short minimum_to_wake; - char *read_buf; - int read_head; - int read_tail; - int read_cnt; - unsigned char *echo_buf; - unsigned int echo_pos; - unsigned int echo_cnt; - int canon_data; - unsigned long canon_head; - unsigned int canon_column; struct mutex atomic_read_lock; struct mutex atomic_write_lock; struct mutex output_lock; -- cgit v1.2.3 From bddc7152f68bc1e0ee1f55a8055e33531f384101 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:42 +0200 Subject: TTY: move ldisc data from tty_struct: locks atomic_write_lock is not n_tty specific, so move it up in the tty_struct. And since these are the last ones to move, remove also the comment saying there are some ldisc' members. There are none now. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 123 ++++++++++++++++++++++++++++++--------------------- drivers/tty/tty_io.c | 4 -- include/linux/tty.h | 11 +---- 3 files changed, 73 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 4794537a50ff..0a6fcda96152 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -96,6 +96,11 @@ struct n_tty_data { int canon_data; unsigned long canon_head; unsigned int canon_column; + + struct mutex atomic_read_lock; + struct mutex output_lock; + struct mutex echo_lock; + spinlock_t read_lock; }; static inline int tty_put_user(struct tty_struct *tty, unsigned char x, @@ -171,14 +176,15 @@ static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) static void put_tty_queue(unsigned char c, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; unsigned long flags; /* * The problem of stomping on the buffers ends here. * Why didn't anyone see this one coming? --AJK */ - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); put_tty_queue_nolock(c, tty); - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); } /** @@ -212,13 +218,13 @@ static void reset_buffer_flags(struct tty_struct *tty) struct n_tty_data *ldata = tty->disc_data; unsigned long flags; - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); ldata->read_head = ldata->read_tail = ldata->read_cnt = 0; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); - mutex_lock(&tty->echo_lock); + mutex_lock(&ldata->echo_lock); ldata->echo_pos = ldata->echo_cnt = ldata->echo_overrun = 0; - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); ldata->canon_head = ldata->canon_data = ldata->erasing = 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); @@ -270,7 +276,7 @@ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) unsigned long flags; ssize_t n = 0; - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); if (!ldata->icanon) { n = ldata->read_cnt; } else if (ldata->canon_data) { @@ -278,7 +284,7 @@ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) ldata->canon_head - ldata->read_tail : ldata->canon_head + (N_TTY_BUF_SIZE - ldata->read_tail); } - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); return n; } @@ -408,14 +414,15 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) static int process_output(unsigned char c, struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; int space, retval; - mutex_lock(&tty->output_lock); + mutex_lock(&ldata->output_lock); space = tty_write_room(tty); retval = do_output_char(c, tty, space); - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->output_lock); if (retval < 0) return -1; else @@ -449,11 +456,11 @@ static ssize_t process_output_block(struct tty_struct *tty, int i; const unsigned char *cp; - mutex_lock(&tty->output_lock); + mutex_lock(&ldata->output_lock); space = tty_write_room(tty); if (!space) { - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->output_lock); return 0; } if (nr > space) @@ -496,7 +503,7 @@ static ssize_t process_output_block(struct tty_struct *tty, break_out: i = tty->ops->write(tty, buf, i); - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->output_lock); return i; } @@ -536,8 +543,8 @@ static void process_echoes(struct tty_struct *tty) if (!ldata->echo_cnt) return; - mutex_lock(&tty->output_lock); - mutex_lock(&tty->echo_lock); + mutex_lock(&ldata->output_lock); + mutex_lock(&ldata->echo_lock); space = tty_write_room(tty); @@ -682,8 +689,8 @@ static void process_echoes(struct tty_struct *tty) ldata->echo_overrun = 0; } - mutex_unlock(&tty->echo_lock); - mutex_unlock(&tty->output_lock); + mutex_unlock(&ldata->echo_lock); + mutex_unlock(&ldata->output_lock); if (tty->ops->flush_chars) tty->ops->flush_chars(tty); @@ -748,12 +755,14 @@ static void add_echo_byte(unsigned char c, struct tty_struct *tty) static void echo_move_back_col(struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); add_echo_byte(ECHO_OP_START, tty); add_echo_byte(ECHO_OP_MOVE_BACK_COL, tty); - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -768,12 +777,14 @@ static void echo_move_back_col(struct tty_struct *tty) static void echo_set_canon_col(struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); add_echo_byte(ECHO_OP_START, tty); add_echo_byte(ECHO_OP_SET_CANON_COL, tty); - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -796,7 +807,9 @@ static void echo_set_canon_col(struct tty_struct *tty) static void echo_erase_tab(unsigned int num_chars, int after_tab, struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); add_echo_byte(ECHO_OP_START, tty); add_echo_byte(ECHO_OP_ERASE_TAB, tty); @@ -810,7 +823,7 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, add_echo_byte(num_chars, tty); - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -828,7 +841,9 @@ static void echo_erase_tab(unsigned int num_chars, int after_tab, static void echo_char_raw(unsigned char c, struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); if (c == ECHO_OP_START) { add_echo_byte(ECHO_OP_START, tty); @@ -837,7 +852,7 @@ static void echo_char_raw(unsigned char c, struct tty_struct *tty) add_echo_byte(c, tty); } - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -856,7 +871,9 @@ static void echo_char_raw(unsigned char c, struct tty_struct *tty) static void echo_char(unsigned char c, struct tty_struct *tty) { - mutex_lock(&tty->echo_lock); + struct n_tty_data *ldata = tty->disc_data; + + mutex_lock(&ldata->echo_lock); if (c == ECHO_OP_START) { add_echo_byte(ECHO_OP_START, tty); @@ -867,7 +884,7 @@ static void echo_char(unsigned char c, struct tty_struct *tty) add_echo_byte(c, tty); } - mutex_unlock(&tty->echo_lock); + mutex_unlock(&ldata->echo_lock); } /** @@ -914,19 +931,19 @@ static void eraser(unsigned char c, struct tty_struct *tty) kill_type = WERASE; else { if (!L_ECHO(tty)) { - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & (N_TTY_BUF_SIZE - 1)); ldata->read_head = ldata->canon_head; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); return; } if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); ldata->read_cnt -= ((ldata->read_head - ldata->canon_head) & (N_TTY_BUF_SIZE - 1)); ldata->read_head = ldata->canon_head; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); finish_erasing(tty); echo_char(KILL_CHAR(tty), tty); /* Add a newline if ECHOK is on and ECHOKE is off. */ @@ -960,10 +977,10 @@ static void eraser(unsigned char c, struct tty_struct *tty) break; } cnt = (ldata->read_head - head) & (N_TTY_BUF_SIZE-1); - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); ldata->read_head = head; ldata->read_cnt -= cnt; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (L_ECHO(tty)) { if (L_ECHOPRT(tty)) { if (!ldata->erasing) { @@ -1338,12 +1355,12 @@ send_signal: put_tty_queue(c, tty); handle_newline: - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); set_bit(ldata->read_head, ldata->read_flags); put_tty_queue_nolock(c, tty); ldata->canon_head = ldata->read_head; ldata->canon_data++; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); @@ -1417,7 +1434,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, unsigned long cpuflags; if (ldata->real_raw) { - spin_lock_irqsave(&tty->read_lock, cpuflags); + spin_lock_irqsave(&ldata->read_lock, cpuflags); i = min(N_TTY_BUF_SIZE - ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_head); i = min(count, i); @@ -1433,7 +1450,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, memcpy(ldata->read_buf + ldata->read_head, cp, i); ldata->read_head = (ldata->read_head + i) & (N_TTY_BUF_SIZE-1); ldata->read_cnt += i; - spin_unlock_irqrestore(&tty->read_lock, cpuflags); + spin_unlock_irqrestore(&ldata->read_lock, cpuflags); } else { for (i = count, p = cp, f = fp; i; i--, p++) { if (f) @@ -1626,6 +1643,10 @@ static int n_tty_open(struct tty_struct *tty) goto err; ldata->overrun_time = jiffies; + mutex_init(&ldata->atomic_read_lock); + mutex_init(&ldata->output_lock); + mutex_init(&ldata->echo_lock); + spin_lock_init(&ldata->read_lock); /* These are ugly. Currently a malloc failure here can panic */ ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); @@ -1677,7 +1698,7 @@ static inline int input_available_p(struct tty_struct *tty, int amt) * buffer, and once to drain the space from the (physical) beginning of * the buffer to head pointer. * - * Called under the tty->atomic_read_lock sem + * Called under the ldata->atomic_read_lock sem * */ @@ -1693,10 +1714,10 @@ static int copy_from_read_buf(struct tty_struct *tty, bool is_eof; retval = 0; - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); n = min(ldata->read_cnt, N_TTY_BUF_SIZE - ldata->read_tail); n = min(*nr, n); - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (n) { retval = copy_to_user(*b, &ldata->read_buf[ldata->read_tail], n); n -= retval; @@ -1704,13 +1725,13 @@ static int copy_from_read_buf(struct tty_struct *tty, ldata->read_buf[ldata->read_tail] == EOF_CHAR(tty); tty_audit_add_data(tty, &ldata->read_buf[ldata->read_tail], n, ldata->icanon); - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); ldata->read_tail = (ldata->read_tail + n) & (N_TTY_BUF_SIZE-1); ldata->read_cnt -= n; /* Turn single EOF into zero-length read */ if (L_EXTPROC(tty) && ldata->icanon && is_eof && !ldata->read_cnt) n = 0; - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); *b += n; *nr -= n; } @@ -1818,10 +1839,10 @@ do_it_again: * Internal serialization of reads. */ if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&tty->atomic_read_lock)) + if (!mutex_trylock(&ldata->atomic_read_lock)) return -EAGAIN; } else { - if (mutex_lock_interruptible(&tty->atomic_read_lock)) + if (mutex_lock_interruptible(&ldata->atomic_read_lock)) return -ERESTARTSYS; } packet = tty->packet; @@ -1890,7 +1911,7 @@ do_it_again: if (ldata->icanon && !L_EXTPROC(tty)) { /* N.B. avoid overrun if nr == 0 */ - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); while (nr && ldata->read_cnt) { int eol; @@ -1908,25 +1929,25 @@ do_it_again: if (--ldata->canon_data < 0) ldata->canon_data = 0; } - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (!eol || (c != __DISABLED_CHAR)) { if (tty_put_user(tty, c, b++)) { retval = -EFAULT; b--; - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); break; } nr--; } if (eol) { tty_audit_push(tty); - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); break; } - spin_lock_irqsave(&tty->read_lock, flags); + spin_lock_irqsave(&ldata->read_lock, flags); } - spin_unlock_irqrestore(&tty->read_lock, flags); + spin_unlock_irqrestore(&ldata->read_lock, flags); if (retval) break; } else { @@ -1958,7 +1979,7 @@ do_it_again: if (time) timeout = time; } - mutex_unlock(&tty->atomic_read_lock); + mutex_unlock(&ldata->atomic_read_lock); remove_wait_queue(&tty->read_wait, &wait); if (!waitqueue_active(&tty->read_wait)) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 67b024ca16ec..f90b6217b3ba 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2939,11 +2939,7 @@ void initialize_tty_struct(struct tty_struct *tty, init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup); - mutex_init(&tty->atomic_read_lock); mutex_init(&tty->atomic_write_lock); - mutex_init(&tty->output_lock); - mutex_init(&tty->echo_lock); - spin_lock_init(&tty->read_lock); spin_lock_init(&tty->ctrl_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); diff --git a/include/linux/tty.h b/include/linux/tty.h index 226cf20e0150..08787ece3fdc 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -235,6 +235,7 @@ struct tty_struct { struct mutex ldisc_mutex; struct tty_ldisc *ldisc; + struct mutex atomic_write_lock; struct mutex legacy_mutex; struct mutex termios_mutex; spinlock_t ctrl_lock; @@ -265,20 +266,10 @@ struct tty_struct { #define N_TTY_BUF_SIZE 4096 - /* - * The following is data for the N_TTY line discipline. For - * historical reasons, this is included in the tty structure. - * Mostly locked by the BKL. - */ unsigned char closing:1; unsigned short minimum_to_wake; - struct mutex atomic_read_lock; - struct mutex atomic_write_lock; - struct mutex output_lock; - struct mutex echo_lock; unsigned char *write_buf; int write_cnt; - spinlock_t read_lock; /* If the tty has a pending do_SAK, queue it here - akpm */ struct work_struct SAK_work; struct tty_port *port; -- cgit v1.2.3 From 2fc20661e3171d45e8e58a61eb5c6b7d8d614fde Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:44 +0200 Subject: TTY: move TTY_FLUSH* flags to tty_port They are only TTY buffers specific. And the buffers will go to tty_port in the next patches. So to remove the need to have both tty_port and tty_struct at some places, let us move the flags to tty_port. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 18 ++++++++++-------- include/linux/tty.h | 5 +++-- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 8b00f6a34a7d..6f366f257fba 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -134,17 +134,18 @@ static void __tty_buffer_flush(struct tty_struct *tty) void tty_buffer_flush(struct tty_struct *tty) { + struct tty_port *port = tty->port; unsigned long flags; spin_lock_irqsave(&tty->buf.lock, flags); /* If the data is being pushed to the tty layer then we can't process it here. Instead set a flag and the flush_to_ldisc path will process the flush request before it exits */ - if (test_bit(TTY_FLUSHING, &tty->flags)) { - set_bit(TTY_FLUSHPENDING, &tty->flags); + if (test_bit(TTYP_FLUSHING, &port->iflags)) { + set_bit(TTYP_FLUSHPENDING, &port->iflags); spin_unlock_irqrestore(&tty->buf.lock, flags); wait_event(tty->read_wait, - test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); + test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); return; } else __tty_buffer_flush(tty); @@ -450,6 +451,7 @@ static void flush_to_ldisc(struct work_struct *work) { struct tty_struct *tty = container_of(work, struct tty_struct, buf.work); + struct tty_port *port = tty->port; unsigned long flags; struct tty_ldisc *disc; @@ -459,7 +461,7 @@ static void flush_to_ldisc(struct work_struct *work) spin_lock_irqsave(&tty->buf.lock, flags); - if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { + if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { struct tty_buffer *head; while ((head = tty->buf.head) != NULL) { int count; @@ -477,7 +479,7 @@ static void flush_to_ldisc(struct work_struct *work) /* Ldisc or user is trying to flush the buffers we are feeding to the ldisc, stop feeding the line discipline as we want to empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) + if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) break; if (!tty->receive_room) break; @@ -491,14 +493,14 @@ static void flush_to_ldisc(struct work_struct *work) flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); } - clear_bit(TTY_FLUSHING, &tty->flags); + clear_bit(TTYP_FLUSHING, &port->iflags); } /* We may have a deferred request to flush the input buffer, if so pull the chain under the lock and empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { + if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { __tty_buffer_flush(tty); - clear_bit(TTY_FLUSHPENDING, &tty->flags); + clear_bit(TTYP_FLUSHPENDING, &port->iflags); wake_up(&tty->read_wait); } spin_unlock_irqrestore(&tty->buf.lock, flags); diff --git a/include/linux/tty.h b/include/linux/tty.h index 08787ece3fdc..b4b3c568d242 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -197,6 +197,9 @@ struct tty_port { wait_queue_head_t close_wait; /* Close waiters */ wait_queue_head_t delta_msr_wait; /* Modem status change */ unsigned long flags; /* TTY flags ASY_*/ + unsigned long iflags; /* TTYP_ internal flags */ +#define TTYP_FLUSHING 1 /* Flushing to ldisc in progress */ +#define TTYP_FLUSHPENDING 2 /* Queued buffer flush pending */ unsigned char console:1; /* port is a console */ struct mutex mutex; /* Locking */ struct mutex buf_mutex; /* Buffer alloc lock */ @@ -309,8 +312,6 @@ struct tty_file_private { #define TTY_PTY_LOCK 16 /* pty private */ #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ #define TTY_HUPPED 18 /* Post driver->hangup() */ -#define TTY_FLUSHING 19 /* Flushing to ldisc in progress */ -#define TTY_FLUSHPENDING 20 /* Queued buffer flush pending */ #define TTY_HUPPING 21 /* ->hangup() in progress */ #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty)) -- cgit v1.2.3 From 967fab6916681e5ab131fdef1226327b02454f19 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:46 +0200 Subject: TTY: add port -> tty link For that purpose we have to temporarily introduce a second tty back pointer into tty_port. It is because serial layer, and maybe others, still do not use tty_port_tty_set/get. So that we cannot set the tty_port->tty to NULL at will now. Yes, the fix would be to convert whole serial layer and all its users to tty_port_tty_set/get. However we are in the process of removing the need of tty in most of the call sites, so this would lead to a duplicated work. Instead we have now tty_port->itty (internal tty) which will be used only in flush_to_ldisc. For that one it is ensured that itty is valid wherever the work is run. IOW, the work is synchronously cancelled before we set itty to NULL and also before hangup is processed. After we need only tty_port and not tty_struct in most code, this shall be changed to tty_port_tty_set/get and itty removed completely. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 2 ++ drivers/tty/tty_io.c | 3 +++ include/linux/tty.h | 1 + 3 files changed, 6 insertions(+) (limited to 'include') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 2728afe52eea..c32690862671 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -345,6 +345,7 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, tty_port_init(ports[1]); o_tty->port = ports[0]; tty->port = ports[1]; + o_tty->port->itty = o_tty; tty_driver_kref_get(driver); tty->count++; @@ -371,6 +372,7 @@ static void pty_unix98_shutdown(struct tty_struct *tty) static void pty_cleanup(struct tty_struct *tty) { + tty->port->itty = NULL; kfree(tty->port); } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index f90b6217b3ba..202008f38ca3 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1417,6 +1417,8 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n", __func__, tty->driver->name); + tty->port->itty = tty; + /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need @@ -1552,6 +1554,7 @@ static void release_tty(struct tty_struct *tty, int idx) tty->ops->shutdown(tty); tty_free_termios(tty); tty_driver_remove_tty(tty->driver, tty); + tty->port->itty = NULL; if (tty->link) tty_kref_put(tty->link); diff --git a/include/linux/tty.h b/include/linux/tty.h index b4b3c568d242..9be74d649a51 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -189,6 +189,7 @@ struct tty_port_operations { struct tty_port { struct tty_struct *tty; /* Back pointer */ + struct tty_struct *itty; /* internal back ptr */ const struct tty_port_operations *ops; /* Port operations */ spinlock_t lock; /* Lock protecting tty field */ int blocked_open; /* Waiting to open */ -- cgit v1.2.3 From ecbbfd44a08fa80e0d664814efd4c187721b85f6 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 18 Oct 2012 22:26:47 +0200 Subject: TTY: move tty buffers to tty_port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So this is it. The big step why we did all the work over the past kernel releases. Now everything is prepared, so nothing protects us from doing that big step. | | \ \ nnnn/^l | | | | \ / / | | | '-,.__ => \/ ,-` => | '-,.__ | O __.´´) ( .` | O __.´´) ~~~ ~~ `` ~~~ ~~ The buffers are now in the tty_port structure and we can start teaching the buffer helpers (insert char/string, flip etc.) to use tty_port instead of tty_struct all around. Signed-off-by: Jiri Slaby Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 7 +++- drivers/tty/pty.c | 2 +- drivers/tty/tty_buffer.c | 102 ++++++++++++++++++++++++----------------------- drivers/tty/tty_io.c | 2 - drivers/tty/tty_ldisc.c | 10 ++--- drivers/tty/tty_port.c | 2 + include/linux/tty.h | 6 +-- include/linux/tty_flip.h | 2 +- 8 files changed, 70 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 531e539dbfcf..60b076cc4e20 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -149,8 +149,11 @@ static void n_tty_set_room(struct tty_struct *tty) tty->receive_room = left; /* Did this open up the receive buffer? We may need to flip */ - if (left && !old_left) - schedule_work(&tty->buf.work); + if (left && !old_left) { + WARN_RATELIMIT(tty->port->itty == NULL, + "scheduling with invalid itty"); + schedule_work(&tty->port->buf.work); + } } static void put_tty_queue_nolock(unsigned char c, struct n_tty_data *ldata) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index c32690862671..4219f040adb8 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -93,7 +93,7 @@ static void pty_unthrottle(struct tty_struct *tty) static int pty_space(struct tty_struct *to) { - int n = 8192 - to->buf.memory_used; + int n = 8192 - to->port->buf.memory_used; if (n < 0) return 0; return n; diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index ddd74d41cbb2..06725f5cc819 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -27,9 +27,9 @@ * Locking: none */ -void tty_buffer_free_all(struct tty_struct *tty) +void tty_buffer_free_all(struct tty_port *port) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; struct tty_buffer *thead; while ((thead = buf->head) != NULL) { @@ -56,11 +56,11 @@ void tty_buffer_free_all(struct tty_struct *tty) * Locking: Caller must hold tty->buf.lock */ -static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) +static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) { struct tty_buffer *p; - if (tty->buf.memory_used + size > 65536) + if (port->buf.memory_used + size > 65536) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) @@ -72,7 +72,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) p->read = 0; p->char_buf_ptr = (char *)(p->data); p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; - tty->buf.memory_used += size; + port->buf.memory_used += size; return p; } @@ -87,9 +87,9 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) * Locking: Caller must hold tty->buf.lock */ -static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) +static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; /* Dumb strategy for now - should keep some stats */ buf->memory_used -= b->size; @@ -114,14 +114,14 @@ static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) * Locking: Caller must hold tty->buf.lock */ -static void __tty_buffer_flush(struct tty_struct *tty) +static void __tty_buffer_flush(struct tty_port *port) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; struct tty_buffer *thead; while ((thead = buf->head) != NULL) { buf->head = thead->next; - tty_buffer_free(tty, thead); + tty_buffer_free(port, thead); } buf->tail = NULL; } @@ -140,7 +140,7 @@ static void __tty_buffer_flush(struct tty_struct *tty) void tty_buffer_flush(struct tty_struct *tty) { struct tty_port *port = tty->port; - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); @@ -155,7 +155,7 @@ void tty_buffer_flush(struct tty_struct *tty) test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); return; } else - __tty_buffer_flush(tty); + __tty_buffer_flush(port); spin_unlock_irqrestore(&buf->lock, flags); } @@ -171,9 +171,9 @@ void tty_buffer_flush(struct tty_struct *tty) * Locking: Caller must hold tty->buf.lock */ -static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) +static struct tty_buffer *tty_buffer_find(struct tty_port *port, size_t size) { - struct tty_buffer **tbh = &tty->buf.free; + struct tty_buffer **tbh = &port->buf.free; while ((*tbh) != NULL) { struct tty_buffer *t = *tbh; if (t->size >= size) { @@ -182,14 +182,14 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) t->used = 0; t->commit = 0; t->read = 0; - tty->buf.memory_used += t->size; + port->buf.memory_used += t->size; return t; } tbh = &((*tbh)->next); } /* Round the buffer size out */ size = (size + 0xFF) & ~0xFF; - return tty_buffer_alloc(tty, size); + return tty_buffer_alloc(port, size); /* Should possibly check if this fails for the largest buffer we have queued and recycle that ? */ } @@ -200,11 +200,11 @@ static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) * * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. - * Locking: Caller must hold tty->buf.lock + * Locking: Caller must hold port->buf.lock */ -static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) +static int __tty_buffer_request_room(struct tty_port *port, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; struct tty_buffer *b, *n; int left; /* OPTIMISATION: We could keep a per tty "zero" sized buffer to @@ -218,7 +218,7 @@ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) if (left < size) { /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(tty, size)) != NULL) { + if ((n = tty_buffer_find(port, size)) != NULL) { if (b != NULL) { b->next = n; b->commit = b->used; @@ -241,16 +241,17 @@ static int __tty_buffer_request_room(struct tty_struct *tty, size_t size) * Make at least size bytes of linear space available for the tty * buffer. If we fail return the size we managed to find. * - * Locking: Takes tty->buf.lock + * Locking: Takes port->buf.lock */ int tty_buffer_request_room(struct tty_struct *tty, size_t size) { + struct tty_port *port = tty->port; unsigned long flags; int length; - spin_lock_irqsave(&tty->buf.lock, flags); - length = __tty_buffer_request_room(tty, size); - spin_unlock_irqrestore(&tty->buf.lock, flags); + spin_lock_irqsave(&port->buf.lock, flags); + length = __tty_buffer_request_room(port, size); + spin_unlock_irqrestore(&port->buf.lock, flags); return length; } EXPORT_SYMBOL_GPL(tty_buffer_request_room); @@ -265,13 +266,13 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); * Queue a series of bytes to the tty buffering. All the characters * passed are marked with the supplied flag. Returns the number added. * - * Locking: Called functions may take tty->buf.lock + * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, const unsigned char *chars, char flag, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); @@ -280,7 +281,7 @@ int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, flags); - space = __tty_buffer_request_room(tty, goal); + space = __tty_buffer_request_room(tty->port, goal); tb = buf->tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { @@ -311,13 +312,13 @@ EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); * the flags array indicates the status of the character. Returns the * number added. * - * Locking: Called functions may take tty->buf.lock + * Locking: Called functions may take port->buf.lock */ int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *chars, const char *flags, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int copied = 0; do { int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); @@ -326,7 +327,7 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, __flags); - space = __tty_buffer_request_room(tty, goal); + space = __tty_buffer_request_room(tty->port, goal); tb = buf->tail; /* If there is no space then tb may be NULL */ if (unlikely(space == 0)) { @@ -357,12 +358,12 @@ EXPORT_SYMBOL(tty_insert_flip_string_flags); * Note that this function can only be used when the low_latency flag * is unset. Otherwise the workqueue won't be flushed. * - * Locking: Takes tty->buf.lock + * Locking: Takes port->buf.lock */ void tty_schedule_flip(struct tty_struct *tty) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); @@ -385,19 +386,19 @@ EXPORT_SYMBOL(tty_schedule_flip); * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! * - * Locking: May call functions taking tty->buf.lock + * Locking: May call functions taking port->buf.lock */ int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, - size_t size) + size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int space; unsigned long flags; struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, flags); - space = __tty_buffer_request_room(tty, size); + space = __tty_buffer_request_room(tty->port, size); tb = buf->tail; if (likely(space)) { @@ -423,19 +424,19 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string); * that need their own block copy routines into the buffer. There is no * guarantee the buffer is a DMA target! * - * Locking: May call functions taking tty->buf.lock + * Locking: May call functions taking port->buf.lock */ int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; int space; unsigned long __flags; struct tty_buffer *tb; spin_lock_irqsave(&buf->lock, __flags); - space = __tty_buffer_request_room(tty, size); + space = __tty_buffer_request_room(tty->port, size); tb = buf->tail; if (likely(space)) { @@ -464,13 +465,16 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); static void flush_to_ldisc(struct work_struct *work) { - struct tty_struct *tty = - container_of(work, struct tty_struct, buf.work); - struct tty_port *port = tty->port; - struct tty_bufhead *buf = &tty->buf; + struct tty_port *port = container_of(work, struct tty_port, buf.work); + struct tty_bufhead *buf = &port->buf; + struct tty_struct *tty; unsigned long flags; struct tty_ldisc *disc; + tty = port->itty; + if (WARN_RATELIMIT(tty == NULL, "tty is NULL")) + return; + disc = tty_ldisc_ref(tty); if (disc == NULL) /* !TTY_LDISC */ return; @@ -489,7 +493,7 @@ static void flush_to_ldisc(struct work_struct *work) if (head->next == NULL) break; buf->head = head->next; - tty_buffer_free(tty, head); + tty_buffer_free(port, head); continue; } /* Ldisc or user is trying to flush the buffers @@ -515,7 +519,7 @@ static void flush_to_ldisc(struct work_struct *work) /* We may have a deferred request to flush the input buffer, if so pull the chain under the lock and empty the queue */ if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { - __tty_buffer_flush(tty); + __tty_buffer_flush(port); clear_bit(TTYP_FLUSHPENDING, &port->iflags); wake_up(&tty->read_wait); } @@ -535,7 +539,7 @@ static void flush_to_ldisc(struct work_struct *work) void tty_flush_to_ldisc(struct tty_struct *tty) { if (!tty->low_latency) - flush_work(&tty->buf.work); + flush_work(&tty->port->buf.work); } /** @@ -553,7 +557,7 @@ void tty_flush_to_ldisc(struct tty_struct *tty) void tty_flip_buffer_push(struct tty_struct *tty) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &tty->port->buf; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); @@ -578,9 +582,9 @@ EXPORT_SYMBOL(tty_flip_buffer_push); * Locking: none */ -void tty_buffer_init(struct tty_struct *tty) +void tty_buffer_init(struct tty_port *port) { - struct tty_bufhead *buf = &tty->buf; + struct tty_bufhead *buf = &port->buf; spin_lock_init(&buf->lock); buf->head = NULL; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 202008f38ca3..a3eba7f359ed 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -186,7 +186,6 @@ void free_tty_struct(struct tty_struct *tty) if (tty->dev) put_device(tty->dev); kfree(tty->write_buf); - tty_buffer_free_all(tty); tty->magic = 0xDEADDEAD; kfree(tty); } @@ -2935,7 +2934,6 @@ void initialize_tty_struct(struct tty_struct *tty, tty_ldisc_init(tty); tty->session = NULL; tty->pgrp = NULL; - tty_buffer_init(tty); mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); mutex_init(&tty->ldisc_mutex); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 47e3968df10b..f4e6754525dc 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -512,7 +512,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) static int tty_ldisc_halt(struct tty_struct *tty) { clear_bit(TTY_LDISC, &tty->flags); - return cancel_work_sync(&tty->buf.work); + return cancel_work_sync(&tty->port->buf.work); } /** @@ -525,7 +525,7 @@ static void tty_ldisc_flush_works(struct tty_struct *tty) { flush_work(&tty->hangup_work); flush_work(&tty->SAK_work); - flush_work(&tty->buf.work); + flush_work(&tty->port->buf.work); } /** @@ -704,9 +704,9 @@ enable: /* Restart the work queue in case no characters kick it off. Safe if already running */ if (work) - schedule_work(&tty->buf.work); + schedule_work(&tty->port->buf.work); if (o_work) - schedule_work(&o_tty->buf.work); + schedule_work(&o_tty->port->buf.work); mutex_unlock(&tty->ldisc_mutex); tty_unlock(tty); return retval; @@ -817,7 +817,7 @@ void tty_ldisc_hangup(struct tty_struct *tty) */ clear_bit(TTY_LDISC, &tty->flags); tty_unlock(tty); - cancel_work_sync(&tty->buf.work); + cancel_work_sync(&tty->port->buf.work); mutex_unlock(&tty->ldisc_mutex); retry: tty_lock(tty); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index d7bdd8d0c23f..416b42f7c346 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -21,6 +21,7 @@ void tty_port_init(struct tty_port *port) { memset(port, 0, sizeof(*port)); + tty_buffer_init(port); init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); init_waitqueue_head(&port->delta_msr_wait); @@ -126,6 +127,7 @@ static void tty_port_destructor(struct kref *kref) struct tty_port *port = container_of(kref, struct tty_port, kref); if (port->xmit_buf) free_page((unsigned long)port->xmit_buf); + tty_buffer_free_all(port); if (port->ops->destruct) port->ops->destruct(port); else diff --git a/include/linux/tty.h b/include/linux/tty.h index 9be74d649a51..d7ff88fb8967 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -188,6 +188,7 @@ struct tty_port_operations { }; struct tty_port { + struct tty_bufhead buf; /* Locked internally */ struct tty_struct *tty; /* Back pointer */ struct tty_struct *itty; /* internal back ptr */ const struct tty_port_operations *ops; /* Port operations */ @@ -259,7 +260,6 @@ struct tty_struct { struct tty_struct *link; struct fasync_struct *fasync; - struct tty_bufhead buf; /* Locked internally */ int alt_speed; /* For magic substitution of 38400 bps */ wait_queue_head_t write_wait; wait_queue_head_t read_wait; @@ -388,9 +388,9 @@ extern void disassociate_ctty(int priv); extern void no_tty(void); extern void tty_flip_buffer_push(struct tty_struct *tty); extern void tty_flush_to_ldisc(struct tty_struct *tty); -extern void tty_buffer_free_all(struct tty_struct *tty); +extern void tty_buffer_free_all(struct tty_port *port); extern void tty_buffer_flush(struct tty_struct *tty); -extern void tty_buffer_init(struct tty_struct *tty); +extern void tty_buffer_init(struct tty_port *port); extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 9239d033a0a3..2002344ed36a 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -11,7 +11,7 @@ void tty_schedule_flip(struct tty_struct *tty); static inline int tty_insert_flip_char(struct tty_struct *tty, unsigned char ch, char flag) { - struct tty_buffer *tb = tty->buf.tail; + struct tty_buffer *tb = tty->port->buf.tail; if (tb && tb->used < tb->size) { tb->flag_buf_ptr[tb->used] = flag; tb->char_buf_ptr[tb->used++] = ch; -- cgit v1.2.3 From c6298038bcfc20710430a4ad069bb1f3f069997c Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Wed, 24 Oct 2012 23:43:21 +0400 Subject: tty, ioctls -- Add new ioctl definitions for tty flags fetching This patch defines new ioctl codes TIOCGPKT, TIOCGPTLCK, TIOCGEXCL for fetching pty's packet mode and locking state, and exclusive mode of tty. [ No real handlers for the codes though, this will be addressed in another patch for easier review and bisectability ] Signed-off-by: Cyrill Gorcunov CC: Alan Cox CC: "H. Peter Anvin" CC: Pavel Emelyanov CC: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- arch/alpha/include/asm/ioctls.h | 3 +++ arch/mips/include/uapi/asm/ioctls.h | 3 +++ arch/parisc/include/uapi/asm/ioctls.h | 3 +++ arch/powerpc/include/uapi/asm/ioctls.h | 3 +++ arch/sh/include/uapi/asm/ioctls.h | 3 +++ arch/sparc/include/uapi/asm/ioctls.h | 3 +++ arch/xtensa/include/uapi/asm/ioctls.h | 3 +++ fs/compat_ioctl.c | 3 +++ include/uapi/asm-generic/ioctls.h | 3 +++ 9 files changed, 27 insertions(+) (limited to 'include') diff --git a/arch/alpha/include/asm/ioctls.h b/arch/alpha/include/asm/ioctls.h index 80e1cee90f1f..92c557be49fc 100644 --- a/arch/alpha/include/asm/ioctls.h +++ b/arch/alpha/include/asm/ioctls.h @@ -95,6 +95,9 @@ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/mips/include/uapi/asm/ioctls.h b/arch/mips/include/uapi/asm/ioctls.h index 92403c3d6007..addd56b60694 100644 --- a/arch/mips/include/uapi/asm/ioctls.h +++ b/arch/mips/include/uapi/asm/ioctls.h @@ -86,6 +86,9 @@ #define TIOCGDEV _IOR('T', 0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T', 0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ /* I hope the range from 0x5480 on is free ... */ #define TIOCSCTTY 0x5480 /* become controlling tty */ diff --git a/arch/parisc/include/uapi/asm/ioctls.h b/arch/parisc/include/uapi/asm/ioctls.h index 054ec06f9e23..66719c38a36b 100644 --- a/arch/parisc/include/uapi/asm/ioctls.h +++ b/arch/parisc/include/uapi/asm/ioctls.h @@ -55,6 +55,9 @@ #define TIOCGDEV _IOR('T',0x32, int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 diff --git a/arch/powerpc/include/uapi/asm/ioctls.h b/arch/powerpc/include/uapi/asm/ioctls.h index e9b78870aaab..49a25796a61a 100644 --- a/arch/powerpc/include/uapi/asm/ioctls.h +++ b/arch/powerpc/include/uapi/asm/ioctls.h @@ -97,6 +97,9 @@ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/sh/include/uapi/asm/ioctls.h b/arch/sh/include/uapi/asm/ioctls.h index a6769f352bf6..342241079760 100644 --- a/arch/sh/include/uapi/asm/ioctls.h +++ b/arch/sh/include/uapi/asm/ioctls.h @@ -88,6 +88,9 @@ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP _IO('T', 0x37) +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */ #define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */ diff --git a/arch/sparc/include/uapi/asm/ioctls.h b/arch/sparc/include/uapi/asm/ioctls.h index 9155f7041d44..897d1723fa14 100644 --- a/arch/sparc/include/uapi/asm/ioctls.h +++ b/arch/sparc/include/uapi/asm/ioctls.h @@ -21,6 +21,9 @@ #define TCSETSF2 _IOW('T', 15, struct termios2) #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCVHANGUP _IO('T', 0x37) +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ /* Note that all the ioctls that are not available in Linux have a * double underscore on the front to: a) avoid some programs to diff --git a/arch/xtensa/include/uapi/asm/ioctls.h b/arch/xtensa/include/uapi/asm/ioctls.h index 2aa4cd9f0cec..b4cb1100c0fb 100644 --- a/arch/xtensa/include/uapi/asm/ioctls.h +++ b/arch/xtensa/include/uapi/asm/ioctls.h @@ -101,6 +101,9 @@ #define TIOCGDEV _IOR('T',0x32, unsigned int) /* Get primary device node of /dev/console */ #define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCVHANGUP _IO('T', 0x37) +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define TIOCSERCONFIG _IO('T', 83) #define TIOCSERGWILD _IOR('T', 84, int) diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index f5054025f9da..89cf6014a967 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -842,6 +842,9 @@ COMPATIBLE_IOCTL(TIOCGDEV) COMPATIBLE_IOCTL(TIOCCBRK) COMPATIBLE_IOCTL(TIOCGSID) COMPATIBLE_IOCTL(TIOCGICOUNT) +COMPATIBLE_IOCTL(TIOCGPKT) +COMPATIBLE_IOCTL(TIOCGPTLCK) +COMPATIBLE_IOCTL(TIOCGEXCL) /* Little t */ COMPATIBLE_IOCTL(TIOCGETD) COMPATIBLE_IOCTL(TIOCSETD) diff --git a/include/uapi/asm-generic/ioctls.h b/include/uapi/asm-generic/ioctls.h index 199975fac395..143dacbb7d9a 100644 --- a/include/uapi/asm-generic/ioctls.h +++ b/include/uapi/asm-generic/ioctls.h @@ -74,6 +74,9 @@ #define TCSETXW 0x5435 #define TIOCSIG _IOW('T', 0x36, int) /* pty: generate signal */ #define TIOCVHANGUP 0x5437 +#define TIOCGPKT _IOR('T', 0x38, int) /* Get packet mode state */ +#define TIOCGPTLCK _IOR('T', 0x39, int) /* Get Pty lock state */ +#define TIOCGEXCL _IOR('T', 0x40, int) /* Get exclusive mode state */ #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 -- cgit v1.2.3 From 2ac4ad2a1468123f6bb439a547880a9c0d302e0a Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Sat, 27 Oct 2012 12:47:12 +0530 Subject: serial/arc-uart: Add new driver Driver for non-standard on-chip UART, instantiated in the ARC (Synopsys) FPGA Boards such as ARCAngel4/ML50x Signed-off-by: Vineet Gupta Reviewed-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 23 ++ drivers/tty/serial/Makefile | 1 + drivers/tty/serial/arc_uart.c | 746 +++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 2 + 4 files changed, 772 insertions(+) create mode 100644 drivers/tty/serial/arc_uart.c (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2a53be5f010d..b1768012ed21 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1423,4 +1423,27 @@ config SERIAL_EFM32_UART_CONSOLE depends on SERIAL_EFM32_UART=y select SERIAL_CORE_CONSOLE +config SERIAL_ARC + tristate "ARC UART driver support" + select SERIAL_CORE + help + Driver for on-chip UART for ARC(Synopsys) for the legacy + FPGA Boards (ML50x/ARCAngel4) + +config SERIAL_ARC_CONSOLE + bool "Console on ARC UART" + depends on SERIAL_ARC=y + select SERIAL_CORE_CONSOLE + help + Enable system Console on ARC UART + +config SERIAL_ARC_NR_PORTS + int "Number of ARC UART ports" + depends on SERIAL_ARC + range 1 3 + default "1" + help + Set this to the number of serial ports you want the driver + to support. + endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 4f694dafa719..df1b998c436b 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -82,3 +82,4 @@ obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o +obj-$(CONFIG_SERIAL_ARC) += arc_uart.o diff --git a/drivers/tty/serial/arc_uart.c b/drivers/tty/serial/arc_uart.c new file mode 100644 index 000000000000..e9c61d1b1c79 --- /dev/null +++ b/drivers/tty/serial/arc_uart.c @@ -0,0 +1,746 @@ +/* + * ARC On-Chip(fpga) UART Driver + * + * Copyright (C) 2010-2012 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * vineetg: July 10th 2012 + * -Decoupled the driver from arch/arc + * +Using platform_get_resource() for irq/membase (thx to bfin_uart.c) + * +Using early_platform_xxx() for early console (thx to mach-shmobile/xxx) + * + * Vineetg: Aug 21st 2010 + * -Is uart_tx_stopped() not done in tty write path as it has already been + * taken care of, in serial core + * + * Vineetg: Aug 18th 2010 + * -New Serial Core based ARC UART driver + * -Derived largely from blackfin driver albiet with some major tweaks + * + * TODO: + * -check if sysreq works + */ + +#if defined(CONFIG_SERIAL_ARC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/************************************* + * ARC UART Hardware Specs + ************************************/ +#define ARC_UART_TX_FIFO_SIZE 1 + +/* + * UART Register set (this is not a Standards Compliant IP) + * Also each reg is Word aligned, but only 8 bits wide + */ +#define R_ID0 0 +#define R_ID1 4 +#define R_ID2 8 +#define R_ID3 12 +#define R_DATA 16 +#define R_STS 20 +#define R_BAUDL 24 +#define R_BAUDH 28 + +/* Bits for UART Status Reg (R/W) */ +#define RXIENB 0x04 /* Receive Interrupt Enable */ +#define TXIENB 0x40 /* Transmit Interrupt Enable */ + +#define RXEMPTY 0x20 /* Receive FIFO Empty: No char receivede */ +#define TXEMPTY 0x80 /* Transmit FIFO Empty, thus char can be written into */ + +#define RXFULL 0x08 /* Receive FIFO full */ +#define RXFULL1 0x10 /* Receive FIFO has space for 1 char (tot space=4) */ + +#define RXFERR 0x01 /* Frame Error: Stop Bit not detected */ +#define RXOERR 0x02 /* OverFlow Err: Char recv but RXFULL still set */ + +/* Uart bit fiddling helpers: lowest level */ +#define RBASE(uart, reg) (uart->port.membase + reg) +#define UART_REG_SET(u, r, v) writeb((v), RBASE(u, r)) +#define UART_REG_GET(u, r) readb(RBASE(u, r)) + +#define UART_REG_OR(u, r, v) UART_REG_SET(u, r, UART_REG_GET(u, r) | (v)) +#define UART_REG_CLR(u, r, v) UART_REG_SET(u, r, UART_REG_GET(u, r) & ~(v)) + +/* Uart bit fiddling helpers: API level */ +#define UART_SET_DATA(uart, val) UART_REG_SET(uart, R_DATA, val) +#define UART_GET_DATA(uart) UART_REG_GET(uart, R_DATA) + +#define UART_SET_BAUDH(uart, val) UART_REG_SET(uart, R_BAUDH, val) +#define UART_SET_BAUDL(uart, val) UART_REG_SET(uart, R_BAUDL, val) + +#define UART_CLR_STATUS(uart, val) UART_REG_CLR(uart, R_STS, val) +#define UART_GET_STATUS(uart) UART_REG_GET(uart, R_STS) + +#define UART_ALL_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, RXIENB|TXIENB) +#define UART_RX_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, RXIENB) +#define UART_TX_IRQ_DISABLE(uart) UART_REG_CLR(uart, R_STS, TXIENB) + +#define UART_ALL_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, RXIENB|TXIENB) +#define UART_RX_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, RXIENB) +#define UART_TX_IRQ_ENABLE(uart) UART_REG_OR(uart, R_STS, TXIENB) + +#define ARC_SERIAL_DEV_NAME "ttyARC" + +struct arc_uart_port { + struct uart_port port; + unsigned long baud; + int is_emulated; /* H/w vs. Instruction Set Simulator */ +}; + +#define to_arc_port(uport) container_of(uport, struct arc_uart_port, port) + +static struct arc_uart_port arc_uart_ports[CONFIG_SERIAL_ARC_NR_PORTS]; + +#ifdef CONFIG_SERIAL_ARC_CONSOLE +static struct console arc_console; +#endif + +#define DRIVER_NAME "arc-uart" + +static struct uart_driver arc_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = ARC_SERIAL_DEV_NAME, + .major = 0, + .minor = 0, + .nr = CONFIG_SERIAL_ARC_NR_PORTS, +#ifdef CONFIG_SERIAL_ARC_CONSOLE + .cons = &arc_console, +#endif +}; + +static void arc_serial_stop_rx(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + UART_RX_IRQ_DISABLE(uart); +} + +static void arc_serial_stop_tx(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + while (!(UART_GET_STATUS(uart) & TXEMPTY)) + cpu_relax(); + + UART_TX_IRQ_DISABLE(uart); +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int arc_serial_tx_empty(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + unsigned int stat; + + stat = UART_GET_STATUS(uart); + if (stat & TXEMPTY) + return TIOCSER_TEMT; + + return 0; +} + +/* + * Driver internal routine, used by both tty(serial core) as well as tx-isr + * -Called under spinlock in either cases + * -also tty->stopped / tty->hw_stopped has already been checked + * = by uart_start( ) before calling us + * = tx_ist checks that too before calling + */ +static void arc_serial_tx_chars(struct arc_uart_port *uart) +{ + struct circ_buf *xmit = &uart->port.state->xmit; + int sent = 0; + unsigned char ch; + + if (unlikely(uart->port.x_char)) { + UART_SET_DATA(uart, uart->port.x_char); + uart->port.icount.tx++; + uart->port.x_char = 0; + sent = 1; + } else if (xmit->tail != xmit->head) { /* TODO: uart_circ_empty */ + ch = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + uart->port.icount.tx++; + while (!(UART_GET_STATUS(uart) & TXEMPTY)) + cpu_relax(); + UART_SET_DATA(uart, ch); + sent = 1; + } + + /* + * If num chars in xmit buffer are too few, ask tty layer for more. + * By Hard ISR to schedule processing in software interrupt part + */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uart->port); + + if (sent) + UART_TX_IRQ_ENABLE(uart); +} + +/* + * port is locked and interrupts are disabled + * uart_start( ) calls us under the port spinlock irqsave + */ +static void arc_serial_start_tx(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + arc_serial_tx_chars(uart); +} + +static void arc_serial_rx_chars(struct arc_uart_port *uart) +{ + struct tty_struct *tty = tty_port_tty_get(&uart->port.state->port); + unsigned int status, ch, flg = 0; + + if (!tty) + return; + + /* + * UART has 4 deep RX-FIFO. Driver's recongnition of this fact + * is very subtle. Here's how ... + * Upon getting a RX-Intr, such that RX-EMPTY=0, meaning data available, + * driver reads the DATA Reg and keeps doing that in a loop, until + * RX-EMPTY=1. Multiple chars being avail, with a single Interrupt, + * before RX-EMPTY=0, implies some sort of buffering going on in the + * controller, which is indeed the Rx-FIFO. + */ + while (!((status = UART_GET_STATUS(uart)) & RXEMPTY)) { + + ch = UART_GET_DATA(uart); + uart->port.icount.rx++; + + if (unlikely(status & (RXOERR | RXFERR))) { + if (status & RXOERR) { + uart->port.icount.overrun++; + flg = TTY_OVERRUN; + UART_CLR_STATUS(uart, RXOERR); + } + + if (status & RXFERR) { + uart->port.icount.frame++; + flg = TTY_FRAME; + UART_CLR_STATUS(uart, RXFERR); + } + } else + flg = TTY_NORMAL; + + if (unlikely(uart_handle_sysrq_char(&uart->port, ch))) + goto done; + + uart_insert_char(&uart->port, status, RXOERR, ch, flg); + +done: + tty_flip_buffer_push(tty); + } + + tty_kref_put(tty); +} + +/* + * A note on the Interrupt handling state machine of this driver + * + * kernel printk writes funnel thru the console driver framework and in order + * to keep things simple as well as efficient, it writes to UART in polled + * mode, in one shot, and exits. + * + * OTOH, Userland output (via tty layer), uses interrupt based writes as there + * can be undeterministic delay between char writes. + * + * Thus Rx-interrupts are always enabled, while tx-interrupts are by default + * disabled. + * + * When tty has some data to send out, serial core calls driver's start_tx + * which + * -checks-if-tty-buffer-has-char-to-send + * -writes-data-to-uart + * -enable-tx-intr + * + * Once data bits are pushed out, controller raises the Tx-room-avail-Interrupt. + * The first thing Tx ISR does is disable further Tx interrupts (as this could + * be the last char to send, before settling down into the quiet polled mode). + * It then calls the exact routine used by tty layer write to send out any + * more char in tty buffer. In case of sending, it re-enables Tx-intr. In case + * of no data, it remains disabled. + * This is how the transmit state machine is dynamically switched on/off + */ + +static irqreturn_t arc_serial_isr(int irq, void *dev_id) +{ + struct arc_uart_port *uart = dev_id; + unsigned int status; + + status = UART_GET_STATUS(uart); + + /* + * Single IRQ for both Rx (data available) Tx (room available) Interrupt + * notifications from the UART Controller. + * To demultiplex between the two, we check the relevant bits + */ + if ((status & RXIENB) && !(status & RXEMPTY)) { + + /* already in ISR, no need of xx_irqsave */ + spin_lock(&uart->port.lock); + arc_serial_rx_chars(uart); + spin_unlock(&uart->port.lock); + } + + if ((status & TXIENB) && (status & TXEMPTY)) { + + /* Unconditionally disable further Tx-Interrupts. + * will be enabled by tx_chars() if needed. + */ + UART_TX_IRQ_DISABLE(uart); + + spin_lock(&uart->port.lock); + + if (!uart_tx_stopped(&uart->port)) + arc_serial_tx_chars(uart); + + spin_unlock(&uart->port.lock); + } + + return IRQ_HANDLED; +} + +static unsigned int arc_serial_get_mctrl(struct uart_port *port) +{ + /* + * Pretend we have a Modem status reg and following bits are + * always set, to satify the serial core state machine + * (DSR) Data Set Ready + * (CTS) Clear To Send + * (CAR) Carrier Detect + */ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void arc_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* MCR not present */ +} + +/* Enable Modem Status Interrupts */ + +static void arc_serial_enable_ms(struct uart_port *port) +{ + /* MSR not present */ +} + +static void arc_serial_break_ctl(struct uart_port *port, int break_state) +{ + /* ARC UART doesn't support sending Break signal */ +} + +static int arc_serial_startup(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + /* Before we hook up the ISR, Disable all UART Interrupts */ + UART_ALL_IRQ_DISABLE(uart); + + if (request_irq(uart->port.irq, arc_serial_isr, 0, "arc uart rx-tx", + uart)) { + dev_warn(uart->port.dev, "Unable to attach ARC UART intr\n"); + return -EBUSY; + } + + UART_RX_IRQ_ENABLE(uart); /* Only Rx IRQ enabled to begin with */ + + return 0; +} + +/* This is not really needed */ +static void arc_serial_shutdown(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + free_irq(uart->port.irq, uart); +} + +static void +arc_serial_set_termios(struct uart_port *port, struct ktermios *new, + struct ktermios *old) +{ + struct arc_uart_port *uart = to_arc_port(port); + unsigned int baud, uartl, uarth, hw_val; + unsigned long flags; + + /* + * Use the generic handler so that any specially encoded baud rates + * such as SPD_xx flags or "%B0" can be handled + * Max Baud I suppose will not be more than current 115K * 4 + * Formula for ARC UART is: hw-val = ((CLK/(BAUD*4)) -1) + * spread over two 8-bit registers + */ + baud = uart_get_baud_rate(port, new, old, 0, 460800); + + hw_val = port->uartclk / (uart->baud * 4) - 1; + uartl = hw_val & 0xFF; + uarth = (hw_val >> 8) & 0xFF; + + /* + * UART ISS(Instruction Set simulator) emulation has a subtle bug: + * A existing value of Baudh = 0 is used as a indication to startup + * it's internal state machine. + * Thus if baudh is set to 0, 2 times, it chokes. + * This happens with BAUD=115200 and the formaula above + * Until that is fixed, when running on ISS, we will set baudh to !0 + */ + if (uart->is_emulated) + uarth = 1; + + spin_lock_irqsave(&port->lock, flags); + + UART_ALL_IRQ_DISABLE(uart); + + UART_SET_BAUDL(uart, uartl); + UART_SET_BAUDH(uart, uarth); + + UART_RX_IRQ_ENABLE(uart); + + /* + * UART doesn't support Parity/Hardware Flow Control; + * Only supports 8N1 character size + */ + new->c_cflag &= ~(CMSPAR|CRTSCTS|CSIZE); + new->c_cflag |= CS8; + + if (old) + tty_termios_copy_hw(new, old); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(new)) + tty_termios_encode_baud_rate(new, baud, baud); + + uart_update_timeout(port, new->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *arc_serial_type(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + + return uart->port.type == PORT_ARC ? DRIVER_NAME : NULL; +} + +static void arc_serial_release_port(struct uart_port *port) +{ +} + +static int arc_serial_request_port(struct uart_port *port) +{ + return 0; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + */ +static int +arc_serial_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (port->type != PORT_UNKNOWN && ser->type != PORT_ARC) + return -EINVAL; + + return 0; +} + +/* + * Configure/autoconfigure the port. + */ +static void arc_serial_config_port(struct uart_port *port, int flags) +{ + struct arc_uart_port *uart = to_arc_port(port); + + if (flags & UART_CONFIG_TYPE) + uart->port.type = PORT_ARC; +} + +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_ARC_CONSOLE) + +static void arc_serial_poll_putchar(struct uart_port *port, unsigned char chr) +{ + struct arc_uart_port *uart = to_arc_port(port); + + while (!(UART_GET_STATUS(uart) & TXEMPTY)) + cpu_relax(); + + UART_SET_DATA(uart, chr); +} +#endif + +#ifdef CONFIG_CONSOLE_POLL +static int arc_serial_poll_getchar(struct uart_port *port) +{ + struct arc_uart_port *uart = to_arc_port(port); + unsigned char chr; + + while (!(UART_GET_STATUS(uart) & RXEMPTY)) + cpu_relax(); + + chr = UART_GET_DATA(uart); + return chr; +} +#endif + +static struct uart_ops arc_serial_pops = { + .tx_empty = arc_serial_tx_empty, + .set_mctrl = arc_serial_set_mctrl, + .get_mctrl = arc_serial_get_mctrl, + .stop_tx = arc_serial_stop_tx, + .start_tx = arc_serial_start_tx, + .stop_rx = arc_serial_stop_rx, + .enable_ms = arc_serial_enable_ms, + .break_ctl = arc_serial_break_ctl, + .startup = arc_serial_startup, + .shutdown = arc_serial_shutdown, + .set_termios = arc_serial_set_termios, + .type = arc_serial_type, + .release_port = arc_serial_release_port, + .request_port = arc_serial_request_port, + .config_port = arc_serial_config_port, + .verify_port = arc_serial_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char = arc_serial_poll_putchar, + .poll_get_char = arc_serial_poll_getchar, +#endif +}; + +static int __devinit +arc_uart_init_one(struct platform_device *pdev, struct arc_uart_port *uart) +{ + struct resource *res, *res2; + unsigned long *plat_data; + + if (pdev->id < 0 || pdev->id >= CONFIG_SERIAL_ARC_NR_PORTS) { + dev_err(&pdev->dev, "Wrong uart platform device id.\n"); + return -ENOENT; + } + + plat_data = ((unsigned long *)(pdev->dev.platform_data)); + uart->baud = plat_data[0]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res2) + return -ENODEV; + + uart->port.mapbase = res->start; + uart->port.membase = ioremap_nocache(res->start, resource_size(res)); + if (!uart->port.membase) + /* No point of dev_err since UART itself is hosed here */ + return -ENXIO; + + uart->port.irq = res2->start; + uart->port.dev = &pdev->dev; + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF; + uart->port.line = pdev->id; + uart->port.ops = &arc_serial_pops; + + uart->port.uartclk = plat_data[1]; + uart->port.fifosize = ARC_UART_TX_FIFO_SIZE; + + /* + * uart_insert_char( ) uses it in decideding whether to ignore a + * char or not. Explicitly setting it here, removes the subtelty + */ + uart->port.ignore_status_mask = 0; + + /* Real Hardware vs. emulated to work around a bug */ + uart->is_emulated = !!plat_data[2]; + + return 0; +} + +#ifdef CONFIG_SERIAL_ARC_CONSOLE + +static int __devinit arc_serial_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= CONFIG_SERIAL_ARC_NR_PORTS) + return -ENODEV; + + /* + * The uart port backing the console (e.g. ttyARC1) might not have been + * init yet. If so, defer the console setup to after the port. + */ + port = &arc_uart_ports[co->index].port; + if (!port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + /* + * Serial core will call port->ops->set_termios( ) + * which will set the baud reg + */ + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static void arc_serial_console_putchar(struct uart_port *port, int ch) +{ + arc_serial_poll_putchar(port, (unsigned char)ch); +} + +/* + * Interrupts are disabled on entering + */ +static void arc_serial_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &arc_uart_ports[co->index].port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + uart_console_write(port, s, count, arc_serial_console_putchar); + spin_unlock_irqrestore(&port->lock, flags); +} + +static struct console arc_console = { + .name = ARC_SERIAL_DEV_NAME, + .write = arc_serial_console_write, + .device = uart_console_device, + .setup = arc_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &arc_uart_driver +}; + +static __init void early_serial_write(struct console *con, const char *s, + unsigned int n) +{ + struct uart_port *port = &arc_uart_ports[con->index].port; + unsigned int i; + + for (i = 0; i < n; i++, s++) { + if (*s == '\n') + arc_serial_poll_putchar(port, '\r'); + arc_serial_poll_putchar(port, *s); + } +} + +static struct __initdata console arc_early_serial_console = { + .name = "early_ARCuart", + .write = early_serial_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1 +}; + +static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev) +{ + arc_early_serial_console.index = pdev->id; + + arc_uart_init_one(pdev, &arc_uart_ports[pdev->id]); + + arc_serial_console_setup(&arc_early_serial_console, NULL); + + register_console(&arc_early_serial_console); + return 0; +} +#else +static int __devinit arc_serial_probe_earlyprintk(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif /* CONFIG_SERIAL_ARC_CONSOLE */ + +static int __devinit arc_serial_probe(struct platform_device *pdev) +{ + struct arc_uart_port *uart; + int rc; + + if (is_early_platform_device(pdev)) + return arc_serial_probe_earlyprintk(pdev); + + uart = &arc_uart_ports[pdev->id]; + rc = arc_uart_init_one(pdev, uart); + if (rc) + return rc; + + return uart_add_one_port(&arc_uart_driver, &uart->port); +} + +static int __devexit arc_serial_remove(struct platform_device *pdev) +{ + /* This will never be called */ + return 0; +} + +static struct platform_driver arc_platform_driver = { + .probe = arc_serial_probe, + .remove = __devexit_p(arc_serial_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +#ifdef CONFIG_SERIAL_ARC_CONSOLE +/* + * Register an early platform driver of "earlyprintk" class. + * ARCH platform code installs the driver and probes the early devices + * The installation could rely on user specifying earlyprintk=xyx in cmd line + * or it could be done independently, for all "earlyprintk" class drivers. + * [see arch/arc/plat-arcfpga/platform.c] + */ +early_platform_init("earlyprintk", &arc_platform_driver); + +#endif /* CONFIG_SERIAL_ARC_CONSOLE */ + +static int __init arc_serial_init(void) +{ + int ret; + + ret = uart_register_driver(&arc_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&arc_platform_driver); + if (ret) + uart_unregister_driver(&arc_uart_driver); + + return ret; +} + +static void __exit arc_serial_exit(void) +{ + platform_driver_unregister(&arc_platform_driver); + uart_unregister_driver(&arc_uart_driver); +} + +module_init(arc_serial_init); +module_exit(arc_serial_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("plat-arcfpga/uart"); +MODULE_AUTHOR("Vineet Gupta"); +MODULE_DESCRIPTION("ARC(Synopsys) On-Chip(fpga) serial driver"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 7e1ab20adc03..ebcc73f0418a 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -215,5 +215,7 @@ /* Energy Micro efm32 SoC */ #define PORT_EFMUART 100 +/* ARC (Synopsys) on-chip UART */ +#define PORT_ARC 101 #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v1.2.3 From de274bfe0fc81def6ddb8a17020a9a4b56477cc4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 15 Nov 2012 09:49:54 +0100 Subject: TTY: introduce tty_port_destroy After commit "TTY: move tty buffers to tty_port", the tty buffers are not freed in some drivers. This is because tty_port_destructor is not called whenever a tty_port is freed. This was an assumption I counted with but was unfortunately untrue. Those using refcounting are safe now, but for those which do not we introduce a function to be called right before the tty_port is freed by the drivers. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_port.c | 16 +++++++++++++++- include/linux/tty.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index fdc42c2d565f..b7ff59d3db88 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -122,12 +122,26 @@ void tty_port_free_xmit_buf(struct tty_port *port) } EXPORT_SYMBOL(tty_port_free_xmit_buf); +/** + * tty_port_destroy -- destroy inited port + * @port: tty port to be doestroyed + * + * When a port was initialized using tty_port_init, one has to destroy the + * port by this function. Either indirectly by using tty_port refcounting + * (tty_port_put) or directly if refcounting is not used. + */ +void tty_port_destroy(struct tty_port *port) +{ + tty_buffer_free_all(port); +} +EXPORT_SYMBOL(tty_port_destroy); + static void tty_port_destructor(struct kref *kref) { struct tty_port *port = container_of(kref, struct tty_port, kref); if (port->xmit_buf) free_page((unsigned long)port->xmit_buf); - tty_buffer_free_all(port); + tty_port_destroy(port); if (port->ops && port->ops->destruct) port->ops->destruct(port); else diff --git a/include/linux/tty.h b/include/linux/tty.h index d7ff88fb8967..8db1b569c37a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -455,6 +455,7 @@ extern struct device *tty_port_register_device_attr(struct tty_port *port, const struct attribute_group **attr_grp); extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); +extern void tty_port_destroy(struct tty_port *port); extern void tty_port_put(struct tty_port *port); static inline struct tty_port *tty_port_get(struct tty_port *port) -- cgit v1.2.3 From ed71871bed7198ca4aa6a79b7a93b73ad6408e98 Mon Sep 17 00:00:00 2001 From: Noam Camus Date: Fri, 16 Nov 2012 07:03:05 +0200 Subject: tty/8250_early: Turn serial_in/serial_out into weak symbols. Allows overriding default methods serial_in/serial_out. In such platform specific replacement it is possible to use other regshift, biased register offset, any other manipulation that is not covered with common default methods. Overriding default methods may be useful for platforms which got serial peripheral with registers represented in big endian. In this situation and assuming that 32 bit operations / alignment is required then it may be useful to swab words before/after accessing the serial registers. Signed-off-by: Noam Camus Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_early.c | 42 ++++++++++++++++++------------------ include/linux/serial_8250.h | 2 ++ 2 files changed, 23 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index 843a150ba105..f53a7db4350d 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -48,7 +48,7 @@ struct early_serial8250_device { static struct early_serial8250_device early_device; -static unsigned int __init serial_in(struct uart_port *port, int offset) +unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset) { switch (port->iotype) { case UPIO_MEM: @@ -62,7 +62,7 @@ static unsigned int __init serial_in(struct uart_port *port, int offset) } } -static void __init serial_out(struct uart_port *port, int offset, int value) +void __weak __init serial8250_early_out(struct uart_port *port, int offset, int value) { switch (port->iotype) { case UPIO_MEM: @@ -84,7 +84,7 @@ static void __init wait_for_xmitr(struct uart_port *port) unsigned int status; for (;;) { - status = serial_in(port, UART_LSR); + status = serial8250_early_in(port, UART_LSR); if ((status & BOTH_EMPTY) == BOTH_EMPTY) return; cpu_relax(); @@ -94,7 +94,7 @@ static void __init wait_for_xmitr(struct uart_port *port) static void __init serial_putc(struct uart_port *port, int c) { wait_for_xmitr(port); - serial_out(port, UART_TX, c); + serial8250_early_out(port, UART_TX, c); } static void __init early_serial8250_write(struct console *console, @@ -104,14 +104,14 @@ static void __init early_serial8250_write(struct console *console, unsigned int ier; /* Save the IER and disable interrupts */ - ier = serial_in(port, UART_IER); - serial_out(port, UART_IER, 0); + ier = serial8250_early_in(port, UART_IER); + serial8250_early_out(port, UART_IER, 0); uart_console_write(port, s, count, serial_putc); /* Wait for transmitter to become empty and restore the IER */ wait_for_xmitr(port); - serial_out(port, UART_IER, ier); + serial8250_early_out(port, UART_IER, ier); } static unsigned int __init probe_baud(struct uart_port *port) @@ -119,11 +119,11 @@ static unsigned int __init probe_baud(struct uart_port *port) unsigned char lcr, dll, dlm; unsigned int quot; - lcr = serial_in(port, UART_LCR); - serial_out(port, UART_LCR, lcr | UART_LCR_DLAB); - dll = serial_in(port, UART_DLL); - dlm = serial_in(port, UART_DLM); - serial_out(port, UART_LCR, lcr); + lcr = serial8250_early_in(port, UART_LCR); + serial8250_early_out(port, UART_LCR, lcr | UART_LCR_DLAB); + dll = serial8250_early_in(port, UART_DLL); + dlm = serial8250_early_in(port, UART_DLM); + serial8250_early_out(port, UART_LCR, lcr); quot = (dlm << 8) | dll; return (port->uartclk / 16) / quot; @@ -135,17 +135,17 @@ static void __init init_port(struct early_serial8250_device *device) unsigned int divisor; unsigned char c; - serial_out(port, UART_LCR, 0x3); /* 8n1 */ - serial_out(port, UART_IER, 0); /* no interrupt */ - serial_out(port, UART_FCR, 0); /* no fifo */ - serial_out(port, UART_MCR, 0x3); /* DTR + RTS */ + serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */ + serial8250_early_out(port, UART_IER, 0); /* no interrupt */ + serial8250_early_out(port, UART_FCR, 0); /* no fifo */ + serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */ divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); - c = serial_in(port, UART_LCR); - serial_out(port, UART_LCR, c | UART_LCR_DLAB); - serial_out(port, UART_DLL, divisor & 0xff); - serial_out(port, UART_DLM, (divisor >> 8) & 0xff); - serial_out(port, UART_LCR, c & ~UART_LCR_DLAB); + c = serial8250_early_in(port, UART_LCR); + serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB); + serial8250_early_out(port, UART_DLL, divisor & 0xff); + serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff); + serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); } static int __init parse_options(struct early_serial8250_device *device, diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index c174c90fb3fb..c490d20b3fb8 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -105,6 +105,8 @@ extern int early_serial_setup(struct uart_port *port); extern int serial8250_find_port(struct uart_port *p); extern int serial8250_find_port_for_earlycon(void); +extern unsigned int serial8250_early_in(struct uart_port *port, int offset); +extern void serial8250_early_out(struct uart_port *port, int offset, int value); extern int setup_early_serial8250_console(char *cmdline); extern void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old); -- cgit v1.2.3 From dc96efb72054985c0912f831da009a2da4e9f6dd Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Mon, 19 Nov 2012 09:12:04 -0600 Subject: Serial: Add support for new devices: Exar's XR17V35x family of multi-port PCIe UARTs Add support for new devices: Exar's XR17V35x family of multi-port PCIe UARTs. Signed-off-by: Matt Schulte Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.c | 71 ++++++++++++++++++++++++++++ drivers/tty/serial/8250/8250_pci.c | 96 ++++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 3 ++ include/uapi/linux/serial_core.h | 3 +- include/uapi/linux/serial_reg.h | 6 +++ 5 files changed, 178 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c index 2af83a246499..3624df674a31 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c @@ -282,6 +282,15 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, }, + [PORT_XR17V35X] = { + .name = "XR17V35X", + .fifo_size = 256, + .tx_loadsz = 256, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11 | + UART_FCR_T_TRIG_11, + .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR | + UART_CAP_SLEEP, + }, [PORT_LPC3220] = { .name = "LPC3220", .fifo_size = 64, @@ -455,6 +464,7 @@ static void io_serial_out(struct uart_port *p, int offset, int value) } static int serial8250_default_handle_irq(struct uart_port *port); +static int exar_handle_irq(struct uart_port *port); static void set_io_from_upio(struct uart_port *p) { @@ -574,6 +584,18 @@ EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); */ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) { + /* + * Exar UARTs have a SLEEP register that enables or disables + * each UART to enter sleep mode separately. On the XR17V35x the + * register is accessible to each UART at the UART_EXAR_SLEEP + * offset but the UART channel may only write to the corresponding + * bit. + */ + if (p->port.type == PORT_XR17V35X) { + serial_out(p, UART_EXAR_SLEEP, 0xff); + return; + } + if (p->capabilities & UART_CAP_SLEEP) { if (p->capabilities & UART_CAP_EFR) { serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B); @@ -881,6 +903,27 @@ static void autoconfig_16550a(struct uart_8250_port *up) up->port.type = PORT_16550A; up->capabilities |= UART_CAP_FIFO; + /* + * XR17V35x UARTs have an extra divisor register, DLD + * that gets enabled with when DLAB is set which will + * cause the device to incorrectly match and assign + * port type to PORT_16650. The EFR for this UART is + * found at offset 0x09. Instead check the Deice ID (DVID) + * register for a 2, 4 or 8 port UART. + */ + status1 = serial_in(up, UART_EXAR_DVID); + if (status1 == 0x82 || status1 == 0x84 || status1 == 0x88) { + if (up->port.flags & UPF_EXAR_EFR) { + DEBUG_AUTOCONF("Exar XR17V35x "); + up->port.type = PORT_XR17V35X; + up->capabilities |= UART_CAP_AFE | UART_CAP_EFR | + UART_CAP_SLEEP; + + return; + } + + } + /* * Check for presence of the EFR when DLAB is set. * Only ST16C650V1 UARTs pass this test. @@ -1515,6 +1558,30 @@ static int serial8250_default_handle_irq(struct uart_port *port) return serial8250_handle_irq(port, iir); } +/* + * These Exar UARTs have an extra interrupt indicator that could + * fire for a few unimplemented interrupts. One of which is a + * wakeup event when coming out of sleep. Put this here just + * to be on the safe side that these interrupts don't go unhandled. + */ +static int exar_handle_irq(struct uart_port *port) +{ + unsigned char int0, int1, int2, int3; + unsigned int iir = serial_port_in(port, UART_IIR); + int ret; + + ret = serial8250_handle_irq(port, iir); + + if (port->type == PORT_XR17V35X) { + int0 = serial_port_in(port, 0x80); + int1 = serial_port_in(port, 0x81); + int2 = serial_port_in(port, 0x82); + int3 = serial_port_in(port, 0x83); + } + + return ret; +} + /* * This is the serial driver's interrupt routine. * @@ -2614,6 +2681,10 @@ static void serial8250_config_port(struct uart_port *port, int flags) serial8250_release_rsa_resource(up); if (port->type == PORT_UNKNOWN) serial8250_release_std_resource(up); + + /* Fixme: probably not the best place for this */ + if (port->type == PORT_XR17V35X) + port->handle_irq = exar_handle_irq; } static int diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 97058c1d7d45..2285d3283b3b 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1164,6 +1164,39 @@ pci_xr17c154_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +static int +pci_xr17v35x_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + u8 __iomem *p; + + p = pci_ioremap_bar(priv->dev, 0); + + port->port.flags |= UPF_EXAR_EFR; + + /* + * Setup Multipurpose Input/Output pins. + */ + if (idx == 0) { + writeb(0x00, p + 0x8f); /*MPIOINT[7:0]*/ + writeb(0x00, p + 0x90); /*MPIOLVL[7:0]*/ + writeb(0x00, p + 0x91); /*MPIO3T[7:0]*/ + writeb(0x00, p + 0x92); /*MPIOINV[7:0]*/ + writeb(0x00, p + 0x93); /*MPIOSEL[7:0]*/ + writeb(0x00, p + 0x94); /*MPIOOD[7:0]*/ + writeb(0x00, p + 0x95); /*MPIOINT[15:8]*/ + writeb(0x00, p + 0x96); /*MPIOLVL[15:8]*/ + writeb(0x00, p + 0x97); /*MPIO3T[15:8]*/ + writeb(0x00, p + 0x98); /*MPIOINV[15:8]*/ + writeb(0x00, p + 0x99); /*MPIOSEL[15:8]*/ + writeb(0x00, p + 0x9a); /*MPIOOD[15:8]*/ + } + iounmap(p); + + return pci_default_setup(priv, board, port, idx); +} + static int pci_wch_ch353_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1622,6 +1655,27 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_xr17c154_setup, }, + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17V352, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17V354, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, + { + .vendor = PCI_VENDOR_ID_EXAR, + .device = PCI_DEVICE_ID_EXAR_XR17V358, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, /* * Xircom cards */ @@ -1962,6 +2016,9 @@ enum pci_board_num_t { pbn_exar_XR17C152, pbn_exar_XR17C154, pbn_exar_XR17C158, + pbn_exar_XR17V352, + pbn_exar_XR17V354, + pbn_exar_XR17V358, pbn_exar_ibm_saturn, pbn_pasemi_1682M, pbn_ni8430_2, @@ -2580,6 +2637,30 @@ static struct pciserial_board pci_boards[] = { .base_baud = 921600, .uart_offset = 0x200, }, + [pbn_exar_XR17V352] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 7812500, + .uart_offset = 0x400, + .reg_shift = 0, + .first_offset = 0, + }, + [pbn_exar_XR17V354] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 7812500, + .uart_offset = 0x400, + .reg_shift = 0, + .first_offset = 0, + }, + [pbn_exar_XR17V358] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 7812500, + .uart_offset = 0x400, + .reg_shift = 0, + .first_offset = 0, + }, [pbn_exar_ibm_saturn] = { .flags = FL_BASE0, .num_ports = 1, @@ -3826,6 +3907,21 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_exar_XR17C158 }, + /* + * Exar Corp. XR17V35[248] Dual/Quad/Octal PCIe UARTs + */ + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V352, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V352 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V354, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V354 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17V358, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V358 }, /* * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 9d36b829533a..0199a7a76fcb 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1985,6 +1985,9 @@ #define PCI_DEVICE_ID_EXAR_XR17C152 0x0152 #define PCI_DEVICE_ID_EXAR_XR17C154 0x0154 #define PCI_DEVICE_ID_EXAR_XR17C158 0x0158 +#define PCI_DEVICE_ID_EXAR_XR17V352 0x0352 +#define PCI_DEVICE_ID_EXAR_XR17V354 0x0354 +#define PCI_DEVICE_ID_EXAR_XR17V358 0x0358 #define PCI_VENDOR_ID_MICROGATE 0x13c0 #define PCI_DEVICE_ID_MICROGATE_USC 0x0010 diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index ebcc73f0418a..78f99d97475b 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -49,7 +49,8 @@ #define PORT_XR17D15X 21 /* Exar XR17D15x UART */ #define PORT_LPC3220 22 /* NXP LPC32xx SoC "Standard" UART */ #define PORT_8250_CIR 23 /* CIR infrared port, has its own driver */ -#define PORT_MAX_8250 23 /* max port ID */ +#define PORT_XR17V35X 24 /* Exar XR17V35x UARTs */ +#define PORT_MAX_8250 24 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 5ed325e88a81..d0b47607b90b 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -367,5 +367,11 @@ #define UART_OMAP_MDR1_CIR_MODE 0x06 /* CIR mode */ #define UART_OMAP_MDR1_DISABLE 0x07 /* Disable (default state) */ +/* + * These are definitions for the XR17V35X and XR17D15X + */ +#define UART_EXAR_SLEEP 0x8b /* Sleep mode */ +#define UART_EXAR_DVID 0x8d /* Device identification */ + #endif /* _LINUX_SERIAL_REG_H */ -- cgit v1.2.3 From d02f81555362e0032080af62154dca00d5ec99e0 Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Tue, 20 Nov 2012 11:21:17 -0600 Subject: Add register definitions used in several Exar PCI/PCIe UARTs Add register definitions used in several Exar PCI/PCIe UARTs Signed-off-by: Matt Schulte Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/serial_reg.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index d0b47607b90b..e6322605b138 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -368,10 +368,22 @@ #define UART_OMAP_MDR1_DISABLE 0x07 /* Disable (default state) */ /* - * These are definitions for the XR17V35X and XR17D15X + * These are definitions for the Exar XR17V35X and XR17(C|D)15X */ +#define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */ #define UART_EXAR_SLEEP 0x8b /* Sleep mode */ #define UART_EXAR_DVID 0x8d /* Device identification */ +#define UART_EXAR_FCTR 0x08 /* Feature Control Register */ +#define UART_FCTR_EXAR_IRDA 0x08 /* IrDa data encode select */ +#define UART_FCTR_EXAR_485 0x10 /* Auto 485 half duplex dir ctl */ +#define UART_FCTR_EXAR_TRGA 0x00 /* FIFO trigger table A */ +#define UART_FCTR_EXAR_TRGB 0x60 /* FIFO trigger table B */ +#define UART_FCTR_EXAR_TRGC 0x80 /* FIFO trigger table C */ +#define UART_FCTR_EXAR_TRGD 0xc0 /* FIFO trigger table D programmable */ + +#define UART_EXAR_TXTRG 0x0a /* Tx FIFO trigger level write-only */ +#define UART_EXAR_RXTRG 0x0b /* Rx FIFO trigger level write-only */ + #endif /* _LINUX_SERIAL_REG_H */ -- cgit v1.2.3 From 14faa8cce88e18ed4daf5c3643f0faa4198863f9 Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Wed, 21 Nov 2012 10:35:15 -0600 Subject: tty/8250 Add support for Commtech's Fastcom Async-335 and Fastcom Async-PCIe cards Add support for Commtech's Fastcom Async-335 and Fastcom Async-PCIe cards Signed-off-by: Matt Schulte Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 163 +++++++++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 2 + 2 files changed, 165 insertions(+) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 089fd43e3784..a795bd971eaa 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1201,6 +1201,55 @@ pci_xr17v35x_setup(struct serial_private *priv, return pci_default_setup(priv, board, port, idx); } +#define PCI_DEVICE_ID_COMMTECH_4222PCI335 0x0004 +#define PCI_DEVICE_ID_COMMTECH_4224PCI335 0x0002 +#define PCI_DEVICE_ID_COMMTECH_2324PCI335 0x000a +#define PCI_DEVICE_ID_COMMTECH_2328PCI335 0x000b + +static int +pci_fastcom335_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + u8 __iomem *p; + + p = pci_ioremap_bar(priv->dev, 0); + if (p == NULL) + return -ENOMEM; + + port->port.flags |= UPF_EXAR_EFR; + + /* + * Setup Multipurpose Input/Output pins. + */ + if (idx == 0) { + switch (priv->dev->device) { + case PCI_DEVICE_ID_COMMTECH_4222PCI335: + case PCI_DEVICE_ID_COMMTECH_4224PCI335: + writeb(0x78, p + 0x90); /* MPIOLVL[7:0] */ + writeb(0x00, p + 0x92); /* MPIOINV[7:0] */ + writeb(0x00, p + 0x93); /* MPIOSEL[7:0] */ + break; + case PCI_DEVICE_ID_COMMTECH_2324PCI335: + case PCI_DEVICE_ID_COMMTECH_2328PCI335: + writeb(0x00, p + 0x90); /* MPIOLVL[7:0] */ + writeb(0xc0, p + 0x92); /* MPIOINV[7:0] */ + writeb(0xc0, p + 0x93); /* MPIOSEL[7:0] */ + break; + } + writeb(0x00, p + 0x8f); /* MPIOINT[7:0] */ + writeb(0x00, p + 0x91); /* MPIO3T[7:0] */ + writeb(0x00, p + 0x94); /* MPIOOD[7:0] */ + } + writeb(0x00, p + UART_EXAR_8XMODE); + writeb(UART_FCTR_EXAR_TRGD, p + UART_EXAR_FCTR); + writeb(32, p + UART_EXAR_TXTRG); + writeb(32, p + UART_EXAR_RXTRG); + iounmap(p); + + return pci_default_setup(priv, board, port, idx); +} + static int pci_wch_ch353_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -1250,6 +1299,10 @@ pci_wch_ch353_setup(struct serial_private *priv, #define PCI_VENDOR_ID_AGESTAR 0x5372 #define PCI_DEVICE_ID_AGESTAR_9375 0x6872 #define PCI_VENDOR_ID_ASIX 0x9710 +#define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0019 +#define PCI_DEVICE_ID_COMMTECH_4224PCIE 0x0020 +#define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 + /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -1845,6 +1898,59 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_asix_setup, }, + /* + * Commtech, Inc. Fastcom adapters + * + */ + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_4222PCI335, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fastcom335_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_4224PCI335, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fastcom335_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_2324PCI335, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fastcom335_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_2328PCI335, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_fastcom335_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_4222PCIE, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_4224PCIE, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, + { + .vendor = PCI_VENDOR_ID_COMMTECH, + .device = PCI_DEVICE_ID_COMMTECH_4228PCIE, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_xr17v35x_setup, + }, /* * Default "match everything" terminator entry */ @@ -1921,6 +2027,10 @@ enum pci_board_num_t { pbn_b0_4_1152000, + pbn_b0_2_1152000_200, + pbn_b0_4_1152000_200, + pbn_b0_8_1152000_200, + pbn_b0_2_1843200, pbn_b0_4_1843200, @@ -2118,6 +2228,27 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 8, }, + [pbn_b0_2_1152000_200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1152000, + .uart_offset = 0x200, + }, + + [pbn_b0_4_1152000_200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1152000, + .uart_offset = 0x200, + }, + + [pbn_b0_8_1152000_200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1152000, + .uart_offset = 0x200, + }, + [pbn_b0_2_1843200] = { .flags = FL_BASE0, .num_ports = 2, @@ -4356,6 +4487,38 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, + /* + * Commtech, Inc. Fastcom adapters + */ + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4222PCI335, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_b0_2_1152000_200 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4224PCI335, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_b0_4_1152000_200 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_2324PCI335, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_b0_4_1152000_200 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_2328PCI335, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_b0_8_1152000_200 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4222PCIE, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V352 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4224PCIE, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V354 }, + { PCI_VENDOR_ID_COMMTECH, PCI_DEVICE_ID_COMMTECH_4228PCIE, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17V358 }, + /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 0199a7a76fcb..0f8447376ddb 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2326,6 +2326,8 @@ #define PCI_VENDOR_ID_TOPSPIN 0x1867 +#define PCI_VENDOR_ID_COMMTECH 0x18f7 + #define PCI_VENDOR_ID_SILAN 0x1904 #define PCI_VENDOR_ID_RENESAS 0x1912 -- cgit v1.2.3