/*
 * 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 "usbfn_io_mgr.h"
#include "usbd_wrapper.h"

#define HDF_LOG_TAG usbfn_io_mgr

static int32_t ReqToIoData(struct UsbFnRequest *req, struct IoData *ioData, uint32_t aio, uint32_t timeout)
{
    if (req == NULL || ioData == NULL) {
        HDF_LOGE("%{public}s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    struct ReqList *reqList = (struct ReqList *)req;
    ioData->aio = aio;
    if (req->type == USB_REQUEST_TYPE_PIPE_WRITE) {
        ioData->read = 0;
    } else if (req->type == USB_REQUEST_TYPE_PIPE_READ) {
        ioData->read = 1;
    }
    ioData->buf = reqList->buf;
    ioData->len = req->length;
    ioData->timeout = timeout;

    return 0;
}

int32_t OpenEp0AndMapAddr(struct UsbFnFuncMgr *funcMgr)
{
    int32_t ret;
    struct UsbFnAdapterOps *fnOps = UsbFnAdapterGetOps();
    funcMgr->fd = fnOps->openPipe(funcMgr->name, 0);
    if (funcMgr->fd <= 0) {
        HDF_LOGE("%{public}s:%{public}d openPipe failed", __func__, __LINE__);
        return HDF_ERR_IO;
    }

    ret = fnOps->queueInit(funcMgr->fd);
    if (ret) {
        HDF_LOGE("%{public}s:%{public}d queueInit failed", __func__, __LINE__);
        return HDF_ERR_IO;
    }
    return 0;
}

static UsbFnRequestType GetReqType(struct UsbHandleMgr *handle, uint8_t pipe)
{
    struct UsbFnPipeInfo info = {0};
    UsbFnRequestType type = USB_REQUEST_TYPE_INVALID;
    if (pipe > 0) {
        int32_t ret = UsbFnIoMgrInterfaceGetPipeInfo(&(handle->intfMgr->interface), pipe - 1, &info);
        if (ret) {
            HDF_LOGE("%{public}s:%{public}d UsbFnMgrInterfaceGetPipeInfo err", __func__, __LINE__);
            type = USB_REQUEST_TYPE_INVALID;
        }
        if (info.dir == USB_PIPE_DIRECTION_IN) {
            type = USB_REQUEST_TYPE_PIPE_WRITE;
        } else if (info.dir == USB_PIPE_DIRECTION_OUT) {
            type = USB_REQUEST_TYPE_PIPE_READ;
        }
    }
    return type;
}

struct UsbFnRequest *UsbFnIoMgrRequestAlloc(struct UsbHandleMgr *handle, uint8_t pipe, uint32_t len)
{
    int32_t ep;
    struct UsbFnInterfaceMgr *intfMgr = handle->intfMgr;
    struct UsbFnFuncMgr *funcMgr = intfMgr->funcMgr;
    struct UsbFnAdapterOps *fnOps = UsbFnAdapterGetOps();
    if (pipe == 0) {
        if (funcMgr->fd <= 0) {
            int32_t ret = OpenEp0AndMapAddr(funcMgr);
            if (ret) {
                return NULL;
            }
        }
        ep = funcMgr->fd;
    } else if (pipe <= MAX_EP) {
        ep = handle->fds[pipe - 1];
    } else {
        return NULL;
    }
    uint8_t *mapAddr = fnOps->mapAddr(ep, len);
    if (mapAddr == NULL || mapAddr == (uint8_t *)-1) {
        HDF_LOGE("%{public}s:%{public}d mapAddr %{public}d to %{public}p failed", __func__, __LINE__, ep, mapAddr);
        return NULL;
    }

    struct ReqList *reqList = UsbFnMemCalloc(sizeof(struct ReqList));
    if (reqList == NULL) {
        HDF_LOGE("%{public}s:%{public}d UsbFnMemCalloc err", __func__, __LINE__);
        return NULL;
    }
    struct UsbFnRequest *req = &reqList->req;

    if (pipe == 0) {
        DListInsertTail(&reqList->entry, &funcMgr->reqEntry);
    } else {
        DListInsertTail(&reqList->entry, &handle->reqEntry);
    }
    reqList->handle = handle;
    reqList->fd = ep;
    reqList->buf = (uintptr_t)mapAddr;
    reqList->pipe = pipe;
    reqList->bufLen = len;
    req->length = len;
    req->obj = handle->intfMgr->interface.object;
    req->buf = mapAddr;
    req->type = GetReqType(handle, pipe);

    return req;
}

int32_t UsbFnIoMgrRequestFree(struct UsbFnRequest *req)
{
    struct GenericMemory mem;
    int32_t ret;
    if (req == NULL) {
        HDF_LOGE("%{public}s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    struct ReqList *reqList = (struct ReqList *)req;
    struct UsbFnAdapterOps *fnOps = UsbFnAdapterGetOps();

    ret = fnOps->unmapAddr(req->buf, reqList->bufLen);
    if (ret) {
        HDF_LOGE("%{public}s:%{public}d ummapAddr failed, ret=%{public}d ", __func__, __LINE__, ret);
        return HDF_ERR_DEVICE_BUSY;
    }
    mem.size = reqList->bufLen;
    mem.buf = (uint64_t)req->buf;
    ret = fnOps->releaseBuf(reqList->fd, &mem);
    if (ret) {
        HDF_LOGE("%{public}s releaseBuf err::%{public}d", __func__, ret);
        return HDF_ERR_INVALID_PARAM;
    }

    DListRemove(&reqList->entry);
    UsbFnMemFree(reqList);
    return 0;
}

int32_t UsbFnIoMgrRequestSubmitAsync(struct UsbFnRequest *req)
{
    int32_t ret;
    struct IoData ioData = {0};
    struct ReqList *reqList = NULL;
    if (req == NULL) {
        HDF_LOGE("%{public}s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    reqList = (struct ReqList *)req;
    if (ReqToIoData(req, &ioData, 1, 0)) {
        return HDF_ERR_IO;
    }

    struct UsbFnAdapterOps *fnOps = UsbFnAdapterGetOps();
    ret = fnOps->pipeIo(reqList->fd, &ioData);
    if (ret != HDF_SUCCESS) {
        HDF_LOGE("%{public}s pipeIo failed fd:%{public}d read:%{public}u", __func__, reqList->fd, ioData.read);
    }

    return ret;
}

int32_t UsbFnIoMgrRequestCancel(struct UsbFnRequest *req)
{
    int32_t ret;
    struct IoData ioData = {0};
    struct ReqList *reqList = NULL;
    if (req == NULL) {
        HDF_LOGE("%{public}s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    reqList = (struct ReqList *)req;
    if (ReqToIoData(req, &ioData, 1, 0)) {
        return HDF_ERR_IO;
    }
    struct UsbFnAdapterOps *fnOps = UsbFnAdapterGetOps();
    ret = fnOps->cancelIo(reqList->fd, &ioData);

    return ret;
}

int32_t UsbFnIoMgrRequestGetStatus(struct UsbFnRequest *req, UsbRequestStatus *status)
{
    struct IoData ioData = {0};
    struct ReqList *reqList;
    if (req == NULL) {
        HDF_LOGE("%{public}s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    reqList = (struct ReqList *)req;
    if (ReqToIoData(req, &ioData, 1, 0)) {
        return HDF_ERR_IO;
    }
    struct UsbFnAdapterOps *fnOps = UsbFnAdapterGetOps();
    *status = -(fnOps->getReqStatus(reqList->fd, &ioData));

    return 0;
}

int32_t UsbFnIoMgrRequestSubmitSync(struct UsbFnRequest *req, uint32_t timeout)
{
    int32_t ret;
    struct IoData ioData = {0};
    struct ReqList *reqList;

    if (req == NULL) {
        HDF_LOGE("%{public}s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    reqList = (struct ReqList *)req;
    if (ReqToIoData(req, &ioData, 0, timeout)) {
        return HDF_ERR_IO;
    }
    struct UsbFnAdapterOps *fnOps = UsbFnAdapterGetOps();
    ret = fnOps->pipeIo(reqList->fd, &ioData);
    if (ret > 0) {
        req->status = USB_REQUEST_COMPLETED;
        req->actual = (uint32_t)ret;
        return 0;
    }

    return ret;
}

static int32_t HandleInit(struct UsbHandleMgr *handle, struct UsbFnInterfaceMgr *interfaceMgr)
{
    int32_t ret;
    uint32_t i, j;
    struct UsbFnAdapterOps *fnOps = UsbFnAdapterGetOps();

    DListHeadInit(&handle->reqEntry);
    handle->numFd = interfaceMgr->interface.info.numPipes;
    for (i = 0; i < handle->numFd; i++) {
        handle->fds[i] = fnOps->openPipe(interfaceMgr->funcMgr->name, interfaceMgr->startEpId + i);
        if (handle->fds[i] <= 0) {
            return HDF_ERR_IO;
        }

        ret = fnOps->queueInit(handle->fds[i]);
        if (ret) {
            HDF_LOGE("%{public}s: queueInit failed ret = %{public}d", __func__, ret);
            return HDF_ERR_IO;
        }

        handle->reqEvent[i] = UsbFnMemCalloc(sizeof(struct UsbFnReqEvent) * MAX_REQUEST);
        if (handle->reqEvent[i] == NULL) {
            HDF_LOGE("%{public}s: UsbFnMemCalloc failed", __func__);
            goto FREE_EVENT;
        }
    }
    handle->intfMgr = interfaceMgr;
    return 0;

FREE_EVENT:
    for (j = 0; j < i; j++) {
        UsbFnMemFree(handle->reqEvent[j]);
    }
    return HDF_ERR_IO;
}

struct UsbHandleMgr *UsbFnIoMgrInterfaceOpen(struct UsbFnInterface *interface)
{
    int32_t ret;
    if (interface == NULL) {
        return NULL;
    }
    struct UsbFnInterfaceMgr *interfaceMgr = (struct UsbFnInterfaceMgr *)interface;
    if (interfaceMgr->isOpen) {
        HDF_LOGE("%{public}s: interface has opened", __func__);
        return NULL;
    }
    struct UsbHandleMgr *handle = UsbFnMemCalloc(sizeof(struct UsbHandleMgr));
    if (handle == NULL) {
        HDF_LOGE("%{public}s: malloc UsbHandleMgr failed", __func__);
        return NULL;
    }

    ret = HandleInit(handle, interfaceMgr);
    if (ret) {
        HDF_LOGE("%{public}s: HandleInit failed", __func__);
        UsbFnMemFree(handle);
        return NULL;
    }

    interfaceMgr->isOpen = true;
    interfaceMgr->handle = handle;
    return handle;
}

int32_t UsbFnIoMgrInterfaceClose(struct UsbHandleMgr *handle)
{
    if (handle == NULL) {
        HDF_LOGE("%{public}s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    struct UsbFnAdapterOps *fnOps = UsbFnAdapterGetOps();
    struct UsbFnInterfaceMgr *interfaceMgr = handle->intfMgr;
    if (interfaceMgr == NULL || interfaceMgr->isOpen == false) {
        HDF_LOGE("%{public}s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    for (uint32_t i = 0; i < handle->numFd; i++) {
        int32_t ret = fnOps->queueDel(handle->fds[i]);
        if (ret) {
            HDF_LOGE("%{public}s:%{public}d queueDel failed, ret=%{public}d ", __func__, __LINE__, ret);
            return HDF_ERR_DEVICE_BUSY;
        }

        ret = fnOps->closePipe(handle->fds[i]);
        if (ret) {
            HDF_LOGE("%{public}s:%{public}d closePipe failed, ret=%{public}d ", __func__, __LINE__, ret);
            return HDF_ERR_DEVICE_BUSY;
        }
        handle->fds[i] = -1;
        UsbFnMemFree(handle->reqEvent[i]);
        handle->reqEvent[i] = NULL;
    }

    UsbFnMemFree(handle);
    handle = NULL;
    interfaceMgr->isOpen = false;
    interfaceMgr->handle = NULL;
    return 0;
}

int32_t UsbFnIoMgrInterfaceGetPipeInfo(struct UsbFnInterface *interface, uint8_t pipeId, struct UsbFnPipeInfo *info)
{
    int32_t ret;
    int32_t fd;
    if (info == NULL || interface == NULL || pipeId >= interface->info.numPipes) {
        HDF_LOGE("%{public}s invalid param", __func__);
        return HDF_ERR_INVALID_PARAM;
    }
    struct UsbFnAdapterOps *fnOps = UsbFnAdapterGetOps();
    struct UsbFnInterfaceMgr *interfaceMgr = (struct UsbFnInterfaceMgr *)interface;
    if (interfaceMgr->isOpen) {
        if (pipeId >= MAX_EP) {
            HDF_LOGE("%{public}s pipeId overflow", __func__);
            return HDF_ERR_INVALID_PARAM;
        }
        fd = interfaceMgr->handle->fds[pipeId];
        ret = fnOps->getPipeInfo(fd, info);
        if (ret) {
            HDF_LOGE("%{public}s: getPipeInfo failed", __func__);
            return HDF_ERR_DEVICE_BUSY;
        }
    } else {
        fd = fnOps->openPipe(interfaceMgr->funcMgr->name, interfaceMgr->startEpId + pipeId);
        if (fd <= 0) {
            HDF_LOGE("%{public}s: openPipe failed", __func__);
            return HDF_ERR_IO;
        }
        ret = fnOps->getPipeInfo(fd, info);
        if (ret) {
            fnOps->closePipe(fd);
            HDF_LOGE("%{public}s: getPipeInfo failed", __func__);
            return HDF_ERR_DEVICE_BUSY;
        }
        fnOps->closePipe(fd);
    }

    info->id = pipeId;
    return ret;
}