xref: /netbsd-src/sys/dev/usb/umcpmio_subr.c (revision 3bfaa97146632a7fff2425e11c47d4964bacef79)
1 /*	$NetBSD: umcpmio_subr.c,v 1.1 2024/12/16 16:37:38 brad Exp $	*/
2 
3 /*
4  * Copyright (c) 2024 Brad Spencer <brad@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.1 2024/12/16 16:37:38 brad Exp $");
21 
22 #ifdef _KERNEL_OPT
23 #include "opt_usb.h"
24 #endif
25 
26 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/conf.h>
29 #include <sys/kernel.h>
30 #include <sys/kmem.h>
31 #include <sys/device.h>
32 #include <sys/sysctl.h>
33 #include <sys/tty.h>
34 #include <sys/file.h>
35 #include <sys/vnode.h>
36 #include <sys/kauth.h>
37 #include <sys/lwp.h>
38 
39 #include <dev/usb/usb.h>
40 #include <dev/usb/usbhid.h>
41 
42 #include <dev/usb/usbdi.h>
43 #include <dev/usb/usbdi_util.h>
44 #include <dev/usb/usbdevs.h>
45 #include <dev/usb/uhidev.h>
46 #include <dev/hid/hid.h>
47 
48 #include <dev/usb/umcpmio.h>
49 #include <dev/usb/umcpmio_subr.h>
50 #include <dev/usb/umcpmio_hid_reports.h>
51 
52 int umcpmio_send_report(struct umcpmio_softc *, uint8_t *, size_t, uint8_t *, int);
53 
54 #define UMCPMIO_DEBUG 1
55 #ifdef UMCPMIO_DEBUG
56 #define DPRINTF(x)	if (umcpmiodebug) printf x
57 #define DPRINTFN(n, x)	if (umcpmiodebug > (n)) printf x
58 extern int	umcpmiodebug;
59 #else
60 #define DPRINTF(x)	__nothing
61 #define DPRINTFN(n,x)	__nothing
62 #endif
63 
64 /* Handy functions that do a bunch of things for the main driver code */
65 
66 int
67 umcpmio_get_status(struct umcpmio_softc *sc,
68     struct mcp2221_status_res *res, bool takemutex)
69 {
70 	struct mcp2221_status_req req;
71 	int err = 0;
72 
73 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
74 	req.cmd = MCP2221_CMD_STATUS;
75 
76 	if (takemutex)
77 		mutex_enter(&sc->sc_action_mutex);
78 	err = umcpmio_send_report(sc, (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
79 	if (takemutex)
80 		mutex_exit(&sc->sc_action_mutex);
81 
82 	return(err);
83 }
84 
85 void
86 umcpmio_set_i2c_speed(struct mcp2221_status_req *req,
87     int flags)
88 {
89 	int i2cbaud = MCP2221_DEFAULT_I2C_SPEED;
90 
91 	if (flags & I2C_F_SPEED)
92 		i2cbaud = 400000;
93 
94 	req->set_i2c_speed = MCP2221_I2C_SET_SPEED;
95 	if (i2cbaud <= 0)
96 		i2cbaud = MCP2221_DEFAULT_I2C_SPEED;
97 
98 	/* Everyone and their brother seems to store the I2C divider like this,
99 	 * so do likewise */
100 
101 	req->i2c_clock_divider = (MCP2221_INTERNAL_CLOCK / i2cbaud) - 3;
102 }
103 
104 int
105 umcpmio_put_status(struct umcpmio_softc *sc,
106     struct mcp2221_status_req *req, struct mcp2221_status_res *res,
107     bool takemutex)
108 {
109 	int err = 0;
110 
111 	req->cmd = MCP2221_CMD_STATUS;
112 
113 	if (takemutex)
114 		mutex_enter(&sc->sc_action_mutex);
115 	err = umcpmio_send_report(sc, (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
116 	if (takemutex)
117 		mutex_exit(&sc->sc_action_mutex);
118 
119 	return(err);
120 }
121 
122 int
123 umcpmio_set_i2c_speed_one(struct umcpmio_softc *sc,
124     int flags, bool takemutex)
125 {
126 	int err = 0;
127 	struct mcp2221_status_req req;
128 	struct mcp2221_status_res res;
129 
130 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
131 	umcpmio_set_i2c_speed(&req, flags);
132 	err = umcpmio_put_status(sc, &req, &res, takemutex);
133 	if (! err) {
134 		if (res.set_i2c_speed == MCP2221_I2C_SPEED_BUSY)
135 			err = EBUSY;
136 	}
137 
138 	return(err);
139 }
140 
141 int
142 umcpmio_get_sram(struct umcpmio_softc *sc,
143     struct mcp2221_get_sram_res *res, bool takemutex)
144 {
145 	struct mcp2221_get_sram_req req;
146 	int err = 0;
147 
148 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
149 	req.cmd = MCP2221_CMD_GET_SRAM;
150 
151 	if (takemutex)
152 		mutex_enter(&sc->sc_action_mutex);
153 	err = umcpmio_send_report(sc, (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
154 	if (takemutex)
155 		mutex_exit(&sc->sc_action_mutex);
156 
157 	return(err);
158 }
159 
160 int
161 umcpmio_put_sram(struct umcpmio_softc *sc,
162     struct mcp2221_set_sram_req *req, struct mcp2221_set_sram_res *res,
163     bool takemutex)
164 {
165 	int err = 0;
166 
167 	req->cmd = MCP2221_CMD_SET_SRAM;
168 
169 	if (takemutex)
170 		mutex_enter(&sc->sc_action_mutex);
171 	err = umcpmio_send_report(sc, (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
172 	if (takemutex)
173 		mutex_exit(&sc->sc_action_mutex);
174 
175 	return(err);
176 }
177 
178 /* We call the dedicated function ALT3 everywhere */
179 
180 uint32_t
181 umcpmio_sram_gpio_to_flags(uint8_t gp_setting)
182 {
183 	uint32_t r = 0;
184 
185 	switch (gp_setting & MCP2221_SRAM_PIN_TYPE_MASK) {
186 	case MCP2221_SRAM_PIN_IS_DED:
187 		r |= GPIO_PIN_ALT3;
188 		break;
189 	case MCP2221_SRAM_PIN_IS_ALT0:
190 		r |= GPIO_PIN_ALT0;
191 		break;
192 	case MCP2221_SRAM_PIN_IS_ALT1:
193 		r |= GPIO_PIN_ALT1;
194 		break;
195 	case MCP2221_SRAM_PIN_IS_ALT2:
196 		r |= GPIO_PIN_ALT2;
197 		break;
198 	case MCP2221_SRAM_PIN_IS_GPIO:
199 	default:
200 		if ((gp_setting & MCP2221_SRAM_GPIO_TYPE_MASK) == MCP2221_SRAM_GPIO_INPUT)
201 			r |= GPIO_PIN_INPUT;
202 		else
203 			r |= GPIO_PIN_OUTPUT;
204 		break;
205 	}
206 
207 	return(r);
208 }
209 
210 void
211 umcpmio_set_gpio_value_sram(struct mcp2221_set_sram_req *req, int pin, bool value)
212 {
213 	uint8_t *alter = NULL;
214 	uint8_t *newvalue = NULL;
215 
216 	if (pin >=0 && pin < MCP2221_NPINS) {
217 		switch (pin) {
218 		case 0:
219 			alter = &req->alter_gpio_config;
220 			newvalue = &req->gp0_settings;
221 			break;
222 		case 1:
223 			alter = &req->alter_gpio_config;
224 			newvalue = &req->gp1_settings;
225 			break;
226 		case 2:
227 			alter = &req->alter_gpio_config;
228 			newvalue = &req->gp2_settings;
229 			break;
230 		case 3:
231 			alter = &req->alter_gpio_config;
232 			newvalue = &req->gp3_settings;
233 			break;
234 		default:
235 			break;
236 		}
237 
238 		if (alter != NULL) {
239 			*alter = MCP2221_SRAM_ALTER_GPIO;
240 			if (value)
241 				*newvalue |= MCP2221_SRAM_GPIO_HIGH;
242 			else
243 				*newvalue &= ~MCP2221_SRAM_GPIO_HIGH;
244 		}
245 	}
246 }
247 
248 void
249 umcpmio_set_gpio_dir_sram(struct mcp2221_set_sram_req *req, int pin, int flags)
250 {
251 	uint8_t *alter = NULL;
252 	uint8_t *newvalue = NULL;
253 
254 	if (pin >=0 && pin < MCP2221_NPINS) {
255 		switch (pin) {
256 		case 0:
257 			alter = &req->alter_gpio_config;
258 			newvalue = &req->gp0_settings;
259 			break;
260 		case 1:
261 			alter = &req->alter_gpio_config;
262 			newvalue = &req->gp1_settings;
263 			break;
264 		case 2:
265 			alter = &req->alter_gpio_config;
266 			newvalue = &req->gp2_settings;
267 			break;
268 		case 3:
269 			alter = &req->alter_gpio_config;
270 			newvalue = &req->gp3_settings;
271 			break;
272 		default:
273 			break;
274 		}
275 
276 		if (alter != NULL) {
277 			*alter = MCP2221_SRAM_ALTER_GPIO;
278 			if (flags & GPIO_PIN_INPUT)
279 				*newvalue |= MCP2221_SRAM_GPIO_INPUT;
280 			else
281 				*newvalue &= ~MCP2221_SRAM_GPIO_INPUT;
282 		}
283 	}
284 }
285 
286 void
287 umcpmio_set_gpio_designation_sram(struct mcp2221_set_sram_req *req, int pin, int flags)
288 {
289 	uint8_t *alter = NULL;
290 	uint8_t *newvalue = NULL;
291 	uint32_t altmask = GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT2 | GPIO_PIN_ALT3;
292 
293 	if (pin >=0 && pin < MCP2221_NPINS) {
294 		switch (pin) {
295 		case 0:
296 			alter = &req->alter_gpio_config;
297 			newvalue = &req->gp0_settings;
298 			break;
299 		case 1:
300 			alter = &req->alter_gpio_config;
301 			newvalue = &req->gp1_settings;
302 			break;
303 		case 2:
304 			alter = &req->alter_gpio_config;
305 			newvalue = &req->gp2_settings;
306 			break;
307 		case 3:
308 			alter = &req->alter_gpio_config;
309 			newvalue = &req->gp3_settings;
310 			break;
311 		default:
312 			break;
313 		}
314 
315 		if (alter != NULL) {
316 			int nv = *newvalue;
317 
318 			*alter = MCP2221_SRAM_ALTER_GPIO;
319 			nv &= 0xF8;
320 
321 			if (flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INPUT)) {
322 				nv |= MCP2221_SRAM_PIN_IS_GPIO;
323 			} else {
324 				switch (flags & altmask) {
325 				case GPIO_PIN_ALT0:
326 					nv |= MCP2221_SRAM_PIN_IS_ALT0;
327 					break;
328 				case GPIO_PIN_ALT1:
329 					nv |= MCP2221_SRAM_PIN_IS_ALT1;
330 					break;
331 				case GPIO_PIN_ALT2:
332 					nv |= MCP2221_SRAM_PIN_IS_ALT2;
333 					break;
334 					/* ALT3 will always be used as the dedicated function specific to the pin.
335 					 * Not all of the pins will have the alt functions below #3.
336 					 */
337 				case GPIO_PIN_ALT3:
338 					nv |= MCP2221_SRAM_PIN_IS_DED;
339 					break;
340 				default:
341 					break;
342 				}
343 			}
344 			*newvalue = nv;
345 		}
346 	}
347 }
348 
349 void
350 umcpmio_set_gpio_irq_sram(struct mcp2221_set_sram_req *req, int irqmode)
351 {
352 	req->alter_gpio_config = MCP2221_SRAM_ALTER_GPIO;
353 
354 	if (irqmode & (GPIO_INTR_POS_EDGE | GPIO_INTR_DOUBLE_EDGE)) {
355 		req->irq_config |= MCP2221_SRAM_ALTER_IRQ | MCP2221_SRAM_ALTER_POS_EDGE | MCP2221_SRAM_ENABLE_POS_EDGE | MCP2221_SRAM_CLEAR_IRQ;
356 	}
357 	if (irqmode & (GPIO_INTR_NEG_EDGE | GPIO_INTR_DOUBLE_EDGE)) {
358 		req->irq_config |= MCP2221_SRAM_ALTER_IRQ | MCP2221_SRAM_ALTER_NEG_EDGE | MCP2221_SRAM_ENABLE_NEG_EDGE | MCP2221_SRAM_CLEAR_IRQ;
359 	}
360 
361 	if (req->irq_config != 0) {
362 		req->gp1_settings = MCP2221_SRAM_PIN_IS_ALT2;
363 	} else {
364 		req->irq_config = MCP2221_SRAM_ALTER_IRQ | MCP2221_SRAM_CLEAR_IRQ;
365 		req->gp1_settings = MCP2221_SRAM_PIN_IS_GPIO | MCP2221_SRAM_GPIO_INPUT;
366 	}
367 }
368 
369 /* It is unfortunate that the GET and PUT requests are not symertric.  That is,
370  * the bits sort of line up but not quite between a GET and PUT. */
371 
372 static struct umcpmio_mapping_put umcpmio_vref_puts[] = {
373 	{
374 		.tname = "4.096V",
375 		.mask = 0x06 | 0x01,
376 	},
377 	{
378 		.tname = "2.048V",
379 		.mask = 0x04 | 0x01,
380 	},
381 	{
382 		.tname = "1.024V",
383 		.mask = 0x02 | 0x01,
384 	},
385 	{
386 		.tname = "OFF",
387 		.mask = 0x00 | 0x01,
388 	},
389 	{
390 		.tname = "VDD",
391 		.mask = 0x00,
392 	}
393 };
394 
395 void
396 umcpmio_set_dac_vref(struct mcp2221_set_sram_req *req, char *newvref)
397 {
398 	int i;
399 
400 	for (i = 0; i < __arraycount(umcpmio_vref_puts); i++) {
401 		if (strncmp(newvref, umcpmio_vref_puts[i].tname,
402 		    UMCPMIO_VREF_NAME) == 0) {
403 			break;
404 		}
405 	}
406 
407 	if (i == __arraycount(umcpmio_vref_puts))
408 		return;
409 
410 	req->dac_voltage_reference |= umcpmio_vref_puts[i].mask | MCP2221_SRAM_CHANGE_DAC_VREF;
411 }
412 
413 int
414 umcpmio_set_dac_vref_one(struct umcpmio_softc *sc, char *newvref, bool takemutex)
415 {
416 	struct mcp2221_set_sram_req req;
417 	struct mcp2221_set_sram_res res;
418 	int err = 0;
419 
420 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
421 	umcpmio_set_dac_vref(&req, newvref);
422 	err = umcpmio_put_sram(sc, &req, &res, takemutex);
423 
424 	return err;
425 }
426 
427 void
428 umcpmio_set_dac_value(struct mcp2221_set_sram_req *req, uint8_t newvalue)
429 {
430 	req->set_dac_output_value |= (newvalue & MCP2221_SRAM_DAC_VALUE_MASK) | MCP2221_SRAM_CHANGE_DAC_VREF;
431 }
432 
433 int
434 umcpmio_set_dac_value_one(struct umcpmio_softc *sc, uint8_t newvalue, bool takemutex)
435 {
436 	struct mcp2221_set_sram_req req;
437 	struct mcp2221_set_sram_res res;
438 	int err = 0;
439 
440 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
441 	umcpmio_set_dac_value(&req, newvalue);
442 	err = umcpmio_put_sram(sc, &req, &res, takemutex);
443 
444 	return err;
445 }
446 
447 void
448 umcpmio_set_adc_vref(struct mcp2221_set_sram_req *req, char *newvref)
449 {
450 	int i;
451 
452 	for (i = 0; i < __arraycount(umcpmio_vref_puts); i++) {
453 		if (strncmp(newvref, umcpmio_vref_puts[i].tname,
454 		    UMCPMIO_VREF_NAME) == 0) {
455 			break;
456 		}
457 	}
458 
459 	if (i == __arraycount(umcpmio_vref_puts))
460 		return;
461 
462 	req->adc_voltage_reference |= umcpmio_vref_puts[i].mask | MCP2221_SRAM_CHANGE_ADC_VREF;
463 }
464 
465 int
466 umcpmio_set_adc_vref_one(struct umcpmio_softc *sc, char *newvref, bool takemutex)
467 {
468 	struct mcp2221_set_sram_req req;
469 	struct mcp2221_set_sram_res res;
470 	int err = 0;
471 
472 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
473 	umcpmio_set_adc_vref(&req, newvref);
474 	err = umcpmio_put_sram(sc, &req, &res, takemutex);
475 
476 	return err;
477 }
478 
479 static struct umcpmio_mapping_put umcpmio_dc_puts[] = {
480 	{
481 		.tname = "75%",
482 		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_75,
483 	},
484 	{
485 		.tname = "50%",
486 		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_50,
487 	},
488 	{
489 		.tname = "25%",
490 		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_25,
491 	},
492 	{
493 		.tname = "0%",
494 		.mask = MCP2221_SRAM_GPIO_CLOCK_DC_0,
495 	}
496 };
497 
498 void
499 umcpmio_set_gpioclock_dc(struct mcp2221_set_sram_req *req, char *new_dc)
500 {
501 	int i;
502 
503 	for (i = 0; i < __arraycount(umcpmio_dc_puts); i++) {
504 		if (strncmp(new_dc, umcpmio_dc_puts[i].tname,
505 		    UMCPMIO_VREF_NAME) == 0) {
506 			break;
507 		}
508 	}
509 
510 	if (i == __arraycount(umcpmio_dc_puts))
511 		return;
512 
513 	req->clock_output_divider |= umcpmio_dc_puts[i].mask;
514 }
515 
516 int
517 umcpmio_set_gpioclock_dc_one(struct umcpmio_softc *sc, char *new_dutycycle, bool takemutex)
518 {
519 	struct mcp2221_get_sram_res current_sram_res;
520 	struct mcp2221_set_sram_req req;
521 	struct mcp2221_set_sram_res res;
522 	int err = 0;
523 
524 	err = umcpmio_get_sram(sc, &current_sram_res, takemutex);
525 	if (! err) {
526 		memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
527 		umcpmio_set_gpioclock_dc(&req, new_dutycycle);
528 		DPRINTF(("umcpmio_set_gpioclock_dc_one: req.clock_output_divider=%02x,current mask=%02x\n",req.clock_output_divider,current_sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_CD_MASK));
529 		req.clock_output_divider |= (current_sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_CD_MASK) | MCP2221_SRAM_GPIO_CHANGE_DCCD;
530 		DPRINTF(("umcpmio_set_gpioclock_dc_one: SET req.clock_output_divider=%02x\n",req.clock_output_divider));
531 		err = umcpmio_put_sram(sc, &req, &res, takemutex);
532 	}
533 
534 	return err;
535 }
536 
537 static struct umcpmio_mapping_put umcpmio_cd_puts[] = {
538 	{
539 		.tname = "375kHz",
540 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_375KHZ,
541 	},
542 	{
543 		.tname = "750kHz",
544 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_750KHZ,
545 	},
546 	{
547 		.tname = "1.5MHz",
548 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_1P5MHZ,
549 	},
550 	{
551 		.tname = "3MHz",
552 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_3MHZ,
553 	},
554 	{
555 		.tname = "6MHz",
556 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_6MHZ,
557 	},
558 	{
559 		.tname = "12MHz",
560 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_12MHZ,
561 	},
562 	{
563 		.tname = "24MHz",
564 		.mask = MCP2221_SRAM_GPIO_CLOCK_CD_24MHZ,
565 	}
566 };
567 
568 void
569 umcpmio_set_gpioclock_cd(struct mcp2221_set_sram_req *req, char *new_cd)
570 {
571 	int i;
572 
573 	for (i = 0; i < __arraycount(umcpmio_cd_puts); i++) {
574 		if (strncmp(new_cd, umcpmio_cd_puts[i].tname,
575 		    UMCPMIO_CD_NAME) == 0) {
576 			break;
577 		}
578 	}
579 
580 	if (i == __arraycount(umcpmio_cd_puts))
581 		return;
582 
583 	req->clock_output_divider |= umcpmio_cd_puts[i].mask;
584 }
585 
586 int
587 umcpmio_set_gpioclock_cd_one(struct umcpmio_softc *sc, char *new_clockdivider, bool takemutex)
588 {
589 	struct mcp2221_get_sram_res current_sram_res;
590 	struct mcp2221_set_sram_req req;
591 	struct mcp2221_set_sram_res res;
592 	int err = 0;
593 
594 	err = umcpmio_get_sram(sc, &current_sram_res, takemutex);
595 	if (! err) {
596 		memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
597 		umcpmio_set_gpioclock_cd(&req, new_clockdivider);
598 		DPRINTF(("umcpmio_set_gpioclock_cd_one: req.clock_output_divider=%02x,current mask=%02x\n",req.clock_output_divider,current_sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_CD_MASK));
599 		req.clock_output_divider |= (current_sram_res.clock_divider & MCP2221_SRAM_GPIO_CLOCK_DC_MASK) | MCP2221_SRAM_GPIO_CHANGE_DCCD;
600 		DPRINTF(("umcpmio_set_gpioclock_cd_one: SET req.clock_output_divider=%02x\n",req.clock_output_divider));
601 		err = umcpmio_put_sram(sc, &req, &res, takemutex);
602 	}
603 
604 	return err;
605 }
606 
607 int
608 umcpmio_get_gpio_cfg(struct umcpmio_softc *sc,
609     struct mcp2221_get_gpio_cfg_res *res, bool takemutex)
610 {
611 	struct mcp2221_get_gpio_cfg_req req;
612 	int err = 0;
613 
614 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
615 	req.cmd = MCP2221_CMD_GET_GPIO_CFG;
616 
617 	if (takemutex)
618 		mutex_enter(&sc->sc_action_mutex);
619 	err = umcpmio_send_report(sc, (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
620 	if (takemutex)
621 		mutex_exit(&sc->sc_action_mutex);
622 
623 	return(err);
624 }
625 
626 int
627 umcpmio_put_gpio_cfg(struct umcpmio_softc *sc,
628     struct mcp2221_set_gpio_cfg_req *req, struct mcp2221_set_gpio_cfg_res *res,
629     bool takemutex)
630 {
631 	int err = 0;
632 
633 	req->cmd = MCP2221_CMD_SET_GPIO_CFG;
634 
635 	if (takemutex)
636 		mutex_enter(&sc->sc_action_mutex);
637 	err = umcpmio_send_report(sc, (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
638 	if (takemutex)
639 		mutex_exit(&sc->sc_action_mutex);
640 
641 	return(err);
642 }
643 
644 /* So... if the pin isn't set to GPIO, just call the output LOW */
645 
646 int
647 umcpmio_get_gpio_value(struct umcpmio_softc *sc,
648     int pin, bool takemutex)
649 {
650 	struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res;
651 	int err = 0;
652 	int r = GPIO_PIN_LOW;
653 
654 	err = umcpmio_get_gpio_cfg(sc, &get_gpio_cfg_res, takemutex);
655 	if (! err) {
656 		if (get_gpio_cfg_res.cmd == MCP2221_CMD_GET_GPIO_CFG &&
657 		    get_gpio_cfg_res.completion == MCP2221_CMD_COMPLETE_OK) {
658 			switch (pin) {
659 			case 0:
660 				if (get_gpio_cfg_res.gp0_pin_value != MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
661 					if (get_gpio_cfg_res.gp0_pin_value == 0x01)
662 						r = GPIO_PIN_HIGH;
663 				break;
664 			case 1:
665 				if (get_gpio_cfg_res.gp1_pin_value != MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
666 					if (get_gpio_cfg_res.gp1_pin_value == 0x01)
667 						r = GPIO_PIN_HIGH;
668 				break;
669 			case 2:
670 				if (get_gpio_cfg_res.gp2_pin_value != MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
671 					if (get_gpio_cfg_res.gp2_pin_value == 0x01)
672 						r = GPIO_PIN_HIGH;
673 				break;
674 			case 3:
675 				if (get_gpio_cfg_res.gp3_pin_value != MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
676 					if (get_gpio_cfg_res.gp3_pin_value == 0x01)
677 						r = GPIO_PIN_HIGH;
678 				break;
679 			default:
680 				break;
681 			}
682 		} else {
683 			device_printf(sc->sc_dev, "umcpmio_get_gpio_value: wrong command or error: %02x %02x\n",
684 			    get_gpio_cfg_res.cmd,
685 			    get_gpio_cfg_res.completion);
686 		}
687 	}
688 
689 	return(r);
690 }
691 
692 void
693 umcpmio_set_gpio_value(struct mcp2221_set_gpio_cfg_req *req,
694     int pin, bool value)
695 {
696 	uint8_t *alter = NULL;
697 	uint8_t *newvalue = NULL;
698 
699 	if (pin >=0 && pin < MCP2221_NPINS) {
700 		switch (pin) {
701 		case 0:
702 			alter = &req->alter_gp0_value;
703 			newvalue = &req->new_gp0_value;
704 			break;
705 		case 1:
706 			alter = &req->alter_gp1_value;
707 			newvalue = &req->new_gp1_value;
708 			break;
709 		case 2:
710 			alter = &req->alter_gp2_value;
711 			newvalue = &req->new_gp2_value;
712 			break;
713 		case 3:
714 			alter = &req->alter_gp3_value;
715 			newvalue = &req->new_gp3_value;
716 			break;
717 		default:
718 			break;
719 		}
720 
721 		if (alter != NULL) {
722 			*alter = MCP2221_GPIO_CFG_ALTER;
723 			*newvalue = 0;
724 			if (value)
725 				*newvalue = 1;
726 		}
727 	}
728 }
729 
730 int
731 umcpmio_set_gpio_value_one(struct umcpmio_softc *sc,
732     int pin, bool value, bool takemutex)
733 {
734 	int err = 0;
735 	struct mcp2221_set_gpio_cfg_req req;
736 	struct mcp2221_set_gpio_cfg_res res;
737 
738 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
739 	umcpmio_set_gpio_value(&req, pin, value);
740 	err = umcpmio_put_gpio_cfg(sc, &req, &res, takemutex);
741 	if (! err) {
742 		if (res.cmd == MCP2221_CMD_SET_GPIO_CFG &&
743 		    res.completion == MCP2221_CMD_COMPLETE_OK) {
744 		} else {
745 			err = EIO;
746 			device_printf(sc->sc_dev, "umcpmio_gpio_pin_write:  not the command desired, or error: %02x %02x\n",
747 			    res.cmd,
748 			    res.completion);
749 		}
750 	}
751 
752 	return(err);
753 }
754 
755 int
756 umcpmio_get_flash(struct umcpmio_softc *sc, uint8_t subcode,
757     struct mcp2221_get_flash_res *res, bool takemutex)
758 {
759 	struct mcp2221_get_flash_req req;
760 	int err = 0;
761 
762 	memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
763 	req.cmd = MCP2221_CMD_GET_FLASH;
764 
765 	if (subcode < MCP2221_FLASH_SUBCODE_CS ||
766 	    subcode > MCP2221_FLASH_SUBCODE_CHIPSN)
767 		return(EINVAL);
768 
769 	req.subcode = subcode;
770 
771 	if (takemutex)
772 		mutex_enter(&sc->sc_action_mutex);
773 	err = umcpmio_send_report(sc, (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
774 	if (takemutex)
775 		mutex_exit(&sc->sc_action_mutex);
776 
777 	return(err);
778 }
779 
780 int
781 umcpmio_put_flash(struct umcpmio_softc *sc, struct mcp2221_put_flash_req *req,
782     struct mcp2221_put_flash_res *res, bool takemutex)
783 {
784 	int err = 0;
785 
786 	req->cmd = MCP2221_CMD_SET_FLASH;
787 
788 	if (req->subcode < MCP2221_FLASH_SUBCODE_CS ||
789 	    req->subcode > MCP2221_FLASH_SUBCODE_CHIPSN) {
790 		DPRINTF(("umcpmio_put_flash: subcode out of range: subcode=%d\n",req->subcode));
791 		return(EINVAL);
792 	}
793 
794 	if (takemutex)
795 		mutex_enter(&sc->sc_action_mutex);
796 	err = umcpmio_send_report(sc, (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE, (uint8_t *)res, sc->sc_cv_wait);
797 	if (takemutex)
798 		mutex_exit(&sc->sc_action_mutex);
799 
800 	return(err);
801 }
802 
803