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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -102,8 +102,6 @@ public:
|
||||
}
|
||||
virtual void on_init_changed(void) {
|
||||
}
|
||||
virtual void worker(void) {
|
||||
}
|
||||
|
||||
void test(void);
|
||||
};
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
#define MILLION 1000000L
|
||||
#define BILLION (1000L * MILLION)
|
||||
|
||||
#define BIT(n) (1 << (n))
|
||||
|
||||
|
||||
#ifndef _UTILS_CPP_
|
||||
extern volatile int SIGINTreceived;
|
||||
#endif
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 << ".";
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
355
10.02_devices/2_src/dl11w.cpp
Normal file
355
10.02_devices/2_src/dl11w.cpp
Normal 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()");
|
||||
}
|
||||
}
|
||||
|
||||
188
10.02_devices/2_src/dl11w.hpp
Normal file
188
10.02_devices/2_src/dl11w.hpp
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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" ;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -100,7 +100,7 @@ public:
|
||||
void on_init_changed(void) override;
|
||||
|
||||
// background worker function
|
||||
void worker(void) override;
|
||||
void worker(unsigned instance) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -187,7 +187,7 @@ public:
|
||||
void clear_error_register(void);
|
||||
|
||||
// background worker function
|
||||
void worker(void) override;
|
||||
void worker(unsigned instance) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
823
10.02_devices/2_src/rs232.cpp
Normal file
823
10.02_devices/2_src/rs232.cpp
Normal 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 */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
87
10.02_devices/2_src/rs232.hpp
Normal file
87
10.02_devices/2_src/rs232.hpp
Normal 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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 $@
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user