usb.py 13 KB

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