xref: /illumos-gate/usr/src/uts/common/io/i2c/ctrl/i2csim/i2csim.c (revision 0cbe48189888d02563dba9c90132ac391ba233b6)
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, &reg);
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