9
0
Fork 0
nuttx-bb/misc/drivers/rtl8187x/rtl8187x.c

4214 lines
125 KiB
C
Executable File

/****************************************************************************
* 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 */