| /* |
| * Copyright (c) 2020, Mediatek Inc. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #pragma GCC diagnostic ignored "-Wall" |
| #pragma GCC diagnostic ignored "-Wextra" |
| #pragma GCC diagnostic ignored "-Wsign-compare" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <ctype.h> |
| #include <endian.h> |
| #include <errno.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| |
| #include "ufs.h" |
| #include "ufs_cmds.h" |
| #include "options.h" |
| |
| #define STR_BUF_LEN 33 |
| #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) |
| #define ATTR_RSRV() "Reserved", BYTE, ACC_INVALID, MODE_INVALID, LEVEL_INVALID |
| |
| #define CONFIG_HEADER_OFFSET 0x16 |
| #define CONFIG_LUN_OFFSET 0x1A |
| |
| /* Config desc. offsets for UFS 2.0 - 3.0 spec */ |
| #define CONFIG_HEADER_OFFSET_3_0 0x10 |
| #define CONFIG_LUN_OFFSET_3_0 0x10 |
| |
| struct desc_field_offset device_desc_field_name[] = { |
| {"bLength", 0x00, BYTE}, |
| {"bDescriptorType", 0x01, BYTE}, |
| {"bDevice", 0x02, BYTE}, |
| {"bDeviceClass", 0x03, BYTE}, |
| {"bDeviceSubClass", 0x04, BYTE}, |
| {"bProtocol", 0x05, BYTE}, |
| {"bNumberLU", 0x06, BYTE}, |
| {"bNumberWLU", 0x07, BYTE}, |
| {"bBootEnable", 0x08, BYTE}, |
| {"bDescrAccessEn", 0x09, BYTE}, |
| {"bInitPowerMode", 0x0A, BYTE}, |
| {"bHighPriorityLUN", 0x0B, BYTE}, |
| {"bSecureRemovalType", 0x0C, BYTE}, |
| {"bSecurityLU", 0x0D, BYTE}, |
| {"bBackgroundOpsTermLat", 0x0E, BYTE}, |
| {"bInitActiveICCLevel", 0x0F, BYTE}, |
| {"wSpecVersion", 0x10, WORD}, |
| {"wManufactureDate", 0x12, WORD}, |
| {"iManufactureName", 0x14, BYTE}, |
| {"iProductName", 0x15, BYTE}, |
| {"iSerialNumber", 0x16, BYTE}, |
| {"iOemID", 0x17, BYTE}, |
| {"ManufacturerID", 0x18, WORD}, |
| {"bUD0BaseOffset", 0x1A, BYTE}, |
| {"bUDConfigPLength", 0x1B, BYTE}, |
| {"bDeviceRTTCap", 0x1C, BYTE}, |
| {"wPeriodicRTCUpdate", 0x1D, WORD}, |
| {"bUFSFeaturesSupport", 0x1F, BYTE}, |
| {"bFFUTimeout", 0x20, BYTE}, |
| {"bQueueDepth", 0x21, BYTE}, |
| {"wDeviceVersion", 0x22, WORD}, |
| {"bNumSecureWPArea", 0x24, BYTE}, |
| {"dPSAMaxDataSize", 0x25, DWORD}, |
| {"bPSAStateTimeout", 0x29, BYTE}, |
| {"iProductRevisionLevel", 0x2A, BYTE}, |
| {"Reserved1", 0x2B, BYTE}, |
| {"Reserved2", 0x2C, DWORD}, |
| {"Reserved3", 0x30, DWORD}, |
| {"Reserved4", 0x34, DWORD}, |
| {"Reserved5", 0x38, DWORD}, |
| {"Reserved6", 0x3c, DWORD}, |
| {"wHPBVersion", 0x40, WORD}, |
| {"bHPBControl", 0x42, BYTE}, |
| {"Reserved8", 0x43, DWORD}, |
| {"Reserved9", 0x47, DDWORD}, |
| {"dExtendedUFSFeaturesSupport", 0x4F, DWORD}, |
| {"bWriteBoosterBufferPreserveUserSpaceEn", 0x53, BYTE}, |
| {"bWriteBoosterBufferType", 0x54, BYTE}, |
| {"dNumSharedWriteBoosterBufferAllocUnits", 0x55, DWORD} |
| }; |
| |
| struct desc_field_offset device_config_desc_field_name[] = { |
| {"bLength", 0x00, BYTE}, |
| {"bDescriptorType", 0x01, BYTE}, |
| {"bConfDescContinue", 0x02, BYTE}, |
| {"bBootEnable", 0x03, BYTE}, |
| {"bDescrAccessEn", 0x04, BYTE}, |
| {"bInitPowerMode", 0x05, BYTE}, |
| {"bHighPriorityLUN", 0x06, BYTE}, |
| {"bSecureRemovalType", 0x07, BYTE}, |
| {"bInitActiveICCLevel", 0x08, BYTE}, |
| {"wPeriodicRTCUpdate", 0x09, WORD}, |
| {"bHPBControl", 0x0B, BYTE}, |
| {"bRPMBRegionEnable", 0x0C, BYTE}, |
| {"bRPMBRegion1Size", 0x0D, BYTE}, |
| {"bRPMBRegion2Size", 0x0E, BYTE}, |
| {"bRPMBRegion3Size", 0x0F, BYTE}, |
| {"bWriteBoosterBufferPreserveUserSpaceEn", 0x10, BYTE}, |
| {"bWriteBoosterBufferType", 0x11, BYTE}, |
| {"dNumSharedWriteBoosterBufferAllocUnits", 0x12, DWORD} |
| }; |
| |
| struct desc_field_offset device_config_unit_desc_field_name[] = { |
| {"bLUEnable", 0x00, BYTE}, |
| {"bBootLunID", 0x01, BYTE}, |
| {"bLUWriteProtect", 0x02, BYTE}, |
| {"bMemoryType", 0x03, BYTE}, |
| {"dNumAllocUnits", 0x04, DWORD}, |
| {"bDataReliability", 0x08, BYTE}, |
| {"bLogicalBlockSize", 0x09, BYTE}, |
| {"bProvisioningType", 0x0A, BYTE}, |
| {"wContextCapabilities", 0x0B, WORD}, |
| {"wLUMaxActiveHPBRegions", 0x10, WORD}, |
| {"wHPBPinnedRegionStartIdx", 0x12, WORD}, |
| {"wNumHPBPinnedRegions", 0x14, WORD}, |
| {"dLUNumWriteBoosterBufferAllocUnits", 0x16, DWORD} |
| }; |
| |
| struct desc_field_offset device_geo_desc_conf_field_name[] = { |
| {"bLength", 0x00, BYTE}, |
| {"bDescriptorType ", 0x01, BYTE}, |
| {"bMediaTechnology", 0x02, BYTE}, |
| {"qTotalRawDeviceCapacity", 0x04, DDWORD}, |
| {"bMaxNumberLU", 0x0C, BYTE}, |
| {"dSegmentSize", 0x0D, DWORD}, |
| {"bAllocationUnitSize", 0x11, BYTE}, |
| {"bMinAddrBlockSize", 0x12, BYTE}, |
| {"bOptimalReadBlockSize", 0x13, BYTE}, |
| {"bOptimalWriteBlockSize", 0x14, BYTE}, |
| {"bMaxInBufferSize", 0x15, BYTE}, |
| {"bMaxOutBufferSize", 0x16, BYTE}, |
| {"bRPMB_ReadWriteSize", 0x17, BYTE}, |
| {"bDynamicCapacityResourcePolicy", 0x18, BYTE}, |
| {"bDataOrdering", 0x19, BYTE}, |
| {"bMaxContexIDNumber", 0x1A, BYTE}, |
| {"bSysDataTagUnitSize", 0x1B, BYTE}, |
| {"bSysDataTagResSize", 0x1C, BYTE}, |
| {"bSupportedSecRTypes", 0x1D, BYTE}, |
| {"wSupportedMemoryTypes", 0x1E, WORD}, |
| {"dSystemCodeMaxNAllocU", 0x20, DWORD}, |
| {"wSystemCodeCapAdjFac", 0x24, DWORD}, |
| {"dNonPersistMaxNAllocU", 0x26, DWORD}, |
| {"wNonPersistCapAdjFac", 0x2A, WORD}, |
| {"dEnhanced1MaxNAllocU", 0x2C, DWORD}, |
| {"wEnhanced1CapAdjFac", 0x30, WORD}, |
| {"dEnhanced2MaxNAllocU", 0x32, DWORD}, |
| {"wEnhanced2CapAdjFac", 0x36, WORD}, |
| {"dEnhanced3MaxNAllocU", 0x38, DWORD}, |
| {"wEnhanced3CapAdjFac", 0x3C, WORD}, |
| {"dEnhanced4MaxNAllocU", 0x3E, DWORD}, |
| {"wEnhanced4CapAdjFac", 0X42, WORD}, |
| {"dOptimalLogicalBlockSize", 0X44, DWORD}, |
| {"bHPBRegionSize", 0X48, BYTE}, |
| {"bHPBNumberLU", 0X49, BYTE}, |
| {"bHPBSubRegionSize", 0X4a, BYTE}, |
| {"wDeviceMaxActiveHPBRegions", 0X4b, WORD}, |
| {"Reserved", 0X4d, WORD}, |
| {"dWriteBoosterBufferMaxNAllocUnits", 0X4f, DWORD}, |
| {"bDeviceMaxWriteBoosterLUs", 0X53, BYTE}, |
| {"bWriteBoosterBufferCapAdjFac", 0X54, BYTE}, |
| {"bSupportedWriteBoosterBufferUserSpaceReductionTypes", 0X55, BYTE}, |
| {"bSupportedWriteBoosterBufferTypes", 0X56, BYTE} |
| }; |
| |
| struct desc_field_offset device_interconnect_desc_conf_field_name[] = { |
| {"bLength", 0x00, BYTE}, |
| {"bDescriptorType", 0x01, BYTE}, |
| {"bcdUniproVersion", 0x02, WORD}, |
| {"bcdMphyVersion ", 0x04, WORD}, |
| }; |
| |
| struct desc_field_offset device_unit_desc_field_name[] = { |
| {"bLength", 0x00, BYTE}, |
| {"bDescriptorType", 0x01, BYTE}, |
| {"bUnitIndex", 0x02, BYTE}, |
| {"bLUEnable", 0x03, BYTE}, |
| {"bBootLunID", 0x04, BYTE}, |
| {"bLUWriteProtect", 0x05, BYTE}, |
| {"bLUQueueDepth", 0x06, BYTE}, |
| {"bPSASensitive", 0x07, BYTE}, |
| {"bMemoryType", 0x08, BYTE}, |
| {"bDataReliability", 0x09, BYTE}, |
| {"bLogicalBlockSize", 0x0A, BYTE}, |
| {"qLogicalBlockCount", 0x0B, DDWORD}, |
| {"dEraseBlockSize", 0x13, DWORD}, |
| {"bProvisioningType", 0x17, BYTE}, |
| {"qPhyMemResourceCount", 0x18, DDWORD}, |
| {"wContextCapabilities", 0x20, WORD}, |
| {"bLargeUnitGranularity_M1", 0x22, BYTE}, |
| {"wLUMaxActiveHPBRegions", 0x23, WORD}, |
| {"wHPBPinnedRegionStartIdx", 0x25, WORD}, |
| {"wNumHPBPinnedRegions", 0x27, WORD}, |
| {"dLUNumWriteBoosterBufferAllocUnits", 0x29, DWORD} |
| }; |
| |
| struct desc_field_offset device_unit_rpmb_desc_field_name[] = { |
| {"bLength", 0x00, BYTE}, |
| {"bDescriptorType", 0x01, BYTE}, |
| {"bUnitIndex", 0x02, BYTE}, |
| {"bLUEnable", 0x03, BYTE}, |
| {"bBootLunID", 0x04, BYTE}, |
| {"bLUWriteProtect", 0x05, BYTE}, |
| {"bLUQueueDepth", 0x06, BYTE}, |
| {"bPSASensitive", 0x07, BYTE}, |
| {"bMemoryType", 0x08, BYTE}, |
| {"bRPMBRegionEnable", 0x09, BYTE}, |
| {"bLogicalBlockSize", 0x0A, BYTE}, |
| {"qLogicalBlockCount", 0x0B, DDWORD}, |
| {"bRPMBRegion0Size", 0x13, BYTE}, |
| {"bRPMBRegion1Size", 0x14, BYTE}, |
| {"bRPMBRegion2Size", 0x15, BYTE}, |
| {"bRPMBRegion3Size", 0x16, BYTE}, |
| {"bProvisioningType", 0x17, BYTE}, |
| {"qPhyMemResourceCount", 0x18, DDWORD}, |
| {"wContextCapabilities", 0x20, WORD}, |
| {"wContextCapabilities", 0x22, BYTE}, |
| }; |
| |
| struct desc_field_offset device_power_desc_conf_field_name[] = { |
| {"bLength", 0x00, BYTE}, |
| {"bDescriptorType", 0x01, BYTE}, |
| {"wActiveICCLevelsVCC", 0x02, 32}, |
| {"wActiveICCLevelsVCCQ", 0x22, 32}, |
| {"wActiveICCLevelsVCCQ2", 0x42, 32} |
| }; |
| |
| struct desc_field_offset device_health_desc_conf_field_name[] = { |
| {"bLength", 0x00, BYTE}, |
| {"bDescriptorType", 0x01, BYTE}, |
| {"bPreEOLInfo", 0x02, BYTE}, |
| {"bDeviceLifeTimeEstA", 0x03, BYTE}, |
| {"bDeviceLifeTimeEstB", 0x04, BYTE}, |
| {"VendorPropInfo", 0x05, 32}, |
| {"dRefreshTotalCount", 0x25, DWORD}, |
| {"dRefreshProgress", 0x29, DWORD}, |
| }; |
| |
| struct query_err_res { |
| char *name; |
| __u8 opcode; |
| }; |
| |
| struct attr_fields ufs_attrs[] = { |
| {"bBootLunEn", BYTE, (URD|UWRT), (READ_ONLY|WRITE_PRSIST), DEV}, |
| {"bMAX_DATA_SIZE_FOR_HPB_SINGLE_CMD", BYTE, URD, READ_ONLY, DEV}, |
| {"bCurrentPowerMode", BYTE, URD, READ_ONLY, DEV}, |
| {"bActiveICCLevel", BYTE, (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| {"bOutOfOrderDataEn", BYTE, (URD|UWRT), (READ_NRML|WRITE_ONCE), DEV}, |
| {"bBackgroundOpStatus", BYTE, URD, READ_ONLY, DEV}, |
| {"bPurgeStatus", BYTE, URD, READ_ONLY, DEV}, |
| {"bMaxDataInSize", BYTE, (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| {"bMaxDataOutSize", BYTE, (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| {"dDynCapNeeded", WORD, URD, READ_ONLY, ARRAY}, |
| {"bRefClkFreq", BYTE, (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| {"bConfigDescrLock", BYTE, (URD|UWRT), (READ_NRML|WRITE_ONCE), DEV}, |
| {"bMaxNumOfRTT", BYTE, (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| {"wExceptionEventControl", WORD, URD, READ_NRML, DEV}, |
| {"wExceptionEventStatus", WORD, URD, READ_ONLY, DEV}, |
| {"dSecondsPassed", DWORD, UWRT, WRITE_ONLY, DEV}, |
| {"wContextConf", WORD, (URD|UWRT), (READ_NRML|WRITE_VLT), ARRAY}, |
| {"Reserved", BYTE, ACC_INVALID, MODE_INVALID, LEVEL_INVALID}, |
| {"Reserved", BYTE, ACC_INVALID, MODE_INVALID, LEVEL_INVALID}, |
| {"Reserved", BYTE, ACC_INVALID, MODE_INVALID, LEVEL_INVALID}, |
| {"bDeviceFFUStatus", BYTE, URD, READ_ONLY, DEV}, |
| {"bPSAState", BYTE, (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| {"dPSADataSize", DWORD, (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| {"bRefClkGatingWaitTime", BYTE, URD, READ_ONLY, DEV}, |
| {"bDeviceCaseRoughTemperaure", BYTE, URD, READ_ONLY, DEV}, |
| {"bDeviceTooHighTempBoundary", BYTE, URD, READ_ONLY, DEV}, |
| /*1A*/ {"bDeviceTooLowTempBoundary", BYTE, URD, READ_ONLY, DEV}, |
| /*1B*/ {"bThrottlingStatus", BYTE, URD, READ_ONLY, DEV}, |
| /*1C*/ {"bWBBufFlushStatus", BYTE, URD, READ_ONLY, DEV | ARRAY}, |
| /*1D*/ {"bAvailableWBBufSize", BYTE, URD, READ_ONLY, DEV | ARRAY}, |
| /*1E*/ {"bWBBufLifeTimeEst", BYTE, URD, READ_ONLY, DEV | ARRAY}, |
| /*1F*/ {"bCurrentWBBufSize", DWORD, URD, READ_ONLY, DEV | ARRAY}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| {ATTR_RSRV()}, |
| /*2C*/ {"bRefreshStatus", BYTE, URD, READ_ONLY, DEV}, |
| {"bRefreshFreq", BYTE, (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| {"bRefreshUnit", BYTE, (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| {"bRefreshMethod", BYTE, (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV} |
| }; |
| |
| struct flag_fields ufs_flags[] = { |
| {"Reserved", ACC_INVALID, MODE_INVALID, LEVEL_INVALID}, |
| {"fDeviceInit", (URD|UWRT), (READ_NRML|SET_ONLY), DEV}, |
| {"fPermanentWPEn", (URD|UWRT), (READ_NRML|WRITE_ONCE), DEV}, |
| {"fPowerOnWPEn", (URD|UWRT), (READ_NRML|WRITE_PWR), DEV}, |
| {"fBackgroundOpsEn", (URD|UWRT), (READ_NRML|WRITE_VLT), DEV}, |
| {"fDeviceLifeSpanModeEn", (URD|UWRT), (READ_NRML|WRITE_VLT), DEV}, |
| {"fPurgeEnable", UWRT, (WRITE_ONLY|WRITE_VLT), DEV}, |
| {"fRefreshEnable", UWRT, (WRITE_ONLY|WRITE_VLT), DEV}, |
| {"fPhyResourceRemoval", (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| {"fBusyRTC", URD, READ_ONLY, DEV}, |
| {"Reserved", ACC_INVALID, MODE_INVALID, LEVEL_INVALID}, |
| {"fPermanentlyDisableFw", (URD|UWRT), (READ_NRML|WRITE_ONCE), DEV}, |
| {"Reserved", ACC_INVALID, MODE_INVALID, LEVEL_INVALID}, |
| /*D*/ {"Reserved", ACC_INVALID, MODE_INVALID, LEVEL_INVALID}, |
| /*E*/ {"fWriteBoosterEn", (URD|UWRT), (READ_NRML|WRITE_VLT), DEV | ARRAY}, |
| /*F*/ {"fWBFlushEn", (URD|UWRT), (READ_NRML|WRITE_VLT), DEV | ARRAY}, |
| /*10h*/ {"fWBFlushDuringHibernate", (URD|UWRT), (READ_NRML|WRITE_VLT), |
| DEV | ARRAY}, |
| /*11h*/ {"fHPBReset", (URD|UWRT), (READ_NRML|SET_ONLY), DEV}, |
| /*12h*/ {"fHPBEn", (URD|UWRT), (READ_NRML|WRITE_PRSIST), DEV}, |
| }; |
| |
| static struct query_err_res query_err_status[] = { |
| {"Success", 0xF0}, |
| {"Reserved1", 0xF1}, |
| {"Reserved2", 0xF2}, |
| {"Reserved3", 0xF3}, |
| {"Reserved4", 0xF4}, |
| {"Reserved5", 0xF5}, |
| {"Parameter not readable", 0xF6}, |
| {"Parameter not written", 0xF7}, |
| {"Parameter already written", 0xF8}, |
| {"Invalid LENGTH", 0xF9}, |
| {"Invalid value", 0xFA}, |
| {"Invalid SELECTOR", 0xFB}, |
| {"Invalid INDEX", 0xFC}, |
| {"Invalid IDN", 0xFD}, |
| {"Invalid OPCODE", 0xFE}, |
| {"General failure", 0xFF} |
| }; |
| |
| static const char *const desc_text[] = { |
| "Device", |
| "Config", |
| "Unit", |
| "RFU0", |
| "Interconnect", |
| "String", |
| "RFU1", |
| "Geometry", |
| "Power", |
| "Health" |
| }; |
| |
| int do_read_desc(int fd, struct ufs_bsg_request *bsg_req, |
| struct ufs_bsg_reply *bsg_rsp, __u8 idn, __u8 index, |
| __u16 desc_buf_len, __u8 *data_buf); |
| static int do_unit_desc(int fd, __u8 lun); |
| static int do_power_desc(int fd); |
| static int do_conf_desc(int fd, __u8 opt, __u8 index, char *data_file); |
| static int do_string_desc(int fd, char *str_data, __u8 idn, __u8 opr, |
| __u8 index); |
| int do_query_rq(int fd, struct ufs_bsg_request *bsg_req, |
| struct ufs_bsg_reply *bsg_rsp, __u8 query_req_func, |
| __u8 opcode, __u8 idn, __u8 index, __u8 sel, |
| __u16 req_buf_len, __u16 res_buf_len, __u8 *data_buf); |
| static int do_write_desc(int fd, struct ufs_bsg_request *bsg_req, |
| struct ufs_bsg_reply *bsg_rsp, __u8 idn, __u8 index, |
| __u16 desc_buf_len, __u8 *data_buf); |
| static void query_response_error(__u8 opcode, __u8 idn); |
| |
| static void print_power_desc_icc(__u8 *desc_buf, int vccIndex) |
| { |
| int i, offset = 0; |
| struct desc_field_offset *tmp; |
| |
| if (vccIndex < 2 || |
| vccIndex > ARRAY_SIZE(device_power_desc_conf_field_name) - 1) { |
| print_error("Illegal power desc index %d", vccIndex); |
| return; |
| } |
| |
| tmp = &device_power_desc_conf_field_name[vccIndex]; |
| offset = tmp->offset; |
| printf("\nPower Descriptor %s", tmp->name); |
| for (i = offset; i < offset + 32 ; i += 2) { |
| printf("\nLevel %2d value : 0x%x", (i - offset)/2, |
| be16toh((__u16)desc_buf[i])); |
| } |
| printf("\n"); |
| } |
| |
| static void print_vendor_info(__u8 *desc_buf, int len) |
| { |
| int i; |
| |
| if (!desc_buf) |
| return; |
| |
| for (i = 0 ; i < len; i++) { |
| if (!(i%16)) |
| printf("\t\n"); |
| printf("0x%02x ", desc_buf[i]); |
| } |
| printf("\n"); |
| } |
| |
| void print_descriptors(char *desc_str, __u8 *desc_buf, |
| struct desc_field_offset *desc_array, int arr_size) |
| { |
| int i; |
| struct desc_field_offset *tmp; |
| char str_buf[STR_BUF_LEN]; |
| int offset = 0; |
| |
| for (i = 0; offset < arr_size; ++i) { |
| tmp = &desc_array[i]; |
| offset = tmp->offset + tmp->width_in_bytes; |
| |
| if (tmp->width_in_bytes == BYTE) { |
| printf("%s [Byte offset 0x%x]: %s = 0x%x\n", desc_str, |
| tmp->offset, tmp->name, desc_buf[tmp->offset]); |
| } else if (tmp->width_in_bytes == WORD) { |
| printf("%s [Byte offset 0x%x]: %s = 0x%x\n", desc_str, |
| tmp->offset, tmp->name, |
| be16toh(*(__u16 *)&desc_buf[tmp->offset])); |
| } else if (tmp->width_in_bytes == DWORD) { |
| printf("%s [Byte offset 0x%x]: %s = 0x%x\n", desc_str, |
| tmp->offset, tmp->name, |
| be32toh(*(__u32 *)&desc_buf[tmp->offset])); |
| } else if (tmp->width_in_bytes == DDWORD) { |
| printf("%s [Byte offset 0x%x]: %s = 0x%lx\n", |
| desc_str, tmp->offset, tmp->name, |
| be64toh(*(__u64 *)&desc_buf[tmp->offset])); |
| } else if ((tmp->width_in_bytes > DDWORD) && |
| tmp->width_in_bytes < STR_BUF_LEN) { |
| if (!strcmp(tmp->name, "VendorPropInfo")) { |
| printf("%s [Byte offset 0x%x]: %s =\n", |
| desc_str, |
| tmp->offset, |
| tmp->name); |
| print_vendor_info(&desc_buf[tmp->offset], |
| tmp->width_in_bytes); |
| } else { |
| memset(str_buf, 0, STR_BUF_LEN); |
| memcpy(str_buf, &desc_buf[tmp->offset], |
| tmp->width_in_bytes); |
| printf("%s [Byte offset 0x%x]: %s = %s\n", |
| desc_str, |
| tmp->offset, tmp->name, str_buf); |
| } |
| } else { |
| printf("%s [Byte offset 0x%x]: %s Wrong Width = %d", |
| desc_str, tmp->offset, tmp->name, |
| tmp->width_in_bytes); |
| } |
| } |
| } |
| |
| static char *access_type_string(__u8 current_att, __u8 config_type, |
| char *access_string) |
| { |
| enum acc_mode mode; |
| |
| switch (config_type) { |
| case ATTR_TYPE: |
| if (current_att >= QUERY_ATTR_IDN_MAX) |
| return NULL; |
| mode = ufs_attrs[current_att].acc_mode; |
| break; |
| case FLAG_TYPE: |
| if (current_att >= QUERY_FLAG_IDN_MAX) |
| return NULL; |
| mode = ufs_flags[current_att].acc_mode; |
| break; |
| default: |
| return NULL; |
| } |
| |
| if (mode & READ_NRML) |
| strcat(access_string, " | Read"); |
| if (mode & READ_ONLY) |
| strcat(access_string, " | ReadOnly"); |
| if (mode & WRITE_ONLY) |
| strcat(access_string, " | WriteOnly"); |
| if (mode & WRITE_ONCE) |
| strcat(access_string, " | WriteOnce"); |
| if (mode & WRITE_PRSIST) |
| strcat(access_string, " | Persistent"); |
| if (mode & WRITE_VLT) |
| strcat(access_string, " | Volatile"); |
| if (mode & SET_ONLY) |
| strcat(access_string, " | SetOnly"); |
| if (mode & WRITE_PWR) |
| strcat(access_string, " | ResetOnPower"); |
| |
| return access_string; |
| } |
| |
| static void query_response_error(__u8 opcode, __u8 idn) |
| { |
| __u8 query_response_inx = opcode & 0x0F; |
| |
| printf("\n %s, for idn 0x%02x\n", |
| query_err_status[query_response_inx].name, idn); |
| } |
| |
| void desc_help(char *tool_name) |
| { |
| printf("\n Descriptor command usage:\n"); |
| printf("\n\t%s desc [-t] <descriptor idn> [-a|-r|-w] <data> [-p] " |
| "<device_path>Â \n", tool_name); |
| printf("\n\t-t\t description type idn\n" |
| "\t\t Available description types based on UFS ver 3.1 :\n" |
| "\t\t\t0:\tDevice\n" |
| "\t\t\t1:\tConfiguration\n" |
| "\t\t\t2:\tUnit\n" |
| "\t\t\t3:\tRFU\n" |
| "\t\t\t4:\tInterconnect\n" |
| "\t\t\t5:\tString\n" |
| "\t\t\t6:\tRFU\n" |
| "\t\t\t7:\tGeometry\n" |
| "\t\t\t8:\tPower\n" |
| "\t\t\t9:\tDevice Health\n" |
| "\t\t\t10..255: RFU\n"); |
| printf("\n\t-r\t read operation (default) for readable descriptors\n"); |
| printf("\n\t-w\t write operation , for writable descriptors\n"); |
| printf("\t\t Set the input configuration file after -w opt\n"); |
| printf("\t\t for Configuration descriptor\n"); |
| printf("\t\t Set the input string after -w opt\n"); |
| printf("\t\t for String descriptor\n"); |
| printf("\n\t-i\t Set index parameter(default = 0)\n"); |
| printf("\n\t-s\t Set selector parameter(default = 0)\n"); |
| printf("\n\t-p\t path to ufs bsg device\n"); |
| } |
| |
| void attribute_help(char *tool_name) |
| { |
| __u8 current_att = 0; |
| char access_string[100] = {0}; |
| |
| printf("\n Attributes command usage:\n"); |
| printf("\n\t%s attr [-t] <attr_idn> [-a|-r|-w] <data_hex> [-p]" |
| " <device_path>Â \n", tool_name); |
| printf("\n\t-t\t Attributes type idn\n" |
| "\t\t Available attributes and its access based on" |
| " UFS ver 3.1 :\n"); |
| |
| while (current_att < ARRAY_SIZE(ufs_attrs)) { |
| printf("\t\t\t %-3d: %-25s %s\n", |
| current_att, |
| ufs_attrs[current_att].name, |
| access_type_string(current_att, ATTR_TYPE, |
| access_string)); |
| current_att++; |
| memset(access_string, 0, 100); |
| } |
| |
| printf("\n\t-a\tread and print all readable attributes" |
| " for the device\n"); |
| printf("\n\t-r\tread operation (default), for readable attribute(s)\n"); |
| printf("\n\t-w\twrite operation (with hex data)," |
| " for writable attribute\n"); |
| printf("\n\t-i\t Set index parameter(default = 0)\n"); |
| printf("\n\t-s\t Set selector parameter(default = 0)\n"); |
| printf("\n\t-p\tpath to ufs bsg device\n"); |
| printf("\n\tExample - Read bBootLunEn\n" |
| "\t\t%s attr -t 0 -p /dev/ufs-bsg\n", tool_name); |
| } |
| |
| void flag_help(char *tool_name) |
| { |
| __u8 current_flag = 0; |
| char access_string[100] = {0}; |
| |
| printf("\n Flags command usage:\n"); |
| printf("\n\t%s fl [-t] <flag idn> [-a|-r|-o|-e] [-p]" |
| " <device_path>\n", tool_name); |
| printf("\n\t-t\t Flags type idn\n" |
| "\t\t Available flags and its access, based on UFS ver 3.1 :\n"); |
| |
| while (current_flag < QUERY_FLAG_IDN_MAX) { |
| printf("\t\t\t %-3d: %-25s %s\n", current_flag, |
| ufs_flags[current_flag].name, |
| access_type_string(current_flag, FLAG_TYPE, |
| access_string)); |
| current_flag++; |
| memset(access_string, 0, 100); |
| } |
| printf("\n\t-a\t read and print all readable flags for the device\n"); |
| printf("\n\t-r\t read operation (default), for readable flag(s)\n"); |
| printf("\n\t-e\t set flag operation\n"); |
| printf("\n\t-c\t clear/reset flag operation\n"); |
| printf("\n\t-o\t toggle flag operation\n"); |
| printf("\n\t-i\t Set index parameter(default = 0)\n"); |
| printf("\n\t-s\t Set selector parameter(default = 0)\n"); |
| printf("\n\t-p\t path to ufs bsg device\n"); |
| printf("\n\tExample - Read the bkops operation flag\n" |
| "\t\t%s fl -t 4 -p /dev/ufs-bsg\n", tool_name); |
| } |
| |
| int do_device_desc(int fd, __u8 *desc_buff) |
| { |
| struct ufs_bsg_request bsg_req = {0}; |
| struct ufs_bsg_reply bsg_rsp = {0}; |
| __u8 data_buf[QUERY_DESC_DEVICE_MAX_SIZE] = {0}; |
| int rc = 0; |
| |
| rc = do_read_desc(fd, &bsg_req, &bsg_rsp, |
| QUERY_DESC_IDN_DEVICE, 0, |
| QUERY_DESC_DEVICE_MAX_SIZE, data_buf); |
| if (rc) { |
| print_error("Could not read device descriptor , error %d", rc); |
| goto out; |
| } |
| if (!desc_buff) |
| print_descriptors("Device Descriptor", data_buf, |
| device_desc_field_name, data_buf[0]); |
| else |
| memcpy(desc_buff, data_buf, data_buf[0]); |
| |
| out: |
| return rc; |
| } |
| |
| static int do_unit_desc(int fd, __u8 lun) |
| { |
| struct ufs_bsg_request bsg_req = {0}; |
| struct ufs_bsg_reply bsg_rsp = {0}; |
| __u8 data_buf[QUERY_DESC_UNIT_MAX_SIZE] = {0}; |
| int ret = 0; |
| |
| ret = do_read_desc(fd, &bsg_req, &bsg_rsp, QUERY_DESC_IDN_UNIT, lun, |
| QUERY_DESC_UNIT_MAX_SIZE, data_buf); |
| if (ret) { |
| print_error("Could not read unit descriptor error", ret); |
| goto out; |
| } |
| |
| if (lun == 0xc4) |
| print_descriptors("RPMB LUN Descriptor", data_buf, |
| device_unit_rpmb_desc_field_name, data_buf[0]); |
| else |
| print_descriptors("LUN Descriptor", data_buf, |
| device_unit_desc_field_name, data_buf[0]); |
| |
| |
| out: |
| return ret; |
| } |
| |
| static int do_interconnect_desc(int fd) |
| { |
| struct ufs_bsg_request bsg_req = {0}; |
| struct ufs_bsg_reply bsg_rsp = {0}; |
| __u8 data_buf[QUERY_DESC_INTERCONNECT_MAX_SIZE] = {0}; |
| int ret = 0; |
| |
| ret = do_read_desc(fd, &bsg_req, &bsg_rsp, QUERY_DESC_IDN_INTERCONNECT, |
| 0, QUERY_DESC_INTERCONNECT_MAX_SIZE, data_buf); |
| if (ret) { |
| print_error("Could not read interconnect descriptor error %d", |
| ret); |
| goto out; |
| } |
| |
| print_descriptors("Interconnect Descriptor", data_buf, |
| device_interconnect_desc_conf_field_name, data_buf[0]); |
| |
| out: |
| return ret; |
| } |
| |
| static int do_geo_desc(int fd) |
| { |
| struct ufs_bsg_request bsg_req = {0}; |
| struct ufs_bsg_reply bsg_rsp = {0}; |
| __u8 data_buf[QUERY_DESC_GEOMETRY_MAX_SIZE] = {0}; |
| int ret = 0; |
| |
| ret = do_read_desc(fd, &bsg_req, &bsg_rsp, QUERY_DESC_IDN_GEOMETRY, 0, |
| QUERY_DESC_GEOMETRY_MAX_SIZE, data_buf); |
| if (ret) { |
| print_error("Could not read geometry descriptor , error %d", |
| ret); |
| goto out; |
| } |
| |
| print_descriptors("Geometry Descriptor", data_buf, |
| device_geo_desc_conf_field_name, data_buf[0]); |
| |
| out: |
| return ret; |
| } |
| |
| static int do_power_desc(int fd) |
| { |
| struct ufs_bsg_request bsg_req = {0}; |
| struct ufs_bsg_reply bsg_rsp = {0}; |
| __u8 data_buf[QUERY_DESC_POWER_MAX_SIZE] = {0}; |
| int ret = 0; |
| |
| ret = do_read_desc(fd, &bsg_req, &bsg_rsp, |
| QUERY_DESC_IDN_POWER, 0, QUERY_DESC_POWER_MAX_SIZE, |
| data_buf); |
| if (ret) { |
| print_error("Could not read power descriptor , error %d", ret); |
| goto out; |
| } |
| |
| printf("Power Descriptor[Byte offset 0x%x]: %s = 0x%x\n", |
| device_power_desc_conf_field_name[0].offset, |
| device_power_desc_conf_field_name[0].name, data_buf[0]); |
| |
| printf("Power Descriptor[Byte offset 0x%x]: %s = 0x%x\n", |
| device_power_desc_conf_field_name[1].offset, |
| device_power_desc_conf_field_name[1].name, data_buf[1]); |
| |
| print_power_desc_icc(data_buf, 2); |
| print_power_desc_icc(data_buf, 3); |
| print_power_desc_icc(data_buf, 4); |
| |
| out: |
| return ret; |
| } |
| |
| static int do_health_desc(int fd) |
| { |
| struct ufs_bsg_request bsg_req = {0}; |
| struct ufs_bsg_reply bsg_rsp = {0}; |
| __u8 data_buf[QUERY_DESC_HEALTH_MAX_SIZE] = {0}; |
| int ret = 0; |
| |
| ret = do_read_desc(fd, &bsg_req, &bsg_rsp, QUERY_DESC_IDN_HEALTH, 0, |
| QUERY_DESC_HEALTH_MAX_SIZE, data_buf); |
| if (ret) { |
| print_error("Could not read device health descriptor error %d", |
| ret); |
| goto out; |
| } |
| |
| print_descriptors("Device Health Descriptor:", data_buf, |
| device_health_desc_conf_field_name, data_buf[0]); |
| |
| out: |
| return ret; |
| } |
| |
| static void create_str_desc_data(__u8 *dest_buf, const char *str, __u8 len) |
| { |
| int j = 3; |
| int i; |
| |
| dest_buf[0] = len * 2 + 2; |
| dest_buf[1] = QUERY_DESC_IDN_STRING; |
| for (i = 0; i < len ; i++) { |
| dest_buf[j] = *(str++); |
| j = j + 2; |
| } |
| } |
| |
| |
| |
| |
| |
| static int do_string_desc(int fd, char *str_data, __u8 idn, __u8 opr, |
| __u8 index) |
| { |
| int rc = 0; |
| __u8 data_buf[QUERY_DESC_STRING_MAX_SIZE] = {0}; |
| struct ufs_bsg_request bsg_req = {0}; |
| struct ufs_bsg_reply bsg_rsp = {0}; |
| int len, i; |
| |
| if (opr == WRITE) { |
| len = strlen(str_data); |
| create_str_desc_data(data_buf, str_data, len); |
| rc = do_write_desc(fd, &bsg_req, &bsg_rsp, |
| QUERY_DESC_IDN_STRING, index, |
| len * 2 + 2, data_buf); |
| if (rc == OK) |
| printf("\nString Descriptor was written\n"); |
| } else { |
| rc = do_read_desc(fd, &bsg_req, &bsg_rsp, QUERY_DESC_IDN_STRING, |
| index, QUERY_DESC_STRING_MAX_SIZE, data_buf); |
| if (!rc) { |
| printf("\nString Desc(Row data):\n"); |
| for (i = 0; i < bsg_rsp.reply_payload_rcv_len; i++) |
| printf("0x%02x ", data_buf[i]); |
| printf("\n"); |
| } |
| } |
| return rc; |
| } |
| |
| static int do_conf_desc(int fd, __u8 opt, __u8 index, char *data_file) |
| { |
| int rc = OK; |
| int file_size; |
| struct ufs_bsg_request bsg_req = {0}; |
| struct ufs_bsg_reply bsg_rsp = {0}; |
| __u8 conf_desc_buf[QUERY_DESC_CONFIGURAION_MAX_SIZE] = {0}; |
| int offset, i; |
| int data_fd = INVALID; |
| char *filename_header = "config_desc_data_ind_%d"; |
| char output_file[30] = {0}; |
| |
| if (opt == WRITE) { |
| data_fd = open(data_file, O_RDONLY); |
| if (data_fd < 0) { |
| perror("can't open input file"); |
| return ERROR; |
| } |
| |
| file_size = lseek(data_fd, 0, SEEK_END); |
| if (file_size <= 0) { |
| print_error("Wrong config file"); |
| rc = ERROR; |
| goto out; |
| } |
| lseek(data_fd, 0, SEEK_SET); |
| |
| rc = read(data_fd, conf_desc_buf, file_size); |
| if (rc <= 0) { |
| print_error("Cannot config file"); |
| rc = ERROR; |
| goto out; |
| } |
| |
| rc = do_write_desc(fd, &bsg_req, &bsg_rsp, |
| QUERY_DESC_IDN_CONFIGURAION, index, |
| file_size, |
| conf_desc_buf); |
| if (!rc) |
| printf("Config Descriptor was written to device\n"); |
| } else { |
| __u8 head_off = CONFIG_HEADER_OFFSET; |
| __u8 lun_off = CONFIG_LUN_OFFSET; |
| |
| rc = do_read_desc(fd, &bsg_req, &bsg_rsp, |
| QUERY_DESC_IDN_CONFIGURAION, |
| index, QUERY_DESC_CONFIGURAION_MAX_SIZE, |
| conf_desc_buf); |
| if (rc) { |
| print_error("Coudn't read config descriptor error %d", |
| rc); |
| |
| goto out; |
| } |
| |
| if (conf_desc_buf[0] == QUERY_DESC_CONFIGURAION_MAX_SIZE_3_0) { |
| head_off = CONFIG_HEADER_OFFSET_3_0; |
| lun_off = CONFIG_LUN_OFFSET_3_0; |
| } |
| |
| print_descriptors("Config Device Descriptor:", |
| conf_desc_buf, |
| device_config_desc_field_name, |
| head_off); |
| |
| offset = head_off; |
| for (i = 0 ; i < 8; i++) { |
| printf("Config %d Unit Descriptor:\n", i); |
| print_descriptors("Config Descriptor:", |
| conf_desc_buf + offset, |
| device_config_unit_desc_field_name, |
| lun_off); |
| offset = offset + lun_off; |
| } |
| sprintf(output_file, filename_header, index); |
| data_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, |
| S_IRUSR | S_IWUSR); |
| if (data_fd < 0) { |
| perror("can't open output file"); |
| return ERROR; |
| } |
| |
| rc = write(data_fd, conf_desc_buf, conf_desc_buf[0]); |
| if (rc <= 0) { |
| print_error("Could not write config data into %s file", |
| output_file); |
| rc = ERROR; |
| goto out; |
| } |
| |
| printf("Config Descriptor was written into %s file\n", |
| output_file); |
| } |
| out: |
| if (data_fd != INVALID) |
| close(data_fd); |
| return rc; |
| } |
| |
| int do_desc(struct tool_options *opt) |
| { |
| int fd; |
| int rc = OK; |
| int oflag = O_RDWR; |
| |
| if (opt->opr == READ_ALL || opt->opr == READ) |
| oflag = O_RDONLY; |
| |
| fd = open(opt->path, oflag); |
| if (fd < 0) { |
| print_error("open"); |
| return ERROR; |
| } |
| |
| if (opt->opr == READ_ALL) { |
| if (do_device_desc(fd, NULL) || do_unit_desc(fd, 0) || |
| do_interconnect_desc(fd) || do_geo_desc(fd) || |
| do_power_desc(fd) || |
| do_health_desc(fd) || |
| do_conf_desc(fd, READ, 0, NULL)) |
| rc = ERROR; |
| goto out; |
| } |
| |
| switch (opt->idn) { |
| case QUERY_DESC_IDN_DEVICE: |
| rc = do_device_desc(fd, NULL); |
| break; |
| case QUERY_DESC_IDN_CONFIGURAION: |
| if (opt->opr == READ) |
| rc = do_conf_desc(fd, opt->opr, opt->index, NULL); |
| else |
| rc = do_conf_desc(fd, opt->opr, opt->index, |
| (char *)opt->data); |
| break; |
| case QUERY_DESC_IDN_UNIT: |
| rc = do_unit_desc(fd, opt->index); |
| break; |
| case QUERY_DESC_IDN_GEOMETRY: |
| rc = do_geo_desc(fd); |
| break; |
| case QUERY_DESC_IDN_POWER: |
| rc = do_power_desc(fd); |
| break; |
| case QUERY_DESC_IDN_STRING: |
| rc = do_string_desc(fd, (char *)opt->data, opt->idn, opt->opr, |
| opt->index); |
| break; |
| case QUERY_DESC_IDN_HEALTH: |
| rc = do_health_desc(fd); |
| break; |
| case QUERY_DESC_IDN_INTERCONNECT: |
| rc = do_interconnect_desc(fd); |
| break; |
| default: |
| print_error("Unsupported Descriptor type %d", opt->idn); |
| rc = -EINVAL; |
| break; |
| } |
| |
| out: |
| close(fd); |
| return rc; |
| } |
| |
| void print_attribute(struct attr_fields *attr, __u8 *attr_buffer) |
| { |
| if (attr->width_in_bytes == BYTE) |
| printf("%-26s := 0x%02x\n", attr->name, attr_buffer[0]); |
| else if (attr->width_in_bytes == WORD) |
| printf("%-26s := 0x%04x\n", attr->name, *(__u16 *)attr_buffer); |
| else if (attr->width_in_bytes == DWORD) |
| printf("%-26s := 0x%08x\n", attr->name, |
| be32toh(*(__u32 *)attr_buffer)); |
| } |
| |
| int do_query_rq(int fd, struct ufs_bsg_request *bsg_req, |
| struct ufs_bsg_reply *bsg_rsp, __u8 query_req_func, |
| __u8 opcode, __u8 idn, __u8 index, __u8 sel, |
| __u16 req_buf_len, __u16 res_buf_len, __u8 *data_buf) |
| { |
| int rc = OK; |
| __u8 res_code; |
| __u16 len = res_buf_len; |
| |
| if (req_buf_len > 0) |
| len = req_buf_len; |
| |
| prepare_upiu(bsg_req, query_req_func, len, opcode, idn, |
| index, sel); |
| |
| rc = send_bsg_scsi_trs(fd, bsg_req, bsg_rsp, req_buf_len, res_buf_len, |
| data_buf); |
| |
| if (rc) { |
| print_error("%s: query failed, status %d idn: %d, i: %d, s: %d", |
| __func__, rc, idn, index, sel); |
| rc = ERROR; |
| goto out; |
| } |
| |
| res_code = (be32toh(bsg_rsp->upiu_rsp.header.dword_1) >> 8) & 0xff; |
| if (res_code) { |
| query_response_error(res_code, idn); |
| rc = ERROR; |
| } |
| out: |
| return rc; |
| } |
| |
| static int do_write_desc(int fd, struct ufs_bsg_request *bsg_req, |
| struct ufs_bsg_reply *bsg_rsp, __u8 idn, __u8 index, |
| __u16 desc_buf_len, __u8 *data_buf) |
| { |
| return do_query_rq(fd, bsg_req, bsg_rsp, |
| UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, |
| UPIU_QUERY_OPCODE_WRITE_DESC, idn, index, |
| 0, desc_buf_len, 0, data_buf); |
| } |
| |
| static int check_read_desc_size(__u8 idn, __u8 *data_buf) |
| { |
| bool unoff = false; |
| int rc = OK; |
| |
| switch (idn) { |
| case QUERY_DESC_IDN_DEVICE: |
| if ((data_buf[0] != QUERY_DESC_DEVICE_MAX_SIZE) && |
| (data_buf[0] != QUERY_DESC_DEVICE_MAX_SIZE_3_0)) |
| unoff = true; |
| break; |
| case QUERY_DESC_IDN_CONFIGURAION: |
| if ((data_buf[0] != QUERY_DESC_CONFIGURAION_MAX_SIZE) && |
| (data_buf[0] != QUERY_DESC_CONFIGURAION_MAX_SIZE_3_0)) |
| unoff = true; |
| break; |
| case QUERY_DESC_IDN_UNIT: |
| if ((data_buf[0] != QUERY_DESC_UNIT_MAX_SIZE) && |
| (data_buf[0] != QUERY_DESC_UNIT_MAX_SIZE_3_0)) |
| unoff = true; |
| break; |
| case QUERY_DESC_IDN_INTERCONNECT: |
| if (data_buf[0] != QUERY_DESC_INTERCONNECT_MAX_SIZE) |
| unoff = true; |
| break; |
| case QUERY_DESC_IDN_GEOMETRY: |
| if ((data_buf[0] != QUERY_DESC_GEOMETRY_MAX_SIZE) && |
| (data_buf[0] != QUERY_DESC_GEOMETRY_MAX_SIZE_3_0)) |
| unoff = true; |
| break; |
| case QUERY_DESC_IDN_POWER: |
| if (data_buf[0] != QUERY_DESC_POWER_MAX_SIZE) |
| unoff = true; |
| break; |
| case QUERY_DESC_IDN_HEALTH: |
| if ((data_buf[0] != QUERY_DESC_HEALTH_MAX_SIZE) && |
| (data_buf[0] != QUERY_DESC_HEALTH_MAX_SIZE_2_1)) |
| unoff = true; |
| break; |
| } |
| |
| if (unoff) { |
| int file_status; |
| |
| rc = ERROR; |
| print_error("Unofficial %s desc size, len = 0x%x", |
| (char *)desc_text[idn], data_buf[0]); |
| file_status = write_file("unofficial.dat", data_buf, |
| data_buf[0]); |
| if (!file_status) |
| printf("\nunofficial.dat raw data file was created\n"); |
| } |
| |
| return rc; |
| } |
| |
| int do_read_desc(int fd, struct ufs_bsg_request *bsg_req, |
| struct ufs_bsg_reply *bsg_rsp, __u8 idn, __u8 index, |
| __u16 desc_buf_len, __u8 *data_buf) |
| { |
| int rc; |
| |
| rc = do_query_rq(fd, bsg_req, bsg_rsp, |
| UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, |
| UPIU_QUERY_OPCODE_READ_DESC, idn, index, 0, |
| 0, desc_buf_len, data_buf); |
| if (!rc) |
| rc = check_read_desc_size(idn, data_buf); |
| |
| return rc; |
| } |
| |
| int do_attributes(struct tool_options *opt) |
| { |
| int fd; |
| int rc = OK; |
| struct attr_fields *tmp = NULL; |
| int oflag = O_RDWR; |
| __u8 att_idn; |
| __u32 attr_value; |
| struct ufs_bsg_request bsg_req = {0}; |
| struct ufs_bsg_reply bsg_rsp = {0}; |
| |
| if (opt->opr == READ_ALL || opt->opr == READ) |
| oflag = O_RDONLY; |
| |
| fd = open(opt->path, oflag); |
| if (fd < 0) { |
| print_error("open"); |
| return ERROR; |
| } |
| tmp = &ufs_attrs[opt->idn]; |
| |
| if (opt->opr == READ_ALL) { |
| att_idn = QUERY_ATTR_IDN_BOOT_LU_EN; |
| |
| while (att_idn < ARRAY_SIZE(ufs_attrs)) { |
| tmp = &ufs_attrs[att_idn]; |
| if (tmp->acc_type == ACC_INVALID || |
| tmp->acc_mode == WRITE_ONLY) { |
| att_idn++; |
| continue; |
| } |
| |
| rc = do_query_rq(fd, &bsg_req, &bsg_rsp, |
| UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, |
| UPIU_QUERY_OPCODE_READ_ATTR, att_idn, |
| opt->index, opt->selector, 0, 0, 0); |
| if (rc == OK) { |
| attr_value = be32toh(bsg_rsp.upiu_rsp.qr.value); |
| print_attribute(tmp, (__u8 *)&attr_value); |
| } |
| |
| memset(&bsg_rsp, 0, BSG_REPLY_SZ); |
| att_idn++; |
| } |
| } else if (opt->opr == WRITE) { |
| if (tmp->acc_type == ACC_INVALID || |
| tmp->acc_mode == READ_ONLY) { |
| print_error("%s Attribute is not writable", tmp->name); |
| rc = ERROR; |
| goto out; |
| } |
| |
| attr_value = *(__u32 *)opt->data; |
| switch (tmp->width_in_bytes) { |
| case BYTE: |
| if (attr_value > 0xFF) { |
| print_error("Wrong write data for %s attr\n", |
| tmp->name); |
| rc = ERROR; |
| goto out; |
| } |
| break; |
| case WORD: |
| if (attr_value > 0xFFFF) { |
| print_error("Wrong write data for %s attr\n", |
| tmp->name); |
| rc = ERROR; |
| goto out; |
| } |
| break; |
| case DWORD: |
| /* avoid -switch warning - no need to check value */ |
| break; |
| default: |
| print_error("Unsupported width %d", |
| tmp->width_in_bytes); |
| rc = ERROR; |
| goto out; |
| } |
| |
| bsg_req.upiu_req.qr.value = htobe32(attr_value); |
| rc = do_query_rq(fd, &bsg_req, &bsg_rsp, |
| UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, |
| UPIU_QUERY_OPCODE_WRITE_ATTR, opt->idn, |
| opt->index, opt->selector, 0, 0, 0); |
| } else if (opt->opr == READ) { |
| if (tmp->acc_type == ACC_INVALID || |
| tmp->acc_mode == WRITE_ONLY) { |
| print_error("%s attribute is not readable", tmp->name); |
| rc = ERROR; |
| goto out; |
| } |
| |
| rc = do_query_rq(fd, &bsg_req, &bsg_rsp, |
| UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, |
| UPIU_QUERY_OPCODE_READ_ATTR, opt->idn, |
| opt->index, opt->selector, 0, 0, 0); |
| if (rc == OK) { |
| attr_value = be32toh(bsg_rsp.upiu_rsp.qr.value); |
| print_attribute(tmp, (__u8 *)&attr_value); |
| } |
| } |
| out: |
| close(fd); |
| return rc; |
| } |
| |
| int do_flags(struct tool_options *opt) |
| { |
| int fd; |
| int rc = OK; |
| __u8 opcode, flag_idn; |
| struct flag_fields *tmp; |
| struct ufs_bsg_request bsg_req = {0}; |
| struct ufs_bsg_reply bsg_rsp = {0}; |
| int oflag = O_RDWR; |
| |
| if (opt->opr == READ_ALL || opt->opr == READ) |
| oflag = O_RDONLY; |
| |
| fd = open(opt->path, oflag); |
| if (fd < 0) { |
| print_error("open"); |
| return ERROR; |
| } |
| |
| tmp = &ufs_flags[opt->idn]; |
| |
| switch (opt->opr) { |
| case READ_ALL: |
| flag_idn = QUERY_FLAG_IDN_FDEVICEINIT; |
| printf("UFS Device Flags:\n"); |
| while (flag_idn < QUERY_FLAG_IDN_MAX) { |
| tmp = &ufs_flags[flag_idn]; |
| if (tmp->acc_type == ACC_INVALID || |
| tmp->acc_type == UWRT) { |
| flag_idn++; |
| continue; |
| } |
| |
| rc = do_query_rq(fd, &bsg_req, &bsg_rsp, |
| UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, |
| UPIU_QUERY_OPCODE_READ_FLAG, flag_idn, |
| opt->index, opt->selector, 0, 0, 0); |
| if (rc == OK) { |
| printf("%-26s := 0x%01x\n", tmp->name, |
| be32toh(bsg_rsp.upiu_rsp.qr.value) & |
| 0xff); |
| } else { |
| /* on failuire make note and keep going */ |
| print_error("Read for flag %s failed", |
| tmp->name); |
| } |
| |
| memset(&bsg_rsp, 0, BSG_REPLY_SZ); |
| flag_idn++; |
| } |
| break; |
| case CLEAR_FLAG: |
| case TOGGLE_FLAG: |
| case SET_FLAG: |
| if (tmp->acc_type == ACC_INVALID || |
| tmp->acc_mode == READ_ONLY) { |
| print_error("%s flag is not writable", tmp->name); |
| rc = ERROR; |
| } else if ((tmp->acc_mode & SET_ONLY) && |
| opt->opr != SET_FLAG) { |
| print_error("Only set operation supported for %s flag", |
| tmp->name); |
| rc = ERROR; |
| } else { |
| if (opt->opr == CLEAR_FLAG) |
| opcode = UPIU_QUERY_OPCODE_CLEAR_FLAG; |
| else if (opt->opr == SET_FLAG) |
| opcode = UPIU_QUERY_OPCODE_SET_FLAG; |
| else if (opt->opr == TOGGLE_FLAG) |
| opcode = UPIU_QUERY_OPCODE_TOGGLE_FLAG; |
| rc = do_query_rq(fd, &bsg_req, &bsg_rsp, |
| UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, |
| opcode, opt->idn, opt->index, |
| opt->selector, 0, 0, 0); |
| if (rc) |
| print_error("The operation for flag %s failed", |
| tmp->name); |
| } |
| break; |
| case READ:/*Read operation */ |
| if (tmp->acc_type == ACC_INVALID || tmp->acc_type == UWRT) { |
| print_error("%s flag is not readable", tmp->name); |
| rc = ERROR; |
| } else { |
| rc = do_query_rq(fd, &bsg_req, &bsg_rsp, |
| UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, |
| UPIU_QUERY_OPCODE_READ_FLAG, opt->idn, |
| opt->index, opt->selector, 0, 0, 0); |
| if (rc == OK) |
| printf("%-26s := 0x%01x\n", tmp->name, |
| be32toh(bsg_rsp.upiu_rsp.qr.value) & |
| 0xff); |
| else |
| print_error("Read for flag %s failed", tmp->name); |
| } |
| break; |
| default: |
| print_error("Unsupported operation for %s flag", tmp->name); |
| rc = ERROR; |
| break; |
| } |
| |
| close(fd); |
| return rc; |
| } |