nuttx/net/bluetooth/bluetooth_sendmsg.c
zhanghongyu c481374fb8 devif_poll: only call the corresponding xxx_poll when there is data to be sent
reduce the execution consumption of irrelevant code

testing the TX rates of TCP and UDP based on the Infineon board can
increase them by 13%.

Signed-off-by: zhanghongyu <zhanghongyu@xiaomi.com>
2026-01-02 00:04:16 +08:00

569 lines
16 KiB
C

/****************************************************************************
* net/bluetooth/bluetooth_sendmsg.c
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <nuttx/config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <netpacket/bluetooth.h>
#include <arch/irq.h>
#include <nuttx/semaphore.h>
#include <nuttx/mm/iob.h>
#include <nuttx/net/radiodev.h>
#include <nuttx/net/bluetooth.h>
#include <nuttx/net/net.h>
#include <nuttx/net/ip.h>
#include <nuttx/wireless/bluetooth/bt_hci.h>
#include "utils/utils.h"
#include "netdev/netdev.h"
#include "devif/devif.h"
#include "socket/socket.h"
#include "bluetooth/bluetooth.h"
#ifdef CONFIG_NET_BLUETOOTH
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure holds the state of the send operation until it can be
* operated upon from the event handler.
*/
struct bluetooth_sendto_s
{
FAR struct socket *is_sock; /* Points to the parent socket structure */
FAR struct devif_callback_s *is_cb; /* Reference to callback instance */
bt_addr_t is_destaddr; /* Frame destination address */
uint16_t is_channel; /* Frame destination channel */
sem_t is_sem; /* Used to wake up the waiting thread */
FAR const uint8_t *is_buffer; /* User buffer of data to send */
size_t is_buflen; /* Number of bytes in the is_buffer */
ssize_t is_sent; /* The number of bytes sent (or error) */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: bluetooth_sendto_eventhandler
****************************************************************************/
static uint32_t bluetooth_sendto_eventhandler(FAR struct net_driver_s *dev,
FAR void *pvpriv,
uint32_t flags)
{
FAR struct radio_driver_s *radio;
FAR struct bluetooth_sendto_s *pstate;
struct bluetooth_frame_meta_s meta;
FAR struct iob_s *iob;
int hdrlen;
int ret;
DEBUGASSERT(pvpriv != NULL && dev != NULL);
/* Ignore polls from non Bluetooth network drivers */
if (dev->d_lltype != NET_LL_BLUETOOTH)
{
return flags;
}
/* Make sure that this is the driver to which the socket is connected. */
/* #warning Missing logic */
pstate = pvpriv;
radio = (FAR struct radio_driver_s *)dev;
ninfo("flags: %" PRIx32 " sent: %zd\n", flags, pstate->is_sent);
if (pstate != NULL && (flags & BLUETOOTH_POLL) != 0)
{
/* Initialize the meta data */
BLUETOOTH_ADDRCOPY(&meta.bm_raddr, &pstate->is_destaddr);
meta.bm_channel = pstate->is_channel;
meta.bm_proto = pstate->is_sock->s_proto;
/* Get the Bluetooth MAC header length */
hdrlen = radio->r_get_mhrlen(radio, &meta);
if (hdrlen < 0)
{
nerr("ERROR: Failed to get header length: %d\n", hdrlen);
ret = hdrlen;
goto errout;
}
/* Verify that the user buffer can fit within the frame with this
* MAC header.
*/
DEBUGASSERT(BLUETOOTH_MAX_FRAMELEN <= CONFIG_IOB_BUFSIZE);
if (pstate->is_buflen + hdrlen > BLUETOOTH_MAX_FRAMELEN)
{
nerr("ERROR: User buffer will not fit into the frame: %u > %u\n",
(unsigned int)(pstate->is_buflen + hdrlen),
(unsigned int)CONFIG_IOB_BUFSIZE);
ret = -E2BIG;
goto errout;
}
/* Allocate an IOB to hold the frame data */
iob = net_ioballoc(false);
if (iob == NULL)
{
nwarn("WARNING: Failed to allocate IOB\n");
return flags;
}
/* Initialize the IOB */
iob->io_offset = hdrlen;
iob->io_len = pstate->is_buflen + hdrlen;
iob->io_pktlen = pstate->is_buflen + hdrlen;
/* Copy the user data into the IOB */
memcpy(&iob->io_data[hdrlen], pstate->is_buffer, pstate->is_buflen);
/* And submit the IOB to the network driver */
ret = radio->r_req_data(radio, &meta, iob);
if (ret < 0)
{
nerr("ERROR: r_req_data() failed: %d\n", ret);
goto errout;
}
/* Save the successful result */
pstate->is_sent = pstate->is_buflen;
/* Don't allow any further call backs. */
pstate->is_cb->flags = 0;
pstate->is_cb->priv = NULL;
pstate->is_cb->event = NULL;
/* Wake up the waiting thread */
nxsem_post(&pstate->is_sem);
}
return flags;
errout:
/* Don't allow any further call backs. */
pstate->is_cb->flags = 0;
pstate->is_cb->priv = NULL;
pstate->is_cb->event = NULL;
pstate->is_sent = ret;
/* Wake up the waiting thread */
nxsem_post(&pstate->is_sem);
return flags;
}
/****************************************************************************
* Name: bluetooth_sendto
*
* Description:
* If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET)
* socket, the parameters to and 'tolen' are ignored (and the error EISCONN
* may be returned when they are not NULL and 0), and the error ENOTCONN is
* returned when the socket was not actually connected.
*
* Input Parameters:
* psock A pointer to a NuttX-specific, internal socket structure
* buf Data to send
* len Length of data to send
* flags Send flags
* to Address of recipient
* tolen The length of the address structure
*
* Returned Value:
* On success, returns the number of characters sent. On error,
* a negated errno value is retruend. See sendto() for the complete list
* of return values.
*
****************************************************************************/
static ssize_t bluetooth_sendto(FAR struct socket *psock,
FAR const void *buf, size_t len, int flags,
FAR const struct sockaddr *to,
socklen_t tolen)
{
FAR struct radio_driver_s *radio;
FAR struct bluetooth_conn_s *conn;
struct bluetooth_sendto_s state;
int ret = OK;
/* Only SOCK_RAW is supported */
if (psock->s_type != SOCK_RAW)
{
/* EDESTADDRREQ. Signifies that the socket is not connection-mode and
* no peer address is set.
*/
return -EDESTADDRREQ;
}
/* Verify that the sockfd corresponds to valid, allocated socket */
if (psock == NULL || psock->s_conn == NULL)
{
return -EBADF;
}
conn = psock->s_conn;
/* Verify that the address is large enough to be a valid PF_BLUETOOTH
* address.
*/
if (tolen < sizeof(bt_addr_t))
{
return -EDESTADDRREQ;
}
/* Get the device driver that will service this transfer */
if (psock->s_proto == BTPROTO_L2CAP)
{
radio = bluetooth_find_device(conn, &conn->bc_laddr);
if (radio == NULL)
{
return -ENODEV;
}
}
else if (psock->s_proto == BTPROTO_HCI)
{
/* TODO: should actually look among BT devices */
radio =
(FAR struct radio_driver_s *)netdev_findbyindex(conn->bc_ldev + 1);
DEBUGASSERT(radio->r_dev.d_lltype == NET_LL_BLUETOOTH);
if (radio == NULL)
{
return -ENODEV;
}
}
else
{
return -EOPNOTSUPP;
}
/* Perform the send operation */
/* Initialize the state structure. This is done with the network locked
* because we don't want anything to happen until we are ready.
*/
net_lock();
memset(&state, 0, sizeof(struct bluetooth_sendto_s));
nxsem_init(&state.is_sem, 0, 0); /* Doesn't really fail */
state.is_sock = psock; /* Socket descriptor to use */
state.is_buflen = len; /* Number of bytes to send */
state.is_buffer = buf; /* Buffer to send from */
/* Copy the destination address */
if (psock->s_proto == BTPROTO_L2CAP)
{
FAR struct sockaddr_l2 *destaddr = (FAR struct sockaddr_l2 *)to;
memcpy(&state.is_destaddr, &destaddr->l2_bdaddr,
sizeof(bt_addr_t));
}
else if (psock->s_proto == BTPROTO_HCI)
{
FAR struct sockaddr_hci *destaddr = (FAR struct sockaddr_hci *)to;
state.is_channel = destaddr->hci_channel;
}
else
{
ret = -EOPNOTSUPP;
goto err_with_net;
}
if (len > 0)
{
/* Allocate resource to receive a callback */
state.is_cb = bluetooth_callback_alloc(&radio->r_dev, conn);
if (state.is_cb)
{
/* Set up the callback in the connection */
state.is_cb->flags = BLUETOOTH_POLL;
state.is_cb->priv = (FAR void *)&state;
state.is_cb->event = bluetooth_sendto_eventhandler;
/* Notify the device driver that new TX data is available. */
netdev_txnotify_dev(&radio->r_dev, BLUETOOTH_POLL);
/* Wait for the send to complete or an error to occur.
* net_sem_wait will also terminate if a signal is received.
*/
ret = net_sem_wait(&state.is_sem);
/* Make sure that no further events are processed */
bluetooth_callback_free(&radio->r_dev, conn, state.is_cb);
}
}
nxsem_destroy(&state.is_sem);
net_unlock();
/* Check for a errors, Errors are signaled by negative errno values
* for the send length
*/
if (state.is_sent < 0)
{
return state.is_sent;
}
/* If net_sem_wait failed, then we were probably reawakened by a signal.
* In this case, net_sem_wait will have returned negated errno
* appropriately.
*/
if (ret < 0)
{
return ret;
}
/* Return the number of bytes actually sent */
return state.is_sent;
err_with_net:
nxsem_destroy(&state.is_sem);
net_unlock();
return ret;
}
/****************************************************************************
* Name: bluetooth_l2cap_send
*
* Description:
* Socket send() method for the PF_BLUETOOTH socket over BTPROTO_L2CAP.
*
* Input Parameters:
* psock An instance of the internal socket structure.
* buf Data to send
* len Length of data to send
* flags Send flags
*
* Returned Value:
* On success, returns the number of characters sent. On error, a negated
* errno value is returned (see send() for the list of appropriate error
* values.
*
****************************************************************************/
static ssize_t bluetooth_l2cap_send(FAR struct socket *psock,
FAR const void *buf,
size_t len, int flags)
{
struct sockaddr_l2 to;
FAR struct bluetooth_conn_s *conn;
ssize_t ret;
conn = psock->s_conn;
if (!_SS_ISCONNECTED(conn->bc_conn.s_flags))
{
ret = -ENOTCONN;
}
else
{
to.l2_family = AF_BLUETOOTH;
memcpy(&to.l2_bdaddr, &conn->bc_raddr, sizeof(bt_addr_t));
to.l2_cid = conn->bc_channel;
/* Then perform the send() as sendto() */
ret = bluetooth_sendto(psock, buf, len, flags,
(FAR const struct sockaddr *)&to,
sizeof(struct sockaddr_l2));
}
return ret;
}
/****************************************************************************
* Name: bluetooth_hci_send
*
* Description:
* Socket send() method for the PF_BLUETOOTH socket over BTPROTO_HCI.
*
* Input Parameters:
* psock An instance of the internal socket structure.
* buf Data to send
* len Length of data to send
* flags Send flags
*
* Returned Value:
* On success, returns the number of characters sent. On error, a negated
* errno value is returned (see send() for the list of appropriate error
* values.
*
****************************************************************************/
static ssize_t bluetooth_hci_send(FAR struct socket *psock,
FAR const void *buf,
size_t len, int flags)
{
/* We only support sendto() for HCI sockets */
return -EPFNOSUPPORT;
}
/****************************************************************************
* Name: bluetooth_send
*
* Description:
* Socket send() method for the PF_BLUETOOTH socket.
*
* Input Parameters:
* psock An instance of the internal socket structure.
* buf Data to send
* len Length of data to send
* flags Send flags
*
* Returned Value:
* On success, returns the number of characters sent. On error, a negated
* errno value is returned (see send() for the list of appropriate error
* values.
*
****************************************************************************/
static ssize_t bluetooth_send(FAR struct socket *psock, FAR const void *buf,
size_t len, int flags)
{
ssize_t ret;
/* Only SOCK_RAW is supported */
if (psock->s_type == SOCK_RAW)
{
switch (psock->s_proto)
{
case BTPROTO_L2CAP:
{
ret = bluetooth_l2cap_send(psock, buf, len, flags);
break;
}
case BTPROTO_HCI:
{
ret = bluetooth_hci_send(psock, buf, len, flags);
break;
}
default:
ret = -EPFNOSUPPORT;
}
}
else
{
ret = -EINVAL;
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bluetooth_sendmsg
*
* Description:
* If sendmsg() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET)
* socket, the parameters 'msg_name' and 'msg_namelen' are ignored (and the
* error EISCONN may be returned when they are not NULL and 0), and the
* error ENOTCONN is returned when the socket was not actually connected.
*
* Input Parameters:
* psock A pointer to a NuttX-specific, internal socket structure
* msg data to send
* flags Send flags
*
* Returned Value:
* On success, returns the number of characters sent. On error,
* a negated errno value is returned. See sendmsg() for the complete list
* of return values.
*
****************************************************************************/
ssize_t bluetooth_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
int flags)
{
FAR const void *buf = msg->msg_iov->iov_base;
size_t len = msg->msg_iov->iov_len;
FAR const struct sockaddr *to = msg->msg_name;
socklen_t tolen = msg->msg_namelen;
/* Validity check, only single iov supported */
if (msg->msg_iovlen != 1)
{
return -ENOTSUP;
}
return to ? bluetooth_sendto(psock, buf, len, flags, to, tolen) :
bluetooth_send(psock, buf, len, flags);
}
#endif /* CONFIG_NET_BLUETOOTH */