LaunchAPPL: serial connection

This commit is contained in:
Wolfgang Thaller 2018-04-21 00:03:29 +02:00
parent bdf4685d82
commit bafd53f2ae
11 changed files with 410 additions and 55 deletions

View File

@ -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()

View File

@ -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)

View File

@ -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.
};

168
LaunchAPPL/Client/Serial.cc Normal file
View File

@ -0,0 +1,168 @@
#include "Serial.h"
#include "Launcher.h"
#include "Utilities.h"
#include "Stream.h"
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <arpa/inet.h>
#include <poll.h>
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<std::string>();
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<std::string>()->default_value("/dev/ttyUSB0"), "serial port to use")
;
}
bool Serial::CheckOptions(variables_map &options)
{
return true;
}
std::unique_ptr<Launcher> Serial::MakeLauncher(variables_map &options)
{
return std::unique_ptr<Launcher>(new SerialLauncher(options));
}

View File

@ -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<Launcher> MakeLauncher(variables_map& options);
};
#endif // EXECUTOR_H

View File

@ -0,0 +1,5 @@
add_library(LaunchAPPLCommon
Stream.h
Stream.cc
)
target_include_directories(LaunchAPPLCommon PUBLIC .)

View File

@ -0,0 +1,50 @@
#include "Stream.h"
#include <string.h>
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;
}
}

View File

@ -0,0 +1,26 @@
#include <stdint.h>
#include <stddef.h>
#include <vector>
class StreamListener
{
public:
virtual size_t onReceive(const uint8_t* p, size_t n) = 0;
};
class Stream
{
StreamListener *listener_;
std::vector<uint8_t> 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);
};

View File

@ -1 +1,2 @@
add_application(LaunchAPPLServer CONSOLE main.cc)
target_link_libraries(LaunchAPPLServer LaunchAPPLCommon)

View File

@ -1,38 +0,0 @@
#include <stdint.h>
#include <stddef.h>
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); }
};

View File

@ -5,7 +5,8 @@
#include <Events.h>
#include <string.h>
#include "Stream.h"
#include <Stream.h>
#include <Processes.h>
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;