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, ®);
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