usb.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  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
  8. from greaseweazle import version
  9. from greaseweazle.flux import Flux
  10. ## Control-Path command set
  11. class ControlCmd:
  12. ClearComms = 10000
  13. Normal = 9600
  14. ## Command set
  15. class Cmd:
  16. GetInfo = 0
  17. Seek = 1
  18. Side = 2
  19. SetParams = 3
  20. GetParams = 4
  21. Motor = 5
  22. ReadFlux = 6
  23. WriteFlux = 7
  24. GetFluxStatus = 8
  25. GetIndexTimes = 9
  26. Select = 10
  27. SwitchFwMode = 11
  28. # Bootloader specific:
  29. Update = 1
  30. ## Command responses/acknowledgements
  31. class Ack:
  32. Okay = 0
  33. BadCommand = 1
  34. NoIndex = 2
  35. NoTrk0 = 3
  36. FluxOverflow = 4
  37. FluxUnderflow = 5
  38. Wrprot = 6
  39. Max = 6
  40. ## Cmd.{Get,Set}Params indexes
  41. class Params:
  42. Delays = 0
  43. ## CmdError: Encapsulates a command acknowledgement.
  44. class CmdError(Exception):
  45. str = [ "Okay", "Bad Command", "No Index", "Track 0 not found",
  46. "Flux Overflow", "Flux Underflow", "Disk is Write Protected" ]
  47. def __init__(self, cmd, code):
  48. self.cmd = cmd
  49. self.code = code
  50. def __str__(self):
  51. if self.code <= Ack.Max:
  52. return self.str[self.code]
  53. return "Unknown Error (%u)" % self.code
  54. class Unit:
  55. ## Unit information, instance variables:
  56. ## major, minor: Greaseweazle firmware version number
  57. ## max_index: Maximum index timings for Cmd.ReadFlux
  58. ## max_cmd: Maximum Cmd number accepted by this unit
  59. ## sample_freq: Resolution of all time values passed to/from this unit
  60. ## Unit(ser):
  61. ## Accepts a Pyserial instance for Greaseweazle communications.
  62. def __init__(self, ser):
  63. self.ser = ser
  64. self.reset()
  65. # Copy firmware info to instance variables (see above for definitions).
  66. self._send_cmd(struct.pack("3B", Cmd.GetInfo, 3, 0))
  67. x = struct.unpack("<4BIH22x", self.ser.read(32))
  68. (self.major, self.minor, self.max_index,
  69. self.max_cmd, self.sample_freq, self.hw_type) = x
  70. # Old firmware doesn't report HW type but runs on STM32F1 only.
  71. if self.hw_type == 0:
  72. self.hw_type = 1
  73. # Check whether firmware is in update mode: limited command set if so.
  74. self.update_mode = (self.max_index == 0)
  75. if self.update_mode:
  76. self.update_jumpered = (self.sample_freq & 1)
  77. del self.max_index
  78. del self.sample_freq
  79. return
  80. # We are running main firmware: Check whether an update is needed.
  81. # We can use only the GetInfo command if the firmware is out of date.
  82. self.update_needed = (version.major != self.major
  83. or version.minor != self.minor)
  84. if self.update_needed:
  85. return
  86. # Initialise the delay properties with current firmware values.
  87. self._send_cmd(struct.pack("4B", Cmd.GetParams, 4, Params.Delays, 10))
  88. (self._select_delay, self._step_delay,
  89. self._seek_settle_delay, self._motor_delay,
  90. self._auto_off_delay) = struct.unpack("<5H", self.ser.read(10))
  91. ## reset:
  92. ## Resets communications with Greaseweazle.
  93. def reset(self):
  94. self.ser.reset_output_buffer()
  95. self.ser.baudrate = ControlCmd.ClearComms
  96. self.ser.baudrate = ControlCmd.Normal
  97. self.ser.reset_input_buffer()
  98. ## _send_cmd:
  99. ## Send given command byte sequence to Greaseweazle.
  100. ## Raise a CmdError if command fails.
  101. def _send_cmd(self, cmd):
  102. self.ser.write(cmd)
  103. (c,r) = struct.unpack("2B", self.ser.read(2))
  104. assert c == cmd[0]
  105. if r != 0:
  106. raise CmdError(c, r)
  107. ## seek:
  108. ## Seek the selected drive's heads to the specified track (cyl, side).
  109. def seek(self, cyl, side):
  110. self._send_cmd(struct.pack("3B", Cmd.Seek, 3, cyl))
  111. self._send_cmd(struct.pack("3B", Cmd.Side, 3, side))
  112. ## drive_select:
  113. ## Select/deselect the drive.
  114. def drive_select(self, state):
  115. self._send_cmd(struct.pack("3B", Cmd.Select, 3, int(state)))
  116. ## drive_motor:
  117. ## Turn the selected drive's motor on/off.
  118. def drive_motor(self, state):
  119. self._send_cmd(struct.pack("3B", Cmd.Motor, 3, int(state)))
  120. ## _get_index_times:
  121. ## Get index timing values for the last .read_track() command.
  122. def _get_index_times(self, nr):
  123. self._send_cmd(struct.pack("4B", Cmd.GetIndexTimes, 4, 0, nr))
  124. x = struct.unpack("<%dI" % nr, self.ser.read(4*nr))
  125. return x
  126. ## switch_fw_mode:
  127. ## Switch between update bootloader and main firmware.
  128. def switch_fw_mode(self, mode):
  129. self._send_cmd(struct.pack("3B", Cmd.SwitchFwMode, 3, int(mode)))
  130. ## update_firmware:
  131. ## Update Greaseweazle to the given new firmware.
  132. def update_firmware(self, dat):
  133. self._send_cmd(struct.pack("<2BI", Cmd.Update, 6, len(dat)))
  134. self.ser.write(dat)
  135. (ack,) = struct.unpack("B", self.ser.read(1))
  136. return ack
  137. ## _decode_flux:
  138. ## Decode the Greaseweazle data stream into a list of flux samples.
  139. def _decode_flux(self, dat):
  140. flux = []
  141. dat_i = iter(dat)
  142. try:
  143. while True:
  144. i = next(dat_i)
  145. if i < 250:
  146. flux.append(i)
  147. elif i == 255:
  148. val = (next(dat_i) & 254) >> 1
  149. val += (next(dat_i) & 254) << 6
  150. val += (next(dat_i) & 254) << 13
  151. val += (next(dat_i) & 254) << 20
  152. flux.append(val)
  153. else:
  154. val = (i - 249) * 250
  155. val += next(dat_i) - 1
  156. flux.append(val)
  157. except StopIteration:
  158. pass
  159. assert flux[-1] == 0
  160. return flux[:-1]
  161. ## _encode_flux:
  162. ## Convert the given flux timings into an encoded data stream.
  163. def _encode_flux(self, flux):
  164. dat = bytearray()
  165. for val in flux:
  166. if val == 0:
  167. pass
  168. elif val < 250:
  169. dat.append(val)
  170. else:
  171. high = val // 250
  172. if high <= 5:
  173. dat.append(249+high)
  174. dat.append(1 + val%250)
  175. else:
  176. dat.append(255)
  177. dat.append(1 | (val<<1) & 255)
  178. dat.append(1 | (val>>6) & 255)
  179. dat.append(1 | (val>>13) & 255)
  180. dat.append(1 | (val>>20) & 255)
  181. dat.append(0) # End of Stream
  182. return dat
  183. ## _read_track:
  184. ## Private helper which issues command requests to Greaseweazle.
  185. def _read_track(self, nr_revs):
  186. # Request and read all flux timings for this track.
  187. dat = bytearray()
  188. self._send_cmd(struct.pack("3B", Cmd.ReadFlux, 3, nr_revs+1))
  189. while True:
  190. dat += self.ser.read(1)
  191. dat += self.ser.read(self.ser.in_waiting)
  192. if dat[-1] == 0:
  193. break
  194. # Check flux status. An exception is raised if there was an error.
  195. self._send_cmd(struct.pack("2B", Cmd.GetFluxStatus, 2))
  196. return dat
  197. ## read_track:
  198. ## Read and decode flux and index timings for the current track.
  199. def read_track(self, nr_revs, nr_retries=5):
  200. retry = 0
  201. while True:
  202. try:
  203. dat = self._read_track(nr_revs)
  204. except CmdError as error:
  205. # An error occurred. We may retry on transient overflows.
  206. if error.code == Ack.FluxOverflow and retry < nr_retries:
  207. retry += 1
  208. else:
  209. raise error
  210. else:
  211. # Success!
  212. break
  213. # Decode the flux list and read the index-times list.
  214. flux_list = self._decode_flux(dat)
  215. index_list = self._get_index_times(nr_revs+1)
  216. # Clip the initial partial revolution.
  217. to_index = index_list[0]
  218. for i in range(len(flux_list)):
  219. to_index -= flux_list[i]
  220. if to_index < 0:
  221. flux_list[i] = -to_index
  222. flux_list = flux_list[i:]
  223. break
  224. if to_index >= 0:
  225. # We ran out of flux.
  226. flux_list = []
  227. index_list = index_list[1:]
  228. # Success: Return the requested full index-to-index revolutions.
  229. return Flux(index_list, flux_list, self.sample_freq)
  230. ## write_track:
  231. ## Write the given flux stream to the current track via Greaseweazle.
  232. def write_track(self, flux_list, nr_retries=5):
  233. # Create encoded data stream.
  234. dat = self._encode_flux(flux_list)
  235. retry = 0
  236. while True:
  237. try:
  238. # Write the flux stream to the track via Greaseweazle.
  239. self._send_cmd(struct.pack("<2BIB", Cmd.WriteFlux, 7, 0, 1))
  240. self.ser.write(dat)
  241. self.ser.read(1) # Sync with Greaseweazle
  242. self._send_cmd(struct.pack("2B", Cmd.GetFluxStatus, 2))
  243. except CmdError as error:
  244. # An error occurred. We may retry on transient underflows.
  245. if error.code == Ack.FluxUnderflow and retry < nr_retries:
  246. retry += 1
  247. else:
  248. raise error
  249. else:
  250. # Success!
  251. break
  252. ##
  253. ## Delay-property public getters and setters:
  254. ## select_delay: Delay (usec) after asserting drive select
  255. ## step_delay: Delay (usec) after issuing a head-step command
  256. ## seek_settle_delay: Delay (msec) after completing a head-seek operation
  257. ## motor_delay: Delay (msec) after turning on drive spindle motor
  258. ## auto_off_delay: Timeout (msec) since last command upon which all
  259. ## drives are deselected and spindle motors turned off
  260. ##
  261. def _set_delays(self):
  262. self._send_cmd(struct.pack("<3B5H", Cmd.SetParams,
  263. 3+5*2, Params.Delays,
  264. self._select_delay, self._step_delay,
  265. self._seek_settle_delay,
  266. self._motor_delay, self._auto_off_delay))
  267. @property
  268. def select_delay(self):
  269. return self._select_delay
  270. @select_delay.setter
  271. def select_delay(self, select_delay):
  272. self._select_delay = select_delay
  273. self._set_delays()
  274. @property
  275. def step_delay(self):
  276. return self._step_delay
  277. @step_delay.setter
  278. def step_delay(self, step_delay):
  279. self._step_delay = step_delay
  280. self._set_delays()
  281. @property
  282. def seek_settle_delay(self):
  283. return self._seek_settle_delay
  284. @seek_settle_delay.setter
  285. def seek_settle_delay(self, seek_settle_delay):
  286. self._seek_settle_delay = seek_settle_delay
  287. self._set_delays()
  288. @property
  289. def motor_delay(self):
  290. return self._motor_delay
  291. @motor_delay.setter
  292. def motor_delay(self, motor_delay):
  293. self._motor_delay = motor_delay
  294. self._set_delays()
  295. @property
  296. def auto_off_delay(self):
  297. return self._auto_off_delay
  298. @auto_off_delay.setter
  299. def auto_off_delay(self, auto_off_delay):
  300. self._auto_off_delay = auto_off_delay
  301. self._set_delays()
  302. # Local variables:
  303. # python-indent: 4
  304. # End: