| /**************************************************************************** |
| * net/bluetooth/bluetooth_recvmsg.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 <nuttx/config.h> |
| |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <debug.h> |
| #include <assert.h> |
| |
| #include <netpacket/bluetooth.h> |
| #include <arch/irq.h> |
| |
| #include <nuttx/semaphore.h> |
| #include <nuttx/mm/iob.h> |
| #include <nuttx/net/net.h> |
| #include <nuttx/net/radiodev.h> |
| #include <nuttx/net/bluetooth.h> |
| #include <nuttx/wireless/bluetooth/bt_hci.h> |
| |
| #include "netdev/netdev.h" |
| #include "devif/devif.h" |
| #include "socket/socket.h" |
| #include "bluetooth/bluetooth.h" |
| |
| #ifdef CONFIG_NET_BLUETOOTH |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct bluetooth_recvfrom_s |
| { |
| FAR struct socket *ir_sock; /* Points to the parent socket structure */ |
| FAR struct devif_callback_s *ir_cb; /* Reference to callback instance */ |
| FAR struct sockaddr *ir_from; /* Location to return the from address */ |
| FAR uint8_t *ir_buffer; /* Pointer to receive buffer */ |
| size_t ir_buflen; /* Length of receive buffer */ |
| sem_t ir_sem; /* Semaphore signals recv completion */ |
| ssize_t ir_result; /* Success:size, failure:negated errno */ |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: bluetooth_count_frames |
| * |
| * Description: |
| * Return the number of frames in the RX queue. |
| * |
| * Input Parameters: |
| * conn - The socket connection structure. |
| * |
| * Returned Value: |
| * The number of frames in the queue. |
| * |
| ****************************************************************************/ |
| |
| #if CONFIG_NET_BLUETOOTH_BACKLOG > 0 |
| static int bluetooth_count_frames(FAR struct bluetooth_conn_s *conn) |
| { |
| FAR struct bluetooth_container_s *container; |
| int count; |
| |
| for (count = 0, container = conn->bc_rxhead; |
| container != NULL; |
| count++, container = container->bn_flink) |
| { |
| } |
| |
| return count; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: bluetooth_recvfrom_sender |
| * |
| * Description: |
| * Perform the reception operation if there are any queued frames in the |
| * RX frame queue. |
| * |
| * Input Parameters: |
| * |
| * Returned Value: |
| * |
| * Assumptions: |
| * The network is locked |
| * |
| ****************************************************************************/ |
| |
| static ssize_t |
| bluetooth_recvfrom_rxqueue(FAR struct radio_driver_s *radio, |
| FAR struct bluetooth_recvfrom_s *pstate) |
| { |
| FAR struct bluetooth_container_s *container; |
| FAR struct sockaddr_l2 *iaddr; |
| FAR struct bluetooth_conn_s *conn; |
| FAR struct iob_s *iob; |
| size_t copylen; |
| int ret = -EAGAIN; |
| |
| /* Check if there is anything in in the RX input queue */ |
| |
| DEBUGASSERT(pstate != NULL && pstate->ir_sock != NULL); |
| conn = (FAR struct bluetooth_conn_s *)pstate->ir_sock->s_conn; |
| DEBUGASSERT(conn != NULL); |
| |
| if (conn->bc_rxhead != NULL) |
| { |
| /* Remove the container from the RX input queue. */ |
| |
| container = conn->bc_rxhead; |
| DEBUGASSERT(container != NULL); |
| conn->bc_rxhead = container->bn_flink; |
| container->bn_flink = NULL; |
| |
| /* Did the RX queue become empty? */ |
| |
| if (conn->bc_rxhead == NULL) |
| { |
| conn->bc_rxtail = NULL; |
| } |
| |
| #if CONFIG_NET_BLUETOOTH_BACKLOG > 0 |
| /* Decrement the count of frames in the queue. */ |
| |
| DEBUGASSERT(conn->bc_backlog > 0); |
| conn->bc_backlog--; |
| DEBUGASSERT((int)conn->bc_backlog == bluetooth_count_frames(conn)); |
| #endif |
| |
| /* Extract the IOB containing the frame from the container */ |
| |
| iob = container->bn_iob; |
| container->bn_iob = NULL; |
| DEBUGASSERT(iob != NULL); |
| |
| /* Copy the new packet data into the user buffer */ |
| |
| copylen = iob->io_len - iob->io_offset; |
| memcpy(pstate->ir_buffer, &iob->io_data[iob->io_offset], copylen); |
| |
| ninfo("Received %d bytes\n", (int)copylen); |
| ret = copylen; |
| |
| /* If a 'from' address pointer was supplied, copy the source address |
| * in the container there. |
| */ |
| |
| if (pstate->ir_from != NULL) |
| { |
| iaddr = (FAR struct sockaddr_l2 *)pstate->ir_from; |
| iaddr->l2_family = AF_BLUETOOTH; |
| BLUETOOTH_ADDRCOPY(&iaddr->l2_bdaddr, &container->bn_raddr); |
| iaddr->l2_cid = container->bn_channel; |
| } |
| |
| /* Free both the IOB and the container */ |
| |
| iob_free(iob); |
| bluetooth_container_free(container); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: bluetooth_recvfrom_eventhandler |
| * |
| * Description: |
| * |
| * Input Parameters: |
| * |
| * Returned Value: |
| * |
| * Assumptions: |
| * The network is locked. |
| * |
| ****************************************************************************/ |
| |
| static uint16_t bluetooth_recvfrom_eventhandler(FAR struct net_driver_s *dev, |
| FAR void *pvpriv, |
| uint16_t flags) |
| { |
| FAR struct bluetooth_recvfrom_s *pstate; |
| FAR struct radio_driver_s *radio; |
| ssize_t ret; |
| |
| ninfo("flags: %04x\n", flags); |
| |
| 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 bound. */ |
| |
| /* #warning Missing logic */ |
| |
| pstate = pvpriv; |
| radio = (FAR struct radio_driver_s *)dev; |
| |
| /* 'pstate' might be null in some race conditions (?) */ |
| |
| if (pstate != NULL) |
| { |
| /* If a new packet is available, then complete the read action. */ |
| |
| if ((flags & BLUETOOTH_NEWDATA) != 0) |
| { |
| /* Attempt to receive the frame */ |
| |
| ret = bluetooth_recvfrom_rxqueue(radio, pstate); |
| if (ret > 0) |
| { |
| /* Don't allow any further call backs. */ |
| |
| pstate->ir_cb->flags = 0; |
| pstate->ir_cb->priv = NULL; |
| pstate->ir_cb->event = NULL; |
| pstate->ir_result = ret; |
| |
| /* indicate that the data has been consumed */ |
| |
| flags &= ~BLUETOOTH_NEWDATA; |
| |
| /* Wake up the waiting thread, returning the number of bytes |
| * actually read. |
| */ |
| |
| nxsem_post(&pstate->ir_sem); |
| } |
| } |
| } |
| |
| return flags; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: bluetooth_recvmsg |
| * |
| * Description: |
| * Implements the socket recvfrom interface for the case of the AF_INET |
| * and AF_INET6 address families. bluetooth_recvmsg() receives messages |
| * from a socket, and may be used to receive data on a socket whether or |
| * not it is connection-oriented. |
| * |
| * If msg_name is not NULL, and the underlying protocol provides the source |
| * address, this source address is filled in. The argument 'msg_namelen' is |
| * initialized to the size of the buffer associated with msg_name, and |
| * modified on return to indicate the actual size of the address stored |
| * there. |
| * |
| * Input Parameters: |
| * psock A pointer to a NuttX-specific, internal socket structure |
| * msg Buffer to receive data |
| * flags Receive flags |
| * |
| * Returned Value: |
| * On success, returns the number of characters received. If no data is |
| * available to be received and the peer has performed an orderly shutdown, |
| * recvmsg() will return 0. Otherwise, on errors, a negated errno value is |
| * returned (see recvmsg() for the list of appropriate error values). |
| * |
| * Assumptions: |
| * The network is locked. |
| * |
| ****************************************************************************/ |
| |
| ssize_t bluetooth_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, |
| int flags) |
| { |
| FAR void *buf = msg->msg_iov->iov_base; |
| size_t len = msg->msg_iov->iov_len; |
| FAR struct sockaddr *from = msg->msg_name; |
| FAR socklen_t *fromlen = &msg->msg_namelen; |
| FAR struct bluetooth_conn_s *conn = psock->s_conn; |
| FAR struct radio_driver_s *radio; |
| struct bluetooth_recvfrom_s state; |
| ssize_t ret; |
| |
| /* If a 'from' address has been provided, verify that it is large |
| * enough to hold this address family. |
| */ |
| |
| if (from != NULL && *fromlen < sizeof(struct sockaddr_l2)) |
| { |
| return -EINVAL; |
| } |
| |
| if (psock->s_type != SOCK_RAW) |
| { |
| nerr("ERROR: Unsupported socket type: %d\n", psock->s_type); |
| return -EPROTONOSUPPORT; |
| } |
| |
| /* Perform the packet recvmsg() 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_recvfrom_s)); |
| |
| state.ir_buflen = len; |
| state.ir_buffer = buf; |
| state.ir_sock = psock; |
| state.ir_from = from; |
| |
| /* Get the device driver that will service this transfer */ |
| |
| radio = bluetooth_find_device(conn, &conn->bc_laddr); |
| if (radio == NULL) |
| { |
| ret = -ENODEV; |
| goto errout_with_lock; |
| } |
| |
| /* Before we wait for data, let's check if there are already frame(s) |
| * waiting in the RX queue. |
| */ |
| |
| ret = bluetooth_recvfrom_rxqueue(radio, &state); |
| if (ret > 0) |
| { |
| /* Good newe! We have a frame and we are done. */ |
| |
| net_unlock(); |
| return ret; |
| } |
| |
| nxsem_init(&state.ir_sem, 0, 0); /* Doesn't really fail */ |
| |
| /* Set up the callback in the connection */ |
| |
| state.ir_cb = bluetooth_callback_alloc(&radio->r_dev, conn); |
| if (state.ir_cb) |
| { |
| state.ir_cb->flags = (BLUETOOTH_NEWDATA | BLUETOOTH_POLL); |
| state.ir_cb->priv = (FAR void *)&state; |
| state.ir_cb->event = bluetooth_recvfrom_eventhandler; |
| |
| /* Wait for either the receive to complete or for an error/timeout to |
| * occur. NOTES: (1) net_sem_wait will also terminate if a signal |
| * is received, (2) the network is locked! It will be un-locked while |
| * the task sleeps and automatically re-locked when the task restarts. |
| */ |
| |
| net_sem_wait(&state.ir_sem); |
| |
| /* Make sure that no further events are processed */ |
| |
| bluetooth_callback_free(&radio->r_dev, conn, state.ir_cb); |
| ret = state.ir_result; |
| } |
| else |
| { |
| ret = -EBUSY; |
| } |
| |
| nxsem_destroy(&state.ir_sem); |
| |
| errout_with_lock: |
| net_unlock(); |
| return ret; |
| } |
| |
| #endif /* CONFIG_NET_BLUETOOTH */ |