escc: Attempt to add DBDMA support.

Move dma channels from EsccController to EsccChannel.
Add DBDMA callbacks.
This commit is contained in:
joevt 2024-03-20 10:39:43 -07:00 committed by dingusdev
parent 6adba32238
commit 159fe8d48e
2 changed files with 146 additions and 6 deletions

View File

@ -21,6 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
/** @file Enhanced Serial Communications Controller (ESCC) emulation. */
#include <core/timermanager.h>
#include <devices/deviceregistry.h>
#include <devices/serial/chario.h>
#include <devices/serial/escc.h>
@ -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<string> CharIoBackends = {"null", "stdio", "socket"};
static const PropMap Escc_Properties = {

View File

@ -25,6 +25,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#define ESCC_H
#include <devices/common/hwcomponent.h>
#include <devices/common/dbdma.h>
#include <devices/serial/chario.h>
#include <cinttypes>
@ -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<DMAChannel*>(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