/* -*- linux-c -*- */ /* $Id: at76c503.c,v 1.76 2006/06/21 08:50:07 maximsch2 Exp $ * * USB at76c503/at76c505 driver * * Copyright (c) 2002 - 2003 Oliver Kurth * Copyright (c) 2004 Joerg Albert * Copyright (c) 2004 Nick Jones * Copyright (c) 2004 Balint Seeber * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This file is part of the Berlios driver for WLAN USB devices based on the * Atmel AT76C503A/505/505A. See at76c503.h for details. * * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed * * History: * * 2002_12_31: * - first ping, ah-hoc mode works, fw version 0.90.2 only * * 2003_01_07 0.1: * - first release * * 2003_01_08 0.2: * - moved rx code into tasklet * - added locking in keventd handler * - support big endian in ieee802_11.h * - external firmware downloader now in this module * * 2003_01_10 0.3: * - now using 8 spaces for tab indentations * - added tx rate settings (patch from Joerg Albert (jal)) * - created functions for mib settings * * 2003_01_19 0.4: * - use usbdfu for the internal firmware * * 2003_01_27 0.5: * - implemented WEP. Thanks to jal * - added frag and rts ioctl calls (jal again) * - module parameter to give names other than eth * * 2003_01_28 0.6: * - make it compile with kernel < 2.4.20 (there is no owner field * in struct usb_driver) * - fixed a small bug for the module param eth_name * - do not use GFP_DMA, GFP_KERNEL is enough * - no down() in _tx() because that's in interrupt. Use * spin_lock_irq() instead * - should not stop net queue on urb errors * - cleanup in ioctl(): locked it altogether, this makes it easier * to maintain * - tried to implement promisc. mode: does not work with this device * - tried to implement setting mac address: does not * seem to work with this device * - now use fw version 0.90.2 #140 (prev. was #93). Does not help... * * 2003_01_30 0.7: * - now works with fw 0.100.2 (solution was: wait for completion * of commands) * - setting MAC address now works (thx to a small fix by jal) * - it turned out that promisc. mode is not possible. The firmware * does not allow it. I hope that it will be implemented in a future * version. * * 2003_02_13 0.8: * - scan mode implemented by jal * - infra structure mode by jal * - some small cleanups (removed dead code) * * history can now be found in the cvs log at http://at76c503a.berlios.de * * TODO: * - monitor mode * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if WIRELESS_EXT > 12 #include #define IW_REQUEST_INFO struct iw_request_info #else #define EIWCOMMIT EINPROGRESS #define IW_REQUEST_INFO void #endif // #if WIRELESS_EXT > 12 /* not defined in WIRELESS_EXT < 15 */ #ifndef IW_MODE_MONITOR # define IW_MODE_MONITOR 6 #endif #include /* for rtnl_lock() */ #include #ifdef CONFIG_IPAQ_HANDHELD #include #include #endif #include "at76c503.h" #include "at76_ieee802_11.h" #include "at76_usbdfu.h" /* timeout in seconds for the usb_control_msg in get_cmd_status * and set_card_command */ #ifndef USB_CTRL_GET_TIMEOUT # define USB_CTRL_GET_TIMEOUT 5 #endif /* number of endpoints of an interface */ #define NUM_EP(intf) (intf)->altsetting[0].desc.bNumEndpoints #define EP(intf,nr) (intf)->altsetting[0].endpoint[(nr)].desc #define GET_DEV(udev) usb_get_dev((udev)) #define PUT_DEV(udev) usb_put_dev((udev)) #define SET_NETDEV_OWNER(ndev,owner) /* not needed anymore ??? */ static inline int submit_urb(struct urb *urb, int mem_flags) { return usb_submit_urb(urb, mem_flags); } static inline struct urb *alloc_urb(int iso_pk, int mem_flags) { return usb_alloc_urb(iso_pk, mem_flags); } /* Backwards compatibility for usb_kill_urb() */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) # define usb_kill_urb usb_unlink_urb #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 9) # define eth_hdr(s) (s)->mac.ethernet # define set_eth_hdr(s,p) (s)->mac.ethernet=(p) #else # define set_eth_hdr(s,p) (s)->mac.raw=(unsigned char *)(p) #endif /* wireless extension level this source currently supports */ #define WIRELESS_EXT_SUPPORTED 16 #ifndef USB_ST_URB_PENDING #define USB_ST_URB_PENDING (-EINPROGRESS) #define USB_ST_STALL (-EPIPE) #endif #ifndef USB_ASYNC_UNLINK #ifdef URB_ASYNC_UNLINK #define USB_ASYNC_UNLINK URB_ASYNC_UNLINK #else #define USB_ASYNC_UNLINK 0 #endif #endif #ifndef FILL_BULK_URB #define FILL_BULK_URB(a,b,c,d,e,f,g) usb_fill_bulk_urb(a,b,c,d,e,f,g) #endif /* debug bits */ #define DBG_PROGRESS 0x00000001 /* progress of scan-join-(auth-assoc)-connected */ #define DBG_BSS_TABLE 0x00000002 /* show the bss table after scans */ #define DBG_IOCTL 0x00000004 /* ioctl calls / settings */ #define DBG_KEVENT 0x00000008 /* kevents */ #define DBG_TX_DATA 0x00000010 /* tx header */ #define DBG_TX_DATA_CONTENT 0x00000020 /* tx content */ #define DBG_TX_MGMT 0x00000040 #define DBG_RX_DATA 0x00000080 /* rx data header */ #define DBG_RX_DATA_CONTENT 0x00000100 /* rx data content */ #define DBG_RX_MGMT 0x00000200 /* rx mgmt header except beacon and probe responses */ #define DBG_RX_BEACON 0x00000400 /* rx beacon */ #define DBG_RX_CTRL 0x00000800 /* rx control */ #define DBG_RX_MGMT_CONTENT 0x00001000 /* rx mgmt content */ #define DBG_RX_FRAGS 0x00002000 /* rx data fragment handling */ #define DBG_DEVSTART 0x00004000 /* fw download, device start */ #define DBG_URB 0x00008000 /* rx urb status, ... */ #define DBG_RX_ATMEL_HDR 0x00010000 /* the atmel specific header of each rx packet */ #define DBG_PROC_ENTRY 0x00020000 /* procedure entries and exits */ #define DBG_PM 0x00040000 /* power management settings */ #define DBG_BSS_MATCH 0x00080000 /* show why a certain bss did not match */ #define DBG_PARAMS 0x00100000 /* show the configured parameters */ #define DBG_WAIT_COMPLETE 0x00200000 /* show the wait_completion progress */ #define DBG_RX_FRAGS_SKB 0x00400000 /* show skb header for incoming rx fragments */ #define DBG_BSS_TABLE_RM 0x00800000 /* inform on removal of old bss table entries */ #define DBG_MONITOR_MODE 0x01000000 /* debugs from monitor mode */ #define DBG_MIB 0x02000000 /* dump all MIBs in startup_device */ #define DBG_MGMT_TIMER 0x04000000 /* dump mgmt_timer ops */ #define DBG_WE_EVENTS 0x08000000 /* dump wireless events */ #define DBG_DEFAULTS 0 static int debug = DBG_DEFAULTS; static const u8 zeros[32]; /* Use our own dbg macro */ #undef dbg #define dbg(bits, format, arg...) \ do { \ if (debug & (bits)) \ printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg);\ } while (0) /* uncond. debug output */ #define dbg_uc(format, arg...) \ printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg) #ifndef min #define min(x,y) ((x) < (y) ? (x) : (y)) #endif #define assert(x) \ do {\ if (!(x)) \ err(__FILE__ ":%d assertion " #x " failed", __LINE__);\ } while (0) /* how often do we re-try these packets ? */ #define AUTH_RETRIES 3 #define ASSOC_RETRIES 3 #define DISASSOC_RETRIES 3 #ifdef CONFIG_IPAQ_HANDHELD #define scan_hook(x) \ do { \ if (machine_is_h5400()) { \ if (x) \ ipaq_led_blink (RED_LED, 1, 2); \ else \ ipaq_led_off (RED_LED); \ } \ } while (0) #else #define scan_hook(x) #endif #define NEW_STATE(dev,newstate) \ do {\ scan_hook(newstate == SCANNING); \ dbg(DBG_PROGRESS, "%s: state %d -> %d (" #newstate ")",\ dev->netdev->name, dev->istate, newstate);\ dev->istate = newstate;\ } while (0) /* the beacon timeout in infra mode when we are connected (in seconds) */ #define BEACON_TIMEOUT 10 /* the interval in ticks we poll if scan is completed */ #define SCAN_POLL_INTERVAL (HZ/4) /* Version Information */ #define DRIVER_DESC "Generic Atmel at76c503/at76c505 routines" #define DRIVER_AUTHOR \ "Oliver Kurth, Joerg Albert , Alex, Nick Jones, "\ "Balint Seeber " /* Module paramaters */ module_param(debug, int, 0400); MODULE_PARM_DESC(debug, "Debugging level"); static int rx_copybreak = 200; module_param(rx_copybreak, int, 0400); MODULE_PARM_DESC(rx_copybreak, "rx packet copy threshold"); static int scan_min_time = 10; module_param(scan_min_time, int, 0400); MODULE_PARM_DESC(scan_min_time, "scan min channel time (default: 10)"); static int scan_max_time = 120; module_param(scan_max_time, int, 0400); MODULE_PARM_DESC(scan_max_time, "scan max channel time (default: 120)"); static int scan_mode = SCAN_TYPE_ACTIVE; module_param(scan_mode, int, 0400); MODULE_PARM_DESC(scan_mode, "scan mode: 0 active (with ProbeReq, default), 1 passive"); static int preamble_type = PREAMBLE_TYPE_LONG; module_param(preamble_type, int, 0400); MODULE_PARM_DESC(preamble_type, "preamble type: 0 long (default), 1 short"); static int auth_mode = 0; module_param(auth_mode, int, 0400); MODULE_PARM_DESC(auth_mode, "authentication mode: 0 open system (default), " "1 shared secret"); static int pm_mode = PM_ACTIVE; module_param(pm_mode, int, 0400); MODULE_PARM_DESC(pm_mode, "power management mode: 1 active (def.), 2 powersave, 3 smart save"); static int pm_period = 0; module_param(pm_period, int, 0400); MODULE_PARM_DESC(pm_period, "period of waking up the device in usec"); static int international_roaming = IR_OFF; module_param(international_roaming, int, 0400); MODULE_PARM_DESC(international_roaming, "enable international roaming: 0 (no, default), 1 (yes)"); static int default_iw_mode = IW_MODE_INFRA; module_param(default_iw_mode, int, 0400); MODULE_PARM_DESC(default_iw_mode, "default IW mode for a new device: " "1 (ad-hoc), 2 (infrastructure, def.), 6 (monitor mode)"); static int monitor_scan_min_time = 50; module_param(monitor_scan_min_time, int, 0400); MODULE_PARM_DESC(monitor_scan_min_time, "scan min channel time in MONITOR MODE (default: 50)"); static int monitor_scan_max_time = 600; module_param(monitor_scan_max_time, int, 0400); MODULE_PARM_DESC(monitor_scan_max_time, "scan max channel time in MONITOR MODE (default: 600)"); #define DEF_RTS_THRESHOLD 1536 #define DEF_FRAG_THRESHOLD 1536 #define DEF_SHORT_RETRY_LIMIT 8 //#define DEF_LONG_RETRY_LIMIT 4 #define DEF_ESSID "" #define DEF_ESSID_LEN 0 #define DEF_CHANNEL 10 #define MAX_RTS_THRESHOLD 2347 #define MAX_FRAG_THRESHOLD 2346 #define MIN_FRAG_THRESHOLD 256 /* The frequency of each channel in MHz */ const long channel_frequency[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) ) /* the broadcast address */ const u8 bc_addr[ETH_ALEN] = {0xff,0xff,0xff,0xff,0xff,0xff}; const u8 off_addr[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /* the supported rates of this hardware, bit7 marks a mandantory rate */ const u8 hw_rates[4] = {0x82,0x84,0x0b,0x16}; /* the max padding size for tx in bytes (see calc_padding)*/ #define MAX_PADDING_SIZE 53 /* a ieee820.11 frame header without addr4 */ struct ieee802_11_mgmt { u16 frame_ctl; u16 duration_id; u8 addr1[ETH_ALEN]; /* destination addr */ u8 addr2[ETH_ALEN]; /* source addr */ u8 addr3[ETH_ALEN]; /* BSSID */ u16 seq_ctl; u8 data[1508]; u32 fcs; } __attribute__ ((packed)); /* the size of the ieee802.11 header (excl. the at76c503 tx header) */ #define IEEE802_11_MGMT_HEADER_SIZE offsetof(struct ieee802_11_mgmt, data) #define BEACON_MAX_DATA_LENGTH 1500 /* beacon in ieee802_11_mgmt.data */ struct ieee802_11_beacon_data { u8 timestamp[8]; // TSFTIMER u16 beacon_interval; // Kms between TBTTs (Target Beacon Transmission Times) u16 capability_information; u8 data[BEACON_MAX_DATA_LENGTH]; /* contains: SSID (tag,length,value), Supported Rates (tlv), channel */ } __attribute__ ((packed)); /* disassoc frame in ieee802_11_mgmt.data */ struct ieee802_11_disassoc_frame { u16 reason; } __attribute__ ((packed)); #define DISASSOC_FRAME_SIZE \ (AT76C503_TX_HDRLEN + IEEE802_11_MGMT_HEADER_SIZE +\ sizeof(struct ieee802_11_disassoc_frame)) /* assoc request in ieee802_11_mgmt.data */ struct ieee802_11_assoc_req { u16 capability; u16 listen_interval; u8 data[1]; /* variable number of bytes for SSID and supported rates (tlv coded) */ }; /* the maximum size of an AssocReq packet */ #define ASSOCREQ_MAX_SIZE \ (AT76C503_TX_HDRLEN + IEEE802_11_MGMT_HEADER_SIZE +\ offsetof(struct ieee802_11_assoc_req,data) +\ 1+1+IW_ESSID_MAX_SIZE + 1+1+4) /* reassoc request in ieee802_11_mgmt.data */ struct ieee802_11_reassoc_req { u16 capability; u16 listen_interval; u8 curr_ap[ETH_ALEN]; /* the bssid of the AP we are currently associated to */ u8 data[1]; /* variable number of bytes for SSID and supported rates (tlv coded) */ } __attribute__ ((packed)); /* the maximum size of an AssocReq packet */ #define REASSOCREQ_MAX_SIZE \ (AT76C503_TX_HDRLEN + IEEE802_11_MGMT_HEADER_SIZE +\ offsetof(struct ieee802_11_reassoc_req,data) +\ 1+1+IW_ESSID_MAX_SIZE + 1+1+4) /* assoc/reassoc response */ struct ieee802_11_assoc_resp { u16 capability; u16 status; u16 assoc_id; u8 data[1]; /* variable number of bytes for supported rates (tlv coded) */ } __attribute__ ((packed)); /* auth. request/response in ieee802_11_mgmt.data */ struct ieee802_11_auth_frame { u16 algorithm; u16 seq_nr; u16 status; u8 challenge[0]; } __attribute__ ((packed)); /* for shared secret auth, add the challenge text size */ #define AUTH_FRAME_SIZE \ (AT76C503_TX_HDRLEN + IEEE802_11_MGMT_HEADER_SIZE +\ sizeof(struct ieee802_11_auth_frame)) /* deauth frame in ieee802_11_mgmt.data */ struct ieee802_11_deauth_frame { u16 reason; } __attribute__ ((packed)); #define DEAUTH_FRAME_SIZE \ (AT76C503_TX_HDRLEN + IEEE802_11_MGMT_HEADER_SIZE +\ sizeof(struct ieee802_11_disauth_frame)) #define KEVENT_CTRL_HALT 1 #define KEVENT_NEW_BSS 2 #define KEVENT_SET_PROMISC 3 #define KEVENT_MGMT_TIMEOUT 4 #define KEVENT_SCAN 5 #define KEVENT_JOIN 6 #define KEVENT_STARTIBSS 7 #define KEVENT_SUBMIT_RX 8 #define KEVENT_RESTART 9 /* restart the device */ #define KEVENT_ASSOC_DONE 10 /* execute the power save settings: listen interval, pm mode, assoc id */ #define KEVENT_EXTERNAL_FW 11 #define KEVENT_INTERNAL_FW 12 #define KEVENT_RESET_DEVICE 13 #define KEVENT_MONITOR 14 static DECLARE_WAIT_QUEUE_HEAD(wait_queue); static u8 snapsig[] = {0xaa, 0xaa, 0x03}; //#ifdef COLLAPSE_RFC1042 /* RFC 1042 encapsulates Ethernet frames in 802.2 SNAP (0xaa, 0xaa, 0x03) with * a SNAP OID of 0 (0x00, 0x00, 0x00) */ static u8 rfc1042sig[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; //#endif /* COLLAPSE_RFC1042 */ /* local function prototypes */ static void iwspy_update(struct at76c503 *dev, struct at76c503_rx_buffer *buf); static void at76c503_read_bulk_callback (struct urb *urb); static void at76c503_write_bulk_callback(struct urb *urb); static void defer_kevent (struct at76c503 *dev, int flag); static struct bss_info *find_matching_bss(struct at76c503 *dev, struct bss_info *curr); static int auth_req(struct at76c503 *dev, struct bss_info *bss, int seq_nr, u8 *challenge); static int disassoc_req(struct at76c503 *dev, struct bss_info *bss); static int assoc_req(struct at76c503 *dev, struct bss_info *bss); static int reassoc_req(struct at76c503 *dev, struct bss_info *curr, struct bss_info *new); static void dump_bss_table(struct at76c503 *dev, int force_output); static int submit_rx_urb(struct at76c503 *dev); static int startup_device(struct at76c503 *dev); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) static int update_usb_intf_descr(struct at76c503 *dev); #endif static int set_iroaming(struct at76c503 *dev, int onoff); static void set_monitor_mode(struct at76c503 *dev, int use_prism); /* disassemble the firmware image */ static int at76c503_get_fw_info(u8 *fw_data, int fw_size, u32 *board, u32 *version, char **str, u8 **ext_fw, u32 *ext_fw_size, u8 **int_fw, u32 *int_fw_size); /* second step of initialisation (after fw download) */ static int init_new_device(struct at76c503 *dev); /* some abbrev. for wireless events */ #if WIRELESS_EXT > 13 static inline void iwevent_scan_complete(struct net_device *dev) { union iwreq_data wrqu; wrqu.data.length = 0; wrqu.data.flags = 0; wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); dbg(DBG_WE_EVENTS, "%s: SIOCGIWSCAN sent", dev->name); } static inline void iwevent_bss_connect(struct net_device *dev, u8 *bssid) { union iwreq_data wrqu; wrqu.data.length = 0; wrqu.data.flags = 0; memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); wrqu.ap_addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); dbg(DBG_WE_EVENTS, "%s: %s: SIOCGIWAP sent", dev->name, __FUNCTION__); } static inline void iwevent_bss_disconnect(struct net_device *dev) { union iwreq_data wrqu; wrqu.data.length = 0; wrqu.data.flags = 0; memset(wrqu.ap_addr.sa_data, '\0', ETH_ALEN); wrqu.ap_addr.sa_family = ARPHRD_ETHER; wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); dbg(DBG_WE_EVENTS, "%s: %s: SIOCGIWAP sent", dev->name, __FUNCTION__); } #else static inline void iwevent_scan_complete(struct net_device *dev) {} static inline void iwevent_bss_connect(struct net_device *dev, u8 *bssid) {} static inline void iwevent_bss_disconnect(struct net_device *dev) {} #endif /* #if WIRELESS_EXT > 13 */ /* hexdump len many bytes from buf into obuf, separated by delim, add a trailing \0 into obuf */ static char *hex2str(char *obuf, u8 *buf, int len, char delim) { #define BIN2HEX(x) ((x) < 10 ? '0'+(x) : (x)+'A'-10) char *ret = obuf; while (len--) { *obuf++ = BIN2HEX(*buf>>4); *obuf++ = BIN2HEX(*buf&0xf); if (delim != '\0') *obuf++ = delim; buf++; } if (delim != '\0' && obuf > ret) obuf--; // remove last inserted delimiter *obuf = '\0'; return ret; } /* == PROC is_cloaked_ssid == returns != 0, if the given SSID is a cloaked one: - length 0 - length > 0, all bytes are \0 - length == 1, SSID ' ' */ static inline int is_cloaked_ssid(u8 *ssid, int length) { return (length == 0) || (length == 1 && *ssid == ' ') || (length > 0 && !memcmp(ssid,zeros,length)); } static inline void free_bss_list(struct at76c503 *dev) { struct list_head *next, *ptr; unsigned long flags; spin_lock_irqsave(&dev->bss_list_spinlock, flags); dev->curr_bss = dev->new_bss = NULL; list_for_each_safe(ptr, next, &dev->bss_list) { list_del(ptr); kfree(list_entry(ptr, struct bss_info, list)); } spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); } static inline char *mac2str(u8 *mac) { static char str [6*3]; sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return str; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) /* == PROC analyze_usb_config == This procedure analyzes the configuration after the USB device got reset and find the start of the interface and the two endpoint descriptors. Returns < 0 if the descriptors seems to be wrong. */ static int analyze_usb_config(u8 *cfgd, int cfgd_len, int *intf_idx, int *ep0_idx, int *ep1_idx) { u8 *cfgd_start = cfgd; u8 *cfgd_end = cfgd + cfgd_len; /* first byte after config descriptor */ int nr_intf=0, nr_ep=0; /* number of interface, number of endpoint descr. found */ assert(cfgd_len >= 2); if (cfgd_len < 2) return -1; if (*(cfgd+1) != USB_DT_CONFIG) { err("not a config descriptor"); return -2; } if (*cfgd != USB_DT_CONFIG_SIZE) { err("invalid length for config descriptor: %d", *cfgd); return -3; } /* scan the config descr */ while ((cfgd+1) < cfgd_end) { switch (*(cfgd+1)) { case USB_DT_INTERFACE: nr_intf++; if (nr_intf == 1) *intf_idx = cfgd - cfgd_start; break; case USB_DT_ENDPOINT: nr_ep++; if (nr_ep == 1) *ep0_idx = cfgd - cfgd_start; else if (nr_ep == 2) *ep1_idx = cfgd - cfgd_start; break; default: ; } cfgd += *cfgd; } /* while ((cfgd+1) < cfgd_end) */ if (nr_ep != 2 || nr_intf != 1) { err("unexpected nr of intf (%d) or endpoints (%d)", nr_intf, nr_ep); return -4; } return 0; } /* end of analyze_usb_config */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) /* == PROC update_usb_intf_descr == currently (2.6.0-test2) usb_reset_device() does not recognize that the interface descr. are changed. This procedure reads the configuration and does a limited parsing of the interface and endpoint descriptors. This is IMHO needed until usb_reset_device() is changed inside the kernel's USB subsystem. Copied from usb/core/config.c:usb_get_configuration() THIS IS VERY UGLY CODE - DO NOT COPY IT ! */ #define AT76C503A_USB_CONFDESCR_LEN 0x20 /* the short configuration descriptor before reset */ //#define AT76C503A_USB_SHORT_CONFDESCR_LEN 0x19 static int update_usb_intf_descr(struct at76c503 *dev) { int intf0; /* begin of intf descriptor in configuration */ int ep0, ep1; /* begin of endpoint descriptors */ struct usb_device *udev = dev->udev; struct usb_config_descriptor *cfg_desc; int result = 0, size; u8 *buffer; struct usb_host_interface *ifp; int i; dbg(DBG_DEVSTART, "%s: ENTER", __FUNCTION__); cfg_desc = (struct usb_config_descriptor *) kmalloc(AT76C503A_USB_CONFDESCR_LEN, GFP_KERNEL); if (!cfg_desc) { err("cannot kmalloc config desc"); return -ENOMEM; } result = usb_get_descriptor(udev, USB_DT_CONFIG, 0, cfg_desc, AT76C503A_USB_CONFDESCR_LEN); if (result < AT76C503A_USB_CONFDESCR_LEN) { if (result < 0) err("unable to get descriptor"); else { err("config descriptor too short (expected >= %i, got %i)", AT76C503A_USB_CONFDESCR_LEN, result); result = -EINVAL; } goto err; } /* now check the config descriptor */ le16_to_cpus(&cfg_desc->wTotalLength); size = cfg_desc->wTotalLength; buffer = (u8 *)cfg_desc; if (cfg_desc->bNumInterfaces > 1) { err("found %d interfaces", cfg_desc->bNumInterfaces); result = - EINVAL; goto err; } if ((result=analyze_usb_config(buffer, size, &intf0, &ep0, &ep1))) { err("analyze_usb_config returned %d for config desc %s", result, hex2str(dev->obuf, (u8 *)cfg_desc, min((int)(sizeof(dev->obuf)-1)/2,size), '\0')); result=-EINVAL; goto err; } /* we got the correct config descriptor - update the interface's endpoints */ ifp = &udev->actconfig->interface[0]->altsetting[0]; if (ifp->endpoint) kfree(ifp->endpoint); memcpy(&ifp->desc, buffer+intf0, USB_DT_INTERFACE_SIZE); if (!(ifp->endpoint = kmalloc(2 * sizeof(struct usb_host_endpoint), GFP_KERNEL))) { result = -ENOMEM; goto err; } memset(ifp->endpoint, 0, 2 * sizeof(struct usb_host_endpoint)); memcpy(&ifp->endpoint[0].desc, buffer+ep0, USB_DT_ENDPOINT_SIZE); le16_to_cpus(&ifp->endpoint[0].desc.wMaxPacketSize); memcpy(&ifp->endpoint[1].desc, buffer+ep1, USB_DT_ENDPOINT_SIZE); le16_to_cpus(&ifp->endpoint[1].desc.wMaxPacketSize); /* we must set the max packet for the new ep (see usb_set_maxpacket() ) */ #define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN)) for(i=0; i < ifp->desc.bNumEndpoints; i++) { struct usb_endpoint_descriptor *d = &ifp->endpoint[i].desc; int b = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; if (usb_endpoint_out(d->bEndpointAddress)) { if (d->wMaxPacketSize > udev->epmaxpacketout[b]) udev->epmaxpacketout[b] = d->wMaxPacketSize; } else { if (d->wMaxPacketSize > udev->epmaxpacketin[b]) udev->epmaxpacketin[b] = d->wMaxPacketSize; } } dbg(DBG_DEVSTART, "%s: ifp %p num_altsetting %d " "endpoint addr x%x, x%x", __FUNCTION__, ifp, udev->actconfig->interface[0]->num_altsetting, ifp->endpoint[0].desc.bEndpointAddress, ifp->endpoint[1].desc.bEndpointAddress); result = 0; err: kfree(cfg_desc); dbg(DBG_DEVSTART, "%s: EXIT with %d", __FUNCTION__, result); return result; } /* update_usb_intf_descr */ #else /* 2.4.x version */ /* == PROC update_usb_intf_descr == currently (2.4.23) usb_reset_device() does not recognize that the interface descr. are changed. This procedure reads the configuration and does a limited parsing of the interface and endpoint descriptors. This is IMHO needed until usb_reset_device() is changed inside the kernel's USB subsystem. Copied from usb/core/config.c:usb_get_configuration() THIS IS VERY UGLY CODE - DO NOT COPY IT ! */ #define AT76C503A_USB_CONFDESCR_LEN 0x20 /* the short configuration descriptor before reset */ //#define AT76C503A_USB_SHORT_CONFDESCR_LEN 0x19 int update_usb_intf_descr(struct at76c503 *dev) { int intf0; /* begin of intf descriptor in configuration */ int ep0, ep1; /* begin of endpoint descriptors */ struct usb_device *udev = dev->udev; struct usb_config_descriptor *cfg_desc; int result = 0, size; u8 *buffer; struct usb_interface_descriptor *ifp; int i; dbg(DBG_DEVSTART, "%s: ENTER", __FUNCTION__); cfg_desc = (struct usb_config_descriptor *) kmalloc(AT76C503A_USB_CONFDESCR_LEN, GFP_KERNEL); if (!cfg_desc) { err("cannot kmalloc config desc"); return -ENOMEM; } result = usb_get_descriptor(udev, USB_DT_CONFIG, 0, cfg_desc, AT76C503A_USB_CONFDESCR_LEN); if (result < AT76C503A_USB_CONFDESCR_LEN) { if (result < 0) err("unable to get descriptor"); else { err("config descriptor too short (expected >= %i, got %i)", AT76C503A_USB_CONFDESCR_LEN, result); result = -EINVAL; } goto err; } /* now check the config descriptor */ le16_to_cpus(&cfg_desc->wTotalLength); size = cfg_desc->wTotalLength; buffer = (u8 *)cfg_desc; if (cfg_desc->bNumInterfaces > 1) { err("found %d interfaces", cfg_desc->bNumInterfaces); result = - EINVAL; goto err; } if ((result=analyze_usb_config(buffer, size, &intf0, &ep0, &ep1))) { err("analyze_usb_config returned %d for config desc %s", result, hex2str(dev->obuf, (u8 *)cfg_desc, min((int)(sizeof(dev->obuf)-1)/2, size), '\0')); result=-EINVAL; goto err; } /* we got the correct config descriptor - update the interface's endpoints */ ifp = &udev->actconfig->interface[0].altsetting[0]; if (ifp->endpoint) kfree(ifp->endpoint); //memcpy(&ifp->desc, expected_cfg_descr+intf0, USB_DT_INTERFACE_SIZE); if (!(ifp->endpoint = kmalloc(2 * sizeof(struct usb_endpoint_descriptor), GFP_KERNEL))) { result = -ENOMEM; goto err; } memset(ifp->endpoint, 0, 2 * sizeof(struct usb_endpoint_descriptor)); memcpy(&ifp->endpoint[0], buffer+ep0, USB_DT_ENDPOINT_SIZE); le16_to_cpus(&ifp->endpoint[0].wMaxPacketSize); memcpy(&ifp->endpoint[1], buffer+ep1, USB_DT_ENDPOINT_SIZE); le16_to_cpus(&ifp->endpoint[1].wMaxPacketSize); /* fill the interface data */ memcpy(ifp, buffer+intf0, USB_DT_INTERFACE_SIZE); /* we must set the max packet for the new ep (see usb_set_maxpacket() ) */ for(i=0; i < ifp->bNumEndpoints; i++) { struct usb_endpoint_descriptor *d = &ifp->endpoint[i]; int b = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; if (usb_endpoint_out(d->bEndpointAddress)) { if (d->wMaxPacketSize > udev->epmaxpacketout[b]) udev->epmaxpacketout[b] = d->wMaxPacketSize; } else { if (d->wMaxPacketSize > udev->epmaxpacketin[b]) udev->epmaxpacketin[b] = d->wMaxPacketSize; } } dbg(DBG_DEVSTART, "%s: ifp %p num_altsetting %d " "endpoint addr x%x, x%x", __FUNCTION__, ifp, udev->actconfig->interface[0].num_altsetting, ifp->endpoint[0].bEndpointAddress, ifp->endpoint[1].bEndpointAddress); result = 0; err: kfree(cfg_desc); dbg(DBG_DEVSTART, "%s: EXIT with %d", __FUNCTION__, result); return result; } /* update_usb_intf_descr */ # endif /* #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) ... #else ... */ #endif /* #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) */ int at76c503_remap(struct usb_device *udev) { int ret; ret = usb_control_msg(udev, usb_sndctrlpipe(udev,0), 0x0a, INTERFACE_VENDOR_REQUEST_OUT, 0, 0, NULL, 0, HZ * USB_CTRL_GET_TIMEOUT); if (ret < 0) return ret; return 0; } static int get_op_mode(struct usb_device *udev) { int ret; u8 op_mode; ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, INTERFACE_VENDOR_REQUEST_IN, 0x01, 0, &op_mode, 1, HZ * USB_CTRL_GET_TIMEOUT); if(ret < 0) return ret; return op_mode; } /* this loads a block of the second part of the firmware */ static inline int load_ext_fw_block(struct usb_device *udev, int i, unsigned char *buf, int bsize) { return usb_control_msg(udev, usb_sndctrlpipe(udev,0), 0x0e, DEVICE_VENDOR_REQUEST_OUT, 0x0802, i, buf, bsize, HZ * USB_CTRL_GET_TIMEOUT); } static inline int get_hw_cfg_rfmd(struct usb_device *udev, unsigned char *buf, int buf_size) { return usb_control_msg(udev, usb_rcvctrlpipe(udev,0), 0x33, INTERFACE_VENDOR_REQUEST_IN, ((0x0a << 8) | 0x02), 0, buf, buf_size, HZ * USB_CTRL_GET_TIMEOUT); } /* Intersil boards use a different "value" for GetHWConfig requests */ static inline int get_hw_cfg_intersil(struct usb_device *udev, unsigned char *buf, int buf_size) { return usb_control_msg(udev, usb_rcvctrlpipe(udev,0), 0x33, INTERFACE_VENDOR_REQUEST_IN, ((0x09 << 8) | 0x02), 0, buf, buf_size, HZ * USB_CTRL_GET_TIMEOUT); } /* Get the hardware configuration for the adapter and place the appropriate * data in the appropriate fields of 'dev' (the GetHWConfig request and * interpretation of the result depends on the type of board we're dealing * with) */ static int get_hw_config(struct at76c503 *dev) { int ret; union { struct hwcfg_intersil i; struct hwcfg_rfmd r3; struct hwcfg_r505 r5; } *hwcfg = kmalloc(sizeof(*hwcfg), GFP_KERNEL); if (!hwcfg) return -ENOMEM; switch (dev->board_type) { case BOARDTYPE_503_INTERSIL_3861: case BOARDTYPE_503_INTERSIL_3863: ret = get_hw_cfg_intersil(dev->udev, (unsigned char *)&hwcfg->i, sizeof(hwcfg->i)); if (ret < 0) break; memcpy(dev->mac_addr, hwcfg->i.mac_addr, ETH_ALEN); memcpy(dev->cr31_values, hwcfg->i.cr31_values, 14); memcpy(dev->cr58_values, hwcfg->i.cr58_values, 14); memcpy(dev->pidvid, hwcfg->i.pidvid, 4); dev->regulatory_domain = hwcfg->i.regulatory_domain; break; case BOARDTYPE_503_RFMD: case BOARDTYPE_503_RFMD_ACC: ret = get_hw_cfg_rfmd(dev->udev, (unsigned char *)&hwcfg->r3, sizeof(hwcfg->r3)); if (ret < 0) break; memcpy(dev->cr20_values, hwcfg->r3.cr20_values, 14); memcpy(dev->cr21_values, hwcfg->r3.cr21_values, 14); memcpy(dev->bb_cr, hwcfg->r3.bb_cr, 14); memcpy(dev->pidvid, hwcfg->r3.pidvid, 4); memcpy(dev->mac_addr, hwcfg->r3.mac_addr, ETH_ALEN); dev->regulatory_domain = hwcfg->r3.regulatory_domain; memcpy(dev->low_power_values, hwcfg->r3.low_power_values, 14); memcpy(dev->normal_power_values, hwcfg->r3.normal_power_values, 14); break; case BOARDTYPE_505_RFMD: case BOARDTYPE_505_RFMD_2958: case BOARDTYPE_505A_RFMD_2958: ret = get_hw_cfg_rfmd(dev->udev, (unsigned char *)&hwcfg->r5, sizeof(hwcfg->r5)); if (ret < 0) break; memcpy(dev->cr39_values, hwcfg->r5.cr39_values, 14); memcpy(dev->bb_cr, hwcfg->r5.bb_cr, 14); memcpy(dev->pidvid, hwcfg->r5.pidvid, 4); memcpy(dev->mac_addr, hwcfg->r5.mac_addr, ETH_ALEN); dev->regulatory_domain = hwcfg->r5.regulatory_domain; memcpy(dev->cr15_values, hwcfg->r5.cr15_values, 14); break; default: err("Bad board type set (%d). Unable to get hardware config.", dev->board_type); ret = -EINVAL; } kfree(hwcfg); if (ret < 0) { err("Get HW Config failed (%d)", ret); } return ret; } /* == PROC getRegDomain == */ struct reg_domain const *getRegDomain(u16 code) { static struct reg_domain const fd_tab[] = { {0x10, "FCC (U.S)", 0x7ff}, /* ch 1-11 */ {0x20, "IC (Canada)", 0x7ff}, /* ch 1-11 */ {0x30, "ETSI (Europe - (Spain+France)", 0x1fff}, /* ch 1-13 */ {0x31, "Spain", 0x600}, /* ch 10,11 */ {0x32, "France", 0x1e00}, /* ch 10-13 */ {0x40, "MKK (Japan)", 0x2000}, /* ch 14 */ {0x41, "MKK1 (Japan)", 0x3fff}, /* ch 1-14 */ {0x50, "Israel", 0x3fc}, /* ch 3-9 */ }; static int const tab_len = sizeof(fd_tab) / sizeof(struct reg_domain); /* use this if an unknown code comes in */ static struct reg_domain const unknown = {0, "", 0xffffffff}; int i; for(i=0; i < tab_len; i++) if (code == fd_tab[i].code) break; return (i >= tab_len) ? &unknown : &fd_tab[i]; } /* getFreqDomain */ static inline int get_mib(struct usb_device *udev, u16 mib, u8 *buf, int buf_size) { return usb_control_msg(udev, usb_rcvctrlpipe(udev,0), 0x33, INTERFACE_VENDOR_REQUEST_IN, mib << 8, 0, buf, buf_size, HZ * USB_CTRL_GET_TIMEOUT); } static inline int get_cmd_status(struct usb_device *udev, u8 cmd, u8 *cmd_status) { return usb_control_msg(udev, usb_rcvctrlpipe(udev,0), 0x22, INTERFACE_VENDOR_REQUEST_IN, cmd, 0, cmd_status, 40, HZ * USB_CTRL_GET_TIMEOUT); } #define EXT_FW_BLOCK_SIZE 1024 static int download_external_fw(struct usb_device *udev, u8 *buf, int size) { int i = 0, ret = 0; u8 *block; if (size < 0) return -EINVAL; if ((size > 0) && (buf == NULL)) return -EFAULT; block = kmalloc(EXT_FW_BLOCK_SIZE, GFP_KERNEL); if (block == NULL) return -ENOMEM; dbg(DBG_DEVSTART, "downloading external firmware"); while(size > 0){ int bsize = size > EXT_FW_BLOCK_SIZE ? EXT_FW_BLOCK_SIZE : size; memcpy(block, buf, bsize); dbg(DBG_DEVSTART, "ext fw, size left = %5d, bsize = %4d, i = %2d", size, bsize, i); if((ret = load_ext_fw_block(udev, i, block, bsize)) < 0){ err("load_ext_fw_block failed: %d, i = %d", ret, i); goto exit; } buf += bsize; size -= bsize; i++; } /* for fw >= 0.100, the device needs an extra empty block: */ if((ret = load_ext_fw_block(udev, i, block, 0)) < 0){ err("load_ext_fw_block failed: %d, i = %d", ret, i); goto exit; } exit: kfree(block); return ret; } static int set_card_command(struct usb_device *udev, int cmd, unsigned char *buf, int buf_size) { int ret; struct at76c503_command *cmd_buf = (struct at76c503_command *)kmalloc( sizeof(struct at76c503_command) + buf_size, GFP_KERNEL); if(cmd_buf){ cmd_buf->cmd = cmd; cmd_buf->reserved = 0; cmd_buf->size = cpu_to_le16(buf_size); if(buf_size > 0) memcpy(&(cmd_buf[1]), buf, buf_size); ret = usb_control_msg(udev, usb_sndctrlpipe(udev,0), 0x0e, DEVICE_VENDOR_REQUEST_OUT, 0, 0, cmd_buf, sizeof(struct at76c503_command) + buf_size, HZ * USB_CTRL_GET_TIMEOUT); kfree(cmd_buf); return ret; } return -ENOMEM; } #define MAKE_CMD_STATUS_CASE(c) case (c): return #c static const char* get_cmd_status_string(u8 cmd_status) { switch (cmd_status) { MAKE_CMD_STATUS_CASE(CMD_STATUS_IDLE); MAKE_CMD_STATUS_CASE(CMD_STATUS_COMPLETE); MAKE_CMD_STATUS_CASE(CMD_STATUS_UNKNOWN); MAKE_CMD_STATUS_CASE(CMD_STATUS_INVALID_PARAMETER); MAKE_CMD_STATUS_CASE(CMD_STATUS_FUNCTION_NOT_SUPPORTED); MAKE_CMD_STATUS_CASE(CMD_STATUS_TIME_OUT); MAKE_CMD_STATUS_CASE(CMD_STATUS_IN_PROGRESS); MAKE_CMD_STATUS_CASE(CMD_STATUS_HOST_FAILURE); MAKE_CMD_STATUS_CASE(CMD_STATUS_SCAN_FAILED); } return "UNKNOWN"; } /* TODO: should timeout */ int wait_completion(struct at76c503 *dev, int cmd) { u8 *cmd_status = kmalloc(40, GFP_KERNEL); struct net_device *netdev = dev->netdev; int ret = 0; do{ ret = get_cmd_status(dev->udev, cmd, cmd_status); if(ret < 0){ err("%s: get_cmd_status failed: %d", netdev->name, ret); break; } dbg(DBG_WAIT_COMPLETE, "%s: Waiting on cmd %d, cmd_status[5] = %d (%s)", dev->netdev->name, cmd, cmd_status[5], get_cmd_status_string(cmd_status[5])); if(cmd_status[5] == CMD_STATUS_IN_PROGRESS || cmd_status[5] == CMD_STATUS_IDLE){ set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ/10); // 100 ms }else break; }while(1); if (ret >= 0) /* if get_cmd_status did not fail, return the status retrieved */ ret = cmd_status[5]; kfree(cmd_status); return ret; } static int set_mib(struct at76c503 *dev, struct set_mib_buffer *buf) { struct usb_device *udev = dev->udev; int ret; struct at76c503_command *cmd_buf = (struct at76c503_command *)kmalloc( sizeof(struct at76c503_command) + buf->size + 4, GFP_KERNEL); if(cmd_buf){ cmd_buf->cmd = CMD_SET_MIB; cmd_buf->reserved = 0; cmd_buf->size = cpu_to_le16(buf->size + 4); memcpy(&(cmd_buf[1]), buf, buf->size + 4); ret = usb_control_msg(udev, usb_sndctrlpipe(udev,0), 0x0e, DEVICE_VENDOR_REQUEST_OUT, 0, 0, cmd_buf, sizeof(struct at76c503_command) + buf->size + 4, HZ * USB_CTRL_GET_TIMEOUT); if (ret >= 0) if ((ret=wait_completion(dev, CMD_SET_MIB)) != CMD_STATUS_COMPLETE) { info("%s: set_mib: wait_completion failed with %d", dev->netdev->name, ret); ret = -156; /* ??? */ } kfree(cmd_buf); return ret; } return -ENOMEM; } /* return < 0 on error, == 0 if no command sent, == 1 if cmd sent */ static int set_radio(struct at76c503 *dev, int on_off) { int ret; if(dev->radio_on != on_off){ ret = set_card_command(dev->udev, CMD_RADIO, NULL, 0); if(ret < 0){ err("%s: set_card_command(CMD_RADIO) failed: %d", dev->netdev->name, ret); } else ret = 1; dev->radio_on = on_off; } else ret = 0; return ret; } /* == PROC set_pm_mode == sets power save modi (PM_ACTIVE/PM_SAVE/PM_SMART_SAVE) */ static int set_pm_mode(struct at76c503 *dev, u8 mode) __attribute__ ((unused)); static int set_pm_mode(struct at76c503 *dev, u8 mode) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC_MGMT; dev->mib_buf.size = 1; dev->mib_buf.index = POWER_MGMT_MODE_OFFSET; dev->mib_buf.data[0] = mode; ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (pm_mode) failed: %d", dev->netdev->name, ret); } return ret; } /* == PROC set_associd == sets the assoc id for power save mode */ static int set_associd(struct at76c503 *dev, u16 id) __attribute__ ((unused)); static int set_associd(struct at76c503 *dev, u16 id) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC_MGMT; dev->mib_buf.size = 2; dev->mib_buf.index = STATION_ID_OFFSET; dev->mib_buf.data[0] = id & 0xff; dev->mib_buf.data[1] = id >> 8; ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (associd) failed: %d", dev->netdev->name, ret); } return ret; } /* == PROC set_listen_interval == sets the listen interval for power save mode. really needed, as we have a similar parameter in the assocreq ??? */ static int set_listen_interval(struct at76c503 *dev, u16 interval) __attribute__ ((unused)); static int set_listen_interval(struct at76c503 *dev, u16 interval) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC; dev->mib_buf.size = 2; dev->mib_buf.index = STATION_ID_OFFSET; dev->mib_buf.data[0] = interval & 0xff; dev->mib_buf.data[1] = interval >> 8; ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (listen_interval) failed: %d", dev->netdev->name, ret); } return ret; } static int set_preamble(struct at76c503 *dev, u8 type) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_LOCAL; dev->mib_buf.size = 1; dev->mib_buf.index = PREAMBLE_TYPE_OFFSET; dev->mib_buf.data[0] = type; ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (preamble) failed: %d", dev->netdev->name, ret); } return ret; } static int set_frag(struct at76c503 *dev, u16 size) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC; dev->mib_buf.size = 2; dev->mib_buf.index = FRAGMENTATION_OFFSET; *(u16*)dev->mib_buf.data = cpu_to_le16(size); ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (frag threshold) failed: %d", dev->netdev->name, ret); } return ret; } static int set_rts(struct at76c503 *dev, u16 size) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC; dev->mib_buf.size = 2; dev->mib_buf.index = RTS_OFFSET; *(u16*)dev->mib_buf.data = cpu_to_le16(size); ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (rts) failed: %d", dev->netdev->name, ret); } return ret; } static int set_autorate_fallback(struct at76c503 *dev, int onoff) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_LOCAL; dev->mib_buf.size = 1; dev->mib_buf.index = TX_AUTORATE_FALLBACK_OFFSET; dev->mib_buf.data[0] = onoff; ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (autorate fallback) failed: %d", dev->netdev->name, ret); } return ret; } static int set_mac_address(struct at76c503 *dev, void *addr) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC_ADD; dev->mib_buf.size = ETH_ALEN; dev->mib_buf.index = offsetof(struct mib_mac_addr, mac_addr); memcpy(dev->mib_buf.data, addr, ETH_ALEN); ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (MAC_ADDR, mac_addr) failed: %d", dev->netdev->name, ret); } return ret; } #if 0 /* implemented to get promisc. mode working, but does not help. May still be useful for multicast eventually. */ static int set_group_address(struct at76c503 *dev, u8 *addr, int n) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC_ADD; dev->mib_buf.size = ETH_ALEN; dev->mib_buf.index = offsetof(struct mib_mac_addr, group_addr) + n*ETH_ALEN; memcpy(dev->mib_buf.data, addr, ETH_ALEN); ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (MIB_MAC_ADD, group_addr) failed: %d", dev->netdev->name, ret); } #if 1 /* I do not know anything about the group_addr_status field... (oku)*/ memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC_ADD; dev->mib_buf.size = 1; dev->mib_buf.index = offsetof(struct mib_mac_addr, group_addr_status) + n; dev->mib_buf.data[0] = 1; ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (MIB_MAC_ADD, group_addr_status) failed: %d", dev->netdev->name, ret); } #endif return ret; } #endif static int set_promisc(struct at76c503 *dev, int onoff) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_LOCAL; dev->mib_buf.size = 1; dev->mib_buf.index = offsetof(struct mib_local, promiscuous_mode); dev->mib_buf.data[0] = onoff ? 1 : 0; ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (promiscous_mode) failed: %d", dev->netdev->name, ret); } return ret; } static int dump_mib_mac_addr(struct at76c503 *dev) __attribute__ ((unused)); static int dump_mib_mac_addr(struct at76c503 *dev) { int ret = 0; struct mib_mac_addr *mac_addr = kmalloc(sizeof(struct mib_mac_addr), GFP_KERNEL); if(!mac_addr){ ret = -ENOMEM; goto exit; } ret = get_mib(dev->udev, MIB_MAC_ADD, (u8*)mac_addr, sizeof(struct mib_mac_addr)); if(ret < 0){ err("%s: get_mib (MAC_ADDR) failed: %d", dev->netdev->name, ret); goto err; } dbg_uc("%s: MIB MAC_ADDR: mac_addr %s res 0x%x 0x%x group_addr %s status %d %d %d %d", dev->netdev->name, mac2str(mac_addr->mac_addr), mac_addr->res[0], mac_addr->res[1], hex2str(dev->obuf, (u8 *)mac_addr->group_addr, min((int)(sizeof(dev->obuf)-1)/2, 4*ETH_ALEN), '\0'), mac_addr->group_addr_status[0], mac_addr->group_addr_status[1], mac_addr->group_addr_status[2], mac_addr->group_addr_status[3]); err: kfree(mac_addr); exit: return ret; } static int dump_mib_mac_wep(struct at76c503 *dev) __attribute__ ((unused)); static int dump_mib_mac_wep(struct at76c503 *dev) { int ret = 0; struct mib_mac_wep *mac_wep = kmalloc(sizeof(struct mib_mac_wep), GFP_KERNEL); if(!mac_wep){ ret = -ENOMEM; goto exit; } ret = get_mib(dev->udev, MIB_MAC_WEP, (u8*)mac_wep, sizeof(struct mib_mac_wep)); if(ret < 0){ err("%s: get_mib (MAC_WEP) failed: %d", dev->netdev->name, ret); goto err; } dbg_uc("%s: MIB MAC_WEP: priv_invoked %u def_key_id %u key_len %u " "excl_unencr %u wep_icv_err %u wep_excluded %u encr_level %u key %d: %s", dev->netdev->name, mac_wep->privacy_invoked, mac_wep->wep_default_key_id, mac_wep->wep_key_mapping_len, mac_wep->exclude_unencrypted,le32_to_cpu( mac_wep->wep_icv_error_count), le32_to_cpu(mac_wep->wep_excluded_count), mac_wep->encryption_level, mac_wep->wep_default_key_id, mac_wep->wep_default_key_id < 4 ? hex2str(dev->obuf, mac_wep->wep_default_keyvalue[mac_wep->wep_default_key_id], min((int)(sizeof(dev->obuf)-1)/2, mac_wep->encryption_level == 2 ? 13 : 5), '\0') : ""); err: kfree(mac_wep); exit: return ret; } static int dump_mib_mac_mgmt(struct at76c503 *dev) __attribute__ ((unused)); static int dump_mib_mac_mgmt(struct at76c503 *dev) { int ret = 0; struct mib_mac_mgmt *mac_mgmt = kmalloc(sizeof(struct mib_mac_mgmt), GFP_KERNEL); char country_string[4]; if(!mac_mgmt){ ret = -ENOMEM; goto exit; } ret = get_mib(dev->udev, MIB_MAC_MGMT, (u8*)mac_mgmt, sizeof(struct mib_mac_mgmt)); if(ret < 0){ err("%s: get_mib failed: %d", dev->netdev->name, ret); goto err; } memcpy(&country_string, mac_mgmt->country_string, 3); country_string[3] = '\0'; dbg_uc("%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration %d " "medium_occupancy_limit %d station_id 0x%x ATIM_window %d " "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d " "current_bssid %s current_essid %s current_bss_type %d " "pm_mode %d ibss_change %d res %d " "multi_domain_capability_implemented %d " "international_roaming %d country_string %s", dev->netdev->name, le16_to_cpu(mac_mgmt->beacon_period), le16_to_cpu(mac_mgmt->CFP_max_duration), le16_to_cpu(mac_mgmt->medium_occupancy_limit), le16_to_cpu(mac_mgmt->station_id), le16_to_cpu(mac_mgmt->ATIM_window), mac_mgmt->CFP_mode, mac_mgmt->privacy_option_implemented, mac_mgmt->DTIM_period, mac_mgmt->CFP_period, mac2str(mac_mgmt->current_bssid), hex2str(dev->obuf, (u8 *)mac_mgmt->current_essid, min((int)(sizeof(dev->obuf)-1)/2, IW_ESSID_MAX_SIZE), '\0'), mac_mgmt->current_bss_type, mac_mgmt->power_mgmt_mode, mac_mgmt->ibss_change, mac_mgmt->res, mac_mgmt->multi_domain_capability_implemented, mac_mgmt->multi_domain_capability_enabled, country_string); err: kfree(mac_mgmt); exit: return ret; } static int dump_mib_mac(struct at76c503 *dev) __attribute__ ((unused)); static int dump_mib_mac(struct at76c503 *dev) { int ret = 0; struct mib_mac *mac = kmalloc(sizeof(struct mib_mac), GFP_KERNEL); if(!mac){ ret = -ENOMEM; goto exit; } ret = get_mib(dev->udev, MIB_MAC, (u8*)mac, sizeof(struct mib_mac)); if(ret < 0){ err("%s: get_mib failed: %d", dev->netdev->name, ret); goto err; } dbg_uc("%s: MIB MAC: max_tx_msdu_lifetime %d max_rx_lifetime %d " "frag_threshold %d rts_threshold %d cwmin %d cwmax %d " "short_retry_time %d long_retry_time %d scan_type %d " "scan_channel %d probe_delay %u min_channel_time %d " "max_channel_time %d listen_int %d desired_ssid %s " "desired_bssid %s desired_bsstype %d", dev->netdev->name, le32_to_cpu(mac->max_tx_msdu_lifetime), le32_to_cpu(mac->max_rx_lifetime), le16_to_cpu(mac->frag_threshold), le16_to_cpu(mac->rts_threshold), le16_to_cpu(mac->cwmin), le16_to_cpu(mac->cwmax), mac->short_retry_time, mac->long_retry_time, mac->scan_type, mac->scan_channel, le16_to_cpu(mac->probe_delay), le16_to_cpu(mac->min_channel_time), le16_to_cpu(mac->max_channel_time), le16_to_cpu(mac->listen_interval), hex2str(dev->obuf, mac->desired_ssid, min((int)(sizeof(dev->obuf)-1)/2, IW_ESSID_MAX_SIZE), '\0'), mac2str(mac->desired_bssid), mac->desired_bsstype); err: kfree(mac); exit: return ret; } static int dump_mib_phy(struct at76c503 *dev) __attribute__ ((unused)); static int dump_mib_phy(struct at76c503 *dev) { int ret = 0; struct mib_phy *phy = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); if(!phy){ ret = -ENOMEM; goto exit; } ret = get_mib(dev->udev, MIB_PHY, (u8*)phy, sizeof(struct mib_phy)); if(ret < 0){ err("%s: get_mib failed: %d", dev->netdev->name, ret); goto err; } dbg_uc("%s: MIB PHY: ed_threshold %d slot_time %d sifs_time %d " "preamble_length %d plcp_header_length %d mpdu_max_length %d " "cca_mode_supported %d operation_rate_set " "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d " "phy_type %d current_reg_domain %d", dev->netdev->name, le32_to_cpu(phy->ed_threshold), le16_to_cpu(phy->slot_time), le16_to_cpu(phy->sifs_time), le16_to_cpu(phy->preamble_length), le16_to_cpu(phy->plcp_header_length), le16_to_cpu(phy->mpdu_max_length), le16_to_cpu(phy->cca_mode_supported), phy->operation_rate_set[0], phy->operation_rate_set[1], phy->operation_rate_set[2], phy->operation_rate_set[3], phy->channel_id, phy->current_cca_mode, phy->phy_type, phy->current_reg_domain); err: kfree(phy); exit: return ret; } static int dump_mib_local(struct at76c503 *dev) __attribute__ ((unused)); static int dump_mib_local(struct at76c503 *dev) { int ret = 0; struct mib_local *local = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); if(!local){ ret = -ENOMEM; goto exit; } ret = get_mib(dev->udev, MIB_LOCAL, (u8*)local, sizeof(struct mib_local)); if(ret < 0){ err("%s: get_mib failed: %d", dev->netdev->name, ret); goto err; } dbg_uc("%s: MIB PHY: beacon_enable %d txautorate_fallback %d " "ssid_size %d promiscuous_mode %d preamble_type %d", dev->netdev->name, local->beacon_enable, local->txautorate_fallback, local->ssid_size, local->promiscuous_mode, local->preamble_type); err: kfree(local); exit: return ret; } static int get_mib_mdomain(struct at76c503 *dev, struct mib_mdomain *val) __attribute__ ((unused)); static int get_mib_mdomain(struct at76c503 *dev, struct mib_mdomain *val) { int ret = 0; struct mib_mdomain *mdomain = kmalloc(sizeof(struct mib_mdomain), GFP_KERNEL); if(!mdomain){ ret = -ENOMEM; goto exit; } ret = get_mib(dev->udev, MIB_MDOMAIN, (u8*)mdomain, sizeof(struct mib_mdomain)); if(ret < 0){ err("%s: get_mib failed: %d", dev->netdev->name, ret); goto err; } memcpy(val, mdomain, sizeof(*val)); err: kfree(mdomain); exit: return ret; } static void dump_mib_mdomain(struct at76c503 *dev) __attribute__ ((unused)); static void dump_mib_mdomain(struct at76c503 *dev) { char obuf1[2*14+1], obuf2[2*14+1]; /* to hexdump tx_powerlevel, channel_list */ int ret; struct mib_mdomain mdomain; if ((ret=get_mib_mdomain(dev, &mdomain)) < 0) { err("%s: get_mib_mdomain returned %d", __FUNCTION__, ret); return; } dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %s tx_powerlevel %s", dev->netdev->name, hex2str(obuf1, mdomain.channel_list, (sizeof(obuf1)-1)/2,'\0'), hex2str(obuf2, mdomain.tx_powerlevel, (sizeof(obuf2)-1)/2,'\0')); } static int get_current_bssid(struct at76c503 *dev) { int ret = 0; struct mib_mac_mgmt *mac_mgmt = kmalloc(sizeof(struct mib_mac_mgmt), GFP_KERNEL); if(!mac_mgmt){ ret = -ENOMEM; goto exit; } ret = get_mib(dev->udev, MIB_MAC_MGMT, (u8*)mac_mgmt, sizeof(struct mib_mac_mgmt)); if(ret < 0){ err("%s: get_mib failed: %d", dev->netdev->name, ret); goto err; } memcpy(dev->bssid, mac_mgmt->current_bssid, ETH_ALEN); info("using BSSID %s", mac2str(dev->bssid)); err: kfree(mac_mgmt); exit: return ret; } static int get_current_channel(struct at76c503 *dev) { int ret = 0; struct mib_phy *phy = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); if(!phy){ ret = -ENOMEM; goto exit; } ret = get_mib(dev->udev, MIB_PHY, (u8*)phy, sizeof(struct mib_phy)); if(ret < 0){ err("%s: get_mib(MIB_PHY) failed: %d", dev->netdev->name, ret); goto err; } dev->channel = phy->channel_id; err: kfree(phy); exit: return ret; } /* == PROC start_scan == start a scan. use_essid is != 0 if any probe_delay (if scan mode is not passive) should contain the ESSID configured. ir_step describes the international roaming step (0, 1) */ static int start_scan(struct at76c503 *dev, int use_essid, int ir_step) { struct at76c503_start_scan scan; memset(&scan, 0, sizeof(struct at76c503_start_scan)); memset(scan.bssid, 0xff, ETH_ALEN); if (use_essid) { memcpy(scan.essid, dev->essid, IW_ESSID_MAX_SIZE); scan.essid_size = dev->essid_size; } else scan.essid_size = 0; //jal: why should we start at a certain channel? we do scan the whole range //allowed by reg domain. scan.channel = dev->channel; /* atmelwlandriver differs between scan type 0 and 1 (active/passive) For ad-hoc mode, it uses type 0 only.*/ if ((dev->international_roaming == IR_ON && ir_step == 0) || dev->iw_mode == IW_MODE_MONITOR) scan.scan_type = SCAN_TYPE_PASSIVE; else scan.scan_type = dev->scan_mode; /* INFO: For probe_delay, not multiplying by 1024 as this will be slightly less than min_channel_time (per spec: probe delay < min. channel time) */ if (dev->istate == MONITORING) { scan.min_channel_time = cpu_to_le16(dev->monitor_scan_min_time); scan.max_channel_time = cpu_to_le16(dev->monitor_scan_max_time); scan.probe_delay = cpu_to_le16(dev->monitor_scan_min_time * 1000); } else { scan.min_channel_time = cpu_to_le16(dev->scan_min_time); scan.max_channel_time = cpu_to_le16(dev->scan_max_time); scan.probe_delay = cpu_to_le16(dev->scan_min_time * 1000); } if (dev->international_roaming == IR_ON && ir_step == 1) scan.international_scan = 0; else scan.international_scan = dev->international_roaming; /* other values are set to 0 for type 0 */ dbg(DBG_PROGRESS, "%s: start_scan (use_essid = %d, intl = %d, " "channel = %d, probe_delay = %d, scan_min_time = %d, " "scan_max_time = %d)", dev->netdev->name, use_essid, scan.international_scan, scan.channel, le16_to_cpu(scan.probe_delay), le16_to_cpu(scan.min_channel_time), le16_to_cpu(scan.max_channel_time)); return set_card_command(dev->udev, CMD_SCAN, (unsigned char*)&scan, sizeof(scan)); } static int start_ibss(struct at76c503 *dev) { struct at76c503_start_bss bss; memset(&bss, 0, sizeof(struct at76c503_start_bss)); memset(bss.bssid, 0xff, ETH_ALEN); memcpy(bss.essid, dev->essid, IW_ESSID_MAX_SIZE); bss.essid_size = dev->essid_size; bss.bss_type = ADHOC_MODE; bss.channel = dev->channel; return set_card_command(dev->udev, CMD_START_IBSS, (unsigned char*)&bss, sizeof(struct at76c503_start_bss)); } /* idx points into dev->bss */ static int join_bss(struct at76c503 *dev, struct bss_info *ptr) { struct at76c503_join join; assert(ptr != NULL); memset(&join, 0, sizeof(struct at76c503_join)); memcpy(join.bssid, ptr->bssid, ETH_ALEN); memcpy(join.essid, ptr->ssid, ptr->ssid_len); join.essid_size = ptr->ssid_len; join.bss_type = (dev->iw_mode == IW_MODE_ADHOC ? 1 : 2); join.channel = ptr->channel; join.timeout = cpu_to_le16(2000); dbg(DBG_PROGRESS, "%s join addr %s ssid %s type %d ch %d timeout %d", dev->netdev->name, mac2str(join.bssid), join.essid, join.bss_type, join.channel, le16_to_cpu(join.timeout)); return set_card_command(dev->udev, CMD_JOIN, (unsigned char*)&join, sizeof(struct at76c503_join)); } /* join_bss */ /* the firmware download timeout (after remap) */ void fw_dl_timeout(unsigned long par) { struct at76c503 *dev = (struct at76c503 *)par; defer_kevent(dev, KEVENT_RESET_DEVICE); } /* the restart timer timed out */ void restart_timeout(unsigned long par) { struct at76c503 *dev = (struct at76c503 *)par; defer_kevent(dev, KEVENT_RESTART); } /* we got to check the bss_list for old entries */ void bss_list_timeout(unsigned long par) { struct at76c503 *dev = (struct at76c503 *)par; unsigned long flags; struct list_head *lptr, *nptr; struct bss_info *ptr; spin_lock_irqsave(&dev->bss_list_spinlock, flags); list_for_each_safe(lptr, nptr, &dev->bss_list) { ptr = list_entry(lptr, struct bss_info, list); if (ptr != dev->curr_bss && ptr != dev->new_bss && time_after(jiffies, ptr->last_rx+BSS_LIST_TIMEOUT)) { dbg(DBG_BSS_TABLE_RM, "%s: bss_list: removing old BSS %s ch %d", dev->netdev->name, mac2str(ptr->bssid), ptr->channel); list_del(&ptr->list); kfree(ptr); } } spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); /* restart the timer */ mod_timer(&dev->bss_list_timer, jiffies+BSS_LIST_TIMEOUT); } /* we got a timeout for a infrastructure mgmt packet */ void mgmt_timeout(unsigned long par) { struct at76c503 *dev = (struct at76c503 *)par; defer_kevent(dev, KEVENT_MGMT_TIMEOUT); } /* == PROC handle_mgmt_timeout_scan == */ /* called in istate SCANNING on expiry of the mgmt_timer, when a scan was run before (dev->scan_runs > 0) */ void handle_mgmt_timeout_scan(struct at76c503 *dev) { u8 *cmd_status; int ret; struct mib_mdomain mdomain; cmd_status = kmalloc(40, GFP_KERNEL); if (cmd_status == NULL) { err("%s: %s: cmd_status kmalloc returned NULL", dev->netdev->name, __FUNCTION__); return; } if ((ret=get_cmd_status(dev->udev, CMD_SCAN, cmd_status)) < 0) { err("%s: %s: get_cmd_status failed with %d", dev->netdev->name, __FUNCTION__, ret); cmd_status[5] = CMD_STATUS_IN_PROGRESS; /* INFO: Hope it was a one off error - if not, scanning further down the line and stop this cycle */ } dbg(DBG_PROGRESS, "%s %s:%d got cmd_status %d (istate %d, " "scan_runs %d)", dev->netdev->name, __FUNCTION__, __LINE__, cmd_status[5], dev->istate, dev->scan_runs); if (cmd_status[5] == CMD_STATUS_COMPLETE) { if (dev->istate == SCANNING) { dump_bss_table(dev,0); switch (dev->scan_runs) { case 1: assert(dev->international_roaming); if ((ret=get_mib_mdomain(dev, &mdomain)) < 0) { err("get_mib_mdomain returned %d", ret); } else { char obuf1[2*14+1], obuf2[2*14+1]; dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %s " "tx_powerlevel %s", dev->netdev->name, hex2str(obuf1, mdomain.channel_list, (sizeof(obuf1)-1)/2,'\0'), hex2str(obuf2, mdomain.tx_powerlevel, (sizeof(obuf2)-1)/2,'\0')); } if ((ret = start_scan(dev, 0, 1)) < 0) { err("%s: %s: start_scan (ANY) failed with %d", dev->netdev->name, __FUNCTION__, ret); } dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer for %d ticks", __FUNCTION__, __LINE__, SCAN_POLL_INTERVAL); mod_timer(&dev->mgmt_timer, jiffies + SCAN_POLL_INTERVAL); break; case 2: if ((ret = start_scan(dev, 1, 1)) < 0) { err("%s: %s: start_scan (SSID) failed with %d", dev->netdev->name, __FUNCTION__, ret); } dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer for %d ticks", __FUNCTION__, __LINE__, SCAN_POLL_INTERVAL); mod_timer(&dev->mgmt_timer, jiffies + SCAN_POLL_INTERVAL); break; case 3: /* report the end of scan to user space */ iwevent_scan_complete(dev->netdev); NEW_STATE(dev,JOINING); assert(dev->curr_bss == NULL); /* done in free_bss_list, find_bss will start with first bss */ /* call join_bss immediately after re-run of all other threads in kevent */ defer_kevent(dev,KEVENT_JOIN); break; default: err("unexpected dev->scan_runs %d", dev->scan_runs); } /* switch (dev->scan_runs)*/ dev->scan_runs++; } else { assert(dev->istate == MONITORING); dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE: restart scan", dev->netdev->name); start_scan(dev, 0, 0); dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer for %d ticks", __FUNCTION__, __LINE__, SCAN_POLL_INTERVAL); mod_timer(&dev->mgmt_timer, jiffies + SCAN_POLL_INTERVAL); } } else { if ((cmd_status[5] != CMD_STATUS_IN_PROGRESS) && (cmd_status[5] != CMD_STATUS_IDLE)) err("%s: %s: Bad scan status: %s", dev->netdev->name, __FUNCTION__, get_cmd_status_string(cmd_status[5])); /* the first cmd status after scan start is always a IDLE -> start the timer to poll again until COMPLETED */ dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer for %d ticks", __FUNCTION__, __LINE__, SCAN_POLL_INTERVAL); mod_timer(&dev->mgmt_timer, jiffies + SCAN_POLL_INTERVAL); } kfree(cmd_status); } /* the deferred procedure called from kevent() */ void handle_mgmt_timeout(struct at76c503 *dev) { if ((dev->istate != SCANNING && dev->istate != MONITORING) || (debug & DBG_MGMT_TIMER)) /* this is normal behaviour in states MONITORING, SCANNING ... */ dbg(DBG_PROGRESS, "%s: timeout, state %d", dev->netdev->name, dev->istate); switch(dev->istate) { case MONITORING: case SCANNING: handle_mgmt_timeout_scan(dev); break; case JOINING: assert(0); break; case CONNECTED: /* we haven't received the beacon of this BSS for BEACON_TIMEOUT seconds */ info("%s: lost beacon bssid %s", dev->netdev->name, mac2str(dev->curr_bss->bssid)); /* jal: starting mgmt_timer in adhoc mode is questionable, but I'll leave it here to track down another lockup problem */ if (dev->iw_mode != IW_MODE_ADHOC) { netif_carrier_off(dev->netdev); netif_stop_queue(dev->netdev); iwevent_bss_disconnect(dev->netdev); NEW_STATE(dev,SCANNING); defer_kevent(dev,KEVENT_SCAN); } break; case AUTHENTICATING: if (dev->retries-- >= 0) { auth_req(dev, dev->curr_bss, 1, NULL); dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __FUNCTION__, __LINE__); mod_timer(&dev->mgmt_timer, jiffies+HZ); } else { /* try to get next matching BSS */ NEW_STATE(dev,JOINING); defer_kevent(dev,KEVENT_JOIN); } break; case ASSOCIATING: if (dev->retries-- >= 0) { assoc_req(dev,dev->curr_bss); dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __FUNCTION__, __LINE__); mod_timer(&dev->mgmt_timer, jiffies+HZ); } else { /* jal: TODO: we may be authenticated to several BSS and may try to associate to the next of them here in the future ... */ /* try to get next matching BSS */ NEW_STATE(dev,JOINING); defer_kevent(dev,KEVENT_JOIN); } break; case REASSOCIATING: if (dev->retries-- >= 0) reassoc_req(dev, dev->curr_bss, dev->new_bss); else { /* we disassociate from the curr_bss and scan again ... */ NEW_STATE(dev,DISASSOCIATING); dev->retries = DISASSOC_RETRIES; disassoc_req(dev, dev->curr_bss); } dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __FUNCTION__, __LINE__); mod_timer(&dev->mgmt_timer, jiffies+HZ); break; case DISASSOCIATING: if (dev->retries-- >= 0) { disassoc_req(dev, dev->curr_bss); dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __FUNCTION__, __LINE__); mod_timer(&dev->mgmt_timer,jiffies+HZ); } else { /* we scan again ... */ NEW_STATE(dev,SCANNING); defer_kevent(dev,KEVENT_SCAN); } break; case INIT: break; default: assert(0); } /* switch (dev->istate) */ }/* handle_mgmt_timeout */ /* calc. the padding from txbuf->wlength (which excludes the USB TX header) guess this is needed to compensate a flaw in the AT76C503A USB part ... */ static inline int calc_padding(int wlen) { /* add the USB TX header */ wlen += AT76C503_TX_HDRLEN; wlen = wlen % 64; if (wlen < 50) return 50 - wlen; if (wlen >=61) return 64 + 50 - wlen; return 0; } /* send a management frame on bulk-out. txbuf->wlength must be set (in LE format !) */ static int send_mgmt_bulk(struct at76c503 *dev, struct at76c503_tx_buffer *txbuf) { unsigned long flags; int ret = 0; int urb_status; void *oldbuf = NULL; netif_carrier_off(dev->netdev); /* disable running netdev watchdog */ netif_stop_queue(dev->netdev); /* stop tx data packets */ spin_lock_irqsave(&dev->mgmt_spinlock, flags); if ((urb_status=dev->write_urb->status) == USB_ST_URB_PENDING) { oldbuf=dev->next_mgmt_bulk; /* to kfree below */ dev->next_mgmt_bulk = txbuf; txbuf = NULL; } spin_unlock_irqrestore(&dev->mgmt_spinlock, flags); if (oldbuf) { /* a data/mgmt tx is already pending in the URB - if this is no error in some situations we must implement a queue or silently modify the old msg */ err("%s: %s removed pending mgmt buffer %s", dev->netdev->name, __FUNCTION__, hex2str(dev->obuf, (u8 *)dev->next_mgmt_bulk, min((int)(sizeof(dev->obuf))/3, 64),' ')); kfree(dev->next_mgmt_bulk); } if (txbuf) { txbuf->tx_rate = 0; // txbuf->padding = 0; txbuf->padding = cpu_to_le16(calc_padding(le16_to_cpu(txbuf->wlength))); if (dev->next_mgmt_bulk) { err("%s: %s URB status %d, but mgmt is pending", dev->netdev->name, __FUNCTION__, urb_status); } dbg(DBG_TX_MGMT, "%s: tx mgmt: wlen %d tx_rate %d pad %d %s", dev->netdev->name, le16_to_cpu(txbuf->wlength), txbuf->tx_rate, le16_to_cpu(txbuf->padding), hex2str(dev->obuf, txbuf->packet, min((sizeof(dev->obuf)-1)/2, (size_t)le16_to_cpu(txbuf->wlength)),'\0')); /* txbuf was not consumed above -> send mgmt msg immediately */ memcpy(dev->bulk_out_buffer, txbuf, le16_to_cpu(txbuf->wlength) + AT76C503_TX_HDRLEN); FILL_BULK_URB(dev->write_urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), dev->bulk_out_buffer, le16_to_cpu(txbuf->wlength) + le16_to_cpu(txbuf->padding) + AT76C503_TX_HDRLEN, (usb_complete_t)at76c503_write_bulk_callback, dev); ret = submit_urb(dev->write_urb, GFP_ATOMIC); if (ret) { err("%s: %s error in tx submit urb: %d", dev->netdev->name, __FUNCTION__, ret); } kfree(txbuf); } /* if (txbuf) */ return ret; } /* send_mgmt_bulk */ static int disassoc_req(struct at76c503 *dev, struct bss_info *bss) { struct at76c503_tx_buffer *tx_buffer; struct ieee802_11_mgmt *mgmt; struct ieee802_11_disassoc_frame *req; assert(bss != NULL); if (bss == NULL) return -EFAULT; tx_buffer = kmalloc(DISASSOC_FRAME_SIZE + MAX_PADDING_SIZE, GFP_ATOMIC); if (!tx_buffer) return -ENOMEM; mgmt = (struct ieee802_11_mgmt *)&(tx_buffer->packet); req = (struct ieee802_11_disassoc_frame *)&(mgmt->data); /* make wireless header */ mgmt->frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_MGMT|IEEE802_11_STYPE_AUTH); mgmt->duration_id = cpu_to_le16(0x8000); memcpy(mgmt->addr1, bss->bssid, ETH_ALEN); memcpy(mgmt->addr2, dev->netdev->dev_addr, ETH_ALEN); memcpy(mgmt->addr3, bss->bssid, ETH_ALEN); mgmt->seq_ctl = cpu_to_le16(0); req->reason = 0; /* init. at76c503 tx header */ tx_buffer->wlength = cpu_to_le16(DISASSOC_FRAME_SIZE - AT76C503_TX_HDRLEN); dbg(DBG_TX_MGMT, "%s: DisAssocReq bssid %s", dev->netdev->name, mac2str(mgmt->addr3)); /* either send immediately (if no data tx is pending or put it in pending list */ return send_mgmt_bulk(dev, tx_buffer); } /* disassoc_req */ /* challenge is the challenge string (in TLV format) we got with seq_nr 2 for shared secret authentication only and send in seq_nr 3 WEP encrypted to prove we have the correct WEP key; otherwise it is NULL */ static int auth_req(struct at76c503 *dev, struct bss_info *bss, int seq_nr, u8 *challenge) { struct at76c503_tx_buffer *tx_buffer; struct ieee802_11_mgmt *mgmt; struct ieee802_11_auth_frame *req; int buf_len = (seq_nr != 3 ? AUTH_FRAME_SIZE : AUTH_FRAME_SIZE + 1 + 1 + challenge[1]); assert(bss != NULL); assert(seq_nr != 3 || challenge != NULL); tx_buffer = kmalloc(buf_len + MAX_PADDING_SIZE, GFP_ATOMIC); if (!tx_buffer) return -ENOMEM; mgmt = (struct ieee802_11_mgmt *)&(tx_buffer->packet); req = (struct ieee802_11_auth_frame *)&(mgmt->data); /* make wireless header */ /* first auth msg is not encrypted, only the second (seq_nr == 3) */ mgmt->frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_MGMT | IEEE802_11_STYPE_AUTH | (seq_nr == 3 ? IEEE802_11_FCTL_WEP : 0)); mgmt->duration_id = cpu_to_le16(0x8000); memcpy(mgmt->addr1, bss->bssid, ETH_ALEN); memcpy(mgmt->addr2, dev->netdev->dev_addr, ETH_ALEN); memcpy(mgmt->addr3, bss->bssid, ETH_ALEN); mgmt->seq_ctl = cpu_to_le16(0); req->algorithm = cpu_to_le16(dev->auth_mode); req->seq_nr = cpu_to_le16(seq_nr); req->status = cpu_to_le16(0); if (seq_nr == 3) memcpy(req->challenge, challenge, 1+1+challenge[1]); /* init. at76c503 tx header */ tx_buffer->wlength = cpu_to_le16(buf_len - AT76C503_TX_HDRLEN); dbg(DBG_TX_MGMT, "%s: AuthReq bssid %s alg %d seq_nr %d", dev->netdev->name, mac2str(mgmt->addr3), le16_to_cpu(req->algorithm), le16_to_cpu(req->seq_nr)); if (seq_nr == 3) { dbg(DBG_TX_MGMT, "%s: AuthReq challenge: %s ...", dev->netdev->name, hex2str(dev->obuf, req->challenge, min((int)sizeof(dev->obuf)/3, 18),' ')); } /* either send immediately (if no data tx is pending or put it in pending list */ return send_mgmt_bulk(dev, tx_buffer); } /* auth_req */ static int assoc_req(struct at76c503 *dev, struct bss_info *bss) { struct at76c503_tx_buffer *tx_buffer; struct ieee802_11_mgmt *mgmt; struct ieee802_11_assoc_req *req; u8 *tlv; assert(bss != NULL); tx_buffer = kmalloc(ASSOCREQ_MAX_SIZE + MAX_PADDING_SIZE, GFP_ATOMIC); if (!tx_buffer) return -ENOMEM; mgmt = (struct ieee802_11_mgmt *)&(tx_buffer->packet); req = (struct ieee802_11_assoc_req *)&(mgmt->data); tlv = req->data; /* make wireless header */ mgmt->frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_MGMT|IEEE802_11_STYPE_ASSOC_REQ); mgmt->duration_id = cpu_to_le16(0x8000); memcpy(mgmt->addr1, bss->bssid, ETH_ALEN); memcpy(mgmt->addr2, dev->netdev->dev_addr, ETH_ALEN); memcpy(mgmt->addr3, bss->bssid, ETH_ALEN); mgmt->seq_ctl = cpu_to_le16(0); /* we must set the Privacy bit in the capabilites to assure an Agere-based AP with optional WEP transmits encrypted frames to us. AP only set the Privacy bit in their capabilities if WEP is mandatory in the BSS! */ req->capability = cpu_to_le16(bss->capa | (dev->wep_enabled ? IEEE802_11_CAPA_PRIVACY : 0) | (dev->preamble_type == PREAMBLE_TYPE_SHORT ? IEEE802_11_CAPA_SHORT_PREAMBLE : 0)); req->listen_interval = cpu_to_le16(2 * bss->beacon_interval); /* write TLV data elements */ *tlv++ = IE_ID_SSID; *tlv++ = bss->ssid_len; memcpy(tlv, bss->ssid, bss->ssid_len); tlv += bss->ssid_len; *tlv++ = IE_ID_SUPPORTED_RATES; *tlv++ = sizeof(hw_rates); memcpy(tlv, hw_rates, sizeof(hw_rates)); tlv += sizeof(hw_rates); /* tlv points behind the supp_rates field */ /* init. at76c503 tx header */ tx_buffer->wlength = cpu_to_le16(tlv-(u8 *)mgmt); { /* output buffer for ssid and rates */ char orates[4*2+1] __attribute__ ((unused)); int len; tlv = req->data; len = min(IW_ESSID_MAX_SIZE, (int)*(tlv+1)); memcpy(dev->obuf, tlv+2, len); dev->obuf[len] = '\0'; tlv += (1 + 1 + *(tlv+1)); /* points to IE of rates now */ dbg(DBG_TX_MGMT, "%s: AssocReq bssid %s capa x%04x ssid %s rates %s", dev->netdev->name, mac2str(mgmt->addr3), le16_to_cpu(req->capability), dev->obuf, hex2str(orates,tlv+2,min((sizeof(orates)-1)/2,(size_t)*(tlv+1)), '\0')); } /* either send immediately (if no data tx is pending or put it in pending list */ return send_mgmt_bulk(dev, tx_buffer); } /* assoc_req */ /* we are currently associated to curr_bss and want to reassoc to new_bss */ static int reassoc_req(struct at76c503 *dev, struct bss_info *curr_bss, struct bss_info *new_bss) { struct at76c503_tx_buffer *tx_buffer; struct ieee802_11_mgmt *mgmt; struct ieee802_11_reassoc_req *req; u8 *tlv; assert(curr_bss != NULL); assert(new_bss != NULL); if (curr_bss == NULL || new_bss == NULL) return -EFAULT; tx_buffer = kmalloc(REASSOCREQ_MAX_SIZE + MAX_PADDING_SIZE, GFP_ATOMIC); if (!tx_buffer) return -ENOMEM; mgmt = (struct ieee802_11_mgmt *)&(tx_buffer->packet); req = (struct ieee802_11_reassoc_req *)&(mgmt->data); tlv = req->data; /* make wireless header */ /* jal: encrypt this packet if wep_enabled is TRUE ??? */ mgmt->frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_MGMT|IEEE802_11_STYPE_REASSOC_REQ); mgmt->duration_id = cpu_to_le16(0x8000); memcpy(mgmt->addr1, new_bss->bssid, ETH_ALEN); memcpy(mgmt->addr2, dev->netdev->dev_addr, ETH_ALEN); memcpy(mgmt->addr3, new_bss->bssid, ETH_ALEN); mgmt->seq_ctl = cpu_to_le16(0); /* we must set the Privacy bit in the capabilites to assure an Agere-based AP with optional WEP transmits encrypted frames to us. AP only set the Privacy bit in their capabilities if WEP is mandatory in the BSS! */ req->capability = cpu_to_le16(new_bss->capa | (dev->wep_enabled ? IEEE802_11_CAPA_PRIVACY : 0) | (dev->preamble_type == PREAMBLE_TYPE_SHORT ? IEEE802_11_CAPA_SHORT_PREAMBLE : 0)); req->listen_interval = cpu_to_le16(2 * new_bss->beacon_interval); memcpy(req->curr_ap, curr_bss->bssid, ETH_ALEN); /* write TLV data elements */ *tlv++ = IE_ID_SSID; *tlv++ = new_bss->ssid_len; memcpy(tlv,new_bss->ssid, new_bss->ssid_len); tlv += new_bss->ssid_len; *tlv++ = IE_ID_SUPPORTED_RATES; *tlv++ = sizeof(hw_rates); memcpy(tlv, hw_rates, sizeof(hw_rates)); tlv += sizeof(hw_rates); /* tlv points behind the supp_rates field */ /* init. at76c503 tx header */ tx_buffer->wlength = cpu_to_le16(tlv-(u8 *)mgmt); { /* output buffer for rates and bssid */ char orates[4*2+1] __attribute__ ((unused)); char ocurr[6*3+1] __attribute__ ((unused)); tlv = req->data; memcpy(dev->obuf, tlv+2, min(sizeof(dev->obuf),(size_t)*(tlv+1))); dev->obuf[IW_ESSID_MAX_SIZE] = '\0'; tlv += (1 + 1 + *(tlv+1)); /* points to IE of rates now */ dbg(DBG_TX_MGMT, "%s: ReAssocReq curr %s new %s capa x%04x ssid %s rates %s", dev->netdev->name, hex2str(ocurr, req->curr_ap, ETH_ALEN, ':'), mac2str(mgmt->addr3), le16_to_cpu(req->capability), dev->obuf, hex2str(orates,tlv+2,min((sizeof(orates)-1)/2,(size_t)*(tlv+1)), '\0')); } /* either send immediately (if no data tx is pending or put it in pending list */ return send_mgmt_bulk(dev, tx_buffer); } /* reassoc_req */ #if 0 /* delete it !!! */ /* == PROC handle_scan == */ static int handle_scan(struct at76c503 *dev) { char obuf1[2*14+1], obuf2[2*14+1]; /* to hexdump tx_powerlevel, channel_list */ int ret; struct mib_mdomain mdomain; if (dev->istate == INIT) { ret = -EPERM; goto end_scan; } assert(dev->istate == SCANNING); /* empty the driver's bss list */ free_bss_list(dev); if (dev->international_roaming) { if ((ret = start_scan(dev, 1, 0)) < 0) { err("%s: start_scan failed with %d", dev->netdev->name, ret); goto end_scan; } if ((ret = wait_completion(dev,CMD_SCAN)) != CMD_STATUS_COMPLETE) { err("%s: 1.start_scan completed with %d", dev->netdev->name, ret); goto end_scan; } if ((ret=get_mib_mdomain(dev, &mdomain)) < 0) { err("get_mib_mdomain returned %d", ret); goto end_scan; } dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %s tx_powerlevel %s", dev->netdev->name, hex2str(obuf1, mdomain.channel_list, (sizeof(obuf1)-1)/2,'\0'), hex2str(obuf2, mdomain.tx_powerlevel, (sizeof(obuf2)-1)/2,'\0')); /* dump the results of the scan */ dump_bss_table(dev, 0); } if ((ret = start_scan(dev, 0, 1)) < 0) { err("%s: start_scan (ANY) failed with %d", dev->netdev->name, ret); goto end_scan; } if ((ret = wait_completion(dev,CMD_SCAN)) != CMD_STATUS_COMPLETE) { err("%s: start_scan (ANY) completed with %d", dev->netdev->name, ret); goto end_scan; } if ((ret = start_scan(dev, 1, 1)) < 0) { err("%s: start_scan (SSID) failed with %d", dev->netdev->name, ret); goto end_scan; } if ((ret = wait_completion(dev,CMD_SCAN)) != CMD_STATUS_COMPLETE) { err("%s: start_scan (SSID) completed with %d", dev->netdev->name, ret); goto end_scan; } /* dump the results of the scan with real ssid */ dump_bss_table(dev, 0); iwevent_scan_complete(dev->netdev); /* report the end of scan to user space */ end_scan: return (ret < 0); } #endif //#if 0 #if 0 /* == PROC re_register_intf == re-register the interfaces with the driver core. Taken from usb.c:usb_new_device() after the call to usb_get_configuration() */ int re_register_intf(struct usb_device *dev) { int i; dbg(DBG_DEVSTART, "%s: ENTER", __FUNCTION__); /* Register all of the interfaces for this device with the driver core. * Remember, interfaces get bound to drivers, not devices. */ for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { struct usb_interface *interface = &dev->actconfig->interface[i]; struct usb_interface_descriptor *desc; desc = &interface->altsetting [interface->act_altsetting].desc; interface->dev.parent = &dev->dev; interface->dev.driver = NULL; interface->dev.bus = dev->dev.bus; /* &usb_bus_type */ interface->dev.dma_mask = dev->dev.parent->dma_mask; sprintf (&interface->dev.bus_id[0], "%d-%s:%d", dev->bus->busnum, dev->devpath, desc->bInterfaceNumber); if (!desc->iInterface || usb_string (dev, desc->iInterface, interface->dev.name, sizeof interface->dev.name) <= 0) { /* typically devices won't bother with interface * descriptions; this is the normal case. an * interface's driver might describe it better. * (also: iInterface is per-altsetting ...) */ sprintf (&interface->dev.name[0], "usb-%s-%s interface %d", dev->bus->bus_name, dev->devpath, desc->bInterfaceNumber); } dev_dbg (&dev->dev, "%s - registering interface %s\n", __FUNCTION__, interface->dev.bus_id); device_add (&interface->dev); // usb_create_driverfs_intf_files (interface); } dbg(DBG_DEVSTART, "%s: EXIT", __FUNCTION__); return 0; } /* re_register_intf */ #endif /* shamelessly copied from usbnet.c (oku) */ static void defer_kevent (struct at76c503 *dev, int flag) { set_bit (flag, &dev->kevent_flags); if (!schedule_work (&dev->kevent)) dbg(DBG_KEVENT, "%s: kevent %d may have been dropped", dev->netdev->name, flag); else dbg(DBG_KEVENT, "%s: kevent %d scheduled", dev->netdev->name, flag); } static void kevent(void *data) { struct at76c503 *dev = data; int ret; unsigned long flags; /* on errors, bits aren't cleared, but no reschedule is done. So work will be done next time something else has to be done. This is ugly. TODO! (oku) */ dbg(DBG_KEVENT, "%s: kevent entry flags: 0x%lx", dev->netdev->name, dev->kevent_flags); down(&dev->sem); if(test_bit(KEVENT_CTRL_HALT, &dev->kevent_flags)){ /* this never worked... but it seems that it's rarely necessary, if at all (oku) */ ret = usb_clear_halt(dev->udev, usb_sndctrlpipe (dev->udev, 0)); if(ret < 0) err("usb_clear_halt() failed: %d", ret); else{ clear_bit(KEVENT_CTRL_HALT, &dev->kevent_flags); info("usb_clear_halt() succesful"); } } if(test_bit(KEVENT_NEW_BSS, &dev->kevent_flags)){ struct net_device *netdev = dev->netdev; struct mib_mac_mgmt *mac_mgmt = kmalloc(sizeof(struct mib_mac_mgmt), GFP_KERNEL); ret = get_mib(dev->udev, MIB_MAC_MGMT, (u8*)mac_mgmt, sizeof(struct mib_mac_mgmt)); if(ret < 0){ err("%s: get_mib failed: %d", netdev->name, ret); goto new_bss_clean; } dbg(DBG_PROGRESS, "ibss_change = 0x%2x", mac_mgmt->ibss_change); memcpy(dev->bssid, mac_mgmt->current_bssid, ETH_ALEN); dbg(DBG_PROGRESS, "using BSSID %s", mac2str(dev->bssid)); iwevent_bss_connect(dev->netdev, dev->bssid); memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC_MGMT; dev->mib_buf.size = 1; dev->mib_buf.index = IBSS_CHANGE_OK_OFFSET; ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (ibss change ok) failed: %d", netdev->name, ret); goto new_bss_clean; } clear_bit(KEVENT_NEW_BSS, &dev->kevent_flags); new_bss_clean: kfree(mac_mgmt); } if(test_bit(KEVENT_SET_PROMISC, &dev->kevent_flags)){ info("%s: KEVENT_SET_PROMISC", dev->netdev->name); set_promisc(dev, dev->promisc); clear_bit(KEVENT_SET_PROMISC, &dev->kevent_flags); } /* check this _before_ KEVENT_JOIN, 'cause _JOIN sets _STARTIBSS bit */ if (test_bit(KEVENT_STARTIBSS, &dev->kevent_flags)) { clear_bit(KEVENT_STARTIBSS, &dev->kevent_flags); assert(dev->istate == STARTIBSS); ret = start_ibss(dev); if(ret < 0){ err("%s: start_ibss failed: %d", dev->netdev->name, ret); goto end_startibss; } ret = wait_completion(dev, CMD_START_IBSS); if (ret != CMD_STATUS_COMPLETE) { err("%s start_ibss failed to complete,%d", dev->netdev->name, ret); goto end_startibss; } ret = get_current_bssid(dev); if(ret < 0) goto end_startibss; ret = get_current_channel(dev); if(ret < 0) goto end_startibss; /* not sure what this is good for ??? */ memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC_MGMT; dev->mib_buf.size = 1; dev->mib_buf.index = IBSS_CHANGE_OK_OFFSET; ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (ibss change ok) failed: %d", dev->netdev->name, ret); goto end_startibss; } netif_carrier_on(dev->netdev); netif_start_queue(dev->netdev); } end_startibss: /* check this _before_ KEVENT_SCAN, 'cause _SCAN sets _JOIN bit */ if (test_bit(KEVENT_JOIN, &dev->kevent_flags)) { clear_bit(KEVENT_JOIN, &dev->kevent_flags); if (dev->istate == INIT) goto end_join; assert(dev->istate == JOINING); /* dev->curr_bss == NULL signals a new round, starting with list_entry(dev->bss_list.next, ...) */ /* secure the access to dev->curr_bss ! */ spin_lock_irqsave(&dev->bss_list_spinlock, flags); dev->curr_bss=find_matching_bss(dev, dev->curr_bss); spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); if (dev->curr_bss != NULL) { if ((ret=join_bss(dev,dev->curr_bss)) < 0) { err("%s: join_bss failed with %d", dev->netdev->name, ret); goto end_join; } ret=wait_completion(dev,CMD_JOIN); if (ret != CMD_STATUS_COMPLETE) { if (ret != CMD_STATUS_TIME_OUT) err("%s join_bss completed with %d", dev->netdev->name, ret); else info("%s join_bss ssid %s timed out", dev->netdev->name, mac2str(dev->curr_bss->bssid)); /* retry next BSS immediately */ defer_kevent(dev,KEVENT_JOIN); goto end_join; } /* here we have joined the (I)BSS */ if (dev->iw_mode == IW_MODE_ADHOC) { struct bss_info *bptr = dev->curr_bss; NEW_STATE(dev,CONNECTED); /* get ESSID, BSSID and channel for dev->curr_bss */ dev->essid_size = bptr->ssid_len; memcpy(dev->essid, bptr->ssid, bptr->ssid_len); memcpy(dev->bssid, bptr->bssid, ETH_ALEN); dev->channel = bptr->channel; iwevent_bss_connect(dev->netdev,bptr->bssid); netif_carrier_on(dev->netdev); netif_start_queue(dev->netdev); /* just to be sure */ del_timer_sync(&dev->mgmt_timer); } else { /* send auth req */ NEW_STATE(dev,AUTHENTICATING); auth_req(dev, dev->curr_bss, 1, NULL); dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __FUNCTION__, __LINE__); mod_timer(&dev->mgmt_timer, jiffies+HZ); } goto end_join; } /* if (dev->curr_bss != NULL) */ /* here we haven't found a matching (i)bss ... */ if (dev->iw_mode == IW_MODE_ADHOC) { NEW_STATE(dev,STARTIBSS); defer_kevent(dev,KEVENT_STARTIBSS); goto end_join; } /* haven't found a matching BSS in infra mode - try again */ NEW_STATE(dev,SCANNING); defer_kevent(dev, KEVENT_SCAN); } /* if (test_bit(KEVENT_JOIN, &dev->kevent_flags)) */ end_join: if(test_bit(KEVENT_MGMT_TIMEOUT, &dev->kevent_flags)){ clear_bit(KEVENT_MGMT_TIMEOUT, &dev->kevent_flags); handle_mgmt_timeout(dev); } if (test_bit(KEVENT_SCAN, &dev->kevent_flags)) { clear_bit(KEVENT_SCAN, &dev->kevent_flags); assert(dev->istate == SCANNING); /* empty the driver's bss list */ free_bss_list(dev); /* jal: a hack: we have an additional scan run for international roaming with use_essid == 1 */ dev->scan_runs = dev->international_roaming ? 1 : 2; if ((ret=start_scan(dev, dev->international_roaming ? 1 : 0, 0)) < 0) { err("%s: %s: start_scan failed with %d", dev->netdev->name, __FUNCTION__, ret); } else { dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer for %d ticks", __FUNCTION__, __LINE__, SCAN_POLL_INTERVAL); mod_timer(&dev->mgmt_timer, jiffies + SCAN_POLL_INTERVAL); } } /* if (test_bit(KEVENT_SCAN, &dev->kevent_flags)) */ if (test_bit(KEVENT_SUBMIT_RX, &dev->kevent_flags)) { clear_bit(KEVENT_SUBMIT_RX, &dev->kevent_flags); submit_rx_urb(dev); } if (test_bit(KEVENT_RESTART, &dev->kevent_flags)) { clear_bit(KEVENT_RESTART, &dev->kevent_flags); assert(dev->istate == INIT); startup_device(dev); /* call it here for default_iw_mode == IW_MODE_MONITOR and no subsequent "iwconfig wlanX mode monitor" or "iwpriv wlanX monitor 1|2 C" to set dev->netdev->type correctly */ set_monitor_mode(dev, dev->monitor_prism_header); netif_carrier_off(dev->netdev); /* disable running netdev watchdog */ netif_stop_queue(dev->netdev); /* stop tx data packets */ if (dev->iw_mode != IW_MODE_MONITOR) { NEW_STATE(dev,SCANNING); defer_kevent(dev,KEVENT_SCAN); } else { NEW_STATE(dev,MONITORING); start_scan(dev,0,0); dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer for %d ticks", __FUNCTION__, __LINE__, SCAN_POLL_INTERVAL); mod_timer(&dev->mgmt_timer, jiffies + SCAN_POLL_INTERVAL); } } if (test_bit(KEVENT_ASSOC_DONE, &dev->kevent_flags)) { clear_bit(KEVENT_ASSOC_DONE, &dev->kevent_flags); assert(dev->istate == ASSOCIATING || dev->istate == REASSOCIATING); if (dev->iw_mode == IW_MODE_INFRA) { assert(dev->curr_bss != NULL); if (dev->curr_bss != NULL && dev->pm_mode != PM_ACTIVE) { /* calc the listen interval in units of beacon intervals of the curr_bss */ dev->pm_period_beacon = (dev->pm_period_us >> 10) / dev->curr_bss->beacon_interval; #if 0 /* only to check if we need to set the listen interval here or could do it in the (re)assoc_req parameter */ dump_mib_mac(dev); #endif if (dev->pm_period_beacon < 2) dev->pm_period_beacon = 2; else if ( dev->pm_period_beacon > 0xffff) dev->pm_period_beacon = 0xffff; dbg(DBG_PM, "%s: pm_mode %d assoc id x%x listen int %d", dev->netdev->name, dev->pm_mode, dev->curr_bss->assoc_id, dev->pm_period_beacon); set_associd(dev, dev->curr_bss->assoc_id); set_listen_interval(dev, (u16)dev->pm_period_beacon); set_pm_mode(dev, dev->pm_mode); #if 0 dump_mib_mac(dev); dump_mib_mac_mgmt(dev); #endif } } netif_carrier_on(dev->netdev); netif_wake_queue(dev->netdev); /* _start_queue ??? */ NEW_STATE(dev,CONNECTED); iwevent_bss_connect(dev->netdev,dev->curr_bss->bssid); dbg(DBG_PROGRESS, "%s: connected to BSSID %s", dev->netdev->name, mac2str(dev->curr_bss->bssid)); } if (test_bit(KEVENT_RESET_DEVICE, &dev->kevent_flags)) { clear_bit(KEVENT_RESET_DEVICE, &dev->kevent_flags); dbg(DBG_DEVSTART, "resetting the device"); usb_reset_device(dev->udev); /* with 2.6.8 the reset above will cause a disconnect as the USB subsys recognizes the change in the config descriptors. Subsequently the device will be registered again. */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) //jal: patch the state (patch by Dmitri) dev->udev->state = USB_STATE_CONFIGURED; #endif /* jal: currently (2.6.0-test2 and 2.4.23) usb_reset_device() does not recognize that the interface descr. are changed. This procedure reads the configuration and does a limited parsing of the interface and endpoint descriptors */ update_usb_intf_descr(dev); /* continue immediately with external fw download */ set_bit (KEVENT_EXTERNAL_FW, &dev->kevent_flags); #else /* kernel >= 2.6.8 */ NEW_STATE(dev, WAIT_FOR_DISCONNECT); #endif } if (test_bit(KEVENT_EXTERNAL_FW, &dev->kevent_flags)) { u8 op_mode; clear_bit(KEVENT_EXTERNAL_FW, &dev->kevent_flags); op_mode = get_op_mode(dev->udev); dbg(DBG_DEVSTART, "opmode %d", op_mode); if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { err("unexpected opmode %d", op_mode); goto end_external_fw; } if (dev->extfw && dev->extfw_size) { ret = download_external_fw(dev->udev, dev->extfw, dev->extfw_size); if (ret < 0) { err("Downloading external firmware failed: %d", ret); goto end_external_fw; } if (dev->board_type == BOARDTYPE_505A_RFMD_2958) { info("200 ms delay for board type 7"); /* jal: can I do this in kevent ??? */ set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ/5+1); } } NEW_STATE(dev,INIT); init_new_device(dev); } end_external_fw: if (test_bit(KEVENT_INTERNAL_FW, &dev->kevent_flags)) { clear_bit(KEVENT_INTERNAL_FW, &dev->kevent_flags); dbg(DBG_DEVSTART, "downloading internal firmware"); ret=usbdfu_download(dev->udev, dev->intfw, dev->intfw_size, dev->board_type == BOARDTYPE_505A_RFMD_2958 ? 2000: 0); if (ret < 0) { err("downloading internal fw failed with %d",ret); goto end_internal_fw; } dbg(DBG_DEVSTART, "sending REMAP"); /* no REMAP for 505A (see SF driver) */ if (dev->board_type != BOARDTYPE_505A_RFMD_2958) if ((ret=at76c503_remap(dev->udev)) < 0) { err("sending REMAP failed with %d",ret); goto end_internal_fw; } dbg(DBG_DEVSTART, "sleeping for 2 seconds"); NEW_STATE(dev,EXTFW_DOWNLOAD); mod_timer(&dev->fw_dl_timer, jiffies+2*HZ+1); } end_internal_fw: up(&dev->sem); dbg(DBG_KEVENT, "%s: kevent exit flags: 0x%lx", dev->netdev->name, dev->kevent_flags); return; } static int essid_matched(struct at76c503 *dev, struct bss_info *ptr) { /* common criteria for both modi */ int retval = (dev->essid_size == 0 /* ANY ssid */ || (dev->essid_size == ptr->ssid_len && !memcmp(dev->essid, ptr->ssid, ptr->ssid_len))); if (!retval) dbg(DBG_BSS_MATCH, "%s bss table entry %p: essid didn't match", dev->netdev->name, ptr); return retval; } static inline int mode_matched(struct at76c503 *dev, struct bss_info *ptr) { int retval; if (dev->iw_mode == IW_MODE_ADHOC) retval = ptr->capa & IEEE802_11_CAPA_IBSS; else retval = ptr->capa & IEEE802_11_CAPA_ESS; if (!retval) dbg(DBG_BSS_MATCH, "%s bss table entry %p: mode didn't match", dev->netdev->name, ptr); return retval; } static int rates_matched(struct at76c503 *dev, struct bss_info *ptr) { int i; u8 *rate; for(i=0,rate=ptr->rates; i < ptr->rates_len; i++,rate++) if (*rate & 0x80) { /* this is a basic rate we have to support (see IEEE802.11, ch. 7.3.2.2) */ if (*rate != (0x80|hw_rates[0]) && *rate != (0x80|hw_rates[1]) && *rate != (0x80|hw_rates[2]) && *rate != (0x80|hw_rates[3])) { dbg(DBG_BSS_MATCH, "%s: bss table entry %p: basic rate %02x not supported", dev->netdev->name, ptr, *rate); return 0; } } /* if we use short preamble, the bss must support it */ if (dev->preamble_type == PREAMBLE_TYPE_SHORT && !(ptr->capa & IEEE802_11_CAPA_SHORT_PREAMBLE)) { dbg(DBG_BSS_MATCH, "%s: %p does not support short preamble", dev->netdev->name, ptr); return 0; } else return 1; } static inline int wep_matched(struct at76c503 *dev, struct bss_info *ptr) { if (!dev->wep_enabled && ptr->capa & IEEE802_11_CAPA_PRIVACY) { /* we have disabled WEP, but the BSS signals privacy */ dbg(DBG_BSS_MATCH, "%s: bss table entry %p: requires encryption", dev->netdev->name, ptr); return 0; } /* otherwise if the BSS does not signal privacy it may well accept encrypted packets from us ... */ return 1; } static inline int bssid_matched(struct at76c503 *dev, struct bss_info *ptr) { if (!dev->wanted_bssid_valid || !memcmp(ptr->bssid, dev->wanted_bssid, ETH_ALEN)) { return 1; } else { if (debug & DBG_BSS_MATCH) { dbg_uc("%s: requested bssid - %s does not match", dev->netdev->name, mac2str(dev->wanted_bssid)); dbg_uc(" AP bssid - %s of bss table entry %p", mac2str(ptr->bssid), ptr); } return 0; } } static void dump_bss_table(struct at76c503 *dev, int force_output) { struct bss_info *ptr; /* hex dump output buffer for debug */ unsigned long flags; struct list_head *lptr; if ((debug & DBG_BSS_TABLE) || (force_output)) { spin_lock_irqsave(&dev->bss_list_spinlock, flags); dbg_uc("%s BSS table (curr=%p, new=%p):", dev->netdev->name, dev->curr_bss, dev->new_bss); list_for_each(lptr, &dev->bss_list) { ptr = list_entry(lptr, struct bss_info, list); dbg_uc("0x%p: bssid %s channel %d ssid %s (%s)" " capa x%04x rates %s rssi %d link %d noise %d", ptr, mac2str(ptr->bssid), ptr->channel, ptr->ssid, hex2str(dev->obuf, ptr->ssid, min((sizeof(dev->obuf)-1)/2, (size_t)ptr->ssid_len),'\0'), le16_to_cpu(ptr->capa), hex2str(dev->obuf_s, ptr->rates, min(sizeof(dev->obuf_s)/3, (size_t)ptr->rates_len), ' '), ptr->rssi, ptr->link_qual, ptr->noise_level); } spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); } } /* try to find a matching bss in dev->bss, starting at position start. returns the ptr to a matching bss in the list or NULL if none found */ /* last is the last bss tried, last == NULL signals a new round, starting with list_entry(dev->bss_list.next, ...) */ /* this proc must be called inside an aquired dev->bss_list_spinlock otherwise the timeout on bss may remove the newly chosen entry ! */ static struct bss_info *find_matching_bss(struct at76c503 *dev, struct bss_info *last) { struct bss_info *ptr = NULL; struct list_head *curr; curr = last != NULL ? last->list.next : dev->bss_list.next; while (curr != &dev->bss_list) { ptr = list_entry(curr, struct bss_info, list); if (essid_matched(dev,ptr) && mode_matched(dev,ptr) && wep_matched(dev,ptr) && rates_matched(dev,ptr) && bssid_matched(dev,ptr)) break; curr = curr->next; } if (curr == &dev->bss_list) ptr = NULL; /* otherwise ptr points to the struct bss_info we have chosen */ dbg(DBG_BSS_TABLE, "%s %s: returned %p", dev->netdev->name, __FUNCTION__, ptr); return ptr; } /* find_matching_bss */ /* we got an association response */ static void rx_mgmt_assoc(struct at76c503 *dev, struct at76c503_rx_buffer *buf) { struct ieee802_11_mgmt *mgmt = (struct ieee802_11_mgmt *)buf->packet; struct ieee802_11_assoc_resp *resp = (struct ieee802_11_assoc_resp *)mgmt->data; u16 assoc_id = le16_to_cpu(resp->assoc_id); u16 status = le16_to_cpu(resp->status); u16 capa = le16_to_cpu(resp->capability); dbg(DBG_RX_MGMT, "%s: rx AssocResp bssid %s capa x%04x status x%04x " "assoc_id x%04x rates %s", dev->netdev->name, mac2str(mgmt->addr3), capa, status, assoc_id, hex2str(dev->obuf, resp->data+2, min((size_t)*(resp->data+1),(sizeof(dev->obuf)-1)/2), '\0')); if (dev->istate == ASSOCIATING) { assert(dev->curr_bss != NULL); if (dev->curr_bss == NULL) return; if (status == IEEE802_11_STATUS_SUCCESS) { struct bss_info *ptr = dev->curr_bss; ptr->assoc_id = assoc_id & 0x3fff; /* update iwconfig params */ memcpy(dev->bssid, ptr->bssid, ETH_ALEN); memcpy(dev->essid, ptr->ssid, ptr->ssid_len); dev->essid_size = ptr->ssid_len; dev->channel = ptr->channel; defer_kevent(dev,KEVENT_ASSOC_DONE); } else { NEW_STATE(dev,JOINING); defer_kevent(dev,KEVENT_JOIN); } del_timer_sync(&dev->mgmt_timer); } else info("%s: AssocResp in state %d ignored", dev->netdev->name, dev->istate); } /* rx_mgmt_assoc */ static void rx_mgmt_reassoc(struct at76c503 *dev, struct at76c503_rx_buffer *buf) { struct ieee802_11_mgmt *mgmt = (struct ieee802_11_mgmt *)buf->packet; struct ieee802_11_assoc_resp *resp = (struct ieee802_11_assoc_resp *)mgmt->data; unsigned long flags; u16 capa = le16_to_cpu(resp->capability); u16 status = le16_to_cpu(resp->status); u16 assoc_id = le16_to_cpu(resp->assoc_id); dbg(DBG_RX_MGMT, "%s: rx ReAssocResp bssid %s capa x%04x status x%04x " "assoc_id x%04x rates %s", dev->netdev->name, mac2str(mgmt->addr3), capa, status, assoc_id, hex2str(dev->obuf, resp->data+2, min((size_t)*(resp->data+1),(sizeof(dev->obuf)-1)/2), '\0')); if (dev->istate == REASSOCIATING) { assert(dev->new_bss != NULL); if (dev->new_bss == NULL) return; if (status == IEEE802_11_STATUS_SUCCESS) { struct bss_info *bptr = dev->new_bss; bptr->assoc_id = assoc_id; NEW_STATE(dev,CONNECTED); iwevent_bss_connect(dev->netdev,bptr->bssid); spin_lock_irqsave(&dev->bss_list_spinlock, flags); dev->curr_bss = dev->new_bss; dev->new_bss = NULL; spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); /* get ESSID, BSSID and channel for dev->curr_bss */ dev->essid_size = bptr->ssid_len; memcpy(dev->essid, bptr->ssid, bptr->ssid_len); memcpy(dev->bssid, bptr->bssid, ETH_ALEN); dev->channel = bptr->channel; dbg(DBG_PROGRESS, "%s: reassociated to BSSID %s", dev->netdev->name, mac2str(dev->bssid)); defer_kevent(dev, KEVENT_ASSOC_DONE); } else { del_timer_sync(&dev->mgmt_timer); NEW_STATE(dev,JOINING); defer_kevent(dev,KEVENT_JOIN); } } else info("%s: ReAssocResp in state %d ignored", dev->netdev->name, dev->istate); } /* rx_mgmt_reassoc */ static void rx_mgmt_disassoc(struct at76c503 *dev, struct at76c503_rx_buffer *buf) { struct ieee802_11_mgmt *mgmt = (struct ieee802_11_mgmt *)buf->packet; struct ieee802_11_disassoc_frame *resp = (struct ieee802_11_disassoc_frame *)mgmt->data; dbg(DBG_RX_MGMT, "%s: rx DisAssoc bssid %s reason x%04x destination %s", dev->netdev->name, mac2str(mgmt->addr3), le16_to_cpu(resp->reason), hex2str(dev->obuf, mgmt->addr1, min((int)sizeof(dev->obuf)/3, ETH_ALEN), ':')); if (dev->istate == SCANNING || dev->istate == INIT) return; assert(dev->curr_bss != NULL); if (dev->curr_bss == NULL) return; if (dev->istate == REASSOCIATING) { assert(dev->new_bss != NULL); if (dev->new_bss == NULL) return; } if (!memcmp(mgmt->addr3, dev->curr_bss->bssid, ETH_ALEN) && (!memcmp(dev->netdev->dev_addr, mgmt->addr1, ETH_ALEN) || !memcmp(bc_addr, mgmt->addr1, ETH_ALEN))) { /* this is a DisAssoc from the BSS we are connected or trying to connect to, directed to us or broadcasted */ /* jal: TODO: can the Disassoc also come from the BSS we've sent a ReAssocReq to (i.e. from dev->new_bss) ? */ if (dev->istate == DISASSOCIATING || dev->istate == ASSOCIATING || dev->istate == REASSOCIATING || dev->istate == CONNECTED || dev->istate == JOINING) { if (dev->istate == CONNECTED) { netif_carrier_off(dev->netdev); netif_stop_queue(dev->netdev); iwevent_bss_disconnect(dev->netdev); } del_timer_sync(&dev->mgmt_timer); NEW_STATE(dev,JOINING); defer_kevent(dev,KEVENT_JOIN); } else /* ignore DisAssoc in states AUTH, ASSOC */ info("%s: DisAssoc in state %d ignored", dev->netdev->name, dev->istate); } /* ignore DisAssoc to other STA or from other BSSID */ } /* rx_mgmt_disassoc */ static void rx_mgmt_auth(struct at76c503 *dev, struct at76c503_rx_buffer *buf) { struct ieee802_11_mgmt *mgmt = (struct ieee802_11_mgmt *)buf->packet; struct ieee802_11_auth_frame *resp = (struct ieee802_11_auth_frame *)mgmt->data; int seq_nr = le16_to_cpu(resp->seq_nr); int alg = le16_to_cpu(resp->algorithm); int status = le16_to_cpu(resp->status); dbg(DBG_RX_MGMT, "%s: rx AuthFrame bssid %s alg %d seq_nr %d status %d " "destination %s", dev->netdev->name, mac2str(mgmt->addr3), alg, seq_nr, status, hex2str(dev->obuf, mgmt->addr1, min((int)sizeof(dev->obuf)/3, ETH_ALEN), ':')); if (alg == IEEE802_11_AUTH_ALG_SHARED_SECRET && seq_nr == 2) { dbg(DBG_RX_MGMT, "%s: AuthFrame challenge %s ...", dev->netdev->name, hex2str(dev->obuf, resp->challenge, min((int)sizeof(dev->obuf)/3,18), ' ')); } if (dev->istate != AUTHENTICATING) { info("%s: ignored AuthFrame in state %d", dev->netdev->name, dev->istate); return; } if (dev->auth_mode != alg) { info("%s: ignored AuthFrame for alg %d", dev->netdev->name, alg); return; } assert(dev->curr_bss != NULL); if (dev->curr_bss == NULL) return; if (!memcmp(mgmt->addr3, dev->curr_bss->bssid, ETH_ALEN) && !memcmp(dev->netdev->dev_addr, mgmt->addr1, ETH_ALEN)) { /* this is a AuthFrame from the BSS we are connected or trying to connect to, directed to us */ if (status != IEEE802_11_STATUS_SUCCESS) { del_timer_sync(&dev->mgmt_timer); /* try to join next bss */ NEW_STATE(dev,JOINING); defer_kevent(dev,KEVENT_JOIN); return; } if (dev->auth_mode == IEEE802_11_AUTH_ALG_OPEN_SYSTEM || seq_nr == 4) { dev->retries = ASSOC_RETRIES; NEW_STATE(dev,ASSOCIATING); assoc_req(dev, dev->curr_bss); dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __FUNCTION__, __LINE__); mod_timer(&dev->mgmt_timer,jiffies+HZ); return; } assert(seq_nr == 2); auth_req(dev, dev->curr_bss, seq_nr+1, resp->challenge); dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __FUNCTION__, __LINE__); mod_timer(&dev->mgmt_timer,jiffies+HZ); } /* else: ignore AuthFrames to other receipients */ } /* rx_mgmt_auth */ static void rx_mgmt_deauth(struct at76c503 *dev, struct at76c503_rx_buffer *buf) { struct ieee802_11_mgmt *mgmt = (struct ieee802_11_mgmt *)buf->packet; struct ieee802_11_deauth_frame *resp = (struct ieee802_11_deauth_frame *)mgmt->data; dbg(DBG_RX_MGMT|DBG_PROGRESS, "%s: rx DeAuth bssid %s reason x%04x destination %s", dev->netdev->name, mac2str(mgmt->addr3), le16_to_cpu(resp->reason), hex2str(dev->obuf, mgmt->addr1, min((int)sizeof(dev->obuf)/3,ETH_ALEN), ':')); if (dev->istate == DISASSOCIATING || dev->istate == AUTHENTICATING || dev->istate == ASSOCIATING || dev->istate == REASSOCIATING || dev->istate == CONNECTED) { assert(dev->curr_bss != NULL); if (dev->curr_bss == NULL) return; if (!memcmp(mgmt->addr3, dev->curr_bss->bssid, ETH_ALEN) && (!memcmp(dev->netdev->dev_addr, mgmt->addr1, ETH_ALEN) || !memcmp(bc_addr, mgmt->addr1, ETH_ALEN))) { /* this is a DeAuth from the BSS we are connected or trying to connect to, directed to us or broadcasted */ if (dev->istate == CONNECTED) iwevent_bss_disconnect(dev->netdev); NEW_STATE(dev,JOINING); defer_kevent(dev,KEVENT_JOIN); del_timer_sync(&dev->mgmt_timer); } /* ignore DeAuth to other STA or from other BSSID */ } else { /* ignore DeAuth in states SCANNING */ info("%s: DeAuth in state %d ignored", dev->netdev->name, dev->istate); } } /* rx_mgmt_deauth */ static void rx_mgmt_beacon(struct at76c503 *dev, struct at76c503_rx_buffer *buf) { struct ieee802_11_mgmt *mgmt = (struct ieee802_11_mgmt *)buf->packet; /* beacon content */ struct ieee802_11_beacon_data *bdata = (struct ieee802_11_beacon_data *)mgmt->data; /* length of var length beacon parameters */ int varpar_len = min((int)buf->wlength - (int)(offsetof(struct ieee802_11_mgmt, data) + offsetof(struct ieee802_11_beacon_data, data)), BEACON_MAX_DATA_LENGTH); struct list_head *lptr; struct bss_info *match; /* entry matching addr3 with its bssid */ int new_entry = 0; int len; struct data_element { u8 type; u8 length; u8 data_head; } *element; int have_ssid = 0; int have_rates = 0; int have_channel = 0; int keep_going = 1; unsigned long flags; spin_lock_irqsave(&dev->bss_list_spinlock, flags); if (dev->istate == CONNECTED) { /* in state CONNECTED we use the mgmt_timer to control the beacon of the BSS */ assert(dev->curr_bss != NULL); if (dev->curr_bss == NULL) goto rx_mgmt_beacon_end; if (!memcmp(dev->curr_bss->bssid, mgmt->addr3, ETH_ALEN)) { //dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer " // "+BEACON_TIMEOUT*HZ", __FUNCTION__, __LINE__); mod_timer(&dev->mgmt_timer, jiffies+BEACON_TIMEOUT*HZ); dev->curr_bss->rssi = buf->rssi; goto rx_mgmt_beacon_end; } } /* look if we have this BSS already in the list */ match = NULL; if (!list_empty(&dev->bss_list)) { list_for_each(lptr, &dev->bss_list) { struct bss_info *bss_ptr = list_entry(lptr, struct bss_info, list); if (!memcmp(bss_ptr->bssid, mgmt->addr3, ETH_ALEN)) { match = bss_ptr; break; } } } if (match == NULL) { /* haven't found the bss in the list */ if ((match=kmalloc(sizeof(struct bss_info), GFP_ATOMIC)) == NULL) { dbg(DBG_BSS_TABLE, "%s: cannot kmalloc new bss info (%zd byte)", dev->netdev->name, sizeof(struct bss_info)); goto rx_mgmt_beacon_end; } memset(match,0,sizeof(*match)); new_entry = 1; /* append new struct into list */ list_add_tail(&match->list, &dev->bss_list); } /* we either overwrite an existing entry or append a new one match points to the entry in both cases */ match->capa = le16_to_cpu(bdata->capability_information); /* while beacon_interval is not (!) */ match->beacon_interval = le16_to_cpu(bdata->beacon_interval); match->rssi = buf->rssi; match->link_qual = buf->link_quality; match->noise_level = buf->noise_level; memcpy(match->mac,mgmt->addr2,ETH_ALEN); //just for info memcpy(match->bssid,mgmt->addr3,ETH_ALEN); dbg(DBG_RX_BEACON, "%s: bssid %s", dev->netdev->name, mac2str(match->bssid)); element = (struct data_element*)bdata->data; #define data_end(element) (&(element->data_head) + element->length) // This routine steps through the bdata->data array to try and get // some useful information about the access point. // Currently, this implementation supports receipt of: SSID, // supported transfer rates and channel, in any order, with some // tolerance for intermittent unknown codes (although this // functionality may not be necessary as the useful information will // usually arrive in consecutively, but there have been some // reports of some of the useful information fields arriving in a // different order). // It does not support any more IE_ID types as although IE_ID_TIM may // be supported (on my AP at least). // The bdata->data array is about 1500 bytes long but only ~36 of those // bytes are useful, hence the have_ssid etc optimistaions. while (keep_going && ((int)(data_end(element) - bdata->data) <= varpar_len)) { switch (element->type) { case IE_ID_SSID: len = min(IW_ESSID_MAX_SIZE, (int)element->length); if (!have_ssid && ((new_entry) || !is_cloaked_ssid(&(element->data_head), len))) { /* we copy only if this is a new entry, or the incoming SSID is not a cloaked SSID. This will protect us from overwriting a real SSID read in a ProbeResponse with a cloaked one from a following beacon. */ match->ssid_len = len; memcpy(match->ssid, &(element->data_head), len); match->ssid[len] = '\0'; /* terminate the string for printing */ dbg(DBG_RX_BEACON, "%s: SSID - %s", dev->netdev->name, match->ssid); } have_ssid = 1; break; case IE_ID_SUPPORTED_RATES: if (!have_rates) { match->rates_len = min((int)sizeof(match->rates), (int)element->length); memcpy(match->rates, &(element->data_head), match->rates_len); have_rates = 1; dbg(DBG_RX_BEACON, "%s: SUPPORTED RATES %s", dev->netdev->name, hex2str(dev->obuf, &(element->data_head), min((sizeof(dev->obuf)-1)/2, (size_t)element->length), '\0')); } break; case IE_ID_DS_PARAM_SET: if (!have_channel) { match->channel = element->data_head; have_channel = 1; dbg(DBG_RX_BEACON, "%s: CHANNEL - %d", dev->netdev->name, match->channel); } break; case IE_ID_CF_PARAM_SET: case IE_ID_TIM: case IE_ID_IBSS_PARAM_SET: default: { dbg(DBG_RX_BEACON, "%s: beacon IE id %d len %d %s", dev->netdev->name, element->type, element->length, hex2str(dev->obuf,&(element->data_head), min((sizeof(dev->obuf)-1)/2, (size_t)element->length),'\0')); break; } } // switch (element->type) // advance to the next 'element' of data element = (struct data_element*)data_end(element); // Optimisation: after all, the bdata->data array is // varpar_len bytes long, whereas we get all of the useful // information after only ~36 bytes, this saves us a lot of // time (and trouble as the remaining portion of the array // could be full of junk) // Comment this out if you want to see what other information // comes from the AP - although little of it may be useful //if (have_ssid && have_rates && have_channel) // keep_going = 0; } dbg(DBG_RX_BEACON, "%s: Finished processing beacon data", dev->netdev->name); match->last_rx = jiffies; /* record last rx of beacon */ rx_mgmt_beacon_end: spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); } /* rx_mgmt_beacon */ static void update_wstats(struct at76c503 *dev, struct at76c503_rx_buffer *buf) { struct iw_statistics *wstats = &dev->wstats; u16 lev_dbm; if (buf->rssi > 1) { lev_dbm = (buf->rssi * 5 / 2); if (lev_dbm > 255) lev_dbm = 255; wstats->qual.qual = buf->link_quality; wstats->qual.level = lev_dbm; wstats->qual.noise = buf->noise_level; wstats->qual.updated = 7; } } static void rx_mgmt(struct at76c503 *dev, struct at76c503_rx_buffer *buf) { struct ieee802_11_mgmt *mgmt = ( struct ieee802_11_mgmt *)buf->packet; u16 subtype = le16_to_cpu(mgmt->frame_ctl) & IEEE802_11_FCTL_STYPE; /* update wstats */ if (dev->istate != INIT && dev->istate != SCANNING) { /* jal: this is a dirty hack needed by Tim in adhoc mode */ if (dev->iw_mode == IW_MODE_ADHOC || (dev->curr_bss != NULL && !memcmp(mgmt->addr3, dev->curr_bss->bssid, ETH_ALEN))) { /* Data packets always seem to have a 0 link level, so we only read link quality info from management packets. Atmel driver actually averages the present, and previous values, we just present the raw value at the moment - TJS */ update_wstats(dev, buf); } } if (debug & DBG_RX_MGMT_CONTENT) { dbg_uc("%s rx mgmt subtype x%x %s", dev->netdev->name, subtype, hex2str(dev->obuf, (u8 *)mgmt, min((sizeof(dev->obuf)-1)/2, (size_t)le16_to_cpu(buf->wlength)), '\0')); } switch (subtype) { case IEEE802_11_STYPE_BEACON: case IEEE802_11_STYPE_PROBE_RESP: rx_mgmt_beacon(dev,buf); break; case IEEE802_11_STYPE_ASSOC_RESP: rx_mgmt_assoc(dev,buf); break; case IEEE802_11_STYPE_REASSOC_RESP: rx_mgmt_reassoc(dev,buf); break; case IEEE802_11_STYPE_DISASSOC: rx_mgmt_disassoc(dev,buf); break; case IEEE802_11_STYPE_AUTH: rx_mgmt_auth(dev,buf); break; case IEEE802_11_STYPE_DEAUTH: rx_mgmt_deauth(dev,buf); break; default: info("%s: mgmt, but not beacon, subtype = %x", dev->netdev->name, subtype); } return; } static void dbg_dumpbuf(const char *tag, const u8 *buf, int size) { int i; if (!debug) return; for (i=0; i <-- 6 --> <-- 2 --> <-46-1500-> Type 0x80 0x00 = TCP/IP Type 0x06 0x00 = XNS Type 0x81 0x37 = Novell NetWare 802.3 +---------+---------+---------+---------- | Dst | Src | Length | Data... +---------+---------+---------+---------- <-- 6 --> <-- 6 --> <-- 2 --> <-46-1500-> 802.2 (802.3 with 802.2 header) +---------+---------+---------+-------+-------+-------+---------- | Dst | Src | Length | DSAP | SSAP |Control| Data... +---------+---------+---------+-------+-------+-------+---------- <- 1 -> <- 1 -> <- 1 -> <-43-1497-> SNAP (802.3 with 802.2 and SNAP headers) +---------+---------+---------+-------+-------+-------+-----------+---------+----------- | Dst | Src | Length | 0xAA | 0xAA | 0x03 | Org Code | Type | Data... +---------+---------+---------+-------+-------+-------+-----------+---------+----------- <-- 3 --> <-- 2 --> <-38-1492-> */ /* Convert the 802.11 header on a packet into an ethernet-style header * (basically, pretend we're an ethernet card receiving ethernet packets) * * This routine returns with the skbuff pointing to the actual data (just past * the end of the newly-created ethernet header). */ static void ieee80211_to_eth(struct sk_buff *skb, int iw_mode) { struct ieee802_11_hdr *i802_11_hdr; struct ethhdr *eth_hdr_p; u8 *src_addr; u8 *dest_addr; unsigned short proto = 0; int build_ethhdr = 1; i802_11_hdr = (struct ieee802_11_hdr *)skb->data; #if 0 { dbg_uc("%s: ENTRY skb len %d data %s", __FUNCTION__, skb->len, hex2str(dev->obuf, skb->data, min((int)sizeof(dev->obuf)/3,64), ' ')); } #endif skb_pull(skb, sizeof(struct ieee802_11_hdr)); // skb_trim(skb, skb->len - 4); /* Trim CRC */ src_addr = iw_mode == IW_MODE_ADHOC ? i802_11_hdr->addr2 : i802_11_hdr->addr3; dest_addr = i802_11_hdr->addr1; eth_hdr_p = (struct ethhdr *)skb->data; if (!memcmp(eth_hdr_p->h_source, src_addr, ETH_ALEN) && !memcmp(eth_hdr_p->h_dest, dest_addr, ETH_ALEN)) { /* An ethernet frame is encapsulated within the data portion. * Just use its header instead. */ skb_pull(skb, sizeof(struct ethhdr)); build_ethhdr = 0; } else if (!memcmp(skb->data, snapsig, sizeof(snapsig))) { /* SNAP frame - collapse it */ skb_pull(skb, sizeof(rfc1042sig)+2); proto = *(unsigned short *)(skb->data - 2); } else { #ifdef IEEE_STANDARD /* According to all standards, we should assume the data * portion contains 802.2 LLC information, so we should give it * an 802.3 header (which has the same implications) */ proto = htons(skb->len); #else /* IEEE_STANDARD */ /* Unfortunately, it appears no actual 802.11 implementations * follow any standards specs. They all appear to put a * 16-bit ethertype after the 802.11 header instead, so we take * that value and make it into an Ethernet-II packet. */ /* Note that this means we can never support non-SNAP 802.2 * frames (because we can't tell when we get one) */ /* jal: This isn't true. My WRT54G happily sends SNAP. Difficult to speak for all APs, so I don't dare to define IEEE_STANDARD ... */ proto = *(unsigned short *)(skb->data); skb_pull(skb, 2); #endif /* IEEE_STANDARD */ } eth_hdr_p = (struct ethhdr *)(skb->data-sizeof(struct ethhdr)); set_eth_hdr(skb, eth_hdr_p); if (build_ethhdr) { /* This needs to be done in this order (eth_hdr_p->h_dest may * overlap src_addr) */ memcpy(eth_hdr_p->h_source, src_addr, ETH_ALEN); memcpy(eth_hdr_p->h_dest, dest_addr, ETH_ALEN); /* make an 802.3 header (proto = length) */ eth_hdr_p->h_proto = proto; } if (ntohs(eth_hdr_p->h_proto) > 1518) { skb->protocol = eth_hdr_p->h_proto; } else if (*(unsigned short *)skb->data == 0xFFFF) { /* Magic hack for Novell IPX-in-802.3 packets */ skb->protocol = htons(ETH_P_802_3); } else { /* Assume it's an 802.2 packet (it should be, and we have no * good way to tell if it isn't) */ skb->protocol = htons(ETH_P_802_2); } #if 0 { char da[3*ETH_ALEN], sa[3*ETH_ALEN]; dbg_uc("%s: EXIT skb da %s sa %s proto x%04x len %d data %s", __FUNCTION__, hex2str(da, eth_hdr(skb)->h_dest, ETH_ALEN, ':'), hex2str(sa, eth_hdr(skb)->h_source, ETH_ALEN, ':'), ntohs(skb->protocol), skb->len, hex2str(dev->obuf, skb->data, min((int)sizeof(dev->obuf)/3,64), ' ')); } #endif } /* Adjust the skb to trim the hardware header and CRC, and set up skb->mac, * skb->protocol, etc. */ static void ieee80211_fixup(struct sk_buff *skb, int iw_mode) { struct ieee802_11_hdr *i802_11_hdr; struct ethhdr *eth_hdr_p; u8 *src_addr; u8 *dest_addr; unsigned short proto = 0; i802_11_hdr = (struct ieee802_11_hdr *)skb->data; skb_pull(skb, sizeof(struct ieee802_11_hdr)); // skb_trim(skb, skb->len - 4); /* Trim CRC */ src_addr = iw_mode == IW_MODE_ADHOC ? i802_11_hdr->addr2 : i802_11_hdr->addr3; dest_addr = i802_11_hdr->addr1; skb->mac.raw = (unsigned char *)i802_11_hdr; eth_hdr_p = (struct ethhdr *)skb->data; if (!memcmp(eth_hdr_p->h_source, src_addr, ETH_ALEN) && !memcmp(eth_hdr_p->h_dest, dest_addr, ETH_ALEN)) { /* There's an ethernet header encapsulated within the data * portion, count it as part of the hardware header */ skb_pull(skb, sizeof(struct ethhdr)); proto = eth_hdr_p->h_proto; } else if (!memcmp(skb->data, snapsig, sizeof(snapsig))) { /* SNAP frame - collapse it */ /* RFC1042/802.1h encapsulated packet. Treat the SNAP header * as part of the HW header and note the protocol. */ /* NOTE: prism2 doesn't collapse Appletalk frames (why?). */ skb_pull(skb, sizeof(rfc1042sig) + 2); proto = *(unsigned short *)(skb->data - 2); } if (ntohs(proto) > 1518) { skb->protocol = proto; } else { #ifdef IEEE_STANDARD /* According to all standards, we should assume the data * portion contains 802.2 LLC information */ skb->protocol = htons(ETH_P_802_2); #else /* IEEE_STANDARD */ /* Unfortunately, it appears no actual 802.11 implementations * follow any standards specs. They all appear to put a * 16-bit ethertype after the 802.11 header instead, so we'll * use that (and consider it part of the hardware header). */ /* Note that this means we can never support non-SNAP 802.2 * frames (because we can't tell when we get one) */ skb->protocol = *(unsigned short *)skb->data; skb_pull(skb, 2); #endif /* IEEE_STANDARD */ } } /* check for fragmented data in dev->rx_skb. If the packet was no fragment or it was the last of a fragment set a skb containing the whole packet is returned for further processing. Otherwise we get NULL and are done and the packet is either stored inside the fragment buffer or thrown away. The check for rx_copybreak is moved here. Every returned skb starts with the ieee802_11 header and contains _no_ FCS at the end */ static struct sk_buff *check_for_rx_frags(struct at76c503 *dev) { struct sk_buff *skb = (struct sk_buff *)dev->rx_skb; struct at76c503_rx_buffer *buf = (struct at76c503_rx_buffer *)skb->data; struct ieee802_11_hdr *i802_11_hdr = (struct ieee802_11_hdr *)buf->packet; /* seq_ctrl, fragment_number, sequence number of new packet */ u16 sctl = le16_to_cpu(i802_11_hdr->seq_ctl); u16 fragnr = sctl & 0xf; u16 seqnr = sctl>>4; u16 frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl); /* length including the IEEE802.11 header, excl. the trailing FCS, excl. the struct at76c503_rx_buffer */ int length = le16_to_cpu(buf->wlength) - dev->rx_data_fcs_len; /* where does the data payload start in skb->data ? */ u8 *data = (u8 *)i802_11_hdr + sizeof(struct ieee802_11_hdr); /* length of payload, excl. the trailing FCS */ int data_len = length - (data - (u8 *)i802_11_hdr); int i; struct rx_data_buf *bptr, *optr; unsigned long oldest = ~0UL; dbg(DBG_RX_FRAGS, "%s: rx data frame_ctl %04x addr2 %s seq/frag %d/%d " "length %d data %d: %s ...", dev->netdev->name, frame_ctl, mac2str(i802_11_hdr->addr2), seqnr, fragnr, length, data_len, hex2str(dev->obuf, data, min((int)(sizeof(dev->obuf)-1)/2,32), '\0')); dbg(DBG_RX_FRAGS_SKB, "%s: incoming skb: head %p data %p " "tail %p end %p len %d", dev->netdev->name, skb->head, skb->data, skb->tail, skb->end, skb->len); if (data_len <= 0) { /* buffers contains no data */ info("%s: rx skb without data", dev->netdev->name); return NULL; } if (fragnr == 0 && !(frame_ctl & IEEE802_11_FCTL_MOREFRAGS)) { /* unfragmented packet received */ if (length < rx_copybreak && (skb = dev_alloc_skb(length)) != NULL) { memcpy(skb_put(skb, length), dev->rx_skb->data + AT76C503_RX_HDRLEN, length); } else { skb_pull(skb, AT76C503_RX_HDRLEN); skb_trim(skb, length); /* Use a new skb for the next receive */ dev->rx_skb = NULL; } dbg(DBG_RX_FRAGS, "%s: unfragmented", dev->netdev->name); return skb; } /* remove the at76c503_rx_buffer header - we don't need it anymore */ /* we need the IEEE802.11 header (for the addresses) if this packet is the first of a chain */ assert(length > AT76C503_RX_HDRLEN); skb_pull(skb, AT76C503_RX_HDRLEN); /* remove FCS at end */ skb_trim(skb, length); dbg(DBG_RX_FRAGS_SKB, "%s: trimmed skb: head %p data %p tail %p " "end %p len %d data %p data_len %d", dev->netdev->name, skb->head, skb->data, skb->tail, skb->end, skb->len, data, data_len); /* look if we've got a chain for the sender address. afterwards optr points to first free or the oldest entry, or, if i < NR_RX_DATA_BUF, bptr points to the entry for the sender address */ /* determining the oldest entry doesn't cope with jiffies wrapping but I don't care to delete a young entry at these rare moments ... */ for(i=0,bptr=dev->rx_data,optr=NULL; i < NR_RX_DATA_BUF; i++,bptr++) { if (bptr->skb != NULL) { if (!memcmp(i802_11_hdr->addr2, bptr->sender,ETH_ALEN)) break; else if (optr == NULL) { optr = bptr; oldest = bptr->last_rx; } else { if (bptr->last_rx < oldest) optr = bptr; } } else { optr = bptr; oldest = 0UL; } } if (i < NR_RX_DATA_BUF) { dbg(DBG_RX_FRAGS, "%s: %d. cacheentry (seq/frag=%d/%d) " "matched sender addr", dev->netdev->name, i, bptr->seqnr, bptr->fragnr); /* bptr points to an entry for the sender address */ if (bptr->seqnr == seqnr) { int left; /* the fragment has the current sequence number */ if (((bptr->fragnr+1)&0xf) == fragnr) { bptr->last_rx = jiffies; /* the next following fragment number -> add the data at the end */ /* is & 0xf necessary above ??? */ // for test only ??? if ((left=skb_tailroom(bptr->skb)) < data_len) { info("%s: only %d byte free (need %d)", dev->netdev->name, left, data_len); } else memcpy(skb_put(bptr->skb, data_len), data, data_len); bptr->fragnr = fragnr; if (!(frame_ctl & IEEE802_11_FCTL_MOREFRAGS)) { /* this was the last fragment - send it */ skb = bptr->skb; bptr->skb = NULL; /* free the entry */ dbg(DBG_RX_FRAGS, "%s: last frag of seq %d", dev->netdev->name, seqnr); return skb; } else return NULL; } else { /* wrong fragment number -> ignore it */ dbg(DBG_RX_FRAGS, "%s: frag nr does not match: %d+1 != %d", dev->netdev->name, bptr->fragnr, fragnr); return NULL; } } else { /* got another sequence number */ if (fragnr == 0) { /* it's the start of a new chain - replace the old one by this */ /* bptr->sender has the correct value already */ dbg(DBG_RX_FRAGS, "%s: start of new seq %d, " "removing old seq %d", dev->netdev->name, seqnr, bptr->seqnr); bptr->seqnr = seqnr; bptr->fragnr = 0; bptr->last_rx = jiffies; /* swap bptr->skb and dev->rx_skb */ skb = bptr->skb; bptr->skb = dev->rx_skb; dev->rx_skb = skb; } else { /* it from the middle of a new chain -> delete the old entry and skip the new one */ dbg(DBG_RX_FRAGS, "%s: middle of new seq %d (%d) " "removing old seq %d", dev->netdev->name, seqnr, fragnr, bptr->seqnr); dev_kfree_skb(bptr->skb); bptr->skb = NULL; } return NULL; } } else { /* if we didn't find a chain for the sender address optr points either to the first free or the oldest entry */ if (fragnr != 0) { /* this is not the begin of a fragment chain ... */ dbg(DBG_RX_FRAGS, "%s: no chain for non-first fragment (%d)", dev->netdev->name, fragnr); return NULL; } assert(optr != NULL); if (optr == NULL) return NULL; if (optr->skb != NULL) { /* swap the skb's */ skb = optr->skb; optr->skb = dev->rx_skb; dev->rx_skb = skb; dbg(DBG_RX_FRAGS, "%s: free old contents: sender %s seq/frag %d/%d", dev->netdev->name, mac2str(optr->sender), optr->seqnr, optr->fragnr); } else { /* take the skb from dev->rx_skb */ optr->skb = dev->rx_skb; dev->rx_skb = NULL; /* let submit_rx_urb() allocate a new skb */ dbg(DBG_RX_FRAGS, "%s: use a free entry", dev->netdev->name); } memcpy(optr->sender, i802_11_hdr->addr2, ETH_ALEN); optr->seqnr = seqnr; optr->fragnr = 0; optr->last_rx = jiffies; return NULL; } } /* check_for_rx_frags */ /* rx interrupt: we expect the complete data buffer in dev->rx_skb */ static void rx_data(struct at76c503 *dev) { struct net_device *netdev = (struct net_device *)dev->netdev; struct net_device_stats *stats = &dev->stats; struct sk_buff *skb = dev->rx_skb; struct at76c503_rx_buffer *buf = (struct at76c503_rx_buffer *)skb->data; struct ieee802_11_hdr *i802_11_hdr; int length = le16_to_cpu(buf->wlength); if (debug & DBG_RX_DATA) { dbg_uc("%s received data packet:", netdev->name); dbg_dumpbuf(" rxhdr", skb->data, AT76C503_RX_HDRLEN); } if (debug & DBG_RX_DATA_CONTENT) dbg_dumpbuf("packet", skb->data + AT76C503_RX_HDRLEN, length); if ((skb=check_for_rx_frags(dev)) == NULL) return; /* if an skb is returned, the at76c503a_rx_header and the FCS is already removed */ i802_11_hdr = (struct ieee802_11_hdr *)skb->data; skb->dev = netdev; skb->ip_summed = CHECKSUM_NONE; /* TODO: should check CRC */ if (i802_11_hdr->addr1[0] & 1) { if (!memcmp(i802_11_hdr->addr1, netdev->broadcast, ETH_ALEN)) skb->pkt_type = PACKET_BROADCAST; else skb->pkt_type = PACKET_MULTICAST; } else if (memcmp(i802_11_hdr->addr1, netdev->dev_addr, ETH_ALEN)) { skb->pkt_type=PACKET_OTHERHOST; } if (netdev->type == ARPHRD_ETHER) { ieee80211_to_eth(skb, dev->iw_mode); } else { ieee80211_fixup(skb, dev->iw_mode); } netdev->last_rx = jiffies; netif_rx(skb); stats->rx_packets++; stats->rx_bytes += length; return; } static int submit_rx_urb(struct at76c503 *dev) { int ret, size; struct sk_buff *skb = dev->rx_skb; if (dev->read_urb == NULL) { err("%s: dev->read_urb is NULL", __FUNCTION__); return -EFAULT; } if (skb == NULL) { skb = dev_alloc_skb(sizeof(struct at76c503_rx_buffer)); if (skb == NULL) { err("%s: unable to allocate rx skbuff.", dev->netdev->name); ret = -ENOMEM; goto exit; } dev->rx_skb = skb; } else { skb_push(skb, skb_headroom(skb)); skb_trim(skb, 0); } size = skb_tailroom(skb); FILL_BULK_URB(dev->read_urb, dev->udev, usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), skb_put(skb, size), size, (usb_complete_t)at76c503_read_bulk_callback, dev); ret = submit_urb(dev->read_urb, GFP_ATOMIC); if (ret < 0) { if (ret == -ENODEV) dbg(DBG_DEVSTART, "usb_submit_urb returned -ENODEV"); else err("%s: rx, usb_submit_urb failed: %d", dev->netdev->name, ret); } exit: if (ret < 0) { if (ret != -ENODEV) { /* If we can't submit the URB, the adapter becomes completely * useless, so try again later */ if (--dev->nr_submit_rx_tries > 0) defer_kevent(dev, KEVENT_SUBMIT_RX); else { err("%s: giving up to submit rx urb after %d failures -" " please unload the driver and/or power cycle the device", dev->netdev->name, NR_SUBMIT_RX_TRIES); } } } else /* reset counter to initial value */ dev->nr_submit_rx_tries = NR_SUBMIT_RX_TRIES; return ret; } /* we are doing a lot of things here in an interrupt. Need a bh handler (Watching TV with a TV card is probably a good test: if you see flickers, we are doing too much. Currently I do see flickers... even with our tasklet :-( ) Maybe because the bttv driver and usb-uhci use the same interrupt */ /* Or maybe because our BH handler is preempting bttv's BH handler.. BHs don't * solve everything.. (alex) */ static void at76c503_read_bulk_callback (struct urb *urb) { struct at76c503 *dev = (struct at76c503 *)urb->context; dev->rx_urb = urb; tasklet_schedule(&dev->tasklet); return; } static void rx_monitor_mode(struct at76c503 *dev) { struct net_device *netdev = (struct net_device *)dev->netdev; struct at76c503_rx_buffer *buf = (struct at76c503_rx_buffer *)dev->rx_skb->data; /* length including the IEEE802.11 header, excl. the trailing FCS, excl. the struct at76c503_rx_buffer */ int length = le16_to_cpu(buf->wlength) - dev->rx_data_fcs_len; struct sk_buff *skb = dev->rx_skb; struct net_device_stats *stats = &dev->stats; struct iw_statistics *wstats = &dev->wstats; update_wstats(dev, buf); if (length < 0) { /* buffer contains no data */ dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE: rx skb without data", dev->netdev->name); return; } if (netdev->type == ARPHRD_IEEE80211_PRISM) { int skblen = sizeof(p80211msg_lnxind_wlansniffrm_t) + length; p80211msg_lnxind_wlansniffrm_t *prism; u8* payload; if ((skb = dev_alloc_skb(skblen)) == NULL) { err("%s: MONITOR MODE: dev_alloc_skb for Prism header " "returned NULL", dev->netdev->name); return; } skb_put(skb, skblen); prism = (p80211msg_lnxind_wlansniffrm_t*)skb->data; payload = skb->data + sizeof(p80211msg_lnxind_wlansniffrm_t); prism->msgcode = DIDmsg_lnxind_wlansniffrm; prism->msglen = sizeof(p80211msg_lnxind_wlansniffrm_t); strcpy(prism->devname, netdev->name); prism->hosttime.did = DIDmsg_lnxind_wlansniffrm_hosttime; prism->hosttime.status = 0; prism->hosttime.len = 4; prism->hosttime.data = jiffies; prism->mactime.did = DIDmsg_lnxind_wlansniffrm_mactime; prism->mactime.status = 0; prism->mactime.len = 4; memcpy(&prism->mactime.data, buf->rx_time, 4); prism->channel.did = DIDmsg_lnxind_wlansniffrm_channel; prism->channel.status = P80211ENUM_msgitem_status_no_value; prism->channel.len = 4; /* INFO: The channel is encoded in the beacon info. (Kismet can figure it out, so less processing here) */ prism->channel.data = 0; prism->rssi.did = DIDmsg_lnxind_wlansniffrm_rssi; prism->rssi.status = 0; prism->rssi.len = 4; prism->rssi.data = buf->rssi; prism->sq.did = DIDmsg_lnxind_wlansniffrm_sq; prism->sq.status = 0; prism->sq.len = 4; prism->sq.data = wstats->qual.qual; prism->signal.did = DIDmsg_lnxind_wlansniffrm_signal; prism->signal.status = 0; prism->signal.len = 4; prism->signal.data = wstats->qual.level; prism->noise.did = DIDmsg_lnxind_wlansniffrm_noise; prism->noise.status = 0; prism->noise.len = 4; prism->noise.data = wstats->qual.noise; prism->rate.did = DIDmsg_lnxind_wlansniffrm_rate; prism->rate.status = 0; prism->rate.len = 4; prism->rate.data = hw_rates[buf->rx_rate] & (~0x80); prism->istx.did = DIDmsg_lnxind_wlansniffrm_istx; prism->istx.status = 0; prism->istx.len = 4; prism->istx.data = P80211ENUM_truth_false; prism->frmlen.did = DIDmsg_lnxind_wlansniffrm_frmlen; prism->frmlen.status = 0; prism->frmlen.len = 4; prism->frmlen.data = length; memcpy(payload, buf->packet, length); } else { /* netdev->type != ARPHRD_IEEE80211_PRISM */ skb_pull(skb, AT76C503_RX_HDRLEN); skb_trim(skb, length); dev->rx_skb = NULL; } skb->dev = netdev; skb->ip_summed = CHECKSUM_NONE; skb->mac.raw = skb->data; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_80211_RAW); netdev->last_rx = jiffies; netif_rx(skb); stats->rx_packets++; stats->rx_bytes += length; } /* end of rx_monitor_mode */ static void rx_tasklet(unsigned long param) { struct at76c503 *dev = (struct at76c503 *)param; struct urb *urb; struct net_device *netdev; struct at76c503_rx_buffer *buf; struct ieee802_11_hdr *i802_11_hdr; u16 frame_ctl; if (!dev) return; urb = dev->rx_urb; netdev = (struct net_device *)dev->netdev; if (dev->flags & AT76C503A_UNPLUG) { dbg(DBG_DEVSTART, "flag UNPLUG set"); if (urb) dbg(DBG_DEVSTART, "urb status %d", urb->status); return; } if(!urb || !dev->rx_skb || !netdev || !dev->rx_skb->data) return; buf = (struct at76c503_rx_buffer *)dev->rx_skb->data; if (!buf) return; i802_11_hdr = (struct ieee802_11_hdr *)buf->packet; if (!i802_11_hdr) return; frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl); if(urb->status != 0){ if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) { dbg(DBG_URB,"%s %s: - nonzero read bulk status received: %d", __FUNCTION__, netdev->name, urb->status); goto no_more_urb; } return; } if (debug & DBG_RX_ATMEL_HDR) { dbg_uc("%s: rx frame: rate %d rssi %d noise %d link %d %s", dev->netdev->name, buf->rx_rate, buf->rssi, buf->noise_level, buf->link_quality, hex2str(dev->obuf,(u8 *)i802_11_hdr, min((int)(sizeof(dev->obuf)-1)/2,48),'\0')); } if (dev->istate == MONITORING) { rx_monitor_mode(dev); goto finish; } /* there is a new bssid around, accept it: */ if(buf->newbss && dev->iw_mode == IW_MODE_ADHOC) { dbg(DBG_PROGRESS, "%s: rx newbss", netdev->name); defer_kevent(dev, KEVENT_NEW_BSS); } switch (frame_ctl & IEEE802_11_FCTL_FTYPE) { case IEEE802_11_FTYPE_DATA: rx_data(dev); break; case IEEE802_11_FTYPE_MGMT: /* jal: TODO: find out if we can update iwspy also on other frames than management (might depend on the radio chip / firmware version !) */ iwspy_update(dev, buf); rx_mgmt(dev, buf); break; case IEEE802_11_FTYPE_CTL: dbg(DBG_RX_CTRL, "%s: ignored ctrl frame: %04x", dev->netdev->name, frame_ctl); break; default: info("%s: it's a frame from mars: %2x", dev->netdev->name, frame_ctl); } /* switch (frame_ctl & IEEE802_11_FCTL_FTYPE) */ finish: submit_rx_urb(dev); no_more_urb: return; } static void at76c503_write_bulk_callback (struct urb *urb) { struct at76c503 *dev = (struct at76c503 *)urb->context; struct net_device_stats *stats = &dev->stats; unsigned long flags; struct at76c503_tx_buffer *mgmt_buf; int ret; if(urb->status != 0){ if((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) { dbg(DBG_URB, "%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); }else return; /* urb has been unlinked */ stats->tx_errors++; }else stats->tx_packets++; spin_lock_irqsave(&dev->mgmt_spinlock, flags); mgmt_buf = dev->next_mgmt_bulk; dev->next_mgmt_bulk = NULL; spin_unlock_irqrestore(&dev->mgmt_spinlock, flags); if (mgmt_buf) { /* we don't copy the padding bytes, but add them to the length */ memcpy(dev->bulk_out_buffer, mgmt_buf, le16_to_cpu(mgmt_buf->wlength) + offsetof(struct at76c503_tx_buffer,packet)); FILL_BULK_URB(dev->write_urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), dev->bulk_out_buffer, le16_to_cpu(mgmt_buf->wlength) + le16_to_cpu(mgmt_buf->padding) + AT76C503_TX_HDRLEN, (usb_complete_t)at76c503_write_bulk_callback, dev); ret = submit_urb(dev->write_urb, GFP_ATOMIC); if (ret) { err("%s: %s error in tx submit urb: %d", dev->netdev->name, __FUNCTION__, ret); } kfree(mgmt_buf); } else netif_wake_queue(dev->netdev); } #ifdef CONFIG_IPAQ_HANDHELD static struct timer_list led_timer; static void ipaq_clear_led (unsigned long time) { ipaq_led_off (RED_LED_2); } static void ipaq_blink_led (void) { ipaq_led_on (RED_LED_2); mod_timer (&led_timer, jiffies + (HZ / 25)); } static void ipaq_init_led (void) { led_timer.function = ipaq_clear_led; init_timer (&led_timer); } #endif static int at76c503_tx(struct sk_buff *skb, struct net_device *netdev) { struct at76c503 *dev = (struct at76c503 *)(netdev->priv); struct net_device_stats *stats = &dev->stats; int ret = 0; int wlen; int submit_len; struct at76c503_tx_buffer *tx_buffer = (struct at76c503_tx_buffer *)dev->bulk_out_buffer; struct ieee802_11_hdr *i802_11_hdr = (struct ieee802_11_hdr *)&(tx_buffer->packet); u8 *payload = tx_buffer->packet + sizeof(struct ieee802_11_hdr); if (netif_queue_stopped(netdev)) { err("%s: %s called while netdev is stopped", netdev->name, __FUNCTION__); //skip this packet dev_kfree_skb(skb); return 0; } if (dev->write_urb->status == USB_ST_URB_PENDING) { err("%s: %s called while dev->write_urb is pending for tx", netdev->name, __FUNCTION__); //skip this packet dev_kfree_skb(skb); return 0; } if (skb->len < 2*ETH_ALEN) { err("%s: %s: skb too short (%d)", dev->netdev->name, __FUNCTION__, skb->len); dev_kfree_skb(skb); return 0; } #ifdef CONFIG_IPAQ_HANDHELD if (machine_is_h5400 ()) ipaq_blink_led (); #endif /* we can get rid of memcpy, if we set netdev->hard_header_len to 8 + sizeof(struct ieee802_11_hdr), because then we have enough space */ // dbg(DBG_TX, "skb->data - skb->head = %d", skb->data - skb->head); if (ntohs(*(u16 *)(skb->data + 2*ETH_ALEN)) <= 1518) { /* this is a 802.3 packet */ if (skb->data[2*ETH_ALEN+2] == rfc1042sig[0] && skb->data[2*ETH_ALEN+2+1] == rfc1042sig[1]) { /* higher layer delivered SNAP header - keep it */ memcpy(payload, skb->data + 2*ETH_ALEN+2, skb->len - 2*ETH_ALEN -2); wlen = sizeof(struct ieee802_11_hdr) + skb->len - 2*ETH_ALEN -2; } else { err("%s: %s: no support for non-SNAP 802.2 packets " "(DSAP x%02x SSAP x%02x cntrl x%02x)", dev->netdev->name, __FUNCTION__, skb->data[2*ETH_ALEN+2], skb->data[2*ETH_ALEN+2+1], skb->data[2*ETH_ALEN+2+2]); dev_kfree_skb(skb); return 0; } } else { /* add RFC 1042 header in front */ memcpy(payload, rfc1042sig, sizeof(rfc1042sig)); memcpy(payload + sizeof(rfc1042sig), skb->data + 2*ETH_ALEN, skb->len - 2*ETH_ALEN); wlen = sizeof(struct ieee802_11_hdr) + sizeof(rfc1042sig) + skb->len - 2*ETH_ALEN; } /* make wireless header */ i802_11_hdr->frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_DATA | (dev->wep_enabled ? IEEE802_11_FCTL_WEP : 0) | (dev->iw_mode == IW_MODE_INFRA ? IEEE802_11_FCTL_TODS : 0)); if(dev->iw_mode == IW_MODE_ADHOC){ memcpy(i802_11_hdr->addr1, skb->data, ETH_ALEN); /* destination */ memcpy(i802_11_hdr->addr2, skb->data+ETH_ALEN, ETH_ALEN); /* source */ memcpy(i802_11_hdr->addr3, dev->bssid, ETH_ALEN); }else if(dev->iw_mode == IW_MODE_INFRA){ memcpy(i802_11_hdr->addr1, dev->bssid, ETH_ALEN); memcpy(i802_11_hdr->addr2, skb->data+ETH_ALEN, ETH_ALEN); /* source */ memcpy(i802_11_hdr->addr3, skb->data, ETH_ALEN); /* destination */ } i802_11_hdr->duration_id = cpu_to_le16(0); i802_11_hdr->seq_ctl = cpu_to_le16(0); /* setup 'atmel' header */ tx_buffer->wlength = cpu_to_le16(wlen); tx_buffer->tx_rate = dev->txrate; /* for broadcast destination addresses, the firmware 0.100.x seems to choose the highest rate set with CMD_STARTUP in basic_rate_set replacing this value */ memset(tx_buffer->reserved, 0, 4); tx_buffer->padding = cpu_to_le16(calc_padding(wlen)); submit_len = wlen + AT76C503_TX_HDRLEN + le16_to_cpu(tx_buffer->padding); { dbg(DBG_TX_DATA_CONTENT, "%s skb->data %s", dev->netdev->name, hex2str(dev->obuf, skb->data, min((int)(sizeof(dev->obuf)-1)/2,32),'\0')); dbg(DBG_TX_DATA, "%s tx wlen x%x pad x%x rate %d hdr %s", dev->netdev->name, le16_to_cpu(tx_buffer->wlength), le16_to_cpu(tx_buffer->padding), tx_buffer->tx_rate, hex2str(dev->obuf, (u8 *)i802_11_hdr, min((sizeof(dev->obuf)-1)/2, sizeof(struct ieee802_11_hdr)),'\0')); dbg(DBG_TX_DATA_CONTENT, "%s payload %s", dev->netdev->name, hex2str(dev->obuf, payload, min((int)(sizeof(dev->obuf)-1)/2,48),'\0')); } /* send stuff */ netif_stop_queue(netdev); netdev->trans_start = jiffies; FILL_BULK_URB(dev->write_urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), tx_buffer, submit_len, (usb_complete_t)at76c503_write_bulk_callback, dev); ret = submit_urb(dev->write_urb, GFP_ATOMIC); if(ret){ stats->tx_errors++; err("%s: error in tx submit urb: %d", netdev->name, ret); if (ret == -EINVAL) err("-EINVAL: urb %p urb->hcpriv %p urb->complete %p", dev->write_urb, dev->write_urb ? dev->write_urb->hcpriv : (void *)-1, dev->write_urb ? dev->write_urb->complete : (void *)-1); goto err; } stats->tx_bytes += skb->len; dev_kfree_skb(skb); return 0; err: return ret; } static void at76c503_tx_timeout(struct net_device *netdev) { struct at76c503 *dev = (struct at76c503 *)(netdev->priv); if (!dev) return; warn("%s: tx timeout.", netdev->name); #if WIRELESS_EXT < 17 dev->write_urb->transfer_flags |= USB_ASYNC_UNLINK; #endif usb_unlink_urb(dev->write_urb); dev->stats.tx_errors++; } static int startup_device(struct at76c503 *dev) { struct at76c503_card_config *ccfg = &dev->card_config; int ret; if (debug & DBG_PARAMS) { char ossid[IW_ESSID_MAX_SIZE+1]; /* make dev->essid printable */ assert(dev->essid_size <= IW_ESSID_MAX_SIZE); memcpy(ossid, dev->essid, dev->essid_size); ossid[dev->essid_size] = '\0'; dbg_uc("%s param: ssid %s (%s) mode %s ch %d wep %s key %d keylen %d", dev->netdev->name, ossid, hex2str(dev->obuf, dev->essid, min((int)(sizeof(dev->obuf)-1)/2, IW_ESSID_MAX_SIZE), '\0'), dev->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra", dev->channel, dev->wep_enabled ? "enabled" : "disabled", dev->wep_key_id, dev->wep_keys_len[dev->wep_key_id]); dbg_uc("%s param: preamble %s rts %d retry %d frag %d " "txrate %s auth_mode %d", dev->netdev->name, dev->preamble_type == PREAMBLE_TYPE_SHORT ? "short" : "long", dev->rts_threshold, dev->short_retry_limit, dev->frag_threshold, dev->txrate == TX_RATE_1MBIT ? "1MBit" : dev->txrate == TX_RATE_2MBIT ? "2MBit" : dev->txrate == TX_RATE_5_5MBIT ? "5.5MBit" : dev->txrate == TX_RATE_11MBIT ? "11MBit" : dev->txrate == TX_RATE_AUTO ? "auto" : "", dev->auth_mode); dbg_uc("%s param: pm_mode %d pm_period %d auth_mode %s " "scan_times %d %d scan_mode %s international_roaming %d", dev->netdev->name, dev->pm_mode, dev->pm_period_us, dev->auth_mode == IEEE802_11_AUTH_ALG_OPEN_SYSTEM ? "open" : "shared_secret", dev->scan_min_time, dev->scan_max_time, dev->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive", dev->international_roaming); } memset(ccfg, 0, sizeof(struct at76c503_card_config)); ccfg->promiscuous_mode = 0; ccfg->short_retry_limit = dev->short_retry_limit; //ccfg->long_retry_limit = dev->long_retry_limit; if (dev->wep_enabled) { if (dev->wep_keys_len[dev->wep_key_id] > WEP_SMALL_KEY_LEN) ccfg->encryption_type = 2; else ccfg->encryption_type = 1; /* jal: always exclude unencrypted if WEP is active */ ccfg->exclude_unencrypted = 1; } else { ccfg->exclude_unencrypted = 0; ccfg->encryption_type = 0; } ccfg->rts_threshold = cpu_to_le16(dev->rts_threshold); ccfg->fragmentation_threshold = cpu_to_le16(dev->frag_threshold); memcpy(ccfg->basic_rate_set, hw_rates, 4); /* jal: really needed, we do a set_mib for autorate later ??? */ ccfg->auto_rate_fallback = (dev->txrate == TX_RATE_AUTO ? 1 : 0); ccfg->channel = dev->channel; ccfg->privacy_invoked = dev->wep_enabled; memcpy(ccfg->current_ssid, dev->essid, IW_ESSID_MAX_SIZE); ccfg->ssid_len = dev->essid_size; ccfg->wep_default_key_id = dev->wep_key_id; memcpy(ccfg->wep_default_key_value, dev->wep_keys, 4 * WEP_KEY_SIZE); ccfg->short_preamble = dev->preamble_type; ccfg->beacon_period = cpu_to_le16(100); ret = set_card_command(dev->udev, CMD_STARTUP, (unsigned char *)&dev->card_config, sizeof(struct at76c503_card_config)); if(ret < 0){ err("%s: set_card_command failed: %d", dev->netdev->name, ret); return ret; } wait_completion(dev, CMD_STARTUP); /* remove BSSID from previous run */ memset(dev->bssid, 0, ETH_ALEN); if (set_radio(dev, 1) == 1) wait_completion(dev, CMD_RADIO); if ((ret=set_preamble(dev, dev->preamble_type)) < 0) return ret; if ((ret=set_frag(dev, dev->frag_threshold)) < 0) return ret; if ((ret=set_rts(dev, dev->rts_threshold)) < 0) return ret; if ((ret=set_autorate_fallback(dev, dev->txrate == TX_RATE_AUTO ? 1 : 0)) < 0) return ret; if ((ret=set_pm_mode(dev, dev->pm_mode)) < 0) return ret; if ((ret=set_iroaming(dev, dev->international_roaming)) < 0) return ret; if (debug & DBG_MIB) { dump_mib_mac(dev); dump_mib_mac_addr(dev); dump_mib_mac_mgmt(dev); dump_mib_mac_wep(dev); dump_mib_mdomain(dev); dump_mib_phy(dev); dump_mib_local(dev); } return 0; } static int at76c503_open(struct net_device *netdev) { struct at76c503 *dev = (struct at76c503 *)(netdev->priv); int ret = 0; dbg(DBG_PROC_ENTRY, "at76c503_open entry"); if(down_interruptible(&dev->sem)) return -EINTR; /* if netdev->dev_addr != dev->mac_addr we must set the mac address in the device ! */ if (memcmp(netdev->dev_addr, dev->mac_addr, ETH_ALEN)) { if (set_mac_address(dev,netdev->dev_addr) >= 0) dbg(DBG_PROGRESS, "%s: set new MAC addr %s", netdev->name, mac2str(netdev->dev_addr)); } #if 0 //test only !!! dump_mib_mac_addr(dev); #endif dev->nr_submit_rx_tries = NR_SUBMIT_RX_TRIES; /* init counter */ if((ret=submit_rx_urb(dev)) < 0){ err("%s: open: submit_rx_urb failed: %d", netdev->name, ret); goto err; } dev->open_count++; defer_kevent(dev, KEVENT_RESTART); dbg(DBG_PROC_ENTRY, "at76c503_open end"); err: up(&dev->sem); return ret < 0 ? ret : 0; } static int at76c503_stop(struct net_device *netdev) { struct at76c503 *dev = (struct at76c503 *)(netdev->priv); unsigned long flags; dbg(DBG_DEVSTART, "%s: ENTER", __FUNCTION__); if (down_interruptible(&dev->sem)) return -EINTR; netif_stop_queue(netdev); NEW_STATE(dev,INIT); if (!(dev->flags & AT76C503A_UNPLUG)) { /* we are called by "ifconfig wlanX down", not because the device isn't avail. anymore */ set_radio(dev, 0); /* we unlink the read urb, because the _open() submits it again. _delete_device() takes care of the read_urb otherwise. */ usb_kill_urb(dev->read_urb); } del_timer_sync(&dev->mgmt_timer); spin_lock_irqsave(&dev->mgmt_spinlock,flags); if (dev->next_mgmt_bulk) { kfree(dev->next_mgmt_bulk); dev->next_mgmt_bulk = NULL; } spin_unlock_irqrestore(&dev->mgmt_spinlock,flags); /* free the bss_list */ free_bss_list(dev); assert(dev->open_count > 0); dev->open_count--; up(&dev->sem); dbg(DBG_DEVSTART, "%s: EXIT", __FUNCTION__); return 0; } static struct net_device_stats *at76c503_get_stats(struct net_device *netdev) { struct at76c503 *dev = (struct at76c503 *)netdev->priv; return &dev->stats; } static struct iw_statistics *at76c503_get_wireless_stats(struct net_device *netdev) { struct at76c503 *dev = (struct at76c503 *)netdev->priv; dbg(DBG_IOCTL, "RETURN qual %d level %d noise %d updated %d", dev->wstats.qual.qual, dev->wstats.qual.level, dev->wstats.qual.noise, dev->wstats.qual.updated); return &dev->wstats; } static void at76c503_set_multicast(struct net_device *netdev) { struct at76c503 *dev = (struct at76c503 *)netdev->priv; int promisc; promisc = ((netdev->flags & IFF_PROMISC) != 0); if(promisc != dev->promisc){ /* grmbl. This gets called in interrupt. */ dev->promisc = promisc; defer_kevent(dev, KEVENT_SET_PROMISC); } } /* we only store the new mac address in netdev struct, it got set when the netdev gets opened. */ static int at76c503_set_mac_address(struct net_device *netdev, void *addr) { struct sockaddr *mac = addr; memcpy(netdev->dev_addr, mac->sa_data, ETH_ALEN); return 1; } /* == PROC iwspy_update == check if we spy on the sender address of buf and update statistics */ static void iwspy_update(struct at76c503 *dev, struct at76c503_rx_buffer *buf) { #if (WIRELESS_EXT > 15) || (IW_MAX_SPY > 0) struct ieee802_11_hdr *hdr = (struct ieee802_11_hdr *)buf->packet; u16 lev_dbm = buf->rssi * 5 / 2; struct iw_quality wstats = { .qual = buf->link_quality, .level = (lev_dbm > 255) ? 255 : lev_dbm, .noise = buf->noise_level, .updated = 1, }; #if WIRELESS_EXT <= 15 int i = 0; #endif spin_lock_bh(&(dev->spy_spinlock)); #if WIRELESS_EXT > 15 if (dev->spy_data.spy_number > 0) { wireless_spy_update(dev->netdev, hdr->addr2, &wstats); } #else for (i; i < dev->iwspy_nr; i++) { if (!memcmp(hdr->addr2, dev->iwspy_addr[i].sa_data, ETH_ALEN)) { memcpy(&(dev->iwspy_stats[i]), &wstats, sizeof(wstats)); break; } } #endif // #if WIRELESS_EXT > 15 spin_unlock_bh(&(dev->spy_spinlock)); #endif // #if (WIRELESS_EXT > 15) || (IW_MAX_SPY > 0) } /* iwspy_update */ static int ethtool_ioctl(struct at76c503 *dev, void *useraddr) { u32 ethcmd; if (get_user(ethcmd, (u32 *)useraddr)) return -EFAULT; #if 0 { dbg_uc("%s: %s: ethcmd=x%x buf: %s", dev->netdev->name, __FUNCTION__, ethcmd, hex2str(dev->obuf,useraddr, min((int)(sizeof(dev->obuf)-1)/2,32), '\0')); } #endif switch (ethcmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo *info = kmalloc(sizeof(struct ethtool_drvinfo), GFP_KERNEL); if (!info) return -ENOMEM; info->cmd = ETHTOOL_GDRVINFO; strncpy(info->driver, "at76c503", sizeof(info->driver)-1); strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); info->version[sizeof(info->version)-1] = '\0'; snprintf(info->bus_info, sizeof(info->bus_info)-1, "usb%d:%d", dev->udev->bus->busnum, dev->udev->devnum); snprintf(info->fw_version, sizeof(info->fw_version)-1, "%d.%d.%d-%d", dev->fw_version.major, dev->fw_version.minor, dev->fw_version.patch, dev->fw_version.build); if (copy_to_user (useraddr, info, sizeof (*info))) { kfree(info); return -EFAULT; } kfree(info); return 0; } break; default: break; } return -EOPNOTSUPP; } /******************************************************************************* * structure that describes the private ioctls/iw handlers of this driver */ static const struct iw_priv_args at76c503_priv_args[] = { { PRIV_IOCTL_SET_SHORT_PREAMBLE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "short_preamble" }, // 0 - long, 1 -short { PRIV_IOCTL_SET_DEBUG, // we must pass the new debug mask as a string, // 'cause iwpriv cannot parse hex numbers // starting with 0x :-( IW_PRIV_TYPE_CHAR | 10, 0, "set_debug"}, // set debug value { PRIV_IOCTL_SET_POWERSAVE_MODE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "powersave_mode"}, // 1 - active, 2 - power save, // 3 - smart power save { PRIV_IOCTL_SET_SCAN_TIMES, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "scan_times"}, // min_channel_time, // max_channel_time { PRIV_IOCTL_SET_SCAN_MODE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "scan_mode"}, // 0 - active, 1 - passive scan { PRIV_IOCTL_SET_INTL_ROAMING, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "intl_roaming"}, /* needed for Kismet, orinoco mode */ { PRIV_IOCTL_SET_MONITOR_MODE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "monitor"}, /* param1: monitor mode: 0 (off), 1 (on,Prism header), 2 (on, no Prism header) param2: channel (to start scan at) */ }; /******************************************************************************* * at76c503 implementations of iw_handler functions: */ static int at76c503_iw_handler_commit(struct net_device *netdev, IW_REQUEST_INFO *info, void *null, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; unsigned long flags; dbg(DBG_IOCTL, "%s %s: restarting the device", netdev->name, __FUNCTION__); // stop any pending tx bulk urb //TODO // jal: TODO: protect access to dev->istate by a spinlock // (ISR's on other processors may read/write it) if (dev->istate != INIT) { dev->istate = INIT; // stop pending management stuff del_timer_sync(&dev->mgmt_timer); spin_lock_irqsave(&dev->mgmt_spinlock,flags); if (dev->next_mgmt_bulk) { kfree(dev->next_mgmt_bulk); dev->next_mgmt_bulk = NULL; } spin_unlock_irqrestore(&dev->mgmt_spinlock,flags); netif_carrier_off(dev->netdev); netif_stop_queue(dev->netdev); } // do the restart after two seconds to catch // following ioctl's (from more params of iwconfig) // in _one_ restart mod_timer(&dev->restart_timer, jiffies+2*HZ); return 0; } static int at76c503_iw_handler_get_name(struct net_device *netdev, IW_REQUEST_INFO *info, char *name, char *extra) { strcpy(name, "IEEE 802.11-DS"); dbg(DBG_IOCTL, "%s: SIOCGIWNAME - name %s", netdev->name, name); return 0; } static int at76c503_iw_handler_set_freq(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_freq *freq, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int chan = -1; int ret = -EIWCOMMIT; dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - freq.m %d freq.e %d", netdev->name, freq->m, freq->e); // modelled on orinoco.c if ((freq->e == 0) && (freq->m <= 1000)) { // Setting by channel number chan = freq->m; } else { // Setting by frequency - search the table int mult = 1; int i; for (i = 0; i < (6 - freq->e); i++) { mult *= 10; } for (i = 0; i < NUM_CHANNELS; i++) { if (freq->m == (channel_frequency[i] * mult)) chan = i + 1; } } if (chan < 1 || !dev->domain ) { // non-positive channels are invalid // we need a domain info to set the channel // either that or an invalid frequency was // provided by the user ret = -EINVAL; } else if (!dev->international_roaming) { if (!(dev->domain->channel_map & (1 << (chan-1)))) { info("%s: channel %d not allowed for domain %s " "(and international_roaming is OFF)", dev->netdev->name, chan, dev->domain->name); ret = -EINVAL; } } if (ret == -EIWCOMMIT) { dev->channel = chan; dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - ch %d", netdev->name, chan); } return ret; } static int at76c503_iw_handler_get_freq(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_freq *freq, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; freq->m = dev->channel; freq->e = 0; if (dev->channel) { dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - freq %ld x 10e%d", netdev->name, channel_frequency[dev->channel - 1], 6); } dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - ch %d", netdev->name, dev->channel); return 0; } static int at76c503_iw_handler_set_mode(struct net_device *netdev, IW_REQUEST_INFO *info, __u32 *mode, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int ret = -EIWCOMMIT; dbg(DBG_IOCTL, "%s: SIOCSIWMODE - %d", netdev->name, *mode); if ((*mode != IW_MODE_ADHOC) && (*mode != IW_MODE_INFRA) && (*mode != IW_MODE_MONITOR)) { ret = -EINVAL; } else { dev->iw_mode = *mode; } return ret; } static int at76c503_iw_handler_get_mode(struct net_device *netdev, IW_REQUEST_INFO *info, __u32 *mode, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; *mode = dev->iw_mode; dbg(DBG_IOCTL, "%s: SIOCGIWMODE - %d", netdev->name, *mode); return 0; } static int at76c503_iw_handler_get_range(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { // inspired by atmel.c struct at76c503 *dev = (struct at76c503*)netdev->priv; struct iw_range *range = (struct iw_range*)extra; int i; data->length = sizeof(struct iw_range); memset(range, 0, sizeof(struct iw_range)); //TODO: range->throughput = xxxxxx; range->min_nwid = 0x0000; range->max_nwid = 0x0000; // this driver doesn't maintain sensitivity information range->sensitivity = 0; range->max_qual.qual = ((dev->board_type == BOARDTYPE_503_INTERSIL_3861) || (dev->board_type == BOARDTYPE_503_INTERSIL_3863)) ? 100 : 0; range->max_qual.level = 255; range->max_qual.noise = 0; range->avg_qual.qual = ((dev->board_type == BOARDTYPE_503_INTERSIL_3861) || (dev->board_type == BOARDTYPE_503_INTERSIL_3863)) ? 30 : 0; range->avg_qual.level = 30; range->avg_qual.noise = 0; range->bitrate[0] = 1000000; range->bitrate[1] = 2000000; range->bitrate[2] = 5500000; range->bitrate[3] = 11000000; range->num_bitrates = 4; range->min_rts = 0; range->max_rts = MAX_RTS_THRESHOLD; range->min_frag = MIN_FRAG_THRESHOLD; range->max_frag = MAX_FRAG_THRESHOLD; //range->min_pmp = 0; //range->max_pmp = 5000000; //range->min_pmt = 0; //range->max_pnt = 0; //range->pmp_flags = IW_POWER_PERIOD; //range->pmt_flags = 0; //range->pm_capa = IW_POWER_PERIOD; //TODO: find out what values we can use to describe PM capabilities range->pmp_flags = IW_POWER_ON; range->pmt_flags = IW_POWER_ON; range->pm_capa = 0; range->encoding_size[0] = WEP_SMALL_KEY_LEN; range->encoding_size[1] = WEP_LARGE_KEY_LEN; range->num_encoding_sizes = 2; range->max_encoding_tokens = NR_WEP_KEYS; //TODO: do we need this? what is a valid value if we don't support? //range->encoding_login_index = -1; /* both WL-240U and Linksys WUSB11 v2.6 specify 15 dBm as output power - take this for all (ignore antenna gains) */ range->txpower[0] = 15; range->num_txpower = 1; range->txpower_capa = IW_TXPOW_DBM; // at time of writing (22/Feb/2004), version we intend to support // is v16, range->we_version_source = WIRELESS_EXT_SUPPORTED; range->we_version_compiled = WIRELESS_EXT; // same as the values used in atmel.c range->retry_capa = IW_RETRY_LIMIT ; range->retry_flags = IW_RETRY_LIMIT; range->r_time_flags = 0; range->min_retry = 1; range->max_retry = 255; range->num_channels = NUM_CHANNELS; range->num_frequency = 0; for (i = 0; i < 32; //number of bits in reg_domain.channel_map i++) { // test if channel map bit is raised if (dev->domain->channel_map & (0x1 << i)) { range->num_frequency += 1; range->freq[i].i = i + 1; range->freq[i].m = channel_frequency[i] * 100000; range->freq[i].e = 1; // channel frequency*100000 * 10^1 } } dbg(DBG_IOCTL, "%s: SIOCGIWRANGE", netdev->name); return 0; } static int at76c503_iw_handler_get_priv(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { dbg(DBG_IOCTL, "%s: SIOCGIWPRIV", netdev->name); memcpy(extra, at76c503_priv_args, sizeof(at76c503_priv_args)); data->length = sizeof(at76c503_priv_args) / sizeof(at76c503_priv_args[0]); return 0; } #if (WIRELESS_EXT > 15) || (IW_MAX_SPY > 0) static int at76c503_iw_handler_set_spy(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; #if WIRELESS_EXT <= 15 int i = 0; #endif int ret = 0; dbg(DBG_IOCTL, "%s: SIOCSIWSPY - number of addresses %d", netdev->name, data->length); spin_lock_bh(&(dev->spy_spinlock)); #if WIRELESS_EXT > 15 ret = iw_handler_set_spy(dev->netdev, info, (union iwreq_data *)data, extra); #else if (&data == NULL) { return -EFAULT; } if (data->length > IW_MAX_SPY) { return -E2BIG; } dev->iwspy_nr = data->length; if (dev->iwspy_nr > 0) { memcpy(dev->iwspy_addr, extra, sizeof(struct sockaddr) * dev->iwspy_nr); memset(dev->iwspy_stats, 0, sizeof(dev->iwspy_stats)); } // Time to show what we have done... dbg(DBG_IOCTL, "%s: New spy list:", netdev->name); for (i = 0; i < dev->iwspy_nr; i++) { dbg(DBG_IOCTL, "%s: SIOCSIWSPY - entry %d: %s", netdev->name, i + 1, mac2str(dev->iwspy_addr[i].sa_data)); } #endif // #if WIRELESS_EXT > 15 spin_unlock_bh(&(dev->spy_spinlock)); return ret; } static int at76c503_iw_handler_get_spy(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; #if WIRELESS_EXT <= 15 int i = 0; #endif int ret = 0; spin_lock_bh(&(dev->spy_spinlock)); #if WIRELESS_EXT > 15 ret = iw_handler_get_spy(dev->netdev, info, (union iwreq_data *)data, extra); #else data->length = dev->iwspy_nr; if ((data->length > 0) && extra) { // Push stuff to user space memcpy(extra, dev->iwspy_addr, sizeof(struct sockaddr) * data->length); memcpy(extra + sizeof(struct sockaddr) * data->length, dev->iwspy_stats, sizeof(struct iw_quality) * data->length); for (i = 0; i < dev->iwspy_nr; i++) { dev->iwspy_stats[i].updated = 0; } } #endif // #if WIRELESS_EXT > 15 spin_unlock_bh(&(dev->spy_spinlock)); dbg(DBG_IOCTL, "%s: SIOCGIWSPY - number of addresses %d", netdev->name, data->length); return ret; } #endif // #if (WIRELESS_EXT > 15) || (IW_MAX_SPY > 0) #if WIRELESS_EXT > 15 static int at76c503_iw_handler_set_thrspy(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int ret; dbg(DBG_IOCTL, "%s: SIOCSIWTHRSPY - number of addresses %d)", netdev->name, data->length); spin_lock_bh(&(dev->spy_spinlock)); ret = iw_handler_set_thrspy(netdev, info, (union iwreq_data *)data, extra); spin_unlock_bh(&(dev->spy_spinlock)); return ret; } static int at76c503_iw_handler_get_thrspy(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int ret; spin_lock_bh(&(dev->spy_spinlock)); ret = iw_handler_get_thrspy(netdev, info, (union iwreq_data *)data, extra); spin_unlock_bh(&(dev->spy_spinlock)); dbg(DBG_IOCTL, "%s: SIOCGIWTHRSPY - number of addresses %d)", netdev->name, data->length); return ret; } #endif //#if WIRELESS_EXT > 15 static int at76c503_iw_handler_set_wap(struct net_device *netdev, IW_REQUEST_INFO *info, struct sockaddr *ap_addr, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; dbg(DBG_IOCTL, "%s: SIOCSIWAP - wap/bssid %s", netdev->name, mac2str(ap_addr->sa_data)); // if the incomming address == ff:ff:ff:ff:ff:ff, the user has // chosen any or auto AP preference if (!memcmp(ap_addr->sa_data, bc_addr, ETH_ALEN) || !memcmp(ap_addr->sa_data, off_addr, ETH_ALEN)) { dev->wanted_bssid_valid = 0; } else { // user wants to set a preferred AP address dev->wanted_bssid_valid = 1; memcpy(dev->wanted_bssid, ap_addr->sa_data, ETH_ALEN); } return -EIWCOMMIT; } static int at76c503_iw_handler_get_wap(struct net_device *netdev, IW_REQUEST_INFO *info, struct sockaddr *ap_addr, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; ap_addr->sa_family = ARPHRD_ETHER; memcpy(ap_addr->sa_data, dev->bssid, ETH_ALEN); dbg(DBG_IOCTL, "%s: SIOCGIWAP - wap/bssid %s", netdev->name, mac2str(ap_addr->sa_data)); return 0; } /*static int at76c503_iw_handler_get_waplist(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { return -EOPNOTSUPP; }*/ #if WIRELESS_EXT > 13 static int at76c503_iw_handler_set_scan(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *param, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; unsigned long flags; int ret = 0; #if WIRELESS_EXT > 19 struct iw_scan_req *req = NULL; #endif dbg(DBG_IOCTL, "%s: SIOCSIWSCAN", netdev->name); /* jal: we don't allow "iwlist wlanX scan" while we are in monitor mode */ if (dev->iw_mode == IW_MODE_MONITOR) return -EBUSY; // stop pending management stuff del_timer_sync(&(dev->mgmt_timer)); spin_lock_irqsave(&(dev->mgmt_spinlock), flags); if (dev->next_mgmt_bulk) { kfree(dev->next_mgmt_bulk); dev->next_mgmt_bulk = NULL; } spin_unlock_irqrestore(&(dev->mgmt_spinlock), flags); if (netif_running(dev->netdev)) { // pause network activity netif_carrier_off(dev->netdev); netif_stop_queue(dev->netdev); } #if WIRELESS_EXT > 19 if (wrqu->data.length && wrqu->data.length == sizeof(struct iw_scan_req)) { req = (struct iw_scan_req *)extra; if (req->scan_type == IW_SCAN_TYPE_PASSIVE) dev->scan_mode = SCAN_TYPE_PASSIVE; else if (req->scan_type == IW_SCAN_TYPE_ACTIVE) dev->scan_mode = SCAN_TYPE_ACTIVE; /* Sanity check values? */ if (req->min_channel_time > 0) { if (dev->istate == MONITORING) dev->monitor_scan_min_time = req->min_channel_time; else dev->scan_min_time = min_channel_time; } if (req->max_channel_time > 0) { if (dev->istate == MONITORING) dev->monitor_scan_max_time = req->max_channel_time; else dev->scan_max_time = max_channel_time; } } #endif // change to scanning state NEW_STATE(dev, SCANNING); defer_kevent(dev, KEVENT_SCAN); return ret; } static int at76c503_iw_handler_get_scan(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; unsigned long flags; struct list_head *lptr, *nptr; struct bss_info *curr_bss; struct iw_event *iwe = kmalloc(sizeof(struct iw_event), GFP_KERNEL); char *curr_val, *curr_pos = extra; int i; dbg(DBG_IOCTL, "%s: SIOCGIWSCAN", netdev->name); if (!iwe) return -ENOMEM; if (dev->istate == SCANNING) /* scan not yet finished */ return -EAGAIN; spin_lock_irqsave(&(dev->bss_list_spinlock), flags); list_for_each_safe(lptr, nptr, &(dev->bss_list)) { curr_bss = list_entry(lptr, struct bss_info, list); iwe->cmd = SIOCGIWAP; iwe->u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe->u.ap_addr.sa_data, curr_bss->bssid, 6); curr_pos = iwe_stream_add_event(curr_pos, extra + IW_SCAN_MAX_DATA, iwe, IW_EV_ADDR_LEN); iwe->u.data.length = curr_bss->ssid_len; iwe->cmd = SIOCGIWESSID; // 1: SSID on , 0: SSID off/any iwe->u.data.flags = (dev->essid_size > 0) ? 1 : 0; curr_pos = iwe_stream_add_point(curr_pos, extra + IW_SCAN_MAX_DATA, iwe, curr_bss->ssid); iwe->cmd = SIOCGIWMODE; iwe->u.mode = (curr_bss->capa & IEEE802_11_CAPA_IBSS) ? IW_MODE_ADHOC : (curr_bss->capa & IEEE802_11_CAPA_ESS) ? IW_MODE_MASTER : IW_MODE_AUTO; // IW_MODE_AUTO = 0 which I thought is // the most logical value to return in this case curr_pos = iwe_stream_add_event(curr_pos, extra + IW_SCAN_MAX_DATA, iwe, IW_EV_UINT_LEN); iwe->cmd = SIOCGIWFREQ; iwe->u.freq.m = curr_bss->channel; iwe->u.freq.e = 0; curr_pos = iwe_stream_add_event(curr_pos, extra + IW_SCAN_MAX_DATA, iwe, IW_EV_FREQ_LEN); iwe->cmd = SIOCGIWENCODE; if (curr_bss->capa & IEEE802_11_CAPA_PRIVACY) { iwe->u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; } else { iwe->u.data.flags = IW_ENCODE_DISABLED; } iwe->u.data.length = 0; curr_pos = iwe_stream_add_point(curr_pos, extra + IW_SCAN_MAX_DATA, iwe, NULL); /* Add quality statistics */ iwe->cmd = IWEVQUAL; iwe->u.qual.level = curr_bss->rssi; /* do some calibration here ? */ iwe->u.qual.noise = 0; iwe->u.qual.qual = curr_bss->link_qual; /* good old Intersil radios report this, too */ /* Add new value to event */ curr_pos = iwe_stream_add_event(curr_pos, extra + IW_SCAN_MAX_DATA, iwe, IW_EV_QUAL_LEN); /* Rate : stuffing multiple values in a single event require a bit * more of magic - Jean II */ curr_val = curr_pos + IW_EV_LCP_LEN; iwe->cmd = SIOCGIWRATE; /* Those two flags are ignored... */ iwe->u.bitrate.fixed = iwe->u.bitrate.disabled = 0; /* Max 8 values */ for(i=0; i < curr_bss->rates_len; i++) { /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe->u.bitrate.value = ((curr_bss->rates[i] & 0x7f) * 500000); /* Add new value to event */ curr_val = iwe_stream_add_value(curr_pos, curr_val, extra + IW_SCAN_MAX_DATA, iwe, IW_EV_PARAM_LEN); } /* Check if we added any event */ if ((curr_val - curr_pos) > IW_EV_LCP_LEN) curr_pos = curr_val; // more information may be sent back using IWECUSTOM } spin_unlock_irqrestore(&(dev->bss_list_spinlock), flags); data->length = (curr_pos - extra); data->flags = 0; kfree(iwe); return 0; } #endif // #if WIRELESS_EXT > 13 static int at76c503_iw_handler_set_essid(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; dbg(DBG_IOCTL, "%s: SIOCSIWESSID - %s", netdev->name, extra); if (data->flags) { memcpy(dev->essid, extra, data->length); dev->essid_size = data->length; #if WIRELESS_EXT < 21 /* For historic reasons, the SSID length used to include one * extra character, C string nul termination, even though SSID is * really an octet string that should not be presented as a C * string. WE-21 changes this to explicitly require the length * _not_ to include nul termination, but for WE < 21, decrement * the length count here to remove the nul termination. */ dev->essid_size = min(dev->essid_size - 1, 0); #endif } else { /* Use any SSID */ dev->essid_size = 0; } return -EIWCOMMIT; } static int at76c503_iw_handler_get_essid(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; if (dev->essid_size) { // not the ANY ssid in dev->essid data->flags = 1; data->length = dev->essid_size; memcpy(extra, dev->essid, data->length); extra[data->length] = '\0'; data->length += 1; } else { // the ANY ssid was specified if (dev->istate == CONNECTED && dev->curr_bss != NULL) { // report the SSID we have found data->flags = 1; data->length = dev->curr_bss->ssid_len; memcpy(extra, dev->curr_bss->ssid, data->length); extra[dev->curr_bss->ssid_len] = '\0'; data->length += 1; } else { // report ANY back data->flags=0; data->length=0; } } dbg(DBG_IOCTL, "%s: SIOCGIWESSID - %s", netdev->name, extra); return 0; } static int at76c503_iw_handler_set_nickname(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; dbg(DBG_IOCTL, "%s: SIOCSIWNICKN - %s", netdev->name, extra); // iwconfig gives length including 0 byte like in the case of essid memcpy(dev->nickn, extra, data->length); return 0; } static int at76c503_iw_handler_get_nickname(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; data->length = strlen(dev->nickn); memcpy(extra, dev->nickn, data->length); extra[data->length] = '\0'; data->length += 1; dbg(DBG_IOCTL, "%s: SIOCGIWNICKN - %s", netdev->name, extra); return 0; } static int at76c503_iw_handler_set_rate(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *bitrate, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int ret = -EIWCOMMIT; dbg(DBG_IOCTL, "%s: SIOCSIWRATE - %d", netdev->name, bitrate->value); switch (bitrate->value) { case -1: dev->txrate = TX_RATE_AUTO; break; // auto rate case 1000000: dev->txrate = TX_RATE_1MBIT; break; case 2000000: dev->txrate = TX_RATE_2MBIT; break; case 5500000: dev->txrate = TX_RATE_5_5MBIT; break; case 11000000: dev->txrate = TX_RATE_11MBIT; break; default: ret = -EINVAL; } return ret; } static int at76c503_iw_handler_get_rate(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *bitrate, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int ret = 0; switch (dev->txrate) { // return max rate if RATE_AUTO case TX_RATE_AUTO: bitrate->value = 11000000; break; case TX_RATE_1MBIT: bitrate->value = 1000000; break; case TX_RATE_2MBIT: bitrate->value = 2000000; break; case TX_RATE_5_5MBIT: bitrate->value = 5500000; break; case TX_RATE_11MBIT: bitrate->value = 11000000; break; default: ret = -EINVAL; } bitrate->fixed = (dev->txrate != TX_RATE_AUTO); bitrate->disabled = 0; dbg(DBG_IOCTL, "%s: SIOCGIWRATE - %d", netdev->name, bitrate->value); return ret; } static int at76c503_iw_handler_set_rts(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *rts, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int ret = -EIWCOMMIT; int rthr = rts->value; dbg(DBG_IOCTL, "%s: SIOCSIWRTS - value %d disabled %s", netdev->name, rts->value, (rts->disabled) ? "true" : "false"); if (rts->disabled) rthr = MAX_RTS_THRESHOLD; if ((rthr < 0) || (rthr > MAX_RTS_THRESHOLD)) { ret = -EINVAL; } else { dev->rts_threshold = rthr; } return ret; } static int at76c503_iw_handler_get_rts(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *rts, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; rts->value = dev->rts_threshold; rts->disabled = (rts->value >= MAX_RTS_THRESHOLD); rts->fixed = 1; dbg(DBG_IOCTL, "%s: SIOCGIWRTS - value %d disabled %s", netdev->name, rts->value, (rts->disabled) ? "true" : "false"); return 0; } static int at76c503_iw_handler_set_frag(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *frag, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int ret = -EIWCOMMIT; int fthr = frag->value; dbg(DBG_IOCTL, "%s: SIOCSIWFRAG - value %d, disabled %s", netdev->name, frag->value, (frag->disabled) ? "true" : "false"); if(frag->disabled) fthr = MAX_FRAG_THRESHOLD; if ((fthr < MIN_FRAG_THRESHOLD) || (fthr > MAX_FRAG_THRESHOLD)) { ret = -EINVAL; } else { dev->frag_threshold = fthr & ~0x1; // get an even value } return ret; } static int at76c503_iw_handler_get_frag(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *frag, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; frag->value = dev->frag_threshold; frag->disabled = (frag->value >= MAX_FRAG_THRESHOLD); frag->fixed = 1; dbg(DBG_IOCTL, "%s: SIOCGIWFRAG - value %d, disabled %s", netdev->name, frag->value, (frag->disabled) ? "true" : "false"); return 0; } static int at76c503_iw_handler_get_txpow(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *power, char *extra) { power->value = 15; power->fixed = 1; /* No power control */ power->disabled = 0; power->flags = IW_TXPOW_DBM; dbg(DBG_IOCTL, "%s: SIOCGIWTXPOW - txpow %d dBm", netdev->name, power->value); return 0; } /* jal: short retry is handled by the firmware (at least 0.90.x), while long retry is not (?) */ static int at76c503_iw_handler_set_retry(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *retry, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int ret = -EIWCOMMIT; dbg(DBG_IOCTL, "%s: SIOCSIWRETRY disabled %d flags x%x val %d", netdev->name, retry->disabled, retry->flags, retry->value); if(!retry->disabled && (retry->flags & IW_RETRY_LIMIT)) { if ((retry->flags & IW_RETRY_MIN) || !(retry->flags & IW_RETRY_MAX)) { dev->short_retry_limit = retry->value; } else ret = -EINVAL; } else { ret = -EINVAL; } return ret; } // adapted (ripped) from atmel.c static int at76c503_iw_handler_get_retry(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *retry, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; dbg(DBG_IOCTL, "%s: SIOCGIWRETRY", netdev->name); retry->disabled = 0; // Can't be disabled // Note : by default, display the min retry number //if((retry->flags & IW_RETRY_MAX)) { // retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; // retry->value = dev->long_retry_limit; //} else { retry->flags = IW_RETRY_LIMIT; retry->value = dev->short_retry_limit; //if(dev->long_retry_limit != dev->short_retry_limit) { // dev->retry.flags |= IW_RETRY_MIN; //} //} return 0; } static int at76c503_iw_handler_set_encode(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *encoding, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int index = (encoding->flags & IW_ENCODE_INDEX) - 1; int len = encoding->length; dbg(DBG_IOCTL, "%s: SIOCSIWENCODE - enc.flags %08x " "pointer %p len %d", netdev->name, encoding->flags, encoding->pointer, encoding->length); dbg(DBG_IOCTL, "%s: SIOCSIWENCODE - old wepstate: enabled %s key_id %d " "auth_mode %s", netdev->name, (dev->wep_enabled) ? "true" : "false", dev->wep_key_id, (dev->auth_mode == IEEE802_11_AUTH_ALG_SHARED_SECRET) ? "restricted" : "open"); // take the old default key if index is invalid if ((index < 0) || (index >= NR_WEP_KEYS)) index = dev->wep_key_id; if (len > 0) { if (len > WEP_LARGE_KEY_LEN) len = WEP_LARGE_KEY_LEN; memset(dev->wep_keys[index], 0, WEP_KEY_SIZE); memcpy(dev->wep_keys[index], extra, len); dev->wep_keys_len[index] = (len <= WEP_SMALL_KEY_LEN) ? WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN; dev->wep_enabled = 1; } dev->wep_key_id = index; dev->wep_enabled = ((encoding->flags & IW_ENCODE_DISABLED) == 0); if (encoding->flags & IW_ENCODE_RESTRICTED) dev->auth_mode = IEEE802_11_AUTH_ALG_SHARED_SECRET; if (encoding->flags & IW_ENCODE_OPEN) dev->auth_mode = IEEE802_11_AUTH_ALG_OPEN_SYSTEM; dbg(DBG_IOCTL, "%s: SIOCSIWENCODE - new wepstate: enabled %s key_id %d " "key_len %d auth_mode %s", netdev->name, (dev->wep_enabled) ? "true" : "false", dev->wep_key_id + 1, dev->wep_keys_len[dev->wep_key_id], (dev->auth_mode == IEEE802_11_AUTH_ALG_SHARED_SECRET) ? "restricted" : "open"); return -EIWCOMMIT; } static int at76c503_iw_handler_get_encode(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *encoding, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int index = (encoding->flags & IW_ENCODE_INDEX) - 1; if ((index < 0) || (index >= NR_WEP_KEYS)) index = dev->wep_key_id; encoding->flags = (dev->auth_mode == IEEE802_11_AUTH_ALG_SHARED_SECRET) ? IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN; if (!dev->wep_enabled) encoding->flags |= IW_ENCODE_DISABLED; if (encoding->pointer) { encoding->length = dev->wep_keys_len[index]; memcpy(extra, dev->wep_keys[index], dev->wep_keys_len[index]); encoding->flags |= (index + 1); } dbg(DBG_IOCTL, "%s: SIOCGIWENCODE - enc.flags %08x " "pointer %p len %d", netdev->name, encoding->flags, encoding->pointer, encoding->length); dbg(DBG_IOCTL, "%s: SIOCGIWENCODE - wepstate: enabled %s key_id %d " "key_len %d auth_mode %s", netdev->name, (dev->wep_enabled) ? "true" : "false", dev->wep_key_id + 1, dev->wep_keys_len[dev->wep_key_id], (dev->auth_mode == IEEE802_11_AUTH_ALG_SHARED_SECRET) ? "restricted" : "open"); return 0; } static int at76c503_iw_handler_set_power(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *power, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; dbg(DBG_IOCTL, "%s: SIOCSIWPOWER - disabled %s flags x%x value x%x", netdev->name, (power->disabled) ? "true" : "false", power->flags, power->value); if (power->disabled) { dev->pm_mode = PM_ACTIVE; } else { // we set the listen_interval based on the period given // no idea how to handle the timeout of iwconfig ??? if (power->flags & IW_POWER_PERIOD) { dev->pm_period_us = power->value; } dev->pm_mode = PM_SAVE; // use iw_priv to select SMART_SAVE } return -EIWCOMMIT; } static int at76c503_iw_handler_get_power(struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_param *power, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; power->disabled = dev->pm_mode == PM_ACTIVE; if ((power->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { power->flags = IW_POWER_TIMEOUT; power->value = 0; } else { unsigned long flags; u16 beacon_int; // of the current bss power->flags = IW_POWER_PERIOD; spin_lock_irqsave(&dev->bss_list_spinlock, flags); beacon_int = dev->curr_bss != NULL ? dev->curr_bss->beacon_interval : 0; spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); if (beacon_int != 0) { power->value = (beacon_int * dev->pm_period_beacon) << 10; } else { power->value = dev->pm_period_us; } } power->flags |= IW_POWER_ALL_R; dbg(DBG_IOCTL, "%s: SIOCGIWPOWER - disabled %s flags x%x value x%x", netdev->name, (power->disabled) ? "true" : "false", power->flags, power->value); return 0; } /******************************************************************************* * Private IOCTLS */ static int at76c503_iw_handler_PRIV_IOCTL_SET_SHORT_PREAMBLE (struct net_device *netdev, IW_REQUEST_INFO *info, char *name, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int val = *((int *)name); int ret = -EIWCOMMIT; dbg(DBG_IOCTL, "%s: PRIV_IOCTL_SET_SHORT_PREAMBLE, %d", netdev->name, val); if (val < 0 || val > 2) { //allow value of 2 - in the win98 driver it stands //for "auto preamble" ...? ret = -EINVAL; } else { dev->preamble_type = val; } return ret; } static int at76c503_iw_handler_PRIV_IOCTL_SET_DEBUG (struct net_device *netdev, IW_REQUEST_INFO *info, struct iw_point *data, char *extra) { char *ptr; u32 val; if (data->length > 0) { val = simple_strtol(extra, &ptr, 0); if (ptr == extra) { val = DBG_DEFAULTS; } dbg_uc("%s: PRIV_IOCTL_SET_DEBUG input %d: %s -> x%x", netdev->name, data->length, extra, val); } else { val = DBG_DEFAULTS; } dbg_uc("%s: PRIV_IOCTL_SET_DEBUG, old 0x%x new 0x%x", netdev->name, debug, val); /* jal: some more output to pin down lockups */ dbg_uc("%s: netif running %d queue_stopped %d carrier_ok %d", netdev->name, netif_running(netdev), netif_queue_stopped(netdev), netif_carrier_ok(netdev)); debug = val; return 0; } static int at76c503_iw_handler_PRIV_IOCTL_SET_POWERSAVE_MODE (struct net_device *netdev, IW_REQUEST_INFO *info, char *name, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int val = *((int *)name); int ret = -EIWCOMMIT; dbg(DBG_IOCTL, "%s: PRIV_IOCTL_SET_POWERSAVE_MODE, %d (%s)", netdev->name, val, val == PM_ACTIVE ? "active" : val == PM_SAVE ? "save" : val == PM_SMART_SAVE ? "smart save" : ""); if (val < PM_ACTIVE || val > PM_SMART_SAVE) { ret = -EINVAL; } else { dev->pm_mode = val; } return ret; } static int at76c503_iw_handler_PRIV_IOCTL_SET_SCAN_TIMES (struct net_device *netdev, IW_REQUEST_INFO *info, char *name, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int mint = *((int *)name); int maxt = *((int *)name + 1); int ret = -EIWCOMMIT; dbg(DBG_IOCTL, "%s: PRIV_IOCTL_SET_SCAN_TIMES - min %d max %d", netdev->name, mint, maxt); if (mint <= 0 || maxt <= 0 || mint > maxt) { ret = -EINVAL; } else { if (dev->istate == MONITORING) { dev->monitor_scan_min_time = mint; dev->monitor_scan_max_time = maxt; ret = 0; } else { dev->scan_min_time = mint; dev->scan_max_time = maxt; } } return ret; } static int at76c503_iw_handler_PRIV_IOCTL_SET_SCAN_MODE (struct net_device *netdev, IW_REQUEST_INFO *info, char *name, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int val = *((int *)name); int ret = -EIWCOMMIT; dbg(DBG_IOCTL, "%s: PRIV_IOCTL_SET_SCAN_MODE - mode %s", netdev->name, (val = SCAN_TYPE_ACTIVE) ? "active" : (val = SCAN_TYPE_PASSIVE) ? "passive" : ""); if (val != SCAN_TYPE_ACTIVE && val != SCAN_TYPE_PASSIVE) { ret = -EINVAL; } else { dev->scan_mode = val; } return ret; } static int set_iroaming(struct at76c503 *dev, int onoff) { int ret = 0; memset(&dev->mib_buf, 0, sizeof(struct set_mib_buffer)); dev->mib_buf.type = MIB_MAC_MGMT; dev->mib_buf.size = 1; dev->mib_buf.index = IROAMING_OFFSET; dev->mib_buf.data[0] = (dev->international_roaming ? 1 : 0); ret = set_mib(dev, &dev->mib_buf); if(ret < 0){ err("%s: set_mib (intl_roaming_enable) failed: %d", dev->netdev->name, ret); } return ret; } static int at76c503_iw_handler_PRIV_IOCTL_SET_INTL_ROAMING (struct net_device *netdev, IW_REQUEST_INFO *info, char *name, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int val = *((int *)name); int ret = -EIWCOMMIT; dbg(DBG_IOCTL, "%s: PRIV_IOCTL_SET_INTL_ROAMING - mode %s", netdev->name, (val == IR_OFF) ? "off" : (val == IR_ON) ? "on" : ""); if (val != IR_OFF && val != IR_ON) { ret = -EINVAL; } else { if (dev->international_roaming != val) { dev->international_roaming = val; set_iroaming(dev, val); } } return ret; } /* == PROC set_monitor_mode == sets dev->netdev->type */ static void set_monitor_mode(struct at76c503 *dev, int use_prism) { if (dev->iw_mode == IW_MODE_MONITOR) { if (use_prism) { dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE ON: " "Prism headers ARE used", dev->netdev->name); dev->netdev->type = ARPHRD_IEEE80211_PRISM; } else { dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE ON: " "Prism headers NOT used", dev->netdev->name); dev->netdev->type = ARPHRD_IEEE80211; } } else { dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE OFF", dev->netdev->name); dev->netdev->type = ARPHRD_ETHER; } } /* set_monitor_mode */ static int at76c503_iw_handler_PRIV_IOCTL_SET_MONITOR_MODE (struct net_device *netdev, IW_REQUEST_INFO *info, char *name, char *extra) { struct at76c503 *dev = (struct at76c503*)netdev->priv; int *params = ((int *)name); int mode = params[0]; int channel = params[1]; int ret = 0; dbg(DBG_IOCTL, "%s: PRIV_IOCTL_SET_MONITOR_MODE - mode %d ch %d", netdev->name, mode, channel); if (mode != MM_OFF && mode != MM_ON && mode != MM_ON_NO_PRISM) ret = -EINVAL; else { if (mode != MM_OFF) { if ((channel >= 1) && (channel <= (sizeof(channel_frequency) / sizeof(channel_frequency[0])))) // INFO: This doesn't actually affect the scan dev->channel = channel; dev->monitor_prism_header = (mode == MM_ON); if (dev->iw_mode != IW_MODE_MONITOR) { ret = -EIWCOMMIT; dev->iw_mode = IW_MODE_MONITOR; } } else { /* mode == MM_OFF */ if (dev->iw_mode == IW_MODE_MONITOR) { ret = -EIWCOMMIT; dev->iw_mode = IW_MODE_INFRA; } } set_monitor_mode(dev, dev->monitor_prism_header); } return ret; } #if WIRELESS_EXT > 12 /******************************************************************************* * structure that advertises the iw handlers of this driver */ static const iw_handler at76c503_handlers[] = { (iw_handler) at76c503_iw_handler_commit, // SIOCSIWCOMMIT (iw_handler) at76c503_iw_handler_get_name, // SIOCGIWNAME (iw_handler) NULL, // SIOCSIWNWID (iw_handler) NULL, // SIOCGIWNWID (iw_handler) at76c503_iw_handler_set_freq, // SIOCSIWFREQ (iw_handler) at76c503_iw_handler_get_freq, // SIOCGIWFREQ (iw_handler) at76c503_iw_handler_set_mode, // SIOCSIWMODE (iw_handler) at76c503_iw_handler_get_mode, // SIOCGIWMODE (iw_handler) NULL, // SIOCSIWSENS (iw_handler) NULL, // SIOCGIWSENS (iw_handler) NULL, // -- hole -- (iw_handler) at76c503_iw_handler_get_range, // SIOCGIWRANGE (iw_handler) NULL, // -- hole -- (iw_handler) NULL, // SIOCGIWPRIV (iw_handler) NULL, // -- hole -- (iw_handler) NULL, // -- hole -- #if (WIRELESS_EXT > 15) || (IW_MAX_SPY > 0) (iw_handler) at76c503_iw_handler_set_spy, // SIOCSIWSPY (iw_handler) at76c503_iw_handler_get_spy, // SIOCGIWSPY #else (iw_handler) NULL, // SIOCSIWSPY (iw_handler) NULL, // SIOCGIWSPY #endif #if WIRELESS_EXT > 15 (iw_handler) at76c503_iw_handler_set_thrspy, // SIOCSIWTHRSPY (iw_handler) at76c503_iw_handler_get_thrspy, // SIOCGIWTHRSPY #else (iw_handler) NULL, // SIOCSIWTHRSPY (iw_handler) NULL, // SIOCGIWTHRSPY #endif (iw_handler) at76c503_iw_handler_set_wap, // SIOCSIWAP (iw_handler) at76c503_iw_handler_get_wap, // SIOCGIWAP (iw_handler) NULL, // -- hole -- (iw_handler) NULL, // SIOCGIWAPLIST #if WIRELESS_EXT > 13 (iw_handler) at76c503_iw_handler_set_scan, // SIOCSIWSCAN (iw_handler) at76c503_iw_handler_get_scan, // SIOCGIWSCAN #else (iw_handler) NULL, // SIOCSIWSCAN (iw_handler) NULL, // SIOCGIWSCAN #endif (iw_handler) at76c503_iw_handler_set_essid, // SIOCSIWESSID (iw_handler) at76c503_iw_handler_get_essid, // SIOCGIWESSID (iw_handler) at76c503_iw_handler_set_nickname, // SIOCSIWNICKN (iw_handler) at76c503_iw_handler_get_nickname, // SIOCGIWNICKN (iw_handler) NULL, // -- hole -- (iw_handler) NULL, // -- hole -- (iw_handler) at76c503_iw_handler_set_rate, // SIOCSIWRATE (iw_handler) at76c503_iw_handler_get_rate, // SIOCGIWRATE (iw_handler) at76c503_iw_handler_set_rts, // SIOCSIWRTS (iw_handler) at76c503_iw_handler_get_rts, // SIOCGIWRTS (iw_handler) at76c503_iw_handler_set_frag, // SIOCSIWFRAG (iw_handler) at76c503_iw_handler_get_frag, // SIOCGIWFRAG (iw_handler) NULL, // SIOCSIWTXPOW (iw_handler) at76c503_iw_handler_get_txpow, // SIOCGIWTXPOW (iw_handler) at76c503_iw_handler_set_retry, // SIOCSIWRETRY (iw_handler) at76c503_iw_handler_get_retry, // SIOCGIWRETRY (iw_handler) at76c503_iw_handler_set_encode, // SIOCSIWENCODE (iw_handler) at76c503_iw_handler_get_encode, // SIOCGIWENCODE (iw_handler) at76c503_iw_handler_set_power, // SIOCSIWPOWER (iw_handler) at76c503_iw_handler_get_power, // SIOCGIWPOWER }; /******************************************************************************* * structure that advertises the private iw handlers of this driver */ static const iw_handler at76c503_priv_handlers[] = { (iw_handler) at76c503_iw_handler_PRIV_IOCTL_SET_SHORT_PREAMBLE, (iw_handler) NULL, (iw_handler) at76c503_iw_handler_PRIV_IOCTL_SET_DEBUG, (iw_handler) NULL, (iw_handler) at76c503_iw_handler_PRIV_IOCTL_SET_POWERSAVE_MODE, (iw_handler) NULL, (iw_handler) at76c503_iw_handler_PRIV_IOCTL_SET_SCAN_TIMES, (iw_handler) NULL, (iw_handler) at76c503_iw_handler_PRIV_IOCTL_SET_SCAN_MODE, (iw_handler) NULL, (iw_handler) at76c503_iw_handler_PRIV_IOCTL_SET_INTL_ROAMING, (iw_handler) NULL, (iw_handler) at76c503_iw_handler_PRIV_IOCTL_SET_MONITOR_MODE, (iw_handler) NULL, }; static const struct iw_handler_def at76c503_handler_def = { .num_standard = sizeof(at76c503_handlers)/sizeof(iw_handler), .num_private = sizeof(at76c503_priv_handlers)/sizeof(iw_handler), .num_private_args = sizeof(at76c503_priv_args)/ sizeof(struct iw_priv_args), .standard = (iw_handler *) at76c503_handlers, .private = (iw_handler *) at76c503_priv_handlers, .private_args = (struct iw_priv_args *) at76c503_priv_args, #if WIRELESS_EXT > 16 .get_wireless_stats = at76c503_get_wireless_stats, #endif #if WIRELESS_EXT == 15 || WIRELESS_EXT == 16 .spy_offset = offsetof(struct at76c503, spy_data), #endif }; #endif // #if WIRELESS_EXT > 12 static int at76c503_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) { struct at76c503 *dev = (struct at76c503*)netdev->priv; struct iwreq *wrq = (struct iwreq *)rq; int ret = 0; char *extra; if (! netif_device_present(netdev)) return -ENODEV; if (down_interruptible(&dev->sem)) return -EINTR; switch (cmd) { // rudimentary ethtool support for hotplug of SuSE 8.3 case SIOCETHTOOL: { ret = ethtool_ioctl(dev,rq->ifr_data); } break; case SIOCGIWNAME: { at76c503_iw_handler_get_name(netdev, NULL, wrq->u.name, NULL); } break; case SIOCSIWFREQ: { ret = at76c503_iw_handler_set_freq(netdev, NULL, &(wrq->u.freq), NULL); } break; case SIOCGIWFREQ: { at76c503_iw_handler_get_freq(netdev, NULL, &(wrq->u.freq), NULL); } break; case SIOCSIWMODE: { ret = at76c503_iw_handler_set_mode(netdev, NULL, &(wrq->u.mode), NULL); } break; case SIOCGIWMODE: { at76c503_iw_handler_get_mode(netdev, NULL, &(wrq->u.mode), NULL); } break; case SIOCGIWRANGE: if (!(extra=kmalloc(sizeof(struct iw_range), GFP_KERNEL))) { ret = -ENOMEM; break; } at76c503_iw_handler_get_range(netdev, NULL, &(wrq->u.data), extra); if (copy_to_user(wrq->u.data.pointer, extra, sizeof(struct iw_range))) { ret = -EFAULT; } kfree(extra); break; case SIOCGIWPRIV: if (!(extra=kmalloc(sizeof(at76c503_priv_args), GFP_KERNEL))) { ret = -ENOMEM; break; } at76c503_iw_handler_get_priv(netdev, NULL, &(wrq->u.data), extra); if (copy_to_user(wrq->u.data.pointer, extra, sizeof(at76c503_priv_args))) { ret = -EFAULT; } kfree(extra); break; #if (WIRELESS_EXT <= 15) && (IW_MAX_SPY > 0) // Set the spy list case SIOCSIWSPY: if (!(extra=kmalloc(sizeof(struct sockaddr) * IW_MAX_SPY, GFP_KERNEL))) { ret = -ENOMEM; break; } if (wrq->u.data.length > IW_MAX_SPY) { ret = -E2BIG; goto sspyerror; } if (copy_from_user(extra, wrq->u.data.pointer, sizeof(struct sockaddr) * dev->iwspy_nr)) { dev->iwspy_nr = 0; ret = -EFAULT; goto sspyerror; } // never needs a device restart at76c503_iw_handler_set_spy(netdev, NULL, &(wrq->u.data), extra); sspyerror: kfree(extra); break; // Get the spy list case SIOCGIWSPY: if (!(extra = (char*)kmalloc((sizeof(struct sockaddr) + sizeof(struct iw_quality)) * wrq->u.data.length, GFP_KERNEL))) { ret = -ENOMEM; break; } at76c503_iw_handler_get_spy(netdev, NULL, &(wrq->u.data), extra); if (copy_to_user(wrq->u.data.pointer, dev->iwspy_addr, (sizeof(struct sockaddr) + sizeof(struct iw_quality)) * wrq->u.data.length)) { ret = -EFAULT; } kfree(extra); break; #endif // #if (WIRELESS_EXT <= 15) && (IW_MAX_SPY > 0) case SIOCSIWAP: at76c503_iw_handler_set_wap(netdev, NULL, &(wrq->u.ap_addr), NULL); break; case SIOCGIWAP: at76c503_iw_handler_get_wap(netdev, NULL, &(wrq->u.ap_addr), NULL); break; /*case SIOCGIWAPLIST: break;*/ #if WIRELESS_EXT > 13 case SIOCSIWSCAN: ret = at76c503_iw_handler_set_scan(netdev, NULL, NULL, NULL); break; case SIOCGIWSCAN: if (!(extra=kmalloc(IW_SCAN_MAX_DATA, GFP_KERNEL))) { ret = -ENOMEM; break; } at76c503_iw_handler_get_scan(netdev, NULL, &(wrq->u.data), extra); if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length)) { ret = -EFAULT; } kfree(extra); break; #endif // #if WIRELESS_EXT > 13 case SIOCSIWESSID: if (!(extra=kmalloc(IW_ESSID_MAX_SIZE + 1, GFP_KERNEL))) { ret = -ENOMEM; break; } if (wrq->u.data.length > IW_ESSID_MAX_SIZE) { ret = -E2BIG; goto sessiderror; } if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length)) { ret = -EFAULT; goto sessiderror; } ret = at76c503_iw_handler_set_essid(netdev, NULL, &(wrq->u.data), extra); sessiderror: kfree(extra); break; case SIOCGIWESSID: if (!(extra=kmalloc(IW_ESSID_MAX_SIZE + 1, GFP_KERNEL))) { ret = -ENOMEM; break; } at76c503_iw_handler_get_essid(netdev, NULL, &(wrq->u.data), extra); if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length)) { ret = -EFAULT; } kfree(extra); break; case SIOCSIWNICKN: if (!(extra=(char*)kmalloc(IW_ESSID_MAX_SIZE + 1, GFP_KERNEL))) { ret = -ENOMEM; break; } if (wrq->u.data.length > IW_ESSID_MAX_SIZE) { ret = -E2BIG; goto snickerror; } if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length)) { ret = -EFAULT; goto snickerror; } ret = at76c503_iw_handler_set_nickname(netdev, NULL, &(wrq->u.data), extra); snickerror: kfree(extra); break; case SIOCGIWNICKN: if (!(extra=kmalloc(IW_ESSID_MAX_SIZE + 1, GFP_KERNEL))) { ret = -ENOMEM; break; } at76c503_iw_handler_get_nickname(netdev, NULL, &(wrq->u.data), extra); if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length)) { ret = -EFAULT; } kfree(extra); break; case SIOCSIWRATE: ret = at76c503_iw_handler_set_rate(netdev, NULL, &(wrq->u.bitrate), NULL); break; case SIOCGIWRATE: at76c503_iw_handler_get_rate(netdev, NULL, &(wrq->u.bitrate), NULL); break; case SIOCSIWRTS: ret = at76c503_iw_handler_set_rts(netdev, NULL, &(wrq->u.rts), NULL); break; case SIOCGIWRTS: at76c503_iw_handler_get_rts(netdev, NULL, &(wrq->u.rts), NULL); break; case SIOCSIWFRAG: ret = at76c503_iw_handler_set_frag(netdev, NULL, &(wrq->u.frag), NULL); break; case SIOCGIWFRAG: at76c503_iw_handler_get_frag(netdev, NULL, &(wrq->u.frag), NULL); break; /*case SIOCSIWTXPOW: break;*/ case SIOCGIWTXPOW: at76c503_iw_handler_get_txpow(netdev, NULL, &(wrq->u.power), NULL); break; case SIOCSIWRETRY: ret = at76c503_iw_handler_set_retry(netdev, NULL, &(wrq->u.retry), NULL); break; case SIOCGIWRETRY: at76c503_iw_handler_get_retry(netdev, NULL, &(wrq->u.retry), NULL); break; case SIOCSIWENCODE: if (!(extra=kmalloc(WEP_KEY_SIZE + 1, GFP_KERNEL))) { ret = -ENOMEM; break; } if (wrq->u.data.length > WEP_KEY_SIZE) { ret = -E2BIG; goto sencodeerror; } if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length)) { ret = -EFAULT; goto sencodeerror; } ret = at76c503_iw_handler_set_encode(netdev, NULL, &(wrq->u.encoding), extra); sencodeerror: kfree(extra); break; case SIOCGIWENCODE: if (!(extra=kmalloc(WEP_KEY_SIZE + 1, GFP_KERNEL))) { ret = -ENOMEM; break; } at76c503_iw_handler_get_encode(netdev, NULL, &(wrq->u.encoding), extra); if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length)) { ret = -EFAULT; } kfree(extra); break; case SIOCSIWPOWER: ret = at76c503_iw_handler_set_power(netdev, NULL, &(wrq->u.power), NULL); break; case SIOCGIWPOWER: at76c503_iw_handler_get_power(netdev, NULL, &(wrq->u.power), NULL); break; case PRIV_IOCTL_SET_SHORT_PREAMBLE: ret = at76c503_iw_handler_PRIV_IOCTL_SET_SHORT_PREAMBLE (netdev, NULL, wrq->u.name, NULL); break; case PRIV_IOCTL_SET_DEBUG: if (!(extra=kmalloc(wrq->u.data.length, GFP_KERNEL))) { ret = -ENOMEM; break; } if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length)) { ret = -EFAULT; goto set_debug_end; } at76c503_iw_handler_PRIV_IOCTL_SET_DEBUG (netdev, NULL, &(wrq->u.data), extra); set_debug_end: kfree(extra); break; case PRIV_IOCTL_SET_POWERSAVE_MODE: ret = at76c503_iw_handler_PRIV_IOCTL_SET_POWERSAVE_MODE (netdev, NULL, wrq->u.name, NULL); break; case PRIV_IOCTL_SET_SCAN_TIMES: ret = at76c503_iw_handler_PRIV_IOCTL_SET_SCAN_TIMES (netdev, NULL, wrq->u.name, NULL); break; case PRIV_IOCTL_SET_SCAN_MODE: ret = at76c503_iw_handler_PRIV_IOCTL_SET_SCAN_MODE (netdev, NULL, wrq->u.name, NULL); break; case PRIV_IOCTL_SET_INTL_ROAMING: ret = at76c503_iw_handler_PRIV_IOCTL_SET_INTL_ROAMING (netdev, NULL, wrq->u.name, NULL); break; case PRIV_IOCTL_SET_MONITOR_MODE: ret = at76c503_iw_handler_PRIV_IOCTL_SET_MONITOR_MODE (netdev, NULL, wrq->u.name, NULL); break; default: dbg(DBG_IOCTL, "%s: ioctl not supported (0x%x)", netdev->name, cmd); ret = -EOPNOTSUPP; } // we only restart the device if it was changed and already opened // before if (ret == -EIWCOMMIT) { if (dev->open_count > 0) { at76c503_iw_handler_commit(netdev, NULL, NULL, NULL); } // reset ret so that this ioctl can return success ret = 0; } up(&dev->sem); return ret; } void at76c503_delete_device(struct at76c503 *dev) { int i; if (!dev) return; /* signal to _stop() that the device is gone */ dev->flags |= AT76C503A_UNPLUG; dbg(DBG_PROC_ENTRY, "%s: ENTER",__FUNCTION__); if (dev->flags & AT76C503A_NETDEV_REGISTERED) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17) if (down_trylock(&rtnl_sem) != 0) { #else if (rtnl_trylock() == 0) { #endif info("%s: rtnl_sem already down'ed", __FUNCTION__); } else { /* synchronously calls at76c503_stop() */ unregister_netdevice(dev->netdev); rtnl_unlock(); } } PUT_DEV(dev->udev); // assuming we used keventd, it must quiesce too flush_scheduled_work (); if(dev->bulk_out_buffer != NULL) kfree(dev->bulk_out_buffer); kfree(dev->ctrl_buffer); if(dev->write_urb != NULL) { usb_kill_urb(dev->write_urb); usb_free_urb(dev->write_urb); } if(dev->read_urb != NULL) { usb_kill_urb(dev->read_urb); usb_free_urb(dev->read_urb); } if(dev->ctrl_buffer != NULL) { usb_kill_urb(dev->ctrl_urb); usb_free_urb(dev->ctrl_urb); } dbg(DBG_PROC_ENTRY,"%s: unlinked urbs",__FUNCTION__); if(dev->rx_skb != NULL) kfree_skb(dev->rx_skb); free_bss_list(dev); del_timer_sync(&dev->bss_list_timer); if (dev->istate == CONNECTED) iwevent_bss_disconnect(dev->netdev); for(i=0; i < NR_RX_DATA_BUF; i++) if (dev->rx_data[i].skb != NULL) { dev_kfree_skb(dev->rx_data[i].skb); dev->rx_data[i].skb = NULL; } dbg(DBG_PROC_ENTRY, "%s: before freeing dev/netdev", __FUNCTION__); free_netdev(dev->netdev); /* dev is in net_dev */ #ifdef CONFIG_IPAQ_HANDHELD if (machine_is_h5400()) { ipaq_led_off (RED_LED); ipaq_led_off (RED_LED_2); } #endif dbg(DBG_PROC_ENTRY, "%s: EXIT", __FUNCTION__); } static int at76c503_alloc_urbs(struct at76c503 *dev) { struct usb_interface *interface = dev->interface; // struct usb_host_interface *iface_desc = &interface->altsetting[0]; struct usb_endpoint_descriptor *endpoint; struct usb_device *udev = dev->udev; int i, buffer_size; dbg(DBG_PROC_ENTRY, "%s: ENTER", __FUNCTION__); dbg(DBG_URB, "%s: NumEndpoints %d ", __FUNCTION__, NUM_EP(interface)); for(i = 0; i < NUM_EP(interface); i++) { endpoint = &EP(interface,i); dbg(DBG_URB, "%s: %d. endpoint: addr x%x attr x%x", __FUNCTION__, i, endpoint->bEndpointAddress, endpoint->bmAttributes); if ((endpoint->bEndpointAddress & 0x80) && ((endpoint->bmAttributes & 3) == 0x02)) { /* we found a bulk in endpoint */ dev->read_urb = alloc_urb(0, GFP_KERNEL); if (!dev->read_urb) { err("No free urbs available"); return -1; } dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; } if (((endpoint->bEndpointAddress & 0x80) == 0x00) && ((endpoint->bmAttributes & 3) == 0x02)) { /* we found a bulk out endpoint */ dev->write_urb = alloc_urb(0, GFP_KERNEL); if (!dev->write_urb) { err("no free urbs available"); return -1; } buffer_size = sizeof(struct at76c503_tx_buffer) + MAX_PADDING_SIZE; dev->bulk_out_size = buffer_size; dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL); if (!dev->bulk_out_buffer) { err("couldn't allocate bulk_out_buffer"); return -1; } FILL_BULK_URB(dev->write_urb, udev, usb_sndbulkpipe(udev, endpoint->bEndpointAddress), dev->bulk_out_buffer, buffer_size, (usb_complete_t)at76c503_write_bulk_callback, dev); } } dev->ctrl_urb = alloc_urb(0, GFP_KERNEL); if (!dev->ctrl_urb) { err("no free urbs available"); return -1; } dev->ctrl_buffer = kmalloc(1024, GFP_KERNEL); if (!dev->ctrl_buffer) { err("couldn't allocate ctrl_buffer"); return -1; } dbg(DBG_PROC_ENTRY, "%s: EXIT", __FUNCTION__); return 0; } struct at76c503 *alloc_new_device(struct usb_device *udev, int board_type, const char *netdev_name) { struct net_device *netdev; struct at76c503 *dev = NULL; int i; /* allocate memory for our device state and intialize it */ netdev = alloc_etherdev(sizeof(struct at76c503)); if (netdev == NULL) { err("out of memory"); return NULL; } dev = (struct at76c503 *)netdev->priv; memset(dev, 0, sizeof(*dev)); dev->udev = udev; dev->netdev = netdev; init_MUTEX (&dev->sem); INIT_WORK (&dev->kevent, kevent, dev); dev->open_count = 0; dev->flags = 0; init_timer(&dev->restart_timer); dev->restart_timer.data = (unsigned long)dev; dev->restart_timer.function = restart_timeout; init_timer(&dev->mgmt_timer); dev->mgmt_timer.data = (unsigned long)dev; dev->mgmt_timer.function = mgmt_timeout; init_timer(&dev->fw_dl_timer); dev->fw_dl_timer.data = (unsigned long)dev; dev->fw_dl_timer.function = fw_dl_timeout; dev->mgmt_spinlock = SPIN_LOCK_UNLOCKED; dev->next_mgmt_bulk = NULL; dev->istate = INTFW_DOWNLOAD; /* initialize empty BSS list */ dev->curr_bss = dev->new_bss = NULL; INIT_LIST_HEAD(&dev->bss_list); dev->bss_list_spinlock = SPIN_LOCK_UNLOCKED; init_timer(&dev->bss_list_timer); dev->bss_list_timer.data = (unsigned long)dev; dev->bss_list_timer.function = bss_list_timeout; #if (WIRELESS_EXT > 15) || (IW_MAX_SPY > 0) dev->spy_spinlock = SPIN_LOCK_UNLOCKED; #if WIRELESS_EXT <= 15 dev->iwspy_nr = 0; #endif #endif /* mark all rx data entries as unused */ for(i=0; i < NR_RX_DATA_BUF; i++) dev->rx_data[i].skb = NULL; dev->tasklet.func = rx_tasklet; dev->tasklet.data = (unsigned long)dev; dev->board_type = board_type; dev->pm_mode = pm_mode; dev->pm_period_us = pm_period; dev_alloc_name(netdev, netdev_name); return dev; } /* alloc_new_device */ /* == PROC init_new_device == firmware got downloaded, we can continue with init */ /* We may have to move the register_netdev into alloc_new_device, because hotplug may try to configure the netdev _before_ (or parallel to) the download of firmware */ int init_new_device(struct at76c503 *dev) { struct net_device *netdev = dev->netdev; int ret; /* set up the endpoint information */ /* check out the endpoints */ dev->interface = dev->udev->actconfig->interface[0]; dbg(DBG_DEVSTART, "USB interface: %d endpoints", NUM_EP(dev->interface)); /* we let this timer run the whole time this driver instance lives */ mod_timer(&dev->bss_list_timer, jiffies+BSS_LIST_TIMEOUT); #ifdef CONFIG_IPAQ_HANDHELD if (machine_is_h5400 ()) ipaq_init_led (); #endif if(at76c503_alloc_urbs(dev) < 0) goto error; /* get firmware version */ ret = get_mib(dev->udev, MIB_FW_VERSION, (u8*)&dev->fw_version, sizeof(dev->fw_version)); if((ret < 0) || ((dev->fw_version.major == 0) && (dev->fw_version.minor == 0) && (dev->fw_version.patch == 0) && (dev->fw_version.build == 0))){ err("getting firmware failed with %d, or version is 0", ret); err("this probably means that the ext. fw was not loaded correctly"); goto error; } /* fw 0.84 doesn't send FCS with rx data */ if (dev->fw_version.major == 0 && dev->fw_version.minor <= 84) dev->rx_data_fcs_len = 0; else dev->rx_data_fcs_len = 4; info("$Id: at76c503.c,v 1.76 2006/06/21 08:50:07 maximsch2 Exp $ compiled %s %s", __DATE__, __TIME__); info("firmware version %d.%d.%d #%d (fcs_len %d)", dev->fw_version.major, dev->fw_version.minor, dev->fw_version.patch, dev->fw_version.build, dev->rx_data_fcs_len); /* MAC address */ ret = get_hw_config(dev); if(ret < 0){ err("could not get MAC address"); goto error; } dev->domain = getRegDomain(dev->regulatory_domain); /* init. netdev->dev_addr */ memcpy(netdev->dev_addr, dev->mac_addr, ETH_ALEN); info("device's MAC %s, regulatory domain %s (id %d)", mac2str(dev->mac_addr), dev->domain->name, dev->regulatory_domain); /* initializing */ dev->international_roaming = international_roaming; dev->channel = DEF_CHANNEL; dev->iw_mode = default_iw_mode; dev->monitor_prism_header = 1; memset(dev->essid, 0, IW_ESSID_MAX_SIZE); memcpy(dev->essid, DEF_ESSID, DEF_ESSID_LEN); dev->essid_size = DEF_ESSID_LEN; strncpy(dev->nickn, DEF_ESSID, sizeof(dev->nickn)); dev->rts_threshold = DEF_RTS_THRESHOLD; dev->frag_threshold = DEF_FRAG_THRESHOLD; dev->short_retry_limit = DEF_SHORT_RETRY_LIMIT; //dev->long_retr_limit = DEF_LONG_RETRY_LIMIT; dev->txrate = TX_RATE_AUTO; dev->preamble_type = preamble_type; dev->auth_mode = auth_mode ? IEEE802_11_AUTH_ALG_SHARED_SECRET : IEEE802_11_AUTH_ALG_OPEN_SYSTEM; dev->scan_min_time = scan_min_time; dev->scan_max_time = scan_max_time; dev->scan_mode = scan_mode; dev->monitor_scan_min_time = monitor_scan_min_time; dev->monitor_scan_max_time = monitor_scan_max_time; netdev->flags &= ~IFF_MULTICAST; /* not yet or never */ netdev->open = at76c503_open; netdev->stop = at76c503_stop; netdev->get_stats = at76c503_get_stats; #if WIRELESS_EXT > 16 /* Add pointers to enable iwspy support. */ dev->wireless_data.spy_data = &dev->spy_data; netdev->wireless_data = &dev->wireless_data; #else /* WIRELESS_EXT > 16 */ netdev->get_wireless_stats = at76c503_get_wireless_stats; #endif /* WIRELESS_EXT > 16 */ netdev->hard_start_xmit = at76c503_tx; netdev->tx_timeout = at76c503_tx_timeout; netdev->watchdog_timeo = 2 * HZ; #if WIRELESS_EXT > 12 netdev->wireless_handlers = (struct iw_handler_def*)&at76c503_handler_def; #endif netdev->do_ioctl = at76c503_ioctl; netdev->set_multicast_list = at76c503_set_multicast; netdev->set_mac_address = at76c503_set_mac_address; // netdev->hard_header_len = 8 + sizeof(struct ieee802_11_hdr); /* // netdev->hard_header = at76c503_header; */ /* putting this inside rtnl_lock() - rtnl_unlock() hangs modprobe ...? */ ret = register_netdev(dev->netdev); if (ret) { err("unable to register netdevice %s (status %d)!", dev->netdev->name, ret); return -1; } info("registered %s", dev->netdev->name); dev->flags |= AT76C503A_NETDEV_REGISTERED; return 0; error: at76c503_delete_device(dev); return -1; } /* init_new_device */ /* == PROC at76c503_get_fw_info == disassembles the firmware image into version, str, internal and external fw part. returns 0 on success, < 0 on error */ static int at76c503_get_fw_info(u8 *fw_data, int fw_size, u32 *board, u32 *version, char **str, u8 **int_fw, u32 *int_fw_size, u8 **ext_fw, u32 *ext_fw_size) { /* fw structure (all numbers are little_endian) offset length description 0 4 crc 32 (seed ~0, no post, all gaps are zeros, header included) 4 4 board type (see at76c503.h) 8 4 version (major<<24|middle<<16|minor<<8|build) c 4 offset of printable string (id) area from begin of image (must be \0 terminated !) 10 4 offset of internal fw part area 14 4 length of internal fw part 18 4 offset of external fw part area (may be first byte _behind_ image in case we have no external part) 1c 4 length of external fw part */ u32 val; if (fw_size < 0x21) { err("fw too short (x%x)",fw_size); return -EFAULT; } /* crc currently not checked */ memcpy(&val,fw_data+4,4); *board = le32_to_cpu(val); memcpy(&val,fw_data+8,4); *version = le32_to_cpu(val); memcpy(&val,fw_data+0xc,4); *str = fw_data + le32_to_cpu(val); memcpy(&val,fw_data+0x10,4); *int_fw = fw_data + le32_to_cpu(val); memcpy(&val,fw_data+0x14,4); *int_fw_size = le32_to_cpu(val); memcpy(&val,fw_data+0x18,4); *ext_fw = fw_data + le32_to_cpu(val); memcpy(&val,fw_data+0x1c,4); *ext_fw_size = le32_to_cpu(val); return 0; } /* == PROC at76c503_do_probe == */ int at76c503_do_probe(struct module *mod, struct usb_device *udev, struct usb_driver *calling_driver, u8 *fw_data, int fw_size, u32 board_type, const char *netdev_name, void **devptr) { struct usb_interface *intf = udev->actconfig->interface[0]; int ret; struct at76c503 *dev = NULL; int op_mode; char *id_str; u32 version; GET_DEV(udev); if ((dev=alloc_new_device(udev, (u8)board_type, netdev_name)) == NULL) { ret = -ENOMEM; goto error; } op_mode = get_op_mode(udev); usb_set_intfdata(intf, dev); dev->interface = intf; dbg(DBG_DEVSTART, "opmode %d", op_mode); /* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ??? we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */ if (op_mode == OPMODE_HW_CONFIG_MODE) { err("cannot handle a device in HW_CONFIG_MODE (opmode %d)", op_mode); ret = -ENODEV; goto error; } if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { dbg(DBG_DEVSTART, "need to download firmware"); /* disassem. the firmware */ if ((ret=at76c503_get_fw_info(fw_data, fw_size, &dev->board_type, &version, &id_str, &dev->intfw, &dev->intfw_size, &dev->extfw, &dev->extfw_size))) { goto error; } dbg(DBG_DEVSTART, "firmware board %u version %u.%u.%u#%u " "(int %x:%tx, ext %x:%tx)", dev->board_type, version>>24,(version>>16)&0xff, (version>>8)&0xff, version&0xff, dev->intfw_size, dev->intfw-fw_data, dev->extfw_size, dev->extfw-fw_data); if (*id_str) dbg(DBG_DEVSTART, "firmware id %s",id_str); if (dev->board_type != board_type) { err("inconsistent board types %u != %u", board_type, dev->board_type); at76c503_delete_device(dev); goto error; } /* download internal firmware part */ dbg(DBG_DEVSTART, "downloading internal firmware"); NEW_STATE(dev,INTFW_DOWNLOAD); defer_kevent(dev,KEVENT_INTERNAL_FW); } else { /* internal firmware already inside the device */ /* get firmware version to test if external firmware is loaded */ /* This works only for newer firmware, e.g. the Intersil 0.90.x says "control timeout on ep0in" and subsequent get_op_mode() fail too :-( */ int force_fw_dwl = 0; /* disassem. the firmware */ if ((ret=at76c503_get_fw_info(fw_data, fw_size, &dev->board_type, &version, &id_str, &dev->intfw, &dev->intfw_size, &dev->extfw, &dev->extfw_size))) { goto error; } /* if version >= 0.100.x.y or device with built-in flash we can query the device * for the fw version */ if (version >= ((0<<24)|(100<<16)) || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { ret = get_mib(udev, MIB_FW_VERSION, (u8*)&dev->fw_version, sizeof(dev->fw_version)); } else { /* force fw download only if the device has no flash inside */ force_fw_dwl = 1; } if ((force_fw_dwl) || (ret < 0) || ((dev->fw_version.major == 0) && (dev->fw_version.minor == 0) && (dev->fw_version.patch == 0) && (dev->fw_version.build == 0))) { if (force_fw_dwl) dbg(DBG_DEVSTART, "forced download of external firmware part"); else dbg(DBG_DEVSTART, "cannot get firmware (ret %d) or all zeros " "- download external firmware", ret); dbg(DBG_DEVSTART, "firmware board %u version %u.%u.%u#%u " "(int %x:%tx, ext %x:%tx)", dev->board_type, version>>24,(version>>16)&0xff, (version>>8)&0xff, version&0xff, dev->intfw_size, dev->intfw-fw_data, dev->extfw_size, dev->extfw-fw_data); if (*id_str) dbg(DBG_DEVSTART, "firmware id %s",id_str); if (dev->board_type != board_type) { err("inconsistent board types %u != %u", board_type, dev->board_type); at76c503_delete_device(dev); goto error; } NEW_STATE(dev,EXTFW_DOWNLOAD); defer_kevent(dev,KEVENT_EXTERNAL_FW); } else { NEW_STATE(dev,INIT); if (init_new_device(dev) < 0) { ret = -ENODEV; goto error; } } } SET_NETDEV_DEV(dev->netdev, &intf->dev); *devptr = dev; /* return dev for 2.4.x kernel probe functions */ return 0; error: PUT_DEV(udev); *devptr = NULL; return ret; } /** * at76c503_usbdfu_post * * Called by usbdfu driver after the firmware has been downloaded, before * the final reset. * (this is called in a usb probe (khubd) context) */ int at76c503_usbdfu_post(struct usb_device *udev) { int result; dbg(DBG_DEVSTART, "Sending remap command..."); result = at76c503_remap(udev); if (result < 0) { err("Remap command failed (%d)", result); return result; } return 0; } /** * at76c503_init */ static int __init at76c503_init(void) { info(DRIVER_DESC " " DRIVER_VERSION); return 0; } /** * at76c503_exit */ static void __exit at76c503_exit(void) { info(DRIVER_DESC " " DRIVER_VERSION " exit"); } module_init (at76c503_init); module_exit (at76c503_exit); EXPORT_SYMBOL(at76c503_do_probe); EXPORT_SYMBOL(at76c503_delete_device); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL");