#!/usr/bin/python3.1

# Version 1.0  6.12.2012 works

# scans for BusPirate port
# Reads GUID info interactively

# Copyright (C) 2012, Arno Wagner <arno@wagner.name>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# version 2, or a later version at your choice, as published by the 
# Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
# USA.

import serial, time, sys

candidates = ['/dev/ttyUSB0','/dev/ttyUSB1','/dev/ttyUSB2','/dev/ttyUSB3',
        '/dev/ttyUSB4','/dev/ttyUSB5','/dev/ttyUSB6','/dev/ttyUSB7',
        '/dev/ttyUSB8','/dev/ttyUSB9','/dev/ttyUSB10','/dev/ttyUSB11',
        '/dev/ttyUSB12','/dev/ttyUSB13','/dev/ttyUSB14','/dev/ttyUSB15']

def test_if(name):
  try:
    s = serial.Serial(port=name, baudrate=115200, timeout=0.1)
  except Exception as e:
    return None
  else:
    return s
    
def test_bp(s):
  if s == None:
    return None 
  else:  
    s.write(b'#\n')
    i = s.read(10000)
    sl = i.decode('ascii').splitlines() 

    if len(sl) < 4 or \
       sl[0] != '#' or      \
       sl[1] != 'RESET' or  \
       sl[2] != '' or       \
       sl[3][0:10] != 'Bus Pirate':
       return None;
    return True

def find_bp():
  # tests list of candidates. Returns serial instance if exactly one found. 
  # If several found, aborts with exception.
  # If none found, returns None           
  cnt = 0
  s_bp = None
  n_bp = None
  n_str = ''
  for n in candidates:
    s = test_if(n)
    is_bp = test_bp(s)
    if is_bp: 
      cnt += 1
      s_bp = s
      n_bp = n
      n_str += n + '\n'
  if cnt > 1:
    raise IOError('More than one Bus Pirate found! Interfaces: \n'+n_str)
  return (s_bp, n_bp)  
    
    
def safe_exit(msg):
  # exit with BP reset
  print('  ** ERROR -- safe_exit() called **')
  print('  MSG: ',msg)
  s.write(b'#\n')
  i = s.read(100)
  sl = i.decode('ascii').splitlines()
#  print(sl)
  # Note: seems reset from SPI does sometimes not echo the #...
  if sl[0]!= 'RESET' and sl[1] != 'RESET': 
    print('  ** error exit reset failed! **')
  sys.exit(-1)  
  

a = find_bp()
(s,n) = a
#print('device:    ', n)
#print('seri_inst: ', s) 
 
# Start interaction
s = serial.Serial(port=n, baudrate=115200, timeout=0.1)

# reset just in case
s.write(b'#\n')
i = s.read(200)
sl = i.decode('ascii').splitlines()
#print(sl)
if sl[1] != 'RESET': safe_exit('initiial reset failed')

# m set SPI
s.write(b'm\n')
i = s.read(100)
sl = i.decode('ascii').splitlines()
#print(sl)
    
# config speed
s.write(b'5\n') # spi
i = s.read(100)
sl = i.decode('ascii').splitlines()
#print(sl)

s.write(b'4\n') # 1MHz
i = s.read(100)
sl = i.decode('ascii').splitlines()
#print(sl)

s.write(b'1\n') # 1: clock idle low 2: clock idle high
i = s.read(100)
sl = i.decode('ascii').splitlines()
#print(sl)

s.write(b'2\n') # 1: idle to active 2: active to idle (deault)
i = s.read(100)
sl = i.decode('ascii').splitlines()
#print(sl)

s.write(b'1\n') # sample in 1: middle 2: end
i = s.read(100)
sl = i.decode('ascii').splitlines()
#print(sl)

s.write(b'2\n') # /CS
i = s.read(100)
sl = i.decode('ascii').splitlines()
#print(sl)

s.write(b'2\n') # 1: Open Drain (unreliable) 2: normal
i = s.read(100)
sl = i.decode('ascii').splitlines()
#print(sl)
if sl[1] != 'Ready': safe_exit('SPI detail setting failed')

#s.write(b'P\n') # Activate pullups
#i = s.read(100)
#sl = i.decode('ascii').splitlines()
##print(sl)
#if sl[1] != 'Pull-up resistors ON': safe_exit('pull-up setting failed')

s.write(b'W\n') # Activate power
i = s.read(100)
sl = i.decode('ascii').splitlines()
#print(sl)
if sl[1] != 'Power supplies ON': safe_exit('power ON failed')

# give is a bit of time to come up and settle
time.sleep(0.200)

#s.write(b'v\n') # verify voltages
#i = s.read(1000)
#sl = i.decode('ascii').splitlines()
#print(sl)
##if sl[1] != 'Pull-up resistors ON': safe_exit('voltage check failed')

# read guid
s.write(b'[0x4b 0x00 0x00 0x00 0x00 r:8]\n') # Activate power
i = s.read(1000)
sl = i.decode('ascii').splitlines()
print(sl)
r = sl[7]
if r[0:6] != 'READ: ': safe_exit('read failed')

ds = r[6:]
print('ds: ',ds)
dss = ds.split()
for n in dss: print(int(n, 16), end=' ')
print()





# Final reset
s.write(b'#\n')
i = s.read(200)
sl = i.decode('ascii').splitlines()
#print(sl)
# Note: Seems reset from SPI does sometimes not echo the '#'
if sl[0] != 'RESET' and sl[1] != 'RESET': safe_exit('final reset failed')





