1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2025 Oxide Computer Company
14 */
15
16 /*
17 * Verify that we fail when we're missing a field in a given request. This works
18 * from the empty device profile and targets smbussim1 where there will be no
19 * devices.
20 */
21
22 #include <stdlib.h>
23 #include <err.h>
24
25 #include "libi2c_test_util.h"
26
27 static bool
test_add(i2c_hdl_t * hdl,i2c_dev_add_req_t * req,const char * desc)28 test_add(i2c_hdl_t *hdl, i2c_dev_add_req_t *req, const char *desc)
29 {
30 if (i2c_device_add_req_exec(req)) {
31 warnx("TEST FAILED: incomplete device add (%s) accidentally "
32 "succeeded", desc);
33 return (false);
34 } else if (i2c_err(hdl) != I2C_ERR_ADD_DEV_REQ_MISSING_FIELDS) {
35 warnx("TEST FAILED: incomplete device add (%s) failed with "
36 "%s (0x%x), expected I2C_ERR_ADD_DEV_REQ_MISSING_FIELDS "
37 "(0x%x)", desc, i2c_errtostr(hdl, i2c_err(hdl)),
38 i2c_err(hdl), I2C_ERR_ADD_DEV_REQ_MISSING_FIELDS);
39 return (false);
40 } else {
41 (void) printf("TEST PASSED: incomplete device add (%s) failed "
42 "with I2C_ERR_ADD_DEV_REQ_MISSING_FIELDS\n", desc);
43 return (true);
44 }
45 }
46
47 /*
48 * Test that we error with missing fields. The compatible field is optional so
49 * this is basically just checking combinations of name and addr.
50 */
51 static bool
missing_adds(i2c_hdl_t * hdl,i2c_port_t * port)52 missing_adds(i2c_hdl_t *hdl, i2c_port_t *port)
53 {
54 bool ret = true;
55 i2c_dev_add_req_t *add;
56
57 if (!i2c_device_add_req_init(port, &add)) {
58 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
59 "initialize device add requiest");
60 }
61
62 if (!test_add(hdl, add, "all")) {
63 ret = false;
64 }
65
66 const i2c_addr_t addr = { I2C_ADDR_7BIT, 0x23 };
67 if (!i2c_device_add_req_set_addr(add, &addr)) {
68 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to set "
69 "device add address");
70 }
71
72 if (!test_add(hdl, add, "name")) {
73 ret = false;
74 }
75 i2c_device_add_req_fini(add);
76
77 if (!i2c_device_add_req_init(port, &add)) {
78 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
79 "initialize device add requiest");
80 }
81
82 if (!i2c_device_add_req_set_name(add, "foobar")) {
83 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to set "
84 "device add address");
85 }
86
87 if (!test_add(hdl, add, "addr")) {
88 ret = false;
89 }
90
91 i2c_device_add_req_fini(add);
92
93 return (ret);
94 }
95
96 static bool
test_i2c_io(i2c_hdl_t * hdl,i2c_io_req_t * req,const char * desc)97 test_i2c_io(i2c_hdl_t *hdl, i2c_io_req_t *req, const char *desc)
98 {
99 if (i2c_io_req_exec(req)) {
100 warnx("TEST FAILED: incomplete I2C I/O (%s) accidentally "
101 "succeeded", desc);
102 return (false);
103 } else if (i2c_err(hdl) != I2C_ERR_IO_REQ_MISSING_FIELDS) {
104 warnx("TEST FAILED: incomplete I2C I/O (%s) failed with "
105 "%s (0x%x), expected I2C_ERR_IO_REQ_MISSING_FIELDS (0x%x)",
106 desc, i2c_errtostr(hdl, i2c_err(hdl)), i2c_err(hdl),
107 I2C_ERR_IO_REQ_MISSING_FIELDS);
108 return (false);
109 } else {
110 (void) printf("TEST PASSED: incomplete I2C I/O (%s) failed "
111 "with I2C_ERR_IO_REQ_MISSING_FIELDS\n", desc);
112 return (true);
113 }
114 }
115
116 static bool
missing_i2c_io(i2c_hdl_t * hdl,i2c_port_t * port)117 missing_i2c_io(i2c_hdl_t *hdl, i2c_port_t *port)
118 {
119 bool ret = true;
120 i2c_io_req_t *req;
121
122 if (!i2c_io_req_init(port, &req)) {
123 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
124 "initialize I2C I/O request");
125 }
126
127 if (!test_i2c_io(hdl, req, "all")) {
128 ret = false;
129 }
130
131 uint8_t rbuf[4] = { 0 };
132 uint8_t wbuf[4] = { 0 };
133 if (!i2c_io_req_set_transmit_data(req, wbuf, sizeof (wbuf))) {
134 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
135 "set I2C transmit buffer");
136 }
137
138 if (!i2c_io_req_set_receive_buf(req, rbuf, sizeof (rbuf))) {
139 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
140 "set I2C receive buffer");
141 }
142
143 if (!test_i2c_io(hdl, req, "addr")) {
144 ret = false;
145 }
146 i2c_io_req_fini(req);
147 return (ret);
148 }
149
150 static bool
test_smbus_io(i2c_hdl_t * hdl,smbus_io_req_t * req,const char * desc)151 test_smbus_io(i2c_hdl_t *hdl, smbus_io_req_t *req, const char *desc)
152 {
153 if (smbus_io_req_exec(req)) {
154 warnx("TEST FAILED: incomplete SMBus I/O (%s) accidentally "
155 "succeeded", desc);
156 return (false);
157 } else if (i2c_err(hdl) != I2C_ERR_IO_REQ_MISSING_FIELDS) {
158 warnx("TEST FAILED: incomplete SMBus I/O (%s) failed with "
159 "%s (0x%x), expected I2C_ERR_IO_REQ_MISSING_FIELDS (0x%x)",
160 desc, i2c_errtostr(hdl, i2c_err(hdl)), i2c_err(hdl),
161 I2C_ERR_IO_REQ_MISSING_FIELDS);
162 return (false);
163 } else {
164 (void) printf("TEST PASSED: incomplete SMBus I/O (%s) failed "
165 "with I2C_ERR_IO_REQ_MISSING_FIELDS\n", desc);
166 return (true);
167 }
168 }
169
170 /*
171 * We only test a handful of the various SMBus operations here and hope that if
172 * it works for them it works for most.
173 */
174 static bool
missing_smbus_io(i2c_hdl_t * hdl,i2c_port_t * port)175 missing_smbus_io(i2c_hdl_t *hdl, i2c_port_t *port)
176 {
177 bool ret = true;
178 smbus_io_req_t *req;
179
180 if (!smbus_io_req_init(port, &req)) {
181 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
182 "initialize SMBus I/O request");
183 }
184
185 if (!test_smbus_io(hdl, req, "all")) {
186 ret = false;
187 }
188 smbus_io_req_fini(req);
189
190 if (!smbus_io_req_init(port, &req)) {
191 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
192 "initialize SMBus I/O request");
193 }
194
195 const i2c_addr_t addr = { I2C_ADDR_7BIT, 0x42 };
196 if (!smbus_io_req_set_addr(req, &addr)) {
197 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
198 "set SMBus request address");
199 }
200
201 if (!test_smbus_io(hdl, req, "command")) {
202 ret = false;
203 }
204 smbus_io_req_fini(req);
205
206 if (!smbus_io_req_init(port, &req)) {
207 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
208 "initialize SMBus I/O request");
209 }
210
211 if (!smbus_io_req_set_write_u16(req, 0x23, 0x7777)) {
212 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
213 "set write u16 command");
214 }
215
216 if (!test_smbus_io(hdl, req, "addr: u16")) {
217 ret = false;
218 }
219 smbus_io_req_fini(req);
220
221 if (!smbus_io_req_init(port, &req)) {
222 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
223 "initialize SMBus I/O request");
224 }
225
226 uint8_t buf[4];
227 if (!smbus_io_req_set_read_block_i2c(req, 0x23, buf, sizeof (buf))) {
228 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
229 "set read I2C block command");
230 }
231
232 if (!test_smbus_io(hdl, req, "addr: read block i2c")) {
233 ret = false;
234 }
235 smbus_io_req_fini(req);
236
237
238 return (ret);
239 }
240
241 int
main(void)242 main(void)
243 {
244 i2c_port_t *port;
245 int ret = EXIT_SUCCESS;
246 i2c_hdl_t *hdl = i2c_init();
247 if (hdl == NULL) {
248 err(EXIT_FAILURE, "INTERNAL TEST FAILURE: failed to create "
249 "libi2c handle");
250 }
251
252 if (!i2c_port_init_by_path(hdl, "smbussim1/0", &port)) {
253 libi2c_test_fatal(hdl, "INTERNAL TEST FAILURE: failed to "
254 "initialize port smbussim1/0");
255 }
256
257 if (!missing_adds(hdl, port)) {
258 ret = EXIT_FAILURE;
259 }
260
261 if (!missing_i2c_io(hdl, port)) {
262 ret = EXIT_FAILURE;
263 }
264
265 if (!missing_smbus_io(hdl, port)) {
266 ret = EXIT_FAILURE;
267 }
268
269 if (ret == EXIT_SUCCESS) {
270 (void) printf("All tests passed successfully\n");
271 }
272 i2c_port_fini(port);
273 i2c_fini(hdl);
274 return (ret);
275 }
276