usb: Move driver into library

This raised the opportunity to structure the library more cleanly for each
supported platform.
This commit is contained in:
Sebastian Sumpf
2013-02-20 17:15:29 +01:00
committed by Norman Feske
parent ebc76fc13b
commit 6ef3f0f153
37 changed files with 281 additions and 275 deletions

View File

@ -0,0 +1,240 @@
/*
* \brief Signal context for completions and events
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-05-23
*/
/*
* Copyright (C) 2012-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <signal.h>
#include <lx_emul.h>
static Signal_helper *_signal = 0;
/**
* Context for events
*/
class Event_context : public Driver_context
{
private:
Genode::Signal_context_capability _ctx_cap;
Event_context()
: _ctx_cap(_signal->receiver()->manage(this)) {
_signal->sender()->context(_ctx_cap); }
public:
static Event_context *e()
{
static Event_context _e;
return &_e;
}
void submit() {
_signal->sender()->submit(); }
void handle() {
Routine::schedule_all(); }
char const *debug() { return "Event_context"; }
};
void Event::init(Genode::Signal_receiver *recv) {
_signal = new (Genode::env()->heap()) Signal_helper(recv); }
/**
* Delayed work
*/
class Work : public Genode::List<Work>::Element
{
private:
void *_work;
bool _delayed;
static Genode::List<Work> *_list()
{
static Genode::List<Work> _l;
return &_l;
}
public:
Work(void *work, bool delayed) : _work(work), _delayed(delayed) { }
static void schedule(void *work, bool delayed)
{
Work *w = new (Genode::env()->heap()) Work(work, delayed);
_list()->insert(w);
}
static void exec()
{
while (_list()->first()) {
Work *w = _list()->first();
_list()->remove(w);
if (w->_delayed) {
delayed_work *work = static_cast<delayed_work *>(w->_work);
work->work.func(&(work)->work);
}
else {
work_struct *work = static_cast<work_struct *>(w->_work);
work->func(work);
}
destroy(Genode::env()->heap(), w);
}
}
};
/************************
** linux/completion.h **
************************/
void __wake_up() { Routine::schedule_all(); }
void __wait_event()
{
/* schedule work first */
Work::exec();
/* schedule other routines or wait for signals */
Service_handler::s()->process();
}
void init_completion(struct completion *work)
{
dde_kit_log(DEBUG_COMPLETION, "New completion %p", work);
work->done = 0;
}
void complete(struct completion *work)
{
dde_kit_log(DEBUG_COMPLETION, "%p", work);
work->done = 1;
/* send signal */
Event_context::e()->submit();
}
void complete_and_exit(struct completion *work, long code)
{
dde_kit_log(DEBUG_COMPLETION, "%p", work);
complete(work);
Routine::remove();
}
static void __wait_completion(struct completion *work)
{
while (!work->done)
__wait_event();
work->done = 0;
}
static unsigned long
__wait_completion_timeout(struct completion *work, unsigned long timeout)
{
unsigned long _j = jiffies + (timeout / HZ);
while (!work->done) {
__wait_event();
if (_j >= jiffies)
return 0;
}
work->done = 0;
return _j - jiffies;
}
unsigned long wait_for_completion_timeout(struct completion *work,
unsigned long timeout)
{
dde_kit_log(DEBUG_COMPLETION, "%p state: %u", work, work->done);
__wait_completion(work);
return 1;
}
int wait_for_completion_interruptible(struct completion *work)
{
dde_kit_log(DEBUG_COMPLETION, "%p state: %u", work, work->done);
__wait_completion(work);
return 0;
}
long wait_for_completion_interruptible_timeout(struct completion *work,
unsigned long timeout)
{
dde_kit_log(DEBUG_COMPLETION, "%p state: %u", work, work->done);
__wait_completion(work);
return 1;
}
void wait_for_completion(struct completion *work)
{
dde_kit_log(DEBUG_COMPLETION, "%p state: %u", work, work->done);
__wait_completion(work);
}
/*******************
** linux/timer.h **
*******************/
signed long schedule_timeout_uninterruptible(signed long timeout)
{
dde_kit_log(DEBUG_COMPLETION, "%ld\n", timeout);
__wait_event();
return 0;
}
int wake_up_process(struct task_struct *tsk)
{
Routine::schedule_all();
return 0;
}
/***********************
** linux/workquque.h **
***********************/
int schedule_delayed_work(struct delayed_work *work, unsigned long delay)
{
Work::schedule((void *)work, true);
//work->work.func(&(work)->work);
return 0;
}
int schedule_work(struct work_struct *work)
{
Work::schedule((void *)work, false);
return 1;
}

View File

@ -0,0 +1,214 @@
/*
* \brief Signal context for IRQ's
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-05-23
*/
/*
* Copyright (C) 2012-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <signal.h>
#include <lx_emul.h>
extern "C" {
#include <dde_kit/interrupt.h>
}
/* our local incarnation of sender and receiver */
static Signal_helper *_signal = 0;
static Genode::Lock _irq_sync(Genode::Lock::LOCKED);
static Genode::Lock _irq_wait(Genode::Lock::LOCKED);
/**
* This contains the Linux-driver handlers
*/
struct Irq_handler : Genode::List<Irq_handler>::Element
{
void *dev; /* Linux device */
irq_handler_t handler; /* Linux handler */
Irq_handler(void *dev, irq_handler_t handler)
: dev(dev), handler(handler) { }
};
/**
* Signal context for IRQs
*/
class Irq_context : public Driver_context,
public Genode::List<Irq_context>::Element
{
private:
typedef Genode::List<Irq_context>::Element LE;
unsigned int _irq; /* IRQ number */
Genode::List<Irq_handler> _handler_list; /* List of registered handlers */
Genode::Signal_context_capability _ctx_cap; /* capability for this context */
static Genode::List<Irq_context> *_list()
{
static Genode::List<Irq_context> _l;
return &_l;
}
/**
* Find context for given IRQ number
*/
static Irq_context *_find_ctx(unsigned int irq)
{
for (Irq_context *i = _list()->first(); i; i = i->LE::next())
if (i->_irq == irq)
return i;
return 0;
}
/* called by the DDE kit upon IRQ */
static void _dde_handler(void *irq)
{
/*
* Make sure there is only one interrupt handled at a time, since dde_kit
* will use one thread per IRQ
*/
static Genode::Lock handler_lock;
Genode::Lock::Guard guard(handler_lock);
/* unlock if main thread is waiting */
_irq_wait.unlock();
Irq_context *ctx = static_cast<Irq_context *>(irq);
/* set context & submit signal */
_signal->sender()->context(ctx->_ctx_cap);
_signal->sender()->submit();
/* wait for interrupt to get acked at device side */
_irq_sync.lock();
}
/**
* Call one IRQ handler
*/
inline bool _handle_one(Irq_handler *h)
{
bool handled = false;
/*
* It might be that the next interrupt triggers right after the device has
* acknowledged the IRQ
*/
do {
if (h->handler(_irq, h->dev) != IRQ_HANDLED)
return handled;
handled = true;
} while (true);
}
/**
* Call all handlers registered for this context
*/
bool _handle()
{
bool handled = false;
/* report IRQ to all clients */
for (Irq_handler *h = _handler_list.first(); h; h = h->next()) {
handled |= _handle_one(h);
dde_kit_log(DEBUG_IRQ, "IRQ: %u ret: %u h: %p dev: %p", _irq, handled, h->handler, h->dev);
}
/* interrupt should be acked at device now */
_irq_sync.unlock();
if (handled)
Routine::schedule_all();
return handled;
}
public:
Irq_context(unsigned int irq)
: _irq(irq),
_ctx_cap(_signal->receiver()->manage(this))
{
/* register at DDE (shared) */
int ret = dde_kit_interrupt_attach(_irq, 0, 0, _dde_handler, this);
if (ret)
PERR("Interrupt attach return %d for IRQ %u", ret, irq);
dde_kit_interrupt_enable(_irq);
_list()->insert(this);
}
inline void handle() { _handle(); }
const char *debug() { return "Irq_context"; }
/**
* Request an IRQ
*/
static void request_irq(unsigned int irq, irq_handler_t handler, void *dev)
{
Irq_handler *h = new(Genode::env()->heap()) Irq_handler(dev, handler);
Irq_context *ctx = _find_ctx(irq);
/* if this IRQ is not registered */
if (!ctx)
ctx = new (Genode::env()->heap()) Irq_context(irq);
/* register Linux handler */
ctx->_handler_list.insert(h);
}
static bool check_irq()
{
bool handled = false;
for (Irq_context *i = _list()->first(); i; i = i->LE::next())
handled |= i->_handle();
return handled;
}
static void wait()
{
_irq_wait.lock();
check_irq();
}
};
void Irq::init(Genode::Signal_receiver *recv) {
_signal = new (Genode::env()->heap()) Signal_helper(recv); }
void Irq::check_irq()
{
if (!Irq_context::check_irq())
Irq_context::wait();
}
/***********************
** linux/interrupt.h **
***********************/
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
dde_kit_log(DEBUG_IRQ, "Request irq %u handler %p", irq, handler);
Irq_context::request_irq(irq, handler, dev);
return 0;
}

View File

@ -0,0 +1,151 @@
/*
* \brief Signal context for timer events
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-05-23
*/
/*
* Copyright (C) 2012-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <base/env.h>
#include <base/printf.h>
#include <lx_emul.h>
#include "signal.h"
static void handler(void *timer);
/* our local incarnation of sender and receiver */
static Signal_helper *_signal = 0;
/**
* Signal context for time-outs
*/
class Timer_context : public Driver_context
{
private:
timer_list *_timer; /* Linux timer */
dde_kit_timer *_dde_timer; /* DDE kit timer */
Genode::Signal_context_capability _ctx_cap; /* Signal-context cap
for this timer */
public:
Timer_context(timer_list *timer)
: _timer(timer), _dde_timer(0),
_ctx_cap(_signal->receiver()->manage(this)) { }
~Timer_context()
{
_signal->receiver()->dissolve(this);
}
/* call timer function */
void handle() { _timer->function(_timer->data); }
/* schedule next timeout */
void schedule(unsigned long expires)
{
if (!_dde_timer)
_dde_timer = dde_kit_timer_add(handler, this, expires);
else
dde_kit_timer_schedule_absolute(_dde_timer, expires);
}
char const *debug() { return "Timer_context"; }
/**
* Return true if timer is pending
*/
bool pending() const
{
return _dde_timer ? dde_kit_timer_pending(_dde_timer) : false;
}
/**
* Return internal signal cap
*/
Genode::Signal_context_capability cap() const { return _ctx_cap; }
/**
* Convert 'timer_list' to 'Timer_conext'
*/
static Timer_context *to_ctx(timer_list const *timer) {
return static_cast<Timer_context *>(timer->timer); }
void remove()
{
if (_dde_timer)
dde_kit_timer_del(_dde_timer);
_dde_timer = 0;
}
timer_list *l() { return _timer; }
};
/**
* C handler for DDE timer interface
*/
static void handler(void *timer)
{
Timer_context *t = static_cast<Timer_context *>(timer);
/* set context and submit */
_signal->sender()->context(t->cap());
_signal->sender()->submit();
}
void Timer::init(Genode::Signal_receiver *recv) {
_signal = new (Genode::env()->heap()) Signal_helper(recv); }
/*******************
** linux/timer.h **
*******************/
void init_timer(struct timer_list *timer) {
timer->timer = (void *) new (Genode::env()->heap()) Timer_context(timer); }
int mod_timer(struct timer_list *timer, unsigned long expires)
{
dde_kit_log(DEBUG_TIMER, "Timer: %p j: %lu ex: %lu func %p",
timer, jiffies, expires, timer->function);
Timer_context::to_ctx(timer)->schedule(expires);
return 0;
}
void setup_timer(struct timer_list *timer,void (*function)(unsigned long),
unsigned long data)
{
timer->function = function;
timer->data = data;
init_timer(timer);
}
int timer_pending(const struct timer_list * timer)
{
bool pending = Timer_context::to_ctx(timer)->pending();
dde_kit_log(DEBUG_TIMER, "Pending %p %u", timer, pending);
return pending;
}
int del_timer(struct timer_list *timer)
{
dde_kit_log(DEBUG_TIMER, "Delete timer %p", timer);
Timer_context::to_ctx(timer)->remove();
return 0;
}