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 20typedef struct console_ctx { 21 zx_device_t* zxdev; 22} console_device_t; 23 24static 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 33static 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 42static 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 50static 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 76static 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 100static 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 120static void console_release(void* ctx) { 121 console_device_t* console = ctx; 122 free(console); 123} 124 125static 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 132static zx_status_t console_bind(void* ctx, zx_device_t* parent) { 133 console_device_t* console = calloc(1, sizeof(console_device_t)); 134 if (!console) { 135 return ZX_ERR_NO_MEMORY; 136 } 137 device_add_args_t args = { 138 .version = DEVICE_ADD_ARGS_VERSION, 139 .name = "console", 140 .ctx = console, 141 .ops = &console_device_proto, 142 }; 143 144 zx_status_t status = device_add(parent, &args, &console->zxdev); 145 if (status != ZX_OK) { 146 printf("console: device_add() failed\n"); 147 free(console); 148 return status; 149 } 150 151 thrd_t t; 152 thrd_create_with_name(&t, debug_reader, console->zxdev, "debug-reader"); 153 154 return ZX_OK; 155} 156 157static zx_driver_ops_t console_driver_ops = { 158 .version = DRIVER_OPS_VERSION, 159 .bind = console_bind, 160}; 161 162ZIRCON_DRIVER_BEGIN(console, console_driver_ops, "zircon", "0.1", 1) 163 BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_MISC_PARENT), 164ZIRCON_DRIVER_END(console) 165