USB.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. # greaseweazle/USB.py
  2. #
  3. # Written & released by Keir Fraser <keir.xen@gmail.com>
  4. #
  5. # This is free and unencumbered software released into the public domain.
  6. # See the file COPYING for more details, or visit <http://unlicense.org>.
  7. import struct, collections
  8. from greaseweazle import version
  9. ## Control-Path command set
  10. class ControlCmd:
  11. ClearComms = 10000
  12. Normal = 9600
  13. ## Command set
  14. class Cmd:
  15. GetInfo = 0
  16. Seek = 1
  17. Side = 2
  18. SetParams = 3
  19. GetParams = 4
  20. Motor = 5
  21. ReadFlux = 6
  22. WriteFlux = 7
  23. GetFluxStatus = 8
  24. GetIndexTimes = 9
  25. Select = 10
  26. # Bootloader specific:
  27. Update = 1
  28. ## Command responses/acknowledgements
  29. class Ack:
  30. Okay = 0
  31. BadCommand = 1
  32. NoIndex = 2
  33. NoTrk0 = 3
  34. FluxOverflow = 4
  35. FluxUnderflow = 5
  36. Wrprot = 6
  37. Max = 6
  38. ## Cmd.{Get,Set}Params indexes
  39. class Params:
  40. Delays = 0
  41. ## CmdError: Encapsulates a command acknowledgement.
  42. class CmdError(Exception):
  43. str = [ "Okay", "Bad Command", "No Index", "Track 0 not found",
  44. "Flux Overflow", "Flux Underflow", "Disk is Write Protected" ]
  45. def __init__(self, cmd, code):
  46. self.cmd = cmd
  47. self.code = code
  48. def __str__(self):
  49. if self.code <= Ack.Max:
  50. return self.str[self.code]
  51. return "Unknown Error (%u)" % self.code
  52. class Unit:
  53. ## Unit information, instance variables:
  54. ## major, minor: Greaseweazle firmware version number
  55. ## max_index: Maximum index timings for Cmd.ReadFlux
  56. ## max_cmd: Maximum Cmd number accepted by this unit
  57. ## sample_freq: Resolution of all time values passed to/from this unit
  58. ## Unit(ser):
  59. ## Accepts a Pyserial instance for Greaseweazle communications.
  60. def __init__(self, ser):
  61. self.ser = ser
  62. self.reset()
  63. # Copy firmware info to instance variables (see above for definitions).
  64. self.send_cmd(struct.pack("3B", Cmd.GetInfo, 3, 0))
  65. x = struct.unpack("<4BI24x", self.ser.read(32))
  66. (self.major, self.minor, self.max_index,
  67. self.max_cmd, self.sample_freq) = x
  68. # Check whether firmware is in update mode: limited command set if so.
  69. self.update_mode = (self.max_index == 0)
  70. if self.update_mode:
  71. self.update_jumpered = (self.sample_freq & 1)
  72. del self.max_index
  73. del self.sample_freq
  74. return
  75. # We are running main firmware: Check whether an update is needed.
  76. # We can use only the GetInfo command if the firmware is out of date.
  77. self.update_needed = (version.major != self.major
  78. or version.minor != self.minor)
  79. if self.update_needed:
  80. return
  81. # Initialise the delay properties with current firmware values.
  82. self.send_cmd(struct.pack("4B", Cmd.GetParams, 4, Params.Delays, 10))
  83. (self._select_delay, self._step_delay,
  84. self._seek_settle_delay, self._motor_delay,
  85. self._auto_off_delay) = struct.unpack("<5H", self.ser.read(10))
  86. ## reset:
  87. ## Resets communications with Greaseweazle.
  88. def reset(self):
  89. self.ser.reset_output_buffer()
  90. self.ser.baudrate = ControlCmd.ClearComms
  91. self.ser.baudrate = ControlCmd.Normal
  92. self.ser.reset_input_buffer()
  93. ## send_cmd:
  94. ## Send given command byte sequence to Greaseweazle.
  95. ## Raise a CmdError if command fails.
  96. def send_cmd(self, cmd):
  97. self.ser.write(cmd)
  98. (c,r) = struct.unpack("2B", self.ser.read(2))
  99. assert c == cmd[0]
  100. if r != 0:
  101. raise CmdError(c,r)
  102. ## seek:
  103. ## Seek the selected drive's heads to the specified track (cyl, side).
  104. def seek(self, cyl, side):
  105. self.send_cmd(struct.pack("3B", Cmd.Seek, 3, cyl))
  106. self.send_cmd(struct.pack("3B", Cmd.Side, 3, side))
  107. ## drive_select:
  108. ## Select/deselect the drive.
  109. def drive_select(self, state):
  110. self.send_cmd(struct.pack("3B", Cmd.Select, 3, int(state)))
  111. ## drive_motor:
  112. ## Turn the selected drive's motor on/off.
  113. def drive_motor(self, state):
  114. self.send_cmd(struct.pack("3B", Cmd.Motor, 3, int(state)))
  115. ## get_index_times:
  116. ## Get index timing values for the last .read_track() command.
  117. def get_index_times(self, nr):
  118. self.send_cmd(struct.pack("4B", Cmd.GetIndexTimes, 4, 0, nr))
  119. x = struct.unpack("<%dI" % nr, self.ser.read(4*nr))
  120. return x
  121. ## update_firmware:
  122. ## Update Greaseweazle to the given new firmware.
  123. def update_firmware(self, dat):
  124. self.send_cmd(struct.pack("<2BI", Cmd.Update, 6, len(dat)))
  125. self.ser.write(dat)
  126. (ack,) = struct.unpack("B", self.ser.read(1))
  127. return ack
  128. ## decode_flux:
  129. ## Decode the Greaseweazle data stream into a list of flux samples.
  130. def decode_flux(self, dat):
  131. flux = []
  132. while dat:
  133. i = dat.popleft()
  134. if i < 250:
  135. flux.append(i)
  136. elif i == 255:
  137. val = (dat.popleft() & 254) >> 1
  138. val += (dat.popleft() & 254) << 6
  139. val += (dat.popleft() & 254) << 13
  140. val += (dat.popleft() & 254) << 20
  141. flux.append(val)
  142. else:
  143. val = (i - 249) * 250
  144. val += dat.popleft() - 1
  145. flux.append(val)
  146. assert flux[-1] == 0
  147. return flux[:-1]
  148. ## encode_flux:
  149. ## Convert the given flux timings into an encoded data stream.
  150. def encode_flux(self, flux):
  151. dat = bytearray()
  152. for val in flux:
  153. if val == 0:
  154. pass
  155. elif val < 250:
  156. dat.append(val)
  157. else:
  158. high = val // 250
  159. if high <= 5:
  160. dat.append(249+high)
  161. dat.append(1 + val%250)
  162. else:
  163. dat.append(255)
  164. dat.append(1 | (val<<1) & 255)
  165. dat.append(1 | (val>>6) & 255)
  166. dat.append(1 | (val>>13) & 255)
  167. dat.append(1 | (val>>20) & 255)
  168. dat.append(0) # End of Stream
  169. return dat
  170. ## read_track:
  171. ## Read flux timings as encoded data stream for the current track.
  172. def read_track(self, nr_idx):
  173. dat = collections.deque()
  174. self.send_cmd(struct.pack("3B", Cmd.ReadFlux, 3, nr_idx))
  175. while True:
  176. dat += self.ser.read(1)
  177. dat += self.ser.read(self.ser.in_waiting)
  178. if dat[-1] == 0:
  179. break
  180. try:
  181. self.send_cmd(struct.pack("2B", Cmd.GetFluxStatus, 2))
  182. except CmdError as error:
  183. del dat
  184. return error.code, None, None
  185. return Ack.Okay, self.get_index_times(nr_idx), dat
  186. ## write_track:
  187. ## Write the given data stream to the current track via Greaseweazle.
  188. def write_track(self, dat):
  189. self.send_cmd(struct.pack("<2BIB", Cmd.WriteFlux, 7, 0, 1))
  190. self.ser.write(dat)
  191. self.ser.read(1) # Sync with Greaseweazle
  192. try:
  193. self.send_cmd(struct.pack("2B", Cmd.GetFluxStatus, 2))
  194. except CmdError as error:
  195. return error.code
  196. return Ack.Okay
  197. ##
  198. ## Delay-property public getters and setters:
  199. ## select_delay: Delay (usec) after asserting drive select
  200. ## step_delay: Delay (usec) after issuing a head-step command
  201. ## seek_settle_delay: Delay (msec) after completing a head-seek operation
  202. ## motor_delay: Delay (msec) after turning on drive spindle motor
  203. ## auto_off_delay: Timeout (msec) since last command upon which all
  204. ## drives are deselected and spindle motors turned off
  205. ##
  206. def _set_delays(self):
  207. self.send_cmd(struct.pack("<3B5H", Cmd.SetParams,
  208. 3+5*2, Params.Delays,
  209. self._select_delay, self._step_delay,
  210. self._seek_settle_delay,
  211. self._motor_delay, self._auto_off_delay))
  212. @property
  213. def select_delay(self):
  214. return self._select_delay
  215. @select_delay.setter
  216. def select_delay(self, select_delay):
  217. self._select_delay = select_delay
  218. self._set_delays()
  219. @property
  220. def step_delay(self):
  221. return self._step_delay
  222. @step_delay.setter
  223. def step_delay(self, step_delay):
  224. self._step_delay = step_delay
  225. self._set_delays()
  226. @property
  227. def seek_settle_delay(self):
  228. return self._seek_settle_delay
  229. @seek_settle_delay.setter
  230. def seek_settle_delay(self, seek_settle_delay):
  231. self._seek_settle_delay = seek_settle_delay
  232. self._set_delays()
  233. @property
  234. def motor_delay(self):
  235. return self._motor_delay
  236. @motor_delay.setter
  237. def motor_delay(self, motor_delay):
  238. self._motor_delay = motor_delay
  239. self._set_delays()
  240. @property
  241. def auto_off_delay(self):
  242. return self._auto_off_delay
  243. @auto_off_delay.setter
  244. def auto_off_delay(self, auto_off_delay):
  245. self._auto_off_delay = auto_off_delay
  246. self._set_delays()
  247. # Local variables:
  248. # python-indent: 4
  249. # End: