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 */ 105ce808b74SJag Raman static 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 364f8c55335SJag Raman static void vcc_rx_timer(unsigned long index) 365f8c55335SJag Raman { 366f8c55335SJag Raman struct vio_driver_state *vio; 367f8c55335SJag Raman struct vcc_port *port; 368f8c55335SJag Raman unsigned long flags; 369f8c55335SJag Raman int rv; 370f8c55335SJag Raman 371f8c55335SJag Raman port = vcc_get_ne(index); 372f8c55335SJag Raman if (!port) 373f8c55335SJag Raman return; 374f8c55335SJag Raman 375f8c55335SJag Raman spin_lock_irqsave(&port->lock, flags); 376f8c55335SJag Raman port->rx_timer.expires = 0; 377f8c55335SJag Raman 378f8c55335SJag Raman vio = &port->vio; 379f8c55335SJag Raman 380f8c55335SJag Raman enable_irq(vio->vdev->rx_irq); 381f8c55335SJag Raman 382f8c55335SJag Raman if (!port->tty || port->removed) 383f8c55335SJag Raman goto done; 384f8c55335SJag Raman 385f8c55335SJag Raman rv = vcc_ldc_read(port); 386f8c55335SJag Raman if (rv == -ECONNRESET) 387f8c55335SJag Raman vio_conn_reset(vio); 388f8c55335SJag Raman 389f8c55335SJag Raman done: 390f8c55335SJag Raman spin_unlock_irqrestore(&port->lock, flags); 391f8c55335SJag Raman vcc_put(port, false); 392f8c55335SJag Raman } 393f8c55335SJag Raman 394f8c55335SJag Raman static void vcc_tx_timer(unsigned long index) 395f8c55335SJag Raman { 396f8c55335SJag Raman struct vcc_port *port; 397f8c55335SJag Raman struct vio_vcc *pkt; 398f8c55335SJag Raman unsigned long flags; 399f8c55335SJag Raman int tosend = 0; 400f8c55335SJag Raman int rv; 401f8c55335SJag Raman 402f8c55335SJag Raman port = vcc_get_ne(index); 403f8c55335SJag Raman if (!port) 404f8c55335SJag Raman return; 405f8c55335SJag Raman 406f8c55335SJag Raman spin_lock_irqsave(&port->lock, flags); 407f8c55335SJag Raman port->tx_timer.expires = 0; 408f8c55335SJag Raman 409f8c55335SJag Raman if (!port->tty || port->removed) 410f8c55335SJag Raman goto done; 411f8c55335SJag Raman 412f8c55335SJag Raman tosend = min(VCC_BUFF_LEN, port->chars_in_buffer); 413f8c55335SJag Raman if (!tosend) 414f8c55335SJag Raman goto done; 415f8c55335SJag Raman 416f8c55335SJag Raman pkt = &port->buffer; 417f8c55335SJag Raman pkt->tag.type = VIO_TYPE_DATA; 418f8c55335SJag Raman pkt->tag.stype = tosend; 419f8c55335SJag Raman vccdbgl(port->vio.lp); 420f8c55335SJag Raman 421f8c55335SJag Raman rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); 422f8c55335SJag Raman WARN_ON(!rv); 423f8c55335SJag Raman 424f8c55335SJag Raman if (rv < 0) { 425f8c55335SJag Raman vccdbg("VCC: ldc_write()=%d\n", rv); 426f8c55335SJag Raman vcc_kick_tx(port); 427f8c55335SJag Raman } else { 428f8c55335SJag Raman struct tty_struct *tty = port->tty; 429f8c55335SJag Raman 430f8c55335SJag Raman port->chars_in_buffer = 0; 431f8c55335SJag Raman if (tty) 432f8c55335SJag Raman tty_wakeup(tty); 433f8c55335SJag Raman } 434f8c55335SJag Raman 435f8c55335SJag Raman done: 436f8c55335SJag Raman spin_unlock_irqrestore(&port->lock, flags); 437f8c55335SJag Raman vcc_put(port, false); 438f8c55335SJag Raman } 439f8c55335SJag Raman 440103b9b0bSJag Raman /** 441103b9b0bSJag Raman * vcc_event() - LDC event processing engine 442103b9b0bSJag Raman * @arg: VCC private data 443103b9b0bSJag Raman * @event: LDC event 444103b9b0bSJag Raman * 445103b9b0bSJag Raman * Handles LDC events for VCC 446103b9b0bSJag Raman */ 4475d171050SJag Raman static void vcc_event(void *arg, int event) 4485d171050SJag Raman { 449103b9b0bSJag Raman struct vio_driver_state *vio; 450103b9b0bSJag Raman struct vcc_port *port; 451103b9b0bSJag Raman unsigned long flags; 452103b9b0bSJag Raman int rv; 453103b9b0bSJag Raman 454103b9b0bSJag Raman port = arg; 455103b9b0bSJag Raman vio = &port->vio; 456103b9b0bSJag Raman 457103b9b0bSJag Raman spin_lock_irqsave(&port->lock, flags); 458103b9b0bSJag Raman 459103b9b0bSJag Raman switch (event) { 460103b9b0bSJag Raman case LDC_EVENT_RESET: 461103b9b0bSJag Raman case LDC_EVENT_UP: 462103b9b0bSJag Raman vio_link_state_change(vio, event); 463103b9b0bSJag Raman break; 464103b9b0bSJag Raman 465103b9b0bSJag Raman case LDC_EVENT_DATA_READY: 466103b9b0bSJag Raman rv = vcc_ldc_read(port); 467103b9b0bSJag Raman if (rv == -ECONNRESET) 468103b9b0bSJag Raman vio_conn_reset(vio); 469103b9b0bSJag Raman break; 470103b9b0bSJag Raman 471103b9b0bSJag Raman default: 472103b9b0bSJag Raman pr_err("VCC: unexpected LDC event(%d)\n", event); 473103b9b0bSJag Raman } 474103b9b0bSJag Raman 475103b9b0bSJag Raman spin_unlock_irqrestore(&port->lock, flags); 4765d171050SJag Raman } 4775d171050SJag Raman 4785d171050SJag Raman static struct ldc_channel_config vcc_ldc_cfg = { 4795d171050SJag Raman .event = vcc_event, 4805d171050SJag Raman .mtu = VIO_VCC_MTU_SIZE, 4815d171050SJag Raman .mode = LDC_MODE_RAW, 4825d171050SJag Raman .debug = 0, 4835d171050SJag Raman }; 4845d171050SJag Raman 4855d171050SJag Raman /* Ordered from largest major to lowest */ 4865d171050SJag Raman static struct vio_version vcc_versions[] = { 4875d171050SJag Raman { .major = 1, .minor = 0 }, 4885d171050SJag Raman }; 4895d171050SJag Raman 4908868e44aSJag Raman static ssize_t vcc_sysfs_domain_show(struct device *dev, 4918868e44aSJag Raman struct device_attribute *attr, 4928868e44aSJag Raman char *buf) 4938868e44aSJag Raman { 4948868e44aSJag Raman struct vcc_port *port; 4958868e44aSJag Raman int rv; 4968868e44aSJag Raman 4978868e44aSJag Raman port = dev_get_drvdata(dev); 4988868e44aSJag Raman if (!port) 4998868e44aSJag Raman return -ENODEV; 5008868e44aSJag Raman 5018868e44aSJag Raman rv = scnprintf(buf, PAGE_SIZE, "%s\n", port->domain); 5028868e44aSJag Raman 5038868e44aSJag Raman return rv; 5048868e44aSJag Raman } 5058868e44aSJag Raman 5068868e44aSJag Raman static int vcc_send_ctl(struct vcc_port *port, int ctl) 5078868e44aSJag Raman { 5088868e44aSJag Raman struct vio_vcc pkt; 5098868e44aSJag Raman int rv; 5108868e44aSJag Raman 5118868e44aSJag Raman pkt.tag.type = VIO_TYPE_CTRL; 5128868e44aSJag Raman pkt.tag.sid = ctl; 5138868e44aSJag Raman pkt.tag.stype = 0; 5148868e44aSJag Raman 5158868e44aSJag Raman rv = ldc_write(port->vio.lp, &pkt, sizeof(pkt.tag)); 5168868e44aSJag Raman WARN_ON(!rv); 5178868e44aSJag Raman vccdbg("VCC: ldc_write(%ld)=%d\n", sizeof(pkt.tag), rv); 5188868e44aSJag Raman 5198868e44aSJag Raman return rv; 5208868e44aSJag Raman } 5218868e44aSJag Raman 5228868e44aSJag Raman static ssize_t vcc_sysfs_break_store(struct device *dev, 5238868e44aSJag Raman struct device_attribute *attr, 5248868e44aSJag Raman const char *buf, size_t count) 5258868e44aSJag Raman { 5268868e44aSJag Raman struct vcc_port *port; 5278868e44aSJag Raman unsigned long flags; 5288868e44aSJag Raman int rv = count; 5298868e44aSJag Raman int brk; 5308868e44aSJag Raman 5318868e44aSJag Raman port = dev_get_drvdata(dev); 5328868e44aSJag Raman if (!port) 5338868e44aSJag Raman return -ENODEV; 5348868e44aSJag Raman 5358868e44aSJag Raman spin_lock_irqsave(&port->lock, flags); 5368868e44aSJag Raman 5378868e44aSJag Raman if (sscanf(buf, "%ud", &brk) != 1 || brk != 1) 5388868e44aSJag Raman rv = -EINVAL; 5398868e44aSJag Raman else if (vcc_send_ctl(port, VCC_CTL_BREAK) < 0) 540f8c55335SJag Raman vcc_kick_tx(port); 5418868e44aSJag Raman 5428868e44aSJag Raman spin_unlock_irqrestore(&port->lock, flags); 5438868e44aSJag Raman 5448868e44aSJag Raman return rv; 5458868e44aSJag Raman } 5468868e44aSJag Raman 5478868e44aSJag Raman static DEVICE_ATTR(domain, 0400, vcc_sysfs_domain_show, NULL); 5488868e44aSJag Raman static DEVICE_ATTR(break, 0200, NULL, vcc_sysfs_break_store); 5498868e44aSJag Raman 5508868e44aSJag Raman static struct attribute *vcc_sysfs_entries[] = { 5518868e44aSJag Raman &dev_attr_domain.attr, 5528868e44aSJag Raman &dev_attr_break.attr, 5538868e44aSJag Raman NULL 5548868e44aSJag Raman }; 5558868e44aSJag Raman 5568868e44aSJag Raman static struct attribute_group vcc_attribute_group = { 5578868e44aSJag Raman .name = NULL, 5588868e44aSJag Raman .attrs = vcc_sysfs_entries, 5598868e44aSJag Raman }; 5608868e44aSJag Raman 5615d171050SJag Raman /** 5625d171050SJag Raman * vcc_probe() - Initialize VCC port 5635d171050SJag Raman * @vdev: Pointer to VIO device of the new VCC port 5645d171050SJag Raman * @id: VIO device ID 5655d171050SJag Raman * 5665d171050SJag Raman * Initializes a VCC port to receive serial console data from 5675d171050SJag Raman * the guest domain. Sets up a TTY end point on the control 5685d171050SJag Raman * domain. Sets up VIO/LDC link between the guest & control 5695d171050SJag Raman * domain endpoints. 5705d171050SJag Raman * 5715d171050SJag Raman * Return: status of the probe 5725d171050SJag Raman */ 5735d171050SJag Raman static int vcc_probe(struct vio_dev *vdev, const struct vio_device_id *id) 5745d171050SJag Raman { 5755d171050SJag Raman struct mdesc_handle *hp; 5765d171050SJag Raman struct vcc_port *port; 5775d171050SJag Raman struct device *dev; 5785d171050SJag Raman const char *domain; 5795d171050SJag Raman char *name; 5805d171050SJag Raman u64 node; 5815d171050SJag Raman int rv; 5825d171050SJag Raman 5835d171050SJag Raman vccdbg("VCC: name=%s\n", dev_name(&vdev->dev)); 5845d171050SJag Raman 5855d171050SJag Raman if (!vcc_tty_driver) { 5865d171050SJag Raman pr_err("VCC: TTY driver not registered\n"); 5875d171050SJag Raman return -ENODEV; 5885d171050SJag Raman } 5895d171050SJag Raman 5905d171050SJag Raman port = kzalloc(sizeof(struct vcc_port), GFP_KERNEL); 5915d171050SJag Raman if (!port) 5925d171050SJag Raman return -ENOMEM; 5935d171050SJag Raman 5945d171050SJag Raman name = kstrdup(dev_name(&vdev->dev), GFP_KERNEL); 5955d171050SJag Raman 5965d171050SJag Raman rv = vio_driver_init(&port->vio, vdev, VDEV_CONSOLE_CON, vcc_versions, 5975d171050SJag Raman ARRAY_SIZE(vcc_versions), NULL, name); 5985d171050SJag Raman if (rv) 5995d171050SJag Raman goto free_port; 6005d171050SJag Raman 6015d171050SJag Raman port->vio.debug = vcc_dbg_vio; 6025d171050SJag Raman vcc_ldc_cfg.debug = vcc_dbg_ldc; 6035d171050SJag Raman 6045d171050SJag Raman rv = vio_ldc_alloc(&port->vio, &vcc_ldc_cfg, port); 6055d171050SJag Raman if (rv) 6065d171050SJag Raman goto free_port; 6075d171050SJag Raman 6085d171050SJag Raman spin_lock_init(&port->lock); 6095d171050SJag Raman 6105d171050SJag Raman port->index = vcc_table_add(port); 6115d171050SJag Raman if (port->index == -1) { 6125d171050SJag Raman pr_err("VCC: no more TTY indices left for allocation\n"); 6135d171050SJag Raman goto free_ldc; 6145d171050SJag Raman } 6155d171050SJag Raman 6165d171050SJag Raman /* Register the device using VCC table index as TTY index */ 6175d171050SJag Raman dev = tty_register_device(vcc_tty_driver, port->index, &vdev->dev); 6185d171050SJag Raman if (IS_ERR(dev)) { 6195d171050SJag Raman rv = PTR_ERR(dev); 6205d171050SJag Raman goto free_table; 6215d171050SJag Raman } 6225d171050SJag Raman 6235d171050SJag Raman hp = mdesc_grab(); 6245d171050SJag Raman 6255d171050SJag Raman node = vio_vdev_node(hp, vdev); 6265d171050SJag Raman if (node == MDESC_NODE_NULL) { 6275d171050SJag Raman rv = -ENXIO; 6285d171050SJag Raman mdesc_release(hp); 6295d171050SJag Raman goto unreg_tty; 6305d171050SJag Raman } 6315d171050SJag Raman 6325d171050SJag Raman domain = mdesc_get_property(hp, node, "vcc-domain-name", NULL); 6335d171050SJag Raman if (!domain) { 6345d171050SJag Raman rv = -ENXIO; 6355d171050SJag Raman mdesc_release(hp); 6365d171050SJag Raman goto unreg_tty; 6375d171050SJag Raman } 6385d171050SJag Raman port->domain = kstrdup(domain, GFP_KERNEL); 6395d171050SJag Raman 6405d171050SJag Raman mdesc_release(hp); 6415d171050SJag Raman 6428868e44aSJag Raman rv = sysfs_create_group(&vdev->dev.kobj, &vcc_attribute_group); 6438868e44aSJag Raman if (rv) 6448868e44aSJag Raman goto free_domain; 6458868e44aSJag Raman 646f8c55335SJag Raman init_timer(&port->rx_timer); 647f8c55335SJag Raman port->rx_timer.function = vcc_rx_timer; 648f8c55335SJag Raman port->rx_timer.data = port->index; 649f8c55335SJag Raman 650f8c55335SJag Raman init_timer(&port->tx_timer); 651f8c55335SJag Raman port->tx_timer.function = vcc_tx_timer; 652f8c55335SJag Raman port->tx_timer.data = port->index; 653f8c55335SJag Raman 6545d171050SJag Raman dev_set_drvdata(&vdev->dev, port); 6555d171050SJag Raman 6565d171050SJag Raman /* It's possible to receive IRQs in the middle of vio_port_up. Disable 6575d171050SJag Raman * IRQs until the port is up. 6585d171050SJag Raman */ 6595d171050SJag Raman disable_irq_nosync(vdev->rx_irq); 6605d171050SJag Raman vio_port_up(&port->vio); 6615d171050SJag Raman enable_irq(vdev->rx_irq); 6625d171050SJag Raman 6635d171050SJag Raman return 0; 6645d171050SJag Raman 6658868e44aSJag Raman free_domain: 6668868e44aSJag Raman kfree(port->domain); 6675d171050SJag Raman unreg_tty: 6685d171050SJag Raman tty_unregister_device(vcc_tty_driver, port->index); 6695d171050SJag Raman free_table: 6705d171050SJag Raman vcc_table_remove(port->index); 6715d171050SJag Raman free_ldc: 6725d171050SJag Raman vio_ldc_free(&port->vio); 6735d171050SJag Raman free_port: 6745d171050SJag Raman kfree(name); 6755d171050SJag Raman kfree(port); 6765d171050SJag Raman 6775d171050SJag Raman return rv; 6785d171050SJag Raman } 6795d171050SJag Raman 6805d171050SJag Raman /** 6815d171050SJag Raman * vcc_remove() - Terminate a VCC port 6825d171050SJag Raman * @vdev: Pointer to VIO device of the VCC port 6835d171050SJag Raman * 6845d171050SJag Raman * Terminates a VCC port. Sets up the teardown of TTY and 6855d171050SJag Raman * VIO/LDC link between guest and primary domains. 6865d171050SJag Raman * 6875d171050SJag Raman * Return: status of removal 6885d171050SJag Raman */ 6895d171050SJag Raman static int vcc_remove(struct vio_dev *vdev) 6905d171050SJag Raman { 6915d171050SJag Raman struct vcc_port *port = dev_get_drvdata(&vdev->dev); 6925d171050SJag Raman 6935d171050SJag Raman if (!port) 6945d171050SJag Raman return -ENODEV; 6955d171050SJag Raman 696f8c55335SJag Raman del_timer_sync(&port->rx_timer); 697f8c55335SJag Raman del_timer_sync(&port->tx_timer); 698f8c55335SJag Raman 6995d171050SJag Raman /* If there's a process with the device open, do a synchronous 7005d171050SJag Raman * hangup of the TTY. This *may* cause the process to call close 7015d171050SJag Raman * asynchronously, but it's not guaranteed. 7025d171050SJag Raman */ 7035d171050SJag Raman if (port->tty) 7045d171050SJag Raman tty_vhangup(port->tty); 7055d171050SJag Raman 7065d171050SJag Raman /* Get exclusive reference to VCC, ensures that there are no other 7075d171050SJag Raman * clients to this port 7085d171050SJag Raman */ 7095d171050SJag Raman port = vcc_get(port->index, true); 7105d171050SJag Raman 7115d171050SJag Raman if (WARN_ON(!port)) 7125d171050SJag Raman return -ENODEV; 7135d171050SJag Raman 7145d171050SJag Raman tty_unregister_device(vcc_tty_driver, port->index); 7155d171050SJag Raman 7165d171050SJag Raman del_timer_sync(&port->vio.timer); 7175d171050SJag Raman vio_ldc_free(&port->vio); 7188868e44aSJag Raman sysfs_remove_group(&vdev->dev.kobj, &vcc_attribute_group); 7195d171050SJag Raman dev_set_drvdata(&vdev->dev, NULL); 7205d171050SJag Raman if (port->tty) { 7215d171050SJag Raman port->removed = true; 7225d171050SJag Raman vcc_put(port, true); 7235d171050SJag Raman } else { 7245d171050SJag Raman vcc_table_remove(port->index); 7255d171050SJag Raman 7265d171050SJag Raman kfree(port->vio.name); 7275d171050SJag Raman kfree(port->domain); 7285d171050SJag Raman kfree(port); 7295d171050SJag Raman } 7305d171050SJag Raman 7315d171050SJag Raman return 0; 7325d171050SJag Raman } 7335d171050SJag Raman 7345d171050SJag Raman static const struct vio_device_id vcc_match[] = { 7355d171050SJag Raman { 7365d171050SJag Raman .type = "vcc-port", 7375d171050SJag Raman }, 7385d171050SJag Raman {}, 7395d171050SJag Raman }; 7405d171050SJag Raman MODULE_DEVICE_TABLE(vio, vcc_match); 7415d171050SJag Raman 7425d171050SJag Raman static struct vio_driver vcc_driver = { 7435d171050SJag Raman .id_table = vcc_match, 7445d171050SJag Raman .probe = vcc_probe, 7455d171050SJag Raman .remove = vcc_remove, 7465d171050SJag Raman .name = "vcc", 7475d171050SJag Raman }; 7485d171050SJag Raman 74963a71744SJag Raman static int vcc_open(struct tty_struct *tty, struct file *vcc_file) 75063a71744SJag Raman { 75163a71744SJag Raman struct vcc_port *port; 75263a71744SJag Raman 75363a71744SJag Raman if (unlikely(!tty)) { 75463a71744SJag Raman pr_err("VCC: open: Invalid TTY handle\n"); 75563a71744SJag Raman return -ENXIO; 75663a71744SJag Raman } 75763a71744SJag Raman 75863a71744SJag Raman if (tty->count > 1) 75963a71744SJag Raman return -EBUSY; 76063a71744SJag Raman 76163a71744SJag Raman port = vcc_get_ne(tty->index); 76263a71744SJag Raman if (unlikely(!port)) { 76363a71744SJag Raman pr_err("VCC: open: Failed to find VCC port\n"); 76463a71744SJag Raman return -ENODEV; 76563a71744SJag Raman } 76663a71744SJag Raman 76763a71744SJag Raman if (unlikely(!port->vio.lp)) { 76863a71744SJag Raman pr_err("VCC: open: LDC channel not configured\n"); 76963a71744SJag Raman vcc_put(port, false); 77063a71744SJag Raman return -EPIPE; 77163a71744SJag Raman } 77263a71744SJag Raman vccdbgl(port->vio.lp); 77363a71744SJag Raman 77463a71744SJag Raman vcc_put(port, false); 77563a71744SJag Raman 77663a71744SJag Raman if (unlikely(!tty->port)) { 77763a71744SJag Raman pr_err("VCC: open: TTY port not found\n"); 77863a71744SJag Raman return -ENXIO; 77963a71744SJag Raman } 78063a71744SJag Raman 78163a71744SJag Raman if (unlikely(!tty->port->ops)) { 78263a71744SJag Raman pr_err("VCC: open: TTY ops not defined\n"); 78363a71744SJag Raman return -ENXIO; 78463a71744SJag Raman } 78563a71744SJag Raman 78663a71744SJag Raman return tty_port_open(tty->port, tty, vcc_file); 78763a71744SJag Raman } 78863a71744SJag Raman 78963a71744SJag Raman static void vcc_close(struct tty_struct *tty, struct file *vcc_file) 79063a71744SJag Raman { 79163a71744SJag Raman if (unlikely(!tty)) { 79263a71744SJag Raman pr_err("VCC: close: Invalid TTY handle\n"); 79363a71744SJag Raman return; 79463a71744SJag Raman } 79563a71744SJag Raman 79663a71744SJag Raman if (unlikely(tty->count > 1)) 79763a71744SJag Raman return; 79863a71744SJag Raman 79963a71744SJag Raman if (unlikely(!tty->port)) { 80063a71744SJag Raman pr_err("VCC: close: TTY port not found\n"); 80163a71744SJag Raman return; 80263a71744SJag Raman } 80363a71744SJag Raman 80463a71744SJag Raman tty_port_close(tty->port, tty, vcc_file); 80563a71744SJag Raman } 80663a71744SJag Raman 8076a3ff25bSJag Raman static void vcc_ldc_hup(struct vcc_port *port) 8086a3ff25bSJag Raman { 8096a3ff25bSJag Raman unsigned long flags; 8106a3ff25bSJag Raman 8116a3ff25bSJag Raman spin_lock_irqsave(&port->lock, flags); 8126a3ff25bSJag Raman 8136a3ff25bSJag Raman if (vcc_send_ctl(port, VCC_CTL_HUP) < 0) 8146a3ff25bSJag Raman vcc_kick_tx(port); 8156a3ff25bSJag Raman 8166a3ff25bSJag Raman spin_unlock_irqrestore(&port->lock, flags); 8176a3ff25bSJag Raman } 8186a3ff25bSJag Raman 8196a3ff25bSJag Raman static void vcc_hangup(struct tty_struct *tty) 8206a3ff25bSJag Raman { 8216a3ff25bSJag Raman struct vcc_port *port; 8226a3ff25bSJag Raman 8236a3ff25bSJag Raman if (unlikely(!tty)) { 8246a3ff25bSJag Raman pr_err("VCC: hangup: Invalid TTY handle\n"); 8256a3ff25bSJag Raman return; 8266a3ff25bSJag Raman } 8276a3ff25bSJag Raman 8286a3ff25bSJag Raman port = vcc_get_ne(tty->index); 8296a3ff25bSJag Raman if (unlikely(!port)) { 8306a3ff25bSJag Raman pr_err("VCC: hangup: Failed to find VCC port\n"); 8316a3ff25bSJag Raman return; 8326a3ff25bSJag Raman } 8336a3ff25bSJag Raman 8346a3ff25bSJag Raman if (unlikely(!tty->port)) { 8356a3ff25bSJag Raman pr_err("VCC: hangup: TTY port not found\n"); 8366a3ff25bSJag Raman vcc_put(port, false); 8376a3ff25bSJag Raman return; 8386a3ff25bSJag Raman } 8396a3ff25bSJag Raman 8406a3ff25bSJag Raman vcc_ldc_hup(port); 8416a3ff25bSJag Raman 8426a3ff25bSJag Raman vcc_put(port, false); 8436a3ff25bSJag Raman 8446a3ff25bSJag Raman tty_port_hangup(tty->port); 8456a3ff25bSJag Raman } 8466a3ff25bSJag Raman 847*cf045260SJag Raman static int vcc_write(struct tty_struct *tty, const unsigned char *buf, 848*cf045260SJag Raman int count) 849*cf045260SJag Raman { 850*cf045260SJag Raman struct vcc_port *port; 851*cf045260SJag Raman struct vio_vcc *pkt; 852*cf045260SJag Raman unsigned long flags; 853*cf045260SJag Raman int total_sent = 0; 854*cf045260SJag Raman int tosend = 0; 855*cf045260SJag Raman int rv = -EINVAL; 856*cf045260SJag Raman 857*cf045260SJag Raman if (unlikely(!tty)) { 858*cf045260SJag Raman pr_err("VCC: write: Invalid TTY handle\n"); 859*cf045260SJag Raman return -ENXIO; 860*cf045260SJag Raman } 861*cf045260SJag Raman 862*cf045260SJag Raman port = vcc_get_ne(tty->index); 863*cf045260SJag Raman if (unlikely(!port)) { 864*cf045260SJag Raman pr_err("VCC: write: Failed to find VCC port"); 865*cf045260SJag Raman return -ENODEV; 866*cf045260SJag Raman } 867*cf045260SJag Raman 868*cf045260SJag Raman spin_lock_irqsave(&port->lock, flags); 869*cf045260SJag Raman 870*cf045260SJag Raman pkt = &port->buffer; 871*cf045260SJag Raman pkt->tag.type = VIO_TYPE_DATA; 872*cf045260SJag Raman 873*cf045260SJag Raman while (count > 0) { 874*cf045260SJag Raman /* Minimum of data to write and space available */ 875*cf045260SJag Raman tosend = min(count, (VCC_BUFF_LEN - port->chars_in_buffer)); 876*cf045260SJag Raman 877*cf045260SJag Raman if (!tosend) 878*cf045260SJag Raman break; 879*cf045260SJag Raman 880*cf045260SJag Raman memcpy(&pkt->data[port->chars_in_buffer], &buf[total_sent], 881*cf045260SJag Raman tosend); 882*cf045260SJag Raman port->chars_in_buffer += tosend; 883*cf045260SJag Raman pkt->tag.stype = tosend; 884*cf045260SJag Raman 885*cf045260SJag Raman vccdbg("TAG [%02x:%02x:%04x:%08x]\n", pkt->tag.type, 886*cf045260SJag Raman pkt->tag.stype, pkt->tag.stype_env, pkt->tag.sid); 887*cf045260SJag Raman vccdbg("DATA [%s]\n", pkt->data); 888*cf045260SJag Raman vccdbgl(port->vio.lp); 889*cf045260SJag Raman 890*cf045260SJag Raman /* Since we know we have enough room in VCC buffer for tosend 891*cf045260SJag Raman * we record that it was sent regardless of whether the 892*cf045260SJag Raman * hypervisor actually took it because we have it buffered. 893*cf045260SJag Raman */ 894*cf045260SJag Raman rv = ldc_write(port->vio.lp, pkt, (VIO_TAG_SIZE + tosend)); 895*cf045260SJag Raman vccdbg("VCC: write: ldc_write(%d)=%d\n", 896*cf045260SJag Raman (VIO_TAG_SIZE + tosend), rv); 897*cf045260SJag Raman 898*cf045260SJag Raman total_sent += tosend; 899*cf045260SJag Raman count -= tosend; 900*cf045260SJag Raman if (rv < 0) { 901*cf045260SJag Raman vcc_kick_tx(port); 902*cf045260SJag Raman break; 903*cf045260SJag Raman } 904*cf045260SJag Raman 905*cf045260SJag Raman port->chars_in_buffer = 0; 906*cf045260SJag Raman } 907*cf045260SJag Raman 908*cf045260SJag Raman spin_unlock_irqrestore(&port->lock, flags); 909*cf045260SJag Raman 910*cf045260SJag Raman vcc_put(port, false); 911*cf045260SJag Raman 912*cf045260SJag Raman vccdbg("VCC: write: total=%d rv=%d", total_sent, rv); 913*cf045260SJag Raman 914*cf045260SJag Raman return total_sent ? total_sent : rv; 915*cf045260SJag Raman } 916*cf045260SJag Raman 917*cf045260SJag Raman static int vcc_write_room(struct tty_struct *tty) 918*cf045260SJag Raman { 919*cf045260SJag Raman struct vcc_port *port; 920*cf045260SJag Raman u64 num; 921*cf045260SJag Raman 922*cf045260SJag Raman if (unlikely(!tty)) { 923*cf045260SJag Raman pr_err("VCC: write_room: Invalid TTY handle\n"); 924*cf045260SJag Raman return -ENXIO; 925*cf045260SJag Raman } 926*cf045260SJag Raman 927*cf045260SJag Raman port = vcc_get_ne(tty->index); 928*cf045260SJag Raman if (unlikely(!port)) { 929*cf045260SJag Raman pr_err("VCC: write_room: Failed to find VCC port\n"); 930*cf045260SJag Raman return -ENODEV; 931*cf045260SJag Raman } 932*cf045260SJag Raman 933*cf045260SJag Raman num = VCC_BUFF_LEN - port->chars_in_buffer; 934*cf045260SJag Raman 935*cf045260SJag Raman vcc_put(port, false); 936*cf045260SJag Raman 937*cf045260SJag Raman return num; 938*cf045260SJag Raman } 939*cf045260SJag Raman 94063a71744SJag Raman static const struct tty_operations vcc_ops = { 94163a71744SJag Raman .open = vcc_open, 94263a71744SJag Raman .close = vcc_close, 9436a3ff25bSJag Raman .hangup = vcc_hangup, 944*cf045260SJag Raman .write = vcc_write, 945*cf045260SJag Raman .write_room = vcc_write_room, 94663a71744SJag Raman }; 947ce808b74SJag Raman 948ce808b74SJag Raman #define VCC_TTY_FLAGS (TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_REAL_RAW) 949ce808b74SJag Raman 950ce808b74SJag Raman static int vcc_tty_init(void) 951ce808b74SJag Raman { 952ce808b74SJag Raman int rv; 953ce808b74SJag Raman 954ce808b74SJag Raman pr_info("VCC: %s\n", version); 955ce808b74SJag Raman 956ce808b74SJag Raman vcc_tty_driver = tty_alloc_driver(VCC_MAX_PORTS, VCC_TTY_FLAGS); 957ce808b74SJag Raman if (!vcc_tty_driver) { 958ce808b74SJag Raman pr_err("VCC: TTY driver alloc failed\n"); 959ce808b74SJag Raman return -ENOMEM; 960ce808b74SJag Raman } 961ce808b74SJag Raman 962ce808b74SJag Raman vcc_tty_driver->driver_name = vcc_driver_name; 963ce808b74SJag Raman vcc_tty_driver->name = vcc_device_node; 964ce808b74SJag Raman 965ce808b74SJag Raman vcc_tty_driver->minor_start = VCC_MINOR_START; 966ce808b74SJag Raman vcc_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; 967ce808b74SJag Raman vcc_tty_driver->init_termios = vcc_tty_termios; 968ce808b74SJag Raman 969ce808b74SJag Raman tty_set_operations(vcc_tty_driver, &vcc_ops); 970ce808b74SJag Raman 971ce808b74SJag Raman rv = tty_register_driver(vcc_tty_driver); 972ce808b74SJag Raman if (rv) { 973ce808b74SJag Raman pr_err("VCC: TTY driver registration failed\n"); 974ce808b74SJag Raman put_tty_driver(vcc_tty_driver); 975ce808b74SJag Raman vcc_tty_driver = NULL; 976ce808b74SJag Raman return rv; 977ce808b74SJag Raman } 978ce808b74SJag Raman 979ce808b74SJag Raman vccdbg("VCC: TTY driver registered\n"); 980ce808b74SJag Raman 981ce808b74SJag Raman return 0; 982ce808b74SJag Raman } 983ce808b74SJag Raman 984ce808b74SJag Raman static void vcc_tty_exit(void) 985ce808b74SJag Raman { 986ce808b74SJag Raman tty_unregister_driver(vcc_tty_driver); 987ce808b74SJag Raman put_tty_driver(vcc_tty_driver); 988ce808b74SJag Raman vccdbg("VCC: TTY driver unregistered\n"); 989ce808b74SJag Raman 990ce808b74SJag Raman vcc_tty_driver = NULL; 991ce808b74SJag Raman } 992ce808b74SJag Raman 99355bd2133SJag Raman static int __init vcc_init(void) 99455bd2133SJag Raman { 995ce808b74SJag Raman int rv; 996ce808b74SJag Raman 997ce808b74SJag Raman rv = vcc_tty_init(); 998ce808b74SJag Raman if (rv) { 999ce808b74SJag Raman pr_err("VCC: TTY init failed\n"); 1000ce808b74SJag Raman return rv; 1001ce808b74SJag Raman } 1002ce808b74SJag Raman 10035d171050SJag Raman rv = vio_register_driver(&vcc_driver); 10045d171050SJag Raman if (rv) { 10055d171050SJag Raman pr_err("VCC: VIO driver registration failed\n"); 10065d171050SJag Raman vcc_tty_exit(); 10075d171050SJag Raman } else { 10085d171050SJag Raman vccdbg("VCC: VIO driver registered successfully\n"); 10095d171050SJag Raman } 10105d171050SJag Raman 1011ce808b74SJag Raman return rv; 101255bd2133SJag Raman } 101355bd2133SJag Raman 101455bd2133SJag Raman static void __exit vcc_exit(void) 101555bd2133SJag Raman { 10165d171050SJag Raman vio_unregister_driver(&vcc_driver); 10175d171050SJag Raman vccdbg("VCC: VIO driver unregistered\n"); 1018ce808b74SJag Raman vcc_tty_exit(); 1019ce808b74SJag Raman vccdbg("VCC: TTY driver unregistered\n"); 102055bd2133SJag Raman } 102155bd2133SJag Raman 102255bd2133SJag Raman module_init(vcc_init); 102355bd2133SJag Raman module_exit(vcc_exit); 1024