171 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2020 The Android Open Source Project
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| #define LOG_TAG "dChargerDetect"
 | |
| 
 | |
| #include <android-base/file.h>
 | |
| #include <android-base/parseint.h>
 | |
| #include <android-base/strings.h>
 | |
| #include <cutils/klog.h>
 | |
| #include <dirent.h>
 | |
| #include <pixelhealth/ChargerDetect.h>
 | |
| #include <pixelhealth/HealthHelper.h>
 | |
| 
 | |
| #include <string>
 | |
| 
 | |
| constexpr char kPowerSupplySysfsPath[]{"/sys/class/power_supply/"};
 | |
| constexpr char kUsbOnlinePath[]{"/sys/class/power_supply/usb/online"};
 | |
| constexpr char kUsbPowerSupplySysfsPath[]{"/sys/class/power_supply/usb/usb_type"};
 | |
| constexpr char kTcpmPsyFilter[]{"tcpm"};
 | |
| using aidl::android::hardware::health::HealthInfo;
 | |
| using android::BatteryMonitor;
 | |
| 
 | |
| namespace hardware {
 | |
| namespace google {
 | |
| namespace pixel {
 | |
| namespace health {
 | |
| 
 | |
| int ChargerDetect::readFromFile(const std::string& path, std::string* buf) {
 | |
|     if (android::base::ReadFileToString(path.c_str(), buf)) {
 | |
|         *buf = android::base::Trim(*buf);
 | |
|     }
 | |
|     return buf->length();
 | |
| }
 | |
| 
 | |
| int ChargerDetect::getIntField(const std::string& path) {
 | |
|     std::string buf;
 | |
|     int value = 0;
 | |
| 
 | |
|     if (readFromFile(path, &buf) > 0)
 | |
|         android::base::ParseInt(buf, &value);
 | |
| 
 | |
|     return value;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Traverses through /sys/class/power_supply/ to identify TCPM(Type-C/PD) power supply.
 | |
|  * TCPM power supply's name follows the format "tcpm-source-psy-6-0025" with i2c/i3c bus id
 | |
|  * and client id(SID) baked in.
 | |
|  */
 | |
| void ChargerDetect::populateTcpmPsyName(std::string* tcpmPsyName) {
 | |
|     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kPowerSupplySysfsPath), closedir);
 | |
|     if (dir == NULL) {
 | |
|             KLOG_ERROR(LOG_TAG, "Could not open %s\n", kPowerSupplySysfsPath);
 | |
|     } else {
 | |
|         struct dirent* entry;
 | |
| 
 | |
|         while ((entry = readdir(dir.get()))) {
 | |
|             const char* name = entry->d_name;
 | |
| 
 | |
|             KLOG_INFO(LOG_TAG, "Psy name:%s", name);
 | |
|             if (strstr(name, kTcpmPsyFilter)) {
 | |
|                 *tcpmPsyName = name;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * The contents of /sys/class/power_supply/<Power supply name>/usb_type follows the format:
 | |
|  * Unknown [SDP] CDP DCP
 | |
|  * with the current selected value encloses within square braces.
 | |
|  * This functions extracts the current selected value and returns it back to the caller.
 | |
|  */
 | |
| int ChargerDetect::getPsyUsbType(const std::string& path, std::string* type) {
 | |
|     size_t start;
 | |
|     std::string usbType;
 | |
|     int ret;
 | |
| 
 | |
|     ret = readFromFile(path, &usbType);
 | |
|     if (ret <= 0) {
 | |
|         KLOG_ERROR(LOG_TAG, "Error reading %s: %d\n", path.c_str(), ret);
 | |
|         return -EINVAL;
 | |
|     }
 | |
| 
 | |
|     start = usbType.find('[');
 | |
|     if (start == std::string::npos) {
 | |
|         KLOG_ERROR(LOG_TAG, "'[' not found in %s: %s\n", path.c_str(), usbType.c_str());
 | |
|         return -EINVAL;
 | |
|     }
 | |
| 
 | |
|     *type = usbType.substr(start + 1, usbType.find(']') - start - 1);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Reads the usb power_supply's usb_type and the tcpm power_supply's usb_type to infer
 | |
|  * HealthInfo(hardware/interfaces/health/1.0/types.hal) online property.
 | |
|  */
 | |
| void ChargerDetect::onlineUpdate(HealthInfo *health_info) {
 | |
|     std::string tcpmOnlinePath, usbPsyType;
 | |
|     static std::string tcpmPsyName;
 | |
|     int ret;
 | |
| 
 | |
|     health_info->chargerAcOnline = false;
 | |
|     health_info->chargerUsbOnline = false;
 | |
| 
 | |
|     if (tcpmPsyName.empty()) {
 | |
|         populateTcpmPsyName(&tcpmPsyName);
 | |
|         KLOG_INFO(LOG_TAG, "TcpmPsyName:%s\n", tcpmPsyName.c_str());
 | |
|     }
 | |
| 
 | |
|     if (!getIntField(kUsbOnlinePath)) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     ret = getPsyUsbType(kUsbPowerSupplySysfsPath, &usbPsyType);
 | |
|     if (!ret) {
 | |
|         if (usbPsyType == "CDP" || usbPsyType == "DCP") {
 | |
|             health_info->chargerAcOnline = true;
 | |
|             return;
 | |
|         } else if (usbPsyType == "SDP") {
 | |
|             health_info->chargerUsbOnline = true;
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Safe to assume AC charger here if BC1.2 non compliant */
 | |
|     health_info->chargerAcOnline = true;
 | |
| 
 | |
|     if (tcpmPsyName.empty()) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     ret = getPsyUsbType(std::string(kPowerSupplySysfsPath) + tcpmPsyName + "/usb_type",
 | |
|                         &usbPsyType);
 | |
|     if (ret < 0) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     KLOG_INFO(LOG_TAG, "TcpmPsy Usbtype:%s\n", usbPsyType.c_str());
 | |
| 
 | |
|     return;
 | |
| }
 | |
| 
 | |
| void ChargerDetect::onlineUpdate(struct android::BatteryProperties *props) {
 | |
|     HealthInfo health_info = ToHealthInfo(props);
 | |
|     onlineUpdate(&health_info);
 | |
|     // Propagate the changes to props
 | |
|     props->chargerAcOnline = health_info.chargerAcOnline;
 | |
|     props->chargerUsbOnline = health_info.chargerUsbOnline;
 | |
|     // onlineUpdate doesn't change chargerWirelessOnline and other fields.
 | |
| }
 | |
| 
 | |
| }  // namespace health
 | |
| }  // namespace pixel
 | |
| }  // namespace google
 | |
| }  // namespace hardware
 |