/* D11 macdbg functions for Broadcom 802.11abgn * Networking Adapter Device Drivers. * * Broadcom Proprietary and Confidential. Copyright (C) 2020, * All Rights Reserved. * * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom; * the contents of this file may not be disclosed to third parties, * copied or duplicated in any form, in whole or in part, without * the prior written permission of Broadcom. * * * <> * * $Id: dhd_macdbg.c 670412 2016-11-15 20:01:18Z shinuk $ */ #ifdef BCMDBG #include #include #include #include #include #include "d11reglist_proto.h" #include "dhdioctl.h" #include #ifdef BCMDBUS #include #define BUS_IOVAR_OP(a, b, c, d, e, f, g) dbus_iovar_op(a->dbus, b, c, d, e, f, g) #else #include #define BUS_IOVAR_OP dhd_bus_iovar_op #endif typedef struct _macdbg_info_t { dhd_pub_t *dhdp; d11regs_list_t *pd11regs; uint16 d11regs_sz; d11regs_list_t *pd11regs_x; uint16 d11regsx_sz; svmp_list_t *psvmpmems; uint16 svmpmems_sz; } macdbg_info_t; #define SVMPLIST_HARDCODE int dhd_macdbg_attach(dhd_pub_t *dhdp) { macdbg_info_t *macdbg_info = MALLOCZ(dhdp->osh, sizeof(*macdbg_info)); #ifdef SVMPLIST_HARDCODE svmp_list_t svmpmems[] = { {0x20000, 256}, {0x21e10, 16}, {0x20300, 16}, {0x20700, 16}, {0x20b00, 16}, {0x20be0, 16}, {0x20bff, 16}, {0xc000, 32}, {0xe000, 32}, {0x10000, 0x8000}, {0x18000, 0x8000} }; #endif /* SVMPLIST_HARDCODE */ if (macdbg_info == NULL) { return BCME_NOMEM; } dhdp->macdbg_info = macdbg_info; macdbg_info->dhdp = dhdp; #ifdef SVMPLIST_HARDCODE macdbg_info->psvmpmems = MALLOCZ(dhdp->osh, sizeof(svmpmems)); if (macdbg_info->psvmpmems == NULL) { return BCME_NOMEM; } macdbg_info->svmpmems_sz = ARRAYSIZE(svmpmems); memcpy(macdbg_info->psvmpmems, svmpmems, sizeof(svmpmems)); DHD_ERROR(("%s: psvmpmems %p svmpmems_sz %d\n", __FUNCTION__, macdbg_info->psvmpmems, macdbg_info->svmpmems_sz)); #endif return BCME_OK; } void dhd_macdbg_detach(dhd_pub_t *dhdp) { macdbg_info_t *macdbg_info = dhdp->macdbg_info; ASSERT(macdbg_info); if (macdbg_info->pd11regs) { ASSERT(macdbg_info->d11regs_sz > 0); MFREE(dhdp->osh, macdbg_info->pd11regs, (macdbg_info->d11regs_sz * sizeof(macdbg_info->pd11regs[0]))); macdbg_info->d11regs_sz = 0; } if (macdbg_info->pd11regs_x) { ASSERT(macdbg_info->d11regsx_sz > 0); MFREE(dhdp->osh, macdbg_info->pd11regs_x, (macdbg_info->d11regsx_sz * sizeof(macdbg_info->pd11regs_x[0]))); macdbg_info->d11regsx_sz = 0; } if (macdbg_info->psvmpmems) { ASSERT(macdbg_info->svmpmems_sz > 0); MFREE(dhdp->osh, macdbg_info->psvmpmems, (macdbg_info->svmpmems_sz * sizeof(macdbg_info->psvmpmems[0]))); macdbg_info->svmpmems_sz = 0; } MFREE(dhdp->osh, macdbg_info, sizeof(*macdbg_info)); } void dhd_macdbg_event_handler(dhd_pub_t *dhdp, uint32 reason, uint8 *event_data, uint32 datalen) { d11regs_list_t *pd11regs; macdbg_info_t *macdbg_info = dhdp->macdbg_info; uint d11regs_sz; DHD_TRACE(("%s: reason %d datalen %d\n", __FUNCTION__, reason, datalen)); switch (reason) { case WLC_E_MACDBG_LIST_PSMX: /* Fall through */ case WLC_E_MACDBG_LIST_PSM: pd11regs = MALLOCZ(dhdp->osh, datalen); if (pd11regs == NULL) { DHD_ERROR(("%s: NOMEM for len %d\n", __FUNCTION__, datalen)); return; } memcpy(pd11regs, event_data, datalen); d11regs_sz = datalen / sizeof(pd11regs[0]); DHD_ERROR(("%s: d11regs %p d11regs_sz %d\n", __FUNCTION__, pd11regs, d11regs_sz)); if (reason == WLC_E_MACDBG_LIST_PSM) { macdbg_info->pd11regs = pd11regs; macdbg_info->d11regs_sz = (uint16)d11regs_sz; } else { macdbg_info->pd11regs_x = pd11regs; macdbg_info->d11regsx_sz = (uint16)d11regs_sz; } break; case WLC_E_MACDBG_REGALL: #ifdef LINUX /* Schedule to work queue as this context could be ISR */ dhd_schedule_macdbg_dump(dhdp); #else /* Dump PSMr */ (void) dhd_macdbg_dumpmac(dhdp, NULL, 0, NULL, FALSE); /* Dump PSMx */ (void) dhd_macdbg_dumpmac(dhdp, NULL, 0, NULL, TRUE); /* Dump SVMP mems */ (void) dhd_macdbg_dumpsvmp(dhdp, NULL, 0, NULL); #endif break; default: DHD_ERROR(("%s: Unknown reason %d\n", __FUNCTION__, reason)); } return; } static uint16 _dhd_get_ihr16(macdbg_info_t *macdbg_info, uint16 addr, struct bcmstrbuf *b, bool verbose) { sdreg_t sdreg; uint16 val; sdreg.func = 2; sdreg.offset = (0x1000 | addr); BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg", &sdreg, sizeof(sdreg), &val, sizeof(val), IOV_GET); if (verbose) { if (b) { bcm_bprintf(b, "DEBUG: IHR16: read 0x%08x, size 2, value 0x%04x\n", (addr + 0x18001000), val); } else { printf("DEBUG: IHR16: read 0x%08x, size 2, value 0x%04x\n", (addr + 0x18001000), val); } } return val; } static uint32 _dhd_get_ihr32(macdbg_info_t *macdbg_info, uint16 addr, struct bcmstrbuf *b, bool verbose) { sdreg_t sdreg; uint32 val; sdreg.func = 4; sdreg.offset = (0x1000 | addr); BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg", &sdreg, sizeof(sdreg), &val, sizeof(val), IOV_GET); if (verbose) { if (b) { bcm_bprintf(b, "DEBUG: IHR32: read 0x%08x, size 4, value 0x%08x\n", (addr + 0x18001000), val); } else { printf("DEBUG: IHR32: read 0x%08x, size 4, value 0x%08x\n", (addr + 0x18001000), val); } } return val; } static void _dhd_set_ihr16(macdbg_info_t *macdbg_info, uint16 addr, uint16 val, struct bcmstrbuf *b, bool verbose) { sdreg_t sdreg; sdreg.func = 2; sdreg.offset = (0x1000 | addr); sdreg.value = val; if (verbose) { if (b) { bcm_bprintf(b, "DEBUG: IHR16: write 0x%08x, size 2, value 0x%04x\n", (addr + 0x18001000), val); } else { printf("DEBUG: IHR16: write 0x%08x, size 2, value 0x%04x\n", (addr + 0x18001000), val); } } BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg", NULL, 0, &sdreg, sizeof(sdreg), IOV_SET); } static void _dhd_set_ihr32(macdbg_info_t *macdbg_info, uint16 addr, uint32 val, struct bcmstrbuf *b, bool verbose) { sdreg_t sdreg; sdreg.func = 4; sdreg.offset = (0x1000 | addr); sdreg.value = val; if (verbose) { if (b) { bcm_bprintf(b, "DEBUG: IHR32: write 0x%08x, size 4, value 0x%08x\n", (addr + 0x18001000), val); } else { printf("DEBUG: IHR32: write 0x%08x, size 4, value 0x%08x\n", (addr + 0x18001000), val); } } BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg", NULL, 0, &sdreg, sizeof(sdreg), IOV_SET); } static uint32 _dhd_get_d11obj32(macdbg_info_t *macdbg_info, uint16 objaddr, uint32 sel, struct bcmstrbuf *b, bool verbose) { uint32 val; sdreg_t sdreg; sdreg.func = 4; // 4bytes by default. sdreg.offset = 0x1160; if (objaddr == 0xffff) { if (verbose) { goto objaddr_read; } else { goto objdata_read; } } if (objaddr & 0x3) { printf("%s: ERROR! Invalid addr 0x%x\n", __FUNCTION__, objaddr); } sdreg.value = (sel | (objaddr >> 2)); if (verbose) { if (b) { bcm_bprintf(b, "DEBUG: %s: Indirect: write 0x%08x, size %d, value 0x%08x\n", (sel & 0x00020000) ? "SCR":"SHM", (sdreg.offset + 0x18000000), sdreg.func, sdreg.value); } else { printf("DEBUG: %s: Indirect: write 0x%08x, size %d, value 0x%08x\n", (sel & 0x00020000) ? "SCR":"SHM", (sdreg.offset + 0x18000000), sdreg.func, sdreg.value); } } BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg", NULL, 0, &sdreg, sizeof(sdreg), IOV_SET); objaddr_read: /* Give some time to obj addr register */ BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg", &sdreg, sizeof(sdreg), &val, sizeof(val), IOV_GET); if (verbose) { if (b) { bcm_bprintf(b, "DEBUG: %s: Indirect: Read 0x%08x, size %d, value 0x%08x\n", (sel & 0x00020000) ? "SCR":"SHM", (sdreg.offset + 0x18000000), sdreg.func, val); } else { printf("DEBUG: %s: Indirect: Read 0x%08x, size %d, value 0x%08x\n", (sel & 0x00020000) ? "SCR":"SHM", (sdreg.offset + 0x18000000), sdreg.func, val); } } objdata_read: sdreg.offset = 0x1164; BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg", &sdreg, sizeof(sdreg), &val, sizeof(val), IOV_GET); if (verbose) { if (b) { bcm_bprintf(b, "DEBUG: %s: Indirect: Read 0x%08x, size %d, value 0x%04x\n", (sel & 0x00020000) ? "SCR":"SHM", (sdreg.offset + 0x18000000), sdreg.func, val); } else { printf("DEBUG: %s: Indirect: Read 0x%08x, size %d, value 0x%04x\n", (sel & 0x00020000) ? "SCR":"SHM", (sdreg.offset + 0x18000000), sdreg.func, val); } } return val; } static uint16 _dhd_get_d11obj16(macdbg_info_t *macdbg_info, uint16 objaddr, uint32 sel, d11obj_cache_t *obj_cache, struct bcmstrbuf *b, bool verbose) { uint32 val; if (obj_cache && obj_cache->cache_valid && ((obj_cache->sel ^ sel) & (0xffffff)) == 0) { if (obj_cache->addr32 == (objaddr & ~0x3)) { /* XXX: Same objaddr read as the previous one */ if (verbose) { if (b) { bcm_bprintf(b, "DEBUG: %s: Read cache value: " "addr32 0x%04x, sel 0x%08x, value 0x%08x\n", (sel & 0x00020000) ? "SCR":"SHM", obj_cache->addr32, obj_cache->sel, obj_cache->val); } else { printf("DEBUG: %s: Read cache value: " "addr32 0x%04x, sel 0x%08x, value 0x%08x\n", (sel & 0x00020000) ? "SCR":"SHM", obj_cache->addr32, obj_cache->sel, obj_cache->val); } } val = obj_cache->val; goto exit; } else if ((obj_cache->sel & 0x02000000) && (obj_cache->addr32 + 4 == (objaddr & ~0x3))) { /* XXX: objaddr is auto incrementing, so just read objdata */ if (verbose) { if (b) { bcm_bprintf(b, "DEBUG: %s: Read objdata only: " "addr32 0x%04x, sel 0x%08x, value 0x%08x\n", (sel & 0x00020000) ? "SCR":"SHM", obj_cache->addr32, obj_cache->sel, obj_cache->val); } else { printf("DEBUG: %s: Read objdata only: " "addr32 0x%04x, sel 0x%08x, value 0x%08x\n", (sel & 0x00020000) ? "SCR":"SHM", obj_cache->addr32, obj_cache->sel, obj_cache->val); } } val = _dhd_get_d11obj32(macdbg_info, 0xffff, sel, b, verbose); goto exit; } } val = _dhd_get_d11obj32(macdbg_info, (objaddr & ~0x2), sel, b, verbose); exit: if (obj_cache) { obj_cache->addr32 = (objaddr & ~0x3); obj_cache->sel = sel; obj_cache->val = val; obj_cache->cache_valid = TRUE; } return (uint16)((objaddr & 0x2) ? (val >> 16) : val); } static int _dhd_print_d11reg(macdbg_info_t *macdbg_info, int idx, int type, uint16 addr, struct bcmstrbuf *b, d11obj_cache_t *obj_cache, bool verbose) { const char *regname[D11REG_TYPE_MAX] = D11REGTYPENAME; uint32 val; if (type == D11REG_TYPE_IHR32) { if ((addr & 0x3)) { printf("%s: ERROR! Invalid addr 0x%x\n", __FUNCTION__, addr); addr &= ~0x3; } val = _dhd_get_ihr32(macdbg_info, addr, b, verbose); if (b) { bcm_bprintf(b, "%-3d %s 0x%-4x = 0x%-8x\n", idx, regname[type], addr, val); } else { printf("%-3d %s 0x%-4x = 0x%-8x\n", idx, regname[type], addr, val); } } else { switch (type) { case D11REG_TYPE_IHR16: { if ((addr & 0x1)) { printf("%s: ERROR! Invalid addr 0x%x\n", __FUNCTION__, addr); addr &= ~0x1; } val = _dhd_get_ihr16(macdbg_info, addr, b, verbose); break; } case D11REG_TYPE_IHRX16: val = _dhd_get_d11obj16(macdbg_info, (addr - 0x400) << 1, 0x020b0000, obj_cache, b, verbose); break; case D11REG_TYPE_SCR: val = _dhd_get_d11obj16(macdbg_info, addr << 2, 0x02020000, obj_cache, b, verbose); break; case D11REG_TYPE_SCRX: val = _dhd_get_d11obj16(macdbg_info, addr << 2, 0x020a0000, obj_cache, b, verbose); break; case D11REG_TYPE_SHM: val = _dhd_get_d11obj16(macdbg_info, addr, 0x02010000, obj_cache, b, verbose); break; case D11REG_TYPE_SHMX: val = _dhd_get_d11obj16(macdbg_info, addr, 0x02090000, obj_cache, b, verbose); break; default: printf("Unrecognized type %d!\n", type); return 0; } if (b) { bcm_bprintf(b, "%-3d %s 0x%-4x = 0x%-4x\n", idx, regname[type], addr, val); } else { printf("%-3d %s 0x%-4x = 0x%-4x\n", idx, regname[type], addr, val); } } return 1; } static int _dhd_print_d11regs(macdbg_info_t *macdbg_info, d11regs_list_t *pregs, int start_idx, struct bcmstrbuf *b, bool verbose) { uint16 addr; int idx = 0; d11obj_cache_t obj_cache = {0, 0, 0, FALSE}; addr = pregs->addr; if (pregs->type >= D11REG_TYPE_MAX) { printf("%s: wrong type %d\n", __FUNCTION__, pregs->type); return 0; } if (pregs->bitmap) { while (pregs->bitmap) { if (pregs->bitmap && (pregs->bitmap & 0x1)) { _dhd_print_d11reg(macdbg_info, (idx + start_idx), pregs->type, addr, b, &obj_cache, verbose); idx++; } pregs->bitmap = pregs->bitmap >> 1; addr += pregs->step; } } else { for (; idx < pregs->cnt; idx++) { _dhd_print_d11reg(macdbg_info, (idx + start_idx), pregs->type, addr, b, &obj_cache, verbose); addr += pregs->step; } } return idx; } static int _dhd_pd11regs_bylist(macdbg_info_t *macdbg_info, d11regs_list_t *reglist, uint16 reglist_sz, struct bcmstrbuf *b) { uint i, idx = 0; if (reglist != NULL && reglist_sz > 0) { for (i = 0; i < reglist_sz; i++) { DHD_TRACE(("%s %d %p %d\n", __FUNCTION__, __LINE__, ®list[i], reglist_sz)); idx += _dhd_print_d11regs(macdbg_info, ®list[i], idx, b, FALSE); } } return idx; } int dhd_macdbg_dumpmac(dhd_pub_t *dhdp, char *buf, int buflen, int *outbuflen, bool dump_x) { macdbg_info_t *macdbg_info = dhdp->macdbg_info; struct bcmstrbuf *b = NULL; struct bcmstrbuf bcmstrbuf; uint cnt = 0; DHD_TRACE(("%s %d %p %d %p %d %p %d\n", __FUNCTION__, __LINE__, buf, buflen, macdbg_info->pd11regs, macdbg_info->d11regs_sz, macdbg_info->pd11regs_x, macdbg_info->d11regsx_sz)); if (buf && buflen > 0) { bcm_binit(&bcmstrbuf, buf, buflen); b = &bcmstrbuf; } if (!dump_x) { /* Dump PSMr */ cnt += _dhd_pd11regs_bylist(macdbg_info, macdbg_info->pd11regs, macdbg_info->d11regs_sz, b); } else { /* Dump PSMx */ cnt += _dhd_pd11regs_bylist(macdbg_info, macdbg_info->pd11regs_x, macdbg_info->d11regsx_sz, b); } if (b && outbuflen) { if ((uint)buflen > BCMSTRBUF_LEN(b)) { *outbuflen = buflen - BCMSTRBUF_LEN(b); } else { DHD_ERROR(("%s: buflen insufficient!\n", __FUNCTION__)); *outbuflen = buflen; /* Do not return buftooshort to allow printing macregs we have got */ } } return ((cnt > 0) ? BCME_OK : BCME_UNSUPPORTED); } int dhd_macdbg_pd11regs(dhd_pub_t *dhdp, char *params, int plen, char *buf, int buflen) { macdbg_info_t *macdbg_info = dhdp->macdbg_info; dhd_pd11regs_param *pd11regs = (void *)params; dhd_pd11regs_buf *pd11regs_buf = (void *)buf; uint16 start_idx; bool verbose; d11regs_list_t reglist; struct bcmstrbuf *b = NULL; struct bcmstrbuf bcmstrbuf; start_idx = pd11regs->start_idx; verbose = pd11regs->verbose; memcpy(®list, pd11regs->plist, sizeof(reglist)); memset(buf, '\0', buflen); bcm_binit(&bcmstrbuf, (char *)(pd11regs_buf->pbuf), (buflen - OFFSETOF(dhd_pd11regs_buf, pbuf))); b = &bcmstrbuf; pd11regs_buf->idx = (uint16)_dhd_print_d11regs(macdbg_info, ®list, start_idx, b, verbose); return ((pd11regs_buf->idx > 0) ? BCME_OK : BCME_ERROR); } int dhd_macdbg_reglist(dhd_pub_t *dhdp, char *buf, int buflen) { int err, desc_idx = 0; dhd_maclist_t *maclist = (dhd_maclist_t *)buf; macdbg_info_t *macdbg_info = dhdp->macdbg_info; void *xtlvbuf_p = maclist->plist; uint16 xtlvbuflen = (uint16)buflen; xtlv_desc_t xtlv_desc[] = { {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL} }; if (!macdbg_info->pd11regs) { err = BCME_NOTFOUND; goto exit; } ASSERT(macdbg_info->d11regs_sz > 0); xtlv_desc[desc_idx].type = DHD_MACLIST_XTLV_R; xtlv_desc[desc_idx].len = macdbg_info->d11regs_sz * (uint16)sizeof(*(macdbg_info->pd11regs)); xtlv_desc[desc_idx].ptr = macdbg_info->pd11regs; desc_idx++; if (macdbg_info->pd11regs_x) { ASSERT(macdbg_info->d11regsx_sz); xtlv_desc[desc_idx].type = DHD_MACLIST_XTLV_X; xtlv_desc[desc_idx].len = macdbg_info->d11regsx_sz * (uint16)sizeof(*(macdbg_info->pd11regs_x)); xtlv_desc[desc_idx].ptr = macdbg_info->pd11regs_x; desc_idx++; } if (macdbg_info->psvmpmems) { ASSERT(macdbg_info->svmpmems_sz); xtlv_desc[desc_idx].type = DHD_SVMPLIST_XTLV; xtlv_desc[desc_idx].len = macdbg_info->svmpmems_sz * (uint16)sizeof(*(macdbg_info->psvmpmems)); xtlv_desc[desc_idx].ptr = macdbg_info->psvmpmems; desc_idx++; } err = bcm_pack_xtlv_buf_from_mem((uint8 **)&xtlvbuf_p, &xtlvbuflen, xtlv_desc, BCM_XTLV_OPTION_ALIGN32); maclist->version = 0; /* No version control for now anyway */ maclist->bytes_len = (buflen - xtlvbuflen); exit: return err; } static int _dhd_print_svmps(macdbg_info_t *macdbg_info, svmp_list_t *psvmp, int start_idx, struct bcmstrbuf *b, bool verbose) { int idx; uint32 addr, mem_id, offset, prev_mem_id, prev_offset; uint16 cnt, val; BCM_REFERENCE(start_idx); /* Set tbl ID and tbl offset. */ _dhd_set_ihr32(macdbg_info, 0x3fc, 0x30000d, b, verbose); _dhd_set_ihr32(macdbg_info, 0x3fc, 0x8000000e, b, verbose); addr = psvmp->addr; cnt = psvmp->cnt; /* In validate previous mem_id and offset */ prev_mem_id = (uint32)(-1); prev_offset = (uint32)(-1); for (idx = 0; idx < cnt; idx++, addr++) { mem_id = (addr >> 15); offset = (addr & 0x7fff) >> 1; if (mem_id != prev_mem_id) { /* Set mem_id */ _dhd_set_ihr32(macdbg_info, 0x3fc, ((mem_id & 0xffff0000) | 0x10), b, verbose); _dhd_set_ihr32(macdbg_info, 0x3fc, ((mem_id << 16) | 0xf), b, verbose); } if (offset != prev_offset) { /* XXX: Is this needed? * _dhd_set_ihr32(macdbg_info, 0x3fc, 0x30000d, b, verbose); */ /* svmp offset */ _dhd_set_ihr32(macdbg_info, 0x3fc, ((offset << 16) | 0xe), b, verbose); } /* Read hi or lo */ _dhd_set_ihr16(macdbg_info, 0x3fc, ((addr & 0x1) ? 0x10 : 0xf), b, verbose); val = _dhd_get_ihr16(macdbg_info, 0x3fe, b, verbose); if (b) { bcm_bprintf(b, "0x%-4x 0x%-4x\n", addr, val); } else { printf("0x%-4x 0x%-4x\n", addr, val); } prev_mem_id = mem_id; prev_offset = offset; } return idx; } static int _dhd_psvmps_bylist(macdbg_info_t *macdbg_info, svmp_list_t *svmplist, uint16 svmplist_sz, struct bcmstrbuf *b) { uint i, idx = 0; if (svmplist != NULL && svmplist_sz > 0) { for (i = 0; i < svmplist_sz; i++) { DHD_TRACE(("%s %d %p %d\n", __FUNCTION__, __LINE__, &svmplist[i], svmplist_sz)); idx += _dhd_print_svmps(macdbg_info, &svmplist[i], idx, b, FALSE); } } return idx; } int dhd_macdbg_dumpsvmp(dhd_pub_t *dhdp, char *buf, int buflen, int *outbuflen) { macdbg_info_t *macdbg_info = dhdp->macdbg_info; struct bcmstrbuf *b = NULL; struct bcmstrbuf bcmstrbuf; uint cnt = 0; DHD_TRACE(("%s %d %p %d %p %d\n", __FUNCTION__, __LINE__, buf, buflen, macdbg_info->psvmpmems, macdbg_info->svmpmems_sz)); if (buf && buflen > 0) { bcm_binit(&bcmstrbuf, buf, buflen); b = &bcmstrbuf; } cnt = _dhd_psvmps_bylist(macdbg_info, macdbg_info->psvmpmems, macdbg_info->svmpmems_sz, b); if (b && outbuflen) { if ((uint)buflen > BCMSTRBUF_LEN(b)) { *outbuflen = buflen - BCMSTRBUF_LEN(b); } else { DHD_ERROR(("%s: buflen insufficient!\n", __FUNCTION__)); *outbuflen = buflen; /* Do not return buftooshort to allow printing macregs we have got */ } } return ((cnt > 0) ? BCME_OK : BCME_UNSUPPORTED); } int dhd_macdbg_psvmpmems(dhd_pub_t *dhdp, char *params, int plen, char *buf, int buflen) { macdbg_info_t *macdbg_info = dhdp->macdbg_info; dhd_pd11regs_param *pd11regs = (void *)params; dhd_pd11regs_buf *pd11regs_buf = (void *)buf; uint16 start_idx; bool verbose; svmp_list_t reglist; struct bcmstrbuf *b = NULL; struct bcmstrbuf bcmstrbuf; start_idx = pd11regs->start_idx; verbose = pd11regs->verbose; memcpy(®list, pd11regs->plist, sizeof(reglist)); memset(buf, '\0', buflen); bcm_binit(&bcmstrbuf, (char *)(pd11regs_buf->pbuf), (buflen - OFFSETOF(dhd_pd11regs_buf, pbuf))); b = &bcmstrbuf; pd11regs_buf->idx = (uint16)_dhd_print_svmps(macdbg_info, ®list, start_idx, b, verbose); return ((pd11regs_buf->idx > 0) ? BCME_OK : BCME_ERROR); } #endif /* BCMDBG */