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 <stdarg.h>
30#include <ctype.h>
31#include <assert.h>
32
33#include "vchost.h"
34
35#include "interface/vcos/vcos.h"
36#include "vcinclude/common.h"
37#include "vc_vchi_gencmd.h"
38#include "interface/vchi/vchi.h"
39#include "interface/vchi/common/endian.h"
40#include "interface/vmcs_host/vc_gencmd_defs.h"
41
42#ifdef HAVE_GENCMD_VERSION
43extern const char *gencmd_get_build_version(void);
44#error
45#endif
46
47/******************************************************************************
48Local types and defines.
49******************************************************************************/
50#define GENCMD_MAX_LENGTH 512
51typedef struct {
52 VCHI_SERVICE_HANDLE_T open_handle[VCHI_MAX_NUM_CONNECTIONS];
53 uint32_t msg_flag[VCHI_MAX_NUM_CONNECTIONS];
54 char command_buffer[GENCMD_MAX_LENGTH+1];
55 char response_buffer[GENCMDSERVICE_MSGFIFO_SIZE];
56 uint32_t response_length; //Length of response minus the error code
57 int num_connections;
58 VCOS_MUTEX_T lock;
59 int initialised;
60 VCOS_EVENT_T message_available_event;
61} GENCMD_SERVICE_T;
62
63static GENCMD_SERVICE_T gencmd_client;
64
65
66/******************************************************************************
67Static function.
68******************************************************************************/
69static void gencmd_callback( void *callback_param,
70 VCHI_CALLBACK_REASON_T reason,
71 void *msg_handle );
72
73static __inline int lock_obtain (void) {
74 int ret = -1;
75 if(gencmd_client.initialised && vcos_mutex_lock(&gencmd_client.lock) == VCOS_SUCCESS)
76 {
77 ret = 0;
78 }
79
80 return ret;
81}
82static __inline void lock_release (void) {
83 vcos_mutex_unlock(&gencmd_client.lock);
84}
85
86int use_gencmd_service(void) {
87 int ret = 0;
88 int i=0;
89 for(i = 0; i < gencmd_client.num_connections; i++) {
90 ret = (ret == 0) ? vchi_service_use(gencmd_client.open_handle[i]) : ret;
91 }
92 return ret;
93}
94
95int release_gencmd_service(void) {
96 int ret = 0;
97 int i=0;
98 for(i = 0; i < gencmd_client.num_connections; i++) {
99 ret = (ret == 0) ? vchi_service_release(gencmd_client.open_handle[i]) : ret;
100 }
101 return ret;
102}
103
104
105//call vc_vchi_gencmd_init to initialise
106int vc_gencmd_init() {
107 assert(0);
108 return 0;
109}
110
111/******************************************************************************
112NAME
113 vc_vchi_gencmd_init
114
115SYNOPSIS
116 void vc_vchi_gencmd_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections )
117
118FUNCTION
119 Initialise the general command service for use. A negative return value
120 indicates failure (which may mean it has not been started on VideoCore).
121
122RETURNS
123 int
124******************************************************************************/
125
126void vc_vchi_gencmd_init (VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections )
127{
128 VCOS_STATUS_T status;
129 int32_t success;
130 int i;
131
132 if (gencmd_client.initialised)
133 return;
134
135 // record the number of connections
136 memset( &gencmd_client, 0, sizeof(GENCMD_SERVICE_T) );
137 gencmd_client.num_connections = (int) num_connections;
138
139 status = vcos_mutex_create(&gencmd_client.lock, "HGencmd");
140 vcos_assert(status == VCOS_SUCCESS);
141 status = vcos_event_create(&gencmd_client.message_available_event, "HGencmd");
142 vcos_assert(status == VCOS_SUCCESS);
143
144 for (i=0; i<gencmd_client.num_connections; i++) {
145
146 // Create a 'LONG' service on the each of the connections
147 SERVICE_CREATION_T gencmd_parameters = { VCHI_VERSION(VC_GENCMD_VER),
148 MAKE_FOURCC("GCMD"), // 4cc service code
149 connections[i], // passed in fn ptrs
150 0, // tx fifo size (unused)
151 0, // tx fifo size (unused)
152 &gencmd_callback, // service callback
153 &gencmd_client.message_available_event, // callback parameter
154 VC_FALSE, // want_unaligned_bulk_rx
155 VC_FALSE, // want_unaligned_bulk_tx
156 VC_FALSE // want_crc
157 };
158
159 success = vchi_service_open( initialise_instance, &gencmd_parameters, &gencmd_client.open_handle[i] );
160 assert( success == 0 );
161 }
162
163 gencmd_client.initialised = 1;
164 release_gencmd_service();
165}
166
167/******************************************************************************
168NAME
169 gencmd_callback
170
171SYNOPSIS
172 void gencmd_callback( void *callback_param,
173 const VCHI_CALLBACK_REASON_T reason,
174 const void *msg_handle )
175
176FUNCTION
177 VCHI callback
178
179RETURNS
180 int
181******************************************************************************/
182static void gencmd_callback( void *callback_param,
183 const VCHI_CALLBACK_REASON_T reason,
184 void *msg_handle ) {
185
186 VCOS_EVENT_T *event = (VCOS_EVENT_T *)callback_param;
187
188 (void)msg_handle;
189 if ( reason != VCHI_CALLBACK_MSG_AVAILABLE || !event)
190 return;
191
192 vcos_event_signal(event);
193}
194
195/******************************************************************************
196NAME
197 vc_gencmd_stop
198
199SYNOPSIS
200 int vc_gencmd_stop()
201
202FUNCTION
203 This tells us that the generak command service has stopped, thereby preventing
204 any of the functions from doing anything.
205
206RETURNS
207 int
208******************************************************************************/
209
210void vc_gencmd_stop () {
211 // Assume a "power down" gencmd has been sent and the lock is held. There will
212 // be no response so this should be called instead.
213 int32_t success,i;
214
215 if (!gencmd_client.initialised)
216 return;
217
218 if(lock_obtain() == 0)
219 {
220 use_gencmd_service();
221
222 for(i = 0; i< (int32_t)gencmd_client.num_connections; i++) {
223 success = vchi_service_close( gencmd_client.open_handle[i]);
224 assert(success == 0);
225 }
226
227 gencmd_client.initialised = 0;
228
229 lock_release();
230
231 vcos_mutex_delete(&gencmd_client.lock);
232 vcos_event_delete(&gencmd_client.message_available_event);
233 }
234}
235
236/******************************************************************************
237NAME
238 vc_gencmd_send
239
240SYNOPSIS
241 int vc_gencmd_send( const char *format, ... )
242
243FUNCTION
244 Send a string to general command service.
245
246RETURNS
247 int
248******************************************************************************/
249int vc_gencmd_send_list ( const char *format, va_list a )
250{
251 int success = -1;
252
253 // Obtain the lock and keep it so no one else can butt in while we await the response.
254 if(lock_obtain() == 0)
255 {
256 int length = vsnprintf( gencmd_client.command_buffer, GENCMD_MAX_LENGTH, format, a );
257
258 if (length >= 0 && length < GENCMD_MAX_LENGTH)
259 {
260 int i;
261 use_gencmd_service();
262 for( i=0; i<gencmd_client.num_connections; i++ ) {
263 success = vchi_msg_queue( gencmd_client.open_handle[i],
264 gencmd_client.command_buffer,
265 length+1,
266 VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL );
267
268 if(success == 0)
269 { // only want to send on one connection, so break on success
270 break;
271 }
272 }
273 release_gencmd_service();
274 }
275
276 lock_release();
277 }
278
279 return success;
280}
281
282int vc_gencmd_send ( const char *format, ... )
283{
284 va_list a;
285 int rv;
286
287 va_start ( a, format );
288 rv = vc_gencmd_send_list( format, a );
289 va_end ( a );
290 return rv;
291}
292
293/******************************************************************************
294NAME
295 vc_gencmd_read_response
296
297SYNOPSIS
298 int vc_gencmd_read_response
299
300FUNCTION
301 Block until something comes back
302
303RETURNS
304 Error code from dequeue message
305******************************************************************************/
306int vc_gencmd_read_response (char *response, int maxlen) {
307 int i = 0;
308 int success = -1;
309 int ret_code = 0;
310 int32_t sem_ok = 0;
311
312 if(lock_obtain() == 0)
313 {
314 //Note this will ALWAYS reset response buffer and overwrite any partially read responses
315 use_gencmd_service();
316 do {
317 //TODO : we need to deal with messages coming through on more than one connections properly
318 //At the moment it will always try to read the first connection if there is something there
319 for(i = 0; i < gencmd_client.num_connections; i++) {
320 //Check if there is something in the queue, if so return immediately
321 //otherwise wait for the event and read again
322 success = (int) vchi_msg_dequeue( gencmd_client.open_handle[i], gencmd_client.response_buffer,
323 sizeof(gencmd_client.response_buffer), &gencmd_client.response_length, VCHI_FLAGS_NONE);
324 if(success == 0) {
325 ret_code = VC_VTOH32( *(int *)gencmd_client.response_buffer );
326 break;
327 } else {
328 gencmd_client.response_length = 0;
329 }
330 }
331 } while(!gencmd_client.response_length && vcos_event_wait(&gencmd_client.message_available_event) == VCOS_SUCCESS);
332
333 if(gencmd_client.response_length && sem_ok == 0) {
334 gencmd_client.response_length -= sizeof(int); //first word is error code
335 memcpy(response, gencmd_client.response_buffer+sizeof(int), (size_t) vcos_min((int)gencmd_client.response_length, (int)maxlen));
336 }
337
338 release_gencmd_service();
339 lock_release();
340 }
341
342 // If we read anything, return the VideoCore code. Error codes < 0 mean we failed to
343 // read anything...
344 //How do we let the caller know the response code of gencmd?
345 //return ret_code;
346
347 return success;
348}
349
350/******************************************************************************
351NAME
352 vc_gencmd
353
354SYNOPSIS
355 int vc_gencmd(char *response, int maxlen, const char *format, ...)
356
357FUNCTION
358 Send a gencmd and receive the response as per vc_gencmd read_response.
359
360RETURNS
361 int
362******************************************************************************/
363int vc_gencmd(char *response, int maxlen, const char *format, ...) {
364 va_list args;
365 int ret = -1;
366
367 use_gencmd_service();
368
369 va_start(args, format);
370 ret = vc_gencmd_send_list(format, args);
371 va_end (args);
372
373 if (ret >= 0) {
374 ret = vc_gencmd_read_response(response, maxlen);
375 }
376
377 release_gencmd_service();
378
379 return ret;
380}
381
382/******************************************************************************
383NAME
384 vc_gencmd_string_property
385
386SYNOPSIS
387 int vc_gencmd_string_property(char *text, char *property, char **value, int *length)
388
389FUNCTION
390 Given a text string, containing items of the form property=value,
391 look for the named property and return the value. The start of the value
392 is returned, along with its length. The value may contain spaces, in which
393 case it is enclosed in double quotes. The double quotes are not included in
394 the return parameters. Return non-zero if the property is found.
395
396RETURNS
397 int
398******************************************************************************/
399
400int vc_gencmd_string_property(char *text, const char *property, char **value, int *length) {
401#define READING_PROPERTY 0
402#define READING_VALUE 1
403#define READING_VALUE_QUOTED 2
404 int state = READING_PROPERTY;
405 int delimiter = 1, match = 0, len = (int)strlen(property);
406 char *prop_start=text, *value_start=text;
407 for (; *text; text++) {
408 int ch = *text;
409 switch (state) {
410 case READING_PROPERTY:
411 if (delimiter) prop_start = text;
412 if (isspace(ch)) delimiter = 1;
413 else if (ch == '=') {
414 delimiter = 1;
415 match = (text-prop_start==len && strncmp(prop_start, property, (size_t)(text-prop_start))==0);
416 state = READING_VALUE;
417 }
418 else delimiter = 0;
419 break;
420 case READING_VALUE:
421 if (delimiter) value_start = text;
422 if (isspace(ch)) {
423 if (match) goto success;
424 delimiter = 1;
425 state = READING_PROPERTY;
426 }
427 else if (delimiter && ch == '"') {
428 delimiter = 1;
429 state = READING_VALUE_QUOTED;
430 }
431 else delimiter = 0;
432 break;
433 case READING_VALUE_QUOTED:
434 if (delimiter) value_start = text;
435 if (ch == '"') {
436 if (match) goto success;
437 delimiter = 1;
438 state = READING_PROPERTY;
439 }
440 else delimiter = 0;
441 break;
442 }
443 }
444 if (match) goto success;
445 return 0;
446success:
447 *value = value_start;
448 *length = text - value_start;
449 return 1;
450}
451
452/******************************************************************************
453NAME
454 vc_gencmd_number_property
455
456SYNOPSIS
457 int vc_gencmd_number_property(char *text, char *property, int *number)
458
459FUNCTION
460 Given a text string, containing items of the form property=value,
461 look for the named property and return the numeric value. If such a numeric
462 value is successfully found, return 1; otherwise return 0.
463
464RETURNS
465 int
466******************************************************************************/
467
468int vc_gencmd_number_property(char *text, const char *property, int *number) {
469 char *value, temp;
470 int length, retval;
471 if (vc_gencmd_string_property(text, property, &value, &length) == 0)
472 return 0;
473 temp = value[length];
474 value[length] = 0;
475 /* coverity[secure_coding] - this is not insecure */
476 retval = sscanf(value, "0x%x", (unsigned int*)number);
477 if (retval != 1)
478 /* coverity[secure_coding] - this is not insecure */
479 retval = sscanf(value, "%d", number);
480 value[length] = temp;
481 return retval;
482
483}
484
485/******************************************************************************
486NAME
487 vc_gencmd_until
488
489SYNOPSIS
490 int vc_gencmd_until(const char *cmd, const char *error_string, int timeout);
491
492FUNCTION
493 Sends the command repeatedly, until one of the following situations occurs:
494 The specified response string is found within the gencmd response.
495 The specified error string is found within the gencmd response.
496 The timeout is reached.
497
498 The timeout is a rough value, do not use it for precise timing.
499
500RETURNS
501 0 if the requested response was detected.
502 1 if the error string is detected or the timeout is reached.
503******************************************************************************/
504int vc_gencmd_until( char *cmd,
505 const char *property,
506 char *value,
507 const char *error_string,
508 int timeout) {
509 char response[128];
510 int length;
511 char *ret_value;
512 int ret = 1;
513
514 use_gencmd_service();
515 for (;timeout > 0; timeout -= 10) {
516 vc_gencmd(response, (int)sizeof(response), cmd);
517 if (strstr(response,error_string)) {
518 ret = 1;
519 break;
520 }
521 else if (vc_gencmd_string_property(response, property, &ret_value, &length) &&
522 strncmp(value,ret_value,(size_t)length)==0) {
523 ret = 0;
524 break;
525 }
526 vcos_sleep(10);
527 }
528 release_gencmd_service();
529
530 return ret;
531}
532
533