xref: /illumos-gate/usr/src/lib/libi2c/common/libi2c_io.c (revision 32002227574cf0a435dc03de622191ca53724f0a)
1*32002227SRobert Mustacchi /*
2*32002227SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*32002227SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*32002227SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*32002227SRobert Mustacchi  * 1.0 of the CDDL.
6*32002227SRobert Mustacchi  *
7*32002227SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*32002227SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*32002227SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*32002227SRobert Mustacchi  */
11*32002227SRobert Mustacchi 
12*32002227SRobert Mustacchi /*
13*32002227SRobert Mustacchi  * Copyright 2025 Oxide Computer Company
14*32002227SRobert Mustacchi  */
15*32002227SRobert Mustacchi 
16*32002227SRobert Mustacchi /*
17*32002227SRobert Mustacchi  * I/O related functions.
18*32002227SRobert Mustacchi  */
19*32002227SRobert Mustacchi 
20*32002227SRobert Mustacchi #include <stdlib.h>
21*32002227SRobert Mustacchi #include <string.h>
22*32002227SRobert Mustacchi #include <unistd.h>
23*32002227SRobert Mustacchi #include <endian.h>
24*32002227SRobert Mustacchi 
25*32002227SRobert Mustacchi #include "libi2c_impl.h"
26*32002227SRobert Mustacchi 
27*32002227SRobert Mustacchi void
i2c_io_req_fini(i2c_io_req_t * req)28*32002227SRobert Mustacchi i2c_io_req_fini(i2c_io_req_t *req)
29*32002227SRobert Mustacchi {
30*32002227SRobert Mustacchi 	free(req);
31*32002227SRobert Mustacchi }
32*32002227SRobert Mustacchi 
33*32002227SRobert Mustacchi bool
i2c_io_req_init(i2c_port_t * port,i2c_io_req_t ** reqp)34*32002227SRobert Mustacchi i2c_io_req_init(i2c_port_t *port, i2c_io_req_t **reqp)
35*32002227SRobert Mustacchi {
36*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = port->port_hdl;
37*32002227SRobert Mustacchi 	i2c_io_req_t *req;
38*32002227SRobert Mustacchi 
39*32002227SRobert Mustacchi 	if (reqp == NULL) {
40*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
41*32002227SRobert Mustacchi 		    "invalid i2c_io_req_t output pointer: %p", reqp));
42*32002227SRobert Mustacchi 	}
43*32002227SRobert Mustacchi 
44*32002227SRobert Mustacchi 	req = calloc(1, sizeof (i2c_io_req_t));
45*32002227SRobert Mustacchi 	if (req == NULL) {
46*32002227SRobert Mustacchi 		int e = errno;
47*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
48*32002227SRobert Mustacchi 		    "memory for a new i2c_io_req_t"));
49*32002227SRobert Mustacchi 	}
50*32002227SRobert Mustacchi 	req->io_port = port;
51*32002227SRobert Mustacchi 
52*32002227SRobert Mustacchi 	*reqp = req;
53*32002227SRobert Mustacchi 	return (i2c_success(hdl));
54*32002227SRobert Mustacchi }
55*32002227SRobert Mustacchi 
56*32002227SRobert Mustacchi /*
57*32002227SRobert Mustacchi  * Set the address for a request. Note that we don't care if the address is
58*32002227SRobert Mustacchi  * reserved or not in the library. We ultimately leave that to the kernel.
59*32002227SRobert Mustacchi  */
60*32002227SRobert Mustacchi bool
i2c_io_req_set_addr(i2c_io_req_t * req,const i2c_addr_t * addr)61*32002227SRobert Mustacchi i2c_io_req_set_addr(i2c_io_req_t *req, const i2c_addr_t *addr)
62*32002227SRobert Mustacchi {
63*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->io_port->port_hdl;
64*32002227SRobert Mustacchi 
65*32002227SRobert Mustacchi 	if (addr == NULL) {
66*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
67*32002227SRobert Mustacchi 		    "invalid i2c_addr_t pointer: %p", addr));
68*32002227SRobert Mustacchi 	}
69*32002227SRobert Mustacchi 
70*32002227SRobert Mustacchi 	if (!i2c_addr_validate(hdl, addr)) {
71*32002227SRobert Mustacchi 		return (false);
72*32002227SRobert Mustacchi 	}
73*32002227SRobert Mustacchi 
74*32002227SRobert Mustacchi 	req->io_addr = *addr;
75*32002227SRobert Mustacchi 	req->io_addr_valid = true;
76*32002227SRobert Mustacchi 	return (i2c_success(hdl));
77*32002227SRobert Mustacchi }
78*32002227SRobert Mustacchi 
79*32002227SRobert Mustacchi bool
i2c_io_req_set_transmit_data(i2c_io_req_t * req,const void * buf,size_t len)80*32002227SRobert Mustacchi i2c_io_req_set_transmit_data(i2c_io_req_t *req, const void *buf, size_t len)
81*32002227SRobert Mustacchi {
82*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->io_port->port_hdl;
83*32002227SRobert Mustacchi 
84*32002227SRobert Mustacchi 	if (buf == NULL && len > 0) {
85*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "transmit "
86*32002227SRobert Mustacchi 		    "buffer cannot be a NULL pointer when the length is "
87*32002227SRobert Mustacchi 		    "non-zero (0x%zu)", len));
88*32002227SRobert Mustacchi 	} else if (buf != NULL && len == 0) {
89*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_WRITE_LEN_RANGE, 0,
90*32002227SRobert Mustacchi 		    "transmit data length cannot be zero when given a "
91*32002227SRobert Mustacchi 		    "non-NULL pointer " "(%p)", buf));
92*32002227SRobert Mustacchi 	} else if (len > I2C_REQ_MAX) {
93*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_WRITE_LEN_RANGE, 0, "cannot "
94*32002227SRobert Mustacchi 		    "transmit more than %zu bytes in a request, valid range is "
95*32002227SRobert Mustacchi 		    "[0x00, 0x%x]", len, I2C_REQ_MAX));
96*32002227SRobert Mustacchi 	}
97*32002227SRobert Mustacchi 
98*32002227SRobert Mustacchi 	req->io_tx_len = len;
99*32002227SRobert Mustacchi 	req->io_tx_buf = buf;
100*32002227SRobert Mustacchi 	return (i2c_success(hdl));
101*32002227SRobert Mustacchi }
102*32002227SRobert Mustacchi 
103*32002227SRobert Mustacchi bool
i2c_io_req_set_receive_buf(i2c_io_req_t * req,void * buf,size_t len)104*32002227SRobert Mustacchi i2c_io_req_set_receive_buf(i2c_io_req_t *req, void *buf, size_t len)
105*32002227SRobert Mustacchi {
106*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->io_port->port_hdl;
107*32002227SRobert Mustacchi 
108*32002227SRobert Mustacchi 	if (buf == NULL && len > 0) {
109*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "receive "
110*32002227SRobert Mustacchi 		    "buffer cannot be a NULL pointer when the length is "
111*32002227SRobert Mustacchi 		    "non-zero (0x%zu)", len));
112*32002227SRobert Mustacchi 	} else if (buf != NULL && len == 0) {
113*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_READ_LEN_RANGE, 0, "receive "
114*32002227SRobert Mustacchi 		    "data length cannot be zero when given a non-NULL pointer "
115*32002227SRobert Mustacchi 		    "(%p)", buf));
116*32002227SRobert Mustacchi 	} else if (len > I2C_REQ_MAX) {
117*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_READ_LEN_RANGE, 0, "cannot "
118*32002227SRobert Mustacchi 		    "receive more %zu bytes in a request, valid range is "
119*32002227SRobert Mustacchi 		    "[0x00, 0x%x]", len, I2C_REQ_MAX));
120*32002227SRobert Mustacchi 	}
121*32002227SRobert Mustacchi 
122*32002227SRobert Mustacchi 	req->io_rx_len = len;
123*32002227SRobert Mustacchi 	req->io_rx_buf = buf;
124*32002227SRobert Mustacchi 	return (i2c_success(hdl));
125*32002227SRobert Mustacchi }
126*32002227SRobert Mustacchi 
127*32002227SRobert Mustacchi bool
i2c_io_req_exec(i2c_io_req_t * req)128*32002227SRobert Mustacchi i2c_io_req_exec(i2c_io_req_t *req)
129*32002227SRobert Mustacchi {
130*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->io_port->port_hdl;
131*32002227SRobert Mustacchi 	i2c_req_t i2c;
132*32002227SRobert Mustacchi 
133*32002227SRobert Mustacchi 	if (!req->io_addr_valid) {
134*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_REQ_MISSING_FIELDS, 0,
135*32002227SRobert Mustacchi 		    "cannot execute I/O request due to missing fields: "
136*32002227SRobert Mustacchi 		    "device address"));
137*32002227SRobert Mustacchi 	}
138*32002227SRobert Mustacchi 
139*32002227SRobert Mustacchi 	if (req->io_tx_len == 0 && req->io_rx_len == 0) {
140*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_REQ_IO_INVALID, 0,
141*32002227SRobert Mustacchi 		    "I/O request invalid: no transmit or receive specified"));
142*32002227SRobert Mustacchi 	}
143*32002227SRobert Mustacchi 
144*32002227SRobert Mustacchi 	(void) memset(&i2c, 0, sizeof (i2c_req_t));
145*32002227SRobert Mustacchi 	i2c.ir_addr = req->io_addr;
146*32002227SRobert Mustacchi 	i2c.ir_wlen = req->io_tx_len;
147*32002227SRobert Mustacchi 	i2c.ir_rlen = req->io_rx_len;
148*32002227SRobert Mustacchi 	if (i2c.ir_wlen > 0) {
149*32002227SRobert Mustacchi 		(void) memcpy(i2c.ir_wdata, req->io_tx_buf, req->io_tx_len);
150*32002227SRobert Mustacchi 	}
151*32002227SRobert Mustacchi 
152*32002227SRobert Mustacchi 	if (ioctl(req->io_port->port_fd, UI2C_IOCTL_I2C_REQ, &i2c) != 0) {
153*32002227SRobert Mustacchi 		int e = errno;
154*32002227SRobert Mustacchi 		return (i2c_ioctl_syserror(hdl, e, "I2C I/O request"));
155*32002227SRobert Mustacchi 	}
156*32002227SRobert Mustacchi 
157*32002227SRobert Mustacchi 	if (i2c.ir_error.i2c_error != I2C_CORE_E_OK) {
158*32002227SRobert Mustacchi 		return (i2c_ioctl_error(hdl, &i2c.ir_error, "I2C I/O request"));
159*32002227SRobert Mustacchi 	}
160*32002227SRobert Mustacchi 
161*32002227SRobert Mustacchi 	if (i2c.ir_rlen > 0) {
162*32002227SRobert Mustacchi 		(void) memcpy(req->io_rx_buf, i2c.ir_rdata, req->io_rx_len);
163*32002227SRobert Mustacchi 	}
164*32002227SRobert Mustacchi 
165*32002227SRobert Mustacchi 	return (i2c_success(hdl));
166*32002227SRobert Mustacchi }
167*32002227SRobert Mustacchi 
168*32002227SRobert Mustacchi void
smbus_io_req_fini(smbus_io_req_t * req)169*32002227SRobert Mustacchi smbus_io_req_fini(smbus_io_req_t *req)
170*32002227SRobert Mustacchi {
171*32002227SRobert Mustacchi 	free(req);
172*32002227SRobert Mustacchi }
173*32002227SRobert Mustacchi 
174*32002227SRobert Mustacchi 
175*32002227SRobert Mustacchi /*
176*32002227SRobert Mustacchi  * Reset all I/O fields before we set something.
177*32002227SRobert Mustacchi  */
178*32002227SRobert Mustacchi static void
smbus_io_req_reset(smbus_io_req_t * req)179*32002227SRobert Mustacchi smbus_io_req_reset(smbus_io_req_t *req)
180*32002227SRobert Mustacchi {
181*32002227SRobert Mustacchi 	req->sir_op_valid = false;
182*32002227SRobert Mustacchi 	req->sir_op = UINT32_MAX;
183*32002227SRobert Mustacchi 	req->sir_flags = 0;
184*32002227SRobert Mustacchi 	req->sir_cmd = 0;
185*32002227SRobert Mustacchi 	req->sir_write = 0;
186*32002227SRobert Mustacchi 	req->sir_writep = NULL;
187*32002227SRobert Mustacchi 	req->sir_wlen = 0;
188*32002227SRobert Mustacchi 	req->sir_rlen = 0;
189*32002227SRobert Mustacchi }
190*32002227SRobert Mustacchi 
191*32002227SRobert Mustacchi bool
smbus_io_req_init(i2c_port_t * port,smbus_io_req_t ** reqp)192*32002227SRobert Mustacchi smbus_io_req_init(i2c_port_t *port, smbus_io_req_t **reqp)
193*32002227SRobert Mustacchi {
194*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = port->port_hdl;
195*32002227SRobert Mustacchi 	smbus_io_req_t *req;
196*32002227SRobert Mustacchi 
197*32002227SRobert Mustacchi 	if (reqp == NULL) {
198*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
199*32002227SRobert Mustacchi 		    "invalid smbus_io_req_t output pointer: %p", reqp));
200*32002227SRobert Mustacchi 	}
201*32002227SRobert Mustacchi 
202*32002227SRobert Mustacchi 	req = calloc(1, sizeof (smbus_io_req_t));
203*32002227SRobert Mustacchi 	if (req == NULL) {
204*32002227SRobert Mustacchi 		int e = errno;
205*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_NO_MEM, e, "failed to allocate "
206*32002227SRobert Mustacchi 		    "memory for a new smbus_io_req_t"));
207*32002227SRobert Mustacchi 	}
208*32002227SRobert Mustacchi 	req->sir_port = port;
209*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
210*32002227SRobert Mustacchi 
211*32002227SRobert Mustacchi 	*reqp = req;
212*32002227SRobert Mustacchi 	return (i2c_success(hdl));
213*32002227SRobert Mustacchi }
214*32002227SRobert Mustacchi 
215*32002227SRobert Mustacchi bool
smbus_io_req_set_addr(smbus_io_req_t * req,const i2c_addr_t * addr)216*32002227SRobert Mustacchi smbus_io_req_set_addr(smbus_io_req_t *req, const i2c_addr_t *addr)
217*32002227SRobert Mustacchi {
218*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->sir_port->port_hdl;
219*32002227SRobert Mustacchi 
220*32002227SRobert Mustacchi 	if (addr == NULL) {
221*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
222*32002227SRobert Mustacchi 		    "invalid i2c_addr_t pointer: %p", addr));
223*32002227SRobert Mustacchi 	}
224*32002227SRobert Mustacchi 
225*32002227SRobert Mustacchi 	if (!i2c_addr_validate(hdl, addr)) {
226*32002227SRobert Mustacchi 		return (false);
227*32002227SRobert Mustacchi 	}
228*32002227SRobert Mustacchi 
229*32002227SRobert Mustacchi 	req->sir_addr = *addr;
230*32002227SRobert Mustacchi 	req->sir_addr_valid = true;
231*32002227SRobert Mustacchi 	return (i2c_success(hdl));
232*32002227SRobert Mustacchi }
233*32002227SRobert Mustacchi 
234*32002227SRobert Mustacchi bool
smbus_io_req_set_quick_cmd(smbus_io_req_t * req,bool write)235*32002227SRobert Mustacchi smbus_io_req_set_quick_cmd(smbus_io_req_t *req, bool write)
236*32002227SRobert Mustacchi {
237*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
238*32002227SRobert Mustacchi 	req->sir_op_valid = true;
239*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_QUICK_COMMAND;
240*32002227SRobert Mustacchi 	req->sir_flags = write ? I2C_IO_REQ_F_QUICK_WRITE : 0;
241*32002227SRobert Mustacchi 
242*32002227SRobert Mustacchi 	return (i2c_success(req->sir_port->port_hdl));
243*32002227SRobert Mustacchi }
244*32002227SRobert Mustacchi 
245*32002227SRobert Mustacchi bool
smbus_io_req_set_send_byte(smbus_io_req_t * req,uint8_t u8)246*32002227SRobert Mustacchi smbus_io_req_set_send_byte(smbus_io_req_t *req, uint8_t u8)
247*32002227SRobert Mustacchi {
248*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
249*32002227SRobert Mustacchi 	req->sir_op_valid = true;
250*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_SEND_BYTE;
251*32002227SRobert Mustacchi 	req->sir_write = u8;
252*32002227SRobert Mustacchi 
253*32002227SRobert Mustacchi 	return (i2c_success(req->sir_port->port_hdl));
254*32002227SRobert Mustacchi }
255*32002227SRobert Mustacchi 
256*32002227SRobert Mustacchi bool
smbus_io_req_set_write_u8(smbus_io_req_t * req,uint8_t cmd,uint8_t u8)257*32002227SRobert Mustacchi smbus_io_req_set_write_u8(smbus_io_req_t *req, uint8_t cmd, uint8_t u8)
258*32002227SRobert Mustacchi {
259*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
260*32002227SRobert Mustacchi 	req->sir_op_valid = true;
261*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_WRITE_BYTE;
262*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
263*32002227SRobert Mustacchi 	req->sir_write = u8;
264*32002227SRobert Mustacchi 
265*32002227SRobert Mustacchi 	return (i2c_success(req->sir_port->port_hdl));
266*32002227SRobert Mustacchi }
267*32002227SRobert Mustacchi 
268*32002227SRobert Mustacchi bool
smbus_io_req_set_write_u16(smbus_io_req_t * req,uint8_t cmd,uint16_t u16)269*32002227SRobert Mustacchi smbus_io_req_set_write_u16(smbus_io_req_t *req, uint8_t cmd, uint16_t u16)
270*32002227SRobert Mustacchi {
271*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
272*32002227SRobert Mustacchi 	req->sir_op_valid = true;
273*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_WRITE_WORD;
274*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
275*32002227SRobert Mustacchi 	req->sir_write = u16;
276*32002227SRobert Mustacchi 
277*32002227SRobert Mustacchi 	return (i2c_success(req->sir_port->port_hdl));
278*32002227SRobert Mustacchi }
279*32002227SRobert Mustacchi 
280*32002227SRobert Mustacchi bool
smbus_io_req_set_write_u32(smbus_io_req_t * req,uint8_t cmd,uint32_t u32)281*32002227SRobert Mustacchi smbus_io_req_set_write_u32(smbus_io_req_t *req, uint8_t cmd, uint32_t u32)
282*32002227SRobert Mustacchi {
283*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
284*32002227SRobert Mustacchi 	req->sir_op_valid = true;
285*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_WRITE_U32;
286*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
287*32002227SRobert Mustacchi 	req->sir_write = u32;
288*32002227SRobert Mustacchi 
289*32002227SRobert Mustacchi 	return (i2c_success(req->sir_port->port_hdl));
290*32002227SRobert Mustacchi }
291*32002227SRobert Mustacchi 
292*32002227SRobert Mustacchi bool
smbus_io_req_set_write_u64(smbus_io_req_t * req,uint8_t cmd,uint64_t u64)293*32002227SRobert Mustacchi smbus_io_req_set_write_u64(smbus_io_req_t *req, uint8_t cmd, uint64_t u64)
294*32002227SRobert Mustacchi {
295*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
296*32002227SRobert Mustacchi 	req->sir_op_valid = true;
297*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_WRITE_U64;
298*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
299*32002227SRobert Mustacchi 	req->sir_write = u64;
300*32002227SRobert Mustacchi 
301*32002227SRobert Mustacchi 	return (i2c_success(req->sir_port->port_hdl));
302*32002227SRobert Mustacchi }
303*32002227SRobert Mustacchi 
304*32002227SRobert Mustacchi bool
smbus_io_req_set_write_block(smbus_io_req_t * req,uint8_t cmd,const void * wdata,size_t wlen,bool i2c)305*32002227SRobert Mustacchi smbus_io_req_set_write_block(smbus_io_req_t *req, uint8_t cmd,
306*32002227SRobert Mustacchi     const void *wdata, size_t wlen, bool i2c)
307*32002227SRobert Mustacchi {
308*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->sir_port->port_hdl;
309*32002227SRobert Mustacchi 
310*32002227SRobert Mustacchi 	if (wdata == NULL) {
311*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
312*32002227SRobert Mustacchi 		    "invalid input data pointer: %p", wdata));
313*32002227SRobert Mustacchi 	}
314*32002227SRobert Mustacchi 
315*32002227SRobert Mustacchi 	if (wlen == 0) {
316*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_WRITE_LEN_RANGE, 0, "write "
317*32002227SRobert Mustacchi 		    "block requests must tranmit a non-zero amount of data"));
318*32002227SRobert Mustacchi 	} else if (wlen > I2C_REQ_MAX) {
319*32002227SRobert Mustacchi 		/*
320*32002227SRobert Mustacchi 		 * We only check against the maximum size range and leave it to
321*32002227SRobert Mustacchi 		 * the kernel to do the actual SMBus check as some block I2C
322*32002227SRobert Mustacchi 		 * writes can exceed SMBus 2.0 limits (especially after
323*32002227SRobert Mustacchi 		 * translation).
324*32002227SRobert Mustacchi 		 */
325*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_WRITE_LEN_RANGE, 0, "cannot "
326*32002227SRobert Mustacchi 		    "transmit %zu bytes in a request, valid range is [0x00, "
327*32002227SRobert Mustacchi 		    "0x%x]", wlen, I2C_REQ_MAX));
328*32002227SRobert Mustacchi 	}
329*32002227SRobert Mustacchi 
330*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
331*32002227SRobert Mustacchi 	req->sir_op_valid = true;
332*32002227SRobert Mustacchi 	req->sir_op = i2c ? SMBUS_OP_I2C_WRITE_BLOCK : SMBUS_OP_WRITE_BLOCK;
333*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
334*32002227SRobert Mustacchi 	req->sir_writep = wdata;
335*32002227SRobert Mustacchi 	req->sir_wlen = wlen;
336*32002227SRobert Mustacchi 
337*32002227SRobert Mustacchi 	return (i2c_success(hdl));
338*32002227SRobert Mustacchi }
339*32002227SRobert Mustacchi 
340*32002227SRobert Mustacchi bool
smbus_io_req_set_recv_byte(smbus_io_req_t * req,uint8_t * u8p)341*32002227SRobert Mustacchi smbus_io_req_set_recv_byte(smbus_io_req_t *req, uint8_t *u8p)
342*32002227SRobert Mustacchi {
343*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->sir_port->port_hdl;
344*32002227SRobert Mustacchi 
345*32002227SRobert Mustacchi 	if (u8p == NULL) {
346*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
347*32002227SRobert Mustacchi 		    "invalid uint8_t pointer: %p", u8p));
348*32002227SRobert Mustacchi 	}
349*32002227SRobert Mustacchi 
350*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
351*32002227SRobert Mustacchi 	req->sir_op_valid = true;
352*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_RECV_BYTE;
353*32002227SRobert Mustacchi 	req->sir_readp = u8p;
354*32002227SRobert Mustacchi 
355*32002227SRobert Mustacchi 	return (i2c_success(hdl));
356*32002227SRobert Mustacchi }
357*32002227SRobert Mustacchi 
358*32002227SRobert Mustacchi bool
smbus_io_req_set_read_u8(smbus_io_req_t * req,uint8_t cmd,uint8_t * u8p)359*32002227SRobert Mustacchi smbus_io_req_set_read_u8(smbus_io_req_t *req, uint8_t cmd, uint8_t *u8p)
360*32002227SRobert Mustacchi {
361*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->sir_port->port_hdl;
362*32002227SRobert Mustacchi 
363*32002227SRobert Mustacchi 	if (u8p == NULL) {
364*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
365*32002227SRobert Mustacchi 		    "invalid uint8_t pointer: %p", u8p));
366*32002227SRobert Mustacchi 	}
367*32002227SRobert Mustacchi 
368*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
369*32002227SRobert Mustacchi 	req->sir_op_valid = true;
370*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_READ_BYTE;
371*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
372*32002227SRobert Mustacchi 	req->sir_readp = u8p;
373*32002227SRobert Mustacchi 
374*32002227SRobert Mustacchi 	return (i2c_success(hdl));
375*32002227SRobert Mustacchi }
376*32002227SRobert Mustacchi 
377*32002227SRobert Mustacchi bool
smbus_io_req_set_read_u16(smbus_io_req_t * req,uint8_t cmd,uint16_t * u16p)378*32002227SRobert Mustacchi smbus_io_req_set_read_u16(smbus_io_req_t *req, uint8_t cmd, uint16_t *u16p)
379*32002227SRobert Mustacchi {
380*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->sir_port->port_hdl;
381*32002227SRobert Mustacchi 
382*32002227SRobert Mustacchi 	if (u16p == NULL) {
383*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
384*32002227SRobert Mustacchi 		    "invalid uint16_t pointer: %p", u16p));
385*32002227SRobert Mustacchi 	}
386*32002227SRobert Mustacchi 
387*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
388*32002227SRobert Mustacchi 	req->sir_op_valid = true;
389*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_READ_WORD;
390*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
391*32002227SRobert Mustacchi 	req->sir_readp = u16p;
392*32002227SRobert Mustacchi 
393*32002227SRobert Mustacchi 	return (i2c_success(hdl));
394*32002227SRobert Mustacchi }
395*32002227SRobert Mustacchi 
396*32002227SRobert Mustacchi bool
smbus_io_req_set_read_u32(smbus_io_req_t * req,uint8_t cmd,uint32_t * u32p)397*32002227SRobert Mustacchi smbus_io_req_set_read_u32(smbus_io_req_t *req, uint8_t cmd, uint32_t *u32p)
398*32002227SRobert Mustacchi {
399*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->sir_port->port_hdl;
400*32002227SRobert Mustacchi 
401*32002227SRobert Mustacchi 	if (u32p == NULL) {
402*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
403*32002227SRobert Mustacchi 		    "invalid uint32_t pointer: %p", u32p));
404*32002227SRobert Mustacchi 	}
405*32002227SRobert Mustacchi 
406*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
407*32002227SRobert Mustacchi 	req->sir_op_valid = true;
408*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_READ_U32;
409*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
410*32002227SRobert Mustacchi 	req->sir_readp = u32p;
411*32002227SRobert Mustacchi 
412*32002227SRobert Mustacchi 	return (i2c_success(hdl));
413*32002227SRobert Mustacchi }
414*32002227SRobert Mustacchi 
415*32002227SRobert Mustacchi bool
smbus_io_req_set_read_u64(smbus_io_req_t * req,uint8_t cmd,uint64_t * u64p)416*32002227SRobert Mustacchi smbus_io_req_set_read_u64(smbus_io_req_t *req, uint8_t cmd, uint64_t *u64p)
417*32002227SRobert Mustacchi {
418*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->sir_port->port_hdl;
419*32002227SRobert Mustacchi 
420*32002227SRobert Mustacchi 	if (u64p == NULL) {
421*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
422*32002227SRobert Mustacchi 		    "invalid uint64_t pointer: %p", u64p));
423*32002227SRobert Mustacchi 	}
424*32002227SRobert Mustacchi 
425*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
426*32002227SRobert Mustacchi 	req->sir_op_valid = true;
427*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_READ_U64;
428*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
429*32002227SRobert Mustacchi 	req->sir_readp = u64p;
430*32002227SRobert Mustacchi 
431*32002227SRobert Mustacchi 	return (i2c_success(hdl));
432*32002227SRobert Mustacchi }
433*32002227SRobert Mustacchi 
434*32002227SRobert Mustacchi bool
smbus_io_req_set_read_block_i2c(smbus_io_req_t * req,uint8_t cmd,void * rdata,size_t rlen)435*32002227SRobert Mustacchi smbus_io_req_set_read_block_i2c(smbus_io_req_t *req, uint8_t cmd, void *rdata,
436*32002227SRobert Mustacchi     size_t rlen)
437*32002227SRobert Mustacchi {
438*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->sir_port->port_hdl;
439*32002227SRobert Mustacchi 
440*32002227SRobert Mustacchi 	if (rdata == NULL) {
441*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
442*32002227SRobert Mustacchi 		    "invalid output data pointer: %p", rdata));
443*32002227SRobert Mustacchi 	}
444*32002227SRobert Mustacchi 
445*32002227SRobert Mustacchi 	if (rlen == 0) {
446*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_READ_LEN_RANGE, 0, "read "
447*32002227SRobert Mustacchi 		    "block requests must tranmit a non-zero amount of data"));
448*32002227SRobert Mustacchi 	} else if (rlen > I2C_REQ_MAX) {
449*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_WRITE_LEN_RANGE, 0, "cannot "
450*32002227SRobert Mustacchi 		    "receive %zu bytes in a request, valid range is [0x00, "
451*32002227SRobert Mustacchi 		    "0x%x]", rlen, I2C_REQ_MAX));
452*32002227SRobert Mustacchi 	}
453*32002227SRobert Mustacchi 
454*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
455*32002227SRobert Mustacchi 	req->sir_op_valid = true;
456*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_I2C_READ_BLOCK;
457*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
458*32002227SRobert Mustacchi 	req->sir_readp = rdata;
459*32002227SRobert Mustacchi 	req->sir_rlen = rlen;
460*32002227SRobert Mustacchi 
461*32002227SRobert Mustacchi 	return (i2c_success(req->sir_port->port_hdl));
462*32002227SRobert Mustacchi }
463*32002227SRobert Mustacchi 
464*32002227SRobert Mustacchi bool
smbus_io_req_set_process_call(smbus_io_req_t * req,uint8_t cmd,uint16_t wdata,uint16_t * rdatap)465*32002227SRobert Mustacchi smbus_io_req_set_process_call(smbus_io_req_t *req, uint8_t cmd, uint16_t wdata,
466*32002227SRobert Mustacchi     uint16_t *rdatap)
467*32002227SRobert Mustacchi {
468*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->sir_port->port_hdl;
469*32002227SRobert Mustacchi 
470*32002227SRobert Mustacchi 	if (rdatap == NULL) {
471*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_BAD_PTR, 0, "encountered "
472*32002227SRobert Mustacchi 		    "invalid output data pointer: %p", rdatap));
473*32002227SRobert Mustacchi 	}
474*32002227SRobert Mustacchi 
475*32002227SRobert Mustacchi 	smbus_io_req_reset(req);
476*32002227SRobert Mustacchi 	req->sir_op_valid = true;
477*32002227SRobert Mustacchi 	req->sir_op = SMBUS_OP_PROCESS_CALL;
478*32002227SRobert Mustacchi 	req->sir_cmd = cmd;
479*32002227SRobert Mustacchi 	req->sir_write = wdata;
480*32002227SRobert Mustacchi 	req->sir_readp = rdatap;
481*32002227SRobert Mustacchi 
482*32002227SRobert Mustacchi 	return (i2c_success(req->sir_port->port_hdl));
483*32002227SRobert Mustacchi }
484*32002227SRobert Mustacchi 
485*32002227SRobert Mustacchi bool
smbus_io_req_exec(smbus_io_req_t * req)486*32002227SRobert Mustacchi smbus_io_req_exec(smbus_io_req_t *req)
487*32002227SRobert Mustacchi {
488*32002227SRobert Mustacchi 	i2c_hdl_t *hdl = req->sir_port->port_hdl;
489*32002227SRobert Mustacchi 	smbus_req_t smbus;
490*32002227SRobert Mustacchi 
491*32002227SRobert Mustacchi 	if (!req->sir_addr_valid || !req->sir_op_valid) {
492*32002227SRobert Mustacchi 		const char *miss;
493*32002227SRobert Mustacchi 
494*32002227SRobert Mustacchi 		if (!req->sir_addr_valid && !req->sir_op_valid) {
495*32002227SRobert Mustacchi 			miss = "device address, SMBus operation code";
496*32002227SRobert Mustacchi 		} else if (!req->sir_op_valid) {
497*32002227SRobert Mustacchi 			miss = "SMBus operation code";
498*32002227SRobert Mustacchi 		} else {
499*32002227SRobert Mustacchi 			miss = "device address";
500*32002227SRobert Mustacchi 		}
501*32002227SRobert Mustacchi 		return (i2c_error(hdl, I2C_ERR_IO_REQ_MISSING_FIELDS, 0,
502*32002227SRobert Mustacchi 		    "cannot execute I/O request due to missing fields: %s",
503*32002227SRobert Mustacchi 		    miss));
504*32002227SRobert Mustacchi 	}
505*32002227SRobert Mustacchi 
506*32002227SRobert Mustacchi 	(void) memset(&smbus, 0, sizeof (smbus_req_t));
507*32002227SRobert Mustacchi 	smbus.smbr_op = req->sir_op;
508*32002227SRobert Mustacchi 	smbus.smbr_flags = req->sir_flags;
509*32002227SRobert Mustacchi 	smbus.smbr_addr = req->sir_addr;
510*32002227SRobert Mustacchi 	smbus.smbr_cmd = req->sir_cmd;
511*32002227SRobert Mustacchi 
512*32002227SRobert Mustacchi 	/*
513*32002227SRobert Mustacchi 	 * Copy relevant data into the request for anything that needs to write.
514*32002227SRobert Mustacchi 	 * The actual write length or read length is only relevant for block
515*32002227SRobert Mustacchi 	 * requests. The rest get their length dictated by the actual opcode.
516*32002227SRobert Mustacchi 	 * SMBus transmits all data in little endian.
517*32002227SRobert Mustacchi 	 */
518*32002227SRobert Mustacchi 	switch (req->sir_op) {
519*32002227SRobert Mustacchi 	case SMBUS_OP_SEND_BYTE:
520*32002227SRobert Mustacchi 	case SMBUS_OP_WRITE_BYTE:
521*32002227SRobert Mustacchi 		smbus.smbr_wdata[0] = (uint8_t)req->sir_write;
522*32002227SRobert Mustacchi 		break;
523*32002227SRobert Mustacchi 	case SMBUS_OP_WRITE_WORD:
524*32002227SRobert Mustacchi 	case SMBUS_OP_PROCESS_CALL: {
525*32002227SRobert Mustacchi 		uint16_t u16 = htole16((uint16_t)req->sir_write);
526*32002227SRobert Mustacchi 		(void) memcpy(smbus.smbr_wdata, &u16, sizeof (u16));
527*32002227SRobert Mustacchi 		break;
528*32002227SRobert Mustacchi 	}
529*32002227SRobert Mustacchi 	case SMBUS_OP_WRITE_U32: {
530*32002227SRobert Mustacchi 		uint32_t u32 = htole32((uint32_t)req->sir_write);
531*32002227SRobert Mustacchi 		(void) memcpy(smbus.smbr_wdata, &u32, sizeof (u32));
532*32002227SRobert Mustacchi 		break;
533*32002227SRobert Mustacchi 	}
534*32002227SRobert Mustacchi 	case SMBUS_OP_WRITE_U64: {
535*32002227SRobert Mustacchi 		uint64_t u64 = htole64(req->sir_write);
536*32002227SRobert Mustacchi 		(void) memcpy(smbus.smbr_wdata, &u64, sizeof (u64));
537*32002227SRobert Mustacchi 		break;
538*32002227SRobert Mustacchi 	}
539*32002227SRobert Mustacchi 	case SMBUS_OP_I2C_WRITE_BLOCK:
540*32002227SRobert Mustacchi 	case SMBUS_OP_WRITE_BLOCK:
541*32002227SRobert Mustacchi 		smbus.smbr_wlen = req->sir_wlen;
542*32002227SRobert Mustacchi 		(void) memcpy(smbus.smbr_wdata, req->sir_writep, req->sir_wlen);
543*32002227SRobert Mustacchi 		break;
544*32002227SRobert Mustacchi 	case SMBUS_OP_I2C_READ_BLOCK:
545*32002227SRobert Mustacchi 		smbus.smbr_rlen = req->sir_rlen;
546*32002227SRobert Mustacchi 		break;
547*32002227SRobert Mustacchi 	default:
548*32002227SRobert Mustacchi 		break;
549*32002227SRobert Mustacchi 	}
550*32002227SRobert Mustacchi 
551*32002227SRobert Mustacchi 	if (ioctl(req->sir_port->port_fd, UI2C_IOCTL_SMBUS_REQ, &smbus) != 0) {
552*32002227SRobert Mustacchi 		int e = errno;
553*32002227SRobert Mustacchi 		return (i2c_ioctl_syserror(hdl, e, "SMBus I/O request"));
554*32002227SRobert Mustacchi 	}
555*32002227SRobert Mustacchi 
556*32002227SRobert Mustacchi 	if (smbus.smbr_error.i2c_error != I2C_CORE_E_OK) {
557*32002227SRobert Mustacchi 		return (i2c_ioctl_error(hdl, &smbus.smbr_error,
558*32002227SRobert Mustacchi 		    "SMBus I/O request"));
559*32002227SRobert Mustacchi 	}
560*32002227SRobert Mustacchi 
561*32002227SRobert Mustacchi 	switch (req->sir_op) {
562*32002227SRobert Mustacchi 	case SMBUS_OP_RECV_BYTE:
563*32002227SRobert Mustacchi 	case SMBUS_OP_READ_BYTE:
564*32002227SRobert Mustacchi 		*(uint8_t *)req->sir_readp = smbus.smbr_rdata[0];
565*32002227SRobert Mustacchi 		break;
566*32002227SRobert Mustacchi 	case SMBUS_OP_READ_WORD:
567*32002227SRobert Mustacchi 	case SMBUS_OP_PROCESS_CALL: {
568*32002227SRobert Mustacchi 		uint16_t u16;
569*32002227SRobert Mustacchi 		(void) memcpy(&u16, smbus.smbr_rdata, sizeof (uint16_t));
570*32002227SRobert Mustacchi 		*(uint16_t *)req->sir_readp = letoh16(u16);
571*32002227SRobert Mustacchi 		break;
572*32002227SRobert Mustacchi 	}
573*32002227SRobert Mustacchi 	case SMBUS_OP_READ_U32: {
574*32002227SRobert Mustacchi 		uint32_t u32;
575*32002227SRobert Mustacchi 		(void) memcpy(&u32, smbus.smbr_rdata, sizeof (uint32_t));
576*32002227SRobert Mustacchi 		*(uint32_t *)req->sir_readp = letoh32(u32);
577*32002227SRobert Mustacchi 		break;
578*32002227SRobert Mustacchi 	}
579*32002227SRobert Mustacchi 	case SMBUS_OP_READ_U64: {
580*32002227SRobert Mustacchi 		uint64_t u64;
581*32002227SRobert Mustacchi 		(void) memcpy(&u64, smbus.smbr_rdata, sizeof (uint64_t));
582*32002227SRobert Mustacchi 		*(uint64_t *)req->sir_readp = letoh64(u64);
583*32002227SRobert Mustacchi 		break;
584*32002227SRobert Mustacchi 	}
585*32002227SRobert Mustacchi 	/*
586*32002227SRobert Mustacchi 	 * Right now, only I2C Read block is supported earlier since we haven't
587*32002227SRobert Mustacchi 	 * plumbed through all the variable length read functions for lack of
588*32002227SRobert Mustacchi 	 * testing.
589*32002227SRobert Mustacchi 	 */
590*32002227SRobert Mustacchi 	case SMBUS_OP_READ_BLOCK:
591*32002227SRobert Mustacchi 	case SMBUS_OP_I2C_READ_BLOCK:
592*32002227SRobert Mustacchi 	case SMBUS_OP_BLOCK_PROCESS_CALL:
593*32002227SRobert Mustacchi 		(void) memcpy(req->sir_readp, smbus.smbr_rdata,
594*32002227SRobert Mustacchi 		    smbus.smbr_rlen);
595*32002227SRobert Mustacchi 	default:
596*32002227SRobert Mustacchi 		break;
597*32002227SRobert Mustacchi 	}
598*32002227SRobert Mustacchi 
599*32002227SRobert Mustacchi 	return (i2c_success(hdl));
600*32002227SRobert Mustacchi }
601