util.py 5.2 KB

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