You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

636 lines
16KB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <sys/socket.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>
  9. #include <termios.h>
  10. #include <sys/time.h>
  11. #include "chipinfo_avr.h"
  12. #include "multiboot.h"
  13. #include "optarg.h"
  14. #define READ_BLOCK_SIZE 256 /* bytes in one flash/eeprom read request */
  15. #define WRITE_BLOCK_SIZE 16 /* bytes in one eeprom write request */
  16. #define CMD_SWITCH_APPLICATION 0x01
  17. #define CMD_GET_BOOTLOADER_VERSION 0x02
  18. #define CMD_GET_CHIP_INFO 0x03
  19. #define CMD_READ_MEMORY 0x11
  20. #define CMD_WRITE_MEMORY 0x12
  21. #define CAUSE_SUCCESS 0x00
  22. #define CAUSE_COMMAND_NOT_SUPPORTED 0xF0
  23. #define CAUSE_INVALID_PARAMETER 0xF1
  24. #define CAUSE_UNSPECIFIED_ERROR 0xFF
  25. /* CMD_SWITCH_APPLICATION parameter */
  26. #define BOOTTYPE_BOOTLOADER 0x00
  27. #define BOOTTYPE_APPLICATION 0x80
  28. #define MEMTYPE_FLASH 0x01
  29. #define MEMTYPE_EEPROM 0x02
  30. #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
  31. #define MIN(a, b) ((a) < (b) ? (a) : (b))
  32. struct multiboot_ops mpm_ops;
  33. struct mpm_privdata {
  34. char *device;
  35. int fd;
  36. int connected;
  37. int address;
  38. int flashsize;
  39. int flashpage;
  40. int eepromsize;
  41. struct termios oldtio;
  42. };
  43. static struct option mpm_optargs[] = {
  44. {"address", 1, 0, 'a'}, /* -a <addr> */
  45. {"device", 1, 0, 'd'}, /* [ -d <device> ] */
  46. };
  47. static int mpm_optarg_cb(int val, const char *arg, void *privdata)
  48. {
  49. struct mpm_privdata *mpm = (struct mpm_privdata *)privdata;
  50. switch (val) {
  51. case 'a': /* address */
  52. {
  53. char *endptr;
  54. mpm->address = strtol(arg, &endptr, 16);
  55. if (*endptr != '\0' || mpm->address < 0x01 || mpm->address > 0x7F) {
  56. fprintf(stderr, "invalid address: '%s'\n", arg);
  57. return -1;
  58. }
  59. }
  60. break;
  61. case 'd': /* device */
  62. {
  63. if (mpm->device != NULL) {
  64. fprintf(stderr, "invalid device: '%s'\n", optarg);
  65. return -1;
  66. }
  67. mpm->device = strdup(optarg);
  68. if (mpm->device == NULL) {
  69. perror("strdup()");
  70. return -1;
  71. }
  72. }
  73. break;
  74. case 'h':
  75. case '?': /* error */
  76. fprintf(stderr, "Usage: mpmboot [options]\n"
  77. " -a <address> - selects mpm address (0x01 - 0xFF)\n"
  78. " -d <device> - selects mpm device\n"
  79. " -r <flash|eeprom>:<file> - reads flash/eeprom to file (.bin | .hex | -)\n"
  80. " -w <flash|eeprom>:<file> - write flash/eeprom from file (.bin | .hex)\n"
  81. " -n - disable verify after write\n"
  82. " -p <0|1|2> - progress bar mode\n"
  83. "\n"
  84. "Example: mpmboot -d /dev/ttyUSB0 -a 0x22 -w flash:blmc.hex -w flash:blmc_eeprom.hex\n"
  85. "\n");
  86. return -1;
  87. default:
  88. return 1;
  89. }
  90. return 0;
  91. }
  92. static struct multiboot * mpm_alloc(void)
  93. {
  94. struct multiboot * mboot = malloc(sizeof(struct multiboot));
  95. if (mboot == NULL)
  96. return NULL;
  97. memset(mboot, 0x00, sizeof(struct multiboot));
  98. mboot->ops = &mpm_ops;
  99. struct mpm_privdata *mpm = malloc(sizeof(struct mpm_privdata));
  100. if (mpm == NULL) {
  101. free(mboot);
  102. return NULL;
  103. }
  104. memset(mpm, 0x00, sizeof(struct mpm_privdata));
  105. mpm->device = NULL;
  106. mpm->address = 0;
  107. optarg_register(mpm_optargs, ARRAY_SIZE(mpm_optargs), mpm_optarg_cb, (void *)mpm);
  108. mboot->privdata = mpm;
  109. return mboot;
  110. }
  111. static void mpm_free(struct multiboot *mboot)
  112. {
  113. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  114. if (mpm->device != NULL)
  115. free(mpm->device);
  116. free(mpm);
  117. free(mboot);
  118. }
  119. static int mpm_get_memtype(struct multiboot *mboot, const char *memname)
  120. {
  121. if (strcmp(memname, "flash") == 0)
  122. return MEMTYPE_FLASH;
  123. else if (strcmp(memname, "eeprom") == 0)
  124. return MEMTYPE_EEPROM;
  125. return -1;
  126. }
  127. static int mpm_get_memsize(struct multiboot *mboot, int memtype)
  128. {
  129. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  130. if (!mpm->connected)
  131. return 0;
  132. switch (memtype) {
  133. case MEMTYPE_FLASH:
  134. return mpm->flashsize;
  135. case MEMTYPE_EEPROM:
  136. return mpm->eepromsize;
  137. default:
  138. return 0;
  139. }
  140. }
  141. static int mpm_send(struct mpm_privdata *mpm, uint8_t command, uint8_t *data, int length)
  142. {
  143. struct termios tio;
  144. if (tcgetattr(mpm->fd, &tio) < 0) {
  145. perror("tcgetattr(tio)");
  146. return -1;
  147. }
  148. tio.c_cflag |= PARODD;
  149. if (tcsetattr(mpm->fd, TCSAFLUSH, &tio) < 0) {
  150. perror("tcsetattr(tio)");
  151. return -1;
  152. }
  153. // usleep(5000);
  154. uint8_t address = mpm->address;
  155. if (write(mpm->fd, &address, sizeof(address)) != sizeof(address)) {
  156. perror("write(address)");
  157. return -1;
  158. }
  159. usleep(500);
  160. tio.c_cflag &= ~(PARODD);
  161. if (tcsetattr(mpm->fd, TCSAFLUSH, &tio) < 0) {
  162. perror("tcsetattr(tio)");
  163. return -1;
  164. }
  165. uint8_t header[3];
  166. header[0] = command;
  167. header[1] = (length >> 8) & 0xFF;
  168. header[2] = length & 0xFF;
  169. if (write(mpm->fd, header, sizeof(header)) != sizeof(header)) {
  170. perror("write(header)");
  171. return -1;
  172. }
  173. if (data != NULL && length != 0) {
  174. if (write(mpm->fd, data, length) != length) {
  175. perror("write(data)");
  176. return -1;
  177. }
  178. }
  179. return 0;
  180. }
  181. static int myread(int fd, void *data, int size)
  182. {
  183. int pos = 0;
  184. while (1) {
  185. fd_set fdset;
  186. struct timeval timeout = { .tv_sec = 1, .tv_usec = 0 };
  187. FD_ZERO(&fdset);
  188. FD_SET(fd, &fdset);
  189. int ret = select(fd +1, &fdset, NULL, NULL, &timeout);
  190. if (ret == -1) {
  191. perror("select");
  192. return -1;
  193. } else if (ret == 0) {
  194. break;
  195. } else if (FD_ISSET(fd, &fdset)) {
  196. int len = read(fd, data + pos, size - pos);
  197. if (len < 0) {
  198. return -1;
  199. } else {
  200. pos += len;
  201. if (pos == size) {
  202. break;
  203. }
  204. }
  205. }
  206. }
  207. return pos;
  208. }
  209. static int mpm_recv(struct mpm_privdata *mpm, uint8_t command, uint8_t *cause, uint8_t *buffer, uint16_t buffersize)
  210. {
  211. int len;
  212. uint8_t header[4];
  213. len = myread(mpm->fd, header, sizeof(header));
  214. if (len != sizeof(header)) {
  215. fprintf(stderr, "short read() from device (not addressed?)\n");
  216. return -1;
  217. }
  218. if (header[0] != command) {
  219. fprintf(stderr, "invalid command response (0x%02x != 0x%02x)\n", header[0], command);
  220. return -1;
  221. }
  222. *cause = header[1];
  223. uint16_t length = (header[2] << 8) | header[3];
  224. // printf("mpm_recv() cmd=0x%02x cause=0x%02x length=0x%04x\n", command, *cause, length);
  225. uint16_t bufferpos = 0;
  226. while (length > 0) {
  227. /* free space in output buffer? */
  228. if ((bufferpos < buffersize) && (buffer != NULL)) {
  229. uint16_t size = MIN(buffersize - bufferpos, length);
  230. len = myread(mpm->fd, buffer + bufferpos, size);
  231. if (len <= 0) {
  232. fprintf(stderr, "short read() from device (%d != %d)\n", len, size);
  233. return -1;
  234. }
  235. bufferpos += len;
  236. length -= len;
  237. } else {
  238. uint8_t dummy[256];
  239. /* no space in output buffer, but device still sends data -> do dummy read */
  240. uint16_t size = MIN(sizeof(dummy), length);
  241. len = myread(mpm->fd, dummy, size);
  242. if (len <= 0) {
  243. fprintf(stderr, "short read() from device (%d != %d)\n", len, size);
  244. return -1;
  245. }
  246. length -= len;
  247. }
  248. }
  249. return bufferpos;
  250. }
  251. static void mpm_close_device(struct mpm_privdata *mpm)
  252. {
  253. /* delay close() / tcsetattr() */
  254. usleep(100000);
  255. tcsetattr(mpm->fd, TCSANOW, &mpm->oldtio);
  256. close(mpm->fd);
  257. }
  258. static int mpm_open_device(struct mpm_privdata *mpm)
  259. {
  260. mpm->fd = open(mpm->device, O_RDWR | O_NOCTTY | O_CLOEXEC);
  261. if (mpm->fd < 0) {
  262. perror("open()");
  263. return -1;
  264. }
  265. if (tcgetattr(mpm->fd, &mpm->oldtio) < 0) {
  266. perror("tcgetattr(oldtio)");
  267. close(mpm->fd);
  268. return -1;
  269. }
  270. struct termios newtio;
  271. memset(&newtio, 0, sizeof(newtio));
  272. newtio.c_iflag |= IGNBRK;
  273. newtio.c_cflag |= B115200 | CS8 | CLOCAL | CREAD | PARENB | CMSPAR;
  274. newtio.c_cc[VMIN] = 1;
  275. newtio.c_cc[VTIME] = 0;
  276. int err = tcsetattr(mpm->fd, TCSANOW, &newtio);
  277. if (err < 0) {
  278. perror("tcsetattr(newtio)");
  279. close(mpm->fd);
  280. return -1;
  281. }
  282. mpm->connected = 1;
  283. return 0;
  284. }
  285. static int mpm_switch_application(struct mpm_privdata *mpm, uint8_t application)
  286. {
  287. uint8_t data[] = { application };
  288. int ret = mpm_send(mpm, CMD_SWITCH_APPLICATION, data, sizeof(data));
  289. if (ret < 0)
  290. return ret;
  291. uint8_t cause = CAUSE_SUCCESS;
  292. ret = mpm_recv(mpm, CMD_SWITCH_APPLICATION, &cause, NULL, 0);
  293. if (ret < 0)
  294. return ret;
  295. return (cause != CAUSE_SUCCESS);
  296. }
  297. static int mpm_read_version(struct mpm_privdata *mpm, uint8_t *version, uint16_t length)
  298. {
  299. memset(version, 0, length);
  300. int ret = mpm_send(mpm, CMD_GET_BOOTLOADER_VERSION, NULL, 0);
  301. if (ret < 0)
  302. return ret;
  303. uint8_t cause = CAUSE_SUCCESS;
  304. ret = mpm_recv(mpm, CMD_GET_BOOTLOADER_VERSION, &cause, version, length);
  305. if (ret < 0)
  306. return ret;
  307. int i;
  308. for (i = 0; i < length; i++)
  309. version[i] &= ~0x80;
  310. return (cause != CAUSE_SUCCESS);
  311. }
  312. static int mpm_read_chipinfo(struct mpm_privdata *mpm, uint8_t *chipinfo, uint16_t length)
  313. {
  314. int ret = mpm_send(mpm, CMD_GET_CHIP_INFO, NULL, 0);
  315. if (ret < 0)
  316. return ret;
  317. uint8_t cause = CAUSE_SUCCESS;
  318. ret = mpm_recv(mpm, CMD_GET_CHIP_INFO, &cause, chipinfo, length);
  319. if (ret < 0)
  320. return ret;
  321. return (cause != CAUSE_SUCCESS);
  322. }
  323. static int mpm_read_memory(struct mpm_privdata *mpm, uint8_t *buffer, uint16_t size, uint8_t memtype, uint16_t address)
  324. {
  325. uint8_t param[5] = {
  326. memtype,
  327. (address >> 8) & 0xFF,
  328. (address & 0xFF),
  329. (size >> 8) & 0xFF,
  330. (size & 0xFF)
  331. };
  332. int ret = mpm_send(mpm, CMD_READ_MEMORY, param, sizeof(param));
  333. if (ret < 0)
  334. return ret;
  335. uint8_t cause = CAUSE_SUCCESS;
  336. ret = mpm_recv(mpm, CMD_READ_MEMORY, &cause, buffer, size);
  337. if (ret < 0)
  338. return ret;
  339. return (cause != CAUSE_SUCCESS);
  340. }
  341. static int mpm_write_memory(struct mpm_privdata *mpm, uint8_t *buffer, uint16_t size, uint8_t memtype, uint16_t address)
  342. {
  343. int bufsize;
  344. if (memtype == MEMTYPE_FLASH) {
  345. if ((address & (mpm->flashpage -1)) != 0x00) {
  346. fprintf(stderr, "mpm_write_memory(): address 0x%04x not aligned to pagesize 0x%02x\n", address, mpm->flashpage);
  347. return -1;
  348. }
  349. bufsize = 5 + mpm->flashpage;
  350. } else {
  351. bufsize = 5 + size;
  352. }
  353. uint8_t *cmd = malloc(bufsize);
  354. if (cmd == NULL)
  355. return -1;
  356. cmd[0] = memtype;
  357. cmd[1] = (address >> 8) & 0xFF;
  358. cmd[2] = (address & 0xFF);
  359. cmd[3] = ((bufsize -5) >> 8) & 0xFF;
  360. cmd[4] = ((bufsize -5) & 0xFF);
  361. memcpy(cmd +5, buffer, size);
  362. if (memtype == MEMTYPE_FLASH) {
  363. memset(cmd +5 +size, 0xFF, mpm->flashpage - size);
  364. }
  365. int ret = mpm_send(mpm, CMD_WRITE_MEMORY, cmd, bufsize);
  366. if (ret < 0)
  367. return ret;
  368. free(cmd);
  369. uint8_t cause = CAUSE_SUCCESS;
  370. ret = mpm_recv(mpm, CMD_WRITE_MEMORY, &cause, NULL, 0);
  371. if (ret < 0)
  372. return ret;
  373. return (cause != CAUSE_SUCCESS);
  374. }
  375. static int mpm_close(struct multiboot *mboot)
  376. {
  377. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  378. if (mpm->connected)
  379. mpm_switch_application(mpm, BOOTTYPE_APPLICATION);
  380. mpm_close_device(mpm);
  381. return 0;
  382. }
  383. static int mpm_open(struct multiboot *mboot)
  384. {
  385. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  386. if (mpm->address == 0) {
  387. fprintf(stderr, "abort: no address given\n");
  388. return -1;
  389. }
  390. if (mpm->device == NULL) {
  391. fprintf(stderr, "abort: no device given\n");
  392. return -1;
  393. }
  394. if (mpm_open_device(mpm) < 0)
  395. return -1;
  396. if (mpm_switch_application(mpm, BOOTTYPE_BOOTLOADER)) {
  397. fprintf(stderr, "failed to switch to bootloader (invalid address?)\n");
  398. mpm_close(mboot);
  399. return -1;
  400. }
  401. /* wait for watchdog and startup time */
  402. usleep(100000);
  403. char version[16];
  404. if (mpm_read_version(mpm, (uint8_t *)version, sizeof(version))) {
  405. fprintf(stderr, "failed to get bootloader version");
  406. mpm_close(mboot);
  407. return -1;
  408. }
  409. uint8_t chipinfo[8];
  410. if (mpm_read_chipinfo(mpm, chipinfo, sizeof(chipinfo))) {
  411. fprintf(stderr, "failed to get bootloader version");
  412. mpm_close(mboot);
  413. return -1;
  414. }
  415. const char *chipname = chipinfo_get_avr_name(chipinfo);
  416. mpm->flashpage = chipinfo[3];
  417. mpm->flashsize = (chipinfo[4] << 8) + chipinfo[5];
  418. mpm->eepromsize = (chipinfo[6] << 8) + chipinfo[7];
  419. printf("device : %-16s (address: 0x%02X)\n", mpm->device, mpm->address);
  420. printf("version : %-16s (sig: 0x%02x 0x%02x 0x%02x => %s)\n", version, chipinfo[0], chipinfo[1], chipinfo[2], chipname);
  421. printf("flash size : 0x%04x / %5d (0x%02x bytes/page)\n", mpm->flashsize, mpm->flashsize, mpm->flashpage);
  422. printf("eeprom size : 0x%04x / %5d\n", mpm->eepromsize, mpm->eepromsize);
  423. return 0;
  424. }
  425. static int mpm_read(struct multiboot *mboot, struct databuf *dbuf, int memtype)
  426. {
  427. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  428. char *progress_msg = (memtype == MEMTYPE_FLASH) ? "reading flash" : "reading eeprom";
  429. int pos = 0;
  430. int size = (memtype == MEMTYPE_FLASH) ? mpm->flashsize : mpm->eepromsize;
  431. while (pos < size) {
  432. mboot->progress_cb(progress_msg, pos, size);
  433. int len = MIN(READ_BLOCK_SIZE, size - pos);
  434. if (mpm_read_memory(mpm, dbuf->data + pos, len, memtype, pos)) {
  435. mboot->progress_cb(progress_msg, -1, -1);
  436. return -1;
  437. }
  438. pos += len;
  439. }
  440. dbuf->length = pos;
  441. mboot->progress_cb(progress_msg, pos, size);
  442. return 0;
  443. }
  444. static int mpm_write(struct multiboot *mboot, struct databuf *dbuf, int memtype)
  445. {
  446. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  447. char *progress_msg = (memtype == MEMTYPE_FLASH) ? "writing flash" : "writing eeprom";
  448. int pos = 0;
  449. while (pos < dbuf->length) {
  450. mboot->progress_cb(progress_msg, pos, dbuf->length);
  451. int len = (memtype == MEMTYPE_FLASH) ? mpm->flashpage : WRITE_BLOCK_SIZE;
  452. len = MIN(len, dbuf->length - pos);
  453. if (mpm_write_memory(mpm, dbuf->data + pos, len, memtype, pos)) {
  454. mboot->progress_cb(progress_msg, -1, -1);
  455. return -1;
  456. }
  457. pos += len;
  458. }
  459. mboot->progress_cb(progress_msg, pos, dbuf->length);
  460. return 0;
  461. }
  462. static int mpm_verify(struct multiboot *mboot, struct databuf *dbuf, int memtype)
  463. {
  464. struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata;
  465. char *progress_msg = (memtype == MEMTYPE_FLASH) ? "verifing flash" : "verifing eeprom";
  466. int pos = 0;
  467. uint8_t comp[READ_BLOCK_SIZE];
  468. while (pos < dbuf->length) {
  469. mboot->progress_cb(progress_msg, pos, dbuf->length);
  470. int len = MIN(READ_BLOCK_SIZE, dbuf->length - pos);
  471. if (mpm_read_memory(mpm, comp, len, memtype, pos)) {
  472. mboot->progress_cb(progress_msg, -1, -1);
  473. return -1;
  474. }
  475. if (memcmp(comp, dbuf->data + pos, len) != 0x00) {
  476. mboot->progress_cb(progress_msg, -1, -1);
  477. fprintf(stderr, "verify failed at page 0x%04x!!\n", pos);
  478. return -1;
  479. }
  480. pos += len;
  481. }
  482. dbuf->length = pos;
  483. mboot->progress_cb(progress_msg, pos, dbuf->length);
  484. return 0;
  485. }
  486. struct multiboot_ops mpm_ops = {
  487. .alloc = mpm_alloc,
  488. .free = mpm_free,
  489. .get_memtype = mpm_get_memtype,
  490. .get_memsize = mpm_get_memsize,
  491. .open = mpm_open,
  492. .close = mpm_close,
  493. .read = mpm_read,
  494. .write = mpm_write,
  495. .verify = mpm_verify,
  496. };