Merge tag 'for_linus-3.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/jwesse...
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / tidspbridge / rmgr / strm.c
1 /*
2 * strm.c
3 *
4 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5 *
6 * DSP/BIOS Bridge Stream Manager.
7 *
8 * Copyright (C) 2005-2006 Texas Instruments, Inc.
9 *
10 * This package is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 */
18
19 #include <linux/types.h>
20
21 /* ----------------------------------- Host OS */
22 #include <dspbridge/host_os.h>
23
24 /* ----------------------------------- DSP/BIOS Bridge */
25 #include <dspbridge/dbdefs.h>
26
27 /* ----------------------------------- OS Adaptation Layer */
28 #include <dspbridge/sync.h>
29
30 /* ----------------------------------- Bridge Driver */
31 #include <dspbridge/dspdefs.h>
32
33 /* ----------------------------------- Resource Manager */
34 #include <dspbridge/nodepriv.h>
35
36 /* ----------------------------------- Others */
37 #include <dspbridge/cmm.h>
38
39 /* ----------------------------------- This */
40 #include <dspbridge/strm.h>
41
42 #include <dspbridge/resourcecleanup.h>
43
44 /* ----------------------------------- Defines, Data Structures, Typedefs */
45 #define DEFAULTTIMEOUT 10000
46 #define DEFAULTNUMBUFS 2
47
48 /*
49 * ======== strm_mgr ========
50 * The strm_mgr contains device information needed to open the underlying
51 * channels of a stream.
52 */
53 struct strm_mgr {
54 struct dev_object *dev_obj; /* Device for this processor */
55 struct chnl_mgr *chnl_mgr; /* Channel manager */
56 /* Function interface to Bridge driver */
57 struct bridge_drv_interface *intf_fxns;
58 };
59
60 /*
61 * ======== strm_object ========
62 * This object is allocated in strm_open().
63 */
64 struct strm_object {
65 struct strm_mgr *strm_mgr_obj;
66 struct chnl_object *chnl_obj;
67 u32 dir; /* DSP_TONODE or DSP_FROMNODE */
68 u32 timeout;
69 u32 num_bufs; /* Max # of bufs allowed in stream */
70 u32 bufs_in_strm; /* Current # of bufs in stream */
71 u32 bytes; /* bytes transferred since idled */
72 /* STREAM_IDLE, STREAM_READY, ... */
73 enum dsp_streamstate strm_state;
74 void *user_event; /* Saved for strm_get_info() */
75 enum dsp_strmmode strm_mode; /* STRMMODE_[PROCCOPY][ZEROCOPY]... */
76 u32 dma_chnl_id; /* DMA chnl id */
77 u32 dma_priority; /* DMA priority:DMAPRI_[LOW][HIGH] */
78 u32 segment_id; /* >0 is SM segment.=0 is local heap */
79 u32 buf_alignment; /* Alignment for stream bufs */
80 /* Stream's SM address translator */
81 struct cmm_xlatorobject *xlator;
82 };
83
84 /* ----------------------------------- Function Prototypes */
85 static int delete_strm(struct strm_object *stream_obj);
86
87 /*
88 * ======== strm_allocate_buffer ========
89 * Purpose:
90 * Allocates buffers for a stream.
91 */
92 int strm_allocate_buffer(struct strm_res_object *strmres, u32 usize,
93 u8 **ap_buffer, u32 num_bufs,
94 struct process_context *pr_ctxt)
95 {
96 int status = 0;
97 u32 alloc_cnt = 0;
98 u32 i;
99 struct strm_object *stream_obj = strmres->stream;
100
101 if (stream_obj) {
102 /*
103 * Allocate from segment specified at time of stream open.
104 */
105 if (usize == 0)
106 status = -EINVAL;
107
108 } else {
109 status = -EFAULT;
110 }
111
112 if (status)
113 goto func_end;
114
115 for (i = 0; i < num_bufs; i++) {
116 (void)cmm_xlator_alloc_buf(stream_obj->xlator, &ap_buffer[i],
117 usize);
118 if (ap_buffer[i] == NULL) {
119 status = -ENOMEM;
120 alloc_cnt = i;
121 break;
122 }
123 }
124 if (status)
125 strm_free_buffer(strmres, ap_buffer, alloc_cnt, pr_ctxt);
126
127 if (status)
128 goto func_end;
129
130 drv_proc_update_strm_res(num_bufs, strmres);
131
132 func_end:
133 return status;
134 }
135
136 /*
137 * ======== strm_close ========
138 * Purpose:
139 * Close a stream opened with strm_open().
140 */
141 int strm_close(struct strm_res_object *strmres,
142 struct process_context *pr_ctxt)
143 {
144 struct bridge_drv_interface *intf_fxns;
145 struct chnl_info chnl_info_obj;
146 int status = 0;
147 struct strm_object *stream_obj = strmres->stream;
148
149 if (!stream_obj) {
150 status = -EFAULT;
151 } else {
152 /* Have all buffers been reclaimed? If not, return
153 * -EPIPE */
154 intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
155 status =
156 (*intf_fxns->chnl_get_info) (stream_obj->chnl_obj,
157 &chnl_info_obj);
158
159 if (chnl_info_obj.cio_cs > 0 || chnl_info_obj.cio_reqs > 0)
160 status = -EPIPE;
161 else
162 status = delete_strm(stream_obj);
163 }
164
165 if (status)
166 goto func_end;
167
168 idr_remove(pr_ctxt->stream_id, strmres->id);
169 func_end:
170 dev_dbg(bridge, "%s: stream_obj: %p, status 0x%x\n", __func__,
171 stream_obj, status);
172 return status;
173 }
174
175 /*
176 * ======== strm_create ========
177 * Purpose:
178 * Create a STRM manager object.
179 */
180 int strm_create(struct strm_mgr **strm_man,
181 struct dev_object *dev_obj)
182 {
183 struct strm_mgr *strm_mgr_obj;
184 int status = 0;
185
186 *strm_man = NULL;
187 /* Allocate STRM manager object */
188 strm_mgr_obj = kzalloc(sizeof(struct strm_mgr), GFP_KERNEL);
189 if (strm_mgr_obj == NULL)
190 status = -ENOMEM;
191 else
192 strm_mgr_obj->dev_obj = dev_obj;
193
194 /* Get Channel manager and Bridge function interface */
195 if (!status) {
196 status = dev_get_chnl_mgr(dev_obj, &(strm_mgr_obj->chnl_mgr));
197 if (!status) {
198 (void)dev_get_intf_fxns(dev_obj,
199 &(strm_mgr_obj->intf_fxns));
200 }
201 }
202
203 if (!status)
204 *strm_man = strm_mgr_obj;
205 else
206 kfree(strm_mgr_obj);
207
208 return status;
209 }
210
211 /*
212 * ======== strm_delete ========
213 * Purpose:
214 * Delete the STRM Manager Object.
215 */
216 void strm_delete(struct strm_mgr *strm_mgr_obj)
217 {
218 kfree(strm_mgr_obj);
219 }
220
221 /*
222 * ======== strm_free_buffer ========
223 * Purpose:
224 * Frees the buffers allocated for a stream.
225 */
226 int strm_free_buffer(struct strm_res_object *strmres, u8 ** ap_buffer,
227 u32 num_bufs, struct process_context *pr_ctxt)
228 {
229 int status = 0;
230 u32 i = 0;
231 struct strm_object *stream_obj = strmres->stream;
232
233 if (!stream_obj)
234 status = -EFAULT;
235
236 if (!status) {
237 for (i = 0; i < num_bufs; i++) {
238 status =
239 cmm_xlator_free_buf(stream_obj->xlator,
240 ap_buffer[i]);
241 if (status)
242 break;
243 ap_buffer[i] = NULL;
244 }
245 }
246 drv_proc_update_strm_res(num_bufs - i, strmres);
247
248 return status;
249 }
250
251 /*
252 * ======== strm_get_info ========
253 * Purpose:
254 * Retrieves information about a stream.
255 */
256 int strm_get_info(struct strm_object *stream_obj,
257 struct stream_info *stream_info,
258 u32 stream_info_size)
259 {
260 struct bridge_drv_interface *intf_fxns;
261 struct chnl_info chnl_info_obj;
262 int status = 0;
263 void *virt_base = NULL; /* NULL if no SM used */
264
265 if (!stream_obj) {
266 status = -EFAULT;
267 } else {
268 if (stream_info_size < sizeof(struct stream_info)) {
269 /* size of users info */
270 status = -EINVAL;
271 }
272 }
273 if (status)
274 goto func_end;
275
276 intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
277 status =
278 (*intf_fxns->chnl_get_info) (stream_obj->chnl_obj,
279 &chnl_info_obj);
280 if (status)
281 goto func_end;
282
283 if (stream_obj->xlator) {
284 /* We have a translator */
285 cmm_xlator_info(stream_obj->xlator, (u8 **) &virt_base, 0,
286 stream_obj->segment_id, false);
287 }
288 stream_info->segment_id = stream_obj->segment_id;
289 stream_info->strm_mode = stream_obj->strm_mode;
290 stream_info->virt_base = virt_base;
291 stream_info->user_strm->number_bufs_allowed = stream_obj->num_bufs;
292 stream_info->user_strm->number_bufs_in_stream = chnl_info_obj.cio_cs +
293 chnl_info_obj.cio_reqs;
294 /* # of bytes transferred since last call to DSPStream_Idle() */
295 stream_info->user_strm->number_bytes = chnl_info_obj.bytes_tx;
296 stream_info->user_strm->sync_object_handle = chnl_info_obj.event_obj;
297 /* Determine stream state based on channel state and info */
298 if (chnl_info_obj.state & CHNL_STATEEOS) {
299 stream_info->user_strm->ss_stream_state = STREAM_DONE;
300 } else {
301 if (chnl_info_obj.cio_cs > 0)
302 stream_info->user_strm->ss_stream_state = STREAM_READY;
303 else if (chnl_info_obj.cio_reqs > 0)
304 stream_info->user_strm->ss_stream_state =
305 STREAM_PENDING;
306 else
307 stream_info->user_strm->ss_stream_state = STREAM_IDLE;
308
309 }
310 func_end:
311 return status;
312 }
313
314 /*
315 * ======== strm_idle ========
316 * Purpose:
317 * Idles a particular stream.
318 */
319 int strm_idle(struct strm_object *stream_obj, bool flush_data)
320 {
321 struct bridge_drv_interface *intf_fxns;
322 int status = 0;
323
324 if (!stream_obj) {
325 status = -EFAULT;
326 } else {
327 intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
328
329 status = (*intf_fxns->chnl_idle) (stream_obj->chnl_obj,
330 stream_obj->timeout,
331 flush_data);
332 }
333
334 dev_dbg(bridge, "%s: stream_obj: %p flush_data: 0x%x status: 0x%x\n",
335 __func__, stream_obj, flush_data, status);
336 return status;
337 }
338
339 /*
340 * ======== strm_issue ========
341 * Purpose:
342 * Issues a buffer on a stream
343 */
344 int strm_issue(struct strm_object *stream_obj, u8 *pbuf, u32 ul_bytes,
345 u32 ul_buf_size, u32 dw_arg)
346 {
347 struct bridge_drv_interface *intf_fxns;
348 int status = 0;
349 void *tmp_buf = NULL;
350
351 if (!stream_obj) {
352 status = -EFAULT;
353 } else {
354 intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
355
356 if (stream_obj->segment_id != 0) {
357 tmp_buf = cmm_xlator_translate(stream_obj->xlator,
358 (void *)pbuf,
359 CMM_VA2DSPPA);
360 if (tmp_buf == NULL)
361 status = -ESRCH;
362
363 }
364 if (!status) {
365 status = (*intf_fxns->chnl_add_io_req)
366 (stream_obj->chnl_obj, pbuf, ul_bytes, ul_buf_size,
367 (u32) tmp_buf, dw_arg);
368 }
369 if (status == -EIO)
370 status = -ENOSR;
371 }
372
373 dev_dbg(bridge, "%s: stream_obj: %p pbuf: %p ul_bytes: 0x%x dw_arg:"
374 " 0x%x status: 0x%x\n", __func__, stream_obj, pbuf,
375 ul_bytes, dw_arg, status);
376 return status;
377 }
378
379 /*
380 * ======== strm_open ========
381 * Purpose:
382 * Open a stream for sending/receiving data buffers to/from a task or
383 * XDAIS socket node on the DSP.
384 */
385 int strm_open(struct node_object *hnode, u32 dir, u32 index,
386 struct strm_attr *pattr,
387 struct strm_res_object **strmres,
388 struct process_context *pr_ctxt)
389 {
390 struct strm_mgr *strm_mgr_obj;
391 struct bridge_drv_interface *intf_fxns;
392 u32 ul_chnl_id;
393 struct strm_object *strm_obj = NULL;
394 s8 chnl_mode;
395 struct chnl_attr chnl_attr_obj;
396 int status = 0;
397 struct cmm_object *hcmm_mgr = NULL; /* Shared memory manager hndl */
398
399 void *stream_res;
400
401 *strmres = NULL;
402 if (dir != DSP_TONODE && dir != DSP_FROMNODE) {
403 status = -EPERM;
404 } else {
405 /* Get the channel id from the node (set in node_connect()) */
406 status = node_get_channel_id(hnode, dir, index, &ul_chnl_id);
407 }
408 if (!status)
409 status = node_get_strm_mgr(hnode, &strm_mgr_obj);
410
411 if (!status) {
412 strm_obj = kzalloc(sizeof(struct strm_object), GFP_KERNEL);
413 if (strm_obj == NULL) {
414 status = -ENOMEM;
415 } else {
416 strm_obj->strm_mgr_obj = strm_mgr_obj;
417 strm_obj->dir = dir;
418 strm_obj->strm_state = STREAM_IDLE;
419 strm_obj->user_event = pattr->user_event;
420 if (pattr->stream_attr_in != NULL) {
421 strm_obj->timeout =
422 pattr->stream_attr_in->timeout;
423 strm_obj->num_bufs =
424 pattr->stream_attr_in->num_bufs;
425 strm_obj->strm_mode =
426 pattr->stream_attr_in->strm_mode;
427 strm_obj->segment_id =
428 pattr->stream_attr_in->segment_id;
429 strm_obj->buf_alignment =
430 pattr->stream_attr_in->buf_alignment;
431 strm_obj->dma_chnl_id =
432 pattr->stream_attr_in->dma_chnl_id;
433 strm_obj->dma_priority =
434 pattr->stream_attr_in->dma_priority;
435 chnl_attr_obj.uio_reqs =
436 pattr->stream_attr_in->num_bufs;
437 } else {
438 strm_obj->timeout = DEFAULTTIMEOUT;
439 strm_obj->num_bufs = DEFAULTNUMBUFS;
440 strm_obj->strm_mode = STRMMODE_PROCCOPY;
441 strm_obj->segment_id = 0; /* local mem */
442 strm_obj->buf_alignment = 0;
443 strm_obj->dma_chnl_id = 0;
444 strm_obj->dma_priority = 0;
445 chnl_attr_obj.uio_reqs = DEFAULTNUMBUFS;
446 }
447 chnl_attr_obj.reserved1 = NULL;
448 /* DMA chnl flush timeout */
449 chnl_attr_obj.reserved2 = strm_obj->timeout;
450 chnl_attr_obj.event_obj = NULL;
451 if (pattr->user_event != NULL)
452 chnl_attr_obj.event_obj = pattr->user_event;
453
454 }
455 }
456 if (status)
457 goto func_cont;
458
459 if ((pattr->virt_base == NULL) || !(pattr->virt_size > 0))
460 goto func_cont;
461
462 /* No System DMA */
463 /* Get the shared mem mgr for this streams dev object */
464 status = dev_get_cmm_mgr(strm_mgr_obj->dev_obj, &hcmm_mgr);
465 if (!status) {
466 /*Allocate a SM addr translator for this strm. */
467 status = cmm_xlator_create(&strm_obj->xlator, hcmm_mgr, NULL);
468 if (!status) {
469 /* Set translators Virt Addr attributes */
470 status = cmm_xlator_info(strm_obj->xlator,
471 (u8 **) &pattr->virt_base,
472 pattr->virt_size,
473 strm_obj->segment_id, true);
474 }
475 }
476 func_cont:
477 if (!status) {
478 /* Open channel */
479 chnl_mode = (dir == DSP_TONODE) ?
480 CHNL_MODETODSP : CHNL_MODEFROMDSP;
481 intf_fxns = strm_mgr_obj->intf_fxns;
482 status = (*intf_fxns->chnl_open) (&(strm_obj->chnl_obj),
483 strm_mgr_obj->chnl_mgr,
484 chnl_mode, ul_chnl_id,
485 &chnl_attr_obj);
486 if (status) {
487 /*
488 * over-ride non-returnable status codes so we return
489 * something documented
490 */
491 if (status != -ENOMEM && status !=
492 -EINVAL && status != -EPERM) {
493 /*
494 * We got a status that's not return-able.
495 * Assert that we got something we were
496 * expecting (-EFAULT isn't acceptable,
497 * strm_mgr_obj->chnl_mgr better be valid or we
498 * assert here), and then return -EPERM.
499 */
500 status = -EPERM;
501 }
502 }
503 }
504 if (!status) {
505 status = drv_proc_insert_strm_res_element(strm_obj,
506 &stream_res, pr_ctxt);
507 if (status)
508 delete_strm(strm_obj);
509 else
510 *strmres = (struct strm_res_object *)stream_res;
511 } else {
512 (void)delete_strm(strm_obj);
513 }
514
515 dev_dbg(bridge, "%s: hnode: %p dir: 0x%x index: 0x%x pattr: %p "
516 "strmres: %p status: 0x%x\n", __func__,
517 hnode, dir, index, pattr, strmres, status);
518 return status;
519 }
520
521 /*
522 * ======== strm_reclaim ========
523 * Purpose:
524 * Relcaims a buffer from a stream.
525 */
526 int strm_reclaim(struct strm_object *stream_obj, u8 ** buf_ptr,
527 u32 *nbytes, u32 *buff_size, u32 *pdw_arg)
528 {
529 struct bridge_drv_interface *intf_fxns;
530 struct chnl_ioc chnl_ioc_obj;
531 int status = 0;
532 void *tmp_buf = NULL;
533
534 if (!stream_obj) {
535 status = -EFAULT;
536 goto func_end;
537 }
538 intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
539
540 status =
541 (*intf_fxns->chnl_get_ioc) (stream_obj->chnl_obj,
542 stream_obj->timeout,
543 &chnl_ioc_obj);
544 if (!status) {
545 *nbytes = chnl_ioc_obj.byte_size;
546 if (buff_size)
547 *buff_size = chnl_ioc_obj.buf_size;
548
549 *pdw_arg = chnl_ioc_obj.arg;
550 if (!CHNL_IS_IO_COMPLETE(chnl_ioc_obj)) {
551 if (CHNL_IS_TIMED_OUT(chnl_ioc_obj)) {
552 status = -ETIME;
553 } else {
554 /* Allow reclaims after idle to succeed */
555 if (!CHNL_IS_IO_CANCELLED(chnl_ioc_obj))
556 status = -EPERM;
557
558 }
559 }
560 /* Translate zerocopy buffer if channel not canceled. */
561 if (!status
562 && (!CHNL_IS_IO_CANCELLED(chnl_ioc_obj))
563 && (stream_obj->strm_mode == STRMMODE_ZEROCOPY)) {
564 /*
565 * This is a zero-copy channel so chnl_ioc_obj.buf
566 * contains the DSP address of SM. We need to
567 * translate it to a virtual address for the user
568 * thread to access.
569 * Note: Could add CMM_DSPPA2VA to CMM in the future.
570 */
571 tmp_buf = cmm_xlator_translate(stream_obj->xlator,
572 chnl_ioc_obj.buf,
573 CMM_DSPPA2PA);
574 if (tmp_buf != NULL) {
575 /* now convert this GPP Pa to Va */
576 tmp_buf = cmm_xlator_translate(stream_obj->
577 xlator,
578 tmp_buf,
579 CMM_PA2VA);
580 }
581 if (tmp_buf == NULL)
582 status = -ESRCH;
583
584 chnl_ioc_obj.buf = tmp_buf;
585 }
586 *buf_ptr = chnl_ioc_obj.buf;
587 }
588 func_end:
589 dev_dbg(bridge, "%s: stream_obj: %p buf_ptr: %p nbytes: %p "
590 "pdw_arg: %p status 0x%x\n", __func__, stream_obj,
591 buf_ptr, nbytes, pdw_arg, status);
592 return status;
593 }
594
595 /*
596 * ======== strm_register_notify ========
597 * Purpose:
598 * Register to be notified on specific events for this stream.
599 */
600 int strm_register_notify(struct strm_object *stream_obj, u32 event_mask,
601 u32 notify_type, struct dsp_notification
602 * hnotification)
603 {
604 struct bridge_drv_interface *intf_fxns;
605 int status = 0;
606
607 if (!stream_obj) {
608 status = -EFAULT;
609 } else if ((event_mask & ~((DSP_STREAMIOCOMPLETION) |
610 DSP_STREAMDONE)) != 0) {
611 status = -EINVAL;
612 } else {
613 if (notify_type != DSP_SIGNALEVENT)
614 status = -ENOSYS;
615
616 }
617 if (!status) {
618 intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
619
620 status =
621 (*intf_fxns->chnl_register_notify) (stream_obj->
622 chnl_obj,
623 event_mask,
624 notify_type,
625 hnotification);
626 }
627
628 return status;
629 }
630
631 /*
632 * ======== strm_select ========
633 * Purpose:
634 * Selects a ready stream.
635 */
636 int strm_select(struct strm_object **strm_tab, u32 strms,
637 u32 *pmask, u32 utimeout)
638 {
639 u32 index;
640 struct chnl_info chnl_info_obj;
641 struct bridge_drv_interface *intf_fxns;
642 struct sync_object **sync_events = NULL;
643 u32 i;
644 int status = 0;
645
646 *pmask = 0;
647 for (i = 0; i < strms; i++) {
648 if (!strm_tab[i]) {
649 status = -EFAULT;
650 break;
651 }
652 }
653 if (status)
654 goto func_end;
655
656 /* Determine which channels have IO ready */
657 for (i = 0; i < strms; i++) {
658 intf_fxns = strm_tab[i]->strm_mgr_obj->intf_fxns;
659 status = (*intf_fxns->chnl_get_info) (strm_tab[i]->chnl_obj,
660 &chnl_info_obj);
661 if (status) {
662 break;
663 } else {
664 if (chnl_info_obj.cio_cs > 0)
665 *pmask |= (1 << i);
666
667 }
668 }
669 if (!status && utimeout > 0 && *pmask == 0) {
670 /* Non-zero timeout */
671 sync_events = kmalloc(strms * sizeof(struct sync_object *),
672 GFP_KERNEL);
673
674 if (sync_events == NULL) {
675 status = -ENOMEM;
676 } else {
677 for (i = 0; i < strms; i++) {
678 intf_fxns =
679 strm_tab[i]->strm_mgr_obj->intf_fxns;
680 status = (*intf_fxns->chnl_get_info)
681 (strm_tab[i]->chnl_obj, &chnl_info_obj);
682 if (status)
683 break;
684 else
685 sync_events[i] =
686 chnl_info_obj.sync_event;
687
688 }
689 }
690 if (!status) {
691 status =
692 sync_wait_on_multiple_events(sync_events, strms,
693 utimeout, &index);
694 if (!status) {
695 /* Since we waited on the event, we have to
696 * reset it */
697 sync_set_event(sync_events[index]);
698 *pmask = 1 << index;
699 }
700 }
701 }
702 func_end:
703 kfree(sync_events);
704
705 return status;
706 }
707
708 /*
709 * ======== delete_strm ========
710 * Purpose:
711 * Frees the resources allocated for a stream.
712 */
713 static int delete_strm(struct strm_object *stream_obj)
714 {
715 struct bridge_drv_interface *intf_fxns;
716 int status = 0;
717
718 if (stream_obj) {
719 if (stream_obj->chnl_obj) {
720 intf_fxns = stream_obj->strm_mgr_obj->intf_fxns;
721 /* Channel close can fail only if the channel handle
722 * is invalid. */
723 status = (*intf_fxns->chnl_close)
724 (stream_obj->chnl_obj);
725 }
726 /* Free all SM address translator resources */
727 kfree(stream_obj->xlator);
728 kfree(stream_obj);
729 } else {
730 status = -EFAULT;
731 }
732 return status;
733 }