* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "osal_sysevent.h"
#include "hdf_io_service_if.h"
#include "hdf_log.h"
#include "osal_mem.h"
#include "osal_mutex.h"
#define HDF_LOG_TAG usysevent
#define KEVENT_IOSERVICE_NAME "hdf_kevent"
#define KEVENT_COMPLETE_EVENT 1
struct HdfSysEventNotifier {
struct OsalMutex mutex;
struct DListHead notifyNodeList;
struct DListHead waitList;
struct HdfDevEventlistener ioServiceListener;
struct HdfIoService *keventIoService;
};
struct HdfSysEventNotifier *g_hdfSysEventNotifier;
static struct HdfSysEventNotifier *HdfSysEventNotifierGetInstance(void)
{
if (g_hdfSysEventNotifier != NULL) {
return g_hdfSysEventNotifier;
}
struct HdfSysEventNotifier *notifier = OsalMemCalloc(sizeof(struct HdfSysEventNotifier));
if (notifier == NULL) {
return NULL;
}
int ret = OsalMutexInit(¬ifier->mutex);
if (ret != HDF_SUCCESS) {
OsalMemFree(notifier);
return NULL;
}
DListHeadInit(¬ifier->notifyNodeList);
g_hdfSysEventNotifier = notifier;
return notifier;
}
static int FinishEvent(struct HdfIoService *service, const struct HdfSysEvent *event)
{
struct HdfSBuf *sbuf = HdfSbufObtain(sizeof(uint64_t));
if (sbuf == NULL) {
return HDF_ERR_MALLOC_FAIL;
}
if (!HdfSbufWriteUint64(sbuf, event->syncToken)) {
HdfSbufRecycle(sbuf);
return HDF_FAILURE;
}
int ret = service->dispatcher->Dispatch(&service->object, KEVENT_COMPLETE_EVENT, sbuf, NULL);
if (ret != HDF_SUCCESS) {
HDF_LOGE("failed to finish sysevent, %{public}d", ret);
}
HdfSbufRecycle(sbuf);
return ret;
}
static int OnKEventReceived(
struct HdfDevEventlistener *listener, struct HdfIoService *service, uint32_t id, struct HdfSBuf *data)
{
struct HdfSysEventNotifier *notifier = (struct HdfSysEventNotifier *)listener->priv;
if (notifier == NULL) {
return HDF_ERR_INVALID_PARAM;
}
if (id != HDF_SYSEVENT) {
return HDF_ERR_INVALID_OBJECT;
}
struct HdfSysEvent *receivedEvent = NULL;
uint32_t receivedEventLen = 0;
if (!HdfSbufReadBuffer(data, (const void **)&receivedEvent, &receivedEventLen) ||
receivedEventLen != sizeof(struct HdfSysEvent)) {
HDF_LOGE("failed to read kevent object");
return HDF_FAILURE;
}
const char *eventContent = HdfSbufReadString(data);
eventContent = eventContent == NULL ? "" : eventContent;
OsalMutexLock(¬ifier->mutex);
struct HdfSysEventNotifyNode *notifyNode = NULL;
DLIST_FOR_EACH_ENTRY(notifyNode, ¬ifier->notifyNodeList, struct HdfSysEventNotifyNode, listNode) {
if (receivedEvent->eventClass & notifyNode->classFilter) {
(void)notifyNode->callback(
notifyNode, receivedEvent->eventClass, receivedEvent->eventid, eventContent);
}
}
if (receivedEvent->syncToken != 0) {
(void)FinishEvent(service, receivedEvent);
}
OsalMutexUnlock(¬ifier->mutex);
return HDF_SUCCESS;
}
static int InitKeventIoServiceListenerLocked(struct HdfSysEventNotifier *notifier)
{
if (notifier->keventIoService == NULL) {
notifier->keventIoService = HdfIoServiceBind(KEVENT_IOSERVICE_NAME);
}
if (notifier->keventIoService == NULL) {
HDF_LOGE(" ioservice %{public}s is invalid", KEVENT_IOSERVICE_NAME);
return HDF_DEV_ERR_NO_DEVICE;
}
notifier->ioServiceListener.onReceive = OnKEventReceived;
notifier->ioServiceListener.priv = notifier;
int ret = HdfDeviceRegisterEventListener(notifier->keventIoService, ¬ifier->ioServiceListener);
if (ret != HDF_SUCCESS) {
HDF_LOGE(" ioservice %{public}s is invalid", KEVENT_IOSERVICE_NAME);
HdfIoServiceRecycle(notifier->keventIoService);
notifier->keventIoService = NULL;
}
return ret;
}
static void DeInitKeventIoServiceListenerLocked(struct HdfSysEventNotifier *notifier)
{
if (notifier->keventIoService == NULL) {
return;
}
(void)HdfDeviceUnregisterEventListener(notifier->keventIoService, ¬ifier->ioServiceListener);
HdfIoServiceRecycle(notifier->keventIoService);
notifier->keventIoService = NULL;
}
int32_t HdfSysEventNotifyRegister(struct HdfSysEventNotifyNode *notifierNode, uint64_t classSet)
{
if (notifierNode == NULL) {
return HDF_ERR_INVALID_PARAM;
}
struct HdfSysEventNotifier *notifier = HdfSysEventNotifierGetInstance();
if (notifier == NULL) {
return HDF_DEV_ERR_NO_MEMORY;
}
OsalMutexLock(¬ifier->mutex);
DListInsertTail(¬ifierNode->listNode, ¬ifier->notifyNodeList);
notifierNode->classFilter = classSet;
int32_t ret = InitKeventIoServiceListenerLocked(notifier);
if (ret != HDF_SUCCESS) {
DListRemove(¬ifierNode->listNode);
}
OsalMutexUnlock(¬ifier->mutex);
return ret;
}
void HdfSysEventNotifyUnregister(struct HdfSysEventNotifyNode *notifierNode)
{
if (notifierNode == NULL) {
return;
}
struct HdfSysEventNotifier *notifier = HdfSysEventNotifierGetInstance();
if (notifier == NULL) {
return;
}
OsalMutexLock(¬ifier->mutex);
DListRemove(¬ifierNode->listNode);
if (DListIsEmpty(¬ifier->notifyNodeList)) {
DeInitKeventIoServiceListenerLocked(notifier);
}
OsalMutexUnlock(¬ifier->mutex);
}