New DDE-Linux-based USB driver

The new 'dde_linux' repository will host device drivers ported from the
Linux kernel. In contrast to the original 'linux_drivers' repository,
'dde_linux' does not contain any 3rd-party source code. To download the
Linux kernel source code and extract the drivers, execute the 'make
prepare' rule of the top-level Makefile. The initial version of the
'dde_linux' repository comes with an USB driver. The porting methodology
follows the path of the Intel GEM port. Instead of attempting to provide
a generic Linux environment that works across drivers, each driver comes
with a specially tailored DDE.

The DDE consists of Genode-specific implementations of Linux API
functions as declared in 'lx_emul.h'. Most of these functions are
dummies that must merely be provided to resolve dependencies at the
linking stage. They are called by unused code-paths.

As of now, the USB driver support UHCI, EHCI on the x86_32 platform. I
exposes USB HID devices and USB storage devices via Genode's input-session
and block-session respectively.

The USB driver is accompanied with two run scripts 'run/usb_hid.run' and
'run/usb_storage.run'.
This commit is contained in:
Sebastian Sumpf 2012-05-24 21:31:02 +02:00 committed by Christian Helmuth
parent 9471490d01
commit 9f73476b37
27 changed files with 6784 additions and 4 deletions

2
.gitignore vendored
View File

@ -13,6 +13,8 @@
/base-okl4/download /base-okl4/download
/base-pistachio/contrib /base-pistachio/contrib
/dde_ipxe/contrib /dde_ipxe/contrib
/dde_linux/contrib
/dde_linux/download
/libports/contrib /libports/contrib
/libports/download /libports/download
/libports/include/EGL/egl.h /libports/include/EGL/egl.h

11
README
View File

@ -127,10 +127,17 @@ The Genode source tree is composed of the following subdirectories:
For instructions about how to use this mechanism, please consult the README For instructions about how to use this mechanism, please consult the README
file at the top level of the repository. file at the top level of the repository.
:'linux_drivers': :'dde_linux':
This source-code repository contains the device driver environment for This source-code repository contains the device driver environment for
executing Linux device drivers natively on Genode. executing Linux device drivers natively on Genode. Currently, this
repository hosts the USB stack.
:'linux_drivers':
Deprecated version of the Linux device driver environment featuring audio
drivers and the Intel GEM driver. This repository is in a transitionary
phase. Its functionality will be incorporated into 'dde_linux'.
:'dde_ipxe': :'dde_ipxe':

98
dde_linux/Makefile Normal file
View File

@ -0,0 +1,98 @@
#
# \brief Download integrate Linux kernel sources with Genode
# \author Norman Feske
# \date 2012-01-28
CONTRIB_DIR = contrib
DOWNLOAD_DIR = download
VERBOSE ?= @
ECHO = @echo
PATCHES = $(shell find patches -name *.patch)
LINUX = linux-3.2.2
LINUX_TBZ2 = $(LINUX).tar.bz2
LINUX_URL = http://www.kernel.org/pub/linux/kernel/v3.x/$(LINUX_TBZ2)
# Linux utilities
CONTENT += include/linux/list.h
CONTENT += $(addprefix include/linux/,pci_ids.h pci_regs.h usb.h hid.h hiddev.h input.h mod_devicetable.h)
CONTENT += $(addprefix include/linux/byteorder/,generic.h little_endian.h)
CONTENT += include/linux/swab.h
CONTENT += $(addprefix include/asm-generic/bitops/,__ffs.h non-atomic.h)
# USB core
CONTENT += drivers/usb/core
CONTENT += drivers/usb/usb-common.c
CONTENT_INCLUDE_USB := ch11.h ch9.h ehci_def.h hcd.h input.h otg.h quirks.h storage.h
CONTENT += $(addprefix include/linux/usb/,$(CONTENT_INCLUDE_USB))
# needed by usb/core/devio.c
CONTENT += include/linux/usbdevice_fs.h include/asm-generic/ioctl.h
# USB host-controller driver
CONTENT_USB_HOST := ehci.h ehci-hcd.c ehci-hub.c ehci-dbg.c ehci-mem.c \
ehci-q.c ehci-pci.c ehci-sched.c ehci-sysfs.c
CONTENT_USB_HOST += ohci.h ohci-hcd.c ohci-hub.c ohci-dbg.c ohci-mem.c \
ohci-q.c ohci-pci.c ehci-lpm.c
CONTENT_USB_HOST += uhci-hcd.h uhci-hcd.c uhci-debug.c uhci-q.c uhci-hub.c \
uhci-pci.c
CONTENT_USB_HOST += pci-quirks.h pci-quirks.c xhci-ext-caps.h
CONTENT += $(addprefix drivers/usb/host/,$(CONTENT_USB_HOST))
# USB storage driver
CONTENT += drivers/usb/storage/
CONTENT += include/linux/usb_usual.h
# SCSI support for storage
CONTENT += $(addprefix drivers/scsi/,scsi.h scsi.c constants.c scsi_priv.h scsi_logging.h)
CONTENT += $(addprefix include/scsi/,scsi.h scsi_host.h)
# USB hid driver
CONTENT += drivers/hid/hid-input.c drivers/hid/hid-core.c drivers/hid/hid-ids.h
CONTENT += drivers/hid/usbhid
# needed by USB hid
CONTENT_INPUT := input.c evdev.c input-compat.h
CONTENT += $(addprefix drivers/input/,$(CONTENT_INPUT))
CONTENT += include/linux/input/mt.h
CONTRIB_CONTENT := $(addprefix $(CONTRIB_DIR)/,$(CONTENT))
#
# Print help information by default
#
help:
$(ECHO)
$(ECHO) "Download integrate Linux kernel sources with Genode"
$(ECHO)
$(ECHO) "--- available commands ---"
$(ECHO) "prepare - download and integrate Linux source code"
$(ECHO) "clean - remove contib sources except downloaded archives"
$(ECHO) "cleanall - remove contib sources and downloaded archives"
$(ECHO)
prepare: $(CONTRIB_DIR)/.prepared
$(CONTRIB_DIR)/.prepared: Makefile
$(CONTRIB_DIR)/.prepared: $(DOWNLOAD_DIR)/$(LINUX_TBZ2)
$(ECHO) "extracting source code to '$(CONTRIB_DIR)'"
$(VERBOSE)tar xfj $< --transform "s/$(LINUX)/$(CONTRIB_DIR)/" $(addprefix $(LINUX)/,$(CONTENT))
$(VERBOSE)touch $@
$(ECHO) "applying patches to '$(CONTRIB_DIR)/'"
$(VERBOSE)for i in $(PATCHES); do patch -d $(CONTRIB_DIR) -p1 < $$i; done
$(DOWNLOAD_DIR):
$(VERBOSE)mkdir -p $@
$(DOWNLOAD_DIR)/$(LINUX_TBZ2): $(DOWNLOAD_DIR)
$(ECHO) "downloading source code to '$@'"
$(VERBOSE)cd $(DOWNLOAD_DIR); wget -c $(LINUX_URL)
$(VERBOSE)touch $@
clean:
$(VERBOSE)rm -f $(CONTRIB_DIR)
cleanall: clean
$(VERBOSE)rm -rf $(DOWNLOAD_DIR)

37
dde_linux/README Normal file
View File

@ -0,0 +1,37 @@
Device drivers ported from the Linux kernel
USB
###
HID
~~~
Supports keyboard and mouse. A run script can be found under 'run/usb_hid.run'.
Configuration snippet:
!<start name="usb_drv">
! <resource name="RAM" quantum="3M"/>
! <provides><service name="Input"/></provides>
! <config>
! <hid/>
! </config>
!</start>
Note: It has been observed that certain 1.0 versions of Qemu do not generate
mouse interrupts. The mouse driver should work correctly on Qemu 1.0.93 and
above.
STORAGE
~~~~~~~
Currently supports one USB storage device. Hot plugging has not been tested. A
run script can be found under 'run/usb_storage.run'.
Configuration snippet:
!<start name="usb_drv">
! <resource name="RAM" quantum="2M"/>
! <provides> <service name="Block"/> </provides>
! <config><storage /></config>
!</start>

View File

@ -0,0 +1,22 @@
diff -r 1d04c9c5fa8a drivers/input/evdev.c
--- a/drivers/input/evdev.c Tue Apr 17 16:14:54 2012 +0200
+++ b/drivers/input/evdev.c Wed Apr 18 11:25:37 2012 +0200
@@ -957,6 +957,9 @@
if (error)
goto err_cleanup_evdev;
+ evdev_open_device(evdev);
+ dev_info(evdev, "%s\n", dev->name);
+
return 0;
err_cleanup_evdev:
@@ -986,7 +989,7 @@
MODULE_DEVICE_TABLE(input, evdev_ids);
static struct input_handler evdev_handler = {
- .event = evdev_event,
+ .event = genode_evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,

75
dde_linux/run/usb_hid.run Normal file
View File

@ -0,0 +1,75 @@
#
# Build
#
set build_components {
core init
drivers/timer
drivers/pci
drivers/usb
test/input
}
build $build_components
create_boot_directory
#
# Generate config
#
append config {
<config>
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL" />
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="pci_drv">
<resource name="RAM" quantum="1M"/>
<provides><service name="PCI"/></provides>
</start>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="usb_drv">
<resource name="RAM" quantum="3M"/>
<provides><service name="Input"/></provides>
<config>
<hid/>
</config>
</start>
<start name="test-input">
<resource name="RAM" quantum="1M"/>
</start>
</config>}
install_config $config
#
# Boot modules
#
# generic modules
set boot_modules {
core init timer pci_drv usb_drv test-input
}
build_boot_image $boot_modules
append qemu_args " -m 256 -usb -usbdevice mouse -usbdevice keyboard"
run_genode_until forever

View File

@ -0,0 +1,107 @@
#
# \brief Test for using the Block (Storage) service of usb_drv
# \author Christian Prochaska
# \date 2011-06-24
#
#
# Build
#
build {
core init
drivers/pci
drivers/timer
drivers/usb
test/ahci
}
create_boot_directory
#
# Generate config
#
set config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="RAM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="CAP"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
<service name="SIGNAL"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<start name="timer">
<resource name="RAM" quantum="512K"/>
<provides> <service name="Timer"/> </provides>
</start>
<start name="usb_drv">
<resource name="RAM" quantum="3M"/>
<provides> <service name="Block"/> </provides>
<config><storage /></config>
</start>
<start name="pci_drv">
<resource name="RAM" quantum="512K"/>
<provides> <service name="PCI"/> </provides>
</start>
<start name="test-usb">
<resource name="RAM" quantum="1M" />
<binary name="test-ahci" />
</start>
</config>
}
install_config $config
#
# Boot modules
#
# generic modules
set boot_modules {
core init timer usb_drv pci_drv test-ahci
}
build_boot_image $boot_modules
#
# Execute test case
#
set disk_image "bin/test.img"
set cmd "dd if=/dev/zero of=$disk_image bs=1024 count=65536"
puts "creating disk image:\n$cmd"
catch { exec sh -c $cmd }
set cmd "mkfs.vfat -F32 $disk_image"
puts "formating disk image with vfat file system:\$cmd"
catch { exec sh -c $cmd }
#
# Qemu opts for UHCI
#
#append qemu_args " -m 64 -nographic -usbdevice disk::$disk_image -boot order=d"
#
# Qemu opts for EHCI
#
append qemu_args "-drive if=none,id=disk,file=$disk_image"
append qemu_args { \
-m 64 -nographic -M pc \
-device usb-ehci,id=ehci \
-device usb-storage,bus=ehci.0,drive=disk \
-boot order=d }
run_genode_until {.*child exited with exit value 0.*} 40
puts "\ntest succeeded\n"
# vi: set ft=tcl :

View File

@ -0,0 +1,782 @@
/*
* \brief Dummy functions
* \author Norman Feske
* \author Sebastian sumpf
* \date 2011-01-29
*/
/*
* Copyright (C) 2011-2012 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.
*/
/* Linux kernel API */
#include <lx_emul.h>
/* DDE-Kit includes */
#include <dde_kit/printf.h>
/* Linux includes */
#include <linux/input.h>
#define SKIP_VERBOSE 0
#if VERBOSE_LX_EMUL
#define TRACE dde_kit_printf("\033[32m%s\033[0m called, not implemented\n", __PRETTY_FUNCTION__)
#else
#define TRACE
#endif
#if SKIP_VERBOSE
#define SKIP dde_kit_printf("\033[34m%s\033[0m: skipped\n", __PRETTY_FUNCTION__)
#else
#define SKIP
#endif
/******************
** asm/atomic.h **
******************/
int atomic_inc_return(atomic_t *v) { TRACE; return 0; }
/*******************************
** linux/byteorder/generic.h **
*******************************/
u16 get_unaligned_le16(const void *p) { TRACE; return 0; }
u32 get_unaligned_le32(const void *p) { TRACE; return 0; }
/*******************************
** linux/errno.h and friends **
*******************************/
long PTR_ERR(const void *ptr) { TRACE; return 0; }
/********************
** linux/kernel.h **
********************/
void might_sleep() { SKIP; }
char *kasprintf(gfp_t gfp, const char *fmt, ...) { TRACE; return NULL; }
int kstrtouint(const char *s, unsigned int base, unsigned int *res) { TRACE; return 0; }
int sprintf(char *buf, const char *fmt, ...) { TRACE; return 0; }
int sscanf(const char *b, const char *s, ...) { TRACE; return 0; }
int scnprintf(char *buf, size_t size, const char *fmt, ...) { TRACE; return 0; }
int strict_strtoul(const char *s, unsigned int base, unsigned long *res) { TRACE; return 0; }
long simple_strtoul(const char *cp, char **endp, unsigned int base) { TRACE; return 0; }
/******************
** linux/log2.h **
******************/
int roundup_pow_of_two(u32 n) { TRACE; return 0; }
/********************
** linux/printk.h **
********************/
void print_hex_dump(const char *level, const char *prefix_str,
int prefix_type, int rowsize, int groupsize,
const void *buf, size_t len, bool ascii) { TRACE; }
/**********************************
** linux/bitops.h, asm/bitops.h **
**********************************/
int ffs(int x) { TRACE; return 0; }
/********************
** linux/string.h **
********************/
int memcmp(const void *dst, const void *src, size_t s) { TRACE; return 0; }
char *strcat(char *dest, const char *src) { TRACE; return 0; }
int strcmp(const char *s1, const char *s2) { TRACE; return 0; }
int strncmp(const char *cs, const char *ct, size_t count) { TRACE; return 0; }
char *strncpy(char *dst, const char *src, size_t s) { TRACE; return NULL; }
char *strchr(const char *s, int n) { TRACE; return NULL; }
char *strrchr(const char *s, int n) { TRACE; return NULL; }
size_t strlcpy(char *dest, const char *src, size_t size) { TRACE; return 0; }
char *strsep(char **s,const char *d) { TRACE; return NULL; }
char *kstrdup(const char *s, gfp_t gfp) { TRACE; return 0; }
char *strstr(const char *h, const char *n) { TRACE; return 0; }
/*****************
** linux/nls.h **
*****************/
int utf16s_to_utf8s(const wchar_t *pwcs, int len,
enum utf16_endian endian, u8 *s, int maxlen) { TRACE; return 0; }
/*******************
** linux/ctype.h **
*******************/
int isprint(int v) { TRACE; return 0; }
/**********************
** linux/spinlock.h **
**********************/
void spin_lock(spinlock_t *lock) { SKIP; }
void spin_lock_nested(spinlock_t *lock, int subclass) { TRACE; }
void spin_unlock(spinlock_t *lock) { SKIP; }
void spin_lock_init(spinlock_t *lock) { SKIP; }
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags) { SKIP; }
void spin_lock_irqrestore(spinlock_t *lock, unsigned long flags) { SKIP; }
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags) { SKIP; }
void spin_lock_irq(spinlock_t *lock) { SKIP; }
void spin_unlock_irq(spinlock_t *lock) { SKIP; }
void assert_spin_locked(spinlock_t *lock) { TRACE;}
/*******************
** linux/mutex.h **
*******************/
void mutex_lock_nested(struct mutex *lock, unsigned int subclass) { TRACE; }
int mutex_lock_interruptible(struct mutex *m) { TRACE; return 0; }
/*******************
** linux/rwsem.h **
*******************/
void down_read(struct rw_semaphore *sem) { TRACE; }
void up_read(struct rw_semaphore *sem) { TRACE; }
void down_write(struct rw_semaphore *sem) { TRACE; }
void up_write(struct rw_semaphore *sem) { TRACE; }
/*******************
** linux/ktime.h **
*******************/
ktime_t ktime_add_ns(const ktime_t kt, u64 nsec) { TRACE; ktime_t ret = { 0 }; return ret; }
s64 ktime_us_delta(const ktime_t later, const ktime_t earlier) { TRACE; return 0; };
/*******************
** linux/timer.h **
*******************/
int del_timer_sync(struct timer_list *timer) { TRACE; return 0; }
unsigned long round_jiffies(unsigned long j) { TRACE; return 1; }
/*********************
** linux/hrtimer.h **
*********************/
ktime_t ktime_get_real(void) { TRACE; ktime_t ret; return ret; }
/*******************
** linux/delay.h **
*******************/
void mdelay(unsigned long msecs) { TRACE; }
/***********************
** linux/workquque.h **
***********************/
bool cancel_work_sync(struct work_struct *work) { TRACE; return 0; }
int cancel_delayed_work_sync(struct delayed_work *work) { TRACE; return 0; }
int schedule_work(struct work_struct *work) { TRACE; return 0; }
bool flush_work_sync(struct work_struct *work) { TRACE; return 0; }
/******************
** linux/wait.h **
******************/
void init_waitqueue_head(wait_queue_head_t *q) { TRACE; }
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { TRACE; }
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { TRACE; }
/******************
** linux/time.h **
******************/
struct timespec current_kernel_time(void)
{
struct timespec t = { 0, 0 };
return t;
}
void do_gettimeofday(struct timeval *tv) { TRACE; }
/*******************
** linux/sched.h **
*******************/
int kill_pid_info_as_cred(int i, struct siginfo *s, struct pid *p,
const struct cred *c, u32 v) { TRACE; return 0; }
pid_t task_pid_nr(struct task_struct *tsk) { TRACE; return 0; }
struct pid *task_pid(struct task_struct *task) { TRACE; return NULL; }
void __set_current_state(int state) { TRACE; }
int signal_pending(struct task_struct *p) { TRACE; return 0; }
void schedule(void) { TRACE; }
void yield(void) { TRACE; }
void cpu_relax(void) { TRACE; }
struct task_struct *current;
/*********************
** linux/kthread.h **
*********************/
int kthread_should_stop(void) { SKIP; return 0; }
int kthread_stop(struct task_struct *k) { TRACE; return 0; }
/**********************
** linux/notifier.h **
**********************/
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *nb) { TRACE; return 0; }
int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
struct notifier_block *nb) { TRACE; return 0; }
int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
unsigned long val, void *v) { TRACE; return 0; }
int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
struct notifier_block *nb) { TRACE; return 0; }
int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
struct notifier_block *nb) { TRACE; return 0; }
/*********************
** linux/kobject.h **
*********************/
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) { TRACE; return 0; }
char *kobject_name(const struct kobject *kobj) { TRACE; return 0; }
char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask) { TRACE; return 0; }
/*******************
** linux/sysfs.h **
*******************/
int sysfs_create_group(struct kobject *kobj,
const struct attribute_group *grp) { TRACE; return 0; }
void sysfs_remove_group(struct kobject *kobj,
const struct attribute_group *grp) { TRACE; }
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp) { TRACE; return 0; }
ssize_t simple_read_from_buffer(void __user *to, size_t count,
loff_t *ppos, const void *from, size_t available) { TRACE; return 0; }
/************************
** linux/pm_runtime.h **
************************/
int pm_runtime_set_active(struct device *dev) { TRACE; return 0; }
void pm_suspend_ignore_children(struct device *dev, bool enable) { TRACE; }
void pm_runtime_enable(struct device *dev) { TRACE; }
void pm_runtime_disable(struct device *dev) { TRACE; }
void pm_runtime_set_suspended(struct device *dev) { TRACE; }
void pm_runtime_get_noresume(struct device *dev) { TRACE; }
void pm_runtime_put_noidle(struct device *dev) { TRACE; }
void pm_runtime_use_autosuspend(struct device *dev) { TRACE; }
int pm_runtime_put_sync_autosuspend(struct device *dev) { TRACE; return 0; }
void pm_runtime_no_callbacks(struct device *dev) { TRACE; }
/***********************
** linux/pm_wakeup.h **
***********************/
int device_init_wakeup(struct device *dev, bool val) { TRACE; return 0; }
int device_wakeup_enable(struct device *dev) { TRACE; return 0; }
bool device_may_wakeup(struct device *dev) { TRACE; return 0; }
int device_set_wakeup_enable(struct device *dev, bool enable) { TRACE; return 0; }
bool device_can_wakeup(struct device *dev) { TRACE; return 0; }
/********************
** linux/device.h **
********************/
int dev_set_name(struct device *dev, const char *name, ...) { TRACE; return 0; }
const char *dev_name(const struct device *dev) { TRACE; return NULL; }
int dev_to_node(struct device *dev) { TRACE; return 0; }
void set_dev_node(struct device *dev, int node) { TRACE; }
struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, void *drvdata,
const char *fmt, ...) { TRACE; return NULL; }
void device_destroy(struct class *cls, dev_t devt) { TRACE; }
void device_unregister(struct device *dev) { TRACE; }
void device_lock(struct device *dev) { TRACE; }
int device_trylock(struct device *dev) { TRACE; return 0; }
void device_unlock(struct device *dev) { TRACE; }
void device_del(struct device *dev) { TRACE; }
void device_initialize(struct device *dev) { TRACE; }
int device_attach(struct device *dev) { TRACE; return 0; }
int device_is_registered(struct device *dev) { TRACE; return 0; }
int device_bind_driver(struct device *dev) { TRACE; return 0; }
void device_release_driver(struct device *dev) { TRACE; }
void device_enable_async_suspend(struct device *dev) { TRACE; }
void device_set_wakeup_capable(struct device *dev, bool capable) { TRACE; }
int device_create_bin_file(struct device *dev,
const struct bin_attribute *attr) { TRACE; return 0; }
void device_remove_bin_file(struct device *dev,
const struct bin_attribute *attr) { TRACE; }
int device_create_file(struct device *device,
const struct device_attribute *entry) { TRACE; return 0; }
void device_remove_file(struct device *dev,
const struct device_attribute *attr) { TRACE; }
void put_device(struct device *dev) { TRACE; }
void driver_unregister(struct device_driver *drv) { TRACE; }
int driver_attach(struct device_driver *drv) { TRACE; return 0; }
int driver_create_file(struct device_driver *driver,
const struct driver_attribute *attr) { TRACE; return 0; }
void driver_remove_file(struct device_driver *driver,
const struct driver_attribute *attr) { TRACE; }
struct device_driver *get_driver(struct device_driver *drv) { TRACE; return NULL; }
void put_driver(struct device_driver *drv) { TRACE; }
struct device *bus_find_device(struct bus_type *bus, struct device *start,
void *data,
int (*match)(struct device *dev, void *data)) { TRACE; return NULL; }
int bus_register(struct bus_type *bus) { TRACE; return 0; }
void bus_unregister(struct bus_type *bus) { TRACE; }
int bus_register_notifier(struct bus_type *bus,
struct notifier_block *nb) { TRACE; return 0; }
int bus_unregister_notifier(struct bus_type *bus,
struct notifier_block *nb) { TRACE; return 0; }
struct class *__class_create(struct module *owner,
const char *name,
struct lock_class_key *key) { TRACE; return NULL; }
int class_register(struct class *cls) { TRACE; return 0; }
void class_unregister(struct class *cls) { TRACE; }
void class_destroy(struct class *cls) { TRACE; }
/*****************************
** linux/platform_device.h **
*****************************/
void *platform_get_drvdata(const struct platform_device *pdev) { TRACE; return NULL; }
/********************
** linux/dcache.h **
********************/
void d_instantiate(struct dentry *dentry, struct inode *i) { TRACE; }
int d_unhashed(struct dentry *dentry) { TRACE; return 0; }
void d_delete(struct dentry *d) { TRACE; }
struct dentry *d_alloc_root(struct inode *i) { TRACE; return NULL; }
struct dentry *dget(struct dentry *dentry) { TRACE; return NULL; }
void dput(struct dentry *dentry) { TRACE; }
void dont_mount(struct dentry *dentry) { TRACE; }
/******************
** linux/poll.h **
******************/
void poll_wait(struct file *f, wait_queue_head_t *w, poll_table *p) { TRACE; }
/********************
** linux/statfs.h **
********************/
loff_t default_llseek(struct file *file, loff_t offset, int origin) { TRACE; return 0; }
/****************
** linux/fs.h **
****************/
unsigned iminor(const struct inode *inode) { TRACE; return 0; }
unsigned imajor(const struct inode *inode) { TRACE; return 0; }
int register_chrdev_region(dev_t d, unsigned v, const char *s) { TRACE; return 0; }
void unregister_chrdev_region(dev_t d, unsigned v) { TRACE; }
void fops_put(struct file_operations const *fops) { TRACE; }
loff_t noop_llseek(struct file *file, loff_t offset, int origin) { TRACE; return 0; }
int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops) { TRACE; return 0; }
void unregister_chrdev(unsigned int major, const char *name) { TRACE; }
struct inode *new_inode(struct super_block *sb) { TRACE; return NULL; }
unsigned int get_next_ino(void) { TRACE; return 0; }
void init_special_inode(struct inode *i, umode_t m, dev_t d) { TRACE; }
int generic_delete_inode(struct inode *inode) { TRACE; return 0; }
void drop_nlink(struct inode *inode) { TRACE; }
void inc_nlink(struct inode *inode) { TRACE; }
void dentry_unhash(struct dentry *dentry) { TRACE; }
void iput(struct inode *i) { TRACE; }
struct dentry *mount_single(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *,
void *, int)) { TRACE; return NULL; }
int nonseekable_open(struct inode *inode, struct file *filp) { TRACE; return 0; }
int simple_statfs(struct dentry *d, struct kstatfs *k) { TRACE; return 0; }
int simple_pin_fs(struct file_system_type *t, struct vfsmount **mount, int *count) { TRACE; return 0; }
void simple_release_fs(struct vfsmount **mount, int *count) { TRACE; }
void kill_litter_super(struct super_block *sb) { TRACE; }
int register_filesystem(struct file_system_type *t) { TRACE; return 0; }
int unregister_filesystem(struct file_system_type *t) { TRACE; return 0; }
void kill_fasync(struct fasync_struct **fp, int sig, int band) { TRACE; }
int fasync_add_entry(int fd, struct file *filp, struct fasync_struct **fapp) { TRACE; return 0; }
const struct file_operations simple_dir_operations;
const struct inode_operations simple_dir_inode_operations;
/*******************
** linux/namei.h **
*******************/
struct dentry *lookup_one_len(const char *c, struct dentry *e, int v) { TRACE; return NULL; }
/**********************
** linux/seq_file.h **
**********************/
int seq_printf(struct seq_file *f, const char *fmt, ...) { TRACE; return 0; }
/*****************
** linux/gfp.h **
*****************/
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order) { TRACE; return 0; }
void __free_pages(struct page *p, unsigned int order) { TRACE; }
void free_pages(unsigned long addr, unsigned int order) { TRACE; }
/*********************
** linux/proc_fs.h **
*********************/
struct proc_dir_entry *proc_mkdir(const char *s,struct proc_dir_entry *e) { TRACE; return NULL; }
void remove_proc_entry(const char *name, struct proc_dir_entry *parent) { TRACE; }
/********************
* linux/debugfs.h **
********************/
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent) { TRACE; return (struct dentry *)1; }
struct dentry *debugfs_create_file(const char *name, mode_t mode,
struct dentry *parent, void *data,
const struct file_operations *fops) { TRACE; return (struct dentry *)1; }
void debugfs_remove(struct dentry *dentry) { TRACE; }
/************************
** linux/page-flags.h **
************************/
bool is_highmem(void *ptr) { TRACE; return 0; }
/****************
** linux/mm.h **
****************/
struct zone *page_zone(const struct page *page) { TRACE; return NULL; }
/**********************
** linux/highmem.h **
**********************/
void *kmap(struct page *page) { TRACE; return 0; }
void kunmap(struct page *page) { TRACE; }
/**********************
** asm-generic/io.h **
**********************/
void *ioremap(resource_size_t offset, unsigned long size) { TRACE; return NULL; }
void iounmap(volatile void *addr) { TRACE; }
void native_io_delay(void) { TRACE; }
/********************
** linux/ioport.h **
********************/
void release_region(resource_size_t start, resource_size_t n) { TRACE; }
void release_mem_region(resource_size_t start, resource_size_t n) { TRACE; }
/**
* SKIP
*/
/* implemented in Pci_driver */
struct resource *request_region(resource_size_t start, resource_size_t n,
const char *name) { SKIP; return (struct resource *)1; }
/* implemented in Pci_driver */
struct resource *request_mem_region(resource_size_t start, resource_size_t n,
const char *name) { SKIP; return (struct resource *)1; }
/***********************
** linux/interrupt.h **
***********************/
void local_irq_enable(void) { TRACE; }
void local_irq_disable(void) { TRACE; }
void free_irq(unsigned int i, void *p) { TRACE; }
/*********************
** linux/hardirq.h **
*********************/
void synchronize_irq(unsigned int irq) { TRACE; }
/*****************
** linux/pci.h **
*****************/
int pci_bus_read_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 *val) { TRACE; return 0; }
int pci_bus_write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 val) { TRACE; return 0; }
void *pci_get_drvdata(struct pci_dev *pdev) { TRACE; return NULL; }
void pci_dev_put(struct pci_dev *dev) { TRACE; }
struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device,
struct pci_dev *from) { TRACE; return NULL; }
void pci_disable_device(struct pci_dev *dev) { TRACE; }
int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask) { TRACE; return 0; }
void pci_unregister_driver(struct pci_driver *drv) { TRACE; }
bool pci_dev_run_wake(struct pci_dev *dev) { TRACE; return 0; }
int pci_set_mwi(struct pci_dev *dev) { TRACE; return 0; }
int pci_find_capability(struct pci_dev *dev, int cap) { TRACE; return 0; }
struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn) { TRACE; return NULL; }
const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,
struct pci_dev *dev) { TRACE; return 0; }
void *pci_ioremap_bar(struct pci_dev *pdev, int bar);
/**
* Omitted PCI functions
*/
/* scans resources, this is already implemented in 'pci_driver.cc' */
int pci_enable_device(struct pci_dev *dev) { SKIP; return 0; }
/* implemented in Pci_driver::_setup_pci_device */
void pci_set_master(struct pci_dev *dev) { SKIP; }
/**********************
** linux/irqflags.h **
**********************/
unsigned long local_irq_save(unsigned long flags) { SKIP; return 0; }
unsigned long local_irq_restore(unsigned long flags) { SKIP; return 0; }
/*************************
** linux/dma-mapping.h **
*************************/
void dma_unmap_single_attrs(struct device *dev, dma_addr_t addr,
size_t size,
enum dma_data_direction dir,
struct dma_attrs *attrs) { SKIP; }
void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir,
struct dma_attrs *attrs) { SKIP; }
void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
enum dma_data_direction direction) { SKIP;; }
int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { SKIP; return 0; }
/*****************
** linux/pid.h **
*****************/
void put_pid(struct pid *pid) { TRACE; }
struct pid *get_pid(struct pid *pid) { TRACE; return NULL; }
/******************
** linux/cred.h **
******************/
void put_cred(struct cred const *c) { TRACE; }
const struct cred *get_cred(const struct cred *cred) { TRACE; return NULL; }
/**********************
** linux/security.h **
**********************/
void security_task_getsecid(struct task_struct *p, u32 *secid) { TRACE; }
/******************
** linux/cdev.h **
******************/
void cdev_init(struct cdev *c, const struct file_operations *fops) { TRACE; }
int cdev_add(struct cdev *c, dev_t d, unsigned v) { TRACE; return 0; }
void cdev_del(struct cdev *c) { TRACE; }
/*********************
** linux/utsname.h **
*********************/
struct new_utsname *init_utsname(void)
{
static struct new_utsname uts = { .sysname = "Genode.UTS", .release = "1.0" };
return &uts;
}
struct new_utsname *utsname(void) { TRACE; return NULL; }
/*********************
** linux/freezer.h **
*********************/
void set_freezable(void) { TRACE; }
/********************
** linux/parser.h **
********************/
int match_token(char *s, const match_table_t table, substring_t args[]) { TRACE; return 0; }
int match_int(substring_t *s, int *result) { TRACE; return 0; }
int match_octal(substring_t *s, int *result) { TRACE; return 0; }
/*********************
** linux/semaphore **
*********************/
void sema_init(struct semaphore *sem, int val) { TRACE; }
int down_trylock(struct semaphore *sem) { TRACE; return 0; }
int down_interruptible(struct semaphore *sem) { TRACE; return 0; }
void up(struct semaphore *sem) { TRACE; }
/*******************
** linux/input.h **
*******************/
void input_ff_destroy(struct input_dev *dev) { TRACE; }
int input_ff_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { TRACE; return 0; }
int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, struct file *file) { TRACE; return 0; }
int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file) { TRACE; return 0; }
/*********************
** input-compat.h" **
*********************/
int input_event_from_user(const char __user *buffer, struct input_event *event) { TRACE; return 0; }
int input_event_to_user(char __user *buffer, const struct input_event *event) { TRACE; return 0; }
int input_ff_effect_from_user(const char __user *buffer, size_t size, struct ff_effect *effect) { TRACE; return 0;}
/****************
** linux/mt.h **
****************/
void input_mt_destroy_slots(struct input_dev *dev) { TRACE; }
/*********************
** linux/vmalloc.h **
*********************/
void *vmalloc(unsigned long size) { TRACE; return 0; }
/********************
** linux/blkdev.h **
********************/
void blk_queue_bounce_limit(struct request_queue *q, u64 dma_mask) { TRACE; }
void blk_queue_update_dma_alignment(struct request_queue *q, int mask) { TRACE; }
void blk_queue_max_hw_sectors(struct request_queue *q, unsigned int max_hw_sectors) { TRACE; }
unsigned int queue_max_hw_sectors(struct request_queue *q) { TRACE; return 0; }
/**********************
** scsi/scsi_cmnd.h **
**********************/
void scsi_set_resid(struct scsi_cmnd *cmd, int resid) { TRACE; }
int scsi_get_resid(struct scsi_cmnd *cmd) { TRACE; return 0; }
/********************
** scsi/scsi_eh.h **
*******************/
void scsi_report_bus_reset(struct Scsi_Host *shost, int channel) { TRACE; }
void scsi_report_device_reset(struct Scsi_Host *shost, int channel, int target) { TRACE; }
void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd,
struct scsi_eh_save *ses, unsigned char *cmnd,
int cmnd_size, unsigned sense_bytes) { TRACE; }
void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd,
struct scsi_eh_save *ses) { TRACE; }
int scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
struct scsi_sense_hdr *sshdr) { TRACE; return 0; }
const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len,
int desc_type) { TRACE; return 0; }
/***********************
** drivers/scsi/sd.h **
**********************/
struct scsi_disk *scsi_disk(struct gendisk *disk) { TRACE; return 0; }
/**********************
** scsi/scsi_host.h **
**********************/
int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
struct device *dma_dev) { TRACE; return 0; }
void scsi_remove_host(struct Scsi_Host *shost) { TRACE; }
void scsi_host_put(struct Scsi_Host *shost) { TRACE; }
struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost) { TRACE; return 0; }

View File

@ -0,0 +1,136 @@
/*
* \brief Input service and event handler
* \author Christian Helmuth
* \author Dirk Vogt <dvogt@os.inf.tu-dresden.de>
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2009-04-20
*
* The original implementation was in the L4Env from the TUD:OS group
* (l4/pkg/input/lib/src/l4evdev.c). This file was released under the terms of
* the GNU General Public License version 2.
*/
/* Linux */
#include <linux/input.h>
/* Local */
#include <lx_emul.h>
/* Callback function to Genode subsystem */
static genode_input_event_cb handler;;
void genode_evdev_event(struct input_handle *handle, unsigned int type,
unsigned int code, int value)
{
#if DEBUG_EVDEV
static unsigned long count = 0;
#endif
/* filter sound events */
if (test_bit(EV_SND, handle->dev->evbit)) return;
/* filter input_repeat_key() */
if ((type == EV_KEY) && (value == 2)) return;
/* filter EV_SYN */
if (type == EV_SYN) return;
/* generate arguments and call back */
enum input_event_type arg_type;
unsigned arg_keycode = KEY_UNKNOWN;
int arg_ax = 0, arg_ay = 0, arg_rx = 0, arg_ry = 0;
switch (type) {
case EV_KEY:
arg_keycode = code;
switch (value) {
case 0:
arg_type = EVENT_TYPE_RELEASE;
break;
case 1:
arg_type = EVENT_TYPE_PRESS;
break;
default:
printk("Unknown key event value %d - not handled\n", value);
return;
}
break;
case EV_ABS:
switch (code) {
case ABS_X:
arg_type = EVENT_TYPE_MOTION;
arg_ax = value;
break;
case ABS_Y:
arg_type = EVENT_TYPE_MOTION;
arg_ay = value;
break;
case ABS_WHEEL:
/*
* XXX I do not know, how to handle this correctly. At least, this
* scheme works on Qemu.
*/
arg_type = EVENT_TYPE_WHEEL;
arg_ry = value;
break;
default:
printk("Unknown absolute event code %d - not handled\n", code);
return;
}
break;
case EV_REL:
switch (code) {
case REL_X:
arg_type = EVENT_TYPE_MOTION;
arg_rx = value;
break;
case REL_Y:
arg_type = EVENT_TYPE_MOTION;
arg_ry = value;
break;
case REL_HWHEEL:
arg_type = EVENT_TYPE_WHEEL;
arg_rx = value;
break;
case REL_WHEEL:
arg_type = EVENT_TYPE_WHEEL;
arg_ry = value;
break;
default:
printk("Unknown relative event code %d - not handled\n", code);
return;
}
break;
default:
printk("Unknown event type %d - not handled\n", type);
return;
}
if (handler)
handler(arg_type, arg_keycode, arg_ax, arg_ay, arg_rx, arg_ry);
printk("EVENT: t: %x c: %x ax: %d ay %d rx: %d ry %d\n",
arg_type, arg_keycode, arg_ax, arg_ay, arg_rx, arg_ry);
#if DEBUG_EVDEV
printk("event[%ld]. dev: %s, type: %d, code: %d, value: %d\n",
count++, handle->dev->name, type, code, value);
#endif
}
void genode_input_register(genode_input_event_cb h) { handler = h; }

View File

@ -0,0 +1,77 @@
/*
* \brief Linux 2.6 Input driver for USB HID
* \author Christian Helmuth
* \author Christian Prochaska
* \author Sebastian Sumpf
* \date 2011-07-15
*/
/*
* Copyright (C) 2011-2012 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/printf.h>
#include <base/rpc_server.h>
#include <input/component.h>
#include <os/ring_buffer.h>
#include <lx_emul.h>
#undef RELEASE
using namespace Genode;
/*********************
** Input component **
*********************/
typedef Ring_buffer<Input::Event, 512> Input_ring_buffer;
static Input_ring_buffer ev_queue;
namespace Input {
void event_handling(bool enable) { }
bool event_pending() { return !ev_queue.empty(); }
Event get_event() { return ev_queue.get(); }
}
/**
* Input event call-back function
*/
static void input_callback(enum input_event_type type,
unsigned keycode,
int absolute_x, int absolute_y,
int relative_x, int relative_y)
{
Input::Event::Type t = Input::Event::INVALID;
switch (type) {
case EVENT_TYPE_PRESS: t = Input::Event::PRESS; break;
case EVENT_TYPE_RELEASE: t = Input::Event::RELEASE; break;
case EVENT_TYPE_MOTION: t = Input::Event::MOTION; break;
case EVENT_TYPE_WHEEL: t = Input::Event::WHEEL; break;
}
try {
ev_queue.add(Input::Event(t, keycode,
absolute_x, absolute_y,
relative_x, relative_y));
} catch (Input_ring_buffer::Overflow) {
PWRN("input ring buffer overflow");
}
}
void start_input_service(void *ep)
{
Rpc_entrypoint *e = static_cast<Rpc_entrypoint *>(ep);
static Input::Root input_root(e, env()->heap());
env()->parent()->announce(e->manage(&input_root));
genode_input_register(input_callback);
}

View File

@ -0,0 +1,770 @@
/*
* \brief Emulation of Linux kernel interfaces
* \author Norman Feske
* \author Sebastian Sumpf
* \date 2012-01-29
*/
/*
* Copyright (C) 2012 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.
*/
/* Genode includes */
#include <base/allocator_avl.h>
#include <dataspace/client.h>
#include <rm_session/connection.h>
#include <timer_session/connection.h>
#include <util/string.h>
/* Local includes */
#include "routine.h"
#include "signal.h"
#include "lx_emul.h"
/* DDE kit includes */
extern "C" {
#include <dde_kit/semaphore.h>
#include <dde_kit/memory.h>
#include <dde_kit/pgtab.h>
#include <dde_kit/resources.h>
#include <dde_kit/interrupt.h>
#include <dde_kit/thread.h>
}
#if VERBOSE_LX_EMUL
#define TRACE dde_kit_printf("\033[35m%s\033[0m called\n", __PRETTY_FUNCTION__)
#define UNSUPPORTED dde_kit_printf("\033[31m%s\033[0m unsupported arguments\n", __PRETTY_FUNCTION__)
#else
#define TRACE
#define UNSUPPORTED
#endif
/***********************
** Atomic operations **
***********************/
/*
* Actually not atomic, for now
*/
unsigned int atomic_read(atomic_t *p) { return *(volatile int *)p; }
void atomic_inc(atomic_t *v) { (*(volatile int *)v)++; }
void atomic_dec(atomic_t *v) { (*(volatile int *)v)--; }
void atomic_add(int i, atomic_t *v) { (*(volatile int *)v) += i; }
void atomic_sub(int i, atomic_t *v) { (*(volatile int *)v) -= i; }
void atomic_set(atomic_t *p, unsigned int v) { (*(volatile int *)p) = v; }
/*******************
** linux/mutex.h **
*******************/
void mutex_init (struct mutex *m) { if (m->lock) dde_kit_lock_init (&m->lock); }
void mutex_lock (struct mutex *m) { if (m->lock) dde_kit_lock_lock ( m->lock); }
void mutex_unlock(struct mutex *m) { if (m->lock) dde_kit_lock_unlock( m->lock); }
/*************************************
** Memory allocation, linux/slab.h **
*************************************/
void *kmalloc(size_t size, gfp_t flags)
{
return dde_kit_large_malloc(size);
}
void *kzalloc(size_t size, gfp_t flags)
{
void *addr = dde_kit_large_malloc(size);
if (addr)
Genode::memset(addr, 0, size);
return addr;
}
void *kcalloc(size_t n, size_t size, gfp_t flags)
{
if (size != 0 && n > ~0UL / size)
return 0;
return kzalloc(n * size, flags);
}
void kfree(const void *p)
{
dde_kit_large_free((void *)p);
}
/*********************
** linux/vmalloc.h **
*********************/
void *vzalloc(unsigned long size)
{
void *ptr = dde_kit_simple_malloc(size);
if (ptr)
Genode::memset(ptr, 0, size);
return ptr;
}
void vfree(void *addr) { dde_kit_simple_free(addr); }
/******************
** linux/kref.h **
******************/
void kref_init(struct kref *kref)
{
dde_kit_log(DEBUG_KREF,"%s ref: %p", __func__, kref);
kref->refcount.v = 1;
}
void kref_get(struct kref *kref)
{
kref->refcount.v++;
dde_kit_log(DEBUG_KREF, "%s ref: %p c: %d", __func__, kref, kref->refcount.v);
}
int kref_put(struct kref *kref, void (*release) (struct kref *kref))
{
dde_kit_log(DEBUG_KREF, "%s: ref: %p c: %d", __func__, kref, kref->refcount.v);
if (!--kref->refcount.v) {
release(kref);
return 1;
}
return 0;
}
/*********************
** linux/uaccess.h **
*********************/
size_t copy_to_user(void *dst, void const *src, size_t len)
{
if (dst && src && len)
Genode::memcpy(dst, src, len);
return 0;
}
size_t copy_from_user(void *dst, void const *src, size_t len)
{
if (dst && src && len)
Genode::memcpy(dst, src, len);
return 0;
}
bool access_ok(int access, void *addr, size_t size) { return 1; }
/********************
** linux/string.h **
********************/
void *memcpy(void *dest, const void *src, size_t n) {
return Genode::memcpy(dest, src, n); }
void *memset(void *s, int c, size_t n) {
return Genode::memset(s, c, n); }
int snprintf(char *buf, size_t size, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
Genode::String_console sc(buf, size);
sc.vprintf(fmt, args);
va_end(args);
return sc.len();
}
size_t strlen(const char *s) { return Genode::strlen(s); }
size_t strlcat(char *dest, const char *src, size_t n)
{
size_t len = strlen(dest);
Genode::strncpy(dest + len, src, n);
dest[len + n] = 0;
return n;
}
void *kmemdup(const void *src, size_t len, gfp_t gfp)
{
void *ptr = kmalloc(len, 0);
memcpy(ptr, src, len);
return ptr;
}
void *memscan(void *addr, int c, size_t size)
{
unsigned char* p = (unsigned char *)addr;
for (size_t s = 0; s < size; s++, p++)
if (*p == c)
break;
return (void *)p;
}
/******************
** linux/log2.h **
******************/
int ilog2(u32 n) { return Genode::log2(n); }
/********************
** linux/slab.h **
********************/
struct kmem_cache
{
const char *name; /* cache name */
unsigned size; /* object size */
struct dde_kit_slab *dde_kit_slab_cache; /* backing DDE kit cache */
};
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align,
unsigned long falgs, void (*ctor)(void *))
{
dde_kit_log(DEBUG_SLAB, "\"%s\" obj_size=%d", name, size);
struct kmem_cache *cache;
if (!name) {
printk("kmem_cache name reqeuired\n");
return 0;
}
cache = (struct kmem_cache *)dde_kit_simple_malloc(sizeof(*cache));
if (!cache) {
printk("No memory for slab cache\n");
return 0;
}
/* initialize a physically contiguous cache for kmem */
if (!(cache->dde_kit_slab_cache = dde_kit_slab_init(size))) {
printk("DDE kit slab init failed\n");
dde_kit_simple_free(cache);
return 0;
}
cache->name = name;
cache->size = size;
return cache;
}
void kmem_cache_destroy(struct kmem_cache *cache)
{
dde_kit_log(DEBUG_SLAB, "\"%s\"", cache->name);
dde_kit_slab_destroy(cache->dde_kit_slab_cache);
dde_kit_simple_free(cache);
}
void *kmem_cache_zalloc(struct kmem_cache *cache, gfp_t flags)
{
void *ret;
dde_kit_log(DEBUG_SLAB, "\"%s\" flags=%x", cache->name, flags);
ret = dde_kit_slab_alloc(cache->dde_kit_slab_cache);
/* return here in case of error */
if (!ret) return 0;
/* zero page */
memset(ret, 0, cache->size);
return ret;
}
void kmem_cache_free(struct kmem_cache *cache, void *objp)
{
dde_kit_log(DEBUG_SLAB, "\"%s\" (%p)", cache->name, objp);
dde_kit_slab_free(cache->dde_kit_slab_cache, objp);
}
/**********************
** asm-generic/io.h **
**********************/
void *ioremap_wc(resource_size_t phys_addr, unsigned long size)
{
dde_kit_addr_t map_addr;
if (dde_kit_request_mem(phys_addr, size, 1, &map_addr)) {
PERR("Failed to request I/O memory: [%x,%lx)", phys_addr, phys_addr + size);
return 0;
}
return (void *)map_addr;
}
/********************
** linux/device.h **
********************/
/**
* Simple driver management class
*/
class Driver : public Genode::List<Driver>::Element
{
private:
struct device_driver *_drv; /* Linux driver */
public:
Driver(struct device_driver *drv) : _drv(drv)
{
list()->insert(this);
}
/**
* List of all currently registered drivers
*/
static Genode::List<Driver> *list()
{
static Genode::List<Driver> _list;
return &_list;
}
/**
* Match device and drivers
*/
bool match(struct device *dev)
{
bool ret = _drv->bus->match ? _drv->bus->match(dev, _drv) : true;
dde_kit_log(DEBUG_DRIVER, "MATCH: %s ret: %u match: %p",
_drv->name, ret, _drv->bus->match);
return ret;
}
/**
* Probe device with driver
*/
int probe(struct device *dev)
{
dev->driver = _drv;
if (dev->bus->probe) {
dde_kit_log(DEBUG_DRIVER, "Probing device bus");
return dev->bus->probe(dev);
} else if (_drv->probe) {
dde_kit_log(DEBUG_DRIVER, "Probing driver: %s", _drv->name);
return _drv->probe(dev);
}
return 0;
}
};
int driver_register(struct device_driver *drv)
{
dde_kit_log(DEBUG_DRIVER, "%s at %p", drv->name, drv);
new (Genode::env()->heap()) Driver(drv);
return 0;
}
int device_add(struct device *dev)
{
if (dev->driver)
return 0;
/* foreach driver match and probe device */
for (Driver *driver = Driver::list()->first(); driver; driver = driver->next())
if (driver->match(dev)) {
int ret = driver->probe(dev);
dde_kit_log(DEBUG_DRIVER, "Probe return %d", ret);
if (!ret)
return 0;
}
return 0;
}
int device_register(struct device *dev)
{
//XXX: initialize DMA pools (see device_initialize)
return device_add(dev);
}
void *dev_get_drvdata(const struct device *dev)
{
return dev->driver_data;
}
int dev_set_drvdata(struct device *dev, void *data)
{
dev->driver_data = data; return 0;
}
struct device *get_device(struct device *dev) { TRACE; return dev; }
long find_next_zero_bit_le(const void *addr,
unsigned long size, unsigned long offset)
{
unsigned long max_size = sizeof(long) * 8;
if (offset >= max_size) {
PWRN("Offset greater max size");
return offset + size;
}
for (; offset < max_size; offset++)
if (!(*(unsigned long*)addr & (1 << offset)))
return offset;
PERR("No zero bit findable");
return offset + size;
}
/*******************************
** linux/byteorder/generic.h **
*******************************/
void put_unaligned_le32(u32 val, void *p)
{
struct __una_u32 *ptr = (struct __una_u32 *)p;
ptr->x = val;
}
u64 get_unaligned_le64(const void *p)
{
struct __una_u64 *ptr = (struct __una_u64 *)p;
return ptr->x;
}
void put_unaligned_le64(u64 val, void *p)
{
struct __una_u64 *ptr = (struct __una_u64 *)p;
ptr->x = val;
}
/**********************************
** linux/bitops.h, asm/bitops.h **
**********************************/
int fls(int x)
{
if (!x)
return 0;
for (int i = 31; i >= 0; i--)
if (x & (1 << i))
return i;
return 0;
}
/*******************
** linux/delay.h **
*******************/
static Timer::Connection _timer;
void udelay(unsigned long usecs)
{
_timer.msleep(usecs < 1000 ? 1 : usecs / 1000);
}
void msleep(unsigned int msecs) { _timer.msleep(msecs); }
/*********************
** linux/jiffies.h **
*********************/
/*
* We use DDE kit's jiffies in 100Hz granularity.
*/
enum { JIFFIES_TICK_MS = 1000 / DDE_KIT_HZ };
unsigned long msecs_to_jiffies(const unsigned int m) { return m / JIFFIES_TICK_MS; }
long time_after_eq(long a, long b) { return (a - b) >= 0; }
long time_after(long a, long b) { return (b - a) > 0; }
/*********************
** linux/dmapool.h **
*********************/
namespace Genode {
/**
* Dma-pool manager
*/
class Dma
{
private:
enum { SIZE = 1024 * 1024 };
addr_t _base; /* virt base of pool */
addr_t _base_phys; /* phys base of pool */
Allocator_avl _range; /* range allocator for pool */
Dma() : _range(env()->heap())
{
Ram_dataspace_capability cap = env()->ram_session()->alloc(SIZE);
_base_phys = Dataspace_client(cap).phys_addr();
_base = (addr_t)env()->rm_session()->attach(cap);
dde_kit_log(DEBUG_DMA, "New DMA range [%lx-%lx)", _base, _base + SIZE);
_range.add_range(_base, SIZE);
}
public:
static Dma* pool()
{
static Dma _p;
return &_p;
}
/**
* Alloc 'size' bytes of DMA memory
*/
void *alloc(size_t size, int align = PAGE_SHIFT)
{
void *addr;
if (!_range.alloc_aligned(size, &addr, align)) {
PERR("DMA of %zu bytes allocation failed", size);
return 0;
}
return addr;
}
/**
* Free DMA memory
*/
void free(void *addr) { _range.free(addr); }
/**
* Get phys for virt address
*/
addr_t phys_addr(void *addr)
{
addr_t a = (addr_t)addr;
if (a < _base || a >= _base + SIZE) {
PERR("No DMA phys addr for %lx", a);
return 0;
}
return (a - _base) + _base_phys;
}
};
}
struct dma_pool
{
size_t size;
int align;
};
struct dma_pool *dma_pool_create(const char *name, struct device *d, size_t size,
size_t align, size_t alloc)
{
dde_kit_log(DEBUG_DMA, "size: %zx align:%zx", size, align);
if (align & (align - 1))
return 0;
dma_pool *pool = new(Genode::env()->heap()) dma_pool;
pool->align = Genode::log2((int)align);
pool->size = size;
return pool;
}
void dma_pool_destroy(struct dma_pool *d)
{
dde_kit_log(DEBUG_DMA, "close");
destroy(Genode::env()->heap(), d);
}
static void* _alloc(size_t size, int align, dma_addr_t *dma)
{
void *addr = Genode::Dma::pool()->alloc(size, align);
dde_kit_log(DEBUG_DMA, "addr: %p size %zx align: %d", addr, size, align);
if (!addr)
return 0;
*dma = (dma_addr_t)Genode::Dma::pool()->phys_addr(addr);
return addr;
}
void *dma_pool_alloc(struct dma_pool *d, gfp_t f, dma_addr_t *dma)
{
return _alloc(d->size, d->align, dma);
}
void dma_pool_free(struct dma_pool *d, void *vaddr, dma_addr_t a)
{
dde_kit_log(DEBUG_DMA, "free: addr %p, size: %zx", vaddr, d->size);
Genode::Dma::pool()->free(vaddr);
}
void *dma_alloc_coherent(struct device *, size_t size, dma_addr_t *dma, gfp_t)
{
return _alloc(size, PAGE_SHIFT, dma);
}
void dma_free_coherent(struct device *, size_t size, void *vaddr, dma_addr_t)
{
dde_kit_log(DEBUG_DMA, "free: addr %p, size: %zx", vaddr, size);
Genode::Dma::pool()->free(vaddr);
}
/*************************
** linux/dma-mapping.h **
*************************/
/**
* Translate virt to phys using DDE-kit
*/
dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
size_t size,
enum dma_data_direction dir,
struct dma_attrs *attrs)
{
dma_addr_t phys = (dma_addr_t)dde_kit_pgtab_get_physaddr(ptr);
dde_kit_log(DEBUG_DMA, "virt: %p phys: %lx", ptr, phys);
return phys;
}
dma_addr_t dma_map_page(struct device *dev, struct page *page,
size_t offset, size_t size,
enum dma_data_direction dir)
{
dde_kit_log(DEBUG_DMA, "virt: %p phys: %lx offs: %zx", page->virt, page->phys, offset);
return page->phys + offset;
}
int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir,
struct dma_attrs *attrs) { return nents; }
/***********************
** linux/workquque.h **
***********************/
int schedule_delayed_work(struct delayed_work *work, unsigned long delay)
{
work->work.func(&(work)->work);
return 0;
}
/*********************
** linux/kthread.h **
*********************/
struct task_struct *kthread_run(int (*fn)(void *), void *arg, const char *n, ...)
{
dde_kit_log(DEBUG_THREAD, "Run %s", n);
Routine::add(fn, arg, n);
return 0;
}
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[], ...)
{
/*
* This is just called for delayed device scanning (see
* 'drivers/usb/storage/usb.c')
*/
dde_kit_log(DEBUG_THREAD, "Create %s", namefmt);
Routine::add(threadfn, data, namefmt);
return 0;
}
/*************************
** linux/scatterlist.h **
*************************/
struct scatterlist *sg_next(struct scatterlist *sg)
{
if (sg->last)
return 0;
return sg++;
}
struct page *sg_page(struct scatterlist *sg)
{
if (!sg)
return 0;
return (page *)sg->page_link;
}
void *sg_virt(struct scatterlist *sg)
{
if (!sg || !sg->page_link)
return 0;
struct page *page = (struct page *)sg->page_link;
return (void *)((unsigned long)page->virt + sg->offset);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,135 @@
/*
* \brief USB driver main program
* \author Norman Feske
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-01-29
*/
/*
* Copyright (C) 2012 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.
*/
/* Genode */
#include <base/rpc_server.h>
#include <base/printf.h>
#include <base/sleep.h>
#include <cap_session/connection.h>
#include <os/config.h>
#include <util/xml_node.h>
/* Local */
#include "storage/component.h"
#include "routine.h"
#include "signal.h"
extern "C" {
#include <dde_kit/timer.h>
}
using namespace Genode;
extern "C" void subsys_usb_init();
extern "C" void subsys_input_init();
extern "C" void module_ehci_hcd_init();
extern "C" void module_evdev_init();
extern "C" void module_hid_init();
extern "C" void module_hid_init_core();
extern "C" void module_uhci_hcd_init();
extern "C" void module_usb_mouse_init();
extern "C" void module_usb_kbd_init();
extern "C" void module_usb_stor_init();
extern "C" void start_input_service(void *ep);
Routine *Routine::_current = 0;
Routine *Routine::_dead = 0;
bool Routine::_all = false;
void breakpoint() { PDBG("BREAK"); }
static void init(bool hid, bool stor)
{
/* start jiffies */
dde_kit_timer_init(0, 0);
/* USB */
subsys_usb_init();
/* input + HID */
if (hid) {
subsys_input_init();
module_evdev_init();
/* HID */
module_hid_init();
module_usb_mouse_init();
module_usb_kbd_init();
}
/*
* Host controller.
*
* ehci_hcd should always be loaded before uhci_hcd and ohci_hcd, not after
*/
module_ehci_hcd_init();
module_uhci_hcd_init();
/* storage */
if (stor)
module_usb_stor_init();
}
int main(int, char **)
{
/*
* Initialize server entry point
*/
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
static Rpc_entrypoint ep_hid(&cap, STACK_SIZE, "usb_hid_ep");
static Signal_receiver recv;
bool hid = false;
bool stor = false;
try {
config()->xml_node().sub_node("hid");
start_input_service(&ep_hid);
hid = true;
} catch (Config::Invalid) {
PDBG("No <config> node found - not starting any USB services");
return 0;
} catch (Xml_node::Nonexistent_sub_node) {
PDBG("No <hid> config node found - not starting the USB HID (Input) service");
}
try {
config()->xml_node().sub_node("storage");
stor = true;
} catch (Xml_node::Nonexistent_sub_node) {
PDBG("No <storage> config node found - not starting the USB Storage (Block) service");
}
Timer::init(&recv);
Irq::init(&recv);
Event::init(&recv);
Service_handler::s()->receiver(&recv);
Storage::init(&recv);
Routine::add(0, 0, "Main", true);
Routine::current_use_first();
init(hid, stor);
Routine::remove();
/* will never be reached */
sleep_forever();
return 0;
}

View File

@ -0,0 +1,320 @@
/*
* \brief Emulate 'pci_dev' structure
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-04-02
*/
/*
* Copyright (C) 2012 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.
*/
/* Genode inludes */
#include <pci_session/connection.h>
#include <pci_device/client.h>
/* Linux includes */
#include <lx_emul.h>
struct bus_type pci_bus_type;
/**
* Scan PCI bus and probe for HCDs
*/
class Pci_driver
{
private:
pci_driver *_drv; /* Linux PCI driver */
Pci::Device_capability _cap; /* PCI cap */
pci_device_id const *_id; /* matched id for this driver */
public:
pci_dev *_dev; /* Linux PCI device */
private:
/* offset used in PCI config space */
enum Pci_config { IRQ = 0x3c, REV = 0x8, CMD = 0x4 };
/**
* Match class code of device with driver id
*/
bool _match(pci_device_id const *id)
{
Pci::Device_client client(_cap);
if (!((id->device_class ^ client.class_code()) & id->class_mask)) {
_id = id;
return true;
}
return false;
}
/**
* Match supported device ID of driver to this device
*/
bool _match()
{
pci_device_id const *id = _drv->id_table;
if (!id)
return false;
while (id->vendor || id->subvendor || id->class_mask) {
if (_match(id)) {
dde_kit_log(DEBUG_PCI, "Device matched %p", this);
return true;
}
id++;
}
return false;
}
/**
* Fill Linux device informations
*/
void _setup_pci_device()
{
using namespace Pci;
_dev = new (Genode::env()->heap()) pci_dev;
Device_client client(_cap);
_dev->vendor = client.vendor_id();
_dev->device = client.device_id();
_dev->device_class = client.class_code();
_dev->revision = client.config_read(REV, Device::ACCESS_8BIT);
_dev->dev.driver = &_drv->driver;
/* read interrupt line */
_dev->irq = client.config_read(IRQ, Device::ACCESS_8BIT);
/* hide ourselfs in bus structure */
_dev->bus = (struct pci_bus *)this;
/* setup resources */
bool io = false;
for (int i = 0; i < Device::NUM_RESOURCES; i++) {
Device::Resource res = client.resource(i);
_dev->resource[i].start = res.base();
_dev->resource[i].end = res.base() + res.size() - 1;
_dev->resource[i].flags = res.type() == Device::Resource::IO
? IORESOURCE_IO : 0;
/* request port I/O session */
if (res.type() == Device::Resource::IO) {
if (dde_kit_request_io(res.base(), res.size()))
PERR("Failed to request I/O: [%u,%u)",
res.base(), res.base() + res.size());
io = true;
dde_kit_log(DEBUG_PCI, "I/O [%u-%u)",
res.base(), res.base() + res.size());
}
/* request I/O memory (write combined) */
if (res.type() == Device::Resource::MEMORY)
dde_kit_log(DEBUG_PCI, "I/O memory [%x-%x)", res.base(),
res.base() + res.size());
}
/* enable bus master and io bits */
uint16_t cmd = client.config_read(CMD, Device::ACCESS_16BIT);
cmd |= io ? 0x1 : 0;
/* enable bus master */
cmd |= 0x4;
client.config_write(CMD, cmd, Device::ACCESS_16BIT);
}
/**
* Probe device with driver
*/
bool _probe()
{
_setup_pci_device();
if (!_drv->probe(_dev, _id))
return true;
PERR("Probe failed\n");
return false;
}
template <typename T>
Pci::Device::Access_size _access_size(T t)
{
switch (sizeof(T))
{
case 1:
return Pci::Device::ACCESS_8BIT;
case 2:
return Pci::Device::ACCESS_16BIT;
default:
return Pci::Device::ACCESS_32BIT;
}
}
public:
Pci_driver(pci_driver *drv, Pci::Device_capability cap)
: _drv(drv), _cap(cap), _id(0), _dev(0)
{
if (!_match())
throw -1;
if (!_probe())
throw -2;
}
~Pci_driver()
{
if (!_dev)
return;
for (int i = 0; i < Pci::Device::NUM_RESOURCES; i++) {
resource *r = &_dev->resource[i];
if (r->flags & IORESOURCE_IO)
dde_kit_release_io(r->start, (r->end - r->start) + 1);
}
destroy(Genode::env()->heap(), _dev);
}
/**
* Read/write data from/to config space
*/
template <typename T>
void config_read(unsigned int devfn, T *val)
{
Pci::Device_client client(_cap);
*val = client.config_read(devfn, _access_size(*val));
}
template <typename T>
void config_write(unsigned int devfn, T val)
{
Pci::Device_client client(_cap);
client.config_write(devfn, val, _access_size(val));
}
};
/*********************
** Linux interface **
*********************/
int pci_register_driver(struct pci_driver *drv)
{
dde_kit_log(DEBUG_PCI, "DRIVER name: %s", drv->name);
drv->driver.name = drv->name;
using namespace Genode;
Pci::Connection pci;
Pci::Device_capability cap = pci.first_device();
Pci::Device_capability old;
while (cap.valid()) {
uint8_t bus, dev, func;
Pci::Device_client client(cap);
client.bus_address(&bus, &dev, &func);
dde_kit_log(DEBUG_PCI, "bus: %x dev: %x func: %x", bus, dev, func);
Pci_driver *pci_drv= 0;
try {
pci_drv = new (env()->heap()) Pci_driver(drv, cap);
pci.on_destruction(Pci::Connection::KEEP_OPEN);
return 0;
} catch (...) {
destroy(env()->heap(), pci_drv);
pci_drv = 0;
}
old = cap;
cap = pci.next_device(cap);
pci.release_device(old);
}
return -ENODEV;
}
size_t pci_resource_start(struct pci_dev *dev, unsigned bar)
{
if (bar >= DEVICE_COUNT_RESOURCE)
return 0;
return dev->resource[bar].start;
}
size_t pci_resource_len(struct pci_dev *dev, unsigned bar)
{
size_t start = pci_resource_start(dev, bar);
if (!start)
return 0;
return (dev->resource[bar].end - start) + 1;
}
unsigned int pci_resource_flags(struct pci_dev *dev, unsigned bar)
{
if (bar >= DEVICE_COUNT_RESOURCE)
return 0;
return dev->resource[bar].flags;
}
int pci_bus_read_config_byte(struct pci_bus *bus, unsigned int devfn, int, u8 *val)
{
Pci_driver *drv = (Pci_driver *)bus;
drv->config_read(devfn, val);
dde_kit_log(DEBUG_PCI, "READ %p: %x", drv, *val);
return 0;
}
int pci_bus_read_config_word(struct pci_bus *bus, unsigned int devfn, int, u16 *val)
{
Pci_driver *drv = (Pci_driver *)bus;
drv->config_read(devfn, val);
dde_kit_log(DEBUG_PCI, "READ %p: %x", drv, *val);
return 0;
}
int pci_bus_write_config_word(struct pci_bus *bus, unsigned int devfn, int, u16 val)
{
Pci_driver *drv = (Pci_driver *)bus;
dde_kit_log(DEBUG_PCI, "WRITE %p: %x", drv, val);
drv->config_write(devfn, val);
return 0;
}
int pci_bus_write_config_byte(struct pci_bus *bus, unsigned int devfn, int, u8 val)
{
Pci_driver *drv = (Pci_driver *)bus;
dde_kit_log(DEBUG_PCI, "WRITE %p: %x", drv, val);
drv->config_write(devfn, val);
return 0;
}
const char *pci_name(const struct pci_dev *pdev)
{
/* simply return driver name */
return "dummy";
}

View File

@ -0,0 +1,194 @@
/*
* \brief Pseudo-thread implementation using setjmp/longjmp
* \author Sebastian Sumpf <Sebastian.Sumpf@genode-labs.com>
* \date 2012-04-25
*/
/*
* Copyright (C) 2012 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.
*/
#ifndef _ROUTINE_H_
#define _ROUTINE_H_
#include <base/env.h>
#include <util/list.h>
#include <libc/setjmp.h>
extern "C" {
#include <dde_kit/memory.h>
}
static const bool verbose = false;
/**
* Allows pseudo-parallel execution of functions
*/
class Routine : public Genode::List<Routine>::Element
{
private:
enum { STACK_SIZE = 0x2000 };
bool _started; /* true if already started */
jmp_buf _env; /* state */
int (*_func)(void *); /* function to call*/
void *_arg; /* argument for function */
char const *_name; /* name of this object */
char *_stack; /* stack pointer */
static Routine *_current; /* currently scheduled object */
static Routine *_dead; /* object to remove */
static bool _all; /* true when all objects must be scheduled */
/**
* List containing all registered objects
*/
static Genode::List<Routine> *_list()
{
static Genode::List<Routine> _l;
return &_l;
}
/**
* Start/restore
*/
void _run()
{
/* will never return */
if (!_started) {
_started = true;
_stack = (char *)dde_kit_simple_malloc(STACK_SIZE);
if (verbose)
PDBG("Start func %s (%p) sp: %p", _name, _func, (_stack + STACK_SIZE));
/* XXX move to platform code */
/* switch stack and call '_func(_arg)' */
asm volatile ("movl %2, 0(%0);"
"movl %1, -0x4(%0);"
"movl %0, %%esp;"
"call *-4(%%esp);"
: : "r" (_stack + STACK_SIZE), "r" (_func), "r" (_arg));
}
/* restore old state */
if (verbose)
PDBG("Schedule %s (%p)", _name, _func);
_longjmp(_env, 1);
}
/**
* Check for and remove dead objects
*/
static void _check_dead()
{
if (!_dead)
return;
_list()->remove(_dead);
destroy(Genode::env()->heap(), _dead);
_dead = 0;
}
/**
* Get next object to schedule
*/
static Routine *_next(bool all)
{
/* on schedule all start at first element */
if (all) {
_all = true;
return _list()->first();
}
/* disable all at last element */
if (_all && _current && !_current->next())
_all = false;
/* return next element (wrap at the end) */
return _current && _current->next() ? _current->next() : _list()->first();
}
public:
Routine(int (*func)(void*), void *arg, char const *name, bool started)
: _started(started), _func(func), _arg(arg), _name(name), _stack(0) { }
~Routine()
{
if (_stack)
dde_kit_simple_free(_stack);
}
/**
* Schedule next object
*
* If all is true, each object will be scheduled once.
*/
static void schedule(bool all = false) __attribute__((noinline))
{
if (!_list()->first())
return;
Routine *next = _next(all);
if (next == _current)
return;
/* return when restored */
if (_current && _setjmp(_current->_env)) {
_check_dead();
return;
}
_current = next;
_current->_run();
}
/**
* Schedule each object once
*/
static void schedule_all() { schedule(true); }
/**
* Set current to first object
*/
static void current_use_first() { _current = _list()->first(); }
/**
* Add an object
*/
static void add(int (*func)(void *), void *arg, char const *name,
bool started = false)
{
_list()->insert(new (Genode::env()->heap())
Routine(func, arg, name, started));
}
/**
* Remove this object
*/
static void remove()
{
if (!_current)
return;
_dead = _current;
schedule();
}
/**
* True when 'schedule_all' has been called and is still in progress
*/
static bool all() { return _all; }
};
#endif /* _ROUTINE_H_ */

View File

@ -0,0 +1,121 @@
/*
* \brief Main-signal receiver and signal-helper functions
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-05-23
*/
/*
* Copyright (C) 2012 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.
*/
#ifndef _SIGNAL_H_
#define _SIGNAL_H_
#include <base/env.h>
#include <base/printf.h>
#include <base/signal.h>
#include "routine.h"
/**
* Context base for IRQ, Timer, etc.
*/
class Driver_context : public Genode::Signal_context
{
public:
/**
* Perform context operation
*/
virtual void handle() = 0;
};
/**
* This singelton currently received all signals
*/
class Service_handler
{
private:
Genode::Signal_receiver *_receiver;
Service_handler() { }
public:
static Service_handler * s()
{
static Service_handler _s;
return &_s;
}
void receiver(Genode::Signal_receiver *recv) { _receiver = recv; }
/**
* Dispatch for wait for signal
*/
void process()
{
if (Routine::all()) {
Routine::schedule();
return;
}
do {
Genode::Signal s = _receiver->wait_for_signal();
/* handle signal IRQ, timer, or event signals */
Driver_context *ctx = static_cast<Driver_context *>(s.context());
ctx->handle();
} while (_receiver->pending());
}
};
/**
* Helper that holds sender and receiver
*/
class Signal_helper
{
private:
Genode::Signal_receiver *_receiver;
Genode::Signal_transmitter *_sender;
public:
Signal_helper(Genode::Signal_receiver *recv)
: _receiver(recv),
_sender(new (Genode::env()->heap()) Genode::Signal_transmitter()) { }
Genode::Signal_receiver *receiver() const { return _receiver; }
Genode::Signal_transmitter *sender() const { return _sender; }
};
namespace Timer
{
void init(Genode::Signal_receiver *recv);
}
namespace Irq
{
void init(Genode::Signal_receiver *recv);
}
namespace Event
{
void init(Genode::Signal_receiver *recv);
}
namespace Storage
{
void init(Genode::Signal_receiver *recv);
}
#endif /* _SIGNAL_H_ */

View File

@ -0,0 +1,167 @@
/*
* \brief Signal context for completions and events
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-05-23
*/
/*
* Copyright (C) 2012 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(); }
};
void Event::init(Genode::Signal_receiver *recv) {
_signal = new (Genode::env()->heap()) Signal_helper(recv); }
/************************
** linux/completion.h **
************************/
void __wake_up() { Routine::schedule_all(); }
void __wait_event() {
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;
}

View File

@ -0,0 +1,140 @@
/*
* \brief Signal context for IRQ's
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-05-23
*/
/*
* Copyright (C) 2012 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;
/**
* 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:
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->next())
if (i->_irq == irq)
return i;
return 0;
}
/* called by the DDE kit upon IRQ */
static void _dde_handler(void *irq)
{
Irq_context *ctx = static_cast<Irq_context *>(irq);
/* set context & submit signal */
_signal->sender()->context(ctx->_ctx_cap);
_signal->sender()->submit();
}
public:
Irq_context(unsigned int irq)
: _irq(irq),
_ctx_cap(_signal->receiver()->manage(this))
{
/* register at DDE (shared) */
dde_kit_interrupt_attach(_irq, 1, 0, _dde_handler, this);
dde_kit_interrupt_enable(_irq);
_list()->insert(this);
}
void handle()
{
/* report IRQ to all clients */
for (Irq_handler *h = _handler_list.first(); h; h = h->next()) {
irqreturn_t rc;
do {
rc = h->handler(_irq, h->dev);
}
while (rc == IRQ_HANDLED);
dde_kit_log(DEBUG_IRQ, "IRQ: %u ret: %u", _irq, rc);
if (rc == IRQ_HANDLED) {
Routine::schedule_all();
return;
}
}
}
/**
* 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);
}
};
void Irq::init(Genode::Signal_receiver *recv) {
_signal = new (Genode::env()->heap()) Signal_helper(recv); }
/***********************
** 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", irq);
Irq_context::request_irq(irq, handler, dev);
return 0;
}

View File

@ -0,0 +1,150 @@
/*
* \brief Signal context for timer events
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-05-23
*/
/*
* Copyright (C) 2012 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);
}
/**
* 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;
}

View File

@ -0,0 +1,204 @@
/*
* \brief Block-session implementation for USB storage
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-05-23
*/
/*
* Copyright (C) 2012 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.
*/
#ifndef _STORAGE__COMPONENT_H_
#define _STORAGE__COMPONENT_H_
#include <root/component.h>
#include <block_session/rpc_object.h>
#include "signal.h"
namespace Block {
using namespace Genode;
class Session_component;
struct Device
{
/**
* Request block size for driver and medium
*/
virtual Genode::size_t block_size() = 0;
/**
* Request capacity of medium in blocks
*/
virtual Genode::size_t block_count() = 0;
virtual void io(Session_component *session, Packet_descriptor &packet,
addr_t virt, addr_t phys) = 0;
};
template <typename T>
class Signal_dispatcher : public Driver_context,
public Signal_context_capability
{
private:
T &obj;
void (T::*member) ();
Signal_receiver *sig_rec;
public:
/**
* Constructor
*
* \param sig_rec signal receiver to associate the signal
* handler with
* \param obj,member object and member function to call when
* the signal occurs
*/
Signal_dispatcher(Signal_receiver *sig_rec,
T &obj, void (T::*member)())
:
Signal_context_capability(sig_rec->manage(this)),
obj(obj), member(member),
sig_rec(sig_rec)
{ }
~Signal_dispatcher() { sig_rec->dissolve(this); }
void handle() { (obj.*member)(); }
};
class Session_component : public Session_rpc_object
{
private:
addr_t _rq_phys ; /* physical addr. of rq_ds */
Device *_device; /* device this session is using */
Signal_dispatcher<Session_component> _process_packet_dispatcher;
void _process_packets()
{
while (tx_sink()->packet_avail())
{
Packet_descriptor packet = tx_sink()->get_packet();
addr_t virt = (addr_t)tx_sink()->packet_content(packet);
addr_t phys = _rq_phys + packet.offset();
try {
_device->io(this, packet, virt, phys);
} catch (...) { PERR("Failed to queue packet"); }
}
}
public:
/**
* Constructor
*/
Session_component(Dataspace_capability rq_ds,
Rpc_entrypoint &ep,
Signal_receiver *sig_rec,
Device *device)
:
Session_rpc_object(rq_ds, ep),
_rq_phys(Dataspace_client(rq_ds).phys_addr()),
_device(device),
_process_packet_dispatcher(sig_rec, *this,
&Session_component::_process_packets)
{
/*
* Register '_process_packets' dispatch function as signal
* handler for packet-avail and ready-to-ack signals.
*/
_tx.sigh_packet_avail(_process_packet_dispatcher);
_tx.sigh_ready_to_ack(_process_packet_dispatcher);
}
void info(size_t *blk_count, size_t *blk_size,
Operations *ops)
{
*blk_count = _device->block_count();
*blk_size = _device->block_size();
ops->set_operation(Packet_descriptor::READ);
ops->set_operation(Packet_descriptor::WRITE);
}
void complete(Packet_descriptor &packet, bool success)
{
packet.succeeded(success);
tx_sink()->acknowledge_packet(packet);
}
};
/*
* Shortcut for single-client root component
*/
typedef Root_component<Session_component, Single_client> Root_component;
/**
* Root component, handling new session requests
*/
class Root : public Root_component
{
private:
Rpc_entrypoint &_ep;
Signal_receiver *_sig_rec;
Device *_device;
protected:
/**
* Always returns the singleton block-session component
*/
Session_component *_create_session(const char *args)
{
size_t ram_quota =
Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
size_t tx_buf_size =
Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
/* delete ram quota by the memory needed for the session */
size_t session_size = max((size_t)4096,
sizeof(Session_component)
+ sizeof(Allocator_avl));
if (ram_quota < session_size)
throw Root::Quota_exceeded();
/*
* Check if donated ram quota suffices for both communication
* buffers. Also check both sizes separately to handle a
* possible overflow of the sum of both sizes.
*/
if (tx_buf_size > ram_quota - session_size) {
PERR("insufficient 'ram_quota', got %zd, need %zd",
ram_quota, tx_buf_size + session_size);
throw Root::Quota_exceeded();
}
return new (md_alloc())
Session_component(env()->ram_session()->alloc(tx_buf_size),
_ep, _sig_rec, _device);
}
public:
Root(Rpc_entrypoint *session_ep, Allocator *md_alloc,
Signal_receiver *sig_rec, Device *device)
:
Root_component(session_ep, md_alloc),
_ep(*session_ep), _sig_rec(sig_rec), _device(device)
{ }
};
}
#endif /* _STORAGE__COMPONENT_H_ */

View File

@ -0,0 +1,218 @@
/*
* \brief SCSI support emulation
* \author Christian Helmuth
* \author Sebastian Sumpf
* \date 2009-10-29
*
* XXX NOTES XXX
*
* struct scsi_host_template
*
* struct scsi_host
*
* host_lock used by scsi_unlock, scsi_lock
* max_id used by usb_stor_report_device_reset
*
* struct scsi_cmnd
*
* functions
*
* scsi_add_host
* scsi_host_alloc
* scsi_host_get
* scsi_host_put
* scsi_remove_host
* scsi_report_bus_reset
* scsi_report_device_reset
* scsi_scan_host
*/
#include <lx_emul.h>
#include "scsi.h"
#define DEBUG_SCSI 0
/***************
** SCSI host **
***************/
struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *t, int priv_size)
{
dde_kit_log(DEBUG_SCSI, "t=%p, priv_size=%d", t, priv_size);
static int free = 1;
/* XXX we not some extra space for hostdata[] */
static char buffer[4096] __attribute__((aligned(4096)));
static struct Scsi_Host *host = (struct Scsi_Host *)buffer;
/* FIXME we support only one host for now */
if (!free) return 0;
free = 0;
host->host_lock = &host->default_lock;
spin_lock_init(host->host_lock);
host->host_no = 13;
host->max_id = 8;
host->hostt = t;
// rval = scsi_setup_command_freelist(shost);
// if (rval)
// goto fail_kfree;
// shost->ehandler = kthread_run(scsi_error_handler, shost,
// "scsi_eh_%d", shost->host_no);
// if (IS_ERR(shost->ehandler)) {
// rval = PTR_ERR(shost->ehandler);
// goto fail_destroy_freelist;
// }
return host;
}
static struct page *_page(struct scsi_cmnd *cmnd)
{
return (struct page *)cmnd->sdb.table.sgl->page_link;
}
void scsi_alloc_buffer(size_t size, struct scsi_cmnd *cmnd)
{
scsi_setup_buffer(cmnd, size, 0, 0);
struct scatterlist *sgl = cmnd->sdb.table.sgl;
struct page *page = _page(cmnd);
page->virt = kmalloc(size, 0);
page->phys = dma_map_single_attrs(0, page->virt, 0, 0, 0);
sgl->dma_address = page->phys;
}
void scsi_setup_buffer(struct scsi_cmnd *cmnd, size_t size, void *virt, dma_addr_t addr)
{
cmnd->sdb.table.nents = 1;
cmnd->sdb.length = size;
struct scatterlist *sgl = cmnd->sdb.table.sgl;
struct page *page = _page(cmnd);
page->virt = virt;
page->phys = addr;
sgl->page_link = (unsigned long)page;
sgl->offset = 0;
sgl->length = size;
sgl->dma_address = addr;
sgl->last = 1;
}
void scsi_free_buffer(struct scsi_cmnd *cmnd)
{
struct page *page = _page(cmnd);
if (page)
kfree(page->virt);
}
void *scsi_buffer_data(struct scsi_cmnd *cmnd)
{
return _page(cmnd)->virt;
}
struct scsi_cmnd *_scsi_alloc_command()
{
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)kmalloc(sizeof(struct scsi_cmnd), GFP_KERNEL);
cmnd->sdb.table.sgl = (struct scatterlist *)kmalloc(sizeof(struct scatterlist), GFP_KERNEL);
cmnd->cmnd = kzalloc(MAX_COMMAND_SIZE, 0);
cmnd->sdb.table.sgl->page_link = (unsigned long) kzalloc(sizeof(struct page), 0);
return cmnd;
}
void _scsi_free_command(struct scsi_cmnd *cmnd)
{
kfree((void *)cmnd->sdb.table.sgl->page_link);
kfree(cmnd->sdb.table.sgl);
kfree(cmnd->cmnd);
kfree(cmnd);
}
static void inquiry_done(struct scsi_cmnd *cmnd)
{
char *data = (char *)scsi_buffer_data(cmnd);
dde_kit_printf("Vendor id: %c%c%c%c%c%c%c%c Product id: %s\n",
data[8], data[9], data[10], data[11], data[12],
data[13], data[14], data[15], &data[16]);
complete(cmnd->back);
}
static void scsi_done(struct scsi_cmnd *cmd)
{
complete(cmd->back);
}
void scsi_scan_host(struct Scsi_Host *host)
{
struct scsi_cmnd *cmnd;
struct scsi_device *sdev;
struct scsi_target *target;
struct completion compl;
void *result;
init_completion(&compl);
sdev = (struct scsi_device *)kmalloc(sizeof(struct scsi_device), GFP_KERNEL);
target = (struct scsi_target *)kmalloc(sizeof(struct scsi_target), GFP_KERNEL);
cmnd = _scsi_alloc_command();
/* init device */
sdev->sdev_target = target;
sdev->host = host;
sdev->id = 0;
sdev->lun = 0;
host->hostt->slave_alloc(sdev);
host->hostt->slave_configure(sdev);
/* inquiry (36 bytes for usb) */
scsi_alloc_buffer(sdev->inquiry_len, cmnd);
cmnd->cmnd[0] = INQUIRY;
cmnd->cmnd[4] = sdev->inquiry_len;
cmnd->device = sdev;
cmnd->cmd_len = 6;
cmnd->sc_data_direction = DMA_FROM_DEVICE;
cmnd->back = &compl;
cmnd->scsi_done = inquiry_done;
host->hostt->queuecommand(host, cmnd);
wait_for_completion(&compl);
/* if PQ and PDT are zero we have a direct access block device conntected */
result = scsi_buffer_data(cmnd);
if (!((char*)result)[0])
scsi_add_device(sdev);
else {
kfree(sdev);
kfree(target);
}
scsi_free_buffer(cmnd);
_scsi_free_command(cmnd);
}
/**********************
** scsi/scsi_cmnd.h **
**********************/
unsigned scsi_bufflen(struct scsi_cmnd *cmnd) { return cmnd->sdb.length; }
struct scatterlist *scsi_sglist(struct scsi_cmnd *cmnd) { return cmnd->sdb.table.sgl; }
unsigned scsi_sg_count(struct scsi_cmnd *cmnd) { return cmnd->sdb.table.nents; }

View File

@ -0,0 +1,88 @@
/*
* \brief SCSI helpers
* \author Sebastian Sumpf
* \date 2012-05-06
*/
/*
* Copyright (C) 2012 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.
*/
#ifndef _SCSI_H_
#define _SCSI_H_
struct scsi_cmnd;
#ifdef __cplusplus
extern "C" {
#endif
/**
* Add a SCSI device
*
* \param sdev Device to add
*/
void scsi_add_device(struct scsi_device *sdev);
/**
* Alloc data buffer for command
*
* \param size Size of buffer
* \param cmnd Command to assciate buffer
*/
void scsi_alloc_buffer(size_t size, struct scsi_cmnd *cmnd);
/**
* Fill command
*
* \param cmnd Command buffer to setup
* \param size Data size
* \param virt Virtual address of buffer
* \param addr DMA address of buffer
*/
void scsi_setup_buffer(struct scsi_cmnd *cmnd, size_t size, void *virt, dma_addr_t addr);
/**
* Free data buffer of command
*
* \param cmnd Command
*/
void scsi_free_buffer(struct scsi_cmnd *cmnd);
/**
* Get buffer data for command
*
* \param cmnd Command to retrieve buffer pointer
*
* \return Buffer pointer
*/
void *scsi_buffer_data(struct scsi_cmnd *cmnd);
/**
* Allocate a SCSI command
*
* \return Allocated command or zero on failure
*/
struct scsi_cmnd *_scsi_alloc_command();
/**
* Free a SCSI command
*
* \param cmnd Command
*/
void _scsi_free_command(struct scsi_cmnd *cmnd);
#ifdef __cplusplus
}
#endif
#endif /* _SCSI_H_ */

View File

@ -0,0 +1,177 @@
/*
* \brief USB storage glue
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
* \date 2012-05-06
*/
/*
* Copyright (C) 2012 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/rpc_server.h>
#include <block_session/block_session.h>
#include <cap_session/connection.h>
#include <util/endian.h>
#include <util/list.h>
#include <lx_emul.h>
#include "component.h"
#include "signal.h"
#include "scsi.h"
static Signal_helper *_signal = 0;
class Storage_device : public Genode::List<Storage_device>::Element,
public Block::Device
{
private:
Genode::size_t _block_size;
Block::sector_t _block_count;
struct scsi_device *_sdev;
static void _sync_done(struct scsi_cmnd *cmnd) {
complete((struct completion *)cmnd->back); }
static void _async_done(struct scsi_cmnd *cmnd)
{
Block::Session_component *session = static_cast<Block::Session_component *>(cmnd->session);
Block::Packet_descriptor *packet = static_cast<Block::Packet_descriptor *>(cmnd->packet);
if (verbose)
PDBG("ACK packet for block: %zu status: %d", packet->block_number(), cmnd->result);
session->complete(*packet, true);
_scsi_free_command(cmnd);
}
void _capacity()
{
struct completion comp;
struct scsi_cmnd *cmnd = _scsi_alloc_command();
/* alloc data for command */
scsi_alloc_buffer(8, cmnd);
cmnd->cmnd[0] = READ_CAPACITY;
cmnd->cmd_len = 10;
cmnd->device = _sdev;
cmnd->sc_data_direction = DMA_FROM_DEVICE;
init_completion(&comp);
cmnd->back = &comp;
cmnd->scsi_done = _sync_done;
_sdev->host->hostt->queuecommand(_sdev->host, cmnd);
wait_for_completion(&comp);
Genode::uint32_t *data = (Genode::uint32_t *)scsi_buffer_data(cmnd);
_block_count = bswap(data[0]);
_block_size = bswap(data[1]);
/* if device returns the highest block number */
if (!_sdev->fix_capacity)
_block_count++;
if (verbose)
PDBG("block size: %zu block count: %llu", _block_size, _block_count);
scsi_free_buffer(cmnd);
_scsi_free_command(cmnd);
}
Storage_device(struct scsi_device *sdev) : _sdev(sdev)
{
/* read device capacity */
_capacity();
}
public:
static Storage_device *add(struct scsi_device *sdev) {
return new (Genode::env()->heap()) Storage_device(sdev); }
Genode::size_t block_size() { return _block_size; }
Genode::size_t block_count() { return _block_count; }
void io(Block::Session_component *session, Block::Packet_descriptor &packet,
Genode::addr_t virt, Genode::addr_t phys)
{
Block::sector_t block_nr = packet.block_number();
Genode::uint16_t block_count = packet.block_count() & 0xffff;
bool read = packet.operation() == Block::Packet_descriptor::WRITE ? false : true;
if (block_nr > _block_count)
throw -1;
if (verbose)
PDBG("PACKET: phys: %lx block: %llu count: %u %s",
phys, block_nr, block_count, read ? "read" : "write");
struct scsi_cmnd *cmnd = _scsi_alloc_command();
cmnd->cmnd[0] = read ? READ_10 : WRITE_10;
cmnd->cmd_len = 10;
cmnd->device = _sdev;
cmnd->sc_data_direction = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
cmnd->scsi_done = _async_done;
Block::Packet_descriptor *p = new (Genode::env()->heap()) Block::Packet_descriptor();
*p = packet;
cmnd->packet = (void *)p;
cmnd->session = (void *)session;
Genode::uint32_t be_block_nr = bswap<Genode::uint32_t>(block_nr);
Genode::memcpy(&cmnd->cmnd[2], &be_block_nr, 4);
/* transfer one block */
Genode::uint16_t be_block_count = bswap(block_count);
Genode::memcpy(&cmnd->cmnd[7], &be_block_count, 2);
/* setup command */
scsi_setup_buffer(cmnd, block_count * _block_size, (void *)virt, phys);
/*
* Required by 'last_sector_hacks' in 'drivers/usb/storage/transprot.c
*/
struct request req;
req.rq_disk = 0;
cmnd->request = &req;
/* send command to host driver */
if (_sdev->host->hostt->queuecommand(_sdev->host, cmnd)) {
throw -2;
}
}
};
void Storage::init(Genode::Signal_receiver *recv) {
_signal = new (Genode::env()->heap()) Signal_helper(recv); }
void scsi_add_device(struct scsi_device *sdev)
{
using namespace Genode;
static bool announce = false;
Storage_device *device = Storage_device::add(sdev);
/*
* XXX move to 'main'
*/
if (!announce) {
static Cap_connection cap_stor;
static Rpc_entrypoint ep_stor(&cap_stor, 4096, "usb_stor_ep");
static Block::Root root(&ep_stor, env()->heap(), _signal->receiver(), device);
env()->parent()->announce(ep_stor.manage(&root));
announce = true;
}
}

View File

@ -0,0 +1,92 @@
TARGET = usb_drv
REQUIRES = x86 32bit
LIBS = cxx env dde_kit server libc-setjmp signal
SRC_CC = main.cc lx_emul.cc pci_driver.cc irq.cc timer.cc event.cc storage.cc \
input_component.cc
SRC_C = dummies.c scsi.c evdev.c
CONTRIB_DIR := $(REP_DIR)/contrib
DRIVERS_DIR := $(CONTRIB_DIR)/drivers
USB_DIR := $(DRIVERS_DIR)/usb
#
# The order of include-search directories is important, we need to look into
# 'contrib' before falling back to our custom 'lx_emul.h' header.
#
INC_DIR += $(PRG_DIR)
INC_DIR += $(CONTRIB_DIR)/include
INC_DIR += $(shell pwd)
CC_OPT += -U__linux__ -D__KERNEL__
CC_OPT += -DCONFIG_USB_DEVICEFS -DCONFIG_HOTPLUG -DCONFIG_PCI -DDEBUG
CC_WARN = -Wall -Wno-unused-variable -Wno-uninitialized \
-Wno-unused-function \
CC_C_OPT += -Wno-implicit-function-declaration -Wno-unused-but-set-variable \
-Wno-pointer-sign
#
# Suffix of global 'module_init' function
#
MOD_SUFFIX =
CC_OPT += -DMOD_SUFFIX=$(MOD_SUFFIX)
# USB core
SRC_C += $(addprefix usb/core/,$(notdir $(wildcard $(USB_DIR)/core/*.c)))
SRC_C += usb/usb-common.c
# USB host-controller driver
#SRC_C += $(addprefix usb/host/,uhci-hcd.c pci-quirks.c ehci-hcd.c ohci-hcd.c)
SRC_C += $(addprefix usb/host/,pci-quirks.c uhci-hcd.c ehci-hcd.c)
# USB hid
SRC_C += $(addprefix hid/usbhid/,hid-core.c hid-quirks.c usbmouse.c usbkbd.c)
SRC_C += hid/hid-input.c hid/hid-core.c input/evdev.c input/input.c
# USB storage
SRC_C += $(addprefix usb/storage/,scsiglue.c protocol.c transport.c usb.c \
initializers.c option_ms.c sierra_ms.c usual-tables.c)
# SCSI
SRC_C += $(addprefix scsi/,scsi.c constants.c)
#
# Determine the header files included by the contrib code. For each
# of these header files we create a symlink to 'lx_emul.h'.
#
GEN_INCLUDES := $(shell grep -rh "^\#include .*\/" $(CONTRIB_DIR) |\
sed "s/^\#include *[<\"]\(.*\)[>\"].*/\1/" | sort | uniq)
#
# Filter out some black-listed headers
#
NO_GEN_INCLUDES := ../../scsi/sd.h
#
# Filter out original Linux headers that exist in the contrib directory
#
NO_GEN_INCLUDES := $(shell cd $(CONTRIB_DIR)/include; find -name "*.h" | sed "s/.\///")
GEN_INCLUDES := $(filter-out $(NO_GEN_INCLUDES),$(GEN_INCLUDES))
#
# Make sure to create the header symlinks prior building
#
$(SRC_C:.c=.o) $(SRC_CC:.cc=.o): $(GEN_INCLUDES)
#
# Add prefix, since there are two hid-core.c with the same module init function
#
hid/hid-core.o: MOD_SUFFIX="_core"
$(GEN_INCLUDES):
$(VERBOSE)mkdir -p $(dir $@)
$(VERBOSE)ln -s $(REP_DIR)/src/drivers/usb/lx_emul.h $@
vpath %.c $(DRIVERS_DIR)
vpath %.c $(USB_DIR)/host
vpath %.cc $(PRG_DIR)/signal
vpath %.c $(PRG_DIR)/input
vpath %.cc $(PRG_DIR)/input
vpath %.cc $(PRG_DIR)/storage
vpath %.c $(PRG_DIR)/storage

View File

@ -30,6 +30,11 @@
namespace Block { namespace Block {
/**
* Sector type for block session
*/
typedef Genode::uint64_t sector_t;
/** /**
* Represents an operation request with respect to a block, * Represents an operation request with respect to a block,
* the data associated with the 'Packet_descriptor' is either * the data associated with the 'Packet_descriptor' is either

View File

@ -22,7 +22,7 @@ namespace Block {
class Session_rpc_object : public Genode::Rpc_object<Session, Session_rpc_object> class Session_rpc_object : public Genode::Rpc_object<Session, Session_rpc_object>
{ {
private: protected:
Packet_stream_tx::Rpc_object<Tx> _tx; Packet_stream_tx::Rpc_object<Tx> _tx;

View File

@ -1,9 +1,18 @@
# #
# Drivers ported from the Linux kernel # Drivers ported from the Linux kernel (USB)
# #
# Not supported on the ARM architecture. # Not supported on the ARM architecture.
# #
#REPOSITORIES += $(GENODE_DIR)/dde_linux
#
# Additional drivers ported from the Linux kernel (audio, Intel GEM)
#
# Not supported on the ARM architecture.
#
# This repository is deprecated.
#
#REPOSITORIES += $(GENODE_DIR)/linux_drivers #REPOSITORIES += $(GENODE_DIR)/linux_drivers
# #