blob: f111cde9ab2bc7d6ead5bf0015a98fe378be449a [file] [log] [blame]
/*
* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
#ifndef _ATH_LINUX_OSDEP_ADF_H
#define _ATH_LINUX_OSDEP_ADF_H
#include "wlan_opts.h"
#include <adf_os_mem.h>
#include <adf_os_lock.h>
#include <adf_os_time.h>
#include <adf_os_timer.h>
#include <adf_os_io.h>
#include <adf_os_module.h>
#include <adf_os_defer.h>
#include <net/sch_generic.h>
#include <net/inet_ecn.h>
#include <linux/netdevice.h>
#if defined(HIF_USB)
#include <linux/usb.h>
#else
#include <linux/pci.h>
#endif
#include <linux/stddef.h>
#include <linux/err.h>
#include <asm/checksum.h>
#include <net/ip6_checksum.h>
#include <linux/crc32.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <adf_nbuf.h>
#include <sys/queue.h>
#include <linux/if_vlan.h>
#include "if_upperproto.h"
#include "ah_osdep.h"
#ifdef AR9100
#include <ar9100.h>
#endif /* AR9100 */
#define INLINE __adf_os_inline
/* UNREFERENCED_PARAMETER - provide a dummy reference */
#define UNREFERENCED_PARAMETER(an) ((void) (an))
#if ATH_DEBUG
#ifndef ASSERT
#define ASSERT(expr) adf_os_assert(expr)
#endif
#else
#define ASSERT(expr)
#endif /* ATH_DEBUG */
#define OS_LOG_DBGPRINT(_xfmt, ...)
#ifdef AR9100
/*
* Howl needs DDR FIFO flush before any desc/dma data can be read.
*/
#define ATH_FLUSH_FIFO ar9100_flush_wmac
#else
#define ATH_FLUSH_FIFO()
#endif
#define KASSERT(exp, msg) do { \
if (unlikely(!(exp))) { \
printk msg; \
BUG(); \
} \
} while (0)
/*
* Map Linux spin locks to OS independent names
*/
#ifdef ANDROID
static inline void spin_lock_dpc(spinlock_t *lock)
{
adf_os_spin_lock_bh((adf_os_spinlock_t *)lock);
}
static inline void spin_unlock_dpc(spinlock_t *lock)
{
adf_os_spin_unlock_bh((adf_os_spinlock_t *)lock);
}
#else
#define spin_lock_dpc(a) adf_os_spin_lock_bh(a)
#define spin_unlock_dpc(a) adf_os_spin_unlock_bh(a)
#endif
#define spin_lock_destroy(a)
#define os_tasklet_lock(a, b) adf_os_spin_lock_irq(a, b)
#define os_tasklet_unlock(a, b) adf_os_spin_unlock_irq(a, b)
/*
** Need to define byte order based on the CPU configuration.
*/
#ifndef _LITTLE_ENDIAN
#define _LITTLE_ENDIAN 1234
#endif
#ifndef _BIG_ENDIAN
#define _BIG_ENDIAN 4321
#endif
#ifdef __BIG_ENDIAN
#define _BYTE_ORDER _BIG_ENDIAN
#else
#define _BYTE_ORDER _LITTLE_ENDIAN
#endif
/*
* Work Queue related macros
*/
#define ATH_CREATE_WQUEUE(name) adf_os_create_workqueue(name)
#define ATH_CREATE_WORK(a,b,c) adf_os_create_work(0, a, b, c)
#define ATH_CREATE_DELAYED_WORK(a,b,c) adf_os_create_delayed_work(0, a, b, c)
#define ATH_QUEUE_WORK(a,b) adf_os_queue_work(0, a, b)
#define ATH_QUEUE_DELAYED_WORK(a,b,c) adf_os_queue_delayed_work(0, a, b, c)
#define ATH_FLUSH_WQUEUE(a) adf_os_flush_workqueue(0, a)
#define ATH_DESTROY_WQUEUE(a) adf_os_destroy_workqueue(0, a)
/*
* Deduce if tasklets are available. If not then
* fall back to using the immediate work queue.
*/
#define tq_struct adf_os_bh_t
#define ATH_INIT_TQUEUE(a,b,c) adf_os_create_bh(0, a, b, c)
#define ATH_SCHEDULE_TQUEUE(a,b) adf_os_sched_bh(0, a)
typedef unsigned long TQUEUE_ARG;
#define mark_bh(a)
#define ATH_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \
f(struct ctl_table *ctl, int write, void *buffer, \
size_t *lenp, loff_t *ppos)
#define ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \
proc_dointvec(ctl, write, buffer, lenp, ppos)
#define ATH_SYSCTL_PROC_DOSTRING(ctl, write, filp, buffer, lenp, ppos) \
proc_dostring(ctl, write, filp, buffer, lenp, ppos)
/*
* Byte Order stuff
*/
#define le16toh(_x) le16_to_cpu(_x)
#define htole16(_x) cpu_to_le16(_x)
#define htobe16(_x) cpu_to_be16(_x)
#define le32toh(_x) le32_to_cpu(_x)
#define htole32(_x) cpu_to_le32(_x)
#define be16toh(_x) be16_to_cpu(_x)
#define be32toh(_x) be32_to_cpu(_x)
#define htobe32(_x) cpu_to_be32(_x)
#define EOK (0)
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#define IP_PRI_SHIFT 5
#ifndef IPV6_VERSION_MASK
#define IPV6_VERSION_MASK 0xF0000000
#endif
#ifndef IPV6_PRIORITY_MASK
#define IPV6_PRIORITY_MASK 0x0FF00000
#endif
#ifndef IPV6_FLOWLABEL_MASK
#define IPV6_FLOWLABEL_MASK 0x000FFFFF
#endif
#ifndef IPV6_VERSION_SHIFT
#define IPV6_VERSION_SHIFT 28
#endif
#ifndef IPV6_PRIORITY_SHIFT
#define IPV6_PRIORITY_SHIFT 20
#endif
#ifndef IPV6_FLOWLABEL_SHIFT
#define IPV6_FLOWLABEL_SHIFT 0
#endif
#ifndef ARPHRD_IEEE80211
#define ARPHRD_IEEE80211 801 /* IEEE 802.11. */
#endif
#define MAX_TX_RX_PACKET_SIZE 2500
static INLINE const char *
ether_sprintf(const uint8_t mac[6])
{
static char buf[32];
__adf_os_snprint(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return buf;
}
typedef unsigned int rwlock_state_t __attribute__((unused));
#define OS_ATOMIC_CMPXCHG(_Counter, _cmp, _xchg) cmpxchg((int32_t*)(_Counter), _cmp, _xchg)
/*
* Normal Delay functions. Time specified in microseconds.
*/
#define OS_DELAY(_us) adf_os_udelay(_us)
#define OS_SLEEP(_us) schedule_timeout_uninterruptible(usecs_to_jiffies(_us))
#define OS_MEMCPY(_dst, _src, _len) adf_os_mem_copy(_dst, _src, _len)
#define OS_MEMMOVE(_dst, _src, _len) adf_os_mem_move(_dst, _src, _len)
#define OS_MEMZERO(_buf, _len) adf_os_mem_zero(_buf, _len)
#define OS_MEMSET(_buf, _ch, _len) adf_os_mem_set(_buf, _ch, _len)
#define OS_MEMCMP(_mem1, _mem2, _len) adf_os_mem_cmp(_mem1, _mem2, _len)
#define OS_SET_WDTIMEOUT(__sc, __timeo) \
{\
((__sc)->sc_osdev->netdev)->watchdog_timeo = (__timeo);\
}
#define OS_CLR_NETDEV_FLAG(__sc, __flag)\
{\
((__sc)->sc_osdev->netdev)->flags &= ~(__flag);\
}
#define OS_NETIF_WAKE_QUEUE(__sc)\
{\
netif_wake_queue(((__sc)->sc_osdev->netdev));\
}
#define OS_NETIF_STOP_QUEUE(__sc)\
{\
netif_stop_queue(((__sc)->sc_osdev->netdev));\
}
#define OS_NETDEV_UPDATE_TRANS(__sc)\
{\
((__sc)->sc_osdev->netdev)->trans_start = jiffies;\
}
/*
* Locking interface for node
*/
#ifdef ATH_USB
typedef rwlock_t usb_readwrite_lock_t;
#endif
#define OS_RWLOCK_INIT(_rwl) rwlock_init(_rwl)
#define OS_RWLOCK_DESTROY(_nt)
#ifdef CONFIG_SMP
/* Undo the one provided by the kernel to debug spin locks */
#if 0
#undef spin_lock
#undef spin_unlock
#undef spin_trylock
#define spin_lock(x) \
do { \
spin_lock_bh(x);\
} while (0)
#define spin_unlock(x) \
do { \
if (!spin_is_locked(x)) { \
WARN_ON(1); \
printk(KERN_EMERG " %s:%d unlock addr=%pK, %s \n", __func__, __LINE__, x, \
!spin_is_locked(x)? "Not locked":""); \
} \
spin_unlock_bh(x);\
} while (0)
#define spin_trylock(x) spin_trylock_bh(x)
#endif
#define SPIN_LOCK_BH(x) do {\
if (irqs_disabled() || in_irq()) {\
spin_lock(x);\
} else {\
spin_lock_bh(x);\
}\
} while (0)
#define SPIN_UNLOCK_BH(x) do {\
if (irqs_disabled() || in_irq()) {\
spin_unlock(x);\
} else {\
spin_unlock_bh(x);\
}\
} while (0)
#define OS_SUPPORT_ASYNC_Q 1 /* support for handling asyn function calls */
#endif // ifdef CONFIG_SMP
#define OS_RWLOCK_READ_LOCK(rwl, lock_state) do {\
if (irqs_disabled() || in_irq()) {\
read_lock(rwl);\
} else {\
read_lock_bh(rwl);\
}\
} while (0)
#define OS_RWLOCK_WRITE_LOCK(rwl, lock_state) do { \
if (irqs_disabled() || in_irq()) {\
write_lock(rwl);\
} else {\
write_lock_bh(rwl);\
}\
} while(0)
#define OS_RWLOCK_READ_UNLOCK(rwl, lock_state) do {\
if (irqs_disabled() || in_irq()) {\
read_unlock(rwl);\
} else {\
read_unlock_bh(rwl);\
}\
} while (0)
#define OS_RWLOCK_WRITE_UNLOCK(rwl, lock_state) do {\
if (irqs_disabled() || in_irq()) {\
write_unlock(rwl);\
} else {\
write_unlock_bh(rwl);\
}\
} while (0)
#define OS_RWLOCK_READ_LOCK_BH(rwl, lock_state) do { \
read_lock_bh(rwl); \
} while (0)
#define OS_RWLOCK_WRITE_LOCK_BH(rwl, lock_state) do { \
write_lock_bh(rwl); \
} while (0)
#define OS_RWLOCK_READ_UNLOCK_BH(rwl, lock_state) do { \
read_unlock_bh(rwl); \
} while (0)
#define OS_RWLOCK_WRITE_UNLOCK_BH(rwl, lock_state) do { \
write_unlock_bh(rwl); \
} while (0)
/* Irqsave/restore version */
#define OS_RWLOCK_READ_LOCK_IRQSAVE(rwl, lock_state, flags) do { \
read_lock_irqsave(rwl, flags); \
} while (0)
#define OS_RWLOCK_WRITE_LOCK_IRQSAVE(rwl, lock_state, flags) do { \
write_lock_irqsave(rwl, flags); \
} while (0)
#define OS_RWLOCK_READ_UNLOCK_IRQRESTORE(rwl, lock_state, flags) do { \
read_unlock_irqrestore(rwl, flags); \
} while (0)
#define OS_RWLOCK_WRITE_UNLOCK_IRQRESTORE(rwl, lock_state, flags) do { \
write_unlock_irqrestore(rwl, flags); \
} while (0)
#ifndef OS_SUPPORT_ASYNC_Q
#define OS_SUPPORT_ASYNC_Q 0
#endif
/* Calculate the modulo with the following restrictions:
lowest 10 bits of div is zero.
div is at most 20 bits.
Theory for the math.
Suppose x = A + B
Then x % r = ((A % r) + B) % r
For our case, we let A be the top 32-bits of x which is non-zero.
Then x = (2^22 * a) + b.
*/
static INLINE u_int32_t OS_MOD64_TBTT_OFFSET(u_int64_t num64, u_int32_t div)
{
u_int32_t remainder_last10bits = (u_int32_t)num64 & 0x003FF;
/* Remove the low 10 bits. */
div = div >> 10;
num64 = num64 >> 10;
{
/* Get the mod of the top 32 bits */
u_int32_t num_hi = (u_int32_t)(num64 >> 22);
#define BITS_22_MASK 0x003FFFFF
#define BITS_10_MASK 0x000003FF
u_int32_t num_lo = (u_int32_t)(num64) & BITS_22_MASK;
u_int32_t remainder_hi = num_hi % div;
u_int32_t remainder_lo = num_lo;
u_int32_t remainder32 = (remainder_hi << 22) + remainder_lo;
remainder32 = remainder32 % div;
/* Put back the last 10 bits */
remainder32 = (remainder32 << 10) + remainder_last10bits;
return(remainder32);
}
}
/*
* System time interface
*/
typedef adf_os_time_t systime_t;
typedef adf_os_time_t systick_t;
static INLINE adf_os_time_t
OS_GET_TIMESTAMP(void)
{
return adf_os_ticks(); /* Fix double conversion from jiffies to ms */
}
static INLINE adf_os_time_t
OS_GET_TICKS(void)
{
return adf_os_ticks();
}
#define CONVERT_SYSTEM_TIME_TO_MS(_t) adf_os_ticks_to_msecs(_t)
#define CONVERT_SYSTEM_TIME_TO_SEC(_t) (adf_os_ticks_to_msecs(_t) / 1000)
#define CONVERT_SEC_TO_SYSTEM_TIME(_t) ((_t) * HZ)
#define CONVERT_MS_TO_SYSTEM_TIME(_t) ((_t) * HZ/1000)
struct _NIC_DEV;
typedef struct _NIC_DEV * osdev_t;
typedef struct timer_list os_timer_t;
typedef void (*timer_func)(void *);
typedef struct _os_mesg_t {
STAILQ_ENTRY(_os_mesg_t) mesg_next;
u_int16_t mesg_type;
u_int16_t mesg_len;
/* followed by mesg_len bytes */
} os_mesg_t;
#define OS_ASYNC_Q_MAX_MESGS 4
#define OS_SCHEDULE_ROUTING_MESG_TYPE 1
typedef void* os_task_handle_t;
typedef void (*os_tasklet_routine_t)(
void *context,
os_task_handle_t task_handle
);
typedef struct {
os_tasklet_routine_t routine;
atomic_t queued;
void *data;
spinlock_t lock;
} os_task_t;
typedef struct _os_schedule_routing_mesg {
os_tasklet_routine_t routine;
void* context;
} os_schedule_routing_mesg ;
typedef union _os_async_q_mesg {
os_schedule_routing_mesg s_mesg;
} os_async_q_mesg;
typedef void (*os_mesg_handler_t)(
void *ctx,
u_int16_t mesg_type,
u_int16_t mesg_len,
void *mesg
);
typedef struct {
osdev_t dev_handle;
int32_t num_queued;
int32_t mesg_len;
u_int8_t *mesg_queue_buf;
STAILQ_HEAD(, _os_mesg_t) mesg_head; /* queued mesg buffers */
STAILQ_HEAD(, _os_mesg_t) mesg_free_head; /* free mesg buffers */
spinlock_t lock;
spinlock_t ev_handler_lock;
#ifdef USE_SOFTINTR
void *_task;
#else
os_timer_t _timer;
#endif
os_mesg_handler_t handler;
void *ctx;
u_int8_t is_synchronous:1;
} os_mesg_queue_t;
struct ath_freebuf_bin {
TAILQ_ENTRY(ath_freebuf_bin) buf_list;
unsigned char *head;
unsigned char *end;
struct sk_buff *skb;
int lifetime;
};
typedef TAILQ_HEAD(ath_freebufhead_s, ath_freebuf_bin) ath_freebufhead;
struct rxbuf_recycle_ops {
void (*osdev_wbuf_collect)(void *, void *);
void * (*osdev_wbuf_recycle)(void *);
};
/*
* Definition of OS-dependent device structure.
* It'll be opaque to the actual ATH layer.
*/
struct _NIC_DEV {
void *bdev; /* bus device handle */
struct net_device *netdev; /* net device handle (wifi%d) */
adf_os_bh_t intr_tq; /* tasklet */
struct net_device_stats devstats; /* net device statisitics */
HAL_BUS_CONTEXT bc;
#ifdef ATH_PERF_PWR_OFFLOAD
struct device *device; /* generic device */
wait_queue_head_t event_queue;
#endif /* PERF_PWR_OFFLOAD */
#if OS_SUPPORT_ASYNC_Q
os_mesg_queue_t async_q; /* mesgq to handle async calls */
#endif
#ifdef ATH_BUS_PM
u_int8_t isDeviceAsleep;
#endif /* ATH_BUS_PM */
};
static INLINE unsigned char *
OS_MALLOC(osdev_t pNicDev, unsigned long ulSizeInBytes, int gfp)
{
return adf_os_mem_alloc(NULL, ulSizeInBytes);
}
#define OS_FREE(_p) adf_os_mem_free(_p)
#define OS_MALLOC_WITH_TAG(_ppMem, _size, _tag) do { \
*(_ppMem) = adf_os_mem_alloc(NULL, _size); \
} while(0)
#define OS_FREE_WITH_TAG(_pMem, _size) adf_os_mem_free(_pMem)
static INLINE void *
OS_ALLOC_VAP(osdev_t osdev, u_int32_t len)
{
void *netif;
/*
* In NDIS, we just allocate memory for interface object here.
* For Linux, it may need to allocate and setup a net_device.
*/
netif = adf_os_mem_alloc(NULL, len);
if (netif != NULL)
{
adf_os_mem_zero(netif, len);
}
return netif;
}
static INLINE void
OS_FREE_VAP(void *netif)
{
/* Just free the structure in NDIS.
* In Linux, it should call unregister_netdevice
* to free the memory */
adf_os_mem_free(netif);
}
#define bus_type_error 0
#define BUS_DMA_FROMDEVICE 0
#define BUS_DMA_TODEVICE 1
#if bus_type_error
#error "No bus type is specified"
#endif
typedef dma_addr_t * dma_context_t;
#define OS_DMA_MEM_CONTEXT(context) \
dma_addr_t context;
#define OS_GET_DMA_MEM_CONTEXT(var, field) \
&(var->field)
#define OS_COPY_DMA_MEM_CONTEXT(dst, src) \
*dst = *src
#define OS_ZERO_DMA_MEM_CONTEXT(context) \
*context = 0
#ifdef ATH_SUPPORT_SHARED_IRQ
#define ATH_LOCK_IRQ(_osdev) disable_irq(_osdev->netdev->irq)
#define ATH_UNLOCK_IRQ(_osdev) enable_irq(_osdev->netdev->irq)
#else
#define ATH_LOCK_IRQ(_osdev)
#define ATH_UNLOCK_IRQ(_osdev)
#endif
#ifdef ATH_SUPPORT_HTC
#define OS_EXEC_INTSAFE(_osdev, _fn, _arg) do { \
_fn(_arg); \
} while (0)
#else /* #ifdef ATH_SUPPORT_HTC */
#define OS_EXEC_INTSAFE(_osdev, _fn, _arg) do { \
unsigned long flags; \
ATH_LOCK_IRQ(_osdev); \
local_irq_save(flags); \
_fn(_arg); \
local_irq_restore(flags); \
ATH_UNLOCK_IRQ(_osdev); \
} while (0)
#endif /* #ifdef ATH_SUPPORT_HTC */
/*
* Timer Interfaces. Use these macros to declare timer
* and retrieve timer argument. This is mainly for resolving
* different argument types for timer function in different OS.
*/
#define OS_DECLARE_TIMER(_fn) void _fn(void *)
#define OS_TIMER_FUNC(_fn) \
void _fn(void *timer_arg)
#define OS_GET_TIMER_ARG(_arg, _type) \
(_arg) = (_type)(timer_arg)
#define OS_INIT_TIMER(_osdev, _timer, _fn, _ctx, type) adf_os_timer_init(_osdev, _timer, _fn, _ctx, type)
#define OS_SET_TIMER(_timer, _ms) adf_os_timer_mod(_timer, _ms)
#define OS_CANCEL_TIMER(_timer) adf_os_timer_cancel(_timer)
#define OS_FREE_TIMER(_timer) adf_os_timer_cancel(_timer)
static INLINE void
OS_GET_RANDOM_BYTES(void *p, u_int16_t n)
{
get_random_bytes(p, n);
}
typedef enum _mesgq_priority_t {
MESGQ_PRIORITY_LOW,
MESGQ_PRIORITY_NORMAL,
MESGQ_PRIORITY_HIGH
} mesgq_priority_t;
typedef enum _mesgq_event_delivery_type {
MESGQ_ASYNCHRONOUS_EVENT_DELIVERY,
MESGQ_SYNCHRONOUS_EVENT_DELIVERY,
} mesgq_event_delivery_type;
/*
* OS_MESGQ_* API to deliver messages(events) asynchronosly.
* messages are queued up into a queue and are delivered in the context of
* timer thread. this will avoid reentrency issues across different
* module boundaries.
*/
static INLINE void
os_mesgq_handler(void *timer_arg)
{
os_mesg_queue_t *queue = (os_mesg_queue_t*)timer_arg;
os_mesg_t *mesg = NULL;
void *msg;
/*
* Request access to message queue to retrieve message for processing
*/
spin_lock(&(queue->lock));
mesg = STAILQ_FIRST(&queue->mesg_head);
while(mesg) {
STAILQ_REMOVE_HEAD(&queue->mesg_head, mesg_next);
if (mesg->mesg_len) {
msg = (void *) (mesg+1);
} else {
msg = NULL;
}
/*
* Release access to message queue before processing message
*/
spin_unlock(&(queue->lock));
/*
* Ensure just one message can be processes at a time.
*/
spin_lock(&(queue->ev_handler_lock));
queue->handler(queue->ctx,mesg->mesg_type,mesg->mesg_len, msg);
spin_unlock(&(queue->ev_handler_lock));
/*
* Request access to message queue to retrieve next message
*/
spin_lock(&(queue->lock));
queue->num_queued--;
STAILQ_INSERT_TAIL(&queue->mesg_free_head,mesg, mesg_next);
mesg = STAILQ_FIRST(&queue->mesg_head);
}
/*
* Release message queue
*/
spin_unlock(&(queue->lock));
}
/*
* initialize message queue.
* devhandle : os dev handle.
* queue : message queue.
* mesg_len : maximum length of message.
* max_queued : maximum number of messages that can be queued at any time.
* msg_handler : handler function which will be called
* asynchronously to deliver each message.
*/
static INLINE int OS_MESGQ_INIT(osdev_t devhandle, os_mesg_queue_t *queue,
u_int32_t mesg_len, u_int32_t max_queued,
os_mesg_handler_t msg_handler, void *context,
mesgq_priority_t priority,
mesgq_event_delivery_type mq_type)
{
int i,len;
os_mesg_t *mesg;
len = (mesg_len + sizeof(struct _os_mesg_t));
queue->mesg_queue_buf = adf_os_mem_alloc(NULL, (adf_os_size_t) len*max_queued);
if (!queue->mesg_queue_buf)
return -ENOMEM;
queue->dev_handle = devhandle;
STAILQ_INIT(&queue->mesg_head);
STAILQ_INIT(&queue->mesg_free_head);
spin_lock_init(&(queue->lock));
spin_lock_init(&(queue->ev_handler_lock));
mesg = (os_mesg_t *)queue->mesg_queue_buf;
for (i=0;i<max_queued;++i) {
STAILQ_INSERT_TAIL(&queue->mesg_free_head,mesg,mesg_next);
mesg = (os_mesg_t *) ((u_int8_t *) mesg + len);
}
queue->mesg_len = mesg_len;
queue->ctx = context;
queue->handler = msg_handler;
queue->num_queued = 0;
if (mq_type == MESGQ_ASYNCHRONOUS_EVENT_DELIVERY) {
queue->is_synchronous=0;
} else {
queue->is_synchronous=1;
}
#ifdef USE_SOFTINTR
queue->_task = softintr_establish(IPL_SOFTNET,os_mesgq_handler,(void *)queue);
#else
OS_INIT_TIMER(devhandle,&queue->_timer, os_mesgq_handler, queue,
ADF_DEFERRABLE_TIMER);
#endif
return 0;
}
/*
* send a message.
* queue : message queue.
* msg : message (opaque) . the size of the message
* is equal to the mesg_length passed to the OS_MESG_INIT
*
*/
static INLINE int OS_MESGQ_SEND(os_mesg_queue_t *queue,u_int16_t type, u_int16_t len, void *msg)
{
os_mesg_t *mesg;
spin_lock(&(queue->lock));
if (queue->is_synchronous ) {
queue->handler(queue->ctx,type,len, msg);
} else {
mesg = STAILQ_FIRST(&queue->mesg_free_head);
KASSERT(len <= queue->mesg_len, ("len <= queue->mesg_len"));
if (mesg) {
STAILQ_REMOVE_HEAD(&queue->mesg_free_head, mesg_next);
mesg->mesg_type = type;
mesg->mesg_len = len;
if (len) {
OS_MEMCPY((u_int8_t *)(mesg+1),msg,len);
}
STAILQ_INSERT_TAIL(&queue->mesg_head, mesg, mesg_next);
queue->num_queued++;
} else {
spin_unlock(&(queue->lock));
printk("No more message queue buffers !!! \n");
return -ENOMEM;
}
if (queue->num_queued == 1) {
/* schedule a task (timer) to handle the messages */
#ifdef USE_SOFTINTR
softintr_schedule(queue->_task);
#else
OS_SET_TIMER(&queue->_timer,0);
#endif
}
}
spin_unlock(&(queue->lock));
return 0;
}
/*
* this is only for single threaded operating systems.
* assert for now.
*/
static INLINE int OS_MESGQ_SEND_SYNC(os_mesg_queue_t *queue,u_int16_t type, u_int16_t len, void *msg, bool flush)
{
KASSERT(0,(" mesg queue sync send is not supported by linux"));
return 0;
}
static INLINE int OS_MESGQ_CAN_SEND_SYNC(void)
{
return TRUE;
}
/*
* drain all the messages.
* queue : message queue.
*/
static INLINE void OS_MESGQ_DRAIN(os_mesg_queue_t *queue, os_mesg_handler_t msg_handler)
{
os_mesg_t *mesg = NULL;
void *msg;
spin_lock(&(queue->lock));
#ifndef USE_SOFTINTR
OS_CANCEL_TIMER(&queue->_timer);
#endif
mesg = STAILQ_FIRST(&queue->mesg_head);
while(mesg) {
STAILQ_REMOVE_HEAD(&queue->mesg_head, mesg_next);
queue->num_queued--;
if (msg_handler != NULL) {
if (mesg->mesg_len) {
msg = (void *) (mesg+1);
} else {
msg = NULL;
}
msg_handler(queue->ctx, mesg->mesg_type, mesg->mesg_len, msg);
}
STAILQ_INSERT_TAIL(&queue->mesg_free_head,mesg, mesg_next);
mesg = STAILQ_FIRST(&queue->mesg_head);
};
STAILQ_INIT(&queue->mesg_head);
spin_unlock(&(queue->lock));
}
/*
* destroy the message queue.
* queue : message queue.
* reclaim all the resorces.
*/
static INLINE void OS_MESGQ_DESTROY(os_mesg_queue_t *queue)
{
spin_lock(&(queue->lock));
#ifdef USE_SOFTINTR
softintr_disestablish(queue->_task);
#else
OS_CANCEL_TIMER(&queue->_timer);
#endif
queue->num_queued = 0;
STAILQ_INIT(&queue->mesg_head);
STAILQ_INIT(&queue->mesg_free_head);
adf_os_mem_free(queue->mesg_queue_buf);
#ifndef USE_SOFTINTR
OS_FREE_TIMER(&queue->_timer);
#endif
spin_unlock(&(queue->lock));
spin_lock_destroy(&(queue->lock));
spin_lock_destroy(&(queue->ev_handler_lock));
}
/*
* temp WAR for windows hang (dead lock). It can be removed when VAP SM is re-written (bug 65137).
*/
static INLINE int
OS_SCHEDULE_ROUTING(osdev_t pNicDev,
os_tasklet_routine_t routine,
void* context)
{
#if OS_SUPPORT_ASYNC_Q
os_schedule_routing_mesg s_mesg ;
s_mesg.routine = routine;
s_mesg.context = context;
OS_MESGQ_SEND(&pNicDev->async_q,OS_SCHEDULE_ROUTING_MESG_TYPE, sizeof(os_schedule_routing_mesg), &s_mesg);
#else
routine(context, NULL);
#endif
return 0;
}
static INLINE void
OS_FREE_ROUTING(void* workItemHandle)
{
}
/*
** These are required for network manager support
*/
#ifndef SET_NETDEV_DEV
#define SET_NETDEV_DEV(ndev, pdev)
#endif
#ifdef to_net_dev
#define ATH_GET_NETDEV_DEV(ndev) ((ndev)->dev.parent)
#else
#define ATH_GET_NETDEV_DEV(ndev) ((ndev)->class_dev.dev)
#endif
/*
* Opaque S/G List Entry
*/
typedef struct scatterlist sg_t;
#include "hwdef.h"
#ifndef ARRAY_LENGTH
#define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0]))
#endif
#define MIN(a, b) adf_os_min(a, b)
#define MAX(a, b) adf_os_max(a, b)
/*
* PCI configuration space access
*/
#ifdef ATH_PCI
static INLINE u_int32_t
OS_PCI_READ_CONFIG(osdev_t osdev, u_int32_t offset, void *p, u_int32_t bytes)
{
struct pci_dev *pdev = (struct pci_dev *)osdev->bdev;
switch (bytes) {
case 1:
pci_read_config_byte(pdev, offset, p);
break;
case 2:
pci_read_config_word(pdev, offset, p);
break;
case 4:
pci_read_config_dword(pdev, offset, p);
break;
}
return bytes;
}
static INLINE void
OS_PCI_WRITE_CONFIG(osdev_t osdev, u_int32_t offset, void *p, u_int32_t bytes)
{
struct pci_dev *pdev = (struct pci_dev *)osdev->bdev;
switch (bytes) {
case 1:
pci_write_config_byte(pdev, offset, *(u_int8_t *)p);
break;
case 2:
pci_write_config_word(pdev, offset, *(u_int16_t *)p);
break;
case 4:
pci_write_config_dword(pdev, offset, *(u_int32_t *)p);
break;
}
}
#else
static INLINE u_int32_t
OS_PCI_READ_CONFIG(osdev_t osdev, u_int32_t offset, void *p, u_int32_t bytes)
{
OS_MEMSET(p, 0xff, bytes);
return 0;
}
#define OS_PCI_WRITE_CONFIG(_osdev, _offset, _p, _bytes)
#endif
void *OS_ALLOC_VAP(osdev_t dev, u_int32_t len);
void OS_FREE_VAP(void *netif);
// ALLOC_DMA_MAP_CONTEXT_AREA is a NULL macro and is implemented only for BSD.
#define ALLOC_DMA_MAP_CONTEXT_AREA(os_handle, p_memctx)
#define FREE_DMA_CONTEXT_POOL(os_handle, name)
#define ALLOC_DMA_CONTEXT_POOL(os_handle, name, numdesc)
#define ATH_QOSNULL_TXDESC 64
#define ATH_FRAG_PER_MSDU 1
#ifndef ATH_TXBUF
#define ATH_TXBUF 512/ATH_FRAG_PER_MSDU
#endif
/*
* minimum h/w qdepth to be sustained to maximize aggregation
*/
#define ATH_AGGR_MIN_QDEPTH 2
#define OS_MAX_RXBUF_SIZE(_statuslen) (IEEE80211_MAX_MPDU_LEN + _statuslen)
#define ATH_GET_RX_CONTEXT_BUF(_wbuf) \
(ATH_RX_CONTEXT(_wbuf)->ctx_rxbuf)
#define ATH_SET_RX_CONTEXT_BUF(_wbuf, _bf) \
(ATH_GET_RX_CONTEXT_BUF(_wbuf) = _bf)
// This macro is used to avoid another wrapper around ath_rxbuf_alloc.
// For Mac OS, we need to OR in ATH_RXBUF_ALLOC_DONTWAIT with length.
// Not needed for other OS'.
#define ATH_ALLOCATE_RXBUFFER(_sc, _len) ath_rxbuf_alloc(_sc, _len)
#ifndef OS_EXPORT_SYMBOL
#define OS_EXPORT_SYMBOL(_sym) EXPORT_SYMBOL(_sym)
#endif
struct ieee80211_node;
struct ieee80211_cb {
u_int8_t vlan[8]; /* reserve for vlan tag info */
struct ieee80211_node *ni;
u_int32_t flags;
#define N_LINK0 0x01 /* frame needs WEP encryption */
#define N_FF 0x02 /* fast frame */
#define N_PWR_SAV 0x04 /* bypass power save handling */
#define N_UAPSD 0x08 /* frame flagged for u-apsd handling */
#define N_EAPOL 0x10 /* frame flagged for EAPOL handling */
#define N_AMSDU 0x20 /* frame flagged for AMSDU handling */
#define N_NULL_PWR_SAV 0x40 /* null data with power save bit on */
#define N_PROBING 0x80 /* frame flagged as a probing one */
#define N_ERROR 0x100 /* frame flagged as a error one */
#define N_MOREDATA 0x200 /* more data flag */
#define N_SMPSACTM 0x400 /* This frame is SM power save Action Mgmt frame */
#define N_QOS 0x800 /* This is a QOS frame*/
#define N_ENCAP_DONE 0x1000 /* This frame is marked as fast-path pkts, some encapsulation work has been done by h/w */
#define N_CLONED 0x2000 /* frame is cloned in rx path */
#define N_ANT_TRAIN 0x8000 /* frame is smart antenna training packet */
#define N_ANT_TRAIN_LAST 0x20000 /* Last smart antenna training packet */
#ifdef ATH_SUPPORT_WAPI
#define N_WAI 0x40000 /* frame flagged for WAPI handling */
#endif
#define N_FMSS 0x100000 /* frame needs FMS handling */
u_int8_t u_tid; /* user priority from vlan/ip tos */
u_int8_t exemptiontype; /* exemption type of this frame (0,1,2)*/
u_int8_t type; /* type of this frame */
union {
void *context; /* pointer to context area */
}_u;
#if defined(ATH_SUPPORT_P2P) || defined(ATH_SUPPORT_TDLS)
void *complete_handler; /* complete handler */
void *complete_handler_arg; /* complete handler arg */
#endif
};
//#endif /* CONVERGED_SW */
#ifndef ATH_SUPPORT_HTC
/*
* For packet capture, define the same physical layer packet header
* structure as used in the wlan-ng driver
*/
typedef struct {
u_int32_t did;
u_int16_t status;
u_int16_t len;
u_int32_t data;
} p80211item_uint32_t;
typedef struct {
u_int32_t msgcode;
u_int32_t msglen;
#define WLAN_DEVNAMELEN_MAX 16
u_int8_t devname[WLAN_DEVNAMELEN_MAX];
p80211item_uint32_t hosttime;
p80211item_uint32_t mactime;
p80211item_uint32_t channel;
p80211item_uint32_t rssi;
p80211item_uint32_t sq;
p80211item_uint32_t signal;
p80211item_uint32_t noise;
p80211item_uint32_t rate;
p80211item_uint32_t istx;
p80211item_uint32_t frmlen;
} wlan_ng_prism2_header;
#endif
#define N_FLAG_SET(_nbf, _flag) \
(((struct ieee80211_cb *)(_nbf)->cb)->flags |= (_flag))
#define N_FLAG_CLR(_nbf, _flag) \
(((struct ieee80211_cb *)(_nbf)->cb)->flags &= ~(_flag))
#define N_FLAG_GET(_nbf, _flag) \
(((struct ieee80211_cb *)(_nbf)->cb)->flags & (_flag))
#define N_FLAG_IS(_nbf, _flag) \
((((struct ieee80211_cb *)(_nbf)->cb)->flags & (_flag)) == (_flag))
#define N_FLAG_KEEP_ONLY(_nbf, _flag) \
(((struct ieee80211_cb *)(_nbf)->cb)->flags &= (_flag))
#define N_PWR_SAV_SET(nbf) N_FLAG_SET((nbf), N_PWR_SAV)
#define N_PWR_SAV_CLR(nbf) N_FLAG_CLR((nbf), N_PWR_SAV)
#define N_PWR_SAV_GET(nbf) N_FLAG_GET((nbf), N_PWR_SAV)
#define N_PWR_SAV_IS(nbf) N_FLAG_IS((nbf), N_PWR_SAV)
#define N_NULL_PWR_SAV_SET(nbf) N_FLAG_SET((nbf), N_NULL_PWR_SAV)
#define N_NULL_PWR_SAV_CLR(nbf) N_FLAG_CLR((nbf), N_NULL_PWR_SAV)
#define N_NULL_PWR_SAV_GET(nbf) N_FLAG_GET((nbf), N_NULL_PWR_SAV)
#define N_NULL_PWR_SAV_IS(nbf) N_FLAG_IS((nbf), N_NULL_PWR_SAV)
#define N_PROBING_SET(nbf) N_FLAG_SET((nbf), N_PROBING)
#define N_PROBING_CLR(nbf) N_FLAG_CLR((nbf), N_PROBING)
#define N_PROBING_GET(nbf) N_FLAG_GET((nbf), N_PROBING)
#define N_PROBING_IS(nbf) N_FLAG_IS((nbf), N_PROBING)
#define N_CLONED_SET(nbf) N_FLAG_SET((nbf), N_CLONED)
#define N_CLONED_CLR(nbf) N_FLAG_CLR((nbf), N_CLONED)
#define N_CLONED_GET(nbf) N_FLAG_GET((nbf), N_CLONED)
#define N_CLONED_IS(nbf) N_FLAG_IS((nbf), N_CLONED)
#define N_MOREDATA_SET(nbf) N_FLAG_SET((nbf), N_MOREDATA)
#define N_MOREDATA_CLR(nbf) N_FLAG_CLR((nbf), N_MOREDATA)
#define N_MOREDATA_GET(nbf) N_FLAG_GET((nbf), N_MOREDATA)
#define N_MOREDATA_IS(nbf) N_FLAG_IS((nbf), N_MOREDATA)
#define N_SMPSACTM_SET(nbf) N_FLAG_SET((nbf), N_SMPSACTM)
#define N_SMPSACTM_CLR(nbf) N_FLAG_CLR((nbf), N_SMPSACTM)
#define N_SMPSACTM_GET(nbf) N_FLAG_GET((nbf), N_SMPSACTM)
#define N_SMPSACTM_IS(nbf) N_FLAG_IS((nbf), N_SMPSACTM)
#define N_QOS_SET(nbf) N_FLAG_SET((nbf), N_QOS)
#define N_QOS_CLR(nbf) N_FLAG_CLR((nbf), N_QOS)
#define N_QOS_GET(nbf) N_FLAG_GET((nbf), N_QOS)
#define N_QOS_IS(nbf) N_FLAG_IS((nbf), N_QOS)
#define N_EAPOL_SET(nbf) N_FLAG_SET((nbf), N_EAPOL)
#define N_EAPOL_IS(nbf) N_FLAG_IS((nbf), N_EAPOL)
#define N_AMSDU_SET(nbf) N_FLAG_SET((nbf), N_AMSDU)
#define N_AMSDU_IS(nbf) N_FLAG_IS((nbf), N_AMSDU)
#define N_FF_SET(nbf) N_FLAG_SET((nbf), N_FF)
#define N_FF_IS(nbf) N_FLAG_IS((nbf), N_FF)
#define N_UAPSD_SET(nbf) N_FLAG_SET((nbf), N_UAPSD)
#define N_UAPSD_CLR(nbf) N_FLAG_CLR((nbf), N_UAPSD)
#define N_UAPSD_IS(nbf) N_FLAG_IS((nbf), N_UAPSD)
#define N_ENCAP_DONE_IS(nbf) N_FLAG_IS((nbf), N_ENCAP_DONE)
#define N_ENCAP_DONE_SET(nbf) N_FLAG_SET((nbf), N_ENCAP_DONE)
#define N_ENCAP_DONE_CLR(nbf) N_FLAG_CLR((nbf), N_ENCAP_DONE)
#define N_STATUS_SET(nbf, _status)
#define N_STATUS_GET(nbf) \
if(N_FLAG_IS((nbf), N_ERROR)) \
return WB_STATUS_TX_ERROR; \
else \
return WB_STATUS_OK;
#define N_CONTEXT_SET(_nbf, _context) \
(((struct ieee80211_cb *)(_nbf)->cb)->_u.context = (_context))
#define N_CONTEXT_GET(_nbf) \
(((struct ieee80211_cb *)(_nbf)->cb)->_u.context)
#define N_TYPE_SET(_nbf, _type) \
(((struct ieee80211_cb *)(_nbf)->cb)->type = (_type))
#define N_TYPE_GET(_nbf) \
(((struct ieee80211_cb *)(_nbf)->cb)->type)
#define N_NODE_SET(_nbf, _ni) \
(((struct ieee80211_cb *)(_nbf)->cb)->ni = (_ni))
#define N_NODE_GET(_nbf) \
(((struct ieee80211_cb *)(_nbf)->cb)->ni)
#define N_COMPLETE_HANDLER_SET(_nbf, _handler) \
(((struct ieee80211_cb *)(_nbf)->cb)->complete_handler = (_handler))
#define N_COMPLETE_HANDLER_ARG_SET(_nbf, _arg) \
(((struct ieee80211_cb *)(_nbf)->cb)->complete_handler_arg = (_arg))
#define N_COMPLETE_HANDLER_GET(_nbf) \
(((struct ieee80211_cb *)(_nbf)->cb)->complete_handler)
#define N_COMPLETE_HANDLER_ARG_GET(_nbf) \
(((struct ieee80211_cb *)(_nbf)->cb)->complete_handler_arg)
#define N_EXMTYPE_SET(_nbf, _type) \
(((struct ieee80211_cb *)(_nbf)->cb)->exemptiontype = (_type))
#define N_EXMTYPE_GET(_nbf) \
(((struct ieee80211_cb *)(_nbf)->cb)->exemptiontype)
#define N_TID_SET(_nbf, _tid) \
(((struct ieee80211_cb *)(_nbf)->cb)->u_tid = (_tid))
#define N_TID_GET(_nbf) \
(((struct ieee80211_cb *)(_nbf)->cb)->u_tid)
#define N_ANT_GET(_nbf) \
(((struct ieee80211_cb *)(_nbf)->cb)->smart_antenna)
#define N_RATE_INDEX_GET(_nbf) \
(((struct ieee80211_cb *)(_nbf)->cb)->rateIndex)
#define N_TRAIN_PKT_IS(nbf) N_FLAG_IS((nbf), N_ANT_TRAIN)
#define N_TRAIN_LASTPKT_IS(nbf) N_FLAG_IS((nbf), N_ANT_TRAIN_LAST)
#define N_ANT_SET(_nbf, _antenna) \
(((struct ieee80211_cb *)(_nbf)->cb)->smart_antenna = (_antenna))
#define N_RATE_INDEX_SET(_nbf, _idx) \
(((struct ieee80211_cb *)(_nbf)->cb)->rateIndex = (_idx))
#define N_TRAIN_PKT_SET(nbf) N_FLAG_SET((nbf), N_ANT_TRAIN)
#define N_TRAIN_PKT_UNSET(nbf) N_FLAG_CLR((nbf), N_ANT_TRAIN)
#define N_TRAIN_LASTPKT_SET(nbf) N_FLAG_SET((nbf), N_ANT_TRAIN_LAST)
/*
* nbufs on the power save queue are tagged with an age and
* timed out. We reuse the hardware checksum field in the
* nbuf packet header to store this data.
* XXX use private cb area
*/
#define N_AGE_SET(skb, v) (skb)->csum = (v)
#define N_AGE_GET(skb) (skb)->csum
#define N_AGE_SUB(skb, adj) (skb)->csum -= (adj)
#endif /* end of _ATH_LINUX_OSDEP_ADF_H */