root/drivers/usb/host/xhci-dbgtty.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dbc_send_packet
  2. dbc_start_tx
  3. dbc_start_rx
  4. dbc_read_complete
  5. dbc_write_complete
  6. xhci_dbc_free_req
  7. xhci_dbc_alloc_requests
  8. xhci_dbc_free_requests
  9. dbc_tty_install
  10. dbc_tty_open
  11. dbc_tty_close
  12. dbc_tty_write
  13. dbc_tty_put_char
  14. dbc_tty_flush_chars
  15. dbc_tty_write_room
  16. dbc_tty_chars_in_buffer
  17. dbc_tty_unthrottle
  18. xhci_dbc_tty_register_driver
  19. xhci_dbc_tty_unregister_driver
  20. dbc_rx_push
  21. dbc_port_activate
  22. xhci_dbc_tty_init_port
  23. xhci_dbc_tty_exit_port
  24. xhci_dbc_tty_register_device
  25. xhci_dbc_tty_unregister_device

   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 
  17 static unsigned int
  18 dbc_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 
  30 static 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 
  66 static 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 
  93 static void
  94 dbc_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 
 106 static 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 
 128 static 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 
 134 static int
 135 xhci_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_KERNEL);
 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                         dbc_free_request(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 
 160 static void
 161 xhci_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 
 172 static 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 
 181 static 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 
 188 static 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 
 195 static 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 
 211 static 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 
 224 static 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 
 234 static 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 
 247 static 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 
 260 static 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 
 270 static 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 
 282 static struct tty_driver *dbc_tty_driver;
 283 
 284 int 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 
 322 void 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 
 331 static 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 
 404 static 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 
 416 static const struct tty_port_operations dbc_port_ops = {
 417         .activate =     dbc_port_activate,
 418 };
 419 
 420 static void
 421 xhci_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 
 436 static void
 437 xhci_dbc_tty_exit_port(struct dbc_port *port)
 438 {
 439         tasklet_kill(&port->push);
 440         tty_port_destroy(&port->port);
 441 }
 442 
 443 int 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 
 476 request_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 
 481 buf_alloc_fail:
 482         tty_unregister_device(dbc_tty_driver, 0);
 483 
 484 register_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 
 492 void 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 }

/* [<][>][^][v][top][bottom][index][help] */