mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
![Kees Cook](/assets/img/avatar_default.png)
This converts all remaining cases of the old setup_timer() API into using timer_setup(), where the callback argument is the structure already holding the struct timer_list. These should have no behavioral changes, since they just change which pointer is passed into the callback with the same available pointers after conversion. It handles the following examples, in addition to some other variations. Casting from unsigned long: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... setup_timer(&ptr->my_timer, my_callback, ptr); and forced object casts: void my_callback(struct something *ptr) { ... } ... setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr); become: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... timer_setup(&ptr->my_timer, my_callback, 0); Direct function assignments: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... ptr->my_timer.function = my_callback; have a temporary cast added, along with converting the args: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback; And finally, callbacks without a data assignment: void my_callback(unsigned long data) { ... } ... setup_timer(&ptr->my_timer, my_callback, 0); have their argument renamed to verify they're unused during conversion: void my_callback(struct timer_list *unused) { ... } ... timer_setup(&ptr->my_timer, my_callback, 0); The conversion is done with the following Coccinelle script: spatch --very-quiet --all-includes --include-headers \ -I ./arch/x86/include -I ./arch/x86/include/generated \ -I ./include -I ./arch/x86/include/uapi \ -I ./arch/x86/include/generated/uapi -I ./include/uapi \ -I ./include/generated/uapi --include ./include/linux/kconfig.h \ --dir . \ --cocci-file ~/src/data/timer_setup.cocci @fix_address_of@ expression e; @@ setup_timer( -&(e) +&e , ...) // Update any raw setup_timer() usages that have a NULL callback, but // would otherwise match change_timer_function_usage, since the latter // will update all function assignments done in the face of a NULL // function initialization in setup_timer(). @change_timer_function_usage_NULL@ expression _E; identifier _timer; type _cast_data; @@ ( -setup_timer(&_E->_timer, NULL, _E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E->_timer, NULL, (_cast_data)_E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E._timer, NULL, &_E); +timer_setup(&_E._timer, NULL, 0); | -setup_timer(&_E._timer, NULL, (_cast_data)&_E); +timer_setup(&_E._timer, NULL, 0); ) @change_timer_function_usage@ expression _E; identifier _timer; struct timer_list _stl; identifier _callback; type _cast_func, _cast_data; @@ ( -setup_timer(&_E->_timer, _callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | _E->_timer@_stl.function = _callback; | _E->_timer@_stl.function = &_callback; | _E->_timer@_stl.function = (_cast_func)_callback; | _E->_timer@_stl.function = (_cast_func)&_callback; | _E._timer@_stl.function = _callback; | _E._timer@_stl.function = &_callback; | _E._timer@_stl.function = (_cast_func)_callback; | _E._timer@_stl.function = (_cast_func)&_callback; ) // callback(unsigned long arg) @change_callback_handle_cast depends on change_timer_function_usage@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; identifier _handle; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { ( ... when != _origarg _handletype *_handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg ) } // callback(unsigned long arg) without existing variable @change_callback_handle_cast_no_arg depends on change_timer_function_usage && !change_callback_handle_cast@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { + _handletype *_origarg = from_timer(_origarg, t, _timer); + ... when != _origarg - (_handletype *)_origarg + _origarg ... when != _origarg } // Avoid already converted callbacks. @match_callback_converted depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier t; @@ void _callback(struct timer_list *t) { ... } // callback(struct something *handle) @change_callback_handle_arg depends on change_timer_function_usage && !match_callback_converted && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; @@ void _callback( -_handletype *_handle +struct timer_list *t ) { + _handletype *_handle = from_timer(_handle, t, _timer); ... } // If change_callback_handle_arg ran on an empty function, remove // the added handler. @unchange_callback_handle_arg depends on change_timer_function_usage && change_callback_handle_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; identifier t; @@ void _callback(struct timer_list *t) { - _handletype *_handle = from_timer(_handle, t, _timer); } // We only want to refactor the setup_timer() data argument if we've found // the matching callback. This undoes changes in change_timer_function_usage. @unchange_timer_function_usage depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg && !change_callback_handle_arg@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type change_timer_function_usage._cast_data; @@ ( -timer_setup(&_E->_timer, _callback, 0); +setup_timer(&_E->_timer, _callback, (_cast_data)_E); | -timer_setup(&_E._timer, _callback, 0); +setup_timer(&_E._timer, _callback, (_cast_data)&_E); ) // If we fixed a callback from a .function assignment, fix the // assignment cast now. @change_timer_function_assignment depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_func; typedef TIMER_FUNC_TYPE; @@ ( _E->_timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -&_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)_callback; +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -&_callback; +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; ) // Sometimes timer functions are called directly. Replace matched args. @change_timer_function_calls depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression _E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_data; @@ _callback( ( -(_cast_data)_E +&_E->_timer | -(_cast_data)&_E +&_E._timer | -_E +&_E->_timer ) ) // If a timer has been configured without a data argument, it can be // converted without regard to the callback argument, since it is unused. @match_timer_function_unused_data@ expression _E; identifier _timer; identifier _callback; @@ ( -setup_timer(&_E->_timer, _callback, 0); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0L); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0UL); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0L); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0UL); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_timer, _callback, 0); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0L); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0UL); +timer_setup(&_timer, _callback, 0); | -setup_timer(_timer, _callback, 0); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0L); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0UL); +timer_setup(_timer, _callback, 0); ) @change_callback_unused_data depends on match_timer_function_unused_data@ identifier match_timer_function_unused_data._callback; type _origtype; identifier _origarg; @@ void _callback( -_origtype _origarg +struct timer_list *unused ) { ... when != _origarg } Signed-off-by: Kees Cook <keescook@chromium.org>
620 lines
18 KiB
C
620 lines
18 KiB
C
/*
|
|
* 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 <linux/slab.h>
|
|
#include <linux/err.h>
|
|
#include <linux/export.h>
|
|
|
|
#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;
|
|
u8 completed;
|
|
uwb_rc_cmd_cb_f cb;
|
|
void *arg;
|
|
|
|
struct timer_list timer;
|
|
struct list_head list_node;
|
|
};
|
|
|
|
static void uwb_rc_neh_timer(struct timer_list *t);
|
|
|
|
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.
|
|
*
|
|
* We spread the allocation around to reduce the possibility of two
|
|
* 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);
|
|
timer_setup(&neh->timer, uwb_rc_neh_timer, 0);
|
|
|
|
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);
|
|
|
|
del_timer_sync(&neh->timer);
|
|
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;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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;
|
|
unsigned long flags;
|
|
|
|
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);
|
|
if (neh) {
|
|
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);
|
|
uwb_rc_neh_cb(neh, rceb, size);
|
|
} else
|
|
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)
|
|
{
|
|
struct uwb_rc_neh *neh;
|
|
unsigned long flags;
|
|
|
|
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);
|
|
__uwb_rc_neh_rm(rc, neh);
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
del_timer_sync(&neh->timer);
|
|
uwb_rc_neh_cb(neh, NULL, error);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(uwb_rc_neh_error);
|
|
|
|
|
|
static void uwb_rc_neh_timer(struct timer_list *t)
|
|
{
|
|
struct uwb_rc_neh *neh = from_timer(neh, t, timer);
|
|
struct uwb_rc *rc = neh->rc;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&rc->neh_lock, flags);
|
|
if (neh->completed) {
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
return;
|
|
}
|
|
if (neh->context)
|
|
__uwb_rc_neh_rm(rc, neh);
|
|
else
|
|
neh = NULL;
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
if (neh)
|
|
uwb_rc_neh_cb(neh, NULL, -ETIMEDOUT);
|
|
}
|
|
|
|
/** 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;
|
|
struct uwb_rc_neh *neh;
|
|
|
|
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);
|
|
__uwb_rc_neh_rm(rc, neh);
|
|
spin_unlock_irqrestore(&rc->neh_lock, flags);
|
|
|
|
del_timer_sync(&neh->timer);
|
|
uwb_rc_neh_put(neh);
|
|
}
|
|
}
|