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