blob: 75c5d94a47edadcb9c72aa3a383bef8a1278d1da [file] [log] [blame]
Nico Huber1f693db2023-02-11 18:28:33 +01001/*
2 * This file is part of the flashprog project.
3 *
Nico Huber9512c9c2025-01-30 22:38:18 +01004 * Copyright (C) 2023 Nico Huber <nico.h@gmx.de>
5 *
Nico Huber1f693db2023-02-11 18:28:33 +01006 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <stdio.h>
18#include <stdint.h>
19#include <stdlib.h>
Nico Huber8f7122c2023-02-11 18:28:33 +010020#include <stdbool.h>
Nico Huber1f693db2023-02-11 18:28:33 +010021#include <string.h>
22#include <getopt.h>
23#include <limits.h>
24#include <unistd.h>
25
26#include "libflashprog.h"
27#include "chipdrivers.h"
28#include "flash.h"
29#include "cli.h"
30
31enum settings {
32 QUAD_ENABLE,
33};
34
35static const struct reg_bit_info *get_bit_info(
36 const struct flashctx *flash, enum settings setting)
37{
38 switch (setting) {
39 case QUAD_ENABLE:
40 return &flash->chip->reg_bits.qe;
41 default:
42 return NULL;
43 }
44}
45
46static int config_get(const struct flashctx *flash, enum settings setting)
47{
48 const struct reg_bit_info *const bit = get_bit_info(flash, setting);
49 uint8_t reg_val;
50
51 if (!bit)
52 return 1;
53
54 const int ret = spi_read_register(flash, bit->reg, &reg_val);
55 if (ret)
56 return 1;
57
58 printf("%u\n", reg_val >> bit->bit_index & 1);
59 return 0;
60}
61
62static int config_set(const struct flashctx *flash, enum settings setting, unsigned int value)
63{
64 const struct reg_bit_info *const bit = get_bit_info(flash, setting);
65 uint8_t reg_val;
66 int ret;
67
68 if (!bit)
69 return 1;
70
71 ret = spi_read_register(flash, bit->reg, &reg_val);
72 if (ret)
73 return 1;
74
75 reg_val &= ~(1 << bit->bit_index);
76 reg_val |= (value & 1) << bit->bit_index;
77
78 ret = spi_write_register(flash, bit->reg, reg_val, default_wrsr_target(flash));
79 if (ret)
80 return 1;
81
82 return 0;
83}
84
85static void usage(const char *const name, const char *const msg)
86{
87 if (msg)
88 fprintf(stderr, "\nError: %s\n", msg);
89
90 fprintf(stderr, "\nUsage:"
91 "\t%s [get] <options> <setting>\n"
92 "\t%s set <options> [--temporary] <setting> <value>\n",
93 name, name);
Nico Huber8f7122c2023-02-11 18:28:33 +010094 print_generic_options(/* layout_options =>*/false);
Nico Huber1f693db2023-02-11 18:28:33 +010095 fprintf(stderr, "\n<setting> can be\n"
96 " qe | quad-enable Quad-Enable (QE) bit\n"
97 "\nand <value> can be `true', `false', or a number.\n"
98 "\nBy default, the setting is queried (`get').\n"
99 "\n");
100 exit(1);
101}
102
103static int parse_setting(const char *const setting)
104{
105 if (!strcmp(setting, "qe") ||
106 !strcmp(setting, "quad-enable"))
107 return QUAD_ENABLE;
108 return -1;
109}
110
111static int parse_value(const char *const value)
112{
113 if (!strcmp(value, "true"))
114 return 1;
115 if (!strcmp(value, "false"))
116 return 0;
117
118 char *endptr;
119 const unsigned long i = strtoul(value, &endptr, 0);
120 if (value[0] && !endptr[0] && i <= INT_MAX)
121 return i;
122
123 return -1;
124}
125
126int flashprog_config_main(int argc, char *argv[])
127{
128 static const char optstring[] = "+p:c:Vo:h";
129 static const struct option long_options[] = {
130 {"programmer", 1, NULL, 'p'},
131 {"chip", 1, NULL, 'c'},
132 {"verbose", 0, NULL, 'V'},
133 {"output", 1, NULL, 'o'},
134 {"help", 0, NULL, 'h'},
135 {"get", 0, NULL, OPTION_CONFIG_GET},
136 {"set", 0, NULL, OPTION_CONFIG_SET},
137 {"temporary", 0, NULL, OPTION_CONFIG_VOLATILE},
138 {NULL, 0, NULL, 0},
139 };
140 static const struct opt_command cmd_options[] = {
141 {"get", OPTION_CONFIG_GET},
142 {"set", OPTION_CONFIG_SET},
143 {NULL, 0},
144 };
145
146 unsigned int ops = 0;
147 int ret = 1, opt;
148 struct log_args log_args = { FLASHPROG_MSG_INFO, FLASHPROG_MSG_DEBUG2, NULL };
149 struct flash_args flash_args = { 0 };
150 bool get = false, set = false, volat1le = false;
151
152 if (cli_init()) /* TODO: Can be moved below argument parsing once usage() uses `stderr` directly. */
153 goto free_ret;
154
155 if (argc < 2)
156 usage(argv[0], NULL);
157
158 while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) != -1 ||
159 (opt = getopt_command(argc, argv, cmd_options)) != -1) {
160 switch (opt) {
161 case 'V':
162 case 'o':
163 ret = cli_parse_log_args(&log_args, opt, optarg);
164 if (ret == 1)
165 usage(argv[0], NULL);
166 else if (ret)
167 goto free_ret;
168 break;
169 case 'p':
170 case 'c':
171 ret = cli_parse_flash_args(&flash_args, opt, optarg);
172 if (ret == 1)
173 usage(argv[0], NULL);
174 else if (ret)
175 goto free_ret;
176 break;
177 case OPTION_CONFIG_GET:
178 get = true;
179 ++ops;
180 break;
181 case OPTION_CONFIG_SET:
182 set = true;
183 ++ops;
184 break;
185 case OPTION_CONFIG_VOLATILE:
186 volat1le = true;
187 break;
188 case '?':
189 case 'h':
190 usage(argv[0], NULL);
191 break;
192 }
193 }
194
195 if (!ops) {
196 get = true;
197 ++ops;
198 }
199
200 if (ops > 1)
201 usage(argv[0], "Only one operation may be specified.");
202
203 if (!set && volat1le)
204 usage(argv[0], "`--temporary' may only be specified for `set'.");
205 if (get && optind != argc - 1)
206 usage(argv[0], "`get' requires exactly one argument.");
207 if (set && optind != argc - 2)
208 usage(argv[0], "`set' requires exactly two arguments.");
209
210 if (!flash_args.prog_name)
211 usage(argv[0], "No programmer specified.");
212
213 const int setting = parse_setting(argv[optind]);
214 if (setting < 0) {
215 fprintf(stderr, "\nError: Unknown <setting> argument `%s'.\n", argv[optind]);
216 usage(argv[0], NULL);
217 }
218 int value = 0;
219 if (set) {
220 value = parse_value(argv[optind + 1]);
221 if (value < 0) {
222 fprintf(stderr, "\nError: Cannot parse value `%s'.\n", argv[optind + 1]);
223 usage(argv[0], NULL);
224 }
225 }
226
227 struct flashprog_programmer *prog;
228 struct flashprog_flashctx *flash;
229 ret = 1;
230
231 if (log_args.logfile && open_logfile(log_args.logfile))
232 goto free_ret;
233 verbose_screen = log_args.screen_level;
234 verbose_logfile = log_args.logfile_level;
235 start_logging();
236
237 if (flashprog_programmer_init(&prog, flash_args.prog_name, flash_args.prog_args))
238 goto free_ret;
239 if (flashprog_flash_probe(&flash, prog, flash_args.chip)) {
240 fprintf(stderr, "No EEPROM/flash device found.\n");
241 goto shutdown_ret;
242 }
243
244 if (flash->chip->bustype != BUS_SPI || flash->chip->spi_cmd_set != SPI25) {
245 fprintf(stderr, "Only SPI25 flash chips are supported.\n");
246 goto shutdown_ret;
247 }
248
249 flashprog_flag_set(flash, FLASHPROG_FLAG_NON_VOLATILE_WRSR, set && !volat1le);
250
251 if (get)
252 ret = config_get(flash, setting);
253
254 if (set)
255 ret = config_set(flash, setting, value);
256
257 flashprog_flash_release(flash);
258shutdown_ret:
259 flashprog_programmer_shutdown(prog);
260free_ret:
261 free(flash_args.chip);
262 free(flash_args.prog_args);
263 free(flash_args.prog_name);
264 free(log_args.logfile);
265 close_logfile();
266 return ret;
267}