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