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