Home | History | Annotate | Line # | Download | only in usb
      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