[ipc/ipcdev.git] / qnx / src / ipc3x_dev / ti / syslink / rpmsg-resmgr / hlos / knl / Qnx / rpmsg-resmgrdrv.c
1 /*
2 * @file rpmsg-resmgrdrv.c
3 *
4 * @brief driver for resmgr component.
5 *
6 * ============================================================================
7 *
8 * Copyright (c) 2011, Texas Instruments Incorporated
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * * Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * * Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * * Neither the name of Texas Instruments Incorporated nor the names of
22 * its contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
27 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
32 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 * Contact information for paper mail:
37 * Texas Instruments
38 * Post Office Box 655303
39 * Dallas, Texas 75265
40 * Contact information:
41 * http://www-k.ext.ti.com/sc/technical-support/product-information-centers.htm?
42 * DCMP=TIHomeTracking&HQS=Other+OT+home_d_contact
43 * ============================================================================
44 *
45 */
48 /* Standard headers */
49 #include <ti/syslink/Std.h>
51 /* OSAL & Utils headers */
52 #include <ti/syslink/utils/List.h>
53 #include <ti/syslink/utils/String.h>
54 #include <ti/syslink/utils/Trace.h>
55 #include <ti/syslink/utils/Memory.h>
56 #include <ti/syslink/utils/IGateProvider.h>
57 #include <ti/syslink/utils/GateSpinlock.h>
58 #include <ti/ipc/MultiProc.h>
59 #include <_MultiProc.h>
61 /*QNX specific header include */
62 #include <errno.h>
63 #include <unistd.h>
64 #include <sys/iofunc.h>
65 #include <sys/dispatch.h>
66 #include <sys/netmgr.h>
68 /* Module headers */
69 #include <ti/ipc/MessageQCopy.h>
70 #include <_MessageQCopy.h>
71 #include <_MessageQCopyDefs.h>
72 #include "OsalSemaphore.h"
73 #include "std_qnx.h"
74 #include <pthread.h>
76 #include "rpmsg_resmgr.h"
77 #if defined(SYSLINK_USE_IPU_PM)
78 #include "ipu_pm.h"
79 #endif
80 #include <SysLinkMemUtils.h>
82 #define MAX_PROCESSES 32u
84 /*!
85 * @brief Remote connection object
86 */
87 typedef struct rpmsg_resmgr_conn_object {
88 UInt32 addr;
89 /*!< Address of this remote connection */
90 UInt16 procId;
91 /*!< Remote procId */
92 List_Handle clients;
93 /*!< List of clients on this connection/remote proc */
94 UInt32 resRefCount;
95 /*!< Reference count for requested resources */
96 #if defined(USE_MEMMGR)
97 List_Handle tilerBufs;
98 /*!< List of tiler buffers allocated on behalf of remote proc */
99 #endif
100 } rpmsg_resmgr_conn_object;
103 /*!
104 * @brief rpmsg-resmgr Module state object
105 */
106 typedef struct rpmsg_resmgr_ModuleObject_tag {
107 Bool isSetup;
108 /*!< Indicates whether the module has been already setup */
109 IGateProvider_Handle gateHandle;
110 /*!< Handle of gate to be used for local thread safety */
111 rpmsg_resmgr_conn_object * objects [MultiProc_MAXPROCESSORS];
112 /*!< List of all remote connections. */
113 MessageQCopy_Handle mqHandle;
114 /*!< Local mq handle associated with this module */
115 UInt32 endpoint;
116 #if defined(USE_MEMMGR)
117 /*!< Local endpoint associated with the mq handle */
118 MessageQCopy_Handle mqMemHandle;
119 /*!< Local mq handle associated with this module */
120 UInt32 memEndpoint;
121 /*!< Local memMgr endpoint associated with the mq handle */
122 #endif
123 } rpmsg_resmgr_ModuleObject;
125 /*!
126 * @brief Structure of resource manager client information.
127 */
128 typedef struct rpmsg_resmgr_client_tag {
129 List_Elem element;
130 /*!< List element header */
131 UInt32 addr;
132 /*!< Remote addr for the client */
133 List_Handle resources;
134 /*!< List of acquired resources */
135 } rpmsg_resmgr_client ;
137 /*!
138 * @brief Structure of resource information.
139 */
140 typedef struct rpmsg_resmgr_res_tag {
141 List_Elem element;
142 /*!< List element header */
143 UInt32 res_type;
144 /* Type of resource */
145 Char data[];
146 /* Resource-specific request info */
147 } rpmsg_resmgr_res ;
149 /*!
150 * @brief Structure of resource information.
151 */
152 typedef struct rpmsg_resmgr_tiler_res_tag {
153 List_Elem element;
154 /*!< List element header */
155 UInt32 buf;
156 /* Allocated tiler address */
157 } rpmsg_resmgr_tiler_res ;
159 #if defined(SYSLINK_USE_IPU_PM)
160 #define IPU_PM_SELF 100
161 #define IPU_PM_MPU 101
162 #define IPU_PM_CORE 102
163 #endif
165 int rpmsg_resmgr_free(rpmsg_resmgr_conn_object * resmgr, UInt32 addr, struct rprm_request * request);
167 /** ============================================================================
168 * Globals
169 * ============================================================================
170 */
171 /*!
172 * @var rpmsg_resmgr_state
173 *
174 * @brief rpmsg-resmgr state object variable
175 */
176 static rpmsg_resmgr_ModuleObject rpmsg_resmgr_state =
177 {
178 .gateHandle = NULL,
179 .isSetup = FALSE,
180 };
182 extern dispatch_t * syslink_dpp;
184 rpmsg_resmgr_res * find_resource(rpmsg_resmgr_client * client, UInt32 handle)
185 {
186 List_Elem * elem = NULL;
187 rpmsg_resmgr_res * res = NULL;
189 /* Search the client list for this addr */
190 List_traverse(elem, client->resources) {
191 if ((UInt32)elem == handle) {
192 res = (rpmsg_resmgr_res *)elem;
193 break;
194 }
195 }
197 return res;
198 }
200 rpmsg_resmgr_client * find_client(List_Handle client_list, UInt32 addr)
201 {
202 List_Elem * elem = NULL;
203 rpmsg_resmgr_client * client = NULL;
205 /* Search the client list for this addr */
206 List_traverse(elem, client_list) {
207 if (((rpmsg_resmgr_client *)elem)->addr == addr) {
208 client = (rpmsg_resmgr_client *)elem;
209 break;
210 }
211 }
213 return client;
214 }
216 int rpmsg_resmgr_disconnect(rpmsg_resmgr_conn_object * resmgr, UInt32 addr)
217 {
218 int status = EOK;
219 IArg key = NULL;
220 rpmsg_resmgr_client * client = NULL;
222 /* Grab the gate */
223 key = IGateProvider_enter (rpmsg_resmgr_state.gateHandle);
224 /* Check if this addr already connected */
225 client = find_client(resmgr->clients, addr);
227 /* If found, remove from the list */
228 if (client) {
229 List_Elem * elem = NULL, * temp = NULL;
230 rpmsg_resmgr_res * res = NULL;
231 char msg[MessageQCopy_BUFSIZE];
232 struct rprm_request * req = (struct rprm_request *)msg;
233 struct rprm_free_data * fdata = (struct rprm_free_data *)req->data;
235 /* Free up resources that weren't properly freed */
236 List_traverse_safe(elem, temp, client->resources) {
237 res = (rpmsg_resmgr_res *)elem;
238 fdata->res_id = (uint32_t)res;
239 rpmsg_resmgr_free(resmgr, addr, req);
240 }
242 /* Delete the resource list */
243 List_delete(&client->resources);
244 List_remove(resmgr->clients, (List_Elem *)client);
245 Memory_free (NULL, client, sizeof(*client));
246 status = 0;
247 }
248 else {
249 status = -ENOTCONN;
250 }
252 /* Release the gate */
253 IGateProvider_leave (rpmsg_resmgr_state.gateHandle, key);
255 return status;
256 }
258 int rpmsg_resmgr_constraints_rel(rpmsg_resmgr_conn_object * resmgr, UInt32 addr, struct rprm_request * request)
259 {
260 int status = EOK;
261 IArg key = NULL;
262 UInt32 tmp_rsrc;
263 rpmsg_resmgr_client * client = NULL;
264 struct rprm_constraint * cdata = (struct rprm_constraint *)(request->data);
265 #if defined(SYSLINK_USE_IPU_PM)
266 struct ipu_pm_const_req req;
267 #endif
269 /* Grab the gate */
270 key = IGateProvider_enter (rpmsg_resmgr_state.gateHandle);
271 /* Check if this client is connected */
272 client = find_client(resmgr->clients, addr);
274 /* If client found, handle the request */
275 if (client) {
276 /* Find this resource in the resource list */
277 rpmsg_resmgr_res * resource = find_resource(client, cdata->res_id);
278 if (resource) {
279 if (cdata->data.mask & RPRM_SCALE) {
280 /* This is a frequency request */
281 switch (resource->res_type) {
282 case RPRM_IPU:
283 case RPRM_ISS:
284 case RPRM_FDIF:
285 #if defined(SYSLINK_USE_IPU_PM)
286 tmp_rsrc = IPU_PM_CORE;
287 #endif
288 break;
289 default:
290 tmp_rsrc = resource->res_type;
291 break;
292 }
293 #if defined(SYSLINK_USE_IPU_PM)
294 req.rsrc = resource->res_type;
295 req.target_rsrc = tmp_rsrc;
296 req.rate = NO_FREQ_CONSTRAINT;
297 status = ipu_pm_set_rate(&req);
298 #endif
299 }
300 if (cdata->data.mask & RPRM_LATENCY) {
301 /* This is a latency request */
302 /* TODO: handle the latency request */
303 }
304 if (cdata->data.mask & RPRM_BANDWIDTH) {
305 /* This is a bandwidth request */
306 if(resource->res_type == RPRM_IPU) {
307 GT_1trace(curTrace, GT_3CLASS, "###L3 BANDWIDTH CONSTRAINTS release(%d)", cdata->data.bandwidth);
308 #if defined(SYSLINK_USE_IPU_PM)
309 ipu_pm_set_bandwidth(0);
310 #endif
311 }
312 }
313 }
314 else {
315 status = -ENOENT;
316 }
317 }
318 else {
319 status = -ENOTCONN;
320 }
322 /* Release the gate */
323 IGateProvider_leave (rpmsg_resmgr_state.gateHandle, key);
324 return status;
325 }
327 int rpmsg_resmgr_constraints_req(rpmsg_resmgr_conn_object * resmgr, UInt32 addr, struct rprm_request * request)
328 {
329 int status = EOK;
330 IArg key = NULL;
331 UInt32 tmp_rsrc;
332 rpmsg_resmgr_client * client = NULL;
333 struct rprm_constraint * cdata = (struct rprm_constraint *)(request->data);
334 #if defined(SYSLINK_USE_IPU_PM)
335 struct ipu_pm_const_req req;
336 #endif
338 /* Grab the gate */
339 key = IGateProvider_enter (rpmsg_resmgr_state.gateHandle);
340 /* Check if this client is connected */
341 client = find_client(resmgr->clients, addr);
343 /* If client found, handle the request */
344 if (client) {
345 /* Find this resource in the resource list */
346 rpmsg_resmgr_res * resource = find_resource(client, cdata->res_id);
347 if (resource) {
348 if (cdata->data.mask & RPRM_SCALE) {
349 /* This is a frequency request */
350 switch (resource->res_type) {
351 case RPRM_IPU:
352 case RPRM_ISS:
353 case RPRM_FDIF:
354 #if defined(SYSLINK_USE_IPU_PM)
355 tmp_rsrc = IPU_PM_CORE;
356 #endif
357 break;
358 default:
359 tmp_rsrc = resource->res_type;
360 break;
361 }
362 #if defined(SYSLINK_USE_IPU_PM)
363 req.rsrc = resource->res_type;
364 req.target_rsrc = tmp_rsrc;
365 req.rate = cdata->data.frequency;
366 status = ipu_pm_set_rate(&req);
367 #endif
368 }
369 if (cdata->data.mask & RPRM_LATENCY) {
370 /* This is a latency request */
371 /* TODO: handle the latency request */
372 }
373 if (cdata->data.mask & RPRM_BANDWIDTH) {
374 /* This is a bandwidth request */
375 if(resource->res_type == RPRM_IPU) {
376 GT_1trace(curTrace, GT_3CLASS, "###L3 BANDWIDTH CONSTRAINTS req(%d)", cdata->data.bandwidth);
377 #if defined(SYSLINK_USE_IPU_PM)
378 ipu_pm_set_bandwidth(cdata->data.bandwidth);
379 #endif
380 }
381 }
382 }
383 else {
384 status = -ENOENT;
385 }
386 }
387 else {
388 status = -ENOTCONN;
389 }
391 /* Release the gate */
392 IGateProvider_leave (rpmsg_resmgr_state.gateHandle, key);
393 return status;
394 }
398 int rpmsg_resmgr_req_data(rpmsg_resmgr_conn_object * resmgr, UInt32 addr, struct rprm_request * request)
399 {
400 int status = EOK;
401 IArg key = NULL;
402 rpmsg_resmgr_client * client = NULL;
403 struct rprm_reqdata * rdata = (struct rprm_reqdata *)request->data;
405 /* Grab the gate */
406 key = IGateProvider_enter (rpmsg_resmgr_state.gateHandle);
407 /* Check if this client is connected */
408 client = find_client(resmgr->clients, addr);
410 /* If client found, alloc the resource */
411 if (client) {
412 rpmsg_resmgr_res * resource = find_resource(client, rdata->res_id);
413 if (resource) {
414 switch (rdata->type) {
415 case RPRM_REQTYPE_MAXFREQ:
416 {
417 #if defined(SYSLINK_USE_IPU_PM)
418 struct rprm_freq * fdata = (struct rprm_freq *)rdata->data;
419 status = ipu_pm_get_max_freq(resource->res_type, &fdata->value);
420 #else
421 status = -ENOENT;
422 #endif
423 }
424 break;
425 default:
426 status = -ENOENT;
427 break;
428 }
429 }
430 else {
431 status = -ENOENT;
432 }
433 }
434 else {
435 status = -ENOTCONN;
436 }
438 /* Release the gate */
439 IGateProvider_leave (rpmsg_resmgr_state.gateHandle, key);
440 return status;
441 }
443 /* Must be holding gate when calling this function */
444 int rpmsg_resmgr_free(rpmsg_resmgr_conn_object * resmgr, UInt32 addr, struct rprm_request * request)
445 {
446 int status = EOK;
447 struct rprm_free_data * fdata = (struct rprm_free_data *)request->data;
448 rpmsg_resmgr_client * client = NULL;
450 /* Check if this client is connected */
451 client = find_client(resmgr->clients, addr);
453 /* If client found, free the resource */
454 if (client) {
455 /* Find this resource in the resource list */
456 rpmsg_resmgr_res * resource = find_resource(client, fdata->res_id);
457 if (resource) {
458 /* Remove resource from list and free memory */
459 List_remove(client->resources, &resource->element);
460 switch (resource->res_type) {
461 case RPRM_IVAHD:
462 #if defined(SYSLINK_USE_IPU_PM)
463 status = ipu_pm_ivahd_disable();
464 #endif
465 break;
466 case RPRM_IVASEQ0:
467 #if defined(SYSLINK_USE_IPU_PM)
468 status = ipu_pm_ivaseq0_disable();
469 #endif
470 break;
471 case RPRM_IVASEQ1:
472 #if defined(SYSLINK_USE_IPU_PM)
473 status = ipu_pm_ivaseq1_disable();
474 #endif
475 break;
476 case RPRM_L3BUS:
477 /* TODO: handle RPRM_L3BUS free request */
478 break;
479 case RPRM_SL2IF:
480 /* TODO: handle SL2IF free request */
481 break;
482 case RPRM_IPU:
483 /* TODO: handle IPU free request */
484 break;
485 case RPRM_DSP:
486 /* TODO: handle DSP free request */
487 break;
488 case RPRM_SDMA:
489 {
490 #if defined(SYSLINK_USE_IPU_PM)
491 struct rprm_sdma * sdma = (struct rprm_sdma *)resource->data;
492 status = ipu_pm_free_sdma(sdma->num_chs, sdma->channels);
493 if (status < 0) {
494 status = -1;
495 GT_setFailureReason (curTrace,
496 GT_4CLASS,
497 "rpmsg_resmgr_free",
498 status,
499 "Failed to free DMA!");
500 }
501 #endif
502 }
503 break;
504 case RPRM_CAMERA:
505 /* Nothing to do. Always passes. */
506 break;
507 case RPRM_LED:
508 /* Nothing to do. Always passes. */
509 break;
510 default:
511 break;
512 }
513 /* Free the resource */
514 Memory_free(NULL, resource, sizeof(*resource));
515 fdata->res_id = 0;
516 }
517 else {
518 status = -ENOENT;
519 }
520 }
521 else {
522 status = -ENOTCONN;
523 }
525 if (status >= 0) {
526 resmgr->resRefCount--;
527 }
529 return status;
530 }
532 int rpmsg_resmgr_alloc(rpmsg_resmgr_conn_object * resmgr, UInt32 addr, struct rprm_request * request, uint32_t res_type)
533 {
534 int status = EOK;
535 IArg key = NULL;
536 rpmsg_resmgr_client * client = NULL;
537 #if defined(SYSLINK_USE_IPU_PM)
538 struct rprm_alloc_data * adata = (struct rprm_alloc_data *)request->data;
539 #endif
541 /* Grab the gate */
542 key = IGateProvider_enter (rpmsg_resmgr_state.gateHandle);
543 /* Check if this client is connected */
544 client = find_client(resmgr->clients, addr);
546 /* If client found, alloc the resource */
547 if (client) {
548 /* TODO: alloc the resource */
549 switch (res_type) {
550 case RPRM_IVAHD:
551 #if defined(SYSLINK_USE_IPU_PM)
552 status = ipu_pm_ivahd_enable();
553 if (status < 0) {
554 status = -1;
555 ipu_pm_ivahd_disable();
556 GT_setFailureReason (curTrace,
557 GT_4CLASS,
558 "rpmsg_resmgr_alloc",
559 status,
560 "Failed to enable IVAHD!");
561 }
562 #endif
563 break;
564 case RPRM_IVASEQ0:
565 #if defined(SYSLINK_USE_IPU_PM)
566 status = ipu_pm_ivaseq0_enable();
567 #endif
568 break;
569 case RPRM_IVASEQ1:
570 #if defined(SYSLINK_USE_IPU_PM)
571 status = ipu_pm_ivaseq1_enable();
572 #endif
573 break;
574 case RPRM_L3BUS:
575 /* TODO: handle RPRM_L3BUS alloc request */
576 break;
577 case RPRM_SL2IF:
578 /* TODO: handle SL2IF alloc request */
579 break;
580 case RPRM_IPU:
581 /* TODO: handle IPU alloc request */
582 break;
583 case RPRM_DSP:
584 /* TODO: handle DSP alloc request */
585 break;
586 case RPRM_SDMA:
587 {
588 #if defined(SYSLINK_USE_IPU_PM)
589 struct rprm_sdma * sdma = (struct rprm_sdma *)adata->data;
590 status = ipu_pm_alloc_sdma(sdma->num_chs, sdma->channels);
591 if (status < 0) {
592 status = -1;
593 GT_setFailureReason (curTrace,
594 GT_4CLASS,
595 "rpmsg_resmgr_alloc",
596 status,
597 "Failed to alloc DMA!");
598 }
599 #endif
600 }
601 break;
603 case RPRM_CAMERA:
604 {
605 #if defined(SYSLINK_USE_IPU_PM)
606 struct rprm_camera * cam = (struct rprm_camera *)adata->data;
607 status = ipu_pm_camera_enable(cam->mode, cam->on);
608 if (status < 0) {
609 status = -1;
610 GT_setFailureReason (curTrace,
611 GT_4CLASS,
612 "rpmsg_resmgr_alloc",
613 status,
614 "Failed to enable camera!");
615 }
616 #endif
617 }
618 break;
619 case RPRM_LED:
620 {
621 #if defined(SYSLINK_USE_IPU_PM)
622 struct rprm_led * led = (struct rprm_led *)adata->data;
623 led->intensity = ipu_pm_led_enable(led->mode, led->intensity);
624 if (led->intensity == -1) {
625 status = -1;
626 GT_setFailureReason (curTrace,
627 GT_4CLASS,
628 "rpmsg_resmgr_alloc",
629 status,
630 "Failed to enable led!");
631 }
632 #endif
633 }
634 break;
635 default:
636 status = -ENOENT;
637 break;
638 }
639 if (status >= 0) {
640 rpmsg_resmgr_res * resource = NULL;
641 /* store the resource per-client for tracking */
642 resource = Memory_alloc(NULL, sizeof (rpmsg_resmgr_res), 0x0, NULL);
643 if (resource) {
644 resource->res_type = res_type;
645 List_put(client->resources, &resource->element);
646 }
647 else {
648 status = -ENOMEM;
649 }
650 /* give the resource id back to the requester */
651 status = (int)resource;
652 }
653 }
654 else {
655 status = -ENOTCONN;
656 }
658 if (status >= 0) {
659 resmgr->resRefCount++;
660 }
661 /* Release the gate */
662 IGateProvider_leave (rpmsg_resmgr_state.gateHandle, key);
663 return status;
664 }
666 int rpmsg_resmgr_connect(rpmsg_resmgr_conn_object * resmgr, UInt32 addr)
667 {
668 int status = EOK;
669 IArg key = NULL;
670 rpmsg_resmgr_client * client = NULL;
671 List_Params listParams;
673 /* Grab the gate */
674 key = IGateProvider_enter (rpmsg_resmgr_state.gateHandle);
675 /* Check if this addr already connected */
676 client = find_client(resmgr->clients, addr);
678 /* Otherwise, add to the list */
679 if (!client) {
680 client = Memory_alloc (NULL, sizeof(*client), 0x0, NULL);
681 if (client) {
682 client->addr = addr;
683 /* create a list to store resources used by this client */
684 List_Params_init(&listParams);
685 client->resources = List_create(&listParams);
686 if (client->resources) {
687 List_put (resmgr->clients, &(client->element));
688 }
689 else {
690 Memory_free (NULL, client, sizeof(*client));
691 client = NULL;
692 status = -ENOMEM;
693 }
694 }
695 else {
696 status = -ENOMEM;
697 }
698 }
699 else {
700 status = -EISCONN;
701 }
702 /* Release the gate */
703 IGateProvider_leave (rpmsg_resmgr_state.gateHandle, key);
704 return status;
705 }
707 uint32_t rpmsg_resmgr_get_resp_len(uint32_t res_type)
708 {
709 uint32_t len = 0;
711 switch (res_type) {
712 case RPRM_GPTIMER:
713 len = sizeof(struct rprm_gpt);
714 break;
715 case RPRM_AUXCLK:
716 len = sizeof(struct rprm_auxclk);
717 break;
718 case RPRM_REGULATOR:
719 len = sizeof(struct rprm_regulator);
720 break;
721 case RPRM_GPIO:
722 len = sizeof(struct rprm_gpio);
723 break;
724 case RPRM_SDMA:
725 len = sizeof(struct rprm_sdma);
726 break;
727 case RPRM_IPU:
728 case RPRM_DSP:
729 len = sizeof(struct rprm_proc);
730 break;
731 case RPRM_I2C:
732 len = sizeof(struct rprm_i2c);
733 break;
734 case RPRM_CAMERA:
735 len = sizeof(struct rprm_camera);
736 break;
737 case RPRM_LED:
738 len = sizeof(struct rprm_led);
739 break;
740 default:
741 len = 0;
742 break;
743 }
745 return len;
746 }
748 uint32_t rpmsg_resmgr_get_req_data_len(uint32_t res_type)
749 {
750 uint32_t len = 0;
752 switch (res_type) {
753 case RPRM_REQTYPE_MAXFREQ:
754 len = sizeof(struct rprm_freq);
755 break;
756 default:
757 len = 0;
758 break;
759 }
761 return len;
762 }
764 uint32_t rpmsg_resmgr_get_req_len(uint32_t acquire)
765 {
766 uint32_t len = 0;
768 switch (acquire) {
769 case RPRM_REQ_ALLOC:
770 len = sizeof(struct rprm_alloc_data);
771 break;
772 case RPRM_REQ_FREE:
773 len = sizeof(struct rprm_free_data);
774 break;
775 case RPRM_REQ_CONSTRAINTS:
776 len = sizeof(struct rprm_constraint);
777 break;
778 case RPRM_REQ_DATA:
779 len = sizeof(struct rprm_reqdata);
780 break;
781 default:
782 break;
783 }
785 return len;
786 }
788 uint32_t rpmsg_resmgr_get_alloc_data_len(uint32_t res_type)
789 {
790 uint32_t len = 0;
792 switch (res_type) {
793 case RPRM_GPTIMER:
794 len = sizeof(struct rprm_gpt);
795 break;
796 case RPRM_AUXCLK:
797 len = sizeof(struct rprm_auxclk);
798 break;
799 case RPRM_REGULATOR:
800 len = sizeof(struct rprm_regulator);
801 break;
802 case RPRM_GPIO:
803 len = sizeof(struct rprm_gpio);
804 break;
805 case RPRM_SDMA:
806 len = sizeof(struct rprm_sdma);
807 break;
808 case RPRM_IPU:
809 case RPRM_DSP:
810 len = sizeof(struct rprm_proc);
811 break;
812 case RPRM_I2C:
813 len = sizeof(struct rprm_i2c);
814 break;
815 case RPRM_CAMERA:
816 len = sizeof(struct rprm_camera);
817 break;
818 case RPRM_LED:
819 len = sizeof(struct rprm_led);
820 break;
821 default:
822 break;
823 }
825 return len;
826 }
828 /*!
829 * @brief This function implements the callback registered with IPS. Here
830 * to pass event no. back to user function (so that it can do
831 * another level of demultiplexing of callbacks)
832 *
833 * @param procId processor Id from which interrupt is received
834 * @param lineId Interrupt line ID to be used
835 * @param eventId eventId registered
836 * @param arg argument to call back
837 * @param payload payload received
838 *
839 * @sa
840 */
841 Void
842 _rpmsg_resmgr_cb (MessageQCopy_Handle handle, void * data, int len, void * priv, UInt32 src, UInt16 srcProc)
843 {
844 #if !defined(SYSLINK_BUILD_OPTIMIZE)
845 Int32 status = 0;
846 #endif /* if !defined(SYSLINK_BUILD_OPTIMIZE) */
847 int err = EOK;
848 int i = 0;
849 rpmsg_resmgr_conn_object * resmgr_conn = NULL;
850 struct rprm_request * request = (struct rprm_request *)data;
851 char msg[MessageQCopy_BUFSIZE];
852 struct rprm_ack * response = (struct rprm_ack *)msg;
853 int resp_len = 0;
854 int req_len = 0;
855 IArg key = NULL;
856 Int res_type = -1;
857 struct rprm_alloc_data * adata = NULL;
858 struct rprm_ack_data *ack_data = (struct rprm_ack_data *)response->data;
859 struct rprm_reqdata * rdata = NULL;
861 GT_6trace (curTrace,
862 GT_ENTER,
863 "_rpmsg_resmgr_cb",
864 handle,
865 data,
866 len,
867 priv,
868 src,
869 srcProc);
871 if (len < sizeof(struct rprm_request)) {
872 /* Message is not large enough */
873 /* TODO: handle error */
874 return;
875 }
877 req_len = rpmsg_resmgr_get_req_len(request->acquire);
878 if (len < sizeof(struct rprm_request) + req_len) {
879 /* Message is not large enough */
880 /* TODO: handle error */
881 err = -EINVAL;
882 goto exit;
883 }
884 if (request->acquire == RPRM_REQ_ALLOC) {
885 adata = (struct rprm_alloc_data *)request->data;
886 res_type = rpmsg_resmgr_to_type(adata->res_name);
887 if (res_type != -1) {
888 if (res_type == -2) {
889 struct rprm_proc * proc = (struct rprm_proc *)adata->data;
890 if (len < sizeof(struct rprm_request) +
891 sizeof(struct rprm_proc) + req_len) {
892 /* Message is not large enough */
893 err = -EINVAL;
894 goto exit;
895 }
896 res_type = rpmsg_resmgr_to_type(proc->name);
897 }
898 req_len += rpmsg_resmgr_get_alloc_data_len(res_type);
899 if (len < sizeof(struct rprm_request) + req_len) {
900 /* Message is not large enough */
901 err = -EINVAL;
902 goto exit;
903 }
904 }
905 else {
906 err = -EINVAL;
907 goto exit;
908 }
909 }
910 else if (request->acquire == RPRM_REQ_DATA) {
911 rdata = (struct rprm_reqdata *)request->data;
912 res_type = rdata->type;
913 req_len += rpmsg_resmgr_get_req_data_len(res_type);
914 if (len < sizeof(struct rprm_request) + req_len) {
915 /* Message is not large enough */
916 err = -EINVAL;
917 goto exit;
918 }
919 }
921 for (i = 0; i < MultiProc_MAXPROCESSORS; i++) {
922 if (rpmsg_resmgr_state.objects[i] != NULL) {
923 if (rpmsg_resmgr_state.objects[i]->procId == srcProc)
924 break;
925 }
926 }
927 if (i == MultiProc_MAXPROCESSORS) {
928 err = -EINVAL;
929 goto exit;
930 }
931 resmgr_conn = (rpmsg_resmgr_conn_object *) rpmsg_resmgr_state.objects[i];
933 switch (request->acquire) {
934 case RPRM_REQ_ALLOC:
935 err = rpmsg_resmgr_alloc(resmgr_conn, src, request, res_type);
936 if (err > EOK) {
937 resp_len = rpmsg_resmgr_get_resp_len(res_type);
938 ack_data->res_id = err;
939 Memory_copy(ack_data->data, adata->data, resp_len);
940 resp_len += sizeof(*ack_data);
941 err = EOK;
942 }
943 break;
944 case RPRM_REQ_FREE:
945 /* Grab the gate */
946 key = IGateProvider_enter (rpmsg_resmgr_state.gateHandle);
947 err = rpmsg_resmgr_free(resmgr_conn, src, request);
948 /* Release the gate */
949 IGateProvider_leave (rpmsg_resmgr_state.gateHandle, key);
950 /* don't send a response in this case */
951 return;
952 case RPRM_REQ_CONSTRAINTS:
953 resp_len = sizeof(struct rprm_constraint);
954 err = rpmsg_resmgr_constraints_req(resmgr_conn, src, request);
955 break;
956 case RPRM_REL_CONSTRAINTS:
957 err = rpmsg_resmgr_constraints_rel(resmgr_conn, src, request);
958 /* don't send a response in this case */
959 return;
960 case RPRM_REQ_DATA:
961 resp_len = rpmsg_resmgr_get_req_data_len(rdata->type);
962 err = rpmsg_resmgr_req_data(resmgr_conn, src, request);
963 break;
964 default:
965 err = -EINVAL;
966 break;
967 }
969 exit:
970 /* Populate the response */
971 response->acquire = request->acquire;
972 response->ret = err;
974 /* Send the response to the remote core */
975 status = MessageQCopy_send(srcProc, MultiProc_self(), src,
976 rpmsg_resmgr_state.endpoint, response,
977 sizeof(*response) + resp_len,
978 TRUE);
979 if (status < 0) {
980 /* TODO: handle error */
981 }
983 GT_0trace (curTrace, GT_LEAVE, "_rpmsg_resmgr_cb");
984 }
986 #if defined(USE_MEMMGR)
987 /*
988 * MemMgr request sent to MemMgr server
989 */
991 typedef struct {
992 UInt32 reqType;
993 char args[];
994 } MemMgr_Req;
996 /*
997 * MemMgr response received from MemMgr server
998 */
1000 typedef struct {
1001 UInt32 result;
1002 } MemMgr_Resp;
1004 typedef enum {
1005 MemMgr_Function_ALLOC,
1006 MemMgr_Function_FREE
1007 } MemMgr_ReqType;
1009 /*!
1010 * @brief This function implements the callback registered with IPC. Here
1011 * to pass event no. back to user function (so that it can do
1012 * another level of demultiplexing of callbacks)
1013 *
1014 * @param procId processor Id from which interrupt is received
1015 * @param lineId Interrupt line ID to be used
1016 * @param eventId eventId registered
1017 * @param arg argument to call back
1018 * @param payload payload received
1019 *
1020 * @sa
1021 */
1022 Void
1023 _rpmsg_memmgr_cb (MessageQCopy_Handle handle, void * data, int len, void * priv, UInt32 src, UInt16 srcProc)
1024 {
1025 #if !defined(SYSLINK_BUILD_OPTIMIZE)
1026 Int32 status = 0;
1027 #endif /* if !defined(SYSLINK_BUILD_OPTIMIZE) */
1028 int err = EOK;
1029 int i = 0;
1030 rpmsg_resmgr_conn_object * resmgr_conn = NULL;
1031 MemMgr_Req * request = (MemMgr_Req *)data;
1032 char msg[MessageQCopy_BUFSIZE];
1033 MemMgr_Resp * response = (MemMgr_Resp *)msg;
1034 List_Elem * elem = NULL;
1035 rpmsg_resmgr_tiler_res * res = NULL;
1037 GT_6trace (curTrace,
1038 GT_ENTER,
1039 "_rpmsg_memmgr_cb",
1040 handle,
1041 data,
1042 len,
1043 priv,
1044 src,
1045 srcProc);
1047 if (len < sizeof(MemMgr_Req)) {
1048 /* Message is not large enough */
1049 /* TODO: handle error */
1050 return;
1051 }
1053 for (i = 0; i < MultiProc_MAXPROCESSORS; i++) {
1054 if (rpmsg_resmgr_state.objects[i] != NULL) {
1055 if (rpmsg_resmgr_state.objects[i]->procId == srcProc)
1056 break;
1057 }
1058 }
1059 if (i == MultiProc_MAXPROCESSORS) {
1060 /* TODO: handle error */
1061 return;
1062 }
1063 resmgr_conn = (rpmsg_resmgr_conn_object *) rpmsg_resmgr_state.objects[i];
1065 switch (request->reqType) {
1066 case MemMgr_Function_ALLOC:
1067 err = SysLinkMemUtils_alloc(len, (UInt32 *)request->args);
1068 if (err != 0) {
1069 res = Memory_alloc(NULL, sizeof (rpmsg_resmgr_tiler_res), 0x0, NULL);
1070 if (res) {
1071 res->buf = err;
1072 List_put(resmgr_conn->tilerBufs, &res->element);
1073 }
1074 else {
1075 err = 0;
1076 SysLinkMemUtils_free(len, (UInt32 *)&err);
1077 }
1078 }
1079 break;
1080 case MemMgr_Function_FREE:
1081 err = SysLinkMemUtils_free(len, (UInt32 *)request->args);
1082 /* Search the client list for this addr */
1083 List_traverse(elem, resmgr_conn->tilerBufs) {
1084 res = (rpmsg_resmgr_tiler_res *)elem;
1085 if (res->buf == *(UInt32 *)(request->args)) {
1086 break;
1087 }
1088 }
1089 if (res) {
1090 List_remove(resmgr_conn->tilerBufs, &res->element);
1091 }
1092 break;
1093 default:
1094 err = -EINVAL;
1095 break;
1096 }
1098 /* Populate the response */
1099 response->result = err;
1101 /* Send the response to the remote core */
1102 status = MessageQCopy_send(srcProc, MultiProc_self(), src, rpmsg_resmgr_state.memEndpoint, response, sizeof(*response), TRUE);
1103 if (status < 0) {
1104 /* TODO: handle error */
1105 }
1107 GT_0trace (curTrace, GT_LEAVE, "_rpmsg_memmgr_cb");
1108 }
1109 #endif
1111 static
1112 Void
1113 _rpmsg_resmgr_create_conn (UInt16 procId, UInt32 endpoint)
1114 {
1115 Int i = 0;
1116 Bool found = FALSE;
1117 rpmsg_resmgr_conn_object * obj = NULL;
1118 List_Params listParams;
1120 for (i = 0; i < MultiProc_MAXPROCESSORS; i++) {
1121 if (rpmsg_resmgr_state.objects[i] == NULL) {
1122 found = TRUE;
1123 break;
1124 }
1125 }
1127 if (found) {
1128 /* found a space to save this mq handle, allocate memory */
1129 obj = Memory_calloc (NULL, sizeof (rpmsg_resmgr_conn_object), 0x0, NULL);
1130 if (obj) {
1131 /* store the object in the module info */
1132 rpmsg_resmgr_state.objects[i] = obj;
1134 /* store the mq info in the object */
1135 obj->procId = procId;
1136 obj->addr = endpoint;
1138 /* create a list to store remote proc connections */
1139 List_Params_init(&listParams);
1140 obj->clients = List_create(&listParams);
1141 if (obj->clients == NULL) {
1142 rpmsg_resmgr_state.objects[i] = NULL;
1143 Memory_free (NULL, obj, sizeof (rpmsg_resmgr_conn_object));
1144 }
1145 else {
1146 /* create a list to store remote proc connections */
1147 List_Params_init(&listParams);
1148 #if defined(USE_MEMMGR)
1149 obj->tilerBufs = List_create(&listParams);
1150 if (obj->tilerBufs == NULL) {
1151 List_delete(&obj->clients);
1152 obj->clients = NULL;
1153 rpmsg_resmgr_state.objects[i] = NULL;
1154 Memory_free (NULL, obj, sizeof (rpmsg_resmgr_conn_object));
1155 }
1156 #endif
1157 }
1158 }
1159 }
1160 }
1163 static
1164 Void
1165 _rpmsg_resmgr_delete_conn (rpmsg_resmgr_conn_object * obj)
1166 {
1167 Int i = 0;
1168 Bool found = FALSE;
1170 for (i = 0; i < MultiProc_MAXPROCESSORS; i++) {
1171 if (rpmsg_resmgr_state.objects[i] == obj) {
1172 found = TRUE;
1173 break;
1174 }
1175 }
1177 if (found) {
1178 if (obj) {
1179 #if defined(USE_MEMMGR)
1180 if (obj->tilerBufs) {
1181 UInt32 buf = NULL;
1182 List_Elem * elem = NULL, * temp = NULL;
1183 /* Free up resources that weren't properly freed */
1184 List_traverse_safe(elem, temp, obj->tilerBufs) {
1185 buf = ((rpmsg_resmgr_tiler_res *)elem)->buf;
1186 SysLinkMemUtils_free(sizeof(Ptr), &buf);
1187 }
1188 List_delete(&obj->tilerBufs);
1189 }
1190 #endif
1191 if (obj->clients) {
1192 /* Check if there are still registered clients and
1193 disconnect them, thereby freeing the resources. */
1194 List_Elem * elem = NULL, * temp = NULL;
1195 rpmsg_resmgr_client * client = NULL;
1197 List_traverse_safe(elem, temp, obj->clients) {
1198 client = (rpmsg_resmgr_client *)elem;
1199 rpmsg_resmgr_disconnect(obj, client->addr);
1200 }
1202 List_delete(&obj->clients);
1203 }
1204 rpmsg_resmgr_state.objects[i] = NULL;
1205 Memory_free(NULL, obj, sizeof(rpmsg_resmgr_conn_object));
1206 }
1207 }
1208 }
1210 /**
1211 * Callback passed to MessageQCopy_registerNotify.
1212 *
1213 * This callback is called when a remote processor creates a MessageQCopy
1214 * handle with the same name as the local MessageQCopy handle and then
1215 * calls NameMap_register to notify the HOST of the handle.
1216 *
1217 * \param handle The remote handle.
1218 * \param procId The remote proc ID of the remote handle.
1219 * \param endpoint The endpoint address of the remote handle.
1220 * \param create The endpoint is created or deleted
1221 *
1222 * \return None.
1223 */
1225 static
1226 Void
1227 _rpmsg_resmgr_notify_cb (MessageQCopy_Handle handle, UInt16 procId,
1228 UInt32 endpoint, Char * desc, Bool create)
1229 {
1230 int status = EOK;
1231 Int i = 0;
1232 Bool found = FALSE;
1233 rpmsg_resmgr_conn_object * resmgr_conn = NULL;
1234 Char msg[MessageQCopy_BUFSIZE];
1235 struct rprm_ack * response = (struct rprm_ack *)msg;
1237 for (i = 0; i < MultiProc_MAXPROCESSORS; i++) {
1238 if (rpmsg_resmgr_state.objects[i] != NULL) {
1239 if (rpmsg_resmgr_state.objects[i]->procId == procId) {
1240 found = TRUE;
1241 break;
1242 }
1243 }
1244 }
1246 if (found) {
1247 resmgr_conn = (rpmsg_resmgr_conn_object *) rpmsg_resmgr_state.objects[i];
1249 if (create)
1250 status = rpmsg_resmgr_connect(resmgr_conn, endpoint);
1251 else {
1252 status = rpmsg_resmgr_disconnect(resmgr_conn, endpoint);
1253 // don't send a response in this case
1254 return;
1255 }
1256 }
1257 else {
1258 status = -EINVAL;
1259 }
1261 /* Populate the response */
1262 response->ret = status;
1264 /* Send the response to the remote core */
1265 status = MessageQCopy_send(procId, MultiProc_self(), endpoint,
1266 rpmsg_resmgr_state.endpoint, response,
1267 sizeof(*response), TRUE);
1268 if (status < 0) {
1269 /* TODO: handle error */
1270 }
1271 }
1273 Bool
1274 rpmsg_resmgr_allow_hib (UInt16 proc_id)
1275 {
1276 Bool allow = FALSE;
1277 int i = 0;
1278 rpmsg_resmgr_conn_object * resmgr_conn = NULL;
1280 for (i = 0; i < MultiProc_MAXPROCESSORS; i++) {
1281 if (rpmsg_resmgr_state.objects[i] != NULL) {
1282 if (rpmsg_resmgr_state.objects[i]->procId == proc_id)
1283 break;
1284 }
1285 }
1286 if (i < MultiProc_MAXPROCESSORS) {
1287 resmgr_conn = (rpmsg_resmgr_conn_object *) rpmsg_resmgr_state.objects[i];
1289 if (!resmgr_conn->resRefCount)
1290 allow = TRUE;
1291 }
1293 return allow;
1294 }
1296 /*!
1297 * @brief Module setup function.
1298 *
1299 * @sa rpmsg_resmgr_destroy
1300 */
1301 Int
1302 rpmsg_resmgr_setup (Void)
1303 {
1304 Int status = 0;
1305 UInt32 i = 0;
1306 Error_Block eb;
1308 GT_0trace (curTrace, GT_ENTER, "rpmsg_resmgr_setup");
1310 Error_init(&eb);
1312 rpmsg_resmgr_state.gateHandle = (IGateProvider_Handle)
1313 GateSpinlock_create ((GateSpinlock_Handle) NULL, &eb);
1314 #if !defined(SYSLINK_BUILD_OPTIMIZE)
1315 if (rpmsg_resmgr_state.gateHandle == NULL) {
1316 status = -ENOMEM;
1317 GT_setFailureReason (curTrace,
1318 GT_4CLASS,
1319 "_rpmsg_resmgr_setup",
1320 status,
1321 "Failed to create spinlock gate!");
1322 }
1323 else {
1324 #endif /* if !defined(SYSLINK_BUILD_OPTIMIZE) */
1325 rpmsg_resmgr_state.mqHandle = MessageQCopy_create (
1326 MessageQCopy_ADDRANY,
1327 "rpmsg-resmgr",
1328 _rpmsg_resmgr_cb,
1329 NULL,
1330 &rpmsg_resmgr_state.endpoint);
1331 if (rpmsg_resmgr_state.mqHandle == NULL) {
1332 /*! @retval ENOMEM Failed to register MQCopy handle! */
1333 status = -ENOMEM;
1334 GT_setFailureReason (curTrace,
1335 GT_4CLASS,
1336 "rpmsg_resmgr_setup",
1337 status,
1338 "Failed to create MessageQCopy handle!");
1339 }
1340 else {
1341 status = MessageQCopy_registerNotify(rpmsg_resmgr_state.mqHandle,
1342 _rpmsg_resmgr_notify_cb);
1343 if (status >= 0) {
1344 #if defined(USE_MEMMGR)
1345 rpmsg_resmgr_state.mqMemHandle = MessageQCopy_create (
1346 90,
1347 "rpmsg-memmgr",
1348 _rpmsg_memmgr_cb,
1349 NULL,
1350 &rpmsg_resmgr_state.memEndpoint);
1351 if (rpmsg_resmgr_state.mqMemHandle == NULL) {
1352 MessageQCopy_delete (&rpmsg_resmgr_state.mqHandle);
1353 /*! @retval ENOMEM Failed to register MQCopy handle! */
1354 status = -ENOMEM;
1355 GT_setFailureReason (curTrace,
1356 GT_4CLASS,
1357 "rpmsg_resmgr_setup",
1358 status,
1359 "Failed to create MessageQCopy handle!");
1360 }
1361 else {
1362 #endif
1363 for (i = 0; i < MultiProc_getNumProcessors(); i++) {
1364 if (i != MultiProc_self())
1365 _rpmsg_resmgr_create_conn(i,
1366 rpmsg_resmgr_state.endpoint);
1367 }
1368 if (status < 0) {
1369 #if defined(USE_MEMMGR)
1370 MessageQCopy_delete (&rpmsg_resmgr_state.mqMemHandle);
1371 #endif
1372 MessageQCopy_delete (&rpmsg_resmgr_state.mqHandle);
1373 /*! @retval ENOMEM Failed to register MQCopy handle! */
1374 status = -ENOMEM;
1375 GT_setFailureReason (curTrace,
1376 GT_4CLASS,
1377 "rpmsg_resmgr_setup",
1378 status,
1379 "Failed to register MQCopy handle!");
1380 }
1381 if (status >= 0) {
1382 rpmsg_resmgr_state.isSetup = TRUE;
1383 }
1384 #if defined (USE_MEMMGR)
1385 }
1386 #endif
1387 }
1388 else {
1389 GT_setFailureReason (curTrace,
1390 GT_4CLASS,
1391 "_rpmsg_resmgr_setup",
1392 status,
1393 "Failed to register callback!");
1394 }
1395 }
1396 #if !defined(SYSLINK_BUILD_OPTIMIZE)
1397 }
1398 #endif /* if !defined(SYSLINK_BUILD_OPTIMIZE) */
1400 GT_0trace (curTrace, GT_LEAVE, "rpmsg_resmgr_setup");
1401 return status;
1402 }
1405 /*!
1406 * @brief Module destroy function.
1407 *
1408 * @sa rpmsg_resmgr_setup
1409 */
1410 Void
1411 rpmsg_resmgr_destroy (Void)
1412 {
1413 int i = 0;
1415 GT_0trace (curTrace, GT_ENTER, "_rpmsg_resmgr_destroy");
1417 for (i = 0; i < MultiProc_MAXPROCESSORS; i++) {
1418 if (rpmsg_resmgr_state.objects[i])
1419 _rpmsg_resmgr_delete_conn(rpmsg_resmgr_state.objects[i]);
1420 }
1421 #if defined(USE_MEMMGR)
1422 MessageQCopy_delete(&rpmsg_resmgr_state.mqMemHandle);
1423 #endif
1424 MessageQCopy_delete(&rpmsg_resmgr_state.mqHandle);
1426 if (rpmsg_resmgr_state.gateHandle != NULL) {
1427 GateSpinlock_delete ((GateSpinlock_Handle *)
1428 &(rpmsg_resmgr_state.gateHandle));
1429 }
1431 #if defined(SYSLINK_USE_IPU_PM)
1432 ipu_pm_ivahd_off();
1433 #endif
1435 rpmsg_resmgr_state.isSetup = FALSE ;
1437 GT_0trace (curTrace, GT_LEAVE, "_rpmsgDrv_destroy");
1438 }
1441 /** ============================================================================
1442 * Internal functions
1443 * ============================================================================
1444 */