diff --git a/devices/common/dbdma.cpp b/devices/common/dbdma.cpp index 6042d7d..9b29a6d 100644 --- a/devices/common/dbdma.cpp +++ b/devices/common/dbdma.cpp @@ -39,6 +39,12 @@ void DMAChannel::set_callbacks(DbdmaCallback start_cb, DbdmaCallback stop_cb) { this->stop_cb = stop_cb; } +void DMAChannel::set_data_callbacks(DbdmaCallback in_cb, DbdmaCallback out_cb, DbdmaCallback flush_cb) { + this->in_cb = in_cb; + this->out_cb = out_cb; + this->flush_cb = flush_cb; +} + /* Load DMACmd from physical memory. */ DMACmd* DMAChannel::fetch_cmd(uint32_t cmd_addr, DMACmd* p_cmd, bool *is_writable) { MapDmaResult res = mmu_map_dma_mem(cmd_addr, 16, false); @@ -88,6 +94,18 @@ uint8_t DMAChannel::interpret_cmd() { this->queue_data = res.host_va; this->res_count = 0; this->cmd_in_progress = true; + switch (this->cur_cmd) { + case DBDMA_Cmd::OUTPUT_MORE: + case DBDMA_Cmd::OUTPUT_LAST: + if (this->out_cb) + this->out_cb(); + break; + case DBDMA_Cmd::INPUT_MORE: + case DBDMA_Cmd::INPUT_LAST: + if (this->in_cb) + this->in_cb(); + break; + } } else this->finish_cmd(); break; @@ -153,6 +171,7 @@ void DMAChannel::finish_cmd() { if (res.is_writable) WRITE_WORD_LE_A(&cmd_desc[14], this->ch_stat | CH_STAT_ACTIVE); + this->ch_stat &= ~CH_STAT_FLUSH; // react to cmd.b (branch) bits if (cmd_desc[2] & 0xC) { @@ -320,9 +339,13 @@ void DMAChannel::reg_write(uint32_t offset, uint32_t value, int size) { // That means we need to update memory before channel operation // is aborted to prevent data loss. if (new_stat & CH_STAT_FLUSH) { - // NOTE: because this implementation doesn't currently support - // partial memory updates no special action is taken here - new_stat &= ~CH_STAT_FLUSH; + if (this->cur_cmd <= DBDMA_Cmd::INPUT_LAST && this->flush_cb && (new_stat & CH_STAT_ACTIVE) && !(this->ch_stat & CH_STAT_DEAD)) { + this->flush_cb(); + } else { + // NOTE: because this implementation doesn't currently support + // partial memory updates no special action is taken here + new_stat &= ~CH_STAT_FLUSH; + } this->ch_stat = new_stat; } @@ -446,6 +469,40 @@ int DMAChannel::push_data(const char* src_ptr, int len) { return 0; } +void DMAChannel::end_pull_data() { + if (this->ch_stat & CH_STAT_DEAD || !(this->ch_stat & CH_STAT_ACTIVE)) { + // dead or idle channel? -> no more data + LOG_F(WARNING, "%s: Ending Dead/idle channel -> no more data", this->get_name().c_str()); + return; + } + + if (this->queue_len) { + this->queue_len = 0; + } else { + this->ch_stat &= ~CH_STAT_FLUSH; + } + + // proceed with the DBDMA program + this->interpret_cmd(); +} + +void DMAChannel::end_push_data() { + if (this->ch_stat & CH_STAT_DEAD || !(this->ch_stat & CH_STAT_ACTIVE)) { + LOG_F(WARNING, "%s: Attempt to end push data to dead/idle channel", + this->get_name().c_str()); + return; + } + + if (this->queue_len) { + this->queue_len = 0; + } else { + this->ch_stat &= ~CH_STAT_FLUSH; + } + + // proceed with the DBDMA program + this->interpret_cmd(); +} + bool DMAChannel::is_out_active() { if (this->ch_stat & CH_STAT_DEAD || !(this->ch_stat & CH_STAT_ACTIVE)) { return false; diff --git a/devices/common/dbdma.h b/devices/common/dbdma.h index cb40ca1..08989fe 100644 --- a/devices/common/dbdma.h +++ b/devices/common/dbdma.h @@ -109,6 +109,7 @@ public: ~DMAChannel() = default; void set_callbacks(DbdmaCallback start_cb, DbdmaCallback stop_cb); + void set_data_callbacks(DbdmaCallback in_cb, DbdmaCallback out_cb, DbdmaCallback flush_cb); uint32_t reg_read(uint32_t offset, int size); void reg_write(uint32_t offset, uint32_t value, int size); void set_stat(uint8_t new_stat) { this->ch_stat = (this->ch_stat & 0xff00) | new_stat; } @@ -117,6 +118,10 @@ public: bool is_in_active(); DmaPullResult pull_data(uint32_t req_len, uint32_t *avail_len, uint8_t **p_data); int push_data(const char* src_ptr, int len); + int get_pull_data_remaining() { return this->queue_len; } + int get_push_data_remaining() { return this->queue_len; } + void end_pull_data(); + void end_push_data(); void register_dma_int(InterruptCtrl* int_ctrl_obj, uint32_t irq_id) { this->int_ctrl = int_ctrl_obj; @@ -138,6 +143,9 @@ protected: private: std::function start_cb = nullptr; // DMA channel start callback std::function stop_cb = nullptr; // DMA channel stop callback + std::function in_cb = nullptr; // DMA channel in callback + std::function out_cb = nullptr; // DMA channel out callback + std::function flush_cb = nullptr; // DMA channel flush callback uint16_t ch_stat = 0; uint32_t cmd_ptr = 0; diff --git a/devices/common/dmacore.h b/devices/common/dmacore.h index 80f762f..247e877 100644 --- a/devices/common/dmacore.h +++ b/devices/common/dmacore.h @@ -39,6 +39,8 @@ public: virtual bool is_out_active() { return true; }; virtual DmaPullResult pull_data(uint32_t req_len, uint32_t *avail_len, uint8_t **p_data) = 0; + virtual int get_pull_data_remaining() { return 1; }; + virtual void end_pull_data() { }; std::string get_name(void) { return this->name; }; @@ -52,6 +54,8 @@ public: virtual bool is_in_active() { return true; }; virtual int push_data(const char* src_ptr, int len) = 0; + virtual int get_push_data_remaining() { return 1; }; + virtual void end_push_data() { }; std::string get_name(void) { return this->name; };