/* * CAN device - SJA1000 chip emulation for QEMU * * Copyright (c) 2013-2014 Jin Yang * Copyright (c) 2014-2018 Pavel Pisa * * Initial development supported by Google GSoC 2013 from RTEMS project slot * * 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 "can_sja1000.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("[cansja]: " fmt , ## __VA_ARGS__); \ } \ } while (0) static void can_sja_software_reset(CanSJA1000State *s) { s->mode &= ~0x31; s->mode |= 0x01; s->status_pel &= ~0x37; s->status_pel |= 0x34; s->rxbuf_start = 0x00; s->rxmsg_cnt = 0x00; s->rx_cnt = 0x00; } void can_sja_hardware_reset(CanSJA1000State *s) { /* Reset by hardware, p10 */ s->mode = 0x01; s->status_pel = 0x3c; s->interrupt_pel = 0x00; s->clock = 0x00; s->rxbuf_start = 0x00; s->rxmsg_cnt = 0x00; s->rx_cnt = 0x00; s->control = 0x01; s->status_bas = 0x0c; s->interrupt_bas = 0x00; qemu_irq_lower(s->irq); } static void can_sja_single_filter(struct qemu_can_filter *filter, const uint8_t *acr, const uint8_t *amr, int extended) { if (extended) { filter->can_id = (uint32_t)acr[0] << 21; filter->can_id |= (uint32_t)acr[1] << 13; filter->can_id |= (uint32_t)acr[2] << 5; filter->can_id |= (uint32_t)acr[3] >> 3; if (acr[3] & 4) { filter->can_id |= QEMU_CAN_RTR_FLAG; } filter->can_mask = (uint32_t)amr[0] << 21; filter->can_mask |= (uint32_t)amr[1] << 13; filter->can_mask |= (uint32_t)amr[2] << 5; filter->can_mask |= (uint32_t)amr[3] >> 3; filter->can_mask = ~filter->can_mask & QEMU_CAN_EFF_MASK; if (!(amr[3] & 4)) { filter->can_mask |= QEMU_CAN_RTR_FLAG; } } else { filter->can_id = (uint32_t)acr[0] << 3; filter->can_id |= (uint32_t)acr[1] >> 5; if (acr[1] & 0x10) { filter->can_id |= QEMU_CAN_RTR_FLAG; } filter->can_mask = (uint32_t)amr[0] << 3; filter->can_mask |= (uint32_t)amr[1] << 5; filter->can_mask = ~filter->can_mask & QEMU_CAN_SFF_MASK; if (!(amr[1] & 0x10)) { filter->can_mask |= QEMU_CAN_RTR_FLAG; } } } static void can_sja_dual_filter(struct qemu_can_filter *filter, const uint8_t *acr, const uint8_t *amr, int extended) { if (extended) { filter->can_id = (uint32_t)acr[0] << 21; filter->can_id |= (uint32_t)acr[1] << 13; filter->can_mask = (uint32_t)amr[0] << 21; filter->can_mask |= (uint32_t)amr[1] << 13; filter->can_mask = ~filter->can_mask & QEMU_CAN_EFF_MASK & ~0x1fff; } else { filter->can_id = (uint32_t)acr[0] << 3; filter->can_id |= (uint32_t)acr[1] >> 5; if (acr[1] & 0x10) { filter->can_id |= QEMU_CAN_RTR_FLAG; } filter->can_mask = (uint32_t)amr[0] << 3; filter->can_mask |= (uint32_t)amr[1] >> 5; filter->can_mask = ~filter->can_mask & QEMU_CAN_SFF_MASK; if (!(amr[1] & 0x10)) { filter->can_mask |= QEMU_CAN_RTR_FLAG; } } } /* Details in DS-p22, what we need to do here is to test the data. */ static int can_sja_accept_filter(CanSJA1000State *s, const qemu_can_frame *frame) { struct qemu_can_filter filter; if (s->clock & 0x80) { /* PeliCAN Mode */ if (s->mode & (1 << 3)) { /* Single mode. */ if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */ can_sja_single_filter(&filter, s->code_mask + 0, s->code_mask + 4, 1); if (!can_bus_filter_match(&filter, frame->can_id)) { return 0; } } else { /* SFF */ can_sja_single_filter(&filter, s->code_mask + 0, s->code_mask + 4, 0); if (!can_bus_filter_match(&filter, frame->can_id)) { return 0; } if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */ return 1; } if (frame->can_dlc == 0) { return 1; } if ((frame->data[0] & ~(s->code_mask[6])) != (s->code_mask[2] & ~(s->code_mask[6]))) { return 0; } if (frame->can_dlc < 2) { return 1; } if ((frame->data[1] & ~(s->code_mask[7])) == (s->code_mask[3] & ~(s->code_mask[7]))) { return 1; } return 0; } } else { /* Dual mode */ if (frame->can_id & QEMU_CAN_EFF_FLAG) { /* EFF */ can_sja_dual_filter(&filter, s->code_mask + 0, s->code_mask + 4, 1); if (can_bus_filter_match(&filter, frame->can_id)) { return 1; } can_sja_dual_filter(&filter, s->code_mask + 2, s->code_mask + 6, 1); if (can_bus_filter_match(&filter, frame->can_id)) { return 1; } return 0; } else { can_sja_dual_filter(&filter, s->code_mask + 0, s->code_mask + 4, 0); if (can_bus_filter_match(&filter, frame->can_id)) { uint8_t expect; uint8_t mask; expect = s->code_mask[1] << 4; expect |= s->code_mask[3] & 0x0f; mask = s->code_mask[5] << 4; mask |= s->code_mask[7] & 0x0f; mask = ~mask & 0xff; if ((frame->data[0] & mask) == (expect & mask)) { return 1; } } can_sja_dual_filter(&filter, s->code_mask + 2, s->code_mask + 6, 0); if (can_bus_filter_match(&filter, frame->can_id)) { return 1; } return 0; } } } return 1; } 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(); } static void buff2frame_pel(const uint8_t *buff, qemu_can_frame *frame) { uint8_t i; frame->can_id = 0; if (buff[0] & 0x40) { /* RTR */ frame->can_id = QEMU_CAN_RTR_FLAG; } frame->can_dlc = buff[0] & 0x0f; if (buff[0] & 0x80) { /* Extended */ frame->can_id |= QEMU_CAN_EFF_FLAG; frame->can_id |= buff[1] << 21; /* ID.28~ID.21 */ frame->can_id |= buff[2] << 13; /* ID.20~ID.13 */ frame->can_id |= buff[3] << 5; frame->can_id |= buff[4] >> 3; for (i = 0; i < frame->can_dlc; i++) { frame->data[i] = buff[5 + i]; } for (; i < 8; i++) { frame->data[i] = 0; } } else { frame->can_id |= buff[1] << 3; frame->can_id |= buff[2] >> 5; for (i = 0; i < frame->can_dlc; i++) { frame->data[i] = buff[3 + i]; } for (; i < 8; i++) { frame->data[i] = 0; } } } static void buff2frame_bas(const uint8_t *buff, qemu_can_frame *frame) { uint8_t i; frame->can_id = ((buff[0] << 3) & (0xff << 3)) + ((buff[1] >> 5) & 0x07); if (buff[1] & 0x10) { /* RTR */ frame->can_id = QEMU_CAN_RTR_FLAG; } frame->can_dlc = buff[1] & 0x0f; for (i = 0; i < frame->can_dlc; i++) { frame->data[i] = buff[2 + i]; } for (; i < 8; i++) { frame->data[i] = 0; } } static int frame2buff_pel(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 int frame2buff_bas(const qemu_can_frame *frame, uint8_t *buff) { int i; /* * EFF, no support for BasicMode * No use for Error frames now, * they could be used in future to update SJA1000 error state */ if ((frame->can_id & QEMU_CAN_EFF_FLAG) || (frame->can_id & QEMU_CAN_ERR_FLAG)) { return -1; } buff[0] = extract32(frame->can_id, 3, 8); /* ID.10~ID.03 */ buff[1] = extract32(frame->can_id, 0, 3) << 5; /* ID.02~ID.00,xxxxx */ if (frame->can_id & QEMU_CAN_RTR_FLAG) { /* RTR */ buff[1] |= (1 << 4); } buff[1] |= frame->can_dlc & 0x0f; for (i = 0; i < frame->can_dlc; i++) { buff[2 + i] = frame->data[i]; } return frame->can_dlc + 2; } static void can_sja_update_pel_irq(CanSJA1000State *s) { if (s->interrupt_en & s->interrupt_pel) { qemu_irq_raise(s->irq); } else { qemu_irq_lower(s->irq); } } static void can_sja_update_bas_irq(CanSJA1000State *s) { if ((s->control >> 1) & s->interrupt_bas) { qemu_irq_raise(s->irq); } else { qemu_irq_lower(s->irq); } } void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val, unsigned size) { qemu_can_frame frame; uint32_t tmp; uint8_t tmp8, count; DPRINTF("write 0x%02llx addr 0x%02x\n", (unsigned long long)val, (unsigned int)addr); if (addr > CAN_SJA_MEM_SIZE) { return ; } if (s->clock & 0x80) { /* PeliCAN Mode */ switch (addr) { case SJA_MOD: /* Mode register */ s->mode = 0x1f & val; if ((s->mode & 0x01) && ((val & 0x01) == 0)) { /* Go to operation mode from reset mode. */ if (s->mode & (1 << 3)) { /* Single mode. */ /* For EFF */ can_sja_single_filter(&s->filter[0], s->code_mask + 0, s->code_mask + 4, 1); /* For SFF */ can_sja_single_filter(&s->filter[1], s->code_mask + 0, s->code_mask + 4, 0); can_bus_client_set_filters(&s->bus_client, s->filter, 2); } else { /* Dual mode */ /* For EFF */ can_sja_dual_filter(&s->filter[0], s->code_mask + 0, s->code_mask + 4, 1); can_sja_dual_filter(&s->filter[1], s->code_mask + 2, s->code_mask + 6, 1); /* For SFF */ can_sja_dual_filter(&s->filter[2], s->code_mask + 0, s->code_mask + 4, 0); can_sja_dual_filter(&s->filter[3], s->code_mask + 2, s->code_mask + 6, 0); can_bus_client_set_filters(&s->bus_client, s->filter, 4); } s->rxmsg_cnt = 0; s->rx_cnt = 0; } break; case SJA_CMR: /* Command register. */ if (0x01 & val) { /* Send transmission request. */ buff2frame_pel(s->tx_buff, &frame); if (DEBUG_FILTER) { can_display_msg("[cansja]: Tx request " , &frame); } /* * Clear transmission complete status, * and Transmit Buffer Status. * write to the backends. */ s->status_pel &= ~(3 << 2); can_bus_client_send(&s->bus_client, &frame, 1); /* * Set transmission complete status * and Transmit Buffer Status. */ s->status_pel |= (3 << 2); /* Clear transmit status. */ s->status_pel &= ~(1 << 5); s->interrupt_pel |= 0x02; can_sja_update_pel_irq(s); } if (0x04 & val) { /* Release Receive Buffer */ if (s->rxmsg_cnt <= 0) { break; } tmp8 = s->rx_buff[s->rxbuf_start]; count = 0; if (tmp8 & (1 << 7)) { /* EFF */ count += 2; } count += 3; if (!(tmp8 & (1 << 6))) { /* DATA */ count += (tmp8 & 0x0f); } if (DEBUG_FILTER) { qemu_log("[cansja]: message released from " "Rx FIFO cnt=%d, count=%d\n", s->rx_cnt, count); } s->rxbuf_start += count; s->rxbuf_start %= SJA_RCV_BUF_LEN; s->rx_cnt -= count; s->rxmsg_cnt--; if (s->rxmsg_cnt == 0) { s->status_pel &= ~(1 << 0); s->interrupt_pel &= ~(1 << 0); can_sja_update_pel_irq(s); } } if (0x08 & val) { /* Clear data overrun */ s->status_pel &= ~(1 << 1); s->interrupt_pel &= ~(1 << 3); can_sja_update_pel_irq(s); } break; case SJA_SR: /* Status register */ case SJA_IR: /* Interrupt register */ break; /* Do nothing */ case SJA_IER: /* Interrupt enable register */ s->interrupt_en = val; break; case 16: /* RX frame information addr16-28. */ s->status_pel |= (1 << 5); /* Set transmit status. */ case 17 ... 28: if (s->mode & 0x01) { /* Reset mode */ if (addr < 24) { s->code_mask[addr - 16] = val; } } else { /* Operation mode */ s->tx_buff[addr - 16] = val; /* Store to TX buffer directly. */ } break; case SJA_CDR: s->clock = val; break; } } else { /* Basic Mode */ switch (addr) { case SJA_BCAN_CTR: /* Control register, addr 0 */ if ((s->control & 0x01) && ((val & 0x01) == 0)) { /* Go to operation mode from reset mode. */ s->filter[0].can_id = (s->code << 3) & (0xff << 3); tmp = (~(s->mask << 3)) & (0xff << 3); tmp |= QEMU_CAN_EFF_FLAG; /* Only Basic CAN Frame. */ s->filter[0].can_mask = tmp; can_bus_client_set_filters(&s->bus_client, s->filter, 1); s->rxmsg_cnt = 0; s->rx_cnt = 0; } else if (!(s->control & 0x01) && !(val & 0x01)) { can_sja_software_reset(s); } s->control = 0x1f & val; break; case SJA_BCAN_CMR: /* Command register, addr 1 */ if (0x01 & val) { /* Send transmission request. */ buff2frame_bas(s->tx_buff, &frame); if (DEBUG_FILTER) { can_display_msg("[cansja]: Tx request " , &frame); } /* * Clear transmission complete status, * and Transmit Buffer Status. */ s->status_bas &= ~(3 << 2); /* write to the backends. */ can_bus_client_send(&s->bus_client, &frame, 1); /* * Set transmission complete status, * and Transmit Buffer Status. */ s->status_bas |= (3 << 2); /* Clear transmit status. */ s->status_bas &= ~(1 << 5); s->interrupt_bas |= 0x02; can_sja_update_bas_irq(s); } if (0x04 & val) { /* Release Receive Buffer */ if (s->rxmsg_cnt <= 0) { break; } tmp8 = s->rx_buff[(s->rxbuf_start + 1) % SJA_RCV_BUF_LEN]; count = 2 + (tmp8 & 0x0f); if (DEBUG_FILTER) { qemu_log("[cansja]: message released from " "Rx FIFO cnt=%d, count=%d\n", s->rx_cnt, count); } s->rxbuf_start += count; s->rxbuf_start %= SJA_RCV_BUF_LEN; s->rx_cnt -= count; s->rxmsg_cnt--; if (s->rxmsg_cnt == 0) { s->status_bas &= ~(1 << 0); s->interrupt_bas &= ~(1 << 0); can_sja_update_bas_irq(s); } } if (0x08 & val) { /* Clear data overrun */ s->status_bas &= ~(1 << 1); s->interrupt_bas &= ~(1 << 3); can_sja_update_bas_irq(s); } break; case 4: s->code = val; break; case 5: s->mask = val; break; case 10: s->status_bas |= (1 << 5); /* Set transmit status. */ case 11 ... 19: if ((s->control & 0x01) == 0) { /* Operation mode */ s->tx_buff[addr - 10] = val; /* Store to TX buffer directly. */ } break; case SJA_CDR: s->clock = val; break; } } } uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size) { uint64_t temp = 0; DPRINTF("read addr 0x%02x ...\n", (unsigned int)addr); if (addr > CAN_SJA_MEM_SIZE) { return 0; } if (s->clock & 0x80) { /* PeliCAN Mode */ switch (addr) { case SJA_MOD: /* Mode register, addr 0 */ temp = s->mode; break; case SJA_CMR: /* Command register, addr 1 */ temp = 0x00; /* Command register, cannot be read. */ break; case SJA_SR: /* Status register, addr 2 */ temp = s->status_pel; break; case SJA_IR: /* Interrupt register, addr 3 */ temp = s->interrupt_pel; s->interrupt_pel = 0; if (s->rxmsg_cnt) { s->interrupt_pel |= (1 << 0); /* Receive interrupt. */ } can_sja_update_pel_irq(s); break; case SJA_IER: /* Interrupt enable register, addr 4 */ temp = s->interrupt_en; break; case 5: /* Reserved */ case 6: /* Bus timing 0, hardware related, not support now. */ case 7: /* Bus timing 1, hardware related, not support now. */ case 8: /* * Output control register, hardware related, * not supported for now. */ case 9: /* Test. */ case 10 ... 15: /* Reserved */ temp = 0x00; break; case 16 ... 28: if (s->mode & 0x01) { /* Reset mode */ if (addr < 24) { temp = s->code_mask[addr - 16]; } else { temp = 0x00; } } else { /* Operation mode */ temp = s->rx_buff[(s->rxbuf_start + addr - 16) % SJA_RCV_BUF_LEN]; } break; case SJA_CDR: temp = s->clock; break; default: temp = 0xff; } } else { /* Basic Mode */ switch (addr) { case SJA_BCAN_CTR: /* Control register, addr 0 */ temp = s->control; break; case SJA_BCAN_SR: /* Status register, addr 2 */ temp = s->status_bas; break; case SJA_BCAN_IR: /* Interrupt register, addr 3 */ temp = s->interrupt_bas; s->interrupt_bas = 0; if (s->rxmsg_cnt) { s->interrupt_bas |= (1 << 0); /* Receive interrupt. */ } can_sja_update_bas_irq(s); break; case 4: temp = s->code; break; case 5: temp = s->mask; break; case 20 ... 29: temp = s->rx_buff[(s->rxbuf_start + addr - 20) % SJA_RCV_BUF_LEN]; break; case 31: temp = s->clock; break; default: temp = 0xff; break; } } DPRINTF("read addr 0x%02x, %d bytes, content 0x%02lx\n", (int)addr, size, (long unsigned int)temp); return temp; } int can_sja_can_receive(CanBusClientState *client) { CanSJA1000State *s = container_of(client, CanSJA1000State, bus_client); if (s->clock & 0x80) { /* PeliCAN Mode */ if (s->mode & 0x01) { /* reset mode. */ return 0; } } else { /* BasicCAN mode */ if (s->control & 0x01) { return 0; } } return 1; /* always return 1, when operation mode */ } ssize_t can_sja_receive(CanBusClientState *client, const qemu_can_frame *frames, size_t frames_cnt) { CanSJA1000State *s = container_of(client, CanSJA1000State, bus_client); static uint8_t rcv[SJA_MSG_MAX_LEN]; int i; int ret = -1; const qemu_can_frame *frame = frames; if (frames_cnt <= 0) { return 0; } if (DEBUG_FILTER) { can_display_msg("[cansja]: receive ", frame); } if (s->clock & 0x80) { /* PeliCAN Mode */ /* the CAN controller is receiving a message */ s->status_pel |= (1 << 4); if (can_sja_accept_filter(s, frame) == 0) { s->status_pel &= ~(1 << 4); if (DEBUG_FILTER) { qemu_log("[cansja]: filter rejects message\n"); } return ret; } ret = frame2buff_pel(frame, rcv); if (ret < 0) { s->status_pel &= ~(1 << 4); if (DEBUG_FILTER) { qemu_log("[cansja]: message store failed\n"); } return ret; /* maybe not support now. */ } if (s->rx_cnt + ret > SJA_RCV_BUF_LEN) { /* Data overrun. */ s->status_pel |= (1 << 1); /* Overrun status */ s->interrupt_pel |= (1 << 3); s->status_pel &= ~(1 << 4); if (DEBUG_FILTER) { qemu_log("[cansja]: receive FIFO overrun\n"); } can_sja_update_pel_irq(s); return ret; } s->rx_cnt += ret; s->rxmsg_cnt++; if (DEBUG_FILTER) { qemu_log("[cansja]: message stored in receive FIFO\n"); } for (i = 0; i < ret; i++) { s->rx_buff[(s->rx_ptr++) % SJA_RCV_BUF_LEN] = rcv[i]; } s->rx_ptr %= SJA_RCV_BUF_LEN; /* update the pointer. */ s->status_pel |= 0x01; /* Set the Receive Buffer Status. DS-p23 */ s->interrupt_pel |= 0x01; s->status_pel &= ~(1 << 4); s->status_pel |= (1 << 0); can_sja_update_pel_irq(s); } else { /* BasicCAN mode */ /* the CAN controller is receiving a message */ s->status_bas |= (1 << 4); ret = frame2buff_bas(frame, rcv); if (ret < 0) { s->status_bas &= ~(1 << 4); if (DEBUG_FILTER) { qemu_log("[cansja]: message store failed\n"); } return ret; /* maybe not support now. */ } if (s->rx_cnt + ret > SJA_RCV_BUF_LEN) { /* Data overrun. */ s->status_bas |= (1 << 1); /* Overrun status */ s->status_bas &= ~(1 << 4); s->interrupt_bas |= (1 << 3); can_sja_update_bas_irq(s); if (DEBUG_FILTER) { qemu_log("[cansja]: receive FIFO overrun\n"); } return ret; } s->rx_cnt += ret; s->rxmsg_cnt++; if (DEBUG_FILTER) { qemu_log("[cansja]: message stored\n"); } for (i = 0; i < ret; i++) { s->rx_buff[(s->rx_ptr++) % SJA_RCV_BUF_LEN] = rcv[i]; } s->rx_ptr %= SJA_RCV_BUF_LEN; /* update the pointer. */ s->status_bas |= 0x01; /* Set the Receive Buffer Status. DS-p15 */ s->status_bas &= ~(1 << 4); s->interrupt_bas |= (1 << 0); can_sja_update_bas_irq(s); } return 1; } static CanBusClientInfo can_sja_bus_client_info = { .can_receive = can_sja_can_receive, .receive = can_sja_receive, }; int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus) { s->bus_client.info = &can_sja_bus_client_info; if (!bus) { return -EINVAL; } if (can_bus_insert_client(bus, &s->bus_client) < 0) { return -1; } return 0; } void can_sja_disconnect(CanSJA1000State *s) { can_bus_remove_client(&s->bus_client); } int can_sja_init(CanSJA1000State *s, qemu_irq irq) { s->irq = irq; qemu_irq_lower(s->irq); can_sja_hardware_reset(s); return 0; } const VMStateDescription vmstate_qemu_can_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() } }; static int can_sja_post_load(void *opaque, int version_id) { CanSJA1000State *s = opaque; if (s->clock & 0x80) { /* PeliCAN Mode */ can_sja_update_pel_irq(s); } else { can_sja_update_bas_irq(s); } return 0; } /* VMState is needed for live migration of QEMU images */ const VMStateDescription vmstate_can_sja = { .name = "can_sja", .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, .post_load = can_sja_post_load, .fields = (VMStateField[]) { VMSTATE_UINT8(mode, CanSJA1000State), VMSTATE_UINT8(status_pel, CanSJA1000State), VMSTATE_UINT8(interrupt_pel, CanSJA1000State), VMSTATE_UINT8(interrupt_en, CanSJA1000State), VMSTATE_UINT8(rxmsg_cnt, CanSJA1000State), VMSTATE_UINT8(rxbuf_start, CanSJA1000State), VMSTATE_UINT8(clock, CanSJA1000State), VMSTATE_BUFFER(code_mask, CanSJA1000State), VMSTATE_BUFFER(tx_buff, CanSJA1000State), VMSTATE_BUFFER(rx_buff, CanSJA1000State), VMSTATE_UINT32(rx_ptr, CanSJA1000State), VMSTATE_UINT32(rx_cnt, CanSJA1000State), VMSTATE_UINT8(control, CanSJA1000State), VMSTATE_UINT8(status_bas, CanSJA1000State), VMSTATE_UINT8(interrupt_bas, CanSJA1000State), VMSTATE_UINT8(code, CanSJA1000State), VMSTATE_UINT8(mask, CanSJA1000State), VMSTATE_STRUCT_ARRAY(filter, CanSJA1000State, 4, 0, vmstate_qemu_can_filter, qemu_can_filter), VMSTATE_END_OF_LIST() } };