| /**************************************************************************** |
| * drivers/wireless/bluetooth/bt_rpmsghci_server.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 <sys/param.h> |
| |
| #include <assert.h> |
| #include <debug.h> |
| #include <stdlib.h> |
| |
| #include <nuttx/kmalloc.h> |
| #include <nuttx/rptun/openamp.h> |
| #include <nuttx/wireless/bluetooth/bt_rpmsghci.h> |
| |
| #include "bt_rpmsghci.h" |
| |
| /**************************************************************************** |
| * Pre-processor definitions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct rpmsghci_server_s |
| { |
| struct rpmsg_endpoint ept; |
| FAR struct bt_driver_s *btdev; |
| FAR const char *name; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static int rpmsghci_open_handler(FAR struct rpmsg_endpoint *ept, |
| FAR void *data, size_t len, |
| uint32_t src, FAR void *priv); |
| static int rpmsghci_close_handler(FAR struct rpmsg_endpoint *ept, |
| FAR void *data, size_t len, |
| uint32_t src, FAR void *priv); |
| static int rpmsghci_send_handler(FAR struct rpmsg_endpoint *ept, |
| FAR void *data, size_t len, |
| uint32_t src, FAR void *priv); |
| static int rpmsghci_ioctl_handler(FAR struct rpmsg_endpoint *ept, |
| FAR void *data, size_t len, |
| uint32_t src, FAR void *priv); |
| static int rpmsghci_default_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_open_handler, /* RPMSGHCI_OPEN */ |
| rpmsghci_close_handler, /* RPMSGHCI_CLOSE */ |
| rpmsghci_send_handler, /* RPMSGHCI_SEND */ |
| rpmsghci_ioctl_handler, /* RPMSGHCI_IOCTL */ |
| rpmsghci_default_handler, /* RPMSGHCI_RECV */ |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: rpmsghci_get_tx_payload_buffer |
| ****************************************************************************/ |
| |
| static FAR void * |
| rpmsghci_get_tx_payload_buffer(FAR struct rpmsghci_server_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_server_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_open_handler |
| * |
| * Description: |
| * Rpmsg-HCI device open 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_open_handler(FAR struct rpmsg_endpoint *ept, |
| FAR void *data, size_t len, |
| uint32_t src, FAR void *priv) |
| { |
| FAR struct rpmsghci_server_s *server = NULL; |
| FAR struct bt_driver_s *btdev = NULL; |
| FAR struct rpmsghci_data_s *msg = data; |
| int ret = OK; |
| |
| DEBUGASSERT(priv); |
| server = priv; |
| DEBUGASSERT(server->btdev); |
| btdev = server->btdev; |
| |
| if (btdev->open) |
| { |
| ret = btdev->open(btdev); |
| } |
| |
| msg->header.result = ret; |
| |
| return rpmsg_send(ept, msg, sizeof(struct rpmsghci_header_s)); |
| } |
| |
| /**************************************************************************** |
| * Name: rpmsghci_close_handler |
| * |
| * Description: |
| * Rpmsg-HCI close 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_close_handler(FAR struct rpmsg_endpoint *ept, |
| FAR void *data, size_t len, |
| uint32_t src, FAR void *priv) |
| { |
| FAR struct rpmsghci_server_s *server = NULL; |
| FAR struct bt_driver_s *btdev = NULL; |
| FAR struct rpmsghci_data_s *msg = data; |
| int ret = OK; |
| |
| DEBUGASSERT(priv); |
| server = priv; |
| DEBUGASSERT(server->btdev); |
| btdev = server->btdev; |
| |
| if (btdev->close) |
| { |
| btdev->close(btdev); |
| } |
| |
| msg->header.result = ret; |
| |
| return rpmsg_send(ept, msg, sizeof(struct rpmsghci_header_s)); |
| } |
| |
| /**************************************************************************** |
| * Name: rpmsghci_send_handler |
| * |
| * Description: |
| * Rpmsg-HCI send 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_send_handler(FAR struct rpmsg_endpoint *ept, |
| FAR void *data, size_t len, |
| uint32_t src, FAR void *priv) |
| { |
| FAR struct rpmsghci_server_s *server = NULL; |
| FAR struct bt_driver_s *btdev = NULL; |
| FAR struct rpmsghci_data_s *msg = data; |
| int ret = OK; |
| |
| DEBUGASSERT(priv); |
| server = priv; |
| DEBUGASSERT(server->btdev); |
| btdev = server->btdev; |
| |
| wlinfo("rpmsghci_send_handler %d\n", msg->type); |
| |
| ret = btdev->send(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_ioctl_handler |
| * |
| * Description: |
| * Rpmsg-HCI ioctl 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_ioctl_handler(FAR struct rpmsg_endpoint *ept, |
| FAR void *data, size_t len, |
| uint32_t src, FAR void *priv) |
| { |
| FAR struct rpmsghci_server_s *server = NULL; |
| FAR struct bt_driver_s *btdev = NULL; |
| FAR struct rpmsghci_ioctl_s *msg = data; |
| int ret = OK; |
| |
| DEBUGASSERT(priv); |
| server = priv; |
| DEBUGASSERT(server->btdev); |
| btdev = server->btdev; |
| |
| ret = btdev->ioctl(btdev, msg->request, |
| msg->arglen > 0 ? (unsigned long)msg->buf : |
| msg->arg); |
| |
| msg->header.result = ret; |
| |
| /* Send the entire frame so as not to lose ioctl returned data */ |
| |
| return rpmsg_send(ept, msg, len); |
| } |
| |
| /**************************************************************************** |
| * Name: rpmsghci_default_handler |
| * |
| * Description: |
| * Default rpmsg-HCI response handler, this function will be called to |
| * process the return message of rpmsghci_recv() |
| * |
| * 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_ns_match |
| * |
| * Description: |
| * Check if we match to the rpmsg name service. |
| * |
| ****************************************************************************/ |
| |
| static bool rpmsghci_ns_match(FAR struct rpmsg_device *rdev, FAR void *priv, |
| FAR const char *name, uint32_t dest) |
| { |
| return !strncmp(name, RPMSGHCI_NAME_PREFIX, RPMSGHCI_NAME_PREFIX_LEN); |
| } |
| |
| /**************************************************************************** |
| * Name: rpmsghci_ept_cb |
| * |
| * Description: |
| * Rpmsg HCI end point callback function, this function will be called |
| * when receive the client 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_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_ns_unbind |
| * |
| * Description: |
| * Unbind from the rpmsg name service. |
| * |
| ****************************************************************************/ |
| |
| static void rpmsghci_ns_unbind(FAR struct rpmsg_endpoint *ept) |
| { |
| rpmsg_destroy_ept(ept); |
| kmm_free(ept); |
| } |
| |
| /**************************************************************************** |
| * Name: rpmsghci_ns_bind |
| * |
| * Description: |
| * Bind to the rpmsg name service. |
| * |
| ****************************************************************************/ |
| |
| static void rpmsghci_ns_bind(FAR struct rpmsg_device *rdev, FAR void *priv, |
| FAR const char *name, uint32_t dest) |
| { |
| FAR struct rpmsghci_server_s *server = priv; |
| |
| server->ept.priv = priv; |
| rpmsg_create_ept(&server->ept, rdev, name, |
| RPMSG_ADDR_ANY, dest, |
| rpmsghci_ept_cb, rpmsghci_ns_unbind); |
| } |
| |
| /**************************************************************************** |
| * Name: rpmsghci_bt_receive |
| * |
| * Description: |
| * Called by the Bluetooth low-level driver when new data is received from |
| * the radio. Transfers data to the RPMSG client. |
| * |
| * This callback should not be called from the interrupt context ! |
| * |
| * Returned Value: |
| * OK on success; A negated errno value is returned on any failure. |
| * |
| ****************************************************************************/ |
| |
| int rpmsghci_bt_receive(FAR struct bt_driver_s *btdev, uint8_t type, |
| FAR void *data, size_t len) |
| { |
| FAR struct rpmsghci_server_s *priv = NULL; |
| FAR struct rpmsghci_data_s *msg = NULL; |
| uint32_t space = 0; |
| |
| wlinfo("rpmsghci_bt_receive %d\n", type); |
| |
| /* Get RPMSG-HCI data */ |
| |
| DEBUGASSERT(btdev != NULL); |
| priv = btdev->priv; |
| |
| 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_RECV, &msg->header, |
| sizeof(struct rpmsghci_data_s) -1 + len); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: rpmsghci_server_init |
| * |
| * Description: |
| * Rpmsg-HCI server initialize function, the server cpu should call |
| * this function. |
| * |
| * Parameters: |
| * name - RPMSG-HCI server name |
| * bt - BT device handler |
| * |
| * Returned Values: |
| * OK on success; A negated errno value is returned on any failure. |
| * |
| ****************************************************************************/ |
| |
| int rpmshci_server_init(FAR const char *name, FAR struct bt_driver_s *btdev) |
| { |
| struct rpmsghci_server_s *priv = NULL; |
| int ret = OK; |
| |
| /* Validate input */ |
| |
| if (!name || !btdev) |
| { |
| return -EINVAL; |
| } |
| |
| /* Create RPMSG-HCI server */ |
| |
| priv = kmm_zalloc(sizeof(struct rpmsghci_server_s)); |
| if (!priv) |
| { |
| return -ENOMEM; |
| } |
| |
| /* Connect BT receive callback and RPMSG as priv */ |
| |
| btdev->receive = rpmsghci_bt_receive; |
| btdev->priv = priv; |
| |
| /* Initialize RPMSG-HCI server data */ |
| |
| priv->name = name; |
| priv->btdev = btdev; |
| |
| ret = rpmsg_register_callback(priv, NULL, NULL, |
| rpmsghci_ns_match, |
| rpmsghci_ns_bind); |
| if (ret < 0) |
| { |
| kmm_free(priv); |
| } |
| |
| return ret; |
| } |