1 | /* |
2 | Copyright (c) 2012, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, 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 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON 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 |
25 | SOFTWARE, 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 |
43 | extern const char *gencmd_get_build_version(void); |
44 | #error |
45 | #endif |
46 | |
47 | /****************************************************************************** |
48 | Local types and defines. |
49 | ******************************************************************************/ |
50 | #define GENCMD_MAX_LENGTH 512 |
51 | typedef 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 | |
63 | static GENCMD_SERVICE_T gencmd_client; |
64 | |
65 | |
66 | /****************************************************************************** |
67 | Static function. |
68 | ******************************************************************************/ |
69 | static void gencmd_callback( void *callback_param, |
70 | VCHI_CALLBACK_REASON_T reason, |
71 | void *msg_handle ); |
72 | |
73 | static __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 | } |
82 | static __inline void lock_release (void) { |
83 | vcos_mutex_unlock(&gencmd_client.lock); |
84 | } |
85 | |
86 | int 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 | |
95 | int 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 |
106 | int vc_gencmd_init() { |
107 | assert(0); |
108 | return 0; |
109 | } |
110 | |
111 | /****************************************************************************** |
112 | NAME |
113 | vc_vchi_gencmd_init |
114 | |
115 | SYNOPSIS |
116 | void vc_vchi_gencmd_init(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections ) |
117 | |
118 | FUNCTION |
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 | |
122 | RETURNS |
123 | int |
124 | ******************************************************************************/ |
125 | |
126 | void 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 | /****************************************************************************** |
168 | NAME |
169 | gencmd_callback |
170 | |
171 | SYNOPSIS |
172 | void gencmd_callback( void *callback_param, |
173 | const VCHI_CALLBACK_REASON_T reason, |
174 | const void *msg_handle ) |
175 | |
176 | FUNCTION |
177 | VCHI callback |
178 | |
179 | RETURNS |
180 | int |
181 | ******************************************************************************/ |
182 | static 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 | /****************************************************************************** |
196 | NAME |
197 | vc_gencmd_stop |
198 | |
199 | SYNOPSIS |
200 | int vc_gencmd_stop() |
201 | |
202 | FUNCTION |
203 | This tells us that the generak command service has stopped, thereby preventing |
204 | any of the functions from doing anything. |
205 | |
206 | RETURNS |
207 | int |
208 | ******************************************************************************/ |
209 | |
210 | void 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 | /****************************************************************************** |
237 | NAME |
238 | vc_gencmd_send |
239 | |
240 | SYNOPSIS |
241 | int vc_gencmd_send( const char *format, ... ) |
242 | |
243 | FUNCTION |
244 | Send a string to general command service. |
245 | |
246 | RETURNS |
247 | int |
248 | ******************************************************************************/ |
249 | int 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 | |
282 | int 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 | /****************************************************************************** |
294 | NAME |
295 | vc_gencmd_read_response |
296 | |
297 | SYNOPSIS |
298 | int vc_gencmd_read_response |
299 | |
300 | FUNCTION |
301 | Block until something comes back |
302 | |
303 | RETURNS |
304 | Error code from dequeue message |
305 | ******************************************************************************/ |
306 | int 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 | /****************************************************************************** |
351 | NAME |
352 | vc_gencmd |
353 | |
354 | SYNOPSIS |
355 | int vc_gencmd(char *response, int maxlen, const char *format, ...) |
356 | |
357 | FUNCTION |
358 | Send a gencmd and receive the response as per vc_gencmd read_response. |
359 | |
360 | RETURNS |
361 | int |
362 | ******************************************************************************/ |
363 | int 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 | /****************************************************************************** |
383 | NAME |
384 | vc_gencmd_string_property |
385 | |
386 | SYNOPSIS |
387 | int vc_gencmd_string_property(char *text, char *property, char **value, int *length) |
388 | |
389 | FUNCTION |
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 | |
396 | RETURNS |
397 | int |
398 | ******************************************************************************/ |
399 | |
400 | int 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; |
446 | success: |
447 | *value = value_start; |
448 | *length = text - value_start; |
449 | return 1; |
450 | } |
451 | |
452 | /****************************************************************************** |
453 | NAME |
454 | vc_gencmd_number_property |
455 | |
456 | SYNOPSIS |
457 | int vc_gencmd_number_property(char *text, char *property, int *number) |
458 | |
459 | FUNCTION |
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 | |
464 | RETURNS |
465 | int |
466 | ******************************************************************************/ |
467 | |
468 | int 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 | /****************************************************************************** |
486 | NAME |
487 | vc_gencmd_until |
488 | |
489 | SYNOPSIS |
490 | int vc_gencmd_until(const char *cmd, const char *error_string, int timeout); |
491 | |
492 | FUNCTION |
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 | |
500 | RETURNS |
501 | 0 if the requested response was detected. |
502 | 1 if the error string is detected or the timeout is reached. |
503 | ******************************************************************************/ |
504 | int 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 | |