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