/****************************************************************************
 * drivers/usbhost/rtl8187.c
 *
 *   Copyright (C) 2011 Gregory Nutt. All rights reserved.
 *   Authors: Rafael Noronha <rafael@pdsolucoes.com.br>
 *            Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * 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
 *   <http://rt2x00.serialmonkey.com>
 *
 * 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 <flamingice@sourmilk.net>
 *   Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
 *
 *   Based on the r8187 driver, which is:
 *   Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, 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 <nuttx/config.h>

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <time.h>
#include <wdog.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>

#include <nuttx/kmalloc.h>
#include <nuttx/fs.h>
#include <nuttx/clock.h>
#include <nuttx/arch.h>
#include <nuttx/wqueue.h>
#include <nuttx/irq.h>

#include <nuttx/usb/usb.h>
#include <nuttx/usb/usbhost.h>

#include <net/uip/uip.h>
#include <net/uip/uip-arp.h>
#include <net/uip/uip-arch.h>

#include "rtl8187x.h"

#if defined(CONFIG_USBHOST) && defined(CONFIG_NET) && defined(CONFIG_NET_WLAN)

/****************************************************************************
 * 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 uip_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_uiptxpoll(struct uip_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 uip_driver_s *dev);
static int rtl8187x_ifdown(struct uip_driver_s *dev);
static int rtl8187x_txavail(struct uip_driver_s *dev);
#ifdef CONFIG_NET_IGMP
static int rtl8187x_addmac(struct uip_driver_s *dev, FAR const uint8_t *mac);
static int rtl8187x_rmmac(struct uip_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 informatino 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_USB_WLAN_VID,    /* vid */
    CONFIG_USB_WLAN_PID     /* pid */
  },
  {
    0,                      /* base */
    0,                      /* subclass */
    0,                      /* proto */
    CONFIG_USB_WLAN_VID,    /* vid */
    CONFIG_USB_WLAN_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(&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_uiptxpoll
 *
 * Description:
 *   The transmitter is available, check if uIP has any outgoing packets ready
 *   to send.  This is a callback from uip_poll().  uip_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 uip_lock() to assure that we have exclusive access to uIP.
 *
 ****************************************************************************/

static int rtl8187x_uiptxpoll(struct uip_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)
    {
      uip_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)
    {
      uip_lock_t lock;
      uint32_t   now;
      uint32_t   hsecs;

      /* Get exclusive access to uIP */

      lock = uip_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)uip_timer(&priv->ethdev, rtl8187x_uiptxpoll, (int)hsecs);
      uip_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(&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 < UIP_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 uip_eth_hdr *ethhdr = (FAR struct uip_eth_hdr *)priv->rxbuffer;
  uip_lock_t lock;

  /* Get exclusive access to uIP */

  lock               = uip_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(UIP_ETHTYPE_IP6))
#else
  if (ethhdr->type == HTONS(UIP_ETHTYPE_IP))
#endif
    {
      RTL8187X_STATS(priv, rxippackets);
      uip_arp_ipin(&priv->ethdev);
      uip_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)
        {
          uip_arp_out(&priv->ethdev);
          rtl8187x_transmit(priv);
        }
    }
  else if (ethhdr->type == htons(UIP_ETHTYPE_ARP))
    {
      RTL8187X_STATS(priv, rxarppackets);   
      uip_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);
    }

  uip_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(&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 uip_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 uip_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 uip_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)
    {
      uip_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 = uip_lock();
      priv->ethdev.d_buf = &priv->txbuffer[SIZEOF_TXDESC];
      (void)uip_poll(&priv->ethdev, rtl8187x_uiptxpoll);
      uip_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 uip_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 uip_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 |= RTL818X_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, RTL818X_EEPROMCMD_CONFIG);
  regval = rtl818x_ioread8(priv, RTL8187X_ADDR_CONFIG1);
  rtl8187x_iowrite8(priv, RTL8187X_ADDR_CONFIG1, (regval & 0x3f) | 0x80);
  rtl8187x_iowrite8(priv, RTL8187X_ADDR_EEPROMCMD, RTL818X_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 = RTL818X_CMD_TX_ENABLE | RTL818X_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, RTL818X_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 = RTL818X_RXCONF_MGMT | RTL818X_RXCONF_DATA | RTL818X_RXCONF_BROADCAST |
           RTL818X_RXCONF_NICMAC | RTL818X_RX_ONF_BSSID |
           (7 << 13 /* RX FIFO threshold NONE */) |
           (7 << 10 /* MAX RX DMA */) |
           RTL818X_RXCONF_RX_AUTORESETPHY | RTL818X_RXCONF_ONLYERLPKT | RTL818X_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 = RTL818X_TXCONF_HWSEQNUM | RTL818X_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, RTL8187_EEPROM_TXPWR_CHAN_6, &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 && CONFIG_NET_WLAN */


