📄 zd1201.c
字号:
/* * Driver for ZyDAS zd1201 based wireless USB devices. * * Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * Parts of this driver have been derived from a wlan-ng version * modified by ZyDAS. They also made documentation available, thanks! * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. */#include <linux/module.h>#include <linux/usb.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/wireless.h>#include <net/iw_handler.h>#include <linux/string.h>#include <linux/if_arp.h>#include <linux/firmware.h>#include <net/ieee80211.h>#include "zd1201.h"static struct usb_device_id zd1201_table[] = { {USB_DEVICE(0x0586, 0x3400)}, /* Peabird Wireless USB Adapter */ {USB_DEVICE(0x0ace, 0x1201)}, /* ZyDAS ZD1201 Wireless USB Adapter */ {USB_DEVICE(0x050d, 0x6051)}, /* Belkin F5D6051 usb adapter */ {USB_DEVICE(0x0db0, 0x6823)}, /* MSI UB11B usb adapter */ {USB_DEVICE(0x1044, 0x8005)}, /* GIGABYTE GN-WLBZ201 usb adapter */ {}};static int ap = 0; /* Are we an AP or a normal station? */#define ZD1201_VERSION "0.15"MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");MODULE_DESCRIPTION("Driver for ZyDAS ZD1201 based USB Wireless adapters");MODULE_VERSION(ZD1201_VERSION);MODULE_LICENSE("GPL");module_param(ap, int, 0);MODULE_PARM_DESC(ap, "If non-zero Access Point firmware will be loaded");MODULE_DEVICE_TABLE(usb, zd1201_table);static int zd1201_fw_upload(struct usb_device *dev, int apfw){ const struct firmware *fw_entry; char* data; unsigned long len; int err; unsigned char ret; char *buf; char *fwfile; if (apfw) fwfile = "zd1201-ap.fw"; else fwfile = "zd1201.fw"; err = request_firmware(&fw_entry, fwfile, &dev->dev); if (err) { dev_err(&dev->dev, "Failed to load %s firmware file!\n", fwfile); dev_err(&dev->dev, "Make sure the hotplug firmware loader is installed.\n"); dev_err(&dev->dev, "Goto http://linux-lc100020.sourceforge.net for more info\n"); return err; } data = fw_entry->data; len = fw_entry->size; buf = kmalloc(1024, GFP_ATOMIC); if (!buf) goto exit; while (len > 0) { int translen = (len > 1024) ? 1024 : len; memcpy(buf, data, translen); err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0, USB_DIR_OUT | 0x40, 0, 0, buf, translen, ZD1201_FW_TIMEOUT); if (err < 0) goto exit; len -= translen; data += translen; } err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x2, USB_DIR_OUT | 0x40, 0, 0, NULL, 0, ZD1201_FW_TIMEOUT); if (err < 0) goto exit; err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4, USB_DIR_IN | 0x40, 0,0, &ret, sizeof(ret), ZD1201_FW_TIMEOUT); if (err < 0) goto exit; if (ret & 0x80) { err = -EIO; goto exit; } err = 0;exit: kfree(buf); release_firmware(fw_entry); return err;}static void zd1201_usbfree(struct urb *urb, struct pt_regs *regs){ struct zd1201 *zd = urb->context; switch(urb->status) { case -EILSEQ: case -ENODEV: case -ETIMEDOUT: case -ENOENT: case -EPIPE: case -EOVERFLOW: case -ESHUTDOWN: dev_warn(&zd->usb->dev, "%s: urb failed: %d\n", zd->dev->name, urb->status); } kfree(urb->transfer_buffer); usb_free_urb(urb); return;}/* cmdreq message: u32 type u16 cmd u16 parm0 u16 parm1 u16 parm2 u8 pad[4] total: 4 + 2 + 2 + 2 + 2 + 4 = 16*/static int zd1201_docmd(struct zd1201 *zd, int cmd, int parm0, int parm1, int parm2){ unsigned char *command; int ret; struct urb *urb; command = kmalloc(16, GFP_ATOMIC); if (!command) return -ENOMEM; *((__le32*)command) = cpu_to_le32(ZD1201_USB_CMDREQ); *((__le16*)&command[4]) = cpu_to_le16(cmd); *((__le16*)&command[6]) = cpu_to_le16(parm0); *((__le16*)&command[8]) = cpu_to_le16(parm1); *((__le16*)&command[10])= cpu_to_le16(parm2); urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { kfree(command); return -ENOMEM; } usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2), command, 16, zd1201_usbfree, zd); ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret) { kfree(command); usb_free_urb(urb); } return ret;}/* Callback after sending out a packet */static void zd1201_usbtx(struct urb *urb, struct pt_regs *regs){ struct zd1201 *zd = urb->context; netif_wake_queue(zd->dev); return;}/* Incoming data */static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs){ struct zd1201 *zd = urb->context; int free = 0; unsigned char *data = urb->transfer_buffer; struct sk_buff *skb; unsigned char type; if (!zd) { free = 1; goto exit; } switch(urb->status) { case -EILSEQ: case -ENODEV: case -ETIMEDOUT: case -ENOENT: case -EPIPE: case -EOVERFLOW: case -ESHUTDOWN: dev_warn(&zd->usb->dev, "%s: rx urb failed: %d\n", zd->dev->name, urb->status); free = 1; goto exit; } if (urb->status != 0 || urb->actual_length == 0) goto resubmit; type = data[0]; if (type == ZD1201_PACKET_EVENTSTAT || type == ZD1201_PACKET_RESOURCE) { memcpy(zd->rxdata, data, urb->actual_length); zd->rxlen = urb->actual_length; zd->rxdatas = 1; wake_up(&zd->rxdataq); } /* Info frame */ if (type == ZD1201_PACKET_INQUIRE) { int i = 0; unsigned short infotype, framelen, copylen; framelen = le16_to_cpu(*(__le16*)&data[4]); infotype = le16_to_cpu(*(__le16*)&data[6]); if (infotype == ZD1201_INF_LINKSTATUS) { short linkstatus; linkstatus = le16_to_cpu(*(__le16*)&data[8]); switch(linkstatus) { case 1: netif_carrier_on(zd->dev); break; case 2: netif_carrier_off(zd->dev); break; case 3: netif_carrier_off(zd->dev); break; case 4: netif_carrier_on(zd->dev); break; default: netif_carrier_off(zd->dev); } goto resubmit; } if (infotype == ZD1201_INF_ASSOCSTATUS) { short status = le16_to_cpu(*(__le16*)(data+8)); int event; union iwreq_data wrqu; switch (status) { case ZD1201_ASSOCSTATUS_STAASSOC: case ZD1201_ASSOCSTATUS_REASSOC: event = IWEVREGISTERED; break; case ZD1201_ASSOCSTATUS_DISASSOC: case ZD1201_ASSOCSTATUS_ASSOCFAIL: case ZD1201_ASSOCSTATUS_AUTHFAIL: default: event = IWEVEXPIRED; } memcpy(wrqu.addr.sa_data, data+10, ETH_ALEN); wrqu.addr.sa_family = ARPHRD_ETHER; /* Send event to user space */ wireless_send_event(zd->dev, event, &wrqu, NULL); goto resubmit; } if (infotype == ZD1201_INF_AUTHREQ) { union iwreq_data wrqu; memcpy(wrqu.addr.sa_data, data+8, ETH_ALEN); wrqu.addr.sa_family = ARPHRD_ETHER; /* There isn't a event that trully fits this request. We assume that userspace will be smart enough to see a new station being expired and sends back a authstation ioctl to authorize it. */ wireless_send_event(zd->dev, IWEVEXPIRED, &wrqu, NULL); goto resubmit; } /* Other infotypes are handled outside this handler */ zd->rxlen = 0; while (i < urb->actual_length) { copylen = le16_to_cpu(*(__le16*)&data[i+2]); /* Sanity check, sometimes we get junk */ if (copylen+zd->rxlen > sizeof(zd->rxdata)) break; memcpy(zd->rxdata+zd->rxlen, data+i+4, copylen); zd->rxlen += copylen; i += 64; } if (i >= urb->actual_length) { zd->rxdatas = 1; wake_up(&zd->rxdataq); } goto resubmit; } /* Actual data */ if (data[urb->actual_length-1] == ZD1201_PACKET_RXDATA) { int datalen = urb->actual_length-1; unsigned short len, fc, seq; struct hlist_node *node; len = ntohs(*(__be16 *)&data[datalen-2]); if (len>datalen) len=datalen; fc = le16_to_cpu(*(__le16 *)&data[datalen-16]); seq = le16_to_cpu(*(__le16 *)&data[datalen-24]); if(zd->monitor) { if (datalen < 24) goto resubmit; if (!(skb = dev_alloc_skb(datalen+24))) goto resubmit; memcpy(skb_put(skb, 2), &data[datalen-16], 2); memcpy(skb_put(skb, 2), &data[datalen-2], 2); memcpy(skb_put(skb, 6), &data[datalen-14], 6); memcpy(skb_put(skb, 6), &data[datalen-22], 6); memcpy(skb_put(skb, 6), &data[datalen-8], 6); memcpy(skb_put(skb, 2), &data[datalen-24], 2); memcpy(skb_put(skb, len), data, len); skb->dev = zd->dev; skb->dev->last_rx = jiffies; skb->protocol = eth_type_trans(skb, zd->dev); zd->stats.rx_packets++; zd->stats.rx_bytes += skb->len; netif_rx(skb); goto resubmit; } if ((seq & IEEE80211_SCTL_FRAG) || (fc & IEEE80211_FCTL_MOREFRAGS)) { struct zd1201_frag *frag = NULL; char *ptr; if (datalen<14) goto resubmit; if ((seq & IEEE80211_SCTL_FRAG) == 0) { frag = kmalloc(sizeof(*frag), GFP_ATOMIC); if (!frag) goto resubmit; skb = dev_alloc_skb(IEEE80211_DATA_LEN +14+2); if (!skb) { kfree(frag); goto resubmit; } frag->skb = skb; frag->seq = seq & IEEE80211_SCTL_SEQ; skb_reserve(skb, 2); memcpy(skb_put(skb, 12), &data[datalen-14], 12); memcpy(skb_put(skb, 2), &data[6], 2); memcpy(skb_put(skb, len), data+8, len); hlist_add_head(&frag->fnode, &zd->fraglist); goto resubmit; } hlist_for_each_entry(frag, node, &zd->fraglist, fnode) if(frag->seq == (seq&IEEE80211_SCTL_SEQ)) break; if (!frag) goto resubmit; skb = frag->skb; ptr = skb_put(skb, len); if (ptr) memcpy(ptr, data+8, len); if (fc & IEEE80211_FCTL_MOREFRAGS) goto resubmit; hlist_del_init(&frag->fnode); kfree(frag); /* Fallthrough */ } else { if (datalen<14) goto resubmit; skb = dev_alloc_skb(len + 14 + 2); if (!skb) goto resubmit; skb_reserve(skb, 2); memcpy(skb_put(skb, 12), &data[datalen-14], 12); memcpy(skb_put(skb, 2), &data[6], 2); memcpy(skb_put(skb, len), data+8, len); } skb->dev = zd->dev; skb->dev->last_rx = jiffies; skb->protocol = eth_type_trans(skb, zd->dev); zd->stats.rx_packets++; zd->stats.rx_bytes += skb->len; netif_rx(skb); }resubmit: memset(data, 0, ZD1201_RXSIZE); urb->status = 0; urb->dev = zd->usb; if(usb_submit_urb(urb, GFP_ATOMIC)) free = 1;exit: if (free) { zd->rxlen = 0; zd->rxdatas = 1; wake_up(&zd->rxdataq); kfree(urb->transfer_buffer); } return;}static int zd1201_getconfig(struct zd1201 *zd, int rid, void *riddata, unsigned int riddatalen){ int err; int i = 0; int code; int rid_fid; int length; unsigned char *pdata; zd->rxdatas = 0; err = zd1201_docmd(zd, ZD1201_CMDCODE_ACCESS, rid, 0, 0); if (err) return err; wait_event_interruptible(zd->rxdataq, zd->rxdatas); if (!zd->rxlen) return -EIO; code = le16_to_cpu(*(__le16*)(&zd->rxdata[4])); rid_fid = le16_to_cpu(*(__le16*)(&zd->rxdata[6])); length = le16_to_cpu(*(__le16*)(&zd->rxdata[8])); if (length > zd->rxlen) length = zd->rxlen-6; /* If access bit is not on, then error */ if ((code & ZD1201_ACCESSBIT) != ZD1201_ACCESSBIT || rid_fid != rid ) return -EINVAL; /* Not enough buffer for allocating data */ if (riddatalen != (length - 4)) { dev_dbg(&zd->usb->dev, "riddatalen mismatches, expected=%u, (packet=%u) length=%u, rid=0x%04X, rid_fid=0x%04X\n", riddatalen, zd->rxlen, length, rid, rid_fid); return -ENODATA; } zd->rxdatas = 0; /* Issue SetRxRid commnd */ err = zd1201_docmd(zd, ZD1201_CMDCODE_SETRXRID, rid, 0, length); if (err) return err; /* Receive RID record from resource packets */ wait_event_interruptible(zd->rxdataq, zd->rxdatas); if (!zd->rxlen) return -EIO; if (zd->rxdata[zd->rxlen - 1] != ZD1201_PACKET_RESOURCE) { dev_dbg(&zd->usb->dev, "Packet type mismatch: 0x%x not 0x3\n", zd->rxdata[zd->rxlen-1]); return -EINVAL; } /* Set the data pointer and received data length */ pdata = zd->rxdata; length = zd->rxlen; do { int actual_length; actual_length = (length > 64) ? 64 : length; if(pdata[0] != 0x3) { dev_dbg(&zd->usb->dev, "Rx Resource packet type error: %02X\n", pdata[0]); return -EINVAL; } if (actual_length != 64) { /* Trim the last packet type byte */ actual_length--; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -