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