/* $NetBSD: umcpmio_subr.c,v 1.4 2025/11/29 18:39:15 brad Exp $ */ /* * Copyright (c) 2024 Brad Spencer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __KERNEL_RCSID(0, "$NetBSD: umcpmio_subr.c,v 1.4 2025/11/29 18:39:15 brad Exp $"); #ifdef _KERNEL_OPT #include "opt_usb.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UMCPMIO_DEBUG 1 #ifdef UMCPMIO_DEBUG #define DPRINTF(x) do { if (umcpmiodebug) printf x; } while (0) #define DPRINTFN(n, x) do { if (umcpmiodebug > (n)) printf x; } while (0) extern int umcpmiodebug; #else #define DPRINTF(x) __nothing #define DPRINTFN(n,x) __nothing #endif /* Handy functions that do a bunch of things, very often they * don't fit anywhere else. */ void WAITMS(int ms) { if (ms > 0) delay(ms * 1000); } void umcpmio_dump_buffer(bool enabled, uint8_t *buf, u_int len, const char *name) { int i; if (enabled) { DPRINTF(("%s:", name)); for (i = 0; i < len; i++) { DPRINTF((" %02x", buf[i])); } DPRINTF(("\n")); } } int mcp2210_decode_errors(uint8_t request, int err, uint8_t general_result) { int r = 0; if (err) return err; switch (general_result) { case MCP2210_CMD_COMPLETE_OK: break; case MCP2210_CMD_UNKNOWN: r = EINVAL; break; case MCP2210_CMD_ACCESS_REJECTED: case MCP2210_CMD_ACCESS_DENIED: case MCP2210_CMD_EEPROM_LOCKED: r = EPERM; break; case MCP2210_CMD_SPI_BUS_UNAVAIL: case MCP2210_CMD_USB_TRANSFER_IP: r = EBUSY; break; case MCP2210_CMD_EEPROM_FAIL: default: r = EIO; } return r; } int mcp2221_get_status(struct umcpmio_softc *sc, struct mcp2221_status_res *res) { struct mcp2221_status_req req; int err = 0; KASSERT(mutex_owned(&sc->sc_action_mutex)); memset(&req, 0, MCP2221_REQ_BUFFER_SIZE); req.cmd = MCP2221_CMD_STATUS; err = umcpmio_send_report(sc, (uint8_t *) & req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *) res, sc->sc_cv_wait); return err; } int mcp2210_get_status(struct umcpmio_softc *sc, struct mcp2210_status_res *res) { struct mcp2210_status_req req; int err = 0; KASSERT(mutex_owned(&sc->sc_action_mutex)); memset(&req, 0, MCP2210_REQ_BUFFER_SIZE); req.cmd = MCP2210_CMD_STATUS; err = umcpmio_send_report(sc, (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE, (uint8_t *) res, sc->sc_cv_wait); err = mcp2210_decode_errors(req.cmd, err, res->completion); return err; } int mcp2221_put_status(struct umcpmio_softc *sc, struct mcp2221_status_req *req, struct mcp2221_status_res *res) { int err = 0; KASSERT(mutex_owned(&sc->sc_action_mutex)); req->cmd = MCP2221_CMD_STATUS; err = umcpmio_send_report(sc, (uint8_t *) req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *) res, sc->sc_cv_wait); return err; } int mcp2221_get_sram(struct umcpmio_softc *sc, struct mcp2221_get_sram_res *res) { struct mcp2221_get_sram_req req; int err = 0; KASSERT(mutex_owned(&sc->sc_action_mutex)); memset(&req, 0, MCP2221_REQ_BUFFER_SIZE); req.cmd = MCP2221_CMD_GET_SRAM; err = umcpmio_send_report(sc, (uint8_t *) & req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *) res, sc->sc_cv_wait); return err; } int mcp2221_put_sram(struct umcpmio_softc *sc, struct mcp2221_set_sram_req *req, struct mcp2221_set_sram_res *res) { int err = 0; KASSERT(mutex_owned(&sc->sc_action_mutex)); req->cmd = MCP2221_CMD_SET_SRAM; err = umcpmio_send_report(sc, (uint8_t *) req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *) res, sc->sc_cv_wait); return err; } int mcp2210_get_nvram(struct umcpmio_softc *sc, uint8_t subcode, struct mcp2210_get_nvram_res *res) { struct mcp2210_get_nvram_req req; int err = 0; KASSERT(mutex_owned(&sc->sc_action_mutex)); memset(&req, 0, MCP2210_REQ_BUFFER_SIZE); req.cmd = MCP2210_CMD_GET_NVRAM; if (subcode < MCP2210_NVRAM_SUBCODE_SPI || subcode > MCP2210_NVRAM_SUBCODE_USBMAN) return EINVAL; req.subcode = subcode; err = umcpmio_send_report(sc, (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE, (uint8_t *) res, sc->sc_cv_wait); err = mcp2210_decode_errors(req.cmd, err, res->completion); return err; } int mcp2210_set_nvram(struct umcpmio_softc *sc, struct mcp2210_set_nvram_req *req, struct mcp2210_set_nvram_res *res) { int err = 0; KASSERT(mutex_owned(&sc->sc_action_mutex)); req->cmd = MCP2210_CMD_SET_NVRAM; if (req->subcode < MCP2210_NVRAM_SUBCODE_SPI || req->subcode > MCP2210_NVRAM_SUBCODE_USBMAN) { DPRINTF(("mcp2210_set_nvram: subcode out of range:" " subcode=%d\n", req->subcode)); return EINVAL; } err = umcpmio_send_report(sc, (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE, (uint8_t *) res, sc->sc_cv_wait); err = mcp2210_decode_errors(req->cmd, err, res->completion); return err; } int mcp2221_get_flash(struct umcpmio_softc *sc, uint8_t subcode, struct mcp2221_get_flash_res *res) { struct mcp2221_get_flash_req req; int err = 0; KASSERT(mutex_owned(&sc->sc_action_mutex)); memset(&req, 0, MCP2221_REQ_BUFFER_SIZE); req.cmd = MCP2221_CMD_GET_FLASH; if (subcode < MCP2221_FLASH_SUBCODE_CS || subcode > MCP2221_FLASH_SUBCODE_CHIPSN) return EINVAL; req.subcode = subcode; err = umcpmio_send_report(sc, (uint8_t *) & req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *) res, sc->sc_cv_wait); return err; } int mcp2221_put_flash(struct umcpmio_softc *sc, struct mcp2221_put_flash_req *req, struct mcp2221_put_flash_res *res) { int err = 0; KASSERT(mutex_owned(&sc->sc_action_mutex)); req->cmd = MCP2221_CMD_SET_FLASH; if (req->subcode < MCP2221_FLASH_SUBCODE_CS || req->subcode > MCP2221_FLASH_SUBCODE_CHIPSN) { DPRINTF(("mcp2221_put_flash: subcode out of range:" " subcode=%d\n", req->subcode)); return EINVAL; } err = umcpmio_send_report(sc, (uint8_t *) req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *) res, sc->sc_cv_wait); return err; } int mcp2210_read_eeprom(struct umcpmio_softc *sc, uint8_t addr, uint8_t *buf) { struct mcp2210_read_eeprom_req req; struct mcp2210_read_eeprom_res res; int err = 0; req.cmd = MCP2210_CMD_READ_EEPROM; req.addr = addr; mutex_enter(&sc->sc_action_mutex); err = umcpmio_send_report(sc, (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE, (uint8_t *) & res, sc->sc_cv_wait); mutex_exit(&sc->sc_action_mutex); err = mcp2210_decode_errors(req.cmd, err, res.completion); if (!err) *buf = res.value; return err; } int mcp2210_write_eeprom(struct umcpmio_softc *sc, uint8_t addr, uint8_t buf) { struct mcp2210_write_eeprom_req req; struct mcp2210_write_eeprom_res res; int err = 0; req.cmd = MCP2210_CMD_WRITE_EEPROM; req.addr = addr; req.value = buf; mutex_enter(&sc->sc_action_mutex); err = umcpmio_send_report(sc, (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE, (uint8_t *) & res, sc->sc_cv_wait); mutex_exit(&sc->sc_action_mutex); err = mcp2210_decode_errors(req.cmd, err, res.completion); return err; }