diff options
-rw-r--r-- | inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch | 105 | ||||
-rw-r--r-- | ipc-shm-fix-information-leak-to-user.patch | 30 | ||||
-rw-r--r-- | ipc-zero-struct-memory-for-compat-fns.patch | 73 | ||||
-rw-r--r-- | kernel.spec | 42 | ||||
-rw-r--r-- | netlink-make-nlmsg_find_attr-take-a-const-ptr.patch | 29 | ||||
-rw-r--r-- | posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch | 60 | ||||
-rw-r--r-- | tty-icount-changeover-for-other-main-devices.patch | 982 | ||||
-rw-r--r-- | tty-make-tiocgicount-a-handler.patch | 218 |
8 files changed, 1539 insertions, 0 deletions
diff --git a/inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch b/inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch new file mode 100644 index 0000000..3a2703c --- /dev/null +++ b/inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch @@ -0,0 +1,105 @@ +From 9c6594941166d85bbf7f3bb4a79d01cb3d572eac Mon Sep 17 00:00:00 2001 +From: Nelson Elhage <nelhage@ksplice.com> +Date: Wed, 3 Nov 2010 16:35:41 +0000 +Subject: inet_diag: Make sure we actually run the same bytecode we audited. + +We were using nlmsg_find_attr() to look up the bytecode by attribute when +auditing, but then just using the first attribute when actually running +bytecode. So, if we received a message with two attribute elements, where only +the second had type INET_DIAG_REQ_BYTECODE, we would validate and run different +bytecode strings. + +Fix this by consistently using nlmsg_find_attr everywhere. + +Signed-off-by: Nelson Elhage <nelhage@ksplice.com> +Signed-off-by: Thomas Graf <tgraf@infradead.org> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + net/ipv4/inet_diag.c | 27 ++++++++++++++++----------- + 1 files changed, 16 insertions(+), 11 deletions(-) + +diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c +index a706a47..6fe360f 100644 +--- a/net/ipv4/inet_diag.c ++++ b/net/ipv4/inet_diag.c +@@ -489,9 +489,11 @@ static int inet_csk_diag_dump(struct sock *sk, + { + struct inet_diag_req *r = NLMSG_DATA(cb->nlh); + +- if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { ++ if (nlmsg_attrlen(cb->nlh, sizeof(*r))) { + struct inet_diag_entry entry; +- struct rtattr *bc = (struct rtattr *)(r + 1); ++ const struct nlattr *bc = nlmsg_find_attr(cb->nlh, ++ sizeof(*r), ++ INET_DIAG_REQ_BYTECODE); + struct inet_sock *inet = inet_sk(sk); + + entry.family = sk->sk_family; +@@ -511,7 +513,7 @@ static int inet_csk_diag_dump(struct sock *sk, + entry.dport = ntohs(inet->dport); + entry.userlocks = sk->sk_userlocks; + +- if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry)) ++ if (!inet_diag_bc_run(nla_data(bc), nla_len(bc), &entry)) + return 0; + } + +@@ -526,9 +528,11 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, + { + struct inet_diag_req *r = NLMSG_DATA(cb->nlh); + +- if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { ++ if (nlmsg_attrlen(cb->nlh, sizeof(*r))) { + struct inet_diag_entry entry; +- struct rtattr *bc = (struct rtattr *)(r + 1); ++ const struct nlattr *bc = nlmsg_find_attr(cb->nlh, ++ sizeof(*r), ++ INET_DIAG_REQ_BYTECODE); + + entry.family = tw->tw_family; + #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) +@@ -547,7 +551,7 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, + entry.dport = ntohs(tw->tw_dport); + entry.userlocks = 0; + +- if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry)) ++ if (!inet_diag_bc_run(nla_data(bc), nla_len(bc), &entry)) + return 0; + } + +@@ -617,7 +621,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, + struct inet_diag_req *r = NLMSG_DATA(cb->nlh); + struct inet_connection_sock *icsk = inet_csk(sk); + struct listen_sock *lopt; +- struct rtattr *bc = NULL; ++ const struct nlattr *bc = NULL; + struct inet_sock *inet = inet_sk(sk); + int j, s_j; + int reqnum, s_reqnum; +@@ -637,8 +641,9 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, + if (!lopt || !lopt->qlen) + goto out; + +- if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { +- bc = (struct rtattr *)(r + 1); ++ if (nlmsg_attrlen(cb->nlh, sizeof(*r))) { ++ bc = nlmsg_find_attr(cb->nlh, sizeof(*r), ++ INET_DIAG_REQ_BYTECODE); + entry.sport = inet->num; + entry.userlocks = sk->sk_userlocks; + } +@@ -671,8 +676,8 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, + &ireq->rmt_addr; + entry.dport = ntohs(ireq->rmt_port); + +- if (!inet_diag_bc_run(RTA_DATA(bc), +- RTA_PAYLOAD(bc), &entry)) ++ if (!inet_diag_bc_run(nla_data(bc), ++ nla_len(bc), &entry)) + continue; + } + +-- +1.7.3.2 + diff --git a/ipc-shm-fix-information-leak-to-user.patch b/ipc-shm-fix-information-leak-to-user.patch new file mode 100644 index 0000000..b23ad43 --- /dev/null +++ b/ipc-shm-fix-information-leak-to-user.patch @@ -0,0 +1,30 @@ +From: Vasiliy Kulikov <segooon@gmail.com> +Date: Sat, 30 Oct 2010 14:22:49 +0000 (+0400) +Subject: ipc: shm: fix information leak to userland +X-Git-Tag: v2.6.37-rc1~24 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=3af54c9bd9e6f14f896aac1bb0e8405ae0bc7a44 + +ipc: shm: fix information leak to userland + +The shmid_ds structure is copied to userland with shm_unused{,2,3} +fields unitialized. It leads to leaking of contents of kernel stack +memory. + +Signed-off-by: Vasiliy Kulikov <segooon@gmail.com> +Acked-by: Al Viro <viro@ZenIV.linux.org.uk> +Cc: stable@kernel.org +Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> +--- + +diff --git a/ipc/shm.c b/ipc/shm.c +index fd658a1..7d3bb22 100644 +--- a/ipc/shm.c ++++ b/ipc/shm.c +@@ -479,6 +479,7 @@ static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ + { + struct shmid_ds out; + ++ memset(&out, 0, sizeof(out)); + ipc64_perm_to_ipc_perm(&in->shm_perm, &out.shm_perm); + out.shm_segsz = in->shm_segsz; + out.shm_atime = in->shm_atime; diff --git a/ipc-zero-struct-memory-for-compat-fns.patch b/ipc-zero-struct-memory-for-compat-fns.patch new file mode 100644 index 0000000..b682c7d --- /dev/null +++ b/ipc-zero-struct-memory-for-compat-fns.patch @@ -0,0 +1,73 @@ +From: Dan Rosenberg <drosenberg@vsecurity.com> +Date: Wed, 27 Oct 2010 22:34:17 +0000 (-0700) +Subject: ipc: initialize structure memory to zero for compat functions +X-Git-Tag: v2.6.37-rc1~85^2~50 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=03145beb455cf5c20a761e8451e30b8a74ba58d9 + +ipc: initialize structure memory to zero for compat functions + +This takes care of leaking uninitialized kernel stack memory to +userspace from non-zeroed fields in structs in compat ipc functions. + +Signed-off-by: Dan Rosenberg <drosenberg@vsecurity.com> +Cc: Manfred Spraul <manfred@colorfullife.com> +Cc: Arnd Bergmann <arnd@arndb.de> +Cc: <stable@kernel.org> +Signed-off-by: Andrew Morton <akpm@linux-foundation.org> +Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> +--- + +diff --git a/ipc/compat.c b/ipc/compat.c +index 9dc2c7d..845a287 100644 +--- a/ipc/compat.c ++++ b/ipc/compat.c +@@ -241,6 +241,8 @@ long compat_sys_semctl(int first, int second, int third, void __user *uptr) + struct semid64_ds __user *up64; + int version = compat_ipc_parse_version(&third); + ++ memset(&s64, 0, sizeof(s64)); ++ + if (!uptr) + return -EINVAL; + if (get_user(pad, (u32 __user *) uptr)) +@@ -421,6 +423,8 @@ long compat_sys_msgctl(int first, int second, void __user *uptr) + int version = compat_ipc_parse_version(&second); + void __user *p; + ++ memset(&m64, 0, sizeof(m64)); ++ + switch (second & (~IPC_64)) { + case IPC_INFO: + case IPC_RMID: +@@ -594,6 +598,8 @@ long compat_sys_shmctl(int first, int second, void __user *uptr) + int err, err2; + int version = compat_ipc_parse_version(&second); + ++ memset(&s64, 0, sizeof(s64)); ++ + switch (second & (~IPC_64)) { + case IPC_RMID: + case SHM_LOCK: +diff --git a/ipc/compat_mq.c b/ipc/compat_mq.c +index d8d1e9f..380ea4f 100644 +--- a/ipc/compat_mq.c ++++ b/ipc/compat_mq.c +@@ -53,6 +53,9 @@ asmlinkage long compat_sys_mq_open(const char __user *u_name, + void __user *p = NULL; + if (u_attr && oflag & O_CREAT) { + struct mq_attr attr; ++ ++ memset(&attr, 0, sizeof(attr)); ++ + p = compat_alloc_user_space(sizeof(attr)); + if (get_compat_mq_attr(&attr, u_attr) || + copy_to_user(p, &attr, sizeof(attr))) +@@ -127,6 +130,8 @@ asmlinkage long compat_sys_mq_getsetattr(mqd_t mqdes, + struct mq_attr __user *p = compat_alloc_user_space(2 * sizeof(*p)); + long ret; + ++ memset(&mqstat, 0, sizeof(mqstat)); ++ + if (u_mqstat) { + if (get_compat_mq_attr(&mqstat, u_mqstat) || + copy_to_user(p, &mqstat, sizeof(mqstat))) diff --git a/kernel.spec b/kernel.spec index 81c17fc..d99e791 100644 --- a/kernel.spec +++ b/kernel.spec @@ -837,6 +837,17 @@ Patch14200: net-do-not-check-capable-if-kernel.patch # rhbz#596475 Patch14226: add-support-for-ricoh-e822-sdhci.patch +Patch14300: ipc-zero-struct-memory-for-compat-fns.patch +Patch14301: ipc-shm-fix-information-leak-to-user.patch + +Patch14302: inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch +Patch14307: netlink-make-nlmsg_find_attr-take-a-const-ptr.patch + +Patch14303: posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch + +Patch14305: tty-make-tiocgicount-a-handler.patch +Patch14306: tty-icount-changeover-for-other-main-devices.patch + Patch19997: xen.pvops.pre.patch Patch19998: xen.pvops.patch Patch19999: xen.pvops.post.patch @@ -1543,6 +1554,23 @@ ApplyPatch net-do-not-check-capable-if-kernel.patch # rhbz#596475 ApplyPatch add-support-for-ricoh-e822-sdhci.patch +# rhbz#648658 (CVE-2010-4073) +ApplyPatch ipc-zero-struct-memory-for-compat-fns.patch + +# rhbz#648656 (CVE-2010-4072) +ApplyPatch ipc-shm-fix-information-leak-to-user.patch + +# rhbz#651264 (CVE-2010-3880) +ApplyPatch inet_diag-make-sure-we-run-the-same-bytecode-we-audited.patch +ApplyPatch netlink-make-nlmsg_find_attr-take-a-const-ptr.patch + +# rhbz#656264 +ApplyPatch posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch + +# CVE-2010-4077, CVE-2010-4075 (rhbz#648660, #648663) +ApplyPatch tty-make-tiocgicount-a-handler.patch +ApplyPatch tty-icount-changeover-for-other-main-devices.patch + ApplyPatch xen.pvops.pre.patch ApplyPatch xen.pvops.patch ApplyPatch xen.pvops.post.patch @@ -2199,6 +2227,20 @@ fi %kernel_variant_files -k vmlinux %{with_kdump} kdump %changelog +* Fri Nov 26 2010 Kyle McMartin <kyle@redhat.com> +- netlink-make-nlmsg_find_attr-take-a-const-ptr.patch: quiet build warning + the INET_DIAG fix caused. + +* Fri Nov 26 2010 Kyle McMartin <kyle@redhat.com> +- Plug stack leaks in tty/serial drivers. (#648663, #648660) + +* Tue Nov 23 2010 Kyle McMartin <kyle@redhat.com> +- zero struct memory in ipc compat (CVE-2010-4073) (#648658) +- zero struct memory in ipc shm (CVE-2010-4072) (#648656) +- fix logic error in INET_DIAG bytecode auditing (CVE-2010-3880) (#651264) +- posix-cpu-timers: workaround to suppress the problems with mt exec + (rhbz#656264) + * Tue Nov 23 2010 Michael Young <m.a.young@durham.ac.uk> - update pvops to 2.6.32.26 diff --git a/netlink-make-nlmsg_find_attr-take-a-const-ptr.patch b/netlink-make-nlmsg_find_attr-take-a-const-ptr.patch new file mode 100644 index 0000000..5b75ca4 --- /dev/null +++ b/netlink-make-nlmsg_find_attr-take-a-const-ptr.patch @@ -0,0 +1,29 @@ +From 38f1f0db010ac5b981ae06f1fe2fd64095ebb171 Mon Sep 17 00:00:00 2001 +From: Nelson Elhage <nelhage@ksplice.com> +Date: Wed, 3 Nov 2010 16:35:40 +0000 +Subject: [PATCH] netlink: Make nlmsg_find_attr take a const nlmsghdr*. + +This will let us use it on a nlmsghdr stored inside a netlink_callback. + +Signed-off-by: Nelson Elhage <nelhage@ksplice.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + include/net/netlink.h | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/include/net/netlink.h b/include/net/netlink.h +index a63b219..c344646 100644 +--- a/include/net/netlink.h ++++ b/include/net/netlink.h +@@ -384,7 +384,7 @@ static inline int nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen, + * + * Returns the first attribute which matches the specified type. + */ +-static inline struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, ++static inline struct nlattr *nlmsg_find_attr(const struct nlmsghdr *nlh, + int hdrlen, int attrtype) + { + return nla_find(nlmsg_attrdata(nlh, hdrlen), +-- +1.7.3.2 + diff --git a/posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch b/posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch new file mode 100644 index 0000000..92c2849 --- /dev/null +++ b/posix-cpu-timers-workaround-to-suppress-problems-with-mt-exec.patch @@ -0,0 +1,60 @@ +From 9bdade1bc13e547130d2629291758a579722e5d1 Mon Sep 17 00:00:00 2001 +From: Oleg Nesterov <oleg@redhat.com> +Date: Fri, 5 Nov 2010 16:53:42 +0100 +Subject: posix-cpu-timers: workaround to suppress the problems with mt exec + +posix-cpu-timers.c correctly assumes that the dying process does +posix_cpu_timers_exit_group() and removes all !CPUCLOCK_PERTHREAD +timers from signal->cpu_timers list. + +But, it also assumes that timer->it.cpu.task is always the group +leader, and thus the dead ->task means the dead thread group. + +This is obviously not true after de_thread() changes the leader. +After that almost every posix_cpu_timer_ method has problems. + +It is not simple to fix this bug correctly. First of all, I think +that timer->it.cpu should use struct pid instead of task_struct. +Also, the locking should be reworked completely. In particular, +tasklist_lock should not be used at all. This all needs a lot of +nontrivial and hard-to-test changes. + +Change __exit_signal() to do posix_cpu_timers_exit_group() when +the old leader dies during exec. This is not the fix, just the +temporary hack to hide the problem for 2.6.37 and stable. IOW, +this is obviously wrong but this is what we currently have anyway: +cpu timers do not work after mt exec. + +In theory this change adds another race. The exiting leader can +detach the timers which were attached to the new leader. However, +the window between de_thread() and release_task() is small, we +can pretend that sys_timer_create() was called before de_thread(). + +Signed-off-by: Oleg Nesterov <oleg@redhat.com> +Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> +--- + kernel/exit.c | 8 ++++++++ + 1 files changed, 8 insertions(+), 0 deletions(-) + +diff --git a/kernel/exit.c b/kernel/exit.c +index 45102e9..02b7104 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -92,6 +92,14 @@ static void __exit_signal(struct task_struct *tsk) + posix_cpu_timers_exit_group(tsk); + else { + /* ++ * This can only happen if the caller is de_thread(). ++ * FIXME: this is the temporary hack, we should teach ++ * posix-cpu-timers to handle this case correctly. ++ */ ++ if (unlikely(has_group_leader_pid(tsk))) ++ posix_cpu_timers_exit_group(tsk); ++ ++ /* + * If there is any task waiting for the group exit + * then notify it: + */ +-- +1.7.3.2 + diff --git a/tty-icount-changeover-for-other-main-devices.patch b/tty-icount-changeover-for-other-main-devices.patch new file mode 100644 index 0000000..501aa87 --- /dev/null +++ b/tty-icount-changeover-for-other-main-devices.patch @@ -0,0 +1,982 @@ +From f815dfecf23bfd19d4b5e9f1a660b1f5dbe70472 Mon Sep 17 00:00:00 2001 +From: Alan Cox <alan@linux.intel.com> +Date: Thu, 16 Sep 2010 18:21:52 +0100 +Subject: [PATCH 2/2] tty: icount changeover for other main devices + +Again basically cut and paste + +Convert the main driver set to use the hooks for GICOUNT + +Signed-off-by: Alan Cox <alan@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + arch/ia64/hp/sim/simserial.c | 12 +------ + drivers/char/amiserial.c | 56 ++++++++++++++++------------- + drivers/char/cyclades.c | 49 +++++++++++++------------ + drivers/char/ip2/ip2main.c | 72 ++++++++++++++++++++++--------------- + drivers/char/mxser.c | 62 ++++++++++++++++++-------------- + drivers/char/nozomi.c | 37 +++++++++---------- + drivers/char/pcmcia/synclink_cs.c | 60 ++++++++++++++----------------- + drivers/char/synclink.c | 73 +++++++++++++++++-------------------- + drivers/char/synclink_gt.c | 56 +++++++++++++++-------------- + drivers/char/synclinkmp.c | 61 ++++++++++++++----------------- + drivers/serial/68360serial.c | 51 +++++++++++++------------- + net/bluetooth/rfcomm/tty.c | 4 -- + 12 files changed, 297 insertions(+), 296 deletions(-) + +diff --git a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c +index 2bef526..204f650 100644 +--- a/arch/ia64/hp/sim/simserial.c ++++ b/arch/ia64/hp/sim/simserial.c +@@ -395,7 +395,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, + { + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -433,16 +433,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, + case TIOCMIWAIT: + printk(KERN_INFO "rs_ioctl: TIOCMIWAIT: called\n"); + return 0; +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: +- printk(KERN_INFO "rs_ioctl: TIOCGICOUNT called\n"); +- return 0; +- + case TIOCSERGWILD: + case TIOCSERSWILD: + /* "setserial -W" is called in Debian boot */ +diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c +index 4f8d60c..2918d5d 100644 +--- a/drivers/char/amiserial.c ++++ b/drivers/char/amiserial.c +@@ -1263,6 +1263,36 @@ static int rs_break(struct tty_struct *tty, int break_state) + return 0; + } + ++/* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++static int rs_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ struct async_struct *info = tty->driver_data; ++ struct async_icount cnow; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ cnow = info->state->icount; ++ local_irq_restore(flags); ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ ++ return 0; ++} + + static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +@@ -1332,31 +1362,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, + } + /* NOTREACHED */ + +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: +- local_irq_save(flags); +- cnow = info->state->icount; +- local_irq_restore(flags); +- icount.cts = cnow.cts; +- icount.dsr = cnow.dsr; +- icount.rng = cnow.rng; +- icount.dcd = cnow.dcd; +- icount.rx = cnow.rx; +- icount.tx = cnow.tx; +- icount.frame = cnow.frame; +- icount.overrun = cnow.overrun; +- icount.parity = cnow.parity; +- icount.brk = cnow.brk; +- icount.buf_overrun = cnow.buf_overrun; +- +- if (copy_to_user(argp, &icount, sizeof(icount))) +- return -EFAULT; +- return 0; + case TIOCSERGWILD: + case TIOCSERSWILD: + /* "setserial -W" is called in Debian boot */ +@@ -1949,6 +1954,7 @@ static const struct tty_operations serial_ops = { + .wait_until_sent = rs_wait_until_sent, + .tiocmget = rs_tiocmget, + .tiocmset = rs_tiocmset, ++ .get_icount = rs_get_icount, + .proc_fops = &rs_proc_fops, + }; + +diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c +index 9824b41..2364df8 100644 +--- a/drivers/char/cyclades.c ++++ b/drivers/char/cyclades.c +@@ -2791,29 +2791,6 @@ cy_ioctl(struct tty_struct *tty, struct file *file, + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +- case TIOCGICOUNT: { +- struct serial_icounter_struct sic = { }; +- +- spin_lock_irqsave(&info->card->card_lock, flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->card->card_lock, flags); +- +- sic.cts = cnow.cts; +- sic.dsr = cnow.dsr; +- sic.rng = cnow.rng; +- sic.dcd = cnow.dcd; +- sic.rx = cnow.rx; +- sic.tx = cnow.tx; +- sic.frame = cnow.frame; +- sic.overrun = cnow.overrun; +- sic.parity = cnow.parity; +- sic.brk = cnow.brk; +- sic.buf_overrun = cnow.buf_overrun; +- +- if (copy_to_user(argp, &sic, sizeof(sic))) +- ret_val = -EFAULT; +- break; +- } + default: + ret_val = -ENOIOCTLCMD; + } +@@ -2825,6 +2802,31 @@ cy_ioctl(struct tty_struct *tty, struct file *file, + return ret_val; + } /* cy_ioctl */ + ++static int cy_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *sic) ++{ ++ struct cyclades_port *info = tty->driver_data; ++ struct cyclades_icount cnow; /* Used to snapshot */ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->card->card_lock, flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->card->card_lock, flags); ++ ++ sic->cts = cnow.cts; ++ sic->dsr = cnow.dsr; ++ sic->rng = cnow.rng; ++ sic->dcd = cnow.dcd; ++ sic->rx = cnow.rx; ++ sic->tx = cnow.tx; ++ sic->frame = cnow.frame; ++ sic->overrun = cnow.overrun; ++ sic->parity = cnow.parity; ++ sic->brk = cnow.brk; ++ sic->buf_overrun = cnow.buf_overrun; ++ return 0; ++} ++ + /* + * This routine allows the tty driver to be notified when + * device's termios settings have changed. Note that a +@@ -4086,6 +4088,7 @@ static const struct tty_operations cy_ops = { + .wait_until_sent = cy_wait_until_sent, + .tiocmget = cy_tiocmget, + .tiocmset = cy_tiocmset, ++ .get_icount = cy_get_icount, + .proc_fops = &cyclades_proc_fops, + }; + +diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c +index 911e1da..c0f864c 100644 +--- a/drivers/char/ip2/ip2main.c ++++ b/drivers/char/ip2/ip2main.c +@@ -183,6 +183,8 @@ static void ip2_hangup(PTTY); + static int ip2_tiocmget(struct tty_struct *tty, struct file *file); + static int ip2_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); ++static int ip2_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount); + + static void set_irq(int, int); + static void ip2_interrupt_bh(struct work_struct *work); +@@ -454,6 +456,7 @@ static const struct tty_operations ip2_ops = { + .hangup = ip2_hangup, + .tiocmget = ip2_tiocmget, + .tiocmset = ip2_tiocmset, ++ .get_icount = ip2_get_icount, + .proc_fops = &ip2_proc_fops, + }; + +@@ -2124,7 +2127,6 @@ ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) + i2ChanStrPtr pCh = DevTable[tty->index]; + i2eBordStrPtr pB; + struct async_icount cprev, cnow; /* kernel counter temps */ +- struct serial_icounter_struct __user *p_cuser; + int rc = 0; + unsigned long flags; + void __user *argp = (void __user *)arg; +@@ -2293,34 +2295,6 @@ ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) + break; + + /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for RI where +- * only 0->1 is counted. The controller is quite capable of counting +- * both, but this done to preserve compatibility with the standard +- * serial driver. +- */ +- case TIOCGICOUNT: +- ip2trace (CHANN, ITRC_IOCTL, 11, 1, rc ); +- +- write_lock_irqsave(&pB->read_fifo_spinlock, flags); +- cnow = pCh->icount; +- write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); +- p_cuser = argp; +- rc = put_user(cnow.cts, &p_cuser->cts); +- rc = put_user(cnow.dsr, &p_cuser->dsr); +- rc = put_user(cnow.rng, &p_cuser->rng); +- rc = put_user(cnow.dcd, &p_cuser->dcd); +- rc = put_user(cnow.rx, &p_cuser->rx); +- rc = put_user(cnow.tx, &p_cuser->tx); +- rc = put_user(cnow.frame, &p_cuser->frame); +- rc = put_user(cnow.overrun, &p_cuser->overrun); +- rc = put_user(cnow.parity, &p_cuser->parity); +- rc = put_user(cnow.brk, &p_cuser->brk); +- rc = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); +- break; +- +- /* + * The rest are not supported by this driver. By returning -ENOIOCTLCMD they + * will be passed to the line discipline for it to handle. + */ +@@ -2344,6 +2318,46 @@ ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg ) + return rc; + } + ++static int ip2_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ i2ChanStrPtr pCh = DevTable[tty->index]; ++ i2eBordStrPtr pB; ++ struct async_icount cnow; /* kernel counter temp */ ++ unsigned long flags; ++ ++ if ( pCh == NULL ) ++ return -ENODEV; ++ ++ pB = pCh->pMyBord; ++ ++ /* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for RI where ++ * only 0->1 is counted. The controller is quite capable of counting ++ * both, but this done to preserve compatibility with the standard ++ * serial driver. ++ */ ++ ++ write_lock_irqsave(&pB->read_fifo_spinlock, flags); ++ cnow = pCh->icount; ++ write_unlock_irqrestore(&pB->read_fifo_spinlock, flags); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ return 0; ++} ++ + /******************************************************************************/ + /* Function: GetSerialInfo() */ + /* Parameters: Pointer to channel structure */ +diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c +index d2692d4..65aeae8 100644 +--- a/drivers/char/mxser.c ++++ b/drivers/char/mxser.c +@@ -1700,7 +1700,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, + return 0; + } + +- if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && cmd != TIOCGICOUNT && ++ if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && + test_bit(TTY_IO_ERROR, &tty->flags)) + return -EIO; + +@@ -1730,32 +1730,6 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, + + return wait_event_interruptible(info->port.delta_msr_wait, + mxser_cflags_changed(info, arg, &cnow)); +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: { +- struct serial_icounter_struct icnt = { 0 }; +- spin_lock_irqsave(&info->slock, flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->slock, flags); +- +- icnt.frame = cnow.frame; +- icnt.brk = cnow.brk; +- icnt.overrun = cnow.overrun; +- icnt.buf_overrun = cnow.buf_overrun; +- icnt.parity = cnow.parity; +- icnt.rx = cnow.rx; +- icnt.tx = cnow.tx; +- icnt.cts = cnow.cts; +- icnt.dsr = cnow.dsr; +- icnt.rng = cnow.rng; +- icnt.dcd = cnow.dcd; +- +- return copy_to_user(argp, &icnt, sizeof(icnt)) ? -EFAULT : 0; +- } + case MOXA_HighSpeedOn: + return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp); + case MOXA_SDS_RSTICOUNTER: +@@ -1828,6 +1802,39 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, + return 0; + } + ++ /* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++ ++static int mxser_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++ ++{ ++ struct mxser_port *info = tty->driver_data; ++ struct async_icount cnow; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->slock, flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->slock, flags); ++ ++ icount->frame = cnow.frame; ++ icount->brk = cnow.brk; ++ icount->overrun = cnow.overrun; ++ icount->buf_overrun = cnow.buf_overrun; ++ icount->parity = cnow.parity; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ return 0; ++} ++ + static void mxser_stoprx(struct tty_struct *tty) + { + struct mxser_port *info = tty->driver_data; +@@ -2326,6 +2333,7 @@ static const struct tty_operations mxser_ops = { + .wait_until_sent = mxser_wait_until_sent, + .tiocmget = mxser_tiocmget, + .tiocmset = mxser_tiocmset, ++ .get_icount = mxser_get_icount, + }; + + struct tty_port_operations mxser_port_ops = { +diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c +index 824d67c..1dc493f 100644 +--- a/drivers/char/pcmcia/synclink_cs.c ++++ b/drivers/char/pcmcia/synclink_cs.c +@@ -2220,6 +2220,32 @@ static int mgslpc_break(struct tty_struct *tty, int break_state) + return 0; + } + ++static int mgslpc_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; ++ struct mgsl_icount cnow; /* kernel counter temps */ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->lock,flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->lock,flags); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ ++ return 0; ++} ++ + /* Service an IOCTL request + * + * Arguments: +@@ -2235,11 +2261,7 @@ static int mgslpc_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) + { + MGSLPC_INFO * info = (MGSLPC_INFO *)tty->driver_data; +- int error; +- struct mgsl_icount cnow; /* kernel counter temps */ +- struct serial_icounter_struct __user *p_cuser; /* user space */ + void __user *argp = (void __user *)arg; +- unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgslpc_ioctl %s cmd=%08X\n", __FILE__,__LINE__, +@@ -2249,7 +2271,7 @@ static int mgslpc_ioctl(struct tty_struct *tty, struct file * file, + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -2279,34 +2301,6 @@ static int mgslpc_ioctl(struct tty_struct *tty, struct file * file, + return wait_events(info, argp); + case TIOCMIWAIT: + return modem_input_wait(info,(int)arg); +- case TIOCGICOUNT: +- spin_lock_irqsave(&info->lock,flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->lock,flags); +- p_cuser = argp; +- PUT_USER(error,cnow.cts, &p_cuser->cts); +- if (error) return error; +- PUT_USER(error,cnow.dsr, &p_cuser->dsr); +- if (error) return error; +- PUT_USER(error,cnow.rng, &p_cuser->rng); +- if (error) return error; +- PUT_USER(error,cnow.dcd, &p_cuser->dcd); +- if (error) return error; +- PUT_USER(error,cnow.rx, &p_cuser->rx); +- if (error) return error; +- PUT_USER(error,cnow.tx, &p_cuser->tx); +- if (error) return error; +- PUT_USER(error,cnow.frame, &p_cuser->frame); +- if (error) return error; +- PUT_USER(error,cnow.overrun, &p_cuser->overrun); +- if (error) return error; +- PUT_USER(error,cnow.parity, &p_cuser->parity); +- if (error) return error; +- PUT_USER(error,cnow.brk, &p_cuser->brk); +- if (error) return error; +- PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun); +- if (error) return error; +- return 0; + default: + return -ENOIOCTLCMD; + } +diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c +index 0658fc5..9970aca 100644 +--- a/drivers/char/synclink.c ++++ b/drivers/char/synclink.c +@@ -2920,6 +2920,38 @@ static int mgsl_break(struct tty_struct *tty, int break_state) + + } /* end of mgsl_break() */ + ++/* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++static int msgl_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++ ++{ ++ struct mgsl_struct * info = tty->driver_data; ++ struct mgsl_icount cnow; /* kernel counter temps */ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->irq_spinlock,flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->irq_spinlock,flags); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ return 0; ++} ++ + /* mgsl_ioctl() Service an IOCTL request + * + * Arguments: +@@ -2945,7 +2977,7 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file, + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -2958,11 +2990,7 @@ static int mgsl_ioctl(struct tty_struct *tty, struct file * file, + + static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg) + { +- int error; +- struct mgsl_icount cnow; /* kernel counter temps */ + void __user *argp = (void __user *)arg; +- struct serial_icounter_struct __user *p_cuser; /* user space */ +- unsigned long flags; + + switch (cmd) { + case MGSL_IOCGPARAMS: +@@ -2991,40 +3019,6 @@ static int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigne + case TIOCMIWAIT: + return modem_input_wait(info,(int)arg); + +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: +- spin_lock_irqsave(&info->irq_spinlock,flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->irq_spinlock,flags); +- p_cuser = argp; +- PUT_USER(error,cnow.cts, &p_cuser->cts); +- if (error) return error; +- PUT_USER(error,cnow.dsr, &p_cuser->dsr); +- if (error) return error; +- PUT_USER(error,cnow.rng, &p_cuser->rng); +- if (error) return error; +- PUT_USER(error,cnow.dcd, &p_cuser->dcd); +- if (error) return error; +- PUT_USER(error,cnow.rx, &p_cuser->rx); +- if (error) return error; +- PUT_USER(error,cnow.tx, &p_cuser->tx); +- if (error) return error; +- PUT_USER(error,cnow.frame, &p_cuser->frame); +- if (error) return error; +- PUT_USER(error,cnow.overrun, &p_cuser->overrun); +- if (error) return error; +- PUT_USER(error,cnow.parity, &p_cuser->parity); +- if (error) return error; +- PUT_USER(error,cnow.brk, &p_cuser->brk); +- if (error) return error; +- PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun); +- if (error) return error; +- return 0; + default: + return -ENOIOCTLCMD; + } +@@ -4325,6 +4319,7 @@ static const struct tty_operations mgsl_ops = { + .hangup = mgsl_hangup, + .tiocmget = tiocmget, + .tiocmset = tiocmset, ++ .get_icount = msgl_get_icount, + .proc_fops = &mgsl_proc_fops, + }; + +diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c +index 4561ce2..54fa0ee4 100644 +--- a/drivers/char/synclink_gt.c ++++ b/drivers/char/synclink_gt.c +@@ -1025,9 +1025,6 @@ static int ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) + { + struct slgt_info *info = tty->driver_data; +- struct mgsl_icount cnow; /* kernel counter temps */ +- struct serial_icounter_struct __user *p_cuser; /* user space */ +- unsigned long flags; + void __user *argp = (void __user *)arg; + int ret; + +@@ -1036,7 +1033,7 @@ static int ioctl(struct tty_struct *tty, struct file *file, + DBGINFO(("%s ioctl() cmd=%08X\n", info->device_name, cmd)); + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -1089,25 +1086,6 @@ static int ioctl(struct tty_struct *tty, struct file *file, + case MGSL_IOCWAITGPIO: + ret = wait_gpio(info, argp); + break; +- case TIOCGICOUNT: +- spin_lock_irqsave(&info->lock,flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->lock,flags); +- p_cuser = argp; +- if (put_user(cnow.cts, &p_cuser->cts) || +- put_user(cnow.dsr, &p_cuser->dsr) || +- put_user(cnow.rng, &p_cuser->rng) || +- put_user(cnow.dcd, &p_cuser->dcd) || +- put_user(cnow.rx, &p_cuser->rx) || +- put_user(cnow.tx, &p_cuser->tx) || +- put_user(cnow.frame, &p_cuser->frame) || +- put_user(cnow.overrun, &p_cuser->overrun) || +- put_user(cnow.parity, &p_cuser->parity) || +- put_user(cnow.brk, &p_cuser->brk) || +- put_user(cnow.buf_overrun, &p_cuser->buf_overrun)) +- ret = -EFAULT; +- ret = 0; +- break; + default: + ret = -ENOIOCTLCMD; + } +@@ -1115,6 +1093,33 @@ static int ioctl(struct tty_struct *tty, struct file *file, + return ret; + } + ++static int get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++ ++{ ++ struct slgt_info *info = tty->driver_data; ++ struct mgsl_icount cnow; /* kernel counter temps */ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->lock,flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->lock,flags); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ ++ return 0; ++} ++ + /* + * support for 32 bit ioctl calls on 64 bit systems + */ +@@ -1204,10 +1209,6 @@ static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file, + case MGSL_IOCSGPIO: + case MGSL_IOCGGPIO: + case MGSL_IOCWAITGPIO: +- case TIOCGICOUNT: +- rc = ioctl(tty, file, cmd, (unsigned long)(compat_ptr(arg))); +- break; +- + case MGSL_IOCSTXIDLE: + case MGSL_IOCTXENABLE: + case MGSL_IOCRXENABLE: +@@ -3638,6 +3639,7 @@ static const struct tty_operations ops = { + .hangup = hangup, + .tiocmget = tiocmget, + .tiocmset = tiocmset, ++ .get_icount = get_icount, + .proc_fops = &synclink_gt_proc_fops, + }; + +diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c +index 2b18adc..beffc24 100644 +--- a/drivers/char/synclinkmp.c ++++ b/drivers/char/synclinkmp.c +@@ -1255,10 +1255,6 @@ static int do_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) + { + SLMP_INFO *info = tty->driver_data; +- int error; +- struct mgsl_icount cnow; /* kernel counter temps */ +- struct serial_icounter_struct __user *p_cuser; /* user space */ +- unsigned long flags; + void __user *argp = (void __user *)arg; + + if (debug_level >= DEBUG_LEVEL_INFO) +@@ -1269,7 +1265,7 @@ static int do_ioctl(struct tty_struct *tty, struct file *file, + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && +- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ (cmd != TIOCMIWAIT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -1307,34 +1303,6 @@ static int do_ioctl(struct tty_struct *tty, struct file *file, + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +- case TIOCGICOUNT: +- spin_lock_irqsave(&info->lock,flags); +- cnow = info->icount; +- spin_unlock_irqrestore(&info->lock,flags); +- p_cuser = argp; +- PUT_USER(error,cnow.cts, &p_cuser->cts); +- if (error) return error; +- PUT_USER(error,cnow.dsr, &p_cuser->dsr); +- if (error) return error; +- PUT_USER(error,cnow.rng, &p_cuser->rng); +- if (error) return error; +- PUT_USER(error,cnow.dcd, &p_cuser->dcd); +- if (error) return error; +- PUT_USER(error,cnow.rx, &p_cuser->rx); +- if (error) return error; +- PUT_USER(error,cnow.tx, &p_cuser->tx); +- if (error) return error; +- PUT_USER(error,cnow.frame, &p_cuser->frame); +- if (error) return error; +- PUT_USER(error,cnow.overrun, &p_cuser->overrun); +- if (error) return error; +- PUT_USER(error,cnow.parity, &p_cuser->parity); +- if (error) return error; +- PUT_USER(error,cnow.brk, &p_cuser->brk); +- if (error) return error; +- PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun); +- if (error) return error; +- return 0; + default: + return -ENOIOCTLCMD; + } +@@ -1351,6 +1319,32 @@ static int ioctl(struct tty_struct *tty, struct file *file, + return ret; + } + ++static int get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ SLMP_INFO *info = tty->driver_data; ++ struct mgsl_icount cnow; /* kernel counter temps */ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&info->lock,flags); ++ cnow = info->icount; ++ spin_unlock_irqrestore(&info->lock,flags); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ ++ return 0; ++} ++ + /* + * /proc fs routines.... + */ +@@ -3908,6 +3902,7 @@ static const struct tty_operations ops = { + .hangup = hangup, + .tiocmget = tiocmget, + .tiocmset = tiocmset, ++ .get_icount = get_icount, + .proc_fops = &synclinkmp_proc_fops, + }; + +diff --git a/drivers/serial/68360serial.c b/drivers/serial/68360serial.c +index 24661cd..1e4f831 100644 +--- a/drivers/serial/68360serial.c ++++ b/drivers/serial/68360serial.c +@@ -1381,6 +1381,30 @@ static void send_break(ser_info_t *info, unsigned int duration) + } + + ++/* ++ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) ++ * Return: write counters to the user passed counter struct ++ * NB: both 1->0 and 0->1 transitions are counted except for ++ * RI where only 0->1 is counted. ++ */ ++static int rs_360_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ ser_info_t *info = (ser_info_t *)tty->driver_data; ++ struct async_icount cnow; ++ ++ local_irq_disable(); ++ cnow = info->state->icount; ++ local_irq_enable(); ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ ++ return 0; ++} ++ + static int rs_360_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) + { +@@ -1394,7 +1418,7 @@ static int rs_360_ioctl(struct tty_struct *tty, struct file * file, + if (serial_paranoia_check(info, tty->name, "rs_ioctl")) + return -ENODEV; + +- if ((cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { ++ if (cmd != TIOCMIWAIT) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } +@@ -1477,31 +1501,6 @@ static int rs_360_ioctl(struct tty_struct *tty, struct file * file, + return 0; + #endif + +- /* +- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) +- * Return: write counters to the user passed counter struct +- * NB: both 1->0 and 0->1 transitions are counted except for +- * RI where only 0->1 is counted. +- */ +- case TIOCGICOUNT: +- local_irq_disable(); +- cnow = info->state->icount; +- local_irq_enable(); +- p_cuser = (struct serial_icounter_struct *) arg; +-/* error = put_user(cnow.cts, &p_cuser->cts); */ +-/* if (error) return error; */ +-/* error = put_user(cnow.dsr, &p_cuser->dsr); */ +-/* if (error) return error; */ +-/* error = put_user(cnow.rng, &p_cuser->rng); */ +-/* if (error) return error; */ +-/* error = put_user(cnow.dcd, &p_cuser->dcd); */ +-/* if (error) return error; */ +- +- put_user(cnow.cts, &p_cuser->cts); +- put_user(cnow.dsr, &p_cuser->dsr); +- put_user(cnow.rng, &p_cuser->rng); +- put_user(cnow.dcd, &p_cuser->dcd); +- return 0; + + default: + return -ENOIOCTLCMD; +diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c +index 309b6c2..8996006 100644 +--- a/net/bluetooth/rfcomm/tty.c ++++ b/net/bluetooth/rfcomm/tty.c +@@ -844,10 +844,6 @@ static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned + BT_DBG("TIOCMIWAIT"); + break; + +- case TIOCGICOUNT: +- BT_DBG("TIOCGICOUNT"); +- break; +- + case TIOCGSERIAL: + BT_ERR("TIOCGSERIAL is not supported"); + return -ENOIOCTLCMD; +diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c +index dc52f75..19b3e1d 100644 +--- a/drivers/char/nozomi.c ++++ b/drivers/char/nozomi.c +@@ -1783,24 +1783,24 @@ static int ntty_cflags_changed(struct port *port, unsigned long flags, + return ret; + } + +-static int ntty_ioctl_tiocgicount(struct port *port, void __user *argp) ++static int ntty_tiocgicount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) + { ++ struct port *port = tty->driver_data; + const struct async_icount cnow = port->tty_icount; +- struct serial_icounter_struct icount; +- +- icount.cts = cnow.cts; +- icount.dsr = cnow.dsr; +- icount.rng = cnow.rng; +- icount.dcd = cnow.dcd; +- icount.rx = cnow.rx; +- icount.tx = cnow.tx; +- icount.frame = cnow.frame; +- icount.overrun = cnow.overrun; +- icount.parity = cnow.parity; +- icount.brk = cnow.brk; +- icount.buf_overrun = cnow.buf_overrun; +- +- return copy_to_user(argp, &icount, sizeof(icount)) ? -EFAULT : 0; ++ ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; ++ return 0; + } + + static int ntty_ioctl(struct tty_struct *tty, struct file *file, +@@ -1819,9 +1819,7 @@ static int ntty_ioctl(struct tty_struct *tty, struct file *file, + rval = wait_event_interruptible(port->tty_wait, + ntty_cflags_changed(port, arg, &cprev)); + break; +- } case TIOCGICOUNT: +- rval = ntty_ioctl_tiocgicount(port, argp); +- break; ++ } + default: + DBG1("ERR: 0x%08X, %d", cmd, cmd); + break; +@@ -1895,6 +1893,7 @@ static const struct tty_operations tty_ops = { + .chars_in_buffer = ntty_chars_in_buffer, + .tiocmget = ntty_tiocmget, + .tiocmset = ntty_tiocmset, ++ .get_icount = ntty_tiocgicount, + }; + + /* Module initialization */ + +-- +1.7.3.2 diff --git a/tty-make-tiocgicount-a-handler.patch b/tty-make-tiocgicount-a-handler.patch new file mode 100644 index 0000000..48d59b6 --- /dev/null +++ b/tty-make-tiocgicount-a-handler.patch @@ -0,0 +1,218 @@ +From 52583e6fde34587a38c72a5c094c17e0a3503791 Mon Sep 17 00:00:00 2001 +From: Alan Cox <alan@linux.intel.com> +Date: Thu, 16 Sep 2010 18:21:24 +0100 +Subject: [PATCH 1/2] tty: Make tiocgicount a handler + +Dan Rosenberg noted that various drivers return the struct with uncleared +fields. Instead of spending forever trying to stomp all the drivers that +get it wrong (and every new driver) do the job in one place. + +This first patch adds the needed operations and hooks them up, including +the needed USB midlayer and serial core plumbing. + +Signed-off-by: Alan Cox <alan@linux.intel.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + drivers/char/tty_io.c | 21 +++++++++++++++++++++ + drivers/serial/serial_core.c | 37 +++++++++++++++++-------------------- + drivers/usb/serial/usb-serial.c | 13 +++++++++++++ + include/linux/tty_driver.h | 9 +++++++++ + include/linux/usb/serial.h | 2 ++ + 5 files changed, 62 insertions(+), 20 deletions(-) + +diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c +index 507441a..3a69c39 100644 +--- a/drivers/char/tty_io.c ++++ b/drivers/char/tty_io.c +@@ -96,6 +96,7 @@ + #include <linux/bitops.h> + #include <linux/delay.h> + #include <linux/seq_file.h> ++#include <linux/serial.h> + + #include <linux/uaccess.h> + #include <asm/system.h> +@@ -2456,6 +2457,20 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int + return tty->ops->tiocmset(tty, file, set, clear); + } + ++static int tty_tiocgicount(struct tty_struct *tty, void __user *arg) ++{ ++ int retval = -EINVAL; ++ struct serial_icounter_struct icount; ++ memset(&icount, 0, sizeof(icount)); ++ if (tty->ops->get_icount) ++ retval = tty->ops->get_icount(tty, &icount); ++ if (retval != 0) ++ return retval; ++ if (copy_to_user(arg, &icount, sizeof(icount))) ++ return -EFAULT; ++ return 0; ++} ++ + struct tty_struct *tty_pair_get_tty(struct tty_struct *tty) + { + if (tty->driver->type == TTY_DRIVER_TYPE_PTY && +@@ -2576,6 +2591,12 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + case TIOCMBIC: + case TIOCMBIS: + return tty_tiocmset(tty, file, cmd, p); ++ case TIOCGICOUNT: ++ retval = tty_tiocgicount(tty, p); ++ /* For the moment allow fall through to the old method */ ++ if (retval != -EINVAL) ++ return retval; ++ break; + case TCFLSH: + switch (arg) { + case TCIFLUSH: +diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c +index 7f28307..232e2bb 100644 +--- a/drivers/serial/serial_core.c ++++ b/drivers/serial/serial_core.c +@@ -1074,10 +1074,10 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +-static int uart_get_count(struct uart_state *state, +- struct serial_icounter_struct __user *icnt) ++static int uart_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) + { +- struct serial_icounter_struct icount; ++ struct uart_state *state = tty->driver_data; + struct uart_icount cnow; + struct uart_port *uport = state->uart_port; + +@@ -1085,19 +1085,19 @@ static int uart_get_count(struct uart_state *state, + memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&uport->lock); + +- icount.cts = cnow.cts; +- icount.dsr = cnow.dsr; +- icount.rng = cnow.rng; +- icount.dcd = cnow.dcd; +- icount.rx = cnow.rx; +- icount.tx = cnow.tx; +- icount.frame = cnow.frame; +- icount.overrun = cnow.overrun; +- icount.parity = cnow.parity; +- icount.brk = cnow.brk; +- icount.buf_overrun = cnow.buf_overrun; ++ icount->cts = cnow.cts; ++ icount->dsr = cnow.dsr; ++ icount->rng = cnow.rng; ++ icount->dcd = cnow.dcd; ++ icount->rx = cnow.rx; ++ icount->tx = cnow.tx; ++ icount->frame = cnow.frame; ++ icount->overrun = cnow.overrun; ++ icount->parity = cnow.parity; ++ icount->brk = cnow.brk; ++ icount->buf_overrun = cnow.buf_overrun; + +- return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; ++ return 0; + } + + /* +@@ -1150,10 +1150,6 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, + case TIOCMIWAIT: + ret = uart_wait_modem_status(state, arg); + break; +- +- case TIOCGICOUNT: +- ret = uart_get_count(state, uarg); +- break; + } + + if (ret != -ENOIOCTLCMD) +@@ -2305,6 +2301,7 @@ static const struct tty_operations uart_ops = { + #endif + .tiocmget = uart_tiocmget, + .tiocmset = uart_tiocmset, ++ .get_icount = uart_get_icount, + #ifdef CONFIG_CONSOLE_POLL + .poll_init = uart_poll_init, + .poll_get_char = uart_poll_get_char, +diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c +index 941c2d4..8aea96b 100644 +--- a/drivers/usb/serial/usb-serial.c ++++ b/drivers/usb/serial/usb-serial.c +@@ -519,6 +519,18 @@ static int serial_tiocmset(struct tty_struct *tty, struct file *file, + return -EINVAL; + } + ++static int serial_get_icount(struct tty_struct *tty, ++ struct serial_icounter_struct *icount) ++{ ++ struct usb_serial_port *port = tty->driver_data; ++ ++ dbg("%s - port %d", __func__, port->number); ++ ++ if (port->serial->type->get_icount) ++ return port->serial->type->get_icount(tty, icount); ++ return -EINVAL; ++} ++ + /* + * We would be calling tty_wakeup here, but unfortunately some line + * disciplines have an annoying habit of calling tty->write from +@@ -1208,6 +1220,7 @@ static const struct tty_operations serial_ops = { + .chars_in_buffer = serial_chars_in_buffer, + .tiocmget = serial_tiocmget, + .tiocmset = serial_tiocmset, ++ .get_icount = serial_get_icount, + .cleanup = serial_cleanup, + .install = serial_install, + .proc_fops = &serial_proc_fops, +diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h +index b086779..db2d227 100644 +--- a/include/linux/tty_driver.h ++++ b/include/linux/tty_driver.h +@@ -224,6 +224,12 @@ + * unless the tty also has a valid tty->termiox pointer. + * + * Optional: Called under the termios lock ++ * ++ * int (*get_icount)(struct tty_struct *tty, struct serial_icounter *icount); ++ * ++ * Called when the device receives a TIOCGICOUNT ioctl. Passed a kernel ++ * structure to complete. This method is optional and will only be called ++ * if provided (otherwise EINVAL will be returned). + */ + + #include <linux/fs.h> +@@ -232,6 +238,7 @@ + + struct tty_struct; + struct tty_driver; ++struct serial_icounter_struct; + + struct tty_operations { + struct tty_struct * (*lookup)(struct tty_driver *driver, +@@ -268,6 +275,8 @@ struct tty_operations { + unsigned int set, unsigned int clear); + int (*resize)(struct tty_struct *tty, struct winsize *ws); + int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew); ++ int (*get_icount)(struct tty_struct *tty, ++ struct serial_icounter_struct *icount); + #ifdef CONFIG_CONSOLE_POLL + int (*poll_init)(struct tty_driver *driver, int line, char *options); + int (*poll_get_char)(struct tty_driver *driver, int line); +diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h +index 84a4c44..8288b57 100644 +--- a/include/linux/usb/serial.h ++++ b/include/linux/usb/serial.h +@@ -271,6 +271,8 @@ struct usb_serial_driver { + int (*tiocmget)(struct tty_struct *tty, struct file *file); + int (*tiocmset)(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); ++ int (*get_icount)(struct tty_struct *tty, ++ struct serial_icounter_struct *icount); + /* Called by the tty layer for port level work. There may or may not + be an attached tty at this point */ + void (*dtr_rts)(struct usb_serial_port *port, int on); +-- +1.7.3.2 + |