/* * Copyright (c) 2005, Adaptive Digital Technologies, Inc. * Copyright (c) 2005-2010, Digium Incorporated * * File Name: GpakCust.c * * Description: * This file contains host system dependent functions to support generic * G.PAK API functions. The file is integrated into the host processor * connected to C55x G.PAK DSPs via a Host Port Interface. * * Note: This file is supplied by Adaptive Digital Technologies and * modified by Digium in order to support the VPMADT032 modules. * * This program has been released under the terms of the GPL version 2 by * permission of Adaptive Digital Technologies, Inc. * */ /* * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2 as published by the * Free Software Foundation. See the LICENSE file included with * this program for more details. */ #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) #include #else #include #endif #include #include #include "GpakCust.h" #include "GpakApi.h" #include "adt_lec.h" #include "voicebus.h" #include "vpmadtreg.h" static rwlock_t ifacelock; static struct vpmadt032 *ifaces[MAX_DSP_CORES]; #define vpm_info(vpm, format, arg...) \ dev_info(&vpm->vb->pdev->dev , format , ## arg) static inline struct vpmadt032 *find_iface(const unsigned short dspid) { struct vpmadt032 *ret; read_lock(&ifacelock); if (ifaces[dspid]) { ret = ifaces[dspid]; } else { ret = NULL; } read_unlock(&ifacelock); return ret; } static struct vpmadt032_cmd *vpmadt032_get_free_cmd(struct vpmadt032 *vpm) { struct vpmadt032_cmd *cmd; might_sleep(); cmd = kmalloc(sizeof(struct vpmadt032_cmd), GFP_KERNEL); if (unlikely(!cmd)) return NULL; memset(cmd, 0, sizeof(*cmd)); init_completion(&cmd->complete); return cmd; } /* Wait for any outstanding commands to the VPMADT032 to complete */ static inline int vpmadt032_io_wait(struct vpmadt032 *vpm) { unsigned long flags; int empty; while (1) { spin_lock_irqsave(&vpm->list_lock, flags); empty = list_empty(&vpm->pending_cmds) && list_empty(&vpm->active_cmds); spin_unlock_irqrestore(&vpm->list_lock, flags); if (empty) { break; } else { msleep(1); } } return 0; } /* Issue a read command to a register on the VPMADT032. We'll get the results * later. */ static struct vpmadt032_cmd *vpmadt032_getreg_full_async(struct vpmadt032 *vpm, int pagechange, unsigned short addr) { unsigned long flags; struct vpmadt032_cmd *cmd; cmd = vpmadt032_get_free_cmd(vpm); if (!cmd) return NULL; cmd->desc = (pagechange) ? __VPM150M_RWPAGE | __VPM150M_RD : __VPM150M_RD; cmd->address = addr; cmd->data = 0; spin_lock_irqsave(&vpm->list_lock, flags); list_add_tail(&cmd->node, &vpm->pending_cmds); spin_unlock_irqrestore(&vpm->list_lock, flags); return cmd; } /* Get the results from a previous call to vpmadt032_getreg_full_async. */ static int vpmadt032_getreg_full_return(struct vpmadt032 *vpm, int pagechange, u16 addr, u16 *outbuf, struct vpmadt032_cmd *cmd) { unsigned long flags; unsigned long ret; BUG_ON(!cmd); /* We'll wait for 200ms */ ret = wait_for_completion_timeout(&cmd->complete, HZ/5); if (unlikely(!ret)) { spin_lock_irqsave(&vpm->list_lock, flags); list_del(&cmd->node); spin_unlock_irqrestore(&vpm->list_lock, flags); kfree(cmd); return -EIO; } if (cmd->desc & __VPM150M_FIN) { *outbuf = cmd->data; cmd->desc = 0; ret = 0; } list_del(&cmd->node); kfree(cmd); return 0; } /* Read one of the registers on the VPMADT032 */ static int vpmadt032_getreg_full(struct vpmadt032 *vpm, int pagechange, u16 addr, u16 *outbuf) { struct vpmadt032_cmd *cmd; cmd = vpmadt032_getreg_full_async(vpm, pagechange, addr); if (unlikely(!cmd)) { return -ENOMEM; } return vpmadt032_getreg_full_return(vpm, pagechange, addr, outbuf, cmd); } static int vpmadt032_setreg_full(struct vpmadt032 *vpm, int pagechange, unsigned int addr, u16 data) { unsigned long flags; struct vpmadt032_cmd *cmd; cmd = vpmadt032_get_free_cmd(vpm); if (!cmd) return -ENOMEM; cmd->desc = cpu_to_le16((pagechange) ? (__VPM150M_WR|__VPM150M_RWPAGE) : __VPM150M_WR); cmd->address = cpu_to_le16(addr); cmd->data = cpu_to_le16(data); spin_lock_irqsave(&vpm->list_lock, flags); list_add_tail(&cmd->node, &vpm->pending_cmds); spin_unlock_irqrestore(&vpm->list_lock, flags); return 0; } static int vpmadt032_setpage(struct vpmadt032 *vpm, u16 addr) { addr &= 0xf; /* We do not need to set the page if we're already on the page we're * interested in. */ if (vpm->curpage == addr) return 0; else vpm->curpage = addr; return vpmadt032_setreg_full(vpm, 1, 0, addr); } static unsigned char vpmadt032_getpage(struct vpmadt032 *vpm) { unsigned short res; const int pagechange = 1; vpmadt032_getreg_full(vpm, pagechange, 0, &res); return res; } static int vpmadt032_getreg(struct vpmadt032 *vpm, unsigned int addr, u16 *data) { unsigned short res; vpmadt032_setpage(vpm, addr >> 16); res = vpmadt032_getreg_full(vpm, 0, addr & 0xffff, data); return res; } static int vpmadt032_setreg(struct vpmadt032 *vpm, unsigned int addr, u16 data) { int res; vpmadt032_setpage(vpm, addr >> 16); res = vpmadt032_setreg_full(vpm, 0, addr & 0xffff, data); return res; } struct change_order { struct list_head node; unsigned int channel; struct adt_lec_params params; }; static struct change_order *alloc_change_order(void) { return kzalloc(sizeof(struct change_order), GFP_ATOMIC); } static void free_change_order(struct change_order *order) { kfree(order); } static int vpmadt032_control(struct vpmadt032 *vpm, unsigned short int channel, GpakAlgCtrl_t control_code, GPAK_AlgControlStat_t *pstatus) { gpakAlgControlStat_t stat; int retry = 4; while (retry--) { stat = gpakAlgControl(vpm->dspid, channel, control_code, pstatus); if (AcDspCommFailure == stat) msleep(5); else break; } return stat; } static int vpmadt032_enable_ec(struct vpmadt032 *vpm, int channel, enum adt_companding companding) { int res; GPAK_AlgControlStat_t pstatus; GpakAlgCtrl_t control; control = (ADT_COMP_ALAW == companding) ? EnableALawSwCompanding : EnableMuLawSwCompanding; if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) { const char *law; law = (control == EnableMuLawSwCompanding) ? "MuLaw" : "ALaw"; vpm_info(vpm, "Enabling ecan on channel: %d (%s)\n", channel, law); } res = vpmadt032_control(vpm, channel, control, &pstatus); if (res) { vpm_info(vpm, "Unable to set SW Companding on " "channel %d (reason %d)\n", channel, res); } res = vpmadt032_control(vpm, channel, EnableEcanA, &pstatus); return res; } static int vpmadt032_disable_ec(struct vpmadt032 *vpm, int channel) { int res; GPAK_AlgControlStat_t pstatus; if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) vpm_info(vpm, "Disabling ecan on channel: %d\n", channel); res = vpmadt032_control(vpm, channel, BypassSwCompanding, &pstatus); if (res) { vpm_info(vpm, "Unable to disable sw companding on " "echo cancellation channel %d (reason %d)\n", channel, res); } res = vpmadt032_control(vpm, channel, BypassEcanA, &pstatus); return res; } static struct change_order *get_next_order(struct vpmadt032 *vpm) { struct change_order *order; spin_lock(&vpm->change_list_lock); if (!list_empty(&vpm->change_list)) { order = list_entry(vpm->change_list.next, struct change_order, node); list_del(&order->node); } else { order = NULL; } spin_unlock(&vpm->change_list_lock); return order; } static int nlp_settings_changed(const struct adt_lec_params *a, const struct adt_lec_params *b) { return ((a->nlp_type != b->nlp_type) || (a->nlp_threshold != b->nlp_threshold) || (a->nlp_max_suppress != b->nlp_max_suppress)); } static int echocan_on(const struct adt_lec_params *new, const struct adt_lec_params *old) { return ((new->tap_length != old->tap_length) && (new->tap_length > 0)); } static int echocan_off(const struct adt_lec_params *new, const struct adt_lec_params *old) { return ((new->tap_length != old->tap_length) && (0 == new->tap_length)); } static void update_channel_config(struct vpmadt032 *vpm, unsigned int channel, struct adt_lec_params *desired) { int res; GPAK_AlgControlStat_t pstatus; GPAK_ChannelConfigStat_t cstatus; GPAK_TearDownChanStat_t tstatus; GpakChannelConfig_t chanconfig; if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) { vpm_info(vpm, "Reconfiguring chan %d for nlp %d, " "nlp_thresh %d, and max_supp %d\n", channel + 1, desired->nlp_type, desired->nlp_threshold, desired->nlp_max_suppress); } vpm->setchanconfig_from_state(vpm, channel, &chanconfig); res = gpakTearDownChannel(vpm->dspid, channel, &tstatus); if (res) return; res = gpakConfigureChannel(vpm->dspid, channel, tdmToTdm, &chanconfig, &cstatus); if (res) return; if (!desired->tap_length) { res = vpmadt032_control(vpm, channel, BypassSwCompanding, &pstatus); if (res) { vpm_info(vpm, "Unable to disable sw companding on " "echo cancellation channel %d (reason %d)\n", channel, res); } vpmadt032_control(vpm, channel, BypassEcanA, &pstatus); } return; } /** * vpmadt032_bh - Changes the echocan parameters on the vpmadt032 module. * * This function is typically scheduled to run in the workqueue by the * vpmadt032_echocan_with_params function. This is because communicating with * the hardware can take some time while messages are sent to the VPMADT032 * module and the driver waits for the responses. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) static void vpmadt032_bh(void *data) { struct vpmadt032 *vpm = data; #else static void vpmadt032_bh(struct work_struct *data) { struct vpmadt032 *vpm = container_of(data, struct vpmadt032, work); #endif struct change_order *order; while ((order = get_next_order(vpm))) { struct adt_lec_params *old; struct adt_lec_params *new; unsigned int channel; channel = order->channel; BUG_ON(channel >= ARRAY_SIZE(vpm->curecstate)); old = &vpm->curecstate[channel]; new = &order->params; if (nlp_settings_changed(new, old)) update_channel_config(vpm, channel, new); else if (echocan_on(new, old)) vpmadt032_enable_ec(vpm, channel, new->companding); else if (echocan_off(new, old)) vpmadt032_disable_ec(vpm, channel); *old = order->params; free_change_order(order); } } #include "adt_lec.c" static void vpmadt032_check_and_schedule_update(struct vpmadt032 *vpm, struct change_order *order) { struct change_order *cur; struct change_order *n; INIT_LIST_HEAD(&order->node); spin_lock(&vpm->change_list_lock); list_for_each_entry_safe(cur, n, &vpm->change_list, node) { if (cur->channel == order->channel) { list_replace(&cur->node, &order->node); free_change_order(cur); break; } } if (list_empty(&order->node)) list_add_tail(&order->node, &vpm->change_list); spin_unlock(&vpm->change_list_lock); queue_work(vpm->wq, &vpm->work); } int vpmadt032_echocan_create(struct vpmadt032 *vpm, int channo, enum adt_companding companding, struct dahdi_echocanparams *ecp, struct dahdi_echocanparam *p) { unsigned int ret; struct change_order *order = alloc_change_order(); if (!order) return -ENOMEM; memcpy(&order->params, &vpm->curecstate[channo], sizeof(order->params)); ret = adt_lec_parse_params(&order->params, ecp, p); if (ret) { free_change_order(order); return ret; } if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) { vpm_info(vpm, "Channel is %d length %d\n", channo, ecp->tap_length); } /* The driver cannot control the number of taps on the VPMADT032 * module. Instead, it uses tap_length to enable or disable the echo * cancellation. */ order->params.tap_length = (ecp->tap_length) ? 1 : 0; order->params.companding = companding; order->channel = channo; vpmadt032_check_and_schedule_update(vpm, order); return 0; } EXPORT_SYMBOL(vpmadt032_echocan_create); void vpmadt032_echocan_free(struct vpmadt032 *vpm, int channo, struct dahdi_echocan_state *ec) { struct change_order *order; order = alloc_change_order(); WARN_ON(!order); if (!order) return; adt_lec_init_defaults(&order->params, 0); order->params.nlp_type = vpm->options.vpmnlptype; order->params.nlp_threshold = vpm->options.vpmnlpthresh; order->params.nlp_max_suppress = vpm->options.vpmnlpmaxsupp; order->channel = channo; if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) vpm_info(vpm, "Channel is %d length 0\n", channo); vpmadt032_check_and_schedule_update(vpm, order); } EXPORT_SYMBOL(vpmadt032_echocan_free); struct vpmadt032 * vpmadt032_alloc(struct vpmadt032_options *options, const char *board_name) { struct vpmadt032 *vpm; int i; const char *suffix = "-vpm"; size_t length; might_sleep(); length = strlen(board_name) + strlen(suffix) + 1; /* Add a little extra to store the wq_name. */ vpm = kzalloc(sizeof(*vpm) + length, GFP_KERNEL); if (!vpm) return NULL; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) /* There is a limit on the length of the name of the workqueue. */ strcpy(vpm->wq_name, "vpmadt032"); #else strcpy(vpm->wq_name, board_name); strcat(vpm->wq_name, suffix); #endif /* Init our vpmadt032 struct */ memcpy(&vpm->options, options, sizeof(*options)); spin_lock_init(&vpm->list_lock); spin_lock_init(&vpm->change_list_lock); INIT_LIST_HEAD(&vpm->change_list); INIT_LIST_HEAD(&vpm->pending_cmds); INIT_LIST_HEAD(&vpm->active_cmds); sema_init(&vpm->sem, 1); vpm->curpage = 0x80; vpm->dspid = -1; /* Do not use the global workqueue for processing these events. Some of * the operations can take 100s of ms, most of that time spent sleeping. * On single CPU systems, this unduly serializes operations accross * multiple vpmadt032 instances. */ vpm->wq = create_singlethread_workqueue(vpm->wq_name); if (!vpm->wq) { kfree(vpm); return NULL; } /* Place this structure in the ifaces array so that the DspId from the * Gpak Library can be used to locate it. */ write_lock(&ifacelock); for (i=0; idspid = i; break; } } write_unlock(&ifacelock); if (-1 == vpm->dspid) { kfree(vpm); dev_notice(&vpm->vb->pdev->dev, "Unable to initialize another vpmadt032 modules\n"); vpm = NULL; } return vpm; } EXPORT_SYMBOL(vpmadt032_alloc); int vpmadt032_reset(struct vpmadt032 *vpm) { int res; gpakPingDspStat_t pingstatus; u16 version; might_sleep(); set_bit(VPM150M_HPIRESET, &vpm->control); msleep(2000); while (test_bit(VPM150M_HPIRESET, &vpm->control)) msleep(1); /* Set us up to page 0 */ vpmadt032_setpage(vpm, 0); res = vpmadtreg_loadfirmware(vpm->vb); if (res) { dev_info(&vpm->vb->pdev->dev, "Failed to load the firmware.\n"); return res; } vpm->curpage = -1; set_bit(VPM150M_SWRESET, &vpm->control); while (test_bit(VPM150M_SWRESET, &vpm->control)) msleep(1); /* Set us up to page 0 */ pingstatus = gpakPingDsp(vpm->dspid, &version); if (!pingstatus) { vpm_info(vpm, "VPM present and operational " "(Firmware version %x)\n", version); vpm->version = version; res = 0; } else { res = -EIO; } return res; } EXPORT_SYMBOL(vpmadt032_reset); int vpmadt032_init(struct vpmadt032 *vpm, struct voicebus *vb) { int i; u16 reg; int res = -EFAULT; gpakPingDspStat_t pingstatus; BUG_ON(!vpm->setchanconfig_from_state); BUG_ON(!vpm->wq); BUG_ON(!vb); vpm->vb = vb; might_sleep(); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) INIT_WORK(&vpm->work, vpmadt032_bh, vpm); #else INIT_WORK(&vpm->work, vpmadt032_bh); #endif if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) dev_info(&vpm->vb->pdev->dev, "VPMADT032 Testing page access: "); for (i = 0; i < 0xf; i++) { int x; for (x = 0; x < 3; x++) { vpmadt032_setpage(vpm, i); reg = vpmadt032_getpage(vpm); if (reg != i) { if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) dev_info(&vpm->vb->pdev->dev, "Failed: Sent %x != %x VPMADT032 Failed HI page test\n", i, reg); res = -ENODEV; goto failed_exit; } } } if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) dev_info(&vpm->vb->pdev->dev, "Passed\n"); set_bit(VPM150M_HPIRESET, &vpm->control); while (test_bit(VPM150M_HPIRESET, &vpm->control)) msleep(1); msleep(250); /* Set us up to page 0 */ vpmadt032_setpage(vpm, 0); if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) dev_info(&vpm->vb->pdev->dev, "VPMADT032 now doing address test: "); for (i = 0; i < 16; i++) { int x; for (x = 0; x < 2; x++) { vpmadt032_setreg(vpm, 0x1000, i); vpmadt032_getreg(vpm, 0x1000, ®); if (reg != i) { printk(KERN_CONT "VPMADT032 Failed address test\n"); res = -EIO; } } } if (vpm->options.debug & DEBUG_VPMADT032_ECHOCAN) printk(KERN_CONT "Passed\n"); set_bit(VPM150M_HPIRESET, &vpm->control); while (test_bit(VPM150M_HPIRESET, &vpm->control)) msleep(1); res = vpmadtreg_loadfirmware(vb); if (res) { dev_info(&vb->pdev->dev, "Failed to load the firmware.\n"); return res; } vpm->curpage = -1; dev_info(&vb->pdev->dev, "Booting VPMADT032\n"); set_bit(VPM150M_SWRESET, &vpm->control); while (test_bit(VPM150M_SWRESET, &vpm->control)) msleep(1); pingstatus = gpakPingDsp(vpm->dspid, &vpm->version); if (!pingstatus) { dev_info(&vb->pdev->dev, "VPM present and operational " "(Firmware version %x)\n", vpm->version); } else { dev_notice(&vpm->vb->pdev->dev, "VPMADT032 Failed! Unable to ping the DSP (%d)!\n", pingstatus); res = -EIO; goto failed_exit; } return 0; failed_exit: return res; } EXPORT_SYMBOL(vpmadt032_init); void vpmadt032_get_default_parameters(struct GpakEcanParms *p) { memset(p, 0, sizeof(*p)); p->EcanTapLength = 1024; p->EcanNlpType = DEFAULT_NLPTYPE; p->EcanAdaptEnable = 1; p->EcanG165DetEnable = 1; p->EcanDblTalkThresh = 6; p->EcanMaxDoubleTalkThres = 40; p->EcanNlpThreshold = DEFAULT_NLPTHRESH; p->EcanNlpConv = 18; p->EcanNlpUnConv = 12; p->EcanNlpMaxSuppress = DEFAULT_NLPMAXSUPP; p->EcanCngThreshold = 43; p->EcanAdaptLimit = 50; p->EcanCrossCorrLimit = 15; p->EcanNumFirSegments = 3; p->EcanFirSegmentLen = 48; p->EcanReconvergenceCheckEnable = 2; p->EcanTandemOperationEnable = 0; p->EcanMixedFourWireMode = 0; } EXPORT_SYMBOL(vpmadt032_get_default_parameters); void vpmadt032_free(struct vpmadt032 *vpm) { unsigned long flags; struct vpmadt032_cmd *cmd; struct change_order *order; LIST_HEAD(local_list); if (!vpm) return; BUG_ON(!vpm->wq); destroy_workqueue(vpm->wq); /* Move all the commands onto the local list protected by the locks */ spin_lock_irqsave(&vpm->list_lock, flags); list_splice(&vpm->pending_cmds, &local_list); list_splice(&vpm->active_cmds, &local_list); spin_unlock_irqrestore(&vpm->list_lock, flags); while (!list_empty(&local_list)) { cmd = list_entry(local_list.next, struct vpmadt032_cmd, node); list_del(&cmd->node); kfree(cmd); } spin_lock(&vpm->change_list_lock); list_splice(&vpm->change_list, &local_list); spin_unlock(&vpm->change_list_lock); while (!list_empty(&local_list)) { order = list_entry(local_list.next, struct change_order, node); list_del(&order->node); kfree(order); } BUG_ON(ifaces[vpm->dspid] != vpm); write_lock(&ifacelock); ifaces[vpm->dspid] = NULL; write_unlock(&ifacelock); kfree(vpm); } EXPORT_SYMBOL(vpmadt032_free); int vpmadt032_module_init(void) { rwlock_init(&ifacelock); memset(ifaces, 0, sizeof(ifaces)); return 0; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * gpakReadDspMemory - Read DSP memory. * * FUNCTION * This function reads a contiguous block of words from DSP memory starting at * the specified address. * * RETURNS * nothing * */ void gpakReadDspMemory( unsigned short int DspId, /* DSP Identifier (0 to MAX_DSP_CORES-1) */ DSP_ADDRESS DspAddress, /* DSP's memory address of first word */ unsigned int NumWords, /* number of contiguous words to read */ DSP_WORD *pWordValues /* pointer to array of word values variable */ ) { struct vpmadt032 *vpm = find_iface(DspId); int i; int ret; vpmadt032_io_wait(vpm); if ( NumWords < VPM150M_MAX_COMMANDS ) { struct vpmadt032_cmd *cmds[VPM150M_MAX_COMMANDS]; memset(cmds, 0, sizeof(cmds)); vpmadt032_setpage(vpm, DspAddress >> 16); DspAddress &= 0xffff; for (i=0; i < NumWords; ++i) { if (!(cmds[i] = vpmadt032_getreg_full_async(vpm,0,DspAddress+i))) { return; } } for (i=NumWords-1; i >=0; --i) { ret = vpmadt032_getreg_full_return(vpm,0,DspAddress+i,&pWordValues[i], cmds[i]); if (0 != ret) { return; } } } else { for (i=0; ivb->pdev->dev, "Writing %d words to memory\n", NumWords); if (vpm) { for (i = 0; i < NumWords; ++i) { vpmadt032_setreg(vpm, DspAddress + i, pWordValues[i]); } #if 0 for (i = 0; i < NumWords; i++) { if (wctdm_vpmadt032_getreg(wc, DspAddress + i) != pWordValues[i]) { dev_notice(&vpm->vb->pdev->dev, "Error in write. Address %x is not %x\n", DspAddress + i, pWordValues[i]); } } #endif } return; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * gpakHostDelay - Delay for a fixed time interval. * * FUNCTION * This function delays for a fixed time interval before returning. The time * interval is the Host Port Interface sampling period when polling a DSP for * replies to command messages. * * RETURNS * nothing * */ void gpakHostDelay(void) { } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * gpakLockAccess - Lock access to the specified DSP. * * FUNCTION * This function aquires exclusive access to the specified DSP. * * RETURNS * nothing * */ void gpakLockAccess(unsigned short DspId) { struct vpmadt032 *vpm; vpm = find_iface(DspId); if (!vpm) return; down(&vpm->sem); return; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * gpakUnlockAccess - Unlock access to the specified DSP. * * FUNCTION * This function releases exclusive access to the specified DSP. * * RETURNS * nothing * */ void gpakUnlockAccess(unsigned short DspId) { struct vpmadt032 *vpm; vpm = find_iface(DspId); if (vpm) { up(&vpm->sem); } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * gpakReadFile - Read a block of bytes from a G.PAK Download file. * * FUNCTION * This function reads a contiguous block of bytes from a G.PAK Download file * starting at the current file position. * * RETURNS * The number of bytes read from the file. * -1 indicates an error occurred. * 0 indicates all bytes have been read (end of file) * */ int gpakReadFile( GPAK_FILE_ID FileId, /* G.PAK Download File Identifier */ unsigned char *pBuffer, /* pointer to buffer for storing bytes */ unsigned int NumBytes /* number of bytes to read */ ) { /* The firmware is loaded into the part by a closed-source firmware * loader, and therefore this function should never be called. */ WARN_ON(1); return -1; }