14786d2efSLu Baolu // SPDX-License-Identifier: GPL-2.0 20e1acecaSLee Jones /* 3dfba2174SLu Baolu * xhci-dbgtty.c - tty glue for xHCI debug capability 4dfba2174SLu Baolu * 5dfba2174SLu Baolu * Copyright (C) 2017 Intel Corporation 6dfba2174SLu Baolu * 7dfba2174SLu Baolu * Author: Lu Baolu <baolu.lu@linux.intel.com> 8dfba2174SLu Baolu */ 9dfba2174SLu Baolu 10dfba2174SLu Baolu #include <linux/slab.h> 11dfba2174SLu Baolu #include <linux/tty.h> 12dfba2174SLu Baolu #include <linux/tty_flip.h> 13e1ec140fSMathias Nyman #include <linux/idr.h> 14dfba2174SLu Baolu 15dfba2174SLu Baolu #include "xhci.h" 16dfba2174SLu Baolu #include "xhci-dbgcap.h" 17dfba2174SLu Baolu 184521f161SMathias Nyman static struct tty_driver *dbc_tty_driver; 19e1ec140fSMathias Nyman static struct idr dbc_tty_minors; 20e1ec140fSMathias Nyman static DEFINE_MUTEX(dbc_tty_minors_lock); 214521f161SMathias Nyman 229a360a7cSMathias Nyman static inline struct dbc_port *dbc_to_port(struct xhci_dbc *dbc) 239a360a7cSMathias Nyman { 249a360a7cSMathias Nyman return dbc->priv; 259a360a7cSMathias Nyman } 269a360a7cSMathias Nyman 27*c691c2adSMathias Nyman static unsigned int 28*c691c2adSMathias Nyman dbc_kfifo_to_req(struct dbc_port *port, char *packet) 29*c691c2adSMathias Nyman { 30*c691c2adSMathias Nyman unsigned int len; 31*c691c2adSMathias Nyman 32*c691c2adSMathias Nyman len = kfifo_len(&port->port.xmit_fifo); 33*c691c2adSMathias Nyman 34*c691c2adSMathias Nyman if (len == 0) 35*c691c2adSMathias Nyman return 0; 36*c691c2adSMathias Nyman 37*c691c2adSMathias Nyman len = min(len, DBC_MAX_PACKET); 38*c691c2adSMathias Nyman 39*c691c2adSMathias Nyman if (port->tx_boundary) 40*c691c2adSMathias Nyman len = min(port->tx_boundary, len); 41*c691c2adSMathias Nyman 42*c691c2adSMathias Nyman len = kfifo_out(&port->port.xmit_fifo, packet, len); 43*c691c2adSMathias Nyman 44*c691c2adSMathias Nyman if (port->tx_boundary) 45*c691c2adSMathias Nyman port->tx_boundary -= len; 46*c691c2adSMathias Nyman 47*c691c2adSMathias Nyman return len; 48*c691c2adSMathias Nyman } 49*c691c2adSMathias Nyman 50dfba2174SLu Baolu static int dbc_start_tx(struct dbc_port *port) 51dfba2174SLu Baolu __releases(&port->port_lock) 52dfba2174SLu Baolu __acquires(&port->port_lock) 53dfba2174SLu Baolu { 54dfba2174SLu Baolu int len; 55dfba2174SLu Baolu struct dbc_request *req; 56dfba2174SLu Baolu int status = 0; 57dfba2174SLu Baolu bool do_tty_wake = false; 58dfba2174SLu Baolu struct list_head *pool = &port->write_pool; 59dfba2174SLu Baolu 60dfba2174SLu Baolu while (!list_empty(pool)) { 61dfba2174SLu Baolu req = list_entry(pool->next, struct dbc_request, list_pool); 62*c691c2adSMathias Nyman len = dbc_kfifo_to_req(port, req->buf); 63dfba2174SLu Baolu if (len == 0) 64dfba2174SLu Baolu break; 65dfba2174SLu Baolu do_tty_wake = true; 66dfba2174SLu Baolu 67dfba2174SLu Baolu req->length = len; 68dfba2174SLu Baolu list_del(&req->list_pool); 69dfba2174SLu Baolu 70dfba2174SLu Baolu spin_unlock(&port->port_lock); 71e0aa56dcSMathias Nyman status = dbc_ep_queue(req); 72dfba2174SLu Baolu spin_lock(&port->port_lock); 73dfba2174SLu Baolu 74dfba2174SLu Baolu if (status) { 75dfba2174SLu Baolu list_add(&req->list_pool, pool); 76dfba2174SLu Baolu break; 77dfba2174SLu Baolu } 78dfba2174SLu Baolu } 79dfba2174SLu Baolu 80dfba2174SLu Baolu if (do_tty_wake && port->port.tty) 81dfba2174SLu Baolu tty_wakeup(port->port.tty); 82dfba2174SLu Baolu 83dfba2174SLu Baolu return status; 84dfba2174SLu Baolu } 85dfba2174SLu Baolu 86dfba2174SLu Baolu static void dbc_start_rx(struct dbc_port *port) 87dfba2174SLu Baolu __releases(&port->port_lock) 88dfba2174SLu Baolu __acquires(&port->port_lock) 89dfba2174SLu Baolu { 90dfba2174SLu Baolu struct dbc_request *req; 91dfba2174SLu Baolu int status; 92dfba2174SLu Baolu struct list_head *pool = &port->read_pool; 93dfba2174SLu Baolu 94dfba2174SLu Baolu while (!list_empty(pool)) { 95dfba2174SLu Baolu if (!port->port.tty) 96dfba2174SLu Baolu break; 97dfba2174SLu Baolu 98dfba2174SLu Baolu req = list_entry(pool->next, struct dbc_request, list_pool); 99dfba2174SLu Baolu list_del(&req->list_pool); 100dfba2174SLu Baolu req->length = DBC_MAX_PACKET; 101dfba2174SLu Baolu 102dfba2174SLu Baolu spin_unlock(&port->port_lock); 103e0aa56dcSMathias Nyman status = dbc_ep_queue(req); 104dfba2174SLu Baolu spin_lock(&port->port_lock); 105dfba2174SLu Baolu 106dfba2174SLu Baolu if (status) { 107dfba2174SLu Baolu list_add(&req->list_pool, pool); 108dfba2174SLu Baolu break; 109dfba2174SLu Baolu } 110dfba2174SLu Baolu } 111dfba2174SLu Baolu } 112dfba2174SLu Baolu 113dfba2174SLu Baolu static void 114f39f3afdSMathias Nyman dbc_read_complete(struct xhci_dbc *dbc, struct dbc_request *req) 115dfba2174SLu Baolu { 116a098dc8bSLu Baolu unsigned long flags; 1179a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc); 118dfba2174SLu Baolu 119a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 120dfba2174SLu Baolu list_add_tail(&req->list_pool, &port->read_queue); 121dfba2174SLu Baolu tasklet_schedule(&port->push); 122a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 123dfba2174SLu Baolu } 124dfba2174SLu Baolu 125f39f3afdSMathias Nyman static void dbc_write_complete(struct xhci_dbc *dbc, struct dbc_request *req) 126dfba2174SLu Baolu { 127a098dc8bSLu Baolu unsigned long flags; 1289a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc); 129dfba2174SLu Baolu 130a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 131dfba2174SLu Baolu list_add(&req->list_pool, &port->write_pool); 132dfba2174SLu Baolu switch (req->status) { 133dfba2174SLu Baolu case 0: 134dfba2174SLu Baolu dbc_start_tx(port); 135dfba2174SLu Baolu break; 136dfba2174SLu Baolu case -ESHUTDOWN: 137dfba2174SLu Baolu break; 138dfba2174SLu Baolu default: 139f39f3afdSMathias Nyman dev_warn(dbc->dev, "unexpected write complete status %d\n", 140dfba2174SLu Baolu req->status); 141dfba2174SLu Baolu break; 142dfba2174SLu Baolu } 143a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 144dfba2174SLu Baolu } 145dfba2174SLu Baolu 146e0aa56dcSMathias Nyman static void xhci_dbc_free_req(struct dbc_request *req) 147dfba2174SLu Baolu { 148dfba2174SLu Baolu kfree(req->buf); 149e0aa56dcSMathias Nyman dbc_free_request(req); 150dfba2174SLu Baolu } 151dfba2174SLu Baolu 152dfba2174SLu Baolu static int 153e0aa56dcSMathias Nyman xhci_dbc_alloc_requests(struct xhci_dbc *dbc, unsigned int direction, 154e0aa56dcSMathias Nyman struct list_head *head, 155f39f3afdSMathias Nyman void (*fn)(struct xhci_dbc *, struct dbc_request *)) 156dfba2174SLu Baolu { 157dfba2174SLu Baolu int i; 158dfba2174SLu Baolu struct dbc_request *req; 159dfba2174SLu Baolu 160dfba2174SLu Baolu for (i = 0; i < DBC_QUEUE_SIZE; i++) { 161e0aa56dcSMathias Nyman req = dbc_alloc_request(dbc, direction, GFP_KERNEL); 162dfba2174SLu Baolu if (!req) 163dfba2174SLu Baolu break; 164dfba2174SLu Baolu 165dfba2174SLu Baolu req->length = DBC_MAX_PACKET; 166dfba2174SLu Baolu req->buf = kmalloc(req->length, GFP_KERNEL); 167dfba2174SLu Baolu if (!req->buf) { 168e0aa56dcSMathias Nyman dbc_free_request(req); 169dfba2174SLu Baolu break; 170dfba2174SLu Baolu } 171dfba2174SLu Baolu 172dfba2174SLu Baolu req->complete = fn; 173dfba2174SLu Baolu list_add_tail(&req->list_pool, head); 174dfba2174SLu Baolu } 175dfba2174SLu Baolu 176dfba2174SLu Baolu return list_empty(head) ? -ENOMEM : 0; 177dfba2174SLu Baolu } 178dfba2174SLu Baolu 179dfba2174SLu Baolu static void 180e0aa56dcSMathias Nyman xhci_dbc_free_requests(struct list_head *head) 181dfba2174SLu Baolu { 182dfba2174SLu Baolu struct dbc_request *req; 183dfba2174SLu Baolu 184dfba2174SLu Baolu while (!list_empty(head)) { 185dfba2174SLu Baolu req = list_entry(head->next, struct dbc_request, list_pool); 186dfba2174SLu Baolu list_del(&req->list_pool); 187e0aa56dcSMathias Nyman xhci_dbc_free_req(req); 188dfba2174SLu Baolu } 189dfba2174SLu Baolu } 190dfba2174SLu Baolu 191dfba2174SLu Baolu static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty) 192dfba2174SLu Baolu { 193e1ec140fSMathias Nyman struct dbc_port *port; 194e1ec140fSMathias Nyman 195e1ec140fSMathias Nyman mutex_lock(&dbc_tty_minors_lock); 196e1ec140fSMathias Nyman port = idr_find(&dbc_tty_minors, tty->index); 197e1ec140fSMathias Nyman mutex_unlock(&dbc_tty_minors_lock); 198e1ec140fSMathias Nyman 199e1ec140fSMathias Nyman if (!port) 200e1ec140fSMathias Nyman return -ENXIO; 201dfba2174SLu Baolu 202dfba2174SLu Baolu tty->driver_data = port; 203dfba2174SLu Baolu 204dfba2174SLu Baolu return tty_port_install(&port->port, driver, tty); 205dfba2174SLu Baolu } 206dfba2174SLu Baolu 207dfba2174SLu Baolu static int dbc_tty_open(struct tty_struct *tty, struct file *file) 208dfba2174SLu Baolu { 209dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 210dfba2174SLu Baolu 211dfba2174SLu Baolu return tty_port_open(&port->port, tty, file); 212dfba2174SLu Baolu } 213dfba2174SLu Baolu 214dfba2174SLu Baolu static void dbc_tty_close(struct tty_struct *tty, struct file *file) 215dfba2174SLu Baolu { 216dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 217dfba2174SLu Baolu 218dfba2174SLu Baolu tty_port_close(&port->port, tty, file); 219dfba2174SLu Baolu } 220dfba2174SLu Baolu 22195713967SJiri Slaby (SUSE) static ssize_t dbc_tty_write(struct tty_struct *tty, const u8 *buf, 22295713967SJiri Slaby (SUSE) size_t count) 223dfba2174SLu Baolu { 224dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 225dfba2174SLu Baolu unsigned long flags; 226*c691c2adSMathias Nyman unsigned int written = 0; 227dfba2174SLu Baolu 228dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 229*c691c2adSMathias Nyman 230*c691c2adSMathias Nyman /* 231*c691c2adSMathias Nyman * Treat tty write as one usb transfer. Make sure the writes are turned 232*c691c2adSMathias Nyman * into TRB request having the same size boundaries as the tty writes. 233*c691c2adSMathias Nyman * Don't add data to kfifo before previous write is turned into TRBs 234*c691c2adSMathias Nyman */ 235*c691c2adSMathias Nyman if (port->tx_boundary) { 236*c691c2adSMathias Nyman spin_unlock_irqrestore(&port->port_lock, flags); 237*c691c2adSMathias Nyman return 0; 238*c691c2adSMathias Nyman } 239*c691c2adSMathias Nyman 240*c691c2adSMathias Nyman if (count) { 241*c691c2adSMathias Nyman written = kfifo_in(&port->port.xmit_fifo, buf, count); 242*c691c2adSMathias Nyman 243*c691c2adSMathias Nyman if (written == count) 244*c691c2adSMathias Nyman port->tx_boundary = kfifo_len(&port->port.xmit_fifo); 245*c691c2adSMathias Nyman 246dfba2174SLu Baolu dbc_start_tx(port); 247*c691c2adSMathias Nyman } 248*c691c2adSMathias Nyman 249dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 250dfba2174SLu Baolu 251*c691c2adSMathias Nyman return written; 252dfba2174SLu Baolu } 253dfba2174SLu Baolu 254dcaafbe6SJiri Slaby (SUSE) static int dbc_tty_put_char(struct tty_struct *tty, u8 ch) 255dfba2174SLu Baolu { 256dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 257dfba2174SLu Baolu unsigned long flags; 258dfba2174SLu Baolu int status; 259dfba2174SLu Baolu 260dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 2617cbcb40dSJiri Slaby (SUSE) status = kfifo_put(&port->port.xmit_fifo, ch); 262dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 263dfba2174SLu Baolu 264dfba2174SLu Baolu return status; 265dfba2174SLu Baolu } 266dfba2174SLu Baolu 267dfba2174SLu Baolu static void dbc_tty_flush_chars(struct tty_struct *tty) 268dfba2174SLu Baolu { 269dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 270dfba2174SLu Baolu unsigned long flags; 271dfba2174SLu Baolu 272dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 273dfba2174SLu Baolu dbc_start_tx(port); 274dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 275dfba2174SLu Baolu } 276dfba2174SLu Baolu 27703b3b1a2SJiri Slaby static unsigned int dbc_tty_write_room(struct tty_struct *tty) 278dfba2174SLu Baolu { 279dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 280dfba2174SLu Baolu unsigned long flags; 28103b3b1a2SJiri Slaby unsigned int room; 282dfba2174SLu Baolu 283dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 2847cbcb40dSJiri Slaby (SUSE) room = kfifo_avail(&port->port.xmit_fifo); 285*c691c2adSMathias Nyman 286*c691c2adSMathias Nyman if (port->tx_boundary) 287*c691c2adSMathias Nyman room = 0; 288*c691c2adSMathias Nyman 289dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 290dfba2174SLu Baolu 291dfba2174SLu Baolu return room; 292dfba2174SLu Baolu } 293dfba2174SLu Baolu 294fff4ef17SJiri Slaby static unsigned int dbc_tty_chars_in_buffer(struct tty_struct *tty) 295dfba2174SLu Baolu { 296dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 297dfba2174SLu Baolu unsigned long flags; 298fff4ef17SJiri Slaby unsigned int chars; 299dfba2174SLu Baolu 300dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 3017cbcb40dSJiri Slaby (SUSE) chars = kfifo_len(&port->port.xmit_fifo); 302dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 303dfba2174SLu Baolu 304dfba2174SLu Baolu return chars; 305dfba2174SLu Baolu } 306dfba2174SLu Baolu 307dfba2174SLu Baolu static void dbc_tty_unthrottle(struct tty_struct *tty) 308dfba2174SLu Baolu { 309dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 310dfba2174SLu Baolu unsigned long flags; 311dfba2174SLu Baolu 312dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 313dfba2174SLu Baolu tasklet_schedule(&port->push); 314dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 315dfba2174SLu Baolu } 316dfba2174SLu Baolu 317dfba2174SLu Baolu static const struct tty_operations dbc_tty_ops = { 318dfba2174SLu Baolu .install = dbc_tty_install, 319dfba2174SLu Baolu .open = dbc_tty_open, 320dfba2174SLu Baolu .close = dbc_tty_close, 321dfba2174SLu Baolu .write = dbc_tty_write, 322dfba2174SLu Baolu .put_char = dbc_tty_put_char, 323dfba2174SLu Baolu .flush_chars = dbc_tty_flush_chars, 324dfba2174SLu Baolu .write_room = dbc_tty_write_room, 325dfba2174SLu Baolu .chars_in_buffer = dbc_tty_chars_in_buffer, 326dfba2174SLu Baolu .unthrottle = dbc_tty_unthrottle, 327dfba2174SLu Baolu }; 328dfba2174SLu Baolu 32981d324cdSAllen Pais static void dbc_rx_push(struct tasklet_struct *t) 330dfba2174SLu Baolu { 331dfba2174SLu Baolu struct dbc_request *req; 332dfba2174SLu Baolu struct tty_struct *tty; 333a098dc8bSLu Baolu unsigned long flags; 334dfba2174SLu Baolu bool do_push = false; 335dfba2174SLu Baolu bool disconnect = false; 33681d324cdSAllen Pais struct dbc_port *port = from_tasklet(port, t, push); 337dfba2174SLu Baolu struct list_head *queue = &port->read_queue; 338dfba2174SLu Baolu 339a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 340dfba2174SLu Baolu tty = port->port.tty; 341dfba2174SLu Baolu while (!list_empty(queue)) { 342dfba2174SLu Baolu req = list_first_entry(queue, struct dbc_request, list_pool); 343dfba2174SLu Baolu 344dfba2174SLu Baolu if (tty && tty_throttled(tty)) 345dfba2174SLu Baolu break; 346dfba2174SLu Baolu 347dfba2174SLu Baolu switch (req->status) { 348dfba2174SLu Baolu case 0: 349dfba2174SLu Baolu break; 350dfba2174SLu Baolu case -ESHUTDOWN: 351dfba2174SLu Baolu disconnect = true; 352dfba2174SLu Baolu break; 353dfba2174SLu Baolu default: 354dfba2174SLu Baolu pr_warn("ttyDBC0: unexpected RX status %d\n", 355dfba2174SLu Baolu req->status); 356dfba2174SLu Baolu break; 357dfba2174SLu Baolu } 358dfba2174SLu Baolu 359dfba2174SLu Baolu if (req->actual) { 360dfba2174SLu Baolu char *packet = req->buf; 361dfba2174SLu Baolu unsigned int n, size = req->actual; 362dfba2174SLu Baolu int count; 363dfba2174SLu Baolu 364dfba2174SLu Baolu n = port->n_read; 365dfba2174SLu Baolu if (n) { 366dfba2174SLu Baolu packet += n; 367dfba2174SLu Baolu size -= n; 368dfba2174SLu Baolu } 369dfba2174SLu Baolu 370dfba2174SLu Baolu count = tty_insert_flip_string(&port->port, packet, 371dfba2174SLu Baolu size); 372dfba2174SLu Baolu if (count) 373dfba2174SLu Baolu do_push = true; 374dfba2174SLu Baolu if (count != size) { 375dfba2174SLu Baolu port->n_read += count; 376dfba2174SLu Baolu break; 377dfba2174SLu Baolu } 378dfba2174SLu Baolu port->n_read = 0; 379dfba2174SLu Baolu } 380dfba2174SLu Baolu 381dfba2174SLu Baolu list_move(&req->list_pool, &port->read_pool); 382dfba2174SLu Baolu } 383dfba2174SLu Baolu 384dfba2174SLu Baolu if (do_push) 385dfba2174SLu Baolu tty_flip_buffer_push(&port->port); 386dfba2174SLu Baolu 387dfba2174SLu Baolu if (!list_empty(queue) && tty) { 388dfba2174SLu Baolu if (!tty_throttled(tty)) { 389dfba2174SLu Baolu if (do_push) 390dfba2174SLu Baolu tasklet_schedule(&port->push); 391dfba2174SLu Baolu else 392dfba2174SLu Baolu pr_warn("ttyDBC0: RX not scheduled?\n"); 393dfba2174SLu Baolu } 394dfba2174SLu Baolu } 395dfba2174SLu Baolu 396dfba2174SLu Baolu if (!disconnect) 397dfba2174SLu Baolu dbc_start_rx(port); 398dfba2174SLu Baolu 399a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 400dfba2174SLu Baolu } 401dfba2174SLu Baolu 402dfba2174SLu Baolu static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty) 403dfba2174SLu Baolu { 404a098dc8bSLu Baolu unsigned long flags; 405dfba2174SLu Baolu struct dbc_port *port = container_of(_port, struct dbc_port, port); 406dfba2174SLu Baolu 407a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 408dfba2174SLu Baolu dbc_start_rx(port); 409a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 410dfba2174SLu Baolu 411dfba2174SLu Baolu return 0; 412dfba2174SLu Baolu } 413dfba2174SLu Baolu 414dfba2174SLu Baolu static const struct tty_port_operations dbc_port_ops = { 415dfba2174SLu Baolu .activate = dbc_port_activate, 416dfba2174SLu Baolu }; 417dfba2174SLu Baolu 418dfba2174SLu Baolu static void 41991aaf974SMathias Nyman xhci_dbc_tty_init_port(struct xhci_dbc *dbc, struct dbc_port *port) 420dfba2174SLu Baolu { 421dfba2174SLu Baolu tty_port_init(&port->port); 422dfba2174SLu Baolu spin_lock_init(&port->port_lock); 42381d324cdSAllen Pais tasklet_setup(&port->push, dbc_rx_push); 424dfba2174SLu Baolu INIT_LIST_HEAD(&port->read_pool); 425dfba2174SLu Baolu INIT_LIST_HEAD(&port->read_queue); 426dfba2174SLu Baolu INIT_LIST_HEAD(&port->write_pool); 427dfba2174SLu Baolu 428dfba2174SLu Baolu port->port.ops = &dbc_port_ops; 429dfba2174SLu Baolu port->n_read = 0; 430dfba2174SLu Baolu } 431dfba2174SLu Baolu 432dfba2174SLu Baolu static void 433dfba2174SLu Baolu xhci_dbc_tty_exit_port(struct dbc_port *port) 434dfba2174SLu Baolu { 435dfba2174SLu Baolu tasklet_kill(&port->push); 436dfba2174SLu Baolu tty_port_destroy(&port->port); 437dfba2174SLu Baolu } 438dfba2174SLu Baolu 43925252919SWei Yongjun static int xhci_dbc_tty_register_device(struct xhci_dbc *dbc) 440dfba2174SLu Baolu { 441dfba2174SLu Baolu int ret; 442dfba2174SLu Baolu struct device *tty_dev; 4439a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc); 444dfba2174SLu Baolu 445688915b1SMathias Nyman if (port->registered) 446688915b1SMathias Nyman return -EBUSY; 447688915b1SMathias Nyman 44891aaf974SMathias Nyman xhci_dbc_tty_init_port(dbc, port); 449dfba2174SLu Baolu 450e1ec140fSMathias Nyman mutex_lock(&dbc_tty_minors_lock); 451e1ec140fSMathias Nyman port->minor = idr_alloc(&dbc_tty_minors, port, 0, 64, GFP_KERNEL); 452e1ec140fSMathias Nyman mutex_unlock(&dbc_tty_minors_lock); 453e1ec140fSMathias Nyman 454e1ec140fSMathias Nyman if (port->minor < 0) { 455e1ec140fSMathias Nyman ret = port->minor; 456e1ec140fSMathias Nyman goto err_idr; 457e1ec140fSMathias Nyman } 458e1ec140fSMathias Nyman 4597cbcb40dSJiri Slaby (SUSE) ret = kfifo_alloc(&port->port.xmit_fifo, DBC_WRITE_BUF_SIZE, 4607cbcb40dSJiri Slaby (SUSE) GFP_KERNEL); 461dfba2174SLu Baolu if (ret) 462880de403SJohan Hovold goto err_exit_port; 463dfba2174SLu Baolu 464e0aa56dcSMathias Nyman ret = xhci_dbc_alloc_requests(dbc, BULK_IN, &port->read_pool, 465dfba2174SLu Baolu dbc_read_complete); 466dfba2174SLu Baolu if (ret) 467880de403SJohan Hovold goto err_free_fifo; 468dfba2174SLu Baolu 469e0aa56dcSMathias Nyman ret = xhci_dbc_alloc_requests(dbc, BULK_OUT, &port->write_pool, 470dfba2174SLu Baolu dbc_write_complete); 471dfba2174SLu Baolu if (ret) 472880de403SJohan Hovold goto err_free_requests; 473880de403SJohan Hovold 474880de403SJohan Hovold tty_dev = tty_port_register_device(&port->port, 475e1ec140fSMathias Nyman dbc_tty_driver, port->minor, NULL); 476880de403SJohan Hovold if (IS_ERR(tty_dev)) { 477880de403SJohan Hovold ret = PTR_ERR(tty_dev); 478880de403SJohan Hovold goto err_free_requests; 479880de403SJohan Hovold } 480dfba2174SLu Baolu 481dfba2174SLu Baolu port->registered = true; 482dfba2174SLu Baolu 483dfba2174SLu Baolu return 0; 484dfba2174SLu Baolu 485880de403SJohan Hovold err_free_requests: 486e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_pool); 487e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->write_pool); 488880de403SJohan Hovold err_free_fifo: 4897cbcb40dSJiri Slaby (SUSE) kfifo_free(&port->port.xmit_fifo); 490880de403SJohan Hovold err_exit_port: 491e1ec140fSMathias Nyman idr_remove(&dbc_tty_minors, port->minor); 492e1ec140fSMathias Nyman err_idr: 493dfba2174SLu Baolu xhci_dbc_tty_exit_port(port); 494dfba2174SLu Baolu 495b396fa39SMathias Nyman dev_err(dbc->dev, "can't register tty port, err %d\n", ret); 496dfba2174SLu Baolu 497dfba2174SLu Baolu return ret; 498dfba2174SLu Baolu } 499dfba2174SLu Baolu 50025252919SWei Yongjun static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc) 501dfba2174SLu Baolu { 5029a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc); 503dfba2174SLu Baolu 504688915b1SMathias Nyman if (!port->registered) 505688915b1SMathias Nyman return; 506e1ec140fSMathias Nyman tty_unregister_device(dbc_tty_driver, port->minor); 507dfba2174SLu Baolu xhci_dbc_tty_exit_port(port); 508dfba2174SLu Baolu port->registered = false; 509dfba2174SLu Baolu 510e1ec140fSMathias Nyman mutex_lock(&dbc_tty_minors_lock); 511e1ec140fSMathias Nyman idr_remove(&dbc_tty_minors, port->minor); 512e1ec140fSMathias Nyman mutex_unlock(&dbc_tty_minors_lock); 513e1ec140fSMathias Nyman 5147cbcb40dSJiri Slaby (SUSE) kfifo_free(&port->port.xmit_fifo); 515e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_pool); 516e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_queue); 517e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->write_pool); 518dfba2174SLu Baolu } 5194521f161SMathias Nyman 5206ae6470bSMathias Nyman static const struct dbc_driver dbc_driver = { 5216ae6470bSMathias Nyman .configure = xhci_dbc_tty_register_device, 5226ae6470bSMathias Nyman .disconnect = xhci_dbc_tty_unregister_device, 5236ae6470bSMathias Nyman }; 5246ae6470bSMathias Nyman 5255ce036b9SMathias Nyman int xhci_dbc_tty_probe(struct device *dev, void __iomem *base, struct xhci_hcd *xhci) 5264521f161SMathias Nyman { 5275ce036b9SMathias Nyman struct xhci_dbc *dbc; 5289a360a7cSMathias Nyman struct dbc_port *port; 5294521f161SMathias Nyman int status; 5304521f161SMathias Nyman 5316aec5000SMathias Nyman if (!dbc_tty_driver) 5326aec5000SMathias Nyman return -ENODEV; 5334521f161SMathias Nyman 5349a360a7cSMathias Nyman port = kzalloc(sizeof(*port), GFP_KERNEL); 5356aec5000SMathias Nyman if (!port) 5366aec5000SMathias Nyman return -ENOMEM; 5376ae6470bSMathias Nyman 5385ce036b9SMathias Nyman dbc = xhci_alloc_dbc(dev, base, &dbc_driver); 539e1ec140fSMathias Nyman 5405ce036b9SMathias Nyman if (!dbc) { 5415ce036b9SMathias Nyman status = -ENOMEM; 5425ce036b9SMathias Nyman goto out2; 5435ce036b9SMathias Nyman } 5445ce036b9SMathias Nyman 5455ce036b9SMathias Nyman dbc->priv = port; 5465ce036b9SMathias Nyman 5475ce036b9SMathias Nyman /* get rid of xhci once this is a real driver binding to a device */ 5485ce036b9SMathias Nyman xhci->dbc = dbc; 5495ce036b9SMathias Nyman 5504521f161SMathias Nyman return 0; 5515ce036b9SMathias Nyman out2: 5525ce036b9SMathias Nyman kfree(port); 5536aec5000SMathias Nyman 5544521f161SMathias Nyman return status; 5554521f161SMathias Nyman } 5564521f161SMathias Nyman 5574521f161SMathias Nyman /* 5584521f161SMathias Nyman * undo what probe did, assume dbc is stopped already. 5594521f161SMathias Nyman * we also assume tty_unregister_device() is called before this 5604521f161SMathias Nyman */ 5614521f161SMathias Nyman void xhci_dbc_tty_remove(struct xhci_dbc *dbc) 5624521f161SMathias Nyman { 5639a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc); 5649a360a7cSMathias Nyman 5655ce036b9SMathias Nyman xhci_dbc_remove(dbc); 5669a360a7cSMathias Nyman kfree(port); 5674521f161SMathias Nyman } 5684521f161SMathias Nyman 5696aec5000SMathias Nyman int dbc_tty_init(void) 5704521f161SMathias Nyman { 5714521f161SMathias Nyman int ret; 5724521f161SMathias Nyman 573e1ec140fSMathias Nyman idr_init(&dbc_tty_minors); 574e1ec140fSMathias Nyman 575e1ec140fSMathias Nyman dbc_tty_driver = tty_alloc_driver(64, TTY_DRIVER_REAL_RAW | 5764521f161SMathias Nyman TTY_DRIVER_DYNAMIC_DEV); 577e1ec140fSMathias Nyman if (IS_ERR(dbc_tty_driver)) { 578e1ec140fSMathias Nyman idr_destroy(&dbc_tty_minors); 5794521f161SMathias Nyman return PTR_ERR(dbc_tty_driver); 580e1ec140fSMathias Nyman } 5814521f161SMathias Nyman 5824521f161SMathias Nyman dbc_tty_driver->driver_name = "dbc_serial"; 5834521f161SMathias Nyman dbc_tty_driver->name = "ttyDBC"; 5844521f161SMathias Nyman 5854521f161SMathias Nyman dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; 5864521f161SMathias Nyman dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL; 5874521f161SMathias Nyman dbc_tty_driver->init_termios = tty_std_termios; 5884521f161SMathias Nyman dbc_tty_driver->init_termios.c_cflag = 5894521f161SMathias Nyman B9600 | CS8 | CREAD | HUPCL | CLOCAL; 5904521f161SMathias Nyman dbc_tty_driver->init_termios.c_ispeed = 9600; 5914521f161SMathias Nyman dbc_tty_driver->init_termios.c_ospeed = 9600; 5924521f161SMathias Nyman 5934521f161SMathias Nyman tty_set_operations(dbc_tty_driver, &dbc_tty_ops); 5944521f161SMathias Nyman 5954521f161SMathias Nyman ret = tty_register_driver(dbc_tty_driver); 5964521f161SMathias Nyman if (ret) { 5974521f161SMathias Nyman pr_err("Can't register dbc tty driver\n"); 5989f90a4ddSJiri Slaby tty_driver_kref_put(dbc_tty_driver); 599e1ec140fSMathias Nyman idr_destroy(&dbc_tty_minors); 6004521f161SMathias Nyman } 601e1ec140fSMathias Nyman 6024521f161SMathias Nyman return ret; 6034521f161SMathias Nyman } 6044521f161SMathias Nyman 6056aec5000SMathias Nyman void dbc_tty_exit(void) 6064521f161SMathias Nyman { 6074521f161SMathias Nyman if (dbc_tty_driver) { 6084521f161SMathias Nyman tty_unregister_driver(dbc_tty_driver); 6099f90a4ddSJiri Slaby tty_driver_kref_put(dbc_tty_driver); 6104521f161SMathias Nyman dbc_tty_driver = NULL; 6114521f161SMathias Nyman } 612e1ec140fSMathias Nyman 613e1ec140fSMathias Nyman idr_destroy(&dbc_tty_minors); 6144521f161SMathias Nyman } 615