xref: /illumos-gate/usr/src/uts/common/io/i2c/ctrl/i2csim/i2csim.c (revision 0cbe48189888d02563dba9c90132ac391ba233b6)
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  * I2C Controller that is used for userland simulation. This is designd for
18  * testing purposes. The main instance creates a character device that is used
19  * to communicate with userland. A single dev_info_t is created which we
20  * register with the controller framework multiple times to represent a few
21  * different devices. Userland is responsible for reading and replying to I/O
22  * requests. Property requests are instead handled in the kernel.
23  */
24 
25 #include <sys/modctl.h>
26 #include <sys/conf.h>
27 #include <sys/devops.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/file.h>
33 #include <sys/open.h>
34 #include <sys/cred.h>
35 
36 #include <sys/i2c/controller.h>
37 #include "i2csim.h"
38 
39 /*
40  * We currently have three controllers. One that is pure i2c. One that is a
41  * 2-port pure SMBus. One that is a hybrid SMBus and I2C controller.
42  */
43 #define	I2CSIM_NCTRLS	2u
44 
45 /*
46  * This is an arbitrary and odd size to try to trigger some edge conditions in
47  * drivers.
48  */
49 #define	I2CSIM_I2C_MAX	77
50 
51 typedef struct i2csim_ctrl {
52 	const char *isc_name;
53 	uint32_t isc_nports;
54 	i2c_ctrl_type_t isc_type;
55 	const i2c_ctrl_ops_t *isc_ops;
56 	smbus_prop_op_t isc_smbus_ops;
57 	uint32_t isc_max_read;
58 	uint32_t isc_max_write;
59 	uint32_t isc_max_block;
60 } i2csim_ctrl_t;
61 
62 typedef enum i2csim_state {
63 	/*
64 	 * Indicates that there is currently no request.
65 	 */
66 	I2CSIM_S_IDLE,
67 	/*
68 	 * Indicates that a request is currently assigned and we are waiting for
69 	 * userland to process it.
70 	 */
71 	I2CSIM_S_REQ,
72 	/*
73 	 * Indicates that we have received a reply from userland.
74 	 */
75 	I2CSIM_S_REPLY
76 } i2csim_state_t;
77 
78 typedef struct i2csim {
79 	dev_info_t *sim_dip;
80 	bool sim_open;
81 	uint64_t sim_seq;
82 	i2c_ctrl_hdl_t *sim_hdls[I2CSIM_NCTRLS];
83 	/*
84 	 * Request-related data.
85 	 */
86 	kmutex_t sim_mutex;
87 	kcondvar_t sim_cv;
88 	i2csim_state_t sim_state;
89 	i2c_req_t *sim_i2c_req;
90 	smbus_req_t *sim_smbus_req;
91 	const i2csim_ctrl_t *sim_ctrl_req;
92 	uint32_t sim_port_req;
93 	struct pollhead sim_ph;
94 } i2csim_t;
95 
96 i2csim_t i2csim;
97 
98 static void
i2csim_io_no_userland(i2csim_t * sim)99 i2csim_io_no_userland(i2csim_t *sim)
100 {
101 	VERIFY(MUTEX_HELD(&sim->sim_mutex));
102 	if (sim->sim_i2c_req != NULL) {
103 		i2c_ctrl_io_error(&sim->sim_i2c_req->ir_error,
104 		    I2C_CORE_E_CONTROLLER, I2C_CTRL_E_DRIVER);
105 	}
106 
107 	if (sim->sim_smbus_req != NULL) {
108 		i2c_ctrl_io_error(&sim->sim_smbus_req->smbr_error,
109 		    I2C_CORE_E_CONTROLLER, I2C_CTRL_E_DRIVER);
110 	}
111 
112 	sim->sim_state = I2CSIM_S_REPLY;
113 	cv_broadcast(&sim->sim_cv);
114 }
115 
116 static void
i2csim_io_request(i2csim_t * sim,const i2csim_ctrl_t * ctrl,uint32_t port,i2c_req_t * i2c_req,smbus_req_t * smbus_req)117 i2csim_io_request(i2csim_t *sim, const i2csim_ctrl_t *ctrl, uint32_t port,
118     i2c_req_t *i2c_req, smbus_req_t *smbus_req)
119 {
120 	VERIFY((i2c_req != NULL && smbus_req == NULL) ||
121 	    (i2c_req == NULL && smbus_req != NULL));
122 
123 	mutex_enter(&sim->sim_mutex);
124 	while (sim->sim_state != I2CSIM_S_IDLE) {
125 		cv_wait(&sim->sim_cv, &sim->sim_mutex);
126 	}
127 
128 	VERIFY3P(sim->sim_i2c_req, ==, NULL);
129 	VERIFY3P(sim->sim_smbus_req, ==, NULL);
130 	VERIFY3P(sim->sim_ctrl_req, ==, NULL);
131 	sim->sim_i2c_req = i2c_req;
132 	sim->sim_smbus_req = smbus_req;
133 	sim->sim_ctrl_req = ctrl;
134 	sim->sim_port_req = port;
135 	sim->sim_seq++;
136 	sim->sim_state = I2CSIM_S_REQ;
137 	pollwakeup(&sim->sim_ph, POLLIN);
138 	cv_broadcast(&sim->sim_cv);
139 
140 	if (!sim->sim_open) {
141 		i2csim_io_no_userland(sim);
142 	}
143 
144 	while (sim->sim_state != I2CSIM_S_REPLY) {
145 		cv_wait(&sim->sim_cv, &sim->sim_mutex);
146 	}
147 
148 	sim->sim_i2c_req = NULL;
149 	sim->sim_smbus_req = NULL;
150 	sim->sim_ctrl_req = NULL;
151 	sim->sim_port_req = 0;
152 	sim->sim_state = I2CSIM_S_IDLE;
153 	cv_broadcast(&sim->sim_cv);
154 
155 	mutex_exit(&sim->sim_mutex);
156 }
157 
158 static void
i2csim_io_i2c(void * arg,uint32_t port,i2c_req_t * req)159 i2csim_io_i2c(void *arg, uint32_t port, i2c_req_t *req)
160 {
161 	const i2csim_ctrl_t *ctrl = arg;
162 
163 	i2csim_io_request(&i2csim, ctrl, port, req, NULL);
164 }
165 
166 static void
i2csim_io_smbus(void * arg,uint32_t port,smbus_req_t * req)167 i2csim_io_smbus(void *arg, uint32_t port, smbus_req_t *req)
168 {
169 	const i2csim_ctrl_t *ctrl = arg;
170 
171 	i2csim_io_request(&i2csim, ctrl, port, NULL, req);
172 }
173 
174 static i2c_errno_t
i2csim_prop_info(void * arg,i2c_prop_t prop,i2c_prop_info_t * info)175 i2csim_prop_info(void *arg, i2c_prop_t prop, i2c_prop_info_t *info)
176 {
177 	const i2csim_ctrl_t *ctrl = arg;
178 
179 	switch (prop) {
180 	case I2C_PROP_BUS_SPEED:
181 		i2c_prop_info_set_pos_bit32(info, I2C_SPEED_STD);
182 		break;
183 	case SMBUS_PROP_SUP_OPS:
184 	case SMBUS_PROP_MAX_BLOCK:
185 		if (ctrl->isc_type != I2C_CTRL_TYPE_SMBUS) {
186 			return (I2C_PROP_E_UNSUP);
187 		}
188 		break;
189 	case I2C_PROP_MAX_READ:
190 	case I2C_PROP_MAX_WRITE:
191 		if (ctrl->isc_type != I2C_CTRL_TYPE_I2C) {
192 			return (I2C_PROP_E_UNSUP);
193 		}
194 		break;
195 	default:
196 		return (I2C_PROP_E_UNSUP);
197 
198 	}
199 
200 	i2c_prop_info_set_perm(info, I2C_PROP_PERM_RO);
201 
202 	return (I2C_CORE_E_OK);
203 }
204 
205 static i2c_errno_t
i2csim_prop_get(void * arg,i2c_prop_t prop,void * buf,size_t buflen)206 i2csim_prop_get(void *arg, i2c_prop_t prop, void *buf, size_t buflen)
207 {
208 	uint32_t val;
209 	const i2csim_ctrl_t *ctrl = arg;
210 
211 	/*
212 	 * First determine if this is a property this type of controller should
213 	 * support or not.
214 	 */
215 	switch (prop) {
216 	case I2C_PROP_MAX_READ:
217 	case I2C_PROP_MAX_WRITE:
218 		if (ctrl->isc_type != I2C_CTRL_TYPE_I2C) {
219 			return (I2C_PROP_E_UNSUP);
220 		}
221 		break;
222 	case SMBUS_PROP_MAX_BLOCK:
223 	case SMBUS_PROP_SUP_OPS:
224 		if (ctrl->isc_type != I2C_CTRL_TYPE_SMBUS) {
225 			return (I2C_PROP_E_UNSUP);
226 		}
227 		break;
228 	default:
229 		break;
230 	}
231 
232 	switch (prop) {
233 	case I2C_PROP_BUS_SPEED:
234 		val = I2C_SPEED_STD;
235 		break;
236 	case SMBUS_PROP_SUP_OPS:
237 		val = ctrl->isc_smbus_ops;
238 		break;
239 	case I2C_PROP_MAX_READ:
240 		val = ctrl->isc_max_read;
241 		break;
242 	case I2C_PROP_MAX_WRITE:
243 		val = ctrl->isc_max_write;
244 		break;
245 	case SMBUS_PROP_MAX_BLOCK:
246 		val = ctrl->isc_max_block;
247 		break;
248 	default:
249 		return (I2C_PROP_E_UNSUP);
250 	}
251 
252 	VERIFY3U(buflen, >=, sizeof (val));
253 	bcopy(&val, buf, sizeof (val));
254 	return (I2C_CORE_E_OK);
255 }
256 
257 static const i2c_ctrl_ops_t i2csim_i2c_ops = {
258 	.i2c_port_name_f = i2c_ctrl_port_name_portno,
259 	.i2c_io_i2c_f = i2csim_io_i2c,
260 	.i2c_prop_info_f = i2csim_prop_info,
261 	.i2c_prop_get_f = i2csim_prop_get
262 };
263 
264 static const i2c_ctrl_ops_t i2csim_smbus_ops = {
265 	.i2c_port_name_f = i2c_ctrl_port_name_portno,
266 	.i2c_io_smbus_f = i2csim_io_smbus,
267 	.i2c_prop_info_f = i2csim_prop_info,
268 	.i2c_prop_get_f = i2csim_prop_get
269 };
270 
271 static const i2csim_ctrl_t i2csim_ctrls[I2CSIM_NCTRLS] = { {
272 	.isc_name = "i2csim0",
273 	.isc_nports = 1,
274 	.isc_type = I2C_CTRL_TYPE_I2C,
275 	.isc_ops = &i2csim_i2c_ops,
276 	.isc_max_read = I2CSIM_I2C_MAX,
277 	.isc_max_write = I2CSIM_I2C_MAX
278 }, {
279 	.isc_name = "smbussim1",
280 	.isc_nports = 2,
281 	.isc_type = I2C_CTRL_TYPE_SMBUS,
282 	.isc_ops = &i2csim_smbus_ops,
283 	.isc_smbus_ops = SMBUS_PROP_OP_QUICK_COMMAND | SMBUS_PROP_OP_SEND_BYTE |
284 	    SMBUS_PROP_OP_RECV_BYTE | SMBUS_PROP_OP_WRITE_BYTE |
285 	    SMBUS_PROP_OP_READ_BYTE | SMBUS_PROP_OP_WRITE_WORD |
286 	    SMBUS_PROP_OP_READ_WORD | SMBUS_PROP_OP_PROCESS_CALL |
287 	    SMBUS_PROP_OP_WRITE_BLOCK | SMBUS_PROP_OP_READ_BLOCK |
288 	    SMBUS_PROP_OP_BLOCK_PROCESS_CALL | SMBUS_PROP_OP_I2C_WRITE_BLOCK |
289 	    SMBUS_PROP_OP_I2C_READ_BLOCK,
290 	.isc_max_block = SMBUS_V2_MAX_BLOCK
291 } };
292 
293 static int
i2csim_open(dev_t * devp,int flags,int otype,cred_t * credp)294 i2csim_open(dev_t *devp, int flags, int otype, cred_t *credp)
295 {
296 	i2csim_t *sim = &i2csim;
297 
298 	if (drv_priv(credp) != 0)
299 		return (EPERM);
300 
301 	if (otype != OTYP_CHR)
302 		return (ENOTSUP);
303 
304 	if ((flags & (FREAD | FWRITE)) == 0)
305 		return (EINVAL);
306 
307 	if (getminor(*devp) != 0)
308 		return (ENXIO);
309 
310 	mutex_enter(&sim->sim_mutex);
311 	if (sim->sim_open) {
312 		mutex_exit(&sim->sim_mutex);
313 		return (EBUSY);
314 	}
315 
316 	sim->sim_open = true;
317 	mutex_exit(&sim->sim_mutex);
318 	return (0);
319 }
320 
321 static uint32_t
i2csim_ctrl_index(i2csim_t * sim)322 i2csim_ctrl_index(i2csim_t *sim)
323 {
324 	VERIFY(MUTEX_HELD(&sim->sim_mutex));
325 
326 	for (uint32_t i = 0; i < I2CSIM_NCTRLS; i++) {
327 		if (sim->sim_ctrl_req == &i2csim_ctrls[i]) {
328 			return (i);
329 		}
330 	}
331 
332 	panic("programming error: invalid i2csim_ctrl_t");
333 }
334 
335 static int
i2csim_ioctl_request(i2csim_t * sim,intptr_t arg,int mode)336 i2csim_ioctl_request(i2csim_t *sim, intptr_t arg, int mode)
337 {
338 	mutex_enter(&sim->sim_mutex);
339 	while (sim->sim_state != I2CSIM_S_REQ) {
340 		if ((mode & (FNONBLOCK | FNDELAY)) != 0) {
341 			mutex_exit(&sim->sim_mutex);
342 			return (EAGAIN);
343 		}
344 
345 		if (cv_wait_sig(&sim->sim_cv, &sim->sim_mutex) == 0) {
346 			mutex_exit(&sim->sim_mutex);
347 			return (EINTR);
348 		}
349 	}
350 
351 
352 	i2csim_req_t req;
353 	bzero(&req, sizeof (req));
354 	req.i2csim_seq = sim->sim_seq;
355 	req.i2csim_ctrl = i2csim_ctrl_index(sim);
356 	req.i2csim_port = sim->sim_port_req;
357 	req.i2csim_type = sim->sim_ctrl_req->isc_type;
358 	if (sim->sim_i2c_req != NULL) {
359 		bcopy(sim->sim_i2c_req, &req.i2csim_i2c, sizeof (i2c_req_t));
360 	}
361 
362 	if (sim->sim_smbus_req != NULL) {
363 		bcopy(sim->sim_smbus_req, &req.i2csim_smbus,
364 		    sizeof (smbus_req_t));
365 	}
366 
367 	int ret = ddi_copyout(&req, (void *)arg, sizeof (i2csim_req_t),
368 	    mode & FKIOCTL);
369 	mutex_exit(&sim->sim_mutex);
370 	if (ret != 0) {
371 		return (EFAULT);
372 	}
373 
374 	return (0);
375 }
376 
377 static int
i2csim_ioctl_reply(i2csim_t * sim,intptr_t arg,int mode)378 i2csim_ioctl_reply(i2csim_t *sim, intptr_t arg, int mode)
379 {
380 	i2csim_req_t req;
381 
382 	mutex_enter(&sim->sim_mutex);
383 	if (sim->sim_state != I2CSIM_S_REQ) {
384 		mutex_exit(&sim->sim_mutex);
385 		return (EAGAIN);
386 	}
387 
388 	if (ddi_copyin((void *)arg, &req, sizeof (i2csim_req_t),
389 	    mode & FKIOCTL) != 0) {
390 		mutex_exit(&sim->sim_mutex);
391 		return (EFAULT);
392 	}
393 
394 	/*
395 	 * We ignore the controller and port ID and rely on the sequence number
396 	 * for checking that this makes sense. We do check the type to ensure
397 	 * that the structure we're going to copy data from makes sense.
398 	 */
399 	if (req.i2csim_seq != sim->sim_seq ||
400 	    req.i2csim_type != sim->sim_ctrl_req->isc_type) {
401 		mutex_exit(&sim->sim_mutex);
402 		return (EINVAL);
403 	}
404 
405 	switch (req.i2csim_type) {
406 	case I2C_CTRL_TYPE_I2C:
407 		sim->sim_i2c_req->ir_error = req.i2csim_i2c.ir_error;
408 		bcopy(req.i2csim_i2c.ir_rdata, sim->sim_i2c_req->ir_rdata,
409 		    sizeof (sim->sim_i2c_req->ir_rdata));
410 		break;
411 	case I2C_CTRL_TYPE_SMBUS:
412 		sim->sim_smbus_req->smbr_error = req.i2csim_smbus.smbr_error;
413 		sim->sim_smbus_req->smbr_rlen = req.i2csim_smbus.smbr_rlen;
414 		bcopy(req.i2csim_smbus.smbr_rdata,
415 		    sim->sim_smbus_req->smbr_rdata,
416 		    sizeof (sim->sim_smbus_req->smbr_rdata));
417 		break;
418 	default:
419 		mutex_exit(&sim->sim_mutex);
420 		return (EINVAL);
421 	}
422 
423 	sim->sim_state = I2CSIM_S_REPLY;
424 	cv_broadcast(&sim->sim_cv);
425 	mutex_exit(&sim->sim_mutex);
426 
427 	return (0);
428 }
429 
430 static int
i2csim_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)431 i2csim_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
432     int *rvalp)
433 {
434 	i2csim_t *sim = &i2csim;
435 
436 	if (getminor(dev) != 0) {
437 		return (ENXIO);
438 	}
439 
440 	switch (cmd) {
441 	case I2CSIM_REQUEST:
442 		return (i2csim_ioctl_request(sim, arg, mode));
443 	case I2CSIM_REPLY:
444 		return (i2csim_ioctl_reply(sim, arg, mode));
445 	default:
446 		return (ENOTTY);
447 	}
448 }
449 
450 static int
i2csim_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)451 i2csim_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
452     struct pollhead **phpp)
453 {
454 	i2csim_t *sim = &i2csim;
455 	short ready = 0;
456 
457 	if (getminor(dev) != 0) {
458 		return (ENXIO);
459 	}
460 
461 	mutex_enter(&sim->sim_mutex);
462 	if (sim->sim_state == I2CSIM_S_REQ) {
463 		ready |= POLLIN;
464 	}
465 
466 	*reventsp = ready & events;
467 	if ((*reventsp == 0 && !anyyet) || (events & POLLET)) {
468 		*phpp = &sim->sim_ph;
469 	}
470 	mutex_exit(&sim->sim_mutex);
471 
472 	return (0);
473 }
474 
475 static int
i2csim_close(dev_t dev,int flags,int otype,cred_t * credp)476 i2csim_close(dev_t dev, int flags, int otype, cred_t *credp)
477 {
478 	i2csim_t *sim = &i2csim;
479 
480 	if (otype != OTYP_CHR) {
481 		return (EINVAL);
482 	}
483 
484 	mutex_enter(&sim->sim_mutex);
485 	sim->sim_open = false;
486 	if (sim->sim_state == I2CSIM_S_REQ) {
487 		i2csim_io_no_userland(sim);
488 	}
489 	pollwakeup(&sim->sim_ph, POLLERR);
490 	pollhead_clean(&sim->sim_ph);
491 	mutex_exit(&sim->sim_mutex);
492 	return (0);
493 }
494 
495 static struct cb_ops i2csim_cb_ops = {
496 	.cb_open = i2csim_open,
497 	.cb_close = i2csim_close,
498 	.cb_strategy = nodev,
499 	.cb_print = nodev,
500 	.cb_dump = nodev,
501 	.cb_read = nodev,
502 	.cb_write = nodev,
503 	.cb_ioctl = i2csim_ioctl,
504 	.cb_devmap = nodev,
505 	.cb_mmap = nodev,
506 	.cb_segmap = nodev,
507 	.cb_chpoll = i2csim_chpoll,
508 	.cb_prop_op = ddi_prop_op,
509 	.cb_flag = D_MP,
510 	.cb_rev = CB_REV,
511 	.cb_aread = nodev,
512 	.cb_awrite = nodev
513 };
514 
515 /*
516  * We allow controller unregistration to fail during detach. However, if we are
517  * trying to do this during attach, we require that it succeed.
518  */
519 static bool
i2csim_unregister(i2csim_t * sim,bool detach)520 i2csim_unregister(i2csim_t *sim, bool detach)
521 {
522 	for (uint32_t i = 0; i < I2CSIM_NCTRLS; i++) {
523 		i2c_ctrl_reg_error_t ret;
524 
525 		ret = i2c_ctrl_unregister(sim->sim_hdls[i]);
526 		if (detach && ret != I2C_CTRL_REG_E_OK) {
527 			dev_err(sim->sim_dip, CE_WARN, "failed to unregister "
528 			    "controller %u", i);
529 			return (false);
530 		}
531 
532 		VERIFY3U(ret, ==, I2C_CTRL_REG_E_OK);
533 	}
534 
535 	return (true);
536 }
537 
538 static void
i2csim_cleanup(i2csim_t * sim)539 i2csim_cleanup(i2csim_t *sim)
540 {
541 	ddi_remove_minor_node(sim->sim_dip, NULL);
542 	cv_destroy(&sim->sim_cv);
543 	mutex_destroy(&sim->sim_mutex);
544 	sim->sim_dip = NULL;
545 }
546 
547 static int
i2csim_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)548 i2csim_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
549 {
550 	if (cmd == DDI_RESUME) {
551 		return (DDI_SUCCESS);
552 	} else if (cmd != DDI_ATTACH) {
553 		return (DDI_FAILURE);
554 	}
555 
556 	if (ddi_get_instance(dip) != 0) {
557 		dev_err(dip, CE_WARN, "only a single instance of i2csim is "
558 		    "supported");
559 		return (DDI_FAILURE);
560 	}
561 
562 	VERIFY3P(i2csim.sim_dip, ==, NULL);
563 	i2csim.sim_dip = dip;
564 	mutex_init(&i2csim.sim_mutex, NULL, MUTEX_DRIVER, NULL);
565 	cv_init(&i2csim.sim_cv, NULL, CV_DRIVER, NULL);
566 	if (ddi_create_minor_node(i2csim.sim_dip, "ctrl", S_IFCHR, 0,
567 	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
568 		dev_err(dip, CE_WARN, "failed to create control minor node");
569 		goto err;
570 	}
571 
572 	for (size_t i = 0; i < I2CSIM_NCTRLS; i++) {
573 		i2c_ctrl_reg_error_t ret;
574 		i2c_ctrl_register_t *reg;
575 
576 		ret = i2c_ctrl_register_alloc(I2C_CTRL_PROVIDER, &reg);
577 		if (ret != 0) {
578 			dev_err(i2csim.sim_dip, CE_WARN, "failed to allocate "
579 			    "i2c controller registration structure: 0x%x", ret);
580 			goto err;
581 		}
582 
583 		reg->ic_type = i2csim_ctrls[i].isc_type;
584 		reg->ic_nports = i2csim_ctrls[i].isc_nports;
585 		reg->ic_name =  i2csim_ctrls[i].isc_name;
586 		reg->ic_dip = i2csim.sim_dip;
587 		reg->ic_drv = (void *)&i2csim_ctrls[i];
588 		reg->ic_ops = i2csim_ctrls[i].isc_ops;
589 
590 		ret = i2c_ctrl_register(reg, &i2csim.sim_hdls[i]);
591 		i2c_ctrl_register_free(reg);
592 		if (ret != 0) {
593 			dev_err(i2csim.sim_dip, CE_WARN, "failed to register "
594 			    "controller %zu with i2c framework: 0x%x", i, ret);
595 			(void) i2csim_unregister(&i2csim, false);
596 			goto err;
597 		}
598 
599 	}
600 
601 	return (DDI_SUCCESS);
602 
603 err:
604 	i2csim_cleanup(&i2csim);
605 	return (DDI_FAILURE);
606 }
607 
608 static int
i2csim_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** outp)609 i2csim_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **outp)
610 {
611 	switch (cmd) {
612 	case DDI_INFO_DEVT2DEVINFO:
613 		VERIFY3P(i2csim.sim_dip, !=, NULL);
614 		*outp = i2csim.sim_dip;
615 		break;
616 	case DDI_INFO_DEVT2INSTANCE:
617 		VERIFY3P(i2csim.sim_dip, !=, NULL);
618 		*outp = i2csim.sim_dip;
619 		*outp = (void *)(uintptr_t)ddi_get_instance(i2csim.sim_dip);
620 		break;
621 	default:
622 		return (DDI_FAILURE);
623 	}
624 
625 	return (DDI_SUCCESS);
626 }
627 
628 static int
i2csim_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)629 i2csim_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
630 {
631 	if (cmd == DDI_SUSPEND) {
632 		return (DDI_FAILURE);
633 	} else if (cmd != DDI_DETACH) {
634 		return (DDI_FAILURE);
635 	}
636 
637 	VERIFY3P(dip, ==, i2csim.sim_dip);
638 
639 	if (!i2csim_unregister(&i2csim, true)) {
640 		return (DDI_FAILURE);
641 	}
642 
643 	i2csim_cleanup(&i2csim);
644 	VERIFY3P(i2csim.sim_dip, ==, NULL);
645 	return (DDI_SUCCESS);
646 }
647 
648 static struct dev_ops i2csim_dev_ops = {
649 	.devo_rev = DEVO_REV,
650 	.devo_refcnt = 0,
651 	.devo_identify = nulldev,
652 	.devo_getinfo = i2csim_getinfo,
653 	.devo_probe = nulldev,
654 	.devo_attach = i2csim_attach,
655 	.devo_detach = i2csim_detach,
656 	.devo_reset = nodev,
657 	.devo_quiesce = ddi_quiesce_not_supported,
658 	.devo_cb_ops = &i2csim_cb_ops
659 };
660 
661 static struct modldrv i2csim_modldrv = {
662 	.drv_modops = &mod_driverops,
663 	.drv_linkinfo = "I2C Simulation Controller",
664 	.drv_dev_ops = &i2csim_dev_ops
665 };
666 
667 static struct modlinkage i2csim_modlinkage = {
668 	.ml_rev = MODREV_1,
669 	.ml_linkage = { &i2csim_modldrv, NULL }
670 };
671 
672 int
_init(void)673 _init(void)
674 {
675 	int ret;
676 
677 	i2c_ctrl_mod_init(&i2csim_dev_ops);
678 	if ((ret = mod_install(&i2csim_modlinkage)) != 0) {
679 		i2c_ctrl_mod_fini(&i2csim_dev_ops);
680 	}
681 
682 	return (ret);
683 }
684 
685 int
_info(struct modinfo * modinfop)686 _info(struct modinfo *modinfop)
687 {
688 	return (mod_info(&i2csim_modlinkage, modinfop));
689 }
690 
691 int
_fini(void)692 _fini(void)
693 {
694 	int ret;
695 
696 	if ((ret = mod_remove(&i2csim_modlinkage)) == 0) {
697 		i2c_ctrl_mod_fini(&i2csim_dev_ops);
698 	}
699 
700 	return (ret);
701 }
702