mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Initial work towards Applesauce support --- you can connect to the device and
test bandwidth.
This commit is contained in:
		
							
								
								
									
										1
									
								
								build.py
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								build.py
									
									
									
									
									
								
							| @@ -90,6 +90,7 @@ cxxlibrary( | ||||
|         "./lib/proto.cc", | ||||
|         "./lib/readerwriter.cc", | ||||
|         "./lib/sector.cc", | ||||
|         "./lib/usb/applesauceusb.cc", | ||||
|         "./lib/usb/fluxengineusb.cc", | ||||
|         "./lib/usb/greaseweazle.cc", | ||||
|         "./lib/usb/greaseweazleusb.cc", | ||||
|   | ||||
							
								
								
									
										335
									
								
								lib/usb/applesauceusb.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								lib/usb/applesauceusb.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,335 @@ | ||||
| #include "lib/globals.h" | ||||
| #include "protocol.h" | ||||
| #include "lib/fluxmap.h" | ||||
| #include "lib/bytes.h" | ||||
| #include "lib/usb/usb.pb.h" | ||||
| #include "applesauce.h" | ||||
| #include "serial.h" | ||||
| #include "usb.h" | ||||
| #include <unistd.h> | ||||
|  | ||||
| static uint32_t ss_rand_next(uint32_t x) | ||||
| { | ||||
|     return (x & 1) ? (x >> 1) ^ 0x80000062 : x >> 1; | ||||
| } | ||||
|  | ||||
| class ApplesauceUsb : public USB | ||||
| { | ||||
| public: | ||||
|     ApplesauceUsb(const std::string& port, const ApplesauceProto& config): | ||||
|         _serial(SerialPort::openSerialPort(port)), | ||||
|         _config(config) | ||||
|     { | ||||
|         std::string s = sendrecv("?"); | ||||
|         if (s != "Applesauce") | ||||
|             error( | ||||
|                 "Applesauce device not responding (expected 'Applesauce', got " | ||||
|                 "'{}')", | ||||
|                 s); | ||||
|     } | ||||
|  | ||||
|     ~ApplesauceUsb() | ||||
|     { | ||||
|         sendrecv("disconnect"); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::string sendrecv(const std::string& command) | ||||
|     { | ||||
|         _serial->writeLine(command); | ||||
|         return _serial->readLine(); | ||||
|     } | ||||
|  | ||||
|     void connect() | ||||
|     { | ||||
|         if (!_connected) | ||||
|         { | ||||
|             std::string probe = sendrecv("connect"); | ||||
|             if (probe != ".") | ||||
|                 error("Applesauce could not find any drives"); | ||||
|             _connected = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     int getVersion() override | ||||
|     { | ||||
|         // do_command({CMD_GET_INFO, 3, GETINFO_FIRMWARE}); | ||||
|         // | ||||
|         // Bytes response = _serial->readBytes(32); | ||||
|         // ByteReader br(response); | ||||
|         // | ||||
|         // br.seek(4); | ||||
|         // nanoseconds_t freq = br.read_le32(); | ||||
|         // _clock = 1000000000 / freq; | ||||
|         // | ||||
|         // br.seek(0); | ||||
|         // return br.read_be16(); | ||||
|         error("unsupported operation getVersion on the Greaseweazle"); | ||||
|     } | ||||
|  | ||||
|     void seek(int track) override | ||||
|     { | ||||
|         // do_command({CMD_SEEK, 3, (uint8_t)track}); | ||||
|         error("unsupported operation seek on the Greaseweazle"); | ||||
|     } | ||||
|  | ||||
|     nanoseconds_t getRotationalPeriod(int hardSectorCount) override | ||||
|     { | ||||
|         // if (hardSectorCount != 0) | ||||
|         //     error("hard sectors are currently unsupported on the | ||||
|         //     Greaseweazle"); | ||||
|  | ||||
|         // /* The Greaseweazle doesn't have a command to fetch the period | ||||
|         // directly, | ||||
|         //  * so we have to do a flux read. */ | ||||
|  | ||||
|         // switch (_version) | ||||
|         // { | ||||
|         //     case V22: | ||||
|         //         do_command({CMD_READ_FLUX, 2}); | ||||
|         //         break; | ||||
|  | ||||
|         //     case V24: | ||||
|         //     case V29: | ||||
|         //     { | ||||
|         //         Bytes cmd(8); | ||||
|         //         cmd.writer() | ||||
|         //             .write_8(CMD_READ_FLUX) | ||||
|         //             .write_8(cmd.size()) | ||||
|         //             .write_le32(0)  // ticks default value (guessed) | ||||
|         //             .write_le16(2); // revolutions | ||||
|         //         do_command(cmd); | ||||
|         //     } | ||||
|         // } | ||||
|  | ||||
|         // uint32_t ticks_gw = 0; | ||||
|         // uint32_t firstindex = ~0; | ||||
|         // uint32_t secondindex = ~0; | ||||
|         // for (;;) | ||||
|         // { | ||||
|         //     uint8_t b = _serial->readByte(); | ||||
|         //     if (!b) | ||||
|         //         break; | ||||
|  | ||||
|         //     if (b == 255) | ||||
|         //     { | ||||
|         //         switch (_serial->readByte()) | ||||
|         //         { | ||||
|         //             case FLUXOP_INDEX: | ||||
|         //             { | ||||
|         //                 uint32_t index = read_28() + ticks_gw; | ||||
|         //                 if (firstindex == ~0) | ||||
|         //                     firstindex = index; | ||||
|         //                 else if (secondindex == ~0) | ||||
|         //                     secondindex = index; | ||||
|         //                 break; | ||||
|         //             } | ||||
|  | ||||
|         //             case FLUXOP_SPACE: | ||||
|         //                 ticks_gw += read_28(); | ||||
|         //                 break; | ||||
|  | ||||
|         //             default: | ||||
|         //                 error("bad opcode in Greaseweazle stream"); | ||||
|         //         } | ||||
|         //     } | ||||
|         //     else | ||||
|         //     { | ||||
|         //         if (b < 250) | ||||
|         //             ticks_gw += b; | ||||
|         //         else | ||||
|         //         { | ||||
|         //             int delta = 250 + (b - 250) * 255 + _serial->readByte() - | ||||
|         //             1; ticks_gw += delta; | ||||
|         //         } | ||||
|         //     } | ||||
|         // } | ||||
|  | ||||
|         // if (secondindex == ~0) | ||||
|         //     error( | ||||
|         //         "unable to determine disk rotational period (is a disk in the | ||||
|         //         " "drive?)"); | ||||
|         // do_command({CMD_GET_FLUX_STATUS, 2}); | ||||
|  | ||||
|         // _revolutions = (nanoseconds_t)(secondindex - firstindex) * _clock; | ||||
|         // return _revolutions; | ||||
|         error("unsupported operation getRotationalPeriod on the Greaseweazle"); | ||||
|     } | ||||
|  | ||||
|     void testBulkWrite() override | ||||
|     { | ||||
|         int max = std::stoi(sendrecv("data:?max")); | ||||
|         fmt::print("Writing data: "); | ||||
|  | ||||
|         if (sendrecv(fmt::format("data:>{}", max)) != ".") | ||||
|             error("Cannot write to Applesauce"); | ||||
|  | ||||
|         Bytes junk(max); | ||||
|         uint32_t seed = 0; | ||||
|         for (int i = 0; i < max; i++) | ||||
|         { | ||||
|             junk[i] = seed; | ||||
|             seed = ss_rand_next(seed); | ||||
|         } | ||||
|         double start_time = getCurrentTime(); | ||||
|         _serial->write(junk); | ||||
|         _serial->readLine(); | ||||
|         double elapsed_time = getCurrentTime() - start_time; | ||||
|  | ||||
|         std::cout << fmt::format( | ||||
|             "transferred {} bytes from PC -> device in {} ms ({} kb/s)\n", | ||||
|             max, | ||||
|             int(elapsed_time * 1000.0), | ||||
|             int((max / 1024.0) / elapsed_time)); | ||||
|     } | ||||
|  | ||||
|     void testBulkRead() override | ||||
|     { | ||||
|         int max = std::stoi(sendrecv("data:?max")); | ||||
|         fmt::print("Reading data: "); | ||||
|  | ||||
|         if (sendrecv(fmt::format("data:<{}", max)) != ".") | ||||
|             error("Cannot read from Applesauce"); | ||||
|  | ||||
|         double start_time = getCurrentTime(); | ||||
|         _serial->readBytes(max); | ||||
|         double elapsed_time = getCurrentTime() - start_time; | ||||
|  | ||||
|         std::cout << fmt::format( | ||||
|             "transferred {} bytes from device -> PC in {} ms ({} kb/s)\n", | ||||
|             max, | ||||
|             int(elapsed_time * 1000.0), | ||||
|             int((max / 1024.0) / elapsed_time)); | ||||
|     } | ||||
|  | ||||
|     Bytes read(int side, | ||||
|         bool synced, | ||||
|         nanoseconds_t readTime, | ||||
|         nanoseconds_t hardSectorThreshold) override | ||||
|     { | ||||
|         // if (hardSectorThreshold != 0) | ||||
|         // error("hard sectors are currently unsupported on the Greaseweazle"); | ||||
|         // | ||||
|         // do_command({CMD_HEAD, 3, (uint8_t)side}); | ||||
|         // | ||||
|         // switch (_version) | ||||
|         // { | ||||
|         // case V22: | ||||
|         // { | ||||
|         // int revolutions = (readTime + _revolutions - 1) / _revolutions; | ||||
|         // Bytes cmd(4); | ||||
|         // cmd.writer() | ||||
|         // .write_8(CMD_READ_FLUX) | ||||
|         // .write_8(cmd.size()) | ||||
|         // .write_le32(revolutions + (synced ? 1 : 0)); | ||||
|         // do_command(cmd); | ||||
|         // break; | ||||
|         // } | ||||
|         // | ||||
|         // case V24: | ||||
|         // case V29: | ||||
|         // { | ||||
|         // Bytes cmd(8); | ||||
|         // cmd.writer() | ||||
|         // .write_8(CMD_READ_FLUX) | ||||
|         // .write_8(cmd.size()) | ||||
|         // .write_le32( | ||||
|         // (readTime + (synced ? _revolutions : 0)) / _clock) | ||||
|         // .write_le16(0); | ||||
|         // do_command(cmd); | ||||
|         // } | ||||
|         // } | ||||
|         // | ||||
|         // Bytes buffer; | ||||
|         // ByteWriter bw(buffer); | ||||
|         // for (;;) | ||||
|         // { | ||||
|         // uint8_t b = _serial->readByte(); | ||||
|         // if (!b) | ||||
|         // break; | ||||
|         // bw.write_8(b); | ||||
|         // } | ||||
|         // | ||||
|         // do_command({CMD_GET_FLUX_STATUS, 2}); | ||||
|         // | ||||
|         // Bytes fldata = greaseWeazleToFluxEngine(buffer, _clock); | ||||
|         // if (synced) | ||||
|         // fldata = stripPartialRotation(fldata); | ||||
|         // return fldata; | ||||
|         error("unsupported operation read on the Greaseweazle"); | ||||
|     } | ||||
|  | ||||
|     void write(int side, | ||||
|         const Bytes& fldata, | ||||
|         nanoseconds_t hardSectorThreshold) override | ||||
|     { | ||||
|         // if (hardSectorThreshold != 0) | ||||
|         // error("hard sectors are currently unsupported on the Greaseweazle"); | ||||
|         // | ||||
|         // do_command({CMD_HEAD, 3, (uint8_t)side}); | ||||
|         // switch (_version) | ||||
|         // { | ||||
|         // case V22: | ||||
|         // do_command({CMD_WRITE_FLUX, 3, 1}); | ||||
|         // break; | ||||
|         // | ||||
|         // case V24: | ||||
|         // case V29: | ||||
|         // do_command({CMD_WRITE_FLUX, 4, 1, 1}); | ||||
|         // break; | ||||
|         // } | ||||
|         // _serial->write(fluxEngineToGreaseweazle(fldata, _clock)); | ||||
|         // _serial->readByte(); /* synchronise */ | ||||
|         // | ||||
|         // do_command({CMD_GET_FLUX_STATUS, 2}); | ||||
|         error("unsupported operation write on the Greaseweazle"); | ||||
|     } | ||||
|  | ||||
|     void erase(int side, nanoseconds_t hardSectorThreshold) override | ||||
|     { | ||||
|         // if (hardSectorThreshold != 0) | ||||
|         // error("hard sectors are currently unsupported on the Greaseweazle"); | ||||
|         // | ||||
|         // do_command({CMD_HEAD, 3, (uint8_t)side}); | ||||
|         // | ||||
|         // Bytes cmd(6); | ||||
|         // ByteWriter bw(cmd); | ||||
|         // bw.write_8(CMD_ERASE_FLUX); | ||||
|         // bw.write_8(cmd.size()); | ||||
|         // bw.write_le32(200e6 / _clock); | ||||
|         // do_command(cmd); | ||||
|         // _serial->readByte(); /* synchronise */ | ||||
|         // | ||||
|         // do_command({CMD_GET_FLUX_STATUS, 2}); | ||||
|         error("unsupported operation erase on the Greaseweazle"); | ||||
|     } | ||||
|  | ||||
|     void setDrive(int drive, bool high_density, int index_mode) override | ||||
|     { | ||||
|         if (drive != 0) | ||||
|             error("the Applesauce only supports drive 0"); | ||||
|  | ||||
|         connect(); | ||||
|         sendrecv("drive:enable"); | ||||
|         sendrecv("motor:on"); | ||||
|         sendrecv(fmt::format("dpc:density{}", high_density)); | ||||
|     } | ||||
|  | ||||
|     void measureVoltages(struct voltages_frame* voltages) override | ||||
|     { | ||||
|         error("unsupported operation on the Greaseweazle"); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::unique_ptr<SerialPort> _serial; | ||||
|     const ApplesauceProto& _config; | ||||
|     bool _connected = false; | ||||
| }; | ||||
|  | ||||
| USB* createApplesauceUsb(const std::string& port, const ApplesauceProto& config) | ||||
| { | ||||
|     return new ApplesauceUsb(port, config); | ||||
| } | ||||
|  | ||||
| // vim: sw=4 ts=4 et | ||||
| @@ -124,11 +124,6 @@ public: | ||||
|         return br.read_be16(); | ||||
|     } | ||||
|  | ||||
|     void recalibrate() override | ||||
|     { | ||||
|         seek(0); | ||||
|     } | ||||
|  | ||||
|     void seek(int track) override | ||||
|     { | ||||
|         do_command({CMD_SEEK, 3, (uint8_t)track}); | ||||
|   | ||||
| @@ -254,6 +254,11 @@ uint8_t SerialPort::readByte() | ||||
|     return b; | ||||
| } | ||||
|  | ||||
| void SerialPort::writeByte(uint8_t b) | ||||
| { | ||||
|     this->write(&b, 1); | ||||
| } | ||||
|  | ||||
| void SerialPort::write(const Bytes& bytes) | ||||
| { | ||||
|     int ptr = 0; | ||||
| @@ -264,6 +269,28 @@ void SerialPort::write(const Bytes& bytes) | ||||
|     } | ||||
| } | ||||
|  | ||||
| void SerialPort::writeLine(const std::string& chars) | ||||
| { | ||||
|     this->write((const uint8_t*)&chars[0], chars.size()); | ||||
|     writeByte('\n'); | ||||
| } | ||||
|  | ||||
| std::string SerialPort::readLine() | ||||
| { | ||||
|     std::string s; | ||||
|  | ||||
|     for (;;) | ||||
|     { | ||||
|         uint8_t b = readByte(); | ||||
|         if (b == '\r') | ||||
|             continue; | ||||
|         if (b == '\n') | ||||
|             return s; | ||||
|  | ||||
|         s += b; | ||||
|     } | ||||
| } | ||||
|  | ||||
| std::unique_ptr<SerialPort> SerialPort::openSerialPort(const std::string& path) | ||||
| { | ||||
|     return std::make_unique<SerialPortImpl>(path); | ||||
|   | ||||
| @@ -17,8 +17,12 @@ public: | ||||
|     void read(Bytes& bytes); | ||||
|     Bytes readBytes(size_t count); | ||||
|     uint8_t readByte(); | ||||
|     void writeByte(uint8_t b); | ||||
|     void write(const Bytes& bytes); | ||||
|  | ||||
|     void writeLine(const std::string& chars); | ||||
|     std::string readLine(); | ||||
|  | ||||
| private: | ||||
|     uint8_t _readbuffer[4096]; | ||||
|     size_t _readbuffer_ptr = 0; | ||||
|   | ||||
| @@ -77,6 +77,14 @@ USB* get_usb_impl() | ||||
|         return createGreaseweazleUsb(conf.port(), conf); | ||||
|     } | ||||
|  | ||||
|     if (globalConfig()->usb().has_applesauce() && | ||||
|         globalConfig()->usb().applesauce().has_port()) | ||||
|     { | ||||
|         const auto& conf = globalConfig()->usb().applesauce(); | ||||
|         log("Using Applesauce on serial port {}", conf.port()); | ||||
|         return createApplesauceUsb(conf.port(), conf); | ||||
|     } | ||||
|  | ||||
|     /* Otherwise, select a device by USB ID. */ | ||||
|  | ||||
|     auto candidate = selectDevice(); | ||||
| @@ -94,7 +102,11 @@ USB* get_usb_impl() | ||||
|                 candidate->serialPort, globalConfig()->usb().greaseweazle()); | ||||
|  | ||||
|         case APPLESAUCE_ID: | ||||
|             error("Applesauce not supported yet"); | ||||
|             log("Using Applesauce {} on {}", | ||||
|                 candidate->serial, | ||||
|                 candidate->serialPort); | ||||
|             return createApplesauceUsb( | ||||
|                 candidate->serialPort, globalConfig()->usb().applesauce()); | ||||
|  | ||||
|         default: | ||||
|             error("internal"); | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| class Fluxmap; | ||||
| class GreaseweazleProto; | ||||
| class ApplesauceProto; | ||||
| namespace libusbp | ||||
| { | ||||
|     class device; | ||||
| @@ -17,7 +18,10 @@ public: | ||||
|     virtual ~USB(); | ||||
|  | ||||
|     virtual int getVersion() = 0; | ||||
|     virtual void recalibrate() = 0; | ||||
|     virtual void recalibrate() | ||||
|     { | ||||
|         seek(0); | ||||
|     }; | ||||
|     virtual void seek(int track) = 0; | ||||
|     virtual nanoseconds_t getRotationalPeriod(int hardSectorCount) = 0; | ||||
|     virtual void testBulkWrite() = 0; | ||||
| @@ -41,6 +45,8 @@ extern USB& getUsb(); | ||||
| extern USB* createFluxengineUsb(libusbp::device& device); | ||||
| extern USB* createGreaseweazleUsb( | ||||
|     const std::string& serialPort, const GreaseweazleProto& config); | ||||
| extern USB* createApplesauceUsb( | ||||
|     const std::string& serialPort, const ApplesauceProto& config); | ||||
|  | ||||
| static inline int usbGetVersion() | ||||
| { | ||||
|   | ||||
| @@ -16,9 +16,15 @@ message GreaseweazleProto { | ||||
| 		[(help) = "which FDD bus type is in use", default = IBMPC]; | ||||
| } | ||||
|  | ||||
| message ApplesauceProto { | ||||
| 	optional string port = 1 | ||||
| 		[(help) = "Applesauce serial port to use"]; | ||||
| } | ||||
|  | ||||
| message UsbProto { | ||||
| 	optional string serial = 1 | ||||
| 		[(help) = "serial number of FluxEngine or Greaseweazle device to use"]; | ||||
|  | ||||
| 	optional GreaseweazleProto greaseweazle = 2 [(help) = "Greaseweazle-specific options"]; | ||||
| 	optional ApplesauceProto applesauce = 3 [(help) = "Applesauce-specific options"]; | ||||
| } | ||||
|   | ||||
| @@ -42,13 +42,17 @@ std::vector<std::shared_ptr<CandidateDevice>> findUsbDevices() | ||||
|                 candidate->serial = get_serial_number(it); | ||||
|  | ||||
|                 if (id == GREASEWEAZLE_ID) | ||||
|                     candidate->type = DEVICE_GREASEWEAZLE; | ||||
|                 else if (id == APPLESAUCE_ID) | ||||
|                     candidate->type = DEVICE_APPLESAUCE; | ||||
|                 else if (id == FLUXENGINE_ID) | ||||
|                     candidate->type = DEVICE_FLUXENGINE; | ||||
|  | ||||
|                 if ((id == GREASEWEAZLE_ID) || (id == APPLESAUCE_ID)) | ||||
|                 { | ||||
|                     libusbp::serial_port port(candidate->device); | ||||
|                     candidate->serialPort = port.get_name(); | ||||
|                     candidate->type = DEVICE_GREASEWEAZLE; | ||||
|                 } | ||||
|                 else if (id == FLUXENGINE_ID) | ||||
|                     candidate->type = DEVICE_FLUXENGINE; | ||||
|  | ||||
|                 candidates.push_back(std::move(candidate)); | ||||
|             } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user