import  os 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  usb1 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  struct 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  binascii 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  . base  import  BaseSTBootloaderHandle 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  . spi  import  STBootloaderSPIHandle ,  PandaSpiException 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  . usb  import  STBootloaderUSBHandle 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  . constants  import  FW_PATH ,  McuType 
 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								class  PandaDFU : 
 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  def  __init__ ( self ,  dfu_serial :  str  |  None ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    # try USB, then SPI 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    handle :  BaseSTBootloaderHandle  |  None 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    self . _context ,  handle  =  PandaDFU . usb_connect ( dfu_serial ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  handle  is  None : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      self . _context ,  handle  =  PandaDFU . spi_connect ( dfu_serial ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  handle  is  None : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      raise  Exception ( f " failed to open DFU device  { dfu_serial } " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _handle :  BaseSTBootloaderHandle  =  handle 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _mcu_type :  McuType  =  self . _handle . get_mcu_type ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  def  __enter__ ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  self 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  __exit__ ( self ,  * args ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . close ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  close ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  self . _handle  is  not  None : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . _handle . close ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . _handle  =  None 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  self . _context  is  not  None : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        self . _context . close ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  @staticmethod 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  def  usb_connect ( dfu_serial :  str  |  None ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    handle  =  None 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    context  =  usb1 . USBContext ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    context . open ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  device  in  context . getDeviceList ( skip_on_error = True ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  device . getVendorID ( )  ==  0x0483  and  device . getProductID ( )  ==  0xdf11 : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        try : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          this_dfu_serial  =  device . open ( ) . getASCIIStringDescriptor ( 3 ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        except  Exception : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          continue 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  this_dfu_serial  ==  dfu_serial  or  dfu_serial  is  None : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          handle  =  STBootloaderUSBHandle ( device ,  device . open ( ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          break 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    return  context ,  handle 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  @staticmethod 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  def  spi_connect ( dfu_serial :  str  |  None ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    handle  =  None 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    this_dfu_serial  =  None 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    try : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      handle  =  STBootloaderSPIHandle ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      this_dfu_serial  =  PandaDFU . st_serial_to_dfu_serial ( handle . get_uid ( ) ,  handle . get_mcu_type ( ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    except  PandaSpiException : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      handle  =  None 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  dfu_serial  is  not  None  and  dfu_serial  !=  this_dfu_serial : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      handle  =  None 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								    return  None ,  handle 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  @staticmethod 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  def  usb_list ( )  - >  list [ str ] : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    dfu_serials  =  [ ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    try : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      with  usb1 . USBContext ( )  as  context : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        for  device  in  context . getDeviceList ( skip_on_error = True ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          if  device . getVendorID ( )  ==  0x0483  and  device . getProductID ( )  ==  0xdf11 : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            try : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              dfu_serials . append ( device . open ( ) . getASCIIStringDescriptor ( 3 ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            except  Exception : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              pass 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    except  Exception : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      pass 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  dfu_serials 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  @staticmethod 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								  def  spi_list ( )  - >  list [ str ] : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    try : 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								      _ ,  h  =  PandaDFU . spi_connect ( None ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  h  is  not  None : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        dfu_serial  =  PandaDFU . st_serial_to_dfu_serial ( h . get_uid ( ) ,  h . get_mcu_type ( ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        return  [ dfu_serial ,  ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    except  PandaSpiException : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      pass 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  [ ] 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  @staticmethod 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  st_serial_to_dfu_serial ( st :  str ,  mcu_type :  McuType  =  McuType . F4 ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  st  is  None  or  st  ==  " none " : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      return  None 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    uid_base  =  struct . unpack ( " H "  *  6 ,  bytes . fromhex ( st ) ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  mcu_type  ==  McuType . H7 : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      return  binascii . hexlify ( struct . pack ( " !HHH " ,  uid_base [ 1 ]  +  uid_base [ 5 ] ,  uid_base [ 0 ]  +  uid_base [ 4 ] ,  uid_base [ 3 ] ) ) . upper ( ) . decode ( " utf-8 " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    else : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      return  binascii . hexlify ( struct . pack ( " !HHH " ,  uid_base [ 1 ]  +  uid_base [ 5 ] ,  uid_base [ 0 ]  +  uid_base [ 4 ]  +  0xA ,  uid_base [ 3 ] ) ) . upper ( ) . decode ( " utf-8 " ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  get_mcu_type ( self )  - >  McuType : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  self . _mcu_type 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  reset ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _handle . jump ( self . _mcu_type . config . bootstub_address ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  program_bootstub ( self ,  code_bootstub ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _handle . clear_status ( ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    # erase all sectors 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  i  in  range ( len ( self . _mcu_type . config . sector_sizes ) ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      self . _handle . erase_sector ( i ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . _handle . program ( self . _mcu_type . config . bootstub_address ,  code_bootstub ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  recover ( self ) : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    fn  =  os . path . join ( FW_PATH ,  self . _mcu_type . config . bootstub_fn ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    with  open ( fn ,  " rb " )  as  f : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      code  =  f . read ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . program_bootstub ( code ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    self . reset ( ) 
  
						 
					
						
							
								
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  @staticmethod 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  def  list ( )  - >  list [ str ] : 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    ret  =  PandaDFU . usb_list ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    ret  + =  PandaDFU . spi_list ( ) 
  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  list ( set ( ret ) )