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
35 extern "C" {
36 #endif /* __cplusplus */
37 constexpr size_t MAX_ACTION_LEN = 20;
38 constexpr size_t MAX_DEVPATH_LEN = 250;
39 constexpr size_t MAX_SUBSYSTEM_LEN = 30;
40 constexpr size_t MAX_DEVTYPE_LEN = 30;
41 constexpr size_t MAX_DEVNUM_LEN = 10;
42 constexpr size_t MAX_BUSNUM_LEN = 10;
43 constexpr size_t MAX_TASK_NUM = 100000;
44 #define HDF_LOG_TAG usb_ddk_uevent_queue
45 struct 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 
54 class TaskQueue {
55 public:
56     TaskQueue() = default;
57     void Init(void);
58     void UnInit(void);
59     ~TaskQueue();
60     int32_t AddTask(const DdkUeventTaskInfo &task);
61 
62 private:
63     std::queue<DdkUeventTaskInfo> taskQueue_;
64     std::mutex queueLock_;
65     std::condition_variable conditionVariable_;
66     bool threadRun_ {true};
67 };
68 
DdkUeventCopyTask(DdkUeventTaskInfo & task,const struct DdkUeventInfo * info)69 static 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 
DdkUeventAddDevice(const char * devPath)109 static 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 
DdkUeventRemoveDevice(const char * busNum,const char * devNum)126 static 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 
DdkDispatchUevent(const struct DdkUeventTaskInfo * info)139 static 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 
Init(void)153 void 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 
UnInit(void)177 void 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 
~TaskQueue()188 TaskQueue::~TaskQueue()
189 {
190     UnInit();
191 }
192 
AddTask(const DdkUeventTaskInfo & task)193 int32_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 
206 static TaskQueue g_taskQueue;
207 
DdkUeventStartDispatchThread()208 int32_t DdkUeventStartDispatchThread()
209 {
210     g_taskQueue.Init();
211     return HDF_SUCCESS;
212 }
213 
DdkUeventAddTask(const struct DdkUeventInfo * info)214 int32_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     }
237     return g_taskQueue.AddTask(task);
238 }
239 
240 #ifdef __cplusplus
241 }
242 #endif /* __cplusplus */