From 4062386b973e08b6fff2bf045a77d2088be4d7e3 Mon Sep 17 00:00:00 2001 From: Joerg Hoppe Date: Sun, 23 Jun 2019 12:00:13 +0200 Subject: [PATCH] Multiple parallel instances of device::worker() possible --- 10.01_base/2_src/arm/device.cpp | 110 ++-- 10.01_base/2_src/arm/device.hpp | 39 +- 10.01_base/2_src/arm/storagedrive.hpp | 2 - 10.01_base/2_src/arm/unibusadapter.cpp | 15 +- 10.01_base/2_src/arm/unibusadapter.hpp | 2 +- 10.01_base/2_src/arm/unibusdevice.cpp | 4 + 10.01_base/2_src/arm/unibusdevice.hpp | 3 +- 10.01_base/2_src/arm/utils.hpp | 3 + 10.02_devices/2_src/cpu.cpp | 5 +- 10.02_devices/2_src/cpu.hpp | 2 +- 10.02_devices/2_src/demo_io.cpp | 5 +- 10.02_devices/2_src/demo_io.hpp | 2 +- 10.02_devices/2_src/demo_regs.cpp | 5 +- 10.02_devices/2_src/demo_regs.hpp | 2 +- 10.02_devices/2_src/dl11w.cpp | 355 +++++++++++ 10.02_devices/2_src/dl11w.hpp | 188 ++++++ 10.02_devices/2_src/mscp_drive.cpp | 5 +- 10.02_devices/2_src/mscp_drive.hpp | 2 - 10.02_devices/2_src/mscp_server.cpp | 1 + 10.02_devices/2_src/mscp_server.hpp | 1 - 10.02_devices/2_src/panel.cpp | 5 +- 10.02_devices/2_src/panel.hpp | 2 +- 10.02_devices/2_src/rk05.cpp | 3 +- 10.02_devices/2_src/rk05.hpp | 2 +- 10.02_devices/2_src/rk11.cpp | 5 +- 10.02_devices/2_src/rk11.hpp | 2 +- 10.02_devices/2_src/rl0102.cpp | 5 +- 10.02_devices/2_src/rl0102.hpp | 2 +- 10.02_devices/2_src/rl11.cpp | 5 +- 10.02_devices/2_src/rl11.hpp | 2 +- 10.02_devices/2_src/rs232.cpp | 823 +++++++++++++++++++++++++ 10.02_devices/2_src/rs232.hpp | 87 +++ 10.02_devices/2_src/uda.cpp | 6 +- 10.02_devices/2_src/uda.hpp | 2 +- 10.03_app_demo/2_src/makefile | 8 + 10.03_app_demo/2_src/menu_devices.cpp | 10 + 36 files changed, 1620 insertions(+), 100 deletions(-) create mode 100644 10.02_devices/2_src/dl11w.cpp create mode 100644 10.02_devices/2_src/dl11w.hpp create mode 100644 10.02_devices/2_src/rs232.cpp create mode 100644 10.02_devices/2_src/rs232.hpp diff --git a/10.01_base/2_src/arm/device.cpp b/10.01_base/2_src/arm/device.cpp index 60eb782..a20b471 100644 --- a/10.01_base/2_src/arm/device.cpp +++ b/10.01_base/2_src/arm/device.cpp @@ -55,29 +55,27 @@ list 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); + } } } diff --git a/10.01_base/2_src/arm/device.hpp b/10.01_base/2_src/arm/device.hpp index f07a273..5019449 100644 --- a/10.01_base/2_src/arm/device.hpp +++ b/10.01_base/2_src/arm/device.hpp @@ -32,17 +32,28 @@ #include 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 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 diff --git a/10.01_base/2_src/arm/storagedrive.hpp b/10.01_base/2_src/arm/storagedrive.hpp index e15df05..93613fc 100644 --- a/10.01_base/2_src/arm/storagedrive.hpp +++ b/10.01_base/2_src/arm/storagedrive.hpp @@ -102,8 +102,6 @@ public: } virtual void on_init_changed(void) { } - virtual void worker(void) { - } void test(void); }; diff --git a/10.01_base/2_src/arm/unibusadapter.cpp b/10.01_base/2_src/arm/unibusadapter.cpp index 1ac1206..c6dd27b 100644 --- a/10.01_base/2_src/arm/unibusadapter.cpp +++ b/10.01_base/2_src/arm/unibusadapter.cpp @@ -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" diff --git a/10.01_base/2_src/arm/unibusadapter.hpp b/10.01_base/2_src/arm/unibusadapter.hpp index c569aff..94d6080 100644 --- a/10.01_base/2_src/arm/unibusadapter.hpp +++ b/10.01_base/2_src/arm/unibusadapter.hpp @@ -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); diff --git a/10.01_base/2_src/arm/unibusdevice.cpp b/10.01_base/2_src/arm/unibusdevice.cpp index 123ff05..9a15f22 100644 --- a/10.01_base/2_src/arm/unibusdevice.cpp +++ b/10.01_base/2_src/arm/unibusdevice.cpp @@ -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); diff --git a/10.01_base/2_src/arm/unibusdevice.hpp b/10.01_base/2_src/arm/unibusdevice.hpp index 1722047..4d43e95 100644 --- a/10.01_base/2_src/arm/unibusdevice.hpp +++ b/10.01_base/2_src/arm/unibusdevice.hpp @@ -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! diff --git a/10.01_base/2_src/arm/utils.hpp b/10.01_base/2_src/arm/utils.hpp index f11bc58..2819ed9 100644 --- a/10.01_base/2_src/arm/utils.hpp +++ b/10.01_base/2_src/arm/utils.hpp @@ -40,6 +40,9 @@ #define MILLION 1000000L #define BILLION (1000L * MILLION) +#define BIT(n) (1 << (n)) + + #ifndef _UTILS_CPP_ extern volatile int SIGINTreceived; #endif diff --git a/10.02_devices/2_src/cpu.cpp b/10.02_devices/2_src/cpu.cpp index 368e250..d1658b3 100644 --- a/10.02_devices/2_src/cpu.cpp +++ b/10.02_devices/2_src/cpu.cpp @@ -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); diff --git a/10.02_devices/2_src/cpu.hpp b/10.02_devices/2_src/cpu.hpp index 4197557..9e3d864 100644 --- a/10.02_devices/2_src/cpu.hpp +++ b/10.02_devices/2_src/cpu.hpp @@ -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) diff --git a/10.02_devices/2_src/demo_io.cpp b/10.02_devices/2_src/demo_io.cpp index 42c93de..3fa75e4 100644 --- a/10.02_devices/2_src/demo_io.cpp +++ b/10.02_devices/2_src/demo_io.cpp @@ -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; diff --git a/10.02_devices/2_src/demo_io.hpp b/10.02_devices/2_src/demo_io.hpp index cefdec0..0bc4e71 100644 --- a/10.02_devices/2_src/demo_io.hpp +++ b/10.02_devices/2_src/demo_io.hpp @@ -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) diff --git a/10.02_devices/2_src/demo_regs.cpp b/10.02_devices/2_src/demo_regs.cpp index 4e59d25..cb212d4 100644 --- a/10.02_devices/2_src/demo_regs.cpp +++ b/10.02_devices/2_src/demo_regs.cpp @@ -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 << "."; } diff --git a/10.02_devices/2_src/demo_regs.hpp b/10.02_devices/2_src/demo_regs.hpp index ad33873..5a8b8e1 100644 --- a/10.02_devices/2_src/demo_regs.hpp +++ b/10.02_devices/2_src/demo_regs.hpp @@ -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) diff --git a/10.02_devices/2_src/dl11w.cpp b/10.02_devices/2_src/dl11w.cpp new file mode 100644 index 0000000..db2a40d --- /dev/null +++ b/10.02_devices/2_src/dl11w.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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()"); + } +} + diff --git a/10.02_devices/2_src/dl11w.hpp b/10.02_devices/2_src/dl11w.hpp new file mode 100644 index 0000000..d5918a0 --- /dev/null +++ b/10.02_devices/2_src/dl11w.hpp @@ -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 + +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 diff --git a/10.02_devices/2_src/mscp_drive.cpp b/10.02_devices/2_src/mscp_drive.cpp index 72772bb..e2a2afc 100644 --- a/10.02_devices/2_src/mscp_drive.cpp +++ b/10.02_devices/2_src/mscp_drive.cpp @@ -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. diff --git a/10.02_devices/2_src/mscp_drive.hpp b/10.02_devices/2_src/mscp_drive.hpp index 04dd154..6754a6a 100644 --- a/10.02_devices/2_src/mscp_drive.hpp +++ b/10.02_devices/2_src/mscp_drive.hpp @@ -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"); diff --git a/10.02_devices/2_src/mscp_server.cpp b/10.02_devices/2_src/mscp_server.cpp index b5e3c40..cc4e17f 100644 --- a/10.02_devices/2_src/mscp_server.cpp +++ b/10.02_devices/2_src/mscp_server.cpp @@ -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" ; diff --git a/10.02_devices/2_src/mscp_server.hpp b/10.02_devices/2_src/mscp_server.hpp index 8b72757..669d5a8 100644 --- a/10.02_devices/2_src/mscp_server.hpp +++ b/10.02_devices/2_src/mscp_server.hpp @@ -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); diff --git a/10.02_devices/2_src/panel.cpp b/10.02_devices/2_src/panel.cpp index c226c99..e12839e 100644 --- a/10.02_devices/2_src/panel.cpp +++ b/10.02_devices/2_src/panel.cpp @@ -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); diff --git a/10.02_devices/2_src/panel.hpp b/10.02_devices/2_src/panel.hpp index dd93eb8..2d48ae8 100644 --- a/10.02_devices/2_src/panel.hpp +++ b/10.02_devices/2_src/panel.hpp @@ -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 diff --git a/10.02_devices/2_src/rk05.cpp b/10.02_devices/2_src/rk05.cpp index 850a7c2..077860d 100755 --- a/10.02_devices/2_src/rk05.cpp +++ b/10.02_devices/2_src/rk05.cpp @@ -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) { diff --git a/10.02_devices/2_src/rk05.hpp b/10.02_devices/2_src/rk05.hpp index bf7085f..a5a0bcf 100755 --- a/10.02_devices/2_src/rk05.hpp +++ b/10.02_devices/2_src/rk05.hpp @@ -100,7 +100,7 @@ public: void on_init_changed(void) override; // background worker function - void worker(void) override; + void worker(unsigned instance) override; }; #endif diff --git a/10.02_devices/2_src/rk11.cpp b/10.02_devices/2_src/rk11.cpp index 40afe2a..1f1173f 100755 --- a/10.02_devices/2_src/rk11.cpp +++ b/10.02_devices/2_src/rk11.cpp @@ -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) { diff --git a/10.02_devices/2_src/rk11.hpp b/10.02_devices/2_src/rk11.hpp index 78c3eda..779190d 100755 --- a/10.02_devices/2_src/rk11.hpp +++ b/10.02_devices/2_src/rk11.hpp @@ -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( diff --git a/10.02_devices/2_src/rl0102.cpp b/10.02_devices/2_src/rl0102.cpp index 3615085..c39aac4 100644 --- a/10.02_devices/2_src/rl0102.cpp +++ b/10.02_devices/2_src/rl0102.cpp @@ -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); diff --git a/10.02_devices/2_src/rl0102.hpp b/10.02_devices/2_src/rl0102.hpp index fa5a749..a2920ea 100644 --- a/10.02_devices/2_src/rl0102.hpp +++ b/10.02_devices/2_src/rl0102.hpp @@ -187,7 +187,7 @@ public: void clear_error_register(void); // background worker function - void worker(void) override; + void worker(unsigned instance) override; }; #endif diff --git a/10.02_devices/2_src/rl11.cpp b/10.02_devices/2_src/rl11.cpp index 8ad41ce..1b06221 100644 --- a/10.02_devices/2_src/rl11.cpp +++ b/10.02_devices/2_src/rl11.cpp @@ -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 */ diff --git a/10.02_devices/2_src/rl11.hpp b/10.02_devices/2_src/rl11.hpp index 73ef9ef..7498dcd 100644 --- a/10.02_devices/2_src/rl11.hpp +++ b/10.02_devices/2_src/rl11.hpp @@ -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); diff --git a/10.02_devices/2_src/rs232.cpp b/10.02_devices/2_src/rs232.cpp new file mode 100644 index 0000000..20bb5f6 --- /dev/null +++ b/10.02_devices/2_src/rs232.cpp @@ -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 . +* +*************************************************************************** +*/ + + +/* 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. +* +*************************************************************************** +*/ + +/* 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 +#include + + + +#if defined(__linux__) || defined(__FreeBSD__) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include + +#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 + + diff --git a/10.02_devices/2_src/uda.cpp b/10.02_devices/2_src/uda.cpp index 8a133be..ccda235 100644 --- a/10.02_devices/2_src/uda.cpp +++ b/10.02_devices/2_src/uda.cpp @@ -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. diff --git a/10.02_devices/2_src/uda.hpp b/10.02_devices/2_src/uda.hpp index 367f41d..afa4bdc 100644 --- a/10.02_devices/2_src/uda.hpp +++ b/10.02_devices/2_src/uda.hpp @@ -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, diff --git a/10.03_app_demo/2_src/makefile b/10.03_app_demo/2_src/makefile index a566175..eec8f2f 100644 --- a/10.03_app_demo/2_src/makefile +++ b/10.03_app_demo/2_src/makefile @@ -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 $@ diff --git a/10.03_app_demo/2_src/menu_devices.cpp b/10.03_app_demo/2_src/menu_devices.cpp index 10806f2..69924bf 100644 --- a/10.03_app_demo/2_src/menu_devices.cpp +++ b/10.03_app_demo/2_src/menu_devices.cpp @@ -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;