Commit 9afac70a authored by David Kilroy's avatar David Kilroy Committed by John W. Linville
Browse files

orinoco: add orinoco_usb driver



This driver uses the core orinoco modules for the bulk of
the functionality. The low level hermes routines (for local bus
cards) are replaced, the driver supplies its own ndo_xmit_start
function, and locking is done with the _bh variant.

Some recent functionality is not available to the USB cards yet
(firmware loading and WPA).

Out-of-tree driver originally written by Manuel Estrada Sainz.

Thanks to Mark Davis for supplying hardware to test the updates.
Signed-off-by: default avatarDavid Kilroy <kilroyd@googlemail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent bcad6e80
......@@ -132,3 +132,10 @@ config PCMCIA_SPECTRUM
This driver requires firmware download on startup. Utilities
for downloading Symbol firmware are available at
<http://sourceforge.net/projects/orinoco/>
config ORINOCO_USB
tristate "Agere Orinoco USB support"
depends on USB && HERMES
select FW_LOADER
---help---
This driver is for USB versions of the Agere Orinoco card.
......@@ -11,6 +11,7 @@ obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o
obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o
obj-$(CONFIG_NORTEL_HERMES) += orinoco_nortel.o
obj-$(CONFIG_PCMCIA_SPECTRUM) += spectrum_cs.o
obj-$(CONFIG_ORINOCO_USB) += orinoco_usb.o
# Orinoco should be endian clean.
ccflags-y += -D__CHECK_ENDIAN__
......@@ -407,6 +407,7 @@ typedef struct hermes {
#define HERMES_32BIT_REGSPACING 1
u16 inten; /* Which interrupts should be enabled? */
const struct hermes_ops *ops;
void *priv;
} hermes_t;
/* Register access convenience macros */
......
......@@ -797,7 +797,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
stats->rx_dropped++;
}
static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
{
struct orinoco_private *priv = ndev_priv(dev);
struct net_device_stats *stats = &priv->stats;
......@@ -918,6 +918,7 @@ update_stats:
out:
kfree(desc);
}
EXPORT_SYMBOL(__orinoco_ev_rx);
static void orinoco_rx(struct net_device *dev,
struct hermes_rx_descriptor *desc,
......@@ -1359,7 +1360,7 @@ static void orinoco_process_scan_results(struct work_struct *work)
spin_unlock_irqrestore(&priv->scan_lock, flags);
}
static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
{
struct orinoco_private *priv = ndev_priv(dev);
u16 infofid;
......@@ -1577,6 +1578,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
return;
}
EXPORT_SYMBOL(__orinoco_ev_info);
static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
{
......
......@@ -197,6 +197,9 @@ extern int orinoco_up(struct orinoco_private *priv);
extern void orinoco_down(struct orinoco_private *priv);
extern irqreturn_t orinoco_interrupt(int irq, void *dev_id);
extern void __orinoco_ev_info(struct net_device *dev, hermes_t *hw);
extern void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw);
/* Common ndo functions exported for reuse by orinoco_usb */
int orinoco_open(struct net_device *dev);
int orinoco_stop(struct net_device *dev);
......
/*
* USB Orinoco driver
*
* Copyright (c) 2003 Manuel Estrada Sainz
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in
* which case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
*
* Queueing code based on linux-wlan-ng 0.2.1-pre5
*
* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
*
* The license is the same as above.
*
* Initialy based on USB Skeleton driver - 0.7
*
* Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* NOTE: The original USB Skeleton driver is GPL, but all that code is
* gone so MPL/GPL applies.
*/
#define DRIVER_NAME "orinoco_usb"
#define PFX DRIVER_NAME ": "
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/smp_lock.h>
#include <linux/usb.h>
#include <linux/timer.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
#include <linux/firmware.h>
#include "orinoco.h"
#ifndef URB_ASYNC_UNLINK
#define URB_ASYNC_UNLINK 0
#endif
/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2)
struct header_struct {
/* 802.3 */
u8 dest[ETH_ALEN];
u8 src[ETH_ALEN];
__be16 len;
/* 802.2 */
u8 dsap;
u8 ssap;
u8 ctrl;
/* SNAP */
u8 oui[3];
__be16 ethertype;
} __attribute__ ((packed));
struct ez_usb_fw {
u16 size;
const u8 *code;
};
static struct ez_usb_fw firmware = {
.size = 0,
.code = NULL,
};
#ifdef CONFIG_USB_DEBUG
static int debug = 1;
#else
static int debug;
#endif
/* Debugging macros */
#undef dbg
#define dbg(format, arg...) \
do { if (debug) printk(KERN_DEBUG PFX "%s: " format "\n", \
__func__ , ## arg); } while (0)
#undef err
#define err(format, arg...) \
do { printk(KERN_ERR PFX format "\n", ## arg); } while (0)
/* Module paramaters */
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug enabled or not");
MODULE_FIRMWARE("orinoco_ezusb_fw");
/*
* Under some conditions, the card gets stuck and stops paying attention
* to the world (i.e. data communication stalls) until we do something to
* it. Sending an INQ_TALLIES command seems to be enough and should be
* harmless otherwise. This behaviour has been observed when using the
* driver on a systemimager client during installation. In the past a
* timer was used to send INQ_TALLIES commands when there was no other
* activity, but it was troublesome and was removed.
*/
#define USB_COMPAQ_VENDOR_ID 0x049f /* Compaq Computer Corp. */
#define USB_COMPAQ_WL215_ID 0x001f /* Compaq WL215 USB Adapter */
#define USB_COMPAQ_W200_ID 0x0076 /* Compaq W200 USB Adapter */
#define USB_HP_WL215_ID 0x0082 /* Compaq WL215 USB Adapter */
#define USB_MELCO_VENDOR_ID 0x0411
#define USB_BUFFALO_L11_ID 0x0006 /* BUFFALO WLI-USB-L11 */
#define USB_BUFFALO_L11G_WR_ID 0x000B /* BUFFALO WLI-USB-L11G-WR */
#define USB_BUFFALO_L11G_ID 0x000D /* BUFFALO WLI-USB-L11G */
#define USB_LUCENT_VENDOR_ID 0x047E /* Lucent Technologies */
#define USB_LUCENT_ORINOCO_ID 0x0300 /* Lucent/Agere Orinoco USB Client */
#define USB_AVAYA8_VENDOR_ID 0x0D98
#define USB_AVAYAE_VENDOR_ID 0x0D9E
#define USB_AVAYA_WIRELESS_ID 0x0300 /* Avaya Wireless USB Card */
#define USB_AGERE_VENDOR_ID 0x0D4E /* Agere Systems */
#define USB_AGERE_MODEL0801_ID 0x1000 /* Wireless USB Card Model 0801 */
#define USB_AGERE_MODEL0802_ID 0x1001 /* Wireless USB Card Model 0802 */
#define USB_AGERE_REBRANDED_ID 0x047A /* WLAN USB Card */
#define USB_ELSA_VENDOR_ID 0x05CC
#define USB_ELSA_AIRLANCER_ID 0x3100 /* ELSA AirLancer USB-11 */
#define USB_LEGEND_VENDOR_ID 0x0E7C
#define USB_LEGEND_JOYNET_ID 0x0300 /* Joynet WLAN USB Card */
#define USB_SAMSUNG_VENDOR_ID 0x04E8
#define USB_SAMSUNG_SEW2001U1_ID 0x5002 /* Samsung SEW-2001u Card */
#define USB_SAMSUNG_SEW2001U2_ID 0x5B11 /* Samsung SEW-2001u Card */
#define USB_SAMSUNG_SEW2003U_ID 0x7011 /* Samsung SEW-2003U Card */
#define USB_IGATE_VENDOR_ID 0x0681
#define USB_IGATE_IGATE_11M_ID 0x0012 /* I-GATE 11M USB Card */
#define USB_FUJITSU_VENDOR_ID 0x0BF8
#define USB_FUJITSU_E1100_ID 0x1002 /* connect2AIR WLAN E-1100 USB */
#define USB_2WIRE_VENDOR_ID 0x1630
#define USB_2WIRE_WIRELESS_ID 0xff81 /* 2Wire Wireless USB adapter */
#define EZUSB_REQUEST_FW_TRANS 0xA0
#define EZUSB_REQUEST_TRIGER 0xAA
#define EZUSB_REQUEST_TRIG_AC 0xAC
#define EZUSB_CPUCS_REG 0x7F92
#define EZUSB_RID_TX 0x0700
#define EZUSB_RID_RX 0x0701
#define EZUSB_RID_INIT1 0x0702
#define EZUSB_RID_ACK 0x0710
#define EZUSB_RID_DOCMD 0x0860
/* Recognize info frames */
#define EZUSB_IS_INFO(id) ((id >= 0xF000) && (id <= 0xF2FF))
#define EZUSB_MAGIC 0x0210
#define EZUSB_FRAME_DATA 1
#define EZUSB_FRAME_CONTROL 2
#define DEF_TIMEOUT (3*HZ)
#define BULK_BUF_SIZE 2048
#define FW_BUF_SIZE 64
#define FW_VAR_OFFSET_PTR 0x359
#define FW_VAR_VALUE 0
#define FW_HOLE_START 0x100
#define FW_HOLE_END 0x300
struct ezusb_packet {
__le16 magic; /* 0x0210 */
u8 req_reply_count;
u8 ans_reply_count;
__le16 frame_type; /* 0x01 for data frames, 0x02 otherwise */
__le16 size; /* transport size */
__le16 crc; /* CRC up to here */
__le16 hermes_len;
__le16 hermes_rid;
u8 data[0];
} __attribute__ ((packed));
/* Table of devices that work or may work with this driver */
static struct usb_device_id ezusb_table[] = {
{USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_WL215_ID)},
{USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_HP_WL215_ID)},
{USB_DEVICE(USB_COMPAQ_VENDOR_ID, USB_COMPAQ_W200_ID)},
{USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11_ID)},
{USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_WR_ID)},
{USB_DEVICE(USB_MELCO_VENDOR_ID, USB_BUFFALO_L11G_ID)},
{USB_DEVICE(USB_LUCENT_VENDOR_ID, USB_LUCENT_ORINOCO_ID)},
{USB_DEVICE(USB_AVAYA8_VENDOR_ID, USB_AVAYA_WIRELESS_ID)},
{USB_DEVICE(USB_AVAYAE_VENDOR_ID, USB_AVAYA_WIRELESS_ID)},
{USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0801_ID)},
{USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_MODEL0802_ID)},
{USB_DEVICE(USB_ELSA_VENDOR_ID, USB_ELSA_AIRLANCER_ID)},
{USB_DEVICE(USB_LEGEND_VENDOR_ID, USB_LEGEND_JOYNET_ID)},
{USB_DEVICE_VER(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U1_ID,
0, 0)},
{USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2001U2_ID)},
{USB_DEVICE(USB_SAMSUNG_VENDOR_ID, USB_SAMSUNG_SEW2003U_ID)},
{USB_DEVICE(USB_IGATE_VENDOR_ID, USB_IGATE_IGATE_11M_ID)},
{USB_DEVICE(USB_FUJITSU_VENDOR_ID, USB_FUJITSU_E1100_ID)},
{USB_DEVICE(USB_2WIRE_VENDOR_ID, USB_2WIRE_WIRELESS_ID)},
{USB_DEVICE(USB_AGERE_VENDOR_ID, USB_AGERE_REBRANDED_ID)},
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ezusb_table);
/* Structure to hold all of our device specific stuff */
struct ezusb_priv {
struct usb_device *udev;
struct net_device *dev;
struct mutex mtx;
spinlock_t req_lock;
struct list_head req_pending;
struct list_head req_active;
spinlock_t reply_count_lock;
u16 hermes_reg_fake[0x40];
u8 *bap_buf;
struct urb *read_urb;
int read_pipe;
int write_pipe;
u8 reply_count;
};
enum ezusb_state {
EZUSB_CTX_START,
EZUSB_CTX_QUEUED,
EZUSB_CTX_REQ_SUBMITTED,
EZUSB_CTX_REQ_COMPLETE,
EZUSB_CTX_RESP_RECEIVED,
EZUSB_CTX_REQ_TIMEOUT,
EZUSB_CTX_REQ_FAILED,
EZUSB_CTX_RESP_TIMEOUT,
EZUSB_CTX_REQSUBMIT_FAIL,
EZUSB_CTX_COMPLETE,
};
struct request_context {
struct list_head list;
atomic_t refcount;
struct completion done; /* Signals that CTX is dead */
int killed;
struct urb *outurb; /* OUT for req pkt */
struct ezusb_priv *upriv;
struct ezusb_packet *buf;
int buf_length;
struct timer_list timer; /* Timeout handling */
enum ezusb_state state; /* Current state */
/* the RID that we will wait for */
u16 out_rid;
u16 in_rid;
};
/* Forward declarations */
static void ezusb_ctx_complete(struct request_context *ctx);
static void ezusb_req_queue_run(struct ezusb_priv *upriv);
static void ezusb_bulk_in_callback(struct urb *urb);
static inline u8 ezusb_reply_inc(u8 count)
{
if (count < 0x7F)
return count + 1;
else
return 1;
}
static void ezusb_request_context_put(struct request_context *ctx)
{
if (!atomic_dec_and_test(&ctx->refcount))
return;
WARN_ON(!ctx->done.done);
BUG_ON(ctx->outurb->status == -EINPROGRESS);
BUG_ON(timer_pending(&ctx->timer));
usb_free_urb(ctx->outurb);
kfree(ctx->buf);
kfree(ctx);
}
static inline void ezusb_mod_timer(struct ezusb_priv *upriv,
struct timer_list *timer,
unsigned long expire)
{
if (!upriv->udev)
return;
mod_timer(timer, expire);
}
static void ezusb_request_timerfn(u_long _ctx)
{
struct request_context *ctx = (void *) _ctx;
ctx->outurb->transfer_flags |= URB_ASYNC_UNLINK;
if (usb_unlink_urb(ctx->outurb) == -EINPROGRESS) {
ctx->state = EZUSB_CTX_REQ_TIMEOUT;
} else {
ctx->state = EZUSB_CTX_RESP_TIMEOUT;
dbg("couldn't unlink");
atomic_inc(&ctx->refcount);
ctx->killed = 1;
ezusb_ctx_complete(ctx);
ezusb_request_context_put(ctx);
}
};
static struct request_context *ezusb_alloc_ctx(struct ezusb_priv *upriv,
u16 out_rid, u16 in_rid)
{
struct request_context *ctx;
ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
return NULL;
memset(ctx, 0, sizeof(*ctx));
ctx->buf = kmalloc(BULK_BUF_SIZE, GFP_ATOMIC);
if (!ctx->buf) {
kfree(ctx);
return NULL;
}
ctx->outurb = usb_alloc_urb(0, GFP_ATOMIC);
if (!ctx->outurb) {
kfree(ctx->buf);
kfree(ctx);
return NULL;
}
ctx->upriv = upriv;
ctx->state = EZUSB_CTX_START;
ctx->out_rid = out_rid;
ctx->in_rid = in_rid;
atomic_set(&ctx->refcount, 1);
init_completion(&ctx->done);
init_timer(&ctx->timer);
ctx->timer.function = ezusb_request_timerfn;
ctx->timer.data = (u_long) ctx;
return ctx;
}
/* Hopefully the real complete_all will soon be exported, in the mean
* while this should work. */
static inline void ezusb_complete_all(struct completion *comp)
{
complete(comp);
complete(comp);
complete(comp);
complete(comp);
}
static void ezusb_ctx_complete(struct request_context *ctx)
{
struct ezusb_priv *upriv = ctx->upriv;
unsigned long flags;
spin_lock_irqsave(&upriv->req_lock, flags);
list_del_init(&ctx->list);
if (upriv->udev) {
spin_unlock_irqrestore(&upriv->req_lock, flags);
ezusb_req_queue_run(upriv);
spin_lock_irqsave(&upriv->req_lock, flags);
}
switch (ctx->state) {
case EZUSB_CTX_COMPLETE:
case EZUSB_CTX_REQSUBMIT_FAIL:
case EZUSB_CTX_REQ_FAILED:
case EZUSB_CTX_REQ_TIMEOUT:
case EZUSB_CTX_RESP_TIMEOUT:
spin_unlock_irqrestore(&upriv->req_lock, flags);
if ((ctx->out_rid == EZUSB_RID_TX) && upriv->dev) {
struct net_device *dev = upriv->dev;
struct orinoco_private *priv = ndev_priv(dev);
struct net_device_stats *stats = &priv->stats;
if (ctx->state != EZUSB_CTX_COMPLETE)
stats->tx_errors++;
else
stats->tx_packets++;
netif_wake_queue(dev);
}
ezusb_complete_all(&ctx->done);
ezusb_request_context_put(ctx);
break;
default:
spin_unlock_irqrestore(&upriv->req_lock, flags);
if (!upriv->udev) {
/* This is normal, as all request contexts get flushed
* when the device is disconnected */
err("Called, CTX not terminating, but device gone");
ezusb_complete_all(&ctx->done);
ezusb_request_context_put(ctx);
break;
}
err("Called, CTX not in terminating state.");
/* Things are really bad if this happens. Just leak
* the CTX because it may still be linked to the
* queue or the OUT urb may still be active.
* Just leaking at least prevents an Oops or Panic.
*/
break;
}
}
/**
* ezusb_req_queue_run:
* Description:
* Note: Only one active CTX at any one time, because there's no
* other (reliable) way to match the response URB to the correct
* CTX.
**/
static void ezusb_req_queue_run(struct ezusb_priv *upriv)
{
unsigned long flags;
struct request_context *ctx;
int result;
spin_lock_irqsave(&upriv->req_lock, flags);
if (!list_empty(&upriv->req_active))
goto unlock;
if (list_empty(&upriv->req_pending))
goto unlock;
ctx =
list_entry(upriv->req_pending.next, struct request_context,
list);
if (!ctx->upriv->udev)
goto unlock;
/* We need to split this off to avoid a race condition */
list_move_tail(&ctx->list, &upriv->req_active);
if (ctx->state == EZUSB_CTX_QUEUED) {
atomic_inc(&ctx->refcount);
result = usb_submit_urb(ctx->outurb, GFP_ATOMIC);
if (result) {
ctx->state = EZUSB_CTX_REQSUBMIT_FAIL;
spin_unlock_irqrestore(&upriv->req_lock, flags);
err("Fatal, failed to submit command urb."
" error=%d\n", result);
ezusb_ctx_complete(ctx);
ezusb_request_context_put(ctx);
goto done;
}
ctx->state = EZUSB_CTX_REQ_SUBMITTED;
ezusb_mod_timer(ctx->upriv, &ctx->timer,
jiffies + DEF_TIMEOUT);
}
unlock:
spin_unlock_irqrestore(&upriv->req_lock, flags);
done:
return;
}
static void ezusb_req_enqueue_run(struct ezusb_priv *upriv,
struct request_context *ctx)
{
unsigned long flags;
spin_lock_irqsave(&upriv->req_lock, flags);
if (!ctx->upriv->udev) {
spin_unlock_irqrestore(&upriv->req_lock, flags);
goto done;
}
atomic_inc(&ctx->refcount);
list_add_tail(&ctx->list, &upriv->req_pending);
spin_unlock_irqrestore(&upriv->req_lock, flags);
ctx->state = EZUSB_CTX_QUEUED;
ezusb_req_queue_run(upriv);
done:
return;
}
static void ezusb_request_out_callback(struct urb *urb)
{
unsigned long flags;
enum ezusb_state state;
struct request_context *ctx = urb->context;
struct ezusb_priv *upriv = ctx->upriv;