Commit e85d0918 authored by Daniel Drake's avatar Daniel Drake Committed by Jeff Garzik

[PATCH] ZyDAS ZD1211 USB-WLAN driver

There are 60+ USB wifi adapters available on the market based on the ZyDAS
ZD1211 chip.

Unlike the predecessor (ZD1201), ZD1211 does not have a hardware MAC, so most
data operations are coordinated by the device driver. The ZD1211 chip sits
alongside an RF transceiver which is also controlled by the driver. Our driver
currently supports 2 RF types, we know of one other available in a few marketed
products which we will be supporting soon.

Our driver also supports the newer revision of ZD1211, called ZD1211B. The
initialization and RF operations are slightly different for the new revision,
but the main difference is 802.11e support. Our driver does not support the
QoS features yet, but we think we know how to use them.

This driver is based on ZyDAS's own GPL driver available from www.zydas.com.tw.
ZyDAS engineers have been responsive and supportive of our efforts, so thumbs
up to them. Additionally, the firmware is redistributable and they have
provided device specs.

This driver has been written primarily by Ulrich Kunitz and myself. Graham
Gower, Greg KH, Remco and Bryan Rittmeyer have also contributed. The
developers of ieee80211 and softmac have made our lives so much easier- thanks!

We maintain a small info-page: http://zd1211.ath.cx/wiki/DriverRewrite

If there is enough time for review, we would like to aim for inclusion in
2.6.18. The driver works nicely as a STA, and can connect to both open and
encrypted networks (we are using software-based encryption for now). We will
work towards supporting more advanced features in the future (ad-hoc, master
mode, 802.11a, ...).
Signed-off-by: default avatarDaniel Drake <dsd@gentoo.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 4a232e72
...@@ -550,6 +550,7 @@ config USB_ZD1201 ...@@ -550,6 +550,7 @@ config USB_ZD1201
source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/hostap/Kconfig"
source "drivers/net/wireless/bcm43xx/Kconfig" source "drivers/net/wireless/bcm43xx/Kconfig"
source "drivers/net/wireless/zd1211rw/Kconfig"
# yes, this works even when no drivers are selected # yes, this works even when no drivers are selected
config NET_WIRELESS config NET_WIRELESS
......
...@@ -36,6 +36,7 @@ obj-$(CONFIG_PRISM54) += prism54/ ...@@ -36,6 +36,7 @@ obj-$(CONFIG_PRISM54) += prism54/
obj-$(CONFIG_HOSTAP) += hostap/ obj-$(CONFIG_HOSTAP) += hostap/
obj-$(CONFIG_BCM43XX) += bcm43xx/ obj-$(CONFIG_BCM43XX) += bcm43xx/
obj-$(CONFIG_ZD1211RW) += zd1211rw/
# 16-bit wireless PCMCIA client drivers # 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
......
config ZD1211RW
tristate "ZyDAS ZD1211/ZD1211B USB-wireless support"
depends on USB && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL
select FW_LOADER
---help---
This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless
chip, present in many USB-wireless adapters.
Device firmware is required alongside this driver. You can download the
firmware distribution from http://zd1211.ath.cx/get-firmware
config ZD1211RW_DEBUG
bool "ZyDAS ZD1211 debugging"
depends on ZD1211RW
---help---
ZD1211 debugging messages. Choosing Y will result in additional debug
messages being saved to your kernel logs, which may help debug any
problems.
obj-$(CONFIG_ZD1211RW) += zd1211rw.o
zd1211rw-objs := zd_chip.o zd_ieee80211.o \
zd_mac.o zd_netdev.o \
zd_rf_al2230.o zd_rf_rf2959.o \
zd_rf.o zd_usb.o zd_util.o
ifeq ($(CONFIG_ZD1211RW_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
This diff is collapsed.
This diff is collapsed.
/* zd_def.h
*
* 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
*/
#ifndef _ZD_DEF_H
#define _ZD_DEF_H
#include <linux/kernel.h>
#include <linux/stringify.h>
#include <linux/device.h>
#include <linux/kernel.h>
#define dev_printk_f(level, dev, fmt, args...) \
dev_printk(level, dev, "%s() " fmt, __func__, ##args)
#ifdef DEBUG
# define dev_dbg_f(dev, fmt, args...) \
dev_printk_f(KERN_DEBUG, dev, fmt, ## args)
#else
# define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0)
#endif /* DEBUG */
#ifdef DEBUG
# define ZD_ASSERT(x) \
do { \
if (!(x)) { \
pr_debug("%s:%d ASSERT %s VIOLATED!\n", \
__FILE__, __LINE__, __stringify(x)); \
} \
} while (0)
#else
# define ZD_ASSERT(x) do { } while (0)
#endif
#endif /* _ZD_DEF_H */
/* zd_ieee80211.c
*
* 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
*/
/*
* A lot of this code is generic and should be moved into the upper layers
* at some point.
*/
#include <linux/errno.h>
#include <linux/wireless.h>
#include <linux/kernel.h>
#include <net/ieee80211.h>
#include "zd_def.h"
#include "zd_ieee80211.h"
#include "zd_mac.h"
static const struct channel_range channel_ranges[] = {
[0] = { 0, 0},
[ZD_REGDOMAIN_FCC] = { 1, 12},
[ZD_REGDOMAIN_IC] = { 1, 12},
[ZD_REGDOMAIN_ETSI] = { 1, 14},
[ZD_REGDOMAIN_JAPAN] = { 1, 14},
[ZD_REGDOMAIN_SPAIN] = { 1, 14},
[ZD_REGDOMAIN_FRANCE] = { 1, 14},
[ZD_REGDOMAIN_JAPAN_ADD] = {14, 15},
};
const struct channel_range *zd_channel_range(u8 regdomain)
{
if (regdomain >= ARRAY_SIZE(channel_ranges))
regdomain = 0;
return &channel_ranges[regdomain];
}
int zd_regdomain_supports_channel(u8 regdomain, u8 channel)
{
const struct channel_range *range = zd_channel_range(regdomain);
return range->start <= channel && channel < range->end;
}
int zd_regdomain_supported(u8 regdomain)
{
const struct channel_range *range = zd_channel_range(regdomain);
return range->start != 0;
}
/* Stores channel frequencies in MHz. */
static const u16 channel_frequencies[] = {
2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447,
2452, 2457, 2462, 2467, 2472, 2484,
};
#define NUM_CHANNELS ARRAY_SIZE(channel_frequencies)
static int compute_freq(struct iw_freq *freq, u32 mhz, u32 hz)
{
u32 factor;
freq->e = 0;
if (mhz >= 1000000000U) {
pr_debug("zd1211 mhz %u to large\n", mhz);
freq->m = 0;
return -EINVAL;
}
factor = 1000;
while (mhz >= factor) {
freq->e += 1;
factor *= 10;
}
factor /= 1000U;
freq->m = mhz * (1000000U/factor) + hz/factor;
return 0;
}
int zd_channel_to_freq(struct iw_freq *freq, u8 channel)
{
if (channel > NUM_CHANNELS) {
freq->m = 0;
freq->e = 0;
return -EINVAL;
}
if (!channel) {
freq->m = 0;
freq->e = 0;
return -EINVAL;
}
return compute_freq(freq, channel_frequencies[channel-1], 0);
}
static int freq_to_mhz(const struct iw_freq *freq)
{
u32 factor;
int e;
/* Such high frequencies are not supported. */
if (freq->e > 6)
return -EINVAL;
factor = 1;
for (e = freq->e; e > 0; --e) {
factor *= 10;
}
factor = 1000000U / factor;
if (freq->m % factor) {
return -EINVAL;
}
return freq->m / factor;
}
int zd_find_channel(u8 *channel, const struct iw_freq *freq)
{
int i, r;
u32 mhz;
if (!(freq->flags & IW_FREQ_FIXED))
return 0;
if (freq->m < 1000) {
if (freq->m > NUM_CHANNELS || freq->m == 0)
return -EINVAL;
*channel = freq->m;
return 1;
}
r = freq_to_mhz(freq);
if (r < 0)
return r;
mhz = r;
for (i = 0; i < NUM_CHANNELS; i++) {
if (mhz == channel_frequencies[i]) {
*channel = i+1;
return 1;
}
}
return -EINVAL;
}
int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain)
{
struct ieee80211_geo geo;
const struct channel_range *range;
int i;
u8 channel;
dev_dbg(zd_mac_dev(zd_netdev_mac(ieee->dev)),
"regdomain %#04x\n", regdomain);
range = zd_channel_range(regdomain);
if (range->start == 0) {
dev_err(zd_mac_dev(zd_netdev_mac(ieee->dev)),
"zd1211 regdomain %#04x not supported\n",
regdomain);
return -EINVAL;
}
memset(&geo, 0, sizeof(geo));
for (i = 0, channel = range->start; channel < range->end; channel++) {
struct ieee80211_channel *chan = &geo.bg[i++];
chan->freq = channel_frequencies[channel - 1];
chan->channel = channel;
}
geo.bg_channels = i;
memcpy(geo.name, "XX ", 4);
ieee80211_set_geo(ieee, &geo);
return 0;
}
#ifndef _ZD_IEEE80211_H
#define _ZD_IEEE80211_H
#include <net/ieee80211.h>
#include "zd_types.h"
/* Additional definitions from the standards.
*/
#define ZD_REGDOMAIN_FCC 0x10
#define ZD_REGDOMAIN_IC 0x20
#define ZD_REGDOMAIN_ETSI 0x30
#define ZD_REGDOMAIN_SPAIN 0x31
#define ZD_REGDOMAIN_FRANCE 0x32
#define ZD_REGDOMAIN_JAPAN_ADD 0x40
#define ZD_REGDOMAIN_JAPAN 0x41
enum {
MIN_CHANNEL24 = 1,
MAX_CHANNEL24 = 14,
};
struct channel_range {
u8 start;
u8 end; /* exclusive (channel must be less than end) */
};
struct iw_freq;
int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain);
const struct channel_range *zd_channel_range(u8 regdomain);
int zd_regdomain_supports_channel(u8 regdomain, u8 channel);
int zd_regdomain_supported(u8 regdomain);
/* for 2.4 GHz band */
int zd_channel_to_freq(struct iw_freq *freq, u8 channel);
int zd_find_channel(u8 *channel, const struct iw_freq *freq);
#define ZD_PLCP_SERVICE_LENGTH_EXTENSION 0x80
struct ofdm_plcp_header {
u8 prefix[3];
__le16 service;
} __attribute__((packed));
static inline u8 zd_ofdm_plcp_header_rate(
const struct ofdm_plcp_header *header)
{
return header->prefix[0] & 0xf;
}
#define ZD_OFDM_RATE_6M 0xb
#define ZD_OFDM_RATE_9M 0xf
#define ZD_OFDM_RATE_12M 0xa
#define ZD_OFDM_RATE_18M 0xe
#define ZD_OFDM_RATE_24M 0x9
#define ZD_OFDM_RATE_36M 0xd
#define ZD_OFDM_RATE_48M 0x8
#define ZD_OFDM_RATE_54M 0xc
struct cck_plcp_header {
u8 signal;
u8 service;
__le16 length;
__le16 crc16;
} __attribute__((packed));
static inline u8 zd_cck_plcp_header_rate(const struct cck_plcp_header *header)
{
return header->signal;
}
#define ZD_CCK_SIGNAL_1M 0x0a
#define ZD_CCK_SIGNAL_2M 0x14
#define ZD_CCK_SIGNAL_5M5 0x37
#define ZD_CCK_SIGNAL_11M 0x6e
enum ieee80211_std {
IEEE80211B = 0x01,
IEEE80211A = 0x02,
IEEE80211G = 0x04,
};
#endif /* _ZD_IEEE80211_H */
This diff is collapsed.
/* zd_mac.c
*
* 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
*/
#ifndef _ZD_MAC_H
#define _ZD_MAC_H
#include <linux/wireless.h>
#include <linux/kernel.h>
#include <net/ieee80211.h>
#include <net/ieee80211softmac.h>
#include "zd_chip.h"
#include "zd_netdev.h"
struct zd_ctrlset {
u8 modulation;
__le16 tx_length;
u8 control;
/* stores only the difference to tx_length on ZD1211B */
__le16 packet_length;
__le16 current_length;
u8 service;
__le16 next_frame_length;
} __attribute__((packed));
#define ZD_CS_RESERVED_SIZE 25
/* zd_crtlset field modulation */
#define ZD_CS_RATE_MASK 0x0f
#define ZD_CS_TYPE_MASK 0x10
#define ZD_CS_RATE(modulation) ((modulation) & ZD_CS_RATE_MASK)
#define ZD_CS_TYPE(modulation) ((modulation) & ZD_CS_TYPE_MASK)
#define ZD_CS_CCK 0x00
#define ZD_CS_OFDM 0x10
#define ZD_CS_CCK_RATE_1M 0x00
#define ZD_CS_CCK_RATE_2M 0x01
#define ZD_CS_CCK_RATE_5_5M 0x02
#define ZD_CS_CCK_RATE_11M 0x03
/* The rates for OFDM are encoded as in the PLCP header. Use ZD_OFDM_RATE_*.
*/
/* bit 5 is preamble (when in CCK mode), or a/g selection (when in OFDM mode) */
#define ZD_CS_CCK_PREA_LONG 0x00
#define ZD_CS_CCK_PREA_SHORT 0x20
#define ZD_CS_OFDM_MODE_11G 0x00
#define ZD_CS_OFDM_MODE_11A 0x20
/* zd_ctrlset control field */
#define ZD_CS_NEED_RANDOM_BACKOFF 0x01
#define ZD_CS_MULTICAST 0x02
#define ZD_CS_FRAME_TYPE_MASK 0x0c
#define ZD_CS_DATA_FRAME 0x00
#define ZD_CS_PS_POLL_FRAME 0x04
#define ZD_CS_MANAGEMENT_FRAME 0x08
#define ZD_CS_NO_SEQUENCE_CTL_FRAME 0x0c
#define ZD_CS_WAKE_DESTINATION 0x10
#define ZD_CS_RTS 0x20
#define ZD_CS_ENCRYPT 0x40
#define ZD_CS_SELF_CTS 0x80
/* Incoming frames are prepended by a PLCP header */
#define ZD_PLCP_HEADER_SIZE 5
struct rx_length_info {
__le16 length[3];
__le16 tag;
} __attribute__((packed));
#define RX_LENGTH_INFO_TAG 0x697e
struct rx_status {
/* rssi */
u8 signal_strength;
u8 signal_quality_cck;
u8 signal_quality_ofdm;
u8 decryption_type;
u8 frame_status;
} __attribute__((packed));
/* rx_status field decryption_type */
#define ZD_RX_NO_WEP 0
#define ZD_RX_WEP64 1
#define ZD_RX_TKIP 2
#define ZD_RX_AES 4
#define ZD_RX_WEP128 5
#define ZD_RX_WEP256 6
/* rx_status field frame_status */
#define ZD_RX_FRAME_MODULATION_MASK 0x01
#define ZD_RX_CCK 0x00
#define ZD_RX_OFDM 0x01
#define ZD_RX_TIMEOUT_ERROR 0x02
#define ZD_RX_FIFO_OVERRUN_ERROR 0x04
#define ZD_RX_DECRYPTION_ERROR 0x08
#define ZD_RX_CRC32_ERROR 0x10
#define ZD_RX_NO_ADDR1_MATCH_ERROR 0x20
#define ZD_RX_CRC16_ERROR 0x40
#define ZD_RX_ERROR 0x80
enum mac_flags {
MAC_FIXED_CHANNEL = 0x01,
};
struct zd_mac {
struct net_device *netdev;
struct zd_chip chip;
spinlock_t lock;
/* Unlocked reading possible */
struct iw_statistics iw_stats;
u8 qual_average;
u8 rssi_average;
u8 regdomain;
u8 default_regdomain;
u8 requested_channel;
};
static inline struct ieee80211_device *zd_mac_to_ieee80211(struct zd_mac *mac)
{
return zd_netdev_ieee80211(mac->netdev);
}
static inline struct zd_mac *zd_netdev_mac(struct net_device *netdev)
{
return ieee80211softmac_priv(netdev);
}
static inline struct zd_mac *zd_chip_to_mac(struct zd_chip *chip)
{
return container_of(chip, struct zd_mac, chip);
}
static inline struct zd_mac *zd_usb_to_mac(struct zd_usb *usb)
{
return zd_chip_to_mac(zd_usb_to_chip(usb));
}
#define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip))
int zd_mac_init(struct zd_mac *mac,
struct net_device *netdev,
struct usb_interface *intf);
void zd_mac_clear(struct zd_mac *mac);
int zd_mac_init_hw(struct zd_mac *mac, u8 device_type);
int zd_mac_open(struct net_device *netdev);
int zd_mac_stop(struct net_device *netdev);
int zd_mac_set_mac_address(struct net_device *dev, void *p);
int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length);
int zd_mac_set_regdomain(struct zd_mac *zd_mac, u8 regdomain);
u8 zd_mac_get_regdomain(struct zd_mac *zd_mac);
int zd_mac_request_channel(struct zd_mac *mac, u8 channel);
int zd_mac_get_channel(struct zd_mac *mac, u8 *channel, u8 *flags);
int zd_mac_set_mode(struct zd_mac *mac, u32 mode);
int zd_mac_get_mode(struct zd_mac *mac, u32 *mode);
int zd_mac_get_range(struct zd_mac *mac, struct iw_range *range);
struct iw_statistics *zd_mac_get_wireless_stats(struct net_device *ndev);
#ifdef DEBUG
void zd_dump_rx_status(const struct rx_status *status);
#else
#define zd_dump_rx_status(status)
#endif /* DEBUG */
#endif /* _ZD_MAC_H */
/* zd_netdev.c
*
* 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
*/
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <net/ieee80211.h>
#include <net/ieee80211softmac.h>
#include <net/ieee80211softmac_wx.h>
#include <net/iw_handler.h>
#include "zd_def.h"
#include "zd_netdev.h"
#include "zd_mac.h"
#include "zd_ieee80211.h"
/* Region 0 means reset regdomain to default. */
static int zd_set_regdomain(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *req, char *extra)
{
const u8 *regdomain = (u8 *)req;
return zd_mac_set_regdomain(zd_netdev_mac(netdev), *regdomain);
}
static int zd_get_regdomain(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *req, char *extra)
{
u8 *regdomain = (u8 *)req;
if (!regdomain)
return -EINVAL;
*regdomain = zd_mac_get_regdomain(zd_netdev_mac(netdev));
return 0;
}
static const struct iw_priv_args zd_priv_args[] = {
{
.cmd = ZD_PRIV_SET_REGDOMAIN,
.set_args = IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
.name = "set_regdomain",
},
{
.cmd = ZD_PRIV_GET_REGDOMAIN,
.get_args = IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
.name = "get_regdomain",
},
};
#define PRIV_OFFSET(x) [(x)-SIOCIWFIRSTPRIV]
static const iw_handler zd_priv_handler[] = {
PRIV_OFFSET(ZD_PRIV_SET_REGDOMAIN) = zd_set_regdomain,
PRIV_OFFSET(ZD_PRIV_GET_REGDOMAIN) = zd_get_regdomain,
};
static int iw_get_name(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *req, char *extra)
{
/* FIXME: check whether 802.11a will also supported, add also
* zd1211B, if we support it.
*/
strlcpy(req->name, "802.11g zd1211", IFNAMSIZ);
return 0;
}
static int iw_set_freq(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *req, char *extra)
{
int r;
struct zd_mac *mac = zd_netdev_mac(netdev);
struct iw_freq *freq = &req->freq;
u8 channel;
r = zd_find_channel(&channel, freq);
if (r < 0)
return r;
r = zd_mac_request_channel(mac, channel);
return r;
}
static int iw_get_freq(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *req, char *extra)
{
int r;
struct zd_mac *mac = zd_netdev_mac(netdev);
struct iw_freq *freq = &req->freq;
u8 channel;
u8 flags;
r = zd_mac_get_channel(mac, &channel, &flags);
if (r)
return r;
freq->flags = (flags & MAC_FIXED_CHANNEL) ?
IW_FREQ_FIXED : IW_FREQ_AUTO;
dev_dbg_f(zd_mac_dev(mac), "channel %s\n",
(flags & MAC_FIXED_CHANNEL) ? "fixed" : "auto");
return zd_channel_to_freq(freq, channel);
}
static int iw_set_mode(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *req, char *extra)
{
return zd_mac_set_mode(zd_netdev_mac(netdev), req->mode);
}
static int iw_get_mode(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *req, char *extra)
{
return zd_mac_get_mode(zd_netdev_mac(netdev), &req->mode);
}
static int iw_get_range(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *req, char *extra)
{
struct iw_range *range = (struct iw_range *)extra;
dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), "\n");
req->data.length = sizeof(*range);
return zd_mac_get_range(zd_netdev_mac(netdev), range);
}
static int iw_set_encode(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
return ieee80211_wx_set_encode(zd_netdev_ieee80211(netdev), info,
data, extra);
}
static int iw_get_encode(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
return ieee80211_wx_get_encode(zd_netdev_ieee80211(netdev), info,
data, extra);
}
static int iw_set_encodeext(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
return ieee80211_wx_set_encodeext(zd_netdev_ieee80211(netdev), info,
data, extra);
}
static int iw_get_encodeext(struct net_device *netdev,
struct iw_request_info *info,
union iwreq_data *data,
char *extra)
{
return ieee80211_wx_get_encodeext(zd_netdev_ieee80211(netdev), info,
data, extra);
}
#define WX(x) [(x)-SIOCIWFIRST]
static const iw_handler zd_standard_iw_handlers[] = {
WX(SIOCGIWNAME) = iw_get_name,
WX(SIOCSIWFREQ) = iw_set_freq,
WX(SIOCGIWFREQ) = iw_get_freq,
WX(SIOCSIWMODE) = iw_set_mode,
WX(SIOCGIWMODE) = iw_get_mode,
WX(SIOCGIWRANGE) = iw_get_range,
WX(SIOCSIWENCODE) = iw_set_encode,
WX(SIOCGIWENCODE) = iw_get_encode,
WX(SIOCSIWENCODEEXT) = iw_set_encodeext,
WX(SIOCGIWENCODEEXT) = iw_get_encodeext,
WX(SIOCSIWAUTH) = ieee80211_wx_set_auth,
WX(SIOCGIWAUTH) = ieee80211_wx_get_auth,
WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan,
WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results,
WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid,
WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid,
WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap,
WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap,
WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate,
WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate,
WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie,
WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie,
WX(SIOCSIWMLME) = ieee80211softmac_wx_set_mlme,
};
static const struct iw_handler_def iw_handler_def = {
.standard = zd_standard_iw_handlers,
.num_standard = ARRAY_SIZE(zd_standard_iw_handlers),
.private = zd_priv_handler,
.num_private = ARRAY_SIZE(zd_priv_handler),
.private_args = zd_priv_args,
.num_private_args = ARRAY_SIZE(zd_priv_args),
.get_wireless_stats = zd_mac_get_wireless_stats,
};
struct net_device *zd_netdev_alloc(struct usb_interface *intf)
{
int r;
struct net_device *netdev;
struct zd_mac *mac;
netdev = alloc_ieee80211softmac(sizeof(struct zd_mac));
if (!netdev) {
dev_dbg_f(&intf->dev, "out of memory\n");
return NULL;
}
mac = zd_netdev_mac(netdev);
r = zd_mac_init(mac, netdev, intf);
if (r) {
usb_set_intfdata(intf, NULL);
free_ieee80211(netdev);
return NULL;
}
SET_MODULE_OWNER(netdev);
SET_NETDEV_DEV(netdev, &intf->dev);
dev_dbg_f(&intf->dev, "netdev->flags %#06hx\n", netdev->flags);
dev_dbg_f(&intf->dev, "netdev->features %#010lx\n", netdev->features);
netdev->open = zd_mac_open;
netdev->stop = zd_mac_stop;
/* netdev->get_stats = */
/* netdev->set_multicast_list = */
netdev->set_mac_address = zd_mac_set_mac_address;
netdev->wireless_handlers = &iw_handler_def;
/* netdev->ethtool_ops = */
return netdev;
}
void zd_netdev_free(struct net_device *netdev)
{
if (!netdev)
return;
zd_mac_clear(zd_netdev_mac(netdev));
free_ieee80211(netdev);
}
void zd_netdev_disconnect(struct net_device *netdev)
{
unregister_netdev(netdev);
}
/* zd_netdev.h: Header for net device related functions.
*
* 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
*/
#ifndef _ZD_NETDEV_H
#define _ZD_NETDEV_H
#include <linux/usb.h>
#include <linux/netdevice.h>
#include <net/ieee80211.h>
#define ZD_PRIV_SET_REGDOMAIN (SIOCIWFIRSTPRIV)
#define ZD_PRIV_GET_REGDOMAIN (SIOCIWFIRSTPRIV+1)
static inline struct ieee80211_device *zd_netdev_ieee80211(
struct net_device *ndev)
{
return netdev_priv(ndev);
}
static inline struct net_device *zd_ieee80211_to_netdev(
struct ieee80211_device *ieee)
{
return ieee->dev;
}
struct net_device *zd_netdev_alloc(struct usb_interface *intf);
void zd_netdev_free(struct net_device *netdev);
void zd_netdev_disconnect(struct net_device *netdev);
#endif /* _ZD_NETDEV_H */
/* zd_rf.c
*
* 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
*/
#include <linux/errno.h>
#include <linux/string.h>
#include "zd_def.h"
#include "zd_rf.h"
#include "zd_ieee80211.h"
#include "zd_chip.h"
static const char *rfs[] = {
[0] = "unknown RF0",
[1] = "unknown RF1",
[UW2451_RF] = "UW2451_RF",
[UCHIP_RF] = "UCHIP_RF",
[AL2230_RF] = "AL2230_RF",
[AL7230B_RF] = "AL7230B_RF",
[THETA_RF] = "THETA_RF",
[AL2210_RF] = "AL2210_RF",
[MAXIM_NEW_RF] = "MAXIM_NEW_RF",
[UW2453_RF] = "UW2453_RF",
[AL2230S_RF] = "AL2230S_RF",
[RALINK_RF] = "RALINK_RF",
[INTERSIL_RF] = "INTERSIL_RF",
[RF2959_RF] = "RF2959_RF",
[MAXIM_NEW2_RF] = "MAXIM_NEW2_RF",
[PHILIPS_RF] = "PHILIPS_RF",
};
const char *zd_rf_name(u8 type)
{
if (type & 0xf0)
type = 0;
return rfs[type];
}
void zd_rf_init(struct zd_rf *rf)
{
memset(rf, 0, sizeof(*rf));
}
void zd_rf_clear(struct zd_rf *rf)
{
memset(rf, 0, sizeof(*rf));
}
int zd_rf_init_hw(struct zd_rf *rf, u8 type)
{
int r, t;
struct zd_chip *chip = zd_rf_to_chip(rf);
ZD_ASSERT(mutex_is_locked(&chip->mutex));
switch (type) {
case RF2959_RF:
r = zd_rf_init_rf2959(rf);
if (r)
return r;
break;
case AL2230_RF:
r = zd_rf_init_al2230(rf);
if (r)
return r;
break;
default:
dev_err(zd_chip_dev(chip),
"RF %s %#x is not supported\n", zd_rf_name(type), type);
rf->type = 0;
return -ENODEV;
}
rf->type = type;
r = zd_chip_lock_phy_regs(chip);
if (r)
return r;
t = rf->init_hw(rf);
r = zd_chip_unlock_phy_regs(chip);
if (t)
r = t;
return r;
}
int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size)
{
return scnprintf(buffer, size, "%s", zd_rf_name(rf->type));
}
int zd_rf_set_channel(struct zd_rf *rf, u8 channel)
{
int r;
ZD_ASSERT(mutex_is_locked(&zd_rf_to_chip(rf)->mutex));
if (channel < MIN_CHANNEL24)
return -EINVAL;
if (channel > MAX_CHANNEL24)
return -EINVAL;
dev_dbg_f(zd_chip_dev(zd_rf_to_chip(rf)), "channel: %d\n", channel);
r = rf->set_channel(rf, channel);
if (r >= 0)
rf->channel = channel;
return r;
}
int zd_switch_radio_on(struct zd_rf *rf)
{
int r, t;
struct zd_chip *chip = zd_rf_to_chip(rf);
ZD_ASSERT(mutex_is_locked(&chip->mutex));
r = zd_chip_lock_phy_regs(chip);
if (r)
return r;
t = rf->switch_radio_on(rf);
r = zd_chip_unlock_phy_regs(chip);
if (t)
r = t;
return r;
}
int zd_switch_radio_off(struct zd_rf *rf)
{
int r, t;
struct zd_chip *chip = zd_rf_to_chip(rf);
/* TODO: move phy regs handling to zd_chip */
ZD_ASSERT(mutex_is_locked(&chip->mutex));
r = zd_chip_lock_phy_regs(chip);
if (r)
return r;
t = rf->switch_radio_off(rf);
r = zd_chip_unlock_phy_regs(chip);
if (t)
r = t;
return r;
}
/* zd_rf.h
*
* 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
*/
#ifndef _ZD_RF_H
#define _ZD_RF_H
#include "zd_types.h"
#define UW2451_RF 0x2
#define UCHIP_RF 0x3
#define AL2230_RF 0x4
#define AL7230B_RF 0x5 /* a,b,g */
#define THETA_RF 0x6
#define AL2210_RF 0x7
#define MAXIM_NEW_RF 0x8
#define UW2453_RF 0x9
#define AL2230S_RF 0xa
#define RALINK_RF 0xb
#define INTERSIL_RF 0xc
#define RF2959_RF 0xd
#define MAXIM_NEW2_RF 0xe
#define PHILIPS_RF 0xf
#define RF_CHANNEL(ch) [(ch)-1]
/* Provides functions of the RF transceiver. */
enum {
RF_REG_BITS = 6,
RF_VALUE_BITS = 18,
RF_RV_BITS = RF_REG_BITS + RF_VALUE_BITS,
};
struct zd_rf {
u8 type;
u8 channel;
/*
* Whether this RF should patch the 6M band edge
* (assuming E2P_POD agrees)
*/
u8 patch_6m_band_edge:1;
/* RF-specific functions */
int (*init_hw)(struct zd_rf *rf);
int (*set_channel)(struct zd_rf *rf, u8 channel);
int (*switch_radio_on)(struct zd_rf *rf);
int (*switch_radio_off)(struct zd_rf *rf);
};
const char *zd_rf_name(u8 type);
void zd_rf_init(struct zd_rf *rf);
void zd_rf_clear(struct zd_rf *rf);
int zd_rf_init_hw(struct zd_rf *rf, u8 type);
int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size);
int zd_rf_set_channel(struct zd_rf *rf, u8 channel);
int zd_switch_radio_on(struct zd_rf *rf);
int zd_switch_radio_off(struct zd_rf *rf);
/* Functions for individual RF chips */
int zd_rf_init_rf2959(struct zd_rf *rf);
int zd_rf_init_al2230(struct zd_rf *rf);
#endif /* _ZD_RF_H */
/* zd_rf_al2230.c: Functions for the AL2230 RF controller
*
* 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
*/
#include <linux/kernel.h>
#include "zd_rf.h"
#include "zd_usb.h"
#include "zd_chip.h"
static const u32 al2230_table[][3] = {
RF_CHANNEL( 1) = { 0x03f790, 0x033331, 0x00000d, },
RF_CHANNEL( 2) = { 0x03f790, 0x0b3331, 0x00000d, },
RF_CHANNEL( 3) = { 0x03e790, 0x033331, 0x00000d, },
RF_CHANNEL( 4) = { 0x03e790, 0x0b3331, 0x00000d, },
RF_CHANNEL( 5) = { 0x03f7a0, 0x033331, 0x00000d, },
RF_CHANNEL( 6) = { 0x03f7a0, 0x0b3331, 0x00000d, },
RF_CHANNEL( 7) = { 0x03e7a0, 0x033331, 0x00000d, },
RF_CHANNEL( 8) = { 0x03e7a0, 0x0b3331, 0x00000d, },
RF_CHANNEL( 9) = { 0x03f7b0, 0x033331, 0x00000d, },
RF_CHANNEL(10) = { 0x03f7b0, 0x0b3331, 0x00000d, },
RF_CHANNEL(11) = { 0x03e7b0, 0x033331, 0x00000d, },
RF_CHANNEL(12) = { 0x03e7b0, 0x0b3331, 0x00000d, },
RF_CHANNEL(13) = { 0x03f7c0, 0x033331, 0x00000d, },
RF_CHANNEL(14) = { 0x03e7c0, 0x066661, 0x00000d, },
};
static int zd1211_al2230_init_hw(struct zd_rf *rf)
{
int r;
struct zd_chip *chip = zd_rf_to_chip(rf);
static const struct zd_ioreq16 ioreqs[] = {
{ CR15, 0x20 }, { CR23, 0x40 }, { CR24, 0x20 },
{ CR26, 0x11 }, { CR28, 0x3e }, { CR29, 0x00 },
{ CR44, 0x33 }, { CR106, 0x2a }, { CR107, 0x1a },
{ CR109, 0x09 }, { CR110, 0x27 }, { CR111, 0x2b },
{ CR112, 0x2b }, { CR119, 0x0a }, { CR10, 0x89 },
/* for newest (3rd cut) AL2300 */
{ CR17, 0x28 },
{ CR26, 0x93 }, { CR34, 0x30 },
/* for newest (3rd cut) AL2300 */
{ CR35, 0x3e },
{ CR41, 0x24 }, { CR44, 0x32 },
/* for newest (3rd cut) AL2300 */
{ CR46, 0x96 },
{ CR47, 0x1e }, { CR79, 0x58 }, { CR80, 0x30 },
{ CR81, 0x30 }, { CR87, 0x0a }, { CR89, 0x04 },
{ CR92, 0x0a }, { CR99, 0x28 }, { CR100, 0x00 },
{ CR101, 0x13 }, { CR102, 0x27 }, { CR106, 0x24 },
{ CR107, 0x2a }, { CR109, 0x09 }, { CR110, 0x13 },
{ CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 },
{ CR114, 0x27 },
/* for newest (3rd cut) AL2300 */
{ CR115, 0x24 },
{ CR116, 0x24 }, { CR117, 0xf4 }, { CR118, 0xfc },
{ CR119, 0x10 }, { CR120, 0x4f }, { CR121, 0x77 },
{ CR122, 0xe0 }, { CR137, 0x88 }, { CR252, 0xff },
{ CR253, 0xff },
/* These following happen separately in the vendor driver */
{ },
/* shdnb(PLL_ON)=0 */
{ CR251, 0x2f },
/* shdnb(PLL_ON)=1 */
{ CR251, 0x3f },
{ CR138, 0x28 }, { CR203, 0x06 },
};
static const u32 rv[] = {
/* Channel 1 */
0x03f790,
0x033331,
0x00000d,
0x0b3331,
0x03b812,
0x00fff3,
0x000da4,
0x0f4dc5, /* fix freq shift, 0x04edc5 */
0x0805b6,
0x011687,
0x000688,
0x0403b9, /* external control TX power (CR31) */
0x00dbba,
0x00099b,
0x0bdffc,
0x00000d,
0x00500f,
/* These writes happen separately in the vendor driver */
0x00d00f,
0x004c0f,
0x00540f,
0x00700f,
0x00500f,
};
r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
if (r)
return r;
r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
if (r)
return r;
return 0;
}
static int zd1211b_al2230_init_hw(struct zd_rf *rf)
{
int r;
struct zd_chip *chip = zd_rf_to_chip(rf);
static const struct zd_ioreq16 ioreqs1[] = {
{ CR10, 0x89 }, { CR15, 0x20 },
{ CR17, 0x2B }, /* for newest(3rd cut) AL2230 */
{ CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x93 },
{ CR28, 0x3e }, { CR29, 0x00 },
{ CR33, 0x28 }, /* 5621 */
{ CR34, 0x30 },
{ CR35, 0x3e }, /* for newest(3rd cut) AL2230 */
{ CR41, 0x24 }, { CR44, 0x32 },
{ CR46, 0x99 }, /* for newest(3rd cut) AL2230 */
{ CR47, 0x1e },
/* ZD1211B 05.06.10 */
{ CR48, 0x00 }, { CR49, 0x00 }, { CR51, 0x01 },
{ CR52, 0x80 }, { CR53, 0x7e }, { CR65, 0x00 },
{ CR66, 0x00 }, { CR67, 0x00 }, { CR68, 0x00 },
{ CR69, 0x28 },
{ CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 },
{ CR87, 0x0a }, { CR89, 0x04 },
{ CR91, 0x00 }, /* 5621 */
{ CR92, 0x0a },
{ CR98, 0x8d }, /* 4804, for 1212 new algorithm */
{ CR99, 0x00 }, /* 5621 */
{ CR101, 0x13 }, { CR102, 0x27 },
{ CR106, 0x24 }, /* for newest(3rd cut) AL2230 */
{ CR107, 0x2a },
{ CR109, 0x13 }, /* 4804, for 1212 new algorithm */
{ CR110, 0x1f }, /* 4804, for 1212 new algorithm */
{ CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 },
{ CR114, 0x27 },
{ CR115, 0x26 }, /* 24->26 at 4902 for newest(3rd cut) AL2230 */
{ CR116, 0x24 },
{ CR117, 0xfa }, /* for 1211b */
{ CR118, 0xfa }, /* for 1211b */
{ CR119, 0x10 },
{ CR120, 0x4f },
{ CR121, 0x6c }, /* for 1211b */
{ CR122, 0xfc }, /* E0->FC at 4902 */
{ CR123, 0x57 }, /* 5623 */
{ CR125, 0xad }, /* 4804, for 1212 new algorithm */
{ CR126, 0x6c }, /* 5614 */
{ CR127, 0x03 }, /* 4804, for 1212 new algorithm */
{ CR137, 0x50 }, /* 5614 */
{ CR138, 0xa8 },
{ CR144, 0xac }, /* 5621 */
{ CR150, 0x0d }, { CR252, 0x00 }, { CR253, 0x00 },
};
static const u32 rv1[] = {
/* channel 1 */
0x03f790,
0x033331,
0x00000d,
0x0b3331,
0x03b812,
0x00fff3,
0x0005a4,
0x0f4dc5, /* fix freq shift 0x044dc5 */
0x0805b6,
0x0146c7,
0x000688,
0x0403b9, /* External control TX power (CR31) */
0x00dbba,
0x00099b,
0x0bdffc,
0x00000d,
0x00580f,
};
static const struct zd_ioreq16 ioreqs2[] = {
{ CR47, 0x1e }, { CR_RFCFG, 0x03 },
};
static const u32 rv2[] = {
0x00880f,
0x00080f,
};
static const struct zd_ioreq16 ioreqs3[] = {
{ CR_RFCFG, 0x00 }, { CR47, 0x1e }, { CR251, 0x7f },
};
static const u32 rv3[] = {
0x00d80f,
0x00780f,
0x00580f,
};
static const struct zd_ioreq16 ioreqs4[] = {
{ CR138, 0x28 }, { CR203, 0x06 },
};
r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1));
if (r)
return r;
r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS);
if (r)
return r;
r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2));
if (r)
return r;
r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS);
if (r)
return r;
r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3));
if (r)
return r;
r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS);
if (r)
return r;
return zd_iowrite16a_locked(chip, ioreqs4, ARRAY_SIZE(ioreqs4));
}
static int al2230_set_channel(struct zd_rf *rf, u8 channel)
{
int r;
const u32 *rv = al2230_table[channel-1];
struct zd_chip *chip = zd_rf_to_chip(rf);
static const struct zd_ioreq16 ioreqs[] = {
{ CR138, 0x28 },
{ CR203, 0x06 },
};
r = zd_rfwritev_locked(chip, rv, 3, RF_RV_BITS);
if (r)
return r;
return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
}
static int zd1211_al2230_switch_radio_on(struct zd_rf *rf)
{
struct zd_chip *chip = zd_rf_to_chip(rf);
static const struct zd_ioreq16 ioreqs[] = {
{ CR11, 0x00 },
{ CR251, 0x3f },
};
return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
}
static int zd1211b_al2230_switch_radio_on(struct zd_rf *rf)
{
struct zd_chip *chip = zd_rf_to_chip(rf);
static const struct zd_ioreq16 ioreqs[] = {
{ CR11, 0x00 },
{ CR251, 0x7f },
};
return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
}
static int al2230_switch_radio_off(struct zd_rf *rf)
{
struct zd_chip *chip = zd_rf_to_chip(rf);
static const struct zd_ioreq16 ioreqs[] = {
{ CR11, 0x04 },
{ CR251, 0x2f },
};
return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
}
int zd_rf_init_al2230(struct zd_rf *rf)
{
struct zd_chip *chip = zd_rf_to_chip(rf);
rf->set_channel = al2230_set_channel;
rf->switch_radio_off = al2230_switch_radio_off;
if (chip->is_zd1211b) {
rf->init_hw = zd1211b_al2230_init_hw;
rf->switch_radio_on = zd1211b_al2230_switch_radio_on;
} else {
rf->init_hw = zd1211_al2230_init_hw;
rf->switch_radio_on = zd1211_al2230_switch_radio_on;
}
rf->patch_6m_band_edge = 1;
return 0;
}
This diff is collapsed.
/* zd_types.h
*
* 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
*/
#ifndef _ZD_TYPES_H
#define _ZD_TYPES_H
#include <linux/types.h>
/* We have three register spaces mapped into the overall USB address space of
* 64K words (16-bit values). There is the control register space of
* double-word registers, the eeprom register space and the firmware register
* space. The control register space is byte mapped, the others are word
* mapped.
*
* For that reason, we are using byte offsets for control registers and word
* offsets for everything else.
*/
typedef u32 __nocast zd_addr_t;
enum {
ADDR_BASE_MASK = 0xff000000,
ADDR_OFFSET_MASK = 0x0000ffff,
ADDR_ZERO_MASK = 0x00ff0000,
NULL_BASE = 0x00000000,
USB_BASE = 0x01000000,
CR_BASE = 0x02000000,
CR_MAX_OFFSET = 0x0b30,
E2P_BASE = 0x03000000,
E2P_MAX_OFFSET = 0x007e,
FW_BASE = 0x04000000,
FW_MAX_OFFSET = 0x0005,
};
#define ZD_ADDR_BASE(addr) ((u32)(addr) & ADDR_BASE_MASK)
#define ZD_OFFSET(addr) ((u32)(addr) & ADDR_OFFSET_MASK)
#define ZD_ADDR(base, offset) \
((zd_addr_t)(((base) & ADDR_BASE_MASK) | ((offset) & ADDR_OFFSET_MASK)))
#define ZD_NULL_ADDR ((zd_addr_t)0)
#define USB_REG(offset) ZD_ADDR(USB_BASE, offset) /* word addressing */
#define CTL_REG(offset) ZD_ADDR(CR_BASE, offset) /* byte addressing */
#define E2P_REG(offset) ZD_ADDR(E2P_BASE, offset) /* word addressing */
#define FW_REG(offset) ZD_ADDR(FW_BASE, offset) /* word addressing */
static inline zd_addr_t zd_inc_word(zd_addr_t addr)
{
u32 base = ZD_ADDR_BASE(addr);
u32 offset = ZD_OFFSET(addr);
offset += base == CR_BASE ? 2 : 1;
return base | offset;
}
#endif /* _ZD_TYPES_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment