qemu/hw/net/can/ctucan_core.c
<<
>>
Prefs
   1/*
   2 * CTU CAN FD PCI device emulation
   3 * http://canbus.pages.fel.cvut.cz/
   4 *
   5 * Copyright (c) 2019 Jan Charvat (jancharvat.charvat@gmail.com)
   6 *
   7 * Based on Kvaser PCI CAN device (SJA1000 based) emulation implemented by
   8 * Jin Yang and Pavel Pisa
   9 *
  10 * Permission is hereby granted, free of charge, to any person obtaining a copy
  11 * of this software and associated documentation files (the "Software"), to deal
  12 * in the Software without restriction, including without limitation the rights
  13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  14 * copies of the Software, and to permit persons to whom the Software is
  15 * furnished to do so, subject to the following conditions:
  16 *
  17 * The above copyright notice and this permission notice shall be included in
  18 * all copies or substantial portions of the Software.
  19 *
  20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  26 * THE SOFTWARE.
  27 */
  28
  29#include "qemu/osdep.h"
  30#include "qemu/log.h"
  31#include "chardev/char.h"
  32#include "hw/irq.h"
  33#include "migration/vmstate.h"
  34#include "net/can_emu.h"
  35
  36#include "ctucan_core.h"
  37
  38#ifndef DEBUG_CAN
  39#define DEBUG_CAN 0
  40#endif /*DEBUG_CAN*/
  41
  42#define DPRINTF(fmt, ...) \
  43    do { \
  44        if (DEBUG_CAN) { \
  45            qemu_log("[ctucan]: " fmt , ## __VA_ARGS__); \
  46        } \
  47    } while (0)
  48
  49static void ctucan_buff2frame(const uint8_t *buff, qemu_can_frame *frame)
  50{
  51    frame->can_id = 0;
  52    frame->can_dlc = 0;
  53    frame->flags = 0;
  54
  55    if (buff == NULL) {
  56        return;
  57    }
  58    {
  59        union ctu_can_fd_frame_form_w frame_form_w;
  60        union ctu_can_fd_identifier_w identifier_w;
  61        unsigned int ide;
  62        uint32_t w;
  63
  64        w = le32_to_cpu(*(uint32_t *)buff);
  65        frame_form_w = (union ctu_can_fd_frame_form_w)w;
  66        frame->can_dlc = can_dlc2len(frame_form_w.s.dlc);
  67
  68        w = le32_to_cpu(*(uint32_t *)(buff + 4));
  69        identifier_w = (union ctu_can_fd_identifier_w)w;
  70
  71        ide = frame_form_w.s.ide;
  72        if (ide) {
  73            frame->can_id = (identifier_w.s.identifier_base << 18) |
  74                            identifier_w.s.identifier_ext;
  75            frame->can_id |= QEMU_CAN_EFF_FLAG;
  76        } else {
  77            frame->can_id = identifier_w.s.identifier_base;
  78        }
  79
  80        if (frame_form_w.s.esi_rsv) {
  81            frame->flags |= QEMU_CAN_FRMF_ESI;
  82        }
  83
  84        if (frame_form_w.s.rtr) {
  85            frame->can_id |= QEMU_CAN_RTR_FLAG;
  86        }
  87
  88        if (frame_form_w.s.fdf) {   /*CAN FD*/
  89            frame->flags |= QEMU_CAN_FRMF_TYPE_FD;
  90            if (frame_form_w.s.brs) {
  91                frame->flags |= QEMU_CAN_FRMF_BRS;
  92            }
  93        }
  94    }
  95
  96    memcpy(frame->data, buff + 0x10, 0x40);
  97}
  98
  99
 100static int ctucan_frame2buff(const qemu_can_frame *frame, uint8_t *buff)
 101{
 102    unsigned int bytes_cnt = -1;
 103    memset(buff, 0, CTUCAN_MSG_MAX_LEN * sizeof(*buff));
 104
 105    if (frame == NULL) {
 106        return bytes_cnt;
 107    }
 108    {
 109        union ctu_can_fd_frame_form_w frame_form_w;
 110        union ctu_can_fd_identifier_w identifier_w;
 111
 112        frame_form_w.u32 = 0;
 113        identifier_w.u32 = 0;
 114
 115        bytes_cnt = frame->can_dlc;
 116        bytes_cnt = (bytes_cnt + 3) & ~3;
 117        bytes_cnt += 16;
 118        frame_form_w.s.rwcnt = (bytes_cnt >> 2) - 1;
 119
 120        frame_form_w.s.dlc = can_len2dlc(frame->can_dlc);
 121
 122        if (frame->can_id & QEMU_CAN_EFF_FLAG) {
 123            frame_form_w.s.ide = 1;
 124            identifier_w.s.identifier_base =
 125                                    (frame->can_id & 0x1FFC0000) >> 18;
 126            identifier_w.s.identifier_ext = frame->can_id & 0x3FFFF;
 127        } else {
 128            identifier_w.s.identifier_base = frame->can_id & 0x7FF;
 129        }
 130
 131        if (frame->flags & QEMU_CAN_FRMF_ESI) {
 132            frame_form_w.s.esi_rsv = 1;
 133        }
 134
 135        if (frame->can_id & QEMU_CAN_RTR_FLAG) {
 136            frame_form_w.s.rtr = 1;
 137        }
 138
 139        if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) {  /*CAN FD*/
 140           frame_form_w.s.fdf = 1;
 141            if (frame->flags & QEMU_CAN_FRMF_BRS) {
 142                frame_form_w.s.brs = 1;
 143            }
 144        }
 145        *(uint32_t *)buff = cpu_to_le32(frame_form_w.u32);
 146        *(uint32_t *)(buff + 4) = cpu_to_le32(identifier_w.u32);
 147    }
 148
 149    memcpy(buff + 0x10, frame->data, 0x40);
 150
 151    return bytes_cnt;
 152}
 153
 154static void ctucan_update_irq(CtuCanCoreState *s)
 155{
 156    union ctu_can_fd_int_stat int_rq;
 157
 158    int_rq.u32 = 0;
 159
 160    if (s->rx_status_rx_settings.s.rxfrc) {
 161        int_rq.s.rbnei = 1;
 162    }
 163
 164    int_rq.u32 &= ~s->int_mask.u32;
 165    s->int_stat.u32 |= int_rq.u32;
 166    if (s->int_stat.u32 & s->int_ena.u32) {
 167        qemu_irq_raise(s->irq);
 168    } else {
 169        qemu_irq_lower(s->irq);
 170    }
 171}
 172
 173static void ctucan_update_txnf(CtuCanCoreState *s)
 174{
 175    int i;
 176    int txnf;
 177    unsigned int buff_st;
 178
 179    txnf = 0;
 180
 181    for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
 182        buff_st = (s->tx_status.u32 >> (i * 4)) & 0xf;
 183        if (buff_st == TXT_ETY) {
 184            txnf = 1;
 185        }
 186    }
 187    s->status.s.txnf = txnf;
 188}
 189
 190void ctucan_hardware_reset(CtuCanCoreState *s)
 191{
 192    DPRINTF("Hardware reset in progress!!!\n");
 193    int i;
 194    unsigned int buff_st;
 195    uint32_t buff_st_mask;
 196
 197    s->tx_status.u32 = 0;
 198    for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
 199        buff_st_mask = 0xf << (i * 4);
 200        buff_st = TXT_ETY;
 201        s->tx_status.u32 = (s->tx_status.u32 & ~buff_st_mask) |
 202            (buff_st << (i * 4));
 203    }
 204    s->status.s.idle = 1;
 205
 206    ctucan_update_txnf(s);
 207
 208    s->rx_status_rx_settings.u32 = 0;
 209    s->rx_tail_pos = 0;
 210    s->rx_cnt = 0;
 211    s->rx_frame_rem = 0;
 212
 213    /* Flush RX buffer */
 214    s->rx_tail_pos = 0;
 215    s->rx_cnt = 0;
 216    s->rx_frame_rem = 0;
 217
 218    /* Set on progdokum reset value */
 219    s->mode_settings.u32 = 0;
 220    s->mode_settings.s.fde = 1;
 221
 222    s->int_stat.u32 = 0;
 223    s->int_ena.u32 = 0;
 224    s->int_mask.u32 = 0;
 225
 226    s->rx_status_rx_settings.u32 = 0;
 227    s->rx_status_rx_settings.s.rxe = 0;
 228
 229    s->rx_fr_ctr.u32 = 0;
 230    s->tx_fr_ctr.u32 = 0;
 231
 232    s->yolo_reg.s.yolo_val = 3735928559;
 233
 234    qemu_irq_lower(s->irq);
 235}
 236
 237static void ctucan_send_ready_buffers(CtuCanCoreState *s)
 238{
 239    qemu_can_frame frame;
 240    uint8_t *pf;
 241    int buff2tx_idx;
 242    uint32_t tx_prio_max;
 243
 244    if (!s->mode_settings.s.ena) {
 245        return;
 246    }
 247
 248    do {
 249        union ctu_can_fd_int_stat int_stat;
 250        int i;
 251        buff2tx_idx = -1;
 252        tx_prio_max = 0;
 253
 254        for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
 255            uint32_t prio;
 256
 257            if (extract32(s->tx_status.u32, i * 4, 4) != TXT_RDY) {
 258                continue;
 259            }
 260            prio = (s->tx_priority.u32 >> (i * 4)) & 0x7;
 261            if (tx_prio_max < prio) {
 262                tx_prio_max = prio;
 263                buff2tx_idx = i;
 264            }
 265        }
 266        if (buff2tx_idx == -1) {
 267            break;
 268        }
 269        int_stat.u32 = 0;
 270        pf = s->tx_buffer[buff2tx_idx].data;
 271        ctucan_buff2frame(pf, &frame);
 272        s->status.s.idle = 0;
 273        s->status.s.txs = 1;
 274        can_bus_client_send(&s->bus_client, &frame, 1);
 275        s->status.s.idle = 1;
 276        s->status.s.txs = 0;
 277        s->tx_fr_ctr.s.tx_fr_ctr_val++;
 278        int_stat.s.txi = 1;
 279        int_stat.s.txbhci = 1;
 280        s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32;
 281        s->tx_status.u32 = deposit32(s->tx_status.u32,
 282                                     buff2tx_idx * 4, 4, TXT_TOK);
 283    } while (1);
 284}
 285
 286#define CTUCAN_CORE_TXBUFF_SPAN \
 287            (CTU_CAN_FD_TXTB2_DATA_1 - CTU_CAN_FD_TXTB1_DATA_1)
 288
 289void ctucan_mem_write(CtuCanCoreState *s, hwaddr addr, uint64_t val,
 290                       unsigned size)
 291{
 292    int              i;
 293
 294    DPRINTF("write 0x%02llx addr 0x%02x\n",
 295            (unsigned long long)val, (unsigned int)addr);
 296
 297    if (addr >= CTUCAN_CORE_MEM_SIZE) {
 298        return;
 299    }
 300
 301    if (addr >= CTU_CAN_FD_TXTB1_DATA_1) {
 302        int buff_num;
 303        addr -= CTU_CAN_FD_TXTB1_DATA_1;
 304        buff_num = addr / CTUCAN_CORE_TXBUFF_SPAN;
 305        addr %= CTUCAN_CORE_TXBUFF_SPAN;
 306        if ((buff_num < CTUCAN_CORE_TXBUF_NUM) &&
 307            ((addr + size) <= sizeof(s->tx_buffer[buff_num].data))) {
 308            stn_le_p(s->tx_buffer[buff_num].data + addr, size, val);
 309        }
 310    } else {
 311        switch (addr & ~3) {
 312        case CTU_CAN_FD_MODE:
 313            s->mode_settings.u32 = (uint32_t)val;
 314            if (s->mode_settings.s.rst) {
 315                ctucan_hardware_reset(s);
 316                s->mode_settings.s.rst = 0;
 317            }
 318            break;
 319        case CTU_CAN_FD_COMMAND:
 320        {
 321            union ctu_can_fd_command command;
 322            command.u32 = (uint32_t)val;
 323            if (command.s.cdo) {
 324                s->status.s.dor = 0;
 325            }
 326            if (command.s.rrb) {
 327                s->rx_tail_pos = 0;
 328                s->rx_cnt = 0;
 329                s->rx_frame_rem = 0;
 330                s->rx_status_rx_settings.s.rxfrc = 0;
 331            }
 332            if (command.s.txfcrst) {
 333                s->tx_fr_ctr.s.tx_fr_ctr_val = 0;
 334            }
 335            if (command.s.rxfcrst) {
 336                s->rx_fr_ctr.s.rx_fr_ctr_val = 0;
 337            }
 338            break;
 339        }
 340        case CTU_CAN_FD_INT_STAT:
 341            s->int_stat.u32 &= ~(uint32_t)val;
 342            break;
 343        case CTU_CAN_FD_INT_ENA_SET:
 344            s->int_ena.u32 |= (uint32_t)val;
 345            break;
 346        case CTU_CAN_FD_INT_ENA_CLR:
 347            s->int_ena.u32 &= ~(uint32_t)val;
 348            break;
 349        case CTU_CAN_FD_INT_MASK_SET:
 350            s->int_mask.u32 |= (uint32_t)val;
 351            break;
 352        case CTU_CAN_FD_INT_MASK_CLR:
 353            s->int_mask.u32 &= ~(uint32_t)val;
 354            break;
 355        case CTU_CAN_FD_TX_COMMAND:
 356            if (s->mode_settings.s.ena) {
 357                union ctu_can_fd_tx_command tx_command;
 358                union ctu_can_fd_tx_command mask;
 359                unsigned int buff_st;
 360                uint32_t buff_st_mask;
 361
 362                tx_command.u32 = (uint32_t)val;
 363                mask.u32 = 0;
 364                mask.s.txb1 = 1;
 365
 366                for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) {
 367                    if (!(tx_command.u32 & (mask.u32 << i))) {
 368                        continue;
 369                    }
 370                    buff_st_mask = 0xf << (i * 4);
 371                    buff_st = (s->tx_status.u32 >> (i * 4)) & 0xf;
 372                    if (tx_command.s.txca) {
 373                        if (buff_st == TXT_RDY) {
 374                            buff_st = TXT_ABT;
 375                        }
 376                    }
 377                    if (tx_command.s.txcr) {
 378                        if ((buff_st == TXT_TOK) || (buff_st == TXT_ERR) ||
 379                            (buff_st == TXT_ABT) || (buff_st == TXT_ETY))
 380                            buff_st = TXT_RDY;
 381                    }
 382                    if (tx_command.s.txce) {
 383                        if ((buff_st == TXT_TOK) || (buff_st == TXT_ERR) ||
 384                            (buff_st == TXT_ABT))
 385                            buff_st = TXT_ETY;
 386                    }
 387                    s->tx_status.u32 = (s->tx_status.u32 & ~buff_st_mask) |
 388                                        (buff_st << (i * 4));
 389                }
 390
 391                ctucan_send_ready_buffers(s);
 392                ctucan_update_txnf(s);
 393            }
 394            break;
 395        case CTU_CAN_FD_TX_PRIORITY:
 396            s->tx_priority.u32 = (uint32_t)val;
 397            break;
 398        }
 399
 400        ctucan_update_irq(s);
 401    }
 402
 403    return;
 404}
 405
 406uint64_t ctucan_mem_read(CtuCanCoreState *s, hwaddr addr, unsigned size)
 407{
 408    uint32_t val = 0;
 409
 410    DPRINTF("read addr 0x%02x ...\n", (unsigned int)addr);
 411
 412    if (addr > CTUCAN_CORE_MEM_SIZE) {
 413        return 0;
 414    }
 415
 416    switch (addr & ~3) {
 417    case CTU_CAN_FD_DEVICE_ID:
 418        {
 419            union ctu_can_fd_device_id_version idver;
 420            idver.u32 = 0;
 421            idver.s.device_id = CTU_CAN_FD_ID;
 422            idver.s.ver_major = 2;
 423            idver.s.ver_minor = 2;
 424            val = idver.u32;
 425        }
 426        break;
 427    case CTU_CAN_FD_MODE:
 428        val = s->mode_settings.u32;
 429        break;
 430    case CTU_CAN_FD_STATUS:
 431        val = s->status.u32;
 432        break;
 433    case CTU_CAN_FD_INT_STAT:
 434        val = s->int_stat.u32;
 435        break;
 436    case CTU_CAN_FD_INT_ENA_SET:
 437    case CTU_CAN_FD_INT_ENA_CLR:
 438        val = s->int_ena.u32;
 439        break;
 440    case CTU_CAN_FD_INT_MASK_SET:
 441    case CTU_CAN_FD_INT_MASK_CLR:
 442        val = s->int_mask.u32;
 443        break;
 444    case CTU_CAN_FD_RX_MEM_INFO:
 445        s->rx_mem_info.u32 = 0;
 446        s->rx_mem_info.s.rx_buff_size = CTUCAN_RCV_BUF_LEN >> 2;
 447        s->rx_mem_info.s.rx_mem_free = (CTUCAN_RCV_BUF_LEN -
 448                                        s->rx_cnt) >> 2;
 449        val = s->rx_mem_info.u32;
 450        break;
 451    case CTU_CAN_FD_RX_POINTERS:
 452    {
 453        uint32_t rx_head_pos = s->rx_tail_pos + s->rx_cnt;
 454        rx_head_pos %= CTUCAN_RCV_BUF_LEN;
 455        s->rx_pointers.s.rx_wpp = rx_head_pos;
 456        s->rx_pointers.s.rx_rpp = s->rx_tail_pos;
 457        val = s->rx_pointers.u32;
 458        break;
 459    }
 460    case CTU_CAN_FD_RX_STATUS:
 461    case CTU_CAN_FD_RX_SETTINGS:
 462        if (!s->rx_status_rx_settings.s.rxfrc) {
 463            s->rx_status_rx_settings.s.rxe = 1;
 464        } else {
 465            s->rx_status_rx_settings.s.rxe = 0;
 466        }
 467        if (((s->rx_cnt + 3) & ~3) == CTUCAN_RCV_BUF_LEN) {
 468            s->rx_status_rx_settings.s.rxf = 1;
 469        } else {
 470            s->rx_status_rx_settings.s.rxf = 0;
 471        }
 472        val = s->rx_status_rx_settings.u32;
 473        break;
 474    case CTU_CAN_FD_RX_DATA:
 475        if (s->rx_cnt) {
 476            memcpy(&val, s->rx_buff + s->rx_tail_pos, 4);
 477            val = le32_to_cpu(val);
 478            if (!s->rx_frame_rem) {
 479                union ctu_can_fd_frame_form_w frame_form_w;
 480                frame_form_w.u32 = val;
 481                s->rx_frame_rem = frame_form_w.s.rwcnt * 4 + 4;
 482            }
 483            s->rx_cnt -= 4;
 484            s->rx_frame_rem -= 4;
 485            if (!s->rx_frame_rem) {
 486                s->rx_status_rx_settings.s.rxfrc--;
 487                if (!s->rx_status_rx_settings.s.rxfrc) {
 488                    s->status.s.rxne = 0;
 489                    s->status.s.idle = 1;
 490                    s->status.s.rxs = 0;
 491                }
 492            }
 493            s->rx_tail_pos = (s->rx_tail_pos + 4) % CTUCAN_RCV_BUF_LEN;
 494        } else {
 495            val = 0;
 496        }
 497        break;
 498    case CTU_CAN_FD_TX_STATUS:
 499        val = s->tx_status.u32;
 500        break;
 501    case CTU_CAN_FD_TX_PRIORITY:
 502        val = s->tx_priority.u32;
 503        break;
 504    case CTU_CAN_FD_RX_FR_CTR:
 505        val = s->rx_fr_ctr.s.rx_fr_ctr_val;
 506        break;
 507    case CTU_CAN_FD_TX_FR_CTR:
 508        val = s->tx_fr_ctr.s.tx_fr_ctr_val;
 509        break;
 510    case CTU_CAN_FD_YOLO_REG:
 511        val = s->yolo_reg.s.yolo_val;
 512        break;
 513    }
 514
 515    val >>= ((addr & 3) << 3);
 516    if (size < 8) {
 517        val &= ((uint64_t)1 << (size << 3)) - 1;
 518    }
 519
 520    return val;
 521}
 522
 523bool ctucan_can_receive(CanBusClientState *client)
 524{
 525    CtuCanCoreState *s = container_of(client, CtuCanCoreState, bus_client);
 526
 527    if (!s->mode_settings.s.ena) {
 528        return false;
 529    }
 530
 531    return true; /* always return true, when operation mode */
 532}
 533
 534ssize_t ctucan_receive(CanBusClientState *client, const qemu_can_frame *frames,
 535                        size_t frames_cnt)
 536{
 537    CtuCanCoreState *s = container_of(client, CtuCanCoreState, bus_client);
 538    static uint8_t rcv[CTUCAN_MSG_MAX_LEN];
 539    int i;
 540    int ret = -1;
 541    const qemu_can_frame *frame = frames;
 542    union ctu_can_fd_int_stat int_stat;
 543    int_stat.u32 = 0;
 544
 545    if (frames_cnt <= 0) {
 546        return 0;
 547    }
 548
 549    ret = ctucan_frame2buff(frame, rcv);
 550
 551    if (s->rx_cnt + ret > CTUCAN_RCV_BUF_LEN) { /* Data overrun. */
 552        s->status.s.dor = 1;
 553        int_stat.s.doi = 1;
 554        s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32;
 555        ctucan_update_irq(s);
 556        DPRINTF("Receive FIFO overrun\n");
 557        return ret;
 558    }
 559    s->status.s.idle = 0;
 560    s->status.s.rxs = 1;
 561    int_stat.s.rxi = 1;
 562    if (((s->rx_cnt + 3) & ~3) == CTUCAN_RCV_BUF_LEN) {
 563        int_stat.s.rxfi = 1;
 564    }
 565    s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32;
 566    s->rx_fr_ctr.s.rx_fr_ctr_val++;
 567    s->rx_status_rx_settings.s.rxfrc++;
 568    for (i = 0; i < ret; i++) {
 569        s->rx_buff[(s->rx_tail_pos + s->rx_cnt) % CTUCAN_RCV_BUF_LEN] = rcv[i];
 570        s->rx_cnt++;
 571    }
 572    s->status.s.rxne = 1;
 573
 574    ctucan_update_irq(s);
 575
 576    return 1;
 577}
 578
 579static CanBusClientInfo ctucan_bus_client_info = {
 580    .can_receive = ctucan_can_receive,
 581    .receive = ctucan_receive,
 582};
 583
 584
 585int ctucan_connect_to_bus(CtuCanCoreState *s, CanBusState *bus)
 586{
 587    s->bus_client.info = &ctucan_bus_client_info;
 588
 589    if (!bus) {
 590        return -EINVAL;
 591    }
 592
 593    if (can_bus_insert_client(bus, &s->bus_client) < 0) {
 594        return -1;
 595    }
 596
 597    return 0;
 598}
 599
 600void ctucan_disconnect(CtuCanCoreState *s)
 601{
 602    can_bus_remove_client(&s->bus_client);
 603}
 604
 605int ctucan_init(CtuCanCoreState *s, qemu_irq irq)
 606{
 607    s->irq = irq;
 608
 609    qemu_irq_lower(s->irq);
 610
 611    ctucan_hardware_reset(s);
 612
 613    return 0;
 614}
 615
 616const VMStateDescription vmstate_qemu_ctucan_tx_buffer = {
 617    .name = "qemu_ctucan_tx_buffer",
 618    .version_id = 1,
 619    .minimum_version_id = 1,
 620    .minimum_version_id_old = 1,
 621    .fields = (VMStateField[]) {
 622        VMSTATE_UINT8_ARRAY(data, CtuCanCoreMsgBuffer, CTUCAN_CORE_MSG_MAX_LEN),
 623        VMSTATE_END_OF_LIST()
 624    }
 625};
 626
 627static int ctucan_post_load(void *opaque, int version_id)
 628{
 629    CtuCanCoreState *s = opaque;
 630    ctucan_update_irq(s);
 631    return 0;
 632}
 633
 634/* VMState is needed for live migration of QEMU images */
 635const VMStateDescription vmstate_ctucan = {
 636    .name = "ctucan",
 637    .version_id = 1,
 638    .minimum_version_id = 1,
 639    .minimum_version_id_old = 1,
 640    .post_load = ctucan_post_load,
 641    .fields = (VMStateField[]) {
 642        VMSTATE_UINT32(mode_settings.u32, CtuCanCoreState),
 643        VMSTATE_UINT32(status.u32, CtuCanCoreState),
 644        VMSTATE_UINT32(int_stat.u32, CtuCanCoreState),
 645        VMSTATE_UINT32(int_ena.u32, CtuCanCoreState),
 646        VMSTATE_UINT32(int_mask.u32, CtuCanCoreState),
 647        VMSTATE_UINT32(brt.u32, CtuCanCoreState),
 648        VMSTATE_UINT32(brt_fd.u32, CtuCanCoreState),
 649        VMSTATE_UINT32(ewl_erp_fault_state.u32, CtuCanCoreState),
 650        VMSTATE_UINT32(rec_tec.u32, CtuCanCoreState),
 651        VMSTATE_UINT32(err_norm_err_fd.u32, CtuCanCoreState),
 652        VMSTATE_UINT32(ctr_pres.u32, CtuCanCoreState),
 653        VMSTATE_UINT32(filter_a_mask.u32, CtuCanCoreState),
 654        VMSTATE_UINT32(filter_a_val.u32, CtuCanCoreState),
 655        VMSTATE_UINT32(filter_b_mask.u32, CtuCanCoreState),
 656        VMSTATE_UINT32(filter_b_val.u32, CtuCanCoreState),
 657        VMSTATE_UINT32(filter_c_mask.u32, CtuCanCoreState),
 658        VMSTATE_UINT32(filter_c_val.u32, CtuCanCoreState),
 659        VMSTATE_UINT32(filter_ran_low.u32, CtuCanCoreState),
 660        VMSTATE_UINT32(filter_ran_high.u32, CtuCanCoreState),
 661        VMSTATE_UINT32(filter_control_filter_status.u32, CtuCanCoreState),
 662        VMSTATE_UINT32(rx_mem_info.u32, CtuCanCoreState),
 663        VMSTATE_UINT32(rx_pointers.u32, CtuCanCoreState),
 664        VMSTATE_UINT32(rx_status_rx_settings.u32, CtuCanCoreState),
 665        VMSTATE_UINT32(tx_status.u32, CtuCanCoreState),
 666        VMSTATE_UINT32(tx_priority.u32, CtuCanCoreState),
 667        VMSTATE_UINT32(err_capt_alc.u32, CtuCanCoreState),
 668        VMSTATE_UINT32(trv_delay_ssp_cfg.u32, CtuCanCoreState),
 669        VMSTATE_UINT32(rx_fr_ctr.u32, CtuCanCoreState),
 670        VMSTATE_UINT32(tx_fr_ctr.u32, CtuCanCoreState),
 671        VMSTATE_UINT32(debug_register.u32, CtuCanCoreState),
 672        VMSTATE_UINT32(yolo_reg.u32, CtuCanCoreState),
 673        VMSTATE_UINT32(timestamp_low.u32, CtuCanCoreState),
 674        VMSTATE_UINT32(timestamp_high.u32, CtuCanCoreState),
 675
 676        VMSTATE_STRUCT_ARRAY(tx_buffer, CtuCanCoreState,
 677                CTUCAN_CORE_TXBUF_NUM, 0, vmstate_qemu_ctucan_tx_buffer,
 678                CtuCanCoreMsgBuffer),
 679
 680        VMSTATE_BUFFER(rx_buff, CtuCanCoreState),
 681        VMSTATE_UINT32(rx_tail_pos, CtuCanCoreState),
 682        VMSTATE_UINT32(rx_cnt, CtuCanCoreState),
 683        VMSTATE_UINT32(rx_frame_rem, CtuCanCoreState),
 684
 685        VMSTATE_END_OF_LIST()
 686    }
 687};
 688