diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/Kconfig | 6 | ||||
-rw-r--r-- | drivers/char/agp/backend.c | 3 | ||||
-rw-r--r-- | drivers/char/agp/hp-agp.c | 6 | ||||
-rw-r--r-- | drivers/char/agp/intel-gtt.c | 33 | ||||
-rw-r--r-- | drivers/char/apm-emulation.c | 21 | ||||
-rw-r--r-- | drivers/char/hw_random/Kconfig | 28 | ||||
-rw-r--r-- | drivers/char/hw_random/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/hw_random/atmel-rng.c | 158 | ||||
-rw-r--r-- | drivers/char/hw_random/virtio-rng.c | 1 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 33 | ||||
-rw-r--r-- | drivers/char/mem.c | 1 | ||||
-rw-r--r-- | drivers/char/ps3flash.c | 1 | ||||
-rw-r--r-- | drivers/char/ramoops.c | 1 | ||||
-rw-r--r-- | drivers/char/random.c | 23 | ||||
-rw-r--r-- | drivers/char/raw.c | 3 | ||||
-rw-r--r-- | drivers/char/rtc.c | 7 | ||||
-rw-r--r-- | drivers/char/scc.h | 613 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.c | 3 | ||||
-rw-r--r-- | drivers/char/ttyprintk.c | 3 | ||||
-rw-r--r-- | drivers/char/virtio_console.c | 121 |
20 files changed, 358 insertions, 708 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 423fd56bf612..43643033a3ae 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -298,7 +298,7 @@ if RTC_LIB=n config RTC tristate "Enhanced Real Time Clock Support (legacy PC RTC driver)" depends on !PPC && !PARISC && !IA64 && !M68K && !SPARC && !FRV \ - && !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN + && !ARM && !SUPERH && !S390 && !AVR32 && !BLACKFIN && !UML ---help--- If you say Y here and create a character special file /dev/rtc with major number 10 and minor number 135 using mknod ("man mknod"), you @@ -346,7 +346,7 @@ config JS_RTC config GEN_RTC tristate "Generic /dev/rtc emulation" - depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH && !AVR32 && !BLACKFIN + depends on RTC!=y && !IA64 && !ARM && !M32R && !MIPS && !SPARC && !FRV && !S390 && !SUPERH && !AVR32 && !BLACKFIN && !UML ---help--- If you say Y here and create a character special file /dev/rtc with major number 10 and minor number 135 using mknod ("man mknod"), you @@ -490,7 +490,7 @@ config SCx200_GPIO config PC8736x_GPIO tristate "NatSemi PC8736x GPIO Support" - depends on X86_32 + depends on X86_32 && !UML default SCx200_GPIO # mostly N select NSC_GPIO # needed for support routines help diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index f27d0d0816d3..4b71647782d0 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -171,7 +171,7 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) } got_gatt = 1; - bridge->key_list = vmalloc(PAGE_SIZE * 4); + bridge->key_list = vzalloc(PAGE_SIZE * 4); if (bridge->key_list == NULL) { dev_err(&bridge->dev->dev, "can't allocate memory for key lists\n"); @@ -181,7 +181,6 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) got_keylist = 1; /* FIXME vmalloc'd memory not guaranteed contiguous */ - memset(bridge->key_list, 0, PAGE_SIZE * 4); if (bridge->driver->configure()) { dev_err(&bridge->dev->dev, "error configuring host chipset\n"); diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 056b289a1e89..3695773ce7c3 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -336,7 +336,8 @@ hp_zx1_insert_memory (struct agp_memory *mem, off_t pg_start, int type) off_t j, io_pg_start; int io_pg_count; - if (type != 0 || mem->type != 0) { + if (type != mem->type || + agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) { return -EINVAL; } @@ -380,7 +381,8 @@ hp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type) struct _hp_private *hp = &hp_private; int i, io_pg_start, io_pg_count; - if (type != 0 || mem->type != 0) { + if (type != mem->type || + agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) { return -EINVAL; } diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 85151019dde1..c92424ca1a55 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -30,10 +30,10 @@ /* * If we have Intel graphics, we're not going to have anything other than * an Intel IOMMU. So make the correct use of the PCI DMA API contingent - * on the Intel IOMMU support (CONFIG_DMAR). + * on the Intel IOMMU support (CONFIG_INTEL_IOMMU). * Only newer chipsets need to bother with this, of course. */ -#ifdef CONFIG_DMAR +#ifdef CONFIG_INTEL_IOMMU #define USE_PCI_DMA_API 1 #else #define USE_PCI_DMA_API 0 @@ -923,6 +923,9 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, { int ret = -EINVAL; + if (intel_private.base.do_idle_maps) + return -ENODEV; + if (intel_private.clear_fake_agp) { int start = intel_private.base.stolen_size / PAGE_SIZE; int end = intel_private.base.gtt_mappable_entries; @@ -985,6 +988,9 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem, if (mem->page_count == 0) return 0; + if (intel_private.base.do_idle_maps) + return -ENODEV; + intel_gtt_clear_range(pg_start, mem->page_count); if (intel_private.base.needs_dmar) { @@ -1177,6 +1183,26 @@ static void gen6_cleanup(void) { } +/* Certain Gen5 chipsets require require idling the GPU before + * unmapping anything from the GTT when VT-d is enabled. + */ +static inline int needs_idle_maps(void) +{ +#ifdef CONFIG_INTEL_IOMMU + const unsigned short gpu_devid = intel_private.pcidev->device; + extern int intel_iommu_gfx_mapped; + + /* Query intel_iommu to see if we need the workaround. Presumably that + * was loaded first. + */ + if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || + gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) && + intel_iommu_gfx_mapped) + return 1; +#endif + return 0; +} + static int i9xx_setup(void) { u32 reg_addr; @@ -1211,6 +1237,9 @@ static int i9xx_setup(void) intel_private.gtt_bus_addr = reg_addr + gtt_offset; } + if (needs_idle_maps()) + intel_private.base.do_idle_maps = 1; + intel_i9xx_setup_flush(); return 0; diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c index a7346ab97a3c..f4837a893dfa 100644 --- a/drivers/char/apm-emulation.c +++ b/drivers/char/apm-emulation.c @@ -40,10 +40,7 @@ #define APM_MINOR_DEV 134 /* - * See Documentation/Config.help for the configuration options. - * - * Various options can be changed at boot time as follows: - * (We allow underscores for compatibility with the modules code) + * One option can be changed at boot time as follows: * apm=on/off enable/disable APM */ @@ -300,17 +297,13 @@ apm_ioctl(struct file *filp, u_int cmd, u_long arg) /* * Wait for the suspend/resume to complete. If there * are pending acknowledges, we wait here for them. + * wait_event_freezable() is interruptible and pending + * signal can cause busy looping. We aren't doing + * anything critical, chill a bit on each iteration. */ - freezer_do_not_count(); - - wait_event(apm_suspend_waitqueue, - as->suspend_state == SUSPEND_DONE); - - /* - * Since we are waiting until the suspend is done, the - * try_to_freeze() in freezer_count() will not trigger - */ - freezer_count(); + while (wait_event_freezable(apm_suspend_waitqueue, + as->suspend_state == SUSPEND_DONE)) + msleep(10); break; case SUSPEND_ACKTO: as->suspend_result = -ETIMEDOUT; diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 1d2ebc7a4947..0689bf6b0183 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -60,6 +60,19 @@ config HW_RANDOM_AMD If unsure, say Y. +config HW_RANDOM_ATMEL + tristate "Atmel Random Number Generator support" + depends on HW_RANDOM && ARCH_AT91SAM9G45 + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on Atmel AT91 devices. + + To compile this driver as a module, choose M here: the + module will be called atmel-rng. + + If unsure, say Y. + config HW_RANDOM_GEODE tristate "AMD Geode HW Random Number Generator support" depends on HW_RANDOM && X86_32 && PCI @@ -222,3 +235,18 @@ config HW_RANDOM_PPC4XX module will be called ppc4xx-rng. If unsure, say N. + +config UML_RANDOM + depends on UML + tristate "Hardware random number generator" + help + This option enables UML's "hardware" random number generator. It + attaches itself to the host's /dev/random, supplying as much entropy + as the host has, rather than the small amount the UML gets from its + own drivers. It registers itself as a standard hardware random number + generator, major 10, minor 183, and the canonical device name is + /dev/hwrng. + The way to make use of this is to install the rng-tools package + (check your distro, or download from + http://sourceforge.net/projects/gkernel/). rngd periodically reads + /dev/hwrng and injects the entropy into /dev/random. diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index c88f244c8a71..b2ff5265a996 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -7,6 +7,7 @@ rng-core-y := core.o obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o +obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o n2-rng-y := n2-drv.o n2-asm.o diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c new file mode 100644 index 000000000000..241df2e76aba --- /dev/null +++ b/drivers/char/hw_random/atmel-rng.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2011 Peter Korsgaard <jacmet@sunsite.dk> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/hw_random.h> +#include <linux/platform_device.h> + +#define TRNG_CR 0x00 +#define TRNG_ISR 0x1c +#define TRNG_ODATA 0x50 + +#define TRNG_KEY 0x524e4700 /* RNG */ + +struct atmel_trng { + struct clk *clk; + void __iomem *base; + struct hwrng rng; +}; + +static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max, + bool wait) +{ + struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng); + u32 *data = buf; + + /* data ready? */ + if (readl(trng->base + TRNG_ODATA) & 1) { + *data = readl(trng->base + TRNG_ODATA); + return 4; + } else + return 0; +} + +static int atmel_trng_probe(struct platform_device *pdev) +{ + struct atmel_trng *trng; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); + if (!trng) + return -ENOMEM; + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) + return -EBUSY; + + trng->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!trng->base) + return -EBUSY; + + trng->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(trng->clk)) + return PTR_ERR(trng->clk); + + ret = clk_enable(trng->clk); + if (ret) + goto err_enable; + + writel(TRNG_KEY | 1, trng->base + TRNG_CR); + trng->rng.name = pdev->name; + trng->rng.read = atmel_trng_read; + + ret = hwrng_register(&trng->rng); + if (ret) + goto err_register; + + platform_set_drvdata(pdev, trng); + + return 0; + +err_register: + clk_disable(trng->clk); +err_enable: + clk_put(trng->clk); + + return ret; +} + +static int __devexit atmel_trng_remove(struct platform_device *pdev) +{ + struct atmel_trng *trng = platform_get_drvdata(pdev); + + hwrng_unregister(&trng->rng); + + writel(TRNG_KEY, trng->base + TRNG_CR); + clk_disable(trng->clk); + clk_put(trng->clk); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int atmel_trng_suspend(struct device *dev) +{ + struct atmel_trng *trng = dev_get_drvdata(dev); + + clk_disable(trng->clk); + + return 0; +} + +static int atmel_trng_resume(struct device *dev) +{ + struct atmel_trng *trng = dev_get_drvdata(dev); + + return clk_enable(trng->clk); +} + +static const struct dev_pm_ops atmel_trng_pm_ops = { + .suspend = atmel_trng_suspend, + .resume = atmel_trng_resume, +}; +#endif /* CONFIG_PM */ + +static struct platform_driver atmel_trng_driver = { + .probe = atmel_trng_probe, + .remove = __devexit_p(atmel_trng_remove), + .driver = { + .name = "atmel-trng", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &atmel_trng_pm_ops, +#endif /* CONFIG_PM */ + }, +}; + +static int __init atmel_trng_init(void) +{ + return platform_driver_register(&atmel_trng_driver); +} +module_init(atmel_trng_init); + +static void __exit atmel_trng_exit(void) +{ + platform_driver_unregister(&atmel_trng_driver); +} +module_exit(atmel_trng_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>"); +MODULE_DESCRIPTION("Atmel true random number generator driver"); diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 75f1cbd61c17..fd699ccecf5b 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -23,6 +23,7 @@ #include <linux/spinlock.h> #include <linux/virtio.h> #include <linux/virtio_rng.h> +#include <linux/module.h> static struct virtqueue *vq; static unsigned int data_avail; diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 3302586655c4..c2917ffad2c2 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -65,6 +65,7 @@ * mechanism for it at that time. */ #include <asm/kdebug.h> +#include <asm/nmi.h> #define HAVE_DIE_NMI #endif @@ -1077,17 +1078,8 @@ static void ipmi_unregister_watchdog(int ipmi_intf) #ifdef HAVE_DIE_NMI static int -ipmi_nmi(struct notifier_block *self, unsigned long val, void *data) +ipmi_nmi(unsigned int val, struct pt_regs *regs) { - struct die_args *args = data; - - if (val != DIE_NMIUNKNOWN) - return NOTIFY_OK; - - /* Hack, if it's a memory or I/O error, ignore it. */ - if (args->err & 0xc0) - return NOTIFY_OK; - /* * If we get here, it's an NMI that's not a memory or I/O * error. We can't truly tell if it's from IPMI or not @@ -1097,15 +1089,15 @@ ipmi_nmi(struct notifier_block *self, unsigned long val, void *data) if (testing_nmi) { testing_nmi = 2; - return NOTIFY_STOP; + return NMI_HANDLED; } /* If we are not expecting a timeout, ignore it. */ if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) - return NOTIFY_OK; + return NMI_DONE; if (preaction_val != WDOG_PRETIMEOUT_NMI) - return NOTIFY_OK; + return NMI_DONE; /* * If no one else handled the NMI, we assume it was the IPMI @@ -1120,12 +1112,8 @@ ipmi_nmi(struct notifier_block *self, unsigned long val, void *data) panic(PFX "pre-timeout"); } - return NOTIFY_STOP; + return NMI_HANDLED; } - -static struct notifier_block ipmi_nmi_handler = { - .notifier_call = ipmi_nmi -}; #endif static int wdog_reboot_handler(struct notifier_block *this, @@ -1290,7 +1278,8 @@ static void check_parms(void) } } if (do_nmi && !nmi_handler_registered) { - rv = register_die_notifier(&ipmi_nmi_handler); + rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, + "ipmi"); if (rv) { printk(KERN_WARNING PFX "Can't register nmi handler\n"); @@ -1298,7 +1287,7 @@ static void check_parms(void) } else nmi_handler_registered = 1; } else if (!do_nmi && nmi_handler_registered) { - unregister_die_notifier(&ipmi_nmi_handler); + unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); nmi_handler_registered = 0; } #endif @@ -1336,7 +1325,7 @@ static int __init ipmi_wdog_init(void) if (rv) { #ifdef HAVE_DIE_NMI if (nmi_handler_registered) - unregister_die_notifier(&ipmi_nmi_handler); + unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); #endif atomic_notifier_chain_unregister(&panic_notifier_list, &wdog_panic_notifier); @@ -1357,7 +1346,7 @@ static void __exit ipmi_wdog_exit(void) #ifdef HAVE_DIE_NMI if (nmi_handler_registered) - unregister_die_notifier(&ipmi_nmi_handler); + unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); #endif atomic_notifier_chain_unregister(&panic_notifier_list, diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 8fc04b4f311f..145179033716 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -26,6 +26,7 @@ #include <linux/bootmem.h> #include <linux/splice.h> #include <linux/pfn.h> +#include <linux/export.h> #include <asm/uaccess.h> #include <asm/io.h> diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c index d0c57c2e2909..6abdde4da2b7 100644 --- a/drivers/char/ps3flash.c +++ b/drivers/char/ps3flash.c @@ -22,6 +22,7 @@ #include <linux/miscdevice.h> #include <linux/slab.h> #include <linux/uaccess.h> +#include <linux/module.h> #include <asm/lv1call.h> #include <asm/ps3stor.h> diff --git a/drivers/char/ramoops.c b/drivers/char/ramoops.c index 810aff9e750f..7c7f42a1f880 100644 --- a/drivers/char/ramoops.c +++ b/drivers/char/ramoops.c @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/kmsg_dump.h> #include <linux/time.h> +#include <linux/err.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/platform_device.h> diff --git a/drivers/char/random.c b/drivers/char/random.c index c35a785005b0..63e19ba56bbe 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -932,7 +932,21 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, */ void get_random_bytes(void *buf, int nbytes) { - extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0); + char *p = buf; + + while (nbytes) { + unsigned long v; + int chunk = min(nbytes, (int)sizeof(unsigned long)); + + if (!arch_get_random_long(&v)) + break; + + memcpy(buf, &v, chunk); + p += chunk; + nbytes -= chunk; + } + + extract_entropy(&nonblocking_pool, p, nbytes, 0, 0); } EXPORT_SYMBOL(get_random_bytes); @@ -1318,9 +1332,14 @@ late_initcall(random_int_secret_init); DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash); unsigned int get_random_int(void) { - __u32 *hash = get_cpu_var(get_random_int_hash); + __u32 *hash; unsigned int ret; + if (arch_get_random_int(&ret)) + return ret; + + hash = get_cpu_var(get_random_int_hash); + hash[0] += current->pid + jiffies + get_cycles(); md5_transform(hash, random_int_secret); ret = hash[0]; diff --git a/drivers/char/raw.c b/drivers/char/raw.c index b33e8ea314ed..b6de2c047145 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -324,13 +324,12 @@ static int __init raw_init(void) max_raw_minors = MAX_RAW_MINORS; } - raw_devices = vmalloc(sizeof(struct raw_device_data) * max_raw_minors); + raw_devices = vzalloc(sizeof(struct raw_device_data) * max_raw_minors); if (!raw_devices) { printk(KERN_ERR "Not enough memory for raw device structures\n"); ret = -ENOMEM; goto error; } - memset(raw_devices, 0, sizeof(struct raw_device_data) * max_raw_minors); ret = register_chrdev_region(dev, max_raw_minors, "raw"); if (ret) diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index dfa8b3062fda..ccd124ab7ca7 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -80,6 +80,7 @@ #include <linux/bcd.h> #include <linux/delay.h> #include <linux/uaccess.h> +#include <linux/ratelimit.h> #include <asm/current.h> #include <asm/system.h> @@ -1195,10 +1196,8 @@ static void rtc_dropped_irq(unsigned long data) spin_unlock_irq(&rtc_lock); - if (printk_ratelimit()) { - printk(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", - freq); - } + printk_ratelimited(KERN_WARNING "rtc: lost some interrupts at %ldHz.\n", + freq); /* Now we have new data */ wake_up_interruptible(&rtc_wait); diff --git a/drivers/char/scc.h b/drivers/char/scc.h deleted file mode 100644 index 341b1142bea8..000000000000 --- a/drivers/char/scc.h +++ /dev/null @@ -1,613 +0,0 @@ -/* - * atari_SCC.h: Definitions for the Am8530 Serial Communications Controller - * - * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive - * for more details. - * - */ - - -#ifndef _SCC_H -#define _SCC_H - -#include <linux/delay.h> - -/* Special configuration ioctls for the Atari SCC5380 Serial - * Communications Controller - */ - -/* ioctl command codes */ - -#define TIOCGATSCC 0x54c0 /* get SCC configuration */ -#define TIOCSATSCC 0x54c1 /* set SCC configuration */ -#define TIOCDATSCC 0x54c2 /* reset configuration to defaults */ - -/* Clock sources */ - -#define CLK_RTxC 0 -#define CLK_TRxC 1 -#define CLK_PCLK 2 - -/* baud_bases for the common clocks in the Atari. These are the real - * frequencies divided by 16. - */ - -#define SCC_BAUD_BASE_TIMC 19200 /* 0.3072 MHz from TT-MFP, Timer C */ -#define SCC_BAUD_BASE_BCLK 153600 /* 2.4576 MHz */ -#define SCC_BAUD_BASE_PCLK4 229500 /* 3.6720 MHz */ -#define SCC_BAUD_BASE_PCLK 503374 /* 8.0539763 MHz */ -#define SCC_BAUD_BASE_NONE 0 /* for not connected or unused - * clock sources */ - -/* The SCC clock configuration structure */ - -struct scc_clock_config { - unsigned RTxC_base; /* base_baud of RTxC */ - unsigned TRxC_base; /* base_baud of TRxC */ - unsigned PCLK_base; /* base_baud of PCLK, both channels! */ - struct { - unsigned clksrc; /* CLK_RTxC, CLK_TRxC or CLK_PCLK */ - unsigned divisor; /* divisor for base baud, valid values: - * see below */ - } baud_table[17]; /* For 50, 75, 110, 135, 150, 200, 300, - * 600, 1200, 1800, 2400, 4800, 9600, - * 19200, 38400, 57600 and 115200 bps. - * The last two could be replaced by - * other rates > 38400 if they're not - * possible. - */ -}; - -/* The following divisors are valid: - * - * - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use - * the BRG) - * - * - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible) - * - * - CLK_PCLK: >= 4 and even (no direct modes, only BRG) - * - */ - -struct scc_port { - struct gs_port gs; - volatile unsigned char *ctrlp; - volatile unsigned char *datap; - int x_char; /* xon/xoff character */ - int c_dcd; - int channel; - struct scc_port *port_a; /* Reference to port A and B */ - struct scc_port *port_b; /* structs for reg access */ -}; - -#define SCC_MAGIC 0x52696368 - -/***********************************************************************/ -/* */ -/* Register Names */ -/* */ -/***********************************************************************/ - -/* The SCC documentation gives no explicit names to the registers, - * they're just called WR0..15 and RR0..15. To make the source code - * better readable and make the transparent write reg read access (see - * below) possible, I christen them here with self-invented names. - * Note that (real) read registers are assigned numbers 16..31. WR7' - * has number 33. - */ - -#define COMMAND_REG 0 /* wo */ -#define INT_AND_DMA_REG 1 /* wo */ -#define INT_VECTOR_REG 2 /* rw, common to both channels */ -#define RX_CTRL_REG 3 /* rw */ -#define AUX1_CTRL_REG 4 /* rw */ -#define TX_CTRL_REG 5 /* rw */ -#define SYNC_ADR_REG 6 /* wo */ -#define SYNC_CHAR_REG 7 /* wo */ -#define SDLC_OPTION_REG 33 /* wo */ -#define TX_DATA_REG 8 /* wo */ -#define MASTER_INT_CTRL 9 /* wo, common to both channels */ -#define AUX2_CTRL_REG 10 /* rw */ -#define CLK_CTRL_REG 11 /* wo */ -#define TIMER_LOW_REG 12 /* rw */ -#define TIMER_HIGH_REG 13 /* rw */ -#define DPLL_CTRL_REG 14 /* wo */ -#define INT_CTRL_REG 15 /* rw */ - -#define STATUS_REG 16 /* ro */ -#define SPCOND_STATUS_REG 17 /* wo */ -/* RR2 is WR2 for Channel A, Channel B gives vector + current status: */ -#define CURR_VECTOR_REG 18 /* Ch. B only, Ch. A for rw */ -#define INT_PENDING_REG 19 /* Channel A only! */ -/* RR4 is WR4, if b6(MR7') == 1 */ -/* RR5 is WR5, if b6(MR7') == 1 */ -#define FS_FIFO_LOW_REG 22 /* ro */ -#define FS_FIFO_HIGH_REG 23 /* ro */ -#define RX_DATA_REG 24 /* ro */ -/* RR9 is WR3, if b6(MR7') == 1 */ -#define DPLL_STATUS_REG 26 /* ro */ -/* RR11 is WR10, if b6(MR7') == 1 */ -/* RR12 is WR12 */ -/* RR13 is WR13 */ -/* RR14 not present */ -/* RR15 is WR15 */ - - -/***********************************************************************/ -/* */ -/* Register Values */ -/* */ -/***********************************************************************/ - - -/* WR0: COMMAND_REG "CR" */ - -#define CR_RX_CRC_RESET 0x40 -#define CR_TX_CRC_RESET 0x80 -#define CR_TX_UNDERRUN_RESET 0xc0 - -#define CR_EXTSTAT_RESET 0x10 -#define CR_SEND_ABORT 0x18 -#define CR_ENAB_INT_NEXT_RX 0x20 -#define CR_TX_PENDING_RESET 0x28 -#define CR_ERROR_RESET 0x30 -#define CR_HIGHEST_IUS_RESET 0x38 - - -/* WR1: INT_AND_DMA_REG "IDR" */ - -#define IDR_EXTSTAT_INT_ENAB 0x01 -#define IDR_TX_INT_ENAB 0x02 -#define IDR_PARERR_AS_SPCOND 0x04 - -#define IDR_RX_INT_DISAB 0x00 -#define IDR_RX_INT_FIRST 0x08 -#define IDR_RX_INT_ALL 0x10 -#define IDR_RX_INT_SPCOND 0x18 -#define IDR_RX_INT_MASK 0x18 - -#define IDR_WAITREQ_RX 0x20 -#define IDR_WAITREQ_IS_REQ 0x40 -#define IDR_WAITREQ_ENAB 0x80 - - -/* WR3: RX_CTRL_REG "RCR" */ - -#define RCR_RX_ENAB 0x01 -#define RCR_DISCARD_SYNC_CHARS 0x02 -#define RCR_ADDR_SEARCH 0x04 -#define RCR_CRC_ENAB 0x08 -#define RCR_SEARCH_MODE 0x10 -#define RCR_AUTO_ENAB_MODE 0x20 - -#define RCR_CHSIZE_MASK 0xc0 -#define RCR_CHSIZE_5 0x00 -#define RCR_CHSIZE_6 0x40 -#define RCR_CHSIZE_7 0x80 -#define RCR_CHSIZE_8 0xc0 - - -/* WR4: AUX1_CTRL_REG "A1CR" */ - -#define A1CR_PARITY_MASK 0x03 -#define A1CR_PARITY_NONE 0x00 -#define A1CR_PARITY_ODD 0x01 -#define A1CR_PARITY_EVEN 0x03 - -#define A1CR_MODE_MASK 0x0c -#define A1CR_MODE_SYNCR 0x00 -#define A1CR_MODE_ASYNC_1 0x04 -#define A1CR_MODE_ASYNC_15 0x08 -#define A1CR_MODE_ASYNC_2 0x0c - -#define A1CR_SYNCR_MODE_MASK 0x30 -#define A1CR_SYNCR_MONOSYNC 0x00 -#define A1CR_SYNCR_BISYNC 0x10 -#define A1CR_SYNCR_SDLC 0x20 -#define A1CR_SYNCR_EXTCSYNC 0x30 - -#define A1CR_CLKMODE_MASK 0xc0 -#define A1CR_CLKMODE_x1 0x00 -#define A1CR_CLKMODE_x16 0x40 -#define A1CR_CLKMODE_x32 0x80 -#define A1CR_CLKMODE_x64 0xc0 - - -/* WR5: TX_CTRL_REG "TCR" */ - -#define TCR_TX_CRC_ENAB 0x01 -#define TCR_RTS 0x02 -#define TCR_USE_CRC_CCITT 0x00 -#define TCR_USE_CRC_16 0x04 -#define TCR_TX_ENAB 0x08 -#define TCR_SEND_BREAK 0x10 - -#define TCR_CHSIZE_MASK 0x60 -#define TCR_CHSIZE_5 0x00 -#define TCR_CHSIZE_6 0x20 -#define TCR_CHSIZE_7 0x40 -#define TCR_CHSIZE_8 0x60 - -#define TCR_DTR 0x80 - - -/* WR7': SLDC_OPTION_REG "SOR" */ - -#define SOR_AUTO_TX_ENAB 0x01 -#define SOR_AUTO_EOM_RESET 0x02 -#define SOR_AUTO_RTS_MODE 0x04 -#define SOR_NRZI_DISAB_HIGH 0x08 -#define SOR_ALT_DTRREQ_TIMING 0x10 -#define SOR_READ_CRC_CHARS 0x20 -#define SOR_EXTENDED_REG_ACCESS 0x40 - - -/* WR9: MASTER_INT_CTRL "MIC" */ - -#define MIC_VEC_INCL_STAT 0x01 -#define MIC_NO_VECTOR 0x02 -#define MIC_DISAB_LOWER_CHAIN 0x04 -#define MIC_MASTER_INT_ENAB 0x08 -#define MIC_STATUS_HIGH 0x10 -#define MIC_IGN_INTACK 0x20 - -#define MIC_NO_RESET 0x00 -#define MIC_CH_A_RESET 0x40 -#define MIC_CH_B_RESET 0x80 -#define MIC_HARD_RESET 0xc0 - - -/* WR10: AUX2_CTRL_REG "A2CR" */ - -#define A2CR_SYNC_6 0x01 -#define A2CR_LOOP_MODE 0x02 -#define A2CR_ABORT_ON_UNDERRUN 0x04 -#define A2CR_MARK_IDLE 0x08 -#define A2CR_GO_ACTIVE_ON_POLL 0x10 - -#define A2CR_CODING_MASK 0x60 -#define A2CR_CODING_NRZ 0x00 -#define A2CR_CODING_NRZI 0x20 -#define A2CR_CODING_FM1 0x40 -#define A2CR_CODING_FM0 0x60 - -#define A2CR_PRESET_CRC_1 0x80 - - -/* WR11: CLK_CTRL_REG "CCR" */ - -#define CCR_TRxCOUT_MASK 0x03 -#define CCR_TRxCOUT_XTAL 0x00 -#define CCR_TRxCOUT_TXCLK 0x01 -#define CCR_TRxCOUT_BRG 0x02 -#define CCR_TRxCOUT_DPLL 0x03 - -#define CCR_TRxC_OUTPUT 0x04 - -#define CCR_TXCLK_MASK 0x18 -#define CCR_TXCLK_RTxC 0x00 -#define CCR_TXCLK_TRxC 0x08 -#define CCR_TXCLK_BRG 0x10 -#define CCR_TXCLK_DPLL 0x18 - -#define CCR_RXCLK_MASK 0x60 -#define CCR_RXCLK_RTxC 0x00 -#define CCR_RXCLK_TRxC 0x20 -#define CCR_RXCLK_BRG 0x40 -#define CCR_RXCLK_DPLL 0x60 - -#define CCR_RTxC_XTAL 0x80 - - -/* WR14: DPLL_CTRL_REG "DCR" */ - -#define DCR_BRG_ENAB 0x01 -#define DCR_BRG_USE_PCLK 0x02 -#define DCR_DTRREQ_IS_REQ 0x04 -#define DCR_AUTO_ECHO 0x08 -#define DCR_LOCAL_LOOPBACK 0x10 - -#define DCR_DPLL_EDGE_SEARCH 0x20 -#define DCR_DPLL_ERR_RESET 0x40 -#define DCR_DPLL_DISAB 0x60 -#define DCR_DPLL_CLK_BRG 0x80 -#define DCR_DPLL_CLK_RTxC 0xa0 -#define DCR_DPLL_FM 0xc0 -#define DCR_DPLL_NRZI 0xe0 - - -/* WR15: INT_CTRL_REG "ICR" */ - -#define ICR_OPTIONREG_SELECT 0x01 -#define ICR_ENAB_BRG_ZERO_INT 0x02 -#define ICR_USE_FS_FIFO 0x04 -#define ICR_ENAB_DCD_INT 0x08 -#define ICR_ENAB_SYNC_INT 0x10 -#define ICR_ENAB_CTS_INT 0x20 -#define ICR_ENAB_UNDERRUN_INT 0x40 -#define ICR_ENAB_BREAK_INT 0x80 - - -/* RR0: STATUS_REG "SR" */ - -#define SR_CHAR_AVAIL 0x01 -#define SR_BRG_ZERO 0x02 -#define SR_TX_BUF_EMPTY 0x04 -#define SR_DCD 0x08 -#define SR_SYNC_ABORT 0x10 -#define SR_CTS 0x20 -#define SR_TX_UNDERRUN 0x40 -#define SR_BREAK 0x80 - - -/* RR1: SPCOND_STATUS_REG "SCSR" */ - -#define SCSR_ALL_SENT 0x01 -#define SCSR_RESIDUAL_MASK 0x0e -#define SCSR_PARITY_ERR 0x10 -#define SCSR_RX_OVERRUN 0x20 -#define SCSR_CRC_FRAME_ERR 0x40 -#define SCSR_END_OF_FRAME 0x80 - - -/* RR3: INT_PENDING_REG "IPR" */ - -#define IPR_B_EXTSTAT 0x01 -#define IPR_B_TX 0x02 -#define IPR_B_RX 0x04 -#define IPR_A_EXTSTAT 0x08 -#define IPR_A_TX 0x10 -#define IPR_A_RX 0x20 - - -/* RR7: FS_FIFO_HIGH_REG "FFHR" */ - -#define FFHR_CNT_MASK 0x3f -#define FFHR_IS_FROM_FIFO 0x40 -#define FFHR_FIFO_OVERRUN 0x80 - - -/* RR10: DPLL_STATUS_REG "DSR" */ - -#define DSR_ON_LOOP 0x02 -#define DSR_ON_LOOP_SENDING 0x10 -#define DSR_TWO_CLK_MISSING 0x40 -#define DSR_ONE_CLK_MISSING 0x80 - -/***********************************************************************/ -/* */ -/* Register Access */ -/* */ -/***********************************************************************/ - - -/* The SCC needs 3.5 PCLK cycles recovery time between to register - * accesses. PCLK runs with 8 MHz on an Atari, so this delay is 3.5 * - * 125 ns = 437.5 ns. This is too short for udelay(). - * 10/16/95: A tstb st_mfp.par_dt_reg takes 600ns (sure?) and thus should be - * quite right - */ - -#define scc_reg_delay() \ - do { \ - if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) \ - __asm__ __volatile__ ( " nop; nop"); \ - else if (MACH_IS_ATARI) \ - __asm__ __volatile__ ( "tstb %0" : : "g" (*_scc_del) : "cc" );\ - } while (0) - -static unsigned char scc_shadow[2][16]; - -/* The following functions should relax the somehow complicated - * register access of the SCC. _SCCwrite() stores all written values - * (except for WR0 and WR8) in shadow registers for later recall. This - * removes the burden of remembering written values as needed. The - * extra work of storing the value doesn't count, since a delay is - * needed after a SCC access anyway. Additionally, _SCCwrite() manages - * writes to WR0 and WR8 differently, because these can be accessed - * directly with less overhead. Another special case are WR7 and WR7'. - * _SCCwrite automatically checks what of this registers is selected - * and changes b0 of WR15 if needed. - * - * _SCCread() for standard read registers is straightforward, except - * for RR2 (split into two "virtual" registers: one for the value - * written to WR2 (from the shadow) and one for the vector including - * status from RR2, Ch. B) and RR3. The latter must be read from - * Channel A, because it reads as all zeros on Ch. B. RR0 and RR8 can - * be accessed directly as before. - * - * The two inline function contain complicated switch statements. But - * I rely on regno and final_delay being constants, so gcc can reduce - * the whole stuff to just some assembler statements. - * - * _SCCwrite and _SCCread aren't intended to be used directly under - * normal circumstances. The macros SCCread[_ND] and SCCwrite[_ND] are - * for that purpose. They assume that a local variable 'port' is - * declared and pointing to the port's scc_struct entry. The - * variants with "_NB" appended should be used if no other SCC - * accesses follow immediately (within 0.5 usecs). They just skip the - * final delay nops. - * - * Please note that accesses to SCC registers should only take place - * when interrupts are turned off (at least if SCC interrupts are - * enabled). Otherwise, an interrupt could interfere with the - * two-stage accessing process. - * - */ - - -static __inline__ void _SCCwrite( - struct scc_port *port, - unsigned char *shadow, - volatile unsigned char *_scc_del, - int regno, - unsigned char val, int final_delay ) -{ - switch( regno ) { - - case COMMAND_REG: - /* WR0 can be written directly without pointing */ - *port->ctrlp = val; - break; - - case SYNC_CHAR_REG: - /* For WR7, first set b0 of WR15 to 0, if needed */ - if (shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT) { - *port->ctrlp = 15; - shadow[INT_CTRL_REG] &= ~ICR_OPTIONREG_SELECT; - scc_reg_delay(); - *port->ctrlp = shadow[INT_CTRL_REG]; - scc_reg_delay(); - } - goto normal_case; - - case SDLC_OPTION_REG: - /* For WR7', first set b0 of WR15 to 1, if needed */ - if (!(shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT)) { - *port->ctrlp = 15; - shadow[INT_CTRL_REG] |= ICR_OPTIONREG_SELECT; - scc_reg_delay(); - *port->ctrlp = shadow[INT_CTRL_REG]; - scc_reg_delay(); - } - *port->ctrlp = 7; - shadow[8] = val; /* WR7' shadowed at WR8 */ - scc_reg_delay(); - *port->ctrlp = val; - break; - - case TX_DATA_REG: /* WR8 */ - /* TX_DATA_REG can be accessed directly on some h/w */ - if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) - { - *port->ctrlp = regno; - scc_reg_delay(); - *port->ctrlp = val; - } - else - *port->datap = val; - break; - - case MASTER_INT_CTRL: - *port->ctrlp = regno; - val &= 0x3f; /* bits 6..7 are the reset commands */ - scc_shadow[0][regno] = val; - scc_reg_delay(); - *port->ctrlp = val; - break; - - case DPLL_CTRL_REG: - *port->ctrlp = regno; - val &= 0x1f; /* bits 5..7 are the DPLL commands */ - shadow[regno] = val; - scc_reg_delay(); - *port->ctrlp = val; - break; - - case 1 ... 6: - case 10 ... 13: - case 15: - normal_case: - *port->ctrlp = regno; - shadow[regno] = val; - scc_reg_delay(); - *port->ctrlp = val; - break; - - default: - printk( "Bad SCC write access to WR%d\n", regno ); - break; - - } - - if (final_delay) - scc_reg_delay(); -} - - -static __inline__ unsigned char _SCCread( - struct scc_port *port, - unsigned char *shadow, - volatile unsigned char *_scc_del, - int regno, int final_delay ) -{ - unsigned char rv; - - switch( regno ) { - - /* --- real read registers --- */ - case STATUS_REG: - rv = *port->ctrlp; - break; - - case INT_PENDING_REG: - /* RR3: read only from Channel A! */ - port = port->port_a; - goto normal_case; - - case RX_DATA_REG: - /* RR8 can be accessed directly on some h/w */ - if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) - { - *port->ctrlp = 8; - scc_reg_delay(); - rv = *port->ctrlp; - } - else - rv = *port->datap; - break; - - case CURR_VECTOR_REG: - /* RR2 (vector including status) from Ch. B */ - port = port->port_b; - goto normal_case; - - /* --- reading write registers: access the shadow --- */ - case 1 ... 7: - case 10 ... 15: - return shadow[regno]; /* no final delay! */ - - /* WR7' is special, because it is shadowed at the place of WR8 */ - case SDLC_OPTION_REG: - return shadow[8]; /* no final delay! */ - - /* WR9 is special too, because it is common for both channels */ - case MASTER_INT_CTRL: - return scc_shadow[0][9]; /* no final delay! */ - - default: - printk( "Bad SCC read access to %cR%d\n", (regno & 16) ? 'R' : 'W', - regno & ~16 ); - break; - - case SPCOND_STATUS_REG: - case FS_FIFO_LOW_REG: - case FS_FIFO_HIGH_REG: - case DPLL_STATUS_REG: - normal_case: - *port->ctrlp = regno & 0x0f; - scc_reg_delay(); - rv = *port->ctrlp; - break; - - } - - if (final_delay) - scc_reg_delay(); - return rv; -} - -#define SCC_ACCESS_INIT(port) \ - unsigned char *_scc_shadow = &scc_shadow[port->channel][0] - -#define SCCwrite(reg,val) _SCCwrite(port,_scc_shadow,scc_del,(reg),(val),1) -#define SCCwrite_NB(reg,val) _SCCwrite(port,_scc_shadow,scc_del,(reg),(val),0) -#define SCCread(reg) _SCCread(port,_scc_shadow,scc_del,(reg),1) -#define SCCread_NB(reg) _SCCread(port,_scc_shadow,scc_del,(reg),0) - -#define SCCmod(reg,and,or) SCCwrite((reg),(SCCread(reg)&(and))|(or)) - -#endif /* _SCC_H */ diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 9ca5c021d0b6..361a1dff8f77 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -966,6 +966,9 @@ ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr, { struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip->vendor.duration[TPM_LONG] == 0) + return 0; + return sprintf(buf, "%d %d %d [%s]\n", jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]), jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]), diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c index a1f68af4ccf4..eedd5474850c 100644 --- a/drivers/char/ttyprintk.c +++ b/drivers/char/ttyprintk.c @@ -17,6 +17,7 @@ #include <linux/device.h> #include <linux/serial.h> #include <linux/tty.h> +#include <linux/export.h> struct ttyprintk_port { struct tty_port port; @@ -170,7 +171,7 @@ static const struct tty_operations ttyprintk_ops = { .ioctl = tpk_ioctl, }; -struct tty_port_operations null_ops = { }; +static struct tty_port_operations null_ops = { }; static struct tty_driver *ttyprintk_driver; diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index fb68b1295373..8e3c46d67cb3 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -19,8 +19,10 @@ */ #include <linux/cdev.h> #include <linux/debugfs.h> +#include <linux/completion.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/freezer.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/list.h> @@ -32,6 +34,7 @@ #include <linux/virtio_console.h> #include <linux/wait.h> #include <linux/workqueue.h> +#include <linux/module.h> #include "../tty/hvc/hvc_console.h" /* @@ -73,6 +76,7 @@ struct ports_driver_data { static struct ports_driver_data pdrvdata; DEFINE_SPINLOCK(pdrvdata_lock); +DECLARE_COMPLETION(early_console_added); /* This struct holds information that's relevant only for console ports */ struct console { @@ -151,6 +155,10 @@ struct ports_device { int chr_major; }; +struct port_stats { + unsigned long bytes_sent, bytes_received, bytes_discarded; +}; + /* This struct holds the per-port data */ struct port { /* Next port in the list, head is in the ports_device */ @@ -179,6 +187,13 @@ struct port { struct dentry *debugfs_file; /* + * Keep count of the bytes sent, received and discarded for + * this port for accounting and debugging purposes. These + * counts are not reset across port open / close events. + */ + struct port_stats stats; + + /* * The entries in this struct will be valid if this port is * hooked up to an hvc console */ @@ -347,17 +362,19 @@ fail: } /* Callers should take appropriate locks */ -static void *get_inbuf(struct port *port) +static struct port_buffer *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 (port->inbuf) + return port->inbuf; + + buf = virtqueue_get_buf(port->in_vq, &len); if (buf) { buf->len = len; buf->offset = 0; + port->stats.bytes_received += len; } return buf; } @@ -384,32 +401,27 @@ static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) static void discard_port_data(struct port *port) { struct port_buffer *buf; - struct virtqueue *vq; - unsigned int len; - int ret; + unsigned int err; if (!port->portdev) { /* Device has been unplugged. vqs are already gone. */ return; } - vq = port->in_vq; - if (port->inbuf) - buf = port->inbuf; - else - buf = virtqueue_get_buf(vq, &len); + buf = get_inbuf(port); - ret = 0; + err = 0; while (buf) { - if (add_inbuf(vq, buf) < 0) { - ret++; + port->stats.bytes_discarded += buf->len - buf->offset; + if (add_inbuf(port->in_vq, buf) < 0) { + err++; free_buf(buf); } - buf = virtqueue_get_buf(vq, &len); + port->inbuf = NULL; + buf = get_inbuf(port); } - port->inbuf = NULL; - if (ret) + if (err) dev_warn(port->dev, "Errors adding %d buffers back to vq\n", - ret); + err); } static bool port_has_data(struct port *port) @@ -417,18 +429,12 @@ static bool port_has_data(struct port *port) unsigned long flags; bool ret; + ret = false; spin_lock_irqsave(&port->inbuf_lock, flags); - if (port->inbuf) { - ret = true; - goto out; - } port->inbuf = get_inbuf(port); - if (port->inbuf) { + if (port->inbuf) ret = true; - goto out; - } - ret = false; -out: + spin_unlock_irqrestore(&port->inbuf_lock, flags); return ret; } @@ -529,6 +535,8 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, cpu_relax(); done: spin_unlock_irqrestore(&port->outvq_lock, flags); + + port->stats.bytes_sent += in_count; /* * We're expected to return the amount of data we wrote -- all * of it @@ -633,8 +641,8 @@ static ssize_t port_fops_read(struct file *filp, char __user *ubuf, if (filp->f_flags & O_NONBLOCK) return -EAGAIN; - ret = wait_event_interruptible(port->waitqueue, - !will_read_block(port)); + ret = wait_event_freezable(port->waitqueue, + !will_read_block(port)); if (ret < 0) return ret; } @@ -677,8 +685,8 @@ static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, if (nonblock) return -EAGAIN; - ret = wait_event_interruptible(port->waitqueue, - !will_write_block(port)); + ret = wait_event_freezable(port->waitqueue, + !will_write_block(port)); if (ret < 0) return ret; } @@ -1059,6 +1067,14 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, 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, + "bytes_sent: %lu\n", port->stats.bytes_sent); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "bytes_received: %lu\n", + port->stats.bytes_received); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "bytes_discarded: %lu\n", + port->stats.bytes_discarded); + 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, @@ -1143,6 +1159,7 @@ static int add_port(struct ports_device *portdev, u32 id) port->cons.ws.ws_row = port->cons.ws.ws_col = 0; port->host_connected = port->guest_connected = false; + port->stats = (struct port_stats) { 0 }; port->outvq_full = false; @@ -1352,6 +1369,7 @@ static void handle_control_message(struct ports_device *portdev, break; init_port_console(port); + complete(&early_console_added); /* * Could remove the port here in case init fails - but * have to notify the host first. @@ -1394,6 +1412,13 @@ static void handle_control_message(struct ports_device *portdev, break; case VIRTIO_CONSOLE_PORT_NAME: /* + * If we woke up after hibernation, we can get this + * again. Skip it in that case. + */ + if (port->name) + break; + + /* * Skip the size of the header and the cpkt to get the size * of the name that was sent */ @@ -1481,8 +1506,7 @@ static void in_intr(struct virtqueue *vq) return; spin_lock_irqsave(&port->inbuf_lock, flags); - if (!port->inbuf) - port->inbuf = get_inbuf(port); + port->inbuf = get_inbuf(port); /* * Don't queue up data when port is closed. This condition @@ -1563,7 +1587,7 @@ static int init_vqs(struct ports_device *portdev) portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), GFP_KERNEL); if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs || - !portdev->out_vqs) { + !portdev->out_vqs) { err = -ENOMEM; goto free; } @@ -1648,6 +1672,10 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) struct ports_device *portdev; int err; bool multiport; + bool early = early_put_chars != NULL; + + /* Ensure to read early_put_chars now */ + barrier(); portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); if (!portdev) { @@ -1675,13 +1703,11 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) multiport = false; portdev->config.max_nr_ports = 1; - if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { + if (virtio_config_val(vdev, VIRTIO_CONSOLE_F_MULTIPORT, + offsetof(struct virtio_console_config, + max_nr_ports), + &portdev->config.max_nr_ports) == 0) multiport = true; - vdev->config->get(vdev, offsetof(struct virtio_console_config, - max_nr_ports), - &portdev->config.max_nr_ports, - sizeof(portdev->config.max_nr_ports)); - } err = init_vqs(portdev); if (err < 0) { @@ -1719,6 +1745,19 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, VIRTIO_CONSOLE_DEVICE_READY, 1); + + /* + * If there was an early virtio console, assume that there are no + * other consoles. We need to wait until the hvc_alloc matches the + * hvc_instantiate, otherwise tty_open will complain, resulting in + * a "Warning: unable to open an initial console" boot failure. + * Without multiport this is done in add_port above. With multiport + * this might take some host<->guest communication - thus we have to + * wait. + */ + if (multiport && early) + wait_for_completion(&early_console_added); + return 0; free_vqs: |