From 8340edb2d8a24ee88bad3e230b77cbfedf81cff8 Mon Sep 17 00:00:00 2001 From: bnewbold Date: Tue, 21 Aug 2012 21:36:42 -0400 Subject: commit all files and directions See README and LICENSE. --- libertas_uap/Makefile | 6 + libertas_uap/uap_debug.c | 261 +++++++ libertas_uap/uap_drv.h | 667 ++++++++++++++++ libertas_uap/uap_fw.h | 359 +++++++++ libertas_uap/uap_headers.h | 64 ++ libertas_uap/uap_main.c | 1815 +++++++++++++++++++++++++++++++++++++++++++ libertas_uap/uap_proc.c | 296 +++++++ libertas_uap/uap_sdio_mmc.c | 1428 ++++++++++++++++++++++++++++++++++ libertas_uap/uap_sdio_mmc.h | 136 ++++ 9 files changed, 5032 insertions(+) create mode 100644 libertas_uap/Makefile create mode 100644 libertas_uap/uap_debug.c create mode 100644 libertas_uap/uap_drv.h create mode 100644 libertas_uap/uap_fw.h create mode 100644 libertas_uap/uap_headers.h create mode 100644 libertas_uap/uap_main.c create mode 100644 libertas_uap/uap_proc.c create mode 100644 libertas_uap/uap_sdio_mmc.c create mode 100644 libertas_uap/uap_sdio_mmc.h (limited to 'libertas_uap') diff --git a/libertas_uap/Makefile b/libertas_uap/Makefile new file mode 100644 index 0000000..821f2a4 --- /dev/null +++ b/libertas_uap/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_LIBERTAS_UAP) += uap8xxx.o + +uap8xxx-y += uap_main.o uap_sdio_mmc.o +uap8xxx-$(CONFIG_PROC_FS) += uap_proc.o uap_debug.o + +EXTRA_CFLAGS += -DFPNUM='"52"' -DPXA3XX_DMA_ALIGN -DDEBUG_LEVEL1 diff --git a/libertas_uap/uap_debug.c b/libertas_uap/uap_debug.c new file mode 100644 index 0000000..e21c4aa --- /dev/null +++ b/libertas_uap/uap_debug.c @@ -0,0 +1,261 @@ +/** @file uap_debug.c + * @brief This file contains functions for debug proc file. + * + * Copyright (C) 2008-2009, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +#ifdef CONFIG_PROC_FS +#include "uap_headers.h" + +/******************************************************** + Local Variables +********************************************************/ + +#define item_size(n) (sizeof ((uap_adapter *)0)->n) +#define item_addr(n) ((u32) &((uap_adapter *)0)->n) + +#define item_dbg_size(n) (sizeof (((uap_adapter *)0)->dbg.n)) +#define item_dbg_addr(n) ((u32) &(((uap_adapter *)0)->dbg.n)) + +#define item_dev_size(n) (sizeof ((uap_dev_t *)0)->n) +#define item_dev_addr(n) ((u32) &((uap_dev_t *)0)->n) + +/** MicroAp device offset */ +#define OFFSET_UAP_DEV 0x01 +/** Bluetooth adapter offset */ +#define OFFSET_UAP_ADAPTER 0x02 + +struct debug_data +{ + /** Name */ + char name[32]; + /** Size */ + u32 size; + /** Address */ + u32 addr; + /** Offset */ + u32 offset; + /** Flag */ + u32 flag; +}; + +/* To debug any member of uap_adapter, simply add one line here. + */ +static struct debug_data items[] = { + {"cmd_sent", item_dev_size(cmd_sent), 0, item_dev_addr(cmd_sent), + OFFSET_UAP_DEV}, + {"data_sent", item_dev_size(data_sent), 0, item_dev_addr(data_sent), + OFFSET_UAP_DEV}, + {"IntCounter", item_size(IntCounter), 0, item_addr(IntCounter), + OFFSET_UAP_ADAPTER}, + {"cmd_pending", item_size(cmd_pending), 0, item_addr(cmd_pending), + OFFSET_UAP_ADAPTER}, + {"num_cmd_h2c_fail", item_dbg_size(num_cmd_host_to_card_failure), 0, + item_dbg_addr(num_cmd_host_to_card_failure), OFFSET_UAP_ADAPTER}, + {"num_tx_h2c_fail", item_dbg_size(num_tx_host_to_card_failure), 0, + item_dbg_addr(num_tx_host_to_card_failure), OFFSET_UAP_ADAPTER}, + {"psmode", item_size(psmode), 0, item_addr(psmode), OFFSET_UAP_ADAPTER}, + {"ps_state", item_size(ps_state), 0, item_addr(ps_state), + OFFSET_UAP_ADAPTER}, +#ifdef DEBUG_LEVEL1 + {"drvdbg", sizeof(drvdbg), (u32) & drvdbg, 0, 0} +#endif +}; + +static int num_of_items = sizeof(items) / sizeof(items[0]); + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief proc read function + * + * @param page pointer to buffer + * @param s read data starting position + * @param off offset + * @param cnt counter + * @param eof end of file flag + * @param data data to output + * @return number of output data + */ +static int +uap_debug_read(char *page, char **s, off_t off, int cnt, int *eof, void *data) +{ + int val = 0; + char *p = page; + int i; + + struct debug_data *d = (struct debug_data *) data; + + if (MODULE_GET == 0) + return UAP_STATUS_FAILURE; + + for (i = 0; i < num_of_items; i++) { + if (d[i].size == 1) + val = *((u8 *) d[i].addr); + else if (d[i].size == 2) + val = *((u16 *) d[i].addr); + else if (d[i].size == 4) + val = *((u32 *) d[i].addr); + + p += sprintf(p, "%s=%d\n", d[i].name, val); + } + MODULE_PUT; + return p - page; +} + +/** + * @brief proc write function + * + * @param f file pointer + * @param buf pointer to data buffer + * @param cnt data number to write + * @param data data to write + * @return number of data + */ +static int +uap_debug_write(struct file *f, const char *buf, unsigned long cnt, void *data) +{ + int r, i; + char *pdata; + char *p; + char *p0; + char *p1; + char *p2; + struct debug_data *d = (struct debug_data *) data; + + if (MODULE_GET == 0) + return UAP_STATUS_FAILURE; + + pdata = (char *) kmalloc(cnt, GFP_KERNEL); + if (pdata == NULL) { + MODULE_PUT; + return 0; + } + + if (copy_from_user(pdata, buf, cnt)) { + PRINTM(INFO, "Copy from user failed\n"); + kfree(pdata); + MODULE_PUT; + return 0; + } + + p0 = pdata; + for (i = 0; i < num_of_items; i++) { + do { + p = strstr(p0, d[i].name); + if (p == NULL) + break; + p1 = strchr(p, '\n'); + if (p1 == NULL) + break; + p0 = p1++; + p2 = strchr(p, '='); + if (!p2) + break; + p2++; + r = string_to_number(p2); + if (d[i].size == 1) + *((u8 *) d[i].addr) = (u8) r; + else if (d[i].size == 2) + *((u16 *) d[i].addr) = (u16) r; + else if (d[i].size == 4) + *((u32 *) d[i].addr) = (u32) r; + break; + } while (TRUE); + } + kfree(pdata); +#ifdef DEBUG_LEVEL1 + printk(KERN_ALERT "drvdbg = 0x%x\n", drvdbg); + printk(KERN_ALERT "INFO (%08lx) %s\n", DBG_INFO, + (drvdbg & DBG_INFO) ? "X" : ""); + printk(KERN_ALERT "WARN (%08lx) %s\n", DBG_WARN, + (drvdbg & DBG_WARN) ? "X" : ""); + printk(KERN_ALERT "ENTRY (%08lx) %s\n", DBG_ENTRY, + (drvdbg & DBG_ENTRY) ? "X" : ""); + printk(KERN_ALERT "CMD_D (%08lx) %s\n", DBG_CMD_D, + (drvdbg & DBG_CMD_D) ? "X" : ""); + printk(KERN_ALERT "DAT_D (%08lx) %s\n", DBG_DAT_D, + (drvdbg & DBG_DAT_D) ? "X" : ""); + printk(KERN_ALERT "CMND (%08lx) %s\n", DBG_CMND, + (drvdbg & DBG_CMND) ? "X" : ""); + printk(KERN_ALERT "DATA (%08lx) %s\n", DBG_DATA, + (drvdbg & DBG_DATA) ? "X" : ""); + printk(KERN_ALERT "ERROR (%08lx) %s\n", DBG_ERROR, + (drvdbg & DBG_ERROR) ? "X" : ""); + printk(KERN_ALERT "FATAL (%08lx) %s\n", DBG_FATAL, + (drvdbg & DBG_FATAL) ? "X" : ""); + printk(KERN_ALERT "MSG (%08lx) %s\n", DBG_MSG, + (drvdbg & DBG_MSG) ? "X" : ""); +#endif + MODULE_PUT; + return cnt; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief create debug proc file + * + * @param priv pointer uap_private + * @param dev pointer net_device + * @return N/A + */ +void +uap_debug_entry(uap_private * priv, struct net_device *dev) +{ + int i; + struct proc_dir_entry *r; + + if (priv->proc_entry == NULL) + return; + + for (i = 0; i < num_of_items; i++) { + if (items[i].flag & OFFSET_UAP_ADAPTER) + items[i].addr = items[i].offset + (u32) priv->adapter; + if (items[i].flag & OFFSET_UAP_DEV) + items[i].addr = items[i].offset + (u32) & priv->uap_dev; + } + r = create_proc_entry("debug", 0644, priv->proc_entry); + if (r == NULL) + return; + + r->data = &items[0]; + r->read_proc = uap_debug_read; + r->write_proc = uap_debug_write; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) + r->owner = THIS_MODULE; +#endif +} + +/** + * @brief remove proc file + * + * @param priv pointer uap_private + * @return N/A + */ +void +uap_debug_remove(uap_private * priv) +{ + remove_proc_entry("debug", priv->proc_entry); +} + +#endif diff --git a/libertas_uap/uap_drv.h b/libertas_uap/uap_drv.h new file mode 100644 index 0000000..5aa009f --- /dev/null +++ b/libertas_uap/uap_drv.h @@ -0,0 +1,667 @@ +/** @file uap_drv.h + * @brief This file contains Linux OS related definitions and + * declarations, uAP driver + * + * Copyright (C) 2008-2009, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _UAP_DRV_H +#define _UAP_DRV_H + +/** Driver release version */ +#define DRIVER_VERSION "26146" + +/** True */ +#ifndef TRUE +#define TRUE 1 +#endif +/** False */ +#ifndef FALSE +#define FALSE 0 +#endif + +/** Bit definitions */ +#ifndef BIT +#define BIT(x) (1UL << (x)) +#endif + +/** Dma addresses are 32-bits wide. */ +#ifndef __ATTRIB_ALIGN__ +#define __ATTRIB_ALIGN__ __attribute__((aligned(4))) +#endif + +/** attribute pack */ +#ifndef __ATTRIB_PACK__ +#define __ATTRIB_PACK__ __attribute__ ((packed)) +#endif + +/** Debug Macro definition*/ +#ifdef DEBUG_LEVEL1 + +extern u32 drvdbg; + +/** Debug message control bit definition for drvdbg */ +/** Debug message */ +#define DBG_MSG BIT(0) +/** Debug fatal message */ +#define DBG_FATAL BIT(1) +/** Debug error message */ +#define DBG_ERROR BIT(2) +/** Debug data message */ +#define DBG_DATA BIT(3) +/** Debug command message */ +#define DBG_CMND BIT(4) + +/** Debug data */ +#define DBG_DAT_D BIT(16) +/** Debug command */ +#define DBG_CMD_D BIT(17) + +/** Debug entry */ +#define DBG_ENTRY BIT(28) +/** Debug warning */ +#define DBG_WARN BIT(29) +/** Debug info */ +#define DBG_INFO BIT(30) + +/** Print info */ +#define PRINTM_INFO(msg...) {if (drvdbg & DBG_INFO) printk(KERN_DEBUG msg);} +/** Print warn message */ +#define PRINTM_WARN(msg...) {if (drvdbg & DBG_WARN) printk(KERN_DEBUG msg);} +/** Print entry */ +#define PRINTM_ENTRY(msg...) {if (drvdbg & DBG_ENTRY) printk(KERN_DEBUG msg);} +/** Print cmd_d */ +#define PRINTM_CMD_D(msg...) {if (drvdbg & DBG_CMD_D) printk(KERN_DEBUG msg);} +/** Print data_d */ +#define PRINTM_DAT_D(msg...) {if (drvdbg & DBG_DAT_D) printk(KERN_DEBUG msg);} +/** Print command */ +#define PRINTM_CMND(msg...) {if (drvdbg & DBG_CMND) printk(KERN_DEBUG msg);} +/** Print data */ +#define PRINTM_DATA(msg...) {if (drvdbg & DBG_DATA) printk(KERN_DEBUG msg);} +/** Print error message */ +#define PRINTM_ERROR(msg...) {if (drvdbg & DBG_ERROR) printk(KERN_DEBUG msg);} +/** Print fatal message */ +#define PRINTM_FATAL(msg...) {if (drvdbg & DBG_FATAL) printk(KERN_DEBUG msg);} +/** Print message */ +#define PRINTM_MSG(msg...) {if (drvdbg & DBG_MSG) printk(KERN_ALERT msg);} +/** Print level */ +#define PRINTM(level,msg...) PRINTM_##level(msg) + +#else + +#define PRINTM(level,msg...) do {} while (0) + +#endif /* DEBUG_LEVEL1 */ + +/** Wait until a condition becomes true */ +#define ASSERT(cond) \ +do { \ + if (!(cond)) \ + PRINTM(INFO, "ASSERT: %s, %s:%i\n", \ + __FUNCTION__, __FILE__, __LINE__); \ +} while(0) + +/** Log enrty point for debugging */ +#define ENTER() PRINTM(ENTRY, "Enter: %s, %s:%i\n", __FUNCTION__, \ + __FILE__, __LINE__) +/** Log exit point for debugging */ +#define LEAVE() PRINTM(ENTRY, "Leave: %s, %s:%i\n", __FUNCTION__, \ + __FILE__, __LINE__) + +#ifdef DEBUG_LEVEL1 +/** Dump buffer length */ +#define DBG_DUMP_BUF_LEN 64 +/** Maximum dump per line */ +#define MAX_DUMP_PER_LINE 16 +/** Data dump length */ +#define DATA_DUMP_LEN 32 + +static inline void +hexdump(char *prompt, u8 * buf, int len) +{ + int i; + char dbgdumpbuf[DBG_DUMP_BUF_LEN]; + char *ptr = dbgdumpbuf; + + printk(KERN_DEBUG "%s:\n", prompt); + for (i = 1; i <= len; i++) { + ptr += sprintf(ptr, "%02x ", *buf); + buf++; + if (i % MAX_DUMP_PER_LINE == 0) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + ptr = dbgdumpbuf; + } + } + if (len % MAX_DUMP_PER_LINE) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + } +} + +/** Debug command */ +#define DBG_HEXDUMP_CMD_D(x,y,z) {if (drvdbg & DBG_CMD_D) hexdump(x,y,z);} +/** Debug data */ +#define DBG_HEXDUMP_DAT_D(x,y,z) {if (drvdbg & DBG_DAT_D) hexdump(x,y,z);} +/** Debug hexdump */ +#define DBG_HEXDUMP(level,x,y,z) DBG_HEXDUMP_##level(x,y,z) +/** hexdump */ +#define HEXDUMP(x,y,z) {if (drvdbg & DBG_INFO) hexdump(x,y,z);} +#else +/** Do nothing since debugging is not turned on */ +#define DBG_HEXDUMP(level,x,y,z) do {} while (0) +/** Do nothing since debugging is not turned on */ +#define HEXDUMP(x,y,z) do {} while (0) +#endif + +/** + * Typedefs + */ +/** Unsigned char */ +typedef u8 BOOLEAN; + +/* + * OS macro definitions + */ +/** OS macro to get time */ +#define os_time_get() jiffies + +/** OS macro to update transfer start time */ +#define UpdateTransStart(dev) { \ + dev->trans_start = jiffies; \ +} + +/** Try to get a reference to the module */ +#define MODULE_GET try_module_get(THIS_MODULE) +/** Decrease module reference count */ +#define MODULE_PUT module_put(THIS_MODULE) + +/** OS macro to initialize semaphore */ +#define OS_INIT_SEMAPHORE(x) sema_init(x,1) +/** OS macro to acquire blocking semaphore */ +#define OS_ACQ_SEMAPHORE_BLOCK(x) down_interruptible(x) +/** OS macro to acquire non-blocking semaphore */ +#define OS_ACQ_SEMAPHORE_NOBLOCK(x) down_trylock(x) +/** OS macro to release semaphore */ +#define OS_REL_SEMAPHORE(x) up(x) + +static inline void +os_sched_timeout(u32 millisec) +{ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((millisec * HZ) / 1000); +} + +/** Maximum size of ethernet packet */ +#define MRVDRV_MAXIMUM_ETH_PACKET_SIZE 1514 + +/** Maximum size of multicast list */ +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 + +/** Find minimum */ +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/** Find maximum */ +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +/** Find number of elements */ +#ifndef NELEMENTS +#define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) +#endif + +/** Buffer Constants */ + +/** Size of command buffer */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Length of device length */ +#define DEV_NAME_LEN 32 + +/** Length of ethernet address */ +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + +/** Default watchdog timeout */ +#define MRVDRV_DEFAULT_WATCHDOG_TIMEOUT (2 * HZ) + +/** Success */ +#define UAP_STATUS_SUCCESS (0) +/** Failure */ +#define UAP_STATUS_FAILURE (-1) +/** Not accepted */ +#define UAP_STATUS_NOT_ACCEPTED (-2) + +/** Max loop count (* 100ms) for waiting device ready at init time */ +#define MAX_WAIT_DEVICE_READY_COUNT 50 + +/** Tx high watermark. Stop Tx queue after this is crossed */ +#define TX_HIGH_WATERMARK 4 +/** Tx low watermark. Restart Tx queue after this is crossed */ +#define TX_LOW_WATERMARK 2 + +/** Netlink protocol number */ +#define NETLINK_MARVELL (MAX_LINKS - 1) +/** Netlink maximum payload size */ +#define NL_MAX_PAYLOAD 1024 +/** Netlink multicast group number */ +#define NL_MULTICAST_GROUP 1 + +/** 20 seconds */ +#define MRVDRV_TIMER_20S 20000 + +/** Host Command option for wait till Send */ +#define HostCmd_OPTION_WAITFORSEND 0x0001 +/** Host Command option for wait for RSP */ +#define HostCmd_OPTION_WAITFORRSP 0x0002 +/** Host Command option for wait for RSP or Timeout */ +#define HostCmd_OPTION_WAITFORRSP_TIMEOUT 0x0003 +/** Host Command option for wait for RSP of sleep confirm */ +#define HostCmd_OPTION_WAITFORRSP_SLEEPCONFIRM 0x0004 + +/** Sleep until a condition gets true or a timeout elapses */ +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000)) + +/** Private command ID to Host command */ +#define UAPHOSTCMD (SIOCDEVPRIVATE + 1) + +/** Private command ID to Power Mode */ +#define UAP_POWER_MODE (SIOCDEVPRIVATE + 3) +/** sleep_param */ +typedef struct _ps_sleep_param +{ + /** control bitmap */ + u32 ctrl_bitmap; + /** minimum sleep period (micro second) */ + u32 min_sleep; + /** maximum sleep period (micro second) */ + u32 max_sleep; +} ps_sleep_param; + +/** inactivity sleep_param */ +typedef struct _inact_sleep_param +{ + /** inactivity timeout (micro second) */ + u32 inactivity_to; + /** miniumu awake period (micro second) */ + u32 min_awake; + /** maximum awake period (micro second) */ + u32 max_awake; +} inact_sleep_param; + +/** flag for ps mode */ +#define PS_FLAG_PS_MODE 1 +/** flag for sleep param */ +#define PS_FLAG_SLEEP_PARAM 2 +/** flag for inactivity sleep param */ +#define PS_FLAG_INACT_SLEEP_PARAM 4 + +/** Disable power mode */ +#define PS_MODE_DISABLE 0 +/** Enable periodic dtim ps */ +#define PS_MODE_PERIODIC_DTIM 1 +/** Enable inactivity ps */ +#define PS_MODE_INACTIVITY 2 + +/** sleep parameter */ +#define SLEEP_PARAMETER 1 +/** inactivity sleep parameter */ +#define INACTIVITY_SLEEP_PARAMETER 2 +/** ps_mgmt */ +typedef struct _ps_mgmt +{ + /** flags for valid field */ + u16 flags; + /** power mode */ + u16 ps_mode; + /** sleep param */ + ps_sleep_param sleep_param; + /** inactivity sleep param */ + inact_sleep_param inact_param; +} ps_mgmt; + +/** Semaphore structure */ +typedef struct semaphore SEMAPHORE; + +/** Global Varibale Declaration */ +/** Private data structure of the device */ +typedef struct _uap_private uap_private; +/** Adapter data structure of the device */ +typedef struct _uap_adapter uap_adapter; +/** private structure */ +extern uap_private *uappriv; + +/** ENUM definition*/ + +/** Hardware status codes */ +typedef enum _HARDWARE_STATUS +{ + HWReady, + HWInitializing, + HWReset, + HWClosing, + HWNotReady +} HARDWARE_STATUS; + +/** info for debug purpose */ +typedef struct _uap_dbg +{ + /** Number of host to card command failures */ + u32 num_cmd_host_to_card_failure; + /** Number of host to card Tx failures */ + u32 num_tx_host_to_card_failure; +} uap_dbg; + +/** Set thread state */ +#define OS_SET_THREAD_STATE(x) set_current_state(x) + +typedef struct +{ + /** Task */ + struct task_struct *task; + /** Queue */ + wait_queue_head_t waitQ; + /** PID */ + pid_t pid; + /** Private structure */ + void *priv; +} uap_thread; + +static inline void +uap_activate_thread(uap_thread * thr) +{ + /** Record the thread pid */ + thr->pid = current->pid; + + /** Initialize the wait queue */ + init_waitqueue_head(&thr->waitQ); +} + +static inline void +uap_deactivate_thread(uap_thread * thr) +{ + thr->pid = 0; + return; +} + +static inline void +uap_create_thread(int (*uapfunc) (void *), uap_thread * thr, char *name) +{ + thr->task = kthread_run(uapfunc, thr, "%s", name); +} + +static inline int +uap_terminate_thread(uap_thread * thr) +{ + /* Check if the thread is active or not */ + if (!thr->pid) + return -1; + kthread_stop(thr->task); + return 0; +} + +/** Data structure for the Marvell uAP device */ +typedef struct _uap_dev +{ + /** device name */ + char name[DEV_NAME_LEN]; + /** card pointer */ + void *card; + /** IO port */ + u32 ioport; + /** Rx unit */ + u8 rx_unit; + /** Data sent: + TRUE - Data is sent to fw, no Tx Done received + FALSE - Tx done received for previous Tx */ + BOOLEAN data_sent; + /** CMD sent: + TRUE - CMD is sent to fw, no CMD Done received + FALSE - CMD done received for previous CMD */ + BOOLEAN cmd_sent; + /** netdev pointer */ + struct net_device *netdev; +} uap_dev_t, *puap_dev_t; + +/** Private structure for the MV device */ +struct _uap_private +{ + /** Device open */ + int open; + + /** Device adapter structure */ + uap_adapter *adapter; + /** Device structure */ + uap_dev_t uap_dev; + + /** Net device statistics structure */ + struct net_device_stats stats; + + /** Number of Tx timeouts */ + u32 num_tx_timeout; + + /** Media connection status */ + BOOLEAN MediaConnected; + +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc_uap; + struct proc_dir_entry *proc_entry; +#endif /* CONFIG_PROC_FS */ + + /** Firmware helper */ + const struct firmware *fw_helper; + /** Firmware */ + const struct firmware *firmware; + /** Hotplug device */ + struct device *hotplug_device; + /** thread to service interrupts */ + uap_thread MainThread; + /** Driver lock */ + spinlock_t driver_lock; + /** Driver lock flags */ + ulong driver_flags; + +}; + +/** PS_CMD_ConfirmSleep */ +typedef struct _PS_CMD_ConfirmSleep +{ + /** SDIO Length */ + u16 SDLen; + /** SDIO Type */ + u16 SDType; + /** Command */ + u16 Command; + /** Size */ + u16 Size; + /** Sequence number */ + u16 SeqNum; + /** Result */ + u16 Result; +} __ATTRIB_PACK__ PS_CMD_ConfirmSleep, *PPS_CMD_ConfirmSleep; + +/** Wlan Adapter data structure*/ +struct _uap_adapter +{ + /** Power save confirm sleep command */ + PS_CMD_ConfirmSleep PSConfirmSleep; + /** Device status */ + HARDWARE_STATUS HardwareStatus; + /** Interrupt counter */ + u32 IntCounter; + /** Tx packet queue */ + struct sk_buff_head tx_queue; + /** Cmd packet queue */ + struct sk_buff_head cmd_queue; + /** Command sequence number */ + u16 SeqNum; + /** Command buffer */ + u8 *CmdBuf; + /** cmd pending flag */ + u8 cmd_pending; + /** cmd wait option */ + u8 cmd_wait_option; + /** Command buffer length */ + u32 CmdSize; + /** Command wait queue */ + wait_queue_head_t cmdwait_q __ATTRIB_ALIGN__; + /** Command wait queue state flag */ + u8 CmdWaitQWoken; + /** PnP support */ + BOOLEAN SurpriseRemoved; + /** Debug */ + uap_dbg dbg; + /** Netlink kernel socket */ + struct sock *nl_sk; + /** Semaphore for CMD */ + SEMAPHORE CmdSem; + /** Power Save mode */ + u8 psmode; + /** Power Save state */ + u8 ps_state; + /** Number of wakeup tries */ + u32 WakeupTries; +}; + +static inline int +os_upload_rx_packet(uap_private * priv, struct sk_buff *skb) +{ + skb->dev = priv->uap_dev.netdev; + skb->protocol = eth_type_trans(skb, priv->uap_dev.netdev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + return 0; +} + +/* + * netif carrier_on/off and start(wake)/stop_queue handling + */ +static inline void +os_carrier_on(uap_private * priv) +{ + if (!netif_carrier_ok(priv->uap_dev.netdev) && + (priv->MediaConnected == TRUE)) { + netif_carrier_on(priv->uap_dev.netdev); + } +} + +static inline void +os_carrier_off(uap_private * priv) +{ + if (netif_carrier_ok(priv->uap_dev.netdev)) { + netif_carrier_off(priv->uap_dev.netdev); + } +} + +static inline void +os_start_queue(uap_private * priv) +{ + if (netif_queue_stopped(priv->uap_dev.netdev) && + (priv->MediaConnected == TRUE)) { + netif_wake_queue(priv->uap_dev.netdev); + } +} + +static inline void +os_stop_queue(uap_private * priv) +{ + if (!netif_queue_stopped(priv->uap_dev.netdev)) { + netif_stop_queue(priv->uap_dev.netdev); + } +} + +/** Interface specific header */ +#define INTF_HEADER_LEN 4 + +/** headroom alignment for tx packet */ +#define HEADER_ALIGNMENT 8 + +/** The number of times to try when polling for status bits */ +#define MAX_POLL_TRIES 100 + +/** Length of SNAP header */ +#define MRVDRV_SNAP_HEADER_LEN 8 + +/** Extra length of Tx packet buffer */ +#define EXTRA_LEN 36 + +/** Buffer size for ethernet Tx packets */ +#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(TxPD) + EXTRA_LEN) + +/** Buffer size for ethernet Rx packets */ +#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(RxPD) \ + + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) + +/** Packet type: data, command & event */ +typedef enum _mv_type +{ + MV_TYPE_DAT = 0, + MV_TYPE_CMD = 1, + MV_TYPE_EVENT = 3 +} mv_type; + +/** Disable interrupt */ +#define OS_INT_DISABLE spin_lock_irqsave(&priv->driver_lock, priv->driver_flags) +/** Enable interrupt */ +#define OS_INT_RESTORE spin_unlock_irqrestore(&priv->driver_lock, priv->driver_flags) + +int uap_process_rx_packet(uap_private * priv, struct sk_buff *skb); +void uap_interrupt(uap_private * priv); +uap_private *uap_add_card(void *card); +int uap_remove_card(void *card); +int uap_process_event(uap_private * priv, u8 * payload, uint len); +int uap_soft_reset(uap_private * priv); +int uap_process_sleep_confirm_resp(uap_private * priv, u8 * resp, int resp_len); + +#ifdef CONFIG_PROC_FS +/** The proc fs interface */ +void uap_proc_entry(uap_private * priv, struct net_device *dev); +void uap_proc_remove(uap_private * priv); +int string_to_number(char *s); +void uap_debug_entry(uap_private * priv, struct net_device *dev); +void uap_debug_remove(uap_private * priv); +#endif /* CONFIG_PROC_FS */ + +int sbi_register(void); + +void sbi_unregister(void); +int sbi_register_dev(uap_private * priv); +int sbi_unregister_dev(uap_private * priv); +int sbi_prog_fw_w_helper(uap_private *); + +int sbi_host_to_card(uap_private * priv, u8 * payload, u16 nb); +int sbi_enable_host_int(uap_private * priv); +int sbi_disable_host_int(uap_private * priv); + +int sbi_get_int_status(uap_private * priv, u8 * ireg); +/** Check firmware status */ +int sbi_check_fw_status(uap_private *, int); +int sbi_prog_helper(uap_private *); + +int sbi_wakeup_firmware(uap_private * priv); + +#endif /* _UAP_DRV_H */ diff --git a/libertas_uap/uap_fw.h b/libertas_uap/uap_fw.h new file mode 100644 index 0000000..23a40d6 --- /dev/null +++ b/libertas_uap/uap_fw.h @@ -0,0 +1,359 @@ +/** @file uap_fw.h + * + * @brief This file contains firmware specific defines. + * + * Copyright (C) 2008-2009, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/******************************************************** +Change log: + 02/26/08: Initial creation +********************************************************/ + +#ifndef _UAP_FW_H +#define _UAP_FW_H + +/** uap upload size */ +#define UAP_UPLD_SIZE 2312 +/** Packet type Micro AP */ +#define PKT_TYPE_MICROAP 1 +/** Packet type client */ +#define PKT_TYPE_CLIENT 0 + +/** TxPD descriptor */ +typedef struct _TxPD +{ + /** Bss Type */ + u8 BssType; + /** Bss num */ + u8 BssNum; + /** Tx packet length */ + u16 TxPktLength; + /** Tx packet offset */ + u16 TxPktOffset; + /** Tx packet type */ + u16 TxPktType; + /** Tx Control */ + u32 TxControl; + /** reserved */ + u32 reserved[2]; +} __ATTRIB_PACK__ TxPD, *PTxPD; + +/** RxPD Descriptor */ +typedef struct _RxPD +{ + /** Bss Type */ + u8 BssType; + /** Bss Num */ + u8 BssNum; + /** Tx packet length */ + u16 RxPktLength; + /** Tx packet offset */ + u16 RxPktOffset; +} __ATTRIB_PACK__ RxPD, *PRxPD; + +#ifdef BIG_ENDIAN +/** Convert from 16 bit little endian format to CPU format */ +#define uap_le16_to_cpu(x) le16_to_cpu(x) +/** Convert from 32 bit little endian format to CPU format */ +#define uap_le32_to_cpu(x) le32_to_cpu(x) +/** Convert from 64 bit little endian format to CPU format */ +#define uap_le64_to_cpu(x) le64_to_cpu(x) +/** Convert to 16 bit little endian format from CPU format */ +#define uap_cpu_to_le16(x) cpu_to_le16(x) +/** Convert to 32 bit little endian format from CPU format */ +#define uap_cpu_to_le32(x) cpu_to_le32(x) +/** Convert to 64 bit little endian format from CPU format */ +#define uap_cpu_to_le64(x) cpu_to_le64(x) + +/** Convert TxPD to little endian format from CPU format */ +#define endian_convert_TxPD(x); \ + { \ + (x)->TxPktLength = uap_cpu_to_le16((x)->TxPktLength); \ + (x)->TxPktOffset = uap_cpu_to_le32((x)->TxPktOffset); \ + (x)->TxControl = uap_cpu_to_le32((x)->TxControl); \ + (x)->TxPktType = uap_cpu_to_le32((x)->TxPktType); \ + } + +/** Convert RxPD from little endian format to CPU format */ +#define endian_convert_RxPD(x); \ + { \ + (x)->RxPktLength = uap_le16_to_cpu((x)->RxPktLength); \ + (x)->RxPktOffset = uap_le32_to_cpu((x)->RxPktOffset); \ + } +#else /* BIG_ENDIAN */ +/** Do nothing */ +#define uap_le16_to_cpu(x) x +/** Do nothing */ +#define uap_le32_to_cpu(x) x +/** Do nothing */ +#define uap_le64_to_cpu(x) x +/** Do nothing */ +#define uap_cpu_to_le16(x) x +/** Do nothing */ +#define uap_cpu_to_le32(x) x +/** Do nothing */ +#define uap_cpu_to_le64(x) x + +/** Do nothing */ +#define endian_convert_TxPD(x) +/** Do nothing */ +#define endian_convert_RxPD(x) +#endif /* BIG_ENDIAN */ + +/** Host Command ID : Function initialization */ +#define HostCmd_CMD_FUNC_INIT 0x00a9 +/** Host Command ID : Function shutdown */ +#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa + +/** Host Command id: SYS_INFO */ +#define HOST_CMD_APCMD_SYS_INFO 0x00ae +/** Host Command id: SYS_RESET */ +#define HOST_CMD_APCMD_SYS_RESET 0x00af +/** Host Command id: SYS_CONFIGURE */ +#define HOST_CMD_APCMD_SYS_CONFIGURE 0x00b0 +/** Host Command id: BSS_START */ +#define HOST_CMD_APCMD_BSS_START 0x00b1 +/** Host Command id: SYS_STOP */ +#define HOST_CMD_APCMD_BSS_STOP 0x00b2 +/** Host Command id: STA_LIST */ +#define HOST_CMD_APCMD_STA_LIST 0x00b3 +/** Host Command id: STA_FILTER_TABLE */ +#define HOST_CMD_APCMD_STA_FILTER_TABLE 0x00b4 +/** Host Command id: STA_DEAUTH */ +#define HOST_CMD_APCMD_STA_DEAUTH 0x00b5 +/** Host Command id: SOFT_RESET */ +#define HOST_CMD_APCMD_SOFT_RESET 0x00d5 +/** Host Command id: POWER_MGMT_EXT */ +#define HOST_CMD_POWER_MGMT_EXT 0x00ef +/** Host Command id: SLEEP_CONFIRM*/ +#define HOST_CMD_SLEEP_CONFIRM 0x00d8 + +/** TLV type : SSID */ +#define TLV_TYPE_SSID 0x0000 +/** TLV type : Rates */ +#define TLV_TYPE_RATES 0x0001 +/** TLV type : PHY DS */ +#define TLV_TYPE_PHY_DS 0x0003 + +/** TLV Id : Base id */ +#define PROPRIETARY_TLV_BASE_ID 0x0100 +/** TLV Id : AP_MAC_ADDRESS */ +#define MRVL_AP_MAC_ADDRESS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 43) +/** TLV Id : Beacon period */ +#define MRVL_BEACON_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 44) +/** TLV Id : Dtim period */ +#define MRVL_DTIM_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 45) +/** TLV Id : Basic rates */ +#define MRVL_BASIC_RATES_TLV_ID (PROPRIETARY_TLV_BASE_ID + 46) +/** TLV Id : Tx Power */ +#define MRVL_TX_POWER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 47) +/** TLV Id : Broadcast SSID control */ +#define MRVL_BCAST_SSID_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 48) +/** TLV Id : Preamble control */ +#define MRVL_PREAMBLE_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 49) +/** TLV Id : Antenna control */ +#define MRVL_ANTENNA_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 50) +/** TLV Id : RTS threshold */ +#define MRVL_RTS_THRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 51) +/** TLV Id : Radio control */ +#define MRVL_RADIO_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 52) +/** TLV Id : TX data rate */ +#define MRVL_TX_DATA_RATE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 53) +/** TLV Id : Packet forward control */ +#define MRVL_PKT_FWD_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 54) +/** TLV Id : STA info */ +#define MRVL_STA_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 55) +/** TLV Id : STA MAC address filter */ +#define MRVL_STA_MAC_ADDR_FILTER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 56) +/** TLV Id : STA ageout timer */ +#define MRVL_STA_AGEOUT_TIMER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 57) +/** TLV Id : Security config */ +#define MRVL_SECURITY_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 58) +/** TLV Id : WEP KEY */ +#define MRVL_WEP_KEY_TLV_ID (PROPRIETARY_TLV_BASE_ID + 59) +/** TLV Id : WPA Passphrase */ +#define MRVL_WPA_PASSPHRASE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 60) + +/** Action get */ +#define ACTION_GET 0 +/** Action set */ +#define ACTION_SET 1 +/** Length of ethernet address */ +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif + +/** HostCmd_DS_GEN */ +typedef struct +{ + /** Command */ + u16 Command; + /** Size */ + u16 Size; + /** Sequence number */ + u16 SeqNum; + /** Result */ + u16 Result; +} __ATTRIB_PACK__ HostCmd_DS_GEN; + +/** Size of HostCmd_DS_GEN */ +#define S_DS_GEN sizeof(HostCmd_DS_GEN) + +/** _HostCmd_HEADER*/ +typedef struct +{ + /** Command Header : Command */ + u16 Command; + /** Command Header : Size */ + u16 Size; +} __ATTRIB_PACK__ HostCmd_HEADER; + +/** HostCmd_SYS_CONFIG */ +typedef struct _HostCmd_SYS_CONFIG +{ + /** CMD Action GET/SET*/ + u16 Action; + /** Tlv buffer */ + u8 TlvBuffer[0]; +} __ATTRIB_PACK__ HostCmd_SYS_CONFIG; + +/** HostCmd_DS_POWER_MGMT_EXT */ +typedef struct _HostCmd_DS_POWER_MGMT_EXT +{ + /** CMD Action Get/Set*/ + u16 action; + /** power mode */ + u16 power_mode; +} __ATTRIB_PACK__ HostCmd_DS_POWER_MGMT_EXT; + +/** _HostCmd_DS_COMMAND*/ +typedef struct _HostCmd_DS_COMMAND +{ + + /** Command Header : Command */ + u16 Command; + /** Command Header : Size */ + u16 Size; + /** Command Header : Sequence number */ + u16 SeqNum; + /** Command Header : Result */ + u16 Result; + /** Command Body */ + union + { + HostCmd_SYS_CONFIG sys_config; + HostCmd_DS_POWER_MGMT_EXT pm_cfg; + + } params; +} __ATTRIB_PACK__ HostCmd_DS_COMMAND; + +/** MrvlIEtypesHeader_*/ +typedef struct _MrvlIEtypesHeader +{ + /** Header type */ + u16 Type; + /** Header length */ + u16 Len; +} __ATTRIB_PACK__ MrvlIEtypesHeader_t; + +/** MrvlIEtypes_Data_t */ +typedef struct _MrvlIEtypes_Data_t +{ + /** Header */ + MrvlIEtypesHeader_t Header; + /** Data */ + u8 Data[1]; +} __ATTRIB_PACK__ MrvlIEtypes_Data_t; + +/** MrvlIEtypes_ChanListParamSet_t */ +typedef struct _MrvlIEtypes_MacAddr_t +{ + /** Header */ + MrvlIEtypesHeader_t Header; + /** AP MAC address */ + u8 ApMacAddr[ETH_ALEN]; +} __ATTRIB_PACK__ MrvlIEtypes_MacAddr_t; + +/** Event ID: BSS started */ +#define MICRO_AP_EV_ID_BSS_START 46 + +/** Event ID: BSS idle event */ +#define MICRO_AP_EV_BSS_IDLE 67 + +/** Event ID: BSS active event */ +#define MICRO_AP_EV_BSS_ACTIVE 68 + +/** Event ID: PS_AWAKE */ +#define EVENT_PS_AWAKE 0x0a + +/** Event ID: PS_SLEEP */ +#define EVENT_PS_SLEEP 0x0b + +/** PS_STATE */ +typedef enum _PS_STATE +{ + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP +} PS_STATE; + +/** TLV type: AP Sleep param */ +#define TLV_TYPE_AP_SLEEP_PARAM (PROPRIETARY_TLV_BASE_ID + 106) +/** TLV type: AP Inactivity Sleep param */ +#define TLV_TYPE_AP_INACT_SLEEP_PARAM (PROPRIETARY_TLV_BASE_ID + 107) + +/** MrvlIEtypes_sleep_param_t */ +typedef struct _MrvlIEtypes_sleep_param_t +{ + /** Header */ + MrvlIEtypesHeader_t header; + /** control bitmap */ + u32 ctrl_bitmap; + /** min_sleep */ + u32 min_sleep; + /** max_sleep */ + u32 max_sleep; +} __ATTRIB_PACK__ MrvlIEtypes_sleep_param_t; + +/** MrvlIEtypes_inact_sleep_param_t */ +typedef struct _MrvlIEtypes_inact_sleep_param_t +{ + /** Header */ + MrvlIEtypesHeader_t header; + /** inactivity timeout */ + u32 inactivity_to; + /** min_awake */ + u32 min_awake; + /** max_awake */ + u32 max_awake; +} __ATTRIB_PACK__ MrvlIEtypes_inact_sleep_param_t; + +/** AP_Event */ +typedef struct _AP_Event +{ + /** Event ID */ + u32 EventId; + /* + * Reserved for STA_ASSOCIATED event and contains + * status information for the MIC_COUNTERMEASURES event. + */ + /** Reserved/status */ + u16 status; + /** AP MAC address */ + u8 MacAddr[ETH_ALEN]; +} __ATTRIB_PACK__ AP_Event; +#endif /* _UAP_FW_H */ diff --git a/libertas_uap/uap_headers.h b/libertas_uap/uap_headers.h new file mode 100644 index 0000000..fa09af4 --- /dev/null +++ b/libertas_uap/uap_headers.h @@ -0,0 +1,64 @@ +/** @file uap_headers.h + * + * @brief This file contains all the necessary include file. + * + * Copyright (C) 2008-2009, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +#ifndef _UAP_HEADERS_H +#define _UAP_HEADERS_H + +/* Linux header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) +#include +#else +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) +#include +#endif + +/* Net header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uap_drv.h" +#include "uap_fw.h" + +#include +#include +#include +#include +#include "uap_sdio_mmc.h" + +#endif /* _UAP_HEADERS_H */ diff --git a/libertas_uap/uap_main.c b/libertas_uap/uap_main.c new file mode 100644 index 0000000..0cbbe1f --- /dev/null +++ b/libertas_uap/uap_main.c @@ -0,0 +1,1815 @@ +/** @file uap_main.c + * @brief This file contains the major functions in uAP + * driver. It includes init, exit etc.. + * This file also contains the initialization for SW, + * FW and HW + * + * Copyright (C) 2008-2009, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/** + * @mainpage uAP Linux Driver + * + * @section overview_sec Overview + * + * This is Linux reference driver for Marvell uAP. + * + * @section copyright_sec Copyright + * + * Copyright (C) 2008, Marvell International Ltd. + * + */ + +#include "uap_headers.h" + +/** + * the global variable of a pointer to uap_private + * structure variable + */ +uap_private *uappriv = NULL; +#ifdef DEBUG_LEVEL1 +#define DEFAULT_DEBUG_MASK (DBG_MSG | DBG_FATAL | DBG_ERROR) +u32 drvdbg = DEFAULT_DEBUG_MASK; +#endif +/** Helper name */ +char *helper_name = NULL; +/** Firmware name */ +char *fw_name = NULL; + +/** Semaphore for add/remove card */ +SEMAPHORE AddRemoveCardSem; + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function send sleep confirm command to firmware + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS for success otherwise UAP_STATUS_FAILURE + */ +static int +uap_dnld_sleep_confirm_cmd(uap_private * priv) +{ + uap_adapter *Adapter = priv->adapter; + int ret = UAP_STATUS_SUCCESS; + ENTER(); + PRINTM(CMND, "Sleep confirm\n"); + Adapter->cmd_pending = TRUE; + Adapter->cmd_wait_option = HostCmd_OPTION_WAITFORRSP_SLEEPCONFIRM; + ret = + sbi_host_to_card(priv, (u8 *) & Adapter->PSConfirmSleep, + sizeof(PS_CMD_ConfirmSleep)); + if (ret != UAP_STATUS_SUCCESS) { + Adapter->ps_state = PS_STATE_AWAKE; + Adapter->cmd_pending = FALSE; + Adapter->cmd_wait_option = FALSE; + } + LEAVE(); + return ret; +} + +/** + * @brief This function process sleep confirm resp from firmware + * + * @param priv A pointer to uap_private structure + * @param resp A pointer to resp buf + * @param resp_len resp buf len + * @return UAP_STATUS_SUCCESS for success otherwise UAP_STATUS_FAILURE + */ +int +uap_process_sleep_confirm_resp(uap_private * priv, u8 * resp, int resp_len) +{ + int ret = UAP_STATUS_SUCCESS; + HostCmd_DS_COMMAND *cmd; + uap_adapter *Adapter = priv->adapter; + ENTER(); + PRINTM(CMND, "Sleep confirm resp\n"); + if (!resp_len) { + PRINTM(ERROR, "Cmd Size is 0\n"); + ret = -EFAULT; + goto done; + } + cmd = (HostCmd_DS_COMMAND *) resp; + cmd->Result = uap_le16_to_cpu(cmd->Result); + if (cmd->Result != UAP_STATUS_SUCCESS) { + PRINTM(ERROR, "HOST_CMD_APCMD_PS_SLEEP_CONFIRM fail=%x\n", cmd->Result); + ret = -EFAULT; + } + done: + if (ret == UAP_STATUS_SUCCESS) + Adapter->ps_state = PS_STATE_SLEEP; + else + Adapter->ps_state = PS_STATE_AWAKE; + LEAVE(); + return ret; +} + +/** + * @brief This function checks condition and prepares to + * send sleep confirm command to firmware if OK. + * + * @param priv A pointer to uap_private structure + * @return n/a + */ +static void +uap_ps_cond_check(uap_private * priv) +{ + uap_adapter *Adapter = priv->adapter; + + ENTER(); + if (!priv->uap_dev.cmd_sent && + !Adapter->cmd_pending && !Adapter->IntCounter) { + uap_dnld_sleep_confirm_cmd(priv); + } else { + PRINTM(INFO, "Delay Sleep Confirm (%s%s%s)\n", + (priv->uap_dev.cmd_sent) ? "D" : "", + (Adapter->cmd_pending) ? "C" : "", + (Adapter->IntCounter) ? "I" : ""); + } + LEAVE(); +} + +/** + * @brief This function add cmd to cmdQ and waiting for response + * + * @param priv A pointer to uap_private structure + * @param skb A pointer to the skb for process + * @param wait_option Wait option + * @return UAP_STATUS_SUCCESS for success otherwise UAP_STATUS_FAILURE + */ +static int +uap_process_cmd(uap_private * priv, struct sk_buff *skb, u8 wait_option) +{ + uap_adapter *Adapter = priv->adapter; + int ret = UAP_STATUS_SUCCESS; + HostCmd_DS_COMMAND *cmd; + u8 *headptr; + ENTER(); + if (Adapter->HardwareStatus != HWReady) { + PRINTM(ERROR, "Hw not ready, uap_process_cmd\n"); + kfree(skb); + LEAVE(); + return -EFAULT; + } + skb->cb[0] = wait_option; + headptr = skb->data; + *(u16 *) & headptr[0] = uap_cpu_to_le16(skb->len); + *(u16 *) & headptr[2] = uap_cpu_to_le16(MV_TYPE_CMD); + cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); + Adapter->SeqNum++; + cmd->SeqNum = uap_cpu_to_le16(Adapter->SeqNum); + PRINTM(CMND, "process_cmd: %x\n", cmd->Command); + DBG_HEXDUMP(CMD_D, "process_cmd", (u8 *) cmd, cmd->Size); + if (!wait_option) { + skb_queue_tail(&priv->adapter->cmd_queue, skb); + wake_up_interruptible(&priv->MainThread.waitQ); + LEAVE(); + return ret; + } + if (OS_ACQ_SEMAPHORE_BLOCK(&Adapter->CmdSem)) { + PRINTM(ERROR, "Acquire semaphore error, uap_prepare_cmd\n"); + kfree(skb); + LEAVE(); + return -EBUSY; + } + skb_queue_tail(&priv->adapter->cmd_queue, skb); + Adapter->CmdWaitQWoken = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + /* Sleep until response is generated by FW */ + if (wait_option == HostCmd_OPTION_WAITFORRSP_TIMEOUT) { + if (!os_wait_interruptible_timeout + (Adapter->cmdwait_q, Adapter->CmdWaitQWoken, MRVDRV_TIMER_20S)) { + PRINTM(ERROR, "Cmd timeout\n"); + Adapter->cmd_pending = FALSE; + ret = -EFAULT; + } + } else + wait_event_interruptible(Adapter->cmdwait_q, Adapter->CmdWaitQWoken); + OS_REL_SEMAPHORE(&Adapter->CmdSem); + LEAVE(); + return ret; +} + +/** + * @brief Inspect the response buffer for pointers to expected TLVs + * + * + * @param pTlv Pointer to the start of the TLV buffer to parse + * @param tlvBufSize Size of the TLV buffer + * @param reqTlvType request tlv's tlvtype + * @param ppTlv Output parameter: Pointer to the request TLV if found + * + * @return void + */ +static void +uap_get_tlv_ptrs(MrvlIEtypes_Data_t * pTlv, int tlvBufSize, + u16 reqTlvType, MrvlIEtypes_Data_t ** ppTlv) +{ + MrvlIEtypes_Data_t *pCurrentTlv; + int tlvBufLeft; + u16 tlvType; + u16 tlvLen; + + ENTER(); + pCurrentTlv = pTlv; + tlvBufLeft = tlvBufSize; + *ppTlv = NULL; + PRINTM(INFO, "uap_get_tlv: tlvBufSize = %d, reqTlvType=%x\n", tlvBufSize, + reqTlvType); + while (tlvBufLeft >= sizeof(MrvlIEtypesHeader_t)) { + tlvType = uap_le16_to_cpu(pCurrentTlv->Header.Type); + tlvLen = uap_le16_to_cpu(pCurrentTlv->Header.Len); + if (reqTlvType == tlvType) + *ppTlv = (MrvlIEtypes_Data_t *) pCurrentTlv; + if (*ppTlv) { + HEXDUMP("TLV Buf", (u8 *) * ppTlv, tlvLen); + break; + } + tlvBufLeft -= (sizeof(pTlv->Header) + tlvLen); + pCurrentTlv = (MrvlIEtypes_Data_t *) (pCurrentTlv->Data + tlvLen); + } /* while */ + LEAVE(); +} + +/** + * @brief This function get mac + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS on success, otherwise failure code + */ +static int +uap_get_mac_address(uap_private * priv) +{ + int ret = UAP_STATUS_SUCCESS; + u32 CmdSize; + HostCmd_DS_COMMAND *cmd; + uap_adapter *Adapter = priv->adapter; + struct sk_buff *skb; + MrvlIEtypes_MacAddr_t *pMacAddrTlv; + MrvlIEtypes_Data_t *pTlv; + u16 tlvBufSize; + ENTER(); + skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!skb) { + PRINTM(ERROR, "No free skb\n"); + ret = -ENOMEM; + goto done; + } + CmdSize = + S_DS_GEN + sizeof(HostCmd_SYS_CONFIG) + sizeof(MrvlIEtypes_MacAddr_t); + cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); + cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_SYS_CONFIGURE); + cmd->Size = uap_cpu_to_le16(CmdSize); + cmd->params.sys_config.Action = uap_cpu_to_le16(ACTION_GET); + pMacAddrTlv = + (MrvlIEtypes_MacAddr_t *) (skb->data + INTF_HEADER_LEN + S_DS_GEN + + sizeof(HostCmd_SYS_CONFIG)); + pMacAddrTlv->Header.Type = uap_cpu_to_le16(MRVL_AP_MAC_ADDRESS_TLV_ID); + pMacAddrTlv->Header.Len = uap_cpu_to_le16(ETH_ALEN); + skb_put(skb, CmdSize + INTF_HEADER_LEN); + if (UAP_STATUS_SUCCESS != + uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { + PRINTM(ERROR, "Fail to process cmd SYS_CONFIGURE Query\n"); + ret = -EFAULT; + goto done; + } + if (!Adapter->CmdSize) { + PRINTM(ERROR, "Cmd Size is 0\n"); + ret = -EFAULT; + goto done; + } + cmd = (HostCmd_DS_COMMAND *) Adapter->CmdBuf; + cmd->Result = uap_le16_to_cpu(cmd->Result); + if (cmd->Result != UAP_STATUS_SUCCESS) { + PRINTM(ERROR, "uap_get_mac_address fail=%x\n", cmd->Result); + ret = -EFAULT; + goto done; + } + pTlv = + (MrvlIEtypes_Data_t *) (Adapter->CmdBuf + S_DS_GEN + + sizeof(HostCmd_SYS_CONFIG)); + tlvBufSize = Adapter->CmdSize - S_DS_GEN - sizeof(HostCmd_SYS_CONFIG); + uap_get_tlv_ptrs(pTlv, tlvBufSize, MRVL_AP_MAC_ADDRESS_TLV_ID, + (MrvlIEtypes_Data_t **) & pMacAddrTlv); + if (pMacAddrTlv) { + memcpy(priv->uap_dev.netdev->dev_addr, pMacAddrTlv->ApMacAddr, + ETH_ALEN); + HEXDUMP("Original MAC addr", priv->uap_dev.netdev->dev_addr, ETH_ALEN); + } + done: + LEAVE(); + return ret; +} + +/** + * @brief This function checks the conditions and sends packet to device + * + * @param priv A pointer to uap_private structure + * @param skb A pointer to the skb for process + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +uap_process_tx(uap_private * priv, struct sk_buff *skb) +{ + uap_adapter *Adapter = priv->adapter; + int ret = UAP_STATUS_SUCCESS; + TxPD *pLocalTxPD; + u8 *headptr; + struct sk_buff *newskb; + int newheadlen; + ENTER(); + ASSERT(skb); + if (!skb) { + LEAVE(); + return UAP_STATUS_FAILURE; + } + if (skb_headroom(skb) < (sizeof(TxPD) + INTF_HEADER_LEN + HEADER_ALIGNMENT)) { + newheadlen = sizeof(TxPD) + INTF_HEADER_LEN + HEADER_ALIGNMENT; + PRINTM(WARN, "Tx: Insufficient skb headroom %d\n", skb_headroom(skb)); + /* Insufficient skb headroom - allocate a new skb */ + newskb = skb_realloc_headroom(skb, newheadlen); + if (unlikely(newskb == NULL)) { + PRINTM(ERROR, "Tx: Cannot allocate skb\n"); + ret = UAP_STATUS_FAILURE; + goto done; + } + kfree_skb(skb); + skb = newskb; + PRINTM(INFO, "new skb headroom %d\n", skb_headroom(skb)); + } + /* headptr should be aligned */ + headptr = skb->data - sizeof(TxPD) - INTF_HEADER_LEN; + headptr = (u8 *) ((u32) headptr & ~((u32) (HEADER_ALIGNMENT - 1))); + + pLocalTxPD = (TxPD *) (headptr + INTF_HEADER_LEN); + memset(pLocalTxPD, 0, sizeof(TxPD)); + pLocalTxPD->BssType = PKT_TYPE_MICROAP; + pLocalTxPD->TxPktLength = skb->len; + /* offset of actual data */ + pLocalTxPD->TxPktOffset = (long) skb->data - (long) pLocalTxPD; + endian_convert_TxPD(pLocalTxPD); + *(u16 *) & headptr[0] = + uap_cpu_to_le16(skb->len + ((long) skb->data - (long) headptr)); + *(u16 *) & headptr[2] = uap_cpu_to_le16(MV_TYPE_DAT); + ret = + sbi_host_to_card(priv, headptr, + skb->len + ((long) skb->data - (long) headptr)); + if (ret) { + PRINTM(ERROR, "uap_process_tx Error: sbi_host_to_card failed: 0x%X\n", + ret); + Adapter->dbg.num_tx_host_to_card_failure++; + goto done; + } + PRINTM(DATA, "Data => FW\n"); + DBG_HEXDUMP(DAT_D, "Tx", headptr, + MIN(skb->len + sizeof(TxPD), DATA_DUMP_LEN)); + done: + /* Freed skb */ + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** + * @brief This function initializes the adapter structure + * and set default value to the member of adapter. + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +uap_init_sw(uap_private * priv) +{ + uap_adapter *Adapter = priv->adapter; + + ENTER(); + + if (!(Adapter->CmdBuf = kmalloc(MRVDRV_SIZE_OF_CMD_BUFFER, GFP_KERNEL))) { + PRINTM(INFO, "Failed to allocate command buffer!\n"); + LEAVE(); + return UAP_STATUS_FAILURE; + } + + Adapter->cmd_pending = FALSE; + Adapter->CmdWaitQWoken = FALSE; + Adapter->ps_state = PS_STATE_AWAKE; + Adapter->WakeupTries = 0; + + memset(&Adapter->PSConfirmSleep, 0, sizeof(PS_CMD_ConfirmSleep)); + /** SDIO header */ + Adapter->PSConfirmSleep.SDLen = + uap_cpu_to_le16(sizeof(PS_CMD_ConfirmSleep)); + Adapter->PSConfirmSleep.SDType = uap_cpu_to_le16(MV_TYPE_CMD); + Adapter->PSConfirmSleep.SeqNum = 0; + Adapter->PSConfirmSleep.Command = uap_cpu_to_le16(HOST_CMD_SLEEP_CONFIRM); + Adapter->PSConfirmSleep.Size = uap_cpu_to_le16(sizeof(HostCmd_DS_GEN)); + Adapter->PSConfirmSleep.Result = 0; + + init_waitqueue_head(&Adapter->cmdwait_q); + OS_INIT_SEMAPHORE(&Adapter->CmdSem); + + skb_queue_head_init(&Adapter->tx_queue); + skb_queue_head_init(&Adapter->cmd_queue); + + /* Status variable */ + Adapter->HardwareStatus = HWInitializing; + + /* PnP support */ + Adapter->SurpriseRemoved = FALSE; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + Adapter->nl_sk = netlink_kernel_create(NETLINK_MARVELL, + NL_MULTICAST_GROUP, NULL, + THIS_MODULE); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + Adapter->nl_sk = netlink_kernel_create(NETLINK_MARVELL, + NL_MULTICAST_GROUP, NULL, NULL, + THIS_MODULE); +#else + Adapter->nl_sk = netlink_kernel_create(&init_net, NETLINK_MARVELL, + NL_MULTICAST_GROUP, NULL, NULL, + THIS_MODULE); +#endif +#endif + if (!Adapter->nl_sk) { + PRINTM(ERROR, + "Could not initialize netlink event passing mechanism!\n"); + } + LEAVE(); + return UAP_STATUS_SUCCESS; +} + +/** + * @brief This function sends FUNC_INIT command to firmware + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS on success, otherwise failure code + */ +static int +uap_func_init(uap_private * priv) +{ + int ret = UAP_STATUS_SUCCESS; + u32 CmdSize; + HostCmd_DS_GEN *cmd; + uap_adapter *Adapter = priv->adapter; + struct sk_buff *skb; + ENTER(); + if (Adapter->HardwareStatus != HWReady) { + PRINTM(ERROR, "uap_func_init:Hardware is not ready!\n"); + ret = -EFAULT; + goto done; + } + skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!skb) { + PRINTM(ERROR, "No free skb\n"); + ret = -ENOMEM; + goto done; + } + CmdSize = sizeof(HostCmd_DS_GEN); + cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); + cmd->Command = uap_cpu_to_le16(HostCmd_CMD_FUNC_INIT); + cmd->Size = uap_cpu_to_le16(CmdSize); + skb_put(skb, CmdSize + INTF_HEADER_LEN); + PRINTM(CMND, "HostCmd_CMD_FUNC_INIT\n"); + if (UAP_STATUS_SUCCESS != + uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { + PRINTM(ERROR, "Fail to process cmd HostCmd_CMD_FUNC_INIT\n"); + ret = -EFAULT; + goto done; + } + done: + LEAVE(); + return ret; +} + +/** + * @brief This function sends FUNC_SHUTDOWN command to firmware + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS on success, otherwise failure code + */ +static int __exit +uap_func_shutdown(uap_private * priv) +{ + int ret = UAP_STATUS_SUCCESS; + u32 CmdSize; + HostCmd_DS_GEN *cmd; + uap_adapter *Adapter = priv->adapter; + struct sk_buff *skb; + ENTER(); + if (Adapter->HardwareStatus != HWReady) { + PRINTM(ERROR, "uap_func_shutdown:Hardware is not ready!\n"); + ret = -EFAULT; + goto done; + } + skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!skb) { + PRINTM(ERROR, "No free skb\n"); + ret = -ENOMEM; + goto done; + } + CmdSize = sizeof(HostCmd_DS_GEN); + cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); + cmd->Command = uap_cpu_to_le16(HostCmd_CMD_FUNC_SHUTDOWN); + cmd->Size = uap_cpu_to_le16(CmdSize); + skb_put(skb, CmdSize + INTF_HEADER_LEN); + PRINTM(CMND, "HostCmd_CMD_FUNC_SHUTDOWN\n"); + if (UAP_STATUS_SUCCESS != + uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { + PRINTM(ERROR, "Fail to process cmd HostCmd_CMD_FUNC_SHUTDOWN\n"); + ret = -EFAULT; + goto done; + } + done: + LEAVE(); + return ret; +} + +/** + * @brief This function initializes firmware + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +uap_init_fw(uap_private * priv) +{ + int ret = UAP_STATUS_SUCCESS; + ENTER(); + sbi_disable_host_int(priv); + /* Check if firmware is already running */ + if (sbi_check_fw_status(priv, 1) == UAP_STATUS_SUCCESS) { + PRINTM(MSG, "UAP FW already running! Skip FW download\n"); + } else { + if ((ret = request_firmware(&priv->fw_helper, helper_name, + priv->hotplug_device)) < 0) { + PRINTM(FATAL, + "request_firmware() failed (helper), error code = %#x\n", + ret); + goto done; + } + + /* Download the helper */ + ret = sbi_prog_helper(priv); + + if (ret) { + PRINTM(FATAL, + "Bootloader in invalid state! Helper download failed!\n"); + ret = UAP_STATUS_FAILURE; + goto done; + } + if ((ret = request_firmware(&priv->firmware, fw_name, + priv->hotplug_device)) < 0) { + PRINTM(FATAL, "request_firmware() failed, error code = %#x\n", ret); + goto done; + } + + /* Download the main firmware via the helper firmware */ + if (sbi_prog_fw_w_helper(priv)) { + PRINTM(FATAL, "UAP FW download failed!\n"); + ret = UAP_STATUS_FAILURE; + goto done; + } + /* Check if the firmware is downloaded successfully or not */ + if (sbi_check_fw_status(priv, MAX_FIRMWARE_POLL_TRIES) == + UAP_STATUS_FAILURE) { + PRINTM(FATAL, "FW failed to be active in time!\n"); + ret = UAP_STATUS_FAILURE; + goto done; + } + PRINTM(MSG, "UAP FW is active\n"); + } + sbi_enable_host_int(priv); + priv->adapter->HardwareStatus = HWReady; + if (uap_func_init(priv) != UAP_STATUS_SUCCESS) { + ret = UAP_STATUS_FAILURE; + goto done; + } + done: + if (priv->fw_helper) + release_firmware(priv->fw_helper); + if (priv->firmware) + release_firmware(priv->firmware); + LEAVE(); + return ret; + +} + +/** + * @brief This function frees the structure of adapter + * + * @param priv A pointer to uap_private structure + * @return n/a + */ +static void +uap_free_adapter(uap_private * priv) +{ + uap_adapter *Adapter = priv->adapter; + + ENTER(); + + if (Adapter) { + if ((Adapter->nl_sk) && ((Adapter->nl_sk)->sk_socket)) { + sock_release((Adapter->nl_sk)->sk_socket); + Adapter->nl_sk = NULL; + } + if (Adapter->CmdBuf) + kfree(Adapter->CmdBuf); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->cmd_queue); + /* Free the adapter object itself */ + kfree(Adapter); + priv->adapter = NULL; + } + + LEAVE(); +} + +/** + * @brief This function handles the major job in uap driver. + * it handles the event generated by firmware, rx data received + * from firmware and tx data sent from kernel. + * + * @param data A pointer to uap_thread structure + * @return BT_STATUS_SUCCESS + */ +static int +uap_service_main_thread(void *data) +{ + uap_thread *thread = data; + uap_private *priv = thread->priv; + uap_adapter *Adapter = priv->adapter; + wait_queue_t wait; + u8 ireg = 0; + struct sk_buff *skb; + ENTER(); + uap_activate_thread(thread); + init_waitqueue_entry(&wait, current); + current->flags |= PF_NOFREEZE; + + for (;;) { + add_wait_queue(&thread->waitQ, &wait); + OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE); + if ((Adapter->WakeupTries) || + (!Adapter->IntCounter && Adapter->ps_state == PS_STATE_PRE_SLEEP) || + (!priv->adapter->IntCounter + && (priv->uap_dev.data_sent || + skb_queue_empty(&priv->adapter->tx_queue)) + && (priv->uap_dev.cmd_sent || Adapter->cmd_pending || + skb_queue_empty(&priv->adapter->cmd_queue)) + )) { + PRINTM(INFO, "Main: Thread sleeping...\n"); + schedule(); + } + OS_SET_THREAD_STATE(TASK_RUNNING); + remove_wait_queue(&thread->waitQ, &wait); + if (kthread_should_stop() || Adapter->SurpriseRemoved) { + PRINTM(INFO, "main-thread: break from main thread: " + "SurpriseRemoved=0x%x\n", Adapter->SurpriseRemoved); + /* Cancel pending command */ + if (Adapter->cmd_pending == TRUE) { + /* Wake up cmd Q */ + Adapter->CmdWaitQWoken = TRUE; + wake_up_interruptible(&Adapter->cmdwait_q); + } + break; + } + + PRINTM(INFO, "Main: Thread waking up...\n"); + if (priv->adapter->IntCounter) { + OS_INT_DISABLE; + Adapter->IntCounter = 0; + OS_INT_RESTORE; + sbi_get_int_status(priv, &ireg); + } else if ((priv->adapter->ps_state == PS_STATE_SLEEP) && + (!skb_queue_empty(&priv->adapter->cmd_queue) || + !skb_queue_empty(&priv->adapter->tx_queue))) { + priv->adapter->WakeupTries++; + PRINTM(CMND, "%lu : Wakeup device...\n", os_time_get()); + sbi_wakeup_firmware(priv); + continue; + } + if (Adapter->ps_state == PS_STATE_PRE_SLEEP) + uap_ps_cond_check(priv); + + /* The PS state is changed during processing of Sleep Request event + above */ + if ((Adapter->ps_state == PS_STATE_SLEEP) || + (Adapter->ps_state == PS_STATE_PRE_SLEEP)) + continue; + /* Execute the next command */ + if (!priv->uap_dev.cmd_sent && !Adapter->cmd_pending && + (Adapter->HardwareStatus == HWReady)) { + if (!skb_queue_empty(&priv->adapter->cmd_queue)) { + skb = skb_dequeue(&priv->adapter->cmd_queue); + if (skb) { + Adapter->CmdSize = 0; + Adapter->cmd_pending = TRUE; + Adapter->cmd_wait_option = skb->cb[0]; + if (sbi_host_to_card(priv, skb->data, skb->len)) { + PRINTM(ERROR, "Cmd:sbi_host_to_card failed!\n"); + Adapter->cmd_pending = FALSE; + Adapter->dbg.num_cmd_host_to_card_failure++; + /* Wake up cmd Q */ + Adapter->CmdWaitQWoken = TRUE; + wake_up_interruptible(&Adapter->cmdwait_q); + } else { + if (Adapter->cmd_wait_option == + HostCmd_OPTION_WAITFORSEND) { + /* Wake up cmd Q */ + Adapter->CmdWaitQWoken = TRUE; + wake_up_interruptible(&Adapter->cmdwait_q); + Adapter->cmd_wait_option = FALSE; + } + } + kfree_skb(skb); + } + } + } + if (!priv->uap_dev.data_sent && (Adapter->HardwareStatus == HWReady)) { + if (!skb_queue_empty(&priv->adapter->tx_queue)) { + skb = skb_dequeue(&priv->adapter->tx_queue); + if (skb) { + if (uap_process_tx(priv, skb)) { + priv->stats.tx_dropped++; + priv->stats.tx_errors++; + os_start_queue(priv); + } else { + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + } + + } + } + } + } + uap_deactivate_thread(thread); + LEAVE(); + return UAP_STATUS_SUCCESS; +} + +/** + * @brief uap hostcmd ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return UAP_STATUS_SUCCESS --success, otherwise fail + */ +/********* format of ifr_data *************/ +/* buf_len + Hostcmd_body */ +/* buf_len: 4 bytes */ +/* the length of the buf which */ +/* can be used to return data */ +/* to application */ +/* Hostcmd_body */ +/*******************************************/ +static int +uap_hostcmd_ioctl(struct net_device *dev, struct ifreq *req) +{ + u32 buf_len; + HostCmd_HEADER head; + uap_private *priv = (uap_private *) netdev_priv(dev); + uap_adapter *Adapter = priv->adapter; + int ret = UAP_STATUS_SUCCESS; + struct sk_buff *skb; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(ERROR, "uap_hostcmd_ioctl() corrupt data\n"); + LEAVE(); + return -EFAULT; + } + if (copy_from_user(&buf_len, req->ifr_data, sizeof(buf_len))) { + PRINTM(ERROR, "Copy from user failed\n"); + LEAVE(); + return -EFAULT; + } + memset(&head, 0, sizeof(HostCmd_HEADER)); + /* Get the command size from user space */ + if (copy_from_user + (&head, req->ifr_data + sizeof(buf_len), sizeof(HostCmd_HEADER))) { + PRINTM(ERROR, "Copy from user failed\n"); + LEAVE(); + return -EFAULT; + } + head.Size = uap_le16_to_cpu(head.Size); + if (head.Size > MRVDRV_SIZE_OF_CMD_BUFFER) { + PRINTM(ERROR, "CmdSize too big=%d\n", head.Size); + LEAVE(); + return -EFAULT; + } + PRINTM(CMND, "ioctl: hostcmd=%x, size=%d,buf_len=%d\n", head.Command, + head.Size, buf_len); + skb = dev_alloc_skb(head.Size + INTF_HEADER_LEN); + if (!skb) { + PRINTM(ERROR, "No free skb\n"); + LEAVE(); + return -ENOMEM; + } + + /* Get the command from user space */ + if (copy_from_user + (skb->data + INTF_HEADER_LEN, req->ifr_data + sizeof(buf_len), + head.Size)) { + PRINTM(ERROR, "Copy from user failed\n"); + LEAVE(); + return -EFAULT; + } + skb_put(skb, head.Size + INTF_HEADER_LEN); + if (UAP_STATUS_SUCCESS != + uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP)) { + PRINTM(ERROR, "Fail to process cmd\n"); + LEAVE(); + return -EFAULT; + } + if (!Adapter->CmdSize) { + PRINTM(ERROR, "Cmd Size is 0\n"); + LEAVE(); + return -EFAULT; + } + if (Adapter->CmdSize > buf_len) { + PRINTM(ERROR, "buf_len is too small\n"); + LEAVE(); + return -EFAULT; + } + /* Copy to user */ + if (copy_to_user + (req->ifr_data + sizeof(buf_len), Adapter->CmdBuf, Adapter->CmdSize)) { + PRINTM(ERROR, "Copy to user failed!\n"); + LEAVE(); + return -EFAULT; + } + LEAVE(); + return ret; +} + +/** + * @brief uap power mode ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return UAP_STATUS_SUCCESS --success, otherwise fail + */ +static int +uap_power_mode_ioctl(struct net_device *dev, struct ifreq *req) +{ + ps_mgmt pm_cfg; + int ret = UAP_STATUS_SUCCESS; + uap_private *priv = (uap_private *) netdev_priv(dev); + uap_adapter *Adapter = priv->adapter; + struct sk_buff *skb = NULL; + HostCmd_DS_COMMAND *cmd; + u32 CmdSize; + u8 *tlv = NULL; + MrvlIEtypes_sleep_param_t *sleep_tlv = NULL; + MrvlIEtypes_inact_sleep_param_t *inact_tlv = NULL; + u16 tlv_buf_left = 0; + MrvlIEtypesHeader_t *tlvbuf = NULL; + u16 tlv_type = 0; + u16 tlv_len = 0; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(ERROR, "uap_power_mode_ioctl() corrupt data\n"); + LEAVE(); + return -EFAULT; + } + + memset(&pm_cfg, 0, sizeof(ps_mgmt)); + if (copy_from_user(&pm_cfg, req->ifr_data, sizeof(ps_mgmt))) { + PRINTM(ERROR, "Copy from user failed\n"); + LEAVE(); + return -EFAULT; + } + PRINTM(CMND, + "ioctl power: flag=0x%x ps_mode=%d ctrl_bitmap=%d min_sleep=%d max_sleep=%d " + "inact_to=%d min_awake=%d max_awake=%d\n", pm_cfg.flags, + (int) pm_cfg.ps_mode, (int) pm_cfg.sleep_param.ctrl_bitmap, + (int) pm_cfg.sleep_param.min_sleep, + (int) pm_cfg.sleep_param.max_sleep, + (int) pm_cfg.inact_param.inactivity_to, + (int) pm_cfg.inact_param.min_awake, + (int) pm_cfg.inact_param.max_awake); + + if (pm_cfg. + flags & ~(PS_FLAG_PS_MODE | PS_FLAG_SLEEP_PARAM | + PS_FLAG_INACT_SLEEP_PARAM)) { + PRINTM(ERROR, "Invalid parameter: flags = 0x%x\n", pm_cfg.flags); + ret = -EINVAL; + goto done; + } + if (pm_cfg.ps_mode > PS_MODE_INACTIVITY) { + PRINTM(ERROR, "Invalid parameter: ps_mode = %d\n", (int) pm_cfg.flags); + ret = -EINVAL; + goto done; + } + + skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!skb) { + PRINTM(INFO, "No free skb\n"); + ret = -ENOMEM; + goto done; + } + + CmdSize = S_DS_GEN + sizeof(HostCmd_DS_POWER_MGMT_EXT); + + cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); + cmd->Command = uap_cpu_to_le16(HOST_CMD_POWER_MGMT_EXT); + if (!pm_cfg.flags) { + cmd->params.pm_cfg.action = uap_cpu_to_le16(ACTION_GET); + } else { + cmd->params.pm_cfg.action = uap_cpu_to_le16(ACTION_SET); + cmd->params.pm_cfg.power_mode = uap_cpu_to_le16(pm_cfg.ps_mode); + tlv = (u8 *) & cmd->params.pm_cfg + sizeof(HostCmd_DS_POWER_MGMT_EXT); + + if ((pm_cfg.ps_mode) && (pm_cfg.flags & PS_FLAG_SLEEP_PARAM)) { + sleep_tlv = (MrvlIEtypes_sleep_param_t *) tlv; + sleep_tlv->header.Type = uap_cpu_to_le16(TLV_TYPE_AP_SLEEP_PARAM); + sleep_tlv->header.Len = + uap_cpu_to_le16(sizeof(MrvlIEtypes_sleep_param_t) - + sizeof(MrvlIEtypesHeader_t)); + sleep_tlv->ctrl_bitmap = + uap_cpu_to_le32(pm_cfg.sleep_param.ctrl_bitmap); + sleep_tlv->min_sleep = + uap_cpu_to_le32(pm_cfg.sleep_param.min_sleep); + sleep_tlv->max_sleep = + uap_cpu_to_le32(pm_cfg.sleep_param.max_sleep); + CmdSize += sizeof(MrvlIEtypes_sleep_param_t); + tlv += sizeof(MrvlIEtypes_sleep_param_t); + } + if ((pm_cfg.ps_mode == PS_MODE_INACTIVITY) && + (pm_cfg.flags & PS_FLAG_INACT_SLEEP_PARAM)) { + inact_tlv = (MrvlIEtypes_inact_sleep_param_t *) tlv; + inact_tlv->header.Type = + uap_cpu_to_le16(TLV_TYPE_AP_INACT_SLEEP_PARAM); + inact_tlv->header.Len = + uap_cpu_to_le16(sizeof(MrvlIEtypes_inact_sleep_param_t) - + sizeof(MrvlIEtypesHeader_t)); + inact_tlv->inactivity_to = + uap_cpu_to_le32(pm_cfg.inact_param.inactivity_to); + inact_tlv->min_awake = + uap_cpu_to_le32(pm_cfg.inact_param.min_awake); + inact_tlv->max_awake = + uap_cpu_to_le32(pm_cfg.inact_param.max_awake); + CmdSize += sizeof(MrvlIEtypes_inact_sleep_param_t); + tlv += sizeof(MrvlIEtypes_inact_sleep_param_t); + } + } + cmd->Size = uap_cpu_to_le16(CmdSize); + skb_put(skb, CmdSize + INTF_HEADER_LEN); + if (UAP_STATUS_SUCCESS != + uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP)) { + PRINTM(ERROR, "Fail to process cmd POWER_MODE\n"); + ret = -EFAULT; + goto done; + } + if (!Adapter->CmdSize) { + PRINTM(ERROR, "Cmd Size is 0\n"); + ret = -EFAULT; + goto done; + } + cmd = (HostCmd_DS_COMMAND *) Adapter->CmdBuf; + cmd->Result = uap_le16_to_cpu(cmd->Result); + if (cmd->Result != UAP_STATUS_SUCCESS) { + PRINTM(ERROR, "HOST_CMD_APCMD_POWER_MODE fail=%x\n", cmd->Result); + ret = -EFAULT; + goto done; + } + if (pm_cfg.flags) { + Adapter->psmode = uap_le16_to_cpu(cmd->params.pm_cfg.power_mode); + } else { + pm_cfg.flags = PS_FLAG_PS_MODE; + pm_cfg.ps_mode = uap_le16_to_cpu(cmd->params.pm_cfg.power_mode); + tlv_buf_left = + cmd->Size - (sizeof(HostCmd_DS_POWER_MGMT_EXT) + S_DS_GEN); + tlvbuf = + (MrvlIEtypesHeader_t *) ((u8 *) & cmd->params.pm_cfg + + sizeof(HostCmd_DS_POWER_MGMT_EXT)); + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + tlv_type = uap_le16_to_cpu(tlvbuf->Type); + tlv_len = uap_le16_to_cpu(tlvbuf->Len); + switch (tlv_type) { + case TLV_TYPE_AP_SLEEP_PARAM: + sleep_tlv = (MrvlIEtypes_sleep_param_t *) tlvbuf; + pm_cfg.flags |= PS_FLAG_SLEEP_PARAM; + pm_cfg.sleep_param.ctrl_bitmap = + uap_le32_to_cpu(sleep_tlv->ctrl_bitmap); + pm_cfg.sleep_param.min_sleep = + uap_le32_to_cpu(sleep_tlv->min_sleep); + pm_cfg.sleep_param.max_sleep = + uap_le32_to_cpu(sleep_tlv->max_sleep); + break; + case TLV_TYPE_AP_INACT_SLEEP_PARAM: + inact_tlv = (MrvlIEtypes_inact_sleep_param_t *) tlvbuf; + pm_cfg.flags |= PS_FLAG_INACT_SLEEP_PARAM; + pm_cfg.inact_param.inactivity_to = + uap_le32_to_cpu(inact_tlv->inactivity_to); + pm_cfg.inact_param.min_awake = + uap_le32_to_cpu(inact_tlv->min_awake); + pm_cfg.inact_param.max_awake = + uap_le32_to_cpu(inact_tlv->max_awake); + break; + } + tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); + tlvbuf = + (MrvlIEtypesHeader_t *) ((u8 *) tlvbuf + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, &pm_cfg, sizeof(ps_mgmt))) { + PRINTM(ERROR, "Copy to user failed!\n"); + LEAVE(); + return -EFAULT; + } + } + done: + LEAVE(); + return ret; +} + +/** + * @brief This function send bss_stop command to firmware + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS on success, otherwise failure code + */ +static int +uap_bss_stop(uap_private * priv) +{ + int ret = UAP_STATUS_SUCCESS; + u32 CmdSize; + HostCmd_DS_GEN *cmd; + uap_adapter *Adapter = priv->adapter; + struct sk_buff *skb; + ENTER(); + if (Adapter->HardwareStatus != HWReady) { + PRINTM(ERROR, "uap_bss_stop:Hardware is not ready!\n"); + ret = -EFAULT; + goto done; + } + skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!skb) { + PRINTM(ERROR, "No free skb\n"); + ret = -ENOMEM; + goto done; + } + CmdSize = sizeof(HostCmd_DS_GEN); + cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); + cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_BSS_STOP); + cmd->Size = uap_cpu_to_le16(CmdSize); + skb_put(skb, CmdSize + INTF_HEADER_LEN); + PRINTM(CMND, "APCMD_BSS_STOP\n"); + if (UAP_STATUS_SUCCESS != + uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { + PRINTM(ERROR, "Fail to process cmd BSS_STOP\n"); + ret = -EFAULT; + goto done; + } + done: + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function send soft_reset command to firmware + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS on success, otherwise failure code + */ +int +uap_soft_reset(uap_private * priv) +{ + int ret = UAP_STATUS_SUCCESS; + u32 CmdSize; + HostCmd_DS_GEN *cmd; + uap_adapter *Adapter = priv->adapter; + struct sk_buff *skb; + ENTER(); + ret = uap_bss_stop(priv); + if (ret != UAP_STATUS_SUCCESS) + goto done; + skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!skb) { + PRINTM(ERROR, "No free skb\n"); + ret = -ENOMEM; + goto done; + } + CmdSize = sizeof(HostCmd_DS_GEN); + cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); + cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_SOFT_RESET); + cmd->Size = uap_cpu_to_le16(CmdSize); + skb_put(skb, CmdSize + INTF_HEADER_LEN); + PRINTM(CMND, "APCMD_SOFT_RESET\n"); + if (UAP_STATUS_SUCCESS != + uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORSEND)) { + PRINTM(ERROR, "Fail to process cmd SOFT_RESET\n"); + ret = -EFAULT; + goto done; + } + Adapter->SurpriseRemoved = TRUE; + /* delay to allow hardware complete reset */ + os_sched_timeout(5); + if (priv->MediaConnected == TRUE) { + os_stop_queue(priv); + os_carrier_off(priv); + priv->MediaConnected = FALSE; + } + Adapter->CmdSize = 0; + Adapter->CmdWaitQWoken = TRUE; + wake_up_interruptible(&Adapter->cmdwait_q); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->cmd_queue); + done: + LEAVE(); + return ret; +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer + * + * @param priv A pointer to uap_private + * @param skb A pointer to skb which includes the received packet + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +uap_process_rx_packet(uap_private * priv, struct sk_buff *skb) +{ + int ret = UAP_STATUS_SUCCESS; + RxPD *pRxPD; + ENTER(); + priv->adapter->ps_state = PS_STATE_AWAKE; + pRxPD = (RxPD *) skb->data; + endian_convert_RxPD(pRxPD); + DBG_HEXDUMP(DAT_D, "Rx", skb->data, MIN(skb->len, DATA_DUMP_LEN)); + skb_pull(skb, pRxPD->RxPktOffset); + priv->stats.rx_packets++; + priv->stats.rx_bytes += skb->len; + os_upload_rx_packet(priv, skb); + LEAVE(); + return ret; +} + +/** + * @brief This function opens the network device + * + * @param dev A pointer to net_device structure + * @return UAP_STATUS_SUCCESS + */ +static int +uap_open(struct net_device *dev) +{ + uap_private *priv = (uap_private *) (uap_private *) netdev_priv(dev); + uap_adapter *Adapter = priv->adapter; + int i = 0; + + ENTER(); + + /* On some systems the device open handler will be called before HW ready. */ + /* Use the following flag check and wait function to work around the issue. */ + while ((Adapter->HardwareStatus != HWReady) && + (i < MAX_WAIT_DEVICE_READY_COUNT)) { + i++; + os_sched_timeout(100); + } + if (i >= MAX_WAIT_DEVICE_READY_COUNT) { + PRINTM(FATAL, "HW not ready, uap_open() return failure\n"); + LEAVE(); + return UAP_STATUS_FAILURE; + } + + if (MODULE_GET == 0) + return UAP_STATUS_FAILURE; + + priv->open = TRUE; + if (priv->MediaConnected == TRUE) { + os_carrier_on(priv); + os_start_queue(priv); + } else { + os_stop_queue(priv); + os_carrier_off(priv); + } + LEAVE(); + return UAP_STATUS_SUCCESS; +} + +/** + * @brief This function closes the network device + * + * @param dev A pointer to net_device structure + * @return UAP_STATUS_SUCCESS + */ +static int +uap_close(struct net_device *dev) +{ + uap_private *priv = (uap_private *) netdev_priv(dev); + + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + os_stop_queue(priv); + os_carrier_off(priv); + + MODULE_PUT; + priv->open = FALSE; + LEAVE(); + return UAP_STATUS_SUCCESS; +} + +/** + * @brief This function returns the network statistics + * + * @param dev A pointer to uap_private structure + * @return A pointer to net_device_stats structure + */ +static struct net_device_stats * +uap_get_stats(struct net_device *dev) +{ + uap_private *priv = (uap_private *) netdev_priv(dev); + + return &priv->stats; +} + +/** + * @brief This function sets the MAC address to firmware. + * + * @param dev A pointer to uap_private structure + * @param addr MAC address to set + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +uap_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = UAP_STATUS_SUCCESS; + uap_private *priv = (uap_private *) netdev_priv(dev); + struct sockaddr *pHwAddr = (struct sockaddr *) addr; + u32 CmdSize; + HostCmd_DS_COMMAND *cmd; + MrvlIEtypes_MacAddr_t *pMacAddrTlv; + uap_adapter *Adapter = priv->adapter; + struct sk_buff *skb; + + ENTER(); + + /* Dump MAC address */ + DBG_HEXDUMP(CMD_D, "Original MAC addr", dev->dev_addr, ETH_ALEN); + DBG_HEXDUMP(CMD_D, "New MAC addr", pHwAddr->sa_data, ETH_ALEN); + if (priv->open && (priv->MediaConnected == TRUE)) { + os_carrier_on(priv); + os_start_queue(priv); + } + skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!skb) { + PRINTM(ERROR, "No free skb\n"); + LEAVE(); + return -ENOMEM; + } + CmdSize = + S_DS_GEN + sizeof(HostCmd_SYS_CONFIG) + sizeof(MrvlIEtypes_MacAddr_t); + cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); + cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_SYS_CONFIGURE); + cmd->Size = uap_cpu_to_le16(CmdSize); + cmd->params.sys_config.Action = uap_cpu_to_le16(ACTION_SET); + pMacAddrTlv = + (MrvlIEtypes_MacAddr_t *) ((u8 *) cmd + S_DS_GEN + + sizeof(HostCmd_SYS_CONFIG)); + pMacAddrTlv->Header.Type = uap_cpu_to_le16(MRVL_AP_MAC_ADDRESS_TLV_ID); + pMacAddrTlv->Header.Len = uap_cpu_to_le16(ETH_ALEN); + memcpy(pMacAddrTlv->ApMacAddr, pHwAddr->sa_data, ETH_ALEN); + skb_put(skb, CmdSize + INTF_HEADER_LEN); + PRINTM(CMND, "set_mac_address\n"); + if (UAP_STATUS_SUCCESS != + uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { + PRINTM(ERROR, "Fail to set mac address\n"); + LEAVE(); + return -EFAULT; + } + if (!Adapter->CmdSize) { + PRINTM(ERROR, "Cmd Size is 0\n"); + LEAVE(); + return -EFAULT; + } + cmd = (HostCmd_DS_COMMAND *) Adapter->CmdBuf; + cmd->Result = uap_cpu_to_le16(cmd->Result); + if (cmd->Result != UAP_STATUS_SUCCESS) { + PRINTM(ERROR, "set mac addrress fail,cmd result=%x\n", cmd->Result); + ret = -EFAULT; + } else + memcpy(dev->dev_addr, pHwAddr->sa_data, ETH_ALEN); + LEAVE(); + return ret; +} + +/** + * @brief This function handles the timeout of packet + * transmission + * + * @param dev A pointer to net_device structure + * @return n/a + */ +static void +uap_tx_timeout(struct net_device *dev) +{ + uap_private *priv = (uap_private *) netdev_priv(dev); + + ENTER(); + + PRINTM(DATA, "Tx timeout\n"); + UpdateTransStart(dev); + priv->num_tx_timeout++; + priv->adapter->IntCounter++; + wake_up_interruptible(&priv->MainThread.waitQ); + + LEAVE(); +} + +/** + * @brief This function handles packet transmission + * + * @param skb A pointer to sk_buff structure + * @param dev A pointer to net_device structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +uap_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + uap_private *priv = (uap_private *) netdev_priv(dev); + int ret = UAP_STATUS_SUCCESS; + + ENTER(); + PRINTM(DATA, "Data <= kernel\n"); + DBG_HEXDUMP(DAT_D, "Tx", skb->data, MIN(skb->len, DATA_DUMP_LEN)); + /* skb sanity check */ + if (!skb->len || (skb->len > MRVDRV_MAXIMUM_ETH_PACKET_SIZE)) { + PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len, + MRVDRV_MAXIMUM_ETH_PACKET_SIZE); + priv->stats.tx_dropped++; + kfree(skb); + goto done; + } + skb_queue_tail(&priv->adapter->tx_queue, skb); + wake_up_interruptible(&priv->MainThread.waitQ); + if (skb_queue_len(&priv->adapter->tx_queue) > TX_HIGH_WATERMARK) { + UpdateTransStart(dev); + os_stop_queue(priv); + } + done: + LEAVE(); + return ret; +} + +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd command + * @return UAP_STATUS_SUCCESS--success, otherwise fail + */ +static int +uap_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int ret = UAP_STATUS_SUCCESS; + + ENTER(); + + PRINTM(CMND, "uap_do_ioctl: ioctl cmd = 0x%x\n", cmd); + + switch (cmd) { + case UAPHOSTCMD: + ret = uap_hostcmd_ioctl(dev, req); + break; + case UAP_POWER_MODE: + ret = uap_power_mode_ioctl(dev, req); + break; + default: + ret = -EINVAL; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function handles events generated by firmware + * + * @param priv A pointer to uap_private structure + * @param payload A pointer to payload buffer + * @param len Length of the payload + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +uap_process_event(uap_private * priv, u8 * payload, uint len) +{ + int ret = UAP_STATUS_SUCCESS; + uap_adapter *Adapter = priv->adapter; + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + struct sock *sk = Adapter->nl_sk; + AP_Event *pEvent; + + ENTER(); + Adapter->ps_state = PS_STATE_AWAKE; + if (len > NL_MAX_PAYLOAD) { + PRINTM(ERROR, "event size is too big!!! len=%d\n", len); + ret = UAP_STATUS_FAILURE; + goto done; + } + pEvent = (AP_Event *) payload; + PRINTM(CMND, "Event: %d\n", pEvent->EventId); + switch (pEvent->EventId) { + case MICRO_AP_EV_ID_BSS_START: + memcpy(priv->uap_dev.netdev->dev_addr, pEvent->MacAddr, ETH_ALEN); + DBG_HEXDUMP(CMD_D, "BSS MAC addr", priv->uap_dev.netdev->dev_addr, + ETH_ALEN); + break; + case MICRO_AP_EV_BSS_ACTIVE: + // carrier on + priv->MediaConnected = TRUE; + os_carrier_on(priv); + os_start_queue(priv); + break; + case MICRO_AP_EV_BSS_IDLE: + os_stop_queue(priv); + os_carrier_off(priv); + priv->MediaConnected = FALSE; + break; + case EVENT_PS_AWAKE: + PRINTM(CMND, "UAP: PS_AWAKE\n"); + Adapter->ps_state = PS_STATE_AWAKE; + Adapter->WakeupTries = 0; + break; + case EVENT_PS_SLEEP: + PRINTM(CMND, "UAP: PS_SLEEP\n"); + Adapter->ps_state = PS_STATE_PRE_SLEEP; + break; + default: + break; + } + if ((pEvent->EventId == EVENT_PS_AWAKE) || + (pEvent->EventId == EVENT_PS_SLEEP)) + goto done; + if (sk) { + /* Allocate skb */ + if (!(skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD), GFP_ATOMIC))) { + PRINTM(ERROR, "Could not allocate skb for netlink.\n"); + ret = UAP_STATUS_FAILURE; + goto done; + } + nlh = (struct nlmsghdr *) skb->data; + nlh->nlmsg_len = NLMSG_SPACE(len); + + /* From kernel */ + nlh->nlmsg_pid = 0; + nlh->nlmsg_flags = 0; + + /* Data */ + skb_put(skb, nlh->nlmsg_len); + memcpy(NLMSG_DATA(nlh), payload, len); + + /* From Kernel */ + NETLINK_CB(skb).pid = 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + /* Multicast message */ + NETLINK_CB(skb).dst_pid = 0; +#endif + + /* Multicast group number */ + NETLINK_CB(skb).dst_group = NL_MULTICAST_GROUP; + + /* Send message */ + netlink_broadcast(sk, skb, 0, NL_MULTICAST_GROUP, GFP_KERNEL); + + ret = UAP_STATUS_SUCCESS; + } else { + PRINTM(ERROR, "Could not send event through NETLINK. Link down.\n"); + ret = UAP_STATUS_FAILURE; + } + done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the interrupt. it will change PS + * state if applicable. it will wake up main_thread to handle + * the interrupt event as well. + * + * @param priv A pointer to uap_private structure + * @return n/a + */ +void +uap_interrupt(uap_private * priv) +{ + ENTER(); + priv->adapter->IntCounter++; + priv->adapter->WakeupTries = 0; + PRINTM(INFO, "*\n"); + wake_up_interruptible(&priv->MainThread.waitQ); + + LEAVE(); + +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) +/** Network device handlers */ +static const struct net_device_ops uap_netdev_ops = { + .ndo_open = uap_open, + .ndo_start_xmit = uap_hard_start_xmit, + .ndo_stop = uap_close, + .ndo_do_ioctl = uap_do_ioctl, + .ndo_set_mac_address = uap_set_mac_address, + .ndo_tx_timeout = uap_tx_timeout, + .ndo_get_stats = uap_get_stats, +}; +#endif + +/** + * @brief This function adds the card. it will probe the + * card, allocate the uap_priv and initialize the device. + * + * @param card A pointer to card + * @return A pointer to uap_private structure + */ +uap_private * +uap_add_card(void *card) +{ + struct net_device *dev = NULL; + uap_private *priv = NULL; + + ENTER(); + + if (OS_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + /* Allocate an Ethernet device */ + if (!(dev = alloc_etherdev(sizeof(uap_private)))) { + PRINTM(FATAL, "Init ethernet device failed!\n"); + goto error; + } + priv = (uap_private *) netdev_priv(dev); + + /* Allocate name */ + if (dev_alloc_name(dev, "uap%d") < 0) { + PRINTM(ERROR, "Could not allocate device name!\n"); + goto error; + } + + /* Allocate buffer for uap_adapter */ + if (!(priv->adapter = kmalloc(sizeof(uap_adapter), GFP_KERNEL))) { + PRINTM(FATAL, "Allocate buffer for uap_adapter failed!\n"); + goto error; + } + memset(priv->adapter, 0, sizeof(uap_adapter)); + + priv->uap_dev.netdev = dev; + priv->uap_dev.card = card; + priv->MediaConnected = FALSE; + uappriv = priv; + ((struct sdio_mmc_card *) card)->priv = priv; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + SET_MODULE_OWNER(dev); +#endif + + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) + dev->open = uap_open; + dev->stop = uap_close; + dev->hard_start_xmit = uap_hard_start_xmit; + dev->tx_timeout = uap_tx_timeout; + dev->get_stats = uap_get_stats; + dev->do_ioctl = uap_do_ioctl; + dev->set_mac_address = uap_set_mac_address; + dev->set_multicast_list = uap_set_multicast_list; +#else + dev->netdev_ops = &uap_netdev_ops; +#endif + dev->watchdog_timeo = MRVDRV_DEFAULT_WATCHDOG_TIMEOUT; + dev->hard_header_len += sizeof(TxPD) + INTF_HEADER_LEN; + dev->hard_header_len += HEADER_ALIGNMENT; +#define NETIF_F_DYNALLOC 16 + dev->features |= NETIF_F_DYNALLOC; + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + + /* Init SW */ + if (uap_init_sw(priv)) { + PRINTM(FATAL, "Software Init Failed\n"); + goto error; + } + + PRINTM(INFO, "Starting kthread...\n"); + priv->MainThread.priv = priv; + spin_lock_init(&priv->driver_lock); + uap_create_thread(uap_service_main_thread, &priv->MainThread, + "uap_main_service"); + while (priv->MainThread.pid == 0) { + os_sched_timeout(2); + } + + /* Register the device */ + if (sbi_register_dev(priv) < 0) { + PRINTM(FATAL, "Failed to register uap device!\n"); + goto err_registerdev; + } +#ifdef FW_DNLD_NEEDED + SET_NETDEV_DEV(dev, priv->hotplug_device); +#endif + + /* Init FW and HW */ + if (uap_init_fw(priv)) { + PRINTM(FATAL, "Firmware Init Failed\n"); + goto err_init_fw; + } + + priv->uap_dev.cmd_sent = FALSE; + priv->uap_dev.data_sent = FALSE; + + /* Get mac address from firmware */ + if (uap_get_mac_address(priv)) { + PRINTM(FATAL, "Fail to get mac address\n"); + goto err_init_fw; + } + /* Register network device */ + if (register_netdev(dev)) { + printk(KERN_ERR "Cannot register network device!\n"); + goto err_init_fw; + } +#ifdef CONFIG_PROC_FS + uap_proc_entry(priv, dev); + uap_debug_entry(priv, dev); +#endif /* CPNFIG_PROC_FS */ + OS_REL_SEMAPHORE(&AddRemoveCardSem); + + LEAVE(); + return priv; + err_init_fw: + sbi_unregister_dev(priv); + err_registerdev: + ((struct sdio_mmc_card *) card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) { + os_sched_timeout(1); + } + error: + if (dev) { + if (dev->reg_state == NETREG_REGISTERED) + unregister_netdev(dev); + if (priv->adapter) + uap_free_adapter(priv); + free_netdev(dev); + uappriv = NULL; + } + OS_REL_SEMAPHORE(&AddRemoveCardSem); + exit_sem_err: + LEAVE(); + return NULL; +} + +/** + * @brief This function removes the card. + * + * @param card A pointer to card + * @return UAP_STATUS_SUCCESS + */ +int +uap_remove_card(void *card) +{ + uap_private *priv = uappriv; + uap_adapter *Adapter; + struct net_device *dev; + + ENTER(); + + if (OS_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + if (!priv || !(Adapter = priv->adapter)) { + goto exit_remove; + } + Adapter->SurpriseRemoved = TRUE; + if (Adapter->cmd_pending == TRUE) { + /* Wake up cmd Q */ + Adapter->CmdWaitQWoken = TRUE; + wake_up_interruptible(&Adapter->cmdwait_q); + } + dev = priv->uap_dev.netdev; + if (priv->MediaConnected == TRUE) { + os_stop_queue(priv); + os_carrier_off(priv); + priv->MediaConnected = FALSE; + } + Adapter->CmdSize = 0; + Adapter->CmdWaitQWoken = TRUE; + wake_up_interruptible(&Adapter->cmdwait_q); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->cmd_queue); + + /* Disable interrupts on the card */ + sbi_disable_host_int(priv); + PRINTM(INFO, "netdev_finish_unregister: %s%s.\n", dev->name, + (dev->features & NETIF_F_DYNALLOC) ? "" : ", old style"); + unregister_netdev(dev); + PRINTM(INFO, "Unregister finish\n"); + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) { + os_sched_timeout(1); + } + + if ((Adapter->nl_sk) && ((Adapter->nl_sk)->sk_socket)) { + sock_release((Adapter->nl_sk)->sk_socket); + Adapter->nl_sk = NULL; + } +#ifdef CONFIG_PROC_FS + uap_debug_remove(priv); + uap_proc_remove(priv); +#endif + sbi_unregister_dev(priv); + PRINTM(INFO, "Free Adapter\n"); + uap_free_adapter(priv); + priv->uap_dev.netdev = NULL; + free_netdev(dev); + uappriv = NULL; + + exit_remove: + OS_REL_SEMAPHORE(&AddRemoveCardSem); + exit_sem_err: + LEAVE(); + return UAP_STATUS_SUCCESS; +} + +/** + * @brief This function initializes module. + * + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int __init +uap_init_module(void) +{ + int ret = UAP_STATUS_SUCCESS; + ENTER(); + + OS_INIT_SEMAPHORE(&AddRemoveCardSem); + ret = sbi_register(); + LEAVE(); + return ret; +} + +/** + * @brief This function cleans module + * + * @return n/a + */ +static void __exit +uap_cleanup_module(void) +{ + ENTER(); + + if (OS_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + if ((uappriv) && (uappriv->adapter)) { + uap_func_shutdown(uappriv); + } + OS_REL_SEMAPHORE(&AddRemoveCardSem); + exit_sem_err: + sbi_unregister(); + LEAVE(); +} + +module_init(uap_init_module); +module_exit(uap_cleanup_module); +module_param(helper_name, charp, 0); +MODULE_PARM_DESC(helper_name, "Helper name"); +module_param(fw_name, charp, 0); +MODULE_PARM_DESC(fw_name, "Firmware name"); + +MODULE_DESCRIPTION("M-UAP Driver"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/libertas_uap/uap_proc.c b/libertas_uap/uap_proc.c new file mode 100644 index 0000000..a28a9a6 --- /dev/null +++ b/libertas_uap/uap_proc.c @@ -0,0 +1,296 @@ +/** @file uap_proc.c + * @brief This file contains functions for proc file. + * + * Copyright (C) 2008-2009, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +#ifdef CONFIG_PROC_FS +#include "uap_headers.h" + +/** /proc directory root */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#define PROC_DIR NULL +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) +#define PROC_DIR &proc_root +#else +#define PROC_DIR proc_net +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief proc read function + * + * @param page pointer to buffer + * @param start read data starting position + * @param offset offset + * @param count counter + * @param eof end of file flag + * @param data data to output + * @return number of output data + */ +static int +uap_proc_read(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + int i; + char *p = page; + struct net_device *netdev = data; + struct netdev_hw_addr *ha; + char fmt[64]; + uap_private *priv = (uap_private *) netdev_priv(netdev); + + if (offset != 0) { + *eof = 1; + goto exit; + } + + strcpy(fmt, DRIVER_VERSION); + + p += sprintf(p, "driver_name = " "\"uap\"\n"); + p += sprintf(p, "driver_version = %s-(FP%s)", fmt, FPNUM); + p += sprintf(p, "\nInterfaceName=\"%s\"\n", netdev->name); + p += sprintf(p, "State=\"%s\"\n", + ((priv->MediaConnected == + FALSE) ? "Disconnected" : "Connected")); + p += sprintf(p, "MACAddress=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", + netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], + netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); + i = 0; + netdev_for_each_mc_addr(ha, netdev) { + ++i; + } + p += sprintf(p, "MCCount=\"%d\"\n", i); + + /* + * Put out the multicast list + */ + i = 0; + netdev_for_each_mc_addr(ha, netdev) { + p += sprintf(p, + "MCAddr[%d]=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", + i++, + ha->addr[0], ha->addr[1], + ha->addr[2], ha->addr[3], + ha->addr[4], ha->addr[5]); + } + + p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); + p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); + p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); + p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); + p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); + p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); + p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); + p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); + p += sprintf(p, "num_tx_timeout = %u\n", priv->num_tx_timeout); + p += sprintf(p, "carrier %s\n", + ((netif_carrier_ok(priv->uap_dev.netdev)) ? "on" : "off")); + p += sprintf(p, "tx queue %s\n", + ((netif_queue_stopped(priv->uap_dev.netdev)) ? "stopped" : + "started")); + + exit: + return (p - page); +} + +/** + * @brief hwstatus proc write function + * + * @param f file pointer + * @param buf pointer to data buffer + * @param cnt data number to write + * @param data data to write + * @return number of data + */ +static int +uap_hwstatus_write(struct file *f, const char *buf, unsigned long cnt, + void *data) +{ + struct net_device *netdev = data; + uap_private *priv = (uap_private *) netdev_priv(netdev); + char databuf[10]; + int hwstatus; + MODULE_GET; + if (cnt > 10) { + MODULE_PUT; + return cnt; + } + if (copy_from_user(databuf, buf, cnt)) { + MODULE_PUT; + return 0; + } + hwstatus = string_to_number(databuf); + switch (hwstatus) { + case HWReset: + PRINTM(MSG, "reset hw\n"); + uap_soft_reset(priv); + priv->adapter->HardwareStatus = HWReset; + break; + default: + break; + } + MODULE_PUT; + return cnt; +} + +/** + * @brief hwstatus proc read function + * + * @param page pointer to buffer + * @param s read data starting position + * @param off offset + * @param cnt counter + * @param eof end of file flag + * @param data data to output + * @return number of output data + */ +static int +uap_hwstatus_read(char *page, char **s, off_t off, int cnt, int *eof, + void *data) +{ + char *p = page; + struct net_device *netdev = data; + uap_private *priv = (uap_private *) netdev_priv(netdev); + MODULE_GET; + p += sprintf(p, "%d\n", priv->adapter->HardwareStatus); + MODULE_PUT; + return p - page; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief create uap proc file + * + * @param priv pointer uap_private + * @param dev pointer net_device + * @return N/A + */ +void +uap_proc_entry(uap_private * priv, struct net_device *dev) +{ + struct proc_dir_entry *r = PROC_DIR; + + PRINTM(INFO, "Creating Proc Interface\n"); + /* Check if uap directory already exists */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) + for (r = r->subdir; r; r = r->next) { + if (r->namelen && !strcmp("uap", r->name)) { + /* Directory exists */ + PRINTM(WARN, "proc directory already exists!\n"); + priv->proc_uap = r; + break; + } + } +#endif + if (!priv->proc_uap) { + priv->proc_uap = proc_mkdir("uap", PROC_DIR); + if (!priv->proc_uap) + return; + else + atomic_set(&priv->proc_uap->count, 1); + } else { + atomic_inc(&priv->proc_uap->count); + } + priv->proc_entry = proc_mkdir(dev->name, priv->proc_uap); + + if (priv->proc_entry) { + r = create_proc_read_entry("info", 0, priv->proc_entry, uap_proc_read, + dev); + r = create_proc_entry("hwstatus", 0644, priv->proc_entry); + if (r) { + r->data = dev; + r->read_proc = uap_hwstatus_read; + r->write_proc = uap_hwstatus_write; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) + r->owner = THIS_MODULE; +#endif + } else + PRINTM(MSG, "Fail to create proc hwstatus\n"); + } +} + +/** + * @brief remove proc file + * + * @param priv pointer uap_private + * @return N/A + */ +void +uap_proc_remove(uap_private * priv) +{ + if (priv->proc_uap) { + if (priv->proc_entry) { + remove_proc_entry("info", priv->proc_entry); + remove_proc_entry("hwstatus", priv->proc_entry); + } + remove_proc_entry(priv->uap_dev.netdev->name, priv->proc_uap); + atomic_dec(&priv->proc_uap->count); + if (atomic_read(&(priv->proc_uap->count)) == 0) + remove_proc_entry("uap", PROC_DIR); + } +} + +/** + * @brief convert string to number + * + * @param s pointer to numbered string + * @return converted number from string s + */ +int +string_to_number(char *s) +{ + int r = 0; + int base = 0; + int pn = 1; + + if (strncmp(s, "-", 1) == 0) { + pn = -1; + s++; + } + if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) { + base = 16; + s += 2; + } else + base = 10; + + for (s = s; *s != 0; s++) { + if ((*s >= '0') && (*s <= '9')) + r = (r * base) + (*s - '0'); + else if ((*s >= 'A') && (*s <= 'F')) + r = (r * base) + (*s - 'A' + 10); + else if ((*s >= 'a') && (*s <= 'f')) + r = (r * base) + (*s - 'a' + 10); + else + break; + } + + return (r * pn); +} + +#endif diff --git a/libertas_uap/uap_sdio_mmc.c b/libertas_uap/uap_sdio_mmc.c new file mode 100644 index 0000000..86f55b5 --- /dev/null +++ b/libertas_uap/uap_sdio_mmc.c @@ -0,0 +1,1428 @@ +/** @file uap_sdio_mmc.c + * @brief This file contains SDIO IF (interface) module + * related functions. + * + * Copyright (C) 2007-2009, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/**************************************************** +Change log: +****************************************************/ + +#include "uap_sdio_mmc.h" + +#include + +/** define SDIO block size */ +/* We support up to 480-byte block size due to FW buffer limitation. */ +#define SD_BLOCK_SIZE 256 + +/** define allocated buffer size */ +#define ALLOC_BUF_SIZE (((MAX(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, \ + MRVDRV_SIZE_OF_CMD_BUFFER) + INTF_HEADER_LEN \ + + SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE) + +/** Max retry number of CMD53 write */ +#define MAX_WRITE_IOMEM_RETRY 2 + +/******************************************************** + Local Variables +********************************************************/ + +/** SDIO Rx unit */ +static u8 sdio_rx_unit = 0; + +/**Interrupt status */ +static u8 sd_ireg = 0; +/******************************************************** + Global Variables +********************************************************/ +extern u8 *helper_name; +extern u8 *fw_name; +/** Default helper name */ +#define DEFAULT_HELPER_NAME "mrvl/helper_sd.bin" +/** Default firmware name */ +#define DEFAULT_FW_NAME "mrvl/sd8688_ap.bin" + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function reads the IO register. + * + * @param priv A pointer to uap_private structure + * @param reg register to be read + * @param dat A pointer to variable that keeps returned value + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +sbi_read_ioreg(uap_private * priv, u32 reg, u8 * dat) +{ + struct sdio_mmc_card *card; + int ret = UAP_STATUS_FAILURE; + + ENTER(); + + card = priv->uap_dev.card; + if (!card || !card->func) { + PRINTM(ERROR, "sbi_read_ioreg(): card or function is NULL!\n"); + goto done; + } + + *dat = sdio_readb(card->func, reg, &ret); + if (ret) { + PRINTM(ERROR, "sbi_read_ioreg(): sdio_readb failed! ret=%d\n", ret); + goto done; + } + + PRINTM(INFO, "sbi_read_ioreg() priv=%p func=%d reg=%#x dat=%#x\n", priv, + card->func->num, reg, *dat); + + done: + LEAVE(); + return ret; +} + +/** + * @brief This function writes the IO register. + * + * @param priv A pointer to uap_private structure + * @param reg register to be written + * @param dat the value to be written + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +sbi_write_ioreg(uap_private * priv, u32 reg, u8 dat) +{ + struct sdio_mmc_card *card; + int ret = UAP_STATUS_FAILURE; + + ENTER(); + + card = priv->uap_dev.card; + if (!card || !card->func) { + PRINTM(ERROR, "sbi_write_ioreg(): card or function is NULL!\n"); + goto done; + } + + PRINTM(INFO, "sbi_write_ioreg() priv=%p func=%d reg=%#x dat=%#x\n", priv, + card->func->num, reg, dat); + + sdio_writeb(card->func, dat, reg, &ret); + if (ret) { + PRINTM(ERROR, "sbi_write_ioreg(): sdio_readb failed! ret=%d\n", ret); + goto done; + } + + done: + LEAVE(); + return ret; +} + +/** + * @brief This function get rx_unit value + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +sd_get_rx_unit(uap_private * priv) +{ + int ret = UAP_STATUS_SUCCESS; + u8 reg; + + ENTER(); + + ret = sbi_read_ioreg(priv, CARD_RX_UNIT_REG, ®); + if (ret == UAP_STATUS_SUCCESS) + sdio_rx_unit = reg; + + LEAVE(); + return ret; +} + +/** + * @brief This function reads rx length + * + * @param priv A pointer to uap_private structure + * @param dat A pointer to keep returned data + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +sd_read_rx_len(uap_private * priv, u16 * dat) +{ + int ret = UAP_STATUS_SUCCESS; + u8 reg; + + ENTER(); + + ret = sbi_read_ioreg(priv, CARD_RX_LEN_REG, ®); + if (ret == UAP_STATUS_SUCCESS) + *dat = (u16) reg << sdio_rx_unit; + + LEAVE(); + return ret; +} + +/** + * @brief This function reads fw status registers + * + * @param priv A pointer to uap_private structure + * @param dat A pointer to keep returned data + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +sd_read_firmware_status(uap_private * priv, u16 * dat) +{ + int ret = UAP_STATUS_SUCCESS; + u8 fws0; + u8 fws1; + + ENTER(); + + ret = sbi_read_ioreg(priv, CARD_FW_STATUS0_REG, &fws0); + if (ret < 0) { + LEAVE(); + return UAP_STATUS_FAILURE; + } + + ret = sbi_read_ioreg(priv, CARD_FW_STATUS1_REG, &fws1); + if (ret < 0) { + LEAVE(); + return UAP_STATUS_FAILURE; + } + + *dat = (((u16) fws1) << 8) | fws0; + + LEAVE(); + return UAP_STATUS_SUCCESS; +} + +/** + * @brief This function polls the card status register. + * + * @param priv A pointer to uap_private structure + * @param bits the bit mask + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +mv_sdio_poll_card_status(uap_private * priv, u8 bits) +{ + int tries; + u8 cs; + + ENTER(); + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + if (sbi_read_ioreg(priv, CARD_STATUS_REG, &cs) < 0) + break; + else if ((cs & bits) == bits) { + LEAVE(); + return UAP_STATUS_SUCCESS; + } + udelay(10); + } + + PRINTM(WARN, "mv_sdio_poll_card_status failed, tries = %d\n", tries); + + LEAVE(); + return UAP_STATUS_FAILURE; +} + +/** + * @brief This function set the sdio bus width. + * + * @param priv A pointer to uap_private structure + * @param mode 1--1 bit mode, 4--4 bit mode + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +#if 0 +static int +sdio_set_bus_width(uap_private * priv, u8 mode) +{ + ENTER(); + LEAVE(); + return UAP_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function reads data from the card. + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +sd_card_to_host(uap_private * priv) +{ + int ret = UAP_STATUS_SUCCESS; + u16 buf_len = 0; + int buf_block_len; + int blksz; + struct sk_buff *skb = NULL; + u16 type; + u8 *payload = NULL; + struct sdio_mmc_card *card = priv->uap_dev.card; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "card or function is NULL!\n"); + ret = UAP_STATUS_FAILURE; + goto exit; + } + + /* Read the length of data to be transferred */ + ret = sd_read_rx_len(priv, &buf_len); + if (ret < 0) { + PRINTM(ERROR, "card_to_host, read scratch reg failed\n"); + ret = UAP_STATUS_FAILURE; + goto exit; + } + + /* Allocate buffer */ + blksz = SD_BLOCK_SIZE; + buf_block_len = (buf_len + blksz - 1) / blksz; + if (buf_len <= INTF_HEADER_LEN || (buf_block_len * blksz) > ALLOC_BUF_SIZE) { + PRINTM(ERROR, "card_to_host, invalid packet length: %d\n", buf_len); + ret = UAP_STATUS_FAILURE; + goto exit; + } +#ifdef PXA3XX_DMA_ALIGN + skb = dev_alloc_skb(buf_block_len * blksz + PXA3XX_DMA_ALIGNMENT); +#else + skb = dev_alloc_skb(buf_block_len * blksz); +#endif + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + goto exit; + } +#ifdef PXA3XX_DMA_ALIGN + if ((u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)) { + skb_put(skb, (u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)); + skb_pull(skb, (u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)); + } +#endif /* PXA3XX_DMA_ALIGN */ + + payload = skb->tail; + ret = sdio_readsb(card->func, payload, priv->uap_dev.ioport, + buf_block_len * blksz); + if (ret < 0) { + PRINTM(ERROR, "card_to_host, read iomem failed: %d\n", ret); + ret = UAP_STATUS_FAILURE; + goto exit; + } + HEXDUMP("SDIO Blk Rd", payload, blksz * buf_block_len); + /* + * This is SDIO specific header + * u16 length, + * u16 type (MV_TYPE_DAT = 0, MV_TYPE_CMD = 1, MV_TYPE_EVENT = 3) + */ + buf_len = uap_le16_to_cpu(*(u16 *) & payload[0]); + type = uap_le16_to_cpu(*(u16 *) & payload[2]); + switch (type) { + case MV_TYPE_EVENT: + skb_put(skb, buf_len); + skb_pull(skb, INTF_HEADER_LEN); + uap_process_event(priv, skb->data, skb->len); + kfree_skb(skb); + skb = NULL; + break; + case MV_TYPE_CMD: + skb_put(skb, buf_len); + skb_pull(skb, INTF_HEADER_LEN); + priv->adapter->cmd_pending = FALSE; + if (priv->adapter->cmd_wait_option == + HostCmd_OPTION_WAITFORRSP_SLEEPCONFIRM) { + priv->adapter->cmd_wait_option = FALSE; + uap_process_sleep_confirm_resp(priv, skb->data, skb->len); + } else if (priv->adapter->cmd_wait_option) { + memcpy(priv->adapter->CmdBuf, skb->data, skb->len); + priv->adapter->CmdSize = skb->len; + priv->adapter->cmd_wait_option = FALSE; + priv->adapter->CmdWaitQWoken = TRUE; + wake_up_interruptible(&priv->adapter->cmdwait_q); + } + kfree_skb(skb); + skb = NULL; + break; + case MV_TYPE_DAT: + skb_put(skb, buf_len); + skb_pull(skb, INTF_HEADER_LEN); + uap_process_rx_packet(priv, skb); + break; + default: + priv->stats.rx_errors++; + priv->stats.rx_dropped++; + /* Driver specified event and command resp should be handle here */ + PRINTM(INFO, "Unknown PKT type:%d\n", type); + kfree_skb(skb); + skb = NULL; + break; + } + exit: + if (ret) { + if (skb) + kfree_skb(skb); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function enables the host interrupts mask + * + * @param priv A pointer to uap_private structure + * @param mask the interrupt mask + * @return UAP_STATUS_SUCCESS + */ +static int +enable_host_int_mask(uap_private * priv, u8 mask) +{ + int ret = UAP_STATUS_SUCCESS; + + ENTER(); + + /* Simply write the mask to the register */ + ret = sbi_write_ioreg(priv, HOST_INT_MASK_REG, mask); + + if (ret) { + PRINTM(WARN, "Unable to enable the host interrupt!\n"); + ret = UAP_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** @brief This function disables the host interrupts mask. + * + * @param priv A pointer to uap_private structure + * @param mask the interrupt mask + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +disable_host_int_mask(uap_private * priv, u8 mask) +{ + int ret = UAP_STATUS_SUCCESS; + u8 host_int_mask; + + ENTER(); + + /* Read back the host_int_mask register */ + ret = sbi_read_ioreg(priv, HOST_INT_MASK_REG, &host_int_mask); + if (ret) { + ret = UAP_STATUS_FAILURE; + goto done; + } + + /* Update with the mask and write back to the register */ + host_int_mask &= ~mask; + ret = sbi_write_ioreg(priv, HOST_INT_MASK_REG, host_int_mask); + if (ret < 0) { + PRINTM(WARN, "Unable to diable the host interrupt!\n"); + ret = UAP_STATUS_FAILURE; + goto done; + } + + done: + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function handles the interrupt. + * + * @param func A pointer to sdio_func structure. + * @return n/a + */ +static void +sbi_interrupt(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + uap_private *priv; + u8 ireg = 0; + int ret = UAP_STATUS_SUCCESS; + + ENTER(); + + card = sdio_get_drvdata(func); + if (!card || !card->priv) { + PRINTM(MSG, "%s: sbi_interrupt(%p) card or priv is NULL, card=%p\n", + __FUNCTION__, func, card); + LEAVE(); + return; + } + priv = card->priv; +#ifdef FW_WAKEUP_TIME + if ((priv->adapter->wt_pwrup_sending != 0L) && + (priv->adapter->wt_int == 0L)) + priv->adapter->wt_int = get_utimeofday(); +#endif + + ireg = sdio_readb(card->func, HOST_INTSTATUS_REG, &ret); + if (ret) { + PRINTM(WARN, "sdio_read_ioreg: read int status register failed\n"); + goto done; + } + if (ireg != 0) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * Clear the interrupt status register and re-enable the interrupt + */ + PRINTM(INFO, "sdio_ireg = 0x%x\n", ireg); + sdio_writeb(card->func, + ~(ireg) & (DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS), + HOST_INTSTATUS_REG, &ret); + if (ret) { + PRINTM(WARN, + "sdio_write_ioreg: clear int status register failed\n"); + goto done; + } + } + OS_INT_DISABLE; + sd_ireg |= ireg; + OS_INT_RESTORE; + + uap_interrupt(priv); + done: + LEAVE(); +} + +/** + * @brief This function probe the card + * + * @param func A pointer to sdio_func structure + * @param id A pointer to structure sd_device_id + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +static int +uap_probe(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret = UAP_STATUS_FAILURE; + struct sdio_mmc_card *card = NULL; + + ENTER(); + + PRINTM(MSG, "%s: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", + __FUNCTION__, func->vendor, func->device, func->class, func->num); + + card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); + if (!card) { + ret = -ENOMEM; + goto done; + } + + card->func = func; + + if (!uap_add_card(card)) { + PRINTM(ERROR, "%s: uap_add_callback failed\n", __FUNCTION__); + kfree(card); + ret = UAP_STATUS_FAILURE; + goto done; + } + + ret = UAP_STATUS_SUCCESS; + + done: + LEAVE(); + return ret; +} + +/** + * @brief This function removes the card + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +uap_remove(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + + ENTER(); + + if (func) { + card = sdio_get_drvdata(func); + if (card) { + uap_remove_card(card); + kfree(card); + } + } + + LEAVE(); +} + +#ifdef CONFIG_PM +/** + * @brief This function handles client driver suspend + * + * @param func A pointer to sdio_func structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +uap_suspend(struct sdio_func *func) +{ + ENTER(); + LEAVE(); + return 0; +} + +/** + * @brief This function handles client driver resume + * + * @param func A pointer to sdio_func structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +uap_resume(struct sdio_func *func) +{ + ENTER(); + LEAVE(); + return 0; +} +#endif + +/** Device ID for SD8688 */ +#define SD_DEVICE_ID_8688_UAP 0x9104 +/** UAP IDs */ +static const struct sdio_device_id uap_ids[] = { + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SD_DEVICE_ID_8688_UAP)}, + {}, +}; + +MODULE_DEVICE_TABLE(sdio, uap_ids); + +static struct sdio_driver uap_sdio = { + .name = "uap_sdio", + .id_table = uap_ids, + .probe = uap_probe, + .remove = uap_remove, +#ifdef CONFIG_PM +/* .suspend = uap_suspend, */ +/* .resume = uap_resume, */ +#endif + +}; + +/** + * @brief This function registers the IF module in bus driver. + * + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int __init +sbi_register() +{ + int ret = UAP_STATUS_SUCCESS; + + ENTER(); + + /* SDIO Driver Registration */ + if (sdio_register_driver(&uap_sdio) != 0) { + PRINTM(FATAL, "SDIO Driver Registration Failed \n"); + ret = UAP_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function de-registers the IF module in bus driver. + * + * @return n/a + */ +void __exit +sbi_unregister(void) +{ + ENTER(); + + /* SDIO Driver Unregistration */ + sdio_unregister_driver(&uap_sdio); + + LEAVE(); +} + +/** + * @brief This function checks the interrupt status and handle it accordingly. + * + * @param priv A pointer to uap_private structure + * @param ireg A pointer to variable that keeps returned value + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +sbi_get_int_status(uap_private * priv, u8 * ireg) +{ + int ret = UAP_STATUS_SUCCESS; + u8 sdio_ireg = 0; + struct sdio_mmc_card *card = priv->uap_dev.card; + + ENTER(); + + *ireg = 0; + OS_INT_DISABLE; + sdio_ireg = sd_ireg; + sd_ireg = 0; + OS_INT_RESTORE; + + sdio_claim_host(card->func); + + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { /* tx_done INT */ + if (!priv->uap_dev.cmd_sent) { /* tx_done already received */ + PRINTM(INFO, + "warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n", + priv->uap_dev.cmd_sent, sdio_ireg); + } else { + priv->uap_dev.cmd_sent = FALSE; + priv->uap_dev.data_sent = FALSE; + if ( (priv->uap_dev.netdev->reg_state == NETREG_REGISTERED) && (skb_queue_len(&priv->adapter->tx_queue) < TX_LOW_WATERMARK)) { + os_start_queue(priv); + } + } + } + if (sdio_ireg & UP_LD_HOST_INT_STATUS) { + sd_card_to_host(priv); + } + + *ireg = sdio_ireg; + ret = UAP_STATUS_SUCCESS; + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function disables the host interrupts. + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +sbi_disable_host_int(uap_private * priv) +{ + struct sdio_mmc_card *card = priv->uap_dev.card; + int ret; + + ENTER(); + + sdio_claim_host(card->func); + ret = disable_host_int_mask(priv, HIM_DISABLE); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function enables the host interrupts. + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS + */ +int +sbi_enable_host_int(uap_private * priv) +{ + struct sdio_mmc_card *card = priv->uap_dev.card; + int ret; + + ENTER(); + + sdio_claim_host(card->func); + ret = enable_host_int_mask(priv, HIM_ENABLE); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function de-registers the device. + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS + */ +int +sbi_unregister_dev(uap_private * priv) +{ + struct sdio_mmc_card *card = priv->uap_dev.card; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "Error: card or function is NULL!\n"); + goto done; + } + + sdio_claim_host(card->func); + sdio_release_irq(card->func); + sdio_disable_func(card->func); + sdio_release_host(card->func); + + sdio_set_drvdata(card->func, NULL); + + done: + LEAVE(); + return UAP_STATUS_SUCCESS; +} + +/** + * @brief This function registers the device. + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +sbi_register_dev(uap_private * priv) +{ + int ret = UAP_STATUS_FAILURE; + u8 reg; + struct sdio_mmc_card *card = priv->uap_dev.card; + struct sdio_func *func; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "Error: card or function is NULL!\n"); + goto done; + } + + func = card->func; + + /* Initialize the private structure */ + priv->uap_dev.ioport = 0; + + sdio_claim_host(func); + + ret = sdio_enable_func(func); + if (ret) { + PRINTM(FATAL, "sdio_enable_func() failed: ret=%d\n", ret); + goto release_host; + } + + ret = sdio_claim_irq(func, sbi_interrupt); + if (ret) { + PRINTM(FATAL, "sdio_claim_irq failed: ret=%d\n", ret); + goto disable_func; + } + + /* Read the IO port */ + ret = sbi_read_ioreg(priv, IO_PORT_0_REG, ®); + if (ret) + goto release_irq; + else + priv->uap_dev.ioport |= reg; + + ret = sbi_read_ioreg(priv, IO_PORT_1_REG, ®); + if (ret) + goto release_irq; + else + priv->uap_dev.ioport |= (reg << 8); + + ret = sbi_read_ioreg(priv, IO_PORT_2_REG, ®); + if (ret) + goto release_irq; + else + priv->uap_dev.ioport |= (reg << 16); + + PRINTM(INFO, "SDIO FUNC #%d IO port: 0x%x\n", func->num, + priv->uap_dev.ioport); + + ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE); + if (ret) { + PRINTM(ERROR, "%s: cannot set SDIO block size\n", __FUNCTION__); + ret = UAP_STATUS_FAILURE; + goto release_irq; + } + priv->hotplug_device = &func->dev; + + if (helper_name == NULL) { + helper_name = DEFAULT_HELPER_NAME; + } + if (fw_name == NULL) { + fw_name = DEFAULT_FW_NAME; + } + sdio_release_host(func); + + sdio_set_drvdata(func, card); + + ret = UAP_STATUS_SUCCESS; + goto done; + + release_irq: + sdio_release_irq(func); + disable_func: + sdio_disable_func(func); + release_host: + sdio_release_host(func); + + done: + LEAVE(); + return ret; +} + +/** + * @brief This function sends data to the card. + * + * @param priv A pointer to uap_private structure + * @param payload A pointer to the data/cmd buffer + * @param nb the length of data/cmd + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +sbi_host_to_card(uap_private * priv, u8 * payload, u16 nb) +{ + struct sdio_mmc_card *card = priv->uap_dev.card; + int ret = UAP_STATUS_SUCCESS; + int buf_block_len; + int blksz; + int i = 0; + u8 *buf = NULL; +#ifdef PXA3XX_DMA_ALIGN + void *tmpbuf = NULL; + int tmpbufsz; +#endif + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "card or function is NULL!\n"); + LEAVE(); + return UAP_STATUS_FAILURE; + } + buf = payload; +#ifdef PXA3XX_DMA_ALIGN + if ((u32) payload & (PXA3XX_DMA_ALIGNMENT - 1)) { + tmpbufsz = ALIGN_SZ(nb, PXA3XX_DMA_ALIGNMENT); + tmpbuf = kmalloc(tmpbufsz, GFP_KERNEL); + memset(tmpbuf, 0, tmpbufsz); + /* Ensure 8-byte aligned CMD buffer */ + buf = (u8 *) ALIGN_ADDR(tmpbuf, PXA3XX_DMA_ALIGNMENT); + memcpy(buf, payload, nb); + } +#endif + /* Allocate buffer and copy payload */ + blksz = SD_BLOCK_SIZE; + buf_block_len = (nb + blksz - 1) / blksz; + sdio_claim_host(card->func); +#define MAX_WRITE_IOMEM_RETRY 2 + priv->uap_dev.cmd_sent = TRUE; + priv->uap_dev.data_sent = TRUE; + do { + /* Transfer data to card */ + ret = sdio_writesb(card->func, priv->uap_dev.ioport, buf, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, "host_to_card, write iomem (%d) failed: %d\n", i, + ret); + ret = UAP_STATUS_FAILURE; + if (i > MAX_WRITE_IOMEM_RETRY) + goto exit; + } else { + HEXDUMP("SDIO Blk Wr", payload, nb); + } + } while (ret == UAP_STATUS_FAILURE); + exit: + sdio_release_host(card->func); +#ifdef PXA3XX_DMA_ALIGN + if (tmpbuf) + kfree(tmpbuf); +#endif + if (ret == UAP_STATUS_FAILURE) { + priv->uap_dev.cmd_sent = FALSE; + priv->uap_dev.data_sent = FALSE; + } + LEAVE(); + return ret; +} + +/** + * @brief This function reads CIS information. + * + * @param priv A pointer to uap_private structure + * @param cisinfo A pointer to CIS information output buffer + * @param cislen A pointer to length of CIS info output buffer + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +#if 0 +static int +sbi_get_cis_info(uap_private * priv, void *cisinfo, int *cislen) +{ +#define CIS_PTR (0x8000) + struct sdio_mmc_card *card = priv->uap_dev.card; + unsigned int i, cis_ptr = CIS_PTR; + int ret = UAP_STATUS_FAILURE; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "sbi_get_cis_info(): card or function is NULL!\n"); + goto exit; + } +#define MAX_SDIO_CIS_INFO_LEN (256) + if (!cisinfo || (*cislen < MAX_SDIO_CIS_INFO_LEN)) { + PRINTM(WARN, "ERROR! get_cis_info: insufficient buffer passed\n"); + goto exit; + } + + *cislen = MAX_SDIO_CIS_INFO_LEN; + + sdio_claim_host(card->func); + + PRINTM(INFO, "cis_ptr=%#x\n", cis_ptr); + + /* Read the Tuple Data */ + for (i = 0; i < *cislen; i++) { + ((unsigned char *) cisinfo)[i] = + sdio_readb(card->func, cis_ptr + i, &ret); + if (ret) { + PRINTM(WARN, "get_cis_info error: ret=%d\n", ret); + ret = UAP_STATUS_FAILURE; + goto done; + } + PRINTM(INFO, "cisinfo[%d]=%#x\n", i, ((unsigned char *) cisinfo)[i]); + } + + done: + sdio_release_host(card->func); + exit: + LEAVE(); + return ret; +} +#endif +/** + * @brief This function downloads helper image to the card. + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +sbi_prog_helper(uap_private * priv) +{ + struct sdio_mmc_card *card = priv->uap_dev.card; + u8 *helper = NULL; + int helperlen; + int ret = UAP_STATUS_SUCCESS; + void *tmphlprbuf = NULL; + int tmphlprbufsz; + u8 *hlprbuf; + int hlprblknow; + u32 tx_len; +#ifdef FW_DOWNLOAD_SPEED + u32 tv1, tv2; +#endif + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "sbi_prog_helper(): card or function is NULL!\n"); + goto done; + } + + if (priv->fw_helper) { + helper = (u8 *) priv->fw_helper->data; + helperlen = priv->fw_helper->size; + } else { + PRINTM(MSG, "No helper image found! Terminating download.\n"); + LEAVE(); + return UAP_STATUS_FAILURE; + } + + PRINTM(INFO, "Downloading helper image (%d bytes), block size %d bytes\n", + helperlen, SD_BLOCK_SIZE); + +#ifdef FW_DOWNLOAD_SPEED + tv1 = get_utimeofday(); +#endif + +#ifdef PXA3XX_DMA_ALIGN + tmphlprbufsz = ALIGN_SZ(UAP_UPLD_SIZE, PXA3XX_DMA_ALIGNMENT); +#else /* !PXA3XX_DMA_ALIGN */ + tmphlprbufsz = UAP_UPLD_SIZE; +#endif /* !PXA3XX_DMA_ALIGN */ + tmphlprbuf = kmalloc(tmphlprbufsz, GFP_KERNEL); + if (!tmphlprbuf) { + PRINTM(ERROR, + "Unable to allocate buffer for helper. Terminating download\n"); + ret = UAP_STATUS_FAILURE; + goto done; + } + memset(tmphlprbuf, 0, tmphlprbufsz); +#ifdef PXA3XX_DMA_ALIGN + hlprbuf = (u8 *) ALIGN_ADDR(tmphlprbuf, PXA3XX_DMA_ALIGNMENT); +#else /* !PXA3XX_DMA_ALIGN */ + hlprbuf = (u8 *) tmphlprbuf; +#endif /* !PXA3XX_DMA_ALIGN */ + + sdio_claim_host(card->func); + + /* Perform helper data transfer */ + tx_len = (FIRMWARE_TRANSFER_NBLOCK * SD_BLOCK_SIZE) - INTF_HEADER_LEN; + hlprblknow = 0; + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ + ret = mv_sdio_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); + if (ret < 0) { + PRINTM(FATAL, "Helper download poll status timeout @ %d\n", + hlprblknow); + goto done; + } + + /* More data? */ + if (hlprblknow >= helperlen) + break; + + /* Set blocksize to transfer - checking for last block */ + if (helperlen - hlprblknow < tx_len) + tx_len = helperlen - hlprblknow; + + /* Set length to the 4-byte header */ + *(u32 *) hlprbuf = uap_cpu_to_le32(tx_len); + + /* Copy payload to buffer */ + memcpy(&hlprbuf[INTF_HEADER_LEN], &helper[hlprblknow], tx_len); + + PRINTM(INFO, "."); + + /* Send data */ + ret = sdio_writesb(card->func, priv->uap_dev.ioport, + hlprbuf, FIRMWARE_TRANSFER_NBLOCK * SD_BLOCK_SIZE); + + if (ret < 0) { + PRINTM(FATAL, "IO error during helper download @ %d\n", hlprblknow); + goto done; + } + + hlprblknow += tx_len; + } while (TRUE); + +#ifdef FW_DOWNLOAD_SPEED + tv2 = get_utimeofday(); + PRINTM(INFO, "helper: %ld.%03ld.%03ld ", tv1 / 1000000, + (tv1 % 1000000) / 1000, tv1 % 1000); + PRINTM(INFO, " -> %ld.%03ld.%03ld ", tv2 / 1000000, (tv2 % 1000000) / 1000, + tv2 % 1000); + tv2 -= tv1; + PRINTM(INFO, " == %ld.%03ld.%03ld\n", tv2 / 1000000, (tv2 % 1000000) / 1000, + tv2 % 1000); +#endif + + /* Write last EOF data */ + PRINTM(INFO, "\nTransferring helper image EOF block\n"); + memset(hlprbuf, 0x0, SD_BLOCK_SIZE); + ret = sdio_writesb(card->func, priv->uap_dev.ioport, + hlprbuf, SD_BLOCK_SIZE); + + if (ret < 0) { + PRINTM(FATAL, "IO error in writing helper image EOF block\n"); + goto done; + } + + ret = UAP_STATUS_SUCCESS; + + done: + sdio_release_host(card->func); + if (tmphlprbuf) + kfree(tmphlprbuf); + + LEAVE(); + return ret; +} + +/** + * @brief This function downloads firmware image to the card. + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +sbi_prog_fw_w_helper(uap_private * priv) +{ + struct sdio_mmc_card *card = priv->uap_dev.card; + u8 *firmware = NULL; + int firmwarelen; + u8 base0; + u8 base1; + int ret = UAP_STATUS_SUCCESS; + int offset; + void *tmpfwbuf = NULL; + int tmpfwbufsz; + u8 *fwbuf; + u16 len; + int txlen = 0; + int tx_blocks = 0; + int i = 0; + int tries = 0; +#ifdef FW_DOWNLOAD_SPEED + u32 tv1, tv2; +#endif + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "sbi_prog_fw_w_helper(): card or function is NULL!\n"); + goto done; + } + + if (priv->firmware) { + firmware = (u8 *) priv->firmware->data; + firmwarelen = priv->firmware->size; + } else { + PRINTM(MSG, "No firmware image found! Terminating download.\n"); + LEAVE(); + return UAP_STATUS_FAILURE; + } + + PRINTM(INFO, "Downloading FW image (%d bytes)\n", firmwarelen); + +#ifdef FW_DOWNLOAD_SPEED + tv1 = get_utimeofday(); +#endif + +#ifdef PXA3XX_DMA_ALIGN + tmpfwbufsz = ALIGN_SZ(UAP_UPLD_SIZE, PXA3XX_DMA_ALIGNMENT); +#else /* PXA3XX_DMA_ALIGN */ + tmpfwbufsz = UAP_UPLD_SIZE; +#endif /* PXA3XX_DMA_ALIGN */ + tmpfwbuf = kmalloc(tmpfwbufsz, GFP_KERNEL); + if (!tmpfwbuf) { + PRINTM(ERROR, + "Unable to allocate buffer for firmware. Terminating download.\n"); + ret = UAP_STATUS_FAILURE; + goto done; + } + memset(tmpfwbuf, 0, tmpfwbufsz); +#ifdef PXA3XX_DMA_ALIGN + /* Ensure 8-byte aligned firmware buffer */ + fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, PXA3XX_DMA_ALIGNMENT); +#else /* PXA3XX_DMA_ALIGN */ + fwbuf = (u8 *) tmpfwbuf; +#endif /* PXA3XX_DMA_ALIGN */ + + sdio_claim_host(card->func); + + /* Perform firmware data transfer */ + offset = 0; + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ + ret = mv_sdio_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); + if (ret < 0) { + PRINTM(FATAL, "FW download with helper poll status timeout @ %d\n", + offset); + goto done; + } + + /* More data? */ + if (offset >= firmwarelen) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + if ((ret = sbi_read_ioreg(priv, HOST_F1_RD_BASE_0, &base0)) < 0) { + PRINTM(WARN, "Dev BASE0 register read failed:" + " base0=0x%04X(%d). Terminating download.\n", base0, + base0); + ret = UAP_STATUS_FAILURE; + goto done; + } + if ((ret = sbi_read_ioreg(priv, HOST_F1_RD_BASE_1, &base1)) < 0) { + PRINTM(WARN, "Dev BASE1 register read failed:" + " base1=0x%04X(%d). Terminating download.\n", base1, + base1); + ret = UAP_STATUS_FAILURE; + goto done; + } + len = (((u16) base1) << 8) | base0; + + /* For SD8688 wait until the length is not 0, 1 or 2 before + downloading the first FW block, since BOOT code writes the + register to indicate the helper/FW download winner, the value + could be 1 or 2 (Func1 or Func2). */ + if ((len && offset) || (len > 2)) + break; + udelay(10); + } + + if (len == 0) + break; + else if (len > UAP_UPLD_SIZE) { + PRINTM(FATAL, "FW download failure @ %d, invalid length %d\n", + offset, len); + ret = UAP_STATUS_FAILURE; + goto done; + } + + txlen = len; + + if (len & BIT(0)) { + i++; + if (i > MAX_WRITE_IOMEM_RETRY) { + PRINTM(FATAL, + "FW download failure @ %d, over max retry count\n", + offset); + ret = UAP_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, "FW CRC error indicated by the helper:" + " len = 0x%04X, txlen = %d\n", len, txlen); + len &= ~BIT(0); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking for last block */ + if (firmwarelen - offset < txlen) { + txlen = firmwarelen - offset; + } + PRINTM(INFO, "."); + + tx_blocks = (txlen + SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE; + + /* Copy payload to buffer */ + memcpy(fwbuf, &firmware[offset], txlen); + } + + /* Send data */ + ret = sdio_writesb(card->func, priv->uap_dev.ioport, + fwbuf, tx_blocks * SD_BLOCK_SIZE); + + if (ret < 0) { + PRINTM(ERROR, "FW download, write iomem (%d) failed @ %d\n", i, + offset); + if (sbi_write_ioreg(priv, CONFIGURATION_REG, 0x04) < 0) { + PRINTM(ERROR, "write ioreg failed (CFG)\n"); + } + } + + offset += txlen; + } while (TRUE); + + PRINTM(INFO, "\nFW download over, size %d bytes\n", offset); + + ret = UAP_STATUS_SUCCESS; + done: +#ifdef FW_DOWNLOAD_SPEED + tv2 = get_utimeofday(); + PRINTM(INFO, "FW: %ld.%03ld.%03ld ", tv1 / 1000000, + (tv1 % 1000000) / 1000, tv1 % 1000); + PRINTM(INFO, " -> %ld.%03ld.%03ld ", tv2 / 1000000, + (tv2 % 1000000) / 1000, tv2 % 1000); + tv2 -= tv1; + PRINTM(INFO, " == %ld.%03ld.%03ld\n", tv2 / 1000000, + (tv2 % 1000000) / 1000, tv2 % 1000); +#endif + sdio_release_host(card->func); + if (tmpfwbuf) + kfree(tmpfwbuf); + + LEAVE(); + return ret; +} + +/** + * @brief This function checks if the firmware is ready to accept + * command or not. + * + * @param priv A pointer to uap_private structure + * @param pollnum Poll number + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +sbi_check_fw_status(uap_private * priv, int pollnum) +{ + struct sdio_mmc_card *card = priv->uap_dev.card; + int ret = UAP_STATUS_SUCCESS; + u16 firmwarestat; + int tries; + + ENTER(); + + sdio_claim_host(card->func); + + /* Wait for firmware initialization event */ + for (tries = 0; tries < pollnum; tries++) { + if ((ret = sd_read_firmware_status(priv, &firmwarestat)) < 0) + continue; + if (firmwarestat == FIRMWARE_READY) { + ret = UAP_STATUS_SUCCESS; + break; + } else { + mdelay(10); + ret = UAP_STATUS_FAILURE; + } + } + + if (ret < 0) + goto done; + + ret = UAP_STATUS_SUCCESS; + sd_get_rx_unit(priv); + + done: + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function set bus clock on/off + * + * @param priv A pointer to uap_private structure + * @param option TRUE--on , FALSE--off + * @return UAP_STATUS_SUCCESS + */ +#if 0 +static int +sbi_set_bus_clock(uap_private * priv, u8 option) +{ + ENTER(); + LEAVE(); + return UAP_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function wakeup firmware + * + * @param priv A pointer to uap_private structure + * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE + */ +int +sbi_wakeup_firmware(uap_private * priv) +{ + struct sdio_mmc_card *card = priv->uap_dev.card; + int ret = UAP_STATUS_SUCCESS; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "card or function is NULL!\n"); + LEAVE(); + return UAP_STATUS_FAILURE; + } + sdio_claim_host(card->func); + sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); + sdio_release_host(card->func); + LEAVE(); + return ret; +} diff --git a/libertas_uap/uap_sdio_mmc.h b/libertas_uap/uap_sdio_mmc.h new file mode 100644 index 0000000..191c67f --- /dev/null +++ b/libertas_uap/uap_sdio_mmc.h @@ -0,0 +1,136 @@ +/** @file uap_sdio_mmc.h + * @brief This file contains SDIO IF (interface) module + * related macros, enum, and structure. + * + * Copyright (C) 2007-2009, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/**************************************************** +Change log: + 10/10/07: initial version +****************************************************/ + +#ifndef _UAP_SDIO_MMC_H +#define _UAP_SDIO_MMC_H + +#include +#include +#include +#include + +#include "uap_headers.h" + +/** The number of times to try when waiting for downloaded firmware to + become active. (polling the scratch register). */ +#define MAX_FIRMWARE_POLL_TRIES 100 + +/** Firmware ready */ +#define FIRMWARE_READY 0xfedc + +/** Number of firmware blocks to transfer */ +#define FIRMWARE_TRANSFER_NBLOCK 2 + +/* Host Control Registers */ +/** Host Control Registers : I/O port 0 */ +#define IO_PORT_0_REG 0x00 +/** Host Control Registers : I/O port 1 */ +#define IO_PORT_1_REG 0x01 +/** Host Control Registers : I/O port 2 */ +#define IO_PORT_2_REG 0x02 + +/** Host Control Registers : Configuration */ +#define CONFIGURATION_REG 0x03 +/** Host Control Registers : Host without Command 53 finish host */ +#define HOST_WO_CMD53_FINISH_HOST (0x1U << 2) +/** Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) +/** Host Control Registers : Host power down */ +#define HOST_POWER_DOWN (0x1U << 0) + +/** Host Control Registers : Host interrupt mask */ +#define HOST_INT_MASK_REG 0x04 +/** Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/** Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) +/** Enable Host interrupt mask */ +#define HIM_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) +/** Disable Host interrupt mask */ +#define HIM_DISABLE 0xff + +/** Host Control Registers : Host interrupt status */ +#define HOST_INTSTATUS_REG 0x05 +/** Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/** Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/** Host F1 read base 0 */ +#define HOST_F1_RD_BASE_0 0x10 +/** Host F1 read base 1 */ +#define HOST_F1_RD_BASE_1 0x11 + +/** Card Control Registers : Card status register */ +#define CARD_STATUS_REG 0x20 +/** Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/** Card Control Registers : CIS card ready */ +#define CIS_CARD_RDY (0x1U << 2) +/** Card Control Registers : Upload card ready */ +#define UP_LD_CARD_RDY (0x1U << 1) +/** Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/** Card Control Registers : Card OCR 0 register */ +#define CARD_OCR_0_REG 0x34 +/** Card Control Registers : Card OCR 1 register */ +#define CARD_OCR_1_REG 0x35 + +/** Firmware status 0 register */ +#define CARD_FW_STATUS0_REG 0x40 +/** Firmware status 1 register */ +#define CARD_FW_STATUS1_REG 0x41 +/** Rx length register */ +#define CARD_RX_LEN_REG 0x42 +/** Rx unit register */ +#define CARD_RX_UNIT_REG 0x43 + +/** Chip Id Register 0 */ +#define CARD_CHIP_ID_0_REG 0x801c +/** Chip Id Register 1 */ +#define CARD_CHIP_ID_1_REG 0x801d + +#ifdef PXA3XX_DMA_ALIGN +/** DMA alignment value for PXA3XX platforms */ +#define PXA3XX_DMA_ALIGNMENT 8 +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) \ + (((p) + ((a) - 1)) & ~((a) - 1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((u32)(p)) + (((u32)(a)) - 1)) & ~(((u32)(a)) - 1)) +#endif /* PXA3XX_DMA_ALIGN */ + +struct sdio_mmc_card +{ + /** sdio_func structure pointer */ + struct sdio_func *func; + /** uap_private structure pointer */ + uap_private *priv; +}; + +#endif /* _UAP_SDIO_MMC_H */ -- cgit v1.2.3