usb.py 18 KB

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