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#include "ddk_uevent_handle.h"
16
17#include <linux/netlink.h>
18#include <poll.h>
19#include <pthread.h>
20#include <string.h>
21#include <sys/socket.h>
22#include <sys/types.h>
23#include <unistd.h>
24
25#include "ddk_device_manager.h"
26#include "ddk_pnp_listener_mgr.h"
27#include "ddk_uevent_queue.h"
28#include "hdf_base.h"
29#include "hdf_io_service_if.h"
30#include "hdf_log.h"
31#include "osal_time.h"
32#include "securec.h"
33#include "usbfn_uevent_handle.h"
34#include "usbd_wrapper.h"
35
36#define HDF_LOG_TAG usb_ddk_uevent
37
38#ifdef USB_EVENT_NOTIFY_LINUX_NATIVE_MODE
39#define UEVENT_MSG_LEN          2048
40#define UEVENT_SOCKET_GROUPS    0xffffffff
41#define UEVENT_SOCKET_BUFF_SIZE (64 * 1024)
42#define TIMEVAL_SECOND          0
43#define TIMEVAL_USECOND         (100 * 1000)
44#define UEVENT_POLL_WAIT_TIME   100
45
46static int DdkUeventOpen(int *fd)
47{
48    struct sockaddr_nl addr;
49    if (memset_s(&addr, sizeof(addr), 0, sizeof(addr)) != HDF_SUCCESS) {
50        HDF_LOGE("%{public}s: addr memset_s failed!", __func__);
51        return HDF_FAILURE;
52    }
53    addr.nl_family = AF_NETLINK;
54    addr.nl_pid = (uint32_t)getpid();
55    addr.nl_groups = UEVENT_SOCKET_GROUPS;
56
57    int socketfd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
58    if (socketfd < 0) {
59        HDF_LOGE("%{public}s: socketfd failed! ret = %{public}d", __func__, socketfd);
60        return HDF_FAILURE;
61    }
62
63    int buffSize = UEVENT_SOCKET_BUFF_SIZE;
64    if (setsockopt(socketfd, SOL_SOCKET, SO_RCVBUF, &buffSize, sizeof(buffSize)) != 0) {
65        HDF_LOGE("%{public}s: setsockopt failed!", __func__);
66        close(socketfd);
67        return HDF_FAILURE;
68    }
69
70    const int32_t on = 1; // turn on passcred
71    if (setsockopt(socketfd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) != 0) {
72        HDF_LOGE("setsockopt failed!");
73        close(socketfd);
74        return HDF_FAILURE;
75    }
76
77    if (bind(socketfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
78        HDF_LOGE("%{public}s: bind socketfd failed!", __func__);
79        close(socketfd);
80        return HDF_FAILURE;
81    }
82    *fd = socketfd;
83    return HDF_SUCCESS;
84}
85
86static void DdkHandleUevent(const char msg[], ssize_t rcvLen)
87{
88    (void)rcvLen;
89    struct DdkUeventInfo info = {
90        .action = "",
91        .subSystem = "",
92        .busNum = "",
93        .devNum = "",
94        .devPath = "",
95        .devType = "",
96    };
97
98    const char *msgTmp = msg;
99    while (*msgTmp != '\0') {
100        if (strncmp(msgTmp, "ACTION=", strlen("ACTION=")) == 0) {
101            msgTmp += strlen("ACTION=");
102            info.action = msgTmp;
103        } else if (strncmp(msgTmp, "DEVPATH=", strlen("DEVPATH=")) == 0) {
104            msgTmp += strlen("DEVPATH=");
105            info.devPath = msgTmp;
106        } else if (strncmp(msgTmp, "SUBSYSTEM=", strlen("SUBSYSTEM=")) == 0 &&
107            strlen(info.subSystem) == 0) { // some uevent has more than one SUBSYSTEM property
108            msgTmp += strlen("SUBSYSTEM=");
109            info.subSystem = msgTmp;
110        } else if (strncmp(msgTmp, "DEVTYPE=", strlen("DEVTYPE=")) == 0 &&
111            strlen(info.devType) == 0) { // some uevent has more than one DEVTYPE property
112            msgTmp += strlen("DEVTYPE=");
113            info.devType = msgTmp;
114        } else if (strncmp(msgTmp, "BUSNUM=", strlen("BUSNUM=")) == 0) {
115            msgTmp += strlen("BUSNUM=");
116            info.busNum = msgTmp;
117        } else if (strncmp(msgTmp, "DEVNUM=", strlen("DEVNUM=")) == 0) {
118            msgTmp += strlen("DEVNUM=");
119            info.devNum = msgTmp;
120        }
121        msgTmp += strlen(msgTmp) + 1; // 1 is a skip character '\0'
122    }
123
124    DdkUeventAddTask(&info);
125    return;
126}
127
128static ssize_t DdkReadUeventMsg(int sockFd, char *buffer, size_t length)
129{
130    struct iovec iov;
131    iov.iov_base = buffer;
132    iov.iov_len = length;
133
134    struct sockaddr_nl addr;
135    (void)memset_s(&addr, sizeof(addr), 0, sizeof(addr));
136
137    struct msghdr msghdr = {0};
138    msghdr.msg_name = &addr;
139    msghdr.msg_namelen = sizeof(addr);
140    msghdr.msg_iov = &iov;
141    msghdr.msg_iovlen = 1;
142
143    char credMsg[CMSG_SPACE(sizeof(struct ucred))] = {0};
144    msghdr.msg_control = credMsg;
145    msghdr.msg_controllen = sizeof(credMsg);
146
147    ssize_t len = recvmsg(sockFd, &msghdr, 0);
148    if (len <= 0) {
149        return HDF_FAILURE;
150    }
151
152    struct cmsghdr *hdr = CMSG_FIRSTHDR(&msghdr);
153    if (hdr == NULL || hdr->cmsg_type != SCM_CREDENTIALS) {
154        HDF_LOGE("Unexpected control message, ignored");
155        *buffer = '\0';
156        return HDF_FAILURE;
157    }
158
159    return len;
160}
161
162void *DdkUeventMain(void *param)
163{
164    (void)param;
165    int socketfd = -1;
166    if (DdkUeventOpen(&socketfd) != HDF_SUCCESS) {
167        HDF_LOGE("DdkUeventOpen failed");
168        return NULL;
169    }
170
171    ssize_t rcvLen = 0;
172    char msg[UEVENT_MSG_LEN];
173
174    struct pollfd fd;
175    fd.fd = socketfd;
176    fd.events = POLLIN | POLLERR;
177    fd.revents = 0;
178    do {
179        if (poll(&fd, 1, -1) <= 0) {
180            HDF_LOGE("usb event poll fail %{public}d", errno);
181            OsalMSleep(UEVENT_POLL_WAIT_TIME);
182            continue;
183        }
184
185        if (((uint32_t)fd.revents & POLLIN) == POLLIN) {
186            (void)memset_s(&msg, sizeof(msg), 0, sizeof(msg));
187            rcvLen = DdkReadUeventMsg(socketfd, msg, UEVENT_MSG_LEN);
188            if (rcvLen <= 0) {
189                continue;
190            }
191            DdkHandleUevent(msg, rcvLen);
192            UsbFnHandleUevent(msg, rcvLen);
193        } else if (((uint32_t)fd.revents & POLLERR) == POLLERR) {
194            HDF_LOGE("usb event poll error");
195        }
196    } while (true);
197
198    close(socketfd);
199    return NULL;
200}
201
202int32_t DdkUeventInit(const char *gadgetEventPath)
203{
204    DdkUeventStartDispatchThread();
205    return UsbFnUeventInit(gadgetEventPath);
206}
207#else  // USB_EVENT_NOTIFY_LINUX_NATIVE_MODE
208static int32_t DdkUeventCallBack(void *priv, uint32_t id, struct HdfSBuf *data)
209{
210    if (id == USB_PNP_NOTIFY_REPORT_INTERFACE) {
211        return HDF_SUCCESS;
212    }
213
214    if (data == NULL) {
215        HDF_LOGE("%{public}s: HdfIoServiceBind failed.", __func__);
216        return HDF_ERR_INVALID_PARAM;
217    }
218
219    struct UsbPnpNotifyMatchInfoTable *info = NULL;
220    if (id == USB_PNP_NOTIFY_ADD_DEVICE || id == USB_PNP_NOTIFY_REMOVE_DEVICE) {
221        uint32_t infoSize;
222        bool flag = HdfSbufReadBuffer(data, (const void **)(&info), &infoSize);
223        if (!flag || info == NULL) {
224            HDF_LOGE("%{public}s: HdfSbufReadBuffer failed, flag=%{public}d", __func__, flag);
225            return HDF_ERR_INVALID_PARAM;
226        }
227    }
228
229    HDF_LOGI("%{public}s: cmd is: %{public}u.", __func__, id);
230    DdkListenerMgrNotifyAll(info, id);
231    return HDF_SUCCESS;
232}
233
234int32_t DdkUeventInit(const char *gadgetEventPath)
235{
236    (void)gadgetEventPath;
237    struct HdfIoService *usbPnpSrv = HdfIoServiceBind(USB_PNP_NOTIFY_SERVICE_NAME);
238    if (usbPnpSrv == NULL) {
239        HDF_LOGE("%{public}s: HdfIoServiceBind failed.", __func__);
240        return HDF_ERR_INVALID_OBJECT;
241    }
242
243    static struct HdfDevEventlistener usbPnpListener = {.callBack = DdkUeventCallBack};
244    int32_t ret = HdfDeviceRegisterEventListener(usbPnpSrv, &usbPnpListener);
245    if (ret != HDF_SUCCESS) {
246        HDF_LOGE("%{public}s: HdfDeviceRegisterEventListener failed ret=%{public}d", __func__, ret);
247    }
248    return ret;
249}
250#endif // USB_EVENT_NOTIFY_LINUX_NATIVE_MODE
251