1 /*
2 * Copyright (c) 2018 The Fuchsia Authors
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 #include <lib/cdcconsole/cdcconsole.h>
10
11 #include <lk/compiler.h>
12 #include <lk/err.h>
13 #include <string.h>
14
15 #if !CONSOLE_HAS_INPUT_BUFFER
16 #error "CONSOLE_HAS_INPUT_BUFFER needs to be configured for cdcconsole."
17 #endif
18
19 #ifndef CDCCONSOLE_TX_BUFFER_SIZE
20 #define CDCCONSOLE_TX_BUFFER_SIZE 64
21 #endif // CDCCONSOLE_TX_BUFFER_SIZE
22
23 /*
24 * This implementation is pretty simple right now. Possible future work:
25 * * Make cdcconsole_print() asynchronous.
26 * * Buffer output when offline so that you can see boot messages.
27 * * Buffer writes with a timeout allowing the driver to coalesce them into
28 * less packets.
29 */
30
31 static status_t cdcconsole_rx_cb(ep_t endpoint, usbc_transfer_t *t);
32 static status_t cdcconsole_tx_cb(ep_t endpoint, usbc_transfer_t *t);
33
34 // Call with con->tx_lock
cdcconsole_handle_tx(cdcconsole_t * con)35 static void cdcconsole_handle_tx(cdcconsole_t *con) {
36 iovec_t regions[2];
37 cbuf_peek(&con->tx_buf, regions);
38 if (regions[0].iov_len > 0) {
39 cdcserial_write_async(&con->cdc_chan, &con->tx_transfer, cdcconsole_tx_cb,
40 regions[0].iov_len, regions[0].iov_base);
41 con->transmitting = true;
42 } else {
43 con->transmitting = false;
44 }
45 }
46
cdcconsole_print(print_callback_t * cb,const char * str,size_t len)47 static void cdcconsole_print(print_callback_t *cb, const char *str, size_t len) {
48 cdcconsole_t *con = cb->context;
49
50 cbuf_write(&con->tx_buf, str, len, false);
51
52 if (con->online) {
53 spin_lock_saved_state_t state;
54 spin_lock_irqsave(&con->tx_lock, state);
55 if (!con->transmitting) {
56 cdcconsole_handle_tx(con);
57 }
58 spin_unlock_irqrestore(&con->tx_lock, state);
59 }
60 }
61
cdcconsole_queue_read(cdcconsole_t * con)62 static void cdcconsole_queue_read(cdcconsole_t *con) {
63 cdcserial_read_async(&con->cdc_chan, &con->rx_transfer, cdcconsole_rx_cb,
64 sizeof(con->rx_buf), con->rx_buf);
65 }
66
cdcconsole_tx_cb(ep_t endpoint,usbc_transfer_t * t)67 static status_t cdcconsole_tx_cb(ep_t endpoint, usbc_transfer_t *t) {
68 cdcconsole_t *con = containerof(t, cdcconsole_t, tx_transfer);
69 spin_lock_saved_state_t state;
70 spin_lock_irqsave(&con->tx_lock, state);
71
72 // Consume the peeked data.
73 cbuf_read(&con->tx_buf, NULL, t->bufpos, false);
74 cdcconsole_handle_tx(con);
75 spin_unlock_irqrestore(&con->tx_lock, state);
76 return NO_ERROR;
77 }
78
cdcconsole_rx_cb(ep_t endpoint,usbc_transfer_t * t)79 static status_t cdcconsole_rx_cb(ep_t endpoint, usbc_transfer_t *t) {
80 cdcconsole_t *con = containerof(t, cdcconsole_t, rx_transfer);
81
82 cbuf_write(&console_input_cbuf, t->buf, t->bufpos, false);
83 cdcconsole_queue_read(con);
84 return NO_ERROR;
85 }
86
cdcconsole_online(cdcserial_channel_t * chan,bool online)87 static void cdcconsole_online(cdcserial_channel_t *chan, bool online) {
88 cdcconsole_t *con = containerof(chan, cdcconsole_t, cdc_chan);
89 con->online = online;
90 if (online) {
91 spin_lock_saved_state_t state;
92 spin_lock_irqsave(&con->tx_lock, state);
93 if (!con->transmitting) {
94 // Throw away previous data.
95 cbuf_reset(&con->tx_buf);
96 }
97 spin_unlock_irqrestore(&con->tx_lock, state);
98
99 cdcconsole_queue_read(con);
100 }
101 }
102
cdcconsole_init(cdcconsole_t * con,int data_ep_addr,int ctrl_ep_addr)103 void cdcconsole_init(cdcconsole_t *con, int data_ep_addr, int ctrl_ep_addr) {
104 memset(con, 0x0, sizeof(*con));
105
106 cbuf_initialize(&con->tx_buf, CDCCONSOLE_TX_BUFFER_SIZE);
107 spin_lock_init(&con->tx_lock);
108
109 con->cdc_chan.online_cb = cdcconsole_online;
110 cdcserial_create_channel(&con->cdc_chan, data_ep_addr, ctrl_ep_addr);
111
112 con->print_cb.print = cdcconsole_print;
113 con->print_cb.context = con;
114 register_print_callback(&con->print_cb);
115 }
116