/**************************************************************************** * drivers/wireless/bluetooth/bt_rpmsghci.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "bt_rpmsghci.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** * Private Types ****************************************************************************/ struct rpmsghci_s { /* This must be te first thung in the structure so we can simply cast from * struct rpmsghci_s to struct bt_driver_s. */ struct bt_driver_s btdev; struct rpmsg_endpoint ept; FAR const char *cpuname; FAR const char *name; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int rpmsghci_default_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); static int rpmsghci_recv_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv); /**************************************************************************** * Private Data ****************************************************************************/ static const rpmsg_ept_cb g_rpmsghci_handler[] = { rpmsghci_default_handler, /* RPMSGHCI_OPEN */ rpmsghci_default_handler, /* RPMSGHCI_CLOSE */ rpmsghci_default_handler, /* RPMSGHCI_SEND */ rpmsghci_default_handler, /* RPMSGHCI_IOCTL */ rpmsghci_recv_handler, /* RPMSGHCI_RECV */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: rpmsghci_get_tx_payload_buffer ****************************************************************************/ static FAR void * rpmsghci_get_tx_payload_buffer(FAR struct rpmsghci_s *priv, FAR uint32_t *len) { return rpmsg_get_tx_payload_buffer(&priv->ept, len, true); } /**************************************************************************** * Name: rpmsghci_send * * Description: * Send the rpmsg data and wait for ACK. * * Parameters: * priv - rpmsg device handle * command - the command, RPMSGHCI_OPEN, RPMSGHCI_CLOSE, RPMSGHCI_SEND, * RPMSGHCI_IOCTL * msg - the message header * len - length of the payload * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsghci_send(FAR struct rpmsghci_s *priv, uint32_t command, FAR struct rpmsghci_header_s *msg, int len) { struct rpmsghci_cookie_s cookie; int ret = OK; memset(&cookie, 0, sizeof(struct rpmsghci_cookie_s)); nxsem_init(&cookie.sem, 0, 0); msg->command = command; msg->result = -ENXIO; msg->cookie = (uintptr_t)&cookie; ret = rpmsg_send_nocopy(&priv->ept, msg, len); if (ret < 0) { goto errout; } ret = rpmsg_wait(&priv->ept, &cookie.sem); if (ret >= 0) { ret = msg->result; } errout: nxsem_destroy(&cookie.sem); return ret; } /**************************************************************************** * Name: rpmsghci_bt_open * * Description: * Rpmsg-HCI open operation * * Parameters: * btdev - the bt instance * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsghci_bt_open(struct bt_driver_s *btdev) { FAR struct rpmsghci_s *priv = NULL; FAR struct rpmsghci_open_s *msg = NULL; uint32_t space = 0; /* Get RPMSG-HCI data */ DEBUGASSERT(btdev != NULL); priv = (FAR struct rpmsghci_s *)btdev; /* Perform the rpmsg write */ msg = rpmsghci_get_tx_payload_buffer(priv, &space); if (msg == NULL) { return -ENOMEM; } return rpmsghci_send(priv, RPMSGHCI_OPEN, &msg->header, sizeof(struct rpmsghci_open_s)); } /**************************************************************************** * Name: rpmsghci_bt_close * * Description: * Rpmsg-HCI close operation * * Parameters: * btdev - the bt instance * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static void rpmsghci_bt_close(struct bt_driver_s *btdev) { FAR struct rpmsghci_s *priv = NULL; FAR struct rpmsghci_close_s *msg = NULL; uint32_t space = 0; /* Get RPMSG-HCI data */ DEBUGASSERT(btdev != NULL); priv = (FAR struct rpmsghci_s *)btdev; /* Perform the rpmsg write */ msg = rpmsghci_get_tx_payload_buffer(priv, &space); if (msg == NULL) { return; } rpmsghci_send(priv, RPMSGHCI_CLOSE, &msg->header, sizeof(struct rpmsghci_close_s)); } /**************************************************************************** * Name: rpmsghci_bt_send * * Description: * Rpmsg-HCI send operation * * Parameters: * btdev - the bt instance * type - bt command type * data - bt command data * len - bt command data length * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsghci_bt_send(struct bt_driver_s *btdev, uint8_t type, void *data, size_t len) { FAR struct rpmsghci_s *priv = NULL; FAR struct rpmsghci_data_s *msg = NULL; uint32_t space = 0; wlinfo("rpmsghci_bt_send %d\n", type); /* Get RPMSG-HCI data */ DEBUGASSERT(btdev != NULL); priv = (FAR struct rpmsghci_s *)btdev; if (data == NULL) { return -EINVAL; } /* Perform the rpmsg write */ msg = rpmsghci_get_tx_payload_buffer(priv, &space); if (msg == NULL) { return -ENOMEM; } DEBUGASSERT(sizeof(struct rpmsghci_data_s) - 1 + len <= space); memcpy(msg->data, data, len); msg->type = type; return rpmsghci_send(priv, RPMSGHCI_SEND, &msg->header, sizeof(struct rpmsghci_data_s) -1 + len); } /**************************************************************************** * Name: rpmsghci_bt_ioctl * * Description: * Rpmsg-HCI ioctl operation * * Parameters: * btdev - the bt instance * cmd - the ioctl command * arg - the ioctl arguments * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsghci_bt_ioctl(FAR struct bt_driver_s *btdev, int cmd, unsigned long arg) { /* TODO */ return -ENOTTY; } /**************************************************************************** * Name: rpmsghci_default_handler * * Description: * Default rpmsg-HCI response handler, this function will be called to * process the return message of rpmsghci_open(), rpmsghci_close(), * rpmsghci_send(), rpmsghci_ioctl(). * * Parameters: * ept - The rpmsg endpoint * data - The return message * len - The return message length * src - unknow * priv - unknow * * Returned Values: * Always OK * ****************************************************************************/ static int rpmsghci_default_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsghci_header_s *header = data; FAR struct rpmsghci_cookie_s *cookie = NULL; if (header->cookie != 0) { cookie = (FAR struct rpmsghci_cookie_s *)(uintptr_t)header->cookie; return rpmsg_post(ept, &cookie->sem); } return 0; } /**************************************************************************** * Name: rpmsghci_recv_handler * * Description: * Rpmsg-HCI receive handler. * * Parameters: * ept - The rpmsg endpoint * data - The return message * len - The return message length * src - unknow * priv - unknow * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsghci_recv_handler(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsghci_s *client = NULL; FAR struct rpmsghci_data_s *msg = data; int ret = OK; DEBUGASSERT(priv); client = priv; wlinfo("rpmsghci_recv_handler %d\n", msg->type); ret = bt_netdev_receive(&client->btdev, msg->type, msg->data, len - sizeof(struct rpmsghci_data_s) + 1); msg->header.result = ret; return rpmsg_send(ept, msg, sizeof(struct rpmsghci_header_s)); } /**************************************************************************** * Name: rpmsghci_client_ept_cb * * Description: * Rpmsg HCI end point callback function, this function will be called * when receive the remote cpu message. * * Parameters: * ept - The rpmsg-HCI end point * data - The received data * len - The received data length * src - unknow * priv - unknow * * Returned Values: * OK on success; A negated errno value is returned on any failure. * ****************************************************************************/ static int rpmsghci_client_ept_cb(FAR struct rpmsg_endpoint *ept, FAR void *data, size_t len, uint32_t src, FAR void *priv) { FAR struct rpmsghci_header_s *header = data; uint32_t command = header->command; if (command < nitems(g_rpmsghci_handler)) { return g_rpmsghci_handler[command](ept, data, len, src, priv); } return -EINVAL; } /**************************************************************************** * Name: rpmsghci_client_created * * Description: * Rpmsg HCI create function, this function will be called by rptun to * create a rpmsg-hci end point. * * Parameters: * rdev - The rpmsg-hci end point * priv_ - Rpmsg-hci handle * * Returned Values: * None * ****************************************************************************/ static void rpmsghci_client_created(FAR struct rpmsg_device *rdev, FAR void *priv_) { FAR struct rpmsghci_s *priv = priv_; char eptname[RPMSG_NAME_SIZE]; if (strcmp(priv->cpuname, rpmsg_get_cpuname(rdev)) == 0) { snprintf(eptname, RPMSG_NAME_SIZE, "%s%s", RPMSGHCI_NAME_PREFIX, priv->name); priv->ept.priv = priv; rpmsg_create_ept(&priv->ept, rdev, eptname, RPMSG_ADDR_ANY, RPMSG_ADDR_ANY, rpmsghci_client_ept_cb, NULL); } } /**************************************************************************** * Name: rpmsghci_client_destroy * * Description: * Rpmsg HCI destroy function, this function will be called by rptun to * destroy rpmsg-HCI end point. * * Parameters: * rdev - The rpmsg-HCI end point * priv_ - Rpmsg-HCI handle * * Returned Values: * None * ****************************************************************************/ static void rpmsghci_client_destroy(FAR struct rpmsg_device *rdev, FAR void *priv_) { FAR struct rpmsghci_s *priv = priv_; if (!strcmp(priv->cpuname, rpmsg_get_cpuname(rdev))) { rpmsg_destroy_ept(&priv->ept); } } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: rpmsghci_register * * Description: * Rpmsg-HCI client initialize function, the client cpu should call * this function in the board initialize process. * * Parameters: * cpuname - the server cpu name * name - the HCI device you want to access in the remote cpu * * Returned Values: * A non-NULL handle is returned on success; * A NULL is returned on any failure. * ****************************************************************************/ FAR struct bt_driver_s *rpmsghci_register(FAR const char *cpuname, FAR const char *name) { FAR struct rpmsghci_s *priv = NULL; /* Validate input */ if (!cpuname || !name) { return NULL; } /* Create RPMSG-HCI driver */ priv = kmm_zalloc(sizeof(struct rpmsghci_s)); if (!priv) { return NULL; } /* Initialize BT driver */ priv->btdev.open = rpmsghci_bt_open; priv->btdev.close = rpmsghci_bt_close; priv->btdev.send = rpmsghci_bt_send; priv->btdev.ioctl = rpmsghci_bt_ioctl; /* Initialize RPMSG-HCI client data */ priv->cpuname = cpuname; priv->name = name; /* Register the device with the openamp */ rpmsg_register_callback(priv, rpmsghci_client_created, rpmsghci_client_destroy, NULL, NULL); return &priv->btdev; }