1/*
2 * Copyright (c) 2022-2023 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_device_manager.h"
17
18#include <ctype.h>
19#include <dirent.h>
20#include <fcntl.h>
21#include <unistd.h>
22
23#include "ddk_sysfs_device.h"
24#include "hdf_base.h"
25#include "hdf_dlist.h"
26#include "hdf_io_service_if.h"
27#include "hdf_log.h"
28#include "hdf_sbuf.h"
29#include "osal_mem.h"
30#include "osal_mutex.h"
31#include "securec.h"
32#include "usbd_wrapper.h"
33
34#define HDF_LOG_TAG usb_ddk_dev_mgr
35#define USB_GADGET_STATE_PATH "/sys/devices/virtual/"
36struct UsbDdkDeviceInfo {
37    struct OsalMutex deviceMutex;
38    struct DListHead list;
39    struct UsbPnpNotifyMatchInfoTable info;
40};
41
42struct UsbDdkDeviceList {
43    bool isInit;
44    struct OsalMutex listMutex;
45    struct DListHead devList;
46};
47
48#ifdef USB_EVENT_NOTIFY_LINUX_NATIVE_MODE
49static struct UsbDdkDeviceList g_ddkDevList = {.isInit = false};
50#define STATE_STRING_LENGTH 20
51
52char *g_gadgetStatePath = "invalid_path";
53
54static struct UsbDdkDeviceInfo *DdkDevMgrIsDevExists(uint64_t devAddr)
55{
56    OsalMutexLock(&g_ddkDevList.listMutex);
57    if (DListIsEmpty(&g_ddkDevList.devList)) {
58        HDF_LOGI("%{public}s: the devList is empty.", __func__);
59        OsalMutexUnlock(&g_ddkDevList.listMutex);
60        return NULL;
61    }
62
63    struct UsbDdkDeviceInfo *res = NULL;
64    struct UsbDdkDeviceInfo *infoPos = NULL;
65    struct UsbDdkDeviceInfo *infoTemp = NULL;
66    DLIST_FOR_EACH_ENTRY_SAFE(infoPos, infoTemp, &g_ddkDevList.devList, struct UsbDdkDeviceInfo, list) {
67        if (infoPos->info.usbDevAddr == devAddr) {
68            res = infoPos;
69            break;
70        }
71    }
72    OsalMutexUnlock(&g_ddkDevList.listMutex);
73    return res;
74}
75
76static int32_t DdkDevMgrAddDevice(struct UsbDdkDeviceInfo *device)
77{
78    if (device == NULL) {
79        HDF_LOGE("%{public}s: invalid param", __func__);
80        return HDF_ERR_INVALID_PARAM;
81    }
82
83    HDF_LOGI("%{public}s: make device address and whether the device exists", __func__);
84    if (DdkDevMgrIsDevExists(DdkSysfsMakeDevAddr(device->info.busNum, device->info.devNum)) != NULL) {
85        HDF_LOGW("%{public}s: add device repeatedly busNum:%{public}d, devNum:%{public}d", __func__,
86            device->info.busNum, device->info.devNum);
87        return HDF_SUCCESS;
88    }
89
90    OsalMutexLock(&g_ddkDevList.listMutex);
91    DListInsertTail(&device->list, &g_ddkDevList.devList);
92    OsalMutexUnlock(&g_ddkDevList.listMutex);
93    HDF_LOGI("%{public}s: add device successed", __func__);
94    return HDF_SUCCESS;
95}
96
97int32_t DdkDevMgrRemoveDevice(int32_t busNum, int32_t devNum, struct UsbPnpNotifyMatchInfoTable *info)
98{
99    uint64_t devAddr = DdkSysfsMakeDevAddr(busNum, devNum);
100    struct UsbDdkDeviceInfo *dev = DdkDevMgrIsDevExists(devAddr);
101    if (dev == NULL) {
102        HDF_LOGE("%{public}s: no device busNum:%{public}d, devNum:%{public}d", __func__, busNum, devNum);
103        return HDF_DEV_ERR_NO_DEVICE;
104    }
105
106    int32_t ret = memcpy_s(
107        info, sizeof(struct UsbPnpNotifyMatchInfoTable), &dev->info, sizeof(struct UsbPnpNotifyMatchInfoTable));
108    if (ret != EOK) {
109        HDF_LOGE("%{public}s: memcpy_s failed", __func__);
110        return HDF_FAILURE;
111    }
112
113    OsalMutexLock(&g_ddkDevList.listMutex);
114    DListRemove(&dev->list);
115    OsalMemFree(dev);
116    dev = NULL;
117    OsalMutexUnlock(&g_ddkDevList.listMutex);
118    return HDF_SUCCESS;
119}
120
121static int32_t DdkDevMgrInitDevice(struct UsbDdkDeviceInfo *deviceInfo)
122{
123    (void)memset_s(deviceInfo, sizeof(struct UsbDdkDeviceInfo), 0, sizeof(struct UsbDdkDeviceInfo));
124    int32_t ret = OsalMutexInit(&deviceInfo->deviceMutex);
125    if (ret != HDF_SUCCESS) {
126        HDF_LOGE("%{public}s: init mutex failed", __func__);
127        return HDF_FAILURE;
128    }
129    DListHeadInit(&deviceInfo->list);
130
131    return HDF_SUCCESS;
132}
133
134const struct UsbPnpNotifyMatchInfoTable *DdkDevMgrCreateDevice(const char *deviceDir)
135{
136    struct UsbDdkDeviceInfo *device = (struct UsbDdkDeviceInfo *)OsalMemCalloc(sizeof(struct UsbDdkDeviceInfo));
137    if (device == NULL) {
138        HDF_LOGE("%{public}s: init device failed", __func__);
139        return NULL;
140    }
141
142    int32_t status = HDF_SUCCESS;
143    do {
144        // init device
145        status = DdkDevMgrInitDevice(device);
146        if (status != HDF_SUCCESS) {
147            HDF_LOGE("%{public}s: init device failed:%{public}d", __func__, status);
148            break;
149        }
150
151        // get device from sysfs
152        status = DdkSysfsGetDevice(deviceDir, &device->info);
153        if (status != HDF_SUCCESS) {
154            HDF_LOGE("%{public}s: sysfs get device failed:%{public}d", __func__, status);
155            break;
156        }
157
158        // insert device to list
159        status = DdkDevMgrAddDevice(device);
160        if (status != HDF_SUCCESS) {
161            HDF_LOGE("%{public}s: add device failed:%{public}d", __func__, status);
162            break;
163        }
164        return &device->info;
165    } while (0);
166
167    OsalMemFree(device);
168    return status == HDF_SUCCESS ? &device->info : NULL;
169}
170
171static int32_t DdkDevMgrScanSysfs(const char *sysfsDevDir)
172{
173    if (sysfsDevDir == NULL) {
174        HDF_LOGE("%{public}s: invalid param", __func__);
175        return HDF_ERR_INVALID_PARAM;
176    }
177
178    DIR *dir = opendir(sysfsDevDir);
179    if (dir == NULL) {
180        HDF_LOGE("%{public}s: opendir failed sysfsDevDir:%{public}s", __func__, sysfsDevDir);
181        return HDF_ERR_BAD_FD;
182    }
183
184    struct dirent *devHandle;
185    while ((devHandle = readdir(dir))) {
186        // only read dir like 3-1
187        if (devHandle->d_name[0] > '9' || devHandle->d_name[0] < '0' || strchr(devHandle->d_name, ':')) {
188            continue;
189        }
190
191        if (DdkDevMgrCreateDevice(devHandle->d_name) == NULL) {
192            HDF_LOGW("%{public}s: create device failed d_name:%{public}s", __func__, devHandle->d_name);
193        }
194    }
195    closedir(dir);
196    return HDF_SUCCESS;
197}
198
199int32_t DdkDevMgrInit(const char *gadgetStatePath)
200{
201    if (g_ddkDevList.isInit) {
202        return HDF_SUCCESS;
203    }
204
205    if (gadgetStatePath == NULL) {
206        HDF_LOGE("%{public}s: invalid gadgetStatePath", __func__);
207        return HDF_ERR_INVALID_PARAM;
208    }
209
210    g_gadgetStatePath = (char *)gadgetStatePath;
211    int32_t ret = OsalMutexInit(&g_ddkDevList.listMutex);
212    if (ret != HDF_SUCCESS) {
213        HDF_LOGE("%{public}s: init mutex failed", __func__);
214        return HDF_FAILURE;
215    }
216
217    DListHeadInit(&g_ddkDevList.devList);
218    ret = DdkDevMgrScanSysfs(SYSFS_DEVICES_DIR);
219    if (ret != HDF_SUCCESS) {
220        HDF_LOGE("%{public}s: Scan sysfs failed ret=%{public}d", __func__, ret);
221        return HDF_FAILURE;
222    }
223    g_ddkDevList.isInit = true;
224    return HDF_SUCCESS;
225}
226
227int32_t DdkDevMgrForEachDeviceSafe(DdkDevMgrHandleDev handle, void *priv)
228{
229    OsalMutexLock(&g_ddkDevList.listMutex);
230    if (DListIsEmpty(&g_ddkDevList.devList)) {
231        HDF_LOGI("%{public}s:the devList is empty", __func__);
232        OsalMutexUnlock(&g_ddkDevList.listMutex);
233        return HDF_SUCCESS;
234    }
235
236    struct UsbDdkDeviceInfo *pos = NULL;
237    struct UsbDdkDeviceInfo *tmp = NULL;
238    DLIST_FOR_EACH_ENTRY_SAFE(pos, tmp, &g_ddkDevList.devList, struct UsbDdkDeviceInfo, list) {
239        if (handle(&pos->info, priv) != HDF_SUCCESS) {
240            HDF_LOGW("%{public}s: handle failed", __func__);
241        }
242    }
243
244    OsalMutexUnlock(&g_ddkDevList.listMutex);
245    return HDF_SUCCESS;
246}
247
248int32_t DdkDevMgrGetGadgetLinkStatusSafe(DdkDevMgrHandleGadget handle, void *priv)
249{
250    if (priv == NULL || handle == NULL) {
251        HDF_LOGE("%{public}s: invalid param.", __func__);
252        return HDF_ERR_INVALID_OBJECT;
253    }
254
255    char pathBuf[PATH_MAX] = {'\0'};
256    if (realpath(g_gadgetStatePath, pathBuf) == NULL) {
257        HDF_LOGE("%{public}s: path conversion failed", __func__);
258        return HDF_FAILURE;
259    }
260
261    if (strncmp(USB_GADGET_STATE_PATH, pathBuf, strlen(USB_GADGET_STATE_PATH)) != 0) {
262        HDF_LOGE("%{public}s: The file path is incorrect", __func__);
263        return HDF_FAILURE;
264    }
265
266    int32_t fd = open(pathBuf, O_RDONLY | O_CLOEXEC);
267    if (fd == -1) {
268        HDF_LOGE("%{public}s: open %{public}s failed  errno:%{public}d", __func__, g_gadgetStatePath, errno);
269        return HDF_ERR_IO;
270    }
271
272    char buf[STATE_STRING_LENGTH] = {0};
273    ssize_t numRead = read(fd, buf, STATE_STRING_LENGTH);
274    close(fd);
275    if (numRead <= 0) {
276        HDF_LOGE("%{public}s: read state failed errno:%{public}d", __func__, errno);
277        return HDF_ERR_IO;
278    }
279
280    if ((strncmp(buf, "CONNECTED", strlen("CONNECTED")) == 0) ||
281        (strncmp(buf, "CONFIGURED", strlen("CONFIGURED")) == 0)) {
282        // call back
283        if (handle(priv) != HDF_SUCCESS) {
284            HDF_LOGW("%{public}s: handle failed", __func__);
285        }
286    }
287
288    return HDF_SUCCESS;
289}
290bool DdkDevMgrGetGadgetLinkStatus()
291{
292    char pathBuf[PATH_MAX] = {'\0'};
293    if (realpath(g_gadgetStatePath, pathBuf) == NULL) {
294        HDF_LOGE("%{public}s: path conversion failed", __func__);
295        return false;
296    }
297
298    if (strncmp(USB_GADGET_STATE_PATH, pathBuf, strlen(USB_GADGET_STATE_PATH)) != 0) {
299        HDF_LOGE("%{public}s: The file path is incorrect", __func__);
300        return false;
301    }
302
303    int32_t fd = open(pathBuf, O_RDONLY | O_CLOEXEC);
304    if (fd == -1) {
305        HDF_LOGE("%{public}s: open %{public}s failed  errno:%{public}d", __func__, g_gadgetStatePath, errno);
306        return false;
307    }
308
309    char buf[STATE_STRING_LENGTH] = {0};
310    ssize_t numRead = read(fd, buf, STATE_STRING_LENGTH);
311    close(fd);
312    if (numRead <= 0) {
313        HDF_LOGE("%{public}s: read state failed errno:%{public}d", __func__, errno);
314        return false;
315    }
316    HDF_LOGE("%{public}s: read status:%{public}s", __func__, buf);
317    if ((strncmp(buf, "CONNECTED", strlen("CONNECTED")) == 0) ||
318        (strncmp(buf, "CONFIGURED", strlen("CONFIGURED")) == 0)) {
319        return true;
320    }
321    return false;
322}
323#else                                                                           // USB_EVENT_NOTIFY_LINUX_NATIVE_MODE
324struct HdfIoService *g_usbPnpSrv = NULL;
325#define HDF_USB_INFO_MAX_SIZE (127 * sizeof(struct UsbPnpNotifyMatchInfoTable)) // 127  is max deivce num
326int32_t DdkDevMgrInit(const char *gadgetStatePath)
327{
328    (void)gadgetStatePath;
329    g_usbPnpSrv = HdfIoServiceBind(USB_PNP_NOTIFY_SERVICE_NAME);
330    if (g_usbPnpSrv == NULL) {
331        HDF_LOGE("%{public}s: HdfIoServiceBind failed.", __func__);
332        return HDF_ERR_INVALID_OBJECT;
333    }
334    return HDF_SUCCESS;
335}
336
337int32_t DdkDevMgrForEachDeviceSafe(DdkDevMgrHandleDev handle, void *priv)
338{
339    if (g_usbPnpSrv == NULL || handle == NULL) {
340        HDF_LOGE("%{public}s: invalid param.", __func__);
341        return HDF_ERR_INVALID_OBJECT;
342    }
343
344    struct HdfSBuf *reply = HdfSbufObtain(HDF_USB_INFO_MAX_SIZE);
345    if (reply == NULL) {
346        HDF_LOGE("%{public}s: HdfSbufObtain reply failed", __func__);
347        return HDF_DEV_ERR_NO_MEMORY;
348    }
349
350    // request device list from pnp service
351    int32_t ret = g_usbPnpSrv->dispatcher->Dispatch(&g_usbPnpSrv->object, USB_PNP_DRIVER_GETDEVICES, NULL, reply);
352    if (ret != HDF_SUCCESS) {
353        HDF_LOGE("%{public}s:failed to send service call, ret:%{public}d", __func__, ret);
354        HdfSbufRecycle(reply);
355        return ret;
356    }
357
358    // read device list
359    int32_t count = 0;
360    if (!HdfSbufReadInt32(reply, &count)) {
361        HDF_LOGE("%{public}s: failed to read count from reply", __func__);
362        HdfSbufRecycle(reply);
363        return HDF_ERR_INVALID_PARAM;
364    }
365
366    HDF_LOGI("%{public}s: total obj num count:%{public}d ", __func__, count);
367    struct UsbPnpNotifyMatchInfoTable *info = NULL;
368    uint32_t infoSize = 0;
369    for (int32_t i = 0; i < count; ++i) {
370        if (!HdfSbufReadBuffer(reply, (const void **)(&info), &infoSize) || info == NULL) {
371            HDF_LOGE("%{public}s: HdfSbufReadBuffer failed", __func__);
372            HdfSbufRecycle(reply);
373            return HDF_ERR_INVALID_PARAM;
374        }
375        // call back
376        if (handle(info, priv) != HDF_SUCCESS) {
377            HDF_LOGW("%{public}s: handle failed", __func__);
378        }
379    }
380
381    HdfSbufRecycle(reply);
382    return HDF_SUCCESS;
383}
384
385static int32_t DdkDevMgrGetGadgetStatus(int32_t *gadgetStatus)
386{
387    if (g_usbPnpSrv == NULL) {
388        HDF_LOGE("%{public}s: invalid param.", __func__);
389        return HDF_ERR_INVALID_OBJECT;
390    }
391
392    struct HdfSBuf *reply = HdfSbufObtain(HDF_USB_INFO_MAX_SIZE);
393    if (reply == NULL) {
394        HDF_LOGE("%{public}s: HdfSbufObtain reply failed", __func__);
395        return HDF_DEV_ERR_NO_MEMORY;
396    }
397
398    int32_t ret =
399        g_usbPnpSrv->dispatcher->Dispatch(&g_usbPnpSrv->object, USB_PNP_DRIVER_GET_GADGET_LINK_STATUS, NULL, reply);
400    if (ret != HDF_SUCCESS) {
401        HDF_LOGE("%{public}s:failed to send service call, ret:%{public}d", __func__, ret);
402        HdfSbufRecycle(reply);
403        return ret;
404    }
405
406    if (!HdfSbufReadInt32(reply, gadgetStatus)) {
407        HDF_LOGE("%{public}s: failed to read count from reply", __func__);
408        HdfSbufRecycle(reply);
409        return HDF_ERR_INVALID_PARAM;
410    }
411
412    HdfSbufRecycle(reply);
413    return HDF_SUCCESS;
414}
415
416int32_t DdkDevMgrGetGadgetLinkStatusSafe(DdkDevMgrHandleGadget handle, void *priv)
417{
418    if (priv == NULL || handle == NULL) {
419        HDF_LOGE("%{public}s: invalid param.", __func__);
420        return HDF_ERR_INVALID_OBJECT;
421    }
422    int32_t gadgetStatus = 0;
423    if (DdkDevMgrGetGadgetStatus(&gadgetStatus) != HDF_SUCCESS) {
424        HDF_LOGE("%{public}s: DdkDevMgrGetGadgetStatus failed", __func__);
425        return HDF_FAILURE;
426    }
427    // gadget add
428    if (gadgetStatus != 0) {
429        // call back
430        if (handle(priv) != HDF_SUCCESS) {
431            HDF_LOGW("%{public}s: handle failed", __func__);
432        }
433    }
434    return HDF_SUCCESS;
435}
436
437bool DdkDevMgrGetGadgetLinkStatus()
438{
439    int32_t gadgetStatus = 0;
440    if (DdkDevMgrGetGadgetStatus(&gadgetStatus) != HDF_SUCCESS) {
441        HDF_LOGE("%{public}s: DdkDevMgrGetGadgetStatus failed", __func__);
442        return false;
443    }
444    // gadget add
445    if (gadgetStatus != 0) {
446        return gadgetStatus == USB_PNP_DRIVER_GADGET_ADD ? true : false;
447    }
448    return false;
449}
450#endif                                                                          // USB_EVENT_NOTIFY_LINUX_NATIVE_MODE
451