Ejemplo n.º 1
0
/**
 * 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;
}
Ejemplo n.º 2
0
Archivo: pca9665.c Proyecto: lirihe/arm
/**
 * 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 */

}