2008-09-17 23:34:07 +08:00
|
|
|
/*
|
|
|
|
* WUSB Wire Adapter: Radio Control Interface (WUSB[8])
|
|
|
|
* Notification and Event Handling
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005-2006 Intel Corporation
|
|
|
|
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License version
|
|
|
|
* 2 as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
* 02110-1301, USA.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* The RC interface of the Host Wire Adapter (USB dongle) or WHCI PCI
|
|
|
|
* card delivers a stream of notifications and events to the
|
|
|
|
* notification end event endpoint or area. This code takes care of
|
|
|
|
* getting a buffer with that data, breaking it up in separate
|
|
|
|
* notifications and events and then deliver those.
|
|
|
|
*
|
|
|
|
* Events are answers to commands and they carry a context ID that
|
|
|
|
* associates them to the command. Notifications are that,
|
|
|
|
* notifications, they come out of the blue and have a context ID of
|
|
|
|
* zero. Think of the context ID kind of like a handler. The
|
|
|
|
* uwb_rc_neh_* code deals with managing context IDs.
|
|
|
|
*
|
|
|
|
* This is why you require a handle to operate on a UWB host. When you
|
|
|
|
* open a handle a context ID is assigned to you.
|
|
|
|
*
|
|
|
|
* So, as it is done is:
|
|
|
|
*
|
|
|
|
* 1. Add an event handler [uwb_rc_neh_add()] (assigns a ctx id)
|
|
|
|
* 2. Issue command [rc->cmd(rc, ...)]
|
|
|
|
* 3. Arm the timeout timer [uwb_rc_neh_arm()]
|
|
|
|
* 4, Release the reference to the neh [uwb_rc_neh_put()]
|
|
|
|
* 5. Wait for the callback
|
|
|
|
* 6. Command result (RCEB) is passed to the callback
|
|
|
|
*
|
|
|
|
* If (2) fails, you should remove the handle [uwb_rc_neh_rm()]
|
|
|
|
* instead of arming the timer.
|
|
|
|
*
|
|
|
|
* Handles are for using in *serialized* code, single thread.
|
|
|
|
*
|
|
|
|
* When the notification/event comes, the IRQ handler/endpoint
|
|
|
|
* callback passes the data read to uwb_rc_neh_grok() which will break
|
|
|
|
* it up in a discrete series of events, look up who is listening for
|
|
|
|
* them and execute the pertinent callbacks.
|
|
|
|
*
|
|
|
|
* If the reader detects an error while reading the data stream, call
|
|
|
|
* uwb_rc_neh_error().
|
|
|
|
*
|
|
|
|
* CONSTRAINTS/ASSUMPTIONS:
|
|
|
|
*
|
|
|
|
* - Most notifications/events are small (less thank .5k), copying
|
|
|
|
* around is ok.
|
|
|
|
*
|
|
|
|
* - Notifications/events are ALWAYS smaller than PAGE_SIZE
|
|
|
|
*
|
|
|
|
* - Notifications/events always come in a single piece (ie: a buffer
|
|
|
|
* will always contain entire notifications/events).
|
|
|
|
*
|
|
|
|
* - we cannot know in advance how long each event is (because they
|
|
|
|
* lack a length field in their header--smart move by the standards
|
|
|
|
* body, btw). So we need a facility to get the event size given the
|
|
|
|
* header. This is what the EST code does (notif/Event Size
|
|
|
|
* Tables), check nest.c--as well, you can associate the size to
|
|
|
|
* the handle [w/ neh->extra_size()].
|
|
|
|
*
|
|
|
|
* - Most notifications/events are fixed size; only a few are variable
|
|
|
|
* size (NEST takes care of that).
|
|
|
|
*
|
|
|
|
* - Listeners of events expect them, so they usually provide a
|
|
|
|
* buffer, as they know the size. Listeners to notifications don't,
|
|
|
|
* so we allocate their buffers dynamically.
|
|
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/timer.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2008-09-17 23:34:07 +08:00
|
|
|
#include <linux/err.h>
|
2011-07-11 01:18:02 +08:00
|
|
|
#include <linux/export.h>
|
2008-09-17 23:34:07 +08:00
|
|
|
|
|
|
|
#include "uwb-internal.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* UWB Radio Controller Notification/Event Handle
|
|
|
|
*
|
|
|
|
* Represents an entity waiting for an event coming from the UWB Radio
|
|
|
|
* Controller with a given context id (context) and type (evt_type and
|
|
|
|
* evt). On reception of the notification/event, the callback (cb) is
|
|
|
|
* called with the event.
|
|
|
|
*
|
|
|
|
* If the timer expires before the event is received, the callback is
|
|
|
|
* called with -ETIMEDOUT as the event size.
|
|
|
|
*/
|
|
|
|
struct uwb_rc_neh {
|
|
|
|
struct kref kref;
|
|
|
|
|
|
|
|
struct uwb_rc *rc;
|
|
|
|
u8 evt_type;
|
|
|
|
__le16 evt;
|
|
|
|
u8 context;
|
2012-04-16 21:28:28 +08:00
|
|
|
u8 completed;
|
2008-09-17 23:34:07 +08:00
|
|
|
uwb_rc_cmd_cb_f cb;
|
|
|
|
void *arg;
|
|
|
|
|
|
|
|
struct timer_list timer;
|
|
|
|
struct list_head list_node;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void uwb_rc_neh_timer(unsigned long arg);
|
|
|
|
|
|
|
|
static void uwb_rc_neh_release(struct kref *kref)
|
|
|
|
{
|
|
|
|
struct uwb_rc_neh *neh = container_of(kref, struct uwb_rc_neh, kref);
|
|
|
|
|
|
|
|
kfree(neh);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uwb_rc_neh_get(struct uwb_rc_neh *neh)
|
|
|
|
{
|
|
|
|
kref_get(&neh->kref);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* uwb_rc_neh_put - release reference to a neh
|
|
|
|
* @neh: the neh
|
|
|
|
*/
|
|
|
|
void uwb_rc_neh_put(struct uwb_rc_neh *neh)
|
|
|
|
{
|
|
|
|
kref_put(&neh->kref, uwb_rc_neh_release);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Assigns @neh a context id from @rc's pool
|
|
|
|
*
|
|
|
|
* @rc: UWB Radio Controller descriptor; @rc->neh_lock taken
|
|
|
|
* @neh: Notification/Event Handle
|
|
|
|
* @returns 0 if context id was assigned ok; < 0 errno on error (if
|
|
|
|
* all the context IDs are taken).
|
|
|
|
*
|
|
|
|
* (assumes @wa is locked).
|
|
|
|
*
|
|
|
|
* NOTE: WUSB spec reserves context ids 0x00 for notifications and
|
|
|
|
* 0xff is invalid, so they must not be used. Initialization
|
|
|
|
* fills up those two in the bitmap so they are not allocated.
|
|
|
|
*
|
2009-09-29 09:43:57 +08:00
|
|
|
* We spread the allocation around to reduce the possibility of two
|
2008-09-17 23:34:07 +08:00
|
|
|
* consecutive opened @neh's getting the same context ID assigned (to
|
|
|
|
* avoid surprises with late events that timed out long time ago). So
|
|
|
|
* first we search from where @rc->ctx_roll is, if not found, we
|
|
|
|
* search from zero.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
int __uwb_rc_ctx_get(struct uwb_rc *rc, struct uwb_rc_neh *neh)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
result = find_next_zero_bit(rc->ctx_bm, UWB_RC_CTX_MAX,
|
|
|
|
rc->ctx_roll++);
|
|
|
|
if (result < UWB_RC_CTX_MAX)
|
|
|
|
goto found;
|
|
|
|
result = find_first_zero_bit(rc->ctx_bm, UWB_RC_CTX_MAX);
|
|
|
|
if (result < UWB_RC_CTX_MAX)
|
|
|
|
goto found;
|
|
|
|
return -ENFILE;
|
|
|
|
found:
|
|
|
|
set_bit(result, rc->ctx_bm);
|
|
|
|
neh->context = result;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Releases @neh's context ID back to @rc (@rc->neh_lock is locked). */
|
|
|
|
static
|
|
|
|
void __uwb_rc_ctx_put(struct uwb_rc *rc, struct uwb_rc_neh *neh)
|
|
|
|
{
|
|
|
|
struct device *dev = &rc->uwb_dev.dev;
|
|
|
|
if (neh->context == 0)
|
|
|
|
return;
|
|
|
|
if (test_bit(neh->context, rc->ctx_bm) == 0) {
|
|
|
|
dev_err(dev, "context %u not set in bitmap\n",
|
|
|
|
neh->context);
|
|
|
|
WARN_ON(1);
|
|
|
|
}
|
|
|
|
clear_bit(neh->context, rc->ctx_bm);
|
|
|
|
neh->context = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* uwb_rc_neh_add - add a neh for a radio controller command
|
|
|
|
* @rc: the radio controller
|
|
|
|
* @cmd: the radio controller command
|
|
|
|
* @expected_type: the type of the expected response event
|
|
|
|
* @expected_event: the expected event ID
|
|
|
|
* @cb: callback for when the event is received
|
|
|
|
* @arg: argument for the callback
|
|
|
|
*
|
|
|
|
* Creates a neh and adds it to the list of those waiting for an
|
|
|
|
* event. A context ID will be assigned to the command.
|
|
|
|
*/
|
|
|
|
struct uwb_rc_neh *uwb_rc_neh_add(struct uwb_rc *rc, struct uwb_rccb *cmd,
|
|
|
|
u8 expected_type, u16 expected_event,
|
|
|
|
uwb_rc_cmd_cb_f cb, void *arg)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
unsigned long flags;
|
|
|
|
struct device *dev = &rc->uwb_dev.dev;
|
|
|
|
struct uwb_rc_neh *neh;
|
|
|
|
|
|
|
|
neh = kzalloc(sizeof(*neh), GFP_KERNEL);
|
|
|
|
if (neh == NULL) {
|
|
|
|
result = -ENOMEM;
|
|
|
|
goto error_kzalloc;
|
|
|
|
}
|
|
|
|
|
|
|
|
kref_init(&neh->kref);
|
|
|
|
INIT_LIST_HEAD(&neh->list_node);
|
2015-10-21 02:29:53 +08:00
|
|
|
setup_timer(&neh->timer, uwb_rc_neh_timer, (unsigned long)neh);
|
2008-09-17 23:34:07 +08:00
|
|
|
|
|
|
|
neh->rc = rc;
|
|
|
|
neh->evt_type = expected_type;
|
|
|
|
neh->evt = cpu_to_le16(expected_event);
|
|
|
|
neh->cb = cb;
|
|
|
|
neh->arg = arg;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&rc->neh_lock, flags);
|
|
|
|
result = __uwb_rc_ctx_get(rc, neh);
|
|
|
|
if (result >= 0) {
|
|
|
|
cmd->bCommandContext = neh->context;
|
|
|
|
list_add_tail(&neh->list_node, &rc->neh_list);
|
|
|
|
uwb_rc_neh_get(neh);
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
if (result < 0)
|
|
|
|
goto error_ctx_get;
|
|
|
|
|
|
|
|
return neh;
|
|
|
|
|
|
|
|
error_ctx_get:
|
|
|
|
kfree(neh);
|
|
|
|
error_kzalloc:
|
|
|
|
dev_err(dev, "cannot open handle to radio controller: %d\n", result);
|
|
|
|
return ERR_PTR(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
|
|
|
|
{
|
|
|
|
__uwb_rc_ctx_put(rc, neh);
|
|
|
|
list_del(&neh->list_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* uwb_rc_neh_rm - remove a neh.
|
|
|
|
* @rc: the radio controller
|
|
|
|
* @neh: the neh to remove
|
|
|
|
*
|
|
|
|
* Remove an active neh immediately instead of waiting for the event
|
|
|
|
* (or a time out).
|
|
|
|
*/
|
|
|
|
void uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&rc->neh_lock, flags);
|
|
|
|
__uwb_rc_neh_rm(rc, neh);
|
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
|
2008-11-08 02:19:19 +08:00
|
|
|
del_timer_sync(&neh->timer);
|
2008-09-17 23:34:07 +08:00
|
|
|
uwb_rc_neh_put(neh);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* uwb_rc_neh_arm - arm an event handler timeout timer
|
|
|
|
*
|
|
|
|
* @rc: UWB Radio Controller
|
|
|
|
* @neh: Notification/event handler for @rc
|
|
|
|
*
|
|
|
|
* The timer is only armed if the neh is active.
|
|
|
|
*/
|
|
|
|
void uwb_rc_neh_arm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&rc->neh_lock, flags);
|
|
|
|
if (neh->context)
|
|
|
|
mod_timer(&neh->timer,
|
|
|
|
jiffies + msecs_to_jiffies(UWB_RC_CMD_TIMEOUT_MS));
|
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uwb_rc_neh_cb(struct uwb_rc_neh *neh, struct uwb_rceb *rceb, size_t size)
|
|
|
|
{
|
|
|
|
(*neh->cb)(neh->rc, neh->arg, rceb, size);
|
|
|
|
uwb_rc_neh_put(neh);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool uwb_rc_neh_match(struct uwb_rc_neh *neh, const struct uwb_rceb *rceb)
|
|
|
|
{
|
|
|
|
return neh->evt_type == rceb->bEventType
|
|
|
|
&& neh->evt == rceb->wEvent
|
|
|
|
&& neh->context == rceb->bEventContext;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find the handle waiting for a RC Radio Control Event
|
|
|
|
*
|
|
|
|
* @rc: UWB Radio Controller
|
|
|
|
* @rceb: Pointer to the RCEB buffer
|
|
|
|
* @event_size: Pointer to the size of the RCEB buffer. Might be
|
|
|
|
* adjusted to take into account the @neh->extra_size
|
|
|
|
* settings.
|
|
|
|
*
|
|
|
|
* If the listener has no buffer (NULL buffer), one is allocated for
|
|
|
|
* the right size (the amount of data received). @neh->ptr will point
|
|
|
|
* to the event payload, which always starts with a 'struct
|
|
|
|
* uwb_rceb'. kfree() it when done.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
struct uwb_rc_neh *uwb_rc_neh_lookup(struct uwb_rc *rc,
|
|
|
|
const struct uwb_rceb *rceb)
|
|
|
|
{
|
|
|
|
struct uwb_rc_neh *neh = NULL, *h;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&rc->neh_lock, flags);
|
|
|
|
|
|
|
|
list_for_each_entry(h, &rc->neh_list, list_node) {
|
|
|
|
if (uwb_rc_neh_match(h, rceb)) {
|
|
|
|
neh = h;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (neh)
|
|
|
|
__uwb_rc_neh_rm(rc, neh);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
|
|
|
|
return neh;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-12 21:28:48 +08:00
|
|
|
/*
|
2008-09-17 23:34:07 +08:00
|
|
|
* Process notifications coming from the radio control interface
|
|
|
|
*
|
|
|
|
* @rc: UWB Radio Control Interface descriptor
|
|
|
|
* @neh: Notification/Event Handler @neh->ptr points to
|
|
|
|
* @uwb_evt->buffer.
|
|
|
|
*
|
|
|
|
* This function is called by the event/notif handling subsystem when
|
|
|
|
* notifications arrive (hwarc_probe() arms a notification/event handle
|
|
|
|
* that calls back this function for every received notification; this
|
|
|
|
* function then will rearm itself).
|
|
|
|
*
|
|
|
|
* Notification data buffers are dynamically allocated by the NEH
|
|
|
|
* handling code in neh.c [uwb_rc_neh_lookup()]. What is actually
|
|
|
|
* allocated is space to contain the notification data.
|
|
|
|
*
|
|
|
|
* Buffers are prefixed with a Radio Control Event Block (RCEB) as
|
|
|
|
* defined by the WUSB Wired-Adapter Radio Control interface. We
|
|
|
|
* just use it for the notification code.
|
|
|
|
*
|
|
|
|
* On each case statement we just transcode endianess of the different
|
|
|
|
* fields. We declare a pointer to a RCI definition of an event, and
|
|
|
|
* then to a UWB definition of the same event (which are the same,
|
|
|
|
* remember). Event if we use different pointers
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void uwb_rc_notif(struct uwb_rc *rc, struct uwb_rceb *rceb, ssize_t size)
|
|
|
|
{
|
|
|
|
struct device *dev = &rc->uwb_dev.dev;
|
|
|
|
struct uwb_event *uwb_evt;
|
|
|
|
|
|
|
|
if (size == -ESHUTDOWN)
|
|
|
|
return;
|
|
|
|
if (size < 0) {
|
|
|
|
dev_err(dev, "ignoring event with error code %zu\n",
|
|
|
|
size);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uwb_evt = kzalloc(sizeof(*uwb_evt), GFP_ATOMIC);
|
|
|
|
if (unlikely(uwb_evt == NULL)) {
|
|
|
|
dev_err(dev, "no memory to queue event 0x%02x/%04x/%02x\n",
|
|
|
|
rceb->bEventType, le16_to_cpu(rceb->wEvent),
|
|
|
|
rceb->bEventContext);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
uwb_evt->rc = __uwb_rc_get(rc); /* will be put by uwbd's uwbd_event_handle() */
|
|
|
|
uwb_evt->ts_jiffies = jiffies;
|
|
|
|
uwb_evt->type = UWB_EVT_TYPE_NOTIF;
|
|
|
|
uwb_evt->notif.size = size;
|
|
|
|
uwb_evt->notif.rceb = rceb;
|
|
|
|
|
|
|
|
uwbd_event_queue(uwb_evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uwb_rc_neh_grok_event(struct uwb_rc *rc, struct uwb_rceb *rceb, size_t size)
|
|
|
|
{
|
|
|
|
struct device *dev = &rc->uwb_dev.dev;
|
|
|
|
struct uwb_rc_neh *neh;
|
|
|
|
struct uwb_rceb *notif;
|
2012-04-16 21:28:28 +08:00
|
|
|
unsigned long flags;
|
2008-09-17 23:34:07 +08:00
|
|
|
|
|
|
|
if (rceb->bEventContext == 0) {
|
|
|
|
notif = kmalloc(size, GFP_ATOMIC);
|
|
|
|
if (notif) {
|
|
|
|
memcpy(notif, rceb, size);
|
|
|
|
uwb_rc_notif(rc, notif, size);
|
|
|
|
} else
|
|
|
|
dev_err(dev, "event 0x%02x/%04x/%02x (%zu bytes): no memory\n",
|
|
|
|
rceb->bEventType, le16_to_cpu(rceb->wEvent),
|
|
|
|
rceb->bEventContext, size);
|
|
|
|
} else {
|
|
|
|
neh = uwb_rc_neh_lookup(rc, rceb);
|
2008-11-08 02:19:19 +08:00
|
|
|
if (neh) {
|
2012-04-16 21:28:28 +08:00
|
|
|
spin_lock_irqsave(&rc->neh_lock, flags);
|
|
|
|
/* to guard against a timeout */
|
|
|
|
neh->completed = 1;
|
|
|
|
del_timer(&neh->timer);
|
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
2008-09-17 23:34:07 +08:00
|
|
|
uwb_rc_neh_cb(neh, rceb, size);
|
2008-11-08 02:19:19 +08:00
|
|
|
} else
|
2008-09-17 23:34:07 +08:00
|
|
|
dev_warn(dev, "event 0x%02x/%04x/%02x (%zu bytes): nobody cared\n",
|
|
|
|
rceb->bEventType, le16_to_cpu(rceb->wEvent),
|
|
|
|
rceb->bEventContext, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a buffer with one or more UWB RC events/notifications, break
|
|
|
|
* them up and dispatch them.
|
|
|
|
*
|
|
|
|
* @rc: UWB Radio Controller
|
|
|
|
* @buf: Buffer with the stream of notifications/events
|
|
|
|
* @buf_size: Amount of data in the buffer
|
|
|
|
*
|
|
|
|
* Note each notification/event starts always with a 'struct
|
|
|
|
* uwb_rceb', so the minimum size if 4 bytes.
|
|
|
|
*
|
|
|
|
* The device may pass us events formatted differently than expected.
|
|
|
|
* These are first filtered, potentially creating a new event in a new
|
|
|
|
* memory location. If a new event is created by the filter it is also
|
|
|
|
* freed here.
|
|
|
|
*
|
|
|
|
* For each notif/event, tries to guess the size looking at the EST
|
|
|
|
* tables, then looks for a neh that is waiting for that event and if
|
|
|
|
* found, copies the payload to the neh's buffer and calls it back. If
|
|
|
|
* not, the data is ignored.
|
|
|
|
*
|
|
|
|
* Note that if we can't find a size description in the EST tables, we
|
|
|
|
* still might find a size in the 'neh' handle in uwb_rc_neh_lookup().
|
|
|
|
*
|
|
|
|
* Assumptions:
|
|
|
|
*
|
|
|
|
* @rc->neh_lock is NOT taken
|
|
|
|
*
|
|
|
|
* We keep track of various sizes here:
|
|
|
|
* size: contains the size of the buffer that is processed for the
|
|
|
|
* incoming event. this buffer may contain events that are not
|
|
|
|
* formatted as WHCI.
|
|
|
|
* real_size: the actual space taken by this event in the buffer.
|
|
|
|
* We need to keep track of the real size of an event to be able to
|
|
|
|
* advance the buffer correctly.
|
|
|
|
* event_size: the size of the event as expected by the core layer
|
|
|
|
* [OR] the size of the event after filtering. if the filtering
|
|
|
|
* created a new event in a new memory location then this is
|
|
|
|
* effectively the size of a new event buffer
|
|
|
|
*/
|
|
|
|
void uwb_rc_neh_grok(struct uwb_rc *rc, void *buf, size_t buf_size)
|
|
|
|
{
|
|
|
|
struct device *dev = &rc->uwb_dev.dev;
|
|
|
|
void *itr;
|
|
|
|
struct uwb_rceb *rceb;
|
|
|
|
size_t size, real_size, event_size;
|
|
|
|
int needtofree;
|
|
|
|
|
|
|
|
itr = buf;
|
|
|
|
size = buf_size;
|
|
|
|
while (size > 0) {
|
|
|
|
if (size < sizeof(*rceb)) {
|
|
|
|
dev_err(dev, "not enough data in event buffer to "
|
|
|
|
"process incoming events (%zu left, minimum is "
|
|
|
|
"%zu)\n", size, sizeof(*rceb));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
rceb = itr;
|
|
|
|
if (rc->filter_event) {
|
|
|
|
needtofree = rc->filter_event(rc, &rceb, size,
|
|
|
|
&real_size, &event_size);
|
|
|
|
if (needtofree < 0 && needtofree != -ENOANO) {
|
|
|
|
dev_err(dev, "BUG: Unable to filter event "
|
|
|
|
"(0x%02x/%04x/%02x) from "
|
|
|
|
"device. \n", rceb->bEventType,
|
|
|
|
le16_to_cpu(rceb->wEvent),
|
|
|
|
rceb->bEventContext);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
needtofree = -ENOANO;
|
|
|
|
/* do real processing if there was no filtering or the
|
|
|
|
* filtering didn't act */
|
|
|
|
if (needtofree == -ENOANO) {
|
|
|
|
ssize_t ret = uwb_est_find_size(rc, rceb, size);
|
|
|
|
if (ret < 0)
|
|
|
|
break;
|
|
|
|
if (ret > size) {
|
|
|
|
dev_err(dev, "BUG: hw sent incomplete event "
|
|
|
|
"0x%02x/%04x/%02x (%zd bytes), only got "
|
|
|
|
"%zu bytes. We don't handle that.\n",
|
|
|
|
rceb->bEventType, le16_to_cpu(rceb->wEvent),
|
|
|
|
rceb->bEventContext, ret, size);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
real_size = event_size = ret;
|
|
|
|
}
|
|
|
|
uwb_rc_neh_grok_event(rc, rceb, event_size);
|
|
|
|
|
|
|
|
if (needtofree == 1)
|
|
|
|
kfree(rceb);
|
|
|
|
|
|
|
|
itr += real_size;
|
|
|
|
size -= real_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(uwb_rc_neh_grok);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The entity that reads from the device notification/event channel has
|
|
|
|
* detected an error.
|
|
|
|
*
|
|
|
|
* @rc: UWB Radio Controller
|
|
|
|
* @error: Errno error code
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void uwb_rc_neh_error(struct uwb_rc *rc, int error)
|
|
|
|
{
|
2008-11-08 02:19:19 +08:00
|
|
|
struct uwb_rc_neh *neh;
|
2008-09-17 23:34:07 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
2008-11-08 02:19:19 +08:00
|
|
|
for (;;) {
|
|
|
|
spin_lock_irqsave(&rc->neh_lock, flags);
|
|
|
|
if (list_empty(&rc->neh_list)) {
|
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
neh = list_first_entry(&rc->neh_list, struct uwb_rc_neh, list_node);
|
2008-09-17 23:34:07 +08:00
|
|
|
__uwb_rc_neh_rm(rc, neh);
|
2008-11-08 02:19:19 +08:00
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
|
|
|
|
del_timer_sync(&neh->timer);
|
2008-09-17 23:34:07 +08:00
|
|
|
uwb_rc_neh_cb(neh, NULL, error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(uwb_rc_neh_error);
|
|
|
|
|
|
|
|
|
|
|
|
static void uwb_rc_neh_timer(unsigned long arg)
|
|
|
|
{
|
|
|
|
struct uwb_rc_neh *neh = (struct uwb_rc_neh *)arg;
|
|
|
|
struct uwb_rc *rc = neh->rc;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&rc->neh_lock, flags);
|
2012-04-16 21:28:28 +08:00
|
|
|
if (neh->completed) {
|
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
2008-11-08 02:19:19 +08:00
|
|
|
if (neh->context)
|
|
|
|
__uwb_rc_neh_rm(rc, neh);
|
|
|
|
else
|
|
|
|
neh = NULL;
|
2008-09-17 23:34:07 +08:00
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
|
2008-11-08 02:19:19 +08:00
|
|
|
if (neh)
|
|
|
|
uwb_rc_neh_cb(neh, NULL, -ETIMEDOUT);
|
2008-09-17 23:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Initializes the @rc's neh subsystem
|
|
|
|
*/
|
|
|
|
void uwb_rc_neh_create(struct uwb_rc *rc)
|
|
|
|
{
|
|
|
|
spin_lock_init(&rc->neh_lock);
|
|
|
|
INIT_LIST_HEAD(&rc->neh_list);
|
|
|
|
set_bit(0, rc->ctx_bm); /* 0 is reserved (see [WUSB] table 8-65) */
|
|
|
|
set_bit(0xff, rc->ctx_bm); /* and 0xff is invalid */
|
|
|
|
rc->ctx_roll = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Release's the @rc's neh subsystem */
|
|
|
|
void uwb_rc_neh_destroy(struct uwb_rc *rc)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
2008-11-08 02:19:19 +08:00
|
|
|
struct uwb_rc_neh *neh;
|
2008-09-17 23:34:07 +08:00
|
|
|
|
2008-11-08 02:19:19 +08:00
|
|
|
for (;;) {
|
|
|
|
spin_lock_irqsave(&rc->neh_lock, flags);
|
|
|
|
if (list_empty(&rc->neh_list)) {
|
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
neh = list_first_entry(&rc->neh_list, struct uwb_rc_neh, list_node);
|
2008-09-17 23:34:07 +08:00
|
|
|
__uwb_rc_neh_rm(rc, neh);
|
2008-11-08 02:19:19 +08:00
|
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
|
|
|
|
del_timer_sync(&neh->timer);
|
2008-09-17 23:34:07 +08:00
|
|
|
uwb_rc_neh_put(neh);
|
|
|
|
}
|
|
|
|
}
|