/**************************************************************************** * drivers/usbhost/rtl8187.c * * Copyright (C) 2011, 2012 Gregory Nutt. All rights reserved. * Authors: Rafael Noronha * Gregory Nutt * * Portions of the logic in this file derives from the KisMAC RTL8187x driver * * Created by pr0gg3d on 02/24/08. * * Which, in turn, came frm the SourceForge rt2x00 project: * * Copyright (C) 2004 - 2006 rt2x00 SourceForge Project * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * There are probably also pieces from the Linux RTL8187x driver * * Copyright 2007 Michael Wu * Copyright 2007 Andrea Merello * * Based on the r8187 driver, which is: * Copyright 2004-2005 Andrea Merello , et al. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtl8187x.h" #if defined(CONFIG_USBHOST) && defined(CONFIG_NET) /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Configuration ************************************************************/ #ifndef CONFIG_NET_NOINTS # warning "CONFIG_NET_NOINTS must be set" #endif #ifndef CONFIG_NET_MULTIBUFFER # warning "CONFIG_NET_MULTIBUFFER must be set" #endif #ifndef CONFIG_SCHED_WORKQUEUE # warning "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)" #endif /* Driver support ***********************************************************/ /* This format is used to construct the /dev/wlan[n] device driver path. It * defined here so that it will be used consistently in all places. */ #define DEV_FORMAT "wlan%d" /* Used in rtl8187x_cfgdesc() */ #define USBHOST_IFFOUND 0x01 #define USBHOST_BINFOUND 0x02 #define USBHOST_BOUTFOUND 0x04 #define USBHOST_ALLFOUND 0x07 #define USBHOST_MAX_CREFS 0x7fff /* TX poll delay = 1 seconds. CLK_TCK is the number of clock ticks per second */ #define RTL8187X_TXDELAY (1*CLK_TCK) #define RTL8187X_RETRYDELAY (CLK_TCK/2) /* RX poll delay = 100 millseconds. */ #define RTL8187X_RXDELAY (CLK_TCK / 10) /* TX timeout = 1 minute */ #define RTL8187X_TXTIMEOUT (60*CLK_TCK) /* Statistics helper */ #ifdef CONFIG_NET_STATISTICS # define RTL8187X_STATS(p,f) (p->stats.f)++ #else # define RTL8187X_STATS(p,f) #endif /**************************************************************************** * Private Types ****************************************************************************/ /* Describes one IEEE 802.11 Channel */ struct ieee80211_channel_s { uint16_t chan; /* Channel number (IEEE 802.11) */ uint16_t freq; /* Frequency in MHz */ uint32_t val; /* HW specific value for the channel */ uint32_t flag; /* Flag for hostapd use (IEEE80211_CHAN_*) */ uint8_t pwrlevel; uint8_t antmax; }; /* Driver statistics */ #ifdef CONFIG_NET_STATISTICS struct rtl8187x_statistics_s { uint32_t transmitted; /* Number of packets transmitted */ uint32_t txfailed; /* - Number of failed packet transmissions */ uint32_t received; /* Number of packets received: */ uint32_t rxdropped; /* RX Dropped: */ uint32_t rxtoosmall; /* - Number of bad, small packets received */ uint32_t rxtoobig; /* - Number of bad, big packets received */ uint32_t rxcrcerr; /* - Number of packets received with a CRC error */ uint32_t rxbadproto; /* - Number of dropped packets with bad protocol */ /* RX Good: received - rxdropped */ uint32_t rxippackets; /* - Number of good IP packets */ uint32_t rxarppackets; /* - Number of good ARP packets */ }; #endif /* This structure contains the internal, private state of the USB host class * driver. */ struct rtl8187x_state_s { /* This is the externally visible portion of the USB class state. This must * be the first thing in the structure so that 'struct rtl8187x_state_s' can * be obtained from the class instance by a simple cast. */ struct usbhost_class_s class; /* This is an instance of the USB host controller driver bound to this class instance */ struct usbhost_driver_s *hcd; /* The following fields support the USB class driver */ volatile bool disconnected; /* TRUE: Device has been disconnected */ bool bifup; /* TRUE: Ethernet interface is up */ bool silence; /* TRUE: Packets are being received */ uint8_t ifno; /* Interface number */ uint8_t asicrev; /* ASIC revision number */ uint8_t rate; /* RX rate parameter */ uint8_t width; /* EEPROM width (see PCI_EEPROM_WIDTH_* defines) */ uint8_t datain; /* EEPROM data input */ uint8_t dataout; /* EEPROM data output */ uint8_t dataclk; /* EEPROM data clock */ uint8_t chipsel; /* EEPROM chip select */ uint8_t signal; /* Estimated signal strength */ int8_t crefs; /* >0: The driver is busy and cannot be destoryed */ uint16_t rxpwrbase; /* RX power base */ uint32_t lastpoll; /* Time of last poll */ sem_t exclsem; /* Used to maintain mutual exclusive access */ struct work_s wkdisconn; /* For performing disconnect on the worker thread */ struct work_s wktxpoll; /* Perform TX poll on work thread */ struct work_s wkrxpoll; /* Perform RX poll on work thread */ FAR struct usb_ctrlreq_s *ctrlreq; /* The allocated request buffer */ FAR uint8_t *tbuffer; /* The allocated transfer buffer */ size_t tbuflen; /* Size of the allocated transfer buffer */ usbhost_ep_t epin; /* IN endpoint */ usbhost_ep_t epout; /* OUT endpoint */ WDOG_ID wdtxpoll; /* TX poll timer */ WDOG_ID wdrxpoll; /* RX poll timer */ /* Chip-specific function pointers */ void (*rfinit)(FAR struct rtl8187x_state_s *); void (*settxpower)(FAR struct rtl8187x_state_s *priv, int channel); /* Channel configuration */ struct ieee80211_channel_s channels[RTL8187X_NCHANNELS]; /* Statistics */ #ifdef CONFIG_NET_STATISTICS struct rtl8187x_statistics_s stats; #endif /* This holds the information visible to uIP/NuttX */ struct net_driver_s ethdev; /* Interface understood by uIP */ FAR uint8_t *txbuffer; /* The allocated TX I/O buffer */ FAR uint8_t *rxbuffer; /* The allocated RX I/O buffer */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* General Utility Functions ************************************************/ /* Semaphores */ static void rtl8187x_takesem(sem_t *sem); #define rtl8187x_givesem(s) sem_post(s); /* Memory allocation services */ static inline FAR struct rtl8187x_state_s *rtl8187x_allocclass(void); static inline void rtl8187x_freeclass(FAR struct rtl8187x_state_s *class); /* Standard USB host class functions ****************************************/ /* Worker thread actions */ static void rtl8187x_destroy(FAR void *arg); /* Helpers for rtl8187x_connect() */ static inline int rtl8187x_cfgdesc(FAR struct rtl8187x_state_s *priv, FAR const uint8_t *configdesc, int desclen, uint8_t funcaddr); static inline int rtl8187x_devinit(FAR struct rtl8187x_state_s *priv); /* (Little Endian) Data helpers */ static inline uint16_t rtl8187x_host2le16(uint16_t val); static inline uint16_t rtl8187x_le2host16(uint16_t val); static inline uint32_t rtl8187x_host2le32(uint32_t val); static inline uint32_t rtl8187x_le2host32(uint32_t val); static inline uint16_t rtl8187x_getle16(const uint8_t *val); static inline uint32_t rtl8187x_getle32(const uint8_t *val); static inline void rtl8187x_putle16(uint8_t *dest, uint16_t val); static void rtl8187x_putle32(uint8_t *dest, uint32_t val); /* Transfer descriptor memory management */ static inline int rtl8187x_allocbuffers(FAR struct rtl8187x_state_s *priv); static inline int rtl8187x_freebuffers(FAR struct rtl8187x_state_s *priv); /* struct usbhost_registry_s methods */ static struct usbhost_class_s *rtl8187x_create(FAR struct usbhost_driver_s *hcd, FAR const struct usbhost_id_s *id); /* struct usbhost_class_s methods */ static int rtl8187x_connect(FAR struct usbhost_class_s *class, FAR const uint8_t *configdesc, int desclen, uint8_t funcaddr); static int rtl8187x_disconnected(FAR struct usbhost_class_s *class); /* Vendor-Specific USB host support *****************************************/ static uint8_t rtl8187x_ioread8(struct rtl8187x_state_s *priv, uint16_t addr); static uint16_t rtl8187x_ioread16(struct rtl8187x_state_s *priv, uint16_t addr); static uint32_t rtl8187x_ioread32(struct rtl8187x_state_s *priv, uint16_t addr); static int rtl8187x_iowrite8(struct rtl8187x_state_s *priv, uint16_t addr, uint8_t val); static int rtl8187x_iowrite16(struct rtl8187x_state_s *priv, uint16_t addr, uint16_t val); static int rtl8187x_iowrite32(struct rtl8187x_state_s *priv, uint16_t addr, uint32_t val); static uint16_t rtl8187x_read(FAR struct rtl8187x_state_s *priv, uint8_t addr); static inline void rtl8187x_write_8051(FAR struct rtl8187x_state_s *priv, uint8_t addr, uint16_t data); static inline void rtl8187x_write_bitbang(struct rtl8187x_state_s *priv, uint8_t addr, uint16_t data); static void rtl8187x_write(FAR struct rtl8187x_state_s *priv, uint8_t addr, uint16_t data); /* Ethernet driver methods **************************************************/ /* TX logic */ static int rtl8187x_transmit(FAR struct rtl8187x_state_s *priv); static int rtl8187x_txpoll(struct net_driver_s *dev); static void rtl8187x_txpollwork(FAR void *arg); static void rtl8187x_txpolltimer(int argc, uint32_t arg, ...); /* RX logic */ static inline int rtl8187x_receive(FAR struct rtl8187x_state_s *priv, unsigned int iolen, unsigned int *pktlen); static inline void rtl8187x_rxdispatch(FAR struct rtl8187x_state_s *priv, unsigned int pktlen); static void rtl8187x_rxpollwork(FAR void *arg); static void rtl8187x_rxpolltimer(int argc, uint32_t arg, ...); /* Network callback functions */ static int rtl8187x_ifup(struct net_driver_s *dev); static int rtl8187x_ifdown(struct net_driver_s *dev); static int rtl8187x_txavail(struct net_driver_s *dev); #ifdef CONFIG_NET_IGMP static int rtl8187x_addmac(struct net_driver_s *dev, FAR const uint8_t *mac); static int rtl8187x_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac); #endif /* EEPROM support */ static inline void rtl8187x_eeprom_pulsehigh(FAR struct rtl8187x_state_s *priv); static inline void rtl8187x_eeprom_pulselow(FAR struct rtl8187x_state_s *priv); static void rtl8187x_eeprom_rdsetup(FAR struct rtl8187x_state_s *priv); static void rtl8187x_eeprom_wrsetup(FAR struct rtl8187x_state_s *priv); static void rtl8187x_eeprom_cleanup(FAR struct rtl8187x_state_s *priv); static void rtl8187x_eeprom_wrbits(FAR struct rtl8187x_state_s *priv, uint16_t data, uint16_t count); static void rtl8187x_eeprom_rdbits(FAR struct rtl8187x_state_s *priv, FAR uint16_t * data, uint16_t count); static void rtl8187x_eeprom_read(FAR struct rtl8187x_state_s *priv, uint8_t word, uint16_t *data); static void rtl8187x_eeprom_multiread(FAR struct rtl8187x_state_s *priv, uint8_t word, FAR uint16_t *data, uint16_t nwords); /* PHY support */ static void rtl8187x_wrphy(FAR struct rtl8187x_state_s *priv, uint8_t addr, uint32_t data); static inline void rtl8187x_wrphyofdm(FAR struct rtl8187x_state_s *priv, uint8_t addr, uint32_t data); static inline void rtl8187x_wrphycck(FAR struct rtl8187x_state_s *priv, uint8_t addr, uint32_t data); /* Chip-specific RF initialization and TX power setup */ static void rtl8225_rfinit(FAR struct rtl8187x_state_s *priv); static void rtl8225z2_rfinit(FAR struct rtl8187x_state_s *priv); static void rtl8225_settxpower(FAR struct rtl8187x_state_s *priv, int channel); static void rtl8225z2_settxpower(FAR struct rtl8187x_state_s *priv, int channel); /* RTL8187 Ethernet initialization and registration */ static int rtl8187x_reset(struct rtl8187x_state_s *priv); static void rtl8187x_setchannel(FAR struct rtl8187x_state_s *priv, int channel); static int rtl8187x_start(FAR struct rtl8187x_state_s *priv); static void rtl8187x_stop(FAR struct rtl8187x_state_s *priv); static inline int rtl8187x_setup(FAR struct rtl8187x_state_s *priv); static int rtl8187x_netinitialize(FAR struct rtl8187x_state_s *priv); static int rtl8187x_netuninitialize(FAR struct rtl8187x_state_s *priv); /**************************************************************************** * Private Data ****************************************************************************/ /* This structure provides the registry entry ID information that will be * used to associate the USB class driver to a connected USB device. */ static const const struct usbhost_id_s g_id[2] = { { USB_CLASS_VENDOR_SPEC, /* base */ 0xff, /* subclass */ 0xff, /* proto */ CONFIG_RTL8187_VID, /* vid */ CONFIG_RTL8187_PID /* pid */ }, { 0, /* base */ 0, /* subclass */ 0, /* proto */ CONFIG_RTL8187_VID, /* vid */ CONFIG_RTL8187_PID /* pid */ } }; /* This is the USB host wireless LAN class's registry entry */ static struct usbhost_registry_s g_wlan = { NULL, /* flink */ rtl8187x_create, /* create */ 2, /* nids */ g_id /* id[] */ }; /* This is a bitmap that is used to allocate device names /dev/wlana-z. */ static uint32_t g_devinuse; /* Default values for IEEE 802.11 channels */ static const struct ieee80211_channel_s g_channels[RTL8187X_NCHANNELS] = { { 1, 2412, 0, 0, 0, 0}, { 2, 2417, 0, 0, 0, 0}, { 3, 2422, 0, 0, 0, 0}, { 4, 2427, 0, 0, 0, 0}, { 5, 2432, 0, 0, 0, 0}, { 6, 2437, 0, 0, 0, 0}, { 7, 2442, 0, 0, 0, 0}, { 8, 2447, 0, 0, 0, 0}, { 9, 2452, 0, 0, 0, 0}, {10, 2457, 0, 0, 0, 0}, {11, 2462, 0, 0, 0, 0}, {12, 2467, 0, 0, 0, 0}, {13, 2472, 0, 0, 0, 0}, {14, 2484, 0, 0, 0, 0} }; static const uint32_t g_chanselect[RTL8187X_NCHANNELS] = { 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 }; /* RTL8225-specific settings */ static const uint16_t g_rtl8225bcd_rxgain[] = { 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb }; static const uint8_t g_rtl8225_agc[] = { 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; static const uint8_t g_rtl8225_gain[] = { 0x23, 0x88, 0x7c, 0xa5, /* -82dBm */ 0x23, 0x88, 0x7c, 0xb5, /* -82dBm */ 0x23, 0x88, 0x7c, 0xc5, /* -82dBm */ 0x33, 0x80, 0x79, 0xc5, /* -78dBm */ 0x43, 0x78, 0x76, 0xc5, /* -74dBm */ 0x53, 0x60, 0x73, 0xc5, /* -70dBm */ 0x63, 0x58, 0x70, 0xc5, /* -66dBm */ }; static const uint8_t g_rtl8225_threshold[] = { 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd }; static const uint8_t g_rtl8225_txgaincckofdm[] = { 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e }; static const uint8_t g_rtl8225_txpowercck[] = { 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 }; static const uint8_t g_rtl8225_txpowercckch14[] = { 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 }; static const uint8_t g_rtl8225_txpowerofdm[] = { 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 }; /* RTL8225z2-Specific settings */ static const uint8_t g_rtl8225z2_txpowercckch14[] = { 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 }; static const uint8_t g_rtl8225z2_txpowercck[] = { 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 }; static const uint8_t g_rtl8225z2_txpowerofdm[] = { 0x42, 0x00, 0x40, 0x00, 0x40 }; static const uint8_t g_rtl8225z2_txgaincckofdm[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 }; static const uint16_t g_rtl8225z2_rxgain[] = { 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb }; static const uint8_t g_rtl8225z2_gainbg[] = { 0x23, 0x15, 0xa5, /* -82-1dBm */ 0x23, 0x15, 0xb5, /* -82-2dBm */ 0x23, 0x15, 0xc5, /* -82-3dBm */ 0x33, 0x15, 0xc5, /* -78dBm */ 0x43, 0x15, 0xc5, /* -74dBm */ 0x53, 0x15, 0xc5, /* -70dBm */ 0x63, 0x15, 0xc5 /* -66dBm */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: rtl8187x_takesem * * Description: * This is just a wrapper to handle the annoying behavior of semaphore * waits that return due to the receipt of a signal. * ****************************************************************************/ static void rtl8187x_takesem(sem_t *sem) { /* Take the semaphore (perhaps waiting) */ while (sem_wait(sem) != 0) { /* The only case that an error should occr here is if the wait was * awakened by a signal. */ ASSERT(errno == EINTR); } } /**************************************************************************** * Name: rtl8187x_allocclass * * Description: * This is really part of the logic that implements the create() method * of struct usbhost_registry_s. This function allocates memory for one * new class instance. * * Input Parameters: * None * * Returned Values: * On success, this function will return a non-NULL instance of struct * usbhost_class_s. NULL is returned on failure; this function will * will fail only if there are insufficient resources to create another * USB host class instance. * ****************************************************************************/ static inline FAR struct rtl8187x_state_s *rtl8187x_allocclass(void) { FAR struct rtl8187x_state_s *priv; DEBUGASSERT(!up_interrupt_context()); priv = (FAR struct rtl8187x_state_s *)kmalloc(sizeof(struct rtl8187x_state_s)); uvdbg("Allocated: %p\n", priv);; return priv; } /**************************************************************************** * Name: rtl8187x_freeclass * * Description: * Free a class instance previously allocated by rtl8187x_allocclass(). * * Input Parameters: * class - A reference to the class instance to be freed. * * Returned Values: * None * ****************************************************************************/ static inline void rtl8187x_freeclass(FAR struct rtl8187x_state_s *class) { DEBUGASSERT(class != NULL); /* Free the class instance (calling sched_free() in case we are executing * from an interrupt handler. */ uvdbg("Freeing: %p\n", class); kfree(class); } /**************************************************************************** * Name: rtl8187x_destroy * * Description: * The USB device has been disconnected and the refernce count on the USB * host class instance has gone to 1.. Time to destroy the USB host class * instance. * * Input Parameters: * arg - A reference to the class instance to be destroyed. * * Returned Values: * None * ****************************************************************************/ static void rtl8187x_destroy(FAR void *arg) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)arg; DEBUGASSERT(priv != NULL); uvdbg("crefs: %d\n", priv->crefs); /* Unregister WLAN network interface */ rtl8187x_netuninitialize(priv); /* Free the endpoints */ if (priv->epout) { DRVR_EPFREE(priv->hcd, priv->epout); } if (priv->epin) { DRVR_EPFREE(priv->hcd, priv->epin); } /* Free any transfer buffers */ rtl8187x_freebuffers(priv); /* Destroy the semaphores */ sem_destroy(&priv->exclsem); /* Disconnect the USB host device */ DRVR_DISCONNECT(priv->hcd); /* And free the class instance. Hmmm.. this may execute on the worker * thread and the work structure is part of what is getting freed. That * should be okay because once the work contained is removed from the * queue, it should not longer be accessed by the worker thread. */ rtl8187x_freeclass(priv); } /**************************************************************************** * Name: rtl8187x_cfgdesc * * Description: * This function implements the connect() method of struct * usbhost_class_s. This method is a callback into the class * implementation. It is used to provide the device's configuration * descriptor to the class so that the class may initialize properly * * Input Parameters: * priv - The USB host class instance. * configdesc - A pointer to a uint8_t buffer container the configuration descripor. * desclen - The length in bytes of the configuration descriptor. * funcaddr - The USB address of the function containing the endpoint that EP0 * controls * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is * returned indicating the nature of the failure * * Assumptions: * This function will *not* be called from an interrupt handler. * ****************************************************************************/ static inline int rtl8187x_cfgdesc(FAR struct rtl8187x_state_s *priv, FAR const uint8_t *configdesc, int desclen, uint8_t funcaddr) { FAR struct usb_cfgdesc_s *cfgdesc; FAR struct usb_desc_s *desc; FAR struct usbhost_epdesc_s bindesc; FAR struct usbhost_epdesc_s boutdesc; int remaining; uint8_t found = 0; int ret; uvdbg("desclen:%d funcaddr:%d\n", desclen, funcaddr); DEBUGASSERT(priv != NULL && configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s)); /* Verify that we were passed a configuration descriptor */ cfgdesc = (FAR struct usb_cfgdesc_s *)configdesc; if (cfgdesc->type != USB_DESC_TYPE_CONFIG) { return -EINVAL; } /* Get the total length of the configuration descriptor (little endian). * It might be a good check to get the number of interfaces here too. */ remaining = (int)rtl8187x_getle16(cfgdesc->totallen); /* Skip to the next entry descriptor */ configdesc += cfgdesc->len; remaining -= cfgdesc->len; /* Loop while there are more descriptors to examine */ while (remaining >= sizeof(struct usb_desc_s)) { /* What is the next descriptor? */ desc = (FAR struct usb_desc_s *)configdesc; switch (desc->type) { /* Interface descriptor. We really should get the number of endpoints * from this descriptor too. */ case USB_DESC_TYPE_INTERFACE: { FAR struct usb_ifdesc_s *ifdesc = (FAR struct usb_ifdesc_s *)configdesc; uvdbg("Interface descriptor\n"); DEBUGASSERT(remaining >= USB_SIZEOF_IFDESC); /* Save the interface number and mark ONLY the interface found */ priv->ifno = ifdesc->ifno; found = USBHOST_IFFOUND; } break; /* Endpoint descriptor. Here, we expect two bulk endpoints, an IN * and an OUT. */ case USB_DESC_TYPE_ENDPOINT: { FAR struct usb_epdesc_s *epdesc = (FAR struct usb_epdesc_s *)configdesc; uvdbg("Endpoint descriptor\n"); DEBUGASSERT(remaining >= USB_SIZEOF_EPDESC); /* Check for a bulk endpoint. */ #warning "Review needed" /* For RTL8187B, the Linux driver hardcodes EP 3 for receiving and EP 2 for transmitting. * Otherwise, it uses EP 1 receiving and some other EP for transmitting (maybe 12). */ if ((epdesc->attr & USB_EP_ATTR_XFERTYPE_MASK) == USB_EP_ATTR_XFER_BULK) { /* Yes.. it is a bulk endpoint. IN or OUT? */ if (USB_ISEPOUT(epdesc->addr)) { /* It is an OUT bulk endpoint. There should be only one * bulk OUT endpoint. */ if ((found & USBHOST_BOUTFOUND) != 0) { /* Oops.. more than one endpoint. We don't know * what to do with this. */ return -EINVAL; } found |= USBHOST_BOUTFOUND; /* Save the bulk OUT endpoint information */ boutdesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK; boutdesc.in = false; boutdesc.funcaddr = funcaddr; boutdesc.xfrtype = USB_EP_ATTR_XFER_BULK; boutdesc.interval = epdesc->interval; boutdesc.mxpacketsize = rtl8187x_getle16(epdesc->mxpacketsize); uvdbg("Bulk OUT EP addr:%d mxpacketsize:%d\n", boutdesc.addr, boutdesc.mxpacketsize); } else { /* It is an IN bulk endpoint. There should be only one * bulk IN endpoint. */ if ((found & USBHOST_BINFOUND) != 0) { /* Oops.. more than one endpoint. We don't know * what to do with this. */ return -EINVAL; } found |= USBHOST_BINFOUND; /* Save the bulk IN endpoint information */ bindesc.addr = epdesc->addr & USB_EP_ADDR_NUMBER_MASK; bindesc.in = 1; bindesc.funcaddr = funcaddr; bindesc.xfrtype = USB_EP_ATTR_XFER_BULK; bindesc.interval = epdesc->interval; bindesc.mxpacketsize = rtl8187x_getle16(epdesc->mxpacketsize); uvdbg("Bulk IN EP addr:%d mxpacketsize:%d\n", bindesc.addr, bindesc.mxpacketsize); } } } break; /* Other descriptors are just ignored for now */ default: break; } /* If we found everything we need with this interface, then break out * of the loop early. */ if (found == USBHOST_ALLFOUND) { break; } /* Increment the address of the next descriptor */ configdesc += desc->len; remaining -= desc->len; } /* Sanity checking... did we find all of things that we need? */ if (found != USBHOST_ALLFOUND) { udbg("ERROR: Found IF:%s BIN:%s BOUT:%s\n", (found & USBHOST_IFFOUND) != 0 ? "YES" : "NO", (found & USBHOST_BINFOUND) != 0 ? "YES" : "NO", (found & USBHOST_BOUTFOUND) != 0 ? "YES" : "NO"); return -EINVAL; } /* We are good... Allocate the endpoints */ ret = DRVR_EPALLOC(priv->hcd, &boutdesc, &priv->epout); if (ret != OK) { udbg("ERROR: Failed to allocate Bulk OUT endpoint\n"); return ret; } ret = DRVR_EPALLOC(priv->hcd, &bindesc, &priv->epin); if (ret != OK) { udbg("ERROR: Failed to allocate Bulk IN endpoint\n"); (void)DRVR_EPFREE(priv->hcd, priv->epout); return ret; } uvdbg("Endpoints allocated\n"); return OK; } /**************************************************************************** * Name: rtl8187x_devinit * * Description: * The USB device has been successfully connected. This completes the * initialization operations. It is first called after the * configuration descriptor has been received. * * This function is called from the connect() method. This function always * executes on the thread of the caller of connect(). * * Input Parameters: * priv - A reference to the class instance. * * Returned Values: * None * ****************************************************************************/ static inline int rtl8187x_devinit(FAR struct rtl8187x_state_s *priv) { int ret = OK; /* Set aside a transfer buffer for exclusive use by the class driver */ /* Increment the reference count. This will prevent rtl8187x_destroy() from * being called asynchronously if the device is removed. */ priv->crefs++; DEBUGASSERT(priv->crefs == 2); /* Configure the device and register the network driver */ uvdbg("Register ethernet device\n"); ret = rtl8187x_netinitialize(priv); /* Check if we successfully initialized. We now have to be concerned * about asynchronous modification of crefs because the network * driver has been registered. */ if (ret == OK) { rtl8187x_takesem(&priv->exclsem); /* Decrement the reference count */ priv->crefs--; /* Handle a corner case where (1) open() has been called so the * reference count was > 2, but the device has been disconnected. * In this case, the class instance needs to persist until close() * is called. */ if (priv->crefs <= 1 && priv->disconnected) { /* The will cause the enumeration logic to disconnect * the class driver. */ ret = -ENODEV; } /* Release the semaphore... there is a race condition here. * Decrementing the reference count and releasing the semaphore * allows usbhost_destroy() to execute (on the worker thread); * the class driver instance could get destoryed before we are * ready to handle it! */ rtl8187x_givesem(&priv->exclsem); } return ret; } /**************************************************************************** * Name: rtl8187x_host2le16 and rtl8187x_host2le32 * * Description: * Convert a 16/32-bit value in host byte order to little endian byte order. * * Input Parameters: * val - A pointer to the first byte of the little endian value. * * Returned Values: * A uint16_t representing the whole 16-bit integer value * ****************************************************************************/ static inline uint16_t rtl8187x_host2le16(uint16_t val) { #ifdef CONFIG_ENDIAN_BIG uint16_t ret = ((val & 0x00ff) << 8) | ((val)) >> 8) & 0x00ff)) return ret #else return val; #endif } static inline uint16_t rtl8187x_le2host16(uint16_t val) { #ifdef CONFIG_ENDIAN_BIG uint16_t ret = ((val & 0x00ff) << 8) | ((val)) >> 8) & 0x00ff)) return ret #else return val; #endif } static inline uint32_t rtl8187x_host2le32(uint32_t val) { #ifdef CONFIG_ENDIAN_BIG uint32_t ret = ((val & 0x000000ffL) << 24) | ((val & 0x0000ff00L) << 8) | ((val & 0x00ff0000L) >> 8) | ((val & 0xff000000L) >> 24)) return ret #else return val; #endif } static inline uint32_t rtl8187x_le2host32(uint32_t val) { #ifdef CONFIG_ENDIAN_BIG uint32_t ret = ((val & 0x000000ffL) << 24) | ((val & 0x0000ff00L) << 8) | ((val & 0x00ff0000L) >> 8) | ((val & 0xff000000L) >> 24)) return ret #else return val; #endif } /**************************************************************************** * Name: rtl8187x_getle16 and rtl8187x_getle32 * * Description: * Get a (possibly unaligned) 16-bit little endian value. * * Input Parameters: * val - A pointer to the first byte of the little endian value. * * Returned Values: * A uint16_t representing the whole 16-bit integer value * ****************************************************************************/ static inline uint16_t rtl8187x_getle16(const uint8_t *val) { /* Little endian means LS byte first in byte stream */ return (uint16_t)val[1] << 8 | (uint16_t)val[0]; } static inline uint32_t rtl8187x_getle32(const uint8_t *val) { /* Little endian means LS halfword first in byte stream */ return (uint32_t)rtl8187x_getle16(&val[2]) << 16 | (uint32_t)rtl8187x_getle16(val); } /**************************************************************************** * Name: rtl8187x_putle16 and rtl8187x_putle32 * * Description: * Put a (possibly unaligned) 16/32-bit little endian value. * * Input Parameters: * dest - A pointer to the first byte to save the little endian value. * val - The 16-bit value to be saved. * * Returned Values: * None * ****************************************************************************/ static void rtl8187x_putle16(uint8_t *dest, uint16_t val) { /* Little endian means LS byte first in byte stream */ dest[0] = val & 0xff; /* Little endian means LS byte first in byte stream */ dest[1] = val >> 8; } static void rtl8187x_putle32(uint8_t *dest, uint32_t val) { /* Little endian means LS halfword first in byte stream */ rtl8187x_putle16(dest, (uint16_t)(val & 0xffff)); rtl8187x_putle16(dest+2, (uint16_t)(val >> 16)); } /**************************************************************************** * Name: rtl8187x_allocbuffers * * Description: * Allocate transfer buffer memory. * * Input Parameters: * priv - A reference to the class instance. * * Returned Values: * On sucess, zero (OK) is returned. On failure, an negated errno value * is returned to indicate the nature of the failure. * ****************************************************************************/ static inline int rtl8187x_allocbuffers(FAR struct rtl8187x_state_s *priv) { size_t buflen; int ret; DEBUGASSERT(priv && priv->ctrlreq == NULL && priv->tbuffer == NULL); /* Allocate TD buffers for use in this driver. We will need two: One for * the request and one for the data buffer. */ ret = DRVR_ALLOC(priv->hcd, (FAR uint8_t **)&priv->ctrlreq, &buflen); if (ret != OK) { uvdbg("DRVR_ALLOC(ctrlreq) failed: %d\n", ret); return ret; } ret = DRVR_ALLOC(priv->hcd, &priv->tbuffer, &priv->tbuflen); if (ret != OK) { uvdbg("DRVR_ALLOC(tbuffer) failed: %d\n", ret); return ret; } /* Allocate one TX I/O buffer big enough to hold one full packet plus * the TX descriptor. */ buflen = CONFIG_NET_BUFSIZE + 2 + SIZEOF_TXDESC; ret = DRVR_IOALLOC(priv->hcd, &priv->txbuffer, buflen); if (ret != OK) { uvdbg("DRVR_ALLOC(txbuffer) failed: %d\n", ret); } /* Allocate one RX I/O buffer big enough to hold one full packet plus * the RX descriptor. */ buflen = CONFIG_NET_BUFSIZE + 2 + SIZEOF_RXDESC; ret = DRVR_IOALLOC(priv->hcd, &priv->rxbuffer, buflen); if (ret != OK) { uvdbg("DRVR_ALLOC(rxbuffer) failed: %d\n", ret); } return ret; } /**************************************************************************** * Name: rtl8187x_freebuffers * * Description: * Free allocated buffer memory. * * Input Parameters: * priv - A reference to the class instance. * * Returned Values: * On sucess, zero (OK) is returned. On failure, an negated errno value * is returned to indicate the nature of the failure. * ****************************************************************************/ static inline int rtl8187x_freebuffers(FAR struct rtl8187x_state_s *priv) { DEBUGASSERT(priv); if (priv->ctrlreq) { DEBUGASSERT(priv->hcd && priv->tbuffer); /* Free the allocated control request */ (void)DRVR_FREE(priv->hcd, (FAR uint8_t *)priv->ctrlreq); priv->ctrlreq = NULL; /* Free the allocated buffer */ (void)DRVR_FREE(priv->hcd, priv->tbuffer); priv->tbuffer = NULL; priv->tbuflen = 0; } return OK; } /**************************************************************************** * struct usbhost_registry_s methods ****************************************************************************/ /**************************************************************************** * Name: rtl8187x_create * * Description: * This function implements the create() method of struct usbhost_registry_s. * The create() method is a callback into the class implementation. It is * used to (1) create a new instance of the USB host class state and to (2) * bind a USB host driver "session" to the class instance. Use of this * create() method will support environments where there may be multiple * USB ports and multiple USB devices simultaneously connected. * * Input Parameters: * hcd - An instance of struct usbhost_driver_s that the class * implementation will "bind" to its state structure and will * subsequently use to communicate with the USB host driver. * id - In the case where the device supports multiple base classes, * subclasses, or protocols, this specifies which to configure for. * * Returned Values: * On success, this function will return a non-NULL instance of struct * usbhost_class_s that can be used by the USB host driver to communicate * with the USB host class. NULL is returned on failure; this function * will fail only if the hcd input parameter is NULL or if there are * insufficient resources to create another USB host class instance. * ****************************************************************************/ static FAR struct usbhost_class_s *rtl8187x_create(FAR struct usbhost_driver_s *hcd, FAR const struct usbhost_id_s *id) { FAR struct rtl8187x_state_s *priv; int ret; DEBUGASSERT(hcd && id); /* Allocate a USB host class instance */ uvdbg("vid:%04x pid:%04x\n", id->vid, id->pid); priv = rtl8187x_allocclass(); if (priv) { /* Initialize the allocated storage class instance */ memset(priv, 0, sizeof(struct rtl8187x_state_s)); /* Initialize networking method function pointers */ priv->ethdev.d_ifup = rtl8187x_ifup; /* I/F down callback */ priv->ethdev.d_ifdown = rtl8187x_ifdown; /* I/F up (new IP address) callback */ priv->ethdev.d_txavail = rtl8187x_txavail; /* New TX data callback */ #ifdef CONFIG_NET_IGMP priv->ethdev.d_addmac = rtl8187x_addmac; /* Add multicast MAC address */ priv->ethdev.d_rmmac = rtl8187x_rmmac; /* Remove multicast MAC address */ #endif priv->ethdev.d_private = (void*)priv; /* Used to recover private state from dev */ /* Initialize class method function pointers */ priv->class.connect = rtl8187x_connect; priv->class.disconnected = rtl8187x_disconnected; /* Bind the host controller driver to the storage class instance */ priv->hcd = hcd; /* The initial reference count is 1... One reference is held by the * USB host controller driver */ priv->crefs = 1; /* Allocate buffering */ ret = rtl8187x_allocbuffers(priv); if (ret != OK) { udbg("ERROR: Failed to allocate buffers: %d\n", ret); goto errout; } /* Create a watchdog for timed polling for and timing of transfers */ priv->wdtxpoll = wd_create(); /* Create periodic TX poll timer */ priv->wdrxpoll = wd_create(); /* Create periodic RX poll timer */ /* Initialize semphores (this works okay in the interrupt context) */ sem_init(&priv->exclsem, 0, 1); /* Return the instance of the USB class driver */ return &priv->class; } /* An error occurred. Free the allocation and return NULL on all failures */ errout: if (priv) { rtl8187x_freeclass(priv); } return NULL; } /**************************************************************************** * struct usbhost_class_s methods ****************************************************************************/ /**************************************************************************** * Name: rtl8187x_connect * * Description: * This function implements the connect() method of struct * usbhost_class_s. This method is a callback into the class * implementation. It is used to provide the device's configuration * descriptor to the class so that the class may initialize properly * * Input Parameters: * class - The USB host class entry previously obtained from a call to create(). * configdesc - A pointer to a uint8_t buffer container the configuration descripor. * desclen - The length in bytes of the configuration descriptor. * funcaddr - The USB address of the function containing the endpoint that EP0 * controls * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value is * returned indicating the nature of the failure * * NOTE that the class instance remains valid upon return with a failure. It is * the responsibility of the higher level enumeration logic to call * CLASS_DISCONNECTED to free up the class driver resources. * * Assumptions: * - This function will *not* be called from an interrupt handler. * - If this function returns an error, the USB host controller driver * must call to DISCONNECTED method to recover from the error * ****************************************************************************/ static int rtl8187x_connect(FAR struct usbhost_class_s *class, FAR const uint8_t *configdesc, int desclen, uint8_t funcaddr) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)class; int ret; DEBUGASSERT(priv != NULL && configdesc != NULL && desclen >= sizeof(struct usb_cfgdesc_s)); /* Parse the configuration descriptor to get the endpoints */ ret = rtl8187x_cfgdesc(priv, configdesc, desclen, funcaddr); if (ret != OK) { udbg("rtl8187x_cfgdesc() failed: %d\n", ret); } else { /* Now configure the device and register the NuttX driver */ ret = rtl8187x_devinit(priv); if (ret != OK) { udbg("rtl8187x_devinit() failed: %d\n", ret); } } return ret; } /**************************************************************************** * Name: rtl8187x_disconnected * * Description: * This function implements the disconnected() method of struct * usbhost_class_s. This method is a callback into the class * implementation. It is used to inform the class that the USB device has * been disconnected. * * Input Parameters: * class - The USB host class entry previously obtained from a call to * create(). * * Returned Values: * On success, zero (OK) is returned. On a failure, a negated errno value * is returned indicating the nature of the failure * * Assumptions: * This function may be called from an interrupt handler. * ****************************************************************************/ static int rtl8187x_disconnected(struct usbhost_class_s *class) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)class; irqstate_t flags; DEBUGASSERT(priv != NULL); /* Set an indication to any users of the device that the device is no * longer available. */ flags = irqsave(); priv->disconnected = true; /* Now check the number of references on the class instance. The USB host * controller driver has just reliquished its reference. If the reference * count would go to zero, then we can free the class instance now. Otherwise, * we will have to wait until the holders of the references free them. */ ullvdbg("crefs: %d\n", priv->crefs); if (--priv->crefs <= 0) { /* Destroy the class instance. Defer the destruction to the worker thread. * (in case we were callded from an interrupt handler). */ ullvdbg("Queuing destruction: worker %p->%p\n", priv->wkdisconn.worker, rtl8187x_destroy); DEBUGASSERT(priv->wkdisconn.worker == NULL); (void)work_queue(HPWORK, &priv->wkdisconn, rtl8187x_destroy, priv, 0); } irqrestore(flags); return OK; } /**************************************************************************** * Function: rtl8187x_ioread8/16/32 * * Description: * Read 8, 16, or 32 bits from the RTL8187x. * * Parameters: * priv - Reference to the driver state structure * addr - Device addresses * * Returned Value: * The value read from the the RTL8187x (no error indication returned). * * Assumptions: * Not called from an interrupt handler. * ****************************************************************************/ static uint8_t rtl8187x_ioread8(struct rtl8187x_state_s *priv, uint16_t addr) { FAR struct usb_ctrlreq_s *ctrlreq = priv->ctrlreq; int ret; DEBUGASSERT(ctrlreq && priv->tbuffer); ctrlreq->type = (USB_REQ_DIR_IN | USB_REQ_TYPE_VENDOR | USB_REQ_RECIPIENT_DEVICE); ctrlreq->req = RTL8187X_REQ_GETREG; rtl8187x_putle16(ctrlreq->value, addr); rtl8187x_putle16(ctrlreq->index, 0); rtl8187x_putle16(ctrlreq->len, sizeof(uint8_t)); ret = DRVR_CTRLIN(priv->hcd, priv->ctrlreq, priv->tbuffer); if (ret != OK) { udbg("ERROR: DRVR_CTRLIN returned %d\n", ret); return 0; } return *((uint8_t*)priv->tbuffer); } static uint16_t rtl8187x_ioread16(struct rtl8187x_state_s*priv, uint16_t addr) { FAR struct usb_ctrlreq_s *ctrlreq = priv->ctrlreq; int ret; DEBUGASSERT(ctrlreq && priv->tbuffer); ctrlreq->type = (USB_REQ_DIR_IN | USB_REQ_TYPE_VENDOR | USB_REQ_RECIPIENT_DEVICE); ctrlreq->req = RTL8187X_REQ_GETREG; rtl8187x_putle16(ctrlreq->value, addr); rtl8187x_putle16(ctrlreq->index, 0); rtl8187x_putle16(ctrlreq->len, sizeof(uint16_t)); ret = DRVR_CTRLIN(priv->hcd, priv->ctrlreq, priv->tbuffer); if (ret != OK) { udbg("ERROR: DRVR_CTRLIN returned %d\n", ret); return 0; } return rtl8187x_getle16(priv->tbuffer); } static uint32_t rtl8187x_ioread32(struct rtl8187x_state_s*priv, uint16_t addr) { FAR struct usb_ctrlreq_s *ctrlreq = priv->ctrlreq; int ret; DEBUGASSERT(ctrlreq && priv->tbuffer); ctrlreq->type = (USB_REQ_DIR_IN | USB_REQ_TYPE_VENDOR | USB_REQ_RECIPIENT_DEVICE); ctrlreq->req = RTL8187X_REQ_GETREG; rtl8187x_putle16(ctrlreq->value, addr); rtl8187x_putle16(ctrlreq->index, 0); rtl8187x_putle16(ctrlreq->len, sizeof(uint32_t)); ret = DRVR_CTRLIN(priv->hcd, priv->ctrlreq, priv->tbuffer); if (ret != OK) { udbg("ERROR: DRVR_CTRLIN returned %d\n", ret); return 0; } return rtl8187x_getle32(priv->tbuffer); } /**************************************************************************** * Function: rtl8187x_iowrite8/16/32 * * Description: * Write a 8, 16, or 32 bits to the RTL8187x. * * Parameters: * priv - Reference to the driver state structure * addr - Device addresses * val - The value to write * * Returned Value: * OK on success; a negated errno on failure * * Assumptions: * Not called from an interrupt handler. * ****************************************************************************/ static int rtl8187x_iowrite8(struct rtl8187x_state_s *priv, uint16_t addr, uint8_t val) { FAR struct usb_ctrlreq_s *ctrlreq = priv->ctrlreq; DEBUGASSERT(ctrlreq && priv->tbuffer); ctrlreq->type = (USB_REQ_DIR_OUT | USB_REQ_TYPE_VENDOR | USB_REQ_RECIPIENT_DEVICE); ctrlreq->req = RTL8187X_REQ_SETREG; rtl8187x_putle16(ctrlreq->value, addr); rtl8187x_putle16(ctrlreq->index, 0); rtl8187x_putle16(ctrlreq->len, sizeof(uint8_t)); priv->tbuffer[0] = val; return DRVR_CTRLOUT(priv->hcd, priv->ctrlreq, priv->tbuffer); } static int rtl8187x_iowrite16(struct rtl8187x_state_s *priv, uint16_t addr, uint16_t val) { FAR struct usb_ctrlreq_s *ctrlreq = priv->ctrlreq; DEBUGASSERT(ctrlreq && priv->tbuffer); ctrlreq->type = (USB_REQ_DIR_OUT | USB_REQ_TYPE_VENDOR | USB_REQ_RECIPIENT_DEVICE); ctrlreq->req = RTL8187X_REQ_SETREG; rtl8187x_putle16(ctrlreq->value, addr); rtl8187x_putle16(ctrlreq->index, 0); rtl8187x_putle16(ctrlreq->len, sizeof(uint16_t)); rtl8187x_putle16(priv->tbuffer, val); return DRVR_CTRLOUT(priv->hcd, priv->ctrlreq, priv->tbuffer); } static int rtl8187x_iowrite32(struct rtl8187x_state_s *priv, uint16_t addr, uint32_t val) { FAR struct usb_ctrlreq_s *ctrlreq = priv->ctrlreq; DEBUGASSERT(ctrlreq && priv->tbuffer); ctrlreq->type = (USB_REQ_DIR_OUT | USB_REQ_TYPE_VENDOR | USB_REQ_RECIPIENT_DEVICE); ctrlreq->req = RTL8187X_REQ_SETREG; rtl8187x_putle16(ctrlreq->value, addr); rtl8187x_putle16(ctrlreq->index, 0); rtl8187x_putle16(ctrlreq->len, sizeof(uint32_t)); rtl8187x_putle32(priv->tbuffer, val); return DRVR_CTRLOUT(priv->hcd, priv->ctrlreq, priv->tbuffer); } /**************************************************************************** * Function: rtl8187x_read * * Description: * Read RTL register value * * Parameters: * priv - Reference to the driver state structure * addr - Register address * * Returned Value: * Value * ****************************************************************************/ static uint16_t rtl8187x_read(FAR struct rtl8187x_state_s *priv, uint8_t addr) { uint16_t reg80; uint16_t reg82; uint16_t reg84; uint16_t ret; int i; reg80 = rtl8187x_ioread16(priv, RTL8187X_ADDR_RFPINSOUTPUT); reg82 = rtl8187x_ioread16(priv, RTL8187X_ADDR_RFPINSENABLE); reg84 = rtl8187x_ioread16(priv, RTL8187X_ADDR_RFPINSSELECT); reg80 &= ~0xf; rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSENABLE, reg82 | 0x000f); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, reg84 | 0x000f); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 2)); usleep(4); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80); usleep(5); for (i = 4; i >= 0; i--) { uint16_t reg = reg80 | ((addr >> i) & 1); if (!(i & 1)) { rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg); usleep(1); } rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg | (1 << 1)); usleep(2); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg | (1 << 1)); usleep(2); if (i & 1) { rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg); usleep(1); } } rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 3) | (1 << 1)); usleep(2); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 3)); usleep(2); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 3)); usleep(2); ret = 0; for (i = 11; i >= 0; i--) { rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 3)); usleep(1); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 3) | (1 << 1)); usleep(2); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 3) | (1 << 1)); usleep(2); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 3) | (1 << 1)); usleep(2); if (rtl8187x_ioread16(priv, RTL8187X_ADDR_RFPINSINPUT) & (1 << 1)) ret |= 1 << i; rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 3)); usleep(2); } rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 3) | (1 << 2)); usleep(2); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSENABLE, reg82); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, reg84); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, 0x03a0); return ret; } /**************************************************************************** * Function: rtl8187x_write * * Description: * Write RTL register value * * Parameters: * priv - Reference to the driver state structure * addr - Register address * * Returned Value: * Value * ****************************************************************************/ static inline void rtl8187x_write_8051(FAR struct rtl8187x_state_s *priv, uint8_t addr, uint16_t data) { struct usb_ctrlreq_s *ctrlreq; uint16_t reg80; uint16_t reg82; uint16_t reg84; int ret; reg80 = rtl8187x_ioread16(priv, RTL8187X_ADDR_RFPINSOUTPUT); reg82 = rtl8187x_ioread16(priv, RTL8187X_ADDR_RFPINSENABLE); reg84 = rtl8187x_ioread16(priv, RTL8187X_ADDR_RFPINSSELECT); reg80 &= ~(0x3 << 2); reg84 &= ~0xf; rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSENABLE, reg82 | 0x0007); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, reg84 | 0x0007); usleep(10); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 2)); usleep(2); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80); usleep(10); ctrlreq = priv->ctrlreq; ctrlreq->type = (USB_REQ_DIR_OUT | USB_REQ_TYPE_VENDOR | USB_REQ_RECIPIENT_DEVICE); ctrlreq->req = RTL8187X_REQ_GETREG; rtl8187x_putle16(ctrlreq->value, addr); rtl8187x_putle16(ctrlreq->index, 0x8225); rtl8187x_putle16(ctrlreq->len, sizeof(uint16_t)); rtl8187x_putle16(priv->tbuffer, data); ret = DRVR_CTRLOUT(priv->hcd, priv->ctrlreq, priv->tbuffer); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 2)); usleep(10); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 2)); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, reg84); usleep(2000); } static inline void rtl8187x_write_bitbang(struct rtl8187x_state_s *priv, uint8_t addr, uint16_t data) { uint16_t reg80, reg84, reg82; uint32_t bangdata; int i; bangdata = (data << 4) | (addr & 0xf); reg80 = rtl8187x_ioread16(priv, RTL8187X_ADDR_RFPINSOUTPUT) & 0xfff3; reg82 = rtl8187x_ioread16(priv, RTL8187X_ADDR_RFPINSENABLE); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSENABLE, reg82 | 0x7); reg84 = rtl8187x_ioread16(priv, RTL8187X_ADDR_RFPINSSELECT); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, reg84 | 0x7); usleep(10); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 2)); usleep(2); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80); usleep(10); for (i = 15; i >= 0; i--) { uint16_t reg = reg80 | (bangdata & (1 << i)) >> i; if (i & 1) rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg | (1 << 1)); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg | (1 << 1)); if (!(i & 1)) rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg); } rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 2)); usleep(10); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, reg80 | (1 << 2)); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, reg84); usleep(2); } static void rtl8187x_write(FAR struct rtl8187x_state_s *priv, uint8_t addr, uint16_t data) { if (priv->asicrev) { rtl8187x_write_8051(priv, addr, rtl8187x_host2le16(data)); } else { rtl8187x_write_bitbang(priv, addr, data); } } /**************************************************************************** * Function: rtl8187x_transmit * * Description: * Start hardware transmission. Called either from the watchdog based * polling or to send a response to an incoming packet. * * Parameters: * priv - Reference to the driver state structure * * Returned Value: * OK on success; a negated errno on failure * * Assumptions: * May or may not be called from an interrupt handler. In either case, * global interrupts are disabled, either explicitly or indirectly through * interrupt handling logic. * ****************************************************************************/ static int rtl8187x_transmit(FAR struct rtl8187x_state_s *priv) { int ret; /* Get exclusive access to the USB controller interface */ DEBUGASSERT(priv && priv->txbuffer); rtl8187x_takesem(&priv->exclsem); /* Check if the RTL8187 is still connected */ if (priv->disconnected) { /* No... the wan driver is no longer bound to the class. That means that * the USB device is no longer connected. Refuse any attempt to write to * the device. */ ret = -ENODEV; } else if (!priv->bifup) { /* The interface is not up. */ ret = -EAGAIN; } else { FAR struct rtl8187x_txdesc_s *txdesc = (FAR struct rtl8187x_txdesc_s *)priv->txbuffer; unsigned int datlen = priv->ethdev.d_len; uint32_t flags; uint32_t retry; /* Increment statistics */ RTL8187X_STATS(priv, transmitted); /* Construct the TX descriptor at the beginning of the IO buffer. This * memory was previously reserved just for this use. */ flags = datlen | RTL8187X_TXDESC_FLAG_NOENC | RTL8187X_RATE_11 << 24; txdesc->flags = rtl8187x_host2le32(flags); txdesc->rtsduration = 0; txdesc->len = 0; retry = rtl8187x_host2le32(3) | /* CWMIN */ (7 << 4) | /* CMAX */ (0 << 8); /* retry lim */ txdesc->retry = rtl8187x_host2le32(retry); #ifdef CONFIG_RTL8187B #warning "This number is bogus" txdesc->txduration = 40; #endif /* And transfer the packet */ ret = DRVR_TRANSFER(priv->hcd, priv->epout, priv->txbuffer, datlen + SIZEOF_TXDESC); if (ret != OK) { RTL8187X_STATS(priv, txfailed); } } rtl8187x_givesem(&priv->exclsem); return ret; } /**************************************************************************** * Function: rtl8187x_txpoll * * Description: * The transmitter is available, check if uIP has any outgoing packets ready * to send. This is a callback from devif_poll(). devif_poll() may be called: * * 1. When the preceding TX packet send is complete, * 2. When the preceding TX packet send timesout and the interface is reset * 3. During normal TX polling * * Parameters: * dev - Reference to the NuttX driver state structure * * Returned Value: * OK on success; a negated errno on failure * * Assumptions: * Never called from an interrupt handler. The polling process was * initiated by a normal thread (possibly the worker thread). The initiator * has called net_lock() to assure that we have exclusive access to uIP. * ****************************************************************************/ static int rtl8187x_txpoll(struct net_driver_s *dev) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)dev->d_private; /* 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->ethdev.d_len > 0) { arp_out(&priv->ethdev); rtl8187x_transmit(priv); } /* If zero is returned, the polling will continue until all connections have * been examined. */ return 0; } /**************************************************************************** * Function: rtl8187x_txpollwork * * Description: * Periodic timer handler. The poll occurs on the worker thread. The * poll work was scheduled by rtl8187x_txpolltimer when the poll timer * expired. * * Parameters: * arg - The passed argument (priv) * * Returned Value: * None * * Assumptions: * Global interrupts are disabled by the watchdog logic. * ****************************************************************************/ static void rtl8187x_txpollwork(FAR void *arg) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)arg; /* Verify that the RTL8187 is still connected and that the interface is up */ if (!priv->disconnected && priv->bifup) { net_lock_t lock; uint32_t now; uint32_t hsecs; /* Get exclusive access to uIP */ lock = net_lock(); /* Estimate the elapsed time in hsecs since the last poll */ now = g_system_timer; hsecs = (priv->lastpoll - now + CLK_TCK / 4) / (CLK_TCK / 2); priv->lastpoll = now; /* Update TCP timing states and poll uIP for new XMIT data. Pass an * offset address into the txbuffer, reserving space for the TX * descriptor at the beginning of the buffer. */ priv->ethdev.d_buf = &priv->txbuffer[SIZEOF_TXDESC]; (void)devif_timer(&priv->ethdev, rtl8187x_txpoll, (int)hsecs); net_unlock(lock); } } /**************************************************************************** * Function: rtl8187x_txpolltimer * * Description: * Periodic timer handler. Called from the timer interrupt handler. The * actually polling is performed by on the work threader thread by * rtl8187x_txpollwork(). * * Parameters: * argc - The number of available arguments * arg - The first argument * * Returned Value: * None * * Assumptions: * Global interrupts are disabled by the watchdog logic. * ****************************************************************************/ static void rtl8187x_txpolltimer(int argc, uint32_t arg, ...) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)arg; uint32_t delay = RTL8187X_TXDELAY; /* Verify that the RTL8187 is still connected and that the interface is up */ if (!priv->disconnected && priv->bifup) { /* Check for over-run... What if the last queued poll work has not yet ran? * That could be the case if the system were too busy, if we are polling to * rapidly, or if something is hung. */ if (priv->wktxpoll.worker != NULL) { ulldbg("ERROR: TX work overrun!\n"); delay = RTL8187X_RETRYDELAY; } else { (void)work_queue(HPWORK, &priv->wktxpoll, rtl8187x_txpollwork, priv, 0); } } /* Setup the watchdog poll timer again -- possibly using a shorter retry delay */ (void)wd_start(priv->wdtxpoll, delay, rtl8187x_txpolltimer, 1, arg); } /**************************************************************************** * Function: rtl8187x_receive * * Description: * Called upon receipt of a new USB packet on the epin endpoint. Analyzes * the RX header in priv->rxbuffer and returns the packet length * * Parameters: * priv - Reference to the driver state structure * iolen - The size of the received USB packet * pktlen - The returned size of the packet * * Returned Value: * None * * Assumptions: * The caller holds the exclsem and has exclusive access to the USB * interface. * ****************************************************************************/ static inline int rtl8187x_receive(FAR struct rtl8187x_state_s *priv, unsigned int iolen, unsigned int *pktlen) { struct rtl8187x_rxdesc_s *rxdesc; uint32_t flags; uint16_t rxlen; int signal; /* Increment statistics */ RTL8187X_STATS(priv, received); /* Make sure that the packet was large enough to contain an RX descriptor * and an Ethernet header */ if (iolen < NET_LLH_LEN + SIZEOF_RXDESC) { RTL8187X_STATS(priv, rxtoosmall); RTL8187X_STATS(priv, rxdropped); return -EINVAL; } /* The RX descriptor lies at the end of the IO buffer */ rxdesc = (struct rtl8187x_rxdesc_s *)(priv->rxbuffer + (iolen - SIZEOF_RXDESC)); flags = rtl8187x_le2host32(rxdesc->flags); if (flags & RTL8187X_RXDESC_FLAG_CRC32ERR) { udbg("Bad CRC\n"); RTL8187X_STATS(priv, rxcrcerr); RTL8187X_STATS(priv, rxdropped); return -EINVAL; } /* Get the actual packet length and rate from the RX descriptor flags */ rxlen = (flags & 0xfff) - 4; priv->rate = (flags >> 20) & 0xf; /* Perform signal strength calculation */ #ifdef CONFIG_RTL8187B /* Linux has this: * signal = -4 - ((27 * hdr->agc) >> 6); * antenna = (hdr->signal >> 7) & 1; * mactime = le64_to_cpu(hdr->mac_time) * Otherwise * signal = 14 - hdr->agc / 2; * antenna = (hdr->rssi >> 7) & 1; * mactime = le64_to_cpu(hdr->mac_time */ #warning "Signal computations must change for RTL8187B" #endif signal = rxdesc->agc >> 1; if (priv->rate) { /* OFDM rate */ if (signal > 90) { signal = 90; } else if (signal < 25) { signal = 25; } signal = 90 - signal; } else { /* CCK rate */ if (signal > 95) { signal = 95; } else if (signal < 30) { signal = 30; } signal = 95 - signal; } /* Signal strength: (100.0 / 65.0) * signal */ priv->signal = (uint8_t) (20 * signal + 7) / 13; priv->silence = false; /* Check if uIP is configured to handle a packet of this size */ if (iolen > CONFIG_NET_BUFSIZE + SIZEOF_RXDESC + 2) { RTL8187X_STATS(priv, rxtoobig); RTL8187X_STATS(priv, rxdropped); return -EINVAL; } /* Return the packet length. This is the length of the value packet * data at the beginning of the buffer (we no longer need the RX descriptor) */ *pktlen = rxlen; return OK; } /**************************************************************************** * Function: rtl8187x_rxdispatch * * Description: * Analyzes the ethernet header of the received packet (in priv->rxbuffer) * and fowards valid Ethernet packets to uIP. * * Parameters: * priv - Reference to the driver state structure * pktlen - Returned size of the packet * * Returned Value: * None * * Assumptions: * Global interrupts are disabled by interrupt handling logic. * ****************************************************************************/ static inline void rtl8187x_rxdispatch(FAR struct rtl8187x_state_s *priv, unsigned int pktlen) { FAR struct eth_hdr_s *ethhdr = (FAR struct eth_hdr_s *)priv->rxbuffer; net_lock_t lock; /* Get exclusive access to uIP */ lock = net_lock(); priv->ethdev.d_buf = priv->rxbuffer; priv->ethdev.d_len = pktlen; /* We only accept IP packets of the configured type and ARP packets */ #ifdef CONFIG_NET_IPv6 if (ethhdr->type == HTONS(ETHTYPE_IP6)) #else if (ethhdr->type == HTONS(ETHTYPE_IP)) #endif { RTL8187X_STATS(priv, rxippackets); arp_ipin(&priv->ethdev); devif_input(&priv->ethdev); /* 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->ethdev.d_len > 0) { arp_out(&priv->ethdev); rtl8187x_transmit(priv); } } else if (ethhdr->type == htons(ETHTYPE_ARP)) { RTL8187X_STATS(priv, rxarppackets); arp_arpin(&priv->ethdev); /* 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->ethdev.d_len > 0) { rtl8187x_transmit(priv); } } else { RTL8187X_STATS(priv, rxbadproto); RTL8187X_STATS(priv, rxdropped); } net_unlock(lock); } /**************************************************************************** * Function: rtl8187x_rxpollwork * * Description: * Periodic timer handler. The poll occurs on the worker thread. The * poll work was scheduled by rtl8187x_rxpolltimer when the poll timer * expired. * * Parameters: * arg - The passed argument (priv) * * Returned Value: * None * * Assumptions: * Global interrupts are disabled by the watchdog logic. * ****************************************************************************/ static void rtl8187x_rxpollwork(FAR void *arg) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)arg; /* Get exclusive access to the USB controller interface and the device * structure */ DEBUGASSERT(priv && priv->rxbuffer); rtl8187x_takesem(&priv->exclsem); /* Verify that the RTL8187 is still connected and that the interface is up */ if (!priv->disconnected && priv->bifup) { unsigned int iolen; int ret; /* Attempt to read from the bulkin endpoint */ ret = DRVR_TRANSFER(priv->hcd, priv->epin, priv->rxbuffer, CONFIG_NET_BUFSIZE + SIZEOF_RXDESC + 2); /* How dow we get the length of the transfer? */ #warning "Missing logic" iolen = 0; if (ret == OK) { unsigned int pktlen; /* Analyze the packet and copy it into the RX buffer */ ret = rtl8187x_receive(priv, iolen, &pktlen); if (ret == OK) { /* Now we can relinquish the USB interface and device * structure. */ rtl8187x_givesem(&priv->exclsem); /* Send the packet to uIP */ rtl8187x_rxdispatch(priv, pktlen); return; } } } rtl8187x_givesem(&priv->exclsem); } /**************************************************************************** * Function: rtl8187x_rxpolltimer * * Description: * Periodic timer handler. Called from the timer interrupt handler. The * actually polling is performed by on the work threader thread by * rtl8187x_rxpollwork(). * * Parameters: * argc - The number of available arguments * arg - The first argument * * Returned Value: * None * * Assumptions: * Global interrupts are disabled by the watchdog logic. * ****************************************************************************/ static void rtl8187x_rxpolltimer(int argc, uint32_t arg, ...) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)arg; /* Verify that the RTL8187 is still connected and that the interface is up */ if (!priv->disconnected && priv->bifup) { /* Check for over-run... What if the last queued poll work has not yet ran? * That could be the case if the system were too busy, if we are polling to * rapidly, or if something is hung. */ if (priv->wkrxpoll.worker != NULL) { ulldbg("ERROR: RX work overrun!\n"); } else { (void)work_queue(HPWORK, &priv->wkrxpoll, rtl8187x_rxpollwork, priv, 0); } } /* Setup the watchdog poll timer again -- possibly using a shorter retry delay */ (void)wd_start(priv->wdrxpoll, RTL8187X_RXDELAY, rtl8187x_rxpolltimer, 1, arg); } /**************************************************************************** * Function: rtl8187x_ifup * * Description: * NuttX Callback: Bring up the WLAN interface when an IP address is * provided * * Parameters: * dev - Reference to the NuttX driver state structure * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static int rtl8187x_ifup(struct net_driver_s *dev) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)dev->d_private; int ret; ndbg("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 ); /* Initialize PHYs and the WLAN interface */ ret = rtl8187x_start(priv); if (ret == OK) { /* Set up and activate TX timer processes */ (void)wd_start(priv->wdtxpoll, RTL8187X_TXDELAY, rtl8187x_txpolltimer, 1, (uint32_t)priv); priv->lastpoll = g_system_timer; /* Set up and activate RX timer processes */ (void)wd_start(priv->wdrxpoll, RTL8187X_RXDELAY, rtl8187x_rxpolltimer, 1, (uint32_t)priv); /* Mark the interface as up. */ priv->bifup = true; } return ret; } /**************************************************************************** * Function: rtl8187x_ifdown * * Description: * NuttX Callback: Stop the interface. * * Parameters: * dev - Reference to the NuttX driver state structure * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static int rtl8187x_ifdown(struct net_driver_s *dev) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)dev->d_private; irqstate_t flags; /* Cancel the TX and RX poll timer. */ flags = irqsave(); wd_cancel(priv->wdtxpoll); wd_cancel(priv->wdrxpoll); /* Put the the EMAC is its non-operational state. This should be a known * configuration that will guarantee the rtl8187x_ifup() always successfully * successfully brings the interface back up. */ rtl8187x_stop(priv); /* Mark the device "down" */ priv->bifup = false; irqrestore(flags); return OK; } /**************************************************************************** * Function: rtl8187x_txavail * * Description: * Driver callback invoked when new TX data is available. This is a * stimulus perform an out-of-cycle poll and, thereby, reduce the TX * latency. * * Parameters: * dev - Reference to the NuttX driver state structure * * Returned Value: * None * * Assumptions: * Called in normal user mode * ****************************************************************************/ static int rtl8187x_txavail(struct net_driver_s *dev) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)dev->d_private; /* Ignore the notification if the interface is not yet up */ if (priv->bifup) { net_lock_t lock; /* If so, then poll uIP for new XMIT data. Pass the offset address * into the txbuffer, reserving space for the TX descriptor at the * beginning of the buffer. */ lock = net_lock(); priv->ethdev.d_buf = &priv->txbuffer[SIZEOF_TXDESC]; (void)devif_poll(&priv->ethdev, rtl8187x_txpoll); net_unlock(lock); } return OK; } /**************************************************************************** * Function: rtl8187x_addmac * * Description: * NuttX Callback: Add the specified MAC address to the hardware multicast * address filtering * * Parameters: * dev - Reference to the NuttX driver state structure * mac - The MAC address to be added * * Returned Value: * None * * Assumptions: * ****************************************************************************/ #ifdef CONFIG_NET_IGMP static int rtl8187x_addmac(struct net_driver_s *dev, FAR const uint8_t *mac) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)dev->d_private; /* Add the MAC address to the hardware multicast routing table */ return OK; } #endif /**************************************************************************** * Function: rtl8187x_rmmac * * Description: * NuttX Callback: Remove the specified MAC address from the hardware multicast * address filtering * * Parameters: * dev - Reference to the NuttX driver state structure * mac - The MAC address to be removed * * Returned Value: * None * * Assumptions: * ****************************************************************************/ #ifdef CONFIG_NET_IGMP static int rtl8187x_rmmac(struct net_driver_s *dev, FAR const uint8_t *mac) { FAR struct rtl8187x_state_s *priv = (FAR struct rtl8187x_state_s *)dev->d_private; /* Add the MAC address to the hardware multicast routing table */ return OK; } #endif /**************************************************************************** * Function: Various low-level EEPROM Support functions * * Description: * NuttX Callback: Remove the specified MAC address from the hardware multicast * address filtering * * Parameters: * priv - Reference to the NuttX driver state structure * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static inline void rtl8187x_eeprom_pulsehigh(FAR struct rtl8187x_state_s *priv) { priv->dataclk = 1; rtl8187x_eeprom_wrsetup(priv); /* Add a short delay for the pulse to work. According to the specifications * the "maximum minimum" time should be 450ns. */ usleep(1); } static inline void rtl8187x_eeprom_pulselow(FAR struct rtl8187x_state_s *priv) { priv->dataclk = 0; rtl8187x_eeprom_wrsetup(priv); /* Add a short delay for the pulse to work. According to the specifications * the "maximum minimum" time should be 450ns. */ usleep(1); } static void rtl8187x_eeprom_rdsetup(FAR struct rtl8187x_state_s *priv) { uint8_t reg = rtl8187x_ioread8(priv, RTL8187X_ADDR_EEPROMCMD); priv->datain = reg & RTL8187X_EEPROMCMD_WRITE; priv->dataout = reg & RTL8187X_EEPROMCMD_READ; priv->dataclk = reg & RTL8187X_EEPROMCMD_CK; priv->chipsel = reg & RTL8187X_EEPROMCMD_CS; } static void rtl8187x_eeprom_wrsetup(FAR struct rtl8187x_state_s *priv) { uint8_t reg = RTL8187X_EEPROMCMD_PROGRAM; if (priv->datain) { reg |= RTL8187X_EEPROMCMD_WRITE; } if (priv->dataout) { reg |= RTL8187X_EEPROMCMD_READ; } if (priv->dataclk) { reg |= RTL8187X_EEPROMCMD_CK; } if (priv->chipsel) { reg |= RTL8187X_EEPROMCMD_CS; } rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, reg); usleep(10); } static void rtl8187x_eeprom_cleanup(FAR struct rtl8187x_state_s *priv) { /* Clear chip_select and data_in flags. */ rtl8187x_eeprom_rdsetup(priv); priv->datain = 0; priv->chipsel = 0; rtl8187x_eeprom_wrsetup(priv); /* Kick a pulse. */ rtl8187x_eeprom_pulsehigh(priv); rtl8187x_eeprom_pulselow(priv); } static void rtl8187x_eeprom_wrbits(FAR struct rtl8187x_state_s *priv, uint16_t data, uint16_t count) { unsigned int i; rtl8187x_eeprom_rdsetup(priv); /* Clear data flags. */ priv->datain = 0; priv->dataout = 0; /* Start writing all bits. */ for (i = count; i > 0; i--) { /* Check if this bit needs to be set. */ priv->datain = ! !(data & (1 << (i - 1))); /* Write the bit to the eeprom register. */ rtl8187x_eeprom_wrsetup(priv); /* Kick a pulse. */ rtl8187x_eeprom_pulsehigh(priv); rtl8187x_eeprom_pulselow(priv); } priv->datain = 0; rtl8187x_eeprom_wrsetup(priv); } static void rtl8187x_eeprom_rdbits(FAR struct rtl8187x_state_s *priv, FAR uint16_t * data, uint16_t count) { unsigned int i; uint16_t buf = 0; rtl8187x_eeprom_rdsetup(priv); /* Clear data flags. */ priv->datain = 0; priv->dataout = 0; /* Start reading all bits. */ for (i = count; i > 0; i--) { rtl8187x_eeprom_pulsehigh(priv); rtl8187x_eeprom_rdsetup(priv); /* Clear data_in flag. */ priv->datain = 0; /* Read if the bit has been set. */ if (priv->dataout) { buf |= (1 << (i - 1)); } rtl8187x_eeprom_pulselow(priv); } *data = buf; } /**************************************************************************** * Function: rtl8187x_eeprom_read * * Description: * Read data from EEPROM * * Parameters: * priv - Reference to the NuttX driver state structure * word - * data - Location for data to be written * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static void rtl8187x_eeprom_read(FAR struct rtl8187x_state_s *priv, uint8_t word, uint16_t *data) { uint16_t command; /* Clear all flags, and enable chip select. */ rtl8187x_eeprom_rdsetup(priv); priv->datain = 0; priv->dataout = 0; priv->dataclk = 0; priv->chipsel = 1; rtl8187x_eeprom_wrsetup(priv); /* Kick a pulse. */ rtl8187x_eeprom_pulsehigh(priv); rtl8187x_eeprom_pulselow(priv); /* Select the read opcode and the word to be read. */ command = (PCI_EEPROM_READ_OPCODE << priv->width) | word; rtl8187x_eeprom_wrbits(priv, command, PCI_EEPROM_WIDTH_OPCODE + priv->width); /* Read the requested 16 bits. */ rtl8187x_eeprom_rdbits(priv, data, 16); /* Cleanup eeprom register. */ rtl8187x_eeprom_cleanup(priv); } /**************************************************************************** * Function: rtl8187x_eeprom_multiread * * Description: * A convenience wrapper around rtl8187x_eeprom_read to obtain multiple * words from the EEPROM. * * Parameters: * priv - Reference to the NuttX driver state structure * word - * data - Location for data to be written * nwords - The number of words to be read * * Returned Value: * None * * Assumptions: * ****************************************************************************/ static void rtl8187x_eeprom_multiread(FAR struct rtl8187x_state_s *priv, uint8_t word, FAR uint16_t *data, uint16_t nwords) { unsigned int i; uint16_t tmp; for (i = 0; i < nwords; i++) { tmp = 0; rtl8187x_eeprom_read(priv, word + i, &tmp); data[i] = rtl8187x_host2le16(tmp); } } /**************************************************************************** * Function: PHY support functions * * Description: * Configure PHY * * Parameters: * priv - Private driver state information * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static void rtl8187x_wrphy(FAR struct rtl8187x_state_s *priv, uint8_t addr, uint32_t data) { data <<= 8; data |= addr | 0x80; rtl8187x_iowrite8(priv, RTL8187X_ADDR_PHY3, (data >> 24) & 0xff); rtl8187x_iowrite8(priv, RTL8187X_ADDR_PHY2, (data >> 16) & 0xff); rtl8187x_iowrite8(priv, RTL8187X_ADDR_PHY1, (data >> 8) & 0xff); rtl8187x_iowrite8(priv, RTL8187X_ADDR_PHY0, data & 0xff); usleep(1000); } static inline void rtl8187x_wrphyofdm(FAR struct rtl8187x_state_s *priv, uint8_t addr, uint32_t data) { rtl8187x_wrphy(priv, addr, data); } static inline void rtl8187x_wrphycck(FAR struct rtl8187x_state_s *priv, uint8_t addr, uint32_t data) { rtl8187x_wrphy(priv, addr, data | 0x10000); } /**************************************************************************** * Function: rtl8225_rfinit and rtl8225z2_rfinit * * Description: * Chip-specific RF initialization * * Parameters: * priv - Private driver state information * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static void rtl8225_rfinit(FAR struct rtl8187x_state_s *priv) { unsigned int i; uvdbg("rfinit"); rtl8187x_write(priv, 0x0, 0x067); usleep(1000); rtl8187x_write(priv, 0x1, 0xFE0); usleep(1000); rtl8187x_write(priv, 0x2, 0x44D); usleep(1000); rtl8187x_write(priv, 0x3, 0x441); usleep(1000); rtl8187x_write(priv, 0x4, 0x486); usleep(1000); rtl8187x_write(priv, 0x5, 0xBC0); usleep(1000); rtl8187x_write(priv, 0x6, 0xAE6); usleep(1000); rtl8187x_write(priv, 0x7, 0x82A); usleep(1000); rtl8187x_write(priv, 0x8, 0x01F); usleep(1000); rtl8187x_write(priv, 0x9, 0x334); usleep(1000); rtl8187x_write(priv, 0xA, 0xFD4); usleep(1000); rtl8187x_write(priv, 0xB, 0x391); usleep(1000); rtl8187x_write(priv, 0xC, 0x050); usleep(1000); rtl8187x_write(priv, 0xD, 0x6DB); usleep(1000); rtl8187x_write(priv, 0xE, 0x029); usleep(1000); rtl8187x_write(priv, 0xF, 0x914); usleep(100000); rtl8187x_write(priv, 0x2, 0xC4D); usleep(200000); rtl8187x_write(priv, 0x2, 0x44D); usleep(200000); if (!(rtl8187x_read(priv, 6) & (1 << 7))) { rtl8187x_write(priv, 0x02, 0x0c4d); usleep(200000); rtl8187x_write(priv, 0x02, 0x044d); usleep(100000); if (!(rtl8187x_read(priv, 6) & (1 << 7))) { udbg("RF Calibration Failed! %x\n", rtl8187x_read(priv, 6)); } } rtl8187x_write(priv, 0x0, 0x127); for (i = 0; i < ARRAY_SIZE(g_rtl8225bcd_rxgain); i++) { rtl8187x_write(priv, 0x1, i + 1); rtl8187x_write(priv, 0x2, g_rtl8225bcd_rxgain[i]); } rtl8187x_write(priv, 0x0, 0x027); rtl8187x_write(priv, 0x0, 0x22F); for (i = 0; i < ARRAY_SIZE(g_rtl8225_agc); i++) { rtl8187x_wrphyofdm(priv, 0xB, g_rtl8225_agc[i]); usleep(1000); rtl8187x_wrphyofdm(priv, 0xA, 0x80 + i); usleep(1000); } usleep(1000); rtl8187x_wrphyofdm(priv, 0x00, 0x01); usleep(1000); rtl8187x_wrphyofdm(priv, 0x01, 0x02); usleep(1000); rtl8187x_wrphyofdm(priv, 0x02, 0x42); usleep(1000); rtl8187x_wrphyofdm(priv, 0x03, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x04, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x05, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x06, 0x40); usleep(1000); rtl8187x_wrphyofdm(priv, 0x07, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x08, 0x40); usleep(1000); rtl8187x_wrphyofdm(priv, 0x09, 0xfe); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0a, 0x09); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0b, 0x80); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0c, 0x01); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0e, 0xd3); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0f, 0x38); usleep(1000); rtl8187x_wrphyofdm(priv, 0x10, 0x84); usleep(1000); rtl8187x_wrphyofdm(priv, 0x11, 0x06); usleep(1000); rtl8187x_wrphyofdm(priv, 0x12, 0x20); usleep(1000); rtl8187x_wrphyofdm(priv, 0x13, 0x20); usleep(1000); rtl8187x_wrphyofdm(priv, 0x14, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x15, 0x40); usleep(1000); rtl8187x_wrphyofdm(priv, 0x16, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x17, 0x40); usleep(1000); rtl8187x_wrphyofdm(priv, 0x18, 0xef); usleep(1000); rtl8187x_wrphyofdm(priv, 0x19, 0x19); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1a, 0x20); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1b, 0x76); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1c, 0x04); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1e, 0x95); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1f, 0x75); usleep(1000); rtl8187x_wrphyofdm(priv, 0x20, 0x1f); usleep(1000); rtl8187x_wrphyofdm(priv, 0x21, 0x27); usleep(1000); rtl8187x_wrphyofdm(priv, 0x22, 0x16); usleep(1000); rtl8187x_wrphyofdm(priv, 0x24, 0x46); usleep(1000); rtl8187x_wrphyofdm(priv, 0x25, 0x20); usleep(1000); rtl8187x_wrphyofdm(priv, 0x26, 0x90); usleep(1000); rtl8187x_wrphyofdm(priv, 0x27, 0x88); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0d, g_rtl8225_gain[2 * 4]); rtl8187x_wrphyofdm(priv, 0x1b, g_rtl8225_gain[2 * 4 + 2]); rtl8187x_wrphyofdm(priv, 0x1d, g_rtl8225_gain[2 * 4 + 3]); rtl8187x_wrphyofdm(priv, 0x23, g_rtl8225_gain[2 * 4 + 1]); rtl8187x_wrphycck(priv, 0x00, 0x98); usleep(1000); rtl8187x_wrphycck(priv, 0x03, 0x20); usleep(1000); rtl8187x_wrphycck(priv, 0x04, 0x7e); usleep(1000); rtl8187x_wrphycck(priv, 0x05, 0x12); usleep(1000); rtl8187x_wrphycck(priv, 0x06, 0xfc); usleep(1000); rtl8187x_wrphycck(priv, 0x07, 0x78); usleep(1000); rtl8187x_wrphycck(priv, 0x08, 0x2e); usleep(1000); rtl8187x_wrphycck(priv, 0x10, 0x9b); usleep(1000); rtl8187x_wrphycck(priv, 0x11, 0x88); usleep(1000); rtl8187x_wrphycck(priv, 0x12, 0x47); usleep(1000); rtl8187x_wrphycck(priv, 0x13, 0xd0); rtl8187x_wrphycck(priv, 0x19, 0x00); rtl8187x_wrphycck(priv, 0x1a, 0xa0); rtl8187x_wrphycck(priv, 0x1b, 0x08); rtl8187x_wrphycck(priv, 0x40, 0x86); rtl8187x_wrphycck(priv, 0x41, 0x8d); usleep(1000); rtl8187x_wrphycck(priv, 0x42, 0x15); usleep(1000); rtl8187x_wrphycck(priv, 0x43, 0x18); usleep(1000); rtl8187x_wrphycck(priv, 0x44, 0x1f); usleep(1000); rtl8187x_wrphycck(priv, 0x45, 0x1e); usleep(1000); rtl8187x_wrphycck(priv, 0x46, 0x1a); usleep(1000); rtl8187x_wrphycck(priv, 0x47, 0x15); usleep(1000); rtl8187x_wrphycck(priv, 0x48, 0x10); usleep(1000); rtl8187x_wrphycck(priv, 0x49, 0x0a); usleep(1000); rtl8187x_wrphycck(priv, 0x4a, 0x05); usleep(1000); rtl8187x_wrphycck(priv, 0x4b, 0x02); usleep(1000); rtl8187x_wrphycck(priv, 0x4c, 0x05); usleep(1000); rtl8187x_iowrite8(priv, RTL8187X_ADDR_TESTR, 0x0D); rtl8225_settxpower(priv, 1); /* RX antenna default to A */ rtl8187x_wrphycck(priv, 0x10, 0x9b); usleep(1000); /* B: 0xDB */ rtl8187x_wrphyofdm(priv, 0x26, 0x90); usleep(1000); /* B: 0x10 */ rtl8187x_iowrite8(priv, RTL8187X_ADDR_TXANTENNA, 0x03); /* B: 0x00 */ usleep(1000); rtl8187x_iowrite32(priv, 0xff94, 0x3dc00002); /* Set sensitivity */ rtl8187x_write(priv, 0x0c, 0x50); rtl8187x_wrphyofdm(priv, 0x0d, g_rtl8225_gain[2 * 4]); rtl8187x_wrphyofdm(priv, 0x1b, g_rtl8225_gain[2 * 4 + 2]); rtl8187x_wrphyofdm(priv, 0x1d, g_rtl8225_gain[2 * 4 + 3]); rtl8187x_wrphyofdm(priv, 0x23, g_rtl8225_gain[2 * 4 + 1]); rtl8187x_wrphycck(priv, 0x41, g_rtl8225_threshold[2]); } static void rtl8225z2_rfinit(FAR struct rtl8187x_state_s *priv) { unsigned int i; rtl8187x_write(priv, 0x0, 0x2BF); usleep(1000); rtl8187x_write(priv, 0x1, 0xEE0); usleep(1000); rtl8187x_write(priv, 0x2, 0x44D); usleep(1000); rtl8187x_write(priv, 0x3, 0x441); usleep(1000); rtl8187x_write(priv, 0x4, 0x8C3); usleep(1000); rtl8187x_write(priv, 0x5, 0xC72); usleep(1000); rtl8187x_write(priv, 0x6, 0x0E6); usleep(1000); rtl8187x_write(priv, 0x7, 0x82A); usleep(1000); rtl8187x_write(priv, 0x8, 0x03F); usleep(1000); rtl8187x_write(priv, 0x9, 0x335); usleep(1000); rtl8187x_write(priv, 0xa, 0x9D4); usleep(1000); rtl8187x_write(priv, 0xb, 0x7BB); usleep(1000); rtl8187x_write(priv, 0xc, 0x850); usleep(1000); rtl8187x_write(priv, 0xd, 0xCDF); usleep(1000); rtl8187x_write(priv, 0xe, 0x02B); usleep(1000); rtl8187x_write(priv, 0xf, 0x114); usleep(100000); rtl8187x_write(priv, 0x0, 0x1B7); for (i = 0; i < ARRAY_SIZE(g_rtl8225z2_rxgain); i++) { rtl8187x_write(priv, 0x1, i + 1); rtl8187x_write(priv, 0x2, g_rtl8225z2_rxgain[i]); } rtl8187x_write(priv, 0x3, 0x080); rtl8187x_write(priv, 0x5, 0x004); rtl8187x_write(priv, 0x0, 0x0B7); rtl8187x_write(priv, 0x2, 0xc4D); usleep(200000); rtl8187x_write(priv, 0x2, 0x44D); usleep(100000); if (!(rtl8187x_read(priv, 6) & (1 << 7))) { rtl8187x_write(priv, 0x02, 0x0C4D); usleep(200000); rtl8187x_write(priv, 0x02, 0x044D); usleep(100000); if (!(rtl8187x_read(priv, 6) & (1 << 7))) { udbg("RF Calibration Failed! %x\n", rtl8187x_read(priv, 6)); } } usleep(200000); rtl8187x_write(priv, 0x0, 0x2BF); for (i = 0; i < ARRAY_SIZE(g_rtl8225_agc); i++) { rtl8187x_wrphyofdm(priv, 0xB, g_rtl8225_agc[i]); usleep(1000); rtl8187x_wrphyofdm(priv, 0xA, 0x80 + i); usleep(1000); } usleep(1000); rtl8187x_wrphyofdm(priv, 0x00, 0x01); usleep(1000); rtl8187x_wrphyofdm(priv, 0x01, 0x02); usleep(1000); rtl8187x_wrphyofdm(priv, 0x02, 0x42); usleep(1000); rtl8187x_wrphyofdm(priv, 0x03, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x04, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x05, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x06, 0x40); usleep(1000); rtl8187x_wrphyofdm(priv, 0x07, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x08, 0x40); usleep(1000); rtl8187x_wrphyofdm(priv, 0x09, 0xfe); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0a, 0x08); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0b, 0x80); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0c, 0x01); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0d, 0x43); rtl8187x_wrphyofdm(priv, 0x0e, 0xd3); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0f, 0x38); usleep(1000); rtl8187x_wrphyofdm(priv, 0x10, 0x84); usleep(1000); rtl8187x_wrphyofdm(priv, 0x11, 0x07); usleep(1000); rtl8187x_wrphyofdm(priv, 0x12, 0x20); usleep(1000); rtl8187x_wrphyofdm(priv, 0x13, 0x20); usleep(1000); rtl8187x_wrphyofdm(priv, 0x14, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x15, 0x40); usleep(1000); rtl8187x_wrphyofdm(priv, 0x16, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x17, 0x40); usleep(1000); rtl8187x_wrphyofdm(priv, 0x18, 0xef); usleep(1000); rtl8187x_wrphyofdm(priv, 0x19, 0x19); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1a, 0x20); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1b, 0x15); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1c, 0x04); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1d, 0xc5); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1e, 0x95); usleep(1000); rtl8187x_wrphyofdm(priv, 0x1f, 0x75); usleep(1000); rtl8187x_wrphyofdm(priv, 0x20, 0x1f); usleep(1000); rtl8187x_wrphyofdm(priv, 0x21, 0x17); usleep(1000); rtl8187x_wrphyofdm(priv, 0x22, 0x16); usleep(1000); rtl8187x_wrphyofdm(priv, 0x23, 0x80); usleep(1000); // FIXME: not needed? rtl8187x_wrphyofdm(priv, 0x24, 0x46); usleep(1000); rtl8187x_wrphyofdm(priv, 0x25, 0x00); usleep(1000); rtl8187x_wrphyofdm(priv, 0x26, 0x90); usleep(1000); rtl8187x_wrphyofdm(priv, 0x27, 0x88); usleep(1000); rtl8187x_wrphyofdm(priv, 0x0b, g_rtl8225z2_gainbg[4 * 3]); rtl8187x_wrphyofdm(priv, 0x1b, g_rtl8225z2_gainbg[4 * 3 + 1]); rtl8187x_wrphyofdm(priv, 0x1d, g_rtl8225z2_gainbg[4 * 3 + 2]); rtl8187x_wrphyofdm(priv, 0x21, 0x37); rtl8187x_wrphycck(priv, 0x00, 0x98); usleep(1000); rtl8187x_wrphycck(priv, 0x03, 0x20); usleep(1000); rtl8187x_wrphycck(priv, 0x04, 0x7e); usleep(1000); rtl8187x_wrphycck(priv, 0x05, 0x12); usleep(1000); rtl8187x_wrphycck(priv, 0x06, 0xfc); usleep(1000); rtl8187x_wrphycck(priv, 0x07, 0x78); usleep(1000); rtl8187x_wrphycck(priv, 0x08, 0x2e); usleep(1000); rtl8187x_wrphycck(priv, 0x10, 0x9b); usleep(1000); rtl8187x_wrphycck(priv, 0x11, 0x88); usleep(1000); rtl8187x_wrphycck(priv, 0x12, 0x47); usleep(1000); rtl8187x_wrphycck(priv, 0x13, 0xd0); rtl8187x_wrphycck(priv, 0x19, 0x00); rtl8187x_wrphycck(priv, 0x1a, 0xa0); rtl8187x_wrphycck(priv, 0x1b, 0x08); rtl8187x_wrphycck(priv, 0x40, 0x86); rtl8187x_wrphycck(priv, 0x41, 0x8d); usleep(1000); rtl8187x_wrphycck(priv, 0x42, 0x15); usleep(1000); rtl8187x_wrphycck(priv, 0x43, 0x18); usleep(1000); rtl8187x_wrphycck(priv, 0x44, 0x36); usleep(1000); rtl8187x_wrphycck(priv, 0x45, 0x35); usleep(1000); rtl8187x_wrphycck(priv, 0x46, 0x2e); usleep(1000); rtl8187x_wrphycck(priv, 0x47, 0x25); usleep(1000); rtl8187x_wrphycck(priv, 0x48, 0x1c); usleep(1000); rtl8187x_wrphycck(priv, 0x49, 0x12); usleep(1000); rtl8187x_wrphycck(priv, 0x4a, 0x09); usleep(1000); rtl8187x_wrphycck(priv, 0x4b, 0x04); usleep(1000); rtl8187x_wrphycck(priv, 0x4c, 0x05); usleep(1000); rtl8187x_iowrite8(priv, 0xff5B, 0x0D); usleep(1000); rtl8225z2_settxpower(priv, 1); /* RX antenna default to A */ rtl8187x_wrphycck(priv, 0x10, 0x9b); usleep(1000); /* B: 0xDB */ rtl8187x_wrphyofdm(priv, 0x26, 0x90); usleep(1000); /* B: 0x10 */ rtl8187x_iowrite8(priv, RTL8187X_ADDR_TXANTENNA, 0x03); /* B: 0x00 */ usleep(1000); rtl8187x_iowrite32(priv, 0xff94, 0x3dc00002); } /**************************************************************************** * Function: rtl8187x_anaparam2on and rtl8187x_anaparamon * * Description: * Chip-specific TX power configuration * * Parameters: * priv - Private driver state information * channel - The selected channel * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static void rtl8187x_anaparam2on(FAR struct rtl8187x_state_s *priv) { uint8_t regval; rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_CONFIG); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_CONFIG3); rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG3, regval | RTL8187X_CONFIG3_ANAPARAMWRITE); #ifdef CONFIG_RTL8187B rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM2, RTL8187B_RTL8225_ANAPARAM2_ON); #else rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM2, RTL8187X_RTL8225_ANAPARAM2_ON); #endif rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG3, regval & ~RTL8187X_CONFIG3_ANAPARAMWRITE); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_NORMAL); } static void rtl8187x_anaparamon(FAR struct rtl8187x_state_s *priv) { uint8_t regval; rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_CONFIG); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_CONFIG3); rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG3, regval | RTL8187X_CONFIG3_ANAPARAMWRITE); #ifdef CONFIG_RTL8187B rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM, RTL8187B_RTL8225_ANAPARAM_ON); rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM2, RTL8187B_RTL8225_ANAPARAM2_ON); rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM3, RTL8187B_RTL8225_ANAPARAM3_ON); #else rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM, RTL8187X_RTL8225_ANAPARAM_ON); rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM2, RTL8187X_RTL8225_ANAPARAM2_ON); #endif rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG3, regval & ~RTL8187X_CONFIG3_ANAPARAMWRITE); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_NORMAL); } static void rtl8187x_anaparamoff(FAR struct rtl8187x_state_s *priv) { uint8_t regval; rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_CONFIG); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_CONFIG3); rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG3, regval | RTL8187X_CONFIG3_ANAPARAMWRITE); #ifdef CONFIG_RTL8187B rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM, RTL8187B_RTL8225_ANAPARAM_OFF); rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM2, RTL8187B_RTL8225_ANAPARAM2_OFF); rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM3, RTL8187B_RTL8225_ANAPARAM3_OFF); #else rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM, RTL8187X_RTL8225_ANAPARAM_OFF); rtl8187x_iowrite32(priv, RTL8187X_ADDR_ANAPARAM2, RTL8187X_RTL8225_ANAPARAM2_OFF); #endif rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG3, regval & ~RTL8187X_CONFIG3_ANAPARAMWRITE); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_NORMAL); } /**************************************************************************** * Function: rtl8225_settxpower and * * Description: * Chip-specific TX power configuration * * Parameters: * priv - Private driver state information * channel - The selected channel * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static void rtl8225_settxpower(FAR struct rtl8187x_state_s *priv, int channel) { uint8_t cck_power, ofdm_power; const uint8_t *tmp; int i; cck_power = priv->channels[channel - 1].val & 0xf; ofdm_power = priv->channels[channel - 1].val >> 4; cck_power = MIN(cck_power, (uint8_t) 11); ofdm_power = MIN(ofdm_power, (uint8_t) 35); rtl8187x_iowrite8(priv, RTL8187X_ADDR_TXGAINCCK, g_rtl8225_txgaincckofdm[cck_power / 6] >> 1); if (channel == 14) { tmp = &g_rtl8225_txpowercckch14[(cck_power % 6) * 8]; } else { tmp = &g_rtl8225_txpowercck[(cck_power % 6) * 8]; } for (i = 0; i < 8; i++) { rtl8187x_wrphycck(priv, 0x44 + i, *tmp++); } usleep(1000); // FIXME: optional? /* anaparam2 on */ rtl8187x_anaparam2on(priv); rtl8187x_wrphyofdm(priv, 2, 0x42); rtl8187x_wrphyofdm(priv, 6, 0x00); rtl8187x_wrphyofdm(priv, 8, 0x00); rtl8187x_iowrite8(priv, RTL8187X_ADDR_TXGAINOFDM, g_rtl8225_txgaincckofdm[ofdm_power / 6] >> 1); tmp = &g_rtl8225_txpowerofdm[ofdm_power % 6]; rtl8187x_wrphyofdm(priv, 5, *tmp); rtl8187x_wrphyofdm(priv, 7, *tmp); usleep(1000); } static void rtl8225z2_settxpower(FAR struct rtl8187x_state_s *priv, int channel) { uint8_t cck_power; uint8_t ofdm_power; const uint8_t *tmp; int i; cck_power = priv->channels[channel - 1].val & 0xF; ofdm_power = priv->channels[channel - 1].val >> 4; cck_power = MIN(cck_power, (uint8_t) 15); cck_power += priv->rxpwrbase & 0xF; cck_power = MIN(cck_power, (uint8_t) 35); ofdm_power = MIN(ofdm_power, (uint8_t) 15); ofdm_power += priv->rxpwrbase >> 4; ofdm_power = MIN(ofdm_power, (uint8_t) 35); if (channel == 14) { tmp = g_rtl8225z2_txpowercckch14; } else { tmp = g_rtl8225z2_txpowercck; } for (i = 0; i < 8; i++) { rtl8187x_wrphycck(priv, 0x44 + i, *tmp++); } rtl8187x_iowrite8(priv, RTL8187X_ADDR_TXGAINCCK, g_rtl8225z2_txgaincckofdm[cck_power]); usleep(1000); /* anaparam2 on */ rtl8187x_anaparam2on(priv); rtl8187x_wrphyofdm(priv, 2, 0x42); rtl8187x_wrphyofdm(priv, 5, 0x00); rtl8187x_wrphyofdm(priv, 6, 0x40); rtl8187x_wrphyofdm(priv, 7, 0x00); rtl8187x_wrphyofdm(priv, 8, 0x40); rtl8187x_iowrite8(priv, RTL8187X_ADDR_TXGAINOFDM, g_rtl8225z2_txgaincckofdm[ofdm_power]); usleep(1000); } /**************************************************************************** * Function: rtl8187x_reset * * Description: * Reset and initialize hardware * * Parameters: * priv - Private driver state information * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static int rtl8187x_reset(struct rtl8187x_state_s *priv) { uint8_t regval; int i; #ifdef CONFIG_RTL8187B int ret; /* Turn on ANAPARAM */ rtl8187x_anaparamon(priv); /* Reset PLL sequence on 8187B. Realtek note: reduces power * consumption about 30 mA */ rtl8187x_iowrite8(priv, (uint8_t*)0xff61, 0x10); regval = rtl818x_ioread8(priv, (uint8_t*)0xff62); rtl8187x_iowrite8(priv, (uint8_t*)0xff62, regval & ~(1 << 5)); rtl8187x_iowrite8(priv, (uint8_t*)0xff62, regval | (1 << 5)); ret = rtl8187_cmd_reset(dev); if (ret != 0) { return ret; } rtl8187x_anaparamon(priv) /* BRSR (Basic Rate Set Register) on 8187B looks to be the same as * RESP_RATE on 8187L in Realtek sources: each bit should be each * one of the 12 rates, all are enabled */ rtl8187x_iowrite16(priv, (uint16_t*)0xff34, 0x0fff); regval = rtl818x_ioread8(priv, RTL8187X_ADDR_CWCONF); regval |= RTL8187X_CW_CONF_PERPACKET_RETRY_SHIFT; rtl8187x_iowrite8(priv, RTL8187X_ADDR_CWCONF, regval); /* Auto Rate Fallback Register (ARFR): 1M-54M setting */ rtl8187x_iowrite16_idx(priv, (uint16_t*)0xffe0, 0x0fff, 1); rtl8187x_iowrite8_idx(priv, (uint8_t*)0xffe2, 0x00, 1); rtl8187x_iowrite16_idx(priv, (uint16_t*)0xffd4, 0xffff, 1); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_CONFIG); regval = rtl818x_ioread8(priv, RTL8187X_ADDR_CONFIG1); rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG1, (regval & 0x3f) | 0x80); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_NORMAL); rtl8187x_iowrite8(priv, RTL8187X_ADDR_WPACONF, 0); for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) { rtl8187x_iowrite8_idx(priv, (uint8_t*)(uintptr_t) (rtl8187b_reg_table[i][0] | 0xff00), rtl8187b_reg_table[i][1], rtl8187b_reg_table[i][2]); } rtl8187x_iowrite16(priv, RTL8187X_ADDR_TIDACMAP, 0xfa50); rtl8187x_iowrite16(priv, RTL8187X_ADDR_INTMIG, 0); rtl8187x_iowrite32_idx(priv, (uint32_t*)0xfff0, 0, 1); rtl8187x_iowrite32_idx(priv, (uint32_t*)0xfff4, 0, 1); rtl8187x_iowrite8_idx(priv, (uint8_t*)0xfff8, 0, 1); rtl8187x_iowrite32(priv, RTL8187X_ADDR_RFTIMING, 0x00004001); /* RFSW_CTRL register */ rtl8187x_iowrite16_idx(priv, (uint16_t*)0xff72, 0x569a, 2); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, 0x0480); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, 0x2488); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSENABLE, 0x1fff); msleep(100); priv->rf->init(dev); regval = RTL8187X_CMD_TX_ENABLE | RTL8187X_CMD_RX_ENABLE; rtl8187x_iowrite8(priv, RTL8187X_ADDR_CMD, regval); rtl8187x_iowrite16(priv, RTL8187X_ADDR_INTMASK, 0xffff); rtl8187x_iowrite8(priv, (uint8_t*)0xfe41, 0xf4); rtl8187x_iowrite8(priv, (uint8_t*)0xfe40, 0x00); rtl8187x_iowrite8(priv, (uint8_t*)0xfe42, 0x00); rtl8187x_iowrite8(priv, (uint8_t*)0xfe42, 0x01); rtl8187x_iowrite8(priv, (uint8_t*)0xfe40, 0x0f); rtl8187x_iowrite8(priv, (uint8_t*)0xfe42, 0x00); rtl8187x_iowrite8(priv, (uint8_t*)0xfe42, 0x01); regval = rtl818x_ioread8(priv, (uint8_t*)0xffdb); rtl8187x_iowrite8(priv, (uint8_t*)0xffdb, regval | (1 << 2)); rtl8187x_iowrite16_idx(priv, (uint16_t*)0xff72, 0x59fa, 3); rtl8187x_iowrite16_idx(priv, (uint16_t*)0xff74, 0x59d2, 3); rtl8187x_iowrite16_idx(priv, (uint16_t*)0xff76, 0x59d2, 3); rtl8187x_iowrite16_idx(priv, (uint16_t*)0xff78, 0x19fa, 3); rtl8187x_iowrite16_idx(priv, (uint16_t*)0xff7a, 0x19fa, 3); rtl8187x_iowrite16_idx(priv, (uint16_t*)0xff7c, 0x00d0, 3); rtl8187x_iowrite8(priv, (uint8_t*)0xff61, 0); rtl8187x_iowrite8_idx(priv, (uint8_t*)0xff80, 0x0f, 1); rtl8187x_iowrite8_idx(priv, (uint8_t*)0xff83, 0x03, 1); rtl8187x_iowrite8(priv, (uint8_t*)0xffda, 0x10); rtl8187x_iowrite8_idx(priv, (uint8_t*)0xff4d, 0x08, 2); rtl8187x_iowrite32(priv, rtl8187x_addr_hssipara, 0x0600321b); rtl8187x_iowrite16_idx(priv, (uint16_t*)0xffec, 0x0800, 1); priv->slot_time = 0x9; priv->aifsn[0] = 2; /* AIFSN[AC_VO] */ priv->aifsn[1] = 2; /* AIFSN[AC_VI] */ priv->aifsn[2] = 7; /* AIFSN[AC_BK] */ priv->aifsn[3] = 3; /* AIFSN[AC_BE] */ rtl8187x_iowrite8(priv, RTL8187X_ADDR_ACMCONTROL, 0); /* ENEDCA flag must always be set, transmit issues? */ rtl8187x_iowrite8(priv, RTL8187X_ADDR_MSR, RTL8187X_MSR_ENEDCA); #else /* reset */ rtl8187x_anaparamon(priv); rtl8187x_iowrite16(priv, RTL8187X_ADDR_INTMASK, 0); usleep(200000); rtl8187x_iowrite8(priv, 0xfe18, 0x10); rtl8187x_iowrite8(priv, 0xfe18, 0x11); rtl8187x_iowrite8(priv, 0xfe18, 0x00); usleep(200000); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_CMD); regval &= (1 << 1); regval |= RTL8187X_CMD_RESET; rtl8187x_iowrite8(priv, RTL8187X_ADDR_CMD, regval); i = 10; do { usleep(2000); if (!(rtl8187x_ioread8(priv, RTL8187X_ADDR_CMD) & RTL8187X_CMD_RESET)) break; } while (--i); if (!i) { udbg("Reset timeout!\n"); return -ETIMEDOUT; } /* Reload registers from eeprom */ rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_LOAD); i = 10; do { usleep(4000); if (!(rtl8187x_ioread8(priv, RTL8187X_ADDR_EEPROMCMD) & RTL8187X_EEPROMCMD_CONFIG)) break; } while (--i); if (!i) { udbg("%s: eeprom reset timeout!\n"); return -ETIMEDOUT; } rtl8187x_anaparamon(priv); /* Setup card */ rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, 0); rtl8187x_iowrite8(priv, RTL8187X_ADDR_GPIO, 0); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, (4 << 8)); rtl8187x_iowrite8(priv, RTL8187X_ADDR_GPIO, 1); rtl8187x_iowrite8(priv, RTL8187X_ADDR_GPENABLE, 0); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_CONFIG); rtl8187x_iowrite16(priv, 0xffF4, 0xffFF); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_CONFIG1); regval &= 0x3F; regval |= 0x80; rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG1, regval); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_NORMAL); rtl8187x_iowrite32(priv, RTL8187X_ADDR_INTTIMEOUT, 0); rtl8187x_iowrite8(priv, RTL8187X_ADDR_WPACONF, 0); rtl8187x_iowrite8(priv, RTL8187X_ADDR_RATEFALLBACK, 0x81); // TODO: set RESP_RATE and BRSR properly rtl8187x_iowrite8(priv, RTL8187X_ADDR_RESPRATE, (8 << 4) | 0); rtl8187x_iowrite16(priv, RTL8187X_ADDR_BRSR, 0x01F3); /* host_usb_init */ rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, 0); rtl8187x_iowrite8(priv, RTL8187X_ADDR_GPIO, 0); regval = rtl8187x_ioread8(priv, 0xfe53); rtl8187x_iowrite8(priv, 0xfe53, regval | (1 << 7)); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, (4 << 8)); rtl8187x_iowrite8(priv, RTL8187X_ADDR_GPIO, 0x20); rtl8187x_iowrite8(priv, RTL8187X_ADDR_GPENABLE, 0); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSOUTPUT, 0x80); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSSELECT, 0x80); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSENABLE, 0x80); usleep(100000); rtl8187x_iowrite32(priv, RTL8187X_ADDR_RFTIMING, 0x000a8008); rtl8187x_iowrite16(priv, RTL8187X_ADDR_BRSR, 0xffFF); rtl8187x_iowrite32(priv, RTL8187X_ADDR_RFPARA, 0x00100044); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_CONFIG); rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG3, 0x44); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_NORMAL); rtl8187x_iowrite16(priv, RTL8187X_ADDR_RFPINSENABLE, 0x1FF7); usleep(100000); priv->rfinit(priv); rtl8187x_iowrite16(priv, RTL8187X_ADDR_BRSR, 0x01F3); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_PGSELECT) & ~1; rtl8187x_iowrite8(priv, RTL8187X_ADDR_PGSELECT, regval | 1); rtl8187x_iowrite16(priv, 0xffFE, 0x10); rtl8187x_iowrite8(priv, RTL8187X_ADDR_TALLYSEL, 0x80); rtl8187x_iowrite8(priv, 0xffFF, 0x60); rtl8187x_iowrite8(priv, RTL8187X_ADDR_PGSELECT, regval); #endif return OK; } /**************************************************************************** * Function: rtl8187x_setchannel * * Description: * Select the specified channel * * Parameters: * priv - Private driver state information * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static void rtl8187x_setchannel(FAR struct rtl8187x_state_s *priv, int channel) { uint32_t regval; regval = rtl8187x_ioread32(priv, RTL8187X_ADDR_TXCONF); /* Enable TX loopback on MAC level to avoid TX during channel changes, as * this has be seen to causes problems and the card will stop work until next * reset */ rtl8187x_iowrite32(priv, RTL8187X_ADDR_TXCONF, regval | RTL8187X_TXCONF_LOOPBACKMAC); usleep(10000); priv->settxpower(priv, channel); rtl8187x_write(priv, 0x7, g_chanselect[channel - 1]); usleep(20000); rtl8187x_iowrite32(priv, RTL8187X_ADDR_TXCONF, regval); } /**************************************************************************** * Function: rtl8187_start * * Description: * Bring up the RTL8187 * * Parameters: * priv - Private driver state information * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static int rtl8187x_start(FAR struct rtl8187x_state_s *priv) { uint32_t regval; int ret; /* Reset and initialize the hardware */ ret = rtl8187x_reset(priv); if (ret != OK) { return ret; } #ifdef CONFIG_RTL8187B regval = RTL8187X_RXCONF_MGMT | RTL8187X_RXCONF_DATA | RTL8187X_RXCONF_BROADCAST | RTL8187X_RXCONF_NICMAC | RTL8187X_RX_ONF_BSSID | (7 << 13 /* RX FIFO threshold NONE */) | (7 << 10 /* MAX RX DMA */) | RTL8187X_RXCONF_RX_AUTORESETPHY | RTL8187X_RXCONF_ONLYERLPKT | RTL8187X_RXCONF_MULTICAST; rtl8187x_iowrite32(priv, RTL8187X_ADDR_RXCONF, regval); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_TXAGCCTL); regval &= ~RTL8187X_TXAGCCTL_PERPACKETGAINSHIFT; regval &= ~RTL8187X_TXAGCCTL_PERPACKETANTSELSHIFT; regval &= ~RTL8187X_TXAGCCTL_FEEDBACKANT; rtl8187x_iowrite8(priv, RTL8187X_ADDR_TXAGCCTL, regval); regval = RTL8187X_TXCONF_HWSEQNUM | RTL8187X_TXCONF_DISREQQSIZE | (7 << 8 /* short retry limit */) | (7 << 0 /* long retry limit */) | (7 << 21 /* MAX TX DMA */); rtl8187x_iowrite32(priv, RTL8187X_ADDR_TXCONF, regval); #else rtl8187x_iowrite16(priv, RTL8187X_ADDR_INTMASK, 0xffff); rtl8187x_iowrite32(priv, RTL8187X_ADDR_MAR0, ~0); rtl8187x_iowrite32(priv, RTL8187X_ADDR_MAR1, ~0); regval = RTL8187X_RXCONF_ONLYERLPKT | RTL8187X_RXCONF_RXAUTORESETPHY | RTL8187X_RXCONF_BSSID | RTL8187X_RXCONF_MGMT | RTL8187X_RXCONF_DATA | RTL8187X_RXCONF_CTRL | (7 << 13 /* RX fifo threshold none */ ) | (7 << 10 /* MAX RX DMA */ ) | RTL8187X_RXCONF_BROADCAST | RTL8187X_RXCONF_NICMAC | RTL8187X_RXCONF_MONITOR; rtl8187x_iowrite32(priv, RTL8187X_ADDR_RXCONF, regval); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_CWCONF); regval &= ~RTL8187X_CWCONF_PERPACKETCWSHIFT; regval |= RTL8187X_CWCONF_PERPACKETRETRYSHIFT; rtl8187x_iowrite8(priv, RTL8187X_ADDR_CWCONF, regval); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_TXAGCCTL); regval &= ~RTL8187X_TXAGCCTL_PERPACKETGAINSHIFT; regval &= ~RTL8187X_TXAGCCTL_PERPACKETANTSELSHIFT; regval &= ~RTL8187X_TXAGCCTL_FEEDBACKANT; rtl8187x_iowrite8(priv, RTL8187X_ADDR_TXAGCCTL, regval); regval = RTL8187X_TXCONF_CWMIN | (7 << 21 /* MAX TX DMA */ ) | RTL8187X_TXCONF_NOICV; rtl8187x_iowrite32(priv, RTL8187X_ADDR_TXCONF, regval); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_CMD); regval |= RTL8187X_CMD_TXENABLE; regval |= RTL8187X_CMD_RXENABLE; rtl8187x_iowrite8(priv, RTL8187X_ADDR_CMD, regval); #endif return OK; } /**************************************************************************** * Function: rtl8187x_stop * * Description: * Bring down the RTL8187 * * Parameters: * priv - Private driver state information * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static void rtl8187x_stop(FAR struct rtl8187x_state_s *priv) { uint32_t regval; rtl8187x_iowrite16(priv, RTL8187X_ADDR_INTMASK, 0); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_CMD); regval &= ~RTL8187X_CMD_TXENABLE; regval &= ~RTL8187X_CMD_RXENABLE; rtl8187x_iowrite8(priv, RTL8187X_ADDR_CMD, regval); rtl8187x_write(priv, 0x4, 0x1f); usleep(1000); /* RF stop */ rtl8187x_anaparamoff(priv); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_CONFIG); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_CONFIG4); rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG4, regval | RTL8187X_CONFIG4_VCOOFF); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_NORMAL); } /**************************************************************************** * Function: rtl8187_setup * * Description: * Configure the RTL8187 * * Parameters: * priv - Private driver state information * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static int rtl8187x_setup(FAR struct rtl8187x_state_s *priv) { struct ieee80211_channel_s *channel; uint16_t permaddr[3]; uint16_t txpwr, regval; int i; /* Copy the default channel information */ memcpy(priv->channels, g_channels, RTL8187X_NCHANNELS*sizeof(struct ieee80211_channel_s)); /* Get the EEPROM width */ if (rtl8187x_ioread32(priv, RTL8187X_ADDR_RXCONF) & (1 << 6)) { priv->width = PCI_EEPROM_WIDTH_93C66; } else { priv->width = PCI_EEPROM_WIDTH_93C46; } rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_CONFIG); usleep(10); rtl8187x_eeprom_multiread(priv, RTL8187X_EEPROM_MACADDR, permaddr, 3); udbg("MAC address: %04x.%04x.%04x", permaddr[0], permaddr[1], permaddr[2]); channel = priv->channels; for (i = 0; i < 3; i++) { rtl8187x_eeprom_read(priv, RTL8187X_EEPROM_TXPWRCHAN1 + i, &txpwr); (*channel++).val = txpwr & 0xff; (*channel++).val = txpwr >> 8; } for (i = 0; i < 2; i++) { rtl8187x_eeprom_read(priv, RTL8187X_EEPROM_TXPWRCHAN4 + i, &txpwr); (*channel++).val = txpwr & 0xff; (*channel++).val = txpwr >> 8; } #ifdef CONFIG_RTL8187B rtl8187x_eeprom_read(&priv, RTL8187X_EEPROM_TXPWRCHAN6, &txpwr); (*channel++).val = txpwr & 0xff; rtl8187x_eeprom_read(&priv, 0x0a, &txpwr); (*channel++).val = txpwr & 0xff; rtl8187x_eeprom_read(&priv, 0x1c, &txpwr); (*channel++).val = txpwr & 0xff; (*channel++).val= txpwr; #else for (i = 0; i < 2; i++) { rtl8187x_eeprom_read(priv, RTL8187X_EEPROM_TXPWRCHAN6 + i, &txpwr); (*channel++).val = txpwr & 0xff; (*channel++).val = txpwr >> 8; } #endif rtl8187x_eeprom_read(priv, RTL8187X_EEPROM_TXPWRBASE, &priv->rxpwrbase); regval = rtl8187x_ioread8(priv, RTL8187X_ADDR_PGSELECT) & ~1; rtl8187x_iowrite8(priv, RTL8187X_ADDR_PGSELECT, regval | 1); /* 0 means asic B-cut, we should use SW 3 wire bit-by-bit banging for radio. * 1 means we can use USB specific request to write radio registers */ priv->asicrev = rtl8187x_ioread8(priv, 0xffFE) & 0x3; rtl8187x_iowrite8(priv, RTL8187X_ADDR_PGSELECT, regval); rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL8187X_EEPROMCMD_NORMAL); rtl8187x_write(priv, 0, 0x1b7); if (rtl8187x_read(priv, 8) != 0x588 || rtl8187x_read(priv, 9) != 0x700) { priv->rfinit = rtl8225_rfinit; priv->settxpower = rtl8225_settxpower; } else { priv->rfinit = rtl8225z2_rfinit; priv->settxpower = rtl8225z2_settxpower; } rtl8187x_write(priv, 0, 0x0b7); /* Save the MAC address in the device structure */ priv->ethdev.d_mac.ether_addr_octet[0] = permaddr[0] & 0xff; priv->ethdev.d_mac.ether_addr_octet[1] = permaddr[0] >> 8; priv->ethdev.d_mac.ether_addr_octet[2] = permaddr[1] & 0xff; priv->ethdev.d_mac.ether_addr_octet[3] = permaddr[1] >> 8; priv->ethdev.d_mac.ether_addr_octet[4] = permaddr[2] & 0xff; priv->ethdev.d_mac.ether_addr_octet[5] = permaddr[2] >> 8; /* Provide information about the RTL device */ udbg("hwaddr %02x.%02x.%02x.%02x.%02x.%02x, rtl8187 V%d + %s\n", priv->ethdev.d_mac.ether_addr_octet[0], priv->ethdev.d_mac.ether_addr_octet[1], priv->ethdev.d_mac.ether_addr_octet[2], priv->ethdev.d_mac.ether_addr_octet[3], priv->ethdev.d_mac.ether_addr_octet[4], priv->ethdev.d_mac.ether_addr_octet[5], priv->asicrev, priv->rfinit == rtl8225_rfinit ? "rtl8225" : "rtl8225z2"); return 0; } /**************************************************************************** * Function: rtl8187x_netinitialize * * Description: * Initialize the WLAN controller and driver * * Parameters: * intf - In the case where there are multiple EMACs, this value * identifies which EMAC is to be initialized. * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static int rtl8187x_netinitialize(FAR struct rtl8187x_state_s *priv) { int ret; /* Initialize the RTL8187x */ ret = rtl8187x_setup(priv); if (ret == OK) { /* Put the interface in the down state. */ rtl8187x_ifdown(&priv->ethdev); /* Register the device with the OS so that socket IOCTLs can be performed */ (void)netdev_register(&priv->ethdev); } return OK; } /**************************************************************************** * Function: rtl8187x_netuninitialize * * Description: * Un-initialize the RTL8187x. This only happens when the RTL8187x device * is removed from the USB slot. * * Parameters: * intf - In the case where there are multiple EMACs, this value * identifies which EMAC is to be initialized. * * Returned Value: * OK on success; Negated errno on failure. * * Assumptions: * ****************************************************************************/ static int rtl8187x_netuninitialize(FAR struct rtl8187x_state_s *priv) { irqstate_t flags; /* Cancel the TX and RX poll timers */ flags = irqsave(); wd_cancel(priv->wdtxpoll); wd_cancel(priv->wdrxpoll); /* Mark the device "down" */ priv->bifup = false; irqrestore(flags); /* Unregister the device */ (void)netdev_unregister(&priv->ethdev); return OK; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: usbhost_wlaninit * * Description: * Initialize the USB class driver. This function should be called * be platform-specific code in order to initialize and register support * for the USB host class device. * * Input Parameters: * None * * Returned Values: * On success this function will return zero (OK); A negated errno value * will be returned on failure. * ****************************************************************************/ int usbhost_wlaninit(void) { /* Advertise our availability to support RTL8187x devices */ uvdbg("Register RTL8187x driver\n"); return usbhost_registerclass(&g_wlan); } #endif /* CONFIG_USBHOST && CONFIG_NET */