From: Dan Williams <dcbw@redhat.com>
To: Luis Carlos Cobo <luisca@cozybit.com>
Cc: linux-wireless@vger.kernel.org, johannes@sipsolutions.net
Subject: Re: [PATCH 3/6] libertastf: main.c, data paths and mac80211 handlers
Date: Sun, 27 Jul 2008 11:44:07 -0400 [thread overview]
Message-ID: <1217173447.2700.29.camel@localhost.localdomain> (raw)
In-Reply-To: <48763861.34062c0a.38e5.1fca@mx.google.com>
On Thu, 2008-07-10 at 17:11 +0200, Luis Carlos Cobo wrote:
> This patch contains most of the libertastf driver, just lacking command helper
> functions and usb specific functions. Currently, monitor, managed, ap and mesh
> interfaces are supported. Even though this driver supports the same hardware as
> the "libertas" driver, it uses a different (thin) firmware, that makes it
> suitable for a mac80211 driver.
>
> Signed-off-by: Luis Carlos Cobo <luisca@cozybit.com>
> Tested-by: Javier Cardona <javier@cozybit.com>
Looks good to me...
> ---
> drivers/net/wireless/libertastf/main.c | 607 ++++++++++++++++++++++++++++++++
> 1 files changed, 607 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/wireless/libertastf/main.c
>
> diff --git a/drivers/net/wireless/libertastf/main.c b/drivers/net/wireless/libertastf/main.c
> new file mode 100644
> index 0000000..7e780f1
> --- /dev/null
> +++ b/drivers/net/wireless/libertastf/main.c
> @@ -0,0 +1,607 @@
> +/*
> + * Copyright (C) 2008, cozybit Inc.
> + * Copyright (C) 2003-2006, Marvell International Ltd.
> + *
> + * 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
> + */
> +#include "libertastf.h"
> +
> +#define DRIVER_RELEASE_VERSION "004.p0"
> +/* thinfirm version: 5.132.X.pX */
> +#define LBTF_FW_VER_MASK 0xffff0000
> +#define LBTF_FW_VER 0x05840000
> +#define QOS_CONTROL_LEN 2
> +
> +static const char lbtf_driver_version[] = "THINFIRM-USB8388-" DRIVER_RELEASE_VERSION;
> +struct workqueue_struct *lbtf_wq;
> +
> +static const struct ieee80211_channel lbtf_channels[] = {
> + { .center_freq = 2412, .hw_value = 1 },
> + { .center_freq = 2417, .hw_value = 2 },
> + { .center_freq = 2422, .hw_value = 3 },
> + { .center_freq = 2427, .hw_value = 4 },
> + { .center_freq = 2432, .hw_value = 5 },
> + { .center_freq = 2437, .hw_value = 6 },
> + { .center_freq = 2442, .hw_value = 7 },
> + { .center_freq = 2447, .hw_value = 8 },
> + { .center_freq = 2452, .hw_value = 9 },
> + { .center_freq = 2457, .hw_value = 10 },
> + { .center_freq = 2462, .hw_value = 11 },
> + { .center_freq = 2467, .hw_value = 12 },
> + { .center_freq = 2472, .hw_value = 13 },
> + { .center_freq = 2484, .hw_value = 14 },
> +};
> +
> +/* This table contains the hardware specific values for the modulation rates. */
> +static const struct ieee80211_rate lbtf_rates[] = {
> + { .bitrate = 10,
> + .hw_value = 0, },
> + { .bitrate = 20,
> + .hw_value = 1,
> + .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> + { .bitrate = 55,
> + .hw_value = 2,
> + .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> + { .bitrate = 110,
> + .hw_value = 3,
> + .flags = IEEE80211_RATE_SHORT_PREAMBLE },
> + { .bitrate = 60,
> + .hw_value = 5,
> + .flags = 0 },
> + { .bitrate = 90,
> + .hw_value = 6,
> + .flags = 0 },
> + { .bitrate = 120,
> + .hw_value = 7,
> + .flags = 0 },
> + { .bitrate = 180,
> + .hw_value = 8,
> + .flags = 0 },
> + { .bitrate = 240,
> + .hw_value = 9,
> + .flags = 0 },
> + { .bitrate = 360,
> + .hw_value = 10,
> + .flags = 0 },
> + { .bitrate = 480,
> + .hw_value = 11,
> + .flags = 0 },
> + { .bitrate = 540,
> + .hw_value = 12,
> + .flags = 0 },
> +};
> +
> +
> +static void lbtf_cmd_work(struct work_struct *work)
> +{
> + struct lbtf_private *priv = container_of(work, struct lbtf_private,
> + cmd_work);
> + spin_lock_irq(&priv->driver_lock);
> + /* command response? */
> + if (priv->cmd_response_rxed) {
> + priv->cmd_response_rxed = 0;
> + spin_unlock_irq(&priv->driver_lock);
> + lbtf_process_rx_command(priv);
> + spin_lock_irq(&priv->driver_lock);
> + }
> +
> + if (priv->cmd_timed_out && priv->cur_cmd) {
> + struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
> +
> + if (++priv->nr_retries > 10) {
> + lbtf_complete_command(priv, cmdnode,
> + -ETIMEDOUT);
> + priv->nr_retries = 0;
> + } else {
> + priv->cur_cmd = NULL;
> +
> + /* Stick it back at the _top_ of the pending
> + * queue for immediate resubmission */
> + list_add(&cmdnode->list, &priv->cmdpendingq);
> + }
> + }
> + priv->cmd_timed_out = 0;
> + spin_unlock_irq(&priv->driver_lock);
> +
> + if (!priv->fw_ready)
> + return;
> + /* Execute the next command */
> + if (!priv->cur_cmd)
> + lbtf_execute_next_command(priv);
> +}
> +
> +/**
> + * lbtf_setup_firmware: initialize firmware.
> + *
> + * @priv A pointer to struct lbtf_private structure
> + *
> + * Returns: 0 on success.
> + */
> +static int lbtf_setup_firmware(struct lbtf_private *priv)
> +{
> + int ret = -1;
> +
> + /*
> + * Read priv address from HW
> + */
> + memset(priv->current_addr, 0xff, ETH_ALEN);
> + ret = lbtf_update_hw_spec(priv);
> + if (ret) {
> + ret = -1;
> + goto done;
> + }
> +
> + lbtf_set_mac_control(priv);
> + lbtf_set_radio_control(priv);
> +
> + ret = 0;
> +done:
> + return ret;
> +}
> +
> +/**
> + * This function handles the timeout of command sending.
> + * It will re-send the same command again.
> + */
> +static void command_timer_fn(unsigned long data)
> +{
> + struct lbtf_private *priv = (struct lbtf_private *)data;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&priv->driver_lock, flags);
> +
> + if (!priv->cur_cmd) {
> + printk(KERN_DEBUG "libertastf: command timer expired; "
> + "no pending command\n");
> + goto out;
> + }
> +
> + printk(KERN_DEBUG "libertas: command %x timed out\n",
> + le16_to_cpu(priv->cur_cmd->cmdbuf->command));
> +
> + priv->cmd_timed_out = 1;
> + queue_work(lbtf_wq, &priv->cmd_work);
> +out:
> + spin_unlock_irqrestore(&priv->driver_lock, flags);
> +}
> +
> +static int lbtf_init_adapter(struct lbtf_private *priv)
> +{
> + memset(priv->current_addr, 0xff, ETH_ALEN);
> + mutex_init(&priv->lock);
> +
> + priv->vif = NULL;
> + setup_timer(&priv->command_timer, command_timer_fn,
> + (unsigned long)priv);
> +
> + INIT_LIST_HEAD(&priv->cmdfreeq);
> + INIT_LIST_HEAD(&priv->cmdpendingq);
> +
> + spin_lock_init(&priv->driver_lock);
> +
> + /* Allocate the command buffers */
> + if (lbtf_allocate_cmd_buffer(priv))
> + return -1;
> +
> + return 0;
> +}
> +
> +static void lbtf_free_adapter(struct lbtf_private *priv)
> +{
> + lbtf_free_cmd_buffer(priv);
> + del_timer(&priv->command_timer);
> +}
> +
> +static int lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> +{
> + int len = skb->len;
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> + struct lbtf_private *priv = hw->priv;
> + struct txpd *txpd = (struct txpd *)
> + skb_push(skb, sizeof(struct txpd));
> + int ret;
> +
> + if (priv->surpriseremoved) {
> + dev_kfree_skb_any(skb);
> + return -1;
> + }
> +
> + memset(txpd, 0, sizeof(struct txpd));
> + /* Activate per-packet rate selection */
> + txpd->tx_control |= cpu_to_le32(MRVL_PER_PACKET_RATE |
> + ieee80211_get_tx_rate(hw, info)->hw_value);
> +
> + /* copy destination address from 802.11 header */
> + memcpy(txpd->tx_dest_addr_high, skb->data + sizeof(struct txpd) + 4,
> + ETH_ALEN);
> + txpd->tx_packet_length = cpu_to_le16(len);
> + txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
> +
> + /* queue will be restarted when we receive transmission feedback */
> + ieee80211_stop_queues(hw);
> + BUG_ON(priv->tx_skb);
> + spin_lock_irq(&priv->driver_lock);
> + priv->tx_skb = skb;
> + ret = priv->hw_host_to_card(priv, MVMS_DAT, skb->data, skb->len);
> + spin_unlock_irq(&priv->driver_lock);
> + if (ret) {
> + dev_kfree_skb_any(skb);
> + priv->tx_skb = NULL;
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +static int lbtf_op_start(struct ieee80211_hw *hw)
> +{
> + struct lbtf_private *priv = hw->priv;
> + void *card = priv->card;
> + int ret = -1;
> +
> + if (!priv->fw_ready)
> + /* Upload firmware */
> + if (priv->hw_prog_firmware(card))
> + goto err_prog_firmware;
> +
> + /* poke the firmware */
> + priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
> + priv->radioon = RADIO_ON;
> + priv->mac_control = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;
> + ret = lbtf_setup_firmware(priv);
> + if (ret)
> + goto err_prog_firmware;
> +
> + if ((priv->fwrelease & LBTF_FW_VER_MASK) != LBTF_FW_VER) {
> + ret = -1;
> + goto err_prog_firmware;
> + }
> +
> + printk(KERN_INFO "libertastf: Marvell WLAN 802.11 thinfirm adapter\n");
> + return 0;
> +
> +err_prog_firmware:
> + priv->hw_reset_device(card);
> + return ret;
> +}
> +
> +static void lbtf_op_stop(struct ieee80211_hw *hw)
> +{
> + struct lbtf_private *priv = hw->priv;
> + int flags;
> +
> + struct cmd_ctrl_node *cmdnode;
> + /* Flush pending command nodes */
> + spin_lock_irqsave(&priv->driver_lock, flags);
> + list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
> + cmdnode->result = -ENOENT;
> + cmdnode->cmdwaitqwoken = 1;
> + wake_up_interruptible(&cmdnode->cmdwait_q);
> + }
> +
> + spin_unlock_irqrestore(&priv->driver_lock, flags);
> + cancel_work_sync(&priv->cmd_work);
> + priv->radioon = RADIO_OFF;
> + lbtf_set_radio_control(priv);
> +
> + return;
> +}
> +
> +static int lbtf_op_add_interface(struct ieee80211_hw *hw,
> + struct ieee80211_if_init_conf *conf)
> +{
> + struct lbtf_private *priv = hw->priv;
> + if (priv->vif != NULL)
> + return -EOPNOTSUPP;
> +
> + priv->vif = conf->vif;
> + switch (conf->type) {
> + case IEEE80211_IF_TYPE_MESH_POINT:
> + case IEEE80211_IF_TYPE_AP:
> + lbtf_set_mode(priv, LBTF_AP_MODE);
> + break;
> + case IEEE80211_IF_TYPE_STA:
> + lbtf_set_mode(priv, LBTF_STA_MODE);
> + break;
> + default:
> + priv->vif = NULL;
> + return -EOPNOTSUPP;
> + }
> + lbtf_set_mac_address(priv, (u8 *) conf->mac_addr);
> + return 0;
> +}
> +
> +static void lbtf_op_remove_interface(struct ieee80211_hw *hw,
> + struct ieee80211_if_init_conf *conf)
> +{
> + struct lbtf_private *priv = hw->priv;
> +
> + if (priv->vif->type == IEEE80211_IF_TYPE_AP ||
> + priv->vif->type == IEEE80211_IF_TYPE_MESH_POINT)
> + lbtf_beacon_ctrl(priv, 0, 0);
> + lbtf_set_mode(priv, LBTF_PASSIVE_MODE);
> + lbtf_set_bssid(priv, 0, NULL);
> + priv->vif = NULL;
> +}
> +
> +static int lbtf_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf)
> +{
> + struct lbtf_private *priv = hw->priv;
> + if (conf->channel->center_freq != priv->cur_freq) {
> + priv->cur_freq = conf->channel->center_freq;
> + lbtf_set_channel(priv, conf->channel->hw_value);
> + }
> + return 0;
> +}
> +
> +static int lbtf_op_config_interface(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_if_conf *conf)
> +{
> + struct lbtf_private *priv = hw->priv;
> +
> + switch (priv->vif->type) {
> + case IEEE80211_IF_TYPE_AP:
> + case IEEE80211_IF_TYPE_MESH_POINT:
> + if (conf->beacon) {
> + lbtf_beacon_set(priv, conf->beacon);
> + kfree_skb(conf->beacon);
> + lbtf_beacon_ctrl(priv, 1, hw->conf.beacon_int);
> + }
> + break;
> + default:
> + break;
> + }
> +
> + if (conf->bssid)
> + lbtf_set_bssid(priv, 1, conf->bssid);
> +
> + return 0;
> +}
> +
> +#define SUPPORTED_FIF_FLAGS (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)
> +static void lbtf_op_configure_filter(struct ieee80211_hw *hw,
> + unsigned int changed_flags,
> + unsigned int *new_flags,
> + int mc_count, struct dev_mc_list *mclist)
> +{
> + struct lbtf_private *priv = hw->priv;
> + int old_mac_control = priv->mac_control;
> + int i;
> + changed_flags &= SUPPORTED_FIF_FLAGS;
> + *new_flags &= SUPPORTED_FIF_FLAGS;
> +
> + if (!changed_flags)
> + return;
> +
> + if (*new_flags & (FIF_PROMISC_IN_BSS))
> + priv->mac_control |= CMD_ACT_MAC_PROMISCUOUS_ENABLE;
> + else
> + priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE;
> + if (*new_flags & (FIF_ALLMULTI) ||
> + mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) {
> + priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
> + priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE;
> + } else if (mc_count) {
> + priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE;
> + priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
> + priv->nr_of_multicastmacaddr = mc_count;
> + for (i = 0; i < mc_count; i++) {
> + if (!mclist)
> + break;
> + memcpy(&priv->multicastlist[i], mclist->da_addr,
> + ETH_ALEN);
> + mclist = mclist->next;
> + }
> + lbtf_cmd_set_mac_multicast_addr(priv);
> + } else
> + priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE |
> + CMD_ACT_MAC_ALL_MULTICAST_ENABLE);
> +
> + if (priv->mac_control != old_mac_control)
> + lbtf_set_mac_control(priv);
> +}
> +
> +static void lbtf_op_bss_info_changed(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_bss_conf *bss_conf,
> + u32 changes)
> +{
> + struct lbtf_private *priv = hw->priv;
> +
> + if (changes & BSS_CHANGED_ERP_PREAMBLE) {
> + if (bss_conf->use_short_preamble)
> + priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
> + else
> + priv->preamble = CMD_TYPE_LONG_PREAMBLE;
> + lbtf_set_radio_control(priv);
> + }
> +
> + return;
> +}
> +
> +static int lbtf_op_set_tim(struct ieee80211_hw *hw, int aid, int set)
> +{
> + struct lbtf_private *priv = hw->priv;
> + struct sk_buff *beacon;
> +
> + beacon = ieee80211_beacon_get(hw, priv->vif);
> + if (unlikely(!beacon))
> + return -ENOMEM;
> + lbtf_beacon_set(priv, beacon);
> + kfree_skb(beacon);
> +
> + return 0;
> +}
> +
> +static const struct ieee80211_ops lbtf_ops = {
> + .tx = lbtf_op_tx,
> + .start = lbtf_op_start,
> + .stop = lbtf_op_stop,
> + .add_interface = lbtf_op_add_interface,
> + .remove_interface = lbtf_op_remove_interface,
> + .config = lbtf_op_config,
> + .config_interface = lbtf_op_config_interface,
> + .configure_filter = lbtf_op_configure_filter,
> + .bss_info_changed = lbtf_op_bss_info_changed,
> + .set_tim = lbtf_op_set_tim,
> +};
> +
> +int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb)
> +{
> + struct ieee80211_rx_status stats;
> + struct rxpd *prxpd;
> + bool is_qos, is_4addr, is_amsdu, need_padding;
> + unsigned int flags;
> + u16 fc;
> +
> + prxpd = (struct rxpd *) skb->data;
> +
> + stats.flag = 0;
> + if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
> + stats.flag |= RX_FLAG_FAILED_FCS_CRC;
> + stats.freq = priv->cur_freq;
> + stats.band = IEEE80211_BAND_2GHZ;
> + stats.signal = prxpd->snr;
> + stats.noise = prxpd->nf;
> + stats.qual = prxpd->snr - prxpd->nf;
> + /* Marvell rate index has a hole at value 4 */
> + if (prxpd->rx_rate > 4)
> + --prxpd->rx_rate;
> + stats.rate_idx = prxpd->rx_rate;
> + skb_pull(skb, sizeof(struct rxpd));
> +
> + fc = le16_to_cpu(*((__le16 *) skb->data));
> + flags = le32_to_cpu(*(__le32 *)(skb->data + 4));
> +
> + is_qos = ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
> + (fc & IEEE80211_STYPE_QOS_DATA);
> + is_4addr = (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
> + (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS);
> + is_amsdu = ((fc & 0x8C) == 0x88) &&
> + (*(skb->data + ieee80211_get_hdrlen(fc) - QOS_CONTROL_LEN)
> + & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT);
> +
> + need_padding = is_qos ^ is_4addr ^ is_amsdu;
> + if (need_padding) {
> + memmove(skb->data + 2, skb->data, skb->len);
> + skb_reserve(skb, 2);
> + }
> +
> + ieee80211_rx_irqsafe(priv->hw, skb, &stats);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(lbtf_rx);
> +
> +/**
> + * lbtf_add_card: Add and initialize the card, no fw upload yet.
> + *
> + * @card A pointer to card
> + *
> + * Returns: pointer to struct lbtf_priv.
> + */
> +struct lbtf_private *lbtf_add_card(void *card, struct device *dmdev)
> +{
> + struct ieee80211_hw *hw;
> + struct lbtf_private *priv = NULL;
> +
> + hw = ieee80211_alloc_hw(sizeof(struct lbtf_private), &lbtf_ops);
> + if (!hw)
> + goto done;
> +
> + priv = hw->priv;
> + if (lbtf_init_adapter(priv))
> + goto err_init_adapter;
> +
> + priv->hw = hw;
> + priv->card = card;
> + priv->tx_skb = NULL;
> +
> + hw->queues = 1;
> + hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE;
> + hw->extra_tx_headroom = sizeof(struct txpd);
> + memcpy(priv->channels, lbtf_channels, sizeof(lbtf_channels));
> + memcpy(priv->rates, lbtf_rates, sizeof(lbtf_rates));
> + priv->band.n_bitrates = ARRAY_SIZE(lbtf_rates);
> + priv->band.bitrates = priv->rates;
> + priv->band.n_channels = ARRAY_SIZE(lbtf_channels);
> + priv->band.channels = priv->channels;
> + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
> +
> + SET_IEEE80211_DEV(hw, dmdev);
> +
> + INIT_WORK(&priv->cmd_work, lbtf_cmd_work);
> + if (ieee80211_register_hw(hw))
> + goto err_init_adapter;
> +
> + goto done;
> +
> +err_init_adapter:
> + lbtf_free_adapter(priv);
> + ieee80211_free_hw(hw);
> + priv = NULL;
> +
> +done:
> + return priv;
> +}
> +EXPORT_SYMBOL_GPL(lbtf_add_card);
> +
> +
> +int lbtf_remove_card(struct lbtf_private *priv)
> +{
> + struct ieee80211_hw *hw = priv->hw;
> +
> + priv->surpriseremoved = 1;
> + del_timer(&priv->command_timer);
> + lbtf_free_adapter(priv);
> + priv->hw = NULL;
> + ieee80211_unregister_hw(hw);
> + ieee80211_free_hw(hw);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(lbtf_remove_card);
> +
> +void lbtf_send_tx_feedback(struct lbtf_private *priv, u8 retrycnt, u8 fail)
> +{
> + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb);
> + memset(&info->status, 0, sizeof(info->status));
> + /*
> + * Commented out, otherwise we never go beyond 1Mbit/s using mac80211
> + * default pid rc algorithm.
> + *
> + * info->status.retry_count = MRVL_DEFAULT_RETRIES - retrycnt;
> + */
> + info->status.excessive_retries = fail ? 1 : 0;
> + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !fail)
> + info->flags |= IEEE80211_TX_STAT_ACK;
> + skb_pull(priv->tx_skb, sizeof(struct txpd));
> + ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb);
> + priv->tx_skb = NULL;
> + ieee80211_wake_queues(priv->hw);
> +}
> +EXPORT_SYMBOL_GPL(lbtf_send_tx_feedback);
> +
> +static int __init lbtf_init_module(void)
> +{
> + lbtf_wq = create_workqueue("libertastf");
> + if (lbtf_wq == NULL) {
> + printk(KERN_ERR "libertastf: couldn't create workqueue\n");
> + return -ENOMEM;
> + }
> + return 0;
> +}
> +
> +static void __exit lbtf_exit_module(void)
> +{
> + destroy_workqueue(lbtf_wq);
> +}
> +
> +module_init(lbtf_init_module);
> +module_exit(lbtf_exit_module);
> +
> +MODULE_DESCRIPTION("Libertas WLAN Thinfirm Driver Library");
> +MODULE_AUTHOR("Cozybit Inc.");
> +MODULE_LICENSE("GPL");
prev parent reply other threads:[~2008-07-27 15:46 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-07-10 15:11 [PATCH 3/6] libertastf: main.c, data paths and mac80211 handlers Luis Carlos Cobo
2008-07-27 15:44 ` Dan Williams [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1217173447.2700.29.camel@localhost.localdomain \
--to=dcbw@redhat.com \
--cc=johannes@sipsolutions.net \
--cc=linux-wireless@vger.kernel.org \
--cc=luisca@cozybit.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox