/**********************************************************

Prototype	int spi_data_inqueue (struct spi_data_queue_info * queue_info,
					void * data, unsigned int length )

Type		function

Description	inqueue data to spi rx or tx buffer

Param input	queue_info : queue for inqueue

			data : address of data

			lenth : length of data

Return value	0 : fail

			1 : success

***********************************************************/
int spi_data_inqueue(struct spi_data_queue_info *queue_info,
		void *data, unsigned int length)
{
	char *pdata;
	struct spi_data_queue *queue;
	pdata = data;
	queue = queue_info->header;

	if (queue->tail < queue->head) {
		if ((queue->head - queue->tail) < length) {
			pr_err("%s%s[%u],%s[%u],%s[%u],%s[%u]\n",
				"[SPI] ERROR : spi_data_inqueue : ",
				"queue is overflow head", queue->head,
				"tail", queue->tail, "length", length,
				"buf_size", queue_info->buf_size);
			return -1;
		}
	} else {
		if ((queue_info->buf_size - (queue->tail - queue->head))
			< length) {
			pr_err("%s%s[%u],%s[%u],%s[%u],%s[%u]\n",
			"[SPI] ERROR : spi_data_inqueue : queue is overflow ",
			"head", queue->head, "tail", queue->tail,
			"request length", length,
			"buf_size", queue_info->buf_size);
			return -1;
		}
	}

	if (&spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_RX] == queue_info
	  || &spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_RX] == queue_info
	  || &spi_queue_info[SPI_DATA_QUEUE_TYPE_RFS_RX] == queue_info) {
		pdata += (SPI_DATA_MUX_SIZE+SPI_DATA_LENGTH_SIZE);
		length -= (SPI_DATA_MUX_SIZE+SPI_DATA_LENGTH_SIZE);
	}

	if (queue_info->buf_size < (queue->tail + length)) {
		/* maximum  < tail + length */
		unsigned int pre_data_len = 0;
		pre_data_len = queue_info->buf_size - queue->tail;
		spi_os_memcpy(queue_info->buffer + queue->tail,
			pdata, pre_data_len);
		spi_os_memcpy(queue_info->buffer, pdata + pre_data_len,
			length - pre_data_len);
		queue->tail = ((queue->tail+length)%queue_info->buf_size);
	} else if (queue_info->buf_size == (queue->tail + length)) {
		/* maximum  == tail + length */
		spi_os_memcpy((queue_info->buffer + queue->tail),
			(char *)pdata, length);
		queue->tail = 0;
	} else {
		/* maximum > tail + length */
		spi_os_memcpy((queue_info->buffer + queue->tail),
			(char *)pdata, length);
		queue->tail = queue->tail + length;
	}

	return 1;
}
static int _spi_data_verify(void *buf,
	unsigned int mux)
{
	unsigned int length;

	unsigned int max_data_len = SPI_DATA_MAX_SIZE_PER_PACKET;
	char bof = 0x00;
	char eof = 0x00;

	spi_os_memcpy(&length, (char *)buf+SPI_DATA_LENGTH_OFFSET,
		SPI_DATA_LENGTH_SIZE);
	spi_os_memcpy(&bof, (char *)buf+SPI_DATA_BOF_OFFSET, SPI_DATA_BOF_SIZE);

	if (mux & SPI_DATA_MUX_MORE_H || mux & SPI_DATA_MUX_MORE_M)
		max_data_len += SPI_DATA_EOF_SIZE;

	if (bof == SPI_DATA_BOF) {
		if (length <= max_data_len) {
			if (mux & SPI_DATA_MUX_MORE_H
				|| mux & SPI_DATA_MUX_MORE_M)
				return 1;
			else {
				spi_os_memcpy(&eof,
					(char *)buf+SPI_DATA_EOF_OFFSET(length),
					SPI_DATA_EOF_SIZE);

				if (eof != SPI_DATA_EOF) {
					pr_err("%s %s\n",
						"[SPI] ERROR : _spi_data_verify:",
						"invalid");

					return 0;
				}
			}
		} else {
			pr_err("%s %s\n",
				"[SPI] ERROR : _spi_data_verify:",
				"invalid : length error");

			return 0;
		}
	} else {
		pr_err("%s %s\n",
			"[SPI] ERROR : _spi_data_verify:",
			"invalid : bof error");

		return 0;
	}

	return 1;
}
예제 #3
0
/**********************************************************
Prototype	unsigned int _pack_spi_data (SPI_DATA_TYPE_T type,void * buf, void * data, unsigned int length)
Type			static function
Description	pack data for spi
Param input	type		: type of data type
			buf			: address of buffer to be saved
			data		: address of data to pack
			length	: length of input data
Return value	length of packed data
***********************************************************/
static unsigned int _pack_spi_data(enum SPI_DATA_TYPE_T type, void *buf,
		void *data, unsigned int length)
{
	char *spi_packet	 = NULL;
	unsigned int out_length = 0;

	spi_packet = (char *) buf;

	spi_os_memset((char *)spi_packet, 0x00, (unsigned int)length);
	spi_os_memset((char *)spi_packet, (unsigned char)SPI_DATA_BOF,
		SPI_DATA_BOF_SIZE);
	spi_os_memcpy((char *)spi_packet + SPI_DATA_BOF_SIZE, data, length);
	spi_os_memset((char *)spi_packet + SPI_DATA_BOF_SIZE + length,
		(unsigned char)SPI_DATA_EOF, SPI_DATA_EOF_SIZE);

	out_length = SPI_DATA_BOF_SIZE + length + SPI_DATA_EOF_SIZE;

	return out_length;
}
int spi_data_parsing_rx_packet(void *buf, unsigned int length)
{
	struct spi_data_packet_header *spi_packet_header;
	char *spi_packet;
	unsigned int spi_packet_length;
	unsigned int spi_packet_cur_pos = SPI_DATA_PACKET_HEADER_SIZE;

	unsigned int spi_data_mux;
	unsigned int spi_data_length;
	char *spi_cur_data;

	struct spi_data_queue_info *queue_info;
	struct spi_data_div_buf *tx_div_buf;


	/* check spi packet header */
	if (*(unsigned int *)buf == 0x00000000
		|| *(unsigned int *)buf == 0xFFFFFFFF) {
		/* if spi header is invalid, */
		/* read spi header again with next 4 byte */
		buf += SPI_DATA_PACKET_HEADER_SIZE;
	}

	spi_packet = (char *) buf;

	/* read spi packet header */
	spi_packet_header = (struct spi_data_packet_header *) buf;
	spi_packet_length = SPI_DATA_PACKET_HEADER_SIZE +
		spi_packet_header->current_data_size;


	do {
		/* read spi data mux and set current queue */
		spi_os_memcpy(&spi_data_mux,
			spi_packet + spi_packet_cur_pos, SPI_DATA_MUX_SIZE);

		switch (spi_data_mux & SPI_DATA_MUX_NORMAL_MASK) {
		case SPI_DATA_MUX_IPC:
			queue_info =
			&spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_RX];
			tx_div_buf =
				&spi_div_buf[SPI_DATA_QUEUE_TYPE_IPC_RX];
			break;

		case SPI_DATA_MUX_RAW:
			queue_info =
				&spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_RX];
			tx_div_buf =
				&spi_div_buf[SPI_DATA_QUEUE_TYPE_RAW_RX];
			break;

		case SPI_DATA_MUX_RFS:
			queue_info =
				&spi_queue_info[SPI_DATA_QUEUE_TYPE_RFS_RX];
			tx_div_buf =
				&spi_div_buf[SPI_DATA_QUEUE_TYPE_RFS_RX];
			break;

		default:
			pr_err("%s len[%u], pos[%u]\n",
				"[SPI] ERROR : spi_data_parsing_rx_packet : MUX error",
				spi_packet_length, spi_packet_cur_pos);

			spi_os_trace_dump_low("mux error",
				spi_packet + spi_packet_cur_pos, 16);
			return spi_packet_cur_pos - SPI_DATA_PACKET_HEADER_SIZE;
		}

		/* read spi data length */
		spi_os_memcpy(&spi_data_length, spi_packet +
			spi_packet_cur_pos + SPI_DATA_LENGTH_OFFSET,
			SPI_DATA_LENGTH_SIZE);

		if (spi_data_mux & SPI_DATA_MUX_MORE_H
			|| spi_data_mux & SPI_DATA_MUX_MORE_M)
			spi_data_length += SPI_DATA_HEADER_SIZE_FRONT;
		else if (spi_data_mux & SPI_DATA_MUX_MORE_T)
			spi_data_length += SPI_DATA_HEADER_SIZE;
		else
			spi_data_length += SPI_DATA_HEADER_SIZE;

		/* read data and make spi data */
		spi_cur_data = spi_packet + spi_packet_cur_pos;

		/* verify spi data */
		if (_spi_data_verify(spi_cur_data, spi_data_mux) == 0) {
			spi_packet_cur_pos += spi_data_length;
			continue;
		}

		/* inqueue rx buffer */
		if (spi_data_mux & SPI_DATA_MUX_MORE_H
			|| spi_data_mux & SPI_DATA_MUX_MORE_M) {
			/* middle of divided packet. save to rx_div_buf */
			spi_os_memcpy((void *)(tx_div_buf->buffer +
				tx_div_buf->length),
				spi_cur_data, spi_data_length);
			tx_div_buf->length +=
				(spi_data_length - SPI_DATA_HEADER_SIZE_FRONT);
		} else if (spi_data_mux & SPI_DATA_MUX_MORE_T) {
			unsigned int spi_origine_len = 0;

			/* tail of divided packet. save to rx_div_buf */
			spi_os_memcpy((void *)(tx_div_buf->buffer +
				tx_div_buf->length),
				spi_cur_data, spi_data_length);
			tx_div_buf->length += spi_data_length;
			/* update spi data length at spi header */
			spi_origine_len =
				tx_div_buf->length - SPI_DATA_HEADER_SIZE;
			spi_os_memcpy(tx_div_buf->buffer +
				SPI_DATA_LENGTH_OFFSET,
				&(spi_origine_len), SPI_DATA_LENGTH_SIZE);

			/* inqueue from rx_div_buf */
			spi_data_inqueue(queue_info,
				tx_div_buf->buffer, tx_div_buf->length);

			spi_os_memset(tx_div_buf->buffer,
				0, SPI_DATA_DIVIDE_BUFFER_SIZE);
			tx_div_buf->length = 0;
		} else {
			/* normal packet */
			spi_data_inqueue(queue_info,
				spi_cur_data, spi_data_length);
		}

		/* move spi packet current posision */
		spi_packet_cur_pos += spi_data_length;
	} while ((spi_packet_length - 1) > spi_packet_cur_pos);

	return 1;
}
static int _prepare_tx_type_packet(void *buf,
	struct spi_data_queue_info *queue_info,
	struct spi_data_div_buf *spi_data_buf,
	enum SPI_DATA_TYPE_T spi_type)
{
	char *spi_packet;

	struct spi_data_packet_header	*spi_packet_header;
	unsigned int spi_packet_free_length;

	unsigned int spi_packet_count = 0;
	unsigned int cur_dequeue_length;
	unsigned int spi_data_mux;

	struct spi_data_queue *queue;

	queue = queue_info->header;

	spi_packet = (char *)buf;
	spi_packet_header = (struct spi_data_packet_header *)buf;
	spi_packet_free_length = SPI_DATA_PACKET_MAX_PACKET_BODY_SIZE -
		spi_packet_header->current_data_size;

	/* not enough space in spi packet */
	/* spi_packet_header->current_data_size > 2022(2048 - 16) */
	if (spi_packet_header->current_data_size >
		SPI_DATA_PACKET_MAX_PACKET_BODY_SIZE - SPI_DATA_MIN_SIZE) {
		if (spi_data_check_tx_queue() == 1)
			spi_packet_header->more = 1;
		return 0;
	}

	while ((queue->head != queue->tail) || (spi_data_buf->length > 0)) {
		spi_os_memset(gspi_data_prepare_packet,
			0, SPI_DEV_MAX_PACKET_SIZE);
		cur_dequeue_length = 0;

		/* dequeue SPI data */
		if (spi_data_buf->length > 0) {
			if ((*(unsigned int *)spi_data_buf->buffer)
				== SPI_DATA_FF_PADDING_HEADER) {
				/* if data has 0xFF padding header */
				/* send packet directly */
				spi_os_memcpy(spi_packet, spi_data_buf->buffer,
					spi_data_buf->length);
				spi_data_buf->length = 0;
				return 0;
			} else {
				/* read from tx div buf */
				spi_os_memcpy(gspi_data_prepare_packet,
					spi_data_buf->buffer,
					spi_data_buf->length);
				cur_dequeue_length = spi_data_buf->length;
				spi_data_buf->length = 0;
			}
		} else {
			/* read from tx queue */
			cur_dequeue_length = spi_data_dequeue(queue_info,
				gspi_data_prepare_packet);
		}

		if (cur_dequeue_length < 0)
			continue;

		if (spi_packet_free_length < cur_dequeue_length) {
			spi_os_memcpy(spi_data_buf->buffer,
				gspi_data_prepare_packet, cur_dequeue_length);
			spi_data_buf->length = cur_dequeue_length;
			spi_packet_header->more = 1;
			return 0;
		}

		/* check mux value */
		spi_os_memcpy(&spi_data_mux, gspi_data_prepare_packet,
			SPI_DATA_MUX_SIZE);
		if (spi_data_mux == 0)
			spi_os_memcpy(gspi_data_prepare_packet,
				&spi_type, SPI_DATA_MUX_SIZE);

		spi_os_memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE +
			spi_packet_header->current_data_size,
			gspi_data_prepare_packet, cur_dequeue_length);

		/* update header */
		spi_packet_header->current_data_size += cur_dequeue_length;
		spi_packet_free_length -= cur_dequeue_length;

		/* increase spi packet count */
		spi_packet_count++;

		/* check spi packet size */
		if (spi_packet_free_length < SPI_DATA_MIN_SIZE) {
			if (spi_data_check_tx_queue() == 1)
				spi_packet_header->more = 1;
			return 0;
		}

		/* check spi maximum count per packet */
		if (spi_packet_count >= SPI_DATA_MAX_COUNT_PER_PACKET) {

			pr_err("%s %s\n",
				"[SPI] ERROR : spi _prepare_tx_type_packet :",
				"spi_packet_count is full");

			return 0;
		}
	}

	return spi_packet_header->current_data_size +
		SPI_DATA_PACKET_HEADER_SIZE;
}
unsigned int spi_data_dequeue(struct spi_data_queue_info *queue_info,
		void *pdata)
{
	unsigned int length = 0, buf_length, pre_data_len;
	struct spi_data_queue *queue;

	queue = queue_info->header;

	if (queue->tail == queue->head) { /* empty */
		pr_err("[SPI] ERROR : spi_data_dequeue: queue is empty\n");
		return -1;
	}

	if (queue->head == queue_info->buf_size)
		queue->head = 0;

	/* check  length of data */
	if (&spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_TX] == queue_info
		|| &spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_RX] == queue_info) {
		/* IPC Case */
		if (queue_info->buf_size == (queue->head + SPI_DATA_BOF_SIZE)) {
			/* maximum == head + pos_len */
			spi_os_memcpy(&length, queue_info->buffer,
				SPI_DATA_IPC_INNER_LENGTH_SIZE);
		} else if (queue_info->buf_size < (queue->head +
			SPI_DATA_BOF_SIZE +
			SPI_DATA_IPC_INNER_LENGTH_SIZE)) {
			/* maximum  < head + pos_len */
			char data_header[SPI_DATA_IPC_INNER_LENGTH_SIZE] = {0,};
			pre_data_len = queue_info->buf_size - queue->head -
				SPI_DATA_BOF_SIZE;

			spi_os_memcpy(data_header, (queue_info->buffer +
				queue->head + SPI_DATA_BOF_SIZE), pre_data_len);
			spi_os_memcpy(data_header + pre_data_len,
				queue_info->buffer,
				SPI_DATA_IPC_INNER_LENGTH_SIZE - pre_data_len);
			spi_os_memcpy(&length, data_header,
				SPI_DATA_IPC_INNER_LENGTH_SIZE);
		} else { /* maximum > head + pos_len */
			spi_os_memcpy(&length,
				(queue_info->buffer + queue->head +
				SPI_DATA_BOF_SIZE),
				SPI_DATA_IPC_INNER_LENGTH_SIZE);
		}
	} else { /* RAW, RFS Case */
		if (queue_info->buf_size == (queue->head + SPI_DATA_BOF_SIZE)) {
			/* maximum == head + pos_len */
			spi_os_memcpy(&length, queue_info->buffer,
				SPI_DATA_INNER_LENGTH_SIZE);
		} else if (queue_info->buf_size < (queue->head +
			SPI_DATA_BOF_SIZE + SPI_DATA_INNER_LENGTH_SIZE)) {
			/* maximum < head + pos_len */
			char data_header[SPI_DATA_INNER_LENGTH_SIZE] = {0,};
			pre_data_len = queue_info->buf_size -
				queue->head - SPI_DATA_BOF_SIZE;

			spi_os_memcpy(data_header,
				(queue_info->buffer + queue->head +
				SPI_DATA_BOF_SIZE),
				pre_data_len);
			spi_os_memcpy(data_header + pre_data_len,
				queue_info->buffer,
				SPI_DATA_INNER_LENGTH_SIZE - pre_data_len);
			spi_os_memcpy(&length, data_header,
				SPI_DATA_INNER_LENGTH_SIZE);
		} else { /* maximum > head + pos_len */
			spi_os_memcpy(&length,
				(queue_info->buffer + queue->head +
				SPI_DATA_BOF_SIZE),
				SPI_DATA_INNER_LENGTH_SIZE);
		}
	}
	length += SPI_DATA_BOF_SIZE + SPI_DATA_EOF_SIZE;
	buf_length = length + SPI_DATA_MUX_SIZE + SPI_DATA_LENGTH_SIZE;

	if (length > SPI_DEV_MAX_PACKET_SIZE) {
		if (&spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_TX] == queue_info ||
		&spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_RX] == queue_info) {
			/* IPC Case */
			pr_err("%s %s[%x],%s[%u],%s[%u],%s[%u]\n",
				"[SPI] ERROR : spi_data_dequeue: IPC error",
				"length", length, "buf_size",
				queue_info->buf_size, "head",
				queue->head, "tail", queue->tail);
		} else if (
		&spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_TX] == queue_info
		|| &spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_RX] == queue_info) {
			/* RAW Case */
			pr_err("%s %s[%x],%s[%u],%s[%u],%s[%u]\n",
				"[SPI] ERROR : spi_data_dequeue: RAW error",
				"length", length,
				"buf_size", queue_info->buf_size,
				"head", queue->head, "tail", queue->tail);
		} else { /* RFS Case */
			pr_err("%s %s[%x],%s[%u],%s[%u],%s[%u]\n",
				"[SPI] ERROR : spi_data_dequeue: RFS error",
				"length", length,
				"buf_size", queue_info->buf_size,
				"head", queue->head, "tail", queue->tail);
		}
		spi_os_trace_dump_low("spi_data_dequeue error",
			queue_info->buffer + queue->head - 1, 16);
		spi_os_trace_dump_low("spi_data_dequeue error",
			queue_info->buffer + queue->tail - 1, 16);
		return -1;
	}

	if (&spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_TX] == queue_info
	  || &spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_TX] == queue_info
	  || &spi_queue_info[SPI_DATA_QUEUE_TYPE_RFS_TX] == queue_info) {
		unsigned int templength;

		spi_os_memcpy((char *) pdata, &queue_info->type,
			SPI_DATA_MUX_SIZE);
		pdata += SPI_DATA_MUX_SIZE;
		templength = length-SPI_DATA_BOF_SIZE-SPI_DATA_EOF_SIZE;
		spi_os_memcpy((char *) pdata, &templength,
			SPI_DATA_LENGTH_SIZE);
		pdata += SPI_DATA_LENGTH_SIZE;
	}

	if (queue->tail > queue->head) {
		if (queue->tail - queue->head < length) {
			pr_err("%s %s tail[%u], head[%u], length[%u]\n",
				"[SPI] ERROR : spi_data_dequeue:",
				"request data length is less than queue`s remain data.",
				queue->tail, queue->head, length);

			spi_os_trace_dump_low("spi_data_dequeue error",
				queue_info->buffer + queue->head - 1, 16);
			spi_os_trace_dump_low("spi_data_dequeue error",
				queue_info->buffer + queue->tail - 1, 16);
			return -1;
		}
	} else if (queue->tail < queue->head) {
		if ((queue_info->buf_size - queue->head + queue->tail)
			< length) {
			pr_err("%s %s tail[%u], head[%u], length[%u]\n",
				"[SPI] ERROR : spi_data_dequeue:",
				"request data length is less than queue`s remain data.",
				queue->tail, queue->head, length);

			spi_os_trace_dump_low("spi_data_dequeue error",
				queue_info->buffer + queue->head - 1, 16);
			spi_os_trace_dump_low("spi_data_dequeue error",
				queue_info->buffer + queue->tail - 1, 16);
			return -1;
		}
	}

	if (queue_info->buf_size < (queue->head+length)) {
		/* maximum < head + length */
		pre_data_len = queue_info->buf_size - queue->head;
		spi_os_memcpy((char *)pdata,
			queue_info->buffer + queue->head, pre_data_len);
		spi_os_memcpy((char *)pdata + pre_data_len,
			queue_info->buffer, length - pre_data_len);
		queue->head = length - pre_data_len;
	} else if (queue_info->buf_size == (queue->head+length)) {
		/* maximum = head + length */
		spi_os_memcpy((char *)pdata,
			(queue_info->buffer + queue->head), length);
		queue->head = 0;
	} else { /* maximum > head + length */
		spi_os_memcpy((char *)pdata,
			(queue_info->buffer + queue->head), length);
		queue->head = queue->head + length;
	}

	return buf_length;
}