usb.py 16 KB

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