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 */
53struct 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 */
64struct 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 */
85static int delete_strm(struct strm_object *stream_obj);
86
87/*
88 *  ======== strm_allocate_buffer ========
89 *  Purpose:
90 *      Allocates buffers for a stream.
91 */
92int 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
132func_end:
133	return status;
134}
135
136/*
137 *  ======== strm_close ========
138 *  Purpose:
139 *      Close a stream opened with strm_open().
140 */
141int 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);
169func_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 */
180int 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 */
216void 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 */
226int 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 */
256int 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	}
310func_end:
311	return status;
312}
313
314/*
315 *  ======== strm_idle ========
316 *  Purpose:
317 *      Idles a particular stream.
318 */
319int 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 */
344int 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 */
385int 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	}
476func_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 */
526int 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	}
588func_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 */
600int 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 */
636int 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	}
702func_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 */
713static 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}
734