summaryrefslogtreecommitdiff
path: root/drivers/staging/ft1000/ft1000-usb
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
commit57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch)
tree5e910f0e82173f4ef4f51111366a3f1299037a7b /drivers/staging/ft1000/ft1000-usb
Initial import
Diffstat (limited to 'drivers/staging/ft1000/ft1000-usb')
-rw-r--r--drivers/staging/ft1000/ft1000-usb/Makefile3
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_debug.c779
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_download.c1052
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_hw.c1587
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_ioctl.h123
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_usb.c254
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_usb.h150
7 files changed, 3948 insertions, 0 deletions
diff --git a/drivers/staging/ft1000/ft1000-usb/Makefile b/drivers/staging/ft1000/ft1000-usb/Makefile
new file mode 100644
index 000000000..7c4b78456
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_FT1000_USB) += ft1000.o
+
+ft1000-y := ft1000_debug.o ft1000_download.o ft1000_hw.o ft1000_usb.o
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_debug.c b/drivers/staging/ft1000/ft1000-usb/ft1000_debug.c
new file mode 100644
index 000000000..2d758fb26
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_debug.c
@@ -0,0 +1,779 @@
+/*
+ *---------------------------------------------------------------------------
+ * 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 = 0;
+
+/*
+ * 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) {
+ } else {
+ /* 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;
+}
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_download.c b/drivers/staging/ft1000/ft1000-usb/ft1000_download.c
new file mode 100644
index 000000000..5def347be
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_download.c
@@ -0,0 +1,1052 @@
+/*
+ * 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. */
+};
+
+#pragma pack(1)
+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;
+};
+
+
+/* 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++;
+ msleep(10);
+
+ }
+
+ 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++;
+ msleep(10);
+ } 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++;
+ msleep(10);
+ }
+
+ 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++;
+ msleep(10);
+ 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;
+}
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c b/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c
new file mode 100644
index 000000000..e6fb066e0
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c
@@ -0,0 +1,1587 @@
+/* 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) {
+ pr_debug("No Network buffers available\n");
+ 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;
+}
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_ioctl.h b/drivers/staging/ft1000/ft1000-usb/ft1000_ioctl.h
new file mode 100644
index 000000000..e9472bebd
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_ioctl.h
@@ -0,0 +1,123 @@
+/*
+ *---------------------------------------------------------------------------
+ * 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_ */
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_usb.c b/drivers/staging/ft1000/ft1000-usb/ft1000_usb.c
new file mode 100644
index 000000000..c4874e85d
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_usb.c
@@ -0,0 +1,254 @@
+/*=====================================================
+ * CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
+ *
+ *
+ * This file is part of Express Card USB Driver
+ *
+ * $Id:
+ *====================================================
+ */
+
+#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 ((endpoint->bEndpointAddress & USB_DIR_IN)
+ && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ ft1000dev->bulk_in_endpointAddr =
+ endpoint->bEndpointAddress;
+ pr_debug("in: %d\n", endpoint->bEndpointAddress);
+ }
+
+ if (!(endpoint->bEndpointAddress & USB_DIR_IN)
+ && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ 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 = reject_firmware(&dsp_fw, "/*(DEBLOBBED)*/", &dev->dev);
+ if (ret < 0) {
+ dev_err(interface->usb_dev, "Error reject_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);
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_usb.h b/drivers/staging/ft1000/ft1000-usb/ft1000_usb.h
new file mode 100644
index 000000000..fea60d565
--- /dev/null
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_usb.h
@@ -0,0 +1,150 @@
+#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);
+extern 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_ */