Advertisement
WhiteHare

avrdude_fixer.pl #2

Nov 4th, 2015
151
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.79 KB | None | 0 0
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;
  4. use IO::Socket::INET;
  5. use Time::HiRes qw/time/;
  6. use IO::Select;
  7. use Data::Dumper;
  8.  
  9. # The networkport this daemon listens on
  10. my $listenport = 5000; # The networkport this daemon listens on
  11.  
  12. # IP and port of the esp-link module.
  13. # Do not use the programming port for this. (This would require some small codechanges below)
  14. my $esplink = "192.168.3.104:23";
  15.  
  16. ###########################################################################
  17. ### Only modify things below this line if you know what you're doing... ###
  18. ###########################################################################
  19.  
  20. # Some STK500 protocol strings
  21. my $STK500_SYNCCMD = chr(0x30) . chr(0x20);
  22. my $STK500_SYNCREPLY = chr(0x14) . chr(0x10);
  23.  
  24. # Make sure we flush socketbuffers after every write (not strictly needed, but it doesn't hurt :-)
  25. $| = 1;
  26.  
  27. # Data structures for connections from client and to mcu.
  28. my $Client = {
  29. fh => undef, # Client network socket
  30. state => 'UNKNOWN', # State for the client statemachine
  31. buff => undef # Contains unprocessed incoming data from the client
  32. };
  33.  
  34. my $Mcu = {
  35. fh => undef, # Client network socket
  36. state => 'UNKNOWN', # State for the mcu statemachine
  37. buff => undef, # Contains unprocessed incoming data from the mcu
  38. };
  39.  
  40. my $timeNewConnection = 0; # Contains the absolute timestamp of a new connection. (only used for logging)
  41.  
  42. ###############################################################################
  43. #
  44. # 'Lowlevel' Socket and watchdog handling
  45. #
  46. # Implements:
  47. # - Accepting new connections
  48. # - (de)multiplexing of incoming data
  49. # - Watchdog functionality
  50. # - Detecting closed connections
  51. # - ...
  52. #
  53. # For the more interesting code, go to the section 'Callback functions' below
  54. #
  55.  
  56. # Create the socket sets to watch for with select()
  57. my $read_watch = new IO::Select();
  58. my $write_watch = new IO::Select();
  59.  
  60. # Create server socket that listens to incoming connection on port 5000
  61. my $server = new IO::Socket::INET(
  62. LocalHost => '0.0.0.0',
  63. LocalPort => $listenport,
  64. Proto => 'tcp',
  65. Listen => 5,
  66. Reuse => 1
  67. ) or die "ERROR in Socket Creation : $!\n";
  68. print "SERVER Waiting for client connection on port $listenport\n";
  69.  
  70. # A listening socket becomes readable on new connection... therefore, it belongs in the read_watch set
  71. $read_watch->add($server);
  72.  
  73. my $watchdogExpiry = -1;
  74. my $watchdogCallback = undef;
  75. my $watchdogInterval = 1;
  76.  
  77. # Enter the endless loop...
  78. while (1) {
  79. my ( $read_ready, $write_ready ) = IO::Select->select( $read_watch, $write_watch, undef, 0.05 );
  80.  
  81. # Process all sockets that are ready for reading...
  82. foreach my $fh (@$read_ready) {
  83. if ( $fh == $server ) {
  84.  
  85. # This is a new connection... accept it
  86. my $fh = $server->accept(); # Get the client socket
  87. setsockopt( $fh, SOL_SOCKET, SO_KEEPALIVE, 1 ); # Active KEEPALIVE to detect broken connections
  88. $read_watch->add($fh); # Keep an eye on this socket...
  89. $timeNewConnection = time(); # All timestamps are relative to client's new connection
  90. client_open($fh);
  91. }
  92. elsif ( $fh == $Client->{fh} ) {
  93. $fh->recv( my $data, 512 );
  94. if ( length($data) == 0 ) {
  95. client_close();
  96. next;
  97. }
  98.  
  99. # print "Clientrcv:\n";
  100. # DumpString($data);
  101. client_recv($data);
  102. }
  103. elsif ( $Mcu->{fh} && $fh == $Mcu->{fh} ) {
  104. $fh->recv( my $data, 512 );
  105.  
  106. #DumpString($data);
  107. if ( length($data) == 0 ) {
  108. mcu_close();
  109. next;
  110. }
  111. mcu_recv($data);
  112. }
  113. }
  114.  
  115. # Watchdog functionality
  116. if ( ( $watchdogExpiry > 0 ) && ( time() > $watchdogExpiry ) ) {
  117. &$watchdogCallback();
  118. $watchdogExpiry += $watchdogInterval;
  119. }
  120. }
  121.  
  122. ### WATCHDOG ###
  123. sub watchdog_start {
  124. my ( $interval, $callback ) = @_;
  125. $watchdogInterval = $interval;
  126. $watchdogExpiry = time() + $watchdogInterval;
  127. $watchdogCallback = $callback;
  128.  
  129. }
  130.  
  131. sub watchdog_stop {
  132. $watchdogExpiry = -1;
  133. $watchdogInterval = 1;
  134. $watchdogCallback = undef;
  135. }
  136.  
  137. sub watchdog_ping {
  138. $watchdogExpiry = time() + $watchdogInterval;
  139. }
  140.  
  141. ###############################################################################
  142. #
  143. # Callback functions
  144. #
  145.  
  146. #####
  147. # client_open
  148. #
  149. # Called when there is a new client connection.
  150. #
  151. # In:
  152. # $fh The socket belonging to the new client connection
  153. #
  154. sub client_open {
  155. my ($fh) = shift;
  156.  
  157. &log( "Client->Proxy: New connection from %s\n", $fh->peerhost() );
  158. $Client->{fh} = $fh;
  159. $Client->{state} = 'EXPECT_SYNC1';
  160. $Client->{buff} = undef;
  161.  
  162. &log("Proxy->MCU: Initiating MCU sync\n");
  163. $Mcu->{fh} = IO::Socket::INET->new($esplink) || die "Cannot connect to esp-link ($esplink): $!\n";
  164. $read_watch->add( $Mcu->{fh} ); # needed for select loop
  165. $Mcu->{state} = 'SENDSYNC';
  166. $Mcu->{buff} = undef;
  167. $Mcu->{fh}->send($STK500_SYNCCMD); # We send it asap to initiate a mcu reset by esp-link
  168. # Fire watchdog every 100ms. On expiry, resend the sync command
  169. watchdog_start(
  170. 0.1,
  171. sub {
  172. # Resending sync cmd
  173. &log("Proxy->MCU: Resending sync\n");
  174. $Mcu->{state} = 'SENDSYNC';
  175. $Mcu->{fh}->send($STK500_SYNCCMD);
  176. $Mcu->{buff} = ''; # Everything we already received cannot be a reply to this command
  177. }
  178. );
  179. }
  180.  
  181. #####
  182. # client_close
  183. #
  184. # Called when the client closes the connection
  185. #
  186. sub client_close {
  187. &cleanup("Client socket read");
  188. }
  189.  
  190. #####
  191. # mcu_close
  192. #
  193. # Called when the client closes the connection
  194. #
  195. sub mcu_close {
  196. &cleanup("Mcu socket read");
  197. }
  198.  
  199. #####
  200. # client_recv
  201. #
  202. # Called on reception of new data from the client
  203. #
  204. # In:
  205. # $data The new data that has arrived
  206. #
  207. sub client_recv {
  208. my ($data) = shift;
  209.  
  210. $Client->{buff} .= $data; # Append data to buffer
  211.  
  212. # We use a while loop here because windows avrdude can send 2 commands in 1 network packet...
  213. while ( length( $Client->{buff} ) != 0 ) {
  214. if ( $Client->{state} eq 'EXPECT_SYNC1' ) {
  215. &log("Client->Proxy: Ignoring 1st sync cmd\n");
  216. $Client->{state} = 'EXPECT_SYNC2';
  217. substr( $Client->{buff}, 0, 2, '' ); # Remove 2 bytes from beginning of buffer
  218. }
  219. elsif ( $Client->{state} eq 'EXPECT_SYNC2' ) {
  220. &log("Client->Proxy: Ignoring 2nd sync cmd\n");
  221. substr( $Client->{buff}, 0, 2, '' ); # Remove 2 bytes from beginning of buffer
  222. $Client->{state} = 'EXPECT_SYNCEXTRA';
  223. }
  224. elsif ( $Client->{state} eq 'EXPECT_SYNCEXTRA' ) {
  225. my $cmd = substr( $Client->{buff}, 0, 2 ); # Get the 1st 2 bytes from the buffer
  226. if ( $cmd eq $STK500_SYNCCMD ) {
  227. &log("Proxy->Client: Sending sync reply to client\n");
  228. substr( $Client->{buff}, 0, 2, '' ); # Remove 2 bytes from beginning of buffer
  229. $Client->{fh}->send($STK500_SYNCREPLY);
  230. }
  231. else {
  232. # Client sent something different than sync cmd... we can only continue once the MCU has sync
  233. &log("Client->Proxy: Client wants to continue...\n");
  234. if ( $Mcu->{state} eq 'SYNC' ) {
  235. bridge_start();
  236. }
  237. else {
  238. # We get here if the client has sent it's first real command and the mcu is not yet in sync
  239. $Client->{state} = 'MCUWAIT';
  240. last; # Escape the while loop while there is still data in the buffer
  241. }
  242. }
  243. }
  244. elsif ( $Client->{state} eq 'MCUWAIT' ) {
  245. last; # Escape the while loop while there is still data in the buffer
  246. }
  247. elsif ( $Client->{state} eq 'BRIDGE' ) {
  248. $Mcu->{fh}->send( $Client->{buff} );
  249. $Client->{buff} = '';
  250. }
  251. }
  252. }
  253.  
  254. #####
  255. # mcu_recv
  256. #
  257. # Called on reception of new data from the mcu
  258. #
  259. # In:
  260. # $data The new data that has arrived
  261. #
  262. sub mcu_recv {
  263. my ($data) = shift;
  264.  
  265. $Mcu->{buff} .= $data; # Append data to buffer
  266.  
  267. if ( $Mcu->{state} eq 'SENDSYNC' ) {
  268.  
  269. # We use a while loop here to deal with the fact the mcu could still be busy
  270. # sending something else. So we need discard any non relevant response
  271. # In esp-link this is easier since there is a direct serial connection
  272. while ( length( $Mcu->{buff} ) != 0 ) {
  273.  
  274. # We got a reply to our sync command, let's check if it is the correct one
  275. my $reply = substr( $Mcu->{buff}, 0, 2 ); # Get the 1st 2 bytes from the buffer
  276. if ( $reply eq $STK500_SYNCREPLY ) {
  277.  
  278. # We got a valid sync reply. Stop the sync watchdog and invoke a slower keep-alive watchdog
  279. watchdog_stop();
  280. watchdog_start(
  281. 0.5,
  282. sub {
  283. &log("Proxy->MCU: Timeout... sending keepalive\n");
  284. $Mcu->{state} = 'SENDSYNC';
  285. $Mcu->{buff} = undef; # Everything we already revceived cannot be a reply on this command
  286. $Mcu->{fh}->send($STK500_SYNCCMD);
  287. }
  288. );
  289. substr( $Mcu->{buff}, 0, 2, '' ); # Remove 2 response bytes from beginning of buffer
  290.  
  291. # We are synced! :-)
  292. &log("MCU->Proxy: MCU is in sync!\n");
  293. $Mcu->{state} = 'SYNC';
  294.  
  295. if ( $Client->{state} eq 'MCUWAIT' ) {
  296.  
  297. # Client was faster than us and is waiting.. let'r loose!
  298. bridge_start();
  299. }
  300. }
  301. else {
  302. &log("MCU->Proxy: Unexpected reply from MCU!... shifting 1 byte\n");
  303.  
  304. #remove 1 character from the buffer and see if the remaining bytes are a valid reply
  305. substr( $Mcu->{buff}, 0, 1, '' ); # Remove 1 byte from beginning of buffer
  306. }
  307. }
  308. }
  309. elsif ( $Mcu->{state} eq 'SYNC' ) {
  310.  
  311. # We're not supposed to receive anything now! We go to bridge mode whenever the client
  312. # is ready to also go to bridge mode. Watchdog sync timeout keeps running...
  313. }
  314. elsif ( $Mcu->{state} eq 'BRIDGE' ) {
  315. $Client->{fh}->send( $Mcu->{buff} );
  316. $Mcu->{buff} = '';
  317. }
  318.  
  319. }
  320.  
  321. ###############################################################################
  322. #
  323. # Helper functions
  324. #
  325. #
  326.  
  327. #####
  328. # bridge_start
  329. #
  330. # Called when all parties are ready to switch into simple bridging mode between client and mcu
  331. #
  332. sub bridge_start {
  333.  
  334. &log("Clienti<->MCU: Entering bridging mode...\n");
  335. watchdog_stop();
  336. $Mcu->{fh}->send( $Client->{buff} ); # Send everything we have from client to the mcu
  337. $Client->{state} = 'BRIDGE';
  338. $Client->{buff} = '';
  339. $Mcu->{state} = 'BRIDGE';
  340. $Mcu->{buff} = '';
  341. }
  342.  
  343. #####
  344. # cleanup
  345. #
  346. # Called when a connection is closed. This function will do all cleanup required and leave the
  347. # system in a state ready to accept new connections
  348. #
  349. sub cleanup {
  350. my ($location) = shift;
  351.  
  352. # Connection closed... abort averything
  353. watchdog_stop();
  354. $read_watch->remove( $Client->{fh} );
  355. $read_watch->remove( $Mcu->{fh} );
  356. $Client->{fh}->close();
  357. $Mcu->{fh}->close();
  358. $Client->{fh} = undef;
  359. $Mcu->{fh} = undef;
  360. $Client->{state} = 'UNKNOWN';
  361. $Mcu->{state} = 'UNKNOWN';
  362. &log( "That's all folks... A message brought to you from '" . $location . "'\n" );
  363. }
  364.  
  365. #####
  366. # DumpString
  367. #
  368. # Used to print a binary string in hex to the console. For debugging purposes
  369. #
  370. sub DumpString {
  371. my $s = shift || "";
  372. my @a = unpack( 'C*', $s );
  373. my $o = 0;
  374. my $i = 0;
  375. print "\tb0 b1 b2 b3 b4 b5 b6 b7\n";
  376. print "\t-- -- -- -- -- -- -- --\n";
  377. while (@a) {
  378. my @b = splice @a, 0, 8;
  379. my @x = map sprintf( "%02x", $_ ), @b;
  380. my $c = substr( $s, $o, 8 );
  381. $c =~ s/[[:^print:]]/ /g;
  382. printf "w%02d", $i;
  383. print " " x 5, join( ' ', @x ), "\n";
  384. $o += 8;
  385. $i++;
  386. }
  387. }
  388.  
  389. #####
  390. # log
  391. #
  392. # Used by the code above to print info on the console. It prepends the printed information
  393. # with a timestamp
  394. #
  395. sub log {
  396. my ( $format, @list ) = @_;
  397. my $time = time();
  398. printf( '%5f ' . $format, $time - $timeNewConnection, @list );
  399. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement