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 27dfba2174SLu Baolu static int dbc_start_tx(struct dbc_port *port) 28dfba2174SLu Baolu __releases(&port->port_lock) 29dfba2174SLu Baolu __acquires(&port->port_lock) 30dfba2174SLu Baolu { 31dfba2174SLu Baolu int len; 32dfba2174SLu Baolu struct dbc_request *req; 33dfba2174SLu Baolu int status = 0; 34dfba2174SLu Baolu bool do_tty_wake = false; 35dfba2174SLu Baolu struct list_head *pool = &port->write_pool; 36dfba2174SLu Baolu 37dfba2174SLu Baolu while (!list_empty(pool)) { 38dfba2174SLu Baolu req = list_entry(pool->next, struct dbc_request, list_pool); 39*7cbcb40dSJiri Slaby (SUSE) len = kfifo_out(&port->port.xmit_fifo, req->buf, DBC_MAX_PACKET); 40dfba2174SLu Baolu if (len == 0) 41dfba2174SLu Baolu break; 42dfba2174SLu Baolu do_tty_wake = true; 43dfba2174SLu Baolu 44dfba2174SLu Baolu req->length = len; 45dfba2174SLu Baolu list_del(&req->list_pool); 46dfba2174SLu Baolu 47dfba2174SLu Baolu spin_unlock(&port->port_lock); 48e0aa56dcSMathias Nyman status = dbc_ep_queue(req); 49dfba2174SLu Baolu spin_lock(&port->port_lock); 50dfba2174SLu Baolu 51dfba2174SLu Baolu if (status) { 52dfba2174SLu Baolu list_add(&req->list_pool, pool); 53dfba2174SLu Baolu break; 54dfba2174SLu Baolu } 55dfba2174SLu Baolu } 56dfba2174SLu Baolu 57dfba2174SLu Baolu if (do_tty_wake && port->port.tty) 58dfba2174SLu Baolu tty_wakeup(port->port.tty); 59dfba2174SLu Baolu 60dfba2174SLu Baolu return status; 61dfba2174SLu Baolu } 62dfba2174SLu Baolu 63dfba2174SLu Baolu static void dbc_start_rx(struct dbc_port *port) 64dfba2174SLu Baolu __releases(&port->port_lock) 65dfba2174SLu Baolu __acquires(&port->port_lock) 66dfba2174SLu Baolu { 67dfba2174SLu Baolu struct dbc_request *req; 68dfba2174SLu Baolu int status; 69dfba2174SLu Baolu struct list_head *pool = &port->read_pool; 70dfba2174SLu Baolu 71dfba2174SLu Baolu while (!list_empty(pool)) { 72dfba2174SLu Baolu if (!port->port.tty) 73dfba2174SLu Baolu break; 74dfba2174SLu Baolu 75dfba2174SLu Baolu req = list_entry(pool->next, struct dbc_request, list_pool); 76dfba2174SLu Baolu list_del(&req->list_pool); 77dfba2174SLu Baolu req->length = DBC_MAX_PACKET; 78dfba2174SLu Baolu 79dfba2174SLu Baolu spin_unlock(&port->port_lock); 80e0aa56dcSMathias Nyman status = dbc_ep_queue(req); 81dfba2174SLu Baolu spin_lock(&port->port_lock); 82dfba2174SLu Baolu 83dfba2174SLu Baolu if (status) { 84dfba2174SLu Baolu list_add(&req->list_pool, pool); 85dfba2174SLu Baolu break; 86dfba2174SLu Baolu } 87dfba2174SLu Baolu } 88dfba2174SLu Baolu } 89dfba2174SLu Baolu 90dfba2174SLu Baolu static void 91f39f3afdSMathias Nyman dbc_read_complete(struct xhci_dbc *dbc, struct dbc_request *req) 92dfba2174SLu Baolu { 93a098dc8bSLu Baolu unsigned long flags; 949a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc); 95dfba2174SLu Baolu 96a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 97dfba2174SLu Baolu list_add_tail(&req->list_pool, &port->read_queue); 98dfba2174SLu Baolu tasklet_schedule(&port->push); 99a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 100dfba2174SLu Baolu } 101dfba2174SLu Baolu 102f39f3afdSMathias Nyman static void dbc_write_complete(struct xhci_dbc *dbc, struct dbc_request *req) 103dfba2174SLu Baolu { 104a098dc8bSLu Baolu unsigned long flags; 1059a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc); 106dfba2174SLu Baolu 107a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 108dfba2174SLu Baolu list_add(&req->list_pool, &port->write_pool); 109dfba2174SLu Baolu switch (req->status) { 110dfba2174SLu Baolu case 0: 111dfba2174SLu Baolu dbc_start_tx(port); 112dfba2174SLu Baolu break; 113dfba2174SLu Baolu case -ESHUTDOWN: 114dfba2174SLu Baolu break; 115dfba2174SLu Baolu default: 116f39f3afdSMathias Nyman dev_warn(dbc->dev, "unexpected write complete status %d\n", 117dfba2174SLu Baolu req->status); 118dfba2174SLu Baolu break; 119dfba2174SLu Baolu } 120a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 121dfba2174SLu Baolu } 122dfba2174SLu Baolu 123e0aa56dcSMathias Nyman static void xhci_dbc_free_req(struct dbc_request *req) 124dfba2174SLu Baolu { 125dfba2174SLu Baolu kfree(req->buf); 126e0aa56dcSMathias Nyman dbc_free_request(req); 127dfba2174SLu Baolu } 128dfba2174SLu Baolu 129dfba2174SLu Baolu static int 130e0aa56dcSMathias Nyman xhci_dbc_alloc_requests(struct xhci_dbc *dbc, unsigned int direction, 131e0aa56dcSMathias Nyman struct list_head *head, 132f39f3afdSMathias Nyman void (*fn)(struct xhci_dbc *, struct dbc_request *)) 133dfba2174SLu Baolu { 134dfba2174SLu Baolu int i; 135dfba2174SLu Baolu struct dbc_request *req; 136dfba2174SLu Baolu 137dfba2174SLu Baolu for (i = 0; i < DBC_QUEUE_SIZE; i++) { 138e0aa56dcSMathias Nyman req = dbc_alloc_request(dbc, direction, GFP_KERNEL); 139dfba2174SLu Baolu if (!req) 140dfba2174SLu Baolu break; 141dfba2174SLu Baolu 142dfba2174SLu Baolu req->length = DBC_MAX_PACKET; 143dfba2174SLu Baolu req->buf = kmalloc(req->length, GFP_KERNEL); 144dfba2174SLu Baolu if (!req->buf) { 145e0aa56dcSMathias Nyman dbc_free_request(req); 146dfba2174SLu Baolu break; 147dfba2174SLu Baolu } 148dfba2174SLu Baolu 149dfba2174SLu Baolu req->complete = fn; 150dfba2174SLu Baolu list_add_tail(&req->list_pool, head); 151dfba2174SLu Baolu } 152dfba2174SLu Baolu 153dfba2174SLu Baolu return list_empty(head) ? -ENOMEM : 0; 154dfba2174SLu Baolu } 155dfba2174SLu Baolu 156dfba2174SLu Baolu static void 157e0aa56dcSMathias Nyman xhci_dbc_free_requests(struct list_head *head) 158dfba2174SLu Baolu { 159dfba2174SLu Baolu struct dbc_request *req; 160dfba2174SLu Baolu 161dfba2174SLu Baolu while (!list_empty(head)) { 162dfba2174SLu Baolu req = list_entry(head->next, struct dbc_request, list_pool); 163dfba2174SLu Baolu list_del(&req->list_pool); 164e0aa56dcSMathias Nyman xhci_dbc_free_req(req); 165dfba2174SLu Baolu } 166dfba2174SLu Baolu } 167dfba2174SLu Baolu 168dfba2174SLu Baolu static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty) 169dfba2174SLu Baolu { 170e1ec140fSMathias Nyman struct dbc_port *port; 171e1ec140fSMathias Nyman 172e1ec140fSMathias Nyman mutex_lock(&dbc_tty_minors_lock); 173e1ec140fSMathias Nyman port = idr_find(&dbc_tty_minors, tty->index); 174e1ec140fSMathias Nyman mutex_unlock(&dbc_tty_minors_lock); 175e1ec140fSMathias Nyman 176e1ec140fSMathias Nyman if (!port) 177e1ec140fSMathias Nyman return -ENXIO; 178dfba2174SLu Baolu 179dfba2174SLu Baolu tty->driver_data = port; 180dfba2174SLu Baolu 181dfba2174SLu Baolu return tty_port_install(&port->port, driver, tty); 182dfba2174SLu Baolu } 183dfba2174SLu Baolu 184dfba2174SLu Baolu static int dbc_tty_open(struct tty_struct *tty, struct file *file) 185dfba2174SLu Baolu { 186dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 187dfba2174SLu Baolu 188dfba2174SLu Baolu return tty_port_open(&port->port, tty, file); 189dfba2174SLu Baolu } 190dfba2174SLu Baolu 191dfba2174SLu Baolu static void dbc_tty_close(struct tty_struct *tty, struct file *file) 192dfba2174SLu Baolu { 193dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 194dfba2174SLu Baolu 195dfba2174SLu Baolu tty_port_close(&port->port, tty, file); 196dfba2174SLu Baolu } 197dfba2174SLu Baolu 19895713967SJiri Slaby (SUSE) static ssize_t dbc_tty_write(struct tty_struct *tty, const u8 *buf, 19995713967SJiri Slaby (SUSE) size_t count) 200dfba2174SLu Baolu { 201dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 202dfba2174SLu Baolu unsigned long flags; 203dfba2174SLu Baolu 204dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 205dfba2174SLu Baolu if (count) 206*7cbcb40dSJiri Slaby (SUSE) count = kfifo_in(&port->port.xmit_fifo, buf, count); 207dfba2174SLu Baolu dbc_start_tx(port); 208dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 209dfba2174SLu Baolu 210dfba2174SLu Baolu return count; 211dfba2174SLu Baolu } 212dfba2174SLu Baolu 213dcaafbe6SJiri Slaby (SUSE) static int dbc_tty_put_char(struct tty_struct *tty, u8 ch) 214dfba2174SLu Baolu { 215dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 216dfba2174SLu Baolu unsigned long flags; 217dfba2174SLu Baolu int status; 218dfba2174SLu Baolu 219dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 220*7cbcb40dSJiri Slaby (SUSE) status = kfifo_put(&port->port.xmit_fifo, ch); 221dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 222dfba2174SLu Baolu 223dfba2174SLu Baolu return status; 224dfba2174SLu Baolu } 225dfba2174SLu Baolu 226dfba2174SLu Baolu static void dbc_tty_flush_chars(struct tty_struct *tty) 227dfba2174SLu Baolu { 228dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 229dfba2174SLu Baolu unsigned long flags; 230dfba2174SLu Baolu 231dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 232dfba2174SLu Baolu dbc_start_tx(port); 233dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 234dfba2174SLu Baolu } 235dfba2174SLu Baolu 23603b3b1a2SJiri Slaby static unsigned int dbc_tty_write_room(struct tty_struct *tty) 237dfba2174SLu Baolu { 238dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 239dfba2174SLu Baolu unsigned long flags; 24003b3b1a2SJiri Slaby unsigned int room; 241dfba2174SLu Baolu 242dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 243*7cbcb40dSJiri Slaby (SUSE) room = kfifo_avail(&port->port.xmit_fifo); 244dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 245dfba2174SLu Baolu 246dfba2174SLu Baolu return room; 247dfba2174SLu Baolu } 248dfba2174SLu Baolu 249fff4ef17SJiri Slaby static unsigned int dbc_tty_chars_in_buffer(struct tty_struct *tty) 250dfba2174SLu Baolu { 251dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 252dfba2174SLu Baolu unsigned long flags; 253fff4ef17SJiri Slaby unsigned int chars; 254dfba2174SLu Baolu 255dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 256*7cbcb40dSJiri Slaby (SUSE) chars = kfifo_len(&port->port.xmit_fifo); 257dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 258dfba2174SLu Baolu 259dfba2174SLu Baolu return chars; 260dfba2174SLu Baolu } 261dfba2174SLu Baolu 262dfba2174SLu Baolu static void dbc_tty_unthrottle(struct tty_struct *tty) 263dfba2174SLu Baolu { 264dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 265dfba2174SLu Baolu unsigned long flags; 266dfba2174SLu Baolu 267dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 268dfba2174SLu Baolu tasklet_schedule(&port->push); 269dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 270dfba2174SLu Baolu } 271dfba2174SLu Baolu 272dfba2174SLu Baolu static const struct tty_operations dbc_tty_ops = { 273dfba2174SLu Baolu .install = dbc_tty_install, 274dfba2174SLu Baolu .open = dbc_tty_open, 275dfba2174SLu Baolu .close = dbc_tty_close, 276dfba2174SLu Baolu .write = dbc_tty_write, 277dfba2174SLu Baolu .put_char = dbc_tty_put_char, 278dfba2174SLu Baolu .flush_chars = dbc_tty_flush_chars, 279dfba2174SLu Baolu .write_room = dbc_tty_write_room, 280dfba2174SLu Baolu .chars_in_buffer = dbc_tty_chars_in_buffer, 281dfba2174SLu Baolu .unthrottle = dbc_tty_unthrottle, 282dfba2174SLu Baolu }; 283dfba2174SLu Baolu 28481d324cdSAllen Pais static void dbc_rx_push(struct tasklet_struct *t) 285dfba2174SLu Baolu { 286dfba2174SLu Baolu struct dbc_request *req; 287dfba2174SLu Baolu struct tty_struct *tty; 288a098dc8bSLu Baolu unsigned long flags; 289dfba2174SLu Baolu bool do_push = false; 290dfba2174SLu Baolu bool disconnect = false; 29181d324cdSAllen Pais struct dbc_port *port = from_tasklet(port, t, push); 292dfba2174SLu Baolu struct list_head *queue = &port->read_queue; 293dfba2174SLu Baolu 294a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 295dfba2174SLu Baolu tty = port->port.tty; 296dfba2174SLu Baolu while (!list_empty(queue)) { 297dfba2174SLu Baolu req = list_first_entry(queue, struct dbc_request, list_pool); 298dfba2174SLu Baolu 299dfba2174SLu Baolu if (tty && tty_throttled(tty)) 300dfba2174SLu Baolu break; 301dfba2174SLu Baolu 302dfba2174SLu Baolu switch (req->status) { 303dfba2174SLu Baolu case 0: 304dfba2174SLu Baolu break; 305dfba2174SLu Baolu case -ESHUTDOWN: 306dfba2174SLu Baolu disconnect = true; 307dfba2174SLu Baolu break; 308dfba2174SLu Baolu default: 309dfba2174SLu Baolu pr_warn("ttyDBC0: unexpected RX status %d\n", 310dfba2174SLu Baolu req->status); 311dfba2174SLu Baolu break; 312dfba2174SLu Baolu } 313dfba2174SLu Baolu 314dfba2174SLu Baolu if (req->actual) { 315dfba2174SLu Baolu char *packet = req->buf; 316dfba2174SLu Baolu unsigned int n, size = req->actual; 317dfba2174SLu Baolu int count; 318dfba2174SLu Baolu 319dfba2174SLu Baolu n = port->n_read; 320dfba2174SLu Baolu if (n) { 321dfba2174SLu Baolu packet += n; 322dfba2174SLu Baolu size -= n; 323dfba2174SLu Baolu } 324dfba2174SLu Baolu 325dfba2174SLu Baolu count = tty_insert_flip_string(&port->port, packet, 326dfba2174SLu Baolu size); 327dfba2174SLu Baolu if (count) 328dfba2174SLu Baolu do_push = true; 329dfba2174SLu Baolu if (count != size) { 330dfba2174SLu Baolu port->n_read += count; 331dfba2174SLu Baolu break; 332dfba2174SLu Baolu } 333dfba2174SLu Baolu port->n_read = 0; 334dfba2174SLu Baolu } 335dfba2174SLu Baolu 336dfba2174SLu Baolu list_move(&req->list_pool, &port->read_pool); 337dfba2174SLu Baolu } 338dfba2174SLu Baolu 339dfba2174SLu Baolu if (do_push) 340dfba2174SLu Baolu tty_flip_buffer_push(&port->port); 341dfba2174SLu Baolu 342dfba2174SLu Baolu if (!list_empty(queue) && tty) { 343dfba2174SLu Baolu if (!tty_throttled(tty)) { 344dfba2174SLu Baolu if (do_push) 345dfba2174SLu Baolu tasklet_schedule(&port->push); 346dfba2174SLu Baolu else 347dfba2174SLu Baolu pr_warn("ttyDBC0: RX not scheduled?\n"); 348dfba2174SLu Baolu } 349dfba2174SLu Baolu } 350dfba2174SLu Baolu 351dfba2174SLu Baolu if (!disconnect) 352dfba2174SLu Baolu dbc_start_rx(port); 353dfba2174SLu Baolu 354a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 355dfba2174SLu Baolu } 356dfba2174SLu Baolu 357dfba2174SLu Baolu static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty) 358dfba2174SLu Baolu { 359a098dc8bSLu Baolu unsigned long flags; 360dfba2174SLu Baolu struct dbc_port *port = container_of(_port, struct dbc_port, port); 361dfba2174SLu Baolu 362a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 363dfba2174SLu Baolu dbc_start_rx(port); 364a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 365dfba2174SLu Baolu 366dfba2174SLu Baolu return 0; 367dfba2174SLu Baolu } 368dfba2174SLu Baolu 369dfba2174SLu Baolu static const struct tty_port_operations dbc_port_ops = { 370dfba2174SLu Baolu .activate = dbc_port_activate, 371dfba2174SLu Baolu }; 372dfba2174SLu Baolu 373dfba2174SLu Baolu static void 37491aaf974SMathias Nyman xhci_dbc_tty_init_port(struct xhci_dbc *dbc, struct dbc_port *port) 375dfba2174SLu Baolu { 376dfba2174SLu Baolu tty_port_init(&port->port); 377dfba2174SLu Baolu spin_lock_init(&port->port_lock); 37881d324cdSAllen Pais tasklet_setup(&port->push, dbc_rx_push); 379dfba2174SLu Baolu INIT_LIST_HEAD(&port->read_pool); 380dfba2174SLu Baolu INIT_LIST_HEAD(&port->read_queue); 381dfba2174SLu Baolu INIT_LIST_HEAD(&port->write_pool); 382dfba2174SLu Baolu 383dfba2174SLu Baolu port->port.ops = &dbc_port_ops; 384dfba2174SLu Baolu port->n_read = 0; 385dfba2174SLu Baolu } 386dfba2174SLu Baolu 387dfba2174SLu Baolu static void 388dfba2174SLu Baolu xhci_dbc_tty_exit_port(struct dbc_port *port) 389dfba2174SLu Baolu { 390dfba2174SLu Baolu tasklet_kill(&port->push); 391dfba2174SLu Baolu tty_port_destroy(&port->port); 392dfba2174SLu Baolu } 393dfba2174SLu Baolu 39425252919SWei Yongjun static int xhci_dbc_tty_register_device(struct xhci_dbc *dbc) 395dfba2174SLu Baolu { 396dfba2174SLu Baolu int ret; 397dfba2174SLu Baolu struct device *tty_dev; 3989a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc); 399dfba2174SLu Baolu 400688915b1SMathias Nyman if (port->registered) 401688915b1SMathias Nyman return -EBUSY; 402688915b1SMathias Nyman 40391aaf974SMathias Nyman xhci_dbc_tty_init_port(dbc, port); 404dfba2174SLu Baolu 405e1ec140fSMathias Nyman mutex_lock(&dbc_tty_minors_lock); 406e1ec140fSMathias Nyman port->minor = idr_alloc(&dbc_tty_minors, port, 0, 64, GFP_KERNEL); 407e1ec140fSMathias Nyman mutex_unlock(&dbc_tty_minors_lock); 408e1ec140fSMathias Nyman 409e1ec140fSMathias Nyman if (port->minor < 0) { 410e1ec140fSMathias Nyman ret = port->minor; 411e1ec140fSMathias Nyman goto err_idr; 412e1ec140fSMathias Nyman } 413e1ec140fSMathias Nyman 414*7cbcb40dSJiri Slaby (SUSE) ret = kfifo_alloc(&port->port.xmit_fifo, DBC_WRITE_BUF_SIZE, 415*7cbcb40dSJiri Slaby (SUSE) GFP_KERNEL); 416dfba2174SLu Baolu if (ret) 417880de403SJohan Hovold goto err_exit_port; 418dfba2174SLu Baolu 419e0aa56dcSMathias Nyman ret = xhci_dbc_alloc_requests(dbc, BULK_IN, &port->read_pool, 420dfba2174SLu Baolu dbc_read_complete); 421dfba2174SLu Baolu if (ret) 422880de403SJohan Hovold goto err_free_fifo; 423dfba2174SLu Baolu 424e0aa56dcSMathias Nyman ret = xhci_dbc_alloc_requests(dbc, BULK_OUT, &port->write_pool, 425dfba2174SLu Baolu dbc_write_complete); 426dfba2174SLu Baolu if (ret) 427880de403SJohan Hovold goto err_free_requests; 428880de403SJohan Hovold 429880de403SJohan Hovold tty_dev = tty_port_register_device(&port->port, 430e1ec140fSMathias Nyman dbc_tty_driver, port->minor, NULL); 431880de403SJohan Hovold if (IS_ERR(tty_dev)) { 432880de403SJohan Hovold ret = PTR_ERR(tty_dev); 433880de403SJohan Hovold goto err_free_requests; 434880de403SJohan Hovold } 435dfba2174SLu Baolu 436dfba2174SLu Baolu port->registered = true; 437dfba2174SLu Baolu 438dfba2174SLu Baolu return 0; 439dfba2174SLu Baolu 440880de403SJohan Hovold err_free_requests: 441e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_pool); 442e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->write_pool); 443880de403SJohan Hovold err_free_fifo: 444*7cbcb40dSJiri Slaby (SUSE) kfifo_free(&port->port.xmit_fifo); 445880de403SJohan Hovold err_exit_port: 446e1ec140fSMathias Nyman idr_remove(&dbc_tty_minors, port->minor); 447e1ec140fSMathias Nyman err_idr: 448dfba2174SLu Baolu xhci_dbc_tty_exit_port(port); 449dfba2174SLu Baolu 450b396fa39SMathias Nyman dev_err(dbc->dev, "can't register tty port, err %d\n", ret); 451dfba2174SLu Baolu 452dfba2174SLu Baolu return ret; 453dfba2174SLu Baolu } 454dfba2174SLu Baolu 45525252919SWei Yongjun static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc) 456dfba2174SLu Baolu { 4579a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc); 458dfba2174SLu Baolu 459688915b1SMathias Nyman if (!port->registered) 460688915b1SMathias Nyman return; 461e1ec140fSMathias Nyman tty_unregister_device(dbc_tty_driver, port->minor); 462dfba2174SLu Baolu xhci_dbc_tty_exit_port(port); 463dfba2174SLu Baolu port->registered = false; 464dfba2174SLu Baolu 465e1ec140fSMathias Nyman mutex_lock(&dbc_tty_minors_lock); 466e1ec140fSMathias Nyman idr_remove(&dbc_tty_minors, port->minor); 467e1ec140fSMathias Nyman mutex_unlock(&dbc_tty_minors_lock); 468e1ec140fSMathias Nyman 469*7cbcb40dSJiri Slaby (SUSE) kfifo_free(&port->port.xmit_fifo); 470e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_pool); 471e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_queue); 472e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->write_pool); 473dfba2174SLu Baolu } 4744521f161SMathias Nyman 4756ae6470bSMathias Nyman static const struct dbc_driver dbc_driver = { 4766ae6470bSMathias Nyman .configure = xhci_dbc_tty_register_device, 4776ae6470bSMathias Nyman .disconnect = xhci_dbc_tty_unregister_device, 4786ae6470bSMathias Nyman }; 4796ae6470bSMathias Nyman 4805ce036b9SMathias Nyman int xhci_dbc_tty_probe(struct device *dev, void __iomem *base, struct xhci_hcd *xhci) 4814521f161SMathias Nyman { 4825ce036b9SMathias Nyman struct xhci_dbc *dbc; 4839a360a7cSMathias Nyman struct dbc_port *port; 4844521f161SMathias Nyman int status; 4854521f161SMathias Nyman 4866aec5000SMathias Nyman if (!dbc_tty_driver) 4876aec5000SMathias Nyman return -ENODEV; 4884521f161SMathias Nyman 4899a360a7cSMathias Nyman port = kzalloc(sizeof(*port), GFP_KERNEL); 4906aec5000SMathias Nyman if (!port) 4916aec5000SMathias Nyman return -ENOMEM; 4926ae6470bSMathias Nyman 4935ce036b9SMathias Nyman dbc = xhci_alloc_dbc(dev, base, &dbc_driver); 494e1ec140fSMathias Nyman 4955ce036b9SMathias Nyman if (!dbc) { 4965ce036b9SMathias Nyman status = -ENOMEM; 4975ce036b9SMathias Nyman goto out2; 4985ce036b9SMathias Nyman } 4995ce036b9SMathias Nyman 5005ce036b9SMathias Nyman dbc->priv = port; 5015ce036b9SMathias Nyman 5025ce036b9SMathias Nyman /* get rid of xhci once this is a real driver binding to a device */ 5035ce036b9SMathias Nyman xhci->dbc = dbc; 5045ce036b9SMathias Nyman 5054521f161SMathias Nyman return 0; 5065ce036b9SMathias Nyman out2: 5075ce036b9SMathias Nyman kfree(port); 5086aec5000SMathias Nyman 5094521f161SMathias Nyman return status; 5104521f161SMathias Nyman } 5114521f161SMathias Nyman 5124521f161SMathias Nyman /* 5134521f161SMathias Nyman * undo what probe did, assume dbc is stopped already. 5144521f161SMathias Nyman * we also assume tty_unregister_device() is called before this 5154521f161SMathias Nyman */ 5164521f161SMathias Nyman void xhci_dbc_tty_remove(struct xhci_dbc *dbc) 5174521f161SMathias Nyman { 5189a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc); 5199a360a7cSMathias Nyman 5205ce036b9SMathias Nyman xhci_dbc_remove(dbc); 5219a360a7cSMathias Nyman kfree(port); 5224521f161SMathias Nyman } 5234521f161SMathias Nyman 5246aec5000SMathias Nyman int dbc_tty_init(void) 5254521f161SMathias Nyman { 5264521f161SMathias Nyman int ret; 5274521f161SMathias Nyman 528e1ec140fSMathias Nyman idr_init(&dbc_tty_minors); 529e1ec140fSMathias Nyman 530e1ec140fSMathias Nyman dbc_tty_driver = tty_alloc_driver(64, TTY_DRIVER_REAL_RAW | 5314521f161SMathias Nyman TTY_DRIVER_DYNAMIC_DEV); 532e1ec140fSMathias Nyman if (IS_ERR(dbc_tty_driver)) { 533e1ec140fSMathias Nyman idr_destroy(&dbc_tty_minors); 5344521f161SMathias Nyman return PTR_ERR(dbc_tty_driver); 535e1ec140fSMathias Nyman } 5364521f161SMathias Nyman 5374521f161SMathias Nyman dbc_tty_driver->driver_name = "dbc_serial"; 5384521f161SMathias Nyman dbc_tty_driver->name = "ttyDBC"; 5394521f161SMathias Nyman 5404521f161SMathias Nyman dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; 5414521f161SMathias Nyman dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL; 5424521f161SMathias Nyman dbc_tty_driver->init_termios = tty_std_termios; 5434521f161SMathias Nyman dbc_tty_driver->init_termios.c_cflag = 5444521f161SMathias Nyman B9600 | CS8 | CREAD | HUPCL | CLOCAL; 5454521f161SMathias Nyman dbc_tty_driver->init_termios.c_ispeed = 9600; 5464521f161SMathias Nyman dbc_tty_driver->init_termios.c_ospeed = 9600; 5474521f161SMathias Nyman 5484521f161SMathias Nyman tty_set_operations(dbc_tty_driver, &dbc_tty_ops); 5494521f161SMathias Nyman 5504521f161SMathias Nyman ret = tty_register_driver(dbc_tty_driver); 5514521f161SMathias Nyman if (ret) { 5524521f161SMathias Nyman pr_err("Can't register dbc tty driver\n"); 5539f90a4ddSJiri Slaby tty_driver_kref_put(dbc_tty_driver); 554e1ec140fSMathias Nyman idr_destroy(&dbc_tty_minors); 5554521f161SMathias Nyman } 556e1ec140fSMathias Nyman 5574521f161SMathias Nyman return ret; 5584521f161SMathias Nyman } 5594521f161SMathias Nyman 5606aec5000SMathias Nyman void dbc_tty_exit(void) 5614521f161SMathias Nyman { 5624521f161SMathias Nyman if (dbc_tty_driver) { 5634521f161SMathias Nyman tty_unregister_driver(dbc_tty_driver); 5649f90a4ddSJiri Slaby tty_driver_kref_put(dbc_tty_driver); 5654521f161SMathias Nyman dbc_tty_driver = NULL; 5664521f161SMathias Nyman } 567e1ec140fSMathias Nyman 568e1ec140fSMathias Nyman idr_destroy(&dbc_tty_minors); 5694521f161SMathias Nyman } 570