| /* Copyright 2018 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "cec.h" |
| #include "console.h" |
| #include "task.h" |
| |
| #define CPRINTF(format, args...) cprintf(CC_CEC, format, ## args) |
| #define CPRINTS(format, args...) cprints(CC_CEC, format, ## args) |
| |
| /* |
| * Mutex for the read-offset of the rx queue. Needed since the |
| * queue is read and flushed from different contexts |
| */ |
| static struct mutex rx_queue_readoffset_mutex; |
| |
| int cec_transfer_get_bit(const struct cec_msg_transfer *transfer) |
| { |
| if (transfer->byte >= MAX_CEC_MSG_LEN) |
| return 0; |
| |
| return transfer->buf[transfer->byte] & (0x80 >> transfer->bit); |
| } |
| |
| void cec_transfer_set_bit(struct cec_msg_transfer *transfer, int val) |
| { |
| uint8_t bit_flag; |
| |
| if (transfer->byte >= MAX_CEC_MSG_LEN) |
| return; |
| bit_flag = 0x80 >> transfer->bit; |
| transfer->buf[transfer->byte] &= ~bit_flag; |
| if (val) |
| transfer->buf[transfer->byte] |= bit_flag; |
| } |
| |
| void cec_transfer_inc_bit(struct cec_msg_transfer *transfer) |
| { |
| if (++(transfer->bit) == 8) { |
| if (transfer->byte >= MAX_CEC_MSG_LEN) |
| return; |
| transfer->bit = 0; |
| transfer->byte++; |
| } |
| } |
| |
| int cec_transfer_is_eom(const struct cec_msg_transfer *transfer, int len) |
| { |
| if (transfer->bit) |
| return 0; |
| return (transfer->byte == len); |
| } |
| |
| void cec_rx_queue_flush(struct cec_rx_queue *queue) |
| { |
| mutex_lock(&rx_queue_readoffset_mutex); |
| queue->read_offset = 0; |
| mutex_unlock(&rx_queue_readoffset_mutex); |
| queue->write_offset = 0; |
| } |
| |
| int cec_rx_queue_push(struct cec_rx_queue *queue, const uint8_t *msg, |
| uint8_t msg_len) |
| { |
| int i; |
| uint32_t offset; |
| |
| if (msg_len > MAX_CEC_MSG_LEN || msg_len == 0) |
| return EC_ERROR_INVAL; |
| |
| offset = queue->write_offset; |
| /* Fill in message length last, if successful. Set to zero for now */ |
| queue->buf[offset] = 0; |
| offset = (offset + 1) % CEC_RX_BUFFER_SIZE; |
| |
| for (i = 0 ; i < msg_len; i++) { |
| if (offset == queue->read_offset) { |
| /* Buffer full */ |
| return EC_ERROR_OVERFLOW; |
| } |
| |
| queue->buf[offset] = msg[i]; |
| offset = (offset + 1) % CEC_RX_BUFFER_SIZE; |
| } |
| |
| /* |
| * Don't commit if we caught up with read-offset |
| * since that would indicate an empty buffer |
| */ |
| if (offset == queue->read_offset) { |
| /* Buffer full */ |
| return EC_ERROR_OVERFLOW; |
| } |
| |
| /* Commit the push */ |
| queue->buf[queue->write_offset] = msg_len; |
| queue->write_offset = offset; |
| |
| return EC_SUCCESS; |
| } |
| |
| int cec_rx_queue_pop(struct cec_rx_queue *queue, uint8_t *msg, |
| uint8_t *msg_len) |
| { |
| int i; |
| |
| mutex_lock(&rx_queue_readoffset_mutex); |
| if (queue->read_offset == queue->write_offset) { |
| /* Queue empty */ |
| mutex_unlock(&rx_queue_readoffset_mutex); |
| *msg_len = 0; |
| return -1; |
| } |
| |
| /* The first byte in the buffer is the message length */ |
| *msg_len = queue->buf[queue->read_offset]; |
| if (*msg_len == 0 || *msg_len > MAX_CEC_MSG_LEN) { |
| mutex_unlock(&rx_queue_readoffset_mutex); |
| *msg_len = 0; |
| CPRINTF("Invalid CEC msg size: %u\n", *msg_len); |
| return -1; |
| } |
| |
| queue->read_offset = (queue->read_offset + 1) % CEC_RX_BUFFER_SIZE; |
| for (i = 0; i < *msg_len; i++) { |
| msg[i] = queue->buf[queue->read_offset]; |
| queue->read_offset = (queue->read_offset + 1) % |
| CEC_RX_BUFFER_SIZE; |
| |
| } |
| |
| mutex_unlock(&rx_queue_readoffset_mutex); |
| |
| return 0; |
| } |