void iboot_loader_run(void) { uint64_t startTime = timer_get_system_microtime(); // boot iboot when either the up button is pressed or after 10 seconds static Boolean buttonPressed = FALSE; static Boolean messageShown = FALSE; while(1) { if (!gpio_pin_state(BUTTONS_VOLUP) || (has_elapsed(startTime, 10 * 1000 * 1000) && !buttonPressed)) { load_iboot(); task_stop(); } if (gpio_pin_state(BUTTONS_HOLD)) { buttonPressed = TRUE; } if (has_elapsed(startTime, 2 * 1000 * 1000) && !messageShown) { // show a welcome message after 2 seconds to skip all of the usb spam bufferPrintf("===================\r\n"); bufferPrintf("Welcome to the 2g touch experimental openiBoot!\r\n"); bufferPrintf("iBoot will be automatically loaded after 10 seconds\r\n"); bufferPrintf("Press the power button to cancel automatic booting\r\n"); bufferPrintf("Press the volume up button to load ios\r\n"); bufferPrintf("===================\r\n"); bufferPrintf("\r\n\r\n\r\n"); messageShown = TRUE; } task_yield(); } }
static int query_adc(int mux) { uint8_t buf[2]; uint32_t mux_masked, result = 0; uint64_t startTime; mux_masked = mux & 0xF; result = pmu_get_reg(PMU_ADC_REG); if (mux == 3) { mux_masked |= 0x20; pmu_write_reg(PMU_MUXSEL_REG, mux_masked, 0); udelay(80000); } pmu_write_reg(PMU_MUXSEL_REG, mux_masked | 0x10, 0); startTime = timer_get_system_microtime(); do { udelay(1000); if (has_elapsed(startTime, 50000)) return -1; result = pmu_get_reg(PMU_ADC_REG); } while (!(result & 0x20)); pmu_get_regs(PMU_ADCVAL_REG, buf, 2); pmu_write_reg(PMU_MUXSEL_REG, 0, 0); return (buf[1] << 4) | (buf[0] & 0xF); }
int dma_cancel(int channel) { //bufferPrintf("cdma: dma_cancel.\r\n"); DMAInfo* dma = &dmaInfo[channel]; uint64_t startTime = timer_get_system_microtime(); if (!dma->signalled) return channel; dma_channel_activate(channel, 1); uint32_t channel_reg = DMA + (channel << 12); if (GET_BITS(GET_REG(channel_reg), 16, 2) == 1) { SET_REG(channel_reg, 4); while (GET_BITS(GET_REG(channel_reg), 16, 2) == 1) { if (has_elapsed(startTime, 10000)) system_panic("CDMA: channel %d timeout during abort\r\n", channel); } SET_REG(channel_reg, 2); } dma->signalled = 1; dma_set_aes(channel, 0); return dma_channel_activate(channel, 0); }
static int wait_for_ready(int timeout) { if((GET_REG(NAND + NAND_STATUS) & NAND_STATUS_READY) != 0) { return 0; } uint64_t startTime = timer_get_system_microtime(); while((GET_REG(NAND + NAND_STATUS) & NAND_STATUS_READY) == 0) { if(has_elapsed(startTime, timeout * 1000)) { return ERROR_TIMEOUT; } } return 0; }
static int wait_for_ecc_interrupt(int timeout) { uint64_t startTime = timer_get_system_microtime(); uint32_t mask = (1 << (NANDECC_INT - VIC_InterruptSeparator)); while((GET_REG(VIC1 + VICRAWINTR) & mask) == 0) { if(has_elapsed(startTime, timeout * 1000)) { return ERROR_TIMEOUT; } } SET_REG(NANDECC + NANDECC_CLEARINT, 1); if((GET_REG(VIC1 + VICRAWINTR) & mask) == 0) { return 0; } else { return ERROR_TIMEOUT; } }
static int wait_for_status_bit_3(int timeout) { if((GET_REG(NAND + NAND_STATUS) & (1 << 3)) != 0) { SET_REG(NAND + NAND_STATUS, 1 << 3); return 0; } uint64_t startTime = timer_get_system_microtime(); while((GET_REG(NAND + NAND_STATUS) & (1 << 3)) == 0) { if(has_elapsed(startTime, timeout * 1000)) { return ERROR_TIMEOUT; } } SET_REG(NAND + NAND_STATUS, 1 << 3); return 0; }
static int wait_for_transfer_done(int timeout) { if((GET_REG(NAND + FMCSTAT) & (1 << 3)) != 0) { SET_REG(NAND + FMCSTAT, 1 << 3); return 0; } uint64_t startTime = timer_get_system_microtime(); while((GET_REG(NAND + FMCSTAT) & (1 << 3)) == 0) { if(has_elapsed(startTime, timeout * 1000)) { return ERROR_TIMEOUT; } } SET_REG(NAND + FMCSTAT, 1 << 3); return 0; }
int spi_rx(int port, uint8_t* buffer, int len, int block, int noTransmitJunk) { if(port > (NUM_SPIPORTS - 1)) { return -1; } SET_REG(SPIRegs[port].control, GET_REG(SPIRegs[port].control) | (1 << 2)); SET_REG(SPIRegs[port].control, GET_REG(SPIRegs[port].control) | (1 << 3)); spi_info[port].rxBuffer = buffer; spi_info[port].rxDone = FALSE; spi_info[port].rxCurrentLen = 0; spi_info[port].rxTotalLen = len; spi_info[port].counter = 0; if(noTransmitJunk == 0) { SET_REG(SPIRegs[port].setup, GET_REG(SPIRegs[port].setup) | 1); } SET_REG(SPIRegs[port].cnt, (len + ((1<<spi_info[port].wordSize)-1)) >> spi_info[port].wordSize); SET_REG(SPIRegs[port].control, 1); if(block) { uint64_t startTime = timer_get_system_microtime(); while(!spi_info[port].rxDone) { // yield if(has_elapsed(startTime, 1000)) { EnterCriticalSection(); spi_info[port].rxDone = TRUE; spi_info[port].rxBuffer = NULL; LeaveCriticalSection(); if(noTransmitJunk == 0) { SET_REG(SPIRegs[port].setup, GET_REG(SPIRegs[port].setup) & ~1); } return -1; } } if(noTransmitJunk == 0) { SET_REG(SPIRegs[port].setup, GET_REG(SPIRegs[port].setup) & ~1); } return len; } else { return 0; } }
static int wait_for_command_done(int bank, int timeout) { uint64_t startTime = timer_get_system_microtime(); if(NoMultibankCmdStatus) bank = 0; else bank &= 0xffff; uint32_t toTest = 1 << (bank + 4); while((GET_REG(NAND + FMCSTAT) & toTest) == 0) { if(has_elapsed(startTime, timeout * 1000)) { return ERROR_TIMEOUT; } } SET_REG(NAND + FMCSTAT, toTest); return 0; }
int dma_finish(int controller, int channel, int timeout) { uint64_t startTime = timer_get_system_microtime(); while(!requests[controller - 1][channel].done) { if(has_elapsed(startTime, timeout * 1000)) { return -1; } } EnterCriticalSection(); requests[controller - 1][channel].started = FALSE; requests[controller - 1][channel].done = FALSE; if(controller == 1) Controller0FreeChannels[channel] = 0; else if(controller == 2) Controller1FreeChannels[channel] = 0; LeaveCriticalSection(); return 0; }
static int nand_bank_reset_helper(int bank, int timeout) { uint64_t startTime = timer_get_system_microtime(); if(NANDBankResetSetting) bank = 0; else bank &= 0xffff; uint32_t toTest = 1 << (bank + 4); while((GET_REG(NAND + NAND_STATUS) & toTest) == 0) { if(has_elapsed(startTime, timeout * 1000)) { return ERROR_TIMEOUT; } } SET_REG(NAND + NAND_STATUS, toTest); return 0; }
int task_sleep(int _ms) { EnterCriticalSection(); if(CurrentRunning == IRQTask) { LeaveCriticalSection(); bufferPrintf("tasks: You can't suspend an ISR.\r\n"); return -1; } TaskDescriptor *next = CurrentRunning->taskList.next; if(next == CurrentRunning) { LeaveCriticalSection(); if (!startTime) { bufferPrintf("tasks: Last thread cannot sleep!\n"); startTime = timer_get_system_microtime(); } if (!has_elapsed(startTime, (_ms * 1000))) task_sleep(_ms); startTime = 0; return 0; } uint32_t ticks = _ms * 1000; TaskDescriptor *task = CurrentRunning; //bufferPrintf("tasks: Putting task %p to sleep for %d ms.\n", task, _ms); task_remove(task); event_add(&task->sleepEvent, ticks, &task_wake_event, task); SwapTask(next); LeaveCriticalSection(); return 0; }
static int bank_setup(int bank) { SET_REG(NAND + NAND_CONFIG, ((NANDSetting1 & NAND_CONFIG_SETTING1MASK) << NAND_CONFIG_SETTING1SHIFT) | ((NANDSetting2 & NAND_CONFIG_SETTING2MASK) << NAND_CONFIG_SETTING2SHIFT) | (1 << (banksTable[bank] + 1)) | NAND_CONFIG_DEFAULTS); uint32_t toTest = 1 << (bank + 4); if((GET_REG(NAND + NAND_STATUS) & toTest) != 0) { SET_REG(NAND + NAND_STATUS, toTest); } SET_REG(NAND + NAND_CON, NAND_CON_SETTING1); SET_REG(NAND + NAND_CMD, NAND_CMD_READSTATUS); wait_for_ready(500); uint64_t startTime = timer_get_system_microtime(); while(TRUE) { SET_REG(NAND + NAND_TRANSFERSIZE, 0); SET_REG(NAND + NAND_CON, NAND_CON_BEGINTRANSFER); if(wait_for_status_bit_3(500) != 0) { bufferPrintf("nand: bank_setup: wait for status bit 3 timed out\r\n"); return ERROR_TIMEOUT; } uint32_t data = GET_REG(NAND + NAND_DMA_SOURCE); SET_REG(NAND + NAND_CON, NAND_CON_SETTING2); if((data & (1 << 6)) == 0) { if(has_elapsed(startTime, 500 * 1000)) { bufferPrintf("nand: bank_setup: wait for bit 6 of DMA timed out\r\n"); return ERROR_TIMEOUT; } } else { break; } } SET_REG(NAND + NAND_CMD, 0); wait_for_ready(500); return 0; }
static int wait_for_nand_bank_ready(int bank) { SET_REG(NAND + FMCTRL0, ((WEHighHoldTime & FMCTRL_TWH_MASK) << FMCTRL_TWH_SHIFT) | ((WPPulseTime & FMCTRL_TWP_MASK) << FMCTRL_TWP_SHIFT) | (1 << (banksTable[bank] + 1)) | FMCTRL0_ON | FMCTRL0_WPB); uint32_t toTest = 1 << (bank + 4); if((GET_REG(NAND + FMCSTAT) & toTest) != 0) { SET_REG(NAND + FMCSTAT, toTest); } SET_REG(NAND + FMCTRL1, FMCTRL1_FLUSHFIFOS); SET_REG(NAND + NAND_CMD, NAND_CMD_READSTATUS); wait_for_ready(500); uint64_t startTime = timer_get_system_microtime(); while(TRUE) { SET_REG(NAND + FMDNUM, 0); SET_REG(NAND + FMCTRL1, FMCTRL1_DOREADDATA); if(wait_for_transfer_done(500) != 0) { bufferPrintf("nand: wait_for_nand_bank_ready: wait for transfer done timed out\r\n"); return ERROR_TIMEOUT; } uint32_t data = GET_REG(NAND + FMFIFO); SET_REG(NAND + FMCTRL1, FMCTRL1_FLUSHRXFIFO); if((data & (1 << 6)) == 0) { if(has_elapsed(startTime, 500 * 1000)) { bufferPrintf("nand: wait_for_nand_bank_ready: wait for bit 6 of DMA timed out\r\n"); return ERROR_TIMEOUT; } } else { break; } } SET_REG(NAND + NAND_CMD, 0); wait_for_ready(500); return 0; }
int radio_read(char* buf, int len) { char b; int i = 0; char* curLine = buf; while(i < (len - 1)) { uint64_t startTime = timer_get_system_microtime(); while(uart_read(RADIO_UART, &b, 1, 0) == 0) { if(has_elapsed(startTime, 500000)) { return i; } } if(b == 0) continue; buf[i] = b; buf[i + 1] = '\0'; if(strstr(curLine, "OK\r") != NULL) { ++i; break; } else if(strstr(curLine, "ERROR\r") != NULL) { ++i; break; } else if(b == '\r') curLine = &buf[i + 1]; else if(b == '\n') curLine = &buf[i + 1]; ++i; } return i; }
int radio_register(int timeout) { char buf[256]; // enable auto registration radio_cmd("at+cops=0\r\n", 10); uint64_t startTime = timer_get_system_microtime(); while(TRUE) { if(has_elapsed(startTime, timeout * 1000)) return -1; char* pos; radio_write("at+cops?\r\n"); radio_read(buf, sizeof(buf)); pos = buf; while(memcmp(pos, "+COPS: ", sizeof("+COPS: ") - 1) != 0) ++pos; if(pos[7] != '0' || pos[8] != ',') { radio_cmd("at+cops=0\r\n", 10); continue; } char* name = &pos[12]; char* lastQuote = name; while(*lastQuote != '\"') ++lastQuote; *lastQuote = '\0'; bufferPrintf("radio: Registered with %s\r\n", name); return 0; } }
int query_adc(int flags, uint32_t* result) { // clear the done bit if it is set pmu_get_reg(PMU_ADCSTS); // set up flags if (flags == 0x3) { pmu_write_reg(PMU_ADCCON, flags | 0x20, FALSE); udelay(80000); } pmu_write_reg(PMU_ADCCON, flags | 0x10, FALSE); // wait until done uint64_t startTime = timer_get_system_microtime(); while (!(pmu_get_reg(PMU_ADCSTS) & 0x2)) { if (has_elapsed(startTime, 40000)) { return -1; } } uint8_t out[2]; pmu_get_regs(PMU_ADCOUT1, out, 2); *result = (out[1] << 2) | (out[0] & 0x3); return 0; }
int wlan_prog_real(const uint8_t* firmware, size_t size) { int ret; uint8_t status; uint8_t *chunk_buffer; uint32_t chunk_size; size_t req_size; uint64_t startTime; bufferPrintf("wlan: programming firmware...\r\n"); chunk_buffer = (uint8_t*) memalign(512, 4); if (!chunk_buffer) { ret = -1; goto release_fw; } ret = sdio_set_block_size(1, 32); if (ret) goto release; while (size) { startTime = timer_get_system_microtime(); while (1) { status = sdio_readb(1, IF_SDIO_STATUS, &ret); if (ret) goto release; if ((status & IF_SDIO_IO_RDY) && (status & IF_SDIO_DL_RDY)) break; if(has_elapsed(startTime, 1000 * 1000)) { ret = -1; goto release; } udelay(1000); } req_size = sdio_readb(1, IF_SDIO_RD_BASE, &ret); if (ret) goto release; req_size |= sdio_readb(1, IF_SDIO_RD_BASE + 1, &ret) << 8; if (ret) goto release; //bufferPrintf("wlan: firmware helper wants %d bytes\r\n", (int)req_size); if (req_size == 0) { bufferPrintf("wlan: firmware helper gave up early\r\n"); ret = -1; goto release; } if (req_size & 0x01) { bufferPrintf("wlan: firmware helper signalled error\r\n"); ret = -1; goto release; } if (req_size > size) req_size = size; while (req_size) { if(req_size > 512) chunk_size = 512; else chunk_size = req_size; memcpy(chunk_buffer, firmware, chunk_size); //bufferPrintf("wlan: sending %d bytes (%d bytes) chunk\r\n", // chunk_size, (chunk_size + 31) / 32 * 32); int to_send; to_send = chunk_size / 32; to_send *= 32; if(to_send < chunk_size) to_send += 32; ret = sdio_writesb(1, ioport, chunk_buffer, to_send); if (ret) goto release; firmware += chunk_size; size -= chunk_size; req_size -= chunk_size; } } ret = 0; bufferPrintf("wlan: waiting for firmware to boot\r\n"); /* wait for the firmware to boot */ startTime = timer_get_system_microtime(); while (TRUE) { uint16_t scratch; scratch = wlan_read_scratch(&ret); if (ret) goto release; if (scratch == IF_SDIO_FIRMWARE_OK) break; if(has_elapsed(startTime, 1000 * 1000)) { ret = -1; goto release; } udelay(10000); } ret = 0; bufferPrintf("wlan: firmware booted!\r\n"); release: free(chunk_buffer); release_fw: if (ret) bufferPrintf("wlan: failed to load firmware\r\n"); return ret; }
int menu_setup(int timeout) { FBWidth = currentWindow->framebuffer.width; FBHeight = currentWindow->framebuffer.height; imgiPhoneOS = framebuffer_load_image(dataiPhoneOSPNG, dataiPhoneOSPNG_size, &imgiPhoneOSWidth, &imgiPhoneOSHeight, TRUE); imgiPhoneOSSelected = framebuffer_load_image(dataiPhoneOSSelectedPNG, dataiPhoneOSSelectedPNG_size, &imgiPhoneOSWidth, &imgiPhoneOSHeight, TRUE); imgConsole = framebuffer_load_image(dataConsolePNG, dataConsolePNG_size, &imgConsoleWidth, &imgConsoleHeight, TRUE); imgConsoleSelected = framebuffer_load_image(dataConsoleSelectedPNG, dataConsoleSelectedPNG_size, &imgConsoleWidth, &imgConsoleHeight, TRUE); imgAndroidOS_unblended = framebuffer_load_image(dataAndroidOSPNG, dataAndroidOSPNG_size, &imgAndroidOSWidth, &imgAndroidOSHeight, TRUE); imgAndroidOSSelected_unblended = framebuffer_load_image(dataAndroidOSSelectedPNG, dataAndroidOSSelectedPNG_size, &imgAndroidOSWidth, &imgAndroidOSHeight, TRUE); imgHeader = framebuffer_load_image(dataHeaderPNG, dataHeaderPNG_size, &imgHeaderWidth, &imgHeaderHeight, TRUE); bufferPrintf("menu: images loaded\r\n"); imgiPhoneOSX = (FBWidth - imgiPhoneOSWidth) / 2; imgiPhoneOSY = 84; imgConsoleX = (FBWidth - imgConsoleWidth) / 2; imgConsoleY = 207; imgAndroidOSX = (FBWidth - imgAndroidOSWidth) / 2; imgAndroidOSY = 330; imgHeaderX = (FBWidth - imgHeaderWidth) / 2; imgHeaderY = 17; framebuffer_draw_image(imgHeader, imgHeaderX, imgHeaderY, imgHeaderWidth, imgHeaderHeight); framebuffer_draw_rect_hgradient(0, 42, 0, 360, FBWidth, (FBHeight - 12) - 360); framebuffer_draw_rect_hgradient(0x22, 0x22, 0, FBHeight - 12, FBWidth, 12); framebuffer_setloc(0, 47); framebuffer_setcolors(COLOR_WHITE, 0x222222); framebuffer_print_force(OPENIBOOT_VERSION_STR); framebuffer_setcolors(COLOR_WHITE, COLOR_BLACK); framebuffer_setloc(0, 0); imgAndroidOS = malloc(imgAndroidOSWidth * imgAndroidOSHeight * sizeof(uint32_t)); imgAndroidOSSelected = malloc(imgAndroidOSWidth * imgAndroidOSHeight * sizeof(uint32_t)); framebuffer_capture_image(imgAndroidOS, imgAndroidOSX, imgAndroidOSY, imgAndroidOSWidth, imgAndroidOSHeight); framebuffer_capture_image(imgAndroidOSSelected, imgAndroidOSX, imgAndroidOSY, imgAndroidOSWidth, imgAndroidOSHeight); framebuffer_blend_image(imgAndroidOS, imgAndroidOSWidth, imgAndroidOSHeight, imgAndroidOS_unblended, imgAndroidOSWidth, imgAndroidOSHeight, 0, 0); framebuffer_blend_image(imgAndroidOSSelected, imgAndroidOSWidth, imgAndroidOSHeight, imgAndroidOSSelected_unblended, imgAndroidOSWidth, imgAndroidOSHeight, 0, 0); Selection = MenuSelectioniPhoneOS; OtherFramebuffer = CurFramebuffer; CurFramebuffer = (volatile uint32_t*) NextFramebuffer; drawSelectionBox(); pmu_set_iboot_stage(0); memcpy((void*)NextFramebuffer, (void*) CurFramebuffer, NextFramebuffer - (uint32_t)CurFramebuffer); uint64_t startTime = timer_get_system_microtime(); while(TRUE) { if(buttons_is_pushed(BUTTONS_HOLD)) { toggle(TRUE); startTime = timer_get_system_microtime(); udelay(200000); } #ifndef CONFIG_IPOD if(!buttons_is_pushed(BUTTONS_VOLUP)) { toggle(FALSE); startTime = timer_get_system_microtime(); udelay(200000); } if(!buttons_is_pushed(BUTTONS_VOLDOWN)) { toggle(TRUE); startTime = timer_get_system_microtime(); udelay(200000); } #endif if(buttons_is_pushed(BUTTONS_HOME)) { break; } if(timeout > 0 && has_elapsed(startTime, (uint64_t)timeout * 1000)) { bufferPrintf("menu: timed out, selecting current item\r\n"); break; } udelay(10000); } if(Selection == MenuSelectioniPhoneOS) { Image* image = images_get(fourcc("ibox")); if(image == NULL) image = images_get(fourcc("ibot")); void* imageData; images_read(image, &imageData); chainload((uint32_t)imageData); } if(Selection == MenuSelectionConsole) { // Reset framebuffer back to original if necessary if((uint32_t) CurFramebuffer == NextFramebuffer) { CurFramebuffer = OtherFramebuffer; currentWindow->framebuffer.buffer = CurFramebuffer; lcd_window_address(2, (uint32_t) CurFramebuffer); } framebuffer_setdisplaytext(TRUE); framebuffer_clear(); } if(Selection == MenuSelectionAndroidOS) { // Reset framebuffer back to original if necessary if((uint32_t) CurFramebuffer == NextFramebuffer) { CurFramebuffer = OtherFramebuffer; currentWindow->framebuffer.buffer = CurFramebuffer; lcd_window_address(2, (uint32_t) CurFramebuffer); } framebuffer_setdisplaytext(TRUE); framebuffer_clear(); #ifndef NO_HFS radio_setup(); nand_setup(); fs_setup(); if(globalFtlHasBeenRestored) /* if ftl has been restored, sync it, so kernel doesn't have to do a ftl_restore again */ { if(ftl_sync()) { bufferPrintf("ftl synced successfully"); } else { bufferPrintf("error syncing ftl"); } } pmu_set_iboot_stage(0); startScripting("linux"); //start script mode if there is a script file boot_linux_from_files(); #endif } return 0; }
int wlan_prog_helper(const uint8_t * firmware, int size) { int ret; uint8_t status; uint8_t *chunk_buffer; uint32_t chunk_size; uint64_t startTime; bufferPrintf("wlan: programming firmware helper...\r\n"); chunk_buffer = (uint8_t*) memalign(64, 4); if (!chunk_buffer) { ret = -1; goto release_fw; } ret = sdio_set_block_size(1, 32); if (ret) goto release; while (size) { startTime = timer_get_system_microtime(); while (TRUE) { status = sdio_readb(1, IF_SDIO_STATUS, &ret); if (ret) goto release; if ((status & IF_SDIO_IO_RDY) && (status & IF_SDIO_DL_RDY)) break; if(has_elapsed(startTime, 1000 * 1000)) { ret = -1; goto release; } udelay(1000); } if(size > 60) chunk_size = 60; else chunk_size = size; *((uint32_t*)chunk_buffer) = chunk_size; memcpy(chunk_buffer + 4, firmware, chunk_size); //bufferPrintf("wlan: sending %d bytes chunk\r\n", chunk_size); ret = sdio_writesb(1, ioport, chunk_buffer, 64); if (ret) goto release; firmware += chunk_size; size -= chunk_size; } /* an empty block marks the end of the transfer */ memset(chunk_buffer, 0, 4); ret = sdio_writesb(1, ioport, chunk_buffer, 64); if (ret) goto release; bufferPrintf("wlan: waiting for helper to boot\r\n"); /* wait for the helper to boot by looking at the size register */ startTime = timer_get_system_microtime(); while (TRUE) { uint16_t req_size; req_size = sdio_readb(1, IF_SDIO_RD_BASE, &ret); if (ret) goto release; req_size |= sdio_readb(1, IF_SDIO_RD_BASE + 1, &ret) << 8; if (ret) goto release; if (req_size != 0) break; if(has_elapsed(startTime, 1000 * 1000)) { ret = -1; goto release; } udelay(10000); } ret = 0; bufferPrintf("wlan: helper has booted!\r\n"); release: free(chunk_buffer); release_fw: if (ret) bufferPrintf("wlan: failed to load helper firmware\r\n"); return ret; }