|  |  |  | @ -22,6 +22,12 @@ | 
			
		
	
		
			
				
					|  |  |  |  | #define SPI_NACK 0x1FU | 
			
		
	
		
			
				
					|  |  |  |  | #define SPI_CHECKSUM_START 0xABU | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | enum SpiError { | 
			
		
	
		
			
				
					|  |  |  |  |   NACK = -2, | 
			
		
	
		
			
				
					|  |  |  |  |   ACK_TIMEOUT = -3, | 
			
		
	
		
			
				
					|  |  |  |  | }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | struct __attribute__((packed)) spi_header { | 
			
		
	
		
			
				
					|  |  |  |  |   uint8_t sync; | 
			
		
	
		
			
				
					|  |  |  |  |   uint8_t endpoint; | 
			
		
	
	
		
			
				
					|  |  |  | @ -40,8 +46,8 @@ public: | 
			
		
	
		
			
				
					|  |  |  |  |   }; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   ~LockEx() { | 
			
		
	
		
			
				
					|  |  |  |  |     m.unlock(); | 
			
		
	
		
			
				
					|  |  |  |  |     flock(fd, LOCK_UN); | 
			
		
	
		
			
				
					|  |  |  |  |     m.unlock(); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | private: | 
			
		
	
	
		
			
				
					|  |  |  | @ -170,7 +176,7 @@ int PandaSpiHandle::bulk_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t t | 
			
		
	
		
			
				
					|  |  |  |  |     if (d < 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       LOGE("SPI: bulk transfer failed with %d", d); | 
			
		
	
		
			
				
					|  |  |  |  |       comms_healthy = false; | 
			
		
	
		
			
				
					|  |  |  |  |       return -1; | 
			
		
	
		
			
				
					|  |  |  |  |       return d; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     ret += d; | 
			
		
	
	
		
			
				
					|  |  |  | @ -210,17 +216,40 @@ bool check_checksum(uint8_t *data, int data_len) { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | int PandaSpiHandle::spi_transfer_retry(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len, unsigned int timeout) { | 
			
		
	
		
			
				
					|  |  |  |  |   int ret; | 
			
		
	
		
			
				
					|  |  |  |  |   int count = 0; | 
			
		
	
		
			
				
					|  |  |  |  |   bool timed_out = false; | 
			
		
	
		
			
				
					|  |  |  |  |   double start_time = millis_since_boot(); | 
			
		
	
		
			
				
					|  |  |  |  |   do { | 
			
		
	
		
			
				
					|  |  |  |  |     // TODO: handle error
 | 
			
		
	
		
			
				
					|  |  |  |  |     ret = spi_transfer(endpoint, tx_data, tx_len, rx_data, max_rx_len); | 
			
		
	
		
			
				
					|  |  |  |  |   } while (ret < 0 && connected && ((timeout == 0) || (millis_since_boot() - start_time) < timeout)); | 
			
		
	
		
			
				
					|  |  |  |  |     ret = spi_transfer(endpoint, tx_data, tx_len, rx_data, max_rx_len, timeout); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (ret < 0) { | 
			
		
	
		
			
				
					|  |  |  |  |       timed_out = (timeout != 0) && (count > 5); | 
			
		
	
		
			
				
					|  |  |  |  |       count += ret == SpiError::ACK_TIMEOUT; | 
			
		
	
		
			
				
					|  |  |  |  |       std::this_thread::yield(); | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |   } while (ret < 0 && connected && !timed_out); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (ret < 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     LOGE("transfer failed, after %d tries, %.2fms", count, millis_since_boot() - start_time); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   return ret; | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | int PandaSpiHandle::wait_for_ack(spi_ioc_transfer &transfer, uint8_t ack) { | 
			
		
	
		
			
				
					|  |  |  |  | int PandaSpiHandle::wait_for_ack(uint8_t ack, uint8_t tx, unsigned int timeout) { | 
			
		
	
		
			
				
					|  |  |  |  |   double start_millis = millis_since_boot(); | 
			
		
	
		
			
				
					|  |  |  |  |   if (timeout <= 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     timeout = SPI_ACK_TIMEOUT; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   spi_ioc_transfer transfer = { | 
			
		
	
		
			
				
					|  |  |  |  |     .tx_buf = (uint64_t)tx_buf, | 
			
		
	
		
			
				
					|  |  |  |  |     .rx_buf = (uint64_t)rx_buf, | 
			
		
	
		
			
				
					|  |  |  |  |     .delay_usecs = 5, | 
			
		
	
		
			
				
					|  |  |  |  |     .len = 1 | 
			
		
	
		
			
				
					|  |  |  |  |   }; | 
			
		
	
		
			
				
					|  |  |  |  |   tx_buf[0] = tx; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   while (true) { | 
			
		
	
		
			
				
					|  |  |  |  |     int ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &transfer); | 
			
		
	
		
			
				
					|  |  |  |  |     if (ret < 0) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -232,20 +261,20 @@ int PandaSpiHandle::wait_for_ack(spi_ioc_transfer &transfer, uint8_t ack) { | 
			
		
	
		
			
				
					|  |  |  |  |       break; | 
			
		
	
		
			
				
					|  |  |  |  |     } else if (rx_buf[0] == SPI_NACK) { | 
			
		
	
		
			
				
					|  |  |  |  |       LOGD("SPI: got NACK"); | 
			
		
	
		
			
				
					|  |  |  |  |       return -1; | 
			
		
	
		
			
				
					|  |  |  |  |       return SpiError::NACK; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     // handle timeout
 | 
			
		
	
		
			
				
					|  |  |  |  |     if (millis_since_boot() - start_millis > SPI_ACK_TIMEOUT) { | 
			
		
	
		
			
				
					|  |  |  |  |     if (millis_since_boot() - start_millis > timeout) { | 
			
		
	
		
			
				
					|  |  |  |  |       LOGD("SPI: timed out waiting for ACK"); | 
			
		
	
		
			
				
					|  |  |  |  |       return -1; | 
			
		
	
		
			
				
					|  |  |  |  |       return SpiError::ACK_TIMEOUT; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   return 0; | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len) { | 
			
		
	
		
			
				
					|  |  |  |  | int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len, unsigned int timeout) { | 
			
		
	
		
			
				
					|  |  |  |  |   int ret; | 
			
		
	
		
			
				
					|  |  |  |  |   uint16_t rx_data_len; | 
			
		
	
		
			
				
					|  |  |  |  |   LockEx lock(spi_fd, hw_lock); | 
			
		
	
	
		
			
				
					|  |  |  | @ -277,9 +306,7 @@ int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // Wait for (N)ACK
 | 
			
		
	
		
			
				
					|  |  |  |  |   tx_buf[0] = 0x11; | 
			
		
	
		
			
				
					|  |  |  |  |   transfer.len = 1; | 
			
		
	
		
			
				
					|  |  |  |  |   ret = wait_for_ack(transfer, SPI_HACK); | 
			
		
	
		
			
				
					|  |  |  |  |   ret = wait_for_ack(SPI_HACK, 0x11, timeout); | 
			
		
	
		
			
				
					|  |  |  |  |   if (ret < 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     goto transfer_fail; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
	
		
			
				
					|  |  |  | @ -297,9 +324,7 @@ int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint8_t *tx_data, uint16_t tx | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // Wait for (N)ACK
 | 
			
		
	
		
			
				
					|  |  |  |  |   tx_buf[0] = 0x13; | 
			
		
	
		
			
				
					|  |  |  |  |   transfer.len = 1; | 
			
		
	
		
			
				
					|  |  |  |  |   ret = wait_for_ack(transfer, SPI_DACK); | 
			
		
	
		
			
				
					|  |  |  |  |   ret = wait_for_ack(SPI_DACK, 0x13, timeout); | 
			
		
	
		
			
				
					|  |  |  |  |   if (ret < 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     goto transfer_fail; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
	
		
			
				
					|  |  |  | 
 |