/** * When a frame is received, cast it to a csp_packet * and send it directly to the CSP new packet function. * Context: ISR only * @param frame */ void csp_i2c_rx(i2c_frame_t * frame, void * pxTaskWoken) { static csp_packet_t * packet; /* Validate input */ if (frame == NULL) return; if ((frame->len < 4) || (frame->len > I2C_MTU)) { csp_if_i2c.frame++; csp_buffer_free_isr(frame); return; } /* Strip the CSP header off the length field before converting to CSP packet */ frame->len -= sizeof(csp_id_t); /* Convert the packet from network to host order */ packet = (csp_packet_t *) frame; packet->id.ext = csp_ntoh32(packet->id.ext); /* Receive the packet in CSP */ csp_new_packet(packet, &csp_if_i2c, pxTaskWoken); }
void csp_new_packet(csp_packet_t * packet, csp_iface_t * interface, CSP_BASE_TYPE * pxTaskWoken) { int result, fifo; if (packet == NULL) { csp_log_warn("csp_new packet called with NULL packet\r\n"); return; } else if (interface == NULL) { csp_log_warn("csp_new packet called with NULL interface\r\n"); if (pxTaskWoken == NULL) csp_buffer_free(packet); else csp_buffer_free_isr(packet); return; } csp_route_queue_t queue_element; queue_element.interface = interface; queue_element.packet = packet; fifo = csp_route_get_fifo(packet->id.pri); result = csp_route_enqueue(router_input_fifo[fifo], &queue_element, 0, pxTaskWoken); if (result != CSP_ERR_NONE) { csp_log_warn("ERROR: Routing input FIFO is FULL. Dropping packet.\r\n"); interface->drop++; if (pxTaskWoken == NULL) csp_buffer_free(packet); else csp_buffer_free_isr(packet); } else { interface->rx++; interface->rxbytes += packet->length; } }
/** pbuf_free * Free buffer element and associated CSP packet buffer element. * @param buf Buffer element to free * @return 0 on success, -1 on error. */ static int pbuf_free(pbuf_element_t *buf, CSP_BASE_TYPE *task_woken) { int32_t refcount; /* Lock packet buffer */ if (task_woken == NULL) CSP_ENTER_CRITICAL(pbuf_sem); /* Free CSP packet */ if (buf->packet != NULL) { /* first check to see if the buffer was already free. This could happen if the top level timeout occurred already. If so, no need to free it again */ refcount = csp_buffer_get_refcount(buf->packet); if (refcount > 0) { if (task_woken == NULL) { csp_buffer_free(buf->packet); } else { csp_buffer_free_isr(buf->packet); } } buf->packet = NULL; } /* Mark buffer element free */ buf->state = BUF_FREE; buf->rx_count = 0; buf->tx_count = 0; buf->cfpid = 0; buf->last_used = 0; buf->remain = 0; /* Unlock packet buffer */ if (task_woken == NULL) CSP_EXIT_CRITICAL(pbuf_sem); return CSP_ERR_NONE; }
/** pbuf_free_locked * Free buffer element and associated CSP packet buffer element with pbuf list locked. * @param buf Buffer element to free * @param free_packet true if the associated packet should be freed as well * @return 0 on success, -1 on error. */ static int pbuf_free_locked(pbuf_element_t *buf, CSP_BASE_TYPE *task_woken, bool free_packet) { /* Free CSP packet */ if (buf->packet != NULL && free_packet) { if (task_woken == NULL) { csp_buffer_free(buf->packet); } else { csp_buffer_free_isr(buf->packet); } } /* Mark buffer element free */ buf->packet = NULL; buf->state = BUF_FREE; buf->rx_count = 0; buf->tx_count = 0; buf->cfpid = 0; buf->last_used = 0; buf->remain = 0; return CSP_ERR_NONE; }
/** * Interrupt service routine * Basically handles the entire protocol */ void __attribute__((noinline)) pca9665_dsr(portBASE_TYPE * task_woken) { static int handle, len; static uint8_t state; static uint8_t dest; /* Loop through number of devices */ for (handle = 0; handle < pca9665_device_count; handle++) { /* Check for interrupt flag in device status register */ if (!(pca9665_read_reg(handle, I2CCON) & CON_SI)) continue; /* We have an interrupt, read the status register */ state = pca9665_read_reg(handle, I2CSTA); /* The I2C driver is one _big_ state-machine */ driver_debug(DEBUG_I2C, "I2C ISR %u %x\n\r", handle, state); switch (state) { /** * MASTER IRQ's */ /* START: is the first ISR that appears for outgoing frames */ case STA_M_REPEATED_START_SENDT: case STA_M_START_SENDT: /* Mark as busy, so start flag is not sent from task context while transmission is active */ device[handle].is_busy = 1; /* If this is the beginning of a new frame, dequeue */ if (device[handle].tx.frame == NULL && device[handle].rx.frame == NULL) { /* Try do dequeue element, if it fails, stop transmission */ xQueueReceiveFromISR(device[handle].tx.queue, &device[handle].tx.frame, task_woken); if (device[handle].tx.frame == NULL) { pca9665_try_tx_from_isr(handle, task_woken); break; } /* If TX len > 0, go for master transmit */ if (device[handle].tx.frame->len) { device[handle].mode = DEVICE_MODE_M_T; device[handle].tx.next_byte = 0; /* If TX len == 0 and RX len > 0, go for master receive */ } else if (device[handle].tx.frame->len_rx) { device[handle].mode = DEVICE_MODE_M_R; device[handle].rx.frame = device[handle].tx.frame; device[handle].tx.frame = NULL; device[handle].rx.frame->len = device[handle].rx.frame->len_rx; device[handle].rx.next_byte = 0; /* Well, this should not happen */ } else { csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; pca9665_try_tx_from_isr(handle, task_woken); break; } } /* If mode is master receiver then set the read-bit in the address field */ if (device[handle].mode == DEVICE_MODE_M_R) { dest = (device[handle].rx.frame->dest << 1) | 0x01; device[handle].rx.next_byte = 0; /* Do first part of frame here */ if (device[handle].rx.frame->len > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); } else { pca9665_write_reg(handle, I2CCOUNT, device[handle].rx.frame->len | 0x80); } pca9665_write_data(handle, &dest, 1); } else { dest = device[handle].tx.frame->dest << 1; device[handle].tx.next_byte = 0; /* Do first part of frame here */ if (device[handle].tx.frame->len + 1 > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); pca9665_write_data(handle, &dest, 1); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], PCA9665_MAX_BUF - 1); device[handle].tx.next_byte += PCA9665_MAX_BUF - 1; } else { pca9665_write_reg(handle, I2CCOUNT, device[handle].tx.frame->len + 1); pca9665_write_data(handle, &dest, 1); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], device[handle].tx.frame->len); device[handle].tx.next_byte += device[handle].tx.frame->len; } } /* Let the hardware continue */ pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* WRITE ACK: A node is ready to be written to */ case STA_M_SLAW_SENDT_ACKED: case STA_M_DATA_SENDT_ACKED: /* Safety first */ if (device[handle].tx.frame == NULL) goto isr_error; /* Calculate remaining length */ len = device[handle].tx.frame->len - device[handle].tx.next_byte; /* Transmit next chunk */ if (len > 0) { if (len > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], PCA9665_MAX_BUF); device[handle].tx.next_byte += PCA9665_MAX_BUF; } else { pca9665_write_reg(handle, I2CCOUNT, len); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], len); device[handle].tx.next_byte += len; } pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* Or, Change from master transmit, to master read if wanted */ } else if (device[handle].tx.frame->len_rx) { device[handle].mode = DEVICE_MODE_M_R; device[handle].rx.frame = device[handle].tx.frame; device[handle].tx.frame = NULL; device[handle].rx.frame->len = device[handle].rx.frame->len_rx; /* We need to send a repeated start now! */ pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA | CON_STA); break; /* Or, We are done */ } else { csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; pca9665_try_tx_from_isr(handle, task_woken); } break; /* WRITE ERROR: A write has failed */ case STA_M_SLAW_SENDT_NACKED: case STA_M_DATA_SENDT_LAST_NACKED: if (device[handle].tx.frame != NULL) { driver_debug(DEBUG_I2C, "I2C SLA+W NACK: 0x%02"PRIx8"\n\r", device[handle].tx.frame->dest); csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; } pca9665_try_tx_from_isr(handle, task_woken); break; /* ARBITRATION LOST: Start condition failed */ case STA_M_ARBITRATION_LOST: /* Restart transmission by resetting next_byte and preserving tx_frame pointer */ device[handle].tx.next_byte = 0; pca9665_try_tx_from_isr(handle, task_woken); break; /* READ ACK: A node is ready to be read from */ case STA_M_SLAR_SENT_ACKED: case STA_M_DATA_RECEIVED_ACKED: case STA_M_DATA_RECEIVED_NACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; if (device[handle].rx.queue == NULL) goto isr_error; pca9665_read_data_to_buffer(handle); int remaining = device[handle].rx.frame->len - device[handle].rx.next_byte; driver_debug(DEBUG_I2C, "RX: Remaining %u\r\n", remaining); /* If no more to receive */ if (remaining == 0) { if (xQueueSendToBackFromISR(device[handle].rx.queue, &device[handle].rx.frame, task_woken) == pdFALSE) { driver_debug(DEBUG_I2C, "I2C rx queue full - freeing\n\r"); csp_buffer_free_isr(device[handle].rx.frame); } device[handle].rx.frame = NULL; pca9665_try_tx_from_isr(handle, task_woken); break; } /* If more than a full PCA9665 buffer remains */ if (remaining > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); /* Otherwise, this is the last bit, set NACK on final slave read */ } else { pca9665_write_reg(handle, I2CCOUNT, remaining | 0x80); } pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* READ ERROR: A read has failed */ case STA_M_SLAR_SENT_NACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; driver_debug(DEBUG_I2C, "I2C SLA+R nacked\n\r"); csp_buffer_free_isr(device[handle].rx.frame); device[handle].rx.frame = NULL; /* Start up again */ pca9665_try_tx_from_isr(handle, task_woken); break; /** * SLAVE RECEIVER BUFFERED MODE */ /* START: Lost the arbitration and is addressed as a slave receiver */ case STA_S_ARB_LOST_SLAW_RECEIVED: case STA_S_ARB_LOST_GC_RECEIVED: /* Preserve TX frame active, so the START flag will be re-set when the * reception is completed */ /* Deliberate Fallthrough */ /* START: Addressed as a slave receiver */ case STA_S_SLAW_RECEIVED_ACKED: case STA_S_GC_RECEIVED: /* Check if RX frame was started */ if (device[handle].rx.frame != NULL) goto isr_error; /* Allocate new frame */ device[handle].rx.frame = csp_buffer_get_isr(I2C_MTU); if (device[handle].rx.frame == NULL) goto isr_error; device[handle].is_busy = 1; device[handle].rx.next_byte = 0; device[handle].rx.frame->len = 0; device[handle].rx.frame->dest = device[handle].slave_addr; pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* READ: Data received. */ case STA_S_DATA_RECEIVED_SLA_ACKED: case STA_S_DATA_RECEIVED_GC_ACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; /* Receive data, if any */ pca9665_read_data_to_buffer(handle); /* Limit incoming bytes */ pca9665_write_reg(handle, I2CCOUNT, (device[handle].rx.next_byte + PCA9665_MAX_BUF > I2C_MTU) ? (I2C_MTU - device[handle].rx.next_byte) | 0x80 : PCA9665_MAX_BUF); pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* STOP or NACK: No more data to receive */ case STA_S_STOP_REP_RECEIVED: case STA_S_DATA_RECEIVED_SLA_NACKED: case STA_S_DATA_RECEIVED_GC_NACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; /* Receive data, if any */ pca9665_read_data_to_buffer(handle); /* Queue up frame */ device[handle].rx.frame->len = device[handle].rx.next_byte; if (device[handle].rx.queue != NULL) { if (xQueueSendToBackFromISR(device[handle].rx.queue, &device[handle].rx.frame, task_woken) == pdFALSE) { driver_debug(DEBUG_I2C, "I2C RX queue full\n\r"); csp_buffer_free_isr(device[handle].rx.frame); } } else if (device[handle].callback != NULL) { device[handle].callback(device[handle].rx.frame, task_woken); } else { csp_buffer_free_isr(device[handle].rx.frame); } /* The frame has been freed now */ device[handle].rx.frame = NULL; /* Set back to master mode */ pca9665_try_tx_from_isr(handle, task_woken); break; /** * Other IRQ's, typically indicates a hardware or protcol error * The IDLE status is considered an error if asserted at the same time as the Serial Interrupt flag */ case STA_IDLE: default: isr_error: /* Soft reset the device */ driver_debug(DEBUG_I2C, "I2C ERR 0x%02X\n\r", state); pca9665_init_registers(handle); /* Clean up RX */ if (device[handle].rx.frame != NULL) { csp_buffer_free_isr(device[handle].rx.frame); device[handle].rx.frame = NULL; } /* Clean up TX */ if (device[handle].tx.frame != NULL) { csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; } /* Start up again */ pca9665_try_tx_from_isr(handle, task_woken); break; } } /**< END Switch/Case */ }