1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ddk_sysfs_device.h"
17#include <dirent.h>
18#include <fcntl.h>
19#include <inttypes.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <unistd.h>
23
24#include "hdf_log.h"
25#include "securec.h"
26#include "usbd_wrapper.h"
27
28#define SYSFS_PATH_LEN   128
29#define PROPERTY_MAX_LEN 128
30#define HDF_LOG_TAG      usb_ddk_sysfs_dev
31
32static inline int32_t DdkSysfsGetBase(const char *propName)
33{
34    if (strcmp(propName, "idProduct") == 0 || strcmp(propName, "idVendor") == 0) {
35        return 16; // 16 means hexadecimal
36    }
37    return 10; // 10 means decimal
38}
39
40inline uint64_t DdkSysfsMakeDevAddr(uint32_t busNum, uint32_t devNum)
41{
42    return (((uint64_t)busNum << 32) | devNum); // 32 means left shift 32 bit
43}
44
45static int32_t DdkSysfsReadProperty(const char *deviceDir, const char *propName, int64_t *value, uint64_t maxVal)
46{
47    char pathTmp[SYSFS_PATH_LEN] = {0};
48    int32_t num = sprintf_s(pathTmp, SYSFS_PATH_LEN, "%s%s/%s", SYSFS_DEVICES_DIR, deviceDir, propName);
49    if (num <= 0) {
50        HDF_LOGE(
51            "%{public}s: sprintf_s error deviceDir:%{public}s, propName:%{public}s", __func__, deviceDir, propName);
52        return HDF_FAILURE;
53    }
54
55    // read string  from file
56    char path[PATH_MAX] = {'\0'};
57    if (realpath(pathTmp, path) == NULL) {
58        HDF_LOGE("file %{public}s is invalid", pathTmp);
59        return HDF_FAILURE;
60    }
61    int32_t fd = open(path, O_RDONLY | O_CLOEXEC);
62    if (fd == -1) {
63        HDF_LOGE("%{public}s: open file failed path:%{public}s, errno:%{public}d", __func__, path, errno);
64        return HDF_ERR_IO;
65    }
66
67    int32_t ret = HDF_SUCCESS;
68    do {
69        char buf[PROPERTY_MAX_LEN] = {0};
70        ssize_t numRead = read(fd, buf, PROPERTY_MAX_LEN);
71        if (numRead <= 0) {
72            HDF_LOGE("%{public}s: read prop failed path:%{public}s, errno:%{public}d", __func__, path, errno);
73            ret = HDF_ERR_IO;
74            break;
75        }
76
77        // convert string to int64_t
78        if (buf[numRead - 1] != '\n') {
79            HDF_LOGE("%{public}s: prop is not end with newline path:%{public}s", __func__, path);
80            ret = HDF_ERR_INVALID_PARAM;
81            break;
82        }
83
84        buf[numRead - 1] = '\0';
85        int64_t res = strtoll(buf, NULL, DdkSysfsGetBase(propName));
86        if (res == LLONG_MAX || res == LLONG_MIN || res > (int64_t)maxVal) {
87            HDF_LOGE("%{public}s: convert failed path:%{public}s, res:%{public}" PRId64 "", __func__, path, res);
88            ret = HDF_ERR_INVALID_PARAM;
89            break;
90        }
91
92        *value = res;
93    } while (0);
94
95    close(fd);
96    return ret;
97}
98
99static int32_t DdkSysfsGetInterface(
100    const char *deviceDir, const char *intfDir, struct UsbPnpNotifyInterfaceInfo * const intf)
101{
102    char intfPath[SYSFS_PATH_LEN] = {0};
103    int32_t num = sprintf_s(intfPath, SYSFS_PATH_LEN, "%s/%s", deviceDir, intfDir);
104    if (num <= 0) {
105        HDF_LOGE("%{public}s: sprintf_s error intfDir:%{public}s", __func__, intfDir);
106        return HDF_FAILURE;
107    }
108
109    int64_t value = 0;
110    int32_t ret = DdkSysfsReadProperty(intfPath, "bInterfaceClass", &value, UINT8_MAX);
111    intf->interfaceClass = (uint8_t)value;
112    ret += DdkSysfsReadProperty(intfPath, "bInterfaceSubClass", &value, UINT8_MAX);
113    intf->interfaceSubClass = (uint8_t)value;
114    ret += DdkSysfsReadProperty(intfPath, "bInterfaceProtocol", &value, UINT8_MAX);
115    intf->interfaceProtocol = (uint8_t)value;
116    ret += DdkSysfsReadProperty(intfPath, "bInterfaceNumber", &value, UINT8_MAX);
117    intf->interfaceNumber = (uint8_t)value;
118    if (ret != HDF_SUCCESS) {
119        HDF_LOGE("%{public}s: get intterface property failed", __func__);
120        return HDF_FAILURE;
121    }
122    return HDF_SUCCESS;
123}
124
125static int32_t DdkSysfsGetActiveInterfaces(
126    const char *deviceDir, uint8_t intfNum, struct UsbPnpNotifyInterfaceInfo intfs[])
127{
128    if (intfNum == 0) {
129        HDF_LOGW("%{public}s: infNum is zero", __func__);
130        return HDF_SUCCESS;
131    }
132
133    int64_t configValue = 0;
134    int32_t ret = DdkSysfsReadProperty(deviceDir, "bConfigurationValue", &configValue, INT8_MAX);
135    if (ret != HDF_SUCCESS) {
136        HDF_LOGE("%{public}s: get bConfigurationValue failed:%{public}d", __func__, ret);
137        return ret;
138    }
139
140    if (configValue == -1) { // unconfigure the device
141        HDF_LOGE("%{public}s: unconfigure the device", __func__);
142        return HDF_FAILURE;
143    }
144
145    char devPath[SYSFS_PATH_LEN] = {0};
146    int32_t num = sprintf_s(devPath, SYSFS_PATH_LEN, "%s%s/", SYSFS_DEVICES_DIR, deviceDir);
147    if (num <= 0) {
148        HDF_LOGE("%{public}s: sprintf_s error deviceDir:%{public}s", __func__, deviceDir);
149        return HDF_FAILURE;
150    }
151
152    DIR *dir = opendir(devPath);
153    if (dir == NULL) {
154        HDF_LOGE("%{public}s: opendir failed sysfsDevDir:%{public}s", __func__, devPath);
155        return HDF_ERR_BAD_FD;
156    }
157
158    struct dirent *devHandle;
159    uint16_t intfIndex = 0;
160    while ((devHandle = readdir(dir)) && (intfIndex < intfNum)) {
161        // only read dir like 3-1:1.1
162        if (strncmp(devHandle->d_name, deviceDir, strlen(deviceDir)) != 0) {
163            continue;
164        }
165
166        ret = DdkSysfsGetInterface(deviceDir, devHandle->d_name, &intfs[intfIndex]);
167        if (ret != HDF_SUCCESS) {
168            HDF_LOGW("%{public}s: create device failed d_name:%{public}s", __func__, devHandle->d_name);
169            continue;
170        }
171
172        ++intfIndex;
173    }
174    closedir(dir);
175
176    if (intfIndex != intfNum) {
177        HDF_LOGE("%{public}s num error intfIndex:%{public}u, intfNum:%{public}u", __func__, intfIndex, intfNum);
178        return HDF_FAILURE;
179    }
180    return HDF_SUCCESS;
181}
182
183static int32_t DdkSysfsStandardizeDevice(struct UsbPnpNotifyMatchInfoTable * const device)
184{
185    device->usbDevAddr = DdkSysfsMakeDevAddr(device->busNum, device->devNum);
186    return HDF_SUCCESS;
187}
188
189int32_t DdkSysfsGetDevice(const char *deviceDir, struct UsbPnpNotifyMatchInfoTable *device)
190{
191    int64_t value = 0;
192    int32_t ret = DdkSysfsReadProperty(deviceDir, "devnum", &value, INT32_MAX);
193    device->devNum = (int32_t)value;
194    ret += DdkSysfsReadProperty(deviceDir, "busnum", &value, INT32_MAX);
195    device->busNum = (int32_t)value;
196    ret += DdkSysfsReadProperty(deviceDir, "bNumInterfaces", &value, UINT8_MAX);
197    device->numInfos = (uint8_t)value;
198
199    struct UsbPnpNotifyDeviceInfo *devInfo = &device->deviceInfo;
200    ret += DdkSysfsReadProperty(deviceDir, "idVendor", &value, UINT16_MAX);
201    devInfo->vendorId = (uint16_t)value;
202    ret += DdkSysfsReadProperty(deviceDir, "idProduct", &value, UINT16_MAX);
203    devInfo->productId = (uint16_t)value;
204    ret += DdkSysfsReadProperty(deviceDir, "bcdDevice", &value, UINT16_MAX);
205    devInfo->bcdDeviceLow = (uint16_t)value;
206    devInfo->bcdDeviceHigh = devInfo->bcdDeviceLow;
207    ret += DdkSysfsReadProperty(deviceDir, "bDeviceClass", &value, UINT8_MAX);
208    devInfo->deviceClass = (uint8_t)value;
209    ret += DdkSysfsReadProperty(deviceDir, "bDeviceSubClass", &value, UINT8_MAX);
210    devInfo->deviceSubClass = (uint8_t)value;
211    ret += DdkSysfsReadProperty(deviceDir, "bDeviceProtocol", &value, UINT8_MAX);
212    devInfo->deviceProtocol = (uint8_t)value;
213    if (ret != HDF_SUCCESS) {
214        HDF_LOGE("%{public}s: get property failed:%{public}d", __func__, ret);
215        return ret;
216    }
217
218    ret = DdkSysfsGetActiveInterfaces(deviceDir, device->numInfos, device->interfaceInfo);
219    if (ret != HDF_SUCCESS) {
220        HDF_LOGE("%{public}s: get active interfaces failed:%{public}d", __func__, ret);
221        return ret;
222    }
223
224    ret = DdkSysfsStandardizeDevice(device);
225    if (ret != HDF_SUCCESS) {
226        HDF_LOGE("%{public}s: standardize failed:%{public}d", __func__, ret);
227        return ret;
228    }
229
230    return HDF_SUCCESS;
231}