1/*
2 * Copyright (c) 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#include "ddk_uevent_queue.h"
16
17#include <condition_variable>
18#include <mutex>
19#include <queue>
20#include <thread>
21
22#include <cstring>
23#include <sys/types.h>
24#include <unistd.h>
25
26#include "ddk_device_manager.h"
27#include "ddk_pnp_listener_mgr.h"
28#include "hdf_base.h"
29#include "hdf_io_service_if.h"
30#include "hdf_log.h"
31#include "securec.h"
32#include "usbd_wrapper.h"
33
34#ifdef __cplusplus
35extern "C" {
36#endif /* __cplusplus */
37constexpr size_t MAX_ACTION_LEN = 20;
38constexpr size_t MAX_DEVPATH_LEN = 250;
39constexpr size_t MAX_SUBSYSTEM_LEN = 30;
40constexpr size_t MAX_DEVTYPE_LEN = 30;
41constexpr size_t MAX_DEVNUM_LEN = 10;
42constexpr size_t MAX_BUSNUM_LEN = 10;
43constexpr size_t MAX_TASK_NUM = 100000;
44#define HDF_LOG_TAG usb_ddk_uevent_queue
45struct DdkUeventTaskInfo {
46    char action[MAX_ACTION_LEN];
47    char devPath[MAX_DEVPATH_LEN];
48    char subSystem[MAX_SUBSYSTEM_LEN];
49    char devType[MAX_DEVTYPE_LEN];
50    char devNum[MAX_DEVNUM_LEN];
51    char busNum[MAX_BUSNUM_LEN];
52};
53
54class TaskQueue {
55public:
56    TaskQueue() = default;
57    void Init(void);
58    void UnInit(void);
59    ~TaskQueue();
60    int32_t AddTask(const DdkUeventTaskInfo &task);
61
62private:
63    std::queue<DdkUeventTaskInfo> taskQueue_;
64    std::mutex queueLock_;
65    std::condition_variable conditionVariable_;
66    bool threadRun_ {true};
67};
68
69static bool DdkUeventCopyTask(DdkUeventTaskInfo &task, const struct DdkUeventInfo *info)
70{
71    int32_t ret = memcpy_s(task.action, MAX_ACTION_LEN, info->action, strlen(info->action));
72    if (ret != EOK) {
73        HDF_LOGE("%{public}s: copy action failed:%{public}s", __func__, info->action);
74        return false;
75    }
76
77    ret = memcpy_s(task.devPath, MAX_DEVPATH_LEN, info->devPath, strlen(info->devPath));
78    if (ret != EOK) {
79        HDF_LOGE("%{public}s: copy devPath failed:%{public}s", __func__, info->devPath);
80        return false;
81    }
82
83    ret = memcpy_s(task.subSystem, MAX_SUBSYSTEM_LEN, info->subSystem, strlen(info->subSystem));
84    if (ret != EOK) {
85        HDF_LOGE("%{public}s: copy subSystem failed:%{public}s", __func__, info->subSystem);
86        return false;
87    }
88
89    ret = memcpy_s(task.devType, MAX_DEVTYPE_LEN, info->devType, strlen(info->devType));
90    if (ret != EOK) {
91        HDF_LOGE("%{public}s: copy devType failed:%{public}s", __func__, info->devType);
92        return false;
93    }
94
95    ret = memcpy_s(task.devNum, MAX_DEVNUM_LEN, info->devNum, strlen(info->devNum));
96    if (ret != EOK) {
97        HDF_LOGE("%{public}s: copy devNum failed:%{public}s", __func__, info->devNum);
98        return false;
99    }
100
101    ret = memcpy_s(task.busNum, MAX_BUSNUM_LEN, info->busNum, strlen(info->busNum));
102    if (ret != EOK) {
103        HDF_LOGE("%{public}s: copy busNum failed:%{public}s", __func__, info->busNum);
104        return false;
105    }
106    return true;
107}
108
109static int32_t DdkUeventAddDevice(const char *devPath)
110{
111    const char *pos = strrchr(devPath, '/');
112    if (pos == nullptr) {
113        HDF_LOGE("%{public}s: no / in devpath:%{public}s", __func__, devPath);
114        return HDF_ERR_INVALID_PARAM;
115    }
116
117    const struct UsbPnpNotifyMatchInfoTable *device = DdkDevMgrCreateDevice(pos + 1); // 1 skip '/'
118    if (device == nullptr) {
119        HDF_LOGE("%{public}s: create device failed:%{public}s", __func__, devPath);
120        return HDF_FAILURE;
121    }
122    DdkListenerMgrNotifyAll(device, USB_PNP_NOTIFY_ADD_DEVICE);
123    return HDF_SUCCESS;
124}
125
126static int32_t DdkUeventRemoveDevice(const char *busNum, const char *devNum)
127{
128    struct UsbPnpNotifyMatchInfoTable dev;
129    int32_t ret =
130        DdkDevMgrRemoveDevice(strtol(busNum, nullptr, 10), strtol(devNum, nullptr, 10), &dev); // 10 means decimal
131    if (ret != HDF_SUCCESS) {
132        HDF_LOGE("%{public}s: remove device failed, busNum:%{public}s, devNum:%{public}s", __func__, busNum, devNum);
133        return HDF_FAILURE;
134    }
135    DdkListenerMgrNotifyAll(&dev, USB_PNP_NOTIFY_REMOVE_DEVICE);
136    return HDF_SUCCESS;
137}
138
139static void DdkDispatchUevent(const struct DdkUeventTaskInfo *info)
140{
141    int32_t ret = HDF_SUCCESS;
142    if (strcmp(info->action, "bind") == 0 && strcmp(info->devType, "usb_device") == 0) {
143        ret = DdkUeventAddDevice(info->devPath);
144    } else if (strcmp(info->action, "remove") == 0 && strcmp(info->devType, "usb_device") == 0) {
145        ret = DdkUeventRemoveDevice(info->busNum, info->devNum);
146    }
147
148    if (ret != HDF_SUCCESS) {
149        HDF_LOGE("%{public}s: action:%{public}s, ret:%{public}d", __func__, info->action, ret);
150    }
151}
152
153void TaskQueue::Init(void)
154{
155    pthread_setname_np(pthread_self(), "ueventTaskQueue");
156    auto taskWork = [this]() -> void {
157        while (threadRun_) {
158            std::unique_lock<std::mutex> uniqueLock(queueLock_);
159            conditionVariable_.wait(uniqueLock, [this] {
160                return (taskQueue_.size() > 0 || !threadRun_);
161            });
162            if (taskQueue_.size() > 0) {
163                DdkUeventTaskInfo task = taskQueue_.front();
164                taskQueue_.pop();
165                uniqueLock.unlock();
166                DdkDispatchUevent(&task);
167            }
168        }
169    };
170    std::thread thd(taskWork);
171
172    thd.detach();
173}
174
175
176
177void TaskQueue::UnInit(void)
178{
179    threadRun_ = false;
180    conditionVariable_.notify_one();
181
182    std::lock_guard<std::mutex> lock(queueLock_);
183    while (!taskQueue_.empty()) {
184        taskQueue_.pop();
185    }
186}
187
188TaskQueue::~TaskQueue()
189{
190    UnInit();
191}
192
193int32_t TaskQueue::AddTask(const DdkUeventTaskInfo &task)
194{
195    std::lock_guard<std::mutex> lock(queueLock_);
196    if (taskQueue_.size() > MAX_TASK_NUM) {
197        HDF_LOGE("%{public}s: task queue is full", __func__);
198        conditionVariable_.notify_one();
199        return HDF_FAILURE;
200    }
201    taskQueue_.emplace(task);
202    conditionVariable_.notify_one();
203    return HDF_SUCCESS;
204}
205
206static TaskQueue g_taskQueue;
207
208int32_t DdkUeventStartDispatchThread()
209{
210    g_taskQueue.Init();
211    return HDF_SUCCESS;
212}
213
214int32_t DdkUeventAddTask(const struct DdkUeventInfo *info)
215{
216    if (strcmp(info->subSystem, "usb") != 0) {
217        return HDF_SUCCESS;
218    }
219    bool isAddDevice = strcmp(info->action, "bind") == 0 && strcmp(info->devType, "usb_device") == 0;
220    bool isRemoveDevice = strcmp(info->action, "remove") == 0 && strcmp(info->devType, "usb_device") == 0;
221    if (!(isAddDevice || isRemoveDevice)) {
222        return HDF_SUCCESS;
223    }
224    HDF_LOGI("%{public}s: bind=%{public}s, subsystem=%{public}s, devType=%{public}s, devPath=%{public}s",
225        __func__, info->action, info->subSystem, info->devType, info->devPath);
226    DdkUeventTaskInfo task {
227        {0x00},
228        {0x00},
229        {0x00},
230        {0x00},
231        {0x00},
232        {0x00},
233    };
234    if (!DdkUeventCopyTask(task, info)) {
235        HDF_LOGW("%{public}s: copy task failed", __func__);
236        return HDF_FAILURE;
237    }
238    return g_taskQueue.AddTask(task);
239}
240
241#ifdef __cplusplus
242}
243#endif /* __cplusplus */