18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Driver for ZyDAS zd1201 based wireless USB devices.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *	Parts of this driver have been derived from a wlan-ng version
88c2ecf20Sopenharmony_ci *	modified by ZyDAS. They also made documentation available, thanks!
98c2ecf20Sopenharmony_ci *	Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/usb.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
178c2ecf20Sopenharmony_ci#include <linux/wireless.h>
188c2ecf20Sopenharmony_ci#include <net/cfg80211.h>
198c2ecf20Sopenharmony_ci#include <net/iw_handler.h>
208c2ecf20Sopenharmony_ci#include <linux/string.h>
218c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
228c2ecf20Sopenharmony_ci#include <linux/firmware.h>
238c2ecf20Sopenharmony_ci#include "zd1201.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic const struct usb_device_id zd1201_table[] = {
268c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0586, 0x3400)}, /* Peabird Wireless USB Adapter */
278c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0ace, 0x1201)}, /* ZyDAS ZD1201 Wireless USB Adapter */
288c2ecf20Sopenharmony_ci	{USB_DEVICE(0x050d, 0x6051)}, /* Belkin F5D6051 usb  adapter */
298c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0db0, 0x6823)}, /* MSI UB11B usb  adapter */
308c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1044, 0x8004)}, /* Gigabyte GN-WLBZ101 */
318c2ecf20Sopenharmony_ci	{USB_DEVICE(0x1044, 0x8005)}, /* GIGABYTE GN-WLBZ201 usb adapter */
328c2ecf20Sopenharmony_ci	{}
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int ap;	/* Are we an AP or a normal station? */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define ZD1201_VERSION	"0.15"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for ZyDAS ZD1201 based USB Wireless adapters");
418c2ecf20Sopenharmony_ciMODULE_VERSION(ZD1201_VERSION);
428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
438c2ecf20Sopenharmony_cimodule_param(ap, int, 0);
448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ap, "If non-zero Access Point firmware will be loaded");
458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, zd1201_table);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int zd1201_fw_upload(struct usb_device *dev, int apfw)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	const struct firmware *fw_entry;
518c2ecf20Sopenharmony_ci	const char *data;
528c2ecf20Sopenharmony_ci	unsigned long len;
538c2ecf20Sopenharmony_ci	int err;
548c2ecf20Sopenharmony_ci	unsigned char ret;
558c2ecf20Sopenharmony_ci	char *buf;
568c2ecf20Sopenharmony_ci	char *fwfile;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (apfw)
598c2ecf20Sopenharmony_ci		fwfile = "zd1201-ap.fw";
608c2ecf20Sopenharmony_ci	else
618c2ecf20Sopenharmony_ci		fwfile = "zd1201.fw";
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	err = request_firmware(&fw_entry, fwfile, &dev->dev);
648c2ecf20Sopenharmony_ci	if (err) {
658c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "Failed to load %s firmware file!\n", fwfile);
668c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "Make sure the hotplug firmware loader is installed.\n");
678c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "Goto http://linux-lc100020.sourceforge.net for more info.\n");
688c2ecf20Sopenharmony_ci		return err;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	data = fw_entry->data;
728c2ecf20Sopenharmony_ci        len = fw_entry->size;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	buf = kmalloc(1024, GFP_ATOMIC);
758c2ecf20Sopenharmony_ci	if (!buf) {
768c2ecf20Sopenharmony_ci		err = -ENOMEM;
778c2ecf20Sopenharmony_ci		goto exit;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	while (len > 0) {
818c2ecf20Sopenharmony_ci		int translen = (len > 1024) ? 1024 : len;
828c2ecf20Sopenharmony_ci		memcpy(buf, data, translen);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0,
858c2ecf20Sopenharmony_ci		    USB_DIR_OUT | 0x40, 0, 0, buf, translen,
868c2ecf20Sopenharmony_ci		    ZD1201_FW_TIMEOUT);
878c2ecf20Sopenharmony_ci		if (err < 0)
888c2ecf20Sopenharmony_ci			goto exit;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		len -= translen;
918c2ecf20Sopenharmony_ci		data += translen;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x2,
958c2ecf20Sopenharmony_ci	    USB_DIR_OUT | 0x40, 0, 0, NULL, 0, ZD1201_FW_TIMEOUT);
968c2ecf20Sopenharmony_ci	if (err < 0)
978c2ecf20Sopenharmony_ci		goto exit;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4,
1008c2ecf20Sopenharmony_ci	    USB_DIR_IN | 0x40, 0, 0, buf, sizeof(ret), ZD1201_FW_TIMEOUT);
1018c2ecf20Sopenharmony_ci	if (err < 0)
1028c2ecf20Sopenharmony_ci		goto exit;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	memcpy(&ret, buf, sizeof(ret));
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (ret & 0x80) {
1078c2ecf20Sopenharmony_ci		err = -EIO;
1088c2ecf20Sopenharmony_ci		goto exit;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	err = 0;
1128c2ecf20Sopenharmony_ciexit:
1138c2ecf20Sopenharmony_ci	kfree(buf);
1148c2ecf20Sopenharmony_ci	release_firmware(fw_entry);
1158c2ecf20Sopenharmony_ci	return err;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ciMODULE_FIRMWARE("zd1201-ap.fw");
1198c2ecf20Sopenharmony_ciMODULE_FIRMWARE("zd1201.fw");
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void zd1201_usbfree(struct urb *urb)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct zd1201 *zd = urb->context;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	switch(urb->status) {
1268c2ecf20Sopenharmony_ci		case -EILSEQ:
1278c2ecf20Sopenharmony_ci		case -ENODEV:
1288c2ecf20Sopenharmony_ci		case -ETIME:
1298c2ecf20Sopenharmony_ci		case -ENOENT:
1308c2ecf20Sopenharmony_ci		case -EPIPE:
1318c2ecf20Sopenharmony_ci		case -EOVERFLOW:
1328c2ecf20Sopenharmony_ci		case -ESHUTDOWN:
1338c2ecf20Sopenharmony_ci			dev_warn(&zd->usb->dev, "%s: urb failed: %d\n",
1348c2ecf20Sopenharmony_ci			    zd->dev->name, urb->status);
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	kfree(urb->transfer_buffer);
1388c2ecf20Sopenharmony_ci	usb_free_urb(urb);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/* cmdreq message:
1428c2ecf20Sopenharmony_ci	u32 type
1438c2ecf20Sopenharmony_ci	u16 cmd
1448c2ecf20Sopenharmony_ci	u16 parm0
1458c2ecf20Sopenharmony_ci	u16 parm1
1468c2ecf20Sopenharmony_ci	u16 parm2
1478c2ecf20Sopenharmony_ci	u8  pad[4]
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	total: 4 + 2 + 2 + 2 + 2 + 4 = 16
1508c2ecf20Sopenharmony_ci*/
1518c2ecf20Sopenharmony_cistatic int zd1201_docmd(struct zd1201 *zd, int cmd, int parm0,
1528c2ecf20Sopenharmony_ci			int parm1, int parm2)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	unsigned char *command;
1558c2ecf20Sopenharmony_ci	int ret;
1568c2ecf20Sopenharmony_ci	struct urb *urb;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	command = kmalloc(16, GFP_ATOMIC);
1598c2ecf20Sopenharmony_ci	if (!command)
1608c2ecf20Sopenharmony_ci		return -ENOMEM;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	*((__le32*)command) = cpu_to_le32(ZD1201_USB_CMDREQ);
1638c2ecf20Sopenharmony_ci	*((__le16*)&command[4]) = cpu_to_le16(cmd);
1648c2ecf20Sopenharmony_ci	*((__le16*)&command[6]) = cpu_to_le16(parm0);
1658c2ecf20Sopenharmony_ci	*((__le16*)&command[8]) = cpu_to_le16(parm1);
1668c2ecf20Sopenharmony_ci	*((__le16*)&command[10])= cpu_to_le16(parm2);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_ATOMIC);
1698c2ecf20Sopenharmony_ci	if (!urb) {
1708c2ecf20Sopenharmony_ci		kfree(command);
1718c2ecf20Sopenharmony_ci		return -ENOMEM;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),
1748c2ecf20Sopenharmony_ci			  command, 16, zd1201_usbfree, zd);
1758c2ecf20Sopenharmony_ci	ret = usb_submit_urb(urb, GFP_ATOMIC);
1768c2ecf20Sopenharmony_ci	if (ret) {
1778c2ecf20Sopenharmony_ci		kfree(command);
1788c2ecf20Sopenharmony_ci		usb_free_urb(urb);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return ret;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/* Callback after sending out a packet */
1858c2ecf20Sopenharmony_cistatic void zd1201_usbtx(struct urb *urb)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct zd1201 *zd = urb->context;
1888c2ecf20Sopenharmony_ci	netif_wake_queue(zd->dev);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/* Incoming data */
1928c2ecf20Sopenharmony_cistatic void zd1201_usbrx(struct urb *urb)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct zd1201 *zd = urb->context;
1958c2ecf20Sopenharmony_ci	int free = 0;
1968c2ecf20Sopenharmony_ci	unsigned char *data = urb->transfer_buffer;
1978c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1988c2ecf20Sopenharmony_ci	unsigned char type;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (!zd)
2018c2ecf20Sopenharmony_ci		return;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	switch(urb->status) {
2048c2ecf20Sopenharmony_ci		case -EILSEQ:
2058c2ecf20Sopenharmony_ci		case -ENODEV:
2068c2ecf20Sopenharmony_ci		case -ETIME:
2078c2ecf20Sopenharmony_ci		case -ENOENT:
2088c2ecf20Sopenharmony_ci		case -EPIPE:
2098c2ecf20Sopenharmony_ci		case -EOVERFLOW:
2108c2ecf20Sopenharmony_ci		case -ESHUTDOWN:
2118c2ecf20Sopenharmony_ci			dev_warn(&zd->usb->dev, "%s: rx urb failed: %d\n",
2128c2ecf20Sopenharmony_ci			    zd->dev->name, urb->status);
2138c2ecf20Sopenharmony_ci			free = 1;
2148c2ecf20Sopenharmony_ci			goto exit;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (urb->status != 0 || urb->actual_length == 0)
2188c2ecf20Sopenharmony_ci		goto resubmit;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	type = data[0];
2218c2ecf20Sopenharmony_ci	if (type == ZD1201_PACKET_EVENTSTAT || type == ZD1201_PACKET_RESOURCE) {
2228c2ecf20Sopenharmony_ci		memcpy(zd->rxdata, data, urb->actual_length);
2238c2ecf20Sopenharmony_ci		zd->rxlen = urb->actual_length;
2248c2ecf20Sopenharmony_ci		zd->rxdatas = 1;
2258c2ecf20Sopenharmony_ci		wake_up(&zd->rxdataq);
2268c2ecf20Sopenharmony_ci	}
2278c2ecf20Sopenharmony_ci	/* Info frame */
2288c2ecf20Sopenharmony_ci	if (type == ZD1201_PACKET_INQUIRE) {
2298c2ecf20Sopenharmony_ci		int i = 0;
2308c2ecf20Sopenharmony_ci		unsigned short infotype, copylen;
2318c2ecf20Sopenharmony_ci		infotype = le16_to_cpu(*(__le16*)&data[6]);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		if (infotype == ZD1201_INF_LINKSTATUS) {
2348c2ecf20Sopenharmony_ci			short linkstatus;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci			linkstatus = le16_to_cpu(*(__le16*)&data[8]);
2378c2ecf20Sopenharmony_ci			switch(linkstatus) {
2388c2ecf20Sopenharmony_ci				case 1:
2398c2ecf20Sopenharmony_ci					netif_carrier_on(zd->dev);
2408c2ecf20Sopenharmony_ci					break;
2418c2ecf20Sopenharmony_ci				case 2:
2428c2ecf20Sopenharmony_ci					netif_carrier_off(zd->dev);
2438c2ecf20Sopenharmony_ci					break;
2448c2ecf20Sopenharmony_ci				case 3:
2458c2ecf20Sopenharmony_ci					netif_carrier_off(zd->dev);
2468c2ecf20Sopenharmony_ci					break;
2478c2ecf20Sopenharmony_ci				case 4:
2488c2ecf20Sopenharmony_ci					netif_carrier_on(zd->dev);
2498c2ecf20Sopenharmony_ci					break;
2508c2ecf20Sopenharmony_ci				default:
2518c2ecf20Sopenharmony_ci					netif_carrier_off(zd->dev);
2528c2ecf20Sopenharmony_ci			}
2538c2ecf20Sopenharmony_ci			goto resubmit;
2548c2ecf20Sopenharmony_ci		}
2558c2ecf20Sopenharmony_ci		if (infotype == ZD1201_INF_ASSOCSTATUS) {
2568c2ecf20Sopenharmony_ci			short status = le16_to_cpu(*(__le16*)(data+8));
2578c2ecf20Sopenharmony_ci			int event;
2588c2ecf20Sopenharmony_ci			union iwreq_data wrqu;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci			switch (status) {
2618c2ecf20Sopenharmony_ci				case ZD1201_ASSOCSTATUS_STAASSOC:
2628c2ecf20Sopenharmony_ci				case ZD1201_ASSOCSTATUS_REASSOC:
2638c2ecf20Sopenharmony_ci					event = IWEVREGISTERED;
2648c2ecf20Sopenharmony_ci					break;
2658c2ecf20Sopenharmony_ci				case ZD1201_ASSOCSTATUS_DISASSOC:
2668c2ecf20Sopenharmony_ci				case ZD1201_ASSOCSTATUS_ASSOCFAIL:
2678c2ecf20Sopenharmony_ci				case ZD1201_ASSOCSTATUS_AUTHFAIL:
2688c2ecf20Sopenharmony_ci				default:
2698c2ecf20Sopenharmony_ci					event = IWEVEXPIRED;
2708c2ecf20Sopenharmony_ci			}
2718c2ecf20Sopenharmony_ci			memcpy(wrqu.addr.sa_data, data+10, ETH_ALEN);
2728c2ecf20Sopenharmony_ci			wrqu.addr.sa_family = ARPHRD_ETHER;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci			/* Send event to user space */
2758c2ecf20Sopenharmony_ci			wireless_send_event(zd->dev, event, &wrqu, NULL);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci			goto resubmit;
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci		if (infotype == ZD1201_INF_AUTHREQ) {
2808c2ecf20Sopenharmony_ci			union iwreq_data wrqu;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci			memcpy(wrqu.addr.sa_data, data+8, ETH_ALEN);
2838c2ecf20Sopenharmony_ci			wrqu.addr.sa_family = ARPHRD_ETHER;
2848c2ecf20Sopenharmony_ci			/* There isn't a event that trully fits this request.
2858c2ecf20Sopenharmony_ci			   We assume that userspace will be smart enough to
2868c2ecf20Sopenharmony_ci			   see a new station being expired and sends back a
2878c2ecf20Sopenharmony_ci			   authstation ioctl to authorize it. */
2888c2ecf20Sopenharmony_ci			wireless_send_event(zd->dev, IWEVEXPIRED, &wrqu, NULL);
2898c2ecf20Sopenharmony_ci			goto resubmit;
2908c2ecf20Sopenharmony_ci		}
2918c2ecf20Sopenharmony_ci		/* Other infotypes are handled outside this handler */
2928c2ecf20Sopenharmony_ci		zd->rxlen = 0;
2938c2ecf20Sopenharmony_ci		while (i < urb->actual_length) {
2948c2ecf20Sopenharmony_ci			copylen = le16_to_cpu(*(__le16*)&data[i+2]);
2958c2ecf20Sopenharmony_ci			/* Sanity check, sometimes we get junk */
2968c2ecf20Sopenharmony_ci			if (copylen+zd->rxlen > sizeof(zd->rxdata))
2978c2ecf20Sopenharmony_ci				break;
2988c2ecf20Sopenharmony_ci			memcpy(zd->rxdata+zd->rxlen, data+i+4, copylen);
2998c2ecf20Sopenharmony_ci			zd->rxlen += copylen;
3008c2ecf20Sopenharmony_ci			i += 64;
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci		if (i >= urb->actual_length) {
3038c2ecf20Sopenharmony_ci			zd->rxdatas = 1;
3048c2ecf20Sopenharmony_ci			wake_up(&zd->rxdataq);
3058c2ecf20Sopenharmony_ci		}
3068c2ecf20Sopenharmony_ci		goto  resubmit;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci	/* Actual data */
3098c2ecf20Sopenharmony_ci	if (data[urb->actual_length-1] == ZD1201_PACKET_RXDATA) {
3108c2ecf20Sopenharmony_ci		int datalen = urb->actual_length-1;
3118c2ecf20Sopenharmony_ci		unsigned short len, fc, seq;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci		len = ntohs(*(__be16 *)&data[datalen-2]);
3148c2ecf20Sopenharmony_ci		if (len>datalen)
3158c2ecf20Sopenharmony_ci			len=datalen;
3168c2ecf20Sopenharmony_ci		fc = le16_to_cpu(*(__le16 *)&data[datalen-16]);
3178c2ecf20Sopenharmony_ci		seq = le16_to_cpu(*(__le16 *)&data[datalen-24]);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci		if (zd->monitor) {
3208c2ecf20Sopenharmony_ci			if (datalen < 24)
3218c2ecf20Sopenharmony_ci				goto resubmit;
3228c2ecf20Sopenharmony_ci			if (!(skb = dev_alloc_skb(datalen+24)))
3238c2ecf20Sopenharmony_ci				goto resubmit;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci			skb_put_data(skb, &data[datalen - 16], 2);
3268c2ecf20Sopenharmony_ci			skb_put_data(skb, &data[datalen - 2], 2);
3278c2ecf20Sopenharmony_ci			skb_put_data(skb, &data[datalen - 14], 6);
3288c2ecf20Sopenharmony_ci			skb_put_data(skb, &data[datalen - 22], 6);
3298c2ecf20Sopenharmony_ci			skb_put_data(skb, &data[datalen - 8], 6);
3308c2ecf20Sopenharmony_ci			skb_put_data(skb, &data[datalen - 24], 2);
3318c2ecf20Sopenharmony_ci			skb_put_data(skb, data, len);
3328c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, zd->dev);
3338c2ecf20Sopenharmony_ci			zd->dev->stats.rx_packets++;
3348c2ecf20Sopenharmony_ci			zd->dev->stats.rx_bytes += skb->len;
3358c2ecf20Sopenharmony_ci			netif_rx(skb);
3368c2ecf20Sopenharmony_ci			goto resubmit;
3378c2ecf20Sopenharmony_ci		}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		if ((seq & IEEE80211_SCTL_FRAG) ||
3408c2ecf20Sopenharmony_ci		    (fc & IEEE80211_FCTL_MOREFRAGS)) {
3418c2ecf20Sopenharmony_ci			struct zd1201_frag *frag = NULL;
3428c2ecf20Sopenharmony_ci			char *ptr;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci			if (datalen<14)
3458c2ecf20Sopenharmony_ci				goto resubmit;
3468c2ecf20Sopenharmony_ci			if ((seq & IEEE80211_SCTL_FRAG) == 0) {
3478c2ecf20Sopenharmony_ci				frag = kmalloc(sizeof(*frag), GFP_ATOMIC);
3488c2ecf20Sopenharmony_ci				if (!frag)
3498c2ecf20Sopenharmony_ci					goto resubmit;
3508c2ecf20Sopenharmony_ci				skb = dev_alloc_skb(IEEE80211_MAX_DATA_LEN +14+2);
3518c2ecf20Sopenharmony_ci				if (!skb) {
3528c2ecf20Sopenharmony_ci					kfree(frag);
3538c2ecf20Sopenharmony_ci					goto resubmit;
3548c2ecf20Sopenharmony_ci				}
3558c2ecf20Sopenharmony_ci				frag->skb = skb;
3568c2ecf20Sopenharmony_ci				frag->seq = seq & IEEE80211_SCTL_SEQ;
3578c2ecf20Sopenharmony_ci				skb_reserve(skb, 2);
3588c2ecf20Sopenharmony_ci				skb_put_data(skb, &data[datalen - 14], 12);
3598c2ecf20Sopenharmony_ci				skb_put_data(skb, &data[6], 2);
3608c2ecf20Sopenharmony_ci				skb_put_data(skb, data + 8, len);
3618c2ecf20Sopenharmony_ci				hlist_add_head(&frag->fnode, &zd->fraglist);
3628c2ecf20Sopenharmony_ci				goto resubmit;
3638c2ecf20Sopenharmony_ci			}
3648c2ecf20Sopenharmony_ci			hlist_for_each_entry(frag, &zd->fraglist, fnode)
3658c2ecf20Sopenharmony_ci				if (frag->seq == (seq&IEEE80211_SCTL_SEQ))
3668c2ecf20Sopenharmony_ci					break;
3678c2ecf20Sopenharmony_ci			if (!frag)
3688c2ecf20Sopenharmony_ci				goto resubmit;
3698c2ecf20Sopenharmony_ci			skb = frag->skb;
3708c2ecf20Sopenharmony_ci			ptr = skb_put(skb, len);
3718c2ecf20Sopenharmony_ci			if (ptr)
3728c2ecf20Sopenharmony_ci				memcpy(ptr, data+8, len);
3738c2ecf20Sopenharmony_ci			if (fc & IEEE80211_FCTL_MOREFRAGS)
3748c2ecf20Sopenharmony_ci				goto resubmit;
3758c2ecf20Sopenharmony_ci			hlist_del_init(&frag->fnode);
3768c2ecf20Sopenharmony_ci			kfree(frag);
3778c2ecf20Sopenharmony_ci		} else {
3788c2ecf20Sopenharmony_ci			if (datalen<14)
3798c2ecf20Sopenharmony_ci				goto resubmit;
3808c2ecf20Sopenharmony_ci			skb = dev_alloc_skb(len + 14 + 2);
3818c2ecf20Sopenharmony_ci			if (!skb)
3828c2ecf20Sopenharmony_ci				goto resubmit;
3838c2ecf20Sopenharmony_ci			skb_reserve(skb, 2);
3848c2ecf20Sopenharmony_ci			skb_put_data(skb, &data[datalen - 14], 12);
3858c2ecf20Sopenharmony_ci			skb_put_data(skb, &data[6], 2);
3868c2ecf20Sopenharmony_ci			skb_put_data(skb, data + 8, len);
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci		skb->protocol = eth_type_trans(skb, zd->dev);
3898c2ecf20Sopenharmony_ci		zd->dev->stats.rx_packets++;
3908c2ecf20Sopenharmony_ci		zd->dev->stats.rx_bytes += skb->len;
3918c2ecf20Sopenharmony_ci		netif_rx(skb);
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ciresubmit:
3948c2ecf20Sopenharmony_ci	memset(data, 0, ZD1201_RXSIZE);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	urb->status = 0;
3978c2ecf20Sopenharmony_ci	urb->dev = zd->usb;
3988c2ecf20Sopenharmony_ci	if(usb_submit_urb(urb, GFP_ATOMIC))
3998c2ecf20Sopenharmony_ci		free = 1;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ciexit:
4028c2ecf20Sopenharmony_ci	if (free) {
4038c2ecf20Sopenharmony_ci		zd->rxlen = 0;
4048c2ecf20Sopenharmony_ci		zd->rxdatas = 1;
4058c2ecf20Sopenharmony_ci		wake_up(&zd->rxdataq);
4068c2ecf20Sopenharmony_ci		kfree(urb->transfer_buffer);
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic int zd1201_getconfig(struct zd1201 *zd, int rid, void *riddata,
4118c2ecf20Sopenharmony_ci	unsigned int riddatalen)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	int err;
4148c2ecf20Sopenharmony_ci	int i = 0;
4158c2ecf20Sopenharmony_ci	int code;
4168c2ecf20Sopenharmony_ci	int rid_fid;
4178c2ecf20Sopenharmony_ci	int length;
4188c2ecf20Sopenharmony_ci	unsigned char *pdata;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	zd->rxdatas = 0;
4218c2ecf20Sopenharmony_ci	err = zd1201_docmd(zd, ZD1201_CMDCODE_ACCESS, rid, 0, 0);
4228c2ecf20Sopenharmony_ci	if (err)
4238c2ecf20Sopenharmony_ci		return err;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	wait_event_interruptible(zd->rxdataq, zd->rxdatas);
4268c2ecf20Sopenharmony_ci	if (!zd->rxlen)
4278c2ecf20Sopenharmony_ci		return -EIO;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	code = le16_to_cpu(*(__le16*)(&zd->rxdata[4]));
4308c2ecf20Sopenharmony_ci	rid_fid = le16_to_cpu(*(__le16*)(&zd->rxdata[6]));
4318c2ecf20Sopenharmony_ci	length = le16_to_cpu(*(__le16*)(&zd->rxdata[8]));
4328c2ecf20Sopenharmony_ci	if (length > zd->rxlen)
4338c2ecf20Sopenharmony_ci		length = zd->rxlen-6;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* If access bit is not on, then error */
4368c2ecf20Sopenharmony_ci	if ((code & ZD1201_ACCESSBIT) != ZD1201_ACCESSBIT || rid_fid != rid )
4378c2ecf20Sopenharmony_ci		return -EINVAL;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* Not enough buffer for allocating data */
4408c2ecf20Sopenharmony_ci	if (riddatalen != (length - 4)) {
4418c2ecf20Sopenharmony_ci		dev_dbg(&zd->usb->dev, "riddatalen mismatches, expected=%u, (packet=%u) length=%u, rid=0x%04X, rid_fid=0x%04X\n",
4428c2ecf20Sopenharmony_ci		    riddatalen, zd->rxlen, length, rid, rid_fid);
4438c2ecf20Sopenharmony_ci		return -ENODATA;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	zd->rxdatas = 0;
4478c2ecf20Sopenharmony_ci	/* Issue SetRxRid commnd */
4488c2ecf20Sopenharmony_ci	err = zd1201_docmd(zd, ZD1201_CMDCODE_SETRXRID, rid, 0, length);
4498c2ecf20Sopenharmony_ci	if (err)
4508c2ecf20Sopenharmony_ci		return err;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	/* Receive RID record from resource packets */
4538c2ecf20Sopenharmony_ci	wait_event_interruptible(zd->rxdataq, zd->rxdatas);
4548c2ecf20Sopenharmony_ci	if (!zd->rxlen)
4558c2ecf20Sopenharmony_ci		return -EIO;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (zd->rxdata[zd->rxlen - 1] != ZD1201_PACKET_RESOURCE) {
4588c2ecf20Sopenharmony_ci		dev_dbg(&zd->usb->dev, "Packet type mismatch: 0x%x not 0x3\n",
4598c2ecf20Sopenharmony_ci		    zd->rxdata[zd->rxlen-1]);
4608c2ecf20Sopenharmony_ci		return -EINVAL;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	/* Set the data pointer and received data length */
4648c2ecf20Sopenharmony_ci	pdata = zd->rxdata;
4658c2ecf20Sopenharmony_ci	length = zd->rxlen;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	do {
4688c2ecf20Sopenharmony_ci		int actual_length;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		actual_length = (length > 64) ? 64 : length;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		if (pdata[0] != 0x3) {
4738c2ecf20Sopenharmony_ci			dev_dbg(&zd->usb->dev, "Rx Resource packet type error: %02X\n",
4748c2ecf20Sopenharmony_ci			    pdata[0]);
4758c2ecf20Sopenharmony_ci			return -EINVAL;
4768c2ecf20Sopenharmony_ci		}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		if (actual_length != 64) {
4798c2ecf20Sopenharmony_ci			/* Trim the last packet type byte */
4808c2ecf20Sopenharmony_ci			actual_length--;
4818c2ecf20Sopenharmony_ci		}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		/* Skip the 4 bytes header (RID length and RID) */
4848c2ecf20Sopenharmony_ci		if (i == 0) {
4858c2ecf20Sopenharmony_ci			pdata += 8;
4868c2ecf20Sopenharmony_ci			actual_length -= 8;
4878c2ecf20Sopenharmony_ci		} else {
4888c2ecf20Sopenharmony_ci			pdata += 4;
4898c2ecf20Sopenharmony_ci			actual_length -= 4;
4908c2ecf20Sopenharmony_ci		}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci		memcpy(riddata, pdata, actual_length);
4938c2ecf20Sopenharmony_ci		riddata += actual_length;
4948c2ecf20Sopenharmony_ci		pdata += actual_length;
4958c2ecf20Sopenharmony_ci		length -= 64;
4968c2ecf20Sopenharmony_ci		i++;
4978c2ecf20Sopenharmony_ci	} while (length > 0);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci/*
5038c2ecf20Sopenharmony_ci *	resreq:
5048c2ecf20Sopenharmony_ci *		byte	type
5058c2ecf20Sopenharmony_ci *		byte	sequence
5068c2ecf20Sopenharmony_ci *		u16	reserved
5078c2ecf20Sopenharmony_ci *		byte	data[12]
5088c2ecf20Sopenharmony_ci *	total: 16
5098c2ecf20Sopenharmony_ci */
5108c2ecf20Sopenharmony_cistatic int zd1201_setconfig(struct zd1201 *zd, int rid, void *buf, int len, int wait)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	int err;
5138c2ecf20Sopenharmony_ci	unsigned char *request;
5148c2ecf20Sopenharmony_ci	int reqlen;
5158c2ecf20Sopenharmony_ci	char seq=0;
5168c2ecf20Sopenharmony_ci	struct urb *urb;
5178c2ecf20Sopenharmony_ci	gfp_t gfp_mask = wait ? GFP_NOIO : GFP_ATOMIC;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	len += 4;			/* first 4 are for header */
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	zd->rxdatas = 0;
5228c2ecf20Sopenharmony_ci	zd->rxlen = 0;
5238c2ecf20Sopenharmony_ci	for (seq=0; len > 0; seq++) {
5248c2ecf20Sopenharmony_ci		request = kmalloc(16, gfp_mask);
5258c2ecf20Sopenharmony_ci		if (!request)
5268c2ecf20Sopenharmony_ci			return -ENOMEM;
5278c2ecf20Sopenharmony_ci		urb = usb_alloc_urb(0, gfp_mask);
5288c2ecf20Sopenharmony_ci		if (!urb) {
5298c2ecf20Sopenharmony_ci			kfree(request);
5308c2ecf20Sopenharmony_ci			return -ENOMEM;
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci		memset(request, 0, 16);
5338c2ecf20Sopenharmony_ci		reqlen = len>12 ? 12 : len;
5348c2ecf20Sopenharmony_ci		request[0] = ZD1201_USB_RESREQ;
5358c2ecf20Sopenharmony_ci		request[1] = seq;
5368c2ecf20Sopenharmony_ci		request[2] = 0;
5378c2ecf20Sopenharmony_ci		request[3] = 0;
5388c2ecf20Sopenharmony_ci		if (request[1] == 0) {
5398c2ecf20Sopenharmony_ci			/* add header */
5408c2ecf20Sopenharmony_ci			*(__le16*)&request[4] = cpu_to_le16((len-2+1)/2);
5418c2ecf20Sopenharmony_ci			*(__le16*)&request[6] = cpu_to_le16(rid);
5428c2ecf20Sopenharmony_ci			memcpy(request+8, buf, reqlen-4);
5438c2ecf20Sopenharmony_ci			buf += reqlen-4;
5448c2ecf20Sopenharmony_ci		} else {
5458c2ecf20Sopenharmony_ci			memcpy(request+4, buf, reqlen);
5468c2ecf20Sopenharmony_ci			buf += reqlen;
5478c2ecf20Sopenharmony_ci		}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci		len -= reqlen;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb,
5528c2ecf20Sopenharmony_ci		    zd->endp_out2), request, 16, zd1201_usbfree, zd);
5538c2ecf20Sopenharmony_ci		err = usb_submit_urb(urb, gfp_mask);
5548c2ecf20Sopenharmony_ci		if (err)
5558c2ecf20Sopenharmony_ci			goto err;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	request = kmalloc(16, gfp_mask);
5598c2ecf20Sopenharmony_ci	if (!request)
5608c2ecf20Sopenharmony_ci		return -ENOMEM;
5618c2ecf20Sopenharmony_ci	urb = usb_alloc_urb(0, gfp_mask);
5628c2ecf20Sopenharmony_ci	if (!urb) {
5638c2ecf20Sopenharmony_ci		kfree(request);
5648c2ecf20Sopenharmony_ci		return -ENOMEM;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci	*((__le32*)request) = cpu_to_le32(ZD1201_USB_CMDREQ);
5678c2ecf20Sopenharmony_ci	*((__le16*)&request[4]) =
5688c2ecf20Sopenharmony_ci	    cpu_to_le16(ZD1201_CMDCODE_ACCESS|ZD1201_ACCESSBIT);
5698c2ecf20Sopenharmony_ci	*((__le16*)&request[6]) = cpu_to_le16(rid);
5708c2ecf20Sopenharmony_ci	*((__le16*)&request[8]) = cpu_to_le16(0);
5718c2ecf20Sopenharmony_ci	*((__le16*)&request[10]) = cpu_to_le16(0);
5728c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),
5738c2ecf20Sopenharmony_ci	     request, 16, zd1201_usbfree, zd);
5748c2ecf20Sopenharmony_ci	err = usb_submit_urb(urb, gfp_mask);
5758c2ecf20Sopenharmony_ci	if (err)
5768c2ecf20Sopenharmony_ci		goto err;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	if (wait) {
5798c2ecf20Sopenharmony_ci		wait_event_interruptible(zd->rxdataq, zd->rxdatas);
5808c2ecf20Sopenharmony_ci		if (!zd->rxlen || le16_to_cpu(*(__le16*)&zd->rxdata[6]) != rid) {
5818c2ecf20Sopenharmony_ci			dev_dbg(&zd->usb->dev, "wrong or no RID received\n");
5828c2ecf20Sopenharmony_ci		}
5838c2ecf20Sopenharmony_ci	}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	return 0;
5868c2ecf20Sopenharmony_cierr:
5878c2ecf20Sopenharmony_ci	kfree(request);
5888c2ecf20Sopenharmony_ci	usb_free_urb(urb);
5898c2ecf20Sopenharmony_ci	return err;
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_cistatic inline int zd1201_getconfig16(struct zd1201 *zd, int rid, short *val)
5938c2ecf20Sopenharmony_ci{
5948c2ecf20Sopenharmony_ci	int err;
5958c2ecf20Sopenharmony_ci	__le16 zdval;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	err = zd1201_getconfig(zd, rid, &zdval, sizeof(__le16));
5988c2ecf20Sopenharmony_ci	if (err)
5998c2ecf20Sopenharmony_ci		return err;
6008c2ecf20Sopenharmony_ci	*val = le16_to_cpu(zdval);
6018c2ecf20Sopenharmony_ci	return 0;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic inline int zd1201_setconfig16(struct zd1201 *zd, int rid, short val)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	__le16 zdval = cpu_to_le16(val);
6078c2ecf20Sopenharmony_ci	return (zd1201_setconfig(zd, rid, &zdval, sizeof(__le16), 1));
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic int zd1201_drvr_start(struct zd1201 *zd)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	int err, i;
6138c2ecf20Sopenharmony_ci	short max;
6148c2ecf20Sopenharmony_ci	__le16 zdmax;
6158c2ecf20Sopenharmony_ci	unsigned char *buffer;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	buffer = kzalloc(ZD1201_RXSIZE, GFP_KERNEL);
6188c2ecf20Sopenharmony_ci	if (!buffer)
6198c2ecf20Sopenharmony_ci		return -ENOMEM;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(zd->rx_urb, zd->usb,
6228c2ecf20Sopenharmony_ci	    usb_rcvbulkpipe(zd->usb, zd->endp_in), buffer, ZD1201_RXSIZE,
6238c2ecf20Sopenharmony_ci	    zd1201_usbrx, zd);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	err = usb_submit_urb(zd->rx_urb, GFP_KERNEL);
6268c2ecf20Sopenharmony_ci	if (err)
6278c2ecf20Sopenharmony_ci		goto err_buffer;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	err = zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
6308c2ecf20Sopenharmony_ci	if (err)
6318c2ecf20Sopenharmony_ci		goto err_urb;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	err = zd1201_getconfig(zd, ZD1201_RID_CNFMAXTXBUFFERNUMBER, &zdmax,
6348c2ecf20Sopenharmony_ci	    sizeof(__le16));
6358c2ecf20Sopenharmony_ci	if (err)
6368c2ecf20Sopenharmony_ci		goto err_urb;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	max = le16_to_cpu(zdmax);
6398c2ecf20Sopenharmony_ci	for (i=0; i<max; i++) {
6408c2ecf20Sopenharmony_ci		err = zd1201_docmd(zd, ZD1201_CMDCODE_ALLOC, 1514, 0, 0);
6418c2ecf20Sopenharmony_ci		if (err)
6428c2ecf20Sopenharmony_ci			goto err_urb;
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	return 0;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cierr_urb:
6488c2ecf20Sopenharmony_ci	usb_kill_urb(zd->rx_urb);
6498c2ecf20Sopenharmony_ci	return err;
6508c2ecf20Sopenharmony_cierr_buffer:
6518c2ecf20Sopenharmony_ci	kfree(buffer);
6528c2ecf20Sopenharmony_ci	return err;
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci/*	Magic alert: The firmware doesn't seem to like the MAC state being
6568c2ecf20Sopenharmony_ci *	toggled in promisc (aka monitor) mode.
6578c2ecf20Sopenharmony_ci *	(It works a number of times, but will halt eventually)
6588c2ecf20Sopenharmony_ci *	So we turn it of before disabling and on after enabling if needed.
6598c2ecf20Sopenharmony_ci */
6608c2ecf20Sopenharmony_cistatic int zd1201_enable(struct zd1201 *zd)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	int err;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	if (zd->mac_enabled)
6658c2ecf20Sopenharmony_ci		return 0;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	err = zd1201_docmd(zd, ZD1201_CMDCODE_ENABLE, 0, 0, 0);
6688c2ecf20Sopenharmony_ci	if (!err)
6698c2ecf20Sopenharmony_ci		zd->mac_enabled = 1;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	if (zd->monitor)
6728c2ecf20Sopenharmony_ci		err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 1);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	return err;
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic int zd1201_disable(struct zd1201 *zd)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	int err;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (!zd->mac_enabled)
6828c2ecf20Sopenharmony_ci		return 0;
6838c2ecf20Sopenharmony_ci	if (zd->monitor) {
6848c2ecf20Sopenharmony_ci		err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0);
6858c2ecf20Sopenharmony_ci		if (err)
6868c2ecf20Sopenharmony_ci			return err;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	err = zd1201_docmd(zd, ZD1201_CMDCODE_DISABLE, 0, 0, 0);
6908c2ecf20Sopenharmony_ci	if (!err)
6918c2ecf20Sopenharmony_ci		zd->mac_enabled = 0;
6928c2ecf20Sopenharmony_ci	return err;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int zd1201_mac_reset(struct zd1201 *zd)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	if (!zd->mac_enabled)
6988c2ecf20Sopenharmony_ci		return 0;
6998c2ecf20Sopenharmony_ci	zd1201_disable(zd);
7008c2ecf20Sopenharmony_ci	return zd1201_enable(zd);
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_cistatic int zd1201_join(struct zd1201 *zd, char *essid, int essidlen)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	int err, val;
7068c2ecf20Sopenharmony_ci	char buf[IW_ESSID_MAX_SIZE+2];
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	err = zd1201_disable(zd);
7098c2ecf20Sopenharmony_ci	if (err)
7108c2ecf20Sopenharmony_ci		return err;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	val = ZD1201_CNFAUTHENTICATION_OPENSYSTEM;
7138c2ecf20Sopenharmony_ci	val |= ZD1201_CNFAUTHENTICATION_SHAREDKEY;
7148c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, val);
7158c2ecf20Sopenharmony_ci	if (err)
7168c2ecf20Sopenharmony_ci		return err;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	*(__le16 *)buf = cpu_to_le16(essidlen);
7198c2ecf20Sopenharmony_ci	memcpy(buf+2, essid, essidlen);
7208c2ecf20Sopenharmony_ci	if (!zd->ap) {	/* Normal station */
7218c2ecf20Sopenharmony_ci		err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
7228c2ecf20Sopenharmony_ci		    IW_ESSID_MAX_SIZE+2, 1);
7238c2ecf20Sopenharmony_ci		if (err)
7248c2ecf20Sopenharmony_ci			return err;
7258c2ecf20Sopenharmony_ci	} else {	/* AP */
7268c2ecf20Sopenharmony_ci		err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNSSID, buf,
7278c2ecf20Sopenharmony_ci		    IW_ESSID_MAX_SIZE+2, 1);
7288c2ecf20Sopenharmony_ci		if (err)
7298c2ecf20Sopenharmony_ci			return err;
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR,
7338c2ecf20Sopenharmony_ci	    zd->dev->dev_addr, zd->dev->addr_len, 1);
7348c2ecf20Sopenharmony_ci	if (err)
7358c2ecf20Sopenharmony_ci		return err;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	err = zd1201_enable(zd);
7388c2ecf20Sopenharmony_ci	if (err)
7398c2ecf20Sopenharmony_ci		return err;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	msleep(100);
7428c2ecf20Sopenharmony_ci	return 0;
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cistatic int zd1201_net_open(struct net_device *dev)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	/* Start MAC with wildcard if no essid set */
7508c2ecf20Sopenharmony_ci	if (!zd->mac_enabled)
7518c2ecf20Sopenharmony_ci		zd1201_join(zd, zd->essid, zd->essidlen);
7528c2ecf20Sopenharmony_ci	netif_start_queue(dev);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	return 0;
7558c2ecf20Sopenharmony_ci}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_cistatic int zd1201_net_stop(struct net_device *dev)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
7608c2ecf20Sopenharmony_ci	return 0;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/*
7648c2ecf20Sopenharmony_ci	RFC 1042 encapsulates Ethernet frames in 802.11 frames
7658c2ecf20Sopenharmony_ci	by prefixing them with 0xaa, 0xaa, 0x03) followed by a SNAP OID of 0
7668c2ecf20Sopenharmony_ci	(0x00, 0x00, 0x00). Zd requires an additional padding, copy
7678c2ecf20Sopenharmony_ci	of ethernet addresses, length of the standard RFC 1042 packet
7688c2ecf20Sopenharmony_ci	and a command byte (which is nul for tx).
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	tx frame (from Wlan NG):
7718c2ecf20Sopenharmony_ci	RFC 1042:
7728c2ecf20Sopenharmony_ci		llc		0xAA 0xAA 0x03 (802.2 LLC)
7738c2ecf20Sopenharmony_ci		snap		0x00 0x00 0x00 (Ethernet encapsulated)
7748c2ecf20Sopenharmony_ci		type		2 bytes, Ethernet type field
7758c2ecf20Sopenharmony_ci		payload		(minus eth header)
7768c2ecf20Sopenharmony_ci	Zydas specific:
7778c2ecf20Sopenharmony_ci		padding		1B if (skb->len+8+1)%64==0
7788c2ecf20Sopenharmony_ci		Eth MAC addr	12 bytes, Ethernet MAC addresses
7798c2ecf20Sopenharmony_ci		length		2 bytes, RFC 1042 packet length
7808c2ecf20Sopenharmony_ci				(llc+snap+type+payload)
7818c2ecf20Sopenharmony_ci		zd		1 null byte, zd1201 packet type
7828c2ecf20Sopenharmony_ci */
7838c2ecf20Sopenharmony_cistatic netdev_tx_t zd1201_hard_start_xmit(struct sk_buff *skb,
7848c2ecf20Sopenharmony_ci						struct net_device *dev)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
7878c2ecf20Sopenharmony_ci	unsigned char *txbuf = zd->txdata;
7888c2ecf20Sopenharmony_ci	int txbuflen, pad = 0, err;
7898c2ecf20Sopenharmony_ci	struct urb *urb = zd->tx_urb;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	if (!zd->mac_enabled || zd->monitor) {
7928c2ecf20Sopenharmony_ci		dev->stats.tx_dropped++;
7938c2ecf20Sopenharmony_ci		kfree_skb(skb);
7948c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	txbuflen = skb->len + 8 + 1;
7998c2ecf20Sopenharmony_ci	if (txbuflen%64 == 0) {
8008c2ecf20Sopenharmony_ci		pad = 1;
8018c2ecf20Sopenharmony_ci		txbuflen++;
8028c2ecf20Sopenharmony_ci	}
8038c2ecf20Sopenharmony_ci	txbuf[0] = 0xAA;
8048c2ecf20Sopenharmony_ci	txbuf[1] = 0xAA;
8058c2ecf20Sopenharmony_ci	txbuf[2] = 0x03;
8068c2ecf20Sopenharmony_ci	txbuf[3] = 0x00;	/* rfc1042 */
8078c2ecf20Sopenharmony_ci	txbuf[4] = 0x00;
8088c2ecf20Sopenharmony_ci	txbuf[5] = 0x00;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	skb_copy_from_linear_data_offset(skb, 12, txbuf + 6, skb->len - 12);
8118c2ecf20Sopenharmony_ci	if (pad)
8128c2ecf20Sopenharmony_ci		txbuf[skb->len-12+6]=0;
8138c2ecf20Sopenharmony_ci	skb_copy_from_linear_data(skb, txbuf + skb->len - 12 + 6 + pad, 12);
8148c2ecf20Sopenharmony_ci	*(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6);
8158c2ecf20Sopenharmony_ci	txbuf[txbuflen-1] = 0;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out),
8188c2ecf20Sopenharmony_ci	    txbuf, txbuflen, zd1201_usbtx, zd);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	err = usb_submit_urb(zd->tx_urb, GFP_ATOMIC);
8218c2ecf20Sopenharmony_ci	if (err) {
8228c2ecf20Sopenharmony_ci		dev->stats.tx_errors++;
8238c2ecf20Sopenharmony_ci		netif_start_queue(dev);
8248c2ecf20Sopenharmony_ci	} else {
8258c2ecf20Sopenharmony_ci		dev->stats.tx_packets++;
8268c2ecf20Sopenharmony_ci		dev->stats.tx_bytes += skb->len;
8278c2ecf20Sopenharmony_ci	}
8288c2ecf20Sopenharmony_ci	kfree_skb(skb);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic void zd1201_tx_timeout(struct net_device *dev, unsigned int txqueue)
8348c2ecf20Sopenharmony_ci{
8358c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	if (!zd)
8388c2ecf20Sopenharmony_ci		return;
8398c2ecf20Sopenharmony_ci	dev_warn(&zd->usb->dev, "%s: TX timeout, shooting down urb\n",
8408c2ecf20Sopenharmony_ci	    dev->name);
8418c2ecf20Sopenharmony_ci	usb_unlink_urb(zd->tx_urb);
8428c2ecf20Sopenharmony_ci	dev->stats.tx_errors++;
8438c2ecf20Sopenharmony_ci	/* Restart the timeout to quiet the watchdog: */
8448c2ecf20Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_cistatic int zd1201_set_mac_address(struct net_device *dev, void *p)
8488c2ecf20Sopenharmony_ci{
8498c2ecf20Sopenharmony_ci	struct sockaddr *addr = p;
8508c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
8518c2ecf20Sopenharmony_ci	int err;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	if (!zd)
8548c2ecf20Sopenharmony_ci		return -ENODEV;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR,
8578c2ecf20Sopenharmony_ci	    addr->sa_data, dev->addr_len, 1);
8588c2ecf20Sopenharmony_ci	if (err)
8598c2ecf20Sopenharmony_ci		return err;
8608c2ecf20Sopenharmony_ci	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	return zd1201_mac_reset(zd);
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic struct iw_statistics *zd1201_get_wireless_stats(struct net_device *dev)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	return &zd->iwstats;
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic void zd1201_set_multicast(struct net_device *dev)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
8758c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
8768c2ecf20Sopenharmony_ci	unsigned char reqbuf[ETH_ALEN*ZD1201_MAXMULTI];
8778c2ecf20Sopenharmony_ci	int i;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (netdev_mc_count(dev) > ZD1201_MAXMULTI)
8808c2ecf20Sopenharmony_ci		return;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	i = 0;
8838c2ecf20Sopenharmony_ci	netdev_for_each_mc_addr(ha, dev)
8848c2ecf20Sopenharmony_ci		memcpy(reqbuf + i++ * ETH_ALEN, ha->addr, ETH_ALEN);
8858c2ecf20Sopenharmony_ci	zd1201_setconfig(zd, ZD1201_RID_CNFGROUPADDRESS, reqbuf,
8868c2ecf20Sopenharmony_ci			 netdev_mc_count(dev) * ETH_ALEN, 0);
8878c2ecf20Sopenharmony_ci}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic int zd1201_config_commit(struct net_device *dev,
8908c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_point *data, char *essid)
8918c2ecf20Sopenharmony_ci{
8928c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	return zd1201_mac_reset(zd);
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cistatic int zd1201_get_name(struct net_device *dev,
8988c2ecf20Sopenharmony_ci    struct iw_request_info *info, char *name, char *extra)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	strcpy(name, "IEEE 802.11b");
9018c2ecf20Sopenharmony_ci	return 0;
9028c2ecf20Sopenharmony_ci}
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_cistatic int zd1201_set_freq(struct net_device *dev,
9058c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_freq *freq, char *extra)
9068c2ecf20Sopenharmony_ci{
9078c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
9088c2ecf20Sopenharmony_ci	short channel = 0;
9098c2ecf20Sopenharmony_ci	int err;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	if (freq->e == 0)
9128c2ecf20Sopenharmony_ci		channel = freq->m;
9138c2ecf20Sopenharmony_ci	else
9148c2ecf20Sopenharmony_ci		channel = ieee80211_frequency_to_channel(freq->m);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, channel);
9178c2ecf20Sopenharmony_ci	if (err)
9188c2ecf20Sopenharmony_ci		return err;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	zd1201_mac_reset(zd);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	return 0;
9238c2ecf20Sopenharmony_ci}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_cistatic int zd1201_get_freq(struct net_device *dev,
9268c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_freq *freq, char *extra)
9278c2ecf20Sopenharmony_ci{
9288c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
9298c2ecf20Sopenharmony_ci	short channel;
9308c2ecf20Sopenharmony_ci	int err;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	err = zd1201_getconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, &channel);
9338c2ecf20Sopenharmony_ci	if (err)
9348c2ecf20Sopenharmony_ci		return err;
9358c2ecf20Sopenharmony_ci	freq->e = 0;
9368c2ecf20Sopenharmony_ci	freq->m = channel;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	return 0;
9398c2ecf20Sopenharmony_ci}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_cistatic int zd1201_set_mode(struct net_device *dev,
9428c2ecf20Sopenharmony_ci    struct iw_request_info *info, __u32 *mode, char *extra)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
9458c2ecf20Sopenharmony_ci	short porttype, monitor = 0;
9468c2ecf20Sopenharmony_ci	unsigned char buffer[IW_ESSID_MAX_SIZE+2];
9478c2ecf20Sopenharmony_ci	int err;
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	if (zd->ap) {
9508c2ecf20Sopenharmony_ci		if (*mode != IW_MODE_MASTER)
9518c2ecf20Sopenharmony_ci			return -EINVAL;
9528c2ecf20Sopenharmony_ci		return 0;
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0);
9568c2ecf20Sopenharmony_ci	if (err)
9578c2ecf20Sopenharmony_ci		return err;
9588c2ecf20Sopenharmony_ci	zd->dev->type = ARPHRD_ETHER;
9598c2ecf20Sopenharmony_ci	switch(*mode) {
9608c2ecf20Sopenharmony_ci		case IW_MODE_MONITOR:
9618c2ecf20Sopenharmony_ci			monitor = 1;
9628c2ecf20Sopenharmony_ci			zd->dev->type = ARPHRD_IEEE80211;
9638c2ecf20Sopenharmony_ci			/* Make sure we are no longer associated with by
9648c2ecf20Sopenharmony_ci			   setting an 'impossible' essid.
9658c2ecf20Sopenharmony_ci			   (otherwise we mess up firmware)
9668c2ecf20Sopenharmony_ci			 */
9678c2ecf20Sopenharmony_ci			zd1201_join(zd, "\0-*#\0", 5);
9688c2ecf20Sopenharmony_ci			/* Put port in pIBSS */
9698c2ecf20Sopenharmony_ci			/* Fall through */
9708c2ecf20Sopenharmony_ci		case 8: /* No pseudo-IBSS in wireless extensions (yet) */
9718c2ecf20Sopenharmony_ci			porttype = ZD1201_PORTTYPE_PSEUDOIBSS;
9728c2ecf20Sopenharmony_ci			break;
9738c2ecf20Sopenharmony_ci		case IW_MODE_ADHOC:
9748c2ecf20Sopenharmony_ci			porttype = ZD1201_PORTTYPE_IBSS;
9758c2ecf20Sopenharmony_ci			break;
9768c2ecf20Sopenharmony_ci		case IW_MODE_INFRA:
9778c2ecf20Sopenharmony_ci			porttype = ZD1201_PORTTYPE_BSS;
9788c2ecf20Sopenharmony_ci			break;
9798c2ecf20Sopenharmony_ci		default:
9808c2ecf20Sopenharmony_ci			return -EINVAL;
9818c2ecf20Sopenharmony_ci	}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
9848c2ecf20Sopenharmony_ci	if (err)
9858c2ecf20Sopenharmony_ci		return err;
9868c2ecf20Sopenharmony_ci	if (zd->monitor && !monitor) {
9878c2ecf20Sopenharmony_ci			zd1201_disable(zd);
9888c2ecf20Sopenharmony_ci			*(__le16 *)buffer = cpu_to_le16(zd->essidlen);
9898c2ecf20Sopenharmony_ci			memcpy(buffer+2, zd->essid, zd->essidlen);
9908c2ecf20Sopenharmony_ci			err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID,
9918c2ecf20Sopenharmony_ci			    buffer, IW_ESSID_MAX_SIZE+2, 1);
9928c2ecf20Sopenharmony_ci			if (err)
9938c2ecf20Sopenharmony_ci				return err;
9948c2ecf20Sopenharmony_ci	}
9958c2ecf20Sopenharmony_ci	zd->monitor = monitor;
9968c2ecf20Sopenharmony_ci	/* If monitor mode is set we don't actually turn it on here since it
9978c2ecf20Sopenharmony_ci	 * is done during mac reset anyway (see zd1201_mac_enable).
9988c2ecf20Sopenharmony_ci	 */
9998c2ecf20Sopenharmony_ci	zd1201_mac_reset(zd);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	return 0;
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistatic int zd1201_get_mode(struct net_device *dev,
10058c2ecf20Sopenharmony_ci    struct iw_request_info *info, __u32 *mode, char *extra)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
10088c2ecf20Sopenharmony_ci	short porttype;
10098c2ecf20Sopenharmony_ci	int err;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	err = zd1201_getconfig16(zd, ZD1201_RID_CNFPORTTYPE, &porttype);
10128c2ecf20Sopenharmony_ci	if (err)
10138c2ecf20Sopenharmony_ci		return err;
10148c2ecf20Sopenharmony_ci	switch(porttype) {
10158c2ecf20Sopenharmony_ci		case ZD1201_PORTTYPE_IBSS:
10168c2ecf20Sopenharmony_ci			*mode = IW_MODE_ADHOC;
10178c2ecf20Sopenharmony_ci			break;
10188c2ecf20Sopenharmony_ci		case ZD1201_PORTTYPE_BSS:
10198c2ecf20Sopenharmony_ci			*mode = IW_MODE_INFRA;
10208c2ecf20Sopenharmony_ci			break;
10218c2ecf20Sopenharmony_ci		case ZD1201_PORTTYPE_WDS:
10228c2ecf20Sopenharmony_ci			*mode = IW_MODE_REPEAT;
10238c2ecf20Sopenharmony_ci			break;
10248c2ecf20Sopenharmony_ci		case ZD1201_PORTTYPE_PSEUDOIBSS:
10258c2ecf20Sopenharmony_ci			*mode = 8;/* No Pseudo-IBSS... */
10268c2ecf20Sopenharmony_ci			break;
10278c2ecf20Sopenharmony_ci		case ZD1201_PORTTYPE_AP:
10288c2ecf20Sopenharmony_ci			*mode = IW_MODE_MASTER;
10298c2ecf20Sopenharmony_ci			break;
10308c2ecf20Sopenharmony_ci		default:
10318c2ecf20Sopenharmony_ci			dev_dbg(&zd->usb->dev, "Unknown porttype: %d\n",
10328c2ecf20Sopenharmony_ci			    porttype);
10338c2ecf20Sopenharmony_ci			*mode = IW_MODE_AUTO;
10348c2ecf20Sopenharmony_ci	}
10358c2ecf20Sopenharmony_ci	if (zd->monitor)
10368c2ecf20Sopenharmony_ci		*mode = IW_MODE_MONITOR;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	return 0;
10398c2ecf20Sopenharmony_ci}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_cistatic int zd1201_get_range(struct net_device *dev,
10428c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_point *wrq, char *extra)
10438c2ecf20Sopenharmony_ci{
10448c2ecf20Sopenharmony_ci	struct iw_range *range = (struct iw_range *)extra;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	wrq->length = sizeof(struct iw_range);
10478c2ecf20Sopenharmony_ci	memset(range, 0, sizeof(struct iw_range));
10488c2ecf20Sopenharmony_ci	range->we_version_compiled = WIRELESS_EXT;
10498c2ecf20Sopenharmony_ci	range->we_version_source = WIRELESS_EXT;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	range->max_qual.qual = 128;
10528c2ecf20Sopenharmony_ci	range->max_qual.level = 128;
10538c2ecf20Sopenharmony_ci	range->max_qual.noise = 128;
10548c2ecf20Sopenharmony_ci	range->max_qual.updated = 7;
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	range->encoding_size[0] = 5;
10578c2ecf20Sopenharmony_ci	range->encoding_size[1] = 13;
10588c2ecf20Sopenharmony_ci	range->num_encoding_sizes = 2;
10598c2ecf20Sopenharmony_ci	range->max_encoding_tokens = ZD1201_NUMKEYS;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	range->num_bitrates = 4;
10628c2ecf20Sopenharmony_ci	range->bitrate[0] = 1000000;
10638c2ecf20Sopenharmony_ci	range->bitrate[1] = 2000000;
10648c2ecf20Sopenharmony_ci	range->bitrate[2] = 5500000;
10658c2ecf20Sopenharmony_ci	range->bitrate[3] = 11000000;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	range->min_rts = 0;
10688c2ecf20Sopenharmony_ci	range->min_frag = ZD1201_FRAGMIN;
10698c2ecf20Sopenharmony_ci	range->max_rts = ZD1201_RTSMAX;
10708c2ecf20Sopenharmony_ci	range->min_frag = ZD1201_FRAGMAX;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	return 0;
10738c2ecf20Sopenharmony_ci}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci/*	Little bit of magic here: we only get the quality if we poll
10768c2ecf20Sopenharmony_ci *	for it, and we never get an actual request to trigger such
10778c2ecf20Sopenharmony_ci *	a poll. Therefore we 'assume' that the user will soon ask for
10788c2ecf20Sopenharmony_ci *	the stats after asking the bssid.
10798c2ecf20Sopenharmony_ci */
10808c2ecf20Sopenharmony_cistatic int zd1201_get_wap(struct net_device *dev,
10818c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct sockaddr *ap_addr, char *extra)
10828c2ecf20Sopenharmony_ci{
10838c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
10848c2ecf20Sopenharmony_ci	unsigned char buffer[6];
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	if (!zd1201_getconfig(zd, ZD1201_RID_COMMSQUALITY, buffer, 6)) {
10878c2ecf20Sopenharmony_ci		/* Unfortunately the quality and noise reported is useless.
10888c2ecf20Sopenharmony_ci		   they seem to be accumulators that increase until you
10898c2ecf20Sopenharmony_ci		   read them, unless we poll on a fixed interval we can't
10908c2ecf20Sopenharmony_ci		   use them
10918c2ecf20Sopenharmony_ci		 */
10928c2ecf20Sopenharmony_ci		/*zd->iwstats.qual.qual = le16_to_cpu(((__le16 *)buffer)[0]);*/
10938c2ecf20Sopenharmony_ci		zd->iwstats.qual.level = le16_to_cpu(((__le16 *)buffer)[1]);
10948c2ecf20Sopenharmony_ci		/*zd->iwstats.qual.noise = le16_to_cpu(((__le16 *)buffer)[2]);*/
10958c2ecf20Sopenharmony_ci		zd->iwstats.qual.updated = 2;
10968c2ecf20Sopenharmony_ci	}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	return zd1201_getconfig(zd, ZD1201_RID_CURRENTBSSID, ap_addr->sa_data, 6);
10998c2ecf20Sopenharmony_ci}
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_cistatic int zd1201_set_scan(struct net_device *dev,
11028c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_point *srq, char *extra)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	/* We do everything in get_scan */
11058c2ecf20Sopenharmony_ci	return 0;
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic int zd1201_get_scan(struct net_device *dev,
11098c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_point *srq, char *extra)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
11128c2ecf20Sopenharmony_ci	int err, i, j, enabled_save;
11138c2ecf20Sopenharmony_ci	struct iw_event iwe;
11148c2ecf20Sopenharmony_ci	char *cev = extra;
11158c2ecf20Sopenharmony_ci	char *end_buf = extra + IW_SCAN_MAX_DATA;
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	/* No scanning in AP mode */
11188c2ecf20Sopenharmony_ci	if (zd->ap)
11198c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	/* Scan doesn't seem to work if disabled */
11228c2ecf20Sopenharmony_ci	enabled_save = zd->mac_enabled;
11238c2ecf20Sopenharmony_ci	zd1201_enable(zd);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	zd->rxdatas = 0;
11268c2ecf20Sopenharmony_ci	err = zd1201_docmd(zd, ZD1201_CMDCODE_INQUIRE,
11278c2ecf20Sopenharmony_ci	     ZD1201_INQ_SCANRESULTS, 0, 0);
11288c2ecf20Sopenharmony_ci	if (err)
11298c2ecf20Sopenharmony_ci		return err;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	wait_event_interruptible(zd->rxdataq, zd->rxdatas);
11328c2ecf20Sopenharmony_ci	if (!zd->rxlen)
11338c2ecf20Sopenharmony_ci		return -EIO;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	if (le16_to_cpu(*(__le16*)&zd->rxdata[2]) != ZD1201_INQ_SCANRESULTS)
11368c2ecf20Sopenharmony_ci		return -EIO;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	for(i=8; i<zd->rxlen; i+=62) {
11398c2ecf20Sopenharmony_ci		iwe.cmd = SIOCGIWAP;
11408c2ecf20Sopenharmony_ci		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
11418c2ecf20Sopenharmony_ci		memcpy(iwe.u.ap_addr.sa_data, zd->rxdata+i+6, 6);
11428c2ecf20Sopenharmony_ci		cev = iwe_stream_add_event(info, cev, end_buf,
11438c2ecf20Sopenharmony_ci					   &iwe, IW_EV_ADDR_LEN);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci		iwe.cmd = SIOCGIWESSID;
11468c2ecf20Sopenharmony_ci		iwe.u.data.length = zd->rxdata[i+16];
11478c2ecf20Sopenharmony_ci		iwe.u.data.flags = 1;
11488c2ecf20Sopenharmony_ci		cev = iwe_stream_add_point(info, cev, end_buf,
11498c2ecf20Sopenharmony_ci					   &iwe, zd->rxdata+i+18);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci		iwe.cmd = SIOCGIWMODE;
11528c2ecf20Sopenharmony_ci		if (zd->rxdata[i+14]&0x01)
11538c2ecf20Sopenharmony_ci			iwe.u.mode = IW_MODE_MASTER;
11548c2ecf20Sopenharmony_ci		else
11558c2ecf20Sopenharmony_ci			iwe.u.mode = IW_MODE_ADHOC;
11568c2ecf20Sopenharmony_ci		cev = iwe_stream_add_event(info, cev, end_buf,
11578c2ecf20Sopenharmony_ci					   &iwe, IW_EV_UINT_LEN);
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci		iwe.cmd = SIOCGIWFREQ;
11608c2ecf20Sopenharmony_ci		iwe.u.freq.m = zd->rxdata[i+0];
11618c2ecf20Sopenharmony_ci		iwe.u.freq.e = 0;
11628c2ecf20Sopenharmony_ci		cev = iwe_stream_add_event(info, cev, end_buf,
11638c2ecf20Sopenharmony_ci					   &iwe, IW_EV_FREQ_LEN);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci		iwe.cmd = SIOCGIWRATE;
11668c2ecf20Sopenharmony_ci		iwe.u.bitrate.fixed = 0;
11678c2ecf20Sopenharmony_ci		iwe.u.bitrate.disabled = 0;
11688c2ecf20Sopenharmony_ci		for (j=0; j<10; j++) if (zd->rxdata[i+50+j]) {
11698c2ecf20Sopenharmony_ci			iwe.u.bitrate.value = (zd->rxdata[i+50+j]&0x7f)*500000;
11708c2ecf20Sopenharmony_ci			cev = iwe_stream_add_event(info, cev, end_buf,
11718c2ecf20Sopenharmony_ci						   &iwe, IW_EV_PARAM_LEN);
11728c2ecf20Sopenharmony_ci		}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci		iwe.cmd = SIOCGIWENCODE;
11758c2ecf20Sopenharmony_ci		iwe.u.data.length = 0;
11768c2ecf20Sopenharmony_ci		if (zd->rxdata[i+14]&0x10)
11778c2ecf20Sopenharmony_ci			iwe.u.data.flags = IW_ENCODE_ENABLED;
11788c2ecf20Sopenharmony_ci		else
11798c2ecf20Sopenharmony_ci			iwe.u.data.flags = IW_ENCODE_DISABLED;
11808c2ecf20Sopenharmony_ci		cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL);
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci		iwe.cmd = IWEVQUAL;
11838c2ecf20Sopenharmony_ci		iwe.u.qual.qual = zd->rxdata[i+4];
11848c2ecf20Sopenharmony_ci		iwe.u.qual.noise= zd->rxdata[i+2]/10-100;
11858c2ecf20Sopenharmony_ci		iwe.u.qual.level = (256+zd->rxdata[i+4]*100)/255-100;
11868c2ecf20Sopenharmony_ci		iwe.u.qual.updated = 7;
11878c2ecf20Sopenharmony_ci		cev = iwe_stream_add_event(info, cev, end_buf,
11888c2ecf20Sopenharmony_ci					   &iwe, IW_EV_QUAL_LEN);
11898c2ecf20Sopenharmony_ci	}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	if (!enabled_save)
11928c2ecf20Sopenharmony_ci		zd1201_disable(zd);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	srq->length = cev - extra;
11958c2ecf20Sopenharmony_ci	srq->flags = 0;
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	return 0;
11988c2ecf20Sopenharmony_ci}
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_cistatic int zd1201_set_essid(struct net_device *dev,
12018c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_point *data, char *essid)
12028c2ecf20Sopenharmony_ci{
12038c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	if (data->length > IW_ESSID_MAX_SIZE)
12068c2ecf20Sopenharmony_ci		return -EINVAL;
12078c2ecf20Sopenharmony_ci	if (data->length < 1)
12088c2ecf20Sopenharmony_ci		data->length = 1;
12098c2ecf20Sopenharmony_ci	zd->essidlen = data->length;
12108c2ecf20Sopenharmony_ci	memset(zd->essid, 0, IW_ESSID_MAX_SIZE+1);
12118c2ecf20Sopenharmony_ci	memcpy(zd->essid, essid, data->length);
12128c2ecf20Sopenharmony_ci	return zd1201_join(zd, zd->essid, zd->essidlen);
12138c2ecf20Sopenharmony_ci}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_cistatic int zd1201_get_essid(struct net_device *dev,
12168c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_point *data, char *essid)
12178c2ecf20Sopenharmony_ci{
12188c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	memcpy(essid, zd->essid, zd->essidlen);
12218c2ecf20Sopenharmony_ci	data->flags = 1;
12228c2ecf20Sopenharmony_ci	data->length = zd->essidlen;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	return 0;
12258c2ecf20Sopenharmony_ci}
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_cistatic int zd1201_get_nick(struct net_device *dev, struct iw_request_info *info,
12288c2ecf20Sopenharmony_ci    struct iw_point *data, char *nick)
12298c2ecf20Sopenharmony_ci{
12308c2ecf20Sopenharmony_ci	strcpy(nick, "zd1201");
12318c2ecf20Sopenharmony_ci	data->flags = 1;
12328c2ecf20Sopenharmony_ci	data->length = strlen(nick);
12338c2ecf20Sopenharmony_ci	return 0;
12348c2ecf20Sopenharmony_ci}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_cistatic int zd1201_set_rate(struct net_device *dev,
12378c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_param *rrq, char *extra)
12388c2ecf20Sopenharmony_ci{
12398c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
12408c2ecf20Sopenharmony_ci	short rate;
12418c2ecf20Sopenharmony_ci	int err;
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	switch (rrq->value) {
12448c2ecf20Sopenharmony_ci		case 1000000:
12458c2ecf20Sopenharmony_ci			rate = ZD1201_RATEB1;
12468c2ecf20Sopenharmony_ci			break;
12478c2ecf20Sopenharmony_ci		case 2000000:
12488c2ecf20Sopenharmony_ci			rate = ZD1201_RATEB2;
12498c2ecf20Sopenharmony_ci			break;
12508c2ecf20Sopenharmony_ci		case 5500000:
12518c2ecf20Sopenharmony_ci			rate = ZD1201_RATEB5;
12528c2ecf20Sopenharmony_ci			break;
12538c2ecf20Sopenharmony_ci		case 11000000:
12548c2ecf20Sopenharmony_ci		default:
12558c2ecf20Sopenharmony_ci			rate = ZD1201_RATEB11;
12568c2ecf20Sopenharmony_ci			break;
12578c2ecf20Sopenharmony_ci	}
12588c2ecf20Sopenharmony_ci	if (!rrq->fixed) { /* Also enable all lower bitrates */
12598c2ecf20Sopenharmony_ci		rate |= rate-1;
12608c2ecf20Sopenharmony_ci	}
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL, rate);
12638c2ecf20Sopenharmony_ci	if (err)
12648c2ecf20Sopenharmony_ci		return err;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	return zd1201_mac_reset(zd);
12678c2ecf20Sopenharmony_ci}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_cistatic int zd1201_get_rate(struct net_device *dev,
12708c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_param *rrq, char *extra)
12718c2ecf20Sopenharmony_ci{
12728c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
12738c2ecf20Sopenharmony_ci	short rate;
12748c2ecf20Sopenharmony_ci	int err;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	err = zd1201_getconfig16(zd, ZD1201_RID_CURRENTTXRATE, &rate);
12778c2ecf20Sopenharmony_ci	if (err)
12788c2ecf20Sopenharmony_ci		return err;
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	switch(rate) {
12818c2ecf20Sopenharmony_ci		case 1:
12828c2ecf20Sopenharmony_ci			rrq->value = 1000000;
12838c2ecf20Sopenharmony_ci			break;
12848c2ecf20Sopenharmony_ci		case 2:
12858c2ecf20Sopenharmony_ci			rrq->value = 2000000;
12868c2ecf20Sopenharmony_ci			break;
12878c2ecf20Sopenharmony_ci		case 5:
12888c2ecf20Sopenharmony_ci			rrq->value = 5500000;
12898c2ecf20Sopenharmony_ci			break;
12908c2ecf20Sopenharmony_ci		case 11:
12918c2ecf20Sopenharmony_ci			rrq->value = 11000000;
12928c2ecf20Sopenharmony_ci			break;
12938c2ecf20Sopenharmony_ci		default:
12948c2ecf20Sopenharmony_ci			rrq->value = 0;
12958c2ecf20Sopenharmony_ci	}
12968c2ecf20Sopenharmony_ci	rrq->fixed = 0;
12978c2ecf20Sopenharmony_ci	rrq->disabled = 0;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	return 0;
13008c2ecf20Sopenharmony_ci}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_cistatic int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info,
13038c2ecf20Sopenharmony_ci    struct iw_param *rts, char *extra)
13048c2ecf20Sopenharmony_ci{
13058c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
13068c2ecf20Sopenharmony_ci	int err;
13078c2ecf20Sopenharmony_ci	short val = rts->value;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	if (rts->disabled || !rts->fixed)
13108c2ecf20Sopenharmony_ci		val = ZD1201_RTSMAX;
13118c2ecf20Sopenharmony_ci	if (val > ZD1201_RTSMAX)
13128c2ecf20Sopenharmony_ci		return -EINVAL;
13138c2ecf20Sopenharmony_ci	if (val < 0)
13148c2ecf20Sopenharmony_ci		return -EINVAL;
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, val);
13178c2ecf20Sopenharmony_ci	if (err)
13188c2ecf20Sopenharmony_ci		return err;
13198c2ecf20Sopenharmony_ci	return zd1201_mac_reset(zd);
13208c2ecf20Sopenharmony_ci}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_cistatic int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info,
13238c2ecf20Sopenharmony_ci    struct iw_param *rts, char *extra)
13248c2ecf20Sopenharmony_ci{
13258c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
13268c2ecf20Sopenharmony_ci	short rtst;
13278c2ecf20Sopenharmony_ci	int err;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	err = zd1201_getconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, &rtst);
13308c2ecf20Sopenharmony_ci	if (err)
13318c2ecf20Sopenharmony_ci		return err;
13328c2ecf20Sopenharmony_ci	rts->value = rtst;
13338c2ecf20Sopenharmony_ci	rts->disabled = (rts->value == ZD1201_RTSMAX);
13348c2ecf20Sopenharmony_ci	rts->fixed = 1;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	return 0;
13378c2ecf20Sopenharmony_ci}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_cistatic int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info,
13408c2ecf20Sopenharmony_ci    struct iw_param *frag, char *extra)
13418c2ecf20Sopenharmony_ci{
13428c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
13438c2ecf20Sopenharmony_ci	int err;
13448c2ecf20Sopenharmony_ci	short val = frag->value;
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	if (frag->disabled || !frag->fixed)
13478c2ecf20Sopenharmony_ci		val = ZD1201_FRAGMAX;
13488c2ecf20Sopenharmony_ci	if (val > ZD1201_FRAGMAX)
13498c2ecf20Sopenharmony_ci		return -EINVAL;
13508c2ecf20Sopenharmony_ci	if (val < ZD1201_FRAGMIN)
13518c2ecf20Sopenharmony_ci		return -EINVAL;
13528c2ecf20Sopenharmony_ci	if (val & 1)
13538c2ecf20Sopenharmony_ci		return -EINVAL;
13548c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, val);
13558c2ecf20Sopenharmony_ci	if (err)
13568c2ecf20Sopenharmony_ci		return err;
13578c2ecf20Sopenharmony_ci	return zd1201_mac_reset(zd);
13588c2ecf20Sopenharmony_ci}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_cistatic int zd1201_get_frag(struct net_device *dev, struct iw_request_info *info,
13618c2ecf20Sopenharmony_ci    struct iw_param *frag, char *extra)
13628c2ecf20Sopenharmony_ci{
13638c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
13648c2ecf20Sopenharmony_ci	short fragt;
13658c2ecf20Sopenharmony_ci	int err;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	err = zd1201_getconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, &fragt);
13688c2ecf20Sopenharmony_ci	if (err)
13698c2ecf20Sopenharmony_ci		return err;
13708c2ecf20Sopenharmony_ci	frag->value = fragt;
13718c2ecf20Sopenharmony_ci	frag->disabled = (frag->value == ZD1201_FRAGMAX);
13728c2ecf20Sopenharmony_ci	frag->fixed = 1;
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	return 0;
13758c2ecf20Sopenharmony_ci}
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_cistatic int zd1201_set_retry(struct net_device *dev,
13788c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_param *rrq, char *extra)
13798c2ecf20Sopenharmony_ci{
13808c2ecf20Sopenharmony_ci	return 0;
13818c2ecf20Sopenharmony_ci}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic int zd1201_get_retry(struct net_device *dev,
13848c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_param *rrq, char *extra)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	return 0;
13878c2ecf20Sopenharmony_ci}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_cistatic int zd1201_set_encode(struct net_device *dev,
13908c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_point *erq, char *key)
13918c2ecf20Sopenharmony_ci{
13928c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
13938c2ecf20Sopenharmony_ci	short i;
13948c2ecf20Sopenharmony_ci	int err, rid;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	if (erq->length > ZD1201_MAXKEYLEN)
13978c2ecf20Sopenharmony_ci		return -EINVAL;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	i = (erq->flags & IW_ENCODE_INDEX)-1;
14008c2ecf20Sopenharmony_ci	if (i == -1) {
14018c2ecf20Sopenharmony_ci		err = zd1201_getconfig16(zd,ZD1201_RID_CNFDEFAULTKEYID,&i);
14028c2ecf20Sopenharmony_ci		if (err)
14038c2ecf20Sopenharmony_ci			return err;
14048c2ecf20Sopenharmony_ci	} else {
14058c2ecf20Sopenharmony_ci		err = zd1201_setconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, i);
14068c2ecf20Sopenharmony_ci		if (err)
14078c2ecf20Sopenharmony_ci			return err;
14088c2ecf20Sopenharmony_ci	}
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	if (i < 0 || i >= ZD1201_NUMKEYS)
14118c2ecf20Sopenharmony_ci		return -EINVAL;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	rid = ZD1201_RID_CNFDEFAULTKEY0 + i;
14148c2ecf20Sopenharmony_ci	err = zd1201_setconfig(zd, rid, key, erq->length, 1);
14158c2ecf20Sopenharmony_ci	if (err)
14168c2ecf20Sopenharmony_ci		return err;
14178c2ecf20Sopenharmony_ci	zd->encode_keylen[i] = erq->length;
14188c2ecf20Sopenharmony_ci	memcpy(zd->encode_keys[i], key, erq->length);
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	i=0;
14218c2ecf20Sopenharmony_ci	if (!(erq->flags & IW_ENCODE_DISABLED & IW_ENCODE_MODE)) {
14228c2ecf20Sopenharmony_ci		i |= 0x01;
14238c2ecf20Sopenharmony_ci		zd->encode_enabled = 1;
14248c2ecf20Sopenharmony_ci	} else
14258c2ecf20Sopenharmony_ci		zd->encode_enabled = 0;
14268c2ecf20Sopenharmony_ci	if (erq->flags & IW_ENCODE_RESTRICTED & IW_ENCODE_MODE) {
14278c2ecf20Sopenharmony_ci		i |= 0x02;
14288c2ecf20Sopenharmony_ci		zd->encode_restricted = 1;
14298c2ecf20Sopenharmony_ci	} else
14308c2ecf20Sopenharmony_ci		zd->encode_restricted = 0;
14318c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_CNFWEBFLAGS, i);
14328c2ecf20Sopenharmony_ci	if (err)
14338c2ecf20Sopenharmony_ci		return err;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	if (zd->encode_enabled)
14368c2ecf20Sopenharmony_ci		i = ZD1201_CNFAUTHENTICATION_SHAREDKEY;
14378c2ecf20Sopenharmony_ci	else
14388c2ecf20Sopenharmony_ci		i = ZD1201_CNFAUTHENTICATION_OPENSYSTEM;
14398c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, i);
14408c2ecf20Sopenharmony_ci	if (err)
14418c2ecf20Sopenharmony_ci		return err;
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	return zd1201_mac_reset(zd);
14448c2ecf20Sopenharmony_ci}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_cistatic int zd1201_get_encode(struct net_device *dev,
14478c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_point *erq, char *key)
14488c2ecf20Sopenharmony_ci{
14498c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
14508c2ecf20Sopenharmony_ci	short i;
14518c2ecf20Sopenharmony_ci	int err;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	if (zd->encode_enabled)
14548c2ecf20Sopenharmony_ci		erq->flags = IW_ENCODE_ENABLED;
14558c2ecf20Sopenharmony_ci	else
14568c2ecf20Sopenharmony_ci		erq->flags = IW_ENCODE_DISABLED;
14578c2ecf20Sopenharmony_ci	if (zd->encode_restricted)
14588c2ecf20Sopenharmony_ci		erq->flags |= IW_ENCODE_RESTRICTED;
14598c2ecf20Sopenharmony_ci	else
14608c2ecf20Sopenharmony_ci		erq->flags |= IW_ENCODE_OPEN;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	i = (erq->flags & IW_ENCODE_INDEX) -1;
14638c2ecf20Sopenharmony_ci	if (i == -1) {
14648c2ecf20Sopenharmony_ci		err = zd1201_getconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, &i);
14658c2ecf20Sopenharmony_ci		if (err)
14668c2ecf20Sopenharmony_ci			return err;
14678c2ecf20Sopenharmony_ci	}
14688c2ecf20Sopenharmony_ci	if (i<0 || i>= ZD1201_NUMKEYS)
14698c2ecf20Sopenharmony_ci		return -EINVAL;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	erq->flags |= i+1;
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	erq->length = zd->encode_keylen[i];
14748c2ecf20Sopenharmony_ci	memcpy(key, zd->encode_keys[i], erq->length);
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	return 0;
14778c2ecf20Sopenharmony_ci}
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_cistatic int zd1201_set_power(struct net_device *dev,
14808c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_param *vwrq, char *extra)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
14838c2ecf20Sopenharmony_ci	short enabled, duration, level;
14848c2ecf20Sopenharmony_ci	int err;
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci	enabled = vwrq->disabled ? 0 : 1;
14878c2ecf20Sopenharmony_ci	if (enabled) {
14888c2ecf20Sopenharmony_ci		if (vwrq->flags & IW_POWER_PERIOD) {
14898c2ecf20Sopenharmony_ci			duration = vwrq->value;
14908c2ecf20Sopenharmony_ci			err = zd1201_setconfig16(zd,
14918c2ecf20Sopenharmony_ci			    ZD1201_RID_CNFMAXSLEEPDURATION, duration);
14928c2ecf20Sopenharmony_ci			if (err)
14938c2ecf20Sopenharmony_ci				return err;
14948c2ecf20Sopenharmony_ci			goto out;
14958c2ecf20Sopenharmony_ci		}
14968c2ecf20Sopenharmony_ci		if (vwrq->flags & IW_POWER_TIMEOUT) {
14978c2ecf20Sopenharmony_ci			err = zd1201_getconfig16(zd,
14988c2ecf20Sopenharmony_ci			    ZD1201_RID_CNFMAXSLEEPDURATION, &duration);
14998c2ecf20Sopenharmony_ci			if (err)
15008c2ecf20Sopenharmony_ci				return err;
15018c2ecf20Sopenharmony_ci			level = vwrq->value * 4 / duration;
15028c2ecf20Sopenharmony_ci			if (level > 4)
15038c2ecf20Sopenharmony_ci				level = 4;
15048c2ecf20Sopenharmony_ci			if (level < 0)
15058c2ecf20Sopenharmony_ci				level = 0;
15068c2ecf20Sopenharmony_ci			err = zd1201_setconfig16(zd, ZD1201_RID_CNFPMEPS,
15078c2ecf20Sopenharmony_ci			    level);
15088c2ecf20Sopenharmony_ci			if (err)
15098c2ecf20Sopenharmony_ci				return err;
15108c2ecf20Sopenharmony_ci			goto out;
15118c2ecf20Sopenharmony_ci		}
15128c2ecf20Sopenharmony_ci		return -EINVAL;
15138c2ecf20Sopenharmony_ci	}
15148c2ecf20Sopenharmony_ciout:
15158c2ecf20Sopenharmony_ci	return zd1201_setconfig16(zd, ZD1201_RID_CNFPMENABLED, enabled);
15168c2ecf20Sopenharmony_ci}
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_cistatic int zd1201_get_power(struct net_device *dev,
15198c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_param *vwrq, char *extra)
15208c2ecf20Sopenharmony_ci{
15218c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
15228c2ecf20Sopenharmony_ci	short enabled, level, duration;
15238c2ecf20Sopenharmony_ci	int err;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMENABLED, &enabled);
15268c2ecf20Sopenharmony_ci	if (err)
15278c2ecf20Sopenharmony_ci		return err;
15288c2ecf20Sopenharmony_ci	err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMEPS, &level);
15298c2ecf20Sopenharmony_ci	if (err)
15308c2ecf20Sopenharmony_ci		return err;
15318c2ecf20Sopenharmony_ci	err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXSLEEPDURATION, &duration);
15328c2ecf20Sopenharmony_ci	if (err)
15338c2ecf20Sopenharmony_ci		return err;
15348c2ecf20Sopenharmony_ci	vwrq->disabled = enabled ? 0 : 1;
15358c2ecf20Sopenharmony_ci	if (vwrq->flags & IW_POWER_TYPE) {
15368c2ecf20Sopenharmony_ci		if (vwrq->flags & IW_POWER_PERIOD) {
15378c2ecf20Sopenharmony_ci			vwrq->value = duration;
15388c2ecf20Sopenharmony_ci			vwrq->flags = IW_POWER_PERIOD;
15398c2ecf20Sopenharmony_ci		} else {
15408c2ecf20Sopenharmony_ci			vwrq->value = duration * level / 4;
15418c2ecf20Sopenharmony_ci			vwrq->flags = IW_POWER_TIMEOUT;
15428c2ecf20Sopenharmony_ci		}
15438c2ecf20Sopenharmony_ci	}
15448c2ecf20Sopenharmony_ci	if (vwrq->flags & IW_POWER_MODE) {
15458c2ecf20Sopenharmony_ci		if (enabled && level)
15468c2ecf20Sopenharmony_ci			vwrq->flags = IW_POWER_UNICAST_R;
15478c2ecf20Sopenharmony_ci		else
15488c2ecf20Sopenharmony_ci			vwrq->flags = IW_POWER_ALL_R;
15498c2ecf20Sopenharmony_ci	}
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	return 0;
15528c2ecf20Sopenharmony_ci}
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_cistatic const iw_handler zd1201_iw_handler[] =
15568c2ecf20Sopenharmony_ci{
15578c2ecf20Sopenharmony_ci	(iw_handler) zd1201_config_commit,	/* SIOCSIWCOMMIT */
15588c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_name,    	/* SIOCGIWNAME */
15598c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* SIOCSIWNWID */
15608c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* SIOCGIWNWID */
15618c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_freq,		/* SIOCSIWFREQ */
15628c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_freq,		/* SIOCGIWFREQ */
15638c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_mode,		/* SIOCSIWMODE */
15648c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_mode,		/* SIOCGIWMODE */
15658c2ecf20Sopenharmony_ci	(iw_handler) NULL,                  	/* SIOCSIWSENS */
15668c2ecf20Sopenharmony_ci	(iw_handler) NULL,           		/* SIOCGIWSENS */
15678c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* SIOCSIWRANGE */
15688c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_range,           /* SIOCGIWRANGE */
15698c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* SIOCSIWPRIV */
15708c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* SIOCGIWPRIV */
15718c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* SIOCSIWSTATS */
15728c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* SIOCGIWSTATS */
15738c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* SIOCSIWSPY */
15748c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* SIOCGIWSPY */
15758c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* -- hole -- */
15768c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* -- hole -- */
15778c2ecf20Sopenharmony_ci	(iw_handler) NULL/*zd1201_set_wap*/,		/* SIOCSIWAP */
15788c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_wap,		/* SIOCGIWAP */
15798c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* -- hole -- */
15808c2ecf20Sopenharmony_ci	(iw_handler) NULL,       		/* SIOCGIWAPLIST */
15818c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_scan,		/* SIOCSIWSCAN */
15828c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_scan,		/* SIOCGIWSCAN */
15838c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_essid,		/* SIOCSIWESSID */
15848c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_essid,		/* SIOCGIWESSID */
15858c2ecf20Sopenharmony_ci	(iw_handler) NULL,         		/* SIOCSIWNICKN */
15868c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_nick, 		/* SIOCGIWNICKN */
15878c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* -- hole -- */
15888c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* -- hole -- */
15898c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_rate,		/* SIOCSIWRATE */
15908c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_rate,		/* SIOCGIWRATE */
15918c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_rts,		/* SIOCSIWRTS */
15928c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_rts,		/* SIOCGIWRTS */
15938c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_frag,		/* SIOCSIWFRAG */
15948c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_frag,		/* SIOCGIWFRAG */
15958c2ecf20Sopenharmony_ci	(iw_handler) NULL,         		/* SIOCSIWTXPOW */
15968c2ecf20Sopenharmony_ci	(iw_handler) NULL,          		/* SIOCGIWTXPOW */
15978c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_retry,		/* SIOCSIWRETRY */
15988c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_retry,		/* SIOCGIWRETRY */
15998c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_encode,		/* SIOCSIWENCODE */
16008c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_encode,		/* SIOCGIWENCODE */
16018c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_power,		/* SIOCSIWPOWER */
16028c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_power,		/* SIOCGIWPOWER */
16038c2ecf20Sopenharmony_ci};
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_cistatic int zd1201_set_hostauth(struct net_device *dev,
16068c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_param *rrq, char *extra)
16078c2ecf20Sopenharmony_ci{
16088c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	if (!zd->ap)
16118c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	return zd1201_setconfig16(zd, ZD1201_RID_CNFHOSTAUTH, rrq->value);
16148c2ecf20Sopenharmony_ci}
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_cistatic int zd1201_get_hostauth(struct net_device *dev,
16178c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_param *rrq, char *extra)
16188c2ecf20Sopenharmony_ci{
16198c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
16208c2ecf20Sopenharmony_ci	short hostauth;
16218c2ecf20Sopenharmony_ci	int err;
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	if (!zd->ap)
16248c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	err = zd1201_getconfig16(zd, ZD1201_RID_CNFHOSTAUTH, &hostauth);
16278c2ecf20Sopenharmony_ci	if (err)
16288c2ecf20Sopenharmony_ci		return err;
16298c2ecf20Sopenharmony_ci	rrq->value = hostauth;
16308c2ecf20Sopenharmony_ci	rrq->fixed = 1;
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	return 0;
16338c2ecf20Sopenharmony_ci}
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_cistatic int zd1201_auth_sta(struct net_device *dev,
16368c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct sockaddr *sta, char *extra)
16378c2ecf20Sopenharmony_ci{
16388c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
16398c2ecf20Sopenharmony_ci	unsigned char buffer[10];
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	if (!zd->ap)
16428c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	memcpy(buffer, sta->sa_data, ETH_ALEN);
16458c2ecf20Sopenharmony_ci	*(short*)(buffer+6) = 0;	/* 0==success, 1==failure */
16468c2ecf20Sopenharmony_ci	*(short*)(buffer+8) = 0;
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	return zd1201_setconfig(zd, ZD1201_RID_AUTHENTICATESTA, buffer, 10, 1);
16498c2ecf20Sopenharmony_ci}
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_cistatic int zd1201_set_maxassoc(struct net_device *dev,
16528c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_param *rrq, char *extra)
16538c2ecf20Sopenharmony_ci{
16548c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci	if (!zd->ap)
16578c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
16588c2ecf20Sopenharmony_ci
16598c2ecf20Sopenharmony_ci	return zd1201_setconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, rrq->value);
16608c2ecf20Sopenharmony_ci}
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_cistatic int zd1201_get_maxassoc(struct net_device *dev,
16638c2ecf20Sopenharmony_ci    struct iw_request_info *info, struct iw_param *rrq, char *extra)
16648c2ecf20Sopenharmony_ci{
16658c2ecf20Sopenharmony_ci	struct zd1201 *zd = netdev_priv(dev);
16668c2ecf20Sopenharmony_ci	short maxassoc;
16678c2ecf20Sopenharmony_ci	int err;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	if (!zd->ap)
16708c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, &maxassoc);
16738c2ecf20Sopenharmony_ci	if (err)
16748c2ecf20Sopenharmony_ci		return err;
16758c2ecf20Sopenharmony_ci	rrq->value = maxassoc;
16768c2ecf20Sopenharmony_ci	rrq->fixed = 1;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	return 0;
16798c2ecf20Sopenharmony_ci}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_cistatic const iw_handler zd1201_private_handler[] = {
16828c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_hostauth,	/* ZD1201SIWHOSTAUTH */
16838c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_hostauth,	/* ZD1201GIWHOSTAUTH */
16848c2ecf20Sopenharmony_ci	(iw_handler) zd1201_auth_sta,		/* ZD1201SIWAUTHSTA */
16858c2ecf20Sopenharmony_ci	(iw_handler) NULL,			/* nothing to get */
16868c2ecf20Sopenharmony_ci	(iw_handler) zd1201_set_maxassoc,	/* ZD1201SIMAXASSOC */
16878c2ecf20Sopenharmony_ci	(iw_handler) zd1201_get_maxassoc,	/* ZD1201GIMAXASSOC */
16888c2ecf20Sopenharmony_ci};
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_cistatic const struct iw_priv_args zd1201_private_args[] = {
16918c2ecf20Sopenharmony_ci	{ ZD1201SIWHOSTAUTH, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
16928c2ecf20Sopenharmony_ci	    IW_PRIV_TYPE_NONE, "sethostauth" },
16938c2ecf20Sopenharmony_ci	{ ZD1201GIWHOSTAUTH, IW_PRIV_TYPE_NONE,
16948c2ecf20Sopenharmony_ci	    IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostauth" },
16958c2ecf20Sopenharmony_ci	{ ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1,
16968c2ecf20Sopenharmony_ci	    IW_PRIV_TYPE_NONE, "authstation" },
16978c2ecf20Sopenharmony_ci	{ ZD1201SIWMAXASSOC, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
16988c2ecf20Sopenharmony_ci	    IW_PRIV_TYPE_NONE, "setmaxassoc" },
16998c2ecf20Sopenharmony_ci	{ ZD1201GIWMAXASSOC, IW_PRIV_TYPE_NONE,
17008c2ecf20Sopenharmony_ci	    IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmaxassoc" },
17018c2ecf20Sopenharmony_ci};
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_cistatic const struct iw_handler_def zd1201_iw_handlers = {
17048c2ecf20Sopenharmony_ci	.num_standard 		= ARRAY_SIZE(zd1201_iw_handler),
17058c2ecf20Sopenharmony_ci	.num_private 		= ARRAY_SIZE(zd1201_private_handler),
17068c2ecf20Sopenharmony_ci	.num_private_args 	= ARRAY_SIZE(zd1201_private_args),
17078c2ecf20Sopenharmony_ci	.standard 		= (iw_handler *)zd1201_iw_handler,
17088c2ecf20Sopenharmony_ci	.private 		= (iw_handler *)zd1201_private_handler,
17098c2ecf20Sopenharmony_ci	.private_args 		= (struct iw_priv_args *) zd1201_private_args,
17108c2ecf20Sopenharmony_ci	.get_wireless_stats	= zd1201_get_wireless_stats,
17118c2ecf20Sopenharmony_ci};
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_cistatic const struct net_device_ops zd1201_netdev_ops = {
17148c2ecf20Sopenharmony_ci	.ndo_open		= zd1201_net_open,
17158c2ecf20Sopenharmony_ci	.ndo_stop		= zd1201_net_stop,
17168c2ecf20Sopenharmony_ci	.ndo_start_xmit		= zd1201_hard_start_xmit,
17178c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= zd1201_tx_timeout,
17188c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= zd1201_set_multicast,
17198c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= zd1201_set_mac_address,
17208c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
17218c2ecf20Sopenharmony_ci};
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_cistatic int zd1201_probe(struct usb_interface *interface,
17248c2ecf20Sopenharmony_ci			const struct usb_device_id *id)
17258c2ecf20Sopenharmony_ci{
17268c2ecf20Sopenharmony_ci	struct zd1201 *zd;
17278c2ecf20Sopenharmony_ci	struct net_device *dev;
17288c2ecf20Sopenharmony_ci	struct usb_device *usb;
17298c2ecf20Sopenharmony_ci	int err;
17308c2ecf20Sopenharmony_ci	short porttype;
17318c2ecf20Sopenharmony_ci	char buf[IW_ESSID_MAX_SIZE+2];
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	usb = interface_to_usbdev(interface);
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(*zd));
17368c2ecf20Sopenharmony_ci	if (!dev)
17378c2ecf20Sopenharmony_ci		return -ENOMEM;
17388c2ecf20Sopenharmony_ci	zd = netdev_priv(dev);
17398c2ecf20Sopenharmony_ci	zd->dev = dev;
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	zd->ap = ap;
17428c2ecf20Sopenharmony_ci	zd->usb = usb;
17438c2ecf20Sopenharmony_ci	zd->removed = 0;
17448c2ecf20Sopenharmony_ci	init_waitqueue_head(&zd->rxdataq);
17458c2ecf20Sopenharmony_ci	INIT_HLIST_HEAD(&zd->fraglist);
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci	err = zd1201_fw_upload(usb, zd->ap);
17488c2ecf20Sopenharmony_ci	if (err) {
17498c2ecf20Sopenharmony_ci		dev_err(&usb->dev, "zd1201 firmware upload failed: %d\n", err);
17508c2ecf20Sopenharmony_ci		goto err_zd;
17518c2ecf20Sopenharmony_ci	}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	zd->endp_in = 1;
17548c2ecf20Sopenharmony_ci	zd->endp_out = 1;
17558c2ecf20Sopenharmony_ci	zd->endp_out2 = 2;
17568c2ecf20Sopenharmony_ci	zd->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
17578c2ecf20Sopenharmony_ci	zd->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
17588c2ecf20Sopenharmony_ci	if (!zd->rx_urb || !zd->tx_urb) {
17598c2ecf20Sopenharmony_ci		err = -ENOMEM;
17608c2ecf20Sopenharmony_ci		goto err_zd;
17618c2ecf20Sopenharmony_ci	}
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	mdelay(100);
17648c2ecf20Sopenharmony_ci	err = zd1201_drvr_start(zd);
17658c2ecf20Sopenharmony_ci	if (err)
17668c2ecf20Sopenharmony_ci		goto err_zd;
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXDATALEN, 2312);
17698c2ecf20Sopenharmony_ci	if (err)
17708c2ecf20Sopenharmony_ci		goto err_start;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL,
17738c2ecf20Sopenharmony_ci	    ZD1201_RATEB1 | ZD1201_RATEB2 | ZD1201_RATEB5 | ZD1201_RATEB11);
17748c2ecf20Sopenharmony_ci	if (err)
17758c2ecf20Sopenharmony_ci		goto err_start;
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	dev->netdev_ops = &zd1201_netdev_ops;
17788c2ecf20Sopenharmony_ci	dev->wireless_handlers = &zd1201_iw_handlers;
17798c2ecf20Sopenharmony_ci	dev->watchdog_timeo = ZD1201_TX_TIMEOUT;
17808c2ecf20Sopenharmony_ci	strcpy(dev->name, "wlan%d");
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci	err = zd1201_getconfig(zd, ZD1201_RID_CNFOWNMACADDR,
17838c2ecf20Sopenharmony_ci	    dev->dev_addr, dev->addr_len);
17848c2ecf20Sopenharmony_ci	if (err)
17858c2ecf20Sopenharmony_ci		goto err_start;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	/* Set wildcard essid to match zd->essid */
17888c2ecf20Sopenharmony_ci	*(__le16 *)buf = cpu_to_le16(0);
17898c2ecf20Sopenharmony_ci	err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
17908c2ecf20Sopenharmony_ci	    IW_ESSID_MAX_SIZE+2, 1);
17918c2ecf20Sopenharmony_ci	if (err)
17928c2ecf20Sopenharmony_ci		goto err_start;
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	if (zd->ap)
17958c2ecf20Sopenharmony_ci		porttype = ZD1201_PORTTYPE_AP;
17968c2ecf20Sopenharmony_ci	else
17978c2ecf20Sopenharmony_ci		porttype = ZD1201_PORTTYPE_BSS;
17988c2ecf20Sopenharmony_ci	err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
17998c2ecf20Sopenharmony_ci	if (err)
18008c2ecf20Sopenharmony_ci		goto err_start;
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &usb->dev);
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	err = register_netdev(dev);
18058c2ecf20Sopenharmony_ci	if (err)
18068c2ecf20Sopenharmony_ci		goto err_start;
18078c2ecf20Sopenharmony_ci	dev_info(&usb->dev, "%s: ZD1201 USB Wireless interface\n",
18088c2ecf20Sopenharmony_ci	    dev->name);
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci	usb_set_intfdata(interface, zd);
18118c2ecf20Sopenharmony_ci	zd1201_enable(zd);	/* zd1201 likes to startup enabled, */
18128c2ecf20Sopenharmony_ci	zd1201_disable(zd);	/* interfering with all the wifis in range */
18138c2ecf20Sopenharmony_ci	return 0;
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_cierr_start:
18168c2ecf20Sopenharmony_ci	/* Leave the device in reset state */
18178c2ecf20Sopenharmony_ci	zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
18188c2ecf20Sopenharmony_cierr_zd:
18198c2ecf20Sopenharmony_ci	usb_free_urb(zd->tx_urb);
18208c2ecf20Sopenharmony_ci	usb_free_urb(zd->rx_urb);
18218c2ecf20Sopenharmony_ci	free_netdev(dev);
18228c2ecf20Sopenharmony_ci	return err;
18238c2ecf20Sopenharmony_ci}
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_cistatic void zd1201_disconnect(struct usb_interface *interface)
18268c2ecf20Sopenharmony_ci{
18278c2ecf20Sopenharmony_ci	struct zd1201 *zd = usb_get_intfdata(interface);
18288c2ecf20Sopenharmony_ci	struct hlist_node *node2;
18298c2ecf20Sopenharmony_ci	struct zd1201_frag *frag;
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	if (!zd)
18328c2ecf20Sopenharmony_ci		return;
18338c2ecf20Sopenharmony_ci	usb_set_intfdata(interface, NULL);
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	hlist_for_each_entry_safe(frag, node2, &zd->fraglist, fnode) {
18368c2ecf20Sopenharmony_ci		hlist_del_init(&frag->fnode);
18378c2ecf20Sopenharmony_ci		kfree_skb(frag->skb);
18388c2ecf20Sopenharmony_ci		kfree(frag);
18398c2ecf20Sopenharmony_ci	}
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	if (zd->tx_urb) {
18428c2ecf20Sopenharmony_ci		usb_kill_urb(zd->tx_urb);
18438c2ecf20Sopenharmony_ci		usb_free_urb(zd->tx_urb);
18448c2ecf20Sopenharmony_ci	}
18458c2ecf20Sopenharmony_ci	if (zd->rx_urb) {
18468c2ecf20Sopenharmony_ci		usb_kill_urb(zd->rx_urb);
18478c2ecf20Sopenharmony_ci		usb_free_urb(zd->rx_urb);
18488c2ecf20Sopenharmony_ci	}
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	if (zd->dev) {
18518c2ecf20Sopenharmony_ci		unregister_netdev(zd->dev);
18528c2ecf20Sopenharmony_ci		free_netdev(zd->dev);
18538c2ecf20Sopenharmony_ci	}
18548c2ecf20Sopenharmony_ci}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_cistatic int zd1201_suspend(struct usb_interface *interface,
18598c2ecf20Sopenharmony_ci			   pm_message_t message)
18608c2ecf20Sopenharmony_ci{
18618c2ecf20Sopenharmony_ci	struct zd1201 *zd = usb_get_intfdata(interface);
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	netif_device_detach(zd->dev);
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	zd->was_enabled = zd->mac_enabled;
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	if (zd->was_enabled)
18688c2ecf20Sopenharmony_ci		return zd1201_disable(zd);
18698c2ecf20Sopenharmony_ci	else
18708c2ecf20Sopenharmony_ci		return 0;
18718c2ecf20Sopenharmony_ci}
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_cistatic int zd1201_resume(struct usb_interface *interface)
18748c2ecf20Sopenharmony_ci{
18758c2ecf20Sopenharmony_ci	struct zd1201 *zd = usb_get_intfdata(interface);
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	if (!zd || !zd->dev)
18788c2ecf20Sopenharmony_ci		return -ENODEV;
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	netif_device_attach(zd->dev);
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	if (zd->was_enabled)
18838c2ecf20Sopenharmony_ci		return zd1201_enable(zd);
18848c2ecf20Sopenharmony_ci	else
18858c2ecf20Sopenharmony_ci		return 0;
18868c2ecf20Sopenharmony_ci}
18878c2ecf20Sopenharmony_ci
18888c2ecf20Sopenharmony_ci#else
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci#define zd1201_suspend NULL
18918c2ecf20Sopenharmony_ci#define zd1201_resume  NULL
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci#endif
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_cistatic struct usb_driver zd1201_usb = {
18968c2ecf20Sopenharmony_ci	.name = "zd1201",
18978c2ecf20Sopenharmony_ci	.probe = zd1201_probe,
18988c2ecf20Sopenharmony_ci	.disconnect = zd1201_disconnect,
18998c2ecf20Sopenharmony_ci	.id_table = zd1201_table,
19008c2ecf20Sopenharmony_ci	.suspend = zd1201_suspend,
19018c2ecf20Sopenharmony_ci	.resume = zd1201_resume,
19028c2ecf20Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
19038c2ecf20Sopenharmony_ci};
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_cimodule_usb_driver(zd1201_usb);
1906