From 338e4fab3d41aa14264e10ce505a7c908633cdad Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 20 Oct 2010 15:55:45 -0700 Subject: drivers/char/agp/parisc-agp.c: eliminate memory leak alloc_pci_dev allocates some memory, so that memory should be freed before leaving the function in an error case. A simplified version of the semantic match that finds this problem is: (http://coccinelle.lip6.fr/) // @r exists@ local idexpression x; expression E; identifier f1; iterator I; @@ x = alloc_pci_dev(...); <... when != x when != true (x == NULL || ...) when != if (...) { <+...x...+> } when != I (...) { <+...x...+> } ( x == NULL | x == E | x->f1 ) ...> * return ...; // Signed-off-by: Julia Lawall Dan Carpenter Dave Airlie Cc: Helge Deller Cc: "James E.J. Bottomley" Signed-off-by: Andrew Morton Signed-off-by: Kyle McMartin --- drivers/char/agp/parisc-agp.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index 1c129211302d..17e380f5f818 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -358,8 +358,12 @@ parisc_agp_setup(void __iomem *ioc_hpa, void __iomem *lba_hpa) bridge->dev = fake_bridge_dev; error = agp_add_bridge(bridge); + if (error) + goto fail; + return 0; fail: + kfree(fake_bridge_dev); return error; } -- cgit v1.2.1 From 4fe7d5a708a955b35e3fdc4dea3e0b7a6ae2eb06 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 2 Sep 2010 16:17:06 +0100 Subject: xen: make hvc_xen console work for dom0. Use the console hypercalls for dom0 console. [ Impact: Add Xen dom0 console ] Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Juan Quintela Signed-off-by: Stefano Stabellini Reviewed-by: Konrad Rzeszutek Wilk --- drivers/char/hvc_xen.c | 98 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 33 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c index 60446f82a3fc..1f7e13a43a88 100644 --- a/drivers/char/hvc_xen.c +++ b/drivers/char/hvc_xen.c @@ -78,7 +78,7 @@ static int __write_console(const char *data, int len) return sent; } -static int write_console(uint32_t vtermno, const char *data, int len) +static int domU_write_console(uint32_t vtermno, const char *data, int len) { int ret = len; @@ -101,7 +101,7 @@ static int write_console(uint32_t vtermno, const char *data, int len) return ret; } -static int read_console(uint32_t vtermno, char *buf, int len) +static int domU_read_console(uint32_t vtermno, char *buf, int len) { struct xencons_interface *intf = xencons_interface(); XENCONS_RING_IDX cons, prod; @@ -122,28 +122,62 @@ static int read_console(uint32_t vtermno, char *buf, int len) return recv; } -static const struct hv_ops hvc_ops = { - .get_chars = read_console, - .put_chars = write_console, +static struct hv_ops domU_hvc_ops = { + .get_chars = domU_read_console, + .put_chars = domU_write_console, .notifier_add = notifier_add_irq, .notifier_del = notifier_del_irq, .notifier_hangup = notifier_hangup_irq, }; -static int __init xen_init(void) +static int dom0_read_console(uint32_t vtermno, char *buf, int len) +{ + return HYPERVISOR_console_io(CONSOLEIO_read, len, buf); +} + +/* + * Either for a dom0 to write to the system console, or a domU with a + * debug version of Xen + */ +static int dom0_write_console(uint32_t vtermno, const char *str, int len) +{ + int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); + if (rc < 0) + return 0; + + return len; +} + +static struct hv_ops dom0_hvc_ops = { + .get_chars = dom0_read_console, + .put_chars = dom0_write_console, + .notifier_add = notifier_add_irq, + .notifier_del = notifier_del_irq, + .notifier_hangup = notifier_hangup_irq, +}; + +static int __init xen_hvc_init(void) { struct hvc_struct *hp; + struct hv_ops *ops; - if (!xen_pv_domain() || - xen_initial_domain() || - !xen_start_info->console.domU.evtchn) + if (!xen_pv_domain()) return -ENODEV; - xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn); + if (xen_initial_domain()) { + ops = &dom0_hvc_ops; + xencons_irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); + } else { + if (!xen_start_info->console.domU.evtchn) + return -ENODEV; + + ops = &domU_hvc_ops; + xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn); + } if (xencons_irq < 0) xencons_irq = 0; /* NO_IRQ */ - hp = hvc_alloc(HVC_COOKIE, xencons_irq, &hvc_ops, 256); + hp = hvc_alloc(HVC_COOKIE, xencons_irq, ops, 256); if (IS_ERR(hp)) return PTR_ERR(hp); @@ -160,7 +194,7 @@ void xen_console_resume(void) rebind_evtchn_irq(xen_start_info->console.domU.evtchn, xencons_irq); } -static void __exit xen_fini(void) +static void __exit xen_hvc_fini(void) { if (hvc) hvc_remove(hvc); @@ -168,29 +202,24 @@ static void __exit xen_fini(void) static int xen_cons_init(void) { + struct hv_ops *ops; + if (!xen_pv_domain()) return 0; - hvc_instantiate(HVC_COOKIE, 0, &hvc_ops); + if (xen_initial_domain()) + ops = &dom0_hvc_ops; + else + ops = &domU_hvc_ops; + + hvc_instantiate(HVC_COOKIE, 0, ops); return 0; } -module_init(xen_init); -module_exit(xen_fini); +module_init(xen_hvc_init); +module_exit(xen_hvc_fini); console_initcall(xen_cons_init); -static void raw_console_write(const char *str, int len) -{ - while(len > 0) { - int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); - if (rc <= 0) - break; - - str += rc; - len -= rc; - } -} - #ifdef CONFIG_EARLY_PRINTK static void xenboot_write_console(struct console *console, const char *string, unsigned len) @@ -198,19 +227,22 @@ static void xenboot_write_console(struct console *console, const char *string, unsigned int linelen, off = 0; const char *pos; - raw_console_write(string, len); + dom0_write_console(0, string, len); + + if (xen_initial_domain()) + return; - write_console(0, "(early) ", 8); + domU_write_console(0, "(early) ", 8); while (off < len && NULL != (pos = strchr(string+off, '\n'))) { linelen = pos-string+off; if (off + linelen > len) break; - write_console(0, string+off, linelen); - write_console(0, "\r\n", 2); + domU_write_console(0, string+off, linelen); + domU_write_console(0, "\r\n", 2); off += linelen + 1; } if (off < len) - write_console(0, string+off, len-off); + domU_write_console(0, string+off, len-off); } struct console xenboot_console = { @@ -222,7 +254,7 @@ struct console xenboot_console = { void xen_raw_console_write(const char *str) { - raw_console_write(str, strlen(str)); + dom0_write_console(0, str, strlen(str)); } void xen_raw_printk(const char *fmt, ...) -- cgit v1.2.1 From 1e0ad2881d50becaeea70ec696a80afeadf944d2 Mon Sep 17 00:00:00 2001 From: Graham Gower Date: Wed, 27 Oct 2010 15:33:00 -0700 Subject: drivers/char/vt_ioctl.c: fix VT_OPENQRY error value When all VT's are in use, VT_OPENQRY casts -1 to unsigned char before returning it to userspace as an int. VT255 is not the next available console. Signed-off-by: Graham Gower Cc: Greg KH Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/vt_ioctl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 38df8c19e74c..6b68a0fb4611 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -503,6 +503,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, struct kbd_struct * kbd; unsigned int console; unsigned char ucval; + unsigned int uival; void __user *up = (void __user *)arg; int i, perm; int ret = 0; @@ -657,7 +658,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, break; case KDGETMODE: - ucval = vc->vc_mode; + uival = vc->vc_mode; goto setint; case KDMAPDISP: @@ -695,7 +696,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, break; case KDGKBMODE: - ucval = ((kbd->kbdmode == VC_RAW) ? K_RAW : + uival = ((kbd->kbdmode == VC_RAW) ? K_RAW : (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW : (kbd->kbdmode == VC_UNICODE) ? K_UNICODE : K_XLATE); @@ -717,9 +718,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, break; case KDGKBMETA: - ucval = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT); + uival = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT); setint: - ret = put_user(ucval, (int __user *)arg); + ret = put_user(uival, (int __user *)arg); break; case KDGETKEYCODE: @@ -949,7 +950,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, for (i = 0; i < MAX_NR_CONSOLES; ++i) if (! VT_IS_IN_USE(i)) break; - ucval = i < MAX_NR_CONSOLES ? (i+1) : -1; + uival = i < MAX_NR_CONSOLES ? (i+1) : -1; goto setint; /* -- cgit v1.2.1 From 60ee6d5faf5f7920ba88b82c072864596f5b88af Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Wed, 27 Oct 2010 15:34:18 -0700 Subject: ipmi: fix __init and __exit attribute locations __init and __exit belong after the return type on functions, not before. Signed-off-by: Corey Minyard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/ipmi_devintf.c | 4 ++-- drivers/char/ipmi/ipmi_msghandler.c | 4 ++-- drivers/char/ipmi/ipmi_si_intf.c | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 1fc8876af1f5..2aa3977aae5e 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -916,7 +916,7 @@ static struct ipmi_smi_watcher smi_watcher = .smi_gone = ipmi_smi_gone, }; -static __init int init_ipmi_devintf(void) +static int __init init_ipmi_devintf(void) { int rv; @@ -954,7 +954,7 @@ static __init int init_ipmi_devintf(void) } module_init(init_ipmi_devintf); -static __exit void cleanup_ipmi(void) +static void __exit cleanup_ipmi(void) { struct ipmi_reg_list *entry, *entry2; mutex_lock(®_list_mutex); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 4f3f8c9ec262..2fe72f8edf44 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -4442,13 +4442,13 @@ static int ipmi_init_msghandler(void) return 0; } -static __init int ipmi_init_msghandler_mod(void) +static int __init ipmi_init_msghandler_mod(void) { ipmi_init_msghandler(); return 0; } -static __exit void cleanup_ipmi(void) +static void __exit cleanup_ipmi(void) { int count; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index b293d57d30a7..035da9e64a17 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1846,7 +1846,7 @@ static int hotmod_handler(const char *val, struct kernel_param *kp) return rv; } -static __devinit void hardcode_find_bmc(void) +static void __devinit hardcode_find_bmc(void) { int i; struct smi_info *info; @@ -2029,7 +2029,7 @@ struct SPMITable { s8 spmi_id[1]; /* A '\0' terminated array starts here. */ }; -static __devinit int try_init_spmi(struct SPMITable *spmi) +static int __devinit try_init_spmi(struct SPMITable *spmi) { struct smi_info *info; @@ -2112,7 +2112,7 @@ static __devinit int try_init_spmi(struct SPMITable *spmi) return 0; } -static __devinit void spmi_find_bmc(void) +static void __devinit spmi_find_bmc(void) { acpi_status status; struct SPMITable *spmi; @@ -2325,7 +2325,7 @@ static int __devinit decode_dmi(const struct dmi_header *dm, return 0; } -static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) +static void __devinit try_init_dmi(struct dmi_ipmi_data *ipmi_data) { struct smi_info *info; @@ -3012,7 +3012,7 @@ static __devinitdata struct ipmi_default_vals { .port = 0 } }; -static __devinit void default_find_bmc(void) +static void __devinit default_find_bmc(void) { struct smi_info *info; int i; @@ -3312,7 +3312,7 @@ static int try_smi_init(struct smi_info *new_smi) return rv; } -static __devinit int init_ipmi_si(void) +static int __devinit init_ipmi_si(void) { int i; char *str; @@ -3525,7 +3525,7 @@ static void cleanup_one_si(struct smi_info *to_clean) kfree(to_clean); } -static __exit void cleanup_ipmi_si(void) +static void __exit cleanup_ipmi_si(void) { struct smi_info *e, *tmp_e; -- cgit v1.2.1 From 713efa9a64fac01abc296228c5ca4f507217a8c9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 Oct 2010 15:34:18 -0700 Subject: drivers/char/rocket.c: release_region or error path There was a release_region() missing on the error path. Signed-off-by: Dan Carpenter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/rocket.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 7c79d243acc9..86308830ac42 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -2345,7 +2345,7 @@ static int __init rp_init(void) ret = tty_register_driver(rocket_driver); if (ret < 0) { printk(KERN_ERR "Couldn't install tty RocketPort driver\n"); - goto err_tty; + goto err_controller; } #ifdef ROCKET_DEBUG_OPEN @@ -2380,6 +2380,9 @@ static int __init rp_init(void) return 0; err_ttyu: tty_unregister_driver(rocket_driver); +err_controller: + if (controller) + release_region(controller, 4); err_tty: put_tty_driver(rocket_driver); err: -- cgit v1.2.1 From 656e17ea9d997db1bb37d90b7a447eb511af5a63 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 27 Oct 2010 15:34:19 -0700 Subject: drivers/char/hvc_console.c: remove unneeded __set_current_state(TASK_RUNNING) This doesn't do anything. Cc: Timur Tabi Cc: Benjamin Herrenschmidt Cc: Amit Shah Cc: Greg Kroah-Hartman Cc: Kumar Gala Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/hvc_console.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 3afd62e856eb..e9cba13ee800 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -713,7 +713,6 @@ static int khvcd(void *unused) struct hvc_struct *hp; set_freezable(); - __set_current_state(TASK_RUNNING); do { poll_mask = 0; hvc_kicked = 0; -- cgit v1.2.1 From dcade5ed16cce572e375bf4e63dd2150c351bf49 Mon Sep 17 00:00:00 2001 From: Dimitri Sivanich Date: Wed, 27 Oct 2010 15:34:20 -0700 Subject: SGI Altix IA64 mmtimer: eliminate long interval timer holdoffs This patch for SGI Altix/IA64 eliminates interval long timer holdoffs in cases where we don't start an interval timer before the expiration time. This sometimes happens when a number of interval timers on the same shub with the same interval run simultaneously. Signed-off-by: Dimitri Sivanich Cc: "Luck, Tony" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/mmtimer.c | 60 +++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 28 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index c070b53984e4..e6d75627c6c8 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -176,9 +176,9 @@ static void mmtimer_setup_int_2(int cpu, u64 expires) * in order to insure that the setup succeeds in a deterministic time frame. * It will check if the interrupt setup succeeded. */ -static int mmtimer_setup(int cpu, int comparator, unsigned long expires) +static int mmtimer_setup(int cpu, int comparator, unsigned long expires, + u64 *set_completion_time) { - switch (comparator) { case 0: mmtimer_setup_int_0(cpu, expires); @@ -191,7 +191,8 @@ static int mmtimer_setup(int cpu, int comparator, unsigned long expires) break; } /* We might've missed our expiration time */ - if (rtc_time() <= expires) + *set_completion_time = rtc_time(); + if (*set_completion_time <= expires) return 1; /* @@ -227,6 +228,8 @@ static int mmtimer_disable_int(long nasid, int comparator) #define TIMER_OFF 0xbadcabLL /* Timer is not setup */ #define TIMER_SET 0 /* Comparator is set for this timer */ +#define MMTIMER_INTERVAL_RETRY_INCREMENT_DEFAULT 40 + /* There is one of these for each timer */ struct mmtimer { struct rb_node list; @@ -242,6 +245,11 @@ struct mmtimer_node { }; static struct mmtimer_node *timers; +static unsigned mmtimer_interval_retry_increment = + MMTIMER_INTERVAL_RETRY_INCREMENT_DEFAULT; +module_param(mmtimer_interval_retry_increment, uint, 0644); +MODULE_PARM_DESC(mmtimer_interval_retry_increment, + "RTC ticks to add to expiration on interval retry (default 40)"); /* * Add a new mmtimer struct to the node's mmtimer list. @@ -289,7 +297,8 @@ static void mmtimer_set_next_timer(int nodeid) struct mmtimer_node *n = &timers[nodeid]; struct mmtimer *x; struct k_itimer *t; - int o; + u64 expires, exp, set_completion_time; + int i; restart: if (n->next == NULL) @@ -300,7 +309,8 @@ restart: if (!t->it.mmtimer.incr) { /* Not an interval timer */ if (!mmtimer_setup(x->cpu, COMPARATOR, - t->it.mmtimer.expires)) { + t->it.mmtimer.expires, + &set_completion_time)) { /* Late setup, fire now */ tasklet_schedule(&n->tasklet); } @@ -308,14 +318,23 @@ restart: } /* Interval timer */ - o = 0; - while (!mmtimer_setup(x->cpu, COMPARATOR, t->it.mmtimer.expires)) { - unsigned long e, e1; - struct rb_node *next; - t->it.mmtimer.expires += t->it.mmtimer.incr << o; - t->it_overrun += 1 << o; - o++; - if (o > 20) { + i = 0; + expires = exp = t->it.mmtimer.expires; + while (!mmtimer_setup(x->cpu, COMPARATOR, expires, + &set_completion_time)) { + int to; + + i++; + expires = set_completion_time + + mmtimer_interval_retry_increment + (1 << i); + /* Calculate overruns as we go. */ + to = ((u64)(expires - exp) / t->it.mmtimer.incr); + if (to) { + t->it_overrun += to; + t->it.mmtimer.expires += t->it.mmtimer.incr * to; + exp = t->it.mmtimer.expires; + } + if (i > 20) { printk(KERN_ALERT "mmtimer: cannot reschedule timer\n"); t->it.mmtimer.clock = TIMER_OFF; n->next = rb_next(&x->list); @@ -323,21 +342,6 @@ restart: kfree(x); goto restart; } - - e = t->it.mmtimer.expires; - next = rb_next(&x->list); - - if (next == NULL) - continue; - - e1 = rb_entry(next, struct mmtimer, list)-> - timer->it.mmtimer.expires; - if (e > e1) { - n->next = next; - rb_erase(&x->list, &n->timer_head); - mmtimer_add_list(x); - goto restart; - } } } -- cgit v1.2.1 From ffd7d6baa65e6161cfd996a59d55c48571c2a5f3 Mon Sep 17 00:00:00 2001 From: Paul Fulghum Date: Wed, 27 Oct 2010 15:34:20 -0700 Subject: synclink_gt: fix per device locking Fix a long standing bug with per device locking. Each device has an associated spinlock for synchronizing access to hardware and state information with the ISR. A single hardware card has one or more devices. Bug: Non ISR code correctly acquires and releases the per device lock. ISR incorrectly always acquires and releases the lock of the first device on the card. The decoupled and list based nature of the ISR and deferred processing interaction allowed this to work in normal operation. Exceptional events like an application forcing hardware shutdown, reset, or reconfiguration while active can trigger the bug. Fixed ISR to acquire and release the per device lock. One exception is manipulation of the GPIO card resource which is global and effectively owned by the first device of the card. Non-ISR access to GPIO resource is changed to use lock of first device on card. Signed-off-by: Paul Fulghum Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/synclink_gt.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 1746d91205f7..11999784383e 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -2357,26 +2357,27 @@ static irqreturn_t slgt_interrupt(int dummy, void *dev_id) DBGISR(("slgt_interrupt irq=%d entry\n", info->irq_level)); - spin_lock(&info->lock); - while((gsr = rd_reg32(info, GSR) & 0xffffff00)) { DBGISR(("%s gsr=%08x\n", info->device_name, gsr)); info->irq_occurred = true; for(i=0; i < info->port_count ; i++) { if (info->port_array[i] == NULL) continue; + spin_lock(&info->port_array[i]->lock); if (gsr & (BIT8 << i)) isr_serial(info->port_array[i]); if (gsr & (BIT16 << (i*2))) isr_rdma(info->port_array[i]); if (gsr & (BIT17 << (i*2))) isr_tdma(info->port_array[i]); + spin_unlock(&info->port_array[i]->lock); } } if (info->gpio_present) { unsigned int state; unsigned int changed; + spin_lock(&info->lock); while ((changed = rd_reg32(info, IOSR)) != 0) { DBGISR(("%s iosr=%08x\n", info->device_name, changed)); /* read latched state of GPIO signals */ @@ -2388,22 +2389,24 @@ static irqreturn_t slgt_interrupt(int dummy, void *dev_id) isr_gpio(info->port_array[i], changed, state); } } + spin_unlock(&info->lock); } for(i=0; i < info->port_count ; i++) { struct slgt_info *port = info->port_array[i]; - - if (port && (port->port.count || port->netcount) && + if (port == NULL) + continue; + spin_lock(&port->lock); + if ((port->port.count || port->netcount) && port->pending_bh && !port->bh_running && !port->bh_requested) { DBGISR(("%s bh queued\n", port->device_name)); schedule_work(&port->task); port->bh_requested = true; } + spin_unlock(&port->lock); } - spin_unlock(&info->lock); - DBGISR(("slgt_interrupt irq=%d exit\n", info->irq_level)); return IRQ_HANDLED; } @@ -2906,7 +2909,7 @@ static int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) info->device_name, gpio.state, gpio.smask, gpio.dir, gpio.dmask)); - spin_lock_irqsave(&info->lock,flags); + spin_lock_irqsave(&info->port_array[0]->lock, flags); if (gpio.dmask) { data = rd_reg32(info, IODR); data |= gpio.dmask & gpio.dir; @@ -2919,7 +2922,7 @@ static int set_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) data &= ~(gpio.smask & ~gpio.state); wr_reg32(info, IOVR, data); } - spin_unlock_irqrestore(&info->lock,flags); + spin_unlock_irqrestore(&info->port_array[0]->lock, flags); return 0; } @@ -3020,7 +3023,7 @@ static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) return -EINVAL; init_cond_wait(&wait, gpio.smask); - spin_lock_irqsave(&info->lock, flags); + spin_lock_irqsave(&info->port_array[0]->lock, flags); /* enable interrupts for watched pins */ wr_reg32(info, IOER, rd_reg32(info, IOER) | gpio.smask); /* get current pin states */ @@ -3032,20 +3035,20 @@ static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *user_gpio) } else { /* wait for target state */ add_cond_wait(&info->gpio_wait_q, &wait); - spin_unlock_irqrestore(&info->lock, flags); + spin_unlock_irqrestore(&info->port_array[0]->lock, flags); schedule(); if (signal_pending(current)) rc = -ERESTARTSYS; else gpio.state = wait.data; - spin_lock_irqsave(&info->lock, flags); + spin_lock_irqsave(&info->port_array[0]->lock, flags); remove_cond_wait(&info->gpio_wait_q, &wait); } /* disable all GPIO interrupts if no waiting processes */ if (info->gpio_wait_q == NULL) wr_reg32(info, IOER, 0); - spin_unlock_irqrestore(&info->lock,flags); + spin_unlock_irqrestore(&info->port_array[0]->lock, flags); if ((rc == 0) && copy_to_user(user_gpio, &gpio, sizeof(gpio))) rc = -EFAULT; @@ -3578,7 +3581,6 @@ static void device_init(int adapter_num, struct pci_dev *pdev) /* copy resource information from first port to others */ for (i = 1; i < port_count; ++i) { - port_array[i]->lock = port_array[0]->lock; port_array[i]->irq_level = port_array[0]->irq_level; port_array[i]->reg_addr = port_array[0]->reg_addr; alloc_dma_bufs(port_array[i]); -- cgit v1.2.1 From 19714a8af8fe8618a9beace1f7a3bb10d55d5e2f Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Wed, 27 Oct 2010 15:34:21 -0700 Subject: drivers/char/applicom.c: fix information leak to userland Structure st_loc is copied to userland with some fields unitialized. It leads to leaking of stack memory. Signed-off-by: Vasiliy Kulikov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/applicom.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/char') diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index e7ba774beda6..25373df1dcf8 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -566,6 +566,7 @@ static ssize_t ac_read (struct file *filp, char __user *buf, size_t count, loff_ struct mailbox mailbox; /* Got a packet for us */ + memset(&st_loc, 0, sizeof(st_loc)); ret = do_ac_read(i, buf, &st_loc, &mailbox); spin_unlock_irqrestore(&apbs[i].mutex, flags); set_current_state(TASK_RUNNING); -- cgit v1.2.1 From b9b1134260e036fb75c468514569864dd6722f3e Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Wed, 27 Oct 2010 15:34:21 -0700 Subject: drivers/char/ppdev.c: fix information leak to userland Structure par_timeout is copied to userland with some padding fields unitialized. Field tv_usec has type __kernel_suseconds_t, it differs from tv_sec's type on some architectures. It leads to leaking of stack memory. Signed-off-by: Vasiliy Kulikov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ppdev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/char') diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 723152d978a9..f176dbaeb15a 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -613,6 +613,7 @@ static int pp_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PPGETTIME: to_jiffies = pp->pdev->timeout; + memset(&par_timeout, 0, sizeof(par_timeout)); par_timeout.tv_sec = to_jiffies / HZ; par_timeout.tv_usec = (to_jiffies % (long)HZ) * (1000000/HZ); if (copy_to_user (argp, &par_timeout, sizeof(struct timeval))) -- cgit v1.2.1 From ed77ed6112f2d4b650f4be7dbaf14e06e1d393a5 Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Wed, 27 Oct 2010 15:34:22 -0700 Subject: drivers/char/synclink_gt.c: fix information leak to userland Structures tmp_params and new_line are copied to userland with some padding fields unitialized. It leads to leaking of stack memory. Signed-off-by: Vasiliy Kulikov Acked-by: Paul Fulghum Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/synclink_gt.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 11999784383e..9f7fc71474b4 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -1132,6 +1132,7 @@ static long get_params32(struct slgt_info *info, struct MGSL_PARAMS32 __user *us struct MGSL_PARAMS32 tmp_params; DBGINFO(("%s get_params32\n", info->device_name)); + memset(&tmp_params, 0, sizeof(tmp_params)); tmp_params.mode = (compat_ulong_t)info->params.mode; tmp_params.loopback = info->params.loopback; tmp_params.flags = info->params.flags; @@ -1617,6 +1618,8 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (cmd != SIOCWANDEV) return hdlc_ioctl(dev, ifr, cmd); + memset(&new_line, 0, sizeof(new_line)); + switch(ifr->ifr_settings.type) { case IF_GET_IFACE: /* return current sync_serial_settings */ -- cgit v1.2.1 From 9807224f1dce5fb746ee33fb67ea2e38dafe3e9c Mon Sep 17 00:00:00 2001 From: Paul Fulghum Date: Wed, 27 Oct 2010 15:34:22 -0700 Subject: drivers/char/synclink_gt.c: add extended sync feature Add support for extended byte synchronous mode feature of hardware. Signed-off-by: Paul Fulghum Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/synclink_gt.c | 111 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 9f7fc71474b4..d01fffeac951 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -301,6 +301,8 @@ struct slgt_info { unsigned int rx_pio; unsigned int if_mode; unsigned int base_clock; + unsigned int xsync; + unsigned int xctrl; /* device status */ @@ -405,6 +407,8 @@ static MGSL_PARAMS default_params = { #define TDCSR 0x94 /* tx DMA control/status */ #define RDDAR 0x98 /* rx DMA descriptor address */ #define TDDAR 0x9c /* tx DMA descriptor address */ +#define XSR 0x40 /* extended sync pattern */ +#define XCR 0x44 /* extended control */ #define RXIDLE BIT14 #define RXBREAK BIT14 @@ -517,6 +521,10 @@ static int set_interface(struct slgt_info *info, int if_mode); static int set_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); static int get_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); static int wait_gpio(struct slgt_info *info, struct gpio_desc __user *gpio); +static int get_xsync(struct slgt_info *info, int __user *if_mode); +static int set_xsync(struct slgt_info *info, int if_mode); +static int get_xctrl(struct slgt_info *info, int __user *if_mode); +static int set_xctrl(struct slgt_info *info, int if_mode); /* * driver functions @@ -1056,6 +1064,14 @@ static int ioctl(struct tty_struct *tty, struct file *file, return get_gpio(info, argp); case MGSL_IOCWAITGPIO: return wait_gpio(info, argp); + case MGSL_IOCGXSYNC: + return get_xsync(info, argp); + case MGSL_IOCSXSYNC: + return set_xsync(info, (int)arg); + case MGSL_IOCGXCTRL: + return get_xctrl(info, argp); + case MGSL_IOCSXCTRL: + return set_xctrl(info, (int)arg); } mutex_lock(&info->port.mutex); switch (cmd) { @@ -1213,12 +1229,16 @@ static long slgt_compat_ioctl(struct tty_struct *tty, struct file *file, case MGSL_IOCSGPIO: case MGSL_IOCGGPIO: case MGSL_IOCWAITGPIO: + case MGSL_IOCGXSYNC: + case MGSL_IOCGXCTRL: case MGSL_IOCSTXIDLE: case MGSL_IOCTXENABLE: case MGSL_IOCRXENABLE: case MGSL_IOCTXABORT: case TIOCMIWAIT: case MGSL_IOCSIF: + case MGSL_IOCSXSYNC: + case MGSL_IOCSXCTRL: rc = ioctl(tty, file, cmd, arg); break; } @@ -1961,6 +1981,7 @@ static void bh_handler(struct work_struct *work) case MGSL_MODE_RAW: case MGSL_MODE_MONOSYNC: case MGSL_MODE_BISYNC: + case MGSL_MODE_XSYNC: while(rx_get_buf(info)); break; } @@ -2889,6 +2910,69 @@ static int set_interface(struct slgt_info *info, int if_mode) return 0; } +static int get_xsync(struct slgt_info *info, int __user *xsync) +{ + DBGINFO(("%s get_xsync=%x\n", info->device_name, info->xsync)); + if (put_user(info->xsync, xsync)) + return -EFAULT; + return 0; +} + +/* + * set extended sync pattern (1 to 4 bytes) for extended sync mode + * + * sync pattern is contained in least significant bytes of value + * most significant byte of sync pattern is oldest (1st sent/detected) + */ +static int set_xsync(struct slgt_info *info, int xsync) +{ + unsigned long flags; + + DBGINFO(("%s set_xsync=%x)\n", info->device_name, xsync)); + spin_lock_irqsave(&info->lock, flags); + info->xsync = xsync; + wr_reg32(info, XSR, xsync); + spin_unlock_irqrestore(&info->lock, flags); + return 0; +} + +static int get_xctrl(struct slgt_info *info, int __user *xctrl) +{ + DBGINFO(("%s get_xctrl=%x\n", info->device_name, info->xctrl)); + if (put_user(info->xctrl, xctrl)) + return -EFAULT; + return 0; +} + +/* + * set extended control options + * + * xctrl[31:19] reserved, must be zero + * xctrl[18:17] extended sync pattern length in bytes + * 00 = 1 byte in xsr[7:0] + * 01 = 2 bytes in xsr[15:0] + * 10 = 3 bytes in xsr[23:0] + * 11 = 4 bytes in xsr[31:0] + * xctrl[16] 1 = enable terminal count, 0=disabled + * xctrl[15:0] receive terminal count for fixed length packets + * value is count minus one (0 = 1 byte packet) + * when terminal count is reached, receiver + * automatically returns to hunt mode and receive + * FIFO contents are flushed to DMA buffers with + * end of frame (EOF) status + */ +static int set_xctrl(struct slgt_info *info, int xctrl) +{ + unsigned long flags; + + DBGINFO(("%s set_xctrl=%x)\n", info->device_name, xctrl)); + spin_lock_irqsave(&info->lock, flags); + info->xctrl = xctrl; + wr_reg32(info, XCR, xctrl); + spin_unlock_irqrestore(&info->lock, flags); + return 0; +} + /* * set general purpose IO pin state and direction * @@ -3768,7 +3852,9 @@ module_exit(slgt_exit); #define CALC_REGADDR() \ unsigned long reg_addr = ((unsigned long)info->reg_addr) + addr; \ if (addr >= 0x80) \ - reg_addr += (info->port_num) * 32; + reg_addr += (info->port_num) * 32; \ + else if (addr >= 0x40) \ + reg_addr += (info->port_num) * 16; static __u8 rd_reg8(struct slgt_info *info, unsigned int addr) { @@ -4187,7 +4273,13 @@ static void sync_mode(struct slgt_info *info) /* TCR (tx control) * - * 15..13 mode, 000=HDLC 001=raw 010=async 011=monosync 100=bisync + * 15..13 mode + * 000=HDLC/SDLC + * 001=raw bit synchronous + * 010=asynchronous/isochronous + * 011=monosync byte synchronous + * 100=bisync byte synchronous + * 101=xsync byte synchronous * 12..10 encoding * 09 CRC enable * 08 CRC32 @@ -4202,6 +4294,9 @@ static void sync_mode(struct slgt_info *info) val = BIT2; switch(info->params.mode) { + case MGSL_MODE_XSYNC: + val |= BIT15 + BIT13; + break; case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; case MGSL_MODE_BISYNC: val |= BIT15; break; case MGSL_MODE_RAW: val |= BIT13; break; @@ -4256,7 +4351,13 @@ static void sync_mode(struct slgt_info *info) /* RCR (rx control) * - * 15..13 mode, 000=HDLC 001=raw 010=async 011=monosync 100=bisync + * 15..13 mode + * 000=HDLC/SDLC + * 001=raw bit synchronous + * 010=asynchronous/isochronous + * 011=monosync byte synchronous + * 100=bisync byte synchronous + * 101=xsync byte synchronous * 12..10 encoding * 09 CRC enable * 08 CRC32 @@ -4268,6 +4369,9 @@ static void sync_mode(struct slgt_info *info) val = 0; switch(info->params.mode) { + case MGSL_MODE_XSYNC: + val |= BIT15 + BIT13; + break; case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break; case MGSL_MODE_BISYNC: val |= BIT15; break; case MGSL_MODE_RAW: val |= BIT13; break; @@ -4684,6 +4788,7 @@ static bool rx_get_buf(struct slgt_info *info) switch(info->params.mode) { case MGSL_MODE_MONOSYNC: case MGSL_MODE_BISYNC: + case MGSL_MODE_XSYNC: /* ignore residue in byte synchronous modes */ if (desc_residue(info->rbufs[i])) count--; -- cgit v1.2.1 From 44cd4dffe28b18139fe76e2a7b39cd1fdb459619 Mon Sep 17 00:00:00 2001 From: Tracey Dent Date: Wed, 27 Oct 2010 15:34:23 -0700 Subject: drivers/char/ip2/Makefile: replace the use of -objs with -y Changed -objs to -y in Makefile. Signed-off-by: Tracey Dent Cc: Jiri Slaby Cc: Michal Marek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ip2/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/ip2/Makefile b/drivers/char/ip2/Makefile index bc397d92b499..7b78e0dfc5b0 100644 --- a/drivers/char/ip2/Makefile +++ b/drivers/char/ip2/Makefile @@ -4,5 +4,5 @@ obj-$(CONFIG_COMPUTONE) += ip2.o -ip2-objs := ip2main.o +ip2-y := ip2main.o -- cgit v1.2.1 From 7fa51743dcf176bcdca43603b4257748a222fe9b Mon Sep 17 00:00:00 2001 From: Tracey Dent Date: Wed, 27 Oct 2010 15:34:23 -0700 Subject: drivers/char/ipmi/Makefile: replace the use of -objs with -y Changed -objs to -y in Makefile. Signed-off-by: Tracey Dent Cc: Jiri Slaby Cc: Michal Marek Cc: Corey Minyard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ipmi/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index eb8a1a8c188e..16a93648d54e 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -2,7 +2,7 @@ # Makefile for the ipmi drivers. # -ipmi_si-objs := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o +ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o -- cgit v1.2.1 From cb299ba8b5ef2239429484072fea394cd7581bd7 Mon Sep 17 00:00:00 2001 From: Tracey Dent Date: Wed, 27 Oct 2010 15:34:24 -0700 Subject: drivers/char/mwave/Makefile: clean up Changed -objs to -y in Makefile and use ccflags-y option. Signed-off-by: Tracey Dent Cc: Jiri Slaby Cc: Michal Marek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/mwave/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mwave/Makefile b/drivers/char/mwave/Makefile index 754c9e2058ed..26b4fce217b6 100644 --- a/drivers/char/mwave/Makefile +++ b/drivers/char/mwave/Makefile @@ -6,10 +6,10 @@ obj-$(CONFIG_MWAVE) += mwave.o -mwave-objs := mwavedd.o smapi.o tp3780i.o 3780i.o +mwave-y := mwavedd.o smapi.o tp3780i.o 3780i.o # To have the mwave driver disable other uarts if necessary # EXTRA_CFLAGS += -DMWAVE_FUTZ_WITH_OTHER_DEVICES # To compile in lots (~20 KiB) of run-time enablable printk()s for debugging: -EXTRA_CFLAGS += -DMW_TRACE +ccflags-y := -DMW_TRACE -- cgit v1.2.1 From a222945eb1568687eec47113027586da47e1ee94 Mon Sep 17 00:00:00 2001 From: Tracey Dent Date: Wed, 27 Oct 2010 15:34:25 -0700 Subject: drivers/char/pcmcia/ipwireless/Makefile: Makefile: replace the use of -objs with -y Changed -objs to -y in Makefile. Signed-off-by: Tracey Dent Cc: Jiri Slaby Cc: Michal Marek Acked-by: Jiri Kosina Acked-by: David Sterba Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/pcmcia/ipwireless/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/ipwireless/Makefile b/drivers/char/pcmcia/ipwireless/Makefile index b71eb593643d..db80873d7f20 100644 --- a/drivers/char/pcmcia/ipwireless/Makefile +++ b/drivers/char/pcmcia/ipwireless/Makefile @@ -6,5 +6,5 @@ obj-$(CONFIG_IPWIRELESS) += ipwireless.o -ipwireless-objs := hardware.o main.o network.o tty.o +ipwireless-y := hardware.o main.o network.o tty.o -- cgit v1.2.1 From 5a780fc096397f9b62bc6dddced509f012a97e78 Mon Sep 17 00:00:00 2001 From: Tracey Dent Date: Wed, 27 Oct 2010 15:34:25 -0700 Subject: drivers/char/rio/Makefile: replace the use of -objs with -y Changed -objs to -y in Makefile. Signed-off-by: Tracey Dent Cc: Jiri Slaby Cc: Michal Marek Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/rio/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/rio/Makefile b/drivers/char/rio/Makefile index 2d1c5a7cba7d..1661875883fb 100644 --- a/drivers/char/rio/Makefile +++ b/drivers/char/rio/Makefile @@ -8,5 +8,5 @@ obj-$(CONFIG_RIO) += rio.o -rio-objs := rio_linux.o rioinit.o rioboot.o riocmd.o rioctrl.o riointr.o \ +rio-y := rio_linux.o rioinit.o rioboot.o riocmd.o rioctrl.o riointr.o \ rioparam.o rioroute.o riotable.o riotty.o -- cgit v1.2.1 From e89d67cfbcc1ee32339da9e816489f69742c3a6e Mon Sep 17 00:00:00 2001 From: Rakib Mullick Date: Wed, 27 Oct 2010 15:34:26 -0700 Subject: drivers/char/mxser.c: fix compilation warning in mxser.c Both mxser_disable_must_enchance_mode() and mxser_get_must_hardware_id() called from function CheckIsMoxaMust(), when CONFIG_PCI=y. So mark both the functions under CONFIG_PCI. We were warned by the following warning. drivers/char/mxser.c:306: warning: `mxser_disable_must_enchance_mode' defined but not used drivers/char/mxser.c:391: warning: `mxser_get_must_hardware_id' defined but not used Signed-off-by: Rakib Mullick Cc: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/mxser.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 463df27494bd..dd9d75351cd6 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -303,6 +303,7 @@ static void mxser_enable_must_enchance_mode(unsigned long baseio) outb(oldlcr, baseio + UART_LCR); } +#ifdef CONFIG_PCI static void mxser_disable_must_enchance_mode(unsigned long baseio) { u8 oldlcr; @@ -317,6 +318,7 @@ static void mxser_disable_must_enchance_mode(unsigned long baseio) outb(efr, baseio + MOXA_MUST_EFR_REGISTER); outb(oldlcr, baseio + UART_LCR); } +#endif static void mxser_set_must_xon1_value(unsigned long baseio, u8 value) { @@ -388,6 +390,7 @@ static void mxser_set_must_enum_value(unsigned long baseio, u8 value) outb(oldlcr, baseio + UART_LCR); } +#ifdef CONFIG_PCI static void mxser_get_must_hardware_id(unsigned long baseio, u8 *pId) { u8 oldlcr; @@ -404,6 +407,7 @@ static void mxser_get_must_hardware_id(unsigned long baseio, u8 *pId) *pId = inb(baseio + MOXA_MUST_HWID_REGISTER); outb(oldlcr, baseio + UART_LCR); } +#endif static void SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(unsigned long baseio) { -- cgit v1.2.1 From c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 27 Oct 2010 15:34:52 -0700 Subject: ramoops: use the platform data structure instead of module params As each board and system has different memory for ramoops. It's better to define the platform data instead of module params. [akpm@linux-foundation.org: fix ramoops_remove() return type] Signed-off-by: Kyungmin Park Cc: Marco Stornelli Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ramoops.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c index 74f00b5ffa36..73dcb0ee41fd 100644 --- a/drivers/char/ramoops.c +++ b/drivers/char/ramoops.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #define RAMOOPS_KERNMSG_HDR "====" #define RAMOOPS_HEADER_SIZE (5 + sizeof(struct timeval)) @@ -91,11 +93,17 @@ static void ramoops_do_dump(struct kmsg_dumper *dumper, cxt->count = (cxt->count + 1) % cxt->max_count; } -static int __init ramoops_init(void) +static int __init ramoops_probe(struct platform_device *pdev) { + struct ramoops_platform_data *pdata = pdev->dev.platform_data; struct ramoops_context *cxt = &oops_cxt; int err = -EINVAL; + if (pdata) { + mem_size = pdata->mem_size; + mem_address = pdata->mem_address; + } + if (!mem_size) { printk(KERN_ERR "ramoops: invalid size specification"); goto fail3; @@ -142,7 +150,7 @@ fail3: return err; } -static void __exit ramoops_exit(void) +static int __exit ramoops_remove(struct platform_device *pdev) { struct ramoops_context *cxt = &oops_cxt; @@ -151,8 +159,26 @@ static void __exit ramoops_exit(void) iounmap(cxt->virt_addr); release_mem_region(cxt->phys_addr, cxt->size); + return 0; } +static struct platform_driver ramoops_driver = { + .remove = __exit_p(ramoops_remove), + .driver = { + .name = "ramoops", + .owner = THIS_MODULE, + }, +}; + +static int __init ramoops_init(void) +{ + return platform_driver_probe(&ramoops_driver, ramoops_probe); +} + +static void __exit ramoops_exit(void) +{ + platform_driver_unregister(&ramoops_driver); +} module_init(ramoops_init); module_exit(ramoops_exit); -- cgit v1.2.1 From 6a915c2bf073743dd31229f1ec2eaa7a2b13c1c3 Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Fri, 29 Oct 2010 12:48:01 -0400 Subject: parisc-agp: fix missing slab.h include Commit 338e4fab added a missing kfree if the alloc_pci_dev failed but forgot to include for the definition of kfree. Signed-off-by: Kyle McMartin --- drivers/char/agp/parisc-agp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/char') diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index 17e380f5f818..94821ab01c6d 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include -- cgit v1.2.1 From 3c80fe4ac9cfb13b1bfa4edf1544e8b656716694 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 9 Dec 2009 14:19:31 +0000 Subject: audit: Call tty_audit_push_task() outside preempt disabled While auditing all tasklist_lock read_lock sites I stumbled over the following call chain: audit_prepare_user_tty() read_lock(&tasklist_lock); tty_audit_push_task(); mutex_lock(&buf->mutex); --> buf->mutex is locked with preemption disabled. Solve this by acquiring a reference to the task struct under rcu_read_lock and call tty_audit_push_task outside of the preempt disabled region. Move all code which needs to be protected by sighand lock into tty_audit_push_task() and use lock/unlock_sighand as we do not hold tasklist_lock. Signed-off-by: Thomas Gleixner Cc: Al Viro Cc: Eric Paris Cc: Oleg Nesterov Signed-off-by: Al Viro --- drivers/char/tty_audit.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c index 1b8ee590b4ca..f64582b0f623 100644 --- a/drivers/char/tty_audit.c +++ b/drivers/char/tty_audit.c @@ -188,25 +188,43 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) } /** - * tty_audit_push_task - Flush task's pending audit data + * tty_audit_push_task - Flush task's pending audit data + * @tsk: task pointer + * @loginuid: sender login uid + * @sessionid: sender session id + * + * Called with a ref on @tsk held. Try to lock sighand and get a + * reference to the tty audit buffer if available. + * Flush the buffer or return an appropriate error code. */ -void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid) +int tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid) { - struct tty_audit_buf *buf; + struct tty_audit_buf *buf = ERR_PTR(-EPERM); + unsigned long flags; - spin_lock_irq(&tsk->sighand->siglock); - buf = tsk->signal->tty_audit_buf; - if (buf) - atomic_inc(&buf->count); - spin_unlock_irq(&tsk->sighand->siglock); - if (!buf) - return; + if (!lock_task_sighand(tsk, &flags)) + return -ESRCH; + + if (tsk->signal->audit_tty) { + buf = tsk->signal->tty_audit_buf; + if (buf) + atomic_inc(&buf->count); + } + unlock_task_sighand(tsk, &flags); + + /* + * Return 0 when signal->audit_tty set + * but tsk->signal->tty_audit_buf == NULL. + */ + if (!buf || IS_ERR(buf)) + return PTR_ERR(buf); mutex_lock(&buf->mutex); tty_audit_buf_push(tsk, loginuid, sessionid, buf); mutex_unlock(&buf->mutex); tty_audit_buf_put(buf); + return 0; } /** -- cgit v1.2.1 From 96fd7ce58ffb5c7bf376796b5525ba3ea1c9d69f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 4 Nov 2010 11:10:29 -0700 Subject: TTY: create drivers/tty and move the tty core files there The tty code should be in its own subdirectory and not in the char driver with all of the cruft that is currently there. Based on work done by Arnd Bergmann Acked-by: Arnd Bergmann Cc: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/Makefile | 11 +- drivers/char/n_gsm.c | 2763 -------------------------------------- drivers/char/n_hdlc.c | 1007 -------------- drivers/char/n_r3964.c | 1264 ------------------ drivers/char/n_tty.c | 2121 ----------------------------- drivers/char/pty.c | 777 ----------- drivers/char/sysrq.c | 811 ----------- drivers/char/tty_audit.c | 358 ----- drivers/char/tty_buffer.c | 524 -------- drivers/char/tty_io.c | 3263 --------------------------------------------- drivers/char/tty_ioctl.c | 1179 ---------------- drivers/char/tty_ldisc.c | 915 ------------- drivers/char/tty_mutex.c | 47 - drivers/char/tty_port.c | 446 ------- 14 files changed, 1 insertion(+), 15485 deletions(-) delete mode 100644 drivers/char/n_gsm.c delete mode 100644 drivers/char/n_hdlc.c delete mode 100644 drivers/char/n_r3964.c delete mode 100644 drivers/char/n_tty.c delete mode 100644 drivers/char/pty.c delete mode 100644 drivers/char/sysrq.c delete mode 100644 drivers/char/tty_audit.c delete mode 100644 drivers/char/tty_buffer.c delete mode 100644 drivers/char/tty_io.c delete mode 100644 drivers/char/tty_ioctl.c delete mode 100644 drivers/char/tty_ldisc.c delete mode 100644 drivers/char/tty_mutex.c delete mode 100644 drivers/char/tty_port.c (limited to 'drivers/char') diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 3a9c01416839..f308494bfc90 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -7,19 +7,13 @@ # FONTMAPFILE = cp437.uni -obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o - -obj-y += tty_mutex.o -obj-$(CONFIG_LEGACY_PTYS) += pty.o -obj-$(CONFIG_UNIX98_PTYS) += pty.o +obj-y += mem.o random.o obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o obj-y += misc.o obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o -obj-$(CONFIG_AUDIT) += tty_audit.o -obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o @@ -41,8 +35,6 @@ obj-$(CONFIG_ISI) += isicom.o obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o -obj-$(CONFIG_N_HDLC) += n_hdlc.o -obj-$(CONFIG_N_GSM) += n_gsm.o obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o @@ -74,7 +66,6 @@ obj-$(CONFIG_PRINTER) += lp.o obj-$(CONFIG_APM_EMULATION) += apm-emulation.o obj-$(CONFIG_DTLK) += dtlk.o -obj-$(CONFIG_R3964) += n_r3964.o obj-$(CONFIG_APPLICOM) += applicom.o obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_RTC) += rtc.o diff --git a/drivers/char/n_gsm.c b/drivers/char/n_gsm.c deleted file mode 100644 index 04ef3ef0a422..000000000000 --- a/drivers/char/n_gsm.c +++ /dev/null @@ -1,2763 +0,0 @@ -/* - * n_gsm.c GSM 0710 tty multiplexor - * Copyright (c) 2009/10 Intel Corporation - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * * THIS IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE * - * - * TO DO: - * Mostly done: ioctls for setting modes/timing - * Partly done: hooks so you can pull off frames to non tty devs - * Restart DLCI 0 when it closes ? - * Test basic encoding - * Improve the tx engine - * Resolve tx side locking by adding a queue_head and routing - * all control traffic via it - * General tidy/document - * Review the locking/move to refcounts more (mux now moved to an - * alloc/free model ready) - * Use newest tty open/close port helpers and install hooks - * What to do about power functions ? - * Termios setting and negotiation - * Do we need a 'which mux are you' ioctl to correlate mux and tty sets - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int debug; -module_param(debug, int, 0600); - -#define T1 (HZ/10) -#define T2 (HZ/3) -#define N2 3 - -/* Use long timers for testing at low speed with debug on */ -#ifdef DEBUG_TIMING -#define T1 HZ -#define T2 (2 * HZ) -#endif - -/* Semi-arbitary buffer size limits. 0710 is normally run with 32-64 byte - limits so this is plenty */ -#define MAX_MRU 512 -#define MAX_MTU 512 - -/* - * Each block of data we have queued to go out is in the form of - * a gsm_msg which holds everything we need in a link layer independant - * format - */ - -struct gsm_msg { - struct gsm_msg *next; - u8 addr; /* DLCI address + flags */ - u8 ctrl; /* Control byte + flags */ - unsigned int len; /* Length of data block (can be zero) */ - unsigned char *data; /* Points into buffer but not at the start */ - unsigned char buffer[0]; -}; - -/* - * Each active data link has a gsm_dlci structure associated which ties - * the link layer to an optional tty (if the tty side is open). To avoid - * complexity right now these are only ever freed up when the mux is - * shut down. - * - * At the moment we don't free DLCI objects until the mux is torn down - * this avoid object life time issues but might be worth review later. - */ - -struct gsm_dlci { - struct gsm_mux *gsm; - int addr; - int state; -#define DLCI_CLOSED 0 -#define DLCI_OPENING 1 /* Sending SABM not seen UA */ -#define DLCI_OPEN 2 /* SABM/UA complete */ -#define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */ - - /* Link layer */ - spinlock_t lock; /* Protects the internal state */ - struct timer_list t1; /* Retransmit timer for SABM and UA */ - int retries; - /* Uplink tty if active */ - struct tty_port port; /* The tty bound to this DLCI if there is one */ - struct kfifo *fifo; /* Queue fifo for the DLCI */ - struct kfifo _fifo; /* For new fifo API porting only */ - int adaption; /* Adaption layer in use */ - u32 modem_rx; /* Our incoming virtual modem lines */ - u32 modem_tx; /* Our outgoing modem lines */ - int dead; /* Refuse re-open */ - /* Flow control */ - int throttled; /* Private copy of throttle state */ - int constipated; /* Throttle status for outgoing */ - /* Packetised I/O */ - struct sk_buff *skb; /* Frame being sent */ - struct sk_buff_head skb_list; /* Queued frames */ - /* Data handling callback */ - void (*data)(struct gsm_dlci *dlci, u8 *data, int len); -}; - -/* DLCI 0, 62/63 are special or reseved see gsmtty_open */ - -#define NUM_DLCI 64 - -/* - * DLCI 0 is used to pass control blocks out of band of the data - * flow (and with a higher link priority). One command can be outstanding - * at a time and we use this structure to manage them. They are created - * and destroyed by the user context, and updated by the receive paths - * and timers - */ - -struct gsm_control { - u8 cmd; /* Command we are issuing */ - u8 *data; /* Data for the command in case we retransmit */ - int len; /* Length of block for retransmission */ - int done; /* Done flag */ - int error; /* Error if any */ -}; - -/* - * Each GSM mux we have is represented by this structure. If we are - * operating as an ldisc then we use this structure as our ldisc - * state. We need to sort out lifetimes and locking with respect - * to the gsm mux array. For now we don't free DLCI objects that - * have been instantiated until the mux itself is terminated. - * - * To consider further: tty open versus mux shutdown. - */ - -struct gsm_mux { - struct tty_struct *tty; /* The tty our ldisc is bound to */ - spinlock_t lock; - - /* Events on the GSM channel */ - wait_queue_head_t event; - - /* Bits for GSM mode decoding */ - - /* Framing Layer */ - unsigned char *buf; - int state; -#define GSM_SEARCH 0 -#define GSM_START 1 -#define GSM_ADDRESS 2 -#define GSM_CONTROL 3 -#define GSM_LEN 4 -#define GSM_DATA 5 -#define GSM_FCS 6 -#define GSM_OVERRUN 7 - unsigned int len; - unsigned int address; - unsigned int count; - int escape; - int encoding; - u8 control; - u8 fcs; - u8 *txframe; /* TX framing buffer */ - - /* Methods for the receiver side */ - void (*receive)(struct gsm_mux *gsm, u8 ch); - void (*error)(struct gsm_mux *gsm, u8 ch, u8 flag); - /* And transmit side */ - int (*output)(struct gsm_mux *mux, u8 *data, int len); - - /* Link Layer */ - unsigned int mru; - unsigned int mtu; - int initiator; /* Did we initiate connection */ - int dead; /* Has the mux been shut down */ - struct gsm_dlci *dlci[NUM_DLCI]; - int constipated; /* Asked by remote to shut up */ - - spinlock_t tx_lock; - unsigned int tx_bytes; /* TX data outstanding */ -#define TX_THRESH_HI 8192 -#define TX_THRESH_LO 2048 - struct gsm_msg *tx_head; /* Pending data packets */ - struct gsm_msg *tx_tail; - - /* Control messages */ - struct timer_list t2_timer; /* Retransmit timer for commands */ - int cretries; /* Command retry counter */ - struct gsm_control *pending_cmd;/* Our current pending command */ - spinlock_t control_lock; /* Protects the pending command */ - - /* Configuration */ - int adaption; /* 1 or 2 supported */ - u8 ftype; /* UI or UIH */ - int t1, t2; /* Timers in 1/100th of a sec */ - int n2; /* Retry count */ - - /* Statistics (not currently exposed) */ - unsigned long bad_fcs; - unsigned long malformed; - unsigned long io_error; - unsigned long bad_size; - unsigned long unsupported; -}; - - -/* - * Mux objects - needed so that we can translate a tty index into the - * relevant mux and DLCI. - */ - -#define MAX_MUX 4 /* 256 minors */ -static struct gsm_mux *gsm_mux[MAX_MUX]; /* GSM muxes */ -static spinlock_t gsm_mux_lock; - -/* - * This section of the driver logic implements the GSM encodings - * both the basic and the 'advanced'. Reliable transport is not - * supported. - */ - -#define CR 0x02 -#define EA 0x01 -#define PF 0x10 - -/* I is special: the rest are ..*/ -#define RR 0x01 -#define UI 0x03 -#define RNR 0x05 -#define REJ 0x09 -#define DM 0x0F -#define SABM 0x2F -#define DISC 0x43 -#define UA 0x63 -#define UIH 0xEF - -/* Channel commands */ -#define CMD_NSC 0x09 -#define CMD_TEST 0x11 -#define CMD_PSC 0x21 -#define CMD_RLS 0x29 -#define CMD_FCOFF 0x31 -#define CMD_PN 0x41 -#define CMD_RPN 0x49 -#define CMD_FCON 0x51 -#define CMD_CLD 0x61 -#define CMD_SNC 0x69 -#define CMD_MSC 0x71 - -/* Virtual modem bits */ -#define MDM_FC 0x01 -#define MDM_RTC 0x02 -#define MDM_RTR 0x04 -#define MDM_IC 0x20 -#define MDM_DV 0x40 - -#define GSM0_SOF 0xF9 -#define GSM1_SOF 0x7E -#define GSM1_ESCAPE 0x7D -#define GSM1_ESCAPE_BITS 0x20 -#define XON 0x11 -#define XOFF 0x13 - -static const struct tty_port_operations gsm_port_ops; - -/* - * CRC table for GSM 0710 - */ - -static const u8 gsm_fcs8[256] = { - 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, - 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, - 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, - 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, - 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, - 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, - 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, - 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, - 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, - 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, - 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, - 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, - 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, - 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, - 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, - 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, - 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, - 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, - 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, - 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, - 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, - 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, - 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, - 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, - 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, - 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, - 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, - 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, - 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, - 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, - 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, - 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF -}; - -#define INIT_FCS 0xFF -#define GOOD_FCS 0xCF - -/** - * gsm_fcs_add - update FCS - * @fcs: Current FCS - * @c: Next data - * - * Update the FCS to include c. Uses the algorithm in the specification - * notes. - */ - -static inline u8 gsm_fcs_add(u8 fcs, u8 c) -{ - return gsm_fcs8[fcs ^ c]; -} - -/** - * gsm_fcs_add_block - update FCS for a block - * @fcs: Current FCS - * @c: buffer of data - * @len: length of buffer - * - * Update the FCS to include c. Uses the algorithm in the specification - * notes. - */ - -static inline u8 gsm_fcs_add_block(u8 fcs, u8 *c, int len) -{ - while (len--) - fcs = gsm_fcs8[fcs ^ *c++]; - return fcs; -} - -/** - * gsm_read_ea - read a byte into an EA - * @val: variable holding value - * c: byte going into the EA - * - * Processes one byte of an EA. Updates the passed variable - * and returns 1 if the EA is now completely read - */ - -static int gsm_read_ea(unsigned int *val, u8 c) -{ - /* Add the next 7 bits into the value */ - *val <<= 7; - *val |= c >> 1; - /* Was this the last byte of the EA 1 = yes*/ - return c & EA; -} - -/** - * gsm_encode_modem - encode modem data bits - * @dlci: DLCI to encode from - * - * Returns the correct GSM encoded modem status bits (6 bit field) for - * the current status of the DLCI and attached tty object - */ - -static u8 gsm_encode_modem(const struct gsm_dlci *dlci) -{ - u8 modembits = 0; - /* FC is true flow control not modem bits */ - if (dlci->throttled) - modembits |= MDM_FC; - if (dlci->modem_tx & TIOCM_DTR) - modembits |= MDM_RTC; - if (dlci->modem_tx & TIOCM_RTS) - modembits |= MDM_RTR; - if (dlci->modem_tx & TIOCM_RI) - modembits |= MDM_IC; - if (dlci->modem_tx & TIOCM_CD) - modembits |= MDM_DV; - return modembits; -} - -/** - * gsm_print_packet - display a frame for debug - * @hdr: header to print before decode - * @addr: address EA from the frame - * @cr: C/R bit from the frame - * @control: control including PF bit - * @data: following data bytes - * @dlen: length of data - * - * Displays a packet in human readable format for debugging purposes. The - * style is based on amateur radio LAP-B dump display. - */ - -static void gsm_print_packet(const char *hdr, int addr, int cr, - u8 control, const u8 *data, int dlen) -{ - if (!(debug & 1)) - return; - - printk(KERN_INFO "%s %d) %c: ", hdr, addr, "RC"[cr]); - - switch (control & ~PF) { - case SABM: - printk(KERN_CONT "SABM"); - break; - case UA: - printk(KERN_CONT "UA"); - break; - case DISC: - printk(KERN_CONT "DISC"); - break; - case DM: - printk(KERN_CONT "DM"); - break; - case UI: - printk(KERN_CONT "UI"); - break; - case UIH: - printk(KERN_CONT "UIH"); - break; - default: - if (!(control & 0x01)) { - printk(KERN_CONT "I N(S)%d N(R)%d", - (control & 0x0E) >> 1, (control & 0xE)>> 5); - } else switch (control & 0x0F) { - case RR: - printk("RR(%d)", (control & 0xE0) >> 5); - break; - case RNR: - printk("RNR(%d)", (control & 0xE0) >> 5); - break; - case REJ: - printk("REJ(%d)", (control & 0xE0) >> 5); - break; - default: - printk(KERN_CONT "[%02X]", control); - } - } - - if (control & PF) - printk(KERN_CONT "(P)"); - else - printk(KERN_CONT "(F)"); - - if (dlen) { - int ct = 0; - while (dlen--) { - if (ct % 8 == 0) - printk(KERN_CONT "\n "); - printk(KERN_CONT "%02X ", *data++); - ct++; - } - } - printk(KERN_CONT "\n"); -} - - -/* - * Link level transmission side - */ - -/** - * gsm_stuff_packet - bytestuff a packet - * @ibuf: input - * @obuf: output - * @len: length of input - * - * Expand a buffer by bytestuffing it. The worst case size change - * is doubling and the caller is responsible for handing out - * suitable sized buffers. - */ - -static int gsm_stuff_frame(const u8 *input, u8 *output, int len) -{ - int olen = 0; - while (len--) { - if (*input == GSM1_SOF || *input == GSM1_ESCAPE - || *input == XON || *input == XOFF) { - *output++ = GSM1_ESCAPE; - *output++ = *input++ ^ GSM1_ESCAPE_BITS; - olen++; - } else - *output++ = *input++; - olen++; - } - return olen; -} - -static void hex_packet(const unsigned char *p, int len) -{ - int i; - for (i = 0; i < len; i++) { - if (i && (i % 16) == 0) - printk("\n"); - printk("%02X ", *p++); - } - printk("\n"); -} - -/** - * gsm_send - send a control frame - * @gsm: our GSM mux - * @addr: address for control frame - * @cr: command/response bit - * @control: control byte including PF bit - * - * Format up and transmit a control frame. These do not go via the - * queueing logic as they should be transmitted ahead of data when - * they are needed. - * - * FIXME: Lock versus data TX path - */ - -static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control) -{ - int len; - u8 cbuf[10]; - u8 ibuf[3]; - - switch (gsm->encoding) { - case 0: - cbuf[0] = GSM0_SOF; - cbuf[1] = (addr << 2) | (cr << 1) | EA; - cbuf[2] = control; - cbuf[3] = EA; /* Length of data = 0 */ - cbuf[4] = 0xFF - gsm_fcs_add_block(INIT_FCS, cbuf + 1, 3); - cbuf[5] = GSM0_SOF; - len = 6; - break; - case 1: - case 2: - /* Control frame + packing (but not frame stuffing) in mode 1 */ - ibuf[0] = (addr << 2) | (cr << 1) | EA; - ibuf[1] = control; - ibuf[2] = 0xFF - gsm_fcs_add_block(INIT_FCS, ibuf, 2); - /* Stuffing may double the size worst case */ - len = gsm_stuff_frame(ibuf, cbuf + 1, 3); - /* Now add the SOF markers */ - cbuf[0] = GSM1_SOF; - cbuf[len + 1] = GSM1_SOF; - /* FIXME: we can omit the lead one in many cases */ - len += 2; - break; - default: - WARN_ON(1); - return; - } - gsm->output(gsm, cbuf, len); - gsm_print_packet("-->", addr, cr, control, NULL, 0); -} - -/** - * gsm_response - send a control response - * @gsm: our GSM mux - * @addr: address for control frame - * @control: control byte including PF bit - * - * Format up and transmit a link level response frame. - */ - -static inline void gsm_response(struct gsm_mux *gsm, int addr, int control) -{ - gsm_send(gsm, addr, 0, control); -} - -/** - * gsm_command - send a control command - * @gsm: our GSM mux - * @addr: address for control frame - * @control: control byte including PF bit - * - * Format up and transmit a link level command frame. - */ - -static inline void gsm_command(struct gsm_mux *gsm, int addr, int control) -{ - gsm_send(gsm, addr, 1, control); -} - -/* Data transmission */ - -#define HDR_LEN 6 /* ADDR CTRL [LEN.2] DATA FCS */ - -/** - * gsm_data_alloc - allocate data frame - * @gsm: GSM mux - * @addr: DLCI address - * @len: length excluding header and FCS - * @ctrl: control byte - * - * Allocate a new data buffer for sending frames with data. Space is left - * at the front for header bytes but that is treated as an implementation - * detail and not for the high level code to use - */ - -static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, - u8 ctrl) -{ - struct gsm_msg *m = kmalloc(sizeof(struct gsm_msg) + len + HDR_LEN, - GFP_ATOMIC); - if (m == NULL) - return NULL; - m->data = m->buffer + HDR_LEN - 1; /* Allow for FCS */ - m->len = len; - m->addr = addr; - m->ctrl = ctrl; - m->next = NULL; - return m; -} - -/** - * gsm_data_kick - poke the queue - * @gsm: GSM Mux - * - * The tty device has called us to indicate that room has appeared in - * the transmit queue. Ram more data into the pipe if we have any - * - * FIXME: lock against link layer control transmissions - */ - -static void gsm_data_kick(struct gsm_mux *gsm) -{ - struct gsm_msg *msg = gsm->tx_head; - int len; - int skip_sof = 0; - - /* FIXME: We need to apply this solely to data messages */ - if (gsm->constipated) - return; - - while (gsm->tx_head != NULL) { - msg = gsm->tx_head; - if (gsm->encoding != 0) { - gsm->txframe[0] = GSM1_SOF; - len = gsm_stuff_frame(msg->data, - gsm->txframe + 1, msg->len); - gsm->txframe[len + 1] = GSM1_SOF; - len += 2; - } else { - gsm->txframe[0] = GSM0_SOF; - memcpy(gsm->txframe + 1 , msg->data, msg->len); - gsm->txframe[msg->len + 1] = GSM0_SOF; - len = msg->len + 2; - } - - if (debug & 4) { - printk("gsm_data_kick: \n"); - hex_packet(gsm->txframe, len); - } - - if (gsm->output(gsm, gsm->txframe + skip_sof, - len - skip_sof) < 0) - break; - /* FIXME: Can eliminate one SOF in many more cases */ - gsm->tx_head = msg->next; - if (gsm->tx_head == NULL) - gsm->tx_tail = NULL; - gsm->tx_bytes -= msg->len; - kfree(msg); - /* For a burst of frames skip the extra SOF within the - burst */ - skip_sof = 1; - } -} - -/** - * __gsm_data_queue - queue a UI or UIH frame - * @dlci: DLCI sending the data - * @msg: message queued - * - * Add data to the transmit queue and try and get stuff moving - * out of the mux tty if not already doing so. The Caller must hold - * the gsm tx lock. - */ - -static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) -{ - struct gsm_mux *gsm = dlci->gsm; - u8 *dp = msg->data; - u8 *fcs = dp + msg->len; - - /* Fill in the header */ - if (gsm->encoding == 0) { - if (msg->len < 128) - *--dp = (msg->len << 1) | EA; - else { - *--dp = (msg->len >> 6) | EA; - *--dp = (msg->len & 127) << 1; - } - } - - *--dp = msg->ctrl; - if (gsm->initiator) - *--dp = (msg->addr << 2) | 2 | EA; - else - *--dp = (msg->addr << 2) | EA; - *fcs = gsm_fcs_add_block(INIT_FCS, dp , msg->data - dp); - /* Ugly protocol layering violation */ - if (msg->ctrl == UI || msg->ctrl == (UI|PF)) - *fcs = gsm_fcs_add_block(*fcs, msg->data, msg->len); - *fcs = 0xFF - *fcs; - - gsm_print_packet("Q> ", msg->addr, gsm->initiator, msg->ctrl, - msg->data, msg->len); - - /* Move the header back and adjust the length, also allow for the FCS - now tacked on the end */ - msg->len += (msg->data - dp) + 1; - msg->data = dp; - - /* Add to the actual output queue */ - if (gsm->tx_tail) - gsm->tx_tail->next = msg; - else - gsm->tx_head = msg; - gsm->tx_tail = msg; - gsm->tx_bytes += msg->len; - gsm_data_kick(gsm); -} - -/** - * gsm_data_queue - queue a UI or UIH frame - * @dlci: DLCI sending the data - * @msg: message queued - * - * Add data to the transmit queue and try and get stuff moving - * out of the mux tty if not already doing so. Take the - * the gsm tx lock and dlci lock. - */ - -static void gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) -{ - unsigned long flags; - spin_lock_irqsave(&dlci->gsm->tx_lock, flags); - __gsm_data_queue(dlci, msg); - spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); -} - -/** - * gsm_dlci_data_output - try and push data out of a DLCI - * @gsm: mux - * @dlci: the DLCI to pull data from - * - * Pull data from a DLCI and send it into the transmit queue if there - * is data. Keep to the MRU of the mux. This path handles the usual tty - * interface which is a byte stream with optional modem data. - * - * Caller must hold the tx_lock of the mux. - */ - -static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) -{ - struct gsm_msg *msg; - u8 *dp; - int len, size; - int h = dlci->adaption - 1; - - len = kfifo_len(dlci->fifo); - if (len == 0) - return 0; - - /* MTU/MRU count only the data bits */ - if (len > gsm->mtu) - len = gsm->mtu; - - size = len + h; - - msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); - /* FIXME: need a timer or something to kick this so it can't - get stuck with no work outstanding and no buffer free */ - if (msg == NULL) - return -ENOMEM; - dp = msg->data; - switch (dlci->adaption) { - case 1: /* Unstructured */ - break; - case 2: /* Unstructed with modem bits. Always one byte as we never - send inline break data */ - *dp += gsm_encode_modem(dlci); - len--; - break; - } - WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len); - __gsm_data_queue(dlci, msg); - /* Bytes of data we used up */ - return size; -} - -/** - * gsm_dlci_data_output_framed - try and push data out of a DLCI - * @gsm: mux - * @dlci: the DLCI to pull data from - * - * Pull data from a DLCI and send it into the transmit queue if there - * is data. Keep to the MRU of the mux. This path handles framed data - * queued as skbuffs to the DLCI. - * - * Caller must hold the tx_lock of the mux. - */ - -static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, - struct gsm_dlci *dlci) -{ - struct gsm_msg *msg; - u8 *dp; - int len, size; - int last = 0, first = 0; - int overhead = 0; - - /* One byte per frame is used for B/F flags */ - if (dlci->adaption == 4) - overhead = 1; - - /* dlci->skb is locked by tx_lock */ - if (dlci->skb == NULL) { - dlci->skb = skb_dequeue(&dlci->skb_list); - if (dlci->skb == NULL) - return 0; - first = 1; - } - len = dlci->skb->len + overhead; - - /* MTU/MRU count only the data bits */ - if (len > gsm->mtu) { - if (dlci->adaption == 3) { - /* Over long frame, bin it */ - kfree_skb(dlci->skb); - dlci->skb = NULL; - return 0; - } - len = gsm->mtu; - } else - last = 1; - - size = len + overhead; - msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); - - /* FIXME: need a timer or something to kick this so it can't - get stuck with no work outstanding and no buffer free */ - if (msg == NULL) - return -ENOMEM; - dp = msg->data; - - if (dlci->adaption == 4) { /* Interruptible framed (Packetised Data) */ - /* Flag byte to carry the start/end info */ - *dp++ = last << 7 | first << 6 | 1; /* EA */ - len--; - } - memcpy(dp, skb_pull(dlci->skb, len), len); - __gsm_data_queue(dlci, msg); - if (last) - dlci->skb = NULL; - return size; -} - -/** - * gsm_dlci_data_sweep - look for data to send - * @gsm: the GSM mux - * - * Sweep the GSM mux channels in priority order looking for ones with - * data to send. We could do with optimising this scan a bit. We aim - * to fill the queue totally or up to TX_THRESH_HI bytes. Once we hit - * TX_THRESH_LO we get called again - * - * FIXME: We should round robin between groups and in theory you can - * renegotiate DLCI priorities with optional stuff. Needs optimising. - */ - -static void gsm_dlci_data_sweep(struct gsm_mux *gsm) -{ - int len; - /* Priority ordering: We should do priority with RR of the groups */ - int i = 1; - - while (i < NUM_DLCI) { - struct gsm_dlci *dlci; - - if (gsm->tx_bytes > TX_THRESH_HI) - break; - dlci = gsm->dlci[i]; - if (dlci == NULL || dlci->constipated) { - i++; - continue; - } - if (dlci->adaption < 3) - len = gsm_dlci_data_output(gsm, dlci); - else - len = gsm_dlci_data_output_framed(gsm, dlci); - if (len < 0) - break; - /* DLCI empty - try the next */ - if (len == 0) - i++; - } -} - -/** - * gsm_dlci_data_kick - transmit if possible - * @dlci: DLCI to kick - * - * Transmit data from this DLCI if the queue is empty. We can't rely on - * a tty wakeup except when we filled the pipe so we need to fire off - * new data ourselves in other cases. - */ - -static void gsm_dlci_data_kick(struct gsm_dlci *dlci) -{ - unsigned long flags; - - spin_lock_irqsave(&dlci->gsm->tx_lock, flags); - /* If we have nothing running then we need to fire up */ - if (dlci->gsm->tx_bytes == 0) - gsm_dlci_data_output(dlci->gsm, dlci); - else if (dlci->gsm->tx_bytes < TX_THRESH_LO) - gsm_dlci_data_sweep(dlci->gsm); - spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); -} - -/* - * Control message processing - */ - - -/** - * gsm_control_reply - send a response frame to a control - * @gsm: gsm channel - * @cmd: the command to use - * @data: data to follow encoded info - * @dlen: length of data - * - * Encode up and queue a UI/UIH frame containing our response. - */ - -static void gsm_control_reply(struct gsm_mux *gsm, int cmd, u8 *data, - int dlen) -{ - struct gsm_msg *msg; - msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->ftype); - msg->data[0] = (cmd & 0xFE) << 1 | EA; /* Clear C/R */ - msg->data[1] = (dlen << 1) | EA; - memcpy(msg->data + 2, data, dlen); - gsm_data_queue(gsm->dlci[0], msg); -} - -/** - * gsm_process_modem - process received modem status - * @tty: virtual tty bound to the DLCI - * @dlci: DLCI to affect - * @modem: modem bits (full EA) - * - * Used when a modem control message or line state inline in adaption - * layer 2 is processed. Sort out the local modem state and throttles - */ - -static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, - u32 modem) -{ - int mlines = 0; - u8 brk = modem >> 6; - - /* Flow control/ready to communicate */ - if (modem & MDM_FC) { - /* Need to throttle our output on this device */ - dlci->constipated = 1; - } - if (modem & MDM_RTC) { - mlines |= TIOCM_DSR | TIOCM_DTR; - dlci->constipated = 0; - gsm_dlci_data_kick(dlci); - } - /* Map modem bits */ - if (modem & MDM_RTR) - mlines |= TIOCM_RTS | TIOCM_CTS; - if (modem & MDM_IC) - mlines |= TIOCM_RI; - if (modem & MDM_DV) - mlines |= TIOCM_CD; - - /* Carrier drop -> hangup */ - if (tty) { - if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD)) - if (!(tty->termios->c_cflag & CLOCAL)) - tty_hangup(tty); - if (brk & 0x01) - tty_insert_flip_char(tty, 0, TTY_BREAK); - } - dlci->modem_rx = mlines; -} - -/** - * gsm_control_modem - modem status received - * @gsm: GSM channel - * @data: data following command - * @clen: command length - * - * We have received a modem status control message. This is used by - * the GSM mux protocol to pass virtual modem line status and optionally - * to indicate break signals. Unpack it, convert to Linux representation - * and if need be stuff a break message down the tty. - */ - -static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) -{ - unsigned int addr = 0; - unsigned int modem = 0; - struct gsm_dlci *dlci; - int len = clen; - u8 *dp = data; - struct tty_struct *tty; - - while (gsm_read_ea(&addr, *dp++) == 0) { - len--; - if (len == 0) - return; - } - /* Must be at least one byte following the EA */ - len--; - if (len <= 0) - return; - - addr >>= 1; - /* Closed port, or invalid ? */ - if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL) - return; - dlci = gsm->dlci[addr]; - - while (gsm_read_ea(&modem, *dp++) == 0) { - len--; - if (len == 0) - return; - } - tty = tty_port_tty_get(&dlci->port); - gsm_process_modem(tty, dlci, modem); - if (tty) { - tty_wakeup(tty); - tty_kref_put(tty); - } - gsm_control_reply(gsm, CMD_MSC, data, clen); -} - -/** - * gsm_control_rls - remote line status - * @gsm: GSM channel - * @data: data bytes - * @clen: data length - * - * The modem sends us a two byte message on the control channel whenever - * it wishes to send us an error state from the virtual link. Stuff - * this into the uplink tty if present - */ - -static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen) -{ - struct tty_struct *tty; - unsigned int addr = 0 ; - u8 bits; - int len = clen; - u8 *dp = data; - - while (gsm_read_ea(&addr, *dp++) == 0) { - len--; - if (len == 0) - return; - } - /* Must be at least one byte following ea */ - len--; - if (len <= 0) - return; - addr >>= 1; - /* Closed port, or invalid ? */ - if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL) - return; - /* No error ? */ - bits = *dp; - if ((bits & 1) == 0) - return; - /* See if we have an uplink tty */ - tty = tty_port_tty_get(&gsm->dlci[addr]->port); - - if (tty) { - if (bits & 2) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - if (bits & 4) - tty_insert_flip_char(tty, 0, TTY_PARITY); - if (bits & 8) - tty_insert_flip_char(tty, 0, TTY_FRAME); - tty_flip_buffer_push(tty); - tty_kref_put(tty); - } - gsm_control_reply(gsm, CMD_RLS, data, clen); -} - -static void gsm_dlci_begin_close(struct gsm_dlci *dlci); - -/** - * gsm_control_message - DLCI 0 control processing - * @gsm: our GSM mux - * @command: the command EA - * @data: data beyond the command/length EAs - * @clen: length - * - * Input processor for control messages from the other end of the link. - * Processes the incoming request and queues a response frame or an - * NSC response if not supported - */ - -static void gsm_control_message(struct gsm_mux *gsm, unsigned int command, - u8 *data, int clen) -{ - u8 buf[1]; - switch (command) { - case CMD_CLD: { - struct gsm_dlci *dlci = gsm->dlci[0]; - /* Modem wishes to close down */ - if (dlci) { - dlci->dead = 1; - gsm->dead = 1; - gsm_dlci_begin_close(dlci); - } - } - break; - case CMD_TEST: - /* Modem wishes to test, reply with the data */ - gsm_control_reply(gsm, CMD_TEST, data, clen); - break; - case CMD_FCON: - /* Modem wants us to STFU */ - gsm->constipated = 1; - gsm_control_reply(gsm, CMD_FCON, NULL, 0); - break; - case CMD_FCOFF: - /* Modem can accept data again */ - gsm->constipated = 0; - gsm_control_reply(gsm, CMD_FCOFF, NULL, 0); - /* Kick the link in case it is idling */ - gsm_data_kick(gsm); - break; - case CMD_MSC: - /* Out of band modem line change indicator for a DLCI */ - gsm_control_modem(gsm, data, clen); - break; - case CMD_RLS: - /* Out of band error reception for a DLCI */ - gsm_control_rls(gsm, data, clen); - break; - case CMD_PSC: - /* Modem wishes to enter power saving state */ - gsm_control_reply(gsm, CMD_PSC, NULL, 0); - break; - /* Optional unsupported commands */ - case CMD_PN: /* Parameter negotiation */ - case CMD_RPN: /* Remote port negotation */ - case CMD_SNC: /* Service negotation command */ - default: - /* Reply to bad commands with an NSC */ - buf[0] = command; - gsm_control_reply(gsm, CMD_NSC, buf, 1); - break; - } -} - -/** - * gsm_control_response - process a response to our control - * @gsm: our GSM mux - * @command: the command (response) EA - * @data: data beyond the command/length EA - * @clen: length - * - * Process a response to an outstanding command. We only allow a single - * control message in flight so this is fairly easy. All the clean up - * is done by the caller, we just update the fields, flag it as done - * and return - */ - -static void gsm_control_response(struct gsm_mux *gsm, unsigned int command, - u8 *data, int clen) -{ - struct gsm_control *ctrl; - unsigned long flags; - - spin_lock_irqsave(&gsm->control_lock, flags); - - ctrl = gsm->pending_cmd; - /* Does the reply match our command */ - command |= 1; - if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) { - /* Our command was replied to, kill the retry timer */ - del_timer(&gsm->t2_timer); - gsm->pending_cmd = NULL; - /* Rejected by the other end */ - if (command == CMD_NSC) - ctrl->error = -EOPNOTSUPP; - ctrl->done = 1; - wake_up(&gsm->event); - } - spin_unlock_irqrestore(&gsm->control_lock, flags); -} - -/** - * gsm_control_transmit - send control packet - * @gsm: gsm mux - * @ctrl: frame to send - * - * Send out a pending control command (called under control lock) - */ - -static void gsm_control_transmit(struct gsm_mux *gsm, struct gsm_control *ctrl) -{ - struct gsm_msg *msg = gsm_data_alloc(gsm, 0, ctrl->len + 1, - gsm->ftype|PF); - if (msg == NULL) - return; - msg->data[0] = (ctrl->cmd << 1) | 2 | EA; /* command */ - memcpy(msg->data + 1, ctrl->data, ctrl->len); - gsm_data_queue(gsm->dlci[0], msg); -} - -/** - * gsm_control_retransmit - retransmit a control frame - * @data: pointer to our gsm object - * - * Called off the T2 timer expiry in order to retransmit control frames - * that have been lost in the system somewhere. The control_lock protects - * us from colliding with another sender or a receive completion event. - * In that situation the timer may still occur in a small window but - * gsm->pending_cmd will be NULL and we just let the timer expire. - */ - -static void gsm_control_retransmit(unsigned long data) -{ - struct gsm_mux *gsm = (struct gsm_mux *)data; - struct gsm_control *ctrl; - unsigned long flags; - spin_lock_irqsave(&gsm->control_lock, flags); - ctrl = gsm->pending_cmd; - if (ctrl) { - gsm->cretries--; - if (gsm->cretries == 0) { - gsm->pending_cmd = NULL; - ctrl->error = -ETIMEDOUT; - ctrl->done = 1; - spin_unlock_irqrestore(&gsm->control_lock, flags); - wake_up(&gsm->event); - return; - } - gsm_control_transmit(gsm, ctrl); - mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); - } - spin_unlock_irqrestore(&gsm->control_lock, flags); -} - -/** - * gsm_control_send - send a control frame on DLCI 0 - * @gsm: the GSM channel - * @command: command to send including CR bit - * @data: bytes of data (must be kmalloced) - * @len: length of the block to send - * - * Queue and dispatch a control command. Only one command can be - * active at a time. In theory more can be outstanding but the matching - * gets really complicated so for now stick to one outstanding. - */ - -static struct gsm_control *gsm_control_send(struct gsm_mux *gsm, - unsigned int command, u8 *data, int clen) -{ - struct gsm_control *ctrl = kzalloc(sizeof(struct gsm_control), - GFP_KERNEL); - unsigned long flags; - if (ctrl == NULL) - return NULL; -retry: - wait_event(gsm->event, gsm->pending_cmd == NULL); - spin_lock_irqsave(&gsm->control_lock, flags); - if (gsm->pending_cmd != NULL) { - spin_unlock_irqrestore(&gsm->control_lock, flags); - goto retry; - } - ctrl->cmd = command; - ctrl->data = data; - ctrl->len = clen; - gsm->pending_cmd = ctrl; - gsm->cretries = gsm->n2; - mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); - gsm_control_transmit(gsm, ctrl); - spin_unlock_irqrestore(&gsm->control_lock, flags); - return ctrl; -} - -/** - * gsm_control_wait - wait for a control to finish - * @gsm: GSM mux - * @control: control we are waiting on - * - * Waits for the control to complete or time out. Frees any used - * resources and returns 0 for success, or an error if the remote - * rejected or ignored the request. - */ - -static int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control) -{ - int err; - wait_event(gsm->event, control->done == 1); - err = control->error; - kfree(control); - return err; -} - - -/* - * DLCI level handling: Needs krefs - */ - -/* - * State transitions and timers - */ - -/** - * gsm_dlci_close - a DLCI has closed - * @dlci: DLCI that closed - * - * Perform processing when moving a DLCI into closed state. If there - * is an attached tty this is hung up - */ - -static void gsm_dlci_close(struct gsm_dlci *dlci) -{ - del_timer(&dlci->t1); - if (debug & 8) - printk("DLCI %d goes closed.\n", dlci->addr); - dlci->state = DLCI_CLOSED; - if (dlci->addr != 0) { - struct tty_struct *tty = tty_port_tty_get(&dlci->port); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } - kfifo_reset(dlci->fifo); - } else - dlci->gsm->dead = 1; - wake_up(&dlci->gsm->event); - /* A DLCI 0 close is a MUX termination so we need to kick that - back to userspace somehow */ -} - -/** - * gsm_dlci_open - a DLCI has opened - * @dlci: DLCI that opened - * - * Perform processing when moving a DLCI into open state. - */ - -static void gsm_dlci_open(struct gsm_dlci *dlci) -{ - /* Note that SABM UA .. SABM UA first UA lost can mean that we go - open -> open */ - del_timer(&dlci->t1); - /* This will let a tty open continue */ - dlci->state = DLCI_OPEN; - if (debug & 8) - printk("DLCI %d goes open.\n", dlci->addr); - wake_up(&dlci->gsm->event); -} - -/** - * gsm_dlci_t1 - T1 timer expiry - * @dlci: DLCI that opened - * - * The T1 timer handles retransmits of control frames (essentially of - * SABM and DISC). We resend the command until the retry count runs out - * in which case an opening port goes back to closed and a closing port - * is simply put into closed state (any further frames from the other - * end will get a DM response) - */ - -static void gsm_dlci_t1(unsigned long data) -{ - struct gsm_dlci *dlci = (struct gsm_dlci *)data; - struct gsm_mux *gsm = dlci->gsm; - - switch (dlci->state) { - case DLCI_OPENING: - dlci->retries--; - if (dlci->retries) { - gsm_command(dlci->gsm, dlci->addr, SABM|PF); - mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); - } else - gsm_dlci_close(dlci); - break; - case DLCI_CLOSING: - dlci->retries--; - if (dlci->retries) { - gsm_command(dlci->gsm, dlci->addr, DISC|PF); - mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); - } else - gsm_dlci_close(dlci); - break; - } -} - -/** - * gsm_dlci_begin_open - start channel open procedure - * @dlci: DLCI to open - * - * Commence opening a DLCI from the Linux side. We issue SABM messages - * to the modem which should then reply with a UA, at which point we - * will move into open state. Opening is done asynchronously with retry - * running off timers and the responses. - */ - -static void gsm_dlci_begin_open(struct gsm_dlci *dlci) -{ - struct gsm_mux *gsm = dlci->gsm; - if (dlci->state == DLCI_OPEN || dlci->state == DLCI_OPENING) - return; - dlci->retries = gsm->n2; - dlci->state = DLCI_OPENING; - gsm_command(dlci->gsm, dlci->addr, SABM|PF); - mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); -} - -/** - * gsm_dlci_begin_close - start channel open procedure - * @dlci: DLCI to open - * - * Commence closing a DLCI from the Linux side. We issue DISC messages - * to the modem which should then reply with a UA, at which point we - * will move into closed state. Closing is done asynchronously with retry - * off timers. We may also receive a DM reply from the other end which - * indicates the channel was already closed. - */ - -static void gsm_dlci_begin_close(struct gsm_dlci *dlci) -{ - struct gsm_mux *gsm = dlci->gsm; - if (dlci->state == DLCI_CLOSED || dlci->state == DLCI_CLOSING) - return; - dlci->retries = gsm->n2; - dlci->state = DLCI_CLOSING; - gsm_command(dlci->gsm, dlci->addr, DISC|PF); - mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); -} - -/** - * gsm_dlci_data - data arrived - * @dlci: channel - * @data: block of bytes received - * @len: length of received block - * - * A UI or UIH frame has arrived which contains data for a channel - * other than the control channel. If the relevant virtual tty is - * open we shovel the bits down it, if not we drop them. - */ - -static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int len) -{ - /* krefs .. */ - struct tty_port *port = &dlci->port; - struct tty_struct *tty = tty_port_tty_get(port); - unsigned int modem = 0; - - if (debug & 16) - printk("%d bytes for tty %p\n", len, tty); - if (tty) { - switch (dlci->adaption) { - /* Unsupported types */ - /* Packetised interruptible data */ - case 4: - break; - /* Packetised uininterruptible voice/data */ - case 3: - break; - /* Asynchronous serial with line state in each frame */ - case 2: - while (gsm_read_ea(&modem, *data++) == 0) { - len--; - if (len == 0) - return; - } - gsm_process_modem(tty, dlci, modem); - /* Line state will go via DLCI 0 controls only */ - case 1: - default: - tty_insert_flip_string(tty, data, len); - tty_flip_buffer_push(tty); - } - tty_kref_put(tty); - } -} - -/** - * gsm_dlci_control - data arrived on control channel - * @dlci: channel - * @data: block of bytes received - * @len: length of received block - * - * A UI or UIH frame has arrived which contains data for DLCI 0 the - * control channel. This should contain a command EA followed by - * control data bytes. The command EA contains a command/response bit - * and we divide up the work accordingly. - */ - -static void gsm_dlci_command(struct gsm_dlci *dlci, u8 *data, int len) -{ - /* See what command is involved */ - unsigned int command = 0; - while (len-- > 0) { - if (gsm_read_ea(&command, *data++) == 1) { - int clen = *data++; - len--; - /* FIXME: this is properly an EA */ - clen >>= 1; - /* Malformed command ? */ - if (clen > len) - return; - if (command & 1) - gsm_control_message(dlci->gsm, command, - data, clen); - else - gsm_control_response(dlci->gsm, command, - data, clen); - return; - } - } -} - -/* - * Allocate/Free DLCI channels - */ - -/** - * gsm_dlci_alloc - allocate a DLCI - * @gsm: GSM mux - * @addr: address of the DLCI - * - * Allocate and install a new DLCI object into the GSM mux. - * - * FIXME: review locking races - */ - -static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) -{ - struct gsm_dlci *dlci = kzalloc(sizeof(struct gsm_dlci), GFP_ATOMIC); - if (dlci == NULL) - return NULL; - spin_lock_init(&dlci->lock); - dlci->fifo = &dlci->_fifo; - if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) { - kfree(dlci); - return NULL; - } - - skb_queue_head_init(&dlci->skb_list); - init_timer(&dlci->t1); - dlci->t1.function = gsm_dlci_t1; - dlci->t1.data = (unsigned long)dlci; - tty_port_init(&dlci->port); - dlci->port.ops = &gsm_port_ops; - dlci->gsm = gsm; - dlci->addr = addr; - dlci->adaption = gsm->adaption; - dlci->state = DLCI_CLOSED; - if (addr) - dlci->data = gsm_dlci_data; - else - dlci->data = gsm_dlci_command; - gsm->dlci[addr] = dlci; - return dlci; -} - -/** - * gsm_dlci_free - release DLCI - * @dlci: DLCI to destroy - * - * Free up a DLCI. Currently to keep the lifetime rules sane we only - * clean up DLCI objects when the MUX closes rather than as the port - * is closed down on both the tty and mux levels. - * - * Can sleep. - */ -static void gsm_dlci_free(struct gsm_dlci *dlci) -{ - struct tty_struct *tty = tty_port_tty_get(&dlci->port); - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } - del_timer_sync(&dlci->t1); - dlci->gsm->dlci[dlci->addr] = NULL; - kfifo_free(dlci->fifo); - kfree(dlci); -} - - -/* - * LAPBish link layer logic - */ - -/** - * gsm_queue - a GSM frame is ready to process - * @gsm: pointer to our gsm mux - * - * At this point in time a frame has arrived and been demangled from - * the line encoding. All the differences between the encodings have - * been handled below us and the frame is unpacked into the structures. - * The fcs holds the header FCS but any data FCS must be added here. - */ - -static void gsm_queue(struct gsm_mux *gsm) -{ - struct gsm_dlci *dlci; - u8 cr; - int address; - /* We have to sneak a look at the packet body to do the FCS. - A somewhat layering violation in the spec */ - - if ((gsm->control & ~PF) == UI) - gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); - if (gsm->fcs != GOOD_FCS) { - gsm->bad_fcs++; - if (debug & 4) - printk("BAD FCS %02x\n", gsm->fcs); - return; - } - address = gsm->address >> 1; - if (address >= NUM_DLCI) - goto invalid; - - cr = gsm->address & 1; /* C/R bit */ - - gsm_print_packet("<--", address, cr, gsm->control, gsm->buf, gsm->len); - - cr ^= 1 - gsm->initiator; /* Flip so 1 always means command */ - dlci = gsm->dlci[address]; - - switch (gsm->control) { - case SABM|PF: - if (cr == 0) - goto invalid; - if (dlci == NULL) - dlci = gsm_dlci_alloc(gsm, address); - if (dlci == NULL) - return; - if (dlci->dead) - gsm_response(gsm, address, DM); - else { - gsm_response(gsm, address, UA); - gsm_dlci_open(dlci); - } - break; - case DISC|PF: - if (cr == 0) - goto invalid; - if (dlci == NULL || dlci->state == DLCI_CLOSED) { - gsm_response(gsm, address, DM); - return; - } - /* Real close complete */ - gsm_response(gsm, address, UA); - gsm_dlci_close(dlci); - break; - case UA: - case UA|PF: - if (cr == 0 || dlci == NULL) - break; - switch (dlci->state) { - case DLCI_CLOSING: - gsm_dlci_close(dlci); - break; - case DLCI_OPENING: - gsm_dlci_open(dlci); - break; - } - break; - case DM: /* DM can be valid unsolicited */ - case DM|PF: - if (cr) - goto invalid; - if (dlci == NULL) - return; - gsm_dlci_close(dlci); - break; - case UI: - case UI|PF: - case UIH: - case UIH|PF: -#if 0 - if (cr) - goto invalid; -#endif - if (dlci == NULL || dlci->state != DLCI_OPEN) { - gsm_command(gsm, address, DM|PF); - return; - } - dlci->data(dlci, gsm->buf, gsm->len); - break; - default: - goto invalid; - } - return; -invalid: - gsm->malformed++; - return; -} - - -/** - * gsm0_receive - perform processing for non-transparency - * @gsm: gsm data for this ldisc instance - * @c: character - * - * Receive bytes in gsm mode 0 - */ - -static void gsm0_receive(struct gsm_mux *gsm, unsigned char c) -{ - switch (gsm->state) { - case GSM_SEARCH: /* SOF marker */ - if (c == GSM0_SOF) { - gsm->state = GSM_ADDRESS; - gsm->address = 0; - gsm->len = 0; - gsm->fcs = INIT_FCS; - } - break; /* Address EA */ - case GSM_ADDRESS: - gsm->fcs = gsm_fcs_add(gsm->fcs, c); - if (gsm_read_ea(&gsm->address, c)) - gsm->state = GSM_CONTROL; - break; - case GSM_CONTROL: /* Control Byte */ - gsm->fcs = gsm_fcs_add(gsm->fcs, c); - gsm->control = c; - gsm->state = GSM_LEN; - break; - case GSM_LEN: /* Length EA */ - gsm->fcs = gsm_fcs_add(gsm->fcs, c); - if (gsm_read_ea(&gsm->len, c)) { - if (gsm->len > gsm->mru) { - gsm->bad_size++; - gsm->state = GSM_SEARCH; - break; - } - gsm->count = 0; - gsm->state = GSM_DATA; - } - break; - case GSM_DATA: /* Data */ - gsm->buf[gsm->count++] = c; - if (gsm->count == gsm->len) - gsm->state = GSM_FCS; - break; - case GSM_FCS: /* FCS follows the packet */ - gsm->fcs = c; - gsm_queue(gsm); - /* And then back for the next frame */ - gsm->state = GSM_SEARCH; - break; - } -} - -/** - * gsm0_receive - perform processing for non-transparency - * @gsm: gsm data for this ldisc instance - * @c: character - * - * Receive bytes in mode 1 (Advanced option) - */ - -static void gsm1_receive(struct gsm_mux *gsm, unsigned char c) -{ - if (c == GSM1_SOF) { - /* EOF is only valid in frame if we have got to the data state - and received at least one byte (the FCS) */ - if (gsm->state == GSM_DATA && gsm->count) { - /* Extract the FCS */ - gsm->count--; - gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]); - gsm->len = gsm->count; - gsm_queue(gsm); - gsm->state = GSM_START; - return; - } - /* Any partial frame was a runt so go back to start */ - if (gsm->state != GSM_START) { - gsm->malformed++; - gsm->state = GSM_START; - } - /* A SOF in GSM_START means we are still reading idling or - framing bytes */ - return; - } - - if (c == GSM1_ESCAPE) { - gsm->escape = 1; - return; - } - - /* Only an unescaped SOF gets us out of GSM search */ - if (gsm->state == GSM_SEARCH) - return; - - if (gsm->escape) { - c ^= GSM1_ESCAPE_BITS; - gsm->escape = 0; - } - switch (gsm->state) { - case GSM_START: /* First byte after SOF */ - gsm->address = 0; - gsm->state = GSM_ADDRESS; - gsm->fcs = INIT_FCS; - /* Drop through */ - case GSM_ADDRESS: /* Address continuation */ - gsm->fcs = gsm_fcs_add(gsm->fcs, c); - if (gsm_read_ea(&gsm->address, c)) - gsm->state = GSM_CONTROL; - break; - case GSM_CONTROL: /* Control Byte */ - gsm->fcs = gsm_fcs_add(gsm->fcs, c); - gsm->control = c; - gsm->count = 0; - gsm->state = GSM_DATA; - break; - case GSM_DATA: /* Data */ - if (gsm->count > gsm->mru ) { /* Allow one for the FCS */ - gsm->state = GSM_OVERRUN; - gsm->bad_size++; - } else - gsm->buf[gsm->count++] = c; - break; - case GSM_OVERRUN: /* Over-long - eg a dropped SOF */ - break; - } -} - -/** - * gsm_error - handle tty error - * @gsm: ldisc data - * @data: byte received (may be invalid) - * @flag: error received - * - * Handle an error in the receipt of data for a frame. Currently we just - * go back to hunting for a SOF. - * - * FIXME: better diagnostics ? - */ - -static void gsm_error(struct gsm_mux *gsm, - unsigned char data, unsigned char flag) -{ - gsm->state = GSM_SEARCH; - gsm->io_error++; -} - -/** - * gsm_cleanup_mux - generic GSM protocol cleanup - * @gsm: our mux - * - * Clean up the bits of the mux which are the same for all framing - * protocols. Remove the mux from the mux table, stop all the timers - * and then shut down each device hanging up the channels as we go. - */ - -void gsm_cleanup_mux(struct gsm_mux *gsm) -{ - int i; - struct gsm_dlci *dlci = gsm->dlci[0]; - struct gsm_msg *txq; - - gsm->dead = 1; - - spin_lock(&gsm_mux_lock); - for (i = 0; i < MAX_MUX; i++) { - if (gsm_mux[i] == gsm) { - gsm_mux[i] = NULL; - break; - } - } - spin_unlock(&gsm_mux_lock); - WARN_ON(i == MAX_MUX); - - del_timer_sync(&gsm->t2_timer); - /* Now we are sure T2 has stopped */ - if (dlci) { - dlci->dead = 1; - gsm_dlci_begin_close(dlci); - wait_event_interruptible(gsm->event, - dlci->state == DLCI_CLOSED); - } - /* Free up any link layer users */ - for (i = 0; i < NUM_DLCI; i++) - if (gsm->dlci[i]) - gsm_dlci_free(gsm->dlci[i]); - /* Now wipe the queues */ - for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { - gsm->tx_head = txq->next; - kfree(txq); - } - gsm->tx_tail = NULL; -} -EXPORT_SYMBOL_GPL(gsm_cleanup_mux); - -/** - * gsm_activate_mux - generic GSM setup - * @gsm: our mux - * - * Set up the bits of the mux which are the same for all framing - * protocols. Add the mux to the mux table so it can be opened and - * finally kick off connecting to DLCI 0 on the modem. - */ - -int gsm_activate_mux(struct gsm_mux *gsm) -{ - struct gsm_dlci *dlci; - int i = 0; - - init_timer(&gsm->t2_timer); - gsm->t2_timer.function = gsm_control_retransmit; - gsm->t2_timer.data = (unsigned long)gsm; - init_waitqueue_head(&gsm->event); - spin_lock_init(&gsm->control_lock); - spin_lock_init(&gsm->tx_lock); - - if (gsm->encoding == 0) - gsm->receive = gsm0_receive; - else - gsm->receive = gsm1_receive; - gsm->error = gsm_error; - - spin_lock(&gsm_mux_lock); - for (i = 0; i < MAX_MUX; i++) { - if (gsm_mux[i] == NULL) { - gsm_mux[i] = gsm; - break; - } - } - spin_unlock(&gsm_mux_lock); - if (i == MAX_MUX) - return -EBUSY; - - dlci = gsm_dlci_alloc(gsm, 0); - if (dlci == NULL) - return -ENOMEM; - gsm->dead = 0; /* Tty opens are now permissible */ - return 0; -} -EXPORT_SYMBOL_GPL(gsm_activate_mux); - -/** - * gsm_free_mux - free up a mux - * @mux: mux to free - * - * Dispose of allocated resources for a dead mux. No refcounting - * at present so the mux must be truely dead. - */ -void gsm_free_mux(struct gsm_mux *gsm) -{ - kfree(gsm->txframe); - kfree(gsm->buf); - kfree(gsm); -} -EXPORT_SYMBOL_GPL(gsm_free_mux); - -/** - * gsm_alloc_mux - allocate a mux - * - * Creates a new mux ready for activation. - */ - -struct gsm_mux *gsm_alloc_mux(void) -{ - struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL); - if (gsm == NULL) - return NULL; - gsm->buf = kmalloc(MAX_MRU + 1, GFP_KERNEL); - if (gsm->buf == NULL) { - kfree(gsm); - return NULL; - } - gsm->txframe = kmalloc(2 * MAX_MRU + 2, GFP_KERNEL); - if (gsm->txframe == NULL) { - kfree(gsm->buf); - kfree(gsm); - return NULL; - } - spin_lock_init(&gsm->lock); - - gsm->t1 = T1; - gsm->t2 = T2; - gsm->n2 = N2; - gsm->ftype = UIH; - gsm->initiator = 0; - gsm->adaption = 1; - gsm->encoding = 1; - gsm->mru = 64; /* Default to encoding 1 so these should be 64 */ - gsm->mtu = 64; - gsm->dead = 1; /* Avoid early tty opens */ - - return gsm; -} -EXPORT_SYMBOL_GPL(gsm_alloc_mux); - - - - -/** - * gsmld_output - write to link - * @gsm: our mux - * @data: bytes to output - * @len: size - * - * Write a block of data from the GSM mux to the data channel. This - * will eventually be serialized from above but at the moment isn't. - */ - -static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len) -{ - if (tty_write_room(gsm->tty) < len) { - set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags); - return -ENOSPC; - } - if (debug & 4) { - printk("-->%d bytes out\n", len); - hex_packet(data, len); - } - gsm->tty->ops->write(gsm->tty, data, len); - return len; -} - -/** - * gsmld_attach_gsm - mode set up - * @tty: our tty structure - * @gsm: our mux - * - * Set up the MUX for basic mode and commence connecting to the - * modem. Currently called from the line discipline set up but - * will need moving to an ioctl path. - */ - -static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) -{ - int ret; - - gsm->tty = tty_kref_get(tty); - gsm->output = gsmld_output; - ret = gsm_activate_mux(gsm); - if (ret != 0) - tty_kref_put(gsm->tty); - return ret; -} - - -/** - * gsmld_detach_gsm - stop doing 0710 mux - * @tty: tty atttached to the mux - * @gsm: mux - * - * Shutdown and then clean up the resources used by the line discipline - */ - -static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) -{ - WARN_ON(tty != gsm->tty); - gsm_cleanup_mux(gsm); - tty_kref_put(gsm->tty); - gsm->tty = NULL; -} - -static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - struct gsm_mux *gsm = tty->disc_data; - const unsigned char *dp; - char *f; - int i; - char buf[64]; - char flags; - - if (debug & 4) { - printk("Inbytes %dd\n", count); - hex_packet(cp, count); - } - - for (i = count, dp = cp, f = fp; i; i--, dp++) { - flags = *f++; - switch (flags) { - case TTY_NORMAL: - gsm->receive(gsm, *dp); - break; - case TTY_OVERRUN: - case TTY_BREAK: - case TTY_PARITY: - case TTY_FRAME: - gsm->error(gsm, *dp, flags); - break; - default: - printk(KERN_ERR "%s: unknown flag %d\n", - tty_name(tty, buf), flags); - break; - } - } - /* FASYNC if needed ? */ - /* If clogged call tty_throttle(tty); */ -} - -/** - * gsmld_chars_in_buffer - report available bytes - * @tty: tty device - * - * Report the number of characters buffered to be delivered to user - * at this instant in time. - * - * Locking: gsm lock - */ - -static ssize_t gsmld_chars_in_buffer(struct tty_struct *tty) -{ - return 0; -} - -/** - * gsmld_flush_buffer - clean input queue - * @tty: terminal device - * - * Flush the input buffer. Called when the line discipline is - * being closed, when the tty layer wants the buffer flushed (eg - * at hangup). - */ - -static void gsmld_flush_buffer(struct tty_struct *tty) -{ -} - -/** - * gsmld_close - close the ldisc for this tty - * @tty: device - * - * Called from the terminal layer when this line discipline is - * being shut down, either because of a close or becsuse of a - * discipline change. The function will not be called while other - * ldisc methods are in progress. - */ - -static void gsmld_close(struct tty_struct *tty) -{ - struct gsm_mux *gsm = tty->disc_data; - - gsmld_detach_gsm(tty, gsm); - - gsmld_flush_buffer(tty); - /* Do other clean up here */ - gsm_free_mux(gsm); -} - -/** - * gsmld_open - open an ldisc - * @tty: terminal to open - * - * Called when this line discipline is being attached to the - * terminal device. Can sleep. Called serialized so that no - * other events will occur in parallel. No further open will occur - * until a close. - */ - -static int gsmld_open(struct tty_struct *tty) -{ - struct gsm_mux *gsm; - - if (tty->ops->write == NULL) - return -EINVAL; - - /* Attach our ldisc data */ - gsm = gsm_alloc_mux(); - if (gsm == NULL) - return -ENOMEM; - - tty->disc_data = gsm; - tty->receive_room = 65536; - - /* Attach the initial passive connection */ - gsm->encoding = 1; - return gsmld_attach_gsm(tty, gsm); -} - -/** - * gsmld_write_wakeup - asynchronous I/O notifier - * @tty: tty device - * - * Required for the ptys, serial driver etc. since processes - * that attach themselves to the master and rely on ASYNC - * IO must be woken up - */ - -static void gsmld_write_wakeup(struct tty_struct *tty) -{ - struct gsm_mux *gsm = tty->disc_data; - unsigned long flags; - - /* Queue poll */ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - gsm_data_kick(gsm); - if (gsm->tx_bytes < TX_THRESH_LO) { - spin_lock_irqsave(&gsm->tx_lock, flags); - gsm_dlci_data_sweep(gsm); - spin_unlock_irqrestore(&gsm->tx_lock, flags); - } -} - -/** - * gsmld_read - read function for tty - * @tty: tty device - * @file: file object - * @buf: userspace buffer pointer - * @nr: size of I/O - * - * Perform reads for the line discipline. We are guaranteed that the - * line discipline will not be closed under us but we may get multiple - * parallel readers and must handle this ourselves. We may also get - * a hangup. Always called in user context, may sleep. - * - * This code must be sure never to sleep through a hangup. - */ - -static ssize_t gsmld_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t nr) -{ - return -EOPNOTSUPP; -} - -/** - * gsmld_write - write function for tty - * @tty: tty device - * @file: file object - * @buf: userspace buffer pointer - * @nr: size of I/O - * - * Called when the owner of the device wants to send a frame - * itself (or some other control data). The data is transferred - * as-is and must be properly framed and checksummed as appropriate - * by userspace. Frames are either sent whole or not at all as this - * avoids pain user side. - */ - -static ssize_t gsmld_write(struct tty_struct *tty, struct file *file, - const unsigned char *buf, size_t nr) -{ - int space = tty_write_room(tty); - if (space >= nr) - return tty->ops->write(tty, buf, nr); - set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - return -ENOBUFS; -} - -/** - * gsmld_poll - poll method for N_GSM0710 - * @tty: terminal device - * @file: file accessing it - * @wait: poll table - * - * Called when the line discipline is asked to poll() for data or - * for special events. This code is not serialized with respect to - * other events save open/close. - * - * This code must be sure never to sleep through a hangup. - * Called without the kernel lock held - fine - */ - -static unsigned int gsmld_poll(struct tty_struct *tty, struct file *file, - poll_table *wait) -{ - unsigned int mask = 0; - struct gsm_mux *gsm = tty->disc_data; - - poll_wait(file, &tty->read_wait, wait); - poll_wait(file, &tty->write_wait, wait); - if (tty_hung_up_p(file)) - mask |= POLLHUP; - if (!tty_is_writelocked(tty) && tty_write_room(tty) > 0) - mask |= POLLOUT | POLLWRNORM; - if (gsm->dead) - mask |= POLLHUP; - return mask; -} - -static int gsmld_config(struct tty_struct *tty, struct gsm_mux *gsm, - struct gsm_config *c) -{ - int need_close = 0; - int need_restart = 0; - - /* Stuff we don't support yet - UI or I frame transport, windowing */ - if ((c->adaption !=1 && c->adaption != 2) || c->k) - return -EOPNOTSUPP; - /* Check the MRU/MTU range looks sane */ - if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8) - return -EINVAL; - if (c->n2 < 3) - return -EINVAL; - if (c->encapsulation > 1) /* Basic, advanced, no I */ - return -EINVAL; - if (c->initiator > 1) - return -EINVAL; - if (c->i == 0 || c->i > 2) /* UIH and UI only */ - return -EINVAL; - /* - * See what is needed for reconfiguration - */ - - /* Timing fields */ - if (c->t1 != 0 && c->t1 != gsm->t1) - need_restart = 1; - if (c->t2 != 0 && c->t2 != gsm->t2) - need_restart = 1; - if (c->encapsulation != gsm->encoding) - need_restart = 1; - if (c->adaption != gsm->adaption) - need_restart = 1; - /* Requires care */ - if (c->initiator != gsm->initiator) - need_close = 1; - if (c->mru != gsm->mru) - need_restart = 1; - if (c->mtu != gsm->mtu) - need_restart = 1; - - /* - * Close down what is needed, restart and initiate the new - * configuration - */ - - if (need_close || need_restart) { - gsm_dlci_begin_close(gsm->dlci[0]); - /* This will timeout if the link is down due to N2 expiring */ - wait_event_interruptible(gsm->event, - gsm->dlci[0]->state == DLCI_CLOSED); - if (signal_pending(current)) - return -EINTR; - } - if (need_restart) - gsm_cleanup_mux(gsm); - - gsm->initiator = c->initiator; - gsm->mru = c->mru; - gsm->encoding = c->encapsulation; - gsm->adaption = c->adaption; - - if (c->i == 1) - gsm->ftype = UIH; - else if (c->i == 2) - gsm->ftype = UI; - - if (c->t1) - gsm->t1 = c->t1; - if (c->t2) - gsm->t2 = c->t2; - - /* FIXME: We need to separate activation/deactivation from adding - and removing from the mux array */ - if (need_restart) - gsm_activate_mux(gsm); - if (gsm->initiator && need_close) - gsm_dlci_begin_open(gsm->dlci[0]); - return 0; -} - -static int gsmld_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct gsm_config c; - struct gsm_mux *gsm = tty->disc_data; - - switch (cmd) { - case GSMIOC_GETCONF: - memset(&c, 0, sizeof(c)); - c.adaption = gsm->adaption; - c.encapsulation = gsm->encoding; - c.initiator = gsm->initiator; - c.t1 = gsm->t1; - c.t2 = gsm->t2; - c.t3 = 0; /* Not supported */ - c.n2 = gsm->n2; - if (gsm->ftype == UIH) - c.i = 1; - else - c.i = 2; - printk("Ftype %d i %d\n", gsm->ftype, c.i); - c.mru = gsm->mru; - c.mtu = gsm->mtu; - c.k = 0; - if (copy_to_user((void *)arg, &c, sizeof(c))) - return -EFAULT; - return 0; - case GSMIOC_SETCONF: - if (copy_from_user(&c, (void *)arg, sizeof(c))) - return -EFAULT; - return gsmld_config(tty, gsm, &c); - default: - return n_tty_ioctl_helper(tty, file, cmd, arg); - } -} - - -/* Line discipline for real tty */ -struct tty_ldisc_ops tty_ldisc_packet = { - .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, - .name = "n_gsm", - .open = gsmld_open, - .close = gsmld_close, - .flush_buffer = gsmld_flush_buffer, - .chars_in_buffer = gsmld_chars_in_buffer, - .read = gsmld_read, - .write = gsmld_write, - .ioctl = gsmld_ioctl, - .poll = gsmld_poll, - .receive_buf = gsmld_receive_buf, - .write_wakeup = gsmld_write_wakeup -}; - -/* - * Virtual tty side - */ - -#define TX_SIZE 512 - -static int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk) -{ - u8 modembits[5]; - struct gsm_control *ctrl; - int len = 2; - - if (brk) - len++; - - modembits[0] = len << 1 | EA; /* Data bytes */ - modembits[1] = dlci->addr << 2 | 3; /* DLCI, EA, 1 */ - modembits[2] = gsm_encode_modem(dlci) << 1 | EA; - if (brk) - modembits[3] = brk << 4 | 2 | EA; /* Valid, EA */ - ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len + 1); - if (ctrl == NULL) - return -ENOMEM; - return gsm_control_wait(dlci->gsm, ctrl); -} - -static int gsm_carrier_raised(struct tty_port *port) -{ - struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); - /* Not yet open so no carrier info */ - if (dlci->state != DLCI_OPEN) - return 0; - if (debug & 2) - return 1; - return dlci->modem_rx & TIOCM_CD; -} - -static void gsm_dtr_rts(struct tty_port *port, int onoff) -{ - struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); - unsigned int modem_tx = dlci->modem_tx; - if (onoff) - modem_tx |= TIOCM_DTR | TIOCM_RTS; - else - modem_tx &= ~(TIOCM_DTR | TIOCM_RTS); - if (modem_tx != dlci->modem_tx) { - dlci->modem_tx = modem_tx; - gsmtty_modem_update(dlci, 0); - } -} - -static const struct tty_port_operations gsm_port_ops = { - .carrier_raised = gsm_carrier_raised, - .dtr_rts = gsm_dtr_rts, -}; - - -static int gsmtty_open(struct tty_struct *tty, struct file *filp) -{ - struct gsm_mux *gsm; - struct gsm_dlci *dlci; - struct tty_port *port; - unsigned int line = tty->index; - unsigned int mux = line >> 6; - - line = line & 0x3F; - - if (mux >= MAX_MUX) - return -ENXIO; - /* FIXME: we need to lock gsm_mux for lifetimes of ttys eventually */ - if (gsm_mux[mux] == NULL) - return -EUNATCH; - if (line == 0 || line > 61) /* 62/63 reserved */ - return -ECHRNG; - gsm = gsm_mux[mux]; - if (gsm->dead) - return -EL2HLT; - dlci = gsm->dlci[line]; - if (dlci == NULL) - dlci = gsm_dlci_alloc(gsm, line); - if (dlci == NULL) - return -ENOMEM; - port = &dlci->port; - port->count++; - tty->driver_data = dlci; - tty_port_tty_set(port, tty); - - dlci->modem_rx = 0; - /* We could in theory open and close before we wait - eg if we get - a DM straight back. This is ok as that will have caused a hangup */ - set_bit(ASYNCB_INITIALIZED, &port->flags); - /* Start sending off SABM messages */ - gsm_dlci_begin_open(dlci); - /* And wait for virtual carrier */ - return tty_port_block_til_ready(port, tty, filp); -} - -static void gsmtty_close(struct tty_struct *tty, struct file *filp) -{ - struct gsm_dlci *dlci = tty->driver_data; - if (dlci == NULL) - return; - if (tty_port_close_start(&dlci->port, tty, filp) == 0) - return; - gsm_dlci_begin_close(dlci); - tty_port_close_end(&dlci->port, tty); - tty_port_tty_set(&dlci->port, NULL); -} - -static void gsmtty_hangup(struct tty_struct *tty) -{ - struct gsm_dlci *dlci = tty->driver_data; - tty_port_hangup(&dlci->port); - gsm_dlci_begin_close(dlci); -} - -static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf, - int len) -{ - struct gsm_dlci *dlci = tty->driver_data; - /* Stuff the bytes into the fifo queue */ - int sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock); - /* Need to kick the channel */ - gsm_dlci_data_kick(dlci); - return sent; -} - -static int gsmtty_write_room(struct tty_struct *tty) -{ - struct gsm_dlci *dlci = tty->driver_data; - return TX_SIZE - kfifo_len(dlci->fifo); -} - -static int gsmtty_chars_in_buffer(struct tty_struct *tty) -{ - struct gsm_dlci *dlci = tty->driver_data; - return kfifo_len(dlci->fifo); -} - -static void gsmtty_flush_buffer(struct tty_struct *tty) -{ - struct gsm_dlci *dlci = tty->driver_data; - /* Caution needed: If we implement reliable transport classes - then the data being transmitted can't simply be junked once - it has first hit the stack. Until then we can just blow it - away */ - kfifo_reset(dlci->fifo); - /* Need to unhook this DLCI from the transmit queue logic */ -} - -static void gsmtty_wait_until_sent(struct tty_struct *tty, int timeout) -{ - /* The FIFO handles the queue so the kernel will do the right - thing waiting on chars_in_buffer before calling us. No work - to do here */ -} - -static int gsmtty_tiocmget(struct tty_struct *tty, struct file *filp) -{ - struct gsm_dlci *dlci = tty->driver_data; - return dlci->modem_rx; -} - -static int gsmtty_tiocmset(struct tty_struct *tty, struct file *filp, - unsigned int set, unsigned int clear) -{ - struct gsm_dlci *dlci = tty->driver_data; - unsigned int modem_tx = dlci->modem_tx; - - modem_tx &= clear; - modem_tx |= set; - - if (modem_tx != dlci->modem_tx) { - dlci->modem_tx = modem_tx; - return gsmtty_modem_update(dlci, 0); - } - return 0; -} - - -static int gsmtty_ioctl(struct tty_struct *tty, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; -} - -static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) -{ - /* For the moment its fixed. In actual fact the speed information - for the virtual channel can be propogated in both directions by - the RPN control message. This however rapidly gets nasty as we - then have to remap modem signals each way according to whether - our virtual cable is null modem etc .. */ - tty_termios_copy_hw(tty->termios, old); -} - -static void gsmtty_throttle(struct tty_struct *tty) -{ - struct gsm_dlci *dlci = tty->driver_data; - if (tty->termios->c_cflag & CRTSCTS) - dlci->modem_tx &= ~TIOCM_DTR; - dlci->throttled = 1; - /* Send an MSC with DTR cleared */ - gsmtty_modem_update(dlci, 0); -} - -static void gsmtty_unthrottle(struct tty_struct *tty) -{ - struct gsm_dlci *dlci = tty->driver_data; - if (tty->termios->c_cflag & CRTSCTS) - dlci->modem_tx |= TIOCM_DTR; - dlci->throttled = 0; - /* Send an MSC with DTR set */ - gsmtty_modem_update(dlci, 0); -} - -static int gsmtty_break_ctl(struct tty_struct *tty, int state) -{ - struct gsm_dlci *dlci = tty->driver_data; - int encode = 0; /* Off */ - - if (state == -1) /* "On indefinitely" - we can't encode this - properly */ - encode = 0x0F; - else if (state > 0) { - encode = state / 200; /* mS to encoding */ - if (encode > 0x0F) - encode = 0x0F; /* Best effort */ - } - return gsmtty_modem_update(dlci, encode); -} - -static struct tty_driver *gsm_tty_driver; - -/* Virtual ttys for the demux */ -static const struct tty_operations gsmtty_ops = { - .open = gsmtty_open, - .close = gsmtty_close, - .write = gsmtty_write, - .write_room = gsmtty_write_room, - .chars_in_buffer = gsmtty_chars_in_buffer, - .flush_buffer = gsmtty_flush_buffer, - .ioctl = gsmtty_ioctl, - .throttle = gsmtty_throttle, - .unthrottle = gsmtty_unthrottle, - .set_termios = gsmtty_set_termios, - .hangup = gsmtty_hangup, - .wait_until_sent = gsmtty_wait_until_sent, - .tiocmget = gsmtty_tiocmget, - .tiocmset = gsmtty_tiocmset, - .break_ctl = gsmtty_break_ctl, -}; - - - -static int __init gsm_init(void) -{ - /* Fill in our line protocol discipline, and register it */ - int status = tty_register_ldisc(N_GSM0710, &tty_ldisc_packet); - if (status != 0) { - printk(KERN_ERR "n_gsm: can't register line discipline (err = %d)\n", status); - return status; - } - - gsm_tty_driver = alloc_tty_driver(256); - if (!gsm_tty_driver) { - tty_unregister_ldisc(N_GSM0710); - printk(KERN_ERR "gsm_init: tty allocation failed.\n"); - return -EINVAL; - } - gsm_tty_driver->owner = THIS_MODULE; - gsm_tty_driver->driver_name = "gsmtty"; - gsm_tty_driver->name = "gsmtty"; - gsm_tty_driver->major = 0; /* Dynamic */ - gsm_tty_driver->minor_start = 0; - gsm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - gsm_tty_driver->subtype = SERIAL_TYPE_NORMAL; - gsm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV - | TTY_DRIVER_HARDWARE_BREAK; - gsm_tty_driver->init_termios = tty_std_termios; - /* Fixme */ - gsm_tty_driver->init_termios.c_lflag &= ~ECHO; - tty_set_operations(gsm_tty_driver, &gsmtty_ops); - - spin_lock_init(&gsm_mux_lock); - - if (tty_register_driver(gsm_tty_driver)) { - put_tty_driver(gsm_tty_driver); - tty_unregister_ldisc(N_GSM0710); - printk(KERN_ERR "gsm_init: tty registration failed.\n"); - return -EBUSY; - } - printk(KERN_INFO "gsm_init: loaded as %d,%d.\n", gsm_tty_driver->major, gsm_tty_driver->minor_start); - return 0; -} - -static void __exit gsm_exit(void) -{ - int status = tty_unregister_ldisc(N_GSM0710); - if (status != 0) - printk(KERN_ERR "n_gsm: can't unregister line discipline (err = %d)\n", status); - tty_unregister_driver(gsm_tty_driver); - put_tty_driver(gsm_tty_driver); - printk(KERN_INFO "gsm_init: unloaded.\n"); -} - -module_init(gsm_init); -module_exit(gsm_exit); - - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_GSM0710); diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c deleted file mode 100644 index 47d32281032c..000000000000 --- a/drivers/char/n_hdlc.c +++ /dev/null @@ -1,1007 +0,0 @@ -/* generic HDLC line discipline for Linux - * - * Written by Paul Fulghum paulkf@microgate.com - * for Microgate Corporation - * - * Microgate and SyncLink are registered trademarks of Microgate Corporation - * - * Adapted from ppp.c, written by Michael Callahan , - * Al Longyear , - * Paul Mackerras - * - * Original release 01/11/99 - * - * This code is released under the GNU General Public License (GPL) - * - * This module implements the tty line discipline N_HDLC for use with - * tty device drivers that support bit-synchronous HDLC communications. - * - * All HDLC data is frame oriented which means: - * - * 1. tty write calls represent one complete transmit frame of data - * The device driver should accept the complete frame or none of - * the frame (busy) in the write method. Each write call should have - * a byte count in the range of 2-65535 bytes (2 is min HDLC frame - * with 1 addr byte and 1 ctrl byte). The max byte count of 65535 - * should include any crc bytes required. For example, when using - * CCITT CRC32, 4 crc bytes are required, so the maximum size frame - * the application may transmit is limited to 65531 bytes. For CCITT - * CRC16, the maximum application frame size would be 65533. - * - * - * 2. receive callbacks from the device driver represents - * one received frame. The device driver should bypass - * the tty flip buffer and call the line discipline receive - * callback directly to avoid fragmenting or concatenating - * multiple frames into a single receive callback. - * - * The HDLC line discipline queues the receive frames in separate - * buffers so complete receive frames can be returned by the - * tty read calls. - * - * 3. tty read calls returns an entire frame of data or nothing. - * - * 4. all send and receive data is considered raw. No processing - * or translation is performed by the line discipline, regardless - * of the tty flags - * - * 5. When line discipline is queried for the amount of receive - * data available (FIOC), 0 is returned if no data available, - * otherwise the count of the next available frame is returned. - * (instead of the sum of all received frame counts). - * - * These conventions allow the standard tty programming interface - * to be used for synchronous HDLC applications when used with - * this line discipline (or another line discipline that is frame - * oriented such as N_PPP). - * - * The SyncLink driver (synclink.c) implements both asynchronous - * (using standard line discipline N_TTY) and synchronous HDLC - * (using N_HDLC) communications, with the latter using the above - * conventions. - * - * This implementation is very basic and does not maintain - * any statistics. The main point is to enforce the raw data - * and frame orientation of HDLC communications. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define HDLC_MAGIC 0x239e - -#include -#include -#include -#include -#include -#include -#include -#include - -#undef VERSION -#define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch)) - -#include -#include -#include -#include -#include -#include -#include -#include /* used in new tty drivers */ -#include /* used in new tty drivers */ -#include -#include - -#include -#include -#include - -/* - * Buffers for individual HDLC frames - */ -#define MAX_HDLC_FRAME_SIZE 65535 -#define DEFAULT_RX_BUF_COUNT 10 -#define MAX_RX_BUF_COUNT 60 -#define DEFAULT_TX_BUF_COUNT 3 - -struct n_hdlc_buf { - struct n_hdlc_buf *link; - int count; - char buf[1]; -}; - -#define N_HDLC_BUF_SIZE (sizeof(struct n_hdlc_buf) + maxframe) - -struct n_hdlc_buf_list { - struct n_hdlc_buf *head; - struct n_hdlc_buf *tail; - int count; - spinlock_t spinlock; -}; - -/** - * struct n_hdlc - per device instance data structure - * @magic - magic value for structure - * @flags - miscellaneous control flags - * @tty - ptr to TTY structure - * @backup_tty - TTY to use if tty gets closed - * @tbusy - reentrancy flag for tx wakeup code - * @woke_up - FIXME: describe this field - * @tbuf - currently transmitting tx buffer - * @tx_buf_list - list of pending transmit frame buffers - * @rx_buf_list - list of received frame buffers - * @tx_free_buf_list - list unused transmit frame buffers - * @rx_free_buf_list - list unused received frame buffers - */ -struct n_hdlc { - int magic; - __u32 flags; - struct tty_struct *tty; - struct tty_struct *backup_tty; - int tbusy; - int woke_up; - struct n_hdlc_buf *tbuf; - struct n_hdlc_buf_list tx_buf_list; - struct n_hdlc_buf_list rx_buf_list; - struct n_hdlc_buf_list tx_free_buf_list; - struct n_hdlc_buf_list rx_free_buf_list; -}; - -/* - * HDLC buffer list manipulation functions - */ -static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list); -static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, - struct n_hdlc_buf *buf); -static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list); - -/* Local functions */ - -static struct n_hdlc *n_hdlc_alloc (void); - -/* debug level can be set by insmod for debugging purposes */ -#define DEBUG_LEVEL_INFO 1 -static int debuglevel; - -/* max frame size for memory allocations */ -static int maxframe = 4096; - -/* TTY callbacks */ - -static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, - __u8 __user *buf, size_t nr); -static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, - const unsigned char *buf, size_t nr); -static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg); -static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, - poll_table *wait); -static int n_hdlc_tty_open(struct tty_struct *tty); -static void n_hdlc_tty_close(struct tty_struct *tty); -static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *cp, - char *fp, int count); -static void n_hdlc_tty_wakeup(struct tty_struct *tty); - -#define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) - -#define tty2n_hdlc(tty) ((struct n_hdlc *) ((tty)->disc_data)) -#define n_hdlc2tty(n_hdlc) ((n_hdlc)->tty) - -static void flush_rx_queue(struct tty_struct *tty) -{ - struct n_hdlc *n_hdlc = tty2n_hdlc(tty); - struct n_hdlc_buf *buf; - - while ((buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list))) - n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, buf); -} - -static void flush_tx_queue(struct tty_struct *tty) -{ - struct n_hdlc *n_hdlc = tty2n_hdlc(tty); - struct n_hdlc_buf *buf; - unsigned long flags; - - while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list))) - n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf); - spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); - if (n_hdlc->tbuf) { - n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, n_hdlc->tbuf); - n_hdlc->tbuf = NULL; - } - spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); -} - -static struct tty_ldisc_ops n_hdlc_ldisc = { - .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, - .name = "hdlc", - .open = n_hdlc_tty_open, - .close = n_hdlc_tty_close, - .read = n_hdlc_tty_read, - .write = n_hdlc_tty_write, - .ioctl = n_hdlc_tty_ioctl, - .poll = n_hdlc_tty_poll, - .receive_buf = n_hdlc_tty_receive, - .write_wakeup = n_hdlc_tty_wakeup, - .flush_buffer = flush_rx_queue, -}; - -/** - * n_hdlc_release - release an n_hdlc per device line discipline info structure - * @n_hdlc - per device line discipline info structure - */ -static void n_hdlc_release(struct n_hdlc *n_hdlc) -{ - struct tty_struct *tty = n_hdlc2tty (n_hdlc); - struct n_hdlc_buf *buf; - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__); - - /* Ensure that the n_hdlcd process is not hanging on select()/poll() */ - wake_up_interruptible (&tty->read_wait); - wake_up_interruptible (&tty->write_wait); - - if (tty->disc_data == n_hdlc) - tty->disc_data = NULL; /* Break the tty->n_hdlc link */ - - /* Release transmit and receive buffers */ - for(;;) { - buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); - if (buf) { - kfree(buf); - } else - break; - } - for(;;) { - buf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); - if (buf) { - kfree(buf); - } else - break; - } - for(;;) { - buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); - if (buf) { - kfree(buf); - } else - break; - } - for(;;) { - buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); - if (buf) { - kfree(buf); - } else - break; - } - kfree(n_hdlc->tbuf); - kfree(n_hdlc); - -} /* end of n_hdlc_release() */ - -/** - * n_hdlc_tty_close - line discipline close - * @tty - pointer to tty info structure - * - * Called when the line discipline is changed to something - * else, the tty is closed, or the tty detects a hangup. - */ -static void n_hdlc_tty_close(struct tty_struct *tty) -{ - struct n_hdlc *n_hdlc = tty2n_hdlc (tty); - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_close() called\n",__FILE__,__LINE__); - - if (n_hdlc != NULL) { - if (n_hdlc->magic != HDLC_MAGIC) { - printk (KERN_WARNING"n_hdlc: trying to close unopened tty!\n"); - return; - } -#if defined(TTY_NO_WRITE_SPLIT) - clear_bit(TTY_NO_WRITE_SPLIT,&tty->flags); -#endif - tty->disc_data = NULL; - if (tty == n_hdlc->backup_tty) - n_hdlc->backup_tty = NULL; - if (tty != n_hdlc->tty) - return; - if (n_hdlc->backup_tty) { - n_hdlc->tty = n_hdlc->backup_tty; - } else { - n_hdlc_release (n_hdlc); - } - } - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_close() success\n",__FILE__,__LINE__); - -} /* end of n_hdlc_tty_close() */ - -/** - * n_hdlc_tty_open - called when line discipline changed to n_hdlc - * @tty - pointer to tty info structure - * - * Returns 0 if success, otherwise error code - */ -static int n_hdlc_tty_open (struct tty_struct *tty) -{ - struct n_hdlc *n_hdlc = tty2n_hdlc (tty); - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_open() called (device=%s)\n", - __FILE__,__LINE__, - tty->name); - - /* There should not be an existing table for this slot. */ - if (n_hdlc) { - printk (KERN_ERR"n_hdlc_tty_open:tty already associated!\n" ); - return -EEXIST; - } - - n_hdlc = n_hdlc_alloc(); - if (!n_hdlc) { - printk (KERN_ERR "n_hdlc_alloc failed\n"); - return -ENFILE; - } - - tty->disc_data = n_hdlc; - n_hdlc->tty = tty; - tty->receive_room = 65536; - -#if defined(TTY_NO_WRITE_SPLIT) - /* change tty_io write() to not split large writes into 8K chunks */ - set_bit(TTY_NO_WRITE_SPLIT,&tty->flags); -#endif - - /* flush receive data from driver */ - tty_driver_flush_buffer(tty); - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_open() success\n",__FILE__,__LINE__); - - return 0; - -} /* end of n_tty_hdlc_open() */ - -/** - * n_hdlc_send_frames - send frames on pending send buffer list - * @n_hdlc - pointer to ldisc instance data - * @tty - pointer to tty instance data - * - * Send frames on pending send buffer list until the driver does not accept a - * frame (busy) this function is called after adding a frame to the send buffer - * list and by the tty wakeup callback. - */ -static void n_hdlc_send_frames(struct n_hdlc *n_hdlc, struct tty_struct *tty) -{ - register int actual; - unsigned long flags; - struct n_hdlc_buf *tbuf; - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_send_frames() called\n",__FILE__,__LINE__); - check_again: - - spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); - if (n_hdlc->tbusy) { - n_hdlc->woke_up = 1; - spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); - return; - } - n_hdlc->tbusy = 1; - n_hdlc->woke_up = 0; - spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); - - /* get current transmit buffer or get new transmit */ - /* buffer from list of pending transmit buffers */ - - tbuf = n_hdlc->tbuf; - if (!tbuf) - tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); - - while (tbuf) { - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)sending frame %p, count=%d\n", - __FILE__,__LINE__,tbuf,tbuf->count); - - /* Send the next block of data to device */ - tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - actual = tty->ops->write(tty, tbuf->buf, tbuf->count); - - /* rollback was possible and has been done */ - if (actual == -ERESTARTSYS) { - n_hdlc->tbuf = tbuf; - break; - } - /* if transmit error, throw frame away by */ - /* pretending it was accepted by driver */ - if (actual < 0) - actual = tbuf->count; - - if (actual == tbuf->count) { - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)frame %p completed\n", - __FILE__,__LINE__,tbuf); - - /* free current transmit buffer */ - n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf); - - /* this tx buffer is done */ - n_hdlc->tbuf = NULL; - - /* wait up sleeping writers */ - wake_up_interruptible(&tty->write_wait); - - /* get next pending transmit buffer */ - tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); - } else { - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)frame %p pending\n", - __FILE__,__LINE__,tbuf); - - /* buffer not accepted by driver */ - /* set this buffer as pending buffer */ - n_hdlc->tbuf = tbuf; - break; - } - } - - if (!tbuf) - tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - - /* Clear the re-entry flag */ - spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); - n_hdlc->tbusy = 0; - spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); - - if (n_hdlc->woke_up) - goto check_again; - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_send_frames() exit\n",__FILE__,__LINE__); - -} /* end of n_hdlc_send_frames() */ - -/** - * n_hdlc_tty_wakeup - Callback for transmit wakeup - * @tty - pointer to associated tty instance data - * - * Called when low level device driver can accept more send data. - */ -static void n_hdlc_tty_wakeup(struct tty_struct *tty) -{ - struct n_hdlc *n_hdlc = tty2n_hdlc(tty); - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_wakeup() called\n",__FILE__,__LINE__); - - if (!n_hdlc) - return; - - if (tty != n_hdlc->tty) { - tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - return; - } - - n_hdlc_send_frames (n_hdlc, tty); - -} /* end of n_hdlc_tty_wakeup() */ - -/** - * n_hdlc_tty_receive - Called by tty driver when receive data is available - * @tty - pointer to tty instance data - * @data - pointer to received data - * @flags - pointer to flags for data - * @count - count of received data in bytes - * - * Called by tty low level driver when receive data is available. Data is - * interpreted as one HDLC frame. - */ -static void n_hdlc_tty_receive(struct tty_struct *tty, const __u8 *data, - char *flags, int count) -{ - register struct n_hdlc *n_hdlc = tty2n_hdlc (tty); - register struct n_hdlc_buf *buf; - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_receive() called count=%d\n", - __FILE__,__LINE__, count); - - /* This can happen if stuff comes in on the backup tty */ - if (!n_hdlc || tty != n_hdlc->tty) - return; - - /* verify line is using HDLC discipline */ - if (n_hdlc->magic != HDLC_MAGIC) { - printk("%s(%d) line not using HDLC discipline\n", - __FILE__,__LINE__); - return; - } - - if ( count>maxframe ) { - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d) rx count>maxframesize, data discarded\n", - __FILE__,__LINE__); - return; - } - - /* get a free HDLC buffer */ - buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); - if (!buf) { - /* no buffers in free list, attempt to allocate another rx buffer */ - /* unless the maximum count has been reached */ - if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT) - buf = kmalloc(N_HDLC_BUF_SIZE, GFP_ATOMIC); - } - - if (!buf) { - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d) no more rx buffers, data discarded\n", - __FILE__,__LINE__); - return; - } - - /* copy received data to HDLC buffer */ - memcpy(buf->buf,data,count); - buf->count=count; - - /* add HDLC buffer to list of received frames */ - n_hdlc_buf_put(&n_hdlc->rx_buf_list, buf); - - /* wake up any blocked reads and perform async signalling */ - wake_up_interruptible (&tty->read_wait); - if (n_hdlc->tty->fasync != NULL) - kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN); - -} /* end of n_hdlc_tty_receive() */ - -/** - * n_hdlc_tty_read - Called to retrieve one frame of data (if available) - * @tty - pointer to tty instance data - * @file - pointer to open file object - * @buf - pointer to returned data buffer - * @nr - size of returned data buffer - * - * Returns the number of bytes returned or error code. - */ -static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, - __u8 __user *buf, size_t nr) -{ - struct n_hdlc *n_hdlc = tty2n_hdlc(tty); - int ret; - struct n_hdlc_buf *rbuf; - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_read() called\n",__FILE__,__LINE__); - - /* Validate the pointers */ - if (!n_hdlc) - return -EIO; - - /* verify user access to buffer */ - if (!access_ok(VERIFY_WRITE, buf, nr)) { - printk(KERN_WARNING "%s(%d) n_hdlc_tty_read() can't verify user " - "buffer\n", __FILE__, __LINE__); - return -EFAULT; - } - - tty_lock(); - - for (;;) { - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { - tty_unlock(); - return -EIO; - } - - n_hdlc = tty2n_hdlc (tty); - if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || - tty != n_hdlc->tty) { - tty_unlock(); - return 0; - } - - rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); - if (rbuf) - break; - - /* no data */ - if (file->f_flags & O_NONBLOCK) { - tty_unlock(); - return -EAGAIN; - } - - interruptible_sleep_on (&tty->read_wait); - if (signal_pending(current)) { - tty_unlock(); - return -EINTR; - } - } - - if (rbuf->count > nr) - /* frame too large for caller's buffer (discard frame) */ - ret = -EOVERFLOW; - else { - /* Copy the data to the caller's buffer */ - if (copy_to_user(buf, rbuf->buf, rbuf->count)) - ret = -EFAULT; - else - ret = rbuf->count; - } - - /* return HDLC buffer to free list unless the free list */ - /* count has exceeded the default value, in which case the */ - /* buffer is freed back to the OS to conserve memory */ - if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT) - kfree(rbuf); - else - n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf); - tty_unlock(); - return ret; - -} /* end of n_hdlc_tty_read() */ - -/** - * n_hdlc_tty_write - write a single frame of data to device - * @tty - pointer to associated tty device instance data - * @file - pointer to file object data - * @data - pointer to transmit data (one frame) - * @count - size of transmit frame in bytes - * - * Returns the number of bytes written (or error code). - */ -static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, - const unsigned char *data, size_t count) -{ - struct n_hdlc *n_hdlc = tty2n_hdlc (tty); - int error = 0; - DECLARE_WAITQUEUE(wait, current); - struct n_hdlc_buf *tbuf; - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_write() called count=%Zd\n", - __FILE__,__LINE__,count); - - /* Verify pointers */ - if (!n_hdlc) - return -EIO; - - if (n_hdlc->magic != HDLC_MAGIC) - return -EIO; - - /* verify frame size */ - if (count > maxframe ) { - if (debuglevel & DEBUG_LEVEL_INFO) - printk (KERN_WARNING - "n_hdlc_tty_write: truncating user packet " - "from %lu to %d\n", (unsigned long) count, - maxframe ); - count = maxframe; - } - - tty_lock(); - - add_wait_queue(&tty->write_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - /* Allocate transmit buffer */ - /* sleep until transmit buffer available */ - while (!(tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list))) { - if (file->f_flags & O_NONBLOCK) { - error = -EAGAIN; - break; - } - schedule(); - - n_hdlc = tty2n_hdlc (tty); - if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || - tty != n_hdlc->tty) { - printk("n_hdlc_tty_write: %p invalid after wait!\n", n_hdlc); - error = -EIO; - break; - } - - if (signal_pending(current)) { - error = -EINTR; - break; - } - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&tty->write_wait, &wait); - - if (!error) { - /* Retrieve the user's buffer */ - memcpy(tbuf->buf, data, count); - - /* Send the data */ - tbuf->count = error = count; - n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf); - n_hdlc_send_frames(n_hdlc,tty); - } - tty_unlock(); - return error; - -} /* end of n_hdlc_tty_write() */ - -/** - * n_hdlc_tty_ioctl - process IOCTL system call for the tty device. - * @tty - pointer to tty instance data - * @file - pointer to open file object for device - * @cmd - IOCTL command code - * @arg - argument for IOCTL call (cmd dependent) - * - * Returns command dependent result. - */ -static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct n_hdlc *n_hdlc = tty2n_hdlc (tty); - int error = 0; - int count; - unsigned long flags; - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_ioctl() called %d\n", - __FILE__,__LINE__,cmd); - - /* Verify the status of the device */ - if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC) - return -EBADF; - - switch (cmd) { - case FIONREAD: - /* report count of read data available */ - /* in next available frame (if any) */ - spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); - if (n_hdlc->rx_buf_list.head) - count = n_hdlc->rx_buf_list.head->count; - else - count = 0; - spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); - error = put_user(count, (int __user *)arg); - break; - - case TIOCOUTQ: - /* get the pending tx byte count in the driver */ - count = tty_chars_in_buffer(tty); - /* add size of next output frame in queue */ - spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags); - if (n_hdlc->tx_buf_list.head) - count += n_hdlc->tx_buf_list.head->count; - spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags); - error = put_user(count, (int __user *)arg); - break; - - case TCFLSH: - switch (arg) { - case TCIOFLUSH: - case TCOFLUSH: - flush_tx_queue(tty); - } - /* fall through to default */ - - default: - error = n_tty_ioctl_helper(tty, file, cmd, arg); - break; - } - return error; - -} /* end of n_hdlc_tty_ioctl() */ - -/** - * n_hdlc_tty_poll - TTY callback for poll system call - * @tty - pointer to tty instance data - * @filp - pointer to open file object for device - * @poll_table - wait queue for operations - * - * Determine which operations (read/write) will not block and return info - * to caller. - * Returns a bit mask containing info on which ops will not block. - */ -static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, - poll_table *wait) -{ - struct n_hdlc *n_hdlc = tty2n_hdlc (tty); - unsigned int mask = 0; - - if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_tty_poll() called\n",__FILE__,__LINE__); - - if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) { - /* queue current process into any wait queue that */ - /* may awaken in the future (read and write) */ - - poll_wait(filp, &tty->read_wait, wait); - poll_wait(filp, &tty->write_wait, wait); - - /* set bits for operations that won't block */ - if (n_hdlc->rx_buf_list.head) - mask |= POLLIN | POLLRDNORM; /* readable */ - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) - mask |= POLLHUP; - if (tty_hung_up_p(filp)) - mask |= POLLHUP; - if (!tty_is_writelocked(tty) && - n_hdlc->tx_free_buf_list.head) - mask |= POLLOUT | POLLWRNORM; /* writable */ - } - return mask; -} /* end of n_hdlc_tty_poll() */ - -/** - * n_hdlc_alloc - allocate an n_hdlc instance data structure - * - * Returns a pointer to newly created structure if success, otherwise %NULL - */ -static struct n_hdlc *n_hdlc_alloc(void) -{ - struct n_hdlc_buf *buf; - int i; - struct n_hdlc *n_hdlc = kmalloc(sizeof(*n_hdlc), GFP_KERNEL); - - if (!n_hdlc) - return NULL; - - memset(n_hdlc, 0, sizeof(*n_hdlc)); - - n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list); - n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list); - n_hdlc_buf_list_init(&n_hdlc->rx_buf_list); - n_hdlc_buf_list_init(&n_hdlc->tx_buf_list); - - /* allocate free rx buffer list */ - for(i=0;irx_free_buf_list,buf); - else if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_alloc(), kalloc() failed for rx buffer %d\n",__FILE__,__LINE__, i); - } - - /* allocate free tx buffer list */ - for(i=0;itx_free_buf_list,buf); - else if (debuglevel >= DEBUG_LEVEL_INFO) - printk("%s(%d)n_hdlc_alloc(), kalloc() failed for tx buffer %d\n",__FILE__,__LINE__, i); - } - - /* Initialize the control block */ - n_hdlc->magic = HDLC_MAGIC; - n_hdlc->flags = 0; - - return n_hdlc; - -} /* end of n_hdlc_alloc() */ - -/** - * n_hdlc_buf_list_init - initialize specified HDLC buffer list - * @list - pointer to buffer list - */ -static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list) -{ - memset(list, 0, sizeof(*list)); - spin_lock_init(&list->spinlock); -} /* end of n_hdlc_buf_list_init() */ - -/** - * n_hdlc_buf_put - add specified HDLC buffer to tail of specified list - * @list - pointer to buffer list - * @buf - pointer to buffer - */ -static void n_hdlc_buf_put(struct n_hdlc_buf_list *list, - struct n_hdlc_buf *buf) -{ - unsigned long flags; - spin_lock_irqsave(&list->spinlock,flags); - - buf->link=NULL; - if (list->tail) - list->tail->link = buf; - else - list->head = buf; - list->tail = buf; - (list->count)++; - - spin_unlock_irqrestore(&list->spinlock,flags); - -} /* end of n_hdlc_buf_put() */ - -/** - * n_hdlc_buf_get - remove and return an HDLC buffer from list - * @list - pointer to HDLC buffer list - * - * Remove and return an HDLC buffer from the head of the specified HDLC buffer - * list. - * Returns a pointer to HDLC buffer if available, otherwise %NULL. - */ -static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list) -{ - unsigned long flags; - struct n_hdlc_buf *buf; - spin_lock_irqsave(&list->spinlock,flags); - - buf = list->head; - if (buf) { - list->head = buf->link; - (list->count)--; - } - if (!list->head) - list->tail = NULL; - - spin_unlock_irqrestore(&list->spinlock,flags); - return buf; - -} /* end of n_hdlc_buf_get() */ - -static char hdlc_banner[] __initdata = - KERN_INFO "HDLC line discipline maxframe=%u\n"; -static char hdlc_register_ok[] __initdata = - KERN_INFO "N_HDLC line discipline registered.\n"; -static char hdlc_register_fail[] __initdata = - KERN_ERR "error registering line discipline: %d\n"; -static char hdlc_init_fail[] __initdata = - KERN_INFO "N_HDLC: init failure %d\n"; - -static int __init n_hdlc_init(void) -{ - int status; - - /* range check maxframe arg */ - if (maxframe < 4096) - maxframe = 4096; - else if (maxframe > 65535) - maxframe = 65535; - - printk(hdlc_banner, maxframe); - - status = tty_register_ldisc(N_HDLC, &n_hdlc_ldisc); - if (!status) - printk(hdlc_register_ok); - else - printk(hdlc_register_fail, status); - - if (status) - printk(hdlc_init_fail, status); - return status; - -} /* end of init_module() */ - -static char hdlc_unregister_ok[] __exitdata = - KERN_INFO "N_HDLC: line discipline unregistered\n"; -static char hdlc_unregister_fail[] __exitdata = - KERN_ERR "N_HDLC: can't unregister line discipline (err = %d)\n"; - -static void __exit n_hdlc_exit(void) -{ - /* Release tty registration of line discipline */ - int status = tty_unregister_ldisc(N_HDLC); - - if (status) - printk(hdlc_unregister_fail, status); - else - printk(hdlc_unregister_ok); -} - -module_init(n_hdlc_init); -module_exit(n_hdlc_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Paul Fulghum paulkf@microgate.com"); -module_param(debuglevel, int, 0); -module_param(maxframe, int, 0); -MODULE_ALIAS_LDISC(N_HDLC); diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c deleted file mode 100644 index 88dda0c45ee0..000000000000 --- a/drivers/char/n_r3964.c +++ /dev/null @@ -1,1264 +0,0 @@ -/* r3964 linediscipline for linux - * - * ----------------------------------------------------------- - * Copyright by - * Philips Automation Projects - * Kassel (Germany) - * ----------------------------------------------------------- - * This software may be used and distributed according to the terms of - * the GNU General Public License, incorporated herein by reference. - * - * Author: - * L. Haag - * - * $Log: n_r3964.c,v $ - * Revision 1.10 2001/03/18 13:02:24 dwmw2 - * Fix timer usage, use spinlocks properly. - * - * Revision 1.9 2001/03/18 12:52:14 dwmw2 - * Merge changes in 2.4.2 - * - * Revision 1.8 2000/03/23 14:14:54 dwmw2 - * Fix race in sleeping in r3964_read() - * - * Revision 1.7 1999/28/08 11:41:50 dwmw2 - * Port to 2.3 kernel - * - * Revision 1.6 1998/09/30 00:40:40 dwmw2 - * Fixed compilation on 2.0.x kernels - * Updated to newly registered tty-ldisc number 9 - * - * Revision 1.5 1998/09/04 21:57:36 dwmw2 - * Signal handling bug fixes, port to 2.1.x. - * - * Revision 1.4 1998/04/02 20:26:59 lhaag - * select, blocking, ... - * - * Revision 1.3 1998/02/12 18:58:43 root - * fixed some memory leaks - * calculation of checksum characters - * - * Revision 1.2 1998/02/07 13:03:34 root - * ioctl read_telegram - * - * Revision 1.1 1998/02/06 19:21:03 root - * Initial revision - * - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* used in new tty drivers */ -#include /* used in new tty drivers */ -#include -#include -#include -#include -#include - -/*#define DEBUG_QUEUE*/ - -/* Log successful handshake and protocol operations */ -/*#define DEBUG_PROTO_S*/ - -/* Log handshake and protocol errors: */ -/*#define DEBUG_PROTO_E*/ - -/* Log Linediscipline operations (open, close, read, write...): */ -/*#define DEBUG_LDISC*/ - -/* Log module and memory operations (init, cleanup; kmalloc, kfree): */ -/*#define DEBUG_MODUL*/ - -/* Macro helpers for debug output: */ -#define TRACE(format, args...) printk("r3964: " format "\n" , ## args) - -#ifdef DEBUG_MODUL -#define TRACE_M(format, args...) printk("r3964: " format "\n" , ## args) -#else -#define TRACE_M(fmt, arg...) do {} while (0) -#endif -#ifdef DEBUG_PROTO_S -#define TRACE_PS(format, args...) printk("r3964: " format "\n" , ## args) -#else -#define TRACE_PS(fmt, arg...) do {} while (0) -#endif -#ifdef DEBUG_PROTO_E -#define TRACE_PE(format, args...) printk("r3964: " format "\n" , ## args) -#else -#define TRACE_PE(fmt, arg...) do {} while (0) -#endif -#ifdef DEBUG_LDISC -#define TRACE_L(format, args...) printk("r3964: " format "\n" , ## args) -#else -#define TRACE_L(fmt, arg...) do {} while (0) -#endif -#ifdef DEBUG_QUEUE -#define TRACE_Q(format, args...) printk("r3964: " format "\n" , ## args) -#else -#define TRACE_Q(fmt, arg...) do {} while (0) -#endif -static void add_tx_queue(struct r3964_info *, struct r3964_block_header *); -static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code); -static void put_char(struct r3964_info *pInfo, unsigned char ch); -static void trigger_transmit(struct r3964_info *pInfo); -static void retry_transmit(struct r3964_info *pInfo); -static void transmit_block(struct r3964_info *pInfo); -static void receive_char(struct r3964_info *pInfo, const unsigned char c); -static void receive_error(struct r3964_info *pInfo, const char flag); -static void on_timeout(unsigned long priv); -static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg); -static int read_telegram(struct r3964_info *pInfo, struct pid *pid, - unsigned char __user * buf); -static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg, - int error_code, struct r3964_block_header *pBlock); -static struct r3964_message *remove_msg(struct r3964_info *pInfo, - struct r3964_client_info *pClient); -static void remove_client_block(struct r3964_info *pInfo, - struct r3964_client_info *pClient); - -static int r3964_open(struct tty_struct *tty); -static void r3964_close(struct tty_struct *tty); -static ssize_t r3964_read(struct tty_struct *tty, struct file *file, - unsigned char __user * buf, size_t nr); -static ssize_t r3964_write(struct tty_struct *tty, struct file *file, - const unsigned char *buf, size_t nr); -static int r3964_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg); -static void r3964_set_termios(struct tty_struct *tty, struct ktermios *old); -static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, - struct poll_table_struct *wait); -static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count); - -static struct tty_ldisc_ops tty_ldisc_N_R3964 = { - .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, - .name = "R3964", - .open = r3964_open, - .close = r3964_close, - .read = r3964_read, - .write = r3964_write, - .ioctl = r3964_ioctl, - .set_termios = r3964_set_termios, - .poll = r3964_poll, - .receive_buf = r3964_receive_buf, -}; - -static void dump_block(const unsigned char *block, unsigned int length) -{ - unsigned int i, j; - char linebuf[16 * 3 + 1]; - - for (i = 0; i < length; i += 16) { - for (j = 0; (j < 16) && (j + i < length); j++) { - sprintf(linebuf + 3 * j, "%02x ", block[i + j]); - } - linebuf[3 * j] = '\0'; - TRACE_PS("%s", linebuf); - } -} - -/************************************************************* - * Driver initialisation - *************************************************************/ - -/************************************************************* - * Module support routines - *************************************************************/ - -static void __exit r3964_exit(void) -{ - int status; - - TRACE_M("cleanup_module()"); - - status = tty_unregister_ldisc(N_R3964); - - if (status != 0) { - printk(KERN_ERR "r3964: error unregistering linediscipline: " - "%d\n", status); - } else { - TRACE_L("linediscipline successfully unregistered"); - } -} - -static int __init r3964_init(void) -{ - int status; - - printk("r3964: Philips r3964 Driver $Revision: 1.10 $\n"); - - /* - * Register the tty line discipline - */ - - status = tty_register_ldisc(N_R3964, &tty_ldisc_N_R3964); - if (status == 0) { - TRACE_L("line discipline %d registered", N_R3964); - TRACE_L("flags=%x num=%x", tty_ldisc_N_R3964.flags, - tty_ldisc_N_R3964.num); - TRACE_L("open=%p", tty_ldisc_N_R3964.open); - TRACE_L("tty_ldisc_N_R3964 = %p", &tty_ldisc_N_R3964); - } else { - printk(KERN_ERR "r3964: error registering line discipline: " - "%d\n", status); - } - return status; -} - -module_init(r3964_init); -module_exit(r3964_exit); - -/************************************************************* - * Protocol implementation routines - *************************************************************/ - -static void add_tx_queue(struct r3964_info *pInfo, - struct r3964_block_header *pHeader) -{ - unsigned long flags; - - spin_lock_irqsave(&pInfo->lock, flags); - - pHeader->next = NULL; - - if (pInfo->tx_last == NULL) { - pInfo->tx_first = pInfo->tx_last = pHeader; - } else { - pInfo->tx_last->next = pHeader; - pInfo->tx_last = pHeader; - } - - spin_unlock_irqrestore(&pInfo->lock, flags); - - TRACE_Q("add_tx_queue %p, length %d, tx_first = %p", - pHeader, pHeader->length, pInfo->tx_first); -} - -static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code) -{ - struct r3964_block_header *pHeader; - unsigned long flags; -#ifdef DEBUG_QUEUE - struct r3964_block_header *pDump; -#endif - - pHeader = pInfo->tx_first; - - if (pHeader == NULL) - return; - -#ifdef DEBUG_QUEUE - printk("r3964: remove_from_tx_queue: %p, length %u - ", - pHeader, pHeader->length); - for (pDump = pHeader; pDump; pDump = pDump->next) - printk("%p ", pDump); - printk("\n"); -#endif - - if (pHeader->owner) { - if (error_code) { - add_msg(pHeader->owner, R3964_MSG_ACK, 0, - error_code, NULL); - } else { - add_msg(pHeader->owner, R3964_MSG_ACK, pHeader->length, - error_code, NULL); - } - wake_up_interruptible(&pInfo->read_wait); - } - - spin_lock_irqsave(&pInfo->lock, flags); - - pInfo->tx_first = pHeader->next; - if (pInfo->tx_first == NULL) { - pInfo->tx_last = NULL; - } - - spin_unlock_irqrestore(&pInfo->lock, flags); - - kfree(pHeader); - TRACE_M("remove_from_tx_queue - kfree %p", pHeader); - - TRACE_Q("remove_from_tx_queue: tx_first = %p, tx_last = %p", - pInfo->tx_first, pInfo->tx_last); -} - -static void add_rx_queue(struct r3964_info *pInfo, - struct r3964_block_header *pHeader) -{ - unsigned long flags; - - spin_lock_irqsave(&pInfo->lock, flags); - - pHeader->next = NULL; - - if (pInfo->rx_last == NULL) { - pInfo->rx_first = pInfo->rx_last = pHeader; - } else { - pInfo->rx_last->next = pHeader; - pInfo->rx_last = pHeader; - } - pInfo->blocks_in_rx_queue++; - - spin_unlock_irqrestore(&pInfo->lock, flags); - - TRACE_Q("add_rx_queue: %p, length = %d, rx_first = %p, count = %d", - pHeader, pHeader->length, - pInfo->rx_first, pInfo->blocks_in_rx_queue); -} - -static void remove_from_rx_queue(struct r3964_info *pInfo, - struct r3964_block_header *pHeader) -{ - unsigned long flags; - struct r3964_block_header *pFind; - - if (pHeader == NULL) - return; - - TRACE_Q("remove_from_rx_queue: rx_first = %p, rx_last = %p, count = %d", - pInfo->rx_first, pInfo->rx_last, pInfo->blocks_in_rx_queue); - TRACE_Q("remove_from_rx_queue: %p, length %u", - pHeader, pHeader->length); - - spin_lock_irqsave(&pInfo->lock, flags); - - if (pInfo->rx_first == pHeader) { - /* Remove the first block in the linked list: */ - pInfo->rx_first = pHeader->next; - - if (pInfo->rx_first == NULL) { - pInfo->rx_last = NULL; - } - pInfo->blocks_in_rx_queue--; - } else { - /* Find block to remove: */ - for (pFind = pInfo->rx_first; pFind; pFind = pFind->next) { - if (pFind->next == pHeader) { - /* Got it. */ - pFind->next = pHeader->next; - pInfo->blocks_in_rx_queue--; - if (pFind->next == NULL) { - /* Oh, removed the last one! */ - pInfo->rx_last = pFind; - } - break; - } - } - } - - spin_unlock_irqrestore(&pInfo->lock, flags); - - kfree(pHeader); - TRACE_M("remove_from_rx_queue - kfree %p", pHeader); - - TRACE_Q("remove_from_rx_queue: rx_first = %p, rx_last = %p, count = %d", - pInfo->rx_first, pInfo->rx_last, pInfo->blocks_in_rx_queue); -} - -static void put_char(struct r3964_info *pInfo, unsigned char ch) -{ - struct tty_struct *tty = pInfo->tty; - /* FIXME: put_char should not be called from an IRQ */ - tty_put_char(tty, ch); - pInfo->bcc ^= ch; -} - -static void flush(struct r3964_info *pInfo) -{ - struct tty_struct *tty = pInfo->tty; - - if (tty == NULL || tty->ops->flush_chars == NULL) - return; - tty->ops->flush_chars(tty); -} - -static void trigger_transmit(struct r3964_info *pInfo) -{ - unsigned long flags; - - spin_lock_irqsave(&pInfo->lock, flags); - - if ((pInfo->state == R3964_IDLE) && (pInfo->tx_first != NULL)) { - pInfo->state = R3964_TX_REQUEST; - pInfo->nRetry = 0; - pInfo->flags &= ~R3964_ERROR; - mod_timer(&pInfo->tmr, jiffies + R3964_TO_QVZ); - - spin_unlock_irqrestore(&pInfo->lock, flags); - - TRACE_PS("trigger_transmit - sent STX"); - - put_char(pInfo, STX); - flush(pInfo); - - pInfo->bcc = 0; - } else { - spin_unlock_irqrestore(&pInfo->lock, flags); - } -} - -static void retry_transmit(struct r3964_info *pInfo) -{ - if (pInfo->nRetry < R3964_MAX_RETRIES) { - TRACE_PE("transmission failed. Retry #%d", pInfo->nRetry); - pInfo->bcc = 0; - put_char(pInfo, STX); - flush(pInfo); - pInfo->state = R3964_TX_REQUEST; - pInfo->nRetry++; - mod_timer(&pInfo->tmr, jiffies + R3964_TO_QVZ); - } else { - TRACE_PE("transmission failed after %d retries", - R3964_MAX_RETRIES); - - remove_from_tx_queue(pInfo, R3964_TX_FAIL); - - put_char(pInfo, NAK); - flush(pInfo); - pInfo->state = R3964_IDLE; - - trigger_transmit(pInfo); - } -} - -static void transmit_block(struct r3964_info *pInfo) -{ - struct tty_struct *tty = pInfo->tty; - struct r3964_block_header *pBlock = pInfo->tx_first; - int room = 0; - - if (tty == NULL || pBlock == NULL) { - return; - } - - room = tty_write_room(tty); - - TRACE_PS("transmit_block %p, room %d, length %d", - pBlock, room, pBlock->length); - - while (pInfo->tx_position < pBlock->length) { - if (room < 2) - break; - - if (pBlock->data[pInfo->tx_position] == DLE) { - /* send additional DLE char: */ - put_char(pInfo, DLE); - } - put_char(pInfo, pBlock->data[pInfo->tx_position++]); - - room--; - } - - if ((pInfo->tx_position == pBlock->length) && (room >= 3)) { - put_char(pInfo, DLE); - put_char(pInfo, ETX); - if (pInfo->flags & R3964_BCC) { - put_char(pInfo, pInfo->bcc); - } - pInfo->state = R3964_WAIT_FOR_TX_ACK; - mod_timer(&pInfo->tmr, jiffies + R3964_TO_QVZ); - } - flush(pInfo); -} - -static void on_receive_block(struct r3964_info *pInfo) -{ - unsigned int length; - struct r3964_client_info *pClient; - struct r3964_block_header *pBlock; - - length = pInfo->rx_position; - - /* compare byte checksum characters: */ - if (pInfo->flags & R3964_BCC) { - if (pInfo->bcc != pInfo->last_rx) { - TRACE_PE("checksum error - got %x but expected %x", - pInfo->last_rx, pInfo->bcc); - pInfo->flags |= R3964_CHECKSUM; - } - } - - /* check for errors (parity, overrun,...): */ - if (pInfo->flags & R3964_ERROR) { - TRACE_PE("on_receive_block - transmission failed error %x", - pInfo->flags & R3964_ERROR); - - put_char(pInfo, NAK); - flush(pInfo); - if (pInfo->nRetry < R3964_MAX_RETRIES) { - pInfo->state = R3964_WAIT_FOR_RX_REPEAT; - pInfo->nRetry++; - mod_timer(&pInfo->tmr, jiffies + R3964_TO_RX_PANIC); - } else { - TRACE_PE("on_receive_block - failed after max retries"); - pInfo->state = R3964_IDLE; - } - return; - } - - /* received block; submit DLE: */ - put_char(pInfo, DLE); - flush(pInfo); - del_timer_sync(&pInfo->tmr); - TRACE_PS(" rx success: got %d chars", length); - - /* prepare struct r3964_block_header: */ - pBlock = kmalloc(length + sizeof(struct r3964_block_header), - GFP_KERNEL); - TRACE_M("on_receive_block - kmalloc %p", pBlock); - - if (pBlock == NULL) - return; - - pBlock->length = length; - pBlock->data = ((unsigned char *)pBlock) + - sizeof(struct r3964_block_header); - pBlock->locks = 0; - pBlock->next = NULL; - pBlock->owner = NULL; - - memcpy(pBlock->data, pInfo->rx_buf, length); - - /* queue block into rx_queue: */ - add_rx_queue(pInfo, pBlock); - - /* notify attached client processes: */ - for (pClient = pInfo->firstClient; pClient; pClient = pClient->next) { - if (pClient->sig_flags & R3964_SIG_DATA) { - add_msg(pClient, R3964_MSG_DATA, length, R3964_OK, - pBlock); - } - } - wake_up_interruptible(&pInfo->read_wait); - - pInfo->state = R3964_IDLE; - - trigger_transmit(pInfo); -} - -static void receive_char(struct r3964_info *pInfo, const unsigned char c) -{ - switch (pInfo->state) { - case R3964_TX_REQUEST: - if (c == DLE) { - TRACE_PS("TX_REQUEST - got DLE"); - - pInfo->state = R3964_TRANSMITTING; - pInfo->tx_position = 0; - - transmit_block(pInfo); - } else if (c == STX) { - if (pInfo->nRetry == 0) { - TRACE_PE("TX_REQUEST - init conflict"); - if (pInfo->priority == R3964_SLAVE) { - goto start_receiving; - } - } else { - TRACE_PE("TX_REQUEST - secondary init " - "conflict!? Switching to SLAVE mode " - "for next rx."); - goto start_receiving; - } - } else { - TRACE_PE("TX_REQUEST - char != DLE: %x", c); - retry_transmit(pInfo); - } - break; - case R3964_TRANSMITTING: - if (c == NAK) { - TRACE_PE("TRANSMITTING - got NAK"); - retry_transmit(pInfo); - } else { - TRACE_PE("TRANSMITTING - got invalid char"); - - pInfo->state = R3964_WAIT_ZVZ_BEFORE_TX_RETRY; - mod_timer(&pInfo->tmr, jiffies + R3964_TO_ZVZ); - } - break; - case R3964_WAIT_FOR_TX_ACK: - if (c == DLE) { - TRACE_PS("WAIT_FOR_TX_ACK - got DLE"); - remove_from_tx_queue(pInfo, R3964_OK); - - pInfo->state = R3964_IDLE; - trigger_transmit(pInfo); - } else { - retry_transmit(pInfo); - } - break; - case R3964_WAIT_FOR_RX_REPEAT: - /* FALLTHROUGH */ - case R3964_IDLE: - if (c == STX) { - /* Prevent rx_queue from overflow: */ - if (pInfo->blocks_in_rx_queue >= - R3964_MAX_BLOCKS_IN_RX_QUEUE) { - TRACE_PE("IDLE - got STX but no space in " - "rx_queue!"); - pInfo->state = R3964_WAIT_FOR_RX_BUF; - mod_timer(&pInfo->tmr, - jiffies + R3964_TO_NO_BUF); - break; - } -start_receiving: - /* Ok, start receiving: */ - TRACE_PS("IDLE - got STX"); - pInfo->rx_position = 0; - pInfo->last_rx = 0; - pInfo->flags &= ~R3964_ERROR; - pInfo->state = R3964_RECEIVING; - mod_timer(&pInfo->tmr, jiffies + R3964_TO_ZVZ); - pInfo->nRetry = 0; - put_char(pInfo, DLE); - flush(pInfo); - pInfo->bcc = 0; - } - break; - case R3964_RECEIVING: - if (pInfo->rx_position < RX_BUF_SIZE) { - pInfo->bcc ^= c; - - if (c == DLE) { - if (pInfo->last_rx == DLE) { - pInfo->last_rx = 0; - goto char_to_buf; - } - pInfo->last_rx = DLE; - break; - } else if ((c == ETX) && (pInfo->last_rx == DLE)) { - if (pInfo->flags & R3964_BCC) { - pInfo->state = R3964_WAIT_FOR_BCC; - mod_timer(&pInfo->tmr, - jiffies + R3964_TO_ZVZ); - } else { - on_receive_block(pInfo); - } - } else { - pInfo->last_rx = c; -char_to_buf: - pInfo->rx_buf[pInfo->rx_position++] = c; - mod_timer(&pInfo->tmr, jiffies + R3964_TO_ZVZ); - } - } - /* else: overflow-msg? BUF_SIZE>MTU; should not happen? */ - break; - case R3964_WAIT_FOR_BCC: - pInfo->last_rx = c; - on_receive_block(pInfo); - break; - } -} - -static void receive_error(struct r3964_info *pInfo, const char flag) -{ - switch (flag) { - case TTY_NORMAL: - break; - case TTY_BREAK: - TRACE_PE("received break"); - pInfo->flags |= R3964_BREAK; - break; - case TTY_PARITY: - TRACE_PE("parity error"); - pInfo->flags |= R3964_PARITY; - break; - case TTY_FRAME: - TRACE_PE("frame error"); - pInfo->flags |= R3964_FRAME; - break; - case TTY_OVERRUN: - TRACE_PE("frame overrun"); - pInfo->flags |= R3964_OVERRUN; - break; - default: - TRACE_PE("receive_error - unknown flag %d", flag); - pInfo->flags |= R3964_UNKNOWN; - break; - } -} - -static void on_timeout(unsigned long priv) -{ - struct r3964_info *pInfo = (void *)priv; - - switch (pInfo->state) { - case R3964_TX_REQUEST: - TRACE_PE("TX_REQUEST - timeout"); - retry_transmit(pInfo); - break; - case R3964_WAIT_ZVZ_BEFORE_TX_RETRY: - put_char(pInfo, NAK); - flush(pInfo); - retry_transmit(pInfo); - break; - case R3964_WAIT_FOR_TX_ACK: - TRACE_PE("WAIT_FOR_TX_ACK - timeout"); - retry_transmit(pInfo); - break; - case R3964_WAIT_FOR_RX_BUF: - TRACE_PE("WAIT_FOR_RX_BUF - timeout"); - put_char(pInfo, NAK); - flush(pInfo); - pInfo->state = R3964_IDLE; - break; - case R3964_RECEIVING: - TRACE_PE("RECEIVING - timeout after %d chars", - pInfo->rx_position); - put_char(pInfo, NAK); - flush(pInfo); - pInfo->state = R3964_IDLE; - break; - case R3964_WAIT_FOR_RX_REPEAT: - TRACE_PE("WAIT_FOR_RX_REPEAT - timeout"); - pInfo->state = R3964_IDLE; - break; - case R3964_WAIT_FOR_BCC: - TRACE_PE("WAIT_FOR_BCC - timeout"); - put_char(pInfo, NAK); - flush(pInfo); - pInfo->state = R3964_IDLE; - break; - } -} - -static struct r3964_client_info *findClient(struct r3964_info *pInfo, - struct pid *pid) -{ - struct r3964_client_info *pClient; - - for (pClient = pInfo->firstClient; pClient; pClient = pClient->next) { - if (pClient->pid == pid) { - return pClient; - } - } - return NULL; -} - -static int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg) -{ - struct r3964_client_info *pClient; - struct r3964_client_info **ppClient; - struct r3964_message *pMsg; - - if ((arg & R3964_SIG_ALL) == 0) { - /* Remove client from client list */ - for (ppClient = &pInfo->firstClient; *ppClient; - ppClient = &(*ppClient)->next) { - pClient = *ppClient; - - if (pClient->pid == pid) { - TRACE_PS("removing client %d from client list", - pid_nr(pid)); - *ppClient = pClient->next; - while (pClient->msg_count) { - pMsg = remove_msg(pInfo, pClient); - if (pMsg) { - kfree(pMsg); - TRACE_M("enable_signals - msg " - "kfree %p", pMsg); - } - } - put_pid(pClient->pid); - kfree(pClient); - TRACE_M("enable_signals - kfree %p", pClient); - return 0; - } - } - return -EINVAL; - } else { - pClient = findClient(pInfo, pid); - if (pClient) { - /* update signal options */ - pClient->sig_flags = arg; - } else { - /* add client to client list */ - pClient = kmalloc(sizeof(struct r3964_client_info), - GFP_KERNEL); - TRACE_M("enable_signals - kmalloc %p", pClient); - if (pClient == NULL) - return -ENOMEM; - - TRACE_PS("add client %d to client list", pid_nr(pid)); - spin_lock_init(&pClient->lock); - pClient->sig_flags = arg; - pClient->pid = get_pid(pid); - pClient->next = pInfo->firstClient; - pClient->first_msg = NULL; - pClient->last_msg = NULL; - pClient->next_block_to_read = NULL; - pClient->msg_count = 0; - pInfo->firstClient = pClient; - } - } - - return 0; -} - -static int read_telegram(struct r3964_info *pInfo, struct pid *pid, - unsigned char __user * buf) -{ - struct r3964_client_info *pClient; - struct r3964_block_header *block; - - if (!buf) { - return -EINVAL; - } - - pClient = findClient(pInfo, pid); - if (pClient == NULL) { - return -EINVAL; - } - - block = pClient->next_block_to_read; - if (!block) { - return 0; - } else { - if (copy_to_user(buf, block->data, block->length)) - return -EFAULT; - - remove_client_block(pInfo, pClient); - return block->length; - } - - return -EINVAL; -} - -static void add_msg(struct r3964_client_info *pClient, int msg_id, int arg, - int error_code, struct r3964_block_header *pBlock) -{ - struct r3964_message *pMsg; - unsigned long flags; - - if (pClient->msg_count < R3964_MAX_MSG_COUNT - 1) { -queue_the_message: - - pMsg = kmalloc(sizeof(struct r3964_message), - error_code ? GFP_ATOMIC : GFP_KERNEL); - TRACE_M("add_msg - kmalloc %p", pMsg); - if (pMsg == NULL) { - return; - } - - spin_lock_irqsave(&pClient->lock, flags); - - pMsg->msg_id = msg_id; - pMsg->arg = arg; - pMsg->error_code = error_code; - pMsg->block = pBlock; - pMsg->next = NULL; - - if (pClient->last_msg == NULL) { - pClient->first_msg = pClient->last_msg = pMsg; - } else { - pClient->last_msg->next = pMsg; - pClient->last_msg = pMsg; - } - - pClient->msg_count++; - - if (pBlock != NULL) { - pBlock->locks++; - } - spin_unlock_irqrestore(&pClient->lock, flags); - } else { - if ((pClient->last_msg->msg_id == R3964_MSG_ACK) - && (pClient->last_msg->error_code == R3964_OVERFLOW)) { - pClient->last_msg->arg++; - TRACE_PE("add_msg - inc prev OVERFLOW-msg"); - } else { - msg_id = R3964_MSG_ACK; - arg = 0; - error_code = R3964_OVERFLOW; - pBlock = NULL; - TRACE_PE("add_msg - queue OVERFLOW-msg"); - goto queue_the_message; - } - } - /* Send SIGIO signal to client process: */ - if (pClient->sig_flags & R3964_USE_SIGIO) { - kill_pid(pClient->pid, SIGIO, 1); - } -} - -static struct r3964_message *remove_msg(struct r3964_info *pInfo, - struct r3964_client_info *pClient) -{ - struct r3964_message *pMsg = NULL; - unsigned long flags; - - if (pClient->first_msg) { - spin_lock_irqsave(&pClient->lock, flags); - - pMsg = pClient->first_msg; - pClient->first_msg = pMsg->next; - if (pClient->first_msg == NULL) { - pClient->last_msg = NULL; - } - - pClient->msg_count--; - if (pMsg->block) { - remove_client_block(pInfo, pClient); - pClient->next_block_to_read = pMsg->block; - } - spin_unlock_irqrestore(&pClient->lock, flags); - } - return pMsg; -} - -static void remove_client_block(struct r3964_info *pInfo, - struct r3964_client_info *pClient) -{ - struct r3964_block_header *block; - - TRACE_PS("remove_client_block PID %d", pid_nr(pClient->pid)); - - block = pClient->next_block_to_read; - if (block) { - block->locks--; - if (block->locks == 0) { - remove_from_rx_queue(pInfo, block); - } - } - pClient->next_block_to_read = NULL; -} - -/************************************************************* - * Line discipline routines - *************************************************************/ - -static int r3964_open(struct tty_struct *tty) -{ - struct r3964_info *pInfo; - - TRACE_L("open"); - TRACE_L("tty=%p, PID=%d, disc_data=%p", - tty, current->pid, tty->disc_data); - - pInfo = kmalloc(sizeof(struct r3964_info), GFP_KERNEL); - TRACE_M("r3964_open - info kmalloc %p", pInfo); - - if (!pInfo) { - printk(KERN_ERR "r3964: failed to alloc info structure\n"); - return -ENOMEM; - } - - pInfo->rx_buf = kmalloc(RX_BUF_SIZE, GFP_KERNEL); - TRACE_M("r3964_open - rx_buf kmalloc %p", pInfo->rx_buf); - - if (!pInfo->rx_buf) { - printk(KERN_ERR "r3964: failed to alloc receive buffer\n"); - kfree(pInfo); - TRACE_M("r3964_open - info kfree %p", pInfo); - return -ENOMEM; - } - - pInfo->tx_buf = kmalloc(TX_BUF_SIZE, GFP_KERNEL); - TRACE_M("r3964_open - tx_buf kmalloc %p", pInfo->tx_buf); - - if (!pInfo->tx_buf) { - printk(KERN_ERR "r3964: failed to alloc transmit buffer\n"); - kfree(pInfo->rx_buf); - TRACE_M("r3964_open - rx_buf kfree %p", pInfo->rx_buf); - kfree(pInfo); - TRACE_M("r3964_open - info kfree %p", pInfo); - return -ENOMEM; - } - - spin_lock_init(&pInfo->lock); - pInfo->tty = tty; - init_waitqueue_head(&pInfo->read_wait); - pInfo->priority = R3964_MASTER; - pInfo->rx_first = pInfo->rx_last = NULL; - pInfo->tx_first = pInfo->tx_last = NULL; - pInfo->rx_position = 0; - pInfo->tx_position = 0; - pInfo->last_rx = 0; - pInfo->blocks_in_rx_queue = 0; - pInfo->firstClient = NULL; - pInfo->state = R3964_IDLE; - pInfo->flags = R3964_DEBUG; - pInfo->nRetry = 0; - - tty->disc_data = pInfo; - tty->receive_room = 65536; - - setup_timer(&pInfo->tmr, on_timeout, (unsigned long)pInfo); - - return 0; -} - -static void r3964_close(struct tty_struct *tty) -{ - struct r3964_info *pInfo = tty->disc_data; - struct r3964_client_info *pClient, *pNext; - struct r3964_message *pMsg; - struct r3964_block_header *pHeader, *pNextHeader; - unsigned long flags; - - TRACE_L("close"); - - /* - * Make sure that our task queue isn't activated. If it - * is, take it out of the linked list. - */ - del_timer_sync(&pInfo->tmr); - - /* Remove client-structs and message queues: */ - pClient = pInfo->firstClient; - while (pClient) { - pNext = pClient->next; - while (pClient->msg_count) { - pMsg = remove_msg(pInfo, pClient); - if (pMsg) { - kfree(pMsg); - TRACE_M("r3964_close - msg kfree %p", pMsg); - } - } - put_pid(pClient->pid); - kfree(pClient); - TRACE_M("r3964_close - client kfree %p", pClient); - pClient = pNext; - } - /* Remove jobs from tx_queue: */ - spin_lock_irqsave(&pInfo->lock, flags); - pHeader = pInfo->tx_first; - pInfo->tx_first = pInfo->tx_last = NULL; - spin_unlock_irqrestore(&pInfo->lock, flags); - - while (pHeader) { - pNextHeader = pHeader->next; - kfree(pHeader); - pHeader = pNextHeader; - } - - /* Free buffers: */ - wake_up_interruptible(&pInfo->read_wait); - kfree(pInfo->rx_buf); - TRACE_M("r3964_close - rx_buf kfree %p", pInfo->rx_buf); - kfree(pInfo->tx_buf); - TRACE_M("r3964_close - tx_buf kfree %p", pInfo->tx_buf); - kfree(pInfo); - TRACE_M("r3964_close - info kfree %p", pInfo); -} - -static ssize_t r3964_read(struct tty_struct *tty, struct file *file, - unsigned char __user * buf, size_t nr) -{ - struct r3964_info *pInfo = tty->disc_data; - struct r3964_client_info *pClient; - struct r3964_message *pMsg; - struct r3964_client_message theMsg; - int ret; - - TRACE_L("read()"); - - tty_lock(); - - pClient = findClient(pInfo, task_pid(current)); - if (pClient) { - pMsg = remove_msg(pInfo, pClient); - if (pMsg == NULL) { - /* no messages available. */ - if (file->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - goto unlock; - } - /* block until there is a message: */ - wait_event_interruptible_tty(pInfo->read_wait, - (pMsg = remove_msg(pInfo, pClient))); - } - - /* If we still haven't got a message, we must have been signalled */ - - if (!pMsg) { - ret = -EINTR; - goto unlock; - } - - /* deliver msg to client process: */ - theMsg.msg_id = pMsg->msg_id; - theMsg.arg = pMsg->arg; - theMsg.error_code = pMsg->error_code; - ret = sizeof(struct r3964_client_message); - - kfree(pMsg); - TRACE_M("r3964_read - msg kfree %p", pMsg); - - if (copy_to_user(buf, &theMsg, ret)) { - ret = -EFAULT; - goto unlock; - } - - TRACE_PS("read - return %d", ret); - goto unlock; - } - ret = -EPERM; -unlock: - tty_unlock(); - return ret; -} - -static ssize_t r3964_write(struct tty_struct *tty, struct file *file, - const unsigned char *data, size_t count) -{ - struct r3964_info *pInfo = tty->disc_data; - struct r3964_block_header *pHeader; - struct r3964_client_info *pClient; - unsigned char *new_data; - - TRACE_L("write request, %d characters", count); -/* - * Verify the pointers - */ - - if (!pInfo) - return -EIO; - -/* - * Ensure that the caller does not wish to send too much. - */ - if (count > R3964_MTU) { - if (pInfo->flags & R3964_DEBUG) { - TRACE_L(KERN_WARNING "r3964_write: truncating user " - "packet from %u to mtu %d", count, R3964_MTU); - } - count = R3964_MTU; - } -/* - * Allocate a buffer for the data and copy it from the buffer with header prepended - */ - new_data = kmalloc(count + sizeof(struct r3964_block_header), - GFP_KERNEL); - TRACE_M("r3964_write - kmalloc %p", new_data); - if (new_data == NULL) { - if (pInfo->flags & R3964_DEBUG) { - printk(KERN_ERR "r3964_write: no memory\n"); - } - return -ENOSPC; - } - - pHeader = (struct r3964_block_header *)new_data; - pHeader->data = new_data + sizeof(struct r3964_block_header); - pHeader->length = count; - pHeader->locks = 0; - pHeader->owner = NULL; - - tty_lock(); - - pClient = findClient(pInfo, task_pid(current)); - if (pClient) { - pHeader->owner = pClient; - } - - memcpy(pHeader->data, data, count); /* We already verified this */ - - if (pInfo->flags & R3964_DEBUG) { - dump_block(pHeader->data, count); - } - -/* - * Add buffer to transmit-queue: - */ - add_tx_queue(pInfo, pHeader); - trigger_transmit(pInfo); - - tty_unlock(); - - return 0; -} - -static int r3964_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct r3964_info *pInfo = tty->disc_data; - if (pInfo == NULL) - return -EINVAL; - switch (cmd) { - case R3964_ENABLE_SIGNALS: - return enable_signals(pInfo, task_pid(current), arg); - case R3964_SETPRIORITY: - if (arg < R3964_MASTER || arg > R3964_SLAVE) - return -EINVAL; - pInfo->priority = arg & 0xff; - return 0; - case R3964_USE_BCC: - if (arg) - pInfo->flags |= R3964_BCC; - else - pInfo->flags &= ~R3964_BCC; - return 0; - case R3964_READ_TELEGRAM: - return read_telegram(pInfo, task_pid(current), - (unsigned char __user *)arg); - default: - return -ENOIOCTLCMD; - } -} - -static void r3964_set_termios(struct tty_struct *tty, struct ktermios *old) -{ - TRACE_L("set_termios"); -} - -/* Called without the kernel lock held - fine */ -static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, - struct poll_table_struct *wait) -{ - struct r3964_info *pInfo = tty->disc_data; - struct r3964_client_info *pClient; - struct r3964_message *pMsg = NULL; - unsigned long flags; - int result = POLLOUT; - - TRACE_L("POLL"); - - pClient = findClient(pInfo, task_pid(current)); - if (pClient) { - poll_wait(file, &pInfo->read_wait, wait); - spin_lock_irqsave(&pInfo->lock, flags); - pMsg = pClient->first_msg; - spin_unlock_irqrestore(&pInfo->lock, flags); - if (pMsg) - result |= POLLIN | POLLRDNORM; - } else { - result = -EINVAL; - } - return result; -} - -static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - struct r3964_info *pInfo = tty->disc_data; - const unsigned char *p; - char *f, flags = 0; - int i; - - for (i = count, p = cp, f = fp; i; i--, p++) { - if (f) - flags = *f++; - if (flags == TTY_NORMAL) { - receive_char(pInfo, *p); - } else { - receive_error(pInfo, flags); - } - - } -} - -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_R3964); diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c deleted file mode 100644 index 428f4fe0b5f7..000000000000 --- a/drivers/char/n_tty.c +++ /dev/null @@ -1,2121 +0,0 @@ -/* - * n_tty.c --- implements the N_TTY line discipline. - * - * This code used to be in tty_io.c, but things are getting hairy - * enough that it made sense to split things off. (The N_TTY - * processing has changed so much that it's hardly recognizable, - * anyway...) - * - * Note that the open routine for N_TTY is guaranteed never to return - * an error. This is because Linux will fall back to setting a line - * to N_TTY if it can not switch to any other line discipline. - * - * Written by Theodore Ts'o, Copyright 1994. - * - * This file also contains code originally written by Linus Torvalds, - * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994. - * - * This file may be redistributed under the terms of the GNU General Public - * License. - * - * Reduced memory usage for older ARM systems - Russell King. - * - * 2000/01/20 Fixed SMP locking on put_tty_queue using bits of - * the patch by Andrew J. Kroll - * who actually finally proved there really was a race. - * - * 2002/03/18 Implemented n_tty_wakeup to send SIGIO POLL_OUTs to - * waiting writing processes-Sapan Bhatia . - * Also fixed a bug in BLOCKING mode where n_tty_write returns - * EAGAIN - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* number of characters left in xmit buffer before select has we have room */ -#define WAKEUP_CHARS 256 - -/* - * This defines the low- and high-watermarks for throttling and - * unthrottling the TTY driver. These watermarks are used for - * controlling the space in the read buffer. - */ -#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ -#define TTY_THRESHOLD_UNTHROTTLE 128 - -/* - * Special byte codes used in the echo buffer to represent operations - * or special handling of characters. Bytes in the echo buffer that - * are not part of such special blocks are treated as normal character - * codes. - */ -#define ECHO_OP_START 0xff -#define ECHO_OP_MOVE_BACK_COL 0x80 -#define ECHO_OP_SET_CANON_COL 0x81 -#define ECHO_OP_ERASE_TAB 0x82 - -static inline int tty_put_user(struct tty_struct *tty, unsigned char x, - unsigned char __user *ptr) -{ - tty_audit_add_data(tty, &x, 1); - return put_user(x, ptr); -} - -/** - * n_tty_set__room - receive space - * @tty: terminal - * - * Called by the driver to find out how much data it is - * permitted to feed to the line discipline without any being lost - * and thus to manage flow control. Not serialized. Answers for the - * "instant". - */ - -static void n_tty_set_room(struct tty_struct *tty) -{ - /* tty->read_cnt is not read locked ? */ - int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; - - /* - * If we are doing input canonicalization, and there are no - * pending newlines, let characters through without limit, so - * that erase characters will be handled. Other excess - * characters will be beeped. - */ - if (left <= 0) - left = tty->icanon && !tty->canon_data; - tty->receive_room = left; -} - -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++; - } -} - -/** - * put_tty_queue - add character to tty - * @c: character - * @tty: tty device - * - * Add a character to the tty read_buf queue. This is done under the - * read_lock to serialize character addition and also to protect us - * against parallel reads or flushes - */ - -static void put_tty_queue(unsigned char c, struct tty_struct *tty) -{ - 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); - put_tty_queue_nolock(c, tty); - spin_unlock_irqrestore(&tty->read_lock, flags); -} - -/** - * check_unthrottle - allow new receive data - * @tty; tty device - * - * Check whether to call the driver unthrottle functions - * - * Can sleep, may be called under the atomic_read_lock mutex but - * this is not guaranteed. - */ -static void check_unthrottle(struct tty_struct *tty) -{ - if (tty->count) - tty_unthrottle(tty); -} - -/** - * reset_buffer_flags - reset buffer state - * @tty: terminal to reset - * - * Reset the read buffer counters, clear the flags, - * and make sure the driver is unthrottled. Called - * from n_tty_open() and n_tty_flush_buffer(). - * - * Locking: tty_read_lock for read fields. - */ - -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; - spin_unlock_irqrestore(&tty->read_lock, flags); - - mutex_lock(&tty->echo_lock); - tty->echo_pos = tty->echo_cnt = tty->echo_overrun = 0; - mutex_unlock(&tty->echo_lock); - - tty->canon_head = tty->canon_data = tty->erasing = 0; - memset(&tty->read_flags, 0, sizeof tty->read_flags); - n_tty_set_room(tty); - check_unthrottle(tty); -} - -/** - * n_tty_flush_buffer - clean input queue - * @tty: terminal device - * - * Flush the input buffer. Called when the line discipline is - * being closed, when the tty layer wants the buffer flushed (eg - * at hangup) or when the N_TTY line discipline internally has to - * clean the pending queue (for example some signals). - * - * Locking: ctrl_lock, read_lock. - */ - -static void n_tty_flush_buffer(struct tty_struct *tty) -{ - unsigned long flags; - /* clear everything and unthrottle the driver */ - reset_buffer_flags(tty); - - if (!tty->link) - return; - - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->link->packet) { - tty->ctrl_status |= TIOCPKT_FLUSHREAD; - wake_up_interruptible(&tty->link->read_wait); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); -} - -/** - * n_tty_chars_in_buffer - report available bytes - * @tty: tty device - * - * Report the number of characters buffered to be delivered to user - * at this instant in time. - * - * Locking: read_lock - */ - -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); - if (!tty->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); - } - spin_unlock_irqrestore(&tty->read_lock, flags); - return n; -} - -/** - * is_utf8_continuation - utf8 multibyte check - * @c: byte to check - * - * Returns true if the utf8 character 'c' is a multibyte continuation - * character. We use this to correctly compute the on screen size - * of the character when printing - */ - -static inline int is_utf8_continuation(unsigned char c) -{ - return (c & 0xc0) == 0x80; -} - -/** - * is_continuation - multibyte check - * @c: byte to check - * - * Returns true if the utf8 character 'c' is a multibyte continuation - * character and the terminal is in unicode mode. - */ - -static inline int is_continuation(unsigned char c, struct tty_struct *tty) -{ - return I_IUTF8(tty) && is_utf8_continuation(c); -} - -/** - * do_output_char - output one character - * @c: character (or partial unicode symbol) - * @tty: terminal device - * @space: space available in tty driver write buffer - * - * This is a helper function that handles one output character - * (including special characters like TAB, CR, LF, etc.), - * doing OPOST processing and putting the results in the - * tty driver's write buffer. - * - * Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY - * and NLDLY. They simply aren't relevant in the world today. - * If you ever need them, add them here. - * - * Returns the number of bytes of buffer space used or -1 if - * no space left. - * - * Locking: should be called under the output_lock to protect - * the column state and space left in the buffer - */ - -static int do_output_char(unsigned char c, struct tty_struct *tty, int space) -{ - int spaces; - - if (!space) - return -1; - - switch (c) { - case '\n': - if (O_ONLRET(tty)) - tty->column = 0; - if (O_ONLCR(tty)) { - if (space < 2) - return -1; - tty->canon_column = tty->column = 0; - tty->ops->write(tty, "\r\n", 2); - return 2; - } - tty->canon_column = tty->column; - break; - case '\r': - if (O_ONOCR(tty) && tty->column == 0) - return 0; - if (O_OCRNL(tty)) { - c = '\n'; - if (O_ONLRET(tty)) - tty->canon_column = tty->column = 0; - break; - } - tty->canon_column = tty->column = 0; - break; - case '\t': - spaces = 8 - (tty->column & 7); - if (O_TABDLY(tty) == XTABS) { - if (space < spaces) - return -1; - tty->column += spaces; - tty->ops->write(tty, " ", spaces); - return spaces; - } - tty->column += spaces; - break; - case '\b': - if (tty->column > 0) - tty->column--; - break; - default: - if (!iscntrl(c)) { - if (O_OLCUC(tty)) - c = toupper(c); - if (!is_continuation(c, tty)) - tty->column++; - } - break; - } - - tty_put_char(tty, c); - return 1; -} - -/** - * process_output - output post processor - * @c: character (or partial unicode symbol) - * @tty: terminal device - * - * Output one character with OPOST processing. - * Returns -1 when the output device is full and the character - * must be retried. - * - * Locking: output_lock to protect column state and space left - * (also, this is called from n_tty_write under the - * tty layer write lock) - */ - -static int process_output(unsigned char c, struct tty_struct *tty) -{ - int space, retval; - - mutex_lock(&tty->output_lock); - - space = tty_write_room(tty); - retval = do_output_char(c, tty, space); - - mutex_unlock(&tty->output_lock); - if (retval < 0) - return -1; - else - return 0; -} - -/** - * process_output_block - block post processor - * @tty: terminal device - * @buf: character buffer - * @nr: number of bytes to output - * - * Output a block of characters with OPOST processing. - * Returns the number of characters output. - * - * This path is used to speed up block console writes, among other - * things when processing blocks of output data. It handles only - * the simple cases normally found and helps to generate blocks of - * symbols for the console driver and thus improve performance. - * - * Locking: output_lock to protect column state and space left - * (also, this is called from n_tty_write under the - * tty layer write lock) - */ - -static ssize_t process_output_block(struct tty_struct *tty, - const unsigned char *buf, unsigned int nr) -{ - int space; - int i; - const unsigned char *cp; - - mutex_lock(&tty->output_lock); - - space = tty_write_room(tty); - if (!space) { - mutex_unlock(&tty->output_lock); - return 0; - } - if (nr > space) - nr = space; - - for (i = 0, cp = buf; i < nr; i++, cp++) { - unsigned char c = *cp; - - switch (c) { - case '\n': - if (O_ONLRET(tty)) - tty->column = 0; - if (O_ONLCR(tty)) - goto break_out; - tty->canon_column = tty->column; - break; - case '\r': - if (O_ONOCR(tty) && tty->column == 0) - goto break_out; - if (O_OCRNL(tty)) - goto break_out; - tty->canon_column = tty->column = 0; - break; - case '\t': - goto break_out; - case '\b': - if (tty->column > 0) - tty->column--; - break; - default: - if (!iscntrl(c)) { - if (O_OLCUC(tty)) - goto break_out; - if (!is_continuation(c, tty)) - tty->column++; - } - break; - } - } -break_out: - i = tty->ops->write(tty, buf, i); - - mutex_unlock(&tty->output_lock); - return i; -} - -/** - * process_echoes - write pending echo characters - * @tty: terminal device - * - * Write previously buffered echo (and other ldisc-generated) - * characters to the tty. - * - * Characters generated by the ldisc (including echoes) need to - * be buffered because the driver's write buffer can fill during - * heavy program output. Echoing straight to the driver will - * often fail under these conditions, causing lost characters and - * resulting mismatches of ldisc state information. - * - * Since the ldisc state must represent the characters actually sent - * to the driver at the time of the write, operations like certain - * changes in column state are also saved in the buffer and executed - * here. - * - * A circular fifo buffer is used so that the most recent characters - * are prioritized. Also, when control characters are echoed with a - * prefixed "^", the pair is treated atomically and thus not separated. - * - * Locking: output_lock to protect column state and space left, - * echo_lock to protect the echo buffer - */ - -static void process_echoes(struct tty_struct *tty) -{ - int space, nr; - unsigned char c; - unsigned char *cp, *buf_end; - - if (!tty->echo_cnt) - return; - - mutex_lock(&tty->output_lock); - mutex_lock(&tty->echo_lock); - - 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; - while (nr > 0) { - c = *cp; - if (c == ECHO_OP_START) { - unsigned char op; - unsigned char *opp; - int no_space_left = 0; - - /* - * If the buffer byte is the start of a multi-byte - * operation, get the next byte, which is either the - * op code or a control character value. - */ - opp = cp + 1; - if (opp == buf_end) - opp -= N_TTY_BUF_SIZE; - op = *opp; - - switch (op) { - unsigned int num_chars, num_bs; - - case ECHO_OP_ERASE_TAB: - if (++opp == buf_end) - opp -= N_TTY_BUF_SIZE; - num_chars = *opp; - - /* - * Determine how many columns to go back - * in order to erase the tab. - * This depends on the number of columns - * used by other characters within the tab - * area. If this (modulo 8) count is from - * the start of input rather than from a - * previous tab, we offset by canon column. - * Otherwise, tab spacing is normal. - */ - if (!(num_chars & 0x80)) - num_chars += tty->canon_column; - num_bs = 8 - (num_chars & 7); - - if (num_bs > space) { - no_space_left = 1; - break; - } - space -= num_bs; - while (num_bs--) { - tty_put_char(tty, '\b'); - if (tty->column > 0) - tty->column--; - } - cp += 3; - nr -= 3; - break; - - case ECHO_OP_SET_CANON_COL: - tty->canon_column = tty->column; - cp += 2; - nr -= 2; - break; - - case ECHO_OP_MOVE_BACK_COL: - if (tty->column > 0) - tty->column--; - cp += 2; - nr -= 2; - break; - - case ECHO_OP_START: - /* This is an escaped echo op start code */ - if (!space) { - no_space_left = 1; - break; - } - tty_put_char(tty, ECHO_OP_START); - tty->column++; - space--; - cp += 2; - nr -= 2; - break; - - default: - /* - * If the op is not a special byte code, - * it is a ctrl char tagged to be echoed - * as "^X" (where X is the letter - * representing the control char). - * Note that we must ensure there is - * enough space for the whole ctrl pair. - * - */ - if (space < 2) { - no_space_left = 1; - break; - } - tty_put_char(tty, '^'); - tty_put_char(tty, op ^ 0100); - tty->column += 2; - space -= 2; - cp += 2; - nr -= 2; - } - - if (no_space_left) - break; - } else { - if (O_OPOST(tty) && - !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { - int retval = do_output_char(c, tty, space); - if (retval < 0) - break; - space -= retval; - } else { - if (!space) - break; - tty_put_char(tty, c); - space -= 1; - } - cp += 1; - nr -= 1; - } - - /* When end of circular buffer reached, wrap around */ - if (cp >= buf_end) - cp -= N_TTY_BUF_SIZE; - } - - if (nr == 0) { - tty->echo_pos = 0; - tty->echo_cnt = 0; - tty->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; - } - - mutex_unlock(&tty->echo_lock); - mutex_unlock(&tty->output_lock); - - if (tty->ops->flush_chars) - tty->ops->flush_chars(tty); -} - -/** - * add_echo_byte - add a byte to the echo buffer - * @c: unicode byte to echo - * @tty: terminal device - * - * Add a character or operation byte to the echo buffer. - * - * Should be called under the echo lock to protect the echo buffer. - */ - -static void add_echo_byte(unsigned char c, struct tty_struct *tty) -{ - int new_byte_pos; - - if (tty->echo_cnt == N_TTY_BUF_SIZE) { - /* Circular buffer is already at capacity */ - new_byte_pos = tty->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) & - (N_TTY_BUF_SIZE - 1)] == - ECHO_OP_ERASE_TAB) { - tty->echo_pos += 3; - tty->echo_cnt -= 2; - } else { - tty->echo_pos += 2; - tty->echo_cnt -= 1; - } - } else { - tty->echo_pos++; - } - tty->echo_pos &= N_TTY_BUF_SIZE - 1; - - tty->echo_overrun = 1; - } else { - new_byte_pos = tty->echo_pos + tty->echo_cnt; - new_byte_pos &= N_TTY_BUF_SIZE - 1; - tty->echo_cnt++; - } - - tty->echo_buf[new_byte_pos] = c; -} - -/** - * echo_move_back_col - add operation to move back a column - * @tty: terminal device - * - * Add an operation to the echo buffer to move back one column. - * - * Locking: echo_lock to protect the echo buffer - */ - -static void echo_move_back_col(struct tty_struct *tty) -{ - mutex_lock(&tty->echo_lock); - - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_MOVE_BACK_COL, tty); - - mutex_unlock(&tty->echo_lock); -} - -/** - * echo_set_canon_col - add operation to set the canon column - * @tty: terminal device - * - * Add an operation to the echo buffer to set the canon column - * to the current column. - * - * Locking: echo_lock to protect the echo buffer - */ - -static void echo_set_canon_col(struct tty_struct *tty) -{ - mutex_lock(&tty->echo_lock); - - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_SET_CANON_COL, tty); - - mutex_unlock(&tty->echo_lock); -} - -/** - * echo_erase_tab - add operation to erase a tab - * @num_chars: number of character columns already used - * @after_tab: true if num_chars starts after a previous tab - * @tty: terminal device - * - * Add an operation to the echo buffer to erase a tab. - * - * Called by the eraser function, which knows how many character - * columns have been used since either a previous tab or the start - * of input. This information will be used later, along with - * canon column (if applicable), to go back the correct number - * of columns. - * - * Locking: echo_lock to protect the echo buffer - */ - -static void echo_erase_tab(unsigned int num_chars, int after_tab, - struct tty_struct *tty) -{ - mutex_lock(&tty->echo_lock); - - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_ERASE_TAB, tty); - - /* We only need to know this modulo 8 (tab spacing) */ - num_chars &= 7; - - /* Set the high bit as a flag if num_chars is after a previous tab */ - if (after_tab) - num_chars |= 0x80; - - add_echo_byte(num_chars, tty); - - mutex_unlock(&tty->echo_lock); -} - -/** - * echo_char_raw - echo a character raw - * @c: unicode byte to echo - * @tty: terminal device - * - * Echo user input back onto the screen. This must be called only when - * L_ECHO(tty) is true. Called from the driver receive_buf path. - * - * This variant does not treat control characters specially. - * - * Locking: echo_lock to protect the echo buffer - */ - -static void echo_char_raw(unsigned char c, struct tty_struct *tty) -{ - mutex_lock(&tty->echo_lock); - - if (c == ECHO_OP_START) { - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_START, tty); - } else { - add_echo_byte(c, tty); - } - - mutex_unlock(&tty->echo_lock); -} - -/** - * echo_char - echo a character - * @c: unicode byte to echo - * @tty: terminal device - * - * Echo user input back onto the screen. This must be called only when - * L_ECHO(tty) is true. Called from the driver receive_buf path. - * - * This variant tags control characters to be echoed as "^X" - * (where X is the letter representing the control char). - * - * Locking: echo_lock to protect the echo buffer - */ - -static void echo_char(unsigned char c, struct tty_struct *tty) -{ - mutex_lock(&tty->echo_lock); - - if (c == ECHO_OP_START) { - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(ECHO_OP_START, tty); - } else { - if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') - add_echo_byte(ECHO_OP_START, tty); - add_echo_byte(c, tty); - } - - mutex_unlock(&tty->echo_lock); -} - -/** - * finish_erasing - complete erase - * @tty: tty doing the erase - */ - -static inline void finish_erasing(struct tty_struct *tty) -{ - if (tty->erasing) { - echo_char_raw('/', tty); - tty->erasing = 0; - } -} - -/** - * eraser - handle erase function - * @c: character input - * @tty: terminal device - * - * Perform erase and necessary output when an erase character is - * present in the stream from the driver layer. Handles the complexities - * of UTF-8 multibyte symbols. - * - * Locking: read_lock for tty buffers - */ - -static void eraser(unsigned char c, struct tty_struct *tty) -{ - enum { ERASE, WERASE, KILL } kill_type; - int head, seen_alnums, cnt; - unsigned long flags; - - /* FIXME: locking needed ? */ - if (tty->read_head == tty->canon_head) { - /* process_output('\a', tty); */ /* what do you think? */ - return; - } - if (c == ERASE_CHAR(tty)) - kill_type = ERASE; - else if (c == WERASE_CHAR(tty)) - kill_type = WERASE; - else { - if (!L_ECHO(tty)) { - spin_lock_irqsave(&tty->read_lock, flags); - tty->read_cnt -= ((tty->read_head - tty->canon_head) & - (N_TTY_BUF_SIZE - 1)); - tty->read_head = tty->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) & - (N_TTY_BUF_SIZE - 1)); - tty->read_head = tty->canon_head; - spin_unlock_irqrestore(&tty->read_lock, flags); - finish_erasing(tty); - echo_char(KILL_CHAR(tty), tty); - /* Add a newline if ECHOK is on and ECHOKE is off. */ - if (L_ECHOK(tty)) - echo_char_raw('\n', tty); - return; - } - kill_type = KILL; - } - - seen_alnums = 0; - /* FIXME: Locking ?? */ - while (tty->read_head != tty->canon_head) { - head = tty->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); - - /* do not partially erase */ - if (is_continuation(c, tty)) - break; - - if (kill_type == WERASE) { - /* Equivalent to BSD's ALTWERASE. */ - if (isalnum(c) || c == '_') - seen_alnums++; - else if (seen_alnums) - break; - } - cnt = (tty->read_head - head) & (N_TTY_BUF_SIZE-1); - spin_lock_irqsave(&tty->read_lock, flags); - tty->read_head = head; - tty->read_cnt -= cnt; - spin_unlock_irqrestore(&tty->read_lock, flags); - if (L_ECHO(tty)) { - if (L_ECHOPRT(tty)) { - if (!tty->erasing) { - echo_char_raw('\\', tty); - tty->erasing = 1; - } - /* if cnt > 1, output a multi-byte character */ - echo_char(c, tty); - while (--cnt > 0) { - head = (head+1) & (N_TTY_BUF_SIZE-1); - echo_char_raw(tty->read_buf[head], tty); - echo_move_back_col(tty); - } - } else if (kill_type == ERASE && !L_ECHOE(tty)) { - echo_char(ERASE_CHAR(tty), tty); - } else if (c == '\t') { - unsigned int num_chars = 0; - int after_tab = 0; - unsigned long tail = tty->read_head; - - /* - * Count the columns used for characters - * since the start of input or after a - * previous tab. - * This info is used to go back the correct - * number of columns. - */ - while (tail != tty->canon_head) { - tail = (tail-1) & (N_TTY_BUF_SIZE-1); - c = tty->read_buf[tail]; - if (c == '\t') { - after_tab = 1; - break; - } else if (iscntrl(c)) { - if (L_ECHOCTL(tty)) - num_chars += 2; - } else if (!is_continuation(c, tty)) { - num_chars++; - } - } - echo_erase_tab(num_chars, after_tab, tty); - } else { - if (iscntrl(c) && L_ECHOCTL(tty)) { - echo_char_raw('\b', tty); - echo_char_raw(' ', tty); - echo_char_raw('\b', tty); - } - if (!iscntrl(c) || L_ECHOCTL(tty)) { - echo_char_raw('\b', tty); - echo_char_raw(' ', tty); - echo_char_raw('\b', tty); - } - } - } - if (kill_type == ERASE) - break; - } - if (tty->read_head == tty->canon_head && L_ECHO(tty)) - finish_erasing(tty); -} - -/** - * isig - handle the ISIG optio - * @sig: signal - * @tty: terminal - * @flush: force flush - * - * Called when a signal is being sent due to terminal input. This - * may caus terminal flushing to take place according to the termios - * settings and character used. Called from the driver receive_buf - * path so serialized. - * - * Locking: ctrl_lock, read_lock (both via flush buffer) - */ - -static inline void isig(int sig, struct tty_struct *tty, int flush) -{ - if (tty->pgrp) - kill_pgrp(tty->pgrp, sig, 1); - if (flush || !L_NOFLSH(tty)) { - n_tty_flush_buffer(tty); - tty_driver_flush_buffer(tty); - } -} - -/** - * n_tty_receive_break - handle break - * @tty: terminal - * - * An RS232 break event has been hit in the incoming bitstream. This - * can cause a variety of events depending upon the termios settings. - * - * Called from the receive_buf path so single threaded. - */ - -static inline void n_tty_receive_break(struct tty_struct *tty) -{ - if (I_IGNBRK(tty)) - return; - if (I_BRKINT(tty)) { - isig(SIGINT, tty, 1); - return; - } - if (I_PARMRK(tty)) { - put_tty_queue('\377', tty); - put_tty_queue('\0', tty); - } - put_tty_queue('\0', tty); - wake_up_interruptible(&tty->read_wait); -} - -/** - * n_tty_receive_overrun - handle overrun reporting - * @tty: terminal - * - * Data arrived faster than we could process it. While the tty - * driver has flagged this the bits that were missed are gone - * forever. - * - * Called from the receive_buf path so single threaded. Does not - * need locking as num_overrun and overrun_time are function - * private. - */ - -static inline void n_tty_receive_overrun(struct tty_struct *tty) -{ - char buf[64]; - - tty->num_overrun++; - if (time_before(tty->overrun_time, jiffies - HZ) || - time_after(tty->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; - } -} - -/** - * n_tty_receive_parity_error - error notifier - * @tty: terminal device - * @c: character - * - * Process a parity error and queue the right data to indicate - * the error case if necessary. Locking as per n_tty_receive_buf. - */ -static inline void n_tty_receive_parity_error(struct tty_struct *tty, - unsigned char c) -{ - if (I_IGNPAR(tty)) - return; - if (I_PARMRK(tty)) { - put_tty_queue('\377', tty); - put_tty_queue('\0', tty); - put_tty_queue(c, tty); - } else if (I_INPCK(tty)) - put_tty_queue('\0', tty); - else - put_tty_queue(c, tty); - wake_up_interruptible(&tty->read_wait); -} - -/** - * n_tty_receive_char - perform processing - * @tty: terminal device - * @c: character - * - * Process an individual character of input received from the driver. - * This is serialized with respect to itself by the rules for the - * driver above. - */ - -static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) -{ - unsigned long flags; - int parmrk; - - if (tty->raw) { - put_tty_queue(c, tty); - return; - } - - if (I_ISTRIP(tty)) - c &= 0x7f; - if (I_IUCLC(tty) && L_IEXTEN(tty)) - c = tolower(c); - - if (L_EXTPROC(tty)) { - put_tty_queue(c, tty); - return; - } - - if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && - I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) && - c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) { - start_tty(tty); - process_echoes(tty); - } - - if (tty->closing) { - if (I_IXON(tty)) { - if (c == START_CHAR(tty)) { - start_tty(tty); - process_echoes(tty); - } else if (c == STOP_CHAR(tty)) - stop_tty(tty); - } - return; - } - - /* - * If the previous character was LNEXT, or we know that this - * character is not one of the characters that we'll have to - * handle specially, do shortcut processing to speed things - * up. - */ - if (!test_bit(c, tty->process_char_map) || tty->lnext) { - tty->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 */ - if (L_ECHO(tty)) - process_output('\a', tty); - return; - } - if (L_ECHO(tty)) { - finish_erasing(tty); - /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) - echo_set_canon_col(tty); - echo_char(c, tty); - process_echoes(tty); - } - if (parmrk) - put_tty_queue(c, tty); - put_tty_queue(c, tty); - return; - } - - if (I_IXON(tty)) { - if (c == START_CHAR(tty)) { - start_tty(tty); - process_echoes(tty); - return; - } - if (c == STOP_CHAR(tty)) { - stop_tty(tty); - return; - } - } - - if (L_ISIG(tty)) { - int signal; - signal = SIGINT; - if (c == INTR_CHAR(tty)) - goto send_signal; - signal = SIGQUIT; - if (c == QUIT_CHAR(tty)) - goto send_signal; - signal = SIGTSTP; - if (c == SUSP_CHAR(tty)) { -send_signal: - /* - * Note that we do not use isig() here because we want - * the order to be: - * 1) flush, 2) echo, 3) signal - */ - if (!L_NOFLSH(tty)) { - n_tty_flush_buffer(tty); - tty_driver_flush_buffer(tty); - } - if (I_IXON(tty)) - start_tty(tty); - if (L_ECHO(tty)) { - echo_char(c, tty); - process_echoes(tty); - } - if (tty->pgrp) - kill_pgrp(tty->pgrp, signal, 1); - return; - } - } - - if (c == '\r') { - if (I_IGNCR(tty)) - return; - if (I_ICRNL(tty)) - c = '\n'; - } else if (c == '\n' && I_INLCR(tty)) - c = '\r'; - - if (tty->icanon) { - if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || - (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { - eraser(c, tty); - process_echoes(tty); - return; - } - if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { - tty->lnext = 1; - if (L_ECHO(tty)) { - finish_erasing(tty); - if (L_ECHOCTL(tty)) { - echo_char_raw('^', tty); - echo_char_raw('\b', tty); - process_echoes(tty); - } - } - return; - } - if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && - L_IEXTEN(tty)) { - unsigned long tail = tty->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); - tail = (tail+1) & (N_TTY_BUF_SIZE-1); - } - process_echoes(tty); - return; - } - if (c == '\n') { - if (tty->read_cnt >= N_TTY_BUF_SIZE) { - if (L_ECHO(tty)) - process_output('\a', tty); - return; - } - if (L_ECHO(tty) || L_ECHONL(tty)) { - echo_char_raw('\n', tty); - process_echoes(tty); - } - goto handle_newline; - } - if (c == EOF_CHAR(tty)) { - if (tty->read_cnt >= N_TTY_BUF_SIZE) - return; - if (tty->canon_head != tty->read_head) - set_bit(TTY_PUSH, &tty->flags); - c = __DISABLED_CHAR; - goto handle_newline; - } - if ((c == EOL_CHAR(tty)) || - (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 (L_ECHO(tty)) - process_output('\a', tty); - return; - } - /* - * XXX are EOL_CHAR and EOL2_CHAR echoed?!? - */ - if (L_ECHO(tty)) { - /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) - echo_set_canon_col(tty); - echo_char(c, tty); - process_echoes(tty); - } - /* - * XXX does PARMRK doubling happen for - * EOL_CHAR and EOL2_CHAR? - */ - if (parmrk) - put_tty_queue(c, tty); - -handle_newline: - spin_lock_irqsave(&tty->read_lock, flags); - set_bit(tty->read_head, tty->read_flags); - put_tty_queue_nolock(c, tty); - tty->canon_head = tty->read_head; - tty->canon_data++; - spin_unlock_irqrestore(&tty->read_lock, flags); - kill_fasync(&tty->fasync, SIGIO, POLL_IN); - if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible(&tty->read_wait); - return; - } - } - - parmrk = (c == (unsigned char) '\377' && I_PARMRK(tty)) ? 1 : 0; - if (tty->read_cnt >= (N_TTY_BUF_SIZE - parmrk - 1)) { - /* beep if no space */ - if (L_ECHO(tty)) - process_output('\a', tty); - return; - } - if (L_ECHO(tty)) { - finish_erasing(tty); - if (c == '\n') - echo_char_raw('\n', tty); - else { - /* Record the column of first canon char. */ - if (tty->canon_head == tty->read_head) - echo_set_canon_col(tty); - echo_char(c, tty); - } - process_echoes(tty); - } - - if (parmrk) - put_tty_queue(c, tty); - - put_tty_queue(c, tty); -} - - -/** - * n_tty_write_wakeup - asynchronous I/O notifier - * @tty: tty device - * - * Required for the ptys, serial driver etc. since processes - * that attach themselves to the master and rely on ASYNC - * IO must be woken up - */ - -static void n_tty_write_wakeup(struct tty_struct *tty) -{ - if (tty->fasync && test_and_clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) - kill_fasync(&tty->fasync, SIGIO, POLL_OUT); -} - -/** - * n_tty_receive_buf - data receive - * @tty: terminal device - * @cp: buffer - * @fp: flag buffer - * @count: characters - * - * Called by the terminal driver when a block of characters has - * been received. This function must be called from soft contexts - * not from interrupt context. The driver is responsible for making - * calls one at a time and in order (or using flush_to_ldisc) - */ - -static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) -{ - const unsigned char *p; - char *f, flags = TTY_NORMAL; - int i; - char buf[64]; - unsigned long cpuflags; - - if (!tty->read_buf) - return; - - if (tty->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(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; - cp += i; - count -= i; - - i = min(N_TTY_BUF_SIZE - tty->read_cnt, - N_TTY_BUF_SIZE - tty->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; - spin_unlock_irqrestore(&tty->read_lock, cpuflags); - } else { - for (i = count, p = cp, f = fp; i; i--, p++) { - if (f) - flags = *f++; - switch (flags) { - case TTY_NORMAL: - n_tty_receive_char(tty, *p); - break; - case TTY_BREAK: - n_tty_receive_break(tty); - break; - case TTY_PARITY: - case TTY_FRAME: - n_tty_receive_parity_error(tty, *p); - break; - case TTY_OVERRUN: - n_tty_receive_overrun(tty); - break; - default: - printk(KERN_ERR "%s: unknown flag %d\n", - tty_name(tty, buf), flags); - break; - } - } - if (tty->ops->flush_chars) - tty->ops->flush_chars(tty); - } - - n_tty_set_room(tty); - - if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || - L_EXTPROC(tty)) { - kill_fasync(&tty->fasync, SIGIO, POLL_IN); - if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible(&tty->read_wait); - } - - /* - * Check the remaining room for the input canonicalization - * mode. We don't want to throttle the driver if we're in - * canonical mode and don't have a newline yet! - */ - if (tty->receive_room < TTY_THRESHOLD_THROTTLE) - tty_throttle(tty); -} - -int is_ignored(int sig) -{ - return (sigismember(¤t->blocked, sig) || - current->sighand->action[sig-1].sa.sa_handler == SIG_IGN); -} - -/** - * n_tty_set_termios - termios data changed - * @tty: terminal - * @old: previous data - * - * Called by the tty layer when the user changes termios flags so - * that the line discipline can plan ahead. This function cannot sleep - * and is protected from re-entry by the tty layer. The user is - * guaranteed that this function will not be re-entered or in progress - * when the ldisc is closed. - * - * Locking: Caller holds tty->termios_mutex - */ - -static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) -{ - int canon_change = 1; - BUG_ON(!tty); - - if (old) - canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON; - if (canon_change) { - memset(&tty->read_flags, 0, sizeof tty->read_flags); - tty->canon_head = tty->read_tail; - tty->canon_data = 0; - tty->erasing = 0; - } - - if (canon_change && !L_ICANON(tty) && tty->read_cnt) - wake_up_interruptible(&tty->read_wait); - - tty->icanon = (L_ICANON(tty) != 0); - if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { - tty->raw = 1; - tty->real_raw = 1; - n_tty_set_room(tty); - return; - } - if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || - I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || - I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || - I_PARMRK(tty)) { - memset(tty->process_char_map, 0, 256/8); - - if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', tty->process_char_map); - if (I_INLCR(tty)) - set_bit('\n', tty->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); - if (L_IEXTEN(tty)) { - set_bit(WERASE_CHAR(tty), - tty->process_char_map); - set_bit(LNEXT_CHAR(tty), - tty->process_char_map); - set_bit(EOL2_CHAR(tty), - tty->process_char_map); - if (L_ECHO(tty)) - set_bit(REPRINT_CHAR(tty), - tty->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); - } - 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); - } - clear_bit(__DISABLED_CHAR, tty->process_char_map); - tty->raw = 0; - tty->real_raw = 0; - } else { - tty->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; - else - tty->real_raw = 0; - } - n_tty_set_room(tty); - /* The termios change make the tty ready for I/O */ - wake_up_interruptible(&tty->write_wait); - wake_up_interruptible(&tty->read_wait); -} - -/** - * n_tty_close - close the ldisc for this tty - * @tty: device - * - * Called from the terminal layer when this line discipline is - * being shut down, either because of a close or becsuse of a - * discipline change. The function will not be called while other - * ldisc methods are in progress. - */ - -static void n_tty_close(struct tty_struct *tty) -{ - n_tty_flush_buffer(tty); - if (tty->read_buf) { - kfree(tty->read_buf); - tty->read_buf = NULL; - } - if (tty->echo_buf) { - kfree(tty->echo_buf); - tty->echo_buf = NULL; - } -} - -/** - * n_tty_open - open an ldisc - * @tty: terminal to open - * - * Called when this line discipline is being attached to the - * terminal device. Can sleep. Called serialized so that no - * other events will occur in parallel. No further open will occur - * until a close. - */ - -static int n_tty_open(struct tty_struct *tty) -{ - if (!tty) - return -EINVAL; - - /* These are ugly. Currently a malloc failure here can panic */ - if (!tty->read_buf) { - tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - if (!tty->read_buf) - return -ENOMEM; - } - if (!tty->echo_buf) { - tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - - if (!tty->echo_buf) - return -ENOMEM; - } - reset_buffer_flags(tty); - tty->column = 0; - n_tty_set_termios(tty, NULL); - tty->minimum_to_wake = 1; - tty->closing = 0; - return 0; -} - -static inline int input_available_p(struct tty_struct *tty, int amt) -{ - tty_flush_to_ldisc(tty); - if (tty->icanon && !L_EXTPROC(tty)) { - if (tty->canon_data) - return 1; - } else if (tty->read_cnt >= (amt ? amt : 1)) - return 1; - - return 0; -} - -/** - * copy_from_read_buf - copy read data directly - * @tty: terminal device - * @b: user data - * @nr: size of data - * - * Helper function to speed up n_tty_read. It is only called when - * ICANON is off; it copies characters straight from the tty queue to - * user space directly. It can be profitably called twice; once to - * drain the space from the tail pointer to the (physical) end of the - * buffer, and once to drain the space from the (physical) beginning of - * the buffer to head pointer. - * - * Called under the tty->atomic_read_lock sem - * - */ - -static int copy_from_read_buf(struct tty_struct *tty, - unsigned char __user **b, - size_t *nr) - -{ - int retval; - size_t n; - unsigned long flags; - - retval = 0; - spin_lock_irqsave(&tty->read_lock, flags); - n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->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); - n -= retval; - tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n); - 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 && n == 1) { - if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty)) - n--; - } - spin_unlock_irqrestore(&tty->read_lock, flags); - *b += n; - *nr -= n; - } - return retval; -} - -extern ssize_t redirected_tty_write(struct file *, const char __user *, - size_t, loff_t *); - -/** - * job_control - check job control - * @tty: tty - * @file: file handle - * - * Perform job control management checks on this file/tty descriptor - * and if appropriate send any needed signals and return a negative - * error code if action should be taken. - * - * FIXME: - * Locking: None - redirected write test is safe, testing - * current->signal should possibly lock current->sighand - * pgrp locking ? - */ - -static int job_control(struct tty_struct *tty, struct file *file) -{ - /* Job control check -- must be done at start and after - every sleep (POSIX.1 7.1.1.4). */ - /* NOTE: not yet done after every sleep pending a thorough - check of the logic of this change. -- jlc */ - /* don't stop on /dev/console */ - if (file->f_op->write != redirected_tty_write && - current->signal->tty == tty) { - if (!tty->pgrp) - printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); - else if (task_pgrp(current) != tty->pgrp) { - if (is_ignored(SIGTTIN) || - is_current_pgrp_orphaned()) - return -EIO; - kill_pgrp(task_pgrp(current), SIGTTIN, 1); - set_thread_flag(TIF_SIGPENDING); - return -ERESTARTSYS; - } - } - return 0; -} - - -/** - * n_tty_read - read function for tty - * @tty: tty device - * @file: file object - * @buf: userspace buffer pointer - * @nr: size of I/O - * - * Perform reads for the line discipline. We are guaranteed that the - * line discipline will not be closed under us but we may get multiple - * parallel readers and must handle this ourselves. We may also get - * a hangup. Always called in user context, may sleep. - * - * This code must be sure never to sleep through a hangup. - */ - -static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, - unsigned char __user *buf, size_t nr) -{ - unsigned char __user *b = buf; - DECLARE_WAITQUEUE(wait, current); - int c; - int minimum, time; - ssize_t retval = 0; - ssize_t size; - long timeout; - unsigned long flags; - int packet; - -do_it_again: - - BUG_ON(!tty->read_buf); - - c = job_control(tty, file); - if (c < 0) - return c; - - minimum = time = 0; - timeout = MAX_SCHEDULE_TIMEOUT; - if (!tty->icanon) { - time = (HZ / 10) * TIME_CHAR(tty); - minimum = MIN_CHAR(tty); - if (minimum) { - if (time) - tty->minimum_to_wake = 1; - else if (!waitqueue_active(&tty->read_wait) || - (tty->minimum_to_wake > minimum)) - tty->minimum_to_wake = minimum; - } else { - timeout = 0; - if (time) { - timeout = time; - time = 0; - } - tty->minimum_to_wake = minimum = 1; - } - } - - /* - * Internal serialization of reads. - */ - if (file->f_flags & O_NONBLOCK) { - if (!mutex_trylock(&tty->atomic_read_lock)) - return -EAGAIN; - } else { - if (mutex_lock_interruptible(&tty->atomic_read_lock)) - return -ERESTARTSYS; - } - packet = tty->packet; - - add_wait_queue(&tty->read_wait, &wait); - while (nr) { - /* First test for status change. */ - if (packet && tty->link->ctrl_status) { - unsigned char cs; - if (b != buf) - break; - spin_lock_irqsave(&tty->link->ctrl_lock, flags); - cs = tty->link->ctrl_status; - tty->link->ctrl_status = 0; - spin_unlock_irqrestore(&tty->link->ctrl_lock, flags); - if (tty_put_user(tty, cs, b++)) { - retval = -EFAULT; - b--; - break; - } - nr--; - break; - } - /* This statement must be first before checking for input - so that any interrupt will set the state back to - TASK_RUNNING. */ - set_current_state(TASK_INTERRUPTIBLE); - - if (((minimum - (b - buf)) < tty->minimum_to_wake) && - ((minimum - (b - buf)) >= 1)) - tty->minimum_to_wake = (minimum - (b - buf)); - - if (!input_available_p(tty, 0)) { - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { - retval = -EIO; - break; - } - if (tty_hung_up_p(file)) - break; - if (!timeout) - break; - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - /* FIXME: does n_tty_set_room need locking ? */ - n_tty_set_room(tty); - timeout = schedule_timeout(timeout); - continue; - } - __set_current_state(TASK_RUNNING); - - /* Deal with packet mode. */ - if (packet && b == buf) { - if (tty_put_user(tty, TIOCPKT_DATA, b++)) { - retval = -EFAULT; - b--; - break; - } - nr--; - } - - if (tty->icanon && !L_EXTPROC(tty)) { - /* N.B. avoid overrun if nr == 0 */ - while (nr && tty->read_cnt) { - int eol; - - eol = test_and_clear_bit(tty->read_tail, - tty->read_flags); - c = tty->read_buf[tty->read_tail]; - spin_lock_irqsave(&tty->read_lock, flags); - tty->read_tail = ((tty->read_tail+1) & - (N_TTY_BUF_SIZE-1)); - tty->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; - } - spin_unlock_irqrestore(&tty->read_lock, flags); - - if (!eol || (c != __DISABLED_CHAR)) { - if (tty_put_user(tty, c, b++)) { - retval = -EFAULT; - b--; - break; - } - nr--; - } - if (eol) { - tty_audit_push(tty); - break; - } - } - if (retval) - break; - } else { - int uncopied; - /* The copy function takes the read lock and handles - locking internally for this case */ - uncopied = copy_from_read_buf(tty, &b, &nr); - uncopied += copy_from_read_buf(tty, &b, &nr); - if (uncopied) { - retval = -EFAULT; - break; - } - } - - /* If there is enough space in the read buffer now, let the - * low-level driver know. We use n_tty_chars_in_buffer() to - * check the buffer, as it now knows about canonical mode. - * Otherwise, if the driver is throttled and the line is - * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, - * we won't get any more characters. - */ - if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { - n_tty_set_room(tty); - check_unthrottle(tty); - } - - if (b - buf >= minimum) - break; - if (time) - timeout = time; - } - mutex_unlock(&tty->atomic_read_lock); - remove_wait_queue(&tty->read_wait, &wait); - - if (!waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = minimum; - - __set_current_state(TASK_RUNNING); - size = b - buf; - if (size) { - retval = size; - if (nr) - clear_bit(TTY_PUSH, &tty->flags); - } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) - goto do_it_again; - - n_tty_set_room(tty); - return retval; -} - -/** - * n_tty_write - write function for tty - * @tty: tty device - * @file: file object - * @buf: userspace buffer pointer - * @nr: size of I/O - * - * Write function of the terminal device. This is serialized with - * respect to other write callers but not to termios changes, reads - * and other such events. Since the receive code will echo characters, - * thus calling driver write methods, the output_lock is used in - * the output processing functions called here as well as in the - * echo processing function to protect the column state and space - * left in the buffer. - * - * This code must be sure never to sleep through a hangup. - * - * Locking: output_lock to protect column state and space left - * (note that the process_output*() functions take this - * lock themselves) - */ - -static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, - const unsigned char *buf, size_t nr) -{ - const unsigned char *b = buf; - DECLARE_WAITQUEUE(wait, current); - int c; - ssize_t retval = 0; - - /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ - if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) { - retval = tty_check_change(tty); - if (retval) - return retval; - } - - /* Write out any echoed characters that are still pending */ - process_echoes(tty); - - add_wait_queue(&tty->write_wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) { - retval = -EIO; - break; - } - if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { - while (nr > 0) { - ssize_t num = process_output_block(tty, b, nr); - if (num < 0) { - if (num == -EAGAIN) - break; - retval = num; - goto break_out; - } - b += num; - nr -= num; - if (nr == 0) - break; - c = *b; - if (process_output(c, tty) < 0) - break; - b++; nr--; - } - if (tty->ops->flush_chars) - tty->ops->flush_chars(tty); - } else { - while (nr > 0) { - c = tty->ops->write(tty, b, nr); - if (c < 0) { - retval = c; - goto break_out; - } - if (!c) - break; - b += c; - nr -= c; - } - } - if (!nr) - break; - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - schedule(); - } -break_out: - __set_current_state(TASK_RUNNING); - remove_wait_queue(&tty->write_wait, &wait); - if (b - buf != nr && tty->fasync) - set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - return (b - buf) ? b - buf : retval; -} - -/** - * n_tty_poll - poll method for N_TTY - * @tty: terminal device - * @file: file accessing it - * @wait: poll table - * - * Called when the line discipline is asked to poll() for data or - * for special events. This code is not serialized with respect to - * other events save open/close. - * - * This code must be sure never to sleep through a hangup. - * Called without the kernel lock held - fine - */ - -static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, - poll_table *wait) -{ - unsigned int mask = 0; - - poll_wait(file, &tty->read_wait, wait); - poll_wait(file, &tty->write_wait, wait); - if (input_available_p(tty, TIME_CHAR(tty) ? 0 : MIN_CHAR(tty))) - mask |= POLLIN | POLLRDNORM; - if (tty->packet && tty->link->ctrl_status) - mask |= POLLPRI | POLLIN | POLLRDNORM; - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) - mask |= POLLHUP; - if (tty_hung_up_p(file)) - mask |= POLLHUP; - if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { - if (MIN_CHAR(tty) && !TIME_CHAR(tty)) - tty->minimum_to_wake = MIN_CHAR(tty); - else - tty->minimum_to_wake = 1; - } - if (tty->ops->write && !tty_is_writelocked(tty) && - tty_chars_in_buffer(tty) < WAKEUP_CHARS && - tty_write_room(tty) > 0) - mask |= POLLOUT | POLLWRNORM; - return mask; -} - -static unsigned long inq_canon(struct tty_struct *tty) -{ - int nr, head, tail; - - if (!tty->canon_data) - return 0; - head = tty->canon_head; - tail = tty->read_tail; - nr = (head - tail) & (N_TTY_BUF_SIZE-1); - /* Skip EOF-chars.. */ - while (head != tail) { - if (test_bit(tail, tty->read_flags) && - tty->read_buf[tail] == __DISABLED_CHAR) - nr--; - tail = (tail+1) & (N_TTY_BUF_SIZE-1); - } - return nr; -} - -static int n_tty_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int retval; - - switch (cmd) { - case TIOCOUTQ: - return put_user(tty_chars_in_buffer(tty), (int __user *) arg); - case TIOCINQ: - /* FIXME: Locking */ - retval = tty->read_cnt; - if (L_ICANON(tty)) - retval = inq_canon(tty); - return put_user(retval, (unsigned int __user *) arg); - default: - return n_tty_ioctl_helper(tty, file, cmd, arg); - } -} - -struct tty_ldisc_ops tty_ldisc_N_TTY = { - .magic = TTY_LDISC_MAGIC, - .name = "n_tty", - .open = n_tty_open, - .close = n_tty_close, - .flush_buffer = n_tty_flush_buffer, - .chars_in_buffer = n_tty_chars_in_buffer, - .read = n_tty_read, - .write = n_tty_write, - .ioctl = n_tty_ioctl, - .set_termios = n_tty_set_termios, - .poll = n_tty_poll, - .receive_buf = n_tty_receive_buf, - .write_wakeup = n_tty_write_wakeup -}; - -/** - * n_tty_inherit_ops - inherit N_TTY methods - * @ops: struct tty_ldisc_ops where to save N_TTY methods - * - * Used by a generic struct tty_ldisc_ops to easily inherit N_TTY - * methods. - */ - -void n_tty_inherit_ops(struct tty_ldisc_ops *ops) -{ - *ops = tty_ldisc_N_TTY; - ops->owner = NULL; - ops->refcount = ops->flags = 0; -} -EXPORT_SYMBOL_GPL(n_tty_inherit_ops); diff --git a/drivers/char/pty.c b/drivers/char/pty.c deleted file mode 100644 index 923a48585501..000000000000 --- a/drivers/char/pty.c +++ /dev/null @@ -1,777 +0,0 @@ -/* - * linux/drivers/char/pty.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef CONFIG_UNIX98_PTYS -static struct tty_driver *ptm_driver; -static struct tty_driver *pts_driver; -#endif - -static void pty_close(struct tty_struct *tty, struct file *filp) -{ - BUG_ON(!tty); - if (tty->driver->subtype == PTY_TYPE_MASTER) - WARN_ON(tty->count > 1); - else { - if (tty->count > 2) - return; - } - wake_up_interruptible(&tty->read_wait); - wake_up_interruptible(&tty->write_wait); - tty->packet = 0; - if (!tty->link) - return; - tty->link->packet = 0; - set_bit(TTY_OTHER_CLOSED, &tty->link->flags); - wake_up_interruptible(&tty->link->read_wait); - wake_up_interruptible(&tty->link->write_wait); - if (tty->driver->subtype == PTY_TYPE_MASTER) { - set_bit(TTY_OTHER_CLOSED, &tty->flags); -#ifdef CONFIG_UNIX98_PTYS - if (tty->driver == ptm_driver) - devpts_pty_kill(tty->link); -#endif - tty_unlock(); - tty_vhangup(tty->link); - tty_lock(); - } -} - -/* - * The unthrottle routine is called by the line discipline to signal - * that it can receive more characters. For PTY's, the TTY_THROTTLED - * flag is always set, to force the line discipline to always call the - * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE - * characters in the queue. This is necessary since each time this - * happens, we need to wake up any sleeping processes that could be - * (1) trying to send data to the pty, or (2) waiting in wait_until_sent() - * for the pty buffer to be drained. - */ -static void pty_unthrottle(struct tty_struct *tty) -{ - tty_wakeup(tty->link); - set_bit(TTY_THROTTLED, &tty->flags); -} - -/** - * pty_space - report space left for writing - * @to: tty we are writing into - * - * The tty buffers allow 64K but we sneak a peak and clip at 8K this - * allows a lot of overspill room for echo and other fun messes to - * be handled properly - */ - -static int pty_space(struct tty_struct *to) -{ - int n = 8192 - to->buf.memory_used; - if (n < 0) - return 0; - return n; -} - -/** - * pty_write - write to a pty - * @tty: the tty we write from - * @buf: kernel buffer of data - * @count: bytes to write - * - * Our "hardware" write method. Data is coming from the ldisc which - * may be in a non sleeping state. We simply throw this at the other - * end of the link as if we were an IRQ handler receiving stuff for - * the other side of the pty/tty pair. - */ - -static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) -{ - struct tty_struct *to = tty->link; - - if (tty->stopped) - return 0; - - if (c > 0) { - /* Stuff the data into the input queue of the other end */ - c = tty_insert_flip_string(to, buf, c); - /* And shovel */ - if (c) { - tty_flip_buffer_push(to); - tty_wakeup(tty); - } - } - return c; -} - -/** - * pty_write_room - write space - * @tty: tty we are writing from - * - * Report how many bytes the ldisc can send into the queue for - * the other device. - */ - -static int pty_write_room(struct tty_struct *tty) -{ - if (tty->stopped) - return 0; - return pty_space(tty->link); -} - -/** - * pty_chars_in_buffer - characters currently in our tx queue - * @tty: our tty - * - * Report how much we have in the transmit queue. As everything is - * instantly at the other end this is easy to implement. - */ - -static int pty_chars_in_buffer(struct tty_struct *tty) -{ - return 0; -} - -/* Set the lock flag on a pty */ -static int pty_set_lock(struct tty_struct *tty, int __user *arg) -{ - int val; - if (get_user(val, arg)) - return -EFAULT; - if (val) - set_bit(TTY_PTY_LOCK, &tty->flags); - else - clear_bit(TTY_PTY_LOCK, &tty->flags); - return 0; -} - -/* Send a signal to the slave */ -static int pty_signal(struct tty_struct *tty, int sig) -{ - unsigned long flags; - struct pid *pgrp; - - if (tty->link) { - spin_lock_irqsave(&tty->link->ctrl_lock, flags); - pgrp = get_pid(tty->link->pgrp); - spin_unlock_irqrestore(&tty->link->ctrl_lock, flags); - - kill_pgrp(pgrp, sig, 1); - put_pid(pgrp); - } - return 0; -} - -static void pty_flush_buffer(struct tty_struct *tty) -{ - struct tty_struct *to = tty->link; - unsigned long flags; - - if (!to) - return; - /* tty_buffer_flush(to); FIXME */ - if (to->packet) { - spin_lock_irqsave(&tty->ctrl_lock, flags); - tty->ctrl_status |= TIOCPKT_FLUSHWRITE; - wake_up_interruptible(&to->read_wait); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - } -} - -static int pty_open(struct tty_struct *tty, struct file *filp) -{ - int retval = -ENODEV; - - if (!tty || !tty->link) - goto out; - - retval = -EIO; - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) - goto out; - if (test_bit(TTY_PTY_LOCK, &tty->link->flags)) - goto out; - if (tty->link->count != 1) - goto out; - - clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); - set_bit(TTY_THROTTLED, &tty->flags); - retval = 0; -out: - return retval; -} - -static void pty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - tty->termios->c_cflag &= ~(CSIZE | PARENB); - tty->termios->c_cflag |= (CS8 | CREAD); -} - -/** - * pty_do_resize - resize event - * @tty: tty being resized - * @ws: window size being set. - * - * Update the termios variables and send the necessary signals to - * peform a terminal resize correctly - */ - -int pty_resize(struct tty_struct *tty, struct winsize *ws) -{ - struct pid *pgrp, *rpgrp; - unsigned long flags; - struct tty_struct *pty = tty->link; - - /* For a PTY we need to lock the tty side */ - mutex_lock(&tty->termios_mutex); - if (!memcmp(ws, &tty->winsize, sizeof(*ws))) - goto done; - - /* Get the PID values and reference them so we can - avoid holding the tty ctrl lock while sending signals. - We need to lock these individually however. */ - - spin_lock_irqsave(&tty->ctrl_lock, flags); - pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - - spin_lock_irqsave(&pty->ctrl_lock, flags); - rpgrp = get_pid(pty->pgrp); - spin_unlock_irqrestore(&pty->ctrl_lock, flags); - - if (pgrp) - kill_pgrp(pgrp, SIGWINCH, 1); - if (rpgrp != pgrp && rpgrp) - kill_pgrp(rpgrp, SIGWINCH, 1); - - put_pid(pgrp); - put_pid(rpgrp); - - tty->winsize = *ws; - pty->winsize = *ws; /* Never used so will go away soon */ -done: - mutex_unlock(&tty->termios_mutex); - return 0; -} - -/* Traditional BSD devices */ -#ifdef CONFIG_LEGACY_PTYS - -static int pty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct tty_struct *o_tty; - int idx = tty->index; - int retval; - - o_tty = alloc_tty_struct(); - if (!o_tty) - return -ENOMEM; - if (!try_module_get(driver->other->owner)) { - /* This cannot in fact currently happen */ - free_tty_struct(o_tty); - return -ENOMEM; - } - initialize_tty_struct(o_tty, driver->other, idx); - - /* We always use new tty termios data so we can do this - the easy way .. */ - retval = tty_init_termios(tty); - if (retval) - goto free_mem_out; - - retval = tty_init_termios(o_tty); - if (retval) { - tty_free_termios(tty); - goto free_mem_out; - } - - /* - * Everything allocated ... set up the o_tty structure. - */ - driver->other->ttys[idx] = o_tty; - tty_driver_kref_get(driver->other); - if (driver->subtype == PTY_TYPE_MASTER) - o_tty->count++; - /* Establish the links in both directions */ - tty->link = o_tty; - o_tty->link = tty; - - tty_driver_kref_get(driver); - tty->count++; - driver->ttys[idx] = tty; - return 0; -free_mem_out: - module_put(o_tty->driver->owner); - free_tty_struct(o_tty); - return -ENOMEM; -} - -static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ - return pty_set_lock(tty, (int __user *) arg); - case TIOCSIG: /* Send signal to other side of pty */ - return pty_signal(tty, (int) arg); - } - return -ENOIOCTLCMD; -} - -static int legacy_count = CONFIG_LEGACY_PTY_COUNT; -module_param(legacy_count, int, 0); - -/* - * The master side of a pty can do TIOCSPTLCK and thus - * has pty_bsd_ioctl. - */ -static const struct tty_operations master_pty_ops_bsd = { - .install = pty_install, - .open = pty_open, - .close = pty_close, - .write = pty_write, - .write_room = pty_write_room, - .flush_buffer = pty_flush_buffer, - .chars_in_buffer = pty_chars_in_buffer, - .unthrottle = pty_unthrottle, - .set_termios = pty_set_termios, - .ioctl = pty_bsd_ioctl, - .resize = pty_resize -}; - -static const struct tty_operations slave_pty_ops_bsd = { - .install = pty_install, - .open = pty_open, - .close = pty_close, - .write = pty_write, - .write_room = pty_write_room, - .flush_buffer = pty_flush_buffer, - .chars_in_buffer = pty_chars_in_buffer, - .unthrottle = pty_unthrottle, - .set_termios = pty_set_termios, - .resize = pty_resize -}; - -static void __init legacy_pty_init(void) -{ - struct tty_driver *pty_driver, *pty_slave_driver; - - if (legacy_count <= 0) - return; - - pty_driver = alloc_tty_driver(legacy_count); - if (!pty_driver) - panic("Couldn't allocate pty driver"); - - pty_slave_driver = alloc_tty_driver(legacy_count); - if (!pty_slave_driver) - panic("Couldn't allocate pty slave driver"); - - pty_driver->owner = THIS_MODULE; - pty_driver->driver_name = "pty_master"; - pty_driver->name = "pty"; - pty_driver->major = PTY_MASTER_MAJOR; - pty_driver->minor_start = 0; - pty_driver->type = TTY_DRIVER_TYPE_PTY; - pty_driver->subtype = PTY_TYPE_MASTER; - pty_driver->init_termios = tty_std_termios; - pty_driver->init_termios.c_iflag = 0; - pty_driver->init_termios.c_oflag = 0; - pty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; - pty_driver->init_termios.c_lflag = 0; - pty_driver->init_termios.c_ispeed = 38400; - pty_driver->init_termios.c_ospeed = 38400; - pty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW; - pty_driver->other = pty_slave_driver; - tty_set_operations(pty_driver, &master_pty_ops_bsd); - - pty_slave_driver->owner = THIS_MODULE; - pty_slave_driver->driver_name = "pty_slave"; - pty_slave_driver->name = "ttyp"; - pty_slave_driver->major = PTY_SLAVE_MAJOR; - pty_slave_driver->minor_start = 0; - pty_slave_driver->type = TTY_DRIVER_TYPE_PTY; - pty_slave_driver->subtype = PTY_TYPE_SLAVE; - pty_slave_driver->init_termios = tty_std_termios; - pty_slave_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; - pty_slave_driver->init_termios.c_ispeed = 38400; - pty_slave_driver->init_termios.c_ospeed = 38400; - pty_slave_driver->flags = TTY_DRIVER_RESET_TERMIOS | - TTY_DRIVER_REAL_RAW; - pty_slave_driver->other = pty_driver; - tty_set_operations(pty_slave_driver, &slave_pty_ops_bsd); - - if (tty_register_driver(pty_driver)) - panic("Couldn't register pty driver"); - if (tty_register_driver(pty_slave_driver)) - panic("Couldn't register pty slave driver"); -} -#else -static inline void legacy_pty_init(void) { } -#endif - -/* Unix98 devices */ -#ifdef CONFIG_UNIX98_PTYS -/* - * sysctl support for setting limits on the number of Unix98 ptys allocated. - * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly. - */ -int pty_limit = NR_UNIX98_PTY_DEFAULT; -static int pty_limit_min; -static int pty_limit_max = NR_UNIX98_PTY_MAX; -static int pty_count; - -static struct cdev ptmx_cdev; - -static struct ctl_table pty_table[] = { - { - .procname = "max", - .maxlen = sizeof(int), - .mode = 0644, - .data = &pty_limit, - .proc_handler = proc_dointvec_minmax, - .extra1 = &pty_limit_min, - .extra2 = &pty_limit_max, - }, { - .procname = "nr", - .maxlen = sizeof(int), - .mode = 0444, - .data = &pty_count, - .proc_handler = proc_dointvec, - }, - {} -}; - -static struct ctl_table pty_kern_table[] = { - { - .procname = "pty", - .mode = 0555, - .child = pty_table, - }, - {} -}; - -static struct ctl_table pty_root_table[] = { - { - .procname = "kernel", - .mode = 0555, - .child = pty_kern_table, - }, - {} -}; - - -static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - switch (cmd) { - case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ - return pty_set_lock(tty, (int __user *)arg); - case TIOCGPTN: /* Get PT Number */ - return put_user(tty->index, (unsigned int __user *)arg); - case TIOCSIG: /* Send signal to other side of pty */ - return pty_signal(tty, (int) arg); - } - - return -ENOIOCTLCMD; -} - -/** - * ptm_unix98_lookup - find a pty master - * @driver: ptm driver - * @idx: tty index - * - * Look up a pty master device. Called under the tty_mutex for now. - * This provides our locking. - */ - -static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, - struct inode *ptm_inode, int idx) -{ - struct tty_struct *tty = devpts_get_tty(ptm_inode, idx); - if (tty) - tty = tty->link; - return tty; -} - -/** - * pts_unix98_lookup - find a pty slave - * @driver: pts driver - * @idx: tty index - * - * Look up a pty master device. Called under the tty_mutex for now. - * This provides our locking. - */ - -static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, - struct inode *pts_inode, int idx) -{ - struct tty_struct *tty = devpts_get_tty(pts_inode, idx); - /* Master must be open before slave */ - if (!tty) - return ERR_PTR(-EIO); - return tty; -} - -static void pty_unix98_shutdown(struct tty_struct *tty) -{ - /* We have our own method as we don't use the tty index */ - kfree(tty->termios); -} - -/* We have no need to install and remove our tty objects as devpts does all - the work for us */ - -static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct tty_struct *o_tty; - int idx = tty->index; - - o_tty = alloc_tty_struct(); - if (!o_tty) - return -ENOMEM; - if (!try_module_get(driver->other->owner)) { - /* This cannot in fact currently happen */ - free_tty_struct(o_tty); - return -ENOMEM; - } - initialize_tty_struct(o_tty, driver->other, idx); - - tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); - if (tty->termios == NULL) - goto free_mem_out; - *tty->termios = driver->init_termios; - tty->termios_locked = tty->termios + 1; - - o_tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); - if (o_tty->termios == NULL) - goto free_mem_out; - *o_tty->termios = driver->other->init_termios; - o_tty->termios_locked = o_tty->termios + 1; - - tty_driver_kref_get(driver->other); - if (driver->subtype == PTY_TYPE_MASTER) - o_tty->count++; - /* Establish the links in both directions */ - tty->link = o_tty; - o_tty->link = tty; - /* - * All structures have been allocated, so now we install them. - * Failures after this point use release_tty to clean up, so - * there's no need to null out the local pointers. - */ - tty_driver_kref_get(driver); - tty->count++; - pty_count++; - return 0; -free_mem_out: - kfree(o_tty->termios); - module_put(o_tty->driver->owner); - free_tty_struct(o_tty); - kfree(tty->termios); - return -ENOMEM; -} - -static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) -{ - pty_count--; -} - -static const struct tty_operations ptm_unix98_ops = { - .lookup = ptm_unix98_lookup, - .install = pty_unix98_install, - .remove = pty_unix98_remove, - .open = pty_open, - .close = pty_close, - .write = pty_write, - .write_room = pty_write_room, - .flush_buffer = pty_flush_buffer, - .chars_in_buffer = pty_chars_in_buffer, - .unthrottle = pty_unthrottle, - .set_termios = pty_set_termios, - .ioctl = pty_unix98_ioctl, - .shutdown = pty_unix98_shutdown, - .resize = pty_resize -}; - -static const struct tty_operations pty_unix98_ops = { - .lookup = pts_unix98_lookup, - .install = pty_unix98_install, - .remove = pty_unix98_remove, - .open = pty_open, - .close = pty_close, - .write = pty_write, - .write_room = pty_write_room, - .flush_buffer = pty_flush_buffer, - .chars_in_buffer = pty_chars_in_buffer, - .unthrottle = pty_unthrottle, - .set_termios = pty_set_termios, - .shutdown = pty_unix98_shutdown -}; - -/** - * ptmx_open - open a unix 98 pty master - * @inode: inode of device file - * @filp: file pointer to tty - * - * Allocate a unix98 pty master device from the ptmx driver. - * - * Locking: tty_mutex protects the init_dev work. tty->count should - * protect the rest. - * allocated_ptys_lock handles the list of free pty numbers - */ - -static int ptmx_open(struct inode *inode, struct file *filp) -{ - struct tty_struct *tty; - int retval; - int index; - - nonseekable_open(inode, filp); - - /* find a device that is not in use. */ - tty_lock(); - index = devpts_new_index(inode); - tty_unlock(); - if (index < 0) - return index; - - mutex_lock(&tty_mutex); - tty_lock(); - tty = tty_init_dev(ptm_driver, index, 1); - mutex_unlock(&tty_mutex); - - if (IS_ERR(tty)) { - retval = PTR_ERR(tty); - goto out; - } - - set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ - - retval = tty_add_file(tty, filp); - if (retval) - goto out; - - retval = devpts_pty_new(inode, tty->link); - if (retval) - goto out1; - - retval = ptm_driver->ops->open(tty, filp); - if (retval) - goto out2; -out1: - tty_unlock(); - return retval; -out2: - tty_unlock(); - tty_release(inode, filp); - return retval; -out: - devpts_kill_index(inode, index); - tty_unlock(); - return retval; -} - -static struct file_operations ptmx_fops; - -static void __init unix98_pty_init(void) -{ - ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); - if (!ptm_driver) - panic("Couldn't allocate Unix98 ptm driver"); - pts_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX); - if (!pts_driver) - panic("Couldn't allocate Unix98 pts driver"); - - ptm_driver->owner = THIS_MODULE; - ptm_driver->driver_name = "pty_master"; - ptm_driver->name = "ptm"; - ptm_driver->major = UNIX98_PTY_MASTER_MAJOR; - ptm_driver->minor_start = 0; - ptm_driver->type = TTY_DRIVER_TYPE_PTY; - ptm_driver->subtype = PTY_TYPE_MASTER; - ptm_driver->init_termios = tty_std_termios; - ptm_driver->init_termios.c_iflag = 0; - ptm_driver->init_termios.c_oflag = 0; - ptm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; - ptm_driver->init_termios.c_lflag = 0; - ptm_driver->init_termios.c_ispeed = 38400; - ptm_driver->init_termios.c_ospeed = 38400; - ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; - ptm_driver->other = pts_driver; - tty_set_operations(ptm_driver, &ptm_unix98_ops); - - pts_driver->owner = THIS_MODULE; - pts_driver->driver_name = "pty_slave"; - pts_driver->name = "pts"; - pts_driver->major = UNIX98_PTY_SLAVE_MAJOR; - pts_driver->minor_start = 0; - pts_driver->type = TTY_DRIVER_TYPE_PTY; - pts_driver->subtype = PTY_TYPE_SLAVE; - pts_driver->init_termios = tty_std_termios; - pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; - pts_driver->init_termios.c_ispeed = 38400; - pts_driver->init_termios.c_ospeed = 38400; - pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; - pts_driver->other = ptm_driver; - tty_set_operations(pts_driver, &pty_unix98_ops); - - if (tty_register_driver(ptm_driver)) - panic("Couldn't register Unix98 ptm driver"); - if (tty_register_driver(pts_driver)) - panic("Couldn't register Unix98 pts driver"); - - register_sysctl_table(pty_root_table); - - /* Now create the /dev/ptmx special device */ - tty_default_fops(&ptmx_fops); - ptmx_fops.open = ptmx_open; - - cdev_init(&ptmx_cdev, &ptmx_fops); - if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) || - register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) - panic("Couldn't register /dev/ptmx driver\n"); - device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); -} - -#else -static inline void unix98_pty_init(void) { } -#endif - -static int __init pty_init(void) -{ - legacy_pty_init(); - unix98_pty_init(); - return 0; -} -module_init(pty_init); diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c deleted file mode 100644 index eaa5d3efa79d..000000000000 --- a/drivers/char/sysrq.c +++ /dev/null @@ -1,811 +0,0 @@ -/* - * Linux Magic System Request Key Hacks - * - * (c) 1997 Martin Mares - * based on ideas by Pavel Machek - * - * (c) 2000 Crutcher Dunnavant - * overhauled to use key registration - * based upon discusions in irc://irc.openprojects.net/#kernelnewbies - * - * Copyright (c) 2010 Dmitry Torokhov - * Input handler conversion - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for fsync_bdev() */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* Whether we react on sysrq keys or just ignore them */ -static int __read_mostly sysrq_enabled = 1; -static bool __read_mostly sysrq_always_enabled; - -static bool sysrq_on(void) -{ - return sysrq_enabled || sysrq_always_enabled; -} - -/* - * A value of 1 means 'all', other nonzero values are an op mask: - */ -static bool sysrq_on_mask(int mask) -{ - return sysrq_always_enabled || - sysrq_enabled == 1 || - (sysrq_enabled & mask); -} - -static int __init sysrq_always_enabled_setup(char *str) -{ - sysrq_always_enabled = true; - pr_info("sysrq always enabled.\n"); - - return 1; -} - -__setup("sysrq_always_enabled", sysrq_always_enabled_setup); - - -static void sysrq_handle_loglevel(int key) -{ - int i; - - i = key - '0'; - console_loglevel = 7; - printk("Loglevel set to %d\n", i); - console_loglevel = i; -} -static struct sysrq_key_op sysrq_loglevel_op = { - .handler = sysrq_handle_loglevel, - .help_msg = "loglevel(0-9)", - .action_msg = "Changing Loglevel", - .enable_mask = SYSRQ_ENABLE_LOG, -}; - -#ifdef CONFIG_VT -static void sysrq_handle_SAK(int key) -{ - struct work_struct *SAK_work = &vc_cons[fg_console].SAK_work; - schedule_work(SAK_work); -} -static struct sysrq_key_op sysrq_SAK_op = { - .handler = sysrq_handle_SAK, - .help_msg = "saK", - .action_msg = "SAK", - .enable_mask = SYSRQ_ENABLE_KEYBOARD, -}; -#else -#define sysrq_SAK_op (*(struct sysrq_key_op *)NULL) -#endif - -#ifdef CONFIG_VT -static void sysrq_handle_unraw(int key) -{ - struct kbd_struct *kbd = &kbd_table[fg_console]; - - if (kbd) - kbd->kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; -} -static struct sysrq_key_op sysrq_unraw_op = { - .handler = sysrq_handle_unraw, - .help_msg = "unRaw", - .action_msg = "Keyboard mode set to system default", - .enable_mask = SYSRQ_ENABLE_KEYBOARD, -}; -#else -#define sysrq_unraw_op (*(struct sysrq_key_op *)NULL) -#endif /* CONFIG_VT */ - -static void sysrq_handle_crash(int key) -{ - char *killer = NULL; - - panic_on_oops = 1; /* force panic */ - wmb(); - *killer = 1; -} -static struct sysrq_key_op sysrq_crash_op = { - .handler = sysrq_handle_crash, - .help_msg = "Crash", - .action_msg = "Trigger a crash", - .enable_mask = SYSRQ_ENABLE_DUMP, -}; - -static void sysrq_handle_reboot(int key) -{ - lockdep_off(); - local_irq_enable(); - emergency_restart(); -} -static struct sysrq_key_op sysrq_reboot_op = { - .handler = sysrq_handle_reboot, - .help_msg = "reBoot", - .action_msg = "Resetting", - .enable_mask = SYSRQ_ENABLE_BOOT, -}; - -static void sysrq_handle_sync(int key) -{ - emergency_sync(); -} -static struct sysrq_key_op sysrq_sync_op = { - .handler = sysrq_handle_sync, - .help_msg = "Sync", - .action_msg = "Emergency Sync", - .enable_mask = SYSRQ_ENABLE_SYNC, -}; - -static void sysrq_handle_show_timers(int key) -{ - sysrq_timer_list_show(); -} - -static struct sysrq_key_op sysrq_show_timers_op = { - .handler = sysrq_handle_show_timers, - .help_msg = "show-all-timers(Q)", - .action_msg = "Show clockevent devices & pending hrtimers (no others)", -}; - -static void sysrq_handle_mountro(int key) -{ - emergency_remount(); -} -static struct sysrq_key_op sysrq_mountro_op = { - .handler = sysrq_handle_mountro, - .help_msg = "Unmount", - .action_msg = "Emergency Remount R/O", - .enable_mask = SYSRQ_ENABLE_REMOUNT, -}; - -#ifdef CONFIG_LOCKDEP -static void sysrq_handle_showlocks(int key) -{ - debug_show_all_locks(); -} - -static struct sysrq_key_op sysrq_showlocks_op = { - .handler = sysrq_handle_showlocks, - .help_msg = "show-all-locks(D)", - .action_msg = "Show Locks Held", -}; -#else -#define sysrq_showlocks_op (*(struct sysrq_key_op *)NULL) -#endif - -#ifdef CONFIG_SMP -static DEFINE_SPINLOCK(show_lock); - -static void showacpu(void *dummy) -{ - unsigned long flags; - - /* Idle CPUs have no interesting backtrace. */ - if (idle_cpu(smp_processor_id())) - return; - - spin_lock_irqsave(&show_lock, flags); - printk(KERN_INFO "CPU%d:\n", smp_processor_id()); - show_stack(NULL, NULL); - spin_unlock_irqrestore(&show_lock, flags); -} - -static void sysrq_showregs_othercpus(struct work_struct *dummy) -{ - smp_call_function(showacpu, NULL, 0); -} - -static DECLARE_WORK(sysrq_showallcpus, sysrq_showregs_othercpus); - -static void sysrq_handle_showallcpus(int key) -{ - /* - * Fall back to the workqueue based printing if the - * backtrace printing did not succeed or the - * architecture has no support for it: - */ - if (!trigger_all_cpu_backtrace()) { - struct pt_regs *regs = get_irq_regs(); - - if (regs) { - printk(KERN_INFO "CPU%d:\n", smp_processor_id()); - show_regs(regs); - } - schedule_work(&sysrq_showallcpus); - } -} - -static struct sysrq_key_op sysrq_showallcpus_op = { - .handler = sysrq_handle_showallcpus, - .help_msg = "show-backtrace-all-active-cpus(L)", - .action_msg = "Show backtrace of all active CPUs", - .enable_mask = SYSRQ_ENABLE_DUMP, -}; -#endif - -static void sysrq_handle_showregs(int key) -{ - struct pt_regs *regs = get_irq_regs(); - if (regs) - show_regs(regs); - perf_event_print_debug(); -} -static struct sysrq_key_op sysrq_showregs_op = { - .handler = sysrq_handle_showregs, - .help_msg = "show-registers(P)", - .action_msg = "Show Regs", - .enable_mask = SYSRQ_ENABLE_DUMP, -}; - -static void sysrq_handle_showstate(int key) -{ - show_state(); -} -static struct sysrq_key_op sysrq_showstate_op = { - .handler = sysrq_handle_showstate, - .help_msg = "show-task-states(T)", - .action_msg = "Show State", - .enable_mask = SYSRQ_ENABLE_DUMP, -}; - -static void sysrq_handle_showstate_blocked(int key) -{ - show_state_filter(TASK_UNINTERRUPTIBLE); -} -static struct sysrq_key_op sysrq_showstate_blocked_op = { - .handler = sysrq_handle_showstate_blocked, - .help_msg = "show-blocked-tasks(W)", - .action_msg = "Show Blocked State", - .enable_mask = SYSRQ_ENABLE_DUMP, -}; - -#ifdef CONFIG_TRACING -#include - -static void sysrq_ftrace_dump(int key) -{ - ftrace_dump(DUMP_ALL); -} -static struct sysrq_key_op sysrq_ftrace_dump_op = { - .handler = sysrq_ftrace_dump, - .help_msg = "dump-ftrace-buffer(Z)", - .action_msg = "Dump ftrace buffer", - .enable_mask = SYSRQ_ENABLE_DUMP, -}; -#else -#define sysrq_ftrace_dump_op (*(struct sysrq_key_op *)NULL) -#endif - -static void sysrq_handle_showmem(int key) -{ - show_mem(); -} -static struct sysrq_key_op sysrq_showmem_op = { - .handler = sysrq_handle_showmem, - .help_msg = "show-memory-usage(M)", - .action_msg = "Show Memory", - .enable_mask = SYSRQ_ENABLE_DUMP, -}; - -/* - * Signal sysrq helper function. Sends a signal to all user processes. - */ -static void send_sig_all(int sig) -{ - struct task_struct *p; - - for_each_process(p) { - if (p->mm && !is_global_init(p)) - /* Not swapper, init nor kernel thread */ - force_sig(sig, p); - } -} - -static void sysrq_handle_term(int key) -{ - send_sig_all(SIGTERM); - console_loglevel = 8; -} -static struct sysrq_key_op sysrq_term_op = { - .handler = sysrq_handle_term, - .help_msg = "terminate-all-tasks(E)", - .action_msg = "Terminate All Tasks", - .enable_mask = SYSRQ_ENABLE_SIGNAL, -}; - -static void moom_callback(struct work_struct *ignored) -{ - out_of_memory(node_zonelist(0, GFP_KERNEL), GFP_KERNEL, 0, NULL); -} - -static DECLARE_WORK(moom_work, moom_callback); - -static void sysrq_handle_moom(int key) -{ - schedule_work(&moom_work); -} -static struct sysrq_key_op sysrq_moom_op = { - .handler = sysrq_handle_moom, - .help_msg = "memory-full-oom-kill(F)", - .action_msg = "Manual OOM execution", - .enable_mask = SYSRQ_ENABLE_SIGNAL, -}; - -#ifdef CONFIG_BLOCK -static void sysrq_handle_thaw(int key) -{ - emergency_thaw_all(); -} -static struct sysrq_key_op sysrq_thaw_op = { - .handler = sysrq_handle_thaw, - .help_msg = "thaw-filesystems(J)", - .action_msg = "Emergency Thaw of all frozen filesystems", - .enable_mask = SYSRQ_ENABLE_SIGNAL, -}; -#endif - -static void sysrq_handle_kill(int key) -{ - send_sig_all(SIGKILL); - console_loglevel = 8; -} -static struct sysrq_key_op sysrq_kill_op = { - .handler = sysrq_handle_kill, - .help_msg = "kill-all-tasks(I)", - .action_msg = "Kill All Tasks", - .enable_mask = SYSRQ_ENABLE_SIGNAL, -}; - -static void sysrq_handle_unrt(int key) -{ - normalize_rt_tasks(); -} -static struct sysrq_key_op sysrq_unrt_op = { - .handler = sysrq_handle_unrt, - .help_msg = "nice-all-RT-tasks(N)", - .action_msg = "Nice All RT Tasks", - .enable_mask = SYSRQ_ENABLE_RTNICE, -}; - -/* Key Operations table and lock */ -static DEFINE_SPINLOCK(sysrq_key_table_lock); - -static struct sysrq_key_op *sysrq_key_table[36] = { - &sysrq_loglevel_op, /* 0 */ - &sysrq_loglevel_op, /* 1 */ - &sysrq_loglevel_op, /* 2 */ - &sysrq_loglevel_op, /* 3 */ - &sysrq_loglevel_op, /* 4 */ - &sysrq_loglevel_op, /* 5 */ - &sysrq_loglevel_op, /* 6 */ - &sysrq_loglevel_op, /* 7 */ - &sysrq_loglevel_op, /* 8 */ - &sysrq_loglevel_op, /* 9 */ - - /* - * a: Don't use for system provided sysrqs, it is handled specially on - * sparc and will never arrive. - */ - NULL, /* a */ - &sysrq_reboot_op, /* b */ - &sysrq_crash_op, /* c & ibm_emac driver debug */ - &sysrq_showlocks_op, /* d */ - &sysrq_term_op, /* e */ - &sysrq_moom_op, /* f */ - /* g: May be registered for the kernel debugger */ - NULL, /* g */ - NULL, /* h - reserved for help */ - &sysrq_kill_op, /* i */ -#ifdef CONFIG_BLOCK - &sysrq_thaw_op, /* j */ -#else - NULL, /* j */ -#endif - &sysrq_SAK_op, /* k */ -#ifdef CONFIG_SMP - &sysrq_showallcpus_op, /* l */ -#else - NULL, /* l */ -#endif - &sysrq_showmem_op, /* m */ - &sysrq_unrt_op, /* n */ - /* o: This will often be registered as 'Off' at init time */ - NULL, /* o */ - &sysrq_showregs_op, /* p */ - &sysrq_show_timers_op, /* q */ - &sysrq_unraw_op, /* r */ - &sysrq_sync_op, /* s */ - &sysrq_showstate_op, /* t */ - &sysrq_mountro_op, /* u */ - /* v: May be registered for frame buffer console restore */ - NULL, /* v */ - &sysrq_showstate_blocked_op, /* w */ - /* x: May be registered on ppc/powerpc for xmon */ - NULL, /* x */ - /* y: May be registered on sparc64 for global register dump */ - NULL, /* y */ - &sysrq_ftrace_dump_op, /* z */ -}; - -/* key2index calculation, -1 on invalid index */ -static int sysrq_key_table_key2index(int key) -{ - int retval; - - if ((key >= '0') && (key <= '9')) - retval = key - '0'; - else if ((key >= 'a') && (key <= 'z')) - retval = key + 10 - 'a'; - else - retval = -1; - return retval; -} - -/* - * get and put functions for the table, exposed to modules. - */ -struct sysrq_key_op *__sysrq_get_key_op(int key) -{ - struct sysrq_key_op *op_p = NULL; - int i; - - i = sysrq_key_table_key2index(key); - if (i != -1) - op_p = sysrq_key_table[i]; - - return op_p; -} - -static void __sysrq_put_key_op(int key, struct sysrq_key_op *op_p) -{ - int i = sysrq_key_table_key2index(key); - - if (i != -1) - sysrq_key_table[i] = op_p; -} - -void __handle_sysrq(int key, bool check_mask) -{ - struct sysrq_key_op *op_p; - int orig_log_level; - int i; - unsigned long flags; - - spin_lock_irqsave(&sysrq_key_table_lock, flags); - /* - * Raise the apparent loglevel to maximum so that the sysrq header - * is shown to provide the user with positive feedback. We do not - * simply emit this at KERN_EMERG as that would change message - * routing in the consumers of /proc/kmsg. - */ - orig_log_level = console_loglevel; - console_loglevel = 7; - printk(KERN_INFO "SysRq : "); - - op_p = __sysrq_get_key_op(key); - if (op_p) { - /* - * Should we check for enabled operations (/proc/sysrq-trigger - * should not) and is the invoked operation enabled? - */ - if (!check_mask || sysrq_on_mask(op_p->enable_mask)) { - printk("%s\n", op_p->action_msg); - console_loglevel = orig_log_level; - op_p->handler(key); - } else { - printk("This sysrq operation is disabled.\n"); - } - } else { - printk("HELP : "); - /* Only print the help msg once per handler */ - for (i = 0; i < ARRAY_SIZE(sysrq_key_table); i++) { - if (sysrq_key_table[i]) { - int j; - - for (j = 0; sysrq_key_table[i] != - sysrq_key_table[j]; j++) - ; - if (j != i) - continue; - printk("%s ", sysrq_key_table[i]->help_msg); - } - } - printk("\n"); - console_loglevel = orig_log_level; - } - spin_unlock_irqrestore(&sysrq_key_table_lock, flags); -} - -void handle_sysrq(int key) -{ - if (sysrq_on()) - __handle_sysrq(key, true); -} -EXPORT_SYMBOL(handle_sysrq); - -#ifdef CONFIG_INPUT - -/* Simple translation table for the SysRq keys */ -static const unsigned char sysrq_xlate[KEY_MAX + 1] = - "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ - "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ - "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ - "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ - "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ - "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ - "\r\000/"; /* 0x60 - 0x6f */ - -static bool sysrq_down; -static int sysrq_alt_use; -static int sysrq_alt; -static DEFINE_SPINLOCK(sysrq_event_lock); - -static bool sysrq_filter(struct input_handle *handle, unsigned int type, - unsigned int code, int value) -{ - bool suppress; - - /* We are called with interrupts disabled, just take the lock */ - spin_lock(&sysrq_event_lock); - - if (type != EV_KEY) - goto out; - - switch (code) { - - case KEY_LEFTALT: - case KEY_RIGHTALT: - if (value) - sysrq_alt = code; - else { - if (sysrq_down && code == sysrq_alt_use) - sysrq_down = false; - - sysrq_alt = 0; - } - break; - - case KEY_SYSRQ: - if (value == 1 && sysrq_alt) { - sysrq_down = true; - sysrq_alt_use = sysrq_alt; - } - break; - - default: - if (sysrq_down && value && value != 2) - __handle_sysrq(sysrq_xlate[code], true); - break; - } - -out: - suppress = sysrq_down; - spin_unlock(&sysrq_event_lock); - - return suppress; -} - -static int sysrq_connect(struct input_handler *handler, - struct input_dev *dev, - const struct input_device_id *id) -{ - struct input_handle *handle; - int error; - - sysrq_down = false; - sysrq_alt = 0; - - handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); - if (!handle) - return -ENOMEM; - - handle->dev = dev; - handle->handler = handler; - handle->name = "sysrq"; - - error = input_register_handle(handle); - if (error) { - pr_err("Failed to register input sysrq handler, error %d\n", - error); - goto err_free; - } - - error = input_open_device(handle); - if (error) { - pr_err("Failed to open input device, error %d\n", error); - goto err_unregister; - } - - return 0; - - err_unregister: - input_unregister_handle(handle); - err_free: - kfree(handle); - return error; -} - -static void sysrq_disconnect(struct input_handle *handle) -{ - input_close_device(handle); - input_unregister_handle(handle); - kfree(handle); -} - -/* - * We are matching on KEY_LEFTALT instead of KEY_SYSRQ because not all - * keyboards have SysRq key predefined and so user may add it to keymap - * later, but we expect all such keyboards to have left alt. - */ -static const struct input_device_id sysrq_ids[] = { - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT | - INPUT_DEVICE_ID_MATCH_KEYBIT, - .evbit = { BIT_MASK(EV_KEY) }, - .keybit = { BIT_MASK(KEY_LEFTALT) }, - }, - { }, -}; - -static struct input_handler sysrq_handler = { - .filter = sysrq_filter, - .connect = sysrq_connect, - .disconnect = sysrq_disconnect, - .name = "sysrq", - .id_table = sysrq_ids, -}; - -static bool sysrq_handler_registered; - -static inline void sysrq_register_handler(void) -{ - int error; - - error = input_register_handler(&sysrq_handler); - if (error) - pr_err("Failed to register input handler, error %d", error); - else - sysrq_handler_registered = true; -} - -static inline void sysrq_unregister_handler(void) -{ - if (sysrq_handler_registered) { - input_unregister_handler(&sysrq_handler); - sysrq_handler_registered = false; - } -} - -#else - -static inline void sysrq_register_handler(void) -{ -} - -static inline void sysrq_unregister_handler(void) -{ -} - -#endif /* CONFIG_INPUT */ - -int sysrq_toggle_support(int enable_mask) -{ - bool was_enabled = sysrq_on(); - - sysrq_enabled = enable_mask; - - if (was_enabled != sysrq_on()) { - if (sysrq_on()) - sysrq_register_handler(); - else - sysrq_unregister_handler(); - } - - return 0; -} - -static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, - struct sysrq_key_op *remove_op_p) -{ - int retval; - unsigned long flags; - - spin_lock_irqsave(&sysrq_key_table_lock, flags); - if (__sysrq_get_key_op(key) == remove_op_p) { - __sysrq_put_key_op(key, insert_op_p); - retval = 0; - } else { - retval = -1; - } - spin_unlock_irqrestore(&sysrq_key_table_lock, flags); - return retval; -} - -int register_sysrq_key(int key, struct sysrq_key_op *op_p) -{ - return __sysrq_swap_key_ops(key, op_p, NULL); -} -EXPORT_SYMBOL(register_sysrq_key); - -int unregister_sysrq_key(int key, struct sysrq_key_op *op_p) -{ - return __sysrq_swap_key_ops(key, NULL, op_p); -} -EXPORT_SYMBOL(unregister_sysrq_key); - -#ifdef CONFIG_PROC_FS -/* - * writing 'C' to /proc/sysrq-trigger is like sysrq-C - */ -static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - if (count) { - char c; - - if (get_user(c, buf)) - return -EFAULT; - __handle_sysrq(c, false); - } - - return count; -} - -static const struct file_operations proc_sysrq_trigger_operations = { - .write = write_sysrq_trigger, - .llseek = noop_llseek, -}; - -static void sysrq_init_procfs(void) -{ - if (!proc_create("sysrq-trigger", S_IWUSR, NULL, - &proc_sysrq_trigger_operations)) - pr_err("Failed to register proc interface\n"); -} - -#else - -static inline void sysrq_init_procfs(void) -{ -} - -#endif /* CONFIG_PROC_FS */ - -static int __init sysrq_init(void) -{ - sysrq_init_procfs(); - - if (sysrq_on()) - sysrq_register_handler(); - - return 0; -} -module_init(sysrq_init); diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c deleted file mode 100644 index f64582b0f623..000000000000 --- a/drivers/char/tty_audit.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Creating audit events from TTY input. - * - * Copyright (C) 2007 Red Hat, Inc. All rights reserved. This copyrighted - * material is made available to anyone wishing to use, modify, copy, or - * redistribute it subject to the terms and conditions of the GNU General - * Public License v.2. - * - * Authors: Miloslav Trmac - */ - -#include -#include -#include - -struct tty_audit_buf { - atomic_t count; - struct mutex mutex; /* Protects all data below */ - int major, minor; /* The TTY which the data is from */ - unsigned icanon:1; - size_t valid; - unsigned char *data; /* Allocated size N_TTY_BUF_SIZE */ -}; - -static struct tty_audit_buf *tty_audit_buf_alloc(int major, int minor, - int icanon) -{ - struct tty_audit_buf *buf; - - buf = kmalloc(sizeof(*buf), GFP_KERNEL); - if (!buf) - goto err; - buf->data = kmalloc(N_TTY_BUF_SIZE, GFP_KERNEL); - if (!buf->data) - goto err_buf; - atomic_set(&buf->count, 1); - mutex_init(&buf->mutex); - buf->major = major; - buf->minor = minor; - buf->icanon = icanon; - buf->valid = 0; - return buf; - -err_buf: - kfree(buf); -err: - return NULL; -} - -static void tty_audit_buf_free(struct tty_audit_buf *buf) -{ - WARN_ON(buf->valid != 0); - kfree(buf->data); - kfree(buf); -} - -static void tty_audit_buf_put(struct tty_audit_buf *buf) -{ - if (atomic_dec_and_test(&buf->count)) - tty_audit_buf_free(buf); -} - -static void tty_audit_log(const char *description, struct task_struct *tsk, - uid_t loginuid, unsigned sessionid, int major, - int minor, unsigned char *data, size_t size) -{ - struct audit_buffer *ab; - - ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_TTY); - if (ab) { - char name[sizeof(tsk->comm)]; - uid_t uid = task_uid(tsk); - - audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u " - "major=%d minor=%d comm=", description, - tsk->pid, uid, loginuid, sessionid, - major, minor); - get_task_comm(name, tsk); - audit_log_untrustedstring(ab, name); - audit_log_format(ab, " data="); - audit_log_n_hex(ab, data, size); - audit_log_end(ab); - } -} - -/** - * tty_audit_buf_push - Push buffered data out - * - * Generate an audit message from the contents of @buf, which is owned by - * @tsk with @loginuid. @buf->mutex must be locked. - */ -static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid, - unsigned int sessionid, - struct tty_audit_buf *buf) -{ - if (buf->valid == 0) - return; - if (audit_enabled == 0) - return; - tty_audit_log("tty", tsk, loginuid, sessionid, buf->major, buf->minor, - buf->data, buf->valid); - buf->valid = 0; -} - -/** - * tty_audit_buf_push_current - Push buffered data out - * - * Generate an audit message from the contents of @buf, which is owned by - * the current task. @buf->mutex must be locked. - */ -static void tty_audit_buf_push_current(struct tty_audit_buf *buf) -{ - uid_t auid = audit_get_loginuid(current); - unsigned int sessionid = audit_get_sessionid(current); - tty_audit_buf_push(current, auid, sessionid, buf); -} - -/** - * tty_audit_exit - Handle a task exit - * - * Make sure all buffered data is written out and deallocate the buffer. - * Only needs to be called if current->signal->tty_audit_buf != %NULL. - */ -void tty_audit_exit(void) -{ - struct tty_audit_buf *buf; - - spin_lock_irq(¤t->sighand->siglock); - buf = current->signal->tty_audit_buf; - current->signal->tty_audit_buf = NULL; - spin_unlock_irq(¤t->sighand->siglock); - if (!buf) - return; - - mutex_lock(&buf->mutex); - tty_audit_buf_push_current(buf); - mutex_unlock(&buf->mutex); - - tty_audit_buf_put(buf); -} - -/** - * tty_audit_fork - Copy TTY audit state for a new task - * - * Set up TTY audit state in @sig from current. @sig needs no locking. - */ -void tty_audit_fork(struct signal_struct *sig) -{ - spin_lock_irq(¤t->sighand->siglock); - sig->audit_tty = current->signal->audit_tty; - spin_unlock_irq(¤t->sighand->siglock); -} - -/** - * tty_audit_tiocsti - Log TIOCSTI - */ -void tty_audit_tiocsti(struct tty_struct *tty, char ch) -{ - struct tty_audit_buf *buf; - int major, minor, should_audit; - - spin_lock_irq(¤t->sighand->siglock); - should_audit = current->signal->audit_tty; - buf = current->signal->tty_audit_buf; - if (buf) - atomic_inc(&buf->count); - spin_unlock_irq(¤t->sighand->siglock); - - major = tty->driver->major; - minor = tty->driver->minor_start + tty->index; - if (buf) { - mutex_lock(&buf->mutex); - if (buf->major == major && buf->minor == minor) - tty_audit_buf_push_current(buf); - mutex_unlock(&buf->mutex); - tty_audit_buf_put(buf); - } - - if (should_audit && audit_enabled) { - uid_t auid; - unsigned int sessionid; - - auid = audit_get_loginuid(current); - sessionid = audit_get_sessionid(current); - tty_audit_log("ioctl=TIOCSTI", current, auid, sessionid, major, - minor, &ch, 1); - } -} - -/** - * tty_audit_push_task - Flush task's pending audit data - * @tsk: task pointer - * @loginuid: sender login uid - * @sessionid: sender session id - * - * Called with a ref on @tsk held. Try to lock sighand and get a - * reference to the tty audit buffer if available. - * Flush the buffer or return an appropriate error code. - */ -int tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid) -{ - struct tty_audit_buf *buf = ERR_PTR(-EPERM); - unsigned long flags; - - if (!lock_task_sighand(tsk, &flags)) - return -ESRCH; - - if (tsk->signal->audit_tty) { - buf = tsk->signal->tty_audit_buf; - if (buf) - atomic_inc(&buf->count); - } - unlock_task_sighand(tsk, &flags); - - /* - * Return 0 when signal->audit_tty set - * but tsk->signal->tty_audit_buf == NULL. - */ - if (!buf || IS_ERR(buf)) - return PTR_ERR(buf); - - mutex_lock(&buf->mutex); - tty_audit_buf_push(tsk, loginuid, sessionid, buf); - mutex_unlock(&buf->mutex); - - tty_audit_buf_put(buf); - return 0; -} - -/** - * tty_audit_buf_get - Get an audit buffer. - * - * Get an audit buffer for @tty, allocate it if necessary. Return %NULL - * 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) -{ - struct tty_audit_buf *buf, *buf2; - - buf = NULL; - buf2 = NULL; - spin_lock_irq(¤t->sighand->siglock); - if (likely(!current->signal->audit_tty)) - goto out; - buf = current->signal->tty_audit_buf; - if (buf) { - atomic_inc(&buf->count); - goto out; - } - spin_unlock_irq(¤t->sighand->siglock); - - buf2 = tty_audit_buf_alloc(tty->driver->major, - tty->driver->minor_start + tty->index, - tty->icanon); - if (buf2 == NULL) { - audit_log_lost("out of memory in TTY auditing"); - return NULL; - } - - spin_lock_irq(¤t->sighand->siglock); - if (!current->signal->audit_tty) - goto out; - buf = current->signal->tty_audit_buf; - if (!buf) { - current->signal->tty_audit_buf = buf2; - buf = buf2; - buf2 = NULL; - } - atomic_inc(&buf->count); - /* Fall through */ - out: - spin_unlock_irq(¤t->sighand->siglock); - if (buf2) - tty_audit_buf_free(buf2); - return buf; -} - -/** - * tty_audit_add_data - Add data for TTY auditing. - * - * Audit @data of @size from @tty, if necessary. - */ -void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, - size_t size) -{ - struct tty_audit_buf *buf; - int major, minor; - - if (unlikely(size == 0)) - return; - - if (tty->driver->type == TTY_DRIVER_TYPE_PTY - && tty->driver->subtype == PTY_TYPE_MASTER) - return; - - buf = tty_audit_buf_get(tty); - if (!buf) - return; - - mutex_lock(&buf->mutex); - major = tty->driver->major; - minor = tty->driver->minor_start + tty->index; - if (buf->major != major || buf->minor != minor - || buf->icanon != tty->icanon) { - tty_audit_buf_push_current(buf); - buf->major = major; - buf->minor = minor; - buf->icanon = tty->icanon; - } - do { - size_t run; - - run = N_TTY_BUF_SIZE - buf->valid; - if (run > size) - run = size; - memcpy(buf->data + buf->valid, data, run); - buf->valid += run; - data += run; - size -= run; - if (buf->valid == N_TTY_BUF_SIZE) - tty_audit_buf_push_current(buf); - } while (size != 0); - mutex_unlock(&buf->mutex); - tty_audit_buf_put(buf); -} - -/** - * tty_audit_push - Push buffered data out - * - * Make sure no audit data is pending for @tty on the current process. - */ -void tty_audit_push(struct tty_struct *tty) -{ - struct tty_audit_buf *buf; - - spin_lock_irq(¤t->sighand->siglock); - if (likely(!current->signal->audit_tty)) { - spin_unlock_irq(¤t->sighand->siglock); - return; - } - buf = current->signal->tty_audit_buf; - if (buf) - atomic_inc(&buf->count); - spin_unlock_irq(¤t->sighand->siglock); - - if (buf) { - int major, minor; - - major = tty->driver->major; - minor = tty->driver->minor_start + tty->index; - mutex_lock(&buf->mutex); - if (buf->major == major && buf->minor == minor) - tty_audit_buf_push_current(buf); - mutex_unlock(&buf->mutex); - tty_audit_buf_put(buf); - } -} diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c deleted file mode 100644 index cc1e9850d655..000000000000 --- a/drivers/char/tty_buffer.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Tty buffer allocation management - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * tty_buffer_free_all - free buffers used by a tty - * @tty: tty to free from - * - * Remove all the buffers pending on a tty whether queued with data - * or in the free ring. Must be called when the tty is no longer in use - * - * Locking: none - */ - -void tty_buffer_free_all(struct tty_struct *tty) -{ - struct tty_buffer *thead; - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; - kfree(thead); - } - while ((thead = tty->buf.free) != NULL) { - tty->buf.free = thead->next; - kfree(thead); - } - tty->buf.tail = NULL; - tty->buf.memory_used = 0; -} - -/** - * tty_buffer_alloc - allocate a tty buffer - * @tty: tty device - * @size: desired size (characters) - * - * Allocate a new tty buffer to hold the desired number of characters. - * Return NULL if out of memory or the allocation would exceed the - * per device queue - * - * Locking: Caller must hold tty->buf.lock - */ - -static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) -{ - struct tty_buffer *p; - - if (tty->buf.memory_used + size > 65536) - return NULL; - p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); - if (p == NULL) - return NULL; - p->used = 0; - p->size = size; - p->next = NULL; - p->commit = 0; - 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; - return p; -} - -/** - * tty_buffer_free - free a tty buffer - * @tty: tty owning the buffer - * @b: the buffer to free - * - * Free a tty buffer, or add it to the free list according to our - * internal strategy - * - * Locking: Caller must hold tty->buf.lock - */ - -static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) -{ - /* Dumb strategy for now - should keep some stats */ - tty->buf.memory_used -= b->size; - WARN_ON(tty->buf.memory_used < 0); - - if (b->size >= 512) - kfree(b); - else { - b->next = tty->buf.free; - tty->buf.free = b; - } -} - -/** - * __tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. Caller must - * hold the buffer lock and must have ensured no parallel flush to - * ldisc is running. - * - * Locking: Caller must hold tty->buf.lock - */ - -static void __tty_buffer_flush(struct tty_struct *tty) -{ - struct tty_buffer *thead; - - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; - tty_buffer_free(tty, thead); - } - tty->buf.tail = NULL; -} - -/** - * tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. If the buffer is - * being processed by flush_to_ldisc then we defer the processing - * to that function - * - * Locking: none - */ - -void tty_buffer_flush(struct tty_struct *tty) -{ - 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); - spin_unlock_irqrestore(&tty->buf.lock, flags); - wait_event(tty->read_wait, - test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); - return; - } else - __tty_buffer_flush(tty); - spin_unlock_irqrestore(&tty->buf.lock, flags); -} - -/** - * tty_buffer_find - find a free tty buffer - * @tty: tty owning the buffer - * @size: characters wanted - * - * Locate an existing suitable tty buffer or if we are lacking one then - * allocate a new one. We round our buffers off in 256 character chunks - * to get better allocation behaviour. - * - * Locking: Caller must hold tty->buf.lock - */ - -static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) -{ - struct tty_buffer **tbh = &tty->buf.free; - while ((*tbh) != NULL) { - struct tty_buffer *t = *tbh; - if (t->size >= size) { - *tbh = t->next; - t->next = NULL; - t->used = 0; - t->commit = 0; - t->read = 0; - tty->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); - /* Should possibly check if this fails for the largest buffer we - have queued and recycle that ? */ -} - -/** - * tty_buffer_request_room - grow tty buffer if needed - * @tty: tty structure - * @size: size desired - * - * 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 - */ -int tty_buffer_request_room(struct tty_struct *tty, size_t size) -{ - struct tty_buffer *b, *n; - int left; - unsigned long flags; - - spin_lock_irqsave(&tty->buf.lock, flags); - - /* OPTIMISATION: We could keep a per tty "zero" sized buffer to - remove this conditional if its worth it. This would be invisible - to the callers */ - if ((b = tty->buf.tail) != NULL) - left = b->size - b->used; - else - left = 0; - - if (left < size) { - /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(tty, size)) != NULL) { - if (b != NULL) { - b->next = n; - b->commit = b->used; - } else - tty->buf.head = n; - tty->buf.tail = n; - } else - size = left; - } - - spin_unlock_irqrestore(&tty->buf.lock, flags); - return size; -} -EXPORT_SYMBOL_GPL(tty_buffer_request_room); - -/** - * tty_insert_flip_string_fixed_flag - Add characters to the tty buffer - * @tty: tty structure - * @chars: characters - * @flag: flag value for each character - * @size: size - * - * 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 - */ - -int tty_insert_flip_string_fixed_flag(struct tty_struct *tty, - const unsigned char *chars, char flag, size_t size) -{ - int copied = 0; - do { - int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); - int space = tty_buffer_request_room(tty, goal); - struct tty_buffer *tb = tty->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) - break; - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memset(tb->flag_buf_ptr + tb->used, flag, space); - tb->used += space; - copied += space; - chars += space; - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - } while (unlikely(size > copied)); - return copied; -} -EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag); - -/** - * tty_insert_flip_string_flags - Add characters to the tty buffer - * @tty: tty structure - * @chars: characters - * @flags: flag bytes - * @size: size - * - * Queue a series of bytes to the tty buffering. For each character - * the flags array indicates the status of the character. Returns the - * number added. - * - * Locking: Called functions may take tty->buf.lock - */ - -int tty_insert_flip_string_flags(struct tty_struct *tty, - const unsigned char *chars, const char *flags, size_t size) -{ - int copied = 0; - do { - int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); - int space = tty_buffer_request_room(tty, goal); - struct tty_buffer *tb = tty->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) - break; - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memcpy(tb->flag_buf_ptr + tb->used, flags, space); - tb->used += space; - copied += space; - chars += space; - flags += space; - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - } while (unlikely(size > copied)); - return copied; -} -EXPORT_SYMBOL(tty_insert_flip_string_flags); - -/** - * tty_schedule_flip - push characters to ldisc - * @tty: tty to push from - * - * Takes any pending buffers and transfers their ownership to the - * ldisc side of the queue. It then schedules those characters for - * processing by the line discipline. - * - * Locking: Takes tty->buf.lock - */ - -void tty_schedule_flip(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); - schedule_delayed_work(&tty->buf.work, 1); -} -EXPORT_SYMBOL(tty_schedule_flip); - -/** - * tty_prepare_flip_string - make room for characters - * @tty: tty - * @chars: return pointer for character write area - * @size: desired size - * - * Prepare a block of space in the buffer for data. Returns the length - * available and buffer pointer to the space which is now allocated and - * accounted for as ready for normal characters. This is used for drivers - * 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 - */ - -int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, - size_t size) -{ - int space = tty_buffer_request_room(tty, size); - if (likely(space)) { - struct tty_buffer *tb = tty->buf.tail; - *chars = tb->char_buf_ptr + tb->used; - memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); - tb->used += space; - } - return space; -} -EXPORT_SYMBOL_GPL(tty_prepare_flip_string); - -/** - * tty_prepare_flip_string_flags - make room for characters - * @tty: tty - * @chars: return pointer for character write area - * @flags: return pointer for status flag write area - * @size: desired size - * - * Prepare a block of space in the buffer for data. Returns the length - * available and buffer pointer to the space which is now allocated and - * accounted for as ready for characters. This is used for drivers - * 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 - */ - -int tty_prepare_flip_string_flags(struct tty_struct *tty, - unsigned char **chars, char **flags, size_t size) -{ - int space = tty_buffer_request_room(tty, size); - if (likely(space)) { - struct tty_buffer *tb = tty->buf.tail; - *chars = tb->char_buf_ptr + tb->used; - *flags = tb->flag_buf_ptr + tb->used; - tb->used += space; - } - return space; -} -EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); - - - -/** - * flush_to_ldisc - * @work: tty structure passed from work queue. - * - * This routine is called out of the software interrupt to flush data - * from the buffer chain to the line discipline. - * - * Locking: holds tty->buf.lock to guard buffer list. Drops the lock - * while invoking the line discipline receive_buf method. The - * receive_buf method is single threaded for each tty instance. - */ - -static void flush_to_ldisc(struct work_struct *work) -{ - struct tty_struct *tty = - container_of(work, struct tty_struct, buf.work.work); - unsigned long flags; - struct tty_ldisc *disc; - - disc = tty_ldisc_ref(tty); - if (disc == NULL) /* !TTY_LDISC */ - return; - - spin_lock_irqsave(&tty->buf.lock, flags); - - if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) { - struct tty_buffer *head; - while ((head = tty->buf.head) != NULL) { - int count; - char *char_buf; - unsigned char *flag_buf; - - count = head->commit - head->read; - if (!count) { - if (head->next == NULL) - break; - tty->buf.head = head->next; - tty_buffer_free(tty, head); - continue; - } - /* 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)) - break; - if (!tty->receive_room) { - schedule_delayed_work(&tty->buf.work, 1); - break; - } - if (count > tty->receive_room) - count = tty->receive_room; - char_buf = head->char_buf_ptr + head->read; - flag_buf = head->flag_buf_ptr + head->read; - head->read += count; - spin_unlock_irqrestore(&tty->buf.lock, flags); - disc->ops->receive_buf(tty, char_buf, - flag_buf, count); - spin_lock_irqsave(&tty->buf.lock, flags); - } - clear_bit(TTY_FLUSHING, &tty->flags); - } - - /* 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)) { - __tty_buffer_flush(tty); - clear_bit(TTY_FLUSHPENDING, &tty->flags); - wake_up(&tty->read_wait); - } - spin_unlock_irqrestore(&tty->buf.lock, flags); - - tty_ldisc_deref(disc); -} - -/** - * tty_flush_to_ldisc - * @tty: tty to push - * - * Push the terminal flip buffers to the line discipline. - * - * Must not be called from IRQ context. - */ -void tty_flush_to_ldisc(struct tty_struct *tty) -{ - flush_delayed_work(&tty->buf.work); -} - -/** - * tty_flip_buffer_push - terminal - * @tty: tty to push - * - * Queue a push of the terminal flip buffers to the line discipline. This - * function must not be called from IRQ context if tty->low_latency is set. - * - * In the event of the queue being busy for flipping the work will be - * held off and retried later. - * - * Locking: tty buffer lock. Driver locks in low latency mode. - */ - -void tty_flip_buffer_push(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); - - if (tty->low_latency) - flush_to_ldisc(&tty->buf.work.work); - else - schedule_delayed_work(&tty->buf.work, 1); -} -EXPORT_SYMBOL(tty_flip_buffer_push); - -/** - * tty_buffer_init - prepare a tty buffer structure - * @tty: tty to initialise - * - * Set up the initial state of the buffer management for a tty device. - * Must be called before the other tty buffer functions are used. - * - * Locking: none - */ - -void tty_buffer_init(struct tty_struct *tty) -{ - spin_lock_init(&tty->buf.lock); - tty->buf.head = NULL; - tty->buf.tail = NULL; - tty->buf.free = NULL; - tty->buf.memory_used = 0; - INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc); -} - diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c deleted file mode 100644 index c05c5af5aa04..000000000000 --- a/drivers/char/tty_io.c +++ /dev/null @@ -1,3263 +0,0 @@ -/* - * linux/drivers/char/tty_io.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles - * or rs-channels. It also implements echoing, cooked mode etc. - * - * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0. - * - * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the - * tty_struct and tty_queue structures. Previously there was an array - * of 256 tty_struct's which was statically allocated, and the - * tty_queue structures were allocated at boot time. Both are now - * dynamically allocated only when the tty is open. - * - * Also restructured routines so that there is more of a separation - * between the high-level tty routines (tty_io.c and tty_ioctl.c) and - * the low-level tty routines (serial.c, pty.c, console.c). This - * makes for cleaner and more compact code. -TYT, 9/17/92 - * - * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines - * which can be dynamically activated and de-activated by the line - * discipline handling modules (like SLIP). - * - * NOTE: pay no attention to the line discipline code (yet); its - * interface is still subject to change in this version... - * -- TYT, 1/31/92 - * - * Added functionality to the OPOST tty handling. No delays, but all - * other bits should be there. - * -- Nick Holloway , 27th May 1993. - * - * Rewrote canonical mode and added more termios flags. - * -- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94 - * - * Reorganized FASYNC support so mouse code can share it. - * -- ctm@ardi.com, 9Sep95 - * - * New TIOCLINUX variants added. - * -- mj@k332.feld.cvut.cz, 19-Nov-95 - * - * Restrict vt switching via ioctl() - * -- grif@cs.ucr.edu, 5-Dec-95 - * - * Move console and virtual terminal code to more appropriate files, - * implement CONFIG_VT and generalize console device interface. - * -- Marko Kohtala , March 97 - * - * Rewrote tty_init_dev and tty_release_dev to eliminate races. - * -- Bill Hawes , June 97 - * - * Added devfs support. - * -- C. Scott Ananian , 13-Jan-1998 - * - * Added support for a Unix98-style ptmx device. - * -- C. Scott Ananian , 14-Jan-1998 - * - * Reduced memory usage for older ARM systems - * -- Russell King - * - * Move do_SAK() into process context. Less stack use in devfs functions. - * alloc_tty_struct() always uses kmalloc() - * -- Andrew Morton 17Mar01 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include - -#undef TTY_DEBUG_HANGUP - -#define TTY_PARANOIA_CHECK 1 -#define CHECK_TTY_COUNT 1 - -struct ktermios tty_std_termios = { /* for the benefit of tty drivers */ - .c_iflag = ICRNL | IXON, - .c_oflag = OPOST | ONLCR, - .c_cflag = B38400 | CS8 | CREAD | HUPCL, - .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | - ECHOCTL | ECHOKE | IEXTEN, - .c_cc = INIT_C_CC, - .c_ispeed = 38400, - .c_ospeed = 38400 -}; - -EXPORT_SYMBOL(tty_std_termios); - -/* This list gets poked at by procfs and various bits of boot up code. This - could do with some rationalisation such as pulling the tty proc function - into this file */ - -LIST_HEAD(tty_drivers); /* linked list of tty drivers */ - -/* Mutex to protect creating and releasing a tty. This is shared with - vt.c for deeply disgusting hack reasons */ -DEFINE_MUTEX(tty_mutex); -EXPORT_SYMBOL(tty_mutex); - -/* Spinlock to protect the tty->tty_files list */ -DEFINE_SPINLOCK(tty_files_lock); - -static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); -static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); -ssize_t redirected_tty_write(struct file *, const char __user *, - size_t, loff_t *); -static unsigned int tty_poll(struct file *, poll_table *); -static int tty_open(struct inode *, struct file *); -long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -#ifdef CONFIG_COMPAT -static long tty_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg); -#else -#define tty_compat_ioctl NULL -#endif -static int __tty_fasync(int fd, struct file *filp, int on); -static int tty_fasync(int fd, struct file *filp, int on); -static void release_tty(struct tty_struct *tty, int idx); -static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); -static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); - -/** - * alloc_tty_struct - allocate a tty object - * - * Return a new empty tty structure. The data fields have not - * been initialized in any way but has been zeroed - * - * Locking: none - */ - -struct tty_struct *alloc_tty_struct(void) -{ - return kzalloc(sizeof(struct tty_struct), GFP_KERNEL); -} - -/** - * free_tty_struct - free a disused tty - * @tty: tty struct to free - * - * Free the write buffers, tty queue and tty memory itself. - * - * Locking: none. Must be called after tty is definitely unused - */ - -void free_tty_struct(struct tty_struct *tty) -{ - if (tty->dev) - put_device(tty->dev); - kfree(tty->write_buf); - tty_buffer_free_all(tty); - kfree(tty); -} - -static inline struct tty_struct *file_tty(struct file *file) -{ - return ((struct tty_file_private *)file->private_data)->tty; -} - -/* Associate a new file with the tty structure */ -int tty_add_file(struct tty_struct *tty, struct file *file) -{ - struct tty_file_private *priv; - - priv = kmalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->tty = tty; - priv->file = file; - file->private_data = priv; - - spin_lock(&tty_files_lock); - list_add(&priv->list, &tty->tty_files); - spin_unlock(&tty_files_lock); - - return 0; -} - -/* Delete file from its tty */ -void tty_del_file(struct file *file) -{ - struct tty_file_private *priv = file->private_data; - - spin_lock(&tty_files_lock); - list_del(&priv->list); - spin_unlock(&tty_files_lock); - file->private_data = NULL; - kfree(priv); -} - - -#define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base) - -/** - * tty_name - return tty naming - * @tty: tty structure - * @buf: buffer for output - * - * Convert a tty structure into a name. The name reflects the kernel - * naming policy and if udev is in use may not reflect user space - * - * Locking: none - */ - -char *tty_name(struct tty_struct *tty, char *buf) -{ - if (!tty) /* Hmm. NULL pointer. That's fun. */ - strcpy(buf, "NULL tty"); - else - strcpy(buf, tty->name); - return buf; -} - -EXPORT_SYMBOL(tty_name); - -int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, - const char *routine) -{ -#ifdef TTY_PARANOIA_CHECK - if (!tty) { - printk(KERN_WARNING - "null TTY for (%d:%d) in %s\n", - imajor(inode), iminor(inode), routine); - return 1; - } - if (tty->magic != TTY_MAGIC) { - printk(KERN_WARNING - "bad magic number for tty struct (%d:%d) in %s\n", - imajor(inode), iminor(inode), routine); - return 1; - } -#endif - return 0; -} - -static int check_tty_count(struct tty_struct *tty, const char *routine) -{ -#ifdef CHECK_TTY_COUNT - struct list_head *p; - int count = 0; - - spin_lock(&tty_files_lock); - list_for_each(p, &tty->tty_files) { - count++; - } - spin_unlock(&tty_files_lock); - if (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_SLAVE && - tty->link && tty->link->count) - count++; - if (tty->count != count) { - printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) " - "!= #fd's(%d) in %s\n", - tty->name, tty->count, count, routine); - return count; - } -#endif - return 0; -} - -/** - * get_tty_driver - find device of a tty - * @dev_t: device identifier - * @index: returns the index of the tty - * - * This routine returns a tty driver structure, given a device number - * and also passes back the index number. - * - * Locking: caller must hold tty_mutex - */ - -static struct tty_driver *get_tty_driver(dev_t device, int *index) -{ - struct tty_driver *p; - - list_for_each_entry(p, &tty_drivers, tty_drivers) { - dev_t base = MKDEV(p->major, p->minor_start); - if (device < base || device >= base + p->num) - continue; - *index = device - base; - return tty_driver_kref_get(p); - } - return NULL; -} - -#ifdef CONFIG_CONSOLE_POLL - -/** - * tty_find_polling_driver - find device of a polled tty - * @name: name string to match - * @line: pointer to resulting tty line nr - * - * This routine returns a tty driver structure, given a name - * and the condition that the tty driver is capable of polled - * operation. - */ -struct tty_driver *tty_find_polling_driver(char *name, int *line) -{ - struct tty_driver *p, *res = NULL; - int tty_line = 0; - int len; - char *str, *stp; - - for (str = name; *str; str++) - if ((*str >= '0' && *str <= '9') || *str == ',') - break; - if (!*str) - return NULL; - - len = str - name; - tty_line = simple_strtoul(str, &str, 10); - - mutex_lock(&tty_mutex); - /* Search through the tty devices to look for a match */ - list_for_each_entry(p, &tty_drivers, tty_drivers) { - if (strncmp(name, p->name, len) != 0) - continue; - stp = str; - if (*stp == ',') - stp++; - if (*stp == '\0') - stp = NULL; - - if (tty_line >= 0 && tty_line < p->num && p->ops && - p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) { - res = tty_driver_kref_get(p); - *line = tty_line; - break; - } - } - mutex_unlock(&tty_mutex); - - return res; -} -EXPORT_SYMBOL_GPL(tty_find_polling_driver); -#endif - -/** - * tty_check_change - check for POSIX terminal changes - * @tty: tty to check - * - * If we try to write to, or set the state of, a terminal and we're - * not in the foreground, send a SIGTTOU. If the signal is blocked or - * ignored, go ahead and perform the operation. (POSIX 7.2) - * - * Locking: ctrl_lock - */ - -int tty_check_change(struct tty_struct *tty) -{ - unsigned long flags; - int ret = 0; - - if (current->signal->tty != tty) - return 0; - - spin_lock_irqsave(&tty->ctrl_lock, flags); - - if (!tty->pgrp) { - printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n"); - goto out_unlock; - } - if (task_pgrp(current) == tty->pgrp) - goto out_unlock; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - if (is_ignored(SIGTTOU)) - goto out; - if (is_current_pgrp_orphaned()) { - ret = -EIO; - goto out; - } - kill_pgrp(task_pgrp(current), SIGTTOU, 1); - set_thread_flag(TIF_SIGPENDING); - ret = -ERESTARTSYS; -out: - return ret; -out_unlock: - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - return ret; -} - -EXPORT_SYMBOL(tty_check_change); - -static ssize_t hung_up_tty_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - return 0; -} - -static ssize_t hung_up_tty_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - return -EIO; -} - -/* No kernel lock held - none needed ;) */ -static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait) -{ - return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM; -} - -static long hung_up_tty_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - return cmd == TIOCSPGRP ? -ENOTTY : -EIO; -} - -static long hung_up_tty_compat_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return cmd == TIOCSPGRP ? -ENOTTY : -EIO; -} - -static const struct file_operations tty_fops = { - .llseek = no_llseek, - .read = tty_read, - .write = tty_write, - .poll = tty_poll, - .unlocked_ioctl = tty_ioctl, - .compat_ioctl = tty_compat_ioctl, - .open = tty_open, - .release = tty_release, - .fasync = tty_fasync, -}; - -static const struct file_operations console_fops = { - .llseek = no_llseek, - .read = tty_read, - .write = redirected_tty_write, - .poll = tty_poll, - .unlocked_ioctl = tty_ioctl, - .compat_ioctl = tty_compat_ioctl, - .open = tty_open, - .release = tty_release, - .fasync = tty_fasync, -}; - -static const struct file_operations hung_up_tty_fops = { - .llseek = no_llseek, - .read = hung_up_tty_read, - .write = hung_up_tty_write, - .poll = hung_up_tty_poll, - .unlocked_ioctl = hung_up_tty_ioctl, - .compat_ioctl = hung_up_tty_compat_ioctl, - .release = tty_release, -}; - -static DEFINE_SPINLOCK(redirect_lock); -static struct file *redirect; - -/** - * tty_wakeup - request more data - * @tty: terminal - * - * Internal and external helper for wakeups of tty. This function - * informs the line discipline if present that the driver is ready - * to receive more output data. - */ - -void tty_wakeup(struct tty_struct *tty) -{ - struct tty_ldisc *ld; - - if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) { - ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->ops->write_wakeup) - ld->ops->write_wakeup(tty); - tty_ldisc_deref(ld); - } - } - wake_up_interruptible_poll(&tty->write_wait, POLLOUT); -} - -EXPORT_SYMBOL_GPL(tty_wakeup); - -/** - * __tty_hangup - actual handler for hangup events - * @work: tty device - * - * This can be called by the "eventd" kernel thread. That is process - * synchronous but doesn't hold any locks, so we need to make sure we - * have the appropriate locks for what we're doing. - * - * The hangup event clears any pending redirections onto the hung up - * device. It ensures future writes will error and it does the needed - * line discipline hangup and signal delivery. The tty object itself - * remains intact. - * - * Locking: - * BTM - * redirect lock for undoing redirection - * file list lock for manipulating list of ttys - * tty_ldisc_lock from called functions - * termios_mutex resetting termios data - * tasklist_lock to walk task list for hangup event - * ->siglock to protect ->signal/->sighand - */ -void __tty_hangup(struct tty_struct *tty) -{ - struct file *cons_filp = NULL; - struct file *filp, *f = NULL; - struct task_struct *p; - struct tty_file_private *priv; - int closecount = 0, n; - unsigned long flags; - int refs = 0; - - if (!tty) - return; - - - spin_lock(&redirect_lock); - if (redirect && file_tty(redirect) == tty) { - f = redirect; - redirect = NULL; - } - spin_unlock(&redirect_lock); - - tty_lock(); - - /* inuse_filps is protected by the single tty lock, - this really needs to change if we want to flush the - workqueue with the lock held */ - check_tty_count(tty, "tty_hangup"); - - spin_lock(&tty_files_lock); - /* This breaks for file handles being sent over AF_UNIX sockets ? */ - list_for_each_entry(priv, &tty->tty_files, list) { - filp = priv->file; - if (filp->f_op->write == redirected_tty_write) - cons_filp = filp; - if (filp->f_op->write != tty_write) - continue; - closecount++; - __tty_fasync(-1, filp, 0); /* can't block */ - filp->f_op = &hung_up_tty_fops; - } - spin_unlock(&tty_files_lock); - - tty_ldisc_hangup(tty); - - read_lock(&tasklist_lock); - if (tty->session) { - do_each_pid_task(tty->session, PIDTYPE_SID, p) { - spin_lock_irq(&p->sighand->siglock); - if (p->signal->tty == tty) { - p->signal->tty = NULL; - /* We defer the dereferences outside fo - the tasklist lock */ - refs++; - } - if (!p->signal->leader) { - spin_unlock_irq(&p->sighand->siglock); - continue; - } - __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); - __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); - put_pid(p->signal->tty_old_pgrp); /* A noop */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->pgrp) - p->signal->tty_old_pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - spin_unlock_irq(&p->sighand->siglock); - } while_each_pid_task(tty->session, PIDTYPE_SID, p); - } - read_unlock(&tasklist_lock); - - spin_lock_irqsave(&tty->ctrl_lock, flags); - clear_bit(TTY_THROTTLED, &tty->flags); - clear_bit(TTY_PUSH, &tty->flags); - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - put_pid(tty->session); - put_pid(tty->pgrp); - tty->session = NULL; - tty->pgrp = NULL; - tty->ctrl_status = 0; - set_bit(TTY_HUPPED, &tty->flags); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - - /* Account for the p->signal references we killed */ - while (refs--) - tty_kref_put(tty); - - /* - * If one of the devices matches a console pointer, we - * cannot just call hangup() because that will cause - * tty->count and state->count to go out of sync. - * So we just call close() the right number of times. - */ - if (cons_filp) { - if (tty->ops->close) - for (n = 0; n < closecount; n++) - tty->ops->close(tty, cons_filp); - } else if (tty->ops->hangup) - (tty->ops->hangup)(tty); - /* - * We don't want to have driver/ldisc interactions beyond - * the ones we did here. The driver layer expects no - * calls after ->hangup() from the ldisc side. However we - * can't yet guarantee all that. - */ - set_bit(TTY_HUPPED, &tty->flags); - tty_ldisc_enable(tty); - - tty_unlock(); - - if (f) - fput(f); -} - -static void do_tty_hangup(struct work_struct *work) -{ - struct tty_struct *tty = - container_of(work, struct tty_struct, hangup_work); - - __tty_hangup(tty); -} - -/** - * tty_hangup - trigger a hangup event - * @tty: tty to hangup - * - * A carrier loss (virtual or otherwise) has occurred on this like - * schedule a hangup sequence to run after this event. - */ - -void tty_hangup(struct tty_struct *tty) -{ -#ifdef TTY_DEBUG_HANGUP - char buf[64]; - printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf)); -#endif - schedule_work(&tty->hangup_work); -} - -EXPORT_SYMBOL(tty_hangup); - -/** - * tty_vhangup - process vhangup - * @tty: tty to hangup - * - * The user has asked via system call for the terminal to be hung up. - * We do this synchronously so that when the syscall returns the process - * is complete. That guarantee is necessary for security reasons. - */ - -void tty_vhangup(struct tty_struct *tty) -{ -#ifdef TTY_DEBUG_HANGUP - char buf[64]; - - printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf)); -#endif - __tty_hangup(tty); -} - -EXPORT_SYMBOL(tty_vhangup); - - -/** - * tty_vhangup_self - process vhangup for own ctty - * - * Perform a vhangup on the current controlling tty - */ - -void tty_vhangup_self(void) -{ - struct tty_struct *tty; - - tty = get_current_tty(); - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } -} - -/** - * tty_hung_up_p - was tty hung up - * @filp: file pointer of tty - * - * Return true if the tty has been subject to a vhangup or a carrier - * loss - */ - -int tty_hung_up_p(struct file *filp) -{ - return (filp->f_op == &hung_up_tty_fops); -} - -EXPORT_SYMBOL(tty_hung_up_p); - -static void session_clear_tty(struct pid *session) -{ - struct task_struct *p; - do_each_pid_task(session, PIDTYPE_SID, p) { - proc_clear_tty(p); - } while_each_pid_task(session, PIDTYPE_SID, p); -} - -/** - * disassociate_ctty - disconnect controlling tty - * @on_exit: true if exiting so need to "hang up" the session - * - * This function is typically called only by the session leader, when - * it wants to disassociate itself from its controlling tty. - * - * It performs the following functions: - * (1) Sends a SIGHUP and SIGCONT to the foreground process group - * (2) Clears the tty from being controlling the session - * (3) Clears the controlling tty for all processes in the - * session group. - * - * The argument on_exit is set to 1 if called when a process is - * exiting; it is 0 if called by the ioctl TIOCNOTTY. - * - * Locking: - * BTM is taken for hysterical raisins, and held when - * called from no_tty(). - * tty_mutex is taken to protect tty - * ->siglock is taken to protect ->signal/->sighand - * tasklist_lock is taken to walk process list for sessions - * ->siglock is taken to protect ->signal/->sighand - */ - -void disassociate_ctty(int on_exit) -{ - struct tty_struct *tty; - struct pid *tty_pgrp = NULL; - - if (!current->signal->leader) - return; - - tty = get_current_tty(); - if (tty) { - tty_pgrp = get_pid(tty->pgrp); - if (on_exit) { - if (tty->driver->type != TTY_DRIVER_TYPE_PTY) - tty_vhangup(tty); - } - tty_kref_put(tty); - } else if (on_exit) { - struct pid *old_pgrp; - spin_lock_irq(¤t->sighand->siglock); - old_pgrp = current->signal->tty_old_pgrp; - current->signal->tty_old_pgrp = NULL; - spin_unlock_irq(¤t->sighand->siglock); - if (old_pgrp) { - kill_pgrp(old_pgrp, SIGHUP, on_exit); - kill_pgrp(old_pgrp, SIGCONT, on_exit); - put_pid(old_pgrp); - } - return; - } - if (tty_pgrp) { - kill_pgrp(tty_pgrp, SIGHUP, on_exit); - if (!on_exit) - kill_pgrp(tty_pgrp, SIGCONT, on_exit); - put_pid(tty_pgrp); - } - - spin_lock_irq(¤t->sighand->siglock); - put_pid(current->signal->tty_old_pgrp); - current->signal->tty_old_pgrp = NULL; - spin_unlock_irq(¤t->sighand->siglock); - - tty = get_current_tty(); - if (tty) { - unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); - put_pid(tty->session); - put_pid(tty->pgrp); - tty->session = NULL; - tty->pgrp = NULL; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - tty_kref_put(tty); - } else { -#ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "error attempted to write to tty [0x%p]" - " = NULL", tty); -#endif - } - - /* Now clear signal->tty under the lock */ - read_lock(&tasklist_lock); - session_clear_tty(task_session(current)); - read_unlock(&tasklist_lock); -} - -/** - * - * no_tty - Ensure the current process does not have a controlling tty - */ -void no_tty(void) -{ - struct task_struct *tsk = current; - tty_lock(); - disassociate_ctty(0); - tty_unlock(); - proc_clear_tty(tsk); -} - - -/** - * stop_tty - propagate flow control - * @tty: tty to stop - * - * Perform flow control to the driver. For PTY/TTY pairs we - * must also propagate the TIOCKPKT status. May be called - * on an already stopped device and will not re-call the driver - * method. - * - * This functionality is used by both the line disciplines for - * halting incoming flow and by the driver. It may therefore be - * called from any context, may be under the tty atomic_write_lock - * but not always. - * - * Locking: - * Uses the tty control lock internally - */ - -void stop_tty(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->stopped) { - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - return; - } - tty->stopped = 1; - if (tty->link && tty->link->packet) { - tty->ctrl_status &= ~TIOCPKT_START; - tty->ctrl_status |= TIOCPKT_STOP; - wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - if (tty->ops->stop) - (tty->ops->stop)(tty); -} - -EXPORT_SYMBOL(stop_tty); - -/** - * start_tty - propagate flow control - * @tty: tty to start - * - * Start a tty that has been stopped if at all possible. Perform - * any necessary wakeups and propagate the TIOCPKT status. If this - * is the tty was previous stopped and is being started then the - * driver start method is invoked and the line discipline woken. - * - * Locking: - * ctrl_lock - */ - -void start_tty(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (!tty->stopped || tty->flow_stopped) { - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - return; - } - tty->stopped = 0; - if (tty->link && tty->link->packet) { - tty->ctrl_status &= ~TIOCPKT_STOP; - tty->ctrl_status |= TIOCPKT_START; - wake_up_interruptible_poll(&tty->link->read_wait, POLLIN); - } - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - if (tty->ops->start) - (tty->ops->start)(tty); - /* If we have a running line discipline it may need kicking */ - tty_wakeup(tty); -} - -EXPORT_SYMBOL(start_tty); - -/** - * tty_read - read method for tty device files - * @file: pointer to tty file - * @buf: user buffer - * @count: size of user buffer - * @ppos: unused - * - * Perform the read system call function on this terminal device. Checks - * for hung up devices before calling the line discipline method. - * - * Locking: - * Locks the line discipline internally while needed. Multiple - * read calls may be outstanding in parallel. - */ - -static ssize_t tty_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - int i; - struct inode *inode = file->f_path.dentry->d_inode; - struct tty_struct *tty = file_tty(file); - struct tty_ldisc *ld; - - if (tty_paranoia_check(tty, inode, "tty_read")) - return -EIO; - if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) - return -EIO; - - /* We want to wait for the line discipline to sort out in this - situation */ - ld = tty_ldisc_ref_wait(tty); - if (ld->ops->read) - i = (ld->ops->read)(tty, file, buf, count); - else - i = -EIO; - tty_ldisc_deref(ld); - if (i > 0) - inode->i_atime = current_fs_time(inode->i_sb); - return i; -} - -void tty_write_unlock(struct tty_struct *tty) -{ - mutex_unlock(&tty->atomic_write_lock); - wake_up_interruptible_poll(&tty->write_wait, POLLOUT); -} - -int tty_write_lock(struct tty_struct *tty, int ndelay) -{ - if (!mutex_trylock(&tty->atomic_write_lock)) { - if (ndelay) - return -EAGAIN; - if (mutex_lock_interruptible(&tty->atomic_write_lock)) - return -ERESTARTSYS; - } - return 0; -} - -/* - * Split writes up in sane blocksizes to avoid - * denial-of-service type attacks - */ -static inline ssize_t do_tty_write( - ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t), - struct tty_struct *tty, - struct file *file, - const char __user *buf, - size_t count) -{ - ssize_t ret, written = 0; - unsigned int chunk; - - ret = tty_write_lock(tty, file->f_flags & O_NDELAY); - if (ret < 0) - return ret; - - /* - * We chunk up writes into a temporary buffer. This - * simplifies low-level drivers immensely, since they - * don't have locking issues and user mode accesses. - * - * But if TTY_NO_WRITE_SPLIT is set, we should use a - * big chunk-size.. - * - * The default chunk-size is 2kB, because the NTTY - * layer has problems with bigger chunks. It will - * claim to be able to handle more characters than - * it actually does. - * - * FIXME: This can probably go away now except that 64K chunks - * are too likely to fail unless switched to vmalloc... - */ - chunk = 2048; - if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags)) - chunk = 65536; - if (count < chunk) - chunk = count; - - /* write_buf/write_cnt is protected by the atomic_write_lock mutex */ - if (tty->write_cnt < chunk) { - unsigned char *buf_chunk; - - if (chunk < 1024) - chunk = 1024; - - buf_chunk = kmalloc(chunk, GFP_KERNEL); - if (!buf_chunk) { - ret = -ENOMEM; - goto out; - } - kfree(tty->write_buf); - tty->write_cnt = chunk; - tty->write_buf = buf_chunk; - } - - /* Do the write .. */ - for (;;) { - size_t size = count; - if (size > chunk) - size = chunk; - ret = -EFAULT; - if (copy_from_user(tty->write_buf, buf, size)) - break; - ret = write(tty, file, tty->write_buf, size); - if (ret <= 0) - break; - written += ret; - buf += ret; - count -= ret; - if (!count) - break; - ret = -ERESTARTSYS; - if (signal_pending(current)) - break; - cond_resched(); - } - if (written) { - struct inode *inode = file->f_path.dentry->d_inode; - inode->i_mtime = current_fs_time(inode->i_sb); - ret = written; - } -out: - tty_write_unlock(tty); - return ret; -} - -/** - * tty_write_message - write a message to a certain tty, not just the console. - * @tty: the destination tty_struct - * @msg: the message to write - * - * This is used for messages that need to be redirected to a specific tty. - * We don't put it into the syslog queue right now maybe in the future if - * really needed. - * - * We must still hold the BTM and test the CLOSING flag for the moment. - */ - -void tty_write_message(struct tty_struct *tty, char *msg) -{ - if (tty) { - mutex_lock(&tty->atomic_write_lock); - tty_lock(); - if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) { - tty_unlock(); - tty->ops->write(tty, msg, strlen(msg)); - } else - tty_unlock(); - tty_write_unlock(tty); - } - return; -} - - -/** - * tty_write - write method for tty device file - * @file: tty file pointer - * @buf: user data to write - * @count: bytes to write - * @ppos: unused - * - * Write data to a tty device via the line discipline. - * - * Locking: - * Locks the line discipline as required - * Writes to the tty driver are serialized by the atomic_write_lock - * and are then processed in chunks to the device. The line discipline - * write method will not be invoked in parallel for each device. - */ - -static ssize_t tty_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct inode *inode = file->f_path.dentry->d_inode; - struct tty_struct *tty = file_tty(file); - struct tty_ldisc *ld; - ssize_t ret; - - if (tty_paranoia_check(tty, inode, "tty_write")) - return -EIO; - if (!tty || !tty->ops->write || - (test_bit(TTY_IO_ERROR, &tty->flags))) - return -EIO; - /* Short term debug to catch buggy drivers */ - if (tty->ops->write_room == NULL) - printk(KERN_ERR "tty driver %s lacks a write_room method.\n", - tty->driver->name); - ld = tty_ldisc_ref_wait(tty); - if (!ld->ops->write) - ret = -EIO; - else - ret = do_tty_write(ld->ops->write, tty, file, buf, count); - tty_ldisc_deref(ld); - return ret; -} - -ssize_t redirected_tty_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct file *p = NULL; - - spin_lock(&redirect_lock); - if (redirect) { - get_file(redirect); - p = redirect; - } - spin_unlock(&redirect_lock); - - if (p) { - ssize_t res; - res = vfs_write(p, buf, count, &p->f_pos); - fput(p); - return res; - } - return tty_write(file, buf, count, ppos); -} - -static char ptychar[] = "pqrstuvwxyzabcde"; - -/** - * pty_line_name - generate name for a pty - * @driver: the tty driver in use - * @index: the minor number - * @p: output buffer of at least 6 bytes - * - * Generate a name from a driver reference and write it to the output - * buffer. - * - * Locking: None - */ -static void pty_line_name(struct tty_driver *driver, int index, char *p) -{ - int i = index + driver->name_base; - /* ->name is initialized to "ttyp", but "tty" is expected */ - sprintf(p, "%s%c%x", - driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name, - ptychar[i >> 4 & 0xf], i & 0xf); -} - -/** - * tty_line_name - generate name for a tty - * @driver: the tty driver in use - * @index: the minor number - * @p: output buffer of at least 7 bytes - * - * Generate a name from a driver reference and write it to the output - * buffer. - * - * Locking: None - */ -static void tty_line_name(struct tty_driver *driver, int index, char *p) -{ - sprintf(p, "%s%d", driver->name, index + driver->name_base); -} - -/** - * tty_driver_lookup_tty() - find an existing tty, if any - * @driver: the driver for the tty - * @idx: the minor number - * - * Return the tty, if found or ERR_PTR() otherwise. - * - * Locking: tty_mutex must be held. If tty is found, the mutex must - * be held until the 'fast-open' is also done. Will change once we - * have refcounting in the driver and per driver locking - */ -static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, - struct inode *inode, int idx) -{ - struct tty_struct *tty; - - if (driver->ops->lookup) - return driver->ops->lookup(driver, inode, idx); - - tty = driver->ttys[idx]; - return tty; -} - -/** - * tty_init_termios - helper for termios setup - * @tty: the tty to set up - * - * Initialise the termios structures for this tty. Thus runs under - * the tty_mutex currently so we can be relaxed about ordering. - */ - -int tty_init_termios(struct tty_struct *tty) -{ - struct ktermios *tp; - int idx = tty->index; - - tp = tty->driver->termios[idx]; - if (tp == NULL) { - tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); - if (tp == NULL) - return -ENOMEM; - memcpy(tp, &tty->driver->init_termios, - sizeof(struct ktermios)); - tty->driver->termios[idx] = tp; - } - tty->termios = tp; - tty->termios_locked = tp + 1; - - /* Compatibility until drivers always set this */ - tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); - tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); - return 0; -} -EXPORT_SYMBOL_GPL(tty_init_termios); - -/** - * tty_driver_install_tty() - install a tty entry in the driver - * @driver: the driver for the tty - * @tty: the tty - * - * Install a tty object into the driver tables. The tty->index field - * will be set by the time this is called. This method is responsible - * for ensuring any need additional structures are allocated and - * configured. - * - * Locking: tty_mutex for now - */ -static int tty_driver_install_tty(struct tty_driver *driver, - struct tty_struct *tty) -{ - int idx = tty->index; - int ret; - - if (driver->ops->install) { - ret = driver->ops->install(driver, tty); - return ret; - } - - if (tty_init_termios(tty) == 0) { - tty_driver_kref_get(driver); - tty->count++; - driver->ttys[idx] = tty; - return 0; - } - return -ENOMEM; -} - -/** - * tty_driver_remove_tty() - remove a tty from the driver tables - * @driver: the driver for the tty - * @idx: the minor number - * - * Remvoe a tty object from the driver tables. The tty->index field - * will be set by the time this is called. - * - * Locking: tty_mutex for now - */ -static void tty_driver_remove_tty(struct tty_driver *driver, - struct tty_struct *tty) -{ - if (driver->ops->remove) - driver->ops->remove(driver, tty); - else - driver->ttys[tty->index] = NULL; -} - -/* - * tty_reopen() - fast re-open of an open tty - * @tty - the tty to open - * - * Return 0 on success, -errno on error. - * - * Locking: tty_mutex must be held from the time the tty was found - * till this open completes. - */ -static int tty_reopen(struct tty_struct *tty) -{ - struct tty_driver *driver = tty->driver; - - if (test_bit(TTY_CLOSING, &tty->flags)) - return -EIO; - - if (driver->type == TTY_DRIVER_TYPE_PTY && - driver->subtype == PTY_TYPE_MASTER) { - /* - * special case for PTY masters: only one open permitted, - * and the slave side open count is incremented as well. - */ - if (tty->count) - return -EIO; - - tty->link->count++; - } - tty->count++; - tty->driver = driver; /* N.B. why do this every time?? */ - - mutex_lock(&tty->ldisc_mutex); - WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); - mutex_unlock(&tty->ldisc_mutex); - - return 0; -} - -/** - * tty_init_dev - initialise a tty device - * @driver: tty driver we are opening a device on - * @idx: device index - * @ret_tty: returned tty structure - * @first_ok: ok to open a new device (used by ptmx) - * - * Prepare a tty device. This may not be a "new" clean device but - * could also be an active device. The pty drivers require special - * handling because of this. - * - * Locking: - * The function is called under the tty_mutex, which - * protects us from the tty struct or driver itself going away. - * - * On exit the tty device has the line discipline attached and - * a reference count of 1. If a pair was created for pty/tty use - * and the other was a pty master then it too has a reference count of 1. - * - * WSH 06/09/97: Rewritten to remove races and properly clean up after a - * failed open. The new code protects the open with a mutex, so it's - * really quite straightforward. The mutex locking can probably be - * relaxed for the (most common) case of reopening a tty. - */ - -struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, - int first_ok) -{ - struct tty_struct *tty; - int retval; - - /* Check if pty master is being opened multiple times */ - if (driver->subtype == PTY_TYPE_MASTER && - (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) { - return ERR_PTR(-EIO); - } - - /* - * First time open is complex, especially for PTY devices. - * This code guarantees that either everything succeeds and the - * TTY is ready for operation, or else the table slots are vacated - * and the allocated memory released. (Except that the termios - * and locked termios may be retained.) - */ - - if (!try_module_get(driver->owner)) - return ERR_PTR(-ENODEV); - - tty = alloc_tty_struct(); - if (!tty) - goto fail_no_mem; - initialize_tty_struct(tty, driver, idx); - - retval = tty_driver_install_tty(driver, tty); - if (retval < 0) { - free_tty_struct(tty); - module_put(driver->owner); - return ERR_PTR(retval); - } - - /* - * Structures all installed ... call the ldisc open routines. - * If we fail here just call release_tty to clean up. No need - * to decrement the use counts, as release_tty doesn't care. - */ - retval = tty_ldisc_setup(tty, tty->link); - if (retval) - goto release_mem_out; - return tty; - -fail_no_mem: - module_put(driver->owner); - return ERR_PTR(-ENOMEM); - - /* call the tty release_tty routine to clean out this slot */ -release_mem_out: - if (printk_ratelimit()) - printk(KERN_INFO "tty_init_dev: ldisc open failed, " - "clearing slot %d\n", idx); - release_tty(tty, idx); - return ERR_PTR(retval); -} - -void tty_free_termios(struct tty_struct *tty) -{ - struct ktermios *tp; - int idx = tty->index; - /* Kill this flag and push into drivers for locking etc */ - if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - /* FIXME: Locking on ->termios array */ - tp = tty->termios; - tty->driver->termios[idx] = NULL; - kfree(tp); - } -} -EXPORT_SYMBOL(tty_free_termios); - -void tty_shutdown(struct tty_struct *tty) -{ - tty_driver_remove_tty(tty->driver, tty); - tty_free_termios(tty); -} -EXPORT_SYMBOL(tty_shutdown); - -/** - * release_one_tty - release tty structure memory - * @kref: kref of tty we are obliterating - * - * Releases memory associated with a tty structure, and clears out the - * driver table slots. This function is called when a device is no longer - * in use. It also gets called when setup of a device fails. - * - * Locking: - * tty_mutex - sometimes only - * takes the file list lock internally when working on the list - * of ttys that the driver keeps. - * - * This method gets called from a work queue so that the driver private - * cleanup ops can sleep (needed for USB at least) - */ -static void release_one_tty(struct work_struct *work) -{ - struct tty_struct *tty = - container_of(work, struct tty_struct, hangup_work); - struct tty_driver *driver = tty->driver; - - if (tty->ops->cleanup) - tty->ops->cleanup(tty); - - tty->magic = 0; - tty_driver_kref_put(driver); - module_put(driver->owner); - - spin_lock(&tty_files_lock); - list_del_init(&tty->tty_files); - spin_unlock(&tty_files_lock); - - put_pid(tty->pgrp); - put_pid(tty->session); - free_tty_struct(tty); -} - -static void queue_release_one_tty(struct kref *kref) -{ - struct tty_struct *tty = container_of(kref, struct tty_struct, kref); - - if (tty->ops->shutdown) - tty->ops->shutdown(tty); - else - tty_shutdown(tty); - - /* The hangup queue is now free so we can reuse it rather than - waste a chunk of memory for each port */ - INIT_WORK(&tty->hangup_work, release_one_tty); - schedule_work(&tty->hangup_work); -} - -/** - * tty_kref_put - release a tty kref - * @tty: tty device - * - * Release a reference to a tty device and if need be let the kref - * layer destruct the object for us - */ - -void tty_kref_put(struct tty_struct *tty) -{ - if (tty) - kref_put(&tty->kref, queue_release_one_tty); -} -EXPORT_SYMBOL(tty_kref_put); - -/** - * release_tty - release tty structure memory - * - * Release both @tty and a possible linked partner (think pty pair), - * and decrement the refcount of the backing module. - * - * Locking: - * tty_mutex - sometimes only - * takes the file list lock internally when working on the list - * of ttys that the driver keeps. - * FIXME: should we require tty_mutex is held here ?? - * - */ -static void release_tty(struct tty_struct *tty, int idx) -{ - /* This should always be true but check for the moment */ - WARN_ON(tty->index != idx); - - if (tty->link) - tty_kref_put(tty->link); - tty_kref_put(tty); -} - -/** - * tty_release - vfs callback for close - * @inode: inode of tty - * @filp: file pointer for handle to tty - * - * Called the last time each file handle is closed that references - * this tty. There may however be several such references. - * - * Locking: - * Takes bkl. See tty_release_dev - * - * Even releasing the tty structures is a tricky business.. We have - * to be very careful that the structures are all released at the - * same time, as interrupts might otherwise get the wrong pointers. - * - * WSH 09/09/97: rewritten to avoid some nasty race conditions that could - * lead to double frees or releasing memory still in use. - */ - -int tty_release(struct inode *inode, struct file *filp) -{ - struct tty_struct *tty = file_tty(filp); - struct tty_struct *o_tty; - int pty_master, tty_closing, o_tty_closing, do_sleep; - int devpts; - int idx; - char buf[64]; - - if (tty_paranoia_check(tty, inode, "tty_release_dev")) - return 0; - - tty_lock(); - check_tty_count(tty, "tty_release_dev"); - - __tty_fasync(-1, filp, 0); - - idx = tty->index; - pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER); - devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; - o_tty = tty->link; - -#ifdef TTY_PARANOIA_CHECK - if (idx < 0 || idx >= tty->driver->num) { - printk(KERN_DEBUG "tty_release_dev: bad idx when trying to " - "free (%s)\n", tty->name); - tty_unlock(); - return 0; - } - if (!devpts) { - if (tty != tty->driver->ttys[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty " - "for (%s)\n", idx, tty->name); - return 0; - } - if (tty->termios != tty->driver->termios[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios " - "for (%s)\n", - idx, tty->name); - return 0; - } - } -#endif - -#ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...", - tty_name(tty, buf), tty->count); -#endif - -#ifdef TTY_PARANOIA_CHECK - if (tty->driver->other && - !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { - if (o_tty != tty->driver->other->ttys[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: other->table[%d] " - "not o_tty for (%s)\n", - idx, tty->name); - return 0 ; - } - if (o_tty->termios != tty->driver->other->termios[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: other->termios[%d] " - "not o_termios for (%s)\n", - idx, tty->name); - return 0; - } - if (o_tty->link != tty) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n"); - return 0; - } - } -#endif - if (tty->ops->close) - tty->ops->close(tty, filp); - - tty_unlock(); - /* - * Sanity check: if tty->count is going to zero, there shouldn't be - * any waiters on tty->read_wait or tty->write_wait. We test the - * wait queues and kick everyone out _before_ actually starting to - * close. This ensures that we won't block while releasing the tty - * structure. - * - * The test for the o_tty closing is necessary, since the master and - * slave sides may close in any order. If the slave side closes out - * first, its count will be one, since the master side holds an open. - * Thus this test wouldn't be triggered at the time the slave closes, - * so we do it now. - * - * Note that it's possible for the tty to be opened again while we're - * flushing out waiters. By recalculating the closing flags before - * each iteration we avoid any problems. - */ - while (1) { - /* Guard against races with tty->count changes elsewhere and - opens on /dev/tty */ - - mutex_lock(&tty_mutex); - tty_lock(); - tty_closing = tty->count <= 1; - o_tty_closing = o_tty && - (o_tty->count <= (pty_master ? 1 : 0)); - do_sleep = 0; - - if (tty_closing) { - if (waitqueue_active(&tty->read_wait)) { - wake_up_poll(&tty->read_wait, POLLIN); - do_sleep++; - } - if (waitqueue_active(&tty->write_wait)) { - wake_up_poll(&tty->write_wait, POLLOUT); - do_sleep++; - } - } - if (o_tty_closing) { - if (waitqueue_active(&o_tty->read_wait)) { - wake_up_poll(&o_tty->read_wait, POLLIN); - do_sleep++; - } - if (waitqueue_active(&o_tty->write_wait)) { - wake_up_poll(&o_tty->write_wait, POLLOUT); - do_sleep++; - } - } - if (!do_sleep) - break; - - printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue " - "active!\n", tty_name(tty, buf)); - tty_unlock(); - mutex_unlock(&tty_mutex); - schedule(); - } - - /* - * The closing flags are now consistent with the open counts on - * both sides, and we've completed the last operation that could - * block, so it's safe to proceed with closing. - */ - if (pty_master) { - if (--o_tty->count < 0) { - printk(KERN_WARNING "tty_release_dev: bad pty slave count " - "(%d) for %s\n", - o_tty->count, tty_name(o_tty, buf)); - o_tty->count = 0; - } - } - if (--tty->count < 0) { - printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n", - tty->count, tty_name(tty, buf)); - tty->count = 0; - } - - /* - * We've decremented tty->count, so we need to remove this file - * descriptor off the tty->tty_files list; this serves two - * purposes: - * - check_tty_count sees the correct number of file descriptors - * associated with this tty. - * - do_tty_hangup no longer sees this file descriptor as - * something that needs to be handled for hangups. - */ - tty_del_file(filp); - - /* - * Perform some housekeeping before deciding whether to return. - * - * Set the TTY_CLOSING flag if this was the last open. In the - * case of a pty we may have to wait around for the other side - * to close, and TTY_CLOSING makes sure we can't be reopened. - */ - if (tty_closing) - set_bit(TTY_CLOSING, &tty->flags); - if (o_tty_closing) - set_bit(TTY_CLOSING, &o_tty->flags); - - /* - * If _either_ side is closing, make sure there aren't any - * processes that still think tty or o_tty is their controlling - * tty. - */ - if (tty_closing || o_tty_closing) { - read_lock(&tasklist_lock); - session_clear_tty(tty->session); - if (o_tty) - session_clear_tty(o_tty->session); - read_unlock(&tasklist_lock); - } - - mutex_unlock(&tty_mutex); - - /* check whether both sides are closing ... */ - if (!tty_closing || (o_tty && !o_tty_closing)) { - tty_unlock(); - return 0; - } - -#ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "freeing tty structure..."); -#endif - /* - * Ask the line discipline code to release its structures - */ - tty_ldisc_release(tty, o_tty); - /* - * The release_tty function takes care of the details of clearing - * the slots and preserving the termios structure. - */ - release_tty(tty, idx); - - /* Make this pty number available for reallocation */ - if (devpts) - devpts_kill_index(inode, idx); - tty_unlock(); - return 0; -} - -/** - * tty_open - open a tty device - * @inode: inode of device file - * @filp: file pointer to tty - * - * tty_open and tty_release keep up the tty count that contains the - * number of opens done on a tty. We cannot use the inode-count, as - * different inodes might point to the same tty. - * - * Open-counting is needed for pty masters, as well as for keeping - * track of serial lines: DTR is dropped when the last close happens. - * (This is not done solely through tty->count, now. - Ted 1/27/92) - * - * The termios state of a pty is reset on first open so that - * settings don't persist across reuse. - * - * Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work. - * tty->count should protect the rest. - * ->siglock protects ->signal/->sighand - */ - -static int tty_open(struct inode *inode, struct file *filp) -{ - struct tty_struct *tty = NULL; - int noctty, retval; - struct tty_driver *driver; - int index; - dev_t device = inode->i_rdev; - unsigned saved_flags = filp->f_flags; - - nonseekable_open(inode, filp); - -retry_open: - noctty = filp->f_flags & O_NOCTTY; - index = -1; - retval = 0; - - mutex_lock(&tty_mutex); - tty_lock(); - - if (device == MKDEV(TTYAUX_MAJOR, 0)) { - tty = get_current_tty(); - if (!tty) { - tty_unlock(); - mutex_unlock(&tty_mutex); - return -ENXIO; - } - driver = tty_driver_kref_get(tty->driver); - index = tty->index; - filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ - /* noctty = 1; */ - /* FIXME: Should we take a driver reference ? */ - tty_kref_put(tty); - goto got_driver; - } -#ifdef CONFIG_VT - if (device == MKDEV(TTY_MAJOR, 0)) { - extern struct tty_driver *console_driver; - driver = tty_driver_kref_get(console_driver); - index = fg_console; - noctty = 1; - goto got_driver; - } -#endif - if (device == MKDEV(TTYAUX_MAJOR, 1)) { - struct tty_driver *console_driver = console_device(&index); - if (console_driver) { - driver = tty_driver_kref_get(console_driver); - if (driver) { - /* Don't let /dev/console block */ - filp->f_flags |= O_NONBLOCK; - noctty = 1; - goto got_driver; - } - } - tty_unlock(); - mutex_unlock(&tty_mutex); - return -ENODEV; - } - - driver = get_tty_driver(device, &index); - if (!driver) { - tty_unlock(); - mutex_unlock(&tty_mutex); - return -ENODEV; - } -got_driver: - if (!tty) { - /* check whether we're reopening an existing tty */ - tty = tty_driver_lookup_tty(driver, inode, index); - - if (IS_ERR(tty)) { - tty_unlock(); - mutex_unlock(&tty_mutex); - return PTR_ERR(tty); - } - } - - if (tty) { - retval = tty_reopen(tty); - if (retval) - tty = ERR_PTR(retval); - } else - tty = tty_init_dev(driver, index, 0); - - mutex_unlock(&tty_mutex); - tty_driver_kref_put(driver); - if (IS_ERR(tty)) { - tty_unlock(); - return PTR_ERR(tty); - } - - retval = tty_add_file(tty, filp); - if (retval) { - tty_unlock(); - return retval; - } - - check_tty_count(tty, "tty_open"); - if (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER) - noctty = 1; -#ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "opening %s...", tty->name); -#endif - if (!retval) { - if (tty->ops->open) - retval = tty->ops->open(tty, filp); - else - retval = -ENODEV; - } - filp->f_flags = saved_flags; - - if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && - !capable(CAP_SYS_ADMIN)) - retval = -EBUSY; - - if (retval) { -#ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "error %d in opening %s...", retval, - tty->name); -#endif - tty_unlock(); /* need to call tty_release without BTM */ - tty_release(inode, filp); - if (retval != -ERESTARTSYS) - return retval; - - if (signal_pending(current)) - return retval; - - schedule(); - /* - * Need to reset f_op in case a hangup happened. - */ - tty_lock(); - if (filp->f_op == &hung_up_tty_fops) - filp->f_op = &tty_fops; - tty_unlock(); - goto retry_open; - } - tty_unlock(); - - - mutex_lock(&tty_mutex); - tty_lock(); - spin_lock_irq(¤t->sighand->siglock); - if (!noctty && - current->signal->leader && - !current->signal->tty && - tty->session == NULL) - __proc_set_tty(current, tty); - spin_unlock_irq(¤t->sighand->siglock); - tty_unlock(); - mutex_unlock(&tty_mutex); - return 0; -} - - - -/** - * tty_poll - check tty status - * @filp: file being polled - * @wait: poll wait structures to update - * - * Call the line discipline polling method to obtain the poll - * status of the device. - * - * Locking: locks called line discipline but ldisc poll method - * may be re-entered freely by other callers. - */ - -static unsigned int tty_poll(struct file *filp, poll_table *wait) -{ - struct tty_struct *tty = file_tty(filp); - struct tty_ldisc *ld; - int ret = 0; - - if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll")) - return 0; - - ld = tty_ldisc_ref_wait(tty); - if (ld->ops->poll) - ret = (ld->ops->poll)(tty, filp, wait); - tty_ldisc_deref(ld); - return ret; -} - -static int __tty_fasync(int fd, struct file *filp, int on) -{ - struct tty_struct *tty = file_tty(filp); - unsigned long flags; - int retval = 0; - - if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync")) - goto out; - - retval = fasync_helper(fd, filp, on, &tty->fasync); - if (retval <= 0) - goto out; - - if (on) { - enum pid_type type; - struct pid *pid; - if (!waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = 1; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (tty->pgrp) { - pid = tty->pgrp; - type = PIDTYPE_PGID; - } else { - pid = task_pid(current); - type = PIDTYPE_PID; - } - get_pid(pid); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - retval = __f_setown(filp, pid, type, 0); - put_pid(pid); - if (retval) - goto out; - } else { - if (!tty->fasync && !waitqueue_active(&tty->read_wait)) - tty->minimum_to_wake = N_TTY_BUF_SIZE; - } - retval = 0; -out: - return retval; -} - -static int tty_fasync(int fd, struct file *filp, int on) -{ - int retval; - tty_lock(); - retval = __tty_fasync(fd, filp, on); - tty_unlock(); - return retval; -} - -/** - * tiocsti - fake input character - * @tty: tty to fake input into - * @p: pointer to character - * - * Fake input to a tty device. Does the necessary locking and - * input management. - * - * FIXME: does not honour flow control ?? - * - * Locking: - * Called functions take tty_ldisc_lock - * current->signal->tty check is safe without locks - * - * FIXME: may race normal receive processing - */ - -static int tiocsti(struct tty_struct *tty, char __user *p) -{ - char ch, mbz = 0; - struct tty_ldisc *ld; - - if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN)) - return -EPERM; - if (get_user(ch, p)) - return -EFAULT; - tty_audit_tiocsti(tty, ch); - ld = tty_ldisc_ref_wait(tty); - ld->ops->receive_buf(tty, &ch, &mbz, 1); - tty_ldisc_deref(ld); - return 0; -} - -/** - * tiocgwinsz - implement window query ioctl - * @tty; tty - * @arg: user buffer for result - * - * Copies the kernel idea of the window size into the user buffer. - * - * Locking: tty->termios_mutex is taken to ensure the winsize data - * is consistent. - */ - -static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) -{ - int err; - - mutex_lock(&tty->termios_mutex); - err = copy_to_user(arg, &tty->winsize, sizeof(*arg)); - mutex_unlock(&tty->termios_mutex); - - return err ? -EFAULT: 0; -} - -/** - * tty_do_resize - resize event - * @tty: tty being resized - * @rows: rows (character) - * @cols: cols (character) - * - * Update the termios variables and send the necessary signals to - * peform a terminal resize correctly - */ - -int tty_do_resize(struct tty_struct *tty, struct winsize *ws) -{ - struct pid *pgrp; - unsigned long flags; - - /* Lock the tty */ - mutex_lock(&tty->termios_mutex); - if (!memcmp(ws, &tty->winsize, sizeof(*ws))) - goto done; - /* Get the PID values and reference them so we can - avoid holding the tty ctrl lock while sending signals */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - - if (pgrp) - kill_pgrp(pgrp, SIGWINCH, 1); - put_pid(pgrp); - - tty->winsize = *ws; -done: - mutex_unlock(&tty->termios_mutex); - return 0; -} - -/** - * tiocswinsz - implement window size set ioctl - * @tty; tty side of tty - * @arg: user buffer for result - * - * Copies the user idea of the window size to the kernel. Traditionally - * this is just advisory information but for the Linux console it - * actually has driver level meaning and triggers a VC resize. - * - * Locking: - * Driver dependant. The default do_resize method takes the - * tty termios mutex and ctrl_lock. The console takes its own lock - * then calls into the default method. - */ - -static int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg) -{ - struct winsize tmp_ws; - if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) - return -EFAULT; - - if (tty->ops->resize) - return tty->ops->resize(tty, &tmp_ws); - else - return tty_do_resize(tty, &tmp_ws); -} - -/** - * tioccons - allow admin to move logical console - * @file: the file to become console - * - * Allow the adminstrator to move the redirected console device - * - * Locking: uses redirect_lock to guard the redirect information - */ - -static int tioccons(struct file *file) -{ - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (file->f_op->write == redirected_tty_write) { - struct file *f; - spin_lock(&redirect_lock); - f = redirect; - redirect = NULL; - spin_unlock(&redirect_lock); - if (f) - fput(f); - return 0; - } - spin_lock(&redirect_lock); - if (redirect) { - spin_unlock(&redirect_lock); - return -EBUSY; - } - get_file(file); - redirect = file; - spin_unlock(&redirect_lock); - return 0; -} - -/** - * fionbio - non blocking ioctl - * @file: file to set blocking value - * @p: user parameter - * - * Historical tty interfaces had a blocking control ioctl before - * the generic functionality existed. This piece of history is preserved - * in the expected tty API of posix OS's. - * - * Locking: none, the open file handle ensures it won't go away. - */ - -static int fionbio(struct file *file, int __user *p) -{ - int nonblock; - - if (get_user(nonblock, p)) - return -EFAULT; - - spin_lock(&file->f_lock); - if (nonblock) - file->f_flags |= O_NONBLOCK; - else - file->f_flags &= ~O_NONBLOCK; - spin_unlock(&file->f_lock); - return 0; -} - -/** - * tiocsctty - set controlling tty - * @tty: tty structure - * @arg: user argument - * - * This ioctl is used to manage job control. It permits a session - * leader to set this tty as the controlling tty for the session. - * - * Locking: - * Takes tty_mutex() to protect tty instance - * Takes tasklist_lock internally to walk sessions - * Takes ->siglock() when updating signal->tty - */ - -static int tiocsctty(struct tty_struct *tty, int arg) -{ - int ret = 0; - if (current->signal->leader && (task_session(current) == tty->session)) - return ret; - - mutex_lock(&tty_mutex); - /* - * The process must be a session leader and - * not have a controlling tty already. - */ - if (!current->signal->leader || current->signal->tty) { - ret = -EPERM; - goto unlock; - } - - if (tty->session) { - /* - * This tty is already the controlling - * tty for another session group! - */ - if (arg == 1 && capable(CAP_SYS_ADMIN)) { - /* - * Steal it away - */ - read_lock(&tasklist_lock); - session_clear_tty(tty->session); - read_unlock(&tasklist_lock); - } else { - ret = -EPERM; - goto unlock; - } - } - proc_set_tty(current, tty); -unlock: - mutex_unlock(&tty_mutex); - return ret; -} - -/** - * tty_get_pgrp - return a ref counted pgrp pid - * @tty: tty to read - * - * Returns a refcounted instance of the pid struct for the process - * group controlling the tty. - */ - -struct pid *tty_get_pgrp(struct tty_struct *tty) -{ - unsigned long flags; - struct pid *pgrp; - - spin_lock_irqsave(&tty->ctrl_lock, flags); - pgrp = get_pid(tty->pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - - return pgrp; -} -EXPORT_SYMBOL_GPL(tty_get_pgrp); - -/** - * tiocgpgrp - get process group - * @tty: tty passed by user - * @real_tty: tty side of the tty pased by the user if a pty else the tty - * @p: returned pid - * - * Obtain the process group of the tty. If there is no process group - * return an error. - * - * Locking: none. Reference to current->signal->tty is safe. - */ - -static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) -{ - struct pid *pid; - int ret; - /* - * (tty == real_tty) is a cheap way of - * testing if the tty is NOT a master pty. - */ - if (tty == real_tty && current->signal->tty != real_tty) - return -ENOTTY; - pid = tty_get_pgrp(real_tty); - ret = put_user(pid_vnr(pid), p); - put_pid(pid); - return ret; -} - -/** - * tiocspgrp - attempt to set process group - * @tty: tty passed by user - * @real_tty: tty side device matching tty passed by user - * @p: pid pointer - * - * Set the process group of the tty to the session passed. Only - * permitted where the tty session is our session. - * - * Locking: RCU, ctrl lock - */ - -static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) -{ - struct pid *pgrp; - pid_t pgrp_nr; - int retval = tty_check_change(real_tty); - unsigned long flags; - - if (retval == -EIO) - return -ENOTTY; - if (retval) - return retval; - if (!current->signal->tty || - (current->signal->tty != real_tty) || - (real_tty->session != task_session(current))) - return -ENOTTY; - if (get_user(pgrp_nr, p)) - return -EFAULT; - if (pgrp_nr < 0) - return -EINVAL; - rcu_read_lock(); - pgrp = find_vpid(pgrp_nr); - retval = -ESRCH; - if (!pgrp) - goto out_unlock; - retval = -EPERM; - if (session_of_pgrp(pgrp) != task_session(current)) - goto out_unlock; - retval = 0; - spin_lock_irqsave(&tty->ctrl_lock, flags); - put_pid(real_tty->pgrp); - real_tty->pgrp = get_pid(pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); -out_unlock: - rcu_read_unlock(); - return retval; -} - -/** - * tiocgsid - get session id - * @tty: tty passed by user - * @real_tty: tty side of the tty pased by the user if a pty else the tty - * @p: pointer to returned session id - * - * Obtain the session id of the tty. If there is no session - * return an error. - * - * Locking: none. Reference to current->signal->tty is safe. - */ - -static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) -{ - /* - * (tty == real_tty) is a cheap way of - * testing if the tty is NOT a master pty. - */ - if (tty == real_tty && current->signal->tty != real_tty) - return -ENOTTY; - if (!real_tty->session) - return -ENOTTY; - return put_user(pid_vnr(real_tty->session), p); -} - -/** - * tiocsetd - set line discipline - * @tty: tty device - * @p: pointer to user data - * - * Set the line discipline according to user request. - * - * Locking: see tty_set_ldisc, this function is just a helper - */ - -static int tiocsetd(struct tty_struct *tty, int __user *p) -{ - int ldisc; - int ret; - - if (get_user(ldisc, p)) - return -EFAULT; - - ret = tty_set_ldisc(tty, ldisc); - - return ret; -} - -/** - * send_break - performed time break - * @tty: device to break on - * @duration: timeout in mS - * - * Perform a timed break on hardware that lacks its own driver level - * timed break functionality. - * - * Locking: - * atomic_write_lock serializes - * - */ - -static int send_break(struct tty_struct *tty, unsigned int duration) -{ - int retval; - - if (tty->ops->break_ctl == NULL) - return 0; - - if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK) - retval = tty->ops->break_ctl(tty, duration); - else { - /* Do the work ourselves */ - if (tty_write_lock(tty, 0) < 0) - return -EINTR; - retval = tty->ops->break_ctl(tty, -1); - if (retval) - goto out; - if (!signal_pending(current)) - msleep_interruptible(duration); - retval = tty->ops->break_ctl(tty, 0); -out: - tty_write_unlock(tty); - if (signal_pending(current)) - retval = -EINTR; - } - return retval; -} - -/** - * tty_tiocmget - get modem status - * @tty: tty device - * @file: user file pointer - * @p: pointer to result - * - * Obtain the modem status bits from the tty driver if the feature - * is supported. Return -EINVAL if it is not available. - * - * Locking: none (up to the driver) - */ - -static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p) -{ - int retval = -EINVAL; - - if (tty->ops->tiocmget) { - retval = tty->ops->tiocmget(tty, file); - - if (retval >= 0) - retval = put_user(retval, p); - } - return retval; -} - -/** - * tty_tiocmset - set modem status - * @tty: tty device - * @file: user file pointer - * @cmd: command - clear bits, set bits or set all - * @p: pointer to desired bits - * - * Set the modem status bits from the tty driver if the feature - * is supported. Return -EINVAL if it is not available. - * - * Locking: none (up to the driver) - */ - -static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd, - unsigned __user *p) -{ - int retval; - unsigned int set, clear, val; - - if (tty->ops->tiocmset == NULL) - return -EINVAL; - - retval = get_user(val, p); - if (retval) - return retval; - set = clear = 0; - switch (cmd) { - case TIOCMBIS: - set = val; - break; - case TIOCMBIC: - clear = val; - break; - case TIOCMSET: - set = val; - clear = ~val; - break; - } - set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; - clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; - 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 && - tty->driver->subtype == PTY_TYPE_MASTER) - tty = tty->link; - return tty; -} -EXPORT_SYMBOL(tty_pair_get_tty); - -struct tty_struct *tty_pair_get_pty(struct tty_struct *tty) -{ - if (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER) - return tty; - return tty->link; -} -EXPORT_SYMBOL(tty_pair_get_pty); - -/* - * Split this up, as gcc can choke on it otherwise.. - */ -long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct tty_struct *tty = file_tty(file); - struct tty_struct *real_tty; - void __user *p = (void __user *)arg; - int retval; - struct tty_ldisc *ld; - struct inode *inode = file->f_dentry->d_inode; - - if (tty_paranoia_check(tty, inode, "tty_ioctl")) - return -EINVAL; - - real_tty = tty_pair_get_tty(tty); - - /* - * Factor out some common prep work - */ - switch (cmd) { - case TIOCSETD: - case TIOCSBRK: - case TIOCCBRK: - case TCSBRK: - case TCSBRKP: - retval = tty_check_change(tty); - if (retval) - return retval; - if (cmd != TIOCCBRK) { - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - } - break; - } - - /* - * Now do the stuff. - */ - switch (cmd) { - case TIOCSTI: - return tiocsti(tty, p); - case TIOCGWINSZ: - return tiocgwinsz(real_tty, p); - case TIOCSWINSZ: - return tiocswinsz(real_tty, p); - case TIOCCONS: - return real_tty != tty ? -EINVAL : tioccons(file); - case FIONBIO: - return fionbio(file, p); - case TIOCEXCL: - set_bit(TTY_EXCLUSIVE, &tty->flags); - return 0; - case TIOCNXCL: - clear_bit(TTY_EXCLUSIVE, &tty->flags); - return 0; - case TIOCNOTTY: - if (current->signal->tty != tty) - return -ENOTTY; - no_tty(); - return 0; - case TIOCSCTTY: - return tiocsctty(tty, arg); - case TIOCGPGRP: - return tiocgpgrp(tty, real_tty, p); - case TIOCSPGRP: - return tiocspgrp(tty, real_tty, p); - case TIOCGSID: - return tiocgsid(tty, real_tty, p); - case TIOCGETD: - return put_user(tty->ldisc->ops->num, (int __user *)p); - case TIOCSETD: - return tiocsetd(tty, p); - /* - * Break handling - */ - case TIOCSBRK: /* Turn break on, unconditionally */ - if (tty->ops->break_ctl) - return tty->ops->break_ctl(tty, -1); - return 0; - case TIOCCBRK: /* Turn break off, unconditionally */ - if (tty->ops->break_ctl) - return tty->ops->break_ctl(tty, 0); - return 0; - case TCSBRK: /* SVID version: non-zero arg --> no break */ - /* non-zero arg means wait for all output data - * to be sent (performed above) but don't send break. - * This is used by the tcdrain() termios function. - */ - if (!arg) - return send_break(tty, 250); - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - return send_break(tty, arg ? arg*100 : 250); - - case TIOCMGET: - return tty_tiocmget(tty, file, p); - case TIOCMSET: - 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: - case TCIOFLUSH: - /* flush tty buffer and allow ldisc to process ioctl */ - tty_buffer_flush(tty); - break; - } - break; - } - if (tty->ops->ioctl) { - retval = (tty->ops->ioctl)(tty, file, cmd, arg); - if (retval != -ENOIOCTLCMD) - return retval; - } - ld = tty_ldisc_ref_wait(tty); - retval = -EINVAL; - if (ld->ops->ioctl) { - retval = ld->ops->ioctl(tty, file, cmd, arg); - if (retval == -ENOIOCTLCMD) - retval = -EINVAL; - } - tty_ldisc_deref(ld); - return retval; -} - -#ifdef CONFIG_COMPAT -static long tty_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct inode *inode = file->f_dentry->d_inode; - struct tty_struct *tty = file_tty(file); - struct tty_ldisc *ld; - int retval = -ENOIOCTLCMD; - - if (tty_paranoia_check(tty, inode, "tty_ioctl")) - return -EINVAL; - - if (tty->ops->compat_ioctl) { - retval = (tty->ops->compat_ioctl)(tty, file, cmd, arg); - if (retval != -ENOIOCTLCMD) - return retval; - } - - ld = tty_ldisc_ref_wait(tty); - if (ld->ops->compat_ioctl) - retval = ld->ops->compat_ioctl(tty, file, cmd, arg); - tty_ldisc_deref(ld); - - return retval; -} -#endif - -/* - * This implements the "Secure Attention Key" --- the idea is to - * prevent trojan horses by killing all processes associated with this - * tty when the user hits the "Secure Attention Key". Required for - * super-paranoid applications --- see the Orange Book for more details. - * - * This code could be nicer; ideally it should send a HUP, wait a few - * seconds, then send a INT, and then a KILL signal. But you then - * have to coordinate with the init process, since all processes associated - * with the current tty must be dead before the new getty is allowed - * to spawn. - * - * Now, if it would be correct ;-/ The current code has a nasty hole - - * it doesn't catch files in flight. We may send the descriptor to ourselves - * via AF_UNIX socket, close it and later fetch from socket. FIXME. - * - * Nasty bug: do_SAK is being called in interrupt context. This can - * deadlock. We punt it up to process context. AKPM - 16Mar2001 - */ -void __do_SAK(struct tty_struct *tty) -{ -#ifdef TTY_SOFT_SAK - tty_hangup(tty); -#else - struct task_struct *g, *p; - struct pid *session; - int i; - struct file *filp; - struct fdtable *fdt; - - if (!tty) - return; - session = tty->session; - - tty_ldisc_flush(tty); - - tty_driver_flush_buffer(tty); - - read_lock(&tasklist_lock); - /* Kill the entire session */ - do_each_pid_task(session, PIDTYPE_SID, p) { - printk(KERN_NOTICE "SAK: killed process %d" - " (%s): task_session(p)==tty->session\n", - task_pid_nr(p), p->comm); - send_sig(SIGKILL, p, 1); - } while_each_pid_task(session, PIDTYPE_SID, p); - /* Now kill any processes that happen to have the - * tty open. - */ - do_each_thread(g, p) { - if (p->signal->tty == tty) { - printk(KERN_NOTICE "SAK: killed process %d" - " (%s): task_session(p)==tty->session\n", - task_pid_nr(p), p->comm); - send_sig(SIGKILL, p, 1); - continue; - } - task_lock(p); - if (p->files) { - /* - * We don't take a ref to the file, so we must - * hold ->file_lock instead. - */ - spin_lock(&p->files->file_lock); - fdt = files_fdtable(p->files); - for (i = 0; i < fdt->max_fds; i++) { - filp = fcheck_files(p->files, i); - if (!filp) - continue; - if (filp->f_op->read == tty_read && - file_tty(filp) == tty) { - printk(KERN_NOTICE "SAK: killed process %d" - " (%s): fd#%d opened to the tty\n", - task_pid_nr(p), p->comm, i); - force_sig(SIGKILL, p); - break; - } - } - spin_unlock(&p->files->file_lock); - } - task_unlock(p); - } while_each_thread(g, p); - read_unlock(&tasklist_lock); -#endif -} - -static void do_SAK_work(struct work_struct *work) -{ - struct tty_struct *tty = - container_of(work, struct tty_struct, SAK_work); - __do_SAK(tty); -} - -/* - * The tq handling here is a little racy - tty->SAK_work may already be queued. - * Fortunately we don't need to worry, because if ->SAK_work is already queued, - * the values which we write to it will be identical to the values which it - * already has. --akpm - */ -void do_SAK(struct tty_struct *tty) -{ - if (!tty) - return; - schedule_work(&tty->SAK_work); -} - -EXPORT_SYMBOL(do_SAK); - -static int dev_match_devt(struct device *dev, void *data) -{ - dev_t *devt = data; - return dev->devt == *devt; -} - -/* Must put_device() after it's unused! */ -static struct device *tty_get_device(struct tty_struct *tty) -{ - dev_t devt = tty_devnum(tty); - return class_find_device(tty_class, NULL, &devt, dev_match_devt); -} - - -/** - * initialize_tty_struct - * @tty: tty to initialize - * - * This subroutine initializes a tty structure that has been newly - * allocated. - * - * Locking: none - tty in question must not be exposed at this point - */ - -void initialize_tty_struct(struct tty_struct *tty, - struct tty_driver *driver, int idx) -{ - memset(tty, 0, sizeof(struct tty_struct)); - kref_init(&tty->kref); - tty->magic = TTY_MAGIC; - tty_ldisc_init(tty); - tty->session = NULL; - tty->pgrp = NULL; - tty->overrun_time = jiffies; - tty->buf.head = tty->buf.tail = NULL; - tty_buffer_init(tty); - mutex_init(&tty->termios_mutex); - mutex_init(&tty->ldisc_mutex); - 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); - - tty->driver = driver; - tty->ops = driver->ops; - tty->index = idx; - tty_line_name(driver, idx, tty->name); - tty->dev = tty_get_device(tty); -} - -/** - * tty_put_char - write one character to a tty - * @tty: tty - * @ch: character - * - * Write one byte to the tty using the provided put_char method - * if present. Returns the number of characters successfully output. - * - * Note: the specific put_char operation in the driver layer may go - * away soon. Don't call it directly, use this method - */ - -int tty_put_char(struct tty_struct *tty, unsigned char ch) -{ - if (tty->ops->put_char) - return tty->ops->put_char(tty, ch); - return tty->ops->write(tty, &ch, 1); -} -EXPORT_SYMBOL_GPL(tty_put_char); - -struct class *tty_class; - -/** - * tty_register_device - register a tty device - * @driver: the tty driver that describes the tty device - * @index: the index in the tty driver for this tty device - * @device: a struct device that is associated with this tty device. - * This field is optional, if there is no known struct device - * for this tty device it can be set to NULL safely. - * - * Returns a pointer to the struct device for this tty device - * (or ERR_PTR(-EFOO) on error). - * - * This call is required to be made to register an individual tty device - * if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set. If - * that bit is not set, this function should not be called by a tty - * driver. - * - * Locking: ?? - */ - -struct device *tty_register_device(struct tty_driver *driver, unsigned index, - struct device *device) -{ - char name[64]; - dev_t dev = MKDEV(driver->major, driver->minor_start) + index; - - if (index >= driver->num) { - printk(KERN_ERR "Attempt to register invalid tty line number " - " (%d).\n", index); - return ERR_PTR(-EINVAL); - } - - if (driver->type == TTY_DRIVER_TYPE_PTY) - pty_line_name(driver, index, name); - else - tty_line_name(driver, index, name); - - return device_create(tty_class, device, dev, NULL, name); -} -EXPORT_SYMBOL(tty_register_device); - -/** - * tty_unregister_device - unregister a tty device - * @driver: the tty driver that describes the tty device - * @index: the index in the tty driver for this tty device - * - * If a tty device is registered with a call to tty_register_device() then - * this function must be called when the tty device is gone. - * - * Locking: ?? - */ - -void tty_unregister_device(struct tty_driver *driver, unsigned index) -{ - device_destroy(tty_class, - MKDEV(driver->major, driver->minor_start) + index); -} -EXPORT_SYMBOL(tty_unregister_device); - -struct tty_driver *alloc_tty_driver(int lines) -{ - struct tty_driver *driver; - - driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL); - if (driver) { - kref_init(&driver->kref); - driver->magic = TTY_DRIVER_MAGIC; - driver->num = lines; - /* later we'll move allocation of tables here */ - } - return driver; -} -EXPORT_SYMBOL(alloc_tty_driver); - -static void destruct_tty_driver(struct kref *kref) -{ - struct tty_driver *driver = container_of(kref, struct tty_driver, kref); - int i; - struct ktermios *tp; - void *p; - - if (driver->flags & TTY_DRIVER_INSTALLED) { - /* - * Free the termios and termios_locked structures because - * we don't want to get memory leaks when modular tty - * drivers are removed from the kernel. - */ - for (i = 0; i < driver->num; i++) { - tp = driver->termios[i]; - if (tp) { - driver->termios[i] = NULL; - kfree(tp); - } - if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) - tty_unregister_device(driver, i); - } - p = driver->ttys; - proc_tty_unregister_driver(driver); - driver->ttys = NULL; - driver->termios = NULL; - kfree(p); - cdev_del(&driver->cdev); - } - kfree(driver); -} - -void tty_driver_kref_put(struct tty_driver *driver) -{ - kref_put(&driver->kref, destruct_tty_driver); -} -EXPORT_SYMBOL(tty_driver_kref_put); - -void tty_set_operations(struct tty_driver *driver, - const struct tty_operations *op) -{ - driver->ops = op; -}; -EXPORT_SYMBOL(tty_set_operations); - -void put_tty_driver(struct tty_driver *d) -{ - tty_driver_kref_put(d); -} -EXPORT_SYMBOL(put_tty_driver); - -/* - * Called by a tty driver to register itself. - */ -int tty_register_driver(struct tty_driver *driver) -{ - int error; - int i; - dev_t dev; - void **p = NULL; - struct device *d; - - if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) { - p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL); - if (!p) - return -ENOMEM; - } - - if (!driver->major) { - error = alloc_chrdev_region(&dev, driver->minor_start, - driver->num, driver->name); - if (!error) { - driver->major = MAJOR(dev); - driver->minor_start = MINOR(dev); - } - } else { - dev = MKDEV(driver->major, driver->minor_start); - error = register_chrdev_region(dev, driver->num, driver->name); - } - if (error < 0) { - kfree(p); - return error; - } - - if (p) { - driver->ttys = (struct tty_struct **)p; - driver->termios = (struct ktermios **)(p + driver->num); - } else { - driver->ttys = NULL; - driver->termios = NULL; - } - - cdev_init(&driver->cdev, &tty_fops); - driver->cdev.owner = driver->owner; - error = cdev_add(&driver->cdev, dev, driver->num); - if (error) { - unregister_chrdev_region(dev, driver->num); - driver->ttys = NULL; - driver->termios = NULL; - kfree(p); - return error; - } - - mutex_lock(&tty_mutex); - list_add(&driver->tty_drivers, &tty_drivers); - mutex_unlock(&tty_mutex); - - if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) { - for (i = 0; i < driver->num; i++) { - d = tty_register_device(driver, i, NULL); - if (IS_ERR(d)) { - error = PTR_ERR(d); - goto err; - } - } - } - proc_tty_register_driver(driver); - driver->flags |= TTY_DRIVER_INSTALLED; - return 0; - -err: - for (i--; i >= 0; i--) - tty_unregister_device(driver, i); - - mutex_lock(&tty_mutex); - list_del(&driver->tty_drivers); - mutex_unlock(&tty_mutex); - - unregister_chrdev_region(dev, driver->num); - driver->ttys = NULL; - driver->termios = NULL; - kfree(p); - return error; -} - -EXPORT_SYMBOL(tty_register_driver); - -/* - * Called by a tty driver to unregister itself. - */ -int tty_unregister_driver(struct tty_driver *driver) -{ -#if 0 - /* FIXME */ - if (driver->refcount) - return -EBUSY; -#endif - unregister_chrdev_region(MKDEV(driver->major, driver->minor_start), - driver->num); - mutex_lock(&tty_mutex); - list_del(&driver->tty_drivers); - mutex_unlock(&tty_mutex); - return 0; -} - -EXPORT_SYMBOL(tty_unregister_driver); - -dev_t tty_devnum(struct tty_struct *tty) -{ - return MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; -} -EXPORT_SYMBOL(tty_devnum); - -void proc_clear_tty(struct task_struct *p) -{ - unsigned long flags; - struct tty_struct *tty; - spin_lock_irqsave(&p->sighand->siglock, flags); - tty = p->signal->tty; - p->signal->tty = NULL; - spin_unlock_irqrestore(&p->sighand->siglock, flags); - tty_kref_put(tty); -} - -/* Called under the sighand lock */ - -static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) -{ - if (tty) { - unsigned long flags; - /* We should not have a session or pgrp to put here but.... */ - spin_lock_irqsave(&tty->ctrl_lock, flags); - put_pid(tty->session); - put_pid(tty->pgrp); - tty->pgrp = get_pid(task_pgrp(tsk)); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - tty->session = get_pid(task_session(tsk)); - if (tsk->signal->tty) { - printk(KERN_DEBUG "tty not NULL!!\n"); - tty_kref_put(tsk->signal->tty); - } - } - put_pid(tsk->signal->tty_old_pgrp); - tsk->signal->tty = tty_kref_get(tty); - tsk->signal->tty_old_pgrp = NULL; -} - -static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) -{ - spin_lock_irq(&tsk->sighand->siglock); - __proc_set_tty(tsk, tty); - spin_unlock_irq(&tsk->sighand->siglock); -} - -struct tty_struct *get_current_tty(void) -{ - struct tty_struct *tty; - unsigned long flags; - - spin_lock_irqsave(¤t->sighand->siglock, flags); - tty = tty_kref_get(current->signal->tty); - spin_unlock_irqrestore(¤t->sighand->siglock, flags); - return tty; -} -EXPORT_SYMBOL_GPL(get_current_tty); - -void tty_default_fops(struct file_operations *fops) -{ - *fops = tty_fops; -} - -/* - * Initialize the console device. This is called *early*, so - * we can't necessarily depend on lots of kernel help here. - * Just do some early initializations, and do the complex setup - * later. - */ -void __init console_init(void) -{ - initcall_t *call; - - /* Setup the default TTY line discipline. */ - tty_ldisc_begin(); - - /* - * set up the console device so that later boot sequences can - * inform about problems etc.. - */ - call = __con_initcall_start; - while (call < __con_initcall_end) { - (*call)(); - call++; - } -} - -static char *tty_devnode(struct device *dev, mode_t *mode) -{ - if (!mode) - return NULL; - if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) || - dev->devt == MKDEV(TTYAUX_MAJOR, 2)) - *mode = 0666; - return NULL; -} - -static int __init tty_class_init(void) -{ - tty_class = class_create(THIS_MODULE, "tty"); - if (IS_ERR(tty_class)) - return PTR_ERR(tty_class); - tty_class->devnode = tty_devnode; - return 0; -} - -postcore_initcall(tty_class_init); - -/* 3/2004 jmc: why do these devices exist? */ - -static struct cdev tty_cdev, console_cdev; - -/* - * Ok, now we can initialize the rest of the tty devices and can count - * on memory allocations, interrupts etc.. - */ -int __init tty_init(void) -{ - cdev_init(&tty_cdev, &tty_fops); - if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) || - register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0) - panic("Couldn't register /dev/tty driver\n"); - device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, - "tty"); - - cdev_init(&console_cdev, &console_fops); - if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) || - register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0) - panic("Couldn't register /dev/console driver\n"); - device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, - "console"); - -#ifdef CONFIG_VT - vty_init(&console_fops); -#endif - return 0; -} - diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c deleted file mode 100644 index 0c1889971459..000000000000 --- a/drivers/char/tty_ioctl.c +++ /dev/null @@ -1,1179 +0,0 @@ -/* - * linux/drivers/char/tty_ioctl.c - * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - * - * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines - * which can be dynamically activated and de-activated by the line - * discipline handling modules (like SLIP). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#undef TTY_DEBUG_WAIT_UNTIL_SENT - -#undef DEBUG - -/* - * Internal flag options for termios setting behavior - */ -#define TERMIOS_FLUSH 1 -#define TERMIOS_WAIT 2 -#define TERMIOS_TERMIO 4 -#define TERMIOS_OLD 8 - - -/** - * tty_chars_in_buffer - characters pending - * @tty: terminal - * - * Return the number of bytes of data in the device private - * output queue. If no private method is supplied there is assumed - * to be no queue on the device. - */ - -int tty_chars_in_buffer(struct tty_struct *tty) -{ - if (tty->ops->chars_in_buffer) - return tty->ops->chars_in_buffer(tty); - else - return 0; -} -EXPORT_SYMBOL(tty_chars_in_buffer); - -/** - * tty_write_room - write queue space - * @tty: terminal - * - * Return the number of bytes that can be queued to this device - * at the present time. The result should be treated as a guarantee - * and the driver cannot offer a value it later shrinks by more than - * the number of bytes written. If no method is provided 2K is always - * returned and data may be lost as there will be no flow control. - */ - -int tty_write_room(struct tty_struct *tty) -{ - if (tty->ops->write_room) - return tty->ops->write_room(tty); - return 2048; -} -EXPORT_SYMBOL(tty_write_room); - -/** - * tty_driver_flush_buffer - discard internal buffer - * @tty: terminal - * - * Discard the internal output buffer for this device. If no method - * is provided then either the buffer cannot be hardware flushed or - * there is no buffer driver side. - */ -void tty_driver_flush_buffer(struct tty_struct *tty) -{ - if (tty->ops->flush_buffer) - tty->ops->flush_buffer(tty); -} -EXPORT_SYMBOL(tty_driver_flush_buffer); - -/** - * tty_throttle - flow control - * @tty: terminal - * - * Indicate that a tty should stop transmitting data down the stack. - * Takes the termios mutex to protect against parallel throttle/unthrottle - * and also to ensure the driver can consistently reference its own - * termios data at this point when implementing software flow control. - */ - -void tty_throttle(struct tty_struct *tty) -{ - mutex_lock(&tty->termios_mutex); - /* check TTY_THROTTLED first so it indicates our state */ - if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && - tty->ops->throttle) - tty->ops->throttle(tty); - mutex_unlock(&tty->termios_mutex); -} -EXPORT_SYMBOL(tty_throttle); - -/** - * tty_unthrottle - flow control - * @tty: terminal - * - * Indicate that a tty may continue transmitting data down the stack. - * Takes the termios mutex to protect against parallel throttle/unthrottle - * and also to ensure the driver can consistently reference its own - * termios data at this point when implementing software flow control. - * - * Drivers should however remember that the stack can issue a throttle, - * then change flow control method, then unthrottle. - */ - -void tty_unthrottle(struct tty_struct *tty) -{ - mutex_lock(&tty->termios_mutex); - if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && - tty->ops->unthrottle) - tty->ops->unthrottle(tty); - mutex_unlock(&tty->termios_mutex); -} -EXPORT_SYMBOL(tty_unthrottle); - -/** - * tty_wait_until_sent - wait for I/O to finish - * @tty: tty we are waiting for - * @timeout: how long we will wait - * - * Wait for characters pending in a tty driver to hit the wire, or - * for a timeout to occur (eg due to flow control) - * - * Locking: none - */ - -void tty_wait_until_sent(struct tty_struct *tty, long timeout) -{ -#ifdef TTY_DEBUG_WAIT_UNTIL_SENT - char buf[64]; - - printk(KERN_DEBUG "%s wait until sent...\n", tty_name(tty, buf)); -#endif - if (!timeout) - timeout = MAX_SCHEDULE_TIMEOUT; - if (wait_event_interruptible_timeout(tty->write_wait, - !tty_chars_in_buffer(tty), timeout) >= 0) { - if (tty->ops->wait_until_sent) - tty->ops->wait_until_sent(tty, timeout); - } -} -EXPORT_SYMBOL(tty_wait_until_sent); - - -/* - * Termios Helper Methods - */ - -static void unset_locked_termios(struct ktermios *termios, - struct ktermios *old, - struct ktermios *locked) -{ - int i; - -#define NOSET_MASK(x, y, z) (x = ((x) & ~(z)) | ((y) & (z))) - - if (!locked) { - printk(KERN_WARNING "Warning?!? termios_locked is NULL.\n"); - return; - } - - NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag); - NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag); - NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag); - NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag); - termios->c_line = locked->c_line ? old->c_line : termios->c_line; - for (i = 0; i < NCCS; i++) - termios->c_cc[i] = locked->c_cc[i] ? - old->c_cc[i] : termios->c_cc[i]; - /* FIXME: What should we do for i/ospeed */ -} - -/* - * Routine which returns the baud rate of the tty - * - * Note that the baud_table needs to be kept in sync with the - * include/asm/termbits.h file. - */ -static const speed_t baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, -#ifdef __sparc__ - 76800, 153600, 307200, 614400, 921600 -#else - 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, - 2500000, 3000000, 3500000, 4000000 -#endif -}; - -#ifndef __sparc__ -static const tcflag_t baud_bits[] = { - B0, B50, B75, B110, B134, B150, B200, B300, B600, - B1200, B1800, B2400, B4800, B9600, B19200, B38400, - B57600, B115200, B230400, B460800, B500000, B576000, - B921600, B1000000, B1152000, B1500000, B2000000, B2500000, - B3000000, B3500000, B4000000 -}; -#else -static const tcflag_t baud_bits[] = { - B0, B50, B75, B110, B134, B150, B200, B300, B600, - B1200, B1800, B2400, B4800, B9600, B19200, B38400, - B57600, B115200, B230400, B460800, B76800, B153600, - B307200, B614400, B921600 -}; -#endif - -static int n_baud_table = ARRAY_SIZE(baud_table); - -/** - * tty_termios_baud_rate - * @termios: termios structure - * - * Convert termios baud rate data into a speed. This should be called - * with the termios lock held if this termios is a terminal termios - * structure. May change the termios data. Device drivers can call this - * function but should use ->c_[io]speed directly as they are updated. - * - * Locking: none - */ - -speed_t tty_termios_baud_rate(struct ktermios *termios) -{ - unsigned int cbaud; - - cbaud = termios->c_cflag & CBAUD; - -#ifdef BOTHER - /* Magic token for arbitary speed via c_ispeed/c_ospeed */ - if (cbaud == BOTHER) - return termios->c_ospeed; -#endif - if (cbaud & CBAUDEX) { - cbaud &= ~CBAUDEX; - - if (cbaud < 1 || cbaud + 15 > n_baud_table) - termios->c_cflag &= ~CBAUDEX; - else - cbaud += 15; - } - return baud_table[cbaud]; -} -EXPORT_SYMBOL(tty_termios_baud_rate); - -/** - * tty_termios_input_baud_rate - * @termios: termios structure - * - * Convert termios baud rate data into a speed. This should be called - * with the termios lock held if this termios is a terminal termios - * structure. May change the termios data. Device drivers can call this - * function but should use ->c_[io]speed directly as they are updated. - * - * Locking: none - */ - -speed_t tty_termios_input_baud_rate(struct ktermios *termios) -{ -#ifdef IBSHIFT - unsigned int cbaud = (termios->c_cflag >> IBSHIFT) & CBAUD; - - if (cbaud == B0) - return tty_termios_baud_rate(termios); - - /* Magic token for arbitary speed via c_ispeed*/ - if (cbaud == BOTHER) - return termios->c_ispeed; - - if (cbaud & CBAUDEX) { - cbaud &= ~CBAUDEX; - - if (cbaud < 1 || cbaud + 15 > n_baud_table) - termios->c_cflag &= ~(CBAUDEX << IBSHIFT); - else - cbaud += 15; - } - return baud_table[cbaud]; -#else - return tty_termios_baud_rate(termios); -#endif -} -EXPORT_SYMBOL(tty_termios_input_baud_rate); - -/** - * tty_termios_encode_baud_rate - * @termios: ktermios structure holding user requested state - * @ispeed: input speed - * @ospeed: output speed - * - * Encode the speeds set into the passed termios structure. This is - * used as a library helper for drivers os that they can report back - * the actual speed selected when it differs from the speed requested - * - * For maximal back compatibility with legacy SYS5/POSIX *nix behaviour - * we need to carefully set the bits when the user does not get the - * desired speed. We allow small margins and preserve as much of possible - * of the input intent to keep compatibility. - * - * Locking: Caller should hold termios lock. This is already held - * when calling this function from the driver termios handler. - * - * The ifdefs deal with platforms whose owners have yet to update them - * and will all go away once this is done. - */ - -void tty_termios_encode_baud_rate(struct ktermios *termios, - speed_t ibaud, speed_t obaud) -{ - int i = 0; - int ifound = -1, ofound = -1; - int iclose = ibaud/50, oclose = obaud/50; - int ibinput = 0; - - if (obaud == 0) /* CD dropped */ - ibaud = 0; /* Clear ibaud to be sure */ - - termios->c_ispeed = ibaud; - termios->c_ospeed = obaud; - -#ifdef BOTHER - /* If the user asked for a precise weird speed give a precise weird - answer. If they asked for a Bfoo speed they many have problems - digesting non-exact replies so fuzz a bit */ - - if ((termios->c_cflag & CBAUD) == BOTHER) - oclose = 0; - if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER) - iclose = 0; - if ((termios->c_cflag >> IBSHIFT) & CBAUD) - ibinput = 1; /* An input speed was specified */ -#endif - termios->c_cflag &= ~CBAUD; - - /* - * Our goal is to find a close match to the standard baud rate - * returned. Walk the baud rate table and if we get a very close - * match then report back the speed as a POSIX Bxxxx value by - * preference - */ - - do { - if (obaud - oclose <= baud_table[i] && - obaud + oclose >= baud_table[i]) { - termios->c_cflag |= baud_bits[i]; - ofound = i; - } - if (ibaud - iclose <= baud_table[i] && - ibaud + iclose >= baud_table[i]) { - /* For the case input == output don't set IBAUD bits - if the user didn't do so */ - if (ofound == i && !ibinput) - ifound = i; -#ifdef IBSHIFT - else { - ifound = i; - termios->c_cflag |= (baud_bits[i] << IBSHIFT); - } -#endif - } - } while (++i < n_baud_table); - - /* - * If we found no match then use BOTHER if provided or warn - * the user their platform maintainer needs to wake up if not. - */ -#ifdef BOTHER - if (ofound == -1) - termios->c_cflag |= BOTHER; - /* Set exact input bits only if the input and output differ or the - user already did */ - if (ifound == -1 && (ibaud != obaud || ibinput)) - termios->c_cflag |= (BOTHER << IBSHIFT); -#else - if (ifound == -1 || ofound == -1) { - printk_once(KERN_WARNING "tty: Unable to return correct " - "speed data as your architecture needs updating.\n"); - } -#endif -} -EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); - -/** - * tty_encode_baud_rate - set baud rate of the tty - * @ibaud: input baud rate - * @obad: output baud rate - * - * Update the current termios data for the tty with the new speed - * settings. The caller must hold the termios_mutex for the tty in - * question. - */ - -void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) -{ - tty_termios_encode_baud_rate(tty->termios, ibaud, obaud); -} -EXPORT_SYMBOL_GPL(tty_encode_baud_rate); - -/** - * tty_get_baud_rate - get tty bit rates - * @tty: tty to query - * - * Returns the baud rate as an integer for this terminal. The - * termios lock must be held by the caller and the terminal bit - * flags may be updated. - * - * Locking: none - */ - -speed_t tty_get_baud_rate(struct tty_struct *tty) -{ - speed_t baud = tty_termios_baud_rate(tty->termios); - - if (baud == 38400 && tty->alt_speed) { - if (!tty->warned) { - printk(KERN_WARNING "Use of setserial/setrocket to " - "set SPD_* flags is deprecated\n"); - tty->warned = 1; - } - baud = tty->alt_speed; - } - - return baud; -} -EXPORT_SYMBOL(tty_get_baud_rate); - -/** - * tty_termios_copy_hw - copy hardware settings - * @new: New termios - * @old: Old termios - * - * Propogate the hardware specific terminal setting bits from - * the old termios structure to the new one. This is used in cases - * where the hardware does not support reconfiguration or as a helper - * in some cases where only minimal reconfiguration is supported - */ - -void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old) -{ - /* The bits a dumb device handles in software. Smart devices need - to always provide a set_termios method */ - new->c_cflag &= HUPCL | CREAD | CLOCAL; - new->c_cflag |= old->c_cflag & ~(HUPCL | CREAD | CLOCAL); - new->c_ispeed = old->c_ispeed; - new->c_ospeed = old->c_ospeed; -} -EXPORT_SYMBOL(tty_termios_copy_hw); - -/** - * tty_termios_hw_change - check for setting change - * @a: termios - * @b: termios to compare - * - * Check if any of the bits that affect a dumb device have changed - * between the two termios structures, or a speed change is needed. - */ - -int tty_termios_hw_change(struct ktermios *a, struct ktermios *b) -{ - if (a->c_ispeed != b->c_ispeed || a->c_ospeed != b->c_ospeed) - return 1; - if ((a->c_cflag ^ b->c_cflag) & ~(HUPCL | CREAD | CLOCAL)) - return 1; - return 0; -} -EXPORT_SYMBOL(tty_termios_hw_change); - -/** - * change_termios - update termios values - * @tty: tty to update - * @new_termios: desired new value - * - * Perform updates to the termios values set on this terminal. There - * is a bit of layering violation here with n_tty in terms of the - * internal knowledge of this function. - * - * Locking: termios_mutex - */ - -static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) -{ - struct ktermios old_termios; - struct tty_ldisc *ld; - unsigned long flags; - - /* - * Perform the actual termios internal changes under lock. - */ - - - /* FIXME: we need to decide on some locking/ordering semantics - for the set_termios notification eventually */ - mutex_lock(&tty->termios_mutex); - old_termios = *tty->termios; - *tty->termios = *new_termios; - unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); - - /* See if packet mode change of state. */ - if (tty->link && tty->link->packet) { - int extproc = (old_termios.c_lflag & EXTPROC) | - (tty->termios->c_lflag & EXTPROC); - int old_flow = ((old_termios.c_iflag & IXON) && - (old_termios.c_cc[VSTOP] == '\023') && - (old_termios.c_cc[VSTART] == '\021')); - int new_flow = (I_IXON(tty) && - STOP_CHAR(tty) == '\023' && - START_CHAR(tty) == '\021'); - if ((old_flow != new_flow) || extproc) { - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (old_flow != new_flow) { - tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); - if (new_flow) - tty->ctrl_status |= TIOCPKT_DOSTOP; - else - tty->ctrl_status |= TIOCPKT_NOSTOP; - } - if (extproc) - tty->ctrl_status |= TIOCPKT_IOCTL; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - wake_up_interruptible(&tty->link->read_wait); - } - } - - if (tty->ops->set_termios) - (*tty->ops->set_termios)(tty, &old_termios); - else - tty_termios_copy_hw(tty->termios, &old_termios); - - ld = tty_ldisc_ref(tty); - if (ld != NULL) { - if (ld->ops->set_termios) - (ld->ops->set_termios)(tty, &old_termios); - tty_ldisc_deref(ld); - } - mutex_unlock(&tty->termios_mutex); -} - -/** - * set_termios - set termios values for a tty - * @tty: terminal device - * @arg: user data - * @opt: option information - * - * Helper function to prepare termios data and run necessary other - * functions before using change_termios to do the actual changes. - * - * Locking: - * Called functions take ldisc and termios_mutex locks - */ - -static int set_termios(struct tty_struct *tty, void __user *arg, int opt) -{ - struct ktermios tmp_termios; - struct tty_ldisc *ld; - int retval = tty_check_change(tty); - - if (retval) - return retval; - - mutex_lock(&tty->termios_mutex); - memcpy(&tmp_termios, tty->termios, sizeof(struct ktermios)); - mutex_unlock(&tty->termios_mutex); - - if (opt & TERMIOS_TERMIO) { - if (user_termio_to_kernel_termios(&tmp_termios, - (struct termio __user *)arg)) - return -EFAULT; -#ifdef TCGETS2 - } else if (opt & TERMIOS_OLD) { - if (user_termios_to_kernel_termios_1(&tmp_termios, - (struct termios __user *)arg)) - return -EFAULT; - } else { - if (user_termios_to_kernel_termios(&tmp_termios, - (struct termios2 __user *)arg)) - return -EFAULT; - } -#else - } else if (user_termios_to_kernel_termios(&tmp_termios, - (struct termios __user *)arg)) - return -EFAULT; -#endif - - /* If old style Bfoo values are used then load c_ispeed/c_ospeed - * with the real speed so its unconditionally usable */ - tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios); - tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios); - - ld = tty_ldisc_ref(tty); - - if (ld != NULL) { - if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer) - ld->ops->flush_buffer(tty); - tty_ldisc_deref(ld); - } - - if (opt & TERMIOS_WAIT) { - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - } - - change_termios(tty, &tmp_termios); - - /* FIXME: Arguably if tmp_termios == tty->termios AND the - actual requested termios was not tmp_termios then we may - want to return an error as no user requested change has - succeeded */ - return 0; -} - -static void copy_termios(struct tty_struct *tty, struct ktermios *kterm) -{ - mutex_lock(&tty->termios_mutex); - memcpy(kterm, tty->termios, sizeof(struct ktermios)); - mutex_unlock(&tty->termios_mutex); -} - -static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm) -{ - mutex_lock(&tty->termios_mutex); - memcpy(kterm, tty->termios_locked, sizeof(struct ktermios)); - mutex_unlock(&tty->termios_mutex); -} - -static int get_termio(struct tty_struct *tty, struct termio __user *termio) -{ - struct ktermios kterm; - copy_termios(tty, &kterm); - if (kernel_termios_to_user_termio(termio, &kterm)) - return -EFAULT; - return 0; -} - - -#ifdef TCGETX - -/** - * set_termiox - set termiox fields if possible - * @tty: terminal - * @arg: termiox structure from user - * @opt: option flags for ioctl type - * - * Implement the device calling points for the SYS5 termiox ioctl - * interface in Linux - */ - -static int set_termiox(struct tty_struct *tty, void __user *arg, int opt) -{ - struct termiox tnew; - struct tty_ldisc *ld; - - if (tty->termiox == NULL) - return -EINVAL; - if (copy_from_user(&tnew, arg, sizeof(struct termiox))) - return -EFAULT; - - ld = tty_ldisc_ref(tty); - if (ld != NULL) { - if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer) - ld->ops->flush_buffer(tty); - tty_ldisc_deref(ld); - } - if (opt & TERMIOS_WAIT) { - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - } - - mutex_lock(&tty->termios_mutex); - if (tty->ops->set_termiox) - tty->ops->set_termiox(tty, &tnew); - mutex_unlock(&tty->termios_mutex); - return 0; -} - -#endif - - -#ifdef TIOCGETP -/* - * These are deprecated, but there is limited support.. - * - * The "sg_flags" translation is a joke.. - */ -static int get_sgflags(struct tty_struct *tty) -{ - int flags = 0; - - if (!(tty->termios->c_lflag & ICANON)) { - if (tty->termios->c_lflag & ISIG) - flags |= 0x02; /* cbreak */ - else - flags |= 0x20; /* raw */ - } - if (tty->termios->c_lflag & ECHO) - flags |= 0x08; /* echo */ - if (tty->termios->c_oflag & OPOST) - if (tty->termios->c_oflag & ONLCR) - flags |= 0x10; /* crmod */ - return flags; -} - -static int get_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) -{ - struct sgttyb tmp; - - mutex_lock(&tty->termios_mutex); - tmp.sg_ispeed = tty->termios->c_ispeed; - tmp.sg_ospeed = tty->termios->c_ospeed; - tmp.sg_erase = tty->termios->c_cc[VERASE]; - tmp.sg_kill = tty->termios->c_cc[VKILL]; - tmp.sg_flags = get_sgflags(tty); - mutex_unlock(&tty->termios_mutex); - - return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0; -} - -static void set_sgflags(struct ktermios *termios, int flags) -{ - termios->c_iflag = ICRNL | IXON; - termios->c_oflag = 0; - termios->c_lflag = ISIG | ICANON; - if (flags & 0x02) { /* cbreak */ - termios->c_iflag = 0; - termios->c_lflag &= ~ICANON; - } - if (flags & 0x08) { /* echo */ - termios->c_lflag |= ECHO | ECHOE | ECHOK | - ECHOCTL | ECHOKE | IEXTEN; - } - if (flags & 0x10) { /* crmod */ - termios->c_oflag |= OPOST | ONLCR; - } - if (flags & 0x20) { /* raw */ - termios->c_iflag = 0; - termios->c_lflag &= ~(ISIG | ICANON); - } - if (!(termios->c_lflag & ICANON)) { - termios->c_cc[VMIN] = 1; - termios->c_cc[VTIME] = 0; - } -} - -/** - * set_sgttyb - set legacy terminal values - * @tty: tty structure - * @sgttyb: pointer to old style terminal structure - * - * Updates a terminal from the legacy BSD style terminal information - * structure. - * - * Locking: termios_mutex - */ - -static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) -{ - int retval; - struct sgttyb tmp; - struct ktermios termios; - - retval = tty_check_change(tty); - if (retval) - return retval; - - if (copy_from_user(&tmp, sgttyb, sizeof(tmp))) - return -EFAULT; - - mutex_lock(&tty->termios_mutex); - termios = *tty->termios; - termios.c_cc[VERASE] = tmp.sg_erase; - termios.c_cc[VKILL] = tmp.sg_kill; - set_sgflags(&termios, tmp.sg_flags); - /* Try and encode into Bfoo format */ -#ifdef BOTHER - tty_termios_encode_baud_rate(&termios, termios.c_ispeed, - termios.c_ospeed); -#endif - mutex_unlock(&tty->termios_mutex); - change_termios(tty, &termios); - return 0; -} -#endif - -#ifdef TIOCGETC -static int get_tchars(struct tty_struct *tty, struct tchars __user *tchars) -{ - struct tchars tmp; - - mutex_lock(&tty->termios_mutex); - tmp.t_intrc = tty->termios->c_cc[VINTR]; - tmp.t_quitc = tty->termios->c_cc[VQUIT]; - tmp.t_startc = tty->termios->c_cc[VSTART]; - tmp.t_stopc = tty->termios->c_cc[VSTOP]; - tmp.t_eofc = tty->termios->c_cc[VEOF]; - tmp.t_brkc = tty->termios->c_cc[VEOL2]; /* what is brkc anyway? */ - mutex_unlock(&tty->termios_mutex); - return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; -} - -static int set_tchars(struct tty_struct *tty, struct tchars __user *tchars) -{ - struct tchars tmp; - - if (copy_from_user(&tmp, tchars, sizeof(tmp))) - return -EFAULT; - mutex_lock(&tty->termios_mutex); - tty->termios->c_cc[VINTR] = tmp.t_intrc; - tty->termios->c_cc[VQUIT] = tmp.t_quitc; - tty->termios->c_cc[VSTART] = tmp.t_startc; - tty->termios->c_cc[VSTOP] = tmp.t_stopc; - tty->termios->c_cc[VEOF] = tmp.t_eofc; - tty->termios->c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */ - mutex_unlock(&tty->termios_mutex); - return 0; -} -#endif - -#ifdef TIOCGLTC -static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) -{ - struct ltchars tmp; - - mutex_lock(&tty->termios_mutex); - tmp.t_suspc = tty->termios->c_cc[VSUSP]; - /* what is dsuspc anyway? */ - tmp.t_dsuspc = tty->termios->c_cc[VSUSP]; - tmp.t_rprntc = tty->termios->c_cc[VREPRINT]; - /* what is flushc anyway? */ - tmp.t_flushc = tty->termios->c_cc[VEOL2]; - tmp.t_werasc = tty->termios->c_cc[VWERASE]; - tmp.t_lnextc = tty->termios->c_cc[VLNEXT]; - mutex_unlock(&tty->termios_mutex); - return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; -} - -static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) -{ - struct ltchars tmp; - - if (copy_from_user(&tmp, ltchars, sizeof(tmp))) - return -EFAULT; - - mutex_lock(&tty->termios_mutex); - tty->termios->c_cc[VSUSP] = tmp.t_suspc; - /* what is dsuspc anyway? */ - tty->termios->c_cc[VEOL2] = tmp.t_dsuspc; - tty->termios->c_cc[VREPRINT] = tmp.t_rprntc; - /* what is flushc anyway? */ - tty->termios->c_cc[VEOL2] = tmp.t_flushc; - tty->termios->c_cc[VWERASE] = tmp.t_werasc; - tty->termios->c_cc[VLNEXT] = tmp.t_lnextc; - mutex_unlock(&tty->termios_mutex); - return 0; -} -#endif - -/** - * send_prio_char - send priority character - * - * Send a high priority character to the tty even if stopped - * - * Locking: none for xchar method, write ordering for write method. - */ - -static int send_prio_char(struct tty_struct *tty, char ch) -{ - int was_stopped = tty->stopped; - - if (tty->ops->send_xchar) { - tty->ops->send_xchar(tty, ch); - return 0; - } - - if (tty_write_lock(tty, 0) < 0) - return -ERESTARTSYS; - - if (was_stopped) - start_tty(tty); - tty->ops->write(tty, &ch, 1); - if (was_stopped) - stop_tty(tty); - tty_write_unlock(tty); - return 0; -} - -/** - * tty_change_softcar - carrier change ioctl helper - * @tty: tty to update - * @arg: enable/disable CLOCAL - * - * Perform a change to the CLOCAL state and call into the driver - * layer to make it visible. All done with the termios mutex - */ - -static int tty_change_softcar(struct tty_struct *tty, int arg) -{ - int ret = 0; - int bit = arg ? CLOCAL : 0; - struct ktermios old; - - mutex_lock(&tty->termios_mutex); - old = *tty->termios; - tty->termios->c_cflag &= ~CLOCAL; - tty->termios->c_cflag |= bit; - if (tty->ops->set_termios) - tty->ops->set_termios(tty, &old); - if ((tty->termios->c_cflag & CLOCAL) != bit) - ret = -EINVAL; - mutex_unlock(&tty->termios_mutex); - return ret; -} - -/** - * tty_mode_ioctl - mode related ioctls - * @tty: tty for the ioctl - * @file: file pointer for the tty - * @cmd: command - * @arg: ioctl argument - * - * Perform non line discipline specific mode control ioctls. This - * is designed to be called by line disciplines to ensure they provide - * consistent mode setting. - */ - -int tty_mode_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct tty_struct *real_tty; - void __user *p = (void __user *)arg; - int ret = 0; - struct ktermios kterm; - - if (tty->driver->type == TTY_DRIVER_TYPE_PTY && - tty->driver->subtype == PTY_TYPE_MASTER) - real_tty = tty->link; - else - real_tty = tty; - - switch (cmd) { -#ifdef TIOCGETP - case TIOCGETP: - return get_sgttyb(real_tty, (struct sgttyb __user *) arg); - case TIOCSETP: - case TIOCSETN: - return set_sgttyb(real_tty, (struct sgttyb __user *) arg); -#endif -#ifdef TIOCGETC - case TIOCGETC: - return get_tchars(real_tty, p); - case TIOCSETC: - return set_tchars(real_tty, p); -#endif -#ifdef TIOCGLTC - case TIOCGLTC: - return get_ltchars(real_tty, p); - case TIOCSLTC: - return set_ltchars(real_tty, p); -#endif - case TCSETSF: - return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_OLD); - case TCSETSW: - return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_OLD); - case TCSETS: - return set_termios(real_tty, p, TERMIOS_OLD); -#ifndef TCGETS2 - case TCGETS: - copy_termios(real_tty, &kterm); - if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm)) - ret = -EFAULT; - return ret; -#else - case TCGETS: - copy_termios(real_tty, &kterm); - if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm)) - ret = -EFAULT; - return ret; - case TCGETS2: - copy_termios(real_tty, &kterm); - if (kernel_termios_to_user_termios((struct termios2 __user *)arg, &kterm)) - ret = -EFAULT; - return ret; - case TCSETSF2: - return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT); - case TCSETSW2: - return set_termios(real_tty, p, TERMIOS_WAIT); - case TCSETS2: - return set_termios(real_tty, p, 0); -#endif - case TCGETA: - return get_termio(real_tty, p); - case TCSETAF: - return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_TERMIO); - case TCSETAW: - return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_TERMIO); - case TCSETA: - return set_termios(real_tty, p, TERMIOS_TERMIO); -#ifndef TCGETS2 - case TIOCGLCKTRMIOS: - copy_termios_locked(real_tty, &kterm); - if (kernel_termios_to_user_termios((struct termios __user *)arg, &kterm)) - ret = -EFAULT; - return ret; - case TIOCSLCKTRMIOS: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - copy_termios_locked(real_tty, &kterm); - if (user_termios_to_kernel_termios(&kterm, - (struct termios __user *) arg)) - return -EFAULT; - mutex_lock(&real_tty->termios_mutex); - memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios)); - mutex_unlock(&real_tty->termios_mutex); - return 0; -#else - case TIOCGLCKTRMIOS: - copy_termios_locked(real_tty, &kterm); - if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &kterm)) - ret = -EFAULT; - return ret; - case TIOCSLCKTRMIOS: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - copy_termios_locked(real_tty, &kterm); - if (user_termios_to_kernel_termios_1(&kterm, - (struct termios __user *) arg)) - return -EFAULT; - mutex_lock(&real_tty->termios_mutex); - memcpy(real_tty->termios_locked, &kterm, sizeof(struct ktermios)); - mutex_unlock(&real_tty->termios_mutex); - return ret; -#endif -#ifdef TCGETX - case TCGETX: { - struct termiox ktermx; - if (real_tty->termiox == NULL) - return -EINVAL; - mutex_lock(&real_tty->termios_mutex); - memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox)); - mutex_unlock(&real_tty->termios_mutex); - if (copy_to_user(p, &ktermx, sizeof(struct termiox))) - ret = -EFAULT; - return ret; - } - case TCSETX: - return set_termiox(real_tty, p, 0); - case TCSETXW: - return set_termiox(real_tty, p, TERMIOS_WAIT); - case TCSETXF: - return set_termiox(real_tty, p, TERMIOS_FLUSH); -#endif - case TIOCGSOFTCAR: - copy_termios(real_tty, &kterm); - ret = put_user((kterm.c_cflag & CLOCAL) ? 1 : 0, - (int __user *)arg); - return ret; - case TIOCSSOFTCAR: - if (get_user(arg, (unsigned int __user *) arg)) - return -EFAULT; - return tty_change_softcar(real_tty, arg); - default: - return -ENOIOCTLCMD; - } -} -EXPORT_SYMBOL_GPL(tty_mode_ioctl); - -int tty_perform_flush(struct tty_struct *tty, unsigned long arg) -{ - struct tty_ldisc *ld; - int retval = tty_check_change(tty); - if (retval) - return retval; - - ld = tty_ldisc_ref_wait(tty); - switch (arg) { - case TCIFLUSH: - if (ld && ld->ops->flush_buffer) - ld->ops->flush_buffer(tty); - break; - case TCIOFLUSH: - if (ld && ld->ops->flush_buffer) - ld->ops->flush_buffer(tty); - /* fall through */ - case TCOFLUSH: - tty_driver_flush_buffer(tty); - break; - default: - tty_ldisc_deref(ld); - return -EINVAL; - } - tty_ldisc_deref(ld); - return 0; -} -EXPORT_SYMBOL_GPL(tty_perform_flush); - -int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - int retval; - - switch (cmd) { - case TCXONC: - retval = tty_check_change(tty); - if (retval) - return retval; - switch (arg) { - case TCOOFF: - if (!tty->flow_stopped) { - tty->flow_stopped = 1; - stop_tty(tty); - } - break; - case TCOON: - if (tty->flow_stopped) { - tty->flow_stopped = 0; - start_tty(tty); - } - break; - case TCIOFF: - if (STOP_CHAR(tty) != __DISABLED_CHAR) - return send_prio_char(tty, STOP_CHAR(tty)); - break; - case TCION: - if (START_CHAR(tty) != __DISABLED_CHAR) - return send_prio_char(tty, START_CHAR(tty)); - break; - default: - return -EINVAL; - } - return 0; - case TCFLSH: - return tty_perform_flush(tty, arg); - case TIOCPKT: - { - int pktmode; - - if (tty->driver->type != TTY_DRIVER_TYPE_PTY || - tty->driver->subtype != PTY_TYPE_MASTER) - return -ENOTTY; - if (get_user(pktmode, (int __user *) arg)) - return -EFAULT; - spin_lock_irqsave(&tty->ctrl_lock, flags); - if (pktmode) { - if (!tty->packet) { - tty->packet = 1; - tty->link->ctrl_status = 0; - } - } else - tty->packet = 0; - spin_unlock_irqrestore(&tty->ctrl_lock, flags); - return 0; - } - default: - /* Try the mode commands */ - return tty_mode_ioctl(tty, file, cmd, arg); - } -} -EXPORT_SYMBOL(n_tty_ioctl_helper); diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c deleted file mode 100644 index 412f9775d19c..000000000000 --- a/drivers/char/tty_ldisc.c +++ /dev/null @@ -1,915 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include /* For the moment */ - -#include -#include - -/* - * This guards the refcounted line discipline lists. The lock - * must be taken with irqs off because there are hangup path - * callers who will do ldisc lookups and cannot sleep. - */ - -static DEFINE_SPINLOCK(tty_ldisc_lock); -static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); -/* Line disc dispatch table */ -static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; - -static inline struct tty_ldisc *get_ldisc(struct tty_ldisc *ld) -{ - if (ld) - atomic_inc(&ld->users); - return ld; -} - -static void put_ldisc(struct tty_ldisc *ld) -{ - unsigned long flags; - - if (WARN_ON_ONCE(!ld)) - return; - - /* - * If this is the last user, free the ldisc, and - * release the ldisc ops. - * - * We really want an "atomic_dec_and_lock_irqsave()", - * but we don't have it, so this does it by hand. - */ - local_irq_save(flags); - if (atomic_dec_and_lock(&ld->users, &tty_ldisc_lock)) { - struct tty_ldisc_ops *ldo = ld->ops; - - ldo->refcount--; - module_put(ldo->owner); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - kfree(ld); - return; - } - local_irq_restore(flags); -} - -/** - * tty_register_ldisc - install a line discipline - * @disc: ldisc number - * @new_ldisc: pointer to the ldisc object - * - * Installs a new line discipline into the kernel. The discipline - * is set up as unreferenced and then made available to the kernel - * from this point onwards. - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) -{ - unsigned long flags; - int ret = 0; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - tty_ldiscs[disc] = new_ldisc; - new_ldisc->num = disc; - new_ldisc->refcount = 0; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - return ret; -} -EXPORT_SYMBOL(tty_register_ldisc); - -/** - * tty_unregister_ldisc - unload a line discipline - * @disc: ldisc number - * @new_ldisc: pointer to the ldisc object - * - * Remove a line discipline from the kernel providing it is not - * currently in use. - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -int tty_unregister_ldisc(int disc) -{ - unsigned long flags; - int ret = 0; - - if (disc < N_TTY || disc >= NR_LDISCS) - return -EINVAL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - if (tty_ldiscs[disc]->refcount) - ret = -EBUSY; - else - tty_ldiscs[disc] = NULL; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - - return ret; -} -EXPORT_SYMBOL(tty_unregister_ldisc); - -static struct tty_ldisc_ops *get_ldops(int disc) -{ - unsigned long flags; - struct tty_ldisc_ops *ldops, *ret; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ret = ERR_PTR(-EINVAL); - ldops = tty_ldiscs[disc]; - if (ldops) { - ret = ERR_PTR(-EAGAIN); - if (try_module_get(ldops->owner)) { - ldops->refcount++; - ret = ldops; - } - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return ret; -} - -static void put_ldops(struct tty_ldisc_ops *ldops) -{ - unsigned long flags; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ldops->refcount--; - module_put(ldops->owner); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); -} - -/** - * tty_ldisc_get - take a reference to an ldisc - * @disc: ldisc number - * - * Takes a reference to a line discipline. Deals with refcounts and - * module locking counts. Returns NULL if the discipline is not available. - * Returns a pointer to the discipline and bumps the ref count if it is - * available - * - * Locking: - * takes tty_ldisc_lock to guard against ldisc races - */ - -static struct tty_ldisc *tty_ldisc_get(int disc) -{ - struct tty_ldisc *ld; - struct tty_ldisc_ops *ldops; - - if (disc < N_TTY || disc >= NR_LDISCS) - return ERR_PTR(-EINVAL); - - /* - * Get the ldisc ops - we may need to request them to be loaded - * dynamically and try again. - */ - ldops = get_ldops(disc); - if (IS_ERR(ldops)) { - request_module("tty-ldisc-%d", disc); - ldops = get_ldops(disc); - if (IS_ERR(ldops)) - return ERR_CAST(ldops); - } - - ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); - if (ld == NULL) { - put_ldops(ldops); - return ERR_PTR(-ENOMEM); - } - - ld->ops = ldops; - atomic_set(&ld->users, 1); - return ld; -} - -static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) -{ - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void *tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) -{ - (*pos)++; - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) -{ -} - -static int tty_ldiscs_seq_show(struct seq_file *m, void *v) -{ - int i = *(loff_t *)v; - struct tty_ldisc_ops *ldops; - - ldops = get_ldops(i); - if (IS_ERR(ldops)) - return 0; - seq_printf(m, "%-10s %2d\n", ldops->name ? ldops->name : "???", i); - put_ldops(ldops); - return 0; -} - -static const struct seq_operations tty_ldiscs_seq_ops = { - .start = tty_ldiscs_seq_start, - .next = tty_ldiscs_seq_next, - .stop = tty_ldiscs_seq_stop, - .show = tty_ldiscs_seq_show, -}; - -static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &tty_ldiscs_seq_ops); -} - -const struct file_operations tty_ldiscs_proc_fops = { - .owner = THIS_MODULE, - .open = proc_tty_ldiscs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -/** - * tty_ldisc_assign - set ldisc on a tty - * @tty: tty to assign - * @ld: line discipline - * - * Install an instance of a line discipline into a tty structure. The - * ldisc must have a reference count above zero to ensure it remains. - * The tty instance refcount starts at zero. - * - * Locking: - * Caller must hold references - */ - -static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) -{ - tty->ldisc = ld; -} - -/** - * tty_ldisc_try - internal helper - * @tty: the tty - * - * Make a single attempt to grab and bump the refcount on - * the tty ldisc. Return 0 on failure or 1 on success. This is - * used to implement both the waiting and non waiting versions - * of tty_ldisc_ref - * - * Locking: takes tty_ldisc_lock - */ - -static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) -{ - unsigned long flags; - struct tty_ldisc *ld; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = NULL; - if (test_bit(TTY_LDISC, &tty->flags)) - ld = get_ldisc(tty->ldisc); - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return ld; -} - -/** - * tty_ldisc_ref_wait - wait for the tty ldisc - * @tty: tty device - * - * Dereference the line discipline for the terminal and take a - * reference to it. If the line discipline is in flux then - * wait patiently until it changes. - * - * Note: Must not be called from an IRQ/timer context. The caller - * must also be careful not to hold other locks that will deadlock - * against a discipline change, such as an existing ldisc reference - * (which we check for) - * - * Locking: call functions take tty_ldisc_lock - */ - -struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) -{ - struct tty_ldisc *ld; - - /* wait_event is a macro */ - wait_event(tty_ldisc_wait, (ld = tty_ldisc_try(tty)) != NULL); - return ld; -} -EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); - -/** - * tty_ldisc_ref - get the tty ldisc - * @tty: tty device - * - * Dereference the line discipline for the terminal and take a - * reference to it. If the line discipline is in flux then - * return NULL. Can be called from IRQ and timer functions. - * - * Locking: called functions take tty_ldisc_lock - */ - -struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) -{ - return tty_ldisc_try(tty); -} -EXPORT_SYMBOL_GPL(tty_ldisc_ref); - -/** - * tty_ldisc_deref - free a tty ldisc reference - * @ld: reference to free up - * - * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May - * be called in IRQ context. - * - * Locking: takes tty_ldisc_lock - */ - -void tty_ldisc_deref(struct tty_ldisc *ld) -{ - put_ldisc(ld); -} -EXPORT_SYMBOL_GPL(tty_ldisc_deref); - -static inline void tty_ldisc_put(struct tty_ldisc *ld) -{ - put_ldisc(ld); -} - -/** - * tty_ldisc_enable - allow ldisc use - * @tty: terminal to activate ldisc on - * - * Set the TTY_LDISC flag when the line discipline can be called - * again. Do necessary wakeups for existing sleepers. Clear the LDISC - * changing flag to indicate any ldisc change is now over. - * - * Note: nobody should set the TTY_LDISC bit except via this function. - * Clearing directly is allowed. - */ - -void tty_ldisc_enable(struct tty_struct *tty) -{ - set_bit(TTY_LDISC, &tty->flags); - clear_bit(TTY_LDISC_CHANGING, &tty->flags); - wake_up(&tty_ldisc_wait); -} - -/** - * tty_ldisc_flush - flush line discipline queue - * @tty: tty - * - * Flush the line discipline queue (if any) for this tty. If there - * is no line discipline active this is a no-op. - */ - -void tty_ldisc_flush(struct tty_struct *tty) -{ - struct tty_ldisc *ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->ops->flush_buffer) - ld->ops->flush_buffer(tty); - tty_ldisc_deref(ld); - } - tty_buffer_flush(tty); -} -EXPORT_SYMBOL_GPL(tty_ldisc_flush); - -/** - * tty_set_termios_ldisc - set ldisc field - * @tty: tty structure - * @num: line discipline number - * - * This is probably overkill for real world processors but - * they are not on hot paths so a little discipline won't do - * any harm. - * - * Locking: takes termios_mutex - */ - -static void tty_set_termios_ldisc(struct tty_struct *tty, int num) -{ - mutex_lock(&tty->termios_mutex); - tty->termios->c_line = num; - mutex_unlock(&tty->termios_mutex); -} - -/** - * tty_ldisc_open - open a line discipline - * @tty: tty we are opening the ldisc on - * @ld: discipline to open - * - * A helper opening method. Also a convenient debugging and check - * point. - * - * Locking: always called with BTM already held. - */ - -static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) -{ - WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags)); - if (ld->ops->open) { - int ret; - /* BTM here locks versus a hangup event */ - WARN_ON(!tty_locked()); - ret = ld->ops->open(tty); - return ret; - } - return 0; -} - -/** - * tty_ldisc_close - close a line discipline - * @tty: tty we are opening the ldisc on - * @ld: discipline to close - * - * A helper close method. Also a convenient debugging and check - * point. - */ - -static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) -{ - WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags)); - clear_bit(TTY_LDISC_OPEN, &tty->flags); - if (ld->ops->close) - ld->ops->close(tty); -} - -/** - * tty_ldisc_restore - helper for tty ldisc change - * @tty: tty to recover - * @old: previous ldisc - * - * Restore the previous line discipline or N_TTY when a line discipline - * change fails due to an open error - */ - -static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) -{ - char buf[64]; - struct tty_ldisc *new_ldisc; - int r; - - /* There is an outstanding reference here so this is safe */ - old = tty_ldisc_get(old->ops->num); - WARN_ON(IS_ERR(old)); - tty_ldisc_assign(tty, old); - tty_set_termios_ldisc(tty, old->ops->num); - if (tty_ldisc_open(tty, old) < 0) { - tty_ldisc_put(old); - /* This driver is always present */ - new_ldisc = tty_ldisc_get(N_TTY); - if (IS_ERR(new_ldisc)) - panic("n_tty: get"); - tty_ldisc_assign(tty, new_ldisc); - tty_set_termios_ldisc(tty, N_TTY); - r = tty_ldisc_open(tty, new_ldisc); - if (r < 0) - panic("Couldn't open N_TTY ldisc for " - "%s --- error %d.", - tty_name(tty, buf), r); - } -} - -/** - * tty_ldisc_halt - shut down the line discipline - * @tty: tty device - * - * Shut down the line discipline and work queue for this tty device. - * The TTY_LDISC flag being cleared ensures no further references can - * be obtained while the delayed work queue halt ensures that no more - * data is fed to the ldisc. - * - * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex) - * in order to make sure any currently executing ldisc work is also - * flushed. - */ - -static int tty_ldisc_halt(struct tty_struct *tty) -{ - clear_bit(TTY_LDISC, &tty->flags); - return cancel_delayed_work_sync(&tty->buf.work); -} - -/** - * tty_set_ldisc - set line discipline - * @tty: the terminal to set - * @ldisc: the line discipline - * - * Set the discipline of a tty line. Must be called from a process - * context. The ldisc change logic has to protect itself against any - * overlapping ldisc change (including on the other end of pty pairs), - * the close of one side of a tty/pty pair, and eventually hangup. - * - * Locking: takes tty_ldisc_lock, termios_mutex - */ - -int tty_set_ldisc(struct tty_struct *tty, int ldisc) -{ - int retval; - struct tty_ldisc *o_ldisc, *new_ldisc; - int work, o_work = 0; - struct tty_struct *o_tty; - - new_ldisc = tty_ldisc_get(ldisc); - if (IS_ERR(new_ldisc)) - return PTR_ERR(new_ldisc); - - tty_lock(); - /* - * We need to look at the tty locking here for pty/tty pairs - * when both sides try to change in parallel. - */ - - o_tty = tty->link; /* o_tty is the pty side or NULL */ - - - /* - * Check the no-op case - */ - - if (tty->ldisc->ops->num == ldisc) { - tty_unlock(); - tty_ldisc_put(new_ldisc); - return 0; - } - - tty_unlock(); - /* - * Problem: What do we do if this blocks ? - * We could deadlock here - */ - - tty_wait_until_sent(tty, 0); - - tty_lock(); - mutex_lock(&tty->ldisc_mutex); - - /* - * We could be midstream of another ldisc change which has - * dropped the lock during processing. If so we need to wait. - */ - - while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { - mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); - wait_event(tty_ldisc_wait, - test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(); - mutex_lock(&tty->ldisc_mutex); - } - - set_bit(TTY_LDISC_CHANGING, &tty->flags); - - /* - * No more input please, we are switching. The new ldisc - * will update this value in the ldisc open function - */ - - tty->receive_room = 0; - - o_ldisc = tty->ldisc; - - tty_unlock(); - /* - * Make sure we don't change while someone holds a - * reference to the line discipline. The TTY_LDISC bit - * prevents anyone taking a reference once it is clear. - * We need the lock to avoid racing reference takers. - * - * We must clear the TTY_LDISC bit here to avoid a livelock - * with a userspace app continually trying to use the tty in - * parallel to the change and re-referencing the tty. - */ - - work = tty_ldisc_halt(tty); - if (o_tty) - o_work = tty_ldisc_halt(o_tty); - - /* - * Wait for ->hangup_work and ->buf.work handlers to terminate. - * We must drop the mutex here in case a hangup is also in process. - */ - - mutex_unlock(&tty->ldisc_mutex); - - flush_scheduled_work(); - - tty_lock(); - mutex_lock(&tty->ldisc_mutex); - if (test_bit(TTY_HUPPED, &tty->flags)) { - /* We were raced by the hangup method. It will have stomped - the ldisc data and closed the ldisc down */ - clear_bit(TTY_LDISC_CHANGING, &tty->flags); - mutex_unlock(&tty->ldisc_mutex); - tty_ldisc_put(new_ldisc); - tty_unlock(); - return -EIO; - } - - /* Shutdown the current discipline. */ - tty_ldisc_close(tty, o_ldisc); - - /* Now set up the new line discipline. */ - tty_ldisc_assign(tty, new_ldisc); - tty_set_termios_ldisc(tty, ldisc); - - retval = tty_ldisc_open(tty, new_ldisc); - if (retval < 0) { - /* Back to the old one or N_TTY if we can't */ - tty_ldisc_put(new_ldisc); - tty_ldisc_restore(tty, o_ldisc); - } - - /* At this point we hold a reference to the new ldisc and a - a reference to the old ldisc. If we ended up flipping back - to the existing ldisc we have two references to it */ - - if (tty->ldisc->ops->num != o_ldisc->ops->num && tty->ops->set_ldisc) - tty->ops->set_ldisc(tty); - - tty_ldisc_put(o_ldisc); - - /* - * Allow ldisc referencing to occur again - */ - - tty_ldisc_enable(tty); - if (o_tty) - tty_ldisc_enable(o_tty); - - /* Restart the work queue in case no characters kick it off. Safe if - already running */ - if (work) - schedule_delayed_work(&tty->buf.work, 1); - if (o_work) - schedule_delayed_work(&o_tty->buf.work, 1); - mutex_unlock(&tty->ldisc_mutex); - tty_unlock(); - return retval; -} - -/** - * tty_reset_termios - reset terminal state - * @tty: tty to reset - * - * Restore a terminal to the driver default state. - */ - -static void tty_reset_termios(struct tty_struct *tty) -{ - mutex_lock(&tty->termios_mutex); - *tty->termios = tty->driver->init_termios; - tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); - tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); - mutex_unlock(&tty->termios_mutex); -} - - -/** - * tty_ldisc_reinit - reinitialise the tty ldisc - * @tty: tty to reinit - * @ldisc: line discipline to reinitialize - * - * Switch the tty to a line discipline and leave the ldisc - * state closed - */ - -static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc) -{ - struct tty_ldisc *ld; - - tty_ldisc_close(tty, tty->ldisc); - tty_ldisc_put(tty->ldisc); - tty->ldisc = NULL; - /* - * Switch the line discipline back - */ - ld = tty_ldisc_get(ldisc); - BUG_ON(IS_ERR(ld)); - tty_ldisc_assign(tty, ld); - tty_set_termios_ldisc(tty, ldisc); -} - -/** - * tty_ldisc_hangup - hangup ldisc reset - * @tty: tty being hung up - * - * Some tty devices reset their termios when they receive a hangup - * event. In that situation we must also switch back to N_TTY properly - * before we reset the termios data. - * - * Locking: We can take the ldisc mutex as the rest of the code is - * careful to allow for this. - * - * In the pty pair case this occurs in the close() path of the - * tty itself so we must be careful about locking rules. - */ - -void tty_ldisc_hangup(struct tty_struct *tty) -{ - struct tty_ldisc *ld; - int reset = tty->driver->flags & TTY_DRIVER_RESET_TERMIOS; - int err = 0; - - /* - * FIXME! What are the locking issues here? This may me overdoing - * things... This question is especially important now that we've - * removed the irqlock. - */ - ld = tty_ldisc_ref(tty); - if (ld != NULL) { - /* We may have no line discipline at this point */ - if (ld->ops->flush_buffer) - ld->ops->flush_buffer(tty); - tty_driver_flush_buffer(tty); - if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && - ld->ops->write_wakeup) - ld->ops->write_wakeup(tty); - if (ld->ops->hangup) - ld->ops->hangup(tty); - tty_ldisc_deref(ld); - } - /* - * FIXME: Once we trust the LDISC code better we can wait here for - * ldisc completion and fix the driver call race - */ - wake_up_interruptible_poll(&tty->write_wait, POLLOUT); - wake_up_interruptible_poll(&tty->read_wait, POLLIN); - /* - * Shutdown the current line discipline, and reset it to - * N_TTY if need be. - * - * Avoid racing set_ldisc or tty_ldisc_release - */ - mutex_lock(&tty->ldisc_mutex); - - /* - * this is like tty_ldisc_halt, but we need to give up - * the BTM before calling cancel_delayed_work_sync, - * which may need to wait for another function taking the BTM - */ - clear_bit(TTY_LDISC, &tty->flags); - tty_unlock(); - cancel_delayed_work_sync(&tty->buf.work); - mutex_unlock(&tty->ldisc_mutex); - - tty_lock(); - mutex_lock(&tty->ldisc_mutex); - - /* At this point we have a closed ldisc and we want to - reopen it. We could defer this to the next open but - it means auditing a lot of other paths so this is - a FIXME */ - if (tty->ldisc) { /* Not yet closed */ - if (reset == 0) { - tty_ldisc_reinit(tty, tty->termios->c_line); - err = tty_ldisc_open(tty, tty->ldisc); - } - /* If the re-open fails or we reset then go to N_TTY. The - N_TTY open cannot fail */ - if (reset || err) { - tty_ldisc_reinit(tty, N_TTY); - WARN_ON(tty_ldisc_open(tty, tty->ldisc)); - } - tty_ldisc_enable(tty); - } - mutex_unlock(&tty->ldisc_mutex); - if (reset) - tty_reset_termios(tty); -} - -/** - * tty_ldisc_setup - open line discipline - * @tty: tty being shut down - * @o_tty: pair tty for pty/tty pairs - * - * Called during the initial open of a tty/pty pair in order to set up the - * line disciplines and bind them to the tty. This has no locking issues - * as the device isn't yet active. - */ - -int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) -{ - struct tty_ldisc *ld = tty->ldisc; - int retval; - - retval = tty_ldisc_open(tty, ld); - if (retval) - return retval; - - if (o_tty) { - retval = tty_ldisc_open(o_tty, o_tty->ldisc); - if (retval) { - tty_ldisc_close(tty, ld); - return retval; - } - tty_ldisc_enable(o_tty); - } - tty_ldisc_enable(tty); - return 0; -} -/** - * tty_ldisc_release - release line discipline - * @tty: tty being shut down - * @o_tty: pair tty for pty/tty pairs - * - * Called during the final close of a tty/pty pair in order to shut down - * the line discpline layer. On exit the ldisc assigned is N_TTY and the - * ldisc has not been opened. - */ - -void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) -{ - /* - * Prevent flush_to_ldisc() from rescheduling the work for later. Then - * kill any delayed work. As this is the final close it does not - * race with the set_ldisc code path. - */ - - tty_unlock(); - tty_ldisc_halt(tty); - flush_scheduled_work(); - tty_lock(); - - mutex_lock(&tty->ldisc_mutex); - /* - * Now kill off the ldisc - */ - tty_ldisc_close(tty, tty->ldisc); - tty_ldisc_put(tty->ldisc); - /* Force an oops if we mess this up */ - tty->ldisc = NULL; - - /* Ensure the next open requests the N_TTY ldisc */ - tty_set_termios_ldisc(tty, N_TTY); - mutex_unlock(&tty->ldisc_mutex); - - /* This will need doing differently if we need to lock */ - if (o_tty) - tty_ldisc_release(o_tty, NULL); - - /* And the memory resources remaining (buffers, termios) will be - disposed of when the kref hits zero */ -} - -/** - * tty_ldisc_init - ldisc setup for new tty - * @tty: tty being allocated - * - * Set up the line discipline objects for a newly allocated tty. Note that - * the tty structure is not completely set up when this call is made. - */ - -void tty_ldisc_init(struct tty_struct *tty) -{ - struct tty_ldisc *ld = tty_ldisc_get(N_TTY); - if (IS_ERR(ld)) - panic("n_tty: init_tty"); - tty_ldisc_assign(tty, ld); -} - -void tty_ldisc_begin(void) -{ - /* Setup the default TTY line discipline. */ - (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); -} diff --git a/drivers/char/tty_mutex.c b/drivers/char/tty_mutex.c deleted file mode 100644 index 133697540c73..000000000000 --- a/drivers/char/tty_mutex.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * drivers/char/tty_lock.c - */ -#include -#include -#include -#include -#include - -/* - * The 'big tty mutex' - * - * This mutex is taken and released by tty_lock() and tty_unlock(), - * replacing the older big kernel lock. - * It can no longer be taken recursively, and does not get - * released implicitly while sleeping. - * - * Don't use in new code. - */ -static DEFINE_MUTEX(big_tty_mutex); -struct task_struct *__big_tty_mutex_owner; -EXPORT_SYMBOL_GPL(__big_tty_mutex_owner); - -/* - * Getting the big tty mutex. - */ -void __lockfunc tty_lock(void) -{ - struct task_struct *task = current; - - WARN_ON(__big_tty_mutex_owner == task); - - mutex_lock(&big_tty_mutex); - __big_tty_mutex_owner = task; -} -EXPORT_SYMBOL(tty_lock); - -void __lockfunc tty_unlock(void) -{ - struct task_struct *task = current; - - WARN_ON(__big_tty_mutex_owner != task); - __big_tty_mutex_owner = NULL; - - mutex_unlock(&big_tty_mutex); -} -EXPORT_SYMBOL(tty_unlock); diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c deleted file mode 100644 index 33d37d230f8f..000000000000 --- a/drivers/char/tty_port.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Tty port functions - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void tty_port_init(struct tty_port *port) -{ - memset(port, 0, sizeof(*port)); - init_waitqueue_head(&port->open_wait); - init_waitqueue_head(&port->close_wait); - init_waitqueue_head(&port->delta_msr_wait); - mutex_init(&port->mutex); - mutex_init(&port->buf_mutex); - spin_lock_init(&port->lock); - port->close_delay = (50 * HZ) / 100; - port->closing_wait = (3000 * HZ) / 100; - kref_init(&port->kref); -} -EXPORT_SYMBOL(tty_port_init); - -int tty_port_alloc_xmit_buf(struct tty_port *port) -{ - /* We may sleep in get_zeroed_page() */ - mutex_lock(&port->buf_mutex); - if (port->xmit_buf == NULL) - port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); - mutex_unlock(&port->buf_mutex); - if (port->xmit_buf == NULL) - return -ENOMEM; - return 0; -} -EXPORT_SYMBOL(tty_port_alloc_xmit_buf); - -void tty_port_free_xmit_buf(struct tty_port *port) -{ - mutex_lock(&port->buf_mutex); - if (port->xmit_buf != NULL) { - free_page((unsigned long)port->xmit_buf); - port->xmit_buf = NULL; - } - mutex_unlock(&port->buf_mutex); -} -EXPORT_SYMBOL(tty_port_free_xmit_buf); - -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); - if (port->ops->destruct) - port->ops->destruct(port); - else - kfree(port); -} - -void tty_port_put(struct tty_port *port) -{ - if (port) - kref_put(&port->kref, tty_port_destructor); -} -EXPORT_SYMBOL(tty_port_put); - -/** - * tty_port_tty_get - get a tty reference - * @port: tty port - * - * Return a refcount protected tty instance or NULL if the port is not - * associated with a tty (eg due to close or hangup) - */ - -struct tty_struct *tty_port_tty_get(struct tty_port *port) -{ - unsigned long flags; - struct tty_struct *tty; - - spin_lock_irqsave(&port->lock, flags); - tty = tty_kref_get(port->tty); - spin_unlock_irqrestore(&port->lock, flags); - return tty; -} -EXPORT_SYMBOL(tty_port_tty_get); - -/** - * tty_port_tty_set - set the tty of a port - * @port: tty port - * @tty: the tty - * - * Associate the port and tty pair. Manages any internal refcounts. - * Pass NULL to deassociate a port - */ - -void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) -{ - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - if (port->tty) - tty_kref_put(port->tty); - port->tty = tty_kref_get(tty); - spin_unlock_irqrestore(&port->lock, flags); -} -EXPORT_SYMBOL(tty_port_tty_set); - -static void tty_port_shutdown(struct tty_port *port) -{ - mutex_lock(&port->mutex); - if (port->ops->shutdown && !port->console && - test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) - port->ops->shutdown(port); - mutex_unlock(&port->mutex); -} - -/** - * tty_port_hangup - hangup helper - * @port: tty port - * - * Perform port level tty hangup flag and count changes. Drop the tty - * reference. - */ - -void tty_port_hangup(struct tty_port *port) -{ - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - port->count = 0; - port->flags &= ~ASYNC_NORMAL_ACTIVE; - if (port->tty) { - set_bit(TTY_IO_ERROR, &port->tty->flags); - tty_kref_put(port->tty); - } - port->tty = NULL; - spin_unlock_irqrestore(&port->lock, flags); - wake_up_interruptible(&port->open_wait); - wake_up_interruptible(&port->delta_msr_wait); - tty_port_shutdown(port); -} -EXPORT_SYMBOL(tty_port_hangup); - -/** - * tty_port_carrier_raised - carrier raised check - * @port: tty port - * - * Wrapper for the carrier detect logic. For the moment this is used - * to hide some internal details. This will eventually become entirely - * internal to the tty port. - */ - -int tty_port_carrier_raised(struct tty_port *port) -{ - if (port->ops->carrier_raised == NULL) - return 1; - return port->ops->carrier_raised(port); -} -EXPORT_SYMBOL(tty_port_carrier_raised); - -/** - * tty_port_raise_dtr_rts - Raise DTR/RTS - * @port: tty port - * - * Wrapper for the DTR/RTS raise logic. For the moment this is used - * to hide some internal details. This will eventually become entirely - * internal to the tty port. - */ - -void tty_port_raise_dtr_rts(struct tty_port *port) -{ - if (port->ops->dtr_rts) - port->ops->dtr_rts(port, 1); -} -EXPORT_SYMBOL(tty_port_raise_dtr_rts); - -/** - * tty_port_lower_dtr_rts - Lower DTR/RTS - * @port: tty port - * - * Wrapper for the DTR/RTS raise logic. For the moment this is used - * to hide some internal details. This will eventually become entirely - * internal to the tty port. - */ - -void tty_port_lower_dtr_rts(struct tty_port *port) -{ - if (port->ops->dtr_rts) - port->ops->dtr_rts(port, 0); -} -EXPORT_SYMBOL(tty_port_lower_dtr_rts); - -/** - * tty_port_block_til_ready - Waiting logic for tty open - * @port: the tty port being opened - * @tty: the tty device being bound - * @filp: the file pointer of the opener - * - * Implement the core POSIX/SuS tty behaviour when opening a tty device. - * Handles: - * - hangup (both before and during) - * - non blocking open - * - rts/dtr/dcd - * - signals - * - port flags and counts - * - * The passed tty_port must implement the carrier_raised method if it can - * do carrier detect and the dtr_rts method if it supports software - * management of these lines. Note that the dtr/rts raise is done each - * iteration as a hangup may have previously dropped them while we wait. - */ - -int tty_port_block_til_ready(struct tty_port *port, - struct tty_struct *tty, struct file *filp) -{ - int do_clocal = 0, retval; - unsigned long flags; - DEFINE_WAIT(wait); - int cd; - - /* block if port is in the process of being closed */ - if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(port->close_wait, - !(port->flags & ASYNC_CLOSING)); - if (port->flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; - } - - /* if non-blocking mode is set we can pass directly to open unless - the port has just hung up or is in another error state */ - if (tty->flags & (1 << TTY_IO_ERROR)) { - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - if (filp->f_flags & O_NONBLOCK) { - /* Indicate we are open */ - if (tty->termios->c_cflag & CBAUD) - tty_port_raise_dtr_rts(port); - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (C_CLOCAL(tty)) - do_clocal = 1; - - /* Block waiting until we can proceed. We may need to wait for the - carrier, but we must also wait for any close that is in progress - before the next open may complete */ - - retval = 0; - - /* The port lock protects the port counts */ - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count--; - port->blocked_open++; - spin_unlock_irqrestore(&port->lock, flags); - - while (1) { - /* Indicate we are open */ - if (tty->termios->c_cflag & CBAUD) - tty_port_raise_dtr_rts(port); - - prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE); - /* Check for a hangup or uninitialised port. - Return accordingly */ - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; - break; - } - /* Probe the carrier. For devices with no carrier detect this - will always return true */ - cd = tty_port_carrier_raised(port); - if (!(port->flags & ASYNC_CLOSING) && - (do_clocal || cd)) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - tty_unlock(); - schedule(); - tty_lock(); - } - finish_wait(&port->open_wait, &wait); - - /* Update counts. A parallel hangup will have set count to zero and - we must not mess that up further */ - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - if (retval == 0) - port->flags |= ASYNC_NORMAL_ACTIVE; - spin_unlock_irqrestore(&port->lock, flags); - return retval; -} -EXPORT_SYMBOL(tty_port_block_til_ready); - -int tty_port_close_start(struct tty_port *port, - struct tty_struct *tty, struct file *filp) -{ - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&port->lock, flags); - return 0; - } - - if (tty->count == 1 && port->count != 1) { - printk(KERN_WARNING - "tty_port_close_start: tty->count = 1 port count = %d.\n", - port->count); - port->count = 1; - } - if (--port->count < 0) { - printk(KERN_WARNING "tty_port_close_start: count = %d\n", - port->count); - port->count = 0; - } - - if (port->count) { - spin_unlock_irqrestore(&port->lock, flags); - if (port->ops->drop) - port->ops->drop(port); - return 0; - } - set_bit(ASYNCB_CLOSING, &port->flags); - tty->closing = 1; - spin_unlock_irqrestore(&port->lock, flags); - /* Don't block on a stalled port, just pull the chain */ - if (tty->flow_stopped) - tty_driver_flush_buffer(tty); - if (test_bit(ASYNCB_INITIALIZED, &port->flags) && - port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, port->closing_wait); - if (port->drain_delay) { - unsigned int bps = tty_get_baud_rate(tty); - long timeout; - - if (bps > 1200) - timeout = max_t(long, - (HZ * 10 * port->drain_delay) / bps, HZ / 10); - else - timeout = 2 * HZ; - schedule_timeout_interruptible(timeout); - } - /* Flush the ldisc buffering */ - tty_ldisc_flush(tty); - - /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to - hang up the line */ - if (tty->termios->c_cflag & HUPCL) - tty_port_lower_dtr_rts(port); - - /* Don't call port->drop for the last reference. Callers will want - to drop the last active reference in ->shutdown() or the tty - shutdown path */ - return 1; -} -EXPORT_SYMBOL(tty_port_close_start); - -void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) -{ - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - tty->closing = 0; - - if (port->blocked_open) { - spin_unlock_irqrestore(&port->lock, flags); - if (port->close_delay) { - msleep_interruptible( - jiffies_to_msecs(port->close_delay)); - } - spin_lock_irqsave(&port->lock, flags); - wake_up_interruptible(&port->open_wait); - } - port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - wake_up_interruptible(&port->close_wait); - spin_unlock_irqrestore(&port->lock, flags); -} -EXPORT_SYMBOL(tty_port_close_end); - -void tty_port_close(struct tty_port *port, struct tty_struct *tty, - struct file *filp) -{ - if (tty_port_close_start(port, tty, filp) == 0) - return; - tty_port_shutdown(port); - set_bit(TTY_IO_ERROR, &tty->flags); - tty_port_close_end(port, tty); - tty_port_tty_set(port, NULL); -} -EXPORT_SYMBOL(tty_port_close); - -int tty_port_open(struct tty_port *port, struct tty_struct *tty, - struct file *filp) -{ - spin_lock_irq(&port->lock); - if (!tty_hung_up_p(filp)) - ++port->count; - spin_unlock_irq(&port->lock); - tty_port_tty_set(port, tty); - - /* - * Do the device-specific open only if the hardware isn't - * already initialized. Serialize open and shutdown using the - * port mutex. - */ - - mutex_lock(&port->mutex); - - if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) { - clear_bit(TTY_IO_ERROR, &tty->flags); - if (port->ops->activate) { - int retval = port->ops->activate(port, tty); - if (retval) { - mutex_unlock(&port->mutex); - return retval; - } - } - set_bit(ASYNCB_INITIALIZED, &port->flags); - } - mutex_unlock(&port->mutex); - return tty_port_block_til_ready(port, tty, filp); -} - -EXPORT_SYMBOL(tty_port_open); -- cgit v1.2.1 From 60d4ae8d436b8be6a8aedb63440203d5395e9f53 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 4 Nov 2010 12:50:47 -0700 Subject: TTY: create drivers/tty/vt and move the vt code there The vt and other related code is moved into the drivers/tty/vt directory. Acked-by: Arnd Bergmann Cc: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/Makefile | 33 - drivers/char/consolemap.c | 745 ------- drivers/char/cp437.uni | 291 --- drivers/char/defkeymap.c_shipped | 262 --- drivers/char/defkeymap.map | 357 ---- drivers/char/keyboard.c | 1454 ------------- drivers/char/selection.c | 348 ---- drivers/char/vc_screen.c | 644 ------ drivers/char/vt.c | 4209 -------------------------------------- drivers/char/vt_ioctl.c | 1788 ---------------- 10 files changed, 10131 deletions(-) delete mode 100644 drivers/char/consolemap.c delete mode 100644 drivers/char/cp437.uni delete mode 100644 drivers/char/defkeymap.c_shipped delete mode 100644 drivers/char/defkeymap.map delete mode 100644 drivers/char/keyboard.c delete mode 100644 drivers/char/selection.c delete mode 100644 drivers/char/vc_screen.c delete mode 100644 drivers/char/vt.c delete mode 100644 drivers/char/vt_ioctl.c (limited to 'drivers/char') diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f308494bfc90..ba53ec956c95 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -2,18 +2,10 @@ # Makefile for the kernel character device drivers. # -# -# This file contains the font map for the default (hardware) font -# -FONTMAPFILE = cp437.uni - obj-y += mem.o random.o obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o obj-y += misc.o -obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o -obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o -obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o @@ -106,28 +98,3 @@ obj-$(CONFIG_RAMOOPS) += ramoops.o obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.o - -# Files generated that shall be removed upon make clean -clean-files := consolemap_deftbl.c defkeymap.c - -quiet_cmd_conmk = CONMK $@ - cmd_conmk = scripts/conmakehash $< > $@ - -$(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE) - $(call cmd,conmk) - -$(obj)/defkeymap.o: $(obj)/defkeymap.c - -# Uncomment if you're changing the keymap and have an appropriate -# loadkeys version for the map. By default, we'll use the shipped -# versions. -# GENERATE_KEYMAP := 1 - -ifdef GENERATE_KEYMAP - -$(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map - loadkeys --mktable $< > $@.tmp - sed -e 's/^static *//' $@.tmp > $@ - rm $@.tmp - -endif diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c deleted file mode 100644 index 45d3e80156d4..000000000000 --- a/drivers/char/consolemap.c +++ /dev/null @@ -1,745 +0,0 @@ -/* - * consolemap.c - * - * Mapping from internal code (such as Latin-1 or Unicode or IBM PC code) - * to font positions. - * - * aeb, 950210 - * - * Support for multiple unimaps by Jakub Jelinek , July 1998 - * - * Fix bug in inverse translation. Stanislav Voronyi , Dec 1998 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static unsigned short translations[][256] = { - /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ - { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, - 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, - 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, - 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, - 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, - 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, - 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, - 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, - 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, - 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, - 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, - 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, - 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff - }, - /* VT100 graphics mapped to Unicode */ - { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002a, 0x2192, 0x2190, 0x2191, 0x2193, 0x002f, - 0x2588, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0, - 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, - 0x2591, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, - 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, - 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f, - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, - 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, - 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, - 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, - 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, - 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, - 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, - 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, - 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, - 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, - 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, - 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, - 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff - }, - /* IBM Codepage 437 mapped to Unicode */ - { - 0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, - 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c, - 0x25b6, 0x25c0, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8, - 0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, - 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, - 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, - 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, - 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, - 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, - 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, - 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, - 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, - 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, - 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, - 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, - 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, - 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, - 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, - 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, - 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 - }, - /* User mapping -- default to codes for direct font mapping */ - { - 0xf000, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007, - 0xf008, 0xf009, 0xf00a, 0xf00b, 0xf00c, 0xf00d, 0xf00e, 0xf00f, - 0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017, - 0xf018, 0xf019, 0xf01a, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, - 0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027, - 0xf028, 0xf029, 0xf02a, 0xf02b, 0xf02c, 0xf02d, 0xf02e, 0xf02f, - 0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, - 0xf038, 0xf039, 0xf03a, 0xf03b, 0xf03c, 0xf03d, 0xf03e, 0xf03f, - 0xf040, 0xf041, 0xf042, 0xf043, 0xf044, 0xf045, 0xf046, 0xf047, - 0xf048, 0xf049, 0xf04a, 0xf04b, 0xf04c, 0xf04d, 0xf04e, 0xf04f, - 0xf050, 0xf051, 0xf052, 0xf053, 0xf054, 0xf055, 0xf056, 0xf057, - 0xf058, 0xf059, 0xf05a, 0xf05b, 0xf05c, 0xf05d, 0xf05e, 0xf05f, - 0xf060, 0xf061, 0xf062, 0xf063, 0xf064, 0xf065, 0xf066, 0xf067, - 0xf068, 0xf069, 0xf06a, 0xf06b, 0xf06c, 0xf06d, 0xf06e, 0xf06f, - 0xf070, 0xf071, 0xf072, 0xf073, 0xf074, 0xf075, 0xf076, 0xf077, - 0xf078, 0xf079, 0xf07a, 0xf07b, 0xf07c, 0xf07d, 0xf07e, 0xf07f, - 0xf080, 0xf081, 0xf082, 0xf083, 0xf084, 0xf085, 0xf086, 0xf087, - 0xf088, 0xf089, 0xf08a, 0xf08b, 0xf08c, 0xf08d, 0xf08e, 0xf08f, - 0xf090, 0xf091, 0xf092, 0xf093, 0xf094, 0xf095, 0xf096, 0xf097, - 0xf098, 0xf099, 0xf09a, 0xf09b, 0xf09c, 0xf09d, 0xf09e, 0xf09f, - 0xf0a0, 0xf0a1, 0xf0a2, 0xf0a3, 0xf0a4, 0xf0a5, 0xf0a6, 0xf0a7, - 0xf0a8, 0xf0a9, 0xf0aa, 0xf0ab, 0xf0ac, 0xf0ad, 0xf0ae, 0xf0af, - 0xf0b0, 0xf0b1, 0xf0b2, 0xf0b3, 0xf0b4, 0xf0b5, 0xf0b6, 0xf0b7, - 0xf0b8, 0xf0b9, 0xf0ba, 0xf0bb, 0xf0bc, 0xf0bd, 0xf0be, 0xf0bf, - 0xf0c0, 0xf0c1, 0xf0c2, 0xf0c3, 0xf0c4, 0xf0c5, 0xf0c6, 0xf0c7, - 0xf0c8, 0xf0c9, 0xf0ca, 0xf0cb, 0xf0cc, 0xf0cd, 0xf0ce, 0xf0cf, - 0xf0d0, 0xf0d1, 0xf0d2, 0xf0d3, 0xf0d4, 0xf0d5, 0xf0d6, 0xf0d7, - 0xf0d8, 0xf0d9, 0xf0da, 0xf0db, 0xf0dc, 0xf0dd, 0xf0de, 0xf0df, - 0xf0e0, 0xf0e1, 0xf0e2, 0xf0e3, 0xf0e4, 0xf0e5, 0xf0e6, 0xf0e7, - 0xf0e8, 0xf0e9, 0xf0ea, 0xf0eb, 0xf0ec, 0xf0ed, 0xf0ee, 0xf0ef, - 0xf0f0, 0xf0f1, 0xf0f2, 0xf0f3, 0xf0f4, 0xf0f5, 0xf0f6, 0xf0f7, - 0xf0f8, 0xf0f9, 0xf0fa, 0xf0fb, 0xf0fc, 0xf0fd, 0xf0fe, 0xf0ff - } -}; - -/* The standard kernel character-to-font mappings are not invertible - -- this is just a best effort. */ - -#define MAX_GLYPH 512 /* Max possible glyph value */ - -static int inv_translate[MAX_NR_CONSOLES]; - -struct uni_pagedir { - u16 **uni_pgdir[32]; - unsigned long refcount; - unsigned long sum; - unsigned char *inverse_translations[4]; - u16 *inverse_trans_unicode; - int readonly; -}; - -static struct uni_pagedir *dflt; - -static void set_inverse_transl(struct vc_data *conp, struct uni_pagedir *p, int i) -{ - int j, glyph; - unsigned short *t = translations[i]; - unsigned char *q; - - if (!p) return; - q = p->inverse_translations[i]; - - if (!q) { - q = p->inverse_translations[i] = (unsigned char *) - kmalloc(MAX_GLYPH, GFP_KERNEL); - if (!q) return; - } - memset(q, 0, MAX_GLYPH); - - for (j = 0; j < E_TABSZ; j++) { - glyph = conv_uni_to_pc(conp, t[j]); - if (glyph >= 0 && glyph < MAX_GLYPH && q[glyph] < 32) { - /* prefer '-' above SHY etc. */ - q[glyph] = j; - } - } -} - -static void set_inverse_trans_unicode(struct vc_data *conp, - struct uni_pagedir *p) -{ - int i, j, k, glyph; - u16 **p1, *p2; - u16 *q; - - if (!p) return; - q = p->inverse_trans_unicode; - if (!q) { - q = p->inverse_trans_unicode = - kmalloc(MAX_GLYPH * sizeof(u16), GFP_KERNEL); - if (!q) - return; - } - memset(q, 0, MAX_GLYPH * sizeof(u16)); - - for (i = 0; i < 32; i++) { - p1 = p->uni_pgdir[i]; - if (!p1) - continue; - for (j = 0; j < 32; j++) { - p2 = p1[j]; - if (!p2) - continue; - for (k = 0; k < 64; k++) { - glyph = p2[k]; - if (glyph >= 0 && glyph < MAX_GLYPH - && q[glyph] < 32) - q[glyph] = (i << 11) + (j << 6) + k; - } - } - } -} - -unsigned short *set_translate(int m, struct vc_data *vc) -{ - inv_translate[vc->vc_num] = m; - return translations[m]; -} - -/* - * Inverse translation is impossible for several reasons: - * 1. The font<->character maps are not 1-1. - * 2. The text may have been written while a different translation map - * was active. - * Still, it is now possible to a certain extent to cut and paste non-ASCII. - */ -u16 inverse_translate(struct vc_data *conp, int glyph, int use_unicode) -{ - struct uni_pagedir *p; - int m; - if (glyph < 0 || glyph >= MAX_GLYPH) - return 0; - else if (!(p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc)) - return glyph; - else if (use_unicode) { - if (!p->inverse_trans_unicode) - return glyph; - else - return p->inverse_trans_unicode[glyph]; - } else { - m = inv_translate[conp->vc_num]; - if (!p->inverse_translations[m]) - return glyph; - else - return p->inverse_translations[m][glyph]; - } -} -EXPORT_SYMBOL_GPL(inverse_translate); - -static void update_user_maps(void) -{ - int i; - struct uni_pagedir *p, *q = NULL; - - for (i = 0; i < MAX_NR_CONSOLES; i++) { - if (!vc_cons_allocated(i)) - continue; - p = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; - if (p && p != q) { - set_inverse_transl(vc_cons[i].d, p, USER_MAP); - set_inverse_trans_unicode(vc_cons[i].d, p); - q = p; - } - } -} - -/* - * Load customizable translation table - * arg points to a 256 byte translation table. - * - * The "old" variants are for translation directly to font (using the - * 0xf000-0xf0ff "transparent" Unicodes) whereas the "new" variants set - * Unicodes explicitly. - */ -int con_set_trans_old(unsigned char __user * arg) -{ - int i; - unsigned short *p = translations[USER_MAP]; - - if (!access_ok(VERIFY_READ, arg, E_TABSZ)) - return -EFAULT; - - for (i=0; i current font conversion - * - * A font has at most 512 chars, usually 256. - * But one font position may represent several Unicode chars. - * A hashtable is somewhat of a pain to deal with, so use a - * "paged table" instead. Simulation has shown the memory cost of - * this 3-level paged table scheme to be comparable to a hash table. - */ - -extern u8 dfont_unicount[]; /* Defined in console_defmap.c */ -extern u16 dfont_unitable[]; - -static void con_release_unimap(struct uni_pagedir *p) -{ - u16 **p1; - int i, j; - - if (p == dflt) dflt = NULL; - for (i = 0; i < 32; i++) { - if ((p1 = p->uni_pgdir[i]) != NULL) { - for (j = 0; j < 32; j++) - kfree(p1[j]); - kfree(p1); - } - p->uni_pgdir[i] = NULL; - } - for (i = 0; i < 4; i++) { - kfree(p->inverse_translations[i]); - p->inverse_translations[i] = NULL; - } - if (p->inverse_trans_unicode) { - kfree(p->inverse_trans_unicode); - p->inverse_trans_unicode = NULL; - } -} - -void con_free_unimap(struct vc_data *vc) -{ - struct uni_pagedir *p; - - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - if (!p) - return; - *vc->vc_uni_pagedir_loc = 0; - if (--p->refcount) - return; - con_release_unimap(p); - kfree(p); -} - -static int con_unify_unimap(struct vc_data *conp, struct uni_pagedir *p) -{ - int i, j, k; - struct uni_pagedir *q; - - for (i = 0; i < MAX_NR_CONSOLES; i++) { - if (!vc_cons_allocated(i)) - continue; - q = (struct uni_pagedir *)*vc_cons[i].d->vc_uni_pagedir_loc; - if (!q || q == p || q->sum != p->sum) - continue; - for (j = 0; j < 32; j++) { - u16 **p1, **q1; - p1 = p->uni_pgdir[j]; q1 = q->uni_pgdir[j]; - if (!p1 && !q1) - continue; - if (!p1 || !q1) - break; - for (k = 0; k < 32; k++) { - if (!p1[k] && !q1[k]) - continue; - if (!p1[k] || !q1[k]) - break; - if (memcmp(p1[k], q1[k], 64*sizeof(u16))) - break; - } - if (k < 32) - break; - } - if (j == 32) { - q->refcount++; - *conp->vc_uni_pagedir_loc = (unsigned long)q; - con_release_unimap(p); - kfree(p); - return 1; - } - } - return 0; -} - -static int -con_insert_unipair(struct uni_pagedir *p, u_short unicode, u_short fontpos) -{ - int i, n; - u16 **p1, *p2; - - if (!(p1 = p->uni_pgdir[n = unicode >> 11])) { - p1 = p->uni_pgdir[n] = kmalloc(32*sizeof(u16 *), GFP_KERNEL); - if (!p1) return -ENOMEM; - for (i = 0; i < 32; i++) - p1[i] = NULL; - } - - if (!(p2 = p1[n = (unicode >> 6) & 0x1f])) { - p2 = p1[n] = kmalloc(64*sizeof(u16), GFP_KERNEL); - if (!p2) return -ENOMEM; - memset(p2, 0xff, 64*sizeof(u16)); /* No glyphs for the characters (yet) */ - } - - p2[unicode & 0x3f] = fontpos; - - p->sum += (fontpos << 20) + unicode; - - return 0; -} - -/* ui is a leftover from using a hashtable, but might be used again */ -int con_clear_unimap(struct vc_data *vc, struct unimapinit *ui) -{ - struct uni_pagedir *p, *q; - - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - if (p && p->readonly) return -EIO; - if (!p || --p->refcount) { - q = kzalloc(sizeof(*p), GFP_KERNEL); - if (!q) { - if (p) p->refcount++; - return -ENOMEM; - } - q->refcount=1; - *vc->vc_uni_pagedir_loc = (unsigned long)q; - } else { - if (p == dflt) dflt = NULL; - p->refcount++; - p->sum = 0; - con_release_unimap(p); - } - return 0; -} - -int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) -{ - int err = 0, err1, i; - struct uni_pagedir *p, *q; - - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - if (p->readonly) return -EIO; - - if (!ct) return 0; - - if (p->refcount > 1) { - int j, k; - u16 **p1, *p2, l; - - err1 = con_clear_unimap(vc, NULL); - if (err1) return err1; - - q = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - for (i = 0, l = 0; i < 32; i++) - if ((p1 = p->uni_pgdir[i])) - for (j = 0; j < 32; j++) - if ((p2 = p1[j])) - for (k = 0; k < 64; k++, l++) - if (p2[k] != 0xffff) { - err1 = con_insert_unipair(q, l, p2[k]); - if (err1) { - p->refcount++; - *vc->vc_uni_pagedir_loc = (unsigned long)p; - con_release_unimap(q); - kfree(q); - return err1; - } - } - p = q; - } else if (p == dflt) - dflt = NULL; - - while (ct--) { - unsigned short unicode, fontpos; - __get_user(unicode, &list->unicode); - __get_user(fontpos, &list->fontpos); - if ((err1 = con_insert_unipair(p, unicode,fontpos)) != 0) - err = err1; - list++; - } - - if (con_unify_unimap(vc, p)) - return err; - - for (i = 0; i <= 3; i++) - set_inverse_transl(vc, p, i); /* Update all inverse translations */ - set_inverse_trans_unicode(vc, p); - - return err; -} - -/* Loads the unimap for the hardware font, as defined in uni_hash.tbl. - The representation used was the most compact I could come up - with. This routine is executed at sys_setup time, and when the - PIO_FONTRESET ioctl is called. */ - -int con_set_default_unimap(struct vc_data *vc) -{ - int i, j, err = 0, err1; - u16 *q; - struct uni_pagedir *p; - - if (dflt) { - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - if (p == dflt) - return 0; - dflt->refcount++; - *vc->vc_uni_pagedir_loc = (unsigned long)dflt; - if (p && --p->refcount) { - con_release_unimap(p); - kfree(p); - } - return 0; - } - - /* The default font is always 256 characters */ - - err = con_clear_unimap(vc, NULL); - if (err) return err; - - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - q = dfont_unitable; - - for (i = 0; i < 256; i++) - for (j = dfont_unicount[i]; j; j--) { - err1 = con_insert_unipair(p, *(q++), i); - if (err1) - err = err1; - } - - if (con_unify_unimap(vc, p)) { - dflt = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - return err; - } - - for (i = 0; i <= 3; i++) - set_inverse_transl(vc, p, i); /* Update all inverse translations */ - set_inverse_trans_unicode(vc, p); - dflt = p; - return err; -} -EXPORT_SYMBOL(con_set_default_unimap); - -int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc) -{ - struct uni_pagedir *q; - - if (!*src_vc->vc_uni_pagedir_loc) - return -EINVAL; - if (*dst_vc->vc_uni_pagedir_loc == *src_vc->vc_uni_pagedir_loc) - return 0; - con_free_unimap(dst_vc); - q = (struct uni_pagedir *)*src_vc->vc_uni_pagedir_loc; - q->refcount++; - *dst_vc->vc_uni_pagedir_loc = (long)q; - return 0; -} - -int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list) -{ - int i, j, k, ect; - u16 **p1, *p2; - struct uni_pagedir *p; - - ect = 0; - if (*vc->vc_uni_pagedir_loc) { - p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - for (i = 0; i < 32; i++) - if ((p1 = p->uni_pgdir[i])) - for (j = 0; j < 32; j++) - if ((p2 = *(p1++))) - for (k = 0; k < 64; k++) { - if (*p2 < MAX_GLYPH && ect++ < ct) { - __put_user((u_short)((i<<11)+(j<<6)+k), - &list->unicode); - __put_user((u_short) *p2, - &list->fontpos); - list++; - } - p2++; - } - } - __put_user(ect, uct); - return ((ect <= ct) ? 0 : -ENOMEM); -} - -void con_protect_unimap(struct vc_data *vc, int rdonly) -{ - struct uni_pagedir *p = (struct uni_pagedir *)*vc->vc_uni_pagedir_loc; - - if (p) - p->readonly = rdonly; -} - -/* - * Always use USER_MAP. These functions are used by the keyboard, - * which shouldn't be affected by G0/G1 switching, etc. - * If the user map still contains default values, i.e. the - * direct-to-font mapping, then assume user is using Latin1. - */ -/* may be called during an interrupt */ -u32 conv_8bit_to_uni(unsigned char c) -{ - unsigned short uni = translations[USER_MAP][c]; - return uni == (0xf000 | c) ? c : uni; -} - -int conv_uni_to_8bit(u32 uni) -{ - int c; - for (c = 0; c < 0x100; c++) - if (translations[USER_MAP][c] == uni || - (translations[USER_MAP][c] == (c | 0xf000) && uni == c)) - return c; - return -1; -} - -int -conv_uni_to_pc(struct vc_data *conp, long ucs) -{ - int h; - u16 **p1, *p2; - struct uni_pagedir *p; - - /* Only 16-bit codes supported at this time */ - if (ucs > 0xffff) - return -4; /* Not found */ - else if (ucs < 0x20) - return -1; /* Not a printable character */ - else if (ucs == 0xfeff || (ucs >= 0x200b && ucs <= 0x200f)) - return -2; /* Zero-width space */ - /* - * UNI_DIRECT_BASE indicates the start of the region in the User Zone - * which always has a 1:1 mapping to the currently loaded font. The - * UNI_DIRECT_MASK indicates the bit span of the region. - */ - else if ((ucs & ~UNI_DIRECT_MASK) == UNI_DIRECT_BASE) - return ucs & UNI_DIRECT_MASK; - - if (!*conp->vc_uni_pagedir_loc) - return -3; - - p = (struct uni_pagedir *)*conp->vc_uni_pagedir_loc; - if ((p1 = p->uni_pgdir[ucs >> 11]) && - (p2 = p1[(ucs >> 6) & 0x1f]) && - (h = p2[ucs & 0x3f]) < MAX_GLYPH) - return h; - - return -4; /* not found */ -} - -/* - * This is called at sys_setup time, after memory and the console are - * initialized. It must be possible to call kmalloc(..., GFP_KERNEL) - * from this function, hence the call from sys_setup. - */ -void __init -console_map_init(void) -{ - int i; - - for (i = 0; i < MAX_NR_CONSOLES; i++) - if (vc_cons_allocated(i) && !*vc_cons[i].d->vc_uni_pagedir_loc) - con_set_default_unimap(vc_cons[i].d); -} - -EXPORT_SYMBOL(con_copy_unimap); diff --git a/drivers/char/cp437.uni b/drivers/char/cp437.uni deleted file mode 100644 index bc6163484f62..000000000000 --- a/drivers/char/cp437.uni +++ /dev/null @@ -1,291 +0,0 @@ -# -# Unicode table for IBM Codepage 437. Note that there are many more -# substitutions that could be conceived (for example, thick-line -# graphs probably should be replaced with double-line ones, accented -# Latin characters should replaced with their nonaccented versions, -# and some upper case Greek characters could be replaced by Latin), however, -# I have limited myself to the Unicodes used by the kernel ISO 8859-1, -# DEC VT, and IBM CP 437 tables. -# -# -------------------------------- -# -# Basic IBM dingbats, some of which will never have a purpose clear -# to mankind -# -0x00 U+0000 -0x01 U+263a -0x02 U+263b -0x03 U+2665 -0x04 U+2666 U+25c6 -0x05 U+2663 -0x06 U+2660 -0x07 U+2022 -0x08 U+25d8 -0x09 U+25cb -0x0a U+25d9 -0x0b U+2642 -0x0c U+2640 -0x0d U+266a -0x0e U+266b -0x0f U+263c U+00a4 -0x10 U+25b6 U+25ba -0x11 U+25c0 U+25c4 -0x12 U+2195 -0x13 U+203c -0x14 U+00b6 -0x15 U+00a7 -0x16 U+25ac -0x17 U+21a8 -0x18 U+2191 -0x19 U+2193 -0x1a U+2192 -0x1b U+2190 -0x1c U+221f -0x1d U+2194 -0x1e U+25b2 -0x1f U+25bc -# -# The ASCII range is identity-mapped, but some of the characters also -# have to act as substitutes, especially the upper-case characters. -# -0x20 U+0020 -0x21 U+0021 -0x22 U+0022 U+00a8 -0x23 U+0023 -0x24 U+0024 -0x25 U+0025 -0x26 U+0026 -0x27 U+0027 U+00b4 -0x28 U+0028 -0x29 U+0029 -0x2a U+002a -0x2b U+002b -0x2c U+002c U+00b8 -0x2d U+002d U+00ad -0x2e U+002e -0x2f U+002f -0x30 U+0030 -0x31 U+0031 -0x32 U+0032 -0x33 U+0033 -0x34 U+0034 -0x35 U+0035 -0x36 U+0036 -0x37 U+0037 -0x38 U+0038 -0x39 U+0039 -0x3a U+003a -0x3b U+003b -0x3c U+003c -0x3d U+003d -0x3e U+003e -0x3f U+003f -0x40 U+0040 -0x41 U+0041 U+00c0 U+00c1 U+00c2 U+00c3 -0x42 U+0042 -0x43 U+0043 U+00a9 -0x44 U+0044 U+00d0 -0x45 U+0045 U+00c8 U+00ca U+00cb -0x46 U+0046 -0x47 U+0047 -0x48 U+0048 -0x49 U+0049 U+00cc U+00cd U+00ce U+00cf -0x4a U+004a -0x4b U+004b U+212a -0x4c U+004c -0x4d U+004d -0x4e U+004e -0x4f U+004f U+00d2 U+00d3 U+00d4 U+00d5 -0x50 U+0050 -0x51 U+0051 -0x52 U+0052 U+00ae -0x53 U+0053 -0x54 U+0054 -0x55 U+0055 U+00d9 U+00da U+00db -0x56 U+0056 -0x57 U+0057 -0x58 U+0058 -0x59 U+0059 U+00dd -0x5a U+005a -0x5b U+005b -0x5c U+005c -0x5d U+005d -0x5e U+005e -0x5f U+005f U+23bd U+f804 -0x60 U+0060 -0x61 U+0061 U+00e3 -0x62 U+0062 -0x63 U+0063 -0x64 U+0064 -0x65 U+0065 -0x66 U+0066 -0x67 U+0067 -0x68 U+0068 -0x69 U+0069 -0x6a U+006a -0x6b U+006b -0x6c U+006c -0x6d U+006d -0x6e U+006e -0x6f U+006f U+00f5 -0x70 U+0070 -0x71 U+0071 -0x72 U+0072 -0x73 U+0073 -0x74 U+0074 -0x75 U+0075 -0x76 U+0076 -0x77 U+0077 -0x78 U+0078 U+00d7 -0x79 U+0079 U+00fd -0x7a U+007a -0x7b U+007b -0x7c U+007c U+00a6 -0x7d U+007d -0x7e U+007e -# -# Okay, what on Earth is this one supposed to be used for? -# -0x7f U+2302 -# -# Non-English characters, mostly lower case letters... -# -0x80 U+00c7 -0x81 U+00fc -0x82 U+00e9 -0x83 U+00e2 -0x84 U+00e4 -0x85 U+00e0 -0x86 U+00e5 -0x87 U+00e7 -0x88 U+00ea -0x89 U+00eb -0x8a U+00e8 -0x8b U+00ef -0x8c U+00ee -0x8d U+00ec -0x8e U+00c4 -0x8f U+00c5 U+212b -0x90 U+00c9 -0x91 U+00e6 -0x92 U+00c6 -0x93 U+00f4 -0x94 U+00f6 -0x95 U+00f2 -0x96 U+00fb -0x97 U+00f9 -0x98 U+00ff -0x99 U+00d6 -0x9a U+00dc -0x9b U+00a2 -0x9c U+00a3 -0x9d U+00a5 -0x9e U+20a7 -0x9f U+0192 -0xa0 U+00e1 -0xa1 U+00ed -0xa2 U+00f3 -0xa3 U+00fa -0xa4 U+00f1 -0xa5 U+00d1 -0xa6 U+00aa -0xa7 U+00ba -0xa8 U+00bf -0xa9 U+2310 -0xaa U+00ac -0xab U+00bd -0xac U+00bc -0xad U+00a1 -0xae U+00ab -0xaf U+00bb -# -# Block graphics -# -0xb0 U+2591 -0xb1 U+2592 -0xb2 U+2593 -0xb3 U+2502 -0xb4 U+2524 -0xb5 U+2561 -0xb6 U+2562 -0xb7 U+2556 -0xb8 U+2555 -0xb9 U+2563 -0xba U+2551 -0xbb U+2557 -0xbc U+255d -0xbd U+255c -0xbe U+255b -0xbf U+2510 -0xc0 U+2514 -0xc1 U+2534 -0xc2 U+252c -0xc3 U+251c -0xc4 U+2500 -0xc5 U+253c -0xc6 U+255e -0xc7 U+255f -0xc8 U+255a -0xc9 U+2554 -0xca U+2569 -0xcb U+2566 -0xcc U+2560 -0xcd U+2550 -0xce U+256c -0xcf U+2567 -0xd0 U+2568 -0xd1 U+2564 -0xd2 U+2565 -0xd3 U+2559 -0xd4 U+2558 -0xd5 U+2552 -0xd6 U+2553 -0xd7 U+256b -0xd8 U+256a -0xd9 U+2518 -0xda U+250c -0xdb U+2588 -0xdc U+2584 -0xdd U+258c -0xde U+2590 -0xdf U+2580 -# -# Greek letters and mathematical symbols -# -0xe0 U+03b1 -0xe1 U+03b2 U+00df -0xe2 U+0393 -0xe3 U+03c0 -0xe4 U+03a3 -0xe5 U+03c3 -0xe6 U+00b5 U+03bc -0xe7 U+03c4 -0xe8 U+03a6 U+00d8 -0xe9 U+0398 -0xea U+03a9 U+2126 -0xeb U+03b4 U+00f0 -0xec U+221e -0xed U+03c6 U+00f8 -0xee U+03b5 U+2208 -0xef U+2229 -0xf0 U+2261 -0xf1 U+00b1 -0xf2 U+2265 -0xf3 U+2264 -0xf4 U+2320 -0xf5 U+2321 -0xf6 U+00f7 -0xf7 U+2248 -0xf8 U+00b0 -0xf9 U+2219 -0xfa U+00b7 -0xfb U+221a -0xfc U+207f -0xfd U+00b2 -# -# Square bullet, non-spacing blank -# Mapping U+fffd to the square bullet means it is the substitution -# character -# -0xfe U+25a0 U+fffd -0xff U+00a0 diff --git a/drivers/char/defkeymap.c_shipped b/drivers/char/defkeymap.c_shipped deleted file mode 100644 index d2208dfe3f67..000000000000 --- a/drivers/char/defkeymap.c_shipped +++ /dev/null @@ -1,262 +0,0 @@ -/* Do not edit this file! It was automatically generated by */ -/* loadkeys --mktable defkeymap.map > defkeymap.c */ - -#include -#include -#include - -u_short plain_map[NR_KEYS] = { - 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, - 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, - 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, - 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, - 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, - 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, - 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, - 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, - 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, - 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, - 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, - 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, - 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, - 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short shift_map[NR_KEYS] = { - 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, - 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, - 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, - 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, - 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, - 0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, - 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, - 0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, - 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307, - 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, - 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a, - 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, - 0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, - 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short altgr_map[NR_KEYS] = { - 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, - 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, - 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, - 0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf201, 0xf702, 0xf914, 0xfb73, - 0xf917, 0xf919, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200, - 0xf200, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xf916, 0xfb76, - 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, - 0xf703, 0xf200, 0xf207, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, - 0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf202, 0xf911, - 0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b, - 0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, - 0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, - 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, - 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short ctrl_map[NR_KEYS] = { - 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, - 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, - 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, - 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013, - 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, - 0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, - 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, - 0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, - 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf204, 0xf307, - 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, - 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf10a, - 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, - 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, - 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short shift_ctrl_map[NR_KEYS] = { - 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, - 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, - 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf702, 0xf001, 0xf013, - 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, - 0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, - 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, - 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307, - 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, - 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, - 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, - 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short alt_map[NR_KEYS] = { - 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, - 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, - 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, - 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, - 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, - 0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, - 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, - 0xf703, 0xf820, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, - 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf907, - 0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, - 0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, - 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, - 0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, - 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short ctrl_alt_map[NR_KEYS] = { - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, - 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf702, 0xf801, 0xf813, - 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, - 0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816, - 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, - 0xf703, 0xf200, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, - 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf200, 0xf307, - 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, - 0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a, - 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, - 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, - 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -ushort *key_maps[MAX_NR_KEYMAPS] = { - plain_map, shift_map, altgr_map, NULL, - ctrl_map, shift_ctrl_map, NULL, NULL, - alt_map, NULL, NULL, NULL, - ctrl_alt_map, NULL -}; - -unsigned int keymap_count = 7; - -/* - * Philosophy: most people do not define more strings, but they who do - * often want quite a lot of string space. So, we statically allocate - * the default and allocate dynamically in chunks of 512 bytes. - */ - -char func_buf[] = { - '\033', '[', '[', 'A', 0, - '\033', '[', '[', 'B', 0, - '\033', '[', '[', 'C', 0, - '\033', '[', '[', 'D', 0, - '\033', '[', '[', 'E', 0, - '\033', '[', '1', '7', '~', 0, - '\033', '[', '1', '8', '~', 0, - '\033', '[', '1', '9', '~', 0, - '\033', '[', '2', '0', '~', 0, - '\033', '[', '2', '1', '~', 0, - '\033', '[', '2', '3', '~', 0, - '\033', '[', '2', '4', '~', 0, - '\033', '[', '2', '5', '~', 0, - '\033', '[', '2', '6', '~', 0, - '\033', '[', '2', '8', '~', 0, - '\033', '[', '2', '9', '~', 0, - '\033', '[', '3', '1', '~', 0, - '\033', '[', '3', '2', '~', 0, - '\033', '[', '3', '3', '~', 0, - '\033', '[', '3', '4', '~', 0, - '\033', '[', '1', '~', 0, - '\033', '[', '2', '~', 0, - '\033', '[', '3', '~', 0, - '\033', '[', '4', '~', 0, - '\033', '[', '5', '~', 0, - '\033', '[', '6', '~', 0, - '\033', '[', 'M', 0, - '\033', '[', 'P', 0, -}; - -char *funcbufptr = func_buf; -int funcbufsize = sizeof(func_buf); -int funcbufleft = 0; /* space left */ - -char *func_table[MAX_NR_FUNC] = { - func_buf + 0, - func_buf + 5, - func_buf + 10, - func_buf + 15, - func_buf + 20, - func_buf + 25, - func_buf + 31, - func_buf + 37, - func_buf + 43, - func_buf + 49, - func_buf + 55, - func_buf + 61, - func_buf + 67, - func_buf + 73, - func_buf + 79, - func_buf + 85, - func_buf + 91, - func_buf + 97, - func_buf + 103, - func_buf + 109, - func_buf + 115, - func_buf + 120, - func_buf + 125, - func_buf + 130, - func_buf + 135, - func_buf + 140, - func_buf + 145, - NULL, - NULL, - func_buf + 149, - NULL, -}; - -struct kbdiacruc accent_table[MAX_DIACR] = { - {'`', 'A', 0300}, {'`', 'a', 0340}, - {'\'', 'A', 0301}, {'\'', 'a', 0341}, - {'^', 'A', 0302}, {'^', 'a', 0342}, - {'~', 'A', 0303}, {'~', 'a', 0343}, - {'"', 'A', 0304}, {'"', 'a', 0344}, - {'O', 'A', 0305}, {'o', 'a', 0345}, - {'0', 'A', 0305}, {'0', 'a', 0345}, - {'A', 'A', 0305}, {'a', 'a', 0345}, - {'A', 'E', 0306}, {'a', 'e', 0346}, - {',', 'C', 0307}, {',', 'c', 0347}, - {'`', 'E', 0310}, {'`', 'e', 0350}, - {'\'', 'E', 0311}, {'\'', 'e', 0351}, - {'^', 'E', 0312}, {'^', 'e', 0352}, - {'"', 'E', 0313}, {'"', 'e', 0353}, - {'`', 'I', 0314}, {'`', 'i', 0354}, - {'\'', 'I', 0315}, {'\'', 'i', 0355}, - {'^', 'I', 0316}, {'^', 'i', 0356}, - {'"', 'I', 0317}, {'"', 'i', 0357}, - {'-', 'D', 0320}, {'-', 'd', 0360}, - {'~', 'N', 0321}, {'~', 'n', 0361}, - {'`', 'O', 0322}, {'`', 'o', 0362}, - {'\'', 'O', 0323}, {'\'', 'o', 0363}, - {'^', 'O', 0324}, {'^', 'o', 0364}, - {'~', 'O', 0325}, {'~', 'o', 0365}, - {'"', 'O', 0326}, {'"', 'o', 0366}, - {'/', 'O', 0330}, {'/', 'o', 0370}, - {'`', 'U', 0331}, {'`', 'u', 0371}, - {'\'', 'U', 0332}, {'\'', 'u', 0372}, - {'^', 'U', 0333}, {'^', 'u', 0373}, - {'"', 'U', 0334}, {'"', 'u', 0374}, - {'\'', 'Y', 0335}, {'\'', 'y', 0375}, - {'T', 'H', 0336}, {'t', 'h', 0376}, - {'s', 's', 0337}, {'"', 'y', 0377}, - {'s', 'z', 0337}, {'i', 'j', 0377}, -}; - -unsigned int accent_table_size = 68; diff --git a/drivers/char/defkeymap.map b/drivers/char/defkeymap.map deleted file mode 100644 index 50b30cace261..000000000000 --- a/drivers/char/defkeymap.map +++ /dev/null @@ -1,357 +0,0 @@ -# Default kernel keymap. This uses 7 modifier combinations. -keymaps 0-2,4-5,8,12 -# Change the above line into -# keymaps 0-2,4-6,8,12 -# in case you want the entries -# altgr control keycode 83 = Boot -# altgr control keycode 111 = Boot -# below. -# -# In fact AltGr is used very little, and one more keymap can -# be saved by mapping AltGr to Alt (and adapting a few entries): -# keycode 100 = Alt -# -keycode 1 = Escape Escape - alt keycode 1 = Meta_Escape -keycode 2 = one exclam - alt keycode 2 = Meta_one -keycode 3 = two at at - control keycode 3 = nul - shift control keycode 3 = nul - alt keycode 3 = Meta_two -keycode 4 = three numbersign - control keycode 4 = Escape - alt keycode 4 = Meta_three -keycode 5 = four dollar dollar - control keycode 5 = Control_backslash - alt keycode 5 = Meta_four -keycode 6 = five percent - control keycode 6 = Control_bracketright - alt keycode 6 = Meta_five -keycode 7 = six asciicircum - control keycode 7 = Control_asciicircum - alt keycode 7 = Meta_six -keycode 8 = seven ampersand braceleft - control keycode 8 = Control_underscore - alt keycode 8 = Meta_seven -keycode 9 = eight asterisk bracketleft - control keycode 9 = Delete - alt keycode 9 = Meta_eight -keycode 10 = nine parenleft bracketright - alt keycode 10 = Meta_nine -keycode 11 = zero parenright braceright - alt keycode 11 = Meta_zero -keycode 12 = minus underscore backslash - control keycode 12 = Control_underscore - shift control keycode 12 = Control_underscore - alt keycode 12 = Meta_minus -keycode 13 = equal plus - alt keycode 13 = Meta_equal -keycode 14 = Delete Delete - control keycode 14 = BackSpace - alt keycode 14 = Meta_Delete -keycode 15 = Tab Tab - alt keycode 15 = Meta_Tab -keycode 16 = q -keycode 17 = w -keycode 18 = e - altgr keycode 18 = Hex_E -keycode 19 = r -keycode 20 = t -keycode 21 = y -keycode 22 = u -keycode 23 = i -keycode 24 = o -keycode 25 = p -keycode 26 = bracketleft braceleft - control keycode 26 = Escape - alt keycode 26 = Meta_bracketleft -keycode 27 = bracketright braceright asciitilde - control keycode 27 = Control_bracketright - alt keycode 27 = Meta_bracketright -keycode 28 = Return - alt keycode 28 = Meta_Control_m -keycode 29 = Control -keycode 30 = a - altgr keycode 30 = Hex_A -keycode 31 = s -keycode 32 = d - altgr keycode 32 = Hex_D -keycode 33 = f - altgr keycode 33 = Hex_F -keycode 34 = g -keycode 35 = h -keycode 36 = j -keycode 37 = k -keycode 38 = l -keycode 39 = semicolon colon - alt keycode 39 = Meta_semicolon -keycode 40 = apostrophe quotedbl - control keycode 40 = Control_g - alt keycode 40 = Meta_apostrophe -keycode 41 = grave asciitilde - control keycode 41 = nul - alt keycode 41 = Meta_grave -keycode 42 = Shift -keycode 43 = backslash bar - control keycode 43 = Control_backslash - alt keycode 43 = Meta_backslash -keycode 44 = z -keycode 45 = x -keycode 46 = c - altgr keycode 46 = Hex_C -keycode 47 = v -keycode 48 = b - altgr keycode 48 = Hex_B -keycode 49 = n -keycode 50 = m -keycode 51 = comma less - alt keycode 51 = Meta_comma -keycode 52 = period greater - control keycode 52 = Compose - alt keycode 52 = Meta_period -keycode 53 = slash question - control keycode 53 = Delete - alt keycode 53 = Meta_slash -keycode 54 = Shift -keycode 55 = KP_Multiply -keycode 56 = Alt -keycode 57 = space space - control keycode 57 = nul - alt keycode 57 = Meta_space -keycode 58 = Caps_Lock -keycode 59 = F1 F11 Console_13 - control keycode 59 = F1 - alt keycode 59 = Console_1 - control alt keycode 59 = Console_1 -keycode 60 = F2 F12 Console_14 - control keycode 60 = F2 - alt keycode 60 = Console_2 - control alt keycode 60 = Console_2 -keycode 61 = F3 F13 Console_15 - control keycode 61 = F3 - alt keycode 61 = Console_3 - control alt keycode 61 = Console_3 -keycode 62 = F4 F14 Console_16 - control keycode 62 = F4 - alt keycode 62 = Console_4 - control alt keycode 62 = Console_4 -keycode 63 = F5 F15 Console_17 - control keycode 63 = F5 - alt keycode 63 = Console_5 - control alt keycode 63 = Console_5 -keycode 64 = F6 F16 Console_18 - control keycode 64 = F6 - alt keycode 64 = Console_6 - control alt keycode 64 = Console_6 -keycode 65 = F7 F17 Console_19 - control keycode 65 = F7 - alt keycode 65 = Console_7 - control alt keycode 65 = Console_7 -keycode 66 = F8 F18 Console_20 - control keycode 66 = F8 - alt keycode 66 = Console_8 - control alt keycode 66 = Console_8 -keycode 67 = F9 F19 Console_21 - control keycode 67 = F9 - alt keycode 67 = Console_9 - control alt keycode 67 = Console_9 -keycode 68 = F10 F20 Console_22 - control keycode 68 = F10 - alt keycode 68 = Console_10 - control alt keycode 68 = Console_10 -keycode 69 = Num_Lock - shift keycode 69 = Bare_Num_Lock -keycode 70 = Scroll_Lock Show_Memory Show_Registers - control keycode 70 = Show_State - alt keycode 70 = Scroll_Lock -keycode 71 = KP_7 - alt keycode 71 = Ascii_7 - altgr keycode 71 = Hex_7 -keycode 72 = KP_8 - alt keycode 72 = Ascii_8 - altgr keycode 72 = Hex_8 -keycode 73 = KP_9 - alt keycode 73 = Ascii_9 - altgr keycode 73 = Hex_9 -keycode 74 = KP_Subtract -keycode 75 = KP_4 - alt keycode 75 = Ascii_4 - altgr keycode 75 = Hex_4 -keycode 76 = KP_5 - alt keycode 76 = Ascii_5 - altgr keycode 76 = Hex_5 -keycode 77 = KP_6 - alt keycode 77 = Ascii_6 - altgr keycode 77 = Hex_6 -keycode 78 = KP_Add -keycode 79 = KP_1 - alt keycode 79 = Ascii_1 - altgr keycode 79 = Hex_1 -keycode 80 = KP_2 - alt keycode 80 = Ascii_2 - altgr keycode 80 = Hex_2 -keycode 81 = KP_3 - alt keycode 81 = Ascii_3 - altgr keycode 81 = Hex_3 -keycode 82 = KP_0 - alt keycode 82 = Ascii_0 - altgr keycode 82 = Hex_0 -keycode 83 = KP_Period -# altgr control keycode 83 = Boot - control alt keycode 83 = Boot -keycode 84 = Last_Console -keycode 85 = -keycode 86 = less greater bar - alt keycode 86 = Meta_less -keycode 87 = F11 F11 Console_23 - control keycode 87 = F11 - alt keycode 87 = Console_11 - control alt keycode 87 = Console_11 -keycode 88 = F12 F12 Console_24 - control keycode 88 = F12 - alt keycode 88 = Console_12 - control alt keycode 88 = Console_12 -keycode 89 = -keycode 90 = -keycode 91 = -keycode 92 = -keycode 93 = -keycode 94 = -keycode 95 = -keycode 96 = KP_Enter -keycode 97 = Control -keycode 98 = KP_Divide -keycode 99 = Control_backslash - control keycode 99 = Control_backslash - alt keycode 99 = Control_backslash -keycode 100 = AltGr -keycode 101 = Break -keycode 102 = Find -keycode 103 = Up -keycode 104 = Prior - shift keycode 104 = Scroll_Backward -keycode 105 = Left - alt keycode 105 = Decr_Console -keycode 106 = Right - alt keycode 106 = Incr_Console -keycode 107 = Select -keycode 108 = Down -keycode 109 = Next - shift keycode 109 = Scroll_Forward -keycode 110 = Insert -keycode 111 = Remove -# altgr control keycode 111 = Boot - control alt keycode 111 = Boot -keycode 112 = Macro -keycode 113 = F13 -keycode 114 = F14 -keycode 115 = Help -keycode 116 = Do -keycode 117 = F17 -keycode 118 = KP_MinPlus -keycode 119 = Pause -keycode 120 = -keycode 121 = -keycode 122 = -keycode 123 = -keycode 124 = -keycode 125 = -keycode 126 = -keycode 127 = -string F1 = "\033[[A" -string F2 = "\033[[B" -string F3 = "\033[[C" -string F4 = "\033[[D" -string F5 = "\033[[E" -string F6 = "\033[17~" -string F7 = "\033[18~" -string F8 = "\033[19~" -string F9 = "\033[20~" -string F10 = "\033[21~" -string F11 = "\033[23~" -string F12 = "\033[24~" -string F13 = "\033[25~" -string F14 = "\033[26~" -string F15 = "\033[28~" -string F16 = "\033[29~" -string F17 = "\033[31~" -string F18 = "\033[32~" -string F19 = "\033[33~" -string F20 = "\033[34~" -string Find = "\033[1~" -string Insert = "\033[2~" -string Remove = "\033[3~" -string Select = "\033[4~" -string Prior = "\033[5~" -string Next = "\033[6~" -string Macro = "\033[M" -string Pause = "\033[P" -compose '`' 'A' to 'À' -compose '`' 'a' to 'à' -compose '\'' 'A' to 'Á' -compose '\'' 'a' to 'á' -compose '^' 'A' to 'Â' -compose '^' 'a' to 'â' -compose '~' 'A' to 'Ã' -compose '~' 'a' to 'ã' -compose '"' 'A' to 'Ä' -compose '"' 'a' to 'ä' -compose 'O' 'A' to 'Å' -compose 'o' 'a' to 'å' -compose '0' 'A' to 'Å' -compose '0' 'a' to 'å' -compose 'A' 'A' to 'Å' -compose 'a' 'a' to 'å' -compose 'A' 'E' to 'Æ' -compose 'a' 'e' to 'æ' -compose ',' 'C' to 'Ç' -compose ',' 'c' to 'ç' -compose '`' 'E' to 'È' -compose '`' 'e' to 'è' -compose '\'' 'E' to 'É' -compose '\'' 'e' to 'é' -compose '^' 'E' to 'Ê' -compose '^' 'e' to 'ê' -compose '"' 'E' to 'Ë' -compose '"' 'e' to 'ë' -compose '`' 'I' to 'Ì' -compose '`' 'i' to 'ì' -compose '\'' 'I' to 'Í' -compose '\'' 'i' to 'í' -compose '^' 'I' to 'Î' -compose '^' 'i' to 'î' -compose '"' 'I' to 'Ï' -compose '"' 'i' to 'ï' -compose '-' 'D' to 'Ð' -compose '-' 'd' to 'ð' -compose '~' 'N' to 'Ñ' -compose '~' 'n' to 'ñ' -compose '`' 'O' to 'Ò' -compose '`' 'o' to 'ò' -compose '\'' 'O' to 'Ó' -compose '\'' 'o' to 'ó' -compose '^' 'O' to 'Ô' -compose '^' 'o' to 'ô' -compose '~' 'O' to 'Õ' -compose '~' 'o' to 'õ' -compose '"' 'O' to 'Ö' -compose '"' 'o' to 'ö' -compose '/' 'O' to 'Ø' -compose '/' 'o' to 'ø' -compose '`' 'U' to 'Ù' -compose '`' 'u' to 'ù' -compose '\'' 'U' to 'Ú' -compose '\'' 'u' to 'ú' -compose '^' 'U' to 'Û' -compose '^' 'u' to 'û' -compose '"' 'U' to 'Ü' -compose '"' 'u' to 'ü' -compose '\'' 'Y' to 'Ý' -compose '\'' 'y' to 'ý' -compose 'T' 'H' to 'Þ' -compose 't' 'h' to 'þ' -compose 's' 's' to 'ß' -compose '"' 'y' to 'ÿ' -compose 's' 'z' to 'ß' -compose 'i' 'j' to 'ÿ' diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c deleted file mode 100644 index e95d7876ca6b..000000000000 --- a/drivers/char/keyboard.c +++ /dev/null @@ -1,1454 +0,0 @@ -/* - * linux/drivers/char/keyboard.c - * - * Written for linux by Johan Myreen as a translation from - * the assembly version by Linus (with diacriticals added) - * - * Some additional features added by Christoph Niemann (ChN), March 1993 - * - * Loadable keymaps by Risto Kankkunen, May 1993 - * - * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993 - * Added decr/incr_console, dynamic keymaps, Unicode support, - * dynamic function/string keys, led setting, Sept 1994 - * `Sticky' modifier keys, 951006. - * - * 11-11-96: SAK should now work in the raw mode (Martin Mares) - * - * Modified to provide 'generic' keyboard support by Hamish Macdonald - * Merge with the m68k keyboard driver and split-off of the PC low-level - * parts by Geert Uytterhoeven, May 1997 - * - * 27-05-97: Added support for the Magic SysRq Key (Martin Mares) - * 30-07-98: Dead keys redone, aeb@cwi.nl. - * 21-08-02: Converted to input API, major cleanup. (Vojtech Pavlik) - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -extern void ctrl_alt_del(void); - -/* - * Exported functions/variables - */ - -#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META)) - -/* - * Some laptops take the 789uiojklm,. keys as number pad when NumLock is on. - * This seems a good reason to start with NumLock off. On HIL keyboards - * of PARISC machines however there is no NumLock key and everyone expects the keypad - * to be used for numbers. - */ - -#if defined(CONFIG_PARISC) && (defined(CONFIG_KEYBOARD_HIL) || defined(CONFIG_KEYBOARD_HIL_OLD)) -#define KBD_DEFLEDS (1 << VC_NUMLOCK) -#else -#define KBD_DEFLEDS 0 -#endif - -#define KBD_DEFLOCK 0 - -void compute_shiftstate(void); - -/* - * Handler Tables. - */ - -#define K_HANDLERS\ - k_self, k_fn, k_spec, k_pad,\ - k_dead, k_cons, k_cur, k_shift,\ - k_meta, k_ascii, k_lock, k_lowercase,\ - k_slock, k_dead2, k_brl, k_ignore - -typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value, - char up_flag); -static k_handler_fn K_HANDLERS; -static k_handler_fn *k_handler[16] = { K_HANDLERS }; - -#define FN_HANDLERS\ - fn_null, fn_enter, fn_show_ptregs, fn_show_mem,\ - fn_show_state, fn_send_intr, fn_lastcons, fn_caps_toggle,\ - fn_num, fn_hold, fn_scroll_forw, fn_scroll_back,\ - fn_boot_it, fn_caps_on, fn_compose, fn_SAK,\ - fn_dec_console, fn_inc_console, fn_spawn_con, fn_bare_num - -typedef void (fn_handler_fn)(struct vc_data *vc); -static fn_handler_fn FN_HANDLERS; -static fn_handler_fn *fn_handler[] = { FN_HANDLERS }; - -/* - * Variables exported for vt_ioctl.c - */ - -/* maximum values each key_handler can handle */ -const int max_vals[] = { - 255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1, - NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1, - 255, NR_LOCK - 1, 255, NR_BRL - 1 -}; - -const int NR_TYPES = ARRAY_SIZE(max_vals); - -struct kbd_struct kbd_table[MAX_NR_CONSOLES]; -EXPORT_SYMBOL_GPL(kbd_table); -static struct kbd_struct *kbd = kbd_table; - -struct vt_spawn_console vt_spawn_con = { - .lock = __SPIN_LOCK_UNLOCKED(vt_spawn_con.lock), - .pid = NULL, - .sig = 0, -}; - -/* - * Variables exported for vt.c - */ - -int shift_state = 0; - -/* - * Internal Data. - */ - -static struct input_handler kbd_handler; -static DEFINE_SPINLOCK(kbd_event_lock); -static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */ -static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ -static bool dead_key_next; -static int npadch = -1; /* -1 or number assembled on pad */ -static unsigned int diacr; -static char rep; /* flag telling character repeat */ - -static unsigned char ledstate = 0xff; /* undefined */ -static unsigned char ledioctl; - -static struct ledptr { - unsigned int *addr; - unsigned int mask; - unsigned char valid:1; -} ledptrs[3]; - -/* - * Notifier list for console keyboard events - */ -static ATOMIC_NOTIFIER_HEAD(keyboard_notifier_list); - -int register_keyboard_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&keyboard_notifier_list, nb); -} -EXPORT_SYMBOL_GPL(register_keyboard_notifier); - -int unregister_keyboard_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&keyboard_notifier_list, nb); -} -EXPORT_SYMBOL_GPL(unregister_keyboard_notifier); - -/* - * Translation of scancodes to keycodes. We set them on only the first - * keyboard in the list that accepts the scancode and keycode. - * Explanation for not choosing the first attached keyboard anymore: - * USB keyboards for example have two event devices: one for all "normal" - * keys and one for extra function keys (like "volume up", "make coffee", - * etc.). So this means that scancodes for the extra function keys won't - * be valid for the first event device, but will be for the second. - */ - -struct getset_keycode_data { - struct input_keymap_entry ke; - int error; -}; - -static int getkeycode_helper(struct input_handle *handle, void *data) -{ - struct getset_keycode_data *d = data; - - d->error = input_get_keycode(handle->dev, &d->ke); - - return d->error == 0; /* stop as soon as we successfully get one */ -} - -int getkeycode(unsigned int scancode) -{ - struct getset_keycode_data d = { - .ke = { - .flags = 0, - .len = sizeof(scancode), - .keycode = 0, - }, - .error = -ENODEV, - }; - - memcpy(d.ke.scancode, &scancode, sizeof(scancode)); - - input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper); - - return d.error ?: d.ke.keycode; -} - -static int setkeycode_helper(struct input_handle *handle, void *data) -{ - struct getset_keycode_data *d = data; - - d->error = input_set_keycode(handle->dev, &d->ke); - - return d->error == 0; /* stop as soon as we successfully set one */ -} - -int setkeycode(unsigned int scancode, unsigned int keycode) -{ - struct getset_keycode_data d = { - .ke = { - .flags = 0, - .len = sizeof(scancode), - .keycode = keycode, - }, - .error = -ENODEV, - }; - - memcpy(d.ke.scancode, &scancode, sizeof(scancode)); - - input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper); - - return d.error; -} - -/* - * Making beeps and bells. Note that we prefer beeps to bells, but when - * shutting the sound off we do both. - */ - -static int kd_sound_helper(struct input_handle *handle, void *data) -{ - unsigned int *hz = data; - struct input_dev *dev = handle->dev; - - if (test_bit(EV_SND, dev->evbit)) { - if (test_bit(SND_TONE, dev->sndbit)) { - input_inject_event(handle, EV_SND, SND_TONE, *hz); - if (*hz) - return 0; - } - if (test_bit(SND_BELL, dev->sndbit)) - input_inject_event(handle, EV_SND, SND_BELL, *hz ? 1 : 0); - } - - return 0; -} - -static void kd_nosound(unsigned long ignored) -{ - static unsigned int zero; - - input_handler_for_each_handle(&kbd_handler, &zero, kd_sound_helper); -} - -static DEFINE_TIMER(kd_mksound_timer, kd_nosound, 0, 0); - -void kd_mksound(unsigned int hz, unsigned int ticks) -{ - del_timer_sync(&kd_mksound_timer); - - input_handler_for_each_handle(&kbd_handler, &hz, kd_sound_helper); - - if (hz && ticks) - mod_timer(&kd_mksound_timer, jiffies + ticks); -} -EXPORT_SYMBOL(kd_mksound); - -/* - * Setting the keyboard rate. - */ - -static int kbd_rate_helper(struct input_handle *handle, void *data) -{ - struct input_dev *dev = handle->dev; - struct kbd_repeat *rep = data; - - if (test_bit(EV_REP, dev->evbit)) { - - if (rep[0].delay > 0) - input_inject_event(handle, - EV_REP, REP_DELAY, rep[0].delay); - if (rep[0].period > 0) - input_inject_event(handle, - EV_REP, REP_PERIOD, rep[0].period); - - rep[1].delay = dev->rep[REP_DELAY]; - rep[1].period = dev->rep[REP_PERIOD]; - } - - return 0; -} - -int kbd_rate(struct kbd_repeat *rep) -{ - struct kbd_repeat data[2] = { *rep }; - - input_handler_for_each_handle(&kbd_handler, data, kbd_rate_helper); - *rep = data[1]; /* Copy currently used settings */ - - return 0; -} - -/* - * Helper Functions. - */ -static void put_queue(struct vc_data *vc, int ch) -{ - struct tty_struct *tty = vc->port.tty; - - if (tty) { - tty_insert_flip_char(tty, ch, 0); - con_schedule_flip(tty); - } -} - -static void puts_queue(struct vc_data *vc, char *cp) -{ - struct tty_struct *tty = vc->port.tty; - - if (!tty) - return; - - while (*cp) { - tty_insert_flip_char(tty, *cp, 0); - cp++; - } - con_schedule_flip(tty); -} - -static void applkey(struct vc_data *vc, int key, char mode) -{ - static char buf[] = { 0x1b, 'O', 0x00, 0x00 }; - - buf[1] = (mode ? 'O' : '['); - buf[2] = key; - puts_queue(vc, buf); -} - -/* - * Many other routines do put_queue, but I think either - * they produce ASCII, or they produce some user-assigned - * string, and in both cases we might assume that it is - * in utf-8 already. - */ -static void to_utf8(struct vc_data *vc, uint c) -{ - if (c < 0x80) - /* 0******* */ - put_queue(vc, c); - else if (c < 0x800) { - /* 110***** 10****** */ - put_queue(vc, 0xc0 | (c >> 6)); - put_queue(vc, 0x80 | (c & 0x3f)); - } else if (c < 0x10000) { - if (c >= 0xD800 && c < 0xE000) - return; - if (c == 0xFFFF) - return; - /* 1110**** 10****** 10****** */ - put_queue(vc, 0xe0 | (c >> 12)); - put_queue(vc, 0x80 | ((c >> 6) & 0x3f)); - put_queue(vc, 0x80 | (c & 0x3f)); - } else if (c < 0x110000) { - /* 11110*** 10****** 10****** 10****** */ - put_queue(vc, 0xf0 | (c >> 18)); - put_queue(vc, 0x80 | ((c >> 12) & 0x3f)); - put_queue(vc, 0x80 | ((c >> 6) & 0x3f)); - put_queue(vc, 0x80 | (c & 0x3f)); - } -} - -/* - * Called after returning from RAW mode or when changing consoles - recompute - * shift_down[] and shift_state from key_down[] maybe called when keymap is - * undefined, so that shiftkey release is seen - */ -void compute_shiftstate(void) -{ - unsigned int i, j, k, sym, val; - - shift_state = 0; - memset(shift_down, 0, sizeof(shift_down)); - - for (i = 0; i < ARRAY_SIZE(key_down); i++) { - - if (!key_down[i]) - continue; - - k = i * BITS_PER_LONG; - - for (j = 0; j < BITS_PER_LONG; j++, k++) { - - if (!test_bit(k, key_down)) - continue; - - sym = U(key_maps[0][k]); - if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK) - continue; - - val = KVAL(sym); - if (val == KVAL(K_CAPSSHIFT)) - val = KVAL(K_SHIFT); - - shift_down[val]++; - shift_state |= (1 << val); - } - } -} - -/* - * We have a combining character DIACR here, followed by the character CH. - * If the combination occurs in the table, return the corresponding value. - * Otherwise, if CH is a space or equals DIACR, return DIACR. - * Otherwise, conclude that DIACR was not combining after all, - * queue it and return CH. - */ -static unsigned int handle_diacr(struct vc_data *vc, unsigned int ch) -{ - unsigned int d = diacr; - unsigned int i; - - diacr = 0; - - if ((d & ~0xff) == BRL_UC_ROW) { - if ((ch & ~0xff) == BRL_UC_ROW) - return d | ch; - } else { - for (i = 0; i < accent_table_size; i++) - if (accent_table[i].diacr == d && accent_table[i].base == ch) - return accent_table[i].result; - } - - if (ch == ' ' || ch == (BRL_UC_ROW|0) || ch == d) - return d; - - if (kbd->kbdmode == VC_UNICODE) - to_utf8(vc, d); - else { - int c = conv_uni_to_8bit(d); - if (c != -1) - put_queue(vc, c); - } - - return ch; -} - -/* - * Special function handlers - */ -static void fn_enter(struct vc_data *vc) -{ - if (diacr) { - if (kbd->kbdmode == VC_UNICODE) - to_utf8(vc, diacr); - else { - int c = conv_uni_to_8bit(diacr); - if (c != -1) - put_queue(vc, c); - } - diacr = 0; - } - - put_queue(vc, 13); - if (vc_kbd_mode(kbd, VC_CRLF)) - put_queue(vc, 10); -} - -static void fn_caps_toggle(struct vc_data *vc) -{ - if (rep) - return; - - chg_vc_kbd_led(kbd, VC_CAPSLOCK); -} - -static void fn_caps_on(struct vc_data *vc) -{ - if (rep) - return; - - set_vc_kbd_led(kbd, VC_CAPSLOCK); -} - -static void fn_show_ptregs(struct vc_data *vc) -{ - struct pt_regs *regs = get_irq_regs(); - - if (regs) - show_regs(regs); -} - -static void fn_hold(struct vc_data *vc) -{ - struct tty_struct *tty = vc->port.tty; - - if (rep || !tty) - return; - - /* - * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty); - * these routines are also activated by ^S/^Q. - * (And SCROLLOCK can also be set by the ioctl KDSKBLED.) - */ - if (tty->stopped) - start_tty(tty); - else - stop_tty(tty); -} - -static void fn_num(struct vc_data *vc) -{ - if (vc_kbd_mode(kbd, VC_APPLIC)) - applkey(vc, 'P', 1); - else - fn_bare_num(vc); -} - -/* - * Bind this to Shift-NumLock if you work in application keypad mode - * but want to be able to change the NumLock flag. - * Bind this to NumLock if you prefer that the NumLock key always - * changes the NumLock flag. - */ -static void fn_bare_num(struct vc_data *vc) -{ - if (!rep) - chg_vc_kbd_led(kbd, VC_NUMLOCK); -} - -static void fn_lastcons(struct vc_data *vc) -{ - /* switch to the last used console, ChN */ - set_console(last_console); -} - -static void fn_dec_console(struct vc_data *vc) -{ - int i, cur = fg_console; - - /* Currently switching? Queue this next switch relative to that. */ - if (want_console != -1) - cur = want_console; - - for (i = cur - 1; i != cur; i--) { - if (i == -1) - i = MAX_NR_CONSOLES - 1; - if (vc_cons_allocated(i)) - break; - } - set_console(i); -} - -static void fn_inc_console(struct vc_data *vc) -{ - int i, cur = fg_console; - - /* Currently switching? Queue this next switch relative to that. */ - if (want_console != -1) - cur = want_console; - - for (i = cur+1; i != cur; i++) { - if (i == MAX_NR_CONSOLES) - i = 0; - if (vc_cons_allocated(i)) - break; - } - set_console(i); -} - -static void fn_send_intr(struct vc_data *vc) -{ - struct tty_struct *tty = vc->port.tty; - - if (!tty) - return; - tty_insert_flip_char(tty, 0, TTY_BREAK); - con_schedule_flip(tty); -} - -static void fn_scroll_forw(struct vc_data *vc) -{ - scrollfront(vc, 0); -} - -static void fn_scroll_back(struct vc_data *vc) -{ - scrollback(vc, 0); -} - -static void fn_show_mem(struct vc_data *vc) -{ - show_mem(); -} - -static void fn_show_state(struct vc_data *vc) -{ - show_state(); -} - -static void fn_boot_it(struct vc_data *vc) -{ - ctrl_alt_del(); -} - -static void fn_compose(struct vc_data *vc) -{ - dead_key_next = true; -} - -static void fn_spawn_con(struct vc_data *vc) -{ - spin_lock(&vt_spawn_con.lock); - if (vt_spawn_con.pid) - if (kill_pid(vt_spawn_con.pid, vt_spawn_con.sig, 1)) { - put_pid(vt_spawn_con.pid); - vt_spawn_con.pid = NULL; - } - spin_unlock(&vt_spawn_con.lock); -} - -static void fn_SAK(struct vc_data *vc) -{ - struct work_struct *SAK_work = &vc_cons[fg_console].SAK_work; - schedule_work(SAK_work); -} - -static void fn_null(struct vc_data *vc) -{ - compute_shiftstate(); -} - -/* - * Special key handlers - */ -static void k_ignore(struct vc_data *vc, unsigned char value, char up_flag) -{ -} - -static void k_spec(struct vc_data *vc, unsigned char value, char up_flag) -{ - if (up_flag) - return; - if (value >= ARRAY_SIZE(fn_handler)) - return; - if ((kbd->kbdmode == VC_RAW || - kbd->kbdmode == VC_MEDIUMRAW) && - value != KVAL(K_SAK)) - return; /* SAK is allowed even in raw mode */ - fn_handler[value](vc); -} - -static void k_lowercase(struct vc_data *vc, unsigned char value, char up_flag) -{ - pr_err("k_lowercase was called - impossible\n"); -} - -static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag) -{ - if (up_flag) - return; /* no action, if this is a key release */ - - if (diacr) - value = handle_diacr(vc, value); - - if (dead_key_next) { - dead_key_next = false; - diacr = value; - return; - } - if (kbd->kbdmode == VC_UNICODE) - to_utf8(vc, value); - else { - int c = conv_uni_to_8bit(value); - if (c != -1) - put_queue(vc, c); - } -} - -/* - * Handle dead key. Note that we now may have several - * dead keys modifying the same character. Very useful - * for Vietnamese. - */ -static void k_deadunicode(struct vc_data *vc, unsigned int value, char up_flag) -{ - if (up_flag) - return; - - diacr = (diacr ? handle_diacr(vc, value) : value); -} - -static void k_self(struct vc_data *vc, unsigned char value, char up_flag) -{ - k_unicode(vc, conv_8bit_to_uni(value), up_flag); -} - -static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag) -{ - k_deadunicode(vc, value, up_flag); -} - -/* - * Obsolete - for backwards compatibility only - */ -static void k_dead(struct vc_data *vc, unsigned char value, char up_flag) -{ - static const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' }; - - k_deadunicode(vc, ret_diacr[value], up_flag); -} - -static void k_cons(struct vc_data *vc, unsigned char value, char up_flag) -{ - if (up_flag) - return; - - set_console(value); -} - -static void k_fn(struct vc_data *vc, unsigned char value, char up_flag) -{ - if (up_flag) - return; - - if ((unsigned)value < ARRAY_SIZE(func_table)) { - if (func_table[value]) - puts_queue(vc, func_table[value]); - } else - pr_err("k_fn called with value=%d\n", value); -} - -static void k_cur(struct vc_data *vc, unsigned char value, char up_flag) -{ - static const char cur_chars[] = "BDCA"; - - if (up_flag) - return; - - applkey(vc, cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE)); -} - -static void k_pad(struct vc_data *vc, unsigned char value, char up_flag) -{ - static const char pad_chars[] = "0123456789+-*/\015,.?()#"; - static const char app_map[] = "pqrstuvwxylSRQMnnmPQS"; - - if (up_flag) - return; /* no action, if this is a key release */ - - /* kludge... shift forces cursor/number keys */ - if (vc_kbd_mode(kbd, VC_APPLIC) && !shift_down[KG_SHIFT]) { - applkey(vc, app_map[value], 1); - return; - } - - if (!vc_kbd_led(kbd, VC_NUMLOCK)) { - - switch (value) { - case KVAL(K_PCOMMA): - case KVAL(K_PDOT): - k_fn(vc, KVAL(K_REMOVE), 0); - return; - case KVAL(K_P0): - k_fn(vc, KVAL(K_INSERT), 0); - return; - case KVAL(K_P1): - k_fn(vc, KVAL(K_SELECT), 0); - return; - case KVAL(K_P2): - k_cur(vc, KVAL(K_DOWN), 0); - return; - case KVAL(K_P3): - k_fn(vc, KVAL(K_PGDN), 0); - return; - case KVAL(K_P4): - k_cur(vc, KVAL(K_LEFT), 0); - return; - case KVAL(K_P6): - k_cur(vc, KVAL(K_RIGHT), 0); - return; - case KVAL(K_P7): - k_fn(vc, KVAL(K_FIND), 0); - return; - case KVAL(K_P8): - k_cur(vc, KVAL(K_UP), 0); - return; - case KVAL(K_P9): - k_fn(vc, KVAL(K_PGUP), 0); - return; - case KVAL(K_P5): - applkey(vc, 'G', vc_kbd_mode(kbd, VC_APPLIC)); - return; - } - } - - put_queue(vc, pad_chars[value]); - if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF)) - put_queue(vc, 10); -} - -static void k_shift(struct vc_data *vc, unsigned char value, char up_flag) -{ - int old_state = shift_state; - - if (rep) - return; - /* - * Mimic typewriter: - * a CapsShift key acts like Shift but undoes CapsLock - */ - if (value == KVAL(K_CAPSSHIFT)) { - value = KVAL(K_SHIFT); - if (!up_flag) - clr_vc_kbd_led(kbd, VC_CAPSLOCK); - } - - if (up_flag) { - /* - * handle the case that two shift or control - * keys are depressed simultaneously - */ - if (shift_down[value]) - shift_down[value]--; - } else - shift_down[value]++; - - if (shift_down[value]) - shift_state |= (1 << value); - else - shift_state &= ~(1 << value); - - /* kludge */ - if (up_flag && shift_state != old_state && npadch != -1) { - if (kbd->kbdmode == VC_UNICODE) - to_utf8(vc, npadch); - else - put_queue(vc, npadch & 0xff); - npadch = -1; - } -} - -static void k_meta(struct vc_data *vc, unsigned char value, char up_flag) -{ - if (up_flag) - return; - - if (vc_kbd_mode(kbd, VC_META)) { - put_queue(vc, '\033'); - put_queue(vc, value); - } else - put_queue(vc, value | 0x80); -} - -static void k_ascii(struct vc_data *vc, unsigned char value, char up_flag) -{ - int base; - - if (up_flag) - return; - - if (value < 10) { - /* decimal input of code, while Alt depressed */ - base = 10; - } else { - /* hexadecimal input of code, while AltGr depressed */ - value -= 10; - base = 16; - } - - if (npadch == -1) - npadch = value; - else - npadch = npadch * base + value; -} - -static void k_lock(struct vc_data *vc, unsigned char value, char up_flag) -{ - if (up_flag || rep) - return; - - chg_vc_kbd_lock(kbd, value); -} - -static void k_slock(struct vc_data *vc, unsigned char value, char up_flag) -{ - k_shift(vc, value, up_flag); - if (up_flag || rep) - return; - - chg_vc_kbd_slock(kbd, value); - /* try to make Alt, oops, AltGr and such work */ - if (!key_maps[kbd->lockstate ^ kbd->slockstate]) { - kbd->slockstate = 0; - chg_vc_kbd_slock(kbd, value); - } -} - -/* by default, 300ms interval for combination release */ -static unsigned brl_timeout = 300; -MODULE_PARM_DESC(brl_timeout, "Braille keys release delay in ms (0 for commit on first key release)"); -module_param(brl_timeout, uint, 0644); - -static unsigned brl_nbchords = 1; -MODULE_PARM_DESC(brl_nbchords, "Number of chords that produce a braille pattern (0 for dead chords)"); -module_param(brl_nbchords, uint, 0644); - -static void k_brlcommit(struct vc_data *vc, unsigned int pattern, char up_flag) -{ - static unsigned long chords; - static unsigned committed; - - if (!brl_nbchords) - k_deadunicode(vc, BRL_UC_ROW | pattern, up_flag); - else { - committed |= pattern; - chords++; - if (chords == brl_nbchords) { - k_unicode(vc, BRL_UC_ROW | committed, up_flag); - chords = 0; - committed = 0; - } - } -} - -static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) -{ - static unsigned pressed, committing; - static unsigned long releasestart; - - if (kbd->kbdmode != VC_UNICODE) { - if (!up_flag) - pr_warning("keyboard mode must be unicode for braille patterns\n"); - return; - } - - if (!value) { - k_unicode(vc, BRL_UC_ROW, up_flag); - return; - } - - if (value > 8) - return; - - if (!up_flag) { - pressed |= 1 << (value - 1); - if (!brl_timeout) - committing = pressed; - } else if (brl_timeout) { - if (!committing || - time_after(jiffies, - releasestart + msecs_to_jiffies(brl_timeout))) { - committing = pressed; - releasestart = jiffies; - } - pressed &= ~(1 << (value - 1)); - if (!pressed && committing) { - k_brlcommit(vc, committing, 0); - committing = 0; - } - } else { - if (committing) { - k_brlcommit(vc, committing, 0); - committing = 0; - } - pressed &= ~(1 << (value - 1)); - } -} - -/* - * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, - * or (ii) whatever pattern of lights people want to show using KDSETLED, - * or (iii) specified bits of specified words in kernel memory. - */ -unsigned char getledstate(void) -{ - return ledstate; -} - -void setledstate(struct kbd_struct *kbd, unsigned int led) -{ - if (!(led & ~7)) { - ledioctl = led; - kbd->ledmode = LED_SHOW_IOCTL; - } else - kbd->ledmode = LED_SHOW_FLAGS; - - set_leds(); -} - -static inline unsigned char getleds(void) -{ - struct kbd_struct *kbd = kbd_table + fg_console; - unsigned char leds; - int i; - - if (kbd->ledmode == LED_SHOW_IOCTL) - return ledioctl; - - leds = kbd->ledflagstate; - - if (kbd->ledmode == LED_SHOW_MEM) { - for (i = 0; i < 3; i++) - if (ledptrs[i].valid) { - if (*ledptrs[i].addr & ledptrs[i].mask) - leds |= (1 << i); - else - leds &= ~(1 << i); - } - } - return leds; -} - -static int kbd_update_leds_helper(struct input_handle *handle, void *data) -{ - unsigned char leds = *(unsigned char *)data; - - if (test_bit(EV_LED, handle->dev->evbit)) { - input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); - input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); - input_inject_event(handle, EV_SYN, SYN_REPORT, 0); - } - - return 0; -} - -/* - * This is the tasklet that updates LED state on all keyboards - * attached to the box. The reason we use tasklet is that we - * need to handle the scenario when keyboard handler is not - * registered yet but we already getting updates form VT to - * update led state. - */ -static void kbd_bh(unsigned long dummy) -{ - unsigned char leds = getleds(); - - if (leds != ledstate) { - input_handler_for_each_handle(&kbd_handler, &leds, - kbd_update_leds_helper); - ledstate = leds; - } -} - -DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); - -#if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(CONFIG_ALPHA) ||\ - defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) ||\ - defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\ - (defined(CONFIG_ARM) && defined(CONFIG_KEYBOARD_ATKBD) && !defined(CONFIG_ARCH_RPC)) ||\ - defined(CONFIG_AVR32) - -#define HW_RAW(dev) (test_bit(EV_MSC, dev->evbit) && test_bit(MSC_RAW, dev->mscbit) &&\ - ((dev)->id.bustype == BUS_I8042) && ((dev)->id.vendor == 0x0001) && ((dev)->id.product == 0x0001)) - -static const unsigned short x86_keycodes[256] = - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84,118, 86, 87, 88,115,120,119,121,112,123, 92, - 284,285,309, 0,312, 91,327,328,329,331,333,335,336,337,338,339, - 367,288,302,304,350, 89,334,326,267,126,268,269,125,347,348,349, - 360,261,262,263,268,376,100,101,321,316,373,286,289,102,351,355, - 103,104,105,275,287,279,258,106,274,107,294,364,358,363,362,361, - 291,108,381,281,290,272,292,305,280, 99,112,257,306,359,113,114, - 264,117,271,374,379,265,266, 93, 94, 95, 85,259,375,260, 90,116, - 377,109,111,277,278,282,283,295,296,297,299,300,301,293,303,307, - 308,310,313,314,315,317,318,319,320,357,322,323,324,325,276,330, - 332,340,365,342,343,344,345,346,356,270,341,368,369,370,371,372 }; - -#ifdef CONFIG_SPARC -static int sparc_l1_a_state; -extern void sun_do_break(void); -#endif - -static int emulate_raw(struct vc_data *vc, unsigned int keycode, - unsigned char up_flag) -{ - int code; - - switch (keycode) { - - case KEY_PAUSE: - put_queue(vc, 0xe1); - put_queue(vc, 0x1d | up_flag); - put_queue(vc, 0x45 | up_flag); - break; - - case KEY_HANGEUL: - if (!up_flag) - put_queue(vc, 0xf2); - break; - - case KEY_HANJA: - if (!up_flag) - put_queue(vc, 0xf1); - break; - - case KEY_SYSRQ: - /* - * Real AT keyboards (that's what we're trying - * to emulate here emit 0xe0 0x2a 0xe0 0x37 when - * pressing PrtSc/SysRq alone, but simply 0x54 - * when pressing Alt+PrtSc/SysRq. - */ - if (test_bit(KEY_LEFTALT, key_down) || - test_bit(KEY_RIGHTALT, key_down)) { - put_queue(vc, 0x54 | up_flag); - } else { - put_queue(vc, 0xe0); - put_queue(vc, 0x2a | up_flag); - put_queue(vc, 0xe0); - put_queue(vc, 0x37 | up_flag); - } - break; - - default: - if (keycode > 255) - return -1; - - code = x86_keycodes[keycode]; - if (!code) - return -1; - - if (code & 0x100) - put_queue(vc, 0xe0); - put_queue(vc, (code & 0x7f) | up_flag); - - break; - } - - return 0; -} - -#else - -#define HW_RAW(dev) 0 - -static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char up_flag) -{ - if (keycode > 127) - return -1; - - put_queue(vc, keycode | up_flag); - return 0; -} -#endif - -static void kbd_rawcode(unsigned char data) -{ - struct vc_data *vc = vc_cons[fg_console].d; - - kbd = kbd_table + vc->vc_num; - if (kbd->kbdmode == VC_RAW) - put_queue(vc, data); -} - -static void kbd_keycode(unsigned int keycode, int down, int hw_raw) -{ - struct vc_data *vc = vc_cons[fg_console].d; - unsigned short keysym, *key_map; - unsigned char type; - bool raw_mode; - struct tty_struct *tty; - int shift_final; - struct keyboard_notifier_param param = { .vc = vc, .value = keycode, .down = down }; - int rc; - - tty = vc->port.tty; - - if (tty && (!tty->driver_data)) { - /* No driver data? Strange. Okay we fix it then. */ - tty->driver_data = vc; - } - - kbd = kbd_table + vc->vc_num; - -#ifdef CONFIG_SPARC - if (keycode == KEY_STOP) - sparc_l1_a_state = down; -#endif - - rep = (down == 2); - - raw_mode = (kbd->kbdmode == VC_RAW); - if (raw_mode && !hw_raw) - if (emulate_raw(vc, keycode, !down << 7)) - if (keycode < BTN_MISC && printk_ratelimit()) - pr_warning("can't emulate rawmode for keycode %d\n", - keycode); - -#ifdef CONFIG_SPARC - if (keycode == KEY_A && sparc_l1_a_state) { - sparc_l1_a_state = false; - sun_do_break(); - } -#endif - - if (kbd->kbdmode == VC_MEDIUMRAW) { - /* - * This is extended medium raw mode, with keys above 127 - * encoded as 0, high 7 bits, low 7 bits, with the 0 bearing - * the 'up' flag if needed. 0 is reserved, so this shouldn't - * interfere with anything else. The two bytes after 0 will - * always have the up flag set not to interfere with older - * applications. This allows for 16384 different keycodes, - * which should be enough. - */ - if (keycode < 128) { - put_queue(vc, keycode | (!down << 7)); - } else { - put_queue(vc, !down << 7); - put_queue(vc, (keycode >> 7) | 0x80); - put_queue(vc, keycode | 0x80); - } - raw_mode = true; - } - - if (down) - set_bit(keycode, key_down); - else - clear_bit(keycode, key_down); - - if (rep && - (!vc_kbd_mode(kbd, VC_REPEAT) || - (tty && !L_ECHO(tty) && tty_chars_in_buffer(tty)))) { - /* - * Don't repeat a key if the input buffers are not empty and the - * characters get aren't echoed locally. This makes key repeat - * usable with slow applications and under heavy loads. - */ - return; - } - - param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate; - param.ledstate = kbd->ledflagstate; - key_map = key_maps[shift_final]; - - rc = atomic_notifier_call_chain(&keyboard_notifier_list, - KBD_KEYCODE, ¶m); - if (rc == NOTIFY_STOP || !key_map) { - atomic_notifier_call_chain(&keyboard_notifier_list, - KBD_UNBOUND_KEYCODE, ¶m); - compute_shiftstate(); - kbd->slockstate = 0; - return; - } - - if (keycode < NR_KEYS) - keysym = key_map[keycode]; - else if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8) - keysym = U(K(KT_BRL, keycode - KEY_BRL_DOT1 + 1)); - else - return; - - type = KTYP(keysym); - - if (type < 0xf0) { - param.value = keysym; - rc = atomic_notifier_call_chain(&keyboard_notifier_list, - KBD_UNICODE, ¶m); - if (rc != NOTIFY_STOP) - if (down && !raw_mode) - to_utf8(vc, keysym); - return; - } - - type -= 0xf0; - - if (type == KT_LETTER) { - type = KT_LATIN; - if (vc_kbd_led(kbd, VC_CAPSLOCK)) { - key_map = key_maps[shift_final ^ (1 << KG_SHIFT)]; - if (key_map) - keysym = key_map[keycode]; - } - } - - param.value = keysym; - rc = atomic_notifier_call_chain(&keyboard_notifier_list, - KBD_KEYSYM, ¶m); - if (rc == NOTIFY_STOP) - return; - - if (raw_mode && type != KT_SPEC && type != KT_SHIFT) - return; - - (*k_handler[type])(vc, keysym & 0xff, !down); - - param.ledstate = kbd->ledflagstate; - atomic_notifier_call_chain(&keyboard_notifier_list, KBD_POST_KEYSYM, ¶m); - - if (type != KT_SLOCK) - kbd->slockstate = 0; -} - -static void kbd_event(struct input_handle *handle, unsigned int event_type, - unsigned int event_code, int value) -{ - /* We are called with interrupts disabled, just take the lock */ - spin_lock(&kbd_event_lock); - - if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev)) - kbd_rawcode(value); - if (event_type == EV_KEY) - kbd_keycode(event_code, value, HW_RAW(handle->dev)); - - spin_unlock(&kbd_event_lock); - - tasklet_schedule(&keyboard_tasklet); - do_poke_blanked_console = 1; - schedule_console_callback(); -} - -static bool kbd_match(struct input_handler *handler, struct input_dev *dev) -{ - int i; - - if (test_bit(EV_SND, dev->evbit)) - return true; - - if (test_bit(EV_KEY, dev->evbit)) { - for (i = KEY_RESERVED; i < BTN_MISC; i++) - if (test_bit(i, dev->keybit)) - return true; - for (i = KEY_BRL_DOT1; i <= KEY_BRL_DOT10; i++) - if (test_bit(i, dev->keybit)) - return true; - } - - return false; -} - -/* - * When a keyboard (or other input device) is found, the kbd_connect - * function is called. The function then looks at the device, and if it - * likes it, it can open it and get events from it. In this (kbd_connect) - * function, we should decide which VT to bind that keyboard to initially. - */ -static int kbd_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) -{ - struct input_handle *handle; - int error; - - handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); - if (!handle) - return -ENOMEM; - - handle->dev = dev; - handle->handler = handler; - handle->name = "kbd"; - - error = input_register_handle(handle); - if (error) - goto err_free_handle; - - error = input_open_device(handle); - if (error) - goto err_unregister_handle; - - return 0; - - err_unregister_handle: - input_unregister_handle(handle); - err_free_handle: - kfree(handle); - return error; -} - -static void kbd_disconnect(struct input_handle *handle) -{ - input_close_device(handle); - input_unregister_handle(handle); - kfree(handle); -} - -/* - * Start keyboard handler on the new keyboard by refreshing LED state to - * match the rest of the system. - */ -static void kbd_start(struct input_handle *handle) -{ - tasklet_disable(&keyboard_tasklet); - - if (ledstate != 0xff) - kbd_update_leds_helper(handle, &ledstate); - - tasklet_enable(&keyboard_tasklet); -} - -static const struct input_device_id kbd_ids[] = { - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT, - .evbit = { BIT_MASK(EV_KEY) }, - }, - - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT, - .evbit = { BIT_MASK(EV_SND) }, - }, - - { }, /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(input, kbd_ids); - -static struct input_handler kbd_handler = { - .event = kbd_event, - .match = kbd_match, - .connect = kbd_connect, - .disconnect = kbd_disconnect, - .start = kbd_start, - .name = "kbd", - .id_table = kbd_ids, -}; - -int __init kbd_init(void) -{ - int i; - int error; - - for (i = 0; i < MAX_NR_CONSOLES; i++) { - kbd_table[i].ledflagstate = KBD_DEFLEDS; - kbd_table[i].default_ledflagstate = KBD_DEFLEDS; - kbd_table[i].ledmode = LED_SHOW_FLAGS; - kbd_table[i].lockstate = KBD_DEFLOCK; - kbd_table[i].slockstate = 0; - kbd_table[i].modeflags = KBD_DEFMODE; - kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; - } - - error = input_register_handler(&kbd_handler); - if (error) - return error; - - tasklet_enable(&keyboard_tasklet); - tasklet_schedule(&keyboard_tasklet); - - return 0; -} diff --git a/drivers/char/selection.c b/drivers/char/selection.c deleted file mode 100644 index ebae344ce910..000000000000 --- a/drivers/char/selection.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * linux/drivers/char/selection.c - * - * This module exports the functions: - * - * 'int set_selection(struct tiocl_selection __user *, struct tty_struct *)' - * 'void clear_selection(void)' - * 'int paste_selection(struct tty_struct *)' - * 'int sel_loadlut(char __user *)' - * - * Now that /dev/vcs exists, most of this can disappear again. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -/* Don't take this from : 011-015 on the screen aren't spaces */ -#define isspace(c) ((c) == ' ') - -extern void poke_blanked_console(void); - -/* Variables for selection control. */ -/* Use a dynamic buffer, instead of static (Dec 1994) */ -struct vc_data *sel_cons; /* must not be deallocated */ -static int use_unicode; -static volatile int sel_start = -1; /* cleared by clear_selection */ -static int sel_end; -static int sel_buffer_lth; -static char *sel_buffer; - -/* clear_selection, highlight and highlight_pointer can be called - from interrupt (via scrollback/front) */ - -/* set reverse video on characters s-e of console with selection. */ -static inline void highlight(const int s, const int e) -{ - invert_screen(sel_cons, s, e-s+2, 1); -} - -/* use complementary color to show the pointer */ -static inline void highlight_pointer(const int where) -{ - complement_pos(sel_cons, where); -} - -static u16 -sel_pos(int n) -{ - return inverse_translate(sel_cons, screen_glyph(sel_cons, n), - use_unicode); -} - -/* remove the current selection highlight, if any, - from the console holding the selection. */ -void -clear_selection(void) { - highlight_pointer(-1); /* hide the pointer */ - if (sel_start != -1) { - highlight(sel_start, sel_end); - sel_start = -1; - } -} - -/* - * User settable table: what characters are to be considered alphabetic? - * 256 bits - */ -static u32 inwordLut[8]={ - 0x00000000, /* control chars */ - 0x03FF0000, /* digits */ - 0x87FFFFFE, /* uppercase and '_' */ - 0x07FFFFFE, /* lowercase */ - 0x00000000, - 0x00000000, - 0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */ - 0xFF7FFFFF /* latin-1 accented letters, not division sign */ -}; - -static inline int inword(const u16 c) { - return c > 0xff || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1); -} - -/* set inwordLut contents. Invoked by ioctl(). */ -int sel_loadlut(char __user *p) -{ - return copy_from_user(inwordLut, (u32 __user *)(p+4), 32) ? -EFAULT : 0; -} - -/* does screen address p correspond to character at LH/RH edge of screen? */ -static inline int atedge(const int p, int size_row) -{ - return (!(p % size_row) || !((p + 2) % size_row)); -} - -/* constrain v such that v <= u */ -static inline unsigned short limit(const unsigned short v, const unsigned short u) -{ - return (v > u) ? u : v; -} - -/* stores the char in UTF8 and returns the number of bytes used (1-3) */ -static int store_utf8(u16 c, char *p) -{ - if (c < 0x80) { - /* 0******* */ - p[0] = c; - return 1; - } else if (c < 0x800) { - /* 110***** 10****** */ - p[0] = 0xc0 | (c >> 6); - p[1] = 0x80 | (c & 0x3f); - return 2; - } else { - /* 1110**** 10****** 10****** */ - p[0] = 0xe0 | (c >> 12); - p[1] = 0x80 | ((c >> 6) & 0x3f); - p[2] = 0x80 | (c & 0x3f); - return 3; - } -} - -/* set the current selection. Invoked by ioctl() or by kernel code. */ -int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty) -{ - struct vc_data *vc = vc_cons[fg_console].d; - int sel_mode, new_sel_start, new_sel_end, spc; - char *bp, *obp; - int i, ps, pe, multiplier; - u16 c; - struct kbd_struct *kbd = kbd_table + fg_console; - - poke_blanked_console(); - - { unsigned short xs, ys, xe, ye; - - if (!access_ok(VERIFY_READ, sel, sizeof(*sel))) - return -EFAULT; - __get_user(xs, &sel->xs); - __get_user(ys, &sel->ys); - __get_user(xe, &sel->xe); - __get_user(ye, &sel->ye); - __get_user(sel_mode, &sel->sel_mode); - xs--; ys--; xe--; ye--; - xs = limit(xs, vc->vc_cols - 1); - ys = limit(ys, vc->vc_rows - 1); - xe = limit(xe, vc->vc_cols - 1); - ye = limit(ye, vc->vc_rows - 1); - ps = ys * vc->vc_size_row + (xs << 1); - pe = ye * vc->vc_size_row + (xe << 1); - - if (sel_mode == TIOCL_SELCLEAR) { - /* useful for screendump without selection highlights */ - clear_selection(); - return 0; - } - - if (mouse_reporting() && (sel_mode & TIOCL_SELMOUSEREPORT)) { - mouse_report(tty, sel_mode & TIOCL_SELBUTTONMASK, xs, ys); - return 0; - } - } - - if (ps > pe) /* make sel_start <= sel_end */ - { - int tmp = ps; - ps = pe; - pe = tmp; - } - - if (sel_cons != vc_cons[fg_console].d) { - clear_selection(); - sel_cons = vc_cons[fg_console].d; - } - use_unicode = kbd && kbd->kbdmode == VC_UNICODE; - - switch (sel_mode) - { - case TIOCL_SELCHAR: /* character-by-character selection */ - new_sel_start = ps; - new_sel_end = pe; - break; - case TIOCL_SELWORD: /* word-by-word selection */ - spc = isspace(sel_pos(ps)); - for (new_sel_start = ps; ; ps -= 2) - { - if ((spc && !isspace(sel_pos(ps))) || - (!spc && !inword(sel_pos(ps)))) - break; - new_sel_start = ps; - if (!(ps % vc->vc_size_row)) - break; - } - spc = isspace(sel_pos(pe)); - for (new_sel_end = pe; ; pe += 2) - { - if ((spc && !isspace(sel_pos(pe))) || - (!spc && !inword(sel_pos(pe)))) - break; - new_sel_end = pe; - if (!((pe + 2) % vc->vc_size_row)) - break; - } - break; - case TIOCL_SELLINE: /* line-by-line selection */ - new_sel_start = ps - ps % vc->vc_size_row; - new_sel_end = pe + vc->vc_size_row - - pe % vc->vc_size_row - 2; - break; - case TIOCL_SELPOINTER: - highlight_pointer(pe); - return 0; - default: - return -EINVAL; - } - - /* remove the pointer */ - highlight_pointer(-1); - - /* select to end of line if on trailing space */ - if (new_sel_end > new_sel_start && - !atedge(new_sel_end, vc->vc_size_row) && - isspace(sel_pos(new_sel_end))) { - for (pe = new_sel_end + 2; ; pe += 2) - if (!isspace(sel_pos(pe)) || - atedge(pe, vc->vc_size_row)) - break; - if (isspace(sel_pos(pe))) - new_sel_end = pe; - } - if (sel_start == -1) /* no current selection */ - highlight(new_sel_start, new_sel_end); - else if (new_sel_start == sel_start) - { - if (new_sel_end == sel_end) /* no action required */ - return 0; - else if (new_sel_end > sel_end) /* extend to right */ - highlight(sel_end + 2, new_sel_end); - else /* contract from right */ - highlight(new_sel_end + 2, sel_end); - } - else if (new_sel_end == sel_end) - { - if (new_sel_start < sel_start) /* extend to left */ - highlight(new_sel_start, sel_start - 2); - else /* contract from left */ - highlight(sel_start, new_sel_start - 2); - } - else /* some other case; start selection from scratch */ - { - clear_selection(); - highlight(new_sel_start, new_sel_end); - } - sel_start = new_sel_start; - sel_end = new_sel_end; - - /* Allocate a new buffer before freeing the old one ... */ - multiplier = use_unicode ? 3 : 1; /* chars can take up to 3 bytes */ - bp = kmalloc(((sel_end-sel_start)/2+1)*multiplier, GFP_KERNEL); - if (!bp) { - printk(KERN_WARNING "selection: kmalloc() failed\n"); - clear_selection(); - return -ENOMEM; - } - kfree(sel_buffer); - sel_buffer = bp; - - obp = bp; - for (i = sel_start; i <= sel_end; i += 2) { - c = sel_pos(i); - if (use_unicode) - bp += store_utf8(c, bp); - else - *bp++ = c; - if (!isspace(c)) - obp = bp; - if (! ((i + 2) % vc->vc_size_row)) { - /* strip trailing blanks from line and add newline, - unless non-space at end of line. */ - if (obp != bp) { - bp = obp; - *bp++ = '\r'; - } - obp = bp; - } - } - sel_buffer_lth = bp - sel_buffer; - return 0; -} - -/* Insert the contents of the selection buffer into the - * queue of the tty associated with the current console. - * Invoked by ioctl(). - */ -int paste_selection(struct tty_struct *tty) -{ - struct vc_data *vc = tty->driver_data; - int pasted = 0; - unsigned int count; - struct tty_ldisc *ld; - DECLARE_WAITQUEUE(wait, current); - - /* always called with BTM from vt_ioctl */ - WARN_ON(!tty_locked()); - - acquire_console_sem(); - poke_blanked_console(); - release_console_sem(); - - ld = tty_ldisc_ref(tty); - if (!ld) { - tty_unlock(); - ld = tty_ldisc_ref_wait(tty); - tty_lock(); - } - - add_wait_queue(&vc->paste_wait, &wait); - while (sel_buffer && sel_buffer_lth > pasted) { - set_current_state(TASK_INTERRUPTIBLE); - if (test_bit(TTY_THROTTLED, &tty->flags)) { - schedule(); - continue; - } - count = sel_buffer_lth - pasted; - count = min(count, tty->receive_room); - tty->ldisc->ops->receive_buf(tty, sel_buffer + pasted, - NULL, count); - pasted += count; - } - remove_wait_queue(&vc->paste_wait, &wait); - __set_current_state(TASK_RUNNING); - - tty_ldisc_deref(ld); - return 0; -} diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c deleted file mode 100644 index 273ab44cc91d..000000000000 --- a/drivers/char/vc_screen.c +++ /dev/null @@ -1,644 +0,0 @@ -/* - * linux/drivers/char/vc_screen.c - * - * Provide access to virtual console memory. - * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled) - * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63) - * [minor: N] - * - * /dev/vcsaN: idem, but including attributes, and prefixed with - * the 4 bytes lines,columns,x,y (as screendump used to give). - * Attribute/character pair is in native endianity. - * [minor: N+128] - * - * This replaces screendump and part of selection, so that the system - * administrator can control access using file system permissions. - * - * aeb@cwi.nl - efter Friedas begravelse - 950211 - * - * machek@k332.feld.cvut.cz - modified not to send characters to wrong console - * - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...) - * - making it shorter - scr_readw are macros which expand in PRETTY long code - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#undef attr -#undef org -#undef addr -#define HEADER_SIZE 4 - -struct vcs_poll_data { - struct notifier_block notifier; - unsigned int cons_num; - bool seen_last_update; - wait_queue_head_t waitq; - struct fasync_struct *fasync; -}; - -static int -vcs_notifier(struct notifier_block *nb, unsigned long code, void *_param) -{ - struct vt_notifier_param *param = _param; - struct vc_data *vc = param->vc; - struct vcs_poll_data *poll = - container_of(nb, struct vcs_poll_data, notifier); - int currcons = poll->cons_num; - - if (code != VT_UPDATE) - return NOTIFY_DONE; - - if (currcons == 0) - currcons = fg_console; - else - currcons--; - if (currcons != vc->vc_num) - return NOTIFY_DONE; - - poll->seen_last_update = false; - wake_up_interruptible(&poll->waitq); - kill_fasync(&poll->fasync, SIGIO, POLL_IN); - return NOTIFY_OK; -} - -static void -vcs_poll_data_free(struct vcs_poll_data *poll) -{ - unregister_vt_notifier(&poll->notifier); - kfree(poll); -} - -static struct vcs_poll_data * -vcs_poll_data_get(struct file *file) -{ - struct vcs_poll_data *poll = file->private_data; - - if (poll) - return poll; - - poll = kzalloc(sizeof(*poll), GFP_KERNEL); - if (!poll) - return NULL; - poll->cons_num = iminor(file->f_path.dentry->d_inode) & 127; - init_waitqueue_head(&poll->waitq); - poll->notifier.notifier_call = vcs_notifier; - if (register_vt_notifier(&poll->notifier) != 0) { - kfree(poll); - return NULL; - } - - /* - * This code may be called either through ->poll() or ->fasync(). - * If we have two threads using the same file descriptor, they could - * both enter this function, both notice that the structure hasn't - * been allocated yet and go ahead allocating it in parallel, but - * only one of them must survive and be shared otherwise we'd leak - * memory with a dangling notifier callback. - */ - spin_lock(&file->f_lock); - if (!file->private_data) { - file->private_data = poll; - } else { - /* someone else raced ahead of us */ - vcs_poll_data_free(poll); - poll = file->private_data; - } - spin_unlock(&file->f_lock); - - return poll; -} - -static int -vcs_size(struct inode *inode) -{ - int size; - int minor = iminor(inode); - int currcons = minor & 127; - struct vc_data *vc; - - if (currcons == 0) - currcons = fg_console; - else - currcons--; - if (!vc_cons_allocated(currcons)) - return -ENXIO; - vc = vc_cons[currcons].d; - - size = vc->vc_rows * vc->vc_cols; - - if (minor & 128) - size = 2*size + HEADER_SIZE; - return size; -} - -static loff_t vcs_lseek(struct file *file, loff_t offset, int orig) -{ - int size; - - mutex_lock(&con_buf_mtx); - size = vcs_size(file->f_path.dentry->d_inode); - switch (orig) { - default: - mutex_unlock(&con_buf_mtx); - return -EINVAL; - case 2: - offset += size; - break; - case 1: - offset += file->f_pos; - case 0: - break; - } - if (offset < 0 || offset > size) { - mutex_unlock(&con_buf_mtx); - return -EINVAL; - } - file->f_pos = offset; - mutex_unlock(&con_buf_mtx); - return file->f_pos; -} - - -static ssize_t -vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - struct inode *inode = file->f_path.dentry->d_inode; - unsigned int currcons = iminor(inode); - struct vc_data *vc; - struct vcs_poll_data *poll; - long pos; - long viewed, attr, read; - int col, maxcol; - unsigned short *org = NULL; - ssize_t ret; - - mutex_lock(&con_buf_mtx); - - pos = *ppos; - - /* Select the proper current console and verify - * sanity of the situation under the console lock. - */ - acquire_console_sem(); - - attr = (currcons & 128); - currcons = (currcons & 127); - if (currcons == 0) { - currcons = fg_console; - viewed = 1; - } else { - currcons--; - viewed = 0; - } - ret = -ENXIO; - if (!vc_cons_allocated(currcons)) - goto unlock_out; - vc = vc_cons[currcons].d; - - ret = -EINVAL; - if (pos < 0) - goto unlock_out; - poll = file->private_data; - if (count && poll) - poll->seen_last_update = true; - read = 0; - ret = 0; - while (count) { - char *con_buf0, *con_buf_start; - long this_round, size; - ssize_t orig_count; - long p = pos; - - /* Check whether we are above size each round, - * as copy_to_user at the end of this loop - * could sleep. - */ - size = vcs_size(inode); - if (pos >= size) - break; - if (count > size - pos) - count = size - pos; - - this_round = count; - if (this_round > CON_BUF_SIZE) - this_round = CON_BUF_SIZE; - - /* Perform the whole read into the local con_buf. - * Then we can drop the console spinlock and safely - * attempt to move it to userspace. - */ - - con_buf_start = con_buf0 = con_buf; - orig_count = this_round; - maxcol = vc->vc_cols; - if (!attr) { - org = screen_pos(vc, p, viewed); - col = p % maxcol; - p += maxcol - col; - while (this_round-- > 0) { - *con_buf0++ = (vcs_scr_readw(vc, org++) & 0xff); - if (++col == maxcol) { - org = screen_pos(vc, p, viewed); - col = 0; - p += maxcol; - } - } - } else { - if (p < HEADER_SIZE) { - size_t tmp_count; - - con_buf0[0] = (char)vc->vc_rows; - con_buf0[1] = (char)vc->vc_cols; - getconsxy(vc, con_buf0 + 2); - - con_buf_start += p; - this_round += p; - if (this_round > CON_BUF_SIZE) { - this_round = CON_BUF_SIZE; - orig_count = this_round - p; - } - - tmp_count = HEADER_SIZE; - if (tmp_count > this_round) - tmp_count = this_round; - - /* Advance state pointers and move on. */ - this_round -= tmp_count; - p = HEADER_SIZE; - con_buf0 = con_buf + HEADER_SIZE; - /* If this_round >= 0, then p is even... */ - } else if (p & 1) { - /* Skip first byte for output if start address is odd - * Update region sizes up/down depending on free - * space in buffer. - */ - con_buf_start++; - if (this_round < CON_BUF_SIZE) - this_round++; - else - orig_count--; - } - if (this_round > 0) { - unsigned short *tmp_buf = (unsigned short *)con_buf0; - - p -= HEADER_SIZE; - p /= 2; - col = p % maxcol; - - org = screen_pos(vc, p, viewed); - p += maxcol - col; - - /* Buffer has even length, so we can always copy - * character + attribute. We do not copy last byte - * to userspace if this_round is odd. - */ - this_round = (this_round + 1) >> 1; - - while (this_round) { - *tmp_buf++ = vcs_scr_readw(vc, org++); - this_round --; - if (++col == maxcol) { - org = screen_pos(vc, p, viewed); - col = 0; - p += maxcol; - } - } - } - } - - /* Finally, release the console semaphore while we push - * all the data to userspace from our temporary buffer. - * - * AKPM: Even though it's a semaphore, we should drop it because - * the pagefault handling code may want to call printk(). - */ - - release_console_sem(); - ret = copy_to_user(buf, con_buf_start, orig_count); - acquire_console_sem(); - - if (ret) { - read += (orig_count - ret); - ret = -EFAULT; - break; - } - buf += orig_count; - pos += orig_count; - read += orig_count; - count -= orig_count; - } - *ppos += read; - if (read) - ret = read; -unlock_out: - release_console_sem(); - mutex_unlock(&con_buf_mtx); - return ret; -} - -static ssize_t -vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - struct inode *inode = file->f_path.dentry->d_inode; - unsigned int currcons = iminor(inode); - struct vc_data *vc; - long pos; - long viewed, attr, size, written; - char *con_buf0; - int col, maxcol; - u16 *org0 = NULL, *org = NULL; - size_t ret; - - mutex_lock(&con_buf_mtx); - - pos = *ppos; - - /* Select the proper current console and verify - * sanity of the situation under the console lock. - */ - acquire_console_sem(); - - attr = (currcons & 128); - currcons = (currcons & 127); - - if (currcons == 0) { - currcons = fg_console; - viewed = 1; - } else { - currcons--; - viewed = 0; - } - ret = -ENXIO; - if (!vc_cons_allocated(currcons)) - goto unlock_out; - vc = vc_cons[currcons].d; - - size = vcs_size(inode); - ret = -EINVAL; - if (pos < 0 || pos > size) - goto unlock_out; - if (count > size - pos) - count = size - pos; - written = 0; - while (count) { - long this_round = count; - size_t orig_count; - long p; - - if (this_round > CON_BUF_SIZE) - this_round = CON_BUF_SIZE; - - /* Temporarily drop the console lock so that we can read - * in the write data from userspace safely. - */ - release_console_sem(); - ret = copy_from_user(con_buf, buf, this_round); - acquire_console_sem(); - - if (ret) { - this_round -= ret; - if (!this_round) { - /* Abort loop if no data were copied. Otherwise - * fail with -EFAULT. - */ - if (written) - break; - ret = -EFAULT; - goto unlock_out; - } - } - - /* The vcs_size might have changed while we slept to grab - * the user buffer, so recheck. - * Return data written up to now on failure. - */ - size = vcs_size(inode); - if (pos >= size) - break; - if (this_round > size - pos) - this_round = size - pos; - - /* OK, now actually push the write to the console - * under the lock using the local kernel buffer. - */ - - con_buf0 = con_buf; - orig_count = this_round; - maxcol = vc->vc_cols; - p = pos; - if (!attr) { - org0 = org = screen_pos(vc, p, viewed); - col = p % maxcol; - p += maxcol - col; - - while (this_round > 0) { - unsigned char c = *con_buf0++; - - this_round--; - vcs_scr_writew(vc, - (vcs_scr_readw(vc, org) & 0xff00) | c, org); - org++; - if (++col == maxcol) { - org = screen_pos(vc, p, viewed); - col = 0; - p += maxcol; - } - } - } else { - if (p < HEADER_SIZE) { - char header[HEADER_SIZE]; - - getconsxy(vc, header + 2); - while (p < HEADER_SIZE && this_round > 0) { - this_round--; - header[p++] = *con_buf0++; - } - if (!viewed) - putconsxy(vc, header + 2); - } - p -= HEADER_SIZE; - col = (p/2) % maxcol; - if (this_round > 0) { - org0 = org = screen_pos(vc, p/2, viewed); - if ((p & 1) && this_round > 0) { - char c; - - this_round--; - c = *con_buf0++; -#ifdef __BIG_ENDIAN - vcs_scr_writew(vc, c | - (vcs_scr_readw(vc, org) & 0xff00), org); -#else - vcs_scr_writew(vc, (c << 8) | - (vcs_scr_readw(vc, org) & 0xff), org); -#endif - org++; - p++; - if (++col == maxcol) { - org = screen_pos(vc, p/2, viewed); - col = 0; - } - } - p /= 2; - p += maxcol - col; - } - while (this_round > 1) { - unsigned short w; - - w = get_unaligned(((unsigned short *)con_buf0)); - vcs_scr_writew(vc, w, org++); - con_buf0 += 2; - this_round -= 2; - if (++col == maxcol) { - org = screen_pos(vc, p, viewed); - col = 0; - p += maxcol; - } - } - if (this_round > 0) { - unsigned char c; - - c = *con_buf0++; -#ifdef __BIG_ENDIAN - vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff) | (c << 8), org); -#else - vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff00) | c, org); -#endif - } - } - count -= orig_count; - written += orig_count; - buf += orig_count; - pos += orig_count; - if (org0) - update_region(vc, (unsigned long)(org0), org - org0); - } - *ppos += written; - ret = written; - if (written) - vcs_scr_updated(vc); - -unlock_out: - release_console_sem(); - - mutex_unlock(&con_buf_mtx); - - return ret; -} - -static unsigned int -vcs_poll(struct file *file, poll_table *wait) -{ - struct vcs_poll_data *poll = vcs_poll_data_get(file); - int ret = 0; - - if (poll) { - poll_wait(file, &poll->waitq, wait); - if (!poll->seen_last_update) - ret = POLLIN | POLLRDNORM; - } - return ret; -} - -static int -vcs_fasync(int fd, struct file *file, int on) -{ - struct vcs_poll_data *poll = file->private_data; - - if (!poll) { - /* don't allocate anything if all we want is disable fasync */ - if (!on) - return 0; - poll = vcs_poll_data_get(file); - if (!poll) - return -ENOMEM; - } - - return fasync_helper(fd, file, on, &poll->fasync); -} - -static int -vcs_open(struct inode *inode, struct file *filp) -{ - unsigned int currcons = iminor(inode) & 127; - int ret = 0; - - tty_lock(); - if(currcons && !vc_cons_allocated(currcons-1)) - ret = -ENXIO; - tty_unlock(); - return ret; -} - -static int vcs_release(struct inode *inode, struct file *file) -{ - struct vcs_poll_data *poll = file->private_data; - - if (poll) - vcs_poll_data_free(poll); - return 0; -} - -static const struct file_operations vcs_fops = { - .llseek = vcs_lseek, - .read = vcs_read, - .write = vcs_write, - .poll = vcs_poll, - .fasync = vcs_fasync, - .open = vcs_open, - .release = vcs_release, -}; - -static struct class *vc_class; - -void vcs_make_sysfs(int index) -{ - device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL, - "vcs%u", index + 1); - device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL, - "vcsa%u", index + 1); -} - -void vcs_remove_sysfs(int index) -{ - device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1)); - device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129)); -} - -int __init vcs_init(void) -{ - unsigned int i; - - if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops)) - panic("unable to get major %d for vcs device", VCS_MAJOR); - vc_class = class_create(THIS_MODULE, "vc"); - - device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); - device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); - for (i = 0; i < MIN_NR_CONSOLES; i++) - vcs_make_sysfs(i); - return 0; -} diff --git a/drivers/char/vt.c b/drivers/char/vt.c deleted file mode 100644 index a8ec48ed14d9..000000000000 --- a/drivers/char/vt.c +++ /dev/null @@ -1,4209 +0,0 @@ -/* - * linux/drivers/char/vt.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * Hopefully this will be a rather complete VT102 implementation. - * - * Beeping thanks to John T Kohl. - * - * Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics - * Chars, and VT100 enhancements by Peter MacDonald. - * - * Copy and paste function by Andrew Haylett, - * some enhancements by Alessandro Rubini. - * - * Code to check for different video-cards mostly by Galen Hunt, - * - * - * Rudimentary ISO 10646/Unicode/UTF-8 character set support by - * Markus Kuhn, . - * - * Dynamic allocation of consoles, aeb@cwi.nl, May 1994 - * Resizing of consoles, aeb, 940926 - * - * Code for xterm like mouse click reporting by Peter Orbaek 20-Jul-94 - * - * - * User-defined bell sound, new setterm control sequences and printk - * redirection by Martin Mares 19-Nov-95 - * - * APM screenblank bug fixed Takashi Manabe - * - * Merge with the abstract console driver by Geert Uytterhoeven - * , Jan 1997. - * - * Original m68k console driver modifications by - * - * - Arno Griffioen - * - David Carter - * - * The abstract console driver provides a generic interface for a text - * console. It supports VGA text mode, frame buffer based graphical consoles - * and special graphics processors that are only accessible through some - * registers (e.g. a TMS340x0 GSP). - * - * The interface to the hardware is specified using a special structure - * (struct consw) which contains function pointers to console operations - * (see for more information). - * - * Support for changeable cursor shape - * by Pavel Machek , August 1997 - * - * Ported to i386 and con_scrolldelta fixed - * by Emmanuel Marty , April 1998 - * - * Resurrected character buffers in videoram plus lots of other trickery - * by Martin Mares , July 1998 - * - * Removed old-style timers, introduced console_timer, made timer - * deletion SMP-safe. 17Jun00, Andrew Morton - * - * Removed console_lock, enabled interrupts across all console operations - * 13 March 2001, Andrew Morton - * - * Fixed UTF-8 mode so alternate charset modes always work according - * to control sequences interpreted in do_con_trol function - * preserving backward VT100 semigraphics compatibility, - * malformed UTF sequences represented as sequences of replacement glyphs, - * original codes or '?' as a last resort if replacement glyph is undefined - * by Adam Tla/lka , Aug 2006 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MAX_NR_CON_DRIVER 16 - -#define CON_DRIVER_FLAG_MODULE 1 -#define CON_DRIVER_FLAG_INIT 2 -#define CON_DRIVER_FLAG_ATTR 4 - -struct con_driver { - const struct consw *con; - const char *desc; - struct device *dev; - int node; - int first; - int last; - int flag; -}; - -static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER]; -const struct consw *conswitchp; - -/* A bitmap for codes <32. A bit of 1 indicates that the code - * corresponding to that bit number invokes some special action - * (such as cursor movement) and should not be displayed as a - * glyph unless the disp_ctrl mode is explicitly enabled. - */ -#define CTRL_ACTION 0x0d00ff81 -#define CTRL_ALWAYS 0x0800f501 /* Cannot be overridden by disp_ctrl */ - -/* - * Here is the default bell parameters: 750HZ, 1/8th of a second - */ -#define DEFAULT_BELL_PITCH 750 -#define DEFAULT_BELL_DURATION (HZ/8) - -struct vc vc_cons [MAX_NR_CONSOLES]; - -#ifndef VT_SINGLE_DRIVER -static const struct consw *con_driver_map[MAX_NR_CONSOLES]; -#endif - -static int con_open(struct tty_struct *, struct file *); -static void vc_init(struct vc_data *vc, unsigned int rows, - unsigned int cols, int do_clear); -static void gotoxy(struct vc_data *vc, int new_x, int new_y); -static void save_cur(struct vc_data *vc); -static void reset_terminal(struct vc_data *vc, int do_clear); -static void con_flush_chars(struct tty_struct *tty); -static int set_vesa_blanking(char __user *p); -static void set_cursor(struct vc_data *vc); -static void hide_cursor(struct vc_data *vc); -static void console_callback(struct work_struct *ignored); -static void blank_screen_t(unsigned long dummy); -static void set_palette(struct vc_data *vc); - -static int printable; /* Is console ready for printing? */ -int default_utf8 = true; -module_param(default_utf8, int, S_IRUGO | S_IWUSR); -int global_cursor_default = -1; -module_param(global_cursor_default, int, S_IRUGO | S_IWUSR); - -static int cur_default = CUR_DEFAULT; -module_param(cur_default, int, S_IRUGO | S_IWUSR); - -/* - * ignore_poke: don't unblank the screen when things are typed. This is - * mainly for the privacy of braille terminal users. - */ -static int ignore_poke; - -int do_poke_blanked_console; -int console_blanked; - -static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */ -static int vesa_off_interval; -static int blankinterval = 10*60; -core_param(consoleblank, blankinterval, int, 0444); - -static DECLARE_WORK(console_work, console_callback); - -/* - * fg_console is the current virtual console, - * last_console is the last used one, - * want_console is the console we want to switch to, - * saved_* variants are for save/restore around kernel debugger enter/leave - */ -int fg_console; -int last_console; -int want_console = -1; -static int saved_fg_console; -static int saved_last_console; -static int saved_want_console; -static int saved_vc_mode; -static int saved_console_blanked; - -/* - * For each existing display, we have a pointer to console currently visible - * on that display, allowing consoles other than fg_console to be refreshed - * appropriately. Unless the low-level driver supplies its own display_fg - * variable, we use this one for the "master display". - */ -static struct vc_data *master_display_fg; - -/* - * Unfortunately, we need to delay tty echo when we're currently writing to the - * console since the code is (and always was) not re-entrant, so we schedule - * all flip requests to process context with schedule-task() and run it from - * console_callback(). - */ - -/* - * For the same reason, we defer scrollback to the console callback. - */ -static int scrollback_delta; - -/* - * Hook so that the power management routines can (un)blank - * the console on our behalf. - */ -int (*console_blank_hook)(int); - -static DEFINE_TIMER(console_timer, blank_screen_t, 0, 0); -static int blank_state; -static int blank_timer_expired; -enum { - blank_off = 0, - blank_normal_wait, - blank_vesa_wait, -}; - -/* - * Notifier list for console events. - */ -static ATOMIC_NOTIFIER_HEAD(vt_notifier_list); - -int register_vt_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&vt_notifier_list, nb); -} -EXPORT_SYMBOL_GPL(register_vt_notifier); - -int unregister_vt_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&vt_notifier_list, nb); -} -EXPORT_SYMBOL_GPL(unregister_vt_notifier); - -static void notify_write(struct vc_data *vc, unsigned int unicode) -{ - struct vt_notifier_param param = { .vc = vc, unicode = unicode }; - atomic_notifier_call_chain(&vt_notifier_list, VT_WRITE, ¶m); -} - -static void notify_update(struct vc_data *vc) -{ - struct vt_notifier_param param = { .vc = vc }; - atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, ¶m); -} -/* - * Low-Level Functions - */ - -#define IS_FG(vc) ((vc)->vc_num == fg_console) - -#ifdef VT_BUF_VRAM_ONLY -#define DO_UPDATE(vc) 0 -#else -#define DO_UPDATE(vc) (CON_IS_VISIBLE(vc) && !console_blanked) -#endif - -static inline unsigned short *screenpos(struct vc_data *vc, int offset, int viewed) -{ - unsigned short *p; - - if (!viewed) - p = (unsigned short *)(vc->vc_origin + offset); - else if (!vc->vc_sw->con_screen_pos) - p = (unsigned short *)(vc->vc_visible_origin + offset); - else - p = vc->vc_sw->con_screen_pos(vc, offset); - return p; -} - -/* Called from the keyboard irq path.. */ -static inline void scrolldelta(int lines) -{ - /* FIXME */ - /* scrolldelta needs some kind of consistency lock, but the BKL was - and still is not protecting versus the scheduled back end */ - scrollback_delta += lines; - schedule_console_callback(); -} - -void schedule_console_callback(void) -{ - schedule_work(&console_work); -} - -static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr) -{ - unsigned short *d, *s; - - if (t+nr >= b) - nr = b - t - 1; - if (b > vc->vc_rows || t >= b || nr < 1) - return; - if (CON_IS_VISIBLE(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_UP, nr)) - return; - d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); - s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr)); - scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row); - scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_video_erase_char, - vc->vc_size_row * nr); -} - -static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, int nr) -{ - unsigned short *s; - unsigned int step; - - if (t+nr >= b) - nr = b - t - 1; - if (b > vc->vc_rows || t >= b || nr < 1) - return; - if (CON_IS_VISIBLE(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_DOWN, nr)) - return; - s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); - step = vc->vc_cols * nr; - scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row); - scr_memsetw(s, vc->vc_video_erase_char, 2 * step); -} - -static void do_update_region(struct vc_data *vc, unsigned long start, int count) -{ -#ifndef VT_BUF_VRAM_ONLY - unsigned int xx, yy, offset; - u16 *p; - - p = (u16 *) start; - if (!vc->vc_sw->con_getxy) { - offset = (start - vc->vc_origin) / 2; - xx = offset % vc->vc_cols; - yy = offset / vc->vc_cols; - } else { - int nxx, nyy; - start = vc->vc_sw->con_getxy(vc, start, &nxx, &nyy); - xx = nxx; yy = nyy; - } - for(;;) { - u16 attrib = scr_readw(p) & 0xff00; - int startx = xx; - u16 *q = p; - while (xx < vc->vc_cols && count) { - if (attrib != (scr_readw(p) & 0xff00)) { - if (p > q) - vc->vc_sw->con_putcs(vc, q, p-q, yy, startx); - startx = xx; - q = p; - attrib = scr_readw(p) & 0xff00; - } - p++; - xx++; - count--; - } - if (p > q) - vc->vc_sw->con_putcs(vc, q, p-q, yy, startx); - if (!count) - break; - xx = 0; - yy++; - if (vc->vc_sw->con_getxy) { - p = (u16 *)start; - start = vc->vc_sw->con_getxy(vc, start, NULL, NULL); - } - } -#endif -} - -void update_region(struct vc_data *vc, unsigned long start, int count) -{ - WARN_CONSOLE_UNLOCKED(); - - if (DO_UPDATE(vc)) { - hide_cursor(vc); - do_update_region(vc, start, count); - set_cursor(vc); - } -} - -/* Structure of attributes is hardware-dependent */ - -static u8 build_attr(struct vc_data *vc, u8 _color, u8 _intensity, u8 _blink, - u8 _underline, u8 _reverse, u8 _italic) -{ - if (vc->vc_sw->con_build_attr) - return vc->vc_sw->con_build_attr(vc, _color, _intensity, - _blink, _underline, _reverse, _italic); - -#ifndef VT_BUF_VRAM_ONLY -/* - * ++roman: I completely changed the attribute format for monochrome - * mode (!can_do_color). The formerly used MDA (monochrome display - * adapter) format didn't allow the combination of certain effects. - * Now the attribute is just a bit vector: - * Bit 0..1: intensity (0..2) - * Bit 2 : underline - * Bit 3 : reverse - * Bit 7 : blink - */ - { - u8 a = _color; - if (!vc->vc_can_do_color) - return _intensity | - (_italic ? 2 : 0) | - (_underline ? 4 : 0) | - (_reverse ? 8 : 0) | - (_blink ? 0x80 : 0); - if (_italic) - a = (a & 0xF0) | vc->vc_itcolor; - else if (_underline) - a = (a & 0xf0) | vc->vc_ulcolor; - else if (_intensity == 0) - a = (a & 0xf0) | vc->vc_ulcolor; - if (_reverse) - a = ((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77); - if (_blink) - a ^= 0x80; - if (_intensity == 2) - a ^= 0x08; - if (vc->vc_hi_font_mask == 0x100) - a <<= 1; - return a; - } -#else - return 0; -#endif -} - -static void update_attr(struct vc_data *vc) -{ - vc->vc_attr = build_attr(vc, vc->vc_color, vc->vc_intensity, - vc->vc_blink, vc->vc_underline, - vc->vc_reverse ^ vc->vc_decscnm, vc->vc_italic); - vc->vc_video_erase_char = (build_attr(vc, vc->vc_color, 1, vc->vc_blink, 0, vc->vc_decscnm, 0) << 8) | ' '; -} - -/* Note: inverting the screen twice should revert to the original state */ -void invert_screen(struct vc_data *vc, int offset, int count, int viewed) -{ - unsigned short *p; - - WARN_CONSOLE_UNLOCKED(); - - count /= 2; - p = screenpos(vc, offset, viewed); - if (vc->vc_sw->con_invert_region) - vc->vc_sw->con_invert_region(vc, p, count); -#ifndef VT_BUF_VRAM_ONLY - else { - u16 *q = p; - int cnt = count; - u16 a; - - if (!vc->vc_can_do_color) { - while (cnt--) { - a = scr_readw(q); - a ^= 0x0800; - scr_writew(a, q); - q++; - } - } else if (vc->vc_hi_font_mask == 0x100) { - while (cnt--) { - a = scr_readw(q); - a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | (((a) & 0x0e00) << 4); - scr_writew(a, q); - q++; - } - } else { - while (cnt--) { - a = scr_readw(q); - a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4); - scr_writew(a, q); - q++; - } - } - } -#endif - if (DO_UPDATE(vc)) - do_update_region(vc, (unsigned long) p, count); -} - -/* used by selection: complement pointer position */ -void complement_pos(struct vc_data *vc, int offset) -{ - static int old_offset = -1; - static unsigned short old; - static unsigned short oldx, oldy; - - WARN_CONSOLE_UNLOCKED(); - - if (old_offset != -1 && old_offset >= 0 && - old_offset < vc->vc_screenbuf_size) { - scr_writew(old, screenpos(vc, old_offset, 1)); - if (DO_UPDATE(vc)) - vc->vc_sw->con_putc(vc, old, oldy, oldx); - } - - old_offset = offset; - - if (offset != -1 && offset >= 0 && - offset < vc->vc_screenbuf_size) { - unsigned short new; - unsigned short *p; - p = screenpos(vc, offset, 1); - old = scr_readw(p); - new = old ^ vc->vc_complement_mask; - scr_writew(new, p); - if (DO_UPDATE(vc)) { - oldx = (offset >> 1) % vc->vc_cols; - oldy = (offset >> 1) / vc->vc_cols; - vc->vc_sw->con_putc(vc, new, oldy, oldx); - } - } - -} - -static void insert_char(struct vc_data *vc, unsigned int nr) -{ - unsigned short *p, *q = (unsigned short *)vc->vc_pos; - - p = q + vc->vc_cols - nr - vc->vc_x; - while (--p >= q) - scr_writew(scr_readw(p), p + nr); - scr_memsetw(q, vc->vc_video_erase_char, nr * 2); - vc->vc_need_wrap = 0; - if (DO_UPDATE(vc)) { - unsigned short oldattr = vc->vc_attr; - vc->vc_sw->con_bmove(vc, vc->vc_y, vc->vc_x, vc->vc_y, vc->vc_x + nr, 1, - vc->vc_cols - vc->vc_x - nr); - vc->vc_attr = vc->vc_video_erase_char >> 8; - while (nr--) - vc->vc_sw->con_putc(vc, vc->vc_video_erase_char, vc->vc_y, vc->vc_x + nr); - vc->vc_attr = oldattr; - } -} - -static void delete_char(struct vc_data *vc, unsigned int nr) -{ - unsigned int i = vc->vc_x; - unsigned short *p = (unsigned short *)vc->vc_pos; - - while (++i <= vc->vc_cols - nr) { - scr_writew(scr_readw(p+nr), p); - p++; - } - scr_memsetw(p, vc->vc_video_erase_char, nr * 2); - vc->vc_need_wrap = 0; - if (DO_UPDATE(vc)) { - unsigned short oldattr = vc->vc_attr; - vc->vc_sw->con_bmove(vc, vc->vc_y, vc->vc_x + nr, vc->vc_y, vc->vc_x, 1, - vc->vc_cols - vc->vc_x - nr); - vc->vc_attr = vc->vc_video_erase_char >> 8; - while (nr--) - vc->vc_sw->con_putc(vc, vc->vc_video_erase_char, vc->vc_y, - vc->vc_cols - 1 - nr); - vc->vc_attr = oldattr; - } -} - -static int softcursor_original; - -static void add_softcursor(struct vc_data *vc) -{ - int i = scr_readw((u16 *) vc->vc_pos); - u32 type = vc->vc_cursor_type; - - if (! (type & 0x10)) return; - if (softcursor_original != -1) return; - softcursor_original = i; - i |= ((type >> 8) & 0xff00 ); - i ^= ((type) & 0xff00 ); - if ((type & 0x20) && ((softcursor_original & 0x7000) == (i & 0x7000))) i ^= 0x7000; - if ((type & 0x40) && ((i & 0x700) == ((i & 0x7000) >> 4))) i ^= 0x0700; - scr_writew(i, (u16 *) vc->vc_pos); - if (DO_UPDATE(vc)) - vc->vc_sw->con_putc(vc, i, vc->vc_y, vc->vc_x); -} - -static void hide_softcursor(struct vc_data *vc) -{ - if (softcursor_original != -1) { - scr_writew(softcursor_original, (u16 *)vc->vc_pos); - if (DO_UPDATE(vc)) - vc->vc_sw->con_putc(vc, softcursor_original, - vc->vc_y, vc->vc_x); - softcursor_original = -1; - } -} - -static void hide_cursor(struct vc_data *vc) -{ - if (vc == sel_cons) - clear_selection(); - vc->vc_sw->con_cursor(vc, CM_ERASE); - hide_softcursor(vc); -} - -static void set_cursor(struct vc_data *vc) -{ - if (!IS_FG(vc) || console_blanked || - vc->vc_mode == KD_GRAPHICS) - return; - if (vc->vc_deccm) { - if (vc == sel_cons) - clear_selection(); - add_softcursor(vc); - if ((vc->vc_cursor_type & 0x0f) != 1) - vc->vc_sw->con_cursor(vc, CM_DRAW); - } else - hide_cursor(vc); -} - -static void set_origin(struct vc_data *vc) -{ - WARN_CONSOLE_UNLOCKED(); - - if (!CON_IS_VISIBLE(vc) || - !vc->vc_sw->con_set_origin || - !vc->vc_sw->con_set_origin(vc)) - vc->vc_origin = (unsigned long)vc->vc_screenbuf; - vc->vc_visible_origin = vc->vc_origin; - vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size; - vc->vc_pos = vc->vc_origin + vc->vc_size_row * vc->vc_y + 2 * vc->vc_x; -} - -static inline void save_screen(struct vc_data *vc) -{ - WARN_CONSOLE_UNLOCKED(); - - if (vc->vc_sw->con_save_screen) - vc->vc_sw->con_save_screen(vc); -} - -/* - * Redrawing of screen - */ - -static void clear_buffer_attributes(struct vc_data *vc) -{ - unsigned short *p = (unsigned short *)vc->vc_origin; - int count = vc->vc_screenbuf_size / 2; - int mask = vc->vc_hi_font_mask | 0xff; - - for (; count > 0; count--, p++) { - scr_writew((scr_readw(p)&mask) | (vc->vc_video_erase_char & ~mask), p); - } -} - -void redraw_screen(struct vc_data *vc, int is_switch) -{ - int redraw = 0; - - WARN_CONSOLE_UNLOCKED(); - - if (!vc) { - /* strange ... */ - /* printk("redraw_screen: tty %d not allocated ??\n", new_console+1); */ - return; - } - - if (is_switch) { - struct vc_data *old_vc = vc_cons[fg_console].d; - if (old_vc == vc) - return; - if (!CON_IS_VISIBLE(vc)) - redraw = 1; - *vc->vc_display_fg = vc; - fg_console = vc->vc_num; - hide_cursor(old_vc); - if (!CON_IS_VISIBLE(old_vc)) { - save_screen(old_vc); - set_origin(old_vc); - } - } else { - hide_cursor(vc); - redraw = 1; - } - - if (redraw) { - int update; - int old_was_color = vc->vc_can_do_color; - - set_origin(vc); - update = vc->vc_sw->con_switch(vc); - set_palette(vc); - /* - * If console changed from mono<->color, the best we can do - * is to clear the buffer attributes. As it currently stands, - * rebuilding new attributes from the old buffer is not doable - * without overly complex code. - */ - if (old_was_color != vc->vc_can_do_color) { - update_attr(vc); - clear_buffer_attributes(vc); - } - - /* Forcibly update if we're panicing */ - if ((update && vc->vc_mode != KD_GRAPHICS) || - vt_force_oops_output(vc)) - do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2); - } - set_cursor(vc); - if (is_switch) { - set_leds(); - compute_shiftstate(); - notify_update(vc); - } -} - -/* - * Allocation, freeing and resizing of VTs. - */ - -int vc_cons_allocated(unsigned int i) -{ - return (i < MAX_NR_CONSOLES && vc_cons[i].d); -} - -static void visual_init(struct vc_data *vc, int num, int init) -{ - /* ++Geert: vc->vc_sw->con_init determines console size */ - if (vc->vc_sw) - module_put(vc->vc_sw->owner); - vc->vc_sw = conswitchp; -#ifndef VT_SINGLE_DRIVER - if (con_driver_map[num]) - vc->vc_sw = con_driver_map[num]; -#endif - __module_get(vc->vc_sw->owner); - vc->vc_num = num; - vc->vc_display_fg = &master_display_fg; - vc->vc_uni_pagedir_loc = &vc->vc_uni_pagedir; - vc->vc_uni_pagedir = 0; - vc->vc_hi_font_mask = 0; - vc->vc_complement_mask = 0; - vc->vc_can_do_color = 0; - vc->vc_panic_force_write = false; - vc->vc_sw->con_init(vc, init); - if (!vc->vc_complement_mask) - vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; - vc->vc_s_complement_mask = vc->vc_complement_mask; - vc->vc_size_row = vc->vc_cols << 1; - vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row; -} - -int vc_allocate(unsigned int currcons) /* return 0 on success */ -{ - WARN_CONSOLE_UNLOCKED(); - - if (currcons >= MAX_NR_CONSOLES) - return -ENXIO; - if (!vc_cons[currcons].d) { - struct vc_data *vc; - struct vt_notifier_param param; - - /* prevent users from taking too much memory */ - if (currcons >= MAX_NR_USER_CONSOLES && !capable(CAP_SYS_RESOURCE)) - return -EPERM; - - /* due to the granularity of kmalloc, we waste some memory here */ - /* the alloc is done in two steps, to optimize the common situation - of a 25x80 console (structsize=216, screenbuf_size=4000) */ - /* although the numbers above are not valid since long ago, the - point is still up-to-date and the comment still has its value - even if only as a historical artifact. --mj, July 1998 */ - param.vc = vc = kzalloc(sizeof(struct vc_data), GFP_KERNEL); - if (!vc) - return -ENOMEM; - vc_cons[currcons].d = vc; - tty_port_init(&vc->port); - INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); - visual_init(vc, currcons, 1); - if (!*vc->vc_uni_pagedir_loc) - con_set_default_unimap(vc); - vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL); - if (!vc->vc_screenbuf) { - kfree(vc); - vc_cons[currcons].d = NULL; - return -ENOMEM; - } - - /* If no drivers have overridden us and the user didn't pass a - boot option, default to displaying the cursor */ - if (global_cursor_default == -1) - global_cursor_default = 1; - - vc_init(vc, vc->vc_rows, vc->vc_cols, 1); - vcs_make_sysfs(currcons); - atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, ¶m); - } - return 0; -} - -static inline int resize_screen(struct vc_data *vc, int width, int height, - int user) -{ - /* Resizes the resolution of the display adapater */ - int err = 0; - - if (vc->vc_mode != KD_GRAPHICS && vc->vc_sw->con_resize) - err = vc->vc_sw->con_resize(vc, width, height, user); - - return err; -} - -/* - * Change # of rows and columns (0 means unchanged/the size of fg_console) - * [this is to be used together with some user program - * like resize that changes the hardware videomode] - */ -#define VC_RESIZE_MAXCOL (32767) -#define VC_RESIZE_MAXROW (32767) - -/** - * vc_do_resize - resizing method for the tty - * @tty: tty being resized - * @real_tty: real tty (different to tty if a pty/tty pair) - * @vc: virtual console private data - * @cols: columns - * @lines: lines - * - * Resize a virtual console, clipping according to the actual constraints. - * If the caller passes a tty structure then update the termios winsize - * information and perform any necessary signal handling. - * - * Caller must hold the console semaphore. Takes the termios mutex and - * ctrl_lock of the tty IFF a tty is passed. - */ - -static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, - unsigned int cols, unsigned int lines) -{ - unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; - unsigned long end; - unsigned int old_cols, old_rows, old_row_size, old_screen_size; - unsigned int new_cols, new_rows, new_row_size, new_screen_size; - unsigned int user; - unsigned short *newscreen; - - WARN_CONSOLE_UNLOCKED(); - - if (!vc) - return -ENXIO; - - user = vc->vc_resize_user; - vc->vc_resize_user = 0; - - if (cols > VC_RESIZE_MAXCOL || lines > VC_RESIZE_MAXROW) - return -EINVAL; - - new_cols = (cols ? cols : vc->vc_cols); - new_rows = (lines ? lines : vc->vc_rows); - new_row_size = new_cols << 1; - new_screen_size = new_row_size * new_rows; - - if (new_cols == vc->vc_cols && new_rows == vc->vc_rows) - return 0; - - newscreen = kmalloc(new_screen_size, GFP_USER); - if (!newscreen) - return -ENOMEM; - - old_rows = vc->vc_rows; - old_cols = vc->vc_cols; - old_row_size = vc->vc_size_row; - old_screen_size = vc->vc_screenbuf_size; - - err = resize_screen(vc, new_cols, new_rows, user); - if (err) { - kfree(newscreen); - return err; - } - - vc->vc_rows = new_rows; - vc->vc_cols = new_cols; - vc->vc_size_row = new_row_size; - vc->vc_screenbuf_size = new_screen_size; - - rlth = min(old_row_size, new_row_size); - rrem = new_row_size - rlth; - old_origin = vc->vc_origin; - new_origin = (long) newscreen; - new_scr_end = new_origin + new_screen_size; - - if (vc->vc_y > new_rows) { - if (old_rows - vc->vc_y < new_rows) { - /* - * Cursor near the bottom, copy contents from the - * bottom of buffer - */ - old_origin += (old_rows - new_rows) * old_row_size; - } else { - /* - * Cursor is in no man's land, copy 1/2 screenful - * from the top and bottom of cursor position - */ - old_origin += (vc->vc_y - new_rows/2) * old_row_size; - } - } - - end = old_origin + old_row_size * min(old_rows, new_rows); - - update_attr(vc); - - while (old_origin < end) { - scr_memcpyw((unsigned short *) new_origin, - (unsigned short *) old_origin, rlth); - if (rrem) - scr_memsetw((void *)(new_origin + rlth), - vc->vc_video_erase_char, rrem); - old_origin += old_row_size; - new_origin += new_row_size; - } - if (new_scr_end > new_origin) - scr_memsetw((void *)new_origin, vc->vc_video_erase_char, - new_scr_end - new_origin); - kfree(vc->vc_screenbuf); - vc->vc_screenbuf = newscreen; - vc->vc_screenbuf_size = new_screen_size; - set_origin(vc); - - /* do part of a reset_terminal() */ - vc->vc_top = 0; - vc->vc_bottom = vc->vc_rows; - gotoxy(vc, vc->vc_x, vc->vc_y); - save_cur(vc); - - if (tty) { - /* Rewrite the requested winsize data with the actual - resulting sizes */ - struct winsize ws; - memset(&ws, 0, sizeof(ws)); - ws.ws_row = vc->vc_rows; - ws.ws_col = vc->vc_cols; - ws.ws_ypixel = vc->vc_scan_lines; - tty_do_resize(tty, &ws); - } - - if (CON_IS_VISIBLE(vc)) - update_screen(vc); - vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num); - return err; -} - -/** - * vc_resize - resize a VT - * @vc: virtual console - * @cols: columns - * @rows: rows - * - * Resize a virtual console as seen from the console end of things. We - * use the common vc_do_resize methods to update the structures. The - * caller must hold the console sem to protect console internals and - * vc->port.tty - */ - -int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) -{ - return vc_do_resize(vc->port.tty, vc, cols, rows); -} - -/** - * vt_resize - resize a VT - * @tty: tty to resize - * @ws: winsize attributes - * - * Resize a virtual terminal. This is called by the tty layer as we - * register our own handler for resizing. The mutual helper does all - * the actual work. - * - * Takes the console sem and the called methods then take the tty - * termios_mutex and the tty ctrl_lock in that order. - */ -static int vt_resize(struct tty_struct *tty, struct winsize *ws) -{ - struct vc_data *vc = tty->driver_data; - int ret; - - acquire_console_sem(); - ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row); - release_console_sem(); - return ret; -} - -void vc_deallocate(unsigned int currcons) -{ - WARN_CONSOLE_UNLOCKED(); - - if (vc_cons_allocated(currcons)) { - struct vc_data *vc = vc_cons[currcons].d; - struct vt_notifier_param param = { .vc = vc }; - - atomic_notifier_call_chain(&vt_notifier_list, VT_DEALLOCATE, ¶m); - vcs_remove_sysfs(currcons); - vc->vc_sw->con_deinit(vc); - put_pid(vc->vt_pid); - module_put(vc->vc_sw->owner); - kfree(vc->vc_screenbuf); - if (currcons >= MIN_NR_CONSOLES) - kfree(vc); - vc_cons[currcons].d = NULL; - } -} - -/* - * VT102 emulator - */ - -#define set_kbd(vc, x) set_vc_kbd_mode(kbd_table + (vc)->vc_num, (x)) -#define clr_kbd(vc, x) clr_vc_kbd_mode(kbd_table + (vc)->vc_num, (x)) -#define is_kbd(vc, x) vc_kbd_mode(kbd_table + (vc)->vc_num, (x)) - -#define decarm VC_REPEAT -#define decckm VC_CKMODE -#define kbdapplic VC_APPLIC -#define lnm VC_CRLF - -/* - * this is what the terminal answers to a ESC-Z or csi0c query. - */ -#define VT100ID "\033[?1;2c" -#define VT102ID "\033[?6c" - -unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, - 8,12,10,14, 9,13,11,15 }; - -/* the default colour table, for VGA+ colour systems */ -int default_red[] = {0x00,0xaa,0x00,0xaa,0x00,0xaa,0x00,0xaa, - 0x55,0xff,0x55,0xff,0x55,0xff,0x55,0xff}; -int default_grn[] = {0x00,0x00,0xaa,0x55,0x00,0x00,0xaa,0xaa, - 0x55,0x55,0xff,0xff,0x55,0x55,0xff,0xff}; -int default_blu[] = {0x00,0x00,0x00,0x00,0xaa,0xaa,0xaa,0xaa, - 0x55,0x55,0x55,0x55,0xff,0xff,0xff,0xff}; - -module_param_array(default_red, int, NULL, S_IRUGO | S_IWUSR); -module_param_array(default_grn, int, NULL, S_IRUGO | S_IWUSR); -module_param_array(default_blu, int, NULL, S_IRUGO | S_IWUSR); - -/* - * gotoxy() must verify all boundaries, because the arguments - * might also be negative. If the given position is out of - * bounds, the cursor is placed at the nearest margin. - */ -static void gotoxy(struct vc_data *vc, int new_x, int new_y) -{ - int min_y, max_y; - - if (new_x < 0) - vc->vc_x = 0; - else { - if (new_x >= vc->vc_cols) - vc->vc_x = vc->vc_cols - 1; - else - vc->vc_x = new_x; - } - - if (vc->vc_decom) { - min_y = vc->vc_top; - max_y = vc->vc_bottom; - } else { - min_y = 0; - max_y = vc->vc_rows; - } - if (new_y < min_y) - vc->vc_y = min_y; - else if (new_y >= max_y) - vc->vc_y = max_y - 1; - else - vc->vc_y = new_y; - vc->vc_pos = vc->vc_origin + vc->vc_y * vc->vc_size_row + (vc->vc_x<<1); - vc->vc_need_wrap = 0; -} - -/* for absolute user moves, when decom is set */ -static void gotoxay(struct vc_data *vc, int new_x, int new_y) -{ - gotoxy(vc, new_x, vc->vc_decom ? (vc->vc_top + new_y) : new_y); -} - -void scrollback(struct vc_data *vc, int lines) -{ - if (!lines) - lines = vc->vc_rows / 2; - scrolldelta(-lines); -} - -void scrollfront(struct vc_data *vc, int lines) -{ - if (!lines) - lines = vc->vc_rows / 2; - scrolldelta(lines); -} - -static void lf(struct vc_data *vc) -{ - /* don't scroll if above bottom of scrolling region, or - * if below scrolling region - */ - if (vc->vc_y + 1 == vc->vc_bottom) - scrup(vc, vc->vc_top, vc->vc_bottom, 1); - else if (vc->vc_y < vc->vc_rows - 1) { - vc->vc_y++; - vc->vc_pos += vc->vc_size_row; - } - vc->vc_need_wrap = 0; - notify_write(vc, '\n'); -} - -static void ri(struct vc_data *vc) -{ - /* don't scroll if below top of scrolling region, or - * if above scrolling region - */ - if (vc->vc_y == vc->vc_top) - scrdown(vc, vc->vc_top, vc->vc_bottom, 1); - else if (vc->vc_y > 0) { - vc->vc_y--; - vc->vc_pos -= vc->vc_size_row; - } - vc->vc_need_wrap = 0; -} - -static inline void cr(struct vc_data *vc) -{ - vc->vc_pos -= vc->vc_x << 1; - vc->vc_need_wrap = vc->vc_x = 0; - notify_write(vc, '\r'); -} - -static inline void bs(struct vc_data *vc) -{ - if (vc->vc_x) { - vc->vc_pos -= 2; - vc->vc_x--; - vc->vc_need_wrap = 0; - notify_write(vc, '\b'); - } -} - -static inline void del(struct vc_data *vc) -{ - /* ignored */ -} - -static void csi_J(struct vc_data *vc, int vpar) -{ - unsigned int count; - unsigned short * start; - - switch (vpar) { - case 0: /* erase from cursor to end of display */ - count = (vc->vc_scr_end - vc->vc_pos) >> 1; - start = (unsigned short *)vc->vc_pos; - if (DO_UPDATE(vc)) { - /* do in two stages */ - vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, - vc->vc_cols - vc->vc_x); - vc->vc_sw->con_clear(vc, vc->vc_y + 1, 0, - vc->vc_rows - vc->vc_y - 1, - vc->vc_cols); - } - break; - case 1: /* erase from start to cursor */ - count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1; - start = (unsigned short *)vc->vc_origin; - if (DO_UPDATE(vc)) { - /* do in two stages */ - vc->vc_sw->con_clear(vc, 0, 0, vc->vc_y, - vc->vc_cols); - vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1, - vc->vc_x + 1); - } - break; - case 2: /* erase whole display */ - count = vc->vc_cols * vc->vc_rows; - start = (unsigned short *)vc->vc_origin; - if (DO_UPDATE(vc)) - vc->vc_sw->con_clear(vc, 0, 0, - vc->vc_rows, - vc->vc_cols); - break; - default: - return; - } - scr_memsetw(start, vc->vc_video_erase_char, 2 * count); - vc->vc_need_wrap = 0; -} - -static void csi_K(struct vc_data *vc, int vpar) -{ - unsigned int count; - unsigned short * start; - - switch (vpar) { - case 0: /* erase from cursor to end of line */ - count = vc->vc_cols - vc->vc_x; - start = (unsigned short *)vc->vc_pos; - if (DO_UPDATE(vc)) - vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, - vc->vc_cols - vc->vc_x); - break; - case 1: /* erase from start of line to cursor */ - start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1)); - count = vc->vc_x + 1; - if (DO_UPDATE(vc)) - vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1, - vc->vc_x + 1); - break; - case 2: /* erase whole line */ - start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1)); - count = vc->vc_cols; - if (DO_UPDATE(vc)) - vc->vc_sw->con_clear(vc, vc->vc_y, 0, 1, - vc->vc_cols); - break; - default: - return; - } - scr_memsetw(start, vc->vc_video_erase_char, 2 * count); - vc->vc_need_wrap = 0; -} - -static void csi_X(struct vc_data *vc, int vpar) /* erase the following vpar positions */ -{ /* not vt100? */ - int count; - - if (!vpar) - vpar++; - count = (vpar > vc->vc_cols - vc->vc_x) ? (vc->vc_cols - vc->vc_x) : vpar; - - scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count); - if (DO_UPDATE(vc)) - vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, count); - vc->vc_need_wrap = 0; -} - -static void default_attr(struct vc_data *vc) -{ - vc->vc_intensity = 1; - vc->vc_italic = 0; - vc->vc_underline = 0; - vc->vc_reverse = 0; - vc->vc_blink = 0; - vc->vc_color = vc->vc_def_color; -} - -/* console_sem is held */ -static void csi_m(struct vc_data *vc) -{ - int i; - - for (i = 0; i <= vc->vc_npar; i++) - switch (vc->vc_par[i]) { - case 0: /* all attributes off */ - default_attr(vc); - break; - case 1: - vc->vc_intensity = 2; - break; - case 2: - vc->vc_intensity = 0; - break; - case 3: - vc->vc_italic = 1; - break; - case 4: - vc->vc_underline = 1; - break; - case 5: - vc->vc_blink = 1; - break; - case 7: - vc->vc_reverse = 1; - break; - case 10: /* ANSI X3.64-1979 (SCO-ish?) - * Select primary font, don't display - * control chars if defined, don't set - * bit 8 on output. - */ - vc->vc_translate = set_translate(vc->vc_charset == 0 - ? vc->vc_G0_charset - : vc->vc_G1_charset, vc); - vc->vc_disp_ctrl = 0; - vc->vc_toggle_meta = 0; - break; - case 11: /* ANSI X3.64-1979 (SCO-ish?) - * Select first alternate font, lets - * chars < 32 be displayed as ROM chars. - */ - vc->vc_translate = set_translate(IBMPC_MAP, vc); - vc->vc_disp_ctrl = 1; - vc->vc_toggle_meta = 0; - break; - case 12: /* ANSI X3.64-1979 (SCO-ish?) - * Select second alternate font, toggle - * high bit before displaying as ROM char. - */ - vc->vc_translate = set_translate(IBMPC_MAP, vc); - vc->vc_disp_ctrl = 1; - vc->vc_toggle_meta = 1; - break; - case 21: - case 22: - vc->vc_intensity = 1; - break; - case 23: - vc->vc_italic = 0; - break; - case 24: - vc->vc_underline = 0; - break; - case 25: - vc->vc_blink = 0; - break; - case 27: - vc->vc_reverse = 0; - break; - case 38: /* ANSI X3.64-1979 (SCO-ish?) - * Enables underscore, white foreground - * with white underscore (Linux - use - * default foreground). - */ - vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0); - vc->vc_underline = 1; - break; - case 39: /* ANSI X3.64-1979 (SCO-ish?) - * Disable underline option. - * Reset colour to default? It did this - * before... - */ - vc->vc_color = (vc->vc_def_color & 0x0f) | (vc->vc_color & 0xf0); - vc->vc_underline = 0; - break; - case 49: - vc->vc_color = (vc->vc_def_color & 0xf0) | (vc->vc_color & 0x0f); - break; - default: - if (vc->vc_par[i] >= 30 && vc->vc_par[i] <= 37) - vc->vc_color = color_table[vc->vc_par[i] - 30] - | (vc->vc_color & 0xf0); - else if (vc->vc_par[i] >= 40 && vc->vc_par[i] <= 47) - vc->vc_color = (color_table[vc->vc_par[i] - 40] << 4) - | (vc->vc_color & 0x0f); - break; - } - update_attr(vc); -} - -static void respond_string(const char *p, struct tty_struct *tty) -{ - while (*p) { - tty_insert_flip_char(tty, *p, 0); - p++; - } - con_schedule_flip(tty); -} - -static void cursor_report(struct vc_data *vc, struct tty_struct *tty) -{ - char buf[40]; - - sprintf(buf, "\033[%d;%dR", vc->vc_y + (vc->vc_decom ? vc->vc_top + 1 : 1), vc->vc_x + 1); - respond_string(buf, tty); -} - -static inline void status_report(struct tty_struct *tty) -{ - respond_string("\033[0n", tty); /* Terminal ok */ -} - -static inline void respond_ID(struct tty_struct * tty) -{ - respond_string(VT102ID, tty); -} - -void mouse_report(struct tty_struct *tty, int butt, int mrx, int mry) -{ - char buf[8]; - - sprintf(buf, "\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx), - (char)('!' + mry)); - respond_string(buf, tty); -} - -/* invoked via ioctl(TIOCLINUX) and through set_selection */ -int mouse_reporting(void) -{ - return vc_cons[fg_console].d->vc_report_mouse; -} - -/* console_sem is held */ -static void set_mode(struct vc_data *vc, int on_off) -{ - int i; - - for (i = 0; i <= vc->vc_npar; i++) - if (vc->vc_ques) { - switch(vc->vc_par[i]) { /* DEC private modes set/reset */ - case 1: /* Cursor keys send ^[Ox/^[[x */ - if (on_off) - set_kbd(vc, decckm); - else - clr_kbd(vc, decckm); - break; - case 3: /* 80/132 mode switch unimplemented */ - vc->vc_deccolm = on_off; -#if 0 - vc_resize(deccolm ? 132 : 80, vc->vc_rows); - /* this alone does not suffice; some user mode - utility has to change the hardware regs */ -#endif - break; - case 5: /* Inverted screen on/off */ - if (vc->vc_decscnm != on_off) { - vc->vc_decscnm = on_off; - invert_screen(vc, 0, vc->vc_screenbuf_size, 0); - update_attr(vc); - } - break; - case 6: /* Origin relative/absolute */ - vc->vc_decom = on_off; - gotoxay(vc, 0, 0); - break; - case 7: /* Autowrap on/off */ - vc->vc_decawm = on_off; - break; - case 8: /* Autorepeat on/off */ - if (on_off) - set_kbd(vc, decarm); - else - clr_kbd(vc, decarm); - break; - case 9: - vc->vc_report_mouse = on_off ? 1 : 0; - break; - case 25: /* Cursor on/off */ - vc->vc_deccm = on_off; - break; - case 1000: - vc->vc_report_mouse = on_off ? 2 : 0; - break; - } - } else { - switch(vc->vc_par[i]) { /* ANSI modes set/reset */ - case 3: /* Monitor (display ctrls) */ - vc->vc_disp_ctrl = on_off; - break; - case 4: /* Insert Mode on/off */ - vc->vc_decim = on_off; - break; - case 20: /* Lf, Enter == CrLf/Lf */ - if (on_off) - set_kbd(vc, lnm); - else - clr_kbd(vc, lnm); - break; - } - } -} - -/* console_sem is held */ -static void setterm_command(struct vc_data *vc) -{ - switch(vc->vc_par[0]) { - case 1: /* set color for underline mode */ - if (vc->vc_can_do_color && - vc->vc_par[1] < 16) { - vc->vc_ulcolor = color_table[vc->vc_par[1]]; - if (vc->vc_underline) - update_attr(vc); - } - break; - case 2: /* set color for half intensity mode */ - if (vc->vc_can_do_color && - vc->vc_par[1] < 16) { - vc->vc_halfcolor = color_table[vc->vc_par[1]]; - if (vc->vc_intensity == 0) - update_attr(vc); - } - break; - case 8: /* store colors as defaults */ - vc->vc_def_color = vc->vc_attr; - if (vc->vc_hi_font_mask == 0x100) - vc->vc_def_color >>= 1; - default_attr(vc); - update_attr(vc); - break; - case 9: /* set blanking interval */ - blankinterval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60; - poke_blanked_console(); - break; - case 10: /* set bell frequency in Hz */ - if (vc->vc_npar >= 1) - vc->vc_bell_pitch = vc->vc_par[1]; - else - vc->vc_bell_pitch = DEFAULT_BELL_PITCH; - break; - case 11: /* set bell duration in msec */ - if (vc->vc_npar >= 1) - vc->vc_bell_duration = (vc->vc_par[1] < 2000) ? - vc->vc_par[1] * HZ / 1000 : 0; - else - vc->vc_bell_duration = DEFAULT_BELL_DURATION; - break; - case 12: /* bring specified console to the front */ - if (vc->vc_par[1] >= 1 && vc_cons_allocated(vc->vc_par[1] - 1)) - set_console(vc->vc_par[1] - 1); - break; - case 13: /* unblank the screen */ - poke_blanked_console(); - break; - case 14: /* set vesa powerdown interval */ - vesa_off_interval = ((vc->vc_par[1] < 60) ? vc->vc_par[1] : 60) * 60 * HZ; - break; - case 15: /* activate the previous console */ - set_console(last_console); - break; - } -} - -/* console_sem is held */ -static void csi_at(struct vc_data *vc, unsigned int nr) -{ - if (nr > vc->vc_cols - vc->vc_x) - nr = vc->vc_cols - vc->vc_x; - else if (!nr) - nr = 1; - insert_char(vc, nr); -} - -/* console_sem is held */ -static void csi_L(struct vc_data *vc, unsigned int nr) -{ - if (nr > vc->vc_rows - vc->vc_y) - nr = vc->vc_rows - vc->vc_y; - else if (!nr) - nr = 1; - scrdown(vc, vc->vc_y, vc->vc_bottom, nr); - vc->vc_need_wrap = 0; -} - -/* console_sem is held */ -static void csi_P(struct vc_data *vc, unsigned int nr) -{ - if (nr > vc->vc_cols - vc->vc_x) - nr = vc->vc_cols - vc->vc_x; - else if (!nr) - nr = 1; - delete_char(vc, nr); -} - -/* console_sem is held */ -static void csi_M(struct vc_data *vc, unsigned int nr) -{ - if (nr > vc->vc_rows - vc->vc_y) - nr = vc->vc_rows - vc->vc_y; - else if (!nr) - nr=1; - scrup(vc, vc->vc_y, vc->vc_bottom, nr); - vc->vc_need_wrap = 0; -} - -/* console_sem is held (except via vc_init->reset_terminal */ -static void save_cur(struct vc_data *vc) -{ - vc->vc_saved_x = vc->vc_x; - vc->vc_saved_y = vc->vc_y; - vc->vc_s_intensity = vc->vc_intensity; - vc->vc_s_italic = vc->vc_italic; - vc->vc_s_underline = vc->vc_underline; - vc->vc_s_blink = vc->vc_blink; - vc->vc_s_reverse = vc->vc_reverse; - vc->vc_s_charset = vc->vc_charset; - vc->vc_s_color = vc->vc_color; - vc->vc_saved_G0 = vc->vc_G0_charset; - vc->vc_saved_G1 = vc->vc_G1_charset; -} - -/* console_sem is held */ -static void restore_cur(struct vc_data *vc) -{ - gotoxy(vc, vc->vc_saved_x, vc->vc_saved_y); - vc->vc_intensity = vc->vc_s_intensity; - vc->vc_italic = vc->vc_s_italic; - vc->vc_underline = vc->vc_s_underline; - vc->vc_blink = vc->vc_s_blink; - vc->vc_reverse = vc->vc_s_reverse; - vc->vc_charset = vc->vc_s_charset; - vc->vc_color = vc->vc_s_color; - vc->vc_G0_charset = vc->vc_saved_G0; - vc->vc_G1_charset = vc->vc_saved_G1; - vc->vc_translate = set_translate(vc->vc_charset ? vc->vc_G1_charset : vc->vc_G0_charset, vc); - update_attr(vc); - vc->vc_need_wrap = 0; -} - -enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey, - EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd, - ESpalette }; - -/* console_sem is held (except via vc_init()) */ -static void reset_terminal(struct vc_data *vc, int do_clear) -{ - vc->vc_top = 0; - vc->vc_bottom = vc->vc_rows; - vc->vc_state = ESnormal; - vc->vc_ques = 0; - vc->vc_translate = set_translate(LAT1_MAP, vc); - vc->vc_G0_charset = LAT1_MAP; - vc->vc_G1_charset = GRAF_MAP; - vc->vc_charset = 0; - vc->vc_need_wrap = 0; - vc->vc_report_mouse = 0; - vc->vc_utf = default_utf8; - vc->vc_utf_count = 0; - - vc->vc_disp_ctrl = 0; - vc->vc_toggle_meta = 0; - - vc->vc_decscnm = 0; - vc->vc_decom = 0; - vc->vc_decawm = 1; - vc->vc_deccm = global_cursor_default; - vc->vc_decim = 0; - - set_kbd(vc, decarm); - clr_kbd(vc, decckm); - clr_kbd(vc, kbdapplic); - clr_kbd(vc, lnm); - kbd_table[vc->vc_num].lockstate = 0; - kbd_table[vc->vc_num].slockstate = 0; - kbd_table[vc->vc_num].ledmode = LED_SHOW_FLAGS; - kbd_table[vc->vc_num].ledflagstate = kbd_table[vc->vc_num].default_ledflagstate; - /* do not do set_leds here because this causes an endless tasklet loop - when the keyboard hasn't been initialized yet */ - - vc->vc_cursor_type = cur_default; - vc->vc_complement_mask = vc->vc_s_complement_mask; - - default_attr(vc); - update_attr(vc); - - vc->vc_tab_stop[0] = 0x01010100; - vc->vc_tab_stop[1] = - vc->vc_tab_stop[2] = - vc->vc_tab_stop[3] = - vc->vc_tab_stop[4] = - vc->vc_tab_stop[5] = - vc->vc_tab_stop[6] = - vc->vc_tab_stop[7] = 0x01010101; - - vc->vc_bell_pitch = DEFAULT_BELL_PITCH; - vc->vc_bell_duration = DEFAULT_BELL_DURATION; - - gotoxy(vc, 0, 0); - save_cur(vc); - if (do_clear) - csi_J(vc, 2); -} - -/* console_sem is held */ -static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) -{ - /* - * Control characters can be used in the _middle_ - * of an escape sequence. - */ - switch (c) { - case 0: - return; - case 7: - if (vc->vc_bell_duration) - kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration); - return; - case 8: - bs(vc); - return; - case 9: - vc->vc_pos -= (vc->vc_x << 1); - while (vc->vc_x < vc->vc_cols - 1) { - vc->vc_x++; - if (vc->vc_tab_stop[vc->vc_x >> 5] & (1 << (vc->vc_x & 31))) - break; - } - vc->vc_pos += (vc->vc_x << 1); - notify_write(vc, '\t'); - return; - case 10: case 11: case 12: - lf(vc); - if (!is_kbd(vc, lnm)) - return; - case 13: - cr(vc); - return; - case 14: - vc->vc_charset = 1; - vc->vc_translate = set_translate(vc->vc_G1_charset, vc); - vc->vc_disp_ctrl = 1; - return; - case 15: - vc->vc_charset = 0; - vc->vc_translate = set_translate(vc->vc_G0_charset, vc); - vc->vc_disp_ctrl = 0; - return; - case 24: case 26: - vc->vc_state = ESnormal; - return; - case 27: - vc->vc_state = ESesc; - return; - case 127: - del(vc); - return; - case 128+27: - vc->vc_state = ESsquare; - return; - } - switch(vc->vc_state) { - case ESesc: - vc->vc_state = ESnormal; - switch (c) { - case '[': - vc->vc_state = ESsquare; - return; - case ']': - vc->vc_state = ESnonstd; - return; - case '%': - vc->vc_state = ESpercent; - return; - case 'E': - cr(vc); - lf(vc); - return; - case 'M': - ri(vc); - return; - case 'D': - lf(vc); - return; - case 'H': - vc->vc_tab_stop[vc->vc_x >> 5] |= (1 << (vc->vc_x & 31)); - return; - case 'Z': - respond_ID(tty); - return; - case '7': - save_cur(vc); - return; - case '8': - restore_cur(vc); - return; - case '(': - vc->vc_state = ESsetG0; - return; - case ')': - vc->vc_state = ESsetG1; - return; - case '#': - vc->vc_state = EShash; - return; - case 'c': - reset_terminal(vc, 1); - return; - case '>': /* Numeric keypad */ - clr_kbd(vc, kbdapplic); - return; - case '=': /* Appl. keypad */ - set_kbd(vc, kbdapplic); - return; - } - return; - case ESnonstd: - if (c=='P') { /* palette escape sequence */ - for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++) - vc->vc_par[vc->vc_npar] = 0; - vc->vc_npar = 0; - vc->vc_state = ESpalette; - return; - } else if (c=='R') { /* reset palette */ - reset_palette(vc); - vc->vc_state = ESnormal; - } else - vc->vc_state = ESnormal; - return; - case ESpalette: - if (isxdigit(c)) { - vc->vc_par[vc->vc_npar++] = hex_to_bin(c); - if (vc->vc_npar == 7) { - int i = vc->vc_par[0] * 3, j = 1; - vc->vc_palette[i] = 16 * vc->vc_par[j++]; - vc->vc_palette[i++] += vc->vc_par[j++]; - vc->vc_palette[i] = 16 * vc->vc_par[j++]; - vc->vc_palette[i++] += vc->vc_par[j++]; - vc->vc_palette[i] = 16 * vc->vc_par[j++]; - vc->vc_palette[i] += vc->vc_par[j]; - set_palette(vc); - vc->vc_state = ESnormal; - } - } else - vc->vc_state = ESnormal; - return; - case ESsquare: - for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++) - vc->vc_par[vc->vc_npar] = 0; - vc->vc_npar = 0; - vc->vc_state = ESgetpars; - if (c == '[') { /* Function key */ - vc->vc_state=ESfunckey; - return; - } - vc->vc_ques = (c == '?'); - if (vc->vc_ques) - return; - case ESgetpars: - if (c == ';' && vc->vc_npar < NPAR - 1) { - vc->vc_npar++; - return; - } else if (c>='0' && c<='9') { - vc->vc_par[vc->vc_npar] *= 10; - vc->vc_par[vc->vc_npar] += c - '0'; - return; - } else - vc->vc_state = ESgotpars; - case ESgotpars: - vc->vc_state = ESnormal; - switch(c) { - case 'h': - set_mode(vc, 1); - return; - case 'l': - set_mode(vc, 0); - return; - case 'c': - if (vc->vc_ques) { - if (vc->vc_par[0]) - vc->vc_cursor_type = vc->vc_par[0] | (vc->vc_par[1] << 8) | (vc->vc_par[2] << 16); - else - vc->vc_cursor_type = cur_default; - return; - } - break; - case 'm': - if (vc->vc_ques) { - clear_selection(); - if (vc->vc_par[0]) - vc->vc_complement_mask = vc->vc_par[0] << 8 | vc->vc_par[1]; - else - vc->vc_complement_mask = vc->vc_s_complement_mask; - return; - } - break; - case 'n': - if (!vc->vc_ques) { - if (vc->vc_par[0] == 5) - status_report(tty); - else if (vc->vc_par[0] == 6) - cursor_report(vc, tty); - } - return; - } - if (vc->vc_ques) { - vc->vc_ques = 0; - return; - } - switch(c) { - case 'G': case '`': - if (vc->vc_par[0]) - vc->vc_par[0]--; - gotoxy(vc, vc->vc_par[0], vc->vc_y); - return; - case 'A': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, vc->vc_x, vc->vc_y - vc->vc_par[0]); - return; - case 'B': case 'e': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, vc->vc_x, vc->vc_y + vc->vc_par[0]); - return; - case 'C': case 'a': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, vc->vc_x + vc->vc_par[0], vc->vc_y); - return; - case 'D': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, vc->vc_x - vc->vc_par[0], vc->vc_y); - return; - case 'E': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, 0, vc->vc_y + vc->vc_par[0]); - return; - case 'F': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, 0, vc->vc_y - vc->vc_par[0]); - return; - case 'd': - if (vc->vc_par[0]) - vc->vc_par[0]--; - gotoxay(vc, vc->vc_x ,vc->vc_par[0]); - return; - case 'H': case 'f': - if (vc->vc_par[0]) - vc->vc_par[0]--; - if (vc->vc_par[1]) - vc->vc_par[1]--; - gotoxay(vc, vc->vc_par[1], vc->vc_par[0]); - return; - case 'J': - csi_J(vc, vc->vc_par[0]); - return; - case 'K': - csi_K(vc, vc->vc_par[0]); - return; - case 'L': - csi_L(vc, vc->vc_par[0]); - return; - case 'M': - csi_M(vc, vc->vc_par[0]); - return; - case 'P': - csi_P(vc, vc->vc_par[0]); - return; - case 'c': - if (!vc->vc_par[0]) - respond_ID(tty); - return; - case 'g': - if (!vc->vc_par[0]) - vc->vc_tab_stop[vc->vc_x >> 5] &= ~(1 << (vc->vc_x & 31)); - else if (vc->vc_par[0] == 3) { - vc->vc_tab_stop[0] = - vc->vc_tab_stop[1] = - vc->vc_tab_stop[2] = - vc->vc_tab_stop[3] = - vc->vc_tab_stop[4] = - vc->vc_tab_stop[5] = - vc->vc_tab_stop[6] = - vc->vc_tab_stop[7] = 0; - } - return; - case 'm': - csi_m(vc); - return; - case 'q': /* DECLL - but only 3 leds */ - /* map 0,1,2,3 to 0,1,2,4 */ - if (vc->vc_par[0] < 4) - setledstate(kbd_table + vc->vc_num, - (vc->vc_par[0] < 3) ? vc->vc_par[0] : 4); - return; - case 'r': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - if (!vc->vc_par[1]) - vc->vc_par[1] = vc->vc_rows; - /* Minimum allowed region is 2 lines */ - if (vc->vc_par[0] < vc->vc_par[1] && - vc->vc_par[1] <= vc->vc_rows) { - vc->vc_top = vc->vc_par[0] - 1; - vc->vc_bottom = vc->vc_par[1]; - gotoxay(vc, 0, 0); - } - return; - case 's': - save_cur(vc); - return; - case 'u': - restore_cur(vc); - return; - case 'X': - csi_X(vc, vc->vc_par[0]); - return; - case '@': - csi_at(vc, vc->vc_par[0]); - return; - case ']': /* setterm functions */ - setterm_command(vc); - return; - } - return; - case ESpercent: - vc->vc_state = ESnormal; - switch (c) { - case '@': /* defined in ISO 2022 */ - vc->vc_utf = 0; - return; - case 'G': /* prelim official escape code */ - case '8': /* retained for compatibility */ - vc->vc_utf = 1; - return; - } - return; - case ESfunckey: - vc->vc_state = ESnormal; - return; - case EShash: - vc->vc_state = ESnormal; - if (c == '8') { - /* DEC screen alignment test. kludge :-) */ - vc->vc_video_erase_char = - (vc->vc_video_erase_char & 0xff00) | 'E'; - csi_J(vc, 2); - vc->vc_video_erase_char = - (vc->vc_video_erase_char & 0xff00) | ' '; - do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2); - } - return; - case ESsetG0: - if (c == '0') - vc->vc_G0_charset = GRAF_MAP; - else if (c == 'B') - vc->vc_G0_charset = LAT1_MAP; - else if (c == 'U') - vc->vc_G0_charset = IBMPC_MAP; - else if (c == 'K') - vc->vc_G0_charset = USER_MAP; - if (vc->vc_charset == 0) - vc->vc_translate = set_translate(vc->vc_G0_charset, vc); - vc->vc_state = ESnormal; - return; - case ESsetG1: - if (c == '0') - vc->vc_G1_charset = GRAF_MAP; - else if (c == 'B') - vc->vc_G1_charset = LAT1_MAP; - else if (c == 'U') - vc->vc_G1_charset = IBMPC_MAP; - else if (c == 'K') - vc->vc_G1_charset = USER_MAP; - if (vc->vc_charset == 1) - vc->vc_translate = set_translate(vc->vc_G1_charset, vc); - vc->vc_state = ESnormal; - return; - default: - vc->vc_state = ESnormal; - } -} - -/* This is a temporary buffer used to prepare a tty console write - * so that we can easily avoid touching user space while holding the - * console spinlock. It is allocated in con_init and is shared by - * this code and the vc_screen read/write tty calls. - * - * We have to allocate this statically in the kernel data section - * since console_init (and thus con_init) are called before any - * kernel memory allocation is available. - */ -char con_buf[CON_BUF_SIZE]; -DEFINE_MUTEX(con_buf_mtx); - -/* is_double_width() is based on the wcwidth() implementation by - * Markus Kuhn -- 2007-05-26 (Unicode 5.0) - * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c - */ -struct interval { - uint32_t first; - uint32_t last; -}; - -static int bisearch(uint32_t ucs, const struct interval *table, int max) -{ - int min = 0; - int mid; - - if (ucs < table[0].first || ucs > table[max].last) - return 0; - while (max >= min) { - mid = (min + max) / 2; - if (ucs > table[mid].last) - min = mid + 1; - else if (ucs < table[mid].first) - max = mid - 1; - else - return 1; - } - return 0; -} - -static int is_double_width(uint32_t ucs) -{ - static const struct interval double_width[] = { - { 0x1100, 0x115F }, { 0x2329, 0x232A }, { 0x2E80, 0x303E }, - { 0x3040, 0xA4CF }, { 0xAC00, 0xD7A3 }, { 0xF900, 0xFAFF }, - { 0xFE10, 0xFE19 }, { 0xFE30, 0xFE6F }, { 0xFF00, 0xFF60 }, - { 0xFFE0, 0xFFE6 }, { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD } - }; - return bisearch(ucs, double_width, ARRAY_SIZE(double_width) - 1); -} - -/* acquires console_sem */ -static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ -#ifdef VT_BUF_VRAM_ONLY -#define FLUSH do { } while(0); -#else -#define FLUSH if (draw_x >= 0) { \ - vc->vc_sw->con_putcs(vc, (u16 *)draw_from, (u16 *)draw_to - (u16 *)draw_from, vc->vc_y, draw_x); \ - draw_x = -1; \ - } -#endif - - int c, tc, ok, n = 0, draw_x = -1; - unsigned int currcons; - unsigned long draw_from = 0, draw_to = 0; - struct vc_data *vc; - unsigned char vc_attr; - struct vt_notifier_param param; - uint8_t rescan; - uint8_t inverse; - uint8_t width; - u16 himask, charmask; - - if (in_interrupt()) - return count; - - might_sleep(); - - acquire_console_sem(); - vc = tty->driver_data; - if (vc == NULL) { - printk(KERN_ERR "vt: argh, driver_data is NULL !\n"); - release_console_sem(); - return 0; - } - - currcons = vc->vc_num; - if (!vc_cons_allocated(currcons)) { - /* could this happen? */ - printk_once("con_write: tty %d not allocated\n", currcons+1); - release_console_sem(); - return 0; - } - - himask = vc->vc_hi_font_mask; - charmask = himask ? 0x1ff : 0xff; - - /* undraw cursor first */ - if (IS_FG(vc)) - hide_cursor(vc); - - param.vc = vc; - - while (!tty->stopped && count) { - int orig = *buf; - c = orig; - buf++; - n++; - count--; - rescan = 0; - inverse = 0; - width = 1; - - /* Do no translation at all in control states */ - if (vc->vc_state != ESnormal) { - tc = c; - } else if (vc->vc_utf && !vc->vc_disp_ctrl) { - /* Combine UTF-8 into Unicode in vc_utf_char. - * vc_utf_count is the number of continuation bytes still - * expected to arrive. - * vc_npar is the number of continuation bytes arrived so - * far - */ -rescan_last_byte: - if ((c & 0xc0) == 0x80) { - /* Continuation byte received */ - static const uint32_t utf8_length_changes[] = { 0x0000007f, 0x000007ff, 0x0000ffff, 0x001fffff, 0x03ffffff, 0x7fffffff }; - if (vc->vc_utf_count) { - vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f); - vc->vc_npar++; - if (--vc->vc_utf_count) { - /* Still need some bytes */ - continue; - } - /* Got a whole character */ - c = vc->vc_utf_char; - /* Reject overlong sequences */ - if (c <= utf8_length_changes[vc->vc_npar - 1] || - c > utf8_length_changes[vc->vc_npar]) - c = 0xfffd; - } else { - /* Unexpected continuation byte */ - vc->vc_utf_count = 0; - c = 0xfffd; - } - } else { - /* Single ASCII byte or first byte of a sequence received */ - if (vc->vc_utf_count) { - /* Continuation byte expected */ - rescan = 1; - vc->vc_utf_count = 0; - c = 0xfffd; - } else if (c > 0x7f) { - /* First byte of a multibyte sequence received */ - vc->vc_npar = 0; - if ((c & 0xe0) == 0xc0) { - vc->vc_utf_count = 1; - vc->vc_utf_char = (c & 0x1f); - } else if ((c & 0xf0) == 0xe0) { - vc->vc_utf_count = 2; - vc->vc_utf_char = (c & 0x0f); - } else if ((c & 0xf8) == 0xf0) { - vc->vc_utf_count = 3; - vc->vc_utf_char = (c & 0x07); - } else if ((c & 0xfc) == 0xf8) { - vc->vc_utf_count = 4; - vc->vc_utf_char = (c & 0x03); - } else if ((c & 0xfe) == 0xfc) { - vc->vc_utf_count = 5; - vc->vc_utf_char = (c & 0x01); - } else { - /* 254 and 255 are invalid */ - c = 0xfffd; - } - if (vc->vc_utf_count) { - /* Still need some bytes */ - continue; - } - } - /* Nothing to do if an ASCII byte was received */ - } - /* End of UTF-8 decoding. */ - /* c is the received character, or U+FFFD for invalid sequences. */ - /* Replace invalid Unicode code points with U+FFFD too */ - if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) - c = 0xfffd; - tc = c; - } else { /* no utf or alternate charset mode */ - tc = vc_translate(vc, c); - } - - param.c = tc; - if (atomic_notifier_call_chain(&vt_notifier_list, VT_PREWRITE, - ¶m) == NOTIFY_STOP) - continue; - - /* If the original code was a control character we - * only allow a glyph to be displayed if the code is - * not normally used (such as for cursor movement) or - * if the disp_ctrl mode has been explicitly enabled. - * Certain characters (as given by the CTRL_ALWAYS - * bitmap) are always displayed as control characters, - * as the console would be pretty useless without - * them; to display an arbitrary font position use the - * direct-to-font zone in UTF-8 mode. - */ - ok = tc && (c >= 32 || - !(vc->vc_disp_ctrl ? (CTRL_ALWAYS >> c) & 1 : - vc->vc_utf || ((CTRL_ACTION >> c) & 1))) - && (c != 127 || vc->vc_disp_ctrl) - && (c != 128+27); - - if (vc->vc_state == ESnormal && ok) { - if (vc->vc_utf && !vc->vc_disp_ctrl) { - if (is_double_width(c)) - width = 2; - } - /* Now try to find out how to display it */ - tc = conv_uni_to_pc(vc, tc); - if (tc & ~charmask) { - if (tc == -1 || tc == -2) { - continue; /* nothing to display */ - } - /* Glyph not found */ - if ((!(vc->vc_utf && !vc->vc_disp_ctrl) || c < 128) && !(c & ~charmask)) { - /* In legacy mode use the glyph we get by a 1:1 mapping. - This would make absolutely no sense with Unicode in mind, - but do this for ASCII characters since a font may lack - Unicode mapping info and we don't want to end up with - having question marks only. */ - tc = c; - } else { - /* Display U+FFFD. If it's not found, display an inverse question mark. */ - tc = conv_uni_to_pc(vc, 0xfffd); - if (tc < 0) { - inverse = 1; - tc = conv_uni_to_pc(vc, '?'); - if (tc < 0) tc = '?'; - } - } - } - - if (!inverse) { - vc_attr = vc->vc_attr; - } else { - /* invert vc_attr */ - if (!vc->vc_can_do_color) { - vc_attr = (vc->vc_attr) ^ 0x08; - } else if (vc->vc_hi_font_mask == 0x100) { - vc_attr = ((vc->vc_attr) & 0x11) | (((vc->vc_attr) & 0xe0) >> 4) | (((vc->vc_attr) & 0x0e) << 4); - } else { - vc_attr = ((vc->vc_attr) & 0x88) | (((vc->vc_attr) & 0x70) >> 4) | (((vc->vc_attr) & 0x07) << 4); - } - FLUSH - } - - while (1) { - if (vc->vc_need_wrap || vc->vc_decim) - FLUSH - if (vc->vc_need_wrap) { - cr(vc); - lf(vc); - } - if (vc->vc_decim) - insert_char(vc, 1); - scr_writew(himask ? - ((vc_attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) : - (vc_attr << 8) + tc, - (u16 *) vc->vc_pos); - if (DO_UPDATE(vc) && draw_x < 0) { - draw_x = vc->vc_x; - draw_from = vc->vc_pos; - } - if (vc->vc_x == vc->vc_cols - 1) { - vc->vc_need_wrap = vc->vc_decawm; - draw_to = vc->vc_pos + 2; - } else { - vc->vc_x++; - draw_to = (vc->vc_pos += 2); - } - - if (!--width) break; - - tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */ - if (tc < 0) tc = ' '; - } - notify_write(vc, c); - - if (inverse) { - FLUSH - } - - if (rescan) { - rescan = 0; - inverse = 0; - width = 1; - c = orig; - goto rescan_last_byte; - } - continue; - } - FLUSH - do_con_trol(tty, vc, orig); - } - FLUSH - console_conditional_schedule(); - release_console_sem(); - notify_update(vc); - return n; -#undef FLUSH -} - -/* - * This is the console switching callback. - * - * Doing console switching in a process context allows - * us to do the switches asynchronously (needed when we want - * to switch due to a keyboard interrupt). Synchronization - * with other console code and prevention of re-entrancy is - * ensured with console_sem. - */ -static void console_callback(struct work_struct *ignored) -{ - acquire_console_sem(); - - if (want_console >= 0) { - if (want_console != fg_console && - vc_cons_allocated(want_console)) { - hide_cursor(vc_cons[fg_console].d); - change_console(vc_cons[want_console].d); - /* we only changed when the console had already - been allocated - a new console is not created - in an interrupt routine */ - } - want_console = -1; - } - if (do_poke_blanked_console) { /* do not unblank for a LED change */ - do_poke_blanked_console = 0; - poke_blanked_console(); - } - if (scrollback_delta) { - struct vc_data *vc = vc_cons[fg_console].d; - clear_selection(); - if (vc->vc_mode == KD_TEXT) - vc->vc_sw->con_scrolldelta(vc, scrollback_delta); - scrollback_delta = 0; - } - if (blank_timer_expired) { - do_blank_screen(0); - blank_timer_expired = 0; - } - notify_update(vc_cons[fg_console].d); - - release_console_sem(); -} - -int set_console(int nr) -{ - struct vc_data *vc = vc_cons[fg_console].d; - - if (!vc_cons_allocated(nr) || vt_dont_switch || - (vc->vt_mode.mode == VT_AUTO && vc->vc_mode == KD_GRAPHICS)) { - - /* - * Console switch will fail in console_callback() or - * change_console() so there is no point scheduling - * the callback - * - * Existing set_console() users don't check the return - * value so this shouldn't break anything - */ - return -EINVAL; - } - - want_console = nr; - schedule_console_callback(); - - return 0; -} - -struct tty_driver *console_driver; - -#ifdef CONFIG_VT_CONSOLE - -/** - * vt_kmsg_redirect() - Sets/gets the kernel message console - * @new: The new virtual terminal number or -1 if the console should stay - * unchanged - * - * By default, the kernel messages are always printed on the current virtual - * console. However, the user may modify that default with the - * TIOCL_SETKMSGREDIRECT ioctl call. - * - * This function sets the kernel message console to be @new. It returns the old - * virtual console number. The virtual terminal number 0 (both as parameter and - * return value) means no redirection (i.e. always printed on the currently - * active console). - * - * The parameter -1 means that only the current console is returned, but the - * value is not modified. You may use the macro vt_get_kmsg_redirect() in that - * case to make the code more understandable. - * - * When the kernel is compiled without CONFIG_VT_CONSOLE, this function ignores - * the parameter and always returns 0. - */ -int vt_kmsg_redirect(int new) -{ - static int kmsg_con; - - if (new != -1) - return xchg(&kmsg_con, new); - else - return kmsg_con; -} - -/* - * Console on virtual terminal - * - * The console must be locked when we get here. - */ - -static void vt_console_print(struct console *co, const char *b, unsigned count) -{ - struct vc_data *vc = vc_cons[fg_console].d; - unsigned char c; - static DEFINE_SPINLOCK(printing_lock); - const ushort *start; - ushort cnt = 0; - ushort myx; - int kmsg_console; - - /* console busy or not yet initialized */ - if (!printable) - return; - if (!spin_trylock(&printing_lock)) - return; - - kmsg_console = vt_get_kmsg_redirect(); - if (kmsg_console && vc_cons_allocated(kmsg_console - 1)) - vc = vc_cons[kmsg_console - 1].d; - - /* read `x' only after setting currcons properly (otherwise - the `x' macro will read the x of the foreground console). */ - myx = vc->vc_x; - - if (!vc_cons_allocated(fg_console)) { - /* impossible */ - /* printk("vt_console_print: tty %d not allocated ??\n", currcons+1); */ - goto quit; - } - - if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc)) - goto quit; - - /* undraw cursor first */ - if (IS_FG(vc)) - hide_cursor(vc); - - start = (ushort *)vc->vc_pos; - - /* Contrived structure to try to emulate original need_wrap behaviour - * Problems caused when we have need_wrap set on '\n' character */ - while (count--) { - c = *b++; - if (c == 10 || c == 13 || c == 8 || vc->vc_need_wrap) { - if (cnt > 0) { - if (CON_IS_VISIBLE(vc)) - vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x); - vc->vc_x += cnt; - if (vc->vc_need_wrap) - vc->vc_x--; - cnt = 0; - } - if (c == 8) { /* backspace */ - bs(vc); - start = (ushort *)vc->vc_pos; - myx = vc->vc_x; - continue; - } - if (c != 13) - lf(vc); - cr(vc); - start = (ushort *)vc->vc_pos; - myx = vc->vc_x; - if (c == 10 || c == 13) - continue; - } - scr_writew((vc->vc_attr << 8) + c, (unsigned short *)vc->vc_pos); - notify_write(vc, c); - cnt++; - if (myx == vc->vc_cols - 1) { - vc->vc_need_wrap = 1; - continue; - } - vc->vc_pos += 2; - myx++; - } - if (cnt > 0) { - if (CON_IS_VISIBLE(vc)) - vc->vc_sw->con_putcs(vc, start, cnt, vc->vc_y, vc->vc_x); - vc->vc_x += cnt; - if (vc->vc_x == vc->vc_cols) { - vc->vc_x--; - vc->vc_need_wrap = 1; - } - } - set_cursor(vc); - notify_update(vc); - -quit: - spin_unlock(&printing_lock); -} - -static struct tty_driver *vt_console_device(struct console *c, int *index) -{ - *index = c->index ? c->index-1 : fg_console; - return console_driver; -} - -static struct console vt_console_driver = { - .name = "tty", - .write = vt_console_print, - .device = vt_console_device, - .unblank = unblank_screen, - .flags = CON_PRINTBUFFER, - .index = -1, -}; -#endif - -/* - * Handling of Linux-specific VC ioctls - */ - -/* - * Generally a bit racy with respect to console_sem(). - * - * There are some functions which don't need it. - * - * There are some functions which can sleep for arbitrary periods - * (paste_selection) but we don't need the lock there anyway. - * - * set_selection has locking, and definitely needs it - */ - -int tioclinux(struct tty_struct *tty, unsigned long arg) -{ - char type, data; - char __user *p = (char __user *)arg; - int lines; - int ret; - - if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN)) - return -EPERM; - if (get_user(type, p)) - return -EFAULT; - ret = 0; - - switch (type) - { - case TIOCL_SETSEL: - acquire_console_sem(); - ret = set_selection((struct tiocl_selection __user *)(p+1), tty); - release_console_sem(); - break; - case TIOCL_PASTESEL: - ret = paste_selection(tty); - break; - case TIOCL_UNBLANKSCREEN: - acquire_console_sem(); - unblank_screen(); - release_console_sem(); - break; - case TIOCL_SELLOADLUT: - ret = sel_loadlut(p); - break; - case TIOCL_GETSHIFTSTATE: - - /* - * Make it possible to react to Shift+Mousebutton. - * Note that 'shift_state' is an undocumented - * kernel-internal variable; programs not closely - * related to the kernel should not use this. - */ - data = shift_state; - ret = __put_user(data, p); - break; - case TIOCL_GETMOUSEREPORTING: - data = mouse_reporting(); - ret = __put_user(data, p); - break; - case TIOCL_SETVESABLANK: - ret = set_vesa_blanking(p); - break; - case TIOCL_GETKMSGREDIRECT: - data = vt_get_kmsg_redirect(); - ret = __put_user(data, p); - break; - case TIOCL_SETKMSGREDIRECT: - if (!capable(CAP_SYS_ADMIN)) { - ret = -EPERM; - } else { - if (get_user(data, p+1)) - ret = -EFAULT; - else - vt_kmsg_redirect(data); - } - break; - case TIOCL_GETFGCONSOLE: - ret = fg_console; - break; - case TIOCL_SCROLLCONSOLE: - if (get_user(lines, (s32 __user *)(p+4))) { - ret = -EFAULT; - } else { - scrollfront(vc_cons[fg_console].d, lines); - ret = 0; - } - break; - case TIOCL_BLANKSCREEN: /* until explicitly unblanked, not only poked */ - acquire_console_sem(); - ignore_poke = 1; - do_blank_screen(0); - release_console_sem(); - break; - case TIOCL_BLANKEDSCREEN: - ret = console_blanked; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -/* - * /dev/ttyN handling - */ - -static int con_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - int retval; - - retval = do_con_write(tty, buf, count); - con_flush_chars(tty); - - return retval; -} - -static int con_put_char(struct tty_struct *tty, unsigned char ch) -{ - if (in_interrupt()) - return 0; /* n_r3964 calls put_char() from interrupt context */ - return do_con_write(tty, &ch, 1); -} - -static int con_write_room(struct tty_struct *tty) -{ - if (tty->stopped) - return 0; - return 32768; /* No limit, really; we're not buffering */ -} - -static int con_chars_in_buffer(struct tty_struct *tty) -{ - return 0; /* we're not buffering */ -} - -/* - * con_throttle and con_unthrottle are only used for - * paste_selection(), which has to stuff in a large number of - * characters... - */ -static void con_throttle(struct tty_struct *tty) -{ -} - -static void con_unthrottle(struct tty_struct *tty) -{ - struct vc_data *vc = tty->driver_data; - - wake_up_interruptible(&vc->paste_wait); -} - -/* - * Turn the Scroll-Lock LED on when the tty is stopped - */ -static void con_stop(struct tty_struct *tty) -{ - int console_num; - if (!tty) - return; - console_num = tty->index; - if (!vc_cons_allocated(console_num)) - return; - set_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); - set_leds(); -} - -/* - * Turn the Scroll-Lock LED off when the console is started - */ -static void con_start(struct tty_struct *tty) -{ - int console_num; - if (!tty) - return; - console_num = tty->index; - if (!vc_cons_allocated(console_num)) - return; - clr_vc_kbd_led(kbd_table + console_num, VC_SCROLLOCK); - set_leds(); -} - -static void con_flush_chars(struct tty_struct *tty) -{ - struct vc_data *vc; - - if (in_interrupt()) /* from flush_to_ldisc */ - return; - - /* if we race with con_close(), vt may be null */ - acquire_console_sem(); - vc = tty->driver_data; - if (vc) - set_cursor(vc); - release_console_sem(); -} - -/* - * Allocate the console screen memory. - */ -static int con_open(struct tty_struct *tty, struct file *filp) -{ - unsigned int currcons = tty->index; - int ret = 0; - - acquire_console_sem(); - if (tty->driver_data == NULL) { - ret = vc_allocate(currcons); - if (ret == 0) { - struct vc_data *vc = vc_cons[currcons].d; - - /* Still being freed */ - if (vc->port.tty) { - release_console_sem(); - return -ERESTARTSYS; - } - tty->driver_data = vc; - vc->port.tty = tty; - - if (!tty->winsize.ws_row && !tty->winsize.ws_col) { - tty->winsize.ws_row = vc_cons[currcons].d->vc_rows; - tty->winsize.ws_col = vc_cons[currcons].d->vc_cols; - } - if (vc->vc_utf) - tty->termios->c_iflag |= IUTF8; - else - tty->termios->c_iflag &= ~IUTF8; - release_console_sem(); - return ret; - } - } - release_console_sem(); - return ret; -} - -static void con_close(struct tty_struct *tty, struct file *filp) -{ - /* Nothing to do - we defer to shutdown */ -} - -static void con_shutdown(struct tty_struct *tty) -{ - struct vc_data *vc = tty->driver_data; - BUG_ON(vc == NULL); - acquire_console_sem(); - vc->port.tty = NULL; - release_console_sem(); - tty_shutdown(tty); -} - -static int default_italic_color = 2; // green (ASCII) -static int default_underline_color = 3; // cyan (ASCII) -module_param_named(italic, default_italic_color, int, S_IRUGO | S_IWUSR); -module_param_named(underline, default_underline_color, int, S_IRUGO | S_IWUSR); - -static void vc_init(struct vc_data *vc, unsigned int rows, - unsigned int cols, int do_clear) -{ - int j, k ; - - vc->vc_cols = cols; - vc->vc_rows = rows; - vc->vc_size_row = cols << 1; - vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row; - - set_origin(vc); - vc->vc_pos = vc->vc_origin; - reset_vc(vc); - for (j=k=0; j<16; j++) { - vc->vc_palette[k++] = default_red[j] ; - vc->vc_palette[k++] = default_grn[j] ; - vc->vc_palette[k++] = default_blu[j] ; - } - vc->vc_def_color = 0x07; /* white */ - vc->vc_ulcolor = default_underline_color; - vc->vc_itcolor = default_italic_color; - vc->vc_halfcolor = 0x08; /* grey */ - init_waitqueue_head(&vc->paste_wait); - reset_terminal(vc, do_clear); -} - -/* - * This routine initializes console interrupts, and does nothing - * else. If you want the screen to clear, call tty_write with - * the appropriate escape-sequence. - */ - -static int __init con_init(void) -{ - const char *display_desc = NULL; - struct vc_data *vc; - unsigned int currcons = 0, i; - - acquire_console_sem(); - - if (conswitchp) - display_desc = conswitchp->con_startup(); - if (!display_desc) { - fg_console = 0; - release_console_sem(); - return 0; - } - - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { - struct con_driver *con_driver = ®istered_con_driver[i]; - - if (con_driver->con == NULL) { - con_driver->con = conswitchp; - con_driver->desc = display_desc; - con_driver->flag = CON_DRIVER_FLAG_INIT; - con_driver->first = 0; - con_driver->last = MAX_NR_CONSOLES - 1; - break; - } - } - - for (i = 0; i < MAX_NR_CONSOLES; i++) - con_driver_map[i] = conswitchp; - - if (blankinterval) { - blank_state = blank_normal_wait; - mod_timer(&console_timer, jiffies + (blankinterval * HZ)); - } - - for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) { - vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT); - INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); - tty_port_init(&vc->port); - visual_init(vc, currcons, 1); - vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT); - vc_init(vc, vc->vc_rows, vc->vc_cols, - currcons || !vc->vc_sw->con_save_screen); - } - currcons = fg_console = 0; - master_display_fg = vc = vc_cons[currcons].d; - set_origin(vc); - save_screen(vc); - gotoxy(vc, vc->vc_x, vc->vc_y); - csi_J(vc, 0); - update_screen(vc); - printk("Console: %s %s %dx%d", - vc->vc_can_do_color ? "colour" : "mono", - display_desc, vc->vc_cols, vc->vc_rows); - printable = 1; - printk("\n"); - - release_console_sem(); - -#ifdef CONFIG_VT_CONSOLE - register_console(&vt_console_driver); -#endif - return 0; -} -console_initcall(con_init); - -static const struct tty_operations con_ops = { - .open = con_open, - .close = con_close, - .write = con_write, - .write_room = con_write_room, - .put_char = con_put_char, - .flush_chars = con_flush_chars, - .chars_in_buffer = con_chars_in_buffer, - .ioctl = vt_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vt_compat_ioctl, -#endif - .stop = con_stop, - .start = con_start, - .throttle = con_throttle, - .unthrottle = con_unthrottle, - .resize = vt_resize, - .shutdown = con_shutdown -}; - -static struct cdev vc0_cdev; - -int __init vty_init(const struct file_operations *console_fops) -{ - cdev_init(&vc0_cdev, console_fops); - if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) || - register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) - panic("Couldn't register /dev/tty0 driver\n"); - device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); - - vcs_init(); - - console_driver = alloc_tty_driver(MAX_NR_CONSOLES); - if (!console_driver) - panic("Couldn't allocate console driver\n"); - console_driver->owner = THIS_MODULE; - console_driver->name = "tty"; - console_driver->name_base = 1; - console_driver->major = TTY_MAJOR; - console_driver->minor_start = 1; - console_driver->type = TTY_DRIVER_TYPE_CONSOLE; - console_driver->init_termios = tty_std_termios; - if (default_utf8) - console_driver->init_termios.c_iflag |= IUTF8; - console_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; - tty_set_operations(console_driver, &con_ops); - if (tty_register_driver(console_driver)) - panic("Couldn't register console driver\n"); - kbd_init(); - console_map_init(); -#ifdef CONFIG_MDA_CONSOLE - mda_console_init(); -#endif - return 0; -} - -#ifndef VT_SINGLE_DRIVER - -static struct class *vtconsole_class; - -static int bind_con_driver(const struct consw *csw, int first, int last, - int deflt) -{ - struct module *owner = csw->owner; - const char *desc = NULL; - struct con_driver *con_driver; - int i, j = -1, k = -1, retval = -ENODEV; - - if (!try_module_get(owner)) - return -ENODEV; - - acquire_console_sem(); - - /* check if driver is registered */ - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { - con_driver = ®istered_con_driver[i]; - - if (con_driver->con == csw) { - desc = con_driver->desc; - retval = 0; - break; - } - } - - if (retval) - goto err; - - if (!(con_driver->flag & CON_DRIVER_FLAG_INIT)) { - csw->con_startup(); - con_driver->flag |= CON_DRIVER_FLAG_INIT; - } - - if (deflt) { - if (conswitchp) - module_put(conswitchp->owner); - - __module_get(owner); - conswitchp = csw; - } - - first = max(first, con_driver->first); - last = min(last, con_driver->last); - - for (i = first; i <= last; i++) { - int old_was_color; - struct vc_data *vc = vc_cons[i].d; - - if (con_driver_map[i]) - module_put(con_driver_map[i]->owner); - __module_get(owner); - con_driver_map[i] = csw; - - if (!vc || !vc->vc_sw) - continue; - - j = i; - - if (CON_IS_VISIBLE(vc)) { - k = i; - save_screen(vc); - } - - old_was_color = vc->vc_can_do_color; - vc->vc_sw->con_deinit(vc); - vc->vc_origin = (unsigned long)vc->vc_screenbuf; - visual_init(vc, i, 0); - set_origin(vc); - update_attr(vc); - - /* If the console changed between mono <-> color, then - * the attributes in the screenbuf will be wrong. The - * following resets all attributes to something sane. - */ - if (old_was_color != vc->vc_can_do_color) - clear_buffer_attributes(vc); - } - - printk("Console: switching "); - if (!deflt) - printk("consoles %d-%d ", first+1, last+1); - if (j >= 0) { - struct vc_data *vc = vc_cons[j].d; - - printk("to %s %s %dx%d\n", - vc->vc_can_do_color ? "colour" : "mono", - desc, vc->vc_cols, vc->vc_rows); - - if (k >= 0) { - vc = vc_cons[k].d; - update_screen(vc); - } - } else - printk("to %s\n", desc); - - retval = 0; -err: - release_console_sem(); - module_put(owner); - return retval; -}; - -#ifdef CONFIG_VT_HW_CONSOLE_BINDING -static int con_is_graphics(const struct consw *csw, int first, int last) -{ - int i, retval = 0; - - for (i = first; i <= last; i++) { - struct vc_data *vc = vc_cons[i].d; - - if (vc && vc->vc_mode == KD_GRAPHICS) { - retval = 1; - break; - } - } - - return retval; -} - -/** - * unbind_con_driver - unbind a console driver - * @csw: pointer to console driver to unregister - * @first: first in range of consoles that @csw should be unbound from - * @last: last in range of consoles that @csw should be unbound from - * @deflt: should next bound console driver be default after @csw is unbound? - * - * To unbind a driver from all possible consoles, pass 0 as @first and - * %MAX_NR_CONSOLES as @last. - * - * @deflt controls whether the console that ends up replacing @csw should be - * the default console. - * - * RETURNS: - * -ENODEV if @csw isn't a registered console driver or can't be unregistered - * or 0 on success. - */ -int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) -{ - struct module *owner = csw->owner; - const struct consw *defcsw = NULL; - struct con_driver *con_driver = NULL, *con_back = NULL; - int i, retval = -ENODEV; - - if (!try_module_get(owner)) - return -ENODEV; - - acquire_console_sem(); - - /* check if driver is registered and if it is unbindable */ - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { - con_driver = ®istered_con_driver[i]; - - if (con_driver->con == csw && - con_driver->flag & CON_DRIVER_FLAG_MODULE) { - retval = 0; - break; - } - } - - if (retval) { - release_console_sem(); - goto err; - } - - retval = -ENODEV; - - /* check if backup driver exists */ - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { - con_back = ®istered_con_driver[i]; - - if (con_back->con && - !(con_back->flag & CON_DRIVER_FLAG_MODULE)) { - defcsw = con_back->con; - retval = 0; - break; - } - } - - if (retval) { - release_console_sem(); - goto err; - } - - if (!con_is_bound(csw)) { - release_console_sem(); - goto err; - } - - first = max(first, con_driver->first); - last = min(last, con_driver->last); - - for (i = first; i <= last; i++) { - if (con_driver_map[i] == csw) { - module_put(csw->owner); - con_driver_map[i] = NULL; - } - } - - if (!con_is_bound(defcsw)) { - const struct consw *defconsw = conswitchp; - - defcsw->con_startup(); - con_back->flag |= CON_DRIVER_FLAG_INIT; - /* - * vgacon may change the default driver to point - * to dummycon, we restore it here... - */ - conswitchp = defconsw; - } - - if (!con_is_bound(csw)) - con_driver->flag &= ~CON_DRIVER_FLAG_INIT; - - release_console_sem(); - /* ignore return value, binding should not fail */ - bind_con_driver(defcsw, first, last, deflt); -err: - module_put(owner); - return retval; - -} -EXPORT_SYMBOL(unbind_con_driver); - -static int vt_bind(struct con_driver *con) -{ - const struct consw *defcsw = NULL, *csw = NULL; - int i, more = 1, first = -1, last = -1, deflt = 0; - - if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || - con_is_graphics(con->con, con->first, con->last)) - goto err; - - csw = con->con; - - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { - struct con_driver *con = ®istered_con_driver[i]; - - if (con->con && !(con->flag & CON_DRIVER_FLAG_MODULE)) { - defcsw = con->con; - break; - } - } - - if (!defcsw) - goto err; - - while (more) { - more = 0; - - for (i = con->first; i <= con->last; i++) { - if (con_driver_map[i] == defcsw) { - if (first == -1) - first = i; - last = i; - more = 1; - } else if (first != -1) - break; - } - - if (first == 0 && last == MAX_NR_CONSOLES -1) - deflt = 1; - - if (first != -1) - bind_con_driver(csw, first, last, deflt); - - first = -1; - last = -1; - deflt = 0; - } - -err: - return 0; -} - -static int vt_unbind(struct con_driver *con) -{ - const struct consw *csw = NULL; - int i, more = 1, first = -1, last = -1, deflt = 0; - - if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) || - con_is_graphics(con->con, con->first, con->last)) - goto err; - - csw = con->con; - - while (more) { - more = 0; - - for (i = con->first; i <= con->last; i++) { - if (con_driver_map[i] == csw) { - if (first == -1) - first = i; - last = i; - more = 1; - } else if (first != -1) - break; - } - - if (first == 0 && last == MAX_NR_CONSOLES -1) - deflt = 1; - - if (first != -1) - unbind_con_driver(csw, first, last, deflt); - - first = -1; - last = -1; - deflt = 0; - } - -err: - return 0; -} -#else -static inline int vt_bind(struct con_driver *con) -{ - return 0; -} -static inline int vt_unbind(struct con_driver *con) -{ - return 0; -} -#endif /* CONFIG_VT_HW_CONSOLE_BINDING */ - -static ssize_t store_bind(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct con_driver *con = dev_get_drvdata(dev); - int bind = simple_strtoul(buf, NULL, 0); - - if (bind) - vt_bind(con); - else - vt_unbind(con); - - return count; -} - -static ssize_t show_bind(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct con_driver *con = dev_get_drvdata(dev); - int bind = con_is_bound(con->con); - - return snprintf(buf, PAGE_SIZE, "%i\n", bind); -} - -static ssize_t show_name(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct con_driver *con = dev_get_drvdata(dev); - - return snprintf(buf, PAGE_SIZE, "%s %s\n", - (con->flag & CON_DRIVER_FLAG_MODULE) ? "(M)" : "(S)", - con->desc); - -} - -static struct device_attribute device_attrs[] = { - __ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind), - __ATTR(name, S_IRUGO, show_name, NULL), -}; - -static int vtconsole_init_device(struct con_driver *con) -{ - int i; - int error = 0; - - con->flag |= CON_DRIVER_FLAG_ATTR; - dev_set_drvdata(con->dev, con); - for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { - error = device_create_file(con->dev, &device_attrs[i]); - if (error) - break; - } - - if (error) { - while (--i >= 0) - device_remove_file(con->dev, &device_attrs[i]); - con->flag &= ~CON_DRIVER_FLAG_ATTR; - } - - return error; -} - -static void vtconsole_deinit_device(struct con_driver *con) -{ - int i; - - if (con->flag & CON_DRIVER_FLAG_ATTR) { - for (i = 0; i < ARRAY_SIZE(device_attrs); i++) - device_remove_file(con->dev, &device_attrs[i]); - con->flag &= ~CON_DRIVER_FLAG_ATTR; - } -} - -/** - * con_is_bound - checks if driver is bound to the console - * @csw: console driver - * - * RETURNS: zero if unbound, nonzero if bound - * - * Drivers can call this and if zero, they should release - * all resources allocated on con_startup() - */ -int con_is_bound(const struct consw *csw) -{ - int i, bound = 0; - - for (i = 0; i < MAX_NR_CONSOLES; i++) { - if (con_driver_map[i] == csw) { - bound = 1; - break; - } - } - - return bound; -} -EXPORT_SYMBOL(con_is_bound); - -/** - * con_debug_enter - prepare the console for the kernel debugger - * @sw: console driver - * - * Called when the console is taken over by the kernel debugger, this - * function needs to save the current console state, then put the console - * into a state suitable for the kernel debugger. - * - * RETURNS: - * Zero on success, nonzero if a failure occurred when trying to prepare - * the console for the debugger. - */ -int con_debug_enter(struct vc_data *vc) -{ - int ret = 0; - - saved_fg_console = fg_console; - saved_last_console = last_console; - saved_want_console = want_console; - saved_vc_mode = vc->vc_mode; - saved_console_blanked = console_blanked; - vc->vc_mode = KD_TEXT; - console_blanked = 0; - if (vc->vc_sw->con_debug_enter) - ret = vc->vc_sw->con_debug_enter(vc); -#ifdef CONFIG_KGDB_KDB - /* Set the initial LINES variable if it is not already set */ - if (vc->vc_rows < 999) { - int linecount; - char lns[4]; - const char *setargs[3] = { - "set", - "LINES", - lns, - }; - if (kdbgetintenv(setargs[0], &linecount)) { - snprintf(lns, 4, "%i", vc->vc_rows); - kdb_set(2, setargs); - } - } -#endif /* CONFIG_KGDB_KDB */ - return ret; -} -EXPORT_SYMBOL_GPL(con_debug_enter); - -/** - * con_debug_leave - restore console state - * @sw: console driver - * - * Restore the console state to what it was before the kernel debugger - * was invoked. - * - * RETURNS: - * Zero on success, nonzero if a failure occurred when trying to restore - * the console. - */ -int con_debug_leave(void) -{ - struct vc_data *vc; - int ret = 0; - - fg_console = saved_fg_console; - last_console = saved_last_console; - want_console = saved_want_console; - console_blanked = saved_console_blanked; - vc_cons[fg_console].d->vc_mode = saved_vc_mode; - - vc = vc_cons[fg_console].d; - if (vc->vc_sw->con_debug_leave) - ret = vc->vc_sw->con_debug_leave(vc); - return ret; -} -EXPORT_SYMBOL_GPL(con_debug_leave); - -/** - * register_con_driver - register console driver to console layer - * @csw: console driver - * @first: the first console to take over, minimum value is 0 - * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1 - * - * DESCRIPTION: This function registers a console driver which can later - * bind to a range of consoles specified by @first and @last. It will - * also initialize the console driver by calling con_startup(). - */ -int register_con_driver(const struct consw *csw, int first, int last) -{ - struct module *owner = csw->owner; - struct con_driver *con_driver; - const char *desc; - int i, retval = 0; - - if (!try_module_get(owner)) - return -ENODEV; - - acquire_console_sem(); - - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { - con_driver = ®istered_con_driver[i]; - - /* already registered */ - if (con_driver->con == csw) - retval = -EINVAL; - } - - if (retval) - goto err; - - desc = csw->con_startup(); - - if (!desc) - goto err; - - retval = -EINVAL; - - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { - con_driver = ®istered_con_driver[i]; - - if (con_driver->con == NULL) { - con_driver->con = csw; - con_driver->desc = desc; - con_driver->node = i; - con_driver->flag = CON_DRIVER_FLAG_MODULE | - CON_DRIVER_FLAG_INIT; - con_driver->first = first; - con_driver->last = last; - retval = 0; - break; - } - } - - if (retval) - goto err; - - con_driver->dev = device_create(vtconsole_class, NULL, - MKDEV(0, con_driver->node), - NULL, "vtcon%i", - con_driver->node); - - if (IS_ERR(con_driver->dev)) { - printk(KERN_WARNING "Unable to create device for %s; " - "errno = %ld\n", con_driver->desc, - PTR_ERR(con_driver->dev)); - con_driver->dev = NULL; - } else { - vtconsole_init_device(con_driver); - } - -err: - release_console_sem(); - module_put(owner); - return retval; -} -EXPORT_SYMBOL(register_con_driver); - -/** - * unregister_con_driver - unregister console driver from console layer - * @csw: console driver - * - * DESCRIPTION: All drivers that registers to the console layer must - * call this function upon exit, or if the console driver is in a state - * where it won't be able to handle console services, such as the - * framebuffer console without loaded framebuffer drivers. - * - * The driver must unbind first prior to unregistration. - */ -int unregister_con_driver(const struct consw *csw) -{ - int i, retval = -ENODEV; - - acquire_console_sem(); - - /* cannot unregister a bound driver */ - if (con_is_bound(csw)) - goto err; - - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { - struct con_driver *con_driver = ®istered_con_driver[i]; - - if (con_driver->con == csw && - con_driver->flag & CON_DRIVER_FLAG_MODULE) { - vtconsole_deinit_device(con_driver); - device_destroy(vtconsole_class, - MKDEV(0, con_driver->node)); - con_driver->con = NULL; - con_driver->desc = NULL; - con_driver->dev = NULL; - con_driver->node = 0; - con_driver->flag = 0; - con_driver->first = 0; - con_driver->last = 0; - retval = 0; - break; - } - } -err: - release_console_sem(); - return retval; -} -EXPORT_SYMBOL(unregister_con_driver); - -/* - * If we support more console drivers, this function is used - * when a driver wants to take over some existing consoles - * and become default driver for newly opened ones. - * - * take_over_console is basically a register followed by unbind - */ -int take_over_console(const struct consw *csw, int first, int last, int deflt) -{ - int err; - - err = register_con_driver(csw, first, last); - - if (!err) - bind_con_driver(csw, first, last, deflt); - - return err; -} - -/* - * give_up_console is a wrapper to unregister_con_driver. It will only - * work if driver is fully unbound. - */ -void give_up_console(const struct consw *csw) -{ - unregister_con_driver(csw); -} - -static int __init vtconsole_class_init(void) -{ - int i; - - vtconsole_class = class_create(THIS_MODULE, "vtconsole"); - if (IS_ERR(vtconsole_class)) { - printk(KERN_WARNING "Unable to create vt console class; " - "errno = %ld\n", PTR_ERR(vtconsole_class)); - vtconsole_class = NULL; - } - - /* Add system drivers to sysfs */ - for (i = 0; i < MAX_NR_CON_DRIVER; i++) { - struct con_driver *con = ®istered_con_driver[i]; - - if (con->con && !con->dev) { - con->dev = device_create(vtconsole_class, NULL, - MKDEV(0, con->node), - NULL, "vtcon%i", - con->node); - - if (IS_ERR(con->dev)) { - printk(KERN_WARNING "Unable to create " - "device for %s; errno = %ld\n", - con->desc, PTR_ERR(con->dev)); - con->dev = NULL; - } else { - vtconsole_init_device(con); - } - } - } - - return 0; -} -postcore_initcall(vtconsole_class_init); - -#endif - -/* - * Screen blanking - */ - -static int set_vesa_blanking(char __user *p) -{ - unsigned int mode; - - if (get_user(mode, p + 1)) - return -EFAULT; - - vesa_blank_mode = (mode < 4) ? mode : 0; - return 0; -} - -void do_blank_screen(int entering_gfx) -{ - struct vc_data *vc = vc_cons[fg_console].d; - int i; - - WARN_CONSOLE_UNLOCKED(); - - if (console_blanked) { - if (blank_state == blank_vesa_wait) { - blank_state = blank_off; - vc->vc_sw->con_blank(vc, vesa_blank_mode + 1, 0); - } - return; - } - - /* entering graphics mode? */ - if (entering_gfx) { - hide_cursor(vc); - save_screen(vc); - vc->vc_sw->con_blank(vc, -1, 1); - console_blanked = fg_console + 1; - blank_state = blank_off; - set_origin(vc); - return; - } - - if (blank_state != blank_normal_wait) - return; - blank_state = blank_off; - - /* don't blank graphics */ - if (vc->vc_mode != KD_TEXT) { - console_blanked = fg_console + 1; - return; - } - - hide_cursor(vc); - del_timer_sync(&console_timer); - blank_timer_expired = 0; - - save_screen(vc); - /* In case we need to reset origin, blanking hook returns 1 */ - i = vc->vc_sw->con_blank(vc, vesa_off_interval ? 1 : (vesa_blank_mode + 1), 0); - console_blanked = fg_console + 1; - if (i) - set_origin(vc); - - if (console_blank_hook && console_blank_hook(1)) - return; - - if (vesa_off_interval && vesa_blank_mode) { - blank_state = blank_vesa_wait; - mod_timer(&console_timer, jiffies + vesa_off_interval); - } - vt_event_post(VT_EVENT_BLANK, vc->vc_num, vc->vc_num); -} -EXPORT_SYMBOL(do_blank_screen); - -/* - * Called by timer as well as from vt_console_driver - */ -void do_unblank_screen(int leaving_gfx) -{ - struct vc_data *vc; - - /* This should now always be called from a "sane" (read: can schedule) - * context for the sake of the low level drivers, except in the special - * case of oops_in_progress - */ - if (!oops_in_progress) - might_sleep(); - - WARN_CONSOLE_UNLOCKED(); - - ignore_poke = 0; - if (!console_blanked) - return; - if (!vc_cons_allocated(fg_console)) { - /* impossible */ - printk("unblank_screen: tty %d not allocated ??\n", fg_console+1); - return; - } - vc = vc_cons[fg_console].d; - /* Try to unblank in oops case too */ - if (vc->vc_mode != KD_TEXT && !vt_force_oops_output(vc)) - return; /* but leave console_blanked != 0 */ - - if (blankinterval) { - mod_timer(&console_timer, jiffies + (blankinterval * HZ)); - blank_state = blank_normal_wait; - } - - console_blanked = 0; - if (vc->vc_sw->con_blank(vc, 0, leaving_gfx) || vt_force_oops_output(vc)) - /* Low-level driver cannot restore -> do it ourselves */ - update_screen(vc); - if (console_blank_hook) - console_blank_hook(0); - set_palette(vc); - set_cursor(vc); - vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num); -} -EXPORT_SYMBOL(do_unblank_screen); - -/* - * This is called by the outside world to cause a forced unblank, mostly for - * oopses. Currently, I just call do_unblank_screen(0), but we could eventually - * call it with 1 as an argument and so force a mode restore... that may kill - * X or at least garbage the screen but would also make the Oops visible... - */ -void unblank_screen(void) -{ - do_unblank_screen(0); -} - -/* - * We defer the timer blanking to work queue so it can take the console mutex - * (console operations can still happen at irq time, but only from printk which - * has the console mutex. Not perfect yet, but better than no locking - */ -static void blank_screen_t(unsigned long dummy) -{ - if (unlikely(!keventd_up())) { - mod_timer(&console_timer, jiffies + (blankinterval * HZ)); - return; - } - blank_timer_expired = 1; - schedule_work(&console_work); -} - -void poke_blanked_console(void) -{ - WARN_CONSOLE_UNLOCKED(); - - /* Add this so we quickly catch whoever might call us in a non - * safe context. Nowadays, unblank_screen() isn't to be called in - * atomic contexts and is allowed to schedule (with the special case - * of oops_in_progress, but that isn't of any concern for this - * function. --BenH. - */ - might_sleep(); - - /* This isn't perfectly race free, but a race here would be mostly harmless, - * at worse, we'll do a spurrious blank and it's unlikely - */ - del_timer(&console_timer); - blank_timer_expired = 0; - - if (ignore_poke || !vc_cons[fg_console].d || vc_cons[fg_console].d->vc_mode == KD_GRAPHICS) - return; - if (console_blanked) - unblank_screen(); - else if (blankinterval) { - mod_timer(&console_timer, jiffies + (blankinterval * HZ)); - blank_state = blank_normal_wait; - } -} - -/* - * Palettes - */ - -static void set_palette(struct vc_data *vc) -{ - WARN_CONSOLE_UNLOCKED(); - - if (vc->vc_mode != KD_GRAPHICS) - vc->vc_sw->con_set_palette(vc, color_table); -} - -static int set_get_cmap(unsigned char __user *arg, int set) -{ - int i, j, k; - - WARN_CONSOLE_UNLOCKED(); - - for (i = 0; i < 16; i++) - if (set) { - get_user(default_red[i], arg++); - get_user(default_grn[i], arg++); - get_user(default_blu[i], arg++); - } else { - put_user(default_red[i], arg++); - put_user(default_grn[i], arg++); - put_user(default_blu[i], arg++); - } - if (set) { - for (i = 0; i < MAX_NR_CONSOLES; i++) - if (vc_cons_allocated(i)) { - for (j = k = 0; j < 16; j++) { - vc_cons[i].d->vc_palette[k++] = default_red[j]; - vc_cons[i].d->vc_palette[k++] = default_grn[j]; - vc_cons[i].d->vc_palette[k++] = default_blu[j]; - } - set_palette(vc_cons[i].d); - } - } - return 0; -} - -/* - * Load palette into the DAC registers. arg points to a colour - * map, 3 bytes per colour, 16 colours, range from 0 to 255. - */ - -int con_set_cmap(unsigned char __user *arg) -{ - int rc; - - acquire_console_sem(); - rc = set_get_cmap (arg,1); - release_console_sem(); - - return rc; -} - -int con_get_cmap(unsigned char __user *arg) -{ - int rc; - - acquire_console_sem(); - rc = set_get_cmap (arg,0); - release_console_sem(); - - return rc; -} - -void reset_palette(struct vc_data *vc) -{ - int j, k; - for (j=k=0; j<16; j++) { - vc->vc_palette[k++] = default_red[j]; - vc->vc_palette[k++] = default_grn[j]; - vc->vc_palette[k++] = default_blu[j]; - } - set_palette(vc); -} - -/* - * Font switching - * - * Currently we only support fonts up to 32 pixels wide, at a maximum height - * of 32 pixels. Userspace fontdata is stored with 32 bytes (shorts/ints, - * depending on width) reserved for each character which is kinda wasty, but - * this is done in order to maintain compatibility with the EGA/VGA fonts. It - * is upto the actual low-level console-driver convert data into its favorite - * format (maybe we should add a `fontoffset' field to the `display' - * structure so we won't have to convert the fontdata all the time. - * /Jes - */ - -#define max_font_size 65536 - -static int con_font_get(struct vc_data *vc, struct console_font_op *op) -{ - struct console_font font; - int rc = -EINVAL; - int c; - - if (vc->vc_mode != KD_TEXT) - return -EINVAL; - - if (op->data) { - font.data = kmalloc(max_font_size, GFP_KERNEL); - if (!font.data) - return -ENOMEM; - } else - font.data = NULL; - - acquire_console_sem(); - if (vc->vc_sw->con_font_get) - rc = vc->vc_sw->con_font_get(vc, &font); - else - rc = -ENOSYS; - release_console_sem(); - - if (rc) - goto out; - - c = (font.width+7)/8 * 32 * font.charcount; - - if (op->data && font.charcount > op->charcount) - rc = -ENOSPC; - if (!(op->flags & KD_FONT_FLAG_OLD)) { - if (font.width > op->width || font.height > op->height) - rc = -ENOSPC; - } else { - if (font.width != 8) - rc = -EIO; - else if ((op->height && font.height > op->height) || - font.height > 32) - rc = -ENOSPC; - } - if (rc) - goto out; - - op->height = font.height; - op->width = font.width; - op->charcount = font.charcount; - - if (op->data && copy_to_user(op->data, font.data, c)) - rc = -EFAULT; - -out: - kfree(font.data); - return rc; -} - -static int con_font_set(struct vc_data *vc, struct console_font_op *op) -{ - struct console_font font; - int rc = -EINVAL; - int size; - - if (vc->vc_mode != KD_TEXT) - return -EINVAL; - if (!op->data) - return -EINVAL; - if (op->charcount > 512) - return -EINVAL; - if (!op->height) { /* Need to guess font height [compat] */ - int h, i; - u8 __user *charmap = op->data; - u8 tmp; - - /* If from KDFONTOP ioctl, don't allow things which can be done in userland, - so that we can get rid of this soon */ - if (!(op->flags & KD_FONT_FLAG_OLD)) - return -EINVAL; - for (h = 32; h > 0; h--) - for (i = 0; i < op->charcount; i++) { - if (get_user(tmp, &charmap[32*i+h-1])) - return -EFAULT; - if (tmp) - goto nonzero; - } - return -EINVAL; - nonzero: - op->height = h; - } - if (op->width <= 0 || op->width > 32 || op->height > 32) - return -EINVAL; - size = (op->width+7)/8 * 32 * op->charcount; - if (size > max_font_size) - return -ENOSPC; - font.charcount = op->charcount; - font.height = op->height; - font.width = op->width; - font.data = memdup_user(op->data, size); - if (IS_ERR(font.data)) - return PTR_ERR(font.data); - acquire_console_sem(); - if (vc->vc_sw->con_font_set) - rc = vc->vc_sw->con_font_set(vc, &font, op->flags); - else - rc = -ENOSYS; - release_console_sem(); - kfree(font.data); - return rc; -} - -static int con_font_default(struct vc_data *vc, struct console_font_op *op) -{ - struct console_font font = {.width = op->width, .height = op->height}; - char name[MAX_FONT_NAME]; - char *s = name; - int rc; - - if (vc->vc_mode != KD_TEXT) - return -EINVAL; - - if (!op->data) - s = NULL; - else if (strncpy_from_user(name, op->data, MAX_FONT_NAME - 1) < 0) - return -EFAULT; - else - name[MAX_FONT_NAME - 1] = 0; - - acquire_console_sem(); - if (vc->vc_sw->con_font_default) - rc = vc->vc_sw->con_font_default(vc, &font, s); - else - rc = -ENOSYS; - release_console_sem(); - if (!rc) { - op->width = font.width; - op->height = font.height; - } - return rc; -} - -static int con_font_copy(struct vc_data *vc, struct console_font_op *op) -{ - int con = op->height; - int rc; - - if (vc->vc_mode != KD_TEXT) - return -EINVAL; - - acquire_console_sem(); - if (!vc->vc_sw->con_font_copy) - rc = -ENOSYS; - else if (con < 0 || !vc_cons_allocated(con)) - rc = -ENOTTY; - else if (con == vc->vc_num) /* nothing to do */ - rc = 0; - else - rc = vc->vc_sw->con_font_copy(vc, con); - release_console_sem(); - return rc; -} - -int con_font_op(struct vc_data *vc, struct console_font_op *op) -{ - switch (op->op) { - case KD_FONT_OP_SET: - return con_font_set(vc, op); - case KD_FONT_OP_GET: - return con_font_get(vc, op); - case KD_FONT_OP_SET_DEFAULT: - return con_font_default(vc, op); - case KD_FONT_OP_COPY: - return con_font_copy(vc, op); - } - return -ENOSYS; -} - -/* - * Interface exported to selection and vcs. - */ - -/* used by selection */ -u16 screen_glyph(struct vc_data *vc, int offset) -{ - u16 w = scr_readw(screenpos(vc, offset, 1)); - u16 c = w & 0xff; - - if (w & vc->vc_hi_font_mask) - c |= 0x100; - return c; -} -EXPORT_SYMBOL_GPL(screen_glyph); - -/* used by vcs - note the word offset */ -unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed) -{ - return screenpos(vc, 2 * w_offset, viewed); -} - -void getconsxy(struct vc_data *vc, unsigned char *p) -{ - p[0] = vc->vc_x; - p[1] = vc->vc_y; -} - -void putconsxy(struct vc_data *vc, unsigned char *p) -{ - hide_cursor(vc); - gotoxy(vc, p[0], p[1]); - set_cursor(vc); -} - -u16 vcs_scr_readw(struct vc_data *vc, const u16 *org) -{ - if ((unsigned long)org == vc->vc_pos && softcursor_original != -1) - return softcursor_original; - return scr_readw(org); -} - -void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org) -{ - scr_writew(val, org); - if ((unsigned long)org == vc->vc_pos) { - softcursor_original = -1; - add_softcursor(vc); - } -} - -void vcs_scr_updated(struct vc_data *vc) -{ - notify_update(vc); -} - -/* - * Visible symbols for modules - */ - -EXPORT_SYMBOL(color_table); -EXPORT_SYMBOL(default_red); -EXPORT_SYMBOL(default_grn); -EXPORT_SYMBOL(default_blu); -EXPORT_SYMBOL(update_region); -EXPORT_SYMBOL(redraw_screen); -EXPORT_SYMBOL(vc_resize); -EXPORT_SYMBOL(fg_console); -EXPORT_SYMBOL(console_blank_hook); -EXPORT_SYMBOL(console_blanked); -EXPORT_SYMBOL(vc_cons); -EXPORT_SYMBOL(global_cursor_default); -#ifndef VT_SINGLE_DRIVER -EXPORT_SYMBOL(take_over_console); -EXPORT_SYMBOL(give_up_console); -#endif diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c deleted file mode 100644 index 6b68a0fb4611..000000000000 --- a/drivers/char/vt_ioctl.c +++ /dev/null @@ -1,1788 +0,0 @@ -/* - * linux/drivers/char/vt_ioctl.c - * - * Copyright (C) 1992 obz under the linux copyright - * - * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993 - * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994 - * Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995 - * Some code moved for less code duplication - Andi Kleen - Mar 1997 - * Check put/get_user, cleanups - acme@conectiva.com.br - Jun 2001 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -char vt_dont_switch; -extern struct tty_driver *console_driver; - -#define VT_IS_IN_USE(i) (console_driver->ttys[i] && console_driver->ttys[i]->count) -#define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || vc_cons[i].d == sel_cons) - -/* - * Console (vt and kd) routines, as defined by USL SVR4 manual, and by - * experimentation and study of X386 SYSV handling. - * - * One point of difference: SYSV vt's are /dev/vtX, which X >= 0, and - * /dev/console is a separate ttyp. Under Linux, /dev/tty0 is /dev/console, - * and the vc start at /dev/ttyX, X >= 1. We maintain that here, so we will - * always treat our set of vt as numbered 1..MAX_NR_CONSOLES (corresponding to - * ttys 0..MAX_NR_CONSOLES-1). Explicitly naming VT 0 is illegal, but using - * /dev/tty0 (fg_console) as a target is legal, since an implicit aliasing - * to the current console is done by the main ioctl code. - */ - -#ifdef CONFIG_X86 -#include -#endif - -static void complete_change_console(struct vc_data *vc); - -/* - * User space VT_EVENT handlers - */ - -struct vt_event_wait { - struct list_head list; - struct vt_event event; - int done; -}; - -static LIST_HEAD(vt_events); -static DEFINE_SPINLOCK(vt_event_lock); -static DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue); - -/** - * vt_event_post - * @event: the event that occurred - * @old: old console - * @new: new console - * - * Post an VT event to interested VT handlers - */ - -void vt_event_post(unsigned int event, unsigned int old, unsigned int new) -{ - struct list_head *pos, *head; - unsigned long flags; - int wake = 0; - - spin_lock_irqsave(&vt_event_lock, flags); - head = &vt_events; - - list_for_each(pos, head) { - struct vt_event_wait *ve = list_entry(pos, - struct vt_event_wait, list); - if (!(ve->event.event & event)) - continue; - ve->event.event = event; - /* kernel view is consoles 0..n-1, user space view is - console 1..n with 0 meaning current, so we must bias */ - ve->event.oldev = old + 1; - ve->event.newev = new + 1; - wake = 1; - ve->done = 1; - } - spin_unlock_irqrestore(&vt_event_lock, flags); - if (wake) - wake_up_interruptible(&vt_event_waitqueue); -} - -/** - * vt_event_wait - wait for an event - * @vw: our event - * - * Waits for an event to occur which completes our vt_event_wait - * structure. On return the structure has wv->done set to 1 for success - * or 0 if some event such as a signal ended the wait. - */ - -static void vt_event_wait(struct vt_event_wait *vw) -{ - unsigned long flags; - /* Prepare the event */ - INIT_LIST_HEAD(&vw->list); - vw->done = 0; - /* Queue our event */ - spin_lock_irqsave(&vt_event_lock, flags); - list_add(&vw->list, &vt_events); - spin_unlock_irqrestore(&vt_event_lock, flags); - /* Wait for it to pass */ - wait_event_interruptible_tty(vt_event_waitqueue, vw->done); - /* Dequeue it */ - spin_lock_irqsave(&vt_event_lock, flags); - list_del(&vw->list); - spin_unlock_irqrestore(&vt_event_lock, flags); -} - -/** - * vt_event_wait_ioctl - event ioctl handler - * @arg: argument to ioctl - * - * Implement the VT_WAITEVENT ioctl using the VT event interface - */ - -static int vt_event_wait_ioctl(struct vt_event __user *event) -{ - struct vt_event_wait vw; - - if (copy_from_user(&vw.event, event, sizeof(struct vt_event))) - return -EFAULT; - /* Highest supported event for now */ - if (vw.event.event & ~VT_MAX_EVENT) - return -EINVAL; - - vt_event_wait(&vw); - /* If it occurred report it */ - if (vw.done) { - if (copy_to_user(event, &vw.event, sizeof(struct vt_event))) - return -EFAULT; - return 0; - } - return -EINTR; -} - -/** - * vt_waitactive - active console wait - * @event: event code - * @n: new console - * - * Helper for event waits. Used to implement the legacy - * event waiting ioctls in terms of events - */ - -int vt_waitactive(int n) -{ - struct vt_event_wait vw; - do { - if (n == fg_console + 1) - break; - vw.event.event = VT_EVENT_SWITCH; - vt_event_wait(&vw); - if (vw.done == 0) - return -EINTR; - } while (vw.event.newev != n); - return 0; -} - -/* - * these are the valid i/o ports we're allowed to change. they map all the - * video ports - */ -#define GPFIRST 0x3b4 -#define GPLAST 0x3df -#define GPNUM (GPLAST - GPFIRST + 1) - -#define i (tmp.kb_index) -#define s (tmp.kb_table) -#define v (tmp.kb_value) -static inline int -do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_struct *kbd) -{ - struct kbentry tmp; - ushort *key_map, val, ov; - - if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry))) - return -EFAULT; - - if (!capable(CAP_SYS_TTY_CONFIG)) - perm = 0; - - switch (cmd) { - case KDGKBENT: - key_map = key_maps[s]; - if (key_map) { - val = U(key_map[i]); - if (kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES) - val = K_HOLE; - } else - val = (i ? K_HOLE : K_NOSUCHMAP); - return put_user(val, &user_kbe->kb_value); - case KDSKBENT: - if (!perm) - return -EPERM; - if (!i && v == K_NOSUCHMAP) { - /* deallocate map */ - key_map = key_maps[s]; - if (s && key_map) { - key_maps[s] = NULL; - if (key_map[0] == U(K_ALLOCATED)) { - kfree(key_map); - keymap_count--; - } - } - break; - } - - if (KTYP(v) < NR_TYPES) { - if (KVAL(v) > max_vals[KTYP(v)]) - return -EINVAL; - } else - if (kbd->kbdmode != VC_UNICODE) - return -EINVAL; - - /* ++Geert: non-PC keyboards may generate keycode zero */ -#if !defined(__mc68000__) && !defined(__powerpc__) - /* assignment to entry 0 only tests validity of args */ - if (!i) - break; -#endif - - if (!(key_map = key_maps[s])) { - int j; - - if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && - !capable(CAP_SYS_RESOURCE)) - return -EPERM; - - key_map = kmalloc(sizeof(plain_map), - GFP_KERNEL); - if (!key_map) - return -ENOMEM; - key_maps[s] = key_map; - key_map[0] = U(K_ALLOCATED); - for (j = 1; j < NR_KEYS; j++) - key_map[j] = U(K_HOLE); - keymap_count++; - } - ov = U(key_map[i]); - if (v == ov) - break; /* nothing to do */ - /* - * Attention Key. - */ - if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN)) - return -EPERM; - key_map[i] = U(v); - if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT)) - compute_shiftstate(); - break; - } - return 0; -} -#undef i -#undef s -#undef v - -static inline int -do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, int perm) -{ - struct kbkeycode tmp; - int kc = 0; - - if (copy_from_user(&tmp, user_kbkc, sizeof(struct kbkeycode))) - return -EFAULT; - switch (cmd) { - case KDGETKEYCODE: - kc = getkeycode(tmp.scancode); - if (kc >= 0) - kc = put_user(kc, &user_kbkc->keycode); - break; - case KDSETKEYCODE: - if (!perm) - return -EPERM; - kc = setkeycode(tmp.scancode, tmp.keycode); - break; - } - return kc; -} - -static inline int -do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm) -{ - struct kbsentry *kbs; - char *p; - u_char *q; - u_char __user *up; - int sz; - int delta; - char *first_free, *fj, *fnw; - int i, j, k; - int ret; - - if (!capable(CAP_SYS_TTY_CONFIG)) - perm = 0; - - kbs = kmalloc(sizeof(*kbs), GFP_KERNEL); - if (!kbs) { - ret = -ENOMEM; - goto reterr; - } - - /* we mostly copy too much here (512bytes), but who cares ;) */ - if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) { - ret = -EFAULT; - goto reterr; - } - kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0'; - i = kbs->kb_func; - - switch (cmd) { - case KDGKBSENT: - sz = sizeof(kbs->kb_string) - 1; /* sz should have been - a struct member */ - up = user_kdgkb->kb_string; - p = func_table[i]; - if(p) - for ( ; *p && sz; p++, sz--) - if (put_user(*p, up++)) { - ret = -EFAULT; - goto reterr; - } - if (put_user('\0', up)) { - ret = -EFAULT; - goto reterr; - } - kfree(kbs); - return ((p && *p) ? -EOVERFLOW : 0); - case KDSKBSENT: - if (!perm) { - ret = -EPERM; - goto reterr; - } - - q = func_table[i]; - first_free = funcbufptr + (funcbufsize - funcbufleft); - for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) - ; - if (j < MAX_NR_FUNC) - fj = func_table[j]; - else - fj = first_free; - - delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string); - if (delta <= funcbufleft) { /* it fits in current buf */ - if (j < MAX_NR_FUNC) { - memmove(fj + delta, fj, first_free - fj); - for (k = j; k < MAX_NR_FUNC; k++) - if (func_table[k]) - func_table[k] += delta; - } - if (!q) - func_table[i] = fj; - funcbufleft -= delta; - } else { /* allocate a larger buffer */ - sz = 256; - while (sz < funcbufsize - funcbufleft + delta) - sz <<= 1; - fnw = kmalloc(sz, GFP_KERNEL); - if(!fnw) { - ret = -ENOMEM; - goto reterr; - } - - if (!q) - func_table[i] = fj; - if (fj > funcbufptr) - memmove(fnw, funcbufptr, fj - funcbufptr); - for (k = 0; k < j; k++) - if (func_table[k]) - func_table[k] = fnw + (func_table[k] - funcbufptr); - - if (first_free > fj) { - memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj); - for (k = j; k < MAX_NR_FUNC; k++) - if (func_table[k]) - func_table[k] = fnw + (func_table[k] - funcbufptr) + delta; - } - if (funcbufptr != func_buf) - kfree(funcbufptr); - funcbufptr = fnw; - funcbufleft = funcbufleft - delta + sz - funcbufsize; - funcbufsize = sz; - } - strcpy(func_table[i], kbs->kb_string); - break; - } - ret = 0; -reterr: - kfree(kbs); - return ret; -} - -static inline int -do_fontx_ioctl(int cmd, struct consolefontdesc __user *user_cfd, int perm, struct console_font_op *op) -{ - struct consolefontdesc cfdarg; - int i; - - if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc))) - return -EFAULT; - - switch (cmd) { - case PIO_FONTX: - if (!perm) - return -EPERM; - op->op = KD_FONT_OP_SET; - op->flags = KD_FONT_FLAG_OLD; - op->width = 8; - op->height = cfdarg.charheight; - op->charcount = cfdarg.charcount; - op->data = cfdarg.chardata; - return con_font_op(vc_cons[fg_console].d, op); - case GIO_FONTX: { - op->op = KD_FONT_OP_GET; - op->flags = KD_FONT_FLAG_OLD; - op->width = 8; - op->height = cfdarg.charheight; - op->charcount = cfdarg.charcount; - op->data = cfdarg.chardata; - i = con_font_op(vc_cons[fg_console].d, op); - if (i) - return i; - cfdarg.charheight = op->height; - cfdarg.charcount = op->charcount; - if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc))) - return -EFAULT; - return 0; - } - } - return -EINVAL; -} - -static inline int -do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_data *vc) -{ - struct unimapdesc tmp; - - if (copy_from_user(&tmp, user_ud, sizeof tmp)) - return -EFAULT; - if (tmp.entries) - if (!access_ok(VERIFY_WRITE, tmp.entries, - tmp.entry_ct*sizeof(struct unipair))) - return -EFAULT; - switch (cmd) { - case PIO_UNIMAP: - if (!perm) - return -EPERM; - return con_set_unimap(vc, tmp.entry_ct, tmp.entries); - case GIO_UNIMAP: - if (!perm && fg_console != vc->vc_num) - return -EPERM; - return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp.entries); - } - return 0; -} - - - -/* - * We handle the console-specific ioctl's here. We allow the - * capability to modify any console, not just the fg_console. - */ -int vt_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - struct vc_data *vc = tty->driver_data; - struct console_font_op op; /* used in multiple places here */ - struct kbd_struct * kbd; - unsigned int console; - unsigned char ucval; - unsigned int uival; - void __user *up = (void __user *)arg; - int i, perm; - int ret = 0; - - console = vc->vc_num; - - tty_lock(); - - if (!vc_cons_allocated(console)) { /* impossible? */ - ret = -ENOIOCTLCMD; - goto out; - } - - - /* - * To have permissions to do most of the vt ioctls, we either have - * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. - */ - perm = 0; - if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) - perm = 1; - - kbd = kbd_table + console; - switch (cmd) { - case TIOCLINUX: - ret = tioclinux(tty, arg); - break; - case KIOCSOUND: - if (!perm) - goto eperm; - /* - * The use of PIT_TICK_RATE is historic, it used to be - * the platform-dependent CLOCK_TICK_RATE between 2.6.12 - * and 2.6.36, which was a minor but unfortunate ABI - * change. - */ - if (arg) - arg = PIT_TICK_RATE / arg; - kd_mksound(arg, 0); - break; - - case KDMKTONE: - if (!perm) - goto eperm; - { - unsigned int ticks, count; - - /* - * Generate the tone for the appropriate number of ticks. - * If the time is zero, turn off sound ourselves. - */ - ticks = HZ * ((arg >> 16) & 0xffff) / 1000; - count = ticks ? (arg & 0xffff) : 0; - if (count) - count = PIT_TICK_RATE / count; - kd_mksound(count, ticks); - break; - } - - case KDGKBTYPE: - /* - * this is naive. - */ - ucval = KB_101; - goto setchar; - - /* - * These cannot be implemented on any machine that implements - * ioperm() in user level (such as Alpha PCs) or not at all. - * - * XXX: you should never use these, just call ioperm directly.. - */ -#ifdef CONFIG_X86 - case KDADDIO: - case KDDELIO: - /* - * KDADDIO and KDDELIO may be able to add ports beyond what - * we reject here, but to be safe... - */ - if (arg < GPFIRST || arg > GPLAST) { - ret = -EINVAL; - break; - } - ret = sys_ioperm(arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0; - break; - - case KDENABIO: - case KDDISABIO: - ret = sys_ioperm(GPFIRST, GPNUM, - (cmd == KDENABIO)) ? -ENXIO : 0; - break; -#endif - - /* Linux m68k/i386 interface for setting the keyboard delay/repeat rate */ - - case KDKBDREP: - { - struct kbd_repeat kbrep; - - if (!capable(CAP_SYS_TTY_CONFIG)) - goto eperm; - - if (copy_from_user(&kbrep, up, sizeof(struct kbd_repeat))) { - ret = -EFAULT; - break; - } - ret = kbd_rate(&kbrep); - if (ret) - break; - if (copy_to_user(up, &kbrep, sizeof(struct kbd_repeat))) - ret = -EFAULT; - break; - } - - case KDSETMODE: - /* - * currently, setting the mode from KD_TEXT to KD_GRAPHICS - * doesn't do a whole lot. i'm not sure if it should do any - * restoration of modes or what... - * - * XXX It should at least call into the driver, fbdev's definitely - * need to restore their engine state. --BenH - */ - if (!perm) - goto eperm; - switch (arg) { - case KD_GRAPHICS: - break; - case KD_TEXT0: - case KD_TEXT1: - arg = KD_TEXT; - case KD_TEXT: - break; - default: - ret = -EINVAL; - goto out; - } - if (vc->vc_mode == (unsigned char) arg) - break; - vc->vc_mode = (unsigned char) arg; - if (console != fg_console) - break; - /* - * explicitly blank/unblank the screen if switching modes - */ - acquire_console_sem(); - if (arg == KD_TEXT) - do_unblank_screen(1); - else - do_blank_screen(1); - release_console_sem(); - break; - - case KDGETMODE: - uival = vc->vc_mode; - goto setint; - - case KDMAPDISP: - case KDUNMAPDISP: - /* - * these work like a combination of mmap and KDENABIO. - * this could be easily finished. - */ - ret = -EINVAL; - break; - - case KDSKBMODE: - if (!perm) - goto eperm; - switch(arg) { - case K_RAW: - kbd->kbdmode = VC_RAW; - break; - case K_MEDIUMRAW: - kbd->kbdmode = VC_MEDIUMRAW; - break; - case K_XLATE: - kbd->kbdmode = VC_XLATE; - compute_shiftstate(); - break; - case K_UNICODE: - kbd->kbdmode = VC_UNICODE; - compute_shiftstate(); - break; - default: - ret = -EINVAL; - goto out; - } - tty_ldisc_flush(tty); - break; - - case KDGKBMODE: - uival = ((kbd->kbdmode == VC_RAW) ? K_RAW : - (kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW : - (kbd->kbdmode == VC_UNICODE) ? K_UNICODE : - K_XLATE); - goto setint; - - /* this could be folded into KDSKBMODE, but for compatibility - reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */ - case KDSKBMETA: - switch(arg) { - case K_METABIT: - clr_vc_kbd_mode(kbd, VC_META); - break; - case K_ESCPREFIX: - set_vc_kbd_mode(kbd, VC_META); - break; - default: - ret = -EINVAL; - } - break; - - case KDGKBMETA: - uival = (vc_kbd_mode(kbd, VC_META) ? K_ESCPREFIX : K_METABIT); - setint: - ret = put_user(uival, (int __user *)arg); - break; - - case KDGETKEYCODE: - case KDSETKEYCODE: - if(!capable(CAP_SYS_TTY_CONFIG)) - perm = 0; - ret = do_kbkeycode_ioctl(cmd, up, perm); - break; - - case KDGKBENT: - case KDSKBENT: - ret = do_kdsk_ioctl(cmd, up, perm, kbd); - break; - - case KDGKBSENT: - case KDSKBSENT: - ret = do_kdgkb_ioctl(cmd, up, perm); - break; - - case KDGKBDIACR: - { - struct kbdiacrs __user *a = up; - struct kbdiacr diacr; - int i; - - if (put_user(accent_table_size, &a->kb_cnt)) { - ret = -EFAULT; - break; - } - for (i = 0; i < accent_table_size; i++) { - diacr.diacr = conv_uni_to_8bit(accent_table[i].diacr); - diacr.base = conv_uni_to_8bit(accent_table[i].base); - diacr.result = conv_uni_to_8bit(accent_table[i].result); - if (copy_to_user(a->kbdiacr + i, &diacr, sizeof(struct kbdiacr))) { - ret = -EFAULT; - break; - } - } - break; - } - case KDGKBDIACRUC: - { - struct kbdiacrsuc __user *a = up; - - if (put_user(accent_table_size, &a->kb_cnt)) - ret = -EFAULT; - else if (copy_to_user(a->kbdiacruc, accent_table, - accent_table_size*sizeof(struct kbdiacruc))) - ret = -EFAULT; - break; - } - - case KDSKBDIACR: - { - struct kbdiacrs __user *a = up; - struct kbdiacr diacr; - unsigned int ct; - int i; - - if (!perm) - goto eperm; - if (get_user(ct,&a->kb_cnt)) { - ret = -EFAULT; - break; - } - if (ct >= MAX_DIACR) { - ret = -EINVAL; - break; - } - accent_table_size = ct; - for (i = 0; i < ct; i++) { - if (copy_from_user(&diacr, a->kbdiacr + i, sizeof(struct kbdiacr))) { - ret = -EFAULT; - break; - } - accent_table[i].diacr = conv_8bit_to_uni(diacr.diacr); - accent_table[i].base = conv_8bit_to_uni(diacr.base); - accent_table[i].result = conv_8bit_to_uni(diacr.result); - } - break; - } - - case KDSKBDIACRUC: - { - struct kbdiacrsuc __user *a = up; - unsigned int ct; - - if (!perm) - goto eperm; - if (get_user(ct,&a->kb_cnt)) { - ret = -EFAULT; - break; - } - if (ct >= MAX_DIACR) { - ret = -EINVAL; - break; - } - accent_table_size = ct; - if (copy_from_user(accent_table, a->kbdiacruc, ct*sizeof(struct kbdiacruc))) - ret = -EFAULT; - break; - } - - /* the ioctls below read/set the flags usually shown in the leds */ - /* don't use them - they will go away without warning */ - case KDGKBLED: - ucval = kbd->ledflagstate | (kbd->default_ledflagstate << 4); - goto setchar; - - case KDSKBLED: - if (!perm) - goto eperm; - if (arg & ~0x77) { - ret = -EINVAL; - break; - } - kbd->ledflagstate = (arg & 7); - kbd->default_ledflagstate = ((arg >> 4) & 7); - set_leds(); - break; - - /* the ioctls below only set the lights, not the functions */ - /* for those, see KDGKBLED and KDSKBLED above */ - case KDGETLED: - ucval = getledstate(); - setchar: - ret = put_user(ucval, (char __user *)arg); - break; - - case KDSETLED: - if (!perm) - goto eperm; - setledstate(kbd, arg); - break; - - /* - * A process can indicate its willingness to accept signals - * generated by pressing an appropriate key combination. - * Thus, one can have a daemon that e.g. spawns a new console - * upon a keypress and then changes to it. - * See also the kbrequest field of inittab(5). - */ - case KDSIGACCEPT: - { - if (!perm || !capable(CAP_KILL)) - goto eperm; - if (!valid_signal(arg) || arg < 1 || arg == SIGKILL) - ret = -EINVAL; - else { - spin_lock_irq(&vt_spawn_con.lock); - put_pid(vt_spawn_con.pid); - vt_spawn_con.pid = get_pid(task_pid(current)); - vt_spawn_con.sig = arg; - spin_unlock_irq(&vt_spawn_con.lock); - } - break; - } - - case VT_SETMODE: - { - struct vt_mode tmp; - - if (!perm) - goto eperm; - if (copy_from_user(&tmp, up, sizeof(struct vt_mode))) { - ret = -EFAULT; - goto out; - } - if (tmp.mode != VT_AUTO && tmp.mode != VT_PROCESS) { - ret = -EINVAL; - goto out; - } - acquire_console_sem(); - vc->vt_mode = tmp; - /* the frsig is ignored, so we set it to 0 */ - vc->vt_mode.frsig = 0; - put_pid(vc->vt_pid); - vc->vt_pid = get_pid(task_pid(current)); - /* no switch is required -- saw@shade.msu.ru */ - vc->vt_newvt = -1; - release_console_sem(); - break; - } - - case VT_GETMODE: - { - struct vt_mode tmp; - int rc; - - acquire_console_sem(); - memcpy(&tmp, &vc->vt_mode, sizeof(struct vt_mode)); - release_console_sem(); - - rc = copy_to_user(up, &tmp, sizeof(struct vt_mode)); - if (rc) - ret = -EFAULT; - break; - } - - /* - * Returns global vt state. Note that VT 0 is always open, since - * it's an alias for the current VT, and people can't use it here. - * We cannot return state for more than 16 VTs, since v_state is short. - */ - case VT_GETSTATE: - { - struct vt_stat __user *vtstat = up; - unsigned short state, mask; - - if (put_user(fg_console + 1, &vtstat->v_active)) - ret = -EFAULT; - else { - state = 1; /* /dev/tty0 is always open */ - for (i = 0, mask = 2; i < MAX_NR_CONSOLES && mask; - ++i, mask <<= 1) - if (VT_IS_IN_USE(i)) - state |= mask; - ret = put_user(state, &vtstat->v_state); - } - break; - } - - /* - * Returns the first available (non-opened) console. - */ - case VT_OPENQRY: - for (i = 0; i < MAX_NR_CONSOLES; ++i) - if (! VT_IS_IN_USE(i)) - break; - uival = i < MAX_NR_CONSOLES ? (i+1) : -1; - goto setint; - - /* - * ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num, - * with num >= 1 (switches to vt 0, our console, are not allowed, just - * to preserve sanity). - */ - case VT_ACTIVATE: - if (!perm) - goto eperm; - if (arg == 0 || arg > MAX_NR_CONSOLES) - ret = -ENXIO; - else { - arg--; - acquire_console_sem(); - ret = vc_allocate(arg); - release_console_sem(); - if (ret) - break; - set_console(arg); - } - break; - - case VT_SETACTIVATE: - { - struct vt_setactivate vsa; - - if (!perm) - goto eperm; - - if (copy_from_user(&vsa, (struct vt_setactivate __user *)arg, - sizeof(struct vt_setactivate))) { - ret = -EFAULT; - goto out; - } - if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES) - ret = -ENXIO; - else { - vsa.console--; - acquire_console_sem(); - ret = vc_allocate(vsa.console); - if (ret == 0) { - struct vc_data *nvc; - /* This is safe providing we don't drop the - console sem between vc_allocate and - finishing referencing nvc */ - nvc = vc_cons[vsa.console].d; - nvc->vt_mode = vsa.mode; - nvc->vt_mode.frsig = 0; - put_pid(nvc->vt_pid); - nvc->vt_pid = get_pid(task_pid(current)); - } - release_console_sem(); - if (ret) - break; - /* Commence switch and lock */ - set_console(arg); - } - } - - /* - * wait until the specified VT has been activated - */ - case VT_WAITACTIVE: - if (!perm) - goto eperm; - if (arg == 0 || arg > MAX_NR_CONSOLES) - ret = -ENXIO; - else - ret = vt_waitactive(arg); - break; - - /* - * If a vt is under process control, the kernel will not switch to it - * immediately, but postpone the operation until the process calls this - * ioctl, allowing the switch to complete. - * - * According to the X sources this is the behavior: - * 0: pending switch-from not OK - * 1: pending switch-from OK - * 2: completed switch-to OK - */ - case VT_RELDISP: - if (!perm) - goto eperm; - - if (vc->vt_mode.mode != VT_PROCESS) { - ret = -EINVAL; - break; - } - /* - * Switching-from response - */ - acquire_console_sem(); - if (vc->vt_newvt >= 0) { - if (arg == 0) - /* - * Switch disallowed, so forget we were trying - * to do it. - */ - vc->vt_newvt = -1; - - else { - /* - * The current vt has been released, so - * complete the switch. - */ - int newvt; - newvt = vc->vt_newvt; - vc->vt_newvt = -1; - ret = vc_allocate(newvt); - if (ret) { - release_console_sem(); - break; - } - /* - * When we actually do the console switch, - * make sure we are atomic with respect to - * other console switches.. - */ - complete_change_console(vc_cons[newvt].d); - } - } else { - /* - * Switched-to response - */ - /* - * If it's just an ACK, ignore it - */ - if (arg != VT_ACKACQ) - ret = -EINVAL; - } - release_console_sem(); - break; - - /* - * Disallocate memory associated to VT (but leave VT1) - */ - case VT_DISALLOCATE: - if (arg > MAX_NR_CONSOLES) { - ret = -ENXIO; - break; - } - if (arg == 0) { - /* deallocate all unused consoles, but leave 0 */ - acquire_console_sem(); - for (i=1; iv_rows) || - get_user(cc, &vtsizes->v_cols)) - ret = -EFAULT; - else { - acquire_console_sem(); - for (i = 0; i < MAX_NR_CONSOLES; i++) { - vc = vc_cons[i].d; - - if (vc) { - vc->vc_resize_user = 1; - vc_resize(vc_cons[i].d, cc, ll); - } - } - release_console_sem(); - } - break; - } - - case VT_RESIZEX: - { - struct vt_consize __user *vtconsize = up; - ushort ll,cc,vlin,clin,vcol,ccol; - if (!perm) - goto eperm; - if (!access_ok(VERIFY_READ, vtconsize, - sizeof(struct vt_consize))) { - ret = -EFAULT; - break; - } - /* FIXME: Should check the copies properly */ - __get_user(ll, &vtconsize->v_rows); - __get_user(cc, &vtconsize->v_cols); - __get_user(vlin, &vtconsize->v_vlin); - __get_user(clin, &vtconsize->v_clin); - __get_user(vcol, &vtconsize->v_vcol); - __get_user(ccol, &vtconsize->v_ccol); - vlin = vlin ? vlin : vc->vc_scan_lines; - if (clin) { - if (ll) { - if (ll != vlin/clin) { - /* Parameters don't add up */ - ret = -EINVAL; - break; - } - } else - ll = vlin/clin; - } - if (vcol && ccol) { - if (cc) { - if (cc != vcol/ccol) { - ret = -EINVAL; - break; - } - } else - cc = vcol/ccol; - } - - if (clin > 32) { - ret = -EINVAL; - break; - } - - for (i = 0; i < MAX_NR_CONSOLES; i++) { - if (!vc_cons[i].d) - continue; - acquire_console_sem(); - if (vlin) - vc_cons[i].d->vc_scan_lines = vlin; - if (clin) - vc_cons[i].d->vc_font.height = clin; - vc_cons[i].d->vc_resize_user = 1; - vc_resize(vc_cons[i].d, cc, ll); - release_console_sem(); - } - break; - } - - case PIO_FONT: { - if (!perm) - goto eperm; - op.op = KD_FONT_OP_SET; - op.flags = KD_FONT_FLAG_OLD | KD_FONT_FLAG_DONT_RECALC; /* Compatibility */ - op.width = 8; - op.height = 0; - op.charcount = 256; - op.data = up; - ret = con_font_op(vc_cons[fg_console].d, &op); - break; - } - - case GIO_FONT: { - op.op = KD_FONT_OP_GET; - op.flags = KD_FONT_FLAG_OLD; - op.width = 8; - op.height = 32; - op.charcount = 256; - op.data = up; - ret = con_font_op(vc_cons[fg_console].d, &op); - break; - } - - case PIO_CMAP: - if (!perm) - ret = -EPERM; - else - ret = con_set_cmap(up); - break; - - case GIO_CMAP: - ret = con_get_cmap(up); - break; - - case PIO_FONTX: - case GIO_FONTX: - ret = do_fontx_ioctl(cmd, up, perm, &op); - break; - - case PIO_FONTRESET: - { - if (!perm) - goto eperm; - -#ifdef BROKEN_GRAPHICS_PROGRAMS - /* With BROKEN_GRAPHICS_PROGRAMS defined, the default - font is not saved. */ - ret = -ENOSYS; - break; -#else - { - op.op = KD_FONT_OP_SET_DEFAULT; - op.data = NULL; - ret = con_font_op(vc_cons[fg_console].d, &op); - if (ret) - break; - con_set_default_unimap(vc_cons[fg_console].d); - break; - } -#endif - } - - case KDFONTOP: { - if (copy_from_user(&op, up, sizeof(op))) { - ret = -EFAULT; - break; - } - if (!perm && op.op != KD_FONT_OP_GET) - goto eperm; - ret = con_font_op(vc, &op); - if (ret) - break; - if (copy_to_user(up, &op, sizeof(op))) - ret = -EFAULT; - break; - } - - case PIO_SCRNMAP: - if (!perm) - ret = -EPERM; - else - ret = con_set_trans_old(up); - break; - - case GIO_SCRNMAP: - ret = con_get_trans_old(up); - break; - - case PIO_UNISCRNMAP: - if (!perm) - ret = -EPERM; - else - ret = con_set_trans_new(up); - break; - - case GIO_UNISCRNMAP: - ret = con_get_trans_new(up); - break; - - case PIO_UNIMAPCLR: - { struct unimapinit ui; - if (!perm) - goto eperm; - ret = copy_from_user(&ui, up, sizeof(struct unimapinit)); - if (ret) - ret = -EFAULT; - else - con_clear_unimap(vc, &ui); - break; - } - - case PIO_UNIMAP: - case GIO_UNIMAP: - ret = do_unimap_ioctl(cmd, up, perm, vc); - break; - - case VT_LOCKSWITCH: - if (!capable(CAP_SYS_TTY_CONFIG)) - goto eperm; - vt_dont_switch = 1; - break; - case VT_UNLOCKSWITCH: - if (!capable(CAP_SYS_TTY_CONFIG)) - goto eperm; - vt_dont_switch = 0; - break; - case VT_GETHIFONTMASK: - ret = put_user(vc->vc_hi_font_mask, - (unsigned short __user *)arg); - break; - case VT_WAITEVENT: - ret = vt_event_wait_ioctl((struct vt_event __user *)arg); - break; - default: - ret = -ENOIOCTLCMD; - } -out: - tty_unlock(); - return ret; -eperm: - ret = -EPERM; - goto out; -} - -void reset_vc(struct vc_data *vc) -{ - vc->vc_mode = KD_TEXT; - kbd_table[vc->vc_num].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; - vc->vt_mode.mode = VT_AUTO; - vc->vt_mode.waitv = 0; - vc->vt_mode.relsig = 0; - vc->vt_mode.acqsig = 0; - vc->vt_mode.frsig = 0; - put_pid(vc->vt_pid); - vc->vt_pid = NULL; - vc->vt_newvt = -1; - if (!in_interrupt()) /* Via keyboard.c:SAK() - akpm */ - reset_palette(vc); -} - -void vc_SAK(struct work_struct *work) -{ - struct vc *vc_con = - container_of(work, struct vc, SAK_work); - struct vc_data *vc; - struct tty_struct *tty; - - acquire_console_sem(); - vc = vc_con->d; - if (vc) { - tty = vc->port.tty; - /* - * SAK should also work in all raw modes and reset - * them properly. - */ - if (tty) - __do_SAK(tty); - reset_vc(vc); - } - release_console_sem(); -} - -#ifdef CONFIG_COMPAT - -struct compat_consolefontdesc { - unsigned short charcount; /* characters in font (256 or 512) */ - unsigned short charheight; /* scan lines per character (1-32) */ - compat_caddr_t chardata; /* font data in expanded form */ -}; - -static inline int -compat_fontx_ioctl(int cmd, struct compat_consolefontdesc __user *user_cfd, - int perm, struct console_font_op *op) -{ - struct compat_consolefontdesc cfdarg; - int i; - - if (copy_from_user(&cfdarg, user_cfd, sizeof(struct compat_consolefontdesc))) - return -EFAULT; - - switch (cmd) { - case PIO_FONTX: - if (!perm) - return -EPERM; - op->op = KD_FONT_OP_SET; - op->flags = KD_FONT_FLAG_OLD; - op->width = 8; - op->height = cfdarg.charheight; - op->charcount = cfdarg.charcount; - op->data = compat_ptr(cfdarg.chardata); - return con_font_op(vc_cons[fg_console].d, op); - case GIO_FONTX: - op->op = KD_FONT_OP_GET; - op->flags = KD_FONT_FLAG_OLD; - op->width = 8; - op->height = cfdarg.charheight; - op->charcount = cfdarg.charcount; - op->data = compat_ptr(cfdarg.chardata); - i = con_font_op(vc_cons[fg_console].d, op); - if (i) - return i; - cfdarg.charheight = op->height; - cfdarg.charcount = op->charcount; - if (copy_to_user(user_cfd, &cfdarg, sizeof(struct compat_consolefontdesc))) - return -EFAULT; - return 0; - } - return -EINVAL; -} - -struct compat_console_font_op { - compat_uint_t op; /* operation code KD_FONT_OP_* */ - compat_uint_t flags; /* KD_FONT_FLAG_* */ - compat_uint_t width, height; /* font size */ - compat_uint_t charcount; - compat_caddr_t data; /* font data with height fixed to 32 */ -}; - -static inline int -compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, - int perm, struct console_font_op *op, struct vc_data *vc) -{ - int i; - - if (copy_from_user(op, fontop, sizeof(struct compat_console_font_op))) - return -EFAULT; - if (!perm && op->op != KD_FONT_OP_GET) - return -EPERM; - op->data = compat_ptr(((struct compat_console_font_op *)op)->data); - op->flags |= KD_FONT_FLAG_OLD; - i = con_font_op(vc, op); - if (i) - return i; - ((struct compat_console_font_op *)op)->data = (unsigned long)op->data; - if (copy_to_user(fontop, op, sizeof(struct compat_console_font_op))) - return -EFAULT; - return 0; -} - -struct compat_unimapdesc { - unsigned short entry_ct; - compat_caddr_t entries; -}; - -static inline int -compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud, - int perm, struct vc_data *vc) -{ - struct compat_unimapdesc tmp; - struct unipair __user *tmp_entries; - - if (copy_from_user(&tmp, user_ud, sizeof tmp)) - return -EFAULT; - tmp_entries = compat_ptr(tmp.entries); - if (tmp_entries) - if (!access_ok(VERIFY_WRITE, tmp_entries, - tmp.entry_ct*sizeof(struct unipair))) - return -EFAULT; - switch (cmd) { - case PIO_UNIMAP: - if (!perm) - return -EPERM; - return con_set_unimap(vc, tmp.entry_ct, tmp_entries); - case GIO_UNIMAP: - if (!perm && fg_console != vc->vc_num) - return -EPERM; - return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp_entries); - } - return 0; -} - -long vt_compat_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - struct vc_data *vc = tty->driver_data; - struct console_font_op op; /* used in multiple places here */ - struct kbd_struct *kbd; - unsigned int console; - void __user *up = (void __user *)arg; - int perm; - int ret = 0; - - console = vc->vc_num; - - tty_lock(); - - if (!vc_cons_allocated(console)) { /* impossible? */ - ret = -ENOIOCTLCMD; - goto out; - } - - /* - * To have permissions to do most of the vt ioctls, we either have - * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. - */ - perm = 0; - if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) - perm = 1; - - kbd = kbd_table + console; - switch (cmd) { - /* - * these need special handlers for incompatible data structures - */ - case PIO_FONTX: - case GIO_FONTX: - ret = compat_fontx_ioctl(cmd, up, perm, &op); - break; - - case KDFONTOP: - ret = compat_kdfontop_ioctl(up, perm, &op, vc); - break; - - case PIO_UNIMAP: - case GIO_UNIMAP: - ret = compat_unimap_ioctl(cmd, up, perm, vc); - break; - - /* - * all these treat 'arg' as an integer - */ - case KIOCSOUND: - case KDMKTONE: -#ifdef CONFIG_X86 - case KDADDIO: - case KDDELIO: -#endif - case KDSETMODE: - case KDMAPDISP: - case KDUNMAPDISP: - case KDSKBMODE: - case KDSKBMETA: - case KDSKBLED: - case KDSETLED: - case KDSIGACCEPT: - case VT_ACTIVATE: - case VT_WAITACTIVE: - case VT_RELDISP: - case VT_DISALLOCATE: - case VT_RESIZE: - case VT_RESIZEX: - goto fallback; - - /* - * the rest has a compatible data structure behind arg, - * but we have to convert it to a proper 64 bit pointer. - */ - default: - arg = (unsigned long)compat_ptr(arg); - goto fallback; - } -out: - tty_unlock(); - return ret; - -fallback: - tty_unlock(); - return vt_ioctl(tty, file, cmd, arg); -} - - -#endif /* CONFIG_COMPAT */ - - -/* - * Performs the back end of a vt switch. Called under the console - * semaphore. - */ -static void complete_change_console(struct vc_data *vc) -{ - unsigned char old_vc_mode; - int old = fg_console; - - last_console = fg_console; - - /* - * If we're switching, we could be going from KD_GRAPHICS to - * KD_TEXT mode or vice versa, which means we need to blank or - * unblank the screen later. - */ - old_vc_mode = vc_cons[fg_console].d->vc_mode; - switch_screen(vc); - - /* - * This can't appear below a successful kill_pid(). If it did, - * then the *blank_screen operation could occur while X, having - * received acqsig, is waking up on another processor. This - * condition can lead to overlapping accesses to the VGA range - * and the framebuffer (causing system lockups). - * - * To account for this we duplicate this code below only if the - * controlling process is gone and we've called reset_vc. - */ - if (old_vc_mode != vc->vc_mode) { - if (vc->vc_mode == KD_TEXT) - do_unblank_screen(1); - else - do_blank_screen(1); - } - - /* - * If this new console is under process control, send it a signal - * telling it that it has acquired. Also check if it has died and - * clean up (similar to logic employed in change_console()) - */ - if (vc->vt_mode.mode == VT_PROCESS) { - /* - * Send the signal as privileged - kill_pid() will - * tell us if the process has gone or something else - * is awry - */ - if (kill_pid(vc->vt_pid, vc->vt_mode.acqsig, 1) != 0) { - /* - * The controlling process has died, so we revert back to - * normal operation. In this case, we'll also change back - * to KD_TEXT mode. I'm not sure if this is strictly correct - * but it saves the agony when the X server dies and the screen - * remains blanked due to KD_GRAPHICS! It would be nice to do - * this outside of VT_PROCESS but there is no single process - * to account for and tracking tty count may be undesirable. - */ - reset_vc(vc); - - if (old_vc_mode != vc->vc_mode) { - if (vc->vc_mode == KD_TEXT) - do_unblank_screen(1); - else - do_blank_screen(1); - } - } - } - - /* - * Wake anyone waiting for their VT to activate - */ - vt_event_post(VT_EVENT_SWITCH, old, vc->vc_num); - return; -} - -/* - * Performs the front-end of a vt switch - */ -void change_console(struct vc_data *new_vc) -{ - struct vc_data *vc; - - if (!new_vc || new_vc->vc_num == fg_console || vt_dont_switch) - return; - - /* - * If this vt is in process mode, then we need to handshake with - * that process before switching. Essentially, we store where that - * vt wants to switch to and wait for it to tell us when it's done - * (via VT_RELDISP ioctl). - * - * We also check to see if the controlling process still exists. - * If it doesn't, we reset this vt to auto mode and continue. - * This is a cheap way to track process control. The worst thing - * that can happen is: we send a signal to a process, it dies, and - * the switch gets "lost" waiting for a response; hopefully, the - * user will try again, we'll detect the process is gone (unless - * the user waits just the right amount of time :-) and revert the - * vt to auto control. - */ - vc = vc_cons[fg_console].d; - if (vc->vt_mode.mode == VT_PROCESS) { - /* - * Send the signal as privileged - kill_pid() will - * tell us if the process has gone or something else - * is awry. - * - * We need to set vt_newvt *before* sending the signal or we - * have a race. - */ - vc->vt_newvt = new_vc->vc_num; - if (kill_pid(vc->vt_pid, vc->vt_mode.relsig, 1) == 0) { - /* - * It worked. Mark the vt to switch to and - * return. The process needs to send us a - * VT_RELDISP ioctl to complete the switch. - */ - return; - } - - /* - * The controlling process has died, so we revert back to - * normal operation. In this case, we'll also change back - * to KD_TEXT mode. I'm not sure if this is strictly correct - * but it saves the agony when the X server dies and the screen - * remains blanked due to KD_GRAPHICS! It would be nice to do - * this outside of VT_PROCESS but there is no single process - * to account for and tracking tty count may be undesirable. - */ - reset_vc(vc); - - /* - * Fall through to normal (VT_AUTO) handling of the switch... - */ - } - - /* - * Ignore all switches in KD_GRAPHICS+VT_AUTO mode - */ - if (vc->vc_mode == KD_GRAPHICS) - return; - - complete_change_console(new_vc); -} - -/* Perform a kernel triggered VT switch for suspend/resume */ - -static int disable_vt_switch; - -int vt_move_to_console(unsigned int vt, int alloc) -{ - int prev; - - acquire_console_sem(); - /* Graphics mode - up to X */ - if (disable_vt_switch) { - release_console_sem(); - return 0; - } - prev = fg_console; - - if (alloc && vc_allocate(vt)) { - /* we can't have a free VC for now. Too bad, - * we don't want to mess the screen for now. */ - release_console_sem(); - return -ENOSPC; - } - - if (set_console(vt)) { - /* - * We're unable to switch to the SUSPEND_CONSOLE. - * Let the calling function know so it can decide - * what to do. - */ - release_console_sem(); - return -EIO; - } - release_console_sem(); - tty_lock(); - if (vt_waitactive(vt + 1)) { - pr_debug("Suspend: Can't switch VCs."); - tty_unlock(); - return -EINTR; - } - tty_unlock(); - return prev; -} - -/* - * Normally during a suspend, we allocate a new console and switch to it. - * When we resume, we switch back to the original console. This switch - * can be slow, so on systems where the framebuffer can handle restoration - * of video registers anyways, there's little point in doing the console - * switch. This function allows you to disable it by passing it '0'. - */ -void pm_set_vt_switch(int do_switch) -{ - acquire_console_sem(); - disable_vt_switch = !do_switch; - release_console_sem(); -} -EXPORT_SYMBOL(pm_set_vt_switch); -- cgit v1.2.1 From 1db01135df7aa8b456e093a781f1d7f7016ec01e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 5 Nov 2010 22:18:23 -0700 Subject: TTY: move .gitignore from drivers/char/ to drivers/tty/vt/ The autogenerated files (consolemap_deftbl.c and defkeymap.c) need to be ignored by git, so move the .gitignore file that was doing it to the properly location now that the files have moved as well. Cc: Arnd Bergmann Cc: Jiri Slaby Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/.gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 drivers/char/.gitignore (limited to 'drivers/char') diff --git a/drivers/char/.gitignore b/drivers/char/.gitignore deleted file mode 100644 index 83683a2d8e6a..000000000000 --- a/drivers/char/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -consolemap_deftbl.c -defkeymap.c -- cgit v1.2.1 From dc98d9650891661a20842a8eef9e76536046d897 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Tue, 9 Nov 2010 14:10:38 -0800 Subject: tty: fix warning in synclink driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During builds I see the following warning - CC [M] drivers/char/pcmcia/synclink_cs.o drivers/char/pcmcia/synclink_cs.c:2194: warning: ‘mgslpc_get_icount’ defined but not used The function is a callback meant to be assigned to get_icount (added during 0587102cf). Fix accordingly. Signed-off-by: Andres Salomon Acked-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index bfc10f89d951..eaa41992fbe2 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2796,6 +2796,7 @@ static const struct tty_operations mgslpc_ops = { .hangup = mgslpc_hangup, .tiocmget = tiocmget, .tiocmset = tiocmset, + .get_icount = mgslpc_get_icount, .proc_fops = &mgslpc_proc_fops, }; -- cgit v1.2.1 From 68e29655cc51761d60d5f27b2738816a5b13e415 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Tue, 26 Oct 2010 15:56:34 +0100 Subject: nozomi: Fix warning from the previous TIOCGCOUNT changes Just remove a now unused variable Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/nozomi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index dd3f9b1f11b4..294d03e8c61a 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -1828,7 +1828,6 @@ static int ntty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { struct port *port = tty->driver_data; - void __user *argp = (void __user *)arg; int rval = -ENOIOCTLCMD; DBG1("******** IOCTL, cmd: %d", cmd); -- cgit v1.2.1 From 20fffee818ec43b64f58ab25c42705b7dcae16e5 Mon Sep 17 00:00:00 2001 From: Nicolas Kaiser Date: Fri, 22 Oct 2010 18:10:24 +0200 Subject: pcmcia/cm4000: fix error code I'm assuming it's not intended to instantly change the error code from -ENODEV to -EIO, is it? Signed-off-by: Nicolas Kaiser Acked-by: Harald Welte Signed-off-by: Dominik Brodowski --- drivers/char/pcmcia/cm4000_cs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index d962f25dcc2a..777181a2e603 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -979,8 +979,9 @@ static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count, if (dev->flags0 & 1) { set_bit(IS_CMM_ABSENT, &dev->flags); rc = -ENODEV; + } else { + rc = -EIO; } - rc = -EIO; goto release_io; } -- cgit v1.2.1 From 86f94e3a1583765476ec06131c755af8318e6470 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Mon, 8 Nov 2010 15:58:01 -0800 Subject: pcmcia: fix warning in synclink driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During builds I see the following warning - CC [M] drivers/char/pcmcia/synclink_cs.o drivers/char/pcmcia/synclink_cs.c:2194: warning: ‘mgslpc_get_icount’ defined but not used The function is a callback meant to be assigned to get_icount (added during 0587102cf). Fix accordingly. Signed-off-by: Andres Salomon Signed-off-by: Dominik Brodowski --- drivers/char/pcmcia/synclink_cs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index bfc10f89d951..eaa41992fbe2 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2796,6 +2796,7 @@ static const struct tty_operations mgslpc_ops = { .hangup = mgslpc_hangup, .tiocmget = tiocmget, .tiocmset = tiocmset, + .get_icount = mgslpc_get_icount, .proc_fops = &mgslpc_proc_fops, }; -- cgit v1.2.1 From 37db8f91b7d9b064bc78acd1c615a410322e275d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 28 Oct 2010 20:29:53 +0200 Subject: amiserial: Remove unused variable icount drivers/char/amiserial.c: In function ?rs_ioctl?: drivers/char/amiserial.c:1302: warning: unused variable ?icount? commit 0587102cf9f427c185bfdeb2cef41e13ee0264b1 ("tty: icount changeover for other main devices") removed the users, but not the actual variable. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/char/amiserial.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index b0a70461a12c..c0bd6f472c52 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -1299,7 +1299,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, { struct async_struct * info = tty->driver_data; struct async_icount cprev, cnow; /* kernel counter temps */ - struct serial_icounter_struct icount; void __user *argp = (void __user *)arg; unsigned long flags; -- cgit v1.2.1 From 6b4e81db2552bad04100e7d5ddeed7e848f53b48 Mon Sep 17 00:00:00 2001 From: Jim Bos Date: Sat, 13 Nov 2010 12:13:53 +0100 Subject: i8k: Tell gcc that *regs gets clobbered More recent GCC caused the i8k driver to stop working, on Slackware compiler was upgraded from gcc-4.4.4 to gcc-4.5.1 after which it didn't work anymore, meaning the driver didn't load or gave total nonsensical output. As it turned out the asm(..) statement forgot to mention it modifies the *regs variable. Credits to Andi Kleen and Andreas Schwab for providing the fix. Signed-off-by: Jim Bos Cc: Andi Kleen Cc: Andreas Schwab Signed-off-by: Linus Torvalds --- drivers/char/i8k.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 3bc0eef88717..f0863bec186f 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -142,7 +142,7 @@ static int i8k_smm(struct smm_regs *regs) "lahf\n\t" "shrl $8,%%eax\n\t" "andl $1,%%eax\n" - :"=a"(rc) + :"=a"(rc), "+m" (*regs) : "a"(regs) : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); #else @@ -167,7 +167,8 @@ static int i8k_smm(struct smm_regs *regs) "movl %%edx,0(%%eax)\n\t" "lahf\n\t" "shrl $8,%%eax\n\t" - "andl $1,%%eax\n":"=a"(rc) + "andl $1,%%eax\n" + :"=a"(rc), "+m" (*regs) : "a"(regs) : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); #endif -- cgit v1.2.1