summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/zydas
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:49:45 +0000
commit2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch)
tree848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/net/wireless/zydas
parentInitial commit. (diff)
downloadlinux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz
linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/net/wireless/zydas')
-rw-r--r--drivers/net/wireless/zydas/Kconfig36
-rw-r--r--drivers/net/wireless/zydas/Makefile4
-rw-r--r--drivers/net/wireless/zydas/zd1201.c1905
-rw-r--r--drivers/net/wireless/zydas/zd1201.h144
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/Kconfig20
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/Makefile10
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_chip.c1547
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_chip.h971
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_def.h57
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_mac.c1538
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_mac.h315
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf.c169
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf.h98
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c431
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c482
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c269
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c527
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_usb.c2030
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_usb.h280
19 files changed, 10833 insertions, 0 deletions
diff --git a/drivers/net/wireless/zydas/Kconfig b/drivers/net/wireless/zydas/Kconfig
new file mode 100644
index 000000000..08574433d
--- /dev/null
+++ b/drivers/net/wireless/zydas/Kconfig
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config WLAN_VENDOR_ZYDAS
+ bool "ZyDAS devices"
+ default y
+ help
+ If you have a wireless card belonging to this class, say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all the
+ questions about these cards. If you say Y, you will be asked for
+ your specific card in the following questions.
+
+if WLAN_VENDOR_ZYDAS
+
+config USB_ZD1201
+ tristate "USB ZD1201 based Wireless device support"
+ depends on CFG80211 && USB
+ select WIRELESS_EXT
+ select WEXT_PRIV
+ select FW_LOADER
+ help
+ Say Y if you want to use wireless LAN adapters based on the ZyDAS
+ ZD1201 chip.
+
+ This driver makes the adapter appear as a normal Ethernet interface,
+ typically on wlan0.
+
+ The zd1201 device requires external firmware to be loaded.
+ This can be found at http://linux-lc100020.sourceforge.net/
+
+ To compile this driver as a module, choose M here: the
+ module will be called zd1201.
+
+source "drivers/net/wireless/zydas/zd1211rw/Kconfig"
+
+endif # WLAN_VENDOR_ZYDAS
diff --git a/drivers/net/wireless/zydas/Makefile b/drivers/net/wireless/zydas/Makefile
new file mode 100644
index 000000000..c70003d30
--- /dev/null
+++ b/drivers/net/wireless/zydas/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_ZD1211RW) += zd1211rw/
+
+obj-$(CONFIG_USB_ZD1201) += zd1201.o
diff --git a/drivers/net/wireless/zydas/zd1201.c b/drivers/net/wireless/zydas/zd1201.c
new file mode 100644
index 000000000..82bc0d442
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1201.c
@@ -0,0 +1,1905 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ZyDAS zd1201 based wireless USB devices.
+ *
+ * Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org)
+ *
+ * Parts of this driver have been derived from a wlan-ng version
+ * modified by ZyDAS. They also made documentation available, thanks!
+ * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <net/cfg80211.h>
+#include <net/iw_handler.h>
+#include <linux/string.h>
+#include <linux/if_arp.h>
+#include <linux/firmware.h>
+#include "zd1201.h"
+
+static const struct usb_device_id zd1201_table[] = {
+ {USB_DEVICE(0x0586, 0x3400)}, /* Peabird Wireless USB Adapter */
+ {USB_DEVICE(0x0ace, 0x1201)}, /* ZyDAS ZD1201 Wireless USB Adapter */
+ {USB_DEVICE(0x050d, 0x6051)}, /* Belkin F5D6051 usb adapter */
+ {USB_DEVICE(0x0db0, 0x6823)}, /* MSI UB11B usb adapter */
+ {USB_DEVICE(0x1044, 0x8004)}, /* Gigabyte GN-WLBZ101 */
+ {USB_DEVICE(0x1044, 0x8005)}, /* GIGABYTE GN-WLBZ201 usb adapter */
+ {}
+};
+
+static int ap; /* Are we an AP or a normal station? */
+
+#define ZD1201_VERSION "0.15"
+
+MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
+MODULE_DESCRIPTION("Driver for ZyDAS ZD1201 based USB Wireless adapters");
+MODULE_VERSION(ZD1201_VERSION);
+MODULE_LICENSE("GPL");
+module_param(ap, int, 0);
+MODULE_PARM_DESC(ap, "If non-zero Access Point firmware will be loaded");
+MODULE_DEVICE_TABLE(usb, zd1201_table);
+
+
+static int zd1201_fw_upload(struct usb_device *dev, int apfw)
+{
+ const struct firmware *fw_entry;
+ const char *data;
+ unsigned long len;
+ int err;
+ unsigned char ret;
+ char *buf;
+ char *fwfile;
+
+ if (apfw)
+ fwfile = "zd1201-ap.fw";
+ else
+ fwfile = "zd1201.fw";
+
+ err = request_firmware(&fw_entry, fwfile, &dev->dev);
+ if (err) {
+ dev_err(&dev->dev, "Failed to load %s firmware file!\n", fwfile);
+ dev_err(&dev->dev, "Make sure the hotplug firmware loader is installed.\n");
+ dev_err(&dev->dev, "Goto http://linux-lc100020.sourceforge.net for more info.\n");
+ return err;
+ }
+
+ data = fw_entry->data;
+ len = fw_entry->size;
+
+ buf = kmalloc(1024, GFP_ATOMIC);
+ if (!buf) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ while (len > 0) {
+ int translen = (len > 1024) ? 1024 : len;
+ memcpy(buf, data, translen);
+
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0,
+ USB_DIR_OUT | 0x40, 0, 0, buf, translen,
+ ZD1201_FW_TIMEOUT);
+ if (err < 0)
+ goto exit;
+
+ len -= translen;
+ data += translen;
+ }
+
+ err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x2,
+ USB_DIR_OUT | 0x40, 0, 0, NULL, 0, ZD1201_FW_TIMEOUT);
+ if (err < 0)
+ goto exit;
+
+ err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4,
+ USB_DIR_IN | 0x40, 0, 0, buf, sizeof(ret), ZD1201_FW_TIMEOUT);
+ if (err < 0)
+ goto exit;
+
+ memcpy(&ret, buf, sizeof(ret));
+
+ if (ret & 0x80) {
+ err = -EIO;
+ goto exit;
+ }
+
+ err = 0;
+exit:
+ kfree(buf);
+ release_firmware(fw_entry);
+ return err;
+}
+
+MODULE_FIRMWARE("zd1201-ap.fw");
+MODULE_FIRMWARE("zd1201.fw");
+
+static void zd1201_usbfree(struct urb *urb)
+{
+ struct zd1201 *zd = urb->context;
+
+ switch(urb->status) {
+ case -EILSEQ:
+ case -ENODEV:
+ case -ETIME:
+ case -ENOENT:
+ case -EPIPE:
+ case -EOVERFLOW:
+ case -ESHUTDOWN:
+ dev_warn(&zd->usb->dev, "%s: urb failed: %d\n",
+ zd->dev->name, urb->status);
+ }
+
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+}
+
+/* cmdreq message:
+ u32 type
+ u16 cmd
+ u16 parm0
+ u16 parm1
+ u16 parm2
+ u8 pad[4]
+
+ total: 4 + 2 + 2 + 2 + 2 + 4 = 16
+*/
+static int zd1201_docmd(struct zd1201 *zd, int cmd, int parm0,
+ int parm1, int parm2)
+{
+ unsigned char *command;
+ int ret;
+ struct urb *urb;
+
+ command = kmalloc(16, GFP_ATOMIC);
+ if (!command)
+ return -ENOMEM;
+
+ *((__le32*)command) = cpu_to_le32(ZD1201_USB_CMDREQ);
+ *((__le16*)&command[4]) = cpu_to_le16(cmd);
+ *((__le16*)&command[6]) = cpu_to_le16(parm0);
+ *((__le16*)&command[8]) = cpu_to_le16(parm1);
+ *((__le16*)&command[10])= cpu_to_le16(parm2);
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ kfree(command);
+ return -ENOMEM;
+ }
+ usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),
+ command, 16, zd1201_usbfree, zd);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret) {
+ kfree(command);
+ usb_free_urb(urb);
+ }
+
+ return ret;
+}
+
+/* Callback after sending out a packet */
+static void zd1201_usbtx(struct urb *urb)
+{
+ struct zd1201 *zd = urb->context;
+ netif_wake_queue(zd->dev);
+}
+
+/* Incoming data */
+static void zd1201_usbrx(struct urb *urb)
+{
+ struct zd1201 *zd = urb->context;
+ int free = 0;
+ unsigned char *data = urb->transfer_buffer;
+ struct sk_buff *skb;
+ unsigned char type;
+
+ if (!zd)
+ return;
+
+ switch(urb->status) {
+ case -EILSEQ:
+ case -ENODEV:
+ case -ETIME:
+ case -ENOENT:
+ case -EPIPE:
+ case -EOVERFLOW:
+ case -ESHUTDOWN:
+ dev_warn(&zd->usb->dev, "%s: rx urb failed: %d\n",
+ zd->dev->name, urb->status);
+ free = 1;
+ goto exit;
+ }
+
+ if (urb->status != 0 || urb->actual_length == 0)
+ goto resubmit;
+
+ type = data[0];
+ if (type == ZD1201_PACKET_EVENTSTAT || type == ZD1201_PACKET_RESOURCE) {
+ memcpy(zd->rxdata, data, urb->actual_length);
+ zd->rxlen = urb->actual_length;
+ zd->rxdatas = 1;
+ wake_up(&zd->rxdataq);
+ }
+ /* Info frame */
+ if (type == ZD1201_PACKET_INQUIRE) {
+ int i = 0;
+ unsigned short infotype, copylen;
+ infotype = le16_to_cpu(*(__le16*)&data[6]);
+
+ if (infotype == ZD1201_INF_LINKSTATUS) {
+ short linkstatus;
+
+ linkstatus = le16_to_cpu(*(__le16*)&data[8]);
+ switch(linkstatus) {
+ case 1:
+ netif_carrier_on(zd->dev);
+ break;
+ case 2:
+ netif_carrier_off(zd->dev);
+ break;
+ case 3:
+ netif_carrier_off(zd->dev);
+ break;
+ case 4:
+ netif_carrier_on(zd->dev);
+ break;
+ default:
+ netif_carrier_off(zd->dev);
+ }
+ goto resubmit;
+ }
+ if (infotype == ZD1201_INF_ASSOCSTATUS) {
+ short status = le16_to_cpu(*(__le16*)(data+8));
+ int event;
+ union iwreq_data wrqu;
+
+ switch (status) {
+ case ZD1201_ASSOCSTATUS_STAASSOC:
+ case ZD1201_ASSOCSTATUS_REASSOC:
+ event = IWEVREGISTERED;
+ break;
+ case ZD1201_ASSOCSTATUS_DISASSOC:
+ case ZD1201_ASSOCSTATUS_ASSOCFAIL:
+ case ZD1201_ASSOCSTATUS_AUTHFAIL:
+ default:
+ event = IWEVEXPIRED;
+ }
+ memcpy(wrqu.addr.sa_data, data+10, ETH_ALEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+
+ /* Send event to user space */
+ wireless_send_event(zd->dev, event, &wrqu, NULL);
+
+ goto resubmit;
+ }
+ if (infotype == ZD1201_INF_AUTHREQ) {
+ union iwreq_data wrqu;
+
+ memcpy(wrqu.addr.sa_data, data+8, ETH_ALEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+ /* There isn't a event that trully fits this request.
+ We assume that userspace will be smart enough to
+ see a new station being expired and sends back a
+ authstation ioctl to authorize it. */
+ wireless_send_event(zd->dev, IWEVEXPIRED, &wrqu, NULL);
+ goto resubmit;
+ }
+ /* Other infotypes are handled outside this handler */
+ zd->rxlen = 0;
+ while (i < urb->actual_length) {
+ copylen = le16_to_cpu(*(__le16*)&data[i+2]);
+ /* Sanity check, sometimes we get junk */
+ if (copylen+zd->rxlen > sizeof(zd->rxdata))
+ break;
+ memcpy(zd->rxdata+zd->rxlen, data+i+4, copylen);
+ zd->rxlen += copylen;
+ i += 64;
+ }
+ if (i >= urb->actual_length) {
+ zd->rxdatas = 1;
+ wake_up(&zd->rxdataq);
+ }
+ goto resubmit;
+ }
+ /* Actual data */
+ if (data[urb->actual_length-1] == ZD1201_PACKET_RXDATA) {
+ int datalen = urb->actual_length-1;
+ unsigned short len, fc, seq;
+
+ len = ntohs(*(__be16 *)&data[datalen-2]);
+ if (len>datalen)
+ len=datalen;
+ fc = le16_to_cpu(*(__le16 *)&data[datalen-16]);
+ seq = le16_to_cpu(*(__le16 *)&data[datalen-24]);
+
+ if (zd->monitor) {
+ if (datalen < 24)
+ goto resubmit;
+ if (!(skb = dev_alloc_skb(datalen+24)))
+ goto resubmit;
+
+ skb_put_data(skb, &data[datalen - 16], 2);
+ skb_put_data(skb, &data[datalen - 2], 2);
+ skb_put_data(skb, &data[datalen - 14], 6);
+ skb_put_data(skb, &data[datalen - 22], 6);
+ skb_put_data(skb, &data[datalen - 8], 6);
+ skb_put_data(skb, &data[datalen - 24], 2);
+ skb_put_data(skb, data, len);
+ skb->protocol = eth_type_trans(skb, zd->dev);
+ zd->dev->stats.rx_packets++;
+ zd->dev->stats.rx_bytes += skb->len;
+ netif_rx(skb);
+ goto resubmit;
+ }
+
+ if ((seq & IEEE80211_SCTL_FRAG) ||
+ (fc & IEEE80211_FCTL_MOREFRAGS)) {
+ struct zd1201_frag *frag = NULL;
+ char *ptr;
+
+ if (datalen<14)
+ goto resubmit;
+ if ((seq & IEEE80211_SCTL_FRAG) == 0) {
+ frag = kmalloc(sizeof(*frag), GFP_ATOMIC);
+ if (!frag)
+ goto resubmit;
+ skb = dev_alloc_skb(IEEE80211_MAX_DATA_LEN +14+2);
+ if (!skb) {
+ kfree(frag);
+ goto resubmit;
+ }
+ frag->skb = skb;
+ frag->seq = seq & IEEE80211_SCTL_SEQ;
+ skb_reserve(skb, 2);
+ skb_put_data(skb, &data[datalen - 14], 12);
+ skb_put_data(skb, &data[6], 2);
+ skb_put_data(skb, data + 8, len);
+ hlist_add_head(&frag->fnode, &zd->fraglist);
+ goto resubmit;
+ }
+ hlist_for_each_entry(frag, &zd->fraglist, fnode)
+ if (frag->seq == (seq&IEEE80211_SCTL_SEQ))
+ break;
+ if (!frag)
+ goto resubmit;
+ skb = frag->skb;
+ ptr = skb_put(skb, len);
+ if (ptr)
+ memcpy(ptr, data+8, len);
+ if (fc & IEEE80211_FCTL_MOREFRAGS)
+ goto resubmit;
+ hlist_del_init(&frag->fnode);
+ kfree(frag);
+ } else {
+ if (datalen<14)
+ goto resubmit;
+ skb = dev_alloc_skb(len + 14 + 2);
+ if (!skb)
+ goto resubmit;
+ skb_reserve(skb, 2);
+ skb_put_data(skb, &data[datalen - 14], 12);
+ skb_put_data(skb, &data[6], 2);
+ skb_put_data(skb, data + 8, len);
+ }
+ skb->protocol = eth_type_trans(skb, zd->dev);
+ zd->dev->stats.rx_packets++;
+ zd->dev->stats.rx_bytes += skb->len;
+ netif_rx(skb);
+ }
+resubmit:
+ memset(data, 0, ZD1201_RXSIZE);
+
+ urb->status = 0;
+ urb->dev = zd->usb;
+ if(usb_submit_urb(urb, GFP_ATOMIC))
+ free = 1;
+
+exit:
+ if (free) {
+ zd->rxlen = 0;
+ zd->rxdatas = 1;
+ wake_up(&zd->rxdataq);
+ kfree(urb->transfer_buffer);
+ }
+}
+
+static int zd1201_getconfig(struct zd1201 *zd, int rid, void *riddata,
+ unsigned int riddatalen)
+{
+ int err;
+ int i = 0;
+ int code;
+ int rid_fid;
+ int length;
+ unsigned char *pdata;
+
+ zd->rxdatas = 0;
+ err = zd1201_docmd(zd, ZD1201_CMDCODE_ACCESS, rid, 0, 0);
+ if (err)
+ return err;
+
+ wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+ if (!zd->rxlen)
+ return -EIO;
+
+ code = le16_to_cpu(*(__le16*)(&zd->rxdata[4]));
+ rid_fid = le16_to_cpu(*(__le16*)(&zd->rxdata[6]));
+ length = le16_to_cpu(*(__le16*)(&zd->rxdata[8]));
+ if (length > zd->rxlen)
+ length = zd->rxlen-6;
+
+ /* If access bit is not on, then error */
+ if ((code & ZD1201_ACCESSBIT) != ZD1201_ACCESSBIT || rid_fid != rid )
+ return -EINVAL;
+
+ /* Not enough buffer for allocating data */
+ if (riddatalen != (length - 4)) {
+ dev_dbg(&zd->usb->dev, "riddatalen mismatches, expected=%u, (packet=%u) length=%u, rid=0x%04X, rid_fid=0x%04X\n",
+ riddatalen, zd->rxlen, length, rid, rid_fid);
+ return -ENODATA;
+ }
+
+ zd->rxdatas = 0;
+ /* Issue SetRxRid commnd */
+ err = zd1201_docmd(zd, ZD1201_CMDCODE_SETRXRID, rid, 0, length);
+ if (err)
+ return err;
+
+ /* Receive RID record from resource packets */
+ wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+ if (!zd->rxlen)
+ return -EIO;
+
+ if (zd->rxdata[zd->rxlen - 1] != ZD1201_PACKET_RESOURCE) {
+ dev_dbg(&zd->usb->dev, "Packet type mismatch: 0x%x not 0x3\n",
+ zd->rxdata[zd->rxlen-1]);
+ return -EINVAL;
+ }
+
+ /* Set the data pointer and received data length */
+ pdata = zd->rxdata;
+ length = zd->rxlen;
+
+ do {
+ int actual_length;
+
+ actual_length = (length > 64) ? 64 : length;
+
+ if (pdata[0] != 0x3) {
+ dev_dbg(&zd->usb->dev, "Rx Resource packet type error: %02X\n",
+ pdata[0]);
+ return -EINVAL;
+ }
+
+ if (actual_length != 64) {
+ /* Trim the last packet type byte */
+ actual_length--;
+ }
+
+ /* Skip the 4 bytes header (RID length and RID) */
+ if (i == 0) {
+ pdata += 8;
+ actual_length -= 8;
+ } else {
+ pdata += 4;
+ actual_length -= 4;
+ }
+
+ memcpy(riddata, pdata, actual_length);
+ riddata += actual_length;
+ pdata += actual_length;
+ length -= 64;
+ i++;
+ } while (length > 0);
+
+ return 0;
+}
+
+/*
+ * resreq:
+ * byte type
+ * byte sequence
+ * u16 reserved
+ * byte data[12]
+ * total: 16
+ */
+static int zd1201_setconfig(struct zd1201 *zd, int rid, const void *buf, int len, int wait)
+{
+ int err;
+ unsigned char *request;
+ int reqlen;
+ char seq=0;
+ struct urb *urb;
+ gfp_t gfp_mask = wait ? GFP_NOIO : GFP_ATOMIC;
+
+ len += 4; /* first 4 are for header */
+
+ zd->rxdatas = 0;
+ zd->rxlen = 0;
+ for (seq=0; len > 0; seq++) {
+ request = kzalloc(16, gfp_mask);
+ if (!request)
+ return -ENOMEM;
+ urb = usb_alloc_urb(0, gfp_mask);
+ if (!urb) {
+ kfree(request);
+ return -ENOMEM;
+ }
+ reqlen = len>12 ? 12 : len;
+ request[0] = ZD1201_USB_RESREQ;
+ request[1] = seq;
+ request[2] = 0;
+ request[3] = 0;
+ if (request[1] == 0) {
+ /* add header */
+ *(__le16*)&request[4] = cpu_to_le16((len-2+1)/2);
+ *(__le16*)&request[6] = cpu_to_le16(rid);
+ memcpy(request+8, buf, reqlen-4);
+ buf += reqlen-4;
+ } else {
+ memcpy(request+4, buf, reqlen);
+ buf += reqlen;
+ }
+
+ len -= reqlen;
+
+ usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb,
+ zd->endp_out2), request, 16, zd1201_usbfree, zd);
+ err = usb_submit_urb(urb, gfp_mask);
+ if (err)
+ goto err;
+ }
+
+ request = kmalloc(16, gfp_mask);
+ if (!request)
+ return -ENOMEM;
+ urb = usb_alloc_urb(0, gfp_mask);
+ if (!urb) {
+ kfree(request);
+ return -ENOMEM;
+ }
+ *((__le32*)request) = cpu_to_le32(ZD1201_USB_CMDREQ);
+ *((__le16*)&request[4]) =
+ cpu_to_le16(ZD1201_CMDCODE_ACCESS|ZD1201_ACCESSBIT);
+ *((__le16*)&request[6]) = cpu_to_le16(rid);
+ *((__le16*)&request[8]) = cpu_to_le16(0);
+ *((__le16*)&request[10]) = cpu_to_le16(0);
+ usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),
+ request, 16, zd1201_usbfree, zd);
+ err = usb_submit_urb(urb, gfp_mask);
+ if (err)
+ goto err;
+
+ if (wait) {
+ wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+ if (!zd->rxlen || le16_to_cpu(*(__le16*)&zd->rxdata[6]) != rid) {
+ dev_dbg(&zd->usb->dev, "wrong or no RID received\n");
+ }
+ }
+
+ return 0;
+err:
+ kfree(request);
+ usb_free_urb(urb);
+ return err;
+}
+
+static inline int zd1201_getconfig16(struct zd1201 *zd, int rid, short *val)
+{
+ int err;
+ __le16 zdval;
+
+ err = zd1201_getconfig(zd, rid, &zdval, sizeof(__le16));
+ if (err)
+ return err;
+ *val = le16_to_cpu(zdval);
+ return 0;
+}
+
+static inline int zd1201_setconfig16(struct zd1201 *zd, int rid, short val)
+{
+ __le16 zdval = cpu_to_le16(val);
+ return (zd1201_setconfig(zd, rid, &zdval, sizeof(__le16), 1));
+}
+
+static int zd1201_drvr_start(struct zd1201 *zd)
+{
+ int err, i;
+ short max;
+ __le16 zdmax;
+ unsigned char *buffer;
+
+ buffer = kzalloc(ZD1201_RXSIZE, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(zd->rx_urb, zd->usb,
+ usb_rcvbulkpipe(zd->usb, zd->endp_in), buffer, ZD1201_RXSIZE,
+ zd1201_usbrx, zd);
+
+ err = usb_submit_urb(zd->rx_urb, GFP_KERNEL);
+ if (err)
+ goto err_buffer;
+
+ err = zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
+ if (err)
+ goto err_urb;
+
+ err = zd1201_getconfig(zd, ZD1201_RID_CNFMAXTXBUFFERNUMBER, &zdmax,
+ sizeof(__le16));
+ if (err)
+ goto err_urb;
+
+ max = le16_to_cpu(zdmax);
+ for (i=0; i<max; i++) {
+ err = zd1201_docmd(zd, ZD1201_CMDCODE_ALLOC, 1514, 0, 0);
+ if (err)
+ goto err_urb;
+ }
+
+ return 0;
+
+err_urb:
+ usb_kill_urb(zd->rx_urb);
+ return err;
+err_buffer:
+ kfree(buffer);
+ return err;
+}
+
+/* Magic alert: The firmware doesn't seem to like the MAC state being
+ * toggled in promisc (aka monitor) mode.
+ * (It works a number of times, but will halt eventually)
+ * So we turn it of before disabling and on after enabling if needed.
+ */
+static int zd1201_enable(struct zd1201 *zd)
+{
+ int err;
+
+ if (zd->mac_enabled)
+ return 0;
+
+ err = zd1201_docmd(zd, ZD1201_CMDCODE_ENABLE, 0, 0, 0);
+ if (!err)
+ zd->mac_enabled = 1;
+
+ if (zd->monitor)
+ err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 1);
+
+ return err;
+}
+
+static int zd1201_disable(struct zd1201 *zd)
+{
+ int err;
+
+ if (!zd->mac_enabled)
+ return 0;
+ if (zd->monitor) {
+ err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0);
+ if (err)
+ return err;
+ }
+
+ err = zd1201_docmd(zd, ZD1201_CMDCODE_DISABLE, 0, 0, 0);
+ if (!err)
+ zd->mac_enabled = 0;
+ return err;
+}
+
+static int zd1201_mac_reset(struct zd1201 *zd)
+{
+ if (!zd->mac_enabled)
+ return 0;
+ zd1201_disable(zd);
+ return zd1201_enable(zd);
+}
+
+static int zd1201_join(struct zd1201 *zd, char *essid, int essidlen)
+{
+ int err, val;
+ char buf[IW_ESSID_MAX_SIZE+2];
+
+ err = zd1201_disable(zd);
+ if (err)
+ return err;
+
+ val = ZD1201_CNFAUTHENTICATION_OPENSYSTEM;
+ val |= ZD1201_CNFAUTHENTICATION_SHAREDKEY;
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, val);
+ if (err)
+ return err;
+
+ *(__le16 *)buf = cpu_to_le16(essidlen);
+ memcpy(buf+2, essid, essidlen);
+ if (!zd->ap) { /* Normal station */
+ err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
+ IW_ESSID_MAX_SIZE+2, 1);
+ if (err)
+ return err;
+ } else { /* AP */
+ err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNSSID, buf,
+ IW_ESSID_MAX_SIZE+2, 1);
+ if (err)
+ return err;
+ }
+
+ err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR,
+ zd->dev->dev_addr, zd->dev->addr_len, 1);
+ if (err)
+ return err;
+
+ err = zd1201_enable(zd);
+ if (err)
+ return err;
+
+ msleep(100);
+ return 0;
+}
+
+static int zd1201_net_open(struct net_device *dev)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+
+ /* Start MAC with wildcard if no essid set */
+ if (!zd->mac_enabled)
+ zd1201_join(zd, zd->essid, zd->essidlen);
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int zd1201_net_stop(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/*
+ RFC 1042 encapsulates Ethernet frames in 802.11 frames
+ by prefixing them with 0xaa, 0xaa, 0x03) followed by a SNAP OID of 0
+ (0x00, 0x00, 0x00). Zd requires an additional padding, copy
+ of ethernet addresses, length of the standard RFC 1042 packet
+ and a command byte (which is nul for tx).
+
+ tx frame (from Wlan NG):
+ RFC 1042:
+ llc 0xAA 0xAA 0x03 (802.2 LLC)
+ snap 0x00 0x00 0x00 (Ethernet encapsulated)
+ type 2 bytes, Ethernet type field
+ payload (minus eth header)
+ Zydas specific:
+ padding 1B if (skb->len+8+1)%64==0
+ Eth MAC addr 12 bytes, Ethernet MAC addresses
+ length 2 bytes, RFC 1042 packet length
+ (llc+snap+type+payload)
+ zd 1 null byte, zd1201 packet type
+ */
+static netdev_tx_t zd1201_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ unsigned char *txbuf = zd->txdata;
+ int txbuflen, pad = 0, err;
+ struct urb *urb = zd->tx_urb;
+
+ if (!zd->mac_enabled || zd->monitor) {
+ dev->stats.tx_dropped++;
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+ netif_stop_queue(dev);
+
+ txbuflen = skb->len + 8 + 1;
+ if (txbuflen%64 == 0) {
+ pad = 1;
+ txbuflen++;
+ }
+ txbuf[0] = 0xAA;
+ txbuf[1] = 0xAA;
+ txbuf[2] = 0x03;
+ txbuf[3] = 0x00; /* rfc1042 */
+ txbuf[4] = 0x00;
+ txbuf[5] = 0x00;
+
+ skb_copy_from_linear_data_offset(skb, 12, txbuf + 6, skb->len - 12);
+ if (pad)
+ txbuf[skb->len-12+6]=0;
+ skb_copy_from_linear_data(skb, txbuf + skb->len - 12 + 6 + pad, 12);
+ *(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6);
+ txbuf[txbuflen-1] = 0;
+
+ usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out),
+ txbuf, txbuflen, zd1201_usbtx, zd);
+
+ err = usb_submit_urb(zd->tx_urb, GFP_ATOMIC);
+ if (err) {
+ dev->stats.tx_errors++;
+ netif_start_queue(dev);
+ } else {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ }
+ kfree_skb(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static void zd1201_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+
+ if (!zd)
+ return;
+ dev_warn(&zd->usb->dev, "%s: TX timeout, shooting down urb\n",
+ dev->name);
+ usb_unlink_urb(zd->tx_urb);
+ dev->stats.tx_errors++;
+ /* Restart the timeout to quiet the watchdog: */
+ netif_trans_update(dev); /* prevent tx timeout */
+}
+
+static int zd1201_set_mac_address(struct net_device *dev, void *p)
+{
+ struct sockaddr *addr = p;
+ struct zd1201 *zd = netdev_priv(dev);
+ int err;
+
+ if (!zd)
+ return -ENODEV;
+
+ err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR,
+ addr->sa_data, dev->addr_len, 1);
+ if (err)
+ return err;
+ eth_hw_addr_set(dev, addr->sa_data);
+
+ return zd1201_mac_reset(zd);
+}
+
+static struct iw_statistics *zd1201_get_wireless_stats(struct net_device *dev)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+
+ return &zd->iwstats;
+}
+
+static void zd1201_set_multicast(struct net_device *dev)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ struct netdev_hw_addr *ha;
+ unsigned char reqbuf[ETH_ALEN*ZD1201_MAXMULTI];
+ int i;
+
+ if (netdev_mc_count(dev) > ZD1201_MAXMULTI)
+ return;
+
+ i = 0;
+ netdev_for_each_mc_addr(ha, dev)
+ memcpy(reqbuf + i++ * ETH_ALEN, ha->addr, ETH_ALEN);
+ zd1201_setconfig(zd, ZD1201_RID_CNFGROUPADDRESS, reqbuf,
+ netdev_mc_count(dev) * ETH_ALEN, 0);
+}
+
+static int zd1201_config_commit(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+
+ return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_name(struct net_device *dev,
+ struct iw_request_info *info, char *name, char *extra)
+{
+ strcpy(name, "IEEE 802.11b");
+ return 0;
+}
+
+static int zd1201_set_freq(struct net_device *dev,
+ struct iw_request_info *info, struct iw_freq *freq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short channel = 0;
+ int err;
+
+ if (freq->e == 0)
+ channel = freq->m;
+ else
+ channel = ieee80211_frequency_to_channel(freq->m);
+
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, channel);
+ if (err)
+ return err;
+
+ zd1201_mac_reset(zd);
+
+ return 0;
+}
+
+static int zd1201_get_freq(struct net_device *dev,
+ struct iw_request_info *info, struct iw_freq *freq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short channel;
+ int err;
+
+ err = zd1201_getconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, &channel);
+ if (err)
+ return err;
+ freq->e = 0;
+ freq->m = channel;
+
+ return 0;
+}
+
+static int zd1201_set_mode(struct net_device *dev,
+ struct iw_request_info *info, __u32 *mode, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short porttype, monitor = 0;
+ unsigned char buffer[IW_ESSID_MAX_SIZE+2];
+ int err;
+
+ if (zd->ap) {
+ if (*mode != IW_MODE_MASTER)
+ return -EINVAL;
+ return 0;
+ }
+
+ err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0);
+ if (err)
+ return err;
+ zd->dev->type = ARPHRD_ETHER;
+ switch(*mode) {
+ case IW_MODE_MONITOR:
+ monitor = 1;
+ zd->dev->type = ARPHRD_IEEE80211;
+ /* Make sure we are no longer associated with by
+ setting an 'impossible' essid.
+ (otherwise we mess up firmware)
+ */
+ zd1201_join(zd, "\0-*#\0", 5);
+ /* Put port in pIBSS */
+ fallthrough;
+ case 8: /* No pseudo-IBSS in wireless extensions (yet) */
+ porttype = ZD1201_PORTTYPE_PSEUDOIBSS;
+ break;
+ case IW_MODE_ADHOC:
+ porttype = ZD1201_PORTTYPE_IBSS;
+ break;
+ case IW_MODE_INFRA:
+ porttype = ZD1201_PORTTYPE_BSS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
+ if (err)
+ return err;
+ if (zd->monitor && !monitor) {
+ zd1201_disable(zd);
+ *(__le16 *)buffer = cpu_to_le16(zd->essidlen);
+ memcpy(buffer+2, zd->essid, zd->essidlen);
+ err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID,
+ buffer, IW_ESSID_MAX_SIZE+2, 1);
+ if (err)
+ return err;
+ }
+ zd->monitor = monitor;
+ /* If monitor mode is set we don't actually turn it on here since it
+ * is done during mac reset anyway (see zd1201_mac_enable).
+ */
+ zd1201_mac_reset(zd);
+
+ return 0;
+}
+
+static int zd1201_get_mode(struct net_device *dev,
+ struct iw_request_info *info, __u32 *mode, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short porttype;
+ int err;
+
+ err = zd1201_getconfig16(zd, ZD1201_RID_CNFPORTTYPE, &porttype);
+ if (err)
+ return err;
+ switch(porttype) {
+ case ZD1201_PORTTYPE_IBSS:
+ *mode = IW_MODE_ADHOC;
+ break;
+ case ZD1201_PORTTYPE_BSS:
+ *mode = IW_MODE_INFRA;
+ break;
+ case ZD1201_PORTTYPE_WDS:
+ *mode = IW_MODE_REPEAT;
+ break;
+ case ZD1201_PORTTYPE_PSEUDOIBSS:
+ *mode = 8;/* No Pseudo-IBSS... */
+ break;
+ case ZD1201_PORTTYPE_AP:
+ *mode = IW_MODE_MASTER;
+ break;
+ default:
+ dev_dbg(&zd->usb->dev, "Unknown porttype: %d\n",
+ porttype);
+ *mode = IW_MODE_AUTO;
+ }
+ if (zd->monitor)
+ *mode = IW_MODE_MONITOR;
+
+ return 0;
+}
+
+static int zd1201_get_range(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *wrq, char *extra)
+{
+ struct iw_range *range = (struct iw_range *)extra;
+
+ wrq->length = sizeof(struct iw_range);
+ memset(range, 0, sizeof(struct iw_range));
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = WIRELESS_EXT;
+
+ range->max_qual.qual = 128;
+ range->max_qual.level = 128;
+ range->max_qual.noise = 128;
+ range->max_qual.updated = 7;
+
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13;
+ range->num_encoding_sizes = 2;
+ range->max_encoding_tokens = ZD1201_NUMKEYS;
+
+ range->num_bitrates = 4;
+ range->bitrate[0] = 1000000;
+ range->bitrate[1] = 2000000;
+ range->bitrate[2] = 5500000;
+ range->bitrate[3] = 11000000;
+
+ range->min_rts = 0;
+ range->min_frag = ZD1201_FRAGMIN;
+ range->max_rts = ZD1201_RTSMAX;
+ range->min_frag = ZD1201_FRAGMAX;
+
+ return 0;
+}
+
+/* Little bit of magic here: we only get the quality if we poll
+ * for it, and we never get an actual request to trigger such
+ * a poll. Therefore we 'assume' that the user will soon ask for
+ * the stats after asking the bssid.
+ */
+static int zd1201_get_wap(struct net_device *dev,
+ struct iw_request_info *info, struct sockaddr *ap_addr, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ unsigned char buffer[6];
+
+ if (!zd1201_getconfig(zd, ZD1201_RID_COMMSQUALITY, buffer, 6)) {
+ /* Unfortunately the quality and noise reported is useless.
+ they seem to be accumulators that increase until you
+ read them, unless we poll on a fixed interval we can't
+ use them
+ */
+ /*zd->iwstats.qual.qual = le16_to_cpu(((__le16 *)buffer)[0]);*/
+ zd->iwstats.qual.level = le16_to_cpu(((__le16 *)buffer)[1]);
+ /*zd->iwstats.qual.noise = le16_to_cpu(((__le16 *)buffer)[2]);*/
+ zd->iwstats.qual.updated = 2;
+ }
+
+ return zd1201_getconfig(zd, ZD1201_RID_CURRENTBSSID, ap_addr->sa_data, 6);
+}
+
+static int zd1201_set_scan(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *srq, char *extra)
+{
+ /* We do everything in get_scan */
+ return 0;
+}
+
+static int zd1201_get_scan(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *srq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ int err, i, j, enabled_save;
+ struct iw_event iwe;
+ char *cev = extra;
+ char *end_buf = extra + IW_SCAN_MAX_DATA;
+
+ /* No scanning in AP mode */
+ if (zd->ap)
+ return -EOPNOTSUPP;
+
+ /* Scan doesn't seem to work if disabled */
+ enabled_save = zd->mac_enabled;
+ zd1201_enable(zd);
+
+ zd->rxdatas = 0;
+ err = zd1201_docmd(zd, ZD1201_CMDCODE_INQUIRE,
+ ZD1201_INQ_SCANRESULTS, 0, 0);
+ if (err)
+ return err;
+
+ wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+ if (!zd->rxlen)
+ return -EIO;
+
+ if (le16_to_cpu(*(__le16*)&zd->rxdata[2]) != ZD1201_INQ_SCANRESULTS)
+ return -EIO;
+
+ for(i=8; i<zd->rxlen; i+=62) {
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, zd->rxdata+i+6, 6);
+ cev = iwe_stream_add_event(info, cev, end_buf,
+ &iwe, IW_EV_ADDR_LEN);
+
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.length = zd->rxdata[i+16];
+ iwe.u.data.flags = 1;
+ cev = iwe_stream_add_point(info, cev, end_buf,
+ &iwe, zd->rxdata+i+18);
+
+ iwe.cmd = SIOCGIWMODE;
+ if (zd->rxdata[i+14]&0x01)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ cev = iwe_stream_add_event(info, cev, end_buf,
+ &iwe, IW_EV_UINT_LEN);
+
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = zd->rxdata[i+0];
+ iwe.u.freq.e = 0;
+ cev = iwe_stream_add_event(info, cev, end_buf,
+ &iwe, IW_EV_FREQ_LEN);
+
+ iwe.cmd = SIOCGIWRATE;
+ iwe.u.bitrate.fixed = 0;
+ iwe.u.bitrate.disabled = 0;
+ for (j=0; j<10; j++) if (zd->rxdata[i+50+j]) {
+ iwe.u.bitrate.value = (zd->rxdata[i+50+j]&0x7f)*500000;
+ cev = iwe_stream_add_event(info, cev, end_buf,
+ &iwe, IW_EV_PARAM_LEN);
+ }
+
+ iwe.cmd = SIOCGIWENCODE;
+ iwe.u.data.length = 0;
+ if (zd->rxdata[i+14]&0x10)
+ iwe.u.data.flags = IW_ENCODE_ENABLED;
+ else
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL);
+
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.qual = zd->rxdata[i+4];
+ iwe.u.qual.noise= zd->rxdata[i+2]/10-100;
+ iwe.u.qual.level = (256+zd->rxdata[i+4]*100)/255-100;
+ iwe.u.qual.updated = 7;
+ cev = iwe_stream_add_event(info, cev, end_buf,
+ &iwe, IW_EV_QUAL_LEN);
+ }
+
+ if (!enabled_save)
+ zd1201_disable(zd);
+
+ srq->length = cev - extra;
+ srq->flags = 0;
+
+ return 0;
+}
+
+static int zd1201_set_essid(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+
+ if (data->length > IW_ESSID_MAX_SIZE)
+ return -EINVAL;
+ if (data->length < 1)
+ data->length = 1;
+ zd->essidlen = data->length;
+ memset(zd->essid, 0, IW_ESSID_MAX_SIZE+1);
+ memcpy(zd->essid, essid, data->length);
+ return zd1201_join(zd, zd->essid, zd->essidlen);
+}
+
+static int zd1201_get_essid(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+
+ memcpy(essid, zd->essid, zd->essidlen);
+ data->flags = 1;
+ data->length = zd->essidlen;
+
+ return 0;
+}
+
+static int zd1201_get_nick(struct net_device *dev, struct iw_request_info *info,
+ struct iw_point *data, char *nick)
+{
+ strcpy(nick, "zd1201");
+ data->flags = 1;
+ data->length = strlen(nick);
+ return 0;
+}
+
+static int zd1201_set_rate(struct net_device *dev,
+ struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short rate;
+ int err;
+
+ switch (rrq->value) {
+ case 1000000:
+ rate = ZD1201_RATEB1;
+ break;
+ case 2000000:
+ rate = ZD1201_RATEB2;
+ break;
+ case 5500000:
+ rate = ZD1201_RATEB5;
+ break;
+ case 11000000:
+ default:
+ rate = ZD1201_RATEB11;
+ break;
+ }
+ if (!rrq->fixed) { /* Also enable all lower bitrates */
+ rate |= rate-1;
+ }
+
+ err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL, rate);
+ if (err)
+ return err;
+
+ return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_rate(struct net_device *dev,
+ struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short rate;
+ int err;
+
+ err = zd1201_getconfig16(zd, ZD1201_RID_CURRENTTXRATE, &rate);
+ if (err)
+ return err;
+
+ switch(rate) {
+ case 1:
+ rrq->value = 1000000;
+ break;
+ case 2:
+ rrq->value = 2000000;
+ break;
+ case 5:
+ rrq->value = 5500000;
+ break;
+ case 11:
+ rrq->value = 11000000;
+ break;
+ default:
+ rrq->value = 0;
+ }
+ rrq->fixed = 0;
+ rrq->disabled = 0;
+
+ return 0;
+}
+
+static int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ int err;
+ short val = rts->value;
+
+ if (rts->disabled || !rts->fixed)
+ val = ZD1201_RTSMAX;
+ if (val > ZD1201_RTSMAX)
+ return -EINVAL;
+ if (val < 0)
+ return -EINVAL;
+
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, val);
+ if (err)
+ return err;
+ return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *rts, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short rtst;
+ int err;
+
+ err = zd1201_getconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, &rtst);
+ if (err)
+ return err;
+ rts->value = rtst;
+ rts->disabled = (rts->value == ZD1201_RTSMAX);
+ rts->fixed = 1;
+
+ return 0;
+}
+
+static int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *frag, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ int err;
+ short val = frag->value;
+
+ if (frag->disabled || !frag->fixed)
+ val = ZD1201_FRAGMAX;
+ if (val > ZD1201_FRAGMAX)
+ return -EINVAL;
+ if (val < ZD1201_FRAGMIN)
+ return -EINVAL;
+ if (val & 1)
+ return -EINVAL;
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, val);
+ if (err)
+ return err;
+ return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_frag(struct net_device *dev, struct iw_request_info *info,
+ struct iw_param *frag, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short fragt;
+ int err;
+
+ err = zd1201_getconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, &fragt);
+ if (err)
+ return err;
+ frag->value = fragt;
+ frag->disabled = (frag->value == ZD1201_FRAGMAX);
+ frag->fixed = 1;
+
+ return 0;
+}
+
+static int zd1201_set_retry(struct net_device *dev,
+ struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+ return 0;
+}
+
+static int zd1201_get_retry(struct net_device *dev,
+ struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+ return 0;
+}
+
+static int zd1201_set_encode(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *erq, char *key)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short i;
+ int err, rid;
+
+ if (erq->length > ZD1201_MAXKEYLEN)
+ return -EINVAL;
+
+ i = (erq->flags & IW_ENCODE_INDEX)-1;
+ if (i == -1) {
+ err = zd1201_getconfig16(zd,ZD1201_RID_CNFDEFAULTKEYID,&i);
+ if (err)
+ return err;
+ } else {
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, i);
+ if (err)
+ return err;
+ }
+
+ if (i < 0 || i >= ZD1201_NUMKEYS)
+ return -EINVAL;
+
+ rid = ZD1201_RID_CNFDEFAULTKEY0 + i;
+ err = zd1201_setconfig(zd, rid, key, erq->length, 1);
+ if (err)
+ return err;
+ zd->encode_keylen[i] = erq->length;
+ memcpy(zd->encode_keys[i], key, erq->length);
+
+ i=0;
+ if (!(erq->flags & IW_ENCODE_DISABLED & IW_ENCODE_MODE)) {
+ i |= 0x01;
+ zd->encode_enabled = 1;
+ } else
+ zd->encode_enabled = 0;
+ if (erq->flags & IW_ENCODE_RESTRICTED & IW_ENCODE_MODE) {
+ i |= 0x02;
+ zd->encode_restricted = 1;
+ } else
+ zd->encode_restricted = 0;
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFWEBFLAGS, i);
+ if (err)
+ return err;
+
+ if (zd->encode_enabled)
+ i = ZD1201_CNFAUTHENTICATION_SHAREDKEY;
+ else
+ i = ZD1201_CNFAUTHENTICATION_OPENSYSTEM;
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, i);
+ if (err)
+ return err;
+
+ return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_encode(struct net_device *dev,
+ struct iw_request_info *info, struct iw_point *erq, char *key)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short i;
+ int err;
+
+ if (zd->encode_enabled)
+ erq->flags = IW_ENCODE_ENABLED;
+ else
+ erq->flags = IW_ENCODE_DISABLED;
+ if (zd->encode_restricted)
+ erq->flags |= IW_ENCODE_RESTRICTED;
+ else
+ erq->flags |= IW_ENCODE_OPEN;
+
+ i = (erq->flags & IW_ENCODE_INDEX) -1;
+ if (i == -1) {
+ err = zd1201_getconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, &i);
+ if (err)
+ return err;
+ }
+ if (i<0 || i>= ZD1201_NUMKEYS)
+ return -EINVAL;
+
+ erq->flags |= i+1;
+
+ erq->length = zd->encode_keylen[i];
+ memcpy(key, zd->encode_keys[i], erq->length);
+
+ return 0;
+}
+
+static int zd1201_set_power(struct net_device *dev,
+ struct iw_request_info *info, struct iw_param *vwrq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short enabled, duration, level;
+ int err;
+
+ enabled = vwrq->disabled ? 0 : 1;
+ if (enabled) {
+ if (vwrq->flags & IW_POWER_PERIOD) {
+ duration = vwrq->value;
+ err = zd1201_setconfig16(zd,
+ ZD1201_RID_CNFMAXSLEEPDURATION, duration);
+ if (err)
+ return err;
+ goto out;
+ }
+ if (vwrq->flags & IW_POWER_TIMEOUT) {
+ err = zd1201_getconfig16(zd,
+ ZD1201_RID_CNFMAXSLEEPDURATION, &duration);
+ if (err)
+ return err;
+ level = vwrq->value * 4 / duration;
+ if (level > 4)
+ level = 4;
+ if (level < 0)
+ level = 0;
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFPMEPS,
+ level);
+ if (err)
+ return err;
+ goto out;
+ }
+ return -EINVAL;
+ }
+out:
+ return zd1201_setconfig16(zd, ZD1201_RID_CNFPMENABLED, enabled);
+}
+
+static int zd1201_get_power(struct net_device *dev,
+ struct iw_request_info *info, struct iw_param *vwrq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short enabled, level, duration;
+ int err;
+
+ err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMENABLED, &enabled);
+ if (err)
+ return err;
+ err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMEPS, &level);
+ if (err)
+ return err;
+ err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXSLEEPDURATION, &duration);
+ if (err)
+ return err;
+ vwrq->disabled = enabled ? 0 : 1;
+ if (vwrq->flags & IW_POWER_TYPE) {
+ if (vwrq->flags & IW_POWER_PERIOD) {
+ vwrq->value = duration;
+ vwrq->flags = IW_POWER_PERIOD;
+ } else {
+ vwrq->value = duration * level / 4;
+ vwrq->flags = IW_POWER_TIMEOUT;
+ }
+ }
+ if (vwrq->flags & IW_POWER_MODE) {
+ if (enabled && level)
+ vwrq->flags = IW_POWER_UNICAST_R;
+ else
+ vwrq->flags = IW_POWER_ALL_R;
+ }
+
+ return 0;
+}
+
+
+static const iw_handler zd1201_iw_handler[] =
+{
+ (iw_handler) zd1201_config_commit, /* SIOCSIWCOMMIT */
+ (iw_handler) zd1201_get_name, /* SIOCGIWNAME */
+ (iw_handler) NULL, /* SIOCSIWNWID */
+ (iw_handler) NULL, /* SIOCGIWNWID */
+ (iw_handler) zd1201_set_freq, /* SIOCSIWFREQ */
+ (iw_handler) zd1201_get_freq, /* SIOCGIWFREQ */
+ (iw_handler) zd1201_set_mode, /* SIOCSIWMODE */
+ (iw_handler) zd1201_get_mode, /* SIOCGIWMODE */
+ (iw_handler) NULL, /* SIOCSIWSENS */
+ (iw_handler) NULL, /* SIOCGIWSENS */
+ (iw_handler) NULL, /* SIOCSIWRANGE */
+ (iw_handler) zd1201_get_range, /* SIOCGIWRANGE */
+ (iw_handler) NULL, /* SIOCSIWPRIV */
+ (iw_handler) NULL, /* SIOCGIWPRIV */
+ (iw_handler) NULL, /* SIOCSIWSTATS */
+ (iw_handler) NULL, /* SIOCGIWSTATS */
+ (iw_handler) NULL, /* SIOCSIWSPY */
+ (iw_handler) NULL, /* SIOCGIWSPY */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL/*zd1201_set_wap*/, /* SIOCSIWAP */
+ (iw_handler) zd1201_get_wap, /* SIOCGIWAP */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* SIOCGIWAPLIST */
+ (iw_handler) zd1201_set_scan, /* SIOCSIWSCAN */
+ (iw_handler) zd1201_get_scan, /* SIOCGIWSCAN */
+ (iw_handler) zd1201_set_essid, /* SIOCSIWESSID */
+ (iw_handler) zd1201_get_essid, /* SIOCGIWESSID */
+ (iw_handler) NULL, /* SIOCSIWNICKN */
+ (iw_handler) zd1201_get_nick, /* SIOCGIWNICKN */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) zd1201_set_rate, /* SIOCSIWRATE */
+ (iw_handler) zd1201_get_rate, /* SIOCGIWRATE */
+ (iw_handler) zd1201_set_rts, /* SIOCSIWRTS */
+ (iw_handler) zd1201_get_rts, /* SIOCGIWRTS */
+ (iw_handler) zd1201_set_frag, /* SIOCSIWFRAG */
+ (iw_handler) zd1201_get_frag, /* SIOCGIWFRAG */
+ (iw_handler) NULL, /* SIOCSIWTXPOW */
+ (iw_handler) NULL, /* SIOCGIWTXPOW */
+ (iw_handler) zd1201_set_retry, /* SIOCSIWRETRY */
+ (iw_handler) zd1201_get_retry, /* SIOCGIWRETRY */
+ (iw_handler) zd1201_set_encode, /* SIOCSIWENCODE */
+ (iw_handler) zd1201_get_encode, /* SIOCGIWENCODE */
+ (iw_handler) zd1201_set_power, /* SIOCSIWPOWER */
+ (iw_handler) zd1201_get_power, /* SIOCGIWPOWER */
+};
+
+static int zd1201_set_hostauth(struct net_device *dev,
+ struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+
+ if (!zd->ap)
+ return -EOPNOTSUPP;
+
+ return zd1201_setconfig16(zd, ZD1201_RID_CNFHOSTAUTH, rrq->value);
+}
+
+static int zd1201_get_hostauth(struct net_device *dev,
+ struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short hostauth;
+ int err;
+
+ if (!zd->ap)
+ return -EOPNOTSUPP;
+
+ err = zd1201_getconfig16(zd, ZD1201_RID_CNFHOSTAUTH, &hostauth);
+ if (err)
+ return err;
+ rrq->value = hostauth;
+ rrq->fixed = 1;
+
+ return 0;
+}
+
+static int zd1201_auth_sta(struct net_device *dev,
+ struct iw_request_info *info, struct sockaddr *sta, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ unsigned char buffer[10];
+
+ if (!zd->ap)
+ return -EOPNOTSUPP;
+
+ memcpy(buffer, sta->sa_data, ETH_ALEN);
+ *(short*)(buffer+6) = 0; /* 0==success, 1==failure */
+ *(short*)(buffer+8) = 0;
+
+ return zd1201_setconfig(zd, ZD1201_RID_AUTHENTICATESTA, buffer, 10, 1);
+}
+
+static int zd1201_set_maxassoc(struct net_device *dev,
+ struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+
+ if (!zd->ap)
+ return -EOPNOTSUPP;
+
+ return zd1201_setconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, rrq->value);
+}
+
+static int zd1201_get_maxassoc(struct net_device *dev,
+ struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+ struct zd1201 *zd = netdev_priv(dev);
+ short maxassoc;
+ int err;
+
+ if (!zd->ap)
+ return -EOPNOTSUPP;
+
+ err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, &maxassoc);
+ if (err)
+ return err;
+ rrq->value = maxassoc;
+ rrq->fixed = 1;
+
+ return 0;
+}
+
+static const iw_handler zd1201_private_handler[] = {
+ (iw_handler) zd1201_set_hostauth, /* ZD1201SIWHOSTAUTH */
+ (iw_handler) zd1201_get_hostauth, /* ZD1201GIWHOSTAUTH */
+ (iw_handler) zd1201_auth_sta, /* ZD1201SIWAUTHSTA */
+ (iw_handler) NULL, /* nothing to get */
+ (iw_handler) zd1201_set_maxassoc, /* ZD1201SIMAXASSOC */
+ (iw_handler) zd1201_get_maxassoc, /* ZD1201GIMAXASSOC */
+};
+
+static const struct iw_priv_args zd1201_private_args[] = {
+ { ZD1201SIWHOSTAUTH, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ IW_PRIV_TYPE_NONE, "sethostauth" },
+ { ZD1201GIWHOSTAUTH, IW_PRIV_TYPE_NONE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostauth" },
+ { ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1,
+ IW_PRIV_TYPE_NONE, "authstation" },
+ { ZD1201SIWMAXASSOC, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ IW_PRIV_TYPE_NONE, "setmaxassoc" },
+ { ZD1201GIWMAXASSOC, IW_PRIV_TYPE_NONE,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmaxassoc" },
+};
+
+static const struct iw_handler_def zd1201_iw_handlers = {
+ .num_standard = ARRAY_SIZE(zd1201_iw_handler),
+ .num_private = ARRAY_SIZE(zd1201_private_handler),
+ .num_private_args = ARRAY_SIZE(zd1201_private_args),
+ .standard = (iw_handler *)zd1201_iw_handler,
+ .private = (iw_handler *)zd1201_private_handler,
+ .private_args = (struct iw_priv_args *) zd1201_private_args,
+ .get_wireless_stats = zd1201_get_wireless_stats,
+};
+
+static const struct net_device_ops zd1201_netdev_ops = {
+ .ndo_open = zd1201_net_open,
+ .ndo_stop = zd1201_net_stop,
+ .ndo_start_xmit = zd1201_hard_start_xmit,
+ .ndo_tx_timeout = zd1201_tx_timeout,
+ .ndo_set_rx_mode = zd1201_set_multicast,
+ .ndo_set_mac_address = zd1201_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int zd1201_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct zd1201 *zd;
+ struct net_device *dev;
+ struct usb_device *usb;
+ int err;
+ short porttype;
+ char buf[IW_ESSID_MAX_SIZE+2];
+ u8 addr[ETH_ALEN];
+
+ usb = interface_to_usbdev(interface);
+
+ dev = alloc_etherdev(sizeof(*zd));
+ if (!dev)
+ return -ENOMEM;
+ zd = netdev_priv(dev);
+ zd->dev = dev;
+
+ zd->ap = ap;
+ zd->usb = usb;
+ zd->removed = 0;
+ init_waitqueue_head(&zd->rxdataq);
+ INIT_HLIST_HEAD(&zd->fraglist);
+
+ err = zd1201_fw_upload(usb, zd->ap);
+ if (err) {
+ dev_err(&usb->dev, "zd1201 firmware upload failed: %d\n", err);
+ goto err_zd;
+ }
+
+ zd->endp_in = 1;
+ zd->endp_out = 1;
+ zd->endp_out2 = 2;
+ zd->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ zd->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!zd->rx_urb || !zd->tx_urb) {
+ err = -ENOMEM;
+ goto err_zd;
+ }
+
+ mdelay(100);
+ err = zd1201_drvr_start(zd);
+ if (err)
+ goto err_zd;
+
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXDATALEN, 2312);
+ if (err)
+ goto err_start;
+
+ err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL,
+ ZD1201_RATEB1 | ZD1201_RATEB2 | ZD1201_RATEB5 | ZD1201_RATEB11);
+ if (err)
+ goto err_start;
+
+ dev->netdev_ops = &zd1201_netdev_ops;
+ dev->wireless_handlers = &zd1201_iw_handlers;
+ dev->watchdog_timeo = ZD1201_TX_TIMEOUT;
+ strcpy(dev->name, "wlan%d");
+
+ err = zd1201_getconfig(zd, ZD1201_RID_CNFOWNMACADDR, addr, ETH_ALEN);
+ if (err)
+ goto err_start;
+ eth_hw_addr_set(dev, addr);
+
+ /* Set wildcard essid to match zd->essid */
+ *(__le16 *)buf = cpu_to_le16(0);
+ err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
+ IW_ESSID_MAX_SIZE+2, 1);
+ if (err)
+ goto err_start;
+
+ if (zd->ap)
+ porttype = ZD1201_PORTTYPE_AP;
+ else
+ porttype = ZD1201_PORTTYPE_BSS;
+ err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
+ if (err)
+ goto err_start;
+
+ SET_NETDEV_DEV(dev, &usb->dev);
+
+ err = register_netdev(dev);
+ if (err)
+ goto err_start;
+ dev_info(&usb->dev, "%s: ZD1201 USB Wireless interface\n",
+ dev->name);
+
+ usb_set_intfdata(interface, zd);
+ zd1201_enable(zd); /* zd1201 likes to startup enabled, */
+ zd1201_disable(zd); /* interfering with all the wifis in range */
+ return 0;
+
+err_start:
+ /* Leave the device in reset state */
+ zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
+err_zd:
+ usb_free_urb(zd->tx_urb);
+ usb_free_urb(zd->rx_urb);
+ free_netdev(dev);
+ return err;
+}
+
+static void zd1201_disconnect(struct usb_interface *interface)
+{
+ struct zd1201 *zd = usb_get_intfdata(interface);
+ struct hlist_node *node2;
+ struct zd1201_frag *frag;
+
+ if (!zd)
+ return;
+ usb_set_intfdata(interface, NULL);
+
+ hlist_for_each_entry_safe(frag, node2, &zd->fraglist, fnode) {
+ hlist_del_init(&frag->fnode);
+ kfree_skb(frag->skb);
+ kfree(frag);
+ }
+
+ if (zd->tx_urb) {
+ usb_kill_urb(zd->tx_urb);
+ usb_free_urb(zd->tx_urb);
+ }
+ if (zd->rx_urb) {
+ usb_kill_urb(zd->rx_urb);
+ usb_free_urb(zd->rx_urb);
+ }
+
+ if (zd->dev) {
+ unregister_netdev(zd->dev);
+ free_netdev(zd->dev);
+ }
+}
+
+#ifdef CONFIG_PM
+
+static int zd1201_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct zd1201 *zd = usb_get_intfdata(interface);
+
+ netif_device_detach(zd->dev);
+
+ zd->was_enabled = zd->mac_enabled;
+
+ if (zd->was_enabled)
+ return zd1201_disable(zd);
+ else
+ return 0;
+}
+
+static int zd1201_resume(struct usb_interface *interface)
+{
+ struct zd1201 *zd = usb_get_intfdata(interface);
+
+ if (!zd || !zd->dev)
+ return -ENODEV;
+
+ netif_device_attach(zd->dev);
+
+ if (zd->was_enabled)
+ return zd1201_enable(zd);
+ else
+ return 0;
+}
+
+#else
+
+#define zd1201_suspend NULL
+#define zd1201_resume NULL
+
+#endif
+
+static struct usb_driver zd1201_usb = {
+ .name = "zd1201",
+ .probe = zd1201_probe,
+ .disconnect = zd1201_disconnect,
+ .id_table = zd1201_table,
+ .suspend = zd1201_suspend,
+ .resume = zd1201_resume,
+ .disable_hub_initiated_lpm = 1,
+};
+
+module_usb_driver(zd1201_usb);
diff --git a/drivers/net/wireless/zydas/zd1201.h b/drivers/net/wireless/zydas/zd1201.h
new file mode 100644
index 000000000..c46ac8755
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1201.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org)
+ *
+ * Parts of this driver have been derived from a wlan-ng version
+ * modified by ZyDAS.
+ * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
+ */
+
+#ifndef _INCLUDE_ZD1201_H_
+#define _INCLUDE_ZD1201_H_
+
+#define ZD1201_NUMKEYS 4
+#define ZD1201_MAXKEYLEN 13
+#define ZD1201_MAXMULTI 16
+#define ZD1201_FRAGMAX 2500
+#define ZD1201_FRAGMIN 256
+#define ZD1201_RTSMAX 2500
+
+#define ZD1201_RXSIZE 3000
+
+struct zd1201 {
+ struct usb_device *usb;
+ int removed;
+ struct net_device *dev;
+ struct iw_statistics iwstats;
+
+ int endp_in;
+ int endp_out;
+ int endp_out2;
+ struct urb *rx_urb;
+ struct urb *tx_urb;
+
+ unsigned char rxdata[ZD1201_RXSIZE];
+ int rxlen;
+ wait_queue_head_t rxdataq;
+ int rxdatas;
+ struct hlist_head fraglist;
+ unsigned char txdata[ZD1201_RXSIZE];
+
+ int ap;
+ char essid[IW_ESSID_MAX_SIZE+1];
+ int essidlen;
+ int mac_enabled;
+ int was_enabled;
+ int monitor;
+ int encode_enabled;
+ int encode_restricted;
+ unsigned char encode_keys[ZD1201_NUMKEYS][ZD1201_MAXKEYLEN];
+ int encode_keylen[ZD1201_NUMKEYS];
+};
+
+struct zd1201_frag {
+ struct hlist_node fnode;
+ int seq;
+ struct sk_buff *skb;
+};
+
+#define ZD1201SIWHOSTAUTH SIOCIWFIRSTPRIV
+#define ZD1201GIWHOSTAUTH ZD1201SIWHOSTAUTH+1
+#define ZD1201SIWAUTHSTA SIOCIWFIRSTPRIV+2
+#define ZD1201SIWMAXASSOC SIOCIWFIRSTPRIV+4
+#define ZD1201GIWMAXASSOC ZD1201SIWMAXASSOC+1
+
+#define ZD1201_FW_TIMEOUT (1000)
+
+#define ZD1201_TX_TIMEOUT (2000)
+
+#define ZD1201_USB_CMDREQ 0
+#define ZD1201_USB_RESREQ 1
+
+#define ZD1201_CMDCODE_INIT 0x00
+#define ZD1201_CMDCODE_ENABLE 0x01
+#define ZD1201_CMDCODE_DISABLE 0x02
+#define ZD1201_CMDCODE_ALLOC 0x0a
+#define ZD1201_CMDCODE_INQUIRE 0x11
+#define ZD1201_CMDCODE_SETRXRID 0x17
+#define ZD1201_CMDCODE_ACCESS 0x21
+
+#define ZD1201_PACKET_EVENTSTAT 0x0
+#define ZD1201_PACKET_RXDATA 0x1
+#define ZD1201_PACKET_INQUIRE 0x2
+#define ZD1201_PACKET_RESOURCE 0x3
+
+#define ZD1201_ACCESSBIT 0x0100
+
+#define ZD1201_RID_CNFPORTTYPE 0xfc00
+#define ZD1201_RID_CNFOWNMACADDR 0xfc01
+#define ZD1201_RID_CNFDESIREDSSID 0xfc02
+#define ZD1201_RID_CNFOWNCHANNEL 0xfc03
+#define ZD1201_RID_CNFOWNSSID 0xfc04
+#define ZD1201_RID_CNFMAXDATALEN 0xfc07
+#define ZD1201_RID_CNFPMENABLED 0xfc09
+#define ZD1201_RID_CNFPMEPS 0xfc0a
+#define ZD1201_RID_CNFMAXSLEEPDURATION 0xfc0c
+#define ZD1201_RID_CNFDEFAULTKEYID 0xfc23
+#define ZD1201_RID_CNFDEFAULTKEY0 0xfc24
+#define ZD1201_RID_CNFDEFAULTKEY1 0xfc25
+#define ZD1201_RID_CNFDEFAULTKEY2 0xfc26
+#define ZD1201_RID_CNFDEFAULTKEY3 0xfc27
+#define ZD1201_RID_CNFWEBFLAGS 0xfc28
+#define ZD1201_RID_CNFAUTHENTICATION 0xfc2a
+#define ZD1201_RID_CNFMAXASSOCSTATIONS 0xfc2b
+#define ZD1201_RID_CNFHOSTAUTH 0xfc2e
+#define ZD1201_RID_CNFGROUPADDRESS 0xfc80
+#define ZD1201_RID_CNFFRAGTHRESHOLD 0xfc82
+#define ZD1201_RID_CNFRTSTHRESHOLD 0xfc83
+#define ZD1201_RID_TXRATECNTL 0xfc84
+#define ZD1201_RID_PROMISCUOUSMODE 0xfc85
+#define ZD1201_RID_CNFBASICRATES 0xfcb3
+#define ZD1201_RID_AUTHENTICATESTA 0xfce3
+#define ZD1201_RID_CURRENTBSSID 0xfd42
+#define ZD1201_RID_COMMSQUALITY 0xfd43
+#define ZD1201_RID_CURRENTTXRATE 0xfd44
+#define ZD1201_RID_CNFMAXTXBUFFERNUMBER 0xfda0
+#define ZD1201_RID_CURRENTCHANNEL 0xfdc1
+
+#define ZD1201_INQ_SCANRESULTS 0xf101
+
+#define ZD1201_INF_LINKSTATUS 0xf200
+#define ZD1201_INF_ASSOCSTATUS 0xf201
+#define ZD1201_INF_AUTHREQ 0xf202
+
+#define ZD1201_ASSOCSTATUS_STAASSOC 0x1
+#define ZD1201_ASSOCSTATUS_REASSOC 0x2
+#define ZD1201_ASSOCSTATUS_DISASSOC 0x3
+#define ZD1201_ASSOCSTATUS_ASSOCFAIL 0x4
+#define ZD1201_ASSOCSTATUS_AUTHFAIL 0x5
+
+#define ZD1201_PORTTYPE_IBSS 0
+#define ZD1201_PORTTYPE_BSS 1
+#define ZD1201_PORTTYPE_WDS 2
+#define ZD1201_PORTTYPE_PSEUDOIBSS 3
+#define ZD1201_PORTTYPE_AP 6
+
+#define ZD1201_RATEB1 1
+#define ZD1201_RATEB2 2
+#define ZD1201_RATEB5 4 /* 5.5 really, but 5 is shorter :) */
+#define ZD1201_RATEB11 8
+
+#define ZD1201_CNFAUTHENTICATION_OPENSYSTEM 0x0001
+#define ZD1201_CNFAUTHENTICATION_SHAREDKEY 0x0002
+
+#endif /* _INCLUDE_ZD1201_H_ */
diff --git a/drivers/net/wireless/zydas/zd1211rw/Kconfig b/drivers/net/wireless/zydas/zd1211rw/Kconfig
new file mode 100644
index 000000000..f9da8c068
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config ZD1211RW
+ tristate "ZyDAS ZD1211/ZD1211B USB-wireless support"
+ depends on USB && MAC80211
+ select FW_LOADER
+ help
+ This is a 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://sf.net/projects/zd1211/files/
+
+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.
+
diff --git a/drivers/net/wireless/zydas/zd1211rw/Makefile b/drivers/net/wireless/zydas/zd1211rw/Makefile
new file mode 100644
index 000000000..1647a449c
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_ZD1211RW) += zd1211rw.o
+
+zd1211rw-objs := zd_chip.o zd_mac.o \
+ zd_rf_al2230.o zd_rf_rf2959.o \
+ zd_rf_al7230b.o zd_rf_uw2453.o \
+ zd_rf.o zd_usb.o
+
+ccflags-$(CONFIG_ZD1211RW_DEBUG) := -DDEBUG
+
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_chip.c b/drivers/net/wireless/zydas/zd1211rw/zd_chip.c
new file mode 100644
index 000000000..3bb51dc8d
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_chip.c
@@ -0,0 +1,1547 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+/* This file implements all the hardware specific functions for the ZD1211
+ * and ZD1211B chips. Support for the ZD1211B was possible after Timothy
+ * Legge sent me a ZD1211B device. Thank you Tim. -- Uli
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include "zd_def.h"
+#include "zd_chip.h"
+#include "zd_mac.h"
+#include "zd_rf.h"
+
+void zd_chip_init(struct zd_chip *chip,
+ struct ieee80211_hw *hw,
+ struct usb_interface *intf)
+{
+ memset(chip, 0, sizeof(*chip));
+ mutex_init(&chip->mutex);
+ zd_usb_init(&chip->usb, hw, intf);
+ zd_rf_init(&chip->rf);
+}
+
+void zd_chip_clear(struct zd_chip *chip)
+{
+ ZD_ASSERT(!mutex_is_locked(&chip->mutex));
+ zd_usb_clear(&chip->usb);
+ zd_rf_clear(&chip->rf);
+ mutex_destroy(&chip->mutex);
+ ZD_MEMCLEAR(chip, sizeof(*chip));
+}
+
+static int scnprint_mac_oui(struct zd_chip *chip, char *buffer, size_t size)
+{
+ u8 *addr = zd_mac_get_perm_addr(zd_chip_to_mac(chip));
+ return scnprintf(buffer, size, "%3phD", addr);
+}
+
+/* Prints an identifier line, which will support debugging. */
+static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size)
+{
+ int i = 0;
+
+ i = scnprintf(buffer, size, "zd1211%s chip ",
+ zd_chip_is_zd1211b(chip) ? "b" : "");
+ i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i);
+ i += scnprintf(buffer+i, size-i, " ");
+ i += scnprint_mac_oui(chip, buffer+i, size-i);
+ i += scnprintf(buffer+i, size-i, " ");
+ i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i);
+ i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type,
+ chip->patch_cck_gain ? 'g' : '-',
+ chip->patch_cr157 ? '7' : '-',
+ chip->patch_6m_band_edge ? '6' : '-',
+ chip->new_phy_layout ? 'N' : '-',
+ chip->al2230s_bit ? 'S' : '-');
+ return i;
+}
+
+static void print_id(struct zd_chip *chip)
+{
+ char buffer[80];
+
+ scnprint_id(chip, buffer, sizeof(buffer));
+ buffer[sizeof(buffer)-1] = 0;
+ dev_info(zd_chip_dev(chip), "%s\n", buffer);
+}
+
+static zd_addr_t inc_addr(zd_addr_t addr)
+{
+ u16 a = (u16)addr;
+ /* Control registers use byte addressing, but everything else uses word
+ * addressing. */
+ if ((a & 0xf000) == CR_START)
+ a += 2;
+ else
+ a += 1;
+ return (zd_addr_t)a;
+}
+
+/* Read a variable number of 32-bit values. Parameter count is not allowed to
+ * exceed USB_MAX_IOREAD32_COUNT.
+ */
+int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr,
+ unsigned int count)
+{
+ int r;
+ int i;
+ zd_addr_t a16[USB_MAX_IOREAD32_COUNT * 2];
+ u16 v16[USB_MAX_IOREAD32_COUNT * 2];
+ unsigned int count16;
+
+ if (count > USB_MAX_IOREAD32_COUNT)
+ return -EINVAL;
+
+ /* Use stack for values and addresses. */
+ count16 = 2 * count;
+ BUG_ON(count16 * sizeof(zd_addr_t) > sizeof(a16));
+ BUG_ON(count16 * sizeof(u16) > sizeof(v16));
+
+ for (i = 0; i < count; i++) {
+ int j = 2*i;
+ /* We read the high word always first. */
+ a16[j] = inc_addr(addr[i]);
+ a16[j+1] = addr[i];
+ }
+
+ r = zd_ioread16v_locked(chip, v16, a16, count16);
+ if (r) {
+ dev_dbg_f(zd_chip_dev(chip),
+ "error: %s. Error number %d\n", __func__, r);
+ return r;
+ }
+
+ for (i = 0; i < count; i++) {
+ int j = 2*i;
+ values[i] = (v16[j] << 16) | v16[j+1];
+ }
+
+ return 0;
+}
+
+static int _zd_iowrite32v_async_locked(struct zd_chip *chip,
+ const struct zd_ioreq32 *ioreqs,
+ unsigned int count)
+{
+ int i, j, r;
+ struct zd_ioreq16 ioreqs16[USB_MAX_IOWRITE32_COUNT * 2];
+ unsigned int count16;
+
+ /* Use stack for values and addresses. */
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+
+ if (count == 0)
+ return 0;
+ if (count > USB_MAX_IOWRITE32_COUNT)
+ return -EINVAL;
+
+ count16 = 2 * count;
+ BUG_ON(count16 * sizeof(struct zd_ioreq16) > sizeof(ioreqs16));
+
+ for (i = 0; i < count; i++) {
+ j = 2*i;
+ /* We write the high word always first. */
+ ioreqs16[j].value = ioreqs[i].value >> 16;
+ ioreqs16[j].addr = inc_addr(ioreqs[i].addr);
+ ioreqs16[j+1].value = ioreqs[i].value;
+ ioreqs16[j+1].addr = ioreqs[i].addr;
+ }
+
+ r = zd_usb_iowrite16v_async(&chip->usb, ioreqs16, count16);
+#ifdef DEBUG
+ if (r) {
+ dev_dbg_f(zd_chip_dev(chip),
+ "error %d in zd_usb_write16v\n", r);
+ }
+#endif /* DEBUG */
+ return r;
+}
+
+int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+ unsigned int count)
+{
+ int r;
+
+ zd_usb_iowrite16v_async_start(&chip->usb);
+ r = _zd_iowrite32v_async_locked(chip, ioreqs, count);
+ if (r) {
+ zd_usb_iowrite16v_async_end(&chip->usb, 0);
+ return r;
+ }
+ return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */);
+}
+
+int zd_iowrite16a_locked(struct zd_chip *chip,
+ const struct zd_ioreq16 *ioreqs, unsigned int count)
+{
+ int r;
+ unsigned int i, j, t, max;
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ zd_usb_iowrite16v_async_start(&chip->usb);
+
+ for (i = 0; i < count; i += j + t) {
+ t = 0;
+ max = count-i;
+ if (max > USB_MAX_IOWRITE16_COUNT)
+ max = USB_MAX_IOWRITE16_COUNT;
+ for (j = 0; j < max; j++) {
+ if (!ioreqs[i+j].addr) {
+ t = 1;
+ break;
+ }
+ }
+
+ r = zd_usb_iowrite16v_async(&chip->usb, &ioreqs[i], j);
+ if (r) {
+ zd_usb_iowrite16v_async_end(&chip->usb, 0);
+ dev_dbg_f(zd_chip_dev(chip),
+ "error zd_usb_iowrite16v. Error number %d\n",
+ r);
+ return r;
+ }
+ }
+
+ return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */);
+}
+
+/* Writes a variable number of 32 bit registers. The functions will split
+ * that in several USB requests. A split can be forced by inserting an IO
+ * request with an zero address field.
+ */
+int zd_iowrite32a_locked(struct zd_chip *chip,
+ const struct zd_ioreq32 *ioreqs, unsigned int count)
+{
+ int r;
+ unsigned int i, j, t, max;
+
+ zd_usb_iowrite16v_async_start(&chip->usb);
+
+ for (i = 0; i < count; i += j + t) {
+ t = 0;
+ max = count-i;
+ if (max > USB_MAX_IOWRITE32_COUNT)
+ max = USB_MAX_IOWRITE32_COUNT;
+ for (j = 0; j < max; j++) {
+ if (!ioreqs[i+j].addr) {
+ t = 1;
+ break;
+ }
+ }
+
+ r = _zd_iowrite32v_async_locked(chip, &ioreqs[i], j);
+ if (r) {
+ zd_usb_iowrite16v_async_end(&chip->usb, 0);
+ dev_dbg_f(zd_chip_dev(chip),
+ "error _%s. Error number %d\n", __func__,
+ r);
+ return r;
+ }
+ }
+
+ return zd_usb_iowrite16v_async_end(&chip->usb, 50 /* ms */);
+}
+
+int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = zd_ioread16_locked(chip, value, addr);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = zd_ioread32_locked(chip, value, addr);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = zd_iowrite16_locked(chip, value, addr);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = zd_iowrite32_locked(chip, value, addr);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses,
+ u32 *values, unsigned int count)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = zd_ioread32v_locked(chip, values, addresses, count);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+ unsigned int count)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = zd_iowrite32a_locked(chip, ioreqs, count);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+static int read_pod(struct zd_chip *chip, u8 *rf_type)
+{
+ int r;
+ u32 value;
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ r = zd_ioread32_locked(chip, &value, E2P_POD);
+ if (r)
+ goto error;
+ dev_dbg_f(zd_chip_dev(chip), "E2P_POD %#010x\n", value);
+
+ /* FIXME: AL2230 handling (Bit 7 in POD) */
+ *rf_type = value & 0x0f;
+ chip->pa_type = (value >> 16) & 0x0f;
+ chip->patch_cck_gain = (value >> 8) & 0x1;
+ chip->patch_cr157 = (value >> 13) & 0x1;
+ chip->patch_6m_band_edge = (value >> 21) & 0x1;
+ chip->new_phy_layout = (value >> 31) & 0x1;
+ chip->al2230s_bit = (value >> 7) & 0x1;
+ chip->link_led = ((value >> 4) & 1) ? LED1 : LED2;
+ chip->supports_tx_led = 1;
+ if (value & (1 << 24)) { /* LED scenario */
+ if (value & (1 << 29))
+ chip->supports_tx_led = 0;
+ }
+
+ dev_dbg_f(zd_chip_dev(chip),
+ "RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d "
+ "patch 6M %d new PHY %d link LED%d tx led %d\n",
+ zd_rf_name(*rf_type), *rf_type,
+ chip->pa_type, chip->patch_cck_gain,
+ chip->patch_cr157, chip->patch_6m_band_edge,
+ chip->new_phy_layout,
+ chip->link_led == LED1 ? 1 : 2,
+ chip->supports_tx_led);
+ return 0;
+error:
+ *rf_type = 0;
+ chip->pa_type = 0;
+ chip->patch_cck_gain = 0;
+ chip->patch_cr157 = 0;
+ chip->patch_6m_band_edge = 0;
+ chip->new_phy_layout = 0;
+ return r;
+}
+
+static int zd_write_mac_addr_common(struct zd_chip *chip, const u8 *mac_addr,
+ const struct zd_ioreq32 *in_reqs,
+ const char *type)
+{
+ int r;
+ struct zd_ioreq32 reqs[2] = {in_reqs[0], in_reqs[1]};
+
+ if (mac_addr) {
+ reqs[0].value = (mac_addr[3] << 24)
+ | (mac_addr[2] << 16)
+ | (mac_addr[1] << 8)
+ | mac_addr[0];
+ reqs[1].value = (mac_addr[5] << 8)
+ | mac_addr[4];
+ dev_dbg_f(zd_chip_dev(chip), "%s addr %pM\n", type, mac_addr);
+ } else {
+ dev_dbg_f(zd_chip_dev(chip), "set NULL %s\n", type);
+ }
+
+ mutex_lock(&chip->mutex);
+ r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs));
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+/* MAC address: if custom mac addresses are to be used CR_MAC_ADDR_P1 and
+ * CR_MAC_ADDR_P2 must be overwritten
+ */
+int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
+{
+ static const struct zd_ioreq32 reqs[2] = {
+ [0] = { .addr = CR_MAC_ADDR_P1 },
+ [1] = { .addr = CR_MAC_ADDR_P2 },
+ };
+
+ return zd_write_mac_addr_common(chip, mac_addr, reqs, "mac");
+}
+
+int zd_write_bssid(struct zd_chip *chip, const u8 *bssid)
+{
+ static const struct zd_ioreq32 reqs[2] = {
+ [0] = { .addr = CR_BSSID_P1 },
+ [1] = { .addr = CR_BSSID_P2 },
+ };
+
+ return zd_write_mac_addr_common(chip, bssid, reqs, "bssid");
+}
+
+int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain)
+{
+ int r;
+ u32 value;
+
+ mutex_lock(&chip->mutex);
+ r = zd_ioread32_locked(chip, &value, E2P_SUBID);
+ mutex_unlock(&chip->mutex);
+ if (r)
+ return r;
+
+ *regdomain = value >> 16;
+ dev_dbg_f(zd_chip_dev(chip), "regdomain: %#04x\n", *regdomain);
+
+ return 0;
+}
+
+static int read_values(struct zd_chip *chip, u8 *values, size_t count,
+ zd_addr_t e2p_addr, u32 guard)
+{
+ int r;
+ int i;
+ u32 v;
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ for (i = 0;;) {
+ r = zd_ioread32_locked(chip, &v,
+ (zd_addr_t)((u16)e2p_addr+i/2));
+ if (r)
+ return r;
+ v -= guard;
+ if (i+4 < count) {
+ values[i++] = v;
+ values[i++] = v >> 8;
+ values[i++] = v >> 16;
+ values[i++] = v >> 24;
+ continue;
+ }
+ for (;i < count; i++)
+ values[i] = v >> (8*(i%3));
+ return 0;
+ }
+}
+
+static int read_pwr_cal_values(struct zd_chip *chip)
+{
+ return read_values(chip, chip->pwr_cal_values,
+ E2P_CHANNEL_COUNT, E2P_PWR_CAL_VALUE1,
+ 0);
+}
+
+static int read_pwr_int_values(struct zd_chip *chip)
+{
+ return read_values(chip, chip->pwr_int_values,
+ E2P_CHANNEL_COUNT, E2P_PWR_INT_VALUE1,
+ E2P_PWR_INT_GUARD);
+}
+
+static int read_ofdm_cal_values(struct zd_chip *chip)
+{
+ int r;
+ int i;
+ static const zd_addr_t addresses[] = {
+ E2P_36M_CAL_VALUE1,
+ E2P_48M_CAL_VALUE1,
+ E2P_54M_CAL_VALUE1,
+ };
+
+ for (i = 0; i < 3; i++) {
+ r = read_values(chip, chip->ofdm_cal_values[i],
+ E2P_CHANNEL_COUNT, addresses[i], 0);
+ if (r)
+ return r;
+ }
+ return 0;
+}
+
+static int read_cal_int_tables(struct zd_chip *chip)
+{
+ int r;
+
+ r = read_pwr_cal_values(chip);
+ if (r)
+ return r;
+ r = read_pwr_int_values(chip);
+ if (r)
+ return r;
+ r = read_ofdm_cal_values(chip);
+ if (r)
+ return r;
+ return 0;
+}
+
+/* phy means physical registers */
+int zd_chip_lock_phy_regs(struct zd_chip *chip)
+{
+ int r;
+ u32 tmp;
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ r = zd_ioread32_locked(chip, &tmp, CR_REG1);
+ if (r) {
+ dev_err(zd_chip_dev(chip), "error ioread32(CR_REG1): %d\n", r);
+ return r;
+ }
+
+ tmp &= ~UNLOCK_PHY_REGS;
+
+ r = zd_iowrite32_locked(chip, tmp, CR_REG1);
+ if (r)
+ dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r);
+ return r;
+}
+
+int zd_chip_unlock_phy_regs(struct zd_chip *chip)
+{
+ int r;
+ u32 tmp;
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ r = zd_ioread32_locked(chip, &tmp, CR_REG1);
+ if (r) {
+ dev_err(zd_chip_dev(chip),
+ "error ioread32(CR_REG1): %d\n", r);
+ return r;
+ }
+
+ tmp |= UNLOCK_PHY_REGS;
+
+ r = zd_iowrite32_locked(chip, tmp, CR_REG1);
+ if (r)
+ dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r);
+ return r;
+}
+
+/* ZD_CR157 can be optionally patched by the EEPROM for original ZD1211 */
+static int patch_cr157(struct zd_chip *chip)
+{
+ int r;
+ u16 value;
+
+ if (!chip->patch_cr157)
+ return 0;
+
+ r = zd_ioread16_locked(chip, &value, E2P_PHY_REG);
+ if (r)
+ return r;
+
+ dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value >> 8);
+ return zd_iowrite32_locked(chip, value >> 8, ZD_CR157);
+}
+
+/*
+ * 6M band edge can be optionally overwritten for certain RF's
+ * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge
+ * bit (for AL2230, AL2230S)
+ */
+static int patch_6m_band_edge(struct zd_chip *chip, u8 channel)
+{
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ if (!chip->patch_6m_band_edge)
+ return 0;
+
+ return zd_rf_patch_6m_band_edge(&chip->rf, channel);
+}
+
+/* Generic implementation of 6M band edge patching, used by most RFs via
+ * zd_rf_generic_patch_6m() */
+int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel)
+{
+ struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 },
+ { ZD_CR47, 0x1e },
+ };
+
+ /* FIXME: Channel 11 is not the edge for all regulatory domains. */
+ if (channel == 1 || channel == 11)
+ ioreqs[0].value = 0x12;
+
+ dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel);
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211_hw_reset_phy(struct zd_chip *chip)
+{
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR0, 0x0a }, { ZD_CR1, 0x06 }, { ZD_CR2, 0x26 },
+ { ZD_CR3, 0x38 }, { ZD_CR4, 0x80 }, { ZD_CR9, 0xa0 },
+ { ZD_CR10, 0x81 }, { ZD_CR11, 0x00 }, { ZD_CR12, 0x7f },
+ { ZD_CR13, 0x8c }, { ZD_CR14, 0x80 }, { ZD_CR15, 0x3d },
+ { ZD_CR16, 0x20 }, { ZD_CR17, 0x1e }, { ZD_CR18, 0x0a },
+ { ZD_CR19, 0x48 }, { ZD_CR20, 0x0c }, { ZD_CR21, 0x0c },
+ { ZD_CR22, 0x23 }, { ZD_CR23, 0x90 }, { ZD_CR24, 0x14 },
+ { ZD_CR25, 0x40 }, { ZD_CR26, 0x10 }, { ZD_CR27, 0x19 },
+ { ZD_CR28, 0x7f }, { ZD_CR29, 0x80 }, { ZD_CR30, 0x4b },
+ { ZD_CR31, 0x60 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x08 },
+ { ZD_CR34, 0x06 }, { ZD_CR35, 0x0a }, { ZD_CR36, 0x00 },
+ { ZD_CR37, 0x00 }, { ZD_CR38, 0x38 }, { ZD_CR39, 0x0c },
+ { ZD_CR40, 0x84 }, { ZD_CR41, 0x2a }, { ZD_CR42, 0x80 },
+ { ZD_CR43, 0x10 }, { ZD_CR44, 0x12 }, { ZD_CR46, 0xff },
+ { ZD_CR47, 0x1E }, { ZD_CR48, 0x26 }, { ZD_CR49, 0x5b },
+ { ZD_CR64, 0xd0 }, { ZD_CR65, 0x04 }, { ZD_CR66, 0x58 },
+ { ZD_CR67, 0xc9 }, { ZD_CR68, 0x88 }, { ZD_CR69, 0x41 },
+ { ZD_CR70, 0x23 }, { ZD_CR71, 0x10 }, { ZD_CR72, 0xff },
+ { ZD_CR73, 0x32 }, { ZD_CR74, 0x30 }, { ZD_CR75, 0x65 },
+ { ZD_CR76, 0x41 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x30 },
+ { ZD_CR79, 0x68 }, { ZD_CR80, 0x64 }, { ZD_CR81, 0x64 },
+ { ZD_CR82, 0x00 }, { ZD_CR83, 0x00 }, { ZD_CR84, 0x00 },
+ { ZD_CR85, 0x02 }, { ZD_CR86, 0x00 }, { ZD_CR87, 0x00 },
+ { ZD_CR88, 0xff }, { ZD_CR89, 0xfc }, { ZD_CR90, 0x00 },
+ { ZD_CR91, 0x00 }, { ZD_CR92, 0x00 }, { ZD_CR93, 0x08 },
+ { ZD_CR94, 0x00 }, { ZD_CR95, 0x00 }, { ZD_CR96, 0xff },
+ { ZD_CR97, 0xe7 }, { ZD_CR98, 0x00 }, { ZD_CR99, 0x00 },
+ { ZD_CR100, 0x00 }, { ZD_CR101, 0xae }, { ZD_CR102, 0x02 },
+ { ZD_CR103, 0x00 }, { ZD_CR104, 0x03 }, { ZD_CR105, 0x65 },
+ { ZD_CR106, 0x04 }, { ZD_CR107, 0x00 }, { ZD_CR108, 0x0a },
+ { ZD_CR109, 0xaa }, { ZD_CR110, 0xaa }, { ZD_CR111, 0x25 },
+ { ZD_CR112, 0x25 }, { ZD_CR113, 0x00 }, { ZD_CR119, 0x1e },
+ { ZD_CR125, 0x90 }, { ZD_CR126, 0x00 }, { ZD_CR127, 0x00 },
+ { },
+ { ZD_CR5, 0x00 }, { ZD_CR6, 0x00 }, { ZD_CR7, 0x00 },
+ { ZD_CR8, 0x00 }, { ZD_CR9, 0x20 }, { ZD_CR12, 0xf0 },
+ { ZD_CR20, 0x0e }, { ZD_CR21, 0x0e }, { ZD_CR27, 0x10 },
+ { ZD_CR44, 0x33 }, { ZD_CR47, 0x1E }, { ZD_CR83, 0x24 },
+ { ZD_CR84, 0x04 }, { ZD_CR85, 0x00 }, { ZD_CR86, 0x0C },
+ { ZD_CR87, 0x12 }, { ZD_CR88, 0x0C }, { ZD_CR89, 0x00 },
+ { ZD_CR90, 0x10 }, { ZD_CR91, 0x08 }, { ZD_CR93, 0x00 },
+ { ZD_CR94, 0x01 }, { ZD_CR95, 0x00 }, { ZD_CR96, 0x50 },
+ { ZD_CR97, 0x37 }, { ZD_CR98, 0x35 }, { ZD_CR101, 0x13 },
+ { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, { ZD_CR104, 0x18 },
+ { ZD_CR105, 0x12 }, { ZD_CR109, 0x27 }, { ZD_CR110, 0x27 },
+ { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, { ZD_CR113, 0x27 },
+ { ZD_CR114, 0x27 }, { ZD_CR115, 0x26 }, { ZD_CR116, 0x24 },
+ { ZD_CR117, 0xfc }, { ZD_CR118, 0xfa }, { ZD_CR120, 0x4f },
+ { ZD_CR125, 0xaa }, { ZD_CR127, 0x03 }, { ZD_CR128, 0x14 },
+ { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 }, { ZD_CR131, 0x0C },
+ { ZD_CR136, 0xdf }, { ZD_CR137, 0x40 }, { ZD_CR138, 0xa0 },
+ { ZD_CR139, 0xb0 }, { ZD_CR140, 0x99 }, { ZD_CR141, 0x82 },
+ { ZD_CR142, 0x54 }, { ZD_CR143, 0x1c }, { ZD_CR144, 0x6c },
+ { ZD_CR147, 0x07 }, { ZD_CR148, 0x4c }, { ZD_CR149, 0x50 },
+ { ZD_CR150, 0x0e }, { ZD_CR151, 0x18 }, { ZD_CR160, 0xfe },
+ { ZD_CR161, 0xee }, { ZD_CR162, 0xaa }, { ZD_CR163, 0xfa },
+ { ZD_CR164, 0xfa }, { ZD_CR165, 0xea }, { ZD_CR166, 0xbe },
+ { ZD_CR167, 0xbe }, { ZD_CR168, 0x6a }, { ZD_CR169, 0xba },
+ { ZD_CR170, 0xba }, { ZD_CR171, 0xba },
+ /* Note: ZD_CR204 must lead the ZD_CR203 */
+ { ZD_CR204, 0x7d },
+ { },
+ { ZD_CR203, 0x30 },
+ };
+
+ int r, t;
+
+ dev_dbg_f(zd_chip_dev(chip), "\n");
+
+ r = zd_chip_lock_phy_regs(chip);
+ if (r)
+ goto out;
+
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ if (r)
+ goto unlock;
+
+ r = patch_cr157(chip);
+unlock:
+ t = zd_chip_unlock_phy_regs(chip);
+ if (t && !r)
+ r = t;
+out:
+ return r;
+}
+
+static int zd1211b_hw_reset_phy(struct zd_chip *chip)
+{
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR0, 0x14 }, { ZD_CR1, 0x06 }, { ZD_CR2, 0x26 },
+ { ZD_CR3, 0x38 }, { ZD_CR4, 0x80 }, { ZD_CR9, 0xe0 },
+ { ZD_CR10, 0x81 },
+ /* power control { { ZD_CR11, 1 << 6 }, */
+ { ZD_CR11, 0x00 },
+ { ZD_CR12, 0xf0 }, { ZD_CR13, 0x8c }, { ZD_CR14, 0x80 },
+ { ZD_CR15, 0x3d }, { ZD_CR16, 0x20 }, { ZD_CR17, 0x1e },
+ { ZD_CR18, 0x0a }, { ZD_CR19, 0x48 },
+ { ZD_CR20, 0x10 }, /* Org:0x0E, ComTrend:RalLink AP */
+ { ZD_CR21, 0x0e }, { ZD_CR22, 0x23 }, { ZD_CR23, 0x90 },
+ { ZD_CR24, 0x14 }, { ZD_CR25, 0x40 }, { ZD_CR26, 0x10 },
+ { ZD_CR27, 0x10 }, { ZD_CR28, 0x7f }, { ZD_CR29, 0x80 },
+ { ZD_CR30, 0x4b }, /* ASIC/FWT, no jointly decoder */
+ { ZD_CR31, 0x60 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x08 },
+ { ZD_CR34, 0x06 }, { ZD_CR35, 0x0a }, { ZD_CR36, 0x00 },
+ { ZD_CR37, 0x00 }, { ZD_CR38, 0x38 }, { ZD_CR39, 0x0c },
+ { ZD_CR40, 0x84 }, { ZD_CR41, 0x2a }, { ZD_CR42, 0x80 },
+ { ZD_CR43, 0x10 }, { ZD_CR44, 0x33 }, { ZD_CR46, 0xff },
+ { ZD_CR47, 0x1E }, { ZD_CR48, 0x26 }, { ZD_CR49, 0x5b },
+ { ZD_CR64, 0xd0 }, { ZD_CR65, 0x04 }, { ZD_CR66, 0x58 },
+ { ZD_CR67, 0xc9 }, { ZD_CR68, 0x88 }, { ZD_CR69, 0x41 },
+ { ZD_CR70, 0x23 }, { ZD_CR71, 0x10 }, { ZD_CR72, 0xff },
+ { ZD_CR73, 0x32 }, { ZD_CR74, 0x30 }, { ZD_CR75, 0x65 },
+ { ZD_CR76, 0x41 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x30 },
+ { ZD_CR79, 0xf0 }, { ZD_CR80, 0x64 }, { ZD_CR81, 0x64 },
+ { ZD_CR82, 0x00 }, { ZD_CR83, 0x24 }, { ZD_CR84, 0x04 },
+ { ZD_CR85, 0x00 }, { ZD_CR86, 0x0c }, { ZD_CR87, 0x12 },
+ { ZD_CR88, 0x0c }, { ZD_CR89, 0x00 }, { ZD_CR90, 0x58 },
+ { ZD_CR91, 0x04 }, { ZD_CR92, 0x00 }, { ZD_CR93, 0x00 },
+ { ZD_CR94, 0x01 },
+ { ZD_CR95, 0x20 }, /* ZD1211B */
+ { ZD_CR96, 0x50 }, { ZD_CR97, 0x37 }, { ZD_CR98, 0x35 },
+ { ZD_CR99, 0x00 }, { ZD_CR100, 0x01 }, { ZD_CR101, 0x13 },
+ { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 }, { ZD_CR104, 0x18 },
+ { ZD_CR105, 0x12 }, { ZD_CR106, 0x04 }, { ZD_CR107, 0x00 },
+ { ZD_CR108, 0x0a }, { ZD_CR109, 0x27 }, { ZD_CR110, 0x27 },
+ { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 }, { ZD_CR113, 0x27 },
+ { ZD_CR114, 0x27 }, { ZD_CR115, 0x26 }, { ZD_CR116, 0x24 },
+ { ZD_CR117, 0xfc }, { ZD_CR118, 0xfa }, { ZD_CR119, 0x1e },
+ { ZD_CR125, 0x90 }, { ZD_CR126, 0x00 }, { ZD_CR127, 0x00 },
+ { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 },
+ { ZD_CR131, 0x0c }, { ZD_CR136, 0xdf }, { ZD_CR137, 0xa0 },
+ { ZD_CR138, 0xa8 }, { ZD_CR139, 0xb4 }, { ZD_CR140, 0x98 },
+ { ZD_CR141, 0x82 }, { ZD_CR142, 0x53 }, { ZD_CR143, 0x1c },
+ { ZD_CR144, 0x6c }, { ZD_CR147, 0x07 }, { ZD_CR148, 0x40 },
+ { ZD_CR149, 0x40 }, /* Org:0x50 ComTrend:RalLink AP */
+ { ZD_CR150, 0x14 }, /* Org:0x0E ComTrend:RalLink AP */
+ { ZD_CR151, 0x18 }, { ZD_CR159, 0x70 }, { ZD_CR160, 0xfe },
+ { ZD_CR161, 0xee }, { ZD_CR162, 0xaa }, { ZD_CR163, 0xfa },
+ { ZD_CR164, 0xfa }, { ZD_CR165, 0xea }, { ZD_CR166, 0xbe },
+ { ZD_CR167, 0xbe }, { ZD_CR168, 0x6a }, { ZD_CR169, 0xba },
+ { ZD_CR170, 0xba }, { ZD_CR171, 0xba },
+ /* Note: ZD_CR204 must lead the ZD_CR203 */
+ { ZD_CR204, 0x7d },
+ {},
+ { ZD_CR203, 0x30 },
+ };
+
+ int r, t;
+
+ dev_dbg_f(zd_chip_dev(chip), "\n");
+
+ r = zd_chip_lock_phy_regs(chip);
+ if (r)
+ goto out;
+
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ t = zd_chip_unlock_phy_regs(chip);
+ if (t && !r)
+ r = t;
+out:
+ return r;
+}
+
+static int hw_reset_phy(struct zd_chip *chip)
+{
+ return zd_chip_is_zd1211b(chip) ? zd1211b_hw_reset_phy(chip) :
+ zd1211_hw_reset_phy(chip);
+}
+
+static int zd1211_hw_init_hmac(struct zd_chip *chip)
+{
+ static const struct zd_ioreq32 ioreqs[] = {
+ { CR_ZD1211_RETRY_MAX, ZD1211_RETRY_COUNT },
+ { CR_RX_THRESHOLD, 0x000c0640 },
+ };
+
+ dev_dbg_f(zd_chip_dev(chip), "\n");
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211b_hw_init_hmac(struct zd_chip *chip)
+{
+ static const struct zd_ioreq32 ioreqs[] = {
+ { CR_ZD1211B_RETRY_MAX, ZD1211B_RETRY_COUNT },
+ { CR_ZD1211B_CWIN_MAX_MIN_AC0, 0x007f003f },
+ { CR_ZD1211B_CWIN_MAX_MIN_AC1, 0x007f003f },
+ { CR_ZD1211B_CWIN_MAX_MIN_AC2, 0x003f001f },
+ { CR_ZD1211B_CWIN_MAX_MIN_AC3, 0x001f000f },
+ { CR_ZD1211B_AIFS_CTL1, 0x00280028 },
+ { CR_ZD1211B_AIFS_CTL2, 0x008C003C },
+ { CR_ZD1211B_TXOP, 0x01800824 },
+ { CR_RX_THRESHOLD, 0x000c0eff, },
+ };
+
+ dev_dbg_f(zd_chip_dev(chip), "\n");
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int hw_init_hmac(struct zd_chip *chip)
+{
+ int r;
+ static const struct zd_ioreq32 ioreqs[] = {
+ { CR_ACK_TIMEOUT_EXT, 0x20 },
+ { CR_ADDA_MBIAS_WARMTIME, 0x30000808 },
+ { CR_SNIFFER_ON, 0 },
+ { CR_RX_FILTER, STA_RX_FILTER },
+ { CR_GROUP_HASH_P1, 0x00 },
+ { CR_GROUP_HASH_P2, 0x80000000 },
+ { CR_REG1, 0xa4 },
+ { CR_ADDA_PWR_DWN, 0x7f },
+ { CR_BCN_PLCP_CFG, 0x00f00401 },
+ { CR_PHY_DELAY, 0x00 },
+ { CR_ACK_TIMEOUT_EXT, 0x80 },
+ { CR_ADDA_PWR_DWN, 0x00 },
+ { CR_ACK_TIME_80211, 0x100 },
+ { CR_RX_PE_DELAY, 0x70 },
+ { CR_PS_CTRL, 0x10000000 },
+ { CR_RTS_CTS_RATE, 0x02030203 },
+ { CR_AFTER_PNP, 0x1 },
+ { CR_WEP_PROTECT, 0x114 },
+ { CR_IFS_VALUE, IFS_VALUE_DEFAULT },
+ { CR_CAM_MODE, MODE_AP_WDS},
+ };
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ if (r)
+ return r;
+
+ return zd_chip_is_zd1211b(chip) ?
+ zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip);
+}
+
+struct aw_pt_bi {
+ u32 atim_wnd_period;
+ u32 pre_tbtt;
+ u32 beacon_interval;
+};
+
+static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
+{
+ int r;
+ static const zd_addr_t aw_pt_bi_addr[] =
+ { CR_ATIM_WND_PERIOD, CR_PRE_TBTT, CR_BCN_INTERVAL };
+ u32 values[3];
+
+ r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr,
+ ARRAY_SIZE(aw_pt_bi_addr));
+ if (r) {
+ memset(s, 0, sizeof(*s));
+ return r;
+ }
+
+ s->atim_wnd_period = values[0];
+ s->pre_tbtt = values[1];
+ s->beacon_interval = values[2];
+ return 0;
+}
+
+static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s)
+{
+ struct zd_ioreq32 reqs[3];
+ u16 b_interval = s->beacon_interval & 0xffff;
+
+ if (b_interval <= 5)
+ b_interval = 5;
+ if (s->pre_tbtt < 4 || s->pre_tbtt >= b_interval)
+ s->pre_tbtt = b_interval - 1;
+ if (s->atim_wnd_period >= s->pre_tbtt)
+ s->atim_wnd_period = s->pre_tbtt - 1;
+
+ reqs[0].addr = CR_ATIM_WND_PERIOD;
+ reqs[0].value = s->atim_wnd_period;
+ reqs[1].addr = CR_PRE_TBTT;
+ reqs[1].value = s->pre_tbtt;
+ reqs[2].addr = CR_BCN_INTERVAL;
+ reqs[2].value = (s->beacon_interval & ~0xffff) | b_interval;
+
+ return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs));
+}
+
+
+static int set_beacon_interval(struct zd_chip *chip, u16 interval,
+ u8 dtim_period, int type)
+{
+ int r;
+ struct aw_pt_bi s;
+ u32 b_interval, mode_flag;
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+
+ if (interval > 0) {
+ switch (type) {
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ mode_flag = BCN_MODE_IBSS;
+ break;
+ case NL80211_IFTYPE_AP:
+ mode_flag = BCN_MODE_AP;
+ break;
+ default:
+ mode_flag = 0;
+ break;
+ }
+ } else {
+ dtim_period = 0;
+ mode_flag = 0;
+ }
+
+ b_interval = mode_flag | (dtim_period << 16) | interval;
+
+ r = zd_iowrite32_locked(chip, b_interval, CR_BCN_INTERVAL);
+ if (r)
+ return r;
+ r = get_aw_pt_bi(chip, &s);
+ if (r)
+ return r;
+ return set_aw_pt_bi(chip, &s);
+}
+
+int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period,
+ int type)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = set_beacon_interval(chip, interval, dtim_period, type);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+static int hw_init(struct zd_chip *chip)
+{
+ int r;
+
+ dev_dbg_f(zd_chip_dev(chip), "\n");
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ r = hw_reset_phy(chip);
+ if (r)
+ return r;
+
+ r = hw_init_hmac(chip);
+ if (r)
+ return r;
+
+ return set_beacon_interval(chip, 100, 0, NL80211_IFTYPE_UNSPECIFIED);
+}
+
+static zd_addr_t fw_reg_addr(struct zd_chip *chip, u16 offset)
+{
+ return (zd_addr_t)((u16)chip->fw_regs_base + offset);
+}
+
+#ifdef DEBUG
+static int dump_cr(struct zd_chip *chip, const zd_addr_t addr,
+ const char *addr_string)
+{
+ int r;
+ u32 value;
+
+ r = zd_ioread32_locked(chip, &value, addr);
+ if (r) {
+ dev_dbg_f(zd_chip_dev(chip),
+ "error reading %s. Error number %d\n", addr_string, r);
+ return r;
+ }
+
+ dev_dbg_f(zd_chip_dev(chip), "%s %#010x\n",
+ addr_string, (unsigned int)value);
+ return 0;
+}
+
+static int test_init(struct zd_chip *chip)
+{
+ int r;
+
+ r = dump_cr(chip, CR_AFTER_PNP, "CR_AFTER_PNP");
+ if (r)
+ return r;
+ r = dump_cr(chip, CR_GPI_EN, "CR_GPI_EN");
+ if (r)
+ return r;
+ return dump_cr(chip, CR_INTERRUPT, "CR_INTERRUPT");
+}
+
+static void dump_fw_registers(struct zd_chip *chip)
+{
+ const zd_addr_t addr[4] = {
+ fw_reg_addr(chip, FW_REG_FIRMWARE_VER),
+ fw_reg_addr(chip, FW_REG_USB_SPEED),
+ fw_reg_addr(chip, FW_REG_FIX_TX_RATE),
+ fw_reg_addr(chip, FW_REG_LED_LINK_STATUS),
+ };
+
+ int r;
+ u16 values[4];
+
+ r = zd_ioread16v_locked(chip, values, (const zd_addr_t*)addr,
+ ARRAY_SIZE(addr));
+ if (r) {
+ dev_dbg_f(zd_chip_dev(chip), "error %d zd_ioread16v_locked\n",
+ r);
+ return;
+ }
+
+ dev_dbg_f(zd_chip_dev(chip), "FW_FIRMWARE_VER %#06hx\n", values[0]);
+ dev_dbg_f(zd_chip_dev(chip), "FW_USB_SPEED %#06hx\n", values[1]);
+ dev_dbg_f(zd_chip_dev(chip), "FW_FIX_TX_RATE %#06hx\n", values[2]);
+ dev_dbg_f(zd_chip_dev(chip), "FW_LINK_STATUS %#06hx\n", values[3]);
+}
+#endif /* DEBUG */
+
+static int print_fw_version(struct zd_chip *chip)
+{
+ struct wiphy *wiphy = zd_chip_to_mac(chip)->hw->wiphy;
+ int r;
+ u16 version;
+
+ r = zd_ioread16_locked(chip, &version,
+ fw_reg_addr(chip, FW_REG_FIRMWARE_VER));
+ if (r)
+ return r;
+
+ dev_info(zd_chip_dev(chip),"firmware version %04hx\n", version);
+
+ snprintf(wiphy->fw_version, sizeof(wiphy->fw_version),
+ "%04hx", version);
+
+ return 0;
+}
+
+static int set_mandatory_rates(struct zd_chip *chip, int gmode)
+{
+ u32 rates;
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ /* This sets the mandatory rates, which only depend from the standard
+ * that the device is supporting. Until further notice we should try
+ * to support 802.11g also for full speed USB.
+ */
+ if (!gmode)
+ rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M;
+ else
+ rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M|
+ CR_RATE_6M|CR_RATE_12M|CR_RATE_24M;
+
+ return zd_iowrite32_locked(chip, rates, CR_MANDATORY_RATE_TBL);
+}
+
+int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip,
+ int preamble)
+{
+ u32 value = 0;
+
+ dev_dbg_f(zd_chip_dev(chip), "preamble=%x\n", preamble);
+ value |= preamble << RTSCTS_SH_RTS_PMB_TYPE;
+ value |= preamble << RTSCTS_SH_CTS_PMB_TYPE;
+
+ /* We always send 11M RTS/self-CTS messages, like the vendor driver. */
+ value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_RTS_RATE;
+ value |= ZD_RX_CCK << RTSCTS_SH_RTS_MOD_TYPE;
+ value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_CTS_RATE;
+ value |= ZD_RX_CCK << RTSCTS_SH_CTS_MOD_TYPE;
+
+ return zd_iowrite32_locked(chip, value, CR_RTS_CTS_RATE);
+}
+
+int zd_chip_enable_hwint(struct zd_chip *chip)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = zd_iowrite32_locked(chip, HWINT_ENABLED, CR_INTERRUPT);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+static int disable_hwint(struct zd_chip *chip)
+{
+ return zd_iowrite32_locked(chip, HWINT_DISABLED, CR_INTERRUPT);
+}
+
+int zd_chip_disable_hwint(struct zd_chip *chip)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = disable_hwint(chip);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+static int read_fw_regs_offset(struct zd_chip *chip)
+{
+ int r;
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ r = zd_ioread16_locked(chip, (u16*)&chip->fw_regs_base,
+ FWRAW_REGS_ADDR);
+ if (r)
+ return r;
+ dev_dbg_f(zd_chip_dev(chip), "fw_regs_base: %#06hx\n",
+ (u16)chip->fw_regs_base);
+
+ return 0;
+}
+
+/* Read mac address using pre-firmware interface */
+int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr)
+{
+ dev_dbg_f(zd_chip_dev(chip), "\n");
+ return zd_usb_read_fw(&chip->usb, E2P_MAC_ADDR_P1, addr,
+ ETH_ALEN);
+}
+
+int zd_chip_init_hw(struct zd_chip *chip)
+{
+ int r;
+ u8 rf_type;
+
+ dev_dbg_f(zd_chip_dev(chip), "\n");
+
+ mutex_lock(&chip->mutex);
+
+#ifdef DEBUG
+ r = test_init(chip);
+ if (r)
+ goto out;
+#endif
+ r = zd_iowrite32_locked(chip, 1, CR_AFTER_PNP);
+ if (r)
+ goto out;
+
+ r = read_fw_regs_offset(chip);
+ if (r)
+ goto out;
+
+ /* GPI is always disabled, also in the other driver.
+ */
+ r = zd_iowrite32_locked(chip, 0, CR_GPI_EN);
+ if (r)
+ goto out;
+ r = zd_iowrite32_locked(chip, CWIN_SIZE, CR_CWMIN_CWMAX);
+ if (r)
+ goto out;
+ /* Currently we support IEEE 802.11g for full and high speed USB.
+ * It might be discussed, whether we should support pure b mode for
+ * full speed USB.
+ */
+ r = set_mandatory_rates(chip, 1);
+ if (r)
+ goto out;
+ /* Disabling interrupts is certainly a smart thing here.
+ */
+ r = disable_hwint(chip);
+ if (r)
+ goto out;
+ r = read_pod(chip, &rf_type);
+ if (r)
+ goto out;
+ r = hw_init(chip);
+ if (r)
+ goto out;
+ r = zd_rf_init_hw(&chip->rf, rf_type);
+ if (r)
+ goto out;
+
+ r = print_fw_version(chip);
+ if (r)
+ goto out;
+
+#ifdef DEBUG
+ dump_fw_registers(chip);
+ r = test_init(chip);
+ if (r)
+ goto out;
+#endif /* DEBUG */
+
+ r = read_cal_int_tables(chip);
+ if (r)
+ goto out;
+
+ print_id(chip);
+out:
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+static int update_pwr_int(struct zd_chip *chip, u8 channel)
+{
+ u8 value = chip->pwr_int_values[channel - 1];
+ return zd_iowrite16_locked(chip, value, ZD_CR31);
+}
+
+static int update_pwr_cal(struct zd_chip *chip, u8 channel)
+{
+ u8 value = chip->pwr_cal_values[channel-1];
+ return zd_iowrite16_locked(chip, value, ZD_CR68);
+}
+
+static int update_ofdm_cal(struct zd_chip *chip, u8 channel)
+{
+ struct zd_ioreq16 ioreqs[3];
+
+ ioreqs[0].addr = ZD_CR67;
+ ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1];
+ ioreqs[1].addr = ZD_CR66;
+ ioreqs[1].value = chip->ofdm_cal_values[OFDM_48M_INDEX][channel-1];
+ ioreqs[2].addr = ZD_CR65;
+ ioreqs[2].value = chip->ofdm_cal_values[OFDM_54M_INDEX][channel-1];
+
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int update_channel_integration_and_calibration(struct zd_chip *chip,
+ u8 channel)
+{
+ int r;
+
+ if (!zd_rf_should_update_pwr_int(&chip->rf))
+ return 0;
+
+ r = update_pwr_int(chip, channel);
+ if (r)
+ return r;
+ if (zd_chip_is_zd1211b(chip)) {
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR69, 0x28 },
+ {},
+ { ZD_CR69, 0x2a },
+ };
+
+ r = update_ofdm_cal(chip, channel);
+ if (r)
+ return r;
+ r = update_pwr_cal(chip, channel);
+ if (r)
+ return r;
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+/* The CCK baseband gain can be optionally patched by the EEPROM */
+static int patch_cck_gain(struct zd_chip *chip)
+{
+ int r;
+ u32 value;
+
+ if (!chip->patch_cck_gain || !zd_rf_should_patch_cck_gain(&chip->rf))
+ return 0;
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ r = zd_ioread32_locked(chip, &value, E2P_PHY_REG);
+ if (r)
+ return r;
+ dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff);
+ return zd_iowrite16_locked(chip, value & 0xff, ZD_CR47);
+}
+
+int zd_chip_set_channel(struct zd_chip *chip, u8 channel)
+{
+ int r, t;
+
+ mutex_lock(&chip->mutex);
+ r = zd_chip_lock_phy_regs(chip);
+ if (r)
+ goto out;
+ r = zd_rf_set_channel(&chip->rf, channel);
+ if (r)
+ goto unlock;
+ r = update_channel_integration_and_calibration(chip, channel);
+ if (r)
+ goto unlock;
+ r = patch_cck_gain(chip);
+ if (r)
+ goto unlock;
+ r = patch_6m_band_edge(chip, channel);
+ if (r)
+ goto unlock;
+ r = zd_iowrite32_locked(chip, 0, CR_CONFIG_PHILIPS);
+unlock:
+ t = zd_chip_unlock_phy_regs(chip);
+ if (t && !r)
+ r = t;
+out:
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+u8 zd_chip_get_channel(struct zd_chip *chip)
+{
+ u8 channel;
+
+ mutex_lock(&chip->mutex);
+ channel = chip->rf.channel;
+ mutex_unlock(&chip->mutex);
+ return channel;
+}
+
+int zd_chip_control_leds(struct zd_chip *chip, enum led_status status)
+{
+ const zd_addr_t a[] = {
+ fw_reg_addr(chip, FW_REG_LED_LINK_STATUS),
+ CR_LED,
+ };
+
+ int r;
+ u16 v[ARRAY_SIZE(a)];
+ struct zd_ioreq16 ioreqs[ARRAY_SIZE(a)] = {
+ [0] = { fw_reg_addr(chip, FW_REG_LED_LINK_STATUS) },
+ [1] = { CR_LED },
+ };
+ u16 other_led;
+
+ mutex_lock(&chip->mutex);
+ r = zd_ioread16v_locked(chip, v, (const zd_addr_t *)a, ARRAY_SIZE(a));
+ if (r)
+ goto out;
+
+ other_led = chip->link_led == LED1 ? LED2 : LED1;
+
+ switch (status) {
+ case ZD_LED_OFF:
+ ioreqs[0].value = FW_LINK_OFF;
+ ioreqs[1].value = v[1] & ~(LED1|LED2);
+ break;
+ case ZD_LED_SCANNING:
+ ioreqs[0].value = FW_LINK_OFF;
+ ioreqs[1].value = v[1] & ~other_led;
+ if ((u32)ktime_get_seconds() % 3 == 0) {
+ ioreqs[1].value &= ~chip->link_led;
+ } else {
+ ioreqs[1].value |= chip->link_led;
+ }
+ break;
+ case ZD_LED_ASSOCIATED:
+ ioreqs[0].value = FW_LINK_TX;
+ ioreqs[1].value = v[1] & ~other_led;
+ ioreqs[1].value |= chip->link_led;
+ break;
+ default:
+ r = -EINVAL;
+ goto out;
+ }
+
+ if (v[0] != ioreqs[0].value || v[1] != ioreqs[1].value) {
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ if (r)
+ goto out;
+ }
+ r = 0;
+out:
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates)
+{
+ int r;
+
+ if (cr_rates & ~(CR_RATES_80211B|CR_RATES_80211G))
+ return -EINVAL;
+
+ mutex_lock(&chip->mutex);
+ r = zd_iowrite32_locked(chip, cr_rates, CR_BASIC_RATE_TBL);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+static inline u8 zd_rate_from_ofdm_plcp_header(const void *rx_frame)
+{
+ return ZD_OFDM | zd_ofdm_plcp_header_rate(rx_frame);
+}
+
+/**
+ * zd_rx_rate - report zd-rate
+ * @rx_frame: received frame
+ * @status: rx_status as given by the device
+ *
+ * This function converts the rate as encoded in the received packet to the
+ * zd-rate, we are using on other places in the driver.
+ */
+u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status)
+{
+ u8 zd_rate;
+ if (status->frame_status & ZD_RX_OFDM) {
+ zd_rate = zd_rate_from_ofdm_plcp_header(rx_frame);
+ } else {
+ switch (zd_cck_plcp_header_signal(rx_frame)) {
+ case ZD_CCK_PLCP_SIGNAL_1M:
+ zd_rate = ZD_CCK_RATE_1M;
+ break;
+ case ZD_CCK_PLCP_SIGNAL_2M:
+ zd_rate = ZD_CCK_RATE_2M;
+ break;
+ case ZD_CCK_PLCP_SIGNAL_5M5:
+ zd_rate = ZD_CCK_RATE_5_5M;
+ break;
+ case ZD_CCK_PLCP_SIGNAL_11M:
+ zd_rate = ZD_CCK_RATE_11M;
+ break;
+ default:
+ zd_rate = 0;
+ }
+ }
+
+ return zd_rate;
+}
+
+int zd_chip_switch_radio_on(struct zd_chip *chip)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = zd_switch_radio_on(&chip->rf);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+int zd_chip_switch_radio_off(struct zd_chip *chip)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = zd_switch_radio_off(&chip->rf);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+int zd_chip_enable_int(struct zd_chip *chip)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ r = zd_usb_enable_int(&chip->usb);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+void zd_chip_disable_int(struct zd_chip *chip)
+{
+ mutex_lock(&chip->mutex);
+ zd_usb_disable_int(&chip->usb);
+ mutex_unlock(&chip->mutex);
+
+ /* cancel pending interrupt work */
+ cancel_work_sync(&zd_chip_to_mac(chip)->process_intr);
+}
+
+int zd_chip_enable_rxtx(struct zd_chip *chip)
+{
+ int r;
+
+ mutex_lock(&chip->mutex);
+ zd_usb_enable_tx(&chip->usb);
+ r = zd_usb_enable_rx(&chip->usb);
+ zd_tx_watchdog_enable(&chip->usb);
+ mutex_unlock(&chip->mutex);
+ return r;
+}
+
+void zd_chip_disable_rxtx(struct zd_chip *chip)
+{
+ mutex_lock(&chip->mutex);
+ zd_tx_watchdog_disable(&chip->usb);
+ zd_usb_disable_rx(&chip->usb);
+ zd_usb_disable_tx(&chip->usb);
+ mutex_unlock(&chip->mutex);
+}
+
+int zd_rfwritev_locked(struct zd_chip *chip,
+ const u32* values, unsigned int count, u8 bits)
+{
+ int r;
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ r = zd_rfwrite_locked(chip, values[i], bits);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+/*
+ * We can optionally program the RF directly through CR regs, if supported by
+ * the hardware. This is much faster than the older method.
+ */
+int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value)
+{
+ const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR244, (value >> 16) & 0xff },
+ { ZD_CR243, (value >> 8) & 0xff },
+ { ZD_CR242, value & 0xff },
+ };
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+int zd_rfwritev_cr_locked(struct zd_chip *chip,
+ const u32 *values, unsigned int count)
+{
+ int r;
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ r = zd_rfwrite_cr_locked(chip, values[i]);
+ if (r)
+ return r;
+ }
+
+ return 0;
+}
+
+int zd_chip_set_multicast_hash(struct zd_chip *chip,
+ struct zd_mc_hash *hash)
+{
+ const struct zd_ioreq32 ioreqs[] = {
+ { CR_GROUP_HASH_P1, hash->low },
+ { CR_GROUP_HASH_P2, hash->high },
+ };
+
+ return zd_iowrite32a(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+u64 zd_chip_get_tsf(struct zd_chip *chip)
+{
+ int r;
+ static const zd_addr_t aw_pt_bi_addr[] =
+ { CR_TSF_LOW_PART, CR_TSF_HIGH_PART };
+ u32 values[2];
+ u64 tsf;
+
+ mutex_lock(&chip->mutex);
+ r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr,
+ ARRAY_SIZE(aw_pt_bi_addr));
+ mutex_unlock(&chip->mutex);
+ if (r)
+ return 0;
+
+ tsf = values[1];
+ tsf = (tsf << 32) | values[0];
+
+ return tsf;
+}
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_chip.h b/drivers/net/wireless/zydas/zd1211rw/zd_chip.h
new file mode 100644
index 000000000..70a1548eb
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_chip.h
@@ -0,0 +1,971 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+#ifndef _ZD_CHIP_H
+#define _ZD_CHIP_H
+
+#include <net/mac80211.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+
+/* Header for the Media Access Controller (MAC) and the Baseband Processor
+ * (BBP). It appears that the ZD1211 wraps the old ZD1205 with USB glue and
+ * adds a processor for handling the USB protocol.
+ */
+
+/* Address space */
+enum {
+ /* CONTROL REGISTERS */
+ CR_START = 0x9000,
+
+
+ /* FIRMWARE */
+ FW_START = 0xee00,
+
+
+ /* EEPROM */
+ E2P_START = 0xf800,
+ E2P_LEN = 0x800,
+
+ /* EEPROM layout */
+ E2P_LOAD_CODE_LEN = 0xe, /* base 0xf800 */
+ E2P_LOAD_VECT_LEN = 0x9, /* base 0xf80e */
+ /* E2P_DATA indexes into this */
+ E2P_DATA_LEN = 0x7e, /* base 0xf817 */
+ E2P_BOOT_CODE_LEN = 0x760, /* base 0xf895 */
+ E2P_INTR_VECT_LEN = 0xb, /* base 0xfff5 */
+
+ /* Some precomputed offsets into the EEPROM */
+ E2P_DATA_OFFSET = E2P_LOAD_CODE_LEN + E2P_LOAD_VECT_LEN,
+ E2P_BOOT_CODE_OFFSET = E2P_DATA_OFFSET + E2P_DATA_LEN,
+};
+
+#define CTL_REG(offset) ((zd_addr_t)(CR_START + (offset)))
+#define E2P_DATA(offset) ((zd_addr_t)(E2P_START + E2P_DATA_OFFSET + (offset)))
+#define FWRAW_DATA(offset) ((zd_addr_t)(FW_START + (offset)))
+
+/* 8-bit hardware registers */
+#define ZD_CR0 CTL_REG(0x0000)
+#define ZD_CR1 CTL_REG(0x0004)
+#define ZD_CR2 CTL_REG(0x0008)
+#define ZD_CR3 CTL_REG(0x000C)
+
+#define ZD_CR5 CTL_REG(0x0010)
+/* bit 5: if set short preamble used
+ * bit 6: filter band - Japan channel 14 on, else off
+ */
+#define ZD_CR6 CTL_REG(0x0014)
+#define ZD_CR7 CTL_REG(0x0018)
+#define ZD_CR8 CTL_REG(0x001C)
+
+#define ZD_CR4 CTL_REG(0x0020)
+
+#define ZD_CR9 CTL_REG(0x0024)
+/* bit 2: antenna switch (together with ZD_CR10) */
+#define ZD_CR10 CTL_REG(0x0028)
+/* bit 1: antenna switch (together with ZD_CR9)
+ * RF2959 controls with ZD_CR11 radion on and off
+ */
+#define ZD_CR11 CTL_REG(0x002C)
+/* bit 6: TX power control for OFDM
+ * RF2959 controls with ZD_CR10 radio on and off
+ */
+#define ZD_CR12 CTL_REG(0x0030)
+#define ZD_CR13 CTL_REG(0x0034)
+#define ZD_CR14 CTL_REG(0x0038)
+#define ZD_CR15 CTL_REG(0x003C)
+#define ZD_CR16 CTL_REG(0x0040)
+#define ZD_CR17 CTL_REG(0x0044)
+#define ZD_CR18 CTL_REG(0x0048)
+#define ZD_CR19 CTL_REG(0x004C)
+#define ZD_CR20 CTL_REG(0x0050)
+#define ZD_CR21 CTL_REG(0x0054)
+#define ZD_CR22 CTL_REG(0x0058)
+#define ZD_CR23 CTL_REG(0x005C)
+#define ZD_CR24 CTL_REG(0x0060) /* CCA threshold */
+#define ZD_CR25 CTL_REG(0x0064)
+#define ZD_CR26 CTL_REG(0x0068)
+#define ZD_CR27 CTL_REG(0x006C)
+#define ZD_CR28 CTL_REG(0x0070)
+#define ZD_CR29 CTL_REG(0x0074)
+#define ZD_CR30 CTL_REG(0x0078)
+#define ZD_CR31 CTL_REG(0x007C) /* TX power control for RF in
+ * CCK mode
+ */
+#define ZD_CR32 CTL_REG(0x0080)
+#define ZD_CR33 CTL_REG(0x0084)
+#define ZD_CR34 CTL_REG(0x0088)
+#define ZD_CR35 CTL_REG(0x008C)
+#define ZD_CR36 CTL_REG(0x0090)
+#define ZD_CR37 CTL_REG(0x0094)
+#define ZD_CR38 CTL_REG(0x0098)
+#define ZD_CR39 CTL_REG(0x009C)
+#define ZD_CR40 CTL_REG(0x00A0)
+#define ZD_CR41 CTL_REG(0x00A4)
+#define ZD_CR42 CTL_REG(0x00A8)
+#define ZD_CR43 CTL_REG(0x00AC)
+#define ZD_CR44 CTL_REG(0x00B0)
+#define ZD_CR45 CTL_REG(0x00B4)
+#define ZD_CR46 CTL_REG(0x00B8)
+#define ZD_CR47 CTL_REG(0x00BC) /* CCK baseband gain
+ * (patch value might be in EEPROM)
+ */
+#define ZD_CR48 CTL_REG(0x00C0)
+#define ZD_CR49 CTL_REG(0x00C4)
+#define ZD_CR50 CTL_REG(0x00C8)
+#define ZD_CR51 CTL_REG(0x00CC) /* TX power control for RF in
+ * 6-36M modes
+ */
+#define ZD_CR52 CTL_REG(0x00D0) /* TX power control for RF in
+ * 48M mode
+ */
+#define ZD_CR53 CTL_REG(0x00D4) /* TX power control for RF in
+ * 54M mode
+ */
+#define ZD_CR54 CTL_REG(0x00D8)
+#define ZD_CR55 CTL_REG(0x00DC)
+#define ZD_CR56 CTL_REG(0x00E0)
+#define ZD_CR57 CTL_REG(0x00E4)
+#define ZD_CR58 CTL_REG(0x00E8)
+#define ZD_CR59 CTL_REG(0x00EC)
+#define ZD_CR60 CTL_REG(0x00F0)
+#define ZD_CR61 CTL_REG(0x00F4)
+#define ZD_CR62 CTL_REG(0x00F8)
+#define ZD_CR63 CTL_REG(0x00FC)
+#define ZD_CR64 CTL_REG(0x0100)
+#define ZD_CR65 CTL_REG(0x0104) /* OFDM 54M calibration */
+#define ZD_CR66 CTL_REG(0x0108) /* OFDM 48M calibration */
+#define ZD_CR67 CTL_REG(0x010C) /* OFDM 36M calibration */
+#define ZD_CR68 CTL_REG(0x0110) /* CCK calibration */
+#define ZD_CR69 CTL_REG(0x0114)
+#define ZD_CR70 CTL_REG(0x0118)
+#define ZD_CR71 CTL_REG(0x011C)
+#define ZD_CR72 CTL_REG(0x0120)
+#define ZD_CR73 CTL_REG(0x0124)
+#define ZD_CR74 CTL_REG(0x0128)
+#define ZD_CR75 CTL_REG(0x012C)
+#define ZD_CR76 CTL_REG(0x0130)
+#define ZD_CR77 CTL_REG(0x0134)
+#define ZD_CR78 CTL_REG(0x0138)
+#define ZD_CR79 CTL_REG(0x013C)
+#define ZD_CR80 CTL_REG(0x0140)
+#define ZD_CR81 CTL_REG(0x0144)
+#define ZD_CR82 CTL_REG(0x0148)
+#define ZD_CR83 CTL_REG(0x014C)
+#define ZD_CR84 CTL_REG(0x0150)
+#define ZD_CR85 CTL_REG(0x0154)
+#define ZD_CR86 CTL_REG(0x0158)
+#define ZD_CR87 CTL_REG(0x015C)
+#define ZD_CR88 CTL_REG(0x0160)
+#define ZD_CR89 CTL_REG(0x0164)
+#define ZD_CR90 CTL_REG(0x0168)
+#define ZD_CR91 CTL_REG(0x016C)
+#define ZD_CR92 CTL_REG(0x0170)
+#define ZD_CR93 CTL_REG(0x0174)
+#define ZD_CR94 CTL_REG(0x0178)
+#define ZD_CR95 CTL_REG(0x017C)
+#define ZD_CR96 CTL_REG(0x0180)
+#define ZD_CR97 CTL_REG(0x0184)
+#define ZD_CR98 CTL_REG(0x0188)
+#define ZD_CR99 CTL_REG(0x018C)
+#define ZD_CR100 CTL_REG(0x0190)
+#define ZD_CR101 CTL_REG(0x0194)
+#define ZD_CR102 CTL_REG(0x0198)
+#define ZD_CR103 CTL_REG(0x019C)
+#define ZD_CR104 CTL_REG(0x01A0)
+#define ZD_CR105 CTL_REG(0x01A4)
+#define ZD_CR106 CTL_REG(0x01A8)
+#define ZD_CR107 CTL_REG(0x01AC)
+#define ZD_CR108 CTL_REG(0x01B0)
+#define ZD_CR109 CTL_REG(0x01B4)
+#define ZD_CR110 CTL_REG(0x01B8)
+#define ZD_CR111 CTL_REG(0x01BC)
+#define ZD_CR112 CTL_REG(0x01C0)
+#define ZD_CR113 CTL_REG(0x01C4)
+#define ZD_CR114 CTL_REG(0x01C8)
+#define ZD_CR115 CTL_REG(0x01CC)
+#define ZD_CR116 CTL_REG(0x01D0)
+#define ZD_CR117 CTL_REG(0x01D4)
+#define ZD_CR118 CTL_REG(0x01D8)
+#define ZD_CR119 CTL_REG(0x01DC)
+#define ZD_CR120 CTL_REG(0x01E0)
+#define ZD_CR121 CTL_REG(0x01E4)
+#define ZD_CR122 CTL_REG(0x01E8)
+#define ZD_CR123 CTL_REG(0x01EC)
+#define ZD_CR124 CTL_REG(0x01F0)
+#define ZD_CR125 CTL_REG(0x01F4)
+#define ZD_CR126 CTL_REG(0x01F8)
+#define ZD_CR127 CTL_REG(0x01FC)
+#define ZD_CR128 CTL_REG(0x0200)
+#define ZD_CR129 CTL_REG(0x0204)
+#define ZD_CR130 CTL_REG(0x0208)
+#define ZD_CR131 CTL_REG(0x020C)
+#define ZD_CR132 CTL_REG(0x0210)
+#define ZD_CR133 CTL_REG(0x0214)
+#define ZD_CR134 CTL_REG(0x0218)
+#define ZD_CR135 CTL_REG(0x021C)
+#define ZD_CR136 CTL_REG(0x0220)
+#define ZD_CR137 CTL_REG(0x0224)
+#define ZD_CR138 CTL_REG(0x0228)
+#define ZD_CR139 CTL_REG(0x022C)
+#define ZD_CR140 CTL_REG(0x0230)
+#define ZD_CR141 CTL_REG(0x0234)
+#define ZD_CR142 CTL_REG(0x0238)
+#define ZD_CR143 CTL_REG(0x023C)
+#define ZD_CR144 CTL_REG(0x0240)
+#define ZD_CR145 CTL_REG(0x0244)
+#define ZD_CR146 CTL_REG(0x0248)
+#define ZD_CR147 CTL_REG(0x024C)
+#define ZD_CR148 CTL_REG(0x0250)
+#define ZD_CR149 CTL_REG(0x0254)
+#define ZD_CR150 CTL_REG(0x0258)
+#define ZD_CR151 CTL_REG(0x025C)
+#define ZD_CR152 CTL_REG(0x0260)
+#define ZD_CR153 CTL_REG(0x0264)
+#define ZD_CR154 CTL_REG(0x0268)
+#define ZD_CR155 CTL_REG(0x026C)
+#define ZD_CR156 CTL_REG(0x0270)
+#define ZD_CR157 CTL_REG(0x0274)
+#define ZD_CR158 CTL_REG(0x0278)
+#define ZD_CR159 CTL_REG(0x027C)
+#define ZD_CR160 CTL_REG(0x0280)
+#define ZD_CR161 CTL_REG(0x0284)
+#define ZD_CR162 CTL_REG(0x0288)
+#define ZD_CR163 CTL_REG(0x028C)
+#define ZD_CR164 CTL_REG(0x0290)
+#define ZD_CR165 CTL_REG(0x0294)
+#define ZD_CR166 CTL_REG(0x0298)
+#define ZD_CR167 CTL_REG(0x029C)
+#define ZD_CR168 CTL_REG(0x02A0)
+#define ZD_CR169 CTL_REG(0x02A4)
+#define ZD_CR170 CTL_REG(0x02A8)
+#define ZD_CR171 CTL_REG(0x02AC)
+#define ZD_CR172 CTL_REG(0x02B0)
+#define ZD_CR173 CTL_REG(0x02B4)
+#define ZD_CR174 CTL_REG(0x02B8)
+#define ZD_CR175 CTL_REG(0x02BC)
+#define ZD_CR176 CTL_REG(0x02C0)
+#define ZD_CR177 CTL_REG(0x02C4)
+#define ZD_CR178 CTL_REG(0x02C8)
+#define ZD_CR179 CTL_REG(0x02CC)
+#define ZD_CR180 CTL_REG(0x02D0)
+#define ZD_CR181 CTL_REG(0x02D4)
+#define ZD_CR182 CTL_REG(0x02D8)
+#define ZD_CR183 CTL_REG(0x02DC)
+#define ZD_CR184 CTL_REG(0x02E0)
+#define ZD_CR185 CTL_REG(0x02E4)
+#define ZD_CR186 CTL_REG(0x02E8)
+#define ZD_CR187 CTL_REG(0x02EC)
+#define ZD_CR188 CTL_REG(0x02F0)
+#define ZD_CR189 CTL_REG(0x02F4)
+#define ZD_CR190 CTL_REG(0x02F8)
+#define ZD_CR191 CTL_REG(0x02FC)
+#define ZD_CR192 CTL_REG(0x0300)
+#define ZD_CR193 CTL_REG(0x0304)
+#define ZD_CR194 CTL_REG(0x0308)
+#define ZD_CR195 CTL_REG(0x030C)
+#define ZD_CR196 CTL_REG(0x0310)
+#define ZD_CR197 CTL_REG(0x0314)
+#define ZD_CR198 CTL_REG(0x0318)
+#define ZD_CR199 CTL_REG(0x031C)
+#define ZD_CR200 CTL_REG(0x0320)
+#define ZD_CR201 CTL_REG(0x0324)
+#define ZD_CR202 CTL_REG(0x0328)
+#define ZD_CR203 CTL_REG(0x032C) /* I2C bus template value & flash
+ * control
+ */
+#define ZD_CR204 CTL_REG(0x0330)
+#define ZD_CR205 CTL_REG(0x0334)
+#define ZD_CR206 CTL_REG(0x0338)
+#define ZD_CR207 CTL_REG(0x033C)
+#define ZD_CR208 CTL_REG(0x0340)
+#define ZD_CR209 CTL_REG(0x0344)
+#define ZD_CR210 CTL_REG(0x0348)
+#define ZD_CR211 CTL_REG(0x034C)
+#define ZD_CR212 CTL_REG(0x0350)
+#define ZD_CR213 CTL_REG(0x0354)
+#define ZD_CR214 CTL_REG(0x0358)
+#define ZD_CR215 CTL_REG(0x035C)
+#define ZD_CR216 CTL_REG(0x0360)
+#define ZD_CR217 CTL_REG(0x0364)
+#define ZD_CR218 CTL_REG(0x0368)
+#define ZD_CR219 CTL_REG(0x036C)
+#define ZD_CR220 CTL_REG(0x0370)
+#define ZD_CR221 CTL_REG(0x0374)
+#define ZD_CR222 CTL_REG(0x0378)
+#define ZD_CR223 CTL_REG(0x037C)
+#define ZD_CR224 CTL_REG(0x0380)
+#define ZD_CR225 CTL_REG(0x0384)
+#define ZD_CR226 CTL_REG(0x0388)
+#define ZD_CR227 CTL_REG(0x038C)
+#define ZD_CR228 CTL_REG(0x0390)
+#define ZD_CR229 CTL_REG(0x0394)
+#define ZD_CR230 CTL_REG(0x0398)
+#define ZD_CR231 CTL_REG(0x039C)
+#define ZD_CR232 CTL_REG(0x03A0)
+#define ZD_CR233 CTL_REG(0x03A4)
+#define ZD_CR234 CTL_REG(0x03A8)
+#define ZD_CR235 CTL_REG(0x03AC)
+#define ZD_CR236 CTL_REG(0x03B0)
+
+#define ZD_CR240 CTL_REG(0x03C0)
+/* bit 7: host-controlled RF register writes
+ * ZD_CR241-ZD_CR245: for hardware controlled writing of RF bits, not needed for
+ * USB
+ */
+#define ZD_CR241 CTL_REG(0x03C4)
+#define ZD_CR242 CTL_REG(0x03C8)
+#define ZD_CR243 CTL_REG(0x03CC)
+#define ZD_CR244 CTL_REG(0x03D0)
+#define ZD_CR245 CTL_REG(0x03D4)
+
+#define ZD_CR251 CTL_REG(0x03EC) /* only used for activation and
+ * deactivation of Airoha RFs AL2230
+ * and AL7230B
+ */
+#define ZD_CR252 CTL_REG(0x03F0)
+#define ZD_CR253 CTL_REG(0x03F4)
+#define ZD_CR254 CTL_REG(0x03F8)
+#define ZD_CR255 CTL_REG(0x03FC)
+
+#define CR_MAX_PHY_REG 255
+
+/* Taken from the ZYDAS driver, not all of them are relevant for the ZD1211
+ * driver.
+ */
+
+#define CR_RF_IF_CLK CTL_REG(0x0400)
+#define CR_RF_IF_DATA CTL_REG(0x0404)
+#define CR_PE1_PE2 CTL_REG(0x0408)
+#define CR_PE2_DLY CTL_REG(0x040C)
+#define CR_LE1 CTL_REG(0x0410)
+#define CR_LE2 CTL_REG(0x0414)
+/* Seems to enable/disable GPI (General Purpose IO?) */
+#define CR_GPI_EN CTL_REG(0x0418)
+#define CR_RADIO_PD CTL_REG(0x042C)
+#define CR_RF2948_PD CTL_REG(0x042C)
+#define CR_ENABLE_PS_MANUAL_AGC CTL_REG(0x043C)
+#define CR_CONFIG_PHILIPS CTL_REG(0x0440)
+#define CR_SA2400_SER_AP CTL_REG(0x0444)
+#define CR_I2C_WRITE CTL_REG(0x0444)
+#define CR_SA2400_SER_RP CTL_REG(0x0448)
+#define CR_RADIO_PE CTL_REG(0x0458)
+#define CR_RST_BUS_MASTER CTL_REG(0x045C)
+#define CR_RFCFG CTL_REG(0x0464)
+#define CR_HSTSCHG CTL_REG(0x046C)
+#define CR_PHY_ON CTL_REG(0x0474)
+#define CR_RX_DELAY CTL_REG(0x0478)
+#define CR_RX_PE_DELAY CTL_REG(0x047C)
+#define CR_GPIO_1 CTL_REG(0x0490)
+#define CR_GPIO_2 CTL_REG(0x0494)
+#define CR_EncryBufMux CTL_REG(0x04A8)
+#define CR_PS_CTRL CTL_REG(0x0500)
+#define CR_ADDA_PWR_DWN CTL_REG(0x0504)
+#define CR_ADDA_MBIAS_WARMTIME CTL_REG(0x0508)
+#define CR_MAC_PS_STATE CTL_REG(0x050C)
+
+#define CR_INTERRUPT CTL_REG(0x0510)
+#define INT_TX_COMPLETE (1 << 0)
+#define INT_RX_COMPLETE (1 << 1)
+#define INT_RETRY_FAIL (1 << 2)
+#define INT_WAKEUP (1 << 3)
+#define INT_DTIM_NOTIFY (1 << 5)
+#define INT_CFG_NEXT_BCN (1 << 6)
+#define INT_BUS_ABORT (1 << 7)
+#define INT_TX_FIFO_READY (1 << 8)
+#define INT_UART (1 << 9)
+#define INT_TX_COMPLETE_EN (1 << 16)
+#define INT_RX_COMPLETE_EN (1 << 17)
+#define INT_RETRY_FAIL_EN (1 << 18)
+#define INT_WAKEUP_EN (1 << 19)
+#define INT_DTIM_NOTIFY_EN (1 << 21)
+#define INT_CFG_NEXT_BCN_EN (1 << 22)
+#define INT_BUS_ABORT_EN (1 << 23)
+#define INT_TX_FIFO_READY_EN (1 << 24)
+#define INT_UART_EN (1 << 25)
+
+#define CR_TSF_LOW_PART CTL_REG(0x0514)
+#define CR_TSF_HIGH_PART CTL_REG(0x0518)
+
+/* Following three values are in time units (1024us)
+ * Following condition must be met:
+ * atim < tbtt < bcn
+ */
+#define CR_ATIM_WND_PERIOD CTL_REG(0x051C)
+#define CR_BCN_INTERVAL CTL_REG(0x0520)
+#define CR_PRE_TBTT CTL_REG(0x0524)
+/* in units of TU(1024us) */
+
+/* for UART support */
+#define CR_UART_RBR_THR_DLL CTL_REG(0x0540)
+#define CR_UART_DLM_IER CTL_REG(0x0544)
+#define CR_UART_IIR_FCR CTL_REG(0x0548)
+#define CR_UART_LCR CTL_REG(0x054c)
+#define CR_UART_MCR CTL_REG(0x0550)
+#define CR_UART_LSR CTL_REG(0x0554)
+#define CR_UART_MSR CTL_REG(0x0558)
+#define CR_UART_ECR CTL_REG(0x055c)
+#define CR_UART_STATUS CTL_REG(0x0560)
+
+#define CR_PCI_TX_ADDR_P1 CTL_REG(0x0600)
+#define CR_PCI_TX_AddR_P2 CTL_REG(0x0604)
+#define CR_PCI_RX_AddR_P1 CTL_REG(0x0608)
+#define CR_PCI_RX_AddR_P2 CTL_REG(0x060C)
+
+/* must be overwritten if custom MAC address will be used */
+#define CR_MAC_ADDR_P1 CTL_REG(0x0610)
+#define CR_MAC_ADDR_P2 CTL_REG(0x0614)
+#define CR_BSSID_P1 CTL_REG(0x0618)
+#define CR_BSSID_P2 CTL_REG(0x061C)
+#define CR_BCN_PLCP_CFG CTL_REG(0x0620)
+
+/* Group hash table for filtering incoming packets.
+ *
+ * The group hash table is 64 bit large and split over two parts. The first
+ * part is the lower part. The upper 6 bits of the last byte of the target
+ * address are used as index. Packets are received if the hash table bit is
+ * set. This is used for multicast handling, but for broadcasts (address
+ * ff:ff:ff:ff:ff:ff) the highest bit in the second table must also be set.
+ */
+#define CR_GROUP_HASH_P1 CTL_REG(0x0624)
+#define CR_GROUP_HASH_P2 CTL_REG(0x0628)
+
+#define CR_RX_TIMEOUT CTL_REG(0x062C)
+
+/* Basic rates supported by the BSS. When producing ACK or CTS messages, the
+ * device will use a rate in this table that is less than or equal to the rate
+ * of the incoming frame which prompted the response. */
+#define CR_BASIC_RATE_TBL CTL_REG(0x0630)
+#define CR_RATE_1M (1 << 0) /* 802.11b */
+#define CR_RATE_2M (1 << 1) /* 802.11b */
+#define CR_RATE_5_5M (1 << 2) /* 802.11b */
+#define CR_RATE_11M (1 << 3) /* 802.11b */
+#define CR_RATE_6M (1 << 8) /* 802.11g */
+#define CR_RATE_9M (1 << 9) /* 802.11g */
+#define CR_RATE_12M (1 << 10) /* 802.11g */
+#define CR_RATE_18M (1 << 11) /* 802.11g */
+#define CR_RATE_24M (1 << 12) /* 802.11g */
+#define CR_RATE_36M (1 << 13) /* 802.11g */
+#define CR_RATE_48M (1 << 14) /* 802.11g */
+#define CR_RATE_54M (1 << 15) /* 802.11g */
+#define CR_RATES_80211G 0xff00
+#define CR_RATES_80211B 0x000f
+
+/* Mandatory rates required in the BSS. When producing ACK or CTS messages, if
+ * the device could not find an appropriate rate in CR_BASIC_RATE_TBL, it will
+ * look for a rate in this table that is less than or equal to the rate of
+ * the incoming frame. */
+#define CR_MANDATORY_RATE_TBL CTL_REG(0x0634)
+#define CR_RTS_CTS_RATE CTL_REG(0x0638)
+
+/* These are all bit indexes in CR_RTS_CTS_RATE, so remember to shift. */
+#define RTSCTS_SH_RTS_RATE 0
+#define RTSCTS_SH_EXP_CTS_RATE 4
+#define RTSCTS_SH_RTS_MOD_TYPE 8
+#define RTSCTS_SH_RTS_PMB_TYPE 9
+#define RTSCTS_SH_CTS_RATE 16
+#define RTSCTS_SH_CTS_MOD_TYPE 24
+#define RTSCTS_SH_CTS_PMB_TYPE 25
+
+#define CR_WEP_PROTECT CTL_REG(0x063C)
+#define CR_RX_THRESHOLD CTL_REG(0x0640)
+
+/* register for controlling the LEDS */
+#define CR_LED CTL_REG(0x0644)
+/* masks for controlling LEDs */
+#define LED1 (1 << 8)
+#define LED2 (1 << 9)
+#define LED_SW (1 << 10)
+
+/* Seems to indicate that the configuration is over.
+ */
+#define CR_AFTER_PNP CTL_REG(0x0648)
+#define CR_ACK_TIME_80211 CTL_REG(0x0658)
+
+#define CR_RX_OFFSET CTL_REG(0x065c)
+
+#define CR_BCN_LENGTH CTL_REG(0x0664)
+#define CR_PHY_DELAY CTL_REG(0x066C)
+#define CR_BCN_FIFO CTL_REG(0x0670)
+#define CR_SNIFFER_ON CTL_REG(0x0674)
+
+#define CR_ENCRYPTION_TYPE CTL_REG(0x0678)
+#define NO_WEP 0
+#define WEP64 1
+#define WEP128 5
+#define WEP256 6
+#define ENC_SNIFFER 8
+
+#define CR_ZD1211_RETRY_MAX CTL_REG(0x067C)
+
+#define CR_REG1 CTL_REG(0x0680)
+/* Setting the bit UNLOCK_PHY_REGS disallows the write access to physical
+ * registers, so one could argue it is a LOCK bit. But calling it
+ * LOCK_PHY_REGS makes it confusing.
+ */
+#define UNLOCK_PHY_REGS (1 << 7)
+
+#define CR_DEVICE_STATE CTL_REG(0x0684)
+#define CR_UNDERRUN_CNT CTL_REG(0x0688)
+
+#define CR_RX_FILTER CTL_REG(0x068c)
+#define RX_FILTER_ASSOC_REQUEST (1 << 0)
+#define RX_FILTER_ASSOC_RESPONSE (1 << 1)
+#define RX_FILTER_REASSOC_REQUEST (1 << 2)
+#define RX_FILTER_REASSOC_RESPONSE (1 << 3)
+#define RX_FILTER_PROBE_REQUEST (1 << 4)
+#define RX_FILTER_PROBE_RESPONSE (1 << 5)
+/* bits 6 and 7 reserved */
+#define RX_FILTER_BEACON (1 << 8)
+#define RX_FILTER_ATIM (1 << 9)
+#define RX_FILTER_DISASSOC (1 << 10)
+#define RX_FILTER_AUTH (1 << 11)
+#define RX_FILTER_DEAUTH (1 << 12)
+#define RX_FILTER_PSPOLL (1 << 26)
+#define RX_FILTER_RTS (1 << 27)
+#define RX_FILTER_CTS (1 << 28)
+#define RX_FILTER_ACK (1 << 29)
+#define RX_FILTER_CFEND (1 << 30)
+#define RX_FILTER_CFACK (1 << 31)
+
+/* Enable bits for all frames you are interested in. */
+#define STA_RX_FILTER (RX_FILTER_ASSOC_REQUEST | RX_FILTER_ASSOC_RESPONSE | \
+ RX_FILTER_REASSOC_REQUEST | RX_FILTER_REASSOC_RESPONSE | \
+ RX_FILTER_PROBE_REQUEST | RX_FILTER_PROBE_RESPONSE | \
+ (0x3 << 6) /* vendor driver sets these reserved bits */ | \
+ RX_FILTER_BEACON | RX_FILTER_ATIM | RX_FILTER_DISASSOC | \
+ RX_FILTER_AUTH | RX_FILTER_DEAUTH | \
+ (0x7 << 13) /* vendor driver sets these reserved bits */ | \
+ RX_FILTER_PSPOLL | RX_FILTER_ACK) /* 0x2400ffff */
+
+#define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \
+ RX_FILTER_CFEND | RX_FILTER_CFACK)
+
+#define BCN_MODE_AP 0x1000000
+#define BCN_MODE_IBSS 0x2000000
+
+/* Monitor mode sets filter to 0xfffff */
+
+#define CR_ACK_TIMEOUT_EXT CTL_REG(0x0690)
+#define CR_BCN_FIFO_SEMAPHORE CTL_REG(0x0694)
+
+#define CR_IFS_VALUE CTL_REG(0x0698)
+#define IFS_VALUE_DIFS_SH 0
+#define IFS_VALUE_EIFS_SH 12
+#define IFS_VALUE_SIFS_SH 24
+#define IFS_VALUE_DEFAULT (( 50 << IFS_VALUE_DIFS_SH) | \
+ (1148 << IFS_VALUE_EIFS_SH) | \
+ ( 10 << IFS_VALUE_SIFS_SH))
+
+#define CR_RX_TIME_OUT CTL_REG(0x069C)
+#define CR_TOTAL_RX_FRM CTL_REG(0x06A0)
+#define CR_CRC32_CNT CTL_REG(0x06A4)
+#define CR_CRC16_CNT CTL_REG(0x06A8)
+#define CR_DECRYPTION_ERR_UNI CTL_REG(0x06AC)
+#define CR_RX_FIFO_OVERRUN CTL_REG(0x06B0)
+
+#define CR_DECRYPTION_ERR_MUL CTL_REG(0x06BC)
+
+#define CR_NAV_CNT CTL_REG(0x06C4)
+#define CR_NAV_CCA CTL_REG(0x06C8)
+#define CR_RETRY_CNT CTL_REG(0x06CC)
+
+#define CR_READ_TCB_ADDR CTL_REG(0x06E8)
+#define CR_READ_RFD_ADDR CTL_REG(0x06EC)
+#define CR_CWMIN_CWMAX CTL_REG(0x06F0)
+#define CR_TOTAL_TX_FRM CTL_REG(0x06F4)
+
+/* CAM: Continuous Access Mode (power management) */
+#define CR_CAM_MODE CTL_REG(0x0700)
+#define MODE_IBSS 0x0
+#define MODE_AP 0x1
+#define MODE_STA 0x2
+#define MODE_AP_WDS 0x3
+
+#define CR_CAM_ROLL_TB_LOW CTL_REG(0x0704)
+#define CR_CAM_ROLL_TB_HIGH CTL_REG(0x0708)
+#define CR_CAM_ADDRESS CTL_REG(0x070C)
+#define CR_CAM_DATA CTL_REG(0x0710)
+
+#define CR_ROMDIR CTL_REG(0x0714)
+
+#define CR_DECRY_ERR_FLG_LOW CTL_REG(0x0714)
+#define CR_DECRY_ERR_FLG_HIGH CTL_REG(0x0718)
+
+#define CR_WEPKEY0 CTL_REG(0x0720)
+#define CR_WEPKEY1 CTL_REG(0x0724)
+#define CR_WEPKEY2 CTL_REG(0x0728)
+#define CR_WEPKEY3 CTL_REG(0x072C)
+#define CR_WEPKEY4 CTL_REG(0x0730)
+#define CR_WEPKEY5 CTL_REG(0x0734)
+#define CR_WEPKEY6 CTL_REG(0x0738)
+#define CR_WEPKEY7 CTL_REG(0x073C)
+#define CR_WEPKEY8 CTL_REG(0x0740)
+#define CR_WEPKEY9 CTL_REG(0x0744)
+#define CR_WEPKEY10 CTL_REG(0x0748)
+#define CR_WEPKEY11 CTL_REG(0x074C)
+#define CR_WEPKEY12 CTL_REG(0x0750)
+#define CR_WEPKEY13 CTL_REG(0x0754)
+#define CR_WEPKEY14 CTL_REG(0x0758)
+#define CR_WEPKEY15 CTL_REG(0x075c)
+#define CR_TKIP_MODE CTL_REG(0x0760)
+
+#define CR_EEPROM_PROTECT0 CTL_REG(0x0758)
+#define CR_EEPROM_PROTECT1 CTL_REG(0x075C)
+
+#define CR_DBG_FIFO_RD CTL_REG(0x0800)
+#define CR_DBG_SELECT CTL_REG(0x0804)
+#define CR_FIFO_Length CTL_REG(0x0808)
+
+
+#define CR_RSSI_MGC CTL_REG(0x0810)
+
+#define CR_PON CTL_REG(0x0818)
+#define CR_RX_ON CTL_REG(0x081C)
+#define CR_TX_ON CTL_REG(0x0820)
+#define CR_CHIP_EN CTL_REG(0x0824)
+#define CR_LO_SW CTL_REG(0x0828)
+#define CR_TXRX_SW CTL_REG(0x082C)
+#define CR_S_MD CTL_REG(0x0830)
+
+#define CR_USB_DEBUG_PORT CTL_REG(0x0888)
+#define CR_ZD1211B_CWIN_MAX_MIN_AC0 CTL_REG(0x0b00)
+#define CR_ZD1211B_CWIN_MAX_MIN_AC1 CTL_REG(0x0b04)
+#define CR_ZD1211B_CWIN_MAX_MIN_AC2 CTL_REG(0x0b08)
+#define CR_ZD1211B_CWIN_MAX_MIN_AC3 CTL_REG(0x0b0c)
+#define CR_ZD1211B_AIFS_CTL1 CTL_REG(0x0b10)
+#define CR_ZD1211B_AIFS_CTL2 CTL_REG(0x0b14)
+#define CR_ZD1211B_TXOP CTL_REG(0x0b20)
+#define CR_ZD1211B_RETRY_MAX CTL_REG(0x0b28)
+
+/* Value for CR_ZD1211_RETRY_MAX & CR_ZD1211B_RETRY_MAX. Vendor driver uses 2,
+ * we use 0. The first rate is tried (count+2), then all next rates are tried
+ * twice, until 1 Mbits is tried. */
+#define ZD1211_RETRY_COUNT 0
+#define ZD1211B_RETRY_COUNT \
+ (ZD1211_RETRY_COUNT << 0)| \
+ (ZD1211_RETRY_COUNT << 8)| \
+ (ZD1211_RETRY_COUNT << 16)| \
+ (ZD1211_RETRY_COUNT << 24)
+
+/* Used to detect PLL lock */
+#define UW2453_INTR_REG ((zd_addr_t)0x85c1)
+
+#define CWIN_SIZE 0x007f043f
+
+
+#define HWINT_ENABLED \
+ (INT_TX_COMPLETE_EN| \
+ INT_RX_COMPLETE_EN| \
+ INT_RETRY_FAIL_EN| \
+ INT_WAKEUP_EN| \
+ INT_CFG_NEXT_BCN_EN)
+
+#define HWINT_DISABLED 0
+
+#define E2P_PWR_INT_GUARD 8
+#define E2P_CHANNEL_COUNT 14
+
+/* If you compare this addresses with the ZYDAS orignal driver, please notify
+ * that we use word mapping for the EEPROM.
+ */
+
+/*
+ * Upper 16 bit contains the regulatory domain.
+ */
+#define E2P_SUBID E2P_DATA(0x00)
+#define E2P_POD E2P_DATA(0x02)
+#define E2P_MAC_ADDR_P1 E2P_DATA(0x04)
+#define E2P_MAC_ADDR_P2 E2P_DATA(0x06)
+#define E2P_PWR_CAL_VALUE1 E2P_DATA(0x08)
+#define E2P_PWR_CAL_VALUE2 E2P_DATA(0x0a)
+#define E2P_PWR_CAL_VALUE3 E2P_DATA(0x0c)
+#define E2P_PWR_CAL_VALUE4 E2P_DATA(0x0e)
+#define E2P_PWR_INT_VALUE1 E2P_DATA(0x10)
+#define E2P_PWR_INT_VALUE2 E2P_DATA(0x12)
+#define E2P_PWR_INT_VALUE3 E2P_DATA(0x14)
+#define E2P_PWR_INT_VALUE4 E2P_DATA(0x16)
+
+/* Contains a bit for each allowed channel. It gives for Europe (ETSI 0x30)
+ * also only 11 channels. */
+#define E2P_ALLOWED_CHANNEL E2P_DATA(0x18)
+
+#define E2P_DEVICE_VER E2P_DATA(0x20)
+#define E2P_PHY_REG E2P_DATA(0x25)
+#define E2P_36M_CAL_VALUE1 E2P_DATA(0x28)
+#define E2P_36M_CAL_VALUE2 E2P_DATA(0x2a)
+#define E2P_36M_CAL_VALUE3 E2P_DATA(0x2c)
+#define E2P_36M_CAL_VALUE4 E2P_DATA(0x2e)
+#define E2P_11A_INT_VALUE1 E2P_DATA(0x30)
+#define E2P_11A_INT_VALUE2 E2P_DATA(0x32)
+#define E2P_11A_INT_VALUE3 E2P_DATA(0x34)
+#define E2P_11A_INT_VALUE4 E2P_DATA(0x36)
+#define E2P_48M_CAL_VALUE1 E2P_DATA(0x38)
+#define E2P_48M_CAL_VALUE2 E2P_DATA(0x3a)
+#define E2P_48M_CAL_VALUE3 E2P_DATA(0x3c)
+#define E2P_48M_CAL_VALUE4 E2P_DATA(0x3e)
+#define E2P_48M_INT_VALUE1 E2P_DATA(0x40)
+#define E2P_48M_INT_VALUE2 E2P_DATA(0x42)
+#define E2P_48M_INT_VALUE3 E2P_DATA(0x44)
+#define E2P_48M_INT_VALUE4 E2P_DATA(0x46)
+#define E2P_54M_CAL_VALUE1 E2P_DATA(0x48) /* ??? */
+#define E2P_54M_CAL_VALUE2 E2P_DATA(0x4a)
+#define E2P_54M_CAL_VALUE3 E2P_DATA(0x4c)
+#define E2P_54M_CAL_VALUE4 E2P_DATA(0x4e)
+#define E2P_54M_INT_VALUE1 E2P_DATA(0x50)
+#define E2P_54M_INT_VALUE2 E2P_DATA(0x52)
+#define E2P_54M_INT_VALUE3 E2P_DATA(0x54)
+#define E2P_54M_INT_VALUE4 E2P_DATA(0x56)
+
+/* This word contains the base address of the FW_REG_ registers below */
+#define FWRAW_REGS_ADDR FWRAW_DATA(0x1d)
+
+/* All 16 bit values, offset from the address in FWRAW_REGS_ADDR */
+enum {
+ FW_REG_FIRMWARE_VER = 0,
+ /* non-zero if USB high speed connection */
+ FW_REG_USB_SPEED = 1,
+ FW_REG_FIX_TX_RATE = 2,
+ /* Seems to be able to control LEDs over the firmware */
+ FW_REG_LED_LINK_STATUS = 3,
+ FW_REG_SOFT_RESET = 4,
+ FW_REG_FLASH_CHK = 5,
+};
+
+/* Values for FW_LINK_STATUS */
+#define FW_LINK_OFF 0x0
+#define FW_LINK_TX 0x1
+/* 0x2 - link led on? */
+
+enum {
+ /* indices for ofdm_cal_values */
+ OFDM_36M_INDEX = 0,
+ OFDM_48M_INDEX = 1,
+ OFDM_54M_INDEX = 2,
+};
+
+struct zd_chip {
+ struct zd_usb usb;
+ struct zd_rf rf;
+ struct mutex mutex;
+ /* Base address of FW_REG_ registers */
+ zd_addr_t fw_regs_base;
+ /* EepSetPoint in the vendor driver */
+ u8 pwr_cal_values[E2P_CHANNEL_COUNT];
+ /* integration values in the vendor driver */
+ u8 pwr_int_values[E2P_CHANNEL_COUNT];
+ /* SetPointOFDM in the vendor driver */
+ u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT];
+ u16 link_led;
+ unsigned int pa_type:4,
+ patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1,
+ new_phy_layout:1, al2230s_bit:1,
+ supports_tx_led:1;
+};
+
+static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb)
+{
+ return container_of(usb, struct zd_chip, usb);
+}
+
+static inline struct zd_chip *zd_rf_to_chip(struct zd_rf *rf)
+{
+ return container_of(rf, struct zd_chip, rf);
+}
+
+#define zd_chip_dev(chip) (&(chip)->usb.intf->dev)
+
+void zd_chip_init(struct zd_chip *chip,
+ struct ieee80211_hw *hw,
+ struct usb_interface *intf);
+void zd_chip_clear(struct zd_chip *chip);
+int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr);
+int zd_chip_init_hw(struct zd_chip *chip);
+int zd_chip_reset(struct zd_chip *chip);
+
+static inline int zd_chip_is_zd1211b(struct zd_chip *chip)
+{
+ return chip->usb.is_zd1211b;
+}
+
+static inline int zd_ioread16v_locked(struct zd_chip *chip, u16 *values,
+ const zd_addr_t *addresses,
+ unsigned int count)
+{
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ return zd_usb_ioread16v(&chip->usb, values, addresses, count);
+}
+
+static inline int zd_ioread16_locked(struct zd_chip *chip, u16 *value,
+ const zd_addr_t addr)
+{
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ return zd_usb_ioread16(&chip->usb, value, addr);
+}
+
+int zd_ioread32v_locked(struct zd_chip *chip, u32 *values,
+ const zd_addr_t *addresses, unsigned int count);
+
+static inline int zd_ioread32_locked(struct zd_chip *chip, u32 *value,
+ const zd_addr_t addr)
+{
+ return zd_ioread32v_locked(chip, value, &addr, 1);
+}
+
+static inline int zd_iowrite16_locked(struct zd_chip *chip, u16 value,
+ zd_addr_t addr)
+{
+ struct zd_ioreq16 ioreq;
+
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ ioreq.addr = addr;
+ ioreq.value = value;
+
+ return zd_usb_iowrite16v(&chip->usb, &ioreq, 1);
+}
+
+int zd_iowrite16a_locked(struct zd_chip *chip,
+ const struct zd_ioreq16 *ioreqs, unsigned int count);
+
+int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+ unsigned int count);
+
+static inline int zd_iowrite32_locked(struct zd_chip *chip, u32 value,
+ zd_addr_t addr)
+{
+ struct zd_ioreq32 ioreq;
+
+ ioreq.addr = addr;
+ ioreq.value = value;
+
+ return _zd_iowrite32v_locked(chip, &ioreq, 1);
+}
+
+int zd_iowrite32a_locked(struct zd_chip *chip,
+ const struct zd_ioreq32 *ioreqs, unsigned int count);
+
+static inline int zd_rfwrite_locked(struct zd_chip *chip, u32 value, u8 bits)
+{
+ ZD_ASSERT(mutex_is_locked(&chip->mutex));
+ return zd_usb_rfwrite(&chip->usb, value, bits);
+}
+
+int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value);
+
+int zd_rfwritev_locked(struct zd_chip *chip,
+ const u32* values, unsigned int count, u8 bits);
+int zd_rfwritev_cr_locked(struct zd_chip *chip,
+ const u32* values, unsigned int count);
+
+/* Locking functions for reading and writing registers.
+ * The different parameters are intentional.
+ */
+int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value);
+int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value);
+int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value);
+int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value);
+int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses,
+ u32 *values, unsigned int count);
+int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
+ unsigned int count);
+
+int zd_chip_set_channel(struct zd_chip *chip, u8 channel);
+static inline u8 _zd_chip_get_channel(struct zd_chip *chip)
+{
+ return chip->rf.channel;
+}
+u8 zd_chip_get_channel(struct zd_chip *chip);
+int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain);
+int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr);
+int zd_write_bssid(struct zd_chip *chip, const u8 *bssid);
+int zd_chip_switch_radio_on(struct zd_chip *chip);
+int zd_chip_switch_radio_off(struct zd_chip *chip);
+int zd_chip_enable_int(struct zd_chip *chip);
+void zd_chip_disable_int(struct zd_chip *chip);
+int zd_chip_enable_rxtx(struct zd_chip *chip);
+void zd_chip_disable_rxtx(struct zd_chip *chip);
+int zd_chip_enable_hwint(struct zd_chip *chip);
+int zd_chip_disable_hwint(struct zd_chip *chip);
+int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel);
+int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, int preamble);
+
+static inline int zd_get_encryption_type(struct zd_chip *chip, u32 *type)
+{
+ return zd_ioread32(chip, CR_ENCRYPTION_TYPE, type);
+}
+
+static inline int zd_set_encryption_type(struct zd_chip *chip, u32 type)
+{
+ return zd_iowrite32(chip, CR_ENCRYPTION_TYPE, type);
+}
+
+static inline int zd_chip_get_basic_rates(struct zd_chip *chip, u16 *cr_rates)
+{
+ return zd_ioread16(chip, CR_BASIC_RATE_TBL, cr_rates);
+}
+
+int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates);
+
+int zd_chip_lock_phy_regs(struct zd_chip *chip);
+int zd_chip_unlock_phy_regs(struct zd_chip *chip);
+
+enum led_status {
+ ZD_LED_OFF = 0,
+ ZD_LED_SCANNING = 1,
+ ZD_LED_ASSOCIATED = 2,
+};
+
+int zd_chip_control_leds(struct zd_chip *chip, enum led_status status);
+
+int zd_set_beacon_interval(struct zd_chip *chip, u16 interval, u8 dtim_period,
+ int type);
+
+static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval)
+{
+ return zd_ioread32(chip, CR_BCN_INTERVAL, interval);
+}
+
+struct rx_status;
+
+u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status);
+
+struct zd_mc_hash {
+ u32 low;
+ u32 high;
+};
+
+static inline void zd_mc_clear(struct zd_mc_hash *hash)
+{
+ hash->low = 0;
+ /* The interfaces must always received broadcasts.
+ * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63.
+ */
+ hash->high = 0x80000000;
+}
+
+static inline void zd_mc_add_all(struct zd_mc_hash *hash)
+{
+ hash->low = hash->high = 0xffffffff;
+}
+
+static inline void zd_mc_add_addr(struct zd_mc_hash *hash, u8 *addr)
+{
+ unsigned int i = addr[5] >> 2;
+ if (i < 32) {
+ hash->low |= 1 << i;
+ } else {
+ hash->high |= 1 << (i-32);
+ }
+}
+
+int zd_chip_set_multicast_hash(struct zd_chip *chip,
+ struct zd_mc_hash *hash);
+
+u64 zd_chip_get_tsf(struct zd_chip *chip);
+
+#endif /* _ZD_CHIP_H */
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_def.h b/drivers/net/wireless/zydas/zd1211rw/zd_def.h
new file mode 100644
index 000000000..8ca2d0aab
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_def.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+#ifndef _ZD_DEF_H
+#define _ZD_DEF_H
+
+#include <linux/kernel.h>
+#include <linux/stringify.h>
+#include <linux/device.h>
+
+typedef u16 __nocast zd_addr_t;
+
+#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)
+# define dev_dbg_f_limit(dev, fmt, args...) do { \
+ if (net_ratelimit()) \
+ dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \
+} while (0)
+# define dev_dbg_f_cond(dev, cond, fmt, args...) ({ \
+ bool __cond = !!(cond); \
+ if (unlikely(__cond)) \
+ dev_printk_f(KERN_DEBUG, dev, fmt, ## args); \
+})
+#else
+# define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0)
+# define dev_dbg_f_limit(dev, fmt, args...) do { (void)(dev); } while (0)
+# define dev_dbg_f_cond(dev, cond, fmt, args...) do { (void)(dev); } while (0)
+#endif /* DEBUG */
+
+#ifdef DEBUG
+# define ZD_ASSERT(x) \
+do { \
+ if (unlikely(!(x))) { \
+ pr_debug("%s:%d ASSERT %s VIOLATED!\n", \
+ __FILE__, __LINE__, __stringify(x)); \
+ dump_stack(); \
+ } \
+} while (0)
+#else
+# define ZD_ASSERT(x) do { } while (0)
+#endif
+
+#ifdef DEBUG
+# define ZD_MEMCLEAR(pointer, size) memset((pointer), 0xff, (size))
+#else
+# define ZD_MEMCLEAR(pointer, size) do { } while (0)
+#endif
+
+#endif /* _ZD_DEF_H */
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
new file mode 100644
index 000000000..80b905d49
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
@@ -0,0 +1,1538 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ * Copyright (C) 2006-2007 Michael Wu <flamingice@sourmilk.net>
+ * Copyright (C) 2007-2008 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu>
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/jiffies.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "zd_def.h"
+#include "zd_chip.h"
+#include "zd_mac.h"
+#include "zd_rf.h"
+
+struct zd_reg_alpha2_map {
+ u32 reg;
+ char alpha2[2];
+};
+
+static struct zd_reg_alpha2_map reg_alpha2_map[] = {
+ { ZD_REGDOMAIN_FCC, "US" },
+ { ZD_REGDOMAIN_IC, "CA" },
+ { ZD_REGDOMAIN_ETSI, "DE" }, /* Generic ETSI, use most restrictive */
+ { ZD_REGDOMAIN_JAPAN, "JP" },
+ { ZD_REGDOMAIN_JAPAN_2, "JP" },
+ { ZD_REGDOMAIN_JAPAN_3, "JP" },
+ { ZD_REGDOMAIN_SPAIN, "ES" },
+ { ZD_REGDOMAIN_FRANCE, "FR" },
+};
+
+/* This table contains the hardware specific values for the modulation rates. */
+static const struct ieee80211_rate zd_rates[] = {
+ { .bitrate = 10,
+ .hw_value = ZD_CCK_RATE_1M, },
+ { .bitrate = 20,
+ .hw_value = ZD_CCK_RATE_2M,
+ .hw_value_short = ZD_CCK_RATE_2M | ZD_CCK_PREA_SHORT,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 55,
+ .hw_value = ZD_CCK_RATE_5_5M,
+ .hw_value_short = ZD_CCK_RATE_5_5M | ZD_CCK_PREA_SHORT,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 110,
+ .hw_value = ZD_CCK_RATE_11M,
+ .hw_value_short = ZD_CCK_RATE_11M | ZD_CCK_PREA_SHORT,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE },
+ { .bitrate = 60,
+ .hw_value = ZD_OFDM_RATE_6M,
+ .flags = 0 },
+ { .bitrate = 90,
+ .hw_value = ZD_OFDM_RATE_9M,
+ .flags = 0 },
+ { .bitrate = 120,
+ .hw_value = ZD_OFDM_RATE_12M,
+ .flags = 0 },
+ { .bitrate = 180,
+ .hw_value = ZD_OFDM_RATE_18M,
+ .flags = 0 },
+ { .bitrate = 240,
+ .hw_value = ZD_OFDM_RATE_24M,
+ .flags = 0 },
+ { .bitrate = 360,
+ .hw_value = ZD_OFDM_RATE_36M,
+ .flags = 0 },
+ { .bitrate = 480,
+ .hw_value = ZD_OFDM_RATE_48M,
+ .flags = 0 },
+ { .bitrate = 540,
+ .hw_value = ZD_OFDM_RATE_54M,
+ .flags = 0 },
+};
+
+/*
+ * Zydas retry rates table. Each line is listed in the same order as
+ * in zd_rates[] and contains all the rate used when a packet is sent
+ * starting with a given rates. Let's consider an example :
+ *
+ * "11 Mbits : 4, 3, 2, 1, 0" means :
+ * - packet is sent using 4 different rates
+ * - 1st rate is index 3 (ie 11 Mbits)
+ * - 2nd rate is index 2 (ie 5.5 Mbits)
+ * - 3rd rate is index 1 (ie 2 Mbits)
+ * - 4th rate is index 0 (ie 1 Mbits)
+ */
+
+static const struct tx_retry_rate zd_retry_rates[] = {
+ { /* 1 Mbits */ 1, { 0 }},
+ { /* 2 Mbits */ 2, { 1, 0 }},
+ { /* 5.5 Mbits */ 3, { 2, 1, 0 }},
+ { /* 11 Mbits */ 4, { 3, 2, 1, 0 }},
+ { /* 6 Mbits */ 5, { 4, 3, 2, 1, 0 }},
+ { /* 9 Mbits */ 6, { 5, 4, 3, 2, 1, 0}},
+ { /* 12 Mbits */ 5, { 6, 3, 2, 1, 0 }},
+ { /* 18 Mbits */ 6, { 7, 6, 3, 2, 1, 0 }},
+ { /* 24 Mbits */ 6, { 8, 6, 3, 2, 1, 0 }},
+ { /* 36 Mbits */ 7, { 9, 8, 6, 3, 2, 1, 0 }},
+ { /* 48 Mbits */ 8, {10, 9, 8, 6, 3, 2, 1, 0 }},
+ { /* 54 Mbits */ 9, {11, 10, 9, 8, 6, 3, 2, 1, 0 }}
+};
+
+static const struct ieee80211_channel zd_channels[] = {
+ { .center_freq = 2412, .hw_value = 1 },
+ { .center_freq = 2417, .hw_value = 2 },
+ { .center_freq = 2422, .hw_value = 3 },
+ { .center_freq = 2427, .hw_value = 4 },
+ { .center_freq = 2432, .hw_value = 5 },
+ { .center_freq = 2437, .hw_value = 6 },
+ { .center_freq = 2442, .hw_value = 7 },
+ { .center_freq = 2447, .hw_value = 8 },
+ { .center_freq = 2452, .hw_value = 9 },
+ { .center_freq = 2457, .hw_value = 10 },
+ { .center_freq = 2462, .hw_value = 11 },
+ { .center_freq = 2467, .hw_value = 12 },
+ { .center_freq = 2472, .hw_value = 13 },
+ { .center_freq = 2484, .hw_value = 14 },
+};
+
+static void housekeeping_init(struct zd_mac *mac);
+static void housekeeping_enable(struct zd_mac *mac);
+static void housekeeping_disable(struct zd_mac *mac);
+static void beacon_init(struct zd_mac *mac);
+static void beacon_enable(struct zd_mac *mac);
+static void beacon_disable(struct zd_mac *mac);
+static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble);
+static int zd_mac_config_beacon(struct ieee80211_hw *hw,
+ struct sk_buff *beacon, bool in_intr);
+
+static int zd_reg2alpha2(u8 regdomain, char *alpha2)
+{
+ unsigned int i;
+ struct zd_reg_alpha2_map *reg_map;
+ for (i = 0; i < ARRAY_SIZE(reg_alpha2_map); i++) {
+ reg_map = &reg_alpha2_map[i];
+ if (regdomain == reg_map->reg) {
+ alpha2[0] = reg_map->alpha2[0];
+ alpha2[1] = reg_map->alpha2[1];
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int zd_check_signal(struct ieee80211_hw *hw, int signal)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+
+ dev_dbg_f_cond(zd_mac_dev(mac), signal < 0 || signal > 100,
+ "%s: signal value from device not in range 0..100, "
+ "but %d.\n", __func__, signal);
+
+ if (signal < 0)
+ signal = 0;
+ else if (signal > 100)
+ signal = 100;
+
+ return signal;
+}
+
+int zd_mac_preinit_hw(struct ieee80211_hw *hw)
+{
+ int r;
+ u8 addr[ETH_ALEN];
+ struct zd_mac *mac = zd_hw_mac(hw);
+
+ r = zd_chip_read_mac_addr_fw(&mac->chip, addr);
+ if (r)
+ return r;
+
+ SET_IEEE80211_PERM_ADDR(hw, addr);
+
+ return 0;
+}
+
+int zd_mac_init_hw(struct ieee80211_hw *hw)
+{
+ int r;
+ struct zd_mac *mac = zd_hw_mac(hw);
+ struct zd_chip *chip = &mac->chip;
+ char alpha2[2];
+ u8 default_regdomain;
+
+ r = zd_chip_enable_int(chip);
+ if (r)
+ goto out;
+ r = zd_chip_init_hw(chip);
+ if (r)
+ goto disable_int;
+
+ ZD_ASSERT(!irqs_disabled());
+
+ r = zd_read_regdomain(chip, &default_regdomain);
+ if (r)
+ goto disable_int;
+ spin_lock_irq(&mac->lock);
+ mac->regdomain = mac->default_regdomain = default_regdomain;
+ spin_unlock_irq(&mac->lock);
+
+ /* We must inform the device that we are doing encryption/decryption in
+ * software at the moment. */
+ r = zd_set_encryption_type(chip, ENC_SNIFFER);
+ if (r)
+ goto disable_int;
+
+ r = zd_reg2alpha2(mac->regdomain, alpha2);
+ if (r)
+ goto disable_int;
+
+ r = regulatory_hint(hw->wiphy, alpha2);
+disable_int:
+ zd_chip_disable_int(chip);
+out:
+ return r;
+}
+
+void zd_mac_clear(struct zd_mac *mac)
+{
+ flush_workqueue(zd_workqueue);
+ zd_chip_clear(&mac->chip);
+ ZD_MEMCLEAR(mac, sizeof(struct zd_mac));
+}
+
+static int set_rx_filter(struct zd_mac *mac)
+{
+ unsigned long flags;
+ u32 filter = STA_RX_FILTER;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ if (mac->pass_ctrl)
+ filter |= RX_FILTER_CTRL;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter);
+}
+
+static int set_mac_and_bssid(struct zd_mac *mac)
+{
+ int r;
+
+ if (!mac->vif)
+ return -1;
+
+ r = zd_write_mac_addr(&mac->chip, mac->vif->addr);
+ if (r)
+ return r;
+
+ /* Vendor driver after setting MAC either sets BSSID for AP or
+ * filter for other modes.
+ */
+ if (mac->type != NL80211_IFTYPE_AP)
+ return set_rx_filter(mac);
+ else
+ return zd_write_bssid(&mac->chip, mac->vif->addr);
+}
+
+static int set_mc_hash(struct zd_mac *mac)
+{
+ struct zd_mc_hash hash;
+ zd_mc_clear(&hash);
+ return zd_chip_set_multicast_hash(&mac->chip, &hash);
+}
+
+int zd_op_start(struct ieee80211_hw *hw)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ struct zd_chip *chip = &mac->chip;
+ struct zd_usb *usb = &chip->usb;
+ int r;
+
+ if (!usb->initialized) {
+ r = zd_usb_init_hw(usb);
+ if (r)
+ goto out;
+ }
+
+ r = zd_chip_enable_int(chip);
+ if (r < 0)
+ goto out;
+
+ r = zd_chip_set_basic_rates(chip, CR_RATES_80211B | CR_RATES_80211G);
+ if (r < 0)
+ goto disable_int;
+ r = set_rx_filter(mac);
+ if (r)
+ goto disable_int;
+ r = set_mc_hash(mac);
+ if (r)
+ goto disable_int;
+
+ /* Wait after setting the multicast hash table and powering on
+ * the radio otherwise interface bring up will fail. This matches
+ * what the vendor driver did.
+ */
+ msleep(10);
+
+ r = zd_chip_switch_radio_on(chip);
+ if (r < 0) {
+ dev_err(zd_chip_dev(chip),
+ "%s: failed to set radio on\n", __func__);
+ goto disable_int;
+ }
+ r = zd_chip_enable_rxtx(chip);
+ if (r < 0)
+ goto disable_radio;
+ r = zd_chip_enable_hwint(chip);
+ if (r < 0)
+ goto disable_rxtx;
+
+ housekeeping_enable(mac);
+ beacon_enable(mac);
+ set_bit(ZD_DEVICE_RUNNING, &mac->flags);
+ return 0;
+disable_rxtx:
+ zd_chip_disable_rxtx(chip);
+disable_radio:
+ zd_chip_switch_radio_off(chip);
+disable_int:
+ zd_chip_disable_int(chip);
+out:
+ return r;
+}
+
+void zd_op_stop(struct ieee80211_hw *hw)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ struct zd_chip *chip = &mac->chip;
+ struct sk_buff *skb;
+ struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue;
+
+ clear_bit(ZD_DEVICE_RUNNING, &mac->flags);
+
+ /* The order here deliberately is a little different from the open()
+ * method, since we need to make sure there is no opportunity for RX
+ * frames to be processed by mac80211 after we have stopped it.
+ */
+
+ zd_chip_disable_rxtx(chip);
+ beacon_disable(mac);
+ housekeeping_disable(mac);
+ flush_workqueue(zd_workqueue);
+
+ zd_chip_disable_hwint(chip);
+ zd_chip_switch_radio_off(chip);
+ zd_chip_disable_int(chip);
+
+
+ while ((skb = skb_dequeue(ack_wait_queue)))
+ dev_kfree_skb_any(skb);
+}
+
+int zd_restore_settings(struct zd_mac *mac)
+{
+ struct sk_buff *beacon;
+ struct zd_mc_hash multicast_hash;
+ unsigned int short_preamble;
+ int r, beacon_interval, beacon_period;
+ u8 channel;
+
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+
+ spin_lock_irq(&mac->lock);
+ multicast_hash = mac->multicast_hash;
+ short_preamble = mac->short_preamble;
+ beacon_interval = mac->beacon.interval;
+ beacon_period = mac->beacon.period;
+ channel = mac->channel;
+ spin_unlock_irq(&mac->lock);
+
+ r = set_mac_and_bssid(mac);
+ if (r < 0) {
+ dev_dbg_f(zd_mac_dev(mac), "set_mac_and_bssid failed, %d\n", r);
+ return r;
+ }
+
+ r = zd_chip_set_channel(&mac->chip, channel);
+ if (r < 0) {
+ dev_dbg_f(zd_mac_dev(mac), "zd_chip_set_channel failed, %d\n",
+ r);
+ return r;
+ }
+
+ set_rts_cts(mac, short_preamble);
+
+ r = zd_chip_set_multicast_hash(&mac->chip, &multicast_hash);
+ if (r < 0) {
+ dev_dbg_f(zd_mac_dev(mac),
+ "zd_chip_set_multicast_hash failed, %d\n", r);
+ return r;
+ }
+
+ if (mac->type == NL80211_IFTYPE_MESH_POINT ||
+ mac->type == NL80211_IFTYPE_ADHOC ||
+ mac->type == NL80211_IFTYPE_AP) {
+ if (mac->vif != NULL) {
+ beacon = ieee80211_beacon_get(mac->hw, mac->vif, 0);
+ if (beacon)
+ zd_mac_config_beacon(mac->hw, beacon, false);
+ }
+
+ zd_set_beacon_interval(&mac->chip, beacon_interval,
+ beacon_period, mac->type);
+
+ spin_lock_irq(&mac->lock);
+ mac->beacon.last_update = jiffies;
+ spin_unlock_irq(&mac->lock);
+ }
+
+ return 0;
+}
+
+/**
+ * zd_mac_tx_status - reports tx status of a packet if required
+ * @hw: a &struct ieee80211_hw pointer
+ * @skb: a sk-buffer
+ * @ackssi: ACK signal strength
+ * @tx_status: success and/or retry
+ *
+ * This information calls ieee80211_tx_status_irqsafe() if required by the
+ * control information. It copies the control information into the status
+ * information.
+ *
+ * If no status information has been requested, the skb is freed.
+ */
+static void zd_mac_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
+ int ackssi, struct tx_status *tx_status)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int i;
+ int success = 1, retry = 1;
+ int first_idx;
+ const struct tx_retry_rate *retries;
+
+ ieee80211_tx_info_clear_status(info);
+
+ if (tx_status) {
+ success = !tx_status->failure;
+ retry = tx_status->retry + success;
+ }
+
+ if (success) {
+ /* success */
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ } else {
+ /* failure */
+ info->flags &= ~IEEE80211_TX_STAT_ACK;
+ }
+
+ first_idx = info->status.rates[0].idx;
+ ZD_ASSERT(0<=first_idx && first_idx<ARRAY_SIZE(zd_retry_rates));
+ retries = &zd_retry_rates[first_idx];
+ ZD_ASSERT(1 <= retry && retry <= retries->count);
+
+ info->status.rates[0].idx = retries->rate[0];
+ info->status.rates[0].count = 1; // (retry > 1 ? 2 : 1);
+
+ for (i=1; i<IEEE80211_TX_MAX_RATES-1 && i<retry; i++) {
+ info->status.rates[i].idx = retries->rate[i];
+ info->status.rates[i].count = 1; // ((i==retry-1) && success ? 1:2);
+ }
+ for (; i<IEEE80211_TX_MAX_RATES && i<retry; i++) {
+ info->status.rates[i].idx = retries->rate[retry - 1];
+ info->status.rates[i].count = 1; // (success ? 1:2);
+ }
+ if (i<IEEE80211_TX_MAX_RATES)
+ info->status.rates[i].idx = -1; /* terminate */
+
+ info->status.ack_signal = zd_check_signal(hw, ackssi);
+ ieee80211_tx_status_irqsafe(hw, skb);
+}
+
+/**
+ * zd_mac_tx_failed - callback for failed frames
+ * @urb: pointer to the urb structure
+ *
+ * This function is called if a frame couldn't be successfully
+ * transferred. The first frame from the tx queue, will be selected and
+ * reported as error to the upper layers.
+ */
+void zd_mac_tx_failed(struct urb *urb)
+{
+ struct ieee80211_hw * hw = zd_usb_to_hw(urb->context);
+ struct zd_mac *mac = zd_hw_mac(hw);
+ struct sk_buff_head *q = &mac->ack_wait_queue;
+ struct sk_buff *skb;
+ struct tx_status *tx_status = (struct tx_status *)urb->transfer_buffer;
+ unsigned long flags;
+ int success = !tx_status->failure;
+ int retry = tx_status->retry + success;
+ int found = 0;
+ int i, position = 0;
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ skb_queue_walk(q, skb) {
+ struct ieee80211_hdr *tx_hdr;
+ struct ieee80211_tx_info *info;
+ int first_idx, final_idx;
+ const struct tx_retry_rate *retries;
+ u8 final_rate;
+
+ position ++;
+
+ /* if the hardware reports a failure and we had a 802.11 ACK
+ * pending, then we skip the first skb when searching for a
+ * matching frame */
+ if (tx_status->failure && mac->ack_pending &&
+ skb_queue_is_first(q, skb)) {
+ continue;
+ }
+
+ tx_hdr = (struct ieee80211_hdr *)skb->data;
+
+ /* we skip all frames not matching the reported destination */
+ if (unlikely(!ether_addr_equal(tx_hdr->addr1, tx_status->mac)))
+ continue;
+
+ /* we skip all frames not matching the reported final rate */
+
+ info = IEEE80211_SKB_CB(skb);
+ first_idx = info->status.rates[0].idx;
+ ZD_ASSERT(0<=first_idx && first_idx<ARRAY_SIZE(zd_retry_rates));
+ retries = &zd_retry_rates[first_idx];
+ if (retry <= 0 || retry > retries->count)
+ continue;
+
+ final_idx = retries->rate[retry - 1];
+ final_rate = zd_rates[final_idx].hw_value;
+
+ if (final_rate != tx_status->rate) {
+ continue;
+ }
+
+ found = 1;
+ break;
+ }
+
+ if (found) {
+ for (i=1; i<=position; i++) {
+ skb = __skb_dequeue(q);
+ zd_mac_tx_status(hw, skb,
+ mac->ack_pending ? mac->ack_signal : 0,
+ i == position ? tx_status : NULL);
+ mac->ack_pending = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&q->lock, flags);
+}
+
+/**
+ * zd_mac_tx_to_dev - callback for USB layer
+ * @skb: a &sk_buff pointer
+ * @error: error value, 0 if transmission successful
+ *
+ * Informs the MAC layer that the frame has successfully transferred to the
+ * device. If an ACK is required and the transfer to the device has been
+ * successful, the packets are put on the @ack_wait_queue with
+ * the control set removed.
+ */
+void zd_mac_tx_to_dev(struct sk_buff *skb, int error)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hw *hw = info->rate_driver_data[0];
+ struct zd_mac *mac = zd_hw_mac(hw);
+
+ ieee80211_tx_info_clear_status(info);
+
+ skb_pull(skb, sizeof(struct zd_ctrlset));
+ if (unlikely(error ||
+ (info->flags & IEEE80211_TX_CTL_NO_ACK))) {
+ /*
+ * FIXME : do we need to fill in anything ?
+ */
+ ieee80211_tx_status_irqsafe(hw, skb);
+ } else {
+ struct sk_buff_head *q = &mac->ack_wait_queue;
+
+ skb_queue_tail(q, skb);
+ while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) {
+ zd_mac_tx_status(hw, skb_dequeue(q),
+ mac->ack_pending ? mac->ack_signal : 0,
+ NULL);
+ mac->ack_pending = 0;
+ }
+ }
+}
+
+static int zd_calc_tx_length_us(u8 *service, u8 zd_rate, u16 tx_length)
+{
+ /* ZD_PURE_RATE() must be used to remove the modulation type flag of
+ * the zd-rate values.
+ */
+ static const u8 rate_divisor[] = {
+ [ZD_PURE_RATE(ZD_CCK_RATE_1M)] = 1,
+ [ZD_PURE_RATE(ZD_CCK_RATE_2M)] = 2,
+ /* Bits must be doubled. */
+ [ZD_PURE_RATE(ZD_CCK_RATE_5_5M)] = 11,
+ [ZD_PURE_RATE(ZD_CCK_RATE_11M)] = 11,
+ [ZD_PURE_RATE(ZD_OFDM_RATE_6M)] = 6,
+ [ZD_PURE_RATE(ZD_OFDM_RATE_9M)] = 9,
+ [ZD_PURE_RATE(ZD_OFDM_RATE_12M)] = 12,
+ [ZD_PURE_RATE(ZD_OFDM_RATE_18M)] = 18,
+ [ZD_PURE_RATE(ZD_OFDM_RATE_24M)] = 24,
+ [ZD_PURE_RATE(ZD_OFDM_RATE_36M)] = 36,
+ [ZD_PURE_RATE(ZD_OFDM_RATE_48M)] = 48,
+ [ZD_PURE_RATE(ZD_OFDM_RATE_54M)] = 54,
+ };
+
+ u32 bits = (u32)tx_length * 8;
+ u32 divisor;
+
+ divisor = rate_divisor[ZD_PURE_RATE(zd_rate)];
+ if (divisor == 0)
+ return -EINVAL;
+
+ switch (zd_rate) {
+ case ZD_CCK_RATE_5_5M:
+ bits = (2*bits) + 10; /* round up to the next integer */
+ break;
+ case ZD_CCK_RATE_11M:
+ if (service) {
+ u32 t = bits % 11;
+ *service &= ~ZD_PLCP_SERVICE_LENGTH_EXTENSION;
+ if (0 < t && t <= 3) {
+ *service |= ZD_PLCP_SERVICE_LENGTH_EXTENSION;
+ }
+ }
+ bits += 10; /* round up to the next integer */
+ break;
+ }
+
+ return bits/divisor;
+}
+
+static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs,
+ struct ieee80211_hdr *header,
+ struct ieee80211_tx_info *info)
+{
+ /*
+ * CONTROL TODO:
+ * - if backoff needed, enable bit 0
+ * - if burst (backoff not needed) disable bit 0
+ */
+
+ cs->control = 0;
+
+ /* First fragment */
+ if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+ cs->control |= ZD_CS_NEED_RANDOM_BACKOFF;
+
+ /* No ACK expected (multicast, etc.) */
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ cs->control |= ZD_CS_NO_ACK;
+
+ /* PS-POLL */
+ if (ieee80211_is_pspoll(header->frame_control))
+ cs->control |= ZD_CS_PS_POLL_FRAME;
+
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
+ cs->control |= ZD_CS_RTS;
+
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ cs->control |= ZD_CS_SELF_CTS;
+
+ /* FIXME: Management frame? */
+}
+
+static bool zd_mac_match_cur_beacon(struct zd_mac *mac, struct sk_buff *beacon)
+{
+ if (!mac->beacon.cur_beacon)
+ return false;
+
+ if (mac->beacon.cur_beacon->len != beacon->len)
+ return false;
+
+ return !memcmp(beacon->data, mac->beacon.cur_beacon->data, beacon->len);
+}
+
+static void zd_mac_free_cur_beacon_locked(struct zd_mac *mac)
+{
+ ZD_ASSERT(mutex_is_locked(&mac->chip.mutex));
+
+ kfree_skb(mac->beacon.cur_beacon);
+ mac->beacon.cur_beacon = NULL;
+}
+
+static void zd_mac_free_cur_beacon(struct zd_mac *mac)
+{
+ mutex_lock(&mac->chip.mutex);
+ zd_mac_free_cur_beacon_locked(mac);
+ mutex_unlock(&mac->chip.mutex);
+}
+
+static int zd_mac_config_beacon(struct ieee80211_hw *hw, struct sk_buff *beacon,
+ bool in_intr)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ int r, ret, num_cmds, req_pos = 0;
+ u32 tmp, j = 0;
+ /* 4 more bytes for tail CRC */
+ u32 full_len = beacon->len + 4;
+ unsigned long end_jiffies, message_jiffies;
+ struct zd_ioreq32 *ioreqs;
+
+ mutex_lock(&mac->chip.mutex);
+
+ /* Check if hw already has this beacon. */
+ if (zd_mac_match_cur_beacon(mac, beacon)) {
+ r = 0;
+ goto out_nofree;
+ }
+
+ /* Alloc memory for full beacon write at once. */
+ num_cmds = 1 + zd_chip_is_zd1211b(&mac->chip) + full_len;
+ ioreqs = kmalloc_array(num_cmds, sizeof(struct zd_ioreq32),
+ GFP_KERNEL);
+ if (!ioreqs) {
+ r = -ENOMEM;
+ goto out_nofree;
+ }
+
+ r = zd_iowrite32_locked(&mac->chip, 0, CR_BCN_FIFO_SEMAPHORE);
+ if (r < 0)
+ goto out;
+ r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE);
+ if (r < 0)
+ goto release_sema;
+ if (in_intr && tmp & 0x2) {
+ r = -EBUSY;
+ goto release_sema;
+ }
+
+ end_jiffies = jiffies + HZ / 2; /*~500ms*/
+ message_jiffies = jiffies + HZ / 10; /*~100ms*/
+ while (tmp & 0x2) {
+ r = zd_ioread32_locked(&mac->chip, &tmp, CR_BCN_FIFO_SEMAPHORE);
+ if (r < 0)
+ goto release_sema;
+ if (time_is_before_eq_jiffies(message_jiffies)) {
+ message_jiffies = jiffies + HZ / 10;
+ dev_err(zd_mac_dev(mac),
+ "CR_BCN_FIFO_SEMAPHORE not ready\n");
+ if (time_is_before_eq_jiffies(end_jiffies)) {
+ dev_err(zd_mac_dev(mac),
+ "Giving up beacon config.\n");
+ r = -ETIMEDOUT;
+ goto reset_device;
+ }
+ }
+ msleep(20);
+ }
+
+ ioreqs[req_pos].addr = CR_BCN_FIFO;
+ ioreqs[req_pos].value = full_len - 1;
+ req_pos++;
+ if (zd_chip_is_zd1211b(&mac->chip)) {
+ ioreqs[req_pos].addr = CR_BCN_LENGTH;
+ ioreqs[req_pos].value = full_len - 1;
+ req_pos++;
+ }
+
+ for (j = 0 ; j < beacon->len; j++) {
+ ioreqs[req_pos].addr = CR_BCN_FIFO;
+ ioreqs[req_pos].value = *((u8 *)(beacon->data + j));
+ req_pos++;
+ }
+
+ for (j = 0; j < 4; j++) {
+ ioreqs[req_pos].addr = CR_BCN_FIFO;
+ ioreqs[req_pos].value = 0x0;
+ req_pos++;
+ }
+
+ BUG_ON(req_pos != num_cmds);
+
+ r = zd_iowrite32a_locked(&mac->chip, ioreqs, num_cmds);
+
+release_sema:
+ /*
+ * Try very hard to release device beacon semaphore, as otherwise
+ * device/driver can be left in unusable state.
+ */
+ end_jiffies = jiffies + HZ / 2; /*~500ms*/
+ ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE);
+ while (ret < 0) {
+ if (in_intr || time_is_before_eq_jiffies(end_jiffies)) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ msleep(20);
+ ret = zd_iowrite32_locked(&mac->chip, 1, CR_BCN_FIFO_SEMAPHORE);
+ }
+
+ if (ret < 0)
+ dev_err(zd_mac_dev(mac), "Could not release "
+ "CR_BCN_FIFO_SEMAPHORE!\n");
+ if (r < 0 || ret < 0) {
+ if (r >= 0)
+ r = ret;
+
+ /* We don't know if beacon was written successfully or not,
+ * so clear current. */
+ zd_mac_free_cur_beacon_locked(mac);
+
+ goto out;
+ }
+
+ /* Beacon has now been written successfully, update current. */
+ zd_mac_free_cur_beacon_locked(mac);
+ mac->beacon.cur_beacon = beacon;
+ beacon = NULL;
+
+ /* 802.11b/g 2.4G CCK 1Mb
+ * 802.11a, not yet implemented, uses different values (see GPL vendor
+ * driver)
+ */
+ r = zd_iowrite32_locked(&mac->chip, 0x00000400 | (full_len << 19),
+ CR_BCN_PLCP_CFG);
+out:
+ kfree(ioreqs);
+out_nofree:
+ kfree_skb(beacon);
+ mutex_unlock(&mac->chip.mutex);
+
+ return r;
+
+reset_device:
+ zd_mac_free_cur_beacon_locked(mac);
+ kfree_skb(beacon);
+
+ mutex_unlock(&mac->chip.mutex);
+ kfree(ioreqs);
+
+ /* semaphore stuck, reset device to avoid fw freeze later */
+ dev_warn(zd_mac_dev(mac), "CR_BCN_FIFO_SEMAPHORE stuck, "
+ "resetting device...");
+ usb_queue_reset_device(mac->chip.usb.intf);
+
+ return r;
+}
+
+static int fill_ctrlset(struct zd_mac *mac,
+ struct sk_buff *skb)
+{
+ int r;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ unsigned int frag_len = skb->len + FCS_LEN;
+ unsigned int packet_length;
+ struct ieee80211_rate *txrate;
+ struct zd_ctrlset *cs = skb_push(skb, sizeof(struct zd_ctrlset));
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ ZD_ASSERT(frag_len <= 0xffff);
+
+ /*
+ * Firmware computes the duration itself (for all frames except PSPoll)
+ * and needs the field set to 0 at input, otherwise firmware messes up
+ * duration_id and sets bits 14 and 15 on.
+ */
+ if (!ieee80211_is_pspoll(hdr->frame_control))
+ hdr->duration_id = 0;
+
+ txrate = ieee80211_get_tx_rate(mac->hw, info);
+
+ cs->modulation = txrate->hw_value;
+ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
+ cs->modulation = txrate->hw_value_short;
+
+ cs->tx_length = cpu_to_le16(frag_len);
+
+ cs_set_control(mac, cs, hdr, info);
+
+ packet_length = frag_len + sizeof(struct zd_ctrlset) + 10;
+ ZD_ASSERT(packet_length <= 0xffff);
+ /* ZD1211B: Computing the length difference this way, gives us
+ * flexibility to compute the packet length.
+ */
+ cs->packet_length = cpu_to_le16(zd_chip_is_zd1211b(&mac->chip) ?
+ packet_length - frag_len : packet_length);
+
+ /*
+ * CURRENT LENGTH:
+ * - transmit frame length in microseconds
+ * - seems to be derived from frame length
+ * - see Cal_Us_Service() in zdinlinef.h
+ * - if macp->bTxBurstEnable is enabled, then multiply by 4
+ * - bTxBurstEnable is never set in the vendor driver
+ *
+ * SERVICE:
+ * - "for PLCP configuration"
+ * - always 0 except in some situations at 802.11b 11M
+ * - see line 53 of zdinlinef.h
+ */
+ cs->service = 0;
+ r = zd_calc_tx_length_us(&cs->service, ZD_RATE(cs->modulation),
+ le16_to_cpu(cs->tx_length));
+ if (r < 0)
+ return r;
+ cs->current_length = cpu_to_le16(r);
+ cs->next_frame_length = 0;
+
+ return 0;
+}
+
+/**
+ * zd_op_tx - transmits a network frame to the device
+ *
+ * @hw: a &struct ieee80211_hw pointer
+ * @control: the control structure
+ * @skb: socket buffer
+ *
+ * This function transmit an IEEE 802.11 network frame to the device. The
+ * control block of the skbuff will be initialized. If necessary the incoming
+ * mac80211 queues will be stopped.
+ */
+static void zd_op_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int r;
+
+ r = fill_ctrlset(mac, skb);
+ if (r)
+ goto fail;
+
+ info->rate_driver_data[0] = hw;
+
+ r = zd_usb_tx(&mac->chip.usb, skb);
+ if (r)
+ goto fail;
+ return;
+
+fail:
+ dev_kfree_skb(skb);
+}
+
+/**
+ * filter_ack - filters incoming packets for acknowledgements
+ * @hw: a &struct ieee80211_hw pointer
+ * @rx_hdr: received header
+ * @stats: the status for the received packet
+ *
+ * This functions looks for ACK packets and tries to match them with the
+ * frames in the tx queue. If a match is found the frame will be dequeued and
+ * the upper layers is informed about the successful transmission. If
+ * mac80211 queues have been stopped and the number of frames still to be
+ * transmitted is low the queues will be opened again.
+ *
+ * Returns 1 if the frame was an ACK, 0 if it was ignored.
+ */
+static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
+ struct ieee80211_rx_status *stats)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ struct sk_buff *skb;
+ struct sk_buff_head *q;
+ unsigned long flags;
+ int found = 0;
+ int i, position = 0;
+
+ if (!ieee80211_is_ack(rx_hdr->frame_control))
+ return 0;
+
+ q = &mac->ack_wait_queue;
+ spin_lock_irqsave(&q->lock, flags);
+ skb_queue_walk(q, skb) {
+ struct ieee80211_hdr *tx_hdr;
+
+ position ++;
+
+ if (mac->ack_pending && skb_queue_is_first(q, skb))
+ continue;
+
+ tx_hdr = (struct ieee80211_hdr *)skb->data;
+ if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1)))
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ for (i=1; i<position; i++) {
+ skb = __skb_dequeue(q);
+ zd_mac_tx_status(hw, skb,
+ mac->ack_pending ? mac->ack_signal : 0,
+ NULL);
+ mac->ack_pending = 0;
+ }
+
+ mac->ack_pending = 1;
+ mac->ack_signal = stats->signal;
+
+ /* Prevent pending tx-packet on AP-mode */
+ if (mac->type == NL80211_IFTYPE_AP) {
+ skb = __skb_dequeue(q);
+ zd_mac_tx_status(hw, skb, mac->ack_signal, NULL);
+ mac->ack_pending = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&q->lock, flags);
+ return 1;
+}
+
+int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ struct ieee80211_rx_status stats;
+ const struct rx_status *status;
+ struct sk_buff *skb;
+ int bad_frame = 0;
+ __le16 fc;
+ int need_padding;
+ int i;
+ u8 rate;
+
+ if (length < ZD_PLCP_HEADER_SIZE + 10 /* IEEE80211_1ADDR_LEN */ +
+ FCS_LEN + sizeof(struct rx_status))
+ return -EINVAL;
+
+ memset(&stats, 0, sizeof(stats));
+
+ /* Note about pass_failed_fcs and pass_ctrl access below:
+ * mac locking intentionally omitted here, as this is the only unlocked
+ * reader and the only writer is configure_filter. Plus, if there were
+ * any races accessing these variables, it wouldn't really matter.
+ * If mac80211 ever provides a way for us to access filter flags
+ * from outside configure_filter, we could improve on this. Also, this
+ * situation may change once we implement some kind of DMA-into-skb
+ * RX path. */
+
+ /* Caller has to ensure that length >= sizeof(struct rx_status). */
+ status = (struct rx_status *)
+ (buffer + (length - sizeof(struct rx_status)));
+ if (status->frame_status & ZD_RX_ERROR) {
+ if (mac->pass_failed_fcs &&
+ (status->frame_status & ZD_RX_CRC32_ERROR)) {
+ stats.flag |= RX_FLAG_FAILED_FCS_CRC;
+ bad_frame = 1;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ stats.freq = zd_channels[_zd_chip_get_channel(&mac->chip) - 1].center_freq;
+ stats.band = NL80211_BAND_2GHZ;
+ stats.signal = zd_check_signal(hw, status->signal_strength);
+
+ rate = zd_rx_rate(buffer, status);
+
+ /* todo: return index in the big switches in zd_rx_rate instead */
+ for (i = 0; i < mac->band.n_bitrates; i++)
+ if (rate == mac->band.bitrates[i].hw_value)
+ stats.rate_idx = i;
+
+ length -= ZD_PLCP_HEADER_SIZE + sizeof(struct rx_status);
+ buffer += ZD_PLCP_HEADER_SIZE;
+
+ /* Except for bad frames, filter each frame to see if it is an ACK, in
+ * which case our internal TX tracking is updated. Normally we then
+ * bail here as there's no need to pass ACKs on up to the stack, but
+ * there is also the case where the stack has requested us to pass
+ * control frames on up (pass_ctrl) which we must consider. */
+ if (!bad_frame &&
+ filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats)
+ && !mac->pass_ctrl)
+ return 0;
+
+ fc = get_unaligned((__le16*)buffer);
+ need_padding = ieee80211_is_data_qos(fc) ^ ieee80211_has_a4(fc);
+
+ skb = dev_alloc_skb(length + (need_padding ? 2 : 0));
+ if (skb == NULL)
+ return -ENOMEM;
+ if (need_padding) {
+ /* Make sure the payload data is 4 byte aligned. */
+ skb_reserve(skb, 2);
+ }
+
+ /* FIXME : could we avoid this big memcpy ? */
+ skb_put_data(skb, buffer, length);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
+ ieee80211_rx_irqsafe(hw, skb);
+ return 0;
+}
+
+static int zd_op_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+
+ /* using NL80211_IFTYPE_UNSPECIFIED to indicate no mode selected */
+ if (mac->type != NL80211_IFTYPE_UNSPECIFIED)
+ return -EOPNOTSUPP;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
+ mac->type = vif->type;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ mac->vif = vif;
+
+ return set_mac_and_bssid(mac);
+}
+
+static void zd_op_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ mac->type = NL80211_IFTYPE_UNSPECIFIED;
+ mac->vif = NULL;
+ zd_set_beacon_interval(&mac->chip, 0, 0, NL80211_IFTYPE_UNSPECIFIED);
+ zd_write_mac_addr(&mac->chip, NULL);
+
+ zd_mac_free_cur_beacon(mac);
+}
+
+static int zd_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ struct ieee80211_conf *conf = &hw->conf;
+
+ spin_lock_irq(&mac->lock);
+ mac->channel = conf->chandef.chan->hw_value;
+ spin_unlock_irq(&mac->lock);
+
+ return zd_chip_set_channel(&mac->chip, conf->chandef.chan->hw_value);
+}
+
+static void zd_beacon_done(struct zd_mac *mac)
+{
+ struct sk_buff *skb, *beacon;
+
+ if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags))
+ return;
+ if (!mac->vif || mac->vif->type != NL80211_IFTYPE_AP)
+ return;
+
+ /*
+ * Send out buffered broad- and multicast frames.
+ */
+ while (!ieee80211_queue_stopped(mac->hw, 0)) {
+ skb = ieee80211_get_buffered_bc(mac->hw, mac->vif);
+ if (!skb)
+ break;
+ zd_op_tx(mac->hw, NULL, skb);
+ }
+
+ /*
+ * Fetch next beacon so that tim_count is updated.
+ */
+ beacon = ieee80211_beacon_get(mac->hw, mac->vif, 0);
+ if (beacon)
+ zd_mac_config_beacon(mac->hw, beacon, true);
+
+ spin_lock_irq(&mac->lock);
+ mac->beacon.last_update = jiffies;
+ spin_unlock_irq(&mac->lock);
+}
+
+static void zd_process_intr(struct work_struct *work)
+{
+ u16 int_status;
+ unsigned long flags;
+ struct zd_mac *mac = container_of(work, struct zd_mac, process_intr);
+
+ spin_lock_irqsave(&mac->lock, flags);
+ int_status = le16_to_cpu(*(__le16 *)(mac->intr_buffer + 4));
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ if (int_status & INT_CFG_NEXT_BCN) {
+ /*dev_dbg_f_limit(zd_mac_dev(mac), "INT_CFG_NEXT_BCN\n");*/
+ zd_beacon_done(mac);
+ } else {
+ dev_dbg_f(zd_mac_dev(mac), "Unsupported interrupt\n");
+ }
+
+ zd_chip_enable_hwint(&mac->chip);
+}
+
+
+static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ struct zd_mc_hash hash;
+ struct netdev_hw_addr *ha;
+
+ zd_mc_clear(&hash);
+
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+ dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", ha->addr);
+ zd_mc_add_addr(&hash, ha->addr);
+ }
+
+ return hash.low | ((u64)hash.high << 32);
+}
+
+#define SUPPORTED_FIF_FLAGS \
+ (FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \
+ FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC)
+static void zd_op_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *new_flags,
+ u64 multicast)
+{
+ struct zd_mc_hash hash = {
+ .low = multicast,
+ .high = multicast >> 32,
+ };
+ struct zd_mac *mac = zd_hw_mac(hw);
+ unsigned long flags;
+ int r;
+
+ /* Only deal with supported flags */
+ changed_flags &= SUPPORTED_FIF_FLAGS;
+ *new_flags &= SUPPORTED_FIF_FLAGS;
+
+ /*
+ * If multicast parameter (as returned by zd_op_prepare_multicast)
+ * has changed, no bit in changed_flags is set. To handle this
+ * situation, we do not return if changed_flags is 0. If we do so,
+ * we will have some issue with IPv6 which uses multicast for link
+ * layer address resolution.
+ */
+ if (*new_flags & FIF_ALLMULTI)
+ zd_mc_add_all(&hash);
+
+ spin_lock_irqsave(&mac->lock, flags);
+ mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL);
+ mac->pass_ctrl = !!(*new_flags & FIF_CONTROL);
+ mac->multicast_hash = hash;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ zd_chip_set_multicast_hash(&mac->chip, &hash);
+
+ if (changed_flags & FIF_CONTROL) {
+ r = set_rx_filter(mac);
+ if (r)
+ dev_err(zd_mac_dev(mac), "set_rx_filter error %d\n", r);
+ }
+
+ /* no handling required for FIF_OTHER_BSS as we don't currently
+ * do BSSID filtering */
+ /* FIXME: in future it would be nice to enable the probe response
+ * filter (so that the driver doesn't see them) until
+ * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd
+ * have to schedule work to enable prbresp reception, which might
+ * happen too late. For now we'll just listen and forward them all the
+ * time. */
+}
+
+static void set_rts_cts(struct zd_mac *mac, unsigned int short_preamble)
+{
+ mutex_lock(&mac->chip.mutex);
+ zd_chip_set_rts_cts_rate_locked(&mac->chip, short_preamble);
+ mutex_unlock(&mac->chip.mutex);
+}
+
+static void zd_op_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u64 changes)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ int associated;
+
+ dev_dbg_f(zd_mac_dev(mac), "changes: %llx\n", changes);
+
+ if (mac->type == NL80211_IFTYPE_MESH_POINT ||
+ mac->type == NL80211_IFTYPE_ADHOC ||
+ mac->type == NL80211_IFTYPE_AP) {
+ associated = true;
+ if (changes & BSS_CHANGED_BEACON) {
+ struct sk_buff *beacon = ieee80211_beacon_get(hw, vif,
+ 0);
+
+ if (beacon) {
+ zd_chip_disable_hwint(&mac->chip);
+ zd_mac_config_beacon(hw, beacon, false);
+ zd_chip_enable_hwint(&mac->chip);
+ }
+ }
+
+ if (changes & BSS_CHANGED_BEACON_ENABLED) {
+ u16 interval = 0;
+ u8 period = 0;
+
+ if (bss_conf->enable_beacon) {
+ period = bss_conf->dtim_period;
+ interval = bss_conf->beacon_int;
+ }
+
+ spin_lock_irq(&mac->lock);
+ mac->beacon.period = period;
+ mac->beacon.interval = interval;
+ mac->beacon.last_update = jiffies;
+ spin_unlock_irq(&mac->lock);
+
+ zd_set_beacon_interval(&mac->chip, interval, period,
+ mac->type);
+ }
+ } else
+ associated = is_valid_ether_addr(bss_conf->bssid);
+
+ spin_lock_irq(&mac->lock);
+ mac->associated = associated;
+ spin_unlock_irq(&mac->lock);
+
+ /* TODO: do hardware bssid filtering */
+
+ if (changes & BSS_CHANGED_ERP_PREAMBLE) {
+ spin_lock_irq(&mac->lock);
+ mac->short_preamble = bss_conf->use_short_preamble;
+ spin_unlock_irq(&mac->lock);
+
+ set_rts_cts(mac, bss_conf->use_short_preamble);
+ }
+}
+
+static u64 zd_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct zd_mac *mac = zd_hw_mac(hw);
+ return zd_chip_get_tsf(&mac->chip);
+}
+
+static const struct ieee80211_ops zd_ops = {
+ .tx = zd_op_tx,
+ .start = zd_op_start,
+ .stop = zd_op_stop,
+ .add_interface = zd_op_add_interface,
+ .remove_interface = zd_op_remove_interface,
+ .config = zd_op_config,
+ .prepare_multicast = zd_op_prepare_multicast,
+ .configure_filter = zd_op_configure_filter,
+ .bss_info_changed = zd_op_bss_info_changed,
+ .get_tsf = zd_op_get_tsf,
+};
+
+struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
+{
+ struct zd_mac *mac;
+ struct ieee80211_hw *hw;
+
+ hw = ieee80211_alloc_hw(sizeof(struct zd_mac), &zd_ops);
+ if (!hw) {
+ dev_dbg_f(&intf->dev, "out of memory\n");
+ return NULL;
+ }
+
+ mac = zd_hw_mac(hw);
+
+ memset(mac, 0, sizeof(*mac));
+ spin_lock_init(&mac->lock);
+ mac->hw = hw;
+
+ mac->type = NL80211_IFTYPE_UNSPECIFIED;
+
+ memcpy(mac->channels, zd_channels, sizeof(zd_channels));
+ memcpy(mac->rates, zd_rates, sizeof(zd_rates));
+ mac->band.n_bitrates = ARRAY_SIZE(zd_rates);
+ mac->band.bitrates = mac->rates;
+ mac->band.n_channels = ARRAY_SIZE(zd_channels);
+ mac->band.channels = mac->channels;
+
+ hw->wiphy->bands[NL80211_BAND_2GHZ] = &mac->band;
+
+ ieee80211_hw_set(hw, MFP_CAPABLE);
+ ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+ ieee80211_hw_set(hw, RX_INCLUDES_FCS);
+ ieee80211_hw_set(hw, SIGNAL_UNSPEC);
+
+ hw->wiphy->interface_modes =
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+ BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP);
+
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
+
+ hw->max_signal = 100;
+ hw->queues = 1;
+ hw->extra_tx_headroom = sizeof(struct zd_ctrlset);
+
+ /*
+ * Tell mac80211 that we support multi rate retries
+ */
+ hw->max_rates = IEEE80211_TX_MAX_RATES;
+ hw->max_rate_tries = 18; /* 9 rates * 2 retries/rate */
+
+ skb_queue_head_init(&mac->ack_wait_queue);
+ mac->ack_pending = 0;
+
+ zd_chip_init(&mac->chip, hw, intf);
+ housekeeping_init(mac);
+ beacon_init(mac);
+ INIT_WORK(&mac->process_intr, zd_process_intr);
+
+ SET_IEEE80211_DEV(hw, &intf->dev);
+ return hw;
+}
+
+#define BEACON_WATCHDOG_DELAY round_jiffies_relative(HZ)
+
+static void beacon_watchdog_handler(struct work_struct *work)
+{
+ struct zd_mac *mac =
+ container_of(work, struct zd_mac, beacon.watchdog_work.work);
+ struct sk_buff *beacon;
+ unsigned long timeout;
+ int interval, period;
+
+ if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags))
+ goto rearm;
+ if (mac->type != NL80211_IFTYPE_AP || !mac->vif)
+ goto rearm;
+
+ spin_lock_irq(&mac->lock);
+ interval = mac->beacon.interval;
+ period = mac->beacon.period;
+ timeout = mac->beacon.last_update +
+ msecs_to_jiffies(interval * 1024 / 1000) * 3;
+ spin_unlock_irq(&mac->lock);
+
+ if (interval > 0 && time_is_before_jiffies(timeout)) {
+ dev_dbg_f(zd_mac_dev(mac), "beacon interrupt stalled, "
+ "restarting. "
+ "(interval: %d, dtim: %d)\n",
+ interval, period);
+
+ zd_chip_disable_hwint(&mac->chip);
+
+ beacon = ieee80211_beacon_get(mac->hw, mac->vif, 0);
+ if (beacon) {
+ zd_mac_free_cur_beacon(mac);
+
+ zd_mac_config_beacon(mac->hw, beacon, false);
+ }
+
+ zd_set_beacon_interval(&mac->chip, interval, period, mac->type);
+
+ zd_chip_enable_hwint(&mac->chip);
+
+ spin_lock_irq(&mac->lock);
+ mac->beacon.last_update = jiffies;
+ spin_unlock_irq(&mac->lock);
+ }
+
+rearm:
+ queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work,
+ BEACON_WATCHDOG_DELAY);
+}
+
+static void beacon_init(struct zd_mac *mac)
+{
+ INIT_DELAYED_WORK(&mac->beacon.watchdog_work, beacon_watchdog_handler);
+}
+
+static void beacon_enable(struct zd_mac *mac)
+{
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+
+ mac->beacon.last_update = jiffies;
+ queue_delayed_work(zd_workqueue, &mac->beacon.watchdog_work,
+ BEACON_WATCHDOG_DELAY);
+}
+
+static void beacon_disable(struct zd_mac *mac)
+{
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+ cancel_delayed_work_sync(&mac->beacon.watchdog_work);
+
+ zd_mac_free_cur_beacon(mac);
+}
+
+#define LINK_LED_WORK_DELAY HZ
+
+static void link_led_handler(struct work_struct *work)
+{
+ struct zd_mac *mac =
+ container_of(work, struct zd_mac, housekeeping.link_led_work.work);
+ struct zd_chip *chip = &mac->chip;
+ int is_associated;
+ int r;
+
+ if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags))
+ goto requeue;
+
+ spin_lock_irq(&mac->lock);
+ is_associated = mac->associated;
+ spin_unlock_irq(&mac->lock);
+
+ r = zd_chip_control_leds(chip,
+ is_associated ? ZD_LED_ASSOCIATED : ZD_LED_SCANNING);
+ if (r)
+ dev_dbg_f(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r);
+
+requeue:
+ queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
+ LINK_LED_WORK_DELAY);
+}
+
+static void housekeeping_init(struct zd_mac *mac)
+{
+ INIT_DELAYED_WORK(&mac->housekeeping.link_led_work, link_led_handler);
+}
+
+static void housekeeping_enable(struct zd_mac *mac)
+{
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+ queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
+ 0);
+}
+
+static void housekeeping_disable(struct zd_mac *mac)
+{
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+ cancel_delayed_work_sync(&mac->housekeeping.link_led_work);
+ zd_chip_control_leds(&mac->chip, ZD_LED_OFF);
+}
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.h b/drivers/net/wireless/zydas/zd1211rw/zd_mac.h
new file mode 100644
index 000000000..5ff84bdc5
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.h
@@ -0,0 +1,315 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+#ifndef _ZD_MAC_H
+#define _ZD_MAC_H
+
+#include <linux/kernel.h>
+#include <net/mac80211.h>
+
+#include "zd_chip.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;
+} __packed;
+
+#define ZD_CS_RESERVED_SIZE 25
+
+/* The field modulation of struct zd_ctrlset controls the bit rate, the use
+ * of short or long preambles in 802.11b (CCK mode) or the use of 802.11a or
+ * 802.11g in OFDM mode.
+ *
+ * The term zd-rate is used for the combination of the modulation type flag
+ * and the "pure" rate value.
+ */
+#define ZD_PURE_RATE_MASK 0x0f
+#define ZD_MODULATION_TYPE_MASK 0x10
+#define ZD_RATE_MASK (ZD_PURE_RATE_MASK|ZD_MODULATION_TYPE_MASK)
+#define ZD_PURE_RATE(modulation) ((modulation) & ZD_PURE_RATE_MASK)
+#define ZD_MODULATION_TYPE(modulation) ((modulation) & ZD_MODULATION_TYPE_MASK)
+#define ZD_RATE(modulation) ((modulation) & ZD_RATE_MASK)
+
+/* The two possible modulation types. Notify that 802.11b doesn't use the CCK
+ * codeing for the 1 and 2 MBit/s rate. We stay with the term here to remain
+ * consistent with uses the term at other places.
+ */
+#define ZD_CCK 0x00
+#define ZD_OFDM 0x10
+
+/* The ZD1211 firmware uses proprietary encodings of the 802.11b (CCK) rates.
+ * For OFDM the PLCP rate encodings are used. We combine these "pure" rates
+ * with the modulation type flag and call the resulting values zd-rates.
+ */
+#define ZD_CCK_RATE_1M (ZD_CCK|0x00)
+#define ZD_CCK_RATE_2M (ZD_CCK|0x01)
+#define ZD_CCK_RATE_5_5M (ZD_CCK|0x02)
+#define ZD_CCK_RATE_11M (ZD_CCK|0x03)
+#define ZD_OFDM_RATE_6M (ZD_OFDM|ZD_OFDM_PLCP_RATE_6M)
+#define ZD_OFDM_RATE_9M (ZD_OFDM|ZD_OFDM_PLCP_RATE_9M)
+#define ZD_OFDM_RATE_12M (ZD_OFDM|ZD_OFDM_PLCP_RATE_12M)
+#define ZD_OFDM_RATE_18M (ZD_OFDM|ZD_OFDM_PLCP_RATE_18M)
+#define ZD_OFDM_RATE_24M (ZD_OFDM|ZD_OFDM_PLCP_RATE_24M)
+#define ZD_OFDM_RATE_36M (ZD_OFDM|ZD_OFDM_PLCP_RATE_36M)
+#define ZD_OFDM_RATE_48M (ZD_OFDM|ZD_OFDM_PLCP_RATE_48M)
+#define ZD_OFDM_RATE_54M (ZD_OFDM|ZD_OFDM_PLCP_RATE_54M)
+
+/* The bit 5 of the zd_ctrlset modulation field controls the preamble in CCK
+ * mode or the 802.11a/802.11g selection in OFDM mode.
+ */
+#define ZD_CCK_PREA_LONG 0x00
+#define ZD_CCK_PREA_SHORT 0x20
+#define ZD_OFDM_MODE_11G 0x00
+#define ZD_OFDM_MODE_11A 0x20
+
+/* zd_ctrlset control field */
+#define ZD_CS_NEED_RANDOM_BACKOFF 0x01
+#define ZD_CS_NO_ACK 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;
+} __packed;
+
+#define RX_LENGTH_INFO_TAG 0x697e
+
+struct rx_status {
+ u8 signal_quality_cck;
+ /* rssi */
+ u8 signal_strength;
+ u8 signal_quality_ofdm;
+ u8 decryption_type;
+ u8 frame_status;
+} __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
+
+struct tx_retry_rate {
+ int count; /* number of valid element in rate[] array */
+ int rate[10]; /* retry rates, described by an index in zd_rates[] */
+};
+
+struct tx_status {
+ u8 type; /* must always be 0x01 : USB_INT_TYPE */
+ u8 id; /* must always be 0xa0 : USB_INT_ID_RETRY_FAILED */
+ u8 rate;
+ u8 pad;
+ u8 mac[ETH_ALEN];
+ u8 retry;
+ u8 failure;
+} __packed;
+
+enum mac_flags {
+ MAC_FIXED_CHANNEL = 0x01,
+};
+
+struct housekeeping {
+ struct delayed_work link_led_work;
+};
+
+struct beacon {
+ struct delayed_work watchdog_work;
+ struct sk_buff *cur_beacon;
+ unsigned long last_update;
+ u16 interval;
+ u8 period;
+};
+
+enum zd_device_flags {
+ ZD_DEVICE_RUNNING,
+};
+
+#define ZD_MAC_STATS_BUFFER_SIZE 16
+
+#define ZD_MAC_MAX_ACK_WAITERS 50
+
+struct zd_mac {
+ struct zd_chip chip;
+ spinlock_t lock;
+ spinlock_t intr_lock;
+ struct ieee80211_hw *hw;
+ struct ieee80211_vif *vif;
+ struct housekeeping housekeeping;
+ struct beacon beacon;
+ struct work_struct set_rts_cts_work;
+ struct work_struct process_intr;
+ struct zd_mc_hash multicast_hash;
+ u8 intr_buffer[USB_MAX_EP_INT_BUFFER];
+ u8 regdomain;
+ u8 default_regdomain;
+ u8 channel;
+ int type;
+ int associated;
+ unsigned long flags;
+ struct sk_buff_head ack_wait_queue;
+ struct ieee80211_channel channels[14];
+ struct ieee80211_rate rates[12];
+ struct ieee80211_supported_band band;
+
+ /* Short preamble (used for RTS/CTS) */
+ unsigned int short_preamble:1;
+
+ /* whether to pass frames with CRC errors to stack */
+ unsigned int pass_failed_fcs:1;
+
+ /* whether to pass control frames to stack */
+ unsigned int pass_ctrl:1;
+
+ /* whether we have received a 802.11 ACK that is pending */
+ unsigned int ack_pending:1;
+
+ /* signal strength of the last 802.11 ACK received */
+ int ack_signal;
+};
+
+#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_2 0x40
+#define ZD_REGDOMAIN_JAPAN 0x41
+#define ZD_REGDOMAIN_JAPAN_3 0x49
+
+enum {
+ MIN_CHANNEL24 = 1,
+ MAX_CHANNEL24 = 14,
+};
+
+#define ZD_PLCP_SERVICE_LENGTH_EXTENSION 0x80
+
+struct ofdm_plcp_header {
+ u8 prefix[3];
+ __le16 service;
+} __packed;
+
+static inline u8 zd_ofdm_plcp_header_rate(const struct ofdm_plcp_header *header)
+{
+ return header->prefix[0] & 0xf;
+}
+
+/* The following defines give the encoding of the 4-bit rate field in the
+ * OFDM (802.11a/802.11g) PLCP header. Notify that these values are used to
+ * define the zd-rate values for OFDM.
+ *
+ * See the struct zd_ctrlset definition in zd_mac.h.
+ */
+#define ZD_OFDM_PLCP_RATE_6M 0xb
+#define ZD_OFDM_PLCP_RATE_9M 0xf
+#define ZD_OFDM_PLCP_RATE_12M 0xa
+#define ZD_OFDM_PLCP_RATE_18M 0xe
+#define ZD_OFDM_PLCP_RATE_24M 0x9
+#define ZD_OFDM_PLCP_RATE_36M 0xd
+#define ZD_OFDM_PLCP_RATE_48M 0x8
+#define ZD_OFDM_PLCP_RATE_54M 0xc
+
+struct cck_plcp_header {
+ u8 signal;
+ u8 service;
+ __le16 length;
+ __le16 crc16;
+} __packed;
+
+static inline u8 zd_cck_plcp_header_signal(const struct cck_plcp_header *header)
+{
+ return header->signal;
+}
+
+/* These defines give the encodings of the signal field in the 802.11b PLCP
+ * header. The signal field gives the bit rate of the following packet. Even
+ * if technically wrong we use CCK here also for the 1 MBit/s and 2 MBit/s
+ * rate to stay consistent with Zydas and our use of the term.
+ *
+ * Notify that these values are *not* used in the zd-rates.
+ */
+#define ZD_CCK_PLCP_SIGNAL_1M 0x0a
+#define ZD_CCK_PLCP_SIGNAL_2M 0x14
+#define ZD_CCK_PLCP_SIGNAL_5M5 0x37
+#define ZD_CCK_PLCP_SIGNAL_11M 0x6e
+
+static inline struct zd_mac *zd_hw_mac(struct ieee80211_hw *hw)
+{
+ return hw->priv;
+}
+
+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));
+}
+
+static inline u8 *zd_mac_get_perm_addr(struct zd_mac *mac)
+{
+ return mac->hw->wiphy->perm_addr;
+}
+
+#define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip))
+
+struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf);
+void zd_mac_clear(struct zd_mac *mac);
+
+int zd_mac_preinit_hw(struct ieee80211_hw *hw);
+int zd_mac_init_hw(struct ieee80211_hw *hw);
+
+int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length);
+void zd_mac_tx_failed(struct urb *urb);
+void zd_mac_tx_to_dev(struct sk_buff *skb, int error);
+
+int zd_op_start(struct ieee80211_hw *hw);
+void zd_op_stop(struct ieee80211_hw *hw);
+int zd_restore_settings(struct zd_mac *mac);
+
+#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 */
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf.c
new file mode 100644
index 000000000..d356ae330
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include "zd_def.h"
+#include "zd_rf.h"
+#include "zd_mac.h"
+#include "zd_chip.h"
+
+static const char * const 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));
+
+ /* default to update channel integration, as almost all RF's do want
+ * this */
+ rf->update_channel_int = 1;
+}
+
+void zd_rf_clear(struct zd_rf *rf)
+{
+ if (rf->clear)
+ rf->clear(rf);
+ ZD_MEMCLEAR(rf, sizeof(*rf));
+}
+
+int zd_rf_init_hw(struct zd_rf *rf, u8 type)
+{
+ int r = 0;
+ int 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);
+ break;
+ case AL2230_RF:
+ case AL2230S_RF:
+ r = zd_rf_init_al2230(rf);
+ break;
+ case AL7230B_RF:
+ r = zd_rf_init_al7230b(rf);
+ break;
+ case MAXIM_NEW_RF:
+ case UW2453_RF:
+ r = zd_rf_init_uw2453(rf);
+ 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;
+ }
+
+ if (r)
+ return r;
+
+ 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;
+}
+
+int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel)
+{
+ if (!rf->patch_6m_band_edge)
+ return 0;
+
+ return rf->patch_6m_band_edge(rf, channel);
+}
+
+int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel)
+{
+ return zd_chip_generic_patch_6m_band(zd_rf_to_chip(rf), channel);
+}
+
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf.h b/drivers/net/wireless/zydas/zd1211rw/zd_rf.h
new file mode 100644
index 000000000..8bfec9e75
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+#ifndef _ZD_RF_H
+#define _ZD_RF_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 channel integration and calibration should be updated
+ * defaults to 1 (yes) */
+ u8 update_channel_int:1;
+
+ /* whether ZD_CR47 should be patched from the EEPROM, if the appropriate
+ * flag is set in the POD. The vendor driver suggests that this should
+ * be done for all RF's, but a bug in their code prevents but their
+ * HW_OverWritePhyRegFromE2P() routine from ever taking effect. */
+ u8 patch_cck_gain:1;
+
+ /* private RF driver data */
+ void *priv;
+
+ /* 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);
+ int (*patch_6m_band_edge)(struct zd_rf *rf, u8 channel);
+ void (*clear)(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);
+
+int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel);
+int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel);
+
+static inline int zd_rf_should_update_pwr_int(struct zd_rf *rf)
+{
+ return rf->update_channel_int;
+}
+
+static inline int zd_rf_should_patch_cck_gain(struct zd_rf *rf)
+{
+ return rf->patch_cck_gain;
+}
+
+int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel);
+int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel);
+
+/* Functions for individual RF chips */
+
+int zd_rf_init_rf2959(struct zd_rf *rf);
+int zd_rf_init_al2230(struct zd_rf *rf);
+int zd_rf_init_al7230b(struct zd_rf *rf);
+int zd_rf_init_uw2453(struct zd_rf *rf);
+
+#endif /* _ZD_RF_H */
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c
new file mode 100644
index 000000000..23ee5571e
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al2230.c
@@ -0,0 +1,431 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+#include <linux/kernel.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+#include "zd_chip.h"
+
+#define IS_AL2230S(chip) ((chip)->al2230s_bit || (chip)->rf.type == AL2230S_RF)
+
+static const u32 zd1211_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 const u32 zd1211b_al2230_table[][3] = {
+ RF_CHANNEL( 1) = { 0x09efc0, 0x8cccc0, 0xb00000, },
+ RF_CHANNEL( 2) = { 0x09efc0, 0x8cccd0, 0xb00000, },
+ RF_CHANNEL( 3) = { 0x09e7c0, 0x8cccc0, 0xb00000, },
+ RF_CHANNEL( 4) = { 0x09e7c0, 0x8cccd0, 0xb00000, },
+ RF_CHANNEL( 5) = { 0x05efc0, 0x8cccc0, 0xb00000, },
+ RF_CHANNEL( 6) = { 0x05efc0, 0x8cccd0, 0xb00000, },
+ RF_CHANNEL( 7) = { 0x05e7c0, 0x8cccc0, 0xb00000, },
+ RF_CHANNEL( 8) = { 0x05e7c0, 0x8cccd0, 0xb00000, },
+ RF_CHANNEL( 9) = { 0x0defc0, 0x8cccc0, 0xb00000, },
+ RF_CHANNEL(10) = { 0x0defc0, 0x8cccd0, 0xb00000, },
+ RF_CHANNEL(11) = { 0x0de7c0, 0x8cccc0, 0xb00000, },
+ RF_CHANNEL(12) = { 0x0de7c0, 0x8cccd0, 0xb00000, },
+ RF_CHANNEL(13) = { 0x03efc0, 0x8cccc0, 0xb00000, },
+ RF_CHANNEL(14) = { 0x03e7c0, 0x866660, 0xb00000, },
+};
+
+static const struct zd_ioreq16 zd1211b_ioreqs_shared_1[] = {
+ { ZD_CR240, 0x57 }, { ZD_CR9, 0xe0 },
+};
+
+static const struct zd_ioreq16 ioreqs_init_al2230s[] = {
+ { ZD_CR47, 0x1e }, /* MARK_002 */
+ { ZD_CR106, 0x22 },
+ { ZD_CR107, 0x2a }, /* MARK_002 */
+ { ZD_CR109, 0x13 }, /* MARK_002 */
+ { ZD_CR118, 0xf8 }, /* MARK_002 */
+ { ZD_CR119, 0x12 }, { ZD_CR122, 0xe0 },
+ { ZD_CR128, 0x10 }, /* MARK_001 from 0xe->0x10 */
+ { ZD_CR129, 0x0e }, /* MARK_001 from 0xd->0x0e */
+ { ZD_CR130, 0x10 }, /* MARK_001 from 0xb->0x0d */
+};
+
+static int zd1211b_al2230_finalize_rf(struct zd_chip *chip)
+{
+ int r;
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 },
+ { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 },
+ { ZD_CR203, 0x06 },
+ { },
+
+ { ZD_CR240, 0x80 },
+ };
+
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ if (r)
+ return r;
+
+ /* related to antenna selection? */
+ if (chip->new_phy_layout) {
+ r = zd_iowrite16_locked(chip, 0xe1, ZD_CR9);
+ if (r)
+ return r;
+ }
+
+ return zd_iowrite16_locked(chip, 0x06, ZD_CR203);
+}
+
+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_init[] = {
+ { ZD_CR15, 0x20 }, { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 },
+ { ZD_CR26, 0x11 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 },
+ { ZD_CR44, 0x33 }, { ZD_CR106, 0x2a }, { ZD_CR107, 0x1a },
+ { ZD_CR109, 0x09 }, { ZD_CR110, 0x27 }, { ZD_CR111, 0x2b },
+ { ZD_CR112, 0x2b }, { ZD_CR119, 0x0a }, { ZD_CR10, 0x89 },
+ /* for newest (3rd cut) AL2300 */
+ { ZD_CR17, 0x28 },
+ { ZD_CR26, 0x93 }, { ZD_CR34, 0x30 },
+ /* for newest (3rd cut) AL2300 */
+ { ZD_CR35, 0x3e },
+ { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 },
+ /* for newest (3rd cut) AL2300 */
+ { ZD_CR46, 0x96 },
+ { ZD_CR47, 0x1e }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 },
+ { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 },
+ { ZD_CR92, 0x0a }, { ZD_CR99, 0x28 }, { ZD_CR100, 0x00 },
+ { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR106, 0x24 },
+ { ZD_CR107, 0x2a }, { ZD_CR109, 0x09 }, { ZD_CR110, 0x13 },
+ { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 },
+ { ZD_CR114, 0x27 },
+ /* for newest (3rd cut) AL2300 */
+ { ZD_CR115, 0x24 },
+ { ZD_CR116, 0x24 }, { ZD_CR117, 0xf4 }, { ZD_CR118, 0xfc },
+ { ZD_CR119, 0x10 }, { ZD_CR120, 0x4f }, { ZD_CR121, 0x77 },
+ { ZD_CR122, 0xe0 }, { ZD_CR137, 0x88 }, { ZD_CR252, 0xff },
+ { ZD_CR253, 0xff },
+ };
+
+ static const struct zd_ioreq16 ioreqs_pll[] = {
+ /* shdnb(PLL_ON)=0 */
+ { ZD_CR251, 0x2f },
+ /* shdnb(PLL_ON)=1 */
+ { ZD_CR251, 0x3f },
+ { ZD_CR138, 0x28 }, { ZD_CR203, 0x06 },
+ };
+
+ static const u32 rv1[] = {
+ /* Channel 1 */
+ 0x03f790,
+ 0x033331,
+ 0x00000d,
+
+ 0x0b3331,
+ 0x03b812,
+ 0x00fff3,
+ };
+
+ static const u32 rv2[] = {
+ 0x000da4,
+ 0x0f4dc5, /* fix freq shift, 0x04edc5 */
+ 0x0805b6,
+ 0x011687,
+ 0x000688,
+ 0x0403b9, /* external control TX power (ZD_CR31) */
+ 0x00dbba,
+ 0x00099b,
+ 0x0bdffc,
+ 0x00000d,
+ 0x00500f,
+ };
+
+ static const u32 rv3[] = {
+ 0x00d00f,
+ 0x004c0f,
+ 0x00540f,
+ 0x00700f,
+ 0x00500f,
+ };
+
+ r = zd_iowrite16a_locked(chip, ioreqs_init, ARRAY_SIZE(ioreqs_init));
+ if (r)
+ return r;
+
+ if (IS_AL2230S(chip)) {
+ r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s,
+ ARRAY_SIZE(ioreqs_init_al2230s));
+ if (r)
+ return r;
+ }
+
+ r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS);
+ if (r)
+ return r;
+
+ /* improve band edge for AL2230S */
+ if (IS_AL2230S(chip))
+ r = zd_rfwrite_locked(chip, 0x000824, RF_RV_BITS);
+ else
+ r = zd_rfwrite_locked(chip, 0x0005a4, RF_RV_BITS);
+ 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, ioreqs_pll, ARRAY_SIZE(ioreqs_pll));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), 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[] = {
+ { ZD_CR10, 0x89 }, { ZD_CR15, 0x20 },
+ { ZD_CR17, 0x2B }, /* for newest(3rd cut) AL2230 */
+ { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 },
+ { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 },
+ { ZD_CR33, 0x28 }, /* 5621 */
+ { ZD_CR34, 0x30 },
+ { ZD_CR35, 0x3e }, /* for newest(3rd cut) AL2230 */
+ { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 },
+ { ZD_CR46, 0x99 }, /* for newest(3rd cut) AL2230 */
+ { ZD_CR47, 0x1e },
+
+ /* ZD1211B 05.06.10 */
+ { ZD_CR48, 0x06 }, { ZD_CR49, 0xf9 }, { ZD_CR51, 0x01 },
+ { ZD_CR52, 0x80 }, { ZD_CR53, 0x7e }, { ZD_CR65, 0x00 },
+ { ZD_CR66, 0x00 }, { ZD_CR67, 0x00 }, { ZD_CR68, 0x00 },
+ { ZD_CR69, 0x28 },
+
+ { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 },
+ { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 },
+ { ZD_CR91, 0x00 }, /* 5621 */
+ { ZD_CR92, 0x0a },
+ { ZD_CR98, 0x8d }, /* 4804, for 1212 new algorithm */
+ { ZD_CR99, 0x00 }, /* 5621 */
+ { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 },
+ { ZD_CR106, 0x24 }, /* for newest(3rd cut) AL2230 */
+ { ZD_CR107, 0x2a },
+ { ZD_CR109, 0x13 }, /* 4804, for 1212 new algorithm */
+ { ZD_CR110, 0x1f }, /* 4804, for 1212 new algorithm */
+ { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 },
+ { ZD_CR114, 0x27 },
+ { ZD_CR115, 0x26 }, /* 24->26 at 4902 for newest(3rd cut)
+ * AL2230
+ */
+ { ZD_CR116, 0x24 },
+ { ZD_CR117, 0xfa }, /* for 1211b */
+ { ZD_CR118, 0xfa }, /* for 1211b */
+ { ZD_CR119, 0x10 },
+ { ZD_CR120, 0x4f },
+ { ZD_CR121, 0x6c }, /* for 1211b */
+ { ZD_CR122, 0xfc }, /* E0->FC at 4902 */
+ { ZD_CR123, 0x57 }, /* 5623 */
+ { ZD_CR125, 0xad }, /* 4804, for 1212 new algorithm */
+ { ZD_CR126, 0x6c }, /* 5614 */
+ { ZD_CR127, 0x03 }, /* 4804, for 1212 new algorithm */
+ { ZD_CR137, 0x50 }, /* 5614 */
+ { ZD_CR138, 0xa8 },
+ { ZD_CR144, 0xac }, /* 5621 */
+ { ZD_CR150, 0x0d }, { ZD_CR252, 0x34 }, { ZD_CR253, 0x34 },
+ };
+
+ static const u32 rv1[] = {
+ 0x8cccd0,
+ 0x481dc0,
+ 0xcfff00,
+ 0x25a000,
+ };
+
+ static const u32 rv2[] = {
+ /* To improve AL2230 yield, improve phase noise, 4713 */
+ 0x25a000,
+ 0xa3b2f0,
+
+ 0x6da010, /* Reg6 update for MP versio */
+ 0xe36280, /* Modified by jxiao for Bor-Chin on 2004/08/02 */
+ 0x116000,
+ 0x9dc020, /* External control TX power (ZD_CR31) */
+ 0x5ddb00, /* RegA update for MP version */
+ 0xd99000, /* RegB update for MP version */
+ 0x3ffbd0, /* RegC update for MP version */
+ 0xb00000, /* RegD update for MP version */
+
+ /* improve phase noise and remove phase calibration,4713 */
+ 0xf01a00,
+ };
+
+ static const struct zd_ioreq16 ioreqs2[] = {
+ { ZD_CR251, 0x2f }, /* shdnb(PLL_ON)=0 */
+ { ZD_CR251, 0x7f }, /* shdnb(PLL_ON)=1 */
+ };
+
+ static const u32 rv3[] = {
+ /* To improve AL2230 yield, 4713 */
+ 0xf01b00,
+ 0xf01e00,
+ 0xf01a00,
+ };
+
+ static const struct zd_ioreq16 ioreqs3[] = {
+ /* related to 6M band edge patching, happens unconditionally */
+ { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 },
+ };
+
+ r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1,
+ ARRAY_SIZE(zd1211b_ioreqs_shared_1));
+ if (r)
+ return r;
+ r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1));
+ if (r)
+ return r;
+
+ if (IS_AL2230S(chip)) {
+ r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s,
+ ARRAY_SIZE(ioreqs_init_al2230s));
+ if (r)
+ return r;
+ }
+
+ r = zd_rfwritev_cr_locked(chip, zd1211b_al2230_table[0], 3);
+ if (r)
+ return r;
+ r = zd_rfwritev_cr_locked(chip, rv1, ARRAY_SIZE(rv1));
+ if (r)
+ return r;
+
+ if (IS_AL2230S(chip))
+ r = zd_rfwrite_locked(chip, 0x241000, RF_RV_BITS);
+ else
+ r = zd_rfwrite_locked(chip, 0x25a000, RF_RV_BITS);
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2));
+ if (r)
+ return r;
+ r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2));
+ if (r)
+ return r;
+ r = zd_rfwritev_cr_locked(chip, rv3, ARRAY_SIZE(rv3));
+ if (r)
+ return r;
+ r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3));
+ if (r)
+ return r;
+ return zd1211b_al2230_finalize_rf(chip);
+}
+
+static int zd1211_al2230_set_channel(struct zd_rf *rf, u8 channel)
+{
+ int r;
+ const u32 *rv = zd1211_al2230_table[channel-1];
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR138, 0x28 },
+ { ZD_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 zd1211b_al2230_set_channel(struct zd_rf *rf, u8 channel)
+{
+ int r;
+ const u32 *rv = zd1211b_al2230_table[channel-1];
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1,
+ ARRAY_SIZE(zd1211b_ioreqs_shared_1));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, rv, 3);
+ if (r)
+ return r;
+
+ return zd1211b_al2230_finalize_rf(chip);
+}
+
+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[] = {
+ { ZD_CR11, 0x00 },
+ { ZD_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[] = {
+ { ZD_CR11, 0x00 },
+ { ZD_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[] = {
+ { ZD_CR11, 0x04 },
+ { ZD_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->switch_radio_off = al2230_switch_radio_off;
+ if (zd_chip_is_zd1211b(chip)) {
+ rf->init_hw = zd1211b_al2230_init_hw;
+ rf->set_channel = zd1211b_al2230_set_channel;
+ rf->switch_radio_on = zd1211b_al2230_switch_radio_on;
+ } else {
+ rf->init_hw = zd1211_al2230_init_hw;
+ rf->set_channel = zd1211_al2230_set_channel;
+ rf->switch_radio_on = zd1211_al2230_switch_radio_on;
+ }
+ rf->patch_6m_band_edge = zd_rf_generic_patch_6m;
+ rf->patch_cck_gain = 1;
+ return 0;
+}
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c
new file mode 100644
index 000000000..356783483
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_al7230b.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+#include <linux/kernel.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+#include "zd_chip.h"
+
+static const u32 chan_rv[][2] = {
+ RF_CHANNEL( 1) = { 0x09ec00, 0x8cccc8 },
+ RF_CHANNEL( 2) = { 0x09ec00, 0x8cccd8 },
+ RF_CHANNEL( 3) = { 0x09ec00, 0x8cccc0 },
+ RF_CHANNEL( 4) = { 0x09ec00, 0x8cccd0 },
+ RF_CHANNEL( 5) = { 0x05ec00, 0x8cccc8 },
+ RF_CHANNEL( 6) = { 0x05ec00, 0x8cccd8 },
+ RF_CHANNEL( 7) = { 0x05ec00, 0x8cccc0 },
+ RF_CHANNEL( 8) = { 0x05ec00, 0x8cccd0 },
+ RF_CHANNEL( 9) = { 0x0dec00, 0x8cccc8 },
+ RF_CHANNEL(10) = { 0x0dec00, 0x8cccd8 },
+ RF_CHANNEL(11) = { 0x0dec00, 0x8cccc0 },
+ RF_CHANNEL(12) = { 0x0dec00, 0x8cccd0 },
+ RF_CHANNEL(13) = { 0x03ec00, 0x8cccc8 },
+ RF_CHANNEL(14) = { 0x03ec00, 0x866660 },
+};
+
+static const u32 std_rv[] = {
+ 0x4ff821,
+ 0xc5fbfc,
+ 0x21ebfe,
+ 0xafd401, /* freq shift 0xaad401 */
+ 0x6cf56a,
+ 0xe04073,
+ 0x193d76,
+ 0x9dd844,
+ 0x500007,
+ 0xd8c010,
+};
+
+static const u32 rv_init1[] = {
+ 0x3c9000,
+ 0xbfffff,
+ 0x700000,
+ 0xf15d58,
+};
+
+static const u32 rv_init2[] = {
+ 0xf15d59,
+ 0xf15d5c,
+ 0xf15d58,
+};
+
+static const struct zd_ioreq16 ioreqs_sw[] = {
+ { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 },
+ { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf },
+};
+
+static int zd1211b_al7230b_finalize(struct zd_chip *chip)
+{
+ int r;
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 },
+ { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 },
+ { ZD_CR203, 0x04 },
+ { },
+ { ZD_CR240, 0x80 },
+ };
+
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ if (r)
+ return r;
+
+ if (chip->new_phy_layout) {
+ /* antenna selection? */
+ r = zd_iowrite16_locked(chip, 0xe5, ZD_CR9);
+ if (r)
+ return r;
+ }
+
+ return zd_iowrite16_locked(chip, 0x04, ZD_CR203);
+}
+
+static int zd1211_al7230b_init_hw(struct zd_rf *rf)
+{
+ int r;
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ /* All of these writes are identical to AL2230 unless otherwise
+ * specified */
+ static const struct zd_ioreq16 ioreqs_1[] = {
+ /* This one is 7230-specific, and happens before the rest */
+ { ZD_CR240, 0x57 },
+ { },
+
+ { ZD_CR15, 0x20 }, { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 },
+ { ZD_CR26, 0x11 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 },
+ { ZD_CR44, 0x33 },
+ /* This value is different for 7230 (was: 0x2a) */
+ { ZD_CR106, 0x22 },
+ { ZD_CR107, 0x1a }, { ZD_CR109, 0x09 }, { ZD_CR110, 0x27 },
+ { ZD_CR111, 0x2b }, { ZD_CR112, 0x2b }, { ZD_CR119, 0x0a },
+ /* This happened further down in AL2230,
+ * and the value changed (was: 0xe0) */
+ { ZD_CR122, 0xfc },
+ { ZD_CR10, 0x89 },
+ /* for newest (3rd cut) AL2300 */
+ { ZD_CR17, 0x28 },
+ { ZD_CR26, 0x93 }, { ZD_CR34, 0x30 },
+ /* for newest (3rd cut) AL2300 */
+ { ZD_CR35, 0x3e },
+ { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 },
+ /* for newest (3rd cut) AL2300 */
+ { ZD_CR46, 0x96 },
+ { ZD_CR47, 0x1e }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 },
+ { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 },
+ { ZD_CR92, 0x0a }, { ZD_CR99, 0x28 },
+ /* This value is different for 7230 (was: 0x00) */
+ { ZD_CR100, 0x02 },
+ { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 },
+ /* This value is different for 7230 (was: 0x24) */
+ { ZD_CR106, 0x22 },
+ /* This value is different for 7230 (was: 0x2a) */
+ { ZD_CR107, 0x3f },
+ { ZD_CR109, 0x09 },
+ /* This value is different for 7230 (was: 0x13) */
+ { ZD_CR110, 0x1f },
+ { ZD_CR111, 0x1f }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 },
+ { ZD_CR114, 0x27 },
+ /* for newest (3rd cut) AL2300 */
+ { ZD_CR115, 0x24 },
+ /* This value is different for 7230 (was: 0x24) */
+ { ZD_CR116, 0x3f },
+ /* This value is different for 7230 (was: 0xf4) */
+ { ZD_CR117, 0xfa },
+ { ZD_CR118, 0xfc }, { ZD_CR119, 0x10 }, { ZD_CR120, 0x4f },
+ { ZD_CR121, 0x77 }, { ZD_CR137, 0x88 },
+ /* This one is 7230-specific */
+ { ZD_CR138, 0xa8 },
+ /* This value is different for 7230 (was: 0xff) */
+ { ZD_CR252, 0x34 },
+ /* This value is different for 7230 (was: 0xff) */
+ { ZD_CR253, 0x34 },
+
+ /* PLL_OFF */
+ { ZD_CR251, 0x2f },
+ };
+
+ static const struct zd_ioreq16 ioreqs_2[] = {
+ { ZD_CR251, 0x3f }, /* PLL_ON */
+ { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 },
+ { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf },
+ };
+
+ r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0]));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1));
+ if (r)
+ return r;
+
+ r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2));
+ if (r)
+ return r;
+
+ r = zd_iowrite16_locked(chip, 0x06, ZD_CR203);
+ if (r)
+ return r;
+ r = zd_iowrite16_locked(chip, 0x80, ZD_CR240);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int zd1211b_al7230b_init_hw(struct zd_rf *rf)
+{
+ int r;
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ static const struct zd_ioreq16 ioreqs_1[] = {
+ { ZD_CR240, 0x57 }, { ZD_CR9, 0x9 },
+ { },
+ { ZD_CR10, 0x8b }, { ZD_CR15, 0x20 },
+ { ZD_CR17, 0x2B }, /* for newest (3rd cut) AL2230 */
+ { ZD_CR20, 0x10 }, /* 4N25->Stone Request */
+ { ZD_CR23, 0x40 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 },
+ { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 },
+ { ZD_CR33, 0x28 }, /* 5613 */
+ { ZD_CR34, 0x30 },
+ { ZD_CR35, 0x3e }, /* for newest (3rd cut) AL2230 */
+ { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 },
+ { ZD_CR46, 0x99 }, /* for newest (3rd cut) AL2230 */
+ { ZD_CR47, 0x1e },
+
+ /* ZD1215 5610 */
+ { ZD_CR48, 0x00 }, { ZD_CR49, 0x00 }, { ZD_CR51, 0x01 },
+ { ZD_CR52, 0x80 }, { ZD_CR53, 0x7e }, { ZD_CR65, 0x00 },
+ { ZD_CR66, 0x00 }, { ZD_CR67, 0x00 }, { ZD_CR68, 0x00 },
+ { ZD_CR69, 0x28 },
+
+ { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 },
+ { ZD_CR87, 0x0A }, { ZD_CR89, 0x04 },
+ { ZD_CR90, 0x58 }, /* 5112 */
+ { ZD_CR91, 0x00 }, /* 5613 */
+ { ZD_CR92, 0x0a },
+ { ZD_CR98, 0x8d }, /* 4804, for 1212 new algorithm */
+ { ZD_CR99, 0x00 }, { ZD_CR100, 0x02 }, { ZD_CR101, 0x13 },
+ { ZD_CR102, 0x27 },
+ { ZD_CR106, 0x20 }, /* change to 0x24 for AL7230B */
+ { ZD_CR109, 0x13 }, /* 4804, for 1212 new algorithm */
+ { ZD_CR112, 0x1f },
+ };
+
+ static const struct zd_ioreq16 ioreqs_new_phy[] = {
+ { ZD_CR107, 0x28 },
+ { ZD_CR110, 0x1f }, /* 5127, 0x13->0x1f */
+ { ZD_CR111, 0x1f }, /* 0x13 to 0x1f for AL7230B */
+ { ZD_CR116, 0x2a }, { ZD_CR118, 0xfa }, { ZD_CR119, 0x12 },
+ { ZD_CR121, 0x6c }, /* 5613 */
+ };
+
+ static const struct zd_ioreq16 ioreqs_old_phy[] = {
+ { ZD_CR107, 0x24 },
+ { ZD_CR110, 0x13 }, /* 5127, 0x13->0x1f */
+ { ZD_CR111, 0x13 }, /* 0x13 to 0x1f for AL7230B */
+ { ZD_CR116, 0x24 }, { ZD_CR118, 0xfc }, { ZD_CR119, 0x11 },
+ { ZD_CR121, 0x6a }, /* 5613 */
+ };
+
+ static const struct zd_ioreq16 ioreqs_2[] = {
+ { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x24 },
+ { ZD_CR117, 0xfa }, { ZD_CR120, 0x4f },
+ { ZD_CR122, 0xfc }, /* E0->FCh at 4901 */
+ { ZD_CR123, 0x57 }, /* 5613 */
+ { ZD_CR125, 0xad }, /* 4804, for 1212 new algorithm */
+ { ZD_CR126, 0x6c }, /* 5613 */
+ { ZD_CR127, 0x03 }, /* 4804, for 1212 new algorithm */
+ { ZD_CR130, 0x10 },
+ { ZD_CR131, 0x00 }, /* 5112 */
+ { ZD_CR137, 0x50 }, /* 5613 */
+ { ZD_CR138, 0xa8 }, /* 5112 */
+ { ZD_CR144, 0xac }, /* 5613 */
+ { ZD_CR148, 0x40 }, /* 5112 */
+ { ZD_CR149, 0x40 }, /* 4O07, 50->40 */
+ { ZD_CR150, 0x1a }, /* 5112, 0C->1A */
+ { ZD_CR252, 0x34 }, { ZD_CR253, 0x34 },
+ { ZD_CR251, 0x2f }, /* PLL_OFF */
+ };
+
+ static const struct zd_ioreq16 ioreqs_3[] = {
+ { ZD_CR251, 0x7f }, /* PLL_ON */
+ { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 }, { ZD_CR130, 0x10 },
+ { ZD_CR38, 0x38 }, { ZD_CR136, 0xdf },
+ };
+
+ r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1));
+ if (r)
+ return r;
+
+ if (chip->new_phy_layout)
+ r = zd_iowrite16a_locked(chip, ioreqs_new_phy,
+ ARRAY_SIZE(ioreqs_new_phy));
+ else
+ r = zd_iowrite16a_locked(chip, ioreqs_old_phy,
+ ARRAY_SIZE(ioreqs_old_phy));
+ if (r)
+ return r;
+
+ r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0]));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1));
+ if (r)
+ return r;
+
+ r = zd_iowrite16a_locked(chip, ioreqs_3, ARRAY_SIZE(ioreqs_3));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2));
+ if (r)
+ return r;
+
+ return zd1211b_al7230b_finalize(chip);
+}
+
+static int zd1211_al7230b_set_channel(struct zd_rf *rf, u8 channel)
+{
+ int r;
+ const u32 *rv = chan_rv[channel-1];
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ static const struct zd_ioreq16 ioreqs[] = {
+ /* PLL_ON */
+ { ZD_CR251, 0x3f },
+ { ZD_CR203, 0x06 }, { ZD_CR240, 0x08 },
+ };
+
+ r = zd_iowrite16_locked(chip, 0x57, ZD_CR240);
+ if (r)
+ return r;
+
+ /* PLL_OFF */
+ r = zd_iowrite16_locked(chip, 0x2f, ZD_CR251);
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+ if (r)
+ return r;
+
+ r = zd_rfwrite_cr_locked(chip, 0x3c9000);
+ if (r)
+ return r;
+ r = zd_rfwrite_cr_locked(chip, 0xf15d58);
+ if (r)
+ return r;
+
+ r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, rv, 2);
+ if (r)
+ return r;
+
+ r = zd_rfwrite_cr_locked(chip, 0x3c9000);
+ if (r)
+ return r;
+
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211b_al7230b_set_channel(struct zd_rf *rf, u8 channel)
+{
+ int r;
+ const u32 *rv = chan_rv[channel-1];
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ r = zd_iowrite16_locked(chip, 0x57, ZD_CR240);
+ if (r)
+ return r;
+ r = zd_iowrite16_locked(chip, 0xe4, ZD_CR9);
+ if (r)
+ return r;
+
+ /* PLL_OFF */
+ r = zd_iowrite16_locked(chip, 0x2f, ZD_CR251);
+ if (r)
+ return r;
+ r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv));
+ if (r)
+ return r;
+
+ r = zd_rfwrite_cr_locked(chip, 0x3c9000);
+ if (r)
+ return r;
+ r = zd_rfwrite_cr_locked(chip, 0xf15d58);
+ if (r)
+ return r;
+
+ r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw));
+ if (r)
+ return r;
+
+ r = zd_rfwritev_cr_locked(chip, rv, 2);
+ if (r)
+ return r;
+
+ r = zd_rfwrite_cr_locked(chip, 0x3c9000);
+ if (r)
+ return r;
+
+ r = zd_iowrite16_locked(chip, 0x7f, ZD_CR251);
+ if (r)
+ return r;
+
+ return zd1211b_al7230b_finalize(chip);
+}
+
+static int zd1211_al7230b_switch_radio_on(struct zd_rf *rf)
+{
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR11, 0x00 },
+ { ZD_CR251, 0x3f },
+ };
+
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int zd1211b_al7230b_switch_radio_on(struct zd_rf *rf)
+{
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR11, 0x00 },
+ { ZD_CR251, 0x7f },
+ };
+
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int al7230b_switch_radio_off(struct zd_rf *rf)
+{
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR11, 0x04 },
+ { ZD_CR251, 0x2f },
+ };
+
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+/* ZD1211B+AL7230B 6m band edge patching differs slightly from other
+ * configurations */
+static int zd1211b_al7230b_patch_6m(struct zd_rf *rf, u8 channel)
+{
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+ struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR128, 0x14 }, { ZD_CR129, 0x12 },
+ };
+
+ /* FIXME: Channel 11 is not the edge for all regulatory domains. */
+ if (channel == 1) {
+ ioreqs[0].value = 0x0e;
+ ioreqs[1].value = 0x10;
+ } else if (channel == 11) {
+ ioreqs[0].value = 0x10;
+ ioreqs[1].value = 0x10;
+ }
+
+ dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel);
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+int zd_rf_init_al7230b(struct zd_rf *rf)
+{
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ if (zd_chip_is_zd1211b(chip)) {
+ rf->init_hw = zd1211b_al7230b_init_hw;
+ rf->switch_radio_on = zd1211b_al7230b_switch_radio_on;
+ rf->set_channel = zd1211b_al7230b_set_channel;
+ rf->patch_6m_band_edge = zd1211b_al7230b_patch_6m;
+ } else {
+ rf->init_hw = zd1211_al7230b_init_hw;
+ rf->switch_radio_on = zd1211_al7230b_switch_radio_on;
+ rf->set_channel = zd1211_al7230b_set_channel;
+ rf->patch_6m_band_edge = zd_rf_generic_patch_6m;
+ rf->patch_cck_gain = 1;
+ }
+
+ rf->switch_radio_off = al7230b_switch_radio_off;
+
+ return 0;
+}
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c
new file mode 100644
index 000000000..e4c1a8a52
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_rf2959.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+#include <linux/kernel.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+#include "zd_chip.h"
+
+static const u32 rf2959_table[][2] = {
+ RF_CHANNEL( 1) = { 0x181979, 0x1e6666 },
+ RF_CHANNEL( 2) = { 0x181989, 0x1e6666 },
+ RF_CHANNEL( 3) = { 0x181999, 0x1e6666 },
+ RF_CHANNEL( 4) = { 0x1819a9, 0x1e6666 },
+ RF_CHANNEL( 5) = { 0x1819b9, 0x1e6666 },
+ RF_CHANNEL( 6) = { 0x1819c9, 0x1e6666 },
+ RF_CHANNEL( 7) = { 0x1819d9, 0x1e6666 },
+ RF_CHANNEL( 8) = { 0x1819e9, 0x1e6666 },
+ RF_CHANNEL( 9) = { 0x1819f9, 0x1e6666 },
+ RF_CHANNEL(10) = { 0x181a09, 0x1e6666 },
+ RF_CHANNEL(11) = { 0x181a19, 0x1e6666 },
+ RF_CHANNEL(12) = { 0x181a29, 0x1e6666 },
+ RF_CHANNEL(13) = { 0x181a39, 0x1e6666 },
+ RF_CHANNEL(14) = { 0x181a60, 0x1c0000 },
+};
+
+#if 0
+static int bits(u32 rw, int from, int to)
+{
+ rw &= ~(0xffffffffU << (to+1));
+ rw >>= from;
+ return rw;
+}
+
+static int bit(u32 rw, int bit)
+{
+ return bits(rw, bit, bit);
+}
+
+static void dump_regwrite(u32 rw)
+{
+ int reg = bits(rw, 18, 22);
+ int rw_flag = bits(rw, 23, 23);
+ PDEBUG("rf2959 %#010x reg %d rw %d", rw, reg, rw_flag);
+
+ switch (reg) {
+ case 0:
+ PDEBUG("reg0 CFG1 ref_sel %d hibernate %d rf_vco_reg_en %d"
+ " if_vco_reg_en %d if_vga_en %d",
+ bits(rw, 14, 15), bit(rw, 3), bit(rw, 2), bit(rw, 1),
+ bit(rw, 0));
+ break;
+ case 1:
+ PDEBUG("reg1 IFPLL1 pll_en1 %d kv_en1 %d vtc_en1 %d lpf1 %d"
+ " cpl1 %d pdp1 %d autocal_en1 %d ld_en1 %d ifloopr %d"
+ " ifloopc %d dac1 %d",
+ bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14),
+ bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10),
+ bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0, 3));
+ break;
+ case 2:
+ PDEBUG("reg2 IFPLL2 n1 %d num1 %d",
+ bits(rw, 6, 17), bits(rw, 0, 5));
+ break;
+ case 3:
+ PDEBUG("reg3 IFPLL3 num %d", bits(rw, 0, 17));
+ break;
+ case 4:
+ PDEBUG("reg4 IFPLL4 dn1 %#04x ct_def1 %d kv_def1 %d",
+ bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3));
+ break;
+ case 5:
+ PDEBUG("reg5 RFPLL1 pll_en %d kv_en %d vtc_en %d lpf %d cpl %d"
+ " pdp %d autocal_en %d ld_en %d rfloopr %d rfloopc %d"
+ " dac %d",
+ bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14),
+ bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10),
+ bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0,3));
+ break;
+ case 6:
+ PDEBUG("reg6 RFPLL2 n %d num %d",
+ bits(rw, 6, 17), bits(rw, 0, 5));
+ break;
+ case 7:
+ PDEBUG("reg7 RFPLL3 num2 %d", bits(rw, 0, 17));
+ break;
+ case 8:
+ PDEBUG("reg8 RFPLL4 dn %#06x ct_def %d kv_def %d",
+ bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3));
+ break;
+ case 9:
+ PDEBUG("reg9 CAL1 tvco %d tlock %d m_ct_value %d ld_window %d",
+ bits(rw, 13, 17), bits(rw, 8, 12), bits(rw, 3, 7),
+ bits(rw, 0, 2));
+ break;
+ case 10:
+ PDEBUG("reg10 TXRX1 rxdcfbbyps %d pcontrol %d txvgc %d"
+ " rxlpfbw %d txlpfbw %d txdiffmode %d txenmode %d"
+ " intbiasen %d tybypass %d",
+ bit(rw, 17), bits(rw, 15, 16), bits(rw, 10, 14),
+ bits(rw, 7, 9), bits(rw, 4, 6), bit(rw, 3), bit(rw, 2),
+ bit(rw, 1), bit(rw, 0));
+ break;
+ case 11:
+ PDEBUG("reg11 PCNT1 mid_bias %d p_desired %d pc_offset %d"
+ " tx_delay %d",
+ bits(rw, 15, 17), bits(rw, 9, 14), bits(rw, 3, 8),
+ bits(rw, 0, 2));
+ break;
+ case 12:
+ PDEBUG("reg12 PCNT2 max_power %d mid_power %d min_power %d",
+ bits(rw, 12, 17), bits(rw, 6, 11), bits(rw, 0, 5));
+ break;
+ case 13:
+ PDEBUG("reg13 VCOT1 rfpll vco comp %d ifpll vco comp %d"
+ " lobias %d if_biasbuf %d if_biasvco %d rf_biasbuf %d"
+ " rf_biasvco %d",
+ bit(rw, 17), bit(rw, 16), bit(rw, 15),
+ bits(rw, 8, 9), bits(rw, 5, 7), bits(rw, 3, 4),
+ bits(rw, 0, 2));
+ break;
+ case 14:
+ PDEBUG("reg14 IQCAL rx_acal %d rx_pcal %d"
+ " tx_acal %d tx_pcal %d",
+ bits(rw, 13, 17), bits(rw, 9, 12), bits(rw, 4, 8),
+ bits(rw, 0, 3));
+ break;
+ }
+}
+#endif /* 0 */
+
+static int rf2959_init_hw(struct zd_rf *rf)
+{
+ int r;
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR2, 0x1E }, { ZD_CR9, 0x20 }, { ZD_CR10, 0x89 },
+ { ZD_CR11, 0x00 }, { ZD_CR15, 0xD0 }, { ZD_CR17, 0x68 },
+ { ZD_CR19, 0x4a }, { ZD_CR20, 0x0c }, { ZD_CR21, 0x0E },
+ { ZD_CR23, 0x48 },
+ /* normal size for cca threshold */
+ { ZD_CR24, 0x14 },
+ /* { ZD_CR24, 0x20 }, */
+ { ZD_CR26, 0x90 }, { ZD_CR27, 0x30 }, { ZD_CR29, 0x20 },
+ { ZD_CR31, 0xb2 }, { ZD_CR32, 0x43 }, { ZD_CR33, 0x28 },
+ { ZD_CR38, 0x30 }, { ZD_CR34, 0x0f }, { ZD_CR35, 0xF0 },
+ { ZD_CR41, 0x2a }, { ZD_CR46, 0x7F }, { ZD_CR47, 0x1E },
+ { ZD_CR51, 0xc5 }, { ZD_CR52, 0xc5 }, { ZD_CR53, 0xc5 },
+ { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 },
+ { ZD_CR82, 0x00 }, { ZD_CR83, 0x24 }, { ZD_CR84, 0x04 },
+ { ZD_CR85, 0x00 }, { ZD_CR86, 0x10 }, { ZD_CR87, 0x2A },
+ { ZD_CR88, 0x10 }, { ZD_CR89, 0x24 }, { ZD_CR90, 0x18 },
+ /* { ZD_CR91, 0x18 }, */
+ /* should solve continuous CTS frame problems */
+ { ZD_CR91, 0x00 },
+ { ZD_CR92, 0x0a }, { ZD_CR93, 0x00 }, { ZD_CR94, 0x01 },
+ { ZD_CR95, 0x00 }, { ZD_CR96, 0x40 }, { ZD_CR97, 0x37 },
+ { ZD_CR98, 0x05 }, { ZD_CR99, 0x28 }, { ZD_CR100, 0x00 },
+ { ZD_CR101, 0x13 }, { ZD_CR102, 0x27 }, { ZD_CR103, 0x27 },
+ { ZD_CR104, 0x18 }, { ZD_CR105, 0x12 },
+ /* normal size */
+ { ZD_CR106, 0x1a },
+ /* { ZD_CR106, 0x22 }, */
+ { ZD_CR107, 0x24 }, { ZD_CR108, 0x0a }, { ZD_CR109, 0x13 },
+ { ZD_CR110, 0x2F }, { ZD_CR111, 0x27 }, { ZD_CR112, 0x27 },
+ { ZD_CR113, 0x27 }, { ZD_CR114, 0x27 }, { ZD_CR115, 0x40 },
+ { ZD_CR116, 0x40 }, { ZD_CR117, 0xF0 }, { ZD_CR118, 0xF0 },
+ { ZD_CR119, 0x16 },
+ /* no TX continuation */
+ { ZD_CR122, 0x00 },
+ /* { ZD_CR122, 0xff }, */
+ { ZD_CR127, 0x03 }, { ZD_CR131, 0x08 }, { ZD_CR138, 0x28 },
+ { ZD_CR148, 0x44 }, { ZD_CR150, 0x10 }, { ZD_CR169, 0xBB },
+ { ZD_CR170, 0xBB },
+ };
+
+ static const u32 rv[] = {
+ 0x000007, /* REG0(CFG1) */
+ 0x07dd43, /* REG1(IFPLL1) */
+ 0x080959, /* REG2(IFPLL2) */
+ 0x0e6666,
+ 0x116a57, /* REG4 */
+ 0x17dd43, /* REG5 */
+ 0x1819f9, /* REG6 */
+ 0x1e6666,
+ 0x214554,
+ 0x25e7fa,
+ 0x27fffa,
+ /* The Zydas driver somehow forgets to set this value. It's
+ * only set for Japan. We are using internal power control
+ * for now.
+ */
+ 0x294128, /* internal power */
+ /* 0x28252c, */ /* External control TX power */
+ /* ZD_CR31_CCK, ZD_CR51_6-36M, ZD_CR52_48M, ZD_CR53_54M */
+ 0x2c0000,
+ 0x300000,
+ 0x340000, /* REG13(0xD) */
+ 0x381e0f, /* REG14(0xE) */
+ /* Bogus, RF2959's data sheet doesn't know register 27, which is
+ * actually referenced here. The commented 0x11 is 17.
+ */
+ 0x6c180f, /* REG27(0x11) */
+ };
+
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ if (r)
+ return r;
+
+ return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
+}
+
+static int rf2959_set_channel(struct zd_rf *rf, u8 channel)
+{
+ int i, r;
+ const u32 *rv = rf2959_table[channel-1];
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ for (i = 0; i < 2; i++) {
+ r = zd_rfwrite_locked(chip, rv[i], RF_RV_BITS);
+ if (r)
+ return r;
+ }
+ return 0;
+}
+
+static int rf2959_switch_radio_on(struct zd_rf *rf)
+{
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR10, 0x89 },
+ { ZD_CR11, 0x00 },
+ };
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int rf2959_switch_radio_off(struct zd_rf *rf)
+{
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR10, 0x15 },
+ { ZD_CR11, 0x81 },
+ };
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+int zd_rf_init_rf2959(struct zd_rf *rf)
+{
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ if (zd_chip_is_zd1211b(chip)) {
+ dev_err(zd_chip_dev(chip),
+ "RF2959 is currently not supported for ZD1211B"
+ " devices\n");
+ return -ENODEV;
+ }
+ rf->init_hw = rf2959_init_hw;
+ rf->set_channel = rf2959_set_channel;
+ rf->switch_radio_on = rf2959_switch_radio_on;
+ rf->switch_radio_off = rf2959_switch_radio_off;
+ return 0;
+}
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c b/drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c
new file mode 100644
index 000000000..a4e7f187d
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_rf_uw2453.c
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "zd_rf.h"
+#include "zd_usb.h"
+#include "zd_chip.h"
+
+/* This RF programming code is based upon the code found in v2.16.0.0 of the
+ * ZyDAS vendor driver. Unlike other RF's, Ubec publish full technical specs
+ * for this RF on their website, so we're able to understand more than
+ * usual as to what is going on. Thumbs up for Ubec for doing that. */
+
+/* The 3-wire serial interface provides access to 8 write-only registers.
+ * The data format is a 4 bit register address followed by a 20 bit value. */
+#define UW2453_REGWRITE(reg, val) ((((reg) & 0xf) << 20) | ((val) & 0xfffff))
+
+/* For channel tuning, we have to configure registers 1 (synthesizer), 2 (synth
+ * fractional divide ratio) and 3 (VCO config).
+ *
+ * We configure the RF to produce an interrupt when the PLL is locked onto
+ * the configured frequency. During initialization, we run through a variety
+ * of different VCO configurations on channel 1 until we detect a PLL lock.
+ * When this happens, we remember which VCO configuration produced the lock
+ * and use it later. Actually, we use the configuration *after* the one that
+ * produced the lock, which seems odd, but it works.
+ *
+ * If we do not see a PLL lock on any standard VCO config, we fall back on an
+ * autocal configuration, which has a fixed (as opposed to per-channel) VCO
+ * config and different synth values from the standard set (divide ratio
+ * is still shared with the standard set). */
+
+/* The per-channel synth values for all standard VCO configurations. These get
+ * written to register 1. */
+static const u8 uw2453_std_synth[] = {
+ RF_CHANNEL( 1) = 0x47,
+ RF_CHANNEL( 2) = 0x47,
+ RF_CHANNEL( 3) = 0x67,
+ RF_CHANNEL( 4) = 0x67,
+ RF_CHANNEL( 5) = 0x67,
+ RF_CHANNEL( 6) = 0x67,
+ RF_CHANNEL( 7) = 0x57,
+ RF_CHANNEL( 8) = 0x57,
+ RF_CHANNEL( 9) = 0x57,
+ RF_CHANNEL(10) = 0x57,
+ RF_CHANNEL(11) = 0x77,
+ RF_CHANNEL(12) = 0x77,
+ RF_CHANNEL(13) = 0x77,
+ RF_CHANNEL(14) = 0x4f,
+};
+
+/* This table stores the synthesizer fractional divide ratio for *all* VCO
+ * configurations (both standard and autocal). These get written to register 2.
+ */
+static const u16 uw2453_synth_divide[] = {
+ RF_CHANNEL( 1) = 0x999,
+ RF_CHANNEL( 2) = 0x99b,
+ RF_CHANNEL( 3) = 0x998,
+ RF_CHANNEL( 4) = 0x99a,
+ RF_CHANNEL( 5) = 0x999,
+ RF_CHANNEL( 6) = 0x99b,
+ RF_CHANNEL( 7) = 0x998,
+ RF_CHANNEL( 8) = 0x99a,
+ RF_CHANNEL( 9) = 0x999,
+ RF_CHANNEL(10) = 0x99b,
+ RF_CHANNEL(11) = 0x998,
+ RF_CHANNEL(12) = 0x99a,
+ RF_CHANNEL(13) = 0x999,
+ RF_CHANNEL(14) = 0xccc,
+};
+
+/* Here is the data for all the standard VCO configurations. We shrink our
+ * table a little by observing that both channels in a consecutive pair share
+ * the same value. We also observe that the high 4 bits ([0:3] in the specs)
+ * are all 'Reserved' and are always set to 0x4 - we chop them off in the data
+ * below. */
+#define CHAN_TO_PAIRIDX(a) ((a - 1) / 2)
+#define RF_CHANPAIR(a,b) [CHAN_TO_PAIRIDX(a)]
+static const u16 uw2453_std_vco_cfg[][7] = {
+ { /* table 1 */
+ RF_CHANPAIR( 1, 2) = 0x664d,
+ RF_CHANPAIR( 3, 4) = 0x604d,
+ RF_CHANPAIR( 5, 6) = 0x6675,
+ RF_CHANPAIR( 7, 8) = 0x6475,
+ RF_CHANPAIR( 9, 10) = 0x6655,
+ RF_CHANPAIR(11, 12) = 0x6455,
+ RF_CHANPAIR(13, 14) = 0x6665,
+ },
+ { /* table 2 */
+ RF_CHANPAIR( 1, 2) = 0x666d,
+ RF_CHANPAIR( 3, 4) = 0x606d,
+ RF_CHANPAIR( 5, 6) = 0x664d,
+ RF_CHANPAIR( 7, 8) = 0x644d,
+ RF_CHANPAIR( 9, 10) = 0x6675,
+ RF_CHANPAIR(11, 12) = 0x6475,
+ RF_CHANPAIR(13, 14) = 0x6655,
+ },
+ { /* table 3 */
+ RF_CHANPAIR( 1, 2) = 0x665d,
+ RF_CHANPAIR( 3, 4) = 0x605d,
+ RF_CHANPAIR( 5, 6) = 0x666d,
+ RF_CHANPAIR( 7, 8) = 0x646d,
+ RF_CHANPAIR( 9, 10) = 0x664d,
+ RF_CHANPAIR(11, 12) = 0x644d,
+ RF_CHANPAIR(13, 14) = 0x6675,
+ },
+ { /* table 4 */
+ RF_CHANPAIR( 1, 2) = 0x667d,
+ RF_CHANPAIR( 3, 4) = 0x607d,
+ RF_CHANPAIR( 5, 6) = 0x665d,
+ RF_CHANPAIR( 7, 8) = 0x645d,
+ RF_CHANPAIR( 9, 10) = 0x666d,
+ RF_CHANPAIR(11, 12) = 0x646d,
+ RF_CHANPAIR(13, 14) = 0x664d,
+ },
+ { /* table 5 */
+ RF_CHANPAIR( 1, 2) = 0x6643,
+ RF_CHANPAIR( 3, 4) = 0x6043,
+ RF_CHANPAIR( 5, 6) = 0x667d,
+ RF_CHANPAIR( 7, 8) = 0x647d,
+ RF_CHANPAIR( 9, 10) = 0x665d,
+ RF_CHANPAIR(11, 12) = 0x645d,
+ RF_CHANPAIR(13, 14) = 0x666d,
+ },
+ { /* table 6 */
+ RF_CHANPAIR( 1, 2) = 0x6663,
+ RF_CHANPAIR( 3, 4) = 0x6063,
+ RF_CHANPAIR( 5, 6) = 0x6643,
+ RF_CHANPAIR( 7, 8) = 0x6443,
+ RF_CHANPAIR( 9, 10) = 0x667d,
+ RF_CHANPAIR(11, 12) = 0x647d,
+ RF_CHANPAIR(13, 14) = 0x665d,
+ },
+ { /* table 7 */
+ RF_CHANPAIR( 1, 2) = 0x6653,
+ RF_CHANPAIR( 3, 4) = 0x6053,
+ RF_CHANPAIR( 5, 6) = 0x6663,
+ RF_CHANPAIR( 7, 8) = 0x6463,
+ RF_CHANPAIR( 9, 10) = 0x6643,
+ RF_CHANPAIR(11, 12) = 0x6443,
+ RF_CHANPAIR(13, 14) = 0x667d,
+ },
+ { /* table 8 */
+ RF_CHANPAIR( 1, 2) = 0x6673,
+ RF_CHANPAIR( 3, 4) = 0x6073,
+ RF_CHANPAIR( 5, 6) = 0x6653,
+ RF_CHANPAIR( 7, 8) = 0x6453,
+ RF_CHANPAIR( 9, 10) = 0x6663,
+ RF_CHANPAIR(11, 12) = 0x6463,
+ RF_CHANPAIR(13, 14) = 0x6643,
+ },
+ { /* table 9 */
+ RF_CHANPAIR( 1, 2) = 0x664b,
+ RF_CHANPAIR( 3, 4) = 0x604b,
+ RF_CHANPAIR( 5, 6) = 0x6673,
+ RF_CHANPAIR( 7, 8) = 0x6473,
+ RF_CHANPAIR( 9, 10) = 0x6653,
+ RF_CHANPAIR(11, 12) = 0x6453,
+ RF_CHANPAIR(13, 14) = 0x6663,
+ },
+ { /* table 10 */
+ RF_CHANPAIR( 1, 2) = 0x666b,
+ RF_CHANPAIR( 3, 4) = 0x606b,
+ RF_CHANPAIR( 5, 6) = 0x664b,
+ RF_CHANPAIR( 7, 8) = 0x644b,
+ RF_CHANPAIR( 9, 10) = 0x6673,
+ RF_CHANPAIR(11, 12) = 0x6473,
+ RF_CHANPAIR(13, 14) = 0x6653,
+ },
+ { /* table 11 */
+ RF_CHANPAIR( 1, 2) = 0x665b,
+ RF_CHANPAIR( 3, 4) = 0x605b,
+ RF_CHANPAIR( 5, 6) = 0x666b,
+ RF_CHANPAIR( 7, 8) = 0x646b,
+ RF_CHANPAIR( 9, 10) = 0x664b,
+ RF_CHANPAIR(11, 12) = 0x644b,
+ RF_CHANPAIR(13, 14) = 0x6673,
+ },
+
+};
+
+/* The per-channel synth values for autocal. These get written to register 1. */
+static const u16 uw2453_autocal_synth[] = {
+ RF_CHANNEL( 1) = 0x6847,
+ RF_CHANNEL( 2) = 0x6847,
+ RF_CHANNEL( 3) = 0x6867,
+ RF_CHANNEL( 4) = 0x6867,
+ RF_CHANNEL( 5) = 0x6867,
+ RF_CHANNEL( 6) = 0x6867,
+ RF_CHANNEL( 7) = 0x6857,
+ RF_CHANNEL( 8) = 0x6857,
+ RF_CHANNEL( 9) = 0x6857,
+ RF_CHANNEL(10) = 0x6857,
+ RF_CHANNEL(11) = 0x6877,
+ RF_CHANNEL(12) = 0x6877,
+ RF_CHANNEL(13) = 0x6877,
+ RF_CHANNEL(14) = 0x684f,
+};
+
+/* The VCO configuration for autocal (all channels) */
+static const u16 UW2453_AUTOCAL_VCO_CFG = 0x6662;
+
+/* TX gain settings. The array index corresponds to the TX power integration
+ * values found in the EEPROM. The values get written to register 7. */
+static u32 uw2453_txgain[] = {
+ [0x00] = 0x0e313,
+ [0x01] = 0x0fb13,
+ [0x02] = 0x0e093,
+ [0x03] = 0x0f893,
+ [0x04] = 0x0ea93,
+ [0x05] = 0x1f093,
+ [0x06] = 0x1f493,
+ [0x07] = 0x1f693,
+ [0x08] = 0x1f393,
+ [0x09] = 0x1f35b,
+ [0x0a] = 0x1e6db,
+ [0x0b] = 0x1ff3f,
+ [0x0c] = 0x1ffff,
+ [0x0d] = 0x361d7,
+ [0x0e] = 0x37fbf,
+ [0x0f] = 0x3ff8b,
+ [0x10] = 0x3ff33,
+ [0x11] = 0x3fb3f,
+ [0x12] = 0x3ffff,
+};
+
+/* RF-specific structure */
+struct uw2453_priv {
+ /* index into synth/VCO config tables where PLL lock was found
+ * -1 means autocal */
+ int config;
+};
+
+#define UW2453_PRIV(rf) ((struct uw2453_priv *) (rf)->priv)
+
+static int uw2453_synth_set_channel(struct zd_chip *chip, int channel,
+ bool autocal)
+{
+ int r;
+ int idx = channel - 1;
+ u32 val;
+
+ if (autocal)
+ val = UW2453_REGWRITE(1, uw2453_autocal_synth[idx]);
+ else
+ val = UW2453_REGWRITE(1, uw2453_std_synth[idx]);
+
+ r = zd_rfwrite_locked(chip, val, RF_RV_BITS);
+ if (r)
+ return r;
+
+ return zd_rfwrite_locked(chip,
+ UW2453_REGWRITE(2, uw2453_synth_divide[idx]), RF_RV_BITS);
+}
+
+static int uw2453_write_vco_cfg(struct zd_chip *chip, u16 value)
+{
+ /* vendor driver always sets these upper bits even though the specs say
+ * they are reserved */
+ u32 val = 0x40000 | value;
+ return zd_rfwrite_locked(chip, UW2453_REGWRITE(3, val), RF_RV_BITS);
+}
+
+static int uw2453_init_mode(struct zd_chip *chip)
+{
+ static const u32 rv[] = {
+ UW2453_REGWRITE(0, 0x25f98), /* enter IDLE mode */
+ UW2453_REGWRITE(0, 0x25f9a), /* enter CAL_VCO mode */
+ UW2453_REGWRITE(0, 0x25f94), /* enter RX/TX mode */
+ UW2453_REGWRITE(0, 0x27fd4), /* power down RSSI circuit */
+ };
+
+ return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
+}
+
+static int uw2453_set_tx_gain_level(struct zd_chip *chip, int channel)
+{
+ u8 int_value = chip->pwr_int_values[channel - 1];
+
+ if (int_value >= ARRAY_SIZE(uw2453_txgain)) {
+ dev_dbg_f(zd_chip_dev(chip), "can't configure TX gain for "
+ "int value %x on channel %d\n", int_value, channel);
+ return 0;
+ }
+
+ return zd_rfwrite_locked(chip,
+ UW2453_REGWRITE(7, uw2453_txgain[int_value]), RF_RV_BITS);
+}
+
+static int uw2453_init_hw(struct zd_rf *rf)
+{
+ int i, r;
+ int found_config = -1;
+ u16 intr_status;
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR10, 0x89 }, { ZD_CR15, 0x20 },
+ { ZD_CR17, 0x28 }, /* 6112 no change */
+ { ZD_CR23, 0x38 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 },
+ { ZD_CR27, 0x15 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 },
+ { ZD_CR33, 0x28 }, { ZD_CR34, 0x30 },
+ { ZD_CR35, 0x43 }, /* 6112 3e->43 */
+ { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 },
+ { ZD_CR46, 0x92 }, /* 6112 96->92 */
+ { ZD_CR47, 0x1e },
+ { ZD_CR48, 0x04 }, /* 5602 Roger */
+ { ZD_CR49, 0xfa }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 },
+ { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 },
+ { ZD_CR91, 0x00 }, { ZD_CR92, 0x0a }, { ZD_CR98, 0x8d },
+ { ZD_CR99, 0x28 }, { ZD_CR100, 0x02 },
+ { ZD_CR101, 0x09 }, /* 6112 13->1f 6220 1f->13 6407 13->9 */
+ { ZD_CR102, 0x27 },
+ { ZD_CR106, 0x1c }, /* 5d07 5112 1f->1c 6220 1c->1f
+ * 6221 1f->1c
+ */
+ { ZD_CR107, 0x1c }, /* 6220 1c->1a 5221 1a->1c */
+ { ZD_CR109, 0x13 },
+ { ZD_CR110, 0x1f }, /* 6112 13->1f 6221 1f->13 6407 13->0x09 */
+ { ZD_CR111, 0x13 }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 },
+ { ZD_CR114, 0x23 }, /* 6221 27->23 */
+ { ZD_CR115, 0x24 }, /* 6112 24->1c 6220 1c->24 */
+ { ZD_CR116, 0x24 }, /* 6220 1c->24 */
+ { ZD_CR117, 0xfa }, /* 6112 fa->f8 6220 f8->f4 6220 f4->fa */
+ { ZD_CR118, 0xf0 }, /* 5d07 6112 f0->f2 6220 f2->f0 */
+ { ZD_CR119, 0x1a }, /* 6112 1a->10 6220 10->14 6220 14->1a */
+ { ZD_CR120, 0x4f },
+ { ZD_CR121, 0x1f }, /* 6220 4f->1f */
+ { ZD_CR122, 0xf0 }, { ZD_CR123, 0x57 }, { ZD_CR125, 0xad },
+ { ZD_CR126, 0x6c }, { ZD_CR127, 0x03 },
+ { ZD_CR128, 0x14 }, /* 6302 12->11 */
+ { ZD_CR129, 0x12 }, /* 6301 10->0f */
+ { ZD_CR130, 0x10 }, { ZD_CR137, 0x50 }, { ZD_CR138, 0xa8 },
+ { ZD_CR144, 0xac }, { ZD_CR146, 0x20 }, { ZD_CR252, 0xff },
+ { ZD_CR253, 0xff },
+ };
+
+ static const u32 rv[] = {
+ UW2453_REGWRITE(4, 0x2b), /* configure receiver gain */
+ UW2453_REGWRITE(5, 0x19e4f), /* configure transmitter gain */
+ UW2453_REGWRITE(6, 0xf81ad), /* enable RX/TX filter tuning */
+ UW2453_REGWRITE(7, 0x3fffe), /* disable TX gain in test mode */
+
+ /* enter CAL_FIL mode, TX gain set by registers, RX gain set by pins,
+ * RSSI circuit powered down, reduced RSSI range */
+ UW2453_REGWRITE(0, 0x25f9c), /* 5d01 cal_fil */
+
+ /* synthesizer configuration for channel 1 */
+ UW2453_REGWRITE(1, 0x47),
+ UW2453_REGWRITE(2, 0x999),
+
+ /* disable manual VCO band selection */
+ UW2453_REGWRITE(3, 0x7602),
+
+ /* enable manual VCO band selection, configure current level */
+ UW2453_REGWRITE(3, 0x46063),
+ };
+
+ 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;
+
+ r = uw2453_init_mode(chip);
+ if (r)
+ return r;
+
+ /* Try all standard VCO configuration settings on channel 1 */
+ for (i = 0; i < ARRAY_SIZE(uw2453_std_vco_cfg) - 1; i++) {
+ /* Configure synthesizer for channel 1 */
+ r = uw2453_synth_set_channel(chip, 1, false);
+ if (r)
+ return r;
+
+ /* Write VCO config */
+ r = uw2453_write_vco_cfg(chip, uw2453_std_vco_cfg[i][0]);
+ if (r)
+ return r;
+
+ /* ack interrupt event */
+ r = zd_iowrite16_locked(chip, 0x0f, UW2453_INTR_REG);
+ if (r)
+ return r;
+
+ /* check interrupt status */
+ r = zd_ioread16_locked(chip, &intr_status, UW2453_INTR_REG);
+ if (r)
+ return r;
+
+ if (!(intr_status & 0xf)) {
+ dev_dbg_f(zd_chip_dev(chip),
+ "PLL locked on configuration %d\n", i);
+ found_config = i;
+ break;
+ }
+ }
+
+ if (found_config == -1) {
+ /* autocal */
+ dev_dbg_f(zd_chip_dev(chip),
+ "PLL did not lock, using autocal\n");
+
+ r = uw2453_synth_set_channel(chip, 1, true);
+ if (r)
+ return r;
+
+ r = uw2453_write_vco_cfg(chip, UW2453_AUTOCAL_VCO_CFG);
+ if (r)
+ return r;
+ }
+
+ /* To match the vendor driver behaviour, we use the configuration after
+ * the one that produced a lock. */
+ UW2453_PRIV(rf)->config = found_config + 1;
+
+ return zd_iowrite16_locked(chip, 0x06, ZD_CR203);
+}
+
+static int uw2453_set_channel(struct zd_rf *rf, u8 channel)
+{
+ int r;
+ u16 vco_cfg;
+ int config = UW2453_PRIV(rf)->config;
+ bool autocal = (config == -1);
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 },
+ { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 },
+ };
+
+ r = uw2453_synth_set_channel(chip, channel, autocal);
+ if (r)
+ return r;
+
+ if (autocal)
+ vco_cfg = UW2453_AUTOCAL_VCO_CFG;
+ else
+ vco_cfg = uw2453_std_vco_cfg[config][CHAN_TO_PAIRIDX(channel)];
+
+ r = uw2453_write_vco_cfg(chip, vco_cfg);
+ if (r)
+ return r;
+
+ r = uw2453_init_mode(chip);
+ if (r)
+ return r;
+
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ if (r)
+ return r;
+
+ r = uw2453_set_tx_gain_level(chip, channel);
+ if (r)
+ return r;
+
+ return zd_iowrite16_locked(chip, 0x06, ZD_CR203);
+}
+
+static int uw2453_switch_radio_on(struct zd_rf *rf)
+{
+ int r;
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+ struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR11, 0x00 }, { ZD_CR251, 0x3f },
+ };
+
+ /* enter RXTX mode */
+ r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f94), RF_RV_BITS);
+ if (r)
+ return r;
+
+ if (zd_chip_is_zd1211b(chip))
+ ioreqs[1].value = 0x7f;
+
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static int uw2453_switch_radio_off(struct zd_rf *rf)
+{
+ int r;
+ struct zd_chip *chip = zd_rf_to_chip(rf);
+ static const struct zd_ioreq16 ioreqs[] = {
+ { ZD_CR11, 0x04 }, { ZD_CR251, 0x2f },
+ };
+
+ /* enter IDLE mode */
+ /* FIXME: shouldn't we go to SLEEP? sent email to zydas */
+ r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f90), RF_RV_BITS);
+ if (r)
+ return r;
+
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+}
+
+static void uw2453_clear(struct zd_rf *rf)
+{
+ kfree(rf->priv);
+}
+
+int zd_rf_init_uw2453(struct zd_rf *rf)
+{
+ rf->init_hw = uw2453_init_hw;
+ rf->set_channel = uw2453_set_channel;
+ rf->switch_radio_on = uw2453_switch_radio_on;
+ rf->switch_radio_off = uw2453_switch_radio_off;
+ rf->patch_6m_band_edge = zd_rf_generic_patch_6m;
+ rf->clear = uw2453_clear;
+ /* we have our own TX integration code */
+ rf->update_channel_int = 0;
+
+ rf->priv = kmalloc(sizeof(struct uw2453_priv), GFP_KERNEL);
+ if (rf->priv == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
new file mode 100644
index 000000000..850c26bc9
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
@@ -0,0 +1,2030 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ * Copyright (C) 2006-2007 Michael Wu <flamingice@sourmilk.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "zd_def.h"
+#include "zd_mac.h"
+#include "zd_usb.h"
+
+static const struct usb_device_id usb_ids[] = {
+ /* ZD1211 */
+ { USB_DEVICE(0x0105, 0x145f), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0586, 0x3402), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0586, 0x3407), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0586, 0x3409), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0ace, 0xa211), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0b05, 0x170c), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0b3b, 0x5630), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x0df6, 0x9075), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x129b, 0x1666), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x1435, 0x0711), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x14ea, 0xab10), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x157e, 0x300a), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x157e, 0x3207), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 },
+ { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 },
+ /* ZD1211B */
+ { USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0409, 0x0248), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0411, 0x00da), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x054c, 0x0257), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0586, 0x3413), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x07fa, 0x1196), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x083a, 0xe501), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x083a, 0xe503), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x083a, 0xe506), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0ace, 0xb215), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0cde, 0x001a), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x0df6, 0x0036), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x129b, 0x1667), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x2019, 0x5303), .driver_info = DEVICE_ZD1211B },
+ { USB_DEVICE(0x2019, 0xed01), .driver_info = DEVICE_ZD1211B },
+ /* "Driverless" devices that need ejecting */
+ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER },
+ { USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER },
+ {}
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip.");
+MODULE_AUTHOR("Ulrich Kunitz");
+MODULE_AUTHOR("Daniel Drake");
+MODULE_VERSION("1.0");
+MODULE_DEVICE_TABLE(usb, usb_ids);
+
+#define FW_ZD1211_PREFIX "zd1211/zd1211_"
+#define FW_ZD1211B_PREFIX "zd1211/zd1211b_"
+
+static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req,
+ unsigned int count);
+
+/* USB device initialization */
+static void int_urb_complete(struct urb *urb);
+
+static int request_fw_file(
+ const struct firmware **fw, const char *name, struct device *device)
+{
+ int r;
+
+ dev_dbg_f(device, "fw name %s\n", name);
+
+ r = request_firmware(fw, name, device);
+ if (r)
+ dev_err(device,
+ "Could not load firmware file %s. Error number %d\n",
+ name, r);
+ return r;
+}
+
+static inline u16 get_bcdDevice(const struct usb_device *udev)
+{
+ return le16_to_cpu(udev->descriptor.bcdDevice);
+}
+
+enum upload_code_flags {
+ REBOOT = 1,
+};
+
+/* Ensures that MAX_TRANSFER_SIZE is even. */
+#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
+
+static int upload_code(struct usb_device *udev,
+ const u8 *data, size_t size, u16 code_offset, int flags)
+{
+ u8 *p;
+ int r;
+
+ /* USB request blocks need "kmalloced" buffers.
+ */
+ p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL);
+ if (!p) {
+ r = -ENOMEM;
+ goto error;
+ }
+
+ size &= ~1;
+ while (size > 0) {
+ size_t transfer_size = size <= MAX_TRANSFER_SIZE ?
+ size : MAX_TRANSFER_SIZE;
+
+ dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size);
+
+ memcpy(p, data, transfer_size);
+ r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_FIRMWARE_DOWNLOAD,
+ USB_DIR_OUT | USB_TYPE_VENDOR,
+ code_offset, 0, p, transfer_size, 1000 /* ms */);
+ if (r < 0) {
+ dev_err(&udev->dev,
+ "USB control request for firmware upload"
+ " failed. Error number %d\n", r);
+ goto error;
+ }
+ transfer_size = r & ~1;
+
+ size -= transfer_size;
+ data += transfer_size;
+ code_offset += transfer_size/sizeof(u16);
+ }
+
+ if (flags & REBOOT) {
+ u8 ret;
+
+ /* Use "DMA-aware" buffer. */
+ r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_REQ_FIRMWARE_CONFIRM,
+ USB_DIR_IN | USB_TYPE_VENDOR,
+ 0, 0, p, sizeof(ret), 5000 /* ms */);
+ if (r != sizeof(ret)) {
+ dev_err(&udev->dev,
+ "control request firmware confirmation failed."
+ " Return value %d\n", r);
+ if (r >= 0)
+ r = -ENODEV;
+ goto error;
+ }
+ ret = p[0];
+ if (ret & 0x80) {
+ dev_err(&udev->dev,
+ "Internal error while downloading."
+ " Firmware confirm return value %#04x\n",
+ (unsigned int)ret);
+ r = -ENODEV;
+ goto error;
+ }
+ dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n",
+ (unsigned int)ret);
+ }
+
+ r = 0;
+error:
+ kfree(p);
+ return r;
+}
+
+static u16 get_word(const void *data, u16 offset)
+{
+ const __le16 *p = data;
+ return le16_to_cpu(p[offset]);
+}
+
+static char *get_fw_name(struct zd_usb *usb, char *buffer, size_t size,
+ const char* postfix)
+{
+ scnprintf(buffer, size, "%s%s",
+ usb->is_zd1211b ?
+ FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX,
+ postfix);
+ return buffer;
+}
+
+static int handle_version_mismatch(struct zd_usb *usb,
+ const struct firmware *ub_fw)
+{
+ struct usb_device *udev = zd_usb_to_usbdev(usb);
+ const struct firmware *ur_fw = NULL;
+ int offset;
+ int r = 0;
+ char fw_name[128];
+
+ r = request_fw_file(&ur_fw,
+ get_fw_name(usb, fw_name, sizeof(fw_name), "ur"),
+ &udev->dev);
+ if (r)
+ goto error;
+
+ r = upload_code(udev, ur_fw->data, ur_fw->size, FW_START, REBOOT);
+ if (r)
+ goto error;
+
+ offset = (E2P_BOOT_CODE_OFFSET * sizeof(u16));
+ r = upload_code(udev, ub_fw->data + offset, ub_fw->size - offset,
+ E2P_START + E2P_BOOT_CODE_OFFSET, REBOOT);
+
+ /* At this point, the vendor driver downloads the whole firmware
+ * image, hacks around with version IDs, and uploads it again,
+ * completely overwriting the boot code. We do not do this here as
+ * it is not required on any tested devices, and it is suspected to
+ * cause problems. */
+error:
+ release_firmware(ur_fw);
+ return r;
+}
+
+static int upload_firmware(struct zd_usb *usb)
+{
+ int r;
+ u16 fw_bcdDevice;
+ u16 bcdDevice;
+ struct usb_device *udev = zd_usb_to_usbdev(usb);
+ const struct firmware *ub_fw = NULL;
+ const struct firmware *uph_fw = NULL;
+ char fw_name[128];
+
+ bcdDevice = get_bcdDevice(udev);
+
+ r = request_fw_file(&ub_fw,
+ get_fw_name(usb, fw_name, sizeof(fw_name), "ub"),
+ &udev->dev);
+ if (r)
+ goto error;
+
+ fw_bcdDevice = get_word(ub_fw->data, E2P_DATA_OFFSET);
+
+ if (fw_bcdDevice != bcdDevice) {
+ dev_info(&udev->dev,
+ "firmware version %#06x and device bootcode version "
+ "%#06x differ\n", fw_bcdDevice, bcdDevice);
+ if (bcdDevice <= 0x4313)
+ dev_warn(&udev->dev, "device has old bootcode, please "
+ "report success or failure\n");
+
+ r = handle_version_mismatch(usb, ub_fw);
+ if (r)
+ goto error;
+ } else {
+ dev_dbg_f(&udev->dev,
+ "firmware device id %#06x is equal to the "
+ "actual device id\n", fw_bcdDevice);
+ }
+
+
+ r = request_fw_file(&uph_fw,
+ get_fw_name(usb, fw_name, sizeof(fw_name), "uphr"),
+ &udev->dev);
+ if (r)
+ goto error;
+
+ r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START, REBOOT);
+ if (r) {
+ dev_err(&udev->dev,
+ "Could not upload firmware code uph. Error number %d\n",
+ r);
+ }
+
+ /* FALL-THROUGH */
+error:
+ release_firmware(ub_fw);
+ release_firmware(uph_fw);
+ return r;
+}
+
+MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ur");
+MODULE_FIRMWARE(FW_ZD1211_PREFIX "ur");
+MODULE_FIRMWARE(FW_ZD1211B_PREFIX "ub");
+MODULE_FIRMWARE(FW_ZD1211_PREFIX "ub");
+MODULE_FIRMWARE(FW_ZD1211B_PREFIX "uphr");
+MODULE_FIRMWARE(FW_ZD1211_PREFIX "uphr");
+
+/* Read data from device address space using "firmware interface" which does
+ * not require firmware to be loaded. */
+int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len)
+{
+ int r;
+ struct usb_device *udev = zd_usb_to_usbdev(usb);
+ u8 *buf;
+
+ /* Use "DMA-aware" buffer. */
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_REQ_FIRMWARE_READ_DATA, USB_DIR_IN | 0x40, addr, 0,
+ buf, len, 5000);
+ if (r < 0) {
+ dev_err(&udev->dev,
+ "read over firmware interface failed: %d\n", r);
+ goto exit;
+ } else if (r != len) {
+ dev_err(&udev->dev,
+ "incomplete read over firmware interface: %d/%d\n",
+ r, len);
+ r = -EIO;
+ goto exit;
+ }
+ r = 0;
+ memcpy(data, buf, len);
+exit:
+ kfree(buf);
+ return r;
+}
+
+#define urb_dev(urb) (&(urb)->dev->dev)
+
+static inline void handle_regs_int_override(struct urb *urb)
+{
+ struct zd_usb *usb = urb->context;
+ struct zd_usb_interrupt *intr = &usb->intr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&intr->lock, flags);
+ if (atomic_read(&intr->read_regs_enabled)) {
+ atomic_set(&intr->read_regs_enabled, 0);
+ intr->read_regs_int_overridden = 1;
+ complete(&intr->read_regs.completion);
+ }
+ spin_unlock_irqrestore(&intr->lock, flags);
+}
+
+static inline void handle_regs_int(struct urb *urb)
+{
+ struct zd_usb *usb = urb->context;
+ struct zd_usb_interrupt *intr = &usb->intr;
+ unsigned long flags;
+ int len;
+ u16 int_num;
+
+ spin_lock_irqsave(&intr->lock, flags);
+
+ int_num = le16_to_cpu(*(__le16 *)(urb->transfer_buffer+2));
+ if (int_num == CR_INTERRUPT) {
+ struct zd_mac *mac = zd_hw_mac(zd_usb_to_hw(urb->context));
+ spin_lock(&mac->lock);
+ memcpy(&mac->intr_buffer, urb->transfer_buffer,
+ USB_MAX_EP_INT_BUFFER);
+ spin_unlock(&mac->lock);
+ schedule_work(&mac->process_intr);
+ } else if (atomic_read(&intr->read_regs_enabled)) {
+ len = urb->actual_length;
+ intr->read_regs.length = urb->actual_length;
+ if (len > sizeof(intr->read_regs.buffer))
+ len = sizeof(intr->read_regs.buffer);
+
+ memcpy(intr->read_regs.buffer, urb->transfer_buffer, len);
+
+ /* Sometimes USB_INT_ID_REGS is not overridden, but comes after
+ * USB_INT_ID_RETRY_FAILED. Read-reg retry then gets this
+ * delayed USB_INT_ID_REGS, but leaves USB_INT_ID_REGS of
+ * retry unhandled. Next read-reg command then might catch
+ * this wrong USB_INT_ID_REGS. Fix by ignoring wrong reads.
+ */
+ if (!check_read_regs(usb, intr->read_regs.req,
+ intr->read_regs.req_count))
+ goto out;
+
+ atomic_set(&intr->read_regs_enabled, 0);
+ intr->read_regs_int_overridden = 0;
+ complete(&intr->read_regs.completion);
+
+ goto out;
+ }
+
+out:
+ spin_unlock_irqrestore(&intr->lock, flags);
+
+ /* CR_INTERRUPT might override read_reg too. */
+ if (int_num == CR_INTERRUPT && atomic_read(&intr->read_regs_enabled))
+ handle_regs_int_override(urb);
+}
+
+static void int_urb_complete(struct urb *urb)
+{
+ int r;
+ struct usb_int_header *hdr;
+ struct zd_usb *usb;
+ struct zd_usb_interrupt *intr;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ESHUTDOWN:
+ case -EINVAL:
+ case -ENODEV:
+ case -ENOENT:
+ case -ECONNRESET:
+ case -EPIPE:
+ dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+ return;
+ default:
+ dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+ goto resubmit;
+ }
+
+ if (urb->actual_length < sizeof(hdr)) {
+ dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb);
+ goto resubmit;
+ }
+
+ hdr = urb->transfer_buffer;
+ if (hdr->type != USB_INT_TYPE) {
+ dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb);
+ goto resubmit;
+ }
+
+ /* USB_INT_ID_RETRY_FAILED triggered by tx-urb submit can override
+ * pending USB_INT_ID_REGS causing read command timeout.
+ */
+ usb = urb->context;
+ intr = &usb->intr;
+ if (hdr->id != USB_INT_ID_REGS && atomic_read(&intr->read_regs_enabled))
+ handle_regs_int_override(urb);
+
+ switch (hdr->id) {
+ case USB_INT_ID_REGS:
+ handle_regs_int(urb);
+ break;
+ case USB_INT_ID_RETRY_FAILED:
+ zd_mac_tx_failed(urb);
+ break;
+ default:
+ dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb,
+ (unsigned int)hdr->id);
+ goto resubmit;
+ }
+
+resubmit:
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r) {
+ dev_dbg_f(urb_dev(urb), "error: resubmit urb %p err code %d\n",
+ urb, r);
+ /* TODO: add worker to reset intr->urb */
+ }
+ return;
+}
+
+static inline int int_urb_interval(struct usb_device *udev)
+{
+ switch (udev->speed) {
+ case USB_SPEED_HIGH:
+ return 4;
+ case USB_SPEED_LOW:
+ return 10;
+ case USB_SPEED_FULL:
+ default:
+ return 1;
+ }
+}
+
+static inline int usb_int_enabled(struct zd_usb *usb)
+{
+ unsigned long flags;
+ struct zd_usb_interrupt *intr = &usb->intr;
+ struct urb *urb;
+
+ spin_lock_irqsave(&intr->lock, flags);
+ urb = intr->urb;
+ spin_unlock_irqrestore(&intr->lock, flags);
+ return urb != NULL;
+}
+
+int zd_usb_enable_int(struct zd_usb *usb)
+{
+ int r;
+ struct usb_device *udev = zd_usb_to_usbdev(usb);
+ struct zd_usb_interrupt *intr = &usb->intr;
+ struct urb *urb;
+
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ ZD_ASSERT(!irqs_disabled());
+ spin_lock_irq(&intr->lock);
+ if (intr->urb) {
+ spin_unlock_irq(&intr->lock);
+ r = 0;
+ goto error_free_urb;
+ }
+ intr->urb = urb;
+ spin_unlock_irq(&intr->lock);
+
+ r = -ENOMEM;
+ intr->buffer = usb_alloc_coherent(udev, USB_MAX_EP_INT_BUFFER,
+ GFP_KERNEL, &intr->buffer_dma);
+ if (!intr->buffer) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "couldn't allocate transfer_buffer\n");
+ goto error_set_urb_null;
+ }
+
+ usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN),
+ intr->buffer, USB_MAX_EP_INT_BUFFER,
+ int_urb_complete, usb,
+ intr->interval);
+ urb->transfer_dma = intr->buffer_dma;
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb);
+ r = usb_submit_urb(urb, GFP_KERNEL);
+ if (r) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "Couldn't submit urb. Error number %d\n", r);
+ goto error;
+ }
+
+ return 0;
+error:
+ usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER,
+ intr->buffer, intr->buffer_dma);
+error_set_urb_null:
+ spin_lock_irq(&intr->lock);
+ intr->urb = NULL;
+ spin_unlock_irq(&intr->lock);
+error_free_urb:
+ usb_free_urb(urb);
+out:
+ return r;
+}
+
+void zd_usb_disable_int(struct zd_usb *usb)
+{
+ unsigned long flags;
+ struct usb_device *udev = zd_usb_to_usbdev(usb);
+ struct zd_usb_interrupt *intr = &usb->intr;
+ struct urb *urb;
+ void *buffer;
+ dma_addr_t buffer_dma;
+
+ spin_lock_irqsave(&intr->lock, flags);
+ urb = intr->urb;
+ if (!urb) {
+ spin_unlock_irqrestore(&intr->lock, flags);
+ return;
+ }
+ intr->urb = NULL;
+ buffer = intr->buffer;
+ buffer_dma = intr->buffer_dma;
+ intr->buffer = NULL;
+ spin_unlock_irqrestore(&intr->lock, flags);
+
+ usb_kill_urb(urb);
+ dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb);
+ usb_free_urb(urb);
+
+ usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER, buffer, buffer_dma);
+}
+
+static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer,
+ unsigned int length)
+{
+ int i;
+ const struct rx_length_info *length_info;
+
+ if (length < sizeof(struct rx_length_info)) {
+ /* It's not a complete packet anyhow. */
+ dev_dbg_f(zd_usb_dev(usb), "invalid, small RX packet : %d\n",
+ length);
+ return;
+ }
+ length_info = (struct rx_length_info *)
+ (buffer + length - sizeof(struct rx_length_info));
+
+ /* It might be that three frames are merged into a single URB
+ * transaction. We have to check for the length info tag.
+ *
+ * While testing we discovered that length_info might be unaligned,
+ * because if USB transactions are merged, the last packet will not
+ * be padded. Unaligned access might also happen if the length_info
+ * structure is not present.
+ */
+ if (get_unaligned_le16(&length_info->tag) == RX_LENGTH_INFO_TAG)
+ {
+ unsigned int l, k, n;
+ for (i = 0, l = 0;; i++) {
+ k = get_unaligned_le16(&length_info->length[i]);
+ if (k == 0)
+ return;
+ n = l+k;
+ if (n > length)
+ return;
+ zd_mac_rx(zd_usb_to_hw(usb), buffer+l, k);
+ if (i >= 2)
+ return;
+ l = (n+3) & ~3;
+ }
+ } else {
+ zd_mac_rx(zd_usb_to_hw(usb), buffer, length);
+ }
+}
+
+static void rx_urb_complete(struct urb *urb)
+{
+ int r;
+ struct zd_usb *usb;
+ struct zd_usb_rx *rx;
+ const u8 *buffer;
+ unsigned int length;
+ unsigned long flags;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ESHUTDOWN:
+ case -EINVAL:
+ case -ENODEV:
+ case -ENOENT:
+ case -ECONNRESET:
+ case -EPIPE:
+ dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+ return;
+ default:
+ dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+ goto resubmit;
+ }
+
+ buffer = urb->transfer_buffer;
+ length = urb->actual_length;
+ usb = urb->context;
+ rx = &usb->rx;
+
+ tasklet_schedule(&rx->reset_timer_tasklet);
+
+ if (length%rx->usb_packet_size > rx->usb_packet_size-4) {
+ /* If there is an old first fragment, we don't care. */
+ dev_dbg_f(urb_dev(urb), "*** first fragment ***\n");
+ ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment));
+ spin_lock_irqsave(&rx->lock, flags);
+ memcpy(rx->fragment, buffer, length);
+ rx->fragment_length = length;
+ spin_unlock_irqrestore(&rx->lock, flags);
+ goto resubmit;
+ }
+
+ spin_lock_irqsave(&rx->lock, flags);
+ if (rx->fragment_length > 0) {
+ /* We are on a second fragment, we believe */
+ ZD_ASSERT(length + rx->fragment_length <=
+ ARRAY_SIZE(rx->fragment));
+ dev_dbg_f(urb_dev(urb), "*** second fragment ***\n");
+ memcpy(rx->fragment+rx->fragment_length, buffer, length);
+ handle_rx_packet(usb, rx->fragment,
+ rx->fragment_length + length);
+ rx->fragment_length = 0;
+ spin_unlock_irqrestore(&rx->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&rx->lock, flags);
+ handle_rx_packet(usb, buffer, length);
+ }
+
+resubmit:
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r)
+ dev_dbg_f(urb_dev(urb), "urb %p resubmit error %d\n", urb, r);
+}
+
+static struct urb *alloc_rx_urb(struct zd_usb *usb)
+{
+ struct usb_device *udev = zd_usb_to_usbdev(usb);
+ struct urb *urb;
+ void *buffer;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return NULL;
+ buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buffer) {
+ usb_free_urb(urb);
+ return NULL;
+ }
+
+ usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
+ buffer, USB_MAX_RX_SIZE,
+ rx_urb_complete, usb);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ return urb;
+}
+
+static void free_rx_urb(struct urb *urb)
+{
+ if (!urb)
+ return;
+ usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+ usb_free_urb(urb);
+}
+
+static int __zd_usb_enable_rx(struct zd_usb *usb)
+{
+ int i, r;
+ struct zd_usb_rx *rx = &usb->rx;
+ struct urb **urbs;
+
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+
+ r = -ENOMEM;
+ urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
+ if (!urbs)
+ goto error;
+ for (i = 0; i < RX_URBS_COUNT; i++) {
+ urbs[i] = alloc_rx_urb(usb);
+ if (!urbs[i])
+ goto error;
+ }
+
+ ZD_ASSERT(!irqs_disabled());
+ spin_lock_irq(&rx->lock);
+ if (rx->urbs) {
+ spin_unlock_irq(&rx->lock);
+ r = 0;
+ goto error;
+ }
+ rx->urbs = urbs;
+ rx->urbs_count = RX_URBS_COUNT;
+ spin_unlock_irq(&rx->lock);
+
+ for (i = 0; i < RX_URBS_COUNT; i++) {
+ r = usb_submit_urb(urbs[i], GFP_KERNEL);
+ if (r)
+ goto error_submit;
+ }
+
+ return 0;
+error_submit:
+ for (i = 0; i < RX_URBS_COUNT; i++) {
+ usb_kill_urb(urbs[i]);
+ }
+ spin_lock_irq(&rx->lock);
+ rx->urbs = NULL;
+ rx->urbs_count = 0;
+ spin_unlock_irq(&rx->lock);
+error:
+ if (urbs) {
+ for (i = 0; i < RX_URBS_COUNT; i++)
+ free_rx_urb(urbs[i]);
+ }
+ return r;
+}
+
+int zd_usb_enable_rx(struct zd_usb *usb)
+{
+ int r;
+ struct zd_usb_rx *rx = &usb->rx;
+
+ mutex_lock(&rx->setup_mutex);
+ r = __zd_usb_enable_rx(usb);
+ mutex_unlock(&rx->setup_mutex);
+
+ zd_usb_reset_rx_idle_timer(usb);
+
+ return r;
+}
+
+static void __zd_usb_disable_rx(struct zd_usb *usb)
+{
+ int i;
+ unsigned long flags;
+ struct urb **urbs;
+ unsigned int count;
+ struct zd_usb_rx *rx = &usb->rx;
+
+ spin_lock_irqsave(&rx->lock, flags);
+ urbs = rx->urbs;
+ count = rx->urbs_count;
+ spin_unlock_irqrestore(&rx->lock, flags);
+ if (!urbs)
+ return;
+
+ for (i = 0; i < count; i++) {
+ usb_kill_urb(urbs[i]);
+ free_rx_urb(urbs[i]);
+ }
+ kfree(urbs);
+
+ spin_lock_irqsave(&rx->lock, flags);
+ rx->urbs = NULL;
+ rx->urbs_count = 0;
+ spin_unlock_irqrestore(&rx->lock, flags);
+}
+
+void zd_usb_disable_rx(struct zd_usb *usb)
+{
+ struct zd_usb_rx *rx = &usb->rx;
+
+ mutex_lock(&rx->setup_mutex);
+ __zd_usb_disable_rx(usb);
+ mutex_unlock(&rx->setup_mutex);
+
+ tasklet_kill(&rx->reset_timer_tasklet);
+ cancel_delayed_work_sync(&rx->idle_work);
+}
+
+static void zd_usb_reset_rx(struct zd_usb *usb)
+{
+ bool do_reset;
+ struct zd_usb_rx *rx = &usb->rx;
+ unsigned long flags;
+
+ mutex_lock(&rx->setup_mutex);
+
+ spin_lock_irqsave(&rx->lock, flags);
+ do_reset = rx->urbs != NULL;
+ spin_unlock_irqrestore(&rx->lock, flags);
+
+ if (do_reset) {
+ __zd_usb_disable_rx(usb);
+ __zd_usb_enable_rx(usb);
+ }
+
+ mutex_unlock(&rx->setup_mutex);
+
+ if (do_reset)
+ zd_usb_reset_rx_idle_timer(usb);
+}
+
+/**
+ * zd_usb_disable_tx - disable transmission
+ * @usb: the zd1211rw-private USB structure
+ *
+ * Frees all URBs in the free list and marks the transmission as disabled.
+ */
+void zd_usb_disable_tx(struct zd_usb *usb)
+{
+ struct zd_usb_tx *tx = &usb->tx;
+ unsigned long flags;
+
+ atomic_set(&tx->enabled, 0);
+
+ /* kill all submitted tx-urbs */
+ usb_kill_anchored_urbs(&tx->submitted);
+
+ spin_lock_irqsave(&tx->lock, flags);
+ WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
+ WARN_ON(tx->submitted_urbs != 0);
+ tx->submitted_urbs = 0;
+ spin_unlock_irqrestore(&tx->lock, flags);
+
+ /* The stopped state is ignored, relying on ieee80211_wake_queues()
+ * in a potentionally following zd_usb_enable_tx().
+ */
+}
+
+/**
+ * zd_usb_enable_tx - enables transmission
+ * @usb: a &struct zd_usb pointer
+ *
+ * This function enables transmission and prepares the &zd_usb_tx data
+ * structure.
+ */
+void zd_usb_enable_tx(struct zd_usb *usb)
+{
+ unsigned long flags;
+ struct zd_usb_tx *tx = &usb->tx;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ atomic_set(&tx->enabled, 1);
+ tx->submitted_urbs = 0;
+ ieee80211_wake_queues(zd_usb_to_hw(usb));
+ tx->stopped = 0;
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void tx_dec_submitted_urbs(struct zd_usb *usb)
+{
+ struct zd_usb_tx *tx = &usb->tx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ --tx->submitted_urbs;
+ if (tx->stopped && tx->submitted_urbs <= ZD_USB_TX_LOW) {
+ ieee80211_wake_queues(zd_usb_to_hw(usb));
+ tx->stopped = 0;
+ }
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+static void tx_inc_submitted_urbs(struct zd_usb *usb)
+{
+ struct zd_usb_tx *tx = &usb->tx;
+ unsigned long flags;
+
+ spin_lock_irqsave(&tx->lock, flags);
+ ++tx->submitted_urbs;
+ if (!tx->stopped && tx->submitted_urbs > ZD_USB_TX_HIGH) {
+ ieee80211_stop_queues(zd_usb_to_hw(usb));
+ tx->stopped = 1;
+ }
+ spin_unlock_irqrestore(&tx->lock, flags);
+}
+
+/**
+ * tx_urb_complete - completes the execution of an URB
+ * @urb: a URB
+ *
+ * This function is called if the URB has been transferred to a device or an
+ * error has happened.
+ */
+static void tx_urb_complete(struct urb *urb)
+{
+ int r;
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *info;
+ struct zd_usb *usb;
+ struct zd_usb_tx *tx;
+
+ skb = (struct sk_buff *)urb->context;
+ info = IEEE80211_SKB_CB(skb);
+ /*
+ * grab 'usb' pointer before handing off the skb (since
+ * it might be freed by zd_mac_tx_to_dev or mac80211)
+ */
+ usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb;
+ tx = &usb->tx;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ESHUTDOWN:
+ case -EINVAL:
+ case -ENODEV:
+ case -ENOENT:
+ case -ECONNRESET:
+ case -EPIPE:
+ dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+ break;
+ default:
+ dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
+ goto resubmit;
+ }
+free_urb:
+ skb_unlink(skb, &usb->tx.submitted_skbs);
+ zd_mac_tx_to_dev(skb, urb->status);
+ usb_free_urb(urb);
+ tx_dec_submitted_urbs(usb);
+ return;
+resubmit:
+ usb_anchor_urb(urb, &tx->submitted);
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r) {
+ usb_unanchor_urb(urb);
+ dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r);
+ goto free_urb;
+ }
+}
+
+/**
+ * zd_usb_tx: initiates transfer of a frame of the device
+ *
+ * @usb: the zd1211rw-private USB structure
+ * @skb: a &struct sk_buff pointer
+ *
+ * This function tranmits a frame to the device. It doesn't wait for
+ * completion. The frame must contain the control set and have all the
+ * control set information available.
+ *
+ * The function returns 0 if the transfer has been successfully initiated.
+ */
+int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
+{
+ int r;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct usb_device *udev = zd_usb_to_usbdev(usb);
+ struct urb *urb;
+ struct zd_usb_tx *tx = &usb->tx;
+
+ if (!atomic_read(&tx->enabled)) {
+ r = -ENOENT;
+ goto out;
+ }
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ r = -ENOMEM;
+ goto out;
+ }
+
+ usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
+ skb->data, skb->len, tx_urb_complete, skb);
+
+ info->rate_driver_data[1] = (void *)jiffies;
+ skb_queue_tail(&tx->submitted_skbs, skb);
+ usb_anchor_urb(urb, &tx->submitted);
+
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r) {
+ dev_dbg_f(zd_usb_dev(usb), "error submit urb %p %d\n", urb, r);
+ usb_unanchor_urb(urb);
+ skb_unlink(skb, &tx->submitted_skbs);
+ goto error;
+ }
+ tx_inc_submitted_urbs(usb);
+ return 0;
+error:
+ usb_free_urb(urb);
+out:
+ return r;
+}
+
+static bool zd_tx_timeout(struct zd_usb *usb)
+{
+ struct zd_usb_tx *tx = &usb->tx;
+ struct sk_buff_head *q = &tx->submitted_skbs;
+ struct sk_buff *skb, *skbnext;
+ struct ieee80211_tx_info *info;
+ unsigned long flags, trans_start;
+ bool have_timedout = false;
+
+ spin_lock_irqsave(&q->lock, flags);
+ skb_queue_walk_safe(q, skb, skbnext) {
+ info = IEEE80211_SKB_CB(skb);
+ trans_start = (unsigned long)info->rate_driver_data[1];
+
+ if (time_is_before_jiffies(trans_start + ZD_TX_TIMEOUT)) {
+ have_timedout = true;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ return have_timedout;
+}
+
+static void zd_tx_watchdog_handler(struct work_struct *work)
+{
+ struct zd_usb *usb =
+ container_of(work, struct zd_usb, tx.watchdog_work.work);
+ struct zd_usb_tx *tx = &usb->tx;
+
+ if (!atomic_read(&tx->enabled) || !tx->watchdog_enabled)
+ goto out;
+ if (!zd_tx_timeout(usb))
+ goto out;
+
+ /* TX halted, try reset */
+ dev_warn(zd_usb_dev(usb), "TX-stall detected, resetting device...");
+
+ usb_queue_reset_device(usb->intf);
+
+ /* reset will stop this worker, don't rearm */
+ return;
+out:
+ queue_delayed_work(zd_workqueue, &tx->watchdog_work,
+ ZD_TX_WATCHDOG_INTERVAL);
+}
+
+void zd_tx_watchdog_enable(struct zd_usb *usb)
+{
+ struct zd_usb_tx *tx = &usb->tx;
+
+ if (!tx->watchdog_enabled) {
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+ queue_delayed_work(zd_workqueue, &tx->watchdog_work,
+ ZD_TX_WATCHDOG_INTERVAL);
+ tx->watchdog_enabled = 1;
+ }
+}
+
+void zd_tx_watchdog_disable(struct zd_usb *usb)
+{
+ struct zd_usb_tx *tx = &usb->tx;
+
+ if (tx->watchdog_enabled) {
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+ tx->watchdog_enabled = 0;
+ cancel_delayed_work_sync(&tx->watchdog_work);
+ }
+}
+
+static void zd_rx_idle_timer_handler(struct work_struct *work)
+{
+ struct zd_usb *usb =
+ container_of(work, struct zd_usb, rx.idle_work.work);
+ struct zd_mac *mac = zd_usb_to_mac(usb);
+
+ if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags))
+ return;
+
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+
+ /* 30 seconds since last rx, reset rx */
+ zd_usb_reset_rx(usb);
+}
+
+static void zd_usb_reset_rx_idle_timer_tasklet(struct tasklet_struct *t)
+{
+ struct zd_usb *usb = from_tasklet(usb, t, rx.reset_timer_tasklet);
+
+ zd_usb_reset_rx_idle_timer(usb);
+}
+
+void zd_usb_reset_rx_idle_timer(struct zd_usb *usb)
+{
+ struct zd_usb_rx *rx = &usb->rx;
+
+ mod_delayed_work(zd_workqueue, &rx->idle_work, ZD_RX_IDLE_INTERVAL);
+}
+
+static inline void init_usb_interrupt(struct zd_usb *usb)
+{
+ struct zd_usb_interrupt *intr = &usb->intr;
+
+ spin_lock_init(&intr->lock);
+ intr->interval = int_urb_interval(zd_usb_to_usbdev(usb));
+ init_completion(&intr->read_regs.completion);
+ atomic_set(&intr->read_regs_enabled, 0);
+ intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT);
+}
+
+static inline void init_usb_rx(struct zd_usb *usb)
+{
+ struct zd_usb_rx *rx = &usb->rx;
+
+ spin_lock_init(&rx->lock);
+ mutex_init(&rx->setup_mutex);
+ if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) {
+ rx->usb_packet_size = 512;
+ } else {
+ rx->usb_packet_size = 64;
+ }
+ ZD_ASSERT(rx->fragment_length == 0);
+ INIT_DELAYED_WORK(&rx->idle_work, zd_rx_idle_timer_handler);
+ rx->reset_timer_tasklet.func = (void (*))
+ zd_usb_reset_rx_idle_timer_tasklet;
+ rx->reset_timer_tasklet.data = (unsigned long)&rx->reset_timer_tasklet;
+}
+
+static inline void init_usb_tx(struct zd_usb *usb)
+{
+ struct zd_usb_tx *tx = &usb->tx;
+
+ spin_lock_init(&tx->lock);
+ atomic_set(&tx->enabled, 0);
+ tx->stopped = 0;
+ skb_queue_head_init(&tx->submitted_skbs);
+ init_usb_anchor(&tx->submitted);
+ tx->submitted_urbs = 0;
+ tx->watchdog_enabled = 0;
+ INIT_DELAYED_WORK(&tx->watchdog_work, zd_tx_watchdog_handler);
+}
+
+void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw,
+ struct usb_interface *intf)
+{
+ memset(usb, 0, sizeof(*usb));
+ usb->intf = usb_get_intf(intf);
+ usb_set_intfdata(usb->intf, hw);
+ init_usb_anchor(&usb->submitted_cmds);
+ init_usb_interrupt(usb);
+ init_usb_tx(usb);
+ init_usb_rx(usb);
+}
+
+void zd_usb_clear(struct zd_usb *usb)
+{
+ usb_set_intfdata(usb->intf, NULL);
+ usb_put_intf(usb->intf);
+ ZD_MEMCLEAR(usb, sizeof(*usb));
+ /* FIXME: usb_interrupt, usb_tx, usb_rx? */
+}
+
+static const char *speed(enum usb_device_speed speed)
+{
+ switch (speed) {
+ case USB_SPEED_LOW:
+ return "low";
+ case USB_SPEED_FULL:
+ return "full";
+ case USB_SPEED_HIGH:
+ return "high";
+ default:
+ return "unknown speed";
+ }
+}
+
+static int scnprint_id(struct usb_device *udev, char *buffer, size_t size)
+{
+ return scnprintf(buffer, size, "%04hx:%04hx v%04hx %s",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ get_bcdDevice(udev),
+ speed(udev->speed));
+}
+
+int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size)
+{
+ struct usb_device *udev = interface_to_usbdev(usb->intf);
+ return scnprint_id(udev, buffer, size);
+}
+
+#ifdef DEBUG
+static void print_id(struct usb_device *udev)
+{
+ char buffer[40];
+
+ scnprint_id(udev, buffer, sizeof(buffer));
+ buffer[sizeof(buffer)-1] = 0;
+ dev_dbg_f(&udev->dev, "%s\n", buffer);
+}
+#else
+#define print_id(udev) do { } while (0)
+#endif
+
+static int eject_installer(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_host_interface *iface_desc = intf->cur_altsetting;
+ struct usb_endpoint_descriptor *endpoint;
+ unsigned char *cmd;
+ u8 bulk_out_ep;
+ int r;
+
+ if (iface_desc->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
+ /* Find bulk out endpoint */
+ for (r = 1; r >= 0; r--) {
+ endpoint = &iface_desc->endpoint[r].desc;
+ if (usb_endpoint_dir_out(endpoint) &&
+ usb_endpoint_xfer_bulk(endpoint)) {
+ bulk_out_ep = endpoint->bEndpointAddress;
+ break;
+ }
+ }
+ if (r == -1) {
+ dev_err(&udev->dev,
+ "zd1211rw: Could not find bulk out endpoint\n");
+ return -ENODEV;
+ }
+
+ cmd = kzalloc(31, GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENODEV;
+
+ /* USB bulk command block */
+ cmd[0] = 0x55; /* bulk command signature */
+ cmd[1] = 0x53; /* bulk command signature */
+ cmd[2] = 0x42; /* bulk command signature */
+ cmd[3] = 0x43; /* bulk command signature */
+ cmd[14] = 6; /* command length */
+
+ cmd[15] = 0x1b; /* SCSI command: START STOP UNIT */
+ cmd[19] = 0x2; /* eject disc */
+
+ dev_info(&udev->dev, "Ejecting virtual installer media...\n");
+ r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep),
+ cmd, 31, NULL, 2000);
+ kfree(cmd);
+ if (r)
+ return r;
+
+ /* At this point, the device disconnects and reconnects with the real
+ * ID numbers. */
+
+ usb_set_intfdata(intf, NULL);
+ return 0;
+}
+
+int zd_usb_init_hw(struct zd_usb *usb)
+{
+ int r;
+ struct zd_mac *mac = zd_usb_to_mac(usb);
+
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+
+ r = upload_firmware(usb);
+ if (r) {
+ dev_err(zd_usb_dev(usb),
+ "couldn't load firmware. Error number %d\n", r);
+ return r;
+ }
+
+ r = usb_reset_configuration(zd_usb_to_usbdev(usb));
+ if (r) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "couldn't reset configuration. Error number %d\n", r);
+ return r;
+ }
+
+ r = zd_mac_init_hw(mac->hw);
+ if (r) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "couldn't initialize mac. Error number %d\n", r);
+ return r;
+ }
+
+ usb->initialized = 1;
+ return 0;
+}
+
+static int probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ int r;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct zd_usb *usb;
+ struct ieee80211_hw *hw = NULL;
+
+ print_id(udev);
+
+ if (id->driver_info & DEVICE_INSTALLER)
+ return eject_installer(intf);
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ case USB_SPEED_HIGH:
+ break;
+ default:
+ dev_dbg_f(&intf->dev, "Unknown USB speed\n");
+ r = -ENODEV;
+ goto error;
+ }
+
+ r = usb_reset_device(udev);
+ if (r) {
+ dev_err(&intf->dev,
+ "couldn't reset usb device. Error number %d\n", r);
+ goto error;
+ }
+
+ hw = zd_mac_alloc_hw(intf);
+ if (hw == NULL) {
+ r = -ENOMEM;
+ goto error;
+ }
+
+ usb = &zd_hw_mac(hw)->chip.usb;
+ usb->is_zd1211b = (id->driver_info == DEVICE_ZD1211B) != 0;
+
+ r = zd_mac_preinit_hw(hw);
+ if (r) {
+ dev_dbg_f(&intf->dev,
+ "couldn't initialize mac. Error number %d\n", r);
+ goto error;
+ }
+
+ r = ieee80211_register_hw(hw);
+ if (r) {
+ dev_dbg_f(&intf->dev,
+ "couldn't register device. Error number %d\n", r);
+ goto error;
+ }
+
+ dev_dbg_f(&intf->dev, "successful\n");
+ dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
+ return 0;
+error:
+ usb_reset_device(interface_to_usbdev(intf));
+ if (hw) {
+ zd_mac_clear(zd_hw_mac(hw));
+ ieee80211_free_hw(hw);
+ }
+ return r;
+}
+
+static void disconnect(struct usb_interface *intf)
+{
+ struct ieee80211_hw *hw = zd_intf_to_hw(intf);
+ struct zd_mac *mac;
+ struct zd_usb *usb;
+
+ /* Either something really bad happened, or we're just dealing with
+ * a DEVICE_INSTALLER. */
+ if (hw == NULL)
+ return;
+
+ mac = zd_hw_mac(hw);
+ usb = &mac->chip.usb;
+
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+
+ ieee80211_unregister_hw(hw);
+
+ /* Just in case something has gone wrong! */
+ zd_usb_disable_tx(usb);
+ zd_usb_disable_rx(usb);
+ zd_usb_disable_int(usb);
+
+ /* If the disconnect has been caused by a removal of the
+ * driver module, the reset allows reloading of the driver. If the
+ * reset will not be executed here, the upload of the firmware in the
+ * probe function caused by the reloading of the driver will fail.
+ */
+ usb_reset_device(interface_to_usbdev(intf));
+
+ zd_mac_clear(mac);
+ ieee80211_free_hw(hw);
+ dev_dbg(&intf->dev, "disconnected\n");
+}
+
+static void zd_usb_resume(struct zd_usb *usb)
+{
+ struct zd_mac *mac = zd_usb_to_mac(usb);
+ int r;
+
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+
+ r = zd_op_start(zd_usb_to_hw(usb));
+ if (r < 0) {
+ dev_warn(zd_usb_dev(usb), "Device resume failed "
+ "with error code %d. Retrying...\n", r);
+ if (usb->was_running)
+ set_bit(ZD_DEVICE_RUNNING, &mac->flags);
+ usb_queue_reset_device(usb->intf);
+ return;
+ }
+
+ if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
+ r = zd_restore_settings(mac);
+ if (r < 0) {
+ dev_dbg(zd_usb_dev(usb),
+ "failed to restore settings, %d\n", r);
+ return;
+ }
+ }
+}
+
+static void zd_usb_stop(struct zd_usb *usb)
+{
+ dev_dbg_f(zd_usb_dev(usb), "\n");
+
+ zd_op_stop(zd_usb_to_hw(usb));
+
+ zd_usb_disable_tx(usb);
+ zd_usb_disable_rx(usb);
+ zd_usb_disable_int(usb);
+
+ usb->initialized = 0;
+}
+
+static int pre_reset(struct usb_interface *intf)
+{
+ struct ieee80211_hw *hw = usb_get_intfdata(intf);
+ struct zd_mac *mac;
+ struct zd_usb *usb;
+
+ if (!hw || intf->condition != USB_INTERFACE_BOUND)
+ return 0;
+
+ mac = zd_hw_mac(hw);
+ usb = &mac->chip.usb;
+
+ usb->was_running = test_bit(ZD_DEVICE_RUNNING, &mac->flags);
+
+ zd_usb_stop(usb);
+
+ mutex_lock(&mac->chip.mutex);
+ return 0;
+}
+
+static int post_reset(struct usb_interface *intf)
+{
+ struct ieee80211_hw *hw = usb_get_intfdata(intf);
+ struct zd_mac *mac;
+ struct zd_usb *usb;
+
+ if (!hw || intf->condition != USB_INTERFACE_BOUND)
+ return 0;
+
+ mac = zd_hw_mac(hw);
+ usb = &mac->chip.usb;
+
+ mutex_unlock(&mac->chip.mutex);
+
+ if (usb->was_running)
+ zd_usb_resume(usb);
+ return 0;
+}
+
+static struct usb_driver driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = usb_ids,
+ .probe = probe,
+ .disconnect = disconnect,
+ .pre_reset = pre_reset,
+ .post_reset = post_reset,
+ .disable_hub_initiated_lpm = 1,
+};
+
+struct workqueue_struct *zd_workqueue;
+
+static int __init usb_init(void)
+{
+ int r;
+
+ pr_debug("%s usb_init()\n", driver.name);
+
+ zd_workqueue = create_singlethread_workqueue(driver.name);
+ if (zd_workqueue == NULL) {
+ pr_err("%s couldn't create workqueue\n", driver.name);
+ return -ENOMEM;
+ }
+
+ r = usb_register(&driver);
+ if (r) {
+ destroy_workqueue(zd_workqueue);
+ pr_err("%s usb_register() failed. Error number %d\n",
+ driver.name, r);
+ return r;
+ }
+
+ pr_debug("%s initialized\n", driver.name);
+ return 0;
+}
+
+static void __exit usb_exit(void)
+{
+ pr_debug("%s usb_exit()\n", driver.name);
+ usb_deregister(&driver);
+ destroy_workqueue(zd_workqueue);
+}
+
+module_init(usb_init);
+module_exit(usb_exit);
+
+static int zd_ep_regs_out_msg(struct usb_device *udev, void *data, int len,
+ int *actual_length, int timeout)
+{
+ /* In USB 2.0 mode EP_REGS_OUT endpoint is interrupt type. However in
+ * USB 1.1 mode endpoint is bulk. Select correct type URB by endpoint
+ * descriptor.
+ */
+ struct usb_host_endpoint *ep;
+ unsigned int pipe;
+
+ pipe = usb_sndintpipe(udev, EP_REGS_OUT);
+ ep = usb_pipe_endpoint(udev, pipe);
+ if (!ep)
+ return -EINVAL;
+
+ if (usb_endpoint_xfer_int(&ep->desc)) {
+ return usb_interrupt_msg(udev, pipe, data, len,
+ actual_length, timeout);
+ } else {
+ pipe = usb_sndbulkpipe(udev, EP_REGS_OUT);
+ return usb_bulk_msg(udev, pipe, data, len, actual_length,
+ timeout);
+ }
+}
+
+static void prepare_read_regs_int(struct zd_usb *usb,
+ struct usb_req_read_regs *req,
+ unsigned int count)
+{
+ struct zd_usb_interrupt *intr = &usb->intr;
+
+ spin_lock_irq(&intr->lock);
+ atomic_set(&intr->read_regs_enabled, 1);
+ intr->read_regs.req = req;
+ intr->read_regs.req_count = count;
+ reinit_completion(&intr->read_regs.completion);
+ spin_unlock_irq(&intr->lock);
+}
+
+static void disable_read_regs_int(struct zd_usb *usb)
+{
+ struct zd_usb_interrupt *intr = &usb->intr;
+
+ spin_lock_irq(&intr->lock);
+ atomic_set(&intr->read_regs_enabled, 0);
+ spin_unlock_irq(&intr->lock);
+}
+
+static bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req,
+ unsigned int count)
+{
+ int i;
+ struct zd_usb_interrupt *intr = &usb->intr;
+ struct read_regs_int *rr = &intr->read_regs;
+ struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer;
+
+ /* The created block size seems to be larger than expected.
+ * However results appear to be correct.
+ */
+ if (rr->length < struct_size(regs, regs, count)) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error: actual length %d less than expected %zu\n",
+ rr->length, struct_size(regs, regs, count));
+ return false;
+ }
+
+ if (rr->length > sizeof(rr->buffer)) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error: actual length %d exceeds buffer size %zu\n",
+ rr->length, sizeof(rr->buffer));
+ return false;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct reg_data *rd = &regs->regs[i];
+ if (rd->addr != req->addr[i]) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "rd[%d] addr %#06hx expected %#06hx\n", i,
+ le16_to_cpu(rd->addr),
+ le16_to_cpu(req->addr[i]));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int get_results(struct zd_usb *usb, u16 *values,
+ struct usb_req_read_regs *req, unsigned int count,
+ bool *retry)
+{
+ int r;
+ int i;
+ struct zd_usb_interrupt *intr = &usb->intr;
+ struct read_regs_int *rr = &intr->read_regs;
+ struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer;
+
+ spin_lock_irq(&intr->lock);
+
+ r = -EIO;
+
+ /* Read failed because firmware bug? */
+ *retry = !!intr->read_regs_int_overridden;
+ if (*retry)
+ goto error_unlock;
+
+ if (!check_read_regs(usb, req, count)) {
+ dev_dbg_f(zd_usb_dev(usb), "error: invalid read regs\n");
+ goto error_unlock;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct reg_data *rd = &regs->regs[i];
+ values[i] = le16_to_cpu(rd->value);
+ }
+
+ r = 0;
+error_unlock:
+ spin_unlock_irq(&intr->lock);
+ return r;
+}
+
+int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
+ const zd_addr_t *addresses, unsigned int count)
+{
+ int r, i, req_len, actual_req_len, try_count = 0;
+ struct usb_device *udev;
+ struct usb_req_read_regs *req = NULL;
+ unsigned long timeout;
+ bool retry = false;
+
+ if (count < 1) {
+ dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n");
+ return -EINVAL;
+ }
+ if (count > USB_MAX_IOREAD16_COUNT) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error: count %u exceeds possible max %u\n",
+ count, USB_MAX_IOREAD16_COUNT);
+ return -EINVAL;
+ }
+ if (!usb_int_enabled(usb)) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error: usb interrupt not enabled\n");
+ return -EWOULDBLOCK;
+ }
+
+ ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
+ BUILD_BUG_ON(sizeof(struct usb_req_read_regs) + USB_MAX_IOREAD16_COUNT *
+ sizeof(__le16) > sizeof(usb->req_buf));
+ BUG_ON(sizeof(struct usb_req_read_regs) + count * sizeof(__le16) >
+ sizeof(usb->req_buf));
+
+ req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16);
+ req = (void *)usb->req_buf;
+
+ req->id = cpu_to_le16(USB_REQ_READ_REGS);
+ for (i = 0; i < count; i++)
+ req->addr[i] = cpu_to_le16((u16)addresses[i]);
+
+retry_read:
+ try_count++;
+ udev = zd_usb_to_usbdev(usb);
+ prepare_read_regs_int(usb, req, count);
+ r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/);
+ if (r) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error in zd_ep_regs_out_msg(). Error number %d\n", r);
+ goto error;
+ }
+ if (req_len != actual_req_len) {
+ dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()\n"
+ " req_len %d != actual_req_len %d\n",
+ req_len, actual_req_len);
+ r = -EIO;
+ goto error;
+ }
+
+ timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion,
+ msecs_to_jiffies(50));
+ if (!timeout) {
+ disable_read_regs_int(usb);
+ dev_dbg_f(zd_usb_dev(usb), "read timed out\n");
+ r = -ETIMEDOUT;
+ goto error;
+ }
+
+ r = get_results(usb, values, req, count, &retry);
+ if (retry && try_count < 20) {
+ dev_dbg_f(zd_usb_dev(usb), "read retry, tries so far: %d\n",
+ try_count);
+ goto retry_read;
+ }
+error:
+ return r;
+}
+
+static void iowrite16v_urb_complete(struct urb *urb)
+{
+ struct zd_usb *usb = urb->context;
+
+ if (urb->status && !usb->cmd_error)
+ usb->cmd_error = urb->status;
+
+ if (!usb->cmd_error &&
+ urb->actual_length != urb->transfer_buffer_length)
+ usb->cmd_error = -EIO;
+}
+
+static int zd_submit_waiting_urb(struct zd_usb *usb, bool last)
+{
+ int r = 0;
+ struct urb *urb = usb->urb_async_waiting;
+
+ if (!urb)
+ return 0;
+
+ usb->urb_async_waiting = NULL;
+
+ if (!last)
+ urb->transfer_flags |= URB_NO_INTERRUPT;
+
+ usb_anchor_urb(urb, &usb->submitted_cmds);
+ r = usb_submit_urb(urb, GFP_KERNEL);
+ if (r) {
+ usb_unanchor_urb(urb);
+ dev_dbg_f(zd_usb_dev(usb),
+ "error in usb_submit_urb(). Error number %d\n", r);
+ goto error;
+ }
+
+ /* fall-through with r == 0 */
+error:
+ usb_free_urb(urb);
+ return r;
+}
+
+void zd_usb_iowrite16v_async_start(struct zd_usb *usb)
+{
+ ZD_ASSERT(usb_anchor_empty(&usb->submitted_cmds));
+ ZD_ASSERT(usb->urb_async_waiting == NULL);
+ ZD_ASSERT(!usb->in_async);
+
+ ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
+
+ usb->in_async = 1;
+ usb->cmd_error = 0;
+ usb->urb_async_waiting = NULL;
+}
+
+int zd_usb_iowrite16v_async_end(struct zd_usb *usb, unsigned int timeout)
+{
+ int r;
+
+ ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
+ ZD_ASSERT(usb->in_async);
+
+ /* Submit last iowrite16v URB */
+ r = zd_submit_waiting_urb(usb, true);
+ if (r) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error in zd_submit_waiting_usb(). "
+ "Error number %d\n", r);
+
+ usb_kill_anchored_urbs(&usb->submitted_cmds);
+ goto error;
+ }
+
+ if (timeout)
+ timeout = usb_wait_anchor_empty_timeout(&usb->submitted_cmds,
+ timeout);
+ if (!timeout) {
+ usb_kill_anchored_urbs(&usb->submitted_cmds);
+ if (usb->cmd_error == -ENOENT) {
+ dev_dbg_f(zd_usb_dev(usb), "timed out");
+ r = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ r = usb->cmd_error;
+error:
+ usb->in_async = 0;
+ return r;
+}
+
+int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
+ unsigned int count)
+{
+ int r;
+ struct usb_device *udev;
+ struct usb_req_write_regs *req = NULL;
+ int i, req_len;
+ struct urb *urb;
+ struct usb_host_endpoint *ep;
+
+ ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
+ ZD_ASSERT(usb->in_async);
+
+ if (count == 0)
+ return 0;
+ if (count > USB_MAX_IOWRITE16_COUNT) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error: count %u exceeds possible max %u\n",
+ count, USB_MAX_IOWRITE16_COUNT);
+ return -EINVAL;
+ }
+
+ udev = zd_usb_to_usbdev(usb);
+
+ ep = usb_pipe_endpoint(udev, usb_sndintpipe(udev, EP_REGS_OUT));
+ if (!ep)
+ return -ENOENT;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ req_len = struct_size(req, reg_writes, count);
+ req = kmalloc(req_len, GFP_KERNEL);
+ if (!req) {
+ r = -ENOMEM;
+ goto error;
+ }
+
+ req->id = cpu_to_le16(USB_REQ_WRITE_REGS);
+ for (i = 0; i < count; i++) {
+ struct reg_data *rw = &req->reg_writes[i];
+ rw->addr = cpu_to_le16((u16)ioreqs[i].addr);
+ rw->value = cpu_to_le16(ioreqs[i].value);
+ }
+
+ /* In USB 2.0 mode endpoint is interrupt type. However in USB 1.1 mode
+ * endpoint is bulk. Select correct type URB by endpoint descriptor.
+ */
+ if (usb_endpoint_xfer_int(&ep->desc))
+ usb_fill_int_urb(urb, udev, usb_sndintpipe(udev, EP_REGS_OUT),
+ req, req_len, iowrite16v_urb_complete, usb,
+ ep->desc.bInterval);
+ else
+ usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
+ req, req_len, iowrite16v_urb_complete, usb);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ /* Submit previous URB */
+ r = zd_submit_waiting_urb(usb, false);
+ if (r) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error in zd_submit_waiting_usb(). "
+ "Error number %d\n", r);
+ goto error;
+ }
+
+ /* Delay submit so that URB_NO_INTERRUPT flag can be set for all URBs
+ * of currect batch except for very last.
+ */
+ usb->urb_async_waiting = urb;
+ return 0;
+error:
+ usb_free_urb(urb);
+ return r;
+}
+
+int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
+ unsigned int count)
+{
+ int r;
+
+ zd_usb_iowrite16v_async_start(usb);
+ r = zd_usb_iowrite16v_async(usb, ioreqs, count);
+ if (r) {
+ zd_usb_iowrite16v_async_end(usb, 0);
+ return r;
+ }
+ return zd_usb_iowrite16v_async_end(usb, 50 /* ms */);
+}
+
+int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
+{
+ int r;
+ struct usb_device *udev;
+ struct usb_req_rfwrite *req = NULL;
+ int i, req_len, actual_req_len;
+ u16 bit_value_template;
+
+ if (bits < USB_MIN_RFWRITE_BIT_COUNT) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error: bits %d are smaller than"
+ " USB_MIN_RFWRITE_BIT_COUNT %d\n",
+ bits, USB_MIN_RFWRITE_BIT_COUNT);
+ return -EINVAL;
+ }
+ if (bits > USB_MAX_RFWRITE_BIT_COUNT) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error: bits %d exceed USB_MAX_RFWRITE_BIT_COUNT %d\n",
+ bits, USB_MAX_RFWRITE_BIT_COUNT);
+ return -EINVAL;
+ }
+#ifdef DEBUG
+ if (value & (~0UL << bits)) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error: value %#09x has bits >= %d set\n",
+ value, bits);
+ return -EINVAL;
+ }
+#endif /* DEBUG */
+
+ dev_dbg_f(zd_usb_dev(usb), "value %#09x bits %d\n", value, bits);
+
+ r = zd_usb_ioread16(usb, &bit_value_template, ZD_CR203);
+ if (r) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error %d: Couldn't read ZD_CR203\n", r);
+ return r;
+ }
+ bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA);
+
+ ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
+ BUILD_BUG_ON(sizeof(struct usb_req_rfwrite) +
+ USB_MAX_RFWRITE_BIT_COUNT * sizeof(__le16) >
+ sizeof(usb->req_buf));
+ BUG_ON(sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16) >
+ sizeof(usb->req_buf));
+
+ req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16);
+ req = (void *)usb->req_buf;
+
+ req->id = cpu_to_le16(USB_REQ_WRITE_RF);
+ /* 1: 3683a, but not used in ZYDAS driver */
+ req->value = cpu_to_le16(2);
+ req->bits = cpu_to_le16(bits);
+
+ for (i = 0; i < bits; i++) {
+ u16 bv = bit_value_template;
+ if (value & (1 << (bits-1-i)))
+ bv |= RF_DATA;
+ req->bit_values[i] = cpu_to_le16(bv);
+ }
+
+ udev = zd_usb_to_usbdev(usb);
+ r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/);
+ if (r) {
+ dev_dbg_f(zd_usb_dev(usb),
+ "error in zd_ep_regs_out_msg(). Error number %d\n", r);
+ goto out;
+ }
+ if (req_len != actual_req_len) {
+ dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()"
+ " req_len %d != actual_req_len %d\n",
+ req_len, actual_req_len);
+ r = -EIO;
+ goto out;
+ }
+
+ /* FALL-THROUGH with r == 0 */
+out:
+ return r;
+}
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.h b/drivers/net/wireless/zydas/zd1211rw/zd_usb.h
new file mode 100644
index 000000000..8f03b09a6
--- /dev/null
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.h
@@ -0,0 +1,280 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ZD1211 USB-WLAN driver for Linux
+ *
+ * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
+ * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
+ */
+
+#ifndef _ZD_USB_H
+#define _ZD_USB_H
+
+#include <linux/completion.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+
+#include "zd_def.h"
+
+#define ZD_USB_TX_HIGH 5
+#define ZD_USB_TX_LOW 2
+
+#define ZD_TX_TIMEOUT (HZ * 5)
+#define ZD_TX_WATCHDOG_INTERVAL round_jiffies_relative(HZ)
+#define ZD_RX_IDLE_INTERVAL round_jiffies_relative(30 * HZ)
+
+enum devicetype {
+ DEVICE_ZD1211 = 0,
+ DEVICE_ZD1211B = 1,
+ DEVICE_INSTALLER = 2,
+};
+
+enum endpoints {
+ EP_CTRL = 0,
+ EP_DATA_OUT = 1,
+ EP_DATA_IN = 2,
+ EP_INT_IN = 3,
+ EP_REGS_OUT = 4,
+};
+
+enum {
+ USB_MAX_TRANSFER_SIZE = 4096, /* bytes */
+ /* FIXME: The original driver uses this value. We have to check,
+ * whether the MAX_TRANSFER_SIZE is sufficient and this needs only be
+ * used if one combined frame is split over two USB transactions.
+ */
+ USB_MAX_RX_SIZE = 4800, /* bytes */
+ USB_MAX_IOWRITE16_COUNT = 15,
+ USB_MAX_IOWRITE32_COUNT = USB_MAX_IOWRITE16_COUNT/2,
+ USB_MAX_IOREAD16_COUNT = 15,
+ USB_MAX_IOREAD32_COUNT = USB_MAX_IOREAD16_COUNT/2,
+ USB_MIN_RFWRITE_BIT_COUNT = 16,
+ USB_MAX_RFWRITE_BIT_COUNT = 28,
+ USB_MAX_EP_INT_BUFFER = 64,
+ USB_ZD1211B_BCD_DEVICE = 0x4810,
+};
+
+enum control_requests {
+ USB_REQ_WRITE_REGS = 0x21,
+ USB_REQ_READ_REGS = 0x22,
+ USB_REQ_WRITE_RF = 0x23,
+ USB_REQ_PROG_FLASH = 0x24,
+ USB_REQ_EEPROM_START = 0x0128, /* ? request is a byte */
+ USB_REQ_EEPROM_MID = 0x28,
+ USB_REQ_EEPROM_END = 0x0228, /* ? request is a byte */
+ USB_REQ_FIRMWARE_DOWNLOAD = 0x30,
+ USB_REQ_FIRMWARE_CONFIRM = 0x31,
+ USB_REQ_FIRMWARE_READ_DATA = 0x32,
+};
+
+struct usb_req_read_regs {
+ __le16 id;
+ __le16 addr[];
+} __packed;
+
+struct reg_data {
+ __le16 addr;
+ __le16 value;
+} __packed;
+
+struct usb_req_write_regs {
+ __le16 id;
+ struct reg_data reg_writes[];
+} __packed;
+
+enum {
+ RF_IF_LE = 0x02,
+ RF_CLK = 0x04,
+ RF_DATA = 0x08,
+};
+
+struct usb_req_rfwrite {
+ __le16 id;
+ __le16 value;
+ /* 1: 3683a */
+ /* 2: other (default) */
+ __le16 bits;
+ /* RF2595: 24 */
+ __le16 bit_values[];
+ /* (ZD_CR203 & ~(RF_IF_LE | RF_CLK | RF_DATA)) | (bit ? RF_DATA : 0) */
+} __packed;
+
+/* USB interrupt */
+
+enum usb_int_id {
+ USB_INT_TYPE = 0x01,
+ USB_INT_ID_REGS = 0x90,
+ USB_INT_ID_RETRY_FAILED = 0xa0,
+};
+
+enum usb_int_flags {
+ USB_INT_READ_REGS_EN = 0x01,
+};
+
+struct usb_int_header {
+ u8 type; /* must always be 1 */
+ u8 id;
+} __packed;
+
+struct usb_int_regs {
+ struct usb_int_header hdr;
+ struct reg_data regs[];
+} __packed;
+
+struct usb_int_retry_fail {
+ struct usb_int_header hdr;
+ u8 new_rate;
+ u8 _dummy;
+ u8 addr[ETH_ALEN];
+ u8 ibss_wakeup_dest;
+} __packed;
+
+struct read_regs_int {
+ struct completion completion;
+ struct usb_req_read_regs *req;
+ unsigned int req_count;
+ /* Stores the USB int structure and contains the USB address of the
+ * first requested register before request.
+ */
+ u8 buffer[USB_MAX_EP_INT_BUFFER];
+ int length;
+ __le16 cr_int_addr;
+};
+
+struct zd_ioreq16 {
+ zd_addr_t addr;
+ u16 value;
+};
+
+struct zd_ioreq32 {
+ zd_addr_t addr;
+ u32 value;
+};
+
+struct zd_usb_interrupt {
+ struct read_regs_int read_regs;
+ spinlock_t lock;
+ struct urb *urb;
+ void *buffer;
+ dma_addr_t buffer_dma;
+ int interval;
+ atomic_t read_regs_enabled;
+ u8 read_regs_int_overridden:1;
+};
+
+static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr)
+{
+ return (struct usb_int_regs *)intr->read_regs.buffer;
+}
+
+#define RX_URBS_COUNT 5
+
+struct zd_usb_rx {
+ spinlock_t lock;
+ struct mutex setup_mutex;
+ struct delayed_work idle_work;
+ struct tasklet_struct reset_timer_tasklet;
+ u8 fragment[2 * USB_MAX_RX_SIZE];
+ unsigned int fragment_length;
+ unsigned int usb_packet_size;
+ struct urb **urbs;
+ int urbs_count;
+};
+
+/**
+ * struct zd_usb_tx - structure used for transmitting frames
+ * @enabled: atomic enabled flag, indicates whether tx is enabled
+ * @lock: lock for transmission
+ * @submitted: anchor for URBs sent to device
+ * @submitted_urbs: atomic integer that counts the URBs having sent to the
+ * device, which haven't been completed
+ * @stopped: indicates whether higher level tx queues are stopped
+ */
+struct zd_usb_tx {
+ atomic_t enabled;
+ spinlock_t lock;
+ struct delayed_work watchdog_work;
+ struct sk_buff_head submitted_skbs;
+ struct usb_anchor submitted;
+ int submitted_urbs;
+ u8 stopped:1, watchdog_enabled:1;
+};
+
+/* Contains the usb parts. The structure doesn't require a lock because intf
+ * will not be changed after initialization.
+ */
+struct zd_usb {
+ struct zd_usb_interrupt intr;
+ struct zd_usb_rx rx;
+ struct zd_usb_tx tx;
+ struct usb_interface *intf;
+ struct usb_anchor submitted_cmds;
+ struct urb *urb_async_waiting;
+ int cmd_error;
+ u8 req_buf[64]; /* zd_usb_iowrite16v needs 62 bytes */
+ u8 is_zd1211b:1, initialized:1, was_running:1, in_async:1;
+};
+
+#define zd_usb_dev(usb) (&usb->intf->dev)
+
+static inline struct usb_device *zd_usb_to_usbdev(struct zd_usb *usb)
+{
+ return interface_to_usbdev(usb->intf);
+}
+
+static inline struct ieee80211_hw *zd_intf_to_hw(struct usb_interface *intf)
+{
+ return usb_get_intfdata(intf);
+}
+
+static inline struct ieee80211_hw *zd_usb_to_hw(struct zd_usb *usb)
+{
+ return zd_intf_to_hw(usb->intf);
+}
+
+void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw,
+ struct usb_interface *intf);
+int zd_usb_init_hw(struct zd_usb *usb);
+void zd_usb_clear(struct zd_usb *usb);
+
+int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size);
+
+void zd_tx_watchdog_enable(struct zd_usb *usb);
+void zd_tx_watchdog_disable(struct zd_usb *usb);
+
+int zd_usb_enable_int(struct zd_usb *usb);
+void zd_usb_disable_int(struct zd_usb *usb);
+
+int zd_usb_enable_rx(struct zd_usb *usb);
+void zd_usb_disable_rx(struct zd_usb *usb);
+
+void zd_usb_reset_rx_idle_timer(struct zd_usb *usb);
+
+void zd_usb_enable_tx(struct zd_usb *usb);
+void zd_usb_disable_tx(struct zd_usb *usb);
+
+int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb);
+
+int zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
+ const zd_addr_t *addresses, unsigned int count);
+
+static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value,
+ const zd_addr_t addr)
+{
+ return zd_usb_ioread16v(usb, value, &addr, 1);
+}
+
+void zd_usb_iowrite16v_async_start(struct zd_usb *usb);
+int zd_usb_iowrite16v_async_end(struct zd_usb *usb, unsigned int timeout);
+int zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
+ unsigned int count);
+int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
+ unsigned int count);
+
+int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits);
+
+int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len);
+
+extern struct workqueue_struct *zd_workqueue;
+
+#endif /* _ZD_USB_H */