netloader.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /*
  2. * Network bootloader library
  3. * Could be / is being extended to handle more network based commands
  4. * Uses wiz5500 library
  5. */
  6. // uses wiz5500.c
  7. // Port for network bootloader
  8. #define NETLOADER_PORT 3220
  9. // Socket to listen to (0-7)
  10. #define NETLOADER_SOCKET 0
  11. // Checks if p starts with cmd
  12. // Returns 1 if true, 0 otherwise
  13. word NETLOADER_frameCompare(char* p, char* cmd)
  14. {
  15. word i = 0;
  16. while (cmd[i] != 0)
  17. {
  18. if (cmd[i] != p[i])
  19. {
  20. return 0;
  21. }
  22. i++;
  23. }
  24. return 1;
  25. }
  26. word NETLOADER_wordPosition = 0;
  27. word NETLOADER_currentByteShift = 24;
  28. // Appends bytes from buffer to words in run address
  29. void NETLOADER_appendBufferToRunAddress(char* b, word len)
  30. {
  31. char* dst = (char*) RUN_ADDR;
  32. word i;
  33. for (i = 0; i < len; i++)
  34. {
  35. char readByte = b[i];
  36. // Read 4 bytes into one word, from left to right
  37. readByte = readByte << NETLOADER_currentByteShift;
  38. dst[NETLOADER_wordPosition] = dst[NETLOADER_wordPosition] + readByte;
  39. if (NETLOADER_currentByteShift == 0)
  40. {
  41. NETLOADER_currentByteShift = 24;
  42. NETLOADER_wordPosition++;
  43. dst[NETLOADER_wordPosition] = 0;
  44. }
  45. else
  46. {
  47. NETLOADER_currentByteShift -= 8;
  48. }
  49. }
  50. }
  51. word NETLOADER_getContentLength(char* rbuf, word rsize)
  52. {
  53. word contentLengthStr[32];
  54. word i = 5; // content length always at 5
  55. char c = rbuf[i];
  56. while (i < rsize && c != ':')
  57. {
  58. contentLengthStr[i-5] = c;
  59. i++;
  60. c = rbuf[i];
  61. }
  62. contentLengthStr[i-5] = 0; // terminate
  63. return strToInt(contentLengthStr);
  64. }
  65. void NETLOADER_getFileName(char* rbuf, word rsize, char* fileNameStr)
  66. {
  67. word i = 0;
  68. char c = rbuf[i];
  69. while (i < rsize && c != ':')
  70. {
  71. i++;
  72. c = rbuf[i];
  73. }
  74. i++; // skip the :
  75. word x = 0;
  76. c = rbuf[i];
  77. while (i <rsize && c != '\n')
  78. {
  79. fileNameStr[x] = c;
  80. i++;
  81. x++;
  82. c = rbuf[i];
  83. }
  84. fileNameStr[x] = 0; // terminate
  85. }
  86. word NETLOADER_getContentStart(char* rbuf, word rsize)
  87. {
  88. word i = 5; // content length always at 5
  89. char c = rbuf[i];
  90. while (i < rsize && c != '\n')
  91. {
  92. i++;
  93. c = rbuf[i];
  94. }
  95. return (i+1);
  96. }
  97. void NETLOADER_runProgramFromMemory()
  98. {
  99. BDOS_Backup();
  100. // indicate that a user program is running
  101. BDOS_userprogramRunning = 1;
  102. // jump to the program
  103. asm(
  104. "; backup registers\n"
  105. "push r1\n"
  106. "push r2\n"
  107. "push r3\n"
  108. "push r4\n"
  109. "push r5\n"
  110. "push r6\n"
  111. "push r7\n"
  112. "push r8\n"
  113. "push r9\n"
  114. "push r10\n"
  115. "push r11\n"
  116. "push r12\n"
  117. "push r13\n"
  118. "push r14\n"
  119. "push r15\n"
  120. "savpc r1\n"
  121. "push r1\n"
  122. "jump 0x400000\n"
  123. "; restore registers\n"
  124. "pop r15\n"
  125. "pop r14\n"
  126. "pop r13\n"
  127. "pop r12\n"
  128. "pop r11\n"
  129. "pop r10\n"
  130. "pop r9\n"
  131. "pop r8\n"
  132. "pop r7\n"
  133. "pop r6\n"
  134. "pop r5\n"
  135. "pop r4\n"
  136. "pop r3\n"
  137. "pop r2\n"
  138. "pop r1\n"
  139. );
  140. // indicate that no user program is running anymore
  141. BDOS_userprogramRunning = 0;
  142. // clear the shell
  143. SHELL_clearCommand();
  144. // setup the shell again
  145. BDOS_Restore();
  146. SHELL_print_prompt();
  147. }
  148. word NETLOADER_percentageDone(word remaining, word full)
  149. {
  150. word x = remaining * 100;
  151. return 100 - MATH_divU(x, full);
  152. }
  153. void NETLOADER_handleSession(word s)
  154. {
  155. word firstResponse = 1;
  156. word contentLengthOrig = 0;
  157. word contentLength = 0;
  158. word currentProgress = 0;
  159. word downloadToFile = 0; // whether we need to download to a file instead
  160. char fileNameStr[32];
  161. char dbuf[10]; // percentage done for progress indication
  162. dbuf[0] = 0; // terminate
  163. while (wizGetSockReg8(s, WIZNET_SnSR) == WIZNET_SOCK_ESTABLISHED)
  164. {
  165. word rsize = wizGetSockReg16(s, WIZNET_SnRX_RSR);
  166. if (rsize != 0)
  167. {
  168. char* rbuf = (char *) TEMP_ADDR;
  169. wizReadRecvData(s, rbuf, rsize);
  170. if (firstResponse)
  171. {
  172. GFX_PrintConsole("\n");
  173. // save to and run from memory
  174. if (rbuf[0] == 'E' && rbuf[1] == 'X' && rbuf[2] == 'E' && rbuf[3] == 'C')
  175. {
  176. GFX_PrintConsole("Receiving program\n");
  177. downloadToFile = 0;
  178. // reset position counters
  179. NETLOADER_wordPosition = 0;
  180. NETLOADER_currentByteShift = 24;
  181. // clear first address of run addr
  182. char* dst = (char*) RUN_ADDR;
  183. dst[0] = 0;
  184. }
  185. // save to file
  186. else if (rbuf[0] == 'D' && rbuf[1] == 'O' && rbuf[2] == 'W' && rbuf[3] == 'N')
  187. {
  188. GFX_PrintConsole("Receiving file\n");
  189. downloadToFile = 1;
  190. // get the filename
  191. NETLOADER_getFileName(rbuf, rsize, fileNameStr);
  192. word failedToCreateFile = 1;
  193. // sanity check current path
  194. if (FS_sendFullPath(SHELL_path) == FS_ANSW_USB_INT_SUCCESS)
  195. {
  196. word retval = FS_open();
  197. // check that we can open the path
  198. if (retval == FS_ANSW_USB_INT_SUCCESS || retval == FS_ANSW_ERR_OPEN_DIR)
  199. {
  200. // check length of filename
  201. if (strlen(fileNameStr) <= 12)
  202. {
  203. // uppercase filename
  204. strToUpper(fileNameStr);
  205. // send filename
  206. FS_sendSinglePath(fileNameStr);
  207. // create the file
  208. if (FS_createFile() == FS_ANSW_USB_INT_SUCCESS)
  209. {
  210. // open the path again
  211. FS_sendFullPath(SHELL_path);
  212. FS_open();
  213. // send filename again
  214. FS_sendSinglePath(fileNameStr);
  215. // open the newly created file
  216. if (FS_open() == FS_ANSW_USB_INT_SUCCESS)
  217. {
  218. // set cursor to start
  219. if (FS_setCursor(0) == FS_ANSW_USB_INT_SUCCESS)
  220. {
  221. failedToCreateFile = 0;
  222. }
  223. }
  224. }
  225. }
  226. }
  227. }
  228. if (failedToCreateFile)
  229. {
  230. FS_close();
  231. wizWriteDataFromMemory(s, "ERR!", 4);
  232. wizCmd(s, WIZNET_CR_DISCON);
  233. GFX_PrintConsole("E: Could not create file\n");
  234. // clear the shell
  235. SHELL_clearCommand();
  236. SHELL_print_prompt();
  237. return;
  238. }
  239. }
  240. // unknown command
  241. else
  242. {
  243. // unsupported command
  244. wizWriteDataFromMemory(s, "ERR!", 4);
  245. wizCmd(s, WIZNET_CR_DISCON);
  246. GFX_PrintConsole("E: Unknown netloader cmd\n");
  247. // clear the shell
  248. SHELL_clearCommand();
  249. SHELL_print_prompt();
  250. return;
  251. }
  252. word dataStart = NETLOADER_getContentStart(rbuf, rsize);
  253. contentLength = NETLOADER_getContentLength(rbuf, rsize);
  254. contentLengthOrig = contentLength;
  255. contentLength -= (rsize - dataStart);
  256. if (downloadToFile)
  257. {
  258. FS_writeFile(rbuf+dataStart, rsize - dataStart);
  259. }
  260. else
  261. {
  262. NETLOADER_appendBufferToRunAddress(rbuf+dataStart, rsize - dataStart);
  263. }
  264. firstResponse = 0;
  265. // all data downloaded
  266. if (contentLength == 0)
  267. {
  268. wizWriteDataFromMemory(s, "THX!", 4);
  269. wizCmd(s, WIZNET_CR_DISCON);
  270. if (downloadToFile)
  271. {
  272. FS_close();
  273. // clear the shell
  274. SHELL_clearCommand();
  275. SHELL_print_prompt();
  276. }
  277. else
  278. {
  279. NETLOADER_runProgramFromMemory();
  280. }
  281. return;
  282. }
  283. }
  284. // not the first response
  285. else
  286. {
  287. // indicate progress
  288. // remove previous percentage
  289. word i = strlen(dbuf);
  290. while (i > 0)
  291. {
  292. GFX_PrintcConsole(0x8); // backspace
  293. i--;
  294. }
  295. if (strlen(dbuf) != 0)
  296. {
  297. GFX_PrintcConsole(0x8); // backspace
  298. }
  299. itoa(NETLOADER_percentageDone(contentLength, contentLengthOrig), dbuf);
  300. GFX_PrintConsole(dbuf);
  301. GFX_PrintcConsole('%');
  302. contentLength -= rsize;
  303. if (downloadToFile)
  304. {
  305. FS_writeFile(rbuf, rsize);
  306. }
  307. else
  308. {
  309. NETLOADER_appendBufferToRunAddress(rbuf, rsize);
  310. }
  311. // all data downloaded
  312. if (contentLength == 0)
  313. {
  314. wizWriteDataFromMemory(s, "THX!", 4);
  315. wizCmd(s, WIZNET_CR_DISCON);
  316. // remove progress prints
  317. word i = strlen(dbuf);
  318. while (i > 0)
  319. {
  320. GFX_PrintcConsole(0x8); // backspace
  321. i--;
  322. }
  323. GFX_PrintcConsole(0x8); // backspace
  324. if (downloadToFile)
  325. {
  326. FS_close();
  327. // clear the shell
  328. SHELL_clearCommand();
  329. SHELL_print_prompt();
  330. }
  331. else
  332. {
  333. NETLOADER_runProgramFromMemory();
  334. }
  335. return;
  336. }
  337. }
  338. }
  339. }
  340. }
  341. // Initialize network bootloader on socket s
  342. void NETLOADER_init(word s)
  343. {
  344. // Open socket in TCP Server mode
  345. wizInitSocketTCP(s, NETLOADER_PORT);
  346. }
  347. // Check for a change in the socket status
  348. // Handles change if exists
  349. word NETLOADER_loop(word s)
  350. {
  351. // Get status for socket s
  352. word sxStatus = wizGetSockReg8(s, WIZNET_SnSR);
  353. if (sxStatus == WIZNET_SOCK_CLOSED)
  354. {
  355. // Open the socket when closed
  356. wizInitSocketTCP(s, NETLOADER_PORT);
  357. }
  358. else if (sxStatus == WIZNET_SOCK_ESTABLISHED)
  359. {
  360. // Handle session when a connection is established
  361. NETLOADER_handleSession(s);
  362. }
  363. else if (sxStatus == WIZNET_SOCK_LISTEN)
  364. {
  365. // Keep on listening
  366. return 1;
  367. }
  368. else if (sxStatus == WIZNET_SOCK_SYNSENT || sxStatus == WIZNET_SOCK_SYNRECV || sxStatus == WIZNET_SOCK_FIN_WAIT || sxStatus == WIZNET_SOCK_TIME_WAIT)
  369. {
  370. // Do nothing in these cases
  371. return 2;
  372. }
  373. else
  374. {
  375. // In other cases, reset the socket
  376. wizInitSocketTCP(s, NETLOADER_PORT);
  377. }
  378. return 0;
  379. }