Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
strm.c
Go to the documentation of this file.
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 
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 */
58 };
59 
60 /*
61  * ======== strm_object ========
62  * This object is allocated in strm_open().
63  */
64 struct strm_object {
67  u32 dir; /* DSP_TONODE or DSP_FROMNODE */
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, ... */
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 =
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) ?
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,
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 }