mirror of https://github.com/commaai/panda.git
123 lines
4.1 KiB
C
123 lines
4.1 KiB
C
/*
|
|
CAN transactions to and from the host come in the form of
|
|
a certain number of CANPacket_t. The transaction is split
|
|
into multiple transfers or chunks.
|
|
|
|
* comms_can_read outputs this buffer in chunks of a specified length.
|
|
chunks are always the given length, except the last one.
|
|
* comms_can_write reads in this buffer in chunks.
|
|
* both functions maintain an overflow buffer for a partial CANPacket_t that
|
|
spans multiple transfers/chunks.
|
|
* the overflow buffers are reset by a dedicated control transfer handler,
|
|
which is sent by the host on each start of a connection.
|
|
*/
|
|
|
|
typedef struct {
|
|
uint32_t ptr;
|
|
uint32_t tail_size;
|
|
uint8_t data[72];
|
|
} asm_buffer;
|
|
|
|
static asm_buffer can_read_buffer = {.ptr = 0U, .tail_size = 0U};
|
|
|
|
int comms_can_read(uint8_t *data, uint32_t max_len) {
|
|
uint32_t pos = 0U;
|
|
|
|
// Send tail of previous message if it is in buffer
|
|
if (can_read_buffer.ptr > 0U) {
|
|
uint32_t overflow_len = MIN(max_len - pos, can_read_buffer.ptr);
|
|
(void)memcpy(&data[pos], can_read_buffer.data, overflow_len);
|
|
pos += overflow_len;
|
|
(void)memcpy(can_read_buffer.data, &can_read_buffer.data[overflow_len], can_read_buffer.ptr - overflow_len);
|
|
can_read_buffer.ptr -= overflow_len;
|
|
}
|
|
|
|
if (can_read_buffer.ptr == 0U) {
|
|
// Fill rest of buffer with new data
|
|
CANPacket_t can_packet;
|
|
while ((pos < max_len) && can_pop(&can_rx_q, &can_packet)) {
|
|
uint32_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[can_packet.data_len_code];
|
|
if ((pos + pckt_len) <= max_len) {
|
|
(void)memcpy(&data[pos], (uint8_t*)&can_packet, pckt_len);
|
|
pos += pckt_len;
|
|
} else {
|
|
(void)memcpy(&data[pos], (uint8_t*)&can_packet, max_len - pos);
|
|
can_read_buffer.ptr += pckt_len - (max_len - pos);
|
|
// cppcheck-suppress objectIndex
|
|
(void)memcpy(can_read_buffer.data, &((uint8_t*)&can_packet)[(max_len - pos)], can_read_buffer.ptr);
|
|
pos = max_len;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
static asm_buffer can_write_buffer = {.ptr = 0U, .tail_size = 0U};
|
|
|
|
// send on CAN
|
|
void comms_can_write(const uint8_t *data, uint32_t len) {
|
|
uint32_t pos = 0U;
|
|
|
|
// Assembling can message with data from buffer
|
|
if (can_write_buffer.ptr != 0U) {
|
|
if (can_write_buffer.tail_size <= (len - pos)) {
|
|
// we have enough data to complete the buffer
|
|
CANPacket_t to_push = {0};
|
|
(void)memcpy(&can_write_buffer.data[can_write_buffer.ptr], &data[pos], can_write_buffer.tail_size);
|
|
can_write_buffer.ptr += can_write_buffer.tail_size;
|
|
pos += can_write_buffer.tail_size;
|
|
|
|
// send out
|
|
(void)memcpy((uint8_t*)&to_push, can_write_buffer.data, can_write_buffer.ptr);
|
|
can_send(&to_push, to_push.bus, false);
|
|
|
|
// reset overflow buffer
|
|
can_write_buffer.ptr = 0U;
|
|
can_write_buffer.tail_size = 0U;
|
|
} else {
|
|
// maybe next time
|
|
uint32_t data_size = len - pos;
|
|
(void) memcpy(&can_write_buffer.data[can_write_buffer.ptr], &data[pos], data_size);
|
|
can_write_buffer.tail_size -= data_size;
|
|
can_write_buffer.ptr += data_size;
|
|
pos += data_size;
|
|
}
|
|
}
|
|
|
|
// rest of the message
|
|
while (pos < len) {
|
|
uint32_t pckt_len = CANPACKET_HEAD_SIZE + dlc_to_len[(data[pos] >> 4U)];
|
|
if ((pos + pckt_len) <= len) {
|
|
CANPacket_t to_push = {0};
|
|
(void)memcpy((uint8_t*)&to_push, &data[pos], pckt_len);
|
|
can_send(&to_push, to_push.bus, false);
|
|
pos += pckt_len;
|
|
} else {
|
|
(void)memcpy(can_write_buffer.data, &data[pos], len - pos);
|
|
can_write_buffer.ptr = len - pos;
|
|
can_write_buffer.tail_size = pckt_len - can_write_buffer.ptr;
|
|
pos += can_write_buffer.ptr;
|
|
}
|
|
}
|
|
|
|
refresh_can_tx_slots_available();
|
|
}
|
|
|
|
void comms_can_reset(void) {
|
|
can_write_buffer.ptr = 0U;
|
|
can_write_buffer.tail_size = 0U;
|
|
can_read_buffer.ptr = 0U;
|
|
can_read_buffer.tail_size = 0U;
|
|
}
|
|
|
|
// TODO: make this more general!
|
|
void refresh_can_tx_slots_available(void) {
|
|
if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_USB_BULK_TRANSFER)) {
|
|
can_tx_comms_resume_usb();
|
|
}
|
|
if (can_tx_check_min_slots_free(MAX_CAN_MSGS_PER_SPI_BULK_TRANSFER)) {
|
|
can_tx_comms_resume_spi();
|
|
}
|
|
}
|