summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/intersil/prism54/islpci_mgt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/intersil/prism54/islpci_mgt.c')
-rw-r--r--drivers/net/wireless/intersil/prism54/islpci_mgt.c491
1 files changed, 0 insertions, 491 deletions
diff --git a/drivers/net/wireless/intersil/prism54/islpci_mgt.c b/drivers/net/wireless/intersil/prism54/islpci_mgt.c
deleted file mode 100644
index 0c7fb76c7d1c..000000000000
--- a/drivers/net/wireless/intersil/prism54/islpci_mgt.c
+++ /dev/null
@@ -1,491 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2002 Intersil Americas Inc.
- * Copyright 2004 Jens Maurer <Jens.Maurer@gmx.net>
- */
-
-#include <linux/netdevice.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <asm/io.h>
-#include <linux/if_arp.h>
-
-#include "prismcompat.h"
-#include "isl_38xx.h"
-#include "islpci_mgt.h"
-#include "isl_oid.h" /* additional types and defs for isl38xx fw */
-#include "isl_ioctl.h"
-
-#include <net/iw_handler.h>
-
-/******************************************************************************
- Global variable definition section
-******************************************************************************/
-int pc_debug = VERBOSE;
-module_param(pc_debug, int, 0);
-
-/******************************************************************************
- Driver general functions
-******************************************************************************/
-#if VERBOSE > SHOW_ERROR_MESSAGES
-void
-display_buffer(char *buffer, int length)
-{
- if ((pc_debug & SHOW_BUFFER_CONTENTS) == 0)
- return;
-
- while (length > 0) {
- printk("[%02x]", *buffer & 255);
- length--;
- buffer++;
- }
-
- printk("\n");
-}
-#endif
-
-/*****************************************************************************
- Queue handling for management frames
-******************************************************************************/
-
-/*
- * Helper function to create a PIMFOR management frame header.
- */
-static void
-pimfor_encode_header(int operation, u32 oid, u32 length, pimfor_header_t *h)
-{
- h->version = PIMFOR_VERSION;
- h->operation = operation;
- h->device_id = PIMFOR_DEV_ID_MHLI_MIB;
- h->flags = 0;
- h->oid = cpu_to_be32(oid);
- h->length = cpu_to_be32(length);
-}
-
-/*
- * Helper function to analyze a PIMFOR management frame header.
- */
-static pimfor_header_t *
-pimfor_decode_header(void *data, int len)
-{
- pimfor_header_t *h = data;
-
- while ((void *) h < data + len) {
- if (h->flags & PIMFOR_FLAG_LITTLE_ENDIAN) {
- le32_to_cpus(&h->oid);
- le32_to_cpus(&h->length);
- } else {
- be32_to_cpus(&h->oid);
- be32_to_cpus(&h->length);
- }
- if (h->oid != OID_INL_TUNNEL)
- return h;
- h++;
- }
- return NULL;
-}
-
-/*
- * Fill the receive queue for management frames with fresh buffers.
- */
-int
-islpci_mgmt_rx_fill(struct net_device *ndev)
-{
- islpci_private *priv = netdev_priv(ndev);
- isl38xx_control_block *cb = /* volatile not needed */
- (isl38xx_control_block *) priv->control_block;
- u32 curr = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ]);
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgmt_rx_fill\n");
-#endif
-
- while (curr - priv->index_mgmt_rx < ISL38XX_CB_MGMT_QSIZE) {
- u32 index = curr % ISL38XX_CB_MGMT_QSIZE;
- struct islpci_membuf *buf = &priv->mgmt_rx[index];
- isl38xx_fragment *frag = &cb->rx_data_mgmt[index];
-
- if (buf->mem == NULL) {
- buf->mem = kmalloc(MGMT_FRAME_SIZE, GFP_ATOMIC);
- if (!buf->mem)
- return -ENOMEM;
- buf->size = MGMT_FRAME_SIZE;
- }
- if (buf->pci_addr == 0) {
- buf->pci_addr = dma_map_single(&priv->pdev->dev,
- buf->mem,
- MGMT_FRAME_SIZE,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(&priv->pdev->dev, buf->pci_addr)) {
- printk(KERN_WARNING
- "Failed to make memory DMA'able.\n");
- return -ENOMEM;
- }
- }
-
- /* be safe: always reset control block information */
- frag->size = cpu_to_le16(MGMT_FRAME_SIZE);
- frag->flags = 0;
- frag->address = cpu_to_le32(buf->pci_addr);
- curr++;
-
- /* The fragment address in the control block must have
- * been written before announcing the frame buffer to
- * device */
- wmb();
- cb->driver_curr_frag[ISL38XX_CB_RX_MGMTQ] = cpu_to_le32(curr);
- }
- return 0;
-}
-
-/*
- * Create and transmit a management frame using "operation" and "oid",
- * with arguments data/length.
- * We either return an error and free the frame, or we return 0 and
- * islpci_mgt_cleanup_transmit() frees the frame in the tx-done
- * interrupt.
- */
-static int
-islpci_mgt_transmit(struct net_device *ndev, int operation, unsigned long oid,
- void *data, int length)
-{
- islpci_private *priv = netdev_priv(ndev);
- isl38xx_control_block *cb =
- (isl38xx_control_block *) priv->control_block;
- void *p;
- int err = -EINVAL;
- unsigned long flags;
- isl38xx_fragment *frag;
- struct islpci_membuf buf;
- u32 curr_frag;
- int index;
- int frag_len = length + PIMFOR_HEADER_SIZE;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_transmit\n");
-#endif
-
- if (frag_len > MGMT_FRAME_SIZE) {
- printk(KERN_DEBUG "%s: mgmt frame too large %d\n",
- ndev->name, frag_len);
- goto error;
- }
-
- err = -ENOMEM;
- p = buf.mem = kmalloc(frag_len, GFP_KERNEL);
- if (!buf.mem)
- goto error;
-
- buf.size = frag_len;
-
- /* create the header directly in the fragment data area */
- pimfor_encode_header(operation, oid, length, (pimfor_header_t *) p);
- p += PIMFOR_HEADER_SIZE;
-
- if (data)
- memcpy(p, data, length);
- else
- memset(p, 0, length);
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- {
- pimfor_header_t *h = buf.mem;
- DEBUG(SHOW_PIMFOR_FRAMES,
- "PIMFOR: op %i, oid 0x%08lx, device %i, flags 0x%x length 0x%x\n",
- h->operation, oid, h->device_id, h->flags, length);
-
- /* display the buffer contents for debugging */
- display_buffer((char *) h, sizeof (pimfor_header_t));
- display_buffer(p, length);
- }
-#endif
-
- err = -ENOMEM;
- buf.pci_addr = dma_map_single(&priv->pdev->dev, buf.mem, frag_len,
- DMA_TO_DEVICE);
- if (dma_mapping_error(&priv->pdev->dev, buf.pci_addr)) {
- printk(KERN_WARNING "%s: cannot map PCI memory for mgmt\n",
- ndev->name);
- goto error_free;
- }
-
- /* Protect the control block modifications against interrupts. */
- spin_lock_irqsave(&priv->slock, flags);
- curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ]);
- if (curr_frag - priv->index_mgmt_tx >= ISL38XX_CB_MGMT_QSIZE) {
- printk(KERN_WARNING "%s: mgmt tx queue is still full\n",
- ndev->name);
- goto error_unlock;
- }
-
- /* commit the frame to the tx device queue */
- index = curr_frag % ISL38XX_CB_MGMT_QSIZE;
- priv->mgmt_tx[index] = buf;
- frag = &cb->tx_data_mgmt[index];
- frag->size = cpu_to_le16(frag_len);
- frag->flags = 0; /* for any other than the last fragment, set to 1 */
- frag->address = cpu_to_le32(buf.pci_addr);
-
- /* The fragment address in the control block must have
- * been written before announcing the frame buffer to
- * device */
- wmb();
- cb->driver_curr_frag[ISL38XX_CB_TX_MGMTQ] = cpu_to_le32(curr_frag + 1);
- spin_unlock_irqrestore(&priv->slock, flags);
-
- /* trigger the device */
- islpci_trigger(priv);
- return 0;
-
- error_unlock:
- spin_unlock_irqrestore(&priv->slock, flags);
- error_free:
- kfree(buf.mem);
- error:
- return err;
-}
-
-/*
- * Receive a management frame from the device.
- * This can be an arbitrary number of traps, and at most one response
- * frame for a previous request sent via islpci_mgt_transmit().
- */
-int
-islpci_mgt_receive(struct net_device *ndev)
-{
- islpci_private *priv = netdev_priv(ndev);
- isl38xx_control_block *cb =
- (isl38xx_control_block *) priv->control_block;
- u32 curr_frag;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_receive\n");
-#endif
-
- /* Only once per interrupt, determine fragment range to
- * process. This avoids an endless loop (i.e. lockup) if
- * frames come in faster than we can process them. */
- curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_RX_MGMTQ]);
- barrier();
-
- for (; priv->index_mgmt_rx < curr_frag; priv->index_mgmt_rx++) {
- pimfor_header_t *header;
- u32 index = priv->index_mgmt_rx % ISL38XX_CB_MGMT_QSIZE;
- struct islpci_membuf *buf = &priv->mgmt_rx[index];
- u16 frag_len;
- int size;
- struct islpci_mgmtframe *frame;
-
- /* I have no idea (and no documentation) if flags != 0
- * is possible. Drop the frame, reuse the buffer. */
- if (le16_to_cpu(cb->rx_data_mgmt[index].flags) != 0) {
- printk(KERN_WARNING "%s: unknown flags 0x%04x\n",
- ndev->name,
- le16_to_cpu(cb->rx_data_mgmt[index].flags));
- continue;
- }
-
- /* The device only returns the size of the header(s) here. */
- frag_len = le16_to_cpu(cb->rx_data_mgmt[index].size);
-
- /*
- * We appear to have no way to tell the device the
- * size of a receive buffer. Thus, if this check
- * triggers, we likely have kernel heap corruption. */
- if (frag_len > MGMT_FRAME_SIZE) {
- printk(KERN_WARNING
- "%s: Bogus packet size of %d (%#x).\n",
- ndev->name, frag_len, frag_len);
- frag_len = MGMT_FRAME_SIZE;
- }
-
- /* Ensure the results of device DMA are visible to the CPU. */
- dma_sync_single_for_cpu(&priv->pdev->dev, buf->pci_addr,
- buf->size, DMA_FROM_DEVICE);
-
- /* Perform endianess conversion for PIMFOR header in-place. */
- header = pimfor_decode_header(buf->mem, frag_len);
- if (!header) {
- printk(KERN_WARNING "%s: no PIMFOR header found\n",
- ndev->name);
- continue;
- }
-
- /* The device ID from the PIMFOR packet received from
- * the MVC is always 0. We forward a sensible device_id.
- * Not that anyone upstream would care... */
- header->device_id = priv->ndev->ifindex;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_PIMFOR_FRAMES,
- "PIMFOR: op %i, oid 0x%08x, device %i, flags 0x%x length 0x%x\n",
- header->operation, header->oid, header->device_id,
- header->flags, header->length);
-
- /* display the buffer contents for debugging */
- display_buffer((char *) header, PIMFOR_HEADER_SIZE);
- display_buffer((char *) header + PIMFOR_HEADER_SIZE,
- header->length);
-#endif
-
- /* nobody sends these */
- if (header->flags & PIMFOR_FLAG_APPLIC_ORIGIN) {
- printk(KERN_DEBUG
- "%s: errant PIMFOR application frame\n",
- ndev->name);
- continue;
- }
-
- /* Determine frame size, skipping OID_INL_TUNNEL headers. */
- size = PIMFOR_HEADER_SIZE + header->length;
- frame = kmalloc(sizeof(struct islpci_mgmtframe) + size,
- GFP_ATOMIC);
- if (!frame)
- continue;
-
- frame->ndev = ndev;
- memcpy(&frame->buf, header, size);
- frame->header = (pimfor_header_t *) frame->buf;
- frame->data = frame->buf + PIMFOR_HEADER_SIZE;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_PIMFOR_FRAMES,
- "frame: header: %p, data: %p, size: %d\n",
- frame->header, frame->data, size);
-#endif
-
- if (header->operation == PIMFOR_OP_TRAP) {
-#if VERBOSE > SHOW_ERROR_MESSAGES
- printk(KERN_DEBUG
- "TRAP: oid 0x%x, device %i, flags 0x%x length %i\n",
- header->oid, header->device_id, header->flags,
- header->length);
-#endif
-
- /* Create work to handle trap out of interrupt
- * context. */
- INIT_WORK(&frame->ws, prism54_process_trap);
- schedule_work(&frame->ws);
-
- } else {
- /* Signal the one waiting process that a response
- * has been received. */
- if ((frame = xchg(&priv->mgmt_received, frame)) != NULL) {
- printk(KERN_WARNING
- "%s: mgmt response not collected\n",
- ndev->name);
- kfree(frame);
- }
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_TRACING, "Wake up Mgmt Queue\n");
-#endif
- wake_up(&priv->mgmt_wqueue);
- }
-
- }
-
- return 0;
-}
-
-/*
- * Cleanup the transmit queue by freeing all frames handled by the device.
- */
-void
-islpci_mgt_cleanup_transmit(struct net_device *ndev)
-{
- islpci_private *priv = netdev_priv(ndev);
- isl38xx_control_block *cb = /* volatile not needed */
- (isl38xx_control_block *) priv->control_block;
- u32 curr_frag;
-
-#if VERBOSE > SHOW_ERROR_MESSAGES
- DEBUG(SHOW_FUNCTION_CALLS, "islpci_mgt_cleanup_transmit\n");
-#endif
-
- /* Only once per cleanup, determine fragment range to
- * process. This avoids an endless loop (i.e. lockup) if
- * the device became confused, incrementing device_curr_frag
- * rapidly. */
- curr_frag = le32_to_cpu(cb->device_curr_frag[ISL38XX_CB_TX_MGMTQ]);
- barrier();
-
- for (; priv->index_mgmt_tx < curr_frag; priv->index_mgmt_tx++) {
- int index = priv->index_mgmt_tx % ISL38XX_CB_MGMT_QSIZE;
- struct islpci_membuf *buf = &priv->mgmt_tx[index];
- dma_unmap_single(&priv->pdev->dev, buf->pci_addr, buf->size,
- DMA_TO_DEVICE);
- buf->pci_addr = 0;
- kfree(buf->mem);
- buf->mem = NULL;
- buf->size = 0;
- }
-}
-
-/*
- * Perform one request-response transaction to the device.
- */
-int
-islpci_mgt_transaction(struct net_device *ndev,
- int operation, unsigned long oid,
- void *senddata, int sendlen,
- struct islpci_mgmtframe **recvframe)
-{
- islpci_private *priv = netdev_priv(ndev);
- const long wait_cycle_jiffies = msecs_to_jiffies(ISL38XX_WAIT_CYCLE * 10);
- long timeout_left = ISL38XX_MAX_WAIT_CYCLES * wait_cycle_jiffies;
- int err;
- DEFINE_WAIT(wait);
-
- *recvframe = NULL;
-
- if (mutex_lock_interruptible(&priv->mgmt_lock))
- return -ERESTARTSYS;
-
- prepare_to_wait(&priv->mgmt_wqueue, &wait, TASK_UNINTERRUPTIBLE);
- err = islpci_mgt_transmit(ndev, operation, oid, senddata, sendlen);
- if (err)
- goto out;
-
- err = -ETIMEDOUT;
- while (timeout_left > 0) {
- int timeleft;
- struct islpci_mgmtframe *frame;
-
- timeleft = schedule_timeout_uninterruptible(wait_cycle_jiffies);
- frame = xchg(&priv->mgmt_received, NULL);
- if (frame) {
- if (frame->header->oid == oid) {
- *recvframe = frame;
- err = 0;
- goto out;
- } else {
- printk(KERN_DEBUG
- "%s: expecting oid 0x%x, received 0x%x.\n",
- ndev->name, (unsigned int) oid,
- frame->header->oid);
- kfree(frame);
- frame = NULL;
- }
- }
- if (timeleft == 0) {
- printk(KERN_DEBUG
- "%s: timeout waiting for mgmt response %lu, "
- "triggering device\n",
- ndev->name, timeout_left);
- islpci_trigger(priv);
- }
- timeout_left += timeleft - wait_cycle_jiffies;
- }
- printk(KERN_WARNING "%s: timeout waiting for mgmt response\n",
- ndev->name);
-
- /* TODO: we should reset the device here */
- out:
- finish_wait(&priv->mgmt_wqueue, &wait);
- mutex_unlock(&priv->mgmt_lock);
- return err;
-}
-