/* * Copyright (c) 2011-2015, Emulex * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FW_UPDATE_RESTRICTED -14 #define UNSUPPORTED_FW_IMAGE -13 static uint16_t get_op_type(uint8_t entry_type); static off_t find_ufi_dir(int fd); /** * @brief Returns the file type. * * This function returns the type of the file - Skyhawk UFI or Lancer object file. * * @param buf_p pointer to the memory holding an initial file data * @param len the data length */ static fFormat_t retrieve_file_format (uint8_t *buf_p, int len) { fFormat_t result = UNKNOWN; uint32_t dw2; /* * Skyhawk UFI files always starts with 'S' character . * It is the first letter of a "ServerEngines" string. */ if (buf_p [0] == 'S' ) { ufi_file_header_t *file_hdr; file_hdr = (ufi_file_header_t *)buf_p; /* * In the Skyhawk format the first byte of the build_version field * gives us the device family. 4=Skyhawk */ if (file_hdr->build_version[0] == '4') { result = SKYHAWK_UFI; } } else { dw2 = *(uint32_t *) &buf_p[4]; /* * Verify if the file is an Lancer Object file. * 0xFEAA is a magic number, 0001 (normal) and 0002 (compressed) is a file types * 0003 seems to be a new valure for Lancer G6 files, 0005 is for Prism. */ if ((dw2 == 0xFEAA0001) || (SWAP_4BYTES(dw2) == 0xFEAA0001) || (dw2 == 0xFEAA0002) || (SWAP_4BYTES(dw2) == 0xFEAA0002) || (dw2 == 0xFEAA0003) || (SWAP_4BYTES(dw2) == 0xFEAA0003) ) { result = LANCER; } else if ((dw2 == 0xFEAA0005) || (SWAP_4BYTES(dw2) == 0xFEAA0005)) { result = PRISM; } } return result; } /** * @brief Initializes files descriptor structure. * * Initializes the file descriptor structure. * * @param file_name_p filename information * @param fdsc_p a file descriptor pointer */ static int file_handler_init (const char *filename_p, dwnld_file_desc_t *fdesc_p) { uint8_t *buf_p; int rd; const char *str_p; off_t ufi_dir_offset; int rc; /* * Get a file statistic information (file length and ...) */ rc = stat(filename_p, &(fdesc_p->st)); if (rc < 0) { if (errno == ENOENT) { printf("File %s not found\n", filename_p); } else { printf("File %s stat returns %s\n", filename_p, strerror(errno)); } return -1; } str_p = strrchr (filename_p, '/'); if (str_p == NULL) { str_p = filename_p; } else { str_p++; } if(strlen(str_p) < 127 ) { strncpy (fdesc_p->file_name, str_p, strlen(str_p)); }else { printf ("file name more than 128 chars\n"); return -1; } /* * Open the file .. */ if ((fdesc_p->fd = open(filename_p, O_RDONLY)) < 0) { printf ("open file failure, (%s)\n", strerror(errno)); return -1; } /* * Allocate a space the file header information. * This includes both the UFI File Header and the Image Header for a UFI file * * UFI header is bigger than Lancer file object header. * Allocate the space to fit UFI header. * */ if ((buf_p = zmalloc(UFI_FILE_HEADER_LENGTH)) == NULL) { close(fdesc_p->fd); printf("Malloc failure - (%s)\n", strerror(errno)); return MALLOC_FAILED; } /* * Copy data into the memory */ if ((rd = read(fdesc_p->fd, buf_p, UFI_FILE_HEADER_LENGTH)) != UFI_FILE_HEADER_LENGTH) { printf("Read failure %d, (%s)\n", rd, strerror(errno)); zfree(buf_p); close(fdesc_p->fd); return -1; } /* * Get the file type and update file descriptor fields. */ switch (retrieve_file_format (buf_p, UFI_FILE_HEADER_LENGTH)) { case SKYHAWK_UFI: fdesc_p->file_type = FILE_TYPE_UFI; fdesc_p->hba_file_type = HBA_FILE_SKYHAWK; fdesc_p->ufi_flash_hdr = zmalloc(sizeof(flash_directory_t)); ufi_dir_offset = find_ufi_dir(fdesc_p->fd); if (ufi_dir_offset < 0) { printf("Unable to find UFI directory in UFI file.\n"); zfree(buf_p); close(fdesc_p->fd); return -1; } if(lseek(fdesc_p->fd, ufi_dir_offset, SEEK_SET)) { /* copy in our UFI flash directory header */ read(fdesc_p->fd, fdesc_p->ufi_flash_hdr, sizeof(flash_directory_t)); } /* First 128 bytes are the UFI header */ memcpy((uint8_t *)&(fdesc_p->ufi_hdr), buf_p, sizeof(ufi_file_header_t)); /* Next 48 bytes are the Image Header */ memcpy((uint8_t *)&(fdesc_p->img_hdr), (buf_p + sizeof(ufi_file_header_t)), sizeof(ufi_image_header_t)); break; case LANCER: fdesc_p->hba_file_type = HBA_FILE_LANCER; break; case PRISM: fdesc_p->hba_file_type = HBA_FILE_PRISM; break; case UNKNOWN: printf ("Unsupported file type.\n"); zfree(buf_p); close(fdesc_p->fd); return -1; } /* * Reset the file reader location. */ lseek(fdesc_p->fd, 0, SEEK_SET); zfree(buf_p); return 0; } /* end of download_ufi_file_int */ /** * @brief Displays content of UFI file header structure. * * The function parses and displays the content of the UFI file header structure. * * @param ufi_hdr_p UFI header structure pointer. */ static void display_ufi_header(ufi_file_header_t *ufi_hdr_p) { int i; printf("\nUFI File Header:\n"); printf("Signature:\n"); for (i = 0; i < 52; i++) { if (isprint(ufi_hdr_p->signature[i])) { printf("%c", ufi_hdr_p->signature[i]); } } printf("\nUFI Version: %08x\n", ufi_hdr_p->ufi_version); printf("FileLen: %d (%08X)\n", ufi_hdr_p->file_len, ufi_hdr_p->file_len); printf("Checksum : %08X\n", ufi_hdr_p->checksum); printf("Antidote: %08X\n", ufi_hdr_p->antidote); printf("Number of Images: %d\n", ufi_hdr_p->numOfImgs); printf("Build: %s\n", trimmed_string(ufi_hdr_p->build_version)); printf("Target platform: "); switch(ufi_hdr_p->build_version[0]) { case '4': printf("Skyhawk\n"); break; default: printf("Unknown\n"); } printf("ASIC Support: 0x%x", ufi_hdr_p->asic_support); if (ufi_hdr_p->build_version[0] == '4') { if (ufi_hdr_p->asic_support == 0x10) { printf(" (Skyhawk B0 only)\n"); } else if (ufi_hdr_p->asic_support == 0x11) { printf(" (Skyhawk B0 or B1 only)\n"); } } else { printf("\n"); } printf("End of UFI File Header dump\n"); } /** * @brief Displays content of UFI flash directory header. * * This function is in part used for debugging and determining what sections exist * in a given UFI file * * @param flash_dir UFI flash directory structure pointer. */ void display_ufi_flash_directory(dwnld_file_desc_t *fdesc) { int i; flash_directory_t *flash_dir = fdesc->ufi_flash_hdr; flash_directory_entry_t *entry; uint32_t crc; off_t offset; printf("\nFlash Directory Header:\n"); printf("Flash Directory Format: %d\n", flash_dir->formatRevNumber); printf("Number of Directory Entries: %d\n", flash_dir->numImages); printf("Flash Directory Entries:\n"); for (i=0; inumImages; i++) { entry = &(flash_dir->entries[i]); printf(" Version: %s\n", trimmed_string(entry->version)); printf(" Entry Type: 0x%02x (%s)\n", entry->entryType, decode_entry_type(entry->entryType)); if (entry->opType == 0xffff) { printf(" Op Type: 0x%04x (based on Entry Type)\n", get_op_type(entry->entryType)); } else { printf(" Op Type: 0x%04x\n", entry->opType); } printf(" Offset: 0x%08x\n", entry->offset); printf(" Image Size: 0x%08x\n", entry->imageSize); printf(" Max Image Size: 0x%08x\n", entry->maxSize); printf(" Checksum: 0x%08x\n", entry->checkSum); /* Get CRC from the end of the actual component */ offset = (long)(entry->offset + entry->maxSize + UFI_FILE_HEADER_LENGTH - 4); if (lseek(fdesc->fd, offset , SEEK_SET) != offset) { fprintf(stderr, "couldn't seek to %ld\n", offset); return; } if (read(fdesc->fd, &crc, 4) != 4) { fprintf(stderr, "Didn't get %d bytes on read\n", 4); return; } printf(" CRC: 0x%08x\n", crc); } printf("\nEnd of Flash Directory Listing dump\n"); } /** * @brief Displays content of UFI image header structure. * * The function displays the content of the UFI image header structure. * * @param ufi_hdr_p UFI image header structure pointer. */ static void display_ufi_image_hdr(ufi_image_header_t *img_hdr_p) { printf("\nFirmware Image Header:\n"); printf("Image ID: %08X\n", img_hdr_p->imageId); printf("Image Offset: %08X\n", img_hdr_p->imageOffset); printf("Image Length: %08X\n", img_hdr_p->imageLength); printf("Image Checksum: %08X\n", img_hdr_p->imageChecksum); printf("Image Version: %s\n", trimmed_string(img_hdr_p->imageVer)); printf("End of Firmware Image Header dump\n"); } /** * @brief Skyhawk firmware download. * * Skyhawk UFI file is read into memory and it is forwarded to the HBA in segments. * * @param device_p A pointer to HBA structure descriptor. * @param fdsc_p A pointer to a file descriptor structure. */ static int download_skyhawk_image (elxu_device_t *device_p, dwnld_file_desc_t *fdsc_p) { flash_directory_t *ufi_dir; int entry_index; int largest_entry; uint8_t *buffer; int rval; long lval; int asic_support_ok; ufi_dir = fdsc_p->ufi_flash_hdr; /* * Check the ASIC_Support field in the UFI header and see if this firmware * is supported on the target ASIC. */ asic_support_ok = 0; switch(fdsc_p->ufi_hdr.asic_support) { case 0x10: /* B0 Firmware, supported only on B0 chips */ if (device_p->asic_id == 0x0410) { asic_support_ok = 1; } break; case 0x11: /* B1 Firmware, supported on B0 and B1 chips */ if ((device_p->asic_id == 0x0410) || (device_p->asic_id == 0x0411)) { asic_support_ok = 1; } break; default: /* Undefined asic_support value */ break; } if (asic_support_ok == 0) { printf("Provided UFI is not supported on this device.\n"); printf("UFI version is 0x%02x, device version is 0x%04x\n", fdsc_p->ufi_hdr.asic_support, device_p->asic_id); close(fdsc_p->fd); return -1; } display_ufi_header(&fdsc_p->ufi_hdr); display_ufi_image_hdr(&fdsc_p->img_hdr); display_ufi_flash_directory(fdsc_p); /* * Allocate a buffer big enough to hold the largest FW section. */ largest_entry = 0; for (entry_index=0; entry_index < ufi_dir->numImages; entry_index++) { if (ufi_dir->entries[entry_index].maxSize > largest_entry) { largest_entry = ufi_dir->entries[entry_index].maxSize; } } buffer = malloc(largest_entry); if (buffer == NULL) { printf("Unable to allocate memory to hold FW image\n"); return -1; } for (entry_index=0; entry_index < ufi_dir->numImages; entry_index++) { flash_directory_entry_t *entry; uint32_t op_type; uint32_t entry_type; uint32_t offset; uint32_t file_crc; uint32_t chip_crc; off_t seek_offset; entry = &(ufi_dir->entries[entry_index]); op_type = entry->opType; entry_type = entry->entryType; if (op_type == 0xffff) { op_type = get_op_type(entry_type); } offset = entry->offset; if (op_type == 0xffff) { printf("Invalid op_type for section %d\n", entry_index); free(buffer); return -1; } /* Read the CRC of the current segment on the device */ rval = mbox_read_flashrom(device_p, MGMT_FLASHROM_OPCODE_IMAGE_CRC, op_type, offset, buffer, 4); /* If reading the CRC failed then we'll just assume the CRCs are different */ if (rval == 0) { chip_crc = (buffer[3] << 24) | (buffer[2] << 16) | (buffer[1] << 8) | buffer[0]; /* Read the CRC from the firmware file */ seek_offset = entry->offset + entry->maxSize + UFI_FILE_HEADER_LENGTH - 4; if (lseek(fdsc_p->fd, seek_offset , SEEK_SET) != seek_offset) { fprintf(stderr, "couldn't seek to %ld\n", seek_offset); } if (read(fdsc_p->fd, &file_crc, 4) != 4) { fprintf(stderr, "Didn't get %d bytes on read\n", 4); } /* * Compare the CRC from the running FW with the CRC in the file * If they match we can skip this section. */ if (chip_crc == file_crc) { printf("%08x: [%s] Unchanged, skipping\n", entry->offset, trimmed_string(entry->version)); continue; } } printf("%08x: [%s] (%d bytes)\n", entry->offset, trimmed_string(entry->version), entry->maxSize); lval = lseek(fdsc_p->fd, entry->offset+UFI_FILE_HEADER_LENGTH, SEEK_SET); if (lval != entry->offset+UFI_FILE_HEADER_LENGTH) { printf("Seek failed to offset 0x%x\n", entry->offset); free(buffer); return -1; } lval = read(fdsc_p->fd, buffer, entry->maxSize); if (lval != entry->maxSize) { printf("Read failed in segment %d\n", entry_index); printf("Expected %d bytes, got %ld\n", entry->maxSize, lval); free(buffer); return -1; } rval = ufi_image_download(device_p, op_type, buffer, entry->maxSize); if (rval != 0) { /* This section of code is temporary (as of March 2014) for the transition between old and new Skyhawk firmware */ if (entry->opType == 0xffff) { if ((entry_type == 0x30) || (entry_type == 0xc0) || (entry_type == 0xd0) || (entry_type == 0xd1) || (entry_type == 0xd2)) { if ((rval == -2) || (rval == -3)) { /* It's OK to ignore this error */ continue; } } } /* At this point we have an error, and it's not one of the cases we're supposed to ignore */ if ((rval == -2) || (rval == -3)) { /* * These two errors indicate a case where we're moving from old style * firmware to new style. This requires a reboot and a second download of * the same firmware. */ printf("This special firmware release requires the firmware download process\n"); printf("to be performed twice. You must reboot the system and download the same\n"); printf("firmware file again to complete the download process.\n"); free(buffer); return -1; } else { /* * Some other unexpected error has occurred. */ printf("Firmware download failed, status is 0x%02x\n", -rval); free(buffer); return -1; } } } printf("Firmware download successful.\n"); printf("Reboot the system to activate the new firmware.\n"); free(buffer); return 0; } /** * @brief Lancer file object. * * Downloads the Lancer file object into specified HBA object directory (object). * * @param device_p A pointer to HBA structure descriptor. * @param object_p An lancer object type * @param fdsc_p A pointer to a file descriptor structure. */ static int download_lancer_object_file (elxu_device_t *device_p, const char *object_p, dwnld_file_desc_t *fdsc_p) { struct stat stat_buf; unsigned char *buf; off_t filesize; int rval; char change_status_text[20]; int change_status; if (fstat(fdsc_p->fd, &stat_buf) == -1) { perror("fstat"); return -1; } filesize = stat_buf.st_size; buf = malloc(filesize); if (buf == NULL) { perror("malloc"); return -1; } if (read(fdsc_p->fd, buf, filesize) != filesize) { fprintf(stderr, "Didn't get %ld bytes on read\n", filesize); free(buf); return -1; } rval = elxu_device_exec(device_p, "/ocs/firmware_write", buf, filesize, change_status_text, 20); if (rval == 0) { printf("Firmware download successful.\n"); change_status = strtoul(change_status_text, NULL, 0); switch (change_status) { case 0x00: printf("No reset needed, new firmware is active.\n"); break; case 0x01: printf("A physical device reset (host reboot) is needed to activate the new firmware\n"); break; case 0x02: case 0x03: printf("Use the firmware-reset option to activate the new firmware.\n"); break; case 0x04: printf("Use port-migration or firmware-reset option to activate new firmware.\n"); printf("Note: With port-migration option, Only particular port will run with new FW\n"); break; default: printf("Unexected value for change_status: %d\n", change_status); break; } } else if (rval == FW_UPDATE_RESTRICTED) { printf("This firmware version is not supported on this HBA model.\n"); } else if (rval == UNSUPPORTED_FW_IMAGE) { printf("Download failed. Firmware does not meet minimum version requirement.\n"); } else { perror("ioctl"); } free(buf); return rval; } /** * @brief Lancer firmware download. * * Donwloads the grp file into HBA. * * @param device_p A pointer to HBA structure descriptor. * @param fdsc_p A pointer to a file descriptor structure. */ static int download_lancer_image (elxu_device_t *device_p, dwnld_file_desc_t *fdsc_p) { int retval = -1; /* * Validate the file type using the file extention. */ if (strstr (fdsc_p->file_name, ".grp") != NULL) { /* * When .grp file, use only the object name - clear file name */ fdsc_p->file_name[0] = '\0'; retval = download_lancer_object_file (device_p, "/prg/", fdsc_p); } else { printf ("The file %s has to be a .grp file type \n", fdsc_p->file_name); } return retval; } /** * @brief Firmware download. * * Based on the file type, download the firmware to an appropriate HBA. * * @param device_p A pointer to HBA structure descriptor. * @param fdsc_p A pointer to a file descriptor structure. * * @return Returns 0 on success, non-zero on failure. */ static int download_handler(elxu_device_t *device_p, dwnld_file_desc_t *fdsc_p) { int retval; switch (fdsc_p->hba_file_type) { case HBA_FILE_SKYHAWK: if (is_skyhawk(device_p)) { printf("Download starting\n"); retval = download_skyhawk_image (device_p, fdsc_p); } else { printf("Firmware is Skyhawk UFI but device is not Skyhawk\n"); retval = -1; } break; case HBA_FILE_LANCER: case HBA_FILE_PRISM: if (is_lancer(device_p) || is_prism(device_p)) { printf("Download starting\n"); retval = download_lancer_image (device_p, fdsc_p); } else { printf("Firmware is Lancer FW file but device is not Lancer\n"); retval = -1; } break; default: printf ("Unknown HBA file type %d \n", fdsc_p->hba_file_type); retval = -1; } if (retval < 0) { printf ("Download Failed\n"); } else { printf("Download Success\n"); if (is_calypso(device_p)) { printf("NOTE: The interface just updated is a dual controller interface.\n"); printf(" You may need to update the firmware on the second controller\n"); printf(" to prevent issues on next boot.\n"); } } return retval; } /** * @brief flash and file data comparison functionality. * * Compare flash with the UFI or Bin file data. * * @param device_p The pointer to the HBA descriptor structure. * @param memFdsc_p The pointer to the memory file descriptor structure. */ static int hba_image_section_fw_verify(elxu_device_t *device_p, dwnld_file_desc_t *fdsc_p) { int failure_detected = 0; uint8_t *file_sect_buf_p; uint8_t *flash_sect_buf_p; flash_directory_t *ufi_dir; int entry_index; int largest_entry; long lval; if (device_p == NULL || fdsc_p == NULL) { printf("Null pointer \n"); return (-1); } ufi_dir = fdsc_p->ufi_flash_hdr; /* * Validate the file type: UFI or Bin */ if (fdsc_p->file_type != FILE_TYPE_UFI) { printf("FW file is not a UFI file\n"); return -1; } printf ("Verify firmware sections ... \n\n"); /* * Find the size of the largest UFI entry */ largest_entry = 0; for (entry_index=0; entry_index < ufi_dir->numImages; entry_index++) { if (ufi_dir->entries[entry_index].maxSize > largest_entry) { largest_entry = ufi_dir->entries[entry_index].maxSize; } } /* * Allocate a buffer for FW sections from the file */ if ((file_sect_buf_p = malloc(largest_entry)) == NULL) { printf("Malloc failure (%s)\n", strerror(errno)); close (fdsc_p->fd); return MALLOC_FAILED; } /* * Allocate a buffer for FW sections from flash */ if ((flash_sect_buf_p = malloc(largest_entry)) == NULL) { printf("Malloc failure (%s)\n", strerror(errno)); close (fdsc_p->fd); free(file_sect_buf_p); return MALLOC_FAILED; } /* Iterate over entries in the UFI dirctory */ for (entry_index=0; entry_index < ufi_dir->numImages; entry_index++) { flash_directory_entry_t *entry; uint32_t op_type; uint32_t entry_type; printf("%d: ", entry_index); entry = &(ufi_dir->entries[entry_index]); op_type = entry->opType; entry_type = entry->entryType; if (op_type == 0xffff) { op_type = get_op_type(entry_type); } if (op_type == 0xffff) { printf("ERROR: Invalid op_type\n"); ++failure_detected; continue; } /* Read the section from the file */ lval = lseek(fdsc_p->fd, entry->offset+UFI_FILE_HEADER_LENGTH, SEEK_SET); if (lval != entry->offset+UFI_FILE_HEADER_LENGTH) { printf("ERROR: File seek failed\n"); ++failure_detected; continue; } lval = read(fdsc_p->fd, file_sect_buf_p, entry->maxSize); if (lval != entry->maxSize) { printf("ERROR: File read failed\n"); ++failure_detected; continue; } /* Read the section from the firmware */ if (ufi_image_upload(device_p, op_type, flash_sect_buf_p, entry->maxSize) != 0) { printf ("ERROR: Firmware read failed\n");; ++failure_detected; continue; } /* * Compare buffers */ if (memcmp(flash_sect_buf_p, file_sect_buf_p, entry->maxSize) != 0) { printf("MISMATCH: Sections do not match\n"); failure_detected++; continue; } else { printf("Match\n"); } } /* * Mismatch detected */ if (failure_detected) { printf("Firmware verification failed - %d mismatched sections \n", failure_detected); } else { printf("Firmware verification was successful! \n"); } zfree(file_sect_buf_p); zfree(flash_sect_buf_p); zfree(fdsc_p->ufi_flash_hdr); close(fdsc_p->fd); return 0; } /** * @brief Determine mailbox op_type from UFI entry_type. * * @par Description * Each firmware section in a UFI directory has an entry type that * describes what type of entry it it. Newer UFI files also have * an op_type field that specify which op_type argument should be * passed in the COMMON_WRITE_FLASHROM mailbox comman. Older UFI * files don't have the op_type present in the directory and it must * be determined from the entry_type. This function returns an * op_type based on the entry_type. * * @param entry_type The entry type to look up. * * @return Returns the op_type that corresponds to the given entry type, * or 0xffff if no match is found. */ static uint16_t get_op_type(uint8_t entry_type) { static int table[][2] = { {0x10, 0x000d}, {0xa0, 0x0000}, {0xb0, 0x0009}, {0x20, 0x0003}, {0x21, 0x0008}, {0x22, 0x0002}, {0xe0, 0x0001}, {0x30, 0x0016}, {0xc0, 0x0015}, {0xd0, 0x0012}, {0xd1, 0x0013}, {0xd2, 0x0017}, {0xd3, 0x0014}, {0x23, 0x0011}, }; int i; for (i=0; i * Function NameDescription * download_ufi_image()Download a .ufi firmware image to an OCe11102 adapter * using the file handler in the elxu_dwnld.c module. * download_lancer_image()Download a .grp firmware image to an LPe16000/LPe16002 * adapter data using the file handler in the elxu_dwnld.c * module. * fat_upload()Upload the OCe11102 FAT using the file handler in the * elxu_dump.c module. * ext_fat_capabilities_upld()Upload the OCe11102 extended FAT capabilities data using * the file handler in the elxu_dump.c module. * ext_fat_snapshot_upload()Upload the OCe11102 extended FAT snapshot using the file * handler in the elxu_dump.c module. * lancer_hba_dump_upload()Upload the LPe16000/LPe16002 adapter data using the file * handler in the elxu_dump.c module. * * */