/* * RTEMS for Nintendo DS WiFi driver. * * Copyright (c) 2008 by Matthieu Bucchianeri * Benjamin Ratier * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * * http://www.rtems.com/license/LICENSE */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * RTEMS event number used by reception task to receive packet */ #define RECEIVE_EVENT RTEMS_EVENT_1 /* * RTEMS event number used to start the transmit daemon */ #define TRANSMIT_EVENT RTEMS_EVENT_2 /* * from compat.c */ void compat_wifi_output (struct mbuf *m); /* * wifi driver structure */ static struct { /* The bsdnet information structure */ struct arpcom arpcom; /* indicate if the hwaddr is already set */ uint8_t is_hwaddr_set; /* the thread ID of the transmit task */ rtems_id tx_id; /* the thread ID of the receive task */ rtems_id rx_id; /* reception queue (filled in compat.c:ReceiveHardwarePacket) */ struct ifqueue recvq; } softc; static void print_byte (unsigned char b) { printk ("%x%x", b >> 4, b & 0x0f); } /* * wifi timer function, must be called every 50 ms */ static void Timer_50ms (void) { Wifi_Timer (50); } /* * notification function to send fifo message to arm7 */ static void arm9_synctoarm7 (void) { /* send fifo message */ REG_IPC_FIFO_TX = 0x87654321; } /* * interrupt handler to receive fifo messages from arm7 */ static void arm9_fifo (void) { u32 value = REG_IPC_FIFO_RX; /* check incoming fifo messages */ if (value == 0x87654321) Wifi_Sync (); } /* * signal an icoming packet */ void wifi_signal (struct mbuf *m) { rtems_interrupt_level level; /* enqueue the incoming packet */ rtems_interrupt_disable (level); IF_ENQUEUE (&softc.recvq, m); rtems_interrupt_enable (level); /* signal the rx daemon */ rtems_event_send (softc.rx_id, RECEIVE_EVENT); } /* * packet reception daemon */ static void wifi_rxd (void *arg) { rtems_interrupt_level level; struct ifnet *ifp = &softc.arpcom.ac_if; while (1) { rtems_event_set events; rtems_bsdnet_event_receive (RECEIVE_EVENT, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &events); while (1) { struct ether_header *eh; struct mbuf *m; rtems_interrupt_disable (level); if (softc.recvq.ifq_head == NULL) { rtems_interrupt_enable (level); break; } /* get next packet */ IF_DEQUEUE (&softc.recvq, m); rtems_interrupt_enable (level); if (m == NULL) { panic ("wifi_rxd"); } m->m_pkthdr.rcvif = ifp; /* extract ethernet header */ eh = mtod (m, struct ether_header *); m->m_data += sizeof (struct ether_header); m->m_len = m->m_pkthdr.len = m->m_len - sizeof (struct ether_header); /* push the packet into the stack */ ether_input (ifp, eh, m); } } } /* * packet sending daemon */ static void wifi_txd (void *arg) { rtems_interrupt_level level; struct ifnet *ifp = &softc.arpcom.ac_if; while (1) { rtems_event_set events; rtems_bsdnet_event_receive (TRANSMIT_EVENT, RTEMS_WAIT | RTEMS_EVENT_ANY, RTEMS_NO_TIMEOUT, &events); while (1) { struct mbuf *m; rtems_interrupt_disable (level); if (ifp->if_snd.ifq_head == NULL) { ifp->if_flags &= ~IFF_OACTIVE; rtems_interrupt_enable (level); break; } /* grab next packet */ IF_DEQUEUE (&ifp->if_snd, m); rtems_interrupt_enable (level); if (m == NULL) { panic ("wifi_txd"); } /* call compatibility glue to dswifi */ compat_wifi_output (m); } } } /* * wifi device initialization procedure */ static void wifi_init (void *arg) { struct ifnet *ifp = &softc.arpcom.ac_if; int i; /* create the receive & send daemons */ if (softc.tx_id == 0) { Wifi_AutoConnect (); while (1) { i = Wifi_AssocStatus (); if (i == ASSOCSTATUS_ASSOCIATED) { printk ("Connected successfully!\n"); break; } if (i == ASSOCSTATUS_CANNOTCONNECT) { printk ("Could not connect!\n"); break; } } printk ("Starting daemon\n"); softc.tx_id = rtems_bsdnet_newproc ("SCtx", 4096, wifi_txd, &softc); softc.rx_id = rtems_bsdnet_newproc ("SCrx", 4096, wifi_rxd, &softc); } ifp->if_flags |= IFF_RUNNING; } /* * wifi deactivation method. */ static void wifi_shutdown (void *arg) { /* XXX */ } /* * ioctl hook function. */ static int wifi_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data) { void *sc = ifp->if_softc; switch (command) { case SIOCGIFADDR: case SIOCSIFADDR: return ether_ioctl (ifp, command, data); case SIOCSIFFLAGS: switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { case IFF_RUNNING: wifi_shutdown (sc); break; case IFF_UP: wifi_init (sc); break; case IFF_UP | IFF_RUNNING: wifi_shutdown (sc); wifi_init (sc); break; default: break; } return 0; default: return EINVAL; } } /* * function for sending a packet */ static void wifi_start (struct ifnet *ifp) { /* wake up the send daemon */ ifp->if_flags |= IFF_OACTIVE; rtems_event_send (softc.tx_id, TRANSMIT_EVENT); } /* * RTEMS device attach method for wiwi driver */ int rtems_wifi_driver_attach (struct rtems_bsdnet_ifconfig *config, int attach) { struct ifnet *ifp = &softc.arpcom.ac_if; printk ("wifi attach...\n"); /* initialize ifnet structure */ memset (&softc, 0, sizeof (softc)); ifp->if_softc = &softc; ifp->if_unit = 0; ifp->if_name = "dswifi"; ifp->if_mtu = ETHERMTU; ifp->if_init = wifi_init; ifp->if_ioctl = wifi_ioctl; ifp->if_watchdog = NULL; ifp->if_start = wifi_start; ifp->if_output = ether_output; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; if (ifp->if_snd.ifq_maxlen == 0) { ifp->if_snd.ifq_maxlen = ifqmaxlen; } if (config->mtu != 0) { ifp->if_mtu = config->mtu; } if (config->hardware_address != NULL) { softc.is_hwaddr_set = 1; memcpy (softc.arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); } else { softc.is_hwaddr_set = 0; } u32 Wifi_pass; int i; /* wifi hardware initialization took from dswifi example code */ /* send fifo message to initialize the arm7 wifi */ REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR; Wifi_pass = Wifi_Init (WIFIINIT_OPTION_USELED | WIFIINIT_OPTION_USECUSTOMALLOC); REG_IPC_FIFO_TX = 0x12345678; REG_IPC_FIFO_TX = Wifi_pass; /* setup fifo IRQ */ irqSet (IRQ_FIFO_NOT_EMPTY, arm9_fifo); irqEnable (IRQ_FIFO_NOT_EMPTY); /* enable FIFO IRQ */ REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_RECV_IRQ; /* tell wifi lib to use our handler to notify arm7 */ Wifi_SetSyncHandler (arm9_synctoarm7); /* set timer3 */ TIMER3_DATA = -6553; // 6553.1 * 256 cycles = ~50ms; TIMER3_CR = 0x00C2; // enable, irq, 1/256 clock /* setup timer IRQ */ irqSet (IRQ_TIMER3, Timer_50ms); irqEnable (IRQ_TIMER3); while (Wifi_CheckInit () == 0) { /* wait for arm7 to be initted successfully */ while (REG_VCOUNT > 192); while (REG_VCOUNT < 192); } /* wifi init complete - wifi lib can now be used! */ printk ("ok.\n"); /* retrieve MAC address if unspecified by the user */ if (!softc.is_hwaddr_set) { Wifi_GetData (WIFIGETDATA_MACADDRESS, ETHER_ADDR_LEN, softc.arpcom.ac_enaddr); } print_byte (softc.arpcom.ac_enaddr[0]); for (i = 1; i < ETHER_ADDR_LEN; i++) { printk (":"); print_byte (softc.arpcom.ac_enaddr[i]); } printk ("\n"); /* attach the interface */ if_attach (ifp); ether_ifattach (ifp); printk ("network device '%s' initialized\n", config->name); return 0; }