util.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. # greaseweazle/tools/util.py
  2. #
  3. # Greaseweazle control script: Utility functions.
  4. #
  5. # Written & released by Keir Fraser <keir.xen@gmail.com>
  6. #
  7. # This is free and unencumbered software released into the public domain.
  8. # See the file COPYING for more details, or visit <http://unlicense.org>.
  9. import argparse, os, sys, serial, struct, time
  10. import serial.tools.list_ports
  11. from greaseweazle import version
  12. from greaseweazle import usb as USB
  13. from greaseweazle.image.scp import SCP
  14. from greaseweazle.image.hfe import HFE
  15. from greaseweazle.image.ipf import IPF
  16. def drive_letter(letter):
  17. types = {
  18. 'A': (USB.BusType.IBMPC, 0),
  19. 'B': (USB.BusType.IBMPC, 1),
  20. '0': (USB.BusType.Shugart, 0),
  21. '1': (USB.BusType.Shugart, 1),
  22. '2': (USB.BusType.Shugart, 2)
  23. }
  24. if not letter.upper() in types:
  25. raise argparse.ArgumentTypeError("invalid drive letter: '%s'" % letter)
  26. return types[letter.upper()]
  27. def get_image_class(name):
  28. image_types = { '.scp': SCP, '.hfe': HFE, '.ipf': IPF }
  29. _, ext = os.path.splitext(name)
  30. if not ext.lower() in image_types:
  31. print("%s: Unrecognised file suffix '%s'" % (name, ext))
  32. return None
  33. return image_types[ext.lower()]
  34. def with_drive_selected(fn, usb, args):
  35. usb.set_bus_type(args.drive[0])
  36. try:
  37. usb.drive_select(args.drive[1])
  38. usb.drive_motor(args.drive[1], True)
  39. fn(usb, args)
  40. except KeyboardInterrupt:
  41. print()
  42. usb.reset()
  43. usb.ser.close()
  44. usb.ser.open()
  45. finally:
  46. usb.drive_motor(args.drive[1], False)
  47. usb.drive_deselect()
  48. def valid_ser_id(ser_id):
  49. return ser_id and ser_id.upper().startswith("GW")
  50. def find_port(old_port=None):
  51. # If we are reopening, and we know the location of the old port, require
  52. # to match on location.
  53. if old_port and old_port.location:
  54. for x in serial.tools.list_ports.comports():
  55. if x.location and x.location == old_port.location:
  56. return x.device
  57. return None
  58. # Score each serial port
  59. best_score, best_port = 0, None
  60. for x in serial.tools.list_ports.comports():
  61. score = 0
  62. if x.manufacturer == "Keir Fraser" and x.product == "Greaseweazle":
  63. score = 20
  64. elif x.vid == 0x1209 and x.pid == 0x4d69:
  65. # Our very own properly-assigned PID. Guaranteed to be us.
  66. score = 20
  67. elif x.vid == 0x1209 and x.pid == 0x0001:
  68. # Our old shared Test PID. It's not guaranteed to be us.
  69. score = 10
  70. if score > 0 and valid_ser_id(x.serial_number):
  71. if not old_port or not valid_ser_id(old_port.serial_number):
  72. score = 20
  73. elif x.serial_number == old_port.serial_number:
  74. score = 30
  75. else:
  76. score = 0
  77. if score > best_score:
  78. best_score, best_port = score, x
  79. if best_port:
  80. return best_port.device
  81. raise serial.SerialException('Could not auto-probe Greaseweazle device')
  82. def port_info(devname):
  83. for x in serial.tools.list_ports.comports():
  84. if x.device == devname:
  85. return x
  86. return None
  87. def usb_reopen(usb, is_update):
  88. mode = { False: 1, True: 0 }
  89. try:
  90. usb.switch_fw_mode(mode[is_update])
  91. except (serial.SerialException, struct.error):
  92. # Mac and Linux raise SerialException ("... returned no data")
  93. # Win10 pyserial returns a short read which fails struct.unpack
  94. pass
  95. usb.ser.close()
  96. for i in range(10):
  97. time.sleep(0.5)
  98. try:
  99. devicename = find_port(usb.port_info)
  100. new_ser = serial.Serial(devicename)
  101. except serial.SerialException:
  102. # Device not found
  103. pass
  104. else:
  105. new_usb = USB.Unit(new_ser)
  106. new_usb.port_info = port_info(devicename)
  107. return new_usb
  108. raise serial.SerialException('Could not reopen port after mode switch')
  109. def usb_open(devicename, is_update=False):
  110. if devicename == "auto":
  111. devicename = find_port()
  112. usb = USB.Unit(serial.Serial(devicename))
  113. usb.port_info = port_info(devicename)
  114. print("** %s v%u.%u [F%u], Host Tools v%u.%u"
  115. % (("Greaseweazle", "Bootloader")[usb.update_mode],
  116. usb.major, usb.minor, usb.hw_type,
  117. version.major, version.minor))
  118. if usb.update_mode and not is_update:
  119. if usb.hw_type == 7 and not usb.update_jumpered:
  120. usb = usb_reopen(usb, is_update)
  121. if not usb.update_mode:
  122. return usb
  123. print("Greaseweazle is in Firmware Update Mode:")
  124. print(" The only available action is \"update <update_file>\"")
  125. if usb.update_jumpered:
  126. print(" Remove the Update Jumper for normal operation")
  127. else:
  128. print(" Main firmware is erased: You *must* perform an update!")
  129. sys.exit(1)
  130. if is_update and not usb.update_mode:
  131. if usb.hw_type == 7:
  132. return usb_reopen(usb, is_update)
  133. print("Greaseweazle is in Normal Mode:")
  134. print(" To \"update\" you must install the Update Jumper")
  135. sys.exit(1)
  136. if not usb.update_mode and usb.update_needed:
  137. print("Firmware is out of date: Require v%u.%u"
  138. % (version.major, version.minor))
  139. if usb.hw_type == 7:
  140. print("Run \"update <update_file>\"")
  141. else:
  142. print("Install the Update Jumper and \"update <update_file>\"")
  143. sys.exit(1)
  144. return usb
  145. # Local variables:
  146. # python-indent: 4
  147. # End: