/* * Dongle BUS interface for USB, OS independent * * Copyright (C) 1999-2016, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that * you also meet, for each linked independent module, the terms and conditions of * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * * * <> * * $Id: dbus_usb.c 565557 2015-06-22 19:29:44Z $ */ /** * @file @brief * This file contains DBUS code that is USB, but not OS specific. DBUS is a Broadcom proprietary * host specific abstraction layer. */ #include #include #include #include #include #include #include uint dbus_msglevel = DBUS_ERROR_VAL; module_param(dbus_msglevel, int, 0); #define USB_DLIMAGE_RETRY_TIMEOUT 3000 /* retry Timeout */ #define USB_SFLASH_DLIMAGE_SPINWAIT 150 /* in unit of ms */ #define USB_SFLASH_DLIMAGE_LIMIT 2000 /* spinwait limit (ms) */ #define POSTBOOT_ID 0xA123 /* ID to detect if dongle has boot up */ #define USB_RESETCFG_SPINWAIT 1 /* wait after resetcfg (ms) */ #define USB_DEV_ISBAD(u) (u->pub->attrib.devid == 0xDEAD) #define USB_DLGO_SPINWAIT 100 /* wait after DL_GO (ms) */ #define TEST_CHIP 0x4328 typedef struct { dbus_pub_t *pub; void *cbarg; dbus_intf_callbacks_t *cbs; /** callbacks into higher DBUS level (dbus.c) */ dbus_intf_t *drvintf; void *usbosl_info; uint32 rdlram_base_addr; uint32 rdlram_size; } usb_info_t; /* * Callbacks common to all USB */ static void dbus_usb_disconnect(void *handle); static void dbus_usb_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb); static void dbus_usb_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status); static void dbus_usb_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status); static void dbus_usb_errhandler(void *handle, int err); static void dbus_usb_ctl_complete(void *handle, int type, int status); static void dbus_usb_state_change(void *handle, int state); static struct dbus_irb* dbus_usb_getirb(void *handle, bool send); static void dbus_usb_rxerr_indicate(void *handle, bool on); #if !defined(BCM_REQUEST_FW) static int dbus_usb_resetcfg(usb_info_t *usbinfo); #endif static int dbus_usb_iovar_op(void *bus, const char *name, void *params, int plen, void *arg, int len, bool set); static int dbus_iovar_process(usb_info_t* usbinfo, const char *name, void *params, int plen, void *arg, int len, bool set); static int dbus_usb_doiovar(usb_info_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name, void *params, int plen, void *arg, int len, int val_size); static int dhdusb_downloadvars(usb_info_t *bus, void *arg, int len); static int dbus_usb_dl_writeimage(usb_info_t *usbinfo, uint8 *fw, int fwlen); static int dbus_usb_dlstart(void *bus, uint8 *fw, int len); static int dbus_usb_dlneeded(void *bus); static int dbus_usb_dlrun(void *bus); static int dbus_usb_rdl_dwnld_state(usb_info_t *usbinfo); /* OS specific */ extern bool dbus_usbos_dl_cmd(void *info, uint8 cmd, void *buffer, int buflen); extern int dbus_usbos_wait(void *info, uint16 ms); extern int dbus_write_membytes(usb_info_t *usbinfo, bool set, uint32 address, uint8 *data, uint size); extern bool dbus_usbos_dl_send_bulk(void *info, void *buffer, int len); extern int dbus_usbos_loopback_tx(void *usbos_info_ptr, int cnt, int size); /** * These functions are called by the lower DBUS level (dbus_usb_os.c) to notify this DBUS level * (dbus_usb.c) of an event. */ static dbus_intf_callbacks_t dbus_usb_intf_cbs = { dbus_usb_send_irb_timeout, dbus_usb_send_irb_complete, dbus_usb_recv_irb_complete, dbus_usb_errhandler, dbus_usb_ctl_complete, dbus_usb_state_change, NULL, /* isr */ NULL, /* dpc */ NULL, /* watchdog */ NULL, /* dbus_if_pktget */ NULL, /* dbus_if_pktfree */ dbus_usb_getirb, dbus_usb_rxerr_indicate }; /* IOVar table */ enum { IOV_SET_DOWNLOAD_STATE = 1, IOV_DBUS_MSGLEVEL, IOV_MEMBYTES, IOV_VARS, IOV_LOOPBACK_TX }; const bcm_iovar_t dhdusb_iovars[] = { {"vars", IOV_VARS, 0, IOVT_BUFFER, 0 }, {"dbus_msglevel", IOV_DBUS_MSGLEVEL, 0, IOVT_UINT32, 0 }, {"dwnldstate", IOV_SET_DOWNLOAD_STATE, 0, IOVT_BOOL, 0 }, {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int) }, {"usb_lb_txfer", IOV_LOOPBACK_TX, 0, IOVT_BUFFER, 2 * sizeof(int) }, {NULL, 0, 0, 0, 0 } }; /* * Need global for probe() and disconnect() since * attach() is not called at probe and detach() * can be called inside disconnect() */ static probe_cb_t probe_cb = NULL; static disconnect_cb_t disconnect_cb = NULL; static void *probe_arg = NULL; static void *disc_arg = NULL; static dbus_intf_t *g_dbusintf = NULL; static dbus_intf_t dbus_usb_intf; /** functions called by higher layer DBUS into lower layer */ /* * dbus_intf_t common to all USB * These functions override dbus_usb_.c. */ static void *dbus_usb_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs); static void dbus_usb_detach(dbus_pub_t *pub, void *info); static void * dbus_usb_probe(void *arg, const char *desc, uint32 bustype, uint32 hdrlen); /* functions */ /** * As part of DBUS initialization/registration, the higher level DBUS (dbus.c) needs to know what * lower level DBUS functions to call (in both dbus_usb.c and dbus_usb_os.c). */ static void * dbus_usb_probe(void *arg, const char *desc, uint32 bustype, uint32 hdrlen) { DBUSTRACE(("%s(): \n", __FUNCTION__)); if (probe_cb) { if (g_dbusintf != NULL) { /* First, initialize all lower-level functions as default * so that dbus.c simply calls directly to dbus_usb_os.c. */ bcopy(g_dbusintf, &dbus_usb_intf, sizeof(dbus_intf_t)); /* Second, selectively override functions we need, if any. */ dbus_usb_intf.attach = dbus_usb_attach; dbus_usb_intf.detach = dbus_usb_detach; dbus_usb_intf.iovar_op = dbus_usb_iovar_op; dbus_usb_intf.dlstart = dbus_usb_dlstart; dbus_usb_intf.dlneeded = dbus_usb_dlneeded; dbus_usb_intf.dlrun = dbus_usb_dlrun; } disc_arg = probe_cb(probe_arg, "DBUS USB", USB_BUS, hdrlen); return disc_arg; } return NULL; } /** * On return, *intf contains this or lower-level DBUS functions to be called by higher * level (dbus.c) */ int dbus_bus_register(int vid, int pid, probe_cb_t prcb, disconnect_cb_t discb, void *prarg, dbus_intf_t **intf, void *param1, void *param2) { int err; DBUSTRACE(("%s(): \n", __FUNCTION__)); probe_cb = prcb; disconnect_cb = discb; probe_arg = prarg; *intf = &dbus_usb_intf; err = dbus_bus_osl_register(vid, pid, dbus_usb_probe, dbus_usb_disconnect, NULL, &g_dbusintf, param1, param2); ASSERT(g_dbusintf); return err; } int dbus_bus_deregister() { DBUSTRACE(("%s(): \n", __FUNCTION__)); return dbus_bus_osl_deregister(); } /** initialization consists of registration followed by 'attach'. */ void * dbus_usb_attach(dbus_pub_t *pub, void *cbarg, dbus_intf_callbacks_t *cbs) { usb_info_t *usb_info; DBUSTRACE(("%s(): \n", __FUNCTION__)); if ((g_dbusintf == NULL) || (g_dbusintf->attach == NULL)) return NULL; /* Sanity check for BUS_INFO() */ ASSERT(OFFSETOF(usb_info_t, pub) == 0); usb_info = MALLOC(pub->osh, sizeof(usb_info_t)); if (usb_info == NULL) return NULL; bzero(usb_info, sizeof(usb_info_t)); usb_info->pub = pub; usb_info->cbarg = cbarg; usb_info->cbs = cbs; usb_info->usbosl_info = (dbus_pub_t *)g_dbusintf->attach(pub, usb_info, &dbus_usb_intf_cbs); if (usb_info->usbosl_info == NULL) { MFREE(pub->osh, usb_info, sizeof(usb_info_t)); return NULL; } /* Save USB OS-specific driver entry points */ usb_info->drvintf = g_dbusintf; pub->bus = usb_info; #if !defined(BCM_REQUEST_FW) if (!dbus_usb_resetcfg(usb_info)) { usb_info->pub->busstate = DBUS_STATE_DL_DONE; } #endif /* Return Lower layer info */ return (void *) usb_info->usbosl_info; } void dbus_usb_detach(dbus_pub_t *pub, void *info) { usb_info_t *usb_info = (usb_info_t *) pub->bus; osl_t *osh = pub->osh; if (usb_info == NULL) return; if (usb_info->drvintf && usb_info->drvintf->detach) usb_info->drvintf->detach(pub, usb_info->usbosl_info); MFREE(osh, usb_info, sizeof(usb_info_t)); } void dbus_usb_disconnect(void *handle) { DBUSTRACE(("%s(): \n", __FUNCTION__)); if (disconnect_cb) disconnect_cb(disc_arg); } /** * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be * notified. */ static void dbus_usb_send_irb_timeout(void *handle, dbus_irb_tx_t *txirb) { usb_info_t *usb_info = (usb_info_t *) handle; DBUSTRACE(("%s\n", __FUNCTION__)); if (usb_info == NULL) return; if (usb_info->cbs && usb_info->cbs->send_irb_timeout) usb_info->cbs->send_irb_timeout(usb_info->cbarg, txirb); } /** * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be * notified. */ static void dbus_usb_send_irb_complete(void *handle, dbus_irb_tx_t *txirb, int status) { usb_info_t *usb_info = (usb_info_t *) handle; if (usb_info == NULL) return; if (usb_info->cbs && usb_info->cbs->send_irb_complete) usb_info->cbs->send_irb_complete(usb_info->cbarg, txirb, status); } /** * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be * notified. */ static void dbus_usb_recv_irb_complete(void *handle, dbus_irb_rx_t *rxirb, int status) { usb_info_t *usb_info = (usb_info_t *) handle; if (usb_info == NULL) return; if (usb_info->cbs && usb_info->cbs->recv_irb_complete) usb_info->cbs->recv_irb_complete(usb_info->cbarg, rxirb, status); } /** Lower DBUS level (dbus_usb_os.c) requests a free IRB. Pass this on to the higher DBUS level. */ static struct dbus_irb* dbus_usb_getirb(void *handle, bool send) { usb_info_t *usb_info = (usb_info_t *) handle; if (usb_info == NULL) return NULL; if (usb_info->cbs && usb_info->cbs->getirb) return usb_info->cbs->getirb(usb_info->cbarg, send); return NULL; } /** * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be * notified. */ static void dbus_usb_rxerr_indicate(void *handle, bool on) { usb_info_t *usb_info = (usb_info_t *) handle; if (usb_info == NULL) return; if (usb_info->cbs && usb_info->cbs->rxerr_indicate) usb_info->cbs->rxerr_indicate(usb_info->cbarg, on); } /** * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be * notified. */ static void dbus_usb_errhandler(void *handle, int err) { usb_info_t *usb_info = (usb_info_t *) handle; if (usb_info == NULL) return; if (usb_info->cbs && usb_info->cbs->errhandler) usb_info->cbs->errhandler(usb_info->cbarg, err); } /** * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be * notified. */ static void dbus_usb_ctl_complete(void *handle, int type, int status) { usb_info_t *usb_info = (usb_info_t *) handle; DBUSTRACE(("%s\n", __FUNCTION__)); if (usb_info == NULL) { DBUSERR(("%s: usb_info is NULL\n", __FUNCTION__)); return; } if (usb_info->cbs && usb_info->cbs->ctl_complete) usb_info->cbs->ctl_complete(usb_info->cbarg, type, status); } /** * When the lower DBUS level (dbus_usb_os.c) signals this event, the higher DBUS level has to be * notified. */ static void dbus_usb_state_change(void *handle, int state) { usb_info_t *usb_info = (usb_info_t *) handle; if (usb_info == NULL) return; if (usb_info->cbs && usb_info->cbs->state_change) usb_info->cbs->state_change(usb_info->cbarg, state); } /** called by higher DBUS level (dbus.c) */ static int dbus_usb_iovar_op(void *bus, const char *name, void *params, int plen, void *arg, int len, bool set) { int err = DBUS_OK; err = dbus_iovar_process((usb_info_t*)bus, name, params, plen, arg, len, set); return err; } /** process iovar request from higher DBUS level */ static int dbus_iovar_process(usb_info_t* usbinfo, const char *name, void *params, int plen, void *arg, int len, bool set) { const bcm_iovar_t *vi = NULL; int bcmerror = 0; int val_size; uint32 actionid; DBUSTRACE(("%s: Enter\n", __FUNCTION__)); ASSERT(name); ASSERT(len >= 0); /* Get MUST have return space */ ASSERT(set || (arg && len)); /* Set does NOT take qualifiers */ ASSERT(!set || (!params && !plen)); /* Look up var locally; if not found pass to host driver */ if ((vi = bcm_iovar_lookup(dhdusb_iovars, name)) == NULL) { /* Not Supported */ bcmerror = BCME_UNSUPPORTED; DBUSTRACE(("%s: IOVAR %s is not supported\n", name, __FUNCTION__)); goto exit; } DBUSTRACE(("%s: %s %s, len %d plen %d\n", __FUNCTION__, name, (set ? "set" : "get"), len, plen)); /* set up 'params' pointer in case this is a set command so that * the convenience int and bool code can be common to set and get */ if (params == NULL) { params = arg; plen = len; } if (vi->type == IOVT_VOID) val_size = 0; else if (vi->type == IOVT_BUFFER) val_size = len; else /* all other types are integer sized */ val_size = sizeof(int); actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); bcmerror = dbus_usb_doiovar(usbinfo, vi, actionid, name, params, plen, arg, len, val_size); exit: return bcmerror; } /* dbus_iovar_process */ static int dbus_usb_doiovar(usb_info_t *bus, const bcm_iovar_t *vi, uint32 actionid, const char *name, void *params, int plen, void *arg, int len, int val_size) { int bcmerror = 0; int32 int_val = 0; int32 int_val2 = 0; bool bool_val = 0; DBUSTRACE(("%s: Enter, action %d name %s params %p plen %d arg %p len %d val_size %d\n", __FUNCTION__, actionid, name, params, plen, arg, len, val_size)); if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0) goto exit; if (plen >= (int)sizeof(int_val)) bcopy(params, &int_val, sizeof(int_val)); if (plen >= (int)sizeof(int_val) * 2) bcopy((void*)((uintptr)params + sizeof(int_val)), &int_val2, sizeof(int_val2)); bool_val = (int_val != 0) ? TRUE : FALSE; switch (actionid) { case IOV_SVAL(IOV_MEMBYTES): case IOV_GVAL(IOV_MEMBYTES): { uint32 address; uint size, dsize; uint8 *data; bool set = (actionid == IOV_SVAL(IOV_MEMBYTES)); ASSERT(plen >= 2*sizeof(int)); address = (uint32)int_val; BCM_REFERENCE(address); bcopy((char *)params + sizeof(int_val), &int_val, sizeof(int_val)); size = (uint)int_val; /* Do some validation */ dsize = set ? plen - (2 * sizeof(int)) : len; if (dsize < size) { DBUSTRACE(("%s: error on %s membytes, addr 0x%08x size %d dsize %d\n", __FUNCTION__, (set ? "set" : "get"), address, size, dsize)); bcmerror = BCME_BADARG; break; } DBUSTRACE(("%s: Request to %s %d bytes at address 0x%08x\n", __FUNCTION__, (set ? "write" : "read"), size, address)); /* Generate the actual data pointer */ data = set ? (uint8*)params + 2 * sizeof(int): (uint8*)arg; /* Call to do the transfer */ bcmerror = dbus_usb_dl_writeimage(BUS_INFO(bus, usb_info_t), data, size); } break; case IOV_SVAL(IOV_SET_DOWNLOAD_STATE): if (bool_val == TRUE) { bcmerror = dbus_usb_dlneeded(bus); dbus_usb_rdl_dwnld_state(BUS_INFO(bus, usb_info_t)); } else { usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); bcmerror = dbus_usb_dlrun(bus); usbinfo->pub->busstate = DBUS_STATE_DL_DONE; } break; case IOV_SVAL(IOV_VARS): bcmerror = dhdusb_downloadvars(BUS_INFO(bus, usb_info_t), arg, len); break; case IOV_GVAL(IOV_DBUS_MSGLEVEL): int_val = (int32)dbus_msglevel; bcopy(&int_val, arg, val_size); break; case IOV_SVAL(IOV_DBUS_MSGLEVEL): dbus_msglevel = int_val; break; #ifdef DBUS_USB_LOOPBACK case IOV_SVAL(IOV_LOOPBACK_TX): bcmerror = dbus_usbos_loopback_tx(BUS_INFO(bus, usb_info_t), int_val, int_val2); break; #endif default: bcmerror = BCME_UNSUPPORTED; break; } exit: return bcmerror; } /* dbus_usb_doiovar */ /** higher DBUS level (dbus.c) wants to set NVRAM variables in dongle */ static int dhdusb_downloadvars(usb_info_t *bus, void *arg, int len) { int bcmerror = 0; uint32 varsize; uint32 varaddr; uint32 varsizew; if (!len) { bcmerror = BCME_BUFTOOSHORT; goto err; } /* RAM size is not set. Set it at dbus_usb_dlneeded */ if (!bus->rdlram_size) bcmerror = BCME_ERROR; /* Even if there are no vars are to be written, we still need to set the ramsize. */ varsize = len ? ROUNDUP(len, 4) : 0; varaddr = (bus->rdlram_size - 4) - varsize; /* Write the vars list */ DBUSTRACE(("WriteVars: @%x varsize=%d\n", varaddr, varsize)); bcmerror = dbus_write_membytes(bus->usbosl_info, TRUE, (varaddr + bus->rdlram_base_addr), arg, varsize); /* adjust to the user specified RAM */ DBUSTRACE(("Usable memory size: %d\n", bus->rdlram_size)); DBUSTRACE(("Vars are at %d, orig varsize is %d\n", varaddr, varsize)); varsize = ((bus->rdlram_size - 4) - varaddr); /* * Determine the length token: * Varsize, converted to words, in lower 16-bits, checksum in upper 16-bits. */ if (bcmerror) { varsizew = 0; } else { varsizew = varsize / 4; varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF); varsizew = htol32(varsizew); } DBUSTRACE(("New varsize is %d, length token=0x%08x\n", varsize, varsizew)); /* Write the length token to the last word */ bcmerror = dbus_write_membytes(bus->usbosl_info, TRUE, ((bus->rdlram_size - 4) + bus->rdlram_base_addr), (uint8*)&varsizew, 4); err: return bcmerror; } /* dbus_usb_doiovar */ #if !defined(BCM_REQUEST_FW) /** * After downloading firmware into dongle and starting it, we need to know if the firmware is * indeed up and running. */ static int dbus_usb_resetcfg(usb_info_t *usbinfo) { void *osinfo; bootrom_id_t id; uint16 waittime = 0; uint32 starttime = 0; uint32 endtime = 0; DBUSTRACE(("%s\n", __FUNCTION__)); if (usbinfo == NULL) return DBUS_ERR; osinfo = usbinfo->usbosl_info; ASSERT(osinfo); /* Give dongle chance to boot */ dbus_usbos_wait(osinfo, USB_SFLASH_DLIMAGE_SPINWAIT); waittime = USB_SFLASH_DLIMAGE_SPINWAIT; while (waittime < USB_DLIMAGE_RETRY_TIMEOUT) { starttime = OSL_SYSUPTIME(); id.chip = 0xDEAD; /* Get the ID */ dbus_usbos_dl_cmd(osinfo, DL_GETVER, &id, sizeof(bootrom_id_t)); id.chip = ltoh32(id.chip); endtime = OSL_SYSUPTIME(); waittime += (endtime - starttime); if (id.chip == POSTBOOT_ID) break; } if (id.chip == POSTBOOT_ID) { DBUSERR(("%s: download done. Bootup time = %d ms postboot chip 0x%x/rev 0x%x\n", __FUNCTION__, waittime, id.chip, id.chiprev)); dbus_usbos_dl_cmd(osinfo, DL_RESETCFG, &id, sizeof(bootrom_id_t)); dbus_usbos_wait(osinfo, USB_RESETCFG_SPINWAIT); return DBUS_OK; } else { DBUSERR(("%s: Cannot talk to Dongle. Wait time = %d ms. Firmware is not UP \n", __FUNCTION__, waittime)); return DBUS_ERR; } return DBUS_OK; } #endif /** before firmware download, the dongle has to be prepared to receive the fw image */ static int dbus_usb_rdl_dwnld_state(usb_info_t *usbinfo) { void *osinfo = usbinfo->usbosl_info; rdl_state_t state; int err = DBUS_OK; /* 1) Prepare USB boot loader for runtime image */ dbus_usbos_dl_cmd(osinfo, DL_START, &state, sizeof(rdl_state_t)); state.state = ltoh32(state.state); state.bytes = ltoh32(state.bytes); /* 2) Check we are in the Waiting state */ if (state.state != DL_WAITING) { DBUSERR(("%s: Failed to DL_START\n", __FUNCTION__)); err = DBUS_ERR; goto fail; } fail: return err; } /** * Dongle contains bootcode in ROM but firmware is (partially) contained in dongle RAM. Therefore, * firmware has to be downloaded into dongle RAM. */ static int dbus_usb_dl_writeimage(usb_info_t *usbinfo, uint8 *fw, int fwlen) { osl_t *osh = usbinfo->pub->osh; void *osinfo = usbinfo->usbosl_info; unsigned int sendlen, sent, dllen; char *bulkchunk = NULL, *dlpos; rdl_state_t state; int err = DBUS_OK; bootrom_id_t id; uint16 wait, wait_time; uint32 dl_trunk_size = RDL_CHUNK; if (BCM4350_CHIP(usbinfo->pub->attrib.devid)) dl_trunk_size = RDL_CHUNK_MAX; while (!bulkchunk) { bulkchunk = MALLOC(osh, dl_trunk_size); if (dl_trunk_size == RDL_CHUNK) break; if (!bulkchunk) { dl_trunk_size /= 2; if (dl_trunk_size < RDL_CHUNK) dl_trunk_size = RDL_CHUNK; } } if (bulkchunk == NULL) { err = DBUS_ERR; goto fail; } sent = 0; dlpos = fw; dllen = fwlen; /* Get chip id and rev */ id.chip = usbinfo->pub->attrib.devid; id.chiprev = usbinfo->pub->attrib.chiprev; DBUSTRACE(("enter %s: fwlen=%d\n", __FUNCTION__, fwlen)); dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, sizeof(rdl_state_t)); /* 3) Load the image */ while ((sent < dllen)) { /* Wait until the usb device reports it received all the bytes we sent */ if (sent < dllen) { if ((dllen-sent) < dl_trunk_size) sendlen = dllen-sent; else sendlen = dl_trunk_size; /* simply avoid having to send a ZLP by ensuring we never have an even * multiple of 64 */ if (!(sendlen % 64)) sendlen -= 4; /* send data */ memcpy(bulkchunk, dlpos, sendlen); if (!dbus_usbos_dl_send_bulk(osinfo, bulkchunk, sendlen)) { err = DBUS_ERR; goto fail; } dlpos += sendlen; sent += sendlen; DBUSTRACE(("%s: sendlen %d\n", __FUNCTION__, sendlen)); } wait = 0; wait_time = USB_SFLASH_DLIMAGE_SPINWAIT; while (!dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, sizeof(rdl_state_t))) { if ((id.chip == 43236) && (id.chiprev == 0)) { DBUSERR(("%s: 43236a0 SFlash delay, waiting for dongle crc check " "completion!!!\n", __FUNCTION__)); dbus_usbos_wait(osinfo, wait_time); wait += wait_time; if (wait >= USB_SFLASH_DLIMAGE_LIMIT) { DBUSERR(("%s: DL_GETSTATE Failed xxxx\n", __FUNCTION__)); err = DBUS_ERR; goto fail; break; } } else { DBUSERR(("%s: DL_GETSTATE Failed xxxx\n", __FUNCTION__)); err = DBUS_ERR; goto fail; } } state.state = ltoh32(state.state); state.bytes = ltoh32(state.bytes); /* restart if an error is reported */ if ((state.state == DL_BAD_HDR) || (state.state == DL_BAD_CRC)) { DBUSERR(("%s: Bad Hdr or Bad CRC\n", __FUNCTION__)); err = DBUS_ERR; goto fail; } } fail: if (bulkchunk) MFREE(osh, bulkchunk, dl_trunk_size); return err; } /* dbus_usb_dl_writeimage */ /** Higher level DBUS layer (dbus.c) requests this layer to download image into dongle */ static int dbus_usb_dlstart(void *bus, uint8 *fw, int len) { usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); int err; DBUSTRACE(("%s\n", __FUNCTION__)); if (usbinfo == NULL) return DBUS_ERR; if (USB_DEV_ISBAD(usbinfo)) return DBUS_ERR; err = dbus_usb_rdl_dwnld_state(usbinfo); if (DBUS_OK == err) { err = dbus_usb_dl_writeimage(usbinfo, fw, len); if (err == DBUS_OK) usbinfo->pub->busstate = DBUS_STATE_DL_DONE; else usbinfo->pub->busstate = DBUS_STATE_DL_PENDING; } else usbinfo->pub->busstate = DBUS_STATE_DL_PENDING; return err; } static bool dbus_usb_update_chipinfo(usb_info_t *usbinfo, uint32 chip) { bool retval = TRUE; /* based on the CHIP Id, store the ram size which is needed for NVRAM download. */ switch (chip) { case 0x4319: usbinfo->rdlram_size = RDL_RAM_SIZE_4319; usbinfo->rdlram_base_addr = RDL_RAM_BASE_4319; break; case 0x4329: usbinfo->rdlram_size = RDL_RAM_SIZE_4329; usbinfo->rdlram_base_addr = RDL_RAM_BASE_4329; break; case 43234: case 43235: case 43236: usbinfo->rdlram_size = RDL_RAM_SIZE_43236; usbinfo->rdlram_base_addr = RDL_RAM_BASE_43236; break; case 0x4328: usbinfo->rdlram_size = RDL_RAM_SIZE_4328; usbinfo->rdlram_base_addr = RDL_RAM_BASE_4328; break; case 0x4322: usbinfo->rdlram_size = RDL_RAM_SIZE_4322; usbinfo->rdlram_base_addr = RDL_RAM_BASE_4322; break; case 0x4360: case 0xAA06: usbinfo->rdlram_size = RDL_RAM_SIZE_4360; usbinfo->rdlram_base_addr = RDL_RAM_BASE_4360; break; case 43242: case 43243: usbinfo->rdlram_size = RDL_RAM_SIZE_43242; usbinfo->rdlram_base_addr = RDL_RAM_BASE_43242; break; case 43143: usbinfo->rdlram_size = RDL_RAM_SIZE_43143; usbinfo->rdlram_base_addr = RDL_RAM_BASE_43143; break; case 0x4350: case 43556: case 43558: case 43569: usbinfo->rdlram_size = RDL_RAM_SIZE_4350; usbinfo->rdlram_base_addr = RDL_RAM_BASE_4350; break; case POSTBOOT_ID: break; default: DBUSERR(("%s: Chip 0x%x Ram size is not known\n", __FUNCTION__, chip)); retval = FALSE; break; } return retval; } /* dbus_usb_update_chipinfo */ /** higher DBUS level (dbus.c) wants to know if firmware download is required. */ static int dbus_usb_dlneeded(void *bus) { usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); void *osinfo; bootrom_id_t id; int dl_needed = 1; DBUSTRACE(("%s\n", __FUNCTION__)); if (usbinfo == NULL) return DBUS_ERR; osinfo = usbinfo->usbosl_info; ASSERT(osinfo); /* Check if firmware downloaded already by querying runtime ID */ id.chip = 0xDEAD; dbus_usbos_dl_cmd(osinfo, DL_GETVER, &id, sizeof(bootrom_id_t)); id.chip = ltoh32(id.chip); id.chiprev = ltoh32(id.chiprev); if (FALSE == dbus_usb_update_chipinfo(usbinfo, id.chip)) { dl_needed = DBUS_ERR; goto exit; } DBUSERR(("%s: chip 0x%x rev 0x%x\n", __FUNCTION__, id.chip, id.chiprev)); if (id.chip == POSTBOOT_ID) { /* This code is needed to support two enumerations on USB1.1 scenario */ DBUSERR(("%s: Firmware already downloaded\n", __FUNCTION__)); dbus_usbos_dl_cmd(osinfo, DL_RESETCFG, &id, sizeof(bootrom_id_t)); dl_needed = DBUS_OK; if (usbinfo->pub->busstate == DBUS_STATE_DL_PENDING) usbinfo->pub->busstate = DBUS_STATE_DL_DONE; } else { usbinfo->pub->attrib.devid = id.chip; usbinfo->pub->attrib.chiprev = id.chiprev; } exit: return dl_needed; } /** After issuing firmware download, higher DBUS level (dbus.c) wants to start the firmware. */ static int dbus_usb_dlrun(void *bus) { usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); void *osinfo; rdl_state_t state; int err = DBUS_OK; DBUSTRACE(("%s\n", __FUNCTION__)); if (usbinfo == NULL) return DBUS_ERR; if (USB_DEV_ISBAD(usbinfo)) return DBUS_ERR; osinfo = usbinfo->usbosl_info; ASSERT(osinfo); /* Check we are runnable */ dbus_usbos_dl_cmd(osinfo, DL_GETSTATE, &state, sizeof(rdl_state_t)); state.state = ltoh32(state.state); state.bytes = ltoh32(state.bytes); /* Start the image */ if (state.state == DL_RUNNABLE) { DBUSTRACE(("%s: Issue DL_GO\n", __FUNCTION__)); dbus_usbos_dl_cmd(osinfo, DL_GO, &state, sizeof(rdl_state_t)); if (usbinfo->pub->attrib.devid == TEST_CHIP) dbus_usbos_wait(osinfo, USB_DLGO_SPINWAIT); // dbus_usb_resetcfg(usbinfo); /* The Donlge may go for re-enumeration. */ } else { DBUSERR(("%s: Dongle not runnable\n", __FUNCTION__)); err = DBUS_ERR; } return err; } /** * As preparation for firmware download, higher DBUS level (dbus.c) requests the firmware image * to be used for the type of dongle detected. Directly called by dbus.c (so not via a callback * construction) */ void dbus_bus_fw_get(void *bus, uint8 **fw, int *fwlen, int *decomp) { usb_info_t *usbinfo = BUS_INFO(bus, usb_info_t); unsigned int devid; unsigned int crev; devid = usbinfo->pub->attrib.devid; crev = usbinfo->pub->attrib.chiprev; *fw = NULL; *fwlen = 0; switch (devid) { case BCM43236_CHIP_ID: case BCM43235_CHIP_ID: case BCM43234_CHIP_ID: case BCM43238_CHIP_ID: { if (crev == 3 || crev == 2 || crev == 1) { #ifdef EMBED_IMAGE_43236b *fw = (uint8 *)dlarray_43236b; *fwlen = sizeof(dlarray_43236b); #endif } } break; case BCM4360_CHIP_ID: case BCM4352_CHIP_ID: case BCM43526_CHIP_ID: #ifdef EMBED_IMAGE_43526a if (crev <= 2) { *fw = (uint8 *)dlarray_43526a; *fwlen = sizeof(dlarray_43526a); } #endif #ifdef EMBED_IMAGE_43526b if (crev > 2) { *fw = (uint8 *)dlarray_43526b; *fwlen = sizeof(dlarray_43526b); } #endif break; case BCM43242_CHIP_ID: #ifdef EMBED_IMAGE_43242a0 *fw = (uint8 *)dlarray_43242a0; *fwlen = sizeof(dlarray_43242a0); #endif break; case BCM43143_CHIP_ID: #ifdef EMBED_IMAGE_43143a0 *fw = (uint8 *)dlarray_43143a0; *fwlen = sizeof(dlarray_43143a0); #endif #ifdef EMBED_IMAGE_43143b0 *fw = (uint8 *)dlarray_43143b0; *fwlen = sizeof(dlarray_43143b0); #endif break; case BCM4350_CHIP_ID: case BCM4354_CHIP_ID: case BCM43556_CHIP_ID: case BCM43558_CHIP_ID: case BCM43566_CHIP_ID: case BCM43568_CHIP_ID: case BCM43570_CHIP_ID: case BCM4358_CHIP_ID: #ifdef EMBED_IMAGE_4350a0 if (crev == 0) { *fw = (uint8 *)dlarray_4350a0; *fwlen = sizeof(dlarray_4350a0); } #endif #ifdef EMBED_IMAGE_4350b0 if (crev == 1) { *fw = (uint8 *)dlarray_4350b0; *fwlen = sizeof(dlarray_4350b0); } #endif #ifdef EMBED_IMAGE_4350b1 if (crev == 2) { *fw = (uint8 *)dlarray_4350b1; *fwlen = sizeof(dlarray_4350b1); } #endif #ifdef EMBED_IMAGE_43556b1 if (crev == 2) { *fw = (uint8 *)dlarray_43556b1; *fwlen = sizeof(dlarray_43556b1); } #endif #ifdef EMBED_IMAGE_4350c0 if (crev == 3) { *fw = (uint8 *)dlarray_4350c0; *fwlen = sizeof(dlarray_4350c0); } #endif /* EMBED_IMAGE_4350c0 */ #ifdef EMBED_IMAGE_4350c1 if (crev == 4) { *fw = (uint8 *)dlarray_4350c1; *fwlen = sizeof(dlarray_4350c1); } #endif /* EMBED_IMAGE_4350c1 */ break; case BCM43569_CHIP_ID: #ifdef EMBED_IMAGE_43569a0 if (crev == 0) { *fw = (uint8 *)dlarray_43569a0; *fwlen = sizeof(dlarray_43569a0); } #endif /* EMBED_IMAGE_43569a0 */ break; default: #ifdef EMBED_IMAGE_GENERIC *fw = (uint8 *)dlarray; *fwlen = sizeof(dlarray); #endif break; } } /* dbus_bus_fw_get */ #ifdef BCM_REQUEST_FW /* Register a dummy USB client driver in order to be notified of new USB device */ int dbus_usb_reg_notify(void* semaphore) { return dbus_usbos_reg_notify(semaphore); } void dbus_usb_unreg_notify(void) { dbus_usbos_unreg_notify(); } #endif