1 /* $NetBSD: umcpmio_subr.c,v 1.4 2025/11/29 18:39:15 brad Exp $ */ 2 3 /* 4 * Copyright (c) 2024 Brad Spencer <brad (at) anduin.eldar.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/cdefs.h> 20 __KERNEL_RCSID(0, "$NetBSD: umcpmio_subr.c,v 1.4 2025/11/29 18:39:15 brad Exp $"); 21 22 #ifdef _KERNEL_OPT 23 #include "opt_usb.h" 24 #endif 25 26 #include <sys/param.h> 27 #include <sys/types.h> 28 29 #include <sys/conf.h> 30 #include <sys/device.h> 31 #include <sys/file.h> 32 #include <sys/kauth.h> 33 #include <sys/kernel.h> 34 #include <sys/kmem.h> 35 #include <sys/lwp.h> 36 #include <sys/sysctl.h> 37 #include <sys/systm.h> 38 #include <sys/tty.h> 39 #include <sys/vnode.h> 40 41 #include <dev/usb/umcpmio.h> 42 #include <dev/usb/umcpmio_transport.h> 43 #include <dev/usb/umcpmio_subr.h> 44 #include <dev/usb/umcpmio_hid_reports.h> 45 46 #define UMCPMIO_DEBUG 1 47 #ifdef UMCPMIO_DEBUG 48 #define DPRINTF(x) do { if (umcpmiodebug) printf x; } while (0) 49 #define DPRINTFN(n, x) do { if (umcpmiodebug > (n)) printf x; } while (0) 50 extern int umcpmiodebug; 51 #else 52 #define DPRINTF(x) __nothing 53 #define DPRINTFN(n,x) __nothing 54 #endif 55 56 /* Handy functions that do a bunch of things, very often they 57 * don't fit anywhere else. 58 */ 59 60 void 61 WAITMS(int ms) 62 { 63 if (ms > 0) 64 delay(ms * 1000); 65 } 66 67 void 68 umcpmio_dump_buffer(bool enabled, uint8_t *buf, u_int len, const char *name) 69 { 70 int i; 71 72 if (enabled) { 73 DPRINTF(("%s:", name)); 74 for (i = 0; i < len; i++) { 75 DPRINTF((" %02x", buf[i])); 76 } 77 DPRINTF(("\n")); 78 } 79 } 80 81 82 int 83 mcp2210_decode_errors(uint8_t request, int err, uint8_t general_result) 84 { 85 int r = 0; 86 87 if (err) 88 return err; 89 90 switch (general_result) { 91 case MCP2210_CMD_COMPLETE_OK: 92 break; 93 case MCP2210_CMD_UNKNOWN: 94 r = EINVAL; 95 break; 96 case MCP2210_CMD_ACCESS_REJECTED: 97 case MCP2210_CMD_ACCESS_DENIED: 98 case MCP2210_CMD_EEPROM_LOCKED: 99 r = EPERM; 100 break; 101 case MCP2210_CMD_SPI_BUS_UNAVAIL: 102 case MCP2210_CMD_USB_TRANSFER_IP: 103 r = EBUSY; 104 break; 105 case MCP2210_CMD_EEPROM_FAIL: 106 default: 107 r = EIO; 108 } 109 110 return r; 111 } 112 113 int 114 mcp2221_get_status(struct umcpmio_softc *sc, 115 struct mcp2221_status_res *res) 116 { 117 struct mcp2221_status_req req; 118 int err = 0; 119 120 KASSERT(mutex_owned(&sc->sc_action_mutex)); 121 122 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE); 123 req.cmd = MCP2221_CMD_STATUS; 124 125 err = umcpmio_send_report(sc, 126 (uint8_t *) & req, MCP2221_REQ_BUFFER_SIZE, 127 (uint8_t *) res, sc->sc_cv_wait); 128 129 return err; 130 } 131 132 int 133 mcp2210_get_status(struct umcpmio_softc *sc, 134 struct mcp2210_status_res *res) 135 { 136 struct mcp2210_status_req req; 137 int err = 0; 138 139 KASSERT(mutex_owned(&sc->sc_action_mutex)); 140 141 memset(&req, 0, MCP2210_REQ_BUFFER_SIZE); 142 req.cmd = MCP2210_CMD_STATUS; 143 144 err = umcpmio_send_report(sc, 145 (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE, 146 (uint8_t *) res, sc->sc_cv_wait); 147 148 err = mcp2210_decode_errors(req.cmd, err, res->completion); 149 150 return err; 151 } 152 153 int 154 mcp2221_put_status(struct umcpmio_softc *sc, 155 struct mcp2221_status_req *req, struct mcp2221_status_res *res) 156 { 157 int err = 0; 158 159 KASSERT(mutex_owned(&sc->sc_action_mutex)); 160 161 req->cmd = MCP2221_CMD_STATUS; 162 163 err = umcpmio_send_report(sc, 164 (uint8_t *) req, MCP2221_REQ_BUFFER_SIZE, 165 (uint8_t *) res, sc->sc_cv_wait); 166 167 return err; 168 } 169 170 int 171 mcp2221_get_sram(struct umcpmio_softc *sc, 172 struct mcp2221_get_sram_res *res) 173 { 174 struct mcp2221_get_sram_req req; 175 int err = 0; 176 177 KASSERT(mutex_owned(&sc->sc_action_mutex)); 178 179 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE); 180 req.cmd = MCP2221_CMD_GET_SRAM; 181 182 err = umcpmio_send_report(sc, 183 (uint8_t *) & req, MCP2221_REQ_BUFFER_SIZE, 184 (uint8_t *) res, sc->sc_cv_wait); 185 186 return err; 187 } 188 189 int 190 mcp2221_put_sram(struct umcpmio_softc *sc, 191 struct mcp2221_set_sram_req *req, struct mcp2221_set_sram_res *res) 192 { 193 int err = 0; 194 195 KASSERT(mutex_owned(&sc->sc_action_mutex)); 196 197 req->cmd = MCP2221_CMD_SET_SRAM; 198 199 err = umcpmio_send_report(sc, 200 (uint8_t *) req, MCP2221_REQ_BUFFER_SIZE, 201 (uint8_t *) res, sc->sc_cv_wait); 202 203 return err; 204 } 205 206 int 207 mcp2210_get_nvram(struct umcpmio_softc *sc, uint8_t subcode, 208 struct mcp2210_get_nvram_res *res) 209 { 210 struct mcp2210_get_nvram_req req; 211 int err = 0; 212 213 KASSERT(mutex_owned(&sc->sc_action_mutex)); 214 215 memset(&req, 0, MCP2210_REQ_BUFFER_SIZE); 216 req.cmd = MCP2210_CMD_GET_NVRAM; 217 218 if (subcode < MCP2210_NVRAM_SUBCODE_SPI || 219 subcode > MCP2210_NVRAM_SUBCODE_USBMAN) 220 return EINVAL; 221 222 req.subcode = subcode; 223 224 err = umcpmio_send_report(sc, 225 (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE, 226 (uint8_t *) res, sc->sc_cv_wait); 227 228 err = mcp2210_decode_errors(req.cmd, err, res->completion); 229 230 return err; 231 } 232 233 int 234 mcp2210_set_nvram(struct umcpmio_softc *sc, struct mcp2210_set_nvram_req *req, 235 struct mcp2210_set_nvram_res *res) 236 { 237 int err = 0; 238 239 KASSERT(mutex_owned(&sc->sc_action_mutex)); 240 241 req->cmd = MCP2210_CMD_SET_NVRAM; 242 243 if (req->subcode < MCP2210_NVRAM_SUBCODE_SPI || 244 req->subcode > MCP2210_NVRAM_SUBCODE_USBMAN) { 245 DPRINTF(("mcp2210_set_nvram: subcode out of range:" 246 " subcode=%d\n", 247 req->subcode)); 248 return EINVAL; 249 } 250 err = umcpmio_send_report(sc, 251 (uint8_t *) req, MCP2210_REQ_BUFFER_SIZE, 252 (uint8_t *) res, sc->sc_cv_wait); 253 254 err = mcp2210_decode_errors(req->cmd, err, res->completion); 255 256 return err; 257 } 258 259 int 260 mcp2221_get_flash(struct umcpmio_softc *sc, uint8_t subcode, 261 struct mcp2221_get_flash_res *res) 262 { 263 struct mcp2221_get_flash_req req; 264 int err = 0; 265 266 KASSERT(mutex_owned(&sc->sc_action_mutex)); 267 268 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE); 269 req.cmd = MCP2221_CMD_GET_FLASH; 270 271 if (subcode < MCP2221_FLASH_SUBCODE_CS || 272 subcode > MCP2221_FLASH_SUBCODE_CHIPSN) 273 return EINVAL; 274 275 req.subcode = subcode; 276 277 err = umcpmio_send_report(sc, 278 (uint8_t *) & req, MCP2221_REQ_BUFFER_SIZE, 279 (uint8_t *) res, sc->sc_cv_wait); 280 281 return err; 282 } 283 284 int 285 mcp2221_put_flash(struct umcpmio_softc *sc, struct mcp2221_put_flash_req *req, 286 struct mcp2221_put_flash_res *res) 287 { 288 int err = 0; 289 290 KASSERT(mutex_owned(&sc->sc_action_mutex)); 291 292 req->cmd = MCP2221_CMD_SET_FLASH; 293 294 if (req->subcode < MCP2221_FLASH_SUBCODE_CS || 295 req->subcode > MCP2221_FLASH_SUBCODE_CHIPSN) { 296 DPRINTF(("mcp2221_put_flash: subcode out of range:" 297 " subcode=%d\n", 298 req->subcode)); 299 return EINVAL; 300 } 301 err = umcpmio_send_report(sc, 302 (uint8_t *) req, MCP2221_REQ_BUFFER_SIZE, 303 (uint8_t *) res, sc->sc_cv_wait); 304 305 return err; 306 } 307 308 int 309 mcp2210_read_eeprom(struct umcpmio_softc *sc, uint8_t addr, uint8_t *buf) 310 { 311 struct mcp2210_read_eeprom_req req; 312 struct mcp2210_read_eeprom_res res; 313 int err = 0; 314 315 req.cmd = MCP2210_CMD_READ_EEPROM; 316 req.addr = addr; 317 318 mutex_enter(&sc->sc_action_mutex); 319 err = umcpmio_send_report(sc, 320 (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE, 321 (uint8_t *) & res, sc->sc_cv_wait); 322 mutex_exit(&sc->sc_action_mutex); 323 324 err = mcp2210_decode_errors(req.cmd, err, res.completion); 325 326 if (!err) 327 *buf = res.value; 328 329 return err; 330 } 331 332 int 333 mcp2210_write_eeprom(struct umcpmio_softc *sc, uint8_t addr, uint8_t buf) 334 { 335 struct mcp2210_write_eeprom_req req; 336 struct mcp2210_write_eeprom_res res; 337 int err = 0; 338 339 req.cmd = MCP2210_CMD_WRITE_EEPROM; 340 req.addr = addr; 341 req.value = buf; 342 343 mutex_enter(&sc->sc_action_mutex); 344 err = umcpmio_send_report(sc, 345 (uint8_t *) & req, MCP2210_REQ_BUFFER_SIZE, 346 (uint8_t *) & res, sc->sc_cv_wait); 347 mutex_exit(&sc->sc_action_mutex); 348 349 err = mcp2210_decode_errors(req.cmd, err, res.completion); 350 351 return err; 352 } 353