usb.py 17 KB

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