| /* | 
 |  * 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 "qemu/bswap.h" | 
 | #include "qemu/bitops.h" | 
 | #include "hw/irq.h" | 
 | #include "migration/vmstate.h" | 
 | #include "net/can_emu.h" | 
 |  | 
 | #include "ctucan_core.h" | 
 |  | 
 | #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) | 
 |  | 
 | 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; | 
 |         uint32_t w; | 
 |  | 
 |         w = le32_to_cpu(*(uint32_t *)buff); | 
 |         frame_form_w = (union ctu_can_fd_frame_form_w)w; | 
 |         frame->can_dlc = can_dlc2len(frame_form_w.s.dlc); | 
 |  | 
 |         w = le32_to_cpu(*(uint32_t *)(buff + 4)); | 
 |         identifier_w = (union ctu_can_fd_identifier_w)w; | 
 |  | 
 |         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) | 
 | { | 
 |     unsigned int bytes_cnt = -1; | 
 |     memset(buff, 0, CTUCAN_MSG_MAX_LEN * sizeof(*buff)); | 
 |  | 
 |     if (frame == NULL) { | 
 |         return bytes_cnt; | 
 |     } | 
 |     { | 
 |         union ctu_can_fd_frame_form_w frame_form_w; | 
 |         union ctu_can_fd_identifier_w identifier_w; | 
 |  | 
 |         frame_form_w.u32 = 0; | 
 |         identifier_w.u32 = 0; | 
 |  | 
 |         bytes_cnt = frame->can_dlc; | 
 |         bytes_cnt = (bytes_cnt + 3) & ~3; | 
 |         bytes_cnt += 16; | 
 |         frame_form_w.s.rwcnt = (bytes_cnt >> 2) - 1; | 
 |  | 
 |         frame_form_w.s.dlc = can_len2dlc(frame->can_dlc); | 
 |  | 
 |         if (frame->can_id & QEMU_CAN_EFF_FLAG) { | 
 |             frame_form_w.s.ide = 1; | 
 |             identifier_w.s.identifier_base = | 
 |                                     (frame->can_id & 0x1FFC0000) >> 18; | 
 |             identifier_w.s.identifier_ext = frame->can_id & 0x3FFFF; | 
 |         } else { | 
 |             identifier_w.s.identifier_base = frame->can_id & 0x7FF; | 
 |         } | 
 |  | 
 |         if (frame->flags & QEMU_CAN_FRMF_ESI) { | 
 |             frame_form_w.s.esi_rsv = 1; | 
 |         } | 
 |  | 
 |         if (frame->can_id & QEMU_CAN_RTR_FLAG) { | 
 |             frame_form_w.s.rtr = 1; | 
 |         } | 
 |  | 
 |         if (frame->flags & QEMU_CAN_FRMF_TYPE_FD) {  /*CAN FD*/ | 
 |            frame_form_w.s.fdf = 1; | 
 |             if (frame->flags & QEMU_CAN_FRMF_BRS) { | 
 |                 frame_form_w.s.brs = 1; | 
 |             } | 
 |         } | 
 |         *(uint32_t *)buff = cpu_to_le32(frame_form_w.u32); | 
 |         *(uint32_t *)(buff + 4) = cpu_to_le32(identifier_w.u32); | 
 |     } | 
 |  | 
 |     memcpy(buff + 0x10, frame->data, 0x40); | 
 |  | 
 |     return bytes_cnt; | 
 | } | 
 |  | 
 | static void ctucan_update_irq(CtuCanCoreState *s) | 
 | { | 
 |     union ctu_can_fd_int_stat int_rq; | 
 |  | 
 |     int_rq.u32 = 0; | 
 |  | 
 |     if (s->rx_status_rx_settings.s.rxfrc) { | 
 |         int_rq.s.rbnei = 1; | 
 |     } | 
 |  | 
 |     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) | 
 | { | 
 |     DPRINTF("Hardware reset in progress!!!\n"); | 
 |     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)); | 
 |     } | 
 |     s->status.s.idle = 1; | 
 |  | 
 |     ctucan_update_txnf(s); | 
 |  | 
 |     s->rx_status_rx_settings.u32 = 0; | 
 |     s->rx_tail_pos = 0; | 
 |     s->rx_cnt = 0; | 
 |     s->rx_frame_rem = 0; | 
 |  | 
 |     /* Flush RX buffer */ | 
 |     s->rx_tail_pos = 0; | 
 |     s->rx_cnt = 0; | 
 |     s->rx_frame_rem = 0; | 
 |  | 
 |     /* Set on progdokum reset value */ | 
 |     s->mode_settings.u32 = 0; | 
 |     s->mode_settings.s.fde = 1; | 
 |  | 
 |     s->int_stat.u32 = 0; | 
 |     s->int_ena.u32 = 0; | 
 |     s->int_mask.u32 = 0; | 
 |  | 
 |     s->rx_status_rx_settings.u32 = 0; | 
 |     s->rx_status_rx_settings.s.rxe = 0; | 
 |  | 
 |     s->rx_fr_ctr.u32 = 0; | 
 |     s->tx_fr_ctr.u32 = 0; | 
 |  | 
 |     s->yolo_reg.s.yolo_val = 3735928559; | 
 |  | 
 |     qemu_irq_lower(s->irq); | 
 | } | 
 |  | 
 | static void ctucan_send_ready_buffers(CtuCanCoreState *s) | 
 | { | 
 |     qemu_can_frame frame; | 
 |     uint8_t *pf; | 
 |     int buff2tx_idx; | 
 |     uint32_t tx_prio_max; | 
 |  | 
 |     if (!s->mode_settings.s.ena) { | 
 |         return; | 
 |     } | 
 |  | 
 |     do { | 
 |         union ctu_can_fd_int_stat int_stat; | 
 |         int i; | 
 |         buff2tx_idx = -1; | 
 |         tx_prio_max = 0; | 
 |  | 
 |         for (i = 0; i < CTUCAN_CORE_TXBUF_NUM; i++) { | 
 |             uint32_t prio; | 
 |  | 
 |             if (extract32(s->tx_status.u32, i * 4, 4) != TXT_RDY) { | 
 |                 continue; | 
 |             } | 
 |             prio = (s->tx_priority.u32 >> (i * 4)) & 0x7; | 
 |             if (tx_prio_max < prio) { | 
 |                 tx_prio_max = prio; | 
 |                 buff2tx_idx = i; | 
 |             } | 
 |         } | 
 |         if (buff2tx_idx == -1) { | 
 |             break; | 
 |         } | 
 |         int_stat.u32 = 0; | 
 |         pf = s->tx_buffer[buff2tx_idx].data; | 
 |         ctucan_buff2frame(pf, &frame); | 
 |         s->status.s.idle = 0; | 
 |         s->status.s.txs = 1; | 
 |         can_bus_client_send(&s->bus_client, &frame, 1); | 
 |         s->status.s.idle = 1; | 
 |         s->status.s.txs = 0; | 
 |         s->tx_fr_ctr.s.tx_fr_ctr_val++; | 
 |         int_stat.s.txi = 1; | 
 |         int_stat.s.txbhci = 1; | 
 |         s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32; | 
 |         s->tx_status.u32 = deposit32(s->tx_status.u32, | 
 |                                      buff2tx_idx * 4, 4, TXT_TOK); | 
 |     } while (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) | 
 | { | 
 |     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) && | 
 |             ((addr + size) <= sizeof(s->tx_buffer[buff_num].data))) { | 
 |             stn_le_p(s->tx_buffer[buff_num].data + addr, size, 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: | 
 |         { | 
 |             union ctu_can_fd_command command; | 
 |             command.u32 = (uint32_t)val; | 
 |             if (command.s.cdo) { | 
 |                 s->status.s.dor = 0; | 
 |             } | 
 |             if (command.s.rrb) { | 
 |                 s->rx_tail_pos = 0; | 
 |                 s->rx_cnt = 0; | 
 |                 s->rx_frame_rem = 0; | 
 |                 s->rx_status_rx_settings.s.rxfrc = 0; | 
 |             } | 
 |             if (command.s.txfcrst) { | 
 |                 s->tx_fr_ctr.s.tx_fr_ctr_val = 0; | 
 |             } | 
 |             if (command.s.rxfcrst) { | 
 |                 s->rx_fr_ctr.s.rx_fr_ctr_val = 0; | 
 |             } | 
 |             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; | 
 |  | 
 |                 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)) | 
 |                             buff_st = TXT_RDY; | 
 |                     } | 
 |                     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_send_ready_buffers(s); | 
 |                 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: | 
 |         s->rx_mem_info.u32 = 0; | 
 |         s->rx_mem_info.s.rx_buff_size = CTUCAN_RCV_BUF_LEN >> 2; | 
 |         s->rx_mem_info.s.rx_mem_free = (CTUCAN_RCV_BUF_LEN - | 
 |                                         s->rx_cnt) >> 2; | 
 |         val = s->rx_mem_info.u32; | 
 |         break; | 
 |     case CTU_CAN_FD_RX_POINTERS: | 
 |     { | 
 |         uint32_t rx_head_pos = s->rx_tail_pos + s->rx_cnt; | 
 |         rx_head_pos %= CTUCAN_RCV_BUF_LEN; | 
 |         s->rx_pointers.s.rx_wpp = rx_head_pos; | 
 |         s->rx_pointers.s.rx_rpp = s->rx_tail_pos; | 
 |         val = s->rx_pointers.u32; | 
 |         break; | 
 |     } | 
 |     case CTU_CAN_FD_RX_STATUS: | 
 |     case CTU_CAN_FD_RX_SETTINGS: | 
 |         if (!s->rx_status_rx_settings.s.rxfrc) { | 
 |             s->rx_status_rx_settings.s.rxe = 1; | 
 |         } else { | 
 |             s->rx_status_rx_settings.s.rxe = 0; | 
 |         } | 
 |         if (((s->rx_cnt + 3) & ~3) == CTUCAN_RCV_BUF_LEN) { | 
 |             s->rx_status_rx_settings.s.rxf = 1; | 
 |         } else { | 
 |             s->rx_status_rx_settings.s.rxf = 0; | 
 |         } | 
 |         val = s->rx_status_rx_settings.u32; | 
 |         break; | 
 |     case CTU_CAN_FD_RX_DATA: | 
 |         if (s->rx_cnt) { | 
 |             memcpy(&val, s->rx_buff + s->rx_tail_pos, 4); | 
 |             val = le32_to_cpu(val); | 
 |             if (!s->rx_frame_rem) { | 
 |                 union ctu_can_fd_frame_form_w frame_form_w; | 
 |                 frame_form_w.u32 = val; | 
 |                 s->rx_frame_rem = frame_form_w.s.rwcnt * 4 + 4; | 
 |             } | 
 |             s->rx_cnt -= 4; | 
 |             s->rx_frame_rem -= 4; | 
 |             if (!s->rx_frame_rem) { | 
 |                 s->rx_status_rx_settings.s.rxfrc--; | 
 |                 if (!s->rx_status_rx_settings.s.rxfrc) { | 
 |                     s->status.s.rxne = 0; | 
 |                     s->status.s.idle = 1; | 
 |                     s->status.s.rxs = 0; | 
 |                 } | 
 |             } | 
 |             s->rx_tail_pos = (s->rx_tail_pos + 4) % CTUCAN_RCV_BUF_LEN; | 
 |         } else { | 
 |             val = 0; | 
 |         } | 
 |         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; | 
 |     case CTU_CAN_FD_RX_FR_CTR: | 
 |         val = s->rx_fr_ctr.s.rx_fr_ctr_val; | 
 |         break; | 
 |     case CTU_CAN_FD_TX_FR_CTR: | 
 |         val = s->tx_fr_ctr.s.tx_fr_ctr_val; | 
 |         break; | 
 |     case CTU_CAN_FD_YOLO_REG: | 
 |         val = s->yolo_reg.s.yolo_val; | 
 |         break; | 
 |     } | 
 |  | 
 |     val >>= ((addr & 3) << 3); | 
 |     if (size < 8) { | 
 |         val &= ((uint64_t)1 << (size << 3)) - 1; | 
 |     } | 
 |  | 
 |     return val; | 
 | } | 
 |  | 
 | bool ctucan_can_receive(CanBusClientState *client) | 
 | { | 
 |     CtuCanCoreState *s = container_of(client, CtuCanCoreState, bus_client); | 
 |  | 
 |     if (!s->mode_settings.s.ena) { | 
 |         return false; | 
 |     } | 
 |  | 
 |     return true; /* always return true, 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; | 
 |     union ctu_can_fd_int_stat int_stat; | 
 |     int_stat.u32 = 0; | 
 |  | 
 |     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; | 
 |         int_stat.s.doi = 1; | 
 |         s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32; | 
 |         ctucan_update_irq(s); | 
 |         DPRINTF("Receive FIFO overrun\n"); | 
 |         return ret; | 
 |     } | 
 |     s->status.s.idle = 0; | 
 |     s->status.s.rxs = 1; | 
 |     int_stat.s.rxi = 1; | 
 |     if (((s->rx_cnt + 3) & ~3) == CTUCAN_RCV_BUF_LEN) { | 
 |         int_stat.s.rxfi = 1; | 
 |     } | 
 |     s->int_stat.u32 |= int_stat.u32 & ~s->int_mask.u32; | 
 |     s->rx_fr_ctr.s.rx_fr_ctr_val++; | 
 |     s->rx_status_rx_settings.s.rxfrc++; | 
 |     for (i = 0; i < ret; i++) { | 
 |         s->rx_buff[(s->rx_tail_pos + s->rx_cnt) % CTUCAN_RCV_BUF_LEN] = rcv[i]; | 
 |         s->rx_cnt++; | 
 |     } | 
 |     s->status.s.rxne = 1; | 
 |  | 
 |     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_tx_buffer = { | 
 |     .name = "qemu_ctucan_tx_buffer", | 
 |     .version_id = 1, | 
 |     .minimum_version_id = 1, | 
 |     .fields = (const 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, | 
 |     .post_load = ctucan_post_load, | 
 |     .fields = (const VMStateField[]) { | 
 |         VMSTATE_UINT32(mode_settings.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(status.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(int_stat.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(int_ena.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(int_mask.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(brt.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(brt_fd.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(ewl_erp_fault_state.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(rec_tec.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(err_norm_err_fd.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(ctr_pres.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(filter_a_mask.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(filter_a_val.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(filter_b_mask.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(filter_b_val.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(filter_c_mask.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(filter_c_val.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(filter_ran_low.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(filter_ran_high.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(filter_control_filter_status.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(rx_mem_info.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(rx_pointers.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(rx_status_rx_settings.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(tx_status.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(tx_priority.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(err_capt_alc.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(trv_delay_ssp_cfg.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(rx_fr_ctr.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(tx_fr_ctr.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(debug_register.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(yolo_reg.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(timestamp_low.u32, CtuCanCoreState), | 
 |         VMSTATE_UINT32(timestamp_high.u32, CtuCanCoreState), | 
 |  | 
 |         VMSTATE_STRUCT_ARRAY(tx_buffer, CtuCanCoreState, | 
 |                 CTUCAN_CORE_TXBUF_NUM, 0, vmstate_qemu_ctucan_tx_buffer, | 
 |                 CtuCanCoreMsgBuffer), | 
 |  | 
 |         VMSTATE_BUFFER(rx_buff, CtuCanCoreState), | 
 |         VMSTATE_UINT32(rx_tail_pos, CtuCanCoreState), | 
 |         VMSTATE_UINT32(rx_cnt, CtuCanCoreState), | 
 |         VMSTATE_UINT32(rx_frame_rem, CtuCanCoreState), | 
 |  | 
 |         VMSTATE_END_OF_LIST() | 
 |     } | 
 | }; |