* [PATCH] UCC TDM driver for QE based MPC83xx platforms.
From: Poonam_Aggrwal-b10812 @ 2008-01-24 4:46 UTC (permalink / raw)
To: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev
Cc: michael.barkowski, kim.phillips, ashish.kalra, timur, rich.cutler
From: Poonam Agarwal-b10812 <b10812@freescale.com>
The UCC TDM driver basically multiplexes and demultiplexes data from
different channels. It can interface with for example SLIC kind of devices
to receive TDM data demultiplex it and send to upper modules. At the
transmit end it receives data for different channels multiplexes it and
sends them on the TDM channel. It internally uses TSA( Time Slot Assigner)
which does multiplexing and demultiplexing, UCC to perform SDMA between
host buffers and the TSA, CMX to connect TSA to UCC.
It can be used by a kernel module which can call tdm_register_client to
get access to a TDM device.
The driver is right now a misc driver with no subsystem as such.
There can be a platform independent TDM layer which is planned to be
done after this. TDM bus sort of thing.
The dts file keeps a track of the TDM devices present on the board.
Depending on them the TDM driver initializes those many driver instances
while coming up.
The driver on the upper level can plug to more than one tdm clients
depending on the availablity of TDM devices. At every new request of a TDM
client to bind with a TDM device, a free driver instance is allocated to
the client.
The interface can be described as follows.
tdm_register_client(struct tdm_client *)
This API returns a pointer to the structure tdm_client which is of
type
struct tdm_client {
u32 client_id;
u32 (*tdm_read)(u32 client_id, short chn_id, short
*pcm_buffer, short len);
u32 (*tdm_write)(u32 client_id, short chn_id, short
*pcm_buffer, short len);
wait_queue_head_t *wakeup_event;
}
It consists of:
- client_id: It is basically to identify the particular TDM
device/driver instance.
- tdm_read: It is a function pointer returned by the TDM driver to be
used to read TDM data from a particular TDM channel.
- tdm_write: It is a function pointer returned by the TDM driver to be
used to write TDM data to a particular TDM channel.
- wakeup_event: It is address of a wait_queue event on which the client
keeps on sleeping, and the TDM driver wakes it up periodically. The driver
is configured to
wake up the client after every 10ms.
Once the TDM client gets registered to a TDM driver instance and a TDM
device, it interfaces with the driver using tdm_read, tdm_write and
wakeup_event.
This driver will run on MPC8323E-RDB platforms.
Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
drivers/misc/Kconfig | 14 +
drivers/misc/Makefile | 1 +
drivers/misc/ucc_tdm.c | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/misc/ucc_tdm.h | 221 +++++++++++
4 files changed, 1236 insertions(+), 0 deletions(-)
create mode 100644 drivers/misc/ucc_tdm.c
create mode 100644 drivers/misc/ucc_tdm.h
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index b5e67c0..628b14b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -232,4 +232,18 @@ config ATMEL_SSC
If unsure, say N.
+config UCC_TDM
+ bool "Freescale UCC TDM Driver"
+ depends on QUICC_ENGINE && UCC_FAST
+ default n
+ ---help---
+ The TDM driver is for UCC based TDM devices for example, TDM device on
+ MPC832x RDB. Select it to run PowerVoIP on MPC832x RDB board.
+ The TDM driver can interface with SLIC kind of devices to transmit
+ and receive TDM samples. The TDM driver receives Time Division
+ multiplexed samples(for different channels) from the SLIC device,
+ demutiplexes them and sends them to the upper layers. At the transmit
+ end the TDM drivers receives samples for different channels, it
+ multiplexes them and sends them to the SLIC device.
+
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 87f2685..6f0c49d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
+obj-$(CONFIG_UCC_TDM) += ucc_tdm.o
diff --git a/drivers/misc/ucc_tdm.c b/drivers/misc/ucc_tdm.c
new file mode 100644
index 0000000..98e7c72
--- /dev/null
+++ b/drivers/misc/ucc_tdm.c
@@ -0,0 +1,1000 @@
+/*
+ * drivers/misc/ucc_tdm.c
+ *
+ * UCC Based Linux TDM Driver
+ * This driver is designed to support UCC based TDM for PowerPC processors.
+ * This driver can interface with SLIC device to run VOIP kind of
+ * applications.
+ *
+ * Author: Ashish Kalra & Poonam Aggrwal
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/autoconf.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+#include <linux/irq.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/timer.h>
+
+#include <asm/immap_qe.h>
+#include <asm/qe.h>
+#include <asm/ucc.h>
+#include <asm/ucc_fast.h>
+#include <asm/ucc_slow.h>
+
+#include "ucc_tdm.h"
+#define DRV_DESC "Freescale QE UCC TDM Driver"
+#define DRV_NAME "ucc_tdm"
+
+
+/*
+ * define the following #define if snooping or hardware-based cache coherency
+ * is disabled on the UCC transparent controller.This flag enables
+ * software-based cache-coherency support by explicitly flushing data cache
+ * contents after setting up the TDM output buffer(s) and invalidating the
+ * data cache contents before the TDM input buffer(s) are read.
+ */
+#undef UCC_CACHE_SNOOPING_DISABLED
+
+#define MAX_NUM_TDM_DEVICES 8
+
+static struct tdm_ctrl *tdm_ctrl[MAX_NUM_TDM_DEVICES];
+
+static int num_tdm_devices;
+static int num_tdm_clients;
+
+static struct ucc_tdm_info utdm_primary_info = {
+ .uf_info = {
+ .tsa = 1,
+ .cdp = 1,
+ .cds = 1,
+ .ctsp = 1,
+ .ctss = 1,
+ .revd = 1,
+ .urfs = 0x128,
+ .utfs = 0x128,
+ .utfet = 0,
+ .utftt = 0x128,
+ .ufpt = 256,
+ .ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_TRANSPARENT,
+ .tenc = UCC_FAST_TX_ENCODING_NRZ,
+ .renc = UCC_FAST_RX_ENCODING_NRZ,
+ .tcrc = UCC_FAST_16_BIT_CRC,
+ .synl = UCC_FAST_SYNC_LEN_NOT_USED,
+ },
+ .ucc_busy = 0,
+};
+
+static struct ucc_tdm_info utdm_info[8];
+
+static void dump_siram(struct tdm_ctrl *tdm_c)
+{
+#ifdef DEBUG
+ int i;
+ u16 phy_num_ts;
+
+ phy_num_ts = tdm_c->physical_num_ts;
+
+ pr_debug("SI TxRAM dump\n");
+ /* each slot entry in SI RAM is of 2 bytes */
+ for (i = 0; i < phy_num_ts * 2; i++)
+ pr_debug("%x ", in_8(&qe_immr->sir.tx[i]));
+ pr_debug("\nSI RxRAM dump\n");
+ for (i = 0; i < phy_num_ts * 2; i++)
+ pr_debug("%x ", in_8(&qe_immr->sir.rx[i]));
+ pr_debug("\n");
+#endif
+}
+
+static void dump_ucc(struct tdm_ctrl *tdm_c)
+{
+#ifdef DEBUG
+ struct ucc_transparent_pram *ucc_pram;
+
+ ucc_pram = tdm_c->ucc_pram;
+
+ pr_debug("%s Dumping UCC Registers\n", __FUNCTION__);
+ ucc_fast_dump_regs(tdm_c->uf_private);
+ pr_debug("%s Dumping UCC Parameter RAM\n", __FUNCTION__);
+ pr_debug("rbase = 0x%x\n", in_be32(&ucc_pram->rbase));
+ pr_debug("rbptr = 0x%x\n", in_be32(&ucc_pram->rbptr));
+ pr_debug("mrblr = 0x%x\n", in_be16(&ucc_pram->mrblr));
+ pr_debug("rbdlen = 0x%x\n", in_be16(&ucc_pram->rbdlen));
+ pr_debug("rbdstat = 0x%x\n", in_be16(&ucc_pram->rbdstat));
+ pr_debug("rstate = 0x%x\n", in_be32(&ucc_pram->rstate));
+ pr_debug("rdptr = 0x%x\n", in_be32(&ucc_pram->rdptr));
+ pr_debug("tbase = 0x%x\n", in_be32(&ucc_pram->tbase));
+ pr_debug("tbptr = 0x%x\n", in_be32(&ucc_pram->tbptr));
+ pr_debug("tbdlen = 0x%x\n", in_be16(&ucc_pram->tbdlen));
+ pr_debug("tbdstat = 0x%x\n", in_be16(&ucc_pram->tbdstat));
+ pr_debug("tstate = 0x%x\n", in_be32(&ucc_pram->tstate));
+ pr_debug("tdptr = 0x%x\n", in_be32(&ucc_pram->tdptr));
+#endif
+}
+
+/*
+ * For use when a framing bit is not present
+ * Program current-route SI ram
+ * Set SIxRAM TDMx
+ * Entries must be in units of 8.
+ * SIR_UCC -> Channel Select
+ * SIR_CNT -> Number of bits or bytes
+ * SIR_BYTE -> Byte or Bit resolution
+ * SIR_LAST -> Indicates last entry in SIxRAM
+ * SIR_IDLE -> The Tx data pin is Tri-stated and the Rx data pin is
+ * ignored
+ */
+static void set_siram(struct tdm_ctrl *tdm_c, enum comm_dir dir)
+{
+ const u16 *mask;
+ u16 temp_mask = 1;
+ u16 siram_code = 0;
+ u32 i, j, k;
+ u32 ucc;
+ u32 phy_num_ts;
+
+ phy_num_ts = tdm_c->physical_num_ts;
+ ucc = tdm_c->ut_info->uf_info.ucc_num;
+
+ if (dir == COMM_DIR_RX)
+ mask = tdm_c->rx_mask;
+ else
+ mask = tdm_c->tx_mask;
+ k = 0;
+ j = 0;
+ for (i = 0; i < phy_num_ts; i++) {
+ if ((mask[k] & temp_mask) == temp_mask)
+ siram_code = SIR_UCC(ucc) | SIR_CNT(0) | SIR_BYTE;
+ else
+ siram_code = SIR_IDLE | SIR_CNT(0) | SIR_BYTE;
+ if (dir == COMM_DIR_RX)
+ out_be16((u16 *)&qe_immr->sir.rx[i * 2], siram_code);
+ else
+ out_be16((u16 *)&qe_immr->sir.tx[i * 2], siram_code);
+ temp_mask = temp_mask << 1;
+ j++;
+ if (j >= 16) {
+ j = 0;
+ temp_mask = 0x0001;
+ k++;
+ }
+ }
+ siram_code = siram_code | SIR_LAST;
+
+ if (dir == COMM_DIR_RX)
+ out_be16((u16 *)&qe_immr->sir.rx[(phy_num_ts - 1) * 2],
+ siram_code);
+ else
+ out_be16((u16 *)&qe_immr->sir.tx[(phy_num_ts - 1) * 2],
+ siram_code);
+}
+
+static void config_si(struct tdm_ctrl *tdm_c)
+{
+ u8 rxsyncdelay, txsyncdelay, tdm_port;
+ u16 sixmr_val = 0;
+ u32 tdma_mode_off;
+ u16 *si1_tdm_mode_reg;
+
+ tdm_port = tdm_c->tdm_port;
+
+ set_siram(tdm_c, COMM_DIR_RX);
+
+ set_siram(tdm_c, COMM_DIR_TX);
+
+ rxsyncdelay = tdm_c->cfg_ctrl.rx_fr_sync_delay;
+ txsyncdelay = tdm_c->cfg_ctrl.tx_fr_sync_delay;
+ if (tdm_c->cfg_ctrl.com_pin)
+ sixmr_val |= SIMODE_CRT;
+ if (tdm_c->cfg_ctrl.fr_sync_level == 1)
+ sixmr_val |= SIMODE_SL;
+ if (tdm_c->cfg_ctrl.clk_edge == 1)
+ sixmr_val |= SIMODE_CE;
+ if (tdm_c->cfg_ctrl.fr_sync_edge == 1)
+ sixmr_val |= SIMODE_FE;
+ sixmr_val |= (SIMODE_TFSD(txsyncdelay) | SIMODE_RFSD(rxsyncdelay));
+
+ tdma_mode_off = SI_TDM_MODE_REGISTER_OFFSET * tdm_c->tdm_port;
+
+ si1_tdm_mode_reg = (u8 *)&qe_immr->si1 + tdma_mode_off;
+ out_be16(si1_tdm_mode_reg, sixmr_val);
+
+ dump_siram(tdm_c);
+}
+
+static int tdm_init(struct tdm_ctrl *tdm_c)
+{
+ u32 tdm_port, ucc, act_num_ts;
+ int ret, i, err;
+ u32 cecr_subblock;
+ u32 pram_offset;
+ u32 rxbdt_offset;
+ u32 txbdt_offset;
+ u32 rx_ucode_buf_offset, tx_ucode_buf_offset;
+ u16 bd_status, bd_len;
+ enum qe_clock clock;
+ struct qe_bd __iomem *rx_bd, *tx_bd;
+
+ tdm_port = tdm_c->tdm_port;
+ ucc = tdm_c->ut_info->uf_info.ucc_num;
+ act_num_ts = tdm_c->cfg_ctrl.active_num_ts;
+
+ /*
+ * TDM Tx and Rx CLKs = 2048 KHz.
+ */
+ if (strstr(tdm_c->ut_info->uf_info.tdm_tx_clk, "BRG")) {
+ clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_tx_clk);
+ err = qe_setbrg(clock, 2048000, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
+ tdm_c->ut_info->uf_info.tdm_tx_clk);
+ return err;
+ }
+ }
+ if (strstr(tdm_c->ut_info->uf_info.tdm_rx_clk, "BRG")) {
+ clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_rx_clk);
+ err = qe_setbrg(clock, 2048000, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
+ tdm_c->ut_info->uf_info.tdm_rx_clk);
+ return err;
+ }
+ }
+ /*
+ * TDM FSyncs = 4 KHz.
+ */
+ if (strstr(tdm_c->ut_info->uf_info.tdm_tx_sync, "BRG")) {
+ clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_tx_sync);
+ err = qe_setbrg(clock, 4000, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
+ tdm_c->ut_info->uf_info.tdm_tx_sync);
+ return err;
+ }
+ }
+ if (strstr(tdm_c->ut_info->uf_info.tdm_rx_sync, "BRG")) {
+ clock = qe_clock_source(tdm_c->ut_info->uf_info.tdm_rx_sync);
+ err = qe_setbrg(clock, 4000, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to set %s\n", __FUNCTION__,
+ tdm_c->ut_info->uf_info.tdm_rx_sync);
+ return err;
+ }
+ }
+
+ tdm_c->ut_info->uf_info.uccm_mask = (u32)
+ ((UCC_TRANS_UCCE_RXB | UCC_TRANS_UCCE_BSY) << 16);
+
+ if (ucc_fast_init(&(tdm_c->ut_info->uf_info), &tdm_c->uf_private)) {
+ printk(KERN_ERR "%s: Failed to init uccf\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ ucc_fast_disable(tdm_c->uf_private, COMM_DIR_RX | COMM_DIR_TX);
+
+ /* Write to QE CECR, UCCx channel to Stop Transmission */
+ cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
+ qe_issue_cmd(QE_STOP_TX, cecr_subblock,
+ (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+ pram_offset = qe_muram_alloc(UCC_TRANSPARENT_PRAM_SIZE,
+ ALIGNMENT_OF_UCC_SLOW_PRAM);
+ if (IS_ERR_VALUE(pram_offset)) {
+ printk(KERN_ERR "%s: Cannot allocate MURAM memory for"
+ " transparent UCC\n", __FUNCTION__);
+ ret = -ENOMEM;
+ goto pram_alloc_error;
+ }
+
+ cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
+ qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, cecr_subblock,
+ QE_CR_PROTOCOL_UNSPECIFIED, pram_offset);
+
+ tdm_c->ucc_pram = qe_muram_addr(pram_offset);
+ tdm_c->ucc_pram_offset = pram_offset;
+
+ /*
+ * zero-out pram, this will also ensure RSTATE, TSTATE are cleared, also
+ * DISFC & CRCEC counters will be initialized.
+ */
+ memset(tdm_c->ucc_pram, 0, sizeof(struct ucc_transparent_pram));
+
+ /* rbase, tbase alignment is 8. */
+ rxbdt_offset = qe_muram_alloc(NR_BUFS * sizeof(struct qe_bd),
+ QE_ALIGNMENT_OF_BD);
+ if (IS_ERR_VALUE(rxbdt_offset)) {
+ printk(KERN_ERR "%s: Cannot allocate MURAM memory for RxBDs\n",
+ __FUNCTION__);
+ ret = -ENOMEM;
+ goto rxbd_alloc_error;
+ }
+ txbdt_offset = qe_muram_alloc(NR_BUFS * sizeof(struct qe_bd),
+ QE_ALIGNMENT_OF_BD);
+ if (IS_ERR_VALUE(txbdt_offset)) {
+ printk(KERN_ERR "%s: Cannot allocate MURAM memory for TxBDs\n",
+ __FUNCTION__);
+ ret = -ENOMEM;
+ goto txbd_alloc_error;
+ }
+ tdm_c->tx_bd = qe_muram_addr(txbdt_offset);
+ tdm_c->rx_bd = qe_muram_addr(rxbdt_offset);
+
+ tdm_c->tx_bd_offset = txbdt_offset;
+ tdm_c->rx_bd_offset = rxbdt_offset;
+
+ rx_bd = tdm_c->rx_bd;
+ tx_bd = tdm_c->tx_bd;
+
+ out_be32(&tdm_c->ucc_pram->rbase, (u32) immrbar_virt_to_phys(rx_bd));
+ out_be32(&tdm_c->ucc_pram->tbase, (u32) immrbar_virt_to_phys(tx_bd));
+
+ for (i = 0; i < NR_BUFS - 1; i++) {
+ bd_status = (u16) ((R_E | R_CM | R_I) >> 16);
+ bd_len = 0;
+ out_be16(&rx_bd->length, bd_len);
+ out_be16(&rx_bd->status, bd_status);
+ out_be32(&rx_bd->buf,
+ tdm_c->dma_input_addr + i * SAMPLE_DEPTH * act_num_ts);
+ rx_bd += 1;
+
+ bd_status = (u16) ((T_R | T_CM) >> 16);
+ bd_len = SAMPLE_DEPTH * act_num_ts;
+ out_be16(&tx_bd->length, bd_len);
+ out_be16(&tx_bd->status, bd_status);
+ out_be32(&tx_bd->buf,
+ tdm_c->dma_output_addr + i * SAMPLE_DEPTH * act_num_ts);
+ tx_bd += 1;
+ }
+
+ bd_status = (u16) ((R_E | R_CM | R_I | R_W) >> 16);
+ bd_len = 0;
+ out_be16(&rx_bd->length, bd_len);
+ out_be16(&rx_bd->status, bd_status);
+ out_be32(&rx_bd->buf,
+ tdm_c->dma_input_addr + i * SAMPLE_DEPTH * act_num_ts);
+
+ bd_status = (u16) ((T_R | T_CM | T_W) >> 16);
+ bd_len = SAMPLE_DEPTH * act_num_ts;
+ out_be16(&tx_bd->length, bd_len);
+ out_be16(&tx_bd->status, bd_status);
+ out_be32(&tx_bd->buf,
+ tdm_c->dma_output_addr + i * SAMPLE_DEPTH * act_num_ts);
+
+ config_si(tdm_c);
+
+ setbits32(&qe_immr->ic.qimr, (0x80000000UL >> ucc));
+
+ rx_ucode_buf_offset = qe_muram_alloc(32, 32);
+ if (IS_ERR_VALUE(rx_ucode_buf_offset)) {
+ printk(KERN_ERR "%s: Cannot allocate MURAM mem for Rx"
+ " ucode buf\n", __FUNCTION__);
+ ret = -ENOMEM;
+ goto rxucode_buf_alloc_error;
+ }
+
+ tx_ucode_buf_offset = qe_muram_alloc(32, 32);
+ if (IS_ERR_VALUE(tx_ucode_buf_offset)) {
+ printk(KERN_ERR "%s: Cannot allocate MURAM mem for Tx"
+ " ucode buf\n", __FUNCTION__);
+ ret = -ENOMEM;
+ goto txucode_buf_alloc_error;
+ }
+ out_be16(&tdm_c->ucc_pram->riptr, (u16) rx_ucode_buf_offset);
+ out_be16(&tdm_c->ucc_pram->tiptr, (u16) tx_ucode_buf_offset);
+
+ tdm_c->rx_ucode_buf_offset = rx_ucode_buf_offset;
+ tdm_c->tx_ucode_buf_offset = tx_ucode_buf_offset;
+
+ /*
+ * set the receive buffer descriptor maximum size to be
+ * SAMPLE_DEPTH * number of active RX channels
+ */
+ out_be16(&tdm_c->ucc_pram->mrblr, (u16) SAMPLE_DEPTH * act_num_ts);
+
+ /*
+ * enable snooping and BE byte ordering on the UCC pram's
+ * tstate & rstate registers.
+ */
+ out_be32(&tdm_c->ucc_pram->tstate, 0x30000000UL);
+ out_be32(&tdm_c->ucc_pram->rstate, 0x30000000UL);
+
+ /*Put UCC transparent controller into serial interface mode. */
+ out_be32(&tdm_c->uf_regs->upsmr, 0);
+
+ /* Reset TX and RX for UCCx */
+ cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
+ qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock,
+ (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+ return 0;
+
+txucode_buf_alloc_error:
+ qe_muram_free(rx_ucode_buf_offset);
+rxucode_buf_alloc_error:
+ qe_muram_free(txbdt_offset);
+txbd_alloc_error:
+ qe_muram_free(rxbdt_offset);
+rxbd_alloc_error:
+ qe_muram_free(pram_offset);
+pram_alloc_error:
+ ucc_fast_free(tdm_c->uf_private);
+ return ret;
+}
+
+static void tdm_deinit(struct tdm_ctrl *tdm_c)
+{
+ qe_muram_free(tdm_c->rx_ucode_buf_offset);
+ qe_muram_free(tdm_c->tx_ucode_buf_offset);
+
+ if (tdm_c->rx_bd_offset) {
+ qe_muram_free(tdm_c->rx_bd_offset);
+ tdm_c->rx_bd = NULL;
+ tdm_c->rx_bd_offset = 0;
+ }
+ if (tdm_c->tx_bd_offset) {
+ qe_muram_free(tdm_c->tx_bd_offset);
+ tdm_c->tx_bd = NULL;
+ tdm_c->tx_bd_offset = 0;
+ }
+ if (tdm_c->ucc_pram_offset) {
+ qe_muram_free(tdm_c->ucc_pram_offset);
+ tdm_c->ucc_pram = NULL;
+ tdm_c->ucc_pram_offset = 0;
+ }
+}
+
+
+static irqreturn_t tdm_isr(int irq, void *dev_id)
+{
+ u8 *input_tdm_buffer, *output_tdm_buffer;
+ u32 txb, rxb;
+ u32 ucc;
+ register u32 ucce = 0;
+ struct tdm_ctrl *tdm_c;
+ tdm_c = (struct tdm_ctrl *)dev_id;
+
+ tdm_c->tdm_icnt++;
+ ucc = tdm_c->ut_info->uf_info.ucc_num;
+ input_tdm_buffer = tdm_c->tdm_input_data;
+ output_tdm_buffer = tdm_c->tdm_output_data;
+
+ if (in_be32(tdm_c->uf_private->p_ucce) &
+ (UCC_TRANS_UCCE_BSY << 16)) {
+ out_be32(tdm_c->uf_private->p_ucce,
+ (UCC_TRANS_UCCE_BSY << 16));
+ pr_info("%s: From tdm isr busy interrupt\n",
+ __FUNCTION__);
+ dump_ucc(tdm_c);
+
+ return IRQ_HANDLED;
+ }
+
+ if (tdm_c->tdm_flag == 1) {
+ /* track phases for Rx/Tx */
+ tdm_c->phase_rx += 1;
+ if (tdm_c->phase_rx == MAX_PHASE)
+ tdm_c->phase_rx = 0;
+
+ tdm_c->phase_tx += 1;
+ if (tdm_c->phase_tx == MAX_PHASE)
+ tdm_c->phase_tx = 0;
+
+#ifdef CONFIG_TDM_HW_LB_TSA_SLIC
+ {
+ u32 temp_rx, temp_tx, phase_tx, phase_rx;
+ int i;
+ phase_rx = tdm_c->phase_rx;
+ phase_tx = tdm_c->phase_tx;
+ if (phase_rx == 0)
+ phase_rx = MAX_PHASE;
+ else
+ phase_rx -= 1;
+ if (phase_tx == 0)
+ phase_tx = MAX_PHASE;
+ else
+ phase_tx -= 1;
+ temp_rx = phase_rx * SAMPLE_DEPTH * ACTIVE_CH;
+ temp_tx = phase_tx * SAMPLE_DEPTH * ACTIVE_CH;
+
+ /*check if loopback received data on TS0 is correct. */
+ pr_debug("%s: check if loopback received data on TS0"
+ " is correct\n", __FUNCTION__);
+ pr_debug("%d,%d ", phase_rx, phase_tx);
+ for (i = 0; i < 8; i++)
+ pr_debug("%1d,%1d ",
+ input_tdm_buffer[temp_rx + i],
+ output_tdm_buffer[temp_tx + i]);
+ pr_debug("\n");
+ }
+#endif
+
+ /* schedule BH */
+ wake_up_interruptible(&tdm_c->wakeup_event);
+ } else {
+ if (tdm_c->tdm_icnt == STUTTER_INT_CNT) {
+ txb = in_be32(&tdm_c->ucc_pram->tbptr) -
+ in_be32(&tdm_c->ucc_pram->tbase);
+ rxb = in_be32(&tdm_c->ucc_pram->rbptr) -
+ in_be32(&tdm_c->ucc_pram->rbase);
+ tdm_c->phase_tx = txb / sizeof(struct qe_bd);
+ tdm_c->phase_rx = rxb / sizeof(struct qe_bd);
+
+#ifdef CONFIG_TDM_HW_LB_TSA_SLIC
+ tdm_c->phase_tx = tdm_c->phase_rx;
+#endif
+
+ /* signal "stuttering" period is over */
+ tdm_c->tdm_flag = 1;
+
+ pr_debug("%s: stuttering period is over\n",
+ __FUNCTION__);
+
+ if (in_be32(tdm_c->uf_private->p_ucce) &
+ (UCC_TRANS_UCCE_TXE << 16)) {
+ u32 cecr_subblock;
+ out_be32(tdm_c->uf_private->p_ucce,
+ (UCC_TRANS_UCCE_TXE << 16));
+ pr_debug("%s: From tdm isr txe interrupt\n",
+ __FUNCTION__);
+
+ cecr_subblock =
+ ucc_fast_get_qe_cr_subblock(ucc);
+ qe_issue_cmd(QE_RESTART_TX, cecr_subblock,
+ (u8) QE_CR_PROTOCOL_UNSPECIFIED,
+ 0);
+ }
+ }
+ }
+
+ ucce = (in_be32(tdm_c->uf_private->p_ucce)
+ & in_be32(tdm_c->uf_private->p_uccm));
+
+ out_be32(tdm_c->uf_private->p_ucce, ucce);
+
+ return IRQ_HANDLED;
+}
+
+static int tdm_start(struct tdm_ctrl *tdm_c)
+{
+ if (request_irq(tdm_c->ut_info->uf_info.irq, tdm_isr,
+ 0, "tdm", tdm_c)) {
+ printk(KERN_ERR "%s: request_irq for tdm_isr failed\n",
+ __FUNCTION__);
+ return -ENODEV;
+ }
+
+ ucc_fast_enable(tdm_c->uf_private, COMM_DIR_RX | COMM_DIR_TX);
+
+ pr_info("%s 16-bit linear pcm mode active with"
+ " slots 0 & 2\n", __FUNCTION__);
+
+ dump_siram(tdm_c);
+ dump_ucc(tdm_c);
+
+ setbits8(&(qe_immr->si1.siglmr1_h), (0x1 << tdm_c->tdm_port));
+ pr_info("%s UCC based TDM enabled\n", __FUNCTION__);
+
+ return 0;
+}
+
+static void tdm_stop(struct tdm_ctrl *tdm_c)
+{
+ u32 port, si;
+ u32 ucc;
+ u32 cecr_subblock;
+
+ port = tdm_c->tdm_port;
+ si = tdm_c->si;
+ ucc = tdm_c->ut_info->uf_info.ucc_num;
+ cecr_subblock = ucc_fast_get_qe_cr_subblock(ucc);
+
+ qe_issue_cmd(QE_GRACEFUL_STOP_TX, cecr_subblock,
+ (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
+ qe_issue_cmd(QE_CLOSE_RX_BD, cecr_subblock,
+ (u8) QE_CR_PROTOCOL_UNSPECIFIED, 0);
+
+ clrbits8(&qe_immr->si1.siglmr1_h, (0x1 << port));
+ ucc_fast_disable(tdm_c->uf_private, COMM_DIR_RX);
+ ucc_fast_disable(tdm_c->uf_private, COMM_DIR_TX);
+ free_irq(tdm_c->ut_info->uf_info.irq, tdm_c);
+}
+
+
+static void config_tdm(struct tdm_ctrl *tdm_c)
+{
+ u32 i, j, k;
+
+ j = 0;
+ k = 0;
+
+ /* Set Mask Bits */
+ for (i = 0; i < ACTIVE_CH; i++) {
+ tdm_c->tx_mask[k] |= (1 << j);
+ tdm_c->rx_mask[k] |= (1 << j);
+ j++;
+ if (j >= 16) {
+ j = 0;
+ k++;
+ }
+ }
+ /* physical number of slots in a frame */
+ tdm_c->physical_num_ts = NUM_TS;
+
+ /* common receive and transmit pins */
+ tdm_c->cfg_ctrl.com_pin = 1;
+
+ /* L1R/TSYNC active logic "1" */
+ tdm_c->cfg_ctrl.fr_sync_level = 0;
+
+ /*
+ * TX data on rising edge of clock
+ * RX data on falling edge
+ */
+ tdm_c->cfg_ctrl.clk_edge = 0;
+
+ /* Frame sync sampled on falling edge */
+ tdm_c->cfg_ctrl.fr_sync_edge = 0;
+
+ /* no bit delay */
+ tdm_c->cfg_ctrl.rx_fr_sync_delay = 0;
+
+ /* no bit delay */
+ tdm_c->cfg_ctrl.tx_fr_sync_delay = 0;
+
+#ifndef CONFIG_TDM_HW_LB_TSA_SLIC
+ if (tdm_c->leg_slic) {
+ /* Need 1 bit delay for Legrity SLIC */
+ tdm_c->cfg_ctrl.rx_fr_sync_delay = 1;
+ tdm_c->cfg_ctrl.tx_fr_sync_delay = 1;
+ pr_info("%s Delay for Legerity!\n", __FUNCTION__);
+ }
+#endif
+
+ tdm_c->cfg_ctrl.active_num_ts = ACTIVE_CH;
+}
+
+static void tdm_read(u32 client_id, short chn_id, short *pcm_buffer,
+ short len)
+{
+ int i;
+ u32 phase_rx;
+ /* point to where to start for the current phase data processing */
+ u32 temp_rx;
+
+ struct tdm_ctrl *tdm_c = tdm_ctrl[client_id];
+
+ u16 *input_tdm_buffer =
+ (u16 *)tdm_c->tdm_input_data;
+
+ phase_rx = tdm_c->phase_rx;
+ if (phase_rx == 0)
+ phase_rx = MAX_PHASE;
+ else
+ phase_rx -= 1;
+
+ temp_rx = phase_rx * SAMPLE_DEPTH * EFF_ACTIVE_CH;
+
+#ifdef UCC_CACHE_SNOOPING_DISABLED
+ flush_dcache_range((size_t) &input_tdm_buffer[temp_rx],
+ (size_t) &input_tdm_buffer[temp_rx +
+ SAMPLE_DEPTH * ACTIVE_CH]);
+#endif
+ for (i = 0; i < len; i++)
+ pcm_buffer[i] =
+ input_tdm_buffer[i * EFF_ACTIVE_CH + temp_rx + chn_id];
+
+}
+
+static void tdm_write(u32 client_id, short chn_id, short *pcm_buffer,
+ short len)
+{
+ int i;
+ int phase_tx;
+ u32 txb;
+ /* point to where to start for the current phase data processing */
+ int temp_tx;
+ struct tdm_ctrl *tdm_c = tdm_ctrl[client_id];
+
+ u16 *output_tdm_buffer;
+ output_tdm_buffer = (u16 *)tdm_c->tdm_output_data;
+ txb = in_be32(&tdm_c->ucc_pram->tbptr) -
+ in_be32(&tdm_c->ucc_pram->tbase);
+ phase_tx = txb / sizeof(struct qe_bd);
+
+ if (phase_tx == 0)
+ phase_tx = MAX_PHASE;
+ else
+ phase_tx -= 1;
+
+ temp_tx = phase_tx * SAMPLE_DEPTH * EFF_ACTIVE_CH;
+
+ for (i = 0; i < len; i++)
+ output_tdm_buffer[i * EFF_ACTIVE_CH + temp_tx + chn_id] =
+ pcm_buffer[i];
+
+#ifdef UCC_CACHE_SNOOPING_DISABLED
+ flush_dcache_range((size_t) &output_tdm_buffer[temp_tx],
+ (size_t) &output_tdm_buffer[temp_tx + SAMPLE_DEPTH *
+ ACTIVE_CH]);
+#endif
+}
+
+
+static int tdm_register_client(struct tdm_client *tdm_client)
+{
+ u32 i;
+ if (num_tdm_clients == num_tdm_devices) {
+ printk(KERN_ERR "all TDM devices busy\n");
+ return -EBUSY;
+ }
+
+ for (i = 0; i < num_tdm_devices; i++) {
+ if (!tdm_ctrl[i]->device_busy) {
+ tdm_ctrl[i]->device_busy = 1;
+ break;
+ }
+ }
+ num_tdm_clients++;
+ tdm_client->client_id = i;
+ tdm_client->tdm_read = tdm_read;
+ tdm_client->tdm_write = tdm_write;
+ tdm_client->wakeup_event =
+ &(tdm_ctrl[i]->wakeup_event);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tdm_register_client);
+
+static int tdm_deregister_client(struct tdm_client *tdm_client)
+{
+ num_tdm_clients--;
+ tdm_ctrl[tdm_client->client_id]->device_busy = 0;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tdm_deregister_client);
+
+static int ucc_tdm_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ struct device_node *np = ofdev->node;
+ struct resource res;
+ const unsigned int *prop;
+ u32 ucc_num, device_num, err, ret = 0;
+ struct device_node *np_tmp = NULL;
+ dma_addr_t physaddr;
+ void *tdm_buff;
+ struct ucc_tdm_info *ut_info;
+
+ prop = of_get_property(np, "device-id", NULL);
+ ucc_num = *prop - 1;
+ if ((ucc_num < 0) || (ucc_num > 7))
+ return -ENODEV;
+
+ ut_info = &utdm_info[ucc_num];
+ if (ut_info == NULL) {
+ printk(KERN_ERR "additional data missing\n");
+ return -ENODEV;
+ }
+ if (ut_info->ucc_busy) {
+ printk(KERN_ERR "UCC in use by another TDM driver instance\n");
+ return -EBUSY;
+ }
+ if (num_tdm_devices == MAX_NUM_TDM_DEVICES) {
+ printk(KERN_ERR "All TDM devices already initialized\n");
+ return -ENODEV;
+ }
+
+ ut_info->ucc_busy = 1;
+ tdm_ctrl[num_tdm_devices++] =
+ kzalloc(sizeof(struct tdm_ctrl), GFP_KERNEL);
+ if (!tdm_ctrl[num_tdm_devices - 1]) {
+ printk(KERN_ERR "%s: no memory to allocate for"
+ " tdm control structure\n", __FUNCTION__);
+ num_tdm_devices--;
+ return -ENOMEM;
+ }
+ device_num = num_tdm_devices - 1;
+
+ tdm_ctrl[device_num]->device = &ofdev->dev;
+ tdm_ctrl[device_num]->ut_info = ut_info;
+
+ tdm_ctrl[device_num]->ut_info->uf_info.ucc_num = ucc_num;
+
+ prop = of_get_property(np, "fsl,tdm-num", NULL);
+ if (prop == NULL) {
+ ret = -EINVAL;
+ goto get_property_error;
+ }
+
+ tdm_ctrl[device_num]->tdm_port = *prop - 1;
+
+ if (tdm_ctrl[device_num]->tdm_port > 3) {
+ ret = -EINVAL;
+ goto get_property_error;
+ }
+
+ prop = of_get_property(np, "fsl,si-num", NULL);
+ if (prop == NULL) {
+ ret = -EINVAL;
+ goto get_property_error;
+ }
+
+ tdm_ctrl[device_num]->si = *prop - 1;
+
+ tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_clk =
+ (char *) of_get_property(np, "fsl,tdm-tx-clk", NULL);
+ if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_clk == NULL) {
+ ret = -EINVAL;
+ goto get_property_error;
+ }
+
+ tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_clk =
+ (char *) of_get_property(np, "fsl,tdm-rx-clk", NULL);
+ if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_clk == NULL) {
+ ret = -EINVAL;
+ goto get_property_error;
+ }
+
+ tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_sync =
+ (char *) of_get_property(np, "fsl,tdm-tx-sync", NULL);
+ if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_sync == NULL) {
+ ret = -EINVAL;
+ goto get_property_error;
+ }
+
+ tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_sync =
+ (char *) of_get_property(np, "fsl,tdm-rx-sync", NULL);
+ if (tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_sync == NULL) {
+ ret = -EINVAL;
+ goto get_property_error;
+ }
+
+ tdm_ctrl[device_num]->ut_info->uf_info.irq =
+ irq_of_parse_and_map(np, 0);
+ err = of_address_to_resource(np, 0, &res);
+ if (err) {
+ ret = EINVAL;
+ goto get_property_error;
+ }
+ tdm_ctrl[device_num]->ut_info->uf_info.regs = res.start;
+ tdm_ctrl[device_num]->uf_regs = of_iomap(np, 0);
+
+ np_tmp = of_find_compatible_node(np_tmp, "slic", "legerity-slic");
+ if (np_tmp != NULL)
+ tdm_ctrl[device_num]->leg_slic = 1;
+ else
+ tdm_ctrl[device_num]->leg_slic = 0;
+
+ config_tdm(tdm_ctrl[device_num]);
+
+ tdm_buff = dma_alloc_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
+ tdm_ctrl[device_num]->cfg_ctrl.active_num_ts,
+ &physaddr, GFP_KERNEL);
+ if (!tdm_buff) {
+ printk(KERN_ERR "ucc-tdm: could not allocate buffer"
+ "descriptors\n");
+ ret = -ENOMEM;
+ goto get_property_error;
+ }
+
+ tdm_ctrl[device_num]->tdm_input_data = tdm_buff;
+ tdm_ctrl[device_num]->dma_input_addr = physaddr;
+
+ tdm_ctrl[device_num]->tdm_output_data = tdm_buff + NR_BUFS *
+ SAMPLE_DEPTH * tdm_ctrl[device_num]->cfg_ctrl.active_num_ts;
+ tdm_ctrl[device_num]->dma_output_addr = physaddr + NR_BUFS *
+ SAMPLE_DEPTH * tdm_ctrl[device_num]->cfg_ctrl.active_num_ts;
+
+ init_waitqueue_head(&(tdm_ctrl[device_num]->wakeup_event));
+
+ ret = tdm_init(tdm_ctrl[device_num]);
+ if (ret != 0)
+ goto tdm_init_error;
+
+ ret = tdm_start(tdm_ctrl[device_num]);
+ if (ret != 0)
+ goto tdm_start_error;
+
+ dev_set_drvdata(&(ofdev->dev), tdm_ctrl[device_num]);
+
+ pr_info("%s UCC based tdm module installed\n", __FUNCTION__);
+ return 0;
+
+tdm_start_error:
+ tdm_deinit(tdm_ctrl[device_num]);
+tdm_init_error:
+ dma_free_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
+ tdm_ctrl[device_num]->cfg_ctrl.active_num_ts,
+ tdm_ctrl[device_num]->tdm_input_data,
+ tdm_ctrl[device_num]->dma_input_addr);
+
+get_property_error:
+ kfree(tdm_ctrl[device_num]);
+ return ret;
+}
+
+static int ucc_tdm_remove(struct of_device *ofdev)
+{
+ struct tdm_ctrl *tdm_c;
+ struct ucc_tdm_info *ut_info;
+ u32 ucc_num;
+
+ tdm_c = dev_get_drvdata(&(ofdev->dev));
+ ucc_num = tdm_c->ut_info->uf_info.ucc_num;
+ ut_info = &utdm_info[ucc_num];
+ tdm_stop(tdm_c);
+ tdm_deinit(tdm_c);
+
+ ucc_fast_free(tdm_c->uf_private);
+
+ dma_free_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
+ tdm_c->cfg_ctrl.active_num_ts,
+ tdm_c->tdm_input_data,
+ tdm_c->dma_input_addr);
+
+ num_tdm_devices--;
+ kfree(tdm_c);
+
+ ut_info->ucc_busy = 0;
+
+ pr_info("%s UCC based tdm module uninstalled\n", __FUNCTION__);
+ return 0;
+}
+
+static struct of_device_id ucc_tdm_match[] = {
+ {
+ .type = "tdm",
+ .compatible = "fsl,ucc-tdm",
+ }, {},
+};
+
+MODULE_DEVICE_TABLE(of, ucc_tdm_match);
+
+static struct of_platform_driver ucc_tdm_driver = {
+ .name = DRV_NAME,
+ .match_table = ucc_tdm_match,
+ .probe = ucc_tdm_probe,
+ .remove = ucc_tdm_remove,
+};
+
+static int __init ucc_tdm_init(void)
+{
+ u32 i;
+
+ pr_info("ucc_tdm: " DRV_DESC "\n");
+ for (i = 0; i < 8; i++)
+ memcpy(&(utdm_info[i]), &utdm_primary_info,
+ sizeof(utdm_primary_info));
+
+ return of_register_platform_driver(&ucc_tdm_driver);
+}
+
+static void __exit ucc_tdm_exit(void)
+{
+ of_unregister_platform_driver(&ucc_tdm_driver);
+}
+
+module_init(ucc_tdm_init);
+module_exit(ucc_tdm_exit);
+MODULE_AUTHOR("Freescale Semiconductor, Inc");
+MODULE_DESCRIPTION(DRV_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/ucc_tdm.h b/drivers/misc/ucc_tdm.h
new file mode 100644
index 0000000..eaf2848
--- /dev/null
+++ b/drivers/misc/ucc_tdm.h
@@ -0,0 +1,221 @@
+/*
+ * drivers/misc/ucc_tdm.h
+ *
+ * UCC Based Linux TDM Driver
+ * This driver is designed to support UCC based TDM for PowerPC processors.
+ * This driver can interface with SLIC device to run VOIP kind of
+ * applications.
+ *
+ * Author: Ashish Kalra & Poonam Aggrwal
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+
+#ifndef TDM_H
+#define TDM_H
+
+#define NUM_TS 8
+#define ACTIVE_CH 8
+
+/* SAMPLE_DEPTH is the sample depth is the number of frames before
+ * an interrupt. Must be a multiple of 4
+ */
+#define SAMPLE_DEPTH 80
+
+/* define the number of Rx interrupts to go by for initial stuttering */
+#define STUTTER_INT_CNT 1
+
+/* BMRx Field Descriptions to specify tstate and rstate in UCC parameter RAM*/
+#define EN_BUS_SNOOPING 0x20
+#define BE_BO 0x10
+
+/* UPSMR Register for Transparent UCC controller Bit definitions*/
+#define NBO 0x00000000 /* Normal Mode 1 bit of data per clock */
+
+/* SI Mode register bit definitions */
+#define NORMAL_OPERATION 0x0000
+#define AUTO_ECHO 0x0400
+#define INTERNAL_LB 0x0800
+#define CONTROL_LB 0x0c00
+#define SIMODE_CRT (0x8000 >> 9)
+#define SIMODE_SL (0x8000 >> 10)
+#define SIMODE_CE (0x8000 >> 11)
+#define SIMODE_FE (0x8000 >> 12)
+#define SIMODE_GM (0x8000 >> 13)
+#define SIMODE_TFSD(val) (val)
+#define SIMODE_RFSD(val) ((val) << 8)
+
+#define SI_TDM_MODE_REGISTER_OFFSET 0
+
+#define R_CM 0x02000000
+#define T_CM 0x02000000
+
+#define SET_RX_SI_RAM(n, val) \
+ out_be16((u16 *)&qe_immr->sir.rx[(n)*2], (u16)(val))
+
+#define SET_TX_SI_RAM(n, val) \
+ out_be16((u16 *)&qe_immr->sir.tx[(n)*2], (u16)(val))
+
+/* SI RAM entries */
+#define SIR_LAST 0x0001
+#define SIR_CNT(n) ((n) << 2)
+#define SIR_BYTE 0x0002
+#define SIR_BIT 0x0000
+#define SIR_IDLE 0
+#define SIR_UCC(uccx) (((uccx+9)) << 5)
+
+/* BRGC Register Bit definitions */
+#define BRGC_RESET (0x1<<17)
+#define BRGC_EN (0x1<<16)
+#define BRGC_EXTC_QE (0x00<<14)
+#define BRGC_EXTC_CLK3 (0x01<<14)
+#define BRGC_EXTC_CLK5 (0x01<<15)
+#define BRGC_EXTC_CLK9 (0x01<<14)
+#define BRGC_EXTC_CLK11 (0x01<<14)
+#define BRGC_EXTC_CLK13 (0x01<<14)
+#define BRGC_EXTC_CLK15 (0x01<<15)
+#define BRGC_ATB (0x1<<13)
+#define BRGC_DIV16 (0x1)
+
+/* structure representing UCC transparent parameter RAM */
+struct ucc_transparent_pram {
+ __be16 riptr;
+ __be16 tiptr;
+ __be16 res0;
+ __be16 mrblr;
+ __be32 rstate;
+ __be32 rbase;
+ __be16 rbdstat;
+ __be16 rbdlen;
+ __be32 rdptr;
+ __be32 tstate;
+ __be32 tbase;
+ __be16 tbdstat;
+ __be16 tbdlen;
+ __be32 tdptr;
+ __be32 rbptr;
+ __be32 tbptr;
+ __be32 rcrc;
+ __be32 res1;
+ __be32 tcrc;
+ __be32 res2;
+ __be32 res3;
+ __be32 c_mask;
+ __be32 c_pres;
+ __be16 disfc;
+ __be16 crcec;
+ __be32 res4[4];
+ __be16 ts_tmp;
+ __be16 tmp_mb;
+};
+
+#define UCC_TRANSPARENT_PRAM_SIZE 0x100
+
+struct tdm_cfg {
+ u8 com_pin; /* Common receive and transmit pins
+ * 0 = separate pins
+ * 1 = common pins
+ */
+
+ u8 fr_sync_level; /* SLx bit Frame Sync Polarity
+ * 0 = L1R/TSYNC active logic "1"
+ * 1 = L1R/TSYNC active logic "0"
+ */
+
+ u8 clk_edge; /* CEx bit Tx Rx Clock Edge
+ * 0 = TX data on rising edge of clock
+ * RX data on falling edge
+ * 1 = TX data on falling edge of clock
+ * RX data on rising edge
+ */
+
+ u8 fr_sync_edge; /* FEx bit Frame sync edge
+ * Determine when the sync pulses are sampled
+ * 0 = Falling edge
+ * 1 = Rising edge
+ */
+
+ u8 rx_fr_sync_delay; /* TFSDx/RFSDx bits Frame Sync Delay
+ * 00 = no bit delay
+ * 01 = 1 bit delay
+ * 10 = 2 bit delay
+ * 11 = 3 bit delay
+ */
+
+ u8 tx_fr_sync_delay; /* TFSDx/RFSDx bits Frame Sync Delay
+ * 00 = no bit delay
+ * 01 = 1 bit delay
+ * 10 = 2 bit delay
+ * 11 = 3 bit delay
+ */
+
+ u8 active_num_ts; /* Number of active time slots in TDM
+ * assume same active Rx/Tx time slots
+ */
+};
+
+struct ucc_tdm_info {
+ struct ucc_fast_info uf_info;
+ u32 ucc_busy;
+};
+
+struct tdm_ctrl {
+ u32 device_busy;
+ struct device *device;
+ struct ucc_fast_private *uf_private;
+ struct ucc_tdm_info *ut_info;
+ u32 tdm_port; /* port for this tdm:TDMA,TDMB,TDMC,TDMD */
+ u32 si; /* serial interface: 0 or 1 */
+ struct ucc_fast __iomem *uf_regs; /* UCC Fast registers */
+ u16 rx_mask[8]; /* Active Receive channels LSB is ch0 */
+ u16 tx_mask[8]; /* Active Transmit channels LSB is ch0 */
+ /* Only channels less than the number of FRAME_SIZE are implemented */
+ struct tdm_cfg cfg_ctrl; /* Signaling controls configuration */
+ u8 *tdm_input_data; /* buffer used for Rx by the tdm */
+ u8 *tdm_output_data; /* buffer used for Tx by the tdm */
+
+ dma_addr_t dma_input_addr; /* dma mapped buffer for TDM Rx */
+ dma_addr_t dma_output_addr; /* dma mapped buffer for TDM Tx */
+ u16 physical_num_ts; /* physical number of timeslots in the tdm
+ frame */
+ u32 phase_rx; /* cycles through 0, 1, 2 */
+ u32 phase_tx; /* cycles through 0, 1, 2 */
+ /*
+ * the following two variables are for dealing with "stutter" problem
+ * "stutter" period is about 20 frames or so, varies depending active
+ * channel num depending on the sample depth, the code should let a
+ * few Rx interrupts go by
+ */
+ u32 tdm_icnt;
+ u32 tdm_flag;
+ struct ucc_transparent_pram __iomem *ucc_pram;
+ struct qe_bd __iomem *tx_bd;
+ struct qe_bd __iomem *rx_bd;
+ u32 ucc_pram_offset;
+ u32 tx_bd_offset;
+ u32 rx_bd_offset;
+ u32 rx_ucode_buf_offset;
+ u32 tx_ucode_buf_offset;
+ bool leg_slic;
+ wait_queue_head_t wakeup_event;
+};
+
+struct tdm_client {
+ u32 client_id;
+ void (*tdm_read)(u32 client_id, short chn_id,
+ short *pcm_buffer, short len);
+ void (*tdm_write)(u32 client_id, short chn_id,
+ short *pcm_buffer, short len);
+ wait_queue_head_t *wakeup_event;
+ };
+
+#define MAX_PHASE 1
+#define NR_BUFS 2
+#define EFF_ACTIVE_CH ACTIVE_CH / 2
+
+#endif
--
1.5.2.4
^ permalink raw reply related
* Re: [IPV4 3/5] fib_trie: dump doesnt use RCU
From: David Miller @ 2008-01-24 4:50 UTC (permalink / raw)
To: shemminger; +Cc: netdev
In-Reply-To: <20080123224858.918669715@linux-foundation.org>
From: Stephen Hemminger <shemminger@linux-foundation.org>
Date: Wed, 23 Jan 2008 14:48:47 -0800
> Since fib dump (via netlink) holds the RTNL mutex, it is unnecessary
> to use RCU, and it is impossible to get truncated (-EBUSY) result.
>
> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
You tested this patch, right? :-/
The whole reason we need the nlk->cb[] state is to hold things across
multiple recvmsg() calls that might be necessary to obtain the full
dump.
rtnetlink goes:
rtnl_lock();
netlink_rcv_skb(skb, &rtnetlink_rcv_msg);
...
static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
...
if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
struct sock *rtnl;
rtnl_dumpit_func dumpit;
dumpit = rtnl_get_dumpit(family, type);
if (dumpit == NULL)
return -EOPNOTSUPP;
__rtnl_unlock();
rtnl = net->rtnl;
err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL);
rtnl_lock();
return err;
(NOTE: Drops RTNL semaphore for netlink_dump_start() call)
...
int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
struct nlmsghdr *nlh,
int (*dump)(struct sk_buff *skb,
struct netlink_callback *),
int (*done)(struct netlink_callback *))
{
...
cb->dump = dump;
cb->done = done;
cb->nlh = nlh;
atomic_inc(&skb->users);
cb->skb = skb;
...
mutex_lock(nlk->cb_mutex);
...
nlk->cb = cb;
mutex_unlock(nlk->cb_mutex);
netlink_dump(sk);
...
static int netlink_dump(struct sock *sk)
{
...
mutex_lock(nlk->cb_mutex);
...
len = cb->dump(skb, cb);
if (len > 0) {
mutex_unlock(nlk->cb_mutex);
skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_data_ready(sk, len);
return 0;
}
(NOTE: Therefore cb->dump() runs without RTNL semaphore held)
...
static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
struct msghdr *msg, size_t len,
int flags)
{
...
if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2)
netlink_dump(sk);
...
Therefore, that RTNL assertion you added should have triggered on any
dump you may have tried since ->dump() is always invoked without the
RTNL semaphore since rtnetlink drops it around the ->dump() call and
the call chain for this fib_trie cause would be:
inet_dump_fib()
fn_trie_dump()
and nothing in that code path retakes the RTNL semaphore.
What test did you run to validate this patch for correctness?
^ permalink raw reply
* [PATCH 2/3] Platform changes for UCC TDM driver for MPC8323ERDB.Also includes related QE changes and dts entries.
From: Poonam_Aggrwal-b10812 @ 2008-01-24 4:49 UTC (permalink / raw)
To: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev
Cc: michael.barkowski, kim.phillips, ashish.kalra, timur, rich.cutler
From: Poonam Aggrwal <b10812@freescale.com>
This patch makes necessary changes in the QE and UCC framework to support
TDM. It also adds support to configure the BRG properly through device
tree entries. Includes the device tree changes for UCC TDM driver as well.
It also includes device tree entries for UCC TDM driver.
Tested on MPC8323ERDB platform.
Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
arch/powerpc/boot/dts/mpc832x_rdb.dts | 58 +++++++
arch/powerpc/sysdev/qe_lib/qe.c | 205 ++++++++++++++++++++++++--
arch/powerpc/sysdev/qe_lib/ucc.c | 265 +++++++++++++++++++++++++++++++++
arch/powerpc/sysdev/qe_lib/ucc_fast.c | 37 +++++
include/asm-powerpc/qe.h | 8 +
include/asm-powerpc/ucc.h | 4 +
include/asm-powerpc/ucc_fast.h | 4 +
7 files changed, 568 insertions(+), 13 deletions(-)
diff --git a/arch/powerpc/boot/dts/mpc832x_rdb.dts b/arch/powerpc/boot/dts/mpc832x_rdb.dts
index 388c8a7..c0e6283 100644
--- a/arch/powerpc/boot/dts/mpc832x_rdb.dts
+++ b/arch/powerpc/boot/dts/mpc832x_rdb.dts
@@ -105,6 +105,17 @@
device_type = "par_io";
num-ports = <7>;
+ ucc1pio:ucc_pin@01 {
+ pio-map = <
+ /* port pin dir open_drain assignment has_irq */
+ 0 e 2 0 1 0 /* CLK11 */
+ 3 16 1 0 2 0 /* BRG9 */
+ 3 1b 1 0 2 0 /* BRG3 */
+ 0 0 3 0 2 0 /* TDMATxD0 */
+ 0 4 3 0 2 0 /* TDMARxD0 */
+ 3 1b 2 0 1 0>; /* CLK1 */
+ };
+
ucc2pio:ucc_pin@02 {
pio-map = <
/* port pin dir open_drain assignment has_irq */
@@ -169,6 +180,36 @@
};
};
+ clocks {
+ compatible = "fsl,cpm-clocks";
+ /* clock freqs in Hz(for CLK1~CLK24).
+ * CLK11 is 1024KHz,
+ * all other clocks unused
+ * #clock-cells define number of cells
+ * used by the clock-frequency.
+ * right now only #clock cells=1 is
+ * implemented. Provision is there to
+ * handle frequencies >4Gig
+ */
+ #clock-cells = <1>;
+ clock-frequency = <0 0 0 0 0 0
+ 0 0 0 0 d#1024000 0
+ 0 0 0 0 0 0
+ 0 0 0 0 0 0>;
+ };
+
+ brg@640 {
+ compatible = "fsl,cpm-brg";
+ /* input clock sources for all the 16 BRGs.
+ * 1-24 for CLK1 to CLK24.
+ * BRG9 uses CLK11,BRG1 and BRG2-8 use
+ * the QE clock.
+ */
+ fsl,brg-sources = <0 0 0 0 0 0 0 0
+ b 0 0 0 0 0 0 0>;
+ reg = <640 7f>;
+ };
+
spi@4c0 {
device_type = "spi";
compatible = "fsl_spi";
@@ -187,6 +228,23 @@
mode = "cpu";
};
+ ucc@2000 {
+ device_type = "tdm";
+ compatible = "fsl,ucc-tdm";
+ model = "UCC";
+ device-id = <1>;
+ fsl,tdm-num = <1>;
+ fsl,si-num = <1>;
+ fsl,tdm-tx-clk = "CLK1";
+ fsl,tdm-rx-clk = "CLK1";
+ fsl,tdm-tx-sync = "BRG9";
+ fsl,tdm-rx-sync = "BRG9";
+ reg = <2000 200>;
+ interrupts = <20>;
+ interrupt-parent = <&qeic>;
+ pio-handle = <&ucc1pio>;
+ };
+
ucc@3000 {
device_type = "network";
compatible = "ucc_geth";
diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c
index 1df3b4a..fddc3d8 100644
--- a/arch/powerpc/sysdev/qe_lib/qe.c
+++ b/arch/powerpc/sysdev/qe_lib/qe.c
@@ -149,20 +149,189 @@ EXPORT_SYMBOL(qe_issue_cmd);
*/
static unsigned int brg_clk = 0;
-unsigned int get_brg_clk(void)
+u32 get_brg_clk(enum qe_clock brgclk, enum qe_clock *brg_source)
{
- struct device_node *qe;
- if (brg_clk)
- return brg_clk;
+ struct device_node *qe, *brg, *clocks;
+ enum qe_clock brg_src;
+ u32 brg_input_freq = 0;
+ u32 brg_num;
+ int ret;
+ const unsigned int *prop;
- qe = of_find_node_by_type(NULL, "qe");
- if (qe) {
+ *brg_source = 0;
+
+ brg_num = brgclk - QE_BRG1;
+ brg = of_find_compatible_node(NULL, NULL, "fsl,cpm-brg");
+ if (brg) {
unsigned int size;
- const u32 *prop = of_get_property(qe, "brg-frequency", &size);
- brg_clk = *prop;
- of_node_put(qe);
- };
- return brg_clk;
+ prop = of_get_property(brg,
+ "fsl,brg-sources", &size);
+ of_node_put(brg);
+
+ if (prop)
+ brg_src = *(prop + brg_num);
+ else {
+ printk(KERN_ERR "%s: invalid fsl,brg-sources in device "
+ "tree\n", __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (brg_src == 0) {
+ *brg_source = 0;
+ if (brg_clk > 0)
+ return brg_clk;
+ qe = of_find_node_by_type(NULL, "qe");
+ if (qe) {
+ unsigned int size;
+ prop = of_get_property
+ (qe, "brg-frequency", &size);
+ if (!prop) {
+ printk(KERN_ERR "%s: QE brg-frequency"
+ "not present in device tree\n",
+ __FUNCTION__);
+ ret = -EINVAL;
+ of_node_put(qe);
+ goto err;
+ }
+ if (*prop) {
+ of_node_put(qe);
+ brg_clk = *prop;
+ return *prop;
+ } else {
+ /*
+ * Older versions of U-Boot do not initialize
+ * the brg-frequency property, so in this case
+ * we assume the BRG frequency is half the QE
+ * bus frequency.
+ */
+ prop = of_get_property(qe,
+ "bus-frequency", NULL);
+ of_node_put(qe);
+ if (!prop) {
+ printk(KERN_ERR "%s: "
+ " QE bus-frequency not present"
+ " in device tree\n",
+ __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (*prop) {
+ brg_clk = *prop / 2;
+ return brg_clk;
+ } else {
+ printk(KERN_ERR "%s: invalid"
+ " QE bus-frequency in device"
+ " tree\n", __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ } else {
+ printk(KERN_ERR "%s: no qe node in device tree"
+ "\n", __FUNCTION__);
+ ret = EINVAL;
+ goto err;
+ }
+ } else {
+ *brg_source = brg_src + QE_CLK1 - 1;
+ clocks = of_find_compatible_node(NULL, NULL,
+ "fsl,cpm-clocks");
+ if (!clocks) {
+ printk(KERN_ERR "%s: no clocks node in device"
+ " tree \n", __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ } else {
+ prop = of_get_property(clocks,
+ "#clock-cells", &size);
+ /*
+ * clock-cells = 1 only supported right now.
+ */
+ if (!prop || *prop != 1) {
+ printk(KERN_ERR "%s: invalid "
+ "#clock-cells value in device tree \n",
+ __FUNCTION__);
+ of_node_put(clocks);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ prop = of_get_property(clocks,
+ "clock-frequency", &size);
+ if (!prop) {
+ printk(KERN_ERR "%s: no"
+ " #clock-frequency prop in device"
+ " tree\n", __FUNCTION__);
+ of_node_put(clocks);
+ ret = -EINVAL;
+ goto err;
+ }
+ brg_input_freq = *(prop+(brg_src - 1));
+ of_node_put(clocks);
+ return brg_input_freq;
+ }
+ }
+ } else {
+ printk(KERN_ERR "%s: no brg node in device tree\n",
+ __FUNCTION__);
+ ret = -EINVAL;
+ goto err;
+ }
+err: return ret;
+}
+
+u32 qe_brg_src(int brg_num, enum qe_clock brg_src)
+{
+ u32 clock_bits, shift;
+
+ clock_bits = 0;
+
+ switch (brg_num) {
+ case 1:
+ case 2:
+ case 5:
+ case 6:
+ switch (brg_src) {
+ case QE_CLK3: clock_bits = 1; break;
+ case QE_CLK5: clock_bits = 2; break;
+ default: break;
+ }
+ break;
+ case 3:
+ case 4:
+ case 7:
+ case 8:
+ switch (brg_src) {
+ case QE_CLK9: clock_bits = 1; break;
+ case QE_CLK15: clock_bits = 2; break;
+ default: break;
+ }
+ break;
+ case 9:
+ case 10:
+ switch (brg_src) {
+ case QE_CLK11: clock_bits = 1; break;
+ default: break;
+ }
+ break;
+ case 11:
+ case 15:
+ case 16:
+ switch (brg_src) {
+ case QE_CLK13: clock_bits = 1; break;
+ default: break;
+ }
+ break;
+ default: clock_bits = 0; break;
+ }
+ shift = 14;
+
+ if (!clock_bits)
+ return -ENOENT;
+
+ clock_bits <<= shift;
+
+ return clock_bits;
}
/* Program the BRG to the given sampling rate and multiplier
@@ -177,11 +346,18 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
{
u32 divisor, tempval;
u32 div16 = 0;
+ u32 brg_clock;
+ enum qe_clock brgsrc;
+ u32 src_bits = 0;
if ((brg < QE_BRG1) || (brg > QE_BRG16))
return -EINVAL;
- divisor = get_brg_clk() / (rate * multiplier);
+ brg_clock = get_brg_clk(brg, &brgsrc);
+ if (brg_clock < 0)
+ return -EINVAL;
+
+ divisor = brg_clock / (rate * multiplier);
if (divisor > QE_BRGC_DIVISOR_MAX + 1) {
div16 = QE_BRGC_DIV16;
@@ -194,8 +370,11 @@ int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier)
if (!div16 && (divisor & 1))
divisor++;
+ if (brgsrc > 0)
+ src_bits = qe_brg_src(brg - QE_BRG1 + 1, brgsrc);
+
tempval = ((divisor - 1) << QE_BRGC_DIVISOR_SHIFT) |
- QE_BRGC_ENABLE | div16;
+ QE_BRGC_ENABLE | div16 | src_bits;
out_be32(&qe_immr->brg.brgc[brg - QE_BRG1], tempval);
diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c
index 0e348d9..f2de0ed 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc.c
@@ -213,3 +213,268 @@ int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock,
return 0;
}
+
+int ucc_set_tdm_rxtx_clk(int tdm_num, char *clk_src, enum comm_dir mode)
+{
+ enum qe_clock clock;
+ u32 clock_bits, shift;
+ struct qe_mux *qe_mux_reg = NULL;
+
+ clock_bits = 0;
+ qe_mux_reg = &qe_immr->qmx;
+
+ if ((tdm_num > 3 || tdm_num < 0))
+ return -EINVAL;
+
+ /* The communications direction must be RX or TX */
+ if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX)))
+ return -EINVAL;
+
+ clock = qe_clock_source(clk_src);
+ switch (mode) {
+ case COMM_DIR_RX:
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK3: clock_bits = 6; break;
+ case QE_CLK8: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 28;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK5: clock_bits = 6; break;
+ case QE_CLK10: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 24;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK7: clock_bits = 6; break;
+ case QE_CLK12: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 20;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK9: clock_bits = 6; break;
+ case QE_CLK14: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 16;
+ break;
+ default:
+ break;
+ }
+ break;
+ case COMM_DIR_TX:
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK4: clock_bits = 6; break;
+ case QE_CLK9: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 12;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK6: clock_bits = 6; break;
+ case QE_CLK11: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 8;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK8: clock_bits = 6; break;
+ case QE_CLK13: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 4;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG3: clock_bits = 1; break;
+ case QE_BRG4: clock_bits = 2; break;
+ case QE_CLK1: clock_bits = 4; break;
+ case QE_CLK2: clock_bits = 5; break;
+ case QE_CLK10: clock_bits = 6; break;
+ case QE_CLK15: clock_bits = 7; break;
+ default: break;
+ }
+ shift = 0;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!clock_bits)
+ return -ENOENT;
+
+ clock_bits <<= shift;
+
+ qe_mux_reg->cmxsi1cr_l |= clock_bits;
+
+ return 0;
+}
+
+int ucc_set_tdm_rxtx_sync(int tdm_num, char *sync_src, enum comm_dir mode)
+{
+ enum qe_clock clock;
+ u32 shift, clock_bits;
+ struct qe_mux *qe_mux_reg = NULL;
+ int source;
+
+ source = -1;
+ qe_mux_reg = &qe_immr->qmx;
+
+ if ((tdm_num > 3 || tdm_num < 0))
+ return -EINVAL;
+
+ /* The communications direction must be RX or TX */
+ if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX)))
+ return -EINVAL;
+
+ switch (mode) {
+ case COMM_DIR_RX:
+ if (strcasecmp("RSYNC", sync_src) == 0) {
+ source = 0;
+ shift = 0;
+ break;
+ }
+ clock = qe_clock_source(sync_src);
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 30;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 28;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 26;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 24;
+ break;
+ default:
+ source = -1;
+ break;
+ }
+ break;
+ case COMM_DIR_TX:
+ if (strcasecmp("TSYNC", sync_src) == 0) {
+ source = 0;
+ shift = 0;
+ break;
+ }
+ clock = qe_clock_source(sync_src);
+ switch (tdm_num) {
+ case 0:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 14;
+ break;
+ case 1:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG10: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 12;
+ break;
+ case 2:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 10;
+ break;
+ case 3:
+ switch (clock) {
+ case QE_BRG9: source = 1; break;
+ case QE_BRG11: source = 2; break;
+ default: source = -1; break;
+ }
+ shift = 8;
+ break;
+ default:
+ source = -1;
+ break;
+ }
+ break;
+ default:
+ source = -1;
+ break;
+ }
+
+ if (source == -1)
+ return -ENOENT;
+
+ clock_bits = (u32) source;
+ clock_bits <<= shift;
+
+
+ qe_mux_reg->cmxsi1syr |= clock_bits;
+
+ return 0;
+}
diff --git a/arch/powerpc/sysdev/qe_lib/ucc_fast.c b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
index 3223acb..9c8559f 100644
--- a/arch/powerpc/sysdev/qe_lib/ucc_fast.c
+++ b/arch/powerpc/sysdev/qe_lib/ucc_fast.c
@@ -327,6 +327,43 @@ int ucc_fast_init(struct ucc_fast_info * uf_info, struct ucc_fast_private ** ucc
ucc_fast_free(uccf);
return -EINVAL;
}
+ } else {
+ /* TDM Rx clock routing */
+ if ((uf_info->tdm_rx_clk != NULL) &&
+ ucc_set_tdm_rxtx_clk(uf_info->ucc_num,
+ uf_info->tdm_rx_clk, COMM_DIR_RX)) {
+ printk(KERN_ERR "%s: illegal value for TDM RX clock",
+ __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
+ /* TDM Tx clock routing */
+ if ((uf_info->tdm_tx_clk != NULL) &&
+ ucc_set_tdm_rxtx_clk(uf_info->ucc_num,
+ uf_info->tdm_tx_clk, COMM_DIR_TX)) {
+ printk(KERN_ERR "%s: illegal value for TDM TX clock",
+ __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
+ /* TDM Rx sync routing */
+ if ((uf_info->tdm_rx_sync != NULL) &&
+ ucc_set_tdm_rxtx_sync(uf_info->ucc_num,
+ uf_info->tdm_rx_sync, COMM_DIR_RX)) {
+ printk(KERN_ERR "%s: illegal value for TDM RX"
+ "Frame sync", __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
+ /* TDM Tx sync routing */
+ if ((uf_info->tdm_tx_sync != NULL) &&
+ ucc_set_tdm_rxtx_sync(uf_info->ucc_num,
+ uf_info->tdm_tx_sync, COMM_DIR_TX)) {
+ printk(KERN_ERR "%s: illegal value for TDM TX"
+ "Frame sync", __FUNCTION__);
+ ucc_fast_free(uccf);
+ return -EINVAL;
+ }
}
/* Set interrupt mask register at UCC level. */
diff --git a/include/asm-powerpc/qe.h b/include/asm-powerpc/qe.h
index bcf60be..51de236 100644
--- a/include/asm-powerpc/qe.h
+++ b/include/asm-powerpc/qe.h
@@ -497,6 +497,14 @@ struct ucc_slow_pram {
#define UCC_GETH_UCCE_RXF1 0x00000002
#define UCC_GETH_UCCE_RXF0 0x00000001
+/* Transparent UCC Event Register (UCCE) */
+#define UCC_TRANS_UCCE_GRA 0x0080
+#define UCC_TRANS_UCCE_TXE 0x0010
+#define UCC_TRANS_UCCE_RXF 0x0008
+#define UCC_TRANS_UCCE_BSY 0x0004
+#define UCC_TRANS_UCCE_TXB 0x0002
+#define UCC_TRANS_UCCE_RXB 0x0001
+
/* UPSMR, when used as a UART */
#define UCC_UART_UPSMR_FLC 0x8000
#define UCC_UART_UPSMR_SL 0x4000
diff --git a/include/asm-powerpc/ucc.h b/include/asm-powerpc/ucc.h
index 46b09ba..153db97 100644
--- a/include/asm-powerpc/ucc.h
+++ b/include/asm-powerpc/ucc.h
@@ -42,6 +42,10 @@ int ucc_set_qe_mux_mii_mng(unsigned int ucc_num);
int ucc_set_qe_mux_rxtx(unsigned int ucc_num, enum qe_clock clock,
enum comm_dir mode);
+int ucc_set_tdm_rxtx_clk(int tdm_num, char *clk_src, enum comm_dir mode);
+
+int ucc_set_tdm_rxtx_sync(int tdm_num, char *clk_src, enum comm_dir mode);
+
int ucc_mux_set_grant_tsa_bkpt(unsigned int ucc_num, int set, u32 mask);
/* QE MUX clock routing for UCC
diff --git a/include/asm-powerpc/ucc_fast.h b/include/asm-powerpc/ucc_fast.h
index f529f70..d267983 100644
--- a/include/asm-powerpc/ucc_fast.h
+++ b/include/asm-powerpc/ucc_fast.h
@@ -152,6 +152,10 @@ struct ucc_fast_info {
enum ucc_fast_rx_decoding_method renc;
enum ucc_fast_transparent_tcrc tcrc;
enum ucc_fast_sync_len synl;
+ char *tdm_rx_clk;
+ char *tdm_tx_clk;
+ char *tdm_rx_sync;
+ char *tdm_tx_sync;
};
struct ucc_fast_private {
--
1.5.2.4
^ permalink raw reply related
* Re: [IPV4 4/5] fib_trie: version 0.410
From: David Miller @ 2008-01-24 4:50 UTC (permalink / raw)
To: shemminger; +Cc: netdev
In-Reply-To: <20080123224858.995658134@linux-foundation.org>
From: Stephen Hemminger <shemminger@linux-foundation.org>
Date: Wed, 23 Jan 2008 14:48:48 -0800
> Increase version to reflect recent changes.
>
> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
I'm dropping this and patch 5 for now.
^ permalink raw reply
* [PATCH UCC TDM 3/3 ] Modified Documentation to explain dts entries for TDM driver
From: Poonam_Aggrwal-b10812 @ 2008-01-24 4:54 UTC (permalink / raw)
To: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev
Cc: michael.barkowski, kim.phillips, ashish.kalra, timur, rich.cutler
From: Poonam Aggrwal <b10812@freescale.com>
Modified Documentation to explain new properties introduced for UCC TDM
driver. Also two new nodes have been added "brg" and "clocks" to configure
a BRG from device tree.
Signed-off-by: Poonam Aggrwal <b10812@freescale.com>
Signed-off-by: Ashish Kalra <ashish.kalra@freescale.com>
Signed-off-by: Kim Phillips <Kim.Phillips@freescale.com>
Signed-off-by: Michael Barkowski <michael.barkowski@freescale.com>
---
Documentation/powerpc/booting-without-of.txt | 96 +++++++++++++++++++++++++-
1 files changed, 94 insertions(+), 2 deletions(-)
diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
index e9a3cb1..94a6b4b 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -1613,8 +1613,8 @@ platforms are moved over to use the flattened-device-tree model.
Required properties:
- device_type : should be "network", "hldc", "uart", "transparent"
- "bisync" or "atm".
- - compatible : could be "ucc_geth" or "fsl_atm" and so on.
+ "bisync", "atm" or "tdm".
+ - compatible : could be "ucc_geth", "fsl_atm" or "fsl,ucc_tdm" and so on.
- model : should be "UCC".
- device-id : the ucc number(1-8), corresponding to UCCx in UM.
- reg : Offset and length of the register set for the device
@@ -1666,7 +1666,44 @@ platforms are moved over to use the flattened-device-tree model.
pio-handle = <140001>;
};
+ Required properties for tdm device_type:
+ - instead of tx-clock and rx-clock following clock properties are
+ required:
+ - fsl,tdm-tx-clk : This property selects the TX clock source for TDM
+ from a bank of clocks.
+ - fsl,tdm-rx-clk : This property selects the RX clock source for TDM
+ from a bank of clocks.
+ - fsl,tdm-tx-sync : This property selects the TX Frame sync source
+ for TDM from a bank of clocks.
+ - fsl,tdm-rx-sync : This property selects the TX Frame sync source
+ for TDM from a bank of clocks.
+
+ All the above mentioned properties are string type with possible
+ values
+ "CLK1", "CLK2", "CLK3"..."CLK24" and so on
+ "BRG1", "BRG2", "BRG3"..."BRG16" and so on
+
+ - fsl,tdm-num : TDM to be used (1,2,3 or 4 for TDMA TDMB TDMC TDMD)
+ - fsl,si-num : Serial Interface to be used.
+ Example:
+ ucc@2000 {
+ device_type = "tdm";
+ compatible = "fsl,ucc-tdm";
+ model = "UCC";
+ device-id = <1>;
+ fsl,tdm-num = <1>;
+ fsl,si-num = <1>;
+ fsl,tdm-tx-clk = "CLK1";
+ fsl,tdm-rx-clk = "CLK1";
+ fsl,tdm-tx-sync = "BRG9";
+ fsl,tdm-rx-sync = "BRG9";
+ reg = <2000 200>;
+ interrupts = <20>;
+ interrupt-parent = <&qeic>;
+ pio-handle = <&ucc1pio>;
+ };
+
v) Parallel I/O Ports
This node configures Parallel I/O ports for CPUs with QE support.
@@ -1772,6 +1809,61 @@ platforms are moved over to use the flattened-device-tree model.
};
};
+ viii) Clocks (clocks)
+ This node specifies the frequency values for all the external clocks
+ viz CLK1 to CLK24 in Hz.
+
+ Required Properties:
+ - compatible : should be "fsl,cpm-clocks".
+ - #clock-cells : It specifies the number of cells occupied by clock-frequency
+ property. Currently #clock-cells = 1 is only supported and implemented.
+ This property is kept for future in case we need frequencies higher than
+ 4 GHz.
+ - clock-frequency : It is a list of u32 values to represent the frequency
+ of each external clock(CLK1 to CLK24) in Hz.Each entry occupies
+ number of cells specified by #clock-cells property(1 for now).
+
+ Example:
+
+ clocks {
+ compatible = "fsl,cpm-clocks";
+ #clock-cells = <1>;
+ /* clock freqs in Hz(for CLK1~CLK24).
+ * CLK11 is 1024KHz,
+ * all other clocks unused
+ */
+ clock-frequency = <0 0 0 0 0 0
+ 0 0 0 0 0 d#1024000 0
+ 0 0 0 0 0 0
+ 0 0 0 0 0 0>;
+ };
+
+ ix) Baud Rate Generator (BRG)
+
+ Required properties:
+ - compatible : shpuld be "fsl,cpm-brg"
+ - fsl,brg-sources : define the input clock for all 16 BRGs. The input
+ clock source could be 1 to 24 for CLK1 to CLK24. Zero means that the
+ particular BRG will be driven by QE clock(BRGCLK).
+ - reg : This property defines the address and size of the memory-mapped
+ registers of the BRG.
+
+ Example:
+
+ brg@640 {
+ compatible = "fsl,qe-brg";
+ /* input clock sources for all the 16 BRGs.
+ * 1-24 for CLK1 to CLK24.
+ * BRG9 uses CLK11 others use
+ * the QE clock.
+ */
+ fsl,brg-sources = <0 0 0 0 0 0 0 0
+ b 0 0 0 0 0 0 0>;
+ reg = <640 7f>;
+ };
+
+ In the above entry, for BRG9 the input clock is 0xb(decimal 11) ie QE_CLK11.
+
j) CFI or JEDEC memory-mapped NOR flash
Flash chips (Memory Technology Devices) are often used for solid state
--
1.5.2.4
^ permalink raw reply related
* Re: [PATCH] ppp: sparse warning fixes
From: David Miller @ 2008-01-24 4:54 UTC (permalink / raw)
To: shemminger; +Cc: paulus, linux-ppp, netdev
In-Reply-To: <20080123154019.7cd9c471@deepthought>
From: Stephen Hemminger <shemminger@vyatta.com>
Date: Wed, 23 Jan 2008 15:40:19 -0800
> Fix a bunch of warnings in PPP and related drivers. Mostly because
> sparse doesn't like it when the the function is only marked private
> in the forward declaration.
>
> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
Applied, thanks Stephen.
^ permalink raw reply
* rebasing net-2.6.25...
From: David Miller @ 2008-01-24 5:00 UTC (permalink / raw)
To: netdev
I am going to work throughout the evening to rebase the net-2.6.25 GIT
tree.
I'm heading off to LCA08 tomorrow afternoon so I wanted to get this
out of the way before leaving.
^ permalink raw reply
* Re: [PATCH] [IPV4] route: fix locking in rt_run_flush()
From: Joonwoo Park @ 2008-01-24 5:06 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David Miller, netdev
In-Reply-To: <4797A729.4030006@cosmosbay.com>
2008/1/24, Eric Dumazet <dada1@cosmosbay.com>:
>
> Unfortunatly, your patch doesnt work on CONFIG_SMP=n (softirq will be disabled
> for the whole scan of table)
>
> Also, some machines around there have 2^22 slots in hash table, and NR_CPUS=4,
> so softirqs will be disabled for a too long time.
>
> Please try net-2.6.25 and submit patches on top of it if necessary, since
> rt_run_flush() has pending changes, not in net-2.6
>
> Note : The 'soft lockup' can be avoided by other means.
>
>
Eric,
Thank you for your help.
I checked net-2.6.25 in this morining :(
And it brought me a conclusion that the fashion of it is the best solution for
the all circumstances.
I believe that
'cond_resched()'
and
'if (!rth) continue' without disable softirq
of net-2.6.25 is correct answer.
Thanks again!
Joonwoo
^ permalink raw reply
* [PATCH 6/6 v2] PS3: gelic: Add support for dual network interface
From: Masakazu Mokuno @ 2008-01-24 5:41 UTC (permalink / raw)
To: netdev; +Cc: linuxppc-dev
In-Reply-To: <20071213211332.BF8C.MOKUNO@sm.sony.co.jp>
Add support for dual network (net_device) interface so that ethernet
and wireless can own separate ethX interfaces.
V2
- Fix the bug that bringing down and up the interface keeps rx
disabled.
- Make 'gelic_net_poll_controller()' extern , as David Woodhouse
pointed out at the previous submission.
- Fix weird usage of member names for the rx descriptor chain
V1
- Export functions which are convenient for both interfaces
- Move irq allocation/release code to driver probe/remove handlers
because interfaces share interrupts.
- Allocate skbs by using dev_alloc_skb() instead of netdev_alloc_skb()
as the interfaces share the hardware rx queue.
- Add gelic_port struct in order to abstract dual interface handling
- Change handlers for hardware queues so that they can handle dual
{source,destination} interfaces.
- Use new NAPI functions
This is a prerequisite for the new PS3 wireless support.
Signed-off-by: Masakazu Mokuno <mokuno@sm.sony.co.jp>
---
drivers/net/ps3_gelic_net.c | 765 +++++++++++++++++++++++++++-----------------
drivers/net/ps3_gelic_net.h | 108 +++++-
2 files changed, 564 insertions(+), 309 deletions(-)
--- a/drivers/net/ps3_gelic_net.c
+++ b/drivers/net/ps3_gelic_net.c
@@ -48,27 +48,22 @@
#include "ps3_gelic_net.h"
#define DRV_NAME "Gelic Network Driver"
-#define DRV_VERSION "1.0"
+#define DRV_VERSION "1.1"
MODULE_AUTHOR("SCE Inc.");
MODULE_DESCRIPTION("Gelic Network driver");
MODULE_LICENSE("GPL");
-static inline struct device *ctodev(struct gelic_card *card)
-{
- return &card->dev->core;
-}
-static inline u64 bus_id(struct gelic_card *card)
-{
- return card->dev->bus_id;
-}
-static inline u64 dev_id(struct gelic_card *card)
-{
- return card->dev->dev_id;
-}
+
+static inline void gelic_card_enable_rxdmac(struct gelic_card *card);
+static inline void gelic_card_disable_rxdmac(struct gelic_card *card);
+static inline void gelic_card_disable_txdmac(struct gelic_card *card);
+static inline void gelic_card_reset_chain(struct gelic_card *card,
+ struct gelic_descr_chain *chain,
+ struct gelic_descr *start_descr);
/* set irq_mask */
-static int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask)
+int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask)
{
int status;
@@ -76,20 +71,23 @@ static int gelic_card_set_irq_mask(struc
mask, 0);
if (status)
dev_info(ctodev(card),
- "lv1_net_set_interrupt_mask failed %d\n", status);
+ "%s failed %d\n", __func__, status);
return status;
}
+
static inline void gelic_card_rx_irq_on(struct gelic_card *card)
{
- gelic_card_set_irq_mask(card, card->ghiintmask | GELIC_CARD_RXINT);
+ card->irq_mask |= GELIC_CARD_RXINT;
+ gelic_card_set_irq_mask(card, card->irq_mask);
}
static inline void gelic_card_rx_irq_off(struct gelic_card *card)
{
- gelic_card_set_irq_mask(card, card->ghiintmask & ~GELIC_CARD_RXINT);
+ card->irq_mask &= ~GELIC_CARD_RXINT;
+ gelic_card_set_irq_mask(card, card->irq_mask);
}
-static void
-gelic_card_get_ether_port_status(struct gelic_card *card, int inform)
+static void gelic_card_get_ether_port_status(struct gelic_card *card,
+ int inform)
{
u64 v2;
struct net_device *ether_netdev;
@@ -100,7 +98,7 @@ gelic_card_get_ether_port_status(struct
&card->ether_port_status, &v2);
if (inform) {
- ether_netdev = card->netdev;
+ ether_netdev = card->netdev[GELIC_PORT_ETHERNET];
if (card->ether_port_status & GELIC_LV1_ETHER_LINK_UP)
netif_carrier_on(ether_netdev);
else
@@ -108,6 +106,48 @@ gelic_card_get_ether_port_status(struct
}
}
+void gelic_card_up(struct gelic_card *card)
+{
+ pr_debug("%s: called\n", __func__);
+ down(&card->updown_lock);
+ if (atomic_inc_return(&card->users) == 1) {
+ pr_debug("%s: real do\n", __func__);
+ /* enable irq */
+ gelic_card_set_irq_mask(card, card->irq_mask);
+ /* start rx */
+ gelic_card_enable_rxdmac(card);
+
+ napi_enable(&card->napi);
+ }
+ up(&card->updown_lock);
+ pr_debug("%s: done\n", __func__);
+}
+
+void gelic_card_down(struct gelic_card *card)
+{
+ u64 mask;
+ pr_debug("%s: called\n", __func__);
+ down(&card->updown_lock);
+ if (atomic_dec_if_positive(&card->users) == 0) {
+ pr_debug("%s: real do\n", __func__);
+ napi_disable(&card->napi);
+ /*
+ * Disable irq. Wireless interrupts will
+ * be disabled later if any
+ */
+ mask = card->irq_mask & (GELIC_CARD_WLAN_EVENT_RECEIVED |
+ GELIC_CARD_WLAN_COMMAND_COMPLETED);
+ gelic_card_set_irq_mask(card, mask);
+ /* stop rx */
+ gelic_card_disable_rxdmac(card);
+ gelic_card_reset_chain(card, &card->rx_chain,
+ card->descr + GELIC_NET_TX_DESCRIPTORS);
+ /* stop tx */
+ gelic_card_disable_txdmac(card);
+ }
+ up(&card->updown_lock);
+ pr_debug("%s: done\n", __func__);
+}
/**
* gelic_descr_get_status -- returns the status of a descriptor
@@ -133,8 +173,8 @@ static void gelic_descr_set_status(struc
enum gelic_descr_dma_status status)
{
descr->dmac_cmd_status = cpu_to_be32(status |
- (be32_to_cpu(descr->dmac_cmd_status) &
- ~GELIC_DESCR_DMA_STAT_MASK));
+ (be32_to_cpu(descr->dmac_cmd_status) &
+ ~GELIC_DESCR_DMA_STAT_MASK));
/*
* dma_cmd_status field is used to indicate whether the descriptor
* is valid or not.
@@ -225,6 +265,31 @@ iommu_error:
}
/**
+ * gelic_card_reset_chain - reset status of a descriptor chain
+ * @card: card structure
+ * @chain: address of chain
+ * @start_descr: address of descriptor array
+ *
+ * Reset the status of dma descriptors to ready state
+ * and re-initialize the hardware chain for later use
+ */
+static void gelic_card_reset_chain(struct gelic_card *card,
+ struct gelic_descr_chain *chain,
+ struct gelic_descr *start_descr)
+{
+ struct gelic_descr *descr;
+
+ for (descr = start_descr; start_descr != descr->next; descr++) {
+ gelic_descr_set_status(descr, GELIC_DESCR_DMA_CARDOWNED);
+ descr->next_descr_addr = cpu_to_be32(descr->next->bus_addr);
+ }
+
+ chain->head = start_descr;
+ chain->tail = (descr - 1);
+
+ (descr - 1)->next_descr_addr = 0;
+}
+/**
* gelic_descr_prepare_rx - reinitializes a rx descriptor
* @card: card structure
* @descr: descriptor to re-init
@@ -235,21 +300,19 @@ iommu_error:
* Activate the descriptor state-wise
*/
static int gelic_descr_prepare_rx(struct gelic_card *card,
- struct gelic_descr *descr)
+ struct gelic_descr *descr)
{
int offset;
unsigned int bufsize;
if (gelic_descr_get_status(descr) != GELIC_DESCR_DMA_NOT_IN_USE)
dev_info(ctodev(card), "%s: ERROR status \n", __func__);
-
/* we need to round up the buffer size to a multiple of 128 */
bufsize = ALIGN(GELIC_NET_MAX_MTU, GELIC_NET_RXBUF_ALIGN);
/* and we need to have it 128 byte aligned, therefore we allocate a
* bit more */
- descr->skb = netdev_alloc_skb(card->netdev,
- bufsize + GELIC_NET_RXBUF_ALIGN - 1);
+ descr->skb = dev_alloc_skb(bufsize + GELIC_NET_RXBUF_ALIGN - 1);
if (!descr->skb) {
descr->buf_addr = 0; /* tell DMAC don't touch memory */
dev_info(ctodev(card),
@@ -349,7 +412,7 @@ static int gelic_card_alloc_rx_skbs(stru
int ret;
chain = &card->rx_chain;
ret = gelic_card_fill_rx_chain(card);
- chain->head = card->rx_top->prev; /* point to the last */
+ chain->tail = card->rx_top->prev; /* point to the last */
return ret;
}
@@ -361,16 +424,14 @@ static int gelic_card_alloc_rx_skbs(stru
* releases a used tx descriptor (unmapping, freeing of skb)
*/
static void gelic_descr_release_tx(struct gelic_card *card,
- struct gelic_descr *descr)
+ struct gelic_descr *descr)
{
struct sk_buff *skb = descr->skb;
-#ifdef DEBUG
- BUG_ON(!(be32_to_cpu(descr->data_status) &
- (1 << GELIC_DESCR_TX_DMA_FRAME_TAIL)));
-#endif
- dma_unmap_single(ctodev(card),
- be32_to_cpu(descr->buf_addr), skb->len, DMA_TO_DEVICE);
+ BUG_ON(!(be32_to_cpu(descr->data_status) & GELIC_DESCR_TX_TAIL));
+
+ dma_unmap_single(ctodev(card), be32_to_cpu(descr->buf_addr), skb->len,
+ DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
descr->buf_addr = 0;
@@ -386,6 +447,20 @@ static void gelic_descr_release_tx(struc
gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE);
}
+static void gelic_card_stop_queues(struct gelic_card *card)
+{
+ netif_stop_queue(card->netdev[GELIC_PORT_ETHERNET]);
+
+ if (card->netdev[GELIC_PORT_WIRELESS])
+ netif_stop_queue(card->netdev[GELIC_PORT_WIRELESS]);
+}
+static void gelic_card_wake_queues(struct gelic_card *card)
+{
+ netif_wake_queue(card->netdev[GELIC_PORT_ETHERNET]);
+
+ if (card->netdev[GELIC_PORT_WIRELESS])
+ netif_wake_queue(card->netdev[GELIC_PORT_WIRELESS]);
+}
/**
* gelic_card_release_tx_chain - processes sent tx descriptors
* @card: adapter structure
@@ -397,12 +472,14 @@ static void gelic_card_release_tx_chain(
{
struct gelic_descr_chain *tx_chain;
enum gelic_descr_dma_status status;
+ struct net_device *netdev;
int release = 0;
for (tx_chain = &card->tx_chain;
tx_chain->head != tx_chain->tail && tx_chain->tail;
tx_chain->tail = tx_chain->tail->next) {
status = gelic_descr_get_status(tx_chain->tail);
+ netdev = tx_chain->tail->skb->dev;
switch (status) {
case GELIC_DESCR_DMA_RESPONSE_ERROR:
case GELIC_DESCR_DMA_PROTECTION_ERROR:
@@ -412,13 +489,13 @@ static void gelic_card_release_tx_chain(
"%s: forcing end of tx descriptor " \
"with status %x\n",
__func__, status);
- card->netdev->stats.tx_dropped++;
+ netdev->stats.tx_dropped++;
break;
case GELIC_DESCR_DMA_COMPLETE:
if (tx_chain->tail->skb) {
- card->netdev->stats.tx_packets++;
- card->netdev->stats.tx_bytes +=
+ netdev->stats.tx_packets++;
+ netdev->stats.tx_bytes +=
tx_chain->tail->skb->len;
}
break;
@@ -435,7 +512,7 @@ static void gelic_card_release_tx_chain(
}
out:
if (!stop && release)
- netif_wake_queue(card->netdev);
+ gelic_card_wake_queues(card);
}
/**
@@ -446,9 +523,9 @@ out:
* netdev interface. It also sets up multicast, allmulti and promisc
* flags appropriately
*/
-static void gelic_net_set_multi(struct net_device *netdev)
+void gelic_net_set_multi(struct net_device *netdev)
{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
struct dev_mc_list *mc;
unsigned int i;
uint8_t *p;
@@ -470,8 +547,8 @@ static void gelic_net_set_multi(struct n
"lv1_net_add_multicast_address failed, %d\n",
status);
- if (netdev->flags & IFF_ALLMULTI
- || netdev->mc_count > GELIC_NET_MC_COUNT_MAX) { /* list max */
+ if ((netdev->flags & IFF_ALLMULTI) ||
+ (netdev->mc_count > GELIC_NET_MC_COUNT_MAX)) {
status = lv1_net_add_multicast_address(bus_id(card),
dev_id(card),
0, 1);
@@ -482,7 +559,7 @@ static void gelic_net_set_multi(struct n
return;
}
- /* set multicast address */
+ /* set multicast addresses */
for (mc = netdev->mc_list; mc; mc = mc->next) {
addr = 0;
p = mc->dmi_addr;
@@ -511,8 +588,19 @@ static inline void gelic_card_enable_rxd
{
int status;
+#ifdef DEBUG
+ if (gelic_descr_get_status(card->rx_chain.head) !=
+ GELIC_DESCR_DMA_CARDOWNED) {
+ printk(KERN_ERR "%s: status=%x\n", __func__,
+ be32_to_cpu(card->rx_chain.head->dmac_cmd_status));
+ printk(KERN_ERR "%s: nextphy=%x\n", __func__,
+ be32_to_cpu(card->rx_chain.head->next_descr_addr));
+ printk(KERN_ERR "%s: head=%p\n", __func__,
+ card->rx_chain.head);
+ }
+#endif
status = lv1_net_start_rx_dma(bus_id(card), dev_id(card),
- card->rx_chain.tail->bus_addr, 0);
+ card->rx_chain.head->bus_addr, 0);
if (status)
dev_info(ctodev(card),
"lv1_net_start_rx_dma failed, status=%d\n", status);
@@ -560,33 +648,19 @@ static inline void gelic_card_disable_tx
*
* always returns 0
*/
-static int gelic_net_stop(struct net_device *netdev)
+int gelic_net_stop(struct net_device *netdev)
{
- struct gelic_card *card = netdev_priv(netdev);
-
- napi_disable(&card->napi);
- netif_stop_queue(netdev);
-
- /* turn off DMA, force end */
- gelic_card_disable_rxdmac(card);
- gelic_card_disable_txdmac(card);
-
- gelic_card_set_irq_mask(card, 0);
+ struct gelic_card *card;
- /* disconnect event port */
- free_irq(card->netdev->irq, card->netdev);
- ps3_sb_event_receive_port_destroy(card->dev, card->netdev->irq);
- card->netdev->irq = NO_IRQ;
+ pr_debug("%s: start\n", __func__);
+ netif_stop_queue(netdev);
netif_carrier_off(netdev);
- /* release chains */
- gelic_card_release_tx_chain(card, 1);
- gelic_card_release_rx_chain(card);
-
- gelic_card_free_chain(card, card->tx_top);
- gelic_card_free_chain(card, card->rx_top);
+ card = netdev_card(netdev);
+ gelic_card_down(card);
+ pr_debug("%s: done\n", __func__);
return 0;
}
@@ -612,7 +686,7 @@ gelic_card_get_next_tx_descr(struct geli
}
/**
- * gelic_descr_set_tx_cmdstat - sets the tx descriptor command field
+ * gelic_net_set_txdescr_cmdstat - sets the tx descriptor command field
* @descr: descriptor structure to fill out
* @skb: packet to consider
*
@@ -677,7 +751,7 @@ static inline struct sk_buff *gelic_put_
}
/**
- * gelic_descr_prepare_tx - get dma address of skb_data
+ * gelic_descr_prepare_tx - setup a descriptor for sending packets
* @card: card structure
* @descr: descriptor structure
* @skb: packet to use
@@ -691,10 +765,13 @@ static int gelic_descr_prepare_tx(struct
{
dma_addr_t buf;
- if (card->vlan_index != -1) {
+ if (card->vlan_required) {
struct sk_buff *skb_tmp;
+ enum gelic_port_type type;
+
+ type = netdev_port(skb->dev)->type;
skb_tmp = gelic_put_vlan_tag(skb,
- card->vlan_id[card->vlan_index]);
+ card->vlan[type].tx);
if (!skb_tmp)
return -ENOMEM;
skb = skb_tmp;
@@ -753,14 +830,14 @@ static int gelic_card_kick_txdma(struct
*
* returns 0 on success, <0 on failure
*/
-static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
+int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev)
{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
struct gelic_descr *descr;
int result;
unsigned long flags;
- spin_lock_irqsave(&card->tx_dma_lock, flags);
+ spin_lock_irqsave(&card->tx_lock, flags);
gelic_card_release_tx_chain(card, 0);
@@ -769,8 +846,8 @@ static int gelic_net_xmit(struct sk_buff
/*
* no more descriptors free
*/
- netif_stop_queue(netdev);
- spin_unlock_irqrestore(&card->tx_dma_lock, flags);
+ gelic_card_stop_queues(card);
+ spin_unlock_irqrestore(&card->tx_lock, flags);
return NETDEV_TX_BUSY;
}
@@ -780,9 +857,9 @@ static int gelic_net_xmit(struct sk_buff
* DMA map failed. As chanses are that failure
* would continue, just release skb and return
*/
- card->netdev->stats.tx_dropped++;
+ netdev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
- spin_unlock_irqrestore(&card->tx_dma_lock, flags);
+ spin_unlock_irqrestore(&card->tx_lock, flags);
return NETDEV_TX_OK;
}
/*
@@ -800,7 +877,7 @@ static int gelic_net_xmit(struct sk_buff
* kick failed.
* release descriptors which were just prepared
*/
- card->netdev->stats.tx_dropped++;
+ netdev->stats.tx_dropped++;
gelic_descr_release_tx(card, descr);
gelic_descr_release_tx(card, descr->next);
card->tx_chain.tail = descr->next->next;
@@ -810,7 +887,7 @@ static int gelic_net_xmit(struct sk_buff
netdev->trans_start = jiffies;
}
- spin_unlock_irqrestore(&card->tx_dma_lock, flags);
+ spin_unlock_irqrestore(&card->tx_lock, flags);
return NETDEV_TX_OK;
}
@@ -818,27 +895,27 @@ static int gelic_net_xmit(struct sk_buff
* gelic_net_pass_skb_up - takes an skb from a descriptor and passes it on
* @descr: descriptor to process
* @card: card structure
+ * @netdev: net_device structure to be passed packet
*
* iommu-unmaps the skb, fills out skb structure and passes the data to the
* stack. The descriptor state is not changed.
*/
static void gelic_net_pass_skb_up(struct gelic_descr *descr,
- struct gelic_card *card)
+ struct gelic_card *card,
+ struct net_device *netdev)
+
{
- struct sk_buff *skb;
- struct net_device *netdev;
+ struct sk_buff *skb = descr->skb;
u32 data_status, data_error;
data_status = be32_to_cpu(descr->data_status);
data_error = be32_to_cpu(descr->data_error);
- netdev = card->netdev;
/* unmap skb buffer */
- skb = descr->skb;
- dma_unmap_single(ctodev(card),
- be32_to_cpu(descr->buf_addr), GELIC_NET_MAX_MTU,
+ dma_unmap_single(ctodev(card), be32_to_cpu(descr->buf_addr),
+ GELIC_NET_MAX_MTU,
DMA_FROM_DEVICE);
- skb_put(skb, descr->valid_size ?
+ skb_put(skb, be32_to_cpu(descr->valid_size)?
be32_to_cpu(descr->valid_size) :
be32_to_cpu(descr->result_size));
if (!descr->valid_size)
@@ -866,8 +943,8 @@ static void gelic_net_pass_skb_up(struct
skb->ip_summed = CHECKSUM_NONE;
/* update netdevice statistics */
- card->netdev->stats.rx_packets++;
- card->netdev->stats.rx_bytes += skb->len;
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += skb->len;
/* pass skb up to stack */
netif_receive_skb(skb);
@@ -886,7 +963,8 @@ static int gelic_card_decode_one_descr(s
{
enum gelic_descr_dma_status status;
struct gelic_descr_chain *chain = &card->rx_chain;
- struct gelic_descr *descr = chain->tail;
+ struct gelic_descr *descr = chain->head;
+ struct net_device *netdev = NULL;
int dmac_chain_ended;
status = gelic_descr_get_status(descr);
@@ -903,12 +981,30 @@ static int gelic_card_decode_one_descr(s
return 0;
}
+ /* netdevice select */
+ if (card->vlan_required) {
+ unsigned int i;
+ u16 vid;
+ vid = *(u16 *)(descr->skb->data) & VLAN_VID_MASK;
+ for (i = 0; i < GELIC_PORT_MAX; i++) {
+ if (card->vlan[i].rx == vid) {
+ netdev = card->netdev[i];
+ break;
+ }
+ };
+ if (GELIC_PORT_MAX <= i) {
+ pr_info("%s: unknown packet vid=%x\n", __func__, vid);
+ goto refill;
+ }
+ } else
+ netdev = card->netdev[GELIC_PORT_ETHERNET];
+
if ((status == GELIC_DESCR_DMA_RESPONSE_ERROR) ||
(status == GELIC_DESCR_DMA_PROTECTION_ERROR) ||
(status == GELIC_DESCR_DMA_FORCE_END)) {
dev_info(ctodev(card), "dropping RX descriptor with state %x\n",
status);
- card->netdev->stats.rx_dropped++;
+ netdev->stats.rx_dropped++;
goto refill;
}
@@ -936,7 +1032,7 @@ static int gelic_card_decode_one_descr(s
}
/* ok, we've got a packet in descr */
- gelic_net_pass_skb_up(descr, card);
+ gelic_net_pass_skb_up(descr, card, netdev);
refill:
/*
* So that always DMAC can see the end
@@ -954,8 +1050,8 @@ refill:
*/
gelic_descr_prepare_rx(card, descr);
- chain->head = descr;
- chain->tail = descr->next;
+ chain->tail = descr;
+ chain->head = descr->next;
/*
* Set this descriptor the end of the chain.
@@ -976,17 +1072,15 @@ refill:
/**
* gelic_net_poll - NAPI poll function called by the stack to return packets
- * @netdev: interface device structure
+ * @napi: napi structure
* @budget: number of packets we can pass to the stack at most
*
- * returns 0 if no more packets available to the driver/stack. Returns 1,
- * if the quota is exceeded, but the driver has still packets.
+ * returns the number of the processed packets
*
*/
static int gelic_net_poll(struct napi_struct *napi, int budget)
{
struct gelic_card *card = container_of(napi, struct gelic_card, napi);
- struct net_device *netdev = card->netdev;
int packets_done = 0;
while (packets_done < budget) {
@@ -997,7 +1091,7 @@ static int gelic_net_poll(struct napi_st
}
if (packets_done < budget) {
- netif_rx_complete(netdev, napi);
+ napi_complete(napi);
gelic_card_rx_irq_on(card);
}
return packets_done;
@@ -1009,7 +1103,7 @@ static int gelic_net_poll(struct napi_st
*
* returns 0 on success, <0 on failure
*/
-static int gelic_net_change_mtu(struct net_device *netdev, int new_mtu)
+int gelic_net_change_mtu(struct net_device *netdev, int new_mtu)
{
/* no need to re-alloc skbs or so -- the max mtu is about 2.3k
* and mtu is outbound only anyway */
@@ -1027,8 +1121,7 @@ static int gelic_net_change_mtu(struct n
static irqreturn_t gelic_card_interrupt(int irq, void *ptr)
{
unsigned long flags;
- struct net_device *netdev = ptr;
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = ptr;
u64 status;
status = card->irq_status;
@@ -1036,6 +1129,8 @@ static irqreturn_t gelic_card_interrupt(
if (!status)
return IRQ_NONE;
+ status &= card->irq_mask;
+
if (card->rx_dma_restart_required) {
card->rx_dma_restart_required = 0;
gelic_card_enable_rxdmac(card);
@@ -1043,21 +1138,22 @@ static irqreturn_t gelic_card_interrupt(
if (status & GELIC_CARD_RXINT) {
gelic_card_rx_irq_off(card);
- netif_rx_schedule(netdev, &card->napi);
+ napi_schedule(&card->napi);
}
if (status & GELIC_CARD_TXINT) {
- spin_lock_irqsave(&card->tx_dma_lock, flags);
+ spin_lock_irqsave(&card->tx_lock, flags);
card->tx_dma_progress = 0;
gelic_card_release_tx_chain(card, 0);
/* kick outstanding tx descriptor if any */
gelic_card_kick_txdma(card, card->tx_chain.tail);
- spin_unlock_irqrestore(&card->tx_dma_lock, flags);
+ spin_unlock_irqrestore(&card->tx_lock, flags);
}
/* ether port status changed */
if (status & GELIC_CARD_PORT_STATUS_CHANGED)
gelic_card_get_ether_port_status(card, 1);
+
return IRQ_HANDLED;
}
@@ -1068,55 +1164,17 @@ static irqreturn_t gelic_card_interrupt(
*
* see Documentation/networking/netconsole.txt
*/
-static void gelic_net_poll_controller(struct net_device *netdev)
+void gelic_net_poll_controller(struct net_device *netdev)
{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
gelic_card_set_irq_mask(card, 0);
gelic_card_interrupt(netdev->irq, netdev);
- gelic_card_set_irq_mask(card, card->ghiintmask);
+ gelic_card_set_irq_mask(card, card->irq_mask);
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
/**
- * gelic_card_open - open device and map dma region
- * @card: card structure
- */
-static int gelic_card_open(struct gelic_card *card)
-{
- int result;
-
- result = ps3_sb_event_receive_port_setup(card->dev, PS3_BINDING_CPU_ANY,
- &card->netdev->irq);
-
- if (result) {
- dev_info(ctodev(card),
- "%s:%d: recieve_port_setup failed (%d)\n",
- __func__, __LINE__, result);
- result = -EPERM;
- goto fail_alloc_irq;
- }
-
- result = request_irq(card->netdev->irq, gelic_card_interrupt,
- IRQF_DISABLED, card->netdev->name, card->netdev);
-
- if (result) {
- dev_info(ctodev(card), "%s:%d: request_irq failed (%d)\n",
- __func__, __LINE__, result);
- goto fail_request_irq;
- }
-
- return 0;
-
-fail_request_irq:
- ps3_sb_event_receive_port_destroy(card->dev, card->netdev->irq);
- card->netdev->irq = NO_IRQ;
-fail_alloc_irq:
- return result;
-}
-
-
-/**
* gelic_net_open - called upon ifonfig up
* @netdev: interface device structure
*
@@ -1125,56 +1183,23 @@ fail_alloc_irq:
* gelic_net_open allocates all the descriptors and memory needed for
* operation, sets up multicast list and enables interrupts
*/
-static int gelic_net_open(struct net_device *netdev)
+int gelic_net_open(struct net_device *netdev)
{
- struct gelic_card *card = netdev_priv(netdev);
-
- dev_dbg(ctodev(card), " -> %s:%d\n", __func__, __LINE__);
+ struct gelic_card *card = netdev_card(netdev);
- gelic_card_open(card);
+ dev_dbg(ctodev(card), " -> %s %p\n", __func__, netdev);
- if (gelic_card_init_chain(card, &card->tx_chain,
- card->descr, GELIC_NET_TX_DESCRIPTORS))
- goto alloc_tx_failed;
- if (gelic_card_init_chain(card, &card->rx_chain,
- card->descr + GELIC_NET_TX_DESCRIPTORS,
- GELIC_NET_RX_DESCRIPTORS))
- goto alloc_rx_failed;
-
- /* head of chain */
- card->tx_top = card->tx_chain.head;
- card->rx_top = card->rx_chain.head;
- dev_dbg(ctodev(card), "descr rx %p, tx %p, size %#lx, num %#x\n",
- card->rx_top, card->tx_top, sizeof(struct gelic_descr),
- GELIC_NET_RX_DESCRIPTORS);
- /* allocate rx skbs */
- if (gelic_card_alloc_rx_skbs(card))
- goto alloc_skbs_failed;
-
- napi_enable(&card->napi);
-
- card->tx_dma_progress = 0;
- card->ghiintmask = GELIC_CARD_RXINT | GELIC_CARD_TXINT |
- GELIC_CARD_PORT_STATUS_CHANGED;
-
- gelic_card_set_irq_mask(card, card->ghiintmask);
- gelic_card_enable_rxdmac(card);
+ gelic_card_up(card);
netif_start_queue(netdev);
gelic_card_get_ether_port_status(card, 1);
+ dev_dbg(ctodev(card), " <- %s\n", __func__);
return 0;
-
-alloc_skbs_failed:
- gelic_card_free_chain(card, card->rx_top);
-alloc_rx_failed:
- gelic_card_free_chain(card, card->tx_top);
-alloc_tx_failed:
- return -ENOMEM;
}
-static void gelic_net_get_drvinfo(struct net_device *netdev,
- struct ethtool_drvinfo *info)
+void gelic_net_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *info)
{
strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1);
strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1);
@@ -1183,7 +1208,7 @@ static void gelic_net_get_drvinfo(struct
static int gelic_ether_get_settings(struct net_device *netdev,
struct ethtool_cmd *cmd)
{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
gelic_card_get_ether_port_status(card, 0);
@@ -1219,35 +1244,25 @@ static int gelic_ether_get_settings(stru
return 0;
}
-static int gelic_net_nway_reset(struct net_device *netdev)
+u32 gelic_net_get_rx_csum(struct net_device *netdev)
{
- if (netif_running(netdev)) {
- gelic_net_stop(netdev);
- gelic_net_open(netdev);
- }
- return 0;
-}
-
-static u32 gelic_net_get_rx_csum(struct net_device *netdev)
-{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
return card->rx_csum;
}
-static int gelic_net_set_rx_csum(struct net_device *netdev, u32 data)
+int gelic_net_set_rx_csum(struct net_device *netdev, u32 data)
{
- struct gelic_card *card = netdev_priv(netdev);
+ struct gelic_card *card = netdev_card(netdev);
card->rx_csum = data;
return 0;
}
-static struct ethtool_ops gelic_net_ethtool_ops = {
+static struct ethtool_ops gelic_ether_ethtool_ops = {
.get_drvinfo = gelic_net_get_drvinfo,
.get_settings = gelic_ether_get_settings,
.get_link = ethtool_op_get_link,
- .nway_reset = gelic_net_nway_reset,
.get_tx_csum = ethtool_op_get_tx_csum,
.set_tx_csum = ethtool_op_set_tx_csum,
.get_rx_csum = gelic_net_get_rx_csum,
@@ -1265,7 +1280,7 @@ static void gelic_net_tx_timeout_task(st
{
struct gelic_card *card =
container_of(work, struct gelic_card, tx_timeout_task);
- struct net_device *netdev = card->netdev;
+ struct net_device *netdev = card->netdev[GELIC_PORT_ETHERNET];
dev_info(ctodev(card), "%s:Timed out. Restarting... \n", __func__);
@@ -1288,11 +1303,11 @@ out:
*
* called, if tx hangs. Schedules a task that resets the interface
*/
-static void gelic_net_tx_timeout(struct net_device *netdev)
+void gelic_net_tx_timeout(struct net_device *netdev)
{
struct gelic_card *card;
- card = netdev_priv(netdev);
+ card = netdev_card(netdev);
atomic_inc(&card->tx_timeout_task_counter);
if (netdev->flags & IFF_UP)
schedule_work(&card->tx_timeout_task);
@@ -1306,7 +1321,8 @@ static void gelic_net_tx_timeout(struct
*
* fills out function pointers in the net_device structure
*/
-static void gelic_ether_setup_netdev_ops(struct net_device *netdev)
+static void gelic_ether_setup_netdev_ops(struct net_device *netdev,
+ struct napi_struct *napi)
{
netdev->open = &gelic_net_open;
netdev->stop = &gelic_net_stop;
@@ -1316,86 +1332,63 @@ static void gelic_ether_setup_netdev_ops
/* tx watchdog */
netdev->tx_timeout = &gelic_net_tx_timeout;
netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT;
- netdev->ethtool_ops = &gelic_net_ethtool_ops;
+ /* NAPI */
+ netif_napi_add(netdev, napi,
+ gelic_net_poll, GELIC_NET_NAPI_WEIGHT);
+ netdev->ethtool_ops = &gelic_ether_ethtool_ops;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ netdev->poll_controller = gelic_net_poll_controller;
+#endif
}
/**
- * gelic_net_setup_netdev - initialization of net_device
+ * gelic_ether_setup_netdev - initialization of net_device
+ * @netdev: net_device structure
* @card: card structure
*
* Returns 0 on success or <0 on failure
*
- * gelic_net_setup_netdev initializes the net_device structure
+ * gelic_ether_setup_netdev initializes the net_device structure
+ * and register it.
**/
-static int gelic_net_setup_netdev(struct gelic_card *card)
+int gelic_net_setup_netdev(struct net_device *netdev, struct gelic_card *card)
{
- struct net_device *netdev = card->netdev;
- struct sockaddr addr;
- unsigned int i;
int status;
u64 v1, v2;
DECLARE_MAC_BUF(mac);
- SET_NETDEV_DEV(netdev, &card->dev->core);
- spin_lock_init(&card->tx_dma_lock);
-
- card->rx_csum = GELIC_NET_RX_CSUM_DEFAULT;
-
- gelic_ether_setup_netdev_ops(netdev);
-
- netif_napi_add(netdev, &card->napi,
- gelic_net_poll, GELIC_NET_NAPI_WEIGHT);
-
netdev->features = NETIF_F_IP_CSUM;
status = lv1_net_control(bus_id(card), dev_id(card),
GELIC_LV1_GET_MAC_ADDRESS,
0, 0, 0, &v1, &v2);
+ v1 <<= 16;
if (status || !is_valid_ether_addr((u8 *)&v1)) {
dev_info(ctodev(card),
"%s:lv1_net_control GET_MAC_ADDR failed %d\n",
__func__, status);
return -EINVAL;
}
- v1 <<= 16;
- memcpy(addr.sa_data, &v1, ETH_ALEN);
- memcpy(netdev->dev_addr, addr.sa_data, ETH_ALEN);
- dev_info(ctodev(card), "MAC addr %s\n",
- print_mac(mac, netdev->dev_addr));
-
- card->vlan_index = -1; /* no vlan */
- for (i = 0; i < GELIC_NET_VLAN_MAX; i++) {
- status = lv1_net_control(bus_id(card), dev_id(card),
- GELIC_LV1_GET_VLAN_ID,
- i + 1, /* index; one based */
- 0, 0, &v1, &v2);
- if (status == LV1_NO_ENTRY) {
- dev_dbg(ctodev(card),
- "GELIC_VLAN_ID no entry:%d, VLAN disabled\n",
- status);
- card->vlan_id[i] = 0;
- } else if (status) {
- dev_dbg(ctodev(card),
- "%s:get vlan id faild, status=%d\n",
- __func__, status);
- card->vlan_id[i] = 0;
- } else {
- card->vlan_id[i] = (u32)v1;
- dev_dbg(ctodev(card), "vlan_id:%d, %lx\n", i, v1);
- }
- }
+ memcpy(netdev->dev_addr, &v1, ETH_ALEN);
- if (card->vlan_id[GELIC_LV1_VLAN_TX_ETHERNET - 1]) {
- card->vlan_index = GELIC_LV1_VLAN_TX_ETHERNET - 1;
+ if (card->vlan_required) {
netdev->hard_header_len += VLAN_HLEN;
+ /*
+ * As vlan is internally used,
+ * we can not receive vlan packets
+ */
+ netdev->features |= NETIF_F_VLAN_CHALLENGED;
}
status = register_netdev(netdev);
if (status) {
- dev_err(ctodev(card), "%s:Couldn't register net_device: %d\n",
- __func__, status);
+ dev_err(ctodev(card), "%s:Couldn't register %s %d\n",
+ __func__, netdev->name, status);
return status;
}
+ dev_info(ctodev(card), "%s: MAC addr %s\n",
+ netdev->name,
+ print_mac(mac, netdev->dev_addr));
return 0;
}
@@ -1407,72 +1400,171 @@ static int gelic_net_setup_netdev(struct
*
* the card and net_device structures are linked to each other
*/
-static struct gelic_card *gelic_alloc_card_net(void)
+#define GELIC_ALIGN (32)
+static struct gelic_card *gelic_alloc_card_net(struct net_device **netdev)
{
- struct net_device *netdev;
struct gelic_card *card;
+ struct gelic_port *port;
+ void *p;
size_t alloc_size;
-
- alloc_size = sizeof(*card) +
- sizeof(struct gelic_descr) * GELIC_NET_RX_DESCRIPTORS +
- sizeof(struct gelic_descr) * GELIC_NET_TX_DESCRIPTORS;
/*
- * we assume private data is allocated 32 bytes (or more) aligned
- * so that gelic_descr should be 32 bytes aligned.
- * Current alloc_etherdev() does do it because NETDEV_ALIGN
- * is 32.
- * check this assumption here.
+ * gelic requires dma descriptor is 32 bytes aligned and
+ * the hypervisor requires irq_status is 8 bytes aligned.
*/
- BUILD_BUG_ON(NETDEV_ALIGN < 32);
BUILD_BUG_ON(offsetof(struct gelic_card, irq_status) % 8);
BUILD_BUG_ON(offsetof(struct gelic_card, descr) % 32);
+ alloc_size =
+ sizeof(struct gelic_card) +
+ sizeof(struct gelic_descr) * GELIC_NET_RX_DESCRIPTORS +
+ sizeof(struct gelic_descr) * GELIC_NET_TX_DESCRIPTORS +
+ GELIC_ALIGN - 1;
- netdev = alloc_etherdev(alloc_size);
- if (!netdev)
+ p = kzalloc(alloc_size, GFP_KERNEL);
+ if (!p)
return NULL;
+ card = PTR_ALIGN(p, GELIC_ALIGN);
+ card->unalign = p;
+
+ /*
+ * alloc netdev
+ */
+ *netdev = alloc_etherdev(sizeof(struct gelic_port));
+ if (!netdev) {
+ kfree(card->unalign);
+ return NULL;
+ }
+ port = netdev_priv(*netdev);
+
+ /* gelic_port */
+ port->netdev = *netdev;
+ port->card = card;
+ port->type = GELIC_PORT_ETHERNET;
+
+ /* gelic_card */
+ card->netdev[GELIC_PORT_ETHERNET] = *netdev;
- card = netdev_priv(netdev);
- card->netdev = netdev;
INIT_WORK(&card->tx_timeout_task, gelic_net_tx_timeout_task);
init_waitqueue_head(&card->waitq);
atomic_set(&card->tx_timeout_task_counter, 0);
+ init_MUTEX(&card->updown_lock);
+ atomic_set(&card->users, 0);
return card;
}
+static void gelic_card_get_vlan_info(struct gelic_card *card)
+{
+ u64 v1, v2;
+ int status;
+ unsigned int i;
+ struct {
+ int tx;
+ int rx;
+ } vlan_id_ix[2] = {
+ [GELIC_PORT_ETHERNET] = {
+ .tx = GELIC_LV1_VLAN_TX_ETHERNET,
+ .rx = GELIC_LV1_VLAN_RX_ETHERNET
+ },
+ [GELIC_PORT_WIRELESS] = {
+ .tx = GELIC_LV1_VLAN_TX_WIRELESS,
+ .rx = GELIC_LV1_VLAN_RX_WIRELESS
+ }
+ };
+
+ for (i = 0; i < ARRAY_SIZE(vlan_id_ix); i++) {
+ /* tx tag */
+ status = lv1_net_control(bus_id(card), dev_id(card),
+ GELIC_LV1_GET_VLAN_ID,
+ vlan_id_ix[i].tx,
+ 0, 0, &v1, &v2);
+ if (status || !v1) {
+ if (status != LV1_NO_ENTRY)
+ dev_dbg(ctodev(card),
+ "get vlan id for tx(%d) failed(%d)\n",
+ vlan_id_ix[i].tx, status);
+ card->vlan[i].tx = 0;
+ card->vlan[i].rx = 0;
+ continue;
+ }
+ card->vlan[i].tx = (u16)v1;
+
+ /* rx tag */
+ status = lv1_net_control(bus_id(card), dev_id(card),
+ GELIC_LV1_GET_VLAN_ID,
+ vlan_id_ix[i].rx,
+ 0, 0, &v1, &v2);
+ if (status || !v1) {
+ if (status != LV1_NO_ENTRY)
+ dev_info(ctodev(card),
+ "get vlan id for rx(%d) failed(%d)\n",
+ vlan_id_ix[i].rx, status);
+ card->vlan[i].tx = 0;
+ card->vlan[i].rx = 0;
+ continue;
+ }
+ card->vlan[i].rx = (u16)v1;
+
+ dev_dbg(ctodev(card), "vlan_id[%d] tx=%02x rx=%02x\n",
+ i, card->vlan[i].tx, card->vlan[i].rx);
+ }
+
+ if (card->vlan[GELIC_PORT_ETHERNET].tx) {
+ BUG_ON(!card->vlan[GELIC_PORT_WIRELESS].tx);
+ card->vlan_required = 1;
+ } else
+ card->vlan_required = 0;
+
+ /* check wirelss capable firmware */
+ if (ps3_compare_firmware_version(1, 6, 0) < 0) {
+ card->vlan[GELIC_PORT_WIRELESS].tx = 0;
+ card->vlan[GELIC_PORT_WIRELESS].rx = 0;
+ }
+
+ dev_info(ctodev(card), "internal vlan %s\n",
+ card->vlan_required? "enabled" : "disabled");
+}
/**
* ps3_gelic_driver_probe - add a device to the control of this driver
*/
static int ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
{
- struct gelic_card *card = gelic_alloc_card_net();
+ struct gelic_card *card;
+ struct net_device *netdev;
int result;
- if (!card) {
- dev_info(&dev->core, "gelic_net_alloc_card failed\n");
- result = -ENOMEM;
- goto fail_alloc_card;
- }
-
- ps3_system_bus_set_driver_data(dev, card);
- card->dev = dev;
-
+ pr_debug("%s: called\n", __func__);
result = ps3_open_hv_device(dev);
if (result) {
- dev_dbg(&dev->core, "ps3_open_hv_device failed\n");
+ dev_dbg(&dev->core, "%s:ps3_open_hv_device failed\n",
+ __func__);
goto fail_open;
}
result = ps3_dma_region_create(dev->d_region);
if (result) {
- dev_dbg(&dev->core, "ps3_dma_region_create failed(%d)\n",
- result);
+ dev_dbg(&dev->core, "%s:ps3_dma_region_create failed(%d)\n",
+ __func__, result);
BUG_ON("check region type");
goto fail_dma_region;
}
+ /* alloc card/netdevice */
+ card = gelic_alloc_card_net(&netdev);
+ if (!card) {
+ dev_info(&dev->core, "%s:gelic_net_alloc_card failed\n",
+ __func__);
+ result = -ENOMEM;
+ goto fail_alloc_card;
+ }
+ ps3_system_bus_set_driver_data(dev, card);
+ card->dev = dev;
+
+ /* get internal vlan info */
+ gelic_card_get_vlan_info(card);
+
+ /* setup interrupt */
result = lv1_net_set_interrupt_status_indicator(bus_id(card),
dev_id(card),
ps3_mm_phys_to_lpar(__pa(&card->irq_status)),
@@ -1480,34 +1572,95 @@ static int ps3_gelic_driver_probe(struct
if (result) {
dev_dbg(&dev->core,
- "lv1_net_set_interrupt_status_indicator failed: %s\n",
- ps3_result(result));
+ "%s:set_interrupt_status_indicator failed: %s\n",
+ __func__, ps3_result(result));
result = -EIO;
goto fail_status_indicator;
}
- result = gelic_net_setup_netdev(card);
+ result = ps3_sb_event_receive_port_setup(dev, PS3_BINDING_CPU_ANY,
+ &card->irq);
+
+ if (result) {
+ dev_info(ctodev(card),
+ "%s:gelic_net_open_device failed (%d)\n",
+ __func__, result);
+ result = -EPERM;
+ goto fail_alloc_irq;
+ }
+ result = request_irq(card->irq, gelic_card_interrupt,
+ IRQF_DISABLED, netdev->name, card);
+
+ if (result) {
+ dev_info(ctodev(card), "%s:request_irq failed (%d)\n",
+ __func__, result);
+ goto fail_request_irq;
+ }
+
+ /* setup card structure */
+ card->irq_mask = GELIC_CARD_RXINT | GELIC_CARD_TXINT |
+ GELIC_CARD_PORT_STATUS_CHANGED;
+ card->rx_csum = GELIC_CARD_RX_CSUM_DEFAULT;
+
+
+ if (gelic_card_init_chain(card, &card->tx_chain,
+ card->descr, GELIC_NET_TX_DESCRIPTORS))
+ goto fail_alloc_tx;
+ if (gelic_card_init_chain(card, &card->rx_chain,
+ card->descr + GELIC_NET_TX_DESCRIPTORS,
+ GELIC_NET_RX_DESCRIPTORS))
+ goto fail_alloc_rx;
+
+ /* head of chain */
+ card->tx_top = card->tx_chain.head;
+ card->rx_top = card->rx_chain.head;
+ dev_dbg(ctodev(card), "descr rx %p, tx %p, size %#lx, num %#x\n",
+ card->rx_top, card->tx_top, sizeof(struct gelic_descr),
+ GELIC_NET_RX_DESCRIPTORS);
+ /* allocate rx skbs */
+ if (gelic_card_alloc_rx_skbs(card))
+ goto fail_alloc_skbs;
+
+ spin_lock_init(&card->tx_lock);
+ card->tx_dma_progress = 0;
+ /* setup net_device structure */
+ netdev->irq = card->irq;
+ SET_NETDEV_DEV(netdev, &card->dev->core);
+ gelic_ether_setup_netdev_ops(netdev, &card->napi);
+ result = gelic_net_setup_netdev(netdev, card);
if (result) {
- dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
- "(%d)\n", __func__, __LINE__, result);
+ dev_dbg(&dev->core, "%s: setup_netdev failed %d",
+ __func__, result);
goto fail_setup_netdev;
}
+ pr_debug("%s: done\n", __func__);
return 0;
fail_setup_netdev:
+fail_alloc_skbs:
+ gelic_card_free_chain(card, card->rx_chain.head);
+fail_alloc_rx:
+ gelic_card_free_chain(card, card->tx_chain.head);
+fail_alloc_tx:
+ free_irq(card->irq, card);
+ netdev->irq = NO_IRQ;
+fail_request_irq:
+ ps3_sb_event_receive_port_destroy(dev, card->irq);
+fail_alloc_irq:
lv1_net_set_interrupt_status_indicator(bus_id(card),
- dev_id(card),
- 0 , 0);
+ bus_id(card),
+ 0, 0);
fail_status_indicator:
+ ps3_system_bus_set_driver_data(dev, NULL);
+ kfree(netdev_card(netdev)->unalign);
+ free_netdev(netdev);
+fail_alloc_card:
ps3_dma_region_free(dev->d_region);
fail_dma_region:
ps3_close_hv_device(dev);
fail_open:
- ps3_system_bus_set_driver_data(dev, NULL);
- free_netdev(card->netdev);
-fail_alloc_card:
return result;
}
@@ -1518,6 +1671,28 @@ fail_alloc_card:
static int ps3_gelic_driver_remove(struct ps3_system_bus_device *dev)
{
struct gelic_card *card = ps3_system_bus_get_driver_data(dev);
+ struct net_device *netdev0;
+ pr_debug("%s: called\n", __func__);
+
+ /* stop interrupt */
+ gelic_card_set_irq_mask(card, 0);
+
+ /* turn off DMA, force end */
+ gelic_card_disable_rxdmac(card);
+ gelic_card_disable_txdmac(card);
+
+ /* release chains */
+ gelic_card_release_tx_chain(card, 1);
+ gelic_card_release_rx_chain(card);
+
+ gelic_card_free_chain(card, card->tx_top);
+ gelic_card_free_chain(card, card->rx_top);
+
+ netdev0 = card->netdev[GELIC_PORT_ETHERNET];
+ /* disconnect event port */
+ free_irq(card->irq, card);
+ netdev0->irq = NO_IRQ;
+ ps3_sb_event_receive_port_destroy(card->dev, card->irq);
wait_event(card->waitq,
atomic_read(&card->tx_timeout_task_counter) == 0);
@@ -1525,8 +1700,9 @@ static int ps3_gelic_driver_remove(struc
lv1_net_set_interrupt_status_indicator(bus_id(card), dev_id(card),
0 , 0);
- unregister_netdev(card->netdev);
- free_netdev(card->netdev);
+ unregister_netdev(netdev0);
+ kfree(netdev_card(netdev0)->unalign);
+ free_netdev(netdev0);
ps3_system_bus_set_driver_data(dev, NULL);
@@ -1534,6 +1710,7 @@ static int ps3_gelic_driver_remove(struc
ps3_close_hv_device(dev);
+ pr_debug("%s: done\n", __func__);
return 0;
}
--- a/drivers/net/ps3_gelic_net.h
+++ b/drivers/net/ps3_gelic_net.h
@@ -35,12 +35,11 @@
#define GELIC_NET_MAX_MTU VLAN_ETH_FRAME_LEN
#define GELIC_NET_MIN_MTU VLAN_ETH_ZLEN
#define GELIC_NET_RXBUF_ALIGN 128
-#define GELIC_NET_RX_CSUM_DEFAULT 1 /* hw chksum */
+#define GELIC_CARD_RX_CSUM_DEFAULT 1 /* hw chksum */
#define GELIC_NET_WATCHDOG_TIMEOUT 5*HZ
#define GELIC_NET_NAPI_WEIGHT (GELIC_NET_RX_DESCRIPTORS)
#define GELIC_NET_BROADCAST_ADDR 0xffffffffffffL
-#define GELIC_NET_VLAN_POS (VLAN_ETH_ALEN * 2)
-#define GELIC_NET_VLAN_MAX 4
+
#define GELIC_NET_MC_COUNT_MAX 32 /* multicast address list */
/* virtual interrupt status register bits */
@@ -206,6 +205,13 @@ enum gelic_lv1_vlan_index {
/* size of hardware part of gelic descriptor */
#define GELIC_DESCR_SIZE (32)
+
+enum gelic_port_type {
+ GELIC_PORT_ETHERNET = 0,
+ GELIC_PORT_WIRELESS = 1,
+ GELIC_PORT_MAX
+};
+
struct gelic_descr {
/* as defined by the hardware */
__be32 buf_addr;
@@ -222,7 +228,6 @@ struct gelic_descr {
dma_addr_t bus_addr;
struct gelic_descr *next;
struct gelic_descr *prev;
- struct vlan_ethhdr vlan;
} __attribute__((aligned(32)));
struct gelic_descr_chain {
@@ -231,43 +236,116 @@ struct gelic_descr_chain {
struct gelic_descr *tail;
};
+struct gelic_vlan_id {
+ u16 tx;
+ u16 rx;
+};
+
struct gelic_card {
- struct net_device *netdev;
struct napi_struct napi;
+ struct net_device *netdev[GELIC_PORT_MAX];
/*
* hypervisor requires irq_status should be
* 8 bytes aligned, but u64 member is
* always disposed in that manner
*/
u64 irq_status;
- u64 ghiintmask;
+ u64 irq_mask;
struct ps3_system_bus_device *dev;
- u32 vlan_id[GELIC_NET_VLAN_MAX];
- int vlan_index;
+ struct gelic_vlan_id vlan[GELIC_PORT_MAX];
+ int vlan_required;
struct gelic_descr_chain tx_chain;
struct gelic_descr_chain rx_chain;
int rx_dma_restart_required;
- /* gurad dmac descriptor chain*/
- spinlock_t chain_lock;
-
int rx_csum;
- /* guard tx_dma_progress */
- spinlock_t tx_dma_lock;
+ /*
+ * tx_lock guards tx descriptor list and
+ * tx_dma_progress.
+ */
+ spinlock_t tx_lock;
int tx_dma_progress;
struct work_struct tx_timeout_task;
atomic_t tx_timeout_task_counter;
wait_queue_head_t waitq;
+ /* only first user should up the card */
+ struct semaphore updown_lock;
+ atomic_t users;
+
u64 ether_port_status;
+ /* original address returned by kzalloc */
+ void *unalign;
+ /*
+ * each netdevice has copy of irq
+ */
+ unsigned int irq;
struct gelic_descr *tx_top, *rx_top;
- struct gelic_descr descr[0];
+ struct gelic_descr descr[0]; /* must be the last */
};
+struct gelic_port {
+ struct gelic_card *card;
+ struct net_device *netdev;
+ enum gelic_port_type type;
+ long priv[0]; /* long for alignment */
+};
-extern unsigned long p_to_lp(long pa);
+static inline struct gelic_card *port_to_card(struct gelic_port *p)
+{
+ return p->card;
+}
+static inline struct net_device *port_to_netdev(struct gelic_port *p)
+{
+ return p->netdev;
+}
+static inline struct gelic_card *netdev_card(struct net_device *d)
+{
+ return ((struct gelic_port *)netdev_priv(d))->card;
+}
+static inline struct gelic_port *netdev_port(struct net_device *d)
+{
+ return (struct gelic_port *)netdev_priv(d);
+}
+static inline struct device *ctodev(struct gelic_card *card)
+{
+ return &card->dev->core;
+}
+static inline u64 bus_id(struct gelic_card *card)
+{
+ return card->dev->bus_id;
+}
+static inline u64 dev_id(struct gelic_card *card)
+{
+ return card->dev->dev_id;
+}
+
+static inline void *port_priv(struct gelic_port *port)
+{
+ return port->priv;
+}
+
+extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask);
+/* shared netdev ops */
+extern void gelic_card_up(struct gelic_card *card);
+extern void gelic_card_down(struct gelic_card *card);
+extern int gelic_net_open(struct net_device *netdev);
+extern int gelic_net_stop(struct net_device *netdev);
+extern int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev);
+extern void gelic_net_set_multi(struct net_device *netdev);
+extern void gelic_net_tx_timeout(struct net_device *netdev);
+extern int gelic_net_change_mtu(struct net_device *netdev, int new_mtu);
+extern int gelic_net_setup_netdev(struct net_device *netdev,
+ struct gelic_card *card);
+
+/* shared ethtool ops */
+extern void gelic_net_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *info);
+extern u32 gelic_net_get_rx_csum(struct net_device *netdev);
+extern int gelic_net_set_rx_csum(struct net_device *netdev, u32 data);
+extern void gelic_net_poll_controller(struct net_device *netdev);
#endif /* _GELIC_NET_H */
^ permalink raw reply
* [PATCH v2] PS3: gelic: Add wireless support for PS3
From: Masakazu Mokuno @ 2008-01-24 5:51 UTC (permalink / raw)
To: linux-wireless-u79uwXL29TY76Z2rM5mHXA
Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
geoffrey.levand-mEdOJwZ7QcZBDgjK7y7TUQ, Geert Uytterhoeven
In-Reply-To: <20071213211626.BF8E.MOKUNO-DfbDroY8Xu1L9jVzuh4AOg@public.gmane.org>
Hi
PS3: gelic: Add wireless support for PS3
This is the version 2 of the re-worked (rewritten) version of the
wireless support driver for PS3. The version 1 of the new driver was
submitted on 13th Dec. 2007 (and the old one was submitted to net-dev ML
in June 2007).
Major differences with the old driver are:
- The new driver has a separate ethX interface from ethernet.
They share the same MAC address.
- Thus we can use both ethernet and wireless simultaenously.
- The new driver returns AP's cipher information by the common IE
format in the scan information.
- Cipher selection is done via the common wireless extension way
V2 changes:
- Emit more null IWAP events at appropriate timings, as Dan Williams
pointed out at the V1 submission.
- Fix sparse warnings
o make some functions as static
o use NULL for pointer instead of 0
- Remove extra space
Signed-off-by: Masakazu Mokuno <mokuno-DfbDroY8Xu1L9jVzuh4AOg@public.gmane.org>
---
drivers/net/Kconfig | 10
drivers/net/Makefile | 3
drivers/net/ps3_gelic_net.c | 18
drivers/net/ps3_gelic_net.h | 6
drivers/net/ps3_gelic_wireless.c | 2753 +++++++++++++++++++++++++++++++++++++++
drivers/net/ps3_gelic_wireless.h | 329 ++++
6 files changed, 3117 insertions(+), 2 deletions(-)
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2302,6 +2302,16 @@ config GELIC_NET
To compile this driver as a module, choose M here: the
module will be called ps3_gelic.
+config GELIC_WIRELESS
+ bool "PS3 Wireless support"
+ depends on GELIC_NET
+ help
+ This option adds the support for the wireless feature of PS3.
+ If you have the wireless-less model of PS3 or have no plan to
+ use wireless feature, disabling this option saves memory. As
+ the driver automatically distinguishes the models, you can
+ safely enable this option even if you have a wireless-less model.
+
config GIANFAR
tristate "Gianfar Ethernet"
depends on 85xx || 83xx || PPC_86xx
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -66,7 +66,8 @@ obj-$(CONFIG_BNX2) += bnx2.o
spidernet-y += spider_net.o spider_net_ethtool.o
obj-$(CONFIG_SPIDER_NET) += spidernet.o sungem_phy.o
obj-$(CONFIG_GELIC_NET) += ps3_gelic.o
-ps3_gelic-objs += ps3_gelic_net.o
+gelic_wireless-$(CONFIG_GELIC_WIRELESS) += ps3_gelic_wireless.o
+ps3_gelic-objs += ps3_gelic_net.o $(gelic_wireless-y)
obj-$(CONFIG_TC35815) += tc35815.o
obj-$(CONFIG_SKGE) += skge.o
obj-$(CONFIG_SKY2) += sky2.o
--- a/drivers/net/ps3_gelic_net.c
+++ b/drivers/net/ps3_gelic_net.c
@@ -46,9 +46,10 @@
#include <asm/lv1call.h>
#include "ps3_gelic_net.h"
+#include "ps3_gelic_wireless.h"
#define DRV_NAME "Gelic Network Driver"
-#define DRV_VERSION "1.1"
+#define DRV_VERSION "2.0"
MODULE_AUTHOR("SCE Inc.");
MODULE_DESCRIPTION("Gelic Network driver");
@@ -1154,6 +1155,12 @@ static irqreturn_t gelic_card_interrupt(
if (status & GELIC_CARD_PORT_STATUS_CHANGED)
gelic_card_get_ether_port_status(card, 1);
+#ifdef CONFIG_GELIC_WIRELESS
+ if (status & (GELIC_CARD_WLAN_EVENT_RECEIVED |
+ GELIC_CARD_WLAN_COMMAND_COMPLETED))
+ gelic_wl_interrupt(card->netdev[GELIC_PORT_WIRELESS], status);
+#endif
+
return IRQ_HANDLED;
}
@@ -1635,6 +1642,12 @@ static int ps3_gelic_driver_probe(struct
goto fail_setup_netdev;
}
+#ifdef CONFIG_GELIC_WIRELESS
+ if (gelic_wl_driver_probe(card)) {
+ dev_dbg(&dev->core, "%s: WL init failed\n", __func__);
+ goto fail_setup_netdev;
+ }
+#endif
pr_debug("%s: done\n", __func__);
return 0;
@@ -1674,6 +1687,9 @@ static int ps3_gelic_driver_remove(struc
struct net_device *netdev0;
pr_debug("%s: called\n", __func__);
+#ifdef CONFIG_GELIC_WIRELESS
+ gelic_wl_driver_remove(card);
+#endif
/* stop interrupt */
gelic_card_set_irq_mask(card, 0);
--- a/drivers/net/ps3_gelic_net.h
+++ b/drivers/net/ps3_gelic_net.h
@@ -57,6 +57,8 @@
#define GELIC_CARD_RX_PROTECTION_ERR 0x0000000004000000L
#define GELIC_CARD_TX_TCP_UDP_CHECKSUM_ERR 0x0000000008000000L
#define GELIC_CARD_PORT_STATUS_CHANGED 0x0000000020000000L
+#define GELIC_CARD_WLAN_EVENT_RECEIVED 0x0000000040000000L
+#define GELIC_CARD_WLAN_COMMAND_COMPLETED 0x0000000080000000L
/* INT 0 */
#define GELIC_CARD_TX_FLAGGED_DESCR 0x0004000000000000L
#define GELIC_CARD_RX_FLAGGED_DESCR 0x0040000000000000L
@@ -180,6 +182,10 @@ enum gelic_lv1_net_control_code {
GELIC_LV1_GET_ETH_PORT_STATUS = 2,
GELIC_LV1_SET_NEGOTIATION_MODE = 3,
GELIC_LV1_GET_VLAN_ID = 4,
+ GELIC_LV1_GET_CHANNEL = 6,
+ GELIC_LV1_POST_WLAN_CMD = 9,
+ GELIC_LV1_GET_WLAN_CMD_RESULT = 10,
+ GELIC_LV1_GET_WLAN_EVENT = 11
};
/* status returened from GET_ETH_PORT_STATUS */
--- /dev/null
+++ b/drivers/net/ps3_gelic_wireless.c
@@ -0,0 +1,2753 @@
+/*
+ * PS3 gelic network driver.
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/wireless.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+
+#include <linux/dma-mapping.h>
+#include <net/checksum.h>
+#include <asm/firmware.h>
+#include <asm/ps3.h>
+#include <asm/lv1call.h>
+
+#include "ps3_gelic_net.h"
+#include "ps3_gelic_wireless.h"
+
+
+static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan);
+static int gelic_wl_try_associate(struct net_device *netdev);
+
+/*
+ * tables
+ */
+
+/* 802.11b/g channel to freq in MHz */
+static const int channel_freq[] = {
+ 2412, 2417, 2422, 2427, 2432,
+ 2437, 2442, 2447, 2452, 2457,
+ 2462, 2467, 2472, 2484
+};
+#define NUM_CHANNELS ARRAY_SIZE(channel_freq)
+
+/* in bps */
+static const int bitrate_list[] = {
+ 1000000,
+ 2000000,
+ 5500000,
+ 11000000,
+ 6000000,
+ 9000000,
+ 12000000,
+ 18000000,
+ 24000000,
+ 36000000,
+ 48000000,
+ 54000000
+};
+#define NUM_BITRATES ARRAY_SIZE(bitrate_list)
+
+/*
+ * wpa2 support requires the hypervisor version 2.0 or later
+ */
+static inline int wpa2_capable(void)
+{
+ return (0 <= ps3_compare_firmware_version(2, 0, 0));
+}
+
+static inline int precise_ie(void)
+{
+ return 0; /* FIXME */
+}
+/*
+ * post_eurus_cmd helpers
+ */
+struct eurus_cmd_arg_info {
+ int pre_arg; /* command requres arg1, arg2 at POST COMMAND */
+ int post_arg; /* command requires arg1, arg2 at GET_RESULT */
+};
+
+static const struct eurus_cmd_arg_info cmd_info[GELIC_EURUS_CMD_MAX_INDEX] = {
+ [GELIC_EURUS_CMD_SET_COMMON_CFG] = { .pre_arg = 1},
+ [GELIC_EURUS_CMD_SET_WEP_CFG] = { .pre_arg = 1},
+ [GELIC_EURUS_CMD_SET_WPA_CFG] = { .pre_arg = 1},
+ [GELIC_EURUS_CMD_GET_COMMON_CFG] = { .post_arg = 1},
+ [GELIC_EURUS_CMD_GET_WEP_CFG] = { .post_arg = 1},
+ [GELIC_EURUS_CMD_GET_WPA_CFG] = { .post_arg = 1},
+ [GELIC_EURUS_CMD_GET_RSSI_CFG] = { .post_arg = 1},
+ [GELIC_EURUS_CMD_GET_SCAN] = { .post_arg = 1},
+};
+
+#ifdef DEBUG
+static const char *cmdstr(enum gelic_eurus_command ix)
+{
+ switch (ix) {
+ case GELIC_EURUS_CMD_ASSOC:
+ return "ASSOC";
+ case GELIC_EURUS_CMD_DISASSOC:
+ return "DISASSOC";
+ case GELIC_EURUS_CMD_START_SCAN:
+ return "SCAN";
+ case GELIC_EURUS_CMD_GET_SCAN:
+ return "GET SCAN";
+ case GELIC_EURUS_CMD_SET_COMMON_CFG:
+ return "SET_COMMON_CFG";
+ case GELIC_EURUS_CMD_GET_COMMON_CFG:
+ return "GET_COMMON_CFG";
+ case GELIC_EURUS_CMD_SET_WEP_CFG:
+ return "SET_WEP_CFG";
+ case GELIC_EURUS_CMD_GET_WEP_CFG:
+ return "GET_WEP_CFG";
+ case GELIC_EURUS_CMD_SET_WPA_CFG:
+ return "SET_WPA_CFG";
+ case GELIC_EURUS_CMD_GET_WPA_CFG:
+ return "GET_WPA_CFG";
+ case GELIC_EURUS_CMD_GET_RSSI_CFG:
+ return "GET_RSSI";
+ default:
+ break;
+ }
+ return "";
+};
+#else
+static inline const char *cmdstr(enum gelic_eurus_command ix)
+{
+ return "";
+}
+#endif
+
+/* synchronously do eurus commands */
+static void gelic_eurus_sync_cmd_worker(struct work_struct *work)
+{
+ struct gelic_eurus_cmd *cmd;
+ struct gelic_card *card;
+ struct gelic_wl_info *wl;
+
+ u64 arg1, arg2;
+
+ pr_debug("%s: <-\n", __func__);
+ cmd = container_of(work, struct gelic_eurus_cmd, work);
+ BUG_ON(cmd_info[cmd->cmd].pre_arg &&
+ cmd_info[cmd->cmd].post_arg);
+ wl = cmd->wl;
+ card = port_to_card(wl_port(wl));
+
+ if (cmd_info[cmd->cmd].pre_arg) {
+ arg1 = ps3_mm_phys_to_lpar(__pa(cmd->buffer));
+ arg2 = cmd->buf_size;
+ } else {
+ arg1 = 0;
+ arg2 = 0;
+ }
+ init_completion(&wl->cmd_done_intr);
+ pr_debug("%s: cmd='%s' start\n", __func__, cmdstr(cmd->cmd));
+ cmd->status = lv1_net_control(bus_id(card), dev_id(card),
+ GELIC_LV1_POST_WLAN_CMD,
+ cmd->cmd, arg1, arg2,
+ &cmd->tag, &cmd->size);
+ if (cmd->status) {
+ complete(&cmd->done);
+ pr_info("%s: cmd issue failed\n", __func__);
+ return;
+ }
+
+ wait_for_completion(&wl->cmd_done_intr);
+
+ if (cmd_info[cmd->cmd].post_arg) {
+ arg1 = ps3_mm_phys_to_lpar(__pa(cmd->buffer));
+ arg2 = cmd->buf_size;
+ } else {
+ arg1 = 0;
+ arg2 = 0;
+ }
+
+ cmd->status = lv1_net_control(bus_id(card), dev_id(card),
+ GELIC_LV1_GET_WLAN_CMD_RESULT,
+ cmd->tag, arg1, arg2,
+ &cmd->cmd_status, &cmd->size);
+#ifdef DEBUG
+ if (cmd->status || cmd->cmd_status) {
+ pr_debug("%s: cmd done tag=%#lx arg1=%#lx, arg2=%#lx\n", __func__,
+ cmd->tag, arg1, arg2);
+ pr_debug("%s: cmd done status=%#x cmd_status=%#lx size=%#lx\n",
+ __func__, cmd->status, cmd->cmd_status, cmd->size);
+ }
+#endif
+ complete(&cmd->done);
+ pr_debug("%s: cmd='%s' done\n", __func__, cmdstr(cmd->cmd));
+}
+
+static struct gelic_eurus_cmd *gelic_eurus_sync_cmd(struct gelic_wl_info *wl,
+ unsigned int eurus_cmd,
+ void *buffer,
+ unsigned int buf_size)
+{
+ struct gelic_eurus_cmd *cmd;
+
+ /* allocate cmd */
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return NULL;
+
+ /* initialize members */
+ cmd->cmd = eurus_cmd;
+ cmd->buffer = buffer;
+ cmd->buf_size = buf_size;
+ cmd->wl = wl;
+ INIT_WORK(&cmd->work, gelic_eurus_sync_cmd_worker);
+ init_completion(&cmd->done);
+ queue_work(wl->eurus_cmd_queue, &cmd->work);
+
+ /* wait for command completion */
+ wait_for_completion(&cmd->done);
+
+ return cmd;
+}
+
+static u32 gelic_wl_get_link(struct net_device *netdev)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
+ u32 ret;
+
+ pr_debug("%s: <-\n", __func__);
+ down(&wl->assoc_stat_lock);
+ if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
+ ret = 1;
+ else
+ ret = 0;
+ up(&wl->assoc_stat_lock);
+ pr_debug("%s: ->\n", __func__);
+ return ret;
+}
+
+static void gelic_wl_send_iwap_event(struct gelic_wl_info *wl, u8 *bssid)
+{
+ union iwreq_data data;
+
+ memset(&data, 0, sizeof(data));
+ if (bssid)
+ memcpy(data.ap_addr.sa_data, bssid, ETH_ALEN);
+ data.ap_addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWAP,
+ &data, NULL);
+}
+
+/*
+ * wireless extension handlers and helpers
+ */
+
+/* SIOGIWNAME */
+static int gelic_wl_get_name(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *iwreq, char *extra)
+{
+ strcpy(iwreq->name, "IEEE 802.11bg");
+ return 0;
+}
+
+static void gelic_wl_get_ch_info(struct gelic_wl_info *wl)
+{
+ struct gelic_card *card = port_to_card(wl_port(wl));
+ u64 ch_info_raw, tmp;
+ int status;
+
+ if (!test_and_set_bit(GELIC_WL_STAT_CH_INFO, &wl->stat)) {
+ status = lv1_net_control(bus_id(card), dev_id(card),
+ GELIC_LV1_GET_CHANNEL, 0, 0, 0,
+ &ch_info_raw,
+ &tmp);
+ /* some fw versions may return error */
+ if (status) {
+ if (status != LV1_NO_ENTRY)
+ pr_info("%s: available ch unknown\n", __func__);
+ wl->ch_info = 0x07ff;/* 11 ch */
+ } else
+ /* 16 bits of MSB has available channels */
+ wl->ch_info = ch_info_raw >> 48;
+ }
+ return;
+}
+
+/* SIOGIWRANGE */
+static int gelic_wl_get_range(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *iwreq, char *extra)
+{
+ struct iw_point *point = &iwreq->data;
+ struct iw_range *range = (struct iw_range *)extra;
+ struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
+ unsigned int i, chs;
+
+ pr_debug("%s: <-\n", __func__);
+ point->length = sizeof(struct iw_range);
+ memset(range, 0, sizeof(struct iw_range));
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 22;
+
+ /* available channels and frequencies */
+ gelic_wl_get_ch_info(wl);
+
+ for (i = 0, chs = 0;
+ i < NUM_CHANNELS && chs < IW_MAX_FREQUENCIES; i++)
+ if (wl->ch_info & (1 << i)) {
+ range->freq[chs].i = i + 1;
+ range->freq[chs].m = channel_freq[i];
+ range->freq[chs].e = 6;
+ chs++;
+ }
+ range->num_frequency = chs;
+ range->old_num_frequency = chs;
+ range->num_channels = chs;
+ range->old_num_channels = chs;
+
+ /* bitrates */
+ for (i = 0; i < NUM_BITRATES; i++)
+ range->bitrate[i] = bitrate_list[i];
+ range->num_bitrates = i;
+
+ /* signal levels */
+ range->max_qual.qual = 100; /* relative value */
+ range->max_qual.level = 100;
+ range->avg_qual.qual = 50;
+ range->avg_qual.level = 50;
+ range->sensitivity = 0;
+
+ /* Event capability */
+ IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
+
+ /* encryption capability */
+ range->enc_capa = IW_ENC_CAPA_WPA |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+ if (wpa2_capable())
+ range->enc_capa |= IW_ENC_CAPA_WPA2;
+ range->encoding_size[0] = 5; /* 40bit WEP */
+ range->encoding_size[1] = 13; /* 104bit WEP */
+ range->encoding_size[2] = 32; /* WPA-PSK */
+ range->num_encoding_sizes = 3;
+ range->max_encoding_tokens = GELIC_WEP_KEYS;
+
+ pr_debug("%s: ->\n", __func__);
+ return 0;
+
+}
+
+/* SIOC{G,S}IWSCAN */
+static int gelic_wl_set_scan(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+
+ return gelic_wl_start_scan(wl, 1);
+}
+
+#define OUI_LEN 3
+static const u8 rsn_oui[OUI_LEN] = { 0x00, 0x0f, 0xac };
+static const u8 wpa_oui[OUI_LEN] = { 0x00, 0x50, 0xf2 };
+
+/*
+ * synthesize WPA/RSN IE data
+ * See WiFi WPA specification and IEEE 802.11-2007 7.3.2.25
+ * for the format
+ */
+static size_t gelic_wl_synthesize_ie(u8 *buf,
+ struct gelic_eurus_scan_info *scan)
+{
+
+ const u8 *oui_header;
+ u8 *start = buf;
+ int rsn;
+ int ccmp;
+
+ pr_debug("%s: <- sec=%16x\n", __func__, scan->security);
+ switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_MASK) {
+ case GELIC_EURUS_SCAN_SEC_WPA:
+ rsn = 0;
+ break;
+ case GELIC_EURUS_SCAN_SEC_WPA2:
+ rsn = 1;
+ break;
+ default:
+ /* WEP or none. No IE returned */
+ return 0;
+ }
+
+ switch (be16_to_cpu(scan->security) & GELIC_EURUS_SCAN_SEC_WPA_MASK) {
+ case GELIC_EURUS_SCAN_SEC_WPA_TKIP:
+ ccmp = 0;
+ break;
+ case GELIC_EURUS_SCAN_SEC_WPA_AES:
+ ccmp = 1;
+ break;
+ default:
+ if (rsn) {
+ ccmp = 1;
+ pr_info("%s: no cipher info. defaulted to CCMP\n",
+ __func__);
+ } else {
+ ccmp = 0;
+ pr_info("%s: no cipher info. defaulted to TKIP\n",
+ __func__);
+ }
+ }
+
+ if (rsn)
+ oui_header = rsn_oui;
+ else
+ oui_header = wpa_oui;
+
+ /* element id */
+ if (rsn)
+ *buf++ = MFIE_TYPE_RSN;
+ else
+ *buf++ = MFIE_TYPE_GENERIC;
+
+ /* length filed; set later */
+ buf++;
+
+ /* wpa special header */
+ if (!rsn) {
+ memcpy(buf, wpa_oui, OUI_LEN);
+ buf += OUI_LEN;
+ *buf++ = 0x01;
+ }
+
+ /* version */
+ *buf++ = 0x01; /* version 1.0 */
+ *buf++ = 0x00;
+
+ /* group cipher */
+ memcpy(buf, oui_header, OUI_LEN);
+ buf += OUI_LEN;
+
+ if (ccmp)
+ *buf++ = 0x04; /* CCMP */
+ else
+ *buf++ = 0x02; /* TKIP */
+
+ /* pairwise key count always 1 */
+ *buf++ = 0x01;
+ *buf++ = 0x00;
+
+ /* pairwise key suit */
+ memcpy(buf, oui_header, OUI_LEN);
+ buf += OUI_LEN;
+ if (ccmp)
+ *buf++ = 0x04; /* CCMP */
+ else
+ *buf++ = 0x02; /* TKIP */
+
+ /* AKM count is 1 */
+ *buf++ = 0x01;
+ *buf++ = 0x00;
+
+ /* AKM suite is assumed as PSK*/
+ memcpy(buf, oui_header, OUI_LEN);
+ buf += OUI_LEN;
+ *buf++ = 0x02; /* PSK */
+
+ /* RSN capabilities is 0 */
+ *buf++ = 0x00;
+ *buf++ = 0x00;
+
+ /* set length field */
+ start[1] = (buf - start - 2);
+
+ pr_debug("%s: ->\n", __func__);
+ return (buf - start);
+}
+
+struct ie_item {
+ u8 *data;
+ u8 len;
+};
+
+struct ie_info {
+ struct ie_item wpa;
+ struct ie_item rsn;
+};
+
+static void gelic_wl_parse_ie(u8 *data, size_t len,
+ struct ie_info *ie_info)
+{
+ size_t data_left = len;
+ u8 *pos = data;
+ u8 item_len;
+ u8 item_id;
+
+ pr_debug("%s: data=%p len=%ld \n", __func__,
+ data, len);
+ memset(ie_info, 0, sizeof(struct ie_info));
+
+ while (0 < data_left) {
+ item_id = *pos++;
+ item_len = *pos++;
+
+ switch (item_id) {
+ case MFIE_TYPE_GENERIC:
+ if (!memcmp(pos, wpa_oui, OUI_LEN) &&
+ pos[OUI_LEN] == 0x01) {
+ ie_info->wpa.data = pos - 2;
+ ie_info->wpa.len = item_len + 2;
+ }
+ break;
+ case MFIE_TYPE_RSN:
+ ie_info->rsn.data = pos - 2;
+ /* length includes the header */
+ ie_info->rsn.len = item_len + 2;
+ break;
+ default:
+ pr_debug("%s: ignore %#x,%d\n", __func__,
+ item_id, item_len);
+ break;
+ }
+ pos += item_len;
+ data_left -= item_len + 2;
+ }
+ pr_debug("%s: wpa=%p,%d wpa2=%p,%d\n", __func__,
+ ie_info->wpa.data, ie_info->wpa.len,
+ ie_info->rsn.data, ie_info->rsn.len);
+}
+
+
+/*
+ * translate the scan informations from hypervisor to a
+ * independent format
+ */
+static char *gelic_wl_translate_scan(struct net_device *netdev,
+ char *ev,
+ char *stop,
+ struct gelic_wl_scan_info *network)
+{
+ struct iw_event iwe;
+ struct gelic_eurus_scan_info *scan = network->hwinfo;
+ char *tmp;
+ u8 rate;
+ unsigned int i, j, len;
+ u8 buf[MAX_WPA_IE_LEN];
+
+ pr_debug("%s: <-\n", __func__);
+
+ /* first entry should be AP's mac address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, &scan->bssid[2], ETH_ALEN);
+ ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_ADDR_LEN);
+
+ /* ESSID */
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ iwe.u.data.length = strnlen(scan->essid, 32);
+ ev = iwe_stream_add_point(ev, stop, &iwe, scan->essid);
+
+ /* FREQUENCY */
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = be16_to_cpu(scan->channel);
+ iwe.u.freq.e = 0; /* table value in MHz */
+ iwe.u.freq.i = 0;
+ ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_FREQ_LEN);
+
+ /* RATES */
+ iwe.cmd = SIOCGIWRATE;
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+ /* to stuff multiple values in one event */
+ tmp = ev + IW_EV_LCP_LEN;
+ /* put them in ascendant order (older is first) */
+ i = 0;
+ j = 0;
+ pr_debug("%s: rates=%d rate=%d\n", __func__,
+ network->rate_len, network->rate_ext_len);
+ while (i < network->rate_len) {
+ if (j < network->rate_ext_len &&
+ ((scan->ext_rate[j] & 0x7f) < (scan->rate[i] & 0x7f)))
+ rate = scan->ext_rate[j++] & 0x7f;
+ else
+ rate = scan->rate[i++] & 0x7f;
+ iwe.u.bitrate.value = rate * 500000; /* 500kbps unit */
+ tmp = iwe_stream_add_value(ev, tmp, stop, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+ while (j < network->rate_ext_len) {
+ iwe.u.bitrate.value = (scan->ext_rate[j++] & 0x7f) * 500000;
+ tmp = iwe_stream_add_value(ev, tmp, stop, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+ /* Check if we added any rate */
+ if (IW_EV_LCP_LEN < (tmp - ev))
+ ev = tmp;
+
+ /* ENCODE */
+ iwe.cmd = SIOCGIWENCODE;
+ if (be16_to_cpu(scan->capability) & 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;
+ ev = iwe_stream_add_point(ev, stop, &iwe, scan->essid);
+
+ /* MODE */
+ iwe.cmd = SIOCGIWMODE;
+ if (be16_to_cpu(scan->capability) &
+ (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
+ if (be16_to_cpu(scan->capability) & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_UINT_LEN);
+ }
+
+ /* QUAL */
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.updated = IW_QUAL_ALL_UPDATED |
+ IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
+ iwe.u.qual.level = be16_to_cpu(scan->rssi);
+ iwe.u.qual.qual = be16_to_cpu(scan->rssi);
+ iwe.u.qual.noise = 0;
+ ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_QUAL_LEN);
+
+ /* RSN */
+ memset(&iwe, 0, sizeof(iwe));
+ if (be16_to_cpu(scan->size) <= sizeof(*scan)) {
+ /* If wpa[2] capable station, synthesize IE and put it */
+ len = gelic_wl_synthesize_ie(buf, scan);
+ if (len) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = len;
+ ev = iwe_stream_add_point(ev, stop, &iwe, buf);
+ }
+ } else {
+ /* this scan info has IE data */
+ struct ie_info ie_info;
+ size_t data_len;
+
+ data_len = be16_to_cpu(scan->size) - sizeof(*scan);
+
+ gelic_wl_parse_ie(scan->elements, data_len, &ie_info);
+
+ if (ie_info.wpa.len && (ie_info.wpa.len <= sizeof(buf))) {
+ memcpy(buf, ie_info.wpa.data, ie_info.wpa.len);
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie_info.wpa.len;
+ ev = iwe_stream_add_point(ev, stop, &iwe, buf);
+ }
+
+ if (ie_info.rsn.len && (ie_info.rsn.len <= sizeof(buf))) {
+ memset(&iwe, 0, sizeof(iwe));
+ memcpy(buf, ie_info.rsn.data, ie_info.rsn.len);
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie_info.rsn.len;
+ ev = iwe_stream_add_point(ev, stop, &iwe, buf);
+ }
+ }
+
+ pr_debug("%s: ->\n", __func__);
+ return ev;
+}
+
+
+static int gelic_wl_get_scan(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ struct gelic_wl_scan_info *scan_info;
+ char *ev = extra;
+ char *stop = ev + wrqu->data.length;
+ int ret = 0;
+ unsigned long this_time = jiffies;
+
+ pr_debug("%s: <-\n", __func__);
+ if (down_interruptible(&wl->scan_lock))
+ return -EAGAIN;
+
+ switch (wl->scan_stat) {
+ case GELIC_WL_SCAN_STAT_SCANNING:
+ /* If a scan in progress, caller should call me again */
+ ret = -EAGAIN;
+ goto out;
+ break;
+
+ case GELIC_WL_SCAN_STAT_INIT:
+ /* last scan request failed or never issued */
+ ret = -ENODEV;
+ goto out;
+ break;
+ case GELIC_WL_SCAN_STAT_GOT_LIST:
+ /* ok, use current list */
+ break;
+ }
+
+ list_for_each_entry(scan_info, &wl->network_list, list) {
+ if (wl->scan_age == 0 ||
+ time_after(scan_info->last_scanned + wl->scan_age,
+ this_time))
+ ev = gelic_wl_translate_scan(netdev, ev, stop,
+ scan_info);
+ else
+ pr_debug("%s:entry too old\n", __func__);
+
+ if (stop - ev <= IW_EV_ADDR_LEN) {
+ ret = -E2BIG;
+ goto out;
+ }
+ }
+
+ wrqu->data.length = ev - extra;
+ wrqu->data.flags = 0;
+out:
+ up(&wl->scan_lock);
+ pr_debug("%s: -> %d %d\n", __func__, ret, wrqu->data.length);
+ return ret;
+}
+
+#ifdef DEBUG
+static void scan_list_dump(struct gelic_wl_info *wl)
+{
+ struct gelic_wl_scan_info *scan_info;
+ int i;
+ DECLARE_MAC_BUF(mac);
+
+ i = 0;
+ list_for_each_entry(scan_info, &wl->network_list, list) {
+ pr_debug("%s: item %d\n", __func__, i++);
+ pr_debug("valid=%d eurusindex=%d last=%lx\n",
+ scan_info->valid, scan_info->eurus_index,
+ scan_info->last_scanned);
+ pr_debug("r_len=%d r_ext_len=%d essid_len=%d\n",
+ scan_info->rate_len, scan_info->rate_ext_len,
+ scan_info->essid_len);
+ /* -- */
+ pr_debug("bssid=%s\n",
+ print_mac(mac, &scan_info->hwinfo->bssid[2]));
+ pr_debug("essid=%s\n", scan_info->hwinfo->essid);
+ }
+}
+#endif
+
+static int gelic_wl_set_auth(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct iw_param *param = &data->param;
+ struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
+ unsigned long irqflag;
+ int ret = 0;
+
+ pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX);
+ spin_lock_irqsave(&wl->lock, irqflag);
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ if (param->value & IW_AUTH_WPA_VERSION_DISABLED) {
+ pr_debug("%s: NO WPA selected\n", __func__);
+ wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
+ wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
+ }
+ if (param->value & IW_AUTH_WPA_VERSION_WPA) {
+ pr_debug("%s: WPA version 1 selected\n", __func__);
+ wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA;
+ wl->group_cipher_method = GELIC_WL_CIPHER_TKIP;
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP;
+ wl->auth_method = GELIC_EURUS_AUTH_OPEN;
+ }
+ if (param->value & IW_AUTH_WPA_VERSION_WPA2) {
+ /*
+ * As the hypervisor may not tell the cipher
+ * information of the AP if it is WPA2,
+ * you will not decide suitable cipher from
+ * its beacon.
+ * You should have knowledge about the AP's
+ * cipher infomation in other method prior to
+ * the association.
+ */
+ if (!precise_ie())
+ pr_info("%s: WPA2 may not work\n", __func__);
+ if (wpa2_capable()) {
+ wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA2;
+ wl->group_cipher_method = GELIC_WL_CIPHER_AES;
+ wl->pairwise_cipher_method =
+ GELIC_WL_CIPHER_AES;
+ wl->auth_method = GELIC_EURUS_AUTH_OPEN;
+ } else
+ ret = -EINVAL;
+ }
+ break;
+
+ case IW_AUTH_CIPHER_PAIRWISE:
+ if (param->value &
+ (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) {
+ pr_debug("%s: WEP selected\n", __func__);
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
+ }
+ if (param->value & IW_AUTH_CIPHER_TKIP) {
+ pr_debug("%s: TKIP selected\n", __func__);
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP;
+ }
+ if (param->value & IW_AUTH_CIPHER_CCMP) {
+ pr_debug("%s: CCMP selected\n", __func__);
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_AES;
+ }
+ if (param->value & IW_AUTH_CIPHER_NONE) {
+ pr_debug("%s: no auth selected\n", __func__);
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
+ }
+ break;
+ case IW_AUTH_CIPHER_GROUP:
+ if (param->value &
+ (IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_WEP40)) {
+ pr_debug("%s: WEP selected\n", __func__);
+ wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
+ }
+ if (param->value & IW_AUTH_CIPHER_TKIP) {
+ pr_debug("%s: TKIP selected\n", __func__);
+ wl->group_cipher_method = GELIC_WL_CIPHER_TKIP;
+ }
+ if (param->value & IW_AUTH_CIPHER_CCMP) {
+ pr_debug("%s: CCMP selected\n", __func__);
+ wl->group_cipher_method = GELIC_WL_CIPHER_AES;
+ }
+ if (param->value & IW_AUTH_CIPHER_NONE) {
+ pr_debug("%s: no auth selected\n", __func__);
+ wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
+ }
+ break;
+ case IW_AUTH_80211_AUTH_ALG:
+ if (param->value & IW_AUTH_ALG_SHARED_KEY) {
+ pr_debug("%s: shared key specified\n", __func__);
+ wl->auth_method = GELIC_EURUS_AUTH_SHARED;
+ } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+ pr_debug("%s: open system specified\n", __func__);
+ wl->auth_method = GELIC_EURUS_AUTH_OPEN;
+ } else
+ ret = -EINVAL;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ if (param->value) {
+ pr_debug("%s: WPA enabled\n", __func__);
+ wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA;
+ } else {
+ pr_debug("%s: WPA disabled\n", __func__);
+ wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
+ }
+ break;
+
+ case IW_AUTH_KEY_MGMT:
+ if (param->value & IW_AUTH_KEY_MGMT_PSK)
+ break;
+ /* intentionally fall through */
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ };
+
+ if (!ret)
+ set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
+
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ pr_debug("%s: -> %d\n", __func__, ret);
+ return ret;
+}
+
+static int gelic_wl_get_auth(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *iwreq, char *extra)
+{
+ struct iw_param *param = &iwreq->param;
+ struct gelic_wl_info *wl = port_wl(netdev_port(netdev));
+ unsigned long irqflag;
+ int ret = 0;
+
+ pr_debug("%s: <- %d\n", __func__, param->flags & IW_AUTH_INDEX);
+ spin_lock_irqsave(&wl->lock, irqflag);
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ switch (wl->wpa_level) {
+ case GELIC_WL_WPA_LEVEL_WPA:
+ param->value |= IW_AUTH_WPA_VERSION_WPA;
+ break;
+ case GELIC_WL_WPA_LEVEL_WPA2:
+ param->value |= IW_AUTH_WPA_VERSION_WPA2;
+ break;
+ default:
+ param->value |= IW_AUTH_WPA_VERSION_DISABLED;
+ }
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ if (wl->auth_method == GELIC_EURUS_AUTH_SHARED)
+ param->value = IW_AUTH_ALG_SHARED_KEY;
+ else if (wl->auth_method == GELIC_EURUS_AUTH_OPEN)
+ param->value = IW_AUTH_ALG_OPEN_SYSTEM;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ switch (wl->wpa_level) {
+ case GELIC_WL_WPA_LEVEL_WPA:
+ case GELIC_WL_WPA_LEVEL_WPA2:
+ param->value = 1;
+ break;
+ default:
+ param->value = 0;
+ break;
+ }
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ pr_debug("%s: -> %d\n", __func__, ret);
+ return ret;
+}
+
+/* SIOC{S,G}IWESSID */
+static int gelic_wl_set_essid(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ unsigned long irqflag;
+
+ pr_debug("%s: <- l=%d f=%d\n", __func__,
+ data->essid.length, data->essid.flags);
+ if (IW_ESSID_MAX_SIZE < data->essid.length)
+ return -EINVAL;
+
+ spin_lock_irqsave(&wl->lock, irqflag);
+ if (data->essid.flags) {
+ wl->essid_len = data->essid.length;
+ memcpy(wl->essid, extra, wl->essid_len);
+ pr_debug("%s: essid = '%s'\n", __func__, extra);
+ set_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat);
+ } else {
+ pr_debug("%s: ESSID any \n", __func__);
+ clear_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat);
+ }
+ set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+
+
+ gelic_wl_try_associate(netdev); /* FIXME */
+ pr_debug("%s: -> \n", __func__);
+ return 0;
+}
+
+static int gelic_wl_get_essid(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ unsigned long irqflag;
+
+ pr_debug("%s: <- \n", __func__);
+ down(&wl->assoc_stat_lock);
+ spin_lock_irqsave(&wl->lock, irqflag);
+ if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat) ||
+ wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) {
+ memcpy(extra, wl->essid, wl->essid_len);
+ data->essid.length = wl->essid_len;
+ data->essid.flags = 1;
+ } else
+ data->essid.flags = 0;
+
+ up(&wl->assoc_stat_lock);
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ pr_debug("%s: -> len=%d \n", __func__, data->essid.length);
+
+ return 0;
+}
+
+/* SIO{S,G}IWENCODE */
+static int gelic_wl_set_encode(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ struct iw_point *enc = &data->encoding;
+ __u16 flags;
+ unsigned int irqflag;
+ int key_index, index_specified;
+ int ret = 0;
+
+ pr_debug("%s: <- \n", __func__);
+ flags = enc->flags & IW_ENCODE_FLAGS;
+ key_index = enc->flags & IW_ENCODE_INDEX;
+
+ pr_debug("%s: key_index = %d\n", __func__, key_index);
+ pr_debug("%s: key_len = %d\n", __func__, enc->length);
+ pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
+
+ if (GELIC_WEP_KEYS < key_index)
+ return -EINVAL;
+
+ spin_lock_irqsave(&wl->lock, irqflag);
+ if (key_index) {
+ index_specified = 1;
+ key_index--;
+ } else {
+ index_specified = 0;
+ key_index = wl->current_key;
+ }
+
+ if (flags & IW_ENCODE_NOKEY) {
+ /* if just IW_ENCODE_NOKEY, change current key index */
+ if (!flags && index_specified) {
+ wl->current_key = key_index;
+ goto done;
+ }
+
+ if (flags & IW_ENCODE_DISABLED) {
+ if (!index_specified) {
+ /* disable encryption */
+ wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
+ wl->pairwise_cipher_method =
+ GELIC_WL_CIPHER_NONE;
+ /* invalidate all key */
+ wl->key_enabled = 0;
+ } else
+ clear_bit(key_index, &wl->key_enabled);
+ }
+
+ if (flags & IW_ENCODE_OPEN)
+ wl->auth_method = GELIC_EURUS_AUTH_OPEN;
+ if (flags & IW_ENCODE_RESTRICTED) {
+ pr_info("%s: shared key mode enabled\n", __func__);
+ wl->auth_method = GELIC_EURUS_AUTH_SHARED;
+ }
+ } else {
+ if (IW_ENCODING_TOKEN_MAX < enc->length) {
+ ret = -EINVAL;
+ goto done;
+ }
+ wl->key_len[key_index] = enc->length;
+ memcpy(wl->key[key_index], extra, enc->length);
+ set_bit(key_index, &wl->key_enabled);
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_WEP;
+ wl->group_cipher_method = GELIC_WL_CIPHER_WEP;
+ }
+ set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
+done:
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ pr_debug("%s: -> \n", __func__);
+ return ret;
+}
+
+static int gelic_wl_get_encode(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ struct iw_point *enc = &data->encoding;
+ unsigned int irqflag;
+ unsigned int key_index, index_specified;
+ int ret = 0;
+
+ pr_debug("%s: <- \n", __func__);
+ key_index = enc->flags & IW_ENCODE_INDEX;
+ pr_debug("%s: flag=%#x point=%p len=%d extra=%p\n", __func__,
+ enc->flags, enc->pointer, enc->length, extra);
+ if (GELIC_WEP_KEYS < key_index)
+ return -EINVAL;
+
+ spin_lock_irqsave(&wl->lock, irqflag);
+ if (key_index) {
+ index_specified = 1;
+ key_index--;
+ } else {
+ index_specified = 0;
+ key_index = wl->current_key;
+ }
+
+ if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
+ switch (wl->auth_method) {
+ case GELIC_EURUS_AUTH_OPEN:
+ enc->flags = IW_ENCODE_OPEN;
+ break;
+ case GELIC_EURUS_AUTH_SHARED:
+ enc->flags = IW_ENCODE_RESTRICTED;
+ break;
+ }
+ } else
+ enc->flags = IW_ENCODE_DISABLED;
+
+ if (test_bit(key_index, &wl->key_enabled)) {
+ if (enc->length < wl->key_len[key_index]) {
+ ret = -EINVAL;
+ goto done;
+ }
+ enc->length = wl->key_len[key_index];
+ memcpy(extra, wl->key[key_index], wl->key_len[key_index]);
+ } else {
+ enc->length = 0;
+ enc->flags |= IW_ENCODE_NOKEY;
+ }
+ enc->flags |= key_index + 1;
+ pr_debug("%s: -> flag=%x len=%d\n", __func__,
+ enc->flags, enc->length);
+
+done:
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ return ret;
+}
+
+/* SIOC{S,G}IWAP */
+static int gelic_wl_set_ap(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ unsigned long irqflag;
+
+ pr_debug("%s: <-\n", __func__);
+ if (data->ap_addr.sa_family != ARPHRD_ETHER)
+ return -EINVAL;
+
+ spin_lock_irqsave(&wl->lock, irqflag);
+ if (is_valid_ether_addr(data->ap_addr.sa_data)) {
+ memcpy(wl->bssid, data->ap_addr.sa_data,
+ ETH_ALEN);
+ set_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat);
+ set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
+ pr_debug("%s: bss=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ __func__,
+ wl->bssid[0], wl->bssid[1],
+ wl->bssid[2], wl->bssid[3],
+ wl->bssid[4], wl->bssid[5]);
+ } else {
+ pr_debug("%s: clear bssid\n", __func__);
+ clear_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat);
+ memset(wl->bssid, 0, ETH_ALEN);
+ }
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ pr_debug("%s: ->\n", __func__);
+ return 0;
+}
+
+static int gelic_wl_get_ap(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ unsigned long irqflag;
+
+ pr_debug("%s: <-\n", __func__);
+ down(&wl->assoc_stat_lock);
+ spin_lock_irqsave(&wl->lock, irqflag);
+ if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED) {
+ data->ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(data->ap_addr.sa_data, wl->active_bssid,
+ ETH_ALEN);
+ } else
+ memset(data->ap_addr.sa_data, 0, ETH_ALEN);
+
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ up(&wl->assoc_stat_lock);
+ pr_debug("%s: ->\n", __func__);
+ return 0;
+}
+
+/* SIOC{S,G}IWENCODEEXT */
+static int gelic_wl_set_encodeext(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ struct iw_point *enc = &data->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ __u16 alg;
+ __u16 flags;
+ unsigned int irqflag;
+ int key_index;
+ int ret = 0;
+
+ pr_debug("%s: <- \n", __func__);
+ flags = enc->flags & IW_ENCODE_FLAGS;
+ alg = ext->alg;
+ key_index = enc->flags & IW_ENCODE_INDEX;
+
+ pr_debug("%s: key_index = %d\n", __func__, key_index);
+ pr_debug("%s: key_len = %d\n", __func__, enc->length);
+ pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
+ pr_debug("%s: ext_flag=%x\n", __func__, ext->ext_flags);
+ pr_debug("%s: ext_key_len=%x\n", __func__, ext->key_len);
+
+ if (GELIC_WEP_KEYS < key_index)
+ return -EINVAL;
+
+ spin_lock_irqsave(&wl->lock, irqflag);
+ if (key_index)
+ key_index--;
+ else
+ key_index = wl->current_key;
+
+ if (!enc->length && (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) {
+ /* reques to change default key index */
+ pr_debug("%s: request to change default key to %d\n",
+ __func__, key_index);
+ wl->current_key = key_index;
+ goto done;
+ }
+
+ if (alg == IW_ENCODE_ALG_NONE || (flags & IW_ENCODE_DISABLED)) {
+ pr_debug("%s: alg disabled\n", __func__);
+ wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
+ wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
+ wl->auth_method = GELIC_EURUS_AUTH_OPEN; /* should be open */
+ } else if (alg == IW_ENCODE_ALG_WEP) {
+ pr_debug("%s: WEP requested\n", __func__);
+ if (flags & IW_ENCODE_OPEN) {
+ pr_debug("%s: open key mode\n", __func__);
+ wl->auth_method = GELIC_EURUS_AUTH_OPEN;
+ }
+ if (flags & IW_ENCODE_RESTRICTED) {
+ pr_debug("%s: shared key mode\n", __func__);
+ wl->auth_method = GELIC_EURUS_AUTH_SHARED;
+ }
+ if (IW_ENCODING_TOKEN_MAX < ext->key_len) {
+ pr_info("%s: key is too long %d\n", __func__,
+ ext->key_len);
+ ret = -EINVAL;
+ goto done;
+ }
+ /* OK, update the key */
+ wl->key_len[key_index] = ext->key_len;
+ memset(wl->key[key_index], 0, IW_ENCODING_TOKEN_MAX);
+ memcpy(wl->key[key_index], ext->key, ext->key_len);
+ set_bit(key_index, &wl->key_enabled);
+ /* remember wep info changed */
+ set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
+ } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
+ pr_debug("%s: TKIP/CCMP requested alg=%d\n", __func__, alg);
+ /* check key length */
+ if (IW_ENCODING_TOKEN_MAX < ext->key_len) {
+ pr_info("%s: key is too long %d\n", __func__,
+ ext->key_len);
+ ret = -EINVAL;
+ goto done;
+ }
+ if (alg == IW_ENCODE_ALG_CCMP) {
+ pr_debug("%s: AES selected\n", __func__);
+ wl->group_cipher_method = GELIC_WL_CIPHER_AES;
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_AES;
+ wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA2;
+ } else {
+ pr_debug("%s: TKIP selected, WPA forced\n", __func__);
+ wl->group_cipher_method = GELIC_WL_CIPHER_TKIP;
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_TKIP;
+ /* FIXME: how do we do if WPA2 + TKIP? */
+ wl->wpa_level = GELIC_WL_WPA_LEVEL_WPA;
+ }
+ if (flags & IW_ENCODE_RESTRICTED)
+ BUG();
+ wl->auth_method = GELIC_EURUS_AUTH_OPEN;
+ /* We should use same key for both and unicast */
+ if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+ pr_debug("%s: group key \n", __func__);
+ else
+ pr_debug("%s: unicast key \n", __func__);
+ /* OK, update the key */
+ wl->key_len[key_index] = ext->key_len;
+ memset(wl->key[key_index], 0, IW_ENCODING_TOKEN_MAX);
+ memcpy(wl->key[key_index], ext->key, ext->key_len);
+ set_bit(key_index, &wl->key_enabled);
+ /* remember info changed */
+ set_bit(GELIC_WL_STAT_CONFIGURED, &wl->stat);
+ }
+done:
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ pr_debug("%s: -> \n", __func__);
+ return ret;
+}
+
+static int gelic_wl_get_encodeext(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ struct iw_point *enc = &data->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ unsigned int irqflag;
+ int key_index;
+ int ret = 0;
+ int max_key_len;
+
+ pr_debug("%s: <- \n", __func__);
+
+ max_key_len = enc->length - sizeof(struct iw_encode_ext);
+ if (max_key_len < 0)
+ return -EINVAL;
+ key_index = enc->flags & IW_ENCODE_INDEX;
+
+ pr_debug("%s: key_index = %d\n", __func__, key_index);
+ pr_debug("%s: key_len = %d\n", __func__, enc->length);
+ pr_debug("%s: flag=%x\n", __func__, enc->flags & IW_ENCODE_FLAGS);
+
+ if (GELIC_WEP_KEYS < key_index)
+ return -EINVAL;
+
+ spin_lock_irqsave(&wl->lock, irqflag);
+ if (key_index)
+ key_index--;
+ else
+ key_index = wl->current_key;
+
+ memset(ext, 0, sizeof(struct iw_encode_ext));
+ switch (wl->group_cipher_method) {
+ case GELIC_WL_CIPHER_WEP:
+ ext->alg = IW_ENCODE_ALG_WEP;
+ enc->flags |= IW_ENCODE_ENABLED;
+ break;
+ case GELIC_WL_CIPHER_TKIP:
+ ext->alg = IW_ENCODE_ALG_TKIP;
+ enc->flags |= IW_ENCODE_ENABLED;
+ break;
+ case GELIC_WL_CIPHER_AES:
+ ext->alg = IW_ENCODE_ALG_CCMP;
+ enc->flags |= IW_ENCODE_ENABLED;
+ break;
+ case GELIC_WL_CIPHER_NONE:
+ default:
+ ext->alg = IW_ENCODE_ALG_NONE;
+ enc->flags |= IW_ENCODE_NOKEY;
+ break;
+ }
+
+ if (!(enc->flags & IW_ENCODE_NOKEY)) {
+ if (max_key_len < wl->key_len[key_index]) {
+ ret = -E2BIG;
+ goto out;
+ }
+ if (test_bit(key_index, &wl->key_enabled))
+ memcpy(ext->key, wl->key[key_index],
+ wl->key_len[key_index]);
+ else
+ pr_debug("%s: disabled key requested ix=%d\n",
+ __func__, key_index);
+ }
+out:
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ pr_debug("%s: -> \n", __func__);
+ return ret;
+}
+/* SIOC{S,G}IWMODE */
+static int gelic_wl_set_mode(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ __u32 mode = data->mode;
+ int ret;
+
+ pr_debug("%s: <- \n", __func__);
+ if (mode == IW_MODE_INFRA)
+ ret = 0;
+ else
+ ret = -EOPNOTSUPP;
+ pr_debug("%s: -> %d\n", __func__, ret);
+ return ret;
+}
+
+static int gelic_wl_get_mode(struct net_device *netdev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ __u32 *mode = &data->mode;
+ pr_debug("%s: <- \n", __func__);
+ *mode = IW_MODE_INFRA;
+ pr_debug("%s: ->\n", __func__);
+ return 0;
+}
+
+/* SIOCIWFIRSTPRIV */
+static int hex2bin(u8 *str, u8 *bin, unsigned int len)
+{
+ unsigned int i;
+ static unsigned char *hex = "0123456789ABCDEF";
+ unsigned char *p, *q;
+ u8 tmp;
+
+ if (len != WPA_PSK_LEN * 2)
+ return -EINVAL;
+
+ for (i = 0; i < WPA_PSK_LEN * 2; i += 2) {
+ p = strchr(hex, toupper(str[i]));
+ q = strchr(hex, toupper(str[i + 1]));
+ if (!p || !q) {
+ pr_info("%s: unconvertible PSK digit=%d\n",
+ __func__, i);
+ return -EINVAL;
+ }
+ tmp = ((p - hex) << 4) + (q - hex);
+ *bin++ = tmp;
+ }
+ return 0;
+};
+
+static int gelic_wl_priv_set_psk(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(net_dev));
+ unsigned int len;
+ unsigned int irqflag;
+ int ret = 0;
+
+ pr_debug("%s:<- len=%d\n", __func__, data->data.length);
+ len = data->data.length - 1;
+ if (len <= 2)
+ return -EINVAL;
+
+ spin_lock_irqsave(&wl->lock, irqflag);
+ if (extra[0] == '"' && extra[len - 1] == '"') {
+ pr_debug("%s: passphrase mode\n", __func__);
+ /* pass phrase */
+ if (GELIC_WL_EURUS_PSK_MAX_LEN < (len - 2)) {
+ pr_info("%s: passphrase too long\n", __func__);
+ ret = -E2BIG;
+ goto out;
+ }
+ memset(wl->psk, 0, sizeof(wl->psk));
+ wl->psk_len = len - 2;
+ memcpy(wl->psk, &(extra[1]), wl->psk_len);
+ wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE;
+ } else {
+ ret = hex2bin(extra, wl->psk, len);
+ if (ret)
+ goto out;
+ wl->psk_len = WPA_PSK_LEN;
+ wl->psk_type = GELIC_EURUS_WPA_PSK_BIN;
+ }
+ set_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat);
+out:
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ pr_debug("%s:->\n", __func__);
+ return ret;
+}
+
+static int gelic_wl_priv_get_psk(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(net_dev));
+ char *p;
+ unsigned int irqflag;
+ unsigned int i;
+
+ pr_debug("%s:<-\n", __func__);
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ spin_lock_irqsave(&wl->lock, irqflag);
+ p = extra;
+ if (test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat)) {
+ if (wl->psk_type == GELIC_EURUS_WPA_PSK_BIN) {
+ for (i = 0; i < wl->psk_len; i++) {
+ sprintf(p, "%02xu", wl->psk[i]);
+ p += 2;
+ }
+ *p = '\0';
+ data->data.length = wl->psk_len * 2;
+ } else {
+ *p++ = '"';
+ memcpy(p, wl->psk, wl->psk_len);
+ p += wl->psk_len;
+ *p++ = '"';
+ *p = '\0';
+ data->data.length = wl->psk_len + 2;
+ }
+ } else
+ /* no psk set */
+ data->data.length = 0;
+ spin_unlock_irqrestore(&wl->lock, irqflag);
+ pr_debug("%s:-> %d\n", __func__, data->data.length);
+ return 0;
+}
+
+/* SIOCGIWNICKN */
+static int gelic_wl_get_nick(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data, char *extra)
+{
+ strcpy(extra, "gelic_wl");
+ data->data.length = strlen(extra);
+ data->data.flags = 1;
+ return 0;
+}
+
+
+/* --- */
+
+static struct iw_statistics *gelic_wl_get_wireless_stats(
+ struct net_device *netdev)
+{
+
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ struct gelic_eurus_cmd *cmd;
+ struct iw_statistics *is;
+ struct gelic_eurus_rssi_info *rssi;
+
+ pr_debug("%s: <-\n", __func__);
+
+ is = &wl->iwstat;
+ memset(is, 0, sizeof(*is));
+ cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_RSSI_CFG,
+ wl->buf, sizeof(*rssi));
+ if (cmd && !cmd->status && !cmd->cmd_status) {
+ rssi = wl->buf;
+ is->qual.level = be16_to_cpu(rssi->rssi);
+ is->qual.updated = IW_QUAL_LEVEL_UPDATED |
+ IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID;
+ } else
+ /* not associated */
+ is->qual.updated = IW_QUAL_ALL_INVALID;
+
+ kfree(cmd);
+ pr_debug("%s: ->\n", __func__);
+ return is;
+}
+
+/*
+ * scanning helpers
+ */
+static int gelic_wl_start_scan(struct gelic_wl_info *wl, int always_scan)
+{
+ struct gelic_eurus_cmd *cmd;
+ int ret = 0;
+
+ pr_debug("%s: <- always=%d\n", __func__, always_scan);
+ if (down_interruptible(&wl->scan_lock))
+ return -ERESTARTSYS;
+
+ /*
+ * If already a scan in progress, do not trigger more
+ */
+ if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING) {
+ pr_debug("%s: scanning now\n", __func__);
+ goto out;
+ }
+
+ init_completion(&wl->scan_done);
+ /*
+ * If we have already a bss list, don't try to get new
+ */
+ if (!always_scan && wl->scan_stat == GELIC_WL_SCAN_STAT_GOT_LIST) {
+ pr_debug("%s: already has the list\n", __func__);
+ complete(&wl->scan_done);
+ goto out;
+ }
+ /*
+ * issue start scan request
+ */
+ wl->scan_stat = GELIC_WL_SCAN_STAT_SCANNING;
+ cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_START_SCAN,
+ NULL, 0);
+ if (!cmd || cmd->status || cmd->cmd_status) {
+ wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
+ complete(&wl->scan_done);
+ ret = -ENOMEM;
+ goto out;
+ }
+ kfree(cmd);
+out:
+ up(&wl->scan_lock);
+ pr_debug("%s: ->\n", __func__);
+ return ret;
+}
+
+/*
+ * retrieve scan result from the chip (hypervisor)
+ * this function is invoked by schedule work.
+ */
+static void gelic_wl_scan_complete_event(struct gelic_wl_info *wl)
+{
+ struct gelic_eurus_cmd *cmd = NULL;
+ struct gelic_wl_scan_info *target, *tmp;
+ struct gelic_wl_scan_info *oldest = NULL;
+ struct gelic_eurus_scan_info *scan_info;
+ unsigned int scan_info_size;
+ union iwreq_data data;
+ unsigned long this_time = jiffies;
+ unsigned int data_len, i, found, r;
+ DECLARE_MAC_BUF(mac);
+
+ pr_debug("%s:start\n", __func__);
+ down(&wl->scan_lock);
+
+ if (wl->scan_stat != GELIC_WL_SCAN_STAT_SCANNING) {
+ /*
+ * stop() may be called while scanning, ignore result
+ */
+ pr_debug("%s: scan complete when stat != scanning(%d)\n",
+ __func__, wl->scan_stat);
+ goto out;
+ }
+
+ cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_GET_SCAN,
+ wl->buf, PAGE_SIZE);
+ if (!cmd || cmd->status || cmd->cmd_status) {
+ wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
+ pr_info("%s:cmd failed\n", __func__);
+ kfree(cmd);
+ goto out;
+ }
+ data_len = cmd->size;
+ pr_debug("%s: data_len = %d\n", __func__, data_len);
+ kfree(cmd);
+
+ /* OK, bss list retrieved */
+ wl->scan_stat = GELIC_WL_SCAN_STAT_GOT_LIST;
+
+ /* mark all entries are old */
+ list_for_each_entry_safe(target, tmp, &wl->network_list, list) {
+ target->valid = 0;
+ /* expire too old entries */
+ if (time_before(target->last_scanned + wl->scan_age,
+ this_time)) {
+ kfree(target->hwinfo);
+ target->hwinfo = NULL;
+ list_move_tail(&target->list, &wl->network_free_list);
+ }
+ }
+
+ /* put them in the newtork_list */
+ scan_info = wl->buf;
+ scan_info_size = 0;
+ i = 0;
+ while (scan_info_size < data_len) {
+ pr_debug("%s:size=%d bssid=%s scan_info=%p\n", __func__,
+ be16_to_cpu(scan_info->size),
+ print_mac(mac, &scan_info->bssid[2]), scan_info);
+ found = 0;
+ oldest = NULL;
+ list_for_each_entry(target, &wl->network_list, list) {
+ if (!compare_ether_addr(&target->hwinfo->bssid[2],
+ &scan_info->bssid[2])) {
+ found = 1;
+ pr_debug("%s: same BBS found scanned list\n",
+ __func__);
+ break;
+ }
+ if (!oldest ||
+ (target->last_scanned < oldest->last_scanned))
+ oldest = target;
+ }
+
+ if (!found) {
+ /* not found in the list */
+ if (list_empty(&wl->network_free_list)) {
+ /* expire oldest */
+ target = oldest;
+ } else {
+ target = list_entry(wl->network_free_list.next,
+ struct gelic_wl_scan_info,
+ list);
+ }
+ }
+
+ /* update the item */
+ target->last_scanned = this_time;
+ target->valid = 1;
+ target->eurus_index = i;
+ kfree(target->hwinfo);
+ target->hwinfo = kzalloc(be16_to_cpu(scan_info->size),
+ GFP_KERNEL);
+ if (!target->hwinfo) {
+ pr_info("%s: kzalloc failed\n", __func__);
+ i++;
+ scan_info_size += be16_to_cpu(scan_info->size);
+ scan_info = (void *)scan_info +
+ be16_to_cpu(scan_info->size);
+ continue;
+ }
+ /* copy hw scan info */
+ memcpy(target->hwinfo, scan_info, scan_info->size);
+ target->essid_len = strnlen(scan_info->essid,
+ sizeof(scan_info->essid));
+ target->rate_len = 0;
+ for (r = 0; r < MAX_RATES_LENGTH; r++)
+ if (scan_info->rate[r])
+ target->rate_len++;
+ if (8 < target->rate_len)
+ pr_info("%s: AP returns %d rates\n", __func__,
+ target->rate_len);
+ target->rate_ext_len = 0;
+ for (r = 0; r < MAX_RATES_EX_LENGTH; r++)
+ if (scan_info->ext_rate[r])
+ target->rate_ext_len++;
+ list_move_tail(&target->list, &wl->network_list);
+ /* bump pointer */
+ i++;
+ scan_info_size += be16_to_cpu(scan_info->size);
+ scan_info = (void *)scan_info + be16_to_cpu(scan_info->size);
+ }
+ memset(&data, 0, sizeof(data));
+ wireless_send_event(port_to_netdev(wl_port(wl)), SIOCGIWSCAN, &data,
+ NULL);
+out:
+ complete(&wl->scan_done);
+ up(&wl->scan_lock);
+ pr_debug("%s:end\n", __func__);
+}
+
+/*
+ * Select an appropriate bss from current scan list regarding
+ * current settings from userspace.
+ * The caller must hold wl->scan_lock,
+ * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST
+ */
+static void update_best(struct gelic_wl_scan_info **best,
+ struct gelic_wl_scan_info *candid,
+ int *best_weight,
+ int *weight)
+{
+ if (*best_weight < ++(*weight)) {
+ *best_weight = *weight;
+ *best = candid;
+ }
+}
+
+static
+struct gelic_wl_scan_info *gelic_wl_find_best_bss(struct gelic_wl_info *wl)
+{
+ struct gelic_wl_scan_info *scan_info;
+ struct gelic_wl_scan_info *best_bss;
+ int weight, best_weight;
+ u16 security;
+ DECLARE_MAC_BUF(mac);
+
+ pr_debug("%s: <-\n", __func__);
+
+ best_bss = NULL;
+ best_weight = 0;
+
+ list_for_each_entry(scan_info, &wl->network_list, list) {
+ pr_debug("%s: station %p\n", __func__, scan_info);
+
+ if (!scan_info->valid) {
+ pr_debug("%s: station invalid\n", __func__);
+ continue;
+ }
+
+ /* If bss specified, check it only */
+ if (test_bit(GELIC_WL_STAT_BSSID_SET, &wl->stat)) {
+ if (!compare_ether_addr(&scan_info->hwinfo->bssid[2],
+ wl->bssid)) {
+ best_bss = scan_info;
+ pr_debug("%s: bssid matched\n", __func__);
+ break;
+ } else {
+ pr_debug("%s: bssid unmached\n", __func__);
+ continue;
+ }
+ }
+
+ weight = 0;
+
+ /* security */
+ security = be16_to_cpu(scan_info->hwinfo->security) &
+ GELIC_EURUS_SCAN_SEC_MASK;
+ if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) {
+ if (security == GELIC_EURUS_SCAN_SEC_WPA2)
+ update_best(&best_bss, scan_info,
+ &best_weight, &weight);
+ else
+ continue;
+ } else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA) {
+ if (security == GELIC_EURUS_SCAN_SEC_WPA)
+ update_best(&best_bss, scan_info,
+ &best_weight, &weight);
+ else
+ continue;
+ } else if (wl->wpa_level == GELIC_WL_WPA_LEVEL_NONE &&
+ wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
+ if (security == GELIC_EURUS_SCAN_SEC_WEP)
+ update_best(&best_bss, scan_info,
+ &best_weight, &weight);
+ else
+ continue;
+ }
+
+ /* If ESSID is set, check it */
+ if (test_bit(GELIC_WL_STAT_ESSID_SET, &wl->stat)) {
+ if ((scan_info->essid_len == wl->essid_len) &&
+ !strncmp(wl->essid,
+ scan_info->hwinfo->essid,
+ scan_info->essid_len))
+ update_best(&best_bss, scan_info,
+ &best_weight, &weight);
+ else
+ continue;
+ }
+ }
+
+#ifdef DEBUG
+ pr_debug("%s: -> bss=%p\n", __func__, best_bss);
+ if (best_bss) {
+ pr_debug("%s:addr=%s\n", __func__,
+ print_mac(mac, &best_bss->hwinfo->bssid[2]));
+ }
+#endif
+ return best_bss;
+}
+
+/*
+ * Setup WEP configuration to the chip
+ * The caller must hold wl->scan_lock,
+ * and on the state of wl->scan_state == GELIC_WL_SCAN_GOT_LIST
+ */
+static int gelic_wl_do_wep_setup(struct gelic_wl_info *wl)
+{
+ unsigned int i;
+ struct gelic_eurus_wep_cfg *wep;
+ struct gelic_eurus_cmd *cmd;
+ int wep104 = 0;
+ int have_key = 0;
+ int ret = 0;
+
+ pr_debug("%s: <-\n", __func__);
+ /* we can assume no one should uses the buffer */
+ wep = wl->buf;
+ memset(wep, 0, sizeof(*wep));
+
+ if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
+ pr_debug("%s: WEP mode\n", __func__);
+ for (i = 0; i < GELIC_WEP_KEYS; i++) {
+ if (!test_bit(i, &wl->key_enabled))
+ continue;
+
+ pr_debug("%s: key#%d enabled\n", __func__, i);
+ have_key = 1;
+ if (wl->key_len[i] == 13)
+ wep104 = 1;
+ else if (wl->key_len[i] != 5) {
+ pr_info("%s: wrong wep key[%d]=%d\n",
+ __func__, i, wl->key_len[i]);
+ ret = -EINVAL;
+ goto out;
+ }
+ memcpy(wep->key[i], wl->key[i], wl->key_len[i]);
+ }
+
+ if (!have_key) {
+ pr_info("%s: all wep key disabled\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (wep104) {
+ pr_debug("%s: 104bit key\n", __func__);
+ wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_104BIT);
+ } else {
+ pr_debug("%s: 40bit key\n", __func__);
+ wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_40BIT);
+ }
+ } else {
+ pr_debug("%s: NO encryption\n", __func__);
+ wep->security = cpu_to_be16(GELIC_EURUS_WEP_SEC_NONE);
+ }
+
+ /* issue wep setup */
+ cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WEP_CFG,
+ wep, sizeof(*wep));
+ if (!cmd)
+ ret = -ENOMEM;
+ else if (cmd->status || cmd->cmd_status)
+ ret = -ENXIO;
+
+ kfree(cmd);
+out:
+ pr_debug("%s: ->\n", __func__);
+ return ret;
+}
+
+#ifdef DEBUG
+static const char *wpasecstr(enum gelic_eurus_wpa_security sec)
+{
+ switch (sec) {
+ case GELIC_EURUS_WPA_SEC_NONE:
+ return "NONE";
+ break;
+ case GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP:
+ return "WPA_TKIP_TKIP";
+ break;
+ case GELIC_EURUS_WPA_SEC_WPA_TKIP_AES:
+ return "WPA_TKIP_AES";
+ break;
+ case GELIC_EURUS_WPA_SEC_WPA_AES_AES:
+ return "WPA_AES_AES";
+ break;
+ case GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP:
+ return "WPA2_TKIP_TKIP";
+ break;
+ case GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES:
+ return "WPA2_TKIP_AES";
+ break;
+ case GELIC_EURUS_WPA_SEC_WPA2_AES_AES:
+ return "WPA2_AES_AES";
+ break;
+ }
+ return "";
+};
+#endif
+
+static int gelic_wl_do_wpa_setup(struct gelic_wl_info *wl)
+{
+ struct gelic_eurus_wpa_cfg *wpa;
+ struct gelic_eurus_cmd *cmd;
+ u16 security;
+ int ret = 0;
+
+ pr_debug("%s: <-\n", __func__);
+ /* we can assume no one should uses the buffer */
+ wpa = wl->buf;
+ memset(wpa, 0, sizeof(*wpa));
+
+ if (!test_bit(GELIC_WL_STAT_WPA_PSK_SET, &wl->stat))
+ pr_info("%s: PSK not configured yet\n", __func__);
+
+ /* copy key */
+ memcpy(wpa->psk, wl->psk, wl->psk_len);
+
+ /* set security level */
+ if (wl->wpa_level == GELIC_WL_WPA_LEVEL_WPA2) {
+ if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) {
+ security = GELIC_EURUS_WPA_SEC_WPA2_AES_AES;
+ } else {
+ if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES &&
+ precise_ie())
+ security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES;
+ else
+ security = GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP;
+ }
+ } else {
+ if (wl->group_cipher_method == GELIC_WL_CIPHER_AES) {
+ security = GELIC_EURUS_WPA_SEC_WPA_AES_AES;
+ } else {
+ if (wl->pairwise_cipher_method == GELIC_WL_CIPHER_AES &&
+ precise_ie())
+ security = GELIC_EURUS_WPA_SEC_WPA_TKIP_AES;
+ else
+ security = GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP;
+ }
+ }
+ wpa->security = cpu_to_be16(security);
+
+ /* PSK type */
+ wpa->psk_type = cpu_to_be16(wl->psk_type);
+#ifdef DEBUG
+ pr_debug("%s: sec=%s psktype=%s\nn", __func__,
+ wpasecstr(wpa->security),
+ (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ?
+ "BIN" : "passphrase");
+#if 0
+ /*
+ * don't enable here if you plan to submit
+ * the debug log because this dumps your precious
+ * passphrase/key.
+ */
+ pr_debug("%s: psk=%s\n",
+ (wpa->psk_type == GELIC_EURUS_WPA_PSK_BIN) ?
+ (char *)"N/A" : (char *)wpa->psk);
+#endif
+#endif
+ /* issue wpa setup */
+ cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_WPA_CFG,
+ wpa, sizeof(*wpa));
+ if (!cmd)
+ ret = -ENOMEM;
+ else if (cmd->status || cmd->cmd_status)
+ ret = -ENXIO;
+ kfree(cmd);
+ pr_debug("%s: --> %d\n", __func__, ret);
+ return ret;
+}
+
+/*
+ * Start association. caller must hold assoc_stat_lock
+ */
+static int gelic_wl_associate_bss(struct gelic_wl_info *wl,
+ struct gelic_wl_scan_info *bss)
+{
+ struct gelic_eurus_cmd *cmd;
+ struct gelic_eurus_common_cfg *common;
+ int ret = 0;
+ unsigned long rc;
+
+ pr_debug("%s: <-\n", __func__);
+
+ /* do common config */
+ common = wl->buf;
+ memset(common, 0, sizeof(*common));
+ common->bss_type = cpu_to_be16(GELIC_EURUS_BSS_INFRA);
+ common->op_mode = cpu_to_be16(GELIC_EURUS_OPMODE_11BG);
+
+ common->scan_index = cpu_to_be16(bss->eurus_index);
+ switch (wl->auth_method) {
+ case GELIC_EURUS_AUTH_OPEN:
+ common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_OPEN);
+ break;
+ case GELIC_EURUS_AUTH_SHARED:
+ common->auth_method = cpu_to_be16(GELIC_EURUS_AUTH_SHARED);
+ break;
+ }
+
+#ifdef DEBUG
+ scan_list_dump(wl);
+#endif
+ pr_debug("%s: common cfg index=%d bsstype=%d auth=%d\n", __func__,
+ be16_to_cpu(common->scan_index),
+ be16_to_cpu(common->bss_type),
+ be16_to_cpu(common->auth_method));
+
+ cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_SET_COMMON_CFG,
+ common, sizeof(*common));
+ if (!cmd || cmd->status || cmd->cmd_status) {
+ ret = -ENOMEM;
+ kfree(cmd);
+ goto out;
+ }
+ kfree(cmd);
+
+ /* WEP/WPA */
+ switch (wl->wpa_level) {
+ case GELIC_WL_WPA_LEVEL_NONE:
+ /* If WEP or no security, setup WEP config */
+ ret = gelic_wl_do_wep_setup(wl);
+ break;
+ case GELIC_WL_WPA_LEVEL_WPA:
+ case GELIC_WL_WPA_LEVEL_WPA2:
+ ret = gelic_wl_do_wpa_setup(wl);
+ break;
+ };
+
+ if (ret) {
+ pr_debug("%s: WEP/WPA setup failed %d\n", __func__,
+ ret);
+ }
+
+ /* start association */
+ init_completion(&wl->assoc_done);
+ wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATING;
+ cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_ASSOC,
+ NULL, 0);
+ if (!cmd || cmd->status || cmd->cmd_status) {
+ pr_debug("%s: assoc request failed\n", __func__);
+ wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
+ kfree(cmd);
+ ret = -ENOMEM;
+ gelic_wl_send_iwap_event(wl, NULL);
+ goto out;
+ }
+ kfree(cmd);
+
+ /* wait for connected event */
+ rc = wait_for_completion_timeout(&wl->assoc_done, HZ * 4);/*FIXME*/
+
+ if (!rc) {
+ /* timeouted. Maybe key or cyrpt mode is wrong */
+ pr_info("%s: connect timeout \n", __func__);
+ cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC,
+ NULL, 0);
+ kfree(cmd);
+ wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
+ gelic_wl_send_iwap_event(wl, NULL);
+ ret = -ENXIO;
+ } else {
+ wl->assoc_stat = GELIC_WL_ASSOC_STAT_ASSOCIATED;
+ /* copy bssid */
+ memcpy(wl->active_bssid, &bss->hwinfo->bssid[2], ETH_ALEN);
+
+ /* send connect event */
+ gelic_wl_send_iwap_event(wl, wl->active_bssid);
+ pr_info("%s: connected\n", __func__);
+ }
+out:
+ pr_debug("%s: ->\n", __func__);
+ return ret;
+}
+
+/*
+ * connected event
+ */
+static void gelic_wl_connected_event(struct gelic_wl_info *wl,
+ u64 event)
+{
+ u64 desired_event = 0;
+
+ switch (wl->wpa_level) {
+ case GELIC_WL_WPA_LEVEL_NONE:
+ desired_event = GELIC_LV1_WL_EVENT_CONNECTED;
+ break;
+ case GELIC_WL_WPA_LEVEL_WPA:
+ case GELIC_WL_WPA_LEVEL_WPA2:
+ desired_event = GELIC_LV1_WL_EVENT_WPA_CONNECTED;
+ break;
+ }
+
+ if (desired_event == event) {
+ pr_debug("%s: completed \n", __func__);
+ complete(&wl->assoc_done);
+ netif_carrier_on(port_to_netdev(wl_port(wl)));
+ } else
+ pr_debug("%s: event %#lx under wpa\n",
+ __func__, event);
+}
+
+/*
+ * disconnect event
+ */
+static void gelic_wl_disconnect_event(struct gelic_wl_info *wl,
+ u64 event)
+{
+ struct gelic_eurus_cmd *cmd;
+ int lock;
+
+ /*
+ * If we fall here in the middle of association,
+ * associate_bss() should be waiting for complation of
+ * wl->assoc_done.
+ * As it waits with timeout, just leave assoc_done
+ * uncompleted, then it terminates with timeout
+ */
+ if (down_trylock(&wl->assoc_stat_lock)) {
+ pr_debug("%s: already locked\n", __func__);
+ lock = 0;
+ } else {
+ pr_debug("%s: obtain lock\n", __func__);
+ lock = 1;
+ }
+
+ cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0);
+ kfree(cmd);
+
+ /* send disconnected event to the supplicant */
+ if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
+ gelic_wl_send_iwap_event(wl, NULL);
+
+ wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
+ netif_carrier_off(port_to_netdev(wl_port(wl)));
+
+ if (lock)
+ up(&wl->assoc_stat_lock);
+}
+/*
+ * event worker
+ */
+#ifdef DEBUG
+static const char *eventstr(enum gelic_lv1_wl_event event)
+{
+ static char buf[32];
+ char *ret;
+ if (event & GELIC_LV1_WL_EVENT_DEVICE_READY)
+ ret = "EURUS_READY";
+ else if (event & GELIC_LV1_WL_EVENT_SCAN_COMPLETED)
+ ret = "SCAN_COMPLETED";
+ else if (event & GELIC_LV1_WL_EVENT_DEAUTH)
+ ret = "DEAUTH";
+ else if (event & GELIC_LV1_WL_EVENT_BEACON_LOST)
+ ret = "BEACON_LOST";
+ else if (event & GELIC_LV1_WL_EVENT_CONNECTED)
+ ret = "CONNECTED";
+ else if (event & GELIC_LV1_WL_EVENT_WPA_CONNECTED)
+ ret = "WPA_CONNECTED";
+ else if (event & GELIC_LV1_WL_EVENT_WPA_ERROR)
+ ret = "WPA_ERROR";
+ else {
+ sprintf(buf, "Unknown(%#x)", event);
+ ret = buf;
+ }
+ return ret;
+}
+#else
+static const char *eventstr(enum gelic_lv1_wl_event event)
+{
+ return NULL;
+}
+#endif
+static void gelic_wl_event_worker(struct work_struct *work)
+{
+ struct gelic_wl_info *wl;
+ struct gelic_port *port;
+ u64 event, tmp;
+ int status;
+
+ pr_debug("%s:start\n", __func__);
+ wl = container_of(work, struct gelic_wl_info, event_work.work);
+ port = wl_port(wl);
+ while (1) {
+ status = lv1_net_control(bus_id(port->card), dev_id(port->card),
+ GELIC_LV1_GET_WLAN_EVENT, 0, 0, 0,
+ &event, &tmp);
+ if (status) {
+ if (status != LV1_NO_ENTRY)
+ pr_debug("%s:wlan event failed %d\n",
+ __func__, status);
+ /* got all events */
+ pr_debug("%s:end\n", __func__);
+ return;
+ }
+ pr_debug("%s: event=%s\n", __func__, eventstr(event));
+ switch (event) {
+ case GELIC_LV1_WL_EVENT_SCAN_COMPLETED:
+ gelic_wl_scan_complete_event(wl);
+ break;
+ case GELIC_LV1_WL_EVENT_BEACON_LOST:
+ case GELIC_LV1_WL_EVENT_DEAUTH:
+ gelic_wl_disconnect_event(wl, event);
+ break;
+ case GELIC_LV1_WL_EVENT_CONNECTED:
+ case GELIC_LV1_WL_EVENT_WPA_CONNECTED:
+ gelic_wl_connected_event(wl, event);
+ break;
+ default:
+ break;
+ }
+ } /* while */
+}
+/*
+ * association worker
+ */
+static void gelic_wl_assoc_worker(struct work_struct *work)
+{
+ struct gelic_wl_info *wl;
+
+ struct gelic_wl_scan_info *best_bss;
+ int ret;
+
+ wl = container_of(work, struct gelic_wl_info, assoc_work.work);
+
+ down(&wl->assoc_stat_lock);
+
+ if (wl->assoc_stat != GELIC_WL_ASSOC_STAT_DISCONN)
+ goto out;
+
+ ret = gelic_wl_start_scan(wl, 0);
+ if (ret == -ERESTARTSYS) {
+ pr_debug("%s: scan start failed association\n", __func__);
+ schedule_delayed_work(&wl->assoc_work, HZ/10); /*FIXME*/
+ goto out;
+ } else if (ret) {
+ pr_info("%s: scan prerequisite failed\n", __func__);
+ goto out;
+ }
+
+ /*
+ * Wait for bss scan completion
+ * If we have scan list already, gelic_wl_start_scan()
+ * returns OK and raises the complete. Thus,
+ * it's ok to wait unconditionally here
+ */
+ wait_for_completion(&wl->scan_done);
+
+ pr_debug("%s: scan done\n", __func__);
+ down(&wl->scan_lock);
+ if (wl->scan_stat != GELIC_WL_SCAN_STAT_GOT_LIST) {
+ gelic_wl_send_iwap_event(wl, NULL);
+ pr_info("%s: no scan list. association failed\n", __func__);
+ goto scan_lock_out;
+ }
+
+ /* find best matching bss */
+ best_bss = gelic_wl_find_best_bss(wl);
+ if (!best_bss) {
+ gelic_wl_send_iwap_event(wl, NULL);
+ pr_info("%s: no bss matched. association failed\n", __func__);
+ goto scan_lock_out;
+ }
+
+ /* ok, do association */
+ ret = gelic_wl_associate_bss(wl, best_bss);
+ if (ret)
+ pr_info("%s: association failed %d\n", __func__, ret);
+scan_lock_out:
+ up(&wl->scan_lock);
+out:
+ up(&wl->assoc_stat_lock);
+}
+/*
+ * Interrupt handler
+ * Called from the ethernet interrupt handler
+ * Processes wireless specific virtual interrupts only
+ */
+void gelic_wl_interrupt(struct net_device *netdev, u64 status)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+
+ if (status & GELIC_CARD_WLAN_COMMAND_COMPLETED) {
+ pr_debug("%s:cmd complete\n", __func__);
+ complete(&wl->cmd_done_intr);
+ }
+
+ if (status & GELIC_CARD_WLAN_EVENT_RECEIVED) {
+ pr_debug("%s:event received\n", __func__);
+ queue_delayed_work(wl->event_queue, &wl->event_work, 0);
+ }
+}
+
+/*
+ * driver helpers
+ */
+#define IW_IOCTL(n) [(n) - SIOCSIWCOMMIT]
+static const iw_handler gelic_wl_wext_handler[] =
+{
+ IW_IOCTL(SIOCGIWNAME) = gelic_wl_get_name,
+ IW_IOCTL(SIOCGIWRANGE) = gelic_wl_get_range,
+ IW_IOCTL(SIOCSIWSCAN) = gelic_wl_set_scan,
+ IW_IOCTL(SIOCGIWSCAN) = gelic_wl_get_scan,
+ IW_IOCTL(SIOCSIWAUTH) = gelic_wl_set_auth,
+ IW_IOCTL(SIOCGIWAUTH) = gelic_wl_get_auth,
+ IW_IOCTL(SIOCSIWESSID) = gelic_wl_set_essid,
+ IW_IOCTL(SIOCGIWESSID) = gelic_wl_get_essid,
+ IW_IOCTL(SIOCSIWENCODE) = gelic_wl_set_encode,
+ IW_IOCTL(SIOCGIWENCODE) = gelic_wl_get_encode,
+ IW_IOCTL(SIOCSIWAP) = gelic_wl_set_ap,
+ IW_IOCTL(SIOCGIWAP) = gelic_wl_get_ap,
+ IW_IOCTL(SIOCSIWENCODEEXT) = gelic_wl_set_encodeext,
+ IW_IOCTL(SIOCGIWENCODEEXT) = gelic_wl_get_encodeext,
+ IW_IOCTL(SIOCSIWMODE) = gelic_wl_set_mode,
+ IW_IOCTL(SIOCGIWMODE) = gelic_wl_get_mode,
+ IW_IOCTL(SIOCGIWNICKN) = gelic_wl_get_nick,
+};
+
+static struct iw_priv_args gelic_wl_private_args[] =
+{
+ {
+ .cmd = GELIC_WL_PRIV_SET_PSK,
+ .set_args = IW_PRIV_TYPE_CHAR |
+ (GELIC_WL_EURUS_PSK_MAX_LEN + 2),
+ .name = "set_psk"
+ },
+ {
+ .cmd = GELIC_WL_PRIV_GET_PSK,
+ .get_args = IW_PRIV_TYPE_CHAR |
+ (GELIC_WL_EURUS_PSK_MAX_LEN + 2),
+ .name = "get_psk"
+ }
+};
+
+static const iw_handler gelic_wl_private_handler[] =
+{
+ gelic_wl_priv_set_psk,
+ gelic_wl_priv_get_psk,
+};
+
+static const struct iw_handler_def gelic_wl_wext_handler_def = {
+ .num_standard = ARRAY_SIZE(gelic_wl_wext_handler),
+ .standard = gelic_wl_wext_handler,
+ .get_wireless_stats = gelic_wl_get_wireless_stats,
+ .num_private = ARRAY_SIZE(gelic_wl_private_handler),
+ .num_private_args = ARRAY_SIZE(gelic_wl_private_args),
+ .private = gelic_wl_private_handler,
+ .private_args = gelic_wl_private_args,
+};
+
+static struct net_device *gelic_wl_alloc(struct gelic_card *card)
+{
+ struct net_device *netdev;
+ struct gelic_port *port;
+ struct gelic_wl_info *wl;
+ unsigned int i;
+
+ pr_debug("%s:start\n", __func__);
+ netdev = alloc_etherdev(sizeof(struct gelic_port) +
+ sizeof(struct gelic_wl_info));
+ pr_debug("%s: netdev =%p card=%p \np", __func__, netdev, card);
+ if (!netdev)
+ return NULL;
+
+ port = netdev_priv(netdev);
+ port->netdev = netdev;
+ port->card = card;
+ port->type = GELIC_PORT_WIRELESS;
+
+ wl = port_wl(port);
+ pr_debug("%s: wl=%p port=%p\n", __func__, wl, port);
+
+ /* allocate scan list */
+ wl->networks = kzalloc(sizeof(struct gelic_wl_scan_info) *
+ GELIC_WL_BSS_MAX_ENT, GFP_KERNEL);
+
+ if (!wl->networks)
+ goto fail_bss;
+
+ wl->eurus_cmd_queue = create_singlethread_workqueue("gelic_cmd");
+ if (!wl->eurus_cmd_queue)
+ goto fail_cmd_workqueue;
+
+ wl->event_queue = create_singlethread_workqueue("gelic_event");
+ if (!wl->event_queue)
+ goto fail_event_workqueue;
+
+ INIT_LIST_HEAD(&wl->network_free_list);
+ INIT_LIST_HEAD(&wl->network_list);
+ for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++)
+ list_add_tail(&wl->networks[i].list,
+ &wl->network_free_list);
+ init_completion(&wl->cmd_done_intr);
+
+ INIT_DELAYED_WORK(&wl->event_work, gelic_wl_event_worker);
+ INIT_DELAYED_WORK(&wl->assoc_work, gelic_wl_assoc_worker);
+ init_MUTEX(&wl->scan_lock);
+ init_MUTEX(&wl->assoc_stat_lock);
+
+ init_completion(&wl->scan_done);
+ /* for the case that no scan request is issued and stop() is called */
+ complete(&wl->scan_done);
+
+ spin_lock_init(&wl->lock);
+
+ wl->scan_age = 5*HZ; /* FIXME */
+
+ /* buffer for receiving scanned list etc */
+ BUILD_BUG_ON(PAGE_SIZE <
+ sizeof(struct gelic_eurus_scan_info) *
+ GELIC_EURUS_MAX_SCAN);
+ wl->buf = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!wl->buf) {
+ pr_info("%s:buffer allocation failed\n", __func__);
+ goto fail_getpage;
+ }
+ pr_debug("%s:end\n", __func__);
+ return netdev;
+
+fail_getpage:
+ destroy_workqueue(wl->event_queue);
+fail_event_workqueue:
+ destroy_workqueue(wl->eurus_cmd_queue);
+fail_cmd_workqueue:
+ kfree(wl->networks);
+fail_bss:
+ free_netdev(netdev);
+ pr_debug("%s:end error\n", __func__);
+ return NULL;
+
+}
+
+static void gelic_wl_free(struct gelic_wl_info *wl)
+{
+ struct gelic_wl_scan_info *scan_info;
+ unsigned int i;
+
+ pr_debug("%s: <-\n", __func__);
+
+ pr_debug("%s: destroy queues\n", __func__);
+ destroy_workqueue(wl->eurus_cmd_queue);
+ destroy_workqueue(wl->event_queue);
+
+ scan_info = wl->networks;
+ for (i = 0; i < GELIC_WL_BSS_MAX_ENT; i++, scan_info++)
+ kfree(scan_info->hwinfo);
+ kfree(wl->networks);
+
+ free_netdev(port_to_netdev(wl_port(wl)));
+
+ pr_debug("%s: ->\n", __func__);
+}
+
+static int gelic_wl_try_associate(struct net_device *netdev)
+{
+ struct gelic_wl_info *wl = port_wl(netdev_priv(netdev));
+ int ret = -1;
+ unsigned int i;
+
+ pr_debug("%s: <-\n", __func__);
+
+ /* check constraits for start association */
+ /* for no access restriction AP */
+ if (wl->group_cipher_method == GELIC_WL_CIPHER_NONE) {
+ if (test_bit(GELIC_WL_STAT_CONFIGURED,
+ &wl->stat))
+ goto do_associate;
+ else {
+ pr_debug("%s: no wep, not configured\n", __func__);
+ return ret;
+ }
+ }
+
+ /* for WEP, one of four keys should be set */
+ if (wl->group_cipher_method == GELIC_WL_CIPHER_WEP) {
+ /* one of keys set */
+ for (i = 0; i < GELIC_WEP_KEYS; i++) {
+ if (test_bit(i, &wl->key_enabled))
+ goto do_associate;
+ }
+ pr_debug("%s: WEP, but no key specified\n", __func__);
+ return ret;
+ }
+
+ /* for WPA[2], psk should be set */
+ if ((wl->group_cipher_method == GELIC_WL_CIPHER_TKIP) ||
+ (wl->group_cipher_method == GELIC_WL_CIPHER_AES)) {
+ if (test_bit(GELIC_WL_STAT_WPA_PSK_SET,
+ &wl->stat))
+ goto do_associate;
+ else {
+ pr_debug("%s: AES/TKIP, but PSK not configured\n",
+ __func__);
+ return ret;
+ }
+ }
+
+do_associate:
+ ret = schedule_delayed_work(&wl->assoc_work, 0);
+ pr_debug("%s: start association work %d\n", __func__, ret);
+ return ret;
+}
+
+/*
+ * netdev handlers
+ */
+static int gelic_wl_open(struct net_device *netdev)
+{
+ struct gelic_card *card = netdev_card(netdev);
+
+ pr_debug("%s:->%p\n", __func__, netdev);
+
+ gelic_card_up(card);
+
+ /* try to associate */
+ gelic_wl_try_associate(netdev);
+
+ netif_start_queue(netdev);
+
+ pr_debug("%s:<-\n", __func__);
+ return 0;
+}
+
+/*
+ * reset state machine
+ */
+static int gelic_wl_reset_state(struct gelic_wl_info *wl)
+{
+ struct gelic_wl_scan_info *target;
+ struct gelic_wl_scan_info *tmp;
+
+ /* empty scan list */
+ list_for_each_entry_safe(target, tmp, &wl->network_list, list) {
+ list_move_tail(&target->list, &wl->network_free_list);
+ }
+ wl->scan_stat = GELIC_WL_SCAN_STAT_INIT;
+
+ /* clear configuration */
+ wl->auth_method = GELIC_EURUS_AUTH_OPEN;
+ wl->group_cipher_method = GELIC_WL_CIPHER_NONE;
+ wl->pairwise_cipher_method = GELIC_WL_CIPHER_NONE;
+ wl->wpa_level = GELIC_WL_WPA_LEVEL_NONE;
+
+ wl->key_enabled = 0;
+ wl->current_key = 0;
+
+ wl->psk_type = GELIC_EURUS_WPA_PSK_PASSPHRASE;
+ wl->psk_len = 0;
+
+ wl->essid_len = 0;
+ memset(wl->essid, 0, sizeof(wl->essid));
+ memset(wl->bssid, 0, sizeof(wl->bssid));
+ memset(wl->active_bssid, 0, sizeof(wl->active_bssid));
+
+ wl->assoc_stat = GELIC_WL_ASSOC_STAT_DISCONN;
+
+ memset(&wl->iwstat, 0, sizeof(wl->iwstat));
+ /* all status bit clear */
+ wl->stat = 0;
+ return 0;
+}
+
+/*
+ * Tell eurus to terminate association
+ */
+static void gelic_wl_disconnect(struct net_device *netdev)
+{
+ struct gelic_port *port = netdev_priv(netdev);
+ struct gelic_wl_info *wl = port_wl(port);
+ struct gelic_eurus_cmd *cmd;
+
+ /*
+ * If scann process is running on chip,
+ * further requests will be rejected
+ */
+ if (wl->scan_stat == GELIC_WL_SCAN_STAT_SCANNING)
+ wait_for_completion_timeout(&wl->scan_done, HZ);
+
+ cmd = gelic_eurus_sync_cmd(wl, GELIC_EURUS_CMD_DISASSOC, NULL, 0);
+ kfree(cmd);
+ gelic_wl_send_iwap_event(wl, NULL);
+};
+
+static int gelic_wl_stop(struct net_device *netdev)
+{
+ struct gelic_port *port = netdev_priv(netdev);
+ struct gelic_wl_info *wl = port_wl(port);
+ struct gelic_card *card = netdev_card(netdev);
+
+ pr_debug("%s:<-\n", __func__);
+
+ /*
+ * Cancel pending association work.
+ * event work can run after netdev down
+ */
+ cancel_delayed_work(&wl->assoc_work);
+
+ if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
+ gelic_wl_disconnect(netdev);
+
+ /* reset our state machine */
+ gelic_wl_reset_state(wl);
+
+ netif_stop_queue(netdev);
+
+ gelic_card_down(card);
+
+ pr_debug("%s:->\n", __func__);
+ return 0;
+}
+
+/* -- */
+
+static struct ethtool_ops gelic_wl_ethtool_ops = {
+ .get_drvinfo = gelic_net_get_drvinfo,
+ .get_link = gelic_wl_get_link,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = ethtool_op_set_tx_csum,
+ .get_rx_csum = gelic_net_get_rx_csum,
+ .set_rx_csum = gelic_net_set_rx_csum,
+};
+
+static void gelic_wl_setup_netdev_ops(struct net_device *netdev)
+{
+ struct gelic_wl_info *wl;
+ wl = port_wl(netdev_priv(netdev));
+ BUG_ON(!wl);
+ netdev->open = &gelic_wl_open;
+ netdev->stop = &gelic_wl_stop;
+ netdev->hard_start_xmit = &gelic_net_xmit;
+ netdev->set_multicast_list = &gelic_net_set_multi;
+ netdev->change_mtu = &gelic_net_change_mtu;
+ netdev->wireless_data = &wl->wireless_data;
+ netdev->wireless_handlers = &gelic_wl_wext_handler_def;
+ /* tx watchdog */
+ netdev->tx_timeout = &gelic_net_tx_timeout;
+ netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT;
+
+ netdev->ethtool_ops = &gelic_wl_ethtool_ops;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ netdev->poll_controller = gelic_net_poll_controller;
+#endif
+}
+
+/*
+ * driver probe/remove
+ */
+int gelic_wl_driver_probe(struct gelic_card *card)
+{
+ int ret;
+ struct net_device *netdev;
+
+ pr_debug("%s:start\n", __func__);
+
+ if (ps3_compare_firmware_version(1, 6, 0) < 0)
+ return 0;
+ if (!card->vlan[GELIC_PORT_WIRELESS].tx)
+ return 0;
+
+ /* alloc netdevice for wireless */
+ netdev = gelic_wl_alloc(card);
+ if (!netdev)
+ return -ENOMEM;
+
+ /* setup net_device structure */
+ gelic_wl_setup_netdev_ops(netdev);
+
+ /* setup some of net_device and register it */
+ ret = gelic_net_setup_netdev(netdev, card);
+ if (ret)
+ goto fail_setup;
+ card->netdev[GELIC_PORT_WIRELESS] = netdev;
+
+ /* add enable wireless interrupt */
+ card->irq_mask |= GELIC_CARD_WLAN_EVENT_RECEIVED |
+ GELIC_CARD_WLAN_COMMAND_COMPLETED;
+ /* to allow wireless commands while both interfaces are down */
+ gelic_card_set_irq_mask(card, GELIC_CARD_WLAN_EVENT_RECEIVED |
+ GELIC_CARD_WLAN_COMMAND_COMPLETED);
+ pr_debug("%s:end\n", __func__);
+ return 0;
+
+fail_setup:
+ gelic_wl_free(port_wl(netdev_port(netdev)));
+
+ return ret;
+}
+
+int gelic_wl_driver_remove(struct gelic_card *card)
+{
+ struct gelic_wl_info *wl;
+ struct net_device *netdev;
+
+ pr_debug("%s:start\n", __func__);
+
+ if (ps3_compare_firmware_version(1, 6, 0) < 0)
+ return 0;
+ if (!card->vlan[GELIC_PORT_WIRELESS].tx)
+ return 0;
+
+ netdev = card->netdev[GELIC_PORT_WIRELESS];
+ wl = port_wl(netdev_priv(netdev));
+
+ /* if the interface was not up, but associated */
+ if (wl->assoc_stat == GELIC_WL_ASSOC_STAT_ASSOCIATED)
+ gelic_wl_disconnect(netdev);
+
+ complete(&wl->cmd_done_intr);
+
+ /* cancel all work queue */
+ cancel_delayed_work(&wl->assoc_work);
+ cancel_delayed_work(&wl->event_work);
+ flush_workqueue(wl->eurus_cmd_queue);
+ flush_workqueue(wl->event_queue);
+
+ unregister_netdev(netdev);
+
+ /* disable wireless interrupt */
+ pr_debug("%s: disable intr\n", __func__);
+ card->irq_mask &= ~(GELIC_CARD_WLAN_EVENT_RECEIVED |
+ GELIC_CARD_WLAN_COMMAND_COMPLETED);
+ /* free bss list, netdev*/
+ gelic_wl_free(wl);
+ pr_debug("%s:end\n", __func__);
+ return 0;
+}
--- /dev/null
+++ b/drivers/net/ps3_gelic_wireless.h
@@ -0,0 +1,329 @@
+/*
+ * PS3 gelic network driver.
+ *
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2007 Sony Corporation
+ *
+ * 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 version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _GELIC_WIRELESS_H
+#define _GELIC_WIRELESS_H
+
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+
+
+/* return value from GELIC_LV1_GET_WLAN_EVENT netcontrol */
+enum gelic_lv1_wl_event {
+ GELIC_LV1_WL_EVENT_DEVICE_READY = 0x01, /* Eurus ready */
+ GELIC_LV1_WL_EVENT_SCAN_COMPLETED = 0x02, /* Scan has completed */
+ GELIC_LV1_WL_EVENT_DEAUTH = 0x04, /* Deauthed by the AP */
+ GELIC_LV1_WL_EVENT_BEACON_LOST = 0x08, /* Beacon lost detected */
+ GELIC_LV1_WL_EVENT_CONNECTED = 0x10, /* Connected to AP */
+ GELIC_LV1_WL_EVENT_WPA_CONNECTED = 0x20, /* WPA connection */
+ GELIC_LV1_WL_EVENT_WPA_ERROR = 0x40, /* MIC error */
+};
+
+/* arguments for GELIC_LV1_POST_WLAN_COMMAND netcontrol */
+enum gelic_eurus_command {
+ GELIC_EURUS_CMD_ASSOC = 1, /* association start */
+ GELIC_EURUS_CMD_DISASSOC = 2, /* disassociate */
+ GELIC_EURUS_CMD_START_SCAN = 3, /* scan start */
+ GELIC_EURUS_CMD_GET_SCAN = 4, /* get scan result */
+ GELIC_EURUS_CMD_SET_COMMON_CFG = 5, /* set common config */
+ GELIC_EURUS_CMD_GET_COMMON_CFG = 6, /* set common config */
+ GELIC_EURUS_CMD_SET_WEP_CFG = 7, /* set WEP config */
+ GELIC_EURUS_CMD_GET_WEP_CFG = 8, /* get WEP config */
+ GELIC_EURUS_CMD_SET_WPA_CFG = 9, /* set WPA config */
+ GELIC_EURUS_CMD_GET_WPA_CFG = 10, /* get WPA config */
+ GELIC_EURUS_CMD_GET_RSSI_CFG = 11, /* get RSSI info. */
+ GELIC_EURUS_CMD_MAX_INDEX
+};
+
+/* for GELIC_EURUS_CMD_COMMON_CFG */
+enum gelic_eurus_bss_type {
+ GELIC_EURUS_BSS_INFRA = 0,
+ GELIC_EURUS_BSS_ADHOC = 1, /* not supported */
+};
+
+enum gelic_eurus_auth_method {
+ GELIC_EURUS_AUTH_OPEN = 0, /* FIXME: WLAN_AUTH_OPEN */
+ GELIC_EURUS_AUTH_SHARED = 1, /* not supported */
+};
+
+enum gelic_eurus_opmode {
+ GELIC_EURUS_OPMODE_11BG = 0, /* 802.11b/g */
+ GELIC_EURUS_OPMODE_11B = 1, /* 802.11b only */
+ GELIC_EURUS_OPMODE_11G = 2, /* 802.11g only */
+};
+
+struct gelic_eurus_common_cfg {
+ /* all fields are big endian */
+ u16 scan_index;
+ u16 bss_type; /* infra or adhoc */
+ u16 auth_method; /* shared key or open */
+ u16 op_mode; /* B/G */
+} __attribute__((packed));
+
+
+/* for GELIC_EURUS_CMD_WEP_CFG */
+enum gelic_eurus_wep_security {
+ GELIC_EURUS_WEP_SEC_NONE = 0,
+ GELIC_EURUS_WEP_SEC_40BIT = 1,
+ GELIC_EURUS_WEP_SEC_104BIT = 2,
+};
+
+struct gelic_eurus_wep_cfg {
+ /* all fields are big endian */
+ u16 security;
+ u8 key[4][16];
+} __attribute__((packed));
+
+/* for GELIC_EURUS_CMD_WPA_CFG */
+enum gelic_eurus_wpa_security {
+ GELIC_EURUS_WPA_SEC_NONE = 0x0000,
+ /* group=TKIP, pairwise=TKIP */
+ GELIC_EURUS_WPA_SEC_WPA_TKIP_TKIP = 0x0001,
+ /* group=AES, pairwise=AES */
+ GELIC_EURUS_WPA_SEC_WPA_AES_AES = 0x0002,
+ /* group=TKIP, pairwise=TKIP */
+ GELIC_EURUS_WPA_SEC_WPA2_TKIP_TKIP = 0x0004,
+ /* group=AES, pairwise=AES */
+ GELIC_EURUS_WPA_SEC_WPA2_AES_AES = 0x0008,
+ /* group=TKIP, pairwise=AES */
+ GELIC_EURUS_WPA_SEC_WPA_TKIP_AES = 0x0010,
+ /* group=TKIP, pairwise=AES */
+ GELIC_EURUS_WPA_SEC_WPA2_TKIP_AES = 0x0020,
+};
+
+enum gelic_eurus_wpa_psk_type {
+ GELIC_EURUS_WPA_PSK_PASSPHRASE = 0, /* passphrase string */
+ GELIC_EURUS_WPA_PSK_BIN = 1, /* 32 bytes binary key */
+};
+
+#define GELIC_WL_EURUS_PSK_MAX_LEN 64
+#define WPA_PSK_LEN 32 /* WPA spec says 256bit */
+
+struct gelic_eurus_wpa_cfg {
+ /* all fields are big endian */
+ u16 security;
+ u16 psk_type; /* psk key encoding type */
+ u8 psk[GELIC_WL_EURUS_PSK_MAX_LEN]; /* psk key; hex or passphrase */
+} __attribute__((packed));
+
+/* for GELIC_EURUS_CMD_{START,GET}_SCAN */
+enum gelic_eurus_scan_capability {
+ GELIC_EURUS_SCAN_CAP_ADHOC = 0x0000,
+ GELIC_EURUS_SCAN_CAP_INFRA = 0x0001,
+ GELIC_EURUS_SCAN_CAP_MASK = 0x0001,
+};
+
+enum gelic_eurus_scan_sec_type {
+ GELIC_EURUS_SCAN_SEC_NONE = 0x0000,
+ GELIC_EURUS_SCAN_SEC_WEP = 0x0100,
+ GELIC_EURUS_SCAN_SEC_WPA = 0x0200,
+ GELIC_EURUS_SCAN_SEC_WPA2 = 0x0400,
+ GELIC_EURUS_SCAN_SEC_MASK = 0x0f00,
+};
+
+enum gelic_eurus_scan_sec_wep_type {
+ GELIC_EURUS_SCAN_SEC_WEP_UNKNOWN = 0x0000,
+ GELIC_EURUS_SCAN_SEC_WEP_40 = 0x0001,
+ GELIC_EURUS_SCAN_SEC_WEP_104 = 0x0002,
+ GELIC_EURUS_SCAN_SEC_WEP_MASK = 0x0003,
+};
+
+enum gelic_eurus_scan_sec_wpa_type {
+ GELIC_EURUS_SCAN_SEC_WPA_UNKNOWN = 0x0000,
+ GELIC_EURUS_SCAN_SEC_WPA_TKIP = 0x0001,
+ GELIC_EURUS_SCAN_SEC_WPA_AES = 0x0002,
+ GELIC_EURUS_SCAN_SEC_WPA_MASK = 0x0003,
+};
+
+/*
+ * hw BSS information structure returned from GELIC_EURUS_CMD_GET_SCAN
+ */
+struct gelic_eurus_scan_info {
+ /* all fields are big endian */
+ __be16 size;
+ __be16 rssi; /* percentage */
+ __be16 channel; /* channel number */
+ __be16 beacon_period; /* FIXME: in msec unit */
+ __be16 capability;
+ __be16 security;
+ u8 bssid[8]; /* last ETH_ALEN are valid. bssid[0],[1] are unused */
+ u8 essid[32]; /* IW_ESSID_MAX_SIZE */
+ u8 rate[16]; /* first MAX_RATES_LENGTH(12) are valid */
+ u8 ext_rate[16]; /* first MAX_RATES_EX_LENGTH(16) are valid */
+ __be32 reserved1;
+ __be32 reserved2;
+ __be32 reserved3;
+ __be32 reserved4;
+ u8 elements[0]; /* ie */
+} __attribute__ ((packed));
+
+/* the hypervisor returns bbs up to 16 */
+#define GELIC_EURUS_MAX_SCAN (16)
+struct gelic_wl_scan_info {
+ struct list_head list;
+ struct gelic_eurus_scan_info *hwinfo;
+
+ int valid; /* set 1 if this entry was in latest scanned list
+ * from Eurus */
+ unsigned int eurus_index; /* index in the Eurus list */
+ unsigned long last_scanned; /* acquired time */
+
+ unsigned int rate_len;
+ unsigned int rate_ext_len;
+ unsigned int essid_len;
+};
+
+/* for GELIC_EURUS_CMD_GET_RSSI */
+struct gelic_eurus_rssi_info {
+ /* big endian */
+ __be16 rssi;
+} __attribute__ ((packed));
+
+
+/* for 'stat' member of gelic_wl_info */
+enum gelic_wl_info_status_bit {
+ GELIC_WL_STAT_CONFIGURED,
+ GELIC_WL_STAT_CH_INFO, /* ch info aquired */
+ GELIC_WL_STAT_ESSID_SET, /* ESSID specified by userspace */
+ GELIC_WL_STAT_BSSID_SET, /* BSSID specified by userspace */
+ GELIC_WL_STAT_WPA_PSK_SET, /* PMK specified by userspace */
+ GELIC_WL_STAT_WPA_LEVEL_SET, /* WEP or WPA[2] selected */
+};
+
+/* for 'scan_stat' member of gelic_wl_info */
+enum gelic_wl_scan_state {
+ /* just initialized or get last scan result failed */
+ GELIC_WL_SCAN_STAT_INIT,
+ /* scan request issued, accepted or chip is scanning */
+ GELIC_WL_SCAN_STAT_SCANNING,
+ /* scan results retrieved */
+ GELIC_WL_SCAN_STAT_GOT_LIST,
+};
+
+/* for 'cipher_method' */
+enum gelic_wl_cipher_method {
+ GELIC_WL_CIPHER_NONE,
+ GELIC_WL_CIPHER_WEP,
+ GELIC_WL_CIPHER_TKIP,
+ GELIC_WL_CIPHER_AES,
+};
+
+/* for 'wpa_level' */
+enum gelic_wl_wpa_level {
+ GELIC_WL_WPA_LEVEL_NONE,
+ GELIC_WL_WPA_LEVEL_WPA,
+ GELIC_WL_WPA_LEVEL_WPA2,
+};
+
+/* for 'assoc_stat' */
+enum gelic_wl_assoc_state {
+ GELIC_WL_ASSOC_STAT_DISCONN,
+ GELIC_WL_ASSOC_STAT_ASSOCIATING,
+ GELIC_WL_ASSOC_STAT_ASSOCIATED,
+};
+/* part of private data alloc_etherdev() allocated */
+#define GELIC_WEP_KEYS 4
+struct gelic_wl_info {
+ /* bss list */
+ struct semaphore scan_lock;
+ struct list_head network_list;
+ struct list_head network_free_list;
+ struct gelic_wl_scan_info *networks;
+
+ unsigned long scan_age; /* last scanned time */
+ enum gelic_wl_scan_state scan_stat;
+ struct completion scan_done;
+
+ /* eurus command queue */
+ struct workqueue_struct *eurus_cmd_queue;
+ struct completion cmd_done_intr;
+
+ /* eurus event handling */
+ struct workqueue_struct *event_queue;
+ struct delayed_work event_work;
+
+ /* wl status bits */
+ unsigned long stat;
+ enum gelic_eurus_auth_method auth_method; /* open/shared */
+ enum gelic_wl_cipher_method group_cipher_method;
+ enum gelic_wl_cipher_method pairwise_cipher_method;
+ enum gelic_wl_wpa_level wpa_level; /* wpa/wpa2 */
+
+ /* association handling */
+ struct semaphore assoc_stat_lock;
+ struct delayed_work assoc_work;
+ enum gelic_wl_assoc_state assoc_stat;
+ struct completion assoc_done;
+
+ spinlock_t lock;
+ u16 ch_info; /* available channels. bit0 = ch1 */
+ /* WEP keys */
+ u8 key[GELIC_WEP_KEYS][IW_ENCODING_TOKEN_MAX];
+ unsigned long key_enabled;
+ unsigned int key_len[GELIC_WEP_KEYS];
+ unsigned int current_key;
+ /* WWPA PSK */
+ u8 psk[GELIC_WL_EURUS_PSK_MAX_LEN];
+ enum gelic_eurus_wpa_psk_type psk_type;
+ unsigned int psk_len;
+
+ u8 essid[IW_ESSID_MAX_SIZE];
+ u8 bssid[ETH_ALEN]; /* userland requested */
+ u8 active_bssid[ETH_ALEN]; /* associated bssid */
+ unsigned int essid_len;
+
+ /* buffer for hypervisor IO */
+ void *buf;
+
+ struct iw_public_data wireless_data;
+ struct iw_statistics iwstat;
+};
+
+#define GELIC_WL_BSS_MAX_ENT 32
+#define GELIC_WL_ASSOC_RETRY 50
+static inline struct gelic_port *wl_port(struct gelic_wl_info *wl)
+{
+ return container_of((void *)wl, struct gelic_port, priv);
+}
+static inline struct gelic_wl_info *port_wl(struct gelic_port *port)
+{
+ return port_priv(port);
+}
+
+struct gelic_eurus_cmd {
+ struct work_struct work;
+ struct gelic_wl_info *wl;
+ unsigned int cmd; /* command code */
+ u64 tag;
+ u64 size;
+ void *buffer;
+ unsigned int buf_size;
+ struct completion done;
+ int status;
+ u64 cmd_status;
+};
+
+/* private ioctls to pass PSK */
+#define GELIC_WL_PRIV_SET_PSK (SIOCIWFIRSTPRIV + 0)
+#define GELIC_WL_PRIV_GET_PSK (SIOCIWFIRSTPRIV + 1)
+
+extern int gelic_wl_driver_probe(struct gelic_card *card);
+extern int gelic_wl_driver_remove(struct gelic_card *card);
+extern void gelic_wl_interrupt(struct net_device *netdev, u64 status);
+#endif /* _GELIC_WIRELESS_H */
^ permalink raw reply
* Re: [PATCH] UCC TDM driver for QE based MPC83xx platforms.
From: Stephen Rothwell @ 2008-01-24 6:19 UTC (permalink / raw)
To: Poonam_Aggrwal-b10812
Cc: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev,
michael.barkowski, rich.cutler, timur, ashish.kalra
In-Reply-To: <Pine.LNX.4.64.0801241014240.27491@linux121>
[-- Attachment #1: Type: text/plain, Size: 3620 bytes --]
On Thu, 24 Jan 2008 10:16:42 +0530 (IST) Poonam_Aggrwal-b10812 <b10812@freescale.com> wrote:
>
> +static int ucc_tdm_probe(struct of_device *ofdev,
> + const struct of_device_id *match)
> +{
> + struct device_node *np = ofdev->node;
> + struct resource res;
> + const unsigned int *prop;
> + u32 ucc_num, device_num, err, ret = 0;
> + struct device_node *np_tmp = NULL;
You don't need to initialise this.
> + dma_addr_t physaddr;
> + void *tdm_buff;
> + struct ucc_tdm_info *ut_info;
> +
> + prop = of_get_property(np, "device-id", NULL);
You should check for (prop == NULL).
> + ucc_num = *prop - 1;
> + if ((ucc_num < 0) || (ucc_num > 7))
> + return -ENODEV;
> +
> + ut_info = &utdm_info[ucc_num];
> + if (ut_info == NULL) {
This cannot be NULL as you have just taken the address of an array
element.
> + tdm_ctrl[device_num]->ut_info = ut_info;
> +
> + tdm_ctrl[device_num]->ut_info->uf_info.ucc_num = ucc_num;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is the same as "ut_info".
> + tdm_ctrl[device_num]->ut_info->uf_info.tdm_tx_clk =
> + (char *) of_get_property(np, "fsl,tdm-tx-clk", NULL);
^
We don't normall put spaces here.
> + tdm_ctrl[device_num]->ut_info->uf_info.tdm_rx_clk =
> + (char *) of_get_property(np, "fsl,tdm-rx-clk", NULL);
^
Ditto. And later as well.
> + tdm_ctrl[device_num]->ut_info->uf_info.irq =
> + irq_of_parse_and_map(np, 0);
> + err = of_address_to_resource(np, 0, &res);
> + if (err) {
> + ret = EINVAL;
This should be -EINVAL or err.
> + goto get_property_error;
You need to do something about unmapping the irq in the error path.
> + tdm_ctrl[device_num]->uf_regs = of_iomap(np, 0);
> +
> + np_tmp = of_find_compatible_node(np_tmp, "slic", "legerity-slic");
> + if (np_tmp != NULL)
> + tdm_ctrl[device_num]->leg_slic = 1;
> + else
> + tdm_ctrl[device_num]->leg_slic = 0;
of_node_ut(np_tmp);
> + tdm_buff = dma_alloc_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
> + tdm_ctrl[device_num]->cfg_ctrl.active_num_ts,
> + &physaddr, GFP_KERNEL);
> + if (!tdm_buff) {
> + printk(KERN_ERR "ucc-tdm: could not allocate buffer"
> + "descriptors\n");
> + ret = -ENOMEM;
> + goto get_property_error;
You need to unmap the uf_regs in the error path.
> +get_property_error:
> + kfree(tdm_ctrl[device_num]);
Do you need to set "tdm_ctrl[device_num]" to NULL and decrement
num_tdm_devices?
> + return ret;
> +}
> +
> +static int ucc_tdm_remove(struct of_device *ofdev)
> +{
> + struct tdm_ctrl *tdm_c;
> + struct ucc_tdm_info *ut_info;
> + u32 ucc_num;
> +
> + tdm_c = dev_get_drvdata(&(ofdev->dev));
dev_set_drvdata(&of_dev->dev, NULL);
> + ucc_num = tdm_c->ut_info->uf_info.ucc_num;
> + ut_info = &utdm_info[ucc_num];
> + tdm_stop(tdm_c);
> + tdm_deinit(tdm_c);
> +
> + ucc_fast_free(tdm_c->uf_private);
> +
> + dma_free_coherent(NULL, 2 * NR_BUFS * SAMPLE_DEPTH *
> + tdm_c->cfg_ctrl.active_num_ts,
> + tdm_c->tdm_input_data,
> + tdm_c->dma_input_addr);
> +
You need to unmap the uf_reg and the irq.
> +static struct of_device_id ucc_tdm_match[] = {
const, please.
> + {
> + .type = "tdm",
> + .compatible = "fsl,ucc-tdm",
> + }, {},
We euld normall format this like:
{ .type = "tdm", .compatible = "fsl,ucc-tdm", },
{},
> +static struct of_platform_driver ucc_tdm_driver = {
.driver = {
> + .name = DRV_NAME,
},
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [PATCH 2/3] Platform changes for UCC TDM driver for MPC8323ERDB.Also includes related QE changes and dts entries.
From: Stephen Rothwell @ 2008-01-24 6:34 UTC (permalink / raw)
To: Poonam_Aggrwal-b10812
Cc: kumar.gala, akpm, linux-kernel, netdev, rubini, linuxppc-dev,
michael.barkowski, rich.cutler, timur, ashish.kalra
In-Reply-To: <Pine.LNX.4.64.0801241016440.27491@linux121>
[-- Attachment #1: Type: text/plain, Size: 3163 bytes --]
This patch needs to come before the previous one ("UCC TDM driver for
QE based MPC83xx platforms") as that uses some of the fields defined here.
On Thu, 24 Jan 2008 10:19:44 +0530 (IST) Poonam_Aggrwal-b10812 <b10812@freescale.com> wrote:
>
> +u32 get_brg_clk(enum qe_clock brgclk, enum qe_clock *brg_source)
> {
> - struct device_node *qe;
> - if (brg_clk)
> - return brg_clk;
> + struct device_node *qe, *brg, *clocks;
> + enum qe_clock brg_src;
> + u32 brg_input_freq = 0;
> + u32 brg_num;
> + int ret;
> + const unsigned int *prop;
>
> - qe = of_find_node_by_type(NULL, "qe");
> - if (qe) {
> + *brg_source = 0;
> +
> + brg_num = brgclk - QE_BRG1;
> + brg = of_find_compatible_node(NULL, NULL, "fsl,cpm-brg");
> + if (brg) {
If you did
if (!brg) {
.
.
goto err;
}
Then you would save indenting all the rest of this function.
> + prop = of_get_property(brg,
> + "fsl,brg-sources", &size);
Join these lines.
> + of_node_put(brg);
> +
> + if (prop)
> + brg_src = *(prop + brg_num);
> + else {
> + printk(KERN_ERR "%s: invalid fsl,brg-sources in device "
> + "tree\n", __FUNCTION__);
> + ret = -EINVAL;
> + goto err;
> + }
> + if (brg_src == 0) {
> + *brg_source = 0;
> + if (brg_clk > 0)
> + return brg_clk;
> + qe = of_find_node_by_type(NULL, "qe");
> + if (qe) {
Again testing (!qe) and jumping to err would save another level if
indentation.
> + unsigned int size;
> + prop = of_get_property
> + (qe, "brg-frequency", &size);
And you wouldn't have to split things like this.
> + if (!prop) {
> + printk(KERN_ERR "%s: QE brg-frequency"
> + "not present in device tree\n",
> + __FUNCTION__);
> + ret = -EINVAL;
> + of_node_put(qe);
> + goto err;
> + }
> + if (*prop) {
> + of_node_put(qe);
> + brg_clk = *prop;
> + return *prop;
> + } else {
This else (and indentation) is unnecessary as you just returned above.
> + } else {
> + *brg_source = brg_src + QE_CLK1 - 1;
> + clocks = of_find_compatible_node(NULL, NULL,
> + "fsl,cpm-clocks");
> + if (!clocks) {
> + printk(KERN_ERR "%s: no clocks node in device"
> + " tree \n", __FUNCTION__);
> + ret = -EINVAL;
> + goto err;
> + } else {
Same here.
> + } else {
> + printk(KERN_ERR "%s: no brg node in device tree\n",
> + __FUNCTION__);
> + ret = -EINVAL;
> + goto err;
This goto is redundant.
> + }
> +err: return ret;
Put the label on a line by itself and indent it one space (that means that
"diff -p will reference the funstion anem instead of the label).
> @@ -152,6 +152,10 @@ struct ucc_fast_info {
> enum ucc_fast_rx_decoding_method renc;
> enum ucc_fast_transparent_tcrc tcrc;
> enum ucc_fast_sync_len synl;
> + char *tdm_rx_clk;
> + char *tdm_tx_clk;
> + char *tdm_rx_sync;
> + char *tdm_tx_sync;
If you make these "const char *" you won't have to cast the results of
of_get_property() that you assign to them.
--
Cheers,
Stephen Rothwell sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply
* Re: [IPV4 3/5] fib_trie: dump doesnt use RCU
From: Patrick McHardy @ 2008-01-24 6:41 UTC (permalink / raw)
To: David Miller; +Cc: shemminger, netdev
In-Reply-To: <20080123.205007.16809712.davem@davemloft.net>
David Miller wrote:
> From: Stephen Hemminger <shemminger@linux-foundation.org>
> Date: Wed, 23 Jan 2008 14:48:47 -0800
>
>
>> Since fib dump (via netlink) holds the RTNL mutex, it is unnecessary
>> to use RCU, and it is impossible to get truncated (-EBUSY) result.
>>
>> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
>>
>
> You tested this patch, right? :-/
>
> The whole reason we need the nlk->cb[] state is to hold things across
> multiple recvmsg() calls that might be necessary to obtain the full
> dump.
>
> rtnetlink goes:
>
> rtnl_lock();
> netlink_rcv_skb(skb, &rtnetlink_rcv_msg);
> ...
> static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
> {
> ...
> if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
> struct sock *rtnl;
> rtnl_dumpit_func dumpit;
>
> dumpit = rtnl_get_dumpit(family, type);
> if (dumpit == NULL)
> return -EOPNOTSUPP;
>
> __rtnl_unlock();
> rtnl = net->rtnl;
> err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL);
> rtnl_lock();
> return err;
>
> (NOTE: Drops RTNL semaphore for netlink_dump_start() call)
>
> ...
> int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
> struct nlmsghdr *nlh,
> int (*dump)(struct sk_buff *skb,
> struct netlink_callback *),
> int (*done)(struct netlink_callback *))
> {
> ...
> cb->dump = dump;
> cb->done = done;
> cb->nlh = nlh;
> atomic_inc(&skb->users);
> cb->skb = skb;
> ...
> mutex_lock(nlk->cb_mutex);
> ...
> nlk->cb = cb;
> mutex_unlock(nlk->cb_mutex);
>
> netlink_dump(sk);
> ...
>
> static int netlink_dump(struct sock *sk)
> {
> ...
> mutex_lock(nlk->cb_mutex);
> ...
> len = cb->dump(skb, cb);
>
> if (len > 0) {
> mutex_unlock(nlk->cb_mutex);
> skb_queue_tail(&sk->sk_receive_queue, skb);
> sk->sk_data_ready(sk, len);
> return 0;
> }
>
> (NOTE: Therefore cb->dump() runs without RTNL semaphore held)
>
> ...
> static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
> struct msghdr *msg, size_t len,
> int flags)
> {
> ...
> if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2)
> netlink_dump(sk);
> ...
>
> Therefore, that RTNL assertion you added should have triggered on any
> dump you may have tried since ->dump() is always invoked without the
> RTNL semaphore since rtnetlink drops it around the ->dump() call and
> the call chain for this fib_trie cause would be:
>
> inet_dump_fib()
> fn_trie_dump()
>
> and nothing in that code path retakes the RTNL semaphore.
Actually we're always holding the rtnl during dumps, nlk->cb_mutex points
to rtnl_mutex in case of rtnetlink. It used to be held only during the first
->dump invocation and not on continuations, but I changed this a few
versions
ago.
^ permalink raw reply
* Re: [IPV4 3/5] fib_trie: dump doesnt use RCU
From: David Miller @ 2008-01-24 6:43 UTC (permalink / raw)
To: kaber; +Cc: shemminger, netdev
In-Reply-To: <47983304.3030309@trash.net>
From: Patrick McHardy <kaber@trash.net>
Date: Thu, 24 Jan 2008 07:41:08 +0100
> David Miller wrote:
> > and nothing in that code path retakes the RTNL semaphore.
>
> Actually we're always holding the rtnl during dumps, nlk->cb_mutex points
> to rtnl_mutex in case of rtnetlink. It used to be held only during the first
> ->dump invocation and not on continuations, but I changed this a few
> versions ago.
My bad. Thanks for the correction Patrick.
But continuations can occur on subsequent recvmsg() calls,
does it return to userspace with the mutex held? If so
I'm pretty sure that's not allowed.
^ permalink raw reply
* Re: 2.6.24-rc8-mm1 Badness at net/ipv4/tcp_input.c:2506
From: Kamalesh Babulal @ 2008-01-24 6:44 UTC (permalink / raw)
To: ilpo.jarvinen
Cc: Andrew Morton, netdev, David Miller, krkumar2, LKML,
Andy Whitcroft, Balbir Singh
In-Reply-To: <20080117023514.9df393cf.akpm@linux-foundation.org>
Hi,
The following call trace is seen in the 2.6.24-rc8-mm1 kernel, which is same as one of
the call trace you have given a debug patch at http://marc.info/?l=linux-netdev&m=120107165228368&w=2
i was not able to apply the debug patch, can you kindly rebase the patch for 2.6.24-rc8-mm1 or let
me know, if i can help you in debugging this call trace.
Jan 24 11:13:57 p55lp6 kernel: [60656.708573] Badness at net/ipv4/tcp_input.c:2506
Jan 24 11:13:57 p55lp6 kernel: [60656.708583] NIP: c0000000003776e0 LR: c0000000003776a8 CTR: c0000000003aaf8c
Jan 24 11:13:57 p55lp6 kernel: [60656.708597] REGS: c00000000f6f34a0 TRAP: 0700 Not tainted (2.6.24-rc8-mm1)
Jan 24 11:13:57 p55lp6 kernel: [60656.708608] MSR: 8000000000029032 <EE,ME,IR,DR> CR: 24000088 XER: 00000018
Jan 24 11:13:57 p55lp6 kernel: [60656.708636] TASK = c000000000571710[0] 'swapper' THREAD: c000000000670000 CPU: 0
Jan 24 11:13:57 p55lp6 kernel: [60656.708648] GPR00: 00000000fffffffc c00000000f6f3720 c000000000664ab0 0000000000000000
Jan 24 11:13:57 p55lp6 kernel: [60656.708663] GPR04: 0000000000000001 000000000000040e 00000000000000d0 0000000000000000
Jan 24 11:13:57 p55lp6 kernel: [60656.708677] GPR08: 0000000000000000 00000000001bce5f 0000000000000001 fffffffffffffffc
Jan 24 11:13:57 p55lp6 kernel: [60656.708690] GPR12: c00000000f6f34f0 c000000000572180 0000000000000000 0000000000000004
Jan 24 11:13:57 p55lp6 kernel: [60656.708704] GPR16: 0000000000000001 000000003e3133e9 0000000000000402 000000000000011f
Jan 24 11:13:57 p55lp6 kernel: [60656.708718] GPR20: 000000000000011f 0000000000000004 0000000000000002 000000003e3133e9
Jan 24 11:13:57 p55lp6 kernel: [60656.708732] GPR24: c00000004c09fe80 0000000000000000 0000000000000000 000000000000040e
Jan 24 11:13:57 p55lp6 kernel: [60656.708745] GPR28: 0000000000000002 000000000000040e c000000000628570 c0000001791ff8d8
Jan 24 11:13:57 p55lp6 kernel: [60656.708760] NIP [c0000000003776e0] .tcp_fastretrans_alert+0xfc/0xe20
Jan 24 11:13:58 p55lp6 kernel: [60656.708778] LR [c0000000003776a8] .tcp_fastretrans_alert+0xc4/0xe20
Jan 24 11:13:58 p55lp6 kernel: [60656.708792] Call Trace:
Jan 24 11:13:58 p55lp6 kernel: [60656.708799] [c00000000f6f3720] [c000000000628570] 0xc000000000628570 (unreliable)
Jan 24 11:13:58 p55lp6 kernel: [60656.708819] [c00000000f6f37d0] [c00000000037a3b0] .tcp_ack+0xf34/0x10e4
Jan 24 11:13:58 p55lp6 kernel: [60656.708836] [c00000000f6f3920] [c00000000037dce8] .tcp_rcv_established+0x114/0x8a8
Jan 24 11:13:58 p55lp6 kernel: [60656.708854] [c00000000f6f39d0] [c00000000038599c] .tcp_v4_do_rcv+0x5c/0x260
Jan 24 11:13:58 p55lp6 kernel: [60656.708871] [c00000000f6f3a90] [c000000000387c24] .tcp_v4_rcv+0x8a0/0x93c
Jan 24 11:13:58 p55lp6 kernel: [60656.708888] [c00000000f6f3b50] [c000000000363fec] .ip_local_deliver_finish+0x164/0x284
Jan 24 11:13:58 p55lp6 kernel: [60656.709148] [c00000000f6f3be0] [c000000000363df8] .ip_rcv_finish+0x480/0x510
Jan 24 11:13:58 p55lp6 kernel: [60656.709165] [c00000000f6f3ca0] [c000000000332438] .netif_receive_skb+0x564/0x630
Jan 24 11:13:58 p55lp6 kernel: [60656.709184] [c00000000f6f3d70] [d0000000001e2630] .ibmveth_poll+0x238/0x3b4 [ibmveth]
Jan 24 11:13:58 p55lp6 kernel: [60656.709208] [c00000000f6f3e30] [c0000000003354c4] .net_rx_action+0x118/0x2e4
Jan 24 11:13:58 p55lp6 kernel: [60656.709226] [c00000000f6f3ef0] [c00000000007ca30] .__do_softirq+0xa8/0x164
Jan 24 11:13:58 p55lp6 kernel: [60656.709244] [c00000000f6f3f90] [c00000000002b7f8] .call_do_softirq+0x14/0x24
Jan 24 11:13:58 p55lp6 kernel: [60656.709262] [c000000000673920] [c00000000000bf74] .do_softirq+0x74/0xc0
Jan 24 11:13:58 p55lp6 kernel: [60656.709280] [c0000000006739b0] [c00000000007cb84] .irq_exit+0x54/0x6c
Jan 24 11:13:58 p55lp6 kernel: [60656.709297] [c000000000673a30] [c00000000000c954] .do_IRQ+0x1b8/0x200
Jan 24 11:13:58 p55lp6 kernel: [60656.709314] [c000000000673ae0] [c000000000004c18] hardware_interrupt_entry+0x18/0x1c
Jan 24 11:13:58 p55lp6 kernel: [60656.709332] --- Exception: 501 at .local_irq_restore+0x3c/0x40
Jan 24 11:13:58 p55lp6 kernel: [60656.709349] LR = .cpu_idle+0x13c/0x240
Jan 24 11:13:58 p55lp6 kernel: [60656.709357] [c000000000673dd0] [c0000000000123c8] .cpu_idle+0x130/0x240 (unreliable)
Jan 24 11:13:58 p55lp6 kernel: [60656.709377] [c000000000673e60] [c000000000009718] .rest_init+0x78/0x90
Jan 24 11:13:58 p55lp6 kernel: [60656.709394] [c000000000673ee0] [c0000000004d09dc] .start_kernel+0x3e8/0x40c
Jan 24 11:13:58 p55lp6 kernel: [60656.709412] [c000000000673f90] [c000000000008580] .start_here_common+0x54/0xd4
Jan 24 11:13:58 p55lp6 kernel: [60656.709429] Instruction dump:
Jan 24 11:13:58 p55lp6 kernel: [60656.709437] 419e0010 38000000 901f06ac 48000010 801f06ac 2f800000 409e0014 801f06b0
Jan 24 11:13:58 p55lp6 kernel: [60656.709464] 38800001 2f800000 409e0008 38800000 <0b040000> e87e80b0 4be81355 60000000
(gdb) p tcp_fastretrans_alert
$1 = {void (struct sock *, int, int)} 0x1ec0 <tcp_fastretrans_alert>
(gdb) p/x 0x1ec0+0xc4
$2 = 0x1f84
(gdb) l *0x1f84
0x1f84 is in tcp_fastretrans_alert (net/ipv4/tcp_input.c:2504).
2499 int is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
2500 int do_lost = is_dupack || ((flag & FLAG_DATA_SACKED) &&
2501 (tcp_fackets_out(tp) > tp->reordering));
2502 int fast_rexmit = 0;
2503
2504 if (WARN_ON(!tp->packets_out && tp->sacked_out))
2505 tp->sacked_out = 0;
2506 if (WARN_ON(!tp->sacked_out && tp->fackets_out))
2507 tp->fackets_out = 0;
2508
--
Thanks & Regards,
Kamalesh Babulal,
Linux Technology Center,
IBM, ISTL.
^ permalink raw reply
* Re: [IPV4 3/5] fib_trie: dump doesnt use RCU
From: Stephen Hemminger @ 2008-01-24 6:45 UTC (permalink / raw)
To: Patrick McHardy; +Cc: David Miller, netdev
In-Reply-To: <47983304.3030309@trash.net>
Patrick McHardy wrote:
> David Miller wrote:
>> From: Stephen Hemminger <shemminger@linux-foundation.org>
>> Date: Wed, 23 Jan 2008 14:48:47 -0800
>>
>>
>>> Since fib dump (via netlink) holds the RTNL mutex, it is unnecessary
>>> to use RCU, and it is impossible to get truncated (-EBUSY) result.
>>>
>>> Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
>>>
>>
>> You tested this patch, right? :-/
>>
>> The whole reason we need the nlk->cb[] state is to hold things across
>> multiple recvmsg() calls that might be necessary to obtain the full
>> dump.
>>
>> rtnetlink goes:
>>
>> rtnl_lock();
>> netlink_rcv_skb(skb, &rtnetlink_rcv_msg);
>> ...
>> static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
>> {
>> ...
>> if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
>> struct sock *rtnl;
>> rtnl_dumpit_func dumpit;
>>
>> dumpit = rtnl_get_dumpit(family, type);
>> if (dumpit == NULL)
>> return -EOPNOTSUPP;
>>
>> __rtnl_unlock();
>> rtnl = net->rtnl;
>> err = netlink_dump_start(rtnl, skb, nlh, dumpit, NULL);
>> rtnl_lock();
>> return err;
>>
>> (NOTE: Drops RTNL semaphore for netlink_dump_start() call)
>>
>> ...
>> int netlink_dump_start(struct sock *ssk, struct sk_buff *skb,
>> struct nlmsghdr *nlh,
>> int (*dump)(struct sk_buff *skb,
>> struct netlink_callback *),
>> int (*done)(struct netlink_callback *))
>> {
>> ...
>> cb->dump = dump;
>> cb->done = done;
>> cb->nlh = nlh;
>> atomic_inc(&skb->users);
>> cb->skb = skb;
>> ...
>> mutex_lock(nlk->cb_mutex);
>> ...
>> nlk->cb = cb;
>> mutex_unlock(nlk->cb_mutex);
>>
>> netlink_dump(sk);
>> ...
>>
>> static int netlink_dump(struct sock *sk)
>> {
>> ...
>> mutex_lock(nlk->cb_mutex);
>> ...
>> len = cb->dump(skb, cb);
>>
>> if (len > 0) {
>> mutex_unlock(nlk->cb_mutex);
>> skb_queue_tail(&sk->sk_receive_queue, skb);
>> sk->sk_data_ready(sk, len);
>> return 0;
>> }
>>
>> (NOTE: Therefore cb->dump() runs without RTNL semaphore held)
>>
>> ...
>> static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
>> struct msghdr *msg, size_t len,
>> int flags)
>> {
>> ...
>> if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2)
>> netlink_dump(sk);
>> ...
>>
>> Therefore, that RTNL assertion you added should have triggered on any
>> dump you may have tried since ->dump() is always invoked without the
>> RTNL semaphore since rtnetlink drops it around the ->dump() call and
>> the call chain for this fib_trie cause would be:
>>
>> inet_dump_fib()
>> fn_trie_dump()
>>
>> and nothing in that code path retakes the RTNL semaphore.
>
> Actually we're always holding the rtnl during dumps, nlk->cb_mutex points
> to rtnl_mutex in case of rtnetlink. It used to be held only during the
> first
> ->dump invocation and not on continuations, but I changed this a few
> versions
> ago.
>
>
Yes, I tested, no the assert didn't hit... Actually, the reason I went
down this path was
because I couldn't trigger -EBUSY with concurrent updates.
P.s: I checked and Quagga handles -EAGAIN, so if you need an error code that
would be the one to use.
^ permalink raw reply
* Re: [IPV4 3/5] fib_trie: dump doesnt use RCU
From: Patrick McHardy @ 2008-01-24 6:47 UTC (permalink / raw)
To: David Miller; +Cc: shemminger, netdev
In-Reply-To: <20080123.224337.80401207.davem@davemloft.net>
David Miller wrote:
> From: Patrick McHardy <kaber@trash.net>
> Date: Thu, 24 Jan 2008 07:41:08 +0100
>
>
>> David Miller wrote:
>>
>>> and nothing in that code path retakes the RTNL semaphore.
>>>
>> Actually we're always holding the rtnl during dumps, nlk->cb_mutex points
>> to rtnl_mutex in case of rtnetlink. It used to be held only during the first
>> ->dump invocation and not on continuations, but I changed this a few
>> versions ago.
>>
>
> My bad. Thanks for the correction Patrick.
>
> But continuations can occur on subsequent recvmsg() calls,
> does it return to userspace with the mutex held? If so
> I'm pretty sure that's not allowed.
>
No, the mutex is dropped between different ->dump invocations.
^ permalink raw reply
* Re: [PATCH 2.6.25 1/1]S2io: Multiqueue network device support implementation
From: Andi Kleen @ 2008-01-24 7:25 UTC (permalink / raw)
To: Ramkrishna Vepa; +Cc: Andi Kleen, Sreenivasa Honnur, netdev, jeff, support
In-Reply-To: <78C9135A3D2ECE4B8162EBDCE82CAD7702E70E92@nekter>
> [Ram] I am assuming that this is with regards to msi-x interrupts. We
Yes.
And avoiding bouncing locks for device state between CPUs.
> have done away with handling tx completion in the interrupt handler, and
> are instead handling them in the context of the transmit. The slow path,
> straggling transmit completions will be handled in the timer context.
Ok -- hopefully you don't have bad corner cases from this when the pipe
is not fully filled and then causing longer latencies on completion.
Old NAPI sometimes suffered from such problems.
-Andi
^ permalink raw reply
* [RESEND][PATCH] pci-skeleton: Misc fixes to build neatly
From: Jike Song @ 2008-01-24 6:51 UTC (permalink / raw)
To: jeff; +Cc: netdev, linux-kernel
Hello Jeff,
I'm sorry for the previous patch, which was mangled by gmail, with
some broken lines.
I'm trying to resend it by git-send-email. Please let me know in case
of any problems.
Thanks a lot.
Best Regards,
Jike
^ permalink raw reply
* [PATCH] pci-skeleton: Misc fixes to build neatly
From: Jike Song @ 2008-01-24 6:51 UTC (permalink / raw)
To: jeff; +Cc: netdev, linux-kernel, Jike Song
In-Reply-To: <1201157496-20665-1-git-send-email-albcamus@gmail.com>
The pci-skeleton.c has several problems with compilation, such as missing args
when calling synchronize_irq(). Fix it.
Signed-off-by: Jike Song <albcamus@gmail.com>
---
drivers/net/pci-skeleton.c | 49 ++++++++++++++++++++++---------------------
1 files changed, 25 insertions(+), 24 deletions(-)
diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c
index ed402e0..fffc49b 100644
--- a/drivers/net/pci-skeleton.c
+++ b/drivers/net/pci-skeleton.c
@@ -541,7 +541,7 @@ static void netdrv_hw_start (struct net_device *dev);
#define NETDRV_W32_F(reg, val32) do { writel ((val32), ioaddr + (reg)); readl (ioaddr + (reg)); } while (0)
-#if MMIO_FLUSH_AUDIT_COMPLETE
+#ifdef MMIO_FLUSH_AUDIT_COMPLETE
/* write MMIO register */
#define NETDRV_W8(reg, val8) writeb ((val8), ioaddr + (reg))
@@ -603,7 +603,7 @@ static int __devinit netdrv_init_board (struct pci_dev *pdev,
return -ENOMEM;
}
SET_NETDEV_DEV(dev, &pdev->dev);
- tp = dev->priv;
+ tp = netdev_priv(dev);
/* enable device (incl. PCI PM wakeup), and bus-mastering */
rc = pci_enable_device (pdev);
@@ -759,7 +759,7 @@ static int __devinit netdrv_init_one (struct pci_dev *pdev,
return i;
}
- tp = dev->priv;
+ tp = netdev_priv(dev);
assert (ioaddr != NULL);
assert (dev != NULL);
@@ -783,7 +783,7 @@ static int __devinit netdrv_init_one (struct pci_dev *pdev,
dev->base_addr = (unsigned long) ioaddr;
/* dev->priv/tp zeroed and aligned in alloc_etherdev */
- tp = dev->priv;
+ tp = netdev_priv(dev);
/* note: tp->chipset set in netdrv_init_board */
tp->drv_flags = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
@@ -841,7 +841,7 @@ static void __devexit netdrv_remove_one (struct pci_dev *pdev)
assert (dev != NULL);
- np = dev->priv;
+ np = netdev_priv(dev);
assert (np != NULL);
unregister_netdev (dev);
@@ -974,7 +974,7 @@ static void mdio_sync (void *mdio_addr)
static int mdio_read (struct net_device *dev, int phy_id, int location)
{
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
void *mdio_addr = tp->mmio_addr + Config4;
int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
int retval = 0;
@@ -1017,7 +1017,7 @@ static int mdio_read (struct net_device *dev, int phy_id, int location)
static void mdio_write (struct net_device *dev, int phy_id, int location,
int value)
{
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
void *mdio_addr = tp->mmio_addr + Config4;
int mii_cmd =
(0x5002 << 16) | (phy_id << 23) | (location << 18) | value;
@@ -1060,7 +1060,7 @@ static void mdio_write (struct net_device *dev, int phy_id, int location,
static int netdrv_open (struct net_device *dev)
{
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
int retval;
#ifdef NETDRV_DEBUG
void *ioaddr = tp->mmio_addr;
@@ -1121,7 +1121,7 @@ static int netdrv_open (struct net_device *dev)
/* Start the hardware at open or resume. */
static void netdrv_hw_start (struct net_device *dev)
{
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
void *ioaddr = tp->mmio_addr;
u32 i;
@@ -1191,7 +1191,7 @@ static void netdrv_hw_start (struct net_device *dev)
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
static void netdrv_init_ring (struct net_device *dev)
{
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
int i;
DPRINTK ("ENTER\n");
@@ -1213,7 +1213,7 @@ static void netdrv_init_ring (struct net_device *dev)
static void netdrv_timer (unsigned long data)
{
struct net_device *dev = (struct net_device *) data;
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
void *ioaddr = tp->mmio_addr;
int next_tick = 60 * HZ;
int mii_lpa;
@@ -1252,9 +1252,10 @@ static void netdrv_timer (unsigned long data)
}
-static void netdrv_tx_clear (struct netdrv_private *tp)
+static void netdrv_tx_clear (struct net_device *dev)
{
int i;
+ struct netdrv_private *tp = netdev_priv(dev);
atomic_set (&tp->cur_tx, 0);
atomic_set (&tp->dirty_tx, 0);
@@ -1278,7 +1279,7 @@ static void netdrv_tx_clear (struct netdrv_private *tp)
static void netdrv_tx_timeout (struct net_device *dev)
{
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
void *ioaddr = tp->mmio_addr;
int i;
u8 tmp8;
@@ -1311,7 +1312,7 @@ static void netdrv_tx_timeout (struct net_device *dev)
/* Stop a shared interrupt from scavenging while we are. */
spin_lock_irqsave (&tp->lock, flags);
- netdrv_tx_clear (tp);
+ netdrv_tx_clear (dev);
spin_unlock_irqrestore (&tp->lock, flags);
@@ -1325,7 +1326,7 @@ static void netdrv_tx_timeout (struct net_device *dev)
static int netdrv_start_xmit (struct sk_buff *skb, struct net_device *dev)
{
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
void *ioaddr = tp->mmio_addr;
int entry;
@@ -1525,7 +1526,7 @@ static void netdrv_rx_interrupt (struct net_device *dev,
DPRINTK ("%s: netdrv_rx() status %4.4x, size %4.4x,"
" cur %4.4x.\n", dev->name, rx_status,
rx_size, cur_rx);
-#if NETDRV_DEBUG > 2
+#if defined(NETDRV_DEBUG) && (NETDRV_DEBUG > 2)
{
int i;
DPRINTK ("%s: Frame contents ", dev->name);
@@ -1648,7 +1649,7 @@ static void netdrv_weird_interrupt (struct net_device *dev,
static irqreturn_t netdrv_interrupt (int irq, void *dev_instance)
{
struct net_device *dev = (struct net_device *) dev_instance;
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
int boguscnt = max_interrupt_work;
void *ioaddr = tp->mmio_addr;
int status = 0, link_changed = 0; /* avoid bogus "uninit" warning */
@@ -1711,7 +1712,7 @@ static irqreturn_t netdrv_interrupt (int irq, void *dev_instance)
static int netdrv_close (struct net_device *dev)
{
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
void *ioaddr = tp->mmio_addr;
unsigned long flags;
@@ -1738,10 +1739,10 @@ static int netdrv_close (struct net_device *dev)
spin_unlock_irqrestore (&tp->lock, flags);
- synchronize_irq ();
+ synchronize_irq (dev->irq);
free_irq (dev->irq, dev);
- netdrv_tx_clear (tp);
+ netdrv_tx_clear (dev);
pci_free_consistent(tp->pci_dev, RX_BUF_TOT_LEN,
tp->rx_ring, tp->rx_ring_dma);
@@ -1762,7 +1763,7 @@ static int netdrv_close (struct net_device *dev)
static int netdrv_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
{
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
struct mii_ioctl_data *data = if_mii(rq);
unsigned long flags;
int rc = 0;
@@ -1805,7 +1806,7 @@ static int netdrv_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
static void netdrv_set_rx_mode (struct net_device *dev)
{
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
void *ioaddr = tp->mmio_addr;
u32 mc_filter[2]; /* Multicast hash filter */
int i, rx_mode;
@@ -1862,7 +1863,7 @@ static void netdrv_set_rx_mode (struct net_device *dev)
static int netdrv_suspend (struct pci_dev *pdev, pm_message_t state)
{
struct net_device *dev = pci_get_drvdata (pdev);
- struct netdrv_private *tp = dev->priv;
+ struct netdrv_private *tp = netdev_priv(dev);
void *ioaddr = tp->mmio_addr;
unsigned long flags;
@@ -1892,7 +1893,7 @@ static int netdrv_suspend (struct pci_dev *pdev, pm_message_t state)
static int netdrv_resume (struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata (pdev);
- struct netdrv_private *tp = dev->priv;
+ /*struct netdrv_private *tp = netdev_priv(dev);*/
if (!netif_running(dev))
return 0;
--
1.5.3.4
^ permalink raw reply related
* Re: [IPV4 3/5] fib_trie: dump doesnt use RCU
From: David Miller @ 2008-01-24 7:26 UTC (permalink / raw)
To: kaber; +Cc: shemminger, netdev
In-Reply-To: <4798348B.4080300@trash.net>
From: Patrick McHardy <kaber@trash.net>
Date: Thu, 24 Jan 2008 07:47:39 +0100
> David Miller wrote:
> > From: Patrick McHardy <kaber@trash.net>
> > Date: Thu, 24 Jan 2008 07:41:08 +0100
> >
> >> David Miller wrote:
> >>
> >>> and nothing in that code path retakes the RTNL semaphore.
> >>>
> >> Actually we're always holding the rtnl during dumps, nlk->cb_mutex points
> >> to rtnl_mutex in case of rtnetlink. It used to be held only during the first
> >> ->dump invocation and not on continuations, but I changed this a few
> >> versions ago.
> >>
> >
> > My bad. Thanks for the correction Patrick.
> >
> > But continuations can occur on subsequent recvmsg() calls,
> > does it return to userspace with the mutex held? If so
> > I'm pretty sure that's not allowed.
>
> No, the mutex is dropped between different ->dump invocations.
Ok, great.
This does mean, however, that the RTNL semaphore is dropped between
->dump() invocations on the same nlk->cb[] instance.
And that has implications for the shortcut Stephen is taking.
Stephen's patch assumes that during a (top-level) dump the table
cannot change. And by the above, we can only conclude that it can in
fact change between ->dump() calls for the same top-level dump run.
So the removal of the -EBUSY code block in his patch isn't valid.
^ permalink raw reply
* Re: [IPV4 3/5] fib_trie: dump doesnt use RCU
From: David Miller @ 2008-01-24 7:27 UTC (permalink / raw)
To: shemminger; +Cc: kaber, netdev
In-Reply-To: <479833EE.1090905@linux-foundation.org>
From: Stephen Hemminger <shemminger@linux-foundation.org>
Date: Wed, 23 Jan 2008 22:45:02 -0800
> Yes, I tested, no the assert didn't hit... Actually, the reason I went
> down this path was
> because I couldn't trigger -EBUSY with concurrent updates.
>
> P.s: I checked and Quagga handles -EAGAIN, so if you need an error code that
> would be the one to use.
Ok, but see my other email, the -EBUSY block can't be removed.
Even though for any given ->dump() call RTNL is held, it
is dropped before the next ->dump() call on the same
piece of nlk->cb[] state.
So the node for the nlk->cb->args[2] key can go away due
to an intervening table change.
^ permalink raw reply
* Re: [PATCH 2.6.25 1/1]S2io: Multiqueue network device support implementation
From: David Miller @ 2008-01-24 7:30 UTC (permalink / raw)
To: andi; +Cc: Ramkrishna.Vepa, Sreenivasa.Honnur, netdev, jeff, support
In-Reply-To: <20080124072549.GA6814@one.firstfloor.org>
From: Andi Kleen <andi@firstfloor.org>
Date: Thu, 24 Jan 2008 08:25:49 +0100
> > have done away with handling tx completion in the interrupt handler, and
> > are instead handling them in the context of the transmit. The slow path,
> > straggling transmit completions will be handled in the timer context.
>
> Ok -- hopefully you don't have bad corner cases from this when the pipe
> is not fully filled and then causing longer latencies on completion.
> Old NAPI sometimes suffered from such problems.
BTW, such a TX completion timer will stall TCP sockets when the TX
queue of the device is partially filled and then sending stops.
TX ring SKB liberation really must be done in a very small finite
amount of time in order to avoid this problem properly.
Deferring it to HZ granular timers doesn't give a quick enough
response, especially at high packet rates.
^ permalink raw reply
* [PATCH] Re: Loopback address to eth0 interface and rooting goes wrong
From: Bjørn Mork @ 2008-01-24 8:08 UTC (permalink / raw)
To: netdev; +Cc: Varun Chandramohan, Gary, Breno Leitao, ecki
In-Reply-To: <47981623.1040100@linux.vnet.ibm.com>
[-- Attachment #1: Type: text/plain, Size: 1156 bytes --]
Hello
Gary < gary.manchon (at) gmail.com > found a problem where he was unable
to recover from a bad network interface configuration
(ifconfig eth0 127.0.0.1), ref
http://www.uwsg.iu.edu/hypermail/linux/net/0801.2/0009.html
This was confirmed by several people.
I suspect that the problem might be this code in net/ipv4/devinet.c ,
which sets ifa_scope to RT_SCOPE_HOST if you configure a loopback
address (127/8) on any interface. I guess it's there to protect us from
sending packets with a loopback source address, which woulnd't look too
good:
static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
{
struct in_device *in_dev = __in_dev_get_rtnl(dev);
ASSERT_RTNL();
if (!in_dev) {
inet_free_ifa(ifa);
return -ENOBUFS;
}
ipv4_devconf_setall(in_dev);
if (ifa->ifa_dev != in_dev) {
BUG_TRAP(!ifa->ifa_dev);
in_dev_hold(in_dev);
ifa->ifa_dev = in_dev;
}
if (LOOPBACK(ifa->ifa_local))
ifa->ifa_scope = RT_SCOPE_HOST;
return inet_insert_ifa(ifa);
}
The real problem is that there's never anything resetting this scope if
you change the address later. This patch adds scope reset when changing
the address.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: devinet-rt_scope-fix.patch --]
[-- Type: text/x-diff, Size: 719 bytes --]
We set the scope to RT_SCOPE_HOST if an IPv4 interface is configured with a
loopback address (127/8). This prevents the interface from sending packets
out the wire. This patch resets the scope if the address is changed again,
to restore normal functionality.
Signed-off-by: Bjorn Mork <bjorn@mork.no>
devinet.c | 1 +
1 file changed, 1 insertion(+)
--- linux-2.6.24-rc8.orig/net/ipv4/devinet.c 2008-01-16 05:22:48.000000000 +0100
+++ linux-2.6.24-rc8/net/ipv4/devinet.c 2008-01-23 19:17:30.000000000 +0100
@@ -753,6 +753,7 @@
inet_del_ifa(in_dev, ifap, 0);
ifa->ifa_broadcast = 0;
ifa->ifa_anycast = 0;
+ ifa->ifa_scope = 0;
}
ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
[-- Attachment #3: Type: text/plain, Size: 13 bytes --]
Bjørn
^ permalink raw reply
* Re: [Bugme-new] [Bug 9806] New: (tun dev) Impossible to deassert IFF_ONE_QUEUE or IFF_NO_PI
From: Andrew Morton @ 2008-01-24 8:33 UTC (permalink / raw)
To: nwfilardo; +Cc: bugme-daemon, maxk, vtun, netdev
In-Reply-To: <bug-9806-10286@http.bugzilla.kernel.org/>
> On Wed, 23 Jan 2008 13:13:13 -0800 (PST) bugme-daemon@bugzilla.kernel.org wrote:
> http://bugzilla.kernel.org/show_bug.cgi?id=9806
>
> Summary: (tun dev) Impossible to deassert IFF_ONE_QUEUE or
> IFF_NO_PI
> Product: Drivers
> Version: 2.5
> KernelVersion: 2.6.23
> Platform: All
> OS/Version: Linux
> Tree: Mainline
> Status: NEW
> Severity: normal
> Priority: P1
> Component: Network
> AssignedTo: jgarzik@pobox.com
> ReportedBy: nwfilardo@gmail.com
>
>
> Problem Description:
>
> The TUN/TAP driver only permits one-way transitions of IFF_NO_PI or
> IFF_ONE_QUEUE during the lifetime of a tap/tun interface. Note that
> tun_set_iff contains
>
> 541 if (ifr->ifr_flags & IFF_NO_PI)
> 542 tun->flags |= TUN_NO_PI;
> 543
> 544 if (ifr->ifr_flags & IFF_ONE_QUEUE)
> 545 tun->flags |= TUN_ONE_QUEUE;
>
> This is easily fixed by adding else branches which clear these bits.
>
> Steps to reproduce:
>
> This is easily reproduced by setting an interface persistant using tunctl then
> attempting to open it as IFF_TAP or IFF_TUN, without asserting the IFF_NO_PI
> flag. The ioctl() will succeed and the ifr.flags word is not modified, but the
> interface remains in IFF_NO_PI mode (as it was set by tunctl).
>
Thanks. Could you please submit the patch via email? Send it to
all recipients of this email.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox