1// Copyright 2017 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 <stdio.h> 6#include <stdlib.h> 7#include <string.h> 8#include <threads.h> 9 10#include <ddk/device.h> 11#include <ddk/driver.h> 12#include <ddk/binding.h> 13 14#include "pty-core.h" 15#include "pty-fifo.h" 16 17#include <zircon/device/pty.h> 18 19typedef struct pty_server_dev { 20 pty_server_t srv; 21 22 mtx_t lock; 23 pty_fifo_t fifo; 24} pty_server_dev_t; 25 26static zx_device_t* pty_root; 27 28#define psd_from_ps(ps) containerof(ps, pty_server_dev_t, srv) 29 30static zx_status_t psd_recv(pty_server_t* ps, const void* data, size_t len, size_t* actual) { 31 if (len == 0) { 32 return 0; 33 } 34 35 pty_server_dev_t* psd = psd_from_ps(ps); 36 37 bool was_empty = pty_fifo_is_empty(&psd->fifo); 38 *actual = pty_fifo_write(&psd->fifo, data, len, false); 39 if (was_empty && *actual) { 40 device_state_set(ps->zxdev, DEV_STATE_READABLE); 41 } 42 43 if (*actual == 0) { 44 return ZX_ERR_SHOULD_WAIT; 45 } else { 46 return ZX_OK; 47 } 48} 49 50static zx_status_t psd_read(void* ctx, void* buf, size_t count, zx_off_t off, size_t* actual) { 51 pty_server_dev_t* psd = ctx; 52 53 bool eof = false; 54 55 mtx_lock(&psd->srv.lock); 56 bool was_full = pty_fifo_is_full(&psd->fifo); 57 size_t length = pty_fifo_read(&psd->fifo, buf, count); 58 if (pty_fifo_is_empty(&psd->fifo)) { 59 if (list_is_empty(&psd->srv.clients)) { 60 eof = true; 61 } else { 62 device_state_clr(psd->srv.zxdev, DEV_STATE_READABLE); 63 } 64 } 65 if (was_full && length) { 66 pty_server_resume_locked(&psd->srv); 67 } 68 mtx_unlock(&psd->srv.lock); 69 70 if (length > 0) { 71 *actual = length; 72 return ZX_OK; 73 } else if (eof) { 74 *actual = 0; 75 return ZX_OK; 76 } else { 77 return ZX_ERR_SHOULD_WAIT; 78 } 79} 80 81static zx_status_t psd_write(void* ctx, const void* buf, size_t count, zx_off_t off, 82 size_t* actual) { 83 pty_server_dev_t* psd = ctx; 84 size_t length; 85 zx_status_t status; 86 87 if ((status = pty_server_send(&psd->srv, buf, count, false, &length)) < 0) { 88 return status; 89 } else { 90 *actual = length; 91 return ZX_OK; 92 } 93} 94 95static zx_status_t psd_ioctl(void* ctx, uint32_t op, 96 const void* in_buf, size_t in_len, 97 void* out_buf, size_t out_len, size_t* out_actual) { 98 pty_server_dev_t* psd = ctx; 99 100 switch (op) { 101 case IOCTL_PTY_SET_WINDOW_SIZE: { 102 const pty_window_size_t* wsz = in_buf; 103 if (in_len != sizeof(pty_window_size_t)) { 104 return ZX_ERR_INVALID_ARGS; 105 } 106 pty_server_set_window_size(&psd->srv, wsz->width, wsz->height); 107 return ZX_OK; 108 } 109 default: 110 return ZX_ERR_NOT_SUPPORTED; 111 } 112} 113 114// Since we have no special functionality, 115// we just use the implementations from pty-core 116// directly. 117static zx_protocol_device_t psd_ops = { 118 .version = DEVICE_OPS_VERSION, 119 // .open = default, allow cloning 120 .open_at = pty_server_openat, 121 .release = pty_server_release, 122 .read = psd_read, 123 .write = psd_write, 124 .ioctl = psd_ioctl, 125}; 126 127 128// ptmx device - used to obtain the pty server of a new pty instance 129 130static zx_status_t ptmx_open(void* ctx, zx_device_t** out, uint32_t flags) { 131 pty_server_dev_t* psd; 132 if ((psd = calloc(1, sizeof(pty_server_dev_t))) == NULL) { 133 return ZX_ERR_NO_MEMORY; 134 } 135 136 pty_server_init(&psd->srv); 137 psd->srv.recv = psd_recv; 138 mtx_init(&psd->lock, mtx_plain); 139 psd->fifo.head = 0; 140 psd->fifo.tail = 0; 141 142 device_add_args_t args = { 143 .version = DEVICE_ADD_ARGS_VERSION, 144 .name = "pty", 145 .ctx = psd, 146 .ops = &psd_ops, 147 .proto_id = ZX_PROTOCOL_PTY, 148 .flags = DEVICE_ADD_INSTANCE, 149 }; 150 151 zx_status_t status; 152 if ((status = device_add(pty_root, &args, &psd->srv.zxdev)) < 0) { 153 free(psd); 154 return status; 155 } 156 157 *out = psd->srv.zxdev; 158 return ZX_OK; 159} 160 161static zx_protocol_device_t ptmx_ops = { 162 .version = DEVICE_OPS_VERSION, 163 .open = ptmx_open, 164}; 165 166static zx_status_t ptmx_bind(void* ctx, zx_device_t* parent) { 167 device_add_args_t args = { 168 .version = DEVICE_ADD_ARGS_VERSION, 169 .name = "ptmx", 170 .ops = &ptmx_ops, 171 }; 172 173 return device_add(parent, &args, &pty_root); 174} 175 176static zx_driver_ops_t ptmx_driver_ops = { 177 .version = DRIVER_OPS_VERSION, 178 .bind = ptmx_bind, 179}; 180 181ZIRCON_DRIVER_BEGIN(ptmx, ptmx_driver_ops, "zircon", "0.1", 1) 182 BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_MISC_PARENT), 183ZIRCON_DRIVER_END(ptzx) 184