linux/drivers/usb/host/xhci-dbgtty.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/**
   3 * xhci-dbgtty.c - tty glue for xHCI debug capability
   4 *
   5 * Copyright (C) 2017 Intel Corporation
   6 *
   7 * Author: Lu Baolu <baolu.lu@linux.intel.com>
   8 */
   9
  10#include <linux/slab.h>
  11#include <linux/tty.h>
  12#include <linux/tty_flip.h>
  13
  14#include "xhci.h"
  15#include "xhci-dbgcap.h"
  16
  17static unsigned int
  18dbc_send_packet(struct dbc_port *port, char *packet, unsigned int size)
  19{
  20        unsigned int            len;
  21
  22        len = kfifo_len(&port->write_fifo);
  23        if (len < size)
  24                size = len;
  25        if (size != 0)
  26                size = kfifo_out(&port->write_fifo, packet, size);
  27        return size;
  28}
  29
  30static int dbc_start_tx(struct dbc_port *port)
  31        __releases(&port->port_lock)
  32        __acquires(&port->port_lock)
  33{
  34        int                     len;
  35        struct dbc_request      *req;
  36        int                     status = 0;
  37        bool                    do_tty_wake = false;
  38        struct list_head        *pool = &port->write_pool;
  39
  40        while (!list_empty(pool)) {
  41                req = list_entry(pool->next, struct dbc_request, list_pool);
  42                len = dbc_send_packet(port, req->buf, DBC_MAX_PACKET);
  43                if (len == 0)
  44                        break;
  45                do_tty_wake = true;
  46
  47                req->length = len;
  48                list_del(&req->list_pool);
  49
  50                spin_unlock(&port->port_lock);
  51                status = dbc_ep_queue(port->out, req, GFP_ATOMIC);
  52                spin_lock(&port->port_lock);
  53
  54                if (status) {
  55                        list_add(&req->list_pool, pool);
  56                        break;
  57                }
  58        }
  59
  60        if (do_tty_wake && port->port.tty)
  61                tty_wakeup(port->port.tty);
  62
  63        return status;
  64}
  65
  66static void dbc_start_rx(struct dbc_port *port)
  67        __releases(&port->port_lock)
  68        __acquires(&port->port_lock)
  69{
  70        struct dbc_request      *req;
  71        int                     status;
  72        struct list_head        *pool = &port->read_pool;
  73
  74        while (!list_empty(pool)) {
  75                if (!port->port.tty)
  76                        break;
  77
  78                req = list_entry(pool->next, struct dbc_request, list_pool);
  79                list_del(&req->list_pool);
  80                req->length = DBC_MAX_PACKET;
  81
  82                spin_unlock(&port->port_lock);
  83                status = dbc_ep_queue(port->in, req, GFP_ATOMIC);
  84                spin_lock(&port->port_lock);
  85
  86                if (status) {
  87                        list_add(&req->list_pool, pool);
  88                        break;
  89                }
  90        }
  91}
  92
  93static void
  94dbc_read_complete(struct xhci_hcd *xhci, struct dbc_request *req)
  95{
  96        unsigned long           flags;
  97        struct xhci_dbc         *dbc = xhci->dbc;
  98        struct dbc_port         *port = &dbc->port;
  99
 100        spin_lock_irqsave(&port->port_lock, flags);
 101        list_add_tail(&req->list_pool, &port->read_queue);
 102        tasklet_schedule(&port->push);
 103        spin_unlock_irqrestore(&port->port_lock, flags);
 104}
 105
 106static void dbc_write_complete(struct xhci_hcd *xhci, struct dbc_request *req)
 107{
 108        unsigned long           flags;
 109        struct xhci_dbc         *dbc = xhci->dbc;
 110        struct dbc_port         *port = &dbc->port;
 111
 112        spin_lock_irqsave(&port->port_lock, flags);
 113        list_add(&req->list_pool, &port->write_pool);
 114        switch (req->status) {
 115        case 0:
 116                dbc_start_tx(port);
 117                break;
 118        case -ESHUTDOWN:
 119                break;
 120        default:
 121                xhci_warn(xhci, "unexpected write complete status %d\n",
 122                          req->status);
 123                break;
 124        }
 125        spin_unlock_irqrestore(&port->port_lock, flags);
 126}
 127
 128static void xhci_dbc_free_req(struct dbc_ep *dep, struct dbc_request *req)
 129{
 130        kfree(req->buf);
 131        dbc_free_request(dep, req);
 132}
 133
 134static int
 135xhci_dbc_alloc_requests(struct dbc_ep *dep, struct list_head *head,
 136                        void (*fn)(struct xhci_hcd *, struct dbc_request *))
 137{
 138        int                     i;
 139        struct dbc_request      *req;
 140
 141        for (i = 0; i < DBC_QUEUE_SIZE; i++) {
 142                req = dbc_alloc_request(dep, GFP_ATOMIC);
 143                if (!req)
 144                        break;
 145
 146                req->length = DBC_MAX_PACKET;
 147                req->buf = kmalloc(req->length, GFP_KERNEL);
 148                if (!req->buf) {
 149                        xhci_dbc_free_req(dep, req);
 150                        break;
 151                }
 152
 153                req->complete = fn;
 154                list_add_tail(&req->list_pool, head);
 155        }
 156
 157        return list_empty(head) ? -ENOMEM : 0;
 158}
 159
 160static void
 161xhci_dbc_free_requests(struct dbc_ep *dep, struct list_head *head)
 162{
 163        struct dbc_request      *req;
 164
 165        while (!list_empty(head)) {
 166                req = list_entry(head->next, struct dbc_request, list_pool);
 167                list_del(&req->list_pool);
 168                xhci_dbc_free_req(dep, req);
 169        }
 170}
 171
 172static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty)
 173{
 174        struct dbc_port         *port = driver->driver_state;
 175
 176        tty->driver_data = port;
 177
 178        return tty_port_install(&port->port, driver, tty);
 179}
 180
 181static int dbc_tty_open(struct tty_struct *tty, struct file *file)
 182{
 183        struct dbc_port         *port = tty->driver_data;
 184
 185        return tty_port_open(&port->port, tty, file);
 186}
 187
 188static void dbc_tty_close(struct tty_struct *tty, struct file *file)
 189{
 190        struct dbc_port         *port = tty->driver_data;
 191
 192        tty_port_close(&port->port, tty, file);
 193}
 194
 195static int dbc_tty_write(struct tty_struct *tty,
 196                         const unsigned char *buf,
 197                         int count)
 198{
 199        struct dbc_port         *port = tty->driver_data;
 200        unsigned long           flags;
 201
 202        spin_lock_irqsave(&port->port_lock, flags);
 203        if (count)
 204                count = kfifo_in(&port->write_fifo, buf, count);
 205        dbc_start_tx(port);
 206        spin_unlock_irqrestore(&port->port_lock, flags);
 207
 208        return count;
 209}
 210
 211static int dbc_tty_put_char(struct tty_struct *tty, unsigned char ch)
 212{
 213        struct dbc_port         *port = tty->driver_data;
 214        unsigned long           flags;
 215        int                     status;
 216
 217        spin_lock_irqsave(&port->port_lock, flags);
 218        status = kfifo_put(&port->write_fifo, ch);
 219        spin_unlock_irqrestore(&port->port_lock, flags);
 220
 221        return status;
 222}
 223
 224static void dbc_tty_flush_chars(struct tty_struct *tty)
 225{
 226        struct dbc_port         *port = tty->driver_data;
 227        unsigned long           flags;
 228
 229        spin_lock_irqsave(&port->port_lock, flags);
 230        dbc_start_tx(port);
 231        spin_unlock_irqrestore(&port->port_lock, flags);
 232}
 233
 234static int dbc_tty_write_room(struct tty_struct *tty)
 235{
 236        struct dbc_port         *port = tty->driver_data;
 237        unsigned long           flags;
 238        int                     room = 0;
 239
 240        spin_lock_irqsave(&port->port_lock, flags);
 241        room = kfifo_avail(&port->write_fifo);
 242        spin_unlock_irqrestore(&port->port_lock, flags);
 243
 244        return room;
 245}
 246
 247static int dbc_tty_chars_in_buffer(struct tty_struct *tty)
 248{
 249        struct dbc_port         *port = tty->driver_data;
 250        unsigned long           flags;
 251        int                     chars = 0;
 252
 253        spin_lock_irqsave(&port->port_lock, flags);
 254        chars = kfifo_len(&port->write_fifo);
 255        spin_unlock_irqrestore(&port->port_lock, flags);
 256
 257        return chars;
 258}
 259
 260static void dbc_tty_unthrottle(struct tty_struct *tty)
 261{
 262        struct dbc_port         *port = tty->driver_data;
 263        unsigned long           flags;
 264
 265        spin_lock_irqsave(&port->port_lock, flags);
 266        tasklet_schedule(&port->push);
 267        spin_unlock_irqrestore(&port->port_lock, flags);
 268}
 269
 270static const struct tty_operations dbc_tty_ops = {
 271        .install                = dbc_tty_install,
 272        .open                   = dbc_tty_open,
 273        .close                  = dbc_tty_close,
 274        .write                  = dbc_tty_write,
 275        .put_char               = dbc_tty_put_char,
 276        .flush_chars            = dbc_tty_flush_chars,
 277        .write_room             = dbc_tty_write_room,
 278        .chars_in_buffer        = dbc_tty_chars_in_buffer,
 279        .unthrottle             = dbc_tty_unthrottle,
 280};
 281
 282static struct tty_driver *dbc_tty_driver;
 283
 284int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci)
 285{
 286        int                     status;
 287        struct xhci_dbc         *dbc = xhci->dbc;
 288
 289        dbc_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW |
 290                                          TTY_DRIVER_DYNAMIC_DEV);
 291        if (IS_ERR(dbc_tty_driver)) {
 292                status = PTR_ERR(dbc_tty_driver);
 293                dbc_tty_driver = NULL;
 294                return status;
 295        }
 296
 297        dbc_tty_driver->driver_name = "dbc_serial";
 298        dbc_tty_driver->name = "ttyDBC";
 299
 300        dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
 301        dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
 302        dbc_tty_driver->init_termios = tty_std_termios;
 303        dbc_tty_driver->init_termios.c_cflag =
 304                        B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 305        dbc_tty_driver->init_termios.c_ispeed = 9600;
 306        dbc_tty_driver->init_termios.c_ospeed = 9600;
 307        dbc_tty_driver->driver_state = &dbc->port;
 308
 309        tty_set_operations(dbc_tty_driver, &dbc_tty_ops);
 310
 311        status = tty_register_driver(dbc_tty_driver);
 312        if (status) {
 313                xhci_err(xhci,
 314                         "can't register dbc tty driver, err %d\n", status);
 315                put_tty_driver(dbc_tty_driver);
 316                dbc_tty_driver = NULL;
 317        }
 318
 319        return status;
 320}
 321
 322void xhci_dbc_tty_unregister_driver(void)
 323{
 324        if (dbc_tty_driver) {
 325                tty_unregister_driver(dbc_tty_driver);
 326                put_tty_driver(dbc_tty_driver);
 327                dbc_tty_driver = NULL;
 328        }
 329}
 330
 331static void dbc_rx_push(unsigned long _port)
 332{
 333        struct dbc_request      *req;
 334        struct tty_struct       *tty;
 335        unsigned long           flags;
 336        bool                    do_push = false;
 337        bool                    disconnect = false;
 338        struct dbc_port         *port = (void *)_port;
 339        struct list_head        *queue = &port->read_queue;
 340
 341        spin_lock_irqsave(&port->port_lock, flags);
 342        tty = port->port.tty;
 343        while (!list_empty(queue)) {
 344                req = list_first_entry(queue, struct dbc_request, list_pool);
 345
 346                if (tty && tty_throttled(tty))
 347                        break;
 348
 349                switch (req->status) {
 350                case 0:
 351                        break;
 352                case -ESHUTDOWN:
 353                        disconnect = true;
 354                        break;
 355                default:
 356                        pr_warn("ttyDBC0: unexpected RX status %d\n",
 357                                req->status);
 358                        break;
 359                }
 360
 361                if (req->actual) {
 362                        char            *packet = req->buf;
 363                        unsigned int    n, size = req->actual;
 364                        int             count;
 365
 366                        n = port->n_read;
 367                        if (n) {
 368                                packet += n;
 369                                size -= n;
 370                        }
 371
 372                        count = tty_insert_flip_string(&port->port, packet,
 373                                                       size);
 374                        if (count)
 375                                do_push = true;
 376                        if (count != size) {
 377                                port->n_read += count;
 378                                break;
 379                        }
 380                        port->n_read = 0;
 381                }
 382
 383                list_move(&req->list_pool, &port->read_pool);
 384        }
 385
 386        if (do_push)
 387                tty_flip_buffer_push(&port->port);
 388
 389        if (!list_empty(queue) && tty) {
 390                if (!tty_throttled(tty)) {
 391                        if (do_push)
 392                                tasklet_schedule(&port->push);
 393                        else
 394                                pr_warn("ttyDBC0: RX not scheduled?\n");
 395                }
 396        }
 397
 398        if (!disconnect)
 399                dbc_start_rx(port);
 400
 401        spin_unlock_irqrestore(&port->port_lock, flags);
 402}
 403
 404static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty)
 405{
 406        unsigned long   flags;
 407        struct dbc_port *port = container_of(_port, struct dbc_port, port);
 408
 409        spin_lock_irqsave(&port->port_lock, flags);
 410        dbc_start_rx(port);
 411        spin_unlock_irqrestore(&port->port_lock, flags);
 412
 413        return 0;
 414}
 415
 416static const struct tty_port_operations dbc_port_ops = {
 417        .activate =     dbc_port_activate,
 418};
 419
 420static void
 421xhci_dbc_tty_init_port(struct xhci_hcd *xhci, struct dbc_port *port)
 422{
 423        tty_port_init(&port->port);
 424        spin_lock_init(&port->port_lock);
 425        tasklet_init(&port->push, dbc_rx_push, (unsigned long)port);
 426        INIT_LIST_HEAD(&port->read_pool);
 427        INIT_LIST_HEAD(&port->read_queue);
 428        INIT_LIST_HEAD(&port->write_pool);
 429
 430        port->in =              get_in_ep(xhci);
 431        port->out =             get_out_ep(xhci);
 432        port->port.ops =        &dbc_port_ops;
 433        port->n_read =          0;
 434}
 435
 436static void
 437xhci_dbc_tty_exit_port(struct dbc_port *port)
 438{
 439        tasklet_kill(&port->push);
 440        tty_port_destroy(&port->port);
 441}
 442
 443int xhci_dbc_tty_register_device(struct xhci_hcd *xhci)
 444{
 445        int                     ret;
 446        struct device           *tty_dev;
 447        struct xhci_dbc         *dbc = xhci->dbc;
 448        struct dbc_port         *port = &dbc->port;
 449
 450        xhci_dbc_tty_init_port(xhci, port);
 451        tty_dev = tty_port_register_device(&port->port,
 452                                           dbc_tty_driver, 0, NULL);
 453        if (IS_ERR(tty_dev)) {
 454                ret = PTR_ERR(tty_dev);
 455                goto register_fail;
 456        }
 457
 458        ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL);
 459        if (ret)
 460                goto buf_alloc_fail;
 461
 462        ret = xhci_dbc_alloc_requests(port->in, &port->read_pool,
 463                                      dbc_read_complete);
 464        if (ret)
 465                goto request_fail;
 466
 467        ret = xhci_dbc_alloc_requests(port->out, &port->write_pool,
 468                                      dbc_write_complete);
 469        if (ret)
 470                goto request_fail;
 471
 472        port->registered = true;
 473
 474        return 0;
 475
 476request_fail:
 477        xhci_dbc_free_requests(port->in, &port->read_pool);
 478        xhci_dbc_free_requests(port->out, &port->write_pool);
 479        kfifo_free(&port->write_fifo);
 480
 481buf_alloc_fail:
 482        tty_unregister_device(dbc_tty_driver, 0);
 483
 484register_fail:
 485        xhci_dbc_tty_exit_port(port);
 486
 487        xhci_err(xhci, "can't register tty port, err %d\n", ret);
 488
 489        return ret;
 490}
 491
 492void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci)
 493{
 494        struct xhci_dbc         *dbc = xhci->dbc;
 495        struct dbc_port         *port = &dbc->port;
 496
 497        tty_unregister_device(dbc_tty_driver, 0);
 498        xhci_dbc_tty_exit_port(port);
 499        port->registered = false;
 500
 501        kfifo_free(&port->write_fifo);
 502        xhci_dbc_free_requests(get_out_ep(xhci), &port->read_pool);
 503        xhci_dbc_free_requests(get_out_ep(xhci), &port->read_queue);
 504        xhci_dbc_free_requests(get_in_ep(xhci), &port->write_pool);
 505}
 506