Commit 754f1ef1 authored by Jan Charvát's avatar Jan Charvát
Browse files

hw/net/can: CTU CAN FD IP open hardware core emulation.

CTU CAN FD project pages:
	https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core

CAN bus CTU FEE Projects Listing page:
	http://canbus.pages.fel.cvut.cz/



Signed-off-by: default avatarJan Charvat <charvj10@fel.cvut.cz>
parent f54ee78a
Loading
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -2,3 +2,6 @@ common-obj-$(CONFIG_CAN_SJA1000) += can_sja1000.o
common-obj-$(CONFIG_CAN_PCI) += can_kvaser_pci.o
common-obj-$(CONFIG_CAN_PCI) += can_pcm3680_pci.o
common-obj-$(CONFIG_CAN_PCI) += can_mioe3680_pci.o

common-obj-$(CONFIG_CAN_PCI) += ctucan_core.o
common-obj-$(CONFIG_CAN_PCI) += ctucan_pci.o
+532 −0
Original line number Diff line number Diff line
/*
 * CTU CAN FD PCI device emulation
 * http://canbus.pages.fel.cvut.cz/
 *
 * Copyright (c) 2019 Jan Charvat (jancharvat.charvat@gmail.com)
 *
 * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
 * Jin Yang and Pavel Pisa
 *
 * 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
 * THE AUTHORS OR COPYRIGHT HOLDERS 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.
 */

#include "qemu/osdep.h"
#include "qemu/log.h"
#include "chardev/char.h"
#include "hw/irq.h"
#include "migration/vmstate.h"
#include "net/can_emu.h"

#include "ctucan_core.h"


#ifndef DEBUG_FILTER
#define DEBUG_FILTER 0
#endif /*DEBUG_FILTER*/

#ifndef DEBUG_CAN
#define DEBUG_CAN 0
#endif /*DEBUG_CAN*/

#define DPRINTF(fmt, ...) \
    do { \
        if (DEBUG_CAN) { \
            qemu_log("[ctucan]: " fmt , ## __VA_ARGS__); \
        } \
    } while (0)

#if 0
static void can_display_msg(const char *prefix, const qemu_can_frame *msg)
{
    int i;

    qemu_log_lock();
    qemu_log("%s%03X [%01d] %s %s",
             prefix,
             msg->can_id & QEMU_CAN_EFF_MASK,
             msg->can_dlc,
             msg->can_id & QEMU_CAN_EFF_FLAG ? "EFF" : "SFF",
             msg->can_id & QEMU_CAN_RTR_FLAG ? "RTR" : "DAT");

    for (i = 0; i < msg->can_dlc; i++) {
        qemu_log(" %02X", msg->data[i]);
    }
    qemu_log("\n");
    qemu_log_flush();
    qemu_log_unlock();
}
#endif

static void ctucan_buff2frame(const uint8_t *buff, qemu_can_frame *frame)
{
    frame->can_id = 0;
    frame->can_dlc = 0;
    frame->flags = 0;

    if (buff == NULL) {
        return;
    }
    {
        union ctu_can_fd_frame_form_w frame_form_w;
        union ctu_can_fd_identifier_w identifier_w;
        unsigned int ide;

        frame_form_w = *(union ctu_can_fd_frame_form_w *) buff;
        frame->can_dlc = can_dlc2len(frame_form_w.s.dlc);

        identifier_w = *(union ctu_can_fd_identifier_w *) (buff + 4);

        ide = frame_form_w.s.ide;
        if (ide) {
            frame->can_id = (identifier_w.s.identifier_base << 18) |
                            identifier_w.s.identifier_ext;
        frame->can_id |= QEMU_CAN_EFF_FLAG;
        } else {
            frame->can_id = identifier_w.s.identifier_base;
        }

        if (frame_form_w.s.esi_rsv) {
            frame->flags |= QEMU_CAN_FRMF_ESI;
        }

        if (frame_form_w.s.rtr) {
            frame->can_id |= QEMU_CAN_RTR_FLAG;
        }

        if (frame_form_w.s.fdf) {   /*CAN FD*/
            frame->flags |= QEMU_CAN_FRMF_TYPE_FD;
            if (frame_form_w.s.brs) {
                frame->flags |= QEMU_CAN_FRMF_BRS;
            }
        }
    }

    memcpy(frame->data, buff + 0x10, 0x40);
}


static int ctucan_frame2buff(const qemu_can_frame *frame, uint8_t *buff)
{
    int i;

    if (frame->can_id & QEMU_CAN_ERR_FLAG) { /* error frame, NOT support now. */
        return -1;
    }

    buff[0] = 0x0f & frame->can_dlc; /* DLC */
    if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */
        buff[0] |= (1 << 6);
    }
    if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */
        buff[0] |= (1 << 7);
        buff[1] = extract32(frame->can_id, 21, 8); /* ID.28~ID.21 */
        buff[2] = extract32(frame->can_id, 13, 8); /* ID.20~ID.13 */
        buff[3] = extract32(frame->can_id, 5, 8);  /* ID.12~ID.05 */
        buff[4] = extract32(frame->can_id, 0, 5) << 3; /* ID.04~ID.00,xxx */
        for (i = 0; i < frame->can_dlc; i++) {
            buff[5 + i] = frame->data[i];
        }
        return frame->can_dlc + 5;
    } else { /* SFF */
        buff[1] = extract32(frame->can_id, 3, 8); /* ID.10~ID.03 */
        buff[2] = extract32(frame->can_id, 0, 3) << 5; /* ID.02~ID.00,xxxxx */
        for (i = 0; i < frame->can_dlc; i++) {
            buff[3 + i] = frame->data[i];
        }

        return frame->can_dlc + 3;
    }

    return -1;
}

static void ctucan_update_irq(CtuCanCoreState *s)
{
    union ctu_can_fd_int_stat int_rq;

    int_rq.u32 = 0;

    int_rq.u32 &= ~s->int_mask.u32;
    s->int_stat.u32 |= int_rq.u32;
    if (s->int_stat.u32 & s->int_ena.u32) {
        qemu_irq_raise(s->irq);
    } else {
        qemu_irq_lower(s->irq);
    }
}

static void ctucan_update_txnf(CtuCanCoreState *s)
{
    int i;
    int txnf;
    unsigned int buff_st;

    txnf = 0;

    for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
        buff_st = (s->tx_status.u32 >> (i * 4)) & 0xf;
        if (buff_st == TXT_ETY) {
            txnf = 1;
        }
    }
    s->status.s.txnf = txnf;
}

void ctucan_hardware_reset(CtuCanCoreState *s)
{
    int i;
    unsigned int buff_st;
    uint32_t buff_st_mask;

    s->tx_status.u32 = 0;
    for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
        buff_st_mask = 0xf << (i * 4);
        buff_st = TXT_ETY;
        s->tx_status.u32 = (s->tx_status.u32 & ~buff_st_mask) |
            (buff_st << (i * 4));
    }

    ctucan_update_txnf(s);

    s->int_stat.u32 = 0;

    qemu_irq_lower(s->irq);
}

/*
 * static const uint32_t ctucan_tx_buf_bases[CTUCAN_CORE_TXBUF_NUM] = {
 *              CTU_CAN_FD_TXTB1_DATA_1, CTU_CAN_FD_TXTB2_DATA_1,
 *              CTU_CAN_FD_TXTB3_DATA_1, CTU_CAN_FD_TXTB4_DATA_1
 * };
 */

#define CTUCAN_CORE_TXBUFF_SPAN     (CTU_CAN_FD_TXTB2_DATA_1 - CTU_CAN_FD_TXTB1_DATA_1)

void ctucan_mem_write(CtuCanCoreState *s, hwaddr addr, uint64_t val,
                       unsigned size)
{
    qemu_can_frame   frame;
    int              i;

    DPRINTF("write 0x%02llx addr 0x%02x\n",
            (unsigned long long)val, (unsigned int)addr);

    if (addr > CTUCAN_CORE_MEM_SIZE) {
        return;
    }

    if (addr >= CTU_CAN_FD_TXTB1_DATA_1) {
        int buff_num;
        addr -= CTU_CAN_FD_TXTB1_DATA_1;
        buff_num = addr / CTUCAN_CORE_TXBUFF_SPAN;
        addr %= CTUCAN_CORE_TXBUFF_SPAN;
        if (buff_num < CTUCAN_CORE_TXBUF_NUM) {
            *(uint32_t *)(s->tx_buffer[buff_num].data + addr) = val;
        }
    } else {
        switch (addr & ~3) {
        case CTU_CAN_FD_MODE:
            s->mode_settings.u32 = (uint32_t)val;
            if (s->mode_settings.s.rst) {
                ctucan_hardware_reset(s);
                s->mode_settings.s.rst = 0;
            }
            break;
        case CTU_CAN_FD_COMMAND:
            s->command.u32 = (uint32_t)val;
            break;
        case CTU_CAN_FD_INT_STAT:
            s->int_stat.u32 &= ~(uint32_t)val;
            break;
        case CTU_CAN_FD_INT_ENA_SET:
            s->int_ena.u32 |= (uint32_t)val;
            break;
        case CTU_CAN_FD_INT_ENA_CLR:
            s->int_ena.u32 &= ~(uint32_t)val;
            break;
        case CTU_CAN_FD_INT_MASK_SET:
            s->int_mask.u32 |= (uint32_t)val;
            break;
        case CTU_CAN_FD_INT_MASK_CLR:
            s->int_mask.u32 &= ~(uint32_t)val;
            break;
        case CTU_CAN_FD_TX_COMMAND:
            if (s->mode_settings.s.ena) {
                union ctu_can_fd_tx_command tx_command;
                union ctu_can_fd_tx_command mask;
                unsigned int buff_st;
                uint32_t buff_st_mask;
                uint8_t *pf;

                tx_command.u32 = (uint32_t)val;
                mask.u32 = 0;
                mask.s.txb1 = 1;
                for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
                    if (!(tx_command.u32 & (mask.u32 << i))) {
                        continue;
                    }
                    buff_st_mask = 0xf << (i * 4);
                    buff_st = (s->tx_status.u32 >> (i * 4)) & 0xf;
                    if (tx_command.s.txca) {
                        if (buff_st == TXT_RDY) {
                            buff_st = TXT_ABT;
                        }
                    }
                    if (tx_command.s.txcr) {
                        if ((buff_st == TXT_TOK) || (buff_st == TXT_ERR) ||
                            (buff_st == TXT_ABT) || (buff_st == TXT_ETY)) {
                            union ctu_can_fd_int_stat int_stat;
                            int_stat.u32 = 0;
                            buff_st = TXT_RDY;
                            pf = s->tx_buffer[i].data;
                            ctucan_buff2frame(pf, &frame);
                            can_bus_client_send(&s->bus_client, &frame, 1);
                            buff_st = TXT_TOK;
                            int_stat.s.txi = 1;
                            int_stat.s.txbhci = 1;
                            s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32;
                        }
                    }
                    if (tx_command.s.txce) {
                        if ((buff_st == TXT_TOK) || (buff_st == TXT_ERR) ||
                            (buff_st == TXT_ABT))
                            buff_st = TXT_ETY;
                    }
                    s->tx_status.u32 = (s->tx_status.u32 & ~buff_st_mask) |
                                        (buff_st << (i * 4));
                }
                ctucan_update_txnf(s);
            }
            break;
        case CTU_CAN_FD_TX_PRIORITY:
            s->tx_priority.u32 = (uint32_t)val;
            break;
        }

        ctucan_update_irq(s);
    }

    return;
}

uint64_t ctucan_mem_read(CtuCanCoreState *s, hwaddr addr, unsigned size)
{
    uint32_t val = 0;

    DPRINTF("read addr 0x%02x ...\n", (unsigned int)addr);

    if (addr > CTUCAN_CORE_MEM_SIZE) {
        return 0;
    }

    switch (addr & ~3) {
    case CTU_CAN_FD_DEVICE_ID:
        {
            union ctu_can_fd_device_id_version idver;
            idver.u32 = 0;
            idver.s.device_id = CTU_CAN_FD_ID;
            idver.s.ver_major = 2;
            idver.s.ver_minor = 2;
            val = idver.u32;
        }
        break;
    case CTU_CAN_FD_MODE:
        val = s->mode_settings.u32;
        break;
    case CTU_CAN_FD_STATUS:
        val = s->status.u32;
        break;
    case CTU_CAN_FD_INT_STAT:
        val = s->int_stat.u32;
        break;
    case CTU_CAN_FD_INT_ENA_SET:
    case CTU_CAN_FD_INT_ENA_CLR:
        val = s->int_ena.u32;
        break;
    case CTU_CAN_FD_INT_MASK_SET:
    case CTU_CAN_FD_INT_MASK_CLR:
        val = s->int_mask.u32;
        break;
    case CTU_CAN_FD_RX_MEM_INFO:
        val = s->rx_mem_info.u32;
        break;
    case CTU_CAN_FD_RX_POINTERS:
        val = s->rx_pointers.u32;
        break;
    case CTU_CAN_FD_RX_STATUS:
    case CTU_CAN_FD_RX_SETTINGS:
        val = s->rx_status_rx_settings.u32;
        break;
    case CTU_CAN_FD_RX_DATA:
        val = s->rx_data.u32;
        break;
    case CTU_CAN_FD_TX_STATUS:
        val = s->tx_status.u32;
        break;
    case CTU_CAN_FD_TX_PRIORITY:
        val = s->tx_priority.u32;
        break;
    }

    val >>= ((addr & 3) << 3);
    if (size < 8) {
        val &= ((uint64_t)1 << (size << 3)) - 1;
    }
    return val;
}

int ctucan_can_receive(CanBusClientState *client)
{
    CtuCanCoreState *s = container_of(client, CtuCanCoreState, bus_client);

    if (s->mode_settings.s.ena) {
        return 0;
    }

    return 1; /* always return 1, when operation mode */
}

ssize_t ctucan_receive(CanBusClientState *client, const qemu_can_frame *frames,
                        size_t frames_cnt)
{
    CtuCanCoreState *s = container_of(client, CtuCanCoreState, bus_client);
    static uint8_t rcv[CTUCAN_MSG_MAX_LEN];
    int i;
    int ret = -1;
    const qemu_can_frame *frame = frames;

    if (frames_cnt <= 0) {
        return 0;
    }

    ret = ctucan_frame2buff(frame, rcv);

    if (s->rx_cnt + ret > CTUCAN_RCV_BUF_LEN) { /* Data overrun. */
        s->status.s.dor = 1;
        ctucan_update_irq(s);
        if (DEBUG_FILTER) {
            qemu_log("[ctucan]: receive FIFO overrun\n");
        }
        return ret;
    }
    s->rx_fr_ctr.u32++;

    for (i = 0; i < ret; i++) {
        s->rx_buff[(s->rx_ptr++) % CTUCAN_RCV_BUF_LEN] = rcv[i];
    }
    s->rx_ptr %= CTUCAN_RCV_BUF_LEN; /* update the pointer. */

    ctucan_update_irq(s);

    return 1;
}

static CanBusClientInfo ctucan_bus_client_info = {
    .can_receive = ctucan_can_receive,
    .receive = ctucan_receive,
};


int ctucan_connect_to_bus(CtuCanCoreState *s, CanBusState *bus)
{
    s->bus_client.info = &ctucan_bus_client_info;

    if (!bus) {
        return -EINVAL;
    }

    if (can_bus_insert_client(bus, &s->bus_client) < 0) {
        return -1;
    }

    return 0;
}

void ctucan_disconnect(CtuCanCoreState *s)
{
    can_bus_remove_client(&s->bus_client);
}

int ctucan_init(CtuCanCoreState *s, qemu_irq irq)
{
    s->irq = irq;

    qemu_irq_lower(s->irq);

    ctucan_hardware_reset(s);

    return 0;
}

const VMStateDescription vmstate_qemu_ctucan_filter = {
    .name = "qemu_can_filter",
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(can_id, qemu_can_filter),
        VMSTATE_UINT32(can_mask, qemu_can_filter),
        VMSTATE_END_OF_LIST()
    }
};

const VMStateDescription vmstate_qemu_ctucan_tx_buffer = {
    .name = "qemu_ctucan_tx_buffer",
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT8_ARRAY(data, CtuCanCoreMsgBuffer, CTUCAN_CORE_MSG_MAX_LEN),
        VMSTATE_END_OF_LIST()
    }
};

static int ctucan_post_load(void *opaque, int version_id)
{
    CtuCanCoreState *s = opaque;
    ctucan_update_irq(s);
    return 0;
}

/* VMState is needed for live migration of QEMU images */
const VMStateDescription vmstate_ctucan = {
    .name = "ctucan",
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
    .post_load = ctucan_post_load,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(mode_settings.u32, CtuCanCoreState),
        VMSTATE_STRUCT_ARRAY(tx_buffer, CtuCanCoreState, CTUCAN_CORE_TXBUF_NUM, 0,
                             vmstate_qemu_ctucan_tx_buffer, CtuCanCoreMsgBuffer),

/* delete later*/

        VMSTATE_BUFFER(rx_buff, CtuCanCoreState),

        VMSTATE_UINT32(rx_ptr, CtuCanCoreState),
        VMSTATE_UINT32(rx_cnt, CtuCanCoreState),

        VMSTATE_STRUCT_ARRAY(filter, CtuCanCoreState, 4, 0,
                             vmstate_qemu_ctucan_filter, qemu_can_filter),


        VMSTATE_END_OF_LIST()
    }
};
+131 −0
Original line number Diff line number Diff line
/*
 * CTU CAN FD device emulation
 * http://canbus.pages.fel.cvut.cz/
 *
 * Copyright (c) 2019 Jan Charvat (jancharvat.charvat@gmail.com)
 * 
 * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
 * Jin Yang and Pavel Pisa
 *
 * 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
 * THE AUTHORS OR COPYRIGHT HOLDERS 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.
 */
#ifndef HW_CAN_CTUCAN_CORE_H
#define HW_CAN_CTUCAN_CORE_H

#include "exec/hwaddr.h"
#include "net/can_emu.h"


#ifndef __LITTLE_ENDIAN_BITFIELD
#define __LITTLE_ENDIAN_BITFIELD 1
#endif

#include "ctu_can_fd_frame.h"
#include "ctu_can_fd_regs.h"

#define CTUCAN_CORE_MEM_SIZE       0x500

/* The max size for a message in FIFO */
#define CTUCAN_MSG_MAX_LEN        (CTU_CAN_FD_DATA_1_4_W + 64)
/* The receive buffer size. */
#define CTUCAN_RCV_BUF_LEN        (1024 * 8)


/* The max size for a message buffer */
#define CTUCAN_CORE_MSG_MAX_LEN       0x50
/* The receive buffer size. */
#define CTUCAN_CORE_RCV_BUF_LEN       0x1000

#define CTUCAN_CORE_TXBUF_NUM			4

typedef struct CtuCanCoreMsgBuffer {
	uint8_t data[CTUCAN_CORE_MSG_MAX_LEN];
} CtuCanCoreMsgBuffer;

typedef struct CtuCanCoreState {
	union ctu_can_fd_mode_settings					mode_settings;
	union ctu_can_fd_status							status;
	union ctu_can_fd_command						command;
	union ctu_can_fd_int_stat						int_stat;
	union ctu_can_fd_int_ena_set					int_ena;
	union ctu_can_fd_int_mask_set					int_mask;
	union ctu_can_fd_btr							brt;
	union ctu_can_fd_btr_fd							brt_fd;
	union ctu_can_fd_ewl_erp_fault_state			ewl_erp_fault_state;
	union ctu_can_fd_rec_tec						rec_tec;
	union ctu_can_fd_err_norm_err_fd				err_norm_err_fd;
	union ctu_can_fd_ctr_pres						ctr_pres;
	union ctu_can_fd_filter_a_mask					filter_a_mask;
	union ctu_can_fd_filter_a_val					filter_a_val;
	union ctu_can_fd_filter_b_mask					filter_b_mask;
	union ctu_can_fd_filter_b_val					filter_b_val;
	union ctu_can_fd_filter_c_mask					filter_c_mask;
	union ctu_can_fd_filter_c_val					filter_c_val;
	union ctu_can_fd_filter_ran_low					filter_ran_low;
	union ctu_can_fd_filter_ran_high				filter_ran_high;
	union ctu_can_fd_filter_control_filter_status	filter_control_filter_status;
	union ctu_can_fd_rx_mem_info					rx_mem_info;
	union ctu_can_fd_rx_pointers					rx_pointers;
	union ctu_can_fd_rx_status_rx_settings			rx_status_rx_settings;
	union ctu_can_fd_rx_data						rx_data;
	union ctu_can_fd_tx_status						tx_status;
	union ctu_can_fd_tx_priority					tx_priority;
	union ctu_can_fd_err_capt_alc					err_capt_alc;
	union ctu_can_fd_trv_delay_ssp_cfg				trv_delay_ssp_cfg;
	union ctu_can_fd_rx_fr_ctr						rx_fr_ctr;
	union ctu_can_fd_tx_fr_ctr						tx_fr_ctr;
	union ctu_can_fd_debug_register					debug_register;
	union ctu_can_fd_yolo_reg						yolo_reg;
	union ctu_can_fd_timestamp_low					timestamp_low;
	union ctu_can_fd_timestamp_high					timestamp_high;
	CtuCanCoreMsgBuffer 							tx_buffer[CTUCAN_CORE_TXBUF_NUM];

    /* PeliCAN state and registers sorted by address */

    uint8_t         rx_buff[CTUCAN_RCV_BUF_LEN];  /* 32~95 .. 64bytes Rx FIFO */
    uint32_t        rx_ptr;        /* Count by bytes. */
    uint32_t        rx_cnt;        /* Count by bytes. */

    qemu_can_filter filter[4];

    qemu_irq          irq;
    CanBusClientState bus_client;
} CtuCanCoreState;

void ctucan_hardware_reset(CtuCanCoreState *s);

void ctucan_mem_write(CtuCanCoreState *s, hwaddr addr, uint64_t val,
                       unsigned size);

uint64_t ctucan_mem_read(CtuCanCoreState *s, hwaddr addr, unsigned size);

int ctucan_connect_to_bus(CtuCanCoreState *s, CanBusState *bus);

void ctucan_disconnect(CtuCanCoreState *s);

int ctucan_init(CtuCanCoreState *s, qemu_irq irq);

int ctucan_can_receive(CanBusClientState *client);

ssize_t ctucan_receive(CanBusClientState *client,
                        const qemu_can_frame *frames, size_t frames_cnt);

extern const VMStateDescription vmstate_ctucan;

#endif
+280 −0

File added.

Preview size limit exceeded, changes collapsed.