diff --git a/LaunchAPPL/CMakeLists.txt b/LaunchAPPL/CMakeLists.txt index 0b4b58faea..da45d1b3d2 100644 --- a/LaunchAPPL/CMakeLists.txt +++ b/LaunchAPPL/CMakeLists.txt @@ -1,5 +1,10 @@ +add_subdirectory(Common) + if(CMAKE_SYSTEM_NAME MATCHES Retro.*) - add_subdirectory(Server) + if(CMAKE_SYSTEM_NAME MATCHES RetroCarbon) + else() + add_subdirectory(Server) + endif() else() add_subdirectory(Client) endif() diff --git a/LaunchAPPL/Client/CMakeLists.txt b/LaunchAPPL/Client/CMakeLists.txt index ca372552f4..5f34ab8282 100644 --- a/LaunchAPPL/Client/CMakeLists.txt +++ b/LaunchAPPL/Client/CMakeLists.txt @@ -4,6 +4,7 @@ set(LAUNCHMETHODS Executor.h Executor.cc MiniVMac.h MiniVMac.cc SSH.h SSH.cc + Serial.h Serial.cc ) if(APPLE) @@ -28,7 +29,7 @@ add_executable(LaunchAPPL target_include_directories(LaunchAPPL PRIVATE ${CMAKE_INSTALL_PREFIX}/include ${Boost_INCLUDE_DIR}) -target_link_libraries(LaunchAPPL ResourceFiles ${Boost_LIBRARIES}) +target_link_libraries(LaunchAPPL ResourceFiles LaunchAPPLCommon ${Boost_LIBRARIES}) if(APPLE) find_library(APPLICATIONSERVICES_FW ApplicationServices) diff --git a/LaunchAPPL/Client/LaunchAPPL.cc b/LaunchAPPL/Client/LaunchAPPL.cc index 9fae7f8eea..2cc894e9d8 100644 --- a/LaunchAPPL/Client/LaunchAPPL.cc +++ b/LaunchAPPL/Client/LaunchAPPL.cc @@ -20,6 +20,7 @@ #include "Executor.h" #include "MiniVMac.h" #include "SSH.h" +#include "Serial.h" namespace po = boost::program_options; namespace fs = boost::filesystem; @@ -42,7 +43,8 @@ static void RegisterLaunchMethods() new Carbon(), #endif new Executor(), new MiniVMac(), - new SSH() + new SSH(), + new Serial() // #### Add new `LaunchMethod`s here. }; diff --git a/LaunchAPPL/Client/Serial.cc b/LaunchAPPL/Client/Serial.cc new file mode 100644 index 0000000000..11516ae943 --- /dev/null +++ b/LaunchAPPL/Client/Serial.cc @@ -0,0 +1,168 @@ +#include "Serial.h" +#include "Launcher.h" +#include "Utilities.h" +#include "Stream.h" +#include +#include +#include +#include +#include + +namespace po = boost::program_options; + + +class SerialStream : public Stream +{ + static const long kReadBufferSize = 4096; + uint8_t readBuffer[kReadBufferSize]; +public: + int fd; + + virtual void write(const void* p, size_t n) override; + + void wait(); + + SerialStream(po::variables_map &options); + ~SerialStream(); +}; + +class SerialLauncher : public Launcher +{ + SerialStream stream; +public: + SerialLauncher(po::variables_map& options); + virtual ~SerialLauncher(); + + virtual bool Go(int timeout = 0); + +private: + void write(const void *p, size_t n) { stream.write(p, n); } + ssize_t read(void * p, size_t n); +}; + + +SerialStream::SerialStream(po::variables_map &options) +{ + std::string port = options["serial-port"].as(); + fd = open(port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY ); + if(fd < 0) + throw std::runtime_error("Cannot open serial port.\n"); + + struct termios tios; + tcgetattr(fd,&tios); + + tios.c_cflag = B19200 | CRTSCTS | CS8 | CLOCAL | CREAD; + tios.c_iflag = 0;//IGNPAR | ICRNL; + tios.c_lflag = 0; + tios.c_oflag = 0; + tios.c_cc[VTIME] = 0; /* inter-character timer unused */ + tios.c_cc[VMIN] = 1; /* blocking read until 5 chars received */ + tcsetattr(fd,TCSANOW,&tios); + usleep(500000); +} +SerialStream::~SerialStream() +{ + close(fd); +} + +void SerialStream::write(const void* p, size_t n) +{ + while(n) + { + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLOUT; + pfd.revents = 0; + poll(&pfd, 1, 1000); + if(pfd.revents & POLLOUT) + { + ssize_t written = ::write(fd, p, n); + if(written > 0) + { + p = (const void*) ( (const char*)p + written ); + n -= written; + } + } + } +} + +void SerialStream::wait() +{ + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN; + pfd.revents = 0; + poll(&pfd, 1, 1000); + if(pfd.revents & POLLIN) + { + ssize_t n = ::read(fd, readBuffer, kReadBufferSize); + if(n > 0) + { + onReceive(readBuffer, n); + } + } +} + + +SerialLauncher::SerialLauncher(po::variables_map &options) + : Launcher(options), stream(options) +{ +} + +SerialLauncher::~SerialLauncher() +{ +} + +ssize_t SerialLauncher::read(void *p, size_t n) +{ + ssize_t available = stream.read(p, n); + while(!available) + { + stream.wait(); + available = stream.read(p, n); + } + return available; +} + +bool SerialLauncher::Go(int timeout) +{ + uint32_t tmp; + + { + std::ostringstream rsrcOut; + app.resources.writeFork(rsrcOut); + std::string rsrc = rsrcOut.str(); + std::string& data = app.data; + + tmp = htonl(data.size()); + write(&tmp, 4); + tmp = htonl(rsrc.size()); + write(&tmp, 4); + + write(data.data(), data.size()); + write(rsrc.data(), rsrc.size()); + } + + read(&tmp, 4); + tmp = ntohl(tmp); + + return false; +} + + +void Serial::GetOptions(options_description &desc) +{ + desc.add_options() + ("serial-port", po::value()->default_value("/dev/ttyUSB0"), "serial port to use") + ; +} + +bool Serial::CheckOptions(variables_map &options) +{ + return true; +} + +std::unique_ptr Serial::MakeLauncher(variables_map &options) +{ + return std::unique_ptr(new SerialLauncher(options)); +} diff --git a/LaunchAPPL/Client/Serial.h b/LaunchAPPL/Client/Serial.h new file mode 100644 index 0000000000..f34c7a7bae --- /dev/null +++ b/LaunchAPPL/Client/Serial.h @@ -0,0 +1,16 @@ +#ifndef SERIAL_H +#define SERIAL_H + +#include "LaunchMethod.h" + +class Serial : public LaunchMethod +{ +public: + virtual std::string GetName() { return "serial"; } + virtual void GetOptions(options_description& desc); + virtual bool CheckOptions(variables_map& options); + + virtual std::unique_ptr MakeLauncher(variables_map& options); +}; + +#endif // EXECUTOR_H diff --git a/LaunchAPPL/Common/CMakeLists.txt b/LaunchAPPL/Common/CMakeLists.txt new file mode 100644 index 0000000000..93247c6e0d --- /dev/null +++ b/LaunchAPPL/Common/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(LaunchAPPLCommon + Stream.h + Stream.cc +) +target_include_directories(LaunchAPPLCommon PUBLIC .) diff --git a/LaunchAPPL/Common/Stream.cc b/LaunchAPPL/Common/Stream.cc new file mode 100644 index 0000000000..42827b7fc0 --- /dev/null +++ b/LaunchAPPL/Common/Stream.cc @@ -0,0 +1,50 @@ +#include "Stream.h" +#include + +Stream::Stream() +{ +} + +Stream::~Stream() +{ +} + +void Stream::onReceive(const uint8_t* p, size_t n) +{ + if(buffer_.empty()) + { + size_t consumed = listener_ ? listener_->onReceive(p,n) : 0; + if(consumed < n) + buffer_.insert(buffer_.end(), p + consumed, p + n); + } + else + { + buffer_.insert(buffer_.end(), p, p+n); + } + + while(!buffer_.empty()) + { + size_t consumed = listener_ ? listener_->onReceive(buffer_.data(), buffer_.size()) : 0; + buffer_.erase(buffer_.begin(), buffer_.begin()+consumed); + + if(!consumed) + break; + } +} + +long Stream::read(void *p, size_t n) +{ + if(buffer_.size() <= n) + { + size_t bufsiz = buffer_.size(); + memcpy(p, buffer_.data(), buffer_.size()); + buffer_.clear(); + return bufsiz; + } + else + { + memcpy(p, buffer_.data(), n); + buffer_.erase(buffer_.begin(), buffer_.begin()+n); + return n; + } +} diff --git a/LaunchAPPL/Common/Stream.h b/LaunchAPPL/Common/Stream.h new file mode 100644 index 0000000000..01598acf8d --- /dev/null +++ b/LaunchAPPL/Common/Stream.h @@ -0,0 +1,26 @@ +#include +#include +#include + +class StreamListener +{ +public: + virtual size_t onReceive(const uint8_t* p, size_t n) = 0; +}; + +class Stream +{ + StreamListener *listener_; + std::vector buffer_; +public: + Stream(); + virtual ~Stream(); + + void setListener(StreamListener *l) { listener_ = l; } + + virtual void write(const void* p, size_t n) = 0; + long read(void *p, size_t n); + +protected: + void onReceive(const uint8_t* p, size_t n); +}; diff --git a/LaunchAPPL/Server/CMakeLists.txt b/LaunchAPPL/Server/CMakeLists.txt index 72bf987be8..3191f4e799 100644 --- a/LaunchAPPL/Server/CMakeLists.txt +++ b/LaunchAPPL/Server/CMakeLists.txt @@ -1 +1,2 @@ add_application(LaunchAPPLServer CONSOLE main.cc) +target_link_libraries(LaunchAPPLServer LaunchAPPLCommon) diff --git a/LaunchAPPL/Server/Stream.h b/LaunchAPPL/Server/Stream.h deleted file mode 100644 index e4e4469316..0000000000 --- a/LaunchAPPL/Server/Stream.h +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include - -class Block -{ - uint8_t *begin_ = nullptr; - uint8_t *end_ = nullptr; -public: - Block() = default; - Block(uint8_t *p, size_t len) : begin_(p), end_(p+len) {} - Block(uint8_t *b, uint8_t *e) : begin_(b), end_(e) {} - - uint8_t * begin() { return begin_; } - const uint8_t * begin() const { return begin_; } - uint8_t * end() { return end_; } - const uint8_t * end() const { return end_; } - - size_t size() const { return end_ - begin_; } -}; - -class StreamListener -{ -public: - virtual void onReceive(const Block& b) = 0; -}; - -class Stream -{ - StreamListener *listener_; -public: - void setListener(StreamListener *l) { listener_ = l; } - - virtual void send(const Block& b) = 0; - -protected: - void onReceive(const Block& b) - { if(listener_) listener_->onReceive(b); } -}; diff --git a/LaunchAPPL/Server/main.cc b/LaunchAPPL/Server/main.cc index 98de49d26a..895bdfcb33 100644 --- a/LaunchAPPL/Server/main.cc +++ b/LaunchAPPL/Server/main.cc @@ -5,7 +5,8 @@ #include #include -#include "Stream.h" +#include +#include class MacSerialStream : public Stream { @@ -16,12 +17,14 @@ class MacSerialStream : public Stream short outRefNum, inRefNum; public: - virtual void send(const Block& b) override; + virtual void write(const void* p, size_t n) override; void idle(); MacSerialStream(); ~MacSerialStream(); + + void close(); }; MacSerialStream::MacSerialStream() @@ -40,30 +43,38 @@ MacSerialStream::MacSerialStream() SerReset(outRefNum, baud19200 | data8 | noParity | stop10); } -MacSerialStream::~MacSerialStream() +void MacSerialStream::close() { + if(inRefNum == 0) + return; SerSetBuf(inRefNum, NULL, 0); CloseDriver(inRefNum); CloseDriver(outRefNum); + inRefNum = outRefNum = 0; } -void MacSerialStream::send(const Block& b) +MacSerialStream::~MacSerialStream() +{ + close(); +} + +void MacSerialStream::write(const void* p, size_t n) { ParamBlockRec pb; memset(&pb, 0, sizeof(pb)); pb.ioParam.ioRefNum = outRefNum; - pb.ioParam.ioBuffer = (Ptr)b.begin(); - pb.ioParam.ioReqCount = b.size(); + pb.ioParam.ioBuffer = (Ptr)p; + pb.ioParam.ioReqCount = n; OSErr err = PBWriteSync(&pb); - } void MacSerialStream::idle() { long count = 0; SerGetBuf(inRefNum, &count); - + if(count) + printf("sergetbuf -> %ld\n", count); while(count > 0) { long count1 = count > kReadBufferSize ? kReadBufferSize : count; @@ -77,18 +88,102 @@ void MacSerialStream::idle() return; count -= count1; - onReceive(Block((uint8_t*)readBuffer, count1)); + printf("onReceive: %ld\n", count1); + onReceive((uint8_t*)readBuffer, count1); + Delay(20,nullptr); } } - +/* class DumpToConsole : public StreamListener { public: - void onReceive(const Block& b) + size_t onReceive(const uint8_t* p, size_t n) { - for(uint8_t c : b) - putchar(c); + for(int i = 0; i < n; i++) + putchar(p[i]); + return n; + } +};*/ + +class Listener : public StreamListener +{ + uint32_t size; + short refNum; +public: + enum class State + { + dataSize, + data, + rsrcSize, + rsrc, + launch, + stop + }; + State state = State::dataSize; + + + size_t onReceive(const uint8_t* p, size_t n) + { + switch(state) + { + case State::dataSize: + { + if(n < 4) + return 0; + size = *(const uint32_t*)p; + + printf("Data Size: %u\n", size); + + FSDelete("\pRetro68App", 0); + Create("\pRetro68App", 0, '????', 'APPL'); + OpenDF("\pRetro68App", 0, &refNum); + state = State::data; + return 4 + onReceive(p+4, n-4); + } + + case State::data: + { + long count = n < size ? n : size; + + FSWrite(refNum, &count, p); + if(count < size) + return count; + FSClose(refNum); + state = State::rsrcSize; + return count; + } + + case State::rsrcSize: + { + if(n < 4) + return 0; + size = *(const uint32_t*)p; + + printf("Rsrc Size: %u\n", size); + + OpenRF("\pRetro68App", 0, &refNum); + state = State::rsrc; + return 4; + } + + case State::rsrc: + { + long count = n < size ? n : size; + + FSWrite(refNum, &count, p); + if(count < size) + { + size -= count; + return count; + } + FSClose(refNum); + + + state = State::launch; + return count; + } + } } }; @@ -101,11 +196,35 @@ int main() { MacSerialStream stream; - DumpToConsole listener; + Listener listener; stream.setListener(&listener); while(!Button()) + { stream.idle(); + + if(listener.state == Listener::State::launch) + { + stream.close(); + { + LaunchParamBlockRec lpb; + memset(&lpb, 0, sizeof(lpb)); + + lpb.reserved1 = (unsigned long) "\pRetro68App"; + lpb.reserved2 = 0; + lpb.launchBlockID = extendedBlock; + lpb.launchEPBLength = 6; + lpb.launchFileFlags = 0; + lpb.launchControlFlags = 0xC000; + + printf("Launching...\n"); + OSErr err = LaunchApplication(&lpb); + printf("Still here after launch (err = %d). Press Enter to exit.\n", (int)err); + getchar(); + return 0; + } + } + } } return 0;