| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | # Serial port management for firmware communication | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright (C) 2016  Kevin O'Connor <kevin@koconnor.net> | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This file may be distributed under the terms of the GNU GPLv3 license. | 
					
						
							| 
									
										
										
										
											2017-03-31 20:48:29 -04:00
										 |  |  | import logging, threading | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | import serial | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-09 19:04:30 -05:00
										 |  |  | import msgproto, chelper, util | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 21:24:27 -05:00
										 |  |  | class error(Exception): | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | class SerialReader: | 
					
						
							| 
									
										
										
										
											2016-07-11 11:41:49 -04:00
										 |  |  |     BITS_PER_BYTE = 10. | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def __init__(self, reactor, serialport, baud): | 
					
						
							|  |  |  |         self.reactor = reactor | 
					
						
							|  |  |  |         self.serialport = serialport | 
					
						
							|  |  |  |         self.baud = baud | 
					
						
							|  |  |  |         # Serial port | 
					
						
							|  |  |  |         self.ser = None | 
					
						
							|  |  |  |         self.msgparser = msgproto.MessageParser() | 
					
						
							|  |  |  |         # C interface | 
					
						
							|  |  |  |         self.ffi_main, self.ffi_lib = chelper.get_ffi() | 
					
						
							|  |  |  |         self.serialqueue = None | 
					
						
							|  |  |  |         self.default_cmd_queue = self.alloc_command_queue() | 
					
						
							|  |  |  |         self.stats_buf = self.ffi_main.new('char[4096]') | 
					
						
							|  |  |  |         # MCU time/clock tracking | 
					
						
							|  |  |  |         self.last_ack_time = self.last_ack_rtt_time = 0. | 
					
						
							|  |  |  |         self.last_ack_clock = self.last_ack_rtt_clock = 0 | 
					
						
							|  |  |  |         self.est_clock = 0. | 
					
						
							|  |  |  |         # Threading | 
					
						
							|  |  |  |         self.lock = threading.Lock() | 
					
						
							|  |  |  |         self.background_thread = None | 
					
						
							|  |  |  |         # Message handlers | 
					
						
							| 
									
										
										
										
											2017-03-03 22:02:27 -05:00
										 |  |  |         self.status_timer = self.reactor.register_timer(self._status_event) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.status_cmd = None | 
					
						
							|  |  |  |         handlers = { | 
					
						
							| 
									
										
										
										
											2016-11-27 17:45:58 -05:00
										 |  |  |             '#unknown': self.handle_unknown, | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |             '#output': self.handle_output, 'status': self.handle_status, | 
					
						
							|  |  |  |             'shutdown': self.handle_output, 'is_shutdown': self.handle_output | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-06-06 12:35:13 -04:00
										 |  |  |         self.handlers = { (k, None): v for k, v in handlers.items() } | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def _bg_thread(self): | 
					
						
							|  |  |  |         response = self.ffi_main.new('struct pull_queue_message *') | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             self.ffi_lib.serialqueue_pull(self.serialqueue, response) | 
					
						
							|  |  |  |             count = response.len | 
					
						
							|  |  |  |             if count <= 0: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             params = self.msgparser.parse(response.msg[0:count]) | 
					
						
							|  |  |  |             params['#sent_time'] = response.sent_time | 
					
						
							|  |  |  |             params['#receive_time'] = response.receive_time | 
					
						
							|  |  |  |             with self.lock: | 
					
						
							|  |  |  |                 hdl = (params['#name'], params.get('oid')) | 
					
						
							|  |  |  |                 hdl = self.handlers.get(hdl, self.handle_default) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 hdl(params) | 
					
						
							|  |  |  |             except: | 
					
						
							|  |  |  |                 logging.exception("Exception in serial callback") | 
					
						
							|  |  |  |     def connect(self): | 
					
						
							| 
									
										
										
										
											2016-11-27 17:45:58 -05:00
										 |  |  |         # Initial connection | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         logging.info("Starting serial connect") | 
					
						
							| 
									
										
										
										
											2016-11-29 17:44:37 -05:00
										 |  |  |         while 1: | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |             starttime = self.reactor.monotonic() | 
					
						
							| 
									
										
										
										
											2016-11-29 17:44:37 -05:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2017-05-08 10:29:59 -04:00
										 |  |  |                 if self.baud: | 
					
						
							|  |  |  |                     self.ser = serial.Serial( | 
					
						
							|  |  |  |                         self.serialport, self.baud, timeout=0) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     self.ser = open(self.serialport, 'rb+') | 
					
						
							| 
									
										
										
										
											2017-03-15 20:45:27 -04:00
										 |  |  |             except (OSError, serial.SerialException), e: | 
					
						
							| 
									
										
										
										
											2016-11-29 17:44:37 -05:00
										 |  |  |                 logging.warn("Unable to open port: %s" % (e,)) | 
					
						
							|  |  |  |                 self.reactor.pause(starttime + 5.) | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2017-05-08 10:29:59 -04:00
										 |  |  |             if self.baud: | 
					
						
							|  |  |  |                 stk500v2_leave(self.ser, self.reactor) | 
					
						
							| 
									
										
										
										
											2016-11-29 17:44:37 -05:00
										 |  |  |             self.serialqueue = self.ffi_lib.serialqueue_alloc( | 
					
						
							|  |  |  |                 self.ser.fileno(), 0) | 
					
						
							|  |  |  |             self.background_thread = threading.Thread(target=self._bg_thread) | 
					
						
							|  |  |  |             self.background_thread.start() | 
					
						
							|  |  |  |             # Obtain and load the data dictionary from the firmware | 
					
						
							|  |  |  |             sbs = SerialBootStrap(self) | 
					
						
							|  |  |  |             identify_data = sbs.get_identify_data(starttime + 5.) | 
					
						
							|  |  |  |             if identify_data is None: | 
					
						
							|  |  |  |                 logging.warn("Timeout on serial connect") | 
					
						
							|  |  |  |                 self.disconnect() | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             break | 
					
						
							| 
									
										
										
										
											2016-11-27 17:45:58 -05:00
										 |  |  |         msgparser = msgproto.MessageParser() | 
					
						
							|  |  |  |         msgparser.process_identify(identify_data) | 
					
						
							|  |  |  |         self.msgparser = msgparser | 
					
						
							|  |  |  |         self.register_callback(self.handle_unknown, '#unknown') | 
					
						
							|  |  |  |         logging.info("Loaded %d commands (%s)" % ( | 
					
						
							|  |  |  |             len(msgparser.messages_by_id), msgparser.version)) | 
					
						
							| 
									
										
										
										
											2016-12-24 12:33:56 -05:00
										 |  |  |         logging.info("MCU config: %s" % (" ".join( | 
					
						
							|  |  |  |             ["%s=%s" % (k, v) for k, v in msgparser.config.items()]))) | 
					
						
							| 
									
										
										
										
											2017-03-03 22:02:27 -05:00
										 |  |  |         # Setup baud adjust | 
					
						
							| 
									
										
										
										
											2016-11-27 17:45:58 -05:00
										 |  |  |         mcu_baud = float(msgparser.config.get('SERIAL_BAUD', 0.)) | 
					
						
							|  |  |  |         if mcu_baud: | 
					
						
							|  |  |  |             baud_adjust = self.BITS_PER_BYTE / mcu_baud | 
					
						
							|  |  |  |             self.ffi_lib.serialqueue_set_baud_adjust( | 
					
						
							|  |  |  |                 self.serialqueue, baud_adjust) | 
					
						
							| 
									
										
										
										
											2017-03-22 22:26:23 -04:00
										 |  |  |         # Enable periodic get_status timer | 
					
						
							|  |  |  |         get_status = msgparser.lookup_command('get_status') | 
					
						
							|  |  |  |         self.status_cmd = get_status.encode() | 
					
						
							|  |  |  |         self.reactor.update_timer(self.status_timer, self.reactor.NOW) | 
					
						
							| 
									
										
										
										
											2017-03-03 22:02:27 -05:00
										 |  |  |         # Load initial last_ack_clock/last_ack_time | 
					
						
							|  |  |  |         uptime_msg = msgparser.create_command('get_uptime') | 
					
						
							|  |  |  |         params = self.send_with_response(uptime_msg, 'uptime') | 
					
						
							|  |  |  |         self.last_ack_clock = (params['high'] << 32) | params['clock'] | 
					
						
							|  |  |  |         self.last_ack_time = params['#receive_time'] | 
					
						
							| 
									
										
										
										
											2017-03-22 22:26:23 -04:00
										 |  |  |         # Make sure est_clock is calculated | 
					
						
							|  |  |  |         starttime = eventtime = self.reactor.monotonic() | 
					
						
							|  |  |  |         while not self.est_clock: | 
					
						
							|  |  |  |             if eventtime > starttime + 5.: | 
					
						
							|  |  |  |                 raise error("timeout on est_clock calculation") | 
					
						
							|  |  |  |             eventtime = self.reactor.pause(eventtime + 0.010) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def connect_file(self, debugoutput, dictionary, pace=False): | 
					
						
							|  |  |  |         self.ser = debugoutput | 
					
						
							|  |  |  |         self.msgparser.process_identify(dictionary, decompress=False) | 
					
						
							|  |  |  |         est_clock = 1000000000000. | 
					
						
							|  |  |  |         if pace: | 
					
						
							| 
									
										
										
										
											2016-12-22 23:47:46 -05:00
										 |  |  |             est_clock = float(self.msgparser.config['CLOCK_FREQ']) | 
					
						
							| 
									
										
										
										
											2016-07-11 11:41:49 -04:00
										 |  |  |         self.serialqueue = self.ffi_lib.serialqueue_alloc(self.ser.fileno(), 1) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.est_clock = est_clock | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |         self.last_ack_time = self.reactor.monotonic() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.last_ack_clock = 0 | 
					
						
							|  |  |  |         self.ffi_lib.serialqueue_set_clock_est( | 
					
						
							|  |  |  |             self.serialqueue, self.est_clock, self.last_ack_time | 
					
						
							|  |  |  |             , self.last_ack_clock) | 
					
						
							|  |  |  |     def disconnect(self): | 
					
						
							| 
									
										
										
										
											2017-03-08 22:01:52 -05:00
										 |  |  |         if self.serialqueue is not None: | 
					
						
							|  |  |  |             self.ffi_lib.serialqueue_exit(self.serialqueue) | 
					
						
							|  |  |  |             if self.background_thread is not None: | 
					
						
							|  |  |  |                 self.background_thread.join() | 
					
						
							|  |  |  |             self.ffi_lib.serialqueue_free(self.serialqueue) | 
					
						
							|  |  |  |             self.background_thread = self.serialqueue = None | 
					
						
							|  |  |  |         if self.ser is not None: | 
					
						
							|  |  |  |             self.ser.close() | 
					
						
							|  |  |  |             self.ser = None | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def stats(self, eventtime): | 
					
						
							|  |  |  |         if self.serialqueue is None: | 
					
						
							|  |  |  |             return "" | 
					
						
							|  |  |  |         sqstats = self.ffi_lib.serialqueue_get_stats( | 
					
						
							|  |  |  |             self.serialqueue, self.stats_buf, len(self.stats_buf)) | 
					
						
							|  |  |  |         sqstats = self.ffi_main.string(self.stats_buf) | 
					
						
							|  |  |  |         tstats = " est_clock=%.3f last_ack_time=%.3f last_ack_clock=%d" % ( | 
					
						
							|  |  |  |             self.est_clock, self.last_ack_time, self.last_ack_clock) | 
					
						
							|  |  |  |         return sqstats + tstats | 
					
						
							|  |  |  |     def _status_event(self, eventtime): | 
					
						
							|  |  |  |         self.send(self.status_cmd) | 
					
						
							|  |  |  |         return eventtime + 1.0 | 
					
						
							|  |  |  |     # Serial response callbacks | 
					
						
							|  |  |  |     def register_callback(self, callback, name, oid=None): | 
					
						
							|  |  |  |         with self.lock: | 
					
						
							|  |  |  |             self.handlers[name, oid] = callback | 
					
						
							|  |  |  |     def unregister_callback(self, name, oid=None): | 
					
						
							|  |  |  |         with self.lock: | 
					
						
							|  |  |  |             del self.handlers[name, oid] | 
					
						
							|  |  |  |     # Clock tracking | 
					
						
							|  |  |  |     def get_clock(self, eventtime): | 
					
						
							|  |  |  |         with self.lock: | 
					
						
							|  |  |  |             return int(self.last_ack_clock | 
					
						
							|  |  |  |                        + (eventtime - self.last_ack_time) * self.est_clock) | 
					
						
							|  |  |  |     def translate_clock(self, raw_clock): | 
					
						
							|  |  |  |         with self.lock: | 
					
						
							|  |  |  |             last_ack_clock = self.last_ack_clock | 
					
						
							|  |  |  |         clock_diff = (last_ack_clock - raw_clock) & 0xffffffff | 
					
						
							|  |  |  |         if clock_diff & 0x80000000: | 
					
						
							|  |  |  |             return last_ack_clock + 0x100000000 - clock_diff | 
					
						
							|  |  |  |         return last_ack_clock - clock_diff | 
					
						
							|  |  |  |     def get_last_clock(self): | 
					
						
							|  |  |  |         with self.lock: | 
					
						
							| 
									
										
										
										
											2016-11-30 19:31:46 -05:00
										 |  |  |             return self.last_ack_clock, self.last_ack_time | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Command sending | 
					
						
							|  |  |  |     def send(self, cmd, minclock=0, reqclock=0, cq=None): | 
					
						
							|  |  |  |         if cq is None: | 
					
						
							|  |  |  |             cq = self.default_cmd_queue | 
					
						
							|  |  |  |         self.ffi_lib.serialqueue_send( | 
					
						
							|  |  |  |             self.serialqueue, cq, cmd, len(cmd), minclock, reqclock) | 
					
						
							|  |  |  |     def encode_and_send(self, data, minclock, reqclock, cq): | 
					
						
							|  |  |  |         self.ffi_lib.serialqueue_encode_and_send( | 
					
						
							|  |  |  |             self.serialqueue, cq, data, len(data), minclock, reqclock) | 
					
						
							| 
									
										
										
										
											2017-03-05 15:00:15 -05:00
										 |  |  |     def send_with_response(self, cmd, name, oid=None): | 
					
						
							|  |  |  |         src = SerialRetryCommand(self, cmd, name, oid) | 
					
						
							| 
									
										
										
										
											2016-11-28 14:52:22 -05:00
										 |  |  |         return src.get_response() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def alloc_command_queue(self): | 
					
						
							| 
									
										
										
										
											2016-11-30 01:58:45 -05:00
										 |  |  |         return self.ffi_main.gc(self.ffi_lib.serialqueue_alloc_commandqueue(), | 
					
						
							|  |  |  |                                 self.ffi_lib.serialqueue_free_commandqueue) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Dumping debug lists | 
					
						
							|  |  |  |     def dump_debug(self): | 
					
						
							|  |  |  |         sdata = self.ffi_main.new('struct pull_queue_message[1024]') | 
					
						
							|  |  |  |         rdata = self.ffi_main.new('struct pull_queue_message[1024]') | 
					
						
							|  |  |  |         scount = self.ffi_lib.serialqueue_extract_old( | 
					
						
							|  |  |  |             self.serialqueue, 1, sdata, len(sdata)) | 
					
						
							|  |  |  |         rcount = self.ffi_lib.serialqueue_extract_old( | 
					
						
							|  |  |  |             self.serialqueue, 0, rdata, len(rdata)) | 
					
						
							|  |  |  |         logging.info("Dumping send queue %d messages" % (scount,)) | 
					
						
							|  |  |  |         for i in range(scount): | 
					
						
							|  |  |  |             msg = sdata[i] | 
					
						
							|  |  |  |             cmds = self.msgparser.dump(msg.msg[0:msg.len]) | 
					
						
							|  |  |  |             logging.info("Sent %d %f %f %d: %s" % ( | 
					
						
							|  |  |  |                 i, msg.receive_time, msg.sent_time, msg.len, ', '.join(cmds))) | 
					
						
							|  |  |  |         logging.info("Dumping receive queue %d messages" % (rcount,)) | 
					
						
							|  |  |  |         for i in range(rcount): | 
					
						
							|  |  |  |             msg = rdata[i] | 
					
						
							|  |  |  |             cmds = self.msgparser.dump(msg.msg[0:msg.len]) | 
					
						
							|  |  |  |             logging.info("Receive: %d %f %f %d: %s" % ( | 
					
						
							|  |  |  |                 i, msg.receive_time, msg.sent_time, msg.len, ', '.join(cmds))) | 
					
						
							|  |  |  |     # Default message handlers | 
					
						
							|  |  |  |     def handle_status(self, params): | 
					
						
							|  |  |  |         with self.lock: | 
					
						
							|  |  |  |             # Update last_ack_time / last_ack_clock | 
					
						
							|  |  |  |             ack_clock = (self.last_ack_clock & ~0xffffffff) | params['clock'] | 
					
						
							|  |  |  |             if ack_clock < self.last_ack_clock: | 
					
						
							|  |  |  |                 ack_clock += 0x100000000 | 
					
						
							|  |  |  |             sent_time = params['#sent_time'] | 
					
						
							|  |  |  |             self.last_ack_time = receive_time = params['#receive_time'] | 
					
						
							|  |  |  |             self.last_ack_clock = ack_clock | 
					
						
							|  |  |  |             # Update est_clock (if applicable) | 
					
						
							|  |  |  |             if receive_time > self.last_ack_rtt_time + 1. and sent_time: | 
					
						
							|  |  |  |                 if self.last_ack_rtt_time: | 
					
						
							|  |  |  |                     timedelta = receive_time - self.last_ack_rtt_time | 
					
						
							|  |  |  |                     clockdelta = ack_clock - self.last_ack_rtt_clock | 
					
						
							|  |  |  |                     estclock = clockdelta / timedelta | 
					
						
							|  |  |  |                     if estclock > self.est_clock and self.est_clock: | 
					
						
							|  |  |  |                         self.est_clock = (self.est_clock * 63. + estclock) / 64. | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         self.est_clock = estclock | 
					
						
							|  |  |  |                 self.last_ack_rtt_time = sent_time | 
					
						
							|  |  |  |                 self.last_ack_rtt_clock = ack_clock | 
					
						
							|  |  |  |             self.ffi_lib.serialqueue_set_clock_est( | 
					
						
							|  |  |  |                 self.serialqueue, self.est_clock, receive_time, ack_clock) | 
					
						
							|  |  |  |     def handle_unknown(self, params): | 
					
						
							|  |  |  |         logging.warn("Unknown message type %d: %s" % ( | 
					
						
							|  |  |  |             params['#msgid'], repr(params['#msg']))) | 
					
						
							|  |  |  |     def handle_output(self, params): | 
					
						
							|  |  |  |         logging.info("%s: %s" % (params['#name'], params['#msg'])) | 
					
						
							|  |  |  |     def handle_default(self, params): | 
					
						
							|  |  |  |         logging.warn("got %s" % (params,)) | 
					
						
							| 
									
										
										
										
											2016-11-30 01:58:45 -05:00
										 |  |  |     def __del__(self): | 
					
						
							|  |  |  |         self.disconnect() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Class to retry sending of a query command until a given response is received | 
					
						
							|  |  |  | class SerialRetryCommand: | 
					
						
							| 
									
										
										
										
											2017-03-08 21:24:27 -05:00
										 |  |  |     TIMEOUT_TIME = 5.0 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     RETRY_TIME = 0.500 | 
					
						
							| 
									
										
										
										
											2017-03-05 15:00:15 -05:00
										 |  |  |     def __init__(self, serial, cmd, name, oid=None): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.serial = serial | 
					
						
							|  |  |  |         self.cmd = cmd | 
					
						
							|  |  |  |         self.name = name | 
					
						
							| 
									
										
										
										
											2017-03-05 15:00:15 -05:00
										 |  |  |         self.oid = oid | 
					
						
							| 
									
										
										
										
											2016-11-28 14:52:22 -05:00
										 |  |  |         self.response = None | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |         self.min_query_time = self.serial.reactor.monotonic() | 
					
						
							| 
									
										
										
										
											2017-03-05 15:00:15 -05:00
										 |  |  |         self.serial.register_callback(self.handle_callback, self.name, self.oid) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.send_timer = self.serial.reactor.register_timer( | 
					
						
							|  |  |  |             self.send_event, self.serial.reactor.NOW) | 
					
						
							| 
									
										
										
										
											2017-03-08 21:24:27 -05:00
										 |  |  |     def unregister(self): | 
					
						
							| 
									
										
										
										
											2017-03-05 15:00:15 -05:00
										 |  |  |         self.serial.unregister_callback(self.name, self.oid) | 
					
						
							| 
									
										
										
										
											2017-03-08 21:24:27 -05:00
										 |  |  |         self.serial.reactor.unregister_timer(self.send_timer) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def send_event(self, eventtime): | 
					
						
							| 
									
										
										
										
											2016-11-28 14:52:22 -05:00
										 |  |  |         if self.response is not None: | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |             return self.serial.reactor.NEVER | 
					
						
							|  |  |  |         self.serial.send(self.cmd) | 
					
						
							|  |  |  |         return eventtime + self.RETRY_TIME | 
					
						
							|  |  |  |     def handle_callback(self, params): | 
					
						
							| 
									
										
										
										
											2016-11-28 14:52:22 -05:00
										 |  |  |         last_sent_time = params['#sent_time'] | 
					
						
							|  |  |  |         if last_sent_time >= self.min_query_time: | 
					
						
							|  |  |  |             self.response = params | 
					
						
							|  |  |  |     def get_response(self): | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |         eventtime = self.serial.reactor.monotonic() | 
					
						
							| 
									
										
										
										
											2016-11-28 14:52:22 -05:00
										 |  |  |         while self.response is None: | 
					
						
							|  |  |  |             eventtime = self.serial.reactor.pause(eventtime + 0.05) | 
					
						
							| 
									
										
										
										
											2017-03-08 21:24:27 -05:00
										 |  |  |             if eventtime > self.min_query_time + self.TIMEOUT_TIME: | 
					
						
							|  |  |  |                 self.unregister() | 
					
						
							|  |  |  |                 raise error("Timeout on wait for '%s' response" % (self.name,)) | 
					
						
							|  |  |  |         self.unregister() | 
					
						
							| 
									
										
										
										
											2016-11-28 14:52:22 -05:00
										 |  |  |         return self.response | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Code to start communication and download message type dictionary | 
					
						
							|  |  |  | class SerialBootStrap: | 
					
						
							|  |  |  |     RETRY_TIME = 0.500 | 
					
						
							|  |  |  |     def __init__(self, serial): | 
					
						
							|  |  |  |         self.serial = serial | 
					
						
							|  |  |  |         self.identify_data = "" | 
					
						
							|  |  |  |         self.identify_cmd = self.serial.msgparser.lookup_command( | 
					
						
							|  |  |  |             "identify offset=%u count=%c") | 
					
						
							|  |  |  |         self.is_done = False | 
					
						
							|  |  |  |         self.serial.register_callback(self.handle_identify, 'identify_response') | 
					
						
							|  |  |  |         self.serial.register_callback(self.handle_unknown, '#unknown') | 
					
						
							|  |  |  |         self.send_timer = self.serial.reactor.register_timer( | 
					
						
							|  |  |  |             self.send_event, self.serial.reactor.NOW) | 
					
						
							| 
									
										
										
										
											2016-11-29 17:44:37 -05:00
										 |  |  |     def get_identify_data(self, timeout): | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |         eventtime = self.serial.reactor.monotonic() | 
					
						
							| 
									
										
										
										
											2016-11-29 17:44:37 -05:00
										 |  |  |         while not self.is_done and eventtime <= timeout: | 
					
						
							| 
									
										
										
										
											2016-11-27 17:45:58 -05:00
										 |  |  |             eventtime = self.serial.reactor.pause(eventtime + 0.05) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.serial.unregister_callback('identify_response') | 
					
						
							| 
									
										
										
										
											2016-11-27 17:45:58 -05:00
										 |  |  |         self.serial.reactor.unregister_timer(self.send_timer) | 
					
						
							| 
									
										
										
										
											2016-11-29 17:44:37 -05:00
										 |  |  |         if not self.is_done: | 
					
						
							|  |  |  |             return None | 
					
						
							| 
									
										
										
										
											2016-11-27 17:45:58 -05:00
										 |  |  |         return self.identify_data | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def handle_identify(self, params): | 
					
						
							|  |  |  |         if self.is_done or params['offset'] != len(self.identify_data): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         msgdata = params['data'] | 
					
						
							|  |  |  |         if not msgdata: | 
					
						
							| 
									
										
										
										
											2016-11-27 17:45:58 -05:00
										 |  |  |             self.is_done = True | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |             return | 
					
						
							|  |  |  |         self.identify_data += msgdata | 
					
						
							|  |  |  |         imsg = self.identify_cmd.encode(len(self.identify_data), 40) | 
					
						
							|  |  |  |         self.serial.send(imsg) | 
					
						
							|  |  |  |     def send_event(self, eventtime): | 
					
						
							|  |  |  |         if self.is_done: | 
					
						
							|  |  |  |             return self.serial.reactor.NEVER | 
					
						
							|  |  |  |         imsg = self.identify_cmd.encode(len(self.identify_data), 40) | 
					
						
							|  |  |  |         self.serial.send(imsg) | 
					
						
							|  |  |  |         return eventtime + self.RETRY_TIME | 
					
						
							|  |  |  |     def handle_unknown(self, params): | 
					
						
							|  |  |  |         logging.debug("Unknown message %d (len %d) while identifying" % ( | 
					
						
							|  |  |  |             params['#msgid'], len(params['#msg']))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Attempt to place an AVR stk500v2 style programmer into normal mode | 
					
						
							| 
									
										
										
										
											2016-11-28 13:14:56 -05:00
										 |  |  | def stk500v2_leave(ser, reactor): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     logging.debug("Starting stk500v2 leave programmer sequence") | 
					
						
							| 
									
										
										
										
											2016-12-09 19:04:30 -05:00
										 |  |  |     util.clear_hupcl(ser.fileno()) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     origbaud = ser.baudrate | 
					
						
							|  |  |  |     # Request a dummy speed first as this seems to help reset the port | 
					
						
							| 
									
										
										
										
											2016-06-11 16:35:48 -04:00
										 |  |  |     ser.baudrate = 2400 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     ser.read(1) | 
					
						
							|  |  |  |     # Send stk500v2 leave programmer sequence | 
					
						
							|  |  |  |     ser.baudrate = 115200 | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     ser.read(4096) | 
					
						
							|  |  |  |     ser.write('\x1b\x01\x00\x01\x0e\x11\x04') | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |     reactor.pause(reactor.monotonic() + 0.050) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     res = ser.read(4096) | 
					
						
							|  |  |  |     logging.debug("Got %s from stk500v2" % (repr(res),)) | 
					
						
							|  |  |  |     ser.baudrate = origbaud | 
					
						
							| 
									
										
										
										
											2017-03-08 22:26:10 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Attempt an arduino style reset on a serial port | 
					
						
							|  |  |  | def arduino_reset(serialport, reactor): | 
					
						
							|  |  |  |     # First try opening the port at 1200 baud | 
					
						
							|  |  |  |     ser = serial.Serial(serialport, 1200, timeout=0) | 
					
						
							|  |  |  |     ser.read(1) | 
					
						
							| 
									
										
										
										
											2017-03-31 20:48:29 -04:00
										 |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							| 
									
										
										
										
											2017-03-08 22:26:10 -05:00
										 |  |  |     # Then try toggling DTR | 
					
						
							|  |  |  |     ser.dtr = True | 
					
						
							| 
									
										
										
										
											2017-03-31 20:48:29 -04:00
										 |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							| 
									
										
										
										
											2017-03-08 22:26:10 -05:00
										 |  |  |     ser.dtr = False | 
					
						
							| 
									
										
										
										
											2017-03-31 20:48:29 -04:00
										 |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							| 
									
										
										
										
											2017-03-08 22:26:10 -05:00
										 |  |  |     ser.close() |