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> 13dfba2174SLu Baolu 14dfba2174SLu Baolu #include "xhci.h" 15dfba2174SLu Baolu #include "xhci-dbgcap.h" 16dfba2174SLu Baolu 174521f161SMathias Nyman static int dbc_tty_init(void); 184521f161SMathias Nyman static void dbc_tty_exit(void); 194521f161SMathias Nyman 204521f161SMathias Nyman static struct tty_driver *dbc_tty_driver; 214521f161SMathias Nyman 22dfba2174SLu Baolu static unsigned int 23dfba2174SLu Baolu dbc_send_packet(struct dbc_port *port, char *packet, unsigned int size) 24dfba2174SLu Baolu { 25dfba2174SLu Baolu unsigned int len; 26dfba2174SLu Baolu 27dfba2174SLu Baolu len = kfifo_len(&port->write_fifo); 28dfba2174SLu Baolu if (len < size) 29dfba2174SLu Baolu size = len; 30dfba2174SLu Baolu if (size != 0) 31dfba2174SLu Baolu size = kfifo_out(&port->write_fifo, packet, size); 32dfba2174SLu Baolu return size; 33dfba2174SLu Baolu } 34dfba2174SLu Baolu 35dfba2174SLu Baolu static int dbc_start_tx(struct dbc_port *port) 36dfba2174SLu Baolu __releases(&port->port_lock) 37dfba2174SLu Baolu __acquires(&port->port_lock) 38dfba2174SLu Baolu { 39dfba2174SLu Baolu int len; 40dfba2174SLu Baolu struct dbc_request *req; 41dfba2174SLu Baolu int status = 0; 42dfba2174SLu Baolu bool do_tty_wake = false; 43dfba2174SLu Baolu struct list_head *pool = &port->write_pool; 44dfba2174SLu Baolu 45dfba2174SLu Baolu while (!list_empty(pool)) { 46dfba2174SLu Baolu req = list_entry(pool->next, struct dbc_request, list_pool); 47dfba2174SLu Baolu len = dbc_send_packet(port, req->buf, DBC_MAX_PACKET); 48dfba2174SLu Baolu if (len == 0) 49dfba2174SLu Baolu break; 50dfba2174SLu Baolu do_tty_wake = true; 51dfba2174SLu Baolu 52dfba2174SLu Baolu req->length = len; 53dfba2174SLu Baolu list_del(&req->list_pool); 54dfba2174SLu Baolu 55dfba2174SLu Baolu spin_unlock(&port->port_lock); 56e0aa56dcSMathias Nyman status = dbc_ep_queue(req); 57dfba2174SLu Baolu spin_lock(&port->port_lock); 58dfba2174SLu Baolu 59dfba2174SLu Baolu if (status) { 60dfba2174SLu Baolu list_add(&req->list_pool, pool); 61dfba2174SLu Baolu break; 62dfba2174SLu Baolu } 63dfba2174SLu Baolu } 64dfba2174SLu Baolu 65dfba2174SLu Baolu if (do_tty_wake && port->port.tty) 66dfba2174SLu Baolu tty_wakeup(port->port.tty); 67dfba2174SLu Baolu 68dfba2174SLu Baolu return status; 69dfba2174SLu Baolu } 70dfba2174SLu Baolu 71dfba2174SLu Baolu static void dbc_start_rx(struct dbc_port *port) 72dfba2174SLu Baolu __releases(&port->port_lock) 73dfba2174SLu Baolu __acquires(&port->port_lock) 74dfba2174SLu Baolu { 75dfba2174SLu Baolu struct dbc_request *req; 76dfba2174SLu Baolu int status; 77dfba2174SLu Baolu struct list_head *pool = &port->read_pool; 78dfba2174SLu Baolu 79dfba2174SLu Baolu while (!list_empty(pool)) { 80dfba2174SLu Baolu if (!port->port.tty) 81dfba2174SLu Baolu break; 82dfba2174SLu Baolu 83dfba2174SLu Baolu req = list_entry(pool->next, struct dbc_request, list_pool); 84dfba2174SLu Baolu list_del(&req->list_pool); 85dfba2174SLu Baolu req->length = DBC_MAX_PACKET; 86dfba2174SLu Baolu 87dfba2174SLu Baolu spin_unlock(&port->port_lock); 88e0aa56dcSMathias Nyman status = dbc_ep_queue(req); 89dfba2174SLu Baolu spin_lock(&port->port_lock); 90dfba2174SLu Baolu 91dfba2174SLu Baolu if (status) { 92dfba2174SLu Baolu list_add(&req->list_pool, pool); 93dfba2174SLu Baolu break; 94dfba2174SLu Baolu } 95dfba2174SLu Baolu } 96dfba2174SLu Baolu } 97dfba2174SLu Baolu 98dfba2174SLu Baolu static void 99f39f3afdSMathias Nyman dbc_read_complete(struct xhci_dbc *dbc, struct dbc_request *req) 100dfba2174SLu Baolu { 101a098dc8bSLu Baolu unsigned long flags; 102dfba2174SLu Baolu struct dbc_port *port = &dbc->port; 103dfba2174SLu Baolu 104a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 105dfba2174SLu Baolu list_add_tail(&req->list_pool, &port->read_queue); 106dfba2174SLu Baolu tasklet_schedule(&port->push); 107a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 108dfba2174SLu Baolu } 109dfba2174SLu Baolu 110f39f3afdSMathias Nyman static void dbc_write_complete(struct xhci_dbc *dbc, struct dbc_request *req) 111dfba2174SLu Baolu { 112a098dc8bSLu Baolu unsigned long flags; 113dfba2174SLu Baolu struct dbc_port *port = &dbc->port; 114dfba2174SLu Baolu 115a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 116dfba2174SLu Baolu list_add(&req->list_pool, &port->write_pool); 117dfba2174SLu Baolu switch (req->status) { 118dfba2174SLu Baolu case 0: 119dfba2174SLu Baolu dbc_start_tx(port); 120dfba2174SLu Baolu break; 121dfba2174SLu Baolu case -ESHUTDOWN: 122dfba2174SLu Baolu break; 123dfba2174SLu Baolu default: 124f39f3afdSMathias Nyman dev_warn(dbc->dev, "unexpected write complete status %d\n", 125dfba2174SLu Baolu req->status); 126dfba2174SLu Baolu break; 127dfba2174SLu Baolu } 128a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 129dfba2174SLu Baolu } 130dfba2174SLu Baolu 131e0aa56dcSMathias Nyman static void xhci_dbc_free_req(struct dbc_request *req) 132dfba2174SLu Baolu { 133dfba2174SLu Baolu kfree(req->buf); 134e0aa56dcSMathias Nyman dbc_free_request(req); 135dfba2174SLu Baolu } 136dfba2174SLu Baolu 137dfba2174SLu Baolu static int 138e0aa56dcSMathias Nyman xhci_dbc_alloc_requests(struct xhci_dbc *dbc, unsigned int direction, 139e0aa56dcSMathias Nyman struct list_head *head, 140f39f3afdSMathias Nyman void (*fn)(struct xhci_dbc *, struct dbc_request *)) 141dfba2174SLu Baolu { 142dfba2174SLu Baolu int i; 143dfba2174SLu Baolu struct dbc_request *req; 144dfba2174SLu Baolu 145dfba2174SLu Baolu for (i = 0; i < DBC_QUEUE_SIZE; i++) { 146e0aa56dcSMathias Nyman req = dbc_alloc_request(dbc, direction, GFP_KERNEL); 147dfba2174SLu Baolu if (!req) 148dfba2174SLu Baolu break; 149dfba2174SLu Baolu 150dfba2174SLu Baolu req->length = DBC_MAX_PACKET; 151dfba2174SLu Baolu req->buf = kmalloc(req->length, GFP_KERNEL); 152dfba2174SLu Baolu if (!req->buf) { 153e0aa56dcSMathias Nyman dbc_free_request(req); 154dfba2174SLu Baolu break; 155dfba2174SLu Baolu } 156dfba2174SLu Baolu 157dfba2174SLu Baolu req->complete = fn; 158dfba2174SLu Baolu list_add_tail(&req->list_pool, head); 159dfba2174SLu Baolu } 160dfba2174SLu Baolu 161dfba2174SLu Baolu return list_empty(head) ? -ENOMEM : 0; 162dfba2174SLu Baolu } 163dfba2174SLu Baolu 164dfba2174SLu Baolu static void 165e0aa56dcSMathias Nyman xhci_dbc_free_requests(struct list_head *head) 166dfba2174SLu Baolu { 167dfba2174SLu Baolu struct dbc_request *req; 168dfba2174SLu Baolu 169dfba2174SLu Baolu while (!list_empty(head)) { 170dfba2174SLu Baolu req = list_entry(head->next, struct dbc_request, list_pool); 171dfba2174SLu Baolu list_del(&req->list_pool); 172e0aa56dcSMathias Nyman xhci_dbc_free_req(req); 173dfba2174SLu Baolu } 174dfba2174SLu Baolu } 175dfba2174SLu Baolu 176dfba2174SLu Baolu static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty) 177dfba2174SLu Baolu { 178dfba2174SLu Baolu struct dbc_port *port = driver->driver_state; 179dfba2174SLu Baolu 180dfba2174SLu Baolu tty->driver_data = port; 181dfba2174SLu Baolu 182dfba2174SLu Baolu return tty_port_install(&port->port, driver, tty); 183dfba2174SLu Baolu } 184dfba2174SLu Baolu 185dfba2174SLu Baolu static int dbc_tty_open(struct tty_struct *tty, struct file *file) 186dfba2174SLu Baolu { 187dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 188dfba2174SLu Baolu 189dfba2174SLu Baolu return tty_port_open(&port->port, tty, file); 190dfba2174SLu Baolu } 191dfba2174SLu Baolu 192dfba2174SLu Baolu static void dbc_tty_close(struct tty_struct *tty, struct file *file) 193dfba2174SLu Baolu { 194dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 195dfba2174SLu Baolu 196dfba2174SLu Baolu tty_port_close(&port->port, tty, file); 197dfba2174SLu Baolu } 198dfba2174SLu Baolu 199dfba2174SLu Baolu static int dbc_tty_write(struct tty_struct *tty, 200dfba2174SLu Baolu const unsigned char *buf, 201dfba2174SLu Baolu int count) 202dfba2174SLu Baolu { 203dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 204dfba2174SLu Baolu unsigned long flags; 205dfba2174SLu Baolu 206dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 207dfba2174SLu Baolu if (count) 208dfba2174SLu Baolu count = kfifo_in(&port->write_fifo, buf, count); 209dfba2174SLu Baolu dbc_start_tx(port); 210dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 211dfba2174SLu Baolu 212dfba2174SLu Baolu return count; 213dfba2174SLu Baolu } 214dfba2174SLu Baolu 215dfba2174SLu Baolu static int dbc_tty_put_char(struct tty_struct *tty, unsigned char ch) 216dfba2174SLu Baolu { 217dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 218dfba2174SLu Baolu unsigned long flags; 219dfba2174SLu Baolu int status; 220dfba2174SLu Baolu 221dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 222dfba2174SLu Baolu status = kfifo_put(&port->write_fifo, ch); 223dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 224dfba2174SLu Baolu 225dfba2174SLu Baolu return status; 226dfba2174SLu Baolu } 227dfba2174SLu Baolu 228dfba2174SLu Baolu static void dbc_tty_flush_chars(struct tty_struct *tty) 229dfba2174SLu Baolu { 230dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 231dfba2174SLu Baolu unsigned long flags; 232dfba2174SLu Baolu 233dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 234dfba2174SLu Baolu dbc_start_tx(port); 235dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 236dfba2174SLu Baolu } 237dfba2174SLu Baolu 238dfba2174SLu Baolu static int dbc_tty_write_room(struct tty_struct *tty) 239dfba2174SLu Baolu { 240dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 241dfba2174SLu Baolu unsigned long flags; 242dfba2174SLu Baolu int room = 0; 243dfba2174SLu Baolu 244dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 245dfba2174SLu Baolu room = kfifo_avail(&port->write_fifo); 246dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 247dfba2174SLu Baolu 248dfba2174SLu Baolu return room; 249dfba2174SLu Baolu } 250dfba2174SLu Baolu 251dfba2174SLu Baolu static int dbc_tty_chars_in_buffer(struct tty_struct *tty) 252dfba2174SLu Baolu { 253dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 254dfba2174SLu Baolu unsigned long flags; 255dfba2174SLu Baolu int chars = 0; 256dfba2174SLu Baolu 257dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 258dfba2174SLu Baolu chars = kfifo_len(&port->write_fifo); 259dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 260dfba2174SLu Baolu 261dfba2174SLu Baolu return chars; 262dfba2174SLu Baolu } 263dfba2174SLu Baolu 264dfba2174SLu Baolu static void dbc_tty_unthrottle(struct tty_struct *tty) 265dfba2174SLu Baolu { 266dfba2174SLu Baolu struct dbc_port *port = tty->driver_data; 267dfba2174SLu Baolu unsigned long flags; 268dfba2174SLu Baolu 269dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags); 270dfba2174SLu Baolu tasklet_schedule(&port->push); 271dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 272dfba2174SLu Baolu } 273dfba2174SLu Baolu 274dfba2174SLu Baolu static const struct tty_operations dbc_tty_ops = { 275dfba2174SLu Baolu .install = dbc_tty_install, 276dfba2174SLu Baolu .open = dbc_tty_open, 277dfba2174SLu Baolu .close = dbc_tty_close, 278dfba2174SLu Baolu .write = dbc_tty_write, 279dfba2174SLu Baolu .put_char = dbc_tty_put_char, 280dfba2174SLu Baolu .flush_chars = dbc_tty_flush_chars, 281dfba2174SLu Baolu .write_room = dbc_tty_write_room, 282dfba2174SLu Baolu .chars_in_buffer = dbc_tty_chars_in_buffer, 283dfba2174SLu Baolu .unthrottle = dbc_tty_unthrottle, 284dfba2174SLu Baolu }; 285dfba2174SLu Baolu 286dfba2174SLu Baolu static void dbc_rx_push(unsigned long _port) 287dfba2174SLu Baolu { 288dfba2174SLu Baolu struct dbc_request *req; 289dfba2174SLu Baolu struct tty_struct *tty; 290a098dc8bSLu Baolu unsigned long flags; 291dfba2174SLu Baolu bool do_push = false; 292dfba2174SLu Baolu bool disconnect = false; 293dfba2174SLu Baolu struct dbc_port *port = (void *)_port; 294dfba2174SLu Baolu struct list_head *queue = &port->read_queue; 295dfba2174SLu Baolu 296a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 297dfba2174SLu Baolu tty = port->port.tty; 298dfba2174SLu Baolu while (!list_empty(queue)) { 299dfba2174SLu Baolu req = list_first_entry(queue, struct dbc_request, list_pool); 300dfba2174SLu Baolu 301dfba2174SLu Baolu if (tty && tty_throttled(tty)) 302dfba2174SLu Baolu break; 303dfba2174SLu Baolu 304dfba2174SLu Baolu switch (req->status) { 305dfba2174SLu Baolu case 0: 306dfba2174SLu Baolu break; 307dfba2174SLu Baolu case -ESHUTDOWN: 308dfba2174SLu Baolu disconnect = true; 309dfba2174SLu Baolu break; 310dfba2174SLu Baolu default: 311dfba2174SLu Baolu pr_warn("ttyDBC0: unexpected RX status %d\n", 312dfba2174SLu Baolu req->status); 313dfba2174SLu Baolu break; 314dfba2174SLu Baolu } 315dfba2174SLu Baolu 316dfba2174SLu Baolu if (req->actual) { 317dfba2174SLu Baolu char *packet = req->buf; 318dfba2174SLu Baolu unsigned int n, size = req->actual; 319dfba2174SLu Baolu int count; 320dfba2174SLu Baolu 321dfba2174SLu Baolu n = port->n_read; 322dfba2174SLu Baolu if (n) { 323dfba2174SLu Baolu packet += n; 324dfba2174SLu Baolu size -= n; 325dfba2174SLu Baolu } 326dfba2174SLu Baolu 327dfba2174SLu Baolu count = tty_insert_flip_string(&port->port, packet, 328dfba2174SLu Baolu size); 329dfba2174SLu Baolu if (count) 330dfba2174SLu Baolu do_push = true; 331dfba2174SLu Baolu if (count != size) { 332dfba2174SLu Baolu port->n_read += count; 333dfba2174SLu Baolu break; 334dfba2174SLu Baolu } 335dfba2174SLu Baolu port->n_read = 0; 336dfba2174SLu Baolu } 337dfba2174SLu Baolu 338dfba2174SLu Baolu list_move(&req->list_pool, &port->read_pool); 339dfba2174SLu Baolu } 340dfba2174SLu Baolu 341dfba2174SLu Baolu if (do_push) 342dfba2174SLu Baolu tty_flip_buffer_push(&port->port); 343dfba2174SLu Baolu 344dfba2174SLu Baolu if (!list_empty(queue) && tty) { 345dfba2174SLu Baolu if (!tty_throttled(tty)) { 346dfba2174SLu Baolu if (do_push) 347dfba2174SLu Baolu tasklet_schedule(&port->push); 348dfba2174SLu Baolu else 349dfba2174SLu Baolu pr_warn("ttyDBC0: RX not scheduled?\n"); 350dfba2174SLu Baolu } 351dfba2174SLu Baolu } 352dfba2174SLu Baolu 353dfba2174SLu Baolu if (!disconnect) 354dfba2174SLu Baolu dbc_start_rx(port); 355dfba2174SLu Baolu 356a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 357dfba2174SLu Baolu } 358dfba2174SLu Baolu 359dfba2174SLu Baolu static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty) 360dfba2174SLu Baolu { 361a098dc8bSLu Baolu unsigned long flags; 362dfba2174SLu Baolu struct dbc_port *port = container_of(_port, struct dbc_port, port); 363dfba2174SLu Baolu 364a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags); 365dfba2174SLu Baolu dbc_start_rx(port); 366a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags); 367dfba2174SLu Baolu 368dfba2174SLu Baolu return 0; 369dfba2174SLu Baolu } 370dfba2174SLu Baolu 371dfba2174SLu Baolu static const struct tty_port_operations dbc_port_ops = { 372dfba2174SLu Baolu .activate = dbc_port_activate, 373dfba2174SLu Baolu }; 374dfba2174SLu Baolu 375dfba2174SLu Baolu static void 37691aaf974SMathias Nyman xhci_dbc_tty_init_port(struct xhci_dbc *dbc, struct dbc_port *port) 377dfba2174SLu Baolu { 378dfba2174SLu Baolu tty_port_init(&port->port); 379dfba2174SLu Baolu spin_lock_init(&port->port_lock); 380dfba2174SLu Baolu tasklet_init(&port->push, dbc_rx_push, (unsigned long)port); 381dfba2174SLu Baolu INIT_LIST_HEAD(&port->read_pool); 382dfba2174SLu Baolu INIT_LIST_HEAD(&port->read_queue); 383dfba2174SLu Baolu INIT_LIST_HEAD(&port->write_pool); 384dfba2174SLu Baolu 385dfba2174SLu Baolu port->port.ops = &dbc_port_ops; 386dfba2174SLu Baolu port->n_read = 0; 387dfba2174SLu Baolu } 388dfba2174SLu Baolu 389dfba2174SLu Baolu static void 390dfba2174SLu Baolu xhci_dbc_tty_exit_port(struct dbc_port *port) 391dfba2174SLu Baolu { 392dfba2174SLu Baolu tasklet_kill(&port->push); 393dfba2174SLu Baolu tty_port_destroy(&port->port); 394dfba2174SLu Baolu } 395dfba2174SLu Baolu 396b396fa39SMathias Nyman int xhci_dbc_tty_register_device(struct xhci_dbc *dbc) 397dfba2174SLu Baolu { 398dfba2174SLu Baolu int ret; 399dfba2174SLu Baolu struct device *tty_dev; 400dfba2174SLu Baolu struct dbc_port *port = &dbc->port; 401dfba2174SLu Baolu 402*688915b1SMathias Nyman if (port->registered) 403*688915b1SMathias Nyman return -EBUSY; 404*688915b1SMathias Nyman 40591aaf974SMathias Nyman xhci_dbc_tty_init_port(dbc, port); 406dfba2174SLu Baolu tty_dev = tty_port_register_device(&port->port, 407dfba2174SLu Baolu dbc_tty_driver, 0, NULL); 40829f65339SDan Carpenter if (IS_ERR(tty_dev)) { 40929f65339SDan Carpenter ret = PTR_ERR(tty_dev); 410dfba2174SLu Baolu goto register_fail; 41129f65339SDan Carpenter } 412dfba2174SLu Baolu 413dfba2174SLu Baolu ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL); 414dfba2174SLu Baolu if (ret) 415dfba2174SLu Baolu goto buf_alloc_fail; 416dfba2174SLu Baolu 417e0aa56dcSMathias Nyman ret = xhci_dbc_alloc_requests(dbc, BULK_IN, &port->read_pool, 418dfba2174SLu Baolu dbc_read_complete); 419dfba2174SLu Baolu if (ret) 420dfba2174SLu Baolu goto request_fail; 421dfba2174SLu Baolu 422e0aa56dcSMathias Nyman ret = xhci_dbc_alloc_requests(dbc, BULK_OUT, &port->write_pool, 423dfba2174SLu Baolu dbc_write_complete); 424dfba2174SLu Baolu if (ret) 425dfba2174SLu Baolu goto request_fail; 426dfba2174SLu Baolu 427dfba2174SLu Baolu port->registered = true; 428dfba2174SLu Baolu 429dfba2174SLu Baolu return 0; 430dfba2174SLu Baolu 431dfba2174SLu Baolu request_fail: 432e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_pool); 433e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->write_pool); 434dfba2174SLu Baolu kfifo_free(&port->write_fifo); 435dfba2174SLu Baolu 436dfba2174SLu Baolu buf_alloc_fail: 437dfba2174SLu Baolu tty_unregister_device(dbc_tty_driver, 0); 438dfba2174SLu Baolu 439dfba2174SLu Baolu register_fail: 440dfba2174SLu Baolu xhci_dbc_tty_exit_port(port); 441dfba2174SLu Baolu 442b396fa39SMathias Nyman dev_err(dbc->dev, "can't register tty port, err %d\n", ret); 443dfba2174SLu Baolu 444dfba2174SLu Baolu return ret; 445dfba2174SLu Baolu } 446dfba2174SLu Baolu 447b396fa39SMathias Nyman void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc) 448dfba2174SLu Baolu { 449dfba2174SLu Baolu struct dbc_port *port = &dbc->port; 450dfba2174SLu Baolu 451*688915b1SMathias Nyman if (!port->registered) 452*688915b1SMathias Nyman return; 453dfba2174SLu Baolu tty_unregister_device(dbc_tty_driver, 0); 454dfba2174SLu Baolu xhci_dbc_tty_exit_port(port); 455dfba2174SLu Baolu port->registered = false; 456dfba2174SLu Baolu 457dfba2174SLu Baolu kfifo_free(&port->write_fifo); 458e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_pool); 459e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_queue); 460e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->write_pool); 461dfba2174SLu Baolu } 4624521f161SMathias Nyman 4636ae6470bSMathias Nyman static const struct dbc_driver dbc_driver = { 4646ae6470bSMathias Nyman .configure = xhci_dbc_tty_register_device, 4656ae6470bSMathias Nyman .disconnect = xhci_dbc_tty_unregister_device, 4666ae6470bSMathias Nyman }; 4676ae6470bSMathias Nyman 4684521f161SMathias Nyman int xhci_dbc_tty_probe(struct xhci_hcd *xhci) 4694521f161SMathias Nyman { 4704521f161SMathias Nyman struct xhci_dbc *dbc = xhci->dbc; 4714521f161SMathias Nyman int status; 4724521f161SMathias Nyman 4734521f161SMathias Nyman /* dbc_tty_init will be called by module init() in the future */ 4744521f161SMathias Nyman status = dbc_tty_init(); 4754521f161SMathias Nyman if (status) 4764521f161SMathias Nyman return status; 4774521f161SMathias Nyman 4786ae6470bSMathias Nyman dbc->driver = &dbc_driver; 4796ae6470bSMathias Nyman 4804521f161SMathias Nyman dbc_tty_driver->driver_state = &dbc->port; 4814521f161SMathias Nyman 4824521f161SMathias Nyman return 0; 4834521f161SMathias Nyman out: 4844521f161SMathias Nyman 4854521f161SMathias Nyman /* dbc_tty_exit will be called by module_exit() in the future */ 4864521f161SMathias Nyman dbc_tty_exit(); 4874521f161SMathias Nyman return status; 4884521f161SMathias Nyman } 4894521f161SMathias Nyman 4904521f161SMathias Nyman /* 4914521f161SMathias Nyman * undo what probe did, assume dbc is stopped already. 4924521f161SMathias Nyman * we also assume tty_unregister_device() is called before this 4934521f161SMathias Nyman */ 4944521f161SMathias Nyman void xhci_dbc_tty_remove(struct xhci_dbc *dbc) 4954521f161SMathias Nyman { 4966ae6470bSMathias Nyman dbc->driver = NULL; 4976ae6470bSMathias Nyman 4984521f161SMathias Nyman /* dbc_tty_exit will be called by module_exit() in the future */ 4994521f161SMathias Nyman dbc_tty_exit(); 5004521f161SMathias Nyman } 5014521f161SMathias Nyman 5024521f161SMathias Nyman static int dbc_tty_init(void) 5034521f161SMathias Nyman { 5044521f161SMathias Nyman int ret; 5054521f161SMathias Nyman 5064521f161SMathias Nyman dbc_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW | 5074521f161SMathias Nyman TTY_DRIVER_DYNAMIC_DEV); 5084521f161SMathias Nyman if (IS_ERR(dbc_tty_driver)) 5094521f161SMathias Nyman return PTR_ERR(dbc_tty_driver); 5104521f161SMathias Nyman 5114521f161SMathias Nyman dbc_tty_driver->driver_name = "dbc_serial"; 5124521f161SMathias Nyman dbc_tty_driver->name = "ttyDBC"; 5134521f161SMathias Nyman 5144521f161SMathias Nyman dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; 5154521f161SMathias Nyman dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL; 5164521f161SMathias Nyman dbc_tty_driver->init_termios = tty_std_termios; 5174521f161SMathias Nyman dbc_tty_driver->init_termios.c_cflag = 5184521f161SMathias Nyman B9600 | CS8 | CREAD | HUPCL | CLOCAL; 5194521f161SMathias Nyman dbc_tty_driver->init_termios.c_ispeed = 9600; 5204521f161SMathias Nyman dbc_tty_driver->init_termios.c_ospeed = 9600; 5214521f161SMathias Nyman 5224521f161SMathias Nyman tty_set_operations(dbc_tty_driver, &dbc_tty_ops); 5234521f161SMathias Nyman 5244521f161SMathias Nyman ret = tty_register_driver(dbc_tty_driver); 5254521f161SMathias Nyman if (ret) { 5264521f161SMathias Nyman pr_err("Can't register dbc tty driver\n"); 5274521f161SMathias Nyman put_tty_driver(dbc_tty_driver); 5284521f161SMathias Nyman } 5294521f161SMathias Nyman return ret; 5304521f161SMathias Nyman } 5314521f161SMathias Nyman 5324521f161SMathias Nyman static void dbc_tty_exit(void) 5334521f161SMathias Nyman { 5344521f161SMathias Nyman if (dbc_tty_driver) { 5354521f161SMathias Nyman tty_unregister_driver(dbc_tty_driver); 5364521f161SMathias Nyman put_tty_driver(dbc_tty_driver); 5374521f161SMathias Nyman dbc_tty_driver = NULL; 5384521f161SMathias Nyman } 5394521f161SMathias Nyman } 540