From a3181906b9d8cfa79f304a832b3bc26ad2d1d173 Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Tue, 1 May 2007 20:00:52 -0400 Subject: [PATCH] Reorder the functions to avoid most forward declarations Always use two newlines between functions. Re-format some function declatrations. Signed-off-by: Pavel Roskin --- at76_usb.c | 7019 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 3575 insertions(+), 3444 deletions(-) diff --git a/at76_usb.c b/at76_usb.c index 59d9129..45d52d8 100644 --- a/at76_usb.c +++ b/at76_usb.c @@ -186,25 +186,6 @@ static int default_iw_mode = IW_MODE_INFRA; static int monitor_scan_min_time = 50; static int monitor_scan_max_time = 600; -/* Function prototypes */ -static void at76_iwspy_update(struct at76_priv *dev, struct at76_rx_buffer *buf); -static void at76_read_bulk_callback(struct urb *urb); -static void at76_write_bulk_callback(struct urb *urb); -static struct bss_info *at76_match_bss(struct at76_priv *dev, - struct bss_info *curr); -static int at76_auth_req(struct at76_priv *dev, struct bss_info *bss, int seq_nr, - struct ieee80211_info_element *challenge); -static int at76_disassoc_req(struct at76_priv *dev, struct bss_info *bss); -static int at76_assoc_req(struct at76_priv *dev, struct bss_info *bss); -static int at76_reassoc_req(struct at76_priv *dev, struct bss_info *curr, - struct bss_info *new); -static void at76_dump_bss_table(struct at76_priv *dev); -static int at76_submit_rx_urb(struct at76_priv *dev); -static int at76_startup_device(struct at76_priv *dev); -static int at76_set_iroaming(struct at76_priv *dev, int onoff); -static void at76_set_monitor_mode(struct at76_priv *dev); -static int at76_init_new_device(struct at76_priv *dev); - /* the supported rates of this hardware, bit7 marks a basic rate */ static const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 }; /* The frequency of each channel in MHz */ @@ -255,6 +236,7 @@ struct dfu_ctx { void *buf; }; + static int at76_dfu_download_block(struct dfu_ctx *ctx, u8 *buffer, int bytes, int block) { @@ -281,6 +263,7 @@ static int at76_dfu_download_block(struct dfu_ctx *ctx, u8 *buffer, int bytes, return result; } + static int at76_dfu_get_status(struct dfu_ctx *ctx, struct dfu_status *status) { int result; @@ -297,6 +280,7 @@ static int at76_dfu_get_status(struct dfu_ctx *ctx, struct dfu_status *status) return result; } + static u8 at76_dfu_get_state(struct usb_device *udev, u8 *state) { int result; @@ -312,6 +296,7 @@ static u8 at76_dfu_get_state(struct usb_device *udev, u8 *state) return result; } + static inline u32 at76_get_timeout(struct dfu_status *s) { u32 ret = (s->poll_timeout[2] << 16) | (s->poll_timeout[1] << 8) | @@ -320,6 +305,7 @@ static inline u32 at76_get_timeout(struct dfu_status *s) return ret; } + static struct dfu_ctx *at76_dfu_alloc_ctx(struct usb_device *udev) { struct dfu_ctx *ctx; @@ -332,6 +318,7 @@ static struct dfu_ctx *at76_dfu_alloc_ctx(struct usb_device *udev) return ctx; } + /* if manifest_sync_timeout > 0 use this timeout (in msec) instead of the one reported by the device in state MANIFEST_SYNC */ static int at76_usbdfu_download(struct usb_device *udev, u8 *dfu_buffer, @@ -473,6 +460,7 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *dfu_buffer, return 0; } + /* some abbrev. for wireless events */ static inline void at76_iwevent_scan_complete(struct net_device *dev) { @@ -483,6 +471,7 @@ static inline void at76_iwevent_scan_complete(struct net_device *dev) at76_dbg(DBG_WE_EVENTS, "%s: SIOCGIWSCAN sent", dev->name); } + static inline void at76_iwevent_bss_connect(struct net_device *dev, u8 *bssid) { union iwreq_data wrqu; @@ -494,6 +483,7 @@ static inline void at76_iwevent_bss_connect(struct net_device *dev, u8 *bssid) at76_dbg(DBG_WE_EVENTS, "%s: %s: SIOCGIWAP sent", dev->name, __FUNCTION__); } + static inline void at76_iwevent_bss_disconnect(struct net_device *dev) { union iwreq_data wrqu; @@ -529,6 +519,7 @@ static char *hex2str(char *obuf, void *buf, int len, char delim) return ret; } + /* check if the given ssid is cloaked */ static inline int at76_is_cloaked_ssid(u8 *ssid, int length) { @@ -539,6 +530,7 @@ static inline int at76_is_cloaked_ssid(u8 *ssid, int length) (length > 0 && !memcmp(ssid, zeros, length)); } + static inline void at76_free_bss_list(struct at76_priv *dev) { struct list_head *next, *ptr; @@ -556,6 +548,7 @@ static inline void at76_free_bss_list(struct at76_priv *dev) spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); } + static inline char *mac2str(u8 *mac) { static char str[6 * 3]; @@ -565,22 +558,18 @@ static inline char *mac2str(u8 *mac) return str; } + /* led trigger */ +static int tx_activity; static void at76_ledtrig_tx_timerfunc(unsigned long data); -DEFINE_LED_TRIGGER(ledtrig_tx); static DEFINE_TIMER(ledtrig_tx_timer, at76_ledtrig_tx_timerfunc, 0, 0); -static int tx_activity; -static int tx_lastactivity; +DEFINE_LED_TRIGGER(ledtrig_tx); -static void at76_ledtrig_tx_activity(void) -{ - tx_activity++; - if (!timer_pending(&ledtrig_tx_timer)) - mod_timer(&ledtrig_tx_timer, jiffies + msecs_to_jiffies(250)); -} static void at76_ledtrig_tx_timerfunc(unsigned long data) { + static int tx_lastactivity; + if (tx_lastactivity != tx_activity) { tx_lastactivity = tx_activity; led_trigger_event(ledtrig_tx, LED_FULL); @@ -591,6 +580,14 @@ static void at76_ledtrig_tx_timerfunc(unsigned long data) } +static void at76_ledtrig_tx_activity(void) +{ + tx_activity++; + if (!timer_pending(&ledtrig_tx_timer)) + mod_timer(&ledtrig_tx_timer, jiffies + msecs_to_jiffies(250)); +} + + static int at76_remap(struct usb_device *udev) { int ret; @@ -616,15 +613,17 @@ static int at76_get_op_mode(struct usb_device *udev) return op_mode; } + /* this loads a block of the second part of the firmware */ -static inline int at76_load_ext_fw_block(struct usb_device *udev, - int i, void *buf, int bsize) +static inline int at76_load_ext_fw_block(struct usb_device *udev, int i, + void *buf, int bsize) { return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, DEVICE_VENDOR_REQUEST_OUT, 0x0802, i, buf, bsize, USB_CTRL_GET_TIMEOUT); } + static inline int at76_get_hw_cfg_rfmd(struct usb_device *udev, union at76_hwcfg *buf, int buf_size) { @@ -634,6 +633,7 @@ static inline int at76_get_hw_cfg_rfmd(struct usb_device *udev, buf, buf_size, USB_CTRL_GET_TIMEOUT); } + /* Intersil boards use a different "value" for GetHWConfig requests */ static inline int get_hw_cfg_intersil(struct usb_device *udev, union at76_hwcfg *buf, int buf_size) @@ -644,6 +644,7 @@ static inline int get_hw_cfg_intersil(struct usb_device *udev, buf, buf_size, 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 @@ -700,6 +701,7 @@ static int at76_get_hw_config(struct at76_priv *dev) return ret; } + static struct reg_domain const *at76_get_reg_domain(u16 code) { static struct reg_domain const fd_tab[] = { @@ -726,6 +728,7 @@ static struct reg_domain const *at76_get_reg_domain(u16 code) return (i >= tab_len) ? &unknown : &fd_tab[i]; } + static inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf, int buf_size) { @@ -735,6 +738,7 @@ static inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf, buf, buf_size, USB_CTRL_GET_TIMEOUT); } + /* Return positive number for status, negative for an error */ static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) { @@ -750,6 +754,7 @@ static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) return stat_buf[5]; } + #define EXT_FW_BLOCK_SIZE 1024 static int at76_download_external_fw(struct usb_device *udev, u8 *buf, int size) { @@ -795,8 +800,9 @@ static int at76_download_external_fw(struct usb_device *udev, u8 *buf, int size) return ret; } -static int at76_set_card_command(struct usb_device *udev, int cmd, - void *buf, int buf_size) + +static int at76_set_card_command(struct usb_device *udev, int cmd, void *buf, + int buf_size) { int ret; struct at76_command *cmd_buf = kmalloc(sizeof(struct at76_command) + @@ -821,8 +827,8 @@ static int at76_set_card_command(struct usb_device *udev, int cmd, return -ENOMEM; } -#define MAKE_CMD_STATUS_CASE(c) case (c): return #c +#define MAKE_CMD_STATUS_CASE(c) case (c): return #c static const char *at76_get_cmd_status_string(u8 cmd_status) { switch (cmd_status) { @@ -840,6 +846,7 @@ static const char *at76_get_cmd_status_string(u8 cmd_status) return "UNKNOWN"; } + /* TODO: should timeout */ static int at76_wait_completion(struct at76_priv *dev, int cmd) { @@ -869,6 +876,7 @@ static int at76_wait_completion(struct at76_priv *dev, int cmd) return status; } + static int at76_set_mib(struct at76_priv *dev, struct set_mib_buffer *buf) { struct usb_device *udev = dev->udev; @@ -901,6 +909,7 @@ static int at76_set_mib(struct at76_priv *dev, struct set_mib_buffer *buf) return ret; } + /* return < 0 on error, == 0 if no command sent, == 1 if cmd sent */ static int at76_set_radio(struct at76_priv *dev, int on_off) { @@ -918,6 +927,7 @@ static int at76_set_radio(struct at76_priv *dev, int on_off) return ret; } + /** * set_pm_mode - set current power save mode * (AT76_PM_OFF/AT76_PM_ON/AT76_PM_SMART) @@ -940,6 +950,7 @@ static int at76_set_pm_mode(struct at76_priv *dev) return ret; } + /* sets the assoc id for power save mode */ static int at76_set_associd(struct at76_priv *dev, u16 id) { @@ -960,6 +971,7 @@ static int at76_set_associd(struct at76_priv *dev, u16 id) return ret; } + /* sets the listen interval for power save mode. really needed, as we have a similar parameter in the assocreq ??? */ static int at76_set_listen_interval(struct at76_priv *dev, u16 interval) @@ -982,6 +994,7 @@ static int at76_set_listen_interval(struct at76_priv *dev, u16 interval) return ret; } + static int at76_set_preamble(struct at76_priv *dev, u8 type) { int ret = 0; @@ -998,6 +1011,7 @@ static int at76_set_preamble(struct at76_priv *dev, u8 type) return ret; } + static int at76_set_frag(struct at76_priv *dev, u16 size) { int ret = 0; @@ -1014,6 +1028,7 @@ static int at76_set_frag(struct at76_priv *dev, u16 size) return ret; } + static int at76_set_rts(struct at76_priv *dev, u16 size) { int ret = 0; @@ -1030,6 +1045,7 @@ static int at76_set_rts(struct at76_priv *dev, u16 size) return ret; } + static int at76_set_autorate_fallback(struct at76_priv *dev, int onoff) { int ret = 0; @@ -1046,6 +1062,7 @@ static int at76_set_autorate_fallback(struct at76_priv *dev, int onoff) return ret; } + static int at76_add_mac_address(struct at76_priv *dev, void *addr) { int ret = 0; @@ -1063,6 +1080,7 @@ static int at76_add_mac_address(struct at76_priv *dev, void *addr) return ret; } + #if 0 /* implemented to get promisc. mode working, but does not help. May still be useful for multicast eventually. */ @@ -1099,6 +1117,7 @@ static int set_group_address(struct at76_priv *dev, u8 *addr, int n) } #endif + static int at76_dump_mib_mac_addr(struct at76_priv *dev) { int ret = 0; @@ -1132,6 +1151,7 @@ static int at76_dump_mib_mac_addr(struct at76_priv *dev) return ret; } + static int at76_dump_mib_mac_wep(struct at76_priv *dev) { int ret = 0; @@ -1177,6 +1197,7 @@ static int at76_dump_mib_mac_wep(struct at76_priv *dev) return ret; } + static int at76_dump_mib_mac_mgmt(struct at76_priv *dev) { int ret = 0; @@ -1232,6 +1253,7 @@ static int at76_dump_mib_mac_mgmt(struct at76_priv *dev) return ret; } + static int at76_dump_mib_mac(struct at76_priv *dev) { int ret = 0; @@ -1279,6 +1301,7 @@ static int at76_dump_mib_mac(struct at76_priv *dev) return ret; } + static int at76_dump_mib_phy(struct at76_priv *dev) { int ret = 0; @@ -1318,6 +1341,7 @@ static int at76_dump_mib_phy(struct at76_priv *dev) return ret; } + static int at76_dump_mib_local(struct at76_priv *dev) { int ret = 0; @@ -1347,6 +1371,7 @@ static int at76_dump_mib_local(struct at76_priv *dev) return ret; } + static int at76_get_mib_mdomain(struct at76_priv *dev, struct mib_mdomain *val) { int ret = 0; @@ -1373,6 +1398,7 @@ static int at76_get_mib_mdomain(struct at76_priv *dev, struct mib_mdomain *val) return ret; } + static void at76_dump_mib_mdomain(struct at76_priv *dev) { char obuf1[2*14+1], obuf2[2*14+1]; /* to hexdump tx_powerlevel, @@ -1393,8 +1419,8 @@ static void at76_dump_mib_mdomain(struct at76_priv *dev) (sizeof(obuf2) - 1) / 2, '\0')); } -static -int at76_get_current_bssid(struct at76_priv *dev) + +static int at76_get_current_bssid(struct at76_priv *dev) { int ret = 0; struct mib_mac_mgmt *mac_mgmt = @@ -1419,6 +1445,7 @@ int at76_get_current_bssid(struct at76_priv *dev) return ret; } + static int at76_get_current_channel(struct at76_priv *dev) { int ret = 0; @@ -1440,6 +1467,7 @@ static int at76_get_current_channel(struct at76_priv *dev) return ret; } + /** * start_scan - start a scan * @@ -1502,6 +1530,7 @@ static int at76_start_scan(struct at76_priv *dev, int use_essid, int ir_step) return at76_set_card_command(dev->udev, CMD_SCAN, &scan, sizeof(scan)); } + static int at76_start_ibss(struct at76_priv *dev) { struct at76_start_bss bss; @@ -1517,6 +1546,7 @@ static int at76_start_ibss(struct at76_priv *dev) sizeof(struct at76_start_bss)); } + /* idx points into dev->bss */ static int at76_join_bss(struct at76_priv *dev, struct bss_info *ptr) { @@ -1539,309 +1569,127 @@ static int at76_join_bss(struct at76_priv *dev, struct bss_info *ptr) sizeof(struct at76_join)); } -/* the firmware download timeout (after remap) */ -static void at76_fw_dl_timeout(unsigned long par) + +/* 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 at76_calc_padding(int wlen) { - struct at76_priv *dev = (struct at76_priv *)par; - schedule_work(&dev->work_reset_device); + /* add the USB TX header */ + wlen += AT76_TX_HDRLEN; + + wlen = wlen % 64; + + if (wlen < 50) + return 50 - wlen; + + if (wlen >= 61) + return 64 + 50 - wlen; + + return 0; } -/* the restart timer timed out */ -static void at76_restart_timeout(unsigned long par) + +/* 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 at76_read_bulk_callback(struct urb *urb) { - struct at76_priv *dev = (struct at76_priv *)par; - schedule_work(&dev->work_restart); + struct at76_priv *priv = urb->context; + + priv->rx_urb = urb; + tasklet_schedule(&priv->tasklet); + return; } -/* we got to check the bss_list for old entries */ -static void at76_bss_list_timeout(unsigned long par) + +static void at76_write_bulk_callback(struct urb *urb) { - struct at76_priv *dev = (struct at76_priv *)par; + struct at76_priv *dev = urb->context; + struct net_device_stats *stats = &dev->stats; unsigned long flags; - struct list_head *lptr, *nptr; - struct bss_info *ptr; - - spin_lock_irqsave(&dev->bss_list_spinlock, flags); + struct at76_tx_buffer *mgmt_buf; + int ret; - list_for_each_safe(lptr, nptr, &dev->bss_list) { + if (urb->status != 0) { + if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) { + at76_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++; - ptr = list_entry(lptr, struct bss_info, list); + 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 (ptr != dev->curr_bss && ptr != dev->new_bss && - time_after(jiffies, ptr->last_rx + BSS_LIST_TIMEOUT)) { - at76_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); + 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 at76_tx_buffer, packet)); + usb_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) + + mgmt_buf->padding + AT76_TX_HDRLEN, + at76_write_bulk_callback, + dev); + ret = usb_submit_urb(dev->write_urb, GFP_ATOMIC); + if (ret) { + err("%s: %s error in tx submit urb: %d", + dev->netdev->name, __FUNCTION__, ret); } - } - spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); - /* restart the timer */ - mod_timer(&dev->bss_list_timer, jiffies + BSS_LIST_TIMEOUT); - + kfree(mgmt_buf); + } else + netif_wake_queue(dev->netdev); } -/* we got a timeout for a infrastructure mgmt packet */ -static void at76_mgmt_timeout(unsigned long par) -{ - struct at76_priv *dev = (struct at76_priv *)par; - schedule_work(&dev->work_mgmt_timeout); -} -/* - * at76_work_mgmt_timeout_scan - expiry of management timer in istate SCANNING - */ -static void at76_handle_mgmt_timeout_scan(struct at76_priv *dev) +/* send a management frame on bulk-out. + txbuf->wlength must be set (in LE format !) */ +static int at76_send_mgmt_bulk(struct at76_priv *dev, + struct at76_tx_buffer *txbuf) { - int status, ret; - struct mib_mdomain mdomain; + unsigned long flags; + int ret = 0; + int urb_status; + void *oldbuf = NULL; - if ((status = at76_get_cmd_status(dev->udev, CMD_SCAN)) < 0) { - err("%s: %s: at76_get_cmd_status failed with %d", - dev->netdev->name, __FUNCTION__, status); - status = CMD_STATUS_IN_PROGRESS; - /* INFO: Hope it was a one off error - if not, scanning - further down the line and stop this cycle */ + 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) == -EINPROGRESS) { + oldbuf = dev->next_mgmt_bulk; /* to kfree below */ + dev->next_mgmt_bulk = txbuf; + txbuf = NULL; } - at76_dbg(DBG_PROGRESS, "%s %s:%d got cmd_status %d (istate %d, " - "scan_runs %d)", - dev->netdev->name, __FUNCTION__, __LINE__, status, - dev->istate, dev->scan_runs); - if (status == CMD_STATUS_COMPLETE) { - if (dev->istate == SCANNING) { - if (at76_debug & DBG_BSS_TABLE) - at76_dump_bss_table(dev); - switch (dev->scan_runs) { + spin_unlock_irqrestore(&dev->mgmt_spinlock, flags); - case 1: - at76_assert(dev->international_roaming); - if ((ret=at76_get_mib_mdomain(dev, &mdomain)) < 0) { - err("at76_get_mib_mdomain returned %d", ret); - } else { - char obuf1[2*14+1], obuf2[2*14+1]; + 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, dev->next_mgmt_bulk, + min((int)(sizeof(dev->obuf)) / 3, 64), ' ')); + kfree(dev->next_mgmt_bulk); + } - at76_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 = at76_start_scan(dev, 0, 1)) < 0) { - err("%s: %s: start_scan (ANY) failed with %d", - dev->netdev->name, __FUNCTION__, ret); - } - at76_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 = at76_start_scan(dev, 1, 1)) < 0) { - err("%s: %s: start_scan (SSID) failed with %d", - dev->netdev->name, __FUNCTION__, ret); - } - at76_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: - dev->scan_state = SCAN_COMPLETED; - /* report the end of scan to user space */ - at76_iwevent_scan_complete(dev->netdev); - dev->istate = JOINING; - /* call join_bss immediately after - re-run of all other threads in at76_devent */ - schedule_work(&dev->work_join); - break; - - default: - err("unexpected dev->scan_runs %d", dev->scan_runs); - } - dev->scan_runs++; - } else { - - at76_assert(dev->istate == MONITORING); - at76_dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE: restart scan", - dev->netdev->name); - at76_start_scan(dev, 0, 0); - at76_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 ((status != CMD_STATUS_IN_PROGRESS) && - (status != CMD_STATUS_IDLE)) - err("%s: %s: Bad scan status: %s", - dev->netdev->name, __FUNCTION__, - at76_get_cmd_status_string(status)); - - /* the first cmd status after scan start is always a IDLE -> - start the timer to poll again until COMPLETED */ - at76_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); - } -} - -/* the deferred procedure called from at76_devent() */ -static void at76_handle_mgmt_timeout(struct at76_priv *dev) -{ - if ((dev->istate != SCANNING && dev->istate != MONITORING) || - (at76_debug & DBG_MGMT_TIMER)) - /* this is normal behavior in states MONITORING, SCANNING ... */ - at76_dbg(DBG_PROGRESS, "%s: timeout, state %d", dev->netdev->name, - dev->istate); - - switch (dev->istate) { - - case MONITORING: - case SCANNING: - at76_handle_mgmt_timeout_scan(dev); - break; - - case JOINING: - at76_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 ad-hoc 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); - at76_iwevent_bss_disconnect(dev->netdev); - dev->istate = SCANNING; - schedule_work(&dev->work_scan); - } - break; - - case AUTHENTICATING: - if (dev->retries-- >= 0) { - at76_auth_req(dev, dev->curr_bss, 1, NULL); - at76_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 */ - dev->istate = JOINING; - schedule_work(&dev->work_join); - } - break; - - case ASSOCIATING: - if (dev->retries-- >= 0) { - at76_assoc_req(dev, dev->curr_bss); - at76_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 */ - dev->istate = JOINING; - schedule_work(&dev->work_join); - } - break; - - case REASSOCIATING: - if (dev->retries-- >= 0) - at76_reassoc_req(dev, dev->curr_bss, dev->new_bss); - else { - /* we disassociate from the curr_bss and - scan again ... */ - dev->istate = DISASSOCIATING; - dev->retries = DISASSOC_RETRIES; - at76_disassoc_req(dev, dev->curr_bss); - } - at76_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) { - at76_disassoc_req(dev, dev->curr_bss); - at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", - __FUNCTION__, __LINE__); - mod_timer(&dev->mgmt_timer, jiffies + HZ); - } else { - /* we scan again ... */ - dev->istate = SCANNING; - schedule_work(&dev->work_scan); - } - break; - - case INIT: - break; - - default: - at76_assert(0); - } -} - -/* 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 at76_calc_padding(int wlen) -{ - /* add the USB TX header */ - wlen += AT76_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 at76_send_mgmt_bulk(struct at76_priv *dev, - struct at76_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) == -EINPROGRESS) { - 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, dev->next_mgmt_bulk, - min((int)(sizeof(dev->obuf)) / 3, 64), ' ')); - kfree(dev->next_mgmt_bulk); - } - - if (txbuf) { + if (txbuf) { txbuf->tx_rate = 0; txbuf->padding = at76_calc_padding(le16_to_cpu(txbuf->wlength)); @@ -1879,60 +1727,22 @@ static int at76_send_mgmt_bulk(struct at76_priv *dev, } /* if (txbuf) */ return ret; - } + /* Go to the next information element */ static inline void next_ie(struct ieee80211_info_element **ie) { *ie = (struct ieee80211_info_element *)(&(*ie)->data[(*ie)->len]); } -static int at76_disassoc_req(struct at76_priv *dev, struct bss_info *bss) -{ - struct at76_tx_buffer *tx_buffer; - struct ieee80211_hdr_3addr *mgmt; - struct ieee80211_disassoc *req; - - at76_assert(bss != NULL); - if (bss == NULL) - return -EFAULT; - - tx_buffer = kmalloc(DISASSOC_FRAME_SIZE + MAX_PADDING_SIZE, GFP_ATOMIC); - if (!tx_buffer) - return -ENOMEM; - - req = (struct ieee80211_disassoc *)(&tx_buffer->packet); - mgmt = &req->header; - - /* make wireless header */ - mgmt->frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT|IEEE80211_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. at76_priv tx header */ - tx_buffer->wlength = cpu_to_le16(DISASSOC_FRAME_SIZE - AT76_TX_HDRLEN); - - at76_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 at76_send_mgmt_bulk(dev, tx_buffer); - -} /* 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 at76_auth_req(struct at76_priv *dev, struct bss_info *bss, int seq_nr, - struct ieee80211_info_element *challenge) +static int at76_auth_req(struct at76_priv *dev, struct bss_info *bss, + int seq_nr, struct ieee80211_info_element *challenge) { struct at76_tx_buffer *tx_buffer; struct ieee80211_hdr_3addr *mgmt; @@ -1982,9 +1792,9 @@ static int at76_auth_req(struct at76_priv *dev, struct bss_info *bss, int seq_nr /* either send immediately (if no data tx is pending or put it in pending list */ return at76_send_mgmt_bulk(dev, tx_buffer); - } + static int at76_assoc_req(struct at76_priv *dev, struct bss_info *bss) { struct at76_tx_buffer *tx_buffer; @@ -2057,9 +1867,9 @@ static int at76_assoc_req(struct at76_priv *dev, struct bss_info *bss) /* either send immediately (if no data tx is pending or put it in pending list */ return at76_send_mgmt_bulk(dev, tx_buffer); - } + /* we are currently associated to curr_bss and want to reassoc to new_bss */ static int at76_reassoc_req(struct at76_priv *dev, struct bss_info *curr_bss, @@ -2139,2301 +1949,1770 @@ static int at76_reassoc_req(struct at76_priv *dev, struct bss_info *curr_bss, /* either send immediately (if no data tx is pending or put it in pending list */ return at76_send_mgmt_bulk(dev, tx_buffer); - } -/* Called after successful association */ -static void at76_work_assoc_done(struct work_struct *work) + +static int at76_disassoc_req(struct at76_priv *dev, struct bss_info *bss) { - struct at76_priv *dev = container_of(work, struct at76_priv, - work_assoc_done); + struct at76_tx_buffer *tx_buffer; + struct ieee80211_hdr_3addr *mgmt; + struct ieee80211_disassoc *req; - down(&dev->sem); + at76_assert(bss != NULL); + if (bss == NULL) + return -EFAULT; - at76_assert(dev->istate == ASSOCIATING || dev->istate == REASSOCIATING); - if (dev->iw_mode == IW_MODE_INFRA) { - at76_assert(dev->curr_bss != NULL); - if (dev->curr_bss != NULL && dev->pm_mode != AT76_PM_OFF) { - /* calculate the listen interval in units of - beacon intervals of the curr_bss */ - u32 pm_period_beacon = (dev->pm_period >> 10) / - dev->curr_bss->beacon_interval; - - pm_period_beacon = max(pm_period_beacon, 2u); - pm_period_beacon = min(pm_period_beacon, 0xffffu); + tx_buffer = kmalloc(DISASSOC_FRAME_SIZE + MAX_PADDING_SIZE, GFP_ATOMIC); + if (!tx_buffer) + return -ENOMEM; - at76_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, pm_period_beacon); + req = (struct ieee80211_disassoc *)(&tx_buffer->packet); + mgmt = &req->header; - at76_set_associd(dev, dev->curr_bss->assoc_id); - at76_set_listen_interval(dev, (u16)pm_period_beacon); -#ifdef DEBUG - at76_dump_mib_mac(dev); - at76_dump_mib_mac_mgmt(dev); -#endif - } - } - at76_set_pm_mode(dev); + /* make wireless header */ + mgmt->frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT|IEEE80211_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); - netif_carrier_on(dev->netdev); - netif_wake_queue(dev->netdev); - dev->istate = CONNECTED; - at76_iwevent_bss_connect(dev->netdev, dev->curr_bss->bssid); - at76_dbg(DBG_PROGRESS, "%s: connected to BSSID %s", - dev->netdev->name, mac2str(dev->curr_bss->bssid)); + req->reason = 0; - up(&dev->sem); -} + /* init. at76_priv tx header */ + tx_buffer->wlength = cpu_to_le16(DISASSOC_FRAME_SIZE - AT76_TX_HDRLEN); -/* Download external firmware */ -static void at76_work_external_fw(struct work_struct *work) -{ - struct at76_priv *dev = container_of(work, struct at76_priv, - work_external_fw); - int ret; - u8 op_mode; + at76_dbg(DBG_TX_MGMT, "%s: DisAssocReq bssid %s", + dev->netdev->name, mac2str(mgmt->addr3)); - down(&dev->sem); + /* either send immediately (if no data tx is pending + or put it in pending list */ + return at76_send_mgmt_bulk(dev, tx_buffer); +} - op_mode = at76_get_op_mode(dev->udev); - at76_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; - } +/* the firmware download timeout (after remap) */ +static void at76_fw_dl_timeout(unsigned long par) +{ + struct at76_priv *dev = (struct at76_priv *)par; + schedule_work(&dev->work_reset_device); +} - if (dev->extfw && dev->extfw_size) { - ret = at76_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"); - /* can we do this with dev->sem down? */ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 5 + 1); - } - } - dev->istate = INIT; - if ((ret = at76_init_new_device(dev)) < 0) - err("Downloading external firmware failed: %d", ret); - end_external_fw: - up(&dev->sem); +/* the restart timer timed out */ +static void at76_restart_timeout(unsigned long par) +{ + struct at76_priv *dev = (struct at76_priv *)par; + schedule_work(&dev->work_restart); } -/* Download internal firmware */ -static void at76_work_internal_fw(struct work_struct *work) -{ - struct at76_priv *dev = container_of(work, struct at76_priv, - work_internal_fw); - int ret; - down(&dev->sem); +/* we got to check the bss_list for old entries */ +static void at76_bss_list_timeout(unsigned long par) +{ + struct at76_priv *dev = (struct at76_priv *)par; + unsigned long flags; + struct list_head *lptr, *nptr; + struct bss_info *ptr; - ret = at76_usbdfu_download(dev->udev, dev->intfw, - dev->intfw_size, - dev->board_type == - BOARDTYPE_505A_RFMD_2958 ? 2000 : 0); + spin_lock_irqsave(&dev->bss_list_spinlock, flags); - if (ret < 0) { - err("downloading internal fw failed with %d", ret); - goto end_internal_fw; - } + list_for_each_safe(lptr, nptr, &dev->bss_list) { - at76_dbg(DBG_DEVSTART, "sending REMAP"); + ptr = list_entry(lptr, struct bss_info, list); - /* no REMAP for 505A (see SF driver) */ - if (dev->board_type != BOARDTYPE_505A_RFMD_2958) - if ((ret = at76_remap(dev->udev)) < 0) { - err("sending REMAP failed with %d", ret); - goto end_internal_fw; + if (ptr != dev->curr_bss && ptr != dev->new_bss && + time_after(jiffies, ptr->last_rx + BSS_LIST_TIMEOUT)) { + at76_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); } - - at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); - dev->istate = EXTFW_DOWNLOAD; - mod_timer(&dev->fw_dl_timer, jiffies + 2 * HZ + 1); - - end_internal_fw: - up(&dev->sem); + } + spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); + /* restart the timer */ + mod_timer(&dev->bss_list_timer, jiffies + BSS_LIST_TIMEOUT); } -/* Try joining a BSS */ -static void at76_work_join(struct work_struct *work) + +static void at76_dump_bss_table(struct at76_priv *dev) { - struct at76_priv *dev = container_of(work, struct at76_priv, - work_join); - int ret; + struct bss_info *ptr; unsigned long flags; + struct list_head *lptr; + char obuf_s[3*32]; - down(&dev->sem); - - if (dev->istate == INIT) - goto end_join; - - at76_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 = at76_match_bss(dev, dev->curr_bss); - spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); - - if (dev->curr_bss != NULL) { - if ((ret = at76_join_bss(dev, dev->curr_bss)) < 0) { - err("%s: join_bss failed with %d", - dev->netdev->name, ret); - goto end_join; - } - - ret = at76_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 */ - schedule_work(&dev->work_join); - goto end_join; - } + pr_debug("%s BSS table (curr=%p, new=%p):", dev->netdev->name, + dev->curr_bss, dev->new_bss); - /* here we have joined the (I)BSS */ - if (dev->iw_mode == IW_MODE_ADHOC) { - struct bss_info *bptr = dev->curr_bss; - dev->istate = 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; - at76_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 */ - dev->istate = AUTHENTICATING; - at76_auth_req(dev, dev->curr_bss, 1, NULL); - at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", - __FUNCTION__, __LINE__); - mod_timer(&dev->mgmt_timer, jiffies + HZ); - } - goto end_join; + list_for_each(lptr, &dev->bss_list) { + ptr = list_entry(lptr, struct bss_info, list); + pr_debug("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'), + ptr->capa, + hex2str(obuf_s, ptr->rates, + min(sizeof(obuf_s) / 3, + (size_t) ptr->rates_len), ' '), + ptr->rssi, ptr->link_qual, ptr->noise_level); } + spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); +} - /* here we haven't found a matching (i)bss ... */ - if (dev->iw_mode == IW_MODE_ADHOC) { - dev->istate = STARTIBSS; - schedule_work(&dev->work_start_ibss); - goto end_join; - } - /* haven't found a matching BSS in infra mode - try again */ - dev->istate = SCANNING; - schedule_work(&dev->work_scan); - end_join: - up(&dev->sem); +/* we got a timeout for a infrastructure mgmt packet */ +static void at76_mgmt_timeout(unsigned long par) +{ + struct at76_priv *dev = (struct at76_priv *)par; + schedule_work(&dev->work_mgmt_timeout); } -static void at76_work_mgmt_timeout(struct work_struct *work) + +/* + * at76_work_mgmt_timeout_scan - expiry of management timer in istate SCANNING + */ +static void at76_handle_mgmt_timeout_scan(struct at76_priv *dev) { - struct at76_priv *dev = container_of(work, struct at76_priv, - work_mgmt_timeout); + int status, ret; + struct mib_mdomain mdomain; - down(&dev->sem); - at76_handle_mgmt_timeout(dev); - up(&dev->sem); -} - -static void at76_work_new_bss(struct work_struct *work) -{ - struct at76_priv *dev = container_of(work, struct at76_priv, - work_new_bss); - int ret; - struct net_device *netdev = dev->netdev; - struct mib_mac_mgmt mac_mgmt; - - down(&dev->sem); - - ret = at76_get_mib(dev->udev, MIB_MAC_MGMT, &mac_mgmt, - sizeof(struct mib_mac_mgmt)); - if (ret < 0) { - err("%s: at76_get_mib failed: %d", netdev->name, ret); - goto new_bss_clean; + if ((status = at76_get_cmd_status(dev->udev, CMD_SCAN)) < 0) { + err("%s: %s: at76_get_cmd_status failed with %d", + dev->netdev->name, __FUNCTION__, status); + status = CMD_STATUS_IN_PROGRESS; + /* INFO: Hope it was a one off error - if not, scanning + further down the line and stop this cycle */ } + at76_dbg(DBG_PROGRESS, "%s %s:%d got cmd_status %d (istate %d, " + "scan_runs %d)", + dev->netdev->name, __FUNCTION__, __LINE__, status, + dev->istate, dev->scan_runs); + if (status == CMD_STATUS_COMPLETE) { + if (dev->istate == SCANNING) { + if (at76_debug & DBG_BSS_TABLE) + at76_dump_bss_table(dev); + switch (dev->scan_runs) { - at76_dbg(DBG_PROGRESS, "ibss_change = 0x%2x", mac_mgmt.ibss_change); - memcpy(dev->bssid, mac_mgmt.current_bssid, ETH_ALEN); - at76_dbg(DBG_PROGRESS, "using BSSID %s", mac2str(dev->bssid)); - - at76_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 = at76_set_mib(dev, &dev->mib_buf); - if (ret < 0) - err("%s: set_mib (ibss change ok) failed: %d", netdev->name, ret); - - new_bss_clean: - up(&dev->sem); -} + case 1: + at76_assert(dev->international_roaming); + if ((ret=at76_get_mib_mdomain(dev, &mdomain)) < 0) { + err("at76_get_mib_mdomain returned %d", ret); + } else { + char obuf1[2*14+1], obuf2[2*14+1]; -static void at76_work_reset_device(struct work_struct *work) -{ - struct at76_priv *dev = container_of(work, struct at76_priv, - work_reset_device); + at76_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 = at76_start_scan(dev, 0, 1)) < 0) { + err("%s: %s: start_scan (ANY) failed with %d", + dev->netdev->name, __FUNCTION__, ret); + } + at76_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; - down(&dev->sem); - usb_reset_device(dev->udev); - dev->istate = WAIT_FOR_DISCONNECT; - up(&dev->sem); -} + case 2: + if ((ret = at76_start_scan(dev, 1, 1)) < 0) { + err("%s: %s: start_scan (SSID) failed with %d", + dev->netdev->name, __FUNCTION__, ret); + } + at76_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; -static void at76_work_restart(struct work_struct *work) -{ - struct at76_priv *dev = container_of(work, struct at76_priv, - work_restart); + case 3: + dev->scan_state = SCAN_COMPLETED; + /* report the end of scan to user space */ + at76_iwevent_scan_complete(dev->netdev); + dev->istate = JOINING; + /* call join_bss immediately after + re-run of all other threads in at76_devent */ + schedule_work(&dev->work_join); + break; - down(&dev->sem); + default: + err("unexpected dev->scan_runs %d", dev->scan_runs); + } + dev->scan_runs++; + } else { - at76_startup_device(dev); - at76_set_monitor_mode(dev); + at76_assert(dev->istate == MONITORING); + at76_dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE: restart scan", + dev->netdev->name); + at76_start_scan(dev, 0, 0); + at76_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); + } - 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) { - dev->istate = SCANNING; - schedule_work(&dev->work_scan); } else { - dev->istate = MONITORING; - at76_start_scan(dev, 0, 0); - at76_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); - } - - up(&dev->sem); -} - -static void at76_work_scan(struct work_struct *work) -{ - struct at76_priv *dev = container_of(work, struct at76_priv, - work_scan); - int ret; - - down(&dev->sem); - - at76_assert(dev->istate == SCANNING); - /* only clear the bss list when a scan is actively initiated, - * otherwise simply rely on at76_bss_list_timeout */ - if (dev->scan_state == SCAN_IN_PROGRESS) - at76_free_bss_list(dev); + if ((status != CMD_STATUS_IN_PROGRESS) && + (status != CMD_STATUS_IDLE)) + err("%s: %s: Bad scan status: %s", + dev->netdev->name, __FUNCTION__, + at76_get_cmd_status_string(status)); - dev->scan_runs = 2; - if ((ret = at76_start_scan(dev, 0, 1)) < 0) { - err("%s: %s: start_scan failed with %d", - dev->netdev->name, __FUNCTION__, ret); - } else { + /* the first cmd status after scan start is always a IDLE -> + start the timer to poll again until COMPLETED */ at76_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); } - - up(&dev->sem); } -static void at76_work_set_promisc(struct work_struct *work) + +/* the deferred procedure called from at76_devent() */ +static void at76_handle_mgmt_timeout(struct at76_priv *dev) { - struct at76_priv *dev = container_of(work, struct at76_priv, - work_set_promisc); - int ret = 0; + if ((dev->istate != SCANNING && dev->istate != MONITORING) || + (at76_debug & DBG_MGMT_TIMER)) + /* this is normal behavior in states MONITORING, SCANNING ... */ + at76_dbg(DBG_PROGRESS, "%s: timeout, state %d", dev->netdev->name, + dev->istate); - down(&dev->sem); + switch (dev->istate) { - 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] = dev->promisc ? 1 : 0; - ret = at76_set_mib(dev, &dev->mib_buf); - if (ret < 0) { - err("%s: set_mib (promiscuous_mode) failed: %d", - dev->netdev->name, ret); - } + case MONITORING: + case SCANNING: + at76_handle_mgmt_timeout_scan(dev); + break; - up(&dev->sem); -} + case JOINING: + at76_assert(0); + break; -static void at76_work_start_ibss(struct work_struct *work) -{ - struct at76_priv *dev = container_of(work, struct at76_priv, - work_start_ibss); - int ret; + 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 ad-hoc 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); + at76_iwevent_bss_disconnect(dev->netdev); + dev->istate = SCANNING; + schedule_work(&dev->work_scan); + } + break; - down(&dev->sem); + case AUTHENTICATING: + if (dev->retries-- >= 0) { + at76_auth_req(dev, dev->curr_bss, 1, NULL); + at76_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 */ + dev->istate = JOINING; + schedule_work(&dev->work_join); + } + break; - at76_assert(dev->istate == STARTIBSS); - ret = at76_start_ibss(dev); - if (ret < 0) { - err("%s: start_ibss failed: %d", dev->netdev->name, ret); - goto end_startibss; - } + case ASSOCIATING: + if (dev->retries-- >= 0) { + at76_assoc_req(dev, dev->curr_bss); + at76_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 ... */ - ret = at76_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; - } + /* try to get next matching BSS */ + dev->istate = JOINING; + schedule_work(&dev->work_join); + } + break; - ret = at76_get_current_bssid(dev); - if (ret < 0) - goto end_startibss; - - ret = at76_get_current_channel(dev); - if (ret < 0) - goto end_startibss; + case REASSOCIATING: + if (dev->retries-- >= 0) + at76_reassoc_req(dev, dev->curr_bss, dev->new_bss); + else { + /* we disassociate from the curr_bss and + scan again ... */ + dev->istate = DISASSOCIATING; + dev->retries = DISASSOC_RETRIES; + at76_disassoc_req(dev, dev->curr_bss); + } + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", + __FUNCTION__, __LINE__); + mod_timer(&dev->mgmt_timer, jiffies + HZ); + break; - /* 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 = at76_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; - } + case DISASSOCIATING: + if (dev->retries-- >= 0) { + at76_disassoc_req(dev, dev->curr_bss); + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", + __FUNCTION__, __LINE__); + mod_timer(&dev->mgmt_timer, jiffies + HZ); + } else { + /* we scan again ... */ + dev->istate = SCANNING; + schedule_work(&dev->work_scan); + } + break; - netif_carrier_on(dev->netdev); - netif_start_queue(dev->netdev); + case INIT: + break; - end_startibss: - up(&dev->sem); + default: + at76_assert(0); + } } -static void at76_work_submit_rx(struct work_struct *work) + +/* Called after successful association */ +static void at76_work_assoc_done(struct work_struct *work) { struct at76_priv *dev = container_of(work, struct at76_priv, - work_submit_rx); + work_assoc_done); down(&dev->sem); - at76_submit_rx_urb(dev); - up(&dev->sem); -} -static int at76_essid_matched(struct at76_priv *dev, struct bss_info *ptr) -{ - /* common criteria for both modi */ + at76_assert(dev->istate == ASSOCIATING || dev->istate == REASSOCIATING); + if (dev->iw_mode == IW_MODE_INFRA) { + at76_assert(dev->curr_bss != NULL); + if (dev->curr_bss != NULL && dev->pm_mode != AT76_PM_OFF) { + /* calculate the listen interval in units of + beacon intervals of the curr_bss */ + u32 pm_period_beacon = (dev->pm_period >> 10) / + dev->curr_bss->beacon_interval; - int ret = (dev->essid_size == 0 /* ANY ssid */ || - (dev->essid_size == ptr->ssid_len && - !memcmp(dev->essid, ptr->ssid, ptr->ssid_len))); - if (!ret) - at76_dbg(DBG_BSS_MATCH, "%s bss table entry %p: essid didn't match", - dev->netdev->name, ptr); - return ret; -} + pm_period_beacon = max(pm_period_beacon, 2u); + pm_period_beacon = min(pm_period_beacon, 0xffffu); -static inline int at76_mode_matched(struct at76_priv *dev, struct bss_info *ptr) -{ - int ret; + at76_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, pm_period_beacon); - if (dev->iw_mode == IW_MODE_ADHOC) - ret = ptr->capa & WLAN_CAPABILITY_IBSS; - else - ret = ptr->capa & WLAN_CAPABILITY_ESS; - if (!ret) - at76_dbg(DBG_BSS_MATCH, "%s bss table entry %p: mode didn't match", - dev->netdev->name, ptr); - return ret; -} + at76_set_associd(dev, dev->curr_bss->assoc_id); + at76_set_listen_interval(dev, (u16)pm_period_beacon); +#ifdef DEBUG + at76_dump_mib_mac(dev); + at76_dump_mib_mac_mgmt(dev); +#endif + } + } + at76_set_pm_mode(dev); -static int at76_rates_matched(struct at76_priv *dev, struct bss_info *ptr) -{ - int i; - u8 *rate; + netif_carrier_on(dev->netdev); + netif_wake_queue(dev->netdev); + dev->istate = CONNECTED; + at76_iwevent_bss_connect(dev->netdev, dev->curr_bss->bssid); + at76_dbg(DBG_PROGRESS, "%s: connected to BSSID %s", + dev->netdev->name, mac2str(dev->curr_bss->bssid)); - 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])) { - at76_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 & WLAN_CAPABILITY_SHORT_PREAMBLE)) { - at76_dbg(DBG_BSS_MATCH, "%s: %p does not support short preamble", - dev->netdev->name, ptr); - return 0; - } else - return 1; + up(&dev->sem); } -static inline int at76_wep_matched(struct at76_priv *dev, struct bss_info *ptr) -{ - if (!dev->wep_enabled && ptr->capa & WLAN_CAPABILITY_PRIVACY) { - /* we have disabled WEP, but the BSS signals privacy */ - at76_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 at76_bssid_matched(struct at76_priv *dev, struct bss_info *ptr) +static void at76_delete_device(struct at76_priv *dev) { - if (!dev->wanted_bssid_valid || - !compare_ether_addr(ptr->bssid, dev->wanted_bssid)) { - return 1; - } else { - at76_dbg(DBG_BSS_MATCH, "%s: requested bssid - %s does not match", - dev->netdev->name, mac2str(dev->wanted_bssid)); - at76_dbg(DBG_BSS_MATCH, " AP bssid - %s of bss table entry %p", - mac2str(ptr->bssid), ptr); - return 0; - } -} + int i; -static void at76_dump_bss_table(struct at76_priv *dev) -{ - struct bss_info *ptr; - unsigned long flags; - struct list_head *lptr; - char obuf_s[3*32]; + if (!dev) + return; - spin_lock_irqsave(&dev->bss_list_spinlock, flags); + /* signal to _stop() that the device is gone */ + dev->device_unplugged = 1; - pr_debug("%s BSS table (curr=%p, new=%p):", dev->netdev->name, - dev->curr_bss, dev->new_bss); + at76_dbg(DBG_PROC_ENTRY, "%s: ENTER",__FUNCTION__); - list_for_each(lptr, &dev->bss_list) { - ptr = list_entry(lptr, struct bss_info, list); - pr_debug("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'), - ptr->capa, - hex2str(obuf_s, ptr->rates, - min(sizeof(obuf_s) / 3, - (size_t) ptr->rates_len), ' '), - ptr->rssi, ptr->link_qual, ptr->noise_level); + if (dev->netdev_registered) { + unregister_netdev(dev->netdev); } - spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); -} -/** - * at76_match_bss - try to find a matching bss in dev->bss - * - * last - last bss tried - * - * last == NULL signals a new round starting with dev->bss_list.next - * this function must be called inside an acquired dev->bss_list_spinlock - * otherwise the timeout on bss may remove the newly chosen entry - */ -static struct bss_info *at76_match_bss(struct at76_priv *dev, - struct bss_info *last) -{ - struct bss_info *ptr = NULL; - struct list_head *curr; + usb_put_dev(dev->udev); - curr = last != NULL ? last->list.next : dev->bss_list.next; - while (curr != &dev->bss_list) { - ptr = list_entry(curr, struct bss_info, list); - if (at76_essid_matched(dev, ptr) && - at76_mode_matched(dev, ptr) && - at76_wep_matched(dev, ptr) && - at76_rates_matched(dev, ptr) && - at76_bssid_matched(dev, ptr)) - break; - curr = curr->next; + /* 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); } - if (curr == &dev->bss_list) - ptr = NULL; - /* otherwise ptr points to the struct bss_info we have chosen */ + at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __FUNCTION__); - at76_dbg(DBG_BSS_TABLE, "%s %s: returned %p", dev->netdev->name, - __FUNCTION__, ptr); - return ptr; -} + if (dev->rx_skb != NULL) + kfree_skb(dev->rx_skb); + at76_free_bss_list(dev); + del_timer_sync(&dev->bss_list_timer); -/* we got an association response */ -static void at76_rx_mgmt_assoc(struct at76_priv *dev, struct at76_rx_buffer *buf) -{ - struct ieee80211_assoc_response *resp = - (struct ieee80211_assoc_response *)buf->packet; - struct ieee80211_hdr_3addr *mgmt = &resp->header; - u16 assoc_id = le16_to_cpu(resp->aid); - u16 status = le16_to_cpu(resp->status); - u16 capa = le16_to_cpu(resp->capability); - at76_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->info_element->data, - min((size_t)resp->info_element->len, (sizeof(dev->obuf)-1)/2), '\0')); - if (dev->istate == ASSOCIATING) { - at76_assert(dev->curr_bss != NULL); - if (dev->curr_bss == NULL) - return; + if (dev->istate == CONNECTED) { + at76_iwevent_bss_disconnect(dev->netdev); + } - if (status == WLAN_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; - schedule_work(&dev->work_assoc_done); - } else { - dev->istate = JOINING; - schedule_work(&dev->work_join); + 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; } - del_timer_sync(&dev->mgmt_timer); - } else { - info("%s: AssocResp in state %d ignored", - dev->netdev->name, dev->istate); - } + at76_dbg(DBG_PROC_ENTRY, "%s: before freeing dev/netdev", __FUNCTION__); + free_netdev(dev->netdev); /* dev is in netdev */ + + at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __FUNCTION__); } -static void at76_rx_mgmt_reassoc(struct at76_priv *dev, - struct at76_rx_buffer *buf) +static int at76_alloc_urbs(struct at76_priv *dev) { - struct ieee80211_assoc_response *resp = - (struct ieee80211_assoc_response *)buf->packet; - struct ieee80211_hdr_3addr *mgmt = &resp->header; - unsigned long flags; - u16 capa = le16_to_cpu(resp->capability); - u16 status = le16_to_cpu(resp->status); - u16 assoc_id = le16_to_cpu(resp->aid); + struct usb_interface *interface = dev->interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_device *udev = dev->udev; + int i, buffer_size; + struct usb_host_interface *iface_desc; - at76_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->info_element->data, - min((size_t) resp->info_element->len, - (sizeof(dev->obuf) - 1) / 2), '\0')); - if (dev->istate == REASSOCIATING) { - at76_assert(dev->new_bss != NULL); - if (dev->new_bss == NULL) - return; + at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __FUNCTION__); - if (status == WLAN_STATUS_SUCCESS) { - struct bss_info *bptr = dev->new_bss; - bptr->assoc_id = assoc_id; - dev->istate = CONNECTED; + at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __FUNCTION__, + interface->altsetting[0].desc.bNumEndpoints); - at76_iwevent_bss_connect(dev->netdev, bptr->bssid); + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; - 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); + at76_dbg(DBG_URB, "%s: %d. endpoint: addr x%x attr x%x", + __FUNCTION__, + i, endpoint->bEndpointAddress, endpoint->bmAttributes); - /* 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; - at76_dbg(DBG_PROGRESS, "%s: reassociated to BSSID %s", - dev->netdev->name, mac2str(dev->bssid)); - schedule_work(&dev->work_assoc_done); - } else { - del_timer_sync(&dev->mgmt_timer); - dev->istate = JOINING; - schedule_work(&dev->work_join); + if ((endpoint->bEndpointAddress & 0x80) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found a bulk in endpoint */ + + dev->read_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->read_urb) { + err("No free urbs available"); + return -ENOMEM; + } + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; } - } else { - info("%s: ReAssocResp in state %d ignored", - dev->netdev->name, dev->istate); + + if (((endpoint->bEndpointAddress & 0x80) == 0x00) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found a bulk out endpoint */ + dev->write_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->write_urb) { + err("no free urbs available"); + return -ENOMEM; + } + buffer_size = sizeof(struct at76_tx_buffer) + + MAX_PADDING_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 -ENOMEM; + } + usb_fill_bulk_urb(dev->write_urb, udev, + usb_sndbulkpipe(udev, + endpoint->bEndpointAddress), + dev->bulk_out_buffer, buffer_size, + at76_write_bulk_callback, dev); + } + } + + dev->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->ctrl_urb) { + err("no free urbs available"); + return -ENOMEM; + } + dev->ctrl_buffer = kmalloc(1024, GFP_KERNEL); + if (!dev->ctrl_buffer) { + err("couldn't allocate ctrl_buffer"); + return -ENOMEM; } + + at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __FUNCTION__); + + return 0; } -static void at76_rx_mgmt_disassoc(struct at76_priv *dev, - struct at76_rx_buffer *buf) +/* we only store the new mac address in netdev struct, + it gets set when the netdev is opened. */ +static int at76_set_mac_address(struct net_device *netdev, void *addr) { - struct ieee80211_disassoc *resp = - (struct ieee80211_disassoc *)buf->packet; - struct ieee80211_hdr_3addr *mgmt = &resp->header; + struct sockaddr *mac = addr; + memcpy(netdev->dev_addr, mac->sa_data, ETH_ALEN); + return 1; +} - at76_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; - at76_assert(dev->curr_bss != NULL); - if (dev->curr_bss == NULL) - return; - if (dev->istate == REASSOCIATING) { - at76_assert(dev->new_bss != NULL); - if (dev->new_bss == NULL) - return; - } - if (!compare_ether_addr(mgmt->addr3, dev->curr_bss->bssid) && - (!compare_ether_addr(dev->netdev->dev_addr, mgmt->addr1) || - is_broadcast_ether_addr(mgmt->addr1))) { - /* 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); - at76_iwevent_bss_disconnect(dev->netdev); - } - del_timer_sync(&dev->mgmt_timer); - dev->istate = JOINING; - schedule_work(&dev->work_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 */ +static struct net_device_stats *at76_get_stats(struct net_device *netdev) +{ + struct at76_priv *dev = netdev_priv(netdev); + return &dev->stats; } -static void at76_rx_mgmt_auth(struct at76_priv *dev, struct at76_rx_buffer *buf) +static struct iw_statistics *at76_get_wireless_stats(struct net_device *netdev) { - struct ieee80211_auth *resp = (struct ieee80211_auth *)buf->packet; - struct ieee80211_hdr_3addr *mgmt = &resp->header; - int seq_nr = le16_to_cpu(resp->transaction); - int alg = le16_to_cpu(resp->algorithm); - int status = le16_to_cpu(resp->status); + struct at76_priv *dev = netdev_priv(netdev); - at76_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), ':')); + at76_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); - if (alg == WLAN_AUTH_SHARED_KEY && seq_nr == 2) { - at76_dbg(DBG_RX_MGMT, "%s: AuthFrame challenge %s ...", - dev->netdev->name, - hex2str(dev->obuf, resp->info_element, - 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; - } + return &dev->wstats; +} - at76_assert(dev->curr_bss != NULL); - if (dev->curr_bss == NULL) - return; - if (!compare_ether_addr(mgmt->addr3, dev->curr_bss->bssid) && - !compare_ether_addr(dev->netdev->dev_addr, mgmt->addr1)) { - /* this is a AuthFrame from the BSS we are connected or - trying to connect to, directed to us */ - if (status != WLAN_STATUS_SUCCESS) { - del_timer_sync(&dev->mgmt_timer); - /* try to join next bss */ - dev->istate = JOINING; - schedule_work(&dev->work_join); - return; - } +static void at76_set_multicast(struct net_device *netdev) +{ + struct at76_priv *dev = netdev_priv(netdev); + int promisc; - if (dev->auth_mode == WLAN_AUTH_OPEN || seq_nr == 4) { - dev->retries = ASSOC_RETRIES; - dev->istate = ASSOCIATING; - at76_assoc_req(dev, dev->curr_bss); - at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", - __FUNCTION__, __LINE__); - mod_timer(&dev->mgmt_timer, jiffies + HZ); - return; - } - - at76_assert(seq_nr == 2); - at76_auth_req(dev, dev->curr_bss, seq_nr + 1, resp->info_element); - at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", - __FUNCTION__, __LINE__); - mod_timer(&dev->mgmt_timer, jiffies + HZ); + promisc = ((netdev->flags & IFF_PROMISC) != 0); + if (promisc != dev->promisc) { + /* grmbl. This gets called in interrupt. */ + dev->promisc = promisc; + schedule_work(&dev->work_set_promisc); } - /* else: ignore AuthFrames to other recipients */ } -static void at76_rx_mgmt_deauth(struct at76_priv *dev, struct at76_rx_buffer *buf) +/******************************************************************************* + * at76_priv implementations of iw_handler functions: + */ +static int at76_iw_handler_commit(struct net_device *netdev, + struct iw_request_info *info, + void *null, char *extra) { - struct ieee80211_disassoc *resp = - (struct ieee80211_disassoc *)buf->packet; - struct ieee80211_hdr_3addr *mgmt = &resp->header; + struct at76_priv *dev = netdev_priv(netdev); + unsigned long flags; + at76_dbg(DBG_IOCTL, "%s %s: restarting the device", netdev->name, + __FUNCTION__); - at76_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) { - at76_assert(dev->curr_bss != NULL); - if (dev->curr_bss == NULL) - return; + /* TODO: stop any pending tx bulk urb */ + if (dev->istate != INIT) { + dev->istate = INIT; + /* stop pending management stuff */ + del_timer_sync(&dev->mgmt_timer); - if (!compare_ether_addr(mgmt->addr3, dev->curr_bss->bssid) && - (!compare_ether_addr(dev->netdev->dev_addr, mgmt->addr1) || - is_broadcast_ether_addr(mgmt->addr1))) { - /* this is a DeAuth from the BSS we are connected or - trying to connect to, directed to us or broadcasted */ - if (dev->istate == CONNECTED) { - at76_iwevent_bss_disconnect(dev->netdev); - } - dev->istate = JOINING; - schedule_work(&dev->work_join); - 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; } - /* 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); + 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 void at76_rx_mgmt_beacon(struct at76_priv *dev, struct at76_rx_buffer *buf) +static int at76_iw_handler_get_name(struct net_device *netdev, + struct iw_request_info *info, + char *name, char *extra) { - /* beacon content */ - struct ieee80211_beacon *bdata = (struct ieee80211_beacon *)buf->packet; - struct ieee80211_hdr_3addr *mgmt = &bdata->header; + strcpy(name, "IEEE 802.11b"); + at76_dbg(DBG_IOCTL, "%s: SIOCGIWNAME - name %s", netdev->name, name); + return 0; +} - /* length of var length beacon parameters */ - int varpar_len = min_t(int, le16_to_cpu(buf->wlength) - - sizeof(struct ieee80211_beacon), - 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 ieee80211_info_element *tlv; - int have_ssid = 0; - int have_rates = 0; - int have_channel = 0; - int keep_going = 1; - unsigned long flags; +static int at76_iw_handler_set_freq(struct net_device *netdev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); + int chan = -1; + int ret = -EIWCOMMIT; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - freq.m %d freq.e %d", netdev->name, + freq->m, freq->e); - 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 */ - at76_assert(dev->curr_bss != NULL); - if (dev->curr_bss == NULL) - goto rx_mgmt_beacon_end; - if (!compare_ether_addr(dev->curr_bss->bssid, mgmt->addr3)) { - mod_timer(&dev->mgmt_timer, jiffies+BEACON_TIMEOUT*HZ); - dev->curr_bss->rssi = buf->rssi; - dev->beacons_received++; - goto rx_mgmt_beacon_end; - } - } + 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; - /* look if we have this BSS already in the list */ - match = NULL; + for (i = 0; i < (6 - freq->e); i++) { + mult *= 10; + } - 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 (!compare_ether_addr(bss_ptr->bssid, mgmt->addr3)) { - match = bss_ptr; - break; - } + for (i = 0; i < NUM_CHANNELS; i++) { + if (freq->m == (channel_frequency[i] * mult)) + chan = i + 1; } } - if (match == NULL) { - /* haven't found the bss in the list */ - if ((match=kmalloc(sizeof(struct bss_info), GFP_ATOMIC)) == NULL) { - at76_dbg(DBG_BSS_TABLE, "%s: cannot kmalloc new bss info (%zd byte)", - dev->netdev->name, sizeof(struct bss_info)); - goto rx_mgmt_beacon_end; + 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; } - 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); + if (ret == -EIWCOMMIT) { + dev->channel = chan; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - ch %d", netdev->name, chan); + } - /* while beacon_interval is not (!) */ - match->beacon_interval = le16_to_cpu(bdata->beacon_interval); + return ret; +} - match->rssi = buf->rssi; - match->link_qual = buf->link_quality; - match->noise_level = buf->noise_level; - memcpy(match->bssid, mgmt->addr3, ETH_ALEN); - at76_dbg(DBG_RX_BEACON, "%s: bssid %s", dev->netdev->name, - mac2str(match->bssid)); - tlv = bdata->info_element; +static int at76_iw_handler_get_freq(struct net_device *netdev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); - /* This routine steps through the bdata->data array to tries to 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 types although MFIE_TYPE_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 optimizations. */ + freq->m = dev->channel; + freq->e = 0; - while (keep_going && - ((&tlv->data[tlv->len] - (u8 *)bdata->info_element) <= varpar_len)) { + if (dev->channel) { + at76_dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - freq %ld x 10e%d", + netdev->name, channel_frequency[dev->channel - 1], 6); + } + at76_dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - ch %d", netdev->name, dev->channel); - switch (tlv->id) { + return 0; +} - case MFIE_TYPE_SSID: - len = min_t(int, IW_ESSID_MAX_SIZE, tlv->len); - if (!have_ssid && ((new_entry) || - !at76_is_cloaked_ssid(tlv->data, 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, tlv->data, len); - match->ssid[len] = '\0'; /* terminate the - string for - printing */ - at76_dbg(DBG_RX_BEACON, "%s: SSID - %s", - dev->netdev->name, match->ssid); - } - have_ssid = 1; - break; +static int at76_iw_handler_set_mode(struct net_device *netdev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); + int ret = -EIWCOMMIT; - case MFIE_TYPE_RATES: - if (!have_rates) { - match->rates_len = - min_t(int, sizeof(match->rates), tlv->len); - memcpy(match->rates, tlv->data, - match->rates_len); - have_rates = 1; - at76_dbg(DBG_RX_BEACON, - "%s: SUPPORTED RATES %s", - dev->netdev->name, - hex2str(dev->obuf, tlv->data, - min_t(int, (sizeof(dev->obuf)-1)/2, - tlv->len), '\0')); - } - break; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWMODE - %d", netdev->name, *mode); - case MFIE_TYPE_DS_SET: - if (!have_channel) { - match->channel = tlv->data[0]; - have_channel = 1; - at76_dbg(DBG_RX_BEACON, "%s: CHANNEL - %d", - dev->netdev->name, match->channel); - } - break; - - case MFIE_TYPE_CF_SET: - case MFIE_TYPE_TIM: - case MFIE_TYPE_IBSS_SET: - default: - at76_dbg(DBG_RX_BEACON, "%s: beacon IE id %d len %d %s", - dev->netdev->name, tlv->id, tlv->len, - hex2str(dev->obuf, tlv->data, - min_t(int, (sizeof(dev->obuf)-1)/2, - tlv->len), '\0')); - break; - } + if ((*mode != IW_MODE_ADHOC) && (*mode != IW_MODE_INFRA) && + (*mode != IW_MODE_MONITOR)) { + ret = -EINVAL; + } else { + dev->iw_mode = *mode; + if( dev->iw_mode != IW_MODE_INFRA) + dev->pm_mode = AT76_PM_OFF; + } + return ret; +} - /* advance to the next informational element */ - next_ie(&tlv); - /* Optimization: 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 */ - } +static int at76_iw_handler_get_mode(struct net_device *netdev, + struct iw_request_info *info, + __u32 * mode, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); - at76_dbg(DBG_RX_BEACON, "%s: Finished processing beacon data", - dev->netdev->name); + *mode = dev->iw_mode; - match->last_rx = jiffies; /* record last rx of beacon */ + at76_dbg(DBG_IOCTL, "%s: SIOCGIWMODE - %d", netdev->name, *mode); -rx_mgmt_beacon_end: - spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); + return 0; } -/* calc the link level from a given rx_buffer */ -static void at76_calc_level(struct at76_priv *dev, struct at76_rx_buffer *buf, - struct iw_quality *qual) +static int at76_iw_handler_get_range(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) { - int max_rssi = 42; /* just a guess for now, might be different for other chips */ - - qual->level = (buf->rssi * 100 / max_rssi); - if (qual->level > 100) - qual->level = 100; - qual->updated |= IW_QUAL_LEVEL_UPDATED; -} + /* inspired by atmel.c */ + struct at76_priv *dev = netdev_priv(netdev); + struct iw_range *range = (struct iw_range *)extra; + int i; + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); -/* calc the link quality from a given rx_buffer */ -static void at76_calc_qual(struct at76_priv *dev, struct at76_rx_buffer *buf, struct iw_quality* qual) -{ - if ((dev->board_type == BOARDTYPE_503_INTERSIL_3861) || - (dev->board_type == BOARDTYPE_503_INTERSIL_3863)) { - qual->qual = buf->link_quality; - } else { - unsigned long msec; + /* TODO: range->throughput = xxxxxx; */ - /* Update qual at most once a second */ - msec = jiffies_to_msecs(jiffies) - dev->beacons_last_qual; - if (msec < 1000) - return; + range->min_nwid = 0x0000; + range->max_nwid = 0x0000; - qual->qual = qual->level * dev->beacons_received * - dev->beacon_period / msec; + /* this driver doesn't maintain sensitivity information */ + range->sensitivity = 0; - dev->beacons_last_qual = jiffies_to_msecs(jiffies); - dev->beacons_received = 0; - } - qual->qual = (qual->qual > 100) ? 100 : qual->qual; - qual->updated |= IW_QUAL_QUAL_UPDATED; -} + range->max_qual.qual = 100; + range->max_qual.level = 100; + range->max_qual.noise = 0; + range->max_qual.updated = IW_QUAL_NOISE_INVALID; -/* calc the noise quality from a given rx_buffer */ -static void at76_calc_noise(struct at76_priv *dev, struct at76_rx_buffer *buf, - struct iw_quality *qual) -{ - qual->noise = 0; - qual->updated |= IW_QUAL_NOISE_INVALID; -} + range->avg_qual.qual = 50; + range->avg_qual.level = 50; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_NOISE_INVALID; -static void at76_update_wstats(struct at76_priv *dev, struct at76_rx_buffer *buf) -{ - struct iw_quality *qual = &dev->wstats.qual; + range->bitrate[0] = 1000000; + range->bitrate[1] = 2000000; + range->bitrate[2] = 5500000; + range->bitrate[3] = 11000000; + range->num_bitrates = 4; - if (buf->rssi && dev->istate == CONNECTED) { - qual->updated = 0; - at76_calc_level(dev, buf, qual); - at76_calc_qual(dev, buf, qual); - at76_calc_noise(dev, buf, qual); - } else { - qual->qual = 0; - qual->level = 0; - qual->noise = 0; - qual->updated = IW_QUAL_ALL_INVALID; - } -} + range->min_rts = 0; + range->max_rts = MAX_RTS_THRESHOLD; -static void at76_rx_mgmt(struct at76_priv *dev, struct at76_rx_buffer *buf) -{ - struct ieee80211_hdr_3addr *mgmt = - (struct ieee80211_hdr_3addr *)buf->packet; - u16 subtype = le16_to_cpu(mgmt->frame_ctl) & IEEE80211_FCTL_STYPE; + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; - /* update wstats */ - if (dev->istate != INIT && dev->istate != SCANNING) { - /* jal: this is a dirty hack needed by Tim in ad-hoc mode */ - if (dev->iw_mode == IW_MODE_ADHOC || (dev->curr_bss != NULL && - !compare_ether_addr(mgmt->addr3, dev->curr_bss->bssid))) { - /* 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 */ - at76_update_wstats(dev, buf); - } - } + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_ON; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_ALL_R; - at76_dbg(DBG_RX_MGMT_CONTENT, "%s rx mgmt subtype x%x %s", - dev->netdev->name, subtype, - hex2str(dev->obuf, mgmt, - min((sizeof(dev->obuf) - 1) / 2, - (size_t) le16_to_cpu(buf->wlength)), '\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 = WEP_KEYS; - switch (subtype) { - case IEEE80211_STYPE_BEACON: - case IEEE80211_STYPE_PROBE_RESP: - at76_rx_mgmt_beacon(dev, buf); - break; + /* 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; - case IEEE80211_STYPE_ASSOC_RESP: - at76_rx_mgmt_assoc(dev, buf); - break; + range->we_version_source = WIRELESS_EXT; + range->we_version_compiled = WIRELESS_EXT; - case IEEE80211_STYPE_REASSOC_RESP: - at76_rx_mgmt_reassoc(dev, buf); - break; + /* 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; - case IEEE80211_STYPE_DISASSOC: - at76_rx_mgmt_disassoc(dev, buf); - break; - case IEEE80211_STYPE_AUTH: - at76_rx_mgmt_auth(dev, buf); - break; + range->num_channels = NUM_CHANNELS; + range->num_frequency = 0; - case IEEE80211_STYPE_DEAUTH: - at76_rx_mgmt_deauth(dev, buf); - break; + for (i = 0; i < NUM_CHANNELS; i++) { + /* test if channel map bit is raised */ + if (dev->domain->channel_map & (0x1 << i)) { + range->num_frequency += 1; - default: - info("%s: mgmt, but not beacon, subtype = %x", - dev->netdev->name, subtype); + range->freq[i].i = i + 1; + range->freq[i].m = channel_frequency[i] * 100000; + range->freq[i].e = 1; /* channel frequency*100000 * 10^1 */ + } } - return; + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRANGE", netdev->name); + + return 0; } -static void at76_dbg_dumpbuf(const char *tag, const u8 *buf, int size) + +static int at76_iw_handler_set_spy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) { - int i; + struct at76_priv *dev = netdev_priv(netdev); + int ret = 0; - if (!at76_debug) - return; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWSPY - number of addresses %d", + netdev->name, data->length); - for (i = 0; i < size; i++) { - if ((i % 8) == 0) { - if (i) - pr_debug("\n"); - pr_debug(__FILE__ ": %s: ", tag); - } - pr_debug("%02x ", buf[i]); - } - pr_debug("\n"); -} + spin_lock_bh(&(dev->spy_spinlock)); + ret = iw_handler_set_spy(dev->netdev, info, (union iwreq_data *)data, + extra); + spin_unlock_bh(&(dev->spy_spinlock)); -/* A short overview on Ethernet-II, 802.2, 802.3 and SNAP - (taken from http://www.geocities.com/billalexander/ethernet.html): + return ret; +} -Ethernet Frame Formats: -Ethernet (a.k.a. Ethernet II) +static int at76_iw_handler_get_spy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ - +---------+---------+---------+---------- - | Dst | Src | Type | Data... - +---------+---------+---------+---------- + struct at76_priv *dev = netdev_priv(netdev); + int ret = 0; - <-- 6 --> <-- 6 --> <-- 2 --> <-46-1500-> + spin_lock_bh(&(dev->spy_spinlock)); + ret = iw_handler_get_spy(dev->netdev, info, + (union iwreq_data *)data, extra); + spin_unlock_bh(&(dev->spy_spinlock)); - Type 0x80 0x00 = TCP/IP - Type 0x06 0x00 = XNS - Type 0x81 0x37 = Novell NetWare + at76_dbg(DBG_IOCTL, "%s: SIOCGIWSPY - number of addresses %d", + netdev->name, data->length); + return ret; +} -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-> +static int at76_iw_handler_set_thrspy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); + int ret; -SNAP (802.3 with 802.2 and SNAP headers) + at76_dbg(DBG_IOCTL, "%s: SIOCSIWTHRSPY - number of addresses %d)", + netdev->name, data->length); - +---------+---------+---------+-------+-------+-------+-----------+---------+----------- - | Dst | Src | Length | 0xAA | 0xAA | 0x03 | Org Code | Type | Data... - +---------+---------+---------+-------+-------+-------+-----------+---------+----------- + spin_lock_bh(&(dev->spy_spinlock)); + ret = iw_handler_set_thrspy(netdev, info, (union iwreq_data *)data, + extra); + spin_unlock_bh(&(dev->spy_spinlock)); - <-- 3 --> <-- 2 --> <-38-1492-> + return ret; +} -*/ -static const u8 snapsig[] = { 0xaa, 0xaa, 0x03 }; -/* RFC 1042 encapsulates Ethernet frames in 802.2 SNAP (0xaa, 0xaa, 0x03) with - * a SNAP OID of 0 (0x00, 0x00, 0x00) */ -static const u8 rfc1042sig[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; -/* 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 at76_ieee80211_to_eth(struct sk_buff *skb, int iw_mode) +static int at76_iw_handler_get_thrspy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) { - struct ieee80211_hdr_3addr *i802_11_hdr; - struct ethhdr *eth_hdr_p; - u8 *src_addr; - u8 *dest_addr; - __be16 proto = 0; - int build_ethhdr = 1; - - i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data; + struct at76_priv *dev = netdev_priv(netdev); + int ret; - dbg("%s: ENTRY skb len %d data %s", __FUNCTION__, - skb->len, hex2str(dev->obuf, skb->data, - min((int)sizeof(dev->obuf) / 3, 64), ' ')); + spin_lock_bh(&(dev->spy_spinlock)); + ret = iw_handler_get_thrspy(netdev, info, (union iwreq_data *)data, + extra); + spin_unlock_bh(&(dev->spy_spinlock)); - skb_pull(skb, sizeof(struct ieee80211_hdr_3addr)); + at76_dbg(DBG_IOCTL, "%s: SIOCGIWTHRSPY - number of addresses %d)", + netdev->name, data->length); - src_addr = iw_mode == IW_MODE_ADHOC ? i802_11_hdr->addr2 - : i802_11_hdr->addr3; - dest_addr = i802_11_hdr->addr1; + return ret; +} - eth_hdr_p = (struct ethhdr *)skb->data; - if (!compare_ether_addr(eth_hdr_p->h_source, src_addr) && - !compare_ether_addr(eth_hdr_p->h_dest, dest_addr)) { - /* 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 = *(__be16 *) (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 = *(__be16 *)(skb->data); - skb_pull(skb, 2); -#endif /* IEEE_STANDARD */ - } +static int at76_iw_handler_set_wap(struct net_device *netdev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); - eth_hdr_p = (struct ethhdr *)(skb->data-sizeof(struct ethhdr)); - skb->mac.raw=(unsigned char *)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; - } + at76_dbg(DBG_IOCTL, "%s: SIOCSIWAP - wap/bssid %s", netdev->name, + mac2str(ap_addr->sa_data)); - 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); + /* if the incoming address == ff:ff:ff:ff:ff:ff, the user has + chosen any or auto AP preference */ + if (is_broadcast_ether_addr(ap_addr->sa_data) + || is_zero_ether_addr(ap_addr->sa_data)) { + dev->wanted_bssid_valid = 0; } 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); + /* user wants to set a preferred AP address */ + dev->wanted_bssid_valid = 1; + memcpy(dev->wanted_bssid, ap_addr->sa_data, ETH_ALEN); } -#ifdef DEBUG - char da[3*ETH_ALEN], sa[3*ETH_ALEN]; -#endif - dbg("%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), ' ')); - + return -EIWCOMMIT; } -/* Adjust the skb to trim the hardware header and CRC, and set up skb->mac, - * skb->protocol, etc. - */ -static void at76_ieee80211_fixup(struct sk_buff *skb, int iw_mode) -{ - struct ieee80211_hdr_3addr *i802_11_hdr; - struct ethhdr *eth_hdr_p; - u8 *src_addr; - u8 *dest_addr; - __be16 proto = 0; - - i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data; - skb_pull(skb, sizeof(struct ieee80211_hdr_3addr)); - src_addr = iw_mode == IW_MODE_ADHOC ? i802_11_hdr->addr2 - : i802_11_hdr->addr3; - dest_addr = i802_11_hdr->addr1; +static int at76_iw_handler_get_wap(struct net_device *netdev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); - skb->mac.raw = (unsigned char *)i802_11_hdr; + ap_addr->sa_family = ARPHRD_ETHER; + memcpy(ap_addr->sa_data, dev->bssid, ETH_ALEN); - eth_hdr_p = (struct ethhdr *)skb->data; - if (!compare_ether_addr(eth_hdr_p->h_source, src_addr) && - !compare_ether_addr(eth_hdr_p->h_dest, dest_addr)) { - /* 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 = *(__be16 *)(skb->data - 2); - } + at76_dbg(DBG_IOCTL, "%s: SIOCGIWAP - wap/bssid %s", netdev->name, + mac2str(ap_addr->sa_data)); - 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 = *(__be16 *)skb->data; - skb_pull(skb, 2); -#endif /* IEEE_STANDARD */ - } + return 0; } -/* 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 *at76_check_for_rx_frags(struct at76_priv *dev) + +static int at76_iw_handler_set_scan(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { - struct sk_buff *skb = dev->rx_skb; - struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data; - struct ieee80211_hdr_3addr *i802_11_hdr = - (struct ieee80211_hdr_3addr *)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); + struct at76_priv *dev = netdev_priv(netdev); + unsigned long flags; + int ret = 0; + struct iw_scan_req *req = NULL; - /* length including the IEEE802.11 header, excl. the trailing FCS, - excl. the struct at76_rx_buffer */ - int length = le16_to_cpu(buf->wlength) - dev->rx_data_fcs_len; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWSCAN", netdev->name); - /* where does the data payload start in skb->data ? */ - u8 *data = (u8 *)i802_11_hdr + sizeof(struct ieee80211_hdr_3addr); + if (!netif_running(netdev)) + return -ENETDOWN; - /* length of payload, excl. the trailing FCS */ - int data_len = length - (data - (u8 *)i802_11_hdr); + /* jal: we don't allow "iwlist ethX scan" while we are + in monitor mode */ + if (dev->iw_mode == IW_MODE_MONITOR) + return -EBUSY; - int i; - struct rx_data_buf *bptr, *optr; - unsigned long oldest = ~0UL; + /* Discard old scan results */ + if ((jiffies - dev->last_scan) > (20 * HZ)) + dev->scan_state = SCAN_IDLE; + dev->last_scan = jiffies; - at76_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')); + /* Initiate a scan command */ + if (dev->scan_state == SCAN_IN_PROGRESS) + return -EBUSY; - at76_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) { - /* make sure data starts in the buffer */ - info("%s: data frame too short", dev->netdev->name); - return NULL; - } - - if (fragnr == 0 && !(frame_ctl & IEEE80211_FCTL_MOREFRAGS)) { - /* unfragmented packet received */ - if (length < rx_copybreak && (skb = dev_alloc_skb(length)) != NULL) { - memcpy(skb_put(skb, length), - dev->rx_skb->data + AT76_RX_HDRLEN, length); - } else { - skb_pull(skb, AT76_RX_HDRLEN); - skb_trim(skb, length); - /* Use a new skb for the next receive */ - dev->rx_skb = NULL; - } + dev->scan_state = SCAN_IN_PROGRESS; - at76_dbg(DBG_RX_FRAGS, "%s: unfragmented", dev->netdev->name); + /* stop pending management stuff */ + del_timer_sync(&(dev->mgmt_timer)); - return skb; + 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); - /* remove the at76_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 */ - - at76_assert(length > AT76_RX_HDRLEN); - skb_pull(skb, AT76_RX_HDRLEN); - /* remove FCS at end */ - skb_trim(skb, length); - - at76_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); + if (netif_running(dev->netdev)) { + /* pause network activity */ + netif_carrier_off(dev->netdev); + netif_stop_queue(dev->netdev); + } + /* Try to do passive or active scan if WE asks as. */ + if (wrqu->data.length + && wrqu->data.length == sizeof(struct iw_scan_req)) { + req = (struct iw_scan_req *)extra; - /* 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 ... */ + 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; - for(i=0,bptr=dev->rx_data,optr=NULL; i < NR_RX_DATA_BUF; i++,bptr++) { - if (bptr->skb != NULL) { - if (!compare_ether_addr(i802_11_hdr->addr2, bptr->sender)) - break; + /* Sanity check values? */ + if (req->min_channel_time > 0) { + if (dev->istate == MONITORING) + dev->monitor_scan_min_time = + req->min_channel_time; else - if (optr == NULL) { - optr = bptr; - oldest = bptr->last_rx; - } else { - if (bptr->last_rx < oldest) - optr = bptr; - } - } else { - optr = bptr; - oldest = 0UL; + dev->scan_min_time = req->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 = req->max_channel_time; } } - if (i < NR_RX_DATA_BUF) { - - at76_dbg(DBG_RX_FRAGS, "%s: %d. cacheentry (seq/frag=%d/%d) " - "matched sender addr", - dev->netdev->name, i, bptr->seqnr, bptr->fragnr); + /* change to scanning state */ + dev->istate = SCANNING; + schedule_work(&dev->work_scan); - /* 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 ??? */ + return ret; +} - /* 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 & - IEEE80211_FCTL_MOREFRAGS)) { - /* this was the last fragment - send it */ - skb = bptr->skb; - bptr->skb = NULL; /* free the entry */ - at76_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 */ - at76_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 */ - at76_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 */ - at76_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 ... */ - at76_dbg(DBG_RX_FRAGS, "%s: no chain for non-first fragment (%d)", - dev->netdev->name, fragnr); - return NULL; - } - at76_assert(optr != NULL); - if (optr == NULL) - return NULL; +static int at76_iw_handler_get_scan(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); + 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; - if (optr->skb != NULL) { - /* swap the skb's */ - skb = optr->skb; - optr->skb = dev->rx_skb; - dev->rx_skb = skb; + at76_dbg(DBG_IOCTL, "%s: SIOCGIWSCAN", netdev->name); - at76_dbg(DBG_RX_FRAGS, "%s: free old contents: sender %s seq/frag %d/%d", - dev->netdev->name, mac2str(optr->sender), - optr->seqnr, optr->fragnr); + if (!iwe) + return -ENOMEM; - } else { - /* take the skb from dev->rx_skb */ - optr->skb = dev->rx_skb; - dev->rx_skb = NULL; /* let at76_submit_rx_urb() allocate a new skb */ + if (dev->scan_state != SCAN_COMPLETED) + /* scan not yet finished */ + return -EAGAIN; - at76_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; + spin_lock_irqsave(&(dev->bss_list_spinlock), flags); - return NULL; - } -} + 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); -/* rx interrupt: we expect the complete data buffer in dev->rx_skb */ -static void at76_rx_data(struct at76_priv *dev) -{ - struct net_device *netdev = dev->netdev; - struct net_device_stats *stats = &dev->stats; - struct sk_buff *skb = dev->rx_skb; - struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data; - struct ieee80211_hdr_3addr *i802_11_hdr; - int length = le16_to_cpu(buf->wlength); + iwe->u.data.length = curr_bss->ssid_len; + iwe->cmd = SIOCGIWESSID; + iwe->u.data.flags = 1; - at76_dbg(DBG_RX_DATA, "%s received data packet:", netdev->name); - if (at76_debug & DBG_RX_DATA) { - at76_dbg_dumpbuf(" rxhdr", skb->data, AT76_RX_HDRLEN); - } - if (at76_debug & DBG_RX_DATA_CONTENT) - at76_dbg_dumpbuf("packet", skb->data + AT76_RX_HDRLEN, length); + curr_pos = iwe_stream_add_point(curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, curr_bss->ssid); - if ((skb = at76_check_for_rx_frags(dev)) == NULL) - return; + iwe->cmd = SIOCGIWMODE; + iwe->u.mode = (curr_bss->capa & WLAN_CAPABILITY_IBSS) ? + IW_MODE_ADHOC : + (curr_bss->capa & WLAN_CAPABILITY_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); - /* if an skb is returned, the at76_rx_buffer and the FCS is already removed */ - i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data; + 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); - skb->dev = netdev; - skb->ip_summed = CHECKSUM_NONE; /* TODO: should check CRC */ + iwe->cmd = SIOCGIWENCODE; + if (curr_bss->capa & WLAN_CAPABILITY_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); - if (i802_11_hdr->addr1[0] & 1) { - if (!compare_ether_addr(i802_11_hdr->addr1, netdev->broadcast)) - skb->pkt_type = PACKET_BROADCAST; - else - skb->pkt_type = PACKET_MULTICAST; - } else if (compare_ether_addr(i802_11_hdr->addr1, netdev->dev_addr)) { - skb->pkt_type = PACKET_OTHERHOST; - } + /* Add quality statistics */ + iwe->cmd = IWEVQUAL; + iwe->u.qual.noise = 0; + iwe->u.qual.updated = + IW_QUAL_NOISE_INVALID | IW_QUAL_LEVEL_UPDATED; + iwe->u.qual.level = (curr_bss->rssi * 100 / 42); + if (iwe->u.qual.level > 100) + iwe->u.qual.level = 100; + if ((dev->board_type == BOARDTYPE_503_INTERSIL_3861) || + (dev->board_type == BOARDTYPE_503_INTERSIL_3863)) { + iwe->u.qual.qual = curr_bss->link_qual; + } else { + iwe->u.qual.qual = 0; + iwe->u.qual.updated |= IW_QUAL_QUAL_INVALID; + } + /* Add new value to event */ + curr_pos = iwe_stream_add_event(curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + IW_EV_QUAL_LEN); - if (netdev->type == ARPHRD_ETHER) { - at76_ieee80211_to_eth(skb, dev->iw_mode); - } else { - at76_ieee80211_fixup(skb, dev->iw_mode); - } + /* Rate : stuffing multiple values in a single event require a bit + * more of magic - Jean II */ + curr_val = curr_pos + IW_EV_LCP_LEN; - netdev->last_rx = jiffies; - netif_rx(skb); - stats->rx_packets++; - stats->rx_bytes += length; + 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); + } - return; -} + /* Check if we added any event */ + if ((curr_val - curr_pos) > IW_EV_LCP_LEN) + curr_pos = curr_val; -static int at76_submit_rx_urb(struct at76_priv *dev) -{ - int ret, size; - struct sk_buff *skb = dev->rx_skb; + /* more information may be sent back using IWECUSTOM */ - if (dev->read_urb == NULL) { - err("%s: dev->read_urb is NULL", __FUNCTION__); - return -EFAULT; } - if (skb == NULL) { - skb = dev_alloc_skb(sizeof(struct at76_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); - } + spin_unlock_irqrestore(&(dev->bss_list_spinlock), flags); - size = skb_tailroom(skb); - usb_fill_bulk_urb(dev->read_urb, dev->udev, - usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), - skb_put(skb, size), size, - at76_read_bulk_callback, dev); - ret = usb_submit_urb(dev->read_urb, GFP_ATOMIC); - if (ret < 0) { - if (ret == -ENODEV) - at76_dbg(DBG_DEVSTART, "usb_submit_urb returned -ENODEV"); - else - err("%s: rx, usb_submit_urb failed: %d", dev->netdev->name, ret); - } + data->length = (curr_pos - extra); + data->flags = 0; -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) - schedule_work(&dev->work_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; + kfree(iwe); + return 0; } -/* 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 at76_read_bulk_callback(struct urb *urb) + +static int at76_iw_handler_set_essid(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) { - struct at76_priv *priv = urb->context; + struct at76_priv *dev = netdev_priv(netdev); - priv->rx_urb = urb; - tasklet_schedule(&priv->tasklet); - return; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWESSID - %s", netdev->name, extra); + + if (data->flags) { + memcpy(dev->essid, extra, data->length); + dev->essid_size = data->length; + } else { + /* Use any SSID */ + dev->essid_size = 0; + } + + return -EIWCOMMIT; } -static void at76_rx_monitor_mode(struct at76_priv *dev) + +static int at76_iw_handler_get_essid(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) { - struct at76_rx_radiotap *rt; - u8 *payload; - int skblen; - struct net_device *netdev = dev->netdev; - struct at76_rx_buffer *buf = - (struct at76_rx_buffer *)dev->rx_skb->data; - /* length including the IEEE802.11 header and the trailing FCS, - but not at76_rx_buffer */ - int length = le16_to_cpu(buf->wlength); - struct sk_buff *skb = dev->rx_skb; - struct net_device_stats *stats = &dev->stats; + struct at76_priv *dev = netdev_priv(netdev); - if (length < dev->rx_data_fcs_len) { - /* buffer contains no data */ - at76_dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE: rx skb without data", - dev->netdev->name); - return; + 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; + } } - skblen = sizeof(struct at76_rx_radiotap) + length; - - if ((skb = dev_alloc_skb(skblen)) == NULL) { - err("%s: MONITOR MODE: dev_alloc_skb for radiotap header " - "returned NULL", dev->netdev->name); - return; - } + at76_dbg(DBG_IOCTL, "%s: SIOCGIWESSID - %s", netdev->name, extra); - skb_put(skb, skblen); + return 0; +} - rt = (struct at76_rx_radiotap *)skb->data; - payload = skb->data + sizeof(struct at76_rx_radiotap); - rt->rt_hdr.it_version = 0; - rt->rt_hdr.it_pad = 0; - rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct at76_rx_radiotap)); - rt->rt_hdr.it_present = cpu_to_le32(AT76_RX_RADIOTAP_PRESENT); +static int at76_iw_handler_set_rate(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *bitrate, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); + int ret = -EIWCOMMIT; - rt->rt_tsft = cpu_to_le64(le32_to_cpu(buf->rx_time)); - rt->rt_rate = hw_rates[buf->rx_rate] & (~0x80); - rt->rt_signal = buf->rssi; - rt->rt_noise = buf->noise_level; - rt->rt_flags = 0; - if (buf->fragmentation) - rt->rt_flags |= IEEE80211_RADIOTAP_F_FRAG; - if (dev->rx_data_fcs_len) - rt->rt_flags |= IEEE80211_RADIOTAP_F_FCS; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWRATE - %d", netdev->name, bitrate->value); - memcpy(payload, buf->packet, length); - 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); + 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; + } - netdev->last_rx = jiffies; - netif_rx(skb); - stats->rx_packets++; - stats->rx_bytes += length; + return ret; } -static void at76_rx_tasklet(unsigned long param) +static int at76_iw_handler_get_rate(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *bitrate, char *extra) { - struct at76_priv *dev = (struct at76_priv *)param; - struct urb *urb; - struct net_device *netdev; - struct at76_rx_buffer *buf; - struct ieee80211_hdr_3addr *i802_11_hdr; - u16 frame_ctl; - - if (!dev) - return; - urb = dev->rx_urb; - netdev = dev->netdev; + struct at76_priv *dev = netdev_priv(netdev); + int ret = 0; - if (dev->device_unplugged) { - at76_dbg(DBG_DEVSTART, "device unplugged"); - if (urb) - at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); - return; + 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; - if (!urb || !dev->rx_skb || !netdev || !dev->rx_skb->data) - return; + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRATE - %d", netdev->name, + bitrate->value); - buf = (struct at76_rx_buffer *)dev->rx_skb->data; + return ret; +} - if (!buf) - return; - i802_11_hdr = (struct ieee80211_hdr_3addr *)buf->packet; - if (!i802_11_hdr) - return; +static int at76_iw_handler_set_rts(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); + int ret = -EIWCOMMIT; + int rthr = rts->value; - frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl); + at76_dbg(DBG_IOCTL, "%s: SIOCSIWRTS - value %d disabled %s", + netdev->name, rts->value, (rts->disabled) ? "true" : "false"); - if (urb->status != 0) { - if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) { - at76_dbg(DBG_URB, - "%s %s: - nonzero read bulk status received: %d", - __FUNCTION__, netdev->name, urb->status); - goto no_more_urb; - } - return; - } + if (rts->disabled) + rthr = MAX_RTS_THRESHOLD; - at76_dbg(DBG_RX_ATMEL_HDR, "%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, i802_11_hdr, - min((int)(sizeof(dev->obuf)-1)/2,48),'\0')); - if (dev->istate == MONITORING) { - at76_rx_monitor_mode(dev); - goto finish; + if ((rthr < 0) || (rthr > MAX_RTS_THRESHOLD)) { + ret = -EINVAL; + } else { + dev->rts_threshold = rthr; } - /* there is a new bssid around, accept it: */ - if (buf->newbss && dev->iw_mode == IW_MODE_ADHOC) { - at76_dbg(DBG_PROGRESS, "%s: rx newbss", netdev->name); - schedule_work(&dev->work_new_bss); - } + return ret; +} - switch (frame_ctl & IEEE80211_FCTL_FTYPE) { - case IEEE80211_FTYPE_DATA: - at76_rx_data(dev); - break; - case IEEE80211_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 !) */ +static int at76_iw_handler_get_rts(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); - at76_iwspy_update(dev, buf); + rts->value = dev->rts_threshold; + rts->disabled = (rts->value >= MAX_RTS_THRESHOLD); + rts->fixed = 1; - at76_rx_mgmt(dev, buf); - break; + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRTS - value %d disabled %s", + netdev->name, rts->value, (rts->disabled) ? "true" : "false"); - case IEEE80211_FTYPE_CTL: - at76_dbg(DBG_RX_CTRL, "%s: ignored ctrl frame: %04x", - dev->netdev->name, frame_ctl); - break; + return 0; +} - default: - info("%s: it's a frame from mars: %2x", dev->netdev->name, - frame_ctl); + +static int at76_iw_handler_set_frag(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); + int ret = -EIWCOMMIT; + int fthr = frag->value; + + at76_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 */ } - finish: - at76_submit_rx_urb(dev); - no_more_urb: - return; + + return ret; } -static void at76_write_bulk_callback(struct urb *urb) + +static int at76_iw_handler_get_frag(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *frag, char *extra) { - struct at76_priv *dev = urb->context; - struct net_device_stats *stats = &dev->stats; - unsigned long flags; - struct at76_tx_buffer *mgmt_buf; - int ret; + struct at76_priv *dev = netdev_priv(netdev); - if (urb->status != 0) { - if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) { - at76_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++; + frag->value = dev->frag_threshold; + frag->disabled = (frag->value >= MAX_FRAG_THRESHOLD); + frag->fixed = 1; - 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); + at76_dbg(DBG_IOCTL, "%s: SIOCGIWFRAG - value %d, disabled %s", + netdev->name, frag->value, (frag->disabled) ? "true" : "false"); - 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 at76_tx_buffer, packet)); - usb_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) + - mgmt_buf->padding + AT76_TX_HDRLEN, - at76_write_bulk_callback, - dev); - ret = usb_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); + return 0; +} + +static int at76_iw_handler_get_txpow(struct net_device *netdev, + struct 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; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWTXPOW - txpow %d dBm", netdev->name, + power->value); + + return 0; } -static int at76_tx(struct sk_buff *skb, struct net_device *netdev) + +/* jal: short retry is handled by the firmware (at least 0.90.x), + while long retry is not (?) */ +static int at76_iw_handler_set_retry(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *retry, char *extra) { struct at76_priv *dev = netdev_priv(netdev); - struct net_device_stats *stats = &dev->stats; - int ret = 0; - int wlen; - int submit_len; - struct at76_tx_buffer *tx_buffer = dev->bulk_out_buffer; - struct ieee80211_hdr_3addr *i802_11_hdr = - (struct ieee80211_hdr_3addr *)&(tx_buffer->packet); - u8 *payload = tx_buffer->packet + sizeof(struct ieee80211_hdr_3addr); - - 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; - } + int ret = -EIWCOMMIT; - if (dev->write_urb->status == -EINPROGRESS) { - err("%s: %s called while dev->write_urb is pending for tx", - netdev->name, __FUNCTION__); - /* skip this packet */ - dev_kfree_skb(skb); - return 0; - } + at76_dbg(DBG_IOCTL, "%s: SIOCSIWRETRY disabled %d flags x%x val %d", + netdev->name, retry->disabled, retry->flags, retry->value); - 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; + 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; } - at76_ledtrig_tx_activity(); /* tell the ledtrigger we send a packet */ + return ret; +} - /* we can get rid of memcpy, if we set netdev->hard_header_len - to 8 + sizeof(struct ieee80211_hdr_3addr), because then we have - enough space - at76_dbg(DBG_TX, "skb->data - skb->head = %d", skb->data - skb->head); */ - if (ntohs(*(__be16 *) (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 ieee80211_hdr_3addr) + 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 ieee80211_hdr_3addr) + sizeof(rfc1042sig) + - skb->len - 2*ETH_ALEN; - } +/* adapted (ripped) from atmel.c */ +static int at76_iw_handler_get_retry(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *retry, char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); - /* make wireless header */ - i802_11_hdr->frame_ctl = - cpu_to_le16(IEEE80211_FTYPE_DATA | - (dev->wep_enabled ? IEEE80211_FCTL_PROTECTED : 0) | - (dev->iw_mode == - IW_MODE_INFRA ? IEEE80211_FCTL_TODS : 0)); + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRETRY", netdev->name); - 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 */ - } + retry->disabled = 0; /* Can't be disabled */ - 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 */ + retry->flags = IW_RETRY_LIMIT; + retry->value = dev->short_retry_limit; - memset(tx_buffer->reserved, 0, 4); + return 0; +} - tx_buffer->padding = at76_calc_padding(wlen); - submit_len = wlen + AT76_TX_HDRLEN + tx_buffer->padding; - { - at76_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')); - at76_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), - tx_buffer->padding, tx_buffer->tx_rate, - hex2str(dev->obuf, i802_11_hdr, - min((sizeof(dev->obuf) - 1) / 2, - sizeof(struct ieee80211_hdr_3addr)), '\0')); - at76_dbg(DBG_TX_DATA_CONTENT, "%s payload %s", dev->netdev->name, - hex2str(dev->obuf, payload, - min((int)(sizeof(dev->obuf) - 1) / 2, 48), '\0')); - } +static int at76_iw_handler_set_encode(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *encoding, + char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); + int index = (encoding->flags & IW_ENCODE_INDEX) - 1; + int len = encoding->length; - /* send stuff */ - netif_stop_queue(netdev); - netdev->trans_start = jiffies; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWENCODE - enc.flags %08x " + "pointer %p len %d", netdev->name, encoding->flags, + encoding->pointer, encoding->length); + at76_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 == WLAN_AUTH_SHARED_KEY) ? "restricted" : "open"); - usb_fill_bulk_urb(dev->write_urb, dev->udev, - usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), - tx_buffer, submit_len, - at76_write_bulk_callback, dev); - ret = usb_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; - } + /* take the old default key if index is invalid */ + if ((index < 0) || (index >= WEP_KEYS)) + index = dev->wep_key_id; - stats->tx_bytes += skb->len; + if (len > 0) { + if (len > WEP_LARGE_KEY_LEN) + len = WEP_LARGE_KEY_LEN; - dev_kfree_skb(skb); - return 0; + memset(dev->wep_keys[index], 0, WEP_KEY_LEN); + 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; + } - err: - return ret; -} + dev->wep_key_id = index; + dev->wep_enabled = ((encoding->flags & IW_ENCODE_DISABLED) == 0); -static void at76_tx_timeout(struct net_device *netdev) -{ - struct at76_priv *dev = netdev_priv(netdev); + if (encoding->flags & IW_ENCODE_RESTRICTED) + dev->auth_mode = WLAN_AUTH_SHARED_KEY; + if (encoding->flags & IW_ENCODE_OPEN) + dev->auth_mode = WLAN_AUTH_OPEN; - if (!dev) - return; - warn("%s: tx timeout.", netdev->name); + at76_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 == WLAN_AUTH_SHARED_KEY) ? "restricted" : "open"); - usb_unlink_urb(dev->write_urb); - dev->stats.tx_errors++; + return -EIWCOMMIT; } -static int at76_startup_device(struct at76_priv *dev) + +static int at76_iw_handler_get_encode(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *encoding, + char *extra) { - struct at76_card_config *ccfg = &dev->card_config; - int ret; + struct at76_priv *dev = netdev_priv(netdev); + int index = (encoding->flags & IW_ENCODE_INDEX) - 1; - if (at76_debug & DBG_PARAMS) { - char ossid[IW_ESSID_MAX_SIZE + 1]; + if ((index < 0) || (index >= WEP_KEYS)) + index = dev->wep_key_id; - /* make dev->essid printable */ - at76_assert(dev->essid_size <= IW_ESSID_MAX_SIZE); - memcpy(ossid, dev->essid, dev->essid_size); - ossid[dev->essid_size] = '\0'; + encoding->flags = + (dev->auth_mode == WLAN_AUTH_SHARED_KEY) ? + IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN; - dbg("%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("%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("%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, - dev->auth_mode == WLAN_AUTH_OPEN ? - "open" : "shared_secret", - dev->scan_min_time, dev->scan_max_time, - dev->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive", - dev->international_roaming); - } + if (!dev->wep_enabled) + encoding->flags |= IW_ENCODE_DISABLED; - memset(ccfg, 0, sizeof(struct at76_card_config)); - ccfg->promiscuous_mode = 0; - ccfg->short_retry_limit = dev->short_retry_limit; + if (encoding->pointer) { + encoding->length = dev->wep_keys_len[index]; - 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; + memcpy(extra, dev->wep_keys[index], dev->wep_keys_len[index]); - /* jal: always exclude unencrypted if WEP is active */ - ccfg->exclude_unencrypted = 1; - } else { - ccfg->exclude_unencrypted = 0; - ccfg->encryption_type = 0; + encoding->flags |= (index + 1); } - 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; + at76_dbg(DBG_IOCTL, "%s: SIOCGIWENCODE - enc.flags %08x " + "pointer %p len %d", netdev->name, encoding->flags, + encoding->pointer, encoding->length); + at76_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 == WLAN_AUTH_SHARED_KEY) ? + "restricted" : "open"); - ccfg->wep_default_key_id = dev->wep_key_id; - memcpy(ccfg->wep_default_key_value, dev->wep_keys, 4 * WEP_KEY_LEN); + return 0; +} - ccfg->short_preamble = dev->preamble_type; - ccfg->beacon_period = cpu_to_le16(dev->beacon_period); - ret = at76_set_card_command(dev->udev, CMD_STARTUP, &dev->card_config, - sizeof(struct at76_card_config)); - if (ret < 0) { - err("%s: at76_set_card_command failed: %d", dev->netdev->name, ret); - return ret; - } +static int at76_iw_handler_set_power(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *prq, char *extra) +{ + int err = -EIWCOMMIT; + struct at76_priv *dev = netdev_priv(netdev); - at76_wait_completion(dev, CMD_STARTUP); + at76_dbg(DBG_IOCTL, "%s: SIOCSIWPOWER - disabled %s flags x%x value x%x", + netdev->name, (prq->disabled) ? "true" : "false", + prq->flags, prq->value); - /* remove BSSID from previous run */ - memset(dev->bssid, 0, ETH_ALEN); - - if (at76_set_radio(dev, 1) == 1) - at76_wait_completion(dev, CMD_RADIO); - - if ((ret = at76_set_preamble(dev, dev->preamble_type)) < 0) - return ret; - - if ((ret = at76_set_frag(dev, dev->frag_threshold)) < 0) - return ret; - - if ((ret = at76_set_rts(dev, dev->rts_threshold)) < 0) - return ret; - - if ((ret = at76_set_autorate_fallback(dev, dev->txrate == TX_RATE_AUTO ? 1 : 0)) < 0) - return ret; - - if ((ret = at76_set_pm_mode(dev)) < 0) - return ret; - - if ((ret = at76_set_iroaming(dev, dev->international_roaming)) < 0) - return ret; - - if (at76_debug & DBG_MIB) { - at76_dump_mib_mac(dev); - at76_dump_mib_mac_addr(dev); - at76_dump_mib_mac_mgmt(dev); - at76_dump_mib_mac_wep(dev); - at76_dump_mib_mdomain(dev); - at76_dump_mib_phy(dev); - at76_dump_mib_local(dev); + if (prq->disabled) { + dev->pm_mode = AT76_PM_OFF; + } else { + switch (prq->flags & IW_POWER_MODE) { + case IW_POWER_ALL_R: + case IW_POWER_ON: + break; + default: + err = -EINVAL; + goto out; + } + if (prq->flags & IW_POWER_PERIOD) { + dev->pm_period = prq->value; + } + if (prq->flags & IW_POWER_TIMEOUT) { + err = -EINVAL; + goto out; + } + dev->pm_mode = AT76_PM_ON; } - - return 0; +out: + return err; } -static int at76_open(struct net_device *netdev) + +static int at76_iw_handler_get_power(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *power, char *extra) { struct at76_priv *dev = netdev_priv(netdev); - int ret = 0; - - at76_dbg(DBG_PROC_ENTRY, "at76_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 (compare_ether_addr(netdev->dev_addr, dev->mac_addr)) { - if (at76_add_mac_address(dev, netdev->dev_addr) >= 0) - at76_dbg(DBG_PROGRESS, "%s: set new MAC addr %s", - netdev->name, mac2str(netdev->dev_addr)); - } -#ifdef DEBUG - at76_dump_mib_mac_addr(dev); -#endif - - dev->scan_state = SCAN_IDLE; - dev->last_scan = jiffies; - dev->nr_submit_rx_tries = NR_SUBMIT_RX_TRIES; /* init counter */ - if ((ret = at76_submit_rx_urb(dev)) < 0) { - err("%s: open: submit_rx_urb failed: %d", netdev->name, ret); - goto err; + if ((power->disabled = (dev->pm_mode == AT76_PM_OFF))) + return 0; + else { + power->flags = IW_POWER_PERIOD; + power->value = dev->pm_period; } + power->flags |= IW_POWER_ALL_R; - dev->open_count++; - - schedule_work(&dev->work_restart); + at76_dbg(DBG_IOCTL, "%s: SIOCGIWPOWER - disabled %s flags x%x value x%x", + netdev->name, (power->disabled) ? "true" : "false", + power->flags, power->value); - at76_dbg(DBG_PROC_ENTRY, "at76_open end"); - err: - up(&dev->sem); - return ret < 0 ? ret : 0; + return 0; } -static int at76_stop(struct net_device *netdev) + +/******************************************************************************* + * Private IOCTLS + */ +static int at76_iw_set_short_preamble(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) { struct at76_priv *dev = netdev_priv(netdev); - unsigned long flags; + int val = *((int *)name); + int ret = -EIWCOMMIT; - at76_dbg(DBG_DEVSTART, "%s: ENTER", __FUNCTION__); + at76_dbg(DBG_IOCTL, "%s: AT76_SET_SHORT_PREAMBLE, %d", + netdev->name, val); - if (down_interruptible(&dev->sem)) - return -EINTR; + 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; + } - netif_stop_queue(netdev); + return ret; +} - dev->istate = INIT; - if (!(dev->device_unplugged)) { - /* we are called by "ifconfig ethX down", not because the - device isn't avail. anymore */ - at76_set_radio(dev, 0); +static int at76_iw_set_debug(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + char *ptr; + u32 val; - /* 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); - } + if (data->length > 0) { + val = simple_strtol(extra, &ptr, 0); - del_timer_sync(&dev->mgmt_timer); + if (ptr == extra) { + val = DBG_DEFAULTS; + } - spin_lock_irqsave(&dev->mgmt_spinlock, flags); - if (dev->next_mgmt_bulk) { - kfree(dev->next_mgmt_bulk); - dev->next_mgmt_bulk = NULL; + dbg("%s: AT76_SET_DEBUG input %d: %s -> x%x", + netdev->name, data->length, extra, val); + } else { + val = DBG_DEFAULTS; } - spin_unlock_irqrestore(&dev->mgmt_spinlock, flags); - /* free the bss_list */ - at76_free_bss_list(dev); + dbg("%s: AT76_SET_DEBUG, old 0x%x new 0x%x", + netdev->name, at76_debug, val); - at76_assert(dev->open_count > 0); - dev->open_count--; + /* jal: some more output to pin down lockups */ + dbg("%s: netif running %d queue_stopped %d carrier_ok %d", + netdev->name, + netif_running(netdev), + netif_queue_stopped(netdev), netif_carrier_ok(netdev)); - up(&dev->sem); - at76_dbg(DBG_DEVSTART, "%s: EXIT", __FUNCTION__); + at76_debug = val; return 0; } -static struct net_device_stats *at76_get_stats(struct net_device *netdev) + +static int at76_iw_set_powersave_mode(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) { struct at76_priv *dev = netdev_priv(netdev); - return &dev->stats; + int val = *((int *)name); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_POWERSAVE_MODE, %d (%s)", + netdev->name, val, + val == AT76_PM_OFF ? "active" : val == AT76_PM_ON ? "save" : + val == AT76_PM_SMART ? "smart save" : ""); + if (val < AT76_PM_OFF || val > AT76_PM_SMART) { + ret = -EINVAL; + } else { + dev->pm_mode = val; + } + + return ret; } -static struct iw_statistics *at76_get_wireless_stats(struct net_device - *netdev) + +static int at76_iw_set_scan_times(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) { struct at76_priv *dev = netdev_priv(netdev); + int mint = *((int *)name); + int maxt = *((int *)name + 1); + int ret = -EIWCOMMIT; - at76_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); + at76_dbg(DBG_IOCTL, "%s: AT76_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 &dev->wstats; + return ret; } -static void at76_set_multicast(struct net_device *netdev) + +static int at76_iw_set_scan_mode(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) { struct at76_priv *dev = netdev_priv(netdev); - int promisc; + int val = *((int *)name); + int ret = -EIWCOMMIT; - promisc = ((netdev->flags & IFF_PROMISC) != 0); - if (promisc != dev->promisc) { - /* grmbl. This gets called in interrupt. */ - dev->promisc = promisc; - schedule_work(&dev->work_set_promisc); + at76_dbg(DBG_IOCTL, "%s: AT76_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; } -} -/* we only store the new mac address in netdev struct, - it gets set when the netdev is opened. */ -static int at76_set_mac_address(struct net_device *netdev, void *addr) -{ - struct sockaddr *mac = addr; - memcpy(netdev->dev_addr, mac->sa_data, ETH_ALEN); - return 1; + return ret; } -/** - * at76_iwspy_update - check if we spy on the sender address of buf and update stats - */ -static void at76_iwspy_update(struct at76_priv *dev, struct at76_rx_buffer *buf) + +static int at76_set_iroaming(struct at76_priv *dev, int onoff) { - struct ieee80211_hdr_3addr *hdr = - (struct ieee80211_hdr_3addr *)buf->packet; - struct iw_quality qual; + int ret = 0; - /* We can only set the level here */ - qual.updated = IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; - qual.level = 0; - qual.noise = 0; - at76_calc_level(dev, buf, &qual); + 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 = at76_set_mib(dev, &dev->mib_buf); + if (ret < 0) { + err("%s: set_mib (intl_roaming_enable) failed: %d", dev->netdev->name, ret); + } - spin_lock_bh(&(dev->spy_spinlock)); + return ret; +} - if (dev->spy_data.spy_number > 0) { - wireless_spy_update(dev->netdev, hdr->addr2, &qual); + +static int at76_iw_set_intl_roaming(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) +{ + struct at76_priv *dev = netdev_priv(netdev); + int val = *((int *)name); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: AT76_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; + at76_set_iroaming(dev, val); + } } - spin_unlock_bh(&(dev->spy_spinlock)); + + return ret; } +/******************************************************************************* + * structure that advertises the iw handlers of this driver + */ +static const iw_handler at76_handlers[] = +{ + [SIOCSIWCOMMIT-SIOCIWFIRST] = (iw_handler) at76_iw_handler_commit, + [SIOCGIWNAME -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_name, + [SIOCSIWFREQ -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_freq, + [SIOCGIWFREQ -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_freq, + [SIOCSIWMODE -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_mode, + [SIOCGIWMODE -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_mode, + [SIOCGIWRANGE -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_range, + [SIOCSIWSPY -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_spy, + [SIOCGIWSPY -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_spy, + [SIOCSIWTHRSPY-SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_thrspy, + [SIOCGIWTHRSPY-SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_thrspy, + [SIOCSIWAP -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_wap, + [SIOCGIWAP -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_wap, + [SIOCSIWSCAN -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_scan, + [SIOCGIWSCAN -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_scan, + [SIOCSIWESSID -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_essid, + [SIOCGIWESSID -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_essid, + [SIOCSIWRATE -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_rate, + [SIOCGIWRATE -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_rate, + [SIOCSIWRTS -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_rts, + [SIOCGIWRTS -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_rts, + [SIOCSIWFRAG -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_frag, + [SIOCGIWFRAG -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_frag, + [SIOCGIWTXPOW -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_txpow, + [SIOCSIWRETRY -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_retry, + [SIOCGIWRETRY -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_retry, + [SIOCSIWENCODE-SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_encode, + [SIOCGIWENCODE-SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_encode, + [SIOCSIWPOWER -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_power, + [SIOCGIWPOWER -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_power, +}; + +#define AT76_SET_PRIV(h, f) [h - SIOCIWFIRSTPRIV] = (iw_handler) f + +/*structure that advertises the private iw handlers of this driver */ +static const iw_handler at76_priv_handlers[] = { + AT76_SET_PRIV(AT76_SET_SHORT_PREAMBLE, at76_iw_set_short_preamble), + AT76_SET_PRIV(AT76_SET_DEBUG, at76_iw_set_debug), + AT76_SET_PRIV(AT76_SET_POWERSAVE_MODE, at76_iw_set_powersave_mode), + AT76_SET_PRIV(AT76_SET_SCAN_TIMES, at76_iw_set_scan_times), + AT76_SET_PRIV(AT76_SET_SCAN_MODE, at76_iw_set_scan_mode), + AT76_SET_PRIV(AT76_SET_INTL_ROAMING, at76_iw_set_intl_roaming), +}; + + /******************************************************************************* * structure that describes the private ioctls/iw handlers of this driver */ @@ -4466,1372 +3745,2334 @@ static const struct iw_priv_args at76_priv_args[] = { "intl_roaming"}, }; -/******************************************************************************* - * at76_priv implementations of iw_handler functions: - */ -static int at76_iw_handler_commit(struct net_device *netdev, - struct iw_request_info *info, - void *null, char *extra) + +static const struct iw_handler_def at76_handler_def = { - struct at76_priv *dev = netdev_priv(netdev); - unsigned long flags; - at76_dbg(DBG_IOCTL, "%s %s: restarting the device", netdev->name, - __FUNCTION__); + .num_standard = ARRAY_SIZE(at76_handlers), + .num_private = ARRAY_SIZE(at76_priv_handlers), + .num_private_args = ARRAY_SIZE(at76_priv_args), + .standard = at76_handlers, + .private = at76_priv_handlers, + .private_args = at76_priv_args, + .get_wireless_stats = at76_get_wireless_stats, +}; - /* TODO: stop any pending tx bulk urb */ - 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); +/* A short overview on Ethernet-II, 802.2, 802.3 and SNAP + (taken from http://www.geocities.com/billalexander/ethernet.html): - netif_carrier_off(dev->netdev); - netif_stop_queue(dev->netdev); - } +Ethernet Frame Formats: - /* 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); +Ethernet (a.k.a. Ethernet II) - return 0; -} + +---------+---------+---------+---------- + | Dst | Src | Type | Data... + +---------+---------+---------+---------- -static int at76_iw_handler_get_name(struct net_device *netdev, - struct iw_request_info *info, - char *name, char *extra) -{ - strcpy(name, "IEEE 802.11b"); - at76_dbg(DBG_IOCTL, "%s: SIOCGIWNAME - name %s", netdev->name, name); - return 0; -} + <-- 6 --> <-- 6 --> <-- 2 --> <-46-1500-> -static int at76_iw_handler_set_freq(struct net_device *netdev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int chan = -1; - int ret = -EIWCOMMIT; - at76_dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - freq.m %d freq.e %d", netdev->name, - freq->m, freq->e); + Type 0x80 0x00 = TCP/IP + Type 0x06 0x00 = XNS + Type 0x81 0x37 = Novell NetWare - 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; - } +802.3 - for (i = 0; i < NUM_CHANNELS; i++) { - if (freq->m == (channel_frequency[i] * mult)) - chan = i + 1; - } - } + +---------+---------+---------+---------- + | Dst | Src | Length | Data... + +---------+---------+---------+---------- - 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; - } - } + <-- 6 --> <-- 6 --> <-- 2 --> <-46-1500-> - if (ret == -EIWCOMMIT) { - dev->channel = chan; - at76_dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - ch %d", netdev->name, chan); - } +802.2 (802.3 with 802.2 header) - return ret; -} + +---------+---------+---------+-------+-------+-------+---------- + | Dst | Src | Length | DSAP | SSAP |Control| Data... + +---------+---------+---------+-------+-------+-------+---------- -static int at76_iw_handler_get_freq(struct net_device *netdev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); + <- 1 -> <- 1 -> <- 1 -> <-43-1497-> - freq->m = dev->channel; - freq->e = 0; +SNAP (802.3 with 802.2 and SNAP headers) - if (dev->channel) { - at76_dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - freq %ld x 10e%d", - netdev->name, channel_frequency[dev->channel - 1], 6); - } - at76_dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - ch %d", netdev->name, dev->channel); + +---------+---------+---------+-------+-------+-------+-----------+---------+----------- + | Dst | Src | Length | 0xAA | 0xAA | 0x03 | Org Code | Type | Data... + +---------+---------+---------+-------+-------+-------+-----------+---------+----------- - return 0; -} + <-- 3 --> <-- 2 --> <-38-1492-> -static int at76_iw_handler_set_mode(struct net_device *netdev, - struct iw_request_info *info, - __u32 * mode, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int ret = -EIWCOMMIT; +*/ +static const u8 snapsig[] = { 0xaa, 0xaa, 0x03 }; - at76_dbg(DBG_IOCTL, "%s: SIOCSIWMODE - %d", netdev->name, *mode); +/* RFC 1042 encapsulates Ethernet frames in 802.2 SNAP (0xaa, 0xaa, 0x03) with + * a SNAP OID of 0 (0x00, 0x00, 0x00) */ +static const u8 rfc1042sig[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; - if ((*mode != IW_MODE_ADHOC) && (*mode != IW_MODE_INFRA) && - (*mode != IW_MODE_MONITOR)) { - ret = -EINVAL; - } else { - dev->iw_mode = *mode; - if( dev->iw_mode != IW_MODE_INFRA) - dev->pm_mode = AT76_PM_OFF; - } - return ret; -} -static int at76_iw_handler_get_mode(struct net_device *netdev, - struct iw_request_info *info, - __u32 * mode, char *extra) +static int at76_tx(struct sk_buff *skb, struct net_device *netdev) { struct at76_priv *dev = netdev_priv(netdev); + struct net_device_stats *stats = &dev->stats; + int ret = 0; + int wlen; + int submit_len; + struct at76_tx_buffer *tx_buffer = dev->bulk_out_buffer; + struct ieee80211_hdr_3addr *i802_11_hdr = + (struct ieee80211_hdr_3addr *)&(tx_buffer->packet); + u8 *payload = tx_buffer->packet + sizeof(struct ieee80211_hdr_3addr); - *mode = dev->iw_mode; - - at76_dbg(DBG_IOCTL, "%s: SIOCGIWMODE - %d", netdev->name, *mode); + 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; + } - return 0; -} - -static int at76_iw_handler_get_range(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - /* inspired by atmel.c */ - struct at76_priv *dev = netdev_priv(netdev); - 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 = 100; - range->max_qual.level = 100; - range->max_qual.noise = 0; - range->max_qual.updated = IW_QUAL_NOISE_INVALID; + if (dev->write_urb->status == -EINPROGRESS) { + err("%s: %s called while dev->write_urb is pending for tx", + netdev->name, __FUNCTION__); + /* skip this packet */ + dev_kfree_skb(skb); + return 0; + } - range->avg_qual.qual = 50; - range->avg_qual.level = 50; - range->avg_qual.noise = 0; - range->avg_qual.updated = IW_QUAL_NOISE_INVALID; + 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; + } - range->bitrate[0] = 1000000; - range->bitrate[1] = 2000000; - range->bitrate[2] = 5500000; - range->bitrate[3] = 11000000; - range->num_bitrates = 4; + at76_ledtrig_tx_activity(); /* tell the ledtrigger we send a packet */ - range->min_rts = 0; - range->max_rts = MAX_RTS_THRESHOLD; + /* we can get rid of memcpy, if we set netdev->hard_header_len + to 8 + sizeof(struct ieee80211_hdr_3addr), because then we have + enough space + at76_dbg(DBG_TX, "skb->data - skb->head = %d", skb->data - skb->head); */ - range->min_frag = MIN_FRAG_THRESHOLD; - range->max_frag = MAX_FRAG_THRESHOLD; + if (ntohs(*(__be16 *) (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 ieee80211_hdr_3addr) + 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 ieee80211_hdr_3addr) + sizeof(rfc1042sig) + + skb->len - 2*ETH_ALEN; + } - range->pmp_flags = IW_POWER_PERIOD; - range->pmt_flags = IW_POWER_ON; - range->pm_capa = IW_POWER_PERIOD | IW_POWER_ALL_R; + /* make wireless header */ + i802_11_hdr->frame_ctl = + cpu_to_le16(IEEE80211_FTYPE_DATA | + (dev->wep_enabled ? IEEE80211_FCTL_PROTECTED : 0) | + (dev->iw_mode == + IW_MODE_INFRA ? IEEE80211_FCTL_TODS : 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 = WEP_KEYS; + 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 */ + } - /* 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; + i802_11_hdr->duration_id = cpu_to_le16(0); + i802_11_hdr->seq_ctl = cpu_to_le16(0); - range->we_version_source = WIRELESS_EXT; - range->we_version_compiled = WIRELESS_EXT; + /* 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 */ - /* 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; + memset(tx_buffer->reserved, 0, 4); + tx_buffer->padding = at76_calc_padding(wlen); + submit_len = wlen + AT76_TX_HDRLEN + tx_buffer->padding; - range->num_channels = NUM_CHANNELS; - range->num_frequency = 0; + { + at76_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')); + at76_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), + tx_buffer->padding, tx_buffer->tx_rate, + hex2str(dev->obuf, i802_11_hdr, + min((sizeof(dev->obuf) - 1) / 2, + sizeof(struct ieee80211_hdr_3addr)), '\0')); + at76_dbg(DBG_TX_DATA_CONTENT, "%s payload %s", dev->netdev->name, + hex2str(dev->obuf, payload, + min((int)(sizeof(dev->obuf) - 1) / 2, 48), '\0')); + } - for (i = 0; i < NUM_CHANNELS; i++) { - /* test if channel map bit is raised */ - if (dev->domain->channel_map & (0x1 << i)) { - range->num_frequency += 1; + /* send stuff */ + netif_stop_queue(netdev); + netdev->trans_start = jiffies; - range->freq[i].i = i + 1; - range->freq[i].m = channel_frequency[i] * 100000; - range->freq[i].e = 1; /* channel frequency*100000 * 10^1 */ - } + usb_fill_bulk_urb(dev->write_urb, dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + tx_buffer, submit_len, + at76_write_bulk_callback, dev); + ret = usb_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; } - at76_dbg(DBG_IOCTL, "%s: SIOCGIWRANGE", netdev->name); + stats->tx_bytes += skb->len; + dev_kfree_skb(skb); return 0; + + err: + return ret; } -static int at76_iw_handler_set_spy(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *data, char *extra) + +static void at76_tx_timeout(struct net_device *netdev) { struct at76_priv *dev = netdev_priv(netdev); - int ret = 0; - - at76_dbg(DBG_IOCTL, "%s: SIOCSIWSPY - number of addresses %d", - netdev->name, data->length); - spin_lock_bh(&(dev->spy_spinlock)); - ret = iw_handler_set_spy(dev->netdev, info, (union iwreq_data *)data, - extra); - spin_unlock_bh(&(dev->spy_spinlock)); + if (!dev) + return; + warn("%s: tx timeout.", netdev->name); - return ret; + usb_unlink_urb(dev->write_urb); + dev->stats.tx_errors++; } -static int at76_iw_handler_get_spy(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *data, char *extra) + +static int at76_submit_rx_urb(struct at76_priv *dev) { + int ret, size; + struct sk_buff *skb = dev->rx_skb; - struct at76_priv *dev = netdev_priv(netdev); - int ret = 0; + if (dev->read_urb == NULL) { + err("%s: dev->read_urb is NULL", __FUNCTION__); + return -EFAULT; + } - spin_lock_bh(&(dev->spy_spinlock)); - ret = iw_handler_get_spy(dev->netdev, info, - (union iwreq_data *)data, extra); - spin_unlock_bh(&(dev->spy_spinlock)); + if (skb == NULL) { + skb = dev_alloc_skb(sizeof(struct at76_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); + } - at76_dbg(DBG_IOCTL, "%s: SIOCGIWSPY - number of addresses %d", - netdev->name, data->length); + size = skb_tailroom(skb); + usb_fill_bulk_urb(dev->read_urb, dev->udev, + usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), + skb_put(skb, size), size, + at76_read_bulk_callback, dev); + ret = usb_submit_urb(dev->read_urb, GFP_ATOMIC); + if (ret < 0) { + if (ret == -ENODEV) + at76_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) + schedule_work(&dev->work_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; } -static int at76_iw_handler_set_thrspy(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *data, char *extra) + +static int at76_open(struct net_device *netdev) { struct at76_priv *dev = netdev_priv(netdev); - int ret; + int ret = 0; - at76_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 at76_iw_handler_get_thrspy(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - 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)); - - at76_dbg(DBG_IOCTL, "%s: SIOCGIWTHRSPY - number of addresses %d)", - netdev->name, data->length); - - return ret; -} - -static int at76_iw_handler_set_wap(struct net_device *netdev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); + at76_dbg(DBG_PROC_ENTRY, "at76_open entry"); - at76_dbg(DBG_IOCTL, "%s: SIOCSIWAP - wap/bssid %s", netdev->name, - mac2str(ap_addr->sa_data)); + if (down_interruptible(&dev->sem)) + return -EINTR; - /* if the incoming address == ff:ff:ff:ff:ff:ff, the user has - chosen any or auto AP preference */ - if (is_broadcast_ether_addr(ap_addr->sa_data) - || is_zero_ether_addr(ap_addr->sa_data)) { - 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); + /* if netdev->dev_addr != dev->mac_addr we must + set the mac address in the device ! */ + if (compare_ether_addr(netdev->dev_addr, dev->mac_addr)) { + if (at76_add_mac_address(dev, netdev->dev_addr) >= 0) + at76_dbg(DBG_PROGRESS, "%s: set new MAC addr %s", + netdev->name, mac2str(netdev->dev_addr)); } +#ifdef DEBUG + at76_dump_mib_mac_addr(dev); +#endif - return -EIWCOMMIT; -} + dev->scan_state = SCAN_IDLE; + dev->last_scan = jiffies; + dev->nr_submit_rx_tries = NR_SUBMIT_RX_TRIES; /* init counter */ -static int at76_iw_handler_get_wap(struct net_device *netdev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); + if ((ret = at76_submit_rx_urb(dev)) < 0) { + err("%s: open: submit_rx_urb failed: %d", netdev->name, ret); + goto err; + } - ap_addr->sa_family = ARPHRD_ETHER; - memcpy(ap_addr->sa_data, dev->bssid, ETH_ALEN); + dev->open_count++; - at76_dbg(DBG_IOCTL, "%s: SIOCGIWAP - wap/bssid %s", netdev->name, - mac2str(ap_addr->sa_data)); + schedule_work(&dev->work_restart); - return 0; + at76_dbg(DBG_PROC_ENTRY, "at76_open end"); + err: + up(&dev->sem); + return ret < 0 ? ret : 0; } -static int at76_iw_handler_set_scan(struct net_device *netdev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) + +static int at76_stop(struct net_device *netdev) { struct at76_priv *dev = netdev_priv(netdev); unsigned long flags; - int ret = 0; - struct iw_scan_req *req = NULL; - at76_dbg(DBG_IOCTL, "%s: SIOCSIWSCAN", netdev->name); + at76_dbg(DBG_DEVSTART, "%s: ENTER", __FUNCTION__); - if (!netif_running(netdev)) - return -ENETDOWN; + if (down_interruptible(&dev->sem)) + return -EINTR; - /* jal: we don't allow "iwlist ethX scan" while we are - in monitor mode */ - if (dev->iw_mode == IW_MODE_MONITOR) - return -EBUSY; + netif_stop_queue(netdev); - /* Discard old scan results */ - if ((jiffies - dev->last_scan) > (20 * HZ)) - dev->scan_state = SCAN_IDLE; - dev->last_scan = jiffies; + dev->istate = INIT; - /* Initiate a scan command */ - if (dev->scan_state == SCAN_IN_PROGRESS) - return -EBUSY; + if (!(dev->device_unplugged)) { + /* we are called by "ifconfig ethX down", not because the + device isn't avail. anymore */ + at76_set_radio(dev, 0); - dev->scan_state = SCAN_IN_PROGRESS; + /* 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); + } - /* stop pending management stuff */ - del_timer_sync(&(dev->mgmt_timer)); + del_timer_sync(&dev->mgmt_timer); - spin_lock_irqsave(&(dev->mgmt_spinlock), flags); + 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); - } - /* Try to do passive or active scan if WE asks as. */ - if (wrqu->data.length - && wrqu->data.length == sizeof(struct iw_scan_req)) { - req = (struct iw_scan_req *)extra; + spin_unlock_irqrestore(&dev->mgmt_spinlock, flags); - 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; + /* free the bss_list */ + at76_free_bss_list(dev); - /* 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 = req->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 = req->max_channel_time; - } - } + at76_assert(dev->open_count > 0); + dev->open_count--; - /* change to scanning state */ - dev->istate = SCANNING; - schedule_work(&dev->work_scan); + up(&dev->sem); + at76_dbg(DBG_DEVSTART, "%s: EXIT", __FUNCTION__); - return ret; + return 0; } -static int at76_iw_handler_get_scan(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *data, char *extra) + +static void at76_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) { struct at76_priv *dev = netdev_priv(netdev); - 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; - at76_dbg(DBG_IOCTL, "%s: SIOCGIWSCAN", netdev->name); + strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1); - if (!iwe) - return -ENOMEM; + strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); + info->version[sizeof(info->version) - 1] = '\0'; - if (dev->scan_state != SCAN_COMPLETED) - /* scan not yet finished */ - return -EAGAIN; + snprintf(info->bus_info, sizeof(info->bus_info) - 1, "usb%d:%d", + dev->udev->bus->busnum, dev->udev->devnum); - spin_lock_irqsave(&(dev->bss_list_spinlock), flags); + 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); +} - 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); +static u32 at76_ethtool_get_link(struct net_device *netdev) +{ + struct at76_priv *dev = netdev_priv(netdev); + return dev->istate == CONNECTED; +} - iwe->u.data.length = curr_bss->ssid_len; - iwe->cmd = SIOCGIWESSID; - iwe->u.data.flags = 1; - curr_pos = iwe_stream_add_point(curr_pos, - extra + IW_SCAN_MAX_DATA, iwe, curr_bss->ssid); +static struct ethtool_ops at76_ethtool_ops = { + .get_drvinfo = at76_ethtool_get_drvinfo, + .get_link = at76_ethtool_get_link, +}; - iwe->cmd = SIOCGIWMODE; - iwe->u.mode = (curr_bss->capa & WLAN_CAPABILITY_IBSS) ? - IW_MODE_ADHOC : - (curr_bss->capa & WLAN_CAPABILITY_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); +/** + * at76_init_new_device - continue device initialization after firmware download + * + * FIXME: We may have to move the register_netdev into at76_alloc_new_device, + * because hotplug may try to configure the netdev _before_ (or parallel to) + * the download of firmware + */ +static int at76_init_new_device(struct at76_priv *dev) +{ + struct net_device *netdev = dev->netdev; + int ret; - iwe->cmd = SIOCGIWENCODE; - if (curr_bss->capa & WLAN_CAPABILITY_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); + /* set up the endpoint information */ + /* check out the endpoints */ - /* Add quality statistics */ - iwe->cmd = IWEVQUAL; - iwe->u.qual.noise = 0; - iwe->u.qual.updated = - IW_QUAL_NOISE_INVALID | IW_QUAL_LEVEL_UPDATED; - iwe->u.qual.level = (curr_bss->rssi * 100 / 42); - if (iwe->u.qual.level > 100) - iwe->u.qual.level = 100; - if ((dev->board_type == BOARDTYPE_503_INTERSIL_3861) || - (dev->board_type == BOARDTYPE_503_INTERSIL_3863)) { - iwe->u.qual.qual = curr_bss->link_qual; - } else { - iwe->u.qual.qual = 0; - iwe->u.qual.updated |= IW_QUAL_QUAL_INVALID; + dev->interface = dev->udev->actconfig->interface[0]; + + at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints", + dev->interface->cur_altsetting->desc.bNumEndpoints); + + if ((ret = at76_alloc_urbs(dev)) < 0) + goto error; + + /* get firmware version */ + ret = at76_get_mib(dev->udev, MIB_FW_VERSION, &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"); + if(ret >= 0) + ret = -ENODEV; + 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("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 = at76_get_hw_config(dev); + if (ret < 0) { + err("could not get MAC address"); + goto error; + } + + dev->domain = at76_get_reg_domain(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; + memset(dev->essid, 0, IW_ESSID_MAX_SIZE); + dev->rts_threshold = DEF_RTS_THRESHOLD; + dev->frag_threshold = DEF_FRAG_THRESHOLD; + dev->short_retry_limit = DEF_SHORT_RETRY_LIMIT; + dev->txrate = TX_RATE_AUTO; + dev->preamble_type = preamble_type; + dev->beacon_period = 100; + dev->beacons_last_qual = jiffies_to_msecs(jiffies); + dev->auth_mode = auth_mode ? WLAN_AUTH_SHARED_KEY : WLAN_AUTH_OPEN; + 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 = at76_open; + netdev->stop = at76_stop; + netdev->get_stats = at76_get_stats; + netdev->ethtool_ops = &at76_ethtool_ops; + + /* Add pointers to enable iwspy support. */ + dev->wireless_data.spy_data = &dev->spy_data; + netdev->wireless_data = &dev->wireless_data; + + netdev->hard_start_xmit = at76_tx; + netdev->tx_timeout = at76_tx_timeout; + netdev->watchdog_timeo = 2 * HZ; + netdev->wireless_handlers = &at76_handler_def; + netdev->set_multicast_list = at76_set_multicast; + netdev->set_mac_address = at76_set_mac_address; + + ret = register_netdev(dev->netdev); + if (ret) { + err("unable to register netdevice %s (status %d)!", + dev->netdev->name, ret); + goto error; + } + info("registered %s", dev->netdev->name); + dev->netdev_registered = 1; + + /* we let this timer run the whole time this driver instance lives */ + mod_timer(&dev->bss_list_timer, jiffies + BSS_LIST_TIMEOUT); + + return 0; + error: + at76_delete_device(dev); + return ret; +} + + +/* Download external firmware */ +static void at76_work_external_fw(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_external_fw); + int ret; + u8 op_mode; + + down(&dev->sem); + + op_mode = at76_get_op_mode(dev->udev); + at76_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 = at76_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"); + /* can we do this with dev->sem down? */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 5 + 1); + } + } + dev->istate = INIT; + if ((ret = at76_init_new_device(dev)) < 0) + err("Downloading external firmware failed: %d", ret); + + end_external_fw: + up(&dev->sem); +} + + +/* Download internal firmware */ +static void at76_work_internal_fw(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_internal_fw); + int ret; + + down(&dev->sem); + + ret = at76_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; + } + + at76_dbg(DBG_DEVSTART, "sending REMAP"); + + /* no REMAP for 505A (see SF driver) */ + if (dev->board_type != BOARDTYPE_505A_RFMD_2958) + if ((ret = at76_remap(dev->udev)) < 0) { + err("sending REMAP failed with %d", ret); + goto end_internal_fw; + } + + at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); + dev->istate = EXTFW_DOWNLOAD; + mod_timer(&dev->fw_dl_timer, jiffies + 2 * HZ + 1); + + end_internal_fw: + up(&dev->sem); +} + + +static int at76_essid_matched(struct at76_priv *dev, struct bss_info *ptr) +{ + /* common criteria for both modi */ + + int ret = (dev->essid_size == 0 /* ANY ssid */ || + (dev->essid_size == ptr->ssid_len && + !memcmp(dev->essid, ptr->ssid, ptr->ssid_len))); + if (!ret) + at76_dbg(DBG_BSS_MATCH, "%s bss table entry %p: essid didn't match", + dev->netdev->name, ptr); + return ret; +} + + +static inline int at76_mode_matched(struct at76_priv *dev, struct bss_info *ptr) +{ + int ret; + + if (dev->iw_mode == IW_MODE_ADHOC) + ret = ptr->capa & WLAN_CAPABILITY_IBSS; + else + ret = ptr->capa & WLAN_CAPABILITY_ESS; + if (!ret) + at76_dbg(DBG_BSS_MATCH, "%s bss table entry %p: mode didn't match", + dev->netdev->name, ptr); + return ret; +} + + +static int at76_rates_matched(struct at76_priv *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])) { + at76_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 & WLAN_CAPABILITY_SHORT_PREAMBLE)) { + at76_dbg(DBG_BSS_MATCH, "%s: %p does not support short preamble", + dev->netdev->name, ptr); + return 0; + } else + return 1; +} + + +static inline int at76_wep_matched(struct at76_priv *dev, struct bss_info *ptr) +{ + if (!dev->wep_enabled && ptr->capa & WLAN_CAPABILITY_PRIVACY) { + /* we have disabled WEP, but the BSS signals privacy */ + at76_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 at76_bssid_matched(struct at76_priv *dev, + struct bss_info *ptr) +{ + if (!dev->wanted_bssid_valid || + !compare_ether_addr(ptr->bssid, dev->wanted_bssid)) { + return 1; + } else { + at76_dbg(DBG_BSS_MATCH, "%s: requested bssid - %s does not match", + dev->netdev->name, mac2str(dev->wanted_bssid)); + at76_dbg(DBG_BSS_MATCH, " AP bssid - %s of bss table entry %p", + mac2str(ptr->bssid), ptr); + return 0; + } +} + + +/** + * at76_match_bss - try to find a matching bss in dev->bss + * + * last - last bss tried + * + * last == NULL signals a new round starting with dev->bss_list.next + * this function must be called inside an acquired dev->bss_list_spinlock + * otherwise the timeout on bss may remove the newly chosen entry + */ +static struct bss_info *at76_match_bss(struct at76_priv *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 (at76_essid_matched(dev, ptr) && + at76_mode_matched(dev, ptr) && + at76_wep_matched(dev, ptr) && + at76_rates_matched(dev, ptr) && + at76_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 */ + + at76_dbg(DBG_BSS_TABLE, "%s %s: returned %p", dev->netdev->name, + __FUNCTION__, ptr); + return ptr; +} + + +/* Try joining a BSS */ +static void at76_work_join(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_join); + int ret; + unsigned long flags; + + down(&dev->sem); + + if (dev->istate == INIT) + goto end_join; + + at76_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 = at76_match_bss(dev, dev->curr_bss); + spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); + + if (dev->curr_bss != NULL) { + if ((ret = at76_join_bss(dev, dev->curr_bss)) < 0) { + err("%s: join_bss failed with %d", + dev->netdev->name, ret); + goto end_join; + } + + ret = at76_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 */ + schedule_work(&dev->work_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; + dev->istate = 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; + at76_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 */ + dev->istate = AUTHENTICATING; + at76_auth_req(dev, dev->curr_bss, 1, NULL); + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", + __FUNCTION__, __LINE__); + mod_timer(&dev->mgmt_timer, jiffies + HZ); + } + goto end_join; + } + + /* here we haven't found a matching (i)bss ... */ + if (dev->iw_mode == IW_MODE_ADHOC) { + dev->istate = STARTIBSS; + schedule_work(&dev->work_start_ibss); + goto end_join; + } + /* haven't found a matching BSS in infra mode - try again */ + dev->istate = SCANNING; + schedule_work(&dev->work_scan); + + end_join: + up(&dev->sem); +} + + +static void at76_work_mgmt_timeout(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_mgmt_timeout); + + down(&dev->sem); + at76_handle_mgmt_timeout(dev); + up(&dev->sem); +} + + +static void at76_work_new_bss(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_new_bss); + int ret; + struct net_device *netdev = dev->netdev; + struct mib_mac_mgmt mac_mgmt; + + down(&dev->sem); + + ret = at76_get_mib(dev->udev, MIB_MAC_MGMT, &mac_mgmt, + sizeof(struct mib_mac_mgmt)); + if (ret < 0) { + err("%s: at76_get_mib failed: %d", netdev->name, ret); + goto new_bss_clean; + } + + at76_dbg(DBG_PROGRESS, "ibss_change = 0x%2x", mac_mgmt.ibss_change); + memcpy(dev->bssid, mac_mgmt.current_bssid, ETH_ALEN); + at76_dbg(DBG_PROGRESS, "using BSSID %s", mac2str(dev->bssid)); + + at76_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 = at76_set_mib(dev, &dev->mib_buf); + if (ret < 0) + err("%s: set_mib (ibss change ok) failed: %d", netdev->name, ret); + + new_bss_clean: + up(&dev->sem); +} + + +static void at76_work_reset_device(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_reset_device); + + down(&dev->sem); + usb_reset_device(dev->udev); + dev->istate = WAIT_FOR_DISCONNECT; + up(&dev->sem); +} + + +static int at76_startup_device(struct at76_priv *dev) +{ + struct at76_card_config *ccfg = &dev->card_config; + int ret; + + if (at76_debug & DBG_PARAMS) { + char ossid[IW_ESSID_MAX_SIZE + 1]; + + /* make dev->essid printable */ + at76_assert(dev->essid_size <= IW_ESSID_MAX_SIZE); + memcpy(ossid, dev->essid, dev->essid_size); + ossid[dev->essid_size] = '\0'; + + dbg("%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("%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("%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, + dev->auth_mode == WLAN_AUTH_OPEN ? + "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 at76_card_config)); + ccfg->promiscuous_mode = 0; + ccfg->short_retry_limit = dev->short_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_LEN); + + ccfg->short_preamble = dev->preamble_type; + ccfg->beacon_period = cpu_to_le16(dev->beacon_period); + + ret = at76_set_card_command(dev->udev, CMD_STARTUP, &dev->card_config, + sizeof(struct at76_card_config)); + if (ret < 0) { + err("%s: at76_set_card_command failed: %d", dev->netdev->name, ret); + return ret; + } + + at76_wait_completion(dev, CMD_STARTUP); + + /* remove BSSID from previous run */ + memset(dev->bssid, 0, ETH_ALEN); + + if (at76_set_radio(dev, 1) == 1) + at76_wait_completion(dev, CMD_RADIO); + + if ((ret = at76_set_preamble(dev, dev->preamble_type)) < 0) + return ret; + + if ((ret = at76_set_frag(dev, dev->frag_threshold)) < 0) + return ret; + + if ((ret = at76_set_rts(dev, dev->rts_threshold)) < 0) + return ret; + + if ((ret = at76_set_autorate_fallback(dev, dev->txrate == TX_RATE_AUTO ? 1 : 0)) < 0) + return ret; + + if ((ret = at76_set_pm_mode(dev)) < 0) + return ret; + + if ((ret = at76_set_iroaming(dev, dev->international_roaming)) < 0) + return ret; + + if (at76_debug & DBG_MIB) { + at76_dump_mib_mac(dev); + at76_dump_mib_mac_addr(dev); + at76_dump_mib_mac_mgmt(dev); + at76_dump_mib_mac_wep(dev); + at76_dump_mib_mdomain(dev); + at76_dump_mib_phy(dev); + at76_dump_mib_local(dev); + } + + return 0; +} + + +/** + * set_monitor_mode - sets dev->netdev->type + */ +static void at76_set_monitor_mode(struct at76_priv *dev) +{ + if (dev->iw_mode == IW_MODE_MONITOR) { + at76_dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE ON", + dev->netdev->name); + dev->netdev->type = ARPHRD_IEEE80211_RADIOTAP; + } else { + at76_dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE OFF", + dev->netdev->name); + dev->netdev->type = ARPHRD_ETHER; + } +} + + +static void at76_work_restart(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_restart); + + down(&dev->sem); + + at76_startup_device(dev); + at76_set_monitor_mode(dev); + + 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) { + dev->istate = SCANNING; + schedule_work(&dev->work_scan); + } else { + dev->istate = MONITORING; + at76_start_scan(dev, 0, 0); + at76_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); + } + + up(&dev->sem); +} + + +static void at76_work_scan(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_scan); + int ret; + + down(&dev->sem); + + at76_assert(dev->istate == SCANNING); + /* only clear the bss list when a scan is actively initiated, + * otherwise simply rely on at76_bss_list_timeout */ + if (dev->scan_state == SCAN_IN_PROGRESS) + at76_free_bss_list(dev); + + dev->scan_runs = 2; + if ((ret = at76_start_scan(dev, 0, 1)) < 0) { + err("%s: %s: start_scan failed with %d", + dev->netdev->name, __FUNCTION__, ret); + } else { + at76_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); + } + + up(&dev->sem); +} + + +static void at76_work_set_promisc(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_set_promisc); + int ret = 0; + + down(&dev->sem); + + 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] = dev->promisc ? 1 : 0; + ret = at76_set_mib(dev, &dev->mib_buf); + if (ret < 0) { + err("%s: set_mib (promiscuous_mode) failed: %d", + dev->netdev->name, ret); + } + + up(&dev->sem); +} + + +static void at76_work_start_ibss(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_start_ibss); + int ret; + + down(&dev->sem); + + at76_assert(dev->istate == STARTIBSS); + ret = at76_start_ibss(dev); + if (ret < 0) { + err("%s: start_ibss failed: %d", dev->netdev->name, ret); + goto end_startibss; + } + + ret = at76_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 = at76_get_current_bssid(dev); + if (ret < 0) + goto end_startibss; + + ret = at76_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 = at76_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: + up(&dev->sem); +} + + +static void at76_work_submit_rx(struct work_struct *work) +{ + struct at76_priv *dev = container_of(work, struct at76_priv, + work_submit_rx); + + down(&dev->sem); + at76_submit_rx_urb(dev); + up(&dev->sem); +} + + +/* we got an association response */ +static void at76_rx_mgmt_assoc(struct at76_priv *dev, + struct at76_rx_buffer *buf) +{ + struct ieee80211_assoc_response *resp = + (struct ieee80211_assoc_response *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; + u16 assoc_id = le16_to_cpu(resp->aid); + u16 status = le16_to_cpu(resp->status); + u16 capa = le16_to_cpu(resp->capability); + at76_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->info_element->data, + min((size_t)resp->info_element->len, (sizeof(dev->obuf)-1)/2), '\0')); + if (dev->istate == ASSOCIATING) { + at76_assert(dev->curr_bss != NULL); + if (dev->curr_bss == NULL) + return; + + if (status == WLAN_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; + schedule_work(&dev->work_assoc_done); + } else { + dev->istate = JOINING; + schedule_work(&dev->work_join); } - /* Add new value to event */ - curr_pos = iwe_stream_add_event(curr_pos, - extra + IW_SCAN_MAX_DATA, iwe, - IW_EV_QUAL_LEN); + del_timer_sync(&dev->mgmt_timer); + } else { + info("%s: AssocResp in state %d ignored", + dev->netdev->name, dev->istate); + } +} - /* 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); +static void at76_rx_mgmt_reassoc(struct at76_priv *dev, + struct at76_rx_buffer *buf) +{ + struct ieee80211_assoc_response *resp = + (struct ieee80211_assoc_response *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; + unsigned long flags; + u16 capa = le16_to_cpu(resp->capability); + u16 status = le16_to_cpu(resp->status); + u16 assoc_id = le16_to_cpu(resp->aid); + + at76_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->info_element->data, + min((size_t) resp->info_element->len, + (sizeof(dev->obuf) - 1) / 2), '\0')); + if (dev->istate == REASSOCIATING) { + at76_assert(dev->new_bss != NULL); + if (dev->new_bss == NULL) + return; + + if (status == WLAN_STATUS_SUCCESS) { + struct bss_info *bptr = dev->new_bss; + bptr->assoc_id = assoc_id; + dev->istate = CONNECTED; + + at76_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; + at76_dbg(DBG_PROGRESS, "%s: reassociated to BSSID %s", + dev->netdev->name, mac2str(dev->bssid)); + schedule_work(&dev->work_assoc_done); + } else { + del_timer_sync(&dev->mgmt_timer); + dev->istate = JOINING; + schedule_work(&dev->work_join); } + } else { + info("%s: ReAssocResp in state %d ignored", + dev->netdev->name, dev->istate); + } +} - /* 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 */ +static void at76_rx_mgmt_disassoc(struct at76_priv *dev, + struct at76_rx_buffer *buf) +{ + struct ieee80211_disassoc *resp = + (struct ieee80211_disassoc *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; + + at76_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; + + at76_assert(dev->curr_bss != NULL); + if (dev->curr_bss == NULL) + return; + if (dev->istate == REASSOCIATING) { + at76_assert(dev->new_bss != NULL); + if (dev->new_bss == NULL) + return; + } + if (!compare_ether_addr(mgmt->addr3, dev->curr_bss->bssid) && + (!compare_ether_addr(dev->netdev->dev_addr, mgmt->addr1) || + is_broadcast_ether_addr(mgmt->addr1))) { + /* 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); + at76_iwevent_bss_disconnect(dev->netdev); + } + del_timer_sync(&dev->mgmt_timer); + dev->istate = JOINING; + schedule_work(&dev->work_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 */ +} + + +static void at76_rx_mgmt_auth(struct at76_priv *dev, struct at76_rx_buffer *buf) +{ + struct ieee80211_auth *resp = (struct ieee80211_auth *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; + int seq_nr = le16_to_cpu(resp->transaction); + int alg = le16_to_cpu(resp->algorithm); + int status = le16_to_cpu(resp->status); + + at76_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 == WLAN_AUTH_SHARED_KEY && seq_nr == 2) { + at76_dbg(DBG_RX_MGMT, "%s: AuthFrame challenge %s ...", + dev->netdev->name, + hex2str(dev->obuf, resp->info_element, + 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; + } + + at76_assert(dev->curr_bss != NULL); + if (dev->curr_bss == NULL) + return; + + if (!compare_ether_addr(mgmt->addr3, dev->curr_bss->bssid) && + !compare_ether_addr(dev->netdev->dev_addr, mgmt->addr1)) { + /* this is a AuthFrame from the BSS we are connected or + trying to connect to, directed to us */ + if (status != WLAN_STATUS_SUCCESS) { + del_timer_sync(&dev->mgmt_timer); + /* try to join next bss */ + dev->istate = JOINING; + schedule_work(&dev->work_join); + return; + } + + if (dev->auth_mode == WLAN_AUTH_OPEN || seq_nr == 4) { + dev->retries = ASSOC_RETRIES; + dev->istate = ASSOCIATING; + at76_assoc_req(dev, dev->curr_bss); + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", + __FUNCTION__, __LINE__); + mod_timer(&dev->mgmt_timer, jiffies + HZ); + return; + } + at76_assert(seq_nr == 2); + at76_auth_req(dev, dev->curr_bss, seq_nr + 1, resp->info_element); + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", + __FUNCTION__, __LINE__); + mod_timer(&dev->mgmt_timer, jiffies + HZ); } + /* else: ignore AuthFrames to other recipients */ +} - spin_unlock_irqrestore(&(dev->bss_list_spinlock), flags); - data->length = (curr_pos - extra); - data->flags = 0; +static void at76_rx_mgmt_deauth(struct at76_priv *dev, + struct at76_rx_buffer *buf) +{ + struct ieee80211_disassoc *resp = + (struct ieee80211_disassoc *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; - kfree(iwe); - return 0; + at76_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) { + at76_assert(dev->curr_bss != NULL); + if (dev->curr_bss == NULL) + return; + + if (!compare_ether_addr(mgmt->addr3, dev->curr_bss->bssid) && + (!compare_ether_addr(dev->netdev->dev_addr, mgmt->addr1) || + is_broadcast_ether_addr(mgmt->addr1))) { + /* this is a DeAuth from the BSS we are connected or + trying to connect to, directed to us or broadcasted */ + if (dev->istate == CONNECTED) { + at76_iwevent_bss_disconnect(dev->netdev); + } + dev->istate = JOINING; + schedule_work(&dev->work_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); + } } -static int at76_iw_handler_set_essid(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *data, char *extra) + +static void at76_rx_mgmt_beacon(struct at76_priv *dev, + struct at76_rx_buffer *buf) { - struct at76_priv *dev = netdev_priv(netdev); + /* beacon content */ + struct ieee80211_beacon *bdata = (struct ieee80211_beacon *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &bdata->header; - at76_dbg(DBG_IOCTL, "%s: SIOCSIWESSID - %s", netdev->name, extra); + /* length of var length beacon parameters */ + int varpar_len = min_t(int, le16_to_cpu(buf->wlength) - + sizeof(struct ieee80211_beacon), + BEACON_MAX_DATA_LENGTH); - if (data->flags) { - memcpy(dev->essid, extra, data->length); - dev->essid_size = data->length; - } else { - /* Use any SSID */ - dev->essid_size = 0; + struct list_head *lptr; + struct bss_info *match; /* entry matching addr3 with its bssid */ + int new_entry = 0; + int len; + struct ieee80211_info_element *tlv; + 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 */ + at76_assert(dev->curr_bss != NULL); + if (dev->curr_bss == NULL) + goto rx_mgmt_beacon_end; + if (!compare_ether_addr(dev->curr_bss->bssid, mgmt->addr3)) { + mod_timer(&dev->mgmt_timer, jiffies+BEACON_TIMEOUT*HZ); + dev->curr_bss->rssi = buf->rssi; + dev->beacons_received++; + goto rx_mgmt_beacon_end; + } } - return -EIWCOMMIT; -} + /* look if we have this BSS already in the list */ + match = NULL; -static int at76_iw_handler_get_essid(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); + 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 (!compare_ether_addr(bss_ptr->bssid, mgmt->addr3)) { + match = bss_ptr; + break; + } + } + } - 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; + if (match == NULL) { + /* haven't found the bss in the list */ + if ((match=kmalloc(sizeof(struct bss_info), GFP_ATOMIC)) == NULL) { + at76_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); } - at76_dbg(DBG_IOCTL, "%s: SIOCGIWESSID - %s", netdev->name, extra); + /* we either overwrite an existing entry or append a new one + match points to the entry in both cases */ - return 0; -} + match->capa = le16_to_cpu(bdata->capability); -static int at76_iw_handler_set_rate(struct net_device *netdev, - struct iw_request_info *info, - struct iw_param *bitrate, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int ret = -EIWCOMMIT; + /* while beacon_interval is not (!) */ + match->beacon_interval = le16_to_cpu(bdata->beacon_interval); - at76_dbg(DBG_IOCTL, "%s: SIOCSIWRATE - %d", netdev->name, bitrate->value); + match->rssi = buf->rssi; + match->link_qual = buf->link_quality; + match->noise_level = buf->noise_level; + memcpy(match->bssid, mgmt->addr3, ETH_ALEN); + at76_dbg(DBG_RX_BEACON, "%s: bssid %s", dev->netdev->name, + mac2str(match->bssid)); - 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; - } + tlv = bdata->info_element; - return ret; -} + /* This routine steps through the bdata->data array to tries to 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 types although MFIE_TYPE_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 optimizations. */ -static int at76_iw_handler_get_rate(struct net_device *netdev, - struct iw_request_info *info, - struct iw_param *bitrate, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int ret = 0; + while (keep_going && + ((&tlv->data[tlv->len] - (u8 *)bdata->info_element) <= varpar_len)) { - 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; - } + switch (tlv->id) { - bitrate->fixed = (dev->txrate != TX_RATE_AUTO); - bitrate->disabled = 0; + case MFIE_TYPE_SSID: + len = min_t(int, IW_ESSID_MAX_SIZE, tlv->len); + if (!have_ssid && ((new_entry) || + !at76_is_cloaked_ssid(tlv->data, 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. */ - at76_dbg(DBG_IOCTL, "%s: SIOCGIWRATE - %d", netdev->name, - bitrate->value); + match->ssid_len = len; + memcpy(match->ssid, tlv->data, len); + match->ssid[len] = '\0'; /* terminate the + string for + printing */ + at76_dbg(DBG_RX_BEACON, "%s: SSID - %s", + dev->netdev->name, match->ssid); + } + have_ssid = 1; + break; - return ret; -} + case MFIE_TYPE_RATES: + if (!have_rates) { + match->rates_len = + min_t(int, sizeof(match->rates), tlv->len); + memcpy(match->rates, tlv->data, + match->rates_len); + have_rates = 1; + at76_dbg(DBG_RX_BEACON, + "%s: SUPPORTED RATES %s", + dev->netdev->name, + hex2str(dev->obuf, tlv->data, + min_t(int, (sizeof(dev->obuf)-1)/2, + tlv->len), '\0')); + } + break; -static int at76_iw_handler_set_rts(struct net_device *netdev, - struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int ret = -EIWCOMMIT; - int rthr = rts->value; + case MFIE_TYPE_DS_SET: + if (!have_channel) { + match->channel = tlv->data[0]; + have_channel = 1; + at76_dbg(DBG_RX_BEACON, "%s: CHANNEL - %d", + dev->netdev->name, match->channel); + } + break; - at76_dbg(DBG_IOCTL, "%s: SIOCSIWRTS - value %d disabled %s", - netdev->name, rts->value, (rts->disabled) ? "true" : "false"); + case MFIE_TYPE_CF_SET: + case MFIE_TYPE_TIM: + case MFIE_TYPE_IBSS_SET: + default: + at76_dbg(DBG_RX_BEACON, "%s: beacon IE id %d len %d %s", + dev->netdev->name, tlv->id, tlv->len, + hex2str(dev->obuf, tlv->data, + min_t(int, (sizeof(dev->obuf)-1)/2, + tlv->len), '\0')); + break; + } - if (rts->disabled) - rthr = MAX_RTS_THRESHOLD; + /* advance to the next informational element */ + next_ie(&tlv); - if ((rthr < 0) || (rthr > MAX_RTS_THRESHOLD)) { - ret = -EINVAL; - } else { - dev->rts_threshold = rthr; + /* Optimization: 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 */ } - return ret; -} - -static int at76_iw_handler_get_rts(struct net_device *netdev, - struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - - rts->value = dev->rts_threshold; - rts->disabled = (rts->value >= MAX_RTS_THRESHOLD); - rts->fixed = 1; + at76_dbg(DBG_RX_BEACON, "%s: Finished processing beacon data", + dev->netdev->name); - at76_dbg(DBG_IOCTL, "%s: SIOCGIWRTS - value %d disabled %s", - netdev->name, rts->value, (rts->disabled) ? "true" : "false"); + match->last_rx = jiffies; /* record last rx of beacon */ - return 0; +rx_mgmt_beacon_end: + spin_unlock_irqrestore(&dev->bss_list_spinlock, flags); } -static int at76_iw_handler_set_frag(struct net_device *netdev, - struct iw_request_info *info, - struct iw_param *frag, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int ret = -EIWCOMMIT; - int fthr = frag->value; - - at76_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 */ - } +/* calc the link level from a given rx_buffer */ +static void at76_calc_level(struct at76_priv *dev, struct at76_rx_buffer *buf, + struct iw_quality *qual) +{ + int max_rssi = 42; /* just a guess for now, might be different for other chips */ - return ret; + qual->level = (buf->rssi * 100 / max_rssi); + if (qual->level > 100) + qual->level = 100; + qual->updated |= IW_QUAL_LEVEL_UPDATED; } -static int at76_iw_handler_get_frag(struct net_device *netdev, - struct iw_request_info *info, - struct iw_param *frag, char *extra) + +/* calc the link quality from a given rx_buffer */ +static void at76_calc_qual(struct at76_priv *dev, struct at76_rx_buffer *buf, + struct iw_quality* qual) { - struct at76_priv *dev = netdev_priv(netdev); + if ((dev->board_type == BOARDTYPE_503_INTERSIL_3861) || + (dev->board_type == BOARDTYPE_503_INTERSIL_3863)) { + qual->qual = buf->link_quality; + } else { + unsigned long msec; - frag->value = dev->frag_threshold; - frag->disabled = (frag->value >= MAX_FRAG_THRESHOLD); - frag->fixed = 1; + /* Update qual at most once a second */ + msec = jiffies_to_msecs(jiffies) - dev->beacons_last_qual; + if (msec < 1000) + return; - at76_dbg(DBG_IOCTL, "%s: SIOCGIWFRAG - value %d, disabled %s", - netdev->name, frag->value, (frag->disabled) ? "true" : "false"); + qual->qual = qual->level * dev->beacons_received * + dev->beacon_period / msec; - return 0; + dev->beacons_last_qual = jiffies_to_msecs(jiffies); + dev->beacons_received = 0; + } + qual->qual = (qual->qual > 100) ? 100 : qual->qual; + qual->updated |= IW_QUAL_QUAL_UPDATED; } -static int at76_iw_handler_get_txpow(struct net_device *netdev, - struct 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; - - at76_dbg(DBG_IOCTL, "%s: SIOCGIWTXPOW - txpow %d dBm", netdev->name, - power->value); - return 0; +/* calc the noise quality from a given rx_buffer */ +static void at76_calc_noise(struct at76_priv *dev, struct at76_rx_buffer *buf, + struct iw_quality *qual) +{ + qual->noise = 0; + qual->updated |= IW_QUAL_NOISE_INVALID; } -/* jal: short retry is handled by the firmware (at least 0.90.x), - while long retry is not (?) */ -static int at76_iw_handler_set_retry(struct net_device *netdev, - struct iw_request_info *info, - struct iw_param *retry, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int ret = -EIWCOMMIT; - at76_dbg(DBG_IOCTL, "%s: SIOCSIWRETRY disabled %d flags x%x val %d", - netdev->name, retry->disabled, retry->flags, retry->value); +static void at76_update_wstats(struct at76_priv *dev, + struct at76_rx_buffer *buf) +{ + struct iw_quality *qual = &dev->wstats.qual; - 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; + if (buf->rssi && dev->istate == CONNECTED) { + qual->updated = 0; + at76_calc_level(dev, buf, qual); + at76_calc_qual(dev, buf, qual); + at76_calc_noise(dev, buf, qual); } else { - ret = -EINVAL; + qual->qual = 0; + qual->level = 0; + qual->noise = 0; + qual->updated = IW_QUAL_ALL_INVALID; } - - return ret; } -/* adapted (ripped) from atmel.c */ -static int at76_iw_handler_get_retry(struct net_device *netdev, - struct iw_request_info *info, - struct iw_param *retry, char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - - at76_dbg(DBG_IOCTL, "%s: SIOCGIWRETRY", netdev->name); - - retry->disabled = 0; /* Can't be disabled */ - - - retry->flags = IW_RETRY_LIMIT; - retry->value = dev->short_retry_limit; - - return 0; -} -static int at76_iw_handler_set_encode(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *encoding, - char *extra) +static void at76_rx_mgmt(struct at76_priv *dev, struct at76_rx_buffer *buf) { - struct at76_priv *dev = netdev_priv(netdev); - int index = (encoding->flags & IW_ENCODE_INDEX) - 1; - int len = encoding->length; - - at76_dbg(DBG_IOCTL, "%s: SIOCSIWENCODE - enc.flags %08x " - "pointer %p len %d", netdev->name, encoding->flags, - encoding->pointer, encoding->length); - at76_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 == WLAN_AUTH_SHARED_KEY) ? "restricted" : "open"); - - /* take the old default key if index is invalid */ - if ((index < 0) || (index >= WEP_KEYS)) - index = dev->wep_key_id; - - if (len > 0) { - if (len > WEP_LARGE_KEY_LEN) - len = WEP_LARGE_KEY_LEN; + struct ieee80211_hdr_3addr *mgmt = + (struct ieee80211_hdr_3addr *)buf->packet; + u16 subtype = le16_to_cpu(mgmt->frame_ctl) & IEEE80211_FCTL_STYPE; - memset(dev->wep_keys[index], 0, WEP_KEY_LEN); - 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; + /* update wstats */ + if (dev->istate != INIT && dev->istate != SCANNING) { + /* jal: this is a dirty hack needed by Tim in ad-hoc mode */ + if (dev->iw_mode == IW_MODE_ADHOC || (dev->curr_bss != NULL && + !compare_ether_addr(mgmt->addr3, dev->curr_bss->bssid))) { + /* 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 */ + at76_update_wstats(dev, buf); + } } - dev->wep_key_id = index; - dev->wep_enabled = ((encoding->flags & IW_ENCODE_DISABLED) == 0); - - if (encoding->flags & IW_ENCODE_RESTRICTED) - dev->auth_mode = WLAN_AUTH_SHARED_KEY; - if (encoding->flags & IW_ENCODE_OPEN) - dev->auth_mode = WLAN_AUTH_OPEN; - - at76_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 == WLAN_AUTH_SHARED_KEY) ? "restricted" : "open"); - - return -EIWCOMMIT; -} + at76_dbg(DBG_RX_MGMT_CONTENT, "%s rx mgmt subtype x%x %s", + dev->netdev->name, subtype, + hex2str(dev->obuf, mgmt, + min((sizeof(dev->obuf) - 1) / 2, + (size_t) le16_to_cpu(buf->wlength)), '\0')); -static int at76_iw_handler_get_encode(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *encoding, - char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int index = (encoding->flags & IW_ENCODE_INDEX) - 1; + switch (subtype) { + case IEEE80211_STYPE_BEACON: + case IEEE80211_STYPE_PROBE_RESP: + at76_rx_mgmt_beacon(dev, buf); + break; - if ((index < 0) || (index >= WEP_KEYS)) - index = dev->wep_key_id; + case IEEE80211_STYPE_ASSOC_RESP: + at76_rx_mgmt_assoc(dev, buf); + break; - encoding->flags = - (dev->auth_mode == WLAN_AUTH_SHARED_KEY) ? - IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN; + case IEEE80211_STYPE_REASSOC_RESP: + at76_rx_mgmt_reassoc(dev, buf); + break; - if (!dev->wep_enabled) - encoding->flags |= IW_ENCODE_DISABLED; + case IEEE80211_STYPE_DISASSOC: + at76_rx_mgmt_disassoc(dev, buf); + break; - if (encoding->pointer) { - encoding->length = dev->wep_keys_len[index]; + case IEEE80211_STYPE_AUTH: + at76_rx_mgmt_auth(dev, buf); + break; - memcpy(extra, dev->wep_keys[index], dev->wep_keys_len[index]); + case IEEE80211_STYPE_DEAUTH: + at76_rx_mgmt_deauth(dev, buf); + break; - encoding->flags |= (index + 1); + default: + info("%s: mgmt, but not beacon, subtype = %x", + dev->netdev->name, subtype); } - at76_dbg(DBG_IOCTL, "%s: SIOCGIWENCODE - enc.flags %08x " - "pointer %p len %d", netdev->name, encoding->flags, - encoding->pointer, encoding->length); - at76_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 == WLAN_AUTH_SHARED_KEY) ? - "restricted" : "open"); - - return 0; + return; } -static int at76_iw_handler_set_power(struct net_device *netdev, - struct iw_request_info *info, - struct iw_param *prq, char *extra) + +static void at76_dbg_dumpbuf(const char *tag, const u8 *buf, int size) { - int err = -EIWCOMMIT; - struct at76_priv *dev = netdev_priv(netdev); + int i; - at76_dbg(DBG_IOCTL, "%s: SIOCSIWPOWER - disabled %s flags x%x value x%x", - netdev->name, (prq->disabled) ? "true" : "false", - prq->flags, prq->value); + if (!at76_debug) + return; - if (prq->disabled) { - dev->pm_mode = AT76_PM_OFF; - } else { - switch (prq->flags & IW_POWER_MODE) { - case IW_POWER_ALL_R: - case IW_POWER_ON: - break; - default: - err = -EINVAL; - goto out; - } - if (prq->flags & IW_POWER_PERIOD) { - dev->pm_period = prq->value; - } - if (prq->flags & IW_POWER_TIMEOUT) { - err = -EINVAL; - goto out; + for (i = 0; i < size; i++) { + if ((i % 8) == 0) { + if (i) + pr_debug("\n"); + pr_debug(__FILE__ ": %s: ", tag); } - dev->pm_mode = AT76_PM_ON; + pr_debug("%02x ", buf[i]); } -out: - return err; + pr_debug("\n"); } -static int at76_iw_handler_get_power(struct net_device *netdev, - struct iw_request_info *info, - struct iw_param *power, char *extra) + +/* 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 at76_ieee80211_to_eth(struct sk_buff *skb, int iw_mode) { - struct at76_priv *dev = netdev_priv(netdev); + struct ieee80211_hdr_3addr *i802_11_hdr; + struct ethhdr *eth_hdr_p; + u8 *src_addr; + u8 *dest_addr; + __be16 proto = 0; + int build_ethhdr = 1; - if ((power->disabled = (dev->pm_mode == AT76_PM_OFF))) - return 0; - else { - power->flags = IW_POWER_PERIOD; - power->value = dev->pm_period; + i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data; + + dbg("%s: ENTRY skb len %d data %s", __FUNCTION__, + skb->len, hex2str(dev->obuf, skb->data, + min((int)sizeof(dev->obuf) / 3, 64), ' ')); + + skb_pull(skb, sizeof(struct ieee80211_hdr_3addr)); + + 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 (!compare_ether_addr(eth_hdr_p->h_source, src_addr) && + !compare_ether_addr(eth_hdr_p->h_dest, dest_addr)) { + /* 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 = *(__be16 *) (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 = *(__be16 *)(skb->data); + skb_pull(skb, 2); +#endif /* IEEE_STANDARD */ } - power->flags |= IW_POWER_ALL_R; - at76_dbg(DBG_IOCTL, "%s: SIOCGIWPOWER - disabled %s flags x%x value x%x", - netdev->name, (power->disabled) ? "true" : "false", - power->flags, power->value); + eth_hdr_p = (struct ethhdr *)(skb->data-sizeof(struct ethhdr)); + skb->mac.raw=(unsigned char *)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; + } - return 0; + 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); + } + +#ifdef DEBUG + char da[3*ETH_ALEN], sa[3*ETH_ALEN]; +#endif + dbg("%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), ' ')); } -/******************************************************************************* - * Private IOCTLS +/* Adjust the skb to trim the hardware header and CRC, and set up skb->mac, + * skb->protocol, etc. */ -static int at76_iw_set_short_preamble(struct net_device *netdev, - struct iw_request_info *info, char *name, - char *extra) +static void at76_ieee80211_fixup(struct sk_buff *skb, int iw_mode) { - struct at76_priv *dev = netdev_priv(netdev); - int val = *((int *)name); - int ret = -EIWCOMMIT; + struct ieee80211_hdr_3addr *i802_11_hdr; + struct ethhdr *eth_hdr_p; + u8 *src_addr; + u8 *dest_addr; + __be16 proto = 0; - at76_dbg(DBG_IOCTL, "%s: AT76_SET_SHORT_PREAMBLE, %d", - netdev->name, val); + i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data; + skb_pull(skb, sizeof(struct ieee80211_hdr_3addr)); - 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; + 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 (!compare_ether_addr(eth_hdr_p->h_source, src_addr) && + !compare_ether_addr(eth_hdr_p->h_dest, dest_addr)) { + /* 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 = *(__be16 *)(skb->data - 2); } - return ret; + 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 = *(__be16 *)skb->data; + skb_pull(skb, 2); +#endif /* IEEE_STANDARD */ + } } -static int at76_iw_set_debug(struct net_device *netdev, - struct iw_request_info *info, - struct iw_point *data, char *extra) + +/* 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 *at76_check_for_rx_frags(struct at76_priv *dev) { - char *ptr; - u32 val; + struct sk_buff *skb = dev->rx_skb; + struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data; + struct ieee80211_hdr_3addr *i802_11_hdr = + (struct ieee80211_hdr_3addr *)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 at76_rx_buffer */ + int length = le16_to_cpu(buf->wlength) - dev->rx_data_fcs_len; - if (data->length > 0) { - val = simple_strtol(extra, &ptr, 0); + /* where does the data payload start in skb->data ? */ + u8 *data = (u8 *)i802_11_hdr + sizeof(struct ieee80211_hdr_3addr); - if (ptr == extra) { - val = DBG_DEFAULTS; - } + /* length of payload, excl. the trailing FCS */ + int data_len = length - (data - (u8 *)i802_11_hdr); - dbg("%s: AT76_SET_DEBUG input %d: %s -> x%x", - netdev->name, data->length, extra, val); - } else { - val = DBG_DEFAULTS; - } + int i; + struct rx_data_buf *bptr, *optr; + unsigned long oldest = ~0UL; - dbg("%s: AT76_SET_DEBUG, old 0x%x new 0x%x", - netdev->name, at76_debug, val); + at76_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')); - /* jal: some more output to pin down lockups */ - dbg("%s: netif running %d queue_stopped %d carrier_ok %d", - netdev->name, - netif_running(netdev), - netif_queue_stopped(netdev), netif_carrier_ok(netdev)); + at76_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); - at76_debug = val; + if (data_len < 0) { + /* make sure data starts in the buffer */ + info("%s: data frame too short", dev->netdev->name); + return NULL; + } - return 0; -} + if (fragnr == 0 && !(frame_ctl & IEEE80211_FCTL_MOREFRAGS)) { + /* unfragmented packet received */ + if (length < rx_copybreak && (skb = dev_alloc_skb(length)) != NULL) { + memcpy(skb_put(skb, length), + dev->rx_skb->data + AT76_RX_HDRLEN, length); + } else { + skb_pull(skb, AT76_RX_HDRLEN); + skb_trim(skb, length); + /* Use a new skb for the next receive */ + dev->rx_skb = NULL; + } -static int at76_iw_set_powersave_mode(struct net_device *netdev, - struct iw_request_info *info, char *name, - char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int val = *((int *)name); - int ret = -EIWCOMMIT; + at76_dbg(DBG_RX_FRAGS, "%s: unfragmented", dev->netdev->name); - at76_dbg(DBG_IOCTL, "%s: AT76_SET_POWERSAVE_MODE, %d (%s)", - netdev->name, val, - val == AT76_PM_OFF ? "active" : val == AT76_PM_ON ? "save" : - val == AT76_PM_SMART ? "smart save" : ""); - if (val < AT76_PM_OFF || val > AT76_PM_SMART) { - ret = -EINVAL; - } else { - dev->pm_mode = val; + return skb; } - return ret; -} + /* remove the at76_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 */ -static int at76_iw_set_scan_times(struct net_device *netdev, - struct iw_request_info *info, char *name, - char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int mint = *((int *)name); - int maxt = *((int *)name + 1); - int ret = -EIWCOMMIT; + at76_assert(length > AT76_RX_HDRLEN); + skb_pull(skb, AT76_RX_HDRLEN); + /* remove FCS at end */ + skb_trim(skb, length); - at76_dbg(DBG_IOCTL, "%s: AT76_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; + at76_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 (!compare_ether_addr(i802_11_hdr->addr2, bptr->sender)) + break; + else + if (optr == NULL) { + optr = bptr; + oldest = bptr->last_rx; + } else { + if (bptr->last_rx < oldest) + optr = bptr; + } } else { - dev->scan_min_time = mint; - dev->scan_max_time = maxt; + optr = bptr; + oldest = 0UL; } } - return ret; -} + if (i < NR_RX_DATA_BUF) { -static int at76_iw_set_scan_mode(struct net_device *netdev, - struct iw_request_info *info, char *name, - char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int val = *((int *)name); - int ret = -EIWCOMMIT; + at76_dbg(DBG_RX_FRAGS, "%s: %d. cacheentry (seq/frag=%d/%d) " + "matched sender addr", + dev->netdev->name, i, bptr->seqnr, bptr->fragnr); - at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_MODE - mode %s", - netdev->name, (val = SCAN_TYPE_ACTIVE) ? "active" : - (val = SCAN_TYPE_PASSIVE) ? "passive" : ""); + /* 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 ??? */ - if (val != SCAN_TYPE_ACTIVE && val != SCAN_TYPE_PASSIVE) { - ret = -EINVAL; + /* 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 & + IEEE80211_FCTL_MOREFRAGS)) { + /* this was the last fragment - send it */ + skb = bptr->skb; + bptr->skb = NULL; /* free the entry */ + at76_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 */ + at76_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 */ + at76_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 */ + at76_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 { - dev->scan_mode = val; - } - - return ret; -} - -static int at76_set_iroaming(struct at76_priv *dev, int onoff) -{ - int ret = 0; + /* if we didn't find a chain for the sender address optr + points either to the first free or the oldest entry */ - 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 = at76_set_mib(dev, &dev->mib_buf); - if (ret < 0) { - err("%s: set_mib (intl_roaming_enable) failed: %d", dev->netdev->name, ret); - } + if (fragnr != 0) { + /* this is not the begin of a fragment chain ... */ + at76_dbg(DBG_RX_FRAGS, "%s: no chain for non-first fragment (%d)", + dev->netdev->name, fragnr); + return NULL; + } + at76_assert(optr != NULL); + if (optr == NULL) + return NULL; - return ret; -} + if (optr->skb != NULL) { + /* swap the skb's */ + skb = optr->skb; + optr->skb = dev->rx_skb; + dev->rx_skb = skb; -static int at76_iw_set_intl_roaming(struct net_device *netdev, - struct iw_request_info *info, char *name, - char *extra) -{ - struct at76_priv *dev = netdev_priv(netdev); - int val = *((int *)name); - int ret = -EIWCOMMIT; + at76_dbg(DBG_RX_FRAGS, "%s: free old contents: sender %s seq/frag %d/%d", + dev->netdev->name, mac2str(optr->sender), + optr->seqnr, optr->fragnr); - at76_dbg(DBG_IOCTL, "%s: AT76_SET_INTL_ROAMING - mode %s", - netdev->name, (val == IR_OFF) ? "off" : - (val == IR_ON) ? "on" : ""); + } else { + /* take the skb from dev->rx_skb */ + optr->skb = dev->rx_skb; + dev->rx_skb = NULL; /* let at76_submit_rx_urb() allocate a new skb */ - if (val != IR_OFF && val != IR_ON) { - ret = -EINVAL; - } else { - if (dev->international_roaming != val) { - dev->international_roaming = val; - at76_set_iroaming(dev, val); + at76_dbg(DBG_RX_FRAGS, "%s: use a free entry", dev->netdev->name); } - } - - return ret; -} + memcpy(optr->sender, i802_11_hdr->addr2, ETH_ALEN); + optr->seqnr = seqnr; + optr->fragnr = 0; + optr->last_rx = jiffies; -/** - * set_monitor_mode - sets dev->netdev->type - */ -static void at76_set_monitor_mode(struct at76_priv *dev) -{ - if (dev->iw_mode == IW_MODE_MONITOR) { - at76_dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE ON", - dev->netdev->name); - dev->netdev->type = ARPHRD_IEEE80211_RADIOTAP; - } else { - at76_dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE OFF", - dev->netdev->name); - dev->netdev->type = ARPHRD_ETHER; + return NULL; } } -/******************************************************************************* - * structure that advertises the iw handlers of this driver - */ -static const iw_handler at76_handlers[] = +/* rx interrupt: we expect the complete data buffer in dev->rx_skb */ +static void at76_rx_data(struct at76_priv *dev) { - [SIOCSIWCOMMIT-SIOCIWFIRST] = (iw_handler) at76_iw_handler_commit, - [SIOCGIWNAME -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_name, - [SIOCSIWFREQ -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_freq, - [SIOCGIWFREQ -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_freq, - [SIOCSIWMODE -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_mode, - [SIOCGIWMODE -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_mode, - [SIOCGIWRANGE -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_range, - [SIOCSIWSPY -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_spy, - [SIOCGIWSPY -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_spy, - [SIOCSIWTHRSPY-SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_thrspy, - [SIOCGIWTHRSPY-SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_thrspy, - [SIOCSIWAP -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_wap, - [SIOCGIWAP -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_wap, - [SIOCSIWSCAN -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_scan, - [SIOCGIWSCAN -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_scan, - [SIOCSIWESSID -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_essid, - [SIOCGIWESSID -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_essid, - [SIOCSIWRATE -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_rate, - [SIOCGIWRATE -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_rate, - [SIOCSIWRTS -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_rts, - [SIOCGIWRTS -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_rts, - [SIOCSIWFRAG -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_frag, - [SIOCGIWFRAG -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_frag, - [SIOCGIWTXPOW -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_txpow, - [SIOCSIWRETRY -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_retry, - [SIOCGIWRETRY -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_retry, - [SIOCSIWENCODE-SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_encode, - [SIOCGIWENCODE-SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_encode, - [SIOCSIWPOWER -SIOCIWFIRST] = (iw_handler) at76_iw_handler_set_power, - [SIOCGIWPOWER -SIOCIWFIRST] = (iw_handler) at76_iw_handler_get_power, -}; - -#define AT76_SET_PRIV(h, f) [h - SIOCIWFIRSTPRIV] = (iw_handler) f - -/*structure that advertises the private iw handlers of this driver */ -static const iw_handler at76_priv_handlers[] = { - AT76_SET_PRIV(AT76_SET_SHORT_PREAMBLE, at76_iw_set_short_preamble), - AT76_SET_PRIV(AT76_SET_DEBUG, at76_iw_set_debug), - AT76_SET_PRIV(AT76_SET_POWERSAVE_MODE, at76_iw_set_powersave_mode), - AT76_SET_PRIV(AT76_SET_SCAN_TIMES, at76_iw_set_scan_times), - AT76_SET_PRIV(AT76_SET_SCAN_MODE, at76_iw_set_scan_mode), - AT76_SET_PRIV(AT76_SET_INTL_ROAMING, at76_iw_set_intl_roaming), -}; + struct net_device *netdev = dev->netdev; + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb = dev->rx_skb; + struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data; + struct ieee80211_hdr_3addr *i802_11_hdr; + int length = le16_to_cpu(buf->wlength); -static const struct iw_handler_def at76_handler_def = -{ - .num_standard = ARRAY_SIZE(at76_handlers), - .num_private = ARRAY_SIZE(at76_priv_handlers), - .num_private_args = ARRAY_SIZE(at76_priv_args), - .standard = at76_handlers, - .private = at76_priv_handlers, - .private_args = at76_priv_args, - .get_wireless_stats = at76_get_wireless_stats, -}; + at76_dbg(DBG_RX_DATA, "%s received data packet:", netdev->name); + if (at76_debug & DBG_RX_DATA) { + at76_dbg_dumpbuf(" rxhdr", skb->data, AT76_RX_HDRLEN); + } + if (at76_debug & DBG_RX_DATA_CONTENT) + at76_dbg_dumpbuf("packet", skb->data + AT76_RX_HDRLEN, length); + if ((skb = at76_check_for_rx_frags(dev)) == NULL) + return; -static void at76_ethtool_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *info) -{ - struct at76_priv *dev = netdev_priv(netdev); + /* if an skb is returned, the at76_rx_buffer and the FCS is already removed */ + i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data; - strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1); + skb->dev = netdev; + skb->ip_summed = CHECKSUM_NONE; /* TODO: should check CRC */ - strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); - info->version[sizeof(info->version) - 1] = '\0'; + if (i802_11_hdr->addr1[0] & 1) { + if (!compare_ether_addr(i802_11_hdr->addr1, netdev->broadcast)) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } else if (compare_ether_addr(i802_11_hdr->addr1, netdev->dev_addr)) { + skb->pkt_type = PACKET_OTHERHOST; + } - snprintf(info->bus_info, sizeof(info->bus_info) - 1, "usb%d:%d", - dev->udev->bus->busnum, dev->udev->devnum); + if (netdev->type == ARPHRD_ETHER) { + at76_ieee80211_to_eth(skb, dev->iw_mode); + } else { + at76_ieee80211_fixup(skb, dev->iw_mode); + } - 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); -} + netdev->last_rx = jiffies; + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += length; -static u32 at76_ethtool_get_link(struct net_device *netdev) -{ - struct at76_priv *dev = netdev_priv(netdev); - return dev->istate == CONNECTED; + return; } -static struct ethtool_ops at76_ethtool_ops = { - .get_drvinfo = at76_ethtool_get_drvinfo, - .get_link = at76_ethtool_get_link, -}; -static void at76_delete_device(struct at76_priv *dev) +static void at76_rx_monitor_mode(struct at76_priv *dev) { - int i; + struct at76_rx_radiotap *rt; + u8 *payload; + int skblen; + struct net_device *netdev = dev->netdev; + struct at76_rx_buffer *buf = + (struct at76_rx_buffer *)dev->rx_skb->data; + /* length including the IEEE802.11 header and the trailing FCS, + but not at76_rx_buffer */ + int length = le16_to_cpu(buf->wlength); + struct sk_buff *skb = dev->rx_skb; + struct net_device_stats *stats = &dev->stats; - if (!dev) + if (length < dev->rx_data_fcs_len) { + /* buffer contains no data */ + at76_dbg(DBG_MONITOR_MODE, "%s: MONITOR MODE: rx skb without data", + dev->netdev->name); return; + } - /* signal to _stop() that the device is gone */ - dev->device_unplugged = 1; - - at76_dbg(DBG_PROC_ENTRY, "%s: ENTER",__FUNCTION__); + skblen = sizeof(struct at76_rx_radiotap) + length; - if (dev->netdev_registered) { - unregister_netdev(dev->netdev); + if ((skb = dev_alloc_skb(skblen)) == NULL) { + err("%s: MONITOR MODE: dev_alloc_skb for radiotap header " + "returned NULL", dev->netdev->name); + return; } - usb_put_dev(dev->udev); + skb_put(skb, skblen); - /* assuming we used keventd, it must quiesce too */ - flush_scheduled_work(); + rt = (struct at76_rx_radiotap *)skb->data; + payload = skb->data + sizeof(struct at76_rx_radiotap); - if (dev->bulk_out_buffer != NULL) - kfree(dev->bulk_out_buffer); + rt->rt_hdr.it_version = 0; + rt->rt_hdr.it_pad = 0; + rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct at76_rx_radiotap)); + rt->rt_hdr.it_present = cpu_to_le32(AT76_RX_RADIOTAP_PRESENT); - kfree(dev->ctrl_buffer); + rt->rt_tsft = cpu_to_le64(le32_to_cpu(buf->rx_time)); + rt->rt_rate = hw_rates[buf->rx_rate] & (~0x80); + rt->rt_signal = buf->rssi; + rt->rt_noise = buf->noise_level; + rt->rt_flags = 0; + if (buf->fragmentation) + rt->rt_flags |= IEEE80211_RADIOTAP_F_FRAG; + if (dev->rx_data_fcs_len) + rt->rt_flags |= IEEE80211_RADIOTAP_F_FCS; - 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); - } + memcpy(payload, buf->packet, length); + 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); - at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __FUNCTION__); + netdev->last_rx = jiffies; + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += length; +} - if (dev->rx_skb != NULL) - kfree_skb(dev->rx_skb); - at76_free_bss_list(dev); - del_timer_sync(&dev->bss_list_timer); +/** + * at76_iwspy_update - check if we spy on the sender address of buf and update stats + */ +static void at76_iwspy_update(struct at76_priv *dev, struct at76_rx_buffer *buf) +{ + struct ieee80211_hdr_3addr *hdr = + (struct ieee80211_hdr_3addr *)buf->packet; + struct iw_quality qual; - if (dev->istate == CONNECTED) { - at76_iwevent_bss_disconnect(dev->netdev); - } + /* We can only set the level here */ + qual.updated = IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; + qual.level = 0; + qual.noise = 0; + at76_calc_level(dev, buf, &qual); - 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; - } - at76_dbg(DBG_PROC_ENTRY, "%s: before freeing dev/netdev", __FUNCTION__); - free_netdev(dev->netdev); /* dev is in netdev */ + spin_lock_bh(&(dev->spy_spinlock)); - at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __FUNCTION__); + if (dev->spy_data.spy_number > 0) { + wireless_spy_update(dev->netdev, hdr->addr2, &qual); + } + spin_unlock_bh(&(dev->spy_spinlock)); } -static int at76_alloc_urbs(struct at76_priv *dev) + +static void at76_rx_tasklet(unsigned long param) { - struct usb_interface *interface = dev->interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_device *udev = dev->udev; - int i, buffer_size; - struct usb_host_interface *iface_desc; + struct at76_priv *dev = (struct at76_priv *)param; + struct urb *urb; + struct net_device *netdev; + struct at76_rx_buffer *buf; + struct ieee80211_hdr_3addr *i802_11_hdr; + u16 frame_ctl; - at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __FUNCTION__); + if (!dev) + return; + urb = dev->rx_urb; + netdev = dev->netdev; - at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __FUNCTION__, - interface->altsetting[0].desc.bNumEndpoints); + if (dev->device_unplugged) { + at76_dbg(DBG_DEVSTART, "device unplugged"); + if (urb) + at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); + return; + } - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { - endpoint = &iface_desc->endpoint[i].desc; - at76_dbg(DBG_URB, "%s: %d. endpoint: addr x%x attr x%x", - __FUNCTION__, - i, endpoint->bEndpointAddress, endpoint->bmAttributes); + if (!urb || !dev->rx_skb || !netdev || !dev->rx_skb->data) + return; - if ((endpoint->bEndpointAddress & 0x80) && - ((endpoint->bmAttributes & 3) == 0x02)) { - /* we found a bulk in endpoint */ + buf = (struct at76_rx_buffer *)dev->rx_skb->data; - dev->read_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->read_urb) { - err("No free urbs available"); - return -ENOMEM; - } - dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; - } + if (!buf) + return; - if (((endpoint->bEndpointAddress & 0x80) == 0x00) && - ((endpoint->bmAttributes & 3) == 0x02)) { - /* we found a bulk out endpoint */ - dev->write_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->write_urb) { - err("no free urbs available"); - return -ENOMEM; - } - buffer_size = sizeof(struct at76_tx_buffer) + - MAX_PADDING_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 -ENOMEM; - } - usb_fill_bulk_urb(dev->write_urb, udev, - usb_sndbulkpipe(udev, - endpoint->bEndpointAddress), - dev->bulk_out_buffer, buffer_size, - at76_write_bulk_callback, dev); + i802_11_hdr = (struct ieee80211_hdr_3addr *)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)) { + at76_dbg(DBG_URB, + "%s %s: - nonzero read bulk status received: %d", + __FUNCTION__, netdev->name, urb->status); + goto no_more_urb; } + return; } - dev->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->ctrl_urb) { - err("no free urbs available"); - return -ENOMEM; + at76_dbg(DBG_RX_ATMEL_HDR, "%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, i802_11_hdr, + min((int)(sizeof(dev->obuf)-1)/2,48),'\0')); + if (dev->istate == MONITORING) { + at76_rx_monitor_mode(dev); + goto finish; } - dev->ctrl_buffer = kmalloc(1024, GFP_KERNEL); - if (!dev->ctrl_buffer) { - err("couldn't allocate ctrl_buffer"); - return -ENOMEM; + + /* there is a new bssid around, accept it: */ + if (buf->newbss && dev->iw_mode == IW_MODE_ADHOC) { + at76_dbg(DBG_PROGRESS, "%s: rx newbss", netdev->name); + schedule_work(&dev->work_new_bss); } - at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __FUNCTION__); + switch (frame_ctl & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_DATA: + at76_rx_data(dev); + break; - return 0; + case IEEE80211_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 !) */ + + at76_iwspy_update(dev, buf); + + at76_rx_mgmt(dev, buf); + break; + + case IEEE80211_FTYPE_CTL: + at76_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); + } + finish: + at76_submit_rx_urb(dev); + no_more_urb: + return; } + static struct at76_priv *at76_alloc_new_device(struct usb_device *udev, int board_type) { @@ -5912,122 +6153,6 @@ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev, } -/** - * at76_init_new_device - continue device initialization after firmware download - * - * FIXME: We may have to move the register_netdev into at76_alloc_new_device, - * because hotplug may try to configure the netdev _before_ (or parallel to) - * the download of firmware - */ -static int at76_init_new_device(struct at76_priv *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]; - - at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints", - dev->interface->cur_altsetting->desc.bNumEndpoints); - - if ((ret = at76_alloc_urbs(dev)) < 0) - goto error; - - /* get firmware version */ - ret = at76_get_mib(dev->udev, MIB_FW_VERSION, &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"); - if(ret >= 0) - ret = -ENODEV; - 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("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 = at76_get_hw_config(dev); - if (ret < 0) { - err("could not get MAC address"); - goto error; - } - - dev->domain = at76_get_reg_domain(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; - memset(dev->essid, 0, IW_ESSID_MAX_SIZE); - dev->rts_threshold = DEF_RTS_THRESHOLD; - dev->frag_threshold = DEF_FRAG_THRESHOLD; - dev->short_retry_limit = DEF_SHORT_RETRY_LIMIT; - dev->txrate = TX_RATE_AUTO; - dev->preamble_type = preamble_type; - dev->beacon_period = 100; - dev->beacons_last_qual = jiffies_to_msecs(jiffies); - dev->auth_mode = auth_mode ? WLAN_AUTH_SHARED_KEY : WLAN_AUTH_OPEN; - 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 = at76_open; - netdev->stop = at76_stop; - netdev->get_stats = at76_get_stats; - netdev->ethtool_ops = &at76_ethtool_ops; - - /* Add pointers to enable iwspy support. */ - dev->wireless_data.spy_data = &dev->spy_data; - netdev->wireless_data = &dev->wireless_data; - - netdev->hard_start_xmit = at76_tx; - netdev->tx_timeout = at76_tx_timeout; - netdev->watchdog_timeo = 2 * HZ; - netdev->wireless_handlers = &at76_handler_def; - netdev->set_multicast_list = at76_set_multicast; - netdev->set_mac_address = at76_set_mac_address; - - ret = register_netdev(dev->netdev); - if (ret) { - err("unable to register netdevice %s (status %d)!", - dev->netdev->name, ret); - goto error; - } - info("registered %s", dev->netdev->name); - dev->netdev_registered = 1; - - /* we let this timer run the whole time this driver instance lives */ - mod_timer(&dev->bss_list_timer, jiffies + BSS_LIST_TIMEOUT); - - return 0; - error: - at76_delete_device(dev); - return ret; -} - - /* Parse the firmware image */ static int at76_parse_fw(struct at76_priv *dev, u8 *fw_data, int fw_size, int board_type) @@ -6069,6 +6194,7 @@ static int at76_parse_fw(struct at76_priv *dev, u8 *fw_data, int fw_size, return 0; } + static int at76_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -6179,6 +6305,7 @@ static int at76_probe(struct usb_interface *interface, return ret; } + static void at76_disconnect(struct usb_interface *interface) { struct at76_priv *priv; @@ -6191,6 +6318,7 @@ static void at76_disconnect(struct usb_interface *interface) info(DRIVER_NAME " disconnected"); } + /* structure for registering this driver with the USB subsystem */ static struct usb_driver at76_driver = { .name = DRIVER_NAME, @@ -6199,6 +6327,7 @@ static struct usb_driver at76_driver = { .id_table = dev_table, }; + static int __init at76_mod_init(void) { int result; @@ -6215,6 +6344,7 @@ static int __init at76_mod_init(void) return result; } + static void __exit at76_mod_exit(void) { int i; @@ -6228,6 +6358,7 @@ static void __exit at76_mod_exit(void) led_trigger_unregister_simple(ledtrig_tx); } + module_param_named(debug, at76_debug, int, 0600); MODULE_PARM_DESC(debug, "Debugging level"); module_param(rx_copybreak, int, 0400); -- cgit v1.2.3