--- /dev/null
+What: /proc/<pid>/oom_adj
+When: August 2012
+Why: /proc/<pid>/oom_adj allows userspace to influence the oom killer's
+ badness heuristic used to determine which task to kill when the kernel
+ is out of memory.
+
+ The badness heuristic has since been rewritten since the introduction of
+ this tunable such that its meaning is deprecated. The value was
+ implemented as a bitshift on a score generated by the badness()
+ function that did not have any precise units of measure. With the
+ rewrite, the score is given as a proportion of available memory to the
+ task allocating pages, so using a bitshift which grows the score
+ exponentially is, thus, impossible to tune with fine granularity.
+
+ A much more powerful interface, /proc/<pid>/oom_score_adj, was
+ introduced with the oom killer rewrite that allows users to increase or
+ decrease the badness() score linearly. This interface will replace
+ /proc/<pid>/oom_adj.
+
+ A warning will be emitted to the kernel log if an application uses this
+ deprecated interface. After it is printed once, future warnings will be
+ suppressed until the kernel is rebooted.
Roadmap:
-2.6.37 Remove experimental tag from mount option
- => should be roughly 6 months after initial merge
- => enough time to:
- => gain confidence and fix problems reported by early
- adopters (a.k.a. guinea pigs)
- => address worst performance regressions and undesired
- behaviours
- => start tuning/optimising code for parallelism
- => start tuning/optimising algorithms consuming
- excessive CPU time
-
2.6.39 Switch default mount option to use delayed logging
=> should be roughly 12 months after initial merge
=> enough time to shake out remaining problems before next round of
Some LEDs can be programmed to blink without any CPU interaction. To
support this feature, a LED driver can optionally implement the
-blink_set() function (see <linux/leds.h>). If implemented, triggers can
-attempt to use it before falling back to software timers. The blink_set()
-function should return 0 if the blink setting is supported, or -EINVAL
-otherwise, which means that LED blinking will be handled by software.
-
-The blink_set() function should choose a user friendly blinking
-value if it is called with *delay_on==0 && *delay_off==0 parameters. In
-this case the driver should give back the chosen value through delay_on
-and delay_off parameters to the leds subsystem.
+blink_set() function (see <linux/leds.h>). To set an LED to blinking,
+however, it is better to use use the API function led_blink_set(),
+as it will check and implement software fallback if necessary.
+
+To turn off blinking again, use the API function led_brightness_set()
+as that will not just set the LED brightness but also stop any software
+timers that may have been required for blinking.
+
+The blink_set() function should choose a user friendly blinking value
+if it is called with *delay_on==0 && *delay_off==0 parameters. In this
+case the driver should give back the chosen value through delay_on and
+delay_off parameters to the leds subsystem.
Setting the brightness to zero with brightness_set() callback function
should completely turn off the LED and cancel the previously programmed
--- /dev/null
+Kernel driver for lp5521
+========================
+
+* National Semiconductor LP5521 led driver chip
+* Datasheet: http://www.national.com/pf/LP/LP5521.html
+
+Authors: Mathias Nyman, Yuri Zaporozhets, Samu Onkalo
+Contact: Samu Onkalo (samu.p.onkalo-at-nokia.com)
+
+Description
+-----------
+
+LP5521 can drive up to 3 channels. Leds can be controlled directly via
+the led class control interface. Channels have generic names:
+lp5521:channelx, where x is 0 .. 2
+
+All three channels can be also controlled using the engine micro programs.
+More details of the instructions can be found from the public data sheet.
+
+Control interface for the engines:
+x is 1 .. 3
+enginex_mode : disabled, load, run
+enginex_load : store program (visible only in engine load mode)
+
+Example (start to blink the channel 2 led):
+cd /sys/class/leds/lp5521:channel2/device
+echo "load" > engine3_mode
+echo "037f4d0003ff6000" > engine3_load
+echo "run" > engine3_mode
+
+stop the engine:
+echo "disabled" > engine3_mode
+
+sysfs contains a selftest entry.
+The test communicates with the chip and checks that
+the clock mode is automatically set to the requested one.
+
+Each channel has its own led current settings.
+/sys/class/leds/lp5521:channel0/led_current - RW
+/sys/class/leds/lp5521:channel0/max_current - RO
+Format: 10x mA i.e 10 means 1.0 mA
+
+example platform data:
+
+Note: chan_nr can have values between 0 and 2.
+
+static struct lp5521_led_config lp5521_led_config[] = {
+ {
+ .chan_nr = 0,
+ .led_current = 50,
+ .max_current = 130,
+ }, {
+ .chan_nr = 1,
+ .led_current = 0,
+ .max_current = 130,
+ }, {
+ .chan_nr = 2,
+ .led_current = 0,
+ .max_current = 130,
+ }
+};
+
+static int lp5521_setup(void)
+{
+ /* setup HW resources */
+}
+
+static void lp5521_release(void)
+{
+ /* Release HW resources */
+}
+
+static void lp5521_enable(bool state)
+{
+ /* Control of chip enable signal */
+}
+
+static struct lp5521_platform_data lp5521_platform_data = {
+ .led_config = lp5521_led_config,
+ .num_channels = ARRAY_SIZE(lp5521_led_config),
+ .clock_mode = LP5521_CLOCK_EXT,
+ .setup_resources = lp5521_setup,
+ .release_resources = lp5521_release,
+ .enable = lp5521_enable,
+};
+
+If the current is set to 0 in the platform data, that channel is
+disabled and it is not visible in the sysfs.
--- /dev/null
+Kernel driver for lp5523
+========================
+
+* National Semiconductor LP5523 led driver chip
+* Datasheet: http://www.national.com/pf/LP/LP5523.html
+
+Authors: Mathias Nyman, Yuri Zaporozhets, Samu Onkalo
+Contact: Samu Onkalo (samu.p.onkalo-at-nokia.com)
+
+Description
+-----------
+LP5523 can drive up to 9 channels. Leds can be controlled directly via
+the led class control interface. Channels have generic names:
+lp5523:channelx where x is 0...8
+
+The chip provides 3 engines. Each engine can control channels without
+interaction from the main CPU. Details of the micro engine code can be found
+from the public data sheet. Leds can be muxed to different channels.
+
+Control interface for the engines:
+x is 1 .. 3
+enginex_mode : disabled, load, run
+enginex_load : microcode load (visible only in load mode)
+enginex_leds : led mux control (visible only in load mode)
+
+cd /sys/class/leds/lp5523:channel2/device
+echo "load" > engine3_mode
+echo "9d80400004ff05ff437f0000" > engine3_load
+echo "111111111" > engine3_leds
+echo "run" > engine3_mode
+
+sysfs contains a selftest entry. It measures each channel
+voltage level and checks if it looks reasonable. If the level is too high,
+the led is missing; if the level is too low, there is a short circuit.
+
+Selftest uses always the current from the platform data.
+
+Each channel contains led current settings.
+/sys/class/leds/lp5523:channel2/led_current - RW
+/sys/class/leds/lp5523:channel2/max_current - RO
+Format: 10x mA i.e 10 means 1.0 mA
+
+Example platform data:
+
+Note - chan_nr can have values between 0 and 8.
+
+static struct lp5523_led_config lp5523_led_config[] = {
+ {
+ .chan_nr = 0,
+ .led_current = 50,
+ .max_current = 130,
+ },
+...
+ }, {
+ .chan_nr = 8,
+ .led_current = 50,
+ .max_current = 130,
+ }
+};
+
+static int lp5523_setup(void)
+{
+ /* Setup HW resources */
+}
+
+static void lp5523_release(void)
+{
+ /* Release HW resources */
+}
+
+static void lp5523_enable(bool state)
+{
+ /* Control chip enable signal */
+}
+
+static struct lp5523_platform_data lp5523_platform_data = {
+ .led_config = lp5523_led_config,
+ .num_channels = ARRAY_SIZE(lp5523_led_config),
+ .clock_mode = LP5523_CLOCK_EXT,
+ .setup_resources = lp5523_setup,
+ .release_resources = lp5523_release,
+ .enable = lp5523_enable,
+};
- core_uses_pid
- ctrl-alt-del
- dentry-state
+- dmesg_restrict
- domainname
- hostname
- hotplug
==============================================================
+dmesg_restrict:
+
+This toggle indicates whether unprivileged users are prevented from using
+dmesg(8) to view messages from the kernel's log buffer. When
+dmesg_restrict is set to (0) there are no restrictions. When
+dmesg_restrict is set set to (1), users must have CAP_SYS_ADMIN to use
+dmesg(8).
+
+The kernel config option CONFIG_SECURITY_DMESG_RESTRICT sets the default
+value of dmesg_restrict.
+
+==============================================================
+
domainname & hostname:
These files can be used to set the NIS/YP domainname and the
config SH_CLK_CPG
bool
+source "drivers/sh/Kconfig"
+
endif
/* FSI */
#define IRQ_FSI evt2irq(0x1840)
+
+static int fsi_set_rate(int is_porta, int rate)
+{
+ struct clk *fsib_clk;
+ struct clk *fdiv_clk = &sh7372_fsidivb_clk;
+ int ret;
+
+ /* set_rate is not needed if port A */
+ if (is_porta)
+ return 0;
+
+ fsib_clk = clk_get(NULL, "fsib_clk");
+ if (IS_ERR(fsib_clk))
+ return -EINVAL;
+
+ switch (rate) {
+ case 48000:
+ clk_set_rate(fsib_clk, clk_round_rate(fsib_clk, 85428000));
+ clk_set_rate(fdiv_clk, clk_round_rate(fdiv_clk, 12204000));
+ ret = SH_FSI_ACKMD_256 | SH_FSI_BPFMD_64;
+ break;
+ default:
+ pr_err("unsupported rate in FSI2 port B\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ clk_put(fsib_clk);
+
+ return ret;
+}
+
static struct sh_fsi_platform_info fsi_info = {
.porta_flags = SH_FSI_BRS_INV |
SH_FSI_OUT_SLAVE_MODE |
SH_FSI_IN_SLAVE_MODE |
SH_FSI_OFMT(PCM) |
SH_FSI_IFMT(PCM),
+
+ .portb_flags = SH_FSI_BRS_INV |
+ SH_FSI_BRM_INV |
+ SH_FSI_LRS_INV |
+ SH_FSI_OFMT(SPDIF),
+ .set_rate = fsi_set_rate,
};
static struct resource fsi_resources[] = {
static struct sh_mobile_hdmi_info hdmi_info = {
.lcd_chan = &sh_mobile_lcdc1_info.ch[0],
.lcd_dev = &lcdc1_device.dev,
+ .flags = HDMI_SND_SRC_SPDIF,
};
static struct resource hdmi_resources[] = {
#define GPIO_PORT9CR 0xE6051009
#define GPIO_PORT10CR 0xE605100A
+#define USCCR1 0xE6058144
static void __init ap4evb_init(void)
{
u32 srcr4;
/* setup USB phy */
__raw_writew(0x8a0a, 0xE6058130); /* USBCR2 */
- /* enable FSI2 */
+ /* enable FSI2 port A (ak4643) */
gpio_request(GPIO_FN_FSIAIBT, NULL);
gpio_request(GPIO_FN_FSIAILR, NULL);
gpio_request(GPIO_FN_FSIAISLD, NULL);
gpio_request(GPIO_PORT41, NULL);
gpio_direction_input(GPIO_PORT41);
+ /* setup FSI2 port B (HDMI) */
+ gpio_request(GPIO_FN_FSIBCK, NULL);
+ __raw_writew(__raw_readw(USCCR1) & ~(1 << 6), USCCR1); /* use SPDIF */
+
/* set SPU2 clock to 119.6 MHz */
clk = clk_get(NULL, "spu_clk");
if (!IS_ERR(clk)) {
#define SMSTPCR3 0xe615013c
#define SMSTPCR4 0xe6150140
+#define FSIDIVA 0xFE1F8000
+#define FSIDIVB 0xFE1F8008
+
/* Platforms must set frequency on their DV_CLKI pin */
struct clk sh7372_dv_clki_clk = {
};
.ops = &pllc2_clk_ops,
.parent = &extal1_div2_clk,
.freq_table = pllc2_freq_table,
+ .nr_freqs = ARRAY_SIZE(pllc2_freq_table) - 1,
.parent_table = pllc2_parent,
.parent_num = ARRAY_SIZE(pllc2_parent),
};
fsibckcr_parent, ARRAY_SIZE(fsibckcr_parent), 6, 2),
};
+/* FSI DIV */
+static unsigned long fsidiv_recalc(struct clk *clk)
+{
+ unsigned long value;
+
+ value = __raw_readl(clk->mapping->base);
+
+ if ((value & 0x3) != 0x3)
+ return 0;
+
+ value >>= 16;
+ if (value < 2)
+ return 0;
+
+ return clk->parent->rate / value;
+}
+
+static long fsidiv_round_rate(struct clk *clk, unsigned long rate)
+{
+ return clk_rate_div_range_round(clk, 2, 0xffff, rate);
+}
+
+static void fsidiv_disable(struct clk *clk)
+{
+ __raw_writel(0, clk->mapping->base);
+}
+
+static int fsidiv_enable(struct clk *clk)
+{
+ unsigned long value;
+
+ value = __raw_readl(clk->mapping->base) >> 16;
+ if (value < 2) {
+ fsidiv_disable(clk);
+ return -ENOENT;
+ }
+
+ __raw_writel((value << 16) | 0x3, clk->mapping->base);
+
+ return 0;
+}
+
+static int fsidiv_set_rate(struct clk *clk,
+ unsigned long rate, int algo_id)
+{
+ int idx;
+
+ if (clk->parent->rate == rate) {
+ fsidiv_disable(clk);
+ return 0;
+ }
+
+ idx = (clk->parent->rate / rate) & 0xffff;
+ if (idx < 2)
+ return -ENOENT;
+
+ __raw_writel(idx << 16, clk->mapping->base);
+ return fsidiv_enable(clk);
+}
+
+static struct clk_ops fsidiv_clk_ops = {
+ .recalc = fsidiv_recalc,
+ .round_rate = fsidiv_round_rate,
+ .set_rate = fsidiv_set_rate,
+ .enable = fsidiv_enable,
+ .disable = fsidiv_disable,
+};
+
+static struct clk_mapping sh7372_fsidiva_clk_mapping = {
+ .phys = FSIDIVA,
+ .len = 8,
+};
+
+struct clk sh7372_fsidiva_clk = {
+ .ops = &fsidiv_clk_ops,
+ .parent = &div6_reparent_clks[DIV6_FSIA], /* late install */
+ .mapping = &sh7372_fsidiva_clk_mapping,
+};
+
+static struct clk_mapping sh7372_fsidivb_clk_mapping = {
+ .phys = FSIDIVB,
+ .len = 8,
+};
+
+struct clk sh7372_fsidivb_clk = {
+ .ops = &fsidiv_clk_ops,
+ .parent = &div6_reparent_clks[DIV6_FSIB], /* late install */
+ .mapping = &sh7372_fsidivb_clk_mapping,
+};
+
+static struct clk *late_main_clks[] = {
+ &sh7372_fsidiva_clk,
+ &sh7372_fsidivb_clk,
+};
+
enum { MSTP001,
MSTP131, MSTP130,
MSTP129, MSTP128, MSTP127, MSTP126, MSTP125,
if (!ret)
ret = sh_clk_mstp32_register(mstp_clks, MSTP_NR);
+ for (k = 0; !ret && (k < ARRAY_SIZE(late_main_clks)); k++)
+ ret = clk_register(late_main_clks[k]);
+
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
if (!ret)
static inline int gpio_to_irq(unsigned gpio)
{
- return -ENOSYS;
+ return __gpio_to_irq(gpio);
}
static inline int irq_to_gpio(unsigned int irq)
{
- return -EINVAL;
+ return -ENOSYS;
}
#endif /* CONFIG_GPIOLIB */
extern struct clk sh7372_pllc2_clk;
extern struct clk sh7372_fsiack_clk;
extern struct clk sh7372_fsibck_clk;
+extern struct clk sh7372_fsidiva_clk;
+extern struct clk sh7372_fsidivb_clk;
#endif /* __ASM_SH7372_H__ */
config CPU_SH2A
bool
select CPU_SH2
+ select UNCACHED_MAPPING
config CPU_SH3
bool
machdir-$(CONFIG_SH_HP6XX) += mach-hp6xx
machdir-$(CONFIG_SH_DREAMCAST) += mach-dreamcast
machdir-$(CONFIG_SH_SH03) += mach-sh03
-machdir-$(CONFIG_SH_SECUREEDGE5410) += mach-snapgear
machdir-$(CONFIG_SH_RTS7751R2D) += mach-r2d
-machdir-$(CONFIG_SH_7751_SYSTEMH) += mach-systemh
-machdir-$(CONFIG_SH_EDOSK7705) += mach-edosk7705
machdir-$(CONFIG_SH_HIGHLANDER) += mach-highlander
machdir-$(CONFIG_SH_MIGOR) += mach-migor
machdir-$(CONFIG_SH_AP325RXA) += mach-ap325rxa
Select 7343 SolutionEngine if configuring for a Hitachi
SH7343 (SH-Mobile 3AS) evaluation board.
-config SH_7751_SYSTEMH
- bool "SystemH7751R"
- depends on CPU_SUBTYPE_SH7751R
- help
- Select SystemH if you are configuring for a Renesas SystemH
- 7751R evaluation board.
-
config SH_HP6XX
bool "HP6XX"
select SYS_SUPPORTS_APM_EMULATION
# Specific board support, not covered by a mach group.
#
obj-$(CONFIG_SH_MAGIC_PANEL_R2) += board-magicpanelr2.o
+obj-$(CONFIG_SH_SECUREEDGE5410) += board-secureedge5410.o
obj-$(CONFIG_SH_SH2007) += board-sh2007.o
obj-$(CONFIG_SH_SH7785LCR) += board-sh7785lcr.o
obj-$(CONFIG_SH_URQUELL) += board-urquell.o
obj-$(CONFIG_SH_SHMIN) += board-shmin.o
+obj-$(CONFIG_SH_EDOSK7705) += board-edosk7705.o
obj-$(CONFIG_SH_EDOSK7760) += board-edosk7760.o
obj-$(CONFIG_SH_ESPT) += board-espt.o
obj-$(CONFIG_SH_POLARIS) += board-polaris.o
--- /dev/null
+/*
+ * arch/sh/boards/renesas/edosk7705/setup.c
+ *
+ * Copyright (C) 2000 Kazumoto Kojima
+ *
+ * Hitachi SolutionEngine Support.
+ *
+ * Modified for edosk7705 development
+ * board by S. Dunn, 2003.
+ */
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/smc91x.h>
+#include <asm/machvec.h>
+#include <asm/sizes.h>
+
+#define SMC_IOBASE 0xA2000000
+#define SMC_IO_OFFSET 0x300
+#define SMC_IOADDR (SMC_IOBASE + SMC_IO_OFFSET)
+
+#define ETHERNET_IRQ 0x09
+
+static void __init sh_edosk7705_init_irq(void)
+{
+ make_imask_irq(ETHERNET_IRQ);
+}
+
+/* eth initialization functions */
+static struct smc91x_platdata smc91x_info = {
+ .flags = SMC91X_USE_16BIT | SMC91X_IO_SHIFT_1 | IORESOURCE_IRQ_LOWLEVEL,
+};
+
+static struct resource smc91x_res[] = {
+ [0] = {
+ .start = SMC_IOADDR,
+ .end = SMC_IOADDR + SZ_32 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = ETHERNET_IRQ,
+ .end = ETHERNET_IRQ,
+ .flags = IORESOURCE_IRQ ,
+ }
+};
+
+static struct platform_device smc91x_dev = {
+ .name = "smc91x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(smc91x_res),
+ .resource = smc91x_res,
+
+ .dev = {
+ .platform_data = &smc91x_info,
+ },
+};
+
+/* platform init code */
+static struct platform_device *edosk7705_devices[] __initdata = {
+ &smc91x_dev,
+};
+
+static int __init init_edosk7705_devices(void)
+{
+ return platform_add_devices(edosk7705_devices,
+ ARRAY_SIZE(edosk7705_devices));
+}
+__initcall(init_edosk7705_devices);
+
+/*
+ * The Machine Vector
+ */
+static struct sh_machine_vector mv_edosk7705 __initmv = {
+ .mv_name = "EDOSK7705",
+ .mv_nr_irqs = 80,
+ .mv_init_irq = sh_edosk7705_init_irq,
+};
--- /dev/null
+/*
+ * Copyright (C) 2002 David McCullough <davidm@snapgear.com>
+ * Copyright (C) 2003 Paul Mundt <lethal@linux-sh.org>
+ *
+ * Based on files with the following comments:
+ *
+ * Copyright (C) 2000 Kazumoto Kojima
+ *
+ * Modified for 7751 Solution Engine by
+ * Ian da Silva and Jeremy Siegel, 2001.
+ */
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <asm/machvec.h>
+#include <mach/secureedge5410.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <cpu/timer.h>
+
+unsigned short secureedge5410_ioport;
+
+/*
+ * EraseConfig handling functions
+ */
+static irqreturn_t eraseconfig_interrupt(int irq, void *dev_id)
+{
+ ctrl_delay(); /* dummy read */
+
+ printk("SnapGear: erase switch interrupt!\n");
+
+ return IRQ_HANDLED;
+}
+
+static int __init eraseconfig_init(void)
+{
+ unsigned int irq = evt2irq(0x240);
+
+ printk("SnapGear: EraseConfig init\n");
+
+ /* Setup "EraseConfig" switch on external IRQ 0 */
+ if (request_irq(irq, eraseconfig_interrupt, IRQF_DISABLED,
+ "Erase Config", NULL))
+ printk("SnapGear: failed to register IRQ%d for Reset witch\n",
+ irq);
+ else
+ printk("SnapGear: registered EraseConfig switch on IRQ%d\n",
+ irq);
+ return 0;
+}
+module_init(eraseconfig_init);
+
+/*
+ * Initialize IRQ setting
+ *
+ * IRL0 = erase switch
+ * IRL1 = eth0
+ * IRL2 = eth1
+ * IRL3 = crypto
+ */
+static void __init init_snapgear_IRQ(void)
+{
+ printk("Setup SnapGear IRQ/IPR ...\n");
+ /* enable individual interrupt mode for externals */
+ plat_irq_setup_pins(IRQ_MODE_IRQ);
+}
+
+/*
+ * The Machine Vector
+ */
+static struct sh_machine_vector mv_snapgear __initmv = {
+ .mv_name = "SnapGear SecureEdge5410",
+ .mv_nr_irqs = 72,
+ .mv_init_irq = init_snapgear_IRQ,
+};
+++ /dev/null
-#
-# Makefile for the EDOSK7705 specific parts of the kernel
-#
-
-obj-y := setup.o io.o
+++ /dev/null
-/*
- * arch/sh/boards/renesas/edosk7705/io.c
- *
- * Copyright (C) 2001 Ian da Silva, Jeremy Siegel
- * Based largely on io_se.c.
- *
- * I/O routines for Hitachi EDOSK7705 board.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/io.h>
-#include <mach/edosk7705.h>
-#include <asm/addrspace.h>
-
-#define SMC_IOADDR 0xA2000000
-
-/* Map the Ethernet addresses as if it is at 0x300 - 0x320 */
-static unsigned long sh_edosk7705_isa_port2addr(unsigned long port)
-{
- /*
- * SMC91C96 registers are 4 byte aligned rather than the
- * usual 2 byte!
- */
- if (port >= 0x300 && port < 0x320)
- return SMC_IOADDR + ((port - 0x300) * 2);
-
- maybebadio(port);
- return port;
-}
-
-/* Trying to read / write bytes on odd-byte boundaries to the Ethernet
- * registers causes problems. So we bit-shift the value and read / write
- * in 2 byte chunks. Setting the low byte to 0 does not cause problems
- * now as odd byte writes are only made on the bit mask / interrupt
- * register. This may not be the case in future Mar-2003 SJD
- */
-unsigned char sh_edosk7705_inb(unsigned long port)
-{
- if (port >= 0x300 && port < 0x320 && port & 0x01)
- return __raw_readw(port - 1) >> 8;
-
- return __raw_readb(sh_edosk7705_isa_port2addr(port));
-}
-
-void sh_edosk7705_outb(unsigned char value, unsigned long port)
-{
- if (port >= 0x300 && port < 0x320 && port & 0x01) {
- __raw_writew(((unsigned short)value << 8), port - 1);
- return;
- }
-
- __raw_writeb(value, sh_edosk7705_isa_port2addr(port));
-}
-
-void sh_edosk7705_insb(unsigned long port, void *addr, unsigned long count)
-{
- unsigned char *p = addr;
-
- while (count--)
- *p++ = sh_edosk7705_inb(port);
-}
-
-void sh_edosk7705_outsb(unsigned long port, const void *addr, unsigned long count)
-{
- unsigned char *p = (unsigned char *)addr;
-
- while (count--)
- sh_edosk7705_outb(*p++, port);
-}
+++ /dev/null
-/*
- * arch/sh/boards/renesas/edosk7705/setup.c
- *
- * Copyright (C) 2000 Kazumoto Kojima
- *
- * Hitachi SolutionEngine Support.
- *
- * Modified for edosk7705 development
- * board by S. Dunn, 2003.
- */
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <asm/machvec.h>
-#include <mach/edosk7705.h>
-
-static void __init sh_edosk7705_init_irq(void)
-{
- /* This is the Ethernet interrupt */
- make_imask_irq(0x09);
-}
-
-/*
- * The Machine Vector
- */
-static struct sh_machine_vector mv_edosk7705 __initmv = {
- .mv_name = "EDOSK7705",
- .mv_nr_irqs = 80,
-
- .mv_inb = sh_edosk7705_inb,
- .mv_outb = sh_edosk7705_outb,
-
- .mv_insb = sh_edosk7705_insb,
- .mv_outsb = sh_edosk7705_outsb,
-
- .mv_init_irq = sh_edosk7705_init_irq,
-};
/*
* map I/O ports to memory-mapped addresses
*/
-static unsigned long microdev_isa_port2addr(unsigned long offset)
+void __iomem *microdev_ioport_map(unsigned long offset, unsigned int len)
{
unsigned long result;
* Configuration Registers
*/
result = IO_SUPERIO_PHYS + (offset << 1);
-#if 0
- } else if (offset == KBD_DATA_REG || offset == KBD_CNTL_REG ||
- offset == KBD_STATUS_REG) {
- /*
- * SMSC FDC37C93xAPM SuperIO chip
- *
- * PS/2 Keyboard + Mouse (ports 0x60 and 0x64).
- */
- result = IO_SUPERIO_PHYS + (offset << 1);
-#endif
} else if (((offset >= IO_IDE1_BASE) &&
(offset < IO_IDE1_BASE + IO_IDE_EXTENT)) ||
(offset == IO_IDE1_MISC)) {
result = PVR;
}
- return result;
-}
-
-#define PORT2ADDR(x) (microdev_isa_port2addr(x))
-
-static inline void delay(void)
-{
-#if defined(CONFIG_PCI)
- /* System board present, just make a dummy SRAM access. (CS0 will be
- mapped to PCI memory, probably good to avoid it.) */
- __raw_readw(0xa6800000);
-#else
- /* CS0 will be mapped to flash, ROM etc so safe to access it. */
- __raw_readw(0xa0000000);
-#endif
-}
-
-unsigned char microdev_inb(unsigned long port)
-{
-#ifdef CONFIG_PCI
- if (port >= PCIBIOS_MIN_IO)
- return microdev_pci_inb(port);
-#endif
- return *(volatile unsigned char*)PORT2ADDR(port);
-}
-
-unsigned short microdev_inw(unsigned long port)
-{
-#ifdef CONFIG_PCI
- if (port >= PCIBIOS_MIN_IO)
- return microdev_pci_inw(port);
-#endif
- return *(volatile unsigned short*)PORT2ADDR(port);
-}
-
-unsigned int microdev_inl(unsigned long port)
-{
-#ifdef CONFIG_PCI
- if (port >= PCIBIOS_MIN_IO)
- return microdev_pci_inl(port);
-#endif
- return *(volatile unsigned int*)PORT2ADDR(port);
-}
-
-void microdev_outw(unsigned short b, unsigned long port)
-{
-#ifdef CONFIG_PCI
- if (port >= PCIBIOS_MIN_IO) {
- microdev_pci_outw(b, port);
- return;
- }
-#endif
- *(volatile unsigned short*)PORT2ADDR(port) = b;
-}
-
-void microdev_outb(unsigned char b, unsigned long port)
-{
-#ifdef CONFIG_PCI
- if (port >= PCIBIOS_MIN_IO) {
- microdev_pci_outb(b, port);
- return;
- }
-#endif
-
- /*
- * There is a board feature with the current SH4-202 MicroDev in
- * that the 2 byte enables (nBE0 and nBE1) are tied together (and
- * to the Chip Select Line (Ethernet_CS)). Due to this connectivity,
- * it is not possible to safely perform 8-bit writes to the
- * Ethernet registers, as 16-bits will be consumed from the Data
- * lines (corrupting the other byte). Hence, this function is
- * written to implement 16-bit read/modify/write for all byte-wide
- * accesses.
- *
- * Note: there is no problem with byte READS (even or odd).
- *
- * Sean McGoogan - 16th June 2003.
- */
- if ((port >= IO_LAN91C111_BASE) &&
- (port < IO_LAN91C111_BASE + IO_LAN91C111_EXTENT)) {
- /*
- * Then are trying to perform a byte-write to the
- * LAN91C111. This needs special care.
- */
- if (port % 2 == 1) { /* is the port odd ? */
- /* unset bit-0, i.e. make even */
- const unsigned long evenPort = port-1;
- unsigned short word;
-
- /*
- * do a 16-bit read/write to write to 'port',
- * preserving even byte.
- *
- * Even addresses are bits 0-7
- * Odd addresses are bits 8-15
- */
- word = microdev_inw(evenPort);
- word = (word & 0xffu) | (b << 8);
- microdev_outw(word, evenPort);
- } else {
- /* else, we are trying to do an even byte write */
- unsigned short word;
-
- /*
- * do a 16-bit read/write to write to 'port',
- * preserving odd byte.
- *
- * Even addresses are bits 0-7
- * Odd addresses are bits 8-15
- */
- word = microdev_inw(port);
- word = (word & 0xff00u) | (b);
- microdev_outw(word, port);
- }
- } else {
- *(volatile unsigned char*)PORT2ADDR(port) = b;
- }
-}
-
-void microdev_outl(unsigned int b, unsigned long port)
-{
-#ifdef CONFIG_PCI
- if (port >= PCIBIOS_MIN_IO) {
- microdev_pci_outl(b, port);
- return;
- }
-#endif
- *(volatile unsigned int*)PORT2ADDR(port) = b;
-}
-
-unsigned char microdev_inb_p(unsigned long port)
-{
- unsigned char v = microdev_inb(port);
- delay();
- return v;
-}
-
-unsigned short microdev_inw_p(unsigned long port)
-{
- unsigned short v = microdev_inw(port);
- delay();
- return v;
-}
-
-unsigned int microdev_inl_p(unsigned long port)
-{
- unsigned int v = microdev_inl(port);
- delay();
- return v;
-}
-
-void microdev_outb_p(unsigned char b, unsigned long port)
-{
- microdev_outb(b, port);
- delay();
-}
-
-void microdev_outw_p(unsigned short b, unsigned long port)
-{
- microdev_outw(b, port);
- delay();
-}
-
-void microdev_outl_p(unsigned int b, unsigned long port)
-{
- microdev_outl(b, port);
- delay();
-}
-
-void microdev_insb(unsigned long port, void *buffer, unsigned long count)
-{
- volatile unsigned char *port_addr;
- unsigned char *buf = buffer;
-
- port_addr = (volatile unsigned char *)PORT2ADDR(port);
-
- while (count--)
- *buf++ = *port_addr;
-}
-
-void microdev_insw(unsigned long port, void *buffer, unsigned long count)
-{
- volatile unsigned short *port_addr;
- unsigned short *buf = buffer;
-
- port_addr = (volatile unsigned short *)PORT2ADDR(port);
-
- while (count--)
- *buf++ = *port_addr;
-}
-
-void microdev_insl(unsigned long port, void *buffer, unsigned long count)
-{
- volatile unsigned long *port_addr;
- unsigned int *buf = buffer;
-
- port_addr = (volatile unsigned long *)PORT2ADDR(port);
-
- while (count--)
- *buf++ = *port_addr;
-}
-
-void microdev_outsb(unsigned long port, const void *buffer, unsigned long count)
-{
- volatile unsigned char *port_addr;
- const unsigned char *buf = buffer;
-
- port_addr = (volatile unsigned char *)PORT2ADDR(port);
-
- while (count--)
- *port_addr = *buf++;
-}
-
-void microdev_outsw(unsigned long port, const void *buffer, unsigned long count)
-{
- volatile unsigned short *port_addr;
- const unsigned short *buf = buffer;
-
- port_addr = (volatile unsigned short *)PORT2ADDR(port);
-
- while (count--)
- *port_addr = *buf++;
-}
-
-void microdev_outsl(unsigned long port, const void *buffer, unsigned long count)
-{
- volatile unsigned long *port_addr;
- const unsigned int *buf = buffer;
-
- port_addr = (volatile unsigned long *)PORT2ADDR(port);
-
- while (count--)
- *port_addr = *buf++;
+ return (void __iomem *)result;
}
static struct sh_machine_vector mv_sh4202_microdev __initmv = {
.mv_name = "SH4-202 MicroDev",
.mv_nr_irqs = 72,
-
- .mv_inb = microdev_inb,
- .mv_inw = microdev_inw,
- .mv_inl = microdev_inl,
- .mv_outb = microdev_outb,
- .mv_outw = microdev_outw,
- .mv_outl = microdev_outl,
-
- .mv_inb_p = microdev_inb_p,
- .mv_inw_p = microdev_inw_p,
- .mv_inl_p = microdev_inl_p,
- .mv_outb_p = microdev_outb_p,
- .mv_outw_p = microdev_outw_p,
- .mv_outl_p = microdev_outl_p,
-
- .mv_insb = microdev_insb,
- .mv_insw = microdev_insw,
- .mv_insl = microdev_insl,
- .mv_outsb = microdev_outsb,
- .mv_outsw = microdev_outsw,
- .mv_outsl = microdev_outsl,
-
+ .mv_ioport_map = microdev_ioport_map,
.mv_init_irq = init_microdev_irq,
};
# Makefile for the 7206 SolutionEngine specific parts of the kernel
#
-obj-y := setup.o io.o irq.o
+obj-y := setup.o irq.o
+++ /dev/null
-/* $Id: io.c,v 1.5 2004/02/22 23:08:43 kkojima Exp $
- *
- * linux/arch/sh/boards/se/7206/io.c
- *
- * Copyright (C) 2006 Yoshinori Sato
- *
- * I/O routine for Hitachi 7206 SolutionEngine.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <asm/io.h>
-#include <mach-se/mach/se7206.h>
-
-
-static inline void delay(void)
-{
- __raw_readw(0x20000000); /* P2 ROM Area */
-}
-
-/* MS7750 requires special versions of in*, out* routines, since
- PC-like io ports are located at upper half byte of 16-bit word which
- can be accessed only with 16-bit wide. */
-
-static inline volatile __u16 *
-port2adr(unsigned int port)
-{
- if (port >= 0x2000 && port < 0x2020)
- return (volatile __u16 *) (PA_MRSHPC + (port - 0x2000));
- else if (port >= 0x300 && port < 0x310)
- return (volatile __u16 *) (PA_SMSC + (port - 0x300));
-
- return (volatile __u16 *)port;
-}
-
-unsigned char se7206_inb(unsigned long port)
-{
- return (*port2adr(port)) & 0xff;
-}
-
-unsigned char se7206_inb_p(unsigned long port)
-{
- unsigned long v;
-
- v = (*port2adr(port)) & 0xff;
- delay();
- return v;
-}
-
-unsigned short se7206_inw(unsigned long port)
-{
- return *port2adr(port);
-}
-
-void se7206_outb(unsigned char value, unsigned long port)
-{
- *(port2adr(port)) = value;
-}
-
-void se7206_outb_p(unsigned char value, unsigned long port)
-{
- *(port2adr(port)) = value;
- delay();
-}
-
-void se7206_outw(unsigned short value, unsigned long port)
-{
- *port2adr(port) = value;
-}
-
-void se7206_insb(unsigned long port, void *addr, unsigned long count)
-{
- volatile __u16 *p = port2adr(port);
- __u8 *ap = addr;
-
- while (count--)
- *ap++ = *p;
-}
-
-void se7206_insw(unsigned long port, void *addr, unsigned long count)
-{
- volatile __u16 *p = port2adr(port);
- __u16 *ap = addr;
- while (count--)
- *ap++ = *p;
-}
-
-void se7206_outsb(unsigned long port, const void *addr, unsigned long count)
-{
- volatile __u16 *p = port2adr(port);
- const __u8 *ap = addr;
-
- while (count--)
- *p = *ap++;
-}
-
-void se7206_outsw(unsigned long port, const void *addr, unsigned long count)
-{
- volatile __u16 *p = port2adr(port);
- const __u16 *ap = addr;
- while (count--)
- *p = *ap++;
-}
make_se7206_irq(IRQ0_IRQ); /* SMC91C111 */
make_se7206_irq(IRQ1_IRQ); /* ATA */
make_se7206_irq(IRQ3_IRQ); /* SLOT / PCM */
- __raw_writew(inw(INTC_ICR1) | 0x000b ,INTC_ICR1 ) ; /* ICR1 */
+
+ __raw_writew(__raw_readw(INTC_ICR1) | 0x000b, INTC_ICR); /* ICR1 */
/* FPGA System register setup*/
__raw_writew(0x0000,INTSTS0); /* Clear INTSTS0 */
__raw_writew(0x0000,INTSTS1); /* Clear INTSTS1 */
+
/* IRQ0=LAN, IRQ1=ATA, IRQ3=SLT,PCM */
__raw_writew(0x0001,INTSEL);
}
static struct sh_machine_vector mv_se __initmv = {
.mv_name = "SolutionEngine",
.mv_nr_irqs = 256,
- .mv_inb = se7206_inb,
- .mv_inw = se7206_inw,
- .mv_outb = se7206_outb,
- .mv_outw = se7206_outw,
-
- .mv_inb_p = se7206_inb_p,
- .mv_inw_p = se7206_inw,
- .mv_outb_p = se7206_outb_p,
- .mv_outw_p = se7206_outw,
-
- .mv_insb = se7206_insb,
- .mv_insw = se7206_insw,
- .mv_outsb = se7206_outsb,
- .mv_outsw = se7206_outsw,
-
.mv_init_irq = init_se7206_IRQ,
};
# Makefile for the 770x SolutionEngine specific parts of the kernel
#
-obj-y := setup.o io.o irq.o
+obj-y := setup.o irq.o
+++ /dev/null
-/*
- * Copyright (C) 2000 Kazumoto Kojima
- *
- * I/O routine for Hitachi SolutionEngine.
- */
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <asm/io.h>
-#include <mach-se/mach/se.h>
-
-/* MS7750 requires special versions of in*, out* routines, since
- PC-like io ports are located at upper half byte of 16-bit word which
- can be accessed only with 16-bit wide. */
-
-static inline volatile __u16 *
-port2adr(unsigned int port)
-{
- if (port & 0xff000000)
- return ( volatile __u16 *) port;
- if (port >= 0x2000)
- return (volatile __u16 *) (PA_MRSHPC + (port - 0x2000));
- else if (port >= 0x1000)
- return (volatile __u16 *) (PA_83902 + (port << 1));
- else
- return (volatile __u16 *) (PA_SUPERIO + (port << 1));
-}
-
-static inline int
-shifted_port(unsigned long port)
-{
- /* For IDE registers, value is not shifted */
- if ((0x1f0 <= port && port < 0x1f8) || port == 0x3f6)
- return 0;
- else
- return 1;
-}
-
-unsigned char se_inb(unsigned long port)
-{
- if (shifted_port(port))
- return (*port2adr(port) >> 8);
- else
- return (*port2adr(port))&0xff;
-}
-
-unsigned char se_inb_p(unsigned long port)
-{
- unsigned long v;
-
- if (shifted_port(port))
- v = (*port2adr(port) >> 8);
- else
- v = (*port2adr(port))&0xff;
- ctrl_delay();
- return v;
-}
-
-unsigned short se_inw(unsigned long port)
-{
- if (port >= 0x2000)
- return *port2adr(port);
- else
- maybebadio(port);
- return 0;
-}
-
-unsigned int se_inl(unsigned long port)
-{
- maybebadio(port);
- return 0;
-}
-
-void se_outb(unsigned char value, unsigned long port)
-{
- if (shifted_port(port))
- *(port2adr(port)) = value << 8;
- else
- *(port2adr(port)) = value;
-}
-
-void se_outb_p(unsigned char value, unsigned long port)
-{
- if (shifted_port(port))
- *(port2adr(port)) = value << 8;
- else
- *(port2adr(port)) = value;
- ctrl_delay();
-}
-
-void se_outw(unsigned short value, unsigned long port)
-{
- if (port >= 0x2000)
- *port2adr(port) = value;
- else
- maybebadio(port);
-}
-
-void se_outl(unsigned int value, unsigned long port)
-{
- maybebadio(port);
-}
-
-void se_insb(unsigned long port, void *addr, unsigned long count)
-{
- volatile __u16 *p = port2adr(port);
- __u8 *ap = addr;
-
- if (shifted_port(port)) {
- while (count--)
- *ap++ = *p >> 8;
- } else {
- while (count--)
- *ap++ = *p;
- }
-}
-
-void se_insw(unsigned long port, void *addr, unsigned long count)
-{
- volatile __u16 *p = port2adr(port);
- __u16 *ap = addr;
- while (count--)
- *ap++ = *p;
-}
-
-void se_insl(unsigned long port, void *addr, unsigned long count)
-{
- maybebadio(port);
-}
-
-void se_outsb(unsigned long port, const void *addr, unsigned long count)
-{
- volatile __u16 *p = port2adr(port);
- const __u8 *ap = addr;
-
- if (shifted_port(port)) {
- while (count--)
- *p = *ap++ << 8;
- } else {
- while (count--)
- *p = *ap++;
- }
-}
-
-void se_outsw(unsigned long port, const void *addr, unsigned long count)
-{
- volatile __u16 *p = port2adr(port);
- const __u16 *ap = addr;
-
- while (count--)
- *p = *ap++;
-}
-
-void se_outsl(unsigned long port, const void *addr, unsigned long count)
-{
- maybebadio(port);
-}
#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712)
.mv_nr_irqs = 104,
#endif
-
- .mv_inb = se_inb,
- .mv_inw = se_inw,
- .mv_inl = se_inl,
- .mv_outb = se_outb,
- .mv_outw = se_outw,
- .mv_outl = se_outl,
-
- .mv_inb_p = se_inb_p,
- .mv_inw_p = se_inw,
- .mv_inl_p = se_inl,
- .mv_outb_p = se_outb_p,
- .mv_outw_p = se_outw,
- .mv_outl_p = se_outl,
-
- .mv_insb = se_insb,
- .mv_insw = se_insw,
- .mv_insl = se_insl,
- .mv_outsb = se_outsb,
- .mv_outsw = se_outsw,
- .mv_outsl = se_outsl,
-
.mv_init_irq = init_se_IRQ,
};
# Makefile for the 7751 SolutionEngine specific parts of the kernel
#
-obj-y := setup.o io.o irq.o
+obj-y := setup.o irq.o
+++ /dev/null
-/*
- * Copyright (C) 2001 Ian da Silva, Jeremy Siegel
- * Based largely on io_se.c.
- *
- * I/O routine for Hitachi 7751 SolutionEngine.
- *
- * Initial version only to support LAN access; some
- * placeholder code from io_se.c left in with the
- * expectation of later SuperIO and PCMCIA access.
- */
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <asm/io.h>
-#include <mach-se/mach/se7751.h>
-#include <asm/addrspace.h>
-
-static inline volatile u16 *port2adr(unsigned int port)
-{
- if (port >= 0x2000)
- return (volatile __u16 *) (PA_MRSHPC + (port - 0x2000));
- maybebadio((unsigned long)port);
- return (volatile __u16*)port;
-}
-
-/*
- * General outline: remap really low stuff [eventually] to SuperIO,
- * stuff in PCI IO space (at or above window at pci.h:PCIBIOS_MIN_IO)
- * is mapped through the PCI IO window. Stuff with high bits (PXSEG)
- * should be way beyond the window, and is used w/o translation for
- * compatibility.
- */
-unsigned char sh7751se_inb(unsigned long port)
-{
- if (PXSEG(port))
- return *(volatile unsigned char *)port;
- else
- return (*port2adr(port)) & 0xff;
-}
-
-unsigned char sh7751se_inb_p(unsigned long port)
-{
- unsigned char v;
-
- if (PXSEG(port))
- v = *(volatile unsigned char *)port;
- else
- v = (*port2adr(port)) & 0xff;
- ctrl_delay();
- return v;
-}
-
-unsigned short sh7751se_inw(unsigned long port)
-{
- if (PXSEG(port))
- return *(volatile unsigned short *)port;
- else if (port >= 0x2000)
- return *port2adr(port);
- else
- maybebadio(port);
- return 0;
-}
-
-unsigned int sh7751se_inl(unsigned long port)
-{
- if (PXSEG(port))
- return *(volatile unsigned long *)port;
- else if (port >= 0x2000)
- return *port2adr(port);
- else
- maybebadio(port);
- return 0;
-}
-
-void sh7751se_outb(unsigned char value, unsigned long port)
-{
-
- if (PXSEG(port))
- *(volatile unsigned char *)port = value;
- else
- *(port2adr(port)) = value;
-}
-
-void sh7751se_outb_p(unsigned char value, unsigned long port)
-{
- if (PXSEG(port))
- *(volatile unsigned char *)port = value;
- else
- *(port2adr(port)) = value;
- ctrl_delay();
-}
-
-void sh7751se_outw(unsigned short value, unsigned long port)
-{
- if (PXSEG(port))
- *(volatile unsigned short *)port = value;
- else if (port >= 0x2000)
- *port2adr(port) = value;
- else
- maybebadio(port);
-}
-
-void sh7751se_outl(unsigned int value, unsigned long port)
-{
- if (PXSEG(port))
- *(volatile unsigned long *)port = value;
- else
- maybebadio(port);
-}
-
-void sh7751se_insl(unsigned long port, void *addr, unsigned long count)
-{
- maybebadio(port);
-}
-
-void sh7751se_outsl(unsigned long port, const void *addr, unsigned long count)
-{
- maybebadio(port);
-}
static struct sh_machine_vector mv_7751se __initmv = {
.mv_name = "7751 SolutionEngine",
.mv_nr_irqs = 72,
-
- .mv_inb = sh7751se_inb,
- .mv_inw = sh7751se_inw,
- .mv_inl = sh7751se_inl,
- .mv_outb = sh7751se_outb,
- .mv_outw = sh7751se_outw,
- .mv_outl = sh7751se_outl,
-
- .mv_inb_p = sh7751se_inb_p,
- .mv_inw_p = sh7751se_inw,
- .mv_inl_p = sh7751se_inl,
- .mv_outb_p = sh7751se_outb_p,
- .mv_outw_p = sh7751se_outw,
- .mv_outl_p = sh7751se_outl,
-
- .mv_insl = sh7751se_insl,
- .mv_outsl = sh7751se_outsl,
-
.mv_init_irq = init_7751se_IRQ,
};
+++ /dev/null
-#
-# Makefile for the SnapGear specific parts of the kernel
-#
-
-obj-y := setup.o io.o
+++ /dev/null
-/*
- * Copyright (C) 2002 David McCullough <davidm@snapgear.com>
- * Copyright (C) 2001 Ian da Silva, Jeremy Siegel
- * Based largely on io_se.c.
- *
- * I/O routine for Hitachi 7751 SolutionEngine.
- *
- * Initial version only to support LAN access; some
- * placeholder code from io_se.c left in with the
- * expectation of later SuperIO and PCMCIA access.
- */
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <asm/io.h>
-#include <asm/addrspace.h>
-
-#ifdef CONFIG_SH_SECUREEDGE5410
-unsigned short secureedge5410_ioport;
-#endif
-
-static inline volatile __u16 *port2adr(unsigned int port)
-{
- maybebadio((unsigned long)port);
- return (volatile __u16*)port;
-}
-
-/*
- * General outline: remap really low stuff [eventually] to SuperIO,
- * stuff in PCI IO space (at or above window at pci.h:PCIBIOS_MIN_IO)
- * is mapped through the PCI IO window. Stuff with high bits (PXSEG)
- * should be way beyond the window, and is used w/o translation for
- * compatibility.
- */
-unsigned char snapgear_inb(unsigned long port)
-{
- if (PXSEG(port))
- return *(volatile unsigned char *)port;
- else
- return (*port2adr(port)) & 0xff;
-}
-
-unsigned char snapgear_inb_p(unsigned long port)
-{
- unsigned char v;
-
- if (PXSEG(port))
- v = *(volatile unsigned char *)port;
- else
- v = (*port2adr(port))&0xff;
- ctrl_delay();
- return v;
-}
-
-unsigned short snapgear_inw(unsigned long port)
-{
- if (PXSEG(port))
- return *(volatile unsigned short *)port;
- else if (port >= 0x2000)
- return *port2adr(port);
- else
- maybebadio(port);
- return 0;
-}
-
-unsigned int snapgear_inl(unsigned long port)
-{
- if (PXSEG(port))
- return *(volatile unsigned long *)port;
- else if (port >= 0x2000)
- return *port2adr(port);
- else
- maybebadio(port);
- return 0;
-}
-
-void snapgear_outb(unsigned char value, unsigned long port)
-{
-
- if (PXSEG(port))
- *(volatile unsigned char *)port = value;
- else
- *(port2adr(port)) = value;
-}
-
-void snapgear_outb_p(unsigned char value, unsigned long port)
-{
- if (PXSEG(port))
- *(volatile unsigned char *)port = value;
- else
- *(port2adr(port)) = value;
- ctrl_delay();
-}
-
-void snapgear_outw(unsigned short value, unsigned long port)
-{
- if (PXSEG(port))
- *(volatile unsigned short *)port = value;
- else if (port >= 0x2000)
- *port2adr(port) = value;
- else
- maybebadio(port);
-}
-
-void snapgear_outl(unsigned int value, unsigned long port)
-{
- if (PXSEG(port))
- *(volatile unsigned long *)port = value;
- else
- maybebadio(port);
-}
-
-void snapgear_insl(unsigned long port, void *addr, unsigned long count)
-{
- maybebadio(port);
-}
-
-void snapgear_outsl(unsigned long port, const void *addr, unsigned long count)
-{
- maybebadio(port);
-}
+++ /dev/null
-/*
- * linux/arch/sh/boards/snapgear/setup.c
- *
- * Copyright (C) 2002 David McCullough <davidm@snapgear.com>
- * Copyright (C) 2003 Paul Mundt <lethal@linux-sh.org>
- *
- * Based on files with the following comments:
- *
- * Copyright (C) 2000 Kazumoto Kojima
- *
- * Modified for 7751 Solution Engine by
- * Ian da Silva and Jeremy Siegel, 2001.
- */
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <asm/machvec.h>
-#include <mach/snapgear.h>
-#include <asm/irq.h>
-#include <asm/io.h>
-#include <cpu/timer.h>
-
-/*
- * EraseConfig handling functions
- */
-
-static irqreturn_t eraseconfig_interrupt(int irq, void *dev_id)
-{
- (void)__raw_readb(0xb8000000); /* dummy read */
-
- printk("SnapGear: erase switch interrupt!\n");
-
- return IRQ_HANDLED;
-}
-
-static int __init eraseconfig_init(void)
-{
- printk("SnapGear: EraseConfig init\n");
- /* Setup "EraseConfig" switch on external IRQ 0 */
- if (request_irq(IRL0_IRQ, eraseconfig_interrupt, IRQF_DISABLED,
- "Erase Config", NULL))
- printk("SnapGear: failed to register IRQ%d for Reset witch\n",
- IRL0_IRQ);
- else
- printk("SnapGear: registered EraseConfig switch on IRQ%d\n",
- IRL0_IRQ);
- return(0);
-}
-
-module_init(eraseconfig_init);
-
-/****************************************************************************/
-/*
- * Initialize IRQ setting
- *
- * IRL0 = erase switch
- * IRL1 = eth0
- * IRL2 = eth1
- * IRL3 = crypto
- */
-
-static void __init init_snapgear_IRQ(void)
-{
- printk("Setup SnapGear IRQ/IPR ...\n");
- /* enable individual interrupt mode for externals */
- plat_irq_setup_pins(IRQ_MODE_IRQ);
-}
-
-/*
- * The Machine Vector
- */
-static struct sh_machine_vector mv_snapgear __initmv = {
- .mv_name = "SnapGear SecureEdge5410",
- .mv_nr_irqs = 72,
-
- .mv_inb = snapgear_inb,
- .mv_inw = snapgear_inw,
- .mv_inl = snapgear_inl,
- .mv_outb = snapgear_outb,
- .mv_outw = snapgear_outw,
- .mv_outl = snapgear_outl,
-
- .mv_inb_p = snapgear_inb_p,
- .mv_inw_p = snapgear_inw,
- .mv_inl_p = snapgear_inl,
- .mv_outb_p = snapgear_outb_p,
- .mv_outw_p = snapgear_outw,
- .mv_outl_p = snapgear_outl,
-
- .mv_init_irq = init_snapgear_IRQ,
-};
+++ /dev/null
-#
-# Makefile for the SystemH specific parts of the kernel
-#
-
-obj-y := setup.o irq.o io.o
-
-# XXX: This wants to be consolidated in arch/sh/drivers/pci, and more
-# importantly, with the generic sh7751_pcic_init() code. For now, we'll
-# just abuse the hell out of kbuild, because we can..
-
-obj-$(CONFIG_PCI) += pci.o
-pci-y := ../../se/7751/pci.o
-
+++ /dev/null
-/*
- * linux/arch/sh/boards/renesas/systemh/io.c
- *
- * Copyright (C) 2001 Ian da Silva, Jeremy Siegel
- * Based largely on io_se.c.
- *
- * I/O routine for Hitachi 7751 Systemh.
- */
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <mach/systemh7751.h>
-#include <asm/addrspace.h>
-#include <asm/io.h>
-
-#define ETHER_IOMAP(adr) (0xB3000000 + (adr)) /*map to 16bits access area
- of smc lan chip*/
-static inline volatile __u16 *
-port2adr(unsigned int port)
-{
- if (port >= 0x2000)
- return (volatile __u16 *) (PA_MRSHPC + (port - 0x2000));
- maybebadio((unsigned long)port);
- return (volatile __u16*)port;
-}
-
-/*
- * General outline: remap really low stuff [eventually] to SuperIO,
- * stuff in PCI IO space (at or above window at pci.h:PCIBIOS_MIN_IO)
- * is mapped through the PCI IO window. Stuff with high bits (PXSEG)
- * should be way beyond the window, and is used w/o translation for
- * compatibility.
- */
-unsigned char sh7751systemh_inb(unsigned long port)
-{
- if (PXSEG(port))
- return *(volatile unsigned char *)port;
- else if (port <= 0x3F1)
- return *(volatile unsigned char *)ETHER_IOMAP(port);
- else
- return (*port2adr(port))&0xff;
-}
-
-unsigned char sh7751systemh_inb_p(unsigned long port)
-{
- unsigned char v;
-
- if (PXSEG(port))
- v = *(volatile unsigned char *)port;
- else if (port <= 0x3F1)
- v = *(volatile unsigned char *)ETHER_IOMAP(port);
- else
- v = (*port2adr(port))&0xff;
- ctrl_delay();
- return v;
-}
-
-unsigned short sh7751systemh_inw(unsigned long port)
-{
- if (PXSEG(port))
- return *(volatile unsigned short *)port;
- else if (port >= 0x2000)
- return *port2adr(port);
- else if (port <= 0x3F1)
- return *(volatile unsigned int *)ETHER_IOMAP(port);
- else
- maybebadio(port);
- return 0;
-}
-
-unsigned int sh7751systemh_inl(unsigned long port)
-{
- if (PXSEG(port))
- return *(volatile unsigned long *)port;
- else if (port >= 0x2000)
- return *port2adr(port);
- else if (port <= 0x3F1)
- return *(volatile unsigned int *)ETHER_IOMAP(port);
- else
- maybebadio(port);
- return 0;
-}
-
-void sh7751systemh_outb(unsigned char value, unsigned long port)
-{
-
- if (PXSEG(port))
- *(volatile unsigned char *)port = value;
- else if (port <= 0x3F1)
- *(volatile unsigned char *)ETHER_IOMAP(port) = value;
- else
- *(port2adr(port)) = value;
-}
-
-void sh7751systemh_outb_p(unsigned char value, unsigned long port)
-{
- if (PXSEG(port))
- *(volatile unsigned char *)port = value;
- else if (port <= 0x3F1)
- *(volatile unsigned char *)ETHER_IOMAP(port) = value;
- else
- *(port2adr(port)) = value;
- ctrl_delay();
-}
-
-void sh7751systemh_outw(unsigned short value, unsigned long port)
-{
- if (PXSEG(port))
- *(volatile unsigned short *)port = value;
- else if (port >= 0x2000)
- *port2adr(port) = value;
- else if (port <= 0x3F1)
- *(volatile unsigned short *)ETHER_IOMAP(port) = value;
- else
- maybebadio(port);
-}
-
-void sh7751systemh_outl(unsigned int value, unsigned long port)
-{
- if (PXSEG(port))
- *(volatile unsigned long *)port = value;
- else
- maybebadio(port);
-}
-
-void sh7751systemh_insb(unsigned long port, void *addr, unsigned long count)
-{
- unsigned char *p = addr;
- while (count--) *p++ = sh7751systemh_inb(port);
-}
-
-void sh7751systemh_insw(unsigned long port, void *addr, unsigned long count)
-{
- unsigned short *p = addr;
- while (count--) *p++ = sh7751systemh_inw(port);
-}
-
-void sh7751systemh_insl(unsigned long port, void *addr, unsigned long count)
-{
- maybebadio(port);
-}
-
-void sh7751systemh_outsb(unsigned long port, const void *addr, unsigned long count)
-{
- unsigned char *p = (unsigned char*)addr;
- while (count--) sh7751systemh_outb(*p++, port);
-}
-
-void sh7751systemh_outsw(unsigned long port, const void *addr, unsigned long count)
-{
- unsigned short *p = (unsigned short*)addr;
- while (count--) sh7751systemh_outw(*p++, port);
-}
-
-void sh7751systemh_outsl(unsigned long port, const void *addr, unsigned long count)
-{
- maybebadio(port);
-}
+++ /dev/null
-/*
- * linux/arch/sh/boards/renesas/systemh/irq.c
- *
- * Copyright (C) 2000 Kazumoto Kojima
- *
- * Hitachi SystemH Support.
- *
- * Modified for 7751 SystemH by
- * Jonathan Short.
- */
-
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-
-#include <mach/systemh7751.h>
-#include <asm/smc37c93x.h>
-
-/* address of external interrupt mask register
- * address must be set prior to use these (maybe in init_XXX_irq())
- * XXX : is it better to use .config than specifying it in code? */
-static unsigned long *systemh_irq_mask_register = (unsigned long *)0xB3F10004;
-static unsigned long *systemh_irq_request_register = (unsigned long *)0xB3F10000;
-
-static void disable_systemh_irq(struct irq_data *data)
-{
- unsigned long val, mask = 0x01 << 1;
-
- /* Clear the "irq"th bit in the mask and set it in the request */
- val = __raw_readl((unsigned long)systemh_irq_mask_register);
- val &= ~mask;
- __raw_writel(val, (unsigned long)systemh_irq_mask_register);
-
- val = __raw_readl((unsigned long)systemh_irq_request_register);
- val |= mask;
- __raw_writel(val, (unsigned long)systemh_irq_request_register);
-}
-
-static void enable_systemh_irq(struct irq_data *data)
-{
- unsigned long val, mask = 0x01 << 1;
-
- /* Set "irq"th bit in the mask register */
- val = __raw_readl((unsigned long)systemh_irq_mask_register);
- val |= mask;
- __raw_writel(val, (unsigned long)systemh_irq_mask_register);
-}
-
-static struct irq_chip systemh_irq_type = {
- .name = "SystemH Register",
- .irq_unmask = enable_systemh_irq,
- .irq_mask = disable_systemh_irq,
-};
-
-void make_systemh_irq(unsigned int irq)
-{
- disable_irq_nosync(irq);
- set_irq_chip_and_handler(irq, &systemh_irq_type, handle_level_irq);
- disable_systemh_irq(irq_get_irq_data(irq));
-}
+++ /dev/null
-/*
- * linux/arch/sh/boards/renesas/systemh/setup.c
- *
- * Copyright (C) 2000 Kazumoto Kojima
- * Copyright (C) 2003 Paul Mundt
- *
- * Hitachi SystemH Support.
- *
- * Modified for 7751 SystemH by Jonathan Short.
- *
- * Rewritten for 2.6 by Paul Mundt.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#include <linux/init.h>
-#include <asm/machvec.h>
-#include <mach/systemh7751.h>
-
-extern void make_systemh_irq(unsigned int irq);
-
-/*
- * Initialize IRQ setting
- */
-static void __init sh7751systemh_init_irq(void)
-{
- make_systemh_irq(0xb); /* Ethernet interrupt */
-}
-
-static struct sh_machine_vector mv_7751systemh __initmv = {
- .mv_name = "7751 SystemH",
- .mv_nr_irqs = 72,
-
- .mv_inb = sh7751systemh_inb,
- .mv_inw = sh7751systemh_inw,
- .mv_inl = sh7751systemh_inl,
- .mv_outb = sh7751systemh_outb,
- .mv_outw = sh7751systemh_outw,
- .mv_outl = sh7751systemh_outl,
-
- .mv_inb_p = sh7751systemh_inb_p,
- .mv_inw_p = sh7751systemh_inw,
- .mv_inl_p = sh7751systemh_inl,
- .mv_outb_p = sh7751systemh_outb_p,
- .mv_outw_p = sh7751systemh_outw,
- .mv_outl_p = sh7751systemh_outl,
-
- .mv_insb = sh7751systemh_insb,
- .mv_insw = sh7751systemh_insw,
- .mv_insl = sh7751systemh_insl,
- .mv_outsb = sh7751systemh_outsb,
- .mv_outsw = sh7751systemh_outsw,
- .mv_outsl = sh7751systemh_outsl,
-
- .mv_init_irq = sh7751systemh_init_irq,
-};
--- /dev/null
+CONFIG_EXPERIMENTAL=y
+# CONFIG_SWAP is not set
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_HOTPLUG is not set
+CONFIG_SLAB=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_CPU_SUBTYPE_SH7751R=y
+CONFIG_MEMORY_SIZE=0x01000000
+CONFIG_FLATMEM_MANUAL=y
+CONFIG_SH_SECUREEDGE5410=y
+CONFIG_SH_DMA=y
+CONFIG_SH_DMA_API=y
+CONFIG_PCI=y
+CONFIG_NET=y
+CONFIG_INET=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK_RO=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_GEOMETRY=y
+# CONFIG_MTD_MAP_BANK_WIDTH_2 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set
+# CONFIG_MTD_CFI_I2 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_PLATRAM=y
+CONFIG_BLK_DEV_RAM=y
+# CONFIG_MISC_DEVICES is not set
+CONFIG_NETDEVICES=y
+CONFIG_NET_ETHERNET=y
+CONFIG_NET_PCI=y
+CONFIG_8139CP=y
+CONFIG_8139TOO=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_DS1302=y
+CONFIG_EXT2_FS=y
+# CONFIG_DNOTIFY is not set
+CONFIG_TMPFS=y
+CONFIG_CRAMFS=y
+CONFIG_ROMFS_FS=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+++ /dev/null
-CONFIG_EXPERIMENTAL=y
-# CONFIG_SWAP is not set
-CONFIG_LOG_BUF_SHIFT=14
-CONFIG_BLK_DEV_INITRD=y
-# CONFIG_SYSCTL_SYSCALL is not set
-# CONFIG_HOTPLUG is not set
-CONFIG_SLAB=y
-# CONFIG_BLK_DEV_BSG is not set
-CONFIG_CPU_SUBTYPE_SH7751R=y
-CONFIG_MEMORY_SIZE=0x01000000
-CONFIG_FLATMEM_MANUAL=y
-CONFIG_SH_SECUREEDGE5410=y
-CONFIG_SH_DMA=y
-CONFIG_SH_DMA_API=y
-CONFIG_PCI=y
-CONFIG_NET=y
-CONFIG_INET=y
-# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
-# CONFIG_INET_XFRM_MODE_TUNNEL is not set
-# CONFIG_INET_XFRM_MODE_BEET is not set
-# CONFIG_INET_LRO is not set
-# CONFIG_INET_DIAG is not set
-# CONFIG_IPV6 is not set
-CONFIG_MTD=y
-CONFIG_MTD_PARTITIONS=y
-CONFIG_MTD_CHAR=y
-CONFIG_MTD_BLOCK_RO=y
-CONFIG_MTD_CFI=y
-CONFIG_MTD_CFI_ADV_OPTIONS=y
-CONFIG_MTD_CFI_GEOMETRY=y
-# CONFIG_MTD_MAP_BANK_WIDTH_2 is not set
-# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set
-# CONFIG_MTD_CFI_I2 is not set
-CONFIG_MTD_CFI_INTELEXT=y
-CONFIG_MTD_PLATRAM=y
-CONFIG_BLK_DEV_RAM=y
-# CONFIG_MISC_DEVICES is not set
-CONFIG_NETDEVICES=y
-CONFIG_NET_ETHERNET=y
-CONFIG_NET_PCI=y
-CONFIG_8139CP=y
-CONFIG_8139TOO=y
-# CONFIG_NETDEV_1000 is not set
-# CONFIG_NETDEV_10000 is not set
-# CONFIG_INPUT_MOUSEDEV is not set
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_SERIO is not set
-# CONFIG_VT is not set
-CONFIG_SERIAL_SH_SCI=y
-CONFIG_SERIAL_SH_SCI_CONSOLE=y
-# CONFIG_HW_RANDOM is not set
-# CONFIG_HWMON is not set
-# CONFIG_HID_SUPPORT is not set
-# CONFIG_USB_SUPPORT is not set
-CONFIG_RTC_CLASS=y
-CONFIG_RTC_DRV_DS1302=y
-CONFIG_EXT2_FS=y
-# CONFIG_DNOTIFY is not set
-CONFIG_TMPFS=y
-CONFIG_CRAMFS=y
-CONFIG_ROMFS_FS=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+++ /dev/null
-CONFIG_EXPERIMENTAL=y
-CONFIG_LOG_BUF_SHIFT=14
-CONFIG_BLK_DEV_INITRD=y
-# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
-# CONFIG_SYSCTL_SYSCALL is not set
-# CONFIG_HOTPLUG is not set
-CONFIG_SLAB=y
-CONFIG_MODULES=y
-CONFIG_MODULE_UNLOAD=y
-# CONFIG_BLK_DEV_BSG is not set
-CONFIG_CPU_SUBTYPE_SH7751R=y
-CONFIG_MEMORY_START=0x0c000000
-CONFIG_MEMORY_SIZE=0x00400000
-CONFIG_FLATMEM_MANUAL=y
-CONFIG_SH_7751_SYSTEMH=y
-CONFIG_PREEMPT=y
-# CONFIG_STANDALONE is not set
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=1024
-# CONFIG_INPUT is not set
-# CONFIG_SERIO_SERPORT is not set
-# CONFIG_VT is not set
-CONFIG_HW_RANDOM=y
-CONFIG_PROC_KCORE=y
-CONFIG_TMPFS=y
-CONFIG_CRAMFS=y
-CONFIG_ROMFS_FS=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
/*
* These will never work in 32-bit, don't even bother.
*/
-#define P1SEGADDR(a) __futile_remapping_attempt
-#define P2SEGADDR(a) __futile_remapping_attempt
-#define P3SEGADDR(a) __futile_remapping_attempt
-#define P4SEGADDR(a) __futile_remapping_attempt
+#define P1SEGADDR(a) ({ (void)(a); BUG(); NULL; })
+#define P2SEGADDR(a) ({ (void)(a); BUG(); NULL; })
+#define P3SEGADDR(a) ({ (void)(a); BUG(); NULL; })
+#define P4SEGADDR(a) ({ (void)(a); BUG(); NULL; })
#endif
#endif /* P1SEG */
#define PHYS_ADDR_MASK29 0x1fffffff
#define PHYS_ADDR_MASK32 0xffffffff
-#ifdef CONFIG_PMB
static inline unsigned long phys_addr_mask(void)
{
/* Is the MMU in 29bit mode? */
return PHYS_ADDR_MASK32;
}
-#elif defined(CONFIG_32BIT)
-static inline unsigned long phys_addr_mask(void)
-{
- return PHYS_ADDR_MASK32;
-}
-#else
-static inline unsigned long phys_addr_mask(void)
-{
- return PHYS_ADDR_MASK29;
-}
-#endif
#define PTE_PHYS_MASK (phys_addr_mask() & PAGE_MASK)
#define PTE_FLAGS_MASK (~(PTE_PHYS_MASK) << PAGE_SHIFT)
#include <linux/compiler.h>
#include <linux/linkage.h>
#include <asm/types.h>
+#include <asm/uncached.h>
#define AT_VECTOR_SIZE_ARCH 5 /* entries in ARCH_DLINFO */
#define instruction_size(insn) (4)
#endif
-extern unsigned long cached_to_uncached;
-extern unsigned long uncached_size;
-
void per_cpu_trap_init(void);
void default_idle(void);
void cpu_idle_wait(void);
__restore_dsp(prev); \
} while (0)
-/*
- * Jump to uncached area.
- * When handling TLB or caches, we need to do it from an uncached area.
- */
-#define jump_to_uncached() \
-do { \
- unsigned long __dummy; \
- \
- __asm__ __volatile__( \
- "mova 1f, %0\n\t" \
- "add %1, %0\n\t" \
- "jmp @%0\n\t" \
- " nop\n\t" \
- ".balign 4\n" \
- "1:" \
- : "=&z" (__dummy) \
- : "r" (cached_to_uncached)); \
-} while (0)
-
-/*
- * Back to cached area.
- */
-#define back_to_cached() \
-do { \
- unsigned long __dummy; \
- ctrl_barrier(); \
- __asm__ __volatile__( \
- "mov.l 1f, %0\n\t" \
- "jmp @%0\n\t" \
- " nop\n\t" \
- ".balign 4\n" \
- "1: .long 2f\n" \
- "2:" \
- : "=&r" (__dummy)); \
-} while (0)
-
#ifdef CONFIG_CPU_HAS_SR_RB
#define lookup_exception_vector() \
({ \
&next->thread); \
} while (0)
-#define jump_to_uncached() do { } while (0)
-#define back_to_cached() do { } while (0)
-
#define __icbi(addr) __asm__ __volatile__ ( "icbi %0, 0\n\t" : : "r" (addr))
#define __ocbp(addr) __asm__ __volatile__ ( "ocbp %0, 0\n\t" : : "r" (addr))
#define __ocbi(addr) __asm__ __volatile__ ( "ocbi %0, 0\n\t" : : "r" (addr))
#include <linux/bug.h>
#ifdef CONFIG_UNCACHED_MAPPING
+extern unsigned long cached_to_uncached;
+extern unsigned long uncached_size;
extern unsigned long uncached_start, uncached_end;
extern int virt_addr_uncached(unsigned long kaddr);
extern void uncached_init(void);
extern void uncached_resize(unsigned long size);
+
+/*
+ * Jump to uncached area.
+ * When handling TLB or caches, we need to do it from an uncached area.
+ */
+#define jump_to_uncached() \
+do { \
+ unsigned long __dummy; \
+ \
+ __asm__ __volatile__( \
+ "mova 1f, %0\n\t" \
+ "add %1, %0\n\t" \
+ "jmp @%0\n\t" \
+ " nop\n\t" \
+ ".balign 4\n" \
+ "1:" \
+ : "=&z" (__dummy) \
+ : "r" (cached_to_uncached)); \
+} while (0)
+
+/*
+ * Back to cached area.
+ */
+#define back_to_cached() \
+do { \
+ unsigned long __dummy; \
+ ctrl_barrier(); \
+ __asm__ __volatile__( \
+ "mov.l 1f, %0\n\t" \
+ "jmp @%0\n\t" \
+ " nop\n\t" \
+ ".balign 4\n" \
+ "1: .long 2f\n" \
+ "2:" \
+ : "=&r" (__dummy)); \
+} while (0)
#else
#define virt_addr_uncached(kaddr) (0)
#define uncached_init() do { } while (0)
#define uncached_resize(size) BUG()
+#define jump_to_uncached() do { } while (0)
+#define back_to_cached() do { } while (0)
#endif
#endif /* __ASM_SH_UNCACHED_H */
+++ /dev/null
-#ifndef __ASM_SH_EDOSK7705_H
-#define __ASM_SH_EDOSK7705_H
-
-#define __IO_PREFIX sh_edosk7705
-#include <asm/io_generic.h>
-
-#endif /* __ASM_SH_EDOSK7705_H */
#define __IO_PREFIX microdev
#include <asm/io_generic.h>
-#if defined(CONFIG_PCI)
-unsigned char microdev_pci_inb(unsigned long port);
-unsigned short microdev_pci_inw(unsigned long port);
-unsigned long microdev_pci_inl(unsigned long port);
-void microdev_pci_outb(unsigned char data, unsigned long port);
-void microdev_pci_outw(unsigned short data, unsigned long port);
-void microdev_pci_outl(unsigned long data, unsigned long port);
-#endif
-
#endif /* __ASM_SH_MICRODEV_H */
--- /dev/null
+/*
+ * include/asm-sh/snapgear.h
+ *
+ * Modified version of io_se.h for the snapgear-specific functions.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * IO functions for a SnapGear
+ */
+
+#ifndef _ASM_SH_IO_SNAPGEAR_H
+#define _ASM_SH_IO_SNAPGEAR_H
+
+#define __IO_PREFIX snapgear
+#include <asm/io_generic.h>
+
+/*
+ * We need to remember what was written to the ioport as some bits
+ * are shared with other functions and you cannot read back what was
+ * written :-|
+ *
+ * Bit Read Write
+ * -----------------------------------------------
+ * D0 DCD on ttySC1 power
+ * D1 Reset Switch heatbeat
+ * D2 ttySC0 CTS (7100) LAN
+ * D3 - WAN
+ * D4 ttySC0 DCD (7100) CONSOLE
+ * D5 - ONLINE
+ * D6 - VPN
+ * D7 - DTR on ttySC1
+ * D8 - ttySC0 RTS (7100)
+ * D9 - ttySC0 DTR (7100)
+ * D10 - RTC SCLK
+ * D11 RTC DATA RTC DATA
+ * D12 - RTS RESET
+ */
+
+#define SECUREEDGE_IOPORT_ADDR ((volatile short *) 0xb0000000)
+extern unsigned short secureedge5410_ioport;
+
+#define SECUREEDGE_WRITE_IOPORT(val, mask) (*SECUREEDGE_IOPORT_ADDR = \
+ (secureedge5410_ioport = \
+ ((secureedge5410_ioport & ~(mask)) | ((val) & (mask)))))
+#define SECUREEDGE_READ_IOPORT() \
+ ((*SECUREEDGE_IOPORT_ADDR&0x0817) | (secureedge5410_ioport&~0x0817))
+
+#endif /* _ASM_SH_IO_SNAPGEAR_H */
+++ /dev/null
-/*
- * include/asm-sh/snapgear.h
- *
- * Modified version of io_se.h for the snapgear-specific functions.
- *
- * May be copied or modified under the terms of the GNU General Public
- * License. See linux/COPYING for more information.
- *
- * IO functions for a SnapGear
- */
-
-#ifndef _ASM_SH_IO_SNAPGEAR_H
-#define _ASM_SH_IO_SNAPGEAR_H
-
-#if defined(CONFIG_CPU_SH4)
-/*
- * The external interrupt lines, these take up ints 0 - 15 inclusive
- * depending on the priority for the interrupt. In fact the priority
- * is the interrupt :-)
- */
-
-#define IRL0_IRQ 2
-#define IRL0_PRIORITY 13
-
-#define IRL1_IRQ 5
-#define IRL1_PRIORITY 10
-
-#define IRL2_IRQ 8
-#define IRL2_PRIORITY 7
-
-#define IRL3_IRQ 11
-#define IRL3_PRIORITY 4
-#endif
-
-#define __IO_PREFIX snapgear
-#include <asm/io_generic.h>
-
-#ifdef CONFIG_SH_SECUREEDGE5410
-/*
- * We need to remember what was written to the ioport as some bits
- * are shared with other functions and you cannot read back what was
- * written :-|
- *
- * Bit Read Write
- * -----------------------------------------------
- * D0 DCD on ttySC1 power
- * D1 Reset Switch heatbeat
- * D2 ttySC0 CTS (7100) LAN
- * D3 - WAN
- * D4 ttySC0 DCD (7100) CONSOLE
- * D5 - ONLINE
- * D6 - VPN
- * D7 - DTR on ttySC1
- * D8 - ttySC0 RTS (7100)
- * D9 - ttySC0 DTR (7100)
- * D10 - RTC SCLK
- * D11 RTC DATA RTC DATA
- * D12 - RTS RESET
- */
-
-#define SECUREEDGE_IOPORT_ADDR ((volatile short *) 0xb0000000)
-extern unsigned short secureedge5410_ioport;
-
-#define SECUREEDGE_WRITE_IOPORT(val, mask) (*SECUREEDGE_IOPORT_ADDR = \
- (secureedge5410_ioport = \
- ((secureedge5410_ioport & ~(mask)) | ((val) & (mask)))))
-#define SECUREEDGE_READ_IOPORT() \
- ((*SECUREEDGE_IOPORT_ADDR&0x0817) | (secureedge5410_ioport&~0x0817))
-#endif
-
-#endif /* _ASM_SH_IO_SNAPGEAR_H */
+++ /dev/null
-#ifndef __ASM_SH_SYSTEMH_7751SYSTEMH_H
-#define __ASM_SH_SYSTEMH_7751SYSTEMH_H
-
-/*
- * linux/include/asm-sh/systemh/7751systemh.h
- *
- * Copyright (C) 2000 Kazumoto Kojima
- *
- * Hitachi SystemH support
-
- * Modified for 7751 SystemH by
- * Jonathan Short, 2002.
- */
-
-/* Box specific addresses. */
-
-#define PA_ROM 0x00000000 /* EPROM */
-#define PA_ROM_SIZE 0x00400000 /* EPROM size 4M byte */
-#define PA_FROM 0x01000000 /* EPROM */
-#define PA_FROM_SIZE 0x00400000 /* EPROM size 4M byte */
-#define PA_EXT1 0x04000000
-#define PA_EXT1_SIZE 0x04000000
-#define PA_EXT2 0x08000000
-#define PA_EXT2_SIZE 0x04000000
-#define PA_SDRAM 0x0c000000
-#define PA_SDRAM_SIZE 0x04000000
-
-#define PA_EXT4 0x12000000
-#define PA_EXT4_SIZE 0x02000000
-#define PA_EXT5 0x14000000
-#define PA_EXT5_SIZE 0x04000000
-#define PA_PCIC 0x18000000 /* MR-SHPC-01 PCMCIA */
-
-#define PA_DIPSW0 0xb9000000 /* Dip switch 5,6 */
-#define PA_DIPSW1 0xb9000002 /* Dip switch 7,8 */
-#define PA_LED 0xba000000 /* LED */
-#define PA_BCR 0xbb000000 /* FPGA on the MS7751SE01 */
-
-#define PA_MRSHPC 0xb83fffe0 /* MR-SHPC-01 PCMCIA controller */
-#define PA_MRSHPC_MW1 0xb8400000 /* MR-SHPC-01 memory window base */
-#define PA_MRSHPC_MW2 0xb8500000 /* MR-SHPC-01 attribute window base */
-#define PA_MRSHPC_IO 0xb8600000 /* MR-SHPC-01 I/O window base */
-#define MRSHPC_MODE (PA_MRSHPC + 4)
-#define MRSHPC_OPTION (PA_MRSHPC + 6)
-#define MRSHPC_CSR (PA_MRSHPC + 8)
-#define MRSHPC_ISR (PA_MRSHPC + 10)
-#define MRSHPC_ICR (PA_MRSHPC + 12)
-#define MRSHPC_CPWCR (PA_MRSHPC + 14)
-#define MRSHPC_MW0CR1 (PA_MRSHPC + 16)
-#define MRSHPC_MW1CR1 (PA_MRSHPC + 18)
-#define MRSHPC_IOWCR1 (PA_MRSHPC + 20)
-#define MRSHPC_MW0CR2 (PA_MRSHPC + 22)
-#define MRSHPC_MW1CR2 (PA_MRSHPC + 24)
-#define MRSHPC_IOWCR2 (PA_MRSHPC + 26)
-#define MRSHPC_CDCR (PA_MRSHPC + 28)
-#define MRSHPC_PCIC_INFO (PA_MRSHPC + 30)
-
-#define BCR_ILCRA (PA_BCR + 0)
-#define BCR_ILCRB (PA_BCR + 2)
-#define BCR_ILCRC (PA_BCR + 4)
-#define BCR_ILCRD (PA_BCR + 6)
-#define BCR_ILCRE (PA_BCR + 8)
-#define BCR_ILCRF (PA_BCR + 10)
-#define BCR_ILCRG (PA_BCR + 12)
-
-#define IRQ_79C973 13
-
-#define __IO_PREFIX sh7751systemh
-#include <asm/io_generic.h>
-
-#endif /* __ASM_SH_SYSTEMH_7751SYSTEMH_H */
* Default rate for the root input clock, reset this with clk_set_rate()
* from the platform code.
*/
-struct clk extal_clk = {
+static struct clk extal_clk = {
.rate = 33333333,
};
.parent = &pll_clk,
};
-struct clk *main_clks[] = {
+static struct clk *main_clks[] = {
&r_clk,
&extal_clk,
&fll_clk,
enum { DIV6_V, DIV6_FA, DIV6_FB, DIV6_I, DIV6_S, DIV6_NR };
-struct clk div6_clks[DIV6_NR] = {
+static struct clk div6_clks[DIV6_NR] = {
[DIV6_V] = SH_CLK_DIV6(&div3_clk, VCLKCR, 0),
[DIV6_FA] = SH_CLK_DIV6(&div3_clk, FCLKACR, 0),
[DIV6_FB] = SH_CLK_DIV6(&div3_clk, FCLKBCR, 0),
config 32BIT
bool
- default y if CPU_SH5
+ default y if CPU_SH5 || !MMU
config PMB
bool "Support 32-bit physical addressing through PMB"
void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
enum dma_data_direction direction)
{
-#if defined(CONFIG_CPU_SH5) || defined(CONFIG_PMB)
- void *p1addr = vaddr;
-#else
- void *p1addr = (void*) P1SEGADDR((unsigned long)vaddr);
-#endif
+ void *addr;
+
+ addr = __in_29bit_mode() ?
+ (void *)P1SEGADDR((unsigned long)vaddr) : vaddr;
switch (direction) {
case DMA_FROM_DEVICE: /* invalidate only */
- __flush_invalidate_region(p1addr, size);
+ __flush_invalidate_region(addr, size);
break;
case DMA_TO_DEVICE: /* writeback only */
- __flush_wback_region(p1addr, size);
+ __flush_wback_region(addr, size);
break;
case DMA_BIDIRECTIONAL: /* writeback and invalidate */
- __flush_purge_region(p1addr, size);
+ __flush_purge_region(addr, size);
break;
default:
BUG();
void __init uncached_init(void)
{
-#ifdef CONFIG_29BIT
+#if defined(CONFIG_29BIT) || !defined(CONFIG_MMU)
uncached_start = P2SEG;
#else
uncached_start = memory_end;
7724SE SH_7724_SOLUTION_ENGINE
7751SE SH_7751_SOLUTION_ENGINE
7780SE SH_7780_SOLUTION_ENGINE
-7751SYSTEMH SH_7751_SYSTEMH
HP6XX SH_HP6XX
DREAMCAST SH_DREAMCAST
SNAPGEAR SH_SECUREEDGE5410
struct task_struct;
-extern long subarch_ptrace(struct task_struct *child, long request, long addr,
- long data);
+extern long subarch_ptrace(struct task_struct *child, long request,
+ unsigned long addr, unsigned long data);
extern unsigned long getreg(struct task_struct *child, int regno);
extern int putreg(struct task_struct *child, int regno, unsigned long value);
extern int get_fpregs(struct user_i387_struct __user *buf,
break;
case PTRACE_SET_THREAD_AREA:
- ret = ptrace_set_thread_area(child, addr, datavp);
+ ret = ptrace_set_thread_area(child, addr, vp);
break;
case PTRACE_FAULTINFO: {
static inline u32 native_apic_msr_read(u32 reg)
{
- u32 low, high;
+ u64 msr;
if (reg == APIC_DFR)
return -1;
- rdmsr(APIC_BASE_MSR + (reg >> 4), low, high);
- return low;
+ rdmsrl(APIC_BASE_MSR + (reg >> 4), msr);
+ return (u32)msr;
}
static inline void native_x2apic_wait_icr_idle(void)
extern void x2apic_icr_write(u32 low, u32 id);
static inline int x2apic_enabled(void)
{
- int msr, msr2;
+ u64 msr;
if (!cpu_has_x2apic)
return 0;
- rdmsr(MSR_IA32_APICBASE, msr, msr2);
+ rdmsrl(MSR_IA32_APICBASE, msr);
if (msr & X2APIC_ENABLE)
return 1;
return 0;
} s;
};
+/* ========================================================================= */
+/* UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR */
+/* ========================================================================= */
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR 0x16000c8UL
+
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR_BASE_SHFT 24
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR_BASE_MASK 0x00000000ff000000UL
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR_M_ALIAS_SHFT 48
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR_M_ALIAS_MASK 0x001f000000000000UL
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR_ENABLE_SHFT 63
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR_ENABLE_MASK 0x8000000000000000UL
+
+union uvh_rh_gam_alias210_overlay_config_0_mmr_u {
+ unsigned long v;
+ struct uvh_rh_gam_alias210_overlay_config_0_mmr_s {
+ unsigned long rsvd_0_23: 24; /* */
+ unsigned long base : 8; /* RW */
+ unsigned long rsvd_32_47: 16; /* */
+ unsigned long m_alias : 5; /* RW */
+ unsigned long rsvd_53_62: 10; /* */
+ unsigned long enable : 1; /* RW */
+ } s;
+};
+
+/* ========================================================================= */
+/* UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR */
+/* ========================================================================= */
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR 0x16000d8UL
+
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR_BASE_SHFT 24
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR_BASE_MASK 0x00000000ff000000UL
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR_M_ALIAS_SHFT 48
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR_M_ALIAS_MASK 0x001f000000000000UL
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR_ENABLE_SHFT 63
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR_ENABLE_MASK 0x8000000000000000UL
+
+union uvh_rh_gam_alias210_overlay_config_1_mmr_u {
+ unsigned long v;
+ struct uvh_rh_gam_alias210_overlay_config_1_mmr_s {
+ unsigned long rsvd_0_23: 24; /* */
+ unsigned long base : 8; /* RW */
+ unsigned long rsvd_32_47: 16; /* */
+ unsigned long m_alias : 5; /* RW */
+ unsigned long rsvd_53_62: 10; /* */
+ unsigned long enable : 1; /* RW */
+ } s;
+};
+
+/* ========================================================================= */
+/* UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR */
+/* ========================================================================= */
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR 0x16000e8UL
+
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR_BASE_SHFT 24
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR_BASE_MASK 0x00000000ff000000UL
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR_M_ALIAS_SHFT 48
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR_M_ALIAS_MASK 0x001f000000000000UL
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR_ENABLE_SHFT 63
+#define UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR_ENABLE_MASK 0x8000000000000000UL
+
+union uvh_rh_gam_alias210_overlay_config_2_mmr_u {
+ unsigned long v;
+ struct uvh_rh_gam_alias210_overlay_config_2_mmr_s {
+ unsigned long rsvd_0_23: 24; /* */
+ unsigned long base : 8; /* RW */
+ unsigned long rsvd_32_47: 16; /* */
+ unsigned long m_alias : 5; /* RW */
+ unsigned long rsvd_53_62: 10; /* */
+ unsigned long enable : 1; /* RW */
+ } s;
+};
+
/* ========================================================================= */
/* UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR */
/* ========================================================================= */
} s;
};
+/* ========================================================================= */
+/* UVH_RH_GAM_CONFIG_MMR */
+/* ========================================================================= */
+#define UVH_RH_GAM_CONFIG_MMR 0x1600000UL
+
+#define UVH_RH_GAM_CONFIG_MMR_M_SKT_SHFT 0
+#define UVH_RH_GAM_CONFIG_MMR_M_SKT_MASK 0x000000000000003fUL
+#define UVH_RH_GAM_CONFIG_MMR_N_SKT_SHFT 6
+#define UVH_RH_GAM_CONFIG_MMR_N_SKT_MASK 0x00000000000003c0UL
+#define UVH_RH_GAM_CONFIG_MMR_MMIOL_CFG_SHFT 12
+#define UVH_RH_GAM_CONFIG_MMR_MMIOL_CFG_MASK 0x0000000000001000UL
+
+union uvh_rh_gam_config_mmr_u {
+ unsigned long v;
+ struct uvh_rh_gam_config_mmr_s {
+ unsigned long m_skt : 6; /* RW */
+ unsigned long n_skt : 4; /* RW */
+ unsigned long rsvd_10_11: 2; /* */
+ unsigned long mmiol_cfg : 1; /* RW */
+ unsigned long rsvd_13_63: 51; /* */
+ } s;
+};
+
/* ========================================================================= */
/* UVH_RH_GAM_GRU_OVERLAY_CONFIG_MMR */
/* ========================================================================= */
} s;
};
-/* ========================================================================= */
-/* UVH_SI_ADDR_MAP_CONFIG */
-/* ========================================================================= */
-#define UVH_SI_ADDR_MAP_CONFIG 0xc80000UL
-
-#define UVH_SI_ADDR_MAP_CONFIG_M_SKT_SHFT 0
-#define UVH_SI_ADDR_MAP_CONFIG_M_SKT_MASK 0x000000000000003fUL
-#define UVH_SI_ADDR_MAP_CONFIG_N_SKT_SHFT 8
-#define UVH_SI_ADDR_MAP_CONFIG_N_SKT_MASK 0x0000000000000f00UL
-
-union uvh_si_addr_map_config_u {
- unsigned long v;
- struct uvh_si_addr_map_config_s {
- unsigned long m_skt : 6; /* RW */
- unsigned long rsvd_6_7: 2; /* */
- unsigned long n_skt : 4; /* RW */
- unsigned long rsvd_12_63: 52; /* */
- } s;
-};
-
-/* ========================================================================= */
-/* UVH_SI_ALIAS0_OVERLAY_CONFIG */
-/* ========================================================================= */
-#define UVH_SI_ALIAS0_OVERLAY_CONFIG 0xc80008UL
-
-#define UVH_SI_ALIAS0_OVERLAY_CONFIG_BASE_SHFT 24
-#define UVH_SI_ALIAS0_OVERLAY_CONFIG_BASE_MASK 0x00000000ff000000UL
-#define UVH_SI_ALIAS0_OVERLAY_CONFIG_M_ALIAS_SHFT 48
-#define UVH_SI_ALIAS0_OVERLAY_CONFIG_M_ALIAS_MASK 0x001f000000000000UL
-#define UVH_SI_ALIAS0_OVERLAY_CONFIG_ENABLE_SHFT 63
-#define UVH_SI_ALIAS0_OVERLAY_CONFIG_ENABLE_MASK 0x8000000000000000UL
-
-union uvh_si_alias0_overlay_config_u {
- unsigned long v;
- struct uvh_si_alias0_overlay_config_s {
- unsigned long rsvd_0_23: 24; /* */
- unsigned long base : 8; /* RW */
- unsigned long rsvd_32_47: 16; /* */
- unsigned long m_alias : 5; /* RW */
- unsigned long rsvd_53_62: 10; /* */
- unsigned long enable : 1; /* RW */
- } s;
-};
-
-/* ========================================================================= */
-/* UVH_SI_ALIAS1_OVERLAY_CONFIG */
-/* ========================================================================= */
-#define UVH_SI_ALIAS1_OVERLAY_CONFIG 0xc80010UL
-
-#define UVH_SI_ALIAS1_OVERLAY_CONFIG_BASE_SHFT 24
-#define UVH_SI_ALIAS1_OVERLAY_CONFIG_BASE_MASK 0x00000000ff000000UL
-#define UVH_SI_ALIAS1_OVERLAY_CONFIG_M_ALIAS_SHFT 48
-#define UVH_SI_ALIAS1_OVERLAY_CONFIG_M_ALIAS_MASK 0x001f000000000000UL
-#define UVH_SI_ALIAS1_OVERLAY_CONFIG_ENABLE_SHFT 63
-#define UVH_SI_ALIAS1_OVERLAY_CONFIG_ENABLE_MASK 0x8000000000000000UL
-
-union uvh_si_alias1_overlay_config_u {
- unsigned long v;
- struct uvh_si_alias1_overlay_config_s {
- unsigned long rsvd_0_23: 24; /* */
- unsigned long base : 8; /* RW */
- unsigned long rsvd_32_47: 16; /* */
- unsigned long m_alias : 5; /* RW */
- unsigned long rsvd_53_62: 10; /* */
- unsigned long enable : 1; /* RW */
- } s;
-};
-
-/* ========================================================================= */
-/* UVH_SI_ALIAS2_OVERLAY_CONFIG */
-/* ========================================================================= */
-#define UVH_SI_ALIAS2_OVERLAY_CONFIG 0xc80018UL
-
-#define UVH_SI_ALIAS2_OVERLAY_CONFIG_BASE_SHFT 24
-#define UVH_SI_ALIAS2_OVERLAY_CONFIG_BASE_MASK 0x00000000ff000000UL
-#define UVH_SI_ALIAS2_OVERLAY_CONFIG_M_ALIAS_SHFT 48
-#define UVH_SI_ALIAS2_OVERLAY_CONFIG_M_ALIAS_MASK 0x001f000000000000UL
-#define UVH_SI_ALIAS2_OVERLAY_CONFIG_ENABLE_SHFT 63
-#define UVH_SI_ALIAS2_OVERLAY_CONFIG_ENABLE_MASK 0x8000000000000000UL
-
-union uvh_si_alias2_overlay_config_u {
- unsigned long v;
- struct uvh_si_alias2_overlay_config_s {
- unsigned long rsvd_0_23: 24; /* */
- unsigned long base : 8; /* RW */
- unsigned long rsvd_32_47: 16; /* */
- unsigned long m_alias : 5; /* RW */
- unsigned long rsvd_53_62: 10; /* */
- unsigned long enable : 1; /* RW */
- } s;
-};
-
-#endif /* _ASM_X86_UV_UV_MMRS_H */
+#endif /* __ASM_UV_MMRS_X86_H__ */
#include <asm/mce.h>
#include <asm/kvm_para.h>
#include <asm/tsc.h>
-#include <asm/atomic.h>
unsigned int num_processors;
#define DEST_SHIFT UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR_DEST_BASE_SHFT
static __initdata struct redir_addr redir_addrs[] = {
- {UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR, UVH_SI_ALIAS0_OVERLAY_CONFIG},
- {UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_1_MMR, UVH_SI_ALIAS1_OVERLAY_CONFIG},
- {UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_2_MMR, UVH_SI_ALIAS2_OVERLAY_CONFIG},
+ {UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR, UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_0_MMR},
+ {UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_1_MMR, UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_1_MMR},
+ {UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_2_MMR, UVH_RH_GAM_ALIAS210_OVERLAY_CONFIG_2_MMR},
};
static __init void get_lowmem_redirect(unsigned long *base, unsigned long *size)
{
- union uvh_si_alias0_overlay_config_u alias;
+ union uvh_rh_gam_alias210_overlay_config_2_mmr_u alias;
union uvh_rh_gam_alias210_redirect_config_2_mmr_u redirect;
int i;
void __init uv_system_init(void)
{
- union uvh_si_addr_map_config_u m_n_config;
+ union uvh_rh_gam_config_mmr_u m_n_config;
union uvh_node_id_u node_id;
unsigned long gnode_upper, lowmem_redir_base, lowmem_redir_size;
int bytes, nid, cpu, lcpu, pnode, blade, i, j, m_val, n_val;
map_low_mmrs();
- m_n_config.v = uv_read_local_mmr(UVH_SI_ADDR_MAP_CONFIG);
+ m_n_config.v = uv_read_local_mmr(UVH_RH_GAM_CONFIG_MMR );
m_val = m_n_config.s.m_skt;
n_val = m_n_config.s.n_skt;
mmr_base =
struct amd_nb *nb;
int i;
- nb = kmalloc(sizeof(struct amd_nb), GFP_KERNEL);
+ nb = kmalloc_node(sizeof(struct amd_nb), GFP_KERNEL | __GFP_ZERO,
+ cpu_to_node(cpu));
if (!nb)
return NULL;
- memset(nb, 0, sizeof(*nb));
nb->nb_id = nb_id;
/*
return 0;
}
- equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size);
+ equiv_cpu_table = vmalloc(size);
if (!equiv_cpu_table) {
pr_err("failed to allocate equivalent CPU table\n");
return 0;
wrmsrl(address, val);
}
-static int __devinit set_check_enable_amd_mmconf(const struct dmi_system_id *d)
+static int __init set_check_enable_amd_mmconf(const struct dmi_system_id *d)
{
pci_probe |= PCI_CHECK_ENABLE_AMD_MMCONF;
return 0;
}
-static const struct dmi_system_id __cpuinitconst mmconf_dmi_table[] = {
+static const struct dmi_system_id __initconst mmconf_dmi_table[] = {
{
.callback = set_check_enable_amd_mmconf,
.ident = "Sun Microsystems Machine",
{}
};
-void __cpuinit check_enable_amd_mmconf_dmi(void)
+/* Called from a __cpuinit function, but only on the BSP. */
+void __ref check_enable_amd_mmconf_dmi(void)
{
dmi_check_system(mmconf_dmi_table);
}
valid_flags = flags;
}
-/*
- * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
- * yielding a 64-bit result.
- */
-static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift)
-{
- u64 product;
-#ifdef __i386__
- u32 tmp1, tmp2;
-#endif
-
- if (shift < 0)
- delta >>= -shift;
- else
- delta <<= shift;
-
-#ifdef __i386__
- __asm__ (
- "mul %5 ; "
- "mov %4,%%eax ; "
- "mov %%edx,%4 ; "
- "mul %5 ; "
- "xor %5,%5 ; "
- "add %4,%%eax ; "
- "adc %5,%%edx ; "
- : "=A" (product), "=r" (tmp1), "=r" (tmp2)
- : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) );
-#elif defined(__x86_64__)
- __asm__ (
- "mul %%rdx ; shrd $32,%%rdx,%%rax"
- : "=a" (product) : "0" (delta), "d" ((u64)mul_frac) );
-#else
-#error implement me!
-#endif
-
- return product;
-}
-
static u64 pvclock_get_nsec_offset(struct pvclock_shadow_time *shadow)
{
u64 delta = native_read_tsc() - shadow->tsc_timestamp;
}
}
-static int tlb_cpuhp_notify(struct notifier_block *n,
+static int __cpuinit tlb_cpuhp_notify(struct notifier_block *n,
unsigned long action, void *hcpu)
{
switch (action & 0xf) {
* each bau_desc is 64 bytes; there are 8 (UV_ITEMS_PER_DESCRIPTOR)
* per cpu; and up to 32 (UV_ADP_SIZE) cpu's per uvhub
*/
- bau_desc = (struct bau_desc *)kmalloc_node(sizeof(struct bau_desc)*
- UV_ADP_SIZE*UV_ITEMS_PER_DESCRIPTOR, GFP_KERNEL, node);
+ bau_desc = kmalloc_node(sizeof(struct bau_desc) * UV_ADP_SIZE
+ * UV_ITEMS_PER_DESCRIPTOR, GFP_KERNEL, node);
BUG_ON(!bau_desc);
pa = uv_gpa(bau_desc); /* need the real nasid*/
struct bau_payload_queue_entry *pqp_malloc;
struct bau_control *bcp;
- pqp = (struct bau_payload_queue_entry *) kmalloc_node(
- (DEST_Q_SIZE + 1) * sizeof(struct bau_payload_queue_entry),
- GFP_KERNEL, node);
+ pqp = kmalloc_node((DEST_Q_SIZE + 1)
+ * sizeof(struct bau_payload_queue_entry),
+ GFP_KERNEL, node);
BUG_ON(!pqp);
pqp_malloc = pqp;
timeout_us = calculate_destination_timeout();
- uvhub_descs = (struct uvhub_desc *)
- kmalloc(nuvhubs * sizeof(struct uvhub_desc), GFP_KERNEL);
+ uvhub_descs = kmalloc(nuvhubs * sizeof(struct uvhub_desc), GFP_KERNEL);
memset(uvhub_descs, 0, nuvhubs * sizeof(struct uvhub_desc));
uvhub_mask = kzalloc((nuvhubs+7)/8, GFP_KERNEL);
for_each_present_cpu(cpu) {
static void pcrypt_fini_padata(struct padata_pcrypt *pcrypt)
{
- kobject_put(&pcrypt->pinst->kobj);
free_cpumask_var(pcrypt->cb_cpumask->mask);
kfree(pcrypt->cb_cpumask);
# char/ comes before serial/ etc so that the VT console is the boot-time
# default.
+obj-y += tty/
obj-y += char/
# gpu/ comes after char for AGP vs DRM startup
device_remove_file(&floppy_device[drive].dev, &dev_attr_cmos);
platform_device_unregister(&floppy_device[drive]);
}
- put_disk(disks[drive]);
blk_cleanup_queue(disks[drive]->queue);
+ put_disk(disks[drive]);
}
del_timer_sync(&fd_timeout);
+++ /dev/null
-consolemap_deftbl.c
-defkeymap.c
# 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 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
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
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
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
unsigned int gfdt = flags & AGP_USER_CACHED_MEMORY_GFDT;
u32 pte_flags;
- if (type_mask == AGP_USER_UNCACHED_MEMORY)
+ if (type_mask == AGP_USER_MEMORY)
pte_flags = GEN6_PTE_UNCACHED | I810_PTE_VALID;
else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC) {
- pte_flags = GEN6_PTE_LLC | I810_PTE_VALID;
+ pte_flags = GEN6_PTE_LLC_MLC | I810_PTE_VALID;
if (gfdt)
pte_flags |= GEN6_PTE_GFDT;
} else { /* set 'normal'/'cached' to LLC by default */
- pte_flags = GEN6_PTE_LLC_MLC | I810_PTE_VALID;
+ pte_flags = GEN6_PTE_LLC | I810_PTE_VALID;
if (gfdt)
pte_flags |= GEN6_PTE_GFDT;
}
+++ /dev/null
-/*
- * 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 <jj@ultra.linux.cz>, July 1998
- *
- * Fix bug in inverse translation. Stanislav Voronyi <stas@cnti.uanet.kharkov.ua>, Dec 1998
- */
-
-#include <linux/module.h>
-#include <linux/kd.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/tty.h>
-#include <asm/uaccess.h>
-#include <linux/consolemap.h>
-#include <linux/vt_kern.h>
-
-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<E_TABSZ ; i++) {
- unsigned char uc;
- __get_user(uc, arg+i);
- p[i] = UNI_DIRECT_BASE | uc;
- }
-
- update_user_maps();
- return 0;
-}
-
-int con_get_trans_old(unsigned char __user * arg)
-{
- int i, ch;
- unsigned short *p = translations[USER_MAP];
-
- if (!access_ok(VERIFY_WRITE, arg, E_TABSZ))
- return -EFAULT;
-
- for (i=0; i<E_TABSZ ; i++)
- {
- ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]);
- __put_user((ch & ~0xff) ? 0 : ch, arg+i);
- }
- return 0;
-}
-
-int con_set_trans_new(ushort __user * arg)
-{
- int i;
- unsigned short *p = translations[USER_MAP];
-
- if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short)))
- return -EFAULT;
-
- for (i=0; i<E_TABSZ ; i++) {
- unsigned short us;
- __get_user(us, arg+i);
- p[i] = us;
- }
-
- update_user_maps();
- return 0;
-}
-
-int con_get_trans_new(ushort __user * arg)
-{
- int i;
- unsigned short *p = translations[USER_MAP];
-
- if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short)))
- return -EFAULT;
-
- for (i=0; i<E_TABSZ ; i++)
- __put_user(p[i], arg+i);
-
- return 0;
-}
-
-/*
- * Unicode -> 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);
+++ /dev/null
-#
-# 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
+++ /dev/null
-/* Do not edit this file! It was automatically generated by */
-/* loadkeys --mktable defkeymap.map > defkeymap.c */
-
-#include <linux/types.h>
-#include <linux/keyboard.h>
-#include <linux/kd.h>
-
-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;
+++ /dev/null
-# 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 'ÿ'
+++ /dev/null
-/*
- * 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 <linux/consolemap.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/irq.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/kbd_diacr.h>
-#include <linux/vt_kern.h>
-#include <linux/input.h>
-#include <linux/reboot.h>
-#include <linux/notifier.h>
-#include <linux/jiffies.h>
-
-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;
-}
+++ /dev/null
-/*
- * 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 <linux/types.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/ctype.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/bitops.h>
-#include <linux/file.h>
-#include <linux/uaccess.h>
-#include <linux/module.h>
-#include <linux/timer.h>
-#include <linux/tty_flip.h>
-#include <linux/tty_driver.h>
-#include <linux/serial.h>
-#include <linux/kfifo.h>
-#include <linux/skbuff.h>
-#include <linux/gsmmux.h>
-
-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);
+++ /dev/null
-/* 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 <callahan@maths.ox.ac.uk>,
- * Al Longyear <longyear@netcom.com>,
- * Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
- *
- * 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 <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-
-#undef VERSION
-#define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch))
-
-#include <linux/poll.h>
-#include <linux/in.h>
-#include <linux/ioctl.h>
-#include <linux/slab.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/smp_lock.h>
-#include <linux/string.h> /* used in new tty drivers */
-#include <linux/signal.h> /* used in new tty drivers */
-#include <linux/if.h>
-#include <linux/bitops.h>
-
-#include <asm/system.h>
-#include <asm/termios.h>
-#include <asm/uaccess.h>
-
-/*
- * 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;i<DEFAULT_RX_BUF_COUNT;i++) {
- buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL);
- if (buf)
- n_hdlc_buf_put(&n_hdlc->rx_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;i<DEFAULT_TX_BUF_COUNT;i++) {
- buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL);
- if (buf)
- n_hdlc_buf_put(&n_hdlc->tx_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);
+++ /dev/null
-/* 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 <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/string.h> /* used in new tty drivers */
-#include <linux/signal.h> /* used in new tty drivers */
-#include <linux/ioctl.h>
-#include <linux/n_r3964.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <asm/uaccess.h>
-
-/*#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);
+++ /dev/null
-/*
- * 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 <ag784@freenet.buffalo.edu>
- * 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 <sapan@corewars.org>.
- * Also fixed a bug in BLOCKING mode where n_tty_write returns
- * EAGAIN
- */
-
-#include <linux/types.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/timer.h>
-#include <linux/ctype.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/bitops.h>
-#include <linux/audit.h>
-#include <linux/file.h>
-#include <linux/uaccess.h>
-#include <linux/module.h>
-
-#include <asm/system.h>
-
-/* number of characters left in xmit buffer before select has we have room */
-#define WAKEUP_CHARS 256
-
-/*
- * This defines the low- and high-watermarks for throttling and
- * unthrottling the TTY driver. These watermarks are used for
- * controlling the space in the read buffer.
- */
-#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
-#define TTY_THRESHOLD_UNTHROTTLE 128
-
-/*
- * 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);
+++ /dev/null
-/*
- * linux/drivers/char/pty.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * Added support for a Unix98-style ptmx device.
- * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 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 <linux/module.h>
-
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/fcntl.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/major.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/smp_lock.h>
-#include <linux/sysctl.h>
-#include <linux/device.h>
-#include <linux/uaccess.h>
-#include <linux/bitops.h>
-#include <linux/devpts_fs.h>
-#include <linux/slab.h>
-
-#include <asm/system.h>
-
-#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);
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/tty.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-
-#include <asm/uaccess.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/consolemap.h>
-#include <linux/selection.h>
-#include <linux/tiocl.h>
-#include <linux/console.h>
-#include <linux/smp_lock.h>
-
-/* Don't take this from <ctype.h>: 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;
-}
+++ /dev/null
-/*
- * Linux Magic System Request Key Hacks
- *
- * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
- * based on ideas by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>
- *
- * (c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
- * 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 <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/mount.h>
-#include <linux/kdev_t.h>
-#include <linux/major.h>
-#include <linux/reboot.h>
-#include <linux/sysrq.h>
-#include <linux/kbd_kern.h>
-#include <linux/proc_fs.h>
-#include <linux/nmi.h>
-#include <linux/quotaops.h>
-#include <linux/perf_event.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/suspend.h>
-#include <linux/writeback.h>
-#include <linux/buffer_head.h> /* for fsync_bdev() */
-#include <linux/swap.h>
-#include <linux/spinlock.h>
-#include <linux/vt_kern.h>
-#include <linux/workqueue.h>
-#include <linux/hrtimer.h>
-#include <linux/oom.h>
-#include <linux/slab.h>
-#include <linux/input.h>
-
-#include <asm/ptrace.h>
-#include <asm/irq_regs.h>
-
-/* 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 <linux/ftrace.h>
-
-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);
+++ /dev/null
-/*
- * 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 <mitr@redhat.com>
- */
-
-#include <linux/audit.h>
-#include <linux/slab.h>
-#include <linux/tty.h>
-
-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);
- }
-}
+++ /dev/null
-/*
- * Tty buffer allocation management
- */
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/wait.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-
-/**
- * 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);
-}
-
+++ /dev/null
-/*
- * 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 <alfie@dcs.warwick.ac.uk>, 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 <Marko.Kohtala@hut.fi>, March 97
- *
- * Rewrote tty_init_dev and tty_release_dev to eliminate races.
- * -- Bill Hawes <whawes@star.net>, June 97
- *
- * Added devfs support.
- * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998
- *
- * Added support for a Unix98-style ptmx device.
- * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
- *
- * Reduced memory usage for older ARM systems
- * -- Russell King <rmk@arm.linux.org.uk>
- *
- * Move do_SAK() into process context. Less stack use in devfs functions.
- * alloc_tty_struct() always uses kmalloc()
- * -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01
- */
-
-#include <linux/types.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/devpts_fs.h>
-#include <linux/file.h>
-#include <linux/fdtable.h>
-#include <linux/console.h>
-#include <linux/timer.h>
-#include <linux/ctype.h>
-#include <linux/kd.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/smp_lock.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/seq_file.h>
-#include <linux/serial.h>
-
-#include <linux/uaccess.h>
-#include <asm/system.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/selection.h>
-
-#include <linux/kmod.h>
-#include <linux/nsproxy.h>
-
-#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;
-}
-
+++ /dev/null
-/*
- * 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 <linux/types.h>
-#include <linux/termios.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/tty.h>
-#include <linux/fcntl.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/bitops.h>
-#include <linux/mutex.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-
-#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);
+++ /dev/null
-#include <linux/types.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/devpts_fs.h>
-#include <linux/file.h>
-#include <linux/console.h>
-#include <linux/timer.h>
-#include <linux/ctype.h>
-#include <linux/kd.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/proc_fs.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/seq_file.h>
-
-#include <linux/uaccess.h>
-#include <asm/system.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/selection.h>
-
-#include <linux/smp_lock.h> /* For the moment */
-
-#include <linux/kmod.h>
-#include <linux/nsproxy.h>
-
-/*
- * 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);
-}
+++ /dev/null
-/*
- * drivers/char/tty_lock.c
- */
-#include <linux/tty.h>
-#include <linux/module.h>
-#include <linux/kallsyms.h>
-#include <linux/semaphore.h>
-#include <linux/sched.h>
-
-/*
- * 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);
+++ /dev/null
-/*
- * Tty port functions
- */
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/wait.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-
-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);
+++ /dev/null
-/*
- * 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 <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/tty.h>
-#include <linux/interrupt.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-#include <linux/vt_kern.h>
-#include <linux/selection.h>
-#include <linux/kbd_kern.h>
-#include <linux/console.h>
-#include <linux/device.h>
-#include <linux/smp_lock.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <linux/signal.h>
-#include <linux/slab.h>
-#include <linux/notifier.h>
-
-#include <asm/uaccess.h>
-#include <asm/byteorder.h>
-#include <asm/unaligned.h>
-
-#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;
-}
+++ /dev/null
-/*
- * 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,
- * <g-hunt@ee.utah.edu>
- *
- * Rudimentary ISO 10646/Unicode/UTF-8 character set support by
- * Markus Kuhn, <mskuhn@immd4.informatik.uni-erlangen.de>.
- *
- * 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
- * <poe@daimi.aau.dk>
- *
- * User-defined bell sound, new setterm control sequences and printk
- * redirection by Martin Mares <mj@k332.feld.cvut.cz> 19-Nov-95
- *
- * APM screenblank bug fixed Takashi Manabe <manabe@roy.dsl.tutics.tut.jp>
- *
- * Merge with the abstract console driver by Geert Uytterhoeven
- * <geert@linux-m68k.org>, Jan 1997.
- *
- * Original m68k console driver modifications by
- *
- * - Arno Griffioen <arno@usn.nl>
- * - David Carter <carter@cs.bris.ac.uk>
- *
- * 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 <linux/console.h> for more information).
- *
- * Support for changeable cursor shape
- * by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>, August 1997
- *
- * Ported to i386 and con_scrolldelta fixed
- * by Emmanuel Marty <core@ggi-project.org>, April 1998
- *
- * Resurrected character buffers in videoram plus lots of other trickery
- * by Martin Mares <mj@atrey.karlin.mff.cuni.cz>, 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 <atlka@pg.gda.pl>, Aug 2006
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/kd.h>
-#include <linux/slab.h>
-#include <linux/major.h>
-#include <linux/mm.h>
-#include <linux/console.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-#include <linux/vt_kern.h>
-#include <linux/selection.h>
-#include <linux/smp_lock.h>
-#include <linux/tiocl.h>
-#include <linux/kbd_kern.h>
-#include <linux/consolemap.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/pm.h>
-#include <linux/font.h>
-#include <linux/bitops.h>
-#include <linux/notifier.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <asm/system.h>
-#include <linux/uaccess.h>
-#include <linux/kdb.h>
-#include <linux/ctype.h>
-
-#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
+++ /dev/null
-/*
- * 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 <linux/types.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/tty.h>
-#include <linux/timer.h>
-#include <linux/kernel.h>
-#include <linux/compat.h>
-#include <linux/module.h>
-#include <linux/kd.h>
-#include <linux/vt.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/major.h>
-#include <linux/fs.h>
-#include <linux/console.h>
-#include <linux/consolemap.h>
-#include <linux/signal.h>
-#include <linux/smp_lock.h>
-#include <linux/timex.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#include <linux/kbd_kern.h>
-#include <linux/vt_kern.h>
-#include <linux/kbd_diacr.h>
-#include <linux/selection.h>
-
-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 <linux/syscalls.h>
-#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; i<MAX_NR_CONSOLES; i++)
- if (! VT_BUSY(i))
- vc_deallocate(i);
- release_console_sem();
- } else {
- /* deallocate a single console, if possible */
- arg--;
- if (VT_BUSY(arg))
- ret = -EBUSY;
- else if (arg) { /* leave 0 */
- acquire_console_sem();
- vc_deallocate(arg);
- release_console_sem();
- }
- }
- break;
-
- case VT_RESIZE:
- {
- struct vt_sizes __user *vtsizes = up;
- struct vc_data *vc;
-
- ushort ll,cc;
- if (!perm)
- goto eperm;
- if (get_user(ll, &vtsizes->v_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);
/* get hold of clock */
p->clk = clk_get(&p->pdev->dev, "cmt_fck");
if (IS_ERR(p->clk)) {
- dev_warn(&p->pdev->dev, "using deprecated clock lookup\n");
- p->clk = clk_get(&p->pdev->dev, cfg->clk);
- if (IS_ERR(p->clk)) {
- dev_err(&p->pdev->dev, "cannot get clock\n");
- ret = PTR_ERR(p->clk);
- goto err1;
- }
+ dev_err(&p->pdev->dev, "cannot get clock\n");
+ ret = PTR_ERR(p->clk);
+ goto err1;
}
if (resource_size(res) == 6) {
/* get hold of clock */
p->clk = clk_get(&p->pdev->dev, "mtu2_fck");
if (IS_ERR(p->clk)) {
- dev_warn(&p->pdev->dev, "using deprecated clock lookup\n");
- p->clk = clk_get(&p->pdev->dev, cfg->clk);
- if (IS_ERR(p->clk)) {
- dev_err(&p->pdev->dev, "cannot get clock\n");
- ret = PTR_ERR(p->clk);
- goto err1;
- }
+ dev_err(&p->pdev->dev, "cannot get clock\n");
+ ret = PTR_ERR(p->clk);
+ goto err1;
}
return sh_mtu2_register(p, (char *)dev_name(&p->pdev->dev),
/* get hold of clock */
p->clk = clk_get(&p->pdev->dev, "tmu_fck");
if (IS_ERR(p->clk)) {
- dev_warn(&p->pdev->dev, "using deprecated clock lookup\n");
- p->clk = clk_get(&p->pdev->dev, cfg->clk);
- if (IS_ERR(p->clk)) {
- dev_err(&p->pdev->dev, "cannot get clock\n");
- ret = PTR_ERR(p->clk);
- goto err1;
- }
+ dev_err(&p->pdev->dev, "cannot get clock\n");
+ ret = PTR_ERR(p->clk);
+ goto err1;
}
return sh_tmu_register(p, (char *)dev_name(&p->pdev->dev),
struct drm_crtc *tmp;
int crtc_mask = 1;
- WARN(!crtc, "checking null crtc?");
+ WARN(!crtc, "checking null crtc?\n");
dev = crtc->dev;
.addr = DDC_ADDR,
.flags = I2C_M_RD,
.len = len,
- .buf = buf + start,
+ .buf = buf,
}
};
static u8 *
drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
{
- int i, j = 0;
+ int i, j = 0, valid_extensions = 0;
u8 *block, *new;
if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL)
for (j = 1; j <= block[0x7e]; j++) {
for (i = 0; i < 4; i++) {
- if (drm_do_probe_ddc_edid(adapter, block, j,
- EDID_LENGTH))
+ if (drm_do_probe_ddc_edid(adapter,
+ block + (valid_extensions + 1) * EDID_LENGTH,
+ j, EDID_LENGTH))
goto out;
- if (drm_edid_block_valid(block + j * EDID_LENGTH))
+ if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH)) {
+ valid_extensions++;
break;
+ }
}
if (i == 4)
- goto carp;
+ dev_warn(connector->dev->dev,
+ "%s: Ignoring invalid EDID block %d.\n",
+ drm_get_connector_name(connector), j);
+ }
+
+ if (valid_extensions != block[0x7e]) {
+ block[EDID_LENGTH-1] += block[0x7e] - valid_extensions;
+ block[0x7e] = valid_extensions;
+ new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL);
+ if (!new)
+ goto out;
+ block = new;
}
return block;
module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
unsigned int i915_powersave = 1;
-module_param_named(powersave, i915_powersave, int, 0400);
+module_param_named(powersave, i915_powersave, int, 0600);
unsigned int i915_lvds_downclock = 0;
module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400);
#define INTEL_PCH_TYPE(dev) (((struct drm_i915_private *)(dev)->dev_private)->pch_type)
#define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT)
+#define HAS_PCH_IBX(dev) (INTEL_PCH_TYPE(dev) == PCH_IBX)
#define PRIMARY_RINGBUFFER_SIZE (128*1024)
static int i915_ring_idle(struct drm_device *dev,
struct intel_ring_buffer *ring)
{
- if (list_empty(&ring->gpu_write_list))
+ if (list_empty(&ring->gpu_write_list) && list_empty(&ring->active_list))
return 0;
i915_gem_flush_ring(dev, NULL, ring,
int ret;
lists_empty = (list_empty(&dev_priv->mm.flushing_list) &&
- list_empty(&dev_priv->render_ring.active_list) &&
- list_empty(&dev_priv->bsd_ring.active_list) &&
- list_empty(&dev_priv->blt_ring.active_list));
+ list_empty(&dev_priv->mm.active_list));
if (lists_empty)
return 0;
* write domain
*/
if (obj->write_domain &&
- obj->write_domain != obj->pending_read_domains) {
+ (obj->write_domain != obj->pending_read_domains ||
+ obj_priv->ring != ring)) {
flush_domains |= obj->write_domain;
invalidate_domains |=
obj->pending_read_domains & ~obj->write_domain;
return 0;
}
+static int
+i915_gem_execbuffer_move_to_gpu(struct drm_device *dev,
+ struct drm_file *file,
+ struct intel_ring_buffer *ring,
+ struct drm_gem_object **objects,
+ int count)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int ret, i;
+
+ /* Zero the global flush/invalidate flags. These
+ * will be modified as new domains are computed
+ * for each object
+ */
+ dev->invalidate_domains = 0;
+ dev->flush_domains = 0;
+ dev_priv->mm.flush_rings = 0;
+ for (i = 0; i < count; i++)
+ i915_gem_object_set_to_gpu_domain(objects[i], ring);
+
+ if (dev->invalidate_domains | dev->flush_domains) {
+#if WATCH_EXEC
+ DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n",
+ __func__,
+ dev->invalidate_domains,
+ dev->flush_domains);
+#endif
+ i915_gem_flush(dev, file,
+ dev->invalidate_domains,
+ dev->flush_domains,
+ dev_priv->mm.flush_rings);
+ }
+
+ for (i = 0; i < count; i++) {
+ struct drm_i915_gem_object *obj = to_intel_bo(objects[i]);
+ /* XXX replace with semaphores */
+ if (obj->ring && ring != obj->ring) {
+ ret = i915_gem_object_wait_rendering(&obj->base, true);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
/* Throttle our rendering by waiting until the ring has completed our requests
* emitted over 20 msec ago.
*
goto err;
}
- /* Zero the global flush/invalidate flags. These
- * will be modified as new domains are computed
- * for each object
- */
- dev->invalidate_domains = 0;
- dev->flush_domains = 0;
- dev_priv->mm.flush_rings = 0;
-
- for (i = 0; i < args->buffer_count; i++) {
- struct drm_gem_object *obj = object_list[i];
-
- /* Compute new gpu domains and update invalidate/flush */
- i915_gem_object_set_to_gpu_domain(obj, ring);
- }
-
- if (dev->invalidate_domains | dev->flush_domains) {
-#if WATCH_EXEC
- DRM_INFO("%s: invalidate_domains %08x flush_domains %08x\n",
- __func__,
- dev->invalidate_domains,
- dev->flush_domains);
-#endif
- i915_gem_flush(dev, file,
- dev->invalidate_domains,
- dev->flush_domains,
- dev_priv->mm.flush_rings);
- }
+ ret = i915_gem_execbuffer_move_to_gpu(dev, file, ring,
+ object_list, args->buffer_count);
+ if (ret)
+ goto err;
for (i = 0; i < args->buffer_count; i++) {
struct drm_gem_object *obj = object_list[i];
alignment = i915_gem_get_gtt_alignment(obj);
if (obj_priv->gtt_offset & (alignment - 1)) {
WARN(obj_priv->pin_count,
- "bo is already pinned with incorrect alignment:"
- " offset=%x, req.alignment=%x\n",
+ "bo is already pinned with incorrect alignment: offset=%x, req.alignment=%x\n",
obj_priv->gtt_offset, alignment);
ret = i915_gem_object_unbind(obj);
if (ret)
struct drm_file *file_priv)
{
struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
- void *obj_addr;
- int ret;
- char __user *user_data;
+ void *vaddr = obj_priv->phys_obj->handle->vaddr + args->offset;
+ char __user *user_data = (char __user *) (uintptr_t) args->data_ptr;
- user_data = (char __user *) (uintptr_t) args->data_ptr;
- obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset;
+ DRM_DEBUG_DRIVER("vaddr %p, %lld\n", vaddr, args->size);
- DRM_DEBUG_DRIVER("obj_addr %p, %lld\n", obj_addr, args->size);
- ret = copy_from_user(obj_addr, user_data, args->size);
- if (ret)
- return -EFAULT;
+ if (__copy_from_user_inatomic_nocache(vaddr, user_data, args->size)) {
+ unsigned long unwritten;
+
+ /* The physical object once assigned is fixed for the lifetime
+ * of the obj, so we can safely drop the lock and continue
+ * to access vaddr.
+ */
+ mutex_unlock(&dev->struct_mutex);
+ unwritten = copy_from_user(vaddr, user_data, args->size);
+ mutex_lock(&dev->struct_mutex);
+ if (unwritten)
+ return -EFAULT;
+ }
drm_agp_chipset_flush(dev);
return 0;
int lists_empty;
lists_empty = list_empty(&dev_priv->mm.flushing_list) &&
- list_empty(&dev_priv->render_ring.active_list) &&
- list_empty(&dev_priv->bsd_ring.active_list) &&
- list_empty(&dev_priv->blt_ring.active_list);
+ list_empty(&dev_priv->mm.active_list);
return !lists_empty;
}
lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
list_empty(&dev_priv->mm.flushing_list) &&
- list_empty(&dev_priv->render_ring.active_list) &&
- list_empty(&dev_priv->bsd_ring.active_list) &&
- list_empty(&dev_priv->blt_ring.active_list));
+ list_empty(&dev_priv->mm.active_list));
if (lists_empty)
return -ENOSPC;
lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
list_empty(&dev_priv->mm.flushing_list) &&
- list_empty(&dev_priv->render_ring.active_list) &&
- list_empty(&dev_priv->bsd_ring.active_list) &&
- list_empty(&dev_priv->blt_ring.active_list));
+ list_empty(&dev_priv->mm.active_list));
BUG_ON(!lists_empty);
return 0;
/* Clock gating state */
intel_init_clock_gating(dev);
- if (HAS_PCH_SPLIT(dev))
+ if (HAS_PCH_SPLIT(dev)) {
ironlake_enable_drps(dev);
+ intel_init_emon(dev);
+ }
/* Cache mode state */
I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
udelay(500);
}
+static void intel_fdi_normal_train(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ u32 reg, temp;
+
+ /* enable normal train */
+ reg = FDI_TX_CTL(pipe);
+ temp = I915_READ(reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
+ I915_WRITE(reg, temp);
+
+ reg = FDI_RX_CTL(pipe);
+ temp = I915_READ(reg);
+ if (HAS_PCH_CPT(dev)) {
+ temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
+ temp |= FDI_LINK_TRAIN_NORMAL_CPT;
+ } else {
+ temp &= ~FDI_LINK_TRAIN_NONE;
+ temp |= FDI_LINK_TRAIN_NONE;
+ }
+ I915_WRITE(reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE);
+
+ /* wait one idle pattern time */
+ POSTING_READ(reg);
+ udelay(1000);
+}
+
/* The FDI link training functions for ILK/Ibexpeak. */
static void ironlake_fdi_link_train(struct drm_crtc *crtc)
{
DRM_DEBUG_KMS("FDI train done\n");
- /* enable normal train */
- reg = FDI_TX_CTL(pipe);
- temp = I915_READ(reg);
- temp &= ~FDI_LINK_TRAIN_NONE;
- temp |= FDI_LINK_TRAIN_NONE | FDI_TX_ENHANCE_FRAME_ENABLE;
- I915_WRITE(reg, temp);
-
- reg = FDI_RX_CTL(pipe);
- temp = I915_READ(reg);
- if (HAS_PCH_CPT(dev)) {
- temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
- temp |= FDI_LINK_TRAIN_NORMAL_CPT;
- } else {
- temp &= ~FDI_LINK_TRAIN_NONE;
- temp |= FDI_LINK_TRAIN_NONE;
- }
- I915_WRITE(reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE);
-
- /* wait one idle pattern time */
- POSTING_READ(reg);
- udelay(1000);
}
static const int const snb_b_fdi_train_param [] = {
I915_WRITE(TRANS_VBLANK(pipe), I915_READ(VBLANK(pipe)));
I915_WRITE(TRANS_VSYNC(pipe), I915_READ(VSYNC(pipe)));
+ intel_fdi_normal_train(crtc);
+
/* For PCH DP, enable TRANS_DP_CTL */
if (HAS_PCH_CPT(dev) &&
intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
udelay(100);
/* Ironlake workaround, disable clock pointer after downing FDI */
- I915_WRITE(FDI_RX_CHICKEN(pipe),
- I915_READ(FDI_RX_CHICKEN(pipe) &
- ~FDI_RX_PHASE_SYNC_POINTER_ENABLE));
+ if (HAS_PCH_IBX(dev))
+ I915_WRITE(FDI_RX_CHICKEN(pipe),
+ I915_READ(FDI_RX_CHICKEN(pipe) &
+ ~FDI_RX_PHASE_SYNC_POINTER_ENABLE));
/* still set train pattern 1 */
reg = FDI_TX_CTL(pipe);
fmin = (rgvmodectl & MEMMODE_FMIN_MASK);
fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >>
MEMMODE_FSTART_SHIFT;
- fstart = fmax;
vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >>
PXVFREQ_PX_SHIFT;
- dev_priv->fmax = fstart; /* IPS callback will increase this */
+ dev_priv->fmax = fmax; /* IPS callback will increase this */
dev_priv->fstart = fstart;
- dev_priv->max_delay = fmax;
+ dev_priv->max_delay = fstart;
dev_priv->min_delay = fmin;
dev_priv->cur_delay = fstart;
- DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n", fmax, fmin,
- fstart);
+ DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n",
+ fmax, fmin, fstart);
I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN);
status = connector_status_connected;
}
- return bit;
+ return status;
}
/**
extern void intel_init_clock_gating(struct drm_device *dev);
extern void ironlake_enable_drps(struct drm_device *dev);
extern void ironlake_disable_drps(struct drm_device *dev);
+extern void intel_init_emon(struct drm_device *dev);
extern int intel_pin_and_fence_fb_obj(struct drm_device *dev,
struct drm_gem_object *obj,
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode;
- if (intel_lvds->edid) {
- drm_mode_connector_update_edid_property(connector,
- intel_lvds->edid);
+ if (intel_lvds->edid)
return drm_add_edid_modes(connector, intel_lvds->edid);
- }
mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode);
if (mode == 0)
*/
intel_lvds->edid = drm_get_edid(connector,
&dev_priv->gmbus[pin].adapter);
-
+ if (intel_lvds->edid) {
+ if (drm_add_edid_modes(connector,
+ intel_lvds->edid)) {
+ drm_mode_connector_update_edid_property(connector,
+ intel_lvds->edid);
+ } else {
+ kfree(intel_lvds->edid);
+ intel_lvds->edid = NULL;
+ }
+ }
if (!intel_lvds->edid) {
/* Didn't get an EDID, so
* Set wide sync ranges so we get all modes
return 0;
err_out:
- iounmap(opregion->header);
+ iounmap(base);
return err;
}
{
int uv_hscale = uv_hsubsampling(rec->flags);
int uv_vscale = uv_vsubsampling(rec->flags);
- u32 stride_mask, depth, tmp;
+ u32 stride_mask;
+ int depth;
+ u32 tmp;
/* check src dimensions */
if (IS_845G(dev) || IS_I830(dev)) {
I915_WRITE_CTL(ring,
((ring->gem_object->size - PAGE_SIZE) & RING_NR_PAGES)
- | RING_NO_REPORT | RING_VALID);
+ | RING_REPORT_64K | RING_VALID);
head = I915_READ_HEAD(ring) & HEAD_ADDR;
/* If the head is still not zero, the ring is dead */
i915_gem_object_unpin(ring->gem_object);
drm_gem_object_unreference(ring->gem_object);
ring->gem_object = NULL;
+
+ if (ring->cleanup)
+ ring->cleanup(ring);
+
cleanup_status_page(dev, ring);
}
{
unsigned long end;
drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 head;
+
+ head = intel_read_status_page(ring, 4);
+ if (head) {
+ ring->head = head & HEAD_ADDR;
+ ring->space = ring->head - (ring->tail + 8);
+ if (ring->space < 0)
+ ring->space += ring->size;
+ if (ring->space >= n)
+ return 0;
+ }
trace_i915_ring_wait_begin (dev);
end = jiffies + 3 * HZ;
/* do nothing */
}
+
+/* Workaround for some stepping of SNB,
+ * each time when BLT engine ring tail moved,
+ * the first command in the ring to be parsed
+ * should be MI_BATCH_BUFFER_START
+ */
+#define NEED_BLT_WORKAROUND(dev) \
+ (IS_GEN6(dev) && (dev->pdev->revision < 8))
+
+static inline struct drm_i915_gem_object *
+to_blt_workaround(struct intel_ring_buffer *ring)
+{
+ return ring->private;
+}
+
+static int blt_ring_init(struct drm_device *dev,
+ struct intel_ring_buffer *ring)
+{
+ if (NEED_BLT_WORKAROUND(dev)) {
+ struct drm_i915_gem_object *obj;
+ u32 __iomem *ptr;
+ int ret;
+
+ obj = to_intel_bo(i915_gem_alloc_object(dev, 4096));
+ if (obj == NULL)
+ return -ENOMEM;
+
+ ret = i915_gem_object_pin(&obj->base, 4096);
+ if (ret) {
+ drm_gem_object_unreference(&obj->base);
+ return ret;
+ }
+
+ ptr = kmap(obj->pages[0]);
+ iowrite32(MI_BATCH_BUFFER_END, ptr);
+ iowrite32(MI_NOOP, ptr+1);
+ kunmap(obj->pages[0]);
+
+ ret = i915_gem_object_set_to_gtt_domain(&obj->base, false);
+ if (ret) {
+ i915_gem_object_unpin(&obj->base);
+ drm_gem_object_unreference(&obj->base);
+ return ret;
+ }
+
+ ring->private = obj;
+ }
+
+ return init_ring_common(dev, ring);
+}
+
+static void blt_ring_begin(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ int num_dwords)
+{
+ if (ring->private) {
+ intel_ring_begin(dev, ring, num_dwords+2);
+ intel_ring_emit(dev, ring, MI_BATCH_BUFFER_START);
+ intel_ring_emit(dev, ring, to_blt_workaround(ring)->gtt_offset);
+ } else
+ intel_ring_begin(dev, ring, 4);
+}
+
+static void blt_ring_flush(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ u32 invalidate_domains,
+ u32 flush_domains)
+{
+ blt_ring_begin(dev, ring, 4);
+ intel_ring_emit(dev, ring, MI_FLUSH_DW);
+ intel_ring_emit(dev, ring, 0);
+ intel_ring_emit(dev, ring, 0);
+ intel_ring_emit(dev, ring, 0);
+ intel_ring_advance(dev, ring);
+}
+
+static u32
+blt_ring_add_request(struct drm_device *dev,
+ struct intel_ring_buffer *ring,
+ u32 flush_domains)
+{
+ u32 seqno = i915_gem_get_seqno(dev);
+
+ blt_ring_begin(dev, ring, 4);
+ intel_ring_emit(dev, ring, MI_STORE_DWORD_INDEX);
+ intel_ring_emit(dev, ring,
+ I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
+ intel_ring_emit(dev, ring, seqno);
+ intel_ring_emit(dev, ring, MI_USER_INTERRUPT);
+ intel_ring_advance(dev, ring);
+
+ DRM_DEBUG_DRIVER("%s %d\n", ring->name, seqno);
+ return seqno;
+}
+
+static void blt_ring_cleanup(struct intel_ring_buffer *ring)
+{
+ if (!ring->private)
+ return;
+
+ i915_gem_object_unpin(ring->private);
+ drm_gem_object_unreference(ring->private);
+ ring->private = NULL;
+}
+
static const struct intel_ring_buffer gen6_blt_ring = {
.name = "blt ring",
.id = RING_BLT,
.mmio_base = BLT_RING_BASE,
.size = 32 * PAGE_SIZE,
- .init = init_ring_common,
+ .init = blt_ring_init,
.write_tail = ring_write_tail,
- .flush = gen6_ring_flush,
- .add_request = ring_add_request,
+ .flush = blt_ring_flush,
+ .add_request = blt_ring_add_request,
.get_seqno = ring_status_page_get_seqno,
.user_irq_get = blt_ring_get_user_irq,
.user_irq_put = blt_ring_put_user_irq,
.dispatch_gem_execbuffer = gen6_ring_dispatch_gem_execbuffer,
+ .cleanup = blt_ring_cleanup,
};
int intel_init_render_ring_buffer(struct drm_device *dev)
struct drm_i915_gem_execbuffer2 *exec,
struct drm_clip_rect *cliprects,
uint64_t exec_offset);
+ void (*cleanup)(struct intel_ring_buffer *ring);
/**
* List of objects currently involved in rendering from the
wait_queue_head_t irq_queue;
drm_local_map_t map;
+
+ void *private;
};
static inline u32
u32 grbm_int_cntl = 0;
if (!rdev->irq.installed) {
- WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
+ WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
return -EINVAL;
}
/* don't enable anything if the ih is disabled */
case 0: /* D1 vblank */
if (disp_int & LB_D1_VBLANK_INTERRUPT) {
drm_handle_vblank(rdev->ddev, 0);
+ rdev->pm.vblank_sync = true;
wake_up(&rdev->irq.vblank_queue);
disp_int &= ~LB_D1_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D1 vblank\n");
case 0: /* D2 vblank */
if (disp_int_cont & LB_D2_VBLANK_INTERRUPT) {
drm_handle_vblank(rdev->ddev, 1);
+ rdev->pm.vblank_sync = true;
wake_up(&rdev->irq.vblank_queue);
disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D2 vblank\n");
case 0: /* D3 vblank */
if (disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) {
drm_handle_vblank(rdev->ddev, 2);
+ rdev->pm.vblank_sync = true;
wake_up(&rdev->irq.vblank_queue);
disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D3 vblank\n");
case 0: /* D4 vblank */
if (disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) {
drm_handle_vblank(rdev->ddev, 3);
+ rdev->pm.vblank_sync = true;
wake_up(&rdev->irq.vblank_queue);
disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D4 vblank\n");
case 0: /* D5 vblank */
if (disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) {
drm_handle_vblank(rdev->ddev, 4);
+ rdev->pm.vblank_sync = true;
wake_up(&rdev->irq.vblank_queue);
disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D5 vblank\n");
case 0: /* D6 vblank */
if (disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) {
drm_handle_vblank(rdev->ddev, 5);
+ rdev->pm.vblank_sync = true;
wake_up(&rdev->irq.vblank_queue);
disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT;
DRM_DEBUG("IH: D6 vblank\n");
int r;
if (rdev->gart.table.ram.ptr) {
- WARN(1, "R100 PCI GART already initialized.\n");
+ WARN(1, "R100 PCI GART already initialized\n");
return 0;
}
/* Initialize common gart structure */
uint32_t tmp = 0;
if (!rdev->irq.installed) {
- WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
+ WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
WREG32(R_000040_GEN_INT_CNTL, 0);
return -EINVAL;
}
int r;
if (rdev->gart.table.vram.robj) {
- WARN(1, "RV370 PCIE GART already initialized.\n");
+ WARN(1, "RV370 PCIE GART already initialized\n");
return 0;
}
/* Initialize common gart structure */
{
u32 temp = (RREG32(CG_THERMAL_STATUS) & ASIC_T_MASK) >>
ASIC_T_SHIFT;
- u32 actual_temp = 0;
- if ((temp >> 7) & 1)
- actual_temp = 0;
- else
- actual_temp = (temp >> 1) & 0xff;
-
- return actual_temp * 1000;
+ return temp * 1000;
}
void r600_pm_get_dynpm_state(struct radeon_device *rdev)
int r;
if (rdev->gart.table.vram.robj) {
- WARN(1, "R600 PCIE GART already initialized.\n");
+ WARN(1, "R600 PCIE GART already initialized\n");
return 0;
}
/* Initialize common gart structure */
u32 hdmi1, hdmi2;
if (!rdev->irq.installed) {
- WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
+ WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
return -EINVAL;
}
/* don't enable anything if the ih is disabled */
if (crev < 2)
return false;
- router.valid = false;
-
obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset);
path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *)
(ctx->bios + data_offset +
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
continue;
+ router.ddc_valid = false;
+ router.cd_valid = false;
for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) {
uint8_t grph_obj_id, grph_obj_num, grph_obj_type;
usDeviceTag));
} else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) {
- router.valid = false;
for (k = 0; k < router_obj->ucNumberOfObjects; k++) {
- u16 router_obj_id = le16_to_cpu(router_obj->asObjects[j].usObjectID);
+ u16 router_obj_id = le16_to_cpu(router_obj->asObjects[k].usObjectID);
if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) {
ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *)
(ctx->bios + data_offset +
ATOM_I2C_RECORD *i2c_record;
ATOM_I2C_ID_CONFIG_ACCESS *i2c_config;
ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path;
+ ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *cd_path;
ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table =
(ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *)
(ctx->bios + data_offset +
case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE:
ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *)
record;
- router.valid = true;
- router.mux_type = ddc_path->ucMuxType;
- router.mux_control_pin = ddc_path->ucMuxControlPin;
- router.mux_state = ddc_path->ucMuxState[enum_id];
+ router.ddc_valid = true;
+ router.ddc_mux_type = ddc_path->ucMuxType;
+ router.ddc_mux_control_pin = ddc_path->ucMuxControlPin;
+ router.ddc_mux_state = ddc_path->ucMuxState[enum_id];
+ break;
+ case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE:
+ cd_path = (ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *)
+ record;
+ router.cd_valid = true;
+ router.cd_mux_type = cd_path->ucMuxType;
+ router.cd_mux_control_pin = cd_path->ucMuxControlPin;
+ router.cd_mux_state = cd_path->ucMuxState[enum_id];
break;
}
record = (ATOM_COMMON_RECORD_HEADER *)
size_t bc_size = sizeof(*bios_connectors) * ATOM_MAX_SUPPORTED_DEVICE;
struct radeon_router router;
- router.valid = false;
+ router.ddc_valid = false;
+ router.cd_valid = false;
bios_connectors = kzalloc(bc_size, GFP_KERNEL);
if (!bios_connectors)
continue;
if (priority == true) {
- DRM_INFO("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict));
- DRM_INFO("in favor of %s\n", drm_get_connector_name(connector));
+ DRM_DEBUG_KMS("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict));
+ DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(connector));
conflict->status = connector_status_disconnected;
radeon_connector_update_scratch_regs(conflict, connector_status_disconnected);
} else {
- DRM_INFO("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector));
- DRM_INFO("in favor of %s\n", drm_get_connector_name(conflict));
+ DRM_DEBUG_KMS("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector));
+ DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(conflict));
current_status = connector_status_disconnected;
}
break;
mode->vdisplay == native_mode->vdisplay) {
*native_mode = *mode;
drm_mode_set_crtcinfo(native_mode, CRTC_INTERLACE_HALVE_V);
- DRM_INFO("Determined LVDS native mode details from EDID\n");
+ DRM_DEBUG_KMS("Determined LVDS native mode details from EDID\n");
break;
}
}
}
if (!native_mode->clock) {
- DRM_INFO("No LVDS native mode details, disabling RMX\n");
+ DRM_DEBUG_KMS("No LVDS native mode details, disabling RMX\n");
radeon_encoder->rmx_type = RMX_OFF;
}
}
radeon_connector->shared_ddc = true;
shared_ddc = true;
}
- if (radeon_connector->router_bus && router->valid &&
+ if (radeon_connector->router_bus && router->ddc_valid &&
(radeon_connector->router.router_id == router->router_id)) {
radeon_connector->shared_ddc = false;
shared_ddc = false;
radeon_connector->connector_object_id = connector_object_id;
radeon_connector->hpd = *hpd;
radeon_connector->router = *router;
- if (router->valid) {
+ if (router->ddc_valid || router->cd_valid) {
radeon_connector->router_bus = radeon_i2c_lookup(rdev, &router->i2c_info);
if (!radeon_connector->router_bus)
goto failed;
radeon_connector->ddc_bus->rec.en_data_reg,
radeon_connector->ddc_bus->rec.y_clk_reg,
radeon_connector->ddc_bus->rec.y_data_reg);
- if (radeon_connector->router_bus)
+ if (radeon_connector->router.ddc_valid)
DRM_INFO(" DDC Router 0x%x/0x%x\n",
- radeon_connector->router.mux_control_pin,
- radeon_connector->router.mux_state);
+ radeon_connector->router.ddc_mux_control_pin,
+ radeon_connector->router.ddc_mux_state);
+ if (radeon_connector->router.cd_valid)
+ DRM_INFO(" Clock/Data Router 0x%x/0x%x\n",
+ radeon_connector->router.cd_mux_control_pin,
+ radeon_connector->router.cd_mux_state);
} else {
if (connector->connector_type == DRM_MODE_CONNECTOR_VGA ||
connector->connector_type == DRM_MODE_CONNECTOR_DVII ||
int ret = 0;
/* on hw with routers, select right port */
- if (radeon_connector->router.valid)
- radeon_router_select_port(radeon_connector);
+ if (radeon_connector->router.ddc_valid)
+ radeon_router_select_ddc_port(radeon_connector);
if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
(radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
int ret = 0;
/* on hw with routers, select right port */
- if (radeon_connector->router.valid)
- radeon_router_select_port(radeon_connector);
+ if (radeon_connector->router.ddc_valid)
+ radeon_router_select_ddc_port(radeon_connector);
if (!radeon_connector->ddc_bus)
return -1;
static void radeon_atom_encoder_prepare(struct drm_encoder *encoder)
{
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
if (radeon_encoder->active_device &
(ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) {
radeon_atom_output_lock(encoder, true);
radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+ /* select the clock/data port if it uses a router */
+ if (connector) {
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ if (radeon_connector->router.cd_valid)
+ radeon_router_select_cd_port(radeon_connector);
+ }
+
/* this is needed for the pll/ss setup to work correctly in some cases */
atombios_set_encoder_crtc_source(encoder);
}
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig;
+
+ /* check for pre-DCE3 cards with shared encoders;
+ * can't really use the links individually, so don't disable
+ * the encoder if it's in use by another connector
+ */
+ if (!ASIC_IS_DCE3(rdev)) {
+ struct drm_encoder *other_encoder;
+ struct radeon_encoder *other_radeon_encoder;
+
+ list_for_each_entry(other_encoder, &dev->mode_config.encoder_list, head) {
+ other_radeon_encoder = to_radeon_encoder(other_encoder);
+ if ((radeon_encoder->encoder_id == other_radeon_encoder->encoder_id) &&
+ drm_helper_encoder_in_use(other_encoder))
+ goto disable_done;
+ }
+ }
+
radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
switch (radeon_encoder->encoder_id) {
break;
}
+disable_done:
if (radeon_encoder_is_digital(encoder)) {
if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI)
r600_hdmi_disable(encoder);
*/
if (seq == rdev->fence_drv.last_seq && radeon_gpu_is_lockup(rdev)) {
/* good news we believe it's a lockup */
- WARN(1, "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n", fence->seq, seq);
+ WARN(1, "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n",
+ fence->seq, seq);
/* FIXME: what should we do ? marking everyone
* as signaled for now
*/
};
/* on hw with routers, select right port */
- if (radeon_connector->router.valid)
- radeon_router_select_port(radeon_connector);
+ if (radeon_connector->router.ddc_valid)
+ radeon_router_select_ddc_port(radeon_connector);
ret = i2c_transfer(&radeon_connector->ddc_bus->adapter, msgs, 2);
if (ret == 2)
addr, val);
}
-/* router switching */
-void radeon_router_select_port(struct radeon_connector *radeon_connector)
+/* ddc router switching */
+void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector)
{
u8 val;
- if (!radeon_connector->router.valid)
+ if (!radeon_connector->router.ddc_valid)
return;
radeon_i2c_get_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x3, &val);
- val &= radeon_connector->router.mux_control_pin;
+ val &= ~radeon_connector->router.ddc_mux_control_pin;
radeon_i2c_put_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x3, val);
radeon_i2c_get_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x1, &val);
- val &= radeon_connector->router.mux_control_pin;
- val |= radeon_connector->router.mux_state;
+ val &= ~radeon_connector->router.ddc_mux_control_pin;
+ val |= radeon_connector->router.ddc_mux_state;
+ radeon_i2c_put_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x1, val);
+}
+
+/* clock/data router switching */
+void radeon_router_select_cd_port(struct radeon_connector *radeon_connector)
+{
+ u8 val;
+
+ if (!radeon_connector->router.cd_valid)
+ return;
+
+ radeon_i2c_get_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x3, &val);
+ val &= ~radeon_connector->router.cd_mux_control_pin;
+ radeon_i2c_put_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x3, val);
+ radeon_i2c_get_byte(radeon_connector->router_bus,
+ radeon_connector->router.i2c_addr,
+ 0x1, &val);
+ val &= ~radeon_connector->router.cd_mux_control_pin;
+ val |= radeon_connector->router.cd_mux_state;
radeon_i2c_put_byte(radeon_connector->router_bus,
radeon_connector->router.i2c_addr,
0x1, val);
};
struct radeon_router {
- bool valid;
u32 router_id;
struct radeon_i2c_bus_rec i2c_info;
u8 i2c_addr;
- u8 mux_type;
- u8 mux_control_pin;
- u8 mux_state;
+ /* i2c mux */
+ bool ddc_valid;
+ u8 ddc_mux_type;
+ u8 ddc_mux_control_pin;
+ u8 ddc_mux_state;
+ /* clock/data mux */
+ bool cd_valid;
+ u8 cd_mux_type;
+ u8 cd_mux_control_pin;
+ u8 cd_mux_state;
};
struct radeon_connector {
u8 slave_addr,
u8 addr,
u8 val);
-extern void radeon_router_select_port(struct radeon_connector *radeon_connector);
+extern void radeon_router_select_ddc_port(struct radeon_connector *radeon_connector);
+extern void radeon_router_select_cd_port(struct radeon_connector *radeon_connector);
extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
type = ttm_bo_type_device;
}
*bo_ptr = NULL;
+
+retry:
bo = kzalloc(sizeof(struct radeon_bo), GFP_KERNEL);
if (bo == NULL)
return -ENOMEM;
bo->gobj = gobj;
bo->surface_reg = -1;
INIT_LIST_HEAD(&bo->list);
-
-retry:
radeon_ttm_placement_from_domain(bo, domain);
/* Kernel allocation are uninterruptible */
mutex_lock(&rdev->vram_mutex);
gtt = container_of(backend, struct radeon_ttm_backend, backend);
gtt->offset = bo_mem->start << PAGE_SHIFT;
if (!gtt->num_pages) {
- WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n", gtt->num_pages, bo_mem, backend);
+ WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n",
+ gtt->num_pages, bo_mem, backend);
}
r = radeon_gart_bind(gtt->rdev, gtt->offset,
gtt->num_pages, gtt->pages);
int r;
if (rdev->gart.table.ram.ptr) {
- WARN(1, "RS400 GART already initialized.\n");
+ WARN(1, "RS400 GART already initialized\n");
return 0;
}
/* Check gart size */
int r;
if (rdev->gart.table.vram.robj) {
- WARN(1, "RS600 GART already initialized.\n");
+ WARN(1, "RS600 GART already initialized\n");
return 0;
}
/* Initialize common gart structure */
~S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(1);
if (!rdev->irq.installed) {
- WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
+ WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
WREG32(R_000040_GEN_INT_CNTL, 0);
return -EINVAL;
}
/*
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
*/
-/* Notes:
- *
- * We store bo pointer in drm_mm_node struct so we know which bo own a
- * specific node. There is no protection on the pointer, thus to make
- * sure things don't go berserk you have to access this pointer while
- * holding the global lru lock and make sure anytime you free a node you
- * reset the pointer to NULL.
- */
#include "ttm/ttm_module.h"
#include "ttm/ttm_bo_driver.h"
#include <linux/mm.h>
#include <linux/file.h>
#include <linux/module.h>
+#include <asm/atomic.h>
#define TTM_ASSERT_LOCKED(param)
#define TTM_DEBUG(fmt, arg...)
ttm_bo_mem_put(bo, &bo->mem);
atomic_set(&bo->reserved, 0);
+
+ /*
+ * Make processes trying to reserve really pick it up.
+ */
+ smp_mb__after_atomic_dec();
wake_up_all(&bo->event_queue);
}
struct ttm_bo_device *bdev = bo->bdev;
struct ttm_bo_global *glob = bo->glob;
struct ttm_bo_driver *driver;
- void *sync_obj;
+ void *sync_obj = NULL;
void *sync_obj_arg;
int put_count;
int ret;
spin_lock(&glob->lru_lock);
}
queue:
- sync_obj = bo->sync_obj;
- sync_obj_arg = bo->sync_obj_arg;
driver = bdev->driver;
+ if (bo->sync_obj)
+ sync_obj = driver->sync_obj_ref(bo->sync_obj);
+ sync_obj_arg = bo->sync_obj_arg;
kref_get(&bo->list_kref);
list_add_tail(&bo->ddestroy, &bdev->ddestroy);
spin_unlock(&glob->lru_lock);
spin_unlock(&bo->lock);
- if (sync_obj)
+ if (sync_obj) {
driver->sync_obj_flush(sync_obj, sync_obj_arg);
+ driver->sync_obj_unref(&sync_obj);
+ }
schedule_delayed_work(&bdev->wq,
((HZ / 100) < 1) ? 1 : HZ / 100);
}
bool no_wait_gpu)
{
struct ttm_bo_device *bdev = bo->bdev;
- struct ttm_bo_global *glob = bdev->glob;
struct ttm_mem_type_manager *man = &bdev->man[mem_type];
int ret;
return ret;
if (mem->mm_node)
break;
- spin_lock(&glob->lru_lock);
- if (list_empty(&man->lru)) {
- spin_unlock(&glob->lru_lock);
- break;
- }
- spin_unlock(&glob->lru_lock);
ret = ttm_mem_evict_first(bdev, mem_type, interruptible,
no_wait_reserve, no_wait_gpu);
if (unlikely(ret != 0))
int ttm_bo_check_placement(struct ttm_buffer_object *bo,
struct ttm_placement *placement)
{
- int i;
+ BUG_ON((placement->fpfn || placement->lpfn) &&
+ (bo->mem.num_pages > (placement->lpfn - placement->fpfn)));
- if (placement->fpfn || placement->lpfn) {
- if (bo->mem.num_pages > (placement->lpfn - placement->fpfn)) {
- printk(KERN_ERR TTM_PFX "Page number range to small "
- "Need %lu pages, range is [%u, %u]\n",
- bo->mem.num_pages, placement->fpfn,
- placement->lpfn);
- return -EINVAL;
- }
- }
- for (i = 0; i < placement->num_placement; i++) {
- if (!capable(CAP_SYS_ADMIN)) {
- if (placement->placement[i] & TTM_PL_FLAG_NO_EVICT) {
- printk(KERN_ERR TTM_PFX "Need to be root to "
- "modify NO_EVICT status.\n");
- return -EINVAL;
- }
- }
- }
- for (i = 0; i < placement->num_busy_placement; i++) {
- if (!capable(CAP_SYS_ADMIN)) {
- if (placement->busy_placement[i] & TTM_PL_FLAG_NO_EVICT) {
- printk(KERN_ERR TTM_PFX "Need to be root to "
- "modify NO_EVICT status.\n");
- return -EINVAL;
- }
- }
- }
return 0;
}
num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (num_pages == 0) {
printk(KERN_ERR TTM_PFX "Illegal buffer object size.\n");
+ if (destroy)
+ (*destroy)(bo);
+ else
+ kfree(bo);
return -EINVAL;
}
bo->destroy = destroy;
int ret = -EINVAL;
struct ttm_mem_type_manager *man;
- if (type >= TTM_NUM_MEM_TYPES) {
- printk(KERN_ERR TTM_PFX "Illegal memory type %d\n", type);
- return ret;
- }
-
+ BUG_ON(type >= TTM_NUM_MEM_TYPES);
man = &bdev->man[type];
- if (man->has_type) {
- printk(KERN_ERR TTM_PFX
- "Memory manager already initialized for type %d\n",
- type);
- return ret;
- }
+ BUG_ON(man->has_type);
ret = bdev->driver->init_mem_type(bdev, type, man);
if (ret)
ret = 0;
if (type != TTM_PL_SYSTEM) {
- if (!p_size) {
- printk(KERN_ERR TTM_PFX
- "Zero size memory manager type %d\n",
- type);
- return ret;
- }
-
ret = (*man->func->init)(man, p_size);
if (ret)
return ret;
/**************************************************************************
*
- * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
+ * Copyright (c) 2007-2010 VMware, Inc., Palo Alto, CA., USA
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
#include "ttm/ttm_module.h"
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_placement.h"
-#include <linux/jiffies.h>
+#include "drm_mm.h"
#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/file.h>
+#include <linux/spinlock.h>
#include <linux/module.h>
+/**
+ * Currently we use a spinlock for the lock, but a mutex *may* be
+ * more appropriate to reduce scheduling latency if the range manager
+ * ends up with very fragmented allocation patterns.
+ */
+
+struct ttm_range_manager {
+ struct drm_mm mm;
+ spinlock_t lock;
+};
+
static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man,
struct ttm_buffer_object *bo,
struct ttm_placement *placement,
struct ttm_mem_reg *mem)
{
- struct ttm_bo_global *glob = man->bdev->glob;
- struct drm_mm *mm = man->priv;
+ struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv;
+ struct drm_mm *mm = &rman->mm;
struct drm_mm_node *node = NULL;
unsigned long lpfn;
int ret;
if (unlikely(ret))
return ret;
- spin_lock(&glob->lru_lock);
+ spin_lock(&rman->lock);
node = drm_mm_search_free_in_range(mm,
mem->num_pages, mem->page_alignment,
placement->fpfn, lpfn, 1);
if (unlikely(node == NULL)) {
- spin_unlock(&glob->lru_lock);
+ spin_unlock(&rman->lock);
return 0;
}
node = drm_mm_get_block_atomic_range(node, mem->num_pages,
- mem->page_alignment,
- placement->fpfn,
- lpfn);
- spin_unlock(&glob->lru_lock);
+ mem->page_alignment,
+ placement->fpfn,
+ lpfn);
+ spin_unlock(&rman->lock);
} while (node == NULL);
mem->mm_node = node;
static void ttm_bo_man_put_node(struct ttm_mem_type_manager *man,
struct ttm_mem_reg *mem)
{
- struct ttm_bo_global *glob = man->bdev->glob;
+ struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv;
if (mem->mm_node) {
- spin_lock(&glob->lru_lock);
+ spin_lock(&rman->lock);
drm_mm_put_block(mem->mm_node);
- spin_unlock(&glob->lru_lock);
+ spin_unlock(&rman->lock);
mem->mm_node = NULL;
}
}
static int ttm_bo_man_init(struct ttm_mem_type_manager *man,
unsigned long p_size)
{
- struct drm_mm *mm;
+ struct ttm_range_manager *rman;
int ret;
- mm = kzalloc(sizeof(*mm), GFP_KERNEL);
- if (!mm)
+ rman = kzalloc(sizeof(*rman), GFP_KERNEL);
+ if (!rman)
return -ENOMEM;
- ret = drm_mm_init(mm, 0, p_size);
+ ret = drm_mm_init(&rman->mm, 0, p_size);
if (ret) {
- kfree(mm);
+ kfree(rman);
return ret;
}
- man->priv = mm;
+ spin_lock_init(&rman->lock);
+ man->priv = rman;
return 0;
}
static int ttm_bo_man_takedown(struct ttm_mem_type_manager *man)
{
- struct ttm_bo_global *glob = man->bdev->glob;
- struct drm_mm *mm = man->priv;
- int ret = 0;
+ struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv;
+ struct drm_mm *mm = &rman->mm;
- spin_lock(&glob->lru_lock);
+ spin_lock(&rman->lock);
if (drm_mm_clean(mm)) {
drm_mm_takedown(mm);
- kfree(mm);
+ spin_unlock(&rman->lock);
+ kfree(rman);
man->priv = NULL;
- } else
- ret = -EBUSY;
- spin_unlock(&glob->lru_lock);
- return ret;
+ return 0;
+ }
+ spin_unlock(&rman->lock);
+ return -EBUSY;
}
static void ttm_bo_man_debug(struct ttm_mem_type_manager *man,
const char *prefix)
{
- struct ttm_bo_global *glob = man->bdev->glob;
- struct drm_mm *mm = man->priv;
+ struct ttm_range_manager *rman = (struct ttm_range_manager *) man->priv;
- spin_lock(&glob->lru_lock);
- drm_mm_debug_table(mm, prefix);
- spin_unlock(&glob->lru_lock);
+ spin_lock(&rman->lock);
+ drm_mm_debug_table(&rman->mm, prefix);
+ spin_unlock(&rman->lock);
}
const struct ttm_mem_type_manager_func ttm_bo_manager_func = {
return ret;
ret = be->func->bind(be, bo_mem);
- if (ret) {
- printk(KERN_ERR TTM_PFX "Couldn't bind backend.\n");
+ if (unlikely(ret != 0))
return ret;
- }
ttm->state = tt_bound;
vsg->num_pages = VIA_PFN(xfer->mem_addr + (xfer->num_lines * xfer->mem_stride - 1)) -
first_pfn + 1;
- if (NULL == (vsg->pages = vmalloc(sizeof(struct page *) * vsg->num_pages)))
+ vsg->pages = vzalloc(sizeof(struct page *) * vsg->num_pages);
+ if (NULL == vsg->pages)
return -ENOMEM;
- memset(vsg->pages, 0, sizeof(struct page *) * vsg->num_pages);
down_read(¤t->mm->mmap_sem);
ret = get_user_pages(current, current->mm,
(unsigned long)xfer->mem_addr,
fence_rep.error = ret;
fence_rep.fence_seq = (uint64_t) sequence;
+ fence_rep.pad64 = 0;
user_fence_rep = (struct drm_vmw_fence_rep __user *)
(unsigned long)arg->fence_rep;
&vmw_vram_ne_placement,
false, &vmw_dmabuf_bo_free);
vmw_overlay_resume_all(dev_priv);
+ if (unlikely(ret != 0))
+ vfbs->buffer = NULL;
return ret;
}
struct vmw_framebuffer_surface *vfbs =
vmw_framebuffer_to_vfbs(&vfb->base);
+ if (unlikely(vfbs->buffer == NULL))
+ return 0;
+
bo = &vfbs->buffer->base;
ttm_bo_unref(&bo);
vfbs->buffer = NULL;
return -EINVAL;
}
- dev_priv->ldu_priv = kmalloc(GFP_KERNEL, sizeof(*dev_priv->ldu_priv));
+ dev_priv->ldu_priv = kmalloc(sizeof(*dev_priv->ldu_priv), GFP_KERNEL);
if (!dev_priv->ldu_priv)
return -ENOMEM;
return -ENOSYS;
}
- overlay = kmalloc(GFP_KERNEL, sizeof(*overlay));
+ overlay = kmalloc(sizeof(*overlay), GFP_KERNEL);
if (!overlay)
return -ENOMEM;
depends on PCI
# Poulsbo stub depends on ACPI_VIDEO when ACPI is enabled
# but for select to work, need to select ACPI_VIDEO's dependencies, ick
+ select VIDEO_OUTPUT_CONTROL if ACPI
+ select BACKLIGHT_CLASS_DEVICE if ACPI
+ select INPUT if ACPI
select ACPI_VIDEO if ACPI
help
Choose this option if you have a system that has Intel GMA500
if NEW_LEDS
config LEDS_CLASS
- tristate "LED Class Support"
+ bool "LED Class Support"
help
This option enables the led sysfs class in /sys/class/leds. You'll
need this to do anything useful with LEDs. If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called leds-lp3944.
+config LEDS_LP5521
+ tristate "LED Support for N.S. LP5521 LED driver chip"
+ depends on LEDS_CLASS && I2C
+ help
+ If you say yes here you get support for the National Semiconductor
+ LP5521 LED driver. It is 3 channel chip with programmable engines.
+ Driver provides direct control via LED class and interface for
+ programming the engines.
+
+config LEDS_LP5523
+ tristate "LED Support for N.S. LP5523 LED driver chip"
+ depends on LEDS_CLASS && I2C
+ help
+ If you say yes here you get support for the National Semiconductor
+ LP5523 LED driver. It is 9 channel chip with programmable engines.
+ Driver provides direct control via LED class and interface for
+ programming the engines.
+
config LEDS_CLEVO_MAIL
tristate "Mail LED on Clevo notebook"
depends on X86 && SERIO_I8042 && DMI
obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
+obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o
+obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o
obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o
obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
__ATTR_NULL,
};
+static void led_timer_function(unsigned long data)
+{
+ struct led_classdev *led_cdev = (void *)data;
+ unsigned long brightness;
+ unsigned long delay;
+
+ if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
+ led_set_brightness(led_cdev, LED_OFF);
+ return;
+ }
+
+ brightness = led_get_brightness(led_cdev);
+ if (!brightness) {
+ /* Time to switch the LED on. */
+ brightness = led_cdev->blink_brightness;
+ delay = led_cdev->blink_delay_on;
+ } else {
+ /* Store the current brightness value to be able
+ * to restore it when the delay_off period is over.
+ */
+ led_cdev->blink_brightness = brightness;
+ brightness = LED_OFF;
+ delay = led_cdev->blink_delay_off;
+ }
+
+ led_set_brightness(led_cdev, brightness);
+
+ mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
+}
+
+static void led_stop_software_blink(struct led_classdev *led_cdev)
+{
+ /* deactivate previous settings */
+ del_timer_sync(&led_cdev->blink_timer);
+ led_cdev->blink_delay_on = 0;
+ led_cdev->blink_delay_off = 0;
+}
+
+static void led_set_software_blink(struct led_classdev *led_cdev,
+ unsigned long delay_on,
+ unsigned long delay_off)
+{
+ int current_brightness;
+
+ current_brightness = led_get_brightness(led_cdev);
+ if (current_brightness)
+ led_cdev->blink_brightness = current_brightness;
+ if (!led_cdev->blink_brightness)
+ led_cdev->blink_brightness = led_cdev->max_brightness;
+
+ if (delay_on == led_cdev->blink_delay_on &&
+ delay_off == led_cdev->blink_delay_off)
+ return;
+
+ led_stop_software_blink(led_cdev);
+
+ led_cdev->blink_delay_on = delay_on;
+ led_cdev->blink_delay_off = delay_off;
+
+ /* never on - don't blink */
+ if (!delay_on)
+ return;
+
+ /* never off - just set to brightness */
+ if (!delay_off) {
+ led_set_brightness(led_cdev, led_cdev->blink_brightness);
+ return;
+ }
+
+ mod_timer(&led_cdev->blink_timer, jiffies + 1);
+}
+
+
/**
* led_classdev_suspend - suspend an led_classdev.
* @led_cdev: the led_classdev to suspend.
led_update_brightness(led_cdev);
+ init_timer(&led_cdev->blink_timer);
+ led_cdev->blink_timer.function = led_timer_function;
+ led_cdev->blink_timer.data = (unsigned long)led_cdev;
+
#ifdef CONFIG_LEDS_TRIGGERS
led_trigger_set_default(led_cdev);
#endif
return 0;
}
-
EXPORT_SYMBOL_GPL(led_classdev_register);
/**
up_write(&led_cdev->trigger_lock);
#endif
+ /* Stop blinking */
+ led_brightness_set(led_cdev, LED_OFF);
+
device_unregister(led_cdev->dev);
down_write(&leds_list_lock);
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);
+void led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ if (led_cdev->blink_set &&
+ led_cdev->blink_set(led_cdev, delay_on, delay_off))
+ return;
+
+ /* blink with 1 Hz as default if nothing specified */
+ if (!*delay_on && !*delay_off)
+ *delay_on = *delay_off = 500;
+
+ led_set_software_blink(led_cdev, *delay_on, *delay_off);
+}
+EXPORT_SYMBOL(led_blink_set);
+
+void led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ led_stop_software_blink(led_cdev);
+ led_cdev->brightness_set(led_cdev, brightness);
+}
+EXPORT_SYMBOL(led_brightness_set);
+
static int __init leds_init(void)
{
leds_class = class_create(THIS_MODULE, "leds");
if (led_cdev->trigger->deactivate)
led_cdev->trigger->deactivate(led_cdev);
led_cdev->trigger = NULL;
- led_set_brightness(led_cdev, LED_OFF);
+ led_brightness_set(led_cdev, LED_OFF);
}
if (trigger) {
write_lock_irqsave(&trigger->leddev_list_lock, flags);
static int __init gpio_led_init(void)
{
- int ret;
+ int ret = 0;
#ifdef CONFIG_LEDS_GPIO_PLATFORM
ret = platform_driver_register(&gpio_led_driver);
--- /dev/null
+/*
+ * LP5521 LED chip driver.
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/leds.h>
+#include <linux/leds-lp5521.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#define LP5521_PROGRAM_LENGTH 32 /* in bytes */
+
+#define LP5521_MAX_LEDS 3 /* Maximum number of LEDs */
+#define LP5521_MAX_ENGINES 3 /* Maximum number of engines */
+
+#define LP5521_ENG_MASK_BASE 0x30 /* 00110000 */
+#define LP5521_ENG_STATUS_MASK 0x07 /* 00000111 */
+
+#define LP5521_CMD_LOAD 0x15 /* 00010101 */
+#define LP5521_CMD_RUN 0x2a /* 00101010 */
+#define LP5521_CMD_DIRECT 0x3f /* 00111111 */
+#define LP5521_CMD_DISABLED 0x00 /* 00000000 */
+
+/* Registers */
+#define LP5521_REG_ENABLE 0x00
+#define LP5521_REG_OP_MODE 0x01
+#define LP5521_REG_R_PWM 0x02
+#define LP5521_REG_G_PWM 0x03
+#define LP5521_REG_B_PWM 0x04
+#define LP5521_REG_R_CURRENT 0x05
+#define LP5521_REG_G_CURRENT 0x06
+#define LP5521_REG_B_CURRENT 0x07
+#define LP5521_REG_CONFIG 0x08
+#define LP5521_REG_R_CHANNEL_PC 0x09
+#define LP5521_REG_G_CHANNEL_PC 0x0A
+#define LP5521_REG_B_CHANNEL_PC 0x0B
+#define LP5521_REG_STATUS 0x0C
+#define LP5521_REG_RESET 0x0D
+#define LP5521_REG_GPO 0x0E
+#define LP5521_REG_R_PROG_MEM 0x10
+#define LP5521_REG_G_PROG_MEM 0x30
+#define LP5521_REG_B_PROG_MEM 0x50
+
+#define LP5521_PROG_MEM_BASE LP5521_REG_R_PROG_MEM
+#define LP5521_PROG_MEM_SIZE 0x20
+
+/* Base register to set LED current */
+#define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT
+
+/* Base register to set the brightness */
+#define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM
+
+/* Bits in ENABLE register */
+#define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */
+#define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */
+#define LP5521_EXEC_RUN 0x2A
+
+/* Bits in CONFIG register */
+#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
+#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
+#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */
+#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */
+#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */
+#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */
+#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */
+#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */
+#define LP5521_CLK_INT 1 /* Internal clock */
+#define LP5521_CLK_AUTO 2 /* Automatic clock selection */
+
+/* Status */
+#define LP5521_EXT_CLK_USED 0x08
+
+struct lp5521_engine {
+ const struct attribute_group *attributes;
+ int id;
+ u8 mode;
+ u8 prog_page;
+ u8 engine_mask;
+};
+
+struct lp5521_led {
+ int id;
+ u8 chan_nr;
+ u8 led_current;
+ u8 max_current;
+ struct led_classdev cdev;
+ struct work_struct brightness_work;
+ u8 brightness;
+};
+
+struct lp5521_chip {
+ struct lp5521_platform_data *pdata;
+ struct mutex lock; /* Serialize control */
+ struct i2c_client *client;
+ struct lp5521_engine engines[LP5521_MAX_ENGINES];
+ struct lp5521_led leds[LP5521_MAX_LEDS];
+ u8 num_channels;
+ u8 num_leds;
+};
+
+#define cdev_to_led(c) container_of(c, struct lp5521_led, cdev)
+#define engine_to_lp5521(eng) container_of((eng), struct lp5521_chip, \
+ engines[(eng)->id - 1])
+#define led_to_lp5521(led) container_of((led), struct lp5521_chip, \
+ leds[(led)->id])
+
+static void lp5521_led_brightness_work(struct work_struct *work);
+
+static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int lp5521_read(struct i2c_client *client, u8 reg, u8 *buf)
+{
+ s32 ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ return -EIO;
+
+ *buf = ret;
+ return 0;
+}
+
+static int lp5521_set_engine_mode(struct lp5521_engine *engine, u8 mode)
+{
+ struct lp5521_chip *chip = engine_to_lp5521(engine);
+ struct i2c_client *client = chip->client;
+ int ret;
+ u8 engine_state;
+
+ /* Only transition between RUN and DIRECT mode are handled here */
+ if (mode == LP5521_CMD_LOAD)
+ return 0;
+
+ if (mode == LP5521_CMD_DISABLED)
+ mode = LP5521_CMD_DIRECT;
+
+ ret = lp5521_read(client, LP5521_REG_OP_MODE, &engine_state);
+
+ /* set mode only for this engine */
+ engine_state &= ~(engine->engine_mask);
+ mode &= engine->engine_mask;
+ engine_state |= mode;
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, engine_state);
+
+ return ret;
+}
+
+static int lp5521_load_program(struct lp5521_engine *eng, const u8 *pattern)
+{
+ struct lp5521_chip *chip = engine_to_lp5521(eng);
+ struct i2c_client *client = chip->client;
+ int ret;
+ int addr;
+ u8 mode;
+
+ /* move current engine to direct mode and remember the state */
+ ret = lp5521_set_engine_mode(eng, LP5521_CMD_DIRECT);
+ usleep_range(1000, 10000);
+ ret |= lp5521_read(client, LP5521_REG_OP_MODE, &mode);
+
+ /* For loading, all the engines to load mode */
+ lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
+ usleep_range(1000, 10000);
+ lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_LOAD);
+ usleep_range(1000, 10000);
+
+ addr = LP5521_PROG_MEM_BASE + eng->prog_page * LP5521_PROG_MEM_SIZE;
+ i2c_smbus_write_i2c_block_data(client,
+ addr,
+ LP5521_PROG_MEM_SIZE,
+ pattern);
+
+ ret |= lp5521_write(client, LP5521_REG_OP_MODE, mode);
+ return ret;
+}
+
+static int lp5521_set_led_current(struct lp5521_chip *chip, int led, u8 curr)
+{
+ return lp5521_write(chip->client,
+ LP5521_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr,
+ curr);
+}
+
+static void lp5521_init_engine(struct lp5521_chip *chip,
+ const struct attribute_group *attr_group)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(chip->engines); i++) {
+ chip->engines[i].id = i + 1;
+ chip->engines[i].engine_mask = LP5521_ENG_MASK_BASE >> (i * 2);
+ chip->engines[i].prog_page = i;
+ chip->engines[i].attributes = &attr_group[i];
+ }
+}
+
+static int lp5521_configure(struct i2c_client *client,
+ const struct attribute_group *attr_group)
+{
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+ int ret;
+
+ lp5521_init_engine(chip, attr_group);
+
+ lp5521_write(client, LP5521_REG_RESET, 0xff);
+
+ usleep_range(10000, 20000);
+
+ /* Set all PWMs to direct control mode */
+ ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
+
+ /* Enable auto-powersave, set charge pump to auto, red to battery */
+ ret |= lp5521_write(client, LP5521_REG_CONFIG,
+ LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT);
+
+ /* Initialize all channels PWM to zero -> leds off */
+ ret |= lp5521_write(client, LP5521_REG_R_PWM, 0);
+ ret |= lp5521_write(client, LP5521_REG_G_PWM, 0);
+ ret |= lp5521_write(client, LP5521_REG_B_PWM, 0);
+
+ /* Set engines are set to run state when OP_MODE enables engines */
+ ret |= lp5521_write(client, LP5521_REG_ENABLE,
+ LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM |
+ LP5521_EXEC_RUN);
+ /* enable takes 500us */
+ usleep_range(500, 20000);
+
+ return ret;
+}
+
+static int lp5521_run_selftest(struct lp5521_chip *chip, char *buf)
+{
+ int ret;
+ u8 status;
+
+ ret = lp5521_read(chip->client, LP5521_REG_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
+ /* Check that ext clock is really in use if requested */
+ if (chip->pdata && chip->pdata->clock_mode == LP5521_CLOCK_EXT)
+ if ((status & LP5521_EXT_CLK_USED) == 0)
+ return -EIO;
+ return 0;
+}
+
+static void lp5521_set_brightness(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct lp5521_led *led = cdev_to_led(cdev);
+ led->brightness = (u8)brightness;
+ schedule_work(&led->brightness_work);
+}
+
+static void lp5521_led_brightness_work(struct work_struct *work)
+{
+ struct lp5521_led *led = container_of(work,
+ struct lp5521_led,
+ brightness_work);
+ struct lp5521_chip *chip = led_to_lp5521(led);
+ struct i2c_client *client = chip->client;
+
+ mutex_lock(&chip->lock);
+ lp5521_write(client, LP5521_REG_LED_PWM_BASE + led->chan_nr,
+ led->brightness);
+ mutex_unlock(&chip->lock);
+}
+
+/* Detect the chip by setting its ENABLE register and reading it back. */
+static int lp5521_detect(struct i2c_client *client)
+{
+ int ret;
+ u8 buf;
+
+ ret = lp5521_write(client, LP5521_REG_ENABLE,
+ LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM);
+ if (ret)
+ return ret;
+ usleep_range(1000, 10000);
+ ret = lp5521_read(client, LP5521_REG_ENABLE, &buf);
+ if (ret)
+ return ret;
+ if (buf != (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM))
+ return -ENODEV;
+
+ return 0;
+}
+
+/* Set engine mode and create appropriate sysfs attributes, if required. */
+static int lp5521_set_mode(struct lp5521_engine *engine, u8 mode)
+{
+ struct lp5521_chip *chip = engine_to_lp5521(engine);
+ struct i2c_client *client = chip->client;
+ struct device *dev = &client->dev;
+ int ret = 0;
+
+ /* if in that mode already do nothing, except for run */
+ if (mode == engine->mode && mode != LP5521_CMD_RUN)
+ return 0;
+
+ if (mode == LP5521_CMD_RUN) {
+ ret = lp5521_set_engine_mode(engine, LP5521_CMD_RUN);
+ } else if (mode == LP5521_CMD_LOAD) {
+ lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED);
+ lp5521_set_engine_mode(engine, LP5521_CMD_LOAD);
+
+ ret = sysfs_create_group(&dev->kobj, engine->attributes);
+ if (ret)
+ return ret;
+ } else if (mode == LP5521_CMD_DISABLED) {
+ lp5521_set_engine_mode(engine, LP5521_CMD_DISABLED);
+ }
+
+ /* remove load attribute from sysfs if not in load mode */
+ if (engine->mode == LP5521_CMD_LOAD && mode != LP5521_CMD_LOAD)
+ sysfs_remove_group(&dev->kobj, engine->attributes);
+
+ engine->mode = mode;
+
+ return ret;
+}
+
+static int lp5521_do_store_load(struct lp5521_engine *engine,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = engine_to_lp5521(engine);
+ struct i2c_client *client = chip->client;
+ int ret, nrchars, offset = 0, i = 0;
+ char c[3];
+ unsigned cmd;
+ u8 pattern[LP5521_PROGRAM_LENGTH] = {0};
+
+ while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) {
+ /* separate sscanfs because length is working only for %s */
+ ret = sscanf(buf + offset, "%2s%n ", c, &nrchars);
+ ret = sscanf(c, "%2x", &cmd);
+ if (ret != 1)
+ goto fail;
+ pattern[i] = (u8)cmd;
+
+ offset += nrchars;
+ i++;
+ }
+
+ /* Each instruction is 16bit long. Check that length is even */
+ if (i % 2)
+ goto fail;
+
+ mutex_lock(&chip->lock);
+ ret = lp5521_load_program(engine, pattern);
+ mutex_unlock(&chip->lock);
+
+ if (ret) {
+ dev_err(&client->dev, "failed loading pattern\n");
+ return ret;
+ }
+
+ return len;
+fail:
+ dev_err(&client->dev, "wrong pattern format\n");
+ return -EINVAL;
+}
+
+static ssize_t store_engine_load(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+ return lp5521_do_store_load(&chip->engines[nr - 1], buf, len);
+}
+
+#define store_load(nr) \
+static ssize_t store_engine##nr##_load(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_load(dev, attr, buf, len, nr); \
+}
+store_load(1)
+store_load(2)
+store_load(3)
+
+static ssize_t show_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+ switch (chip->engines[nr - 1].mode) {
+ case LP5521_CMD_RUN:
+ return sprintf(buf, "run\n");
+ case LP5521_CMD_LOAD:
+ return sprintf(buf, "load\n");
+ case LP5521_CMD_DISABLED:
+ return sprintf(buf, "disabled\n");
+ default:
+ return sprintf(buf, "disabled\n");
+ }
+}
+
+#define show_mode(nr) \
+static ssize_t show_engine##nr##_mode(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return show_engine_mode(dev, attr, buf, nr); \
+}
+show_mode(1)
+show_mode(2)
+show_mode(3)
+
+static ssize_t store_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+ struct lp5521_engine *engine = &chip->engines[nr - 1];
+ mutex_lock(&chip->lock);
+
+ if (!strncmp(buf, "run", 3))
+ lp5521_set_mode(engine, LP5521_CMD_RUN);
+ else if (!strncmp(buf, "load", 4))
+ lp5521_set_mode(engine, LP5521_CMD_LOAD);
+ else if (!strncmp(buf, "disabled", 8))
+ lp5521_set_mode(engine, LP5521_CMD_DISABLED);
+
+ mutex_unlock(&chip->lock);
+ return len;
+}
+
+#define store_mode(nr) \
+static ssize_t store_engine##nr##_mode(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_mode(dev, attr, buf, len, nr); \
+}
+store_mode(1)
+store_mode(2)
+store_mode(3)
+
+static ssize_t show_max_current(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct lp5521_led *led = cdev_to_led(led_cdev);
+
+ return sprintf(buf, "%d\n", led->max_current);
+}
+
+static ssize_t show_current(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct lp5521_led *led = cdev_to_led(led_cdev);
+
+ return sprintf(buf, "%d\n", led->led_current);
+}
+
+static ssize_t store_current(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct lp5521_led *led = cdev_to_led(led_cdev);
+ struct lp5521_chip *chip = led_to_lp5521(led);
+ ssize_t ret;
+ unsigned long curr;
+
+ if (strict_strtoul(buf, 0, &curr))
+ return -EINVAL;
+
+ if (curr > led->max_current)
+ return -EINVAL;
+
+ mutex_lock(&chip->lock);
+ ret = lp5521_set_led_current(chip, led->id, curr);
+ mutex_unlock(&chip->lock);
+
+ if (ret < 0)
+ return ret;
+
+ led->led_current = (u8)curr;
+
+ return len;
+}
+
+static ssize_t lp5521_selftest(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+ int ret;
+
+ mutex_lock(&chip->lock);
+ ret = lp5521_run_selftest(chip, buf);
+ mutex_unlock(&chip->lock);
+ return sprintf(buf, "%s\n", ret ? "FAIL" : "OK");
+}
+
+/* led class device attributes */
+static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current);
+static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL);
+
+static struct attribute *lp5521_led_attributes[] = {
+ &dev_attr_led_current.attr,
+ &dev_attr_max_current.attr,
+ NULL,
+};
+
+static struct attribute_group lp5521_led_attribute_group = {
+ .attrs = lp5521_led_attributes
+};
+
+/* device attributes */
+static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO,
+ show_engine1_mode, store_engine1_mode);
+static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO,
+ show_engine2_mode, store_engine2_mode);
+static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO,
+ show_engine3_mode, store_engine3_mode);
+static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load);
+static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load);
+static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load);
+static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL);
+
+static struct attribute *lp5521_attributes[] = {
+ &dev_attr_engine1_mode.attr,
+ &dev_attr_engine2_mode.attr,
+ &dev_attr_engine3_mode.attr,
+ &dev_attr_selftest.attr,
+ NULL
+};
+
+static struct attribute *lp5521_engine1_attributes[] = {
+ &dev_attr_engine1_load.attr,
+ NULL
+};
+
+static struct attribute *lp5521_engine2_attributes[] = {
+ &dev_attr_engine2_load.attr,
+ NULL
+};
+
+static struct attribute *lp5521_engine3_attributes[] = {
+ &dev_attr_engine3_load.attr,
+ NULL
+};
+
+static const struct attribute_group lp5521_group = {
+ .attrs = lp5521_attributes,
+};
+
+static const struct attribute_group lp5521_engine_group[] = {
+ {.attrs = lp5521_engine1_attributes },
+ {.attrs = lp5521_engine2_attributes },
+ {.attrs = lp5521_engine3_attributes },
+};
+
+static int lp5521_register_sysfs(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ return sysfs_create_group(&dev->kobj, &lp5521_group);
+}
+
+static void lp5521_unregister_sysfs(struct i2c_client *client)
+{
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+ int i;
+
+ sysfs_remove_group(&dev->kobj, &lp5521_group);
+
+ for (i = 0; i < ARRAY_SIZE(chip->engines); i++) {
+ if (chip->engines[i].mode == LP5521_CMD_LOAD)
+ sysfs_remove_group(&dev->kobj,
+ chip->engines[i].attributes);
+ }
+
+ for (i = 0; i < chip->num_leds; i++)
+ sysfs_remove_group(&chip->leds[i].cdev.dev->kobj,
+ &lp5521_led_attribute_group);
+}
+
+static int __init lp5521_init_led(struct lp5521_led *led,
+ struct i2c_client *client,
+ int chan, struct lp5521_platform_data *pdata)
+{
+ struct device *dev = &client->dev;
+ char name[32];
+ int res;
+
+ if (chan >= LP5521_MAX_LEDS)
+ return -EINVAL;
+
+ if (pdata->led_config[chan].led_current == 0)
+ return 0;
+
+ led->led_current = pdata->led_config[chan].led_current;
+ led->max_current = pdata->led_config[chan].max_current;
+ led->chan_nr = pdata->led_config[chan].chan_nr;
+
+ if (led->chan_nr >= LP5521_MAX_LEDS) {
+ dev_err(dev, "Use channel numbers between 0 and %d\n",
+ LP5521_MAX_LEDS - 1);
+ return -EINVAL;
+ }
+
+ snprintf(name, sizeof(name), "%s:channel%d", client->name, chan);
+ led->cdev.brightness_set = lp5521_set_brightness;
+ led->cdev.name = name;
+ res = led_classdev_register(dev, &led->cdev);
+ if (res < 0) {
+ dev_err(dev, "couldn't register led on channel %d\n", chan);
+ return res;
+ }
+
+ res = sysfs_create_group(&led->cdev.dev->kobj,
+ &lp5521_led_attribute_group);
+ if (res < 0) {
+ dev_err(dev, "couldn't register current attribute\n");
+ led_classdev_unregister(&led->cdev);
+ return res;
+ }
+ return 0;
+}
+
+static int lp5521_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lp5521_chip *chip;
+ struct lp5521_platform_data *pdata;
+ int ret, i, led;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, chip);
+ chip->client = client;
+
+ pdata = client->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data\n");
+ ret = -EINVAL;
+ goto fail1;
+ }
+
+ mutex_init(&chip->lock);
+
+ chip->pdata = pdata;
+
+ if (pdata->setup_resources) {
+ ret = pdata->setup_resources();
+ if (ret < 0)
+ goto fail1;
+ }
+
+ if (pdata->enable) {
+ pdata->enable(0);
+ usleep_range(1000, 10000);
+ pdata->enable(1);
+ usleep_range(1000, 10000); /* Spec says min 500us */
+ }
+
+ ret = lp5521_detect(client);
+
+ if (ret) {
+ dev_err(&client->dev, "Chip not found\n");
+ goto fail2;
+ }
+
+ dev_info(&client->dev, "%s programmable led chip found\n", id->name);
+
+ ret = lp5521_configure(client, lp5521_engine_group);
+ if (ret < 0) {
+ dev_err(&client->dev, "error configuring chip\n");
+ goto fail2;
+ }
+
+ /* Initialize leds */
+ chip->num_channels = pdata->num_channels;
+ chip->num_leds = 0;
+ led = 0;
+ for (i = 0; i < pdata->num_channels; i++) {
+ /* Do not initialize channels that are not connected */
+ if (pdata->led_config[i].led_current == 0)
+ continue;
+
+ ret = lp5521_init_led(&chip->leds[led], client, i, pdata);
+ if (ret) {
+ dev_err(&client->dev, "error initializing leds\n");
+ goto fail3;
+ }
+ chip->num_leds++;
+
+ chip->leds[led].id = led;
+ /* Set initial LED current */
+ lp5521_set_led_current(chip, led,
+ chip->leds[led].led_current);
+
+ INIT_WORK(&(chip->leds[led].brightness_work),
+ lp5521_led_brightness_work);
+
+ led++;
+ }
+
+ ret = lp5521_register_sysfs(client);
+ if (ret) {
+ dev_err(&client->dev, "registering sysfs failed\n");
+ goto fail3;
+ }
+ return ret;
+fail3:
+ for (i = 0; i < chip->num_leds; i++) {
+ led_classdev_unregister(&chip->leds[i].cdev);
+ cancel_work_sync(&chip->leds[i].brightness_work);
+ }
+fail2:
+ if (pdata->enable)
+ pdata->enable(0);
+ if (pdata->release_resources)
+ pdata->release_resources();
+fail1:
+ kfree(chip);
+ return ret;
+}
+
+static int lp5521_remove(struct i2c_client *client)
+{
+ struct lp5521_chip *chip = i2c_get_clientdata(client);
+ int i;
+
+ lp5521_unregister_sysfs(client);
+
+ for (i = 0; i < chip->num_leds; i++) {
+ led_classdev_unregister(&chip->leds[i].cdev);
+ cancel_work_sync(&chip->leds[i].brightness_work);
+ }
+
+ if (chip->pdata->enable)
+ chip->pdata->enable(0);
+ if (chip->pdata->release_resources)
+ chip->pdata->release_resources();
+ kfree(chip);
+ return 0;
+}
+
+static const struct i2c_device_id lp5521_id[] = {
+ { "lp5521", 0 }, /* Three channel chip */
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lp5521_id);
+
+static struct i2c_driver lp5521_driver = {
+ .driver = {
+ .name = "lp5521",
+ },
+ .probe = lp5521_probe,
+ .remove = lp5521_remove,
+ .id_table = lp5521_id,
+};
+
+static int __init lp5521_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&lp5521_driver);
+
+ if (ret < 0)
+ printk(KERN_ALERT "Adding lp5521 driver failed\n");
+
+ return ret;
+}
+
+static void __exit lp5521_exit(void)
+{
+ i2c_del_driver(&lp5521_driver);
+}
+
+module_init(lp5521_init);
+module_exit(lp5521_exit);
+
+MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo");
+MODULE_DESCRIPTION("LP5521 LED engine");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * lp5523.c - LP5523 LED Driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/leds.h>
+#include <linux/leds-lp5523.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#define LP5523_REG_ENABLE 0x00
+#define LP5523_REG_OP_MODE 0x01
+#define LP5523_REG_RATIOMETRIC_MSB 0x02
+#define LP5523_REG_RATIOMETRIC_LSB 0x03
+#define LP5523_REG_ENABLE_LEDS_MSB 0x04
+#define LP5523_REG_ENABLE_LEDS_LSB 0x05
+#define LP5523_REG_LED_CNTRL_BASE 0x06
+#define LP5523_REG_LED_PWM_BASE 0x16
+#define LP5523_REG_LED_CURRENT_BASE 0x26
+#define LP5523_REG_CONFIG 0x36
+#define LP5523_REG_CHANNEL1_PC 0x37
+#define LP5523_REG_CHANNEL2_PC 0x38
+#define LP5523_REG_CHANNEL3_PC 0x39
+#define LP5523_REG_STATUS 0x3a
+#define LP5523_REG_GPO 0x3b
+#define LP5523_REG_VARIABLE 0x3c
+#define LP5523_REG_RESET 0x3d
+#define LP5523_REG_TEMP_CTRL 0x3e
+#define LP5523_REG_TEMP_READ 0x3f
+#define LP5523_REG_TEMP_WRITE 0x40
+#define LP5523_REG_LED_TEST_CTRL 0x41
+#define LP5523_REG_LED_TEST_ADC 0x42
+#define LP5523_REG_ENG1_VARIABLE 0x45
+#define LP5523_REG_ENG2_VARIABLE 0x46
+#define LP5523_REG_ENG3_VARIABLE 0x47
+#define LP5523_REG_MASTER_FADER1 0x48
+#define LP5523_REG_MASTER_FADER2 0x49
+#define LP5523_REG_MASTER_FADER3 0x4a
+#define LP5523_REG_CH1_PROG_START 0x4c
+#define LP5523_REG_CH2_PROG_START 0x4d
+#define LP5523_REG_CH3_PROG_START 0x4e
+#define LP5523_REG_PROG_PAGE_SEL 0x4f
+#define LP5523_REG_PROG_MEM 0x50
+
+#define LP5523_CMD_LOAD 0x15 /* 00010101 */
+#define LP5523_CMD_RUN 0x2a /* 00101010 */
+#define LP5523_CMD_DISABLED 0x00 /* 00000000 */
+
+#define LP5523_ENABLE 0x40
+#define LP5523_AUTO_INC 0x40
+#define LP5523_PWR_SAVE 0x20
+#define LP5523_PWM_PWR_SAVE 0x04
+#define LP5523_CP_1 0x08
+#define LP5523_CP_1_5 0x10
+#define LP5523_CP_AUTO 0x18
+#define LP5523_INT_CLK 0x01
+#define LP5523_AUTO_CLK 0x02
+#define LP5523_EN_LEDTEST 0x80
+#define LP5523_LEDTEST_DONE 0x80
+
+#define LP5523_DEFAULT_CURRENT 50 /* microAmps */
+#define LP5523_PROGRAM_LENGTH 32 /* in bytes */
+#define LP5523_PROGRAM_PAGES 6
+#define LP5523_ADC_SHORTCIRC_LIM 80
+
+#define LP5523_LEDS 9
+#define LP5523_ENGINES 3
+
+#define LP5523_ENG_MASK_BASE 0x30 /* 00110000 */
+
+#define LP5523_ENG_STATUS_MASK 0x07 /* 00000111 */
+
+#define LP5523_IRQ_FLAGS IRQF_TRIGGER_FALLING
+
+#define LP5523_EXT_CLK_USED 0x08
+
+#define LED_ACTIVE(mux, led) (!!(mux & (0x0001 << led)))
+#define SHIFT_MASK(id) (((id) - 1) * 2)
+
+struct lp5523_engine {
+ const struct attribute_group *attributes;
+ int id;
+ u8 mode;
+ u8 prog_page;
+ u8 mux_page;
+ u16 led_mux;
+ u8 engine_mask;
+};
+
+struct lp5523_led {
+ int id;
+ u8 chan_nr;
+ u8 led_current;
+ u8 max_current;
+ struct led_classdev cdev;
+ struct work_struct brightness_work;
+ u8 brightness;
+};
+
+struct lp5523_chip {
+ struct mutex lock; /* Serialize control */
+ struct i2c_client *client;
+ struct lp5523_engine engines[LP5523_ENGINES];
+ struct lp5523_led leds[LP5523_LEDS];
+ struct lp5523_platform_data *pdata;
+ u8 num_channels;
+ u8 num_leds;
+};
+
+#define cdev_to_led(c) container_of(c, struct lp5523_led, cdev)
+
+static struct lp5523_chip *engine_to_lp5523(struct lp5523_engine *engine)
+{
+ return container_of(engine, struct lp5523_chip,
+ engines[engine->id - 1]);
+}
+
+static struct lp5523_chip *led_to_lp5523(struct lp5523_led *led)
+{
+ return container_of(led, struct lp5523_chip,
+ leds[led->id]);
+}
+
+static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode);
+static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode);
+static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern);
+
+static void lp5523_led_brightness_work(struct work_struct *work);
+
+static int lp5523_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int lp5523_read(struct i2c_client *client, u8 reg, u8 *buf)
+{
+ s32 ret = i2c_smbus_read_byte_data(client, reg);
+
+ if (ret < 0)
+ return -EIO;
+
+ *buf = ret;
+ return 0;
+}
+
+static int lp5523_detect(struct i2c_client *client)
+{
+ int ret;
+ u8 buf;
+
+ ret = lp5523_write(client, LP5523_REG_ENABLE, 0x40);
+ if (ret)
+ return ret;
+ ret = lp5523_read(client, LP5523_REG_ENABLE, &buf);
+ if (ret)
+ return ret;
+ if (buf == 0x40)
+ return 0;
+ else
+ return -ENODEV;
+}
+
+static int lp5523_configure(struct i2c_client *client)
+{
+ struct lp5523_chip *chip = i2c_get_clientdata(client);
+ int ret = 0;
+ u8 status;
+
+ /* one pattern per engine setting led mux start and stop addresses */
+ u8 pattern[][LP5523_PROGRAM_LENGTH] = {
+ { 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0},
+ { 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0},
+ { 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0},
+ };
+
+ lp5523_write(client, LP5523_REG_RESET, 0xff);
+
+ usleep_range(10000, 100000);
+
+ ret |= lp5523_write(client, LP5523_REG_ENABLE, LP5523_ENABLE);
+ /* Chip startup time after reset is 500 us */
+ usleep_range(1000, 10000);
+
+ ret |= lp5523_write(client, LP5523_REG_CONFIG,
+ LP5523_AUTO_INC | LP5523_PWR_SAVE |
+ LP5523_CP_AUTO | LP5523_AUTO_CLK |
+ LP5523_PWM_PWR_SAVE);
+
+ /* turn on all leds */
+ ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_MSB, 0x01);
+ ret |= lp5523_write(client, LP5523_REG_ENABLE_LEDS_LSB, 0xff);
+
+ /* hardcode 32 bytes of memory for each engine from program memory */
+ ret |= lp5523_write(client, LP5523_REG_CH1_PROG_START, 0x00);
+ ret |= lp5523_write(client, LP5523_REG_CH2_PROG_START, 0x10);
+ ret |= lp5523_write(client, LP5523_REG_CH3_PROG_START, 0x20);
+
+ /* write led mux address space for each channel */
+ ret |= lp5523_load_program(&chip->engines[0], pattern[0]);
+ ret |= lp5523_load_program(&chip->engines[1], pattern[1]);
+ ret |= lp5523_load_program(&chip->engines[2], pattern[2]);
+
+ if (ret) {
+ dev_err(&client->dev, "could not load mux programs\n");
+ return -1;
+ }
+
+ /* set all engines exec state and mode to run 00101010 */
+ ret |= lp5523_write(client, LP5523_REG_ENABLE,
+ (LP5523_CMD_RUN | LP5523_ENABLE));
+
+ ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_RUN);
+
+ if (ret) {
+ dev_err(&client->dev, "could not start mux programs\n");
+ return -1;
+ }
+
+ /* Wait 3ms and check the engine status */
+ usleep_range(3000, 20000);
+ lp5523_read(client, LP5523_REG_STATUS, &status);
+ status &= LP5523_ENG_STATUS_MASK;
+
+ if (status == LP5523_ENG_STATUS_MASK) {
+ dev_dbg(&client->dev, "all engines configured\n");
+ } else {
+ dev_info(&client->dev, "status == %x\n", status);
+ dev_err(&client->dev, "cound not configure LED engine\n");
+ return -1;
+ }
+
+ dev_info(&client->dev, "disabling engines\n");
+
+ ret |= lp5523_write(client, LP5523_REG_OP_MODE, LP5523_CMD_DISABLED);
+
+ return ret;
+}
+
+static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode)
+{
+ struct lp5523_chip *chip = engine_to_lp5523(engine);
+ struct i2c_client *client = chip->client;
+ int ret;
+ u8 engine_state;
+
+ ret = lp5523_read(client, LP5523_REG_OP_MODE, &engine_state);
+ if (ret)
+ goto fail;
+
+ engine_state &= ~(engine->engine_mask);
+
+ /* set mode only for this engine */
+ mode &= engine->engine_mask;
+
+ engine_state |= mode;
+
+ ret |= lp5523_write(client, LP5523_REG_OP_MODE, engine_state);
+fail:
+ return ret;
+}
+
+static int lp5523_load_mux(struct lp5523_engine *engine, u16 mux)
+{
+ struct lp5523_chip *chip = engine_to_lp5523(engine);
+ struct i2c_client *client = chip->client;
+ int ret = 0;
+
+ ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD);
+
+ ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL, engine->mux_page);
+ ret |= lp5523_write(client, LP5523_REG_PROG_MEM,
+ (u8)(mux >> 8));
+ ret |= lp5523_write(client, LP5523_REG_PROG_MEM + 1, (u8)(mux));
+ engine->led_mux = mux;
+
+ return ret;
+}
+
+static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern)
+{
+ struct lp5523_chip *chip = engine_to_lp5523(engine);
+ struct i2c_client *client = chip->client;
+
+ int ret = 0;
+
+ ret |= lp5523_set_engine_mode(engine, LP5523_CMD_LOAD);
+
+ ret |= lp5523_write(client, LP5523_REG_PROG_PAGE_SEL,
+ engine->prog_page);
+ ret |= i2c_smbus_write_i2c_block_data(client, LP5523_REG_PROG_MEM,
+ LP5523_PROGRAM_LENGTH, pattern);
+
+ return ret;
+}
+
+static int lp5523_run_program(struct lp5523_engine *engine)
+{
+ struct lp5523_chip *chip = engine_to_lp5523(engine);
+ struct i2c_client *client = chip->client;
+ int ret;
+
+ ret = lp5523_write(client, LP5523_REG_ENABLE,
+ LP5523_CMD_RUN | LP5523_ENABLE);
+ if (ret)
+ goto fail;
+
+ ret = lp5523_set_engine_mode(engine, LP5523_CMD_RUN);
+fail:
+ return ret;
+}
+
+static int lp5523_mux_parse(const char *buf, u16 *mux, size_t len)
+{
+ int i;
+ u16 tmp_mux = 0;
+ len = len < LP5523_LEDS ? len : LP5523_LEDS;
+ for (i = 0; i < len; i++) {
+ switch (buf[i]) {
+ case '1':
+ tmp_mux |= (1 << i);
+ break;
+ case '0':
+ break;
+ case '\n':
+ i = len;
+ break;
+ default:
+ return -1;
+ }
+ }
+ *mux = tmp_mux;
+
+ return 0;
+}
+
+static void lp5523_mux_to_array(u16 led_mux, char *array)
+{
+ int i, pos = 0;
+ for (i = 0; i < LP5523_LEDS; i++)
+ pos += sprintf(array + pos, "%x", LED_ACTIVE(led_mux, i));
+
+ array[pos] = '\0';
+}
+
+/*--------------------------------------------------------------*/
+/* Sysfs interface */
+/*--------------------------------------------------------------*/
+
+static ssize_t show_engine_leds(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5523_chip *chip = i2c_get_clientdata(client);
+ char mux[LP5523_LEDS + 1];
+
+ lp5523_mux_to_array(chip->engines[nr - 1].led_mux, mux);
+
+ return sprintf(buf, "%s\n", mux);
+}
+
+#define show_leds(nr) \
+static ssize_t show_engine##nr##_leds(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return show_engine_leds(dev, attr, buf, nr); \
+}
+show_leds(1)
+show_leds(2)
+show_leds(3)
+
+static ssize_t store_engine_leds(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5523_chip *chip = i2c_get_clientdata(client);
+ u16 mux = 0;
+
+ if (lp5523_mux_parse(buf, &mux, len))
+ return -EINVAL;
+
+ if (lp5523_load_mux(&chip->engines[nr - 1], mux))
+ return -EINVAL;
+
+ return len;
+}
+
+#define store_leds(nr) \
+static ssize_t store_engine##nr##_leds(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_leds(dev, attr, buf, len, nr); \
+}
+store_leds(1)
+store_leds(2)
+store_leds(3)
+
+static ssize_t lp5523_selftest(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5523_chip *chip = i2c_get_clientdata(client);
+ int i, ret, pos = 0;
+ int led = 0;
+ u8 status, adc, vdd;
+
+ mutex_lock(&chip->lock);
+
+ ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status);
+ if (ret < 0)
+ goto fail;
+
+ /* Check that ext clock is really in use if requested */
+ if ((chip->pdata) && (chip->pdata->clock_mode == LP5523_CLOCK_EXT))
+ if ((status & LP5523_EXT_CLK_USED) == 0)
+ goto fail;
+
+ /* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */
+ lp5523_write(chip->client, LP5523_REG_LED_TEST_CTRL,
+ LP5523_EN_LEDTEST | 16);
+ usleep_range(3000, 10000);
+ ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status);
+ if (!(status & LP5523_LEDTEST_DONE))
+ usleep_range(3000, 10000);
+
+ ret |= lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &vdd);
+ vdd--; /* There may be some fluctuation in measurement */
+
+ for (i = 0; i < LP5523_LEDS; i++) {
+ /* Skip non-existing channels */
+ if (chip->pdata->led_config[i].led_current == 0)
+ continue;
+
+ /* Set default current */
+ lp5523_write(chip->client,
+ LP5523_REG_LED_CURRENT_BASE + i,
+ chip->pdata->led_config[i].led_current);
+
+ lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0xff);
+ /* let current stabilize 2ms before measurements start */
+ usleep_range(2000, 10000);
+ lp5523_write(chip->client,
+ LP5523_REG_LED_TEST_CTRL,
+ LP5523_EN_LEDTEST | i);
+ /* ledtest takes 2.7ms */
+ usleep_range(3000, 10000);
+ ret = lp5523_read(chip->client, LP5523_REG_STATUS, &status);
+ if (!(status & LP5523_LEDTEST_DONE))
+ usleep_range(3000, 10000);
+ ret |= lp5523_read(chip->client, LP5523_REG_LED_TEST_ADC, &adc);
+
+ if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM)
+ pos += sprintf(buf + pos, "LED %d FAIL\n", i);
+
+ lp5523_write(chip->client, LP5523_REG_LED_PWM_BASE + i, 0x00);
+
+ /* Restore current */
+ lp5523_write(chip->client,
+ LP5523_REG_LED_CURRENT_BASE + i,
+ chip->leds[led].led_current);
+ led++;
+ }
+ if (pos == 0)
+ pos = sprintf(buf, "OK\n");
+ goto release_lock;
+fail:
+ pos = sprintf(buf, "FAIL\n");
+
+release_lock:
+ mutex_unlock(&chip->lock);
+
+ return pos;
+}
+
+static void lp5523_set_brightness(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct lp5523_led *led = cdev_to_led(cdev);
+
+ led->brightness = (u8)brightness;
+
+ schedule_work(&led->brightness_work);
+}
+
+static void lp5523_led_brightness_work(struct work_struct *work)
+{
+ struct lp5523_led *led = container_of(work,
+ struct lp5523_led,
+ brightness_work);
+ struct lp5523_chip *chip = led_to_lp5523(led);
+ struct i2c_client *client = chip->client;
+
+ mutex_lock(&chip->lock);
+
+ lp5523_write(client, LP5523_REG_LED_PWM_BASE + led->chan_nr,
+ led->brightness);
+
+ mutex_unlock(&chip->lock);
+}
+
+static int lp5523_do_store_load(struct lp5523_engine *engine,
+ const char *buf, size_t len)
+{
+ struct lp5523_chip *chip = engine_to_lp5523(engine);
+ struct i2c_client *client = chip->client;
+ int ret, nrchars, offset = 0, i = 0;
+ char c[3];
+ unsigned cmd;
+ u8 pattern[LP5523_PROGRAM_LENGTH] = {0};
+
+ while ((offset < len - 1) && (i < LP5523_PROGRAM_LENGTH)) {
+ /* separate sscanfs because length is working only for %s */
+ ret = sscanf(buf + offset, "%2s%n ", c, &nrchars);
+ ret = sscanf(c, "%2x", &cmd);
+ if (ret != 1)
+ goto fail;
+ pattern[i] = (u8)cmd;
+
+ offset += nrchars;
+ i++;
+ }
+
+ /* Each instruction is 16bit long. Check that length is even */
+ if (i % 2)
+ goto fail;
+
+ mutex_lock(&chip->lock);
+
+ ret = lp5523_load_program(engine, pattern);
+ mutex_unlock(&chip->lock);
+
+ if (ret) {
+ dev_err(&client->dev, "failed loading pattern\n");
+ return ret;
+ }
+
+ return len;
+fail:
+ dev_err(&client->dev, "wrong pattern format\n");
+ return -EINVAL;
+}
+
+static ssize_t store_engine_load(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5523_chip *chip = i2c_get_clientdata(client);
+ return lp5523_do_store_load(&chip->engines[nr - 1], buf, len);
+}
+
+#define store_load(nr) \
+static ssize_t store_engine##nr##_load(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_load(dev, attr, buf, len, nr); \
+}
+store_load(1)
+store_load(2)
+store_load(3)
+
+static ssize_t show_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5523_chip *chip = i2c_get_clientdata(client);
+ switch (chip->engines[nr - 1].mode) {
+ case LP5523_CMD_RUN:
+ return sprintf(buf, "run\n");
+ case LP5523_CMD_LOAD:
+ return sprintf(buf, "load\n");
+ case LP5523_CMD_DISABLED:
+ return sprintf(buf, "disabled\n");
+ default:
+ return sprintf(buf, "disabled\n");
+ }
+}
+
+#define show_mode(nr) \
+static ssize_t show_engine##nr##_mode(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return show_engine_mode(dev, attr, buf, nr); \
+}
+show_mode(1)
+show_mode(2)
+show_mode(3)
+
+static ssize_t store_engine_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len, int nr)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lp5523_chip *chip = i2c_get_clientdata(client);
+ struct lp5523_engine *engine = &chip->engines[nr - 1];
+ mutex_lock(&chip->lock);
+
+ if (!strncmp(buf, "run", 3))
+ lp5523_set_mode(engine, LP5523_CMD_RUN);
+ else if (!strncmp(buf, "load", 4))
+ lp5523_set_mode(engine, LP5523_CMD_LOAD);
+ else if (!strncmp(buf, "disabled", 8))
+ lp5523_set_mode(engine, LP5523_CMD_DISABLED);
+
+ mutex_unlock(&chip->lock);
+ return len;
+}
+
+#define store_mode(nr) \
+static ssize_t store_engine##nr##_mode(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ return store_engine_mode(dev, attr, buf, len, nr); \
+}
+store_mode(1)
+store_mode(2)
+store_mode(3)
+
+static ssize_t show_max_current(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct lp5523_led *led = cdev_to_led(led_cdev);
+
+ return sprintf(buf, "%d\n", led->max_current);
+}
+
+static ssize_t show_current(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct lp5523_led *led = cdev_to_led(led_cdev);
+
+ return sprintf(buf, "%d\n", led->led_current);
+}
+
+static ssize_t store_current(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct lp5523_led *led = cdev_to_led(led_cdev);
+ struct lp5523_chip *chip = led_to_lp5523(led);
+ ssize_t ret;
+ unsigned long curr;
+
+ if (strict_strtoul(buf, 0, &curr))
+ return -EINVAL;
+
+ if (curr > led->max_current)
+ return -EINVAL;
+
+ mutex_lock(&chip->lock);
+ ret = lp5523_write(chip->client,
+ LP5523_REG_LED_CURRENT_BASE + led->chan_nr,
+ (u8)curr);
+ mutex_unlock(&chip->lock);
+
+ if (ret < 0)
+ return ret;
+
+ led->led_current = (u8)curr;
+
+ return len;
+}
+
+/* led class device attributes */
+static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current);
+static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL);
+
+static struct attribute *lp5523_led_attributes[] = {
+ &dev_attr_led_current.attr,
+ &dev_attr_max_current.attr,
+ NULL,
+};
+
+static struct attribute_group lp5523_led_attribute_group = {
+ .attrs = lp5523_led_attributes
+};
+
+/* device attributes */
+static DEVICE_ATTR(engine1_mode, S_IRUGO | S_IWUGO,
+ show_engine1_mode, store_engine1_mode);
+static DEVICE_ATTR(engine2_mode, S_IRUGO | S_IWUGO,
+ show_engine2_mode, store_engine2_mode);
+static DEVICE_ATTR(engine3_mode, S_IRUGO | S_IWUGO,
+ show_engine3_mode, store_engine3_mode);
+static DEVICE_ATTR(engine1_leds, S_IRUGO | S_IWUGO,
+ show_engine1_leds, store_engine1_leds);
+static DEVICE_ATTR(engine2_leds, S_IRUGO | S_IWUGO,
+ show_engine2_leds, store_engine2_leds);
+static DEVICE_ATTR(engine3_leds, S_IRUGO | S_IWUGO,
+ show_engine3_leds, store_engine3_leds);
+static DEVICE_ATTR(engine1_load, S_IWUGO, NULL, store_engine1_load);
+static DEVICE_ATTR(engine2_load, S_IWUGO, NULL, store_engine2_load);
+static DEVICE_ATTR(engine3_load, S_IWUGO, NULL, store_engine3_load);
+static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL);
+
+static struct attribute *lp5523_attributes[] = {
+ &dev_attr_engine1_mode.attr,
+ &dev_attr_engine2_mode.attr,
+ &dev_attr_engine3_mode.attr,
+ &dev_attr_selftest.attr,
+ NULL
+};
+
+static struct attribute *lp5523_engine1_attributes[] = {
+ &dev_attr_engine1_load.attr,
+ &dev_attr_engine1_leds.attr,
+ NULL
+};
+
+static struct attribute *lp5523_engine2_attributes[] = {
+ &dev_attr_engine2_load.attr,
+ &dev_attr_engine2_leds.attr,
+ NULL
+};
+
+static struct attribute *lp5523_engine3_attributes[] = {
+ &dev_attr_engine3_load.attr,
+ &dev_attr_engine3_leds.attr,
+ NULL
+};
+
+static const struct attribute_group lp5523_group = {
+ .attrs = lp5523_attributes,
+};
+
+static const struct attribute_group lp5523_engine_group[] = {
+ {.attrs = lp5523_engine1_attributes },
+ {.attrs = lp5523_engine2_attributes },
+ {.attrs = lp5523_engine3_attributes },
+};
+
+static int lp5523_register_sysfs(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int ret;
+
+ ret = sysfs_create_group(&dev->kobj, &lp5523_group);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void lp5523_unregister_sysfs(struct i2c_client *client)
+{
+ struct lp5523_chip *chip = i2c_get_clientdata(client);
+ struct device *dev = &client->dev;
+ int i;
+
+ sysfs_remove_group(&dev->kobj, &lp5523_group);
+
+ for (i = 0; i < ARRAY_SIZE(chip->engines); i++)
+ if (chip->engines[i].mode == LP5523_CMD_LOAD)
+ sysfs_remove_group(&dev->kobj, &lp5523_engine_group[i]);
+
+ for (i = 0; i < chip->num_leds; i++)
+ sysfs_remove_group(&chip->leds[i].cdev.dev->kobj,
+ &lp5523_led_attribute_group);
+}
+
+/*--------------------------------------------------------------*/
+/* Set chip operating mode */
+/*--------------------------------------------------------------*/
+static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode)
+{
+ /* engine to chip */
+ struct lp5523_chip *chip = engine_to_lp5523(engine);
+ struct i2c_client *client = chip->client;
+ struct device *dev = &client->dev;
+ int ret = 0;
+
+ /* if in that mode already do nothing, except for run */
+ if (mode == engine->mode && mode != LP5523_CMD_RUN)
+ return 0;
+
+ if (mode == LP5523_CMD_RUN) {
+ ret = lp5523_run_program(engine);
+ } else if (mode == LP5523_CMD_LOAD) {
+ lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED);
+ lp5523_set_engine_mode(engine, LP5523_CMD_LOAD);
+
+ ret = sysfs_create_group(&dev->kobj, engine->attributes);
+ if (ret)
+ return ret;
+ } else if (mode == LP5523_CMD_DISABLED) {
+ lp5523_set_engine_mode(engine, LP5523_CMD_DISABLED);
+ }
+
+ /* remove load attribute from sysfs if not in load mode */
+ if (engine->mode == LP5523_CMD_LOAD && mode != LP5523_CMD_LOAD)
+ sysfs_remove_group(&dev->kobj, engine->attributes);
+
+ engine->mode = mode;
+
+ return ret;
+}
+
+/*--------------------------------------------------------------*/
+/* Probe, Attach, Remove */
+/*--------------------------------------------------------------*/
+static int __init lp5523_init_engine(struct lp5523_engine *engine, int id)
+{
+ if (id < 1 || id > LP5523_ENGINES)
+ return -1;
+ engine->id = id;
+ engine->engine_mask = LP5523_ENG_MASK_BASE >> SHIFT_MASK(id);
+ engine->prog_page = id - 1;
+ engine->mux_page = id + 2;
+ engine->attributes = &lp5523_engine_group[id - 1];
+
+ return 0;
+}
+
+static int __init lp5523_init_led(struct lp5523_led *led, struct device *dev,
+ int chan, struct lp5523_platform_data *pdata)
+{
+ char name[32];
+ int res;
+
+ if (chan >= LP5523_LEDS)
+ return -EINVAL;
+
+ if (pdata->led_config[chan].led_current) {
+ led->led_current = pdata->led_config[chan].led_current;
+ led->max_current = pdata->led_config[chan].max_current;
+ led->chan_nr = pdata->led_config[chan].chan_nr;
+
+ if (led->chan_nr >= LP5523_LEDS) {
+ dev_err(dev, "Use channel numbers between 0 and %d\n",
+ LP5523_LEDS - 1);
+ return -EINVAL;
+ }
+
+ snprintf(name, 32, "lp5523:channel%d", chan);
+
+ led->cdev.name = name;
+ led->cdev.brightness_set = lp5523_set_brightness;
+ res = led_classdev_register(dev, &led->cdev);
+ if (res < 0) {
+ dev_err(dev, "couldn't register led on channel %d\n",
+ chan);
+ return res;
+ }
+ res = sysfs_create_group(&led->cdev.dev->kobj,
+ &lp5523_led_attribute_group);
+ if (res < 0) {
+ dev_err(dev, "couldn't register current attribute\n");
+ led_classdev_unregister(&led->cdev);
+ return res;
+ }
+ } else {
+ led->led_current = 0;
+ }
+ return 0;
+}
+
+static struct i2c_driver lp5523_driver;
+
+static int lp5523_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lp5523_chip *chip;
+ struct lp5523_platform_data *pdata;
+ int ret, i, led;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, chip);
+ chip->client = client;
+
+ pdata = client->dev.platform_data;
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data\n");
+ ret = -EINVAL;
+ goto fail1;
+ }
+
+ mutex_init(&chip->lock);
+
+ chip->pdata = pdata;
+
+ if (pdata->setup_resources) {
+ ret = pdata->setup_resources();
+ if (ret < 0)
+ goto fail1;
+ }
+
+ if (pdata->enable) {
+ pdata->enable(0);
+ usleep_range(1000, 10000);
+ pdata->enable(1);
+ usleep_range(1000, 10000); /* Spec says min 500us */
+ }
+
+ ret = lp5523_detect(client);
+ if (ret)
+ goto fail2;
+
+ dev_info(&client->dev, "LP5523 Programmable led chip found\n");
+
+ /* Initialize engines */
+ for (i = 0; i < ARRAY_SIZE(chip->engines); i++) {
+ ret = lp5523_init_engine(&chip->engines[i], i + 1);
+ if (ret) {
+ dev_err(&client->dev, "error initializing engine\n");
+ goto fail2;
+ }
+ }
+ ret = lp5523_configure(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "error configuring chip\n");
+ goto fail2;
+ }
+
+ /* Initialize leds */
+ chip->num_channels = pdata->num_channels;
+ chip->num_leds = 0;
+ led = 0;
+ for (i = 0; i < pdata->num_channels; i++) {
+ /* Do not initialize channels that are not connected */
+ if (pdata->led_config[i].led_current == 0)
+ continue;
+
+ ret = lp5523_init_led(&chip->leds[led], &client->dev, i, pdata);
+ if (ret) {
+ dev_err(&client->dev, "error initializing leds\n");
+ goto fail3;
+ }
+ chip->num_leds++;
+
+ chip->leds[led].id = led;
+ /* Set LED current */
+ lp5523_write(client,
+ LP5523_REG_LED_CURRENT_BASE + chip->leds[led].chan_nr,
+ chip->leds[led].led_current);
+
+ INIT_WORK(&(chip->leds[led].brightness_work),
+ lp5523_led_brightness_work);
+
+ led++;
+ }
+
+ ret = lp5523_register_sysfs(client);
+ if (ret) {
+ dev_err(&client->dev, "registering sysfs failed\n");
+ goto fail3;
+ }
+ return ret;
+fail3:
+ for (i = 0; i < chip->num_leds; i++) {
+ led_classdev_unregister(&chip->leds[i].cdev);
+ cancel_work_sync(&chip->leds[i].brightness_work);
+ }
+fail2:
+ if (pdata->enable)
+ pdata->enable(0);
+ if (pdata->release_resources)
+ pdata->release_resources();
+fail1:
+ kfree(chip);
+ return ret;
+}
+
+static int lp5523_remove(struct i2c_client *client)
+{
+ struct lp5523_chip *chip = i2c_get_clientdata(client);
+ int i;
+
+ lp5523_unregister_sysfs(client);
+
+ for (i = 0; i < chip->num_leds; i++) {
+ led_classdev_unregister(&chip->leds[i].cdev);
+ cancel_work_sync(&chip->leds[i].brightness_work);
+ }
+
+ if (chip->pdata->enable)
+ chip->pdata->enable(0);
+ if (chip->pdata->release_resources)
+ chip->pdata->release_resources();
+ kfree(chip);
+ return 0;
+}
+
+static const struct i2c_device_id lp5523_id[] = {
+ { "lp5523", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, lp5523_id);
+
+static struct i2c_driver lp5523_driver = {
+ .driver = {
+ .name = "lp5523",
+ },
+ .probe = lp5523_probe,
+ .remove = lp5523_remove,
+ .id_table = lp5523_id,
+};
+
+static int __init lp5523_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&lp5523_driver);
+
+ if (ret < 0)
+ printk(KERN_ALERT "Adding lp5523 driver failed\n");
+
+ return ret;
+}
+
+static void __exit lp5523_exit(void)
+{
+ i2c_del_driver(&lp5523_driver);
+}
+
+module_init(lp5523_init);
+module_exit(lp5523_exit);
+
+MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>");
+MODULE_DESCRIPTION("LP5523 LED engine");
+MODULE_LICENSE("GPL");
*/
#include <linux/module.h>
-#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
#include <linux/device.h>
-#include <linux/sysdev.h>
-#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/leds.h>
-#include <linux/slab.h>
#include "leds.h"
-struct timer_trig_data {
- int brightness_on; /* LED brightness during "on" period.
- * (LED_OFF < brightness_on <= LED_FULL)
- */
- unsigned long delay_on; /* milliseconds on */
- unsigned long delay_off; /* milliseconds off */
- struct timer_list timer;
-};
-
-static void led_timer_function(unsigned long data)
-{
- struct led_classdev *led_cdev = (struct led_classdev *) data;
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
- unsigned long brightness;
- unsigned long delay;
-
- if (!timer_data->delay_on || !timer_data->delay_off) {
- led_set_brightness(led_cdev, LED_OFF);
- return;
- }
-
- brightness = led_get_brightness(led_cdev);
- if (!brightness) {
- /* Time to switch the LED on. */
- brightness = timer_data->brightness_on;
- delay = timer_data->delay_on;
- } else {
- /* Store the current brightness value to be able
- * to restore it when the delay_off period is over.
- */
- timer_data->brightness_on = brightness;
- brightness = LED_OFF;
- delay = timer_data->delay_off;
- }
-
- led_set_brightness(led_cdev, brightness);
-
- mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
-}
-
static ssize_t led_delay_on_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
- return sprintf(buf, "%lu\n", timer_data->delay_on);
+ return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
}
static ssize_t led_delay_on_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
int ret = -EINVAL;
char *after;
unsigned long state = simple_strtoul(buf, &after, 10);
count++;
if (count == size) {
- if (timer_data->delay_on != state) {
- /* the new value differs from the previous */
- timer_data->delay_on = state;
-
- /* deactivate previous settings */
- del_timer_sync(&timer_data->timer);
-
- /* try to activate hardware acceleration, if any */
- if (!led_cdev->blink_set ||
- led_cdev->blink_set(led_cdev,
- &timer_data->delay_on, &timer_data->delay_off)) {
- /* no hardware acceleration, blink via timer */
- mod_timer(&timer_data->timer, jiffies + 1);
- }
- }
+ led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);
ret = count;
}
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
- return sprintf(buf, "%lu\n", timer_data->delay_off);
+ return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
}
static ssize_t led_delay_off_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
int ret = -EINVAL;
char *after;
unsigned long state = simple_strtoul(buf, &after, 10);
count++;
if (count == size) {
- if (timer_data->delay_off != state) {
- /* the new value differs from the previous */
- timer_data->delay_off = state;
-
- /* deactivate previous settings */
- del_timer_sync(&timer_data->timer);
-
- /* try to activate hardware acceleration, if any */
- if (!led_cdev->blink_set ||
- led_cdev->blink_set(led_cdev,
- &timer_data->delay_on, &timer_data->delay_off)) {
- /* no hardware acceleration, blink via timer */
- mod_timer(&timer_data->timer, jiffies + 1);
- }
- }
+ led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);
ret = count;
}
static void timer_trig_activate(struct led_classdev *led_cdev)
{
- struct timer_trig_data *timer_data;
int rc;
- timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);
- if (!timer_data)
- return;
-
- timer_data->brightness_on = led_get_brightness(led_cdev);
- if (timer_data->brightness_on == LED_OFF)
- timer_data->brightness_on = led_cdev->max_brightness;
- led_cdev->trigger_data = timer_data;
-
- init_timer(&timer_data->timer);
- timer_data->timer.function = led_timer_function;
- timer_data->timer.data = (unsigned long) led_cdev;
+ led_cdev->trigger_data = NULL;
rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
if (rc)
- goto err_out;
+ return;
rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
if (rc)
goto err_out_delayon;
- /* If there is hardware support for blinking, start one
- * user friendly blink rate chosen by the driver.
- */
- if (led_cdev->blink_set)
- led_cdev->blink_set(led_cdev,
- &timer_data->delay_on, &timer_data->delay_off);
+ led_cdev->trigger_data = (void *)1;
return;
err_out_delayon:
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
-err_out:
- led_cdev->trigger_data = NULL;
- kfree(timer_data);
}
static void timer_trig_deactivate(struct led_classdev *led_cdev)
{
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
- unsigned long on = 0, off = 0;
-
- if (timer_data) {
+ if (led_cdev->trigger_data) {
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
- del_timer_sync(&timer_data->timer);
- kfree(timer_data);
}
- /* If there is hardware support for blinking, stop it */
- if (led_cdev->blink_set)
- led_cdev->blink_set(led_cdev, &on, &off);
+ /* Stop blinking */
+ led_brightness_set(led_cdev, LED_OFF);
}
static struct led_trigger timer_led_trigger = {
static void adb_iop_complete(struct iop_msg *msg)
{
struct adb_request *req;
- uint flags;
+ unsigned long flags;
local_irq_save(flags);
{
struct adb_iopmsg *amsg = (struct adb_iopmsg *) msg->message;
struct adb_request *req;
- uint flags;
+ unsigned long flags;
#ifdef DEBUG_ADB_IOP
int i;
#endif
{
struct i2c_client *client = to_i2c_client(dev);
struct als_data *data = i2c_get_clientdata(client);
- unsigned int ret_val;
+ int ret_val;
unsigned long val;
if (strict_strtoul(buf, 10, &val))
{
struct bh1770_chip *chip = dev_get_drvdata(dev);
unsigned long value;
- size_t ret;
+ ssize_t ret;
if (strict_strtoul(buf, 0, &value))
return -EINVAL;
pm_runtime_get_sync(dev);
ret = bh1770_lux_rate(chip, chip->lux_rate_index);
- ret |= bh1770_lux_interrupt_control(chip, BH1770_ENABLE);
+ if (ret < 0) {
+ pm_runtime_put(dev);
+ goto leave;
+ }
+ ret = bh1770_lux_interrupt_control(chip, BH1770_ENABLE);
if (ret < 0) {
pm_runtime_put(dev);
goto leave;
struct device_attribute *attr, const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
- unsigned int ret_val;
+ int ret_val;
unsigned long val;
if (strict_strtoul(buf, 10, &val))
val = 4;
ret_val = i2c_smbus_read_byte_data(client, 0x00);
+ if (ret_val < 0)
+ return ret_val;
ret_val &= 0xFC; /*reset the bit before setting them */
ret_val |= val - 1;
boolean
default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n)
-comment "rt2x00 leds support disabled due to modularized LEDS_CLASS and built-in rt2x00"
- depends on RT2X00_LIB=y && LEDS_CLASS=m
-
config RT2X00_LIB_DEBUGFS
bool "Ralink debugfs support"
depends on RT2X00_LIB && MAC80211_DEBUGFS
list_for_each_entry(port, &rio_mports, node) {
if (!request_mem_region(port->iores.start,
- port->iores.end - port->iores.start,
+ resource_size(&port->iores),
port->name)) {
printk(KERN_ERR
"RIO: Error requesting master port region 0x%016llx-0x%016llx\n",
- (u64)port->iores.start, (u64)port->iores.end - 1);
+ (u64)port->iores.start, (u64)port->iores.end);
rc = -ENOMEM;
goto out;
}
#ifdef CONFIG_SH_SECUREEDGE5410
#include <asm/rtc.h>
-#include <mach/snapgear.h>
+#include <mach/secureedge5410.h>
#define RTC_RESET 0x1000
#define RTC_IODATA 0x0800
static long clk_rate_round_helper(struct clk_rate_round_data *rounder)
{
unsigned long rate_error, rate_error_prev = ~0UL;
- unsigned long rate_best_fit = rounder->rate;
unsigned long highest, lowest, freq;
+ long rate_best_fit = -ENOENT;
int i;
highest = 0;
};
if (clk->nr_freqs < 1)
- return 0;
+ return -ENOSYS;
return clk_rate_round_helper(&table_round);
}
}
EXPORT_SYMBOL_GPL(clk_round_rate);
+long clk_round_parent(struct clk *clk, unsigned long target,
+ unsigned long *best_freq, unsigned long *parent_freq,
+ unsigned int div_min, unsigned int div_max)
+{
+ struct cpufreq_frequency_table *freq, *best = NULL;
+ unsigned long error = ULONG_MAX, freq_high, freq_low, div;
+ struct clk *parent = clk_get_parent(clk);
+
+ if (!parent) {
+ *parent_freq = 0;
+ *best_freq = clk_round_rate(clk, target);
+ return abs(target - *best_freq);
+ }
+
+ for (freq = parent->freq_table; freq->frequency != CPUFREQ_TABLE_END;
+ freq++) {
+ if (freq->frequency == CPUFREQ_ENTRY_INVALID)
+ continue;
+
+ if (unlikely(freq->frequency / target <= div_min - 1)) {
+ unsigned long freq_max;
+
+ freq_max = (freq->frequency + div_min / 2) / div_min;
+ if (error > target - freq_max) {
+ error = target - freq_max;
+ best = freq;
+ if (best_freq)
+ *best_freq = freq_max;
+ }
+
+ pr_debug("too low freq %lu, error %lu\n", freq->frequency,
+ target - freq_max);
+
+ if (!error)
+ break;
+
+ continue;
+ }
+
+ if (unlikely(freq->frequency / target >= div_max)) {
+ unsigned long freq_min;
+
+ freq_min = (freq->frequency + div_max / 2) / div_max;
+ if (error > freq_min - target) {
+ error = freq_min - target;
+ best = freq;
+ if (best_freq)
+ *best_freq = freq_min;
+ }
+
+ pr_debug("too high freq %lu, error %lu\n", freq->frequency,
+ freq_min - target);
+
+ if (!error)
+ break;
+
+ continue;
+ }
+
+ div = freq->frequency / target;
+ freq_high = freq->frequency / div;
+ freq_low = freq->frequency / (div + 1);
+
+ if (freq_high - target < error) {
+ error = freq_high - target;
+ best = freq;
+ if (best_freq)
+ *best_freq = freq_high;
+ }
+
+ if (target - freq_low < error) {
+ error = target - freq_low;
+ best = freq;
+ if (best_freq)
+ *best_freq = freq_low;
+ }
+
+ pr_debug("%u / %lu = %lu, / %lu = %lu, best %lu, parent %u\n",
+ freq->frequency, div, freq_high, div + 1, freq_low,
+ *best_freq, best->frequency);
+
+ if (!error)
+ break;
+ }
+
+ if (parent_freq)
+ *parent_freq = best->frequency;
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(clk_round_parent);
+
#ifdef CONFIG_PM
static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state)
{
* Register the IRQ position with the global IRQ map, then insert
* it in to the radix tree.
*/
- irq_reserve_irqs(irq, 1);
+ irq_reserve_irq(irq);
raw_spin_lock_irqsave(&intc_big_lock, flags);
radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq));
int i;
for (i = 0; i < nr_vecs; i++)
- irq_reserve_irqs(evt2irq(vectors[i].vect), 1);
+ irq_reserve_irq(evt2irq(vectors[i].vect));
}
do {
/* check if host supports scatter requests and it meets our requirements */
- if (device->func->card->host->max_hw_segs < MAX_SCATTER_ENTRIES_PER_REQ) {
+ if (device->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HIF-SCATTER : host only supports scatter of : %d entries, need: %d \n",
- device->func->card->host->max_hw_segs, MAX_SCATTER_ENTRIES_PER_REQ));
+ device->func->card->host->max_segs, MAX_SCATTER_ENTRIES_PER_REQ));
status = A_ENOTSUP;
break;
}
&solo_enc->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
- sizeof(struct videobuf_buffer), fh);
+ sizeof(struct videobuf_buffer), fh, NULL);
spin_unlock(&solo_enc->lock);
&solo_dev->pdev->dev, &fh->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
SOLO_DISP_PIX_FIELD,
- sizeof(struct videobuf_buffer), fh);
+ sizeof(struct videobuf_buffer), fh, NULL);
return 0;
}
--- /dev/null
+obj-y += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
+ tty_buffer.o tty_port.o tty_mutex.o
+obj-$(CONFIG_LEGACY_PTYS) += pty.o
+obj-$(CONFIG_UNIX98_PTYS) += pty.o
+obj-$(CONFIG_AUDIT) += tty_audit.o
+obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
+obj-$(CONFIG_N_HDLC) += n_hdlc.o
+obj-$(CONFIG_N_GSM) += n_gsm.o
+obj-$(CONFIG_R3964) += n_r3964.o
+
+obj-y += vt/
--- /dev/null
+/*
+ * 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 <linux/types.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/bitops.h>
+#include <linux/file.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/tty_flip.h>
+#include <linux/tty_driver.h>
+#include <linux/serial.h>
+#include <linux/kfifo.h>
+#include <linux/skbuff.h>
+#include <linux/gsmmux.h>
+
+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);
--- /dev/null
+/* 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 <callahan@maths.ox.ac.uk>,
+ * Al Longyear <longyear@netcom.com>,
+ * Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+
+#undef VERSION
+#define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch))
+
+#include <linux/poll.h>
+#include <linux/in.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/smp_lock.h>
+#include <linux/string.h> /* used in new tty drivers */
+#include <linux/signal.h> /* used in new tty drivers */
+#include <linux/if.h>
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/termios.h>
+#include <asm/uaccess.h>
+
+/*
+ * 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;i<DEFAULT_RX_BUF_COUNT;i++) {
+ buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL);
+ if (buf)
+ n_hdlc_buf_put(&n_hdlc->rx_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;i<DEFAULT_TX_BUF_COUNT;i++) {
+ buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL);
+ if (buf)
+ n_hdlc_buf_put(&n_hdlc->tx_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);
--- /dev/null
+/* 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h> /* used in new tty drivers */
+#include <linux/signal.h> /* used in new tty drivers */
+#include <linux/ioctl.h>
+#include <linux/n_r3964.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+/*#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);
--- /dev/null
+/*
+ * 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 <ag784@freenet.buffalo.edu>
+ * 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 <sapan@corewars.org>.
+ * Also fixed a bug in BLOCKING mode where n_tty_write returns
+ * EAGAIN
+ */
+
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/bitops.h>
+#include <linux/audit.h>
+#include <linux/file.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+
+/* number of characters left in xmit buffer before select has we have room */
+#define WAKEUP_CHARS 256
+
+/*
+ * This defines the low- and high-watermarks for throttling and
+ * unthrottling the TTY driver. These watermarks are used for
+ * controlling the space in the read buffer.
+ */
+#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
+#define TTY_THRESHOLD_UNTHROTTLE 128
+
+/*
+ * 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);
--- /dev/null
+/*
+ * linux/drivers/char/pty.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Added support for a Unix98-style ptmx device.
+ * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 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 <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/sysctl.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/bitops.h>
+#include <linux/devpts_fs.h>
+#include <linux/slab.h>
+
+#include <asm/system.h>
+
+#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);
--- /dev/null
+/*
+ * Linux Magic System Request Key Hacks
+ *
+ * (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ * based on ideas by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>
+ *
+ * (c) 2000 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
+ * 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 <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/reboot.h>
+#include <linux/sysrq.h>
+#include <linux/kbd_kern.h>
+#include <linux/proc_fs.h>
+#include <linux/nmi.h>
+#include <linux/quotaops.h>
+#include <linux/perf_event.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/suspend.h>
+#include <linux/writeback.h>
+#include <linux/buffer_head.h> /* for fsync_bdev() */
+#include <linux/swap.h>
+#include <linux/spinlock.h>
+#include <linux/vt_kern.h>
+#include <linux/workqueue.h>
+#include <linux/hrtimer.h>
+#include <linux/oom.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+
+#include <asm/ptrace.h>
+#include <asm/irq_regs.h>
+
+/* 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 <linux/ftrace.h>
+
+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);
--- /dev/null
+/*
+ * 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 <mitr@redhat.com>
+ */
+
+#include <linux/audit.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+
+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);
+ }
+}
--- /dev/null
+/*
+ * Tty buffer allocation management
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+/**
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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 <alfie@dcs.warwick.ac.uk>, 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 <Marko.Kohtala@hut.fi>, March 97
+ *
+ * Rewrote tty_init_dev and tty_release_dev to eliminate races.
+ * -- Bill Hawes <whawes@star.net>, June 97
+ *
+ * Added devfs support.
+ * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998
+ *
+ * Added support for a Unix98-style ptmx device.
+ * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
+ *
+ * Reduced memory usage for older ARM systems
+ * -- Russell King <rmk@arm.linux.org.uk>
+ *
+ * Move do_SAK() into process context. Less stack use in devfs functions.
+ * alloc_tty_struct() always uses kmalloc()
+ * -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01
+ */
+
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/devpts_fs.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
+#include <linux/console.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/kd.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/serial.h>
+
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+
+#include <linux/kmod.h>
+#include <linux/nsproxy.h>
+
+#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;
+}
+
--- /dev/null
+/*
+ * 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 <linux/types.h>
+#include <linux/termios.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/tty.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#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);
--- /dev/null
+#include <linux/types.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/devpts_fs.h>
+#include <linux/file.h>
+#include <linux/console.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/kd.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+
+#include <linux/smp_lock.h> /* For the moment */
+
+#include <linux/kmod.h>
+#include <linux/nsproxy.h>
+
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * drivers/char/tty_lock.c
+ */
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/semaphore.h>
+#include <linux/sched.h>
+
+/*
+ * 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);
--- /dev/null
+/*
+ * Tty port functions
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+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);
--- /dev/null
+consolemap_deftbl.c
+defkeymap.c
--- /dev/null
+#
+# This file contains the font map for the default (hardware) font
+#
+FONTMAPFILE = cp437.uni
+
+obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o \
+ selection.o keyboard.o
+obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o
+obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.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
--- /dev/null
+/*
+ * 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 <jj@ultra.linux.cz>, July 1998
+ *
+ * Fix bug in inverse translation. Stanislav Voronyi <stas@cnti.uanet.kharkov.ua>, Dec 1998
+ */
+
+#include <linux/module.h>
+#include <linux/kd.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <asm/uaccess.h>
+#include <linux/consolemap.h>
+#include <linux/vt_kern.h>
+
+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<E_TABSZ ; i++) {
+ unsigned char uc;
+ __get_user(uc, arg+i);
+ p[i] = UNI_DIRECT_BASE | uc;
+ }
+
+ update_user_maps();
+ return 0;
+}
+
+int con_get_trans_old(unsigned char __user * arg)
+{
+ int i, ch;
+ unsigned short *p = translations[USER_MAP];
+
+ if (!access_ok(VERIFY_WRITE, arg, E_TABSZ))
+ return -EFAULT;
+
+ for (i=0; i<E_TABSZ ; i++)
+ {
+ ch = conv_uni_to_pc(vc_cons[fg_console].d, p[i]);
+ __put_user((ch & ~0xff) ? 0 : ch, arg+i);
+ }
+ return 0;
+}
+
+int con_set_trans_new(ushort __user * arg)
+{
+ int i;
+ unsigned short *p = translations[USER_MAP];
+
+ if (!access_ok(VERIFY_READ, arg, E_TABSZ*sizeof(unsigned short)))
+ return -EFAULT;
+
+ for (i=0; i<E_TABSZ ; i++) {
+ unsigned short us;
+ __get_user(us, arg+i);
+ p[i] = us;
+ }
+
+ update_user_maps();
+ return 0;
+}
+
+int con_get_trans_new(ushort __user * arg)
+{
+ int i;
+ unsigned short *p = translations[USER_MAP];
+
+ if (!access_ok(VERIFY_WRITE, arg, E_TABSZ*sizeof(unsigned short)))
+ return -EFAULT;
+
+ for (i=0; i<E_TABSZ ; i++)
+ __put_user(p[i], arg+i);
+
+ return 0;
+}
+
+/*
+ * Unicode -> 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);
--- /dev/null
+#
+# 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
--- /dev/null
+/* Do not edit this file! It was automatically generated by */
+/* loadkeys --mktable defkeymap.map > defkeymap.c */
+
+#include <linux/types.h>
+#include <linux/keyboard.h>
+#include <linux/kd.h>
+
+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;
--- /dev/null
+# 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 'ÿ'
--- /dev/null
+/*
+ * 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 <linux/consolemap.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/kbd_diacr.h>
+#include <linux/vt_kern.h>
+#include <linux/input.h>
+#include <linux/reboot.h>
+#include <linux/notifier.h>
+#include <linux/jiffies.h>
+
+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;
+}
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/consolemap.h>
+#include <linux/selection.h>
+#include <linux/tiocl.h>
+#include <linux/console.h>
+#include <linux/smp_lock.h>
+
+/* Don't take this from <ctype.h>: 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;
+}
--- /dev/null
+/*
+ * 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 <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/interrupt.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/kbd_kern.h>
+#include <linux/console.h>
+#include <linux/device.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+#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;
+}
--- /dev/null
+/*
+ * 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,
+ * <g-hunt@ee.utah.edu>
+ *
+ * Rudimentary ISO 10646/Unicode/UTF-8 character set support by
+ * Markus Kuhn, <mskuhn@immd4.informatik.uni-erlangen.de>.
+ *
+ * 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
+ * <poe@daimi.aau.dk>
+ *
+ * User-defined bell sound, new setterm control sequences and printk
+ * redirection by Martin Mares <mj@k332.feld.cvut.cz> 19-Nov-95
+ *
+ * APM screenblank bug fixed Takashi Manabe <manabe@roy.dsl.tutics.tut.jp>
+ *
+ * Merge with the abstract console driver by Geert Uytterhoeven
+ * <geert@linux-m68k.org>, Jan 1997.
+ *
+ * Original m68k console driver modifications by
+ *
+ * - Arno Griffioen <arno@usn.nl>
+ * - David Carter <carter@cs.bris.ac.uk>
+ *
+ * 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 <linux/console.h> for more information).
+ *
+ * Support for changeable cursor shape
+ * by Pavel Machek <pavel@atrey.karlin.mff.cuni.cz>, August 1997
+ *
+ * Ported to i386 and con_scrolldelta fixed
+ * by Emmanuel Marty <core@ggi-project.org>, April 1998
+ *
+ * Resurrected character buffers in videoram plus lots of other trickery
+ * by Martin Mares <mj@atrey.karlin.mff.cuni.cz>, 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 <atlka@pg.gda.pl>, Aug 2006
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kd.h>
+#include <linux/slab.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <linux/smp_lock.h>
+#include <linux/tiocl.h>
+#include <linux/kbd_kern.h>
+#include <linux/consolemap.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pm.h>
+#include <linux/font.h>
+#include <linux/bitops.h>
+#include <linux/notifier.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/kdb.h>
+#include <linux/ctype.h>
+
+#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
--- /dev/null
+/*
+ * 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 <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/compat.h>
+#include <linux/module.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/console.h>
+#include <linux/consolemap.h>
+#include <linux/signal.h>
+#include <linux/smp_lock.h>
+#include <linux/timex.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/kbd_diacr.h>
+#include <linux/selection.h>
+
+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 <linux/syscalls.h>
+#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; i<MAX_NR_CONSOLES; i++)
+ if (! VT_BUSY(i))
+ vc_deallocate(i);
+ release_console_sem();
+ } else {
+ /* deallocate a single console, if possible */
+ arg--;
+ if (VT_BUSY(arg))
+ ret = -EBUSY;
+ else if (arg) { /* leave 0 */
+ acquire_console_sem();
+ vc_deallocate(arg);
+ release_console_sem();
+ }
+ }
+ break;
+
+ case VT_RESIZE:
+ {
+ struct vt_sizes __user *vtsizes = up;
+ struct vc_data *vc;
+
+ ushort ll,cc;
+ if (!perm)
+ goto eperm;
+ if (get_user(ll, &vtsizes->v_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);
struct device_attribute *attr, const char *buf, size_t count)
{
struct adp8860_bl *data = dev_get_drvdata(dev);
+ int ret = strict_strtoul(buf, 10, &data->cached_daylight_max);
+ if (ret)
+ return ret;
- strict_strtoul(buf, 10, &data->cached_daylight_max);
return adp8860_store(dev, buf, count, ADP8860_BLMX1);
}
static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show,
if (val == 0) {
/* Enable automatic ambient light sensing */
adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN);
- } else if ((val > 0) && (val < 6)) {
+ } else if ((val > 0) && (val <= 3)) {
/* Disable automatic ambient light sensing */
adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN);
mutex_lock(&data->lock);
adp8860_read(data->client, ADP8860_CFGR, ®_val);
reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
- reg_val |= val << CFGR_BLV_SHIFT;
+ reg_val |= (val - 1) << CFGR_BLV_SHIFT;
adp8860_write(data->client, ADP8860_CFGR, reg_val);
mutex_unlock(&data->lock);
}
const u16 slpin = 0x10;
const u16 disoff = 0x28;
- if (power) {
+ if (power <= FB_BLANK_NORMAL) {
if (priv->lcd_on)
return 0;
struct spi_device *spi = st->spi;
struct lms283gf05_pdata *pdata = spi->dev.platform_data;
- if (power) {
+ if (power <= FB_BLANK_NORMAL) {
if (pdata)
lms283gf05_reset(pdata->reset_gpio,
pdata->reset_inverted);
},
.driver_data = (void *)&nvidia_chipset_data,
},
+ {
+ .callback = mbp_dmi_match,
+ .ident = "MacBookAir 3,1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir3,1"),
+ },
+ .driver_data = (void *)&nvidia_chipset_data,
+ },
+ {
+ .callback = mbp_dmi_match,
+ .ident = "MacBookAir 3,2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir3,2"),
+ },
+ .driver_data = (void *)&nvidia_chipset_data,
+ },
{ }
};
struct pwm_device *pwm;
struct device *dev;
unsigned int period;
+ unsigned int lth_brightness;
int (*notify)(struct device *,
int brightness);
};
pwm_config(pb->pwm, 0, pb->period);
pwm_disable(pb->pwm);
} else {
- pwm_config(pb->pwm, brightness * pb->period / max, pb->period);
+ brightness = pb->lth_brightness +
+ (brightness * (pb->period - pb->lth_brightness) / max);
+ pwm_config(pb->pwm, brightness, pb->period);
pwm_enable(pb->pwm);
}
return 0;
pb->period = data->pwm_period_ns;
pb->notify = data->notify;
+ pb->lth_brightness = data->lth_brightness *
+ (data->pwm_period_ns / data->max_brightness);
pb->dev = &pdev->dev;
pb->pwm = pwm_request(data->pwm_id, "backlight");
return strlen(buf);
}
-static DEVICE_ATTR(gamma_table, 0644,
+static DEVICE_ATTR(gamma_table, 0444,
s6e63m0_sysfs_show_gamma_table, NULL);
-static int __init s6e63m0_probe(struct spi_device *spi)
+static int __devinit s6e63m0_probe(struct spi_device *spi)
{
int ret = 0;
struct s6e63m0 *lcd = NULL;
struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
+ device_remove_file(&spi->dev, &dev_attr_gamma_table);
+ device_remove_file(&spi->dev, &dev_attr_gamma_mode);
+ backlight_device_unregister(lcd->bd);
lcd_device_unregister(lcd->ld);
kfree(lcd);
setattr_copy(inode, attrs);
mark_inode_dirty(inode);
- return 0;
cifs_setattr_exit:
kfree(full_path);
struct cifs_sb_info *cifs_sb;
#ifdef CONFIG_CIFS_POSIX
struct cifsFileInfo *pSMBFile = filep->private_data;
- struct cifsTconInfo *tcon = tlink_tcon(pSMBFile->tlink);
+ struct cifsTconInfo *tcon;
__u64 ExtAttrBits = 0;
__u64 ExtAttrMask = 0;
- __u64 caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
+ __u64 caps;
#endif /* CONFIG_CIFS_POSIX */
xid = GetXid();
break;
#ifdef CONFIG_CIFS_POSIX
case FS_IOC_GETFLAGS:
+ if (pSMBFile == NULL)
+ break;
+ tcon = tlink_tcon(pSMBFile->tlink);
+ caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
if (CIFS_UNIX_EXTATTR_CAP & caps) {
rc = CIFSGetExtAttr(xid, tcon, pSMBFile->netfid,
&ExtAttrBits, &ExtAttrMask);
break;
case FS_IOC_SETFLAGS:
+ if (pSMBFile == NULL)
+ break;
+ tcon = tlink_tcon(pSMBFile->tlink);
+ caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
if (CIFS_UNIX_EXTATTR_CAP & caps) {
if (get_user(ExtAttrBits, (int __user *)arg)) {
rc = -EFAULT;
struct ext4_io_page {
struct page *p_page;
- int p_count;
+ atomic_t p_count;
};
#define MAX_IO_PAGES 128
spinlock_t i_completed_io_lock;
/* current io_end structure for async DIO write*/
ext4_io_end_t *cur_aio_dio;
+ atomic_t i_ioend_count; /* Number of outstanding io_end structs */
/*
* Transactions that contain inode's metadata needed to complete
/* page-io.c */
extern int __init ext4_init_pageio(void);
extern void ext4_exit_pageio(void);
+extern void ext4_ioend_wait(struct inode *);
extern void ext4_free_io_end(ext4_io_end_t *io);
extern ext4_io_end_t *ext4_init_io_end(struct inode *inode, gfp_t flags);
extern int ext4_end_io_nolock(ext4_io_end_t *io);
static inline int ext4_begin_ordered_truncate(struct inode *inode,
loff_t new_size)
{
+ trace_ext4_begin_ordered_truncate(inode, new_size);
return jbd2_journal_begin_ordered_truncate(
EXT4_SB(inode->i_sb)->s_journal,
&EXT4_I(inode)->jinode,
handle_t *handle;
int err;
+ trace_ext4_evict_inode(inode);
if (inode->i_nlink) {
truncate_inode_pages(&inode->i_data, 0);
goto no_delete;
int err, ret;
might_sleep();
+ trace_ext4_mark_inode_dirty(inode, _RET_IP_);
err = ext4_reserve_inode_write(handle, inode, &iloc);
if (ext4_handle_valid(handle) &&
EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize &&
* with group lock held. generate_buddy look at
* them with group lock_held
*/
- if (test_opt(sb, DISCARD))
- ext4_issue_discard(sb, block_group, bit, count);
ext4_lock_group(sb, block_group);
mb_clear_bits(bitmap_bh->b_data, bit, count);
mb_free_blocks(inode, &e4b, bit, count);
static struct kmem_cache *io_page_cachep, *io_end_cachep;
+#define WQ_HASH_SZ 37
+#define to_ioend_wq(v) (&ioend_wq[((unsigned long)v) % WQ_HASH_SZ])
+static wait_queue_head_t ioend_wq[WQ_HASH_SZ];
+
int __init ext4_init_pageio(void)
{
+ int i;
+
io_page_cachep = KMEM_CACHE(ext4_io_page, SLAB_RECLAIM_ACCOUNT);
if (io_page_cachep == NULL)
return -ENOMEM;
kmem_cache_destroy(io_page_cachep);
return -ENOMEM;
}
+ for (i = 0; i < WQ_HASH_SZ; i++)
+ init_waitqueue_head(&ioend_wq[i]);
return 0;
}
kmem_cache_destroy(io_page_cachep);
}
+void ext4_ioend_wait(struct inode *inode)
+{
+ wait_queue_head_t *wq = to_ioend_wq(inode);
+
+ wait_event(*wq, (atomic_read(&EXT4_I(inode)->i_ioend_count) == 0));
+}
+
+static void put_io_page(struct ext4_io_page *io_page)
+{
+ if (atomic_dec_and_test(&io_page->p_count)) {
+ end_page_writeback(io_page->p_page);
+ put_page(io_page->p_page);
+ kmem_cache_free(io_page_cachep, io_page);
+ }
+}
+
void ext4_free_io_end(ext4_io_end_t *io)
{
int i;
+ wait_queue_head_t *wq;
BUG_ON(!io);
if (io->page)
put_page(io->page);
- for (i = 0; i < io->num_io_pages; i++) {
- if (--io->pages[i]->p_count == 0) {
- struct page *page = io->pages[i]->p_page;
-
- end_page_writeback(page);
- put_page(page);
- kmem_cache_free(io_page_cachep, io->pages[i]);
- }
- }
+ for (i = 0; i < io->num_io_pages; i++)
+ put_io_page(io->pages[i]);
io->num_io_pages = 0;
- iput(io->inode);
+ wq = to_ioend_wq(io->inode);
+ if (atomic_dec_and_test(&EXT4_I(io->inode)->i_ioend_count) &&
+ waitqueue_active(wq))
+ wake_up_all(wq);
kmem_cache_free(io_end_cachep, io);
}
io = kmem_cache_alloc(io_end_cachep, flags);
if (io) {
memset(io, 0, sizeof(*io));
- io->inode = igrab(inode);
- BUG_ON(!io->inode);
+ atomic_inc(&EXT4_I(inode)->i_ioend_count);
+ io->inode = inode;
INIT_WORK(&io->work, ext4_end_io_work);
INIT_LIST_HEAD(&io->list);
}
struct workqueue_struct *wq;
struct inode *inode;
unsigned long flags;
- ext4_fsblk_t err_block;
int i;
BUG_ON(!io_end);
- inode = io_end->inode;
bio->bi_private = NULL;
bio->bi_end_io = NULL;
if (test_bit(BIO_UPTODATE, &bio->bi_flags))
error = 0;
- err_block = bio->bi_sector >> (inode->i_blkbits - 9);
bio_put(bio);
- if (!(inode->i_sb->s_flags & MS_ACTIVE)) {
- pr_err("sb umounted, discard end_io request for inode %lu\n",
- io_end->inode->i_ino);
- ext4_free_io_end(io_end);
- return;
- }
-
- if (error) {
- io_end->flag |= EXT4_IO_END_ERROR;
- ext4_warning(inode->i_sb, "I/O error writing to inode %lu "
- "(offset %llu size %ld starting block %llu)",
- inode->i_ino,
- (unsigned long long) io_end->offset,
- (long) io_end->size,
- (unsigned long long) err_block);
- }
-
for (i = 0; i < io_end->num_io_pages; i++) {
struct page *page = io_end->pages[i]->p_page;
struct buffer_head *bh, *head;
} while (bh != head);
}
- if (--io_end->pages[i]->p_count == 0) {
- struct page *page = io_end->pages[i]->p_page;
-
- end_page_writeback(page);
- put_page(page);
- kmem_cache_free(io_page_cachep, io_end->pages[i]);
- }
+ put_io_page(io_end->pages[i]);
/*
* If this is a partial write which happened to make
if (!partial_write)
SetPageUptodate(page);
}
-
io_end->num_io_pages = 0;
+ inode = io_end->inode;
+
+ if (error) {
+ io_end->flag |= EXT4_IO_END_ERROR;
+ ext4_warning(inode->i_sb, "I/O error writing to inode %lu "
+ "(offset %llu size %ld starting block %llu)",
+ inode->i_ino,
+ (unsigned long long) io_end->offset,
+ (long) io_end->size,
+ (unsigned long long)
+ bio->bi_sector >> (inode->i_blkbits - 9));
+ }
/* Add the io_end to per-inode completed io list*/
spin_lock_irqsave(&EXT4_I(inode)->i_completed_io_lock, flags);
bio->bi_private = io->io_end = io_end;
bio->bi_end_io = ext4_end_bio;
- io_end->inode = inode;
io_end->offset = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh);
io->io_bio = bio;
if ((io_end->num_io_pages == 0) ||
(io_end->pages[io_end->num_io_pages-1] != io_page)) {
io_end->pages[io_end->num_io_pages++] = io_page;
- io_page->p_count++;
+ atomic_inc(&io_page->p_count);
}
return 0;
}
return -ENOMEM;
}
io_page->p_page = page;
- io_page->p_count = 0;
+ atomic_set(&io_page->p_count, 1);
get_page(page);
for (bh = head = page_buffers(page), block_start = 0;
* PageWriteback bit from the page to prevent the system from
* wedging later on.
*/
- if (io_page->p_count == 0) {
- put_page(page);
- end_page_writeback(page);
- kmem_cache_free(io_page_cachep, io_page);
- }
+ put_io_page(io_page);
return ret;
}
ei->cur_aio_dio = NULL;
ei->i_sync_tid = 0;
ei->i_datasync_tid = 0;
+ atomic_set(&ei->i_ioend_count, 0);
return &ei->vfs_inode;
}
+static int ext4_drop_inode(struct inode *inode)
+{
+ int drop = generic_drop_inode(inode);
+
+ trace_ext4_drop_inode(inode, drop);
+ return drop;
+}
+
static void ext4_destroy_inode(struct inode *inode)
{
+ ext4_ioend_wait(inode);
if (!list_empty(&(EXT4_I(inode)->i_orphan))) {
ext4_msg(inode->i_sb, KERN_ERR,
"Inode %lu (%p): orphan list check failed!",
.destroy_inode = ext4_destroy_inode,
.write_inode = ext4_write_inode,
.dirty_inode = ext4_dirty_inode,
+ .drop_inode = ext4_drop_inode,
.evict_inode = ext4_evict_inode,
.put_super = ext4_put_super,
.sync_fs = ext4_sync_fs,
.destroy_inode = ext4_destroy_inode,
.write_inode = ext4_write_inode,
.dirty_inode = ext4_dirty_inode,
+ .drop_inode = ext4_drop_inode,
.evict_inode = ext4_evict_inode,
.write_super = ext4_write_super,
.put_super = ext4_put_super,
struct ext4_li_request *elr;
unsigned long next_wakeup;
DEFINE_WAIT(wait);
- int ret;
BUG_ON(NULL == eli);
elr = list_entry(pos, struct ext4_li_request,
lr_request);
- if (time_after_eq(jiffies, elr->lr_next_sched))
- ret = ext4_run_li_request(elr);
-
- if (ret) {
- ret = 0;
- ext4_remove_li_request(elr);
- continue;
+ if (time_after_eq(jiffies, elr->lr_next_sched)) {
+ if (ext4_run_li_request(elr) != 0) {
+ /* error, remove the lazy_init job */
+ ext4_remove_li_request(elr);
+ continue;
+ }
}
if (time_before(elr->lr_next_sched, next_wakeup))
if (freezing(current))
refrigerator();
- if (time_after_eq(jiffies, next_wakeup)) {
+ if ((time_after_eq(jiffies, next_wakeup)) ||
+ (MAX_JIFFY_OFFSET == next_wakeup)) {
cond_resched();
continue;
}
get_random_bytes(&sbi->s_next_generation, sizeof(u32));
spin_lock_init(&sbi->s_next_gen_lock);
+ err = percpu_counter_init(&sbi->s_freeblocks_counter,
+ ext4_count_free_blocks(sb));
+ if (!err) {
+ err = percpu_counter_init(&sbi->s_freeinodes_counter,
+ ext4_count_free_inodes(sb));
+ }
+ if (!err) {
+ err = percpu_counter_init(&sbi->s_dirs_counter,
+ ext4_count_dirs(sb));
+ }
+ if (!err) {
+ err = percpu_counter_init(&sbi->s_dirtyblocks_counter, 0);
+ }
+ if (err) {
+ ext4_msg(sb, KERN_ERR, "insufficient memory");
+ goto failed_mount3;
+ }
+
sbi->s_stripe = ext4_get_stripe_size(sbi);
sbi->s_max_writeback_mb_bump = 128;
}
set_task_ioprio(sbi->s_journal->j_task, journal_ioprio);
-no_journal:
- err = percpu_counter_init(&sbi->s_freeblocks_counter,
- ext4_count_free_blocks(sb));
- if (!err)
- err = percpu_counter_init(&sbi->s_freeinodes_counter,
- ext4_count_free_inodes(sb));
- if (!err)
- err = percpu_counter_init(&sbi->s_dirs_counter,
- ext4_count_dirs(sb));
- if (!err)
- err = percpu_counter_init(&sbi->s_dirtyblocks_counter, 0);
- if (err) {
- ext4_msg(sb, KERN_ERR, "insufficient memory");
- goto failed_mount_wq;
- }
+ /*
+ * The journal may have updated the bg summary counts, so we
+ * need to update the global counters.
+ */
+ percpu_counter_set(&sbi->s_freeblocks_counter,
+ ext4_count_free_blocks(sb));
+ percpu_counter_set(&sbi->s_freeinodes_counter,
+ ext4_count_free_inodes(sb));
+ percpu_counter_set(&sbi->s_dirs_counter,
+ ext4_count_dirs(sb));
+ percpu_counter_set(&sbi->s_dirtyblocks_counter, 0);
+no_journal:
EXT4_SB(sb)->dio_unwritten_wq = create_workqueue("ext4-dio-unwritten");
if (!EXT4_SB(sb)->dio_unwritten_wq) {
printk(KERN_ERR "EXT4-fs: failed to create DIO workqueue\n");
jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
}
- percpu_counter_destroy(&sbi->s_freeblocks_counter);
- percpu_counter_destroy(&sbi->s_freeinodes_counter);
- percpu_counter_destroy(&sbi->s_dirs_counter);
- percpu_counter_destroy(&sbi->s_dirtyblocks_counter);
failed_mount3:
if (sbi->s_flex_groups) {
if (is_vmalloc_addr(sbi->s_flex_groups))
else
kfree(sbi->s_flex_groups);
}
+ percpu_counter_destroy(&sbi->s_freeblocks_counter);
+ percpu_counter_destroy(&sbi->s_freeinodes_counter);
+ percpu_counter_destroy(&sbi->s_dirs_counter);
+ percpu_counter_destroy(&sbi->s_dirtyblocks_counter);
failed_mount2:
for (i = 0; i < db_count; i++)
brelse(sbi->s_group_desc[i]);
else
es->s_kbytes_written =
cpu_to_le64(EXT4_SB(sb)->s_kbytes_written);
- if (percpu_counter_initialized(&EXT4_SB(sb)->s_freeblocks_counter))
- ext4_free_blocks_count_set(es, percpu_counter_sum_positive(
- &EXT4_SB(sb)->s_freeblocks_counter));
- if (percpu_counter_initialized(&EXT4_SB(sb)->s_freeinodes_counter))
- es->s_free_inodes_count =
- cpu_to_le32(percpu_counter_sum_positive(
- &EXT4_SB(sb)->s_freeinodes_counter));
+ ext4_free_blocks_count_set(es, percpu_counter_sum_positive(
+ &EXT4_SB(sb)->s_freeblocks_counter));
+ es->s_free_inodes_count =
+ cpu_to_le32(percpu_counter_sum_positive(
+ &EXT4_SB(sb)->s_freeinodes_counter));
sb->s_dirt = 0;
BUFFER_TRACE(sbh, "marking dirty");
mark_buffer_dirty(sbh);
static int ext4_quota_off(struct super_block *sb, int type)
{
- /* Force all delayed allocation blocks to be allocated */
- if (test_opt(sb, DELALLOC)) {
- down_read(&sb->s_umount);
+ /* Force all delayed allocation blocks to be allocated.
+ * Caller already holds s_umount sem */
+ if (test_opt(sb, DELALLOC))
sync_filesystem(sb);
- up_read(&sb->s_umount);
- }
return dquot_quota_off(sb, type);
}
if (creat_flags == HUGETLB_SHMFS_INODE && !can_do_hugetlb_shm()) {
*user = current_user();
if (user_shm_lock(size, *user)) {
- WARN_ONCE(1,
- "Using mlock ulimits for SHM_HUGETLB deprecated\n");
+ printk_once(KERN_WARNING "Using mlock ulimits for SHM_HUGETLB is deprecated\n");
} else {
*user = NULL;
return ERR_PTR(-EPERM);
static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg)
{
- struct file_lock *fl;
+ struct file_lock *fl, *ret;
struct fasync_struct *new;
- struct inode *inode = filp->f_path.dentry->d_inode;
int error;
fl = lease_alloc(filp, arg);
locks_free_lock(fl);
return -ENOMEM;
}
+ ret = fl;
lock_flocks();
- error = __vfs_setlease(filp, arg, &fl);
+ error = __vfs_setlease(filp, arg, &ret);
if (error) {
unlock_flocks();
locks_free_lock(fl);
goto out_free_fasync;
}
+ if (ret != fl)
+ locks_free_lock(fl);
/*
* fasync_insert_entry() returns the old entry if any.
* inserted it into the fasync list. Clear new so that
* we don't release it here.
*/
- if (!fasync_insert_entry(fd, filp, &fl->fl_fasync, new))
+ if (!fasync_insert_entry(fd, filp, &ret->fl_fasync, new))
new = NULL;
- if (error < 0) {
- /* remove lease just inserted by setlease */
- fl->fl_type = F_UNLCK | F_INPROGRESS;
- fl->fl_break_time = jiffies - 10;
- time_out_leases(inode);
- } else {
- error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
- }
+ error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0);
unlock_flocks();
out_free_fasync:
spin_unlock(&clp->cl_lock);
}
-static void nfsd4_register_conn(struct nfsd4_conn *conn)
+static int nfsd4_register_conn(struct nfsd4_conn *conn)
{
conn->cn_xpt_user.callback = nfsd4_conn_lost;
- register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
+ return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
}
static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
{
struct nfsd4_conn *conn;
u32 flags = NFS4_CDFC4_FORE;
+ int ret;
if (ses->se_flags & SESSION4_BACK_CHAN)
flags |= NFS4_CDFC4_BACK;
if (!conn)
return nfserr_jukebox;
nfsd4_hash_conn(conn, ses);
- nfsd4_register_conn(conn);
+ ret = nfsd4_register_conn(conn);
+ if (ret)
+ /* oops; xprt is already down: */
+ nfsd4_conn_lost(&conn->cn_xpt_user);
return nfs_ok;
}
{
struct nfs4_client *clp = ses->se_client;
struct nfsd4_conn *c;
+ int ret;
spin_lock(&clp->cl_lock);
c = __nfsd4_find_conn(new->cn_xprt, ses);
}
__nfsd4_hash_conn(new, ses);
spin_unlock(&clp->cl_lock);
- nfsd4_register_conn(new);
+ ret = nfsd4_register_conn(new);
+ if (ret)
+ /* oops; xprt is already down: */
+ nfsd4_conn_lost(&new->cn_xpt_user);
return;
}
static struct dentry *openprom_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
- return mount_single(fs_type, flags, data, openprom_fill_super)
+ return mount_single(fs_type, flags, data, openprom_fill_super);
}
static struct file_system_type openprom_fs_type = {
uptodate = 0;
/*
- * A hole may still be marked uptodate because discard_buffer
- * leaves the flag set.
+ * set_page_dirty dirties all buffers in a page, independent
+ * of their state. The dirty state however is entirely
+ * meaningless for holes (!mapped && uptodate), so skip
+ * buffers covering holes here.
*/
if (!buffer_mapped(bh) && buffer_uptodate(bh)) {
- ASSERT(!buffer_dirty(bh));
imap_valid = 0;
continue;
}
INIT_LIST_HEAD(list);
spin_lock(dwlk);
list_for_each_entry_safe(bp, n, dwq, b_list) {
- trace_xfs_buf_delwri_split(bp, _RET_IP_);
ASSERT(bp->b_flags & XBF_DELWRI);
if (!XFS_BUF_ISPINNED(bp) && !xfs_buf_cond_lock(bp)) {
_XBF_RUN_QUEUES);
bp->b_flags |= XBF_WRITE;
list_move_tail(&bp->b_list, list);
+ trace_xfs_buf_delwri_split(bp, _RET_IP_);
} else
skipped++;
}
if (IS_ERR(dentry))
return PTR_ERR(dentry);
- kbuf = kmalloc(al_hreq.buflen, GFP_KERNEL);
+ kbuf = kzalloc(al_hreq.buflen, GFP_KERNEL);
if (!kbuf)
goto out_dput;
inode->i_state = I_NEW;
inode_sb_list_add(inode);
- insert_inode_hash(inode);
+ /* make the inode look hashed for the writeback code */
+ hlist_add_fake(&inode->i_hash);
inode->i_mode = ip->i_d.di_mode;
inode->i_nlink = ip->i_d.di_nlink;
mp->m_qflags &= ~XFS_OQUOTA_ENFD;
} else if (!strcmp(this_char, MNTOPT_DELAYLOG)) {
mp->m_flags |= XFS_MOUNT_DELAYLOG;
- cmn_err(CE_WARN,
- "Enabling EXPERIMENTAL delayed logging feature "
- "- use at your own risk.\n");
} else if (!strcmp(this_char, MNTOPT_NODELAYLOG)) {
mp->m_flags &= ~XFS_MOUNT_DELAYLOG;
} else if (!strcmp(this_char, "ihashsize")) {
if (trylock) {
if (!mutex_trylock(&pag->pag_ici_reclaim_lock)) {
skipped++;
+ xfs_perag_put(pag);
continue;
}
first_index = pag->pag_ici_reclaim_cursor;
* If the file's parent directory is known, take its iolock in exclusive
* mode to prevent two sibling files from racing each other to migrate
* themselves and their parent to different AGs.
+ *
+ * Note that we lock the parent directory iolock inside the child
+ * iolock here. That's fine as we never hold both parent and child
+ * iolock in any other place. This is different from the ilock,
+ * which requires locking of the child after the parent for namespace
+ * operations.
*/
if (pip)
- xfs_ilock(pip, XFS_IOLOCK_EXCL);
+ xfs_ilock(pip, XFS_IOLOCK_EXCL | XFS_IOLOCK_PARENT);
/*
* A new AG needs to be found for the file. If the file's parent
pag = radix_tree_delete(&mp->m_perag_tree, agno);
spin_unlock(&mp->m_perag_lock);
ASSERT(pag);
+ ASSERT(atomic_read(&pag->pag_ref) == 0);
call_rcu(&pag->rcu_head, __xfs_free_perag);
}
}
#define xfs_trans_mod_dquot_byino(tp, ip, fields, delta)
#define xfs_trans_apply_dquot_deltas(tp)
#define xfs_trans_unreserve_and_mod_dquots(tp)
-#define xfs_trans_reserve_quota_nblks(tp, ip, nblks, ninos, flags) (0)
-#define xfs_trans_reserve_quota_bydquots(tp, mp, u, g, nb, ni, fl) (0)
+static inline int xfs_trans_reserve_quota_nblks(struct xfs_trans *tp,
+ struct xfs_inode *ip, long nblks, long ninos, uint flags)
+{
+ return 0;
+}
+static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp,
+ struct xfs_mount *mp, struct xfs_dquot *udqp,
+ struct xfs_dquot *gdqp, long nblks, long nions, uint flags)
+{
+ return 0;
+}
#define xfs_qm_vop_create_dqattach(tp, ip, u, g)
#define xfs_qm_vop_rename_dqattach(it) (0)
#define xfs_qm_vop_chown(tp, ip, old, new) (NULL)
#define xfs_qm_dqdetach(ip)
#define xfs_qm_dqrele(d)
#define xfs_qm_statvfs(ip, s)
-#define xfs_qm_sync(mp, fl) (0)
+static inline int xfs_qm_sync(struct xfs_mount *mp, int flags)
+{
+ return 0;
+}
#define xfs_qm_newmount(mp, a, b) (0)
#define xfs_qm_mount_quotas(mp)
#define xfs_qm_unmount(mp)
-#define xfs_qm_unmount_quotas(mp) (0)
+#define xfs_qm_unmount_quotas(mp)
#endif /* CONFIG_XFS_QUOTA */
#define xfs_trans_unreserve_quota_nblks(tp, ip, nblks, ninos, flags) \
* together with the @destroy function,
* enables driver-specific objects derived from a ttm_buffer_object.
* On successful return, the object kref and list_kref are set to 1.
+ * If a failure occurs, the function will call the @destroy function, or
+ * kfree() if @destroy is NULL. Thus, after a failure, dereferencing @bo is
+ * illegal and will likely cause memory corruption.
+ *
* Returns
* -ENOMEM: Out of memory.
* -EINVAL: Invalid placement flags.
struct ttm_mem_type_manager;
struct ttm_mem_type_manager_func {
+ /**
+ * struct ttm_mem_type_manager member init
+ *
+ * @man: Pointer to a memory type manager.
+ * @p_size: Implementation dependent, but typically the size of the
+ * range to be managed in pages.
+ *
+ * Called to initialize a private range manager. The function is
+ * expected to initialize the man::priv member.
+ * Returns 0 on success, negative error code on failure.
+ */
int (*init)(struct ttm_mem_type_manager *man, unsigned long p_size);
+
+ /**
+ * struct ttm_mem_type_manager member takedown
+ *
+ * @man: Pointer to a memory type manager.
+ *
+ * Called to undo the setup done in init. All allocated resources
+ * should be freed.
+ */
int (*takedown)(struct ttm_mem_type_manager *man);
+
+ /**
+ * struct ttm_mem_type_manager member get_node
+ *
+ * @man: Pointer to a memory type manager.
+ * @bo: Pointer to the buffer object we're allocating space for.
+ * @placement: Placement details.
+ * @mem: Pointer to a struct ttm_mem_reg to be filled in.
+ *
+ * This function should allocate space in the memory type managed
+ * by @man. Placement details if
+ * applicable are given by @placement. If successful,
+ * @mem::mm_node should be set to a non-null value, and
+ * @mem::start should be set to a value identifying the beginning
+ * of the range allocated, and the function should return zero.
+ * If the memory region accomodate the buffer object, @mem::mm_node
+ * should be set to NULL, and the function should return 0.
+ * If a system error occured, preventing the request to be fulfilled,
+ * the function should return a negative error code.
+ *
+ * Note that @mem::mm_node will only be dereferenced by
+ * struct ttm_mem_type_manager functions and optionally by the driver,
+ * which has knowledge of the underlying type.
+ *
+ * This function may not be called from within atomic context, so
+ * an implementation can and must use either a mutex or a spinlock to
+ * protect any data structures managing the space.
+ */
int (*get_node)(struct ttm_mem_type_manager *man,
struct ttm_buffer_object *bo,
struct ttm_placement *placement,
struct ttm_mem_reg *mem);
+
+ /**
+ * struct ttm_mem_type_manager member put_node
+ *
+ * @man: Pointer to a memory type manager.
+ * @mem: Pointer to a struct ttm_mem_reg to be filled in.
+ *
+ * This function frees memory type resources previously allocated
+ * and that are identified by @mem::mm_node and @mem::start. May not
+ * be called from within atomic context.
+ */
void (*put_node)(struct ttm_mem_type_manager *man,
struct ttm_mem_reg *mem);
+
+ /**
+ * struct ttm_mem_type_manager member debug
+ *
+ * @man: Pointer to a memory type manager.
+ * @prefix: Prefix to be used in printout to identify the caller.
+ *
+ * This function is called to print out the state of the memory
+ * type manager to aid debugging of out-of-memory conditions.
+ * It may not be called from within atomic context.
+ */
void (*debug)(struct ttm_mem_type_manager *man, const char *prefix);
};
uint64_t size;
uint32_t available_caching;
uint32_t default_caching;
+ const struct ttm_mem_type_manager_func *func;
+ void *priv;
/*
- * Protected by the bdev->lru_lock.
- * TODO: Consider one lru_lock per ttm_mem_type_manager.
- * Plays ill with list removal, though.
+ * Protected by the global->lru_lock.
*/
- const struct ttm_mem_type_manager_func *func;
- void *priv;
+
struct list_head lru;
};
--- /dev/null
+#ifndef _LINUX_ATOMIC_H
+#define _LINUX_ATOMIC_H
+#include <asm/atomic.h>
+
+/**
+ * atomic_inc_not_zero_hint - increment if not null
+ * @v: pointer of type atomic_t
+ * @hint: probable value of the atomic before the increment
+ *
+ * This version of atomic_inc_not_zero() gives a hint of probable
+ * value of the atomic. This helps processor to not read the memory
+ * before doing the atomic read/modify/write cycle, lowering
+ * number of bus transactions on some arches.
+ *
+ * Returns: 0 if increment was not done, 1 otherwise.
+ */
+#ifndef atomic_inc_not_zero_hint
+static inline int atomic_inc_not_zero_hint(atomic_t *v, int hint)
+{
+ int val, c = hint;
+
+ /* sanity test, should be removed by compiler if hint is a constant */
+ if (!hint)
+ return atomic_inc_not_zero(v);
+
+ do {
+ val = atomic_cmpxchg(v, c, c + 1);
+ if (val == c)
+ return 1;
+ c = val;
+ } while (c);
+
+ return 0;
+}
+#endif
+
+#endif /* _LINUX_ATOMIC_H */
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
+#include <linux/hardirq.h>
#include <asm/cacheflush.h>
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define roundup(x, y) ( \
{ \
- typeof(y) __y = y; \
+ const typeof(y) __y = y; \
(((x) + (__y - 1)) / __y) * __y; \
} \
)
unsigned int interval_msec);
extern int printk_delay_msec;
+extern int dmesg_restrict;
/*
* Print a one-time message (analogous to WARN_ONCE() et al):
--- /dev/null
+/*
+ * LP5521 LED chip driver.
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __LINUX_LP5521_H
+#define __LINUX_LP5521_H
+
+/* See Documentation/leds/leds-lp5521.txt */
+
+struct lp5521_led_config {
+ u8 chan_nr;
+ u8 led_current; /* mA x10, 0 if led is not connected */
+ u8 max_current;
+};
+
+#define LP5521_CLOCK_AUTO 0
+#define LP5521_CLOCK_INT 1
+#define LP5521_CLOCK_EXT 2
+
+struct lp5521_platform_data {
+ struct lp5521_led_config *led_config;
+ u8 num_channels;
+ u8 clock_mode;
+ int (*setup_resources)(void);
+ void (*release_resources)(void);
+ void (*enable)(bool state);
+};
+
+#endif /* __LINUX_LP5521_H */
--- /dev/null
+/*
+ * LP5523 LED Driver
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Samu Onkalo <samu.p.onkalo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef __LINUX_LP5523_H
+#define __LINUX_LP5523_H
+
+/* See Documentation/leds/leds-lp5523.txt */
+
+struct lp5523_led_config {
+ u8 chan_nr;
+ u8 led_current; /* mA x10, 0 if led is not connected */
+ u8 max_current;
+};
+
+#define LP5523_CLOCK_AUTO 0
+#define LP5523_CLOCK_INT 1
+#define LP5523_CLOCK_EXT 2
+
+struct lp5523_platform_data {
+ struct lp5523_led_config *led_config;
+ u8 num_channels;
+ u8 clock_mode;
+ int (*setup_resources)(void);
+ void (*release_resources)(void);
+ void (*enable)(bool state);
+};
+
+#endif /* __LINUX_LP5523_H */
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rwsem.h>
+#include <linux/timer.h>
struct device;
/*
/* Get LED brightness level */
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
- /* Activate hardware accelerated blink, delays are in
- * miliseconds and if none is provided then a sensible default
- * should be chosen. The call can adjust the timings if it can't
- * match the values specified exactly. */
+ /*
+ * Activate hardware accelerated blink, delays are in milliseconds
+ * and if both are zero then a sensible default should be chosen.
+ * The call should adjust the timings in that case and if it can't
+ * match the values specified exactly.
+ * Deactivate blinking again when the brightness is set to a fixed
+ * value via the brightness_set() callback.
+ */
int (*blink_set)(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off);
struct list_head node; /* LED Device list */
const char *default_trigger; /* Trigger to use */
+ unsigned long blink_delay_on, blink_delay_off;
+ struct timer_list blink_timer;
+ int blink_brightness;
+
#ifdef CONFIG_LEDS_TRIGGERS
/* Protects the trigger data below */
struct rw_semaphore trigger_lock;
extern void led_classdev_suspend(struct led_classdev *led_cdev);
extern void led_classdev_resume(struct led_classdev *led_cdev);
+/**
+ * led_blink_set - set blinking with software fallback
+ * @led_cdev: the LED to start blinking
+ * @delay_on: the time it should be on (in ms)
+ * @delay_off: the time it should ble off (in ms)
+ *
+ * This function makes the LED blink, attempting to use the
+ * hardware acceleration if possible, but falling back to
+ * software blinking if there is no hardware blinking or if
+ * the LED refuses the passed values.
+ *
+ * Note that if software blinking is active, simply calling
+ * led_cdev->brightness_set() will not stop the blinking,
+ * use led_classdev_brightness_set() instead.
+ */
+extern void led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off);
+/**
+ * led_brightness_set - set LED brightness
+ * @led_cdev: the LED to set
+ * @brightness: the brightness to set it to
+ *
+ * Set an LED's brightness, and, if necessary, cancel the
+ * software blink timer that implements blinking when the
+ * hardware doesn't.
+ */
+extern void led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness);
+
/*
* LED Triggers
*/
#define MMCIF_CE_HOST_STS2 0x0000004C
#define MMCIF_CE_VERSION 0x0000007C
-extern inline u32 sh_mmcif_readl(void __iomem *addr, int reg)
+static inline u32 sh_mmcif_readl(void __iomem *addr, int reg)
{
return readl(addr + reg);
}
-extern inline void sh_mmcif_writel(void __iomem *addr, int reg, u32 val)
+static inline void sh_mmcif_writel(void __iomem *addr, int reg, u32 val)
{
writel(val, addr + reg);
}
#define SH_MMCIF_BBS 512 /* boot block size */
-extern inline void sh_mmcif_boot_cmd_send(void __iomem *base,
+static inline void sh_mmcif_boot_cmd_send(void __iomem *base,
unsigned long cmd, unsigned long arg)
{
sh_mmcif_writel(base, MMCIF_CE_INT, 0);
sh_mmcif_writel(base, MMCIF_CE_CMD_SET, cmd);
}
-extern inline int sh_mmcif_boot_cmd_poll(void __iomem *base, unsigned long mask)
+static inline int sh_mmcif_boot_cmd_poll(void __iomem *base, unsigned long mask)
{
unsigned long tmp;
int cnt;
return -1;
}
-extern inline int sh_mmcif_boot_cmd(void __iomem *base,
+static inline int sh_mmcif_boot_cmd(void __iomem *base,
unsigned long cmd, unsigned long arg)
{
sh_mmcif_boot_cmd_send(base, cmd, arg);
return sh_mmcif_boot_cmd_poll(base, 0x00010000);
}
-extern inline int sh_mmcif_boot_do_read_single(void __iomem *base,
+static inline int sh_mmcif_boot_do_read_single(void __iomem *base,
unsigned int block_nr,
unsigned long *buf)
{
return 0;
}
-extern inline int sh_mmcif_boot_do_read(void __iomem *base,
+static inline int sh_mmcif_boot_do_read(void __iomem *base,
unsigned long first_block,
unsigned long nr_blocks,
void *buf)
return ret;
}
-extern inline void sh_mmcif_boot_init(void __iomem *base)
+static inline void sh_mmcif_boot_init(void __iomem *base)
{
unsigned long tmp;
sh_mmcif_boot_cmd(base, 0x03400040, 0x00010000);
}
-extern inline void sh_mmcif_boot_slurp(void __iomem *base,
+static inline void sh_mmcif_boot_slurp(void __iomem *base,
unsigned char *buf,
unsigned long no_bytes)
{
u64 tstamp_running;
u64 tstamp_stopped;
+ /*
+ * timestamp shadows the actual context timing but it can
+ * be safely used in NMI interrupt context. It reflects the
+ * context time as it was when the event was last scheduled in.
+ *
+ * ctx_time already accounts for ctx->timestamp. Therefore to
+ * compute ctx_time for a sample, simply add perf_clock().
+ */
+ u64 shadow_ctx_time;
+
struct perf_event_attr attr;
struct hw_perf_event hw;
int pwm_id;
unsigned int max_brightness;
unsigned int dft_brightness;
+ unsigned int lth_brightness;
unsigned int pwm_period_ns;
int (*init)(struct device *dev);
int (*notify)(struct device *dev, int brightness);
* needed for RCU lookups (because root->height is unreliable). The only
* time callers need worry about this is when doing a lookup_slot under
* RCU.
+ *
+ * Indirect pointer in fact is also used to tag the last pointer of a node
+ * when it is shrunk, before we rcu free the node. See shrink code for
+ * details.
*/
#define RADIX_TREE_INDIRECT_PTR 1
-#define RADIX_TREE_RETRY ((void *)-1UL)
-
-static inline void *radix_tree_ptr_to_indirect(void *ptr)
-{
- return (void *)((unsigned long)ptr | RADIX_TREE_INDIRECT_PTR);
-}
-static inline void *radix_tree_indirect_to_ptr(void *ptr)
-{
- return (void *)((unsigned long)ptr & ~RADIX_TREE_INDIRECT_PTR);
-}
#define radix_tree_indirect_to_ptr(ptr) \
radix_tree_indirect_to_ptr((void __force *)(ptr))
* removed.
*
* For use with radix_tree_lookup_slot(). Caller must hold tree at least read
- * locked across slot lookup and dereference. More likely, will be used with
- * radix_tree_replace_slot(), as well, so caller will hold tree write locked.
+ * locked across slot lookup and dereference. Not required if write lock is
+ * held (ie. items cannot be concurrently inserted).
+ *
+ * radix_tree_deref_retry must be used to confirm validity of the pointer if
+ * only the read lock is held.
*/
static inline void *radix_tree_deref_slot(void **pslot)
{
- void *ret = rcu_dereference(*pslot);
- if (unlikely(radix_tree_is_indirect_ptr(ret)))
- ret = RADIX_TREE_RETRY;
- return ret;
+ return rcu_dereference(*pslot);
}
+
+/**
+ * radix_tree_deref_retry - check radix_tree_deref_slot
+ * @arg: pointer returned by radix_tree_deref_slot
+ * Returns: 0 if retry is not required, otherwise retry is required
+ *
+ * radix_tree_deref_retry must be used with radix_tree_deref_slot.
+ */
+static inline int radix_tree_deref_retry(void *arg)
+{
+ return unlikely((unsigned long)arg & RADIX_TREE_INDIRECT_PTR);
+}
+
/**
* radix_tree_replace_slot - replace item in a slot
* @pslot: pointer to slot, returned by radix_tree_lookup_slot
#define _LINUX_RESOURCE_H
#include <linux/time.h>
+#include <linux/types.h>
/*
* Resource control/accounting header file for linux
long clk_rate_div_range_round(struct clk *clk, unsigned int div_min,
unsigned int div_max, unsigned long rate);
+long clk_round_parent(struct clk *clk, unsigned long target,
+ unsigned long *best_freq, unsigned long *parent_freq,
+ unsigned int div_min, unsigned int div_max);
+
#define SH_CLK_MSTP32(_parent, _enable_reg, _enable_bit, _flags) \
{ \
.parent = _parent, \
char *name;
long channel_offset;
int timer_bit;
- char *clk;
unsigned long clockevent_rating;
unsigned long clocksource_rating;
};
struct net *xpt_net;
};
-static inline void register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
+static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
{
spin_lock(&xpt->xpt_lock);
- list_add(&u->list, &xpt->xpt_users);
+ list_del_init(&u->list);
spin_unlock(&xpt->xpt_lock);
}
-static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
+static inline int register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
{
spin_lock(&xpt->xpt_lock);
- list_del_init(&u->list);
+ if (test_bit(XPT_CLOSE, &xpt->xpt_flags)) {
+ /*
+ * The connection is about to be deleted soon (or,
+ * worse, may already be deleted--in which case we've
+ * already notified the xpt_users).
+ */
+ spin_unlock(&xpt->xpt_lock);
+ return -ENOTCONN;
+ }
+ list_add(&u->list, &xpt->xpt_users);
spin_unlock(&xpt->xpt_lock);
+ return 0;
}
int svc_reg_xprt_class(struct svc_xprt_class *);
(unsigned long) __entry->dir, __entry->mode)
);
+TRACE_EVENT(ext4_evict_inode,
+ TP_PROTO(struct inode *inode),
+
+ TP_ARGS(inode),
+
+ TP_STRUCT__entry(
+ __field( int, dev_major )
+ __field( int, dev_minor )
+ __field( ino_t, ino )
+ __field( int, nlink )
+ ),
+
+ TP_fast_assign(
+ __entry->dev_major = MAJOR(inode->i_sb->s_dev);
+ __entry->dev_minor = MINOR(inode->i_sb->s_dev);
+ __entry->ino = inode->i_ino;
+ __entry->nlink = inode->i_nlink;
+ ),
+
+ TP_printk("dev %d,%d ino %lu nlink %d",
+ __entry->dev_major, __entry->dev_minor,
+ (unsigned long) __entry->ino, __entry->nlink)
+);
+
+TRACE_EVENT(ext4_drop_inode,
+ TP_PROTO(struct inode *inode, int drop),
+
+ TP_ARGS(inode, drop),
+
+ TP_STRUCT__entry(
+ __field( int, dev_major )
+ __field( int, dev_minor )
+ __field( ino_t, ino )
+ __field( int, drop )
+ ),
+
+ TP_fast_assign(
+ __entry->dev_major = MAJOR(inode->i_sb->s_dev);
+ __entry->dev_minor = MINOR(inode->i_sb->s_dev);
+ __entry->ino = inode->i_ino;
+ __entry->drop = drop;
+ ),
+
+ TP_printk("dev %d,%d ino %lu drop %d",
+ __entry->dev_major, __entry->dev_minor,
+ (unsigned long) __entry->ino, __entry->drop)
+);
+
+TRACE_EVENT(ext4_mark_inode_dirty,
+ TP_PROTO(struct inode *inode, unsigned long IP),
+
+ TP_ARGS(inode, IP),
+
+ TP_STRUCT__entry(
+ __field( int, dev_major )
+ __field( int, dev_minor )
+ __field( ino_t, ino )
+ __field(unsigned long, ip )
+ ),
+
+ TP_fast_assign(
+ __entry->dev_major = MAJOR(inode->i_sb->s_dev);
+ __entry->dev_minor = MINOR(inode->i_sb->s_dev);
+ __entry->ino = inode->i_ino;
+ __entry->ip = IP;
+ ),
+
+ TP_printk("dev %d,%d ino %lu caller %pF",
+ __entry->dev_major, __entry->dev_minor,
+ (unsigned long) __entry->ino, (void *)__entry->ip)
+);
+
+TRACE_EVENT(ext4_begin_ordered_truncate,
+ TP_PROTO(struct inode *inode, loff_t new_size),
+
+ TP_ARGS(inode, new_size),
+
+ TP_STRUCT__entry(
+ __field( int, dev_major )
+ __field( int, dev_minor )
+ __field( ino_t, ino )
+ __field( loff_t, new_size )
+ ),
+
+ TP_fast_assign(
+ __entry->dev_major = MAJOR(inode->i_sb->s_dev);
+ __entry->dev_minor = MINOR(inode->i_sb->s_dev);
+ __entry->ino = inode->i_ino;
+ __entry->new_size = new_size;
+ ),
+
+ TP_printk("dev %d,%d ino %lu new_size %lld",
+ __entry->dev_major, __entry->dev_minor,
+ (unsigned long) __entry->ino,
+ (long long) __entry->new_size)
+);
+
DECLARE_EVENT_CLASS(ext4__write_begin,
TP_PROTO(struct inode *inode, loff_t pos, unsigned int len,
account_global_scheduler_latency(tsk, &lat);
- /*
- * short term hack; if we're > 32 we stop; future we recycle:
- */
- tsk->latency_record_count++;
- if (tsk->latency_record_count >= LT_SAVECOUNT)
- goto out_unlock;
-
- for (i = 0; i < LT_SAVECOUNT; i++) {
+ for (i = 0; i < tsk->latency_record_count; i++) {
struct latency_record *mylat;
int same = 1;
}
}
+ /*
+ * short term hack; if we're > 32 we stop; future we recycle:
+ */
+ if (tsk->latency_record_count >= LT_SAVECOUNT)
+ goto out_unlock;
+
/* Allocated a new one: */
- i = tsk->latency_record_count;
+ i = tsk->latency_record_count++;
memcpy(&tsk->latency_record[i], &lat, sizeof(struct latency_record));
out_unlock:
event->tstamp_running += ctx->time - event->tstamp_stopped;
+ event->shadow_ctx_time = ctx->time - ctx->timestamp;
+
if (!is_software_event(event))
cpuctx->active_oncpu++;
ctx->nr_active++;
}
static void perf_output_read_one(struct perf_output_handle *handle,
- struct perf_event *event)
+ struct perf_event *event,
+ u64 enabled, u64 running)
{
u64 read_format = event->attr.read_format;
u64 values[4];
values[n++] = perf_event_count(event);
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
- values[n++] = event->total_time_enabled +
+ values[n++] = enabled +
atomic64_read(&event->child_total_time_enabled);
}
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
- values[n++] = event->total_time_running +
+ values[n++] = running +
atomic64_read(&event->child_total_time_running);
}
if (read_format & PERF_FORMAT_ID)
* XXX PERF_FORMAT_GROUP vs inherited events seems difficult.
*/
static void perf_output_read_group(struct perf_output_handle *handle,
- struct perf_event *event)
+ struct perf_event *event,
+ u64 enabled, u64 running)
{
struct perf_event *leader = event->group_leader, *sub;
u64 read_format = event->attr.read_format;
values[n++] = 1 + leader->nr_siblings;
if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
- values[n++] = leader->total_time_enabled;
+ values[n++] = enabled;
if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
- values[n++] = leader->total_time_running;
+ values[n++] = running;
if (leader != event)
leader->pmu->read(leader);
}
}
+#define PERF_FORMAT_TOTAL_TIMES (PERF_FORMAT_TOTAL_TIME_ENABLED|\
+ PERF_FORMAT_TOTAL_TIME_RUNNING)
+
static void perf_output_read(struct perf_output_handle *handle,
struct perf_event *event)
{
+ u64 enabled = 0, running = 0, now, ctx_time;
+ u64 read_format = event->attr.read_format;
+
+ /*
+ * compute total_time_enabled, total_time_running
+ * based on snapshot values taken when the event
+ * was last scheduled in.
+ *
+ * we cannot simply called update_context_time()
+ * because of locking issue as we are called in
+ * NMI context
+ */
+ if (read_format & PERF_FORMAT_TOTAL_TIMES) {
+ now = perf_clock();
+ ctx_time = event->shadow_ctx_time + now;
+ enabled = ctx_time - event->tstamp_enabled;
+ running = ctx_time - event->tstamp_running;
+ }
+
if (event->attr.read_format & PERF_FORMAT_GROUP)
- perf_output_read_group(handle, event);
+ perf_output_read_group(handle, event, enabled, running);
else
- perf_output_read_one(handle, event);
+ perf_output_read_one(handle, event, enabled, running);
}
void perf_output_sample(struct perf_output_handle *handle,
}
#endif
+#ifdef CONFIG_SECURITY_DMESG_RESTRICT
+int dmesg_restrict = 1;
+#else
+int dmesg_restrict;
+#endif
+
int do_syslog(int type, char __user *buf, int len, bool from_file)
{
unsigned i, j, limit, count;
int clean_sort_range(struct range *range, int az)
{
- int i, j, k = az - 1, nr_range = 0;
+ int i, j, k = az - 1, nr_range = az;
for (i = 0; i < k; i++) {
if (range[i].end)
.extra2 = &ten_thousand,
},
#endif
+ {
+ .procname = "dmesg_restrict",
+ .data = &dmesg_restrict,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
{
.procname = "ngroups_max",
.data = &ngroups_max,
};
static DEFINE_PER_CPU(struct radix_tree_preload, radix_tree_preloads) = { 0, };
+static inline void *ptr_to_indirect(void *ptr)
+{
+ return (void *)((unsigned long)ptr | RADIX_TREE_INDIRECT_PTR);
+}
+
+static inline void *indirect_to_ptr(void *ptr)
+{
+ return (void *)((unsigned long)ptr & ~RADIX_TREE_INDIRECT_PTR);
+}
+
static inline gfp_t root_gfp_mask(struct radix_tree_root *root)
{
return root->gfp_mask & __GFP_BITS_MASK;
return -ENOMEM;
/* Increase the height. */
- node->slots[0] = radix_tree_indirect_to_ptr(root->rnode);
+ node->slots[0] = indirect_to_ptr(root->rnode);
/* Propagate the aggregated tag info into the new root */
for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) {
newheight = root->height+1;
node->height = newheight;
node->count = 1;
- node = radix_tree_ptr_to_indirect(node);
+ node = ptr_to_indirect(node);
rcu_assign_pointer(root->rnode, node);
root->height = newheight;
} while (height > root->height);
return error;
}
- slot = radix_tree_indirect_to_ptr(root->rnode);
+ slot = indirect_to_ptr(root->rnode);
height = root->height;
shift = (height-1) * RADIX_TREE_MAP_SHIFT;
rcu_assign_pointer(node->slots[offset], slot);
node->count++;
} else
- rcu_assign_pointer(root->rnode,
- radix_tree_ptr_to_indirect(slot));
+ rcu_assign_pointer(root->rnode, ptr_to_indirect(slot));
}
/* Go a level down */
return NULL;
return is_slot ? (void *)&root->rnode : node;
}
- node = radix_tree_indirect_to_ptr(node);
+ node = indirect_to_ptr(node);
height = node->height;
if (index > radix_tree_maxindex(height))
height--;
} while (height > 0);
- return is_slot ? (void *)slot:node;
+ return is_slot ? (void *)slot : indirect_to_ptr(node);
}
/**
height = root->height;
BUG_ON(index > radix_tree_maxindex(height));
- slot = radix_tree_indirect_to_ptr(root->rnode);
+ slot = indirect_to_ptr(root->rnode);
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
while (height > 0) {
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
pathp->node = NULL;
- slot = radix_tree_indirect_to_ptr(root->rnode);
+ slot = indirect_to_ptr(root->rnode);
while (height > 0) {
int offset;
if (!radix_tree_is_indirect_ptr(node))
return (index == 0);
- node = radix_tree_indirect_to_ptr(node);
+ node = indirect_to_ptr(node);
height = node->height;
if (index > radix_tree_maxindex(height))
}
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
- slot = radix_tree_indirect_to_ptr(root->rnode);
+ slot = indirect_to_ptr(root->rnode);
/*
* we fill the path from (root->height - 2) to 0, leaving the index at
results[0] = node;
return 1;
}
- node = radix_tree_indirect_to_ptr(node);
+ node = indirect_to_ptr(node);
max_index = radix_tree_maxindex(node->height);
slot = *(((void ***)results)[ret + i]);
if (!slot)
continue;
- results[ret + nr_found] = rcu_dereference_raw(slot);
+ results[ret + nr_found] =
+ indirect_to_ptr(rcu_dereference_raw(slot));
nr_found++;
}
ret += nr_found;
results[0] = (void **)&root->rnode;
return 1;
}
- node = radix_tree_indirect_to_ptr(node);
+ node = indirect_to_ptr(node);
max_index = radix_tree_maxindex(node->height);
results[0] = node;
return 1;
}
- node = radix_tree_indirect_to_ptr(node);
+ node = indirect_to_ptr(node);
max_index = radix_tree_maxindex(node->height);
slot = *(((void ***)results)[ret + i]);
if (!slot)
continue;
- results[ret + nr_found] = rcu_dereference_raw(slot);
+ results[ret + nr_found] =
+ indirect_to_ptr(rcu_dereference_raw(slot));
nr_found++;
}
ret += nr_found;
results[0] = (void **)&root->rnode;
return 1;
}
- node = radix_tree_indirect_to_ptr(node);
+ node = indirect_to_ptr(node);
max_index = radix_tree_maxindex(node->height);
void *newptr;
BUG_ON(!radix_tree_is_indirect_ptr(to_free));
- to_free = radix_tree_indirect_to_ptr(to_free);
+ to_free = indirect_to_ptr(to_free);
/*
* The candidate node has more than one child, or its child
/*
* We don't need rcu_assign_pointer(), since we are simply
- * moving the node from one part of the tree to another. If
- * it was safe to dereference the old pointer to it
+ * moving the node from one part of the tree to another: if it
+ * was safe to dereference the old pointer to it
* (to_free->slots[0]), it will be safe to dereference the new
- * one (root->rnode).
+ * one (root->rnode) as far as dependent read barriers go.
*/
newptr = to_free->slots[0];
if (root->height > 1)
- newptr = radix_tree_ptr_to_indirect(newptr);
+ newptr = ptr_to_indirect(newptr);
root->rnode = newptr;
root->height--;
+
+ /*
+ * We have a dilemma here. The node's slot[0] must not be
+ * NULLed in case there are concurrent lookups expecting to
+ * find the item. However if this was a bottom-level node,
+ * then it may be subject to the slot pointer being visible
+ * to callers dereferencing it. If item corresponding to
+ * slot[0] is subsequently deleted, these callers would expect
+ * their slot to become empty sooner or later.
+ *
+ * For example, lockless pagecache will look up a slot, deref
+ * the page pointer, and if the page is 0 refcount it means it
+ * was concurrently deleted from pagecache so try the deref
+ * again. Fortunately there is already a requirement for logic
+ * to retry the entire slot lookup -- the indirect pointer
+ * problem (replacing direct root node with an indirect pointer
+ * also results in a stale slot). So tag the slot as indirect
+ * to force callers to retry.
+ */
+ if (root->height == 0)
+ *((unsigned long *)&to_free->slots[0]) |=
+ RADIX_TREE_INDIRECT_PTR;
+
radix_tree_node_free(to_free);
}
}
root->rnode = NULL;
goto out;
}
- slot = radix_tree_indirect_to_ptr(slot);
+ slot = indirect_to_ptr(slot);
shift = (height - 1) * RADIX_TREE_MAP_SHIFT;
pathp->node = NULL;
radix_tree_node_free(to_free);
if (pathp->node->count) {
- if (pathp->node ==
- radix_tree_indirect_to_ptr(root->rnode))
+ if (pathp->node == indirect_to_ptr(root->rnode))
radix_tree_shrink(root);
goto out;
}
pagep = radix_tree_lookup_slot(&mapping->page_tree, offset);
if (pagep) {
page = radix_tree_deref_slot(pagep);
- if (unlikely(!page || page == RADIX_TREE_RETRY))
+ if (unlikely(!page))
+ goto out;
+ if (radix_tree_deref_retry(page))
goto repeat;
if (!page_cache_get_speculative(page))
goto repeat;
}
}
+out:
rcu_read_unlock();
return page;
page = radix_tree_deref_slot((void **)pages[i]);
if (unlikely(!page))
continue;
- /*
- * this can only trigger if nr_found == 1, making livelock
- * a non issue.
- */
- if (unlikely(page == RADIX_TREE_RETRY))
+ if (radix_tree_deref_retry(page)) {
+ if (ret)
+ start = pages[ret-1]->index;
goto restart;
+ }
if (!page_cache_get_speculative(page))
goto repeat;
page = radix_tree_deref_slot((void **)pages[i]);
if (unlikely(!page))
continue;
- /*
- * this can only trigger if nr_found == 1, making livelock
- * a non issue.
- */
- if (unlikely(page == RADIX_TREE_RETRY))
+ if (radix_tree_deref_retry(page))
goto restart;
if (page->mapping == NULL || page->index != index)
page = radix_tree_deref_slot((void **)pages[i]);
if (unlikely(!page))
continue;
- /*
- * this can only trigger if nr_found == 1, making livelock
- * a non issue.
- */
- if (unlikely(page == RADIX_TREE_RETRY))
+ if (radix_tree_deref_retry(page))
goto restart;
if (!page_cache_get_speculative(page))
goto page_not_up_to_date;
if (!trylock_page(page))
goto page_not_up_to_date;
+ /* Did it get truncated before we got the lock? */
+ if (!page->mapping)
+ goto page_not_up_to_date_locked;
if (!mapping->a_ops->is_partially_uptodate(page,
desc, offset))
goto page_not_up_to_date_locked;
memset(mem, 0, size);
mem->stat = alloc_percpu(struct mem_cgroup_stat_cpu);
- if (!mem->stat) {
- if (size < PAGE_SIZE)
- kfree(mem);
- else
- vfree(mem);
- mem = NULL;
- }
+ if (!mem->stat)
+ goto out_free;
spin_lock_init(&mem->pcp_counter_lock);
return mem;
+
+out_free:
+ if (size < PAGE_SIZE)
+ kfree(mem);
+ else
+ vfree(mem);
+ return NULL;
}
/*
mmu_notifier_invalidate_range_end(mm, start, end);
vm_stat_account(mm, oldflags, vma->vm_file, -nrpages);
vm_stat_account(mm, newflags, vma->vm_file, nrpages);
+ perf_event_mmap(vma);
return 0;
fail:
error = mprotect_fixup(vma, &prev, nstart, tmp, newflags);
if (error)
goto out;
- perf_event_mmap(vma);
nstart = tmp;
if (nstart < prev->vm_end)
* back off and wait for congestion to clear because further reclaim
* will encounter the same problem
*/
- if (nr_dirty == nr_congested)
+ if (nr_dirty == nr_congested && nr_dirty != 0)
zone_set_flag(zone, ZONE_CONGESTED);
free_page_list(&free_pages);
If you are unsure as to whether this is required, answer N.
+config SECURITY_DMESG_RESTRICT
+ bool "Restrict unprivileged access to the kernel syslog"
+ default n
+ help
+ This enforces restrictions on unprivileged users reading the kernel
+ syslog via dmesg(8).
+
+ If this option is not selected, no restrictions will be enforced
+ unless the dmesg_restrict sysctl is explicitly set to (1).
+
+ If you are unsure how to answer this question, answer N.
+
config SECURITY
bool "Enable different security models"
depends on SYSFS
error = register_security(&apparmor_ops);
if (error) {
AA_ERROR("Unable to register AppArmor\n");
- goto register_security_out;
+ goto set_init_cxt_out;
}
/* Report that AppArmor successfully initialized */
return error;
+set_init_cxt_out:
+ aa_free_task_context(current->real_cred->security);
+
register_security_out:
aa_free_root_ns();
apparmor_enabled = 0;
return error;
-
}
security_initcall(apparmor_init);
return ns;
fail_unconfined:
- kzfree(ns->base.name);
+ kzfree(ns->base.hname);
fail_ns:
kzfree(ns);
return NULL;
{
if (type != SYSLOG_ACTION_OPEN && from_file)
return 0;
+ if (dmesg_restrict && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
if ((type != SYSLOG_ACTION_READ_ALL &&
type != SYSLOG_ACTION_SIZE_BUFFER) && !capable(CAP_SYS_ADMIN))
return -EPERM;
SYNOPSIS
--------
[verse]
-'perf trace' {record <script> | report <script> [args] }
+'perf trace' [<options>]
+'perf trace' [<options>] record <script> [<record-options>] <command>
+'perf trace' [<options>] report <script> [script-args]
+'perf trace' [<options>] <script> <required-script-args> [<record-options>] <command>
+'perf trace' [<options>] <top-script> [script-args]
DESCRIPTION
-----------
available via 'perf trace -l'). The following variants allow you to
record and run those scripts:
- 'perf trace record <script>' to record the events required for 'perf
- trace report'. <script> is the name displayed in the output of
- 'perf trace --list' i.e. the actual script name minus any language
- extension.
+ 'perf trace record <script> <command>' to record the events required
+ for 'perf trace report'. <script> is the name displayed in the
+ output of 'perf trace --list' i.e. the actual script name minus any
+ language extension. If <command> is not specified, the events are
+ recorded using the -a (system-wide) 'perf record' option.
- 'perf trace report <script>' to run and display the results of
- <script>. <script> is the name displayed in the output of 'perf
+ 'perf trace report <script> [args]' to run and display the results
+ of <script>. <script> is the name displayed in the output of 'perf
trace --list' i.e. the actual script name minus any language
extension. The perf.data output from a previous run of 'perf trace
record <script>' is used and should be present for this command to
- succeed.
+ succeed. [args] refers to the (mainly optional) args expected by
+ the script.
+
+ 'perf trace <script> <required-script-args> <command>' to both
+ record the events required for <script> and to run the <script>
+ using 'live-mode' i.e. without writing anything to disk. <script>
+ is the name displayed in the output of 'perf trace --list' i.e. the
+ actual script name minus any language extension. If <command> is
+ not specified, the events are recorded using the -a (system-wide)
+ 'perf record' option. If <script> has any required args, they
+ should be specified before <command>. This mode doesn't allow for
+ optional script args to be specified; if optional script args are
+ desired, they can be specified using separate 'perf trace record'
+ and 'perf trace report' commands, with the stdout of the record step
+ piped to the stdin of the report script, using the '-o -' and '-i -'
+ options of the corresponding commands.
+
+ 'perf trace <top-script>' to both record the events required for
+ <top-script> and to run the <top-script> using 'live-mode'
+ i.e. without writing anything to disk. <top-script> is the name
+ displayed in the output of 'perf trace --list' i.e. the actual
+ script name minus any language extension; a <top-script> is defined
+ as any script name ending with the string 'top'.
+
+ [<record-options>] can be passed to the record steps of 'perf trace
+ record' and 'live-mode' variants; this isn't possible however for
+ <top-script> 'live-mode' or 'perf trace report' variants.
See the 'SEE ALSO' section for links to language-specific
information on how to write and run your own trace scripts.
OPTIONS
-------
+<command>...::
+ Any command you can specify in a shell.
+
-D::
--dump-raw-trace=::
Display verbose dump of the trace data.
Generate perf-trace.[ext] starter script for given language,
using current perf.data.
+-a::
+ Force system-wide collection. Scripts run without a <command>
+ normally use -a by default, while scripts run with a <command>
+ normally don't - this option allows the latter to be run in
+ system-wide mode.
+
+
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-trace-perl[1],
static bool force, append_file;
-static const struct option options[] = {
+const struct option record_options[] = {
OPT_CALLBACK('e', "event", NULL, "event",
"event selector. use 'perf list' to list available events",
parse_events),
{
int i, j, err = -ENOMEM;
- argc = parse_options(argc, argv, options, record_usage,
+ argc = parse_options(argc, argv, record_options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc && target_pid == -1 && target_tid == -1 &&
!system_wide && !cpu_list)
- usage_with_options(record_usage, options);
+ usage_with_options(record_usage, record_options);
if (force && append_file) {
fprintf(stderr, "Can't overwrite and append at the same time."
" You need to choose between -f and -A");
- usage_with_options(record_usage, options);
+ usage_with_options(record_usage, record_options);
} else if (append_file) {
write_mode = WRITE_APPEND;
} else {
if (thread_num <= 0) {
fprintf(stderr, "Can't find all threads of pid %d\n",
target_pid);
- usage_with_options(record_usage, options);
+ usage_with_options(record_usage, record_options);
}
} else {
all_tids=malloc(sizeof(pid_t));
static pid_t *all_tids = NULL;
static int thread_num = 0;
static bool inherit = false;
-static int profile_cpu = -1;
static int nr_cpus = 0;
static int realtime_prio = 0;
static bool group = false;
else
printf(" (all");
- if (profile_cpu != -1)
- printf(", cpu: %d)\n", profile_cpu);
+ if (cpu_list)
+ printf(", CPU%s: %s)\n", nr_cpus > 1 ? "s" : "", cpu_list);
else {
if (target_tid != -1)
printf(")\n");
else
- printf(", %d CPUs)\n", nr_cpus);
+ printf(", %d CPU%s)\n", nr_cpus, nr_cpus > 1 ? "s" : "");
}
printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
static void start_counter(int i, int counter)
{
struct perf_event_attr *attr;
- int cpu;
+ int cpu = -1;
int thread_index;
- cpu = profile_cpu;
- if (target_tid == -1 && profile_cpu == -1)
+ if (target_tid == -1)
cpu = cpumap[i];
attr = attrs + counter;
#include "util/symbol.h"
#include "util/thread.h"
#include "util/trace-event.h"
+#include "util/parse-options.h"
#include "util/util.h"
static char const *script_name;
static bool debug_mode;
static u64 last_timestamp;
static u64 nr_unordered;
+extern const struct option record_options[];
static int default_start_script(const char *script __unused,
int argc __unused,
{
struct script_desc *s = zalloc(sizeof(*s));
- if (s != NULL)
+ if (s != NULL && name)
s->name = strdup(name);
return s;
static void script_desc__delete(struct script_desc *s)
{
free(s->name);
+ free(s->half_liner);
+ free(s->args);
free(s);
}
return path;
}
+static bool is_top_script(const char *script_path)
+{
+ return ends_with((char *)script_path, "top") == NULL ? false : true;
+}
+
+static int has_required_arg(char *script_path)
+{
+ struct script_desc *desc;
+ int n_args = 0;
+ char *p;
+
+ desc = script_desc__new(NULL);
+
+ if (read_script_info(desc, script_path))
+ goto out;
+
+ if (!desc->args)
+ goto out;
+
+ for (p = desc->args; *p; p++)
+ if (*p == '<')
+ n_args++;
+out:
+ script_desc__delete(desc);
+
+ return n_args;
+}
+
static const char * const trace_usage[] = {
- "perf trace [<options>] <command>",
+ "perf trace [<options>]",
+ "perf trace [<options>] record <script> [<record-options>] <command>",
+ "perf trace [<options>] report <script> [script-args]",
+ "perf trace [<options>] <script> [<record-options>] <command>",
+ "perf trace [<options>] <top-script> [script-args]",
NULL
};
OPT_END()
};
+static bool have_cmd(int argc, const char **argv)
+{
+ char **__argv = malloc(sizeof(const char *) * argc);
+
+ if (!__argv)
+ die("malloc");
+ memcpy(__argv, argv, sizeof(const char *) * argc);
+ argc = parse_options(argc, (const char **)__argv, record_options,
+ NULL, PARSE_OPT_STOP_AT_NON_OPTION);
+ free(__argv);
+
+ return argc != 0;
+}
+
int cmd_trace(int argc, const char **argv, const char *prefix __used)
{
+ char *rec_script_path = NULL;
+ char *rep_script_path = NULL;
struct perf_session *session;
- const char *suffix = NULL;
+ char *script_path = NULL;
const char **__argv;
- char *script_path;
- int i, err;
+ bool system_wide;
+ int i, j, err;
- if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) {
- if (argc < 3) {
- fprintf(stderr,
- "Please specify a record script\n");
- return -1;
- }
- suffix = RECORD_SUFFIX;
+ setup_scripting();
+
+ argc = parse_options(argc, argv, options, trace_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);
+
+ if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {
+ rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);
+ if (!rec_script_path)
+ return cmd_record(argc, argv, NULL);
}
- if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) {
- if (argc < 3) {
+ if (argc > 1 && !strncmp(argv[0], "rep", strlen("rep"))) {
+ rep_script_path = get_script_path(argv[1], REPORT_SUFFIX);
+ if (!rep_script_path) {
fprintf(stderr,
- "Please specify a report script\n");
+ "Please specify a valid report script"
+ "(see 'perf trace -l' for listing)\n");
return -1;
}
- suffix = REPORT_SUFFIX;
}
/* make sure PERF_EXEC_PATH is set for scripts */
perf_set_argv_exec_path(perf_exec_path());
- if (!suffix && argc >= 2 && strncmp(argv[1], "-", strlen("-")) != 0) {
- char *record_script_path, *report_script_path;
+ if (argc && !script_name && !rec_script_path && !rep_script_path) {
int live_pipe[2];
+ int rep_args;
pid_t pid;
- record_script_path = get_script_path(argv[1], RECORD_SUFFIX);
- if (!record_script_path) {
- fprintf(stderr, "record script not found\n");
- return -1;
+ rec_script_path = get_script_path(argv[0], RECORD_SUFFIX);
+ rep_script_path = get_script_path(argv[0], REPORT_SUFFIX);
+
+ if (!rec_script_path && !rep_script_path) {
+ fprintf(stderr, " Couldn't find script %s\n\n See perf"
+ " trace -l for available scripts.\n", argv[0]);
+ usage_with_options(trace_usage, options);
}
- report_script_path = get_script_path(argv[1], REPORT_SUFFIX);
- if (!report_script_path) {
- fprintf(stderr, "report script not found\n");
- return -1;
+ if (is_top_script(argv[0])) {
+ rep_args = argc - 1;
+ } else {
+ int rec_args;
+
+ rep_args = has_required_arg(rep_script_path);
+ rec_args = (argc - 1) - rep_args;
+ if (rec_args < 0) {
+ fprintf(stderr, " %s script requires options."
+ "\n\n See perf trace -l for available "
+ "scripts and options.\n", argv[0]);
+ usage_with_options(trace_usage, options);
+ }
}
if (pipe(live_pipe) < 0) {
}
if (!pid) {
+ system_wide = true;
+ j = 0;
+
dup2(live_pipe[1], 1);
close(live_pipe[0]);
- __argv = malloc(6 * sizeof(const char *));
- __argv[0] = "/bin/sh";
- __argv[1] = record_script_path;
- __argv[2] = "-q";
- __argv[3] = "-o";
- __argv[4] = "-";
- __argv[5] = NULL;
+ if (!is_top_script(argv[0]))
+ system_wide = !have_cmd(argc - rep_args,
+ &argv[rep_args]);
+
+ __argv = malloc((argc + 6) * sizeof(const char *));
+ if (!__argv)
+ die("malloc");
+
+ __argv[j++] = "/bin/sh";
+ __argv[j++] = rec_script_path;
+ if (system_wide)
+ __argv[j++] = "-a";
+ __argv[j++] = "-q";
+ __argv[j++] = "-o";
+ __argv[j++] = "-";
+ for (i = rep_args + 1; i < argc; i++)
+ __argv[j++] = argv[i];
+ __argv[j++] = NULL;
execvp("/bin/sh", (char **)__argv);
+ free(__argv);
exit(-1);
}
dup2(live_pipe[0], 0);
close(live_pipe[1]);
- __argv = malloc((argc + 3) * sizeof(const char *));
- __argv[0] = "/bin/sh";
- __argv[1] = report_script_path;
- for (i = 2; i < argc; i++)
- __argv[i] = argv[i];
- __argv[i++] = "-i";
- __argv[i++] = "-";
- __argv[i++] = NULL;
+ __argv = malloc((argc + 4) * sizeof(const char *));
+ if (!__argv)
+ die("malloc");
+ j = 0;
+ __argv[j++] = "/bin/sh";
+ __argv[j++] = rep_script_path;
+ for (i = 1; i < rep_args + 1; i++)
+ __argv[j++] = argv[i];
+ __argv[j++] = "-i";
+ __argv[j++] = "-";
+ __argv[j++] = NULL;
execvp("/bin/sh", (char **)__argv);
+ free(__argv);
exit(-1);
}
- if (suffix) {
- script_path = get_script_path(argv[2], suffix);
- if (!script_path) {
- fprintf(stderr, "script not found\n");
- return -1;
- }
-
- __argv = malloc((argc + 1) * sizeof(const char *));
- __argv[0] = "/bin/sh";
- __argv[1] = script_path;
- for (i = 3; i < argc; i++)
- __argv[i - 1] = argv[i];
- __argv[argc - 1] = NULL;
+ if (rec_script_path)
+ script_path = rec_script_path;
+ if (rep_script_path)
+ script_path = rep_script_path;
+
+ if (script_path) {
+ system_wide = false;
+ j = 0;
+
+ if (rec_script_path)
+ system_wide = !have_cmd(argc - 1, &argv[1]);
+
+ __argv = malloc((argc + 2) * sizeof(const char *));
+ if (!__argv)
+ die("malloc");
+ __argv[j++] = "/bin/sh";
+ __argv[j++] = script_path;
+ if (system_wide)
+ __argv[j++] = "-a";
+ for (i = 2; i < argc; i++)
+ __argv[j++] = argv[i];
+ __argv[j++] = NULL;
execvp("/bin/sh", (char **)__argv);
+ free(__argv);
exit(-1);
}
- setup_scripting();
-
- argc = parse_options(argc, argv, options, trace_usage,
- PARSE_OPT_STOP_AT_NON_OPTION);
-
if (symbol__init() < 0)
return -1;
if (!script_name)
#!/bin/bash
-perf record -a -e raw_syscalls:sys_exit $@
+perf record -e raw_syscalls:sys_exit $@
#!/bin/bash
-perf record -a -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@
+perf record -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@
#!/bin/bash
-perf record -a -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
+perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
#!/bin/bash
-perf record -a -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
+perf record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write $@
#!/bin/bash
-perf record -a -e sched:sched_switch -e sched:sched_wakeup $@
+perf record -e sched:sched_switch -e sched:sched_wakeup $@
#!/bin/bash
-perf record -a -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@
+perf record -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@
#!/bin/bash
-perf record -a -e raw_syscalls:sys_exit $@
+perf record -e raw_syscalls:sys_exit $@
#!/bin/bash
-perf record -a -e syscalls:sys_enter_futex -e syscalls:sys_exit_futex $@
+perf record -e syscalls:sys_enter_futex -e syscalls:sys_exit_futex $@
#!/bin/bash
-perf record -a -e net:net_dev_xmit -e net:net_dev_queue \
+perf record -e net:net_dev_xmit -e net:net_dev_queue \
-e net:netif_receive_skb -e net:netif_rx \
-e skb:consume_skb -e skb:kfree_skb \
-e skb:skb_copy_datagram_iovec -e napi:napi_poll \
#!/bin/bash
-perf record -m 16384 -a -e sched:sched_wakeup -e sched:sched_wakeup_new -e sched:sched_switch -e sched:sched_migrate_task $@
+perf record -m 16384 -e sched:sched_wakeup -e sched:sched_wakeup_new -e sched:sched_switch -e sched:sched_migrate_task $@
#!/bin/bash
-perf record -a -e raw_syscalls:sys_enter $@
+perf record -e raw_syscalls:sys_enter $@
#!/bin/bash
-perf record -a -e raw_syscalls:sys_enter $@
+perf record -e raw_syscalls:sys_enter $@
#!/bin/bash
-perf record -a -e raw_syscalls:sys_enter $@
+perf record -e raw_syscalls:sys_enter $@
return rc;
}
+static const char yes[] = "Yes", no[] = "No";
+
bool ui__dialog_yesno(const char *msg)
{
/* newtWinChoice should really be accepting const char pointers... */
- char yes[] = "Yes", no[] = "No";
- return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
+ return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1;
}