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