Commit 6512edec authored by Deepa Dinamani's avatar Deepa Dinamani Committed by Greg Kroah-Hartman

staging: ft1000: remove obsolete driver

Remove support for Qleadtek Flash-OFDM modems. Telecom carrier is
discontinuing service for the radio technology.
See http://www.gtigroup.org/news/ind/2015-08-18/6996.html.
Suggested-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarDeepa Dinamani <deepa.kernel@gmail.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent eb96c7fc
......@@ -62,8 +62,6 @@ source "drivers/staging/xgifb/Kconfig"
source "drivers/staging/emxx_udc/Kconfig"
source "drivers/staging/ft1000/Kconfig"
source "drivers/staging/speakup/Kconfig"
source "drivers/staging/ste_rmi4/Kconfig"
......
......@@ -25,7 +25,6 @@ obj-$(CONFIG_IIO) += iio/
obj-$(CONFIG_FB_SM750) += sm750fb/
obj-$(CONFIG_FB_XGI) += xgifb/
obj-$(CONFIG_USB_EMXX) += emxx_udc/
obj-$(CONFIG_FT1000) += ft1000/
obj-$(CONFIG_SPEAKUP) += speakup/
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
obj-$(CONFIG_MFD_NVEC) += nvec/
......
config FT1000
tristate "Drivers for Flarion ft1000 devices"
if FT1000
config FT1000_USB
tristate "Driver for ft1000 usb devices."
depends on USB
depends on NET
help
Say Y if you want to have support for Qleadtek FLASH-OFDM USB Modem [LR7F04],
Qleadtek Express Card or Leadtek Multi-band modem HSDPA.
config FT1000_PCMCIA
tristate "Driver for ft1000 pcmcia device."
depends on PCMCIA
depends on NET
help
Say Y if you want to have support for Flarion card also called
Multimedia Net Card.
endif
obj-$(CONFIG_FT1000_USB) += ft1000-usb/
obj-$(CONFIG_FT1000_PCMCIA) += ft1000-pcmcia/
TODO:
- checkpatch.pl cleanups
- coding style
- sparse fixes
- adapt to latest usb and pcmcia api changes
- change firmware loading for usb driver to proper kernel method (request_firmware)
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
Cc: Marek Belisko <marek.belisko@gmail.com>
obj-$(CONFIG_FT1000_PCMCIA) = ft1000_pcmcia.o
ft1000_pcmcia-y := ft1000_hw.o ft1000_dnld.o ft1000_cs.o
/*---------------------------------------------------------------------------
FT1000 driver for Flarion Flash OFDM NIC Device
Copyright (C) 2002 Flarion Technologies, All rights reserved.
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. 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., 59 Temple Place -
Suite 330, Boston, MA 02111-1307, USA.
---------------------------------------------------------------------------
File: boot.h
Description: boatloader
History:
1/11/05 Whc Ported to Linux.
---------------------------------------------------------------------------*/
#ifndef _BOOTH_
#define _BOOTH_
/* Official bootloader */
static unsigned char bootimage[] = {
0x00, 0x00, 0x01, 0x5E, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0xD7,
0x00, 0x00, 0x01, 0x5E, 0x46, 0xB3,
0xE6, 0x02, 0x00, 0x98, 0xE6, 0x8C,
0x00, 0x98, 0xFB, 0x92, 0xFF, 0xFF,
0x98, 0xFB, 0x94, 0xFF, 0xFF, 0x98,
0xFB, 0x06, 0x08, 0x00, 0x98, 0xFB,
0x96, 0x84, 0x00, 0x98, 0xFB, 0x08,
0x1C, 0x00, 0x98, 0xFB, 0x51, 0x25,
0x10, 0x1C, 0x00, 0xE6, 0x51, 0x01,
0x07, 0xFD, 0x4C, 0xFF, 0x20, 0xF5,
0x51, 0x02, 0x20, 0x08, 0x00, 0x4C,
0xFF, 0x20, 0x3C, 0x00, 0xC0, 0x64,
0x98, 0xC0, 0x66, 0x98, 0xC0, 0x68,
0x98, 0xC0, 0x6A, 0x98, 0xC0, 0x6C,
0x98, 0x90, 0x08, 0x90, 0x09, 0x90,
0x0A, 0x90, 0x0B, 0x90, 0x0C, 0x90,
0x0D, 0x90, 0x0E, 0x90, 0x0F, 0x90,
0x04, 0x90, 0x06, 0xFB, 0x51, 0x22,
0x16, 0x08, 0x03, 0xFB, 0x51, 0x52,
0x16, 0x08, 0x04, 0xFB, 0x51, 0x24,
0x2B, 0x08, 0x06, 0xFB, 0x51, 0x54,
0x2B, 0x08, 0x07, 0xFB, 0x51, 0x24,
0x2B, 0x08, 0x09, 0xFB, 0x51, 0x54,
0x2B, 0x08, 0x0A, 0xFB, 0x51, 0x12,
0x16, 0x08, 0x0C, 0xFB, 0x51, 0x52,
0x16, 0x08, 0x0D, 0x78, 0x00, 0x00,
0x00, 0x16, 0x00, 0x00, 0xEC, 0x31,
0xAE, 0x00, 0x00, 0x81, 0x4C, 0x0F,
0xE6, 0x43, 0xFF, 0xEC, 0x31, 0x4E,
0x00, 0x00, 0x91, 0xEC, 0x31, 0xAE,
0x00, 0x00, 0x91, 0x4C, 0x0F, 0xE6,
0x43, 0xFF, 0xEC, 0x31, 0x5E, 0x00,
0x00, 0xA1, 0xEB, 0x31, 0x08, 0x00,
0x00, 0xA6, 0xEB, 0x31, 0x08, 0x00,
0x00, 0xAC, 0x3C, 0x00, 0xEB, 0x31,
0x08, 0x00, 0x00, 0xA8, 0x76, 0xFE,
0xFE, 0x08, 0xEB, 0x31, 0x08, 0x20,
0x00, 0x00, 0x76, 0xFF, 0xFF, 0x18,
0xED, 0x31, 0x08, 0x20, 0x00, 0x00,
0x26, 0x10, 0x04, 0x10, 0xF5, 0x3C,
0x01, 0x3C, 0x00, 0x08, 0x01, 0x12,
0x3C, 0x11, 0x3C, 0x00, 0x08, 0x01,
0x0B, 0x08, 0x00, 0x6D, 0xEC, 0x31,
0xAE, 0x20, 0x00, 0x06, 0xED, 0x4D,
0x08, 0x00, 0x00, 0x67, 0x80, 0x6F,
0x00, 0x01, 0x0B, 0x6F, 0x00, 0x02,
0x2E, 0x76, 0xEE, 0x01, 0x48, 0x06,
0x01, 0x39, 0xED, 0x4D, 0x18, 0x00,
0x02, 0xED, 0x4D, 0x08, 0x00, 0x04,
0x14, 0x06, 0xA4, 0xED, 0x31, 0x22,
0x00, 0x00, 0xAC, 0x76, 0xEE, 0x07,
0x48, 0x6D, 0x22, 0x01, 0x1E, 0x08,
0x01, 0x58, 0xEB, 0x31, 0x08, 0x00,
0x00, 0xAC, 0x06, 0xFF, 0xBA, 0x3C,
0x00, 0xEB, 0x31, 0x08, 0x20, 0x00,
0x04, 0x3C, 0x30, 0xEB, 0x31, 0x08,
0x20, 0x00, 0x02, 0x3C, 0x10, 0xEB,
0x31, 0x08, 0x20, 0x00, 0x00, 0xED,
0x31, 0x08, 0x20, 0x00, 0x00, 0x04,
0x10, 0xF7, 0xED, 0x31, 0x08, 0x00,
0x00, 0xA2, 0x91, 0x00, 0x9C, 0x3C,
0x80, 0xEB, 0x31, 0x08, 0x20, 0x00,
0x04, 0x3C, 0x20, 0xEB, 0x31, 0x08,
0x20, 0x00, 0x02, 0x3C, 0x10, 0xEB,
0x31, 0x08, 0x20, 0x00, 0x00, 0xED,
0x31, 0x08, 0x20, 0x00, 0x00, 0x04,
0x10, 0xF7, 0xED, 0x31, 0x08, 0x20,
0x00, 0x04, 0x42, 0x10, 0x90, 0x08,
0xEC, 0x31, 0xAE, 0x20, 0x00, 0x06,
0xA4, 0x41, 0x08, 0x00, 0xB6, 0xED,
0x41, 0x28, 0x7D, 0xFF, 0xFF, 0x22,
0xB3, 0x40, 0x98, 0x2A, 0x32, 0xEB,
0x41, 0x28, 0xB4, 0x43, 0xFC, 0x05,
0xFF, 0xE6, 0xA0, 0x31, 0x20, 0x00,
0x06, 0xEB, 0x31, 0x08, 0x20, 0x00,
0x04, 0x3C, 0x20, 0xEB, 0x31, 0x08,
0x20, 0x00, 0x02, 0x3C, 0x10, 0xEB,
0x31, 0x08, 0x20, 0x00, 0x00, 0xED,
0x31, 0x08, 0x20, 0x00, 0x00, 0x04,
0x10, 0xF7, 0xED, 0x31, 0x08, 0x20,
0x00, 0x04, 0x42, 0x10, 0x90, 0x08,
0xEC, 0x31, 0xAE, 0x20, 0x00, 0x06,
0xA4, 0x41, 0x08, 0x00, 0x68, 0xED,
0x41, 0x28, 0x7D, 0xFF, 0xFF, 0x22,
0xB3, 0x40, 0x98, 0x2A, 0x32, 0xEB,
0x41, 0x28, 0xB4, 0x43, 0xFC, 0x05,
0xFF, 0xE6, 0x48, 0x04, 0xEB, 0x31,
0x08, 0x20, 0x00, 0x04, 0xEB, 0x31,
0x18, 0x20, 0x00, 0x02, 0x3C, 0x11,
0xEB, 0x31, 0x18, 0x20, 0x00, 0x00,
0xED, 0x31, 0x08, 0x20, 0x00, 0x00,
0x04, 0x10, 0xF7, 0xED, 0x31, 0x08,
0x20, 0x00, 0x02, 0x66, 0x00, 0x6F,
0x00, 0x01, 0x16, 0x76, 0xEE, 0x06,
0x48, 0x4A, 0x1E, 0x48, 0x04, 0xED,
0x31, 0x08, 0x20, 0x00, 0x04, 0xEB,
0x31, 0x08, 0x00, 0x00, 0xA4, 0x48,
0x04, 0xED, 0x31, 0x08, 0x20, 0x00,
0x04, 0xEB, 0x31, 0x08, 0x00, 0x00,
0xA2, 0x48, 0x04, 0x20, 0x20, 0x4A,
0x7C, 0x46, 0x82, 0x50, 0x05, 0x50,
0x15, 0xB5, 0x1E, 0x98, 0xED, 0x31,
0x08, 0x00, 0x00, 0xA8, 0x10, 0x47,
0x3B, 0x2C, 0x01, 0xDB, 0x40, 0x11,
0x98, 0xC1, 0x1E, 0x98, 0x10, 0x07,
0x30, 0xF9, 0x40, 0x07, 0x18, 0x98,
0x2A, 0x10, 0xEB, 0x31, 0x08, 0x00,
0x00, 0xA8, 0xA4, 0x1E, 0x98, 0xBB,
0x1E, 0x98, 0x50, 0x14, 0x50, 0x04,
0x46, 0x83, 0x48, 0x04, 0x02, 0x01,
0x00, 0x50, 0x05, 0x50, 0x15, 0x10,
0x87, 0x3F, 0x90, 0x2B, 0x18, 0x01,
0x00, 0xC0, 0x31, 0x00, 0x00, 0xAE,
0xDF, 0x41, 0x00, 0x08, 0x00, 0x1A,
0x42, 0x11, 0x67, 0x01, 0xDF, 0x41,
0x02, 0x08, 0x00, 0x10, 0x42, 0x11,
0x62, 0x01, 0xB4, 0x43, 0x4A, 0x68,
0x50, 0x14, 0x50, 0x04, 0x24, 0x10,
0x48, 0x04, 0xF2, 0x31, 0x00, 0x01,
0x00, 0x00, 0xAE, 0xF6, 0x31, 0x00,
0x01, 0x00, 0x00, 0xAE, 0x62, 0xE4,
0xE5, 0x61, 0x04, 0x48, 0x04, 0xE5,
0x63, 0x05, 0x48, 0x04, 0x20, 0x20,
0x00, 0x00, 0x00, 0x00
};
#endif
/*---------------------------------------------------------------------------
FT1000 driver for Flarion Flash OFDM NIC Device
Copyright (C) 2002 Flarion Technologies, All rights reserved.
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. 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., 59 Temple Place -
Suite 330, Boston, MA 02111-1307, USA.
---------------------------------------------------------------------------
Description: Common structures and defines
---------------------------------------------------------------------------*/
#ifndef _FT1000H_
#define _FT1000H_
#include "../ft1000.h"
#define FT1000_DRV_VER 0x01010300
#define FT1000_DPRAM_BASE 0x0000 /* Dual Port RAM starting offset */
/*
* Maximum number of occurrence of pseudo header errors before resetting PC
* Card.
*/
#define MAX_PH_ERR 300
#define SUCCESS 0x00
#define FAILURE 0x01
struct ft1000_pcmcia {
int PktIntfErr;
u16 packetseqnum;
void *link;
};
struct pcmcia_device;
struct net_device;
struct net_device *init_ft1000_card(struct pcmcia_device *link,
void *ft1000_reset);
void stop_ft1000_card(struct net_device *dev);
int card_download(struct net_device *dev, const u8 *pFileStart,
size_t FileLength);
u16 ft1000_read_dpram(struct net_device *dev, int offset);
void card_bootload(struct net_device *dev);
u16 ft1000_read_dpram_mag_16(struct net_device *dev, int offset, int Index);
u32 ft1000_read_dpram_mag_32(struct net_device *dev, int offset);
void ft1000_write_dpram_mag_32(struct net_device *dev, int offset, u32 value);
/* Read the value of a given ASIC register. */
static inline u16 ft1000_read_reg(struct net_device *dev, u16 offset)
{
return inw(dev->base_addr + offset);
}
/* Set the value of a given ASIC register. */
static inline void ft1000_write_reg(struct net_device *dev, u16 offset,
u16 value)
{
outw(value, dev->base_addr + offset);
}
#endif
/*---------------------------------------------------------------------------
FT1000 driver for Flarion Flash OFDM NIC Device
Copyright (C) 1999 David A. Hinds. All Rights Reserved.
Copyright (C) 2002 Flarion Technologies, All rights reserved.
Copyright (C) 2006 Patrik Ostrihon, All rights reserved.
Copyright (C) 2006 ProWeb Consulting, a.s, All rights reserved.
The initial developer of the original code is David A. Hinds
<dahinds@users.sourceforge.net>. Portions created by David A. Hinds.
This file was modified to support the Flarion Flash OFDM NIC Device
by Wai Chan (w.chan@flarion.com).
Port for kernel 2.6 created by Patrik Ostrihon (patrik.ostrihon@pwc.sk)
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. 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., 59 Temple Place -
Suite 330, Boston, MA 02111-1307, USA.
-----------------------------------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
/*====================================================================*/
MODULE_AUTHOR("Wai Chan");
MODULE_DESCRIPTION("FT1000 PCMCIA driver");
MODULE_LICENSE("GPL");
/*====================================================================*/
static int ft1000_config(struct pcmcia_device *link);
static void ft1000_detach(struct pcmcia_device *link);
static int ft1000_attach(struct pcmcia_device *link);
#include "ft1000.h"
/*====================================================================*/
static void ft1000_reset(struct pcmcia_device *link)
{
pcmcia_reset_card(link->socket);
}
static int ft1000_attach(struct pcmcia_device *link)
{
link->priv = NULL;
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
return ft1000_config(link);
}
static void ft1000_detach(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
if (dev)
stop_ft1000_card(dev);
pcmcia_disable_device(link);
free_netdev(dev);
}
static int ft1000_confcheck(struct pcmcia_device *link, void *priv_data)
{
return pcmcia_request_io(link);
}
/*======================================================================
ft1000_config() is scheduled to run after a CARD_INSERTION event
is received, to configure the PCMCIA socket, and to make the
device available to the system.
======================================================================*/
static int ft1000_config(struct pcmcia_device *link)
{
int ret;
dev_dbg(&link->dev, "ft1000_cs: ft1000_config(0x%p)\n", link);
/* setup IO window */
ret = pcmcia_loop_config(link, ft1000_confcheck, NULL);
if (ret) {
dev_err(&link->dev, "Could not configure pcmcia\n");
return -ENODEV;
}
/* configure device */
ret = pcmcia_enable_device(link);
if (ret) {
dev_err(&link->dev, "Could not enable pcmcia\n");
goto failed;
}
link->priv = init_ft1000_card(link, &ft1000_reset);
if (!link->priv) {
dev_err(&link->dev, "Could not register as network device\n");
goto failed;
}
/* Finally, report what we've done */
return 0;
failed:
pcmcia_disable_device(link);
return -ENODEV;
}
static int ft1000_suspend(struct pcmcia_device *link)
{
struct net_device *dev = link->priv;
if (link->open)
netif_device_detach(dev);
return 0;
}
static int ft1000_resume(struct pcmcia_device *link)
{
return 0;
}
/*====================================================================*/
static const struct pcmcia_device_id ft1000_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x0100),
PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x1000),
PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x1300),
PCMCIA_DEVICE_NULL,
};
MODULE_DEVICE_TABLE(pcmcia, ft1000_ids);
static struct pcmcia_driver ft1000_cs_driver = {
.owner = THIS_MODULE,
.name = "ft1000_cs",
.probe = ft1000_attach,
.remove = ft1000_detach,
.id_table = ft1000_ids,
.suspend = ft1000_suspend,
.resume = ft1000_resume,
};
module_pcmcia_driver(ft1000_cs_driver);
/*---------------------------------------------------------------------------
FT1000 driver for Flarion Flash OFDM NIC Device
Copyright (C) 2002 Flarion Technologies, All rights reserved.
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. 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., 59 Temple Place -
Suite 330, Boston, MA 02111-1307, USA.
--------------------------------------------------------------------------
Description: This module will handshake with the DSP bootloader to
download the DSP runtime image.
---------------------------------------------------------------------------*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define __KERNEL_SYSCALLS__
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/unistd.h>
#include <linux/netdevice.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include "ft1000.h"
#include "boot.h"
#define MAX_DSP_WAIT_LOOPS 100
#define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */
#define MAX_LENGTH 0x7f0
#define DWNLD_MAG_HANDSHAKE_LOC 0x00
#define DWNLD_MAG_TYPE_LOC 0x01
#define DWNLD_MAG_SIZE_LOC 0x02
#define DWNLD_MAG_PS_HDR_LOC 0x03
#define DWNLD_HANDSHAKE_LOC 0x02
#define DWNLD_TYPE_LOC 0x04
#define DWNLD_SIZE_MSW_LOC 0x06
#define DWNLD_SIZE_LSW_LOC 0x08
#define DWNLD_PS_HDR_LOC 0x0A
#define HANDSHAKE_TIMEOUT_VALUE 0xF1F1
#define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */
#define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */
#define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */
#define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */
#define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */
#define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */
#define REQUEST_CODE_LENGTH 0x0000
#define REQUEST_RUN_ADDRESS 0x0001
#define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */
#define REQUEST_DONE_BL 0x0003
#define REQUEST_DONE_CL 0x0004
#define REQUEST_VERSION_INFO 0x0005
#define REQUEST_CODE_BY_VERSION 0x0006
#define REQUEST_MAILBOX_DATA 0x0007
#define REQUEST_FILE_CHECKSUM 0x0008
#define STATE_START_DWNLD 0x01
#define STATE_BOOT_DWNLD 0x02
#define STATE_CODE_DWNLD 0x03
#define STATE_DONE_DWNLD 0x04
#define STATE_SECTION_PROV 0x05
#define STATE_DONE_PROV 0x06
#define STATE_DONE_FILE 0x07
struct dsp_file_hdr {
u32 version_id; /* Version ID of this image format. */
u32 package_id; /* Package ID of code release. */
u32 build_date; /* Date/time stamp when file was built. */
u32 commands_offset; /* Offset to attached commands in Pseudo Hdr format. */
u32 loader_offset; /* Offset to bootloader code. */
u32 loader_code_address; /* Start address of bootloader. */
u32 loader_code_end; /* Where bootloader code ends. */
u32 loader_code_size;
u32 version_data_offset; /* Offset were scrambled version data begins. */
u32 version_data_size; /* Size, in words, of scrambled version data. */
u32 nDspImages; /* Number of DSP images in file. */
} __packed;
struct dsp_image_info {
u32 coff_date; /* Date/time when DSP Coff image was built. */
u32 begin_offset; /* Offset in file where image begins. */
u32 end_offset; /* Offset in file where image begins. */
u32 run_address; /* On chip Start address of DSP code. */
u32 image_size; /* Size of image. */
u32 version; /* Embedded version # of DSP code. */
unsigned short checksum; /* Dsp File checksum */
unsigned short pad1;
} __packed;
void card_bootload(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
unsigned long flags;
u32 *pdata;
u32 size;
u32 i;
u32 templong;
netdev_dbg(dev, "card_bootload is called\n");
pdata = (u32 *)bootimage;
size = sizeof(bootimage);
/* check for odd word */
if (size & 0x0003)
size += 4;
/* Provide mutual exclusive access while reading ASIC registers. */
spin_lock_irqsave(&info->dpram_lock, flags);
/* need to set i/o base address initially and hardware will autoincrement */
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_BASE);
/* write bytes */
for (i = 0; i < (size >> 2); i++) {
templong = *pdata++;
outl(templong, dev->base_addr + FT1000_REG_MAG_DPDATA);
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
}
static u16 get_handshake(struct net_device *dev, u16 expected_value)
{
struct ft1000_info *info = netdev_priv(dev);
u16 handshake;
u32 tempx;
int loopcnt;
loopcnt = 0;
while (loopcnt < MAX_DSP_WAIT_LOOPS) {
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_HANDSHAKE_LOC);
handshake = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
} else {
tempx =
ntohl(ft1000_read_dpram_mag_32
(dev, DWNLD_MAG_HANDSHAKE_LOC));
handshake = (u16)tempx;
}
if ((handshake == expected_value)
|| (handshake == HANDSHAKE_RESET_VALUE)) {
return handshake;
}
loopcnt++;
mdelay(DSP_WAIT_SLEEP_TIME);
}
return HANDSHAKE_TIMEOUT_VALUE;
}
static void put_handshake(struct net_device *dev, u16 handshake_value)
{
struct ft1000_info *info = netdev_priv(dev);
u32 tempx;
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_HANDSHAKE_LOC);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, handshake_value); /* Handshake */
} else {
tempx = (u32)handshake_value;
tempx = ntohl(tempx);
ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */
}
}
static u16 get_request_type(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
u16 request_type;
u32 tempx;
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, DWNLD_TYPE_LOC);
request_type = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
} else {
tempx = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_TYPE_LOC);
tempx = ntohl(tempx);
request_type = (u16)tempx;
}
return request_type;
}
static long get_request_value(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
long value;
u16 w_val;
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_SIZE_MSW_LOC);
w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
value = (long)(w_val << 16);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_SIZE_LSW_LOC);
w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
value = (long)(value | w_val);
} else {
value = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC);
value = ntohl(value);
}
return value;
}
static void put_request_value(struct net_device *dev, long lvalue)
{
struct ft1000_info *info = netdev_priv(dev);
u16 size;
u32 tempx;
if (info->AsicID == ELECTRABUZZ_ID) {
size = (u16) (lvalue >> 16);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_SIZE_MSW_LOC);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
size = (u16) (lvalue);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_SIZE_LSW_LOC);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
} else {
tempx = ntohl(lvalue);
ft1000_write_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC, tempx); /* Handshake */
}
}
static u16 hdr_checksum(struct pseudo_hdr *pHdr)
{
u16 *usPtr = (u16 *)pHdr;
u16 chksum;
chksum = (((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
usPtr[4]) ^ usPtr[5]) ^ usPtr[6];
return chksum;
}
int card_download(struct net_device *dev, const u8 *pFileStart,
size_t FileLength)
{
struct ft1000_info *info = netdev_priv(dev);
int Status = SUCCESS;
u32 uiState;
u16 handshake;
struct pseudo_hdr *pHdr;
u16 usHdrLength;
long word_length;
u16 request;
u16 temp;
struct prov_record *pprov_record;
u8 *pbuffer;
struct dsp_file_hdr *pFileHdr5;
struct dsp_image_info *pDspImageInfoV6 = NULL;
long requested_version;
bool bGoodVersion = false;
struct drv_msg *pMailBoxData;
u16 *pUsData = NULL;
u16 *pUsFile = NULL;
u8 *pUcFile = NULL;
u8 *pBootEnd = NULL;
u8 *pCodeEnd = NULL;
int imageN;
long file_version;
long loader_code_address = 0;
long loader_code_size = 0;
long run_address = 0;
long run_size = 0;
unsigned long flags;
unsigned long templong;
unsigned long image_chksum = 0;
file_version = *(long *)pFileStart;
if (file_version != 6) {
pr_err("unsupported firmware version %ld\n", file_version);
Status = FAILURE;
}
uiState = STATE_START_DWNLD;
pFileHdr5 = (struct dsp_file_hdr *)pFileStart;
pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->loader_offset);
pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->loader_offset);
pBootEnd = (u8 *) ((long)pFileStart + pFileHdr5->loader_code_end);
loader_code_address = pFileHdr5->loader_code_address;
loader_code_size = pFileHdr5->loader_code_size;
bGoodVersion = false;
while ((Status == SUCCESS) && (uiState != STATE_DONE_FILE)) {
switch (uiState) {
case STATE_START_DWNLD:
handshake = get_handshake(dev, HANDSHAKE_DSP_BL_READY);
if (handshake == HANDSHAKE_DSP_BL_READY)
put_handshake(dev, HANDSHAKE_DRIVER_READY);
else
Status = FAILURE;
uiState = STATE_BOOT_DWNLD;
break;
case STATE_BOOT_DWNLD:
handshake = get_handshake(dev, HANDSHAKE_REQUEST);
if (handshake == HANDSHAKE_REQUEST) {
/*
* Get type associated with the request.
*/
request = get_request_type(dev);
switch (request) {
case REQUEST_RUN_ADDRESS:
put_request_value(dev,
loader_code_address);
break;
case REQUEST_CODE_LENGTH:
put_request_value(dev,
loader_code_size);
break;
case REQUEST_DONE_BL:
/* Reposition ptrs to beginning of code section */
pUsFile = (u16 *) ((long)pBootEnd);
pUcFile = (u8 *) ((long)pBootEnd);
uiState = STATE_CODE_DWNLD;
break;
case REQUEST_CODE_SEGMENT:
word_length = get_request_value(dev);
if (word_length > MAX_LENGTH) {
Status = FAILURE;
break;
}
if ((word_length * 2 + (long)pUcFile) >
(long)pBootEnd) {
/*
* Error, beyond boot code range.
*/
Status = FAILURE;
break;
}
/* Provide mutual exclusive access while reading ASIC registers. */
spin_lock_irqsave(&info->dpram_lock,
flags);
/*
* Position ASIC DPRAM auto-increment pointer.
*/
outw(DWNLD_MAG_PS_HDR_LOC,
dev->base_addr +
FT1000_REG_DPRAM_ADDR);
if (word_length & 0x01)
word_length++;
word_length = word_length / 2;
for (; word_length > 0; word_length--) { /* In words */
templong = *pUsFile++;
templong |=
(*pUsFile++ << 16);
pUcFile += 4;
outl(templong,
dev->base_addr +
FT1000_REG_MAG_DPDATAL);
}
spin_unlock_irqrestore(&info->
dpram_lock,
flags);
break;
default:
Status = FAILURE;
break;
}
put_handshake(dev, HANDSHAKE_RESPONSE);
} else {
Status = FAILURE;
}
break;
case STATE_CODE_DWNLD:
handshake = get_handshake(dev, HANDSHAKE_REQUEST);
if (handshake == HANDSHAKE_REQUEST) {
/*
* Get type associated with the request.
*/
request = get_request_type(dev);
switch (request) {
case REQUEST_FILE_CHECKSUM:
netdev_dbg(dev,
"ft1000_dnld: REQUEST_FOR_CHECKSUM\n");
put_request_value(dev, image_chksum);
break;
case REQUEST_RUN_ADDRESS:
if (bGoodVersion) {
put_request_value(dev,
run_address);
} else {
Status = FAILURE;
break;
}
break;
case REQUEST_CODE_LENGTH:
if (bGoodVersion) {
put_request_value(dev,
run_size);
} else {
Status = FAILURE;
break;
}
break;
case REQUEST_DONE_CL:
/* Reposition ptrs to beginning of provisioning section */
pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->commands_offset);
pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->commands_offset);
uiState = STATE_DONE_DWNLD;
break;
case REQUEST_CODE_SEGMENT:
if (!bGoodVersion) {
Status = FAILURE;
break;
}
word_length = get_request_value(dev);
if (word_length > MAX_LENGTH) {
Status = FAILURE;
break;
}
if ((word_length * 2 + (long)pUcFile) >
(long)pCodeEnd) {
/*
* Error, beyond boot code range.
*/
Status = FAILURE;
break;
}
/*
* Position ASIC DPRAM auto-increment pointer.
*/
outw(DWNLD_MAG_PS_HDR_LOC,
dev->base_addr +
FT1000_REG_DPRAM_ADDR);
if (word_length & 0x01)
word_length++;
word_length = word_length / 2;
for (; word_length > 0; word_length--) { /* In words */
templong = *pUsFile++;
templong |=
(*pUsFile++ << 16);
pUcFile += 4;
outl(templong,
dev->base_addr +
FT1000_REG_MAG_DPDATAL);
}
break;
case REQUEST_MAILBOX_DATA:
/* Convert length from byte count to word count. Make sure we round up. */
word_length =
(long)(info->DSPInfoBlklen + 1) / 2;
put_request_value(dev, word_length);
pMailBoxData =
(struct drv_msg *)&info->DSPInfoBlk[0];
pUsData =
(u16 *)&pMailBoxData->data[0];
/* Provide mutual exclusive access while reading ASIC registers. */
spin_lock_irqsave(&info->dpram_lock,
flags);
if (file_version == 5) {
/*
* Position ASIC DPRAM auto-increment pointer.
*/
ft1000_write_reg(dev,
FT1000_REG_DPRAM_ADDR,
DWNLD_PS_HDR_LOC);
for (; word_length > 0; word_length--) { /* In words */
temp = ntohs(*pUsData);
ft1000_write_reg(dev,
FT1000_REG_DPRAM_DATA,
temp);
pUsData++;
}
} else {
/*
* Position ASIC DPRAM auto-increment pointer.
*/
outw(DWNLD_MAG_PS_HDR_LOC,
dev->base_addr +
FT1000_REG_DPRAM_ADDR);
if (word_length & 0x01)
word_length++;
word_length = word_length / 2;
for (; word_length > 0; word_length--) { /* In words */
templong = *pUsData++;
templong |=
(*pUsData++ << 16);
outl(templong,
dev->base_addr +
FT1000_REG_MAG_DPDATAL);
}
}
spin_unlock_irqrestore(&info->
dpram_lock,
flags);
break;
case REQUEST_VERSION_INFO:
word_length =
pFileHdr5->version_data_size;
put_request_value(dev, word_length);
pUsFile =
(u16 *) ((long)pFileStart +
pFileHdr5->
version_data_offset);
/* Provide mutual exclusive access while reading ASIC registers. */
spin_lock_irqsave(&info->dpram_lock,
flags);
/*
* Position ASIC DPRAM auto-increment pointer.
*/
outw(DWNLD_MAG_PS_HDR_LOC,
dev->base_addr +
FT1000_REG_DPRAM_ADDR);
if (word_length & 0x01)
word_length++;
word_length = word_length / 2;
for (; word_length > 0; word_length--) { /* In words */
templong =
ntohs(*pUsFile++);
temp =
ntohs(*pUsFile++);
templong |=
(temp << 16);
outl(templong,
dev->base_addr +
FT1000_REG_MAG_DPDATAL);
}
spin_unlock_irqrestore(&info->
dpram_lock,
flags);
break;
case REQUEST_CODE_BY_VERSION:
bGoodVersion = false;
requested_version =
get_request_value(dev);
pDspImageInfoV6 =
(struct dsp_image_info *) ((long)
pFileStart
+
sizeof
(struct dsp_file_hdr));
for (imageN = 0;
imageN <
pFileHdr5->nDspImages;
imageN++) {
temp = (u16)
(pDspImageInfoV6->
version);
templong = temp;
temp = (u16)
(pDspImageInfoV6->
version >> 16);
templong |=
(temp << 16);
if (templong ==
requested_version) {
bGoodVersion =
true;
pUsFile =
(u16
*) ((long)
pFileStart
+
pDspImageInfoV6->
begin_offset);
pUcFile =
(u8
*) ((long)
pFileStart
+
pDspImageInfoV6->
begin_offset);
pCodeEnd =
(u8
*) ((long)
pFileStart
+
pDspImageInfoV6->
end_offset);
run_address =
pDspImageInfoV6->
run_address;
run_size =
pDspImageInfoV6->
image_size;
image_chksum =
(u32)
pDspImageInfoV6->
checksum;
netdev_dbg(dev,
"ft1000_dnld: image_chksum = 0x%8x\n",
(unsigned
int)
image_chksum);
break;
}
pDspImageInfoV6++;
}
if (!bGoodVersion) {
/*
* Error, beyond boot code range.
*/
Status = FAILURE;
break;
}
break;
default:
Status = FAILURE;
break;
}
put_handshake(dev, HANDSHAKE_RESPONSE);
} else {
Status = FAILURE;
}
break;
case STATE_DONE_DWNLD:
if (((unsigned long)(pUcFile) - (unsigned long) pFileStart) >=
(unsigned long)FileLength) {
uiState = STATE_DONE_FILE;
break;
}
pHdr = (struct pseudo_hdr *)pUsFile;
if (pHdr->portdest == 0x80 /* DspOAM */
&& (pHdr->portsrc == 0x00 /* Driver */
|| pHdr->portsrc == 0x10 /* FMM */)) {
uiState = STATE_SECTION_PROV;
} else {
netdev_dbg(dev,
"Download error: Bad Port IDs in Pseudo Record\n");
netdev_dbg(dev, "\t Port Source = 0x%2.2x\n",
pHdr->portsrc);
netdev_dbg(dev, "\t Port Destination = 0x%2.2x\n",
pHdr->portdest);
Status = FAILURE;
}
break;
case STATE_SECTION_PROV:
pHdr = (struct pseudo_hdr *)pUcFile;
if (pHdr->checksum == hdr_checksum(pHdr)) {
if (pHdr->portdest != 0x80 /* Dsp OAM */) {
uiState = STATE_DONE_PROV;
break;
}
usHdrLength = ntohs(pHdr->length); /* Byte length for PROV records */
/* Get buffer for provisioning data */
pbuffer =
kmalloc(usHdrLength + sizeof(struct pseudo_hdr),
GFP_ATOMIC);
if (pbuffer) {
memcpy(pbuffer, pUcFile,
(u32) (usHdrLength +
sizeof(struct pseudo_hdr)));
/* link provisioning data */
pprov_record =
kmalloc(sizeof(struct prov_record),
GFP_ATOMIC);
if (pprov_record) {
pprov_record->pprov_data =
pbuffer;
list_add_tail(&pprov_record->
list,
&info->prov_list);
/* Move to next entry if available */
pUcFile =
(u8 *)((unsigned long) pUcFile +
(unsigned long) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr));
if ((unsigned long) (pUcFile) -
(unsigned long) (pFileStart) >=
(unsigned long)FileLength) {
uiState =
STATE_DONE_FILE;
}
} else {
kfree(pbuffer);
Status = FAILURE;
}
} else {
Status = FAILURE;
}
} else {
/* Checksum did not compute */
Status = FAILURE;
}
break;
case STATE_DONE_PROV:
uiState = STATE_DONE_FILE;
break;
default:
Status = FAILURE;
break;
} /* End Switch */
} /* End while */
return Status;
}
/*---------------------------------------------------------------------------
FT1000 driver for Flarion Flash OFDM NIC Device
Copyright (C) 2002 Flarion Technologies, All rights reserved.
Copyright (C) 2006 Patrik Ostrihon, All rights reserved.
Copyright (C) 2006 ProWeb Consulting, a.s, All rights reserved.
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. 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., 59 Temple Place -
Suite 330, Boston, MA 02111-1307, USA.
-------------------------------------------------------------------------*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/wait.h>
#include <linux/vmalloc.h>
#include <linux/firmware.h>
#include <linux/ethtool.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include <linux/delay.h>
#include "ft1000.h"
static const struct firmware *fw_entry;
static void ft1000_hbchk(u_long data);
static struct timer_list poll_timer = {
.function = ft1000_hbchk
};
static u16 cmdbuffer[1024];
static u8 tempbuffer[1600];
static u8 ft1000_card_present;
static u8 flarion_ft1000_cnt;
static irqreturn_t ft1000_interrupt(int irq, void *dev_id);
static void ft1000_enable_interrupts(struct net_device *dev);
static void ft1000_disable_interrupts(struct net_device *dev);
/* new kernel */
MODULE_AUTHOR("");
MODULE_DESCRIPTION("Support for Flarion Flash OFDM NIC Device. Support for PCMCIA when used with ft1000_cs.");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("FT1000");
#define MAX_RCV_LOOP 100
/*---------------------------------------------------------------------------
Function: ft1000_read_fifo_len
Description: This function will read the ASIC Uplink FIFO status register
which will return the number of bytes remaining in the Uplink FIFO.
Sixteen bytes are subtracted to make sure that the ASIC does not
reach its threshold.
Input:
dev - network device structure
Output:
value - number of bytes available in the ASIC Uplink FIFO.
-------------------------------------------------------------------------*/
static inline u16 ft1000_read_fifo_len(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
if (info->AsicID == ELECTRABUZZ_ID)
return (ft1000_read_reg(dev, FT1000_REG_UFIFO_STAT) - 16);
else
return (ft1000_read_reg(dev, FT1000_REG_MAG_UFSR) - 16);
}
/*---------------------------------------------------------------------------
Function: ft1000_read_dpram
Description: This function will read the specific area of dpram
(Electrabuzz ASIC only)
Input:
dev - device structure
offset - index of dpram
Output:
value - value of dpram
-------------------------------------------------------------------------*/
u16 ft1000_read_dpram(struct net_device *dev, int offset)
{
struct ft1000_info *info = netdev_priv(dev);
unsigned long flags;
u16 data;
/* Provide mutual exclusive access while reading ASIC registers. */
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
data = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
spin_unlock_irqrestore(&info->dpram_lock, flags);
return data;
}
/*---------------------------------------------------------------------------
Function: ft1000_write_dpram
Description: This function will write to a specific area of dpram
(Electrabuzz ASIC only)
Input:
dev - device structure
offset - index of dpram
value - value to write
Output:
none.
-------------------------------------------------------------------------*/
static inline void ft1000_write_dpram(struct net_device *dev,
int offset, u16 value)
{
struct ft1000_info *info = netdev_priv(dev);
unsigned long flags;
/* Provide mutual exclusive access while reading ASIC registers. */
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, value);
spin_unlock_irqrestore(&info->dpram_lock, flags);
}
/*---------------------------------------------------------------------------
Function: ft1000_read_dpram_mag_16
Description: This function will read the specific area of dpram
(Magnemite ASIC only)
Input:
dev - device structure
offset - index of dpram
Output:
value - value of dpram
-------------------------------------------------------------------------*/
u16 ft1000_read_dpram_mag_16(struct net_device *dev, int offset, int Index)
{
struct ft1000_info *info = netdev_priv(dev);
unsigned long flags;
u16 data;
/* Provide mutual exclusive access while reading ASIC registers. */
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
/* check if we want to read upper or lower 32-bit word */
if (Index)
data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAL);
else
data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAH);
spin_unlock_irqrestore(&info->dpram_lock, flags);
return data;
}
/*---------------------------------------------------------------------------
Function: ft1000_write_dpram_mag_16
Description: This function will write to a specific area of dpram
(Magnemite ASIC only)
Input:
dev - device structure
offset - index of dpram
value - value to write
Output:
none.
-------------------------------------------------------------------------*/
static inline void ft1000_write_dpram_mag_16(struct net_device *dev,
int offset, u16 value, int Index)
{
struct ft1000_info *info = netdev_priv(dev);
unsigned long flags;
/* Provide mutual exclusive access while reading ASIC registers. */
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
if (Index)
ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAL, value);
else
ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, value);
spin_unlock_irqrestore(&info->dpram_lock, flags);
}
/*---------------------------------------------------------------------------
Function: ft1000_read_dpram_mag_32
Description: This function will read the specific area of dpram
(Magnemite ASIC only)
Input:
dev - device structure
offset - index of dpram
Output:
value - value of dpram
-------------------------------------------------------------------------*/
u32 ft1000_read_dpram_mag_32(struct net_device *dev, int offset)
{
struct ft1000_info *info = netdev_priv(dev);
unsigned long flags;
u32 data;
/* Provide mutual exclusive access while reading ASIC registers. */
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
data = inl(dev->base_addr + FT1000_REG_MAG_DPDATAL);
spin_unlock_irqrestore(&info->dpram_lock, flags);
return data;
}
/*---------------------------------------------------------------------------
Function: ft1000_write_dpram_mag_32
Description: This function will write to a specific area of dpram
(Magnemite ASIC only)
Input:
dev - device structure
offset - index of dpram
value - value to write
Output:
none.
-------------------------------------------------------------------------*/
void ft1000_write_dpram_mag_32(struct net_device *dev, int offset, u32 value)
{
struct ft1000_info *info = netdev_priv(dev);
unsigned long flags;
/* Provide mutual exclusive access while reading ASIC registers. */
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
outl(value, dev->base_addr + FT1000_REG_MAG_DPDATAL);
spin_unlock_irqrestore(&info->dpram_lock, flags);
}
/*---------------------------------------------------------------------------
Function: ft1000_enable_interrupts
Description: This function will enable interrupts base on the current
interrupt mask.
Input:
dev - device structure
Output:
None.
-------------------------------------------------------------------------*/
static void ft1000_enable_interrupts(struct net_device *dev)
{
u16 tempword;
ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_DEFAULT_MASK);
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK);
pr_debug("current interrupt enable mask = 0x%x\n", tempword);
}
/*---------------------------------------------------------------------------
Function: ft1000_disable_interrupts
Description: This function will disable all interrupts.
Input:
dev - device structure
Output:
None.
-------------------------------------------------------------------------*/
static void ft1000_disable_interrupts(struct net_device *dev)
{
u16 tempword;
ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_MASK_ALL);
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK);
pr_debug("current interrupt enable mask = 0x%x\n", tempword);
}
/*---------------------------------------------------------------------------
Function: ft1000_read_dsp_timer
Description: This function reads the DSP timer and stores its value in the
DSP_TIME field of the ft1000_info struct passed as argument
Input:
dev - device structure
info - ft1000_info structure
Output:
None.
-------------------------------------------------------------------------*/
static void ft1000_read_dsp_timer(struct net_device *dev,
struct ft1000_info *info)
{
if (info->AsicID == ELECTRABUZZ_ID) {
info->DSP_TIME[0] = ft1000_read_dpram(dev, FT1000_DSP_TIMER0);
info->DSP_TIME[1] = ft1000_read_dpram(dev, FT1000_DSP_TIMER1);
info->DSP_TIME[2] = ft1000_read_dpram(dev, FT1000_DSP_TIMER2);
info->DSP_TIME[3] = ft1000_read_dpram(dev, FT1000_DSP_TIMER3);
} else {
info->DSP_TIME[0] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0,
FT1000_MAG_DSP_TIMER0_INDX);
info->DSP_TIME[1] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1,
FT1000_MAG_DSP_TIMER1_INDX);
info->DSP_TIME[2] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2,
FT1000_MAG_DSP_TIMER2_INDX);
info->DSP_TIME[3] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3,
FT1000_MAG_DSP_TIMER3_INDX);
}
}
/*---------------------------------------------------------------------------
Function: ft1000_reset_asic
Description: This function will call the Card Service function to reset the
ASIC.
Input:
dev - device structure
Output:
none
-------------------------------------------------------------------------*/
static void ft1000_reset_asic(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
struct ft1000_pcmcia *pcmcia = info->priv;
u16 tempword;
(*info->ft1000_reset) (pcmcia->link);
/*
* Let's use the register provided by the Magnemite ASIC to reset the
* ASIC and DSP.
*/
if (info->AsicID == MAGNEMITE_ID) {
ft1000_write_reg(dev, FT1000_REG_RESET,
DSP_RESET_BIT | ASIC_RESET_BIT);
}
mdelay(1);
if (info->AsicID == ELECTRABUZZ_ID) {
/* set watermark to -1 in order to not generate an interrupt */
ft1000_write_reg(dev, FT1000_REG_WATERMARK, 0xffff);
} else {
/* set watermark to -1 in order to not generate an interrupt */
ft1000_write_reg(dev, FT1000_REG_MAG_WATERMARK, 0xffff);
}
/* clear interrupts */
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
pr_debug("interrupt status register = 0x%x\n", tempword);
ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword);
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
pr_debug("interrupt status register = 0x%x\n", tempword);
}
/*---------------------------------------------------------------------------
Function: ft1000_reset_card
Description: This function will reset the card
Input:
dev - device structure
Output:
status - false (card reset fail)
true (card reset successful)
-------------------------------------------------------------------------*/
static int ft1000_reset_card(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
u16 tempword;
int i;
unsigned long flags;
struct prov_record *ptr;
struct prov_record *tmp;
info->CardReady = 0;
info->ProgConStat = 0;
info->squeseqnum = 0;
ft1000_disable_interrupts(dev);
/* del_timer(&poll_timer); */
/* Make sure we free any memory reserve for provisioning */
list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) {
pr_debug("deleting provisioning record\n");
list_del(&ptr->list);
kfree(ptr->pprov_data);
kfree(ptr);
}
if (info->AsicID == ELECTRABUZZ_ID) {
pr_debug("resetting DSP\n");
ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT);
} else {
pr_debug("resetting ASIC and DSP\n");
ft1000_write_reg(dev, FT1000_REG_RESET,
DSP_RESET_BIT | ASIC_RESET_BIT);
}
/* Copy DSP session record into info block if this is not a coldstart */
if (ft1000_card_present == 1) {
spin_lock_irqsave(&info->dpram_lock, flags);
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_RX_BASE);
for (i = 0; i < MAX_DSP_SESS_REC; i++) {
info->DSPSess.Rec[i] =
ft1000_read_reg(dev,
FT1000_REG_DPRAM_DATA);
}
} else {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_RX_BASE);
for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) {
info->DSPSess.MagRec[i] =
inl(dev->base_addr
+ FT1000_REG_MAG_DPDATA);
}
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
}
pr_debug("resetting ASIC\n");
mdelay(10);
/* reset ASIC */
ft1000_reset_asic(dev);
pr_debug("downloading dsp image\n");
if (info->AsicID == MAGNEMITE_ID) {
/* Put dsp in reset and take ASIC out of reset */
pr_debug("Put DSP in reset and take ASIC out of reset\n");
ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT);
/* Setting MAGNEMITE ASIC to big endian mode */
ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, HOST_INTF_BE);
/* Download bootloader */
card_bootload(dev);
/* Take DSP out of reset */
ft1000_write_reg(dev, FT1000_REG_RESET, 0);
/* FLARION_DSP_ACTIVE; */
mdelay(10);
pr_debug("Take DSP out of reset\n");
/*
* Wait for 0xfefe indicating dsp ready before starting
* download
*/
for (i = 0; i < 50; i++) {
tempword = ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DPRAM_FEFE,
FT1000_MAG_DPRAM_FEFE_INDX);
if (tempword == 0xfefe)
break;
mdelay(20);
}
if (i == 50) {
pr_debug("No FEFE detected from DSP\n");
return false;
}
} else {
/* Take DSP out of reset */
ft1000_write_reg(dev, FT1000_REG_RESET, ~DSP_RESET_BIT);
mdelay(10);
}
if (card_download(dev, fw_entry->data, fw_entry->size)) {
pr_debug("card download unsuccessful\n");
return false;
}
pr_debug("card download successful\n");
mdelay(10);
if (info->AsicID == ELECTRABUZZ_ID) {
/*
* Need to initialize the FIFO length counter to zero in order
* to sync up with the DSP
*/
info->fifo_cnt = 0;
ft1000_write_dpram(dev, FT1000_FIFO_LEN, info->fifo_cnt);
/* Initialize DSP heartbeat area to ho */
ft1000_write_dpram(dev, FT1000_HI_HO, ho);
tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
pr_debug("hi_ho value = 0x%x\n", tempword);
} else {
/* Initialize DSP heartbeat area to ho */
ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, ho_mag,
FT1000_MAG_HI_HO_INDX);
tempword =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO,
FT1000_MAG_HI_HO_INDX);
pr_debug("hi_ho value = 0x%x\n", tempword);
}
info->CardReady = 1;
ft1000_enable_interrupts(dev);
/* Schedule heartbeat process to run every 2 seconds */
/* poll_timer.expires = jiffies + (2*HZ); */
/* poll_timer.data = (u_long)dev; */
/* add_timer(&poll_timer); */
return true;
}
/*---------------------------------------------------------------------------
Function: ft1000_chkcard
Description: This function will check if the device is presently available on
the system.
Input:
dev - device structure
Output:
status - false (device is not present)
true (device is present)
-------------------------------------------------------------------------*/
static int ft1000_chkcard(struct net_device *dev)
{
u16 tempword;
/*
* Mask register is used to check for device presence since it is never
* set to zero.
*/
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK);
if (tempword == 0) {
pr_debug("IMASK = 0 Card not detected\n");
return false;
}
/*
* The system will return the value of 0xffff for the version register
* if the device is not present.
*/
tempword = ft1000_read_reg(dev, FT1000_REG_ASIC_ID);
if (tempword == 0xffff) {
pr_debug("Version = 0xffff Card not detected\n");
return false;
}
return true;
}
/*---------------------------------------------------------------------------
Function: ft1000_hbchk
Description: This function will perform the heart beat check of the DSP as
well as the ASIC.
Input:
dev - device structure
Output:
none
-------------------------------------------------------------------------*/
static void ft1000_hbchk(u_long data)
{
struct net_device *dev = (struct net_device *)data;
struct ft1000_info *info;
u16 tempword;
info = netdev_priv(dev);
if (info->CardReady == 1) {
/* Perform dsp heartbeat check */
if (info->AsicID == ELECTRABUZZ_ID) {
tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
} else {
tempword =
ntohs(ft1000_read_dpram_mag_16
(dev, FT1000_MAG_HI_HO,
FT1000_MAG_HI_HO_INDX));
}
pr_debug("hi_ho value = 0x%x\n", tempword);
/* Let's perform another check if ho is not detected */
if (tempword != ho) {
if (info->AsicID == ELECTRABUZZ_ID)
tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
else
tempword = ntohs(ft1000_read_dpram_mag_16(dev,
FT1000_MAG_HI_HO,
FT1000_MAG_HI_HO_INDX));
}
if (tempword != ho) {
pr_info("heartbeat failed - no ho detected\n");
ft1000_read_dsp_timer(dev, info);
info->DrvErrNum = DSP_HB_INFO;
if (ft1000_reset_card(dev) == 0) {
pr_info("Hardware Failure Detected - PC Card disabled\n");
info->ProgConStat = 0xff;
return;
}
/* Schedule this module to run every 2 seconds */
poll_timer.expires = jiffies + (2*HZ);
poll_timer.data = (u_long)dev;
add_timer(&poll_timer);
return;
}
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
/* Let's check doorbell again if fail */
if (tempword & FT1000_DB_HB)
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_HB) {
pr_info("heartbeat doorbell not clear by firmware\n");
ft1000_read_dsp_timer(dev, info);
info->DrvErrNum = DSP_HB_INFO;
if (ft1000_reset_card(dev) == 0) {
pr_info("Hardware Failure Detected - PC Card disabled\n");
info->ProgConStat = 0xff;
return;
}
/* Schedule this module to run every 2 seconds */
poll_timer.expires = jiffies + (2*HZ);
poll_timer.data = (u_long)dev;
add_timer(&poll_timer);
return;
}
/*
* Set dedicated area to hi and ring appropriate doorbell
* according to hi/ho heartbeat protocol
*/
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_dpram(dev, FT1000_HI_HO, hi);
} else {
ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag,
FT1000_MAG_HI_HO_INDX);
}
if (info->AsicID == ELECTRABUZZ_ID) {
tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
} else {
tempword =
ntohs(ft1000_read_dpram_mag_16
(dev, FT1000_MAG_HI_HO,
FT1000_MAG_HI_HO_INDX));
}
/* Let's write hi again if fail */
if (tempword != hi) {
if (info->AsicID == ELECTRABUZZ_ID)
ft1000_write_dpram(dev, FT1000_HI_HO, hi);
else
ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO,
hi_mag, FT1000_MAG_HI_HO_INDX);
if (info->AsicID == ELECTRABUZZ_ID)
tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
else
tempword = ntohs(ft1000_read_dpram_mag_16(dev,
FT1000_MAG_HI_HO,
FT1000_MAG_HI_HO_INDX));
}
if (tempword != hi) {
pr_info("heartbeat failed - cannot write hi into DPRAM\n");
ft1000_read_dsp_timer(dev, info);
info->DrvErrNum = DSP_HB_INFO;
if (ft1000_reset_card(dev) == 0) {
pr_info("Hardware Failure Detected - PC Card disabled\n");
info->ProgConStat = 0xff;
return;
}
/* Schedule this module to run every 2 seconds */
poll_timer.expires = jiffies + (2*HZ);
poll_timer.data = (u_long)dev;
add_timer(&poll_timer);
return;
}
ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_HB);
}
/* Schedule this module to run every 2 seconds */
poll_timer.expires = jiffies + (2 * HZ);
poll_timer.data = (u_long)dev;
add_timer(&poll_timer);
}
/*---------------------------------------------------------------------------
Function: ft1000_send_cmd
Description:
Input:
Output:
-------------------------------------------------------------------------*/
static void ft1000_send_cmd(struct net_device *dev, u16 *ptempbuffer, int size,
u16 qtype)
{
struct ft1000_info *info = netdev_priv(dev);
int i;
u16 tempword;
unsigned long flags;
size += sizeof(struct pseudo_hdr);
/* check for odd byte and increment to 16-bit word align value */
if ((size & 0x0001))
size++;
pr_debug("total length = %d\n", size);
pr_debug("length = %d\n", ntohs(*ptempbuffer));
/*
* put message into slow queue area
* All messages are in the form total_len + pseudo header + message body
*/
spin_lock_irqsave(&info->dpram_lock, flags);
/* Make sure SLOWQ doorbell is clear */
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
i = 0;
while (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
i++;
if (i == 10) {
spin_unlock_irqrestore(&info->dpram_lock, flags);
return;
}
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
}
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_TX_BASE);
/* Write total length to dpram */
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
/* Write pseudo header and messgae body */
for (i = 0; i < (size >> 1); i++) {
pr_debug("data %d = 0x%x\n", i, *ptempbuffer);
tempword = htons(*ptempbuffer++);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, tempword);
}
} else {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_TX_BASE);
/* Write total length to dpram */
ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, htons(size));
/* Write pseudo header and messgae body */
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_TX_BASE + 1);
for (i = 0; i < (size >> 2); i++) {
pr_debug("data = 0x%x\n", *ptempbuffer);
outw(*ptempbuffer++,
dev->base_addr + FT1000_REG_MAG_DPDATAL);
pr_debug("data = 0x%x\n", *ptempbuffer);
outw(*ptempbuffer++,
dev->base_addr + FT1000_REG_MAG_DPDATAH);
}
pr_debug("data = 0x%x\n", *ptempbuffer);
outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAL);
pr_debug("data = 0x%x\n", *ptempbuffer);
outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAH);
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
/* ring doorbell to notify DSP that we have a message ready */
ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_TX);
}
/*---------------------------------------------------------------------------
Function: ft1000_receive_cmd
Description: This function will read a message from the dpram area.
Input:
dev - network device structure
pbuffer - caller supply address to buffer
pnxtph - pointer to next pseudo header
Output:
Status = 0 (unsuccessful)
= 1 (successful)
-------------------------------------------------------------------------*/
static bool ft1000_receive_cmd(struct net_device *dev, u16 *pbuffer,
int maxsz, u16 *pnxtph)
{
struct ft1000_info *info = netdev_priv(dev);
u16 size;
u16 *ppseudohdr;
int i;
u16 tempword;
unsigned long flags;
if (info->AsicID == ELECTRABUZZ_ID) {
size = ft1000_read_dpram(dev, *pnxtph)
+ sizeof(struct pseudo_hdr);
} else {
size = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_PH_LEN,
FT1000_MAG_PH_LEN_INDX))
+ sizeof(struct pseudo_hdr);
}
if (size > maxsz) {
pr_debug("Invalid command length = %d\n", size);
return false;
}
ppseudohdr = (u16 *)pbuffer;
spin_lock_irqsave(&info->dpram_lock, flags);
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_RX_BASE + 2);
for (i = 0; i <= (size >> 1); i++) {
tempword =
ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
*pbuffer++ = ntohs(tempword);
}
} else {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_RX_BASE);
*pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH);
pr_debug("received data = 0x%x\n", *pbuffer);
pbuffer++;
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_RX_BASE + 1);
for (i = 0; i <= (size >> 2); i++) {
*pbuffer =
inw(dev->base_addr +
FT1000_REG_MAG_DPDATAL);
pbuffer++;
*pbuffer =
inw(dev->base_addr +
FT1000_REG_MAG_DPDATAH);
pbuffer++;
}
/* copy odd aligned word */
*pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAL);
pr_debug("received data = 0x%x\n", *pbuffer);
pbuffer++;
*pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH);
pr_debug("received data = 0x%x\n", *pbuffer);
pbuffer++;
}
if (size & 0x0001) {
/* copy odd byte from fifo */
tempword = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
*pbuffer = ntohs(tempword);
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
/*
* Check if pseudo header checksum is good
* Calculate pseudo header checksum
*/
tempword = *ppseudohdr++;
for (i = 1; i < 7; i++)
tempword ^= *ppseudohdr++;
if (tempword != *ppseudohdr) {
pr_debug("Pseudo header checksum mismatch\n");
/* Drop this message */
return false;
}
return true;
}
/*---------------------------------------------------------------------------
Function: ft1000_proc_drvmsg
Description: This function will process the various driver messages.
Input:
dev - device structure
pnxtph - pointer to next pseudo header
Output:
none
-------------------------------------------------------------------------*/
static void ft1000_proc_drvmsg(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
u16 msgtype;
u16 tempword;
struct media_msg *pmediamsg;
struct dsp_init_msg *pdspinitmsg;
struct drv_msg *pdrvmsg;
u16 len;
u16 i;
struct prov_record *ptr;
struct pseudo_hdr *ppseudo_hdr;
u16 *pmsg;
struct timeval tv;
union {
u8 byte[2];
u16 wrd;
} convert;
if (info->AsicID == ELECTRABUZZ_ID)
tempword = FT1000_DPRAM_RX_BASE+2;
else
tempword = FT1000_DPRAM_MAG_RX_BASE;
if (ft1000_receive_cmd(dev, &cmdbuffer[0], MAX_CMD_SQSIZE, &tempword)) {
/*
* Get the message type which is total_len + PSEUDO header
* + msgtype + message body
*/
pdrvmsg = (struct drv_msg *)&cmdbuffer[0];
msgtype = ntohs(pdrvmsg->type);
pr_debug("Command message type = 0x%x\n", msgtype);
switch (msgtype) {
case DSP_PROVISION:
pr_debug("Got a provisioning request message from DSP\n");
mdelay(25);
while (list_empty(&info->prov_list) == 0) {
pr_debug("Sending a provisioning message\n");
/* Make sure SLOWQ doorbell is clear */
tempword = ft1000_read_reg(dev,
FT1000_REG_DOORBELL);
i = 0;
while (tempword & FT1000_DB_DPRAM_TX) {
mdelay(5);
i++;
if (i == 10)
break;
}
ptr = list_entry(info->prov_list.next,
struct prov_record, list);
len = *(u16 *)ptr->pprov_data;
len = htons(len);
pmsg = (u16 *)ptr->pprov_data;
ppseudo_hdr = (struct pseudo_hdr *)pmsg;
/* Insert slow queue sequence number */
ppseudo_hdr->seq_num = info->squeseqnum++;
ppseudo_hdr->portsrc = 0;
/* Calculate new checksum */
ppseudo_hdr->checksum = *pmsg++;
pr_debug("checksum = 0x%x\n",
ppseudo_hdr->checksum);
for (i = 1; i < 7; i++) {
ppseudo_hdr->checksum ^= *pmsg++;
pr_debug("checksum = 0x%x\n",
ppseudo_hdr->checksum);
}
ft1000_send_cmd(dev, (u16 *)ptr->pprov_data,
len, SLOWQ_TYPE);
list_del(&ptr->list);
kfree(ptr->pprov_data);
kfree(ptr);
}
/*
* Indicate adapter is ready to take application
* messages after all provisioning messages are sent
*/
info->CardReady = 1;
break;
case MEDIA_STATE:
pmediamsg = (struct media_msg *)&cmdbuffer[0];
if (info->ProgConStat != 0xFF) {
if (pmediamsg->state) {
pr_debug("Media is up\n");
if (info->mediastate == 0) {
netif_carrier_on(dev);
netif_wake_queue(dev);
info->mediastate = 1;
do_gettimeofday(&tv);
info->ConTm = tv.tv_sec;
}
} else {
pr_debug("Media is down\n");
if (info->mediastate == 1) {
info->mediastate = 0;
netif_carrier_off(dev);
netif_stop_queue(dev);
info->ConTm = 0;
}
}
} else {
pr_debug("Media is down\n");
if (info->mediastate == 1) {
info->mediastate = 0;
netif_carrier_off(dev);
netif_stop_queue(dev);
info->ConTm = 0;
}
}
break;
case DSP_INIT_MSG:
pdspinitmsg = (struct dsp_init_msg *)&cmdbuffer[0];
memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ);
pr_debug("DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n",
info->DspVer[0], info->DspVer[1],
info->DspVer[2], info->DspVer[3]);
memcpy(info->HwSerNum, pdspinitmsg->HwSerNum,
HWSERNUMSZ);
memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ);
memcpy(info->eui64, pdspinitmsg->eui64, EUISZ);
dev->dev_addr[0] = info->eui64[0];
dev->dev_addr[1] = info->eui64[1];
dev->dev_addr[2] = info->eui64[2];
dev->dev_addr[3] = info->eui64[5];
dev->dev_addr[4] = info->eui64[6];
dev->dev_addr[5] = info->eui64[7];
if (ntohs(pdspinitmsg->length) ==
(sizeof(struct dsp_init_msg) - 20)) {
memcpy(info->ProductMode,
pdspinitmsg->ProductMode, MODESZ);
memcpy(info->RfCalVer, pdspinitmsg->RfCalVer,
CALVERSZ);
memcpy(info->RfCalDate, pdspinitmsg->RfCalDate,
CALDATESZ);
pr_debug("RFCalVer = 0x%2x 0x%2x\n",
info->RfCalVer[0], info->RfCalVer[1]);
}
break;
case DSP_STORE_INFO:
pr_debug("Got DSP_STORE_INFO\n");
tempword = ntohs(pdrvmsg->length);
info->DSPInfoBlklen = tempword;
if (tempword < (MAX_DSP_SESS_REC - 4)) {
pmsg = (u16 *)&pdrvmsg->data[0];
for (i = 0; i < ((tempword + 1) / 2); i++) {
pr_debug("dsp info data = 0x%x\n",
*pmsg);
info->DSPInfoBlk[i + 10] = *pmsg++;
}
}
break;
case DSP_GET_INFO:
pr_debug("Got DSP_GET_INFO\n");
/*
* copy dsp info block to dsp
* allow any outstanding ioctl to finish
*/
mdelay(10);
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
tempword = ft1000_read_reg(dev,
FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX)
mdelay(10);
}
if ((tempword & FT1000_DB_DPRAM_TX) == 0) {
/*
* Put message into Slow Queue
* Form Pseudo header
*/
pmsg = (u16 *)info->DSPInfoBlk;
ppseudo_hdr = (struct pseudo_hdr *)pmsg;
ppseudo_hdr->length =
htons(info->DSPInfoBlklen + 4);
ppseudo_hdr->source = 0x10;
ppseudo_hdr->destination = 0x20;
ppseudo_hdr->portdest = 0;
ppseudo_hdr->portsrc = 0;
ppseudo_hdr->sh_str_id = 0;
ppseudo_hdr->control = 0;
ppseudo_hdr->rsvd1 = 0;
ppseudo_hdr->rsvd2 = 0;
ppseudo_hdr->qos_class = 0;
/* Insert slow queue sequence number */
ppseudo_hdr->seq_num = info->squeseqnum++;
/* Insert application id */
ppseudo_hdr->portsrc = 0;
/* Calculate new checksum */
ppseudo_hdr->checksum = *pmsg++;
for (i = 1; i < 7; i++)
ppseudo_hdr->checksum ^= *pmsg++;
info->DSPInfoBlk[8] = 0x7200;
info->DSPInfoBlk[9] =
htons(info->DSPInfoBlklen);
ft1000_send_cmd(dev, info->DSPInfoBlk,
(u16)(info->DSPInfoBlklen+4),
0);
}
break;
case GET_DRV_ERR_RPT_MSG:
pr_debug("Got GET_DRV_ERR_RPT_MSG\n");
/*
* copy driver error message to dsp
* allow any outstanding ioctl to finish
*/
mdelay(10);
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
tempword = ft1000_read_reg(dev,
FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX)
mdelay(10);
}
if ((tempword & FT1000_DB_DPRAM_TX) == 0) {
/*
* Put message into Slow Queue
* Form Pseudo header
*/
pmsg = (u16 *)&tempbuffer[0];
ppseudo_hdr = (struct pseudo_hdr *)pmsg;
ppseudo_hdr->length = htons(0x0012);
ppseudo_hdr->source = 0x10;
ppseudo_hdr->destination = 0x20;
ppseudo_hdr->portdest = 0;
ppseudo_hdr->portsrc = 0;
ppseudo_hdr->sh_str_id = 0;
ppseudo_hdr->control = 0;
ppseudo_hdr->rsvd1 = 0;
ppseudo_hdr->rsvd2 = 0;
ppseudo_hdr->qos_class = 0;
/* Insert slow queue sequence number */
ppseudo_hdr->seq_num = info->squeseqnum++;
/* Insert application id */
ppseudo_hdr->portsrc = 0;
/* Calculate new checksum */
ppseudo_hdr->checksum = *pmsg++;
for (i = 1; i < 7; i++)
ppseudo_hdr->checksum ^= *pmsg++;
pmsg = (u16 *)&tempbuffer[16];
*pmsg++ = htons(RSP_DRV_ERR_RPT_MSG);
*pmsg++ = htons(0x000e);
*pmsg++ = htons(info->DSP_TIME[0]);
*pmsg++ = htons(info->DSP_TIME[1]);
*pmsg++ = htons(info->DSP_TIME[2]);
*pmsg++ = htons(info->DSP_TIME[3]);
convert.byte[0] = info->DspVer[0];
convert.byte[1] = info->DspVer[1];
*pmsg++ = convert.wrd;
convert.byte[0] = info->DspVer[2];
convert.byte[1] = info->DspVer[3];
*pmsg++ = convert.wrd;
*pmsg++ = htons(info->DrvErrNum);
ft1000_send_cmd(dev, (u16 *)&tempbuffer[0],
(u16)(0x0012), 0);
info->DrvErrNum = 0;
}
break;
default:
break;
}
}
}
/*---------------------------------------------------------------------------
Function: ft1000_parse_dpram_msg
Description: This function will parse the message received from the DSP
via the DPRAM interface.
Input:
dev - device structure
Output:
status - FAILURE
SUCCESS
-------------------------------------------------------------------------*/
static int ft1000_parse_dpram_msg(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
u16 doorbell;
u16 portid;
u16 nxtph;
u16 total_len;
int i = 0;
unsigned long flags;
doorbell = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
pr_debug("Doorbell = 0x%x\n", doorbell);
if (doorbell & FT1000_ASIC_RESET_REQ) {
/* Copy DSP session record from info block */
spin_lock_irqsave(&info->dpram_lock, flags);
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_RX_BASE);
for (i = 0; i < MAX_DSP_SESS_REC; i++) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA,
info->DSPSess.Rec[i]);
}
} else {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_RX_BASE);
for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) {
outl(info->DSPSess.MagRec[i],
dev->base_addr + FT1000_REG_MAG_DPDATA);
}
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
/* clear ASIC RESET request */
ft1000_write_reg(dev, FT1000_REG_DOORBELL,
FT1000_ASIC_RESET_REQ);
pr_debug("Got an ASIC RESET Request\n");
ft1000_write_reg(dev, FT1000_REG_DOORBELL,
FT1000_ASIC_RESET_DSP);
if (info->AsicID == MAGNEMITE_ID) {
/* Setting MAGNEMITE ASIC to big endian mode */
ft1000_write_reg(dev, FT1000_REG_SUP_CTRL,
HOST_INTF_BE);
}
}
if (doorbell & FT1000_DSP_ASIC_RESET) {
pr_debug("Got a dsp ASIC reset message\n");
ft1000_write_reg(dev, FT1000_REG_DOORBELL,
FT1000_DSP_ASIC_RESET);
udelay(200);
return SUCCESS;
}
if (doorbell & FT1000_DB_DPRAM_RX) {
pr_debug("Got a slow queue message\n");
nxtph = FT1000_DPRAM_RX_BASE + 2;
if (info->AsicID == ELECTRABUZZ_ID) {
total_len =
ft1000_read_dpram(dev, FT1000_DPRAM_RX_BASE);
} else {
total_len =
ntohs(ft1000_read_dpram_mag_16
(dev, FT1000_MAG_TOTAL_LEN,
FT1000_MAG_TOTAL_LEN_INDX));
}
pr_debug("total length = %d\n", total_len);
if ((total_len < MAX_CMD_SQSIZE)
&& (total_len > sizeof(struct pseudo_hdr))) {
total_len += nxtph;
/*
* ft1000_read_reg will return a value that needs to be
* byteswap in order to get DSP_QID_OFFSET.
*/
if (info->AsicID == ELECTRABUZZ_ID) {
portid = (ft1000_read_dpram(dev, DSP_QID_OFFSET
+ FT1000_DPRAM_RX_BASE + 2)
>> 8) & 0xff;
} else {
portid =
ft1000_read_dpram_mag_16
(dev, FT1000_MAG_PORT_ID,
FT1000_MAG_PORT_ID_INDX) & 0xff;
}
pr_debug("DSP_QID = 0x%x\n", portid);
if (portid == DRIVERID) {
/*
* We are assumming one driver message from the
* DSP at a time.
*/
ft1000_proc_drvmsg(dev);
}
}
ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_RX);
}
if (doorbell & FT1000_DB_COND_RESET) {
/* Reset ASIC and DSP */
ft1000_read_dsp_timer(dev, info);
info->DrvErrNum = DSP_CONDRESET_INFO;
pr_debug("DSP conditional reset requested\n");
ft1000_reset_card(dev);
ft1000_write_reg(dev, FT1000_REG_DOORBELL,
FT1000_DB_COND_RESET);
}
/* let's clear any unexpected doorbells from DSP */
doorbell =
doorbell & ~(FT1000_DB_DPRAM_RX | FT1000_ASIC_RESET_REQ |
FT1000_DB_COND_RESET | 0xff00);
if (doorbell) {
pr_debug("Clearing unexpected doorbell = 0x%x\n", doorbell);
ft1000_write_reg(dev, FT1000_REG_DOORBELL, doorbell);
}
return SUCCESS;
}
/*---------------------------------------------------------------------------
Function: ft1000_flush_fifo
Description: This function will flush one packet from the downlink
FIFO.
Input:
dev - device structure
drv_err - driver error causing the flush fifo
Output:
None.
-------------------------------------------------------------------------*/
static void ft1000_flush_fifo(struct net_device *dev, u16 DrvErrNum)
{
struct ft1000_info *info = netdev_priv(dev);
struct ft1000_pcmcia *pcmcia = info->priv;
u16 i;
u32 templong;
u16 tempword;
if (pcmcia->PktIntfErr > MAX_PH_ERR) {
ft1000_read_dsp_timer(dev, info);
info->DrvErrNum = DrvErrNum;
ft1000_reset_card(dev);
return;
}
/* Flush corrupted pkt from FIFO */
i = 0;
do {
if (info->AsicID == ELECTRABUZZ_ID) {
tempword =
ft1000_read_reg(dev, FT1000_REG_DFIFO);
tempword =
ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT);
} else {
templong =
inl(dev->base_addr + FT1000_REG_MAG_DFR);
tempword =
inw(dev->base_addr + FT1000_REG_MAG_DFSR);
}
i++;
/*
* This should never happen unless the ASIC is broken.
* We must reset to recover.
*/
if ((i > 2048) || (tempword == 0)) {
ft1000_read_dsp_timer(dev, info);
if (tempword == 0) {
/*
* Let's check if ASIC reads are still ok by
* reading the Mask register which is never zero
* at this point of the code.
*/
tempword =
inw(dev->base_addr +
FT1000_REG_SUP_IMASK);
if (tempword == 0) {
/*
* This indicates that we can not
* communicate with the ASIC
*/
info->DrvErrNum = FIFO_FLUSH_BADCNT;
} else {
/*
* Let's assume that we really flush
* the FIFO
*/
pcmcia->PktIntfErr++;
return;
}
} else {
info->DrvErrNum = FIFO_FLUSH_MAXLIMIT;
}
return;
}
tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT);
} while ((tempword & 0x03) != 0x03);
if (info->AsicID == ELECTRABUZZ_ID) {
i++;
pr_debug("Flushing FIFO complete = %x\n", tempword);
/* Flush last word in FIFO. */
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
/* Update FIFO counter for DSP */
i = i * 2;
pr_debug("Flush Data byte count to dsp = %d\n", i);
info->fifo_cnt += i;
ft1000_write_dpram(dev, FT1000_FIFO_LEN,
info->fifo_cnt);
} else {
pr_debug("Flushing FIFO complete = %x\n", tempword);
/* Flush last word in FIFO */
templong = inl(dev->base_addr + FT1000_REG_MAG_DFR);
tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT);
pr_debug("FT1000_REG_SUP_STAT = 0x%x\n", tempword);
tempword = inw(dev->base_addr + FT1000_REG_MAG_DFSR);
pr_debug("FT1000_REG_MAG_DFSR = 0x%x\n", tempword);
}
if (DrvErrNum)
pcmcia->PktIntfErr++;
}
/*---------------------------------------------------------------------------
Function: ft1000_copy_up_pkt
Description: This function will pull Flarion packets out of the Downlink
FIFO and convert it to an ethernet packet. The ethernet packet will
then be deliver to the TCP/IP stack.
Input:
dev - device structure
Output:
status - FAILURE
SUCCESS
-------------------------------------------------------------------------*/
static int ft1000_copy_up_pkt(struct net_device *dev)
{
u16 tempword;
struct ft1000_info *info = netdev_priv(dev);
u16 len;
struct sk_buff *skb;
u16 i;
u8 *pbuffer = NULL;
u8 *ptemp = NULL;
u16 chksum;
u32 *ptemplong;
u32 templong;
/* Read length */
if (info->AsicID == ELECTRABUZZ_ID) {
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
len = tempword;
} else {
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
len = ntohs(tempword);
}
chksum = tempword;
pr_debug("Number of Bytes in FIFO = %d\n", len);
if (len > ENET_MAX_SIZE) {
pr_debug("size of ethernet packet invalid\n");
if (info->AsicID == MAGNEMITE_ID) {
/* Read High word to complete 32 bit access */
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
}
ft1000_flush_fifo(dev, DSP_PKTLEN_INFO);
info->stats.rx_errors++;
return FAILURE;
}
skb = dev_alloc_skb(len + 12 + 2);
if (skb == NULL) {
/* Read High word to complete 32 bit access */
if (info->AsicID == MAGNEMITE_ID)
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
ft1000_flush_fifo(dev, 0);
info->stats.rx_errors++;
return FAILURE;
}
pbuffer = (u8 *)skb_put(skb, len + 12);
/* Pseudo header */
if (info->AsicID == ELECTRABUZZ_ID) {
for (i = 1; i < 7; i++) {
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
chksum ^= tempword;
}
/* read checksum value */
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
} else {
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
pr_debug("Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
pr_debug("Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
pr_debug("Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
pr_debug("Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
pr_debug("Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
pr_debug("Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
/* read checksum value */
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
pr_debug("Pseudo = 0x%x\n", tempword);
}
if (chksum != tempword) {
pr_debug("Packet checksum mismatch 0x%x 0x%x\n",
chksum, tempword);
ft1000_flush_fifo(dev, DSP_PKTPHCKSUM_INFO);
info->stats.rx_errors++;
kfree_skb(skb);
return FAILURE;
}
/* subtract the number of bytes read already */
ptemp = pbuffer;
/* fake MAC address */
*pbuffer++ = dev->dev_addr[0];
*pbuffer++ = dev->dev_addr[1];
*pbuffer++ = dev->dev_addr[2];
*pbuffer++ = dev->dev_addr[3];
*pbuffer++ = dev->dev_addr[4];
*pbuffer++ = dev->dev_addr[5];
*pbuffer++ = 0x00;
*pbuffer++ = 0x07;
*pbuffer++ = 0x35;
*pbuffer++ = 0xff;
*pbuffer++ = 0xff;
*pbuffer++ = 0xfe;
if (info->AsicID == ELECTRABUZZ_ID) {
for (i = 0; i < len / 2; i++) {
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
*pbuffer++ = (u8) (tempword >> 8);
*pbuffer++ = (u8)tempword;
if (ft1000_chkcard(dev) == false) {
kfree_skb(skb);
return FAILURE;
}
}
/* Need to read one more word if odd byte */
if (len & 0x0001) {
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
*pbuffer++ = (u8) (tempword >> 8);
}
} else {
ptemplong = (u32 *)pbuffer;
for (i = 0; i < len / 4; i++) {
templong = inl(dev->base_addr + FT1000_REG_MAG_DFR);
pr_debug("Data = 0x%8x\n", templong);
*ptemplong++ = templong;
}
/* Need to read one more word if odd align. */
if (len & 0x0003) {
templong = inl(dev->base_addr + FT1000_REG_MAG_DFR);
pr_debug("Data = 0x%8x\n", templong);
*ptemplong++ = templong;
}
}
pr_debug("Data passed to Protocol layer:\n");
for (i = 0; i < len + 12; i++)
pr_debug("Protocol Data: 0x%x\n", *ptemp++);
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
netif_rx(skb);
info->stats.rx_packets++;
/* Add on 12 bytes for MAC address which was removed */
info->stats.rx_bytes += (len + 12);
if (info->AsicID == ELECTRABUZZ_ID) {
/* track how many bytes have been read from FIFO - round up to
* 16 bit word */
tempword = len + 16;
if (tempword & 0x01)
tempword++;
info->fifo_cnt += tempword;
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_FIFO_LEN);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, info->fifo_cnt);
}
return SUCCESS;
}
/*---------------------------------------------------------------------------
Function: ft1000_copy_down_pkt
Description: This function will take an ethernet packet and convert it to
a Flarion packet prior to sending it to the ASIC Downlink
FIFO.
Input:
dev - device structure
packet - address of ethernet packet
len - length of IP packet
Output:
status - FAILURE
SUCCESS
-------------------------------------------------------------------------*/
static int ft1000_copy_down_pkt(struct net_device *dev, u16 *packet, u16 len)
{
struct ft1000_info *info = netdev_priv(dev);
struct ft1000_pcmcia *pcmcia = info->priv;
union {
struct pseudo_hdr blk;
u16 buff[sizeof(struct pseudo_hdr) >> 1];
u8 buffc[sizeof(struct pseudo_hdr)];
} pseudo;
int i;
u32 *plong;
/* Check if there is room on the FIFO */
if (len > ft1000_read_fifo_len(dev)) {
udelay(10);
if (len > ft1000_read_fifo_len(dev))
udelay(20);
if (len > ft1000_read_fifo_len(dev))
udelay(20);
if (len > ft1000_read_fifo_len(dev))
udelay(20);
if (len > ft1000_read_fifo_len(dev))
udelay(20);
if (len > ft1000_read_fifo_len(dev))
udelay(20);
if (len > ft1000_read_fifo_len(dev)) {
pr_debug("Transmit FIFO is full - pkt drop\n");
info->stats.tx_errors++;
return SUCCESS;
}
}
/* Create pseudo header and send pseudo/ip to hardware */
if (info->AsicID == ELECTRABUZZ_ID)
pseudo.blk.length = len;
else
pseudo.blk.length = ntohs(len);
pseudo.blk.source = DSPID; /* Need to swap to get in correct
order */
pseudo.blk.destination = HOSTID;
pseudo.blk.portdest = NETWORKID; /* Need to swap to get in
correct order */
pseudo.blk.portsrc = DSPAIRID;
pseudo.blk.sh_str_id = 0;
pseudo.blk.control = 0;
pseudo.blk.rsvd1 = 0;
pseudo.blk.seq_num = 0;
pseudo.blk.rsvd2 = pcmcia->packetseqnum++;
pseudo.blk.qos_class = 0;
/* Calculate pseudo header checksum */
pseudo.blk.checksum = pseudo.buff[0];
for (i = 1; i < 7; i++)
pseudo.blk.checksum ^= pseudo.buff[i];
/* Production Mode */
if (info->AsicID == ELECTRABUZZ_ID) {
/* copy first word to UFIFO_BEG reg */
ft1000_write_reg(dev, FT1000_REG_UFIFO_BEG, pseudo.buff[0]);
pr_debug("data 0 BEG = 0x%04x\n", pseudo.buff[0]);
/* copy subsequent words to UFIFO_MID reg */
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[1]);
pr_debug("data 1 MID = 0x%04x\n", pseudo.buff[1]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[2]);
pr_debug("data 2 MID = 0x%04x\n", pseudo.buff[2]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[3]);
pr_debug("data 3 MID = 0x%04x\n", pseudo.buff[3]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[4]);
pr_debug("data 4 MID = 0x%04x\n", pseudo.buff[4]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[5]);
pr_debug("data 5 MID = 0x%04x\n", pseudo.buff[5]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[6]);
pr_debug("data 6 MID = 0x%04x\n", pseudo.buff[6]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[7]);
pr_debug("data 7 MID = 0x%04x\n", pseudo.buff[7]);
/* Write PPP type + IP Packet into Downlink FIFO */
for (i = 0; i < (len >> 1) - 1; i++) {
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID,
htons(*packet));
pr_debug("data %d MID = 0x%04x\n",
i + 8, htons(*packet));
packet++;
}
/* Check for odd byte */
if (len & 0x0001) {
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID,
htons(*packet));
pr_debug("data MID = 0x%04x\n", htons(*packet));
packet++;
ft1000_write_reg(dev, FT1000_REG_UFIFO_END,
htons(*packet));
pr_debug("data %d MID = 0x%04x\n",
i + 8, htons(*packet));
} else {
ft1000_write_reg(dev, FT1000_REG_UFIFO_END,
htons(*packet));
pr_debug("data %d MID = 0x%04x\n",
i + 8, htons(*packet));
}
} else {
outl(*(u32 *)&pseudo.buff[0],
dev->base_addr + FT1000_REG_MAG_UFDR);
pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[0]);
outl(*(u32 *)&pseudo.buff[2],
dev->base_addr + FT1000_REG_MAG_UFDR);
pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[2]);
outl(*(u32 *)&pseudo.buff[4],
dev->base_addr + FT1000_REG_MAG_UFDR);
pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[4]);
outl(*(u32 *)&pseudo.buff[6],
dev->base_addr + FT1000_REG_MAG_UFDR);
pr_debug("Pseudo = 0x%8x\n", *(u32 *)&pseudo.buff[6]);
plong = (u32 *)packet;
/* Write PPP type + IP Packet into Downlink FIFO */
for (i = 0; i < (len >> 2); i++)
outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR);
/* Check for odd alignment */
if (len & 0x0003) {
pr_debug("data = 0x%8x\n", *plong);
outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR);
}
outl(1, dev->base_addr + FT1000_REG_MAG_UFER);
}
info->stats.tx_packets++;
/* Add 14 bytes for MAC address plus ethernet type */
info->stats.tx_bytes += (len + 14);
return SUCCESS;
}
static struct net_device_stats *ft1000_stats(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
return &info->stats;
}
static int ft1000_open(struct net_device *dev)
{
ft1000_reset_card(dev);
/* schedule ft1000_hbchk to perform periodic heartbeat checks on DSP
* and ASIC */
init_timer(&poll_timer);
poll_timer.expires = jiffies + (2 * HZ);
poll_timer.data = (u_long)dev;
add_timer(&poll_timer);
return 0;
}
static int ft1000_close(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
info->CardReady = 0;
del_timer(&poll_timer);
if (ft1000_card_present == 1) {
pr_debug("Media is down\n");
netif_stop_queue(dev);
ft1000_disable_interrupts(dev);
ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT);
/* reset ASIC */
ft1000_reset_asic(dev);
}
return 0;
}
static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
u8 *pdata;
if (skb == NULL) {
pr_debug("skb == NULL!!!\n");
return 0;
}
pr_debug("length of packet = %d\n", skb->len);
pdata = (u8 *)skb->data;
if (info->mediastate == 0) {
/* Drop packet is mediastate is down */
pr_debug("mediastate is down\n");
return SUCCESS;
}
if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) {
/* Drop packet which has invalid size */
pr_debug("invalid ethernet length\n");
return SUCCESS;
}
ft1000_copy_down_pkt(dev, (u16 *) (pdata + ENET_HEADER_SIZE - 2),
skb->len - ENET_HEADER_SIZE + 2);
dev_kfree_skb(skb);
return 0;
}
static irqreturn_t ft1000_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
struct ft1000_info *info = netdev_priv(dev);
u16 tempword;
u16 inttype;
int cnt;
if (info->CardReady == 0) {
ft1000_disable_interrupts(dev);
return IRQ_HANDLED;
}
if (ft1000_chkcard(dev) == false) {
ft1000_disable_interrupts(dev);
return IRQ_HANDLED;
}
ft1000_disable_interrupts(dev);
/* Read interrupt type */
inttype = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
/* Make sure we process all interrupt before leaving the ISR due to the
* edge trigger interrupt type */
while (inttype) {
if (inttype & ISR_DOORBELL_PEND)
ft1000_parse_dpram_msg(dev);
if (inttype & ISR_RCV) {
pr_debug("Data in FIFO\n");
cnt = 0;
do {
/* Check if we have packets in the Downlink
* FIFO */
if (info->AsicID == ELECTRABUZZ_ID) {
tempword = ft1000_read_reg(dev,
FT1000_REG_DFIFO_STAT);
} else {
tempword = ft1000_read_reg(dev,
FT1000_REG_MAG_DFSR);
}
if (!(tempword & 0x1f))
break;
ft1000_copy_up_pkt(dev);
cnt++;
} while (cnt < MAX_RCV_LOOP);
}
/* clear interrupts */
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
pr_debug("interrupt status register = 0x%x\n", tempword);
ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword);
/* Read interrupt type */
inttype = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
pr_debug("interrupt status register after clear = 0x%x\n",
inttype);
}
ft1000_enable_interrupts(dev);
return IRQ_HANDLED;
}
void stop_ft1000_card(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
struct prov_record *ptr;
struct prov_record *tmp;
/* int cnt; */
info->CardReady = 0;
ft1000_card_present = 0;
netif_stop_queue(dev);
ft1000_disable_interrupts(dev);
/* Make sure we free any memory reserve for provisioning */
list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) {
list_del(&ptr->list);
kfree(ptr->pprov_data);
kfree(ptr);
}
kfree(info->priv);
if (info->registered) {
unregister_netdev(dev);
info->registered = 0;
}
free_irq(dev->irq, dev);
release_region(dev->base_addr, 256);
release_firmware(fw_entry);
flarion_ft1000_cnt--;
}
static void ft1000_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct ft1000_info *ft_info;
ft_info = netdev_priv(dev);
strlcpy(info->driver, "ft1000", sizeof(info->driver));
snprintf(info->bus_info, sizeof(info->bus_info), "PCMCIA 0x%lx",
dev->base_addr);
snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d.%d",
ft_info->DspVer[0], ft_info->DspVer[1], ft_info->DspVer[2],
ft_info->DspVer[3]);
}
static u32 ft1000_get_link(struct net_device *dev)
{
struct ft1000_info *info;
info = netdev_priv(dev);
return info->mediastate;
}
static const struct ethtool_ops ops = {
.get_drvinfo = ft1000_get_drvinfo,
.get_link = ft1000_get_link
};
struct net_device *init_ft1000_card(struct pcmcia_device *link,
void *ft1000_reset)
{
struct ft1000_info *info;
struct ft1000_pcmcia *pcmcia;
struct net_device *dev;
static const struct net_device_ops ft1000ops = {
.ndo_open = &ft1000_open,
.ndo_stop = &ft1000_close,
.ndo_start_xmit = &ft1000_start_xmit,
.ndo_get_stats = &ft1000_stats,
};
pr_debug("irq = %d, port = 0x%04llx\n",
link->irq, (unsigned long long)link->resource[0]->start);
flarion_ft1000_cnt++;
if (flarion_ft1000_cnt > 1) {
flarion_ft1000_cnt--;
dev_info(&link->dev,
"This driver can not support more than one instance\n");
return NULL;
}
dev = alloc_etherdev(sizeof(struct ft1000_info));
if (!dev) {
dev_err(&link->dev, "Failed to allocate etherdev\n");
return NULL;
}
SET_NETDEV_DEV(dev, &link->dev);
info = netdev_priv(dev);
memset(info, 0, sizeof(struct ft1000_info));
pr_debug("address of dev = 0x%p\n", dev);
pr_debug("address of dev info = 0x%p\n", info);
pr_debug("device name = %s\n", dev->name);
memset(&info->stats, 0, sizeof(struct net_device_stats));
info->priv = kzalloc(sizeof(struct ft1000_pcmcia), GFP_KERNEL);
pcmcia = info->priv;
pcmcia->link = link;
spin_lock_init(&info->dpram_lock);
info->DrvErrNum = 0;
info->registered = 1;
info->ft1000_reset = ft1000_reset;
info->mediastate = 0;
info->fifo_cnt = 0;
info->CardReady = 0;
info->DSP_TIME[0] = 0;
info->DSP_TIME[1] = 0;
info->DSP_TIME[2] = 0;
info->DSP_TIME[3] = 0;
flarion_ft1000_cnt = 0;
INIT_LIST_HEAD(&info->prov_list);
info->squeseqnum = 0;
/* dev->hard_start_xmit = &ft1000_start_xmit; */
/* dev->get_stats = &ft1000_stats; */
/* dev->open = &ft1000_open; */
/* dev->stop = &ft1000_close; */
dev->netdev_ops = &ft1000ops;
pr_debug("device name = %s\n", dev->name);
dev->irq = link->irq;
dev->base_addr = link->resource[0]->start;
if (pcmcia_get_mac_from_cis(link, dev)) {
netdev_err(dev, "Could not read mac address\n");
goto err_dev;
}
if (request_irq(dev->irq, ft1000_interrupt, IRQF_SHARED, dev->name,
dev)) {
netdev_err(dev, "Could not request_irq\n");
goto err_dev;
}
if (request_region(dev->base_addr, 256, dev->name) == NULL) {
netdev_err(dev, "Could not request_region\n");
goto err_irq;
}
if (register_netdev(dev)) {
pr_debug("Could not register netdev\n");
goto err_reg;
}
info->AsicID = ft1000_read_reg(dev, FT1000_REG_ASIC_ID);
if (info->AsicID == ELECTRABUZZ_ID) {
pr_debug("ELECTRABUZZ ASIC\n");
if (request_firmware(&fw_entry, "ft1000.img",
&link->dev) != 0) {
pr_info("Could not open ft1000.img\n");
goto err_unreg;
}
} else {
pr_debug("MAGNEMITE ASIC\n");
if (request_firmware(&fw_entry, "ft2000.img",
&link->dev) != 0) {
pr_info("Could not open ft2000.img\n");
goto err_unreg;
}
}
ft1000_enable_interrupts(dev);
ft1000_card_present = 1;
dev->ethtool_ops = &ops;
pr_info("%s: addr 0x%04lx irq %d, MAC addr %pM\n",
dev->name, dev->base_addr, dev->irq, dev->dev_addr);
return dev;
err_unreg:
unregister_netdev(dev);
err_reg:
release_region(dev->base_addr, 256);
err_irq:
free_irq(dev->irq, dev);
err_dev:
free_netdev(dev);
return NULL;
}
obj-$(CONFIG_FT1000_USB) += ft1000.o
ft1000-y := ft1000_debug.o ft1000_download.o ft1000_hw.o ft1000_usb.o
/*
*---------------------------------------------------------------------------
* FT1000 driver for Flarion Flash OFDM NIC Device
*
* Copyright (C) 2006 Flarion Technologies, All rights reserved.
*
* 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. 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., 59 Temple Place -
* Suite 330, Boston, MA 02111-1307, USA.
*---------------------------------------------------------------------------
*
* File: ft1000_chdev.c
*
* Description: Custom character device dispatch routines.
*
* History:
* 8/29/02 Whc Ported to Linux.
* 6/05/06 Whc Porting to Linux 2.6.9
*
*---------------------------------------------------------------------------
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/poll.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/ioctl.h>
#include <linux/debugfs.h>
#include "ft1000_usb.h"
static int ft1000_flarion_cnt;
static int ft1000_open(struct inode *inode, struct file *file);
static unsigned int ft1000_poll_dev(struct file *file, poll_table *wait);
static long ft1000_ioctl(struct file *file, unsigned int command,
unsigned long argument);
static int ft1000_release(struct inode *inode, struct file *file);
/* List to free receive command buffer pool */
struct list_head freercvpool;
/* lock to arbitrate free buffer list for receive command data */
spinlock_t free_buff_lock;
int numofmsgbuf;
/*
* Table of entry-point routines for char device
*/
static const struct file_operations ft1000fops = {
.unlocked_ioctl = ft1000_ioctl,
.poll = ft1000_poll_dev,
.open = ft1000_open,
.release = ft1000_release,
.llseek = no_llseek,
};
/*
---------------------------------------------------------------------------
* Function: ft1000_get_buffer
*
* Parameters:
*
* Returns:
*
* Description:
*
* Notes:
*
*---------------------------------------------------------------------------
*/
struct dpram_blk *ft1000_get_buffer(struct list_head *bufflist)
{
unsigned long flags;
struct dpram_blk *ptr;
spin_lock_irqsave(&free_buff_lock, flags);
/* Check if buffer is available */
if (list_empty(bufflist)) {
pr_debug("No more buffer - %d\n", numofmsgbuf);
ptr = NULL;
} else {
numofmsgbuf--;
ptr = list_entry(bufflist->next, struct dpram_blk, list);
list_del(&ptr->list);
/* pr_debug("number of free msg buffers = %d\n", numofmsgbuf); */
}
spin_unlock_irqrestore(&free_buff_lock, flags);
return ptr;
}
/*
*---------------------------------------------------------------------------
* Function: ft1000_free_buffer
*
* Parameters:
*
* Returns:
*
* Description:
*
* Notes:
*
*---------------------------------------------------------------------------
*/
void ft1000_free_buffer(struct dpram_blk *pdpram_blk, struct list_head *plist)
{
unsigned long flags;
spin_lock_irqsave(&free_buff_lock, flags);
/* Put memory back to list */
list_add_tail(&pdpram_blk->list, plist);
numofmsgbuf++;
/*pr_debug("number of free msg buffers = %d\n", numofmsgbuf); */
spin_unlock_irqrestore(&free_buff_lock, flags);
}
/*
*---------------------------------------------------------------------------
* Function: ft1000_CreateDevice
*
* Parameters: dev - pointer to adapter object
*
* Returns: 0 if successful
*
* Description: Creates a private char device.
*
* Notes: Only called by init_module().
*
*---------------------------------------------------------------------------
*/
int ft1000_create_dev(struct ft1000_usb *dev)
{
int result;
int i;
struct dentry *dir, *file;
struct ft1000_debug_dirs *tmp;
/* make a new device name */
sprintf(dev->DeviceName, "%s%d", "FT1000_", dev->CardNumber);
pr_debug("number of instance = %d\n", ft1000_flarion_cnt);
pr_debug("DeviceCreated = %x\n", dev->DeviceCreated);
if (dev->DeviceCreated) {
pr_debug("\"%s\" already registered\n", dev->DeviceName);
return -EIO;
}
/* register the device */
pr_debug("\"%s\" debugfs device registration\n", dev->DeviceName);
tmp = kmalloc(sizeof(struct ft1000_debug_dirs), GFP_KERNEL);
if (tmp == NULL) {
result = -1;
goto fail;
}
dir = debugfs_create_dir(dev->DeviceName, NULL);
if (IS_ERR(dir)) {
result = PTR_ERR(dir);
goto debug_dir_fail;
}
file = debugfs_create_file("device", S_IRUGO | S_IWUSR, dir,
dev, &ft1000fops);
if (IS_ERR(file)) {
result = PTR_ERR(file);
goto debug_file_fail;
}
tmp->dent = dir;
tmp->file = file;
tmp->int_number = dev->CardNumber;
list_add(&tmp->list, &dev->nodes.list);
pr_debug("registered debugfs directory \"%s\"\n", dev->DeviceName);
/* initialize application information */
dev->appcnt = 0;
for (i = 0; i < MAX_NUM_APP; i++) {
dev->app_info[i].nTxMsg = 0;
dev->app_info[i].nRxMsg = 0;
dev->app_info[i].nTxMsgReject = 0;
dev->app_info[i].nRxMsgMiss = 0;
dev->app_info[i].fileobject = NULL;
dev->app_info[i].app_id = i+1;
dev->app_info[i].DspBCMsgFlag = 0;
dev->app_info[i].NumOfMsg = 0;
init_waitqueue_head(&dev->app_info[i].wait_dpram_msg);
INIT_LIST_HEAD(&dev->app_info[i].app_sqlist);
}
dev->DeviceCreated = TRUE;
ft1000_flarion_cnt++;
return 0;
debug_file_fail:
debugfs_remove(dir);
debug_dir_fail:
kfree(tmp);
fail:
return result;
}
/*
*---------------------------------------------------------------------------
* Function: ft1000_DestroyDeviceDEBUG
*
* Parameters: dev - pointer to adapter object
*
* Description: Destroys a private char device.
*
* Notes: Only called by cleanup_module().
*
*---------------------------------------------------------------------------
*/
void ft1000_destroy_dev(struct net_device *netdev)
{
struct ft1000_info *info = netdev_priv(netdev);
struct ft1000_usb *dev = info->priv;
int i;
struct dpram_blk *pdpram_blk;
struct dpram_blk *ptr;
struct list_head *pos, *q;
struct ft1000_debug_dirs *dir;
if (dev->DeviceCreated) {
ft1000_flarion_cnt--;
list_for_each_safe(pos, q, &dev->nodes.list) {
dir = list_entry(pos, struct ft1000_debug_dirs, list);
if (dir->int_number == dev->CardNumber) {
debugfs_remove(dir->file);
debugfs_remove(dir->dent);
list_del(pos);
kfree(dir);
}
}
pr_debug("unregistered device \"%s\"\n", dev->DeviceName);
/* Make sure we free any memory reserve for slow Queue */
for (i = 0; i < MAX_NUM_APP; i++) {
while (list_empty(&dev->app_info[i].app_sqlist) == 0) {
pdpram_blk = list_entry(dev->app_info[i].app_sqlist.next,
struct dpram_blk, list);
list_del(&pdpram_blk->list);
ft1000_free_buffer(pdpram_blk, &freercvpool);
}
wake_up_interruptible(&dev->app_info[i].wait_dpram_msg);
}
/* Remove buffer allocated for receive command data */
if (ft1000_flarion_cnt == 0) {
while (list_empty(&freercvpool) == 0) {
ptr = list_entry(freercvpool.next, struct dpram_blk, list);
list_del(&ptr->list);
kfree(ptr->pbuffer);
kfree(ptr);
}
}
dev->DeviceCreated = FALSE;
}
}
/*
*---------------------------------------------------------------------------
* Function: ft1000_open
*
* Parameters:
*
* Description:
*
* Notes:
*
*---------------------------------------------------------------------------
*/
static int ft1000_open(struct inode *inode, struct file *file)
{
struct ft1000_info *info;
struct ft1000_usb *dev = (struct ft1000_usb *)inode->i_private;
int i, num;
num = MINOR(inode->i_rdev) & 0xf;
pr_debug("minor number=%d\n", num);
info = file->private_data = netdev_priv(dev->net);
pr_debug("f_owner = %p number of application = %d\n",
&file->f_owner, dev->appcnt);
/* Check if maximum number of application exceeded */
if (dev->appcnt > MAX_NUM_APP) {
pr_debug("Maximum number of application exceeded\n");
return -EACCES;
}
/* Search for available application info block */
for (i = 0; i < MAX_NUM_APP; i++) {
if (dev->app_info[i].fileobject == NULL)
break;
}
/* Fail due to lack of application info block */
if (i == MAX_NUM_APP) {
pr_debug("Could not find an application info block\n");
return -EACCES;
}
dev->appcnt++;
dev->app_info[i].fileobject = &file->f_owner;
dev->app_info[i].nTxMsg = 0;
dev->app_info[i].nRxMsg = 0;
dev->app_info[i].nTxMsgReject = 0;
dev->app_info[i].nRxMsgMiss = 0;
nonseekable_open(inode, file);
return 0;
}
/*
*---------------------------------------------------------------------------
* Function: ft1000_poll_dev
*
* Parameters:
*
* Description:
*
* Notes:
*
*---------------------------------------------------------------------------
*/
static unsigned int ft1000_poll_dev(struct file *file, poll_table *wait)
{
struct net_device *netdev = file->private_data;
struct ft1000_info *info = netdev_priv(netdev);
struct ft1000_usb *dev = info->priv;
int i;
if (ft1000_flarion_cnt == 0) {
pr_debug("called with ft1000_flarion_cnt value zero\n");
return -EBADF;
}
/* Search for matching file object */
for (i = 0; i < MAX_NUM_APP; i++) {
if (dev->app_info[i].fileobject == &file->f_owner) {
/* pr_debug("Message is for AppId = %d\n", dev->app_info[i].app_id); */
break;
}
}
/* Could not find application info block */
if (i == MAX_NUM_APP) {
pr_debug("Could not find application info block\n");
return -EACCES;
}
if (list_empty(&dev->app_info[i].app_sqlist) == 0) {
pr_debug("Message detected in slow queue\n");
return(POLLIN | POLLRDNORM | POLLPRI);
}
poll_wait(file, &dev->app_info[i].wait_dpram_msg, wait);
/* pr_debug("Polling for data from DSP\n"); */
return 0;
}
/*
*---------------------------------------------------------------------------
* Function: ft1000_ioctl
*
* Parameters:
*
* Description:
*
* Notes:
*
*---------------------------------------------------------------------------
*/
static long ft1000_ioctl(struct file *file, unsigned int command,
unsigned long argument)
{
void __user *argp = (void __user *)argument;
struct ft1000_info *info;
struct ft1000_usb *ft1000dev;
int result = 0;
int cmd;
int i;
u16 tempword;
unsigned long flags;
struct timeval tv;
struct IOCTL_GET_VER get_ver_data;
struct IOCTL_GET_DSP_STAT get_stat_data;
u8 ConnectionMsg[] = {
0x00, 0x44, 0x10, 0x20, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x93, 0x64,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0a,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x37, 0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7f, 0x00,
0x00, 0x01, 0x00, 0x00
};
unsigned short ledStat = 0;
unsigned short conStat = 0;
if (ft1000_flarion_cnt == 0) {
pr_debug("called with ft1000_flarion_cnt of zero\n");
return -EBADF;
}
/* pr_debug("command = 0x%x argument = 0x%8x\n", command, (u32)argument); */
info = file->private_data;
ft1000dev = info->priv;
cmd = _IOC_NR(command);
/* pr_debug("cmd = 0x%x\n", cmd); */
/* process the command */
switch (cmd) {
case IOCTL_REGISTER_CMD:
pr_debug("IOCTL_FT1000_REGISTER called\n");
result = get_user(tempword, (__u16 __user *)argp);
if (result) {
pr_debug("result = %d failed to get_user\n", result);
break;
}
if (tempword == DSPBCMSGID) {
/* Search for matching file object */
for (i = 0; i < MAX_NUM_APP; i++) {
if (ft1000dev->app_info[i].fileobject == &file->f_owner) {
ft1000dev->app_info[i].DspBCMsgFlag = 1;
pr_debug("Registered for broadcast messages\n");
break;
}
}
}
break;
case IOCTL_GET_VER_CMD:
pr_debug("IOCTL_FT1000_GET_VER called\n");
get_ver_data.drv_ver = FT1000_DRV_VER;
if (copy_to_user(argp, &get_ver_data, sizeof(get_ver_data))) {
pr_debug("copy fault occurred\n");
result = -EFAULT;
break;
}
pr_debug("driver version = 0x%x\n",
(unsigned int)get_ver_data.drv_ver);
break;
case IOCTL_CONNECT:
/* Connect Message */
pr_debug("IOCTL_FT1000_CONNECT\n");
ConnectionMsg[79] = 0xfc;
result = card_send_command(ft1000dev, ConnectionMsg, 0x4c);
break;
case IOCTL_DISCONNECT:
/* Disconnect Message */
pr_debug("IOCTL_FT1000_DISCONNECT\n");
ConnectionMsg[79] = 0xfd;
result = card_send_command(ft1000dev, ConnectionMsg, 0x4c);
break;
case IOCTL_GET_DSP_STAT_CMD:
/* pr_debug("IOCTL_FT1000_GET_DSP_STAT\n"); */
memset(&get_stat_data, 0, sizeof(get_stat_data));
memcpy(get_stat_data.DspVer, info->DspVer, DSPVERSZ);
memcpy(get_stat_data.HwSerNum, info->HwSerNum, HWSERNUMSZ);
memcpy(get_stat_data.Sku, info->Sku, SKUSZ);
memcpy(get_stat_data.eui64, info->eui64, EUISZ);
if (info->ProgConStat != 0xFF) {
ft1000_read_dpram16(ft1000dev, FT1000_MAG_DSP_LED,
(u8 *)&ledStat, FT1000_MAG_DSP_LED_INDX);
get_stat_data.LedStat = ntohs(ledStat);
pr_debug("LedStat = 0x%x\n", get_stat_data.LedStat);
ft1000_read_dpram16(ft1000dev, FT1000_MAG_DSP_CON_STATE,
(u8 *)&conStat, FT1000_MAG_DSP_CON_STATE_INDX);
get_stat_data.ConStat = ntohs(conStat);
pr_debug("ConStat = 0x%x\n", get_stat_data.ConStat);
} else {
get_stat_data.ConStat = 0x0f;
}
get_stat_data.nTxPkts = info->stats.tx_packets;
get_stat_data.nRxPkts = info->stats.rx_packets;
get_stat_data.nTxBytes = info->stats.tx_bytes;
get_stat_data.nRxBytes = info->stats.rx_bytes;
do_gettimeofday(&tv);
get_stat_data.ConTm = (u32)(tv.tv_sec - info->ConTm);
pr_debug("Connection Time = %d\n", (int)get_stat_data.ConTm);
if (copy_to_user(argp, &get_stat_data, sizeof(get_stat_data))) {
pr_debug("copy fault occurred\n");
result = -EFAULT;
break;
}
pr_debug("GET_DSP_STAT succeed\n");
break;
case IOCTL_SET_DPRAM_CMD:
{
struct IOCTL_DPRAM_BLK *dpram_data = NULL;
/* struct IOCTL_DPRAM_COMMAND dpram_command; */
u16 qtype;
u16 msgsz;
struct pseudo_hdr *ppseudo_hdr;
u16 *pmsg;
u16 total_len;
u16 app_index;
u16 status;
/* pr_debug("IOCTL_FT1000_SET_DPRAM called\n");*/
if (ft1000_flarion_cnt == 0)
return -EBADF;
if (ft1000dev->DrvMsgPend)
return -ENOTTY;
if (ft1000dev->fProvComplete == 0)
return -EACCES;
ft1000dev->fAppMsgPend = true;
if (info->CardReady) {
/* pr_debug("try to SET_DPRAM\n"); */
/* Get the length field to see how many bytes to copy */
result = get_user(msgsz, (__u16 __user *)argp);
if (result)
break;
msgsz = ntohs(msgsz);
/* pr_debug("length of message = %d\n", msgsz); */
if (msgsz > MAX_CMD_SQSIZE) {
pr_debug("bad message length = %d\n", msgsz);
result = -EINVAL;
break;
}
result = -ENOMEM;
dpram_data = kmalloc(msgsz + 2, GFP_KERNEL);
if (!dpram_data)
break;
if (copy_from_user(dpram_data, argp, msgsz+2)) {
pr_debug("copy fault occurred\n");
result = -EFAULT;
} else {
/* Check if this message came from a registered application */
for (i = 0; i < MAX_NUM_APP; i++) {
if (ft1000dev->app_info[i].fileobject == &file->f_owner)
break;
}
if (i == MAX_NUM_APP) {
pr_debug("No matching application fileobject\n");
result = -EINVAL;
kfree(dpram_data);
break;
}
app_index = i;
/* Check message qtype type which is the lower byte within qos_class */
qtype = ntohs(dpram_data->pseudohdr.qos_class) & 0xff;
/* pr_debug("qtype = %d\n", qtype); */
if (!qtype) {
/* Put message into Slow Queue */
/* Only put a message into the DPRAM if msg doorbell is available */
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
/* pr_debug("READ REGISTER tempword=%x\n", tempword); */
if (tempword & FT1000_DB_DPRAM_TX) {
/* Suspend for 2ms and try again due to DSP doorbell busy */
mdelay(2);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
/* Suspend for 1ms and try again due to DSP doorbell busy */
mdelay(1);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
/* Suspend for 3ms and try again due to DSP doorbell busy */
mdelay(3);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
pr_debug("Doorbell not available\n");
result = -ENOTTY;
kfree(dpram_data);
break;
}
}
}
}
}
/*pr_debug("finished reading register\n"); */
/* Make sure we are within the limits of the slow queue memory limitation */
if ((msgsz < MAX_CMD_SQSIZE) && (msgsz > PSEUDOSZ)) {
/* Need to put sequence number plus new checksum for message */
pmsg = (u16 *)&dpram_data->pseudohdr;
ppseudo_hdr = (struct pseudo_hdr *)pmsg;
total_len = msgsz+2;
if (total_len & 0x1)
total_len++;
/* Insert slow queue sequence number */
ppseudo_hdr->seq_num = info->squeseqnum++;
ppseudo_hdr->portsrc = ft1000dev->app_info[app_index].app_id;
/* Calculate new checksum */
ppseudo_hdr->checksum = *pmsg++;
/* pr_debug("checksum = 0x%x\n", ppseudo_hdr->checksum); */
for (i = 1; i < 7; i++) {
ppseudo_hdr->checksum ^= *pmsg++;
/* pr_debug("checksum = 0x%x\n", ppseudo_hdr->checksum); */
}
pmsg++;
ppseudo_hdr = (struct pseudo_hdr *)pmsg;
result = card_send_command(ft1000dev, dpram_data, total_len+2);
ft1000dev->app_info[app_index].nTxMsg++;
} else {
result = -EINVAL;
}
}
}
} else {
pr_debug("Card not ready take messages\n");
result = -EACCES;
}
kfree(dpram_data);
}
break;
case IOCTL_GET_DPRAM_CMD:
{
struct dpram_blk *pdpram_blk;
struct IOCTL_DPRAM_BLK __user *pioctl_dpram;
int msglen;
/* pr_debug("IOCTL_FT1000_GET_DPRAM called\n"); */
if (ft1000_flarion_cnt == 0)
return -EBADF;
/* Search for matching file object */
for (i = 0; i < MAX_NUM_APP; i++) {
if (ft1000dev->app_info[i].fileobject == &file->f_owner) {
/*pr_debug("Message is for AppId = %d\n", ft1000dev->app_info[i].app_id); */
break;
}
}
/* Could not find application info block */
if (i == MAX_NUM_APP) {
pr_debug("Could not find application info block\n");
result = -EBADF;
break;
}
result = 0;
pioctl_dpram = argp;
if (list_empty(&ft1000dev->app_info[i].app_sqlist) == 0) {
/* pr_debug("Message detected in slow queue\n"); */
spin_lock_irqsave(&free_buff_lock, flags);
pdpram_blk = list_entry(ft1000dev->app_info[i].app_sqlist.next,
struct dpram_blk, list);
list_del(&pdpram_blk->list);
ft1000dev->app_info[i].NumOfMsg--;
/* pr_debug("NumOfMsg for app %d = %d\n", i, ft1000dev->app_info[i].NumOfMsg); */
spin_unlock_irqrestore(&free_buff_lock, flags);
msglen = ntohs(*(u16 *)pdpram_blk->pbuffer) + PSEUDOSZ;
result = get_user(msglen, &pioctl_dpram->total_len);
if (result)
break;
msglen = htons(msglen);
/* pr_debug("msg length = %x\n", msglen); */
if (copy_to_user(&pioctl_dpram->pseudohdr, pdpram_blk->pbuffer, msglen)) {
pr_debug("copy fault occurred\n");
result = -EFAULT;
break;
}
ft1000_free_buffer(pdpram_blk, &freercvpool);
result = msglen;
}
/* pr_debug("IOCTL_FT1000_GET_DPRAM no message\n"); */
}
break;
default:
pr_debug("unknown command: 0x%x\n", command);
result = -ENOTTY;
break;
}
ft1000dev->fAppMsgPend = false;
return result;
}
/*
*---------------------------------------------------------------------------
* Function: ft1000_release
*
* Parameters:
*
* Description:
*
* Notes:
*
*---------------------------------------------------------------------------
*/
static int ft1000_release(struct inode *inode, struct file *file)
{
struct ft1000_info *info;
struct net_device *dev;
struct ft1000_usb *ft1000dev;
int i;
struct dpram_blk *pdpram_blk;
struct dpram_blk *tmp;
dev = file->private_data;
info = netdev_priv(dev);
ft1000dev = info->priv;
if (ft1000_flarion_cnt == 0) {
ft1000dev->appcnt--;
return -EBADF;
}
/* Search for matching file object */
for (i = 0; i < MAX_NUM_APP; i++) {
if (ft1000dev->app_info[i].fileobject == &file->f_owner) {
/* pr_debug("Message is for AppId = %d\n", ft1000dev->app_info[i].app_id); */
break;
}
}
if (i == MAX_NUM_APP)
return 0;
list_for_each_entry_safe(pdpram_blk, tmp, &ft1000dev->app_info[i].app_sqlist, list) {
pr_debug("Remove and free memory queue up on slow queue\n");
list_del(&pdpram_blk->list);
ft1000_free_buffer(pdpram_blk, &freercvpool);
}
/* initialize application information */
ft1000dev->appcnt--;
pr_debug("appcnt = %d\n", ft1000dev->appcnt);
ft1000dev->app_info[i].fileobject = NULL;
return 0;
}
/*
* CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
*
* This file is part of Express Card USB Driver
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/usb.h>
#include <linux/vmalloc.h>
#include "ft1000_usb.h"
#define DWNLD_HANDSHAKE_LOC 0x02
#define DWNLD_TYPE_LOC 0x04
#define DWNLD_SIZE_MSW_LOC 0x06
#define DWNLD_SIZE_LSW_LOC 0x08
#define DWNLD_PS_HDR_LOC 0x0A
#define MAX_DSP_WAIT_LOOPS 40
#define DSP_WAIT_SLEEP_TIME 1000 /* 1 millisecond */
#define DSP_WAIT_DISPATCH_LVL 50 /* 50 usec */
#define HANDSHAKE_TIMEOUT_VALUE 0xF1F1
#define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */
#define HANDSHAKE_RESET_VALUE_USB 0xFE7E /* When DSP requests startover */
#define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */
#define HANDSHAKE_DSP_BL_READY_USB 0xFE7E /* At start DSP writes this when bootloader ready */
#define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */
#define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */
#define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */
#define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */
#define REQUEST_CODE_LENGTH 0x0000
#define REQUEST_RUN_ADDRESS 0x0001
#define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */
#define REQUEST_DONE_BL 0x0003
#define REQUEST_DONE_CL 0x0004
#define REQUEST_VERSION_INFO 0x0005
#define REQUEST_CODE_BY_VERSION 0x0006
#define REQUEST_MAILBOX_DATA 0x0007
#define REQUEST_FILE_CHECKSUM 0x0008
#define STATE_START_DWNLD 0x01
#define STATE_BOOT_DWNLD 0x02
#define STATE_CODE_DWNLD 0x03
#define STATE_DONE_DWNLD 0x04
#define STATE_SECTION_PROV 0x05
#define STATE_DONE_PROV 0x06
#define STATE_DONE_FILE 0x07
#define MAX_LENGTH 0x7f0
/* Temporary download mechanism for Magnemite */
#define DWNLD_MAG_TYPE_LOC 0x00
#define DWNLD_MAG_LEN_LOC 0x01
#define DWNLD_MAG_ADDR_LOC 0x02
#define DWNLD_MAG_CHKSUM_LOC 0x03
#define DWNLD_MAG_VAL_LOC 0x04
#define HANDSHAKE_MAG_DSP_BL_READY 0xFEFE0000 /* At start DSP writes this when bootloader ready */
#define HANDSHAKE_MAG_DSP_ENTRY 0x01000000 /* Dsp writes this to request for entry address */
#define HANDSHAKE_MAG_DSP_DATA 0x02000000 /* Dsp writes this to request for data block */
#define HANDSHAKE_MAG_DSP_DONE 0x03000000 /* Dsp writes this to indicate download done */
#define HANDSHAKE_MAG_DRV_READY 0xFFFF0000 /* Driver writes this to indicate ready to download */
#define HANDSHAKE_MAG_DRV_DATA 0x02FECDAB /* Driver writes this to indicate data available to DSP */
#define HANDSHAKE_MAG_DRV_ENTRY 0x01FECDAB /* Driver writes this to indicate entry point to DSP */
#define HANDSHAKE_MAG_TIMEOUT_VALUE 0xF1F1
/* New Magnemite downloader */
#define DWNLD_MAG1_HANDSHAKE_LOC 0x00
#define DWNLD_MAG1_TYPE_LOC 0x01
#define DWNLD_MAG1_SIZE_LOC 0x02
#define DWNLD_MAG1_PS_HDR_LOC 0x03
struct dsp_file_hdr {
long version_id; /* Version ID of this image format. */
long package_id; /* Package ID of code release. */
long build_date; /* Date/time stamp when file was built. */
long commands_offset; /* Offset to attached commands in Pseudo Hdr format. */
long loader_offset; /* Offset to bootloader code. */
long loader_code_address; /* Start address of bootloader. */
long loader_code_end; /* Where bootloader code ends. */
long loader_code_size;
long version_data_offset; /* Offset were scrambled version data begins. */
long version_data_size; /* Size, in words, of scrambled version data. */
long nDspImages; /* Number of DSP images in file. */
};
struct dsp_image_info {
long coff_date; /* Date/time when DSP Coff image was built. */
long begin_offset; /* Offset in file where image begins. */
long end_offset; /* Offset in file where image begins. */
long run_address; /* On chip Start address of DSP code. */
long image_size; /* Size of image. */
long version; /* Embedded version # of DSP code. */
unsigned short checksum; /* DSP File checksum */
unsigned short pad1;
} __packed;
/* checks if the doorbell register is cleared */
static int check_usb_db(struct ft1000_usb *ft1000dev)
{
int loopcnt;
u16 temp;
int status;
loopcnt = 0;
while (loopcnt < 10) {
status = ft1000_read_register(ft1000dev, &temp,
FT1000_REG_DOORBELL);
pr_debug("read FT1000_REG_DOORBELL value is %x\n", temp);
if (temp & 0x0080) {
pr_debug("Got checkusb doorbell\n");
status = ft1000_write_register(ft1000dev, 0x0080,
FT1000_REG_DOORBELL);
status = ft1000_write_register(ft1000dev, 0x0100,
FT1000_REG_DOORBELL);
status = ft1000_write_register(ft1000dev, 0x8000,
FT1000_REG_DOORBELL);
break;
}
loopcnt++;
usleep_range(10000, 11000);
}
loopcnt = 0;
while (loopcnt < 20) {
status = ft1000_read_register(ft1000dev, &temp,
FT1000_REG_DOORBELL);
pr_debug("Doorbell = 0x%x\n", temp);
if (temp & 0x8000) {
loopcnt++;
usleep_range(10000, 11000);
} else {
pr_debug("door bell is cleared, return 0\n");
return 0;
}
}
return -1;
}
/* gets the handshake and compares it with the expected value */
static u16 get_handshake(struct ft1000_usb *ft1000dev, u16 expected_value)
{
u16 handshake;
int loopcnt;
int status = 0;
loopcnt = 0;
while (loopcnt < 100) {
/* Need to clear downloader doorbell if Hartley ASIC */
status = ft1000_write_register(ft1000dev, FT1000_DB_DNLD_RX,
FT1000_REG_DOORBELL);
if (ft1000dev->fcodeldr) {
pr_debug("fcodeldr is %d\n", ft1000dev->fcodeldr);
ft1000dev->fcodeldr = 0;
status = check_usb_db(ft1000dev);
if (status != 0) {
pr_debug("check_usb_db failed\n");
break;
}
status = ft1000_write_register(ft1000dev,
FT1000_DB_DNLD_RX,
FT1000_REG_DOORBELL);
}
status = ft1000_read_dpram16(ft1000dev,
DWNLD_MAG1_HANDSHAKE_LOC,
(u8 *)&handshake, 1);
handshake = ntohs(handshake);
if (status)
return HANDSHAKE_TIMEOUT_VALUE;
if ((handshake == expected_value) ||
(handshake == HANDSHAKE_RESET_VALUE_USB)) {
return handshake;
}
loopcnt++;
usleep_range(10000, 11000);
}
return HANDSHAKE_TIMEOUT_VALUE;
}
/* write the handshake value to the handshake location */
static void put_handshake(struct ft1000_usb *ft1000dev, u16 handshake_value)
{
u32 tempx;
u16 tempword;
int status;
tempx = (u32)handshake_value;
tempx = ntohl(tempx);
tempword = (u16)(tempx & 0xffff);
status = ft1000_write_dpram16(ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC,
tempword, 0);
tempword = (u16)(tempx >> 16);
status = ft1000_write_dpram16(ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC,
tempword, 1);
status = ft1000_write_register(ft1000dev, FT1000_DB_DNLD_TX,
FT1000_REG_DOORBELL);
}
static u16 get_handshake_usb(struct ft1000_usb *ft1000dev, u16 expected_value)
{
u16 handshake;
int loopcnt;
u16 temp;
int status = 0;
loopcnt = 0;
handshake = 0;
while (loopcnt < 100) {
if (ft1000dev->usbboot == 2) {
status = ft1000_read_dpram32(ft1000dev, 0,
(u8 *)&ft1000dev->tempbuf[0], 64);
for (temp = 0; temp < 16; temp++) {
pr_debug("tempbuf %d = 0x%x\n",
temp, ft1000dev->tempbuf[temp]);
}
status = ft1000_read_dpram16(ft1000dev,
DWNLD_MAG1_HANDSHAKE_LOC,
(u8 *)&handshake, 1);
pr_debug("handshake from read_dpram16 = 0x%x\n",
handshake);
if (ft1000dev->dspalive == ft1000dev->tempbuf[6]) {
handshake = 0;
} else {
handshake = ft1000dev->tempbuf[1];
ft1000dev->dspalive =
ft1000dev->tempbuf[6];
}
} else {
status = ft1000_read_dpram16(ft1000dev,
DWNLD_MAG1_HANDSHAKE_LOC,
(u8 *)&handshake, 1);
}
loopcnt++;
usleep_range(10000, 11000);
handshake = ntohs(handshake);
if ((handshake == expected_value) ||
(handshake == HANDSHAKE_RESET_VALUE_USB))
return handshake;
}
return HANDSHAKE_TIMEOUT_VALUE;
}
static void put_handshake_usb(struct ft1000_usb *ft1000dev, u16 handshake_value)
{
int i;
for (i = 0; i < 1000; i++)
;
}
static u16 get_request_type(struct ft1000_usb *ft1000dev)
{
u16 request_type;
int status;
u16 tempword;
u32 tempx;
if (ft1000dev->bootmode == 1) {
status = fix_ft1000_read_dpram32(ft1000dev,
DWNLD_MAG1_TYPE_LOC,
(u8 *)&tempx);
tempx = ntohl(tempx);
} else {
tempx = 0;
status = ft1000_read_dpram16(ft1000dev,
DWNLD_MAG1_TYPE_LOC,
(u8 *)&tempword, 1);
tempx |= (tempword << 16);
tempx = ntohl(tempx);
}
request_type = (u16)tempx;
return request_type;
}
static u16 get_request_type_usb(struct ft1000_usb *ft1000dev)
{
u16 request_type;
int status;
u16 tempword;
u32 tempx;
if (ft1000dev->bootmode == 1) {
status = fix_ft1000_read_dpram32(ft1000dev,
DWNLD_MAG1_TYPE_LOC,
(u8 *)&tempx);
tempx = ntohl(tempx);
} else {
if (ft1000dev->usbboot == 2) {
tempx = ft1000dev->tempbuf[2];
tempword = ft1000dev->tempbuf[3];
} else {
tempx = 0;
status = ft1000_read_dpram16(ft1000dev,
DWNLD_MAG1_TYPE_LOC,
(u8 *)&tempword, 1);
}
tempx |= (tempword << 16);
tempx = ntohl(tempx);
}
request_type = (u16)tempx;
return request_type;
}
static long get_request_value(struct ft1000_usb *ft1000dev)
{
u32 value;
u16 tempword;
int status;
if (ft1000dev->bootmode == 1) {
status = fix_ft1000_read_dpram32(ft1000dev,
DWNLD_MAG1_SIZE_LOC,
(u8 *)&value);
value = ntohl(value);
} else {
status = ft1000_read_dpram16(ft1000dev,
DWNLD_MAG1_SIZE_LOC,
(u8 *)&tempword, 0);
value = tempword;
status = ft1000_read_dpram16(ft1000dev,
DWNLD_MAG1_SIZE_LOC,
(u8 *)&tempword, 1);
value |= (tempword << 16);
value = ntohl(value);
}
return value;
}
/* writes a value to DWNLD_MAG1_SIZE_LOC */
static void put_request_value(struct ft1000_usb *ft1000dev, long lvalue)
{
u32 tempx;
int status;
tempx = ntohl(lvalue);
status = fix_ft1000_write_dpram32(ft1000dev, DWNLD_MAG1_SIZE_LOC,
(u8 *)&tempx);
}
/* returns the checksum of the pseudo header */
static u16 hdr_checksum(struct pseudo_hdr *pHdr)
{
u16 *usPtr = (u16 *)pHdr;
u16 chksum;
chksum = (((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
usPtr[4]) ^ usPtr[5]) ^ usPtr[6];
return chksum;
}
static int check_buffers(u16 *buff_w, u16 *buff_r, int len, int offset)
{
int i;
for (i = 0; i < len; i++) {
if (buff_w[i] != buff_r[i + offset])
return -EREMOTEIO;
}
return 0;
}
static int write_dpram32_and_check(struct ft1000_usb *ft1000dev,
u16 tempbuffer[], u16 dpram)
{
int status;
u16 resultbuffer[64];
int i;
for (i = 0; i < 10; i++) {
status = ft1000_write_dpram32(ft1000dev, dpram,
(u8 *)&tempbuffer[0], 64);
if (status == 0) {
/* Work around for ASIC bit stuffing problem. */
if ((tempbuffer[31] & 0xfe00) == 0xfe00) {
status = ft1000_write_dpram32(ft1000dev,
dpram+12, (u8 *)&tempbuffer[24],
64);
}
/* Let's check the data written */
status = ft1000_read_dpram32(ft1000dev, dpram,
(u8 *)&resultbuffer[0], 64);
if ((tempbuffer[31] & 0xfe00) == 0xfe00) {
if (check_buffers(tempbuffer, resultbuffer, 28,
0)) {
pr_debug("DPRAM write failed 1 during bootloading\n");
usleep_range(9000, 11000);
break;
}
status = ft1000_read_dpram32(ft1000dev,
dpram+12,
(u8 *)&resultbuffer[0], 64);
if (check_buffers(tempbuffer, resultbuffer, 16,
24)) {
pr_debug("DPRAM write failed 2 during bootloading\n");
usleep_range(9000, 11000);
break;
}
} else {
if (check_buffers(tempbuffer, resultbuffer, 32,
0)) {
pr_debug("DPRAM write failed 3 during bootloading\n");
usleep_range(9000, 11000);
break;
}
}
if (status == 0)
break;
}
}
return status;
}
/* writes a block of DSP image to DPRAM
* Parameters: struct ft1000_usb - device structure
* u16 **pUsFile - DSP image file pointer in u16
* u8 **pUcFile - DSP image file pointer in u8
* long word_length - length of the buffer to be written to DPRAM
*/
static int write_blk(struct ft1000_usb *ft1000dev, u16 **pUsFile, u8 **pUcFile,
long word_length)
{
int status = 0;
u16 dpram;
int loopcnt, i;
u16 tempword;
u16 tempbuffer[64];
/*pr_debug("start word_length = %d\n",(int)word_length); */
dpram = (u16)DWNLD_MAG1_PS_HDR_LOC;
tempword = *(*pUsFile);
(*pUsFile)++;
status = ft1000_write_dpram16(ft1000dev, dpram, tempword, 0);
tempword = *(*pUsFile);
(*pUsFile)++;
status = ft1000_write_dpram16(ft1000dev, dpram++, tempword, 1);
*pUcFile = *pUcFile + 4;
word_length--;
tempword = (u16)word_length;
word_length = (word_length / 16) + 1;
for (; word_length > 0; word_length--) { /* In words */
loopcnt = 0;
for (i = 0; i < 32; i++) {
if (tempword != 0) {
tempbuffer[i++] = *(*pUsFile);
(*pUsFile)++;
tempbuffer[i] = *(*pUsFile);
(*pUsFile)++;
*pUcFile = *pUcFile + 4;
loopcnt++;
tempword--;
} else {
tempbuffer[i++] = 0;
tempbuffer[i] = 0;
}
}
/*pr_debug("loopcnt is %d\n", loopcnt); */
/*pr_debug("write_blk: bootmode = %d\n", bootmode); */
/*pr_debug("write_blk: dpram = %x\n", dpram); */
if (ft1000dev->bootmode == 0) {
if (dpram >= 0x3F4)
status = ft1000_write_dpram32(ft1000dev, dpram,
(u8 *)&tempbuffer[0], 8);
else
status = ft1000_write_dpram32(ft1000dev, dpram,
(u8 *)&tempbuffer[0], 64);
} else {
status = write_dpram32_and_check(ft1000dev, tempbuffer,
dpram);
if (status != 0) {
pr_debug("Write failed tempbuffer[31] = 0x%x\n",
tempbuffer[31]);
break;
}
}
dpram = dpram + loopcnt;
}
return status;
}
static void usb_dnld_complete(struct urb *urb)
{
/* pr_debug("****** usb_dnld_complete\n"); */
}
/* writes a block of DSP image to DPRAM
* Parameters: struct ft1000_usb - device structure
* u16 **pUsFile - DSP image file pointer in u16
* u8 **pUcFile - DSP image file pointer in u8
* long word_length - length of the buffer to be written to DPRAM
*/
static int write_blk_fifo(struct ft1000_usb *ft1000dev, u16 **pUsFile,
u8 **pUcFile, long word_length)
{
int byte_length;
byte_length = word_length * 4;
if (byte_length && ((byte_length % 64) == 0))
byte_length += 4;
if (byte_length < 64)
byte_length = 68;
usb_init_urb(ft1000dev->tx_urb);
memcpy(ft1000dev->tx_buf, *pUcFile, byte_length);
usb_fill_bulk_urb(ft1000dev->tx_urb,
ft1000dev->dev,
usb_sndbulkpipe(ft1000dev->dev,
ft1000dev->bulk_out_endpointAddr),
ft1000dev->tx_buf, byte_length, usb_dnld_complete,
ft1000dev);
usb_submit_urb(ft1000dev->tx_urb, GFP_ATOMIC);
*pUsFile = *pUsFile + (word_length << 1);
*pUcFile = *pUcFile + (word_length << 2);
return 0;
}
static int scram_start_dwnld(struct ft1000_usb *ft1000dev, u16 *hshake,
u32 *state)
{
int status = 0;
if (ft1000dev->usbboot)
*hshake = get_handshake_usb(ft1000dev, HANDSHAKE_DSP_BL_READY);
else
*hshake = get_handshake(ft1000dev, HANDSHAKE_DSP_BL_READY);
if (*hshake == HANDSHAKE_DSP_BL_READY) {
pr_debug("handshake is HANDSHAKE_DSP_BL_READY, call put_handshake(HANDSHAKE_DRIVER_READY)\n");
put_handshake(ft1000dev, HANDSHAKE_DRIVER_READY);
} else if (*hshake == HANDSHAKE_TIMEOUT_VALUE) {
status = -ETIMEDOUT;
} else {
pr_debug("Download error: Handshake failed\n");
status = -ENETRESET;
}
*state = STATE_BOOT_DWNLD;
return status;
}
static int request_code_segment(struct ft1000_usb *ft1000dev, u16 **s_file,
u8 **c_file, const u8 *endpoint, bool boot_case)
{
long word_length;
int status = 0;
word_length = get_request_value(ft1000dev);
/*pr_debug("word_length = 0x%x\n", (int)word_length); */
/*NdisMSleep (100); */
if (word_length > MAX_LENGTH) {
pr_debug("Download error: Max length exceeded\n");
return -1;
}
if ((word_length * 2 + (long)c_file) > (long)endpoint) {
/* Error, beyond boot code range.*/
pr_debug("Download error: Requested len=%d exceeds BOOT code boundary\n",
(int)word_length);
return -1;
}
if (word_length & 0x1)
word_length++;
word_length = word_length / 2;
if (boot_case) {
status = write_blk(ft1000dev, s_file, c_file, word_length);
/*pr_debug("write_blk returned %d\n", status); */
} else {
status = write_blk_fifo(ft1000dev, s_file, c_file, word_length);
if (ft1000dev->usbboot == 0)
ft1000dev->usbboot++;
if (ft1000dev->usbboot == 1)
status |= ft1000_write_dpram16(ft1000dev,
DWNLD_MAG1_PS_HDR_LOC, 0, 0);
}
return status;
}
/* Scramble downloader for Harley based ASIC via USB interface */
int scram_dnldr(struct ft1000_usb *ft1000dev, void *pFileStart,
u32 FileLength)
{
int status = 0;
u32 state;
u16 handshake;
struct pseudo_hdr *pseudo_header;
u16 pseudo_header_len;
long word_length;
u16 request;
u16 temp;
struct dsp_file_hdr *file_hdr;
struct dsp_image_info *dsp_img_info = NULL;
long requested_version;
bool correct_version;
struct drv_msg *mailbox_data;
u16 *data = NULL;
u16 *s_file = NULL;
u8 *c_file = NULL;
u8 *boot_end = NULL, *code_end = NULL;
int image;
long loader_code_address, loader_code_size = 0;
long run_address = 0, run_size = 0;
u32 templong;
u32 image_chksum = 0;
u16 dpram = 0;
u8 *pbuffer;
struct prov_record *pprov_record;
struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net);
ft1000dev->fcodeldr = 0;
ft1000dev->usbboot = 0;
ft1000dev->dspalive = 0xffff;
/*
* Get version id of file, at first 4 bytes of file, for newer files.
*/
state = STATE_START_DWNLD;
file_hdr = pFileStart;
ft1000_write_register(ft1000dev, 0x800, FT1000_REG_MAG_WATERMARK);
s_file = (u16 *) (pFileStart + file_hdr->loader_offset);
c_file = (u8 *) (pFileStart + file_hdr->loader_offset);
boot_end = (u8 *) (pFileStart + file_hdr->loader_code_end);
loader_code_address = file_hdr->loader_code_address;
loader_code_size = file_hdr->loader_code_size;
correct_version = false;
while ((status == 0) && (state != STATE_DONE_FILE)) {
switch (state) {
case STATE_START_DWNLD:
status = scram_start_dwnld(ft1000dev, &handshake,
&state);
break;
case STATE_BOOT_DWNLD:
pr_debug("STATE_BOOT_DWNLD\n");
ft1000dev->bootmode = 1;
handshake = get_handshake(ft1000dev, HANDSHAKE_REQUEST);
if (handshake == HANDSHAKE_REQUEST) {
/*
* Get type associated with the request.
*/
request = get_request_type(ft1000dev);
switch (request) {
case REQUEST_RUN_ADDRESS:
pr_debug("REQUEST_RUN_ADDRESS\n");
put_request_value(ft1000dev,
loader_code_address);
break;
case REQUEST_CODE_LENGTH:
pr_debug("REQUEST_CODE_LENGTH\n");
put_request_value(ft1000dev,
loader_code_size);
break;
case REQUEST_DONE_BL:
pr_debug("REQUEST_DONE_BL\n");
/* Reposition ptrs to beginning of code section */
s_file = (u16 *) (boot_end);
c_file = (u8 *) (boot_end);
/* pr_debug("download:s_file = 0x%8x\n", (int)s_file); */
/* pr_debug("FT1000:download:c_file = 0x%8x\n", (int)c_file); */
state = STATE_CODE_DWNLD;
ft1000dev->fcodeldr = 1;
break;
case REQUEST_CODE_SEGMENT:
status = request_code_segment(ft1000dev,
&s_file, &c_file,
boot_end,
true);
break;
default:
pr_debug("Download error: Bad request type=%d in BOOT download state\n",
request);
status = -1;
break;
}
if (ft1000dev->usbboot)
put_handshake_usb(ft1000dev,
HANDSHAKE_RESPONSE);
else
put_handshake(ft1000dev,
HANDSHAKE_RESPONSE);
} else {
pr_debug("Download error: Handshake failed\n");
status = -1;
}
break;
case STATE_CODE_DWNLD:
/* pr_debug("STATE_CODE_DWNLD\n"); */
ft1000dev->bootmode = 0;
if (ft1000dev->usbboot)
handshake =
get_handshake_usb(ft1000dev,
HANDSHAKE_REQUEST);
else
handshake =
get_handshake(ft1000dev, HANDSHAKE_REQUEST);
if (handshake == HANDSHAKE_REQUEST) {
/*
* Get type associated with the request.
*/
if (ft1000dev->usbboot)
request =
get_request_type_usb(ft1000dev);
else
request = get_request_type(ft1000dev);
switch (request) {
case REQUEST_FILE_CHECKSUM:
pr_debug("image_chksum = 0x%8x\n",
image_chksum);
put_request_value(ft1000dev,
image_chksum);
break;
case REQUEST_RUN_ADDRESS:
pr_debug("REQUEST_RUN_ADDRESS\n");
if (correct_version) {
pr_debug("run_address = 0x%8x\n",
(int)run_address);
put_request_value(ft1000dev,
run_address);
} else {
pr_debug("Download error: Got Run address request before image offset request\n");
status = -1;
break;
}
break;
case REQUEST_CODE_LENGTH:
pr_debug("REQUEST_CODE_LENGTH\n");
if (correct_version) {
pr_debug("run_size = 0x%8x\n",
(int)run_size);
put_request_value(ft1000dev,
run_size);
} else {
pr_debug("Download error: Got Size request before image offset request\n");
status = -1;
break;
}
break;
case REQUEST_DONE_CL:
ft1000dev->usbboot = 3;
/* Reposition ptrs to beginning of provisioning section */
s_file =
(u16 *) (pFileStart +
file_hdr->commands_offset);
c_file =
(u8 *) (pFileStart +
file_hdr->commands_offset);
state = STATE_DONE_DWNLD;
break;
case REQUEST_CODE_SEGMENT:
/* pr_debug("REQUEST_CODE_SEGMENT - CODELOADER\n"); */
if (!correct_version) {
pr_debug("Download error: Got Code Segment request before image offset request\n");
status = -1;
break;
}
status = request_code_segment(ft1000dev,
&s_file, &c_file,
code_end,
false);
break;
case REQUEST_MAILBOX_DATA:
pr_debug("REQUEST_MAILBOX_DATA\n");
/* Convert length from byte count to word count. Make sure we round up. */
word_length =
(long)(pft1000info->DSPInfoBlklen +
1) / 2;
put_request_value(ft1000dev,
word_length);
mailbox_data =
(struct drv_msg *)&(pft1000info->
DSPInfoBlk[0]);
/*
* Position ASIC DPRAM auto-increment pointer.
*/
data = (u16 *)&mailbox_data->data[0];
dpram = (u16)DWNLD_MAG1_PS_HDR_LOC;
if (word_length & 0x1)
word_length++;
word_length = word_length / 2;
for (; word_length > 0; word_length--) { /* In words */
templong = *data++;
templong |= (*data++ << 16);
status =
fix_ft1000_write_dpram32
(ft1000dev, dpram++,
(u8 *)&templong);
}
break;
case REQUEST_VERSION_INFO:
pr_debug("REQUEST_VERSION_INFO\n");
word_length =
file_hdr->version_data_size;
put_request_value(ft1000dev,
word_length);
/*
* Position ASIC DPRAM auto-increment pointer.
*/
s_file =
(u16 *) (pFileStart +
file_hdr->
version_data_offset);
dpram = (u16)DWNLD_MAG1_PS_HDR_LOC;
if (word_length & 0x1)
word_length++;
word_length = word_length / 2;
for (; word_length > 0; word_length--) { /* In words */
templong = ntohs(*s_file++);
temp = ntohs(*s_file++);
templong |= (temp << 16);
status =
fix_ft1000_write_dpram32
(ft1000dev, dpram++,
(u8 *)&templong);
}
break;
case REQUEST_CODE_BY_VERSION:
pr_debug("REQUEST_CODE_BY_VERSION\n");
correct_version = false;
requested_version =
get_request_value(ft1000dev);
dsp_img_info =
(struct dsp_image_info *)(pFileStart
+
sizeof
(struct
dsp_file_hdr));
for (image = 0;
image < file_hdr->nDspImages;
image++) {
if (dsp_img_info->version ==
requested_version) {
correct_version = true;
pr_debug("correct_version is TRUE\n");
s_file =
(u16 *) (pFileStart
+
dsp_img_info->
begin_offset);
c_file =
(u8 *) (pFileStart +
dsp_img_info->
begin_offset);
code_end =
(u8 *) (pFileStart +
dsp_img_info->
end_offset);
run_address =
dsp_img_info->
run_address;
run_size =
dsp_img_info->
image_size;
image_chksum =
(u32)dsp_img_info->
checksum;
break;
}
dsp_img_info++;
} /* end of for */
if (!correct_version) {
/*
* Error, beyond boot code range.
*/
pr_debug("Download error: Bad Version Request = 0x%x.\n",
(int)requested_version);
status = -1;
break;
}
break;
default:
pr_debug("Download error: Bad request type=%d in CODE download state.\n",
request);
status = -1;
break;
}
if (ft1000dev->usbboot)
put_handshake_usb(ft1000dev,
HANDSHAKE_RESPONSE);
else
put_handshake(ft1000dev,
HANDSHAKE_RESPONSE);
} else {
pr_debug("Download error: Handshake failed\n");
status = -1;
}
break;
case STATE_DONE_DWNLD:
pr_debug("Code loader is done...\n");
state = STATE_SECTION_PROV;
break;
case STATE_SECTION_PROV:
pr_debug("STATE_SECTION_PROV\n");
pseudo_header = (struct pseudo_hdr *)c_file;
if (pseudo_header->checksum ==
hdr_checksum(pseudo_header)) {
if (pseudo_header->portdest !=
0x80 /* Dsp OAM */) {
state = STATE_DONE_PROV;
break;
}
pseudo_header_len = ntohs(pseudo_header->length); /* Byte length for PROV records */
/* Get buffer for provisioning data */
pbuffer =
kmalloc(pseudo_header_len +
sizeof(struct pseudo_hdr),
GFP_ATOMIC);
if (pbuffer) {
memcpy(pbuffer, c_file,
(u32) (pseudo_header_len +
sizeof(struct
pseudo_hdr)));
/* link provisioning data */
pprov_record =
kmalloc(sizeof(struct prov_record),
GFP_ATOMIC);
if (pprov_record) {
pprov_record->pprov_data =
pbuffer;
list_add_tail(&pprov_record->
list,
&pft1000info->
prov_list);
/* Move to next entry if available */
c_file =
(u8 *) ((unsigned long)
c_file +
(u32) ((pseudo_header_len + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr));
if ((unsigned long)(c_file) -
(unsigned long)(pFileStart)
>=
(unsigned long)FileLength) {
state = STATE_DONE_FILE;
}
} else {
kfree(pbuffer);
status = -1;
}
} else {
status = -1;
}
} else {
/* Checksum did not compute */
status = -1;
}
pr_debug("after STATE_SECTION_PROV, state = %d, status= %d\n",
state, status);
break;
case STATE_DONE_PROV:
pr_debug("STATE_DONE_PROV\n");
state = STATE_DONE_FILE;
break;
default:
status = -1;
break;
} /* End Switch */
if (status != 0)
break;
/****
// Check if Card is present
status = Harley_Read_Register(&temp, FT1000_REG_SUP_IMASK);
if ( (status != NDIS_STATUS_SUCCESS) || (temp == 0x0000) ) {
break;
}
status = Harley_Read_Register(&temp, FT1000_REG_ASIC_ID);
if ( (status != NDIS_STATUS_SUCCESS) || (temp == 0xffff) ) {
break;
}
****/
} /* End while */
pr_debug("Download exiting with status = 0x%8x\n", status);
ft1000_write_register(ft1000dev, FT1000_DB_DNLD_TX,
FT1000_REG_DOORBELL);
return status;
}
/* CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
*
*
* This file is part of Express Card USB Driver
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/usb.h>
#include "ft1000_usb.h"
#include <linux/types.h>
#define HARLEY_READ_REGISTER 0x0
#define HARLEY_WRITE_REGISTER 0x01
#define HARLEY_READ_DPRAM_32 0x02
#define HARLEY_READ_DPRAM_LOW 0x03
#define HARLEY_READ_DPRAM_HIGH 0x04
#define HARLEY_WRITE_DPRAM_32 0x05
#define HARLEY_WRITE_DPRAM_LOW 0x06
#define HARLEY_WRITE_DPRAM_HIGH 0x07
#define HARLEY_READ_OPERATION 0xc1
#define HARLEY_WRITE_OPERATION 0x41
#if 0
#define JDEBUG
#endif
static int ft1000_submit_rx_urb(struct ft1000_info *info);
static u8 tempbuffer[1600];
#define MAX_RCV_LOOP 100
/* send a control message via USB interface synchronously
* Parameters: ft1000_usb - device structure
* pipe - usb control message pipe
* request - control request
* requesttype - control message request type
* value - value to be written or 0
* index - register index
* data - data buffer to hold the read/write values
* size - data size
* timeout - control message time out value
*/
static int ft1000_control(struct ft1000_usb *ft1000dev, unsigned int pipe,
u8 request, u8 requesttype, u16 value, u16 index,
void *data, u16 size, int timeout)
{
int ret;
if ((ft1000dev == NULL) || (ft1000dev->dev == NULL)) {
pr_debug("ft1000dev or ft1000dev->dev == NULL, failure\n");
return -ENODEV;
}
ret = usb_control_msg(ft1000dev->dev, pipe, request, requesttype,
value, index, data, size, timeout);
if (ret > 0)
ret = 0;
return ret;
}
/* returns the value in a register */
int ft1000_read_register(struct ft1000_usb *ft1000dev, u16 *Data,
u16 nRegIndx)
{
int ret = 0;
ret = ft1000_control(ft1000dev,
usb_rcvctrlpipe(ft1000dev->dev, 0),
HARLEY_READ_REGISTER,
HARLEY_READ_OPERATION,
0,
nRegIndx,
Data,
2,
USB_CTRL_GET_TIMEOUT);
return ret;
}
/* writes the value in a register */
int ft1000_write_register(struct ft1000_usb *ft1000dev, u16 value,
u16 nRegIndx)
{
int ret = 0;
ret = ft1000_control(ft1000dev,
usb_sndctrlpipe(ft1000dev->dev, 0),
HARLEY_WRITE_REGISTER,
HARLEY_WRITE_OPERATION,
value,
nRegIndx,
NULL,
0,
USB_CTRL_SET_TIMEOUT);
return ret;
}
/* read a number of bytes from DPRAM */
int ft1000_read_dpram32(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer,
u16 cnt)
{
int ret = 0;
ret = ft1000_control(ft1000dev,
usb_rcvctrlpipe(ft1000dev->dev, 0),
HARLEY_READ_DPRAM_32,
HARLEY_READ_OPERATION,
0,
indx,
buffer,
cnt,
USB_CTRL_GET_TIMEOUT);
return ret;
}
/* writes into DPRAM a number of bytes */
int ft1000_write_dpram32(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer,
u16 cnt)
{
int ret = 0;
if (cnt % 4)
cnt += cnt - (cnt % 4);
ret = ft1000_control(ft1000dev,
usb_sndctrlpipe(ft1000dev->dev, 0),
HARLEY_WRITE_DPRAM_32,
HARLEY_WRITE_OPERATION,
0,
indx,
buffer,
cnt,
USB_CTRL_SET_TIMEOUT);
return ret;
}
/* read 16 bits from DPRAM */
int ft1000_read_dpram16(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer,
u8 highlow)
{
int ret = 0;
u8 request;
if (highlow == 0)
request = HARLEY_READ_DPRAM_LOW;
else
request = HARLEY_READ_DPRAM_HIGH;
ret = ft1000_control(ft1000dev,
usb_rcvctrlpipe(ft1000dev->dev, 0),
request,
HARLEY_READ_OPERATION,
0,
indx,
buffer,
2,
USB_CTRL_GET_TIMEOUT);
return ret;
}
/* write into DPRAM a number of bytes */
int ft1000_write_dpram16(struct ft1000_usb *ft1000dev, u16 indx, u16 value,
u8 highlow)
{
int ret = 0;
u8 request;
if (highlow == 0)
request = HARLEY_WRITE_DPRAM_LOW;
else
request = HARLEY_WRITE_DPRAM_HIGH;
ret = ft1000_control(ft1000dev,
usb_sndctrlpipe(ft1000dev->dev, 0),
request,
HARLEY_WRITE_OPERATION,
value,
indx,
NULL,
0,
USB_CTRL_SET_TIMEOUT);
return ret;
}
/* read DPRAM 4 words at a time */
int fix_ft1000_read_dpram32(struct ft1000_usb *ft1000dev, u16 indx,
u8 *buffer)
{
u8 buf[16];
u16 pos;
int ret = 0;
pos = (indx / 4) * 4;
ret = ft1000_read_dpram32(ft1000dev, pos, buf, 16);
if (ret == 0) {
pos = (indx % 4) * 4;
*buffer++ = buf[pos++];
*buffer++ = buf[pos++];
*buffer++ = buf[pos++];
*buffer++ = buf[pos++];
} else {
pr_debug("DPRAM32 Read failed\n");
*buffer++ = 0;
*buffer++ = 0;
*buffer++ = 0;
*buffer++ = 0;
}
return ret;
}
/* Description: This function write to DPRAM 4 words at a time */
int fix_ft1000_write_dpram32(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer)
{
u16 pos1;
u16 pos2;
u16 i;
u8 buf[32];
u8 resultbuffer[32];
u8 *pdata;
int ret = 0;
pos1 = (indx / 4) * 4;
pdata = buffer;
ret = ft1000_read_dpram32(ft1000dev, pos1, buf, 16);
if (ret == 0) {
pos2 = (indx % 4)*4;
buf[pos2++] = *buffer++;
buf[pos2++] = *buffer++;
buf[pos2++] = *buffer++;
buf[pos2++] = *buffer++;
ret = ft1000_write_dpram32(ft1000dev, pos1, buf, 16);
} else {
pr_debug("DPRAM32 Read failed\n");
return ret;
}
ret = ft1000_read_dpram32(ft1000dev, pos1, (u8 *)&resultbuffer[0], 16);
if (ret == 0) {
buffer = pdata;
for (i = 0; i < 16; i++) {
if (buf[i] != resultbuffer[i])
ret = -1;
}
}
if (ret == -1) {
ret = ft1000_write_dpram32(ft1000dev, pos1,
(u8 *)&tempbuffer[0], 16);
ret = ft1000_read_dpram32(ft1000dev, pos1,
(u8 *)&resultbuffer[0], 16);
if (ret == 0) {
buffer = pdata;
for (i = 0; i < 16; i++) {
if (tempbuffer[i] != resultbuffer[i]) {
ret = -1;
pr_debug("Failed to write\n");
}
}
}
}
return ret;
}
/* reset or activate the DSP */
static void card_reset_dsp(struct ft1000_usb *ft1000dev, bool value)
{
int status = 0;
u16 tempword;
status = ft1000_write_register(ft1000dev, HOST_INTF_BE,
FT1000_REG_SUP_CTRL);
status = ft1000_read_register(ft1000dev, &tempword,
FT1000_REG_SUP_CTRL);
if (value) {
pr_debug("Reset DSP\n");
status = ft1000_read_register(ft1000dev, &tempword,
FT1000_REG_RESET);
tempword |= DSP_RESET_BIT;
status = ft1000_write_register(ft1000dev, tempword,
FT1000_REG_RESET);
} else {
pr_debug("Activate DSP\n");
status = ft1000_read_register(ft1000dev, &tempword,
FT1000_REG_RESET);
tempword |= DSP_ENCRYPTED;
tempword &= ~DSP_UNENCRYPTED;
status = ft1000_write_register(ft1000dev, tempword,
FT1000_REG_RESET);
status = ft1000_read_register(ft1000dev, &tempword,
FT1000_REG_RESET);
tempword &= ~EFUSE_MEM_DISABLE;
tempword &= ~DSP_RESET_BIT;
status = ft1000_write_register(ft1000dev, tempword,
FT1000_REG_RESET);
status = ft1000_read_register(ft1000dev, &tempword,
FT1000_REG_RESET);
}
}
/* send a command to ASIC
* Parameters: ft1000_usb - device structure
* ptempbuffer - command buffer
* size - command buffer size
*/
int card_send_command(struct ft1000_usb *ft1000dev, void *ptempbuffer,
int size)
{
int ret;
unsigned short temp;
unsigned char *commandbuf;
pr_debug("enter card_send_command... size=%d\n", size);
ret = ft1000_read_register(ft1000dev, &temp, FT1000_REG_DOORBELL);
if (ret)
return ret;
commandbuf = kmalloc(size + 2, GFP_KERNEL);
if (!commandbuf)
return -ENOMEM;
memcpy((void *)commandbuf + 2, ptempbuffer, size);
if (temp & 0x0100)
usleep_range(900, 1100);
/* check for odd word */
size = size + 2;
/* Must force to be 32 bit aligned */
if (size % 4)
size += 4 - (size % 4);
ret = ft1000_write_dpram32(ft1000dev, 0, commandbuf, size);
if (ret)
return ret;
usleep_range(900, 1100);
ret = ft1000_write_register(ft1000dev, FT1000_DB_DPRAM_TX,
FT1000_REG_DOORBELL);
if (ret)
return ret;
usleep_range(900, 1100);
ret = ft1000_read_register(ft1000dev, &temp, FT1000_REG_DOORBELL);
#if 0
if ((temp & 0x0100) == 0)
pr_debug("Message sent\n");
#endif
return ret;
}
/* load or reload the DSP */
int dsp_reload(struct ft1000_usb *ft1000dev)
{
int status;
u16 tempword;
u32 templong;
struct ft1000_info *pft1000info;
pft1000info = netdev_priv(ft1000dev->net);
pft1000info->CardReady = 0;
/* Program Interrupt Mask register */
status = ft1000_write_register(ft1000dev, 0xffff, FT1000_REG_SUP_IMASK);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
tempword |= ASIC_RESET_BIT;
status = ft1000_write_register(ft1000dev, tempword, FT1000_REG_RESET);
msleep(1000);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
pr_debug("Reset Register = 0x%x\n", tempword);
/* Toggle DSP reset */
card_reset_dsp(ft1000dev, 1);
msleep(1000);
card_reset_dsp(ft1000dev, 0);
msleep(1000);
status = ft1000_write_register(ft1000dev, HOST_INTF_BE,
FT1000_REG_SUP_CTRL);
/* Let's check for FEFE */
status =
ft1000_read_dpram32(ft1000dev, FT1000_MAG_DPRAM_FEFE_INDX,
(u8 *)&templong, 4);
pr_debug("templong (fefe) = 0x%8x\n", templong);
/* call codeloader */
status = scram_dnldr(ft1000dev, pFileStart, FileLength);
if (status != 0)
return -EIO;
msleep(1000);
return 0;
}
/* call the Card Service function to reset the ASIC. */
static void ft1000_reset_asic(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
struct ft1000_usb *ft1000dev = info->priv;
u16 tempword;
/* Let's use the register provided by the Magnemite ASIC to reset the
* ASIC and DSP.
*/
ft1000_write_register(ft1000dev, DSP_RESET_BIT | ASIC_RESET_BIT,
FT1000_REG_RESET);
mdelay(1);
/* set watermark to -1 in order to not generate an interrupt */
ft1000_write_register(ft1000dev, 0xffff, FT1000_REG_MAG_WATERMARK);
/* clear interrupts */
ft1000_read_register(ft1000dev, &tempword, FT1000_REG_SUP_ISR);
pr_debug("interrupt status register = 0x%x\n", tempword);
ft1000_write_register(ft1000dev, tempword, FT1000_REG_SUP_ISR);
ft1000_read_register(ft1000dev, &tempword, FT1000_REG_SUP_ISR);
pr_debug("interrupt status register = 0x%x\n", tempword);
}
static int ft1000_reset_card(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
struct ft1000_usb *ft1000dev = info->priv;
u16 tempword;
struct prov_record *ptr;
struct prov_record *tmp;
ft1000dev->fCondResetPend = true;
info->CardReady = 0;
ft1000dev->fProvComplete = false;
/* Make sure we free any memory reserve for provisioning */
list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) {
pr_debug("deleting provisioning record\n");
list_del(&ptr->list);
kfree(ptr->pprov_data);
kfree(ptr);
}
pr_debug("reset asic\n");
ft1000_reset_asic(dev);
pr_debug("call dsp_reload\n");
dsp_reload(ft1000dev);
pr_debug("dsp reload successful\n");
mdelay(10);
/* Initialize DSP heartbeat area */
ft1000_write_dpram16(ft1000dev, FT1000_MAG_HI_HO, ho_mag,
FT1000_MAG_HI_HO_INDX);
ft1000_read_dpram16(ft1000dev, FT1000_MAG_HI_HO, (u8 *)&tempword,
FT1000_MAG_HI_HO_INDX);
pr_debug("hi_ho value = 0x%x\n", tempword);
info->CardReady = 1;
ft1000dev->fCondResetPend = false;
return TRUE;
}
/* callback function when a urb is transmitted */
static void ft1000_usb_transmit_complete(struct urb *urb)
{
struct ft1000_usb *ft1000dev = urb->context;
if (urb->status)
pr_err("%s: TX status %d\n", ft1000dev->net->name, urb->status);
netif_wake_queue(ft1000dev->net);
}
/* take an ethernet packet and convert it to a Flarion
* packet prior to sending it to the ASIC Downlink FIFO.
*/
static int ft1000_copy_down_pkt(struct net_device *netdev, u8 *packet, u16 len)
{
struct ft1000_info *pInfo = netdev_priv(netdev);
struct ft1000_usb *pFt1000Dev = pInfo->priv;
int count, ret;
u8 *t;
struct pseudo_hdr hdr;
if (!pInfo->CardReady) {
pr_debug("Card Not Ready\n");
return -ENODEV;
}
count = sizeof(struct pseudo_hdr) + len;
if (count > MAX_BUF_SIZE) {
pr_debug("Message Size Overflow! size = %d\n", count);
return -EINVAL;
}
if (count % 4)
count = count + (4 - (count % 4));
memset(&hdr, 0, sizeof(struct pseudo_hdr));
hdr.length = ntohs(count);
hdr.source = 0x10;
hdr.destination = 0x20;
hdr.portdest = 0x20;
hdr.portsrc = 0x10;
hdr.sh_str_id = 0x91;
hdr.control = 0x00;
hdr.checksum = hdr.length ^ hdr.source ^ hdr.destination ^
hdr.portdest ^ hdr.portsrc ^ hdr.sh_str_id ^ hdr.control;
memcpy(&pFt1000Dev->tx_buf[0], &hdr, sizeof(hdr));
memcpy(&pFt1000Dev->tx_buf[sizeof(struct pseudo_hdr)], packet, len);
netif_stop_queue(netdev);
usb_fill_bulk_urb(pFt1000Dev->tx_urb,
pFt1000Dev->dev,
usb_sndbulkpipe(pFt1000Dev->dev,
pFt1000Dev->bulk_out_endpointAddr),
pFt1000Dev->tx_buf, count,
ft1000_usb_transmit_complete, pFt1000Dev);
t = (u8 *)pFt1000Dev->tx_urb->transfer_buffer;
ret = usb_submit_urb(pFt1000Dev->tx_urb, GFP_ATOMIC);
if (ret) {
pr_debug("failed tx_urb %d\n", ret);
return ret;
}
pInfo->stats.tx_packets++;
pInfo->stats.tx_bytes += (len + 14);
return 0;
}
/* transmit an ethernet packet
* Parameters: skb - socket buffer to be sent
* dev - network device
*/
static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ft1000_info *pInfo = netdev_priv(dev);
struct ft1000_usb *pFt1000Dev = pInfo->priv;
u8 *pdata;
int maxlen, pipe;
if (skb == NULL) {
pr_debug("skb == NULL!!!\n");
return NETDEV_TX_OK;
}
if (pFt1000Dev->status & FT1000_STATUS_CLOSING) {
pr_debug("network driver is closed, return\n");
goto err;
}
pipe = usb_sndbulkpipe(pFt1000Dev->dev,
pFt1000Dev->bulk_out_endpointAddr);
maxlen = usb_maxpacket(pFt1000Dev->dev, pipe, usb_pipeout(pipe));
pdata = (u8 *)skb->data;
if (pInfo->mediastate == 0) {
/* Drop packet is mediastate is down */
pr_debug("mediastate is down\n");
goto err;
}
if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) {
/* Drop packet which has invalid size */
pr_debug("invalid ethernet length\n");
goto err;
}
ft1000_copy_down_pkt(dev, pdata + ENET_HEADER_SIZE - 2,
skb->len - ENET_HEADER_SIZE + 2);
err:
dev_kfree_skb(skb);
return NETDEV_TX_OK;
}
/* open the network driver */
static int ft1000_open(struct net_device *dev)
{
struct ft1000_info *pInfo = netdev_priv(dev);
struct ft1000_usb *pFt1000Dev = pInfo->priv;
struct timeval tv;
pr_debug("ft1000_open is called for card %d\n", pFt1000Dev->CardNumber);
pInfo->stats.rx_bytes = 0;
pInfo->stats.tx_bytes = 0;
pInfo->stats.rx_packets = 0;
pInfo->stats.tx_packets = 0;
do_gettimeofday(&tv);
pInfo->ConTm = tv.tv_sec;
pInfo->ProgConStat = 0;
netif_start_queue(dev);
netif_carrier_on(dev);
return ft1000_submit_rx_urb(pInfo);
}
static struct net_device_stats *ft1000_netdev_stats(struct net_device *dev)
{
struct ft1000_info *info = netdev_priv(dev);
return &(info->stats);
}
static const struct net_device_ops ftnet_ops = {
.ndo_open = &ft1000_open,
.ndo_stop = &ft1000_close,
.ndo_start_xmit = &ft1000_start_xmit,
.ndo_get_stats = &ft1000_netdev_stats,
};
/* initialize the network device */
static int ft1000_reset(void *dev)
{
ft1000_reset_card(dev);
return 0;
}
int init_ft1000_netdev(struct ft1000_usb *ft1000dev)
{
struct net_device *netdev;
struct ft1000_info *pInfo = NULL;
struct dpram_blk *pdpram_blk;
int i, ret_val;
struct list_head *cur, *tmp;
char card_nr[2];
u8 gCardIndex = 0;
netdev = alloc_etherdev(sizeof(struct ft1000_info));
if (!netdev) {
pr_debug("can not allocate network device\n");
return -ENOMEM;
}
pInfo = netdev_priv(netdev);
memset(pInfo, 0, sizeof(struct ft1000_info));
dev_alloc_name(netdev, netdev->name);
pr_debug("network device name is %s\n", netdev->name);
if (strncmp(netdev->name, "eth", 3) == 0) {
card_nr[0] = netdev->name[3];
card_nr[1] = '\0';
ret_val = kstrtou8(card_nr, 10, &gCardIndex);
if (ret_val) {
netdev_err(ft1000dev->net, "Can't parse netdev\n");
goto err_net;
}
ft1000dev->CardNumber = gCardIndex;
pr_debug("card number = %d\n", ft1000dev->CardNumber);
} else {
netdev_err(ft1000dev->net, "ft1000: Invalid device name\n");
ret_val = -ENXIO;
goto err_net;
}
memset(&pInfo->stats, 0, sizeof(struct net_device_stats));
spin_lock_init(&pInfo->dpram_lock);
pInfo->priv = ft1000dev;
pInfo->DrvErrNum = 0;
pInfo->registered = 1;
pInfo->ft1000_reset = ft1000_reset;
pInfo->mediastate = 0;
pInfo->fifo_cnt = 0;
ft1000dev->DeviceCreated = FALSE;
pInfo->CardReady = 0;
pInfo->DSP_TIME[0] = 0;
pInfo->DSP_TIME[1] = 0;
pInfo->DSP_TIME[2] = 0;
pInfo->DSP_TIME[3] = 0;
ft1000dev->fAppMsgPend = false;
ft1000dev->fCondResetPend = false;
ft1000dev->usbboot = 0;
ft1000dev->dspalive = 0;
memset(&ft1000dev->tempbuf[0], 0, sizeof(ft1000dev->tempbuf));
INIT_LIST_HEAD(&pInfo->prov_list);
INIT_LIST_HEAD(&ft1000dev->nodes.list);
netdev->netdev_ops = &ftnet_ops;
ft1000dev->net = netdev;
pr_debug("Initialize free_buff_lock and freercvpool\n");
spin_lock_init(&free_buff_lock);
/* initialize a list of buffers to be use for queuing
* up receive command data
*/
INIT_LIST_HEAD(&freercvpool);
/* create list of free buffers */
for (i = 0; i < NUM_OF_FREE_BUFFERS; i++) {
/* Get memory for DPRAM_DATA link list */
pdpram_blk = kmalloc(sizeof(struct dpram_blk), GFP_KERNEL);
if (pdpram_blk == NULL) {
ret_val = -ENOMEM;
goto err_free;
}
/* Get a block of memory to store command data */
pdpram_blk->pbuffer = kmalloc(MAX_CMD_SQSIZE, GFP_KERNEL);
if (pdpram_blk->pbuffer == NULL) {
ret_val = -ENOMEM;
kfree(pdpram_blk);
goto err_free;
}
/* link provisioning data */
list_add_tail(&pdpram_blk->list, &freercvpool);
}
numofmsgbuf = NUM_OF_FREE_BUFFERS;
return 0;
err_free:
list_for_each_safe(cur, tmp, &freercvpool) {
pdpram_blk = list_entry(cur, struct dpram_blk, list);
list_del(&pdpram_blk->list);
kfree(pdpram_blk->pbuffer);
kfree(pdpram_blk);
}
err_net:
free_netdev(netdev);
return ret_val;
}
/* register the network driver */
int reg_ft1000_netdev(struct ft1000_usb *ft1000dev,
struct usb_interface *intf)
{
struct net_device *netdev;
struct ft1000_info *pInfo;
int rc;
netdev = ft1000dev->net;
pInfo = netdev_priv(ft1000dev->net);
ft1000_read_register(ft1000dev, &pInfo->AsicID, FT1000_REG_ASIC_ID);
usb_set_intfdata(intf, pInfo);
SET_NETDEV_DEV(netdev, &intf->dev);
rc = register_netdev(netdev);
if (rc) {
pr_debug("could not register network device\n");
free_netdev(netdev);
return rc;
}
ft1000_create_dev(ft1000dev);
pInfo->CardReady = 1;
return 0;
}
/* take a packet from the FIFO up link and
* convert it into an ethernet packet and deliver it to the IP stack
*/
static int ft1000_copy_up_pkt(struct urb *urb)
{
struct ft1000_info *info = urb->context;
struct ft1000_usb *ft1000dev = info->priv;
struct net_device *net = ft1000dev->net;
u16 tempword;
u16 len;
u16 lena;
struct sk_buff *skb;
u16 i;
u8 *pbuffer = NULL;
u8 *ptemp = NULL;
u16 *chksum;
if (ft1000dev->status & FT1000_STATUS_CLOSING) {
pr_debug("network driver is closed, return\n");
return 0;
}
/* Read length */
len = urb->transfer_buffer_length;
lena = urb->actual_length;
chksum = (u16 *)ft1000dev->rx_buf;
tempword = *chksum++;
for (i = 1; i < 7; i++)
tempword ^= *chksum++;
if (tempword != *chksum) {
info->stats.rx_errors++;
ft1000_submit_rx_urb(info);
return -1;
}
skb = dev_alloc_skb(len + 12 + 2);
if (skb == NULL) {
info->stats.rx_errors++;
ft1000_submit_rx_urb(info);
return -1;
}
pbuffer = (u8 *)skb_put(skb, len + 12);
/* subtract the number of bytes read already */
ptemp = pbuffer;
/* fake MAC address */
*pbuffer++ = net->dev_addr[0];
*pbuffer++ = net->dev_addr[1];
*pbuffer++ = net->dev_addr[2];
*pbuffer++ = net->dev_addr[3];
*pbuffer++ = net->dev_addr[4];
*pbuffer++ = net->dev_addr[5];
*pbuffer++ = 0x00;
*pbuffer++ = 0x07;
*pbuffer++ = 0x35;
*pbuffer++ = 0xff;
*pbuffer++ = 0xff;
*pbuffer++ = 0xfe;
memcpy(pbuffer, ft1000dev->rx_buf + sizeof(struct pseudo_hdr),
len - sizeof(struct pseudo_hdr));
skb->dev = net;
skb->protocol = eth_type_trans(skb, net);
skb->ip_summed = CHECKSUM_UNNECESSARY;
netif_rx(skb);
info->stats.rx_packets++;
/* Add on 12 bytes for MAC address which was removed */
info->stats.rx_bytes += (lena + 12);
ft1000_submit_rx_urb(info);
return 0;
}
/* the receiving function of the network driver */
static int ft1000_submit_rx_urb(struct ft1000_info *info)
{
int result;
struct ft1000_usb *pFt1000Dev = info->priv;
if (pFt1000Dev->status & FT1000_STATUS_CLOSING) {
pr_debug("network driver is closed, return\n");
return -ENODEV;
}
usb_fill_bulk_urb(pFt1000Dev->rx_urb,
pFt1000Dev->dev,
usb_rcvbulkpipe(pFt1000Dev->dev,
pFt1000Dev->bulk_in_endpointAddr),
pFt1000Dev->rx_buf, MAX_BUF_SIZE,
(usb_complete_t)ft1000_copy_up_pkt, info);
result = usb_submit_urb(pFt1000Dev->rx_urb, GFP_ATOMIC);
if (result) {
pr_err("submitting rx_urb %d failed\n", result);
return result;
}
return 0;
}
/* close the network driver */
int ft1000_close(struct net_device *net)
{
struct ft1000_info *pInfo = netdev_priv(net);
struct ft1000_usb *ft1000dev = pInfo->priv;
ft1000dev->status |= FT1000_STATUS_CLOSING;
pr_debug("pInfo=%p, ft1000dev=%p\n", pInfo, ft1000dev);
netif_carrier_off(net);
netif_stop_queue(net);
ft1000dev->status &= ~FT1000_STATUS_CLOSING;
pInfo->ProgConStat = 0xff;
return 0;
}
/* check if the device is presently available on the system. */
static int ft1000_chkcard(struct ft1000_usb *dev)
{
u16 tempword;
int status;
if (dev->fCondResetPend) {
pr_debug("Card is being reset, return FALSE\n");
return TRUE;
}
/* Mask register is used to check for device presence since it is never
* set to zero.
*/
status = ft1000_read_register(dev, &tempword, FT1000_REG_SUP_IMASK);
if (tempword == 0) {
pr_debug("IMASK = 0 Card not detected\n");
return FALSE;
}
/* The system will return the value of 0xffff for the version register
* if the device is not present.
*/
status = ft1000_read_register(dev, &tempword, FT1000_REG_ASIC_ID);
if (tempword != 0x1b01) {
dev->status |= FT1000_STATUS_CLOSING;
pr_debug("Version = 0xffff Card not detected\n");
return FALSE;
}
return TRUE;
}
/* read a message from the dpram area.
* Input:
* dev - network device structure
* pbuffer - caller supply address to buffer
*/
static bool ft1000_receive_cmd(struct ft1000_usb *dev, u16 *pbuffer,
int maxsz)
{
u16 size;
int ret;
u16 *ppseudohdr;
int i;
u16 tempword;
ret =
ft1000_read_dpram16(dev, FT1000_MAG_PH_LEN, (u8 *)&size,
FT1000_MAG_PH_LEN_INDX);
size = ntohs(size) + PSEUDOSZ;
if (size > maxsz) {
pr_debug("Invalid command length = %d\n", size);
return FALSE;
}
ppseudohdr = (u16 *)pbuffer;
ft1000_write_register(dev, FT1000_DPRAM_MAG_RX_BASE,
FT1000_REG_DPRAM_ADDR);
ret =
ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAH);
pbuffer++;
ft1000_write_register(dev, FT1000_DPRAM_MAG_RX_BASE + 1,
FT1000_REG_DPRAM_ADDR);
for (i = 0; i <= (size >> 2); i++) {
ret =
ft1000_read_register(dev, pbuffer,
FT1000_REG_MAG_DPDATAL);
pbuffer++;
ret =
ft1000_read_register(dev, pbuffer,
FT1000_REG_MAG_DPDATAH);
pbuffer++;
}
/* copy odd aligned word */
ret =
ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAL);
pbuffer++;
ret =
ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAH);
pbuffer++;
if (size & 0x0001) {
/* copy odd byte from fifo */
ret =
ft1000_read_register(dev, &tempword,
FT1000_REG_DPRAM_DATA);
*pbuffer = ntohs(tempword);
}
/* Check if pseudo header checksum is good
* Calculate pseudo header checksum
*/
tempword = *ppseudohdr++;
for (i = 1; i < 7; i++)
tempword ^= *ppseudohdr++;
if (tempword != *ppseudohdr)
return FALSE;
return TRUE;
}
static int ft1000_dsp_prov(void *arg)
{
struct ft1000_usb *dev = (struct ft1000_usb *)arg;
struct ft1000_info *info = netdev_priv(dev->net);
u16 tempword;
u16 len;
u16 i = 0;
struct prov_record *ptr;
struct pseudo_hdr *ppseudo_hdr;
u16 *pmsg;
int status;
u16 TempShortBuf[256];
while (list_empty(&info->prov_list) == 0) {
pr_debug("DSP Provisioning List Entry\n");
/* Check if doorbell is available */
pr_debug("check if doorbell is cleared\n");
status = ft1000_read_register(dev, &tempword,
FT1000_REG_DOORBELL);
if (status) {
pr_debug("ft1000_read_register error\n");
break;
}
while (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
i++;
if (i == 10) {
pr_debug("message drop\n");
return -1;
}
ft1000_read_register(dev, &tempword,
FT1000_REG_DOORBELL);
}
if (!(tempword & FT1000_DB_DPRAM_TX)) {
pr_debug("*** Provision Data Sent to DSP\n");
/* Send provisioning data */
ptr = list_entry(info->prov_list.next,
struct prov_record, list);
len = *(u16 *)ptr->pprov_data;
len = htons(len);
len += PSEUDOSZ;
pmsg = (u16 *)ptr->pprov_data;
ppseudo_hdr = (struct pseudo_hdr *)pmsg;
/* Insert slow queue sequence number */
ppseudo_hdr->seq_num = info->squeseqnum++;
ppseudo_hdr->portsrc = 0;
/* Calculate new checksum */
ppseudo_hdr->checksum = *pmsg++;
for (i = 1; i < 7; i++)
ppseudo_hdr->checksum ^= *pmsg++;
TempShortBuf[0] = 0;
TempShortBuf[1] = htons(len);
memcpy(&TempShortBuf[2], ppseudo_hdr, len);
status =
ft1000_write_dpram32(dev, 0,
(u8 *)&TempShortBuf[0],
(unsigned short)(len + 2));
status =
ft1000_write_register(dev, FT1000_DB_DPRAM_TX,
FT1000_REG_DOORBELL);
list_del(&ptr->list);
kfree(ptr->pprov_data);
kfree(ptr);
}
usleep_range(9000, 11000);
}
pr_debug("DSP Provisioning List Entry finished\n");
msleep(100);
dev->fProvComplete = true;
info->CardReady = 1;
return 0;
}
static int ft1000_proc_drvmsg(struct ft1000_usb *dev, u16 size)
{
struct ft1000_info *info = netdev_priv(dev->net);
u16 msgtype;
u16 tempword;
struct media_msg *pmediamsg;
struct dsp_init_msg *pdspinitmsg;
struct drv_msg *pdrvmsg;
u16 i;
struct pseudo_hdr *ppseudo_hdr;
u16 *pmsg;
int status;
union {
u8 byte[2];
u16 wrd;
} convert;
char *cmdbuffer = kmalloc(1600, GFP_KERNEL);
if (!cmdbuffer)
return -ENOMEM;
status = ft1000_read_dpram32(dev, 0x200, cmdbuffer, size);
#ifdef JDEBUG
print_hex_dump_debug("cmdbuffer: ", HEX_DUMP_OFFSET, 16, 1,
cmdbuffer, size, true);
#endif
pdrvmsg = (struct drv_msg *)&cmdbuffer[2];
msgtype = ntohs(pdrvmsg->type);
pr_debug("Command message type = 0x%x\n", msgtype);
switch (msgtype) {
case MEDIA_STATE:{
pr_debug("Command message type = MEDIA_STATE\n");
pmediamsg = (struct media_msg *)&cmdbuffer[0];
if (info->ProgConStat != 0xFF) {
if (pmediamsg->state) {
pr_debug("Media is up\n");
if (info->mediastate == 0) {
if (dev->NetDevRegDone)
netif_wake_queue(dev->net);
info->mediastate = 1;
}
} else {
pr_debug("Media is down\n");
if (info->mediastate == 1) {
info->mediastate = 0;
if (dev->NetDevRegDone)
info->ConTm = 0;
}
}
} else {
pr_debug("Media is down\n");
if (info->mediastate == 1) {
info->mediastate = 0;
info->ConTm = 0;
}
}
break;
}
case DSP_INIT_MSG:{
pr_debug("Command message type = DSP_INIT_MSG\n");
pdspinitmsg = (struct dsp_init_msg *)&cmdbuffer[2];
memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ);
pr_debug("DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n",
info->DspVer[0], info->DspVer[1], info->DspVer[2],
info->DspVer[3]);
memcpy(info->HwSerNum, pdspinitmsg->HwSerNum,
HWSERNUMSZ);
memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ);
memcpy(info->eui64, pdspinitmsg->eui64, EUISZ);
pr_debug("EUI64=%2x.%2x.%2x.%2x.%2x.%2x.%2x.%2x\n",
info->eui64[0], info->eui64[1], info->eui64[2],
info->eui64[3], info->eui64[4], info->eui64[5],
info->eui64[6], info->eui64[7]);
dev->net->dev_addr[0] = info->eui64[0];
dev->net->dev_addr[1] = info->eui64[1];
dev->net->dev_addr[2] = info->eui64[2];
dev->net->dev_addr[3] = info->eui64[5];
dev->net->dev_addr[4] = info->eui64[6];
dev->net->dev_addr[5] = info->eui64[7];
if (ntohs(pdspinitmsg->length) ==
(sizeof(struct dsp_init_msg) - 20)) {
memcpy(info->ProductMode, pdspinitmsg->ProductMode,
MODESZ);
memcpy(info->RfCalVer, pdspinitmsg->RfCalVer, CALVERSZ);
memcpy(info->RfCalDate, pdspinitmsg->RfCalDate,
CALDATESZ);
pr_debug("RFCalVer = 0x%2x 0x%2x\n",
info->RfCalVer[0], info->RfCalVer[1]);
}
break;
}
case DSP_PROVISION:{
pr_debug("Command message type = DSP_PROVISION\n");
/* kick off dspprov routine to start provisioning
* Send provisioning data to DSP
*/
if (list_empty(&info->prov_list) == 0) {
dev->fProvComplete = false;
status = ft1000_dsp_prov(dev);
if (status != 0)
goto out;
} else {
dev->fProvComplete = true;
status = ft1000_write_register(dev, FT1000_DB_HB,
FT1000_REG_DOORBELL);
pr_debug("No more DSP provisioning data in dsp image\n");
}
pr_debug("DSP PROVISION is done\n");
break;
}
case DSP_STORE_INFO:{
pr_debug("Command message type = DSP_STORE_INFO");
tempword = ntohs(pdrvmsg->length);
info->DSPInfoBlklen = tempword;
if (tempword < (MAX_DSP_SESS_REC - 4)) {
pmsg = (u16 *)&pdrvmsg->data[0];
for (i = 0; i < ((tempword + 1) / 2); i++) {
pr_debug("dsp info data = 0x%x\n", *pmsg);
info->DSPInfoBlk[i + 10] = *pmsg++;
}
} else {
info->DSPInfoBlklen = 0;
}
break;
}
case DSP_GET_INFO:{
pr_debug("Got DSP_GET_INFO\n");
/* copy dsp info block to dsp */
dev->DrvMsgPend = 1;
/* allow any outstanding ioctl to finish */
mdelay(10);
status = ft1000_read_register(dev, &tempword,
FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
status = ft1000_read_register(dev, &tempword,
FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
status = ft1000_read_register(dev, &tempword,
FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX)
break;
}
}
/* Put message into Slow Queue Form Pseudo header */
pmsg = (u16 *)info->DSPInfoBlk;
*pmsg++ = 0;
*pmsg++ = htons(info->DSPInfoBlklen + 20 + info->DSPInfoBlklen);
ppseudo_hdr =
(struct pseudo_hdr *)(u16 *)&info->DSPInfoBlk[2];
ppseudo_hdr->length = htons(info->DSPInfoBlklen + 4
+ info->DSPInfoBlklen);
ppseudo_hdr->source = 0x10;
ppseudo_hdr->destination = 0x20;
ppseudo_hdr->portdest = 0;
ppseudo_hdr->portsrc = 0;
ppseudo_hdr->sh_str_id = 0;
ppseudo_hdr->control = 0;
ppseudo_hdr->rsvd1 = 0;
ppseudo_hdr->rsvd2 = 0;
ppseudo_hdr->qos_class = 0;
/* Insert slow queue sequence number */
ppseudo_hdr->seq_num = info->squeseqnum++;
/* Insert application id */
ppseudo_hdr->portsrc = 0;
/* Calculate new checksum */
ppseudo_hdr->checksum = *pmsg++;
for (i = 1; i < 7; i++)
ppseudo_hdr->checksum ^= *pmsg++;
info->DSPInfoBlk[10] = 0x7200;
info->DSPInfoBlk[11] = htons(info->DSPInfoBlklen);
status = ft1000_write_dpram32(dev, 0,
(u8 *)&info->DSPInfoBlk[0],
(unsigned short)(info->DSPInfoBlklen + 22));
status = ft1000_write_register(dev, FT1000_DB_DPRAM_TX,
FT1000_REG_DOORBELL);
dev->DrvMsgPend = 0;
break;
}
case GET_DRV_ERR_RPT_MSG:{
pr_debug("Got GET_DRV_ERR_RPT_MSG\n");
/* copy driver error message to dsp */
dev->DrvMsgPend = 1;
/* allow any outstanding ioctl to finish */
mdelay(10);
status = ft1000_read_register(dev, &tempword,
FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
status = ft1000_read_register(dev, &tempword,
FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX)
mdelay(10);
}
if ((tempword & FT1000_DB_DPRAM_TX) == 0) {
/* Put message into Slow Queue Form Pseudo header */
pmsg = (u16 *)&tempbuffer[0];
ppseudo_hdr = (struct pseudo_hdr *)pmsg;
ppseudo_hdr->length = htons(0x0012);
ppseudo_hdr->source = 0x10;
ppseudo_hdr->destination = 0x20;
ppseudo_hdr->portdest = 0;
ppseudo_hdr->portsrc = 0;
ppseudo_hdr->sh_str_id = 0;
ppseudo_hdr->control = 0;
ppseudo_hdr->rsvd1 = 0;
ppseudo_hdr->rsvd2 = 0;
ppseudo_hdr->qos_class = 0;
/* Insert slow queue sequence number */
ppseudo_hdr->seq_num = info->squeseqnum++;
/* Insert application id */
ppseudo_hdr->portsrc = 0;
/* Calculate new checksum */
ppseudo_hdr->checksum = *pmsg++;
for (i = 1; i < 7; i++)
ppseudo_hdr->checksum ^= *pmsg++;
pmsg = (u16 *)&tempbuffer[16];
*pmsg++ = htons(RSP_DRV_ERR_RPT_MSG);
*pmsg++ = htons(0x000e);
*pmsg++ = htons(info->DSP_TIME[0]);
*pmsg++ = htons(info->DSP_TIME[1]);
*pmsg++ = htons(info->DSP_TIME[2]);
*pmsg++ = htons(info->DSP_TIME[3]);
convert.byte[0] = info->DspVer[0];
convert.byte[1] = info->DspVer[1];
*pmsg++ = convert.wrd;
convert.byte[0] = info->DspVer[2];
convert.byte[1] = info->DspVer[3];
*pmsg++ = convert.wrd;
*pmsg++ = htons(info->DrvErrNum);
status = card_send_command(dev,
(unsigned char *)&tempbuffer[0],
(u16)(0x0012 + PSEUDOSZ));
if (status)
goto out;
info->DrvErrNum = 0;
}
dev->DrvMsgPend = 0;
break;
}
default:
break;
}
status = 0;
out:
kfree(cmdbuffer);
return status;
}
/* Check which application has registered for dsp broadcast messages */
static int dsp_broadcast_msg_id(struct ft1000_usb *dev)
{
struct dpram_blk *pdpram_blk;
unsigned long flags;
int i;
for (i = 0; i < MAX_NUM_APP; i++) {
if ((dev->app_info[i].DspBCMsgFlag)
&& (dev->app_info[i].fileobject)
&& (dev->app_info[i].NumOfMsg
< MAX_MSG_LIMIT)) {
pdpram_blk = ft1000_get_buffer(&freercvpool);
if (pdpram_blk == NULL) {
pr_debug("Out of memory in free receive command pool\n");
dev->app_info[i].nRxMsgMiss++;
return -1;
}
if (ft1000_receive_cmd(dev, pdpram_blk->pbuffer,
MAX_CMD_SQSIZE)) {
/* Put message into the
* appropriate application block
*/
dev->app_info[i].nRxMsg++;
spin_lock_irqsave(&free_buff_lock, flags);
list_add_tail(&pdpram_blk->list,
&dev->app_info[i] .app_sqlist);
dev->app_info[i].NumOfMsg++;
spin_unlock_irqrestore(&free_buff_lock, flags);
wake_up_interruptible(&dev->app_info[i]
.wait_dpram_msg);
} else {
dev->app_info[i].nRxMsgMiss++;
ft1000_free_buffer(pdpram_blk, &freercvpool);
pr_debug("ft1000_get_buffer NULL\n");
return -1;
}
}
}
return 0;
}
static int handle_misc_portid(struct ft1000_usb *dev)
{
struct dpram_blk *pdpram_blk;
int i;
pdpram_blk = ft1000_get_buffer(&freercvpool);
if (pdpram_blk == NULL) {
pr_debug("Out of memory in free receive command pool\n");
return -1;
}
if (!ft1000_receive_cmd(dev, pdpram_blk->pbuffer, MAX_CMD_SQSIZE))
goto exit_failure;
/* Search for correct application block */
for (i = 0; i < MAX_NUM_APP; i++) {
if (dev->app_info[i].app_id == ((struct pseudo_hdr *)
pdpram_blk->pbuffer)->portdest)
break;
}
if (i == MAX_NUM_APP) {
pr_debug("No application matching id = %d\n",
((struct pseudo_hdr *)pdpram_blk->pbuffer)->portdest);
goto exit_failure;
} else if (dev->app_info[i].NumOfMsg > MAX_MSG_LIMIT) {
goto exit_failure;
} else {
dev->app_info[i].nRxMsg++;
/* Put message into the appropriate application block */
list_add_tail(&pdpram_blk->list, &dev->app_info[i].app_sqlist);
dev->app_info[i].NumOfMsg++;
}
return 0;
exit_failure:
ft1000_free_buffer(pdpram_blk, &freercvpool);
return -1;
}
int ft1000_poll(void *dev_id)
{
struct ft1000_usb *dev = (struct ft1000_usb *)dev_id;
struct ft1000_info *info = netdev_priv(dev->net);
u16 tempword;
int status;
u16 size;
int i;
u16 data;
u16 modulo;
u16 portid;
if (ft1000_chkcard(dev) == FALSE) {
pr_debug("failed\n");
return -1;
}
status = ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
if (!status) {
if (tempword & FT1000_DB_DPRAM_RX) {
status = ft1000_read_dpram16(dev,
0x200, (u8 *)&data, 0);
size = ntohs(data) + 16 + 2;
if (size % 4) {
modulo = 4 - (size % 4);
size = size + modulo;
}
status = ft1000_read_dpram16(dev, 0x201,
(u8 *)&portid, 1);
portid &= 0xff;
if (size < MAX_CMD_SQSIZE) {
switch (portid) {
case DRIVERID:
pr_debug("FT1000_REG_DOORBELL message type: FT1000_DB_DPRAM_RX : portid DRIVERID\n");
status = ft1000_proc_drvmsg(dev, size);
if (status != 0)
return status;
break;
case DSPBCMSGID:
status = dsp_broadcast_msg_id(dev);
break;
default:
status = handle_misc_portid(dev);
break;
}
} else
pr_debug("Invalid total length for SlowQ = %d\n",
size);
status = ft1000_write_register(dev,
FT1000_DB_DPRAM_RX,
FT1000_REG_DOORBELL);
} else if (tempword & FT1000_DSP_ASIC_RESET) {
/* Let's reset the ASIC from the Host side as well */
status = ft1000_write_register(dev, ASIC_RESET_BIT,
FT1000_REG_RESET);
status = ft1000_read_register(dev, &tempword,
FT1000_REG_RESET);
i = 0;
while (tempword & ASIC_RESET_BIT) {
status = ft1000_read_register(dev, &tempword,
FT1000_REG_RESET);
usleep_range(9000, 11000);
i++;
if (i == 100)
break;
}
if (i == 100) {
pr_debug("Unable to reset ASIC\n");
return 0;
}
usleep_range(9000, 11000);
/* Program WMARK register */
status = ft1000_write_register(dev, 0x600,
FT1000_REG_MAG_WATERMARK);
/* clear ASIC reset doorbell */
status = ft1000_write_register(dev,
FT1000_DSP_ASIC_RESET,
FT1000_REG_DOORBELL);
usleep_range(9000, 11000);
} else if (tempword & FT1000_ASIC_RESET_REQ) {
pr_debug("FT1000_REG_DOORBELL message type: FT1000_ASIC_RESET_REQ\n");
/* clear ASIC reset request from DSP */
status = ft1000_write_register(dev,
FT1000_ASIC_RESET_REQ,
FT1000_REG_DOORBELL);
status = ft1000_write_register(dev, HOST_INTF_BE,
FT1000_REG_SUP_CTRL);
/* copy dsp session record from Adapter block */
status = ft1000_write_dpram32(dev, 0,
(u8 *)&info->DSPSess.Rec[0], 1024);
status = ft1000_write_register(dev, 0x600,
FT1000_REG_MAG_WATERMARK);
/* ring doorbell to tell DSP that
* ASIC is out of reset
*/
status = ft1000_write_register(dev,
FT1000_ASIC_RESET_DSP,
FT1000_REG_DOORBELL);
} else if (tempword & FT1000_DB_COND_RESET) {
pr_debug("FT1000_REG_DOORBELL message type: FT1000_DB_COND_RESET\n");
if (!dev->fAppMsgPend) {
/* Reset ASIC and DSP */
status = ft1000_read_dpram16(dev,
FT1000_MAG_DSP_TIMER0,
(u8 *)&info->DSP_TIME[0],
FT1000_MAG_DSP_TIMER0_INDX);
status = ft1000_read_dpram16(dev,
FT1000_MAG_DSP_TIMER1,
(u8 *)&info->DSP_TIME[1],
FT1000_MAG_DSP_TIMER1_INDX);
status = ft1000_read_dpram16(dev,
FT1000_MAG_DSP_TIMER2,
(u8 *)&info->DSP_TIME[2],
FT1000_MAG_DSP_TIMER2_INDX);
status = ft1000_read_dpram16(dev,
FT1000_MAG_DSP_TIMER3,
(u8 *)&info->DSP_TIME[3],
FT1000_MAG_DSP_TIMER3_INDX);
info->CardReady = 0;
info->DrvErrNum = DSP_CONDRESET_INFO;
pr_debug("DSP conditional reset requested\n");
info->ft1000_reset(dev->net);
} else {
dev->fProvComplete = false;
dev->fCondResetPend = true;
}
ft1000_write_register(dev, FT1000_DB_COND_RESET,
FT1000_REG_DOORBELL);
}
}
return 0;
}
/*
*---------------------------------------------------------------------------
* FT1000 driver for Flarion Flash OFDM NIC Device
*
* Copyright (C) 2002 Flarion Technologies, All rights reserved.
*
* 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. 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., 59 Temple Place -
* Suite 330, Boston, MA 02111-1307, USA.
*---------------------------------------------------------------------------
*
* File: ft1000_ioctl.h
*
* Description: Common structures and defines relating to IOCTL
*
* History:
* 11/5/02 Whc Created.
*
*---------------------------------------------------------------------------
*/
#ifndef _FT1000IOCTLH_
#define _FT1000IOCTLH_
struct IOCTL_GET_VER {
unsigned long drv_ver;
} __packed;
/* Data structure for Dsp statistics */
struct IOCTL_GET_DSP_STAT {
unsigned char DspVer[DSPVERSZ]; /* DSP version number */
unsigned char HwSerNum[HWSERNUMSZ]; /* Hardware Serial Number */
unsigned char Sku[SKUSZ]; /* SKU */
unsigned char eui64[EUISZ]; /* EUI64 */
unsigned short ConStat; /* Connection Status */
/* Bits 0-3 = Connection Status Field */
/* 0000=Idle (Disconnect) */
/* 0001=Searching */
/* 0010=Active (Connected) */
/* 0011=Waiting for L2 down */
/* 0100=Sleep */
unsigned short LedStat; /* Led Status */
/* Bits 0-3 = Signal Strength Field */
/* 0000 = -105dBm to -92dBm */
/* 0001 = -92dBm to -85dBm */
/* 0011 = -85dBm to -75dBm */
/* 0111 = -75dBm to -50dBm */
/* 1111 = -50dBm to 0dBm */
/* Bits 4-7 = Reserved */
/* Bits 8-11 = SNR Field */
/* 0000 = <2dB */
/* 0001 = 2dB to 8dB */
/* 0011 = 8dB to 15dB */
/* 0111 = 15dB to 22dB */
/* 1111 = >22dB */
/* Bits 12-15 = Reserved */
unsigned long nTxPkts; /* Number of packets transmitted
* from host to dsp
*/
unsigned long nRxPkts; /* Number of packets received from
* dsp to host
*/
unsigned long nTxBytes; /* Number of bytes transmitted
* from host to dsp
*/
unsigned long nRxBytes; /* Number of bytes received from
* dsp to host
*/
unsigned long ConTm; /* Current session connection time
* in seconds
*/
unsigned char CalVer[CALVERSZ]; /* Proprietary Calibration
* Version
*/
unsigned char CalDate[CALDATESZ]; /* Proprietary Calibration Date */
} __packed;
/* Data structure for Dual Ported RAM messaging between Host and Dsp */
struct IOCTL_DPRAM_BLK {
unsigned short total_len;
struct pseudo_hdr pseudohdr;
unsigned char buffer[1780];
} __packed;
struct IOCTL_DPRAM_COMMAND {
unsigned short extra;
struct IOCTL_DPRAM_BLK dpram_blk;
} __packed;
/*
* Custom IOCTL command codes
*/
#define FT1000_MAGIC_CODE 'F'
#define IOCTL_REGISTER_CMD 0
#define IOCTL_SET_DPRAM_CMD 3
#define IOCTL_GET_DPRAM_CMD 4
#define IOCTL_GET_DSP_STAT_CMD 6
#define IOCTL_GET_VER_CMD 7
#define IOCTL_CONNECT 10
#define IOCTL_DISCONNECT 11
#define IOCTL_FT1000_GET_DSP_STAT _IOR(FT1000_MAGIC_CODE, \
IOCTL_GET_DSP_STAT_CMD, \
struct IOCTL_GET_DSP_STAT)
#define IOCTL_FT1000_GET_VER _IOR(FT1000_MAGIC_CODE, IOCTL_GET_VER_CMD, \
struct IOCTL_GET_VER)
#define IOCTL_FT1000_CONNECT _IO(FT1000_MAGIC_CODE, IOCTL_CONNECT)
#define IOCTL_FT1000_DISCONNECT _IO(FT1000_MAGIC_CODE, IOCTL_DISCONNECT)
#define IOCTL_FT1000_SET_DPRAM _IOW(FT1000_MAGIC_CODE, IOCTL_SET_DPRAM_CMD, \
struct IOCTL_DPRAM_BLK)
#define IOCTL_FT1000_GET_DPRAM _IOR(FT1000_MAGIC_CODE, IOCTL_GET_DPRAM_CMD, \
struct IOCTL_DPRAM_BLK)
#define IOCTL_FT1000_REGISTER _IOW(FT1000_MAGIC_CODE, IOCTL_REGISTER_CMD, \
unsigned short *)
#endif /* _FT1000IOCTLH_ */
/*=====================================================
* CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
*
*
* This file is part of Express Card USB Driver
*====================================================
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/firmware.h>
#include "ft1000_usb.h"
#include <linux/kthread.h>
MODULE_DESCRIPTION("FT1000 EXPRESS CARD DRIVER");
MODULE_LICENSE("Dual MPL/GPL");
MODULE_SUPPORTED_DEVICE("QFT FT1000 Express Cards");
void *pFileStart;
size_t FileLength;
#define VENDOR_ID 0x1291 /* Qualcomm vendor id */
#define PRODUCT_ID 0x11 /* fake product id */
/* table of devices that work with this driver */
static struct usb_device_id id_table[] = {
{USB_DEVICE(VENDOR_ID, PRODUCT_ID)},
{},
};
MODULE_DEVICE_TABLE(usb, id_table);
static bool gPollingfailed;
static int ft1000_poll_thread(void *arg)
{
int ret;
while (!kthread_should_stop()) {
usleep_range(10000, 11000);
if (!gPollingfailed) {
ret = ft1000_poll(arg);
if (ret != 0) {
pr_debug("polling failed\n");
gPollingfailed = true;
}
}
}
return 0;
}
static int ft1000_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *dev;
unsigned numaltsetting;
int i, ret = 0, size;
struct ft1000_usb *ft1000dev;
struct ft1000_info *pft1000info = NULL;
const struct firmware *dsp_fw;
ft1000dev = kzalloc(sizeof(struct ft1000_usb), GFP_KERNEL);
if (!ft1000dev)
return -ENOMEM;
dev = interface_to_usbdev(interface);
pr_debug("usb device descriptor info - number of configuration is %d\n",
dev->descriptor.bNumConfigurations);
ft1000dev->dev = dev;
ft1000dev->status = 0;
ft1000dev->net = NULL;
ft1000dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
ft1000dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ft1000dev->tx_urb || !ft1000dev->rx_urb) {
ret = -ENOMEM;
goto err_fw;
}
numaltsetting = interface->num_altsetting;
pr_debug("number of alt settings is: %d\n", numaltsetting);
iface_desc = interface->cur_altsetting;
pr_debug("number of endpoints is: %d\n",
iface_desc->desc.bNumEndpoints);
pr_debug("descriptor type is: %d\n", iface_desc->desc.bDescriptorType);
pr_debug("interface number is: %d\n",
iface_desc->desc.bInterfaceNumber);
pr_debug("alternatesetting is: %d\n",
iface_desc->desc.bAlternateSetting);
pr_debug("interface class is: %d\n", iface_desc->desc.bInterfaceClass);
pr_debug("control endpoint info:\n");
pr_debug("descriptor0 type -- %d\n",
iface_desc->endpoint[0].desc.bmAttributes);
pr_debug("descriptor1 type -- %d\n",
iface_desc->endpoint[1].desc.bmAttributes);
pr_debug("descriptor2 type -- %d\n",
iface_desc->endpoint[2].desc.bmAttributes);
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
endpoint =
(struct usb_endpoint_descriptor *)&iface_desc->
endpoint[i].desc;
pr_debug("endpoint %d\n", i);
pr_debug("bEndpointAddress=%x, bmAttributes=%x\n",
endpoint->bEndpointAddress, endpoint->bmAttributes);
if (usb_endpoint_is_bulk_in(endpoint)) {
ft1000dev->bulk_in_endpointAddr =
endpoint->bEndpointAddress;
pr_debug("in: %d\n", endpoint->bEndpointAddress);
}
if (usb_endpoint_is_bulk_in(endpoint)) {
ft1000dev->bulk_out_endpointAddr =
endpoint->bEndpointAddress;
pr_debug("out: %d\n", endpoint->bEndpointAddress);
}
}
pr_debug("bulk_in=%d, bulk_out=%d\n",
ft1000dev->bulk_in_endpointAddr,
ft1000dev->bulk_out_endpointAddr);
ret = request_firmware(&dsp_fw, "ft3000.img", &dev->dev);
if (ret < 0) {
dev_err(interface->usb_dev, "Error request_firmware()\n");
goto err_fw;
}
size = max_t(uint, dsp_fw->size, 4096);
pFileStart = kmalloc(size, GFP_KERNEL);
if (!pFileStart) {
release_firmware(dsp_fw);
ret = -ENOMEM;
goto err_fw;
}
memcpy(pFileStart, dsp_fw->data, dsp_fw->size);
FileLength = dsp_fw->size;
release_firmware(dsp_fw);
pr_debug("start downloading dsp image...\n");
ret = init_ft1000_netdev(ft1000dev);
if (ret)
goto err_load;
pft1000info = netdev_priv(ft1000dev->net);
pr_debug("pft1000info=%p\n", pft1000info);
ret = dsp_reload(ft1000dev);
if (ret) {
dev_err(interface->usb_dev,
"Problem with DSP image loading\n");
goto err_load;
}
gPollingfailed = false;
ft1000dev->pPollThread =
kthread_run(ft1000_poll_thread, ft1000dev, "ft1000_poll");
if (IS_ERR(ft1000dev->pPollThread)) {
ret = PTR_ERR(ft1000dev->pPollThread);
goto err_load;
}
msleep(500);
while (!pft1000info->CardReady) {
if (gPollingfailed) {
ret = -EIO;
goto err_thread;
}
msleep(100);
pr_debug("Waiting for Card Ready\n");
}
pr_debug("Card Ready!!!! Registering network device\n");
ret = reg_ft1000_netdev(ft1000dev, interface);
if (ret)
goto err_thread;
ft1000dev->NetDevRegDone = 1;
return 0;
err_thread:
kthread_stop(ft1000dev->pPollThread);
err_load:
kfree(pFileStart);
err_fw:
usb_free_urb(ft1000dev->rx_urb);
usb_free_urb(ft1000dev->tx_urb);
kfree(ft1000dev);
return ret;
}
static void ft1000_disconnect(struct usb_interface *interface)
{
struct ft1000_info *pft1000info;
struct ft1000_usb *ft1000dev;
pft1000info = (struct ft1000_info *)usb_get_intfdata(interface);
pr_debug("In disconnect pft1000info=%p\n", pft1000info);
if (pft1000info) {
ft1000dev = pft1000info->priv;
if (ft1000dev->pPollThread)
kthread_stop(ft1000dev->pPollThread);
pr_debug("threads are terminated\n");
if (ft1000dev->net) {
pr_debug("destroy char driver\n");
ft1000_destroy_dev(ft1000dev->net);
unregister_netdev(ft1000dev->net);
pr_debug("network device unregistered\n");
free_netdev(ft1000dev->net);
}
usb_free_urb(ft1000dev->rx_urb);
usb_free_urb(ft1000dev->tx_urb);
pr_debug("urb freed\n");
kfree(ft1000dev);
}
kfree(pFileStart);
}
static struct usb_driver ft1000_usb_driver = {
.name = "ft1000usb",
.probe = ft1000_probe,
.disconnect = ft1000_disconnect,
.id_table = id_table,
};
module_usb_driver(ft1000_usb_driver);
#ifndef _FT1000_USB_H_
#define _FT1000_USB_H_
#include "../ft1000.h"
#include "ft1000_ioctl.h"
#define FT1000_DRV_VER 0x01010403
#define MAX_NUM_APP 6
#define MAX_MSG_LIMIT 200
#define NUM_OF_FREE_BUFFERS 1500
#define PSEUDOSZ 16
struct app_info_block {
u32 nTxMsg; /* DPRAM msg sent to DSP with app_id */
u32 nRxMsg; /* DPRAM msg rcv from dsp with app_id */
u32 nTxMsgReject; /* DPRAM msg rejected due to DSP doorbell
* set
*/
u32 nRxMsgMiss; /* DPRAM msg dropped due to overflow */
struct fown_struct *fileobject;/* Application's file object */
u16 app_id; /* Application id */
int DspBCMsgFlag;
int NumOfMsg; /* number of messages queued up */
wait_queue_head_t wait_dpram_msg;
struct list_head app_sqlist; /* link list of msgs for applicaton on
* slow queue
*/
} __packed;
#define FALSE 0
#define TRUE 1
#define FT1000_STATUS_CLOSING 0x01
#define DSPBCMSGID 0x10
/* Electrabuzz specific DPRAM mapping */
/* this is used by ft1000_usb driver - isn't that a bug? */
#undef FT1000_DPRAM_RX_BASE
#define FT1000_DPRAM_RX_BASE 0x1800 /* RX AREA (SlowQ) */
/* MEMORY MAP FOR MAGNEMITE */
/* the indexes are swapped comparing to PCMCIA - is it OK or a bug? */
#undef FT1000_MAG_DSP_LED_INDX
#define FT1000_MAG_DSP_LED_INDX 0x1 /* dsp led status for PAD
* device
*/
#undef FT1000_MAG_DSP_CON_STATE_INDX
#define FT1000_MAG_DSP_CON_STATE_INDX 0x0 /* DSP Connection Status Info */
/* Maximum times trying to get ASIC out of reset */
#define MAX_ASIC_RESET_CNT 20
#define MAX_BUF_SIZE 4096
struct ft1000_debug_dirs {
struct list_head list;
struct dentry *dent;
struct dentry *file;
int int_number;
};
struct ft1000_usb {
struct usb_device *dev;
struct net_device *net;
u32 status;
struct urb *rx_urb;
struct urb *tx_urb;
u8 tx_buf[MAX_BUF_SIZE];
u8 rx_buf[MAX_BUF_SIZE];
u8 bulk_in_endpointAddr;
u8 bulk_out_endpointAddr;
struct task_struct *pPollThread;
unsigned char fcodeldr;
unsigned char bootmode;
unsigned char usbboot;
unsigned short dspalive;
bool fProvComplete;
bool fCondResetPend;
bool fAppMsgPend;
int DeviceCreated;
int NetDevRegDone;
u8 CardNumber;
u8 DeviceName[15];
struct ft1000_debug_dirs nodes;
spinlock_t fifo_lock;
int appcnt;
struct app_info_block app_info[MAX_NUM_APP];
u16 DrvMsgPend;
unsigned short tempbuf[32];
} __packed;
struct dpram_blk {
struct list_head list;
u16 *pbuffer;
} __packed;
int ft1000_read_register(struct ft1000_usb *ft1000dev,
u16 *Data, u16 nRegIndx);
int ft1000_write_register(struct ft1000_usb *ft1000dev,
u16 value, u16 nRegIndx);
int ft1000_read_dpram32(struct ft1000_usb *ft1000dev,
u16 indx, u8 *buffer, u16 cnt);
int ft1000_write_dpram32(struct ft1000_usb *ft1000dev,
u16 indx, u8 *buffer, u16 cnt);
int ft1000_read_dpram16(struct ft1000_usb *ft1000dev,
u16 indx, u8 *buffer, u8 highlow);
int ft1000_write_dpram16(struct ft1000_usb *ft1000dev,
u16 indx, u16 value, u8 highlow);
int fix_ft1000_read_dpram32(struct ft1000_usb *ft1000dev,
u16 indx, u8 *buffer);
int fix_ft1000_write_dpram32(struct ft1000_usb *ft1000dev,
u16 indx, u8 *buffer);
extern void *pFileStart;
extern size_t FileLength;
extern int numofmsgbuf;
int ft1000_close(struct net_device *dev);
int scram_dnldr(struct ft1000_usb *ft1000dev, void *pFileStart,
u32 FileLength);
extern struct list_head freercvpool;
/* lock to arbitrate free buffer list for receive command data */
extern spinlock_t free_buff_lock;
int ft1000_create_dev(struct ft1000_usb *dev);
void ft1000_destroy_dev(struct net_device *dev);
int card_send_command(struct ft1000_usb *ft1000dev,
void *ptempbuffer, int size);
struct dpram_blk *ft1000_get_buffer(struct list_head *bufflist);
void ft1000_free_buffer(struct dpram_blk *pdpram_blk, struct list_head *plist);
int dsp_reload(struct ft1000_usb *ft1000dev);
int init_ft1000_netdev(struct ft1000_usb *ft1000dev);
struct usb_interface;
int reg_ft1000_netdev(struct ft1000_usb *ft1000dev,
struct usb_interface *intf);
int ft1000_poll(void *dev_id);
#endif /* _FT1000_USB_H_ */
/*
* Common structures and definitions for FT1000 Flarion Flash OFDM PCMCIA and
* USB devices.
*
* Originally copyright (c) 2002 Flarion Technologies
*
*/
#define DSPVERSZ 4
#define HWSERNUMSZ 16
#define SKUSZ 20
#define EUISZ 8
#define MODESZ 2
#define CALVERSZ 2
#define CALDATESZ 6
#define ELECTRABUZZ_ID 0 /* ASIC ID for Electrabuzz */
#define MAGNEMITE_ID 0x1a01 /* ASIC ID for Magnemite */
/* MEMORY MAP common to both ELECTRABUZZ and MAGNEMITE */
#define FT1000_REG_DPRAM_ADDR 0x000E /* DPADR - Dual Port Ram Indirect
* Address Register
*/
#define FT1000_REG_SUP_CTRL 0x0020 /* HCTR - Host Control Register */
#define FT1000_REG_SUP_STAT 0x0022 /* HSTAT - Host Status Register */
#define FT1000_REG_RESET 0x0024 /* HCTR - Host Control Register */
#define FT1000_REG_SUP_ISR 0x0026 /* HISR - Host Interrupt Status
* Register
*/
#define FT1000_REG_SUP_IMASK 0x0028 /* HIMASK - Host Interrupt Mask */
#define FT1000_REG_DOORBELL 0x002a /* DBELL - Door Bell Register */
#define FT1000_REG_ASIC_ID 0x002e /* ASICID - ASIC Identification
* Number
*/
/* MEMORY MAP FOR ELECTRABUZZ ASIC */
#define FT1000_REG_UFIFO_STAT 0x0000 /* UFSR - Uplink FIFO status register */
#define FT1000_REG_UFIFO_BEG 0x0002 /* UFBR - Uplink FIFO beginning
* register
*/
#define FT1000_REG_UFIFO_MID 0x0004 /* UFMR - Uplink FIFO middle register */
#define FT1000_REG_UFIFO_END 0x0006 /* UFER - Uplink FIFO end register */
#define FT1000_REG_DFIFO_STAT 0x0008 /* DFSR - Downlink FIFO status
* register
*/
#define FT1000_REG_DFIFO 0x000A /* DFR - Downlink FIFO Register */
#define FT1000_REG_DPRAM_DATA 0x000C /* DPRAM - Dual Port Indirect
* Data Register
*/
#define FT1000_REG_WATERMARK 0x0010 /* WMARK - Watermark Register */
/* MEMORY MAP FOR MAGNEMITE */
#define FT1000_REG_MAG_UFDR 0x0000 /* UFDR - Uplink FIFO Data
* Register (32-bits)
*/
#define FT1000_REG_MAG_UFDRL 0x0000 /* UFDRL - Uplink FIFO Data
* Register low-word (16-bits)
*/
#define FT1000_REG_MAG_UFDRH 0x0002 /* UFDRH - Uplink FIFO Data Register
* high-word (16-bits)
*/
#define FT1000_REG_MAG_UFER 0x0004 /* UFER - Uplink FIFO End Register */
#define FT1000_REG_MAG_UFSR 0x0006 /* UFSR - Uplink FIFO Status Register */
#define FT1000_REG_MAG_DFR 0x0008 /* DFR - Downlink FIFO Register
* (32-bits)
*/
#define FT1000_REG_MAG_DFRL 0x0008 /* DFRL - Downlink FIFO Register
* low-word (16-bits)
*/
#define FT1000_REG_MAG_DFRH 0x000a /* DFRH - Downlink FIFO Register
* high-word (16-bits)
*/
#define FT1000_REG_MAG_DFSR 0x000c /* DFSR - Downlink FIFO Status
* Register
*/
#define FT1000_REG_MAG_DPDATA 0x0010 /* DPDATA - Dual Port RAM Indirect
* Data Register (32-bits)
*/
#define FT1000_REG_MAG_DPDATAL 0x0010 /* DPDATAL - Dual Port RAM Indirect
* Data Register low-word (16-bits)
*/
#define FT1000_REG_MAG_DPDATAH 0x0012 /* DPDATAH - Dual Port RAM Indirect Data
* Register high-word (16-bits)
*/
#define FT1000_REG_MAG_WATERMARK 0x002c /* WMARK - Watermark Register */
#define FT1000_REG_MAG_VERSION 0x0030 /* LLC Version */
/* Reserved Dual Port RAM offsets for Electrabuzz */
#define FT1000_DPRAM_TX_BASE 0x0002 /* Host to PC Card Messaging Area */
#define FT1000_DPRAM_RX_BASE 0x0800 /* PC Card to Host Messaging Area */
#define FT1000_FIFO_LEN 0x07FC /* total length for DSP FIFO tracking */
#define FT1000_HI_HO 0x07FE /* heartbeat with HI/HO */
#define FT1000_DSP_STATUS 0x0FFE /* dsp status - non-zero is a request
* to reset dsp
*/
#define FT1000_DSP_LED 0x0FFA /* dsp led status for PAD device */
#define FT1000_DSP_CON_STATE 0x0FF8 /* DSP Connection Status Info */
#define FT1000_DPRAM_FEFE 0x0002 /* location for dsp ready indicator */
#define FT1000_DSP_TIMER0 0x1FF0 /* Timer Field from Basestation */
#define FT1000_DSP_TIMER1 0x1FF2 /* Timer Field from Basestation */
#define FT1000_DSP_TIMER2 0x1FF4 /* Timer Field from Basestation */
#define FT1000_DSP_TIMER3 0x1FF6 /* Timer Field from Basestation */
/* Reserved Dual Port RAM offsets for Magnemite */
#define FT1000_DPRAM_MAG_TX_BASE 0x0000 /* Host to PC Card
* Messaging Area
*/
#define FT1000_DPRAM_MAG_RX_BASE 0x0200 /* PC Card to Host
* Messaging Area
*/
#define FT1000_MAG_FIFO_LEN 0x1FF /* total length for DSP
* FIFO tracking
*/
#define FT1000_MAG_FIFO_LEN_INDX 0x1 /* low-word index */
#define FT1000_MAG_HI_HO 0x1FF /* heartbeat with HI/HO */
#define FT1000_MAG_HI_HO_INDX 0x0 /* high-word index */
#define FT1000_MAG_DSP_LED 0x3FE /* dsp led status for
* PAD device
*/
#define FT1000_MAG_DSP_LED_INDX 0x0 /* dsp led status for
* PAD device
*/
#define FT1000_MAG_DSP_CON_STATE 0x3FE /* DSP Connection Status Info */
#define FT1000_MAG_DSP_CON_STATE_INDX 0x1 /* DSP Connection Status Info */
#define FT1000_MAG_DPRAM_FEFE 0x000 /* location for dsp ready
* indicator
*/
#define FT1000_MAG_DPRAM_FEFE_INDX 0x0 /* location for dsp ready
* indicator
*/
#define FT1000_MAG_DSP_TIMER0 0x3FC /* Timer Field from
* Basestation
*/
#define FT1000_MAG_DSP_TIMER0_INDX 0x1
#define FT1000_MAG_DSP_TIMER1 0x3FC /* Timer Field from
* Basestation
*/
#define FT1000_MAG_DSP_TIMER1_INDX 0x0
#define FT1000_MAG_DSP_TIMER2 0x3FD /* Timer Field from
* Basestation
*/
#define FT1000_MAG_DSP_TIMER2_INDX 0x1
#define FT1000_MAG_DSP_TIMER3 0x3FD /* Timer Field from
* Basestation
*/
#define FT1000_MAG_DSP_TIMER3_INDX 0x0
#define FT1000_MAG_TOTAL_LEN 0x200
#define FT1000_MAG_TOTAL_LEN_INDX 0x1
#define FT1000_MAG_PH_LEN 0x200
#define FT1000_MAG_PH_LEN_INDX 0x0
#define FT1000_MAG_PORT_ID 0x201
#define FT1000_MAG_PORT_ID_INDX 0x0
#define HOST_INTF_LE 0x0 /* Host interface little endian mode */
#define HOST_INTF_BE 0x1 /* Host interface big endian mode */
/* FT1000 to Host Doorbell assignments */
#define FT1000_DB_DPRAM_RX 0x0001 /* this value indicates that DSP
* has data for host in DPRAM
*/
#define FT1000_DB_DNLD_RX 0x0002 /* Downloader handshake doorbell */
#define FT1000_ASIC_RESET_REQ 0x0004 /* DSP requesting host to
* reset the ASIC
*/
#define FT1000_DSP_ASIC_RESET 0x0008 /* DSP indicating host that
* it will reset the ASIC
*/
#define FT1000_DB_COND_RESET 0x0010 /* DSP request for a card reset. */
/* Host to FT1000 Doorbell assignments */
#define FT1000_DB_DPRAM_TX 0x0100 /* this value indicates that host
* has data for DSP in DPRAM.
*/
#define FT1000_DB_DNLD_TX 0x0200 /* Downloader handshake doorbell */
#define FT1000_ASIC_RESET_DSP 0x0400 /* Responds to FT1000_ASIC_RESET_REQ */
#define FT1000_DB_HB 0x1000 /* Indicates that supervisor has a
* heartbeat message for DSP.
*/
#define hi 0x6869 /* PC Card heartbeat values */
#define ho 0x686f /* PC Card heartbeat values */
/* Magnemite specific defines */
#define hi_mag 0x6968 /* Byte swap hi to avoid
* additional system call
*/
#define ho_mag 0x6f68 /* Byte swap ho to avoid
* additional system call
*/
/* Bit field definitions for Host Interrupt Status Register */
/* Indicate the cause of an interrupt. */
#define ISR_EMPTY 0x00 /* no bits set */
#define ISR_DOORBELL_ACK 0x01 /* Doorbell acknowledge from DSP */
#define ISR_DOORBELL_PEND 0x02 /* Doorbell pending from DSP */
#define ISR_RCV 0x04 /* Packet available in Downlink FIFO */
#define ISR_WATERMARK 0x08 /* Watermark requirements satisfied */
/* Bit field definition for Host Interrupt Mask */
#define ISR_MASK_NONE 0x0000 /* no bits set */
#define ISR_MASK_DOORBELL_ACK 0x0001 /* Doorbell acknowledge mask */
#define ISR_MASK_DOORBELL_PEND 0x0002 /* Doorbell pending mask */
#define ISR_MASK_RCV 0x0004 /* Downlink Packet available mask */
#define ISR_MASK_WATERMARK 0x0008 /* Watermark interrupt mask */
#define ISR_MASK_ALL 0xffff /* Mask all interrupts */
/* Default interrupt mask
* (Enable Doorbell pending and Packet available interrupts)
*/
#define ISR_DEFAULT_MASK 0x7ff9
/* Bit field definition for Host Control Register */
#define DSP_RESET_BIT 0x0001 /* Bit field to control
* dsp reset state
*/
/* (0 = out of reset 1 = reset) */
#define ASIC_RESET_BIT 0x0002 /* Bit field to control
* ASIC reset state
*/
/* (0 = out of reset 1 = reset) */
#define DSP_UNENCRYPTED 0x0004
#define DSP_ENCRYPTED 0x0008
#define EFUSE_MEM_DISABLE 0x0040
/* Application specific IDs */
#define DSPID 0x20
#define HOSTID 0x10
#define DSPAIRID 0x90
#define DRIVERID 0x00
#define NETWORKID 0x20
/* Size of DPRAM Message */
#define MAX_CMD_SQSIZE 1780
#define ENET_MAX_SIZE 1514
#define ENET_HEADER_SIZE 14
#define SLOWQ_TYPE 0
#define FASTQ_TYPE 1
#define MAX_DSP_SESS_REC 1024
#define DSP_QID_OFFSET 4
/* Driver message types */
#define MEDIA_STATE 0x0010
#define TIME_UPDATE 0x0020
#define DSP_PROVISION 0x0030
#define DSP_INIT_MSG 0x0050
#define DSP_HIBERNATE 0x0060
#define DSP_STORE_INFO 0x0070
#define DSP_GET_INFO 0x0071
#define GET_DRV_ERR_RPT_MSG 0x0073
#define RSP_DRV_ERR_RPT_MSG 0x0074
/* Driver Error Messages for DSP */
#define DSP_HB_INFO 0x7ef0
#define DSP_FIFO_INFO 0x7ef1
#define DSP_CONDRESET_INFO 0x7ef2
#define DSP_CMDLEN_INFO 0x7ef3
#define DSP_CMDPHCKSUM_INFO 0x7ef4
#define DSP_PKTPHCKSUM_INFO 0x7ef5
#define DSP_PKTLEN_INFO 0x7ef6
#define DSP_USER_RESET 0x7ef7
#define FIFO_FLUSH_MAXLIMIT 0x7ef8
#define FIFO_FLUSH_BADCNT 0x7ef9
#define FIFO_ZERO_LEN 0x7efa
/* Pseudo Header structure */
struct pseudo_hdr {
unsigned short length; /* length of msg body */
unsigned char source; /* hardware source id */
/* Host = 0x10 */
/* Dsp = 0x20 */
unsigned char destination; /* hardware destination id
* (refer to source)
*/
unsigned char portdest; /* software destination port id */
/* Host = 0x00 */
/* Applicaton Broadcast = 0x10 */
/* Network Stack = 0x20 */
/* Dsp OAM = 0x80 */
/* Dsp Airlink = 0x90 */
/* Dsp Loader = 0xa0 */
/* Dsp MIP = 0xb0 */
unsigned char portsrc; /* software source port id
* (refer to portdest)
*/
unsigned short sh_str_id; /* not used */
unsigned char control; /* not used */
unsigned char rsvd1;
unsigned char seq_num; /* message sequence number */
unsigned char rsvd2;
unsigned short qos_class; /* not used */
unsigned short checksum; /* pseudo header checksum */
} __packed;
struct drv_msg {
struct pseudo_hdr pseudo;
u16 type;
u16 length;
u8 data[0];
} __packed;
struct media_msg {
struct pseudo_hdr pseudo;
u16 type;
u16 length;
u16 state;
u32 ip_addr;
u32 net_mask;
u32 gateway;
u32 dns_1;
u32 dns_2;
} __packed;
struct dsp_init_msg {
struct pseudo_hdr pseudo;
u16 type;
u16 length;
u8 DspVer[DSPVERSZ]; /* DSP version number */
u8 HwSerNum[HWSERNUMSZ]; /* Hardware Serial Number */
u8 Sku[SKUSZ]; /* SKU */
u8 eui64[EUISZ]; /* EUI64 */
u8 ProductMode[MODESZ]; /* Product Mode (Market/Production) */
u8 RfCalVer[CALVERSZ]; /* Rf Calibration version */
u8 RfCalDate[CALDATESZ]; /* Rf Calibration date */
} __packed;
struct prov_record {
struct list_head list;
u8 *pprov_data;
};
struct ft1000_info {
void *priv;
struct net_device_stats stats;
u16 DrvErrNum;
u16 AsicID;
int CardReady;
int registered;
int mediastate;
u8 squeseqnum; /* sequence number on slow queue */
spinlock_t dpram_lock;
u16 fifo_cnt;
u8 DspVer[DSPVERSZ]; /* DSP version number */
u8 HwSerNum[HWSERNUMSZ]; /* Hardware Serial Number */
u8 Sku[SKUSZ]; /* SKU */
u8 eui64[EUISZ]; /* EUI64 */
time_t ConTm; /* Connection Time */
u8 ProductMode[MODESZ];
u8 RfCalVer[CALVERSZ];
u8 RfCalDate[CALDATESZ];
u16 DSP_TIME[4];
u16 LedStat;
u16 ConStat;
u16 ProgConStat;
struct list_head prov_list;
u16 DSPInfoBlklen;
int (*ft1000_reset)(void *);
u16 DSPInfoBlk[MAX_DSP_SESS_REC];
union {
u16 Rec[MAX_DSP_SESS_REC];
u32 MagRec[MAX_DSP_SESS_REC/2];
} DSPSess;
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment