diff --git a/devices/serial/escc.cpp b/devices/serial/escc.cpp index 42a3736..e1967ac 100644 --- a/devices/serial/escc.cpp +++ b/devices/serial/escc.cpp @@ -21,6 +21,7 @@ along with this program. If not, see . /** @file Enhanced Serial Communications Controller (ESCC) emulation. */ +#include #include #include #include @@ -324,6 +325,97 @@ uint8_t EsccChannel::receive_byte() return c; } +void EsccChannel::dma_start_tx() +{ + +} + +void EsccChannel::dma_start_rx() +{ + +} + +void EsccChannel::dma_stop_tx() +{ + if (this->timer_id_tx) { + TimerManager::get_instance()->cancel_timer(this->timer_id_tx); + this->timer_id_tx = 0; + } +} + +void EsccChannel::dma_stop_rx() +{ + if (this->timer_id_rx) { + TimerManager::get_instance()->cancel_timer(this->timer_id_rx); + this->timer_id_rx = 0; + } +} + +void EsccChannel::dma_in_tx() +{ + LOG_F(ERROR, "%s: Unexpected DMA INPUT command for transmit.", this->name.c_str()); +} + +void EsccChannel::dma_in_rx() +{ + if (dma_ch[1]->get_push_data_remaining()) { + this->timer_id_rx = TimerManager::get_instance()->add_oneshot_timer( + 0, + [this]() { + this->timer_id_rx = 0; + char c = receive_byte(); + int xx = dma_ch[1]->push_data(&c, 1); + this->dma_in_rx(); + }); + } +} + +void EsccChannel::dma_out_tx() +{ + this->timer_id_tx = TimerManager::get_instance()->add_oneshot_timer( + 10, + [this]() { + this->timer_id_tx = 0; + uint8_t *data; + uint32_t avail_len; + + if (dma_ch[1]->pull_data(256, &avail_len, &data) == MoreData) { + while(avail_len) { + this->send_byte(*data++); + avail_len--; + } + this->dma_out_tx(); + } + }); +} + +void EsccChannel::dma_out_rx() +{ + LOG_F(ERROR, "%s: Unexpected DMA OUTPUT command for receive.", this->name.c_str()); +} + +void EsccChannel::dma_flush_tx() +{ + this->dma_stop_tx(); + this->timer_id_tx = TimerManager::get_instance()->add_oneshot_timer( + 10, + [this]() { + this->timer_id_tx = 0; + dma_ch[1]->end_pull_data(); + }); +} + +void EsccChannel::dma_flush_rx() +{ + this->dma_stop_rx(); + this->timer_id_rx = TimerManager::get_instance()->add_oneshot_timer( + 10, + [this]() { + this->timer_id_rx = 0; + dma_ch[1]->end_push_data(); + }); +} + static const vector CharIoBackends = {"null", "stdio", "socket"}; static const PropMap Escc_Properties = { diff --git a/devices/serial/escc.h b/devices/serial/escc.h index 190b10b..c92c6f0 100644 --- a/devices/serial/escc.h +++ b/devices/serial/escc.h @@ -25,6 +25,7 @@ along with this program. If not, see . #define ESCC_H #include +#include #include #include @@ -104,7 +105,55 @@ public: void send_byte(uint8_t value); uint8_t receive_byte(); + void set_dma_channel(int dir_index, DmaBidirChannel *dma_ch) { + this->dma_ch[dir_index] = dma_ch; + auto dbdma_ch = dynamic_cast(dma_ch); + if (dbdma_ch) { + switch (dir_index) { + case 0: + dbdma_ch->set_callbacks( + std::bind(&EsccChannel::dma_start_tx, this), + std::bind(&EsccChannel::dma_stop_tx, this) + ); + dbdma_ch->set_data_callbacks( + std::bind(&EsccChannel::dma_in_tx, this), + std::bind(&EsccChannel::dma_out_tx, this), + std::bind(&EsccChannel::dma_flush_tx, this) + ); + break; + case 1: + dbdma_ch->set_callbacks( + std::bind(&EsccChannel::dma_start_rx, this), + std::bind(&EsccChannel::dma_stop_rx, this) + ); + dbdma_ch->set_data_callbacks( + std::bind(&EsccChannel::dma_in_rx, this), + std::bind(&EsccChannel::dma_out_rx, this), + std::bind(&EsccChannel::dma_flush_rx, this) + ); + break; + } + } + }; + private: + uint32_t timer_id_tx = 0; + uint32_t timer_id_rx = 0; + + void dma_start_tx(); + void dma_stop_tx(); + void dma_in_tx(); + void dma_out_tx(); + void dma_flush_tx(); + + void dma_start_rx(); + void dma_stop_rx(); + void dma_in_rx(); + void dma_out_rx(); + void dma_flush_rx(); + + DmaBidirChannel* dma_ch[2]; + std::string name; uint8_t read_regs[16]; uint8_t write_regs[16]; @@ -132,10 +181,11 @@ public: uint8_t read(uint8_t reg_offset); void write(uint8_t reg_offset, uint8_t value); - void dma_start(); - void dma_stop(); - void set_dma_channel(int ch_index, DmaBidirChannel *dma_ch) { - this->dma_ch[ch_index] = dma_ch; + void set_dma_channel(int ch_dir_index, DmaBidirChannel *dma_ch) { + switch (ch_dir_index >> 1) { + case 0: ch_a->set_dma_channel(ch_dir_index & 1, dma_ch); break; + case 1: ch_b->set_dma_channel(ch_dir_index & 1, dma_ch); break; + } }; private: @@ -149,8 +199,6 @@ private: uint8_t master_int_cntrl; uint8_t int_vec; - - DmaBidirChannel* dma_ch[4]; }; #endif // ESCC_H