Path: blob/main/sys/contrib/ncsw/Peripherals/BM/bm_portal.c
48375 views
/******************************************************************************12� 1995-2003, 2004, 2005-2011 Freescale Semiconductor, Inc.3All rights reserved.45This is proprietary source code of Freescale Semiconductor Inc.,6and its use is subject to the NetComm Device Drivers EULA.7The copyright notice above does not evidence any actual or intended8publication of such source code.910ALTERNATIVELY, redistribution and use in source and binary forms, with11or without modification, are permitted provided that the following12conditions are met:13* Redistributions of source code must retain the above copyright14notice, this list of conditions and the following disclaimer.15* Redistributions in binary form must reproduce the above copyright16notice, this list of conditions and the following disclaimer in the17documentation and/or other materials provided with the distribution.18* Neither the name of Freescale Semiconductor nor the19names of its contributors may be used to endorse or promote products20derived from this software without specific prior written permission.2122THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY23EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED24WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE25DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY26DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES27(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;28LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND29ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT30(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS31SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.32*3334**************************************************************************/35/******************************************************************************36@File bm.c3738@Description BM39*//***************************************************************************/40#include "error_ext.h"41#include "std_ext.h"42#include "string_ext.h"43#include "mem_ext.h"44#include "core_ext.h"4546#include "bm.h"474849#define __ERR_MODULE__ MODULE_BM505152/****************************************/53/* static functions */54/****************************************/5556static uint32_t __poll_portal_slow(t_BmPortal *p);57static void __poll_portal_fast(t_BmPortal *p);5859/* Portal interrupt handler */60static void portal_isr(void *ptr)61{62t_BmPortal *portal = ptr;63/* Only do fast-path handling if it's required */64if (portal->flags & BMAN_PORTAL_FLAG_IRQ_FAST)65__poll_portal_fast(portal);66__poll_portal_slow(portal);6768}6970/**71* bman_create_portal - Manage a Bman s/w portal72* @portal: the s/w corenet portal to use73* @flags: bit-mask of BMAN_PORTAL_FLAG_*** options74* @pools: bit-array of buffer pools available to this portal75* @portal_ctx: opaque user-supplied data to be associated with the portal76*77* Creates a managed portal object. @irq is only used if @flags specifies78* BMAN_PORTAL_FLAG_IRQ. @pools is copied, so the caller can do as they please79* with it after the function returns. It will only be possible to configure80* buffer pool objects as "suppliers" if they are specified in @pools, and the81* driver will only track depletion state changes to the same subset of buffer82* pools. If @pools is NULL, buffer pool depletion state will not be tracked.83* If the BMAN_PORTAL_FLAG_RECOVER flag is specified, then the function will84* attempt to expire any existing RCR entries, otherwise the function will fail85* if RCR is non-empty. If the BMAN_PORTAL_FLAG_WAIT flag is set, the function86* is allowed to block waiting for expiration of RCR. BMAN_PORTAL_FLAG_WAIT_INT87* makes any blocking interruptible.88*/8990static t_Error bman_create_portal(t_BmPortal *p_BmPortal,91uint32_t flags,92const struct bman_depletion *pools)93{94int ret = 0;95uint8_t bpid = 0;96e_BmPortalRcrConsumeMode rcr_cmode;97e_BmPortalProduceMode pmode;9899pmode = e_BmPortalPVB;100rcr_cmode = (flags & BMAN_PORTAL_FLAG_CACHE) ? e_BmPortalRcrCCE : e_BmPortalRcrCCI;101102switch (pmode)103{104case e_BmPortalPCI:105p_BmPortal->cbs[BM_RCR_RING].f_BmCommitCb = bm_rcr_pci_commit;106break;107case e_BmPortalPCE:108p_BmPortal->cbs[BM_RCR_RING].f_BmCommitCb = bm_rcr_pce_commit;109break;110case e_BmPortalPVB:111p_BmPortal->cbs[BM_RCR_RING].f_BmCommitCb = bm_rcr_pvb_commit;112break;113}114switch (rcr_cmode)115{116case e_BmPortalRcrCCI:117p_BmPortal->cbs[BM_RCR_RING].f_BmUpdateCb = bm_rcr_cci_update;118p_BmPortal->cbs[BM_RCR_RING].f_BmPrefetchCb = NULL;119break;120case e_BmPortalRcrCCE:121p_BmPortal->cbs[BM_RCR_RING].f_BmUpdateCb = bm_rcr_cce_update;122p_BmPortal->cbs[BM_RCR_RING].f_BmPrefetchCb = bm_rcr_cce_prefetch;123break;124}125126if (bm_rcr_init(p_BmPortal->p_BmPortalLow, pmode, rcr_cmode)) {127REPORT_ERROR(MAJOR, E_INVALID_STATE, ("RCR initialization failed"));128goto fail_rcr;129}130if (bm_mc_init(p_BmPortal->p_BmPortalLow)) {131REPORT_ERROR(MAJOR, E_INVALID_STATE, ("MC initialization failed"));132goto fail_mc;133}134p_BmPortal->pools[0] = *pools;135bman_depletion_init(&p_BmPortal->pools[1]);136while (bpid < BM_MAX_NUM_OF_POOLS) {137/* Default to all BPIDs disabled, we enable as required138* at run-time. */139bm_isr_bscn_mask(p_BmPortal->p_BmPortalLow, bpid, 0);140bpid++;141}142p_BmPortal->flags = flags;143p_BmPortal->slowpoll = 0;144p_BmPortal->rcrProd = p_BmPortal->rcrCons = 0;145memset(&p_BmPortal->depletionPoolsTable, 0, sizeof(p_BmPortal->depletionPoolsTable));146/* Write-to-clear any stale interrupt status bits */147bm_isr_disable_write(p_BmPortal->p_BmPortalLow, 0xffffffff);148bm_isr_status_clear(p_BmPortal->p_BmPortalLow, 0xffffffff);149bm_isr_enable_write(p_BmPortal->p_BmPortalLow, BM_PIRQ_RCRI | BM_PIRQ_BSCN);150if (flags & BMAN_PORTAL_FLAG_IRQ)151{152XX_SetIntr(p_BmPortal->irq, portal_isr, p_BmPortal);153XX_EnableIntr(p_BmPortal->irq);154/* Enable the bits that make sense */155bm_isr_uninhibit(p_BmPortal->p_BmPortalLow);156} else157/* without IRQ, we can't block */158flags &= ~BMAN_PORTAL_FLAG_WAIT;159/* Need RCR to be empty before continuing */160bm_isr_disable_write(p_BmPortal->p_BmPortalLow, (uint32_t)~BM_PIRQ_RCRI);161if (!(flags & BMAN_PORTAL_FLAG_RECOVER) ||162!(flags & BMAN_PORTAL_FLAG_WAIT))163ret = bm_rcr_get_fill(p_BmPortal->p_BmPortalLow);164if (ret) {165REPORT_ERROR(MAJOR, E_INVALID_STATE, ("RCR unclean, need recovery"));166goto fail_rcr_empty;167}168bm_isr_disable_write(p_BmPortal->p_BmPortalLow, 0);169return E_OK;170fail_rcr_empty:171bm_mc_finish(p_BmPortal->p_BmPortalLow);172fail_mc:173bm_rcr_finish(p_BmPortal->p_BmPortalLow);174fail_rcr:175XX_Free(p_BmPortal);176return ERROR_CODE(E_INVALID_STATE);177}178179static void bman_destroy_portal(t_BmPortal* p_BmPortal)180{181BmUpdate(p_BmPortal, BM_RCR_RING);182if (p_BmPortal->flags & BMAN_PORTAL_FLAG_IRQ)183{184XX_DisableIntr(p_BmPortal->irq);185XX_FreeIntr(p_BmPortal->irq);186}187bm_mc_finish(p_BmPortal->p_BmPortalLow);188bm_rcr_finish(p_BmPortal->p_BmPortalLow);189XX_Free(p_BmPortal->p_BmPortalLow);190}191192/* When release logic waits on available RCR space, we need a global waitqueue193* in the case of "affine" use (as the waits wake on different cpus which means194* different portals - so we can't wait on any per-portal waitqueue). */195196static uint32_t __poll_portal_slow(t_BmPortal* p_BmPortal)197{198struct bman_depletion tmp;199t_BmPool *p_BmPool;200uint32_t ret,is = bm_isr_status_read(p_BmPortal->p_BmPortalLow);201ret = is;202203/* There is a gotcha to be aware of. If we do the query before clearing204* the status register, we may miss state changes that occur between the205* two. If we write to clear the status register before the query, the206* cache-enabled query command may overtake the status register write207* unless we use a heavyweight sync (which we don't want). Instead, we208* write-to-clear the status register then *read it back* before doing209* the query, hence the odd while loop with the 'is' accumulation. */210if (is & BM_PIRQ_BSCN) {211uint32_t i, j;212uint32_t __is;213bm_isr_status_clear(p_BmPortal->p_BmPortalLow, BM_PIRQ_BSCN);214while ((__is = bm_isr_status_read(p_BmPortal->p_BmPortalLow)) & BM_PIRQ_BSCN) {215is |= __is;216bm_isr_status_clear(p_BmPortal->p_BmPortalLow, BM_PIRQ_BSCN);217}218is &= ~BM_PIRQ_BSCN;219BmPortalQuery(p_BmPortal, &tmp, TRUE);220for (i = 0; i < 2; i++) {221uint32_t idx = i * 32;222/* tmp is a mask of currently-depleted pools.223* pools[0] is mask of those we care about.224* pools[1] is our previous view (we only want to225* be told about changes). */226tmp.__state[i] &= p_BmPortal->pools[0].__state[i];227if (tmp.__state[i] == p_BmPortal->pools[1].__state[i])228/* fast-path, nothing to see, move along */229continue;230for (j = 0; j <= 31; j++, idx++) {231int b4 = bman_depletion_get(&p_BmPortal->pools[1], (uint8_t)idx);232int af = bman_depletion_get(&tmp, (uint8_t)idx);233if (b4 == af)234continue;235p_BmPool = p_BmPortal->depletionPoolsTable[idx];236ASSERT_COND(p_BmPool->f_Depletion);237p_BmPool->f_Depletion(p_BmPool->h_App, (bool)af);238}239}240p_BmPortal->pools[1] = tmp;241}242243if (is & BM_PIRQ_RCRI) {244NCSW_PLOCK(p_BmPortal);245p_BmPortal->rcrCons += BmUpdate(p_BmPortal, BM_RCR_RING);246bm_rcr_set_ithresh(p_BmPortal->p_BmPortalLow, 0);247PUNLOCK(p_BmPortal);248bm_isr_status_clear(p_BmPortal->p_BmPortalLow, BM_PIRQ_RCRI);249is &= ~BM_PIRQ_RCRI;250}251252/* There should be no status register bits left undefined */253ASSERT_COND(!is);254return ret;255}256257static void __poll_portal_fast(t_BmPortal* p_BmPortal)258{259UNUSED(p_BmPortal);260/* nothing yet, this is where we'll put optimised RCR consumption261* tracking */262}263264265static __inline__ void rel_set_thresh(t_BmPortal *p_BmPortal, int check)266{267if (!check || !bm_rcr_get_ithresh(p_BmPortal->p_BmPortalLow))268bm_rcr_set_ithresh(p_BmPortal->p_BmPortalLow, RCR_ITHRESH);269}270271/* Used as a wait_event() expression. If it returns non-NULL, any lock will272* remain held. */273static struct bm_rcr_entry *try_rel_start(t_BmPortal *p_BmPortal)274{275struct bm_rcr_entry *r;276277NCSW_PLOCK(p_BmPortal);278if (bm_rcr_get_avail((p_BmPortal)->p_BmPortalLow) < RCR_THRESH)279BmUpdate(p_BmPortal, BM_RCR_RING);280r = bm_rcr_start((p_BmPortal)->p_BmPortalLow);281if (!r) {282rel_set_thresh(p_BmPortal, 1);283PUNLOCK(p_BmPortal);284}285return r;286}287288static __inline__ t_Error wait_rel_start(t_BmPortal *p_BmPortal,289struct bm_rcr_entry **rel,290uint32_t flags)291{292int tries = 100;293294UNUSED(flags);295do {296*rel = try_rel_start(p_BmPortal);297XX_Sleep(1);298} while (!*rel && --tries);299300if (!(*rel))301return ERROR_CODE(E_BUSY);302303return E_OK;304}305306/* This copies Qman's eqcr_completed() routine, see that for details */307static int rel_completed(t_BmPortal *p_BmPortal, uint32_t rcr_poll)308{309uint32_t tr_cons = p_BmPortal->rcrCons;310if (rcr_poll & 0xc0000000) {311rcr_poll &= 0x7fffffff;312tr_cons ^= 0x80000000;313}314if (tr_cons >= rcr_poll)315return 1;316if ((rcr_poll - tr_cons) > BM_RCR_SIZE)317return 1;318if (!bm_rcr_get_fill(p_BmPortal->p_BmPortalLow))319/* If RCR is empty, we must have completed */320return 1;321rel_set_thresh(p_BmPortal, 0);322return 0;323}324325static __inline__ void rel_commit(t_BmPortal *p_BmPortal, uint32_t flags,uint8_t num)326{327uint32_t rcr_poll;328329BmCommit(p_BmPortal, BM_RCR_RING, (uint8_t)(BM_RCR_VERB_CMD_BPID_SINGLE | (num & BM_RCR_VERB_BUFCOUNT_MASK)));330/* increment the producer count and capture it for SYNC */331rcr_poll = ++p_BmPortal->rcrProd;332if ((flags & BMAN_RELEASE_FLAG_WAIT_SYNC) ==333BMAN_RELEASE_FLAG_WAIT_SYNC)334rel_set_thresh(p_BmPortal, 1);335PUNLOCK(p_BmPortal);336if ((flags & BMAN_RELEASE_FLAG_WAIT_SYNC) !=337BMAN_RELEASE_FLAG_WAIT_SYNC)338return;339rel_completed(p_BmPortal, rcr_poll);340}341342343/****************************************/344/* Inter-Module functions */345/****************************************/346347/**348* bman_release - Release buffer(s) to the buffer pool349* @p_BmPool: the buffer pool object to release to350* @bufs: an array of buffers to release351* @num: the number of buffers in @bufs (1-8)352* @flags: bit-mask of BMAN_RELEASE_FLAG_*** options353*354* Adds the given buffers to RCR entries. If the portal @p_BmPortal was created with the355* "COMPACT" flag, then it will be using a compaction algorithm to improve356* utilization of RCR. As such, these buffers may join an existing ring entry357* and/or it may not be issued right away so as to allow future releases to join358* the same ring entry. Use the BMAN_RELEASE_FLAG_NOW flag to override this359* behavior by committing the RCR entry (or entries) right away. If the RCR360* ring is full, the function will return -EBUSY unless BMAN_RELEASE_FLAG_WAIT361* is selected, in which case it will sleep waiting for space to become362* available in RCR. If the function receives a signal before such time (and363* BMAN_RELEASE_FLAG_WAIT_INT is set), the function returns -EINTR. Otherwise,364* it returns zero.365*/366367t_Error BmPortalRelease(t_Handle h_BmPortal,368uint8_t bpid,369struct bm_buffer *bufs,370uint8_t num,371uint32_t flags)372{373t_BmPortal *p_BmPortal = (t_BmPortal *)h_BmPortal;374struct bm_rcr_entry *r;375uint8_t i;376377SANITY_CHECK_RETURN_ERROR(p_BmPortal, E_INVALID_HANDLE);378/* TODO: I'm ignoring BMAN_PORTAL_FLAG_COMPACT for now. */379r = try_rel_start(p_BmPortal);380if (!r) {381if (flags & BMAN_RELEASE_FLAG_WAIT) {382t_Error ret = wait_rel_start(p_BmPortal, &r, flags);383if (ret)384return ret;385} else386return ERROR_CODE(E_BUSY);387ASSERT_COND(r != NULL);388}389r->bpid = bpid;390for (i = 0; i < num; i++) {391r->bufs[i].hi = bufs[i].hi;392r->bufs[i].lo = bufs[i].lo;393}394/* Issue the release command and wait for sync if requested. NB: the395* commit can't fail, only waiting can. Don't propagate any failure if a396* signal arrives, otherwise the caller can't distinguish whether the397* release was issued or not. Code for user-space can check398* signal_pending() after we return. */399rel_commit(p_BmPortal, flags, num);400return E_OK;401}402403uint8_t BmPortalAcquire(t_Handle h_BmPortal,404uint8_t bpid,405struct bm_buffer *bufs,406uint8_t num)407{408t_BmPortal *p_BmPortal = (t_BmPortal *)h_BmPortal;409struct bm_mc_command *mcc;410struct bm_mc_result *mcr;411uint8_t ret = 0;412413SANITY_CHECK_RETURN_VALUE(p_BmPortal, E_INVALID_HANDLE, 0);414NCSW_PLOCK(p_BmPortal);415mcc = bm_mc_start(p_BmPortal->p_BmPortalLow);416mcc->acquire.bpid = bpid;417bm_mc_commit(p_BmPortal->p_BmPortalLow,418(uint8_t)(BM_MCC_VERB_CMD_ACQUIRE |419(num & BM_MCC_VERB_ACQUIRE_BUFCOUNT)));420while (!(mcr = bm_mc_result(p_BmPortal->p_BmPortalLow))) ;421ret = num = (uint8_t)(mcr->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT);422ASSERT_COND(num <= 8);423while (num--) {424bufs[num].bpid = bpid;425bufs[num].hi = mcr->acquire.bufs[num].hi;426bufs[num].lo = mcr->acquire.bufs[num].lo;427}428PUNLOCK(p_BmPortal);429return ret;430}431432t_Error BmPortalQuery(t_Handle h_BmPortal, struct bman_depletion *p_Pools, bool depletion)433{434t_BmPortal *p_BmPortal = (t_BmPortal *)h_BmPortal;435struct bm_mc_result *mcr;436437SANITY_CHECK_RETURN_ERROR(p_BmPortal, E_INVALID_HANDLE);438439NCSW_PLOCK(p_BmPortal);440bm_mc_start(p_BmPortal->p_BmPortalLow);441bm_mc_commit(p_BmPortal->p_BmPortalLow, BM_MCC_VERB_CMD_QUERY);442while (!(mcr = bm_mc_result(p_BmPortal->p_BmPortalLow))) ;443if (depletion)444*p_Pools = mcr->query.ds.state;445else446*p_Pools = mcr->query.as.state;447PUNLOCK(p_BmPortal);448return E_OK;449}450451/****************************************/452/* API Init unit functions */453/****************************************/454455t_Handle BM_PORTAL_Config(t_BmPortalParam *p_BmPortalParam)456{457t_BmPortal *p_BmPortal;458459SANITY_CHECK_RETURN_VALUE(p_BmPortalParam, E_INVALID_HANDLE, NULL);460SANITY_CHECK_RETURN_VALUE(p_BmPortalParam->h_Bm, E_INVALID_HANDLE, NULL);461462p_BmPortal = (t_BmPortal *)XX_Malloc(sizeof(t_BmPortal));463if (!p_BmPortal)464{465REPORT_ERROR(MAJOR, E_NO_MEMORY, ("Bm Portal obj!!!"));466return NULL;467}468memset(p_BmPortal, 0, sizeof(t_BmPortal));469470p_BmPortal->p_BmPortalLow = (struct bm_portal *)XX_Malloc(sizeof(struct bm_portal));471if (!p_BmPortal->p_BmPortalLow)472{473XX_Free(p_BmPortal);474REPORT_ERROR(MAJOR, E_NO_MEMORY, ("Low bm portal obj!!!"));475return NULL;476}477memset(p_BmPortal->p_BmPortalLow, 0, sizeof(struct bm_portal));478479p_BmPortal->p_BmPortalDriverParams = (t_BmPortalDriverParams *)XX_Malloc(sizeof(t_BmPortalDriverParams));480if (!p_BmPortal->p_BmPortalDriverParams)481{482XX_Free(p_BmPortal);483XX_Free(p_BmPortal->p_BmPortalLow);484REPORT_ERROR(MAJOR, E_NO_MEMORY, ("Bm Portal driver parameters"));485return NULL;486}487memset(p_BmPortal->p_BmPortalDriverParams, 0, sizeof(t_BmPortalDriverParams));488489p_BmPortal->p_BmPortalLow->addr.addr_ce = UINT_TO_PTR(p_BmPortalParam->ceBaseAddress);490p_BmPortal->p_BmPortalLow->addr.addr_ci = UINT_TO_PTR(p_BmPortalParam->ciBaseAddress);491p_BmPortal->cpu = (int)p_BmPortalParam->swPortalId;492p_BmPortal->irq = p_BmPortalParam->irq;493494p_BmPortal->h_Bm = p_BmPortalParam->h_Bm;495496p_BmPortal->p_BmPortalDriverParams->hwExtStructsMemAttr = DEFAULT_memAttr;497bman_depletion_fill(&p_BmPortal->p_BmPortalDriverParams->mask);498499return p_BmPortal;500}501502t_Error BM_PORTAL_Init(t_Handle h_BmPortal)503{504t_BmPortal *p_BmPortal = (t_BmPortal *)h_BmPortal;505uint32_t flags;506507SANITY_CHECK_RETURN_ERROR(p_BmPortal, E_INVALID_HANDLE);508509flags = (uint32_t)((p_BmPortal->irq != NO_IRQ) ? BMAN_PORTAL_FLAG_IRQ : 0);510flags |= ((p_BmPortal->p_BmPortalDriverParams->hwExtStructsMemAttr & MEMORY_ATTR_CACHEABLE) ?511BMAN_PORTAL_FLAG_CACHE : 0);512513if (bman_create_portal(p_BmPortal,flags,&p_BmPortal->p_BmPortalDriverParams->mask)!=E_OK)514{515BM_PORTAL_Free(p_BmPortal);516RETURN_ERROR(MAJOR, E_NULL_POINTER, ("create portal failed"));517}518BmSetPortalHandle(p_BmPortal->h_Bm, (t_Handle)p_BmPortal, (e_DpaaSwPortal)p_BmPortal->cpu);519520XX_Free(p_BmPortal->p_BmPortalDriverParams);521p_BmPortal->p_BmPortalDriverParams = NULL;522523DBG(TRACE,("Bman-Portal (%d) @ %p:%p\n",524p_BmPortal->cpu,525p_BmPortal->p_BmPortalLow->addr.addr_ce,526p_BmPortal->p_BmPortalLow->addr.addr_ci527));528529DBG(TRACE,("Bman-Portal (%d) @ 0x%016llx:0x%016llx",530p_BmPortal->cpu,531(uint64_t)XX_VirtToPhys(p_BmPortal->p_BmPortalLow->addr.addr_ce),532(uint64_t)XX_VirtToPhys(p_BmPortal->p_BmPortalLow->addr.addr_ci)533));534535return E_OK;536}537538t_Error BM_PORTAL_Free(t_Handle h_BmPortal)539{540t_BmPortal *p_BmPortal = (t_BmPortal *)h_BmPortal;541542if (!p_BmPortal)543return ERROR_CODE(E_INVALID_HANDLE);544BmSetPortalHandle(p_BmPortal->h_Bm, NULL, (e_DpaaSwPortal)p_BmPortal->cpu);545bman_destroy_portal(p_BmPortal);546XX_Free(p_BmPortal);547return E_OK;548}549550t_Error BM_PORTAL_ConfigMemAttr(t_Handle h_BmPortal, uint32_t hwExtStructsMemAttr)551{552t_BmPortal *p_BmPortal = (t_BmPortal *)h_BmPortal;553554SANITY_CHECK_RETURN_ERROR(p_BmPortal, E_INVALID_HANDLE);555SANITY_CHECK_RETURN_ERROR(p_BmPortal->p_BmPortalDriverParams, E_INVALID_HANDLE);556557p_BmPortal->p_BmPortalDriverParams->hwExtStructsMemAttr = hwExtStructsMemAttr;558559return E_OK;560}561562563