struct bcmf_frame_s *bcmf_sdpcm_alloc_frame(FAR struct bcmf_dev_s *priv, unsigned int len, bool block, bool control) { struct bcmf_sdio_frame *sframe; unsigned int header_len = sizeof(struct bcmf_sdpcm_header); if (!control) { header_len += 2; /* Data frames need alignment padding */ } if (len + header_len > MAX_NET_DEV_MTU + HEADER_SIZE || len > len + header_len) { wlerr("Invalid size %d\n", len); return NULL; } /* Allocate a frame for RX in case of control frame */ sframe = bcmf_sdio_allocate_frame(priv, block, !control); if (sframe == NULL) { return NULL; } sframe->header.len = header_len + len; sframe->header.data += header_len; return &sframe->header; }
int bcmf_sdpcm_process_header(FAR struct bcmf_sdio_dev_s *sbus, struct bcmf_sdpcm_header *header) { if (header->data_offset < sizeof(struct bcmf_sdpcm_header) || header->data_offset > header->size) { wlerr("Invalid data offset\n"); bcmf_sdpcm_rxfail(sbus, false); return -ENXIO; } /* Update tx credits */ sbus->max_seq = header->credit; return OK; }
static int bcmf_transmit(FAR struct bcmf_dev_s *priv, struct bcmf_frame_s *frame) { int ret; frame->len = priv->bc_dev.d_len + (unsigned int)(frame->data - frame->base); ret = bcmf_bdc_transmit_frame(priv, frame); if (ret) { wlerr("ERROR: Failed to transmit frame\n"); return -EIO; } NETDEV_TXPACKETS(priv->bc_dev); return OK; }
int bcmf_netdev_alloc_tx_frame(FAR struct bcmf_dev_s *priv) { if (priv->cur_tx_frame != NULL) { /* Frame available */ return OK; } /* Allocate frame for TX */ priv->cur_tx_frame = bcmf_bdc_allocate_frame(priv, MAX_NET_DEV_MTU, true); if (!priv->cur_tx_frame) { wlerr("ERROR: Cannot allocate TX frame\n"); return -ENOMEM; } return OK; }
int bcmf_sdpcm_sendframe(FAR struct bcmf_dev_s *priv) { int ret; bool is_txframe; dq_entry_t *entry; struct bcmf_sdio_frame *sframe; struct bcmf_sdpcm_header *header; FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s *)priv->bus; if (sbus->tx_queue.tail == NULL) { /* No more frames to send */ return -ENODATA; } if (sbus->tx_seq == sbus->max_seq) { // TODO handle this case wlerr("No credit to send frame\n"); return -EAGAIN; } if (nxsem_wait(&sbus->queue_mutex) < 0) { PANIC(); } entry = sbus->tx_queue.tail; sframe = container_of(entry, struct bcmf_sdio_frame, list_entry); header = (struct bcmf_sdpcm_header *)sframe->header.base; /* Set frame sequence id */ header->sequence = sbus->tx_seq++; // wlinfo("Send frame %p\n", sframe); // bcmf_hexdump(sframe->header.base, sframe->header.len, // (unsigned long)sframe->header.base); ret = bcmf_transfer_bytes(sbus, true, 2, 0, sframe->header.base, sframe->header.len); if (ret != OK) { wlinfo("fail send frame %d\n", ret); ret = -EIO; goto exit_abort; // TODO handle retry count and remove frame from queue + abort TX } /* Frame sent, remove it from queue */ bcmf_dqueue_pop_tail(&sbus->tx_queue); nxsem_post(&sbus->queue_mutex); is_txframe = sframe->tx; /* Free frame buffer */ bcmf_sdio_free_frame(priv, sframe); if (is_txframe) { /* Notify upper layer at least one TX buffer is available */ bcmf_netdev_notify_tx_done(priv); } return OK; exit_abort: // bcmf_sdpcm_txfail(sbus, false); nxsem_post(&sbus->queue_mutex); return ret; }
int bcmf_sdpcm_readframe(FAR struct bcmf_dev_s *priv) { int ret; uint16_t len, checksum; struct bcmf_sdpcm_header *header; struct bcmf_sdio_frame *sframe; FAR struct bcmf_sdio_dev_s *sbus = (FAR struct bcmf_sdio_dev_s *)priv->bus; /* Request free frame buffer */ sframe = bcmf_sdio_allocate_frame(priv, false, false); if (sframe == NULL) { wlinfo("fail alloc\n"); return -EAGAIN; } header = (struct bcmf_sdpcm_header *)sframe->data; /* Read header */ ret = bcmf_transfer_bytes(sbus, false, 2, 0, (uint8_t *)header, 4); if (ret != OK) { wlinfo("failread size\n"); ret = -EIO; goto exit_abort; } len = header->size; checksum = header->checksum; /* All zero means no more to read */ if (!(len | checksum)) { ret = -ENODATA; goto exit_free_frame; } if (((~len & 0xffff) ^ checksum) || len < sizeof(struct bcmf_sdpcm_header)) { wlerr("Invalid header checksum or len %x %x\n", len, checksum); ret = -EINVAL; goto exit_abort; } if (len > sframe->header.len) { wlerr("Frame is too large, cancel %d %d\n", len, sframe->header.len); ret = -ENOMEM; goto exit_abort; } /* Read remaining frame data */ ret = bcmf_transfer_bytes(sbus, false, 2, 0, (uint8_t *)header + 4, len - 4); if (ret != OK) { ret = -EIO; goto exit_abort; } // wlinfo("Receive frame %p %d\n", sframe, len); // bcmf_hexdump((uint8_t *)header, header->size, (unsigned int)header); /* Process and validate header */ ret = bcmf_sdpcm_process_header(sbus, header); if (ret != OK) { wlerr("Error while processing header %d\n", ret); ret = -EINVAL; goto exit_free_frame; } /* Update frame structure */ sframe->header.len = header->size; sframe->header.data += header->data_offset; /* Process received frame content */ switch (header->channel & 0x0f) { case SDPCM_CONTROL_CHANNEL: ret = bcmf_cdc_process_control_frame(priv, &sframe->header); goto exit_free_frame; case SDPCM_EVENT_CHANNEL: if (header->data_offset == header->size) { /* Empty event, ignore */ ret = OK; } else { ret = bcmf_bdc_process_event_frame(priv, &sframe->header); } goto exit_free_frame; case SDPCM_DATA_CHANNEL: /* Queue frame and notify network layer frame is available */ if (nxsem_wait(&sbus->queue_mutex) < 0) { PANIC(); } bcmf_dqueue_push(&sbus->rx_queue, &sframe->list_entry); nxsem_post(&sbus->queue_mutex); bcmf_netdev_notify_rx(priv); /* Upper layer have to free all received frames */ ret = OK; break; default: wlerr("Got unexpected message type %d\n", header->channel); ret = -EINVAL; goto exit_free_frame; } return ret; exit_abort: bcmf_sdpcm_rxfail(sbus, false); exit_free_frame: bcmf_sdio_free_frame(priv, sframe); return ret; }
static void bcmf_receive(FAR struct bcmf_dev_s *priv) { struct bcmf_frame_s *frame; // wlinfo("Entry\n"); do { /* Request frame buffer from bus interface */ frame = bcmf_bdc_rx_frame(priv); if (frame == NULL) { /* No more frame to process */ break; } if (!priv->bc_bifup) { /* Interface down, drop frame */ priv->bus->free_frame(priv, frame); continue; } priv->bc_dev.d_buf = frame->data; priv->bc_dev.d_len = frame->len - (uint32_t)(frame->data - frame->base); wlinfo("Got frame %p %d\n", frame, priv->bc_dev.d_len); #ifdef CONFIG_NET_PKT /* When packet sockets are enabled, feed the frame into the packet tap */ pkt_input(&priv->bc_dev); #endif /* We only accept IP packets of the configured type and ARP packets */ #ifdef CONFIG_NET_IPv4 if (BUF->type == HTONS(ETHTYPE_IP)) { ninfo("IPv4 frame\n"); NETDEV_RXIPV4(&priv->bc_dev); /* Handle ARP on input then give the IPv4 packet to the network * layer */ arp_ipin(&priv->bc_dev); ipv4_input(&priv->bc_dev); /* If the above function invocation resulted in data that should be * sent out on the network, the field d_len will set to a value > 0. */ if (priv->bc_dev.d_len > 0) { /* Update the Ethernet header with the correct MAC address */ #ifdef CONFIG_NET_IPv6 if (IFF_IS_IPv4(priv->bc_dev.d_flags)) #endif { arp_out(&priv->bc_dev); } #ifdef CONFIG_NET_IPv6 else { neighbor_out(&kel->bc_dev); } #endif /* And send the packet */ bcmf_transmit(priv, frame); } else { /* Release RX frame buffer */ priv->bus->free_frame(priv, frame); } } else #endif #ifdef CONFIG_NET_IPv6 if (BUF->type == HTONS(ETHTYPE_IP6)) { ninfo("Iv6 frame\n"); NETDEV_RXIPV6(&priv->bc_dev); /* Give the IPv6 packet to the network layer */ ipv6_input(&priv->bc_dev); /* If the above function invocation resulted in data that should be * sent out on the network, the field d_len will set to a value > 0. */ if (priv->bc_dev.d_len > 0) { /* Update the Ethernet header with the correct MAC address */ #ifdef CONFIG_NET_IPv4 if (IFF_IS_IPv4(priv->bc_dev.d_flags)) { arp_out(&priv->bc_dev); } else #endif #ifdef CONFIG_NET_IPv6 { neighbor_out(&priv->bc_dev); } #endif /* And send the packet */ bcmf_transmit(priv, frame); } else { /* Release RX frame buffer */ priv->bus->free_frame(priv, frame); } } else #endif #ifdef CONFIG_NET_ARP if (BUF->type == htons(ETHTYPE_ARP)) { arp_arpin(&priv->bc_dev); NETDEV_RXARP(&priv->bc_dev); /* If the above function invocation resulted in data that should be * sent out on the network, the field d_len will set to a value > 0. */ if (priv->bc_dev.d_len > 0) { bcmf_transmit(priv, frame); } else { /* Release RX frame buffer */ priv->bus->free_frame(priv, frame); } } else #endif { wlerr("ERROR: RX dropped\n"); NETDEV_RXDROPPED(&priv->bc_dev); priv->bus->free_frame(priv, frame); } } while (1); /* While there are more packets to be processed */ }
int bcmf_bdc_process_event_frame(FAR struct bcmf_dev_s *priv, struct bcmf_frame_s *frame) { int data_size; struct bcmf_bdc_header *header; struct bcmf_event_msg *event_msg; uint32_t event_id; event_handler_t handler; /* Check frame header */ data_size = frame->len - (int)(frame->data - frame->base); if (data_size < sizeof(struct bcmf_bdc_header)) { goto exit_invalid_frame; } header = (struct bcmf_bdc_header *)frame->data; data_size -= sizeof(struct bcmf_bdc_header) + header->data_offset * 4; if (data_size < sizeof(struct bcmf_event_msg)) { goto exit_invalid_frame; } data_size -= sizeof(struct ether_header) + sizeof(struct bcmf_eth_header); /* Check ethernet header */ event_msg = (struct bcmf_event_msg *)(frame->data + sizeof(struct bcmf_bdc_header) + header->data_offset * 4); if (event_msg->eth.ether_type != BCMF_EVENT_ETHER_TYPE || memcmp(event_msg->bcm_eth.oui, bcmf_broadcom_oui, 3)) { goto exit_invalid_frame; } event_id = bcmf_getle32(&event_msg->event.type); if (event_id >= BCMF_EVENT_COUNT) { wlinfo("Invalid event id %d\n", event_id); return -EINVAL; } /* Dispatch event to registered handler */ handler = priv->event_handlers[event_id]; if (handler != NULL) { handler(priv, &event_msg->event, data_size); } return OK; exit_invalid_frame: wlerr("Invalid event frame\n"); bcmf_hexdump(frame->base, frame->len, (unsigned long)frame->base); return -EINVAL; }