/** * Searched a statically assigned array for a free entry * Starts with the last given element + 1 for optimisation * This call is safe from both ISR and task context * @return poiter to a free csp_packet_t or NULL if out of memory */ void * csp_buffer_get(size_t buf_size) { void * buffer; CSP_ENTER_CRITICAL(csp_critical_lock); buffer = csp_buffer_get_isr(buf_size); CSP_EXIT_CRITICAL(csp_critical_lock); return buffer; }
/** * 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 */ }