From 55bd2133fc3c5ee138d19b76ed8f9495b35d57ec Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:02:57 -0400 Subject: sparc64: vcc: Enable VCC module in linux Enables the Virtual Console Concentrator (VCC) module in linux kernel Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/tty/Kconfig | 5 +++++ drivers/tty/Makefile | 1 + drivers/tty/vcc.c | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 drivers/tty/vcc.c (limited to 'drivers/tty') diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 95103054c0e4..8643b55dcae2 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -455,4 +455,9 @@ config MIPS_EJTAG_FDC_KGDB_CHAN help FDC channel number to use for KGDB. +config VCC + tristate "Sun Virtual Console Concentrator" + depends on SUN_LDOMS + help + Support for Sun logical domain consoles. endif # TTY diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 8689279afdf1..16330a819685 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -33,5 +33,6 @@ obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_DA_TTY) += metag_da.o obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o +obj-$(CONFIG_VCC) += vcc.o obj-y += ipwireless/ diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c new file mode 100644 index 000000000000..e1b809927cd6 --- /dev/null +++ b/drivers/tty/vcc.c @@ -0,0 +1,18 @@ +/* vcc.c: sun4v virtual channel concentrator + * + * Copyright (C) 2017 Oracle. All rights reserved. + */ + +#include + +static int __init vcc_init(void) +{ + return 0; +} + +static void __exit vcc_exit(void) +{ +} + +module_init(vcc_init); +module_exit(vcc_exit); -- cgit v1.2.3-59-g8ed1b From f283ebd5642d837d372f5e580d550c34dd6eef3d Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:02:58 -0400 Subject: sparc64: vcc: Add VCC debug message macros Add C macros to print debug messages from VCC module Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- arch/sparc/kernel/ldc.c | 1 + drivers/tty/vcc.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) (limited to 'drivers/tty') diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c index 840e0b21bfe3..1169915f7568 100644 --- a/arch/sparc/kernel/ldc.c +++ b/arch/sparc/kernel/ldc.c @@ -1493,6 +1493,7 @@ void __ldc_print(struct ldc_channel *lp, const char *caller) lp->tx_head, lp->tx_tail, lp->tx_num_entries, lp->rcv_nxt, lp->snd_nxt); } +EXPORT_SYMBOL(__ldc_print); static int write_raw(struct ldc_channel *lp, const void *buf, unsigned int size) { diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index e1b809927cd6..4fd8dd0bda5e 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -5,6 +5,47 @@ #include +#define DRV_MODULE_NAME "vcc" +#define DRV_MODULE_VERSION "1.1" +#define DRV_MODULE_RELDATE "July 1, 2017" + +MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_MODULE_VERSION); + +int vcc_dbg; +int vcc_dbg_ldc; +int vcc_dbg_vio; + +module_param(vcc_dbg, uint, 0664); +module_param(vcc_dbg_ldc, uint, 0664); +module_param(vcc_dbg_vio, uint, 0664); + +#define VCC_DBG_DRV 0x1 +#define VCC_DBG_LDC 0x2 +#define VCC_DBG_PKT 0x4 + +#define vccdbg(f, a...) \ + do { \ + if (vcc_dbg & VCC_DBG_DRV) \ + pr_info(f, ## a); \ + } while (0) \ + +#define vccdbgl(l) \ + do { \ + if (vcc_dbg & VCC_DBG_LDC) \ + ldc_print(l); \ + } while (0) \ + +#define vccdbgp(pkt) \ + do { \ + if (vcc_dbg & VCC_DBG_PKT) { \ + int i; \ + for (i = 0; i < pkt.tag.stype; i++) \ + pr_info("[%c]", pkt.data[i]); \ + } \ + } while (0) \ + static int __init vcc_init(void) { return 0; -- cgit v1.2.3-59-g8ed1b From ce808b746325975192d8cd1d29f1ec03d5b6b0fc Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:02:59 -0400 Subject: sparc64: vcc: TTY driver initialization and cleanup Allocate and register TTY driver during module init. Cleanup TTY driver during module exit. Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 4fd8dd0bda5e..ba3384d23d79 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -4,15 +4,26 @@ */ #include +#include #define DRV_MODULE_NAME "vcc" #define DRV_MODULE_VERSION "1.1" #define DRV_MODULE_RELDATE "July 1, 2017" +static char version[] = + DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")"; + MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); +#define VCC_MAX_PORTS 1024 +#define VCC_MINOR_START 0 /* must be zero */ + +static const char vcc_driver_name[] = "vcc"; +static const char vcc_device_node[] = "vcc"; +static struct tty_driver *vcc_tty_driver; + int vcc_dbg; int vcc_dbg_ldc; int vcc_dbg_vio; @@ -46,13 +57,83 @@ module_param(vcc_dbg_vio, uint, 0664); } \ } while (0) \ -static int __init vcc_init(void) +/* Note: 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 vcc_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 +}; + +static const struct tty_operations vcc_ops; + +#define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) + +static int vcc_tty_init(void) { + int rv; + + pr_info("VCC: %s\n", version); + + vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS); + if (!vcc_tty_driver) { + pr_err("VCC: TTY driver alloc failed\n"); + return -ENOMEM; + } + + vcc_tty_driver->driver_name = vcc_driver_name; + vcc_tty_driver->name = vcc_device_node; + + vcc_tty_driver->minor_start = VCC_MINOR_START; + vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; + vcc_tty_driver->init_termios = vcc_tty_termios; + + tty_set_operations(vcc_tty_driver, &vcc_ops); + + rv = tty_register_driver(vcc_tty_driver); + if (rv) { + pr_err("VCC: TTY driver registration failed\n"); + put_tty_driver(vcc_tty_driver); + vcc_tty_driver = NULL; + return rv; + } + + vccdbg("VCC: TTY driver registered\n"); + return 0; } +static void vcc_tty_exit(void) +{ + tty_unregister_driver(vcc_tty_driver); + put_tty_driver(vcc_tty_driver); + vccdbg("VCC: TTY driver unregistered\n"); + + vcc_tty_driver = NULL; +} + +static int __init vcc_init(void) +{ + int rv; + + rv = vcc_tty_init(); + if (rv) { + pr_err("VCC: TTY init failed\n"); + return rv; + } + + return rv; +} + static void __exit vcc_exit(void) { + vcc_tty_exit(); + vccdbg("VCC: TTY driver unregistered\n"); } module_init(vcc_init); -- cgit v1.2.3-59-g8ed1b From 5d171050e28f823aeb040f2830da4d3422b54b63 Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:03:00 -0400 Subject: sparc64: vcc: Enable VCC port probe and removal Enables VCC port probe and removal to initialize and terminate VCC ports respectively. When a device/port matching the VCC driver is added, the probe function is invoked along with a reference to the device. remove function is called when the device is removed. Also add APIs to cache and retrieve VCC ports from a VCC table Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- arch/sparc/include/asm/vio.h | 9 + arch/sparc/kernel/vio.c | 1 + arch/sparc/kernel/viohs.c | 12 +- drivers/tty/vcc.c | 379 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 398 insertions(+), 3 deletions(-) (limited to 'drivers/tty') diff --git a/arch/sparc/include/asm/vio.h b/arch/sparc/include/asm/vio.h index d1c47e9f0090..f3d4ac232690 100644 --- a/arch/sparc/include/asm/vio.h +++ b/arch/sparc/include/asm/vio.h @@ -52,6 +52,7 @@ struct vio_ver_info { #define VDEV_NETWORK_SWITCH 0x02 #define VDEV_DISK 0x03 #define VDEV_DISK_SERVER 0x04 +#define VDEV_CONSOLE_CON 0x05 u8 resv1[3]; u64 resv2[5]; @@ -282,6 +283,14 @@ struct vio_dring_state { struct ldc_trans_cookie cookies[VIO_MAX_RING_COOKIES]; }; +#define VIO_TAG_SIZE ((int)sizeof(struct vio_msg_tag)) +#define VIO_VCC_MTU_SIZE (LDC_PACKET_SIZE - VIO_TAG_SIZE) + +struct vio_vcc { + struct vio_msg_tag tag; + char data[VIO_VCC_MTU_SIZE]; +}; + static inline void *vio_dring_cur(struct vio_dring_state *dr) { return dr->base + (dr->entry_size * dr->prod); diff --git a/arch/sparc/kernel/vio.c b/arch/sparc/kernel/vio.c index 1c8763c9c52b..da1ac3f22b24 100644 --- a/arch/sparc/kernel/vio.c +++ b/arch/sparc/kernel/vio.c @@ -246,6 +246,7 @@ u64 vio_vdev_node(struct mdesc_handle *hp, struct vio_dev *vdev) return node; } +EXPORT_SYMBOL(vio_vdev_node); static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp, struct vio_dev *vdev) diff --git a/arch/sparc/kernel/viohs.c b/arch/sparc/kernel/viohs.c index d4f13c037a40..dcd278f29573 100644 --- a/arch/sparc/kernel/viohs.c +++ b/arch/sparc/kernel/viohs.c @@ -814,15 +814,21 @@ int vio_driver_init(struct vio_driver_state *vio, struct vio_dev *vdev, case VDEV_NETWORK_SWITCH: case VDEV_DISK: case VDEV_DISK_SERVER: + case VDEV_CONSOLE_CON: break; default: return -EINVAL; } - if (!ops || !ops->send_attr || !ops->handle_attr || - !ops->handshake_complete) - return -EINVAL; + if (dev_class == VDEV_NETWORK || + dev_class == VDEV_NETWORK_SWITCH || + dev_class == VDEV_DISK || + dev_class == VDEV_DISK_SERVER) { + if (!ops || !ops->send_attr || !ops->handle_attr || + !ops->handshake_complete) + return -EINVAL; + } if (!ver_table || ver_table_size < 0) return -EINVAL; diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index ba3384d23d79..4edace1d9e4b 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -3,8 +3,13 @@ * Copyright (C) 2017 Oracle. All rights reserved. */ +#include +#include #include +#include #include +#include +#include #define DRV_MODULE_NAME "vcc" #define DRV_MODULE_VERSION "1.1" @@ -17,6 +22,33 @@ MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); +struct vcc_port { + struct vio_driver_state vio; + + spinlock_t lock; + char *domain; + struct tty_struct *tty; /* only populated while dev is open */ + unsigned long index; /* index into the vcc_table */ + + u64 refcnt; + bool excl_locked; + + bool removed; + + /* This buffer is required to support the tty write_room interface + * and guarantee that any characters that the driver accepts will + * be eventually sent, either immediately or later. + */ + int chars_in_buffer; + struct vio_vcc buffer; + + struct timer_list rx_timer; + struct timer_list tx_timer; +}; + +/* Microseconds that thread will delay waiting for a vcc port ref */ +#define VCC_REF_DELAY 100 + #define VCC_MAX_PORTS 1024 #define VCC_MINOR_START 0 /* must be zero */ @@ -24,6 +56,9 @@ static const char vcc_driver_name[] = "vcc"; static const char vcc_device_node[] = "vcc"; static struct tty_driver *vcc_tty_driver; +static struct vcc_port *vcc_table[VCC_MAX_PORTS]; +static DEFINE_SPINLOCK(vcc_table_lock); + int vcc_dbg; int vcc_dbg_ldc; int vcc_dbg_vio; @@ -70,6 +105,340 @@ static struct ktermios vcc_tty_termios = { .c_ospeed = 38400 }; +/** + * vcc_table_add() - Add VCC port to the VCC table + * @port: pointer to the VCC port + * + * Return: index of the port in the VCC table on success, + * -1 on failure + */ +static int vcc_table_add(struct vcc_port *port) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&vcc_table_lock, flags); + for (i = VCC_MINOR_START; i < VCC_MAX_PORTS; i++) { + if (!vcc_table[i]) { + vcc_table[i] = port; + break; + } + } + spin_unlock_irqrestore(&vcc_table_lock, flags); + + if (i < VCC_MAX_PORTS) + return i; + else + return -1; +} + +/** + * vcc_table_remove() - Removes a VCC port from the VCC table + * @index: Index into the VCC table + */ +static void vcc_table_remove(unsigned long index) +{ + unsigned long flags; + + if (WARN_ON(index >= VCC_MAX_PORTS)) + return; + + spin_lock_irqsave(&vcc_table_lock, flags); + vcc_table[index] = NULL; + spin_unlock_irqrestore(&vcc_table_lock, flags); +} + +/** + * vcc_get() - Gets a reference to VCC port + * @index: Index into the VCC table + * @excl: Indicates if an exclusive access is requested + * + * Return: reference to the VCC port, if found + * NULL, if port not found + */ +static struct vcc_port *vcc_get(unsigned long index, bool excl) +{ + struct vcc_port *port; + unsigned long flags; + +try_again: + spin_lock_irqsave(&vcc_table_lock, flags); + + port = vcc_table[index]; + if (!port) { + spin_unlock_irqrestore(&vcc_table_lock, flags); + return NULL; + } + + if (!excl) { + if (port->excl_locked) { + spin_unlock_irqrestore(&vcc_table_lock, flags); + udelay(VCC_REF_DELAY); + goto try_again; + } + port->refcnt++; + spin_unlock_irqrestore(&vcc_table_lock, flags); + return port; + } + + if (port->refcnt) { + spin_unlock_irqrestore(&vcc_table_lock, flags); + /* Threads wanting exclusive access will wait half the time, + * probably giving them higher priority in the case of + * multiple waiters. + */ + udelay(VCC_REF_DELAY/2); + goto try_again; + } + + port->refcnt++; + port->excl_locked = true; + spin_unlock_irqrestore(&vcc_table_lock, flags); + + return port; +} + +/** + * vcc_put() - Returns a reference to VCC port + * @port: pointer to VCC port + * @excl: Indicates if the returned reference is an exclusive reference + * + * Note: It's the caller's responsibility to ensure the correct value + * for the excl flag + */ +static void vcc_put(struct vcc_port *port, bool excl) +{ + unsigned long flags; + + if (!port) + return; + + spin_lock_irqsave(&vcc_table_lock, flags); + + /* check if caller attempted to put with the wrong flags */ + if (WARN_ON((excl && !port->excl_locked) || + (!excl && port->excl_locked))) + goto done; + + port->refcnt--; + + if (excl) + port->excl_locked = false; + +done: + spin_unlock_irqrestore(&vcc_table_lock, flags); +} + +/** + * vcc_get_ne() - Get a non-exclusive reference to VCC port + * @index: Index into the VCC table + * + * Gets a non-exclusive reference to VCC port, if it's not removed + * + * Return: pointer to the VCC port, if found + * NULL, if port not found + */ +static struct vcc_port *vcc_get_ne(unsigned long index) +{ + struct vcc_port *port; + + port = vcc_get(index, false); + + if (port && port->removed) { + vcc_put(port, false); + return NULL; + } + + return port; +} + +static void vcc_event(void *arg, int event) +{ +} + +static struct ldc_channel_config vcc_ldc_cfg = { + .event = vcc_event, + .mtu = VIO_VCC_MTU_SIZE, + .mode = LDC_MODE_RAW, + .debug = 0, +}; + +/* Ordered from largest major to lowest */ +static struct vio_version vcc_versions[] = { + { .major = 1, .minor = 0 }, +}; + +/** + * vcc_probe() - Initialize VCC port + * @vdev: Pointer to VIO device of the new VCC port + * @id: VIO device ID + * + * Initializes a VCC port to receive serial console data from + * the guest domain. Sets up a TTY end point on the control + * domain. Sets up VIO/LDC link between the guest & control + * domain endpoints. + * + * Return: status of the probe + */ +static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) +{ + struct mdesc_handle *hp; + struct vcc_port *port; + struct device *dev; + const char *domain; + char *name; + u64 node; + int rv; + + vccdbg("VCC: name=%s\n", dev_name(&vdev->dev)); + + if (!vcc_tty_driver) { + pr_err("VCC: TTY driver not registered\n"); + return -ENODEV; + } + + port = kzalloc(sizeof(struct vcc_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + name = kstrdup(dev_name(&vdev->dev), GFP_KERNEL); + + rv = vio_driver_init(&port->vio, vdev, VDEV_CONSOLE_CON, vcc_versions, + ARRAY_SIZE(vcc_versions), NULL, name); + if (rv) + goto free_port; + + port->vio.debug = vcc_dbg_vio; + vcc_ldc_cfg.debug = vcc_dbg_ldc; + + rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port); + if (rv) + goto free_port; + + spin_lock_init(&port->lock); + + port->index = vcc_table_add(port); + if (port->index == -1) { + pr_err("VCC: no more TTY indices left for allocation\n"); + goto free_ldc; + } + + /* Register the device using VCC table index as TTY index */ + dev = tty_register_device(vcc_tty_driver, port->index, &vdev->dev); + if (IS_ERR(dev)) { + rv = PTR_ERR(dev); + goto free_table; + } + + hp = mdesc_grab(); + + node = vio_vdev_node(hp, vdev); + if (node == MDESC_NODE_NULL) { + rv = -ENXIO; + mdesc_release(hp); + goto unreg_tty; + } + + domain = mdesc_get_property(hp, node, "vcc-domain-name", NULL); + if (!domain) { + rv = -ENXIO; + mdesc_release(hp); + goto unreg_tty; + } + port->domain = kstrdup(domain, GFP_KERNEL); + + mdesc_release(hp); + + dev_set_drvdata(&vdev->dev, port); + + /* It's possible to receive IRQs in the middle of vio_port_up. Disable + * IRQs until the port is up. + */ + disable_irq_nosync(vdev->rx_irq); + vio_port_up(&port->vio); + enable_irq(vdev->rx_irq); + + return 0; + +unreg_tty: + tty_unregister_device(vcc_tty_driver, port->index); +free_table: + vcc_table_remove(port->index); +free_ldc: + vio_ldc_free(&port->vio); +free_port: + kfree(name); + kfree(port); + + return rv; +} + +/** + * vcc_remove() - Terminate a VCC port + * @vdev: Pointer to VIO device of the VCC port + * + * Terminates a VCC port. Sets up the teardown of TTY and + * VIO/LDC link between guest and primary domains. + * + * Return: status of removal + */ +static int vcc_remove(struct vio_dev *vdev) +{ + struct vcc_port *port = dev_get_drvdata(&vdev->dev); + + if (!port) + return -ENODEV; + + /* If there's a process with the device open, do a synchronous + * hangup of the TTY. This *may* cause the process to call close + * asynchronously, but it's not guaranteed. + */ + if (port->tty) + tty_vhangup(port->tty); + + /* Get exclusive reference to VCC, ensures that there are no other + * clients to this port + */ + port = vcc_get(port->index, true); + + if (WARN_ON(!port)) + return -ENODEV; + + tty_unregister_device(vcc_tty_driver, port->index); + + del_timer_sync(&port->vio.timer); + vio_ldc_free(&port->vio); + dev_set_drvdata(&vdev->dev, NULL); + + if (port->tty) { + port->removed = true; + vcc_put(port, true); + } else { + vcc_table_remove(port->index); + + kfree(port->vio.name); + kfree(port->domain); + kfree(port); + } + + return 0; +} + +static const struct vio_device_id vcc_match[] = { + { + .type = "vcc-port", + }, + {}, +}; +MODULE_DEVICE_TABLE(vio, vcc_match); + +static struct vio_driver vcc_driver = { + .id_table = vcc_match, + .probe = vcc_probe, + .remove = vcc_remove, + .name = "vcc", +}; + static const struct tty_operations vcc_ops; #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) @@ -127,11 +496,21 @@ static int __init vcc_init(void) return rv; } + rv = vio_register_driver(&vcc_driver); + if (rv) { + pr_err("VCC: VIO driver registration failed\n"); + vcc_tty_exit(); + } else { + vccdbg("VCC: VIO driver registered successfully\n"); + } + return rv; } static void __exit vcc_exit(void) { + vio_unregister_driver(&vcc_driver); + vccdbg("VCC: VIO driver unregistered\n"); vcc_tty_exit(); vccdbg("VCC: TTY driver unregistered\n"); } -- cgit v1.2.3-59-g8ed1b From 8868e44a56160328cd4003d95f4083f64b136e33 Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:03:01 -0400 Subject: sparc64: vcc: Create sysfs attribute group Create sysfs attribute group to show the domain name and send break command. Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 4edace1d9e4b..72e0879e332d 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,9 @@ struct vcc_port { #define VCC_MAX_PORTS 1024 #define VCC_MINOR_START 0 /* must be zero */ +#define VCC_CTL_BREAK -1 +#define VCC_CTL_HUP -2 + static const char vcc_driver_name[] = "vcc"; static const char vcc_device_node[] = "vcc"; static struct tty_driver *vcc_tty_driver; @@ -268,6 +272,77 @@ static struct vio_version vcc_versions[] = { { .major = 1, .minor = 0 }, }; +static ssize_t vcc_sysfs_domain_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct vcc_port *port; + int rv; + + port = dev_get_drvdata(dev); + if (!port) + return -ENODEV; + + rv = scnprintf(buf, PAGE_SIZE, "%s\n", port->domain); + + return rv; +} + +static int vcc_send_ctl(struct vcc_port *port, int ctl) +{ + struct vio_vcc pkt; + int rv; + + pkt.tag.type = VIO_TYPE_CTRL; + pkt.tag.sid = ctl; + pkt.tag.stype = 0; + + rv = ldc_write(port->vio.lp, &pkt, sizeof(pkt.tag)); + WARN_ON(!rv); + vccdbg("VCC: ldc_write(%ld)=%d\n", sizeof(pkt.tag), rv); + + return rv; +} + +static ssize_t vcc_sysfs_break_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vcc_port *port; + unsigned long flags; + int rv = count; + int brk; + + port = dev_get_drvdata(dev); + if (!port) + return -ENODEV; + + spin_lock_irqsave(&port->lock, flags); + + if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) + rv = -EINVAL; + else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) + pr_err("VCC: unable to send CTL_BREAK\n"); + + spin_unlock_irqrestore(&port->lock, flags); + + return rv; +} + +static DEVICE_ATTR(domain, 0400, vcc_sysfs_domain_show, NULL); +static DEVICE_ATTR(break, 0200, NULL, vcc_sysfs_break_store); + +static struct attribute *vcc_sysfs_entries[] = { + &dev_attr_domain.attr, + &dev_attr_break.attr, + NULL +}; + +static struct attribute_group vcc_attribute_group = { + .name = NULL, + .attrs = vcc_sysfs_entries, +}; + /** * vcc_probe() - Initialize VCC port * @vdev: Pointer to VIO device of the new VCC port @@ -349,6 +424,10 @@ static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) mdesc_release(hp); + rv = sysfs_create_group(&vdev->dev.kobj, &vcc_attribute_group); + if (rv) + goto free_domain; + dev_set_drvdata(&vdev->dev, port); /* It's possible to receive IRQs in the middle of vio_port_up. Disable @@ -360,6 +439,8 @@ static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) return 0; +free_domain: + kfree(port->domain); unreg_tty: tty_unregister_device(vcc_tty_driver, port->index); free_table: @@ -408,6 +489,7 @@ static int vcc_remove(struct vio_dev *vdev) del_timer_sync(&port->vio.timer); vio_ldc_free(&port->vio); + sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); dev_set_drvdata(&vdev->dev, NULL); if (port->tty) { -- cgit v1.2.3-59-g8ed1b From f8c553354e71fbb982ff63f2f574c1658aea50a9 Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:03:02 -0400 Subject: sparc64: vcc: Add RX & TX timer for delayed LDC operation Add RX & TX timers to perform delayed/asynchronous LDC read and write operations. Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- arch/sparc/kernel/ldc.c | 1 + drivers/tty/vcc.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 194 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c index 1169915f7568..acffbc894ab0 100644 --- a/arch/sparc/kernel/ldc.c +++ b/arch/sparc/kernel/ldc.c @@ -1480,6 +1480,7 @@ int ldc_rx_reset(struct ldc_channel *lp) { return __set_rx_head(lp, lp->rx_tail); } +EXPORT_SYMBOL(ldc_rx_reset); void __ldc_print(struct ldc_channel *lp, const char *caller) { diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 72e0879e332d..27566d5f7df0 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -52,6 +53,7 @@ struct vcc_port { #define VCC_MAX_PORTS 1024 #define VCC_MINOR_START 0 /* must be zero */ +#define VCC_BUFF_LEN VIO_VCC_MTU_SIZE #define VCC_CTL_BREAK -1 #define VCC_CTL_HUP -2 @@ -256,6 +258,185 @@ static struct vcc_port *vcc_get_ne(unsigned long index) return port; } +static void vcc_kick_rx(struct vcc_port *port) +{ + struct vio_driver_state *vio = &port->vio; + + assert_spin_locked(&port->lock); + + if (!timer_pending(&port->rx_timer) && !port->removed) { + disable_irq_nosync(vio->vdev->rx_irq); + port->rx_timer.expires = (jiffies + 1); + add_timer(&port->rx_timer); + } +} + +static void vcc_kick_tx(struct vcc_port *port) +{ + assert_spin_locked(&port->lock); + + if (!timer_pending(&port->tx_timer) && !port->removed) { + port->tx_timer.expires = (jiffies + 1); + add_timer(&port->tx_timer); + } +} + +static int vcc_rx_check(struct tty_struct *tty, int size) +{ + if (WARN_ON(!tty || !tty->port)) + return 1; + + /* tty_buffer_request_room won't sleep because it uses + * GFP_ATOMIC flag to allocate buffer + */ + if (test_bit(TTY_THROTTLED, &tty->flags) || + (tty_buffer_request_room(tty->port, VCC_BUFF_LEN) < VCC_BUFF_LEN)) + return 0; + + return 1; +} + +static int vcc_rx(struct tty_struct *tty, char *buf, int size) +{ + int len = 0; + + if (WARN_ON(!tty || !tty->port)) + return len; + + len = tty_insert_flip_string(tty->port, buf, size); + if (len) + tty_flip_buffer_push(tty->port); + + return len; +} + +static int vcc_ldc_read(struct vcc_port *port) +{ + struct vio_driver_state *vio = &port->vio; + struct tty_struct *tty; + struct vio_vcc pkt; + int rv = 0; + + tty = port->tty; + if (!tty) { + rv = ldc_rx_reset(vio->lp); + vccdbg("VCC: reset rx q: rv=%d\n", rv); + goto done; + } + + /* Read as long as LDC has incoming data. */ + while (1) { + if (!vcc_rx_check(tty, VIO_VCC_MTU_SIZE)) { + vcc_kick_rx(port); + break; + } + + vccdbgl(vio->lp); + + rv = ldc_read(vio->lp, &pkt, sizeof(pkt)); + if (rv <= 0) + break; + + vccdbg("VCC: ldc_read()=%d\n", rv); + vccdbg("TAG [%02x:%02x:%04x:%08x]\n", + pkt.tag.type, pkt.tag.stype, + pkt.tag.stype_env, pkt.tag.sid); + + if (pkt.tag.type == VIO_TYPE_DATA) { + vccdbgp(pkt); + /* vcc_rx_check ensures memory availability */ + vcc_rx(tty, pkt.data, pkt.tag.stype); + } else { + pr_err("VCC: unknown msg [%02x:%02x:%04x:%08x]\n", + pkt.tag.type, pkt.tag.stype, + pkt.tag.stype_env, pkt.tag.sid); + rv = -ECONNRESET; + break; + } + + WARN_ON(rv != LDC_PACKET_SIZE); + } + +done: + return rv; +} + +static void vcc_rx_timer(unsigned long index) +{ + struct vio_driver_state *vio; + struct vcc_port *port; + unsigned long flags; + int rv; + + port = vcc_get_ne(index); + if (!port) + return; + + spin_lock_irqsave(&port->lock, flags); + port->rx_timer.expires = 0; + + vio = &port->vio; + + enable_irq(vio->vdev->rx_irq); + + if (!port->tty || port->removed) + goto done; + + rv = vcc_ldc_read(port); + if (rv == -ECONNRESET) + vio_conn_reset(vio); + +done: + spin_unlock_irqrestore(&port->lock, flags); + vcc_put(port, false); +} + +static void vcc_tx_timer(unsigned long index) +{ + struct vcc_port *port; + struct vio_vcc *pkt; + unsigned long flags; + int tosend = 0; + int rv; + + port = vcc_get_ne(index); + if (!port) + return; + + spin_lock_irqsave(&port->lock, flags); + port->tx_timer.expires = 0; + + if (!port->tty || port->removed) + goto done; + + tosend = min(VCC_BUFF_LEN, port->chars_in_buffer); + if (!tosend) + goto done; + + pkt = &port->buffer; + pkt->tag.type = VIO_TYPE_DATA; + pkt->tag.stype = tosend; + vccdbgl(port->vio.lp); + + rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); + WARN_ON(!rv); + + if (rv < 0) { + vccdbg("VCC: ldc_write()=%d\n", rv); + vcc_kick_tx(port); + } else { + struct tty_struct *tty = port->tty; + + port->chars_in_buffer = 0; + if (tty) + tty_wakeup(tty); + } + +done: + spin_unlock_irqrestore(&port->lock, flags); + vcc_put(port, false); +} + static void vcc_event(void *arg, int event) { } @@ -322,7 +503,7 @@ static ssize_t vcc_sysfs_break_store(struct device *dev, if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) rv = -EINVAL; else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) - pr_err("VCC: unable to send CTL_BREAK\n"); + vcc_kick_tx(port); spin_unlock_irqrestore(&port->lock, flags); @@ -428,6 +609,14 @@ static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) if (rv) goto free_domain; + init_timer(&port->rx_timer); + port->rx_timer.function = vcc_rx_timer; + port->rx_timer.data = port->index; + + init_timer(&port->tx_timer); + port->tx_timer.function = vcc_tx_timer; + port->tx_timer.data = port->index; + dev_set_drvdata(&vdev->dev, port); /* It's possible to receive IRQs in the middle of vio_port_up. Disable @@ -470,6 +659,9 @@ static int vcc_remove(struct vio_dev *vdev) if (!port) return -ENODEV; + del_timer_sync(&port->rx_timer); + del_timer_sync(&port->tx_timer); + /* If there's a process with the device open, do a synchronous * hangup of the TTY. This *may* cause the process to call close * asynchronously, but it's not guaranteed. @@ -491,7 +683,6 @@ static int vcc_remove(struct vio_dev *vdev) vio_ldc_free(&port->vio); sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); dev_set_drvdata(&vdev->dev, NULL); - if (port->tty) { port->removed = true; vcc_put(port, true); -- cgit v1.2.3-59-g8ed1b From 103b9b0bc9e068d5e9432ee1da526d8feabc0216 Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:03:03 -0400 Subject: sparc64: vcc: Enable LDC event processing engine Enable event processing engine to handle LDC events Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 27566d5f7df0..91f663d207b6 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -437,8 +437,42 @@ done: vcc_put(port, false); } +/** + * vcc_event() - LDC event processing engine + * @arg: VCC private data + * @event: LDC event + * + * Handles LDC events for VCC + */ static void vcc_event(void *arg, int event) { + struct vio_driver_state *vio; + struct vcc_port *port; + unsigned long flags; + int rv; + + port = arg; + vio = &port->vio; + + spin_lock_irqsave(&port->lock, flags); + + switch (event) { + case LDC_EVENT_RESET: + case LDC_EVENT_UP: + vio_link_state_change(vio, event); + break; + + case LDC_EVENT_DATA_READY: + rv = vcc_ldc_read(port); + if (rv == -ECONNRESET) + vio_conn_reset(vio); + break; + + default: + pr_err("VCC: unexpected LDC event(%d)\n", event); + } + + spin_unlock_irqrestore(&port->lock, flags); } static struct ldc_channel_config vcc_ldc_cfg = { -- cgit v1.2.3-59-g8ed1b From 63a7174484223f4ba35a3bad8db43234ab56ecea Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:03:04 -0400 Subject: sparc64: vcc: Add open & close TTY operations Add handlers to support TTY open & close operations Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 91f663d207b6..f964fb20e4ba 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -746,7 +746,68 @@ static struct vio_driver vcc_driver = { .name = "vcc", }; -static const struct tty_operations vcc_ops; +static int vcc_open(struct tty_struct *tty, struct file *vcc_file) +{ + struct vcc_port *port; + + if (unlikely(!tty)) { + pr_err("VCC: open: Invalid TTY handle\n"); + return -ENXIO; + } + + if (tty->count > 1) + return -EBUSY; + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: open: Failed to find VCC port\n"); + return -ENODEV; + } + + if (unlikely(!port->vio.lp)) { + pr_err("VCC: open: LDC channel not configured\n"); + vcc_put(port, false); + return -EPIPE; + } + vccdbgl(port->vio.lp); + + vcc_put(port, false); + + if (unlikely(!tty->port)) { + pr_err("VCC: open: TTY port not found\n"); + return -ENXIO; + } + + if (unlikely(!tty->port->ops)) { + pr_err("VCC: open: TTY ops not defined\n"); + return -ENXIO; + } + + return tty_port_open(tty->port, tty, vcc_file); +} + +static void vcc_close(struct tty_struct *tty, struct file *vcc_file) +{ + if (unlikely(!tty)) { + pr_err("VCC: close: Invalid TTY handle\n"); + return; + } + + if (unlikely(tty->count > 1)) + return; + + if (unlikely(!tty->port)) { + pr_err("VCC: close: TTY port not found\n"); + return; + } + + tty_port_close(tty->port, tty, vcc_file); +} + +static const struct tty_operations vcc_ops = { + .open = vcc_open, + .close = vcc_close, +}; #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) -- cgit v1.2.3-59-g8ed1b From 6a3ff25bc6cda6293eeed6056f73d76eb35e3076 Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:03:05 -0400 Subject: sparc64: vcc: Add hangup TTY operation Add handler to support TTY hangup operation Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index f964fb20e4ba..67be5a57c1b5 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -804,9 +804,50 @@ static void vcc_close(struct tty_struct *tty, struct file *vcc_file) tty_port_close(tty->port, tty, vcc_file); } +static void vcc_ldc_hup(struct vcc_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + if (vcc_send_ctl(port, VCC_CTL_HUP) < 0) + vcc_kick_tx(port); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void vcc_hangup(struct tty_struct *tty) +{ + struct vcc_port *port; + + if (unlikely(!tty)) { + pr_err("VCC: hangup: Invalid TTY handle\n"); + return; + } + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: hangup: Failed to find VCC port\n"); + return; + } + + if (unlikely(!tty->port)) { + pr_err("VCC: hangup: TTY port not found\n"); + vcc_put(port, false); + return; + } + + vcc_ldc_hup(port); + + vcc_put(port, false); + + tty_port_hangup(tty->port); +} + static const struct tty_operations vcc_ops = { .open = vcc_open, .close = vcc_close, + .hangup = vcc_hangup, }; #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) -- cgit v1.2.3-59-g8ed1b From cf0452600b6dbf8bf23b6f7884c7937a0ef60017 Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:03:06 -0400 Subject: sparc64: vcc: Add write & write_room TTY operations Add handlers to support TTY write & write_room operations Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 3 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 67be5a57c1b5..0c0fe228546b 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -844,10 +844,105 @@ static void vcc_hangup(struct tty_struct *tty) tty_port_hangup(tty->port); } +static int vcc_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + struct vcc_port *port; + struct vio_vcc *pkt; + unsigned long flags; + int total_sent = 0; + int tosend = 0; + int rv = -EINVAL; + + if (unlikely(!tty)) { + pr_err("VCC: write: Invalid TTY handle\n"); + return -ENXIO; + } + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: write: Failed to find VCC port"); + return -ENODEV; + } + + spin_lock_irqsave(&port->lock, flags); + + pkt = &port->buffer; + pkt->tag.type = VIO_TYPE_DATA; + + while (count > 0) { + /* Minimum of data to write and space available */ + tosend = min(count, (VCC_BUFF_LEN - port->chars_in_buffer)); + + if (!tosend) + break; + + memcpy(&pkt->data[port->chars_in_buffer], &buf[total_sent], + tosend); + port->chars_in_buffer += tosend; + pkt->tag.stype = tosend; + + vccdbg("TAG [%02x:%02x:%04x:%08x]\n", pkt->tag.type, + pkt->tag.stype, pkt->tag.stype_env, pkt->tag.sid); + vccdbg("DATA [%s]\n", pkt->data); + vccdbgl(port->vio.lp); + + /* Since we know we have enough room in VCC buffer for tosend + * we record that it was sent regardless of whether the + * hypervisor actually took it because we have it buffered. + */ + rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); + vccdbg("VCC: write: ldc_write(%d)=%d\n", + (VIO_TAG_SIZE + tosend), rv); + + total_sent += tosend; + count -= tosend; + if (rv < 0) { + vcc_kick_tx(port); + break; + } + + port->chars_in_buffer = 0; + } + + spin_unlock_irqrestore(&port->lock, flags); + + vcc_put(port, false); + + vccdbg("VCC: write: total=%d rv=%d", total_sent, rv); + + return total_sent ? total_sent : rv; +} + +static int vcc_write_room(struct tty_struct *tty) +{ + struct vcc_port *port; + u64 num; + + if (unlikely(!tty)) { + pr_err("VCC: write_room: Invalid TTY handle\n"); + return -ENXIO; + } + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: write_room: Failed to find VCC port\n"); + return -ENODEV; + } + + num = VCC_BUFF_LEN - port->chars_in_buffer; + + vcc_put(port, false); + + return num; +} + static const struct tty_operations vcc_ops = { - .open = vcc_open, - .close = vcc_close, - .hangup = vcc_hangup, + .open = vcc_open, + .close = vcc_close, + .hangup = vcc_hangup, + .write = vcc_write, + .write_room = vcc_write_room, }; #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) -- cgit v1.2.3-59-g8ed1b From aeee7debf30b43b276f8aad2e889cd50dd3f0945 Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:03:07 -0400 Subject: sparc64: vcc: Add chars_in_buffer TTY operation Add handler to support TTY chars_in_buffer operation Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 0c0fe228546b..070c54188971 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -937,12 +937,36 @@ static int vcc_write_room(struct tty_struct *tty) return num; } +static int vcc_chars_in_buffer(struct tty_struct *tty) +{ + struct vcc_port *port; + u64 num; + + if (unlikely(!tty)) { + pr_err("VCC: chars_in_buffer: Invalid TTY handle\n"); + return -ENXIO; + } + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: chars_in_buffer: Failed to find VCC port\n"); + return -ENODEV; + } + + num = port->chars_in_buffer; + + vcc_put(port, false); + + return num; +} + static const struct tty_operations vcc_ops = { - .open = vcc_open, - .close = vcc_close, - .hangup = vcc_hangup, - .write = vcc_write, - .write_room = vcc_write_room, + .open = vcc_open, + .close = vcc_close, + .hangup = vcc_hangup, + .write = vcc_write, + .write_room = vcc_write_room, + .chars_in_buffer = vcc_chars_in_buffer, }; #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) -- cgit v1.2.3-59-g8ed1b From e942e5775c87aee2b21179aa45e78b81e062b246 Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:03:08 -0400 Subject: sparc64: vcc: Add break_ctl TTY operation Add handler to support TTY break_ctl operation Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 070c54188971..f913217159ee 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -960,6 +960,40 @@ static int vcc_chars_in_buffer(struct tty_struct *tty) return num; } +static int vcc_break_ctl(struct tty_struct *tty, int state) +{ + struct vcc_port *port; + unsigned long flags; + + if (unlikely(!tty)) { + pr_err("VCC: break_ctl: Invalid TTY handle\n"); + return -ENXIO; + } + + port = vcc_get_ne(tty->index); + if (unlikely(!port)) { + pr_err("VCC: break_ctl: Failed to find VCC port\n"); + return -ENODEV; + } + + /* Turn off break */ + if (state == 0) { + vcc_put(port, false); + return 0; + } + + spin_lock_irqsave(&port->lock, flags); + + if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) + vcc_kick_tx(port); + + spin_unlock_irqrestore(&port->lock, flags); + + vcc_put(port, false); + + return 0; +} + static const struct tty_operations vcc_ops = { .open = vcc_open, .close = vcc_close, @@ -967,6 +1001,7 @@ static const struct tty_operations vcc_ops = { .write = vcc_write, .write_room = vcc_write_room, .chars_in_buffer = vcc_chars_in_buffer, + .break_ctl = vcc_break_ctl, }; #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) -- cgit v1.2.3-59-g8ed1b From 8f03f948ba356b7cb254b45a4e78b5b3d86fd3bb Mon Sep 17 00:00:00 2001 From: Jag Raman Date: Tue, 15 Aug 2017 17:03:09 -0400 Subject: sparc64: vcc: Add install & cleanup TTY operations Add handlers to support TTY install & cleanup operations Signed-off-by: Jagannathan Raman Reviewed-by: Liam Merwick Reviewed-by: Shannon Nelson Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index f913217159ee..5e608173b091 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -487,6 +487,8 @@ static struct vio_version vcc_versions[] = { { .major = 1, .minor = 0 }, }; +static struct tty_port_operations vcc_port_ops = { 0 }; + static ssize_t vcc_sysfs_domain_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -994,6 +996,75 @@ static int vcc_break_ctl(struct tty_struct *tty, int state) return 0; } +static int vcc_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct vcc_port *port_vcc; + struct tty_port *port_tty; + int ret; + + if (unlikely(!tty)) { + pr_err("VCC: install: Invalid TTY handle\n"); + return -ENXIO; + } + + if (tty->index >= VCC_MAX_PORTS) + return -EINVAL; + + ret = tty_standard_install(driver, tty); + if (ret) + return ret; + + port_tty = kzalloc(sizeof(struct tty_port), GFP_KERNEL); + if (!port_tty) + return -ENOMEM; + + port_vcc = vcc_get(tty->index, true); + if (!port_vcc) { + pr_err("VCC: install: Failed to find VCC port\n"); + tty->port = NULL; + kfree(port_tty); + return -ENODEV; + } + + tty_port_init(port_tty); + port_tty->ops = &vcc_port_ops; + tty->port = port_tty; + + port_vcc->tty = tty; + + vcc_put(port_vcc, true); + + return 0; +} + +static void vcc_cleanup(struct tty_struct *tty) +{ + struct vcc_port *port; + + if (unlikely(!tty)) { + pr_err("VCC: cleanup: Invalid TTY handle\n"); + return; + } + + port = vcc_get(tty->index, true); + if (port) { + port->tty = NULL; + + if (port->removed) { + vcc_table_remove(tty->index); + kfree(port->vio.name); + kfree(port->domain); + kfree(port); + } else { + vcc_put(port, true); + } + } + + tty_port_destroy(tty->port); + kfree(tty->port); + tty->port = NULL; +} + static const struct tty_operations vcc_ops = { .open = vcc_open, .close = vcc_close, @@ -1002,6 +1073,8 @@ static const struct tty_operations vcc_ops = { .write_room = vcc_write_room, .chars_in_buffer = vcc_chars_in_buffer, .break_ctl = vcc_break_ctl, + .install = vcc_install, + .cleanup = vcc_cleanup, }; #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) -- cgit v1.2.3-59-g8ed1b From c6b4ee9eba97b64750d5e8be31c04b70592592b7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 26 Aug 2017 09:12:05 +0300 Subject: sparc64: vcc: Check for IS_ERR() instead of NULL The tty_alloc_driver() function never returns NULL, it returns error pointers on error. Fixes: ce808b746325 ("sparc64: vcc: TTY driver initialization and cleanup") Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 5e608173b091..716729129e01 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -1086,9 +1086,9 @@ static int vcc_tty_init(void) pr_info("VCC: %s\n", version); vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS); - if (!vcc_tty_driver) { + if (IS_ERR(vcc_tty_driver)) { pr_err("VCC: TTY driver alloc failed\n"); - return -ENOMEM; + return PTR_ERR(vcc_tty_driver); } vcc_tty_driver->driver_name = vcc_driver_name; -- cgit v1.2.3-59-g8ed1b From 5bd0ea9107dca975dc4ba4d9de39b4938d2cb36d Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Mon, 28 Aug 2017 23:58:44 +0530 Subject: sparc64: vcc: make ktermios const Make this const as it is not modified anywhere. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- drivers/tty/vcc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/tty') diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c index 716729129e01..ef01d24858cd 100644 --- a/drivers/tty/vcc.c +++ b/drivers/tty/vcc.c @@ -102,7 +102,7 @@ module_param(vcc_dbg_vio, uint, 0664); * 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 vcc_tty_termios = { +static const struct ktermios vcc_tty_termios = { .c_iflag = IGNBRK | IGNPAR, .c_oflag = OPOST, .c_cflag = B38400 | CS8 | CREAD | HUPCL, -- cgit v1.2.3-59-g8ed1b