diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-02-01 08:16:22 +0300 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-02-01 08:16:22 +0300 |
commit | a1f3d4bba8ea395a39d34ade6017afee8be16031 (patch) | |
tree | 874d843d35622f17aa6c3e048e42cf0d679bcb75 /drivers/char | |
parent | 723d928417bffff6467da155d8ebbbe016464012 (diff) | |
parent | ebf53826e105f488f4f628703a108e98940d1dc5 (diff) | |
download | linux-a1f3d4bba8ea395a39d34ade6017afee8be16031.tar.xz |
Merge commit 'v2.6.38-rc3' into next
Diffstat (limited to 'drivers/char')
81 files changed, 584 insertions, 35149 deletions
diff --git a/drivers/char/.gitignore b/drivers/char/.gitignore deleted file mode 100644 index 83683a2d8e6a..000000000000 --- a/drivers/char/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -consolemap_deftbl.c -defkeymap.c diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 43d3395325c5..b7980a83ce2d 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -5,7 +5,7 @@ menu "Character devices" config VT - bool "Virtual terminal" if EMBEDDED + bool "Virtual terminal" if EXPERT depends on !S390 select INPUT default y @@ -39,13 +39,13 @@ config VT config CONSOLE_TRANSLATIONS depends on VT default y - bool "Enable character translations in console" if EMBEDDED + bool "Enable character translations in console" if EXPERT ---help--- This enables support for font mapping and Unicode translation on virtual consoles. config VT_CONSOLE - bool "Support for console on virtual terminal" if EMBEDDED + bool "Support for console on virtual terminal" if EXPERT depends on VT default y ---help--- @@ -426,10 +426,10 @@ config SGI_MBCS If you have an SGI Altix with an attached SABrick say Y or M here, otherwise say N. -source "drivers/serial/Kconfig" +source "drivers/tty/serial/Kconfig" config UNIX98_PTYS - bool "Unix98 PTY support" if EMBEDDED + bool "Unix98 PTY support" if EXPERT default y ---help--- A pseudo terminal (PTY) is a software device consisting of two @@ -495,7 +495,7 @@ config LEGACY_PTY_COUNT config TTY_PRINTK bool "TTY driver to output user messages via printk" - depends on EMBEDDED + depends on EXPERT default n ---help--- If you say Y here, the support for writing user messages (i.e. @@ -682,6 +682,15 @@ config HVC_UDBG select HVC_DRIVER default n +config HVC_DCC + bool "ARM JTAG DCC console" + depends on ARM + select HVC_DRIVER + help + This console uses the JTAG DCC on ARM to create a console under the HVC + driver. This console is used through a JTAG only on ARM. If you don't have + a JTAG then you probably don't want this option. + config VIRTIO_CONSOLE tristate "Virtio console" depends on VIRTIO @@ -1038,15 +1047,6 @@ config NSC_GPIO pc8736x_gpio drivers. If those drivers are built as modules, this one will be too, named nsc_gpio -config CS5535_GPIO - tristate "AMD CS5535/CS5536 GPIO (Geode Companion Device)" - depends on X86_32 - help - Give userspace access to the GPIO pins on the AMD CS5535 and - CS5536 Geode companion devices. - - If compiled as a module, it will be called cs5535_gpio. - config RAW_DRIVER tristate "RAW driver (/dev/raw/rawN)" depends on BLOCK diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 3a9c01416839..5bc765d4c3ca 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -2,24 +2,10 @@ # Makefile for the kernel character device drivers. # -# -# This file contains the font map for the default (hardware) font -# -FONTMAPFILE = cp437.uni - -obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o - -obj-y += tty_mutex.o -obj-$(CONFIG_LEGACY_PTYS) += pty.o -obj-$(CONFIG_UNIX98_PTYS) += pty.o +obj-y += mem.o random.o obj-$(CONFIG_TTY_PRINTK) += ttyprintk.o obj-y += misc.o -obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o selection.o keyboard.o obj-$(CONFIG_BFIN_JTAG_COMM) += bfin_jtag_comm.o -obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o -obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o -obj-$(CONFIG_AUDIT) += tty_audit.o -obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o @@ -41,29 +27,15 @@ obj-$(CONFIG_ISI) += isicom.o obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o -obj-$(CONFIG_N_HDLC) += n_hdlc.o -obj-$(CONFIG_N_GSM) += n_gsm.o obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o -obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o -obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o -obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o -obj-$(CONFIG_HVC_TILE) += hvc_tile.o -obj-$(CONFIG_HVC_BEAT) += hvc_beat.o -obj-$(CONFIG_HVC_DRIVER) += hvc_console.o -obj-$(CONFIG_HVC_IRQ) += hvc_irq.o -obj-$(CONFIG_HVC_XEN) += hvc_xen.o -obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o -obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o -obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_MMTIMER) += mmtimer.o obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o obj-$(CONFIG_VIOTAPE) += viotape.o -obj-$(CONFIG_HVCS) += hvcs.o obj-$(CONFIG_IBM_BSR) += bsr.o obj-$(CONFIG_SGI_MBCS) += mbcs.o obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o @@ -74,7 +46,6 @@ obj-$(CONFIG_PRINTER) += lp.o obj-$(CONFIG_APM_EMULATION) += apm-emulation.o obj-$(CONFIG_DTLK) += dtlk.o -obj-$(CONFIG_R3964) += n_r3964.o obj-$(CONFIG_APPLICOM) += applicom.o obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_RTC) += rtc.o @@ -98,7 +69,6 @@ obj-$(CONFIG_NWFLASH) += nwflash.o obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o -obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o obj-$(CONFIG_GPIO_TB0219) += tb0219.o obj-$(CONFIG_TELCLOCK) += tlclk.o @@ -115,28 +85,3 @@ obj-$(CONFIG_RAMOOPS) += ramoops.o obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.o - -# Files generated that shall be removed upon make clean -clean-files := consolemap_deftbl.c defkeymap.c - -quiet_cmd_conmk = CONMK $@ - cmd_conmk = scripts/conmakehash $< > $@ - -$(obj)/consolemap_deftbl.c: $(src)/$(FONTMAPFILE) - $(call cmd,conmk) - -$(obj)/defkeymap.o: $(obj)/defkeymap.c - -# Uncomment if you're changing the keymap and have an appropriate -# loadkeys version for the map. By default, we'll use the shipped -# versions. -# GENERATE_KEYMAP := 1 - -ifdef GENERATE_KEYMAP - -$(obj)/defkeymap.c: $(obj)/%.c: $(src)/%.map - loadkeys --mktable $< > $@.tmp - sed -e 's/^static *//' $@.tmp > $@ - rm $@.tmp - -endif diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 5259065f3c79..3e67ddde9e16 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -120,7 +120,6 @@ struct agp_bridge_driver { void (*agp_destroy_page)(struct page *, int flags); void (*agp_destroy_pages)(struct agp_memory *); int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); - void (*chipset_flush)(struct agp_bridge_data *); }; struct agp_bridge_data { diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 42396df55556..9252e85706ef 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -38,7 +38,7 @@ static int agp_bridges_found; static void amd64_tlbflush(struct agp_memory *temp) { - k8_flush_garts(); + amd_flush_garts(); } static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type) @@ -124,7 +124,7 @@ static int amd64_fetch_size(void) u32 temp; struct aper_size_info_32 *values; - dev = k8_northbridges.nb_misc[0]; + dev = node_to_amd_nb(0)->misc; if (dev==NULL) return 0; @@ -181,16 +181,15 @@ static int amd_8151_configure(void) unsigned long gatt_bus = virt_to_phys(agp_bridge->gatt_table_real); int i; - if (!k8_northbridges.gart_supported) + if (!amd_nb_has_feature(AMD_NB_GART)) return 0; /* Configure AGP regs in each x86-64 host bridge. */ - for (i = 0; i < k8_northbridges.num; i++) { + for (i = 0; i < amd_nb_num(); i++) { agp_bridge->gart_bus_addr = - amd64_configure(k8_northbridges.nb_misc[i], - gatt_bus); + amd64_configure(node_to_amd_nb(i)->misc, gatt_bus); } - k8_flush_garts(); + amd_flush_garts(); return 0; } @@ -200,11 +199,11 @@ static void amd64_cleanup(void) u32 tmp; int i; - if (!k8_northbridges.gart_supported) + if (!amd_nb_has_feature(AMD_NB_GART)) return; - for (i = 0; i < k8_northbridges.num; i++) { - struct pci_dev *dev = k8_northbridges.nb_misc[i]; + for (i = 0; i < amd_nb_num(); i++) { + struct pci_dev *dev = node_to_amd_nb(i)->misc; /* disable gart translation */ pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &tmp); tmp &= ~GARTEN; @@ -331,15 +330,15 @@ static __devinit int cache_nbs(struct pci_dev *pdev, u32 cap_ptr) { int i; - if (cache_k8_northbridges() < 0) + if (amd_cache_northbridges() < 0) return -ENODEV; - if (!k8_northbridges.gart_supported) + if (!amd_nb_has_feature(AMD_NB_GART)) return -ENODEV; i = 0; - for (i = 0; i < k8_northbridges.num; i++) { - struct pci_dev *dev = k8_northbridges.nb_misc[i]; + for (i = 0; i < amd_nb_num(); i++) { + struct pci_dev *dev = node_to_amd_nb(i)->misc; if (fix_northbridge(dev, pdev, cap_ptr) < 0) { dev_err(&dev->dev, "no usable aperture found\n"); #ifdef __x86_64__ @@ -416,7 +415,7 @@ static int __devinit uli_agp_init(struct pci_dev *pdev) } /* shadow x86-64 registers into ULi registers */ - pci_read_config_dword (k8_northbridges.nb_misc[0], AMD64_GARTAPERTUREBASE, + pci_read_config_dword (node_to_amd_nb(0)->misc, AMD64_GARTAPERTUREBASE, &httfea); /* if x86-64 aperture base is beyond 4G, exit here */ @@ -484,7 +483,7 @@ static int nforce3_agp_init(struct pci_dev *pdev) pci_write_config_dword(dev1, NVIDIA_X86_64_1_APSIZE, tmp); /* shadow x86-64 registers into NVIDIA registers */ - pci_read_config_dword (k8_northbridges.nb_misc[0], AMD64_GARTAPERTUREBASE, + pci_read_config_dword (node_to_amd_nb(0)->misc, AMD64_GARTAPERTUREBASE, &apbase); /* if x86-64 aperture base is beyond 4G, exit here */ @@ -778,7 +777,7 @@ int __init agp_amd64_init(void) } /* First check that we have at least one AMD64 NB */ - if (!pci_dev_present(k8_nb_ids)) + if (!pci_dev_present(amd_nb_misc_ids)) return -ENODEV; /* Look for any AGP bridge */ diff --git a/drivers/char/agp/compat_ioctl.c b/drivers/char/agp/compat_ioctl.c index 9d2c97a69cdd..a48e05b31593 100644 --- a/drivers/char/agp/compat_ioctl.c +++ b/drivers/char/agp/compat_ioctl.c @@ -276,7 +276,6 @@ long compat_agp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case AGPIOC_CHIPSET_FLUSH32: - ret_val = agpioc_chipset_flush_wrap(curr_priv); break; } diff --git a/drivers/char/agp/compat_ioctl.h b/drivers/char/agp/compat_ioctl.h index 0c9678ac0371..f30e0fd97963 100644 --- a/drivers/char/agp/compat_ioctl.h +++ b/drivers/char/agp/compat_ioctl.h @@ -102,6 +102,5 @@ void agp_free_memory_wrap(struct agp_memory *memory); struct agp_memory *agp_allocate_memory_wrap(size_t pg_count, u32 type); struct agp_memory *agp_find_mem_by_key(int key); struct agp_client *agp_find_client_by_pid(pid_t id); -int agpioc_chipset_flush_wrap(struct agp_file_private *priv); #endif /* _AGP_COMPAT_H */ diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c index 43412c03969e..2e044338753c 100644 --- a/drivers/char/agp/frontend.c +++ b/drivers/char/agp/frontend.c @@ -39,7 +39,6 @@ #include <linux/mm.h> #include <linux/fs.h> #include <linux/sched.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/pgtable.h> #include "agp.h" @@ -958,13 +957,6 @@ static int agpioc_unbind_wrap(struct agp_file_private *priv, void __user *arg) return agp_unbind_memory(memory); } -int agpioc_chipset_flush_wrap(struct agp_file_private *priv) -{ - DBG(""); - agp_flush_chipset(agp_bridge); - return 0; -} - static long agp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1040,7 +1032,6 @@ static long agp_ioctl(struct file *file, break; case AGPIOC_CHIPSET_FLUSH: - ret_val = agpioc_chipset_flush_wrap(curr_priv); break; } diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 4956f1c8f9d5..012cba0d6d96 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -81,13 +81,6 @@ static int agp_get_key(void) return -1; } -void agp_flush_chipset(struct agp_bridge_data *bridge) -{ - if (bridge->driver->chipset_flush) - bridge->driver->chipset_flush(bridge); -} -EXPORT_SYMBOL(agp_flush_chipset); - /* * Use kmalloc if possible for the page list. Otherwise fall back to * vmalloc. This speeds things up and also saves memory for small AGP @@ -487,26 +480,6 @@ int agp_unbind_memory(struct agp_memory *curr) } EXPORT_SYMBOL(agp_unbind_memory); -/** - * agp_rebind_emmory - Rewrite the entire GATT, useful on resume - */ -int agp_rebind_memory(void) -{ - struct agp_memory *curr; - int ret_val = 0; - - spin_lock(&agp_bridge->mapped_lock); - list_for_each_entry(curr, &agp_bridge->mapped_list, mapped_list) { - ret_val = curr->bridge->driver->insert_memory(curr, - curr->pg_start, - curr->type); - if (ret_val != 0) - break; - } - spin_unlock(&agp_bridge->mapped_lock); - return ret_val; -} -EXPORT_SYMBOL(agp_rebind_memory); /* End - Routines for handling swapping of agp_memory into the GATT */ diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index e72f49d52202..857df10c0428 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -717,8 +717,8 @@ static const struct intel_agp_driver_description { { PCI_DEVICE_ID_INTEL_82820_UP_HB, "i820", &intel_820_driver }, { PCI_DEVICE_ID_INTEL_82830_HB, "830M", &intel_830mp_driver }, { PCI_DEVICE_ID_INTEL_82840_HB, "i840", &intel_840_driver }, - { PCI_DEVICE_ID_INTEL_82845_HB, "845G", &intel_845_driver }, - { PCI_DEVICE_ID_INTEL_82845G_HB, "830M", &intel_845_driver }, + { PCI_DEVICE_ID_INTEL_82845_HB, "i845", &intel_845_driver }, + { PCI_DEVICE_ID_INTEL_82845G_HB, "845G", &intel_845_driver }, { PCI_DEVICE_ID_INTEL_82850_HB, "i850", &intel_850_driver }, { PCI_DEVICE_ID_INTEL_82854_HB, "854", &intel_845_driver }, { PCI_DEVICE_ID_INTEL_82855PM_HB, "855PM", &intel_845_driver }, @@ -828,14 +828,9 @@ static void __devexit agp_intel_remove(struct pci_dev *pdev) static int agp_intel_resume(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); - int ret_val; bridge->driver->configure(); - ret_val = agp_rebind_memory(); - if (ret_val != 0) - return ret_val; - return 0; } #endif diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index 90539df02504..c195bfeade11 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -75,6 +75,8 @@ #define I810_GMS_DISABLE 0x00000000 #define I810_PGETBL_CTL 0x2020 #define I810_PGETBL_ENABLED 0x00000001 +/* Note: PGETBL_CTL2 has a different offset on G33. */ +#define I965_PGETBL_CTL2 0x20c4 #define I965_PGETBL_SIZE_MASK 0x0000000e #define I965_PGETBL_SIZE_512KB (0 << 1) #define I965_PGETBL_SIZE_256KB (1 << 1) @@ -82,9 +84,17 @@ #define I965_PGETBL_SIZE_1MB (3 << 1) #define I965_PGETBL_SIZE_2MB (4 << 1) #define I965_PGETBL_SIZE_1_5MB (5 << 1) -#define G33_PGETBL_SIZE_MASK (3 << 8) -#define G33_PGETBL_SIZE_1M (1 << 8) -#define G33_PGETBL_SIZE_2M (2 << 8) +#define G33_GMCH_SIZE_MASK (3 << 8) +#define G33_GMCH_SIZE_1M (1 << 8) +#define G33_GMCH_SIZE_2M (2 << 8) +#define G4x_GMCH_SIZE_MASK (0xf << 8) +#define G4x_GMCH_SIZE_1M (0x1 << 8) +#define G4x_GMCH_SIZE_2M (0x3 << 8) +#define G4x_GMCH_SIZE_VT_1M (0x9 << 8) +#define G4x_GMCH_SIZE_VT_1_5M (0xa << 8) +#define G4x_GMCH_SIZE_VT_2M (0xc << 8) + +#define GFX_FLSH_CNTL 0x2170 /* 915+ */ #define I810_DRAM_CTL 0x3000 #define I810_DRAM_ROW_0 0x00000001 diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 6b6760ea2435..fab3d3265adb 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -24,7 +24,6 @@ #include <asm/smp.h> #include "agp.h" #include "intel-agp.h" -#include <linux/intel-gtt.h> #include <drm/intel-gtt.h> /* @@ -39,40 +38,12 @@ #define USE_PCI_DMA_API 0 #endif -/* Max amount of stolen space, anything above will be returned to Linux */ -int intel_max_stolen = 32 * 1024 * 1024; - -static const struct aper_size_info_fixed intel_i810_sizes[] = -{ - {64, 16384, 4}, - /* The 32M mode still requires a 64k gatt */ - {32, 8192, 4} -}; - -#define AGP_DCACHE_MEMORY 1 -#define AGP_PHYS_MEMORY 2 -#define INTEL_AGP_CACHED_MEMORY 3 - -static struct gatt_mask intel_i810_masks[] = -{ - {.mask = I810_PTE_VALID, .type = 0}, - {.mask = (I810_PTE_VALID | I810_PTE_LOCAL), .type = AGP_DCACHE_MEMORY}, - {.mask = I810_PTE_VALID, .type = 0}, - {.mask = I810_PTE_VALID | I830_PTE_SYSTEM_CACHED, - .type = INTEL_AGP_CACHED_MEMORY} -}; - -#define INTEL_AGP_UNCACHED_MEMORY 0 -#define INTEL_AGP_CACHED_MEMORY_LLC 1 -#define INTEL_AGP_CACHED_MEMORY_LLC_GFDT 2 -#define INTEL_AGP_CACHED_MEMORY_LLC_MLC 3 -#define INTEL_AGP_CACHED_MEMORY_LLC_MLC_GFDT 4 - struct intel_gtt_driver { unsigned int gen : 8; unsigned int is_g33 : 1; unsigned int is_pineview : 1; unsigned int is_ironlake : 1; + unsigned int has_pgtbl_enable : 1; unsigned int dma_mask_size : 8; /* Chipset specific GTT setup */ int (*setup)(void); @@ -95,13 +66,15 @@ static struct _intel_private { u8 __iomem *registers; phys_addr_t gtt_bus_addr; phys_addr_t gma_bus_addr; - phys_addr_t pte_bus_addr; + u32 PGETBL_save; u32 __iomem *gtt; /* I915G */ + bool clear_fake_agp; /* on first access via agp, fill with scratch */ int num_dcache_entries; union { void __iomem *i9xx_flush_page; void *i8xx_flush_page; }; + char *i81x_gtt_table; struct page *i8xx_page; struct resource ifp_resource; int resource_valid; @@ -113,42 +86,31 @@ static struct _intel_private { #define IS_G33 intel_private.driver->is_g33 #define IS_PINEVIEW intel_private.driver->is_pineview #define IS_IRONLAKE intel_private.driver->is_ironlake +#define HAS_PGTBL_EN intel_private.driver->has_pgtbl_enable -static void intel_agp_free_sglist(struct agp_memory *mem) -{ - struct sg_table st; - - st.sgl = mem->sg_list; - st.orig_nents = st.nents = mem->page_count; - - sg_free_table(&st); - - mem->sg_list = NULL; - mem->num_sg = 0; -} - -static int intel_agp_map_memory(struct agp_memory *mem) +int intel_gtt_map_memory(struct page **pages, unsigned int num_entries, + struct scatterlist **sg_list, int *num_sg) { struct sg_table st; struct scatterlist *sg; int i; - if (mem->sg_list) + if (*sg_list) return 0; /* already mapped (for e.g. resume */ - DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); + DBG("try mapping %lu pages\n", (unsigned long)num_entries); - if (sg_alloc_table(&st, mem->page_count, GFP_KERNEL)) + if (sg_alloc_table(&st, num_entries, GFP_KERNEL)) goto err; - mem->sg_list = sg = st.sgl; + *sg_list = sg = st.sgl; - for (i = 0 ; i < mem->page_count; i++, sg = sg_next(sg)) - sg_set_page(sg, mem->pages[i], PAGE_SIZE, 0); + for (i = 0 ; i < num_entries; i++, sg = sg_next(sg)) + sg_set_page(sg, pages[i], PAGE_SIZE, 0); - mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list, - mem->page_count, PCI_DMA_BIDIRECTIONAL); - if (unlikely(!mem->num_sg)) + *num_sg = pci_map_sg(intel_private.pcidev, *sg_list, + num_entries, PCI_DMA_BIDIRECTIONAL); + if (unlikely(!*num_sg)) goto err; return 0; @@ -157,90 +119,22 @@ err: sg_free_table(&st); return -ENOMEM; } +EXPORT_SYMBOL(intel_gtt_map_memory); -static void intel_agp_unmap_memory(struct agp_memory *mem) +void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg) { + struct sg_table st; DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count); - pci_unmap_sg(intel_private.pcidev, mem->sg_list, - mem->page_count, PCI_DMA_BIDIRECTIONAL); - intel_agp_free_sglist(mem); -} + pci_unmap_sg(intel_private.pcidev, sg_list, + num_sg, PCI_DMA_BIDIRECTIONAL); -static int intel_i810_fetch_size(void) -{ - u32 smram_miscc; - struct aper_size_info_fixed *values; - - pci_read_config_dword(intel_private.bridge_dev, - I810_SMRAM_MISCC, &smram_miscc); - values = A_SIZE_FIX(agp_bridge->driver->aperture_sizes); - - if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) { - dev_warn(&intel_private.bridge_dev->dev, "i810 is disabled\n"); - return 0; - } - if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) { - agp_bridge->current_size = (void *) (values + 1); - agp_bridge->aperture_size_idx = 1; - return values[1].size; - } else { - agp_bridge->current_size = (void *) (values); - agp_bridge->aperture_size_idx = 0; - return values[0].size; - } - - return 0; -} + st.sgl = sg_list; + st.orig_nents = st.nents = num_sg; -static int intel_i810_configure(void) -{ - struct aper_size_info_fixed *current_size; - u32 temp; - int i; - - current_size = A_SIZE_FIX(agp_bridge->current_size); - - if (!intel_private.registers) { - pci_read_config_dword(intel_private.pcidev, I810_MMADDR, &temp); - temp &= 0xfff80000; - - intel_private.registers = ioremap(temp, 128 * 4096); - if (!intel_private.registers) { - dev_err(&intel_private.pcidev->dev, - "can't remap memory\n"); - return -ENOMEM; - } - } - - if ((readl(intel_private.registers+I810_DRAM_CTL) - & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) { - /* This will need to be dynamically assigned */ - dev_info(&intel_private.pcidev->dev, - "detected 4MB dedicated video ram\n"); - intel_private.num_dcache_entries = 1024; - } - pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &temp); - agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); - writel(agp_bridge->gatt_bus_addr | I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ - - if (agp_bridge->driver->needs_scratch_page) { - for (i = 0; i < current_size->num_entries; i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); /* PCI posting. */ - } - global_cache_flush(); - return 0; -} - -static void intel_i810_cleanup(void) -{ - writel(0, intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers); /* PCI Posting. */ - iounmap(intel_private.registers); + sg_free_table(&st); } +EXPORT_SYMBOL(intel_gtt_unmap_memory); static void intel_fake_agp_enable(struct agp_bridge_data *bridge, u32 mode) { @@ -277,80 +171,64 @@ static void i8xx_destroy_pages(struct page *page) atomic_dec(&agp_bridge->current_memory_agp); } -static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, - int type) +#define I810_GTT_ORDER 4 +static int i810_setup(void) { - int i, j, num_entries; - void *temp; - int ret = -EINVAL; - int mask_type; - - if (mem->page_count == 0) - goto out; - - temp = agp_bridge->current_size; - num_entries = A_SIZE_FIX(temp)->num_entries; + u32 reg_addr; + char *gtt_table; - if ((pg_start + mem->page_count) > num_entries) - goto out_err; + /* i81x does not preallocate the gtt. It's always 64kb in size. */ + gtt_table = alloc_gatt_pages(I810_GTT_ORDER); + if (gtt_table == NULL) + return -ENOMEM; + intel_private.i81x_gtt_table = gtt_table; + pci_read_config_dword(intel_private.pcidev, I810_MMADDR, ®_addr); + reg_addr &= 0xfff80000; - for (j = pg_start; j < (pg_start + mem->page_count); j++) { - if (!PGE_EMPTY(agp_bridge, readl(agp_bridge->gatt_table+j))) { - ret = -EBUSY; - goto out_err; - } - } + intel_private.registers = ioremap(reg_addr, KB(64)); + if (!intel_private.registers) + return -ENOMEM; - if (type != mem->type) - goto out_err; + writel(virt_to_phys(gtt_table) | I810_PGETBL_ENABLED, + intel_private.registers+I810_PGETBL_CTL); - mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type); + intel_private.gtt_bus_addr = reg_addr + I810_PTE_BASE; - switch (mask_type) { - case AGP_DCACHE_MEMORY: - if (!mem->is_flushed) - global_cache_flush(); - for (i = pg_start; i < (pg_start + mem->page_count); i++) { - writel((i*4096)|I810_PTE_LOCAL|I810_PTE_VALID, - intel_private.registers+I810_PTE_BASE+(i*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); - break; - case AGP_PHYS_MEMORY: - case AGP_NORMAL_MEMORY: - if (!mem->is_flushed) - global_cache_flush(); - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - page_to_phys(mem->pages[i]), mask_type), - intel_private.registers+I810_PTE_BASE+(j*4)); - } - readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); - break; - default: - goto out_err; + if ((readl(intel_private.registers+I810_DRAM_CTL) + & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) { + dev_info(&intel_private.pcidev->dev, + "detected 4MB dedicated video ram\n"); + intel_private.num_dcache_entries = 1024; } -out: - ret = 0; -out_err: - mem->is_flushed = true; - return ret; + return 0; +} + +static void i810_cleanup(void) +{ + writel(0, intel_private.registers+I810_PGETBL_CTL); + free_gatt_pages(intel_private.i81x_gtt_table, I810_GTT_ORDER); } -static int intel_i810_remove_entries(struct agp_memory *mem, off_t pg_start, - int type) +static int i810_insert_dcache_entries(struct agp_memory *mem, off_t pg_start, + int type) { int i; - if (mem->page_count == 0) - return 0; + if ((pg_start + mem->page_count) + > intel_private.num_dcache_entries) + return -EINVAL; + + if (!mem->is_flushed) + global_cache_flush(); - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - writel(agp_bridge->scratch_page, intel_private.registers+I810_PTE_BASE+(i*4)); + for (i = pg_start; i < (pg_start + mem->page_count); i++) { + dma_addr_t addr = i << PAGE_SHIFT; + intel_private.driver->write_entry(addr, + i, type); } - readl(intel_private.registers+I810_PTE_BASE+((i-1)*4)); + readl(intel_private.gtt+i-1); return 0; } @@ -397,29 +275,6 @@ static struct agp_memory *alloc_agpphysmem_i8xx(size_t pg_count, int type) return new; } -static struct agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type) -{ - struct agp_memory *new; - - if (type == AGP_DCACHE_MEMORY) { - if (pg_count != intel_private.num_dcache_entries) - return NULL; - - new = agp_create_memory(1); - if (new == NULL) - return NULL; - - new->type = AGP_DCACHE_MEMORY; - new->page_count = pg_count; - new->num_scratch_pages = 0; - agp_free_page_array(new); - return new; - } - if (type == AGP_PHYS_MEMORY) - return alloc_agpphysmem_i8xx(pg_count, type); - return NULL; -} - static void intel_i810_free_by_type(struct agp_memory *curr) { agp_free_key(curr->key); @@ -437,13 +292,6 @@ static void intel_i810_free_by_type(struct agp_memory *curr) kfree(curr); } -static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge, - dma_addr_t addr, int type) -{ - /* Type checking must be done elsewhere */ - return addr | bridge->driver->masks[type].mask; -} - static int intel_gtt_setup_scratch_page(void) { struct page *page; @@ -455,7 +303,7 @@ static int intel_gtt_setup_scratch_page(void) get_page(page); set_pages_uc(page, 1); - if (USE_PCI_DMA_API && INTEL_GTT_GEN > 2) { + if (intel_private.base.needs_dmar) { dma_addr = pci_map_page(intel_private.pcidev, page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); if (pci_dma_mapping_error(intel_private.pcidev, dma_addr)) @@ -470,34 +318,45 @@ static int intel_gtt_setup_scratch_page(void) return 0; } -static const struct aper_size_info_fixed const intel_fake_agp_sizes[] = { +static void i810_write_entry(dma_addr_t addr, unsigned int entry, + unsigned int flags) +{ + u32 pte_flags = I810_PTE_VALID; + + switch (flags) { + case AGP_DCACHE_MEMORY: + pte_flags |= I810_PTE_LOCAL; + break; + case AGP_USER_CACHED_MEMORY: + pte_flags |= I830_PTE_SYSTEM_CACHED; + break; + } + + writel(addr | pte_flags, intel_private.gtt + entry); +} + +static const struct aper_size_info_fixed intel_fake_agp_sizes[] = { + {32, 8192, 3}, + {64, 16384, 4}, {128, 32768, 5}, - /* The 64M mode still requires a 128k gatt */ - {64, 16384, 5}, {256, 65536, 6}, {512, 131072, 7}, }; -static unsigned int intel_gtt_stolen_entries(void) +static unsigned int intel_gtt_stolen_size(void) { u16 gmch_ctrl; u8 rdct; int local = 0; static const int ddt[4] = { 0, 16, 32, 64 }; - unsigned int overhead_entries, stolen_entries; unsigned int stolen_size = 0; + if (INTEL_GTT_GEN == 1) + return 0; /* no stolen mem on i81x */ + pci_read_config_word(intel_private.bridge_dev, I830_GMCH_CTRL, &gmch_ctrl); - if (INTEL_GTT_GEN > 4 || IS_PINEVIEW) - overhead_entries = 0; - else - overhead_entries = intel_private.base.gtt_mappable_entries - / 1024; - - overhead_entries += 1; /* BIOS popup */ - if (intel_private.bridge_dev->device == PCI_DEVICE_ID_INTEL_82830_HB || intel_private.bridge_dev->device == PCI_DEVICE_ID_INTEL_82845G_HB) { switch (gmch_ctrl & I830_GMCH_GMS_MASK) { @@ -623,12 +482,7 @@ static unsigned int intel_gtt_stolen_entries(void) } } - if (!local && stolen_size > intel_max_stolen) { - dev_info(&intel_private.bridge_dev->dev, - "detected %dK stolen memory, trimming to %dK\n", - stolen_size / KB(1), intel_max_stolen / KB(1)); - stolen_size = intel_max_stolen; - } else if (stolen_size > 0) { + if (stolen_size > 0) { dev_info(&intel_private.bridge_dev->dev, "detected %dK %s memory\n", stolen_size / KB(1), local ? "local" : "stolen"); } else { @@ -637,46 +491,88 @@ static unsigned int intel_gtt_stolen_entries(void) stolen_size = 0; } - stolen_entries = stolen_size/KB(4) - overhead_entries; + return stolen_size; +} - return stolen_entries; +static void i965_adjust_pgetbl_size(unsigned int size_flag) +{ + u32 pgetbl_ctl, pgetbl_ctl2; + + /* ensure that ppgtt is disabled */ + pgetbl_ctl2 = readl(intel_private.registers+I965_PGETBL_CTL2); + pgetbl_ctl2 &= ~I810_PGETBL_ENABLED; + writel(pgetbl_ctl2, intel_private.registers+I965_PGETBL_CTL2); + + /* write the new ggtt size */ + pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL); + pgetbl_ctl &= ~I965_PGETBL_SIZE_MASK; + pgetbl_ctl |= size_flag; + writel(pgetbl_ctl, intel_private.registers+I810_PGETBL_CTL); } -static unsigned int intel_gtt_total_entries(void) +static unsigned int i965_gtt_total_entries(void) { int size; + u32 pgetbl_ctl; + u16 gmch_ctl; - if (IS_G33 || INTEL_GTT_GEN == 4 || INTEL_GTT_GEN == 5) { - u32 pgetbl_ctl; - pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL); + pci_read_config_word(intel_private.bridge_dev, + I830_GMCH_CTRL, &gmch_ctl); - switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) { - case I965_PGETBL_SIZE_128KB: - size = KB(128); + if (INTEL_GTT_GEN == 5) { + switch (gmch_ctl & G4x_GMCH_SIZE_MASK) { + case G4x_GMCH_SIZE_1M: + case G4x_GMCH_SIZE_VT_1M: + i965_adjust_pgetbl_size(I965_PGETBL_SIZE_1MB); break; - case I965_PGETBL_SIZE_256KB: - size = KB(256); + case G4x_GMCH_SIZE_VT_1_5M: + i965_adjust_pgetbl_size(I965_PGETBL_SIZE_1_5MB); break; - case I965_PGETBL_SIZE_512KB: - size = KB(512); + case G4x_GMCH_SIZE_2M: + case G4x_GMCH_SIZE_VT_2M: + i965_adjust_pgetbl_size(I965_PGETBL_SIZE_2MB); break; - case I965_PGETBL_SIZE_1MB: - size = KB(1024); - break; - case I965_PGETBL_SIZE_2MB: - size = KB(2048); - break; - case I965_PGETBL_SIZE_1_5MB: - size = KB(1024 + 512); - break; - default: - dev_info(&intel_private.pcidev->dev, - "unknown page table size, assuming 512KB\n"); - size = KB(512); } + } - return size/4; - } else if (INTEL_GTT_GEN == 6) { + pgetbl_ctl = readl(intel_private.registers+I810_PGETBL_CTL); + + switch (pgetbl_ctl & I965_PGETBL_SIZE_MASK) { + case I965_PGETBL_SIZE_128KB: + size = KB(128); + break; + case I965_PGETBL_SIZE_256KB: + size = KB(256); + break; + case I965_PGETBL_SIZE_512KB: + size = KB(512); + break; + /* GTT pagetable sizes bigger than 512KB are not possible on G33! */ + case I965_PGETBL_SIZE_1MB: + size = KB(1024); + break; + case I965_PGETBL_SIZE_2MB: + size = KB(2048); + break; + case I965_PGETBL_SIZE_1_5MB: + size = KB(1024 + 512); + break; + default: + dev_info(&intel_private.pcidev->dev, + "unknown page table size, assuming 512KB\n"); + size = KB(512); + } + + return size/4; +} + +static unsigned int intel_gtt_total_entries(void) +{ + int size; + + if (IS_G33 || INTEL_GTT_GEN == 4 || INTEL_GTT_GEN == 5) + return i965_gtt_total_entries(); + else if (INTEL_GTT_GEN == 6) { u16 snb_gmch_ctl; pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl); @@ -706,7 +602,18 @@ static unsigned int intel_gtt_mappable_entries(void) { unsigned int aperture_size; - if (INTEL_GTT_GEN == 2) { + if (INTEL_GTT_GEN == 1) { + u32 smram_miscc; + + pci_read_config_dword(intel_private.bridge_dev, + I810_SMRAM_MISCC, &smram_miscc); + + if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) + == I810_GFX_MEM_WIN_32M) + aperture_size = MB(32); + else + aperture_size = MB(64); + } else if (INTEL_GTT_GEN == 2) { u16 gmch_ctrl; pci_read_config_word(intel_private.bridge_dev, @@ -739,7 +646,7 @@ static void intel_gtt_cleanup(void) iounmap(intel_private.gtt); iounmap(intel_private.registers); - + intel_gtt_teardown_scratch_page(); } @@ -755,6 +662,14 @@ static int intel_gtt_init(void) intel_private.base.gtt_mappable_entries = intel_gtt_mappable_entries(); intel_private.base.gtt_total_entries = intel_gtt_total_entries(); + /* save the PGETBL reg for resume */ + intel_private.PGETBL_save = + readl(intel_private.registers+I810_PGETBL_CTL) + & ~I810_PGETBL_ENABLED; + /* we only ever restore the register when enabling the PGTBL... */ + if (HAS_PGTBL_EN) + intel_private.PGETBL_save |= I810_PGETBL_ENABLED; + dev_info(&intel_private.bridge_dev->dev, "detected gtt size: %dK total, %dK mappable\n", intel_private.base.gtt_total_entries * 4, @@ -772,14 +687,9 @@ static int intel_gtt_init(void) global_cache_flush(); /* FIXME: ? */ - /* we have to call this as early as possible after the MMIO base address is known */ - intel_private.base.gtt_stolen_entries = intel_gtt_stolen_entries(); - if (intel_private.base.gtt_stolen_entries == 0) { - intel_private.driver->cleanup(); - iounmap(intel_private.registers); - iounmap(intel_private.gtt); - return -ENOMEM; - } + intel_private.base.stolen_size = intel_gtt_stolen_size(); + + intel_private.base.needs_dmar = USE_PCI_DMA_API && INTEL_GTT_GEN > 2; ret = intel_gtt_setup_scratch_page(); if (ret != 0) { @@ -812,8 +722,10 @@ static int intel_fake_agp_fetch_size(void) static void i830_cleanup(void) { - kunmap(intel_private.i8xx_page); - intel_private.i8xx_flush_page = NULL; + if (intel_private.i8xx_flush_page) { + kunmap(intel_private.i8xx_flush_page); + intel_private.i8xx_flush_page = NULL; + } __free_page(intel_private.i8xx_page); intel_private.i8xx_page = NULL; @@ -860,25 +772,19 @@ static void i830_write_entry(dma_addr_t addr, unsigned int entry, unsigned int flags) { u32 pte_flags = I810_PTE_VALID; - - switch (flags) { - case AGP_DCACHE_MEMORY: - pte_flags |= I810_PTE_LOCAL; - break; - case AGP_USER_CACHED_MEMORY: + + if (flags == AGP_USER_CACHED_MEMORY) pte_flags |= I830_PTE_SYSTEM_CACHED; - break; - } writel(addr | pte_flags, intel_private.gtt + entry); } -static void intel_enable_gtt(void) +static bool intel_enable_gtt(void) { u32 gma_addr; - u16 gmch_ctrl; + u8 __iomem *reg; - if (INTEL_GTT_GEN == 2) + if (INTEL_GTT_GEN <= 2) pci_read_config_dword(intel_private.pcidev, I810_GMADDR, &gma_addr); else @@ -887,13 +793,47 @@ static void intel_enable_gtt(void) intel_private.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); - pci_read_config_word(intel_private.bridge_dev, I830_GMCH_CTRL, &gmch_ctrl); - gmch_ctrl |= I830_GMCH_ENABLED; - pci_write_config_word(intel_private.bridge_dev, I830_GMCH_CTRL, gmch_ctrl); + if (INTEL_GTT_GEN >= 6) + return true; - writel(intel_private.pte_bus_addr|I810_PGETBL_ENABLED, - intel_private.registers+I810_PGETBL_CTL); - readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ + if (INTEL_GTT_GEN == 2) { + u16 gmch_ctrl; + + pci_read_config_word(intel_private.bridge_dev, + I830_GMCH_CTRL, &gmch_ctrl); + gmch_ctrl |= I830_GMCH_ENABLED; + pci_write_config_word(intel_private.bridge_dev, + I830_GMCH_CTRL, gmch_ctrl); + + pci_read_config_word(intel_private.bridge_dev, + I830_GMCH_CTRL, &gmch_ctrl); + if ((gmch_ctrl & I830_GMCH_ENABLED) == 0) { + dev_err(&intel_private.pcidev->dev, + "failed to enable the GTT: GMCH_CTRL=%x\n", + gmch_ctrl); + return false; + } + } + + /* On the resume path we may be adjusting the PGTBL value, so + * be paranoid and flush all chipset write buffers... + */ + if (INTEL_GTT_GEN >= 3) + writel(0, intel_private.registers+GFX_FLSH_CNTL); + + reg = intel_private.registers+I810_PGETBL_CTL; + writel(intel_private.PGETBL_save, reg); + if (HAS_PGTBL_EN && (readl(reg) & I810_PGETBL_ENABLED) == 0) { + dev_err(&intel_private.pcidev->dev, + "failed to enable the GTT: PGETBL=%x [expected %x]\n", + readl(reg), intel_private.PGETBL_save); + return false; + } + + if (INTEL_GTT_GEN >= 3) + writel(0, intel_private.registers+GFX_FLSH_CNTL); + + return true; } static int i830_setup(void) @@ -908,8 +848,6 @@ static int i830_setup(void) return -ENOMEM; intel_private.gtt_bus_addr = reg_addr + I810_PTE_BASE; - intel_private.pte_bus_addr = - readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; intel_i830_setup_flush(); @@ -932,21 +870,12 @@ static int intel_fake_agp_free_gatt_table(struct agp_bridge_data *bridge) static int intel_fake_agp_configure(void) { - int i; - - intel_enable_gtt(); + if (!intel_enable_gtt()) + return -EIO; + intel_private.clear_fake_agp = true; agp_bridge->gart_bus_addr = intel_private.gma_bus_addr; - for (i = intel_private.base.gtt_stolen_entries; - i < intel_private.base.gtt_total_entries; i++) { - intel_private.driver->write_entry(intel_private.scratch_page_dma, - i, 0); - } - readl(intel_private.gtt+i-1); /* PCI Posting. */ - - global_cache_flush(); - return 0; } @@ -963,10 +892,10 @@ static bool i830_check_flags(unsigned int flags) return false; } -static void intel_gtt_insert_sg_entries(struct scatterlist *sg_list, - unsigned int sg_len, - unsigned int pg_start, - unsigned int flags) +void intel_gtt_insert_sg_entries(struct scatterlist *sg_list, + unsigned int sg_len, + unsigned int pg_start, + unsigned int flags) { struct scatterlist *sg; unsigned int len, m; @@ -987,27 +916,41 @@ static void intel_gtt_insert_sg_entries(struct scatterlist *sg_list, } readl(intel_private.gtt+j-1); } +EXPORT_SYMBOL(intel_gtt_insert_sg_entries); + +void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries, + struct page **pages, unsigned int flags) +{ + int i, j; + + for (i = 0, j = first_entry; i < num_entries; i++, j++) { + dma_addr_t addr = page_to_phys(pages[i]); + intel_private.driver->write_entry(addr, + j, flags); + } + readl(intel_private.gtt+j-1); +} +EXPORT_SYMBOL(intel_gtt_insert_pages); static int intel_fake_agp_insert_entries(struct agp_memory *mem, off_t pg_start, int type) { - int i, j; int ret = -EINVAL; - if (mem->page_count == 0) - goto out; + if (intel_private.clear_fake_agp) { + int start = intel_private.base.stolen_size / PAGE_SIZE; + int end = intel_private.base.gtt_mappable_entries; + intel_gtt_clear_range(start, end - start); + intel_private.clear_fake_agp = false; + } - if (pg_start < intel_private.base.gtt_stolen_entries) { - dev_printk(KERN_DEBUG, &intel_private.pcidev->dev, - "pg_start == 0x%.8lx, gtt_stolen_entries == 0x%.8x\n", - pg_start, intel_private.base.gtt_stolen_entries); + if (INTEL_GTT_GEN == 1 && type == AGP_DCACHE_MEMORY) + return i810_insert_dcache_entries(mem, pg_start, type); - dev_info(&intel_private.pcidev->dev, - "trying to insert into local/stolen memory\n"); - goto out_err; - } + if (mem->page_count == 0) + goto out; - if ((pg_start + mem->page_count) > intel_private.base.gtt_total_entries) + if (pg_start + mem->page_count > intel_private.base.gtt_total_entries) goto out_err; if (type != mem->type) @@ -1019,21 +962,17 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, if (!mem->is_flushed) global_cache_flush(); - if (USE_PCI_DMA_API && INTEL_GTT_GEN > 2) { - ret = intel_agp_map_memory(mem); + if (intel_private.base.needs_dmar) { + ret = intel_gtt_map_memory(mem->pages, mem->page_count, + &mem->sg_list, &mem->num_sg); if (ret != 0) return ret; intel_gtt_insert_sg_entries(mem->sg_list, mem->num_sg, pg_start, type); - } else { - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - dma_addr_t addr = page_to_phys(mem->pages[i]); - intel_private.driver->write_entry(addr, - j, type); - } - readl(intel_private.gtt+j-1); - } + } else + intel_gtt_insert_pages(pg_start, mem->page_count, mem->pages, + type); out: ret = 0; @@ -1042,40 +981,54 @@ out_err: return ret; } +void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries) +{ + unsigned int i; + + for (i = first_entry; i < (first_entry + num_entries); i++) { + intel_private.driver->write_entry(intel_private.scratch_page_dma, + i, 0); + } + readl(intel_private.gtt+i-1); +} +EXPORT_SYMBOL(intel_gtt_clear_range); + static int intel_fake_agp_remove_entries(struct agp_memory *mem, off_t pg_start, int type) { - int i; - if (mem->page_count == 0) return 0; - if (pg_start < intel_private.base.gtt_stolen_entries) { - dev_info(&intel_private.pcidev->dev, - "trying to disable local/stolen memory\n"); - return -EINVAL; - } - - if (USE_PCI_DMA_API && INTEL_GTT_GEN > 2) - intel_agp_unmap_memory(mem); + intel_gtt_clear_range(pg_start, mem->page_count); - for (i = pg_start; i < (mem->page_count + pg_start); i++) { - intel_private.driver->write_entry(intel_private.scratch_page_dma, - i, 0); + if (intel_private.base.needs_dmar) { + intel_gtt_unmap_memory(mem->sg_list, mem->num_sg); + mem->sg_list = NULL; + mem->num_sg = 0; } - readl(intel_private.gtt+i-1); return 0; } -static void intel_fake_agp_chipset_flush(struct agp_bridge_data *bridge) -{ - intel_private.driver->chipset_flush(); -} - static struct agp_memory *intel_fake_agp_alloc_by_type(size_t pg_count, int type) { + struct agp_memory *new; + + if (type == AGP_DCACHE_MEMORY && INTEL_GTT_GEN == 1) { + if (pg_count != intel_private.num_dcache_entries) + return NULL; + + new = agp_create_memory(1); + if (new == NULL) + return NULL; + + new->type = AGP_DCACHE_MEMORY; + new->page_count = pg_count; + new->num_scratch_pages = 0; + agp_free_page_array(new); + return new; + } if (type == AGP_PHYS_MEMORY) return alloc_agpphysmem_i8xx(pg_count, type); /* always return NULL for other allocation types for now */ @@ -1190,12 +1143,19 @@ static void i9xx_chipset_flush(void) writel(1, intel_private.i9xx_flush_page); } -static void i965_write_entry(dma_addr_t addr, unsigned int entry, +static void i965_write_entry(dma_addr_t addr, + unsigned int entry, unsigned int flags) { + u32 pte_flags; + + pte_flags = I810_PTE_VALID; + if (flags == AGP_USER_CACHED_MEMORY) + pte_flags |= I830_PTE_SYSTEM_CACHED; + /* Shift high bits down */ addr |= (addr >> 28) & 0xf0; - writel(addr | I810_PTE_VALID, intel_private.gtt + entry); + writel(addr | pte_flags, intel_private.gtt + entry); } static bool gen6_check_flags(unsigned int flags) @@ -1210,14 +1170,14 @@ static void gen6_write_entry(dma_addr_t addr, unsigned int entry, 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; } @@ -1265,40 +1225,11 @@ static int i9xx_setup(void) intel_private.gtt_bus_addr = reg_addr + gtt_offset; } - intel_private.pte_bus_addr = - readl(intel_private.registers+I810_PGETBL_CTL) & 0xfffff000; - intel_i9xx_setup_flush(); return 0; } -static const struct agp_bridge_driver intel_810_driver = { - .owner = THIS_MODULE, - .aperture_sizes = intel_i810_sizes, - .size_type = FIXED_APER_SIZE, - .num_aperture_sizes = 2, - .needs_scratch_page = true, - .configure = intel_i810_configure, - .fetch_size = intel_i810_fetch_size, - .cleanup = intel_i810_cleanup, - .mask_memory = intel_i810_mask_memory, - .masks = intel_i810_masks, - .agp_enable = intel_fake_agp_enable, - .cache_flush = global_cache_flush, - .create_gatt_table = agp_generic_create_gatt_table, - .free_gatt_table = agp_generic_free_gatt_table, - .insert_memory = intel_i810_insert_entries, - .remove_memory = intel_i810_remove_entries, - .alloc_by_type = intel_i810_alloc_by_type, - .free_by_type = intel_i810_free_by_type, - .agp_alloc_page = agp_generic_alloc_page, - .agp_alloc_pages = agp_generic_alloc_pages, - .agp_destroy_page = agp_generic_destroy_page, - .agp_destroy_pages = agp_generic_destroy_pages, - .agp_type_to_mask_type = agp_generic_type_to_mask_type, -}; - static const struct agp_bridge_driver intel_fake_agp_driver = { .owner = THIS_MODULE, .size_type = FIXED_APER_SIZE, @@ -1319,15 +1250,20 @@ static const struct agp_bridge_driver intel_fake_agp_driver = { .agp_alloc_pages = agp_generic_alloc_pages, .agp_destroy_page = agp_generic_destroy_page, .agp_destroy_pages = agp_generic_destroy_pages, - .chipset_flush = intel_fake_agp_chipset_flush, }; static const struct intel_gtt_driver i81x_gtt_driver = { .gen = 1, + .has_pgtbl_enable = 1, .dma_mask_size = 32, + .setup = i810_setup, + .cleanup = i810_cleanup, + .check_flags = i830_check_flags, + .write_entry = i810_write_entry, }; static const struct intel_gtt_driver i8xx_gtt_driver = { .gen = 2, + .has_pgtbl_enable = 1, .setup = i830_setup, .cleanup = i830_cleanup, .write_entry = i830_write_entry, @@ -1337,10 +1273,11 @@ static const struct intel_gtt_driver i8xx_gtt_driver = { }; static const struct intel_gtt_driver i915_gtt_driver = { .gen = 3, + .has_pgtbl_enable = 1, .setup = i9xx_setup, .cleanup = i9xx_cleanup, /* i945 is the last gpu to need phys mem (for overlay and cursors). */ - .write_entry = i830_write_entry, + .write_entry = i830_write_entry, .dma_mask_size = 32, .check_flags = i830_check_flags, .chipset_flush = i9xx_chipset_flush, @@ -1367,6 +1304,7 @@ static const struct intel_gtt_driver pineview_gtt_driver = { }; static const struct intel_gtt_driver i965_gtt_driver = { .gen = 4, + .has_pgtbl_enable = 1, .setup = i9xx_setup, .cleanup = i9xx_cleanup, .write_entry = i965_write_entry, @@ -1410,93 +1348,92 @@ static const struct intel_gtt_driver sandybridge_gtt_driver = { static const struct intel_gtt_driver_description { unsigned int gmch_chip_id; char *name; - const struct agp_bridge_driver *gmch_driver; const struct intel_gtt_driver *gtt_driver; } intel_gtt_chipsets[] = { - { PCI_DEVICE_ID_INTEL_82810_IG1, "i810", &intel_810_driver, + { PCI_DEVICE_ID_INTEL_82810_IG1, "i810", &i81x_gtt_driver}, - { PCI_DEVICE_ID_INTEL_82810_IG3, "i810", &intel_810_driver, + { PCI_DEVICE_ID_INTEL_82810_IG3, "i810", &i81x_gtt_driver}, - { PCI_DEVICE_ID_INTEL_82810E_IG, "i810", &intel_810_driver, + { PCI_DEVICE_ID_INTEL_82810E_IG, "i810", &i81x_gtt_driver}, - { PCI_DEVICE_ID_INTEL_82815_CGC, "i815", &intel_810_driver, + { PCI_DEVICE_ID_INTEL_82815_CGC, "i815", &i81x_gtt_driver}, { PCI_DEVICE_ID_INTEL_82830_CGC, "830M", - &intel_fake_agp_driver, &i8xx_gtt_driver}, - { PCI_DEVICE_ID_INTEL_82845G_IG, "830M", - &intel_fake_agp_driver, &i8xx_gtt_driver}, + &i8xx_gtt_driver}, + { PCI_DEVICE_ID_INTEL_82845G_IG, "845G", + &i8xx_gtt_driver}, { PCI_DEVICE_ID_INTEL_82854_IG, "854", - &intel_fake_agp_driver, &i8xx_gtt_driver}, + &i8xx_gtt_driver}, { PCI_DEVICE_ID_INTEL_82855GM_IG, "855GM", - &intel_fake_agp_driver, &i8xx_gtt_driver}, + &i8xx_gtt_driver}, { PCI_DEVICE_ID_INTEL_82865_IG, "865", - &intel_fake_agp_driver, &i8xx_gtt_driver}, + &i8xx_gtt_driver}, { PCI_DEVICE_ID_INTEL_E7221_IG, "E7221 (i915)", - &intel_fake_agp_driver, &i915_gtt_driver }, + &i915_gtt_driver }, { PCI_DEVICE_ID_INTEL_82915G_IG, "915G", - &intel_fake_agp_driver, &i915_gtt_driver }, + &i915_gtt_driver }, { PCI_DEVICE_ID_INTEL_82915GM_IG, "915GM", - &intel_fake_agp_driver, &i915_gtt_driver }, + &i915_gtt_driver }, { PCI_DEVICE_ID_INTEL_82945G_IG, "945G", - &intel_fake_agp_driver, &i915_gtt_driver }, + &i915_gtt_driver }, { PCI_DEVICE_ID_INTEL_82945GM_IG, "945GM", - &intel_fake_agp_driver, &i915_gtt_driver }, + &i915_gtt_driver }, { PCI_DEVICE_ID_INTEL_82945GME_IG, "945GME", - &intel_fake_agp_driver, &i915_gtt_driver }, + &i915_gtt_driver }, { PCI_DEVICE_ID_INTEL_82946GZ_IG, "946GZ", - &intel_fake_agp_driver, &i965_gtt_driver }, + &i965_gtt_driver }, { PCI_DEVICE_ID_INTEL_82G35_IG, "G35", - &intel_fake_agp_driver, &i965_gtt_driver }, + &i965_gtt_driver }, { PCI_DEVICE_ID_INTEL_82965Q_IG, "965Q", - &intel_fake_agp_driver, &i965_gtt_driver }, + &i965_gtt_driver }, { PCI_DEVICE_ID_INTEL_82965G_IG, "965G", - &intel_fake_agp_driver, &i965_gtt_driver }, + &i965_gtt_driver }, { PCI_DEVICE_ID_INTEL_82965GM_IG, "965GM", - &intel_fake_agp_driver, &i965_gtt_driver }, + &i965_gtt_driver }, { PCI_DEVICE_ID_INTEL_82965GME_IG, "965GME/GLE", - &intel_fake_agp_driver, &i965_gtt_driver }, + &i965_gtt_driver }, { PCI_DEVICE_ID_INTEL_G33_IG, "G33", - &intel_fake_agp_driver, &g33_gtt_driver }, + &g33_gtt_driver }, { PCI_DEVICE_ID_INTEL_Q35_IG, "Q35", - &intel_fake_agp_driver, &g33_gtt_driver }, + &g33_gtt_driver }, { PCI_DEVICE_ID_INTEL_Q33_IG, "Q33", - &intel_fake_agp_driver, &g33_gtt_driver }, + &g33_gtt_driver }, { PCI_DEVICE_ID_INTEL_PINEVIEW_M_IG, "GMA3150", - &intel_fake_agp_driver, &pineview_gtt_driver }, + &pineview_gtt_driver }, { PCI_DEVICE_ID_INTEL_PINEVIEW_IG, "GMA3150", - &intel_fake_agp_driver, &pineview_gtt_driver }, + &pineview_gtt_driver }, { PCI_DEVICE_ID_INTEL_GM45_IG, "GM45", - &intel_fake_agp_driver, &g4x_gtt_driver }, + &g4x_gtt_driver }, { PCI_DEVICE_ID_INTEL_EAGLELAKE_IG, "Eaglelake", - &intel_fake_agp_driver, &g4x_gtt_driver }, + &g4x_gtt_driver }, { PCI_DEVICE_ID_INTEL_Q45_IG, "Q45/Q43", - &intel_fake_agp_driver, &g4x_gtt_driver }, + &g4x_gtt_driver }, { PCI_DEVICE_ID_INTEL_G45_IG, "G45/G43", - &intel_fake_agp_driver, &g4x_gtt_driver }, + &g4x_gtt_driver }, { PCI_DEVICE_ID_INTEL_B43_IG, "B43", - &intel_fake_agp_driver, &g4x_gtt_driver }, + &g4x_gtt_driver }, { PCI_DEVICE_ID_INTEL_B43_1_IG, "B43", - &intel_fake_agp_driver, &g4x_gtt_driver }, + &g4x_gtt_driver }, { PCI_DEVICE_ID_INTEL_G41_IG, "G41", - &intel_fake_agp_driver, &g4x_gtt_driver }, + &g4x_gtt_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_D_IG, - "HD Graphics", &intel_fake_agp_driver, &ironlake_gtt_driver }, + "HD Graphics", &ironlake_gtt_driver }, { PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG, - "HD Graphics", &intel_fake_agp_driver, &ironlake_gtt_driver }, + "HD Graphics", &ironlake_gtt_driver }, { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT1_IG, - "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + "Sandybridge", &sandybridge_gtt_driver }, { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_IG, - "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + "Sandybridge", &sandybridge_gtt_driver }, { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_GT2_PLUS_IG, - "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + "Sandybridge", &sandybridge_gtt_driver }, { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT1_IG, - "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + "Sandybridge", &sandybridge_gtt_driver }, { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_IG, - "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + "Sandybridge", &sandybridge_gtt_driver }, { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_M_GT2_PLUS_IG, - "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + "Sandybridge", &sandybridge_gtt_driver }, { PCI_DEVICE_ID_INTEL_SANDYBRIDGE_S_IG, - "Sandybridge", &intel_fake_agp_driver, &sandybridge_gtt_driver }, + "Sandybridge", &sandybridge_gtt_driver }, { 0, NULL, NULL } }; @@ -1521,21 +1458,20 @@ int intel_gmch_probe(struct pci_dev *pdev, struct agp_bridge_data *bridge) { int i, mask; - bridge->driver = NULL; + intel_private.driver = NULL; for (i = 0; intel_gtt_chipsets[i].name != NULL; i++) { if (find_gmch(intel_gtt_chipsets[i].gmch_chip_id)) { - bridge->driver = - intel_gtt_chipsets[i].gmch_driver; - intel_private.driver = + intel_private.driver = intel_gtt_chipsets[i].gtt_driver; break; } } - if (!bridge->driver) + if (!intel_private.driver) return 0; + bridge->driver = &intel_fake_agp_driver; bridge->dev_private_data = &intel_private; bridge->dev = pdev; @@ -1551,8 +1487,8 @@ int intel_gmch_probe(struct pci_dev *pdev, pci_set_consistent_dma_mask(intel_private.pcidev, DMA_BIT_MASK(mask)); - if (bridge->driver == &intel_810_driver) - return 1; + /*if (bridge->driver == &intel_810_driver) + return 1;*/ if (intel_gtt_init() != 0) return 0; @@ -1561,12 +1497,19 @@ int intel_gmch_probe(struct pci_dev *pdev, } EXPORT_SYMBOL(intel_gmch_probe); -struct intel_gtt *intel_gtt_get(void) +const struct intel_gtt *intel_gtt_get(void) { return &intel_private.base; } EXPORT_SYMBOL(intel_gtt_get); +void intel_gtt_chipset_flush(void) +{ + if (intel_private.driver->chipset_flush) + intel_private.driver->chipset_flush(); +} +EXPORT_SYMBOL(intel_gtt_chipset_flush); + void intel_gmch_remove(struct pci_dev *pdev) { if (intel_private.pcidev) diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index b0a70461a12c..6ee3348bc3e4 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -81,7 +81,6 @@ static char *serial_version = "4.30"; #include <linux/mm.h> #include <linux/seq_file.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/init.h> #include <linux/bitops.h> #include <linux/platform_device.h> @@ -1299,7 +1298,6 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, { struct async_struct * info = tty->driver_data; struct async_icount cprev, cnow; /* kernel counter temps */ - struct serial_icounter_struct icount; void __user *argp = (void __user *)arg; unsigned long flags; diff --git a/drivers/char/bfin_jtag_comm.c b/drivers/char/bfin_jtag_comm.c index e397df3ad98e..16402445f2b2 100644 --- a/drivers/char/bfin_jtag_comm.c +++ b/drivers/char/bfin_jtag_comm.c @@ -183,16 +183,16 @@ bfin_jc_circ_write(const unsigned char *buf, int count) } #ifndef CONFIG_BFIN_JTAG_COMM_CONSOLE -# define acquire_console_sem() -# define release_console_sem() +# define console_lock() +# define console_unlock() #endif static int bfin_jc_write(struct tty_struct *tty, const unsigned char *buf, int count) { int i; - acquire_console_sem(); + console_lock(); i = bfin_jc_circ_write(buf, count); - release_console_sem(); + console_unlock(); wake_up_process(bfin_jc_kthread); return i; } diff --git a/drivers/char/briq_panel.c b/drivers/char/briq_panel.c index f6718f05dad4..095ab90535ce 100644 --- a/drivers/char/briq_panel.c +++ b/drivers/char/briq_panel.c @@ -6,7 +6,6 @@ #include <linux/module.h> -#include <linux/smp_lock.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/tty.h> diff --git a/drivers/char/consolemap.c b/drivers/char/consolemap.c deleted file mode 100644 index 45d3e80156d4..000000000000 --- a/drivers/char/consolemap.c +++ /dev/null @@ -1,745 +0,0 @@ -/* - * consolemap.c - * - * Mapping from internal code (such as Latin-1 or Unicode or IBM PC code) - * to font positions. - * - * aeb, 950210 - * - * Support for multiple unimaps by Jakub Jelinek <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); diff --git a/drivers/char/cp437.uni b/drivers/char/cp437.uni deleted file mode 100644 index bc6163484f62..000000000000 --- a/drivers/char/cp437.uni +++ /dev/null @@ -1,291 +0,0 @@ -# -# Unicode table for IBM Codepage 437. Note that there are many more -# substitutions that could be conceived (for example, thick-line -# graphs probably should be replaced with double-line ones, accented -# Latin characters should replaced with their nonaccented versions, -# and some upper case Greek characters could be replaced by Latin), however, -# I have limited myself to the Unicodes used by the kernel ISO 8859-1, -# DEC VT, and IBM CP 437 tables. -# -# -------------------------------- -# -# Basic IBM dingbats, some of which will never have a purpose clear -# to mankind -# -0x00 U+0000 -0x01 U+263a -0x02 U+263b -0x03 U+2665 -0x04 U+2666 U+25c6 -0x05 U+2663 -0x06 U+2660 -0x07 U+2022 -0x08 U+25d8 -0x09 U+25cb -0x0a U+25d9 -0x0b U+2642 -0x0c U+2640 -0x0d U+266a -0x0e U+266b -0x0f U+263c U+00a4 -0x10 U+25b6 U+25ba -0x11 U+25c0 U+25c4 -0x12 U+2195 -0x13 U+203c -0x14 U+00b6 -0x15 U+00a7 -0x16 U+25ac -0x17 U+21a8 -0x18 U+2191 -0x19 U+2193 -0x1a U+2192 -0x1b U+2190 -0x1c U+221f -0x1d U+2194 -0x1e U+25b2 -0x1f U+25bc -# -# The ASCII range is identity-mapped, but some of the characters also -# have to act as substitutes, especially the upper-case characters. -# -0x20 U+0020 -0x21 U+0021 -0x22 U+0022 U+00a8 -0x23 U+0023 -0x24 U+0024 -0x25 U+0025 -0x26 U+0026 -0x27 U+0027 U+00b4 -0x28 U+0028 -0x29 U+0029 -0x2a U+002a -0x2b U+002b -0x2c U+002c U+00b8 -0x2d U+002d U+00ad -0x2e U+002e -0x2f U+002f -0x30 U+0030 -0x31 U+0031 -0x32 U+0032 -0x33 U+0033 -0x34 U+0034 -0x35 U+0035 -0x36 U+0036 -0x37 U+0037 -0x38 U+0038 -0x39 U+0039 -0x3a U+003a -0x3b U+003b -0x3c U+003c -0x3d U+003d -0x3e U+003e -0x3f U+003f -0x40 U+0040 -0x41 U+0041 U+00c0 U+00c1 U+00c2 U+00c3 -0x42 U+0042 -0x43 U+0043 U+00a9 -0x44 U+0044 U+00d0 -0x45 U+0045 U+00c8 U+00ca U+00cb -0x46 U+0046 -0x47 U+0047 -0x48 U+0048 -0x49 U+0049 U+00cc U+00cd U+00ce U+00cf -0x4a U+004a -0x4b U+004b U+212a -0x4c U+004c -0x4d U+004d -0x4e U+004e -0x4f U+004f U+00d2 U+00d3 U+00d4 U+00d5 -0x50 U+0050 -0x51 U+0051 -0x52 U+0052 U+00ae -0x53 U+0053 -0x54 U+0054 -0x55 U+0055 U+00d9 U+00da U+00db -0x56 U+0056 -0x57 U+0057 -0x58 U+0058 -0x59 U+0059 U+00dd -0x5a U+005a -0x5b U+005b -0x5c U+005c -0x5d U+005d -0x5e U+005e -0x5f U+005f U+23bd U+f804 -0x60 U+0060 -0x61 U+0061 U+00e3 -0x62 U+0062 -0x63 U+0063 -0x64 U+0064 -0x65 U+0065 -0x66 U+0066 -0x67 U+0067 -0x68 U+0068 -0x69 U+0069 -0x6a U+006a -0x6b U+006b -0x6c U+006c -0x6d U+006d -0x6e U+006e -0x6f U+006f U+00f5 -0x70 U+0070 -0x71 U+0071 -0x72 U+0072 -0x73 U+0073 -0x74 U+0074 -0x75 U+0075 -0x76 U+0076 -0x77 U+0077 -0x78 U+0078 U+00d7 -0x79 U+0079 U+00fd -0x7a U+007a -0x7b U+007b -0x7c U+007c U+00a6 -0x7d U+007d -0x7e U+007e -# -# Okay, what on Earth is this one supposed to be used for? -# -0x7f U+2302 -# -# Non-English characters, mostly lower case letters... -# -0x80 U+00c7 -0x81 U+00fc -0x82 U+00e9 -0x83 U+00e2 -0x84 U+00e4 -0x85 U+00e0 -0x86 U+00e5 -0x87 U+00e7 -0x88 U+00ea -0x89 U+00eb -0x8a U+00e8 -0x8b U+00ef -0x8c U+00ee -0x8d U+00ec -0x8e U+00c4 -0x8f U+00c5 U+212b -0x90 U+00c9 -0x91 U+00e6 -0x92 U+00c6 -0x93 U+00f4 -0x94 U+00f6 -0x95 U+00f2 -0x96 U+00fb -0x97 U+00f9 -0x98 U+00ff -0x99 U+00d6 -0x9a U+00dc -0x9b U+00a2 -0x9c U+00a3 -0x9d U+00a5 -0x9e U+20a7 -0x9f U+0192 -0xa0 U+00e1 -0xa1 U+00ed -0xa2 U+00f3 -0xa3 U+00fa -0xa4 U+00f1 -0xa5 U+00d1 -0xa6 U+00aa -0xa7 U+00ba -0xa8 U+00bf -0xa9 U+2310 -0xaa U+00ac -0xab U+00bd -0xac U+00bc -0xad U+00a1 -0xae U+00ab -0xaf U+00bb -# -# Block graphics -# -0xb0 U+2591 -0xb1 U+2592 -0xb2 U+2593 -0xb3 U+2502 -0xb4 U+2524 -0xb5 U+2561 -0xb6 U+2562 -0xb7 U+2556 -0xb8 U+2555 -0xb9 U+2563 -0xba U+2551 -0xbb U+2557 -0xbc U+255d -0xbd U+255c -0xbe U+255b -0xbf U+2510 -0xc0 U+2514 -0xc1 U+2534 -0xc2 U+252c -0xc3 U+251c -0xc4 U+2500 -0xc5 U+253c -0xc6 U+255e -0xc7 U+255f -0xc8 U+255a -0xc9 U+2554 -0xca U+2569 -0xcb U+2566 -0xcc U+2560 -0xcd U+2550 -0xce U+256c -0xcf U+2567 -0xd0 U+2568 -0xd1 U+2564 -0xd2 U+2565 -0xd3 U+2559 -0xd4 U+2558 -0xd5 U+2552 -0xd6 U+2553 -0xd7 U+256b -0xd8 U+256a -0xd9 U+2518 -0xda U+250c -0xdb U+2588 -0xdc U+2584 -0xdd U+258c -0xde U+2590 -0xdf U+2580 -# -# Greek letters and mathematical symbols -# -0xe0 U+03b1 -0xe1 U+03b2 U+00df -0xe2 U+0393 -0xe3 U+03c0 -0xe4 U+03a3 -0xe5 U+03c3 -0xe6 U+00b5 U+03bc -0xe7 U+03c4 -0xe8 U+03a6 U+00d8 -0xe9 U+0398 -0xea U+03a9 U+2126 -0xeb U+03b4 U+00f0 -0xec U+221e -0xed U+03c6 U+00f8 -0xee U+03b5 U+2208 -0xef U+2229 -0xf0 U+2261 -0xf1 U+00b1 -0xf2 U+2265 -0xf3 U+2264 -0xf4 U+2320 -0xf5 U+2321 -0xf6 U+00f7 -0xf7 U+2248 -0xf8 U+00b0 -0xf9 U+2219 -0xfa U+00b7 -0xfb U+221a -0xfc U+207f -0xfd U+00b2 -# -# Square bullet, non-spacing blank -# Mapping U+fffd to the square bullet means it is the substitution -# character -# -0xfe U+25a0 U+fffd -0xff U+00a0 diff --git a/drivers/char/cs5535_gpio.c b/drivers/char/cs5535_gpio.c deleted file mode 100644 index 0cf1e5fad9ab..000000000000 --- a/drivers/char/cs5535_gpio.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - * AMD CS5535/CS5536 GPIO driver. - * Allows a user space process to play with the GPIO pins. - * - * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the smems of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - */ - -#include <linux/fs.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/cdev.h> -#include <linux/ioport.h> -#include <linux/pci.h> - -#include <asm/uaccess.h> -#include <asm/io.h> - - -#define NAME "cs5535_gpio" - -MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>"); -MODULE_DESCRIPTION("AMD CS5535/CS5536 GPIO Pin Driver"); -MODULE_LICENSE("GPL"); - -static int major; -module_param(major, int, 0); -MODULE_PARM_DESC(major, "Major device number"); - -static ulong mask; -module_param(mask, ulong, 0); -MODULE_PARM_DESC(mask, "GPIO channel mask"); - -#define MSR_LBAR_GPIO 0x5140000C - -static u32 gpio_base; - -static struct pci_device_id divil_pci[] = { - { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, - { } /* NULL entry */ -}; -MODULE_DEVICE_TABLE(pci, divil_pci); - -static struct cdev cs5535_gpio_cdev; - -/* reserve 32 entries even though some aren't usable */ -#define CS5535_GPIO_COUNT 32 - -/* IO block size */ -#define CS5535_GPIO_SIZE 256 - -struct gpio_regmap { - u32 rd_offset; - u32 wr_offset; - char on; - char off; -}; -static struct gpio_regmap rm[] = -{ - { 0x30, 0x00, '1', '0' }, /* GPIOx_READ_BACK / GPIOx_OUT_VAL */ - { 0x20, 0x20, 'I', 'i' }, /* GPIOx_IN_EN */ - { 0x04, 0x04, 'O', 'o' }, /* GPIOx_OUT_EN */ - { 0x08, 0x08, 't', 'T' }, /* GPIOx_OUT_OD_EN */ - { 0x18, 0x18, 'P', 'p' }, /* GPIOx_OUT_PU_EN */ - { 0x1c, 0x1c, 'D', 'd' }, /* GPIOx_OUT_PD_EN */ -}; - - -/** - * Gets the register offset for the GPIO bank. - * Low (0-15) starts at 0x00, high (16-31) starts at 0x80 - */ -static inline u32 cs5535_lowhigh_base(int reg) -{ - return (reg & 0x10) << 3; -} - -static ssize_t cs5535_gpio_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - u32 m = iminor(file->f_path.dentry->d_inode); - int i, j; - u32 base = gpio_base + cs5535_lowhigh_base(m); - u32 m0, m1; - char c; - - /** - * Creates the mask for atomic bit programming. - * The high 16 bits and the low 16 bits are used to set the mask. - * For example, GPIO 15 maps to 31,15: 0,1 => On; 1,0=> Off - */ - m1 = 1 << (m & 0x0F); - m0 = m1 << 16; - - for (i = 0; i < len; ++i) { - if (get_user(c, data+i)) - return -EFAULT; - - for (j = 0; j < ARRAY_SIZE(rm); j++) { - if (c == rm[j].on) { - outl(m1, base + rm[j].wr_offset); - /* If enabling output, turn off AUX 1 and AUX 2 */ - if (c == 'O') { - outl(m0, base + 0x10); - outl(m0, base + 0x14); - } - break; - } else if (c == rm[j].off) { - outl(m0, base + rm[j].wr_offset); - break; - } - } - } - *ppos = 0; - return len; -} - -static ssize_t cs5535_gpio_read(struct file *file, char __user *buf, - size_t len, loff_t *ppos) -{ - u32 m = iminor(file->f_path.dentry->d_inode); - u32 base = gpio_base + cs5535_lowhigh_base(m); - int rd_bit = 1 << (m & 0x0f); - int i; - char ch; - ssize_t count = 0; - - if (*ppos >= ARRAY_SIZE(rm)) - return 0; - - for (i = *ppos; (i < (*ppos + len)) && (i < ARRAY_SIZE(rm)); i++) { - ch = (inl(base + rm[i].rd_offset) & rd_bit) ? - rm[i].on : rm[i].off; - - if (put_user(ch, buf+count)) - return -EFAULT; - - count++; - } - - /* add a line-feed if there is room */ - if ((i == ARRAY_SIZE(rm)) && (count < len)) { - put_user('\n', buf + count); - count++; - } - - *ppos += count; - return count; -} - -static int cs5535_gpio_open(struct inode *inode, struct file *file) -{ - u32 m = iminor(inode); - - /* the mask says which pins are usable by this driver */ - if ((mask & (1 << m)) == 0) - return -EINVAL; - - return nonseekable_open(inode, file); -} - -static const struct file_operations cs5535_gpio_fops = { - .owner = THIS_MODULE, - .write = cs5535_gpio_write, - .read = cs5535_gpio_read, - .open = cs5535_gpio_open, - .llseek = no_llseek, -}; - -static int __init cs5535_gpio_init(void) -{ - dev_t dev_id; - u32 low, hi; - int retval; - - if (pci_dev_present(divil_pci) == 0) { - printk(KERN_WARNING NAME ": DIVIL not found\n"); - return -ENODEV; - } - - /* Grab the GPIO I/O range */ - rdmsr(MSR_LBAR_GPIO, low, hi); - - /* Check the mask and whether GPIO is enabled (sanity check) */ - if (hi != 0x0000f001) { - printk(KERN_WARNING NAME ": GPIO not enabled\n"); - return -ENODEV; - } - - /* Mask off the IO base address */ - gpio_base = low & 0x0000ff00; - - /** - * Some GPIO pins - * 31-29,23 : reserved (always mask out) - * 28 : Power Button - * 26 : PME# - * 22-16 : LPC - * 14,15 : SMBus - * 9,8 : UART1 - * 7 : PCI INTB - * 3,4 : UART2/DDC - * 2 : IDE_IRQ0 - * 0 : PCI INTA - * - * If a mask was not specified, be conservative and only allow: - * 1,2,5,6,10-13,24,25,27 - */ - if (mask != 0) - mask &= 0x1f7fffff; - else - mask = 0x0b003c66; - - if (!request_region(gpio_base, CS5535_GPIO_SIZE, NAME)) { - printk(KERN_ERR NAME ": can't allocate I/O for GPIO\n"); - return -ENODEV; - } - - if (major) { - dev_id = MKDEV(major, 0); - retval = register_chrdev_region(dev_id, CS5535_GPIO_COUNT, - NAME); - } else { - retval = alloc_chrdev_region(&dev_id, 0, CS5535_GPIO_COUNT, - NAME); - major = MAJOR(dev_id); - } - - if (retval) { - release_region(gpio_base, CS5535_GPIO_SIZE); - return -1; - } - - printk(KERN_DEBUG NAME ": base=%#x mask=%#lx major=%d\n", - gpio_base, mask, major); - - cdev_init(&cs5535_gpio_cdev, &cs5535_gpio_fops); - cdev_add(&cs5535_gpio_cdev, dev_id, CS5535_GPIO_COUNT); - - return 0; -} - -static void __exit cs5535_gpio_cleanup(void) -{ - dev_t dev_id = MKDEV(major, 0); - - cdev_del(&cs5535_gpio_cdev); - unregister_chrdev_region(dev_id, CS5535_GPIO_COUNT); - release_region(gpio_base, CS5535_GPIO_SIZE); -} - -module_init(cs5535_gpio_init); -module_exit(cs5535_gpio_cleanup); diff --git a/drivers/char/defkeymap.c_shipped b/drivers/char/defkeymap.c_shipped deleted file mode 100644 index d2208dfe3f67..000000000000 --- a/drivers/char/defkeymap.c_shipped +++ /dev/null @@ -1,262 +0,0 @@ -/* Do not edit this file! It was automatically generated by */ -/* loadkeys --mktable defkeymap.map > defkeymap.c */ - -#include <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; diff --git a/drivers/char/defkeymap.map b/drivers/char/defkeymap.map deleted file mode 100644 index 50b30cace261..000000000000 --- a/drivers/char/defkeymap.map +++ /dev/null @@ -1,357 +0,0 @@ -# Default kernel keymap. This uses 7 modifier combinations. -keymaps 0-2,4-5,8,12 -# Change the above line into -# keymaps 0-2,4-6,8,12 -# in case you want the entries -# altgr control keycode 83 = Boot -# altgr control keycode 111 = Boot -# below. -# -# In fact AltGr is used very little, and one more keymap can -# be saved by mapping AltGr to Alt (and adapting a few entries): -# keycode 100 = Alt -# -keycode 1 = Escape Escape - alt keycode 1 = Meta_Escape -keycode 2 = one exclam - alt keycode 2 = Meta_one -keycode 3 = two at at - control keycode 3 = nul - shift control keycode 3 = nul - alt keycode 3 = Meta_two -keycode 4 = three numbersign - control keycode 4 = Escape - alt keycode 4 = Meta_three -keycode 5 = four dollar dollar - control keycode 5 = Control_backslash - alt keycode 5 = Meta_four -keycode 6 = five percent - control keycode 6 = Control_bracketright - alt keycode 6 = Meta_five -keycode 7 = six asciicircum - control keycode 7 = Control_asciicircum - alt keycode 7 = Meta_six -keycode 8 = seven ampersand braceleft - control keycode 8 = Control_underscore - alt keycode 8 = Meta_seven -keycode 9 = eight asterisk bracketleft - control keycode 9 = Delete - alt keycode 9 = Meta_eight -keycode 10 = nine parenleft bracketright - alt keycode 10 = Meta_nine -keycode 11 = zero parenright braceright - alt keycode 11 = Meta_zero -keycode 12 = minus underscore backslash - control keycode 12 = Control_underscore - shift control keycode 12 = Control_underscore - alt keycode 12 = Meta_minus -keycode 13 = equal plus - alt keycode 13 = Meta_equal -keycode 14 = Delete Delete - control keycode 14 = BackSpace - alt keycode 14 = Meta_Delete -keycode 15 = Tab Tab - alt keycode 15 = Meta_Tab -keycode 16 = q -keycode 17 = w -keycode 18 = e - altgr keycode 18 = Hex_E -keycode 19 = r -keycode 20 = t -keycode 21 = y -keycode 22 = u -keycode 23 = i -keycode 24 = o -keycode 25 = p -keycode 26 = bracketleft braceleft - control keycode 26 = Escape - alt keycode 26 = Meta_bracketleft -keycode 27 = bracketright braceright asciitilde - control keycode 27 = Control_bracketright - alt keycode 27 = Meta_bracketright -keycode 28 = Return - alt keycode 28 = Meta_Control_m -keycode 29 = Control -keycode 30 = a - altgr keycode 30 = Hex_A -keycode 31 = s -keycode 32 = d - altgr keycode 32 = Hex_D -keycode 33 = f - altgr keycode 33 = Hex_F -keycode 34 = g -keycode 35 = h -keycode 36 = j -keycode 37 = k -keycode 38 = l -keycode 39 = semicolon colon - alt keycode 39 = Meta_semicolon -keycode 40 = apostrophe quotedbl - control keycode 40 = Control_g - alt keycode 40 = Meta_apostrophe -keycode 41 = grave asciitilde - control keycode 41 = nul - alt keycode 41 = Meta_grave -keycode 42 = Shift -keycode 43 = backslash bar - control keycode 43 = Control_backslash - alt keycode 43 = Meta_backslash -keycode 44 = z -keycode 45 = x -keycode 46 = c - altgr keycode 46 = Hex_C -keycode 47 = v -keycode 48 = b - altgr keycode 48 = Hex_B -keycode 49 = n -keycode 50 = m -keycode 51 = comma less - alt keycode 51 = Meta_comma -keycode 52 = period greater - control keycode 52 = Compose - alt keycode 52 = Meta_period -keycode 53 = slash question - control keycode 53 = Delete - alt keycode 53 = Meta_slash -keycode 54 = Shift -keycode 55 = KP_Multiply -keycode 56 = Alt -keycode 57 = space space - control keycode 57 = nul - alt keycode 57 = Meta_space -keycode 58 = Caps_Lock -keycode 59 = F1 F11 Console_13 - control keycode 59 = F1 - alt keycode 59 = Console_1 - control alt keycode 59 = Console_1 -keycode 60 = F2 F12 Console_14 - control keycode 60 = F2 - alt keycode 60 = Console_2 - control alt keycode 60 = Console_2 -keycode 61 = F3 F13 Console_15 - control keycode 61 = F3 - alt keycode 61 = Console_3 - control alt keycode 61 = Console_3 -keycode 62 = F4 F14 Console_16 - control keycode 62 = F4 - alt keycode 62 = Console_4 - control alt keycode 62 = Console_4 -keycode 63 = F5 F15 Console_17 - control keycode 63 = F5 - alt keycode 63 = Console_5 - control alt keycode 63 = Console_5 -keycode 64 = F6 F16 Console_18 - control keycode 64 = F6 - alt keycode 64 = Console_6 - control alt keycode 64 = Console_6 -keycode 65 = F7 F17 Console_19 - control keycode 65 = F7 - alt keycode 65 = Console_7 - control alt keycode 65 = Console_7 -keycode 66 = F8 F18 Console_20 - control keycode 66 = F8 - alt keycode 66 = Console_8 - control alt keycode 66 = Console_8 -keycode 67 = F9 F19 Console_21 - control keycode 67 = F9 - alt keycode 67 = Console_9 - control alt keycode 67 = Console_9 -keycode 68 = F10 F20 Console_22 - control keycode 68 = F10 - alt keycode 68 = Console_10 - control alt keycode 68 = Console_10 -keycode 69 = Num_Lock - shift keycode 69 = Bare_Num_Lock -keycode 70 = Scroll_Lock Show_Memory Show_Registers - control keycode 70 = Show_State - alt keycode 70 = Scroll_Lock -keycode 71 = KP_7 - alt keycode 71 = Ascii_7 - altgr keycode 71 = Hex_7 -keycode 72 = KP_8 - alt keycode 72 = Ascii_8 - altgr keycode 72 = Hex_8 -keycode 73 = KP_9 - alt keycode 73 = Ascii_9 - altgr keycode 73 = Hex_9 -keycode 74 = KP_Subtract -keycode 75 = KP_4 - alt keycode 75 = Ascii_4 - altgr keycode 75 = Hex_4 -keycode 76 = KP_5 - alt keycode 76 = Ascii_5 - altgr keycode 76 = Hex_5 -keycode 77 = KP_6 - alt keycode 77 = Ascii_6 - altgr keycode 77 = Hex_6 -keycode 78 = KP_Add -keycode 79 = KP_1 - alt keycode 79 = Ascii_1 - altgr keycode 79 = Hex_1 -keycode 80 = KP_2 - alt keycode 80 = Ascii_2 - altgr keycode 80 = Hex_2 -keycode 81 = KP_3 - alt keycode 81 = Ascii_3 - altgr keycode 81 = Hex_3 -keycode 82 = KP_0 - alt keycode 82 = Ascii_0 - altgr keycode 82 = Hex_0 -keycode 83 = KP_Period -# altgr control keycode 83 = Boot - control alt keycode 83 = Boot -keycode 84 = Last_Console -keycode 85 = -keycode 86 = less greater bar - alt keycode 86 = Meta_less -keycode 87 = F11 F11 Console_23 - control keycode 87 = F11 - alt keycode 87 = Console_11 - control alt keycode 87 = Console_11 -keycode 88 = F12 F12 Console_24 - control keycode 88 = F12 - alt keycode 88 = Console_12 - control alt keycode 88 = Console_12 -keycode 89 = -keycode 90 = -keycode 91 = -keycode 92 = -keycode 93 = -keycode 94 = -keycode 95 = -keycode 96 = KP_Enter -keycode 97 = Control -keycode 98 = KP_Divide -keycode 99 = Control_backslash - control keycode 99 = Control_backslash - alt keycode 99 = Control_backslash -keycode 100 = AltGr -keycode 101 = Break -keycode 102 = Find -keycode 103 = Up -keycode 104 = Prior - shift keycode 104 = Scroll_Backward -keycode 105 = Left - alt keycode 105 = Decr_Console -keycode 106 = Right - alt keycode 106 = Incr_Console -keycode 107 = Select -keycode 108 = Down -keycode 109 = Next - shift keycode 109 = Scroll_Forward -keycode 110 = Insert -keycode 111 = Remove -# altgr control keycode 111 = Boot - control alt keycode 111 = Boot -keycode 112 = Macro -keycode 113 = F13 -keycode 114 = F14 -keycode 115 = Help -keycode 116 = Do -keycode 117 = F17 -keycode 118 = KP_MinPlus -keycode 119 = Pause -keycode 120 = -keycode 121 = -keycode 122 = -keycode 123 = -keycode 124 = -keycode 125 = -keycode 126 = -keycode 127 = -string F1 = "\033[[A" -string F2 = "\033[[B" -string F3 = "\033[[C" -string F4 = "\033[[D" -string F5 = "\033[[E" -string F6 = "\033[17~" -string F7 = "\033[18~" -string F8 = "\033[19~" -string F9 = "\033[20~" -string F10 = "\033[21~" -string F11 = "\033[23~" -string F12 = "\033[24~" -string F13 = "\033[25~" -string F14 = "\033[26~" -string F15 = "\033[28~" -string F16 = "\033[29~" -string F17 = "\033[31~" -string F18 = "\033[32~" -string F19 = "\033[33~" -string F20 = "\033[34~" -string Find = "\033[1~" -string Insert = "\033[2~" -string Remove = "\033[3~" -string Select = "\033[4~" -string Prior = "\033[5~" -string Next = "\033[6~" -string Macro = "\033[M" -string Pause = "\033[P" -compose '`' 'A' to 'À' -compose '`' 'a' to 'à' -compose '\'' 'A' to 'Á' -compose '\'' 'a' to 'á' -compose '^' 'A' to 'Â' -compose '^' 'a' to 'â' -compose '~' 'A' to 'Ã' -compose '~' 'a' to 'ã' -compose '"' 'A' to 'Ä' -compose '"' 'a' to 'ä' -compose 'O' 'A' to 'Å' -compose 'o' 'a' to 'å' -compose '0' 'A' to 'Å' -compose '0' 'a' to 'å' -compose 'A' 'A' to 'Å' -compose 'a' 'a' to 'å' -compose 'A' 'E' to 'Æ' -compose 'a' 'e' to 'æ' -compose ',' 'C' to 'Ç' -compose ',' 'c' to 'ç' -compose '`' 'E' to 'È' -compose '`' 'e' to 'è' -compose '\'' 'E' to 'É' -compose '\'' 'e' to 'é' -compose '^' 'E' to 'Ê' -compose '^' 'e' to 'ê' -compose '"' 'E' to 'Ë' -compose '"' 'e' to 'ë' -compose '`' 'I' to 'Ì' -compose '`' 'i' to 'ì' -compose '\'' 'I' to 'Í' -compose '\'' 'i' to 'í' -compose '^' 'I' to 'Î' -compose '^' 'i' to 'î' -compose '"' 'I' to 'Ï' -compose '"' 'i' to 'ï' -compose '-' 'D' to 'Ð' -compose '-' 'd' to 'ð' -compose '~' 'N' to 'Ñ' -compose '~' 'n' to 'ñ' -compose '`' 'O' to 'Ò' -compose '`' 'o' to 'ò' -compose '\'' 'O' to 'Ó' -compose '\'' 'o' to 'ó' -compose '^' 'O' to 'Ô' -compose '^' 'o' to 'ô' -compose '~' 'O' to 'Õ' -compose '~' 'o' to 'õ' -compose '"' 'O' to 'Ö' -compose '"' 'o' to 'ö' -compose '/' 'O' to 'Ø' -compose '/' 'o' to 'ø' -compose '`' 'U' to 'Ù' -compose '`' 'u' to 'ù' -compose '\'' 'U' to 'Ú' -compose '\'' 'u' to 'ú' -compose '^' 'U' to 'Û' -compose '^' 'u' to 'û' -compose '"' 'U' to 'Ü' -compose '"' 'u' to 'ü' -compose '\'' 'Y' to 'Ý' -compose '\'' 'y' to 'ý' -compose 'T' 'H' to 'Þ' -compose 't' 'h' to 'þ' -compose 's' 's' to 'ß' -compose '"' 'y' to 'ÿ' -compose 's' 'z' to 'ß' -compose 'i' 'j' to 'ÿ' diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 55b8667f739f..7066e801b9d3 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -14,7 +14,6 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/kernel.h> -#include <linux/smp_lock.h> #include <linux/types.h> #include <linux/miscdevice.h> #include <linux/major.h> diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c deleted file mode 100644 index 5fe4631e2a61..000000000000 --- a/drivers/char/hvc_beat.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Beat hypervisor console driver - * - * (C) Copyright 2006 TOSHIBA CORPORATION - * - * This code is based on drivers/char/hvc_rtas.c: - * (C) Copyright IBM Corporation 2001-2005 - * (C) Copyright Red Hat, Inc. 2005 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/err.h> -#include <linux/string.h> -#include <linux/console.h> -#include <asm/prom.h> -#include <asm/hvconsole.h> -#include <asm/firmware.h> - -#include "hvc_console.h" - -extern int64_t beat_get_term_char(uint64_t, uint64_t *, uint64_t *, uint64_t *); -extern int64_t beat_put_term_char(uint64_t, uint64_t, uint64_t, uint64_t); - -struct hvc_struct *hvc_beat_dev = NULL; - -/* bug: only one queue is available regardless of vtermno */ -static int hvc_beat_get_chars(uint32_t vtermno, char *buf, int cnt) -{ - static unsigned char q[sizeof(unsigned long) * 2] - __attribute__((aligned(sizeof(unsigned long)))); - static int qlen = 0; - u64 got; - -again: - if (qlen) { - if (qlen > cnt) { - memcpy(buf, q, cnt); - qlen -= cnt; - memmove(q + cnt, q, qlen); - return cnt; - } else { /* qlen <= cnt */ - int r; - - memcpy(buf, q, qlen); - r = qlen; - qlen = 0; - return r; - } - } - if (beat_get_term_char(vtermno, &got, - ((u64 *)q), ((u64 *)q) + 1) == 0) { - qlen = got; - goto again; - } - return 0; -} - -static int hvc_beat_put_chars(uint32_t vtermno, const char *buf, int cnt) -{ - unsigned long kb[2]; - int rest, nlen; - - for (rest = cnt; rest > 0; rest -= nlen) { - nlen = (rest > 16) ? 16 : rest; - memcpy(kb, buf, nlen); - beat_put_term_char(vtermno, nlen, kb[0], kb[1]); - buf += nlen; - } - return cnt; -} - -static const struct hv_ops hvc_beat_get_put_ops = { - .get_chars = hvc_beat_get_chars, - .put_chars = hvc_beat_put_chars, -}; - -static int hvc_beat_useit = 1; - -static int hvc_beat_config(char *p) -{ - hvc_beat_useit = simple_strtoul(p, NULL, 0); - return 0; -} - -static int __init hvc_beat_console_init(void) -{ - if (hvc_beat_useit && of_machine_is_compatible("Beat")) { - hvc_instantiate(0, 0, &hvc_beat_get_put_ops); - } - return 0; -} - -/* temp */ -static int __init hvc_beat_init(void) -{ - struct hvc_struct *hp; - - if (!firmware_has_feature(FW_FEATURE_BEAT)) - return -ENODEV; - - hp = hvc_alloc(0, NO_IRQ, &hvc_beat_get_put_ops, 16); - if (IS_ERR(hp)) - return PTR_ERR(hp); - hvc_beat_dev = hp; - return 0; -} - -static void __exit hvc_beat_exit(void) -{ - if (hvc_beat_dev) - hvc_remove(hvc_beat_dev); -} - -module_init(hvc_beat_init); -module_exit(hvc_beat_exit); - -__setup("hvc_beat=", hvc_beat_config); - -console_initcall(hvc_beat_console_init); diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c deleted file mode 100644 index e9cba13ee800..000000000000 --- a/drivers/char/hvc_console.c +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM - * Copyright (C) 2001 Paul Mackerras <paulus@au.ibm.com>, IBM - * Copyright (C) 2004 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. - * Copyright (C) 2004 IBM Corporation - * - * Additional Author(s): - * Ryan S. Arnold <rsa@us.ibm.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/console.h> -#include <linux/cpumask.h> -#include <linux/init.h> -#include <linux/kbd_kern.h> -#include <linux/kernel.h> -#include <linux/kthread.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/major.h> -#include <linux/sysrq.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/sched.h> -#include <linux/spinlock.h> -#include <linux/delay.h> -#include <linux/freezer.h> -#include <linux/slab.h> - -#include <asm/uaccess.h> - -#include "hvc_console.h" - -#define HVC_MAJOR 229 -#define HVC_MINOR 0 - -/* - * Wait this long per iteration while trying to push buffered data to the - * hypervisor before allowing the tty to complete a close operation. - */ -#define HVC_CLOSE_WAIT (HZ/100) /* 1/10 of a second */ - -/* - * These sizes are most efficient for vio, because they are the - * native transfer size. We could make them selectable in the - * future to better deal with backends that want other buffer sizes. - */ -#define N_OUTBUF 16 -#define N_INBUF 16 - -#define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) - -static struct tty_driver *hvc_driver; -static struct task_struct *hvc_task; - -/* Picks up late kicks after list walk but before schedule() */ -static int hvc_kicked; - -static int hvc_init(void); - -#ifdef CONFIG_MAGIC_SYSRQ -static int sysrq_pressed; -#endif - -/* dynamic list of hvc_struct instances */ -static LIST_HEAD(hvc_structs); - -/* - * Protect the list of hvc_struct instances from inserts and removals during - * list traversal. - */ -static DEFINE_SPINLOCK(hvc_structs_lock); - -/* - * This value is used to assign a tty->index value to a hvc_struct based - * upon order of exposure via hvc_probe(), when we can not match it to - * a console candidate registered with hvc_instantiate(). - */ -static int last_hvc = -1; - -/* - * Do not call this function with either the hvc_structs_lock or the hvc_struct - * lock held. If successful, this function increments the kref reference - * count against the target hvc_struct so it should be released when finished. - */ -static struct hvc_struct *hvc_get_by_index(int index) -{ - struct hvc_struct *hp; - unsigned long flags; - - spin_lock(&hvc_structs_lock); - - list_for_each_entry(hp, &hvc_structs, next) { - spin_lock_irqsave(&hp->lock, flags); - if (hp->index == index) { - kref_get(&hp->kref); - spin_unlock_irqrestore(&hp->lock, flags); - spin_unlock(&hvc_structs_lock); - return hp; - } - spin_unlock_irqrestore(&hp->lock, flags); - } - hp = NULL; - - spin_unlock(&hvc_structs_lock); - return hp; -} - - -/* - * Initial console vtermnos for console API usage prior to full console - * initialization. Any vty adapter outside this range will not have usable - * console interfaces but can still be used as a tty device. This has to be - * static because kmalloc will not work during early console init. - */ -static const struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES]; -static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] = - {[0 ... MAX_NR_HVC_CONSOLES - 1] = -1}; - -/* - * Console APIs, NOT TTY. These APIs are available immediately when - * hvc_console_setup() finds adapters. - */ - -static void hvc_console_print(struct console *co, const char *b, - unsigned count) -{ - char c[N_OUTBUF] __ALIGNED__; - unsigned i = 0, n = 0; - int r, donecr = 0, index = co->index; - - /* Console access attempt outside of acceptable console range. */ - if (index >= MAX_NR_HVC_CONSOLES) - return; - - /* This console adapter was removed so it is not usable. */ - if (vtermnos[index] == -1) - return; - - while (count > 0 || i > 0) { - if (count > 0 && i < sizeof(c)) { - if (b[n] == '\n' && !donecr) { - c[i++] = '\r'; - donecr = 1; - } else { - c[i++] = b[n++]; - donecr = 0; - --count; - } - } else { - r = cons_ops[index]->put_chars(vtermnos[index], c, i); - if (r <= 0) { - /* throw away chars on error */ - i = 0; - } else if (r > 0) { - i -= r; - if (i > 0) - memmove(c, c+r, i); - } - } - } -} - -static struct tty_driver *hvc_console_device(struct console *c, int *index) -{ - if (vtermnos[c->index] == -1) - return NULL; - - *index = c->index; - return hvc_driver; -} - -static int __init hvc_console_setup(struct console *co, char *options) -{ - if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) - return -ENODEV; - - if (vtermnos[co->index] == -1) - return -ENODEV; - - return 0; -} - -static struct console hvc_console = { - .name = "hvc", - .write = hvc_console_print, - .device = hvc_console_device, - .setup = hvc_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -/* - * Early console initialization. Precedes driver initialization. - * - * (1) we are first, and the user specified another driver - * -- index will remain -1 - * (2) we are first and the user specified no driver - * -- index will be set to 0, then we will fail setup. - * (3) we are first and the user specified our driver - * -- index will be set to user specified driver, and we will fail - * (4) we are after driver, and this initcall will register us - * -- if the user didn't specify a driver then the console will match - * - * Note that for cases 2 and 3, we will match later when the io driver - * calls hvc_instantiate() and call register again. - */ -static int __init hvc_console_init(void) -{ - register_console(&hvc_console); - return 0; -} -console_initcall(hvc_console_init); - -/* callback when the kboject ref count reaches zero. */ -static void destroy_hvc_struct(struct kref *kref) -{ - struct hvc_struct *hp = container_of(kref, struct hvc_struct, kref); - unsigned long flags; - - spin_lock(&hvc_structs_lock); - - spin_lock_irqsave(&hp->lock, flags); - list_del(&(hp->next)); - spin_unlock_irqrestore(&hp->lock, flags); - - spin_unlock(&hvc_structs_lock); - - kfree(hp); -} - -/* - * hvc_instantiate() is an early console discovery method which locates - * consoles * prior to the vio subsystem discovering them. Hotplugged - * vty adapters do NOT get an hvc_instantiate() callback since they - * appear after early console init. - */ -int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops) -{ - struct hvc_struct *hp; - - if (index < 0 || index >= MAX_NR_HVC_CONSOLES) - return -1; - - if (vtermnos[index] != -1) - return -1; - - /* make sure no no tty has been registered in this index */ - hp = hvc_get_by_index(index); - if (hp) { - kref_put(&hp->kref, destroy_hvc_struct); - return -1; - } - - vtermnos[index] = vtermno; - cons_ops[index] = ops; - - /* reserve all indices up to and including this index */ - if (last_hvc < index) - last_hvc = index; - - /* if this index is what the user requested, then register - * now (setup won't fail at this point). It's ok to just - * call register again if previously .setup failed. - */ - if (index == hvc_console.index) - register_console(&hvc_console); - - return 0; -} -EXPORT_SYMBOL_GPL(hvc_instantiate); - -/* Wake the sleeping khvcd */ -void hvc_kick(void) -{ - hvc_kicked = 1; - wake_up_process(hvc_task); -} -EXPORT_SYMBOL_GPL(hvc_kick); - -static void hvc_unthrottle(struct tty_struct *tty) -{ - hvc_kick(); -} - -/* - * The TTY interface won't be used until after the vio layer has exposed the vty - * adapter to the kernel. - */ -static int hvc_open(struct tty_struct *tty, struct file * filp) -{ - struct hvc_struct *hp; - unsigned long flags; - int rc = 0; - - /* Auto increments kref reference if found. */ - if (!(hp = hvc_get_by_index(tty->index))) - return -ENODEV; - - spin_lock_irqsave(&hp->lock, flags); - /* Check and then increment for fast path open. */ - if (hp->count++ > 0) { - tty_kref_get(tty); - spin_unlock_irqrestore(&hp->lock, flags); - hvc_kick(); - return 0; - } /* else count == 0 */ - - tty->driver_data = hp; - - hp->tty = tty_kref_get(tty); - - spin_unlock_irqrestore(&hp->lock, flags); - - if (hp->ops->notifier_add) - rc = hp->ops->notifier_add(hp, hp->data); - - /* - * If the notifier fails we return an error. The tty layer - * will call hvc_close() after a failed open but we don't want to clean - * up there so we'll clean up here and clear out the previously set - * tty fields and return the kref reference. - */ - if (rc) { - spin_lock_irqsave(&hp->lock, flags); - hp->tty = NULL; - spin_unlock_irqrestore(&hp->lock, flags); - tty_kref_put(tty); - tty->driver_data = NULL; - kref_put(&hp->kref, destroy_hvc_struct); - printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc); - } - /* Force wakeup of the polling thread */ - hvc_kick(); - - return rc; -} - -static void hvc_close(struct tty_struct *tty, struct file * filp) -{ - struct hvc_struct *hp; - unsigned long flags; - - if (tty_hung_up_p(filp)) - return; - - /* - * No driver_data means that this close was issued after a failed - * hvc_open by the tty layer's release_dev() function and we can just - * exit cleanly because the kref reference wasn't made. - */ - if (!tty->driver_data) - return; - - hp = tty->driver_data; - - spin_lock_irqsave(&hp->lock, flags); - - if (--hp->count == 0) { - /* We are done with the tty pointer now. */ - hp->tty = NULL; - spin_unlock_irqrestore(&hp->lock, flags); - - if (hp->ops->notifier_del) - hp->ops->notifier_del(hp, hp->data); - - /* cancel pending tty resize work */ - cancel_work_sync(&hp->tty_resize); - - /* - * Chain calls chars_in_buffer() and returns immediately if - * there is no buffered data otherwise sleeps on a wait queue - * waking periodically to check chars_in_buffer(). - */ - tty_wait_until_sent(tty, HVC_CLOSE_WAIT); - } else { - if (hp->count < 0) - printk(KERN_ERR "hvc_close %X: oops, count is %d\n", - hp->vtermno, hp->count); - spin_unlock_irqrestore(&hp->lock, flags); - } - - tty_kref_put(tty); - kref_put(&hp->kref, destroy_hvc_struct); -} - -static void hvc_hangup(struct tty_struct *tty) -{ - struct hvc_struct *hp = tty->driver_data; - unsigned long flags; - int temp_open_count; - - if (!hp) - return; - - /* cancel pending tty resize work */ - cancel_work_sync(&hp->tty_resize); - - spin_lock_irqsave(&hp->lock, flags); - - /* - * The N_TTY line discipline has problems such that in a close vs - * open->hangup case this can be called after the final close so prevent - * that from happening for now. - */ - if (hp->count <= 0) { - spin_unlock_irqrestore(&hp->lock, flags); - return; - } - - temp_open_count = hp->count; - hp->count = 0; - hp->n_outbuf = 0; - hp->tty = NULL; - - spin_unlock_irqrestore(&hp->lock, flags); - - if (hp->ops->notifier_hangup) - hp->ops->notifier_hangup(hp, hp->data); - - while(temp_open_count) { - --temp_open_count; - tty_kref_put(tty); - kref_put(&hp->kref, destroy_hvc_struct); - } -} - -/* - * Push buffered characters whether they were just recently buffered or waiting - * on a blocked hypervisor. Call this function with hp->lock held. - */ -static int hvc_push(struct hvc_struct *hp) -{ - int n; - - n = hp->ops->put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf); - if (n <= 0) { - if (n == 0) { - hp->do_wakeup = 1; - return 0; - } - /* throw away output on error; this happens when - there is no session connected to the vterm. */ - hp->n_outbuf = 0; - } else - hp->n_outbuf -= n; - if (hp->n_outbuf > 0) - memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf); - else - hp->do_wakeup = 1; - - return n; -} - -static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - struct hvc_struct *hp = tty->driver_data; - unsigned long flags; - int rsize, written = 0; - - /* This write was probably executed during a tty close. */ - if (!hp) - return -EPIPE; - - if (hp->count <= 0) - return -EIO; - - spin_lock_irqsave(&hp->lock, flags); - - /* Push pending writes */ - if (hp->n_outbuf > 0) - hvc_push(hp); - - while (count > 0 && (rsize = hp->outbuf_size - hp->n_outbuf) > 0) { - if (rsize > count) - rsize = count; - memcpy(hp->outbuf + hp->n_outbuf, buf, rsize); - count -= rsize; - buf += rsize; - hp->n_outbuf += rsize; - written += rsize; - hvc_push(hp); - } - spin_unlock_irqrestore(&hp->lock, flags); - - /* - * Racy, but harmless, kick thread if there is still pending data. - */ - if (hp->n_outbuf) - hvc_kick(); - - return written; -} - -/** - * hvc_set_winsz() - Resize the hvc tty terminal window. - * @work: work structure. - * - * The routine shall not be called within an atomic context because it - * might sleep. - * - * Locking: hp->lock - */ -static void hvc_set_winsz(struct work_struct *work) -{ - struct hvc_struct *hp; - unsigned long hvc_flags; - struct tty_struct *tty; - struct winsize ws; - - hp = container_of(work, struct hvc_struct, tty_resize); - - spin_lock_irqsave(&hp->lock, hvc_flags); - if (!hp->tty) { - spin_unlock_irqrestore(&hp->lock, hvc_flags); - return; - } - ws = hp->ws; - tty = tty_kref_get(hp->tty); - spin_unlock_irqrestore(&hp->lock, hvc_flags); - - tty_do_resize(tty, &ws); - tty_kref_put(tty); -} - -/* - * This is actually a contract between the driver and the tty layer outlining - * how much write room the driver can guarantee will be sent OR BUFFERED. This - * driver MUST honor the return value. - */ -static int hvc_write_room(struct tty_struct *tty) -{ - struct hvc_struct *hp = tty->driver_data; - - if (!hp) - return -1; - - return hp->outbuf_size - hp->n_outbuf; -} - -static int hvc_chars_in_buffer(struct tty_struct *tty) -{ - struct hvc_struct *hp = tty->driver_data; - - if (!hp) - return 0; - return hp->n_outbuf; -} - -/* - * timeout will vary between the MIN and MAX values defined here. By default - * and during console activity we will use a default MIN_TIMEOUT of 10. When - * the console is idle, we increase the timeout value on each pass through - * msleep until we reach the max. This may be noticeable as a brief (average - * one second) delay on the console before the console responds to input when - * there has been no input for some time. - */ -#define MIN_TIMEOUT (10) -#define MAX_TIMEOUT (2000) -static u32 timeout = MIN_TIMEOUT; - -#define HVC_POLL_READ 0x00000001 -#define HVC_POLL_WRITE 0x00000002 - -int hvc_poll(struct hvc_struct *hp) -{ - struct tty_struct *tty; - int i, n, poll_mask = 0; - char buf[N_INBUF] __ALIGNED__; - unsigned long flags; - int read_total = 0; - int written_total = 0; - - spin_lock_irqsave(&hp->lock, flags); - - /* Push pending writes */ - if (hp->n_outbuf > 0) - written_total = hvc_push(hp); - - /* Reschedule us if still some write pending */ - if (hp->n_outbuf > 0) { - poll_mask |= HVC_POLL_WRITE; - /* If hvc_push() was not able to write, sleep a few msecs */ - timeout = (written_total) ? 0 : MIN_TIMEOUT; - } - - /* No tty attached, just skip */ - tty = tty_kref_get(hp->tty); - if (tty == NULL) - goto bail; - - /* Now check if we can get data (are we throttled ?) */ - if (test_bit(TTY_THROTTLED, &tty->flags)) - goto throttled; - - /* If we aren't notifier driven and aren't throttled, we always - * request a reschedule - */ - if (!hp->irq_requested) - poll_mask |= HVC_POLL_READ; - - /* Read data if any */ - for (;;) { - int count = tty_buffer_request_room(tty, N_INBUF); - - /* If flip is full, just reschedule a later read */ - if (count == 0) { - poll_mask |= HVC_POLL_READ; - break; - } - - n = hp->ops->get_chars(hp->vtermno, buf, count); - if (n <= 0) { - /* Hangup the tty when disconnected from host */ - if (n == -EPIPE) { - spin_unlock_irqrestore(&hp->lock, flags); - tty_hangup(tty); - spin_lock_irqsave(&hp->lock, flags); - } else if ( n == -EAGAIN ) { - /* - * Some back-ends can only ensure a certain min - * num of bytes read, which may be > 'count'. - * Let the tty clear the flip buff to make room. - */ - poll_mask |= HVC_POLL_READ; - } - break; - } - for (i = 0; i < n; ++i) { -#ifdef CONFIG_MAGIC_SYSRQ - if (hp->index == hvc_console.index) { - /* Handle the SysRq Hack */ - /* XXX should support a sequence */ - if (buf[i] == '\x0f') { /* ^O */ - /* if ^O is pressed again, reset - * sysrq_pressed and flip ^O char */ - sysrq_pressed = !sysrq_pressed; - if (sysrq_pressed) - continue; - } else if (sysrq_pressed) { - handle_sysrq(buf[i]); - sysrq_pressed = 0; - continue; - } - } -#endif /* CONFIG_MAGIC_SYSRQ */ - tty_insert_flip_char(tty, buf[i], 0); - } - - read_total += n; - } - throttled: - /* Wakeup write queue if necessary */ - if (hp->do_wakeup) { - hp->do_wakeup = 0; - tty_wakeup(tty); - } - bail: - spin_unlock_irqrestore(&hp->lock, flags); - - if (read_total) { - /* Activity is occurring, so reset the polling backoff value to - a minimum for performance. */ - timeout = MIN_TIMEOUT; - - tty_flip_buffer_push(tty); - } - if (tty) - tty_kref_put(tty); - - return poll_mask; -} -EXPORT_SYMBOL_GPL(hvc_poll); - -/** - * __hvc_resize() - Update terminal window size information. - * @hp: HVC console pointer - * @ws: Terminal window size structure - * - * Stores the specified window size information in the hvc structure of @hp. - * The function schedule the tty resize update. - * - * Locking: Locking free; the function MUST be called holding hp->lock - */ -void __hvc_resize(struct hvc_struct *hp, struct winsize ws) -{ - hp->ws = ws; - schedule_work(&hp->tty_resize); -} -EXPORT_SYMBOL_GPL(__hvc_resize); - -/* - * This kthread is either polling or interrupt driven. This is determined by - * calling hvc_poll() who determines whether a console adapter support - * interrupts. - */ -static int khvcd(void *unused) -{ - int poll_mask; - struct hvc_struct *hp; - - set_freezable(); - do { - poll_mask = 0; - hvc_kicked = 0; - try_to_freeze(); - wmb(); - if (!cpus_are_in_xmon()) { - spin_lock(&hvc_structs_lock); - list_for_each_entry(hp, &hvc_structs, next) { - poll_mask |= hvc_poll(hp); - } - spin_unlock(&hvc_structs_lock); - } else - poll_mask |= HVC_POLL_READ; - if (hvc_kicked) - continue; - set_current_state(TASK_INTERRUPTIBLE); - if (!hvc_kicked) { - if (poll_mask == 0) - schedule(); - else { - if (timeout < MAX_TIMEOUT) - timeout += (timeout >> 6) + 1; - - msleep_interruptible(timeout); - } - } - __set_current_state(TASK_RUNNING); - } while (!kthread_should_stop()); - - return 0; -} - -static const struct tty_operations hvc_ops = { - .open = hvc_open, - .close = hvc_close, - .write = hvc_write, - .hangup = hvc_hangup, - .unthrottle = hvc_unthrottle, - .write_room = hvc_write_room, - .chars_in_buffer = hvc_chars_in_buffer, -}; - -struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, - const struct hv_ops *ops, - int outbuf_size) -{ - struct hvc_struct *hp; - int i; - - /* We wait until a driver actually comes along */ - if (!hvc_driver) { - int err = hvc_init(); - if (err) - return ERR_PTR(err); - } - - hp = kzalloc(ALIGN(sizeof(*hp), sizeof(long)) + outbuf_size, - GFP_KERNEL); - if (!hp) - return ERR_PTR(-ENOMEM); - - hp->vtermno = vtermno; - hp->data = data; - hp->ops = ops; - hp->outbuf_size = outbuf_size; - hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))]; - - kref_init(&hp->kref); - - INIT_WORK(&hp->tty_resize, hvc_set_winsz); - spin_lock_init(&hp->lock); - spin_lock(&hvc_structs_lock); - - /* - * find index to use: - * see if this vterm id matches one registered for console. - */ - for (i=0; i < MAX_NR_HVC_CONSOLES; i++) - if (vtermnos[i] == hp->vtermno && - cons_ops[i] == hp->ops) - break; - - /* no matching slot, just use a counter */ - if (i >= MAX_NR_HVC_CONSOLES) - i = ++last_hvc; - - hp->index = i; - - list_add_tail(&(hp->next), &hvc_structs); - spin_unlock(&hvc_structs_lock); - - return hp; -} -EXPORT_SYMBOL_GPL(hvc_alloc); - -int hvc_remove(struct hvc_struct *hp) -{ - unsigned long flags; - struct tty_struct *tty; - - spin_lock_irqsave(&hp->lock, flags); - tty = tty_kref_get(hp->tty); - - if (hp->index < MAX_NR_HVC_CONSOLES) - vtermnos[hp->index] = -1; - - /* Don't whack hp->irq because tty_hangup() will need to free the irq. */ - - spin_unlock_irqrestore(&hp->lock, flags); - - /* - * We 'put' the instance that was grabbed when the kref instance - * was initialized using kref_init(). Let the last holder of this - * kref cause it to be removed, which will probably be the tty_vhangup - * below. - */ - kref_put(&hp->kref, destroy_hvc_struct); - - /* - * This function call will auto chain call hvc_hangup. - */ - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } - return 0; -} -EXPORT_SYMBOL_GPL(hvc_remove); - -/* Driver initialization: called as soon as someone uses hvc_alloc(). */ -static int hvc_init(void) -{ - struct tty_driver *drv; - int err; - - /* We need more than hvc_count adapters due to hotplug additions. */ - drv = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS); - if (!drv) { - err = -ENOMEM; - goto out; - } - - drv->owner = THIS_MODULE; - drv->driver_name = "hvc"; - drv->name = "hvc"; - drv->major = HVC_MAJOR; - drv->minor_start = HVC_MINOR; - drv->type = TTY_DRIVER_TYPE_SYSTEM; - drv->init_termios = tty_std_termios; - drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; - tty_set_operations(drv, &hvc_ops); - - /* Always start the kthread because there can be hotplug vty adapters - * added later. */ - hvc_task = kthread_run(khvcd, NULL, "khvcd"); - if (IS_ERR(hvc_task)) { - printk(KERN_ERR "Couldn't create kthread for console.\n"); - err = PTR_ERR(hvc_task); - goto put_tty; - } - - err = tty_register_driver(drv); - if (err) { - printk(KERN_ERR "Couldn't register hvc console driver\n"); - goto stop_thread; - } - - /* - * Make sure tty is fully registered before allowing it to be - * found by hvc_console_device. - */ - smp_mb(); - hvc_driver = drv; - return 0; - -stop_thread: - kthread_stop(hvc_task); - hvc_task = NULL; -put_tty: - put_tty_driver(drv); -out: - return err; -} - -/* This isn't particularly necessary due to this being a console driver - * but it is nice to be thorough. - */ -static void __exit hvc_exit(void) -{ - if (hvc_driver) { - kthread_stop(hvc_task); - - tty_unregister_driver(hvc_driver); - /* return tty_struct instances allocated in hvc_init(). */ - put_tty_driver(hvc_driver); - unregister_console(&hvc_console); - } -} -module_exit(hvc_exit); diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h deleted file mode 100644 index 54381eba4e4a..000000000000 --- a/drivers/char/hvc_console.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * hvc_console.h - * Copyright (C) 2005 IBM Corporation - * - * Author(s): - * Ryan S. Arnold <rsa@us.ibm.com> - * - * hvc_console header information: - * moved here from arch/powerpc/include/asm/hvconsole.h - * and drivers/char/hvc_console.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef HVC_CONSOLE_H -#define HVC_CONSOLE_H -#include <linux/kref.h> -#include <linux/tty.h> -#include <linux/spinlock.h> - -/* - * This is the max number of console adapters that can/will be found as - * console devices on first stage console init. Any number beyond this range - * can't be used as a console device but is still a valid tty device. - */ -#define MAX_NR_HVC_CONSOLES 16 - -/* - * The Linux TTY code does not support dynamic addition of tty derived devices - * so we need to know how many tty devices we might need when space is allocated - * for the tty device. Since this driver supports hotplug of vty adapters we - * need to make sure we have enough allocated. - */ -#define HVC_ALLOC_TTY_ADAPTERS 8 - -struct hvc_struct { - spinlock_t lock; - int index; - struct tty_struct *tty; - int count; - int do_wakeup; - char *outbuf; - int outbuf_size; - int n_outbuf; - uint32_t vtermno; - const struct hv_ops *ops; - int irq_requested; - int data; - struct winsize ws; - struct work_struct tty_resize; - struct list_head next; - struct kref kref; /* ref count & hvc_struct lifetime */ -}; - -/* implemented by a low level driver */ -struct hv_ops { - int (*get_chars)(uint32_t vtermno, char *buf, int count); - int (*put_chars)(uint32_t vtermno, const char *buf, int count); - - /* Callbacks for notification. Called in open, close and hangup */ - int (*notifier_add)(struct hvc_struct *hp, int irq); - void (*notifier_del)(struct hvc_struct *hp, int irq); - void (*notifier_hangup)(struct hvc_struct *hp, int irq); -}; - -/* Register a vterm and a slot index for use as a console (console_init) */ -extern int hvc_instantiate(uint32_t vtermno, int index, - const struct hv_ops *ops); - -/* register a vterm for hvc tty operation (module_init or hotplug add) */ -extern struct hvc_struct * hvc_alloc(uint32_t vtermno, int data, - const struct hv_ops *ops, int outbuf_size); -/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */ -extern int hvc_remove(struct hvc_struct *hp); - -/* data available */ -int hvc_poll(struct hvc_struct *hp); -void hvc_kick(void); - -/* Resize hvc tty terminal window */ -extern void __hvc_resize(struct hvc_struct *hp, struct winsize ws); - -static inline void hvc_resize(struct hvc_struct *hp, struct winsize ws) -{ - unsigned long flags; - - spin_lock_irqsave(&hp->lock, flags); - __hvc_resize(hp, ws); - spin_unlock_irqrestore(&hp->lock, flags); -} - -/* default notifier for irq based notification */ -extern int notifier_add_irq(struct hvc_struct *hp, int data); -extern void notifier_del_irq(struct hvc_struct *hp, int data); -extern void notifier_hangup_irq(struct hvc_struct *hp, int data); - - -#if defined(CONFIG_XMON) && defined(CONFIG_SMP) -#include <asm/xmon.h> -#else -static inline int cpus_are_in_xmon(void) -{ - return 0; -} -#endif - -#endif // HVC_CONSOLE_H diff --git a/drivers/char/hvc_irq.c b/drivers/char/hvc_irq.c deleted file mode 100644 index 2623e177e8d6..000000000000 --- a/drivers/char/hvc_irq.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright IBM Corp. 2001,2008 - * - * This file contains the IRQ specific code for hvc_console - * - */ - -#include <linux/interrupt.h> - -#include "hvc_console.h" - -static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance) -{ - /* if hvc_poll request a repoll, then kick the hvcd thread */ - if (hvc_poll(dev_instance)) - hvc_kick(); - return IRQ_HANDLED; -} - -/* - * For IRQ based systems these callbacks can be used - */ -int notifier_add_irq(struct hvc_struct *hp, int irq) -{ - int rc; - - if (!irq) { - hp->irq_requested = 0; - return 0; - } - rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, - "hvc_console", hp); - if (!rc) - hp->irq_requested = 1; - return rc; -} - -void notifier_del_irq(struct hvc_struct *hp, int irq) -{ - if (!hp->irq_requested) - return; - free_irq(irq, hp); - hp->irq_requested = 0; -} - -void notifier_hangup_irq(struct hvc_struct *hp, int irq) -{ - notifier_del_irq(hp, irq); -} diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c deleted file mode 100644 index 21c54955084e..000000000000 --- a/drivers/char/hvc_iseries.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * iSeries vio driver interface to hvc_console.c - * - * This code is based heavily on hvc_vio.c and viocons.c - * - * Copyright (C) 2006 Stephen Rothwell, IBM Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include <stdarg.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/spinlock.h> -#include <linux/console.h> - -#include <asm/hvconsole.h> -#include <asm/vio.h> -#include <asm/prom.h> -#include <asm/firmware.h> -#include <asm/iseries/vio.h> -#include <asm/iseries/hv_call.h> -#include <asm/iseries/hv_lp_config.h> -#include <asm/iseries/hv_lp_event.h> - -#include "hvc_console.h" - -#define VTTY_PORTS 10 - -static DEFINE_SPINLOCK(consolelock); -static DEFINE_SPINLOCK(consoleloglock); - -static const char hvc_driver_name[] = "hvc_console"; - -#define IN_BUF_SIZE 200 - -/* - * Our port information. - */ -static struct port_info { - HvLpIndex lp; - u64 seq; /* sequence number of last HV send */ - u64 ack; /* last ack from HV */ - struct hvc_struct *hp; - int in_start; - int in_end; - unsigned char in_buf[IN_BUF_SIZE]; -} port_info[VTTY_PORTS] = { - [ 0 ... VTTY_PORTS - 1 ] = { - .lp = HvLpIndexInvalid - } -}; - -#define viochar_is_console(pi) ((pi) == &port_info[0]) - -static struct vio_device_id hvc_driver_table[] __devinitdata = { - {"serial", "IBM,iSeries-vty"}, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, hvc_driver_table); - -static void hvlog(char *fmt, ...) -{ - int i; - unsigned long flags; - va_list args; - static char buf[256]; - - spin_lock_irqsave(&consoleloglock, flags); - va_start(args, fmt); - i = vscnprintf(buf, sizeof(buf) - 1, fmt, args); - va_end(args); - buf[i++] = '\r'; - HvCall_writeLogBuffer(buf, i); - spin_unlock_irqrestore(&consoleloglock, flags); -} - -/* - * Initialize the common fields in a charLpEvent - */ -static void init_data_event(struct viocharlpevent *viochar, HvLpIndex lp) -{ - struct HvLpEvent *hev = &viochar->event; - - memset(viochar, 0, sizeof(struct viocharlpevent)); - - hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DEFERRED_ACK | - HV_LP_EVENT_INT; - hev->xType = HvLpEvent_Type_VirtualIo; - hev->xSubtype = viomajorsubtype_chario | viochardata; - hev->xSourceLp = HvLpConfig_getLpIndex(); - hev->xTargetLp = lp; - hev->xSizeMinus1 = sizeof(struct viocharlpevent); - hev->xSourceInstanceId = viopath_sourceinst(lp); - hev->xTargetInstanceId = viopath_targetinst(lp); -} - -static int get_chars(uint32_t vtermno, char *buf, int count) -{ - struct port_info *pi; - int n = 0; - unsigned long flags; - - if (vtermno >= VTTY_PORTS) - return -EINVAL; - if (count == 0) - return 0; - - pi = &port_info[vtermno]; - spin_lock_irqsave(&consolelock, flags); - - if (pi->in_end == 0) - goto done; - - n = pi->in_end - pi->in_start; - if (n > count) - n = count; - memcpy(buf, &pi->in_buf[pi->in_start], n); - pi->in_start += n; - if (pi->in_start == pi->in_end) { - pi->in_start = 0; - pi->in_end = 0; - } -done: - spin_unlock_irqrestore(&consolelock, flags); - return n; -} - -static int put_chars(uint32_t vtermno, const char *buf, int count) -{ - struct viocharlpevent *viochar; - struct port_info *pi; - HvLpEvent_Rc hvrc; - unsigned long flags; - int sent = 0; - - if (vtermno >= VTTY_PORTS) - return -EINVAL; - - pi = &port_info[vtermno]; - - spin_lock_irqsave(&consolelock, flags); - - if (viochar_is_console(pi) && !viopath_isactive(pi->lp)) { - HvCall_writeLogBuffer(buf, count); - sent = count; - goto done; - } - - viochar = vio_get_event_buffer(viomajorsubtype_chario); - if (viochar == NULL) { - hvlog("\n\rviocons: Can't get viochar buffer."); - goto done; - } - - while ((count > 0) && ((pi->seq - pi->ack) < VIOCHAR_WINDOW)) { - int len; - - len = (count > VIOCHAR_MAX_DATA) ? VIOCHAR_MAX_DATA : count; - - if (viochar_is_console(pi)) - HvCall_writeLogBuffer(buf, len); - - init_data_event(viochar, pi->lp); - - viochar->len = len; - viochar->event.xCorrelationToken = pi->seq++; - viochar->event.xSizeMinus1 = - offsetof(struct viocharlpevent, data) + len; - - memcpy(viochar->data, buf, len); - - hvrc = HvCallEvent_signalLpEvent(&viochar->event); - if (hvrc) - hvlog("\n\rerror sending event! return code %d\n\r", - (int)hvrc); - sent += len; - count -= len; - buf += len; - } - - vio_free_event_buffer(viomajorsubtype_chario, viochar); -done: - spin_unlock_irqrestore(&consolelock, flags); - return sent; -} - -static const struct hv_ops hvc_get_put_ops = { - .get_chars = get_chars, - .put_chars = put_chars, - .notifier_add = notifier_add_irq, - .notifier_del = notifier_del_irq, - .notifier_hangup = notifier_hangup_irq, -}; - -static int __devinit hvc_vio_probe(struct vio_dev *vdev, - const struct vio_device_id *id) -{ - struct hvc_struct *hp; - struct port_info *pi; - - /* probed with invalid parameters. */ - if (!vdev || !id) - return -EPERM; - - if (vdev->unit_address >= VTTY_PORTS) - return -ENODEV; - - pi = &port_info[vdev->unit_address]; - - hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, - VIOCHAR_MAX_DATA); - if (IS_ERR(hp)) - return PTR_ERR(hp); - pi->hp = hp; - dev_set_drvdata(&vdev->dev, pi); - - return 0; -} - -static int __devexit hvc_vio_remove(struct vio_dev *vdev) -{ - struct port_info *pi = dev_get_drvdata(&vdev->dev); - struct hvc_struct *hp = pi->hp; - - return hvc_remove(hp); -} - -static struct vio_driver hvc_vio_driver = { - .id_table = hvc_driver_table, - .probe = hvc_vio_probe, - .remove = __devexit_p(hvc_vio_remove), - .driver = { - .name = hvc_driver_name, - .owner = THIS_MODULE, - } -}; - -static void hvc_open_event(struct HvLpEvent *event) -{ - unsigned long flags; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - u8 port = cevent->virtual_device; - struct port_info *pi; - int reject = 0; - - if (hvlpevent_is_ack(event)) { - if (port >= VTTY_PORTS) - return; - - spin_lock_irqsave(&consolelock, flags); - - pi = &port_info[port]; - if (event->xRc == HvLpEvent_Rc_Good) { - pi->seq = pi->ack = 0; - /* - * This line allows connections from the primary - * partition but once one is connected from the - * primary partition nothing short of a reboot - * of linux will allow access from the hosting - * partition again without a required iSeries fix. - */ - pi->lp = event->xTargetLp; - } - - spin_unlock_irqrestore(&consolelock, flags); - if (event->xRc != HvLpEvent_Rc_Good) - printk(KERN_WARNING - "hvc: handle_open_event: event->xRc == (%d).\n", - event->xRc); - - if (event->xCorrelationToken != 0) { - atomic_t *aptr= (atomic_t *)event->xCorrelationToken; - atomic_set(aptr, 1); - } else - printk(KERN_WARNING - "hvc: weird...got open ack without atomic\n"); - return; - } - - /* This had better require an ack, otherwise complain */ - if (!hvlpevent_need_ack(event)) { - printk(KERN_WARNING "hvc: viocharopen without ack bit!\n"); - return; - } - - spin_lock_irqsave(&consolelock, flags); - - /* Make sure this is a good virtual tty */ - if (port >= VTTY_PORTS) { - event->xRc = HvLpEvent_Rc_SubtypeError; - cevent->subtype_result_code = viorc_openRejected; - /* - * Flag state here since we can't printk while holding - * the consolelock spinlock. - */ - reject = 1; - } else { - pi = &port_info[port]; - if ((pi->lp != HvLpIndexInvalid) && - (pi->lp != event->xSourceLp)) { - /* - * If this is tty is already connected to a different - * partition, fail. - */ - event->xRc = HvLpEvent_Rc_SubtypeError; - cevent->subtype_result_code = viorc_openRejected; - reject = 2; - } else { - pi->lp = event->xSourceLp; - event->xRc = HvLpEvent_Rc_Good; - cevent->subtype_result_code = viorc_good; - pi->seq = pi->ack = 0; - } - } - - spin_unlock_irqrestore(&consolelock, flags); - - if (reject == 1) - printk(KERN_WARNING "hvc: open rejected: bad virtual tty.\n"); - else if (reject == 2) - printk(KERN_WARNING "hvc: open rejected: console in exclusive " - "use by another partition.\n"); - - /* Return the acknowledgement */ - HvCallEvent_ackLpEvent(event); -} - -/* - * Handle a close charLpEvent. This should ONLY be an Interrupt because the - * virtual console should never actually issue a close event to the hypervisor - * because the virtual console never goes away. A close event coming from the - * hypervisor simply means that there are no client consoles connected to the - * virtual console. - */ -static void hvc_close_event(struct HvLpEvent *event) -{ - unsigned long flags; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - u8 port = cevent->virtual_device; - - if (!hvlpevent_is_int(event)) { - printk(KERN_WARNING - "hvc: got unexpected close acknowledgement\n"); - return; - } - - if (port >= VTTY_PORTS) { - printk(KERN_WARNING - "hvc: close message from invalid virtual device.\n"); - return; - } - - /* For closes, just mark the console partition invalid */ - spin_lock_irqsave(&consolelock, flags); - - if (port_info[port].lp == event->xSourceLp) - port_info[port].lp = HvLpIndexInvalid; - - spin_unlock_irqrestore(&consolelock, flags); -} - -static void hvc_data_event(struct HvLpEvent *event) -{ - unsigned long flags; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - struct port_info *pi; - int n; - u8 port = cevent->virtual_device; - - if (port >= VTTY_PORTS) { - printk(KERN_WARNING "hvc: data on invalid virtual device %d\n", - port); - return; - } - if (cevent->len == 0) - return; - - /* - * Change 05/01/2003 - Ryan Arnold: If a partition other than - * the current exclusive partition tries to send us data - * events then just drop them on the floor because we don't - * want his stinking data. He isn't authorized to receive - * data because he wasn't the first one to get the console, - * therefore he shouldn't be allowed to send data either. - * This will work without an iSeries fix. - */ - pi = &port_info[port]; - if (pi->lp != event->xSourceLp) - return; - - spin_lock_irqsave(&consolelock, flags); - - n = IN_BUF_SIZE - pi->in_end; - if (n > cevent->len) - n = cevent->len; - if (n > 0) { - memcpy(&pi->in_buf[pi->in_end], cevent->data, n); - pi->in_end += n; - } - spin_unlock_irqrestore(&consolelock, flags); - if (n == 0) - printk(KERN_WARNING "hvc: input buffer overflow\n"); -} - -static void hvc_ack_event(struct HvLpEvent *event) -{ - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - unsigned long flags; - u8 port = cevent->virtual_device; - - if (port >= VTTY_PORTS) { - printk(KERN_WARNING "hvc: data on invalid virtual device\n"); - return; - } - - spin_lock_irqsave(&consolelock, flags); - port_info[port].ack = event->xCorrelationToken; - spin_unlock_irqrestore(&consolelock, flags); -} - -static void hvc_config_event(struct HvLpEvent *event) -{ - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - - if (cevent->data[0] == 0x01) - printk(KERN_INFO "hvc: window resized to %d: %d: %d: %d\n", - cevent->data[1], cevent->data[2], - cevent->data[3], cevent->data[4]); - else - printk(KERN_WARNING "hvc: unknown config event\n"); -} - -static void hvc_handle_event(struct HvLpEvent *event) -{ - int charminor; - - if (event == NULL) - return; - - charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; - switch (charminor) { - case viocharopen: - hvc_open_event(event); - break; - case viocharclose: - hvc_close_event(event); - break; - case viochardata: - hvc_data_event(event); - break; - case viocharack: - hvc_ack_event(event); - break; - case viocharconfig: - hvc_config_event(event); - break; - default: - if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } -} - -static int __init send_open(HvLpIndex remoteLp, void *sem) -{ - return HvCallEvent_signalLpEventFast(remoteLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_chario | viocharopen, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(remoteLp), - viopath_targetinst(remoteLp), - (u64)(unsigned long)sem, VIOVERSION << 16, - 0, 0, 0, 0); -} - -static int __init hvc_vio_init(void) -{ - atomic_t wait_flag; - int rc; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -EIO; - - /* +2 for fudge */ - rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), - viomajorsubtype_chario, VIOCHAR_WINDOW + 2); - if (rc) - printk(KERN_WARNING "hvc: error opening to primary %d\n", rc); - - if (viopath_hostLp == HvLpIndexInvalid) - vio_set_hostlp(); - - /* - * And if the primary is not the same as the hosting LP, open to the - * hosting lp - */ - if ((viopath_hostLp != HvLpIndexInvalid) && - (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) { - printk(KERN_INFO "hvc: open path to hosting (%d)\n", - viopath_hostLp); - rc = viopath_open(viopath_hostLp, viomajorsubtype_chario, - VIOCHAR_WINDOW + 2); /* +2 for fudge */ - if (rc) - printk(KERN_WARNING - "error opening to partition %d: %d\n", - viopath_hostLp, rc); - } - - if (vio_setHandler(viomajorsubtype_chario, hvc_handle_event) < 0) - printk(KERN_WARNING - "hvc: error seting handler for console events!\n"); - - /* - * First, try to open the console to the hosting lp. - * Wait on a semaphore for the response. - */ - atomic_set(&wait_flag, 0); - if ((viopath_isactive(viopath_hostLp)) && - (send_open(viopath_hostLp, &wait_flag) == 0)) { - printk(KERN_INFO "hvc: hosting partition %d\n", viopath_hostLp); - while (atomic_read(&wait_flag) == 0) - mb(); - atomic_set(&wait_flag, 0); - } - - /* - * If we don't have an active console, try the primary - */ - if ((!viopath_isactive(port_info[0].lp)) && - (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && - (send_open(HvLpConfig_getPrimaryLpIndex(), &wait_flag) == 0)) { - printk(KERN_INFO "hvc: opening console to primary partition\n"); - while (atomic_read(&wait_flag) == 0) - mb(); - } - - /* Register as a vio device to receive callbacks */ - rc = vio_register_driver(&hvc_vio_driver); - - return rc; -} -module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ - -static void __exit hvc_vio_exit(void) -{ - vio_unregister_driver(&hvc_vio_driver); -} -module_exit(hvc_vio_exit); - -/* the device tree order defines our numbering */ -static int __init hvc_find_vtys(void) -{ - struct device_node *vty; - int num_found = 0; - - for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; - vty = of_find_node_by_name(vty, "vty")) { - const uint32_t *vtermno; - - /* We have statically defined space for only a certain number - * of console adapters. - */ - if ((num_found >= MAX_NR_HVC_CONSOLES) || - (num_found >= VTTY_PORTS)) { - of_node_put(vty); - break; - } - - vtermno = of_get_property(vty, "reg", NULL); - if (!vtermno) - continue; - - if (!of_device_is_compatible(vty, "IBM,iSeries-vty")) - continue; - - if (num_found == 0) - add_preferred_console("hvc", 0, NULL); - hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); - ++num_found; - } - - return num_found; -} -console_initcall(hvc_find_vtys); diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c deleted file mode 100644 index c3425bb3a1f6..000000000000 --- a/drivers/char/hvc_iucv.c +++ /dev/null @@ -1,1337 +0,0 @@ -/* - * hvc_iucv.c - z/VM IUCV hypervisor console (HVC) device driver - * - * This HVC device driver provides terminal access using - * z/VM IUCV communication paths. - * - * Copyright IBM Corp. 2008, 2009 - * - * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com> - */ -#define KMSG_COMPONENT "hvc_iucv" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt - -#include <linux/types.h> -#include <linux/slab.h> -#include <asm/ebcdic.h> -#include <linux/ctype.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/init.h> -#include <linux/mempool.h> -#include <linux/moduleparam.h> -#include <linux/tty.h> -#include <linux/wait.h> -#include <net/iucv/iucv.h> - -#include "hvc_console.h" - - -/* General device driver settings */ -#define HVC_IUCV_MAGIC 0xc9e4c3e5 -#define MAX_HVC_IUCV_LINES HVC_ALLOC_TTY_ADAPTERS -#define MEMPOOL_MIN_NR (PAGE_SIZE / sizeof(struct iucv_tty_buffer)/4) - -/* IUCV TTY message */ -#define MSG_VERSION 0x02 /* Message version */ -#define MSG_TYPE_ERROR 0x01 /* Error message */ -#define MSG_TYPE_TERMENV 0x02 /* Terminal environment variable */ -#define MSG_TYPE_TERMIOS 0x04 /* Terminal IO struct update */ -#define MSG_TYPE_WINSIZE 0x08 /* Terminal window size update */ -#define MSG_TYPE_DATA 0x10 /* Terminal data */ - -struct iucv_tty_msg { - u8 version; /* Message version */ - u8 type; /* Message type */ -#define MSG_MAX_DATALEN ((u16)(~0)) - u16 datalen; /* Payload length */ - u8 data[]; /* Payload buffer */ -} __attribute__((packed)); -#define MSG_SIZE(s) ((s) + offsetof(struct iucv_tty_msg, data)) - -enum iucv_state_t { - IUCV_DISCONN = 0, - IUCV_CONNECTED = 1, - IUCV_SEVERED = 2, -}; - -enum tty_state_t { - TTY_CLOSED = 0, - TTY_OPENED = 1, -}; - -struct hvc_iucv_private { - struct hvc_struct *hvc; /* HVC struct reference */ - u8 srv_name[8]; /* IUCV service name (ebcdic) */ - unsigned char is_console; /* Linux console usage flag */ - enum iucv_state_t iucv_state; /* IUCV connection status */ - enum tty_state_t tty_state; /* TTY status */ - struct iucv_path *path; /* IUCV path pointer */ - spinlock_t lock; /* hvc_iucv_private lock */ -#define SNDBUF_SIZE (PAGE_SIZE) /* must be < MSG_MAX_DATALEN */ - void *sndbuf; /* send buffer */ - size_t sndbuf_len; /* length of send buffer */ -#define QUEUE_SNDBUF_DELAY (HZ / 25) - struct delayed_work sndbuf_work; /* work: send iucv msg(s) */ - wait_queue_head_t sndbuf_waitq; /* wait for send completion */ - struct list_head tty_outqueue; /* outgoing IUCV messages */ - struct list_head tty_inqueue; /* incoming IUCV messages */ - struct device *dev; /* device structure */ -}; - -struct iucv_tty_buffer { - struct list_head list; /* list pointer */ - struct iucv_message msg; /* store an IUCV message */ - size_t offset; /* data buffer offset */ - struct iucv_tty_msg *mbuf; /* buffer to store input/output data */ -}; - -/* IUCV callback handler */ -static int hvc_iucv_path_pending(struct iucv_path *, u8[8], u8[16]); -static void hvc_iucv_path_severed(struct iucv_path *, u8[16]); -static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *); -static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *); - - -/* Kernel module parameter: use one terminal device as default */ -static unsigned long hvc_iucv_devices = 1; - -/* Array of allocated hvc iucv tty lines... */ -static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES]; -#define IUCV_HVC_CON_IDX (0) -/* List of z/VM user ID filter entries (struct iucv_vmid_filter) */ -#define MAX_VMID_FILTER (500) -static size_t hvc_iucv_filter_size; -static void *hvc_iucv_filter; -static const char *hvc_iucv_filter_string; -static DEFINE_RWLOCK(hvc_iucv_filter_lock); - -/* Kmem cache and mempool for iucv_tty_buffer elements */ -static struct kmem_cache *hvc_iucv_buffer_cache; -static mempool_t *hvc_iucv_mempool; - -/* IUCV handler callback functions */ -static struct iucv_handler hvc_iucv_handler = { - .path_pending = hvc_iucv_path_pending, - .path_severed = hvc_iucv_path_severed, - .message_complete = hvc_iucv_msg_complete, - .message_pending = hvc_iucv_msg_pending, -}; - - -/** - * hvc_iucv_get_private() - Return a struct hvc_iucv_private instance. - * @num: The HVC virtual terminal number (vtermno) - * - * This function returns the struct hvc_iucv_private instance that corresponds - * to the HVC virtual terminal number specified as parameter @num. - */ -struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) -{ - if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices)) - return NULL; - return hvc_iucv_table[num - HVC_IUCV_MAGIC]; -} - -/** - * alloc_tty_buffer() - Return a new struct iucv_tty_buffer element. - * @size: Size of the internal buffer used to store data. - * @flags: Memory allocation flags passed to mempool. - * - * This function allocates a new struct iucv_tty_buffer element and, optionally, - * allocates an internal data buffer with the specified size @size. - * The internal data buffer is always allocated with GFP_DMA which is - * required for receiving and sending data with IUCV. - * Note: The total message size arises from the internal buffer size and the - * members of the iucv_tty_msg structure. - * The function returns NULL if memory allocation has failed. - */ -static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags) -{ - struct iucv_tty_buffer *bufp; - - bufp = mempool_alloc(hvc_iucv_mempool, flags); - if (!bufp) - return NULL; - memset(bufp, 0, sizeof(*bufp)); - - if (size > 0) { - bufp->msg.length = MSG_SIZE(size); - bufp->mbuf = kmalloc(bufp->msg.length, flags | GFP_DMA); - if (!bufp->mbuf) { - mempool_free(bufp, hvc_iucv_mempool); - return NULL; - } - bufp->mbuf->version = MSG_VERSION; - bufp->mbuf->type = MSG_TYPE_DATA; - bufp->mbuf->datalen = (u16) size; - } - return bufp; -} - -/** - * destroy_tty_buffer() - destroy struct iucv_tty_buffer element. - * @bufp: Pointer to a struct iucv_tty_buffer element, SHALL NOT be NULL. - */ -static void destroy_tty_buffer(struct iucv_tty_buffer *bufp) -{ - kfree(bufp->mbuf); - mempool_free(bufp, hvc_iucv_mempool); -} - -/** - * destroy_tty_buffer_list() - call destroy_tty_buffer() for each list element. - * @list: List containing struct iucv_tty_buffer elements. - */ -static void destroy_tty_buffer_list(struct list_head *list) -{ - struct iucv_tty_buffer *ent, *next; - - list_for_each_entry_safe(ent, next, list, list) { - list_del(&ent->list); - destroy_tty_buffer(ent); - } -} - -/** - * hvc_iucv_write() - Receive IUCV message & write data to HVC buffer. - * @priv: Pointer to struct hvc_iucv_private - * @buf: HVC buffer for writing received terminal data. - * @count: HVC buffer size. - * @has_more_data: Pointer to an int variable. - * - * The function picks up pending messages from the input queue and receives - * the message data that is then written to the specified buffer @buf. - * If the buffer size @count is less than the data message size, the - * message is kept on the input queue and @has_more_data is set to 1. - * If all message data has been written, the message is removed from - * the input queue. - * - * The function returns the number of bytes written to the terminal, zero if - * there are no pending data messages available or if there is no established - * IUCV path. - * If the IUCV path has been severed, then -EPIPE is returned to cause a - * hang up (that is issued by the HVC layer). - */ -static int hvc_iucv_write(struct hvc_iucv_private *priv, - char *buf, int count, int *has_more_data) -{ - struct iucv_tty_buffer *rb; - int written; - int rc; - - /* immediately return if there is no IUCV connection */ - if (priv->iucv_state == IUCV_DISCONN) - return 0; - - /* if the IUCV path has been severed, return -EPIPE to inform the - * HVC layer to hang up the tty device. */ - if (priv->iucv_state == IUCV_SEVERED) - return -EPIPE; - - /* check if there are pending messages */ - if (list_empty(&priv->tty_inqueue)) - return 0; - - /* receive an iucv message and flip data to the tty (ldisc) */ - rb = list_first_entry(&priv->tty_inqueue, struct iucv_tty_buffer, list); - - written = 0; - if (!rb->mbuf) { /* message not yet received ... */ - /* allocate mem to store msg data; if no memory is available - * then leave the buffer on the list and re-try later */ - rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC | GFP_DMA); - if (!rb->mbuf) - return -ENOMEM; - - rc = __iucv_message_receive(priv->path, &rb->msg, 0, - rb->mbuf, rb->msg.length, NULL); - switch (rc) { - case 0: /* Successful */ - break; - case 2: /* No message found */ - case 9: /* Message purged */ - break; - default: - written = -EIO; - } - /* remove buffer if an error has occured or received data - * is not correct */ - if (rc || (rb->mbuf->version != MSG_VERSION) || - (rb->msg.length != MSG_SIZE(rb->mbuf->datalen))) - goto out_remove_buffer; - } - - switch (rb->mbuf->type) { - case MSG_TYPE_DATA: - written = min_t(int, rb->mbuf->datalen - rb->offset, count); - memcpy(buf, rb->mbuf->data + rb->offset, written); - if (written < (rb->mbuf->datalen - rb->offset)) { - rb->offset += written; - *has_more_data = 1; - goto out_written; - } - break; - - case MSG_TYPE_WINSIZE: - if (rb->mbuf->datalen != sizeof(struct winsize)) - break; - /* The caller must ensure that the hvc is locked, which - * is the case when called from hvc_iucv_get_chars() */ - __hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); - break; - - case MSG_TYPE_ERROR: /* ignored ... */ - case MSG_TYPE_TERMENV: /* ignored ... */ - case MSG_TYPE_TERMIOS: /* ignored ... */ - break; - } - -out_remove_buffer: - list_del(&rb->list); - destroy_tty_buffer(rb); - *has_more_data = !list_empty(&priv->tty_inqueue); - -out_written: - return written; -} - -/** - * hvc_iucv_get_chars() - HVC get_chars operation. - * @vtermno: HVC virtual terminal number. - * @buf: Pointer to a buffer to store data - * @count: Size of buffer available for writing - * - * The HVC thread calls this method to read characters from the back-end. - * If an IUCV communication path has been established, pending IUCV messages - * are received and data is copied into buffer @buf up to @count bytes. - * - * Locking: The routine gets called under an irqsave() spinlock; and - * the routine locks the struct hvc_iucv_private->lock to call - * helper functions. - */ -static int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count) -{ - struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); - int written; - int has_more_data; - - if (count <= 0) - return 0; - - if (!priv) - return -ENODEV; - - spin_lock(&priv->lock); - has_more_data = 0; - written = hvc_iucv_write(priv, buf, count, &has_more_data); - spin_unlock(&priv->lock); - - /* if there are still messages on the queue... schedule another run */ - if (has_more_data) - hvc_kick(); - - return written; -} - -/** - * hvc_iucv_queue() - Buffer terminal data for sending. - * @priv: Pointer to struct hvc_iucv_private instance. - * @buf: Buffer containing data to send. - * @count: Size of buffer and amount of data to send. - * - * The function queues data for sending. To actually send the buffered data, - * a work queue function is scheduled (with QUEUE_SNDBUF_DELAY). - * The function returns the number of data bytes that has been buffered. - * - * If the device is not connected, data is ignored and the function returns - * @count. - * If the buffer is full, the function returns 0. - * If an existing IUCV communicaton path has been severed, -EPIPE is returned - * (that can be passed to HVC layer to cause a tty hangup). - */ -static int hvc_iucv_queue(struct hvc_iucv_private *priv, const char *buf, - int count) -{ - size_t len; - - if (priv->iucv_state == IUCV_DISCONN) - return count; /* ignore data */ - - if (priv->iucv_state == IUCV_SEVERED) - return -EPIPE; - - len = min_t(size_t, count, SNDBUF_SIZE - priv->sndbuf_len); - if (!len) - return 0; - - memcpy(priv->sndbuf + priv->sndbuf_len, buf, len); - priv->sndbuf_len += len; - - if (priv->iucv_state == IUCV_CONNECTED) - schedule_delayed_work(&priv->sndbuf_work, QUEUE_SNDBUF_DELAY); - - return len; -} - -/** - * hvc_iucv_send() - Send an IUCV message containing terminal data. - * @priv: Pointer to struct hvc_iucv_private instance. - * - * If an IUCV communication path has been established, the buffered output data - * is sent via an IUCV message and the number of bytes sent is returned. - * Returns 0 if there is no established IUCV communication path or - * -EPIPE if an existing IUCV communicaton path has been severed. - */ -static int hvc_iucv_send(struct hvc_iucv_private *priv) -{ - struct iucv_tty_buffer *sb; - int rc, len; - - if (priv->iucv_state == IUCV_SEVERED) - return -EPIPE; - - if (priv->iucv_state == IUCV_DISCONN) - return -EIO; - - if (!priv->sndbuf_len) - return 0; - - /* allocate internal buffer to store msg data and also compute total - * message length */ - sb = alloc_tty_buffer(priv->sndbuf_len, GFP_ATOMIC); - if (!sb) - return -ENOMEM; - - memcpy(sb->mbuf->data, priv->sndbuf, priv->sndbuf_len); - sb->mbuf->datalen = (u16) priv->sndbuf_len; - sb->msg.length = MSG_SIZE(sb->mbuf->datalen); - - list_add_tail(&sb->list, &priv->tty_outqueue); - - rc = __iucv_message_send(priv->path, &sb->msg, 0, 0, - (void *) sb->mbuf, sb->msg.length); - if (rc) { - /* drop the message here; however we might want to handle - * 0x03 (msg limit reached) by trying again... */ - list_del(&sb->list); - destroy_tty_buffer(sb); - } - len = priv->sndbuf_len; - priv->sndbuf_len = 0; - - return len; -} - -/** - * hvc_iucv_sndbuf_work() - Send buffered data over IUCV - * @work: Work structure. - * - * This work queue function sends buffered output data over IUCV and, - * if not all buffered data could be sent, reschedules itself. - */ -static void hvc_iucv_sndbuf_work(struct work_struct *work) -{ - struct hvc_iucv_private *priv; - - priv = container_of(work, struct hvc_iucv_private, sndbuf_work.work); - if (!priv) - return; - - spin_lock_bh(&priv->lock); - hvc_iucv_send(priv); - spin_unlock_bh(&priv->lock); -} - -/** - * hvc_iucv_put_chars() - HVC put_chars operation. - * @vtermno: HVC virtual terminal number. - * @buf: Pointer to an buffer to read data from - * @count: Size of buffer available for reading - * - * The HVC thread calls this method to write characters to the back-end. - * The function calls hvc_iucv_queue() to queue terminal data for sending. - * - * Locking: The method gets called under an irqsave() spinlock; and - * locks struct hvc_iucv_private->lock. - */ -static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count) -{ - struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); - int queued; - - if (count <= 0) - return 0; - - if (!priv) - return -ENODEV; - - spin_lock(&priv->lock); - queued = hvc_iucv_queue(priv, buf, count); - spin_unlock(&priv->lock); - - return queued; -} - -/** - * hvc_iucv_notifier_add() - HVC notifier for opening a TTY for the first time. - * @hp: Pointer to the HVC device (struct hvc_struct) - * @id: Additional data (originally passed to hvc_alloc): the index of an struct - * hvc_iucv_private instance. - * - * The function sets the tty state to TTY_OPENED for the struct hvc_iucv_private - * instance that is derived from @id. Always returns 0. - * - * Locking: struct hvc_iucv_private->lock, spin_lock_bh - */ -static int hvc_iucv_notifier_add(struct hvc_struct *hp, int id) -{ - struct hvc_iucv_private *priv; - - priv = hvc_iucv_get_private(id); - if (!priv) - return 0; - - spin_lock_bh(&priv->lock); - priv->tty_state = TTY_OPENED; - spin_unlock_bh(&priv->lock); - - return 0; -} - -/** - * hvc_iucv_cleanup() - Clean up and reset a z/VM IUCV HVC instance. - * @priv: Pointer to the struct hvc_iucv_private instance. - */ -static void hvc_iucv_cleanup(struct hvc_iucv_private *priv) -{ - destroy_tty_buffer_list(&priv->tty_outqueue); - destroy_tty_buffer_list(&priv->tty_inqueue); - - priv->tty_state = TTY_CLOSED; - priv->iucv_state = IUCV_DISCONN; - - priv->sndbuf_len = 0; -} - -/** - * tty_outqueue_empty() - Test if the tty outq is empty - * @priv: Pointer to struct hvc_iucv_private instance. - */ -static inline int tty_outqueue_empty(struct hvc_iucv_private *priv) -{ - int rc; - - spin_lock_bh(&priv->lock); - rc = list_empty(&priv->tty_outqueue); - spin_unlock_bh(&priv->lock); - - return rc; -} - -/** - * flush_sndbuf_sync() - Flush send buffer and wait for completion - * @priv: Pointer to struct hvc_iucv_private instance. - * - * The routine cancels a pending sndbuf work, calls hvc_iucv_send() - * to flush any buffered terminal output data and waits for completion. - */ -static void flush_sndbuf_sync(struct hvc_iucv_private *priv) -{ - int sync_wait; - - cancel_delayed_work_sync(&priv->sndbuf_work); - - spin_lock_bh(&priv->lock); - hvc_iucv_send(priv); /* force sending buffered data */ - sync_wait = !list_empty(&priv->tty_outqueue); /* anything queued ? */ - spin_unlock_bh(&priv->lock); - - if (sync_wait) - wait_event_timeout(priv->sndbuf_waitq, - tty_outqueue_empty(priv), HZ/10); -} - -/** - * hvc_iucv_hangup() - Sever IUCV path and schedule hvc tty hang up - * @priv: Pointer to hvc_iucv_private structure - * - * This routine severs an existing IUCV communication path and hangs - * up the underlying HVC terminal device. - * The hang-up occurs only if an IUCV communication path is established; - * otherwise there is no need to hang up the terminal device. - * - * The IUCV HVC hang-up is separated into two steps: - * 1. After the IUCV path has been severed, the iucv_state is set to - * IUCV_SEVERED. - * 2. Later, when the HVC thread calls hvc_iucv_get_chars(), the - * IUCV_SEVERED state causes the tty hang-up in the HVC layer. - * - * If the tty has not yet been opened, clean up the hvc_iucv_private - * structure to allow re-connects. - * If the tty has been opened, let get_chars() return -EPIPE to signal - * the HVC layer to hang up the tty and, if so, wake up the HVC thread - * to call get_chars()... - * - * Special notes on hanging up a HVC terminal instantiated as console: - * Hang-up: 1. do_tty_hangup() replaces file ops (= hung_up_tty_fops) - * 2. do_tty_hangup() calls tty->ops->close() for console_filp - * => no hangup notifier is called by HVC (default) - * 2. hvc_close() returns because of tty_hung_up_p(filp) - * => no delete notifier is called! - * Finally, the back-end is not being notified, thus, the tty session is - * kept active (TTY_OPEN) to be ready for re-connects. - * - * Locking: spin_lock(&priv->lock) w/o disabling bh - */ -static void hvc_iucv_hangup(struct hvc_iucv_private *priv) -{ - struct iucv_path *path; - - path = NULL; - spin_lock(&priv->lock); - if (priv->iucv_state == IUCV_CONNECTED) { - path = priv->path; - priv->path = NULL; - priv->iucv_state = IUCV_SEVERED; - if (priv->tty_state == TTY_CLOSED) - hvc_iucv_cleanup(priv); - else - /* console is special (see above) */ - if (priv->is_console) { - hvc_iucv_cleanup(priv); - priv->tty_state = TTY_OPENED; - } else - hvc_kick(); - } - spin_unlock(&priv->lock); - - /* finally sever path (outside of priv->lock due to lock ordering) */ - if (path) { - iucv_path_sever(path, NULL); - iucv_path_free(path); - } -} - -/** - * hvc_iucv_notifier_hangup() - HVC notifier for TTY hangups. - * @hp: Pointer to the HVC device (struct hvc_struct) - * @id: Additional data (originally passed to hvc_alloc): - * the index of an struct hvc_iucv_private instance. - * - * This routine notifies the HVC back-end that a tty hangup (carrier loss, - * virtual or otherwise) has occured. - * The z/VM IUCV HVC device driver ignores virtual hangups (vhangup()) - * to keep an existing IUCV communication path established. - * (Background: vhangup() is called from user space (by getty or login) to - * disable writing to the tty by other applications). - * If the tty has been opened and an established IUCV path has been severed - * (we caused the tty hangup), the function calls hvc_iucv_cleanup(). - * - * Locking: struct hvc_iucv_private->lock - */ -static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) -{ - struct hvc_iucv_private *priv; - - priv = hvc_iucv_get_private(id); - if (!priv) - return; - - flush_sndbuf_sync(priv); - - spin_lock_bh(&priv->lock); - /* NOTE: If the hangup was scheduled by ourself (from the iucv - * path_servered callback [IUCV_SEVERED]), we have to clean up - * our structure and to set state to TTY_CLOSED. - * If the tty was hung up otherwise (e.g. vhangup()), then we - * ignore this hangup and keep an established IUCV path open... - * (...the reason is that we are not able to connect back to the - * client if we disconnect on hang up) */ - priv->tty_state = TTY_CLOSED; - - if (priv->iucv_state == IUCV_SEVERED) - hvc_iucv_cleanup(priv); - spin_unlock_bh(&priv->lock); -} - -/** - * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. - * @hp: Pointer to the HVC device (struct hvc_struct) - * @id: Additional data (originally passed to hvc_alloc): - * the index of an struct hvc_iucv_private instance. - * - * This routine notifies the HVC back-end that the last tty device fd has been - * closed. The function calls hvc_iucv_cleanup() to clean up the struct - * hvc_iucv_private instance. - * - * Locking: struct hvc_iucv_private->lock - */ -static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) -{ - struct hvc_iucv_private *priv; - struct iucv_path *path; - - priv = hvc_iucv_get_private(id); - if (!priv) - return; - - flush_sndbuf_sync(priv); - - spin_lock_bh(&priv->lock); - path = priv->path; /* save reference to IUCV path */ - priv->path = NULL; - hvc_iucv_cleanup(priv); - spin_unlock_bh(&priv->lock); - - /* sever IUCV path outside of priv->lock due to lock ordering of: - * priv->lock <--> iucv_table_lock */ - if (path) { - iucv_path_sever(path, NULL); - iucv_path_free(path); - } -} - -/** - * hvc_iucv_filter_connreq() - Filter connection request based on z/VM user ID - * @ipvmid: Originating z/VM user ID (right padded with blanks) - * - * Returns 0 if the z/VM user ID @ipvmid is allowed to connection, otherwise - * non-zero. - */ -static int hvc_iucv_filter_connreq(u8 ipvmid[8]) -{ - size_t i; - - /* Note: default policy is ACCEPT if no filter is set */ - if (!hvc_iucv_filter_size) - return 0; - - for (i = 0; i < hvc_iucv_filter_size; i++) - if (0 == memcmp(ipvmid, hvc_iucv_filter + (8 * i), 8)) - return 0; - return 1; -} - -/** - * hvc_iucv_path_pending() - IUCV handler to process a connection request. - * @path: Pending path (struct iucv_path) - * @ipvmid: z/VM system identifier of originator - * @ipuser: User specified data for this path - * (AF_IUCV: port/service name and originator port) - * - * The function uses the @ipuser data to determine if the pending path belongs - * to a terminal managed by this device driver. - * If the path belongs to this driver, ensure that the terminal is not accessed - * multiple times (only one connection to a terminal is allowed). - * If the terminal is not yet connected, the pending path is accepted and is - * associated to the appropriate struct hvc_iucv_private instance. - * - * Returns 0 if @path belongs to a terminal managed by the this device driver; - * otherwise returns -ENODEV in order to dispatch this path to other handlers. - * - * Locking: struct hvc_iucv_private->lock - */ -static int hvc_iucv_path_pending(struct iucv_path *path, - u8 ipvmid[8], u8 ipuser[16]) -{ - struct hvc_iucv_private *priv; - u8 nuser_data[16]; - u8 vm_user_id[9]; - int i, rc; - - priv = NULL; - for (i = 0; i < hvc_iucv_devices; i++) - if (hvc_iucv_table[i] && - (0 == memcmp(hvc_iucv_table[i]->srv_name, ipuser, 8))) { - priv = hvc_iucv_table[i]; - break; - } - if (!priv) - return -ENODEV; - - /* Enforce that ipvmid is allowed to connect to us */ - read_lock(&hvc_iucv_filter_lock); - rc = hvc_iucv_filter_connreq(ipvmid); - read_unlock(&hvc_iucv_filter_lock); - if (rc) { - iucv_path_sever(path, ipuser); - iucv_path_free(path); - memcpy(vm_user_id, ipvmid, 8); - vm_user_id[8] = 0; - pr_info("A connection request from z/VM user ID %s " - "was refused\n", vm_user_id); - return 0; - } - - spin_lock(&priv->lock); - - /* If the terminal is already connected or being severed, then sever - * this path to enforce that there is only ONE established communication - * path per terminal. */ - if (priv->iucv_state != IUCV_DISCONN) { - iucv_path_sever(path, ipuser); - iucv_path_free(path); - goto out_path_handled; - } - - /* accept path */ - memcpy(nuser_data, ipuser + 8, 8); /* remote service (for af_iucv) */ - memcpy(nuser_data + 8, ipuser, 8); /* local service (for af_iucv) */ - path->msglim = 0xffff; /* IUCV MSGLIMIT */ - path->flags &= ~IUCV_IPRMDATA; /* TODO: use IUCV_IPRMDATA */ - rc = iucv_path_accept(path, &hvc_iucv_handler, nuser_data, priv); - if (rc) { - iucv_path_sever(path, ipuser); - iucv_path_free(path); - goto out_path_handled; - } - priv->path = path; - priv->iucv_state = IUCV_CONNECTED; - - /* flush buffered output data... */ - schedule_delayed_work(&priv->sndbuf_work, 5); - -out_path_handled: - spin_unlock(&priv->lock); - return 0; -} - -/** - * hvc_iucv_path_severed() - IUCV handler to process a path sever. - * @path: Pending path (struct iucv_path) - * @ipuser: User specified data for this path - * (AF_IUCV: port/service name and originator port) - * - * This function calls the hvc_iucv_hangup() function for the - * respective IUCV HVC terminal. - * - * Locking: struct hvc_iucv_private->lock - */ -static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) -{ - struct hvc_iucv_private *priv = path->private; - - hvc_iucv_hangup(priv); -} - -/** - * hvc_iucv_msg_pending() - IUCV handler to process an incoming IUCV message. - * @path: Pending path (struct iucv_path) - * @msg: Pointer to the IUCV message - * - * The function puts an incoming message on the input queue for later - * processing (by hvc_iucv_get_chars() / hvc_iucv_write()). - * If the tty has not yet been opened, the message is rejected. - * - * Locking: struct hvc_iucv_private->lock - */ -static void hvc_iucv_msg_pending(struct iucv_path *path, - struct iucv_message *msg) -{ - struct hvc_iucv_private *priv = path->private; - struct iucv_tty_buffer *rb; - - /* reject messages that exceed max size of iucv_tty_msg->datalen */ - if (msg->length > MSG_SIZE(MSG_MAX_DATALEN)) { - iucv_message_reject(path, msg); - return; - } - - spin_lock(&priv->lock); - - /* reject messages if tty has not yet been opened */ - if (priv->tty_state == TTY_CLOSED) { - iucv_message_reject(path, msg); - goto unlock_return; - } - - /* allocate tty buffer to save iucv msg only */ - rb = alloc_tty_buffer(0, GFP_ATOMIC); - if (!rb) { - iucv_message_reject(path, msg); - goto unlock_return; /* -ENOMEM */ - } - rb->msg = *msg; - - list_add_tail(&rb->list, &priv->tty_inqueue); - - hvc_kick(); /* wake up hvc thread */ - -unlock_return: - spin_unlock(&priv->lock); -} - -/** - * hvc_iucv_msg_complete() - IUCV handler to process message completion - * @path: Pending path (struct iucv_path) - * @msg: Pointer to the IUCV message - * - * The function is called upon completion of message delivery to remove the - * message from the outqueue. Additional delivery information can be found - * msg->audit: rejected messages (0x040000 (IPADRJCT)), and - * purged messages (0x010000 (IPADPGNR)). - * - * Locking: struct hvc_iucv_private->lock - */ -static void hvc_iucv_msg_complete(struct iucv_path *path, - struct iucv_message *msg) -{ - struct hvc_iucv_private *priv = path->private; - struct iucv_tty_buffer *ent, *next; - LIST_HEAD(list_remove); - - spin_lock(&priv->lock); - list_for_each_entry_safe(ent, next, &priv->tty_outqueue, list) - if (ent->msg.id == msg->id) { - list_move(&ent->list, &list_remove); - break; - } - wake_up(&priv->sndbuf_waitq); - spin_unlock(&priv->lock); - destroy_tty_buffer_list(&list_remove); -} - -/** - * hvc_iucv_pm_freeze() - Freeze PM callback - * @dev: IUVC HVC terminal device - * - * Sever an established IUCV communication path and - * trigger a hang-up of the underlying HVC terminal. - */ -static int hvc_iucv_pm_freeze(struct device *dev) -{ - struct hvc_iucv_private *priv = dev_get_drvdata(dev); - - local_bh_disable(); - hvc_iucv_hangup(priv); - local_bh_enable(); - - return 0; -} - -/** - * hvc_iucv_pm_restore_thaw() - Thaw and restore PM callback - * @dev: IUVC HVC terminal device - * - * Wake up the HVC thread to trigger hang-up and respective - * HVC back-end notifier invocations. - */ -static int hvc_iucv_pm_restore_thaw(struct device *dev) -{ - hvc_kick(); - return 0; -} - - -/* HVC operations */ -static const struct hv_ops hvc_iucv_ops = { - .get_chars = hvc_iucv_get_chars, - .put_chars = hvc_iucv_put_chars, - .notifier_add = hvc_iucv_notifier_add, - .notifier_del = hvc_iucv_notifier_del, - .notifier_hangup = hvc_iucv_notifier_hangup, -}; - -/* Suspend / resume device operations */ -static const struct dev_pm_ops hvc_iucv_pm_ops = { - .freeze = hvc_iucv_pm_freeze, - .thaw = hvc_iucv_pm_restore_thaw, - .restore = hvc_iucv_pm_restore_thaw, -}; - -/* IUCV HVC device driver */ -static struct device_driver hvc_iucv_driver = { - .name = KMSG_COMPONENT, - .bus = &iucv_bus, - .pm = &hvc_iucv_pm_ops, -}; - -/** - * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance - * @id: hvc_iucv_table index - * @is_console: Flag if the instance is used as Linux console - * - * This function allocates a new hvc_iucv_private structure and stores - * the instance in hvc_iucv_table at index @id. - * Returns 0 on success; otherwise non-zero. - */ -static int __init hvc_iucv_alloc(int id, unsigned int is_console) -{ - struct hvc_iucv_private *priv; - char name[9]; - int rc; - - priv = kzalloc(sizeof(struct hvc_iucv_private), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - spin_lock_init(&priv->lock); - INIT_LIST_HEAD(&priv->tty_outqueue); - INIT_LIST_HEAD(&priv->tty_inqueue); - INIT_DELAYED_WORK(&priv->sndbuf_work, hvc_iucv_sndbuf_work); - init_waitqueue_head(&priv->sndbuf_waitq); - - priv->sndbuf = (void *) get_zeroed_page(GFP_KERNEL); - if (!priv->sndbuf) { - kfree(priv); - return -ENOMEM; - } - - /* set console flag */ - priv->is_console = is_console; - - /* allocate hvc device */ - priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /* PAGE_SIZE */ - HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256); - if (IS_ERR(priv->hvc)) { - rc = PTR_ERR(priv->hvc); - goto out_error_hvc; - } - - /* notify HVC thread instead of using polling */ - priv->hvc->irq_requested = 1; - - /* setup iucv related information */ - snprintf(name, 9, "lnxhvc%-2d", id); - memcpy(priv->srv_name, name, 8); - ASCEBC(priv->srv_name, 8); - - /* create and setup device */ - priv->dev = kzalloc(sizeof(*priv->dev), GFP_KERNEL); - if (!priv->dev) { - rc = -ENOMEM; - goto out_error_dev; - } - dev_set_name(priv->dev, "hvc_iucv%d", id); - dev_set_drvdata(priv->dev, priv); - priv->dev->bus = &iucv_bus; - priv->dev->parent = iucv_root; - priv->dev->driver = &hvc_iucv_driver; - priv->dev->release = (void (*)(struct device *)) kfree; - rc = device_register(priv->dev); - if (rc) { - put_device(priv->dev); - goto out_error_dev; - } - - hvc_iucv_table[id] = priv; - return 0; - -out_error_dev: - hvc_remove(priv->hvc); -out_error_hvc: - free_page((unsigned long) priv->sndbuf); - kfree(priv); - - return rc; -} - -/** - * hvc_iucv_destroy() - Destroy and free hvc_iucv_private instances - */ -static void __init hvc_iucv_destroy(struct hvc_iucv_private *priv) -{ - hvc_remove(priv->hvc); - device_unregister(priv->dev); - free_page((unsigned long) priv->sndbuf); - kfree(priv); -} - -/** - * hvc_iucv_parse_filter() - Parse filter for a single z/VM user ID - * @filter: String containing a comma-separated list of z/VM user IDs - */ -static const char *hvc_iucv_parse_filter(const char *filter, char *dest) -{ - const char *nextdelim, *residual; - size_t len; - - nextdelim = strchr(filter, ','); - if (nextdelim) { - len = nextdelim - filter; - residual = nextdelim + 1; - } else { - len = strlen(filter); - residual = filter + len; - } - - if (len == 0) - return ERR_PTR(-EINVAL); - - /* check for '\n' (if called from sysfs) */ - if (filter[len - 1] == '\n') - len--; - - if (len > 8) - return ERR_PTR(-EINVAL); - - /* pad with blanks and save upper case version of user ID */ - memset(dest, ' ', 8); - while (len--) - dest[len] = toupper(filter[len]); - return residual; -} - -/** - * hvc_iucv_setup_filter() - Set up z/VM user ID filter - * @filter: String consisting of a comma-separated list of z/VM user IDs - * - * The function parses the @filter string and creates an array containing - * the list of z/VM user ID filter entries. - * Return code 0 means success, -EINVAL if the filter is syntactically - * incorrect, -ENOMEM if there was not enough memory to allocate the - * filter list array, or -ENOSPC if too many z/VM user IDs have been specified. - */ -static int hvc_iucv_setup_filter(const char *val) -{ - const char *residual; - int err; - size_t size, count; - void *array, *old_filter; - - count = strlen(val); - if (count == 0 || (count == 1 && val[0] == '\n')) { - size = 0; - array = NULL; - goto out_replace_filter; /* clear filter */ - } - - /* count user IDs in order to allocate sufficient memory */ - size = 1; - residual = val; - while ((residual = strchr(residual, ',')) != NULL) { - residual++; - size++; - } - - /* check if the specified list exceeds the filter limit */ - if (size > MAX_VMID_FILTER) - return -ENOSPC; - - array = kzalloc(size * 8, GFP_KERNEL); - if (!array) - return -ENOMEM; - - count = size; - residual = val; - while (*residual && count) { - residual = hvc_iucv_parse_filter(residual, - array + ((size - count) * 8)); - if (IS_ERR(residual)) { - err = PTR_ERR(residual); - kfree(array); - goto out_err; - } - count--; - } - -out_replace_filter: - write_lock_bh(&hvc_iucv_filter_lock); - old_filter = hvc_iucv_filter; - hvc_iucv_filter_size = size; - hvc_iucv_filter = array; - write_unlock_bh(&hvc_iucv_filter_lock); - kfree(old_filter); - - err = 0; -out_err: - return err; -} - -/** - * param_set_vmidfilter() - Set z/VM user ID filter parameter - * @val: String consisting of a comma-separated list of z/VM user IDs - * @kp: Kernel parameter pointing to hvc_iucv_filter array - * - * The function sets up the z/VM user ID filter specified as comma-separated - * list of user IDs in @val. - * Note: If it is called early in the boot process, @val is stored and - * parsed later in hvc_iucv_init(). - */ -static int param_set_vmidfilter(const char *val, const struct kernel_param *kp) -{ - int rc; - - if (!MACHINE_IS_VM || !hvc_iucv_devices) - return -ENODEV; - - if (!val) - return -EINVAL; - - rc = 0; - if (slab_is_available()) - rc = hvc_iucv_setup_filter(val); - else - hvc_iucv_filter_string = val; /* defer... */ - return rc; -} - -/** - * param_get_vmidfilter() - Get z/VM user ID filter - * @buffer: Buffer to store z/VM user ID filter, - * (buffer size assumption PAGE_SIZE) - * @kp: Kernel parameter pointing to the hvc_iucv_filter array - * - * The function stores the filter as a comma-separated list of z/VM user IDs - * in @buffer. Typically, sysfs routines call this function for attr show. - */ -static int param_get_vmidfilter(char *buffer, const struct kernel_param *kp) -{ - int rc; - size_t index, len; - void *start, *end; - - if (!MACHINE_IS_VM || !hvc_iucv_devices) - return -ENODEV; - - rc = 0; - read_lock_bh(&hvc_iucv_filter_lock); - for (index = 0; index < hvc_iucv_filter_size; index++) { - start = hvc_iucv_filter + (8 * index); - end = memchr(start, ' ', 8); - len = (end) ? end - start : 8; - memcpy(buffer + rc, start, len); - rc += len; - buffer[rc++] = ','; - } - read_unlock_bh(&hvc_iucv_filter_lock); - if (rc) - buffer[--rc] = '\0'; /* replace last comma and update rc */ - return rc; -} - -#define param_check_vmidfilter(name, p) __param_check(name, p, void) - -static struct kernel_param_ops param_ops_vmidfilter = { - .set = param_set_vmidfilter, - .get = param_get_vmidfilter, -}; - -/** - * hvc_iucv_init() - z/VM IUCV HVC device driver initialization - */ -static int __init hvc_iucv_init(void) -{ - int rc; - unsigned int i; - - if (!hvc_iucv_devices) - return -ENODEV; - - if (!MACHINE_IS_VM) { - pr_notice("The z/VM IUCV HVC device driver cannot " - "be used without z/VM\n"); - rc = -ENODEV; - goto out_error; - } - - if (hvc_iucv_devices > MAX_HVC_IUCV_LINES) { - pr_err("%lu is not a valid value for the hvc_iucv= " - "kernel parameter\n", hvc_iucv_devices); - rc = -EINVAL; - goto out_error; - } - - /* register IUCV HVC device driver */ - rc = driver_register(&hvc_iucv_driver); - if (rc) - goto out_error; - - /* parse hvc_iucv_allow string and create z/VM user ID filter list */ - if (hvc_iucv_filter_string) { - rc = hvc_iucv_setup_filter(hvc_iucv_filter_string); - switch (rc) { - case 0: - break; - case -ENOMEM: - pr_err("Allocating memory failed with " - "reason code=%d\n", 3); - goto out_error; - case -EINVAL: - pr_err("hvc_iucv_allow= does not specify a valid " - "z/VM user ID list\n"); - goto out_error; - case -ENOSPC: - pr_err("hvc_iucv_allow= specifies too many " - "z/VM user IDs\n"); - goto out_error; - default: - goto out_error; - } - } - - hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT, - sizeof(struct iucv_tty_buffer), - 0, 0, NULL); - if (!hvc_iucv_buffer_cache) { - pr_err("Allocating memory failed with reason code=%d\n", 1); - rc = -ENOMEM; - goto out_error; - } - - hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR, - hvc_iucv_buffer_cache); - if (!hvc_iucv_mempool) { - pr_err("Allocating memory failed with reason code=%d\n", 2); - kmem_cache_destroy(hvc_iucv_buffer_cache); - rc = -ENOMEM; - goto out_error; - } - - /* register the first terminal device as console - * (must be done before allocating hvc terminal devices) */ - rc = hvc_instantiate(HVC_IUCV_MAGIC, IUCV_HVC_CON_IDX, &hvc_iucv_ops); - if (rc) { - pr_err("Registering HVC terminal device as " - "Linux console failed\n"); - goto out_error_memory; - } - - /* allocate hvc_iucv_private structs */ - for (i = 0; i < hvc_iucv_devices; i++) { - rc = hvc_iucv_alloc(i, (i == IUCV_HVC_CON_IDX) ? 1 : 0); - if (rc) { - pr_err("Creating a new HVC terminal device " - "failed with error code=%d\n", rc); - goto out_error_hvc; - } - } - - /* register IUCV callback handler */ - rc = iucv_register(&hvc_iucv_handler, 0); - if (rc) { - pr_err("Registering IUCV handlers failed with error code=%d\n", - rc); - goto out_error_hvc; - } - - return 0; - -out_error_hvc: - for (i = 0; i < hvc_iucv_devices; i++) - if (hvc_iucv_table[i]) - hvc_iucv_destroy(hvc_iucv_table[i]); -out_error_memory: - mempool_destroy(hvc_iucv_mempool); - kmem_cache_destroy(hvc_iucv_buffer_cache); -out_error: - if (hvc_iucv_filter) - kfree(hvc_iucv_filter); - hvc_iucv_devices = 0; /* ensure that we do not provide any device */ - return rc; -} - -/** - * hvc_iucv_config() - Parsing of hvc_iucv= kernel command line parameter - * @val: Parameter value (numeric) - */ -static int __init hvc_iucv_config(char *val) -{ - return strict_strtoul(val, 10, &hvc_iucv_devices); -} - - -device_initcall(hvc_iucv_init); -__setup("hvc_iucv=", hvc_iucv_config); -core_param(hvc_iucv_allow, hvc_iucv_filter, vmidfilter, 0640); diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c deleted file mode 100644 index 61c4a61558d9..000000000000 --- a/drivers/char/hvc_rtas.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * IBM RTAS driver interface to hvc_console.c - * - * (C) Copyright IBM Corporation 2001-2005 - * (C) Copyright Red Hat, Inc. 2005 - * - * Author(s): Maximino Augilar <IBM STI Design Center> - * : Ryan S. Arnold <rsa@us.ibm.com> - * : Utz Bacher <utz.bacher@de.ibm.com> - * : David Woodhouse <dwmw2@infradead.org> - * - * inspired by drivers/char/hvc_console.c - * written by Anton Blanchard and Paul Mackerras - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/console.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/init.h> -#include <linux/moduleparam.h> -#include <linux/types.h> - -#include <asm/irq.h> -#include <asm/rtas.h> -#include "hvc_console.h" - -#define hvc_rtas_cookie 0x67781e15 -struct hvc_struct *hvc_rtas_dev; - -static int rtascons_put_char_token = RTAS_UNKNOWN_SERVICE; -static int rtascons_get_char_token = RTAS_UNKNOWN_SERVICE; - -static inline int hvc_rtas_write_console(uint32_t vtermno, const char *buf, - int count) -{ - int i; - - for (i = 0; i < count; i++) { - if (rtas_call(rtascons_put_char_token, 1, 1, NULL, buf[i])) - break; - } - - return i; -} - -static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count) -{ - int i, c; - - for (i = 0; i < count; i++) { - if (rtas_call(rtascons_get_char_token, 0, 2, &c)) - break; - - buf[i] = c; - } - - return i; -} - -static const struct hv_ops hvc_rtas_get_put_ops = { - .get_chars = hvc_rtas_read_console, - .put_chars = hvc_rtas_write_console, -}; - -static int __init hvc_rtas_init(void) -{ - struct hvc_struct *hp; - - if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) - rtascons_put_char_token = rtas_token("put-term-char"); - if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) - return -EIO; - - if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE) - rtascons_get_char_token = rtas_token("get-term-char"); - if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE) - return -EIO; - - BUG_ON(hvc_rtas_dev); - - /* Allocate an hvc_struct for the console device we instantiated - * earlier. Save off hp so that we can return it on exit */ - hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops, 16); - if (IS_ERR(hp)) - return PTR_ERR(hp); - - hvc_rtas_dev = hp; - - return 0; -} -module_init(hvc_rtas_init); - -/* This will tear down the tty portion of the driver */ -static void __exit hvc_rtas_exit(void) -{ - /* Really the fun isn't over until the worker thread breaks down and - * the tty cleans up */ - if (hvc_rtas_dev) - hvc_remove(hvc_rtas_dev); -} -module_exit(hvc_rtas_exit); - -/* This will happen prior to module init. There is no tty at this time? */ -static int __init hvc_rtas_console_init(void) -{ - rtascons_put_char_token = rtas_token("put-term-char"); - if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) - return -EIO; - - rtascons_get_char_token = rtas_token("get-term-char"); - if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE) - return -EIO; - - hvc_instantiate(hvc_rtas_cookie, 0, &hvc_rtas_get_put_ops); - add_preferred_console("hvc", 0, NULL); - - return 0; -} -console_initcall(hvc_rtas_console_init); diff --git a/drivers/char/hvc_tile.c b/drivers/char/hvc_tile.c deleted file mode 100644 index 7a84a0595477..000000000000 --- a/drivers/char/hvc_tile.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2010 Tilera Corporation. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, version 2. - * - * 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, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for - * more details. - * - * Tilera TILE Processor hypervisor console - */ - -#include <linux/console.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/init.h> -#include <linux/moduleparam.h> -#include <linux/types.h> - -#include <hv/hypervisor.h> - -#include "hvc_console.h" - -static int hvc_tile_put_chars(uint32_t vt, const char *buf, int count) -{ - return hv_console_write((HV_VirtAddr)buf, count); -} - -static int hvc_tile_get_chars(uint32_t vt, char *buf, int count) -{ - int i, c; - - for (i = 0; i < count; ++i) { - c = hv_console_read_if_ready(); - if (c < 0) - break; - buf[i] = c; - } - - return i; -} - -static const struct hv_ops hvc_tile_get_put_ops = { - .get_chars = hvc_tile_get_chars, - .put_chars = hvc_tile_put_chars, -}; - -static int __init hvc_tile_console_init(void) -{ - extern void disable_early_printk(void); - hvc_instantiate(0, 0, &hvc_tile_get_put_ops); - add_preferred_console("hvc", 0, NULL); - disable_early_printk(); - return 0; -} -console_initcall(hvc_tile_console_init); - -static int __init hvc_tile_init(void) -{ - struct hvc_struct *s; - s = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); - return IS_ERR(s) ? PTR_ERR(s) : 0; -} -device_initcall(hvc_tile_init); diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c deleted file mode 100644 index b0957e61a7be..000000000000 --- a/drivers/char/hvc_udbg.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * udbg interface to hvc_console.c - * - * (C) Copyright David Gibson, IBM Corporation 2008. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/console.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/init.h> -#include <linux/moduleparam.h> -#include <linux/types.h> -#include <linux/irq.h> - -#include <asm/udbg.h> - -#include "hvc_console.h" - -struct hvc_struct *hvc_udbg_dev; - -static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count) -{ - int i; - - for (i = 0; i < count; i++) - udbg_putc(buf[i]); - - return i; -} - -static int hvc_udbg_get(uint32_t vtermno, char *buf, int count) -{ - int i, c; - - if (!udbg_getc_poll) - return 0; - - for (i = 0; i < count; i++) { - if ((c = udbg_getc_poll()) == -1) - break; - buf[i] = c; - } - - return i; -} - -static const struct hv_ops hvc_udbg_ops = { - .get_chars = hvc_udbg_get, - .put_chars = hvc_udbg_put, -}; - -static int __init hvc_udbg_init(void) -{ - struct hvc_struct *hp; - - BUG_ON(hvc_udbg_dev); - - hp = hvc_alloc(0, NO_IRQ, &hvc_udbg_ops, 16); - if (IS_ERR(hp)) - return PTR_ERR(hp); - - hvc_udbg_dev = hp; - - return 0; -} -module_init(hvc_udbg_init); - -static void __exit hvc_udbg_exit(void) -{ - if (hvc_udbg_dev) - hvc_remove(hvc_udbg_dev); -} -module_exit(hvc_udbg_exit); - -static int __init hvc_udbg_console_init(void) -{ - hvc_instantiate(0, 0, &hvc_udbg_ops); - add_preferred_console("hvc", 0, NULL); - - return 0; -} -console_initcall(hvc_udbg_console_init); diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c deleted file mode 100644 index 27370e99c66f..000000000000 --- a/drivers/char/hvc_vio.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * vio driver interface to hvc_console.c - * - * This code was moved here to allow the remaing code to be reused as a - * generic polling mode with semi-reliable transport driver core to the - * console and tty subsystems. - * - * - * Copyright (C) 2001 Anton Blanchard <anton@au.ibm.com>, IBM - * Copyright (C) 2001 Paul Mackerras <paulus@au.ibm.com>, IBM - * Copyright (C) 2004 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. - * Copyright (C) 2004 IBM Corporation - * - * Additional Author(s): - * Ryan S. Arnold <rsa@us.ibm.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/types.h> -#include <linux/init.h> - -#include <asm/hvconsole.h> -#include <asm/vio.h> -#include <asm/prom.h> -#include <asm/firmware.h> - -#include "hvc_console.h" - -char hvc_driver_name[] = "hvc_console"; - -static struct vio_device_id hvc_driver_table[] __devinitdata = { - {"serial", "hvterm1"}, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, hvc_driver_table); - -static int filtered_get_chars(uint32_t vtermno, char *buf, int count) -{ - unsigned long got; - int i; - - /* - * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion - * so we play safe and avoid the situation where got > count which could - * overload the flip buffer. - */ - if (count < SIZE_VIO_GET_CHARS) - return -EAGAIN; - - got = hvc_get_chars(vtermno, buf, count); - - /* - * Work around a HV bug where it gives us a null - * after every \r. -- paulus - */ - for (i = 1; i < got; ++i) { - if (buf[i] == 0 && buf[i-1] == '\r') { - --got; - if (i < got) - memmove(&buf[i], &buf[i+1], - got - i); - } - } - return got; -} - -static const struct hv_ops hvc_get_put_ops = { - .get_chars = filtered_get_chars, - .put_chars = hvc_put_chars, - .notifier_add = notifier_add_irq, - .notifier_del = notifier_del_irq, - .notifier_hangup = notifier_hangup_irq, -}; - -static int __devinit hvc_vio_probe(struct vio_dev *vdev, - const struct vio_device_id *id) -{ - struct hvc_struct *hp; - - /* probed with invalid parameters. */ - if (!vdev || !id) - return -EPERM; - - hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, - MAX_VIO_PUT_CHARS); - if (IS_ERR(hp)) - return PTR_ERR(hp); - dev_set_drvdata(&vdev->dev, hp); - - return 0; -} - -static int __devexit hvc_vio_remove(struct vio_dev *vdev) -{ - struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); - - return hvc_remove(hp); -} - -static struct vio_driver hvc_vio_driver = { - .id_table = hvc_driver_table, - .probe = hvc_vio_probe, - .remove = __devexit_p(hvc_vio_remove), - .driver = { - .name = hvc_driver_name, - .owner = THIS_MODULE, - } -}; - -static int __init hvc_vio_init(void) -{ - int rc; - - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return -EIO; - - /* Register as a vio device to receive callbacks */ - rc = vio_register_driver(&hvc_vio_driver); - - return rc; -} -module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ - -static void __exit hvc_vio_exit(void) -{ - vio_unregister_driver(&hvc_vio_driver); -} -module_exit(hvc_vio_exit); - -/* the device tree order defines our numbering */ -static int hvc_find_vtys(void) -{ - struct device_node *vty; - int num_found = 0; - - for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; - vty = of_find_node_by_name(vty, "vty")) { - const uint32_t *vtermno; - - /* We have statically defined space for only a certain number - * of console adapters. - */ - if (num_found >= MAX_NR_HVC_CONSOLES) { - of_node_put(vty); - break; - } - - vtermno = of_get_property(vty, "reg", NULL); - if (!vtermno) - continue; - - if (of_device_is_compatible(vty, "hvterm1")) { - hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); - ++num_found; - } - } - - return num_found; -} -console_initcall(hvc_find_vtys); diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c deleted file mode 100644 index 3740e327f180..000000000000 --- a/drivers/char/hvc_xen.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * xen console driver interface to hvc_console.c - * - * (c) 2007 Gerd Hoffmann <kraxel@suse.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/console.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/init.h> -#include <linux/types.h> - -#include <asm/xen/hypervisor.h> - -#include <xen/xen.h> -#include <xen/page.h> -#include <xen/events.h> -#include <xen/interface/io/console.h> -#include <xen/hvc-console.h> - -#include "hvc_console.h" - -#define HVC_COOKIE 0x58656e /* "Xen" in hex */ - -static struct hvc_struct *hvc; -static int xencons_irq; - -/* ------------------------------------------------------------------ */ - -static unsigned long console_pfn = ~0ul; - -static inline struct xencons_interface *xencons_interface(void) -{ - if (console_pfn == ~0ul) - return mfn_to_virt(xen_start_info->console.domU.mfn); - else - return __va(console_pfn << PAGE_SHIFT); -} - -static inline void notify_daemon(void) -{ - /* Use evtchn: this is called early, before irq is set up. */ - notify_remote_via_evtchn(xen_start_info->console.domU.evtchn); -} - -static int __write_console(const char *data, int len) -{ - struct xencons_interface *intf = xencons_interface(); - XENCONS_RING_IDX cons, prod; - int sent = 0; - - cons = intf->out_cons; - prod = intf->out_prod; - mb(); /* update queue values before going on */ - BUG_ON((prod - cons) > sizeof(intf->out)); - - while ((sent < len) && ((prod - cons) < sizeof(intf->out))) - intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++]; - - wmb(); /* write ring before updating pointer */ - intf->out_prod = prod; - - if (sent) - notify_daemon(); - return sent; -} - -static int domU_write_console(uint32_t vtermno, const char *data, int len) -{ - int ret = len; - - /* - * Make sure the whole buffer is emitted, polling if - * necessary. We don't ever want to rely on the hvc daemon - * because the most interesting console output is when the - * kernel is crippled. - */ - while (len) { - int sent = __write_console(data, len); - - data += sent; - len -= sent; - - if (unlikely(len)) - HYPERVISOR_sched_op(SCHEDOP_yield, NULL); - } - - return ret; -} - -static int domU_read_console(uint32_t vtermno, char *buf, int len) -{ - struct xencons_interface *intf = xencons_interface(); - XENCONS_RING_IDX cons, prod; - int recv = 0; - - cons = intf->in_cons; - prod = intf->in_prod; - mb(); /* get pointers before reading ring */ - BUG_ON((prod - cons) > sizeof(intf->in)); - - while (cons != prod && recv < len) - buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)]; - - mb(); /* read ring before consuming */ - intf->in_cons = cons; - - notify_daemon(); - return recv; -} - -static struct hv_ops domU_hvc_ops = { - .get_chars = domU_read_console, - .put_chars = domU_write_console, - .notifier_add = notifier_add_irq, - .notifier_del = notifier_del_irq, - .notifier_hangup = notifier_hangup_irq, -}; - -static int dom0_read_console(uint32_t vtermno, char *buf, int len) -{ - return HYPERVISOR_console_io(CONSOLEIO_read, len, buf); -} - -/* - * Either for a dom0 to write to the system console, or a domU with a - * debug version of Xen - */ -static int dom0_write_console(uint32_t vtermno, const char *str, int len) -{ - int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); - if (rc < 0) - return 0; - - return len; -} - -static struct hv_ops dom0_hvc_ops = { - .get_chars = dom0_read_console, - .put_chars = dom0_write_console, - .notifier_add = notifier_add_irq, - .notifier_del = notifier_del_irq, - .notifier_hangup = notifier_hangup_irq, -}; - -static int __init xen_hvc_init(void) -{ - struct hvc_struct *hp; - struct hv_ops *ops; - - if (!xen_pv_domain()) - return -ENODEV; - - if (xen_initial_domain()) { - ops = &dom0_hvc_ops; - xencons_irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); - } else { - if (!xen_start_info->console.domU.evtchn) - return -ENODEV; - - ops = &domU_hvc_ops; - xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn); - } - if (xencons_irq < 0) - xencons_irq = 0; /* NO_IRQ */ - - hp = hvc_alloc(HVC_COOKIE, xencons_irq, ops, 256); - if (IS_ERR(hp)) - return PTR_ERR(hp); - - hvc = hp; - - console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn); - - return 0; -} - -void xen_console_resume(void) -{ - if (xencons_irq) - rebind_evtchn_irq(xen_start_info->console.domU.evtchn, xencons_irq); -} - -static void __exit xen_hvc_fini(void) -{ - if (hvc) - hvc_remove(hvc); -} - -static int xen_cons_init(void) -{ - struct hv_ops *ops; - - if (!xen_pv_domain()) - return 0; - - if (xen_initial_domain()) - ops = &dom0_hvc_ops; - else - ops = &domU_hvc_ops; - - hvc_instantiate(HVC_COOKIE, 0, ops); - return 0; -} - -module_init(xen_hvc_init); -module_exit(xen_hvc_fini); -console_initcall(xen_cons_init); - -#ifdef CONFIG_EARLY_PRINTK -static void xenboot_write_console(struct console *console, const char *string, - unsigned len) -{ - unsigned int linelen, off = 0; - const char *pos; - - dom0_write_console(0, string, len); - - if (xen_initial_domain()) - return; - - domU_write_console(0, "(early) ", 8); - while (off < len && NULL != (pos = strchr(string+off, '\n'))) { - linelen = pos-string+off; - if (off + linelen > len) - break; - domU_write_console(0, string+off, linelen); - domU_write_console(0, "\r\n", 2); - off += linelen + 1; - } - if (off < len) - domU_write_console(0, string+off, len-off); -} - -struct console xenboot_console = { - .name = "xenboot", - .write = xenboot_write_console, - .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, -}; -#endif /* CONFIG_EARLY_PRINTK */ - -void xen_raw_console_write(const char *str) -{ - dom0_write_console(0, str, strlen(str)); -} - -void xen_raw_printk(const char *fmt, ...) -{ - static char buf[512]; - va_list ap; - - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - xen_raw_console_write(buf); -} diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c deleted file mode 100644 index bedc6c1b6fa5..000000000000 --- a/drivers/char/hvcs.c +++ /dev/null @@ -1,1604 +0,0 @@ -/* - * IBM eServer Hypervisor Virtual Console Server Device Driver - * Copyright (C) 2003, 2004 IBM Corp. - * Ryan S. Arnold (rsa@us.ibm.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author(s) : Ryan S. Arnold <rsa@us.ibm.com> - * - * This is the device driver for the IBM Hypervisor Virtual Console Server, - * "hvcs". The IBM hvcs provides a tty driver interface to allow Linux - * user space applications access to the system consoles of logically - * partitioned operating systems, e.g. Linux, running on the same partitioned - * Power5 ppc64 system. Physical hardware consoles per partition are not - * practical on this hardware so system consoles are accessed by this driver - * using inter-partition firmware interfaces to virtual terminal devices. - * - * A vty is known to the HMC as a "virtual serial server adapter". It is a - * virtual terminal device that is created by firmware upon partition creation - * to act as a partitioned OS's console device. - * - * Firmware dynamically (via hotplug) exposes vty-servers to a running ppc64 - * Linux system upon their creation by the HMC or their exposure during boot. - * The non-user interactive backend of this driver is implemented as a vio - * device driver so that it can receive notification of vty-server lifetimes - * after it registers with the vio bus to handle vty-server probe and remove - * callbacks. - * - * Many vty-servers can be configured to connect to one vty, but a vty can - * only be actively connected to by a single vty-server, in any manner, at one - * time. If the HMC is currently hosting the console for a target Linux - * partition; attempts to open the tty device to the partition's console using - * the hvcs on any partition will return -EBUSY with every open attempt until - * the HMC frees the connection between its vty-server and the desired - * partition's vty device. Conversely, a vty-server may only be connected to - * a single vty at one time even though it may have several configured vty - * partner possibilities. - * - * Firmware does not provide notification of vty partner changes to this - * driver. This means that an HMC Super Admin may add or remove partner vtys - * from a vty-server's partner list but the changes will not be signaled to - * the vty-server. Firmware only notifies the driver when a vty-server is - * added or removed from the system. To compensate for this deficiency, this - * driver implements a sysfs update attribute which provides a method for - * rescanning partner information upon a user's request. - * - * Each vty-server, prior to being exposed to this driver is reference counted - * using the 2.6 Linux kernel kref construct. - * - * For direction on installation and usage of this driver please reference - * Documentation/powerpc/hvcs.txt. - */ - -#include <linux/device.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/kref.h> -#include <linux/kthread.h> -#include <linux/list.h> -#include <linux/major.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/stat.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <asm/hvconsole.h> -#include <asm/hvcserver.h> -#include <asm/uaccess.h> -#include <asm/vio.h> - -/* - * 1.3.0 -> 1.3.1 In hvcs_open memset(..,0x00,..) instead of memset(..,0x3F,00). - * Removed braces around single statements following conditionals. Removed '= - * 0' after static int declarations since these default to zero. Removed - * list_for_each_safe() and replaced with list_for_each_entry() in - * hvcs_get_by_index(). The 'safe' version is un-needed now that the driver is - * using spinlocks. Changed spin_lock_irqsave() to spin_lock() when locking - * hvcs_structs_lock and hvcs_pi_lock since these are not touched in an int - * handler. Initialized hvcs_structs_lock and hvcs_pi_lock to - * SPIN_LOCK_UNLOCKED at declaration time rather than in hvcs_module_init(). - * Added spin_lock around list_del() in destroy_hvcs_struct() to protect the - * list traversals from a deletion. Removed '= NULL' from pointer declaration - * statements since they are initialized NULL by default. Removed wmb() - * instances from hvcs_try_write(). They probably aren't needed with locking in - * place. Added check and cleanup for hvcs_pi_buff = kmalloc() in - * hvcs_module_init(). Exposed hvcs_struct.index via a sysfs attribute so that - * the coupling between /dev/hvcs* and a vty-server can be automatically - * determined. Moved kobject_put() in hvcs_open outside of the - * spin_unlock_irqrestore(). - * - * 1.3.1 -> 1.3.2 Changed method for determining hvcs_struct->index and had it - * align with how the tty layer always assigns the lowest index available. This - * change resulted in a list of ints that denotes which indexes are available. - * Device additions and removals use the new hvcs_get_index() and - * hvcs_return_index() helper functions. The list is created with - * hvsc_alloc_index_list() and it is destroyed with hvcs_free_index_list(). - * Without these fixes hotplug vty-server adapter support goes crazy with this - * driver if the user removes a vty-server adapter. Moved free_irq() outside of - * the hvcs_final_close() function in order to get it out of the spinlock. - * Rearranged hvcs_close(). Cleaned up some printks and did some housekeeping - * on the changelog. Removed local CLC_LENGTH and used HVCS_CLC_LENGTH from - * arch/powerepc/include/asm/hvcserver.h - * - * 1.3.2 -> 1.3.3 Replaced yield() in hvcs_close() with tty_wait_until_sent() to - * prevent possible lockup with realtime scheduling as similarily pointed out by - * akpm in hvc_console. Changed resulted in the removal of hvcs_final_close() - * to reorder cleanup operations and prevent discarding of pending data during - * an hvcs_close(). Removed spinlock protection of hvcs_struct data members in - * hvcs_write_room() and hvcs_chars_in_buffer() because they aren't needed. - */ - -#define HVCS_DRIVER_VERSION "1.3.3" - -MODULE_AUTHOR("Ryan S. Arnold <rsa@us.ibm.com>"); -MODULE_DESCRIPTION("IBM hvcs (Hypervisor Virtual Console Server) Driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(HVCS_DRIVER_VERSION); - -/* - * Wait this long per iteration while trying to push buffered data to the - * hypervisor before allowing the tty to complete a close operation. - */ -#define HVCS_CLOSE_WAIT (HZ/100) /* 1/10 of a second */ - -/* - * Since the Linux TTY code does not currently (2-04-2004) support dynamic - * addition of tty derived devices and we shouldn't allocate thousands of - * tty_device pointers when the number of vty-server & vty partner connections - * will most often be much lower than this, we'll arbitrarily allocate - * HVCS_DEFAULT_SERVER_ADAPTERS tty_structs and cdev's by default when we - * register the tty_driver. This can be overridden using an insmod parameter. - */ -#define HVCS_DEFAULT_SERVER_ADAPTERS 64 - -/* - * The user can't insmod with more than HVCS_MAX_SERVER_ADAPTERS hvcs device - * nodes as a sanity check. Theoretically there can be over 1 Billion - * vty-server & vty partner connections. - */ -#define HVCS_MAX_SERVER_ADAPTERS 1024 - -/* - * We let Linux assign us a major number and we start the minors at zero. There - * is no intuitive mapping between minor number and the target vty-server - * adapter except that each new vty-server adapter is always assigned to the - * smallest minor number available. - */ -#define HVCS_MINOR_START 0 - -/* - * The hcall interface involves putting 8 chars into each of two registers. - * We load up those 2 registers (in arch/powerpc/platforms/pseries/hvconsole.c) - * by casting char[16] to long[2]. It would work without __ALIGNED__, but a - * little (tiny) bit slower because an unaligned load is slower than aligned - * load. - */ -#define __ALIGNED__ __attribute__((__aligned__(8))) - -/* - * How much data can firmware send with each hvc_put_chars()? Maybe this - * should be moved into an architecture specific area. - */ -#define HVCS_BUFF_LEN 16 - -/* - * This is the maximum amount of data we'll let the user send us (hvcs_write) at - * once in a chunk as a sanity check. - */ -#define HVCS_MAX_FROM_USER 4096 - -/* - * Be careful when adding flags to this line discipline. Don't add anything - * that will cause echoing or we'll go into recursive loop echoing chars back - * and forth with the console drivers. - */ -static struct ktermios hvcs_tty_termios = { - .c_iflag = IGNBRK | IGNPAR, - .c_oflag = OPOST, - .c_cflag = B38400 | CS8 | CREAD | HUPCL, - .c_cc = INIT_C_CC, - .c_ispeed = 38400, - .c_ospeed = 38400 -}; - -/* - * This value is used to take the place of a command line parameter when the - * module is inserted. It starts as -1 and stays as such if the user doesn't - * specify a module insmod parameter. If they DO specify one then it is set to - * the value of the integer passed in. - */ -static int hvcs_parm_num_devs = -1; -module_param(hvcs_parm_num_devs, int, 0); - -static const char hvcs_driver_name[] = "hvcs"; -static const char hvcs_device_node[] = "hvcs"; -static const char hvcs_driver_string[] - = "IBM hvcs (Hypervisor Virtual Console Server) Driver"; - -/* Status of partner info rescan triggered via sysfs. */ -static int hvcs_rescan_status; - -static struct tty_driver *hvcs_tty_driver; - -/* - * In order to be somewhat sane this driver always associates the hvcs_struct - * index element with the numerically equal tty->index. This means that a - * hotplugged vty-server adapter will always map to the lowest index valued - * device node. If vty-servers were hotplug removed from the system and then - * new ones added the new vty-server may have the largest slot number of all - * the vty-server adapters in the partition but it may have the lowest dev node - * index of all the adapters due to the hole left by the hotplug removed - * adapter. There are a set of functions provided to get the lowest index for - * a new device as well as return the index to the list. This list is allocated - * with a number of elements equal to the number of device nodes requested when - * the module was inserted. - */ -static int *hvcs_index_list; - -/* - * How large is the list? This is kept for traversal since the list is - * dynamically created. - */ -static int hvcs_index_count; - -/* - * Used by the khvcsd to pick up I/O operations when the kernel_thread is - * already awake but potentially shifted to TASK_INTERRUPTIBLE state. - */ -static int hvcs_kicked; - -/* - * Use by the kthread construct for task operations like waking the sleeping - * thread and stopping the kthread. - */ -static struct task_struct *hvcs_task; - -/* - * We allocate this for the use of all of the hvcs_structs when they fetch - * partner info. - */ -static unsigned long *hvcs_pi_buff; - -/* Only allow one hvcs_struct to use the hvcs_pi_buff at a time. */ -static DEFINE_SPINLOCK(hvcs_pi_lock); - -/* One vty-server per hvcs_struct */ -struct hvcs_struct { - spinlock_t lock; - - /* - * This index identifies this hvcs device as the complement to a - * specific tty index. - */ - unsigned int index; - - struct tty_struct *tty; - int open_count; - - /* - * Used to tell the driver kernel_thread what operations need to take - * place upon this hvcs_struct instance. - */ - int todo_mask; - - /* - * This buffer is required so that when hvcs_write_room() reports that - * it can send HVCS_BUFF_LEN characters that it will buffer the full - * HVCS_BUFF_LEN characters if need be. This is essential for opost - * writes since they do not do high level buffering and expect to be - * able to send what the driver commits to sending buffering - * [e.g. tab to space conversions in n_tty.c opost()]. - */ - char buffer[HVCS_BUFF_LEN]; - int chars_in_buffer; - - /* - * Any variable below the kref is valid before a tty is connected and - * stays valid after the tty is disconnected. These shouldn't be - * whacked until the koject refcount reaches zero though some entries - * may be changed via sysfs initiatives. - */ - struct kref kref; /* ref count & hvcs_struct lifetime */ - int connected; /* is the vty-server currently connected to a vty? */ - uint32_t p_unit_address; /* partner unit address */ - uint32_t p_partition_ID; /* partner partition ID */ - char p_location_code[HVCS_CLC_LENGTH + 1]; /* CLC + Null Term */ - struct list_head next; /* list management */ - struct vio_dev *vdev; -}; - -/* Required to back map a kref to its containing object */ -#define from_kref(k) container_of(k, struct hvcs_struct, kref) - -static LIST_HEAD(hvcs_structs); -static DEFINE_SPINLOCK(hvcs_structs_lock); - -static void hvcs_unthrottle(struct tty_struct *tty); -static void hvcs_throttle(struct tty_struct *tty); -static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance); - -static int hvcs_write(struct tty_struct *tty, - const unsigned char *buf, int count); -static int hvcs_write_room(struct tty_struct *tty); -static int hvcs_chars_in_buffer(struct tty_struct *tty); - -static int hvcs_has_pi(struct hvcs_struct *hvcsd); -static void hvcs_set_pi(struct hvcs_partner_info *pi, - struct hvcs_struct *hvcsd); -static int hvcs_get_pi(struct hvcs_struct *hvcsd); -static int hvcs_rescan_devices_list(void); - -static int hvcs_partner_connect(struct hvcs_struct *hvcsd); -static void hvcs_partner_free(struct hvcs_struct *hvcsd); - -static int hvcs_enable_device(struct hvcs_struct *hvcsd, - uint32_t unit_address, unsigned int irq, struct vio_dev *dev); - -static int hvcs_open(struct tty_struct *tty, struct file *filp); -static void hvcs_close(struct tty_struct *tty, struct file *filp); -static void hvcs_hangup(struct tty_struct * tty); - -static int __devinit hvcs_probe(struct vio_dev *dev, - const struct vio_device_id *id); -static int __devexit hvcs_remove(struct vio_dev *dev); -static int __init hvcs_module_init(void); -static void __exit hvcs_module_exit(void); - -#define HVCS_SCHED_READ 0x00000001 -#define HVCS_QUICK_READ 0x00000002 -#define HVCS_TRY_WRITE 0x00000004 -#define HVCS_READ_MASK (HVCS_SCHED_READ | HVCS_QUICK_READ) - -static inline struct hvcs_struct *from_vio_dev(struct vio_dev *viod) -{ - return dev_get_drvdata(&viod->dev); -} -/* The sysfs interface for the driver and devices */ - -static ssize_t hvcs_partner_vtys_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - int retval; - - spin_lock_irqsave(&hvcsd->lock, flags); - retval = sprintf(buf, "%X\n", hvcsd->p_unit_address); - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; -} -static DEVICE_ATTR(partner_vtys, S_IRUGO, hvcs_partner_vtys_show, NULL); - -static ssize_t hvcs_partner_clcs_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - int retval; - - spin_lock_irqsave(&hvcsd->lock, flags); - retval = sprintf(buf, "%s\n", &hvcsd->p_location_code[0]); - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; -} -static DEVICE_ATTR(partner_clcs, S_IRUGO, hvcs_partner_clcs_show, NULL); - -static ssize_t hvcs_current_vty_store(struct device *dev, struct device_attribute *attr, const char * buf, - size_t count) -{ - /* - * Don't need this feature at the present time because firmware doesn't - * yet support multiple partners. - */ - printk(KERN_INFO "HVCS: Denied current_vty change: -EPERM.\n"); - return -EPERM; -} - -static ssize_t hvcs_current_vty_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - int retval; - - spin_lock_irqsave(&hvcsd->lock, flags); - retval = sprintf(buf, "%s\n", &hvcsd->p_location_code[0]); - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; -} - -static DEVICE_ATTR(current_vty, - S_IRUGO | S_IWUSR, hvcs_current_vty_show, hvcs_current_vty_store); - -static ssize_t hvcs_vterm_state_store(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - - /* writing a '0' to this sysfs entry will result in the disconnect. */ - if (simple_strtol(buf, NULL, 0) != 0) - return -EINVAL; - - spin_lock_irqsave(&hvcsd->lock, flags); - - if (hvcsd->open_count > 0) { - spin_unlock_irqrestore(&hvcsd->lock, flags); - printk(KERN_INFO "HVCS: vterm state unchanged. " - "The hvcs device node is still in use.\n"); - return -EPERM; - } - - if (hvcsd->connected == 0) { - spin_unlock_irqrestore(&hvcsd->lock, flags); - printk(KERN_INFO "HVCS: vterm state unchanged. The" - " vty-server is not connected to a vty.\n"); - return -EPERM; - } - - hvcs_partner_free(hvcsd); - printk(KERN_INFO "HVCS: Closed vty-server@%X and" - " partner vty@%X:%d connection.\n", - hvcsd->vdev->unit_address, - hvcsd->p_unit_address, - (uint32_t)hvcsd->p_partition_ID); - - spin_unlock_irqrestore(&hvcsd->lock, flags); - return count; -} - -static ssize_t hvcs_vterm_state_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - int retval; - - spin_lock_irqsave(&hvcsd->lock, flags); - retval = sprintf(buf, "%d\n", hvcsd->connected); - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; -} -static DEVICE_ATTR(vterm_state, S_IRUGO | S_IWUSR, - hvcs_vterm_state_show, hvcs_vterm_state_store); - -static ssize_t hvcs_index_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - int retval; - - spin_lock_irqsave(&hvcsd->lock, flags); - retval = sprintf(buf, "%d\n", hvcsd->index); - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; -} - -static DEVICE_ATTR(index, S_IRUGO, hvcs_index_show, NULL); - -static struct attribute *hvcs_attrs[] = { - &dev_attr_partner_vtys.attr, - &dev_attr_partner_clcs.attr, - &dev_attr_current_vty.attr, - &dev_attr_vterm_state.attr, - &dev_attr_index.attr, - NULL, -}; - -static struct attribute_group hvcs_attr_group = { - .attrs = hvcs_attrs, -}; - -static ssize_t hvcs_rescan_show(struct device_driver *ddp, char *buf) -{ - /* A 1 means it is updating, a 0 means it is done updating */ - return snprintf(buf, PAGE_SIZE, "%d\n", hvcs_rescan_status); -} - -static ssize_t hvcs_rescan_store(struct device_driver *ddp, const char * buf, - size_t count) -{ - if ((simple_strtol(buf, NULL, 0) != 1) - && (hvcs_rescan_status != 0)) - return -EINVAL; - - hvcs_rescan_status = 1; - printk(KERN_INFO "HVCS: rescanning partner info for all" - " vty-servers.\n"); - hvcs_rescan_devices_list(); - hvcs_rescan_status = 0; - return count; -} - -static DRIVER_ATTR(rescan, - S_IRUGO | S_IWUSR, hvcs_rescan_show, hvcs_rescan_store); - -static void hvcs_kick(void) -{ - hvcs_kicked = 1; - wmb(); - wake_up_process(hvcs_task); -} - -static void hvcs_unthrottle(struct tty_struct *tty) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&hvcsd->lock, flags); - hvcsd->todo_mask |= HVCS_SCHED_READ; - spin_unlock_irqrestore(&hvcsd->lock, flags); - hvcs_kick(); -} - -static void hvcs_throttle(struct tty_struct *tty) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&hvcsd->lock, flags); - vio_disable_interrupts(hvcsd->vdev); - spin_unlock_irqrestore(&hvcsd->lock, flags); -} - -/* - * If the device is being removed we don't have to worry about this interrupt - * handler taking any further interrupts because they are disabled which means - * the hvcs_struct will always be valid in this handler. - */ -static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance) -{ - struct hvcs_struct *hvcsd = dev_instance; - - spin_lock(&hvcsd->lock); - vio_disable_interrupts(hvcsd->vdev); - hvcsd->todo_mask |= HVCS_SCHED_READ; - spin_unlock(&hvcsd->lock); - hvcs_kick(); - - return IRQ_HANDLED; -} - -/* This function must be called with the hvcsd->lock held */ -static void hvcs_try_write(struct hvcs_struct *hvcsd) -{ - uint32_t unit_address = hvcsd->vdev->unit_address; - struct tty_struct *tty = hvcsd->tty; - int sent; - - if (hvcsd->todo_mask & HVCS_TRY_WRITE) { - /* won't send partial writes */ - sent = hvc_put_chars(unit_address, - &hvcsd->buffer[0], - hvcsd->chars_in_buffer ); - if (sent > 0) { - hvcsd->chars_in_buffer = 0; - /* wmb(); */ - hvcsd->todo_mask &= ~(HVCS_TRY_WRITE); - /* wmb(); */ - - /* - * We are still obligated to deliver the data to the - * hypervisor even if the tty has been closed because - * we commited to delivering it. But don't try to wake - * a non-existent tty. - */ - if (tty) { - tty_wakeup(tty); - } - } - } -} - -static int hvcs_io(struct hvcs_struct *hvcsd) -{ - uint32_t unit_address; - struct tty_struct *tty; - char buf[HVCS_BUFF_LEN] __ALIGNED__; - unsigned long flags; - int got = 0; - - spin_lock_irqsave(&hvcsd->lock, flags); - - unit_address = hvcsd->vdev->unit_address; - tty = hvcsd->tty; - - hvcs_try_write(hvcsd); - - if (!tty || test_bit(TTY_THROTTLED, &tty->flags)) { - hvcsd->todo_mask &= ~(HVCS_READ_MASK); - goto bail; - } else if (!(hvcsd->todo_mask & (HVCS_READ_MASK))) - goto bail; - - /* remove the read masks */ - hvcsd->todo_mask &= ~(HVCS_READ_MASK); - - if (tty_buffer_request_room(tty, HVCS_BUFF_LEN) >= HVCS_BUFF_LEN) { - got = hvc_get_chars(unit_address, - &buf[0], - HVCS_BUFF_LEN); - tty_insert_flip_string(tty, buf, got); - } - - /* Give the TTY time to process the data we just sent. */ - if (got) - hvcsd->todo_mask |= HVCS_QUICK_READ; - - spin_unlock_irqrestore(&hvcsd->lock, flags); - /* This is synch because tty->low_latency == 1 */ - if(got) - tty_flip_buffer_push(tty); - - if (!got) { - /* Do this _after_ the flip_buffer_push */ - spin_lock_irqsave(&hvcsd->lock, flags); - vio_enable_interrupts(hvcsd->vdev); - spin_unlock_irqrestore(&hvcsd->lock, flags); - } - - return hvcsd->todo_mask; - - bail: - spin_unlock_irqrestore(&hvcsd->lock, flags); - return hvcsd->todo_mask; -} - -static int khvcsd(void *unused) -{ - struct hvcs_struct *hvcsd; - int hvcs_todo_mask; - - __set_current_state(TASK_RUNNING); - - do { - hvcs_todo_mask = 0; - hvcs_kicked = 0; - wmb(); - - spin_lock(&hvcs_structs_lock); - list_for_each_entry(hvcsd, &hvcs_structs, next) { - hvcs_todo_mask |= hvcs_io(hvcsd); - } - spin_unlock(&hvcs_structs_lock); - - /* - * If any of the hvcs adapters want to try a write or quick read - * don't schedule(), yield a smidgen then execute the hvcs_io - * thread again for those that want the write. - */ - if (hvcs_todo_mask & (HVCS_TRY_WRITE | HVCS_QUICK_READ)) { - yield(); - continue; - } - - set_current_state(TASK_INTERRUPTIBLE); - if (!hvcs_kicked) - schedule(); - __set_current_state(TASK_RUNNING); - } while (!kthread_should_stop()); - - return 0; -} - -static struct vio_device_id hvcs_driver_table[] __devinitdata= { - {"serial-server", "hvterm2"}, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, hvcs_driver_table); - -static void hvcs_return_index(int index) -{ - /* Paranoia check */ - if (!hvcs_index_list) - return; - if (index < 0 || index >= hvcs_index_count) - return; - if (hvcs_index_list[index] == -1) - return; - else - hvcs_index_list[index] = -1; -} - -/* callback when the kref ref count reaches zero */ -static void destroy_hvcs_struct(struct kref *kref) -{ - struct hvcs_struct *hvcsd = from_kref(kref); - struct vio_dev *vdev; - unsigned long flags; - - spin_lock(&hvcs_structs_lock); - spin_lock_irqsave(&hvcsd->lock, flags); - - /* the list_del poisons the pointers */ - list_del(&(hvcsd->next)); - - if (hvcsd->connected == 1) { - hvcs_partner_free(hvcsd); - printk(KERN_INFO "HVCS: Closed vty-server@%X and" - " partner vty@%X:%d connection.\n", - hvcsd->vdev->unit_address, - hvcsd->p_unit_address, - (uint32_t)hvcsd->p_partition_ID); - } - printk(KERN_INFO "HVCS: Destroyed hvcs_struct for vty-server@%X.\n", - hvcsd->vdev->unit_address); - - vdev = hvcsd->vdev; - hvcsd->vdev = NULL; - - hvcsd->p_unit_address = 0; - hvcsd->p_partition_ID = 0; - hvcs_return_index(hvcsd->index); - memset(&hvcsd->p_location_code[0], 0x00, HVCS_CLC_LENGTH + 1); - - spin_unlock_irqrestore(&hvcsd->lock, flags); - spin_unlock(&hvcs_structs_lock); - - sysfs_remove_group(&vdev->dev.kobj, &hvcs_attr_group); - - kfree(hvcsd); -} - -static int hvcs_get_index(void) -{ - int i; - /* Paranoia check */ - if (!hvcs_index_list) { - printk(KERN_ERR "HVCS: hvcs_index_list NOT valid!.\n"); - return -EFAULT; - } - /* Find the numerically lowest first free index. */ - for(i = 0; i < hvcs_index_count; i++) { - if (hvcs_index_list[i] == -1) { - hvcs_index_list[i] = 0; - return i; - } - } - return -1; -} - -static int __devinit hvcs_probe( - struct vio_dev *dev, - const struct vio_device_id *id) -{ - struct hvcs_struct *hvcsd; - int index; - int retval; - - if (!dev || !id) { - printk(KERN_ERR "HVCS: probed with invalid parameter.\n"); - return -EPERM; - } - - /* early to avoid cleanup on failure */ - index = hvcs_get_index(); - if (index < 0) { - return -EFAULT; - } - - hvcsd = kzalloc(sizeof(*hvcsd), GFP_KERNEL); - if (!hvcsd) - return -ENODEV; - - - spin_lock_init(&hvcsd->lock); - /* Automatically incs the refcount the first time */ - kref_init(&hvcsd->kref); - - hvcsd->vdev = dev; - dev_set_drvdata(&dev->dev, hvcsd); - - hvcsd->index = index; - - /* hvcsd->index = ++hvcs_struct_count; */ - hvcsd->chars_in_buffer = 0; - hvcsd->todo_mask = 0; - hvcsd->connected = 0; - - /* - * This will populate the hvcs_struct's partner info fields for the - * first time. - */ - if (hvcs_get_pi(hvcsd)) { - printk(KERN_ERR "HVCS: Failed to fetch partner" - " info for vty-server@%X on device probe.\n", - hvcsd->vdev->unit_address); - } - - /* - * If a user app opens a tty that corresponds to this vty-server before - * the hvcs_struct has been added to the devices list then the user app - * will get -ENODEV. - */ - spin_lock(&hvcs_structs_lock); - list_add_tail(&(hvcsd->next), &hvcs_structs); - spin_unlock(&hvcs_structs_lock); - - retval = sysfs_create_group(&dev->dev.kobj, &hvcs_attr_group); - if (retval) { - printk(KERN_ERR "HVCS: Can't create sysfs attrs for vty-server@%X\n", - hvcsd->vdev->unit_address); - return retval; - } - - printk(KERN_INFO "HVCS: vty-server@%X added to the vio bus.\n", dev->unit_address); - - /* - * DON'T enable interrupts here because there is no user to receive the - * data. - */ - return 0; -} - -static int __devexit hvcs_remove(struct vio_dev *dev) -{ - struct hvcs_struct *hvcsd = dev_get_drvdata(&dev->dev); - unsigned long flags; - struct tty_struct *tty; - - if (!hvcsd) - return -ENODEV; - - /* By this time the vty-server won't be getting any more interrupts */ - - spin_lock_irqsave(&hvcsd->lock, flags); - - tty = hvcsd->tty; - - spin_unlock_irqrestore(&hvcsd->lock, flags); - - /* - * Let the last holder of this object cause it to be removed, which - * would probably be tty_hangup below. - */ - kref_put(&hvcsd->kref, destroy_hvcs_struct); - - /* - * The hangup is a scheduled function which will auto chain call - * hvcs_hangup. The tty should always be valid at this time unless a - * simultaneous tty close already cleaned up the hvcs_struct. - */ - if (tty) - tty_hangup(tty); - - printk(KERN_INFO "HVCS: vty-server@%X removed from the" - " vio bus.\n", dev->unit_address); - return 0; -}; - -static struct vio_driver hvcs_vio_driver = { - .id_table = hvcs_driver_table, - .probe = hvcs_probe, - .remove = __devexit_p(hvcs_remove), - .driver = { - .name = hvcs_driver_name, - .owner = THIS_MODULE, - } -}; - -/* Only called from hvcs_get_pi please */ -static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd) -{ - int clclength; - - hvcsd->p_unit_address = pi->unit_address; - hvcsd->p_partition_ID = pi->partition_ID; - clclength = strlen(&pi->location_code[0]); - if (clclength > HVCS_CLC_LENGTH) - clclength = HVCS_CLC_LENGTH; - - /* copy the null-term char too */ - strncpy(&hvcsd->p_location_code[0], - &pi->location_code[0], clclength + 1); -} - -/* - * Traverse the list and add the partner info that is found to the hvcs_struct - * struct entry. NOTE: At this time I know that partner info will return a - * single entry but in the future there may be multiple partner info entries per - * vty-server and you'll want to zero out that list and reset it. If for some - * reason you have an old version of this driver but there IS more than one - * partner info then hvcsd->p_* will hold the last partner info data from the - * firmware query. A good way to update this code would be to replace the three - * partner info fields in hvcs_struct with a list of hvcs_partner_info - * instances. - * - * This function must be called with the hvcsd->lock held. - */ -static int hvcs_get_pi(struct hvcs_struct *hvcsd) -{ - struct hvcs_partner_info *pi; - uint32_t unit_address = hvcsd->vdev->unit_address; - struct list_head head; - int retval; - - spin_lock(&hvcs_pi_lock); - if (!hvcs_pi_buff) { - spin_unlock(&hvcs_pi_lock); - return -EFAULT; - } - retval = hvcs_get_partner_info(unit_address, &head, hvcs_pi_buff); - spin_unlock(&hvcs_pi_lock); - if (retval) { - printk(KERN_ERR "HVCS: Failed to fetch partner" - " info for vty-server@%x.\n", unit_address); - return retval; - } - - /* nixes the values if the partner vty went away */ - hvcsd->p_unit_address = 0; - hvcsd->p_partition_ID = 0; - - list_for_each_entry(pi, &head, node) - hvcs_set_pi(pi, hvcsd); - - hvcs_free_partner_info(&head); - return 0; -} - -/* - * This function is executed by the driver "rescan" sysfs entry. It shouldn't - * be executed elsewhere, in order to prevent deadlock issues. - */ -static int hvcs_rescan_devices_list(void) -{ - struct hvcs_struct *hvcsd; - unsigned long flags; - - spin_lock(&hvcs_structs_lock); - - list_for_each_entry(hvcsd, &hvcs_structs, next) { - spin_lock_irqsave(&hvcsd->lock, flags); - hvcs_get_pi(hvcsd); - spin_unlock_irqrestore(&hvcsd->lock, flags); - } - - spin_unlock(&hvcs_structs_lock); - - return 0; -} - -/* - * Farm this off into its own function because it could be more complex once - * multiple partners support is added. This function should be called with - * the hvcsd->lock held. - */ -static int hvcs_has_pi(struct hvcs_struct *hvcsd) -{ - if ((!hvcsd->p_unit_address) || (!hvcsd->p_partition_ID)) - return 0; - return 1; -} - -/* - * NOTE: It is possible that the super admin removed a partner vty and then - * added a different vty as the new partner. - * - * This function must be called with the hvcsd->lock held. - */ -static int hvcs_partner_connect(struct hvcs_struct *hvcsd) -{ - int retval; - unsigned int unit_address = hvcsd->vdev->unit_address; - - /* - * If there wasn't any pi when the device was added it doesn't meant - * there isn't any now. This driver isn't notified when a new partner - * vty is added to a vty-server so we discover changes on our own. - * Please see comments in hvcs_register_connection() for justification - * of this bizarre code. - */ - retval = hvcs_register_connection(unit_address, - hvcsd->p_partition_ID, - hvcsd->p_unit_address); - if (!retval) { - hvcsd->connected = 1; - return 0; - } else if (retval != -EINVAL) - return retval; - - /* - * As per the spec re-get the pi and try again if -EINVAL after the - * first connection attempt. - */ - if (hvcs_get_pi(hvcsd)) - return -ENOMEM; - - if (!hvcs_has_pi(hvcsd)) - return -ENODEV; - - retval = hvcs_register_connection(unit_address, - hvcsd->p_partition_ID, - hvcsd->p_unit_address); - if (retval != -EINVAL) { - hvcsd->connected = 1; - return retval; - } - - /* - * EBUSY is the most likely scenario though the vty could have been - * removed or there really could be an hcall error due to the parameter - * data but thanks to ambiguous firmware return codes we can't really - * tell. - */ - printk(KERN_INFO "HVCS: vty-server or partner" - " vty is busy. Try again later.\n"); - return -EBUSY; -} - -/* This function must be called with the hvcsd->lock held */ -static void hvcs_partner_free(struct hvcs_struct *hvcsd) -{ - int retval; - do { - retval = hvcs_free_connection(hvcsd->vdev->unit_address); - } while (retval == -EBUSY); - hvcsd->connected = 0; -} - -/* This helper function must be called WITHOUT the hvcsd->lock held */ -static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, - unsigned int irq, struct vio_dev *vdev) -{ - unsigned long flags; - int rc; - - /* - * It is possible that the vty-server was removed between the time that - * the conn was registered and now. - */ - if (!(rc = request_irq(irq, &hvcs_handle_interrupt, - IRQF_DISABLED, "ibmhvcs", hvcsd))) { - /* - * It is possible the vty-server was removed after the irq was - * requested but before we have time to enable interrupts. - */ - if (vio_enable_interrupts(vdev) == H_SUCCESS) - return 0; - else { - printk(KERN_ERR "HVCS: int enable failed for" - " vty-server@%X.\n", unit_address); - free_irq(irq, hvcsd); - } - } else - printk(KERN_ERR "HVCS: irq req failed for" - " vty-server@%X.\n", unit_address); - - spin_lock_irqsave(&hvcsd->lock, flags); - hvcs_partner_free(hvcsd); - spin_unlock_irqrestore(&hvcsd->lock, flags); - - return rc; - -} - -/* - * This always increments the kref ref count if the call is successful. - * Please remember to dec when you are done with the instance. - * - * NOTICE: Do NOT hold either the hvcs_struct.lock or hvcs_structs_lock when - * calling this function or you will get deadlock. - */ -static struct hvcs_struct *hvcs_get_by_index(int index) -{ - struct hvcs_struct *hvcsd = NULL; - unsigned long flags; - - spin_lock(&hvcs_structs_lock); - /* We can immediately discard OOB requests */ - if (index >= 0 && index < HVCS_MAX_SERVER_ADAPTERS) { - list_for_each_entry(hvcsd, &hvcs_structs, next) { - spin_lock_irqsave(&hvcsd->lock, flags); - if (hvcsd->index == index) { - kref_get(&hvcsd->kref); - spin_unlock_irqrestore(&hvcsd->lock, flags); - spin_unlock(&hvcs_structs_lock); - return hvcsd; - } - spin_unlock_irqrestore(&hvcsd->lock, flags); - } - hvcsd = NULL; - } - - spin_unlock(&hvcs_structs_lock); - return hvcsd; -} - -/* - * This is invoked via the tty_open interface when a user app connects to the - * /dev node. - */ -static int hvcs_open(struct tty_struct *tty, struct file *filp) -{ - struct hvcs_struct *hvcsd; - int rc, retval = 0; - unsigned long flags; - unsigned int irq; - struct vio_dev *vdev; - unsigned long unit_address; - - if (tty->driver_data) - goto fast_open; - - /* - * Is there a vty-server that shares the same index? - * This function increments the kref index. - */ - if (!(hvcsd = hvcs_get_by_index(tty->index))) { - printk(KERN_WARNING "HVCS: open failed, no device associated" - " with tty->index %d.\n", tty->index); - return -ENODEV; - } - - spin_lock_irqsave(&hvcsd->lock, flags); - - if (hvcsd->connected == 0) - if ((retval = hvcs_partner_connect(hvcsd))) - goto error_release; - - hvcsd->open_count = 1; - hvcsd->tty = tty; - tty->driver_data = hvcsd; - - memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); - - /* - * Save these in the spinlock for the enable operations that need them - * outside of the spinlock. - */ - irq = hvcsd->vdev->irq; - vdev = hvcsd->vdev; - unit_address = hvcsd->vdev->unit_address; - - hvcsd->todo_mask |= HVCS_SCHED_READ; - spin_unlock_irqrestore(&hvcsd->lock, flags); - - /* - * This must be done outside of the spinlock because it requests irqs - * and will grab the spinlock and free the connection if it fails. - */ - if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) { - kref_put(&hvcsd->kref, destroy_hvcs_struct); - printk(KERN_WARNING "HVCS: enable device failed.\n"); - return rc; - } - - goto open_success; - -fast_open: - hvcsd = tty->driver_data; - - spin_lock_irqsave(&hvcsd->lock, flags); - kref_get(&hvcsd->kref); - hvcsd->open_count++; - hvcsd->todo_mask |= HVCS_SCHED_READ; - spin_unlock_irqrestore(&hvcsd->lock, flags); - -open_success: - hvcs_kick(); - - printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n", - hvcsd->vdev->unit_address ); - - return 0; - -error_release: - spin_unlock_irqrestore(&hvcsd->lock, flags); - kref_put(&hvcsd->kref, destroy_hvcs_struct); - - printk(KERN_WARNING "HVCS: partner connect failed.\n"); - return retval; -} - -static void hvcs_close(struct tty_struct *tty, struct file *filp) -{ - struct hvcs_struct *hvcsd; - unsigned long flags; - int irq = NO_IRQ; - - /* - * Is someone trying to close the file associated with this device after - * we have hung up? If so tty->driver_data wouldn't be valid. - */ - if (tty_hung_up_p(filp)) - return; - - /* - * No driver_data means that this close was probably issued after a - * failed hvcs_open by the tty layer's release_dev() api and we can just - * exit cleanly. - */ - if (!tty->driver_data) - return; - - hvcsd = tty->driver_data; - - spin_lock_irqsave(&hvcsd->lock, flags); - if (--hvcsd->open_count == 0) { - - vio_disable_interrupts(hvcsd->vdev); - - /* - * NULL this early so that the kernel_thread doesn't try to - * execute any operations on the TTY even though it is obligated - * to deliver any pending I/O to the hypervisor. - */ - hvcsd->tty = NULL; - - irq = hvcsd->vdev->irq; - spin_unlock_irqrestore(&hvcsd->lock, flags); - - tty_wait_until_sent(tty, HVCS_CLOSE_WAIT); - - /* - * This line is important because it tells hvcs_open that this - * device needs to be re-configured the next time hvcs_open is - * called. - */ - tty->driver_data = NULL; - - free_irq(irq, hvcsd); - kref_put(&hvcsd->kref, destroy_hvcs_struct); - return; - } else if (hvcsd->open_count < 0) { - printk(KERN_ERR "HVCS: vty-server@%X open_count: %d" - " is missmanaged.\n", - hvcsd->vdev->unit_address, hvcsd->open_count); - } - - spin_unlock_irqrestore(&hvcsd->lock, flags); - kref_put(&hvcsd->kref, destroy_hvcs_struct); -} - -static void hvcs_hangup(struct tty_struct * tty) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - unsigned long flags; - int temp_open_count; - int irq = NO_IRQ; - - spin_lock_irqsave(&hvcsd->lock, flags); - /* Preserve this so that we know how many kref refs to put */ - temp_open_count = hvcsd->open_count; - - /* - * Don't kref put inside the spinlock because the destruction - * callback may use the spinlock and it may get called before the - * spinlock has been released. - */ - vio_disable_interrupts(hvcsd->vdev); - - hvcsd->todo_mask = 0; - - /* I don't think the tty needs the hvcs_struct pointer after a hangup */ - hvcsd->tty->driver_data = NULL; - hvcsd->tty = NULL; - - hvcsd->open_count = 0; - - /* This will drop any buffered data on the floor which is OK in a hangup - * scenario. */ - memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); - hvcsd->chars_in_buffer = 0; - - irq = hvcsd->vdev->irq; - - spin_unlock_irqrestore(&hvcsd->lock, flags); - - free_irq(irq, hvcsd); - - /* - * We need to kref_put() for every open_count we have since the - * tty_hangup() function doesn't invoke a close per open connection on a - * non-console device. - */ - while(temp_open_count) { - --temp_open_count; - /* - * The final put will trigger destruction of the hvcs_struct. - * NOTE: If this hangup was signaled from user space then the - * final put will never happen. - */ - kref_put(&hvcsd->kref, destroy_hvcs_struct); - } -} - -/* - * NOTE: This is almost always from_user since user level apps interact with the - * /dev nodes. I'm trusting that if hvcs_write gets called and interrupted by - * hvcs_remove (which removes the target device and executes tty_hangup()) that - * tty_hangup will allow hvcs_write time to complete execution before it - * terminates our device. - */ -static int hvcs_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - unsigned int unit_address; - const unsigned char *charbuf; - unsigned long flags; - int total_sent = 0; - int tosend = 0; - int result = 0; - - /* - * If they don't check the return code off of their open they may - * attempt this even if there is no connected device. - */ - if (!hvcsd) - return -ENODEV; - - /* Reasonable size to prevent user level flooding */ - if (count > HVCS_MAX_FROM_USER) { - printk(KERN_WARNING "HVCS write: count being truncated to" - " HVCS_MAX_FROM_USER.\n"); - count = HVCS_MAX_FROM_USER; - } - - charbuf = buf; - - spin_lock_irqsave(&hvcsd->lock, flags); - - /* - * Somehow an open succedded but the device was removed or the - * connection terminated between the vty-server and partner vty during - * the middle of a write operation? This is a crummy place to do this - * but we want to keep it all in the spinlock. - */ - if (hvcsd->open_count <= 0) { - spin_unlock_irqrestore(&hvcsd->lock, flags); - return -ENODEV; - } - - unit_address = hvcsd->vdev->unit_address; - - while (count > 0) { - tosend = min(count, (HVCS_BUFF_LEN - hvcsd->chars_in_buffer)); - /* - * No more space, this probably means that the last call to - * hvcs_write() didn't succeed and the buffer was filled up. - */ - if (!tosend) - break; - - memcpy(&hvcsd->buffer[hvcsd->chars_in_buffer], - &charbuf[total_sent], - tosend); - - hvcsd->chars_in_buffer += tosend; - - result = 0; - - /* - * If this is true then we don't want to try writing to the - * hypervisor because that is the kernel_threads job now. We'll - * just add to the buffer. - */ - if (!(hvcsd->todo_mask & HVCS_TRY_WRITE)) - /* won't send partial writes */ - result = hvc_put_chars(unit_address, - &hvcsd->buffer[0], - hvcsd->chars_in_buffer); - - /* - * Since we know we have enough room in hvcsd->buffer for - * tosend we record that it was sent regardless of whether the - * hypervisor actually took it because we have it buffered. - */ - total_sent+=tosend; - count-=tosend; - if (result == 0) { - hvcsd->todo_mask |= HVCS_TRY_WRITE; - hvcs_kick(); - break; - } - - hvcsd->chars_in_buffer = 0; - /* - * Test after the chars_in_buffer reset otherwise this could - * deadlock our writes if hvc_put_chars fails. - */ - if (result < 0) - break; - } - - spin_unlock_irqrestore(&hvcsd->lock, flags); - - if (result == -1) - return -EIO; - else - return total_sent; -} - -/* - * This is really asking how much can we guarentee that we can send or that we - * absolutely WILL BUFFER if we can't send it. This driver MUST honor the - * return value, hence the reason for hvcs_struct buffering. - */ -static int hvcs_write_room(struct tty_struct *tty) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - - if (!hvcsd || hvcsd->open_count <= 0) - return 0; - - return HVCS_BUFF_LEN - hvcsd->chars_in_buffer; -} - -static int hvcs_chars_in_buffer(struct tty_struct *tty) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - - return hvcsd->chars_in_buffer; -} - -static const struct tty_operations hvcs_ops = { - .open = hvcs_open, - .close = hvcs_close, - .hangup = hvcs_hangup, - .write = hvcs_write, - .write_room = hvcs_write_room, - .chars_in_buffer = hvcs_chars_in_buffer, - .unthrottle = hvcs_unthrottle, - .throttle = hvcs_throttle, -}; - -static int hvcs_alloc_index_list(int n) -{ - int i; - - hvcs_index_list = kmalloc(n * sizeof(hvcs_index_count),GFP_KERNEL); - if (!hvcs_index_list) - return -ENOMEM; - hvcs_index_count = n; - for (i = 0; i < hvcs_index_count; i++) - hvcs_index_list[i] = -1; - return 0; -} - -static void hvcs_free_index_list(void) -{ - /* Paranoia check to be thorough. */ - kfree(hvcs_index_list); - hvcs_index_list = NULL; - hvcs_index_count = 0; -} - -static int __init hvcs_module_init(void) -{ - int rc; - int num_ttys_to_alloc; - - printk(KERN_INFO "Initializing %s\n", hvcs_driver_string); - - /* Has the user specified an overload with an insmod param? */ - if (hvcs_parm_num_devs <= 0 || - (hvcs_parm_num_devs > HVCS_MAX_SERVER_ADAPTERS)) { - num_ttys_to_alloc = HVCS_DEFAULT_SERVER_ADAPTERS; - } else - num_ttys_to_alloc = hvcs_parm_num_devs; - - hvcs_tty_driver = alloc_tty_driver(num_ttys_to_alloc); - if (!hvcs_tty_driver) - return -ENOMEM; - - if (hvcs_alloc_index_list(num_ttys_to_alloc)) { - rc = -ENOMEM; - goto index_fail; - } - - hvcs_tty_driver->owner = THIS_MODULE; - - hvcs_tty_driver->driver_name = hvcs_driver_name; - hvcs_tty_driver->name = hvcs_device_node; - - /* - * We'll let the system assign us a major number, indicated by leaving - * it blank. - */ - - hvcs_tty_driver->minor_start = HVCS_MINOR_START; - hvcs_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; - - /* - * We role our own so that we DONT ECHO. We can't echo because the - * device we are connecting to already echoes by default and this would - * throw us into a horrible recursive echo-echo-echo loop. - */ - hvcs_tty_driver->init_termios = hvcs_tty_termios; - hvcs_tty_driver->flags = TTY_DRIVER_REAL_RAW; - - tty_set_operations(hvcs_tty_driver, &hvcs_ops); - - /* - * The following call will result in sysfs entries that denote the - * dynamically assigned major and minor numbers for our devices. - */ - if (tty_register_driver(hvcs_tty_driver)) { - printk(KERN_ERR "HVCS: registration as a tty driver failed.\n"); - rc = -EIO; - goto register_fail; - } - - hvcs_pi_buff = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!hvcs_pi_buff) { - rc = -ENOMEM; - goto buff_alloc_fail; - } - - hvcs_task = kthread_run(khvcsd, NULL, "khvcsd"); - if (IS_ERR(hvcs_task)) { - printk(KERN_ERR "HVCS: khvcsd creation failed. Driver not loaded.\n"); - rc = -EIO; - goto kthread_fail; - } - - rc = vio_register_driver(&hvcs_vio_driver); - if (rc) { - printk(KERN_ERR "HVCS: can't register vio driver\n"); - goto vio_fail; - } - - /* - * This needs to be done AFTER the vio_register_driver() call or else - * the kobjects won't be initialized properly. - */ - rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan); - if (rc) { - printk(KERN_ERR "HVCS: sysfs attr create failed\n"); - goto attr_fail; - } - - printk(KERN_INFO "HVCS: driver module inserted.\n"); - - return 0; - -attr_fail: - vio_unregister_driver(&hvcs_vio_driver); -vio_fail: - kthread_stop(hvcs_task); -kthread_fail: - kfree(hvcs_pi_buff); -buff_alloc_fail: - tty_unregister_driver(hvcs_tty_driver); -register_fail: - hvcs_free_index_list(); -index_fail: - put_tty_driver(hvcs_tty_driver); - hvcs_tty_driver = NULL; - return rc; -} - -static void __exit hvcs_module_exit(void) -{ - /* - * This driver receives hvcs_remove callbacks for each device upon - * module removal. - */ - - /* - * This synchronous operation will wake the khvcsd kthread if it is - * asleep and will return when khvcsd has terminated. - */ - kthread_stop(hvcs_task); - - spin_lock(&hvcs_pi_lock); - kfree(hvcs_pi_buff); - hvcs_pi_buff = NULL; - spin_unlock(&hvcs_pi_lock); - - driver_remove_file(&hvcs_vio_driver.driver, &driver_attr_rescan); - - vio_unregister_driver(&hvcs_vio_driver); - - tty_unregister_driver(hvcs_tty_driver); - - hvcs_free_index_list(); - - put_tty_driver(hvcs_tty_driver); - - printk(KERN_INFO "HVCS: driver module removed.\n"); -} - -module_init(hvcs_module_init); -module_exit(hvcs_module_exit); diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c deleted file mode 100644 index a2bc885ce60a..000000000000 --- a/drivers/char/hvsi.c +++ /dev/null @@ -1,1314 +0,0 @@ -/* - * Copyright (C) 2004 Hollis Blanchard <hollisb@us.ibm.com>, IBM - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS - * and the service processor on IBM pSeries servers. On these servers, there - * are no serial ports under the OS's control, and sometimes there is no other - * console available either. However, the service processor has two standard - * serial ports, so this over-complicated protocol allows the OS to control - * those ports by proxy. - * - * Besides data, the procotol supports the reading/writing of the serial - * port's DTR line, and the reading of the CD line. This is to allow the OS to - * control a modem attached to the service processor's serial port. Note that - * the OS cannot change the speed of the port through this protocol. - */ - -#undef DEBUG - -#include <linux/console.h> -#include <linux/ctype.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/major.h> -#include <linux/kernel.h> -#include <linux/spinlock.h> -#include <linux/sysrq.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <asm/hvcall.h> -#include <asm/hvconsole.h> -#include <asm/prom.h> -#include <asm/uaccess.h> -#include <asm/vio.h> -#include <asm/param.h> - -#define HVSI_MAJOR 229 -#define HVSI_MINOR 128 -#define MAX_NR_HVSI_CONSOLES 4 - -#define HVSI_TIMEOUT (5*HZ) -#define HVSI_VERSION 1 -#define HVSI_MAX_PACKET 256 -#define HVSI_MAX_READ 16 -#define HVSI_MAX_OUTGOING_DATA 12 -#define N_OUTBUF 12 - -/* - * we pass data via two 8-byte registers, so we would like our char arrays - * properly aligned for those loads. - */ -#define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) - -struct hvsi_struct { - struct delayed_work writer; - struct work_struct handshaker; - wait_queue_head_t emptyq; /* woken when outbuf is emptied */ - wait_queue_head_t stateq; /* woken when HVSI state changes */ - spinlock_t lock; - int index; - struct tty_struct *tty; - int count; - uint8_t throttle_buf[128]; - uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ - /* inbuf is for packet reassembly. leave a little room for leftovers. */ - uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ]; - uint8_t *inbuf_end; - int n_throttle; - int n_outbuf; - uint32_t vtermno; - uint32_t virq; - atomic_t seqno; /* HVSI packet sequence number */ - uint16_t mctrl; - uint8_t state; /* HVSI protocol state */ - uint8_t flags; -#ifdef CONFIG_MAGIC_SYSRQ - uint8_t sysrq; -#endif /* CONFIG_MAGIC_SYSRQ */ -}; -static struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES]; - -static struct tty_driver *hvsi_driver; -static int hvsi_count; -static int (*hvsi_wait)(struct hvsi_struct *hp, int state); - -enum HVSI_PROTOCOL_STATE { - HVSI_CLOSED, - HVSI_WAIT_FOR_VER_RESPONSE, - HVSI_WAIT_FOR_VER_QUERY, - HVSI_OPEN, - HVSI_WAIT_FOR_MCTRL_RESPONSE, - HVSI_FSP_DIED, -}; -#define HVSI_CONSOLE 0x1 - -#define VS_DATA_PACKET_HEADER 0xff -#define VS_CONTROL_PACKET_HEADER 0xfe -#define VS_QUERY_PACKET_HEADER 0xfd -#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc - -/* control verbs */ -#define VSV_SET_MODEM_CTL 1 /* to service processor only */ -#define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */ -#define VSV_CLOSE_PROTOCOL 3 - -/* query verbs */ -#define VSV_SEND_VERSION_NUMBER 1 -#define VSV_SEND_MODEM_CTL_STATUS 2 - -/* yes, these masks are not consecutive. */ -#define HVSI_TSDTR 0x01 -#define HVSI_TSCD 0x20 - -struct hvsi_header { - uint8_t type; - uint8_t len; - uint16_t seqno; -} __attribute__((packed)); - -struct hvsi_data { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint8_t data[HVSI_MAX_OUTGOING_DATA]; -} __attribute__((packed)); - -struct hvsi_control { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint16_t verb; - /* optional depending on verb: */ - uint32_t word; - uint32_t mask; -} __attribute__((packed)); - -struct hvsi_query { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint16_t verb; -} __attribute__((packed)); - -struct hvsi_query_response { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint16_t verb; - uint16_t query_seqno; - union { - uint8_t version; - uint32_t mctrl_word; - } u; -} __attribute__((packed)); - - - -static inline int is_console(struct hvsi_struct *hp) -{ - return hp->flags & HVSI_CONSOLE; -} - -static inline int is_open(struct hvsi_struct *hp) -{ - /* if we're waiting for an mctrl then we're already open */ - return (hp->state == HVSI_OPEN) - || (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE); -} - -static inline void print_state(struct hvsi_struct *hp) -{ -#ifdef DEBUG - static const char *state_names[] = { - "HVSI_CLOSED", - "HVSI_WAIT_FOR_VER_RESPONSE", - "HVSI_WAIT_FOR_VER_QUERY", - "HVSI_OPEN", - "HVSI_WAIT_FOR_MCTRL_RESPONSE", - "HVSI_FSP_DIED", - }; - const char *name = (hp->state < ARRAY_SIZE(state_names)) - ? state_names[hp->state] : "UNKNOWN"; - - pr_debug("hvsi%i: state = %s\n", hp->index, name); -#endif /* DEBUG */ -} - -static inline void __set_state(struct hvsi_struct *hp, int state) -{ - hp->state = state; - print_state(hp); - wake_up_all(&hp->stateq); -} - -static inline void set_state(struct hvsi_struct *hp, int state) -{ - unsigned long flags; - - spin_lock_irqsave(&hp->lock, flags); - __set_state(hp, state); - spin_unlock_irqrestore(&hp->lock, flags); -} - -static inline int len_packet(const uint8_t *packet) -{ - return (int)((struct hvsi_header *)packet)->len; -} - -static inline int is_header(const uint8_t *packet) -{ - struct hvsi_header *header = (struct hvsi_header *)packet; - return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; -} - -static inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) -{ - if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) - return 0; /* don't even have the packet header */ - - if (hp->inbuf_end < (packet + len_packet(packet))) - return 0; /* don't have the rest of the packet */ - - return 1; -} - -/* shift remaining bytes in packetbuf down */ -static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) -{ - int remaining = (int)(hp->inbuf_end - read_to); - - pr_debug("%s: %i chars remain\n", __func__, remaining); - - if (read_to != hp->inbuf) - memmove(hp->inbuf, read_to, remaining); - - hp->inbuf_end = hp->inbuf + remaining; -} - -#ifdef DEBUG -#define dbg_dump_packet(packet) dump_packet(packet) -#define dbg_dump_hex(data, len) dump_hex(data, len) -#else -#define dbg_dump_packet(packet) do { } while (0) -#define dbg_dump_hex(data, len) do { } while (0) -#endif - -static void dump_hex(const uint8_t *data, int len) -{ - int i; - - printk(" "); - for (i=0; i < len; i++) - printk("%.2x", data[i]); - - printk("\n "); - for (i=0; i < len; i++) { - if (isprint(data[i])) - printk("%c", data[i]); - else - printk("."); - } - printk("\n"); -} - -static void dump_packet(uint8_t *packet) -{ - struct hvsi_header *header = (struct hvsi_header *)packet; - - printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, - header->seqno); - - dump_hex(packet, header->len); -} - -static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) -{ - unsigned long got; - - got = hvc_get_chars(hp->vtermno, buf, count); - - return got; -} - -static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, - struct tty_struct **to_hangup, struct hvsi_struct **to_handshake) -{ - struct hvsi_control *header = (struct hvsi_control *)packet; - - switch (header->verb) { - case VSV_MODEM_CTL_UPDATE: - if ((header->word & HVSI_TSCD) == 0) { - /* CD went away; no more connection */ - pr_debug("hvsi%i: CD dropped\n", hp->index); - hp->mctrl &= TIOCM_CD; - /* If userland hasn't done an open(2) yet, hp->tty is NULL. */ - if (hp->tty && !(hp->tty->flags & CLOCAL)) - *to_hangup = hp->tty; - } - break; - case VSV_CLOSE_PROTOCOL: - pr_debug("hvsi%i: service processor came back\n", hp->index); - if (hp->state != HVSI_CLOSED) { - *to_handshake = hp; - } - break; - default: - printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", - hp->index); - dump_packet(packet); - break; - } -} - -static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) -{ - struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; - - switch (hp->state) { - case HVSI_WAIT_FOR_VER_RESPONSE: - __set_state(hp, HVSI_WAIT_FOR_VER_QUERY); - break; - case HVSI_WAIT_FOR_MCTRL_RESPONSE: - hp->mctrl = 0; - if (resp->u.mctrl_word & HVSI_TSDTR) - hp->mctrl |= TIOCM_DTR; - if (resp->u.mctrl_word & HVSI_TSCD) - hp->mctrl |= TIOCM_CD; - __set_state(hp, HVSI_OPEN); - break; - default: - printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); - dump_packet(packet); - break; - } -} - -/* respond to service processor's version query */ -static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) -{ - struct hvsi_query_response packet __ALIGNED__; - int wrote; - - packet.type = VS_QUERY_RESPONSE_PACKET_HEADER; - packet.len = sizeof(struct hvsi_query_response); - packet.seqno = atomic_inc_return(&hp->seqno); - packet.verb = VSV_SEND_VERSION_NUMBER; - packet.u.version = HVSI_VERSION; - packet.query_seqno = query_seqno+1; - - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); - - wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (wrote != packet.len) { - printk(KERN_ERR "hvsi%i: couldn't send query response!\n", - hp->index); - return -EIO; - } - - return 0; -} - -static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) -{ - struct hvsi_query *query = (struct hvsi_query *)packet; - - switch (hp->state) { - case HVSI_WAIT_FOR_VER_QUERY: - hvsi_version_respond(hp, query->seqno); - __set_state(hp, HVSI_OPEN); - break; - default: - printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); - dump_packet(packet); - break; - } -} - -static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) -{ - int i; - - for (i=0; i < len; i++) { - char c = buf[i]; -#ifdef CONFIG_MAGIC_SYSRQ - if (c == '\0') { - hp->sysrq = 1; - continue; - } else if (hp->sysrq) { - handle_sysrq(c); - hp->sysrq = 0; - continue; - } -#endif /* CONFIG_MAGIC_SYSRQ */ - tty_insert_flip_char(hp->tty, c, 0); - } -} - -/* - * We could get 252 bytes of data at once here. But the tty layer only - * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow - * it. Accordingly we won't send more than 128 bytes at a time to the flip - * buffer, which will give the tty buffer a chance to throttle us. Should the - * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be - * revisited. - */ -#define TTY_THRESHOLD_THROTTLE 128 -static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, - const uint8_t *packet) -{ - const struct hvsi_header *header = (const struct hvsi_header *)packet; - const uint8_t *data = packet + sizeof(struct hvsi_header); - int datalen = header->len - sizeof(struct hvsi_header); - int overflow = datalen - TTY_THRESHOLD_THROTTLE; - - pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); - - if (datalen == 0) - return NULL; - - if (overflow > 0) { - pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); - datalen = TTY_THRESHOLD_THROTTLE; - } - - hvsi_insert_chars(hp, data, datalen); - - if (overflow > 0) { - /* - * we still have more data to deliver, so we need to save off the - * overflow and send it later - */ - pr_debug("%s: deferring overflow\n", __func__); - memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); - hp->n_throttle = overflow; - } - - return hp->tty; -} - -/* - * Returns true/false indicating data successfully read from hypervisor. - * Used both to get packets for tty connections and to advance the state - * machine during console handshaking (in which case tty = NULL and we ignore - * incoming data). - */ -static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, - struct tty_struct **hangup, struct hvsi_struct **handshake) -{ - uint8_t *packet = hp->inbuf; - int chunklen; - - *flip = NULL; - *hangup = NULL; - *handshake = NULL; - - chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); - if (chunklen == 0) { - pr_debug("%s: 0-length read\n", __func__); - return 0; - } - - pr_debug("%s: got %i bytes\n", __func__, chunklen); - dbg_dump_hex(hp->inbuf_end, chunklen); - - hp->inbuf_end += chunklen; - - /* handle all completed packets */ - while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { - struct hvsi_header *header = (struct hvsi_header *)packet; - - if (!is_header(packet)) { - printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); - /* skip bytes until we find a header or run out of data */ - while ((packet < hp->inbuf_end) && (!is_header(packet))) - packet++; - continue; - } - - pr_debug("%s: handling %i-byte packet\n", __func__, - len_packet(packet)); - dbg_dump_packet(packet); - - switch (header->type) { - case VS_DATA_PACKET_HEADER: - if (!is_open(hp)) - break; - if (hp->tty == NULL) - break; /* no tty buffer to put data in */ - *flip = hvsi_recv_data(hp, packet); - break; - case VS_CONTROL_PACKET_HEADER: - hvsi_recv_control(hp, packet, hangup, handshake); - break; - case VS_QUERY_RESPONSE_PACKET_HEADER: - hvsi_recv_response(hp, packet); - break; - case VS_QUERY_PACKET_HEADER: - hvsi_recv_query(hp, packet); - break; - default: - printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", - hp->index, header->type); - dump_packet(packet); - break; - } - - packet += len_packet(packet); - - if (*hangup || *handshake) { - pr_debug("%s: hangup or handshake\n", __func__); - /* - * we need to send the hangup now before receiving any more data. - * If we get "data, hangup, data", we can't deliver the second - * data before the hangup. - */ - break; - } - } - - compact_inbuf(hp, packet); - - return 1; -} - -static void hvsi_send_overflow(struct hvsi_struct *hp) -{ - pr_debug("%s: delivering %i bytes overflow\n", __func__, - hp->n_throttle); - - hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); - hp->n_throttle = 0; -} - -/* - * must get all pending data because we only get an irq on empty->non-empty - * transition - */ -static irqreturn_t hvsi_interrupt(int irq, void *arg) -{ - struct hvsi_struct *hp = (struct hvsi_struct *)arg; - struct tty_struct *flip; - struct tty_struct *hangup; - struct hvsi_struct *handshake; - unsigned long flags; - int again = 1; - - pr_debug("%s\n", __func__); - - while (again) { - spin_lock_irqsave(&hp->lock, flags); - again = hvsi_load_chunk(hp, &flip, &hangup, &handshake); - spin_unlock_irqrestore(&hp->lock, flags); - - /* - * we have to call tty_flip_buffer_push() and tty_hangup() outside our - * spinlock. But we also have to keep going until we've read all the - * available data. - */ - - if (flip) { - /* there was data put in the tty flip buffer */ - tty_flip_buffer_push(flip); - flip = NULL; - } - - if (hangup) { - tty_hangup(hangup); - } - - if (handshake) { - pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); - schedule_work(&handshake->handshaker); - } - } - - spin_lock_irqsave(&hp->lock, flags); - if (hp->tty && hp->n_throttle - && (!test_bit(TTY_THROTTLED, &hp->tty->flags))) { - /* we weren't hung up and we weren't throttled, so we can deliver the - * rest now */ - flip = hp->tty; - hvsi_send_overflow(hp); - } - spin_unlock_irqrestore(&hp->lock, flags); - - if (flip) { - tty_flip_buffer_push(flip); - } - - return IRQ_HANDLED; -} - -/* for boot console, before the irq handler is running */ -static int __init poll_for_state(struct hvsi_struct *hp, int state) -{ - unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; - - for (;;) { - hvsi_interrupt(hp->virq, (void *)hp); /* get pending data */ - - if (hp->state == state) - return 0; - - mdelay(5); - if (time_after(jiffies, end_jiffies)) - return -EIO; - } -} - -/* wait for irq handler to change our state */ -static int wait_for_state(struct hvsi_struct *hp, int state) -{ - int ret = 0; - - if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT)) - ret = -EIO; - - return ret; -} - -static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) -{ - struct hvsi_query packet __ALIGNED__; - int wrote; - - packet.type = VS_QUERY_PACKET_HEADER; - packet.len = sizeof(struct hvsi_query); - packet.seqno = atomic_inc_return(&hp->seqno); - packet.verb = verb; - - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); - - wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (wrote != packet.len) { - printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, - wrote); - return -EIO; - } - - return 0; -} - -static int hvsi_get_mctrl(struct hvsi_struct *hp) -{ - int ret; - - set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); - hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); - - ret = hvsi_wait(hp, HVSI_OPEN); - if (ret < 0) { - printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); - set_state(hp, HVSI_OPEN); - return ret; - } - - pr_debug("%s: mctrl 0x%x\n", __func__, hp->mctrl); - - return 0; -} - -/* note that we can only set DTR */ -static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) -{ - struct hvsi_control packet __ALIGNED__; - int wrote; - - packet.type = VS_CONTROL_PACKET_HEADER, - packet.seqno = atomic_inc_return(&hp->seqno); - packet.len = sizeof(struct hvsi_control); - packet.verb = VSV_SET_MODEM_CTL; - packet.mask = HVSI_TSDTR; - - if (mctrl & TIOCM_DTR) - packet.word = HVSI_TSDTR; - - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); - - wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (wrote != packet.len) { - printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); - return -EIO; - } - - return 0; -} - -static void hvsi_drain_input(struct hvsi_struct *hp) -{ - uint8_t buf[HVSI_MAX_READ] __ALIGNED__; - unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; - - while (time_before(end_jiffies, jiffies)) - if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) - break; -} - -static int hvsi_handshake(struct hvsi_struct *hp) -{ - int ret; - - /* - * We could have a CLOSE or other data waiting for us before we even try - * to open; try to throw it all away so we don't get confused. (CLOSE - * is the first message sent up the pipe when the FSP comes online. We - * need to distinguish between "it came up a while ago and we're the first - * user" and "it was just reset before it saw our handshake packet".) - */ - hvsi_drain_input(hp); - - set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); - ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); - if (ret < 0) { - printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); - return ret; - } - - ret = hvsi_wait(hp, HVSI_OPEN); - if (ret < 0) - return ret; - - return 0; -} - -static void hvsi_handshaker(struct work_struct *work) -{ - struct hvsi_struct *hp = - container_of(work, struct hvsi_struct, handshaker); - - if (hvsi_handshake(hp) >= 0) - return; - - printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); - if (is_console(hp)) { - /* - * ttys will re-attempt the handshake via hvsi_open, but - * the console will not. - */ - printk(KERN_ERR "hvsi%i: lost console!\n", hp->index); - } -} - -static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) -{ - struct hvsi_data packet __ALIGNED__; - int ret; - - BUG_ON(count > HVSI_MAX_OUTGOING_DATA); - - packet.type = VS_DATA_PACKET_HEADER; - packet.seqno = atomic_inc_return(&hp->seqno); - packet.len = count + sizeof(struct hvsi_header); - memcpy(&packet.data, buf, count); - - ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (ret == packet.len) { - /* return the number of chars written, not the packet length */ - return count; - } - return ret; /* return any errors */ -} - -static void hvsi_close_protocol(struct hvsi_struct *hp) -{ - struct hvsi_control packet __ALIGNED__; - - packet.type = VS_CONTROL_PACKET_HEADER; - packet.seqno = atomic_inc_return(&hp->seqno); - packet.len = 6; - packet.verb = VSV_CLOSE_PROTOCOL; - - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); - - hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); -} - -static int hvsi_open(struct tty_struct *tty, struct file *filp) -{ - struct hvsi_struct *hp; - unsigned long flags; - int line = tty->index; - int ret; - - pr_debug("%s\n", __func__); - - if (line < 0 || line >= hvsi_count) - return -ENODEV; - hp = &hvsi_ports[line]; - - tty->driver_data = hp; - - mb(); - if (hp->state == HVSI_FSP_DIED) - return -EIO; - - spin_lock_irqsave(&hp->lock, flags); - hp->tty = tty; - hp->count++; - atomic_set(&hp->seqno, 0); - h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); - spin_unlock_irqrestore(&hp->lock, flags); - - if (is_console(hp)) - return 0; /* this has already been handshaked as the console */ - - ret = hvsi_handshake(hp); - if (ret < 0) { - printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); - return ret; - } - - ret = hvsi_get_mctrl(hp); - if (ret < 0) { - printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); - return ret; - } - - ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); - if (ret < 0) { - printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); - return ret; - } - - return 0; -} - -/* wait for hvsi_write_worker to empty hp->outbuf */ -static void hvsi_flush_output(struct hvsi_struct *hp) -{ - wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT); - - /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ - cancel_delayed_work(&hp->writer); - flush_scheduled_work(); - - /* - * it's also possible that our timeout expired and hvsi_write_worker - * didn't manage to push outbuf. poof. - */ - hp->n_outbuf = 0; -} - -static void hvsi_close(struct tty_struct *tty, struct file *filp) -{ - struct hvsi_struct *hp = tty->driver_data; - unsigned long flags; - - pr_debug("%s\n", __func__); - - if (tty_hung_up_p(filp)) - return; - - spin_lock_irqsave(&hp->lock, flags); - - if (--hp->count == 0) { - hp->tty = NULL; - hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ - - /* only close down connection if it is not the console */ - if (!is_console(hp)) { - h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ - __set_state(hp, HVSI_CLOSED); - /* - * any data delivered to the tty layer after this will be - * discarded (except for XON/XOFF) - */ - tty->closing = 1; - - spin_unlock_irqrestore(&hp->lock, flags); - - /* let any existing irq handlers finish. no more will start. */ - synchronize_irq(hp->virq); - - /* hvsi_write_worker will re-schedule until outbuf is empty. */ - hvsi_flush_output(hp); - - /* tell FSP to stop sending data */ - hvsi_close_protocol(hp); - - /* - * drain anything FSP is still in the middle of sending, and let - * hvsi_handshake drain the rest on the next open. - */ - hvsi_drain_input(hp); - - spin_lock_irqsave(&hp->lock, flags); - } - } else if (hp->count < 0) - printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", - hp - hvsi_ports, hp->count); - - spin_unlock_irqrestore(&hp->lock, flags); -} - -static void hvsi_hangup(struct tty_struct *tty) -{ - struct hvsi_struct *hp = tty->driver_data; - unsigned long flags; - - pr_debug("%s\n", __func__); - - spin_lock_irqsave(&hp->lock, flags); - - hp->count = 0; - hp->n_outbuf = 0; - hp->tty = NULL; - - spin_unlock_irqrestore(&hp->lock, flags); -} - -/* called with hp->lock held */ -static void hvsi_push(struct hvsi_struct *hp) -{ - int n; - - if (hp->n_outbuf <= 0) - return; - - n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); - if (n > 0) { - /* success */ - pr_debug("%s: wrote %i chars\n", __func__, n); - hp->n_outbuf = 0; - } else if (n == -EIO) { - __set_state(hp, HVSI_FSP_DIED); - printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); - } -} - -/* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ -static void hvsi_write_worker(struct work_struct *work) -{ - struct hvsi_struct *hp = - container_of(work, struct hvsi_struct, writer.work); - unsigned long flags; -#ifdef DEBUG - static long start_j = 0; - - if (start_j == 0) - start_j = jiffies; -#endif /* DEBUG */ - - spin_lock_irqsave(&hp->lock, flags); - - pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); - - if (!is_open(hp)) { - /* - * We could have a non-open connection if the service processor died - * while we were busily scheduling ourselves. In that case, it could - * be minutes before the service processor comes back, so only try - * again once a second. - */ - schedule_delayed_work(&hp->writer, HZ); - goto out; - } - - hvsi_push(hp); - if (hp->n_outbuf > 0) - schedule_delayed_work(&hp->writer, 10); - else { -#ifdef DEBUG - pr_debug("%s: outbuf emptied after %li jiffies\n", __func__, - jiffies - start_j); - start_j = 0; -#endif /* DEBUG */ - wake_up_all(&hp->emptyq); - tty_wakeup(hp->tty); - } - -out: - spin_unlock_irqrestore(&hp->lock, flags); -} - -static int hvsi_write_room(struct tty_struct *tty) -{ - struct hvsi_struct *hp = tty->driver_data; - - return N_OUTBUF - hp->n_outbuf; -} - -static int hvsi_chars_in_buffer(struct tty_struct *tty) -{ - struct hvsi_struct *hp = tty->driver_data; - - return hp->n_outbuf; -} - -static int hvsi_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct hvsi_struct *hp = tty->driver_data; - const char *source = buf; - unsigned long flags; - int total = 0; - int origcount = count; - - spin_lock_irqsave(&hp->lock, flags); - - pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); - - if (!is_open(hp)) { - /* we're either closing or not yet open; don't accept data */ - pr_debug("%s: not open\n", __func__); - goto out; - } - - /* - * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf - * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls - * will see there is no room in outbuf and return. - */ - while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { - int chunksize = min(count, hvsi_write_room(hp->tty)); - - BUG_ON(hp->n_outbuf < 0); - memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); - hp->n_outbuf += chunksize; - - total += chunksize; - source += chunksize; - count -= chunksize; - hvsi_push(hp); - } - - if (hp->n_outbuf > 0) { - /* - * we weren't able to write it all to the hypervisor. - * schedule another push attempt. - */ - schedule_delayed_work(&hp->writer, 10); - } - -out: - spin_unlock_irqrestore(&hp->lock, flags); - - if (total != origcount) - pr_debug("%s: wanted %i, only wrote %i\n", __func__, origcount, - total); - - return total; -} - -/* - * I have never seen throttle or unthrottle called, so this little throttle - * buffering scheme may or may not work. - */ -static void hvsi_throttle(struct tty_struct *tty) -{ - struct hvsi_struct *hp = tty->driver_data; - - pr_debug("%s\n", __func__); - - h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); -} - -static void hvsi_unthrottle(struct tty_struct *tty) -{ - struct hvsi_struct *hp = tty->driver_data; - unsigned long flags; - int shouldflip = 0; - - pr_debug("%s\n", __func__); - - spin_lock_irqsave(&hp->lock, flags); - if (hp->n_throttle) { - hvsi_send_overflow(hp); - shouldflip = 1; - } - spin_unlock_irqrestore(&hp->lock, flags); - - if (shouldflip) - tty_flip_buffer_push(hp->tty); - - h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); -} - -static int hvsi_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct hvsi_struct *hp = tty->driver_data; - - hvsi_get_mctrl(hp); - return hp->mctrl; -} - -static int hvsi_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct hvsi_struct *hp = tty->driver_data; - unsigned long flags; - uint16_t new_mctrl; - - /* we can only alter DTR */ - clear &= TIOCM_DTR; - set &= TIOCM_DTR; - - spin_lock_irqsave(&hp->lock, flags); - - new_mctrl = (hp->mctrl & ~clear) | set; - - if (hp->mctrl != new_mctrl) { - hvsi_set_mctrl(hp, new_mctrl); - hp->mctrl = new_mctrl; - } - spin_unlock_irqrestore(&hp->lock, flags); - - return 0; -} - - -static const struct tty_operations hvsi_ops = { - .open = hvsi_open, - .close = hvsi_close, - .write = hvsi_write, - .hangup = hvsi_hangup, - .write_room = hvsi_write_room, - .chars_in_buffer = hvsi_chars_in_buffer, - .throttle = hvsi_throttle, - .unthrottle = hvsi_unthrottle, - .tiocmget = hvsi_tiocmget, - .tiocmset = hvsi_tiocmset, -}; - -static int __init hvsi_init(void) -{ - int i; - - hvsi_driver = alloc_tty_driver(hvsi_count); - if (!hvsi_driver) - return -ENOMEM; - - hvsi_driver->owner = THIS_MODULE; - hvsi_driver->driver_name = "hvsi"; - hvsi_driver->name = "hvsi"; - hvsi_driver->major = HVSI_MAJOR; - hvsi_driver->minor_start = HVSI_MINOR; - hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM; - hvsi_driver->init_termios = tty_std_termios; - hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; - hvsi_driver->init_termios.c_ispeed = 9600; - hvsi_driver->init_termios.c_ospeed = 9600; - hvsi_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(hvsi_driver, &hvsi_ops); - - for (i=0; i < hvsi_count; i++) { - struct hvsi_struct *hp = &hvsi_ports[i]; - int ret = 1; - - ret = request_irq(hp->virq, hvsi_interrupt, IRQF_DISABLED, "hvsi", hp); - if (ret) - printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", - hp->virq, ret); - } - hvsi_wait = wait_for_state; /* irqs active now */ - - if (tty_register_driver(hvsi_driver)) - panic("Couldn't register hvsi console driver\n"); - - printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count); - - return 0; -} -device_initcall(hvsi_init); - -/***** console (not tty) code: *****/ - -static void hvsi_console_print(struct console *console, const char *buf, - unsigned int count) -{ - struct hvsi_struct *hp = &hvsi_ports[console->index]; - char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; - unsigned int i = 0, n = 0; - int ret, donecr = 0; - - mb(); - if (!is_open(hp)) - return; - - /* - * ugh, we have to translate LF -> CRLF ourselves, in place. - * copied from hvc_console.c: - */ - while (count > 0 || i > 0) { - if (count > 0 && i < sizeof(c)) { - if (buf[n] == '\n' && !donecr) { - c[i++] = '\r'; - donecr = 1; - } else { - c[i++] = buf[n++]; - donecr = 0; - --count; - } - } else { - ret = hvsi_put_chars(hp, c, i); - if (ret < 0) - i = 0; - i -= ret; - } - } -} - -static struct tty_driver *hvsi_console_device(struct console *console, - int *index) -{ - *index = console->index; - return hvsi_driver; -} - -static int __init hvsi_console_setup(struct console *console, char *options) -{ - struct hvsi_struct *hp; - int ret; - - if (console->index < 0 || console->index >= hvsi_count) - return -1; - hp = &hvsi_ports[console->index]; - - /* give the FSP a chance to change the baud rate when we re-open */ - hvsi_close_protocol(hp); - - ret = hvsi_handshake(hp); - if (ret < 0) - return ret; - - ret = hvsi_get_mctrl(hp); - if (ret < 0) - return ret; - - ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); - if (ret < 0) - return ret; - - hp->flags |= HVSI_CONSOLE; - - return 0; -} - -static struct console hvsi_console = { - .name = "hvsi", - .write = hvsi_console_print, - .device = hvsi_console_device, - .setup = hvsi_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -static int __init hvsi_console_init(void) -{ - struct device_node *vty; - - hvsi_wait = poll_for_state; /* no irqs yet; must poll */ - - /* search device tree for vty nodes */ - for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); - vty != NULL; - vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { - struct hvsi_struct *hp; - const uint32_t *vtermno, *irq; - - vtermno = of_get_property(vty, "reg", NULL); - irq = of_get_property(vty, "interrupts", NULL); - if (!vtermno || !irq) - continue; - - if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { - of_node_put(vty); - break; - } - - hp = &hvsi_ports[hvsi_count]; - INIT_DELAYED_WORK(&hp->writer, hvsi_write_worker); - INIT_WORK(&hp->handshaker, hvsi_handshaker); - init_waitqueue_head(&hp->emptyq); - init_waitqueue_head(&hp->stateq); - spin_lock_init(&hp->lock); - hp->index = hvsi_count; - hp->inbuf_end = hp->inbuf; - hp->state = HVSI_CLOSED; - hp->vtermno = *vtermno; - hp->virq = irq_create_mapping(NULL, irq[0]); - if (hp->virq == NO_IRQ) { - printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", - __func__, irq[0]); - continue; - } - - hvsi_count++; - } - - if (hvsi_count) - register_console(&hvsi_console); - return 0; -} -console_initcall(hvsi_console_init); diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 788da05190cc..2016aad85203 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -37,7 +37,6 @@ #include <linux/kernel.h> #include <linux/fs.h> #include <linux/sched.h> -#include <linux/smp_lock.h> #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/delay.h> diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c index 794aacb715c1..d0387a84eec1 100644 --- a/drivers/char/hw_random/via-rng.c +++ b/drivers/char/hw_random/via-rng.c @@ -24,6 +24,7 @@ * warranty of any kind, whether express or implied. */ +#include <crypto/padlock.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/hw_random.h> @@ -34,7 +35,6 @@ #include <asm/i387.h> -#define PFX KBUILD_MODNAME ": " enum { @@ -81,8 +81,7 @@ static inline u32 xstore(u32 *addr, u32 edx_in) ts_state = irq_ts_save(); asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */" - :"=m"(*addr), "=a"(eax_out) - :"D"(addr), "d"(edx_in)); + : "=m" (*addr), "=a" (eax_out), "+d" (edx_in), "+D" (addr)); irq_ts_restore(ts_state); return eax_out; @@ -90,8 +89,10 @@ static inline u32 xstore(u32 *addr, u32 edx_in) static int via_rng_data_present(struct hwrng *rng, int wait) { + char buf[16 + PADLOCK_ALIGNMENT - STACK_ALIGN] __attribute__ + ((aligned(STACK_ALIGN))); + u32 *via_rng_datum = (u32 *)PTR_ALIGN(&buf[0], PADLOCK_ALIGNMENT); u32 bytes_out; - u32 *via_rng_datum = (u32 *)(&rng->priv); int i; /* We choose the recommended 1-byte-per-instruction RNG rate, @@ -115,6 +116,7 @@ static int via_rng_data_present(struct hwrng *rng, int wait) break; udelay(10); } + rng->priv = *via_rng_datum; return bytes_out ? 1 : 0; } diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 3bc0eef88717..d72433f2d310 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -120,7 +120,7 @@ static int i8k_smm(struct smm_regs *regs) int eax = regs->eax; #if defined(CONFIG_X86_64) - asm("pushq %%rax\n\t" + asm volatile("pushq %%rax\n\t" "movl 0(%%rax),%%edx\n\t" "pushq %%rdx\n\t" "movl 4(%%rax),%%ebx\n\t" @@ -146,7 +146,7 @@ static int i8k_smm(struct smm_regs *regs) : "a"(regs) : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); #else - asm("pushl %%eax\n\t" + asm volatile("pushl %%eax\n\t" "movl 0(%%eax),%%edx\n\t" "push %%edx\n\t" "movl 4(%%eax),%%ebx\n\t" @@ -167,7 +167,8 @@ static int i8k_smm(struct smm_regs *regs) "movl %%edx,0(%%eax)\n\t" "lahf\n\t" "shrl $8,%%eax\n\t" - "andl $1,%%eax\n":"=a"(rc) + "andl $1,%%eax\n" + :"=a"(rc) : "a"(regs) : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); #endif diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index fcd02baa7d65..c3a025356b8b 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -3224,7 +3224,7 @@ ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned lo MODULE_LICENSE("GPL"); -static struct pci_device_id ip2main_pci_tbl[] __devinitdata = { +static struct pci_device_id ip2main_pci_tbl[] __devinitdata __used = { { PCI_DEVICE(PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_IP2EX) }, { } }; diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 2fe72f8edf44..38223e93aa98 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -970,6 +970,33 @@ out_kfree: } EXPORT_SYMBOL(ipmi_create_user); +int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data) +{ + int rv = 0; + ipmi_smi_t intf; + struct ipmi_smi_handlers *handlers; + + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + if (intf->intf_num == if_num) + goto found; + } + /* Not found, return an error */ + rv = -EINVAL; + mutex_unlock(&ipmi_interfaces_mutex); + return rv; + +found: + handlers = intf->handlers; + rv = -ENOSYS; + if (handlers->get_smi_info) + rv = handlers->get_smi_info(intf->send_info, data); + mutex_unlock(&ipmi_interfaces_mutex); + + return rv; +} +EXPORT_SYMBOL(ipmi_get_smi_info); + static void free_user(struct kref *ref) { ipmi_user_t user = container_of(ref, struct ipmi_user, refcount); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 035da9e64a17..b6ae6e9a9c5f 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -57,6 +57,7 @@ #include <asm/irq.h> #include <linux/interrupt.h> #include <linux/rcupdate.h> +#include <linux/ipmi.h> #include <linux/ipmi_smi.h> #include <asm/io.h> #include "ipmi_si_sm.h" @@ -69,6 +70,8 @@ #ifdef CONFIG_PPC_OF #include <linux/of_device.h> #include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #endif #define PFX "ipmi_si: " @@ -107,10 +110,6 @@ enum si_type { }; static char *si_to_str[] = { "kcs", "smic", "bt" }; -enum ipmi_addr_src { - SI_INVALID = 0, SI_HOTMOD, SI_HARDCODED, SI_SPMI, SI_ACPI, SI_SMBIOS, - SI_PCI, SI_DEVICETREE, SI_DEFAULT -}; static char *ipmi_addr_src_to_str[] = { NULL, "hotmod", "hardcoded", "SPMI", "ACPI", "SMBIOS", "PCI", "device-tree", "default" }; @@ -291,6 +290,7 @@ struct smi_info { struct task_struct *thread; struct list_head link; + union ipmi_smi_info_union addr_info; }; #define smi_inc_stat(smi, stat) \ @@ -1186,6 +1186,18 @@ static int smi_start_processing(void *send_info, return 0; } +static int get_smi_info(void *send_info, struct ipmi_smi_info *data) +{ + struct smi_info *smi = send_info; + + data->addr_src = smi->addr_source; + data->dev = smi->dev; + data->addr_info = smi->addr_info; + get_device(smi->dev); + + return 0; +} + static void set_maintenance_mode(void *send_info, int enable) { struct smi_info *smi_info = send_info; @@ -1197,6 +1209,7 @@ static void set_maintenance_mode(void *send_info, int enable) static struct ipmi_smi_handlers handlers = { .owner = THIS_MODULE, .start_processing = smi_start_processing, + .get_smi_info = get_smi_info, .sender = sender, .request_events = request_events, .set_maintenance_mode = set_maintenance_mode, @@ -1928,7 +1941,8 @@ static void __devinit hardcode_find_bmc(void) static int acpi_failure; /* For GPE-type interrupts. */ -static u32 ipmi_acpi_gpe(void *context) +static u32 ipmi_acpi_gpe(acpi_handle gpe_device, + u32 gpe_number, void *context) { struct smi_info *smi_info = context; unsigned long flags; @@ -2156,6 +2170,7 @@ static int __devinit ipmi_pnp_probe(struct pnp_dev *dev, printk(KERN_INFO PFX "probing via ACPI\n"); handle = acpi_dev->handle; + info->addr_info.acpi_info.acpi_handle = handle; /* _IFT tells us the interface type: KCS, BT, etc */ status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp); @@ -2546,7 +2561,7 @@ static int __devinit ipmi_of_probe(struct platform_device *dev, { struct smi_info *info; struct resource resource; - const int *regsize, *regspacing, *regshift; + const __be32 *regsize, *regspacing, *regshift; struct device_node *np = dev->dev.of_node; int ret; int proplen; @@ -2599,9 +2614,9 @@ static int __devinit ipmi_of_probe(struct platform_device *dev, info->io.addr_data = resource.start; - info->io.regsize = regsize ? *regsize : DEFAULT_REGSIZE; - info->io.regspacing = regspacing ? *regspacing : DEFAULT_REGSPACING; - info->io.regshift = regshift ? *regshift : 0; + info->io.regsize = regsize ? be32_to_cpup(regsize) : DEFAULT_REGSIZE; + info->io.regspacing = regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING; + info->io.regshift = regshift ? be32_to_cpup(regshift) : 0; info->irq = irq_of_parse_and_map(dev->dev.of_node, 0); info->dev = &dev->dev; diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index f4d334f2536e..320668f4c3aa 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1081,7 +1081,7 @@ ipmi_nmi(struct notifier_block *self, unsigned long val, void *data) { struct die_args *args = data; - if (val != DIE_NMI) + if (val != DIE_NMIUNKNOWN) return NOTIFY_OK; /* Hack, if it's a memory or I/O error, ignore it. */ diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 667abd23ad6a..7c6de4c92458 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -21,7 +21,6 @@ #include <linux/module.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/tty_flip.h> diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c deleted file mode 100644 index e95d7876ca6b..000000000000 --- a/drivers/char/keyboard.c +++ /dev/null @@ -1,1454 +0,0 @@ -/* - * linux/drivers/char/keyboard.c - * - * Written for linux by Johan Myreen as a translation from - * the assembly version by Linus (with diacriticals added) - * - * Some additional features added by Christoph Niemann (ChN), March 1993 - * - * Loadable keymaps by Risto Kankkunen, May 1993 - * - * Diacriticals redone & other small changes, aeb@cwi.nl, June 1993 - * Added decr/incr_console, dynamic keymaps, Unicode support, - * dynamic function/string keys, led setting, Sept 1994 - * `Sticky' modifier keys, 951006. - * - * 11-11-96: SAK should now work in the raw mode (Martin Mares) - * - * Modified to provide 'generic' keyboard support by Hamish Macdonald - * Merge with the m68k keyboard driver and split-off of the PC low-level - * parts by Geert Uytterhoeven, May 1997 - * - * 27-05-97: Added support for the Magic SysRq Key (Martin Mares) - * 30-07-98: Dead keys redone, aeb@cwi.nl. - * 21-08-02: Converted to input API, major cleanup. (Vojtech Pavlik) - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <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; -} diff --git a/drivers/char/n_gsm.c b/drivers/char/n_gsm.c deleted file mode 100644 index 04ef3ef0a422..000000000000 --- a/drivers/char/n_gsm.c +++ /dev/null @@ -1,2763 +0,0 @@ -/* - * n_gsm.c GSM 0710 tty multiplexor - * Copyright (c) 2009/10 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * * THIS IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE * - * - * TO DO: - * Mostly done: ioctls for setting modes/timing - * Partly done: hooks so you can pull off frames to non tty devs - * Restart DLCI 0 when it closes ? - * Test basic encoding - * Improve the tx engine - * Resolve tx side locking by adding a queue_head and routing - * all control traffic via it - * General tidy/document - * Review the locking/move to refcounts more (mux now moved to an - * alloc/free model ready) - * Use newest tty open/close port helpers and install hooks - * What to do about power functions ? - * Termios setting and negotiation - * Do we need a 'which mux are you' ioctl to correlate mux and tty sets - * - */ - -#include <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); diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c deleted file mode 100644 index 47d32281032c..000000000000 --- a/drivers/char/n_hdlc.c +++ /dev/null @@ -1,1007 +0,0 @@ -/* generic HDLC line discipline for Linux - * - * Written by Paul Fulghum paulkf@microgate.com - * for Microgate Corporation - * - * Microgate and SyncLink are registered trademarks of Microgate Corporation - * - * Adapted from ppp.c, written by Michael Callahan <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); diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c deleted file mode 100644 index 88dda0c45ee0..000000000000 --- a/drivers/char/n_r3964.c +++ /dev/null @@ -1,1264 +0,0 @@ -/* r3964 linediscipline for linux - * - * ----------------------------------------------------------- - * Copyright by - * Philips Automation Projects - * Kassel (Germany) - * ----------------------------------------------------------- - * This software may be used and distributed according to the terms of - * the GNU General Public License, incorporated herein by reference. - * - * Author: - * L. Haag - * - * $Log: n_r3964.c,v $ - * Revision 1.10 2001/03/18 13:02:24 dwmw2 - * Fix timer usage, use spinlocks properly. - * - * Revision 1.9 2001/03/18 12:52:14 dwmw2 - * Merge changes in 2.4.2 - * - * Revision 1.8 2000/03/23 14:14:54 dwmw2 - * Fix race in sleeping in r3964_read() - * - * Revision 1.7 1999/28/08 11:41:50 dwmw2 - * Port to 2.3 kernel - * - * Revision 1.6 1998/09/30 00:40:40 dwmw2 - * Fixed compilation on 2.0.x kernels - * Updated to newly registered tty-ldisc number 9 - * - * Revision 1.5 1998/09/04 21:57:36 dwmw2 - * Signal handling bug fixes, port to 2.1.x. - * - * Revision 1.4 1998/04/02 20:26:59 lhaag - * select, blocking, ... - * - * Revision 1.3 1998/02/12 18:58:43 root - * fixed some memory leaks - * calculation of checksum characters - * - * Revision 1.2 1998/02/07 13:03:34 root - * ioctl read_telegram - * - * Revision 1.1 1998/02/06 19:21:03 root - * Initial revision - * - * - */ - -#include <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); diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c deleted file mode 100644 index 428f4fe0b5f7..000000000000 --- a/drivers/char/n_tty.c +++ /dev/null @@ -1,2121 +0,0 @@ -/* - * n_tty.c --- implements the N_TTY line discipline. - * - * This code used to be in tty_io.c, but things are getting hairy - * enough that it made sense to split things off. (The N_TTY - * processing has changed so much that it's hardly recognizable, - * anyway...) - * - * Note that the open routine for N_TTY is guaranteed never to return - * an error. This is because Linux will fall back to setting a line - * to N_TTY if it can not switch to any other line discipline. - * - * Written by Theodore Ts'o, Copyright 1994. - * - * This file also contains code originally written by Linus Torvalds, - * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994. - * - * This file may be redistributed under the terms of the GNU General Public - * License. - * - * Reduced memory usage for older ARM systems - Russell King. - * - * 2000/01/20 Fixed SMP locking on put_tty_queue using bits of - * the patch by Andrew J. Kroll <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); diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index dd3f9b1f11b4..294d03e8c61a 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -1828,7 +1828,6 @@ static int ntty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { struct port *port = tty->driver_data; - void __user *argp = (void __user *)arg; int rval = -ENOIOCTLCMD; DBG1("******** IOCTL, cmd: %d", cmd); diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index d962f25dcc2a..777181a2e603 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -979,8 +979,9 @@ static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count, if (dev->flags0 & 1) { set_bit(IS_CMM_ABSENT, &dev->flags); rc = -ENODEV; + } else { + rc = -EIO; } - rc = -EIO; goto release_io; } diff --git a/drivers/char/pcmcia/ipwireless/hardware.c b/drivers/char/pcmcia/ipwireless/hardware.c index 99cffdab1056..0aeb5a38d296 100644 --- a/drivers/char/pcmcia/ipwireless/hardware.c +++ b/drivers/char/pcmcia/ipwireless/hardware.c @@ -1729,7 +1729,7 @@ void ipwireless_hardware_free(struct ipw_hardware *hw) ipwireless_stop_interrupts(hw); - flush_scheduled_work(); + flush_work_sync(&hw->work_rx); for (i = 0; i < NL_NUM_OF_ADDRESSES; i++) if (hw->packet_assembler[i] != NULL) diff --git a/drivers/char/pcmcia/ipwireless/network.c b/drivers/char/pcmcia/ipwireless/network.c index 9fe538347932..f7daeea598e4 100644 --- a/drivers/char/pcmcia/ipwireless/network.c +++ b/drivers/char/pcmcia/ipwireless/network.c @@ -430,7 +430,8 @@ void ipwireless_network_free(struct ipw_network *network) network->shutting_down = 1; ipwireless_ppp_close(network); - flush_scheduled_work(); + flush_work_sync(&network->work_go_online); + flush_work_sync(&network->work_go_offline); ipwireless_stop_interrupts(network->hardware); ipwireless_associate_network(network->hardware, NULL); diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index 1a2c2c3b068f..f5eb28b6cb0f 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -577,7 +577,7 @@ void ipwireless_tty_free(struct ipw_tty *tty) mutex_unlock(&ttyj->ipw_tty_mutex); tty_hangup(ttyj->linux_tty); /* Wait till the tty_hangup has completed */ - flush_scheduled_work(); + flush_work_sync(&ttyj->linux_tty->hangup_work); /* FIXME: Exactly how is the tty object locked here against a parallel ioctl etc */ mutex_lock(&ttyj->ipw_tty_mutex); diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index bfc10f89d951..eaa41992fbe2 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2796,6 +2796,7 @@ static const struct tty_operations mgslpc_ops = { .hangup = mgslpc_hangup, .tiocmget = tiocmget, .tiocmset = tiocmset, + .get_icount = mgslpc_get_icount, .proc_fops = &mgslpc_proc_fops, }; diff --git a/drivers/char/pty.c b/drivers/char/pty.c deleted file mode 100644 index 923a48585501..000000000000 --- a/drivers/char/pty.c +++ /dev/null @@ -1,777 +0,0 @@ -/* - * linux/drivers/char/pty.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * Added support for a Unix98-style ptmx device. - * -- C. Scott Ananian <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); diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c index 73dcb0ee41fd..1a9f5f6d6ac5 100644 --- a/drivers/char/ramoops.c +++ b/drivers/char/ramoops.c @@ -29,9 +29,8 @@ #include <linux/ramoops.h> #define RAMOOPS_KERNMSG_HDR "====" -#define RAMOOPS_HEADER_SIZE (5 + sizeof(struct timeval)) -#define RECORD_SIZE 4096 +#define RECORD_SIZE 4096UL static ulong mem_address; module_param(mem_address, ulong, 0400); @@ -65,15 +64,22 @@ static void ramoops_do_dump(struct kmsg_dumper *dumper, struct ramoops_context, dump); unsigned long s1_start, s2_start; unsigned long l1_cpy, l2_cpy; - int res; - char *buf; + int res, hdr_size; + char *buf, *buf_orig; struct timeval timestamp; + if (reason != KMSG_DUMP_OOPS && + reason != KMSG_DUMP_PANIC && + reason != KMSG_DUMP_KEXEC) + return; + /* Only dump oopses if dump_oops is set */ if (reason == KMSG_DUMP_OOPS && !dump_oops) return; - buf = (char *)(cxt->virt_addr + (cxt->count * RECORD_SIZE)); + buf = cxt->virt_addr + (cxt->count * RECORD_SIZE); + buf_orig = buf; + memset(buf, '\0', RECORD_SIZE); res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR); buf += res; @@ -81,8 +87,9 @@ static void ramoops_do_dump(struct kmsg_dumper *dumper, res = sprintf(buf, "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec); buf += res; - l2_cpy = min(l2, (unsigned long)(RECORD_SIZE - RAMOOPS_HEADER_SIZE)); - l1_cpy = min(l1, (unsigned long)(RECORD_SIZE - RAMOOPS_HEADER_SIZE) - l2_cpy); + hdr_size = buf - buf_orig; + l2_cpy = min(l2, RECORD_SIZE - hdr_size); + l1_cpy = min(l1, RECORD_SIZE - hdr_size - l2_cpy); s2_start = l2 - l2_cpy; s1_start = l1 - l1_cpy; diff --git a/drivers/char/random.c b/drivers/char/random.c index 5a1aa64f4e76..72a4fcb17745 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -626,7 +626,7 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num) preempt_disable(); /* if over the trickle threshold, use only 1 in 4096 samples */ if (input_pool.entropy_count > trickle_thresh && - (__get_cpu_var(trickle_count)++ & 0xfff)) + ((__this_cpu_inc_return(trickle_count) - 1) & 0xfff)) goto out; sample.jiffies = jiffies; diff --git a/drivers/char/raw.c b/drivers/char/raw.c index bfe25ea9766b..b4b9d5a47885 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -65,15 +65,12 @@ static int raw_open(struct inode *inode, struct file *filp) if (!bdev) goto out; igrab(bdev->bd_inode); - err = blkdev_get(bdev, filp->f_mode); + err = blkdev_get(bdev, filp->f_mode | FMODE_EXCL, raw_open); if (err) goto out; - err = bd_claim(bdev, raw_open); - if (err) - goto out1; err = set_blocksize(bdev, bdev_logical_block_size(bdev)); if (err) - goto out2; + goto out1; filp->f_flags |= O_DIRECT; filp->f_mapping = bdev->bd_inode->i_mapping; if (++raw_devices[minor].inuse == 1) @@ -83,10 +80,8 @@ static int raw_open(struct inode *inode, struct file *filp) mutex_unlock(&raw_mutex); return 0; -out2: - bd_release(bdev); out1: - blkdev_put(bdev, filp->f_mode); + blkdev_put(bdev, filp->f_mode | FMODE_EXCL); out: mutex_unlock(&raw_mutex); return err; @@ -110,8 +105,7 @@ static int raw_release(struct inode *inode, struct file *filp) } mutex_unlock(&raw_mutex); - bd_release(bdev); - blkdev_put(bdev, filp->f_mode); + blkdev_put(bdev, filp->f_mode | FMODE_EXCL); return 0; } diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index 86308830ac42..3e4e73a0d7c1 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -1764,7 +1764,7 @@ static void rp_flush_buffer(struct tty_struct *tty) #ifdef CONFIG_PCI -static struct pci_device_id __devinitdata rocket_pci_ids[] = { +static struct pci_device_id __devinitdata __used rocket_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_RP, PCI_ANY_ID) }, { } }; diff --git a/drivers/char/selection.c b/drivers/char/selection.c deleted file mode 100644 index ebae344ce910..000000000000 --- a/drivers/char/selection.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * linux/drivers/char/selection.c - * - * This module exports the functions: - * - * 'int set_selection(struct tiocl_selection __user *, struct tty_struct *)' - * 'void clear_selection(void)' - * 'int paste_selection(struct tty_struct *)' - * 'int sel_loadlut(char __user *)' - * - * Now that /dev/vcs exists, most of this can disappear again. - */ - -#include <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; -} diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index f646725bd567..748c3b0ecd89 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -52,7 +52,6 @@ #include <linux/interrupt.h> #include <linux/serial.h> #include <linux/serialP.h> -#include <linux/smp_lock.h> #include <linux/string.h> #include <linux/fcntl.h> #include <linux/ptrace.h> diff --git a/drivers/char/snsc.h b/drivers/char/snsc.h index 4be62eda9fbc..e8c52c882b21 100644 --- a/drivers/char/snsc.h +++ b/drivers/char/snsc.h @@ -19,7 +19,6 @@ #include <linux/types.h> #include <linux/spinlock.h> #include <linux/wait.h> -#include <linux/kobject.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/semaphore.h> diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 73f66d03624d..79e36c878a4c 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1434,7 +1434,7 @@ static int __devexit sonypi_remove(struct platform_device *dev) sonypi_disable(); synchronize_irq(sonypi_device.irq); - flush_scheduled_work(); + flush_work_sync(&sonypi_device.input_work); if (useinput) { input_unregister_device(sonypi_device.input_key_dev); diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 9f8495b4fc8f..c2bca3f25ef3 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -87,7 +87,6 @@ #include <linux/tty_flip.h> #include <linux/mm.h> #include <linux/serial.h> -#include <linux/smp_lock.h> #include <linux/fcntl.h> #include <linux/major.h> #include <linux/delay.h> @@ -2356,7 +2355,7 @@ static void __exit specialix_exit_module(void) func_exit(); } -static struct pci_device_id specialx_pci_tbl[] __devinitdata = { +static struct pci_device_id specialx_pci_tbl[] __devinitdata __used = { { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) }, { } }; diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 4bef6ab83622..461a5a045517 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -40,7 +40,6 @@ #include <linux/stallion.h> #include <linux/ioport.h> #include <linux/init.h> -#include <linux/smp_lock.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/ctype.h> diff --git a/drivers/char/sx.c b/drivers/char/sx.c index e53f16865397..a786326cea2f 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -216,7 +216,6 @@ #include <linux/eisa.h> #include <linux/pci.h> #include <linux/slab.h> -#include <linux/smp_lock.h> #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/bitops.h> diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c deleted file mode 100644 index eaa5d3efa79d..000000000000 --- a/drivers/char/sysrq.c +++ /dev/null @@ -1,811 +0,0 @@ -/* - * Linux Magic System Request Key Hacks - * - * (c) 1997 Martin Mares <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); diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 7c4133582dba..36e0fa161c2b 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -364,12 +364,14 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, tpm_protected_ordinal_duration[ordinal & TPM_PROTECTED_ORDINAL_MASK]; - if (duration_idx != TPM_UNDEFINED) + if (duration_idx != TPM_UNDEFINED) { duration = chip->vendor.duration[duration_idx]; - if (duration <= 0) + /* if duration is 0, it's because chip->vendor.duration wasn't */ + /* filled yet, so we set the lowest timeout just to give enough */ + /* time for tpm_get_timeouts() to succeed */ + return (duration <= 0 ? HZ : duration); + } else return 2 * 60 * HZ; - else - return duration; } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); @@ -736,7 +738,7 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) if (chip == NULL) return -ENODEV; rc = __tpm_pcr_read(chip, pcr_idx, res_buf); - module_put(chip->dev->driver->owner); + tpm_chip_put(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_pcr_read); @@ -775,11 +777,27 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) rc = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, "attempting extend a PCR value"); - module_put(chip->dev->driver->owner); + tpm_chip_put(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_pcr_extend); +int tpm_send(u32 chip_num, void *cmd, size_t buflen) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL) + return -ENODEV; + + rc = transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd"); + + tpm_chip_put(chip); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_send); + ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, char *buf) { @@ -986,7 +1004,7 @@ int tpm_release(struct inode *inode, struct file *file) struct tpm_chip *chip = file->private_data; del_singleshot_timer_sync(&chip->user_read_timer); - flush_scheduled_work(); + flush_work_sync(&chip->work); file->private_data = NULL; atomic_set(&chip->data_pending, 0); kfree(chip->data_buffer); @@ -1038,7 +1056,7 @@ ssize_t tpm_read(struct file *file, char __user *buf, ssize_t ret_size; del_singleshot_timer_sync(&chip->user_read_timer); - flush_scheduled_work(); + flush_work_sync(&chip->work); ret_size = atomic_read(&chip->data_pending); atomic_set(&chip->data_pending, 0); if (ret_size > 0) { /* relay data */ diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 792868d24f2a..72ddb031b69a 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -113,6 +113,11 @@ struct tpm_chip { #define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor) +static inline void tpm_chip_put(struct tpm_chip *chip) +{ + module_put(chip->dev->driver->owner); +} + static inline int tpm_read_index(int base, int index) { outb(index, base); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 1030f8420137..dd21df55689d 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -25,6 +25,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/wait.h> +#include <linux/acpi.h> #include "tpm.h" #define TPM_HEADER_SIZE 10 @@ -78,6 +79,26 @@ enum tis_defaults { static LIST_HEAD(tis_chips); static DEFINE_SPINLOCK(tis_lock); +#ifdef CONFIG_ACPI +static int is_itpm(struct pnp_dev *dev) +{ + struct acpi_device *acpi = pnp_acpi_device(dev); + struct acpi_hardware_id *id; + + list_for_each_entry(id, &acpi->pnp.ids, list) { + if (!strcmp("INTC0102", id->id)) + return 1; + } + + return 0; +} +#else +static int is_itpm(struct pnp_dev *dev) +{ + return 0; +} +#endif + static int check_locality(struct tpm_chip *chip, int l) { if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & @@ -613,6 +634,9 @@ static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, else interrupts = 0; + if (is_itpm(pnp_dev)) + itpm = 1; + return tpm_tis_init(&pnp_dev->dev, start, len, irq); } diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c deleted file mode 100644 index f64582b0f623..000000000000 --- a/drivers/char/tty_audit.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Creating audit events from TTY input. - * - * Copyright (C) 2007 Red Hat, Inc. All rights reserved. This copyrighted - * material is made available to anyone wishing to use, modify, copy, or - * redistribute it subject to the terms and conditions of the GNU General - * Public License v.2. - * - * Authors: Miloslav Trmac <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); - } -} diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c deleted file mode 100644 index cc1e9850d655..000000000000 --- a/drivers/char/tty_buffer.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Tty buffer allocation management - */ - -#include <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); -} - diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c deleted file mode 100644 index c05c5af5aa04..000000000000 --- a/drivers/char/tty_io.c +++ /dev/null @@ -1,3263 +0,0 @@ -/* - * linux/drivers/char/tty_io.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles - * or rs-channels. It also implements echoing, cooked mode etc. - * - * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0. - * - * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the - * tty_struct and tty_queue structures. Previously there was an array - * of 256 tty_struct's which was statically allocated, and the - * tty_queue structures were allocated at boot time. Both are now - * dynamically allocated only when the tty is open. - * - * Also restructured routines so that there is more of a separation - * between the high-level tty routines (tty_io.c and tty_ioctl.c) and - * the low-level tty routines (serial.c, pty.c, console.c). This - * makes for cleaner and more compact code. -TYT, 9/17/92 - * - * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines - * which can be dynamically activated and de-activated by the line - * discipline handling modules (like SLIP). - * - * NOTE: pay no attention to the line discipline code (yet); its - * interface is still subject to change in this version... - * -- TYT, 1/31/92 - * - * Added functionality to the OPOST tty handling. No delays, but all - * other bits should be there. - * -- Nick Holloway <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; -} - diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c deleted file mode 100644 index 0c1889971459..000000000000 --- a/drivers/char/tty_ioctl.c +++ /dev/null @@ -1,1179 +0,0 @@ -/* - * linux/drivers/char/tty_ioctl.c - * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - * - * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines - * which can be dynamically activated and de-activated by the line - * discipline handling modules (like SLIP). - */ - -#include <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); diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c deleted file mode 100644 index 412f9775d19c..000000000000 --- a/drivers/char/tty_ldisc.c +++ /dev/null @@ -1,915 +0,0 @@ -#include <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); -} diff --git a/drivers/char/tty_mutex.c b/drivers/char/tty_mutex.c deleted file mode 100644 index 133697540c73..000000000000 --- a/drivers/char/tty_mutex.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * drivers/char/tty_lock.c - */ -#include <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); diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c deleted file mode 100644 index 33d37d230f8f..000000000000 --- a/drivers/char/tty_port.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Tty port functions - */ - -#include <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); diff --git a/drivers/char/uv_mmtimer.c b/drivers/char/uv_mmtimer.c index 493b47a0d511..956ebe2080a5 100644 --- a/drivers/char/uv_mmtimer.c +++ b/drivers/char/uv_mmtimer.c @@ -23,7 +23,6 @@ #include <linux/interrupt.h> #include <linux/time.h> #include <linux/math64.h> -#include <linux/smp_lock.h> #include <asm/genapic.h> #include <asm/uv/uv_hub.h> diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c deleted file mode 100644 index 273ab44cc91d..000000000000 --- a/drivers/char/vc_screen.c +++ /dev/null @@ -1,644 +0,0 @@ -/* - * linux/drivers/char/vc_screen.c - * - * Provide access to virtual console memory. - * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled) - * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63) - * [minor: N] - * - * /dev/vcsaN: idem, but including attributes, and prefixed with - * the 4 bytes lines,columns,x,y (as screendump used to give). - * Attribute/character pair is in native endianity. - * [minor: N+128] - * - * This replaces screendump and part of selection, so that the system - * administrator can control access using file system permissions. - * - * aeb@cwi.nl - efter Friedas begravelse - 950211 - * - * machek@k332.feld.cvut.cz - modified not to send characters to wrong console - * - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...) - * - making it shorter - scr_readw are macros which expand in PRETTY long code - */ - -#include <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; -} diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c deleted file mode 100644 index 6c1b676643a9..000000000000 --- a/drivers/char/virtio_console.c +++ /dev/null @@ -1,1857 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation - * Copyright (C) 2009, 2010 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include <linux/cdev.h> -#include <linux/debugfs.h> -#include <linux/device.h> -#include <linux/err.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/list.h> -#include <linux/poll.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/virtio.h> -#include <linux/virtio_console.h> -#include <linux/wait.h> -#include <linux/workqueue.h> -#include "hvc_console.h" - -/* - * This is a global struct for storing common data for all the devices - * this driver handles. - * - * Mainly, it has a linked list for all the consoles in one place so - * that callbacks from hvc for get_chars(), put_chars() work properly - * across multiple devices and multiple ports per device. - */ -struct ports_driver_data { - /* Used for registering chardevs */ - struct class *class; - - /* Used for exporting per-port information to debugfs */ - struct dentry *debugfs_dir; - - /* List of all the devices we're handling */ - struct list_head portdevs; - - /* Number of devices this driver is handling */ - unsigned int index; - - /* - * This is used to keep track of the number of hvc consoles - * spawned by this driver. This number is given as the first - * argument to hvc_alloc(). To correctly map an initial - * console spawned via hvc_instantiate to the console being - * hooked up via hvc_alloc, we need to pass the same vtermno. - * - * We also just assume the first console being initialised was - * the first one that got used as the initial console. - */ - unsigned int next_vtermno; - - /* All the console devices handled by this driver */ - struct list_head consoles; -}; -static struct ports_driver_data pdrvdata; - -DEFINE_SPINLOCK(pdrvdata_lock); - -/* This struct holds information that's relevant only for console ports */ -struct console { - /* We'll place all consoles in a list in the pdrvdata struct */ - struct list_head list; - - /* The hvc device associated with this console port */ - struct hvc_struct *hvc; - - /* The size of the console */ - struct winsize ws; - - /* - * This number identifies the number that we used to register - * with hvc in hvc_instantiate() and hvc_alloc(); this is the - * number passed on by the hvc callbacks to us to - * differentiate between the other console ports handled by - * this driver - */ - u32 vtermno; -}; - -struct port_buffer { - char *buf; - - /* size of the buffer in *buf above */ - size_t size; - - /* used length of the buffer */ - size_t len; - /* offset in the buf from which to consume data */ - size_t offset; -}; - -/* - * This is a per-device struct that stores data common to all the - * ports for that device (vdev->priv). - */ -struct ports_device { - /* Next portdev in the list, head is in the pdrvdata struct */ - struct list_head list; - - /* - * Workqueue handlers where we process deferred work after - * notification - */ - struct work_struct control_work; - - struct list_head ports; - - /* To protect the list of ports */ - spinlock_t ports_lock; - - /* To protect the vq operations for the control channel */ - spinlock_t cvq_lock; - - /* The current config space is stored here */ - struct virtio_console_config config; - - /* The virtio device we're associated with */ - struct virtio_device *vdev; - - /* - * A couple of virtqueues for the control channel: one for - * guest->host transfers, one for host->guest transfers - */ - struct virtqueue *c_ivq, *c_ovq; - - /* Array of per-port IO virtqueues */ - struct virtqueue **in_vqs, **out_vqs; - - /* Used for numbering devices for sysfs and debugfs */ - unsigned int drv_index; - - /* Major number for this device. Ports will be created as minors. */ - int chr_major; -}; - -/* This struct holds the per-port data */ -struct port { - /* Next port in the list, head is in the ports_device */ - struct list_head list; - - /* Pointer to the parent virtio_console device */ - struct ports_device *portdev; - - /* The current buffer from which data has to be fed to readers */ - struct port_buffer *inbuf; - - /* - * To protect the operations on the in_vq associated with this - * port. Has to be a spinlock because it can be called from - * interrupt context (get_char()). - */ - spinlock_t inbuf_lock; - - /* Protect the operations on the out_vq. */ - spinlock_t outvq_lock; - - /* The IO vqs for this port */ - struct virtqueue *in_vq, *out_vq; - - /* File in the debugfs directory that exposes this port's information */ - struct dentry *debugfs_file; - - /* - * The entries in this struct will be valid if this port is - * hooked up to an hvc console - */ - struct console cons; - - /* Each port associates with a separate char device */ - struct cdev *cdev; - struct device *dev; - - /* Reference-counting to handle port hot-unplugs and file operations */ - struct kref kref; - - /* A waitqueue for poll() or blocking read operations */ - wait_queue_head_t waitqueue; - - /* The 'name' of the port that we expose via sysfs properties */ - char *name; - - /* We can notify apps of host connect / disconnect events via SIGIO */ - struct fasync_struct *async_queue; - - /* The 'id' to identify the port with the Host */ - u32 id; - - bool outvq_full; - - /* Is the host device open */ - bool host_connected; - - /* We should allow only one process to open a port */ - bool guest_connected; -}; - -/* This is the very early arch-specified put chars function. */ -static int (*early_put_chars)(u32, const char *, int); - -static struct port *find_port_by_vtermno(u32 vtermno) -{ - struct port *port; - struct console *cons; - unsigned long flags; - - spin_lock_irqsave(&pdrvdata_lock, flags); - list_for_each_entry(cons, &pdrvdata.consoles, list) { - if (cons->vtermno == vtermno) { - port = container_of(cons, struct port, cons); - goto out; - } - } - port = NULL; -out: - spin_unlock_irqrestore(&pdrvdata_lock, flags); - return port; -} - -static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev, - dev_t dev) -{ - struct port *port; - unsigned long flags; - - spin_lock_irqsave(&portdev->ports_lock, flags); - list_for_each_entry(port, &portdev->ports, list) - if (port->cdev->dev == dev) - goto out; - port = NULL; -out: - spin_unlock_irqrestore(&portdev->ports_lock, flags); - - return port; -} - -static struct port *find_port_by_devt(dev_t dev) -{ - struct ports_device *portdev; - struct port *port; - unsigned long flags; - - spin_lock_irqsave(&pdrvdata_lock, flags); - list_for_each_entry(portdev, &pdrvdata.portdevs, list) { - port = find_port_by_devt_in_portdev(portdev, dev); - if (port) - goto out; - } - port = NULL; -out: - spin_unlock_irqrestore(&pdrvdata_lock, flags); - return port; -} - -static struct port *find_port_by_id(struct ports_device *portdev, u32 id) -{ - struct port *port; - unsigned long flags; - - spin_lock_irqsave(&portdev->ports_lock, flags); - list_for_each_entry(port, &portdev->ports, list) - if (port->id == id) - goto out; - port = NULL; -out: - spin_unlock_irqrestore(&portdev->ports_lock, flags); - - return port; -} - -static struct port *find_port_by_vq(struct ports_device *portdev, - struct virtqueue *vq) -{ - struct port *port; - unsigned long flags; - - spin_lock_irqsave(&portdev->ports_lock, flags); - list_for_each_entry(port, &portdev->ports, list) - if (port->in_vq == vq || port->out_vq == vq) - goto out; - port = NULL; -out: - spin_unlock_irqrestore(&portdev->ports_lock, flags); - return port; -} - -static bool is_console_port(struct port *port) -{ - if (port->cons.hvc) - return true; - return false; -} - -static inline bool use_multiport(struct ports_device *portdev) -{ - /* - * This condition can be true when put_chars is called from - * early_init - */ - if (!portdev->vdev) - return 0; - return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT); -} - -static void free_buf(struct port_buffer *buf) -{ - kfree(buf->buf); - kfree(buf); -} - -static struct port_buffer *alloc_buf(size_t buf_size) -{ - struct port_buffer *buf; - - buf = kmalloc(sizeof(*buf), GFP_KERNEL); - if (!buf) - goto fail; - buf->buf = kzalloc(buf_size, GFP_KERNEL); - if (!buf->buf) - goto free_buf; - buf->len = 0; - buf->offset = 0; - buf->size = buf_size; - return buf; - -free_buf: - kfree(buf); -fail: - return NULL; -} - -/* Callers should take appropriate locks */ -static void *get_inbuf(struct port *port) -{ - struct port_buffer *buf; - struct virtqueue *vq; - unsigned int len; - - vq = port->in_vq; - buf = virtqueue_get_buf(vq, &len); - if (buf) { - buf->len = len; - buf->offset = 0; - } - return buf; -} - -/* - * Create a scatter-gather list representing our input buffer and put - * it in the queue. - * - * Callers should take appropriate locks. - */ -static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) -{ - struct scatterlist sg[1]; - int ret; - - sg_init_one(sg, buf->buf, buf->size); - - ret = virtqueue_add_buf(vq, sg, 0, 1, buf); - virtqueue_kick(vq); - return ret; -} - -/* Discard any unread data this port has. Callers lockers. */ -static void discard_port_data(struct port *port) -{ - struct port_buffer *buf; - struct virtqueue *vq; - unsigned int len; - int ret; - - vq = port->in_vq; - if (port->inbuf) - buf = port->inbuf; - else - buf = virtqueue_get_buf(vq, &len); - - ret = 0; - while (buf) { - if (add_inbuf(vq, buf) < 0) { - ret++; - free_buf(buf); - } - buf = virtqueue_get_buf(vq, &len); - } - port->inbuf = NULL; - if (ret) - dev_warn(port->dev, "Errors adding %d buffers back to vq\n", - ret); -} - -static bool port_has_data(struct port *port) -{ - unsigned long flags; - bool ret; - - spin_lock_irqsave(&port->inbuf_lock, flags); - if (port->inbuf) { - ret = true; - goto out; - } - port->inbuf = get_inbuf(port); - if (port->inbuf) { - ret = true; - goto out; - } - ret = false; -out: - spin_unlock_irqrestore(&port->inbuf_lock, flags); - return ret; -} - -static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id, - unsigned int event, unsigned int value) -{ - struct scatterlist sg[1]; - struct virtio_console_control cpkt; - struct virtqueue *vq; - unsigned int len; - - if (!use_multiport(portdev)) - return 0; - - cpkt.id = port_id; - cpkt.event = event; - cpkt.value = value; - - vq = portdev->c_ovq; - - sg_init_one(sg, &cpkt, sizeof(cpkt)); - if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt) >= 0) { - virtqueue_kick(vq); - while (!virtqueue_get_buf(vq, &len)) - cpu_relax(); - } - return 0; -} - -static ssize_t send_control_msg(struct port *port, unsigned int event, - unsigned int value) -{ - /* Did the port get unplugged before userspace closed it? */ - if (port->portdev) - return __send_control_msg(port->portdev, port->id, event, value); - return 0; -} - -/* Callers must take the port->outvq_lock */ -static void reclaim_consumed_buffers(struct port *port) -{ - void *buf; - unsigned int len; - - while ((buf = virtqueue_get_buf(port->out_vq, &len))) { - kfree(buf); - port->outvq_full = false; - } -} - -static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, - bool nonblock) -{ - struct scatterlist sg[1]; - struct virtqueue *out_vq; - ssize_t ret; - unsigned long flags; - unsigned int len; - - out_vq = port->out_vq; - - spin_lock_irqsave(&port->outvq_lock, flags); - - reclaim_consumed_buffers(port); - - sg_init_one(sg, in_buf, in_count); - ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf); - - /* Tell Host to go! */ - virtqueue_kick(out_vq); - - if (ret < 0) { - in_count = 0; - goto done; - } - - if (ret == 0) - port->outvq_full = true; - - if (nonblock) - goto done; - - /* - * Wait till the host acknowledges it pushed out the data we - * sent. This is done for data from the hvc_console; the tty - * operations are performed with spinlocks held so we can't - * sleep here. An alternative would be to copy the data to a - * buffer and relax the spinning requirement. The downside is - * we need to kmalloc a GFP_ATOMIC buffer each time the - * console driver writes something out. - */ - while (!virtqueue_get_buf(out_vq, &len)) - cpu_relax(); -done: - spin_unlock_irqrestore(&port->outvq_lock, flags); - /* - * We're expected to return the amount of data we wrote -- all - * of it - */ - return in_count; -} - -/* - * Give out the data that's requested from the buffer that we have - * queued up. - */ -static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, - bool to_user) -{ - struct port_buffer *buf; - unsigned long flags; - - if (!out_count || !port_has_data(port)) - return 0; - - buf = port->inbuf; - out_count = min(out_count, buf->len - buf->offset); - - if (to_user) { - ssize_t ret; - - ret = copy_to_user(out_buf, buf->buf + buf->offset, out_count); - if (ret) - return -EFAULT; - } else { - memcpy(out_buf, buf->buf + buf->offset, out_count); - } - - buf->offset += out_count; - - if (buf->offset == buf->len) { - /* - * We're done using all the data in this buffer. - * Re-queue so that the Host can send us more data. - */ - spin_lock_irqsave(&port->inbuf_lock, flags); - port->inbuf = NULL; - - if (add_inbuf(port->in_vq, buf) < 0) - dev_warn(port->dev, "failed add_buf\n"); - - spin_unlock_irqrestore(&port->inbuf_lock, flags); - } - /* Return the number of bytes actually copied */ - return out_count; -} - -/* The condition that must be true for polling to end */ -static bool will_read_block(struct port *port) -{ - if (!port->guest_connected) { - /* Port got hot-unplugged. Let's exit. */ - return false; - } - return !port_has_data(port) && port->host_connected; -} - -static bool will_write_block(struct port *port) -{ - bool ret; - - if (!port->guest_connected) { - /* Port got hot-unplugged. Let's exit. */ - return false; - } - if (!port->host_connected) - return true; - - spin_lock_irq(&port->outvq_lock); - /* - * Check if the Host has consumed any buffers since we last - * sent data (this is only applicable for nonblocking ports). - */ - reclaim_consumed_buffers(port); - ret = port->outvq_full; - spin_unlock_irq(&port->outvq_lock); - - return ret; -} - -static ssize_t port_fops_read(struct file *filp, char __user *ubuf, - size_t count, loff_t *offp) -{ - struct port *port; - ssize_t ret; - - port = filp->private_data; - - if (!port_has_data(port)) { - /* - * If nothing's connected on the host just return 0 in - * case of list_empty; this tells the userspace app - * that there's no connection - */ - if (!port->host_connected) - return 0; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - - ret = wait_event_interruptible(port->waitqueue, - !will_read_block(port)); - if (ret < 0) - return ret; - } - /* Port got hot-unplugged. */ - if (!port->guest_connected) - return -ENODEV; - /* - * We could've received a disconnection message while we were - * waiting for more data. - * - * This check is not clubbed in the if() statement above as we - * might receive some data as well as the host could get - * disconnected after we got woken up from our wait. So we - * really want to give off whatever data we have and only then - * check for host_connected. - */ - if (!port_has_data(port) && !port->host_connected) - return 0; - - return fill_readbuf(port, ubuf, count, true); -} - -static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, - size_t count, loff_t *offp) -{ - struct port *port; - char *buf; - ssize_t ret; - bool nonblock; - - /* Userspace could be out to fool us */ - if (!count) - return 0; - - port = filp->private_data; - - nonblock = filp->f_flags & O_NONBLOCK; - - if (will_write_block(port)) { - if (nonblock) - return -EAGAIN; - - ret = wait_event_interruptible(port->waitqueue, - !will_write_block(port)); - if (ret < 0) - return ret; - } - /* Port got hot-unplugged. */ - if (!port->guest_connected) - return -ENODEV; - - count = min((size_t)(32 * 1024), count); - - buf = kmalloc(count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = copy_from_user(buf, ubuf, count); - if (ret) { - ret = -EFAULT; - goto free_buf; - } - - /* - * We now ask send_buf() to not spin for generic ports -- we - * can re-use the same code path that non-blocking file - * descriptors take for blocking file descriptors since the - * wait is already done and we're certain the write will go - * through to the host. - */ - nonblock = true; - ret = send_buf(port, buf, count, nonblock); - - if (nonblock && ret > 0) - goto out; - -free_buf: - kfree(buf); -out: - return ret; -} - -static unsigned int port_fops_poll(struct file *filp, poll_table *wait) -{ - struct port *port; - unsigned int ret; - - port = filp->private_data; - poll_wait(filp, &port->waitqueue, wait); - - if (!port->guest_connected) { - /* Port got unplugged */ - return POLLHUP; - } - ret = 0; - if (!will_read_block(port)) - ret |= POLLIN | POLLRDNORM; - if (!will_write_block(port)) - ret |= POLLOUT; - if (!port->host_connected) - ret |= POLLHUP; - - return ret; -} - -static void remove_port(struct kref *kref); - -static int port_fops_release(struct inode *inode, struct file *filp) -{ - struct port *port; - - port = filp->private_data; - - /* Notify host of port being closed */ - send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); - - spin_lock_irq(&port->inbuf_lock); - port->guest_connected = false; - - discard_port_data(port); - - spin_unlock_irq(&port->inbuf_lock); - - spin_lock_irq(&port->outvq_lock); - reclaim_consumed_buffers(port); - spin_unlock_irq(&port->outvq_lock); - - /* - * Locks aren't necessary here as a port can't be opened after - * unplug, and if a port isn't unplugged, a kref would already - * exist for the port. Plus, taking ports_lock here would - * create a dependency on other locks taken by functions - * inside remove_port if we're the last holder of the port, - * creating many problems. - */ - kref_put(&port->kref, remove_port); - - return 0; -} - -static int port_fops_open(struct inode *inode, struct file *filp) -{ - struct cdev *cdev = inode->i_cdev; - struct port *port; - int ret; - - port = find_port_by_devt(cdev->dev); - filp->private_data = port; - - /* Prevent against a port getting hot-unplugged at the same time */ - spin_lock_irq(&port->portdev->ports_lock); - kref_get(&port->kref); - spin_unlock_irq(&port->portdev->ports_lock); - - /* - * Don't allow opening of console port devices -- that's done - * via /dev/hvc - */ - if (is_console_port(port)) { - ret = -ENXIO; - goto out; - } - - /* Allow only one process to open a particular port at a time */ - spin_lock_irq(&port->inbuf_lock); - if (port->guest_connected) { - spin_unlock_irq(&port->inbuf_lock); - ret = -EMFILE; - goto out; - } - - port->guest_connected = true; - spin_unlock_irq(&port->inbuf_lock); - - spin_lock_irq(&port->outvq_lock); - /* - * There might be a chance that we missed reclaiming a few - * buffers in the window of the port getting previously closed - * and opening now. - */ - reclaim_consumed_buffers(port); - spin_unlock_irq(&port->outvq_lock); - - nonseekable_open(inode, filp); - - /* Notify host of port being opened */ - send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); - - return 0; -out: - kref_put(&port->kref, remove_port); - return ret; -} - -static int port_fops_fasync(int fd, struct file *filp, int mode) -{ - struct port *port; - - port = filp->private_data; - return fasync_helper(fd, filp, mode, &port->async_queue); -} - -/* - * The file operations that we support: programs in the guest can open - * a console device, read from it, write to it, poll for data and - * close it. The devices are at - * /dev/vport<device number>p<port number> - */ -static const struct file_operations port_fops = { - .owner = THIS_MODULE, - .open = port_fops_open, - .read = port_fops_read, - .write = port_fops_write, - .poll = port_fops_poll, - .release = port_fops_release, - .fasync = port_fops_fasync, - .llseek = no_llseek, -}; - -/* - * The put_chars() callback is pretty straightforward. - * - * We turn the characters into a scatter-gather list, add it to the - * output queue and then kick the Host. Then we sit here waiting for - * it to finish: inefficient in theory, but in practice - * implementations will do it immediately (lguest's Launcher does). - */ -static int put_chars(u32 vtermno, const char *buf, int count) -{ - struct port *port; - - if (unlikely(early_put_chars)) - return early_put_chars(vtermno, buf, count); - - port = find_port_by_vtermno(vtermno); - if (!port) - return -EPIPE; - - return send_buf(port, (void *)buf, count, false); -} - -/* - * get_chars() is the callback from the hvc_console infrastructure - * when an interrupt is received. - * - * We call out to fill_readbuf that gets us the required data from the - * buffers that are queued up. - */ -static int get_chars(u32 vtermno, char *buf, int count) -{ - struct port *port; - - /* If we've not set up the port yet, we have no input to give. */ - if (unlikely(early_put_chars)) - return 0; - - port = find_port_by_vtermno(vtermno); - if (!port) - return -EPIPE; - - /* If we don't have an input queue yet, we can't get input. */ - BUG_ON(!port->in_vq); - - return fill_readbuf(port, buf, count, false); -} - -static void resize_console(struct port *port) -{ - struct virtio_device *vdev; - - /* The port could have been hot-unplugged */ - if (!port || !is_console_port(port)) - return; - - vdev = port->portdev->vdev; - if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) - hvc_resize(port->cons.hvc, port->cons.ws); -} - -/* We set the configuration at this point, since we now have a tty */ -static int notifier_add_vio(struct hvc_struct *hp, int data) -{ - struct port *port; - - port = find_port_by_vtermno(hp->vtermno); - if (!port) - return -EINVAL; - - hp->irq_requested = 1; - resize_console(port); - - return 0; -} - -static void notifier_del_vio(struct hvc_struct *hp, int data) -{ - hp->irq_requested = 0; -} - -/* The operations for console ports. */ -static const struct hv_ops hv_ops = { - .get_chars = get_chars, - .put_chars = put_chars, - .notifier_add = notifier_add_vio, - .notifier_del = notifier_del_vio, - .notifier_hangup = notifier_del_vio, -}; - -/* - * Console drivers are initialized very early so boot messages can go - * out, so we do things slightly differently from the generic virtio - * initialization of the net and block drivers. - * - * At this stage, the console is output-only. It's too early to set - * up a virtqueue, so we let the drivers do some boutique early-output - * thing. - */ -int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) -{ - early_put_chars = put_chars; - return hvc_instantiate(0, 0, &hv_ops); -} - -int init_port_console(struct port *port) -{ - int ret; - - /* - * The Host's telling us this port is a console port. Hook it - * up with an hvc console. - * - * To set up and manage our virtual console, we call - * hvc_alloc(). - * - * The first argument of hvc_alloc() is the virtual console - * number. The second argument is the parameter for the - * notification mechanism (like irq number). We currently - * leave this as zero, virtqueues have implicit notifications. - * - * The third argument is a "struct hv_ops" containing the - * put_chars() get_chars(), notifier_add() and notifier_del() - * pointers. The final argument is the output buffer size: we - * can do any size, so we put PAGE_SIZE here. - */ - port->cons.vtermno = pdrvdata.next_vtermno; - - port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE); - if (IS_ERR(port->cons.hvc)) { - ret = PTR_ERR(port->cons.hvc); - dev_err(port->dev, - "error %d allocating hvc for port\n", ret); - port->cons.hvc = NULL; - return ret; - } - spin_lock_irq(&pdrvdata_lock); - pdrvdata.next_vtermno++; - list_add_tail(&port->cons.list, &pdrvdata.consoles); - spin_unlock_irq(&pdrvdata_lock); - port->guest_connected = true; - - /* - * Start using the new console output if this is the first - * console to come up. - */ - if (early_put_chars) - early_put_chars = NULL; - - /* Notify host of port being opened */ - send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); - - return 0; -} - -static ssize_t show_port_name(struct device *dev, - struct device_attribute *attr, char *buffer) -{ - struct port *port; - - port = dev_get_drvdata(dev); - - return sprintf(buffer, "%s\n", port->name); -} - -static DEVICE_ATTR(name, S_IRUGO, show_port_name, NULL); - -static struct attribute *port_sysfs_entries[] = { - &dev_attr_name.attr, - NULL -}; - -static struct attribute_group port_attribute_group = { - .name = NULL, /* put in device directory */ - .attrs = port_sysfs_entries, -}; - -static int debugfs_open(struct inode *inode, struct file *filp) -{ - filp->private_data = inode->i_private; - return 0; -} - -static ssize_t debugfs_read(struct file *filp, char __user *ubuf, - size_t count, loff_t *offp) -{ - struct port *port; - char *buf; - ssize_t ret, out_offset, out_count; - - out_count = 1024; - buf = kmalloc(out_count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - port = filp->private_data; - out_offset = 0; - out_offset += snprintf(buf + out_offset, out_count, - "name: %s\n", port->name ? port->name : ""); - out_offset += snprintf(buf + out_offset, out_count - out_offset, - "guest_connected: %d\n", port->guest_connected); - out_offset += snprintf(buf + out_offset, out_count - out_offset, - "host_connected: %d\n", port->host_connected); - out_offset += snprintf(buf + out_offset, out_count - out_offset, - "outvq_full: %d\n", port->outvq_full); - out_offset += snprintf(buf + out_offset, out_count - out_offset, - "is_console: %s\n", - is_console_port(port) ? "yes" : "no"); - out_offset += snprintf(buf + out_offset, out_count - out_offset, - "console_vtermno: %u\n", port->cons.vtermno); - - ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset); - kfree(buf); - return ret; -} - -static const struct file_operations port_debugfs_ops = { - .owner = THIS_MODULE, - .open = debugfs_open, - .read = debugfs_read, -}; - -static void set_console_size(struct port *port, u16 rows, u16 cols) -{ - if (!port || !is_console_port(port)) - return; - - port->cons.ws.ws_row = rows; - port->cons.ws.ws_col = cols; -} - -static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) -{ - struct port_buffer *buf; - unsigned int nr_added_bufs; - int ret; - - nr_added_bufs = 0; - do { - buf = alloc_buf(PAGE_SIZE); - if (!buf) - break; - - spin_lock_irq(lock); - ret = add_inbuf(vq, buf); - if (ret < 0) { - spin_unlock_irq(lock); - free_buf(buf); - break; - } - nr_added_bufs++; - spin_unlock_irq(lock); - } while (ret > 0); - - return nr_added_bufs; -} - -static void send_sigio_to_port(struct port *port) -{ - if (port->async_queue && port->guest_connected) - kill_fasync(&port->async_queue, SIGIO, POLL_OUT); -} - -static int add_port(struct ports_device *portdev, u32 id) -{ - char debugfs_name[16]; - struct port *port; - struct port_buffer *buf; - dev_t devt; - unsigned int nr_added_bufs; - int err; - - port = kmalloc(sizeof(*port), GFP_KERNEL); - if (!port) { - err = -ENOMEM; - goto fail; - } - kref_init(&port->kref); - - port->portdev = portdev; - port->id = id; - - port->name = NULL; - port->inbuf = NULL; - port->cons.hvc = NULL; - port->async_queue = NULL; - - port->cons.ws.ws_row = port->cons.ws.ws_col = 0; - - port->host_connected = port->guest_connected = false; - - port->outvq_full = false; - - port->in_vq = portdev->in_vqs[port->id]; - port->out_vq = portdev->out_vqs[port->id]; - - port->cdev = cdev_alloc(); - if (!port->cdev) { - dev_err(&port->portdev->vdev->dev, "Error allocating cdev\n"); - err = -ENOMEM; - goto free_port; - } - port->cdev->ops = &port_fops; - - devt = MKDEV(portdev->chr_major, id); - err = cdev_add(port->cdev, devt, 1); - if (err < 0) { - dev_err(&port->portdev->vdev->dev, - "Error %d adding cdev for port %u\n", err, id); - goto free_cdev; - } - port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, - devt, port, "vport%up%u", - port->portdev->drv_index, id); - if (IS_ERR(port->dev)) { - err = PTR_ERR(port->dev); - dev_err(&port->portdev->vdev->dev, - "Error %d creating device for port %u\n", - err, id); - goto free_cdev; - } - - spin_lock_init(&port->inbuf_lock); - spin_lock_init(&port->outvq_lock); - init_waitqueue_head(&port->waitqueue); - - /* Fill the in_vq with buffers so the host can send us data. */ - nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); - if (!nr_added_bufs) { - dev_err(port->dev, "Error allocating inbufs\n"); - err = -ENOMEM; - goto free_device; - } - - /* - * If we're not using multiport support, this has to be a console port - */ - if (!use_multiport(port->portdev)) { - err = init_port_console(port); - if (err) - goto free_inbufs; - } - - spin_lock_irq(&portdev->ports_lock); - list_add_tail(&port->list, &port->portdev->ports); - spin_unlock_irq(&portdev->ports_lock); - - /* - * Tell the Host we're set so that it can send us various - * configuration parameters for this port (eg, port name, - * caching, whether this is a console port, etc.) - */ - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); - - if (pdrvdata.debugfs_dir) { - /* - * Finally, create the debugfs file that we can use to - * inspect a port's state at any time - */ - sprintf(debugfs_name, "vport%up%u", - port->portdev->drv_index, id); - port->debugfs_file = debugfs_create_file(debugfs_name, 0444, - pdrvdata.debugfs_dir, - port, - &port_debugfs_ops); - } - return 0; - -free_inbufs: - while ((buf = virtqueue_detach_unused_buf(port->in_vq))) - free_buf(buf); -free_device: - device_destroy(pdrvdata.class, port->dev->devt); -free_cdev: - cdev_del(port->cdev); -free_port: - kfree(port); -fail: - /* The host might want to notify management sw about port add failure */ - __send_control_msg(portdev, id, VIRTIO_CONSOLE_PORT_READY, 0); - return err; -} - -/* No users remain, remove all port-specific data. */ -static void remove_port(struct kref *kref) -{ - struct port *port; - - port = container_of(kref, struct port, kref); - - sysfs_remove_group(&port->dev->kobj, &port_attribute_group); - device_destroy(pdrvdata.class, port->dev->devt); - cdev_del(port->cdev); - - kfree(port->name); - - debugfs_remove(port->debugfs_file); - - kfree(port); -} - -/* - * Port got unplugged. Remove port from portdev's list and drop the - * kref reference. If no userspace has this port opened, it will - * result in immediate removal the port. - */ -static void unplug_port(struct port *port) -{ - struct port_buffer *buf; - - spin_lock_irq(&port->portdev->ports_lock); - list_del(&port->list); - spin_unlock_irq(&port->portdev->ports_lock); - - if (port->guest_connected) { - port->guest_connected = false; - port->host_connected = false; - wake_up_interruptible(&port->waitqueue); - - /* Let the app know the port is going down. */ - send_sigio_to_port(port); - } - - if (is_console_port(port)) { - spin_lock_irq(&pdrvdata_lock); - list_del(&port->cons.list); - spin_unlock_irq(&pdrvdata_lock); -#if 0 - /* - * hvc_remove() not called as removing one hvc port - * results in other hvc ports getting frozen. - * - * Once this is resolved in hvc, this functionality - * will be enabled. Till that is done, the -EPIPE - * return from get_chars() above will help - * hvc_console.c to clean up on ports we remove here. - */ - hvc_remove(port->cons.hvc); -#endif - } - - /* Remove unused data this port might have received. */ - discard_port_data(port); - - reclaim_consumed_buffers(port); - - /* Remove buffers we queued up for the Host to send us data in. */ - while ((buf = virtqueue_detach_unused_buf(port->in_vq))) - free_buf(buf); - - /* - * We should just assume the device itself has gone off -- - * else a close on an open port later will try to send out a - * control message. - */ - port->portdev = NULL; - - /* - * Locks around here are not necessary - a port can't be - * opened after we removed the port struct from ports_list - * above. - */ - kref_put(&port->kref, remove_port); -} - -/* Any private messages that the Host and Guest want to share */ -static void handle_control_message(struct ports_device *portdev, - struct port_buffer *buf) -{ - struct virtio_console_control *cpkt; - struct port *port; - size_t name_size; - int err; - - cpkt = (struct virtio_console_control *)(buf->buf + buf->offset); - - port = find_port_by_id(portdev, cpkt->id); - if (!port && cpkt->event != VIRTIO_CONSOLE_PORT_ADD) { - /* No valid header at start of buffer. Drop it. */ - dev_dbg(&portdev->vdev->dev, - "Invalid index %u in control packet\n", cpkt->id); - return; - } - - switch (cpkt->event) { - case VIRTIO_CONSOLE_PORT_ADD: - if (port) { - dev_dbg(&portdev->vdev->dev, - "Port %u already added\n", port->id); - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); - break; - } - if (cpkt->id >= portdev->config.max_nr_ports) { - dev_warn(&portdev->vdev->dev, - "Request for adding port with out-of-bound id %u, max. supported id: %u\n", - cpkt->id, portdev->config.max_nr_ports - 1); - break; - } - add_port(portdev, cpkt->id); - break; - case VIRTIO_CONSOLE_PORT_REMOVE: - unplug_port(port); - break; - case VIRTIO_CONSOLE_CONSOLE_PORT: - if (!cpkt->value) - break; - if (is_console_port(port)) - break; - - init_port_console(port); - /* - * Could remove the port here in case init fails - but - * have to notify the host first. - */ - break; - case VIRTIO_CONSOLE_RESIZE: { - struct { - __u16 rows; - __u16 cols; - } size; - - if (!is_console_port(port)) - break; - - memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt), - sizeof(size)); - set_console_size(port, size.rows, size.cols); - - port->cons.hvc->irq_requested = 1; - resize_console(port); - break; - } - case VIRTIO_CONSOLE_PORT_OPEN: - port->host_connected = cpkt->value; - wake_up_interruptible(&port->waitqueue); - /* - * If the host port got closed and the host had any - * unconsumed buffers, we'll be able to reclaim them - * now. - */ - spin_lock_irq(&port->outvq_lock); - reclaim_consumed_buffers(port); - spin_unlock_irq(&port->outvq_lock); - - /* - * If the guest is connected, it'll be interested in - * knowing the host connection state changed. - */ - send_sigio_to_port(port); - break; - case VIRTIO_CONSOLE_PORT_NAME: - /* - * Skip the size of the header and the cpkt to get the size - * of the name that was sent - */ - name_size = buf->len - buf->offset - sizeof(*cpkt) + 1; - - port->name = kmalloc(name_size, GFP_KERNEL); - if (!port->name) { - dev_err(port->dev, - "Not enough space to store port name\n"); - break; - } - strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt), - name_size - 1); - port->name[name_size - 1] = 0; - - /* - * Since we only have one sysfs attribute, 'name', - * create it only if we have a name for the port. - */ - err = sysfs_create_group(&port->dev->kobj, - &port_attribute_group); - if (err) { - dev_err(port->dev, - "Error %d creating sysfs device attributes\n", - err); - } else { - /* - * Generate a udev event so that appropriate - * symlinks can be created based on udev - * rules. - */ - kobject_uevent(&port->dev->kobj, KOBJ_CHANGE); - } - break; - } -} - -static void control_work_handler(struct work_struct *work) -{ - struct ports_device *portdev; - struct virtqueue *vq; - struct port_buffer *buf; - unsigned int len; - - portdev = container_of(work, struct ports_device, control_work); - vq = portdev->c_ivq; - - spin_lock(&portdev->cvq_lock); - while ((buf = virtqueue_get_buf(vq, &len))) { - spin_unlock(&portdev->cvq_lock); - - buf->len = len; - buf->offset = 0; - - handle_control_message(portdev, buf); - - spin_lock(&portdev->cvq_lock); - if (add_inbuf(portdev->c_ivq, buf) < 0) { - dev_warn(&portdev->vdev->dev, - "Error adding buffer to queue\n"); - free_buf(buf); - } - } - spin_unlock(&portdev->cvq_lock); -} - -static void in_intr(struct virtqueue *vq) -{ - struct port *port; - unsigned long flags; - - port = find_port_by_vq(vq->vdev->priv, vq); - if (!port) - return; - - spin_lock_irqsave(&port->inbuf_lock, flags); - if (!port->inbuf) - port->inbuf = get_inbuf(port); - - /* - * Don't queue up data when port is closed. This condition - * can be reached when a console port is not yet connected (no - * tty is spawned) and the host sends out data to console - * ports. For generic serial ports, the host won't - * (shouldn't) send data till the guest is connected. - */ - if (!port->guest_connected) - discard_port_data(port); - - spin_unlock_irqrestore(&port->inbuf_lock, flags); - - wake_up_interruptible(&port->waitqueue); - - /* Send a SIGIO indicating new data in case the process asked for it */ - send_sigio_to_port(port); - - if (is_console_port(port) && hvc_poll(port->cons.hvc)) - hvc_kick(); -} - -static void control_intr(struct virtqueue *vq) -{ - struct ports_device *portdev; - - portdev = vq->vdev->priv; - schedule_work(&portdev->control_work); -} - -static void config_intr(struct virtio_device *vdev) -{ - struct ports_device *portdev; - - portdev = vdev->priv; - - if (!use_multiport(portdev)) { - struct port *port; - u16 rows, cols; - - vdev->config->get(vdev, - offsetof(struct virtio_console_config, cols), - &cols, sizeof(u16)); - vdev->config->get(vdev, - offsetof(struct virtio_console_config, rows), - &rows, sizeof(u16)); - - port = find_port_by_id(portdev, 0); - set_console_size(port, rows, cols); - - /* - * We'll use this way of resizing only for legacy - * support. For newer userspace - * (VIRTIO_CONSOLE_F_MULTPORT+), use control messages - * to indicate console size changes so that it can be - * done per-port. - */ - resize_console(port); - } -} - -static int init_vqs(struct ports_device *portdev) -{ - vq_callback_t **io_callbacks; - char **io_names; - struct virtqueue **vqs; - u32 i, j, nr_ports, nr_queues; - int err; - - nr_ports = portdev->config.max_nr_ports; - nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2; - - vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL); - if (!vqs) { - err = -ENOMEM; - goto fail; - } - io_callbacks = kmalloc(nr_queues * sizeof(vq_callback_t *), GFP_KERNEL); - if (!io_callbacks) { - err = -ENOMEM; - goto free_vqs; - } - io_names = kmalloc(nr_queues * sizeof(char *), GFP_KERNEL); - if (!io_names) { - err = -ENOMEM; - goto free_callbacks; - } - portdev->in_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), - GFP_KERNEL); - if (!portdev->in_vqs) { - err = -ENOMEM; - goto free_names; - } - portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), - GFP_KERNEL); - if (!portdev->out_vqs) { - err = -ENOMEM; - goto free_invqs; - } - - /* - * For backward compat (newer host but older guest), the host - * spawns a console port first and also inits the vqs for port - * 0 before others. - */ - j = 0; - io_callbacks[j] = in_intr; - io_callbacks[j + 1] = NULL; - io_names[j] = "input"; - io_names[j + 1] = "output"; - j += 2; - - if (use_multiport(portdev)) { - io_callbacks[j] = control_intr; - io_callbacks[j + 1] = NULL; - io_names[j] = "control-i"; - io_names[j + 1] = "control-o"; - - for (i = 1; i < nr_ports; i++) { - j += 2; - io_callbacks[j] = in_intr; - io_callbacks[j + 1] = NULL; - io_names[j] = "input"; - io_names[j + 1] = "output"; - } - } - /* Find the queues. */ - err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs, - io_callbacks, - (const char **)io_names); - if (err) - goto free_outvqs; - - j = 0; - portdev->in_vqs[0] = vqs[0]; - portdev->out_vqs[0] = vqs[1]; - j += 2; - if (use_multiport(portdev)) { - portdev->c_ivq = vqs[j]; - portdev->c_ovq = vqs[j + 1]; - - for (i = 1; i < nr_ports; i++) { - j += 2; - portdev->in_vqs[i] = vqs[j]; - portdev->out_vqs[i] = vqs[j + 1]; - } - } - kfree(io_callbacks); - kfree(io_names); - kfree(vqs); - - return 0; - -free_names: - kfree(io_names); -free_callbacks: - kfree(io_callbacks); -free_outvqs: - kfree(portdev->out_vqs); -free_invqs: - kfree(portdev->in_vqs); -free_vqs: - kfree(vqs); -fail: - return err; -} - -static const struct file_operations portdev_fops = { - .owner = THIS_MODULE, -}; - -/* - * Once we're further in boot, we get probed like any other virtio - * device. - * - * If the host also supports multiple console ports, we check the - * config space to see how many ports the host has spawned. We - * initialize each port found. - */ -static int __devinit virtcons_probe(struct virtio_device *vdev) -{ - struct ports_device *portdev; - int err; - bool multiport; - - portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); - if (!portdev) { - err = -ENOMEM; - goto fail; - } - - /* Attach this portdev to this virtio_device, and vice-versa. */ - portdev->vdev = vdev; - vdev->priv = portdev; - - spin_lock_irq(&pdrvdata_lock); - portdev->drv_index = pdrvdata.index++; - spin_unlock_irq(&pdrvdata_lock); - - portdev->chr_major = register_chrdev(0, "virtio-portsdev", - &portdev_fops); - if (portdev->chr_major < 0) { - dev_err(&vdev->dev, - "Error %d registering chrdev for device %u\n", - portdev->chr_major, portdev->drv_index); - err = portdev->chr_major; - goto free; - } - - multiport = false; - portdev->config.max_nr_ports = 1; - if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { - multiport = true; - vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; - - vdev->config->get(vdev, offsetof(struct virtio_console_config, - max_nr_ports), - &portdev->config.max_nr_ports, - sizeof(portdev->config.max_nr_ports)); - } - - /* Let the Host know we support multiple ports.*/ - vdev->config->finalize_features(vdev); - - err = init_vqs(portdev); - if (err < 0) { - dev_err(&vdev->dev, "Error %d initializing vqs\n", err); - goto free_chrdev; - } - - spin_lock_init(&portdev->ports_lock); - INIT_LIST_HEAD(&portdev->ports); - - if (multiport) { - unsigned int nr_added_bufs; - - spin_lock_init(&portdev->cvq_lock); - INIT_WORK(&portdev->control_work, &control_work_handler); - - nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->cvq_lock); - if (!nr_added_bufs) { - dev_err(&vdev->dev, - "Error allocating buffers for control queue\n"); - err = -ENOMEM; - goto free_vqs; - } - } else { - /* - * For backward compatibility: Create a console port - * if we're running on older host. - */ - add_port(portdev, 0); - } - - spin_lock_irq(&pdrvdata_lock); - list_add_tail(&portdev->list, &pdrvdata.portdevs); - spin_unlock_irq(&pdrvdata_lock); - - __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, - VIRTIO_CONSOLE_DEVICE_READY, 1); - return 0; - -free_vqs: - /* The host might want to notify mgmt sw about device add failure */ - __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, - VIRTIO_CONSOLE_DEVICE_READY, 0); - vdev->config->del_vqs(vdev); - kfree(portdev->in_vqs); - kfree(portdev->out_vqs); -free_chrdev: - unregister_chrdev(portdev->chr_major, "virtio-portsdev"); -free: - kfree(portdev); -fail: - return err; -} - -static void virtcons_remove(struct virtio_device *vdev) -{ - struct ports_device *portdev; - struct port *port, *port2; - - portdev = vdev->priv; - - spin_lock_irq(&pdrvdata_lock); - list_del(&portdev->list); - spin_unlock_irq(&pdrvdata_lock); - - /* Disable interrupts for vqs */ - vdev->config->reset(vdev); - /* Finish up work that's lined up */ - cancel_work_sync(&portdev->control_work); - - list_for_each_entry_safe(port, port2, &portdev->ports, list) - unplug_port(port); - - unregister_chrdev(portdev->chr_major, "virtio-portsdev"); - - /* - * When yanking out a device, we immediately lose the - * (device-side) queues. So there's no point in keeping the - * guest side around till we drop our final reference. This - * also means that any ports which are in an open state will - * have to just stop using the port, as the vqs are going - * away. - */ - if (use_multiport(portdev)) { - struct port_buffer *buf; - unsigned int len; - - while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) - free_buf(buf); - - while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) - free_buf(buf); - } - - vdev->config->del_vqs(vdev); - kfree(portdev->in_vqs); - kfree(portdev->out_vqs); - - kfree(portdev); -} - -static struct virtio_device_id id_table[] = { - { VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID }, - { 0 }, -}; - -static unsigned int features[] = { - VIRTIO_CONSOLE_F_SIZE, - VIRTIO_CONSOLE_F_MULTIPORT, -}; - -static struct virtio_driver virtio_console = { - .feature_table = features, - .feature_table_size = ARRAY_SIZE(features), - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = virtcons_probe, - .remove = virtcons_remove, - .config_changed = config_intr, -}; - -static int __init init(void) -{ - int err; - - pdrvdata.class = class_create(THIS_MODULE, "virtio-ports"); - if (IS_ERR(pdrvdata.class)) { - err = PTR_ERR(pdrvdata.class); - pr_err("Error %d creating virtio-ports class\n", err); - return err; - } - - pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL); - if (!pdrvdata.debugfs_dir) { - pr_warning("Error %ld creating debugfs dir for virtio-ports\n", - PTR_ERR(pdrvdata.debugfs_dir)); - } - INIT_LIST_HEAD(&pdrvdata.consoles); - INIT_LIST_HEAD(&pdrvdata.portdevs); - - return register_virtio_driver(&virtio_console); -} - -static void __exit fini(void) -{ - unregister_virtio_driver(&virtio_console); - - class_destroy(pdrvdata.class); - if (pdrvdata.debugfs_dir) - debugfs_remove_recursive(pdrvdata.debugfs_dir); -} -module_init(init); -module_exit(fini); - -MODULE_DEVICE_TABLE(virtio, id_table); -MODULE_DESCRIPTION("Virtio console driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/vt.c b/drivers/char/vt.c deleted file mode 100644 index a8ec48ed14d9..000000000000 --- a/drivers/char/vt.c +++ /dev/null @@ -1,4209 +0,0 @@ -/* - * linux/drivers/char/vt.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -/* - * Hopefully this will be a rather complete VT102 implementation. - * - * Beeping thanks to John T Kohl. - * - * Virtual Consoles, Screen Blanking, Screen Dumping, Color, Graphics - * Chars, and VT100 enhancements by Peter MacDonald. - * - * Copy and paste function by Andrew Haylett, - * some enhancements by Alessandro Rubini. - * - * Code to check for different video-cards mostly by Galen Hunt, - * <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 diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c deleted file mode 100644 index 6b68a0fb4611..000000000000 --- a/drivers/char/vt_ioctl.c +++ /dev/null @@ -1,1788 +0,0 @@ -/* - * linux/drivers/char/vt_ioctl.c - * - * Copyright (C) 1992 obz under the linux copyright - * - * Dynamic diacritical handling - aeb@cwi.nl - Dec 1993 - * Dynamic keymap and string allocation - aeb@cwi.nl - May 1994 - * Restrict VT switching via ioctl() - grif@cs.ucr.edu - Dec 1995 - * Some code moved for less code duplication - Andi Kleen - Mar 1997 - * Check put/get_user, cleanups - acme@conectiva.com.br - Jun 2001 - */ - -#include <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); |