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