Message ID | 7b291e4c0fd03581e00c6c22b5be7fe764ab0b76.1515960078.git.pisa@cmp.felk.cvut.cz |
---|---|
State | New |
Headers | show |
Series | CAN bus support for QEMU (SJA1000 PCI so far) | expand |
On 01/14/2018 05:14 PM, pisa@cmp.felk.cvut.cz wrote: > From: Pavel Pisa <pisa@cmp.felk.cvut.cz> > > The core SJA1000 support is independent of following > patches which map SJA1000 chip to PCI boards. > > The work is based on Jin Yang GSoC 2013 work funded > by Google and mentored in frame of RTEMS project GSoC > slot donated to QEMU. > > Rewritten for QEMU-2.0+ versions and architecture cleanup > by Pavel Pisa (Czech Technical University in Prague). > > Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz> > --- > default-configs/pci.mak | 1 + > hw/can/Makefile.objs | 1 + > hw/can/can_sja1000.c | 1013 +++++++++++++++++++++++++++++++++++++++++++++++ > hw/can/can_sja1000.h | 167 ++++++++ > 4 files changed, 1182 insertions(+) > create mode 100644 hw/can/can_sja1000.c > create mode 100644 hw/can/can_sja1000.h > > diff --git a/default-configs/pci.mak b/default-configs/pci.mak > index bbe11887a1..979b649fe5 100644 > --- a/default-configs/pci.mak > +++ b/default-configs/pci.mak > @@ -32,6 +32,7 @@ CONFIG_SERIAL=y > CONFIG_SERIAL_ISA=y > CONFIG_SERIAL_PCI=y > CONFIG_CAN_CORE=y > +CONFIG_CAN_SJA1000=y > CONFIG_IPACK=y > CONFIG_WDT_IB6300ESB=y > CONFIG_PCI_TESTDEV=y > diff --git a/hw/can/Makefile.objs b/hw/can/Makefile.objs > index f999085f7a..3c4bf3bfc1 100644 > --- a/hw/can/Makefile.objs > +++ b/hw/can/Makefile.objs > @@ -7,4 +7,5 @@ common-obj-y += can_socketcan.o > else > common-obj-y += can_host_stub.o > endif > +common-obj-$(CONFIG_CAN_SJA1000) += can_sja1000.o > endif > diff --git a/hw/can/can_sja1000.c b/hw/can/can_sja1000.c > new file mode 100644 > index 0000000000..7f7a6ea244 > --- /dev/null > +++ b/hw/can/can_sja1000.c > @@ -0,0 +1,1013 @@ > +/* > + * 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/hw.h" > +#include "can/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; > + > + s->irq_lower(s->irq_opaque); > +} > + > +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; > + } I hope we inline that later... > + > + 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; > + } ditto. > + > + 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] & 4)) { > + 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; > +} > + > +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; > + if (s->interrupt_en & 0x02) { > + s->irq_raise(s->irq_opaque); > + } > + } > + 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); > + } > + if ((s->interrupt_en & 0x01) && (s->interrupt_pel == 0)) { > + /* no other interrupts. */ > + s->irq_lower(s->irq_opaque); > + } > + } > + if (0x08 & val) { /* Clear data overrun */ > + s->status_pel &= ~(1 << 1); > + s->interrupt_pel &= ~(1 << 3); > + if ((s->interrupt_en & 0x80) && (s->interrupt_pel == 0)) { > + /* no other interrupts. */ > + s->irq_lower(s->irq_opaque); > + } > + } > + 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: > + case 18: > + case 19: > + case 20: > + case 21: > + case 22: > + case 23: > + case 24: > + case 25: > + case 26: > + case 27: > + case 28: 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; > + if (s->control & 0x04) { > + s->irq_raise(s->irq_opaque); > + } > + } > + if (0x04 & val) { /* Release Receive Buffer */ > + if (s->rxmsg_cnt <= 0) { > + break; > + } > + > + qemu_mutex_lock(&s->rx_lock); > + 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--; > + qemu_mutex_unlock(&s->rx_lock); > + > + if (s->rxmsg_cnt == 0) { > + s->status_bas &= ~(1 << 0); > + s->interrupt_bas &= ~(1 << 0); > + } > + if ((s->control & 0x02) && (s->interrupt_bas == 0)) { > + /* no other interrupts. */ > + s->irq_lower(s->irq_opaque); > + } > + } > + if (0x08 & val) { /* Clear data overrun */ > + s->status_bas &= ~(1 << 1); > + s->interrupt_bas &= ~(1 << 3); > + if ((s->control & 0x10) && (s->interrupt_bas == 0)) { > + /* no other interrupts. */ > + s->irq_lower(s->irq_opaque); > + } > + } > + 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: > + case 12: > + case 13: > + case 14: > + case 15: > + case 16: > + case 17: > + case 18: > + case 19: 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. */ > + break; > + } > + s->irq_lower(s->irq_opaque); > + 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: /* Reserved */ > + case 11: > + case 12: > + case 13: > + case 14: > + case 15: > + temp = 0x00; > + break; > + > + case 16: > + case 17: > + case 18: > + case 19: > + case 20: > + case 21: > + case 22: > + case 23: > + case 24: > + case 25: > + case 26: > + case 27: > + case 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. */ > + break; > + } > + s->irq_lower(s->irq_opaque); > + break; > + case 4: > + temp = s->code; > + break; > + case 5: > + temp = s->mask; > + break; > + case 20: > + case 21: > + case 22: > + case 23: > + case 24: > + case 25: > + case 26: > + case 27: > + case 28: > + case 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); > + } > + > + qemu_mutex_lock(&s->rx_lock); /* Just do it quickly :) */ > + 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"); > + } > + goto fail; > + } > + > + ret = frame2buff_pel(frame, rcv); > + if (ret < 0) { > + s->status_pel &= ~(1 << 4); > + if (DEBUG_FILTER) { > + qemu_log("[cansja]: message store failed\n"); > + } > + goto fail; /* 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); > + if (s->interrupt_en & (1 << 3)) { /* Overrun interrupt enable */ > + s->irq_raise(s->irq_opaque); > + } > + s->status_pel &= ~(1 << 4); > + if (DEBUG_FILTER) { > + qemu_log("[cansja]: receive FIFO overrun\n"); > + } > + goto fail; > + } > + 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); > + if (s->interrupt_en & 0x01) { /* Receive Interrupt enable. */ > + s->irq_raise(s->irq_opaque); > + } > + } 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"); > + } > + goto fail; /* 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); > + if (s->control & (1 << 4)) { /* Overrun interrupt enable */ > + s->irq_raise(s->irq_opaque); > + } > + if (DEBUG_FILTER) { > + qemu_log("[cansja]: receive FIFO overrun\n"); > + } > + goto fail; > + } > + 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 |= 0x01; > + if (s->control & 0x02) { /* Receive Interrupt enable. */ > + s->irq_raise(s->irq_opaque); > + } > + } > + ret = 1; > +fail: > + qemu_mutex_unlock(&s->rx_lock); > + > + return ret; > +} > + > +static CanBusClientInfo can_sja_bus_client_info = { > + .can_receive = can_sja_can_receive, > + .receive = can_sja_receive, > + .cleanup = NULL, > + .poll = NULL > +}; > + > + > +int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus) > +{ > + s->bus_client.info = &can_sja_bus_client_info; > + > + 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, CanSJAIrqRaiseLower *irq_raise, > + CanSJAIrqRaiseLower *irq_lower, void *irq_opaque) > +{ > + qemu_mutex_init(&s->rx_lock); > + > + s->irq_raise = irq_raise; > + s->irq_lower = irq_lower; > + s->irq_opaque = irq_opaque; > + > + s->irq_lower(s->irq_opaque); > + > + can_sja_hardware_reset(s); > + > + return 0; > +} > + > +void can_sja_exit(CanSJA1000State *s) > +{ > + qemu_mutex_destroy(&s->rx_lock); > +} > + > +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() > + } > +}; > + > +/* 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, > + .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() > + } > +}; > diff --git a/hw/can/can_sja1000.h b/hw/can/can_sja1000.h > new file mode 100644 > index 0000000000..6f2cfbcb72 > --- /dev/null > +++ b/hw/can/can_sja1000.h > @@ -0,0 +1,167 @@ > +/* > + * 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. > + */ > +#ifndef HW_CAN_SJA1000_H > +#define HW_CAN_SJA1000_H > + > +#include "can/can_emu.h" > + > +#define CAN_SJA_MEM_SIZE 128 > + > +/* The max size for a message buffer, EFF and DLC=8, DS-p39 */ > +#define SJA_MSG_MAX_LEN 13 > +/* The receive buffer size. */ > +#define SJA_RCV_BUF_LEN 64 > + > +typedef void (CanSJAIrqRaiseLower)(void *opaque); > + > +typedef struct CanSJA1000State { > + /* PeliCAN state and registers sorted by address */ > + uint8_t mode; /* 0 .. Mode register, DS-p26 */ > + /* 1 .. Command register */ > + uint8_t status_pel; /* 2 .. Status register, p15 */ > + uint8_t interrupt_pel; /* 3 .. Interrupt register */ > + uint8_t interrupt_en; /* 4 .. Interrupt Enable register */ > + uint8_t rxmsg_cnt; /* 29 .. RX message counter. DS-p49 */ > + uint8_t rxbuf_start; /* 30 .. RX buffer start address, DS-p49 */ > + uint8_t clock; /* 31 .. Clock Divider register, DS-p55 */ > + > + uint8_t code_mask[8]; /* 16~23 */ > + uint8_t tx_buff[13]; /* 96~108 .. transmit buffer */ > + /* 10~19 .. transmit buffer for BasicCAN */ > + > + uint8_t rx_buff[SJA_RCV_BUF_LEN]; /* 32~95 .. 64bytes Rx FIFO */ > + uint32_t rx_ptr; /* Count by bytes. */ > + uint32_t rx_cnt; /* Count by bytes. */ > + > + /* PeliCAN state and registers sorted by address */ > + uint8_t control; /* 0 .. Control register */ > + /* 1 .. Command register */ > + uint8_t status_bas; /* 2 .. Status register */ > + uint8_t interrupt_bas; /* 3 .. Interrupt register */ > + uint8_t code; /* 4 .. Acceptance code register */ > + uint8_t mask; /* 5 .. Acceptance mask register */ > + > + qemu_can_filter filter[4]; > + > + QemuMutex rx_lock; > + CanSJAIrqRaiseLower *irq_raise; > + CanSJAIrqRaiseLower *irq_lower; > + void *irq_opaque; > + CanBusClientState bus_client; > +} CanSJA1000State; > + > +/* PeliCAN mode */ > +enum SJA1000_PeliCAN_regs { > + SJA_MOD = 0x00, > +/* Command register */ why not keep in one line? SJA_MOD = 0x00, /* Command */ SJA_CMR = 0x01, /* Status */ > + SJA_CMR = 0x01, > +/* Status register */ > + SJA_SR = 0x02, > +/* Interrupt register */ > + SJA_IR = 0x03, > +/* Interrupt Enable */ > + SJA_IER = 0x04, > +/* Bus Timing register 0 */ > + SJA_BTR0 = 0x06, > +/* Bus Timing register 1 */ > + SJA_BTR1 = 0x07, > +/* Output Control register */ > + SJA_OCR = 0x08, > +/* Arbitration Lost Capture */ > + SJA_ALC = 0x0b, > +/* Error Code Capture */ > + SJA_ECC = 0x0c, > +/* Error Warning Limit */ > + SJA_EWLR = 0x0d, > +/* RX Error Counter */ > + SJA_RXERR = 0x0e, > +/* TX Error Counter */ > + SJA_TXERR0 = 0x0e, > + SJA_TXERR1 = 0x0f, > +/* Rx Message Counter (number of msgs. in RX FIFO */ > + SJA_RMC = 0x1d, > +/* Rx Buffer Start Addr. (address of current MSG) */ > + SJA_RBSA = 0x1e, > +/* Transmit Buffer (write) Receive Buffer (read) Frame Information */ > + SJA_FRM = 0x10, > +/* > + * ID bytes (11 bits in 0 and 1 for standard message or > + * 16 bits in 0,1 and 13 bits in 2,3 for extended message) > + */ > + SJA_ID0 = 0x11, SJA_ID1 = 0x12, > +/* ID cont. for extended frames */ > + SJA_ID2 = 0x13, SJA_ID3 = 0x14, > +/* Data start standard frame */ > + SJA_DATS = 0x13, > +/* Data start extended frame */ > + SJA_DATE = 0x15, > +/* Acceptance Code (4 bytes) in RESET mode */ > + SJA_ACR0 = 0x10, > +/* Acceptance Mask (4 bytes) in RESET mode */ > + SJA_AMR0 = 0x14, > +/* 4 bytes */ > + SJA_PeliCAN_AC_LEN = 4, > +/* Clock Divider */ > + SJA_CDR = 0x1f > +}; > + > + > +/* PeliCAN mode */ > +enum SJA1000_BasicCAN_regs { > + SJA_BCAN_CTR = 0x00, > +/* Command register */ > + SJA_BCAN_CMR = 0x01, > +/* Status register */ > + SJA_BCAN_SR = 0x02, > +/* Interrupt register */ > + SJA_BCAN_IR = 0x03 > +}; > + > +void can_sja_hardware_reset(CanSJA1000State *s); > + > +void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val, > + unsigned size); > + > +uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size); > + > +int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus); > + > +void can_sja_disconnect(CanSJA1000State *s); > + > +int can_sja_init(CanSJA1000State *s, CanSJAIrqRaiseLower *irq_raise, > + CanSJAIrqRaiseLower *irq_lower, void *irq_opaque); > + > +void can_sja_exit(CanSJA1000State *s); > + > +int can_sja_can_receive(CanBusClientState *client); > + > +ssize_t can_sja_receive(CanBusClientState *client, > + const qemu_can_frame *frames, size_t frames_cnt); > + > +extern const VMStateDescription vmstate_can_sja; > + > +#endif >
diff --git a/default-configs/pci.mak b/default-configs/pci.mak index bbe11887a1..979b649fe5 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -32,6 +32,7 @@ CONFIG_SERIAL=y CONFIG_SERIAL_ISA=y CONFIG_SERIAL_PCI=y CONFIG_CAN_CORE=y +CONFIG_CAN_SJA1000=y CONFIG_IPACK=y CONFIG_WDT_IB6300ESB=y CONFIG_PCI_TESTDEV=y diff --git a/hw/can/Makefile.objs b/hw/can/Makefile.objs index f999085f7a..3c4bf3bfc1 100644 --- a/hw/can/Makefile.objs +++ b/hw/can/Makefile.objs @@ -7,4 +7,5 @@ common-obj-y += can_socketcan.o else common-obj-y += can_host_stub.o endif +common-obj-$(CONFIG_CAN_SJA1000) += can_sja1000.o endif diff --git a/hw/can/can_sja1000.c b/hw/can/can_sja1000.c new file mode 100644 index 0000000000..7f7a6ea244 --- /dev/null +++ b/hw/can/can_sja1000.c @@ -0,0 +1,1013 @@ +/* + * 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/hw.h" +#include "can/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; + + s->irq_lower(s->irq_opaque); +} + +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] & 4)) { + 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; +} + +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; + if (s->interrupt_en & 0x02) { + s->irq_raise(s->irq_opaque); + } + } + 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); + } + if ((s->interrupt_en & 0x01) && (s->interrupt_pel == 0)) { + /* no other interrupts. */ + s->irq_lower(s->irq_opaque); + } + } + if (0x08 & val) { /* Clear data overrun */ + s->status_pel &= ~(1 << 1); + s->interrupt_pel &= ~(1 << 3); + if ((s->interrupt_en & 0x80) && (s->interrupt_pel == 0)) { + /* no other interrupts. */ + s->irq_lower(s->irq_opaque); + } + } + 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: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 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; + if (s->control & 0x04) { + s->irq_raise(s->irq_opaque); + } + } + if (0x04 & val) { /* Release Receive Buffer */ + if (s->rxmsg_cnt <= 0) { + break; + } + + qemu_mutex_lock(&s->rx_lock); + 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--; + qemu_mutex_unlock(&s->rx_lock); + + if (s->rxmsg_cnt == 0) { + s->status_bas &= ~(1 << 0); + s->interrupt_bas &= ~(1 << 0); + } + if ((s->control & 0x02) && (s->interrupt_bas == 0)) { + /* no other interrupts. */ + s->irq_lower(s->irq_opaque); + } + } + if (0x08 & val) { /* Clear data overrun */ + s->status_bas &= ~(1 << 1); + s->interrupt_bas &= ~(1 << 3); + if ((s->control & 0x10) && (s->interrupt_bas == 0)) { + /* no other interrupts. */ + s->irq_lower(s->irq_opaque); + } + } + 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: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 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. */ + break; + } + s->irq_lower(s->irq_opaque); + 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: /* Reserved */ + case 11: + case 12: + case 13: + case 14: + case 15: + temp = 0x00; + break; + + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 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. */ + break; + } + s->irq_lower(s->irq_opaque); + break; + case 4: + temp = s->code; + break; + case 5: + temp = s->mask; + break; + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 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); + } + + qemu_mutex_lock(&s->rx_lock); /* Just do it quickly :) */ + 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"); + } + goto fail; + } + + ret = frame2buff_pel(frame, rcv); + if (ret < 0) { + s->status_pel &= ~(1 << 4); + if (DEBUG_FILTER) { + qemu_log("[cansja]: message store failed\n"); + } + goto fail; /* 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); + if (s->interrupt_en & (1 << 3)) { /* Overrun interrupt enable */ + s->irq_raise(s->irq_opaque); + } + s->status_pel &= ~(1 << 4); + if (DEBUG_FILTER) { + qemu_log("[cansja]: receive FIFO overrun\n"); + } + goto fail; + } + 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); + if (s->interrupt_en & 0x01) { /* Receive Interrupt enable. */ + s->irq_raise(s->irq_opaque); + } + } 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"); + } + goto fail; /* 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); + if (s->control & (1 << 4)) { /* Overrun interrupt enable */ + s->irq_raise(s->irq_opaque); + } + if (DEBUG_FILTER) { + qemu_log("[cansja]: receive FIFO overrun\n"); + } + goto fail; + } + 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 |= 0x01; + if (s->control & 0x02) { /* Receive Interrupt enable. */ + s->irq_raise(s->irq_opaque); + } + } + ret = 1; +fail: + qemu_mutex_unlock(&s->rx_lock); + + return ret; +} + +static CanBusClientInfo can_sja_bus_client_info = { + .can_receive = can_sja_can_receive, + .receive = can_sja_receive, + .cleanup = NULL, + .poll = NULL +}; + + +int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus) +{ + s->bus_client.info = &can_sja_bus_client_info; + + 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, CanSJAIrqRaiseLower *irq_raise, + CanSJAIrqRaiseLower *irq_lower, void *irq_opaque) +{ + qemu_mutex_init(&s->rx_lock); + + s->irq_raise = irq_raise; + s->irq_lower = irq_lower; + s->irq_opaque = irq_opaque; + + s->irq_lower(s->irq_opaque); + + can_sja_hardware_reset(s); + + return 0; +} + +void can_sja_exit(CanSJA1000State *s) +{ + qemu_mutex_destroy(&s->rx_lock); +} + +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() + } +}; + +/* 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, + .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() + } +}; diff --git a/hw/can/can_sja1000.h b/hw/can/can_sja1000.h new file mode 100644 index 0000000000..6f2cfbcb72 --- /dev/null +++ b/hw/can/can_sja1000.h @@ -0,0 +1,167 @@ +/* + * 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. + */ +#ifndef HW_CAN_SJA1000_H +#define HW_CAN_SJA1000_H + +#include "can/can_emu.h" + +#define CAN_SJA_MEM_SIZE 128 + +/* The max size for a message buffer, EFF and DLC=8, DS-p39 */ +#define SJA_MSG_MAX_LEN 13 +/* The receive buffer size. */ +#define SJA_RCV_BUF_LEN 64 + +typedef void (CanSJAIrqRaiseLower)(void *opaque); + +typedef struct CanSJA1000State { + /* PeliCAN state and registers sorted by address */ + uint8_t mode; /* 0 .. Mode register, DS-p26 */ + /* 1 .. Command register */ + uint8_t status_pel; /* 2 .. Status register, p15 */ + uint8_t interrupt_pel; /* 3 .. Interrupt register */ + uint8_t interrupt_en; /* 4 .. Interrupt Enable register */ + uint8_t rxmsg_cnt; /* 29 .. RX message counter. DS-p49 */ + uint8_t rxbuf_start; /* 30 .. RX buffer start address, DS-p49 */ + uint8_t clock; /* 31 .. Clock Divider register, DS-p55 */ + + uint8_t code_mask[8]; /* 16~23 */ + uint8_t tx_buff[13]; /* 96~108 .. transmit buffer */ + /* 10~19 .. transmit buffer for BasicCAN */ + + uint8_t rx_buff[SJA_RCV_BUF_LEN]; /* 32~95 .. 64bytes Rx FIFO */ + uint32_t rx_ptr; /* Count by bytes. */ + uint32_t rx_cnt; /* Count by bytes. */ + + /* PeliCAN state and registers sorted by address */ + uint8_t control; /* 0 .. Control register */ + /* 1 .. Command register */ + uint8_t status_bas; /* 2 .. Status register */ + uint8_t interrupt_bas; /* 3 .. Interrupt register */ + uint8_t code; /* 4 .. Acceptance code register */ + uint8_t mask; /* 5 .. Acceptance mask register */ + + qemu_can_filter filter[4]; + + QemuMutex rx_lock; + CanSJAIrqRaiseLower *irq_raise; + CanSJAIrqRaiseLower *irq_lower; + void *irq_opaque; + CanBusClientState bus_client; +} CanSJA1000State; + +/* PeliCAN mode */ +enum SJA1000_PeliCAN_regs { + SJA_MOD = 0x00, +/* Command register */ + SJA_CMR = 0x01, +/* Status register */ + SJA_SR = 0x02, +/* Interrupt register */ + SJA_IR = 0x03, +/* Interrupt Enable */ + SJA_IER = 0x04, +/* Bus Timing register 0 */ + SJA_BTR0 = 0x06, +/* Bus Timing register 1 */ + SJA_BTR1 = 0x07, +/* Output Control register */ + SJA_OCR = 0x08, +/* Arbitration Lost Capture */ + SJA_ALC = 0x0b, +/* Error Code Capture */ + SJA_ECC = 0x0c, +/* Error Warning Limit */ + SJA_EWLR = 0x0d, +/* RX Error Counter */ + SJA_RXERR = 0x0e, +/* TX Error Counter */ + SJA_TXERR0 = 0x0e, + SJA_TXERR1 = 0x0f, +/* Rx Message Counter (number of msgs. in RX FIFO */ + SJA_RMC = 0x1d, +/* Rx Buffer Start Addr. (address of current MSG) */ + SJA_RBSA = 0x1e, +/* Transmit Buffer (write) Receive Buffer (read) Frame Information */ + SJA_FRM = 0x10, +/* + * ID bytes (11 bits in 0 and 1 for standard message or + * 16 bits in 0,1 and 13 bits in 2,3 for extended message) + */ + SJA_ID0 = 0x11, SJA_ID1 = 0x12, +/* ID cont. for extended frames */ + SJA_ID2 = 0x13, SJA_ID3 = 0x14, +/* Data start standard frame */ + SJA_DATS = 0x13, +/* Data start extended frame */ + SJA_DATE = 0x15, +/* Acceptance Code (4 bytes) in RESET mode */ + SJA_ACR0 = 0x10, +/* Acceptance Mask (4 bytes) in RESET mode */ + SJA_AMR0 = 0x14, +/* 4 bytes */ + SJA_PeliCAN_AC_LEN = 4, +/* Clock Divider */ + SJA_CDR = 0x1f +}; + + +/* PeliCAN mode */ +enum SJA1000_BasicCAN_regs { + SJA_BCAN_CTR = 0x00, +/* Command register */ + SJA_BCAN_CMR = 0x01, +/* Status register */ + SJA_BCAN_SR = 0x02, +/* Interrupt register */ + SJA_BCAN_IR = 0x03 +}; + +void can_sja_hardware_reset(CanSJA1000State *s); + +void can_sja_mem_write(CanSJA1000State *s, hwaddr addr, uint64_t val, + unsigned size); + +uint64_t can_sja_mem_read(CanSJA1000State *s, hwaddr addr, unsigned size); + +int can_sja_connect_to_bus(CanSJA1000State *s, CanBusState *bus); + +void can_sja_disconnect(CanSJA1000State *s); + +int can_sja_init(CanSJA1000State *s, CanSJAIrqRaiseLower *irq_raise, + CanSJAIrqRaiseLower *irq_lower, void *irq_opaque); + +void can_sja_exit(CanSJA1000State *s); + +int can_sja_can_receive(CanBusClientState *client); + +ssize_t can_sja_receive(CanBusClientState *client, + const qemu_can_frame *frames, size_t frames_cnt); + +extern const VMStateDescription vmstate_can_sja; + +#endif