1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <ddk/binding.h>
6 #include <ddk/device.h>
7 #include <ddk/driver.h>
8 
9 #include <zircon/syscalls.h>
10 #include <zircon/types.h>
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <threads.h>
16 
17 #define FIFOSIZE 256
18 #define FIFOMASK (FIFOSIZE - 1)
19 
20 typedef struct console_ctx {
21     zx_device_t* zxdev;
22 } console_device_t;
23 
24 static struct {
25     uint8_t data[FIFOSIZE];
26     uint32_t head;
27     uint32_t tail;
28     mtx_t lock;
29 } fifo = {
30     .lock = MTX_INIT,
31 };
32 
fifo_read(uint8_t * out)33 static zx_status_t fifo_read(uint8_t* out) {
34     if (fifo.head == fifo.tail) {
35         return -1;
36     }
37     *out = fifo.data[fifo.tail];
38     fifo.tail = (fifo.tail + 1) & FIFOMASK;
39     return ZX_OK;
40 }
41 
fifo_write(uint8_t x)42 static void fifo_write(uint8_t x) {
43     uint32_t next = (fifo.head + 1) & FIFOMASK;
44     if (next != fifo.tail) {
45         fifo.data[fifo.head] = x;
46         fifo.head = next;
47     }
48 }
49 
debug_reader(void * arg)50 static int debug_reader(void* arg) {
51     zx_device_t* dev = arg;
52     char ch;
53     for (;;) {
54         size_t length = 1;
55         zx_status_t status = zx_debug_read(get_root_resource(), &ch, &length);
56         if (status == ZX_OK && length == 1) {
57             mtx_lock(&fifo.lock);
58             if (fifo.head == fifo.tail) {
59                 device_state_set(dev, DEV_STATE_READABLE);
60             }
61             fifo_write(ch);
62             mtx_unlock(&fifo.lock);
63         } else if (status == ZX_ERR_NOT_SUPPORTED) {
64             // Silently exit
65             return 0;
66         } else {
67             printf("console: error %d, length %zu from zx_debug_read syscall, exiting.\n",
68                     status, length);
69 
70             return status;
71         }
72     }
73     return 0;
74 }
75 
console_read(void * ctx,void * buf,size_t count,zx_off_t off,size_t * actual)76 static zx_status_t console_read(void* ctx, void* buf, size_t count, zx_off_t off, size_t* actual) {
77     console_device_t* console = ctx;
78 
79     uint8_t* data = buf;
80     mtx_lock(&fifo.lock);
81     while (count-- > 0) {
82         if (fifo_read(data))
83             break;
84         data++;
85     }
86     if (fifo.head == fifo.tail) {
87         device_state_clr(console->zxdev, DEV_STATE_READABLE);
88     }
89     mtx_unlock(&fifo.lock);
90     ssize_t length = data - (uint8_t*)buf;
91     if (length == 0) {
92         return ZX_ERR_SHOULD_WAIT;
93     }
94     *actual = length;
95     return ZX_OK;
96 }
97 
98 #define MAX_WRITE_SIZE 256
99 
console_write(void * ctx,const void * buf,size_t count,zx_off_t off,size_t * actual)100 static zx_status_t console_write(void* ctx, const void* buf, size_t count, zx_off_t off, size_t* actual) {
101     const void* ptr = buf;
102     zx_status_t status = ZX_OK;
103     size_t total = 0;
104     while (count > 0) {
105         size_t xfer = (count > MAX_WRITE_SIZE) ? MAX_WRITE_SIZE : count;
106         if ((status = zx_debug_write(ptr, xfer)) < 0) {
107             break;
108         }
109         ptr += xfer;
110         count -= xfer;
111         total += xfer;
112     }
113     if (total > 0) {
114         *actual = total;
115         status = ZX_OK;
116      }
117      return status;
118 }
119 
console_release(void * ctx)120 static void console_release(void* ctx) {
121     console_device_t* console = ctx;
122     free(console);
123 }
124 
125 static zx_protocol_device_t console_device_proto = {
126     .version = DEVICE_OPS_VERSION,
127     .read = console_read,
128     .write = console_write,
129     .release = console_release,
130 };
131 
console_bind(void * ctx,zx_device_t * parent)132 static zx_status_t console_bind(void* ctx, zx_device_t* parent) {
133     // If we're in an isolated devmgr, we won't have the root resource.  In that
134     // case, just don't bind this driver.
135     if (get_root_resource() == ZX_HANDLE_INVALID) {
136         return ZX_ERR_NOT_SUPPORTED;
137     }
138 
139     console_device_t* console = calloc(1, sizeof(console_device_t));
140     if (!console) {
141         return ZX_ERR_NO_MEMORY;
142     }
143     device_add_args_t args = {
144         .version = DEVICE_ADD_ARGS_VERSION,
145         .name = "console",
146         .ctx = console,
147         .ops = &console_device_proto,
148     };
149 
150     zx_status_t status = device_add(parent, &args, &console->zxdev);
151     if (status != ZX_OK) {
152         printf("console: device_add() failed\n");
153         free(console);
154         return status;
155     }
156 
157     thrd_t t;
158     thrd_create_with_name(&t, debug_reader, console->zxdev, "debug-reader");
159 
160     return ZX_OK;
161 }
162 
163 static zx_driver_ops_t console_driver_ops = {
164     .version = DRIVER_OPS_VERSION,
165     .bind = console_bind,
166 };
167 
168 ZIRCON_DRIVER_BEGIN(console, console_driver_ops, "zircon", "0.1", 1)
169     BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_MISC_PARENT),
170 ZIRCON_DRIVER_END(console)
171