1 /*
2 * Copyright (c) 2013, Texas Instruments Incorporated
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
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33 /*
34 * ======== sphenc1_stubs.c ========
35 * This file contains an implemenation of the ISPHENC1 interface for the
36 * speech encoder class of algorithms.
37 *
38 * These functions are the "client-side" of a "remote" implementation.
39 *
40 */
42 /* This define must precede inclusion of any xdc header files */
43 #define Registry_CURDESC ti_sdo_ce_speech1_sphenc1_desc
45 #include <xdc/std.h>
46 #include <xdc/runtime/Assert.h>
47 #include <xdc/runtime/Diags.h>
48 #include <xdc/runtime/Log.h>
49 #include <xdc/runtime/Registry.h>
51 #include <ti/sdo/ce/visa.h>
52 #include <ti/xdais/dm/isphenc1.h>
53 #include <ti/sdo/ce/osal/Memory.h>
55 #include <string.h> /* for memcpy and memset. */
57 #include "sphenc1.h"
58 #include "_sphenc1.h"
60 extern Registry_Desc ti_sdo_ce_speech1_sphenc1_desc;
63 static XDAS_Int32 process(ISPHENC1_Handle h, XDM1_SingleBufDesc *inBuf,
64 XDM1_SingleBufDesc *outBuf, ISPHENC1_InArgs *inArgs,
65 ISPHENC1_OutArgs *outArgs);
66 static XDAS_Int32 processAsync(ISPHENC1_Handle h, XDM1_SingleBufDesc *inBuf,
67 XDM1_SingleBufDesc *outBuf, ISPHENC1_InArgs *inArgs,
68 ISPHENC1_OutArgs *outArgs);
69 static XDAS_Int32 processWait(ISPHENC1_Handle h, XDM1_SingleBufDesc *inBuf,
70 XDM1_SingleBufDesc *outBuf, ISPHENC1_InArgs *inArgs,
71 ISPHENC1_OutArgs *outArgs, UInt timeout);
72 static XDAS_Int32 control(ISPHENC1_Handle h, ISPHENC1_Cmd id,
73 ISPHENC1_DynamicParams *params, ISPHENC1_Status *status);
74 static XDAS_Int32 marshallMsg(ISPHENC1_Handle h, XDM1_SingleBufDesc *inBuf,
75 XDM1_SingleBufDesc *outBuf, ISPHENC1_InArgs *inArgs,
76 ISPHENC1_OutArgs *outArgs, _SPHENC1_Msg **pmsg);
77 static Void unmarshallMsg(ISPHENC1_Handle h, XDM1_SingleBufDesc *inBuf,
78 XDM1_SingleBufDesc *outBuf, ISPHENC1_InArgs *inArgs,
79 ISPHENC1_OutArgs *outArgs, _SPHENC1_Msg *msg);
82 ISPHENC1_Fxns SPHENC1_STUBS = {
83 {&SPHENC1_STUBS, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
84 process, control,
85 };
88 /*
89 * ======== marshallMsg ========
90 */
91 static XDAS_Int32 marshallMsg(ISPHENC1_Handle h, XDM1_SingleBufDesc *inBuf,
92 XDM1_SingleBufDesc *outBuf, ISPHENC1_InArgs *inArgs,
93 ISPHENC1_OutArgs *outArgs, _SPHENC1_Msg **pmsg)
94 {
95 XDAS_Int32 retVal = ISPHENC1_EOK;
96 VISA_Handle visa = (VISA_Handle)h;
97 _SPHENC1_Msg *msg;
98 ISPHENC1_OutArgs *pMsgOutArgs;
99 Int payloadSize;
101 /*
102 * Validate arguments. Do we want to do this _every_ time, or just in
103 * checked builds?
104 */
105 if ((inArgs == NULL) || (inArgs->size < sizeof(ISPHENC1_InArgs)) ||
106 (outArgs == NULL) || (outArgs->size < sizeof(ISPHENC1_OutArgs))) {
108 /* invalid args, could even assert here, it's a spec violation. */
109 return (SPHENC1_EFAIL);
110 }
112 if (pmsg == NULL) {
113 return (SPHENC1_EFAIL);
114 }
116 /* make sure it'll all fit! */
117 payloadSize = sizeof(VISA_MsgHeader) + sizeof(*inBuf) + sizeof(*outBuf) +
118 inArgs->size + outArgs->size;
120 if (payloadSize > VISA_getMaxMsgSize(visa)) {
121 /* Can't handle these large extended args. */
122 Log_print2(Diags_USER6,
123 "[+6] process> invalid arguments - too big (0x%x > 0x%x). "
124 "Validate .size fields", payloadSize,
125 VISA_getMaxMsgSize(visa));
127 return (SPHENC1_EUNSUPPORTED);
128 }
130 /* get a message appropriate for this algorithm */
131 if ((msg = (_SPHENC1_Msg *)VISA_allocMsg(visa)) == NULL) {
132 return (ISPHENC1_EFAIL);
133 }
135 /* zero out msg->cmd (not msg->visa!) */
136 memset(&(msg->cmd), 0, sizeof(msg->cmd));
138 /*
139 * Marshall the command: copy the client-passed arguments into flattened
140 * message data structures, converting every pointer address to alg.
141 * data buffer into physical address.
142 */
144 /* First specify the processing command that the skeleton should do */
145 msg->visa.cmd = _SPHENC1_CPROCESS;
147 /* commentary follows for marshalling the inBuf argument: */
149 /* 1) inBuf->bufSize is an integer, just copy it */
150 msg->cmd.process.inBuf.bufSize = inBuf->bufSize;
152 /*
153 * 2) inBuf->buf is a pointer, so we convert it and store it
154 * in the message
155 */
156 msg->cmd.process.inBuf.buf = (XDAS_Int8 *)
157 Memory_getBufferPhysicalAddress(inBuf->buf,
158 inBuf->bufSize, NULL);
160 if (msg->cmd.process.inBuf.buf == NULL) {
161 retVal = SPHENC1_EFAIL;
162 goto exit;
163 }
165 /* Clear .accessMask; the local processor won't access this buf */
166 inBuf->accessMask = 0;
168 /* we're done (with inBuf). Because msg->cmd.process is non-cacheable
169 * and contiguous (it has been allocated by MSGQ), we don't have to do
170 * anything else.
171 */
173 /* Now we repeat the procedure for outBuf. Note that
174 * inArgs and outArgs contain no pointers, so we can simply copy the
175 * entire original structure.
176 */
177 msg->cmd.process.outBuf.bufSize = outBuf->bufSize;
179 msg->cmd.process.outBuf.buf = (XDAS_Int8 *)
180 Memory_getBufferPhysicalAddress(outBuf->buf, outBuf->bufSize, NULL);
182 if (msg->cmd.process.outBuf.buf == NULL) {
183 retVal = SPHENC1_EFAIL;
184 goto exit;
185 }
187 /* Clear .accessMask; the local processor won't access this buf */
188 outBuf->accessMask = 0;
190 /* inArgs has a pointer. First, blindly copy all inArgs into the msg... */
191 memcpy(&(msg->cmd.process.inArgs), inArgs, inArgs->size);
193 /* ... then translate the single pointer if necessary */
194 if (inArgs->data.buf != NULL) {
195 msg->cmd.process.inArgs.data.buf = (XDAS_Int8 *)
196 Memory_getBufferPhysicalAddress(inArgs->data.buf,
197 inArgs->data.bufSize, NULL);
198 if (msg->cmd.process.inArgs.data.buf == NULL) {
199 retVal = ISPHENC1_EFAIL;
200 goto exit;
201 }
203 /* Clear .accessMask; the local processor won't access this buf */
204 inArgs->data.accessMask = 0;
205 }
207 /* point at outArgs and set the "size" */
208 pMsgOutArgs = (ISPHENC1_OutArgs *)((UInt)(&(msg->cmd.process.inArgs)) +
209 inArgs->size);
211 /* set the size field - the rest is filled in by the codec */
212 pMsgOutArgs->size = outArgs->size;
214 *pmsg = msg;
216 return retVal;
218 exit:
219 VISA_freeMsg(visa, (VISA_Msg)msg);
221 return retVal;
222 }
224 /*
225 * ======== unmarshallMsg ========
226 */
227 static Void unmarshallMsg(ISPHENC1_Handle h, XDM1_SingleBufDesc *inBuf,
228 XDM1_SingleBufDesc *outBuf, ISPHENC1_InArgs *inArgs,
229 ISPHENC1_OutArgs *outArgs, _SPHENC1_Msg *msg)
230 {
231 VISA_Handle visa = (VISA_Handle)h;
232 ISPHENC1_OutArgs *pMsgOutArgs;
234 /* Do a wholesale replace of skeleton returned structure. */
235 pMsgOutArgs = (ISPHENC1_OutArgs *)((UInt)(&(msg->cmd.process.inArgs)) +
236 inArgs->size);
238 if (VISA_isChecked()) {
239 /* ensure the codec didn't change outArgs->size */
240 Assert_isTrue(pMsgOutArgs->size == outArgs->size, (Assert_Id)NULL);
241 }
243 memcpy(outArgs, pMsgOutArgs, outArgs->size);
245 /* outBuf->bufSize is an _out_ field (set by the alg) so copy it here. */
246 outBuf->bufSize = msg->cmd.process.outBuf.bufSize;
248 /*
249 * Note, we can keep the original outBuf, since the caller's
250 * outBuf already contains the virtual buffer pointers.
251 *
252 * Note also that we did *nothing* with inBuf nor inArgs.
253 *
254 * Note also that we _don't_ update inBuf->accessMask
255 * or inArgs->data.accessMask.
256 * That's accurate, since the local processor _didn't_
257 * access these buffers.
258 */
260 VISA_freeMsg(visa, (VISA_Msg)msg);
262 return;
263 }
265 /*
266 * ======== process ========
267 * This is the sync stub-implementation for the process method
268 */
269 static XDAS_Int32 process(ISPHENC1_Handle h, XDM1_SingleBufDesc *inBuf,
270 XDM1_SingleBufDesc *outBuf, ISPHENC1_InArgs *inArgs,
271 ISPHENC1_OutArgs *outArgs)
272 {
273 XDAS_Int32 retVal;
274 _SPHENC1_Msg *msg;
275 VISA_Handle visa = (VISA_Handle)h;
277 retVal = marshallMsg(h, inBuf, outBuf, inArgs, outArgs, &msg);
278 if (retVal != ISPHENC1_EOK) {
279 return (retVal);
280 }
282 /* send the message to the skeleton and wait for completion */
283 retVal = VISA_call(visa, (VISA_Msg *)&msg);
285 /* Regardless of return value, unmarshall outArgs. */
286 unmarshallMsg(h, inBuf, outBuf, inArgs, outArgs, msg);
288 return (retVal);
289 }
291 /*
292 * ======== processAsync ========
293 * This is the async stub-implementation for the process method
294 */
295 static XDAS_Int32 processAsync(ISPHENC1_Handle h, XDM1_SingleBufDesc *inBuf,
296 XDM1_SingleBufDesc *outBuf, ISPHENC1_InArgs *inArgs,
297 ISPHENC1_OutArgs *outArgs)
298 {
299 XDAS_Int32 retVal;
300 _SPHENC1_Msg *msg;
301 VISA_Handle visa = (VISA_Handle)h;
303 retVal = marshallMsg(h, inBuf, outBuf, inArgs, outArgs, &msg);
304 if (retVal != ISPHENC1_EOK) {
305 return retVal;
306 }
308 /* send the message to the skeleton without waiting for completion */
309 retVal = VISA_callAsync(visa, (VISA_Msg *)&msg);
311 return (retVal);
312 }
314 /*
315 * ======== processWait ========
316 */
317 static XDAS_Int32 processWait(ISPHENC1_Handle h, XDM1_SingleBufDesc *inBuf,
318 XDM1_SingleBufDesc *outBuf, ISPHENC1_InArgs *inArgs,
319 ISPHENC1_OutArgs *outArgs, UInt timeout)
320 {
321 XDAS_Int32 retVal;
322 _SPHENC1_Msg *msg;
323 VISA_Handle visa = (VISA_Handle)h;
325 Assert_isTrue(!VISA_isLocal(visa), (Assert_Id)NULL);
327 /* wait for completion of "last" message */
328 retVal = VISA_wait(visa, (VISA_Msg *)&msg, timeout);
330 /* Unmarshall outArgs if there is a msg to unmarshall. */
331 if (msg != NULL) {
332 Assert_isTrue(msg->visa.cmd == _SPHENC1_CPROCESS, (Assert_Id)NULL);
334 unmarshallMsg(h, inBuf, outBuf, inArgs, outArgs, msg);
335 }
337 return (retVal);
338 }
340 /*
341 * ======== control ========
342 * This is the stub-implementation for the control method
343 */
344 static XDAS_Int32 control(ISPHENC1_Handle h, ISPHENC1_Cmd id,
345 ISPHENC1_DynamicParams *params, ISPHENC1_Status *status)
346 {
347 XDAS_Int32 retVal;
348 VISA_Handle visa = (VISA_Handle)h;
349 _SPHENC1_Msg *msg;
350 ISPHENC1_Status *pMsgStatus;
351 XDAS_Int8 *virtAddr = NULL;
352 Int payloadSize;
354 /*
355 * Validate arguments. Do we want to do this _every_ time, or just in
356 * checked builds?
357 */
358 if ((params == NULL) || (params->size < sizeof(ISPHENC1_DynamicParams)) ||
359 (status == NULL) || (status->size < sizeof(ISPHENC1_Status))) {
361 /* invalid args, could even assert here, it's a spec violation. */
362 return (SPHENC1_EFAIL);
363 }
365 /*
366 * Initialize extendedError to zero so we don't return something
367 * uninitialized in extendedError.
368 */
369 status->extendedError = 0;
371 /* make sure it'll all fit! */
372 payloadSize = sizeof(VISA_MsgHeader) + sizeof(id) + params->size +
373 status->size;
375 if (payloadSize > VISA_getMaxMsgSize(visa)) {
376 /* Can't handle these large extended args. */
377 Log_print2(Diags_USER6,
378 "[+6] control> invalid arguments - too big (0x%x > 0x%x). "
379 "Validate .size fields", payloadSize,
380 VISA_getMaxMsgSize(visa));
382 return (SPHENC1_EUNSUPPORTED);
383 }
385 /* get a message appropriate for this algorithm */
386 if ((msg = (_SPHENC1_Msg *)VISA_allocMsg(visa)) == NULL) {
387 return (ISPHENC1_EFAIL);
388 }
390 /* marshall the command */
391 msg->visa.cmd = _SPHENC1_CCONTROL;
393 msg->cmd.control.id = id;
395 /* params has no pointers so simply memcpy "size" bytes into the msg */
396 memcpy(&(msg->cmd.control.params), params, params->size);
398 /* unmarshall status based on the "size" of params */
399 pMsgStatus = (ISPHENC1_Status *)((UInt)(&(msg->cmd.control.params)) +
400 params->size);
402 /*
403 * Initialize the .size and .data fields - the rest are filled in by
404 * the codec.
405 */
406 pMsgStatus->size = status->size;
408 if (status->data.buf != NULL) {
409 pMsgStatus->data.bufSize = status->data.bufSize;
411 /* save it for later */
412 virtAddr = status->data.buf;
414 pMsgStatus->data.buf = (XDAS_Int8 *)
415 Memory_getBufferPhysicalAddress(status->data.buf,
416 status->data.bufSize, NULL);
418 if (pMsgStatus->data.buf == NULL) {
419 retVal = ISPHENC1_EFAIL;
420 goto exit;
421 }
422 }
423 else {
424 /* Place null into the msg so the skel knows it's invalid */
425 pMsgStatus->data.buf = NULL;
426 }
428 /* send the message to the skeleton and wait for completion */
429 retVal = VISA_call(visa, (VISA_Msg *)&msg);
431 /* ensure we get CCONTROL msg (ensure async CPROCESS pipeline drained) */
432 Assert_isTrue(msg->visa.cmd == _SPHENC1_CCONTROL, (Assert_Id)NULL);
434 /* unmarshall status */
435 pMsgStatus = (ISPHENC1_Status *)((UInt)(&(msg->cmd.control.params)) +
436 params->size);
438 if (VISA_isChecked()) {
439 /* ensure codec didn't modify status->size */
440 Assert_isTrue(pMsgStatus->size == status->size, (Assert_Id)NULL);
442 /*
443 * TODO:L Should we also check that pMsgStatus->data.buf is the same
444 * after the call as before?
445 */
446 }
448 memcpy(status, pMsgStatus, status->size);
450 /*
451 * And finally, restore status->data.buf to its original value. Note that
452 * this works even when status->data.buf was NULL because virtAddr is
453 * initialized to NULL.
454 *
455 * While potentially more confusing, this is just as correct as
456 * (and faster than!) calling Memory_getVirtualBuffer().
457 */
458 status->data.buf = virtAddr;
460 /* Clear .accessMask; the local processor didn't access the buffer */
461 status->data.accessMask = 0;
463 exit:
464 VISA_freeMsg(visa, (VISA_Msg)msg);
466 return (retVal);
467 }
469 /*
470 * ======== SPHENC1_processAsync ========
471 */
472 XDAS_Int32 SPHENC1_processAsync(SPHENC1_Handle handle,
473 XDM1_SingleBufDesc *inBuf, XDM1_SingleBufDesc *outBuf,
474 SPHENC1_InArgs *inArgs, SPHENC1_OutArgs *outArgs)
475 {
476 XDAS_Int32 retVal = SPHENC1_EFAIL;
478 /*
479 * Note, we do this because someday we may allow dynamically changing
480 * the global 'VISA_isChecked()' value on the fly. If we allow that,
481 * we need to ensure the value stays consistent in the context of this call.
482 */
483 Bool checked = VISA_isChecked();
485 Log_print5(Diags_ENTRY, "[+E] SPHENC1_processAsync> "
486 "Enter (handle=0x%x, inBuf=0x%x, outBuf=0x%x, inArgs=0x%x, "
487 "outArgs=0x%x)",
488 (IArg)handle, (IArg)inBuf, (IArg)outBuf, (IArg)inArgs,
489 (IArg)outArgs);
491 if (handle) {
492 ISPHENC1_Handle alg = VISA_getAlgHandle((VISA_Handle)handle);
494 if (alg != NULL) {
495 if (checked) {
497 #if 0 /* Most will complain at this. It's probably a different check level */
498 /* validate inArgs with ranges. */
499 if ((inArgs->nullTrafficChannel != ISPEECH1_NULLTRAFFICMODE_OFF)
500 && (inArgs->nullTrafficChannel !=
501 ISPEECH1_NULLTRAFFICMODE_ON)) {
503 Log_print2(Diags_USER7,
504 "[+7] ERROR> app provided codec (0x%x) with out of range "
505 "inArgs->nullTrafficChannel field (0x%x)",
506 (IArg)alg, (IArg)(inArgs->nullTrafficChannel));
507 }
508 #endif
510 /*
511 * Zero out the outArgs struct (except for .size field);
512 * it's write-only to the codec, so the app shouldn't pass
513 * values through it, nor should the codec expect to
514 * receive values through it.
515 */
516 memset((void *)((XDAS_Int32)(outArgs) + sizeof(outArgs->size)),
517 0, (sizeof(*outArgs) - sizeof(outArgs->size)));
518 }
520 retVal = processAsync(alg, inBuf, outBuf, inArgs, outArgs);
522 if (checked) {
523 #if 0 /* Most will complain at this. It's probably a different check level */
524 /* validate outArgs with ranges. */
525 if ((outArgs->frameType != ISPHENC1_FTYPE_SPEECH)
526 && (outArgs->frameType != ISPHENC1_FTYPE_SIDFRAME)
527 && (outArgs->frameType != ISPHENC1_FTYPE_NODATA)) {
529 Log_print2(Diags_USER7,
530 "[+7] ERROR> codec (0x%x) provided out of range "
531 "outArgs->frameType (0x%x)",
532 (IArg)alg, (IArg)(outArgs->frameType));
533 }
534 #endif
535 }
536 }
537 }
539 Log_print2(Diags_EXIT, "[+X] SPHENC1_processAsync> "
540 "Exit (handle=0x%x, retVal=0x%x)", (IArg)handle, (IArg)retVal);
542 return retVal;
543 }
546 /*
547 * ======== SPHENC1_processWait ========
548 */
549 XDAS_Int32 SPHENC1_processWait(SPHENC1_Handle handle,
550 XDM1_SingleBufDesc *inBuf, XDM1_SingleBufDesc *outBuf,
551 SPHENC1_InArgs *inArgs, SPHENC1_OutArgs *outArgs, UInt timeout)
552 {
553 XDAS_Int32 retVal = SPHENC1_EFAIL;
554 SPHENC1_InArgs refInArgs;
556 /*
557 * Note, we do this because someday we may allow dynamically changing
558 * the global 'VISA_isChecked()' value on the fly. If we allow that,
559 * we need to ensure the value stays consistent in the context of this call.
560 */
561 Bool checked = VISA_isChecked();
563 Log_print5(Diags_ENTRY, "[+E] SPHENC1_processWait> "
564 "Enter (handle=0x%x, inBuf=0x%x, outBuf=0x%x, inArgs=0x%x, "
565 "outArgs=0x%x)",
566 (IArg)handle, (IArg)inBuf, (IArg)outBuf, (IArg)inArgs,
567 (IArg)outArgs);
569 if (handle) {
570 ISPHENC1_Handle alg = VISA_getAlgHandle((VISA_Handle)handle);
572 if (alg != NULL) {
573 if (checked) {
574 /*
575 * Make a reference copy of inArgs so we can check that
576 * the codec didn't modify them during process().
577 */
578 refInArgs = *inArgs;
579 }
581 retVal = processWait(alg, inBuf, outBuf, inArgs, outArgs,
582 timeout);
584 if (checked) {
585 /* ensure the codec didn't modify the read-only inArgs */
586 if (memcmp(&refInArgs, inArgs, sizeof(*inArgs)) != 0) {
587 Log_print1(Diags_USER7,
588 "[+7] ERROR> codec (0x%x) modified read-only inArgs "
589 "struct!", (IArg)alg);
590 }
591 }
592 }
593 }
595 Log_print2(Diags_EXIT, "[+X] SPHENC1_processWait> "
596 "Exit (handle=0x%x, retVal=0x%x)", (IArg)handle, (IArg)retVal);
598 return retVal;
599 }