1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27#include <string.h>
28#include <stdio.h>
29#include "vchost_platform_config.h"
30#include "vchost.h"
31
32#include "interface/vcos/vcos.h"
33#include "interface/vchi/vchi.h"
34#include "interface/vchi/common/endian.h"
35#include "interface/vchi/message_drivers/message.h"
36#include "vc_cecservice.h"
37#include "vc_service_common.h"
38
39/******************************************************************************
40Local types and defines.
41******************************************************************************/
42#ifndef _min
43#define _min(x,y) (((x) <= (y))? (x) : (y))
44#endif
45#ifndef _max
46#define _max(x,y) (((x) >= (y))? (x) : (y))
47#endif
48
49//TV service host side state (mostly the same as Videocore side - TVSERVICE_STATE_T)
50typedef struct {
51 //Generic service stuff
52 VCHI_SERVICE_HANDLE_T client_handle[VCHI_MAX_NUM_CONNECTIONS]; //To connect to server on VC
53 VCHI_SERVICE_HANDLE_T notify_handle[VCHI_MAX_NUM_CONNECTIONS]; //For incoming notification
54 uint32_t msg_flag[VCHI_MAX_NUM_CONNECTIONS];
55 char command_buffer[CECSERVICE_MSGFIFO_SIZE];
56 char response_buffer[CECSERVICE_MSGFIFO_SIZE];
57 uint32_t response_length;
58 uint32_t notify_buffer[CECSERVICE_MSGFIFO_SIZE/sizeof(uint32_t)];
59 uint32_t notify_length;
60 uint32_t num_connections;
61 VCOS_MUTEX_T lock;
62 CECSERVICE_CALLBACK_T notify_fn;
63 void *notify_data;
64 int initialised;
65 int to_exit;
66
67 //CEC state, not much here
68 //Most things live on Videocore side
69 uint16_t physical_address; //16-bit packed physical address
70 CEC_DEVICE_TYPE_T logical_address; //logical address
71 VC_CEC_TOPOLOGY_T *topology; //16-byte aligned for the transfer
72
73} CECSERVICE_HOST_STATE_T;
74
75/******************************************************************************
76Static data.
77******************************************************************************/
78static CECSERVICE_HOST_STATE_T cecservice_client;
79static VCOS_EVENT_T cecservice_message_available_event;
80static VCOS_EVENT_T cecservice_notify_available_event;
81static VCOS_THREAD_T cecservice_notify_task;
82static uint32_t cecservice_log_initialised = 0;
83
84//Command strings - must match what's in vc_cecservice_defs.h
85static char* cecservice_command_strings[] = {
86 "register_cmd",
87 "register_all",
88 "deregister_cmd",
89 "deregister_all",
90 "send_msg",
91 "get_logical_addr",
92 "alloc_logical_addr",
93 "release_logical_addr",
94 "get_topology",
95 "set_vendor_id",
96 "set_osd_name",
97 "get_physical_addr",
98 "get_vendor_id",
99 "poll_addr",
100 "set_logical_addr",
101 "add_device",
102 "set_passive",
103 "end_of_list"
104};
105
106static const uint32_t max_command_strings = sizeof(cecservice_command_strings)/sizeof(char *);
107
108//Notification strings - must match vc_cec.h VC_CEC_NOTIFY_T
109static char* cecservice_notify_strings[] = {
110 "none",
111 "TX",
112 "RX",
113 "User Ctrl Pressed",
114 "User Ctrl Released",
115 "Vendor Remote Down",
116 "Vendor Remote Up",
117 "logical address",
118 "topology",
119 "logical address lost",
120 "???"
121};
122
123static const uint32_t max_notify_strings = sizeof(cecservice_notify_strings)/sizeof(char *);
124
125//Device type strings
126static char* cecservice_devicetype_strings[] = {
127 "TV",
128 "Rec",
129 "Reserved",
130 "Tuner",
131 "Playback",
132 "Audio",
133 "Switch",
134 "VidProc",
135 "8", "9", "10", "11", "12", "13", "14", "invalid"
136};
137
138static const uint32_t max_devicetype_strings = sizeof(cecservice_devicetype_strings)/sizeof(char *);
139
140/******************************************************************************
141Static functions.
142******************************************************************************/
143//Lock the host state
144static __inline int lock_obtain (void) {
145 VCOS_STATUS_T status = VCOS_EAGAIN;
146 if(cecservice_client.initialised && (status = vcos_mutex_lock(&cecservice_client.lock)) == VCOS_SUCCESS) {
147 if(cecservice_client.initialised) { // check service hasn't been closed while we were waiting for the lock.
148 vchi_service_use(cecservice_client.client_handle[0]);
149 return status;
150 } else {
151 vcos_mutex_unlock(&cecservice_client.lock);
152 vc_cec_log_error("CEC Service closed while waiting for lock");
153 return VCOS_EAGAIN;
154 }
155 }
156 vc_cec_log_error("CEC service failed to obtain lock, initialised:%d, lock status:%d",
157 cecservice_client.initialised, status);
158 return status;
159}
160
161//Unlock the host state
162static __inline void lock_release (void) {
163 if(cecservice_client.initialised) {
164 vchi_service_release(cecservice_client.client_handle[0]);
165 }
166 vcos_mutex_unlock(&cecservice_client.lock);
167}
168
169//Forward declarations
170static void cecservice_client_callback( void *callback_param,
171 VCHI_CALLBACK_REASON_T reason,
172 void *msg_handle );
173
174static void cecservice_notify_callback( void *callback_param,
175 VCHI_CALLBACK_REASON_T reason,
176 void *msg_handle );
177
178static int32_t cecservice_wait_for_reply(void *response, uint32_t max_length);
179
180static int32_t cecservice_wait_for_bulk_receive(void *buffer, uint32_t max_length);
181
182static int32_t cecservice_send_command( uint32_t command, const void *buffer, uint32_t length, uint32_t has_reply);
183
184static int32_t cecservice_send_command_reply( uint32_t command, void *buffer, uint32_t length,
185 void *response, uint32_t max_length);
186
187static void *cecservice_notify_func(void *arg);
188
189static void cecservice_logging_init(void);
190
191/******************************************************************************
192 Global data
193*****************************************************************************/
194VCOS_LOG_CAT_T cechost_log_category;
195
196/******************************************************************************
197CEC service API
198******************************************************************************/
199/******************************************************************************
200NAME
201 vc_vchi_cec_init
202
203SYNOPSIS
204 void vc_vchi_cec_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections )
205
206FUNCTION
207 Initialise the CEC service for use. A negative return value
208 indicates failure (which may mean it has not been started on VideoCore).
209
210RETURNS
211 int
212******************************************************************************/
213VCHPRE_ void VCHPOST_ vc_vchi_cec_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) {
214 int32_t success = -1;
215 VCOS_STATUS_T status;
216 VCOS_THREAD_ATTR_T attrs;
217 uint32_t i;
218
219 if (cecservice_client.initialised)
220 return;
221
222 vc_cec_log_info("Initialising CEC service");
223 // record the number of connections
224 vcos_memset( &cecservice_client, 0, sizeof(CECSERVICE_HOST_STATE_T) );
225 cecservice_client.num_connections = num_connections;
226 cecservice_client.physical_address = CEC_CLEAR_ADDR;
227 cecservice_client.logical_address = CEC_AllDevices_eUnRegistered;
228
229 status = vcos_mutex_create(&cecservice_client.lock, "HCEC");
230 vcos_assert(status == VCOS_SUCCESS);
231 status = vcos_event_create(&cecservice_message_available_event, "HCEC");
232 vcos_assert(status == VCOS_SUCCESS);
233 status = vcos_event_create(&cecservice_notify_available_event, "HCEC");
234 vcos_assert(status == VCOS_SUCCESS);
235
236 cecservice_client.topology = vcos_malloc_aligned(sizeof(VC_CEC_TOPOLOGY_T), 16, "HCEC topology");
237 vcos_assert(cecservice_client.topology);
238
239 for (i=0; i < cecservice_client.num_connections; i++) {
240
241 // Create a 'Client' service on the each of the connections
242 SERVICE_CREATION_T cecservice_parameters = { VCHI_VERSION(VC_CECSERVICE_VER),
243 CECSERVICE_CLIENT_NAME, // 4cc service code
244 connections[i], // passed in fn ptrs
245 0, // tx fifo size (unused)
246 0, // tx fifo size (unused)
247 &cecservice_client_callback,// service callback
248 &cecservice_message_available_event, // callback parameter
249 VC_FALSE, // want_unaligned_bulk_rx
250 VC_FALSE, // want_unaligned_bulk_tx
251 VC_FALSE, // want_crc
252 };
253
254 SERVICE_CREATION_T cecservice_parameters2 = { VCHI_VERSION(VC_CECSERVICE_VER),
255 CECSERVICE_NOTIFY_NAME, // 4cc service code
256 connections[i], // passed in fn ptrs
257 0, // tx fifo size (unused)
258 0, // tx fifo size (unused)
259 &cecservice_notify_callback,// service callback
260 &cecservice_notify_available_event, // callback parameter
261 VC_FALSE, // want_unaligned_bulk_rx
262 VC_FALSE, // want_unaligned_bulk_tx
263 VC_FALSE, // want_crc
264 };
265
266 //Create the client to normal CEC service
267 success = vchi_service_open( initialise_instance, &cecservice_parameters, &cecservice_client.client_handle[i] );
268 vcos_assert( success == 0 );
269 if(success)
270 vc_cec_log_error("Failed to connected to CEC service: %d", success);
271
272 //Create the client to the async CEC service (any CEC related notifications)
273 success = vchi_service_open( initialise_instance, &cecservice_parameters2, &cecservice_client.notify_handle[i] );
274 vcos_assert( success == 0 );
275
276 if(success)
277 vc_cec_log_error("Failed to connected to CEC async service: %d", success);
278
279 vchi_service_release(cecservice_client.client_handle[i]);
280 vchi_service_release(cecservice_client.notify_handle[i]);
281 }
282
283 //Create the notifier task
284 vcos_thread_attr_init(&attrs);
285 vcos_thread_attr_setstacksize(&attrs, 2048);
286 vcos_thread_attr_settimeslice(&attrs, 1);
287
288 //Initialise logging
289 cecservice_logging_init();
290
291 status = vcos_thread_create(&cecservice_notify_task, "HCEC Notify", &attrs, cecservice_notify_func, &cecservice_client);
292 vcos_assert(status == VCOS_SUCCESS);
293
294 cecservice_client.initialised = 1;
295 vc_cec_log_info("CEC service initialised");
296}
297
298/***********************************************************
299 * Name: vc_vchi_cec_stop
300 *
301 * Arguments:
302 * -
303 *
304 * Description: Stops the Host side part of CEC service
305 *
306 * Returns: -
307 *
308 ***********************************************************/
309VCHPRE_ void VCHPOST_ vc_vchi_cec_stop( void ) {
310 // Wait for the current lock-holder to finish before zapping TV service
311 uint32_t i;
312
313 if (!cecservice_client.initialised)
314 return;
315
316 if(lock_obtain() == 0)
317 {
318 void *dummy;
319 vchi_service_release(cecservice_client.client_handle[0]);
320 vc_cec_log_info("Stopping CEC service");
321 for (i=0; i < cecservice_client.num_connections; i++) {
322 int32_t result;
323 vchi_service_use(cecservice_client.client_handle[i]);
324 vchi_service_use(cecservice_client.notify_handle[i]);
325 result = vchi_service_close(cecservice_client.client_handle[i]);
326 vcos_assert( result == 0 );
327 result = vchi_service_close(cecservice_client.notify_handle[i]);
328 vcos_assert( result == 0 );
329 }
330 cecservice_client.initialised = 0;
331
332 lock_release();
333 cecservice_client.to_exit = 1;
334 vcos_event_signal(&cecservice_notify_available_event);
335 vcos_thread_join(&cecservice_notify_task, &dummy);
336 vcos_mutex_delete(&cecservice_client.lock);
337 vcos_event_delete(&cecservice_message_available_event);
338 vcos_event_delete(&cecservice_notify_available_event);
339 vcos_free(cecservice_client.topology);
340 vc_cec_log_info("CEC service stopped");
341 }
342}
343
344/***********************************************************
345 * Name: vc_cec_register_callaback
346 *
347 * Arguments:
348 * callback function, context to be passed when function is called
349 *
350 * Description: Register a callback function for all CEC notifications
351 *
352 * Returns: -
353 *
354 ***********************************************************/
355VCHPRE_ void VCHPOST_ vc_cec_register_callback(CECSERVICE_CALLBACK_T callback, void *callback_data) {
356 if(lock_obtain() == 0){
357 cecservice_client.notify_fn = callback;
358 cecservice_client.notify_data = callback_data;
359 vc_cec_log_info("CEC service registered callback 0x%x", (uint32_t) callback);
360 lock_release();
361 } else {
362 vc_cec_log_error("CEC service registered callback 0x%x failed", (uint32_t) callback);
363 }
364}
365
366/*********************************************************************************
367 *
368 * Static functions definitions
369 *
370 *********************************************************************************/
371//TODO: Might need to handle multiple connections later
372/***********************************************************
373 * Name: cecservice_client_callback
374 *
375 * Arguments: semaphore, callback reason and message handle
376 *
377 * Description: Callback when a message is available for CEC service
378 *
379 ***********************************************************/
380static void cecservice_client_callback( void *callback_param,
381 const VCHI_CALLBACK_REASON_T reason,
382 void *msg_handle ) {
383
384 VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param;
385
386 if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || event == NULL)
387 return;
388
389 vcos_event_signal(event);
390}
391
392/***********************************************************
393 * Name: cecservice_notify_callback
394 *
395 * Arguments: semaphore, callback reason and message handle
396 *
397 * Description: Callback when a message is available for CEC notify service
398 *
399 ***********************************************************/
400static void cecservice_notify_callback( void *callback_param,
401 const VCHI_CALLBACK_REASON_T reason,
402 void *msg_handle ) {
403 VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param;
404
405 if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || event == NULL)
406 return;
407
408 vcos_event_signal(event);
409}
410
411/***********************************************************
412 * Name: cecservice_wait_for_reply
413 *
414 * Arguments: response buffer, buffer length
415 *
416 * Description: blocked until something is in the buffer
417 *
418 * Returns zero if successful or error code of vchi otherwise (see vc_service_common_defs.h)
419 * If success, response is updated
420 *
421 ***********************************************************/
422static int32_t cecservice_wait_for_reply(void *response, uint32_t max_length) {
423 int32_t success = 0;
424 uint32_t length_read = 0;
425 do {
426 //TODO : we need to deal with messages coming through on more than one connections properly
427 //At the moment it will always try to read the first connection if there is something there
428 //Check if there is something in the queue, if so return immediately
429 //otherwise wait for the semaphore and read again
430 success = (int32_t) vchi2service_status(vchi_msg_dequeue( cecservice_client.client_handle[0], response, max_length, &length_read, VCHI_FLAGS_NONE ));
431 } while( length_read == 0 && vcos_event_wait(&cecservice_message_available_event) == VCOS_SUCCESS);
432 if(length_read) {
433 vc_cec_log_info("CEC service got reply %d bytes", length_read);
434 } else {
435 vc_cec_log_warn("CEC service wait for reply failed, error: %s",
436 vchi2service_status_string(success));
437 }
438
439 return success;
440}
441
442/***********************************************************
443 * Name: cecservice_wait_for_bulk_receive
444 *
445 * Arguments: response buffer, buffer length
446 *
447 * Description: blocked until bulk receive
448 *
449 * Returns error code of vchi
450 *
451 ***********************************************************/
452static int32_t cecservice_wait_for_bulk_receive(void *buffer, uint32_t max_length) {
453 if(!vcos_verify(buffer)) {
454 vc_cec_log_error("CEC: NULL buffer passed to wait_for_bulk_receive");
455 return -1;
456 }
457 return (int32_t) vchi2service_status(vchi_bulk_queue_receive( cecservice_client.client_handle[0],
458 buffer,
459 max_length,
460 VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE,
461 NULL ));
462}
463
464/***********************************************************
465 * Name: cecservice_send_command
466 *
467 * Arguments: command, parameter buffer, parameter legnth, has reply? (non-zero means yes)
468 *
469 * Description: send a command and optionally wait for its single value response (TV_GENERAL_RESP_T)
470 *
471 * Returns: < 0 if there is VCHI error, if tranmission is successful, value
472 * returned is the response from CEC server (which will be VC_CEC_ERROR_T (>= 0))
473 *
474 ***********************************************************/
475
476static int32_t cecservice_send_command( uint32_t command, const void *buffer, uint32_t length, uint32_t has_reply) {
477 VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)},
478 {buffer, length} };
479 int32_t success = 0;
480 int32_t response = -1;
481 vc_cec_log_info("CEC sending command %s length %d %s",
482 cecservice_command_strings[command], length,
483 (has_reply)? "has reply" : " no reply");
484 if(lock_obtain() == 0)
485 {
486 success = (int32_t) vchi2service_status(vchi_msg_queuev(cecservice_client.client_handle[0],
487 vector, sizeof(vector)/sizeof(vector[0]),
488 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL ));
489 if(success == VC_SERVICE_VCHI_SUCCESS && has_reply) {
490 //otherwise only wait for a reply if we ask for one
491 success = cecservice_wait_for_reply(&response, sizeof(response));
492 if(success == VC_SERVICE_VCHI_SUCCESS) {
493 response = VC_VTOH32(response);
494 } else {
495 response = success;
496 }
497 } else {
498 if(success != VC_SERVICE_VCHI_SUCCESS)
499 vc_cec_log_error("CEC failed to send command %s length %d, error: %s",
500 cecservice_command_strings[command], length,
501 vchi2service_status_string(success));
502 //No reply expected or failed to send, send the success code back instead
503 response = success;
504 }
505 lock_release();
506 }
507 return response;
508}
509
510/***********************************************************
511 * Name: cecservice_send_command_reply
512 *
513 * Arguments: command, parameter buffer, parameter legnth, reply buffer, buffer length
514 *
515 * Description: send a command and wait for its non-single value response (in a buffer)
516 *
517 * Returns: error code, host app is responsible to do endian translation
518 *
519 ***********************************************************/
520static int32_t cecservice_send_command_reply( uint32_t command, void *buffer, uint32_t length,
521 void *response, uint32_t max_length) {
522 VCHI_MSG_VECTOR_T vector[] = { {&command, sizeof(command)},
523 {buffer, length} };
524 int32_t success = VC_SERVICE_VCHI_VCHIQ_ERROR, ret = 0;
525
526 vc_cec_log_info("CEC sending command (with reply) %s length %d",
527 cecservice_command_strings[command], length);
528 if(lock_obtain() == 0)
529 {
530 if((ret = vchi_msg_queuev( cecservice_client.client_handle[0],
531 vector, sizeof(vector)/sizeof(vector[0]),
532 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL )) == VC_SERVICE_VCHI_SUCCESS) {
533 success = cecservice_wait_for_reply(response, max_length);
534 } else {
535 vc_cec_log_error("CEC failed to send command %s length %d, error code %d",
536 cecservice_command_strings[command], length, ret);
537 }
538 lock_release();
539 }
540 return success;
541}
542
543/***********************************************************
544 * Name: cecservice_notify_func
545 *
546 * Arguments: CEC service state
547 *
548 * Description: This is the notification task which receives all CEC
549 * service notifications
550 *
551 * Returns: does not return
552 *
553 ***********************************************************/
554static void *cecservice_notify_func(void *arg) {
555 int32_t success;
556 CECSERVICE_HOST_STATE_T *state = (CECSERVICE_HOST_STATE_T *) arg;
557
558 vc_cec_log_info("CEC service async thread started");
559 while(1) {
560 VCOS_STATUS_T status = vcos_event_wait(&cecservice_notify_available_event);
561 uint32_t cb_reason_str_idx = max_notify_strings - 1;
562 if(status != VCOS_SUCCESS || !state->initialised || state->to_exit)
563 break;
564
565 do {
566 uint32_t reason, param1, param2, param3, param4;
567 //Get all notifications in the queue
568 success = vchi_msg_dequeue( state->notify_handle[0], state->notify_buffer, sizeof(state->notify_buffer), &state->notify_length, VCHI_FLAGS_NONE );
569 if(success != 0 || state->notify_length < sizeof(uint32_t)*5 ) { //reason + 4x32-bit parameter
570 vcos_assert(state->notify_length == sizeof(uint32_t)*5);
571 break;
572 }
573
574 //if(lock_obtain() != 0)
575 // break;
576 //All notifications are of format: reason, param1, param2, param3, param4 (all 32-bit unsigned int)
577 reason = VC_VTOH32(state->notify_buffer[0]);
578 param1 = VC_VTOH32(state->notify_buffer[1]);
579 param2 = VC_VTOH32(state->notify_buffer[2]);
580 param3 = VC_VTOH32(state->notify_buffer[3]);
581 param4 = VC_VTOH32(state->notify_buffer[4]);
582 //lock_release();
583
584 //Store away physical/logical addresses
585 if(CEC_CB_REASON(reason) == VC_CEC_LOGICAL_ADDR) {
586 state->logical_address = (CEC_DEVICE_TYPE_T) param1;
587 state->physical_address = (uint16_t) (param2 & 0xFFFF);
588 }
589
590 switch(CEC_CB_REASON(reason)) {
591 case VC_CEC_NOTIFY_NONE:
592 cb_reason_str_idx = 0; break;
593 case VC_CEC_TX:
594 cb_reason_str_idx = 1; break;
595 case VC_CEC_RX:
596 cb_reason_str_idx = 2; break;
597 case VC_CEC_BUTTON_PRESSED:
598 cb_reason_str_idx = 3; break;
599 case VC_CEC_BUTTON_RELEASE:
600 cb_reason_str_idx = 4; break;
601 case VC_CEC_REMOTE_PRESSED:
602 cb_reason_str_idx = 5; break;
603 case VC_CEC_REMOTE_RELEASE:
604 cb_reason_str_idx = 6; break;
605 case VC_CEC_LOGICAL_ADDR:
606 cb_reason_str_idx = 7; break;
607 case VC_CEC_TOPOLOGY:
608 cb_reason_str_idx = 8; break;
609 case VC_CEC_LOGICAL_ADDR_LOST:
610 cb_reason_str_idx = 9; break;
611 }
612
613 vc_cec_log_info("CEC service callback [%s]: 0x%x, 0x%x, 0x%x, 0x%x",
614 cecservice_notify_strings[cb_reason_str_idx], param1, param2, param3, param4);
615
616 if(state->notify_fn) {
617 (*state->notify_fn)(state->notify_data, reason, param1, param2, param3, param4);
618 } else {
619 vc_cec_log_info("CEC service: No callback handler specified, callback [%s] swallowed",
620 cecservice_notify_strings[cb_reason_str_idx]);
621 }
622
623 } while(success == 0 && state->notify_length >= sizeof(uint32_t)*5); //read the next message if any
624 } //while (1)
625
626 if(state->to_exit)
627 vc_cec_log_info("CEC service async thread exiting");
628
629 return 0;
630}
631
632/***********************************************************
633 * Name: cecservice_logging_init
634 *
635 * Arguments: None
636 *
637 * Description: Initialise VCOS logging
638 *
639 * Returns: -
640 *
641 ***********************************************************/
642static void cecservice_logging_init() {
643 if(cecservice_log_initialised == 0) {
644 vcos_log_set_level(&cechost_log_category, VCOS_LOG_WARN);
645 vcos_log_register("cecservice-client", &cechost_log_category);
646 vc_cec_log_info("CEC HOST: log initialised");
647 cecservice_log_initialised = 1;
648 }
649}
650
651
652/***********************************************************
653 Actual CEC service API starts here
654***********************************************************/
655/***********************************************************
656 * Name: vc_cec_register_command (deprecated)
657 *
658 * Arguments:
659 *
660 * Description
661 *
662 * Returns: zero
663 ***********************************************************/
664VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_register_command(CEC_OPCODE_T opcode) {
665 return 0;
666}
667
668/***********************************************************
669 * Name: vc_cec_register_all (deprecated)
670 *
671 * Arguments:
672 *
673 * Description
674 *
675 * Returns: zero
676 ***********************************************************/
677VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_register_all( void ) {
678 return 0;
679}
680
681/***********************************************************
682 * Name: vc_cec_deregister_command (deprecated)
683 *
684 * Arguments:
685 *
686 * Description
687 *
688 * Returns: zero
689 ***********************************************************/
690VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_deregister_command(CEC_OPCODE_T opcode) {
691 return 0;
692}
693
694/***********************************************************
695 * Name: vc_cec_deregister_all (deprecated)
696 *
697 * Arguments:
698 *
699 * Description
700 *
701 * Returns: zero
702 ***********************************************************/
703VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_deregister_all( void ) {
704 return 0;
705}
706
707/***********************************************************
708 * Name: vc_cec_send_message
709 *
710 * Arguments:
711 * Follower's logical address
712 * Message payload WITHOUT the header byte (can be NULL)
713 * Payload length WITHOUT the header byte (can be zero)
714 * VC_TRUE if the message is a reply to an incoming message
715 * (For poll message set payload to NULL and length to zero)
716 *
717 * Description
718 * Remove all commands to be forwarded. This does not affect
719 * the button presses which are always forwarded
720 *
721 * Returns: if the command is successful (zero) or not (non-zero)
722 * If the command is successful, there will be a Tx callback
723 * in due course to indicate whether the message has been
724 * acknowledged by the recipient or not
725 ***********************************************************/
726VCHPRE_ int VCHPOST_ vc_cec_send_message(const uint32_t follower,
727 const uint8_t *payload,
728 uint32_t length,
729 vcos_bool_t is_reply) {
730 int success = -1;
731 CEC_SEND_MSG_PARAM_T param;
732 if(!vcos_verify(length <= CEC_MAX_XMIT_LENGTH))
733 return -1;
734
735 param.follower = VC_HTOV32(follower);
736 param.length = VC_HTOV32(length);
737 param.is_reply = VC_HTOV32(is_reply);
738 vcos_memset(param.payload, 0, sizeof(param.payload));
739 vc_cec_log_info("CEC service sending CEC message (%d->%d) (0x%02X) length %d%s",
740 cecservice_client.logical_address, follower,
741 (payload)? payload[0] : 0xFF, length, (is_reply)? " as reply" : "");
742
743 if(length > 0 && vcos_verify(payload)) {
744 char s[96] = {0}, *p = &s[0];
745 int i;
746 vcos_memcpy(param.payload, payload, _min(length, CEC_MAX_XMIT_LENGTH));
747 p += sprintf(p, "0x%02X", (cecservice_client.logical_address << 4) | (follower & 0xF));
748 for(i = 0; i < _min(length, CEC_MAX_XMIT_LENGTH); i++) {
749 p += sprintf(p, " %02X", payload[i]);
750 }
751 vc_cec_log_info("CEC message: %s", s);
752 }
753
754 success = cecservice_send_command( VC_CEC_SEND_MSG, &param, sizeof(param), 1);
755 return success;
756}
757
758/***********************************************************
759 * Name: vc_cec_get_logical_address
760 *
761 * Arguments:
762 * pointer to logical address
763 *
764 * Description
765 * Get the logical address, if one is being allocated
766 * 0xF (unregistered) will be returned
767 *
768 * Returns: if the command is successful (zero) or not (non-zero)
769 * logical_address is not modified if command failed
770 ***********************************************************/
771VCHPRE_ int VCHPOST_ vc_cec_get_logical_address(CEC_AllDevices_T *logical_address) {
772 uint32_t response;
773 int32_t success = cecservice_send_command_reply( VC_CEC_GET_LOGICAL_ADDR, NULL, 0,
774 &response, sizeof(response));
775 if(success == 0) {
776 *logical_address = (CEC_AllDevices_T)(VC_VTOH32(response) & 0xF);
777 vc_cec_log_info("CEC got logical address %d", *logical_address);
778 }
779 return success;
780}
781
782/***********************************************************
783 * Name: vc_cec_alloc_logical_address
784 *
785 * Arguments:
786 * None
787 *
788 * Description
789 * Start the allocation of a logical address. The host only
790 * needs to call this if the initial allocation failed
791 * (logical address being 0xF and physical address is NOT 0xFFFF
792 * from VC_CEC_LOGICAL_ADDR notification), or if the host explicitly
793 * released its logical address.
794 *
795 * Returns: if the command is successful (zero) or not (non-zero)
796 * If successful, there will be a callback notification
797 * VC_CEC_LOGICAL_ADDR. The host should wait for this before
798 * calling this function again.
799 ***********************************************************/
800VCHPRE_ int VCHPOST_ vc_cec_alloc_logical_address( void ) {
801 return cecservice_send_command( VC_CEC_ALLOC_LOGICAL_ADDR, NULL, 0, 0);
802}
803
804/***********************************************************
805 * Name: vc_cec_release_logical_address
806 *
807 * Arguments:
808 * None
809 *
810 * Description
811 * Release our logical address. This effectively disables CEC.
812 * The host will need to allocate a new logical address before
813 * doing any CEC calls (send/receive message, get topology, etc.).
814 *
815 * Returns: if the command is successful (zero) or not (non-zero)
816 * The host should get a callback VC_CEC_LOGICAL_ADDR with
817 * 0xF being the logical address and the current physical address.
818 ***********************************************************/
819VCHPRE_ int VCHPOST_ vc_cec_release_logical_address( void ) {
820 return cecservice_send_command( VC_CEC_RELEASE_LOGICAL_ADDR, NULL, 0, 0);
821}
822
823/***********************************************************
824 * Name: vc_cec_get_topology (deprecated)
825 *
826 * Arguments:
827 *
828 * Description
829 *
830 * Returns: if the command is successful (zero) or not (non-zero)
831 *
832 ***********************************************************/
833VCHPRE_ int VCOS_DEPRECATED("returns invalid result") VCHPOST_ vc_cec_get_topology( VC_CEC_TOPOLOGY_T* topology) {
834 int32_t success = -1;
835 vchi_service_use(cecservice_client.client_handle[0]);
836 success = cecservice_send_command( VC_CEC_GET_TOPOLOGY, NULL, 0, 1);
837 if(success == 0) {
838 success = cecservice_wait_for_bulk_receive(cecservice_client.topology, sizeof(VC_CEC_TOPOLOGY_T));
839 }
840 vchi_service_release(cecservice_client.client_handle[0]);
841 if(success == 0) {
842 int i;
843 cecservice_client.topology->active_mask = VC_VTOH16(cecservice_client.topology->active_mask);
844 cecservice_client.topology->num_devices = VC_VTOH16(cecservice_client.topology->num_devices);
845 vc_cec_log_info("CEC topology: mask=0x%x; #device=%d",
846 cecservice_client.topology->active_mask,
847 cecservice_client.topology->num_devices);
848 for(i = 0; i < 15; i++) {
849 cecservice_client.topology->device_attr[i] = VC_VTOH32(cecservice_client.topology->device_attr[i]);
850 }
851 vcos_memcpy(topology, cecservice_client.topology, sizeof(VC_CEC_TOPOLOGY_T));
852 }
853 return success;
854}
855
856/***********************************************************
857 * Name: vc_cec_set_vendor_id
858 *
859 * Arguments:
860 * 24-bit IEEE vendor id
861 *
862 * Description
863 * Set the response to <Give Device Vendor ID>
864 *
865 * Returns: if the command is successful (zero) or not (non-zero)
866 ***********************************************************/
867VCHPRE_ int VCHPOST_ vc_cec_set_vendor_id( uint32_t id ) {
868 uint32_t vendor_id = VC_HTOV32(id);
869 vc_cec_log_info("CEC setting vendor id to 0x%x", vendor_id);
870 return cecservice_send_command( VC_CEC_SET_VENDOR_ID, &vendor_id, sizeof(vendor_id), 0);
871}
872
873/***********************************************************
874 * Name: vc_cec_set_osd_name
875 *
876 * Arguments:
877 * OSD name (14 byte array)
878 *
879 * Description
880 * Set the response to <Give OSD Name>
881 *
882 * Returns: if the command is successful (zero) or not (non-zero)
883 ***********************************************************/
884VCHPRE_ int VCHPOST_ vc_cec_set_osd_name( const char* name ) {
885 vc_cec_log_info("CEC setting OSD name to %s", name);
886 return cecservice_send_command( VC_CEC_SET_OSD_NAME, name, OSD_NAME_LENGTH, 0);
887}
888
889/***********************************************************
890 * Name: vc_cec_get_physical_address
891 *
892 * Arguments:
893 * pointer to physical address (returned as 16-bit packed value)
894 *
895 * Description
896 * Get the physical address
897 *
898 * Returns: if the command is successful (zero) or not (non-zero)
899 * If failed, physical address argument will not be changed
900 * A physical address of 0xFFFF means CEC is not supported
901 ***********************************************************/
902VCHPRE_ int VCHPOST_ vc_cec_get_physical_address(uint16_t *physical_address) {
903 uint32_t response;
904 int32_t success = cecservice_send_command_reply( VC_CEC_GET_PHYSICAL_ADDR, NULL, 0,
905 &response, sizeof(response));
906 if(success == 0) {
907 *physical_address = (uint16_t)(VC_VTOH32(response) & 0xFFFF);
908 vc_cec_log_info("CEC got physical address: %d.%d.%d.%d",
909 (*physical_address >> 12), (*physical_address >> 8) & 0xF,
910 (*physical_address >> 4) & 0xF, (*physical_address) & 0xF);
911 }
912 return success;
913}
914
915
916/***********************************************************
917 * Name: vc_cec_get_vendor_id
918 *
919 * Arguments:
920 * logical address [in]
921 * pointer to 24-bit IEEE vendor id [out]
922 *
923 * Description
924 * Get the vendor ID of the device with the said logical address
925 * Application should send <Give Device Vendor ID> if vendor ID
926 * is not known (and register opcode <Device Vendor ID>)
927 *
928 * Returns: if the command is successful (zero) or not (non-zero)
929 * vendor ID is set to zero if unknown or 0xFFFFFF if
930 * device does not exist.
931 ***********************************************************/
932VCHPRE_ int VCHPOST_ vc_cec_get_vendor_id( const CEC_AllDevices_T logical_address, uint32_t *vendor_id) {
933 uint32_t log_addr = VC_HTOV32(logical_address);
934 uint32_t response;
935 int32_t success = cecservice_send_command_reply( VC_CEC_GET_VENDOR_ID, &log_addr, sizeof(log_addr),
936 &response, sizeof(response));
937 if(success == 0) {
938 vcos_assert(vendor_id);
939 *vendor_id = VC_VTOH32(response);
940 vc_cec_log_info("CEC got vendor id 0x%X", *vendor_id);
941 }
942 return success;
943}
944/***********************************************************
945 * Name: vc_cec_device_type
946 *
947 * Arguments:
948 * logical address [in]
949 *
950 * Description
951 * Get the default device type of a logical address
952 * Logical address 12 to 14 cannot be used
953 *
954 * Returns: For logical addresses 0-11 the default
955 * device type of that address will be returned
956 * logical address 12-14 will return "reserved" type.
957 *
958 ***********************************************************/
959VCHPRE_ CEC_DEVICE_TYPE_T VCHPOST_ vc_cec_device_type(const CEC_AllDevices_T logical_address) {
960 CEC_DEVICE_TYPE_T device_type = CEC_DeviceType_Invalid;
961 switch(logical_address) {
962 case CEC_AllDevices_eSTB1:
963 case CEC_AllDevices_eSTB2:
964 case CEC_AllDevices_eSTB3:
965 case CEC_AllDevices_eSTB4:
966 device_type = CEC_DeviceType_Tuner;
967 break;
968 case CEC_AllDevices_eDVD1:
969 case CEC_AllDevices_eDVD2:
970 case CEC_AllDevices_eDVD3:
971 device_type = CEC_DeviceType_Playback;
972 break;
973 case CEC_AllDevices_eRec1:
974 case CEC_AllDevices_eRec2:
975 case CEC_AllDevices_eRec3:
976 device_type = CEC_DeviceType_Rec;
977 break;
978 case CEC_AllDevices_eAudioSystem:
979 device_type = CEC_DeviceType_Audio;
980 break;
981 case CEC_AllDevices_eTV:
982 device_type = CEC_DeviceType_TV;
983 break;
984 case CEC_AllDevices_eRsvd3:
985 case CEC_AllDevices_eRsvd4:
986 case CEC_AllDevices_eFreeUse:
987 device_type = CEC_DeviceType_Reserved; //XXX: Are we allowed to use this?
988 break;
989 default:
990 vcos_assert(0); //Invalid
991 break;
992 }
993 return device_type;
994}
995
996/***********************************************************
997 * Name: vc_cec_send_message2
998 *
999 * Arguments:
1000 * pointer to encapsulated message
1001 *
1002 * Description
1003 * Call vc_cec_send_message above
1004 * messages are always sent as non-reply
1005 *
1006 * Returns: if the command is successful (zero) or not (non-zero)
1007 * If the command is successful, there will be a Tx callback
1008 * in due course to indicate whether the message has been
1009 * acknowledged by the recipient or not
1010 ***********************************************************/
1011VCHPRE_ int VCHPOST_ vc_cec_send_message2(const VC_CEC_MESSAGE_T *message) {
1012 if(vcos_verify(message)) {
1013 return vc_cec_send_message(message->follower,
1014 (message->length)?
1015 message->payload : NULL,
1016 message->length,
1017 VC_FALSE);
1018 } else {
1019 return -1;
1020 }
1021}
1022
1023/***********************************************************
1024 * Name: vc_cec_param2message
1025 *
1026 * Arguments:
1027 * arguments from CEC callback (reason, param1 to param4)
1028 * pointer to VC_CEC_MESSAGE_T
1029 *
1030 * Description
1031 * Turn the CEC_TX/CEC_RX/BUTTON_PRESS/BUTTON_RELEASE
1032 * callback parameters back into an encapsulated form
1033 *
1034 * Returns: zero normally
1035 * non-zero if something has gone wrong
1036 ***********************************************************/
1037VCHPRE_ int VCHPOST_ vc_cec_param2message( const uint32_t reason, const uint32_t param1,
1038 const uint32_t param2, const uint32_t param3,
1039 const uint32_t param4, VC_CEC_MESSAGE_T *message) {
1040 if(vcos_verify(message &&
1041 CEC_CB_REASON(reason) != VC_CEC_LOGICAL_ADDR &&
1042 CEC_CB_REASON(reason) != VC_CEC_TOPOLOGY)) {
1043 message->length = CEC_CB_MSG_LENGTH(reason) - 1; //Length is without the header byte
1044 message->initiator = CEC_CB_INITIATOR(param1);
1045 message->follower = CEC_CB_FOLLOWER(param1);
1046 if(message->length) {
1047 uint32_t tmp = param1 >> 8;
1048 vcos_memcpy(message->payload, &tmp, sizeof(uint32_t)-1);
1049 vcos_memcpy(message->payload+sizeof(uint32_t)-1, &param2, sizeof(uint32_t));
1050 vcos_memcpy(message->payload+sizeof(uint32_t)*2-1, &param3, sizeof(uint32_t));
1051 vcos_memcpy(message->payload+sizeof(uint32_t)*3-1, &param4, sizeof(uint32_t));
1052 } else {
1053 vcos_memset(message->payload, 0, sizeof(message->payload));
1054 }
1055 return 0;
1056 } else {
1057 return -1;
1058 }
1059}
1060
1061//Extra API if CEC is running in passive mode
1062
1063/***********************************************************
1064 * Name: vc_cec_poll_address
1065 *
1066 * Arguments:
1067 * logical address to try
1068 *
1069 * Description
1070 * Sets and polls a particular address to find out
1071 * its availability in the CEC network. Only available
1072 * when CEC is running in passive mode. The host can
1073 * only call this function during logical address allocation stage.
1074 *
1075 * Returns: 0 if poll is successful (address is occupied)
1076 * >0 if poll is unsuccessful (address is free if error code is VC_CEC_ERROR_NO_ACK)
1077 * <0 other (VCHI) errors
1078 ***********************************************************/
1079VCHPRE_ int VCHPOST_ vc_cec_poll_address(const CEC_AllDevices_T logical_address) {
1080 uint32_t log_addr = VC_HTOV32(logical_address);
1081 int32_t response = VC_CEC_ERROR_INVALID_ARGUMENT;
1082 int32_t success = -1;
1083 vc_cec_log_info("CEC polling address %d", logical_address);
1084 success = cecservice_send_command_reply( VC_CEC_POLL_ADDR, &log_addr, sizeof(log_addr),
1085 &response, sizeof(response));
1086 return (success == 0)? response : success;
1087}
1088
1089/***********************************************************
1090 * Name: vc_cec_set_logical_address
1091 *
1092 * Arguments:
1093 * logical address, device type, vendor id
1094 *
1095 * Description
1096 * sets the logical address, device type and vendor ID to be in use.
1097 * Only available when CEC is running in passive mode. It is the
1098 * responsibility of the host to make sure the logical address
1099 * is actually free (see vc_cec_poll_address). Physical address used
1100 * will be what is read from EDID and cannot be set.
1101 *
1102 * Returns: 0 if successful, non-zero otherwise
1103 ***********************************************************/
1104VCHPRE_ int VCHPOST_ vc_cec_set_logical_address(const CEC_AllDevices_T logical_address,
1105 const CEC_DEVICE_TYPE_T device_type,
1106 const uint32_t vendor_id) {
1107 CEC_SET_LOGICAL_ADDR_PARAM_T param = {VC_HTOV32(logical_address),
1108 VC_HTOV32(device_type),
1109 VC_HTOV32(vendor_id)};
1110 int32_t response = VC_CEC_ERROR_INVALID_ARGUMENT;
1111 int32_t success = VC_CEC_ERROR_INVALID_ARGUMENT;
1112 if(vcos_verify(logical_address <= CEC_AllDevices_eUnRegistered &&
1113 (device_type <= CEC_DeviceType_VidProc ||
1114 device_type == CEC_DeviceType_Invalid))) {
1115 vc_cec_log_info("CEC setting logical address to %d; device type %s; vendor 0x%X",
1116 logical_address,
1117 cecservice_devicetype_strings[device_type], vendor_id );
1118 success = cecservice_send_command_reply( VC_CEC_SET_LOGICAL_ADDR, &param, sizeof(param),
1119 &response, sizeof(response));
1120 } else {
1121 vc_cec_log_error("CEC invalid arguments for set_logical_address");
1122 }
1123 return (success == 0)? response : success;
1124}
1125
1126/***********************************************************
1127 * Name: vc_cec_add_device (deprecated)
1128 *
1129 * Arguments:
1130 *
1131 * Description
1132 *
1133 * Returns: 0 if successful, non-zero otherwise
1134 ***********************************************************/
1135VCHPRE_ int VCOS_DEPRECATED("has no effect") VCHPOST_ vc_cec_add_device(const CEC_AllDevices_T logical_address,
1136 const uint16_t physical_address,
1137 const CEC_DEVICE_TYPE_T device_type,
1138 vcos_bool_t last_device) {
1139 CEC_ADD_DEVICE_PARAM_T param = {VC_HTOV32(logical_address),
1140 VC_HTOV32(physical_address),
1141 VC_HTOV32(device_type),
1142 VC_HTOV32(last_device)};
1143 int32_t response = VC_CEC_ERROR_INVALID_ARGUMENT;
1144 int32_t success = VC_CEC_ERROR_INVALID_ARGUMENT;
1145 if(vcos_verify(logical_address <= CEC_AllDevices_eUnRegistered &&
1146 (device_type <= CEC_DeviceType_VidProc ||
1147 device_type == CEC_DeviceType_Invalid))) {
1148 vc_cec_log_info("CEC adding device %d (0x%X); device type %s",
1149 logical_address, physical_address,
1150 cecservice_devicetype_strings[device_type]);
1151 success = cecservice_send_command_reply( VC_CEC_ADD_DEVICE, &param, sizeof(param),
1152 &response, sizeof(response));
1153 } else {
1154 vc_cec_log_error("CEC invalid arguments for add_device");
1155 }
1156 return (success == 0)? response : success;
1157}
1158
1159/***********************************************************
1160 * Name: vc_cec_set_passive
1161 *
1162 * Arguments:
1163 * Enable/disable (TRUE to enable/ FALSE to disable)
1164 *
1165 * Description
1166 * Enable / disable CEC passive mode
1167 *
1168 * Returns: 0 if successful, non-zero otherwise
1169 ***********************************************************/
1170VCHPRE_ int VCHPOST_ vc_cec_set_passive(vcos_bool_t enabled) {
1171 uint32_t param = VC_HTOV32(enabled);
1172 int32_t response;
1173 int32_t success = cecservice_send_command_reply( VC_CEC_SET_PASSIVE, &param, sizeof(param),
1174 &response, sizeof(response));
1175 return (success == 0)? response : success;
1176}
1177
1178/***********************************************************
1179 API for some common CEC messages, uses the API above to
1180 actually send the message
1181***********************************************************/
1182
1183/***********************************************************
1184 * Name: vc_cec_send_FeatureAbort
1185 *
1186 * Arguments:
1187 * follower, rejected opcode, reject reason, reply or not
1188 *
1189 * Description
1190 * send <Feature Abort> for a received command
1191 *
1192 * Returns: if the command is successful (zero) or not (non-zero)
1193 * Tx callback if successful
1194 ***********************************************************/
1195VCHPRE_ int VCHPOST_ vc_cec_send_FeatureAbort(uint32_t follower,
1196 CEC_OPCODE_T opcode,
1197 CEC_ABORT_REASON_T reason) {
1198 uint8_t tx_buf[3];
1199 tx_buf[0] = CEC_Opcode_FeatureAbort; // <Feature Abort>
1200 tx_buf[1] = opcode;
1201 tx_buf[2] = reason;
1202 return vc_cec_send_message(follower,
1203 tx_buf,
1204 sizeof(tx_buf),
1205 VC_TRUE);
1206}
1207
1208/***********************************************************
1209 * Name: vc_cec_send_ActiveSource
1210 *
1211 * Arguments:
1212 * physical address, reply or not
1213 *
1214 * Description
1215 * send <Active Source>
1216 *
1217 * Returns: if the command is successful (zero) or not (non-zero)
1218 * Tx callback if successful
1219 ***********************************************************/
1220VCHPRE_ int VCHPOST_ vc_cec_send_ActiveSource(uint16_t physical_address,
1221 vcos_bool_t is_reply) {
1222 uint8_t tx_buf[3];
1223 tx_buf[0] = CEC_Opcode_ActiveSource; // <Active Source>
1224 tx_buf[1] = physical_address >> 8; // physical address msb
1225 tx_buf[2] = physical_address & 0x00FF; // physical address lsb
1226 return vc_cec_send_message(CEC_BROADCAST_ADDR, // This is a broadcast only message
1227 tx_buf,
1228 sizeof(tx_buf),
1229 is_reply);
1230}
1231
1232/***********************************************************
1233 * Name: vc_cec_send_ImageViewOn
1234 *
1235 * Arguments:
1236 * follower, reply or not
1237 * Description
1238 * send <Image View On>
1239 *
1240 * Returns: if the command is successful (zero) or not (non-zero)
1241 * Tx callback if successful
1242 ***********************************************************/
1243VCHPRE_ int VCHPOST_ vc_cec_send_ImageViewOn(uint32_t follower,
1244 vcos_bool_t is_reply) {
1245 uint8_t tx_buf[1];
1246 tx_buf[0] = CEC_Opcode_ImageViewOn; // <Image View On> no param required
1247 return vc_cec_send_message(follower,
1248 tx_buf,
1249 sizeof(tx_buf),
1250 is_reply);
1251}
1252
1253/***********************************************************
1254 * Name: vc_cec_send_SetOSDString
1255 *
1256 * Arguments:
1257 * follower, display control, string (char[13]), reply or not
1258 *
1259 * Description
1260 * send <Image View On>
1261 *
1262 * Returns: if the command is successful (zero) or not (non-zero)
1263 * Tx callback if successful
1264 ***********************************************************/
1265VCHPRE_ int VCHPOST_ vc_cec_send_SetOSDString(uint32_t follower,
1266 CEC_DISPLAY_CONTROL_T disp_ctrl,
1267 const char* string,
1268 vcos_bool_t is_reply) {
1269 uint8_t tx_buf[CEC_MAX_XMIT_LENGTH];
1270 tx_buf[0] = CEC_Opcode_SetOSDString; // <Set OSD String>
1271 tx_buf[1] = disp_ctrl;
1272 vcos_memset(&tx_buf[2], 0, sizeof(tx_buf)-2);
1273 vcos_memcpy(&tx_buf[2], string, _min(strlen(string), CEC_MAX_XMIT_LENGTH-2));
1274 return vc_cec_send_message(follower,
1275 tx_buf,
1276 sizeof(tx_buf),
1277 is_reply);
1278}
1279
1280/***********************************************************
1281 * Name: vc_cec_send_Standby
1282 *
1283 * Arguments:
1284 * follower, reply or not
1285 *
1286 * Description
1287 * send <Standby>. Turn other devices to standby
1288 *
1289 * Returns: if the command is successful (zero) or not (non-zero)
1290 * Tx callback if successful
1291 ***********************************************************/
1292VCHPRE_ int VCHPOST_ vc_cec_send_Standby(uint32_t follower, vcos_bool_t is_reply) {
1293 uint8_t tx_buf[1];
1294 tx_buf[0] = CEC_Opcode_Standby; // <Standby>
1295 return vc_cec_send_message(follower,
1296 tx_buf,
1297 sizeof(tx_buf),
1298 is_reply);
1299}
1300
1301/***********************************************************
1302 * Name: vc_cec_send_MenuStatus
1303 *
1304 * Arguments:
1305 * follower, menu state, reply or not
1306 *
1307 * Description
1308 * send <Menu Status> (response to <Menu Request>)
1309 * menu state is either CEC_MENU_STATE_ACTIVATED or CEC_MENU_STATE_DEACTIVATED
1310 *
1311 * Returns: if the command is successful (zero) or not (non-zero)
1312 * Tx callback if successful
1313 ***********************************************************/
1314VCHPRE_ int VCHPOST_ vc_cec_send_MenuStatus(uint32_t follower,
1315 CEC_MENU_STATE_T menu_state,
1316 vcos_bool_t is_reply) {
1317 uint8_t tx_buf[2];
1318 if(!vcos_verify(menu_state < CEC_MENU_STATE_QUERY))
1319 return -1;
1320
1321 tx_buf[0] = CEC_Opcode_MenuStatus; // <Menu Status>
1322 tx_buf[1] = menu_state;
1323 return vc_cec_send_message(follower,
1324 tx_buf,
1325 sizeof(tx_buf),
1326 is_reply);
1327}
1328
1329/***********************************************************
1330 * Name: vc_cec_send_ReportPhysicalAddress
1331 *
1332 * Arguments:
1333 * physical address, device type, reply or not
1334 *
1335 * Description
1336 * send <Report Physical Address> (first command to be
1337 * sent after a successful logical address allocation
1338 * device type should be the appropriate one for
1339 * the allocated logical address
1340 *
1341 * Returns: if the command is successful (zero) or not (non-zero)
1342 * Tx callback if successful. We also get a failure
1343 * if we do not currently have a valid physical address
1344 ***********************************************************/
1345VCHPRE_ int VCHPOST_ vc_cec_send_ReportPhysicalAddress(uint16_t physical_address,
1346 CEC_DEVICE_TYPE_T device_type,
1347 vcos_bool_t is_reply) {
1348 uint8_t tx_buf[4];
1349 if(vcos_verify(physical_address == cecservice_client.physical_address &&
1350 cecservice_client.physical_address != CEC_CLEAR_ADDR)) {
1351 tx_buf[0] = CEC_Opcode_ReportPhysicalAddress;
1352 tx_buf[1] = physical_address >> 8; // physical address msb
1353 tx_buf[2] = physical_address & 0x00FF; // physical address lsb
1354 tx_buf[3] = device_type; // 'device type'
1355 return vc_cec_send_message(CEC_BROADCAST_ADDR, // This is a broadcast only message
1356 tx_buf,
1357 sizeof(tx_buf),
1358 is_reply);
1359 } else {
1360 //Current we do not allow sending a random physical address
1361 vc_cec_log_error("CEC cannot send physical address 0x%X, does not match internal 0x%X",
1362 physical_address, cecservice_client.physical_address);
1363 return VC_CEC_ERROR_NO_PA;
1364 }
1365}
1366