1
0
mirror of https://github.com/livingcomputermuseum/UniBone.git synced 2026-03-10 20:34:42 +00:00

Multiple parallel instances of device::worker() possible

This commit is contained in:
Joerg Hoppe
2019-06-23 12:00:13 +02:00
parent 3952cb93b0
commit 4062386b97
36 changed files with 1620 additions and 100 deletions

View File

@@ -55,29 +55,27 @@ list<device_c *> device_c::mydevices;
// called on cancel and exit()
static void device_worker_pthread_cleanup_handler(void *context) {
device_c *device = (device_c *) context;
device_worker_c *worker_instance = (device_worker_c *) context;
device_c *device = worker_instance->device;
#define this device // make INFO work
device->worker_terminate = false;
device->worker_terminated = true; // ended on its own or on worker_terminate
INFO("Worker terminated for device %s.", device->name.value.c_str());
device->worker_terminate = false;
device->worker_terminated = true; // ended on its own or on worker_terminate
worker_instance->running = false;
INFO("%s::worker(%d) terminated.", device->name.value.c_str(), worker_instance->instance);
// printf("cleanup for device %s\n", device->name.value.c_str()) ;
#undef this
}
static void *device_worker_pthread_wrapper(void *context) {
device_c *device = (device_c *) context;
device_worker_c *worker_instance = (device_worker_c *) context;
device_c *device = worker_instance->device;
int oldstate; // not used
#define this device // make INFO work
// call real worker
INFO("%s::worker() started", device->name.value.c_str());
INFO("%s::worker(%u) started", device->name.value.c_str(), worker_instance->instance);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldstate); //ASYNCH not allowed!
device->worker_terminate = false;
device->worker_terminated = false;
pthread_cleanup_push(device_worker_pthread_cleanup_handler, device);
device->worker();
worker_instance->running = true;
pthread_cleanup_push(device_worker_pthread_cleanup_handler, worker_instance);
device->worker(worker_instance->instance);
pthread_cleanup_pop(1); // call cleanup_handler on regular exit
// not reached on pthread_cancel()
#undef this
@@ -92,8 +90,9 @@ device_c::device_c() {
parent = NULL;
worker_terminate = false;
worker_terminated = true;
// init workers
workers_terminate = false;
set_workers_count(1); // default: 1 worker
// do not link params to this device over param constructor
// creation order of vector vs params?
@@ -128,12 +127,25 @@ device_c::~device_c() {
mydevices.erase(p);
}
// default is 1 worker. Default: null function, terminates if not overwritten by child class
// can be set > 1 if device needs multiple worker instances
// only to be called in constructors
void device_c::set_workers_count(unsigned workers_count) {
workers.resize(workers_count);
for (unsigned instance=0; instance < workers_count; instance++) {
device_worker_c *worker_instance = &workers[instance];
worker_instance->device = this;
worker_instance->instance = instance;
worker_instance->running = false;
}
}
bool device_c::on_param_changed(parameter_c *param) {
if (param == &enabled) {
if (enabled.new_value)
worker_start();
workers_start();
else
worker_stop();
workers_stop();
}
// all devices forward their "on_param_changed" to parent classes,
// until a class rejects a value.
@@ -274,20 +286,22 @@ void device_c::worker_init_realtime_priority(enum worker_priority_e priority) {
/* worker_start - executes threads
*
* use of C++11 std::thread failed:
* thead.join() crashes with random system_errors
* thread.join() crashes with random system_errors
* So use classic "pthread" wrapper
* TODO: crash still there, was caused by cross compile -> back to C++11 threads!
*/
void device_c::worker_start(void) {
worker_terminate = false;
{
void device_c::workers_start(void) {
workers_terminate = false;
for (unsigned instance = 0; instance < workers.size(); instance++) {
device_worker_c *worker_instance = &workers[instance];
worker_instance->running = true;
// start pthread
pthread_attr_t attr;
pthread_attr_init(&attr);
// pthread_attr_setstacksize(&attr, 1024*1024);
assert(worker_terminated); // do not srtat device worker twiche in parallel!
int status = pthread_create(&worker_pthread, &attr, &device_worker_pthread_wrapper,
(void *) this);
int status = pthread_create(&worker_instance->pthread, &attr,
&device_worker_pthread_wrapper, (void *) worker_instance);
if (status != 0) {
FATAL("Failed to create pthread with status = %d", status);
}
@@ -295,32 +309,36 @@ void device_c::worker_start(void) {
}
}
void device_c::worker_stop(void) {
void device_c::workers_stop(void) {
timeout_c timeout;
int status;
if (worker_terminated) {
DEBUG("%s.worker_stop(): already terminated.", name.name.c_str());
return;
}
INFO("Waiting for %s.worker() to stop ...", name.value.c_str());
worker_terminate = true;
// 100ms
timeout.wait_ms(100);
// worker_wrapper must do "worker_terminated = true;" on exit
if (!worker_terminated) {
// if thread is hanging in pthread_cond_wait(): send a cancellation request
status = pthread_cancel(worker_pthread);
if (status != 0)
FATAL("Failed to send cancellation request to worker_pthread with status = %d",
status);
}
// !! If crosscompling: this causes a crash in the worker thread
// !! at pthread_cond_wait() or other cancellation points.
// !! No problem for compiles build on BBB itself.
status = pthread_join(worker_pthread, NULL);
if (status != 0) {
FATAL("Failed to join worker_pthread with status = %d", status);
workers_terminate = true; // global signal to all instances
timeout.wait_ms(100);
for (unsigned instance = 0; instance < workers.size(); instance++) {
device_worker_c *worker_instance = &workers[instance];
// if (!worker_instance->running) {
// DEBUG("%s.worker_stop(%u): already terminated.", name.name.c_str(), instance);
// return;
// }
if (worker_instance->running) {
INFO("%s.worker(%u) not cooperative: cancel it ...", name.value.c_str(), instance);
// if thread is hanging in pthread_cond_wait(): send a cancellation request
status = pthread_cancel(worker_instance->pthread);
if (status != 0)
FATAL("Failed to send cancellation request to worker_pthread with status = %d",
status);
}
// !! If crosscompling: this causes a crash in the worker thread
// !! at pthread_cond_wait() or other cancellation points.
// !! No problem for compiles build on BBB itself.
status = pthread_join(worker_instance->pthread, NULL);
if (status != 0) {
FATAL("Failed to join worker_pthread with status = %d", status);
}
}
}

View File

@@ -32,17 +32,28 @@
#include <mutex>
using namespace std;
#include "utils.hpp"
#include "parameter.hpp"
#include "logsource.hpp"
// instance of a worker thread for a device
class device_c;
class device_worker_c {
public:
// thread for this worker instance
device_c *device; // link to parent
unsigned instance; // id of this running instance
pthread_t pthread;bool running; // run state
};
// abstract unibus device
// maybe mass storage controller, storage drive or other device
// sets device register values depending on internal status,
// reacts on register read/write over UNIBUS by evaluation of PRU events.
class device_c: public logsource_c, public parameterized_c {
private:
void worker_start(void);
void worker_stop(void);
void workers_start(void);
void workers_stop(void);
public:
// the class holds a list of pointers to instantiated devices
@@ -64,14 +75,14 @@ public:
"device installed and ready to use?");
parameter_unsigned_c emulation_speed = parameter_unsigned_c(NULL, "emulation_speed", "es",
false, "", "%d", "1 = original speed, > 1: mechanics is this factor faster", 8, 10);
false, "", "%d", "1 = original speed, > 1: mechanics is this factor faster", 8, 10);
// 1 = original speed, > 1: mechanics is this factor faster
parameter_unsigned_c verbosity = parameter_unsigned_c(NULL, "verbosity", "v", false, "",
"%d", "1 = fatal, 2 = error, 3 = warning, 4 = info, 5 = debug", 8, 10);
// make data exchange with worker atomic
std::mutex worker_mutex;
// std::mutex worker_mutex;
// scheduler settings for worker thread
int worker_sched_policy;
@@ -86,8 +97,9 @@ public:
void worker_boost_realtime_priority(void);
void worker_restore_realtime_priority(void);
device_c();
device_c(void);
virtual ~device_c(); // class with virtual functions should have virtual destructors
void set_workers_count(unsigned workers_count);
virtual bool on_param_changed(parameter_c *param);
@@ -103,12 +115,19 @@ public:
volatile bool init_asserted;
virtual void on_init_changed(void) = 0; // reset device, like UNIBUS INIT
// worker thread
volatile bool worker_terminate; // cmd flag to worker()
volatile bool worker_terminated; // ACK flag from worker()
// worker threads: multiple instances of single worker() are running in parallel
// device must implement a worker(instance) {
// switch((instance) { ... } }
// 'instance' from 0 .. worker_count-1
volatile bool workers_terminate; // cmd flag to all worker() instances
pthread_t worker_pthread;
virtual void worker(void) = 0; // background worker function
vector<device_worker_c> workers;
// default background worker function for devices without need.
virtual void worker(unsigned instance) {
UNUSED(instance);
printf("Warning: default device_c::worker() called, better use set_worker_count(0) ") ;
}
};
#endif

View File

@@ -102,8 +102,6 @@ public:
}
virtual void on_init_changed(void) {
}
virtual void worker(void) {
}
void test(void);
};

View File

@@ -246,17 +246,18 @@ void unibusadapter_c::worker_deviceregister_event() {
}
// runs in background, catches and distributes PRU events
void unibusadapter_c::worker() {
void unibusadapter_c::worker(unsigned instance) {
UNUSED(instance) ; // only one
int res;
// set thread priority to MAX.
// - fastest response to slect() call in prussdrv_pru_wait_event_timeout()
// - fastest response to select() call in prussdrv_pru_wait_event_timeout()
// (minimal I/O latency)
// - not interrupted by other tasks while running
// check with tool "top" or "htop".
worker_init_realtime_priority(rt_max); // set to max prio
while (!worker_terminate) {
while (!workers_terminate) {
// Timing:
// This is THE ONE mechanism where "realtime meets Linux"
// To respond to the PRU signal, Linux must wake up schedule this thread
@@ -368,7 +369,7 @@ bool unibusadapter_c::register_device(unibusdevice_c& device) {
while (device_handle <= MAX_DEVICE_HANDLE && devices[device_handle] != NULL)
device_handle++;
if (device_handle > MAX_DEVICE_HANDLE) {
ERROR("Tried to register more than %u devices!", MAX_DEVICE_HANDLE);
ERROR("register_device() Tried to register more than %u devices!", MAX_DEVICE_HANDLE);
return false;
}
devices[device_handle] = &device;
@@ -383,7 +384,7 @@ bool unibusadapter_c::register_device(unibusdevice_c& device) {
unibusdevice_register_t *device_reg = &(device.registers[i]);
device_reg->addr = device.base_addr.value + 2 * i;
if ( IOPAGE_REGISTER_ENTRY(*deviceregisters,device_reg->addr)!= 0 )
FATAL("IO page address conflict: %s implements register at %06o, belongs already to other device.",
FATAL("register_device() IO page address conflict: %s implements register at %06o, belongs already to other device.",
device.name.value.c_str(), device_reg->addr);
}
@@ -404,7 +405,7 @@ bool unibusadapter_c::register_device(unibusdevice_c& device) {
register_handle = i;
unsigned free_handles = MAX_REGISTER_COUNT - register_handle - 1;
if (free_handles < device.register_count) {
ERROR("Can not register device %s, needs %d register, only %d left.",
ERROR("register_device() can not register device %s, needs %d register, only %d left.",
device.name.value.c_str(), device.register_count, free_handles);
return false;
}
@@ -429,7 +430,7 @@ bool unibusadapter_c::register_device(unibusdevice_c& device) {
if (device_reg->active_on_dati || device_reg->active_on_dato) {
if (device_reg->active_on_dati && !device_reg->active_on_dato) {
FATAL(
"Register configuration error for device %s, register idx %u:\n"
"register_device() Register configuration error for device %s, register idx %u:\n"
"A device register may not be passive on DATO and active on DATI.\n"
"Passive DATO -> value written only saved in shared UNIBUS reg value\n"
"Active DATI: shared UNIBUS reg value updated from flipflops -> DATO value overwritten\n"

View File

@@ -133,7 +133,7 @@ public:
void worker_init_event(void);
void worker_power_event(void);
void worker_deviceregister_event(void);
void worker(void) override; // background worker function
void worker(unsigned instance) override; // background worker function
void dma_worker(void); // background DMA worker
bool register_device(unibusdevice_c& device);

View File

@@ -161,6 +161,10 @@ void unibusdevice_c::reset_unibus_registers() {
}
// set an UNIBUS interrupt condition with intr_vector and intr_level
void unibusdevice_c::interrupt(unsigned vector, unsigned level) {
unibusadapter->request_INTR(vector, level);
}
void unibusdevice_c::interrupt(void) {
// delegate to unibusadapter_c
unibusadapter->request_INTR(intr_level.value, intr_vector.value);

View File

@@ -131,8 +131,9 @@ public:
unibusdevice_register_t *register_by_name(string name);
unibusdevice_register_t *register_by_unibus_address(uint32_t addr);
// set an UNIBUS interrupt condition with intr_vector and intr_level
// set an UNIBUS interrupt condition with parameters intr_vector and intr_level
void interrupt(void);
void interrupt(unsigned vector, unsigned level) ;
// callback to be called on controller register DATI/DATO events.
// must ACK mailbox.event.signal. Asynchron!

View File

@@ -40,6 +40,9 @@
#define MILLION 1000000L
#define BILLION (1000L * MILLION)
#define BIT(n) (1 << (n))
#ifndef _UTILS_CPP_
extern volatile int SIGINTreceived;
#endif

View File

@@ -105,14 +105,15 @@ int cpu_dati(unsigned addr, unsigned *data) {
}
// background worker.
void cpu_c::worker(void) {
void cpu_c::worker(unsigned instance) {
UNUSED(instance) ; // only one
timeout_c timeout;
bool nxm;
unsigned pc = 0;
unsigned dr = 0760102;
unsigned opcode = 0;
(void) opcode;
while (!worker_terminate) {
while (!workers_terminate) {
// run full speed!
timeout.wait_us(1);

View File

@@ -57,7 +57,7 @@ public:
struct KA11 ka11; // Angelos CPU state
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
// called by unibusadapter on emulated register access
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)

View File

@@ -182,9 +182,10 @@ void demo_io_c::gpio_set_output(unsigned output_index, unsigned value) {
// background worker.
// udpate LEDS, poll switches direct to register flipflops
void demo_io_c::worker(void) {
void demo_io_c::worker(unsigned instance) {
UNUSED(instance) ; // only one
timeout_c timeout;
while (!worker_terminate) {
while (!workers_terminate) {
timeout.wait_ms(100);
unsigned i;

View File

@@ -58,7 +58,7 @@ public:
false, "1 = hard wire Switches to LEDs, PDP-11 can not set LEDs");
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
// called by unibusadapter on emulated register access
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)

View File

@@ -80,9 +80,10 @@ bool demo_regs_c::on_param_changed(parameter_c *param) {
// background worker.
// Just print a heart beat
void demo_regs_c::worker(void) {
void demo_regs_c::worker(unsigned instance) {
UNUSED(instance) ; // only one
timeout_c timeout;
while (!worker_terminate) {
while (!workers_terminate) {
timeout.wait_ms(1000);
cout << ".";
}

View File

@@ -43,7 +43,7 @@ public:
bool on_param_changed(parameter_c *param) override; // must implement
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
// called by unibusadapter on emulated register access
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)

View File

@@ -0,0 +1,355 @@
/* DL11W.cpp: sample UNIBUS controller with SLU & LTC logic
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12-nov-2018 JH entered beta phase
20/12/2018 djrm copied to make slu device
14/01/2019 djrm adapted to use UART2 serial port
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include "unibusadapter.hpp"
#include "unibusdevice.hpp" // definition of class device_c
#include "unibus.h"
#include "dl11w.hpp"
#include "gpios.hpp"
#include "rs232.hpp"
char buffer[BUFLEN + 1];
//-------------------------------------------------
slu_c::slu_c() :
unibusdevice_c() // super class constructor
{
//ip_host.value = IP_HOST; // not used
//ip_port.value = IP_PORT; // not used
// static config
name.value = "SLU";
type_name.value = "slu_c";
log_label = "slu";
set_default_bus_params(SLU_ADDR, SLU_VECTOR + 4, SLU_LEVEL); // base addr, intr-vector, intr level
// init parameters
// controller has some register
register_count = slu_idx_count;
reg_rcsr = &(this->registers[slu_idx_rcsr]); // @ base addr
strcpy(reg_rcsr->name, "RCSR"); // Receiver Status Register
reg_rcsr->active_on_dati = true;
reg_rcsr->active_on_dato = true;
reg_rcsr->reset_value = 0 & ! RCSR_RCVR_DONE;
reg_rcsr->writable_bits = 0xff;
reg_rbuf = &(this->registers[slu_idx_rbuf]); // @ base addr
strcpy(reg_rbuf->name, "RBUF"); // Receiver Buffer Register
reg_rbuf->active_on_dati = true; // no controller state change
reg_rbuf->active_on_dato = true;
reg_rbuf->reset_value = 0;
reg_rbuf->writable_bits = 0xff;
reg_xcsr = &(this->registers[slu_idx_xcsr]); // @ base addr
strcpy(reg_xcsr->name, "XCSR"); // Transmitter Status Register
reg_xcsr->active_on_dati = true;
reg_xcsr->active_on_dato = true;
reg_xcsr->reset_value = XCSR_XMIT_RDY; // set
reg_xcsr->writable_bits = 0xff;
reg_xbuf = &(this->registers[slu_idx_xbuf]); // @ base addr
strcpy(reg_xbuf->name, "XBUF"); //Transmitter Buffer Register
reg_xbuf->active_on_dati = true; // no controller state change
reg_xbuf->active_on_dato = true;
reg_xbuf->reset_value = 0;
reg_xbuf->writable_bits = 0xff;
// initialize serial format
baudrate.value = 9600;
mode.value = "8N1";
}
slu_c::~slu_c() {
}
bool slu_c::on_param_changed(parameter_c *param) {
if (param == &enabled) {
cport_nr = 2; /* UART2 */
if (enabled.new_value) {
// enable SLU: setup COM port
if (RS232_OpenComport(cport_nr, baudrate.value, mode.value.c_str())) {
ERROR("Can not open comport");
return false; // reject "enable"
}
// lock serial format settings
baudrate.readonly = true;
mode.readonly = true;
RS232_cputs(cport_nr, "Comport opened\n\r");
} else {
// disable SLU
RS232_CloseComport(cport_nr);
// lock serial format settings
baudrate.readonly = false;
mode.readonly = false;
}
}
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
//--------------------------------------------
// background worker.
void slu_c::worker(void) {
timeout_c timeout;
char mychar;
int i, n;
while (!worker_terminate) {
timeout.wait_ms(SLU_MSRATE_MS);
/* read character from socket */
if (!(RCSR_RCVR_DONE & rcsr.value)) {
bzero(buffer, BUFLEN);
#if 1
if (slu_maint.value) {
n = 1;
buffer[0] = rbuf.value;
buffer[1] = 0;
} else
#endif
{
/* read serial data, if any */
n = RS232_PollComport(cport_nr, (unsigned char*) buffer, 1);
}
for (i = 0; i < n; i++) {
mychar = buffer[i];
// transmit chr to bus
rbuf.value = mychar;
set_register_dati_value(reg_rbuf, rbuf.value, __func__);
// signal data on bus ready to read
rcsr.value |= RCSR_RCVR_DONE;
set_register_dati_value(reg_rcsr, rcsr.value, __func__);
}
}
// transfer received character to socket
if (!(XCSR_XMIT_RDY & xcsr.value)) {
buffer[0] = get_register_dato_value(reg_xbuf);
RS232_SendByte(cport_nr, buffer[0]);
// signal data written
xcsr.value |= XCSR_XMIT_RDY;
set_register_dati_value(reg_xcsr, xcsr.value, __func__);
}
}
}
// process DATI/DATO access to one of my "active" registers
// !! called asynchronuously by PRU, with SSYN asserted and blocking UNIBUS.
// The time between PRU event and program flow into this callback
// is determined by ARM Linux context switch
//
// UNIBUS DATO cycles let dati_flipflops "flicker" outside of this proc:
// do not read back dati_flipflops.
void slu_c::on_after_register_access(unibusdevice_register_t *device_reg,
uint8_t unibus_control) {
if (unibus_control == UNIBUS_CONTROL_DATO) // bus write
set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__);
switch (device_reg->index) {
case slu_idx_rcsr:
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write
rcvr_interrupt_enable.value = !!(reg_rcsr->active_dato_flipflops
& (RCSR_RCVR_INT_ENB));
rdr_enable.value = !!(reg_rcsr->active_dato_flipflops & (RCSR_RDR_ENB));
}
break;
case slu_idx_xbuf:
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write
// signal data has been written to bus
xcsr.value &= ~ XCSR_XMIT_RDY;
set_register_dati_value(reg_xcsr, xcsr.value, __func__);
// get value from bus write and put it into rx buffer
xbuf.value = reg_xbuf->active_dato_flipflops;
}
break;
case slu_idx_xcsr:
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write
xmit_interrupt_enable.value = !!(reg_xcsr->active_dato_flipflops
& (XCSR_XMIT_INT_ENB));
slu_maint.value = !!(reg_xcsr->active_dato_flipflops & (XCSR_MAINT));
}
break;
case slu_idx_rbuf:
if (unibus_control == UNIBUS_CONTROL_DATI) { // bus read
// signal data has been read from bus
rcsr.value &= ~ RCSR_RCVR_DONE;
set_register_dati_value(reg_rcsr, rcsr.value, __func__);
}
break;
default:
break;
}
}
void slu_c::on_power_changed(void) {
if (power_down) { // power-on defaults
}
}
// UNIBUS INIT: clear all registers
void slu_c::on_init_changed(void) {
// write all registers to "reset-values"
if (init_asserted) {
reset_unibus_registers();
INFO("slu_c::on_init()");
}
}
//--------------------------------------------------------------------------------------------------
ltc_c::ltc_c() :
unibusdevice_c() // super class constructor
{
// static config
name.value = "LTC";
type_name.value = "ltc_c";
log_label = "ltc";
set_default_bus_params(LTC_ADDR, LTC_VECTOR, LTC_LEVEL); // base addr, intr-vector, intr level
// init parameters
// controller has some register
register_count = ltc_idx_count;
reg_lks = &(this->registers[ltc_idx_lks]); // @ base addr
strcpy(reg_lks->name, "LKS"); // Line Clock Status Register
reg_lks->active_on_dati = true; // no controller state change
reg_lks->active_on_dato = true;
reg_lks->reset_value = 0;
reg_lks->writable_bits = 0xff;
}
ltc_c::~ltc_c() {
}
bool ltc_c::on_param_changed(parameter_c *param) {
// no own parameter or "enable" logic here
return unibusdevice_c::on_param_changed(param); // more actions (for enable)
}
// background worker.
void ltc_c::worker(void) {
timeout_c timeout;
while (!worker_terminate) {
if (ltc_input.value) {
//should really wait for LTC input trailing edge here
timeout.wait_ms(10000);
} else
timeout.wait_ms(LTC_MSRATE_MS);
#if 0
printf("[%o",buslatches_getval[0]);
#endif
if (lke.value) {
lks.value |= LKS_INT_MON;
set_register_dati_value(reg_lks, lks.value, __func__);
}
}
}
// process DATI/DATO access to one of my "active" registers
void ltc_c::on_after_register_access(unibusdevice_register_t *device_reg,
uint8_t unibus_control) {
if (unibus_control == UNIBUS_CONTROL_DATO) // bus write
set_register_dati_value(device_reg, device_reg->active_dato_flipflops, __func__);
switch (device_reg->index) {
case ltc_idx_lks:
if (unibus_control == UNIBUS_CONTROL_DATI) { // bus read
// signal data has been read from bus
lks.value &= ~ LKS_INT_MON;
set_register_dati_value(reg_lks, lks.value, __func__);
}
if (unibus_control == UNIBUS_CONTROL_DATO) { // bus write
lks.value = reg_lks->active_dato_flipflops;
ltc_interrupt_enable.value = !!(reg_lks->active_dato_flipflops & (LKS_INT_ENB));
#if 0
if (reg_lks->active_dato_flipflops && 1)
{
interrupt();
//DEBUG("Interrupt!");
}
#endif
}
break;
default:
break;
}
}
void ltc_c::on_power_changed(void) {
if (power_down) { // power-on defaults
}
}
// UNIBUS INIT: clear all registers
void ltc_c::on_init_changed(void) {
// write all registers to "reset-values"
if (init_asserted) {
reset_unibus_registers();
INFO("ltc_c::on_init()");
}
}

View File

@@ -0,0 +1,188 @@
/* DL11W.hpp: sample UNIBUS controller with SLU & LTC logic
Copyright (c) 2018, Joerg Hoppe
j_hoppe@t-online.de, www.retrocmp.com
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
JOERG HOPPE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12-nov-2018 JH entered beta phase
20/12/2018 djrm copied to make DL11-W device
*/
#ifndef _DL11W_HPP_
#define _DL11W_HPP_
#include <fstream>
using namespace std;
#include "utils.hpp"
#include "unibusdevice.hpp"
#include "parameter.hpp"
// socket console settings
//#define IP_PORT 5001
//#define IP_HOST "localhost"
// bus properties
#define DL11A 1
#if DL11A // console (teletype keyboard & printer)
#define SLU_ADDR 0777560
#define SLU_LEVEL 04
#define SLU_VECTOR 060
#elif DL11B // paper tape punch and reader
#define SLU_ADDR 0777550
#define SLU_LEVEL 04
#define SLU_VECTOR 070
#else // other serial device
#define SLU_ADDR 0776500
#define SLU_LEVEL 04
#define SLU_VECTOR 0300
#endif
//#define LTC_ADDR 0777546
// moved here to avoid clash with physical LTC installed
#define LTC_ADDR 0777544
#define LTC_LEVEL 06
#define LTC_VECTOR 0100
// global text buffer size for hostname etc
#define BUFLEN 32
// register bit definitions
#define RCSR_RCVR_ACT 004000
#define RCSR_RCVR_DONE 000200
#define RCSR_RCVR_INT_ENB 000100
#define RCSR_RDR_ENB 000001
#define RBUF_ERROR (1 << 15)
#define RBUF_OR_ERR (1 << 14)
#define RBUF_FR_ERR (1 << 13)
#define RBUF_P_ERR (1 << 12)
#define XCSR_XMIT_RDY 000200
#define XCSR_XMIT_INT_ENB 000100
#define XCSR_MAINT 000004
#define XCSR_BREAK 000001
#define LKS_INT_ENB 000100
#define LKS_INT_MON 000200
// background task sleep times
#define SLU_MSRATE_MS 10
#define LTC_MSRATE_MS 50
// unibus register indices
enum slu_reg_index {
slu_idx_rcsr = 0, slu_idx_rbuf, slu_idx_xcsr, slu_idx_xbuf, slu_idx_count,
};
enum ltc_reg_index {
ltc_idx_lks = 0, ltc_idx_count,
};
// ------------------------------------------ SLU -----------------------------
class slu_c: public unibusdevice_c {
private:
int cport_nr; // COM port handle for RS232 library
unibusdevice_register_t *reg_rcsr;
unibusdevice_register_t *reg_rbuf;
unibusdevice_register_t *reg_xcsr;
unibusdevice_register_t *reg_xbuf;
public:
slu_c();
~slu_c();
//parameter_string_c ip_host = parameter_string_c( this, "SLU socket IP host", "host", /*readonly*/ false, "ip hostname");
//parameter_unsigned_c ip_port = parameter_unsigned_c(this, "SLU socket IP port", "port", /*readonly*/ false, "", "%d", "ip port", 32, 10);
parameter_unsigned_c baudrate = parameter_unsigned_c(this, "baudrate", "b", /*readonly*/
false, "", "%d", "Baudrate: 110, 300, ... 115200", 115200, 10);
parameter_string_c mode = parameter_string_c(this, "mode", "m", /*readonly*/false,
"Mode: 8N1, 7E1, ... ");
// @David: duplicating device registers as parameters is not necessary ...
// they can be seen with "exam" anyhow.
parameter_unsigned_c rcsr = parameter_unsigned_c(this, "Receiver Status Register", "rcsr", /*readonly*/
false, "", "%o", "Internal state", 32, 8);
parameter_unsigned_c rbuf = parameter_unsigned_c(this, "Receiver Buffer Register", "rbuf", /*readonly*/
false, "", "%o", "Internal state", 32, 8);
parameter_unsigned_c xcsr = parameter_unsigned_c(this, "Transmitter Status Register",
"xcsr", /*readonly*/false, "", "%o", "Internal state", 32, 8);
parameter_unsigned_c xbuf = parameter_unsigned_c(this, "Transmitter Buffer Register",
"xbuf", /*readonly*/false, "", "%o", "Internal state", 32, 8);
parameter_bool_c xmit_interrupt_enable = parameter_bool_c(this, "XMIT interrupt enable",
"xie",/*readonly*/false, "1 = enable interrupt");
parameter_bool_c rcvr_interrupt_enable = parameter_bool_c(this, "RCVR interrupt enable",
"rie",/*readonly*/false, "1 = enable interrupt");
parameter_bool_c slu_maint = parameter_bool_c(this, "XCSR Maintenance", "maint",/*readonly*/
false, "1 = enable Maintenance mode enabled");
parameter_bool_c rdr_enable = parameter_bool_c(this, "RDR enable", "rdre",/*readonly*/false,
"1 = enable reader enable");
// background worker function
void worker(void) override;
// called by unibusadapter on emulated register access
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
override;
bool on_param_changed(parameter_c *param) override; // must implement
void on_power_changed(void) override;
void on_init_changed(void) override;
};
//-------------------------------------------- LTC -------------------------------------
class ltc_c: public unibusdevice_c {
private:
unibusdevice_register_t *reg_lks;
public:
ltc_c();
~ltc_c();
parameter_unsigned_c lks = parameter_unsigned_c(this, "Line Clock Status Register", "lks", /*readonly*/
false, "", "%o", "Internal state", 32, 8);
parameter_bool_c lke = parameter_bool_c(this, "LKS timer enable", "lke",/*readonly*/false,
"1 = enable update of LKS_IMON by timer");
parameter_bool_c ltc_input = parameter_bool_c(this, "LTC input enable", "ltc",/*readonly*/
false, "1 = enable update of LKS_IMON by LTC Input");
parameter_bool_c ltc_interrupt_enable = parameter_bool_c(this, "LTC interrupt enable",
"lie",/*readonly*/false, "1 = enable interrupt");
// background worker function
void worker(void) override;
// called by unibusadapter on emulated register access
void on_after_register_access(unibusdevice_register_t *device_reg, uint8_t unibus_control)
override;
bool on_param_changed(parameter_c *param) override; // must implement
void on_power_changed(void) override;
void on_init_changed(void) override;
/*
void change_state(unsigned new_state);
void do_command_done(void);
*/
};
#endif

View File

@@ -27,6 +27,7 @@ using namespace std;
mscp_drive_c::mscp_drive_c(storagecontroller_c *controller, uint32_t driveNumber) :
storagedrive_c(controller), _useImageSize(false) {
set_workers_count(0) ; // needs no worker()
log_label = "MSCPD";
SetDriveType("RA81");
SetOffline();
@@ -304,10 +305,6 @@ bool mscp_drive_c::SetDriveType(const char* typeName) {
// worker():
// worker method for this drive. No work is necessary.
//
void mscp_drive_c::worker(void) {
// Nothing to do here at the moment. Thread will terminate immediately.
}
//
// on_power_changed():
// Handle power change notifications.

View File

@@ -48,8 +48,6 @@ public:
void on_power_changed(void) override;
void on_init_changed(void) override;
void worker(void) override;
public:
parameter_bool_c use_image_size = parameter_bool_c(this, "useimagesize", "uis", false,
"Determine unit size from image file instead of drive type");

View File

@@ -69,6 +69,7 @@ mscp_server::mscp_server(
polling_mutex(PTHREAD_MUTEX_INITIALIZER),
_credits(INIT_CREDITS)
{
set_workers_count(0) ; // no std worker()
name.value = "mscp_server" ;
type_name.value = "mscp_server_c" ;
log_label = "MSSVR" ;

View File

@@ -153,7 +153,6 @@ public:
public:
void on_power_changed(void) override {}
void on_init_changed(void) override {}
void worker(void) override {}
private:
uint32_t Abort(void);

View File

@@ -438,10 +438,11 @@ void paneldriver_c::i2c_sync_all_params() {
Query all used I2C chip register,
Update controls and parameters
*/
void paneldriver_c::worker(void) {
void paneldriver_c::worker(unsigned instance) {
UNUSED(instance) ; // only one
timeout_c timeout;
while (!worker_terminate) {
while (!workers_terminate) {
// poll in endless round
i2c_sync_all_params();
timeout.wait_ms(10);

View File

@@ -112,7 +112,7 @@ public:
void i2c_sync_all_params();
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
void on_power_changed(void) override; // must implement
void on_init_changed(void) override; // must implement

View File

@@ -223,7 +223,8 @@ void rk05_c::drive_reset(void) {
// SCP change will be posted when the seek instigated above is completed.
}
void rk05_c::worker(void) {
void rk05_c::worker(unsigned instance) {
UNUSED(instance) ; // only one
timeout_c timeout;
while (true) {

View File

@@ -100,7 +100,7 @@ public:
void on_init_changed(void) override;
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
};
#endif

View File

@@ -197,8 +197,9 @@ void rk11_c::dma_transfer(DMARequest &request)
// Background worker.
// Handle device operations.
void rk11_c::worker(void)
void rk11_c::worker(unsigned instance)
{
UNUSED(instance) ; // only one
worker_init_realtime_priority(rt_device);
@@ -207,7 +208,7 @@ void rk11_c::worker(void)
bool do_interrupt = true;
timeout_c timeout;
while (!worker_terminate)
while (!workers_terminate)
{
switch (_worker_state)
{

View File

@@ -155,7 +155,7 @@ public:
virtual ~rk11_c();
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
// called by unibusadapter on emulated register access
void on_after_register_access(

View File

@@ -633,13 +633,14 @@ bool RL0102_c::cmd_write_next_sector_data(uint16_t *buffer, unsigned buffer_size
}
// thread
void RL0102_c::worker(void) {
void RL0102_c::worker(unsigned instance) {
UNUSED(instance) ; // only one
timeout_c timeout;
// set prio to RT, but less than RL11 controller
worker_init_realtime_priority(rt_device);
while (!worker_terminate) {
while (!workers_terminate) {
// worker_mutex.lock() ; // collision with cmd_seek() and on_xxx_changed()
// states have set error flags not in RL11 CSR: just update
update_status_word(drive_ready_line, drive_error_line);

View File

@@ -187,7 +187,7 @@ public:
void clear_error_register(void);
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
};
#endif

View File

@@ -801,13 +801,14 @@ void RL11_c::state_readwrite() {
// thread
// excutes commands
void RL11_c::worker(void) {
void RL11_c::worker(unsigned instance) {
UNUSED(instance) ; // only one
assert(!pthread_mutex_lock(&on_after_register_access_mutex));
// set prio to RT, but less than unibus_adapter
worker_init_realtime_priority(rt_device);
while (!worker_terminate) {
while (!workers_terminate) {
/* process command state machine in parallel with
"active register" state changes
*/

View File

@@ -102,7 +102,7 @@ public:
void reset(void);
// background worker function
void worker(void) override;
void worker(unsigned instance) override;
RL0102_c *selected_drive(void);

View File

@@ -0,0 +1,823 @@
/*
***************************************************************************
*
* Author: Teunis van Beelen
*
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Teunis van Beelen
*
* Email: teuniz@gmail.com
*
***************************************************************************
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***************************************************************************
*/
/* Last revision: November 22, 2017 */
/* For more info and how to use this library, visit: http://www.teuniz.net/RS-232/ */
#include "rs232.hpp"
#if defined(__linux__) || defined(__FreeBSD__) /* Linux & FreeBSD */
#define RS232_PORTNR 38
int Cport[RS232_PORTNR],
error;
struct termios new_port_settings,
old_port_settings[RS232_PORTNR];
const char *comports[RS232_PORTNR]={
// "/dev/ttyO0","/dev/ttyO1","/dev/ttyO2","/dev/ttyO3","/dev/ttyO4","/dev/ttyO5",
"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3","/dev/ttyS4","/dev/ttyS5",
"/dev/ttyS6","/dev/ttyS7","/dev/ttyS8","/dev/ttyS9","/dev/ttyS10","/dev/ttyS11",
"/dev/ttyS12","/dev/ttyS13","/dev/ttyS14","/dev/ttyS15","/dev/ttyUSB0",
"/dev/ttyUSB1","/dev/ttyUSB2","/dev/ttyUSB3","/dev/ttyUSB4","/dev/ttyUSB5",
"/dev/ttyAMA0","/dev/ttyAMA1","/dev/ttyACM0","/dev/ttyACM1",
"/dev/rfcomm0","/dev/rfcomm1","/dev/ircomm0","/dev/ircomm1",
"/dev/cuau0","/dev/cuau1","/dev/cuau2","/dev/cuau3",
"/dev/cuaU0","/dev/cuaU1","/dev/cuaU2","/dev/cuaU3"};
// returns 0 on success, else error
int RS232_OpenComport(int comport_number, int baudrate, const char *mode)
{
int baudr,
status;
if((comport_number>=RS232_PORTNR)||(comport_number<0))
{
printf("illegal comport number\n");
return(1);
}
switch(baudrate)
{
case 50 : baudr = B50;
break;
case 75 : baudr = B75;
break;
case 110 : baudr = B110;
break;
case 134 : baudr = B134;
break;
case 150 : baudr = B150;
break;
case 200 : baudr = B200;
break;
case 300 : baudr = B300;
break;
case 600 : baudr = B600;
break;
case 1200 : baudr = B1200;
break;
case 1800 : baudr = B1800;
break;
case 2400 : baudr = B2400;
break;
case 4800 : baudr = B4800;
break;
case 9600 : baudr = B9600;
break;
case 19200 : baudr = B19200;
break;
case 38400 : baudr = B38400;
break;
case 57600 : baudr = B57600;
break;
case 115200 : baudr = B115200;
break;
case 230400 : baudr = B230400;
break;
case 460800 : baudr = B460800;
break;
case 500000 : baudr = B500000;
break;
case 576000 : baudr = B576000;
break;
case 921600 : baudr = B921600;
break;
case 1000000 : baudr = B1000000;
break;
case 1152000 : baudr = B1152000;
break;
case 1500000 : baudr = B1500000;
break;
case 2000000 : baudr = B2000000;
break;
case 2500000 : baudr = B2500000;
break;
case 3000000 : baudr = B3000000;
break;
case 3500000 : baudr = B3500000;
break;
case 4000000 : baudr = B4000000;
break;
default : printf("invalid baudrate\n");
return(1);
break;
}
int cbits=CS8,
cpar=0,
ipar=IGNPAR,
bstop=0;
if(strlen(mode) != 3)
{
printf("invalid mode \"%s\"\n", mode);
return(1);
}
switch(mode[0])
{
case '8': cbits = CS8;
break;
case '7': cbits = CS7;
break;
case '6': cbits = CS6;
break;
case '5': cbits = CS5;
break;
default : printf("invalid number of data-bits '%c'\n", mode[0]);
return(1);
break;
}
switch(mode[1])
{
case 'N':
case 'n': cpar = 0;
ipar = IGNPAR;
break;
case 'E':
case 'e': cpar = PARENB;
ipar = INPCK;
break;
case 'O':
case 'o': cpar = (PARENB | PARODD);
ipar = INPCK;
break;
default : printf("invalid parity '%c'\n", mode[1]);
return(1);
break;
}
switch(mode[2])
{
case '1': bstop = 0;
break;
case '2': bstop = CSTOPB;
break;
default : printf("invalid number of stop bits '%c'\n", mode[2]);
return(1);
break;
}
/*
http://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html
http://man7.org/linux/man-pages/man3/termios.3.html
*/
Cport[comport_number] = open(comports[comport_number], O_RDWR | O_NOCTTY | O_NDELAY);
if(Cport[comport_number]==-1)
{
perror("unable to open comport ");
return(1);
}
/* lock access so that another process can't also use the port */
if(flock(Cport[comport_number], LOCK_EX | LOCK_NB) != 0)
{
close(Cport[comport_number]);
perror("Another process has locked the comport.");
return(1);
}
error = tcgetattr(Cport[comport_number], old_port_settings + comport_number);
if(error==-1)
{
close(Cport[comport_number]);
flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */
perror("unable to read portsettings ");
return(1);
}
memset(&new_port_settings, 0, sizeof(new_port_settings)); /* clear the new struct */
new_port_settings.c_cflag = cbits | cpar | bstop | CLOCAL | CREAD;
new_port_settings.c_iflag = ipar;
new_port_settings.c_oflag = 0;
new_port_settings.c_lflag = 0;
new_port_settings.c_cc[VMIN] = 0; /* block untill n bytes are received */
new_port_settings.c_cc[VTIME] = 0; /* block untill a timer expires (n * 100 mSec.) */
cfsetispeed(&new_port_settings, baudr);
cfsetospeed(&new_port_settings, baudr);
error = tcsetattr(Cport[comport_number], TCSANOW, &new_port_settings);
if(error==-1)
{
tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number);
close(Cport[comport_number]);
flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */
perror("unable to adjust portsettings ");
return(1);
}
/* http://man7.org/linux/man-pages/man4/tty_ioctl.4.html */
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number);
flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */
perror("unable to get portstatus");
return(1);
}
status |= TIOCM_DTR; /* turn on DTR */
status |= TIOCM_RTS; /* turn on RTS */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number);
flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */
perror("unable to set portstatus");
return(1);
}
return(0);
}
int RS232_PollComport(int comport_number, unsigned char *buf, int size)
{
int n;
n = read(Cport[comport_number], buf, size);
if(n < 0)
{
if(errno == EAGAIN) return 0;
}
return(n);
}
int RS232_SendByte(int comport_number, unsigned char byte)
{
int n = write(Cport[comport_number], &byte, 1);
if(n < 0)
{
if(errno == EAGAIN)
{
return 0;
}
else
{
return 1;
}
}
return(0);
}
int RS232_SendBuf(int comport_number, unsigned char *buf, int size)
{
int n = write(Cport[comport_number], buf, size);
if(n < 0)
{
if(errno == EAGAIN)
{
return 0;
}
else
{
return -1;
}
}
return(n);
}
void RS232_CloseComport(int comport_number)
{
int status;
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
}
status &= ~TIOCM_DTR; /* turn off DTR */
status &= ~TIOCM_RTS; /* turn off RTS */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
}
tcsetattr(Cport[comport_number], TCSANOW, old_port_settings + comport_number);
close(Cport[comport_number]);
flock(Cport[comport_number], LOCK_UN); /* free the port so that others can use it. */
}
/*
Constant Description
TIOCM_LE DSR (data set ready/line enable)
TIOCM_DTR DTR (data terminal ready)
TIOCM_RTS RTS (request to send)
TIOCM_ST Secondary TXD (transmit)
TIOCM_SR Secondary RXD (receive)
TIOCM_CTS CTS (clear to send)
TIOCM_CAR DCD (data carrier detect)
TIOCM_CD see TIOCM_CAR
TIOCM_RNG RNG (ring)
TIOCM_RI see TIOCM_RNG
TIOCM_DSR DSR (data set ready)
http://man7.org/linux/man-pages/man4/tty_ioctl.4.html
*/
int RS232_IsDCDEnabled(int comport_number)
{
int status;
ioctl(Cport[comport_number], TIOCMGET, &status);
if(status&TIOCM_CAR) return(1);
else return(0);
}
int RS232_IsCTSEnabled(int comport_number)
{
int status;
ioctl(Cport[comport_number], TIOCMGET, &status);
if(status&TIOCM_CTS) return(1);
else return(0);
}
int RS232_IsDSREnabled(int comport_number)
{
int status;
ioctl(Cport[comport_number], TIOCMGET, &status);
if(status&TIOCM_DSR) return(1);
else return(0);
}
void RS232_enableDTR(int comport_number)
{
int status;
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
}
status |= TIOCM_DTR; /* turn on DTR */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
}
}
void RS232_disableDTR(int comport_number)
{
int status;
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
}
status &= ~TIOCM_DTR; /* turn off DTR */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
}
}
void RS232_enableRTS(int comport_number)
{
int status;
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
}
status |= TIOCM_RTS; /* turn on RTS */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
}
}
void RS232_disableRTS(int comport_number)
{
int status;
if(ioctl(Cport[comport_number], TIOCMGET, &status) == -1)
{
perror("unable to get portstatus");
}
status &= ~TIOCM_RTS; /* turn off RTS */
if(ioctl(Cport[comport_number], TIOCMSET, &status) == -1)
{
perror("unable to set portstatus");
}
}
void RS232_flushRX(int comport_number)
{
tcflush(Cport[comport_number], TCIFLUSH);
}
void RS232_flushTX(int comport_number)
{
tcflush(Cport[comport_number], TCOFLUSH);
}
void RS232_flushRXTX(int comport_number)
{
tcflush(Cport[comport_number], TCIOFLUSH);
}
#else /* windows */
#define RS232_PORTNR 16
HANDLE Cport[RS232_PORTNR];
char *comports[RS232_PORTNR]={"\\\\.\\COM1", "\\\\.\\COM2", "\\\\.\\COM3", "\\\\.\\COM4",
"\\\\.\\COM5", "\\\\.\\COM6", "\\\\.\\COM7", "\\\\.\\COM8",
"\\\\.\\COM9", "\\\\.\\COM10", "\\\\.\\COM11", "\\\\.\\COM12",
"\\\\.\\COM13", "\\\\.\\COM14", "\\\\.\\COM15", "\\\\.\\COM16"};
char mode_str[128];
int RS232_OpenComport(int comport_number, int baudrate, const char *mode)
{
if((comport_number>=RS232_PORTNR)||(comport_number<0))
{
printf("illegal comport number\n");
return(1);
}
switch(baudrate)
{
case 110 : strcpy(mode_str, "baud=110");
break;
case 300 : strcpy(mode_str, "baud=300");
break;
case 600 : strcpy(mode_str, "baud=600");
break;
case 1200 : strcpy(mode_str, "baud=1200");
break;
case 2400 : strcpy(mode_str, "baud=2400");
break;
case 4800 : strcpy(mode_str, "baud=4800");
break;
case 9600 : strcpy(mode_str, "baud=9600");
break;
case 19200 : strcpy(mode_str, "baud=19200");
break;
case 38400 : strcpy(mode_str, "baud=38400");
break;
case 57600 : strcpy(mode_str, "baud=57600");
break;
case 115200 : strcpy(mode_str, "baud=115200");
break;
case 128000 : strcpy(mode_str, "baud=128000");
break;
case 256000 : strcpy(mode_str, "baud=256000");
break;
case 500000 : strcpy(mode_str, "baud=500000");
break;
case 1000000 : strcpy(mode_str, "baud=1000000");
break;
default : printf("invalid baudrate\n");
return(1);
break;
}
if(strlen(mode) != 3)
{
printf("invalid mode \"%s\"\n", mode);
return(1);
}
switch(mode[0])
{
case '8': strcat(mode_str, " data=8");
break;
case '7': strcat(mode_str, " data=7");
break;
case '6': strcat(mode_str, " data=6");
break;
case '5': strcat(mode_str, " data=5");
break;
default : printf("invalid number of data-bits '%c'\n", mode[0]);
return(1);
break;
}
switch(mode[1])
{
case 'N':
case 'n': strcat(mode_str, " parity=n");
break;
case 'E':
case 'e': strcat(mode_str, " parity=e");
break;
case 'O':
case 'o': strcat(mode_str, " parity=o");
break;
default : printf("invalid parity '%c'\n", mode[1]);
return(1);
break;
}
switch(mode[2])
{
case '1': strcat(mode_str, " stop=1");
break;
case '2': strcat(mode_str, " stop=2");
break;
default : printf("invalid number of stop bits '%c'\n", mode[2]);
return(1);
break;
}
strcat(mode_str, " dtr=on rts=on");
/*
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363145%28v=vs.85%29.aspx
http://technet.microsoft.com/en-us/library/cc732236.aspx
*/
Cport[comport_number] = CreateFileA(comports[comport_number],
GENERIC_READ|GENERIC_WRITE,
0, /* no share */
NULL, /* no security */
OPEN_EXISTING,
0, /* no threads */
NULL); /* no templates */
if(Cport[comport_number]==INVALID_HANDLE_VALUE)
{
printf("unable to open comport\n");
return(1);
}
DCB port_settings;
memset(&port_settings, 0, sizeof(port_settings)); /* clear the new struct */
port_settings.DCBlength = sizeof(port_settings);
if(!BuildCommDCBA(mode_str, &port_settings))
{
printf("unable to set comport dcb settings\n");
CloseHandle(Cport[comport_number]);
return(1);
}
if(!SetCommState(Cport[comport_number], &port_settings))
{
printf("unable to set comport cfg settings\n");
CloseHandle(Cport[comport_number]);
return(1);
}
COMMTIMEOUTS Cptimeouts;
Cptimeouts.ReadIntervalTimeout = MAXDWORD;
Cptimeouts.ReadTotalTimeoutMultiplier = 0;
Cptimeouts.ReadTotalTimeoutConstant = 0;
Cptimeouts.WriteTotalTimeoutMultiplier = 0;
Cptimeouts.WriteTotalTimeoutConstant = 0;
if(!SetCommTimeouts(Cport[comport_number], &Cptimeouts))
{
printf("unable to set comport time-out settings\n");
CloseHandle(Cport[comport_number]);
return(1);
}
return(0);
}
int RS232_PollComport(int comport_number, unsigned char *buf, int size)
{
int n;
/* added the void pointer cast, otherwise gcc will complain about */
/* "warning: dereferencing type-punned pointer will break strict aliasing rules" */
ReadFile(Cport[comport_number], buf, size, (LPDWORD)((void *)&n), NULL);
return(n);
}
int RS232_SendByte(int comport_number, unsigned char byte)
{
int n;
WriteFile(Cport[comport_number], &byte, 1, (LPDWORD)((void *)&n), NULL);
if(n<0) return(1);
return(0);
}
int RS232_SendBuf(int comport_number, unsigned char *buf, int size)
{
int n;
if(WriteFile(Cport[comport_number], buf, size, (LPDWORD)((void *)&n), NULL))
{
return(n);
}
return(-1);
}
void RS232_CloseComport(int comport_number)
{
CloseHandle(Cport[comport_number]);
}
/*
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363258%28v=vs.85%29.aspx
*/
int RS232_IsDCDEnabled(int comport_number)
{
int status;
GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status));
if(status&MS_RLSD_ON) return(1);
else return(0);
}
int RS232_IsCTSEnabled(int comport_number)
{
int status;
GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status));
if(status&MS_CTS_ON) return(1);
else return(0);
}
int RS232_IsDSREnabled(int comport_number)
{
int status;
GetCommModemStatus(Cport[comport_number], (LPDWORD)((void *)&status));
if(status&MS_DSR_ON) return(1);
else return(0);
}
void RS232_enableDTR(int comport_number)
{
EscapeCommFunction(Cport[comport_number], SETDTR);
}
void RS232_disableDTR(int comport_number)
{
EscapeCommFunction(Cport[comport_number], CLRDTR);
}
void RS232_enableRTS(int comport_number)
{
EscapeCommFunction(Cport[comport_number], SETRTS);
}
void RS232_disableRTS(int comport_number)
{
EscapeCommFunction(Cport[comport_number], CLRRTS);
}
/*
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363428%28v=vs.85%29.aspx
*/
void RS232_flushRX(int comport_number)
{
PurgeComm(Cport[comport_number], PURGE_RXCLEAR | PURGE_RXABORT);
}
void RS232_flushTX(int comport_number)
{
PurgeComm(Cport[comport_number], PURGE_TXCLEAR | PURGE_TXABORT);
}
void RS232_flushRXTX(int comport_number)
{
PurgeComm(Cport[comport_number], PURGE_RXCLEAR | PURGE_RXABORT);
PurgeComm(Cport[comport_number], PURGE_TXCLEAR | PURGE_TXABORT);
}
#endif
void RS232_cputs(int comport_number, const char *text) /* sends a string to serial port */
{
while(*text != 0) RS232_SendByte(comport_number, *(text++));
}
/* return index in comports matching to device name or -1 if not found */
int RS232_GetPortnr(const char *devname)
{
int i;
char str[32];
#if defined(__linux__) || defined(__FreeBSD__) /* Linux & FreeBSD */
strcpy(str, "/dev/");
#else /* windows */
strcpy(str, "\\\\.\\");
#endif
strncat(str, devname, 16);
str[31] = 0;
for(i=0; i<RS232_PORTNR; i++)
{
if(!strcmp(comports[i], str))
{
return i;
}
}
return -1; /* device not found */
}

View File

@@ -0,0 +1,87 @@
/*
***************************************************************************
*
* Author: Teunis van Beelen
*
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Teunis van Beelen
*
* Email: teuniz@gmail.com
*
***************************************************************************
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
***************************************************************************
*/
/* Last revision: August 5, 2017 */
/* For more info and how to use this library, visit: http://www.teuniz.net/RS-232/ */
#ifndef rs232_INCLUDED
#define rs232_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <string.h>
#if defined(__linux__) || defined(__FreeBSD__)
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <sys/file.h>
#include <errno.h>
#else
#include <windows.h>
#endif
int RS232_OpenComport(int, int, const char *);
int RS232_PollComport(int, unsigned char *, int);
int RS232_SendByte(int, unsigned char);
int RS232_SendBuf(int, unsigned char *, int);
void RS232_CloseComport(int);
void RS232_cputs(int, const char *);
int RS232_IsDCDEnabled(int);
int RS232_IsCTSEnabled(int);
int RS232_IsDSREnabled(int);
void RS232_enableDTR(int);
void RS232_disableDTR(int);
void RS232_enableRTS(int);
void RS232_disableRTS(int);
void RS232_flushRX(int);
void RS232_flushTX(int);
void RS232_flushRXTX(int);
int RS232_GetPortnr(const char *);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -162,13 +162,15 @@ void uda_c::StateTransition(
// worker():
// Implements the initialization state machine.
//
void uda_c::worker(void)
void uda_c::worker(unsigned instance)
{
UNUSED(instance) ; // only one
worker_init_realtime_priority(rt_device);
timeout_c timeout;
while (!worker_terminate)
while (!workers_terminate)
{
//
// Wait to be awoken.

View File

@@ -63,7 +63,7 @@ public:
bool on_param_changed(parameter_c *param) override;
void worker(void) override;
void worker(unsigned instance) override;
void on_after_register_access(
unibusdevice_register_t *device_reg,

View File

@@ -104,6 +104,8 @@ OBJECTS = $(OBJDIR)/application.o \
$(OBJDIR)/uda.o \
$(OBJDIR)/mscp_server.o \
$(OBJDIR)/mscp_drive.o \
$(OBJDIR)/rs232.o \
$(OBJDIR)/dl11w.o \
$(OBJDIR)/storagedrive.o \
$(OBJDIR)/storagecontroller.o \
$(OBJDIR)/demo_io.o \
@@ -233,6 +235,12 @@ $(OBJDIR)/mscp_server.o : $(DEVICE_SRC_DIR)/mscp_server.cpp $(DEVICE_SRC_DIR)/
$(OBJDIR)/mscp_drive.o : $(DEVICE_SRC_DIR)/mscp_drive.cpp $(DEVICE_SRC_DIR)/mscp_drive.hpp
$(CC) $(CCFLAGS) $< -o $@
$(OBJDIR)/rs232.o : $(DEVICE_SRC_DIR)/rs232.cpp $(DEVICE_SRC_DIR)/rs232.hpp
$(CC) $(CCFLAGS) $< -o $@
$(OBJDIR)/dl11w.o : $(DEVICE_SRC_DIR)/dl11w.cpp $(DEVICE_SRC_DIR)/dl11w.hpp
$(CC) $(CCFLAGS) $< -o $@
$(OBJDIR)/storagedrive.o : $(BASE_SRC_DIR)/storagedrive.cpp $(BASE_SRC_DIR)/storagedrive.hpp
$(CC) $(CCFLAGS) $< -o $@

View File

@@ -52,6 +52,7 @@
#include "rl11.hpp"
#include "rk11.hpp"
#include "uda.hpp"
#include "dl11w.hpp"
#include "cpu.hpp"
/*** handle loading of memory content from macro-11 listing ***/
@@ -127,6 +128,10 @@ void application_c::menu_devices(bool with_CPU) {
rk11_c *RK11 = new rk11_c();
// Create UDA50
uda_c *UDA50 = new uda_c();
// Create SLU+ LTC
slu_c *DL11 = new slu_c() ;
ltc_c *LTC = new ltc_c() ;
// //demo_regs.install();
// //demo_regs.worker_start();
@@ -433,6 +438,11 @@ void application_c::menu_devices(bool with_CPU) {
delete cpu;
}
LTC->enabled.set(false) ;
delete LTC ;
DL11->enabled.set(false) ;
delete DL11 ;
RL11->enabled.set(false);
delete RL11;