1f16aa97dSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 255bd2133SJag Raman /* vcc.c: sun4v virtual channel concentrator 355bd2133SJag Raman * 455bd2133SJag Raman * Copyright (C) 2017 Oracle. All rights reserved. 555bd2133SJag Raman */ 655bd2133SJag Raman 75d171050SJag Raman #include <linux/delay.h> 85d171050SJag Raman #include <linux/interrupt.h> 955bd2133SJag Raman #include <linux/module.h> 105d171050SJag Raman #include <linux/slab.h> 118868e44aSJag Raman #include <linux/sysfs.h> 12ce808b74SJag Raman #include <linux/tty.h> 13f8c55335SJag Raman #include <linux/tty_flip.h> 14c9874d3fSAl Viro #include <linux/termios_internal.h> 155d171050SJag Raman #include <asm/vio.h> 165d171050SJag Raman #include <asm/ldc.h> 1755bd2133SJag Raman 18f283ebd5SJag Raman MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver"); 19f283ebd5SJag Raman MODULE_LICENSE("GPL"); 2073300191SJiri Slaby MODULE_VERSION("1.1"); 21f283ebd5SJag Raman 225d171050SJag Raman struct vcc_port { 235d171050SJag Raman struct vio_driver_state vio; 245d171050SJag Raman 255d171050SJag Raman spinlock_t lock; 265d171050SJag Raman char *domain; 275d171050SJag Raman struct tty_struct *tty; /* only populated while dev is open */ 285d171050SJag Raman unsigned long index; /* index into the vcc_table */ 295d171050SJag Raman 305d171050SJag Raman u64 refcnt; 315d171050SJag Raman bool excl_locked; 325d171050SJag Raman 335d171050SJag Raman bool removed; 345d171050SJag Raman 355d171050SJag Raman /* This buffer is required to support the tty write_room interface 365d171050SJag Raman * and guarantee that any characters that the driver accepts will 375d171050SJag Raman * be eventually sent, either immediately or later. 385d171050SJag Raman */ 39cfc7c12bSJiri Slaby (SUSE) size_t chars_in_buffer; 405d171050SJag Raman struct vio_vcc buffer; 415d171050SJag Raman 425d171050SJag Raman struct timer_list rx_timer; 435d171050SJag Raman struct timer_list tx_timer; 445d171050SJag Raman }; 455d171050SJag Raman 465d171050SJag Raman /* Microseconds that thread will delay waiting for a vcc port ref */ 475d171050SJag Raman #define VCC_REF_DELAY 100 485d171050SJag Raman 49ce808b74SJag Raman #define VCC_MAX_PORTS 1024 50ce808b74SJag Raman #define VCC_MINOR_START 0 /* must be zero */ 51f8c55335SJag Raman #define VCC_BUFF_LEN VIO_VCC_MTU_SIZE 52ce808b74SJag Raman 538868e44aSJag Raman #define VCC_CTL_BREAK -1 548868e44aSJag Raman #define VCC_CTL_HUP -2 558868e44aSJag Raman 56ce808b74SJag Raman static struct tty_driver *vcc_tty_driver; 57ce808b74SJag Raman 585d171050SJag Raman static struct vcc_port *vcc_table[VCC_MAX_PORTS]; 595d171050SJag Raman static DEFINE_SPINLOCK(vcc_table_lock); 605d171050SJag Raman 614c472fc0SJiri Slaby static unsigned int vcc_dbg; 624c472fc0SJiri Slaby static unsigned int vcc_dbg_ldc; 634c472fc0SJiri Slaby static unsigned int vcc_dbg_vio; 64f283ebd5SJag Raman 65f283ebd5SJag Raman module_param(vcc_dbg, uint, 0664); 66f283ebd5SJag Raman module_param(vcc_dbg_ldc, uint, 0664); 67f283ebd5SJag Raman module_param(vcc_dbg_vio, uint, 0664); 68f283ebd5SJag Raman 69f283ebd5SJag Raman #define VCC_DBG_DRV 0x1 70f283ebd5SJag Raman #define VCC_DBG_LDC 0x2 71f283ebd5SJag Raman #define VCC_DBG_PKT 0x4 72f283ebd5SJag Raman 73f283ebd5SJag Raman #define vccdbg(f, a...) \ 74f283ebd5SJag Raman do { \ 75f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_DRV) \ 76f283ebd5SJag Raman pr_info(f, ## a); \ 77f283ebd5SJag Raman } while (0) \ 78f283ebd5SJag Raman 79f283ebd5SJag Raman #define vccdbgl(l) \ 80f283ebd5SJag Raman do { \ 81f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_LDC) \ 82f283ebd5SJag Raman ldc_print(l); \ 83f283ebd5SJag Raman } while (0) \ 84f283ebd5SJag Raman 85f283ebd5SJag Raman #define vccdbgp(pkt) \ 86f283ebd5SJag Raman do { \ 87f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_PKT) { \ 88f283ebd5SJag Raman int i; \ 89f283ebd5SJag Raman for (i = 0; i < pkt.tag.stype; i++) \ 90f283ebd5SJag Raman pr_info("[%c]", pkt.data[i]); \ 91f283ebd5SJag Raman } \ 92f283ebd5SJag Raman } while (0) \ 93f283ebd5SJag Raman 94ce808b74SJag Raman /* Note: Be careful when adding flags to this line discipline. Don't 95ce808b74SJag Raman * add anything that will cause echoing or we'll go into recursive 96ce808b74SJag Raman * loop echoing chars back and forth with the console drivers. 97ce808b74SJag Raman */ 985bd0ea91SBhumika Goyal static const struct ktermios vcc_tty_termios = { 99ce808b74SJag Raman .c_iflag = IGNBRK | IGNPAR, 100ce808b74SJag Raman .c_oflag = OPOST, 101ce808b74SJag Raman .c_cflag = B38400 | CS8 | CREAD | HUPCL, 102ce808b74SJag Raman .c_cc = INIT_C_CC, 103ce808b74SJag Raman .c_ispeed = 38400, 104ce808b74SJag Raman .c_ospeed = 38400 105ce808b74SJag Raman }; 106ce808b74SJag Raman 1075d171050SJag Raman /** 1085d171050SJag Raman * vcc_table_add() - Add VCC port to the VCC table 1095d171050SJag Raman * @port: pointer to the VCC port 1105d171050SJag Raman * 1115d171050SJag Raman * Return: index of the port in the VCC table on success, 1125d171050SJag Raman * -1 on failure 1135d171050SJag Raman */ 1145d171050SJag Raman static int vcc_table_add(struct vcc_port *port) 1155d171050SJag Raman { 1165d171050SJag Raman unsigned long flags; 1175d171050SJag Raman int i; 1185d171050SJag Raman 1195d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1205d171050SJag Raman for (i = VCC_MINOR_START; i < VCC_MAX_PORTS; i++) { 1215d171050SJag Raman if (!vcc_table[i]) { 1225d171050SJag Raman vcc_table[i] = port; 1235d171050SJag Raman break; 1245d171050SJag Raman } 1255d171050SJag Raman } 1265d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1275d171050SJag Raman 1285d171050SJag Raman if (i < VCC_MAX_PORTS) 1295d171050SJag Raman return i; 1305d171050SJag Raman else 1315d171050SJag Raman return -1; 1325d171050SJag Raman } 1335d171050SJag Raman 1345d171050SJag Raman /** 1355d171050SJag Raman * vcc_table_remove() - Removes a VCC port from the VCC table 1365d171050SJag Raman * @index: Index into the VCC table 1375d171050SJag Raman */ 1385d171050SJag Raman static void vcc_table_remove(unsigned long index) 1395d171050SJag Raman { 1405d171050SJag Raman unsigned long flags; 1415d171050SJag Raman 1425d171050SJag Raman if (WARN_ON(index >= VCC_MAX_PORTS)) 1435d171050SJag Raman return; 1445d171050SJag Raman 1455d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1465d171050SJag Raman vcc_table[index] = NULL; 1475d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1485d171050SJag Raman } 1495d171050SJag Raman 1505d171050SJag Raman /** 1515d171050SJag Raman * vcc_get() - Gets a reference to VCC port 1525d171050SJag Raman * @index: Index into the VCC table 1535d171050SJag Raman * @excl: Indicates if an exclusive access is requested 1545d171050SJag Raman * 1555d171050SJag Raman * Return: reference to the VCC port, if found 1565d171050SJag Raman * NULL, if port not found 1575d171050SJag Raman */ 1585d171050SJag Raman static struct vcc_port *vcc_get(unsigned long index, bool excl) 1595d171050SJag Raman { 1605d171050SJag Raman struct vcc_port *port; 1615d171050SJag Raman unsigned long flags; 1625d171050SJag Raman 1635d171050SJag Raman try_again: 1645d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1655d171050SJag Raman 1665d171050SJag Raman port = vcc_table[index]; 1675d171050SJag Raman if (!port) { 1685d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1695d171050SJag Raman return NULL; 1705d171050SJag Raman } 1715d171050SJag Raman 1725d171050SJag Raman if (!excl) { 1735d171050SJag Raman if (port->excl_locked) { 1745d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1755d171050SJag Raman udelay(VCC_REF_DELAY); 1765d171050SJag Raman goto try_again; 1775d171050SJag Raman } 1785d171050SJag Raman port->refcnt++; 1795d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1805d171050SJag Raman return port; 1815d171050SJag Raman } 1825d171050SJag Raman 1835d171050SJag Raman if (port->refcnt) { 1845d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1855d171050SJag Raman /* Threads wanting exclusive access will wait half the time, 1865d171050SJag Raman * probably giving them higher priority in the case of 1875d171050SJag Raman * multiple waiters. 1885d171050SJag Raman */ 1895d171050SJag Raman udelay(VCC_REF_DELAY/2); 1905d171050SJag Raman goto try_again; 1915d171050SJag Raman } 1925d171050SJag Raman 1935d171050SJag Raman port->refcnt++; 1945d171050SJag Raman port->excl_locked = true; 1955d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1965d171050SJag Raman 1975d171050SJag Raman return port; 1985d171050SJag Raman } 1995d171050SJag Raman 2005d171050SJag Raman /** 2015d171050SJag Raman * vcc_put() - Returns a reference to VCC port 2025d171050SJag Raman * @port: pointer to VCC port 2035d171050SJag Raman * @excl: Indicates if the returned reference is an exclusive reference 2045d171050SJag Raman * 2055d171050SJag Raman * Note: It's the caller's responsibility to ensure the correct value 2065d171050SJag Raman * for the excl flag 2075d171050SJag Raman */ 2085d171050SJag Raman static void vcc_put(struct vcc_port *port, bool excl) 2095d171050SJag Raman { 2105d171050SJag Raman unsigned long flags; 2115d171050SJag Raman 2125d171050SJag Raman if (!port) 2135d171050SJag Raman return; 2145d171050SJag Raman 2155d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 2165d171050SJag Raman 2175d171050SJag Raman /* check if caller attempted to put with the wrong flags */ 2185d171050SJag Raman if (WARN_ON((excl && !port->excl_locked) || 2195d171050SJag Raman (!excl && port->excl_locked))) 2205d171050SJag Raman goto done; 2215d171050SJag Raman 2225d171050SJag Raman port->refcnt--; 2235d171050SJag Raman 2245d171050SJag Raman if (excl) 2255d171050SJag Raman port->excl_locked = false; 2265d171050SJag Raman 2275d171050SJag Raman done: 2285d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 2295d171050SJag Raman } 2305d171050SJag Raman 2315d171050SJag Raman /** 2325d171050SJag Raman * vcc_get_ne() - Get a non-exclusive reference to VCC port 2335d171050SJag Raman * @index: Index into the VCC table 2345d171050SJag Raman * 2355d171050SJag Raman * Gets a non-exclusive reference to VCC port, if it's not removed 2365d171050SJag Raman * 2375d171050SJag Raman * Return: pointer to the VCC port, if found 2385d171050SJag Raman * NULL, if port not found 2395d171050SJag Raman */ 2405d171050SJag Raman static struct vcc_port *vcc_get_ne(unsigned long index) 2415d171050SJag Raman { 2425d171050SJag Raman struct vcc_port *port; 2435d171050SJag Raman 2445d171050SJag Raman port = vcc_get(index, false); 2455d171050SJag Raman 2465d171050SJag Raman if (port && port->removed) { 2475d171050SJag Raman vcc_put(port, false); 2485d171050SJag Raman return NULL; 2495d171050SJag Raman } 2505d171050SJag Raman 2515d171050SJag Raman return port; 2525d171050SJag Raman } 2535d171050SJag Raman 254f8c55335SJag Raman static void vcc_kick_rx(struct vcc_port *port) 255f8c55335SJag Raman { 256f8c55335SJag Raman struct vio_driver_state *vio = &port->vio; 257f8c55335SJag Raman 258f8c55335SJag Raman assert_spin_locked(&port->lock); 259f8c55335SJag Raman 260f8c55335SJag Raman if (!timer_pending(&port->rx_timer) && !port->removed) { 261f8c55335SJag Raman disable_irq_nosync(vio->vdev->rx_irq); 262f8c55335SJag Raman port->rx_timer.expires = (jiffies + 1); 263f8c55335SJag Raman add_timer(&port->rx_timer); 264f8c55335SJag Raman } 265f8c55335SJag Raman } 266f8c55335SJag Raman 267f8c55335SJag Raman static void vcc_kick_tx(struct vcc_port *port) 268f8c55335SJag Raman { 269f8c55335SJag Raman assert_spin_locked(&port->lock); 270f8c55335SJag Raman 271f8c55335SJag Raman if (!timer_pending(&port->tx_timer) && !port->removed) { 272f8c55335SJag Raman port->tx_timer.expires = (jiffies + 1); 273f8c55335SJag Raman add_timer(&port->tx_timer); 274f8c55335SJag Raman } 275f8c55335SJag Raman } 276f8c55335SJag Raman 277f8c55335SJag Raman static int vcc_rx_check(struct tty_struct *tty, int size) 278f8c55335SJag Raman { 279f8c55335SJag Raman if (WARN_ON(!tty || !tty->port)) 280f8c55335SJag Raman return 1; 281f8c55335SJag Raman 282f8c55335SJag Raman /* tty_buffer_request_room won't sleep because it uses 283f8c55335SJag Raman * GFP_ATOMIC flag to allocate buffer 284f8c55335SJag Raman */ 285f8c55335SJag Raman if (test_bit(TTY_THROTTLED, &tty->flags) || 286f8c55335SJag Raman (tty_buffer_request_room(tty->port, VCC_BUFF_LEN) < VCC_BUFF_LEN)) 287f8c55335SJag Raman return 0; 288f8c55335SJag Raman 289f8c55335SJag Raman return 1; 290f8c55335SJag Raman } 291f8c55335SJag Raman 292f8c55335SJag Raman static int vcc_rx(struct tty_struct *tty, char *buf, int size) 293f8c55335SJag Raman { 294f8c55335SJag Raman int len = 0; 295f8c55335SJag Raman 296f8c55335SJag Raman if (WARN_ON(!tty || !tty->port)) 297f8c55335SJag Raman return len; 298f8c55335SJag Raman 299f8c55335SJag Raman len = tty_insert_flip_string(tty->port, buf, size); 300f8c55335SJag Raman if (len) 301f8c55335SJag Raman tty_flip_buffer_push(tty->port); 302f8c55335SJag Raman 303f8c55335SJag Raman return len; 304f8c55335SJag Raman } 305f8c55335SJag Raman 306f8c55335SJag Raman static int vcc_ldc_read(struct vcc_port *port) 307f8c55335SJag Raman { 308f8c55335SJag Raman struct vio_driver_state *vio = &port->vio; 309f8c55335SJag Raman struct tty_struct *tty; 310f8c55335SJag Raman struct vio_vcc pkt; 311f8c55335SJag Raman int rv = 0; 312f8c55335SJag Raman 313f8c55335SJag Raman tty = port->tty; 314f8c55335SJag Raman if (!tty) { 315f8c55335SJag Raman rv = ldc_rx_reset(vio->lp); 316f8c55335SJag Raman vccdbg("VCC: reset rx q: rv=%d\n", rv); 317f8c55335SJag Raman goto done; 318f8c55335SJag Raman } 319f8c55335SJag Raman 320f8c55335SJag Raman /* Read as long as LDC has incoming data. */ 321f8c55335SJag Raman while (1) { 322f8c55335SJag Raman if (!vcc_rx_check(tty, VIO_VCC_MTU_SIZE)) { 323f8c55335SJag Raman vcc_kick_rx(port); 324f8c55335SJag Raman break; 325f8c55335SJag Raman } 326f8c55335SJag Raman 327f8c55335SJag Raman vccdbgl(vio->lp); 328f8c55335SJag Raman 329f8c55335SJag Raman rv = ldc_read(vio->lp, &pkt, sizeof(pkt)); 330f8c55335SJag Raman if (rv <= 0) 331f8c55335SJag Raman break; 332f8c55335SJag Raman 333f8c55335SJag Raman vccdbg("VCC: ldc_read()=%d\n", rv); 334f8c55335SJag Raman vccdbg("TAG [%02x:%02x:%04x:%08x]\n", 335f8c55335SJag Raman pkt.tag.type, pkt.tag.stype, 336f8c55335SJag Raman pkt.tag.stype_env, pkt.tag.sid); 337f8c55335SJag Raman 338f8c55335SJag Raman if (pkt.tag.type == VIO_TYPE_DATA) { 339f8c55335SJag Raman vccdbgp(pkt); 340f8c55335SJag Raman /* vcc_rx_check ensures memory availability */ 341f8c55335SJag Raman vcc_rx(tty, pkt.data, pkt.tag.stype); 342f8c55335SJag Raman } else { 343f8c55335SJag Raman pr_err("VCC: unknown msg [%02x:%02x:%04x:%08x]\n", 344f8c55335SJag Raman pkt.tag.type, pkt.tag.stype, 345f8c55335SJag Raman pkt.tag.stype_env, pkt.tag.sid); 346f8c55335SJag Raman rv = -ECONNRESET; 347f8c55335SJag Raman break; 348f8c55335SJag Raman } 349f8c55335SJag Raman 350f8c55335SJag Raman WARN_ON(rv != LDC_PACKET_SIZE); 351f8c55335SJag Raman } 352f8c55335SJag Raman 353f8c55335SJag Raman done: 354f8c55335SJag Raman return rv; 355f8c55335SJag Raman } 356f8c55335SJag Raman 3574790b6dcSKees Cook static void vcc_rx_timer(struct timer_list *t) 358f8c55335SJag Raman { 3594790b6dcSKees Cook struct vcc_port *port = from_timer(port, t, rx_timer); 360f8c55335SJag Raman struct vio_driver_state *vio; 361f8c55335SJag Raman unsigned long flags; 362f8c55335SJag Raman int rv; 363f8c55335SJag Raman 364f8c55335SJag Raman spin_lock_irqsave(&port->lock, flags); 365f8c55335SJag Raman port->rx_timer.expires = 0; 366f8c55335SJag Raman 367f8c55335SJag Raman vio = &port->vio; 368f8c55335SJag Raman 369f8c55335SJag Raman enable_irq(vio->vdev->rx_irq); 370f8c55335SJag Raman 371f8c55335SJag Raman if (!port->tty || port->removed) 372f8c55335SJag Raman goto done; 373f8c55335SJag Raman 374f8c55335SJag Raman rv = vcc_ldc_read(port); 375f8c55335SJag Raman if (rv == -ECONNRESET) 376f8c55335SJag Raman vio_conn_reset(vio); 377f8c55335SJag Raman 378f8c55335SJag Raman done: 379f8c55335SJag Raman spin_unlock_irqrestore(&port->lock, flags); 380f8c55335SJag Raman vcc_put(port, false); 381f8c55335SJag Raman } 382f8c55335SJag Raman 3834790b6dcSKees Cook static void vcc_tx_timer(struct timer_list *t) 384f8c55335SJag Raman { 3854790b6dcSKees Cook struct vcc_port *port = from_timer(port, t, tx_timer); 386f8c55335SJag Raman struct vio_vcc *pkt; 387f8c55335SJag Raman unsigned long flags; 388cfc7c12bSJiri Slaby (SUSE) size_t tosend = 0; 389f8c55335SJag Raman int rv; 390f8c55335SJag Raman 391f8c55335SJag Raman spin_lock_irqsave(&port->lock, flags); 392f8c55335SJag Raman port->tx_timer.expires = 0; 393f8c55335SJag Raman 394f8c55335SJag Raman if (!port->tty || port->removed) 395f8c55335SJag Raman goto done; 396f8c55335SJag Raman 397f8c55335SJag Raman tosend = min(VCC_BUFF_LEN, port->chars_in_buffer); 398f8c55335SJag Raman if (!tosend) 399f8c55335SJag Raman goto done; 400f8c55335SJag Raman 401f8c55335SJag Raman pkt = &port->buffer; 402f8c55335SJag Raman pkt->tag.type = VIO_TYPE_DATA; 403f8c55335SJag Raman pkt->tag.stype = tosend; 404f8c55335SJag Raman vccdbgl(port->vio.lp); 405f8c55335SJag Raman 406f8c55335SJag Raman rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); 407f8c55335SJag Raman WARN_ON(!rv); 408f8c55335SJag Raman 409f8c55335SJag Raman if (rv < 0) { 410f8c55335SJag Raman vccdbg("VCC: ldc_write()=%d\n", rv); 411f8c55335SJag Raman vcc_kick_tx(port); 412f8c55335SJag Raman } else { 413f8c55335SJag Raman struct tty_struct *tty = port->tty; 414f8c55335SJag Raman 415f8c55335SJag Raman port->chars_in_buffer = 0; 416f8c55335SJag Raman if (tty) 417f8c55335SJag Raman tty_wakeup(tty); 418f8c55335SJag Raman } 419f8c55335SJag Raman 420f8c55335SJag Raman done: 421f8c55335SJag Raman spin_unlock_irqrestore(&port->lock, flags); 422f8c55335SJag Raman vcc_put(port, false); 423f8c55335SJag Raman } 424f8c55335SJag Raman 425103b9b0bSJag Raman /** 426103b9b0bSJag Raman * vcc_event() - LDC event processing engine 427103b9b0bSJag Raman * @arg: VCC private data 428103b9b0bSJag Raman * @event: LDC event 429103b9b0bSJag Raman * 430103b9b0bSJag Raman * Handles LDC events for VCC 431103b9b0bSJag Raman */ 4325d171050SJag Raman static void vcc_event(void *arg, int event) 4335d171050SJag Raman { 434103b9b0bSJag Raman struct vio_driver_state *vio; 435103b9b0bSJag Raman struct vcc_port *port; 436103b9b0bSJag Raman unsigned long flags; 437103b9b0bSJag Raman int rv; 438103b9b0bSJag Raman 439103b9b0bSJag Raman port = arg; 440103b9b0bSJag Raman vio = &port->vio; 441103b9b0bSJag Raman 442103b9b0bSJag Raman spin_lock_irqsave(&port->lock, flags); 443103b9b0bSJag Raman 444103b9b0bSJag Raman switch (event) { 445103b9b0bSJag Raman case LDC_EVENT_RESET: 446103b9b0bSJag Raman case LDC_EVENT_UP: 447103b9b0bSJag Raman vio_link_state_change(vio, event); 448103b9b0bSJag Raman break; 449103b9b0bSJag Raman 450103b9b0bSJag Raman case LDC_EVENT_DATA_READY: 451103b9b0bSJag Raman rv = vcc_ldc_read(port); 452103b9b0bSJag Raman if (rv == -ECONNRESET) 453103b9b0bSJag Raman vio_conn_reset(vio); 454103b9b0bSJag Raman break; 455103b9b0bSJag Raman 456103b9b0bSJag Raman default: 457103b9b0bSJag Raman pr_err("VCC: unexpected LDC event(%d)\n", event); 458103b9b0bSJag Raman } 459103b9b0bSJag Raman 460103b9b0bSJag Raman spin_unlock_irqrestore(&port->lock, flags); 4615d171050SJag Raman } 4625d171050SJag Raman 4635d171050SJag Raman static struct ldc_channel_config vcc_ldc_cfg = { 4645d171050SJag Raman .event = vcc_event, 4655d171050SJag Raman .mtu = VIO_VCC_MTU_SIZE, 4665d171050SJag Raman .mode = LDC_MODE_RAW, 4675d171050SJag Raman .debug = 0, 4685d171050SJag Raman }; 4695d171050SJag Raman 4705d171050SJag Raman /* Ordered from largest major to lowest */ 4715d171050SJag Raman static struct vio_version vcc_versions[] = { 4725d171050SJag Raman { .major = 1, .minor = 0 }, 4735d171050SJag Raman }; 4745d171050SJag Raman 4758f03f948SJag Raman static struct tty_port_operations vcc_port_ops = { 0 }; 4768f03f948SJag Raman 4772877389fSZhen Lei static ssize_t domain_show(struct device *dev, 4788868e44aSJag Raman struct device_attribute *attr, 4798868e44aSJag Raman char *buf) 4808868e44aSJag Raman { 4818868e44aSJag Raman struct vcc_port *port; 4828868e44aSJag Raman int rv; 4838868e44aSJag Raman 4848868e44aSJag Raman port = dev_get_drvdata(dev); 4858868e44aSJag Raman if (!port) 4868868e44aSJag Raman return -ENODEV; 4878868e44aSJag Raman 4888868e44aSJag Raman rv = scnprintf(buf, PAGE_SIZE, "%s\n", port->domain); 4898868e44aSJag Raman 4908868e44aSJag Raman return rv; 4918868e44aSJag Raman } 4928868e44aSJag Raman 4938868e44aSJag Raman static int vcc_send_ctl(struct vcc_port *port, int ctl) 4948868e44aSJag Raman { 4958868e44aSJag Raman struct vio_vcc pkt; 4968868e44aSJag Raman int rv; 4978868e44aSJag Raman 4988868e44aSJag Raman pkt.tag.type = VIO_TYPE_CTRL; 4998868e44aSJag Raman pkt.tag.sid = ctl; 5008868e44aSJag Raman pkt.tag.stype = 0; 5018868e44aSJag Raman 5028868e44aSJag Raman rv = ldc_write(port->vio.lp, &pkt, sizeof(pkt.tag)); 5038868e44aSJag Raman WARN_ON(!rv); 5048868e44aSJag Raman vccdbg("VCC: ldc_write(%ld)=%d\n", sizeof(pkt.tag), rv); 5058868e44aSJag Raman 5068868e44aSJag Raman return rv; 5078868e44aSJag Raman } 5088868e44aSJag Raman 5092877389fSZhen Lei static ssize_t break_store(struct device *dev, 5108868e44aSJag Raman struct device_attribute *attr, 5118868e44aSJag Raman const char *buf, size_t count) 5128868e44aSJag Raman { 5138868e44aSJag Raman struct vcc_port *port; 5148868e44aSJag Raman unsigned long flags; 5158868e44aSJag Raman int rv = count; 5168868e44aSJag Raman int brk; 5178868e44aSJag Raman 5188868e44aSJag Raman port = dev_get_drvdata(dev); 5198868e44aSJag Raman if (!port) 5208868e44aSJag Raman return -ENODEV; 5218868e44aSJag Raman 5228868e44aSJag Raman spin_lock_irqsave(&port->lock, flags); 5238868e44aSJag Raman 5248868e44aSJag Raman if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) 5258868e44aSJag Raman rv = -EINVAL; 5268868e44aSJag Raman else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) 527f8c55335SJag Raman vcc_kick_tx(port); 5288868e44aSJag Raman 5298868e44aSJag Raman spin_unlock_irqrestore(&port->lock, flags); 5308868e44aSJag Raman 5318868e44aSJag Raman return rv; 5328868e44aSJag Raman } 5338868e44aSJag Raman 5342877389fSZhen Lei static DEVICE_ATTR_ADMIN_RO(domain); 5352877389fSZhen Lei static DEVICE_ATTR_WO(break); 5368868e44aSJag Raman 5378868e44aSJag Raman static struct attribute *vcc_sysfs_entries[] = { 5388868e44aSJag Raman &dev_attr_domain.attr, 5398868e44aSJag Raman &dev_attr_break.attr, 5408868e44aSJag Raman NULL 5418868e44aSJag Raman }; 5428868e44aSJag Raman 5438868e44aSJag Raman static struct attribute_group vcc_attribute_group = { 5448868e44aSJag Raman .name = NULL, 5458868e44aSJag Raman .attrs = vcc_sysfs_entries, 5468868e44aSJag Raman }; 5478868e44aSJag Raman 5485d171050SJag Raman /** 5495d171050SJag Raman * vcc_probe() - Initialize VCC port 5505d171050SJag Raman * @vdev: Pointer to VIO device of the new VCC port 5515d171050SJag Raman * @id: VIO device ID 5525d171050SJag Raman * 5535d171050SJag Raman * Initializes a VCC port to receive serial console data from 5545d171050SJag Raman * the guest domain. Sets up a TTY end point on the control 5555d171050SJag Raman * domain. Sets up VIO/LDC link between the guest & control 5565d171050SJag Raman * domain endpoints. 5575d171050SJag Raman * 5585d171050SJag Raman * Return: status of the probe 5595d171050SJag Raman */ 5605d171050SJag Raman static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) 5615d171050SJag Raman { 5625d171050SJag Raman struct mdesc_handle *hp; 5635d171050SJag Raman struct vcc_port *port; 5645d171050SJag Raman struct device *dev; 5655d171050SJag Raman const char *domain; 5665d171050SJag Raman char *name; 5675d171050SJag Raman u64 node; 5685d171050SJag Raman int rv; 5695d171050SJag Raman 5705d171050SJag Raman vccdbg("VCC: name=%s\n", dev_name(&vdev->dev)); 5715d171050SJag Raman 5725d171050SJag Raman if (!vcc_tty_driver) { 5735d171050SJag Raman pr_err("VCC: TTY driver not registered\n"); 5745d171050SJag Raman return -ENODEV; 5755d171050SJag Raman } 5765d171050SJag Raman 5775d171050SJag Raman port = kzalloc(sizeof(struct vcc_port), GFP_KERNEL); 5785d171050SJag Raman if (!port) 5795d171050SJag Raman return -ENOMEM; 5805d171050SJag Raman 5815d171050SJag Raman name = kstrdup(dev_name(&vdev->dev), GFP_KERNEL); 582*d81ffb87SYi Yang if (!name) { 583*d81ffb87SYi Yang rv = -ENOMEM; 584*d81ffb87SYi Yang goto free_port; 585*d81ffb87SYi Yang } 5865d171050SJag Raman 5875d171050SJag Raman rv = vio_driver_init(&port->vio, vdev, VDEV_CONSOLE_CON, vcc_versions, 5885d171050SJag Raman ARRAY_SIZE(vcc_versions), NULL, name); 5895d171050SJag Raman if (rv) 590*d81ffb87SYi Yang goto free_name; 5915d171050SJag Raman 5925d171050SJag Raman port->vio.debug = vcc_dbg_vio; 5935d171050SJag Raman vcc_ldc_cfg.debug = vcc_dbg_ldc; 5945d171050SJag Raman 5955d171050SJag Raman rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port); 5965d171050SJag Raman if (rv) 597*d81ffb87SYi Yang goto free_name; 5985d171050SJag Raman 5995d171050SJag Raman spin_lock_init(&port->lock); 6005d171050SJag Raman 6015d171050SJag Raman port->index = vcc_table_add(port); 6025d171050SJag Raman if (port->index == -1) { 6035d171050SJag Raman pr_err("VCC: no more TTY indices left for allocation\n"); 604ff62255aSWei Yongjun rv = -ENOMEM; 6055d171050SJag Raman goto free_ldc; 6065d171050SJag Raman } 6075d171050SJag Raman 6085d171050SJag Raman /* Register the device using VCC table index as TTY index */ 6095d171050SJag Raman dev = tty_register_device(vcc_tty_driver, port->index, &vdev->dev); 6105d171050SJag Raman if (IS_ERR(dev)) { 6115d171050SJag Raman rv = PTR_ERR(dev); 6125d171050SJag Raman goto free_table; 6135d171050SJag Raman } 6145d171050SJag Raman 6155d171050SJag Raman hp = mdesc_grab(); 6165d171050SJag Raman 6175d171050SJag Raman node = vio_vdev_node(hp, vdev); 6185d171050SJag Raman if (node == MDESC_NODE_NULL) { 6195d171050SJag Raman rv = -ENXIO; 6205d171050SJag Raman mdesc_release(hp); 6215d171050SJag Raman goto unreg_tty; 6225d171050SJag Raman } 6235d171050SJag Raman 6245d171050SJag Raman domain = mdesc_get_property(hp, node, "vcc-domain-name", NULL); 6255d171050SJag Raman if (!domain) { 6265d171050SJag Raman rv = -ENXIO; 6275d171050SJag Raman mdesc_release(hp); 6285d171050SJag Raman goto unreg_tty; 6295d171050SJag Raman } 6305d171050SJag Raman port->domain = kstrdup(domain, GFP_KERNEL); 631*d81ffb87SYi Yang if (!port->domain) { 632*d81ffb87SYi Yang rv = -ENOMEM; 633*d81ffb87SYi Yang goto unreg_tty; 634*d81ffb87SYi Yang } 635*d81ffb87SYi Yang 6365d171050SJag Raman 6375d171050SJag Raman mdesc_release(hp); 6385d171050SJag Raman 6398868e44aSJag Raman rv = sysfs_create_group(&vdev->dev.kobj, &vcc_attribute_group); 6408868e44aSJag Raman if (rv) 6418868e44aSJag Raman goto free_domain; 6428868e44aSJag Raman 6434790b6dcSKees Cook timer_setup(&port->rx_timer, vcc_rx_timer, 0); 6444790b6dcSKees Cook timer_setup(&port->tx_timer, vcc_tx_timer, 0); 645f8c55335SJag Raman 6465d171050SJag Raman dev_set_drvdata(&vdev->dev, port); 6475d171050SJag Raman 6485d171050SJag Raman /* It's possible to receive IRQs in the middle of vio_port_up. Disable 6495d171050SJag Raman * IRQs until the port is up. 6505d171050SJag Raman */ 6515d171050SJag Raman disable_irq_nosync(vdev->rx_irq); 6525d171050SJag Raman vio_port_up(&port->vio); 6535d171050SJag Raman enable_irq(vdev->rx_irq); 6545d171050SJag Raman 6555d171050SJag Raman return 0; 6565d171050SJag Raman 6578868e44aSJag Raman free_domain: 6588868e44aSJag Raman kfree(port->domain); 6595d171050SJag Raman unreg_tty: 6605d171050SJag Raman tty_unregister_device(vcc_tty_driver, port->index); 6615d171050SJag Raman free_table: 6625d171050SJag Raman vcc_table_remove(port->index); 6635d171050SJag Raman free_ldc: 6645d171050SJag Raman vio_ldc_free(&port->vio); 665*d81ffb87SYi Yang free_name: 6665d171050SJag Raman kfree(name); 667*d81ffb87SYi Yang free_port: 6685d171050SJag Raman kfree(port); 6695d171050SJag Raman 6705d171050SJag Raman return rv; 6715d171050SJag Raman } 6725d171050SJag Raman 6735d171050SJag Raman /** 6745d171050SJag Raman * vcc_remove() - Terminate a VCC port 6755d171050SJag Raman * @vdev: Pointer to VIO device of the VCC port 6765d171050SJag Raman * 6775d171050SJag Raman * Terminates a VCC port. Sets up the teardown of TTY and 6785d171050SJag Raman * VIO/LDC link between guest and primary domains. 6795d171050SJag Raman * 6805d171050SJag Raman * Return: status of removal 6815d171050SJag Raman */ 6821553573cSUwe Kleine-König static void vcc_remove(struct vio_dev *vdev) 6835d171050SJag Raman { 6845d171050SJag Raman struct vcc_port *port = dev_get_drvdata(&vdev->dev); 6855d171050SJag Raman 686f8c55335SJag Raman del_timer_sync(&port->rx_timer); 687f8c55335SJag Raman del_timer_sync(&port->tx_timer); 688f8c55335SJag Raman 6895d171050SJag Raman /* If there's a process with the device open, do a synchronous 6905d171050SJag Raman * hangup of the TTY. This *may* cause the process to call close 6915d171050SJag Raman * asynchronously, but it's not guaranteed. 6925d171050SJag Raman */ 6935d171050SJag Raman if (port->tty) 6945d171050SJag Raman tty_vhangup(port->tty); 6955d171050SJag Raman 6965d171050SJag Raman /* Get exclusive reference to VCC, ensures that there are no other 69763e34e70SUwe Kleine-König * clients to this port. This cannot fail. 6985d171050SJag Raman */ 69963e34e70SUwe Kleine-König vcc_get(port->index, true); 7005d171050SJag Raman 7015d171050SJag Raman tty_unregister_device(vcc_tty_driver, port->index); 7025d171050SJag Raman 7035d171050SJag Raman del_timer_sync(&port->vio.timer); 7045d171050SJag Raman vio_ldc_free(&port->vio); 7058868e44aSJag Raman sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); 7065d171050SJag Raman dev_set_drvdata(&vdev->dev, NULL); 7075d171050SJag Raman if (port->tty) { 7085d171050SJag Raman port->removed = true; 7095d171050SJag Raman vcc_put(port, true); 7105d171050SJag Raman } else { 7115d171050SJag Raman vcc_table_remove(port->index); 7125d171050SJag Raman 7135d171050SJag Raman kfree(port->vio.name); 7145d171050SJag Raman kfree(port->domain); 7155d171050SJag Raman kfree(port); 7165d171050SJag Raman } 7175d171050SJag Raman } 7185d171050SJag Raman 7195d171050SJag Raman static const struct vio_device_id vcc_match[] = { 7205d171050SJag Raman { 7215d171050SJag Raman .type = "vcc-port", 7225d171050SJag Raman }, 7235d171050SJag Raman {}, 7245d171050SJag Raman }; 7255d171050SJag Raman MODULE_DEVICE_TABLE(vio, vcc_match); 7265d171050SJag Raman 7275d171050SJag Raman static struct vio_driver vcc_driver = { 7285d171050SJag Raman .id_table = vcc_match, 7295d171050SJag Raman .probe = vcc_probe, 7305d171050SJag Raman .remove = vcc_remove, 7315d171050SJag Raman .name = "vcc", 7325d171050SJag Raman }; 7335d171050SJag Raman 73463a71744SJag Raman static int vcc_open(struct tty_struct *tty, struct file *vcc_file) 73563a71744SJag Raman { 73663a71744SJag Raman struct vcc_port *port; 73763a71744SJag Raman 73863a71744SJag Raman if (tty->count > 1) 73963a71744SJag Raman return -EBUSY; 74063a71744SJag Raman 74163a71744SJag Raman port = vcc_get_ne(tty->index); 74263a71744SJag Raman if (unlikely(!port)) { 74363a71744SJag Raman pr_err("VCC: open: Failed to find VCC port\n"); 74463a71744SJag Raman return -ENODEV; 74563a71744SJag Raman } 74663a71744SJag Raman 74763a71744SJag Raman if (unlikely(!port->vio.lp)) { 74863a71744SJag Raman pr_err("VCC: open: LDC channel not configured\n"); 74963a71744SJag Raman vcc_put(port, false); 75063a71744SJag Raman return -EPIPE; 75163a71744SJag Raman } 75263a71744SJag Raman vccdbgl(port->vio.lp); 75363a71744SJag Raman 75463a71744SJag Raman vcc_put(port, false); 75563a71744SJag Raman 75663a71744SJag Raman if (unlikely(!tty->port)) { 75763a71744SJag Raman pr_err("VCC: open: TTY port not found\n"); 75863a71744SJag Raman return -ENXIO; 75963a71744SJag Raman } 76063a71744SJag Raman 76163a71744SJag Raman if (unlikely(!tty->port->ops)) { 76263a71744SJag Raman pr_err("VCC: open: TTY ops not defined\n"); 76363a71744SJag Raman return -ENXIO; 76463a71744SJag Raman } 76563a71744SJag Raman 76663a71744SJag Raman return tty_port_open(tty->port, tty, vcc_file); 76763a71744SJag Raman } 76863a71744SJag Raman 76963a71744SJag Raman static void vcc_close(struct tty_struct *tty, struct file *vcc_file) 77063a71744SJag Raman { 77163a71744SJag Raman if (unlikely(tty->count > 1)) 77263a71744SJag Raman return; 77363a71744SJag Raman 77463a71744SJag Raman if (unlikely(!tty->port)) { 77563a71744SJag Raman pr_err("VCC: close: TTY port not found\n"); 77663a71744SJag Raman return; 77763a71744SJag Raman } 77863a71744SJag Raman 77963a71744SJag Raman tty_port_close(tty->port, tty, vcc_file); 78063a71744SJag Raman } 78163a71744SJag Raman 7826a3ff25bSJag Raman static void vcc_ldc_hup(struct vcc_port *port) 7836a3ff25bSJag Raman { 7846a3ff25bSJag Raman unsigned long flags; 7856a3ff25bSJag Raman 7866a3ff25bSJag Raman spin_lock_irqsave(&port->lock, flags); 7876a3ff25bSJag Raman 7886a3ff25bSJag Raman if (vcc_send_ctl(port, VCC_CTL_HUP) < 0) 7896a3ff25bSJag Raman vcc_kick_tx(port); 7906a3ff25bSJag Raman 7916a3ff25bSJag Raman spin_unlock_irqrestore(&port->lock, flags); 7926a3ff25bSJag Raman } 7936a3ff25bSJag Raman 7946a3ff25bSJag Raman static void vcc_hangup(struct tty_struct *tty) 7956a3ff25bSJag Raman { 7966a3ff25bSJag Raman struct vcc_port *port; 7976a3ff25bSJag Raman 7986a3ff25bSJag Raman port = vcc_get_ne(tty->index); 7996a3ff25bSJag Raman if (unlikely(!port)) { 8006a3ff25bSJag Raman pr_err("VCC: hangup: Failed to find VCC port\n"); 8016a3ff25bSJag Raman return; 8026a3ff25bSJag Raman } 8036a3ff25bSJag Raman 8046a3ff25bSJag Raman if (unlikely(!tty->port)) { 8056a3ff25bSJag Raman pr_err("VCC: hangup: TTY port not found\n"); 8066a3ff25bSJag Raman vcc_put(port, false); 8076a3ff25bSJag Raman return; 8086a3ff25bSJag Raman } 8096a3ff25bSJag Raman 8106a3ff25bSJag Raman vcc_ldc_hup(port); 8116a3ff25bSJag Raman 8126a3ff25bSJag Raman vcc_put(port, false); 8136a3ff25bSJag Raman 8146a3ff25bSJag Raman tty_port_hangup(tty->port); 8156a3ff25bSJag Raman } 8166a3ff25bSJag Raman 81795713967SJiri Slaby (SUSE) static ssize_t vcc_write(struct tty_struct *tty, const u8 *buf, size_t count) 818cf045260SJag Raman { 819cf045260SJag Raman struct vcc_port *port; 820cf045260SJag Raman struct vio_vcc *pkt; 821cf045260SJag Raman unsigned long flags; 822cfc7c12bSJiri Slaby (SUSE) size_t total_sent = 0; 823cfc7c12bSJiri Slaby (SUSE) size_t tosend = 0; 824cf045260SJag Raman int rv = -EINVAL; 825cf045260SJag Raman 826cf045260SJag Raman port = vcc_get_ne(tty->index); 827cf045260SJag Raman if (unlikely(!port)) { 828cf045260SJag Raman pr_err("VCC: write: Failed to find VCC port"); 829cf045260SJag Raman return -ENODEV; 830cf045260SJag Raman } 831cf045260SJag Raman 832cf045260SJag Raman spin_lock_irqsave(&port->lock, flags); 833cf045260SJag Raman 834cf045260SJag Raman pkt = &port->buffer; 835cf045260SJag Raman pkt->tag.type = VIO_TYPE_DATA; 836cf045260SJag Raman 837cf045260SJag Raman while (count > 0) { 838cf045260SJag Raman /* Minimum of data to write and space available */ 83995713967SJiri Slaby (SUSE) tosend = min_t(size_t, count, 84095713967SJiri Slaby (SUSE) (VCC_BUFF_LEN - port->chars_in_buffer)); 841cf045260SJag Raman 842cf045260SJag Raman if (!tosend) 843cf045260SJag Raman break; 844cf045260SJag Raman 845cf045260SJag Raman memcpy(&pkt->data[port->chars_in_buffer], &buf[total_sent], 846cf045260SJag Raman tosend); 847cf045260SJag Raman port->chars_in_buffer += tosend; 848cf045260SJag Raman pkt->tag.stype = tosend; 849cf045260SJag Raman 850cf045260SJag Raman vccdbg("TAG [%02x:%02x:%04x:%08x]\n", pkt->tag.type, 851cf045260SJag Raman pkt->tag.stype, pkt->tag.stype_env, pkt->tag.sid); 852cf045260SJag Raman vccdbg("DATA [%s]\n", pkt->data); 853cf045260SJag Raman vccdbgl(port->vio.lp); 854cf045260SJag Raman 855cf045260SJag Raman /* Since we know we have enough room in VCC buffer for tosend 856cf045260SJag Raman * we record that it was sent regardless of whether the 857cf045260SJag Raman * hypervisor actually took it because we have it buffered. 858cf045260SJag Raman */ 859cf045260SJag Raman rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); 860cfc7c12bSJiri Slaby (SUSE) vccdbg("VCC: write: ldc_write(%zu)=%d\n", 861cf045260SJag Raman (VIO_TAG_SIZE + tosend), rv); 862cf045260SJag Raman 863cf045260SJag Raman total_sent += tosend; 864cf045260SJag Raman count -= tosend; 865cf045260SJag Raman if (rv < 0) { 866cf045260SJag Raman vcc_kick_tx(port); 867cf045260SJag Raman break; 868cf045260SJag Raman } 869cf045260SJag Raman 870cf045260SJag Raman port->chars_in_buffer = 0; 871cf045260SJag Raman } 872cf045260SJag Raman 873cf045260SJag Raman spin_unlock_irqrestore(&port->lock, flags); 874cf045260SJag Raman 875cf045260SJag Raman vcc_put(port, false); 876cf045260SJag Raman 877cfc7c12bSJiri Slaby (SUSE) vccdbg("VCC: write: total=%zu rv=%d", total_sent, rv); 878cf045260SJag Raman 879cf045260SJag Raman return total_sent ? total_sent : rv; 880cf045260SJag Raman } 881cf045260SJag Raman 88203b3b1a2SJiri Slaby static unsigned int vcc_write_room(struct tty_struct *tty) 883cf045260SJag Raman { 884cf045260SJag Raman struct vcc_port *port; 88503b3b1a2SJiri Slaby unsigned int num; 886cf045260SJag Raman 887cf045260SJag Raman port = vcc_get_ne(tty->index); 888cf045260SJag Raman if (unlikely(!port)) { 889cf045260SJag Raman pr_err("VCC: write_room: Failed to find VCC port\n"); 8906bfbfcfcSJiri Slaby return 0; 891cf045260SJag Raman } 892cf045260SJag Raman 893cf045260SJag Raman num = VCC_BUFF_LEN - port->chars_in_buffer; 894cf045260SJag Raman 895cf045260SJag Raman vcc_put(port, false); 896cf045260SJag Raman 897cf045260SJag Raman return num; 898cf045260SJag Raman } 899cf045260SJag Raman 900fff4ef17SJiri Slaby static unsigned int vcc_chars_in_buffer(struct tty_struct *tty) 901aeee7debSJag Raman { 902aeee7debSJag Raman struct vcc_port *port; 903fff4ef17SJiri Slaby unsigned int num; 904aeee7debSJag Raman 905aeee7debSJag Raman port = vcc_get_ne(tty->index); 906aeee7debSJag Raman if (unlikely(!port)) { 907aeee7debSJag Raman pr_err("VCC: chars_in_buffer: Failed to find VCC port\n"); 90810eb63e5SJiri Slaby return 0; 909aeee7debSJag Raman } 910aeee7debSJag Raman 911aeee7debSJag Raman num = port->chars_in_buffer; 912aeee7debSJag Raman 913aeee7debSJag Raman vcc_put(port, false); 914aeee7debSJag Raman 915aeee7debSJag Raman return num; 916aeee7debSJag Raman } 917aeee7debSJag Raman 918e942e577SJag Raman static int vcc_break_ctl(struct tty_struct *tty, int state) 919e942e577SJag Raman { 920e942e577SJag Raman struct vcc_port *port; 921e942e577SJag Raman unsigned long flags; 922e942e577SJag Raman 923e942e577SJag Raman port = vcc_get_ne(tty->index); 924e942e577SJag Raman if (unlikely(!port)) { 925e942e577SJag Raman pr_err("VCC: break_ctl: Failed to find VCC port\n"); 926e942e577SJag Raman return -ENODEV; 927e942e577SJag Raman } 928e942e577SJag Raman 929e942e577SJag Raman /* Turn off break */ 930e942e577SJag Raman if (state == 0) { 931e942e577SJag Raman vcc_put(port, false); 932e942e577SJag Raman return 0; 933e942e577SJag Raman } 934e942e577SJag Raman 935e942e577SJag Raman spin_lock_irqsave(&port->lock, flags); 936e942e577SJag Raman 937e942e577SJag Raman if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) 938e942e577SJag Raman vcc_kick_tx(port); 939e942e577SJag Raman 940e942e577SJag Raman spin_unlock_irqrestore(&port->lock, flags); 941e942e577SJag Raman 942e942e577SJag Raman vcc_put(port, false); 943e942e577SJag Raman 944e942e577SJag Raman return 0; 945e942e577SJag Raman } 946e942e577SJag Raman 9478f03f948SJag Raman static int vcc_install(struct tty_driver *driver, struct tty_struct *tty) 9488f03f948SJag Raman { 9498f03f948SJag Raman struct vcc_port *port_vcc; 9508f03f948SJag Raman struct tty_port *port_tty; 9518f03f948SJag Raman int ret; 9528f03f948SJag Raman 9538f03f948SJag Raman if (tty->index >= VCC_MAX_PORTS) 9548f03f948SJag Raman return -EINVAL; 9558f03f948SJag Raman 9568f03f948SJag Raman ret = tty_standard_install(driver, tty); 9578f03f948SJag Raman if (ret) 9588f03f948SJag Raman return ret; 9598f03f948SJag Raman 9608f03f948SJag Raman port_tty = kzalloc(sizeof(struct tty_port), GFP_KERNEL); 9618f03f948SJag Raman if (!port_tty) 9628f03f948SJag Raman return -ENOMEM; 9638f03f948SJag Raman 9648f03f948SJag Raman port_vcc = vcc_get(tty->index, true); 9658f03f948SJag Raman if (!port_vcc) { 9668f03f948SJag Raman pr_err("VCC: install: Failed to find VCC port\n"); 9678f03f948SJag Raman tty->port = NULL; 9688f03f948SJag Raman kfree(port_tty); 9698f03f948SJag Raman return -ENODEV; 9708f03f948SJag Raman } 9718f03f948SJag Raman 9728f03f948SJag Raman tty_port_init(port_tty); 9738f03f948SJag Raman port_tty->ops = &vcc_port_ops; 9748f03f948SJag Raman tty->port = port_tty; 9758f03f948SJag Raman 9768f03f948SJag Raman port_vcc->tty = tty; 9778f03f948SJag Raman 9788f03f948SJag Raman vcc_put(port_vcc, true); 9798f03f948SJag Raman 9808f03f948SJag Raman return 0; 9818f03f948SJag Raman } 9828f03f948SJag Raman 9838f03f948SJag Raman static void vcc_cleanup(struct tty_struct *tty) 9848f03f948SJag Raman { 9858f03f948SJag Raman struct vcc_port *port; 9868f03f948SJag Raman 9878f03f948SJag Raman port = vcc_get(tty->index, true); 9888f03f948SJag Raman if (port) { 9898f03f948SJag Raman port->tty = NULL; 9908f03f948SJag Raman 9918f03f948SJag Raman if (port->removed) { 9928f03f948SJag Raman vcc_table_remove(tty->index); 9938f03f948SJag Raman kfree(port->vio.name); 9948f03f948SJag Raman kfree(port->domain); 9958f03f948SJag Raman kfree(port); 9968f03f948SJag Raman } else { 9978f03f948SJag Raman vcc_put(port, true); 9988f03f948SJag Raman } 9998f03f948SJag Raman } 10008f03f948SJag Raman 10018f03f948SJag Raman tty_port_destroy(tty->port); 10028f03f948SJag Raman kfree(tty->port); 10038f03f948SJag Raman tty->port = NULL; 10048f03f948SJag Raman } 10058f03f948SJag Raman 100663a71744SJag Raman static const struct tty_operations vcc_ops = { 100763a71744SJag Raman .open = vcc_open, 100863a71744SJag Raman .close = vcc_close, 10096a3ff25bSJag Raman .hangup = vcc_hangup, 1010cf045260SJag Raman .write = vcc_write, 1011cf045260SJag Raman .write_room = vcc_write_room, 1012aeee7debSJag Raman .chars_in_buffer = vcc_chars_in_buffer, 1013e942e577SJag Raman .break_ctl = vcc_break_ctl, 10148f03f948SJag Raman .install = vcc_install, 10158f03f948SJag Raman .cleanup = vcc_cleanup, 101663a71744SJag Raman }; 1017ce808b74SJag Raman 1018ce808b74SJag Raman #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) 1019ce808b74SJag Raman 1020ce808b74SJag Raman static int vcc_tty_init(void) 1021ce808b74SJag Raman { 1022ce808b74SJag Raman int rv; 1023ce808b74SJag Raman 1024ce808b74SJag Raman vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS); 1025c6b4ee9eSDan Carpenter if (IS_ERR(vcc_tty_driver)) { 1026ce808b74SJag Raman pr_err("VCC: TTY driver alloc failed\n"); 1027c6b4ee9eSDan Carpenter return PTR_ERR(vcc_tty_driver); 1028ce808b74SJag Raman } 1029ce808b74SJag Raman 10309af6f74dSJiri Slaby vcc_tty_driver->driver_name = "vcc"; 10319af6f74dSJiri Slaby vcc_tty_driver->name = "vcc"; 1032ce808b74SJag Raman 1033ce808b74SJag Raman vcc_tty_driver->minor_start = VCC_MINOR_START; 1034ce808b74SJag Raman vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; 1035ce808b74SJag Raman vcc_tty_driver->init_termios = vcc_tty_termios; 1036ce808b74SJag Raman 1037ce808b74SJag Raman tty_set_operations(vcc_tty_driver, &vcc_ops); 1038ce808b74SJag Raman 1039ce808b74SJag Raman rv = tty_register_driver(vcc_tty_driver); 1040ce808b74SJag Raman if (rv) { 1041ce808b74SJag Raman pr_err("VCC: TTY driver registration failed\n"); 10429f90a4ddSJiri Slaby tty_driver_kref_put(vcc_tty_driver); 1043ce808b74SJag Raman vcc_tty_driver = NULL; 1044ce808b74SJag Raman return rv; 1045ce808b74SJag Raman } 1046ce808b74SJag Raman 1047ce808b74SJag Raman vccdbg("VCC: TTY driver registered\n"); 1048ce808b74SJag Raman 1049ce808b74SJag Raman return 0; 1050ce808b74SJag Raman } 1051ce808b74SJag Raman 1052ce808b74SJag Raman static void vcc_tty_exit(void) 1053ce808b74SJag Raman { 1054ce808b74SJag Raman tty_unregister_driver(vcc_tty_driver); 10559f90a4ddSJiri Slaby tty_driver_kref_put(vcc_tty_driver); 1056ce808b74SJag Raman vccdbg("VCC: TTY driver unregistered\n"); 1057ce808b74SJag Raman 1058ce808b74SJag Raman vcc_tty_driver = NULL; 1059ce808b74SJag Raman } 1060ce808b74SJag Raman 106155bd2133SJag Raman static int __init vcc_init(void) 106255bd2133SJag Raman { 1063ce808b74SJag Raman int rv; 1064ce808b74SJag Raman 1065ce808b74SJag Raman rv = vcc_tty_init(); 1066ce808b74SJag Raman if (rv) { 1067ce808b74SJag Raman pr_err("VCC: TTY init failed\n"); 1068ce808b74SJag Raman return rv; 1069ce808b74SJag Raman } 1070ce808b74SJag Raman 10715d171050SJag Raman rv = vio_register_driver(&vcc_driver); 10725d171050SJag Raman if (rv) { 10735d171050SJag Raman pr_err("VCC: VIO driver registration failed\n"); 10745d171050SJag Raman vcc_tty_exit(); 10755d171050SJag Raman } else { 10765d171050SJag Raman vccdbg("VCC: VIO driver registered successfully\n"); 10775d171050SJag Raman } 10785d171050SJag Raman 1079ce808b74SJag Raman return rv; 108055bd2133SJag Raman } 108155bd2133SJag Raman 108255bd2133SJag Raman static void __exit vcc_exit(void) 108355bd2133SJag Raman { 10845d171050SJag Raman vio_unregister_driver(&vcc_driver); 10855d171050SJag Raman vccdbg("VCC: VIO driver unregistered\n"); 1086ce808b74SJag Raman vcc_tty_exit(); 1087ce808b74SJag Raman vccdbg("VCC: TTY driver unregistered\n"); 108855bd2133SJag Raman } 108955bd2133SJag Raman 109055bd2133SJag Raman module_init(vcc_init); 109155bd2133SJag Raman module_exit(vcc_exit); 1092