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-pistachio/contrib
/dde_ipxe/contrib
/dde_linux/contrib
/dde_linux/download
/libports/contrib
/libports/download
/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
file at the top level of the repository.
:'linux_drivers':
:'dde_linux':
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':

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 {
/**
* Sector type for block session
*/
typedef Genode::uint64_t sector_t;
/**
* Represents an operation request with respect to a block,
* 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>
{
private:
protected:
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.
#
#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
#