static int bcmf_ifdown(FAR struct net_driver_s *dev) { wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)dev->d_private; irqstate_t flags; /* Disable the hardware interrupt */ flags = enter_critical_section(); #warning Missing logic /* Cancel the TX poll timer */ wd_cancel(priv->bc_txpoll); /* Put the EMAC in its reset, non-operational state. This should be * a known configuration that will guarantee the bcmf_ifup() always * successfully brings the interface back up. */ /* Mark the device "down" */ priv->bc_bifup = false; leave_critical_section(flags); return OK; }
static int bcmf_rmmac(FAR struct net_driver_s *dev, FAR const uint8_t *mac) { wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)dev->d_private; /* Add the MAC address to the hardware multicast routing table */ return OK; }
static void bcmf_ipv6multicast(FAR struct bcmf_dev_s *priv) { wlinfo("Entry\n"); FAR struct net_driver_s *dev; uint16_t tmp16; uint8_t mac[6]; /* For ICMPv6, we need to add the IPv6 multicast address * * For IPv6 multicast addresses, the Ethernet MAC is derived by * the four low-order octets OR'ed with the MAC 33:33:00:00:00:00, * so for example the IPv6 address FF02:DEAD:BEEF::1:3 would map * to the Ethernet MAC address 33:33:00:01:00:03. * * NOTES: This appears correct for the ICMPv6 Router Solicitation * Message, but the ICMPv6 Neighbor Solicitation message seems to * use 33:33:ff:01:00:03. */ mac[0] = 0x33; mac[1] = 0x33; dev = &priv->bc_dev; tmp16 = dev->d_ipv6addr[6]; mac[2] = 0xff; mac[3] = tmp16 >> 8; tmp16 = dev->d_ipv6addr[7]; mac[4] = tmp16 & 0xff; mac[5] = tmp16 >> 8; ninfo("IPv6 Multicast: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); (void)bcmf_addmac(dev, mac); #ifdef CONFIG_NET_ICMPv6_AUTOCONF /* Add the IPv6 all link-local nodes Ethernet address. This is the * address that we expect to receive ICMPv6 Router Advertisement * packets. */ (void)bcmf_addmac(dev, g_ipv6_ethallnodes.ether_addr_octet); #endif /* CONFIG_NET_ICMPv6_AUTOCONF */ #ifdef CONFIG_NET_ICMPv6_ROUTER /* Add the IPv6 all link-local routers Ethernet address. This is the * address that we expect to receive ICMPv6 Router Solicitation * packets. */ (void)bcmf_addmac(dev, g_ipv6_ethallrouters.ether_addr_octet); #endif /* CONFIG_NET_ICMPv6_ROUTER */ }
static int bcmf_txpoll(FAR struct net_driver_s *dev) { FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)dev->d_private; wlinfo("Entry\n"); /* If the polling resulted in data that should be sent out on the network, * the field d_len is set to a value > 0. */ if (priv->bc_dev.d_len > 0) { /* Look up the destination MAC address and add it to the Ethernet * header. */ #ifdef CONFIG_NET_IPv4 #ifdef CONFIG_NET_IPv6 if (IFF_IS_IPv4(priv->bc_dev.d_flags)) #endif { arp_out(&priv->bc_dev); } #endif /* CONFIG_NET_IPv4 */ #ifdef CONFIG_NET_IPv6 #ifdef CONFIG_NET_IPv4 else #endif { neighbor_out(&priv->bc_dev); } #endif /* CONFIG_NET_IPv6 */ /* Send the packet */ bcmf_transmit(priv, priv->cur_tx_frame); /* Check if there is room in the device to hold another packet. If not, * return a non-zero value to terminate the poll. */ // TODO priv->cur_tx_frame = NULL; return 1; } /* If zero is returned, the polling will continue until all connections have * been examined. */ return 0; }
static int bcmf_ifup(FAR struct net_driver_s *dev) { wlinfo("Entry\n"); FAR struct bcmf_dev_s *priv = (FAR struct bcmf_dev_s *)dev->d_private; #ifdef CONFIG_NET_IPv4 ninfo("Bringing up: %d.%d.%d.%d\n", dev->d_ipaddr & 0xff, (dev->d_ipaddr >> 8) & 0xff, (dev->d_ipaddr >> 16) & 0xff, dev->d_ipaddr >> 24); #endif #ifdef CONFIG_NET_IPv6 ninfo("Bringing up: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", dev->d_ipv6addr[0], dev->d_ipv6addr[1], dev->d_ipv6addr[2], dev->d_ipv6addr[3], dev->d_ipv6addr[4], dev->d_ipv6addr[5], dev->d_ipv6addr[6], dev->d_ipv6addr[7]); #endif /* Initialize PHYs, the Ethernet interface, and setup up Ethernet interrupts */ /* Instantiate the MAC address from priv->bc_dev.d_mac.ether.ether_addr_octet */ #ifdef CONFIG_NET_ICMPv6 /* Set up IPv6 multicast address filtering */ bcmf_ipv6multicast(priv); #endif /* Set and activate a timer process */ (void)wd_start(priv->bc_txpoll, BCMF_WDDELAY, bcmf_poll_expiry, 1, (wdparm_t)priv); /* Enable the hardware interrupt */ priv->bc_bifup = true; 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; }