1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "storaged" 18 19 #include <stdio.h> 20 #include <string.h> 21 #include <sys/statvfs.h> 22 23 #include <android-base/file.h> 24 #include <android-base/parseint.h> 25 #include <android-base/logging.h> 26 #include <android-base/strings.h> 27 #include <log/log_event_list.h> 28 29 #include "storaged.h" 30 31 using namespace std; 32 using namespace android::base; 33 34 const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/"; 35 const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd"; 36 const char* emmc_info_t::emmc_ver_str[9] = { 37 "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1" 38 }; 39 40 const string ufs_info_t::health_file = "/sys/devices/soc/624000.ufshc/health"; 41 42 static bool FileExists(const std::string& filename) 43 { 44 struct stat buffer; 45 return stat(filename.c_str(), &buffer) == 0; 46 } 47 48 storage_info_t* storage_info_t::get_storage_info() 49 { 50 if (FileExists(emmc_info_t::emmc_sysfs) || 51 FileExists(emmc_info_t::emmc_debugfs)) { 52 return new emmc_info_t; 53 } 54 if (FileExists(ufs_info_t::health_file)) { 55 return new ufs_info_t; 56 } 57 return new storage_info_t; 58 } 59 60 void storage_info_t::refresh() 61 { 62 struct statvfs buf; 63 if (statvfs(userdata_path.c_str(), &buf) != 0) { 64 PLOG_TO(SYSTEM, WARNING) << "Failed to get userdata info"; 65 return; 66 } 67 68 userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10; 69 userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10; 70 } 71 72 void storage_info_t::publish() 73 { 74 android_log_event_list(EVENTLOGTAG_EMMCINFO) 75 << version << eol << lifetime_a << lifetime_b 76 << LOG_ID_EVENTS; 77 } 78 79 void emmc_info_t::report() 80 { 81 if (!report_sysfs() && !report_debugfs()) 82 return; 83 84 publish(); 85 } 86 87 bool emmc_info_t::report_sysfs() 88 { 89 string buffer; 90 uint16_t rev = 0; 91 92 if (!ReadFileToString(emmc_sysfs + "rev", &buffer)) { 93 return false; 94 } 95 96 if (sscanf(buffer.c_str(), "0x%hx", &rev) < 1 || 97 rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) { 98 return false; 99 } 100 101 version = "emmc "; 102 version += emmc_ver_str[rev]; 103 104 if (!ReadFileToString(emmc_sysfs + "pre_eol_info", &buffer)) { 105 return false; 106 } 107 108 if (sscanf(buffer.c_str(), "%hx", &eol) < 1 || eol == 0) { 109 return false; 110 } 111 112 if (!ReadFileToString(emmc_sysfs + "life_time", &buffer)) { 113 return false; 114 } 115 116 if (sscanf(buffer.c_str(), "0x%hx 0x%hx", &lifetime_a, &lifetime_b) < 2 || 117 (lifetime_a == 0 && lifetime_b == 0)) { 118 return false; 119 } 120 121 return true; 122 } 123 124 const size_t EXT_CSD_FILE_MIN_SIZE = 1024; 125 /* 2 characters in string for each byte */ 126 const size_t EXT_CSD_REV_IDX = 192 * 2; 127 const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2; 128 const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2; 129 const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2; 130 131 bool emmc_info_t::report_debugfs() 132 { 133 string buffer; 134 uint16_t rev = 0; 135 136 if (!ReadFileToString(emmc_debugfs, &buffer) || 137 buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) { 138 return false; 139 } 140 141 string str = buffer.substr(EXT_CSD_REV_IDX, 2); 142 if (!ParseUint(str, &rev) || 143 rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) { 144 return false; 145 } 146 147 version = "emmc "; 148 version += emmc_ver_str[rev]; 149 150 str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2); 151 if (!ParseUint(str, &eol)) { 152 return false; 153 } 154 155 str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2); 156 if (!ParseUint(str, &lifetime_a)) { 157 return false; 158 } 159 160 str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2); 161 if (!ParseUint(str, &lifetime_b)) { 162 return false; 163 } 164 165 return true; 166 } 167 168 void ufs_info_t::report() 169 { 170 string buffer; 171 if (!ReadFileToString(health_file, &buffer)) { 172 return; 173 } 174 175 vector<string> lines = Split(buffer, "\n"); 176 if (lines.empty()) { 177 return; 178 } 179 180 char rev[8]; 181 if (sscanf(lines[0].c_str(), "ufs version: 0x%7s\n", rev) < 1) { 182 return; 183 } 184 185 version = "ufs " + string(rev); 186 187 for (size_t i = 1; i < lines.size(); i++) { 188 char token[32]; 189 uint16_t val; 190 int ret; 191 if ((ret = sscanf(lines[i].c_str(), 192 "Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx", 193 token, &val)) < 2) { 194 continue; 195 } 196 197 if (string(token) == "bPreEOLInfo") { 198 eol = val; 199 } else if (string(token) == "bDeviceLifeTimeEstA") { 200 lifetime_a = val; 201 } else if (string(token) == "bDeviceLifeTimeEstB") { 202 lifetime_b = val; 203 } 204 } 205 206 if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) { 207 return; 208 } 209 210 publish(); 211 } 212 213