netloader.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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. //"ccache\n"
  121. "savpc r1\n"
  122. "push r1\n"
  123. "jump 0x400000\n"
  124. "; restore registers\n"
  125. "pop r15\n"
  126. "pop r14\n"
  127. "pop r13\n"
  128. "pop r12\n"
  129. "pop r11\n"
  130. "pop r10\n"
  131. "pop r9\n"
  132. "pop r8\n"
  133. "pop r7\n"
  134. "pop r6\n"
  135. "pop r5\n"
  136. "pop r4\n"
  137. "pop r3\n"
  138. "pop r2\n"
  139. "pop r1\n"
  140. );
  141. // indicate that no user program is running anymore
  142. BDOS_userprogramRunning = 0;
  143. // clear the shell
  144. SHELL_clearCommand();
  145. // setup the shell again
  146. BDOS_Restore();
  147. SHELL_print_prompt();
  148. }
  149. word NETLOADER_percentageDone(word remaining, word full)
  150. {
  151. word x = remaining * 100;
  152. return 100 - MATH_divU(x, full);
  153. }
  154. void NETLOADER_handleSession(word s)
  155. {
  156. word firstResponse = 1;
  157. word contentLengthOrig = 0;
  158. word contentLength = 0;
  159. word currentProgress = 0;
  160. word downloadToFile = 0; // whether we need to download to a file instead
  161. char fileNameStr[32];
  162. char dbuf[10]; // percentage done for progress indication
  163. dbuf[0] = 0; // terminate
  164. while (wizGetSockReg8(s, WIZNET_SnSR) == WIZNET_SOCK_ESTABLISHED)
  165. {
  166. word rsize = wizGetSockReg16(s, WIZNET_SnRX_RSR);
  167. if (rsize != 0)
  168. {
  169. char* rbuf = (char *) TEMP_ADDR;
  170. wizReadRecvData(s, rbuf, rsize);
  171. if (firstResponse)
  172. {
  173. GFX_PrintConsole("\n");
  174. // save to and run from memory
  175. if (rbuf[0] == 'E' && rbuf[1] == 'X' && rbuf[2] == 'E' && rbuf[3] == 'C')
  176. {
  177. GFX_PrintConsole("Receiving program\n");
  178. downloadToFile = 0;
  179. // reset position counters
  180. NETLOADER_wordPosition = 0;
  181. NETLOADER_currentByteShift = 24;
  182. // clear first address of run addr
  183. char* dst = (char*) RUN_ADDR;
  184. dst[0] = 0;
  185. }
  186. // save to file
  187. else if (rbuf[0] == 'D' && rbuf[1] == 'O' && rbuf[2] == 'W' && rbuf[3] == 'N')
  188. {
  189. GFX_PrintConsole("Receiving file\n");
  190. downloadToFile = 1;
  191. // get the filename
  192. NETLOADER_getFileName(rbuf, rsize, fileNameStr);
  193. word failedToCreateFile = 1;
  194. // sanity check current path
  195. if (FS_sendFullPath(SHELL_path) == FS_ANSW_USB_INT_SUCCESS)
  196. {
  197. word retval = FS_open();
  198. // check that we can open the path
  199. if (retval == FS_ANSW_USB_INT_SUCCESS || retval == FS_ANSW_ERR_OPEN_DIR)
  200. {
  201. // check length of filename
  202. if (strlen(fileNameStr) <= 12)
  203. {
  204. // uppercase filename
  205. strToUpper(fileNameStr);
  206. // send filename
  207. FS_sendSinglePath(fileNameStr);
  208. // create the file
  209. if (FS_createFile() == FS_ANSW_USB_INT_SUCCESS)
  210. {
  211. // open the path again
  212. FS_sendFullPath(SHELL_path);
  213. FS_open();
  214. // send filename again
  215. FS_sendSinglePath(fileNameStr);
  216. // open the newly created file
  217. if (FS_open() == FS_ANSW_USB_INT_SUCCESS)
  218. {
  219. // set cursor to start
  220. if (FS_setCursor(0) == FS_ANSW_USB_INT_SUCCESS)
  221. {
  222. failedToCreateFile = 0;
  223. }
  224. }
  225. }
  226. }
  227. }
  228. }
  229. if (failedToCreateFile)
  230. {
  231. FS_close();
  232. wizWriteDataFromMemory(s, "ERR!", 4);
  233. wizCmd(s, WIZNET_CR_DISCON);
  234. GFX_PrintConsole("E: Could not create file\n");
  235. // clear the shell
  236. SHELL_clearCommand();
  237. SHELL_print_prompt();
  238. return;
  239. }
  240. }
  241. // unknown command
  242. else
  243. {
  244. // unsupported command
  245. wizWriteDataFromMemory(s, "ERR!", 4);
  246. wizCmd(s, WIZNET_CR_DISCON);
  247. GFX_PrintConsole("E: Unknown netloader cmd\n");
  248. // clear the shell
  249. SHELL_clearCommand();
  250. SHELL_print_prompt();
  251. return;
  252. }
  253. word dataStart = NETLOADER_getContentStart(rbuf, rsize);
  254. contentLength = NETLOADER_getContentLength(rbuf, rsize);
  255. contentLengthOrig = contentLength;
  256. contentLength -= (rsize - dataStart);
  257. if (downloadToFile)
  258. {
  259. FS_writeFile(rbuf+dataStart, rsize - dataStart);
  260. }
  261. else
  262. {
  263. NETLOADER_appendBufferToRunAddress(rbuf+dataStart, rsize - dataStart);
  264. }
  265. firstResponse = 0;
  266. // all data downloaded
  267. if (contentLength == 0)
  268. {
  269. wizWriteDataFromMemory(s, "THX!", 4);
  270. wizCmd(s, WIZNET_CR_DISCON);
  271. if (downloadToFile)
  272. {
  273. FS_close();
  274. // clear the shell
  275. SHELL_clearCommand();
  276. SHELL_print_prompt();
  277. }
  278. else
  279. {
  280. NETLOADER_runProgramFromMemory();
  281. }
  282. return;
  283. }
  284. }
  285. // not the first response
  286. else
  287. {
  288. // indicate progress
  289. // remove previous percentage
  290. word i = strlen(dbuf);
  291. while (i > 0)
  292. {
  293. GFX_PrintcConsole(0x8); // backspace
  294. i--;
  295. }
  296. if (strlen(dbuf) != 0)
  297. {
  298. GFX_PrintcConsole(0x8); // backspace
  299. }
  300. itoa(NETLOADER_percentageDone(contentLength, contentLengthOrig), dbuf);
  301. GFX_PrintConsole(dbuf);
  302. GFX_PrintcConsole('%');
  303. contentLength -= rsize;
  304. if (downloadToFile)
  305. {
  306. FS_writeFile(rbuf, rsize);
  307. }
  308. else
  309. {
  310. NETLOADER_appendBufferToRunAddress(rbuf, rsize);
  311. }
  312. // all data downloaded
  313. if (contentLength == 0)
  314. {
  315. wizWriteDataFromMemory(s, "THX!", 4);
  316. wizCmd(s, WIZNET_CR_DISCON);
  317. // remove progress prints
  318. word i = strlen(dbuf);
  319. while (i > 0)
  320. {
  321. GFX_PrintcConsole(0x8); // backspace
  322. i--;
  323. }
  324. GFX_PrintcConsole(0x8); // backspace
  325. if (downloadToFile)
  326. {
  327. FS_close();
  328. // clear the shell
  329. SHELL_clearCommand();
  330. SHELL_print_prompt();
  331. }
  332. else
  333. {
  334. NETLOADER_runProgramFromMemory();
  335. }
  336. return;
  337. }
  338. }
  339. }
  340. }
  341. }
  342. // Initialize network bootloader on socket s
  343. void NETLOADER_init(word s)
  344. {
  345. // Open socket in TCP Server mode
  346. wizInitSocketTCP(s, NETLOADER_PORT);
  347. }
  348. // Check for a change in the socket status
  349. // Handles change if exists
  350. word NETLOADER_loop(word s)
  351. {
  352. // Get status for socket s
  353. word sxStatus = wizGetSockReg8(s, WIZNET_SnSR);
  354. if (sxStatus == WIZNET_SOCK_CLOSED)
  355. {
  356. // Open the socket when closed
  357. wizInitSocketTCP(s, NETLOADER_PORT);
  358. }
  359. else if (sxStatus == WIZNET_SOCK_ESTABLISHED)
  360. {
  361. // Handle session when a connection is established
  362. NETLOADER_handleSession(s);
  363. }
  364. else if (sxStatus == WIZNET_SOCK_LISTEN)
  365. {
  366. // Keep on listening
  367. return 1;
  368. }
  369. else if (sxStatus == WIZNET_SOCK_SYNSENT || sxStatus == WIZNET_SOCK_SYNRECV || sxStatus == WIZNET_SOCK_FIN_WAIT || sxStatus == WIZNET_SOCK_TIME_WAIT)
  370. {
  371. // Do nothing in these cases
  372. return 2;
  373. }
  374. else
  375. {
  376. // In other cases, reset the socket
  377. wizInitSocketTCP(s, NETLOADER_PORT);
  378. }
  379. return 0;
  380. }