forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1365 lines
54 KiB
1365 lines
54 KiB
/* |
|
Copyright (c) 2012, Broadcom Europe Ltd |
|
All rights reserved. |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions are met: |
|
* Redistributions of source code must retain the above copyright |
|
notice, this list of conditions and the following disclaimer. |
|
* Redistributions in binary form must reproduce the above copyright |
|
notice, this list of conditions and the following disclaimer in the |
|
documentation and/or other materials provided with the distribution. |
|
* Neither the name of the copyright holder nor the |
|
names of its contributors may be used to endorse or promote products |
|
derived from this software without specific prior written permission. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
#include <string.h> |
|
#include <stdio.h> |
|
#include "vchost_platform_config.h" |
|
#include "vchost.h" |
|
|
|
#include "interface/vcos/vcos.h" |
|
#include "interface/vchi/vchi.h" |
|
#include "interface/vchi/common/endian.h" |
|
#include "interface/vchi/message_drivers/message.h" |
|
#include "vc_cecservice.h" |
|
#include "vc_service_common.h" |
|
|
|
/****************************************************************************** |
|
Local types and defines. |
|
******************************************************************************/ |
|
#ifndef _min |
|
#define _min(x,y) (((x) <= (y))? (x) : (y)) |
|
#endif |
|
#ifndef _max |
|
#define _max(x,y) (((x) >= (y))? (x) : (y)) |
|
#endif |
|
|
|
//TV service host side state (mostly the same as Videocore side - TVSERVICE_STATE_T) |
|
typedef struct { |
|
//Generic service stuff |
|
VCHI_SERVICE_HANDLE_T client_handle[VCHI_MAX_NUM_CONNECTIONS]; //To connect to server on VC |
|
VCHI_SERVICE_HANDLE_T notify_handle[VCHI_MAX_NUM_CONNECTIONS]; //For incoming notification |
|
uint32_t msg_flag[VCHI_MAX_NUM_CONNECTIONS]; |
|
char command_buffer[CECSERVICE_MSGFIFO_SIZE]; |
|
char response_buffer[CECSERVICE_MSGFIFO_SIZE]; |
|
uint32_t response_length; |
|
uint32_t notify_buffer[CECSERVICE_MSGFIFO_SIZE/sizeof(uint32_t)]; |
|
uint32_t notify_length; |
|
uint32_t num_connections; |
|
VCOS_MUTEX_T lock; |
|
CECSERVICE_CALLBACK_T notify_fn; |
|
void *notify_data; |
|
int initialised; |
|
int to_exit; |
|
|
|
//CEC state, not much here |
|
//Most things live on Videocore side |
|
uint16_t physical_address; //16-bit packed physical address |
|
CEC_DEVICE_TYPE_T logical_address; //logical address |
|
VC_CEC_TOPOLOGY_T *topology; //16-byte aligned for the transfer |
|
|
|
} CECSERVICE_HOST_STATE_T; |
|
|
|
/****************************************************************************** |
|
Static data. |
|
******************************************************************************/ |
|
static CECSERVICE_HOST_STATE_T cecservice_client; |
|
static VCOS_EVENT_T cecservice_message_available_event; |
|
static VCOS_EVENT_T cecservice_notify_available_event; |
|
static VCOS_THREAD_T cecservice_notify_task; |
|
static uint32_t cecservice_log_initialised = 0; |
|
|
|
//Command strings - must match what's in vc_cecservice_defs.h |
|
static char* cecservice_command_strings[] = { |
|
"register_cmd", |
|
"register_all", |
|
"deregister_cmd", |
|
"deregister_all", |
|
"send_msg", |
|
"get_logical_addr", |
|
"alloc_logical_addr", |
|
"release_logical_addr", |
|
"get_topology", |
|
"set_vendor_id", |
|
"set_osd_name", |
|
"get_physical_addr", |
|
"get_vendor_id", |
|
"poll_addr", |
|
"set_logical_addr", |
|
"add_device", |
|
"set_passive", |
|
"end_of_list" |
|
}; |
|
|
|
static const uint32_t max_command_strings = sizeof(cecservice_command_strings)/sizeof(char *); |
|
|
|
//Notification strings - must match vc_cec.h VC_CEC_NOTIFY_T |
|
static char* cecservice_notify_strings[] = { |
|
"none", |
|
"TX", |
|
"RX", |
|
"User Ctrl Pressed", |
|
"User Ctrl Released", |
|
"Vendor Remote Down", |
|
"Vendor Remote Up", |
|
"logical address", |
|
"topology", |
|
"logical address lost", |
|
"???" |
|
}; |
|
|
|
static const uint32_t max_notify_strings = sizeof(cecservice_notify_strings)/sizeof(char *); |
|
|
|
//Device type strings |
|
static char* cecservice_devicetype_strings[] = { |
|
"TV", |
|
"Rec", |
|
"Reserved", |
|
"Tuner", |
|
"Playback", |
|
"Audio", |
|
"Switch", |
|
"VidProc", |
|
"8", "9", "10", "11", "12", "13", "14", "invalid" |
|
}; |
|
|
|
static const uint32_t max_devicetype_strings = sizeof(cecservice_devicetype_strings)/sizeof(char *); |
|
|
|
/****************************************************************************** |
|
Static functions. |
|
******************************************************************************/ |
|
//Lock the host state |
|
static __inline int lock_obtain (void) { |
|
VCOS_STATUS_T status = VCOS_EAGAIN; |
|
if(cecservice_client.initialised && (status = vcos_mutex_lock(&cecservice_client.lock)) == VCOS_SUCCESS) { |
|
if(cecservice_client.initialised) { // check service hasn't been closed while we were waiting for the lock. |
|
vchi_service_use(cecservice_client.client_handle[0]); |
|
return status; |
|
} else { |
|
vcos_mutex_unlock(&cecservice_client.lock); |
|
vc_cec_log_error("CEC Service closed while waiting for lock"); |
|
return VCOS_EAGAIN; |
|
} |
|
} |
|
vc_cec_log_error("CEC service failed to obtain lock, initialised:%d, lock status:%d", |
|
cecservice_client.initialised, status); |
|
return status; |
|
} |
|
|
|
//Unlock the host state |
|
static __inline void lock_release (void) { |
|
if(cecservice_client.initialised) { |
|
vchi_service_release(cecservice_client.client_handle[0]); |
|
} |
|
vcos_mutex_unlock(&cecservice_client.lock); |
|
} |
|
|
|
//Forward declarations |
|
static void cecservice_client_callback( void *callback_param, |
|
VCHI_CALLBACK_REASON_T reason, |
|
void *msg_handle ); |
|
|
|
static void cecservice_notify_callback( void *callback_param, |
|
VCHI_CALLBACK_REASON_T reason, |
|
void *msg_handle ); |
|
|
|
static int32_t cecservice_wait_for_reply(void *response, uint32_t max_length); |
|
|
|
static int32_t cecservice_wait_for_bulk_receive(void *buffer, uint32_t max_length); |
|
|
|
static int32_t cecservice_send_command( uint32_t command, const void *buffer, uint32_t length, uint32_t has_reply); |
|
|
|
static int32_t cecservice_send_command_reply( uint32_t command, void *buffer, uint32_t length, |
|
void *response, uint32_t max_length); |
|
|
|
static void *cecservice_notify_func(void *arg); |
|
|
|
static void cecservice_logging_init(void); |
|
|
|
/****************************************************************************** |
|
Global data |
|
*****************************************************************************/ |
|
VCOS_LOG_CAT_T cechost_log_category; |
|
|
|
/****************************************************************************** |
|
CEC service API |
|
******************************************************************************/ |
|
/****************************************************************************** |
|
NAME |
|
vc_vchi_cec_init |
|
|
|
SYNOPSIS |
|
void vc_vchi_cec_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) |
|
|
|
FUNCTION |
|
Initialise the CEC service for use. A negative return value |
|
indicates failure (which may mean it has not been started on VideoCore). |
|
|
|
RETURNS |
|
int |
|
******************************************************************************/ |
|
VCHPRE_ void VCHPOST_ vc_vchi_cec_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) { |
|
int32_t success = -1; |
|
VCOS_STATUS_T status; |
|
VCOS_THREAD_ATTR_T attrs; |
|
uint32_t i; |
|
|
|
if (cecservice_client.initialised) |
|
return; |
|
|
|
vc_cec_log_info("Initialising CEC service"); |
|
// record the number of connections |
|
vcos_memset( &cecservice_client, 0, sizeof(CECSERVICE_HOST_STATE_T) ); |
|
cecservice_client.num_connections = num_connections; |
|
cecservice_client.physical_address = CEC_CLEAR_ADDR; |
|
cecservice_client.logical_address = CEC_AllDevices_eUnRegistered; |
|
|
|
status = vcos_mutex_create(&cecservice_client.lock, "HCEC"); |
|
vcos_assert(status == VCOS_SUCCESS); |
|
status = vcos_event_create(&cecservice_message_available_event, "HCEC"); |
|
vcos_assert(status == VCOS_SUCCESS); |
|
status = vcos_event_create(&cecservice_notify_available_event, "HCEC"); |
|
vcos_assert(status == VCOS_SUCCESS); |
|
|
|
cecservice_client.topology = vcos_malloc_aligned(sizeof(VC_CEC_TOPOLOGY_T), 16, "HCEC topology"); |
|
vcos_assert(cecservice_client.topology); |
|
|
|
for (i=0; i < cecservice_client.num_connections; i++) { |
|
|
|
// Create a 'Client' service on the each of the connections |
|
SERVICE_CREATION_T cecservice_parameters = { VCHI_VERSION(VC_CECSERVICE_VER), |
|
CECSERVICE_CLIENT_NAME, // 4cc service code |
|
connections[i], // passed in fn ptrs |
|
0, // tx fifo size (unused) |
|
0, // tx fifo size (unused) |
|
&cecservice_client_callback,// service callback |
|
&cecservice_message_available_event, // callback parameter |
|
VC_FALSE, // want_unaligned_bulk_rx |
|
VC_FALSE, // want_unaligned_bulk_tx |
|
VC_FALSE, // want_crc |
|
}; |
|
|
|
SERVICE_CREATION_T cecservice_parameters2 = { VCHI_VERSION(VC_CECSERVICE_VER), |
|
CECSERVICE_NOTIFY_NAME, // 4cc service code |
|
connections[i], // passed in fn ptrs |
|
0, // tx fifo size (unused) |
|
0, // tx fifo size (unused) |
|
&cecservice_notify_callback,// service callback |
|
&cecservice_notify_available_event, // callback parameter |
|
VC_FALSE, // want_unaligned_bulk_rx |
|
VC_FALSE, // want_unaligned_bulk_tx |
|
VC_FALSE, // want_crc |
|
}; |
|
|
|
//Create the client to normal CEC service |
|
success = vchi_service_open( initialise_instance, &cecservice_parameters, &cecservice_client.client_handle[i] ); |
|
vcos_assert( success == 0 ); |
|
if(success) |
|
vc_cec_log_error("Failed to connected to CEC service: %d", success); |
|
|
|
//Create the client to the async CEC service (any CEC related notifications) |
|
success = vchi_service_open( initialise_instance, &cecservice_parameters2, &cecservice_client.notify_handle[i] ); |
|
vcos_assert( success == 0 ); |
|
|
|
if(success) |
|
vc_cec_log_error("Failed to connected to CEC async service: %d", success); |
|
|
|
vchi_service_release(cecservice_client.client_handle[i]); |
|
vchi_service_release(cecservice_client.notify_handle[i]); |
|
} |
|
|
|
//Create the notifier task |
|
vcos_thread_attr_init(&attrs); |
|
vcos_thread_attr_setstacksize(&attrs, 2048); |
|
vcos_thread_attr_settimeslice(&attrs, 1); |
|
|
|
//Initialise logging |
|
cecservice_logging_init(); |
|
|
|
status = vcos_thread_create(&cecservice_notify_task, "HCEC Notify", &attrs, cecservice_notify_func, &cecservice_client); |
|
vcos_assert(status == VCOS_SUCCESS); |
|
|
|
cecservice_client.initialised = 1; |
|
vc_cec_log_info("CEC service initialised"); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_vchi_cec_stop |
|
* |
|
* Arguments: |
|
* - |
|
* |
|
* Description: Stops the Host side part of CEC service |
|
* |
|
* Returns: - |
|
* |
|
***********************************************************/ |
|
VCHPRE_ void VCHPOST_ vc_vchi_cec_stop( void ) { |
|
// Wait for the current lock-holder to finish before zapping TV service |
|
uint32_t i; |
|
|
|
if (!cecservice_client.initialised) |
|
return; |
|
|
|
if(lock_obtain() == 0) |
|
{ |
|
void *dummy; |
|
vchi_service_release(cecservice_client.client_handle[0]); |
|
vc_cec_log_info("Stopping CEC service"); |
|
for (i=0; i < cecservice_client.num_connections; i++) { |
|
int32_t result; |
|
vchi_service_use(cecservice_client.client_handle[i]); |
|
vchi_service_use(cecservice_client.notify_handle[i]); |
|
result = vchi_service_close(cecservice_client.client_handle[i]); |
|
vcos_assert( result == 0 ); |
|
result = vchi_service_close(cecservice_client.notify_handle[i]); |
|
vcos_assert( result == 0 ); |
|
} |
|
cecservice_client.initialised = 0; |
|
|
|
lock_release(); |
|
cecservice_client.to_exit = 1; |
|
vcos_event_signal(&cecservice_notify_available_event); |
|
vcos_thread_join(&cecservice_notify_task, &dummy); |
|
vcos_mutex_delete(&cecservice_client.lock); |
|
vcos_event_delete(&cecservice_message_available_event); |
|
vcos_event_delete(&cecservice_notify_available_event); |
|
vcos_free(cecservice_client.topology); |
|
vc_cec_log_info("CEC service stopped"); |
|
} |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_register_callaback |
|
* |
|
* Arguments: |
|
* callback function, context to be passed when function is called |
|
* |
|
* Description: Register a callback function for all CEC notifications |
|
* |
|
* Returns: - |
|
* |
|
***********************************************************/ |
|
VCHPRE_ void VCHPOST_ vc_cec_register_callback(CECSERVICE_CALLBACK_T callback, void *callback_data) { |
|
if(lock_obtain() == 0){ |
|
cecservice_client.notify_fn = callback; |
|
cecservice_client.notify_data = callback_data; |
|
vc_cec_log_info("CEC service registered callback 0x%x", (uint32_t) callback); |
|
lock_release(); |
|
} else { |
|
vc_cec_log_error("CEC service registered callback 0x%x failed", (uint32_t) callback); |
|
} |
|
} |
|
|
|
/********************************************************************************* |
|
* |
|
* Static functions definitions |
|
* |
|
*********************************************************************************/ |
|
//TODO: Might need to handle multiple connections later |
|
/*********************************************************** |
|
* Name: cecservice_client_callback |
|
* |
|
* Arguments: semaphore, callback reason and message handle |
|
* |
|
* Description: Callback when a message is available for CEC service |
|
* |
|
***********************************************************/ |
|
static void cecservice_client_callback( void *callback_param, |
|
const VCHI_CALLBACK_REASON_T reason, |
|
void *msg_handle ) { |
|
|
|
VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param; |
|
|
|
if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || event == NULL) |
|
return; |
|
|
|
vcos_event_signal(event); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: cecservice_notify_callback |
|
* |
|
* Arguments: semaphore, callback reason and message handle |
|
* |
|
* Description: Callback when a message is available for CEC notify service |
|
* |
|
***********************************************************/ |
|
static void cecservice_notify_callback( void *callback_param, |
|
const VCHI_CALLBACK_REASON_T reason, |
|
void *msg_handle ) { |
|
VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param; |
|
|
|
if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || event == NULL) |
|
return; |
|
|
|
vcos_event_signal(event); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: cecservice_wait_for_reply |
|
* |
|
* Arguments: response buffer, buffer length |
|
* |
|
* Description: blocked until something is in the buffer |
|
* |
|
* Returns zero if successful or error code of vchi otherwise (see vc_service_common_defs.h) |
|
* If success, response is updated |
|
* |
|
***********************************************************/ |
|
static int32_t cecservice_wait_for_reply(void *response, uint32_t max_length) { |
|
int32_t success = 0; |
|
uint32_t length_read = 0; |
|
do { |
|
//TODO : we need to deal with messages coming through on more than one connections properly |
|
//At the moment it will always try to read the first connection if there is something there |
|
//Check if there is something in the queue, if so return immediately |
|
//otherwise wait for the semaphore and read again |
|
success = (int32_t) vchi2service_status(vchi_msg_dequeue( cecservice_client.client_handle[0], response, max_length, &length_read, VCHI_FLAGS_NONE )); |
|
} while( length_read == 0 && vcos_event_wait(&cecservice_message_available_event) == VCOS_SUCCESS); |
|
if(length_read) { |
|
vc_cec_log_info("CEC service got reply %d bytes", length_read); |
|
} else { |
|
vc_cec_log_warn("CEC service wait for reply failed, error: %s", |
|
vchi2service_status_string(success)); |
|
} |
|
|
|
return success; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: cecservice_wait_for_bulk_receive |
|
* |
|
* Arguments: response buffer, buffer length |
|
* |
|
* Description: blocked until bulk receive |
|
* |
|
* Returns error code of vchi |
|
* |
|
***********************************************************/ |
|
static int32_t cecservice_wait_for_bulk_receive(void *buffer, uint32_t max_length) { |
|
if(!vcos_verify(buffer)) { |
|
vc_cec_log_error("CEC: NULL buffer passed to wait_for_bulk_receive"); |
|
return -1; |
|
} |
|
return (int32_t) vchi2service_status(vchi_bulk_queue_receive( cecservice_client.client_handle[0], |
|
buffer, |
|
max_length, |
|
VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE, |
|
NULL )); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: cecservice_send_command |
|
* |
|
* Arguments: command, parameter buffer, parameter legnth, has reply? (non-zero means yes) |
|
* |
|
* Description: send a command and optionally wait for its single value response (TV_GENERAL_RESP_T) |
|
* |
|
* Returns: < 0 if there is VCHI error, if tranmission is successful, value |
|
* returned is the response from CEC server (which will be VC_CEC_ERROR_T (>= 0)) |
|
* |
|
***********************************************************/ |
|
|
|
static int32_t cecservice_send_command( uint32_t command, const void *buffer, uint32_t length, uint32_t has_reply) { |
|
VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)}, |
|
{buffer, length} }; |
|
int32_t success = 0; |
|
int32_t response = -1; |
|
vc_cec_log_info("CEC sending command %s length %d %s", |
|
cecservice_command_strings[command], length, |
|
(has_reply)? "has reply" : " no reply"); |
|
if(lock_obtain() == 0) |
|
{ |
|
success = (int32_t) vchi2service_status(vchi_msg_queuev(cecservice_client.client_handle[0], |
|
vector, sizeof(vector)/sizeof(vector[0]), |
|
VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL )); |
|
if(success == VC_SERVICE_VCHI_SUCCESS && has_reply) { |
|
//otherwise only wait for a reply if we ask for one |
|
success = cecservice_wait_for_reply(&response, sizeof(response)); |
|
if(success == VC_SERVICE_VCHI_SUCCESS) { |
|
response = VC_VTOH32(response); |
|
} else { |
|
response = success; |
|
} |
|
} else { |
|
if(success != VC_SERVICE_VCHI_SUCCESS) |
|
vc_cec_log_error("CEC failed to send command %s length %d, error: %s", |
|
cecservice_command_strings[command], length, |
|
vchi2service_status_string(success)); |
|
//No reply expected or failed to send, send the success code back instead |
|
response = success; |
|
} |
|
lock_release(); |
|
} |
|
return response; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: cecservice_send_command_reply |
|
* |
|
* Arguments: command, parameter buffer, parameter legnth, reply buffer, buffer length |
|
* |
|
* Description: send a command and wait for its non-single value response (in a buffer) |
|
* |
|
* Returns: error code, host app is responsible to do endian translation |
|
* |
|
***********************************************************/ |
|
static int32_t cecservice_send_command_reply( uint32_t command, void *buffer, uint32_t length, |
|
void *response, uint32_t max_length) { |
|
VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)}, |
|
{buffer, length} }; |
|
int32_t success = VC_SERVICE_VCHI_VCHIQ_ERROR, ret = 0; |
|
|
|
vc_cec_log_info("CEC sending command (with reply) %s length %d", |
|
cecservice_command_strings[command], length); |
|
if(lock_obtain() == 0) |
|
{ |
|
if((ret = vchi_msg_queuev( cecservice_client.client_handle[0], |
|
vector, sizeof(vector)/sizeof(vector[0]), |
|
VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL )) == VC_SERVICE_VCHI_SUCCESS) { |
|
success = cecservice_wait_for_reply(response, max_length); |
|
} else { |
|
vc_cec_log_error("CEC failed to send command %s length %d, error code %d", |
|
cecservice_command_strings[command], length, ret); |
|
} |
|
lock_release(); |
|
} |
|
return success; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: cecservice_notify_func |
|
* |
|
* Arguments: CEC service state |
|
* |
|
* Description: This is the notification task which receives all CEC |
|
* service notifications |
|
* |
|
* Returns: does not return |
|
* |
|
***********************************************************/ |
|
static void *cecservice_notify_func(void *arg) { |
|
int32_t success; |
|
CECSERVICE_HOST_STATE_T *state = (CECSERVICE_HOST_STATE_T *) arg; |
|
|
|
vc_cec_log_info("CEC service async thread started"); |
|
while(1) { |
|
VCOS_STATUS_T status = vcos_event_wait(&cecservice_notify_available_event); |
|
uint32_t cb_reason_str_idx = max_notify_strings - 1; |
|
if(status != VCOS_SUCCESS || !state->initialised || state->to_exit) |
|
break; |
|
|
|
do { |
|
uint32_t reason, param1, param2, param3, param4; |
|
//Get all notifications in the queue |
|
success = vchi_msg_dequeue( state->notify_handle[0], state->notify_buffer, sizeof(state->notify_buffer), &state->notify_length, VCHI_FLAGS_NONE ); |
|
if(success != 0 || state->notify_length < sizeof(uint32_t)*5 ) { //reason + 4x32-bit parameter |
|
vcos_assert(state->notify_length == sizeof(uint32_t)*5); |
|
break; |
|
} |
|
|
|
//if(lock_obtain() != 0) |
|
// break; |
|
//All notifications are of format: reason, param1, param2, param3, param4 (all 32-bit unsigned int) |
|
reason = VC_VTOH32(state->notify_buffer[0]); |
|
param1 = VC_VTOH32(state->notify_buffer[1]); |
|
param2 = VC_VTOH32(state->notify_buffer[2]); |
|
param3 = VC_VTOH32(state->notify_buffer[3]); |
|
param4 = VC_VTOH32(state->notify_buffer[4]); |
|
//lock_release(); |
|
|
|
//Store away physical/logical addresses |
|
if(CEC_CB_REASON(reason) == VC_CEC_LOGICAL_ADDR) { |
|
state->logical_address = (CEC_DEVICE_TYPE_T) param1; |
|
state->physical_address = (uint16_t) (param2 & 0xFFFF); |
|
} |
|
|
|
switch(CEC_CB_REASON(reason)) { |
|
case VC_CEC_NOTIFY_NONE: |
|
cb_reason_str_idx = 0; break; |
|
case VC_CEC_TX: |
|
cb_reason_str_idx = 1; break; |
|
case VC_CEC_RX: |
|
cb_reason_str_idx = 2; break; |
|
case VC_CEC_BUTTON_PRESSED: |
|
cb_reason_str_idx = 3; break; |
|
case VC_CEC_BUTTON_RELEASE: |
|
cb_reason_str_idx = 4; break; |
|
case VC_CEC_REMOTE_PRESSED: |
|
cb_reason_str_idx = 5; break; |
|
case VC_CEC_REMOTE_RELEASE: |
|
cb_reason_str_idx = 6; break; |
|
case VC_CEC_LOGICAL_ADDR: |
|
cb_reason_str_idx = 7; break; |
|
case VC_CEC_TOPOLOGY: |
|
cb_reason_str_idx = 8; break; |
|
case VC_CEC_LOGICAL_ADDR_LOST: |
|
cb_reason_str_idx = 9; break; |
|
} |
|
|
|
vc_cec_log_info("CEC service callback [%s]: 0x%x, 0x%x, 0x%x, 0x%x", |
|
cecservice_notify_strings[cb_reason_str_idx], param1, param2, param3, param4); |
|
|
|
if(state->notify_fn) { |
|
(*state->notify_fn)(state->notify_data, reason, param1, param2, param3, param4); |
|
} else { |
|
vc_cec_log_info("CEC service: No callback handler specified, callback [%s] swallowed", |
|
cecservice_notify_strings[cb_reason_str_idx]); |
|
} |
|
|
|
} while(success == 0 && state->notify_length >= sizeof(uint32_t)*5); //read the next message if any |
|
} //while (1) |
|
|
|
if(state->to_exit) |
|
vc_cec_log_info("CEC service async thread exiting"); |
|
|
|
return 0; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: cecservice_logging_init |
|
* |
|
* Arguments: None |
|
* |
|
* Description: Initialise VCOS logging |
|
* |
|
* Returns: - |
|
* |
|
***********************************************************/ |
|
static void cecservice_logging_init() { |
|
if(cecservice_log_initialised == 0) { |
|
vcos_log_set_level(&cechost_log_category, VCOS_LOG_WARN); |
|
vcos_log_register("cecservice-client", &cechost_log_category); |
|
vc_cec_log_info("CEC HOST: log initialised"); |
|
cecservice_log_initialised = 1; |
|
} |
|
} |
|
|
|
|
|
/*********************************************************** |
|
Actual CEC service API starts here |
|
***********************************************************/ |
|
/*********************************************************** |
|
* Name: vc_cec_register_command (deprecated) |
|
* |
|
* Arguments: |
|
* |
|
* Description |
|
* |
|
* Returns: zero |
|
***********************************************************/ |
|
VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_register_command(CEC_OPCODE_T opcode) { |
|
return 0; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_register_all (deprecated) |
|
* |
|
* Arguments: |
|
* |
|
* Description |
|
* |
|
* Returns: zero |
|
***********************************************************/ |
|
VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_register_all( void ) { |
|
return 0; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_deregister_command (deprecated) |
|
* |
|
* Arguments: |
|
* |
|
* Description |
|
* |
|
* Returns: zero |
|
***********************************************************/ |
|
VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_deregister_command(CEC_OPCODE_T opcode) { |
|
return 0; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_deregister_all (deprecated) |
|
* |
|
* Arguments: |
|
* |
|
* Description |
|
* |
|
* Returns: zero |
|
***********************************************************/ |
|
VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_deregister_all( void ) { |
|
return 0; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_send_message |
|
* |
|
* Arguments: |
|
* Follower's logical address |
|
* Message payload WITHOUT the header byte (can be NULL) |
|
* Payload length WITHOUT the header byte (can be zero) |
|
* VC_TRUE if the message is a reply to an incoming message |
|
* (For poll message set payload to NULL and length to zero) |
|
* |
|
* Description |
|
* Remove all commands to be forwarded. This does not affect |
|
* the button presses which are always forwarded |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* If the command is successful, there will be a Tx callback |
|
* in due course to indicate whether the message has been |
|
* acknowledged by the recipient or not |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_send_message(const uint32_t follower, |
|
const uint8_t *payload, |
|
uint32_t length, |
|
vcos_bool_t is_reply) { |
|
int success = -1; |
|
CEC_SEND_MSG_PARAM_T param; |
|
if(!vcos_verify(length <= CEC_MAX_XMIT_LENGTH)) |
|
return -1; |
|
|
|
param.follower = VC_HTOV32(follower); |
|
param.length = VC_HTOV32(length); |
|
param.is_reply = VC_HTOV32(is_reply); |
|
vcos_memset(param.payload, 0, sizeof(param.payload)); |
|
vc_cec_log_info("CEC service sending CEC message (%d->%d) (0x%02X) length %d%s", |
|
cecservice_client.logical_address, follower, |
|
(payload)? payload[0] : 0xFF, length, (is_reply)? " as reply" : ""); |
|
|
|
if(length > 0 && vcos_verify(payload)) { |
|
char s[96] = {0}, *p = &s[0]; |
|
int i; |
|
vcos_memcpy(param.payload, payload, _min(length, CEC_MAX_XMIT_LENGTH)); |
|
p += sprintf(p, "0x%02X", (cecservice_client.logical_address << 4) | (follower & 0xF)); |
|
for(i = 0; i < _min(length, CEC_MAX_XMIT_LENGTH); i++) { |
|
p += sprintf(p, " %02X", payload[i]); |
|
} |
|
vc_cec_log_info("CEC message: %s", s); |
|
} |
|
|
|
success = cecservice_send_command( VC_CEC_SEND_MSG, ¶m, sizeof(param), 1); |
|
return success; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_get_logical_address |
|
* |
|
* Arguments: |
|
* pointer to logical address |
|
* |
|
* Description |
|
* Get the logical address, if one is being allocated |
|
* 0xF (unregistered) will be returned |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* logical_address is not modified if command failed |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_get_logical_address(CEC_AllDevices_T *logical_address) { |
|
uint32_t response; |
|
int32_t success = cecservice_send_command_reply( VC_CEC_GET_LOGICAL_ADDR, NULL, 0, |
|
&response, sizeof(response)); |
|
if(success == 0) { |
|
*logical_address = (CEC_AllDevices_T)(VC_VTOH32(response) & 0xF); |
|
vc_cec_log_info("CEC got logical address %d", *logical_address); |
|
} |
|
return success; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_alloc_logical_address |
|
* |
|
* Arguments: |
|
* None |
|
* |
|
* Description |
|
* Start the allocation of a logical address. The host only |
|
* needs to call this if the initial allocation failed |
|
* (logical address being 0xF and physical address is NOT 0xFFFF |
|
* from VC_CEC_LOGICAL_ADDR notification), or if the host explicitly |
|
* released its logical address. |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* If successful, there will be a callback notification |
|
* VC_CEC_LOGICAL_ADDR. The host should wait for this before |
|
* calling this function again. |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_alloc_logical_address( void ) { |
|
return cecservice_send_command( VC_CEC_ALLOC_LOGICAL_ADDR, NULL, 0, 0); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_release_logical_address |
|
* |
|
* Arguments: |
|
* None |
|
* |
|
* Description |
|
* Release our logical address. This effectively disables CEC. |
|
* The host will need to allocate a new logical address before |
|
* doing any CEC calls (send/receive message, get topology, etc.). |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* The host should get a callback VC_CEC_LOGICAL_ADDR with |
|
* 0xF being the logical address and the current physical address. |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_release_logical_address( void ) { |
|
return cecservice_send_command( VC_CEC_RELEASE_LOGICAL_ADDR, NULL, 0, 0); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_get_topology (deprecated) |
|
* |
|
* Arguments: |
|
* |
|
* Description |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* |
|
***********************************************************/ |
|
VCHPRE_ int VCOS_DEPRECATED("returns invalid result") VCHPOST_ vc_cec_get_topology( VC_CEC_TOPOLOGY_T* topology) { |
|
int32_t success = -1; |
|
vchi_service_use(cecservice_client.client_handle[0]); |
|
success = cecservice_send_command( VC_CEC_GET_TOPOLOGY, NULL, 0, 1); |
|
if(success == 0) { |
|
success = cecservice_wait_for_bulk_receive(cecservice_client.topology, sizeof(VC_CEC_TOPOLOGY_T)); |
|
} |
|
vchi_service_release(cecservice_client.client_handle[0]); |
|
if(success == 0) { |
|
int i; |
|
cecservice_client.topology->active_mask = VC_VTOH16(cecservice_client.topology->active_mask); |
|
cecservice_client.topology->num_devices = VC_VTOH16(cecservice_client.topology->num_devices); |
|
vc_cec_log_info("CEC topology: mask=0x%x; #device=%d", |
|
cecservice_client.topology->active_mask, |
|
cecservice_client.topology->num_devices); |
|
for(i = 0; i < 15; i++) { |
|
cecservice_client.topology->device_attr[i] = VC_VTOH32(cecservice_client.topology->device_attr[i]); |
|
} |
|
vcos_memcpy(topology, cecservice_client.topology, sizeof(VC_CEC_TOPOLOGY_T)); |
|
} |
|
return success; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_set_vendor_id |
|
* |
|
* Arguments: |
|
* 24-bit IEEE vendor id |
|
* |
|
* Description |
|
* Set the response to <Give Device Vendor ID> |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_set_vendor_id( uint32_t id ) { |
|
uint32_t vendor_id = VC_HTOV32(id); |
|
vc_cec_log_info("CEC setting vendor id to 0x%x", vendor_id); |
|
return cecservice_send_command( VC_CEC_SET_VENDOR_ID, &vendor_id, sizeof(vendor_id), 0); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_set_osd_name |
|
* |
|
* Arguments: |
|
* OSD name (14 byte array) |
|
* |
|
* Description |
|
* Set the response to <Give OSD Name> |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_set_osd_name( const char* name ) { |
|
vc_cec_log_info("CEC setting OSD name to %s", name); |
|
return cecservice_send_command( VC_CEC_SET_OSD_NAME, name, OSD_NAME_LENGTH, 0); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_get_physical_address |
|
* |
|
* Arguments: |
|
* pointer to physical address (returned as 16-bit packed value) |
|
* |
|
* Description |
|
* Get the physical address |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* If failed, physical address argument will not be changed |
|
* A physical address of 0xFFFF means CEC is not supported |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_get_physical_address(uint16_t *physical_address) { |
|
uint32_t response; |
|
int32_t success = cecservice_send_command_reply( VC_CEC_GET_PHYSICAL_ADDR, NULL, 0, |
|
&response, sizeof(response)); |
|
if(success == 0) { |
|
*physical_address = (uint16_t)(VC_VTOH32(response) & 0xFFFF); |
|
vc_cec_log_info("CEC got physical address: %d.%d.%d.%d", |
|
(*physical_address >> 12), (*physical_address >> 8) & 0xF, |
|
(*physical_address >> 4) & 0xF, (*physical_address) & 0xF); |
|
} |
|
return success; |
|
} |
|
|
|
|
|
/*********************************************************** |
|
* Name: vc_cec_get_vendor_id |
|
* |
|
* Arguments: |
|
* logical address [in] |
|
* pointer to 24-bit IEEE vendor id [out] |
|
* |
|
* Description |
|
* Get the vendor ID of the device with the said logical address |
|
* Application should send <Give Device Vendor ID> if vendor ID |
|
* is not known (and register opcode <Device Vendor ID>) |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* vendor ID is set to zero if unknown or 0xFFFFFF if |
|
* device does not exist. |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_get_vendor_id( const CEC_AllDevices_T logical_address, uint32_t *vendor_id) { |
|
uint32_t log_addr = VC_HTOV32(logical_address); |
|
uint32_t response; |
|
int32_t success = cecservice_send_command_reply( VC_CEC_GET_VENDOR_ID, &log_addr, sizeof(log_addr), |
|
&response, sizeof(response)); |
|
if(success == 0) { |
|
vcos_assert(vendor_id); |
|
*vendor_id = VC_VTOH32(response); |
|
vc_cec_log_info("CEC got vendor id 0x%X", *vendor_id); |
|
} |
|
return success; |
|
} |
|
/*********************************************************** |
|
* Name: vc_cec_device_type |
|
* |
|
* Arguments: |
|
* logical address [in] |
|
* |
|
* Description |
|
* Get the default device type of a logical address |
|
* Logical address 12 to 14 cannot be used |
|
* |
|
* Returns: For logical addresses 0-11 the default |
|
* device type of that address will be returned |
|
* logical address 12-14 will return "reserved" type. |
|
* |
|
***********************************************************/ |
|
VCHPRE_ CEC_DEVICE_TYPE_T VCHPOST_ vc_cec_device_type(const CEC_AllDevices_T logical_address) { |
|
CEC_DEVICE_TYPE_T device_type = CEC_DeviceType_Invalid; |
|
switch(logical_address) { |
|
case CEC_AllDevices_eSTB1: |
|
case CEC_AllDevices_eSTB2: |
|
case CEC_AllDevices_eSTB3: |
|
case CEC_AllDevices_eSTB4: |
|
device_type = CEC_DeviceType_Tuner; |
|
break; |
|
case CEC_AllDevices_eDVD1: |
|
case CEC_AllDevices_eDVD2: |
|
case CEC_AllDevices_eDVD3: |
|
device_type = CEC_DeviceType_Playback; |
|
break; |
|
case CEC_AllDevices_eRec1: |
|
case CEC_AllDevices_eRec2: |
|
case CEC_AllDevices_eRec3: |
|
device_type = CEC_DeviceType_Rec; |
|
break; |
|
case CEC_AllDevices_eAudioSystem: |
|
device_type = CEC_DeviceType_Audio; |
|
break; |
|
case CEC_AllDevices_eTV: |
|
device_type = CEC_DeviceType_TV; |
|
break; |
|
case CEC_AllDevices_eRsvd3: |
|
case CEC_AllDevices_eRsvd4: |
|
case CEC_AllDevices_eFreeUse: |
|
device_type = CEC_DeviceType_Reserved; //XXX: Are we allowed to use this? |
|
break; |
|
default: |
|
vcos_assert(0); //Invalid |
|
break; |
|
} |
|
return device_type; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_send_message2 |
|
* |
|
* Arguments: |
|
* pointer to encapsulated message |
|
* |
|
* Description |
|
* Call vc_cec_send_message above |
|
* messages are always sent as non-reply |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* If the command is successful, there will be a Tx callback |
|
* in due course to indicate whether the message has been |
|
* acknowledged by the recipient or not |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_send_message2(const VC_CEC_MESSAGE_T *message) { |
|
if(vcos_verify(message)) { |
|
return vc_cec_send_message(message->follower, |
|
(message->length)? |
|
message->payload : NULL, |
|
message->length, |
|
VC_FALSE); |
|
} else { |
|
return -1; |
|
} |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_param2message |
|
* |
|
* Arguments: |
|
* arguments from CEC callback (reason, param1 to param4) |
|
* pointer to VC_CEC_MESSAGE_T |
|
* |
|
* Description |
|
* Turn the CEC_TX/CEC_RX/BUTTON_PRESS/BUTTON_RELEASE |
|
* callback parameters back into an encapsulated form |
|
* |
|
* Returns: zero normally |
|
* non-zero if something has gone wrong |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_param2message( const uint32_t reason, const uint32_t param1, |
|
const uint32_t param2, const uint32_t param3, |
|
const uint32_t param4, VC_CEC_MESSAGE_T *message) { |
|
if(vcos_verify(message && |
|
CEC_CB_REASON(reason) != VC_CEC_LOGICAL_ADDR && |
|
CEC_CB_REASON(reason) != VC_CEC_TOPOLOGY)) { |
|
message->length = CEC_CB_MSG_LENGTH(reason) - 1; //Length is without the header byte |
|
message->initiator = CEC_CB_INITIATOR(param1); |
|
message->follower = CEC_CB_FOLLOWER(param1); |
|
if(message->length) { |
|
uint32_t tmp = param1 >> 8; |
|
vcos_memcpy(message->payload, &tmp, sizeof(uint32_t)-1); |
|
vcos_memcpy(message->payload+sizeof(uint32_t)-1, ¶m2, sizeof(uint32_t)); |
|
vcos_memcpy(message->payload+sizeof(uint32_t)*2-1, ¶m3, sizeof(uint32_t)); |
|
vcos_memcpy(message->payload+sizeof(uint32_t)*3-1, ¶m4, sizeof(uint32_t)); |
|
} else { |
|
vcos_memset(message->payload, 0, sizeof(message->payload)); |
|
} |
|
return 0; |
|
} else { |
|
return -1; |
|
} |
|
} |
|
|
|
//Extra API if CEC is running in passive mode |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_poll_address |
|
* |
|
* Arguments: |
|
* logical address to try |
|
* |
|
* Description |
|
* Sets and polls a particular address to find out |
|
* its availability in the CEC network. Only available |
|
* when CEC is running in passive mode. The host can |
|
* only call this function during logical address allocation stage. |
|
* |
|
* Returns: 0 if poll is successful (address is occupied) |
|
* >0 if poll is unsuccessful (address is free if error code is VC_CEC_ERROR_NO_ACK) |
|
* <0 other (VCHI) errors |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_poll_address(const CEC_AllDevices_T logical_address) { |
|
uint32_t log_addr = VC_HTOV32(logical_address); |
|
int32_t response = VC_CEC_ERROR_INVALID_ARGUMENT; |
|
int32_t success = -1; |
|
vc_cec_log_info("CEC polling address %d", logical_address); |
|
success = cecservice_send_command_reply( VC_CEC_POLL_ADDR, &log_addr, sizeof(log_addr), |
|
&response, sizeof(response)); |
|
return (success == 0)? response : success; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_set_logical_address |
|
* |
|
* Arguments: |
|
* logical address, device type, vendor id |
|
* |
|
* Description |
|
* sets the logical address, device type and vendor ID to be in use. |
|
* Only available when CEC is running in passive mode. It is the |
|
* responsibility of the host to make sure the logical address |
|
* is actually free (see vc_cec_poll_address). Physical address used |
|
* will be what is read from EDID and cannot be set. |
|
* |
|
* Returns: 0 if successful, non-zero otherwise |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_set_logical_address(const CEC_AllDevices_T logical_address, |
|
const CEC_DEVICE_TYPE_T device_type, |
|
const uint32_t vendor_id) { |
|
CEC_SET_LOGICAL_ADDR_PARAM_T param = {VC_HTOV32(logical_address), |
|
VC_HTOV32(device_type), |
|
VC_HTOV32(vendor_id)}; |
|
int32_t response = VC_CEC_ERROR_INVALID_ARGUMENT; |
|
int32_t success = VC_CEC_ERROR_INVALID_ARGUMENT; |
|
if(vcos_verify(logical_address <= CEC_AllDevices_eUnRegistered && |
|
(device_type <= CEC_DeviceType_VidProc || |
|
device_type == CEC_DeviceType_Invalid))) { |
|
vc_cec_log_info("CEC setting logical address to %d; device type %s; vendor 0x%X", |
|
logical_address, |
|
cecservice_devicetype_strings[device_type], vendor_id ); |
|
success = cecservice_send_command_reply( VC_CEC_SET_LOGICAL_ADDR, ¶m, sizeof(param), |
|
&response, sizeof(response)); |
|
} else { |
|
vc_cec_log_error("CEC invalid arguments for set_logical_address"); |
|
} |
|
return (success == 0)? response : success; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_add_device (deprecated) |
|
* |
|
* Arguments: |
|
* |
|
* Description |
|
* |
|
* Returns: 0 if successful, non-zero otherwise |
|
***********************************************************/ |
|
VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_add_device(const CEC_AllDevices_T logical_address, |
|
const uint16_t physical_address, |
|
const CEC_DEVICE_TYPE_T device_type, |
|
vcos_bool_t last_device) { |
|
CEC_ADD_DEVICE_PARAM_T param = {VC_HTOV32(logical_address), |
|
VC_HTOV32(physical_address), |
|
VC_HTOV32(device_type), |
|
VC_HTOV32(last_device)}; |
|
int32_t response = VC_CEC_ERROR_INVALID_ARGUMENT; |
|
int32_t success = VC_CEC_ERROR_INVALID_ARGUMENT; |
|
if(vcos_verify(logical_address <= CEC_AllDevices_eUnRegistered && |
|
(device_type <= CEC_DeviceType_VidProc || |
|
device_type == CEC_DeviceType_Invalid))) { |
|
vc_cec_log_info("CEC adding device %d (0x%X); device type %s", |
|
logical_address, physical_address, |
|
cecservice_devicetype_strings[device_type]); |
|
success = cecservice_send_command_reply( VC_CEC_ADD_DEVICE, ¶m, sizeof(param), |
|
&response, sizeof(response)); |
|
} else { |
|
vc_cec_log_error("CEC invalid arguments for add_device"); |
|
} |
|
return (success == 0)? response : success; |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_set_passive |
|
* |
|
* Arguments: |
|
* Enable/disable (TRUE to enable/ FALSE to disable) |
|
* |
|
* Description |
|
* Enable / disable CEC passive mode |
|
* |
|
* Returns: 0 if successful, non-zero otherwise |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_set_passive(vcos_bool_t enabled) { |
|
uint32_t param = VC_HTOV32(enabled); |
|
int32_t response; |
|
int32_t success = cecservice_send_command_reply( VC_CEC_SET_PASSIVE, ¶m, sizeof(param), |
|
&response, sizeof(response)); |
|
return (success == 0)? response : success; |
|
} |
|
|
|
/*********************************************************** |
|
API for some common CEC messages, uses the API above to |
|
actually send the message |
|
***********************************************************/ |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_send_FeatureAbort |
|
* |
|
* Arguments: |
|
* follower, rejected opcode, reject reason, reply or not |
|
* |
|
* Description |
|
* send <Feature Abort> for a received command |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* Tx callback if successful |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_send_FeatureAbort(uint32_t follower, |
|
CEC_OPCODE_T opcode, |
|
CEC_ABORT_REASON_T reason) { |
|
uint8_t tx_buf[3]; |
|
tx_buf[0] = CEC_Opcode_FeatureAbort; // <Feature Abort> |
|
tx_buf[1] = opcode; |
|
tx_buf[2] = reason; |
|
return vc_cec_send_message(follower, |
|
tx_buf, |
|
sizeof(tx_buf), |
|
VC_TRUE); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_send_ActiveSource |
|
* |
|
* Arguments: |
|
* physical address, reply or not |
|
* |
|
* Description |
|
* send <Active Source> |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* Tx callback if successful |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_send_ActiveSource(uint16_t physical_address, |
|
vcos_bool_t is_reply) { |
|
uint8_t tx_buf[3]; |
|
tx_buf[0] = CEC_Opcode_ActiveSource; // <Active Source> |
|
tx_buf[1] = physical_address >> 8; // physical address msb |
|
tx_buf[2] = physical_address & 0x00FF; // physical address lsb |
|
return vc_cec_send_message(CEC_BROADCAST_ADDR, // This is a broadcast only message |
|
tx_buf, |
|
sizeof(tx_buf), |
|
is_reply); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_send_ImageViewOn |
|
* |
|
* Arguments: |
|
* follower, reply or not |
|
* Description |
|
* send <Image View On> |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* Tx callback if successful |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_send_ImageViewOn(uint32_t follower, |
|
vcos_bool_t is_reply) { |
|
uint8_t tx_buf[1]; |
|
tx_buf[0] = CEC_Opcode_ImageViewOn; // <Image View On> no param required |
|
return vc_cec_send_message(follower, |
|
tx_buf, |
|
sizeof(tx_buf), |
|
is_reply); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_send_SetOSDString |
|
* |
|
* Arguments: |
|
* follower, display control, string (char[13]), reply or not |
|
* |
|
* Description |
|
* send <Image View On> |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* Tx callback if successful |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_send_SetOSDString(uint32_t follower, |
|
CEC_DISPLAY_CONTROL_T disp_ctrl, |
|
const char* string, |
|
vcos_bool_t is_reply) { |
|
uint8_t tx_buf[CEC_MAX_XMIT_LENGTH]; |
|
tx_buf[0] = CEC_Opcode_SetOSDString; // <Set OSD String> |
|
tx_buf[1] = disp_ctrl; |
|
vcos_memset(&tx_buf[2], 0, sizeof(tx_buf)-2); |
|
vcos_memcpy(&tx_buf[2], string, _min(strlen(string), CEC_MAX_XMIT_LENGTH-2)); |
|
return vc_cec_send_message(follower, |
|
tx_buf, |
|
sizeof(tx_buf), |
|
is_reply); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_send_Standby |
|
* |
|
* Arguments: |
|
* follower, reply or not |
|
* |
|
* Description |
|
* send <Standby>. Turn other devices to standby |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* Tx callback if successful |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_send_Standby(uint32_t follower, vcos_bool_t is_reply) { |
|
uint8_t tx_buf[1]; |
|
tx_buf[0] = CEC_Opcode_Standby; // <Standby> |
|
return vc_cec_send_message(follower, |
|
tx_buf, |
|
sizeof(tx_buf), |
|
is_reply); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_send_MenuStatus |
|
* |
|
* Arguments: |
|
* follower, menu state, reply or not |
|
* |
|
* Description |
|
* send <Menu Status> (response to <Menu Request>) |
|
* menu state is either CEC_MENU_STATE_ACTIVATED or CEC_MENU_STATE_DEACTIVATED |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* Tx callback if successful |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_send_MenuStatus(uint32_t follower, |
|
CEC_MENU_STATE_T menu_state, |
|
vcos_bool_t is_reply) { |
|
uint8_t tx_buf[2]; |
|
if(!vcos_verify(menu_state < CEC_MENU_STATE_QUERY)) |
|
return -1; |
|
|
|
tx_buf[0] = CEC_Opcode_MenuStatus; // <Menu Status> |
|
tx_buf[1] = menu_state; |
|
return vc_cec_send_message(follower, |
|
tx_buf, |
|
sizeof(tx_buf), |
|
is_reply); |
|
} |
|
|
|
/*********************************************************** |
|
* Name: vc_cec_send_ReportPhysicalAddress |
|
* |
|
* Arguments: |
|
* physical address, device type, reply or not |
|
* |
|
* Description |
|
* send <Report Physical Address> (first command to be |
|
* sent after a successful logical address allocation |
|
* device type should be the appropriate one for |
|
* the allocated logical address |
|
* |
|
* Returns: if the command is successful (zero) or not (non-zero) |
|
* Tx callback if successful. We also get a failure |
|
* if we do not currently have a valid physical address |
|
***********************************************************/ |
|
VCHPRE_ int VCHPOST_ vc_cec_send_ReportPhysicalAddress(uint16_t physical_address, |
|
CEC_DEVICE_TYPE_T device_type, |
|
vcos_bool_t is_reply) { |
|
uint8_t tx_buf[4]; |
|
if(vcos_verify(physical_address == cecservice_client.physical_address && |
|
cecservice_client.physical_address != CEC_CLEAR_ADDR)) { |
|
tx_buf[0] = CEC_Opcode_ReportPhysicalAddress; |
|
tx_buf[1] = physical_address >> 8; // physical address msb |
|
tx_buf[2] = physical_address & 0x00FF; // physical address lsb |
|
tx_buf[3] = device_type; // 'device type' |
|
return vc_cec_send_message(CEC_BROADCAST_ADDR, // This is a broadcast only message |
|
tx_buf, |
|
sizeof(tx_buf), |
|
is_reply); |
|
} else { |
|
//Current we do not allow sending a random physical address |
|
vc_cec_log_error("CEC cannot send physical address 0x%X, does not match internal 0x%X", |
|
physical_address, cecservice_client.physical_address); |
|
return VC_CEC_ERROR_NO_PA; |
|
} |
|
}
|
|
|