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/driver.h>
7 #include <ddktl/device.h>
8 #include <ddktl/protocol/test.h>
9 #include <fbl/algorithm.h>
10 #include <fbl/string_piece.h>
11 #include <fbl/unique_ptr.h>
12 #include <fuchsia/device/test/c/fidl.h>
13 #include <lib/zx/channel.h>
14 #include <lib/zx/socket.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 namespace {
20
21 class TestDevice;
22 using TestDeviceType = ddk::Device<TestDevice, ddk::Messageable>;
23
24 class TestDevice : public TestDeviceType,
25 public ddk::TestProtocol<TestDevice, ddk::base_protocol> {
26 public:
TestDevice(zx_device_t * parent)27 TestDevice(zx_device_t* parent) : TestDeviceType(parent) { }
28
29 // Methods required by the ddk mixins
30 zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn);
31 void DdkRelease();
32
33 // Methods required by the TestProtocol mixin
34 void TestSetOutputSocket(zx::socket socket);
35 void TestGetOutputSocket(zx::socket* out_socket);
36 void TestGetChannel(zx::channel* out_channel);
37 void TestSetTestFunc(const test_func_t* func);
38 zx_status_t TestRunTests(test_report_t* out_report);
39 void TestDestroy();
40
41 void SetChannel(zx::channel c);
42 private:
43 zx::socket output_;
44 zx::channel channel_;
45 test_func_t test_func_;
46 };
47
48 class TestRootDevice;
49 using TestRootDeviceType = ddk::Device<TestRootDevice, ddk::Messageable>;
50
51 class TestRootDevice : public TestRootDeviceType {
52 public:
TestRootDevice(zx_device_t * parent)53 TestRootDevice(zx_device_t* parent) : TestRootDeviceType(parent) { }
54
Bind()55 zx_status_t Bind() {
56 return DdkAdd("test");
57 }
58
59 // Methods required by the ddk mixins
60 zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn);
DdkRelease()61 void DdkRelease() { ZX_ASSERT_MSG(false, "TestRootDevice::DdkRelease() not supported\n"); }
62
63 static zx_status_t FidlCreateDevice(void* ctx, const char* name_data, size_t name_len,
64 fidl_txn_t* txn);
65 private:
66 // Create a new child device with this |name|
67 zx_status_t CreateDevice(const fbl::StringPiece& name,
68 char* path_out, size_t path_size, size_t* path_actual);
69 };
70
TestSetOutputSocket(zx::socket socket)71 void TestDevice::TestSetOutputSocket(zx::socket socket) {
72 output_ = std::move(socket);
73 }
74
TestGetOutputSocket(zx::socket * out_socket)75 void TestDevice::TestGetOutputSocket(zx::socket* out_socket) {
76 output_.duplicate(ZX_RIGHT_SAME_RIGHTS, out_socket);
77 }
78
TestGetChannel(zx::channel * out_channel)79 void TestDevice::TestGetChannel(zx::channel* out_channel) {
80 *out_channel = std::move(channel_);
81 }
82
TestSetTestFunc(const test_func_t * func)83 void TestDevice::TestSetTestFunc(const test_func_t* func) {
84 test_func_ = *func;
85 }
86
TestRunTests(test_report_t * report)87 zx_status_t TestDevice::TestRunTests(test_report_t* report) {
88 if (test_func_.callback == NULL) {
89 return ZX_ERR_NOT_SUPPORTED;
90 }
91 return test_func_.callback(test_func_.ctx, report);
92 }
93
TestDestroy()94 void TestDevice::TestDestroy() {
95 DdkRemove();
96 }
97
fidl_SetOutputSocket(void * ctx,zx_handle_t raw_socket)98 static zx_status_t fidl_SetOutputSocket(void* ctx, zx_handle_t raw_socket) {
99 zx::socket socket(raw_socket);
100 auto dev = static_cast<TestDevice*>(ctx);
101 dev->TestSetOutputSocket(std::move(socket));
102 return ZX_OK;
103 }
104
SetChannel(zx::channel c)105 void TestDevice::SetChannel(zx::channel c) {
106 channel_ = std::move(c);
107 }
108
fidl_SetChannel(void * ctx,zx_handle_t raw_channel)109 static zx_status_t fidl_SetChannel(void* ctx, zx_handle_t raw_channel) {
110 zx::channel channel(raw_channel);
111 auto dev = static_cast<TestDevice*>(ctx);
112 dev->SetChannel(std::move(channel));
113 return ZX_OK;
114 }
115
fidl_RunTests(void * ctx,fidl_txn_t * txn)116 static zx_status_t fidl_RunTests(void* ctx, fidl_txn_t* txn) {
117 auto dev = static_cast<TestDevice*>(ctx);
118 test_report_t report = {};
119 fuchsia_device_test_TestReport fidl_report = {};
120
121 zx_status_t status = dev->TestRunTests(&report);
122 if (status == ZX_OK) {
123 fidl_report.test_count = report.n_tests;
124 fidl_report.success_count = report.n_success;
125 fidl_report.failure_count = report.n_failed;
126 }
127 return fuchsia_device_test_DeviceRunTests_reply(txn, status, &fidl_report);
128 }
129
fidl_Destroy(void * ctx)130 static zx_status_t fidl_Destroy(void* ctx) {
131 auto dev = static_cast<TestDevice*>(ctx);
132 dev->TestDestroy();
133 return ZX_OK;
134 }
135
DdkMessage(fidl_msg_t * msg,fidl_txn_t * txn)136 zx_status_t TestDevice::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
137 static const fuchsia_device_test_Device_ops_t kOps = {
138 .SetOutputSocket = fidl_SetOutputSocket,
139 .SetChannel = fidl_SetChannel,
140 .RunTests = fidl_RunTests,
141 .Destroy = fidl_Destroy,
142 };
143 return fuchsia_device_test_Device_dispatch(this, txn, msg, &kOps);
144 }
145
DdkRelease()146 void TestDevice::DdkRelease() {
147 delete this;
148 }
149
CreateDevice(const fbl::StringPiece & name,char * path_out,size_t path_size,size_t * path_actual)150 zx_status_t TestRootDevice::CreateDevice(const fbl::StringPiece& name,
151 char* path_out, size_t path_size, size_t* path_actual) {
152 static_assert(fuchsia_device_test_MAX_DEVICE_NAME_LEN == ZX_DEVICE_NAME_MAX);
153
154 char devname[ZX_DEVICE_NAME_MAX + 1] = {};
155 if (name.size() > 0) {
156 memcpy(devname, name.data(), fbl::min(sizeof(devname) - 1, name.size()));
157 } else {
158 strncpy(devname, "testdev", sizeof(devname) - 1);
159 }
160 devname[sizeof(devname) - 1] = '\0';
161 // truncate trailing ".so"
162 if (!strcmp(devname + strlen(devname) - 3, ".so")) {
163 devname[strlen(devname) - 3] = 0;
164 }
165
166 if (path_size < strlen(devname) + sizeof(fuchsia_device_test_CONTROL_DEVICE) + 1) {
167 return ZX_ERR_BUFFER_TOO_SMALL;
168 }
169
170 auto device = fbl::make_unique<TestDevice>(zxdev());
171 zx_status_t status = device->DdkAdd(devname);
172 if (status != ZX_OK) {
173 return status;
174 }
175 // devmgr now owns this
176 __UNUSED auto ptr = device.release();
177
178 *path_actual = snprintf(path_out, path_size ,"%s/%s", fuchsia_device_test_CONTROL_DEVICE,
179 devname);
180 return ZX_OK;
181 }
182
FidlCreateDevice(void * ctx,const char * name_data,size_t name_len,fidl_txn_t * txn)183 zx_status_t TestRootDevice::FidlCreateDevice(void* ctx, const char* name_data, size_t name_len,
184 fidl_txn_t* txn) {
185 auto root = static_cast<TestRootDevice*>(ctx);
186
187 char path[fuchsia_device_test_MAX_DEVICE_PATH_LEN];
188 size_t path_size = 0;
189 zx_status_t status = root->CreateDevice(fbl::StringPiece(name_data, name_len),
190 path, sizeof(path), &path_size);
191 return fuchsia_device_test_RootDeviceCreateDevice_reply(txn, status, path, path_size);
192 }
193
DdkMessage(fidl_msg_t * msg,fidl_txn_t * txn)194 zx_status_t TestRootDevice::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
195 static const fuchsia_device_test_RootDevice_ops_t kOps = {
196 .CreateDevice = TestRootDevice::FidlCreateDevice,
197 };
198 return fuchsia_device_test_RootDevice_dispatch(this, txn, msg, &kOps);
199 }
200
TestDriverBind(void * ctx,zx_device_t * dev)201 zx_status_t TestDriverBind(void* ctx, zx_device_t* dev) {
202 auto root = fbl::make_unique<TestRootDevice>(dev);
203 zx_status_t status = root->Bind();
204 if (status != ZX_OK) {
205 return status;
206 }
207 // devmgr now owns root
208 __UNUSED auto ptr = root.release();
209 return ZX_OK;
210 }
211
__anonebd3d2880202() 212 const zx_driver_ops_t kTestDriverOps = []() {
213 zx_driver_ops_t driver;
214 driver.version = DRIVER_OPS_VERSION;
215 driver.bind = TestDriverBind;
216 return driver;
217 }();
218
219 } // namespace
220
221 ZIRCON_DRIVER_BEGIN(test, kTestDriverOps, "zircon", "0.1", 1)
222 BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_TEST_PARENT),
223 ZIRCON_DRIVER_END(test)
224