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