Home | History | Annotate | Line # | Download | only in umcpmioctl
      1 /*	$NetBSD: umcpmioctl.c,v 1.6 2025/03/22 06:37:22 rillig 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 #ifdef __RCSID
     21 __RCSID("$NetBSD: umcpmioctl.c,v 1.6 2025/03/22 06:37:22 rillig Exp $");
     22 #endif
     23 
     24 /* Main userland program that can pull the SRAM and FLASH content from a MCP2221
     25  * / MCP2221A chip using umcpmio(4).
     26  */
     27 
     28 #include <stdio.h>
     29 #include <stdint.h>
     30 #include <unistd.h>
     31 #include <stdbool.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <err.h>
     35 #include <fcntl.h>
     36 #include <sys/ioctl.h>
     37 
     38 #include <dev/usb/umcpmio_hid_reports.h>
     39 #include <dev/usb/umcpmio_io.h>
     40 
     41 #define EXTERN extern
     42 #include "umcpmioctl.h"
     43 #include "umcpmioctlconst.h"
     44 #include "printumcpmio.h"
     45 #include "putflash.h"
     46 
     47 __dead static void
     48 usage(int status)
     49 {
     50 	const char *p = getprogname();
     51 
     52 	fprintf(stderr, "Usage: %s [-dh] device cmd args\n\n",
     53 	    p);
     54 
     55 	for (long unsigned int i = 0; i < __arraycount(umcpmioctlcmds); i++) {
     56 		switch (umcpmioctlcmds[i].id) {
     57 		case UMCPMIO_GET:
     58 			for (long unsigned int j = 0; j < __arraycount(getsubcmds); j++) {
     59 				fprintf(stderr, "%s [-dh] device %s %s %s\n",
     60 				    p, umcpmioctlcmds[i].cmd, getsubcmds[j].cmd, getsubcmds[j].helpargs);
     61 			}
     62 			break;
     63 		case UMCPMIO_PUT:
     64 			for (long unsigned int j = 0; j < __arraycount(putsubcmds); j++) {
     65 				fprintf(stderr, "%s [-dh] device %s %s %s\n",
     66 				    p, umcpmioctlcmds[i].cmd, putsubcmds[j].cmd, putsubcmds[j].helpargs);
     67 			}
     68 			break;
     69 		default:
     70 			fprintf(stderr, "%s [-dh] device %s %s\n",
     71 			    p, umcpmioctlcmds[i].cmd, umcpmioctlcmds[i].helpargs);
     72 			break;
     73 		};
     74 	}
     75 
     76 	fprintf(stderr, "\n");
     77 	fprintf(stderr, "sram - The SRAM on the chip\n");
     78 	fprintf(stderr, "gp - The GPIO pin state and function\n");
     79 	fprintf(stderr, "cs - Chip Settings\n");
     80 	fprintf(stderr, "usbman - USB Manufacturer Descriptor\n");
     81 	fprintf(stderr, "usbprod - USB Product Descriptor\n");
     82 	fprintf(stderr, "usbsn - USB Serial Number\n");
     83 	fprintf(stderr, "chipsn - Chip Serial Number\n");
     84 	exit(status);
     85 }
     86 
     87 static int
     88 valid_cmd(const struct umcpmioctlcmd c[], long unsigned int csize, char *cmdtocheck)
     89 {
     90 	int r = -1;
     91 
     92 	for (long unsigned int i = 0; i < csize; i++) {
     93 		if (strncmp(cmdtocheck, c[i].cmd, 16) == 0) {
     94 			r = (int)i;
     95 			break;
     96 		}
     97 	}
     98 
     99 	return r;
    100 }
    101 
    102 int
    103 main(int argc, char *argv[])
    104 {
    105 	int c;
    106 	bool debug = false;
    107 	int fd = -1, error = 0, valid, validsub = -1, validsubsub = -1;
    108 
    109 	while ((c = getopt(argc, argv, "dh")) != -1) {
    110 		switch (c) {
    111 		case 'd':
    112 			debug = true;
    113 			break;
    114 		case 'h':
    115 			usage(0);
    116 		default:
    117 			usage(1);
    118 		}
    119 	}
    120 
    121 	argc -= optind;
    122 	argv += optind;
    123 
    124 	if (debug) {
    125 		fprintf(stderr, "ARGC: %d\n", argc);
    126 		fprintf(stderr, "ARGV[0]: %s ; ARGV[1]: %s ; ARGV[2]: %s ; ARGV[3]: %s; ARGV[4]: %s; ARGV[5]: %s\n",
    127 		    argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
    128 	}
    129 
    130 	if (argc <= 1)
    131 		usage(0);
    132 
    133 	fd = open(argv[0], O_RDWR, 0);
    134 	if (fd == -1) {
    135 		err(EXIT_FAILURE, "open %s", argv[0]);
    136 	}
    137 
    138 	/* Parse out the command line into what the requested action is */
    139 
    140 	valid = valid_cmd(umcpmioctlcmds, __arraycount(umcpmioctlcmds), argv[1]);
    141 	if (valid == -1) {
    142 		fprintf(stderr, "Unknown command: %s\n\n", argv[1]);
    143 		usage(1);
    144 	}
    145 
    146 	uint8_t *buf;
    147 	struct mcp2221_status_res status_res;
    148 	struct mcp2221_get_sram_res get_sram_res;
    149 	struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res;
    150 	struct umcpmio_ioctl_get_flash ioctl_get_flash;
    151 	struct umcpmio_ioctl_put_flash ioctl_put_flash;
    152 
    153 	switch (umcpmioctlcmds[valid].id) {
    154 	case UMCPMIO_GET:
    155 		if (argc < 3) {
    156 			fprintf(stderr, "Missing arguments to get command\n\n");
    157 			usage(1);
    158 		}
    159 		validsub = valid_cmd(getsubcmds, __arraycount(getsubcmds), argv[2]);
    160 		if (validsub == -1) {
    161 			fprintf(stderr, "Unknown subcommand to get: %s\n\n", argv[2]);
    162 			usage(1);
    163 		}
    164 		switch (getsubcmds[validsub].id) {
    165 		case UMCPMIO_IOCTL_GET_SRAM:
    166 			error = ioctl(fd, UMCPMIO_GET_SRAM, &get_sram_res);
    167 			break;
    168 		case UMCPMIO_IOCTL_GET_GP_CFG:
    169 			error = ioctl(fd, UMCPMIO_GET_GP_CFG, &get_gpio_cfg_res);
    170 			break;
    171 		case UMCPMIO_IOCTL_GET_FLASH:
    172 			if (argc != 4) {
    173 				fprintf(stderr, "Missing arguments to get flash command\n\n");
    174 				usage(1);
    175 			}
    176 			validsubsub = valid_cmd(getflashsubcmds, __arraycount(getflashsubcmds), argv[3]);
    177 			if (validsubsub == -1) {
    178 				fprintf(stderr, "Unknown subcommand to get flash: %s %d\n\n", argv[3], validsubsub);
    179 				usage(1);
    180 			}
    181 			switch (getflashsubcmds[validsubsub].id) {
    182 			case UMCPMIO_IOCTL_GET_FLASH_CS:
    183 				ioctl_get_flash.subcode = MCP2221_FLASH_SUBCODE_CS;
    184 				break;
    185 			case UMCPMIO_IOCTL_GET_FLASH_GP:
    186 				ioctl_get_flash.subcode = MCP2221_FLASH_SUBCODE_GP;
    187 				break;
    188 			case UMCPMIO_IOCTL_GET_FLASH_USBMAN:
    189 				ioctl_get_flash.subcode = MCP2221_FLASH_SUBCODE_USBMAN;
    190 				break;
    191 			case UMCPMIO_IOCTL_GET_FLASH_USBPROD:
    192 				ioctl_get_flash.subcode = MCP2221_FLASH_SUBCODE_USBPROD;
    193 				break;
    194 			case UMCPMIO_IOCTL_GET_FLASH_USBSN:
    195 				ioctl_get_flash.subcode = MCP2221_FLASH_SUBCODE_USBSN;
    196 				break;
    197 			case UMCPMIO_IOCTL_GET_FLASH_CHIPSN:
    198 				ioctl_get_flash.subcode = MCP2221_FLASH_SUBCODE_CHIPSN;
    199 				break;
    200 			default:
    201 				fprintf(stderr, "Unhandled subcommand to get flash: %s %d\n\n", argv[3], validsubsub);
    202 				usage(1);
    203 			}
    204 			error = ioctl(fd, UMCPMIO_GET_FLASH, &ioctl_get_flash);
    205 			break;
    206 		default:
    207 			fprintf(stderr, "Unhandled subcommand to get: %s %d\n\n", argv[2], validsub);
    208 			usage(1);
    209 		}
    210 		break;
    211 	case UMCPMIO_PUT:
    212 		if (argc <= 3) {
    213 			fprintf(stderr, "Missing arguments to put command\n\n");
    214 			usage(1);
    215 		}
    216 		validsub = valid_cmd(putsubcmds, __arraycount(putsubcmds), argv[2]);
    217 		if (validsub == -1) {
    218 			fprintf(stderr, "Unknown subcommand to put: %s\n\n", argv[2]);
    219 			usage(1);
    220 		}
    221 		switch (putsubcmds[validsub].id) {
    222 		case UMCPMIO_IOCTL_PUT_FLASH:
    223 			if (argc < 4) {
    224 				fprintf(stderr, "Missing arguments to put flash command\n\n");
    225 				usage(1);
    226 			}
    227 			validsubsub = valid_cmd(putflashsubcmds, __arraycount(putflashsubcmds), argv[3]);
    228 			if (validsubsub == -1) {
    229 				fprintf(stderr, "Unknown subcommand to put flash: %s %d\n\n", argv[3], validsubsub);
    230 				usage(1);
    231 			}
    232 			switch (putflashsubcmds[validsubsub].id) {
    233 			case UMCPMIO_IOCTL_PUT_FLASH_GP:
    234 				memset(&ioctl_put_flash, 0, sizeof(ioctl_put_flash));
    235 				ioctl_put_flash.subcode = MCP2221_FLASH_SUBCODE_GP;
    236 				error = parse_flash_gp_req(fd, &ioctl_put_flash.put_flash_req, argv, 4, argc, debug);
    237 
    238 				if (debug) {
    239 					fprintf(stderr, "REQ FOR FLASH GP PUT: error=%d:\n", error);
    240 					buf = (uint8_t *)&ioctl_put_flash.put_flash_req.cmd;
    241 					for (int i = 0; i < MCP2221_REQ_BUFFER_SIZE; i++) {
    242 						fprintf(stderr, " %02x", buf[i]);
    243 					}
    244 					fprintf(stderr, "\n----\n");
    245 				}
    246 
    247 				if (!error)
    248 					error = ioctl(fd, UMCPMIO_PUT_FLASH, &ioctl_put_flash);
    249 
    250 				break;
    251 			default:
    252 				fprintf(stderr, "Unhandled subcommand to put flash: %s %d\n\n", argv[3], validsubsub);
    253 				usage(1);
    254 			}
    255 			break;
    256 		default:
    257 			fprintf(stderr, "Unhandled subcommand to put: %s %d\n\n", argv[2], validsub);
    258 			usage(1);
    259 		}
    260 		break;
    261 	case UMCPMIO_STATUS:
    262 		if (debug)
    263 			fprintf(stderr, "Doing status\n");
    264 		error = ioctl(fd, UMCPMIO_GET_STATUS, &status_res);
    265 		if (debug)
    266 			fprintf(stderr, "UMCPMIO_GET_STATUS: error=%d, \n", error);
    267 		break;
    268 	default:
    269 		fprintf(stderr, "Unknown handling of command: %d\n", valid);
    270 		exit(2);
    271 	}
    272 	if (error) {
    273 		fprintf(stderr, "Error: %d\n", error);
    274 		exit(1);
    275 	}
    276 	switch (umcpmioctlcmds[valid].id) {
    277 	case UMCPMIO_GET:
    278 		if (debug) {
    279 			switch (getsubcmds[validsub].id) {
    280 			case UMCPMIO_IOCTL_GET_SRAM:
    281 				buf = (uint8_t *)&get_sram_res;
    282 				break;
    283 			case UMCPMIO_IOCTL_GET_GP_CFG:
    284 				buf = (uint8_t *)&get_gpio_cfg_res;
    285 				break;
    286 			case UMCPMIO_IOCTL_GET_FLASH:
    287 				buf = (uint8_t *)&ioctl_get_flash.get_flash_res.cmd;
    288 				break;
    289 			default:
    290 				fprintf(stderr, "Unhandled subcommand in print for get (debug): %s %d\n\n", argv[2], validsub);
    291 				usage(1);
    292 			}
    293 			for (int i = 0; i < MCP2221_RES_BUFFER_SIZE; i++) {
    294 				printf(" %02x", buf[i]);
    295 			}
    296 			printf("\n");
    297 		}
    298 
    299 		switch (getsubcmds[validsub].id) {
    300 		case UMCPMIO_IOCTL_GET_SRAM:
    301 			print_sram(&get_sram_res);
    302 			break;
    303 		case UMCPMIO_IOCTL_GET_GP_CFG:
    304 			print_gpio_cfg(&get_gpio_cfg_res);
    305 			break;
    306 		case UMCPMIO_IOCTL_GET_FLASH:
    307 			print_flash(&ioctl_get_flash.get_flash_res, getflashsubcmds[validsubsub].id);
    308 			break;
    309 		default:
    310 			fprintf(stderr, "Unhandled subcommand in print for get: %s %d\n\n", argv[2], validsub);
    311 			usage(1);
    312 		}
    313 
    314 		break;
    315 	case UMCPMIO_PUT:
    316 		if (debug) {
    317 			switch (putsubcmds[validsub].id) {
    318 			case UMCPMIO_IOCTL_PUT_FLASH:
    319 				buf = (uint8_t *)&ioctl_put_flash.put_flash_res.cmd;
    320 				break;
    321 			default:
    322 				fprintf(stderr, "Unhandled subcommand in print for put (debug): %s %d\n\n", argv[2], validsub);
    323 				usage(1);
    324 			}
    325 			for (int i = 0; i < MCP2221_RES_BUFFER_SIZE; i++) {
    326 				printf(" %02x", buf[i]);
    327 			}
    328 			printf("\n");
    329 		}
    330 
    331 		if (putsubcmds[validsub].id == UMCPMIO_IOCTL_PUT_FLASH &&
    332 		    putflashsubcmds[validsubsub].id == UMCPMIO_IOCTL_PUT_FLASH_GP) {
    333 			switch (ioctl_put_flash.put_flash_res.completion) {
    334 			case MCP2221_CMD_COMPLETE_NO_SUPPORT:
    335 				printf("Command not supported\n");
    336 				exit(2);
    337 			case MCP2221_CMD_COMPLETE_EPERM:
    338 				printf("Permission denied\n");
    339 				exit(2);
    340 			case MCP2221_CMD_COMPLETE_OK:
    341 			default:
    342 				break;
    343 			}
    344 		} else {
    345 			fprintf(stderr, "Unhandled subcommand in print for put: %s %d %s %d\n\n", argv[2], validsub, argv[3], validsubsub);
    346 			usage(1);
    347 		}
    348 		break;
    349 	case UMCPMIO_STATUS:
    350 		if (debug) {
    351 			buf = &status_res.cmd;
    352 			for (int i = 0; i < MCP2221_RES_BUFFER_SIZE; i++) {
    353 				fprintf(stderr, " %02x", buf[i]);
    354 			}
    355 			fprintf(stderr, "\n");
    356 		}
    357 		print_status(&status_res);
    358 		break;
    359 	default:
    360 		fprintf(stderr, "Unknown printing of command: %d\n", valid);
    361 		exit(2);
    362 	}
    363 
    364 	(void)close(fd);
    365 	exit(0);
    366 }
    367