155bd2133SJag Raman /* vcc.c: sun4v virtual channel concentrator 255bd2133SJag Raman * 355bd2133SJag Raman * Copyright (C) 2017 Oracle. All rights reserved. 455bd2133SJag Raman */ 555bd2133SJag Raman 65d171050SJag Raman #include <linux/delay.h> 75d171050SJag Raman #include <linux/interrupt.h> 855bd2133SJag Raman #include <linux/module.h> 95d171050SJag Raman #include <linux/slab.h> 108868e44aSJag Raman #include <linux/sysfs.h> 11ce808b74SJag Raman #include <linux/tty.h> 12f8c55335SJag Raman #include <linux/tty_flip.h> 135d171050SJag Raman #include <asm/vio.h> 145d171050SJag Raman #include <asm/ldc.h> 1555bd2133SJag Raman 16f283ebd5SJag Raman #define DRV_MODULE_NAME "vcc" 17f283ebd5SJag Raman #define DRV_MODULE_VERSION "1.1" 18f283ebd5SJag Raman #define DRV_MODULE_RELDATE "July 1, 2017" 19f283ebd5SJag Raman 20ce808b74SJag Raman static char version[] = 21ce808b74SJag Raman DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")"; 22ce808b74SJag Raman 23f283ebd5SJag Raman MODULE_DESCRIPTION("Sun LDOM virtual console concentrator driver"); 24f283ebd5SJag Raman MODULE_LICENSE("GPL"); 25f283ebd5SJag Raman MODULE_VERSION(DRV_MODULE_VERSION); 26f283ebd5SJag Raman 275d171050SJag Raman struct vcc_port { 285d171050SJag Raman struct vio_driver_state vio; 295d171050SJag Raman 305d171050SJag Raman spinlock_t lock; 315d171050SJag Raman char *domain; 325d171050SJag Raman struct tty_struct *tty; /* only populated while dev is open */ 335d171050SJag Raman unsigned long index; /* index into the vcc_table */ 345d171050SJag Raman 355d171050SJag Raman u64 refcnt; 365d171050SJag Raman bool excl_locked; 375d171050SJag Raman 385d171050SJag Raman bool removed; 395d171050SJag Raman 405d171050SJag Raman /* This buffer is required to support the tty write_room interface 415d171050SJag Raman * and guarantee that any characters that the driver accepts will 425d171050SJag Raman * be eventually sent, either immediately or later. 435d171050SJag Raman */ 445d171050SJag Raman int chars_in_buffer; 455d171050SJag Raman struct vio_vcc buffer; 465d171050SJag Raman 475d171050SJag Raman struct timer_list rx_timer; 485d171050SJag Raman struct timer_list tx_timer; 495d171050SJag Raman }; 505d171050SJag Raman 515d171050SJag Raman /* Microseconds that thread will delay waiting for a vcc port ref */ 525d171050SJag Raman #define VCC_REF_DELAY 100 535d171050SJag Raman 54ce808b74SJag Raman #define VCC_MAX_PORTS 1024 55ce808b74SJag Raman #define VCC_MINOR_START 0 /* must be zero */ 56f8c55335SJag Raman #define VCC_BUFF_LEN VIO_VCC_MTU_SIZE 57ce808b74SJag Raman 588868e44aSJag Raman #define VCC_CTL_BREAK -1 598868e44aSJag Raman #define VCC_CTL_HUP -2 608868e44aSJag Raman 61ce808b74SJag Raman static const char vcc_driver_name[] = "vcc"; 62ce808b74SJag Raman static const char vcc_device_node[] = "vcc"; 63ce808b74SJag Raman static struct tty_driver *vcc_tty_driver; 64ce808b74SJag Raman 655d171050SJag Raman static struct vcc_port *vcc_table[VCC_MAX_PORTS]; 665d171050SJag Raman static DEFINE_SPINLOCK(vcc_table_lock); 675d171050SJag Raman 68f283ebd5SJag Raman int vcc_dbg; 69f283ebd5SJag Raman int vcc_dbg_ldc; 70f283ebd5SJag Raman int vcc_dbg_vio; 71f283ebd5SJag Raman 72f283ebd5SJag Raman module_param(vcc_dbg, uint, 0664); 73f283ebd5SJag Raman module_param(vcc_dbg_ldc, uint, 0664); 74f283ebd5SJag Raman module_param(vcc_dbg_vio, uint, 0664); 75f283ebd5SJag Raman 76f283ebd5SJag Raman #define VCC_DBG_DRV 0x1 77f283ebd5SJag Raman #define VCC_DBG_LDC 0x2 78f283ebd5SJag Raman #define VCC_DBG_PKT 0x4 79f283ebd5SJag Raman 80f283ebd5SJag Raman #define vccdbg(f, a...) \ 81f283ebd5SJag Raman do { \ 82f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_DRV) \ 83f283ebd5SJag Raman pr_info(f, ## a); \ 84f283ebd5SJag Raman } while (0) \ 85f283ebd5SJag Raman 86f283ebd5SJag Raman #define vccdbgl(l) \ 87f283ebd5SJag Raman do { \ 88f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_LDC) \ 89f283ebd5SJag Raman ldc_print(l); \ 90f283ebd5SJag Raman } while (0) \ 91f283ebd5SJag Raman 92f283ebd5SJag Raman #define vccdbgp(pkt) \ 93f283ebd5SJag Raman do { \ 94f283ebd5SJag Raman if (vcc_dbg & VCC_DBG_PKT) { \ 95f283ebd5SJag Raman int i; \ 96f283ebd5SJag Raman for (i = 0; i < pkt.tag.stype; i++) \ 97f283ebd5SJag Raman pr_info("[%c]", pkt.data[i]); \ 98f283ebd5SJag Raman } \ 99f283ebd5SJag Raman } while (0) \ 100f283ebd5SJag Raman 101ce808b74SJag Raman /* Note: Be careful when adding flags to this line discipline. Don't 102ce808b74SJag Raman * add anything that will cause echoing or we'll go into recursive 103ce808b74SJag Raman * loop echoing chars back and forth with the console drivers. 104ce808b74SJag Raman */ 1055bd0ea91SBhumika Goyal static const struct ktermios vcc_tty_termios = { 106ce808b74SJag Raman .c_iflag = IGNBRK | IGNPAR, 107ce808b74SJag Raman .c_oflag = OPOST, 108ce808b74SJag Raman .c_cflag = B38400 | CS8 | CREAD | HUPCL, 109ce808b74SJag Raman .c_cc = INIT_C_CC, 110ce808b74SJag Raman .c_ispeed = 38400, 111ce808b74SJag Raman .c_ospeed = 38400 112ce808b74SJag Raman }; 113ce808b74SJag Raman 1145d171050SJag Raman /** 1155d171050SJag Raman * vcc_table_add() - Add VCC port to the VCC table 1165d171050SJag Raman * @port: pointer to the VCC port 1175d171050SJag Raman * 1185d171050SJag Raman * Return: index of the port in the VCC table on success, 1195d171050SJag Raman * -1 on failure 1205d171050SJag Raman */ 1215d171050SJag Raman static int vcc_table_add(struct vcc_port *port) 1225d171050SJag Raman { 1235d171050SJag Raman unsigned long flags; 1245d171050SJag Raman int i; 1255d171050SJag Raman 1265d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1275d171050SJag Raman for (i = VCC_MINOR_START; i < VCC_MAX_PORTS; i++) { 1285d171050SJag Raman if (!vcc_table[i]) { 1295d171050SJag Raman vcc_table[i] = port; 1305d171050SJag Raman break; 1315d171050SJag Raman } 1325d171050SJag Raman } 1335d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1345d171050SJag Raman 1355d171050SJag Raman if (i < VCC_MAX_PORTS) 1365d171050SJag Raman return i; 1375d171050SJag Raman else 1385d171050SJag Raman return -1; 1395d171050SJag Raman } 1405d171050SJag Raman 1415d171050SJag Raman /** 1425d171050SJag Raman * vcc_table_remove() - Removes a VCC port from the VCC table 1435d171050SJag Raman * @index: Index into the VCC table 1445d171050SJag Raman */ 1455d171050SJag Raman static void vcc_table_remove(unsigned long index) 1465d171050SJag Raman { 1475d171050SJag Raman unsigned long flags; 1485d171050SJag Raman 1495d171050SJag Raman if (WARN_ON(index >= VCC_MAX_PORTS)) 1505d171050SJag Raman return; 1515d171050SJag Raman 1525d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1535d171050SJag Raman vcc_table[index] = NULL; 1545d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1555d171050SJag Raman } 1565d171050SJag Raman 1575d171050SJag Raman /** 1585d171050SJag Raman * vcc_get() - Gets a reference to VCC port 1595d171050SJag Raman * @index: Index into the VCC table 1605d171050SJag Raman * @excl: Indicates if an exclusive access is requested 1615d171050SJag Raman * 1625d171050SJag Raman * Return: reference to the VCC port, if found 1635d171050SJag Raman * NULL, if port not found 1645d171050SJag Raman */ 1655d171050SJag Raman static struct vcc_port *vcc_get(unsigned long index, bool excl) 1665d171050SJag Raman { 1675d171050SJag Raman struct vcc_port *port; 1685d171050SJag Raman unsigned long flags; 1695d171050SJag Raman 1705d171050SJag Raman try_again: 1715d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 1725d171050SJag Raman 1735d171050SJag Raman port = vcc_table[index]; 1745d171050SJag Raman if (!port) { 1755d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1765d171050SJag Raman return NULL; 1775d171050SJag Raman } 1785d171050SJag Raman 1795d171050SJag Raman if (!excl) { 1805d171050SJag Raman if (port->excl_locked) { 1815d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1825d171050SJag Raman udelay(VCC_REF_DELAY); 1835d171050SJag Raman goto try_again; 1845d171050SJag Raman } 1855d171050SJag Raman port->refcnt++; 1865d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1875d171050SJag Raman return port; 1885d171050SJag Raman } 1895d171050SJag Raman 1905d171050SJag Raman if (port->refcnt) { 1915d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 1925d171050SJag Raman /* Threads wanting exclusive access will wait half the time, 1935d171050SJag Raman * probably giving them higher priority in the case of 1945d171050SJag Raman * multiple waiters. 1955d171050SJag Raman */ 1965d171050SJag Raman udelay(VCC_REF_DELAY/2); 1975d171050SJag Raman goto try_again; 1985d171050SJag Raman } 1995d171050SJag Raman 2005d171050SJag Raman port->refcnt++; 2015d171050SJag Raman port->excl_locked = true; 2025d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 2035d171050SJag Raman 2045d171050SJag Raman return port; 2055d171050SJag Raman } 2065d171050SJag Raman 2075d171050SJag Raman /** 2085d171050SJag Raman * vcc_put() - Returns a reference to VCC port 2095d171050SJag Raman * @port: pointer to VCC port 2105d171050SJag Raman * @excl: Indicates if the returned reference is an exclusive reference 2115d171050SJag Raman * 2125d171050SJag Raman * Note: It's the caller's responsibility to ensure the correct value 2135d171050SJag Raman * for the excl flag 2145d171050SJag Raman */ 2155d171050SJag Raman static void vcc_put(struct vcc_port *port, bool excl) 2165d171050SJag Raman { 2175d171050SJag Raman unsigned long flags; 2185d171050SJag Raman 2195d171050SJag Raman if (!port) 2205d171050SJag Raman return; 2215d171050SJag Raman 2225d171050SJag Raman spin_lock_irqsave(&vcc_table_lock, flags); 2235d171050SJag Raman 2245d171050SJag Raman /* check if caller attempted to put with the wrong flags */ 2255d171050SJag Raman if (WARN_ON((excl && !port->excl_locked) || 2265d171050SJag Raman (!excl && port->excl_locked))) 2275d171050SJag Raman goto done; 2285d171050SJag Raman 2295d171050SJag Raman port->refcnt--; 2305d171050SJag Raman 2315d171050SJag Raman if (excl) 2325d171050SJag Raman port->excl_locked = false; 2335d171050SJag Raman 2345d171050SJag Raman done: 2355d171050SJag Raman spin_unlock_irqrestore(&vcc_table_lock, flags); 2365d171050SJag Raman } 2375d171050SJag Raman 2385d171050SJag Raman /** 2395d171050SJag Raman * vcc_get_ne() - Get a non-exclusive reference to VCC port 2405d171050SJag Raman * @index: Index into the VCC table 2415d171050SJag Raman * 2425d171050SJag Raman * Gets a non-exclusive reference to VCC port, if it's not removed 2435d171050SJag Raman * 2445d171050SJag Raman * Return: pointer to the VCC port, if found 2455d171050SJag Raman * NULL, if port not found 2465d171050SJag Raman */ 2475d171050SJag Raman static struct vcc_port *vcc_get_ne(unsigned long index) 2485d171050SJag Raman { 2495d171050SJag Raman struct vcc_port *port; 2505d171050SJag Raman 2515d171050SJag Raman port = vcc_get(index, false); 2525d171050SJag Raman 2535d171050SJag Raman if (port && port->removed) { 2545d171050SJag Raman vcc_put(port, false); 2555d171050SJag Raman return NULL; 2565d171050SJag Raman } 2575d171050SJag Raman 2585d171050SJag Raman return port; 2595d171050SJag Raman } 2605d171050SJag Raman 261f8c55335SJag Raman static void vcc_kick_rx(struct vcc_port *port) 262f8c55335SJag Raman { 263f8c55335SJag Raman struct vio_driver_state *vio = &port->vio; 264f8c55335SJag Raman 265f8c55335SJag Raman assert_spin_locked(&port->lock); 266f8c55335SJag Raman 267f8c55335SJag Raman if (!timer_pending(&port->rx_timer) && !port->removed) { 268f8c55335SJag Raman disable_irq_nosync(vio->vdev->rx_irq); 269f8c55335SJag Raman port->rx_timer.expires = (jiffies + 1); 270f8c55335SJag Raman add_timer(&port->rx_timer); 271f8c55335SJag Raman } 272f8c55335SJag Raman } 273f8c55335SJag Raman 274f8c55335SJag Raman static void vcc_kick_tx(struct vcc_port *port) 275f8c55335SJag Raman { 276f8c55335SJag Raman assert_spin_locked(&port->lock); 277f8c55335SJag Raman 278f8c55335SJag Raman if (!timer_pending(&port->tx_timer) && !port->removed) { 279f8c55335SJag Raman port->tx_timer.expires = (jiffies + 1); 280f8c55335SJag Raman add_timer(&port->tx_timer); 281f8c55335SJag Raman } 282f8c55335SJag Raman } 283f8c55335SJag Raman 284f8c55335SJag Raman static int vcc_rx_check(struct tty_struct *tty, int size) 285f8c55335SJag Raman { 286f8c55335SJag Raman if (WARN_ON(!tty || !tty->port)) 287f8c55335SJag Raman return 1; 288f8c55335SJag Raman 289f8c55335SJag Raman /* tty_buffer_request_room won't sleep because it uses 290f8c55335SJag Raman * GFP_ATOMIC flag to allocate buffer 291f8c55335SJag Raman */ 292f8c55335SJag Raman if (test_bit(TTY_THROTTLED, &tty->flags) || 293f8c55335SJag Raman (tty_buffer_request_room(tty->port, VCC_BUFF_LEN) < VCC_BUFF_LEN)) 294f8c55335SJag Raman return 0; 295f8c55335SJag Raman 296f8c55335SJag Raman return 1; 297f8c55335SJag Raman } 298f8c55335SJag Raman 299f8c55335SJag Raman static int vcc_rx(struct tty_struct *tty, char *buf, int size) 300f8c55335SJag Raman { 301f8c55335SJag Raman int len = 0; 302f8c55335SJag Raman 303f8c55335SJag Raman if (WARN_ON(!tty || !tty->port)) 304f8c55335SJag Raman return len; 305f8c55335SJag Raman 306f8c55335SJag Raman len = tty_insert_flip_string(tty->port, buf, size); 307f8c55335SJag Raman if (len) 308f8c55335SJag Raman tty_flip_buffer_push(tty->port); 309f8c55335SJag Raman 310f8c55335SJag Raman return len; 311f8c55335SJag Raman } 312f8c55335SJag Raman 313f8c55335SJag Raman static int vcc_ldc_read(struct vcc_port *port) 314f8c55335SJag Raman { 315f8c55335SJag Raman struct vio_driver_state *vio = &port->vio; 316f8c55335SJag Raman struct tty_struct *tty; 317f8c55335SJag Raman struct vio_vcc pkt; 318f8c55335SJag Raman int rv = 0; 319f8c55335SJag Raman 320f8c55335SJag Raman tty = port->tty; 321f8c55335SJag Raman if (!tty) { 322f8c55335SJag Raman rv = ldc_rx_reset(vio->lp); 323f8c55335SJag Raman vccdbg("VCC: reset rx q: rv=%d\n", rv); 324f8c55335SJag Raman goto done; 325f8c55335SJag Raman } 326f8c55335SJag Raman 327f8c55335SJag Raman /* Read as long as LDC has incoming data. */ 328f8c55335SJag Raman while (1) { 329f8c55335SJag Raman if (!vcc_rx_check(tty, VIO_VCC_MTU_SIZE)) { 330f8c55335SJag Raman vcc_kick_rx(port); 331f8c55335SJag Raman break; 332f8c55335SJag Raman } 333f8c55335SJag Raman 334f8c55335SJag Raman vccdbgl(vio->lp); 335f8c55335SJag Raman 336f8c55335SJag Raman rv = ldc_read(vio->lp, &pkt, sizeof(pkt)); 337f8c55335SJag Raman if (rv <= 0) 338f8c55335SJag Raman break; 339f8c55335SJag Raman 340f8c55335SJag Raman vccdbg("VCC: ldc_read()=%d\n", rv); 341f8c55335SJag Raman vccdbg("TAG [%02x:%02x:%04x:%08x]\n", 342f8c55335SJag Raman pkt.tag.type, pkt.tag.stype, 343f8c55335SJag Raman pkt.tag.stype_env, pkt.tag.sid); 344f8c55335SJag Raman 345f8c55335SJag Raman if (pkt.tag.type == VIO_TYPE_DATA) { 346f8c55335SJag Raman vccdbgp(pkt); 347f8c55335SJag Raman /* vcc_rx_check ensures memory availability */ 348f8c55335SJag Raman vcc_rx(tty, pkt.data, pkt.tag.stype); 349f8c55335SJag Raman } else { 350f8c55335SJag Raman pr_err("VCC: unknown msg [%02x:%02x:%04x:%08x]\n", 351f8c55335SJag Raman pkt.tag.type, pkt.tag.stype, 352f8c55335SJag Raman pkt.tag.stype_env, pkt.tag.sid); 353f8c55335SJag Raman rv = -ECONNRESET; 354f8c55335SJag Raman break; 355f8c55335SJag Raman } 356f8c55335SJag Raman 357f8c55335SJag Raman WARN_ON(rv != LDC_PACKET_SIZE); 358f8c55335SJag Raman } 359f8c55335SJag Raman 360f8c55335SJag Raman done: 361f8c55335SJag Raman return rv; 362f8c55335SJag Raman } 363f8c55335SJag Raman 364*4790b6dcSKees Cook static void vcc_rx_timer(struct timer_list *t) 365f8c55335SJag Raman { 366*4790b6dcSKees Cook struct vcc_port *port = from_timer(port, t, rx_timer); 367f8c55335SJag Raman struct vio_driver_state *vio; 368f8c55335SJag Raman unsigned long flags; 369f8c55335SJag Raman int rv; 370f8c55335SJag Raman 371f8c55335SJag Raman spin_lock_irqsave(&port->lock, flags); 372f8c55335SJag Raman port->rx_timer.expires = 0; 373f8c55335SJag Raman 374f8c55335SJag Raman vio = &port->vio; 375f8c55335SJag Raman 376f8c55335SJag Raman enable_irq(vio->vdev->rx_irq); 377f8c55335SJag Raman 378f8c55335SJag Raman if (!port->tty || port->removed) 379f8c55335SJag Raman goto done; 380f8c55335SJag Raman 381f8c55335SJag Raman rv = vcc_ldc_read(port); 382f8c55335SJag Raman if (rv == -ECONNRESET) 383f8c55335SJag Raman vio_conn_reset(vio); 384f8c55335SJag Raman 385f8c55335SJag Raman done: 386f8c55335SJag Raman spin_unlock_irqrestore(&port->lock, flags); 387f8c55335SJag Raman vcc_put(port, false); 388f8c55335SJag Raman } 389f8c55335SJag Raman 390*4790b6dcSKees Cook static void vcc_tx_timer(struct timer_list *t) 391f8c55335SJag Raman { 392*4790b6dcSKees Cook struct vcc_port *port = from_timer(port, t, tx_timer); 393f8c55335SJag Raman struct vio_vcc *pkt; 394f8c55335SJag Raman unsigned long flags; 395f8c55335SJag Raman int tosend = 0; 396f8c55335SJag Raman int rv; 397f8c55335SJag Raman 398f8c55335SJag Raman spin_lock_irqsave(&port->lock, flags); 399f8c55335SJag Raman port->tx_timer.expires = 0; 400f8c55335SJag Raman 401f8c55335SJag Raman if (!port->tty || port->removed) 402f8c55335SJag Raman goto done; 403f8c55335SJag Raman 404f8c55335SJag Raman tosend = min(VCC_BUFF_LEN, port->chars_in_buffer); 405f8c55335SJag Raman if (!tosend) 406f8c55335SJag Raman goto done; 407f8c55335SJag Raman 408f8c55335SJag Raman pkt = &port->buffer; 409f8c55335SJag Raman pkt->tag.type = VIO_TYPE_DATA; 410f8c55335SJag Raman pkt->tag.stype = tosend; 411f8c55335SJag Raman vccdbgl(port->vio.lp); 412f8c55335SJag Raman 413f8c55335SJag Raman rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); 414f8c55335SJag Raman WARN_ON(!rv); 415f8c55335SJag Raman 416f8c55335SJag Raman if (rv < 0) { 417f8c55335SJag Raman vccdbg("VCC: ldc_write()=%d\n", rv); 418f8c55335SJag Raman vcc_kick_tx(port); 419f8c55335SJag Raman } else { 420f8c55335SJag Raman struct tty_struct *tty = port->tty; 421f8c55335SJag Raman 422f8c55335SJag Raman port->chars_in_buffer = 0; 423f8c55335SJag Raman if (tty) 424f8c55335SJag Raman tty_wakeup(tty); 425f8c55335SJag Raman } 426f8c55335SJag Raman 427f8c55335SJag Raman done: 428f8c55335SJag Raman spin_unlock_irqrestore(&port->lock, flags); 429f8c55335SJag Raman vcc_put(port, false); 430f8c55335SJag Raman } 431f8c55335SJag Raman 432103b9b0bSJag Raman /** 433103b9b0bSJag Raman * vcc_event() - LDC event processing engine 434103b9b0bSJag Raman * @arg: VCC private data 435103b9b0bSJag Raman * @event: LDC event 436103b9b0bSJag Raman * 437103b9b0bSJag Raman * Handles LDC events for VCC 438103b9b0bSJag Raman */ 4395d171050SJag Raman static void vcc_event(void *arg, int event) 4405d171050SJag Raman { 441103b9b0bSJag Raman struct vio_driver_state *vio; 442103b9b0bSJag Raman struct vcc_port *port; 443103b9b0bSJag Raman unsigned long flags; 444103b9b0bSJag Raman int rv; 445103b9b0bSJag Raman 446103b9b0bSJag Raman port = arg; 447103b9b0bSJag Raman vio = &port->vio; 448103b9b0bSJag Raman 449103b9b0bSJag Raman spin_lock_irqsave(&port->lock, flags); 450103b9b0bSJag Raman 451103b9b0bSJag Raman switch (event) { 452103b9b0bSJag Raman case LDC_EVENT_RESET: 453103b9b0bSJag Raman case LDC_EVENT_UP: 454103b9b0bSJag Raman vio_link_state_change(vio, event); 455103b9b0bSJag Raman break; 456103b9b0bSJag Raman 457103b9b0bSJag Raman case LDC_EVENT_DATA_READY: 458103b9b0bSJag Raman rv = vcc_ldc_read(port); 459103b9b0bSJag Raman if (rv == -ECONNRESET) 460103b9b0bSJag Raman vio_conn_reset(vio); 461103b9b0bSJag Raman break; 462103b9b0bSJag Raman 463103b9b0bSJag Raman default: 464103b9b0bSJag Raman pr_err("VCC: unexpected LDC event(%d)\n", event); 465103b9b0bSJag Raman } 466103b9b0bSJag Raman 467103b9b0bSJag Raman spin_unlock_irqrestore(&port->lock, flags); 4685d171050SJag Raman } 4695d171050SJag Raman 4705d171050SJag Raman static struct ldc_channel_config vcc_ldc_cfg = { 4715d171050SJag Raman .event = vcc_event, 4725d171050SJag Raman .mtu = VIO_VCC_MTU_SIZE, 4735d171050SJag Raman .mode = LDC_MODE_RAW, 4745d171050SJag Raman .debug = 0, 4755d171050SJag Raman }; 4765d171050SJag Raman 4775d171050SJag Raman /* Ordered from largest major to lowest */ 4785d171050SJag Raman static struct vio_version vcc_versions[] = { 4795d171050SJag Raman { .major = 1, .minor = 0 }, 4805d171050SJag Raman }; 4815d171050SJag Raman 4828f03f948SJag Raman static struct tty_port_operations vcc_port_ops = { 0 }; 4838f03f948SJag Raman 4848868e44aSJag Raman static ssize_t vcc_sysfs_domain_show(struct device *dev, 4858868e44aSJag Raman struct device_attribute *attr, 4868868e44aSJag Raman char *buf) 4878868e44aSJag Raman { 4888868e44aSJag Raman struct vcc_port *port; 4898868e44aSJag Raman int rv; 4908868e44aSJag Raman 4918868e44aSJag Raman port = dev_get_drvdata(dev); 4928868e44aSJag Raman if (!port) 4938868e44aSJag Raman return -ENODEV; 4948868e44aSJag Raman 4958868e44aSJag Raman rv = scnprintf(buf, PAGE_SIZE, "%s\n", port->domain); 4968868e44aSJag Raman 4978868e44aSJag Raman return rv; 4988868e44aSJag Raman } 4998868e44aSJag Raman 5008868e44aSJag Raman static int vcc_send_ctl(struct vcc_port *port, int ctl) 5018868e44aSJag Raman { 5028868e44aSJag Raman struct vio_vcc pkt; 5038868e44aSJag Raman int rv; 5048868e44aSJag Raman 5058868e44aSJag Raman pkt.tag.type = VIO_TYPE_CTRL; 5068868e44aSJag Raman pkt.tag.sid = ctl; 5078868e44aSJag Raman pkt.tag.stype = 0; 5088868e44aSJag Raman 5098868e44aSJag Raman rv = ldc_write(port->vio.lp, &pkt, sizeof(pkt.tag)); 5108868e44aSJag Raman WARN_ON(!rv); 5118868e44aSJag Raman vccdbg("VCC: ldc_write(%ld)=%d\n", sizeof(pkt.tag), rv); 5128868e44aSJag Raman 5138868e44aSJag Raman return rv; 5148868e44aSJag Raman } 5158868e44aSJag Raman 5168868e44aSJag Raman static ssize_t vcc_sysfs_break_store(struct device *dev, 5178868e44aSJag Raman struct device_attribute *attr, 5188868e44aSJag Raman const char *buf, size_t count) 5198868e44aSJag Raman { 5208868e44aSJag Raman struct vcc_port *port; 5218868e44aSJag Raman unsigned long flags; 5228868e44aSJag Raman int rv = count; 5238868e44aSJag Raman int brk; 5248868e44aSJag Raman 5258868e44aSJag Raman port = dev_get_drvdata(dev); 5268868e44aSJag Raman if (!port) 5278868e44aSJag Raman return -ENODEV; 5288868e44aSJag Raman 5298868e44aSJag Raman spin_lock_irqsave(&port->lock, flags); 5308868e44aSJag Raman 5318868e44aSJag Raman if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) 5328868e44aSJag Raman rv = -EINVAL; 5338868e44aSJag Raman else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) 534f8c55335SJag Raman vcc_kick_tx(port); 5358868e44aSJag Raman 5368868e44aSJag Raman spin_unlock_irqrestore(&port->lock, flags); 5378868e44aSJag Raman 5388868e44aSJag Raman return rv; 5398868e44aSJag Raman } 5408868e44aSJag Raman 5418868e44aSJag Raman static DEVICE_ATTR(domain, 0400, vcc_sysfs_domain_show, NULL); 5428868e44aSJag Raman static DEVICE_ATTR(break, 0200, NULL, vcc_sysfs_break_store); 5438868e44aSJag Raman 5448868e44aSJag Raman static struct attribute *vcc_sysfs_entries[] = { 5458868e44aSJag Raman &dev_attr_domain.attr, 5468868e44aSJag Raman &dev_attr_break.attr, 5478868e44aSJag Raman NULL 5488868e44aSJag Raman }; 5498868e44aSJag Raman 5508868e44aSJag Raman static struct attribute_group vcc_attribute_group = { 5518868e44aSJag Raman .name = NULL, 5528868e44aSJag Raman .attrs = vcc_sysfs_entries, 5538868e44aSJag Raman }; 5548868e44aSJag Raman 5555d171050SJag Raman /** 5565d171050SJag Raman * vcc_probe() - Initialize VCC port 5575d171050SJag Raman * @vdev: Pointer to VIO device of the new VCC port 5585d171050SJag Raman * @id: VIO device ID 5595d171050SJag Raman * 5605d171050SJag Raman * Initializes a VCC port to receive serial console data from 5615d171050SJag Raman * the guest domain. Sets up a TTY end point on the control 5625d171050SJag Raman * domain. Sets up VIO/LDC link between the guest & control 5635d171050SJag Raman * domain endpoints. 5645d171050SJag Raman * 5655d171050SJag Raman * Return: status of the probe 5665d171050SJag Raman */ 5675d171050SJag Raman static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) 5685d171050SJag Raman { 5695d171050SJag Raman struct mdesc_handle *hp; 5705d171050SJag Raman struct vcc_port *port; 5715d171050SJag Raman struct device *dev; 5725d171050SJag Raman const char *domain; 5735d171050SJag Raman char *name; 5745d171050SJag Raman u64 node; 5755d171050SJag Raman int rv; 5765d171050SJag Raman 5775d171050SJag Raman vccdbg("VCC: name=%s\n", dev_name(&vdev->dev)); 5785d171050SJag Raman 5795d171050SJag Raman if (!vcc_tty_driver) { 5805d171050SJag Raman pr_err("VCC: TTY driver not registered\n"); 5815d171050SJag Raman return -ENODEV; 5825d171050SJag Raman } 5835d171050SJag Raman 5845d171050SJag Raman port = kzalloc(sizeof(struct vcc_port), GFP_KERNEL); 5855d171050SJag Raman if (!port) 5865d171050SJag Raman return -ENOMEM; 5875d171050SJag Raman 5885d171050SJag Raman name = kstrdup(dev_name(&vdev->dev), GFP_KERNEL); 5895d171050SJag Raman 5905d171050SJag Raman rv = vio_driver_init(&port->vio, vdev, VDEV_CONSOLE_CON, vcc_versions, 5915d171050SJag Raman ARRAY_SIZE(vcc_versions), NULL, name); 5925d171050SJag Raman if (rv) 5935d171050SJag Raman goto free_port; 5945d171050SJag Raman 5955d171050SJag Raman port->vio.debug = vcc_dbg_vio; 5965d171050SJag Raman vcc_ldc_cfg.debug = vcc_dbg_ldc; 5975d171050SJag Raman 5985d171050SJag Raman rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port); 5995d171050SJag Raman if (rv) 6005d171050SJag Raman goto free_port; 6015d171050SJag Raman 6025d171050SJag Raman spin_lock_init(&port->lock); 6035d171050SJag Raman 6045d171050SJag Raman port->index = vcc_table_add(port); 6055d171050SJag Raman if (port->index == -1) { 6065d171050SJag Raman pr_err("VCC: no more TTY indices left for allocation\n"); 6075d171050SJag Raman goto free_ldc; 6085d171050SJag Raman } 6095d171050SJag Raman 6105d171050SJag Raman /* Register the device using VCC table index as TTY index */ 6115d171050SJag Raman dev = tty_register_device(vcc_tty_driver, port->index, &vdev->dev); 6125d171050SJag Raman if (IS_ERR(dev)) { 6135d171050SJag Raman rv = PTR_ERR(dev); 6145d171050SJag Raman goto free_table; 6155d171050SJag Raman } 6165d171050SJag Raman 6175d171050SJag Raman hp = mdesc_grab(); 6185d171050SJag Raman 6195d171050SJag Raman node = vio_vdev_node(hp, vdev); 6205d171050SJag Raman if (node == MDESC_NODE_NULL) { 6215d171050SJag Raman rv = -ENXIO; 6225d171050SJag Raman mdesc_release(hp); 6235d171050SJag Raman goto unreg_tty; 6245d171050SJag Raman } 6255d171050SJag Raman 6265d171050SJag Raman domain = mdesc_get_property(hp, node, "vcc-domain-name", NULL); 6275d171050SJag Raman if (!domain) { 6285d171050SJag Raman rv = -ENXIO; 6295d171050SJag Raman mdesc_release(hp); 6305d171050SJag Raman goto unreg_tty; 6315d171050SJag Raman } 6325d171050SJag Raman port->domain = kstrdup(domain, GFP_KERNEL); 6335d171050SJag Raman 6345d171050SJag Raman mdesc_release(hp); 6355d171050SJag Raman 6368868e44aSJag Raman rv = sysfs_create_group(&vdev->dev.kobj, &vcc_attribute_group); 6378868e44aSJag Raman if (rv) 6388868e44aSJag Raman goto free_domain; 6398868e44aSJag Raman 640*4790b6dcSKees Cook timer_setup(&port->rx_timer, vcc_rx_timer, 0); 641*4790b6dcSKees Cook timer_setup(&port->tx_timer, vcc_tx_timer, 0); 642f8c55335SJag Raman 6435d171050SJag Raman dev_set_drvdata(&vdev->dev, port); 6445d171050SJag Raman 6455d171050SJag Raman /* It's possible to receive IRQs in the middle of vio_port_up. Disable 6465d171050SJag Raman * IRQs until the port is up. 6475d171050SJag Raman */ 6485d171050SJag Raman disable_irq_nosync(vdev->rx_irq); 6495d171050SJag Raman vio_port_up(&port->vio); 6505d171050SJag Raman enable_irq(vdev->rx_irq); 6515d171050SJag Raman 6525d171050SJag Raman return 0; 6535d171050SJag Raman 6548868e44aSJag Raman free_domain: 6558868e44aSJag Raman kfree(port->domain); 6565d171050SJag Raman unreg_tty: 6575d171050SJag Raman tty_unregister_device(vcc_tty_driver, port->index); 6585d171050SJag Raman free_table: 6595d171050SJag Raman vcc_table_remove(port->index); 6605d171050SJag Raman free_ldc: 6615d171050SJag Raman vio_ldc_free(&port->vio); 6625d171050SJag Raman free_port: 6635d171050SJag Raman kfree(name); 6645d171050SJag Raman kfree(port); 6655d171050SJag Raman 6665d171050SJag Raman return rv; 6675d171050SJag Raman } 6685d171050SJag Raman 6695d171050SJag Raman /** 6705d171050SJag Raman * vcc_remove() - Terminate a VCC port 6715d171050SJag Raman * @vdev: Pointer to VIO device of the VCC port 6725d171050SJag Raman * 6735d171050SJag Raman * Terminates a VCC port. Sets up the teardown of TTY and 6745d171050SJag Raman * VIO/LDC link between guest and primary domains. 6755d171050SJag Raman * 6765d171050SJag Raman * Return: status of removal 6775d171050SJag Raman */ 6785d171050SJag Raman static int vcc_remove(struct vio_dev *vdev) 6795d171050SJag Raman { 6805d171050SJag Raman struct vcc_port *port = dev_get_drvdata(&vdev->dev); 6815d171050SJag Raman 6825d171050SJag Raman if (!port) 6835d171050SJag Raman return -ENODEV; 6845d171050SJag Raman 685f8c55335SJag Raman del_timer_sync(&port->rx_timer); 686f8c55335SJag Raman del_timer_sync(&port->tx_timer); 687f8c55335SJag Raman 6885d171050SJag Raman /* If there's a process with the device open, do a synchronous 6895d171050SJag Raman * hangup of the TTY. This *may* cause the process to call close 6905d171050SJag Raman * asynchronously, but it's not guaranteed. 6915d171050SJag Raman */ 6925d171050SJag Raman if (port->tty) 6935d171050SJag Raman tty_vhangup(port->tty); 6945d171050SJag Raman 6955d171050SJag Raman /* Get exclusive reference to VCC, ensures that there are no other 6965d171050SJag Raman * clients to this port 6975d171050SJag Raman */ 6985d171050SJag Raman port = vcc_get(port->index, true); 6995d171050SJag Raman 7005d171050SJag Raman if (WARN_ON(!port)) 7015d171050SJag Raman return -ENODEV; 7025d171050SJag Raman 7035d171050SJag Raman tty_unregister_device(vcc_tty_driver, port->index); 7045d171050SJag Raman 7055d171050SJag Raman del_timer_sync(&port->vio.timer); 7065d171050SJag Raman vio_ldc_free(&port->vio); 7078868e44aSJag Raman sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); 7085d171050SJag Raman dev_set_drvdata(&vdev->dev, NULL); 7095d171050SJag Raman if (port->tty) { 7105d171050SJag Raman port->removed = true; 7115d171050SJag Raman vcc_put(port, true); 7125d171050SJag Raman } else { 7135d171050SJag Raman vcc_table_remove(port->index); 7145d171050SJag Raman 7155d171050SJag Raman kfree(port->vio.name); 7165d171050SJag Raman kfree(port->domain); 7175d171050SJag Raman kfree(port); 7185d171050SJag Raman } 7195d171050SJag Raman 7205d171050SJag Raman return 0; 7215d171050SJag Raman } 7225d171050SJag Raman 7235d171050SJag Raman static const struct vio_device_id vcc_match[] = { 7245d171050SJag Raman { 7255d171050SJag Raman .type = "vcc-port", 7265d171050SJag Raman }, 7275d171050SJag Raman {}, 7285d171050SJag Raman }; 7295d171050SJag Raman MODULE_DEVICE_TABLE(vio, vcc_match); 7305d171050SJag Raman 7315d171050SJag Raman static struct vio_driver vcc_driver = { 7325d171050SJag Raman .id_table = vcc_match, 7335d171050SJag Raman .probe = vcc_probe, 7345d171050SJag Raman .remove = vcc_remove, 7355d171050SJag Raman .name = "vcc", 7365d171050SJag Raman }; 7375d171050SJag Raman 73863a71744SJag Raman static int vcc_open(struct tty_struct *tty, struct file *vcc_file) 73963a71744SJag Raman { 74063a71744SJag Raman struct vcc_port *port; 74163a71744SJag Raman 74263a71744SJag Raman if (unlikely(!tty)) { 74363a71744SJag Raman pr_err("VCC: open: Invalid TTY handle\n"); 74463a71744SJag Raman return -ENXIO; 74563a71744SJag Raman } 74663a71744SJag Raman 74763a71744SJag Raman if (tty->count > 1) 74863a71744SJag Raman return -EBUSY; 74963a71744SJag Raman 75063a71744SJag Raman port = vcc_get_ne(tty->index); 75163a71744SJag Raman if (unlikely(!port)) { 75263a71744SJag Raman pr_err("VCC: open: Failed to find VCC port\n"); 75363a71744SJag Raman return -ENODEV; 75463a71744SJag Raman } 75563a71744SJag Raman 75663a71744SJag Raman if (unlikely(!port->vio.lp)) { 75763a71744SJag Raman pr_err("VCC: open: LDC channel not configured\n"); 75863a71744SJag Raman vcc_put(port, false); 75963a71744SJag Raman return -EPIPE; 76063a71744SJag Raman } 76163a71744SJag Raman vccdbgl(port->vio.lp); 76263a71744SJag Raman 76363a71744SJag Raman vcc_put(port, false); 76463a71744SJag Raman 76563a71744SJag Raman if (unlikely(!tty->port)) { 76663a71744SJag Raman pr_err("VCC: open: TTY port not found\n"); 76763a71744SJag Raman return -ENXIO; 76863a71744SJag Raman } 76963a71744SJag Raman 77063a71744SJag Raman if (unlikely(!tty->port->ops)) { 77163a71744SJag Raman pr_err("VCC: open: TTY ops not defined\n"); 77263a71744SJag Raman return -ENXIO; 77363a71744SJag Raman } 77463a71744SJag Raman 77563a71744SJag Raman return tty_port_open(tty->port, tty, vcc_file); 77663a71744SJag Raman } 77763a71744SJag Raman 77863a71744SJag Raman static void vcc_close(struct tty_struct *tty, struct file *vcc_file) 77963a71744SJag Raman { 78063a71744SJag Raman if (unlikely(!tty)) { 78163a71744SJag Raman pr_err("VCC: close: Invalid TTY handle\n"); 78263a71744SJag Raman return; 78363a71744SJag Raman } 78463a71744SJag Raman 78563a71744SJag Raman if (unlikely(tty->count > 1)) 78663a71744SJag Raman return; 78763a71744SJag Raman 78863a71744SJag Raman if (unlikely(!tty->port)) { 78963a71744SJag Raman pr_err("VCC: close: TTY port not found\n"); 79063a71744SJag Raman return; 79163a71744SJag Raman } 79263a71744SJag Raman 79363a71744SJag Raman tty_port_close(tty->port, tty, vcc_file); 79463a71744SJag Raman } 79563a71744SJag Raman 7966a3ff25bSJag Raman static void vcc_ldc_hup(struct vcc_port *port) 7976a3ff25bSJag Raman { 7986a3ff25bSJag Raman unsigned long flags; 7996a3ff25bSJag Raman 8006a3ff25bSJag Raman spin_lock_irqsave(&port->lock, flags); 8016a3ff25bSJag Raman 8026a3ff25bSJag Raman if (vcc_send_ctl(port, VCC_CTL_HUP) < 0) 8036a3ff25bSJag Raman vcc_kick_tx(port); 8046a3ff25bSJag Raman 8056a3ff25bSJag Raman spin_unlock_irqrestore(&port->lock, flags); 8066a3ff25bSJag Raman } 8076a3ff25bSJag Raman 8086a3ff25bSJag Raman static void vcc_hangup(struct tty_struct *tty) 8096a3ff25bSJag Raman { 8106a3ff25bSJag Raman struct vcc_port *port; 8116a3ff25bSJag Raman 8126a3ff25bSJag Raman if (unlikely(!tty)) { 8136a3ff25bSJag Raman pr_err("VCC: hangup: Invalid TTY handle\n"); 8146a3ff25bSJag Raman return; 8156a3ff25bSJag Raman } 8166a3ff25bSJag Raman 8176a3ff25bSJag Raman port = vcc_get_ne(tty->index); 8186a3ff25bSJag Raman if (unlikely(!port)) { 8196a3ff25bSJag Raman pr_err("VCC: hangup: Failed to find VCC port\n"); 8206a3ff25bSJag Raman return; 8216a3ff25bSJag Raman } 8226a3ff25bSJag Raman 8236a3ff25bSJag Raman if (unlikely(!tty->port)) { 8246a3ff25bSJag Raman pr_err("VCC: hangup: TTY port not found\n"); 8256a3ff25bSJag Raman vcc_put(port, false); 8266a3ff25bSJag Raman return; 8276a3ff25bSJag Raman } 8286a3ff25bSJag Raman 8296a3ff25bSJag Raman vcc_ldc_hup(port); 8306a3ff25bSJag Raman 8316a3ff25bSJag Raman vcc_put(port, false); 8326a3ff25bSJag Raman 8336a3ff25bSJag Raman tty_port_hangup(tty->port); 8346a3ff25bSJag Raman } 8356a3ff25bSJag Raman 836cf045260SJag Raman static int vcc_write(struct tty_struct *tty, const unsigned char *buf, 837cf045260SJag Raman int count) 838cf045260SJag Raman { 839cf045260SJag Raman struct vcc_port *port; 840cf045260SJag Raman struct vio_vcc *pkt; 841cf045260SJag Raman unsigned long flags; 842cf045260SJag Raman int total_sent = 0; 843cf045260SJag Raman int tosend = 0; 844cf045260SJag Raman int rv = -EINVAL; 845cf045260SJag Raman 846cf045260SJag Raman if (unlikely(!tty)) { 847cf045260SJag Raman pr_err("VCC: write: Invalid TTY handle\n"); 848cf045260SJag Raman return -ENXIO; 849cf045260SJag Raman } 850cf045260SJag Raman 851cf045260SJag Raman port = vcc_get_ne(tty->index); 852cf045260SJag Raman if (unlikely(!port)) { 853cf045260SJag Raman pr_err("VCC: write: Failed to find VCC port"); 854cf045260SJag Raman return -ENODEV; 855cf045260SJag Raman } 856cf045260SJag Raman 857cf045260SJag Raman spin_lock_irqsave(&port->lock, flags); 858cf045260SJag Raman 859cf045260SJag Raman pkt = &port->buffer; 860cf045260SJag Raman pkt->tag.type = VIO_TYPE_DATA; 861cf045260SJag Raman 862cf045260SJag Raman while (count > 0) { 863cf045260SJag Raman /* Minimum of data to write and space available */ 864cf045260SJag Raman tosend = min(count, (VCC_BUFF_LEN - port->chars_in_buffer)); 865cf045260SJag Raman 866cf045260SJag Raman if (!tosend) 867cf045260SJag Raman break; 868cf045260SJag Raman 869cf045260SJag Raman memcpy(&pkt->data[port->chars_in_buffer], &buf[total_sent], 870cf045260SJag Raman tosend); 871cf045260SJag Raman port->chars_in_buffer += tosend; 872cf045260SJag Raman pkt->tag.stype = tosend; 873cf045260SJag Raman 874cf045260SJag Raman vccdbg("TAG [%02x:%02x:%04x:%08x]\n", pkt->tag.type, 875cf045260SJag Raman pkt->tag.stype, pkt->tag.stype_env, pkt->tag.sid); 876cf045260SJag Raman vccdbg("DATA [%s]\n", pkt->data); 877cf045260SJag Raman vccdbgl(port->vio.lp); 878cf045260SJag Raman 879cf045260SJag Raman /* Since we know we have enough room in VCC buffer for tosend 880cf045260SJag Raman * we record that it was sent regardless of whether the 881cf045260SJag Raman * hypervisor actually took it because we have it buffered. 882cf045260SJag Raman */ 883cf045260SJag Raman rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); 884cf045260SJag Raman vccdbg("VCC: write: ldc_write(%d)=%d\n", 885cf045260SJag Raman (VIO_TAG_SIZE + tosend), rv); 886cf045260SJag Raman 887cf045260SJag Raman total_sent += tosend; 888cf045260SJag Raman count -= tosend; 889cf045260SJag Raman if (rv < 0) { 890cf045260SJag Raman vcc_kick_tx(port); 891cf045260SJag Raman break; 892cf045260SJag Raman } 893cf045260SJag Raman 894cf045260SJag Raman port->chars_in_buffer = 0; 895cf045260SJag Raman } 896cf045260SJag Raman 897cf045260SJag Raman spin_unlock_irqrestore(&port->lock, flags); 898cf045260SJag Raman 899cf045260SJag Raman vcc_put(port, false); 900cf045260SJag Raman 901cf045260SJag Raman vccdbg("VCC: write: total=%d rv=%d", total_sent, rv); 902cf045260SJag Raman 903cf045260SJag Raman return total_sent ? total_sent : rv; 904cf045260SJag Raman } 905cf045260SJag Raman 906cf045260SJag Raman static int vcc_write_room(struct tty_struct *tty) 907cf045260SJag Raman { 908cf045260SJag Raman struct vcc_port *port; 909cf045260SJag Raman u64 num; 910cf045260SJag Raman 911cf045260SJag Raman if (unlikely(!tty)) { 912cf045260SJag Raman pr_err("VCC: write_room: Invalid TTY handle\n"); 913cf045260SJag Raman return -ENXIO; 914cf045260SJag Raman } 915cf045260SJag Raman 916cf045260SJag Raman port = vcc_get_ne(tty->index); 917cf045260SJag Raman if (unlikely(!port)) { 918cf045260SJag Raman pr_err("VCC: write_room: Failed to find VCC port\n"); 919cf045260SJag Raman return -ENODEV; 920cf045260SJag Raman } 921cf045260SJag Raman 922cf045260SJag Raman num = VCC_BUFF_LEN - port->chars_in_buffer; 923cf045260SJag Raman 924cf045260SJag Raman vcc_put(port, false); 925cf045260SJag Raman 926cf045260SJag Raman return num; 927cf045260SJag Raman } 928cf045260SJag Raman 929aeee7debSJag Raman static int vcc_chars_in_buffer(struct tty_struct *tty) 930aeee7debSJag Raman { 931aeee7debSJag Raman struct vcc_port *port; 932aeee7debSJag Raman u64 num; 933aeee7debSJag Raman 934aeee7debSJag Raman if (unlikely(!tty)) { 935aeee7debSJag Raman pr_err("VCC: chars_in_buffer: Invalid TTY handle\n"); 936aeee7debSJag Raman return -ENXIO; 937aeee7debSJag Raman } 938aeee7debSJag Raman 939aeee7debSJag Raman port = vcc_get_ne(tty->index); 940aeee7debSJag Raman if (unlikely(!port)) { 941aeee7debSJag Raman pr_err("VCC: chars_in_buffer: Failed to find VCC port\n"); 942aeee7debSJag Raman return -ENODEV; 943aeee7debSJag Raman } 944aeee7debSJag Raman 945aeee7debSJag Raman num = port->chars_in_buffer; 946aeee7debSJag Raman 947aeee7debSJag Raman vcc_put(port, false); 948aeee7debSJag Raman 949aeee7debSJag Raman return num; 950aeee7debSJag Raman } 951aeee7debSJag Raman 952e942e577SJag Raman static int vcc_break_ctl(struct tty_struct *tty, int state) 953e942e577SJag Raman { 954e942e577SJag Raman struct vcc_port *port; 955e942e577SJag Raman unsigned long flags; 956e942e577SJag Raman 957e942e577SJag Raman if (unlikely(!tty)) { 958e942e577SJag Raman pr_err("VCC: break_ctl: Invalid TTY handle\n"); 959e942e577SJag Raman return -ENXIO; 960e942e577SJag Raman } 961e942e577SJag Raman 962e942e577SJag Raman port = vcc_get_ne(tty->index); 963e942e577SJag Raman if (unlikely(!port)) { 964e942e577SJag Raman pr_err("VCC: break_ctl: Failed to find VCC port\n"); 965e942e577SJag Raman return -ENODEV; 966e942e577SJag Raman } 967e942e577SJag Raman 968e942e577SJag Raman /* Turn off break */ 969e942e577SJag Raman if (state == 0) { 970e942e577SJag Raman vcc_put(port, false); 971e942e577SJag Raman return 0; 972e942e577SJag Raman } 973e942e577SJag Raman 974e942e577SJag Raman spin_lock_irqsave(&port->lock, flags); 975e942e577SJag Raman 976e942e577SJag Raman if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) 977e942e577SJag Raman vcc_kick_tx(port); 978e942e577SJag Raman 979e942e577SJag Raman spin_unlock_irqrestore(&port->lock, flags); 980e942e577SJag Raman 981e942e577SJag Raman vcc_put(port, false); 982e942e577SJag Raman 983e942e577SJag Raman return 0; 984e942e577SJag Raman } 985e942e577SJag Raman 9868f03f948SJag Raman static int vcc_install(struct tty_driver *driver, struct tty_struct *tty) 9878f03f948SJag Raman { 9888f03f948SJag Raman struct vcc_port *port_vcc; 9898f03f948SJag Raman struct tty_port *port_tty; 9908f03f948SJag Raman int ret; 9918f03f948SJag Raman 9928f03f948SJag Raman if (unlikely(!tty)) { 9938f03f948SJag Raman pr_err("VCC: install: Invalid TTY handle\n"); 9948f03f948SJag Raman return -ENXIO; 9958f03f948SJag Raman } 9968f03f948SJag Raman 9978f03f948SJag Raman if (tty->index >= VCC_MAX_PORTS) 9988f03f948SJag Raman return -EINVAL; 9998f03f948SJag Raman 10008f03f948SJag Raman ret = tty_standard_install(driver, tty); 10018f03f948SJag Raman if (ret) 10028f03f948SJag Raman return ret; 10038f03f948SJag Raman 10048f03f948SJag Raman port_tty = kzalloc(sizeof(struct tty_port), GFP_KERNEL); 10058f03f948SJag Raman if (!port_tty) 10068f03f948SJag Raman return -ENOMEM; 10078f03f948SJag Raman 10088f03f948SJag Raman port_vcc = vcc_get(tty->index, true); 10098f03f948SJag Raman if (!port_vcc) { 10108f03f948SJag Raman pr_err("VCC: install: Failed to find VCC port\n"); 10118f03f948SJag Raman tty->port = NULL; 10128f03f948SJag Raman kfree(port_tty); 10138f03f948SJag Raman return -ENODEV; 10148f03f948SJag Raman } 10158f03f948SJag Raman 10168f03f948SJag Raman tty_port_init(port_tty); 10178f03f948SJag Raman port_tty->ops = &vcc_port_ops; 10188f03f948SJag Raman tty->port = port_tty; 10198f03f948SJag Raman 10208f03f948SJag Raman port_vcc->tty = tty; 10218f03f948SJag Raman 10228f03f948SJag Raman vcc_put(port_vcc, true); 10238f03f948SJag Raman 10248f03f948SJag Raman return 0; 10258f03f948SJag Raman } 10268f03f948SJag Raman 10278f03f948SJag Raman static void vcc_cleanup(struct tty_struct *tty) 10288f03f948SJag Raman { 10298f03f948SJag Raman struct vcc_port *port; 10308f03f948SJag Raman 10318f03f948SJag Raman if (unlikely(!tty)) { 10328f03f948SJag Raman pr_err("VCC: cleanup: Invalid TTY handle\n"); 10338f03f948SJag Raman return; 10348f03f948SJag Raman } 10358f03f948SJag Raman 10368f03f948SJag Raman port = vcc_get(tty->index, true); 10378f03f948SJag Raman if (port) { 10388f03f948SJag Raman port->tty = NULL; 10398f03f948SJag Raman 10408f03f948SJag Raman if (port->removed) { 10418f03f948SJag Raman vcc_table_remove(tty->index); 10428f03f948SJag Raman kfree(port->vio.name); 10438f03f948SJag Raman kfree(port->domain); 10448f03f948SJag Raman kfree(port); 10458f03f948SJag Raman } else { 10468f03f948SJag Raman vcc_put(port, true); 10478f03f948SJag Raman } 10488f03f948SJag Raman } 10498f03f948SJag Raman 10508f03f948SJag Raman tty_port_destroy(tty->port); 10518f03f948SJag Raman kfree(tty->port); 10528f03f948SJag Raman tty->port = NULL; 10538f03f948SJag Raman } 10548f03f948SJag Raman 105563a71744SJag Raman static const struct tty_operations vcc_ops = { 105663a71744SJag Raman .open = vcc_open, 105763a71744SJag Raman .close = vcc_close, 10586a3ff25bSJag Raman .hangup = vcc_hangup, 1059cf045260SJag Raman .write = vcc_write, 1060cf045260SJag Raman .write_room = vcc_write_room, 1061aeee7debSJag Raman .chars_in_buffer = vcc_chars_in_buffer, 1062e942e577SJag Raman .break_ctl = vcc_break_ctl, 10638f03f948SJag Raman .install = vcc_install, 10648f03f948SJag Raman .cleanup = vcc_cleanup, 106563a71744SJag Raman }; 1066ce808b74SJag Raman 1067ce808b74SJag Raman #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) 1068ce808b74SJag Raman 1069ce808b74SJag Raman static int vcc_tty_init(void) 1070ce808b74SJag Raman { 1071ce808b74SJag Raman int rv; 1072ce808b74SJag Raman 1073ce808b74SJag Raman pr_info("VCC: %s\n", version); 1074ce808b74SJag Raman 1075ce808b74SJag Raman vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS); 1076c6b4ee9eSDan Carpenter if (IS_ERR(vcc_tty_driver)) { 1077ce808b74SJag Raman pr_err("VCC: TTY driver alloc failed\n"); 1078c6b4ee9eSDan Carpenter return PTR_ERR(vcc_tty_driver); 1079ce808b74SJag Raman } 1080ce808b74SJag Raman 1081ce808b74SJag Raman vcc_tty_driver->driver_name = vcc_driver_name; 1082ce808b74SJag Raman vcc_tty_driver->name = vcc_device_node; 1083ce808b74SJag Raman 1084ce808b74SJag Raman vcc_tty_driver->minor_start = VCC_MINOR_START; 1085ce808b74SJag Raman vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; 1086ce808b74SJag Raman vcc_tty_driver->init_termios = vcc_tty_termios; 1087ce808b74SJag Raman 1088ce808b74SJag Raman tty_set_operations(vcc_tty_driver, &vcc_ops); 1089ce808b74SJag Raman 1090ce808b74SJag Raman rv = tty_register_driver(vcc_tty_driver); 1091ce808b74SJag Raman if (rv) { 1092ce808b74SJag Raman pr_err("VCC: TTY driver registration failed\n"); 1093ce808b74SJag Raman put_tty_driver(vcc_tty_driver); 1094ce808b74SJag Raman vcc_tty_driver = NULL; 1095ce808b74SJag Raman return rv; 1096ce808b74SJag Raman } 1097ce808b74SJag Raman 1098ce808b74SJag Raman vccdbg("VCC: TTY driver registered\n"); 1099ce808b74SJag Raman 1100ce808b74SJag Raman return 0; 1101ce808b74SJag Raman } 1102ce808b74SJag Raman 1103ce808b74SJag Raman static void vcc_tty_exit(void) 1104ce808b74SJag Raman { 1105ce808b74SJag Raman tty_unregister_driver(vcc_tty_driver); 1106ce808b74SJag Raman put_tty_driver(vcc_tty_driver); 1107ce808b74SJag Raman vccdbg("VCC: TTY driver unregistered\n"); 1108ce808b74SJag Raman 1109ce808b74SJag Raman vcc_tty_driver = NULL; 1110ce808b74SJag Raman } 1111ce808b74SJag Raman 111255bd2133SJag Raman static int __init vcc_init(void) 111355bd2133SJag Raman { 1114ce808b74SJag Raman int rv; 1115ce808b74SJag Raman 1116ce808b74SJag Raman rv = vcc_tty_init(); 1117ce808b74SJag Raman if (rv) { 1118ce808b74SJag Raman pr_err("VCC: TTY init failed\n"); 1119ce808b74SJag Raman return rv; 1120ce808b74SJag Raman } 1121ce808b74SJag Raman 11225d171050SJag Raman rv = vio_register_driver(&vcc_driver); 11235d171050SJag Raman if (rv) { 11245d171050SJag Raman pr_err("VCC: VIO driver registration failed\n"); 11255d171050SJag Raman vcc_tty_exit(); 11265d171050SJag Raman } else { 11275d171050SJag Raman vccdbg("VCC: VIO driver registered successfully\n"); 11285d171050SJag Raman } 11295d171050SJag Raman 1130ce808b74SJag Raman return rv; 113155bd2133SJag Raman } 113255bd2133SJag Raman 113355bd2133SJag Raman static void __exit vcc_exit(void) 113455bd2133SJag Raman { 11355d171050SJag Raman vio_unregister_driver(&vcc_driver); 11365d171050SJag Raman vccdbg("VCC: VIO driver unregistered\n"); 1137ce808b74SJag Raman vcc_tty_exit(); 1138ce808b74SJag Raman vccdbg("VCC: TTY driver unregistered\n"); 113955bd2133SJag Raman } 114055bd2133SJag Raman 114155bd2133SJag Raman module_init(vcc_init); 114255bd2133SJag Raman module_exit(vcc_exit); 1143