diff --git a/dde_linux/Makefile b/dde_linux/Makefile
index d87ad8a1a..73803755c 100644
--- a/dde_linux/Makefile
+++ b/dde_linux/Makefile
@@ -56,13 +56,11 @@ CONTENT_INPUT := input.c evdev.c input-compat.h
CONTENT += $(addprefix drivers/input/,$(CONTENT_INPUT))
CONTENT += include/linux/input/mt.h
+# Panda board usb network driver
+CONTENT_NET = usbnet.c smsc95xx.c smsc95xx.h
+CONTENT += $(addprefix drivers/net/usb/,$(CONTENT_NET))
+CONTENT += include/linux/usb/usbnet.h
-# Panda board
-#CONTENT += drivers/mfd/omap-usb-host.c
-#CONTENT += arch/arm/plat-omap/include/plat/usb.h
-#CONTENT_ARCH = usb-host.c mux.h mux2420.h mux2430.h mux34xx.h mux44xx.h
-#CONTENT += $(addprefix arch/arm/mach-omap2/,$(CONTENT_ARCH))
-#CONTRIB_CONTENT := $(addprefix $(CONTRIB_DIR)/,$(CONTENT))
#
#
# Utility to check if a tool is installed
diff --git a/dde_linux/README b/dde_linux/README
index c96d02d3c..55fcfc772 100644
--- a/dde_linux/README
+++ b/dde_linux/README
@@ -22,7 +22,7 @@ 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
+Storage
~~~~~~~
Currently supports one USB storage device. Hot plugging has not been tested. A
@@ -35,3 +35,31 @@ Configuration snippet:
!
!
!
+
+
+Network (Nic)
+~~~~~~~~~~~~~
+
+Supported on PandaBoard only using the 'smsc95xx' driver.
+
+Configuration snippet:
+
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+!
+
+Please observe that this setup starts the HID and Nic service at the same time.
+Also there is the 'mac' attribute where one can specify the hardware address of
+the network interface. This is necessary in case the EEPROM of the network card
+cannot be accessed via the host controller making it impossible to retrieve the
+devices hardware address. If this is the case and no 'mac' attribute is given a
+fallback address will be assigned to the network device. Note that the fallback
+address will always be the same.
diff --git a/dde_linux/patches/csum.patch b/dde_linux/patches/csum.patch
new file mode 100644
index 000000000..b8258c20d
--- /dev/null
+++ b/dde_linux/patches/csum.patch
@@ -0,0 +1,17 @@
+diff -r 4d66ab105ff0 drivers/net/usb/smsc95xx.c
+--- a/drivers/net/usb/smsc95xx.c Fri Jul 06 17:50:36 2012 +0200
++++ b/drivers/net/usb/smsc95xx.c Fri Jul 06 17:55:13 2012 +0200
+@@ -1032,7 +1032,12 @@
+
+ static void smsc95xx_rx_csum_offload(struct sk_buff *skb)
+ {
+- skb->csum = *(u16 *)(skb_tail_pointer(skb) - 2);
++ /*
++ * Use bytewise access to avoid alignment issues on packets that have none
++ * aligned sizes
++ */
++ char *tail = skb_tail_pointer(skb);
++ skb->csum = (*(tail - 2) << 8) | *(tail - 1);
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ skb_trim(skb, skb->len - 2);
+ }
diff --git a/dde_linux/run/usb_net.run b/dde_linux/run/usb_net.run
new file mode 100644
index 000000000..399db51b2
--- /dev/null
+++ b/dde_linux/run/usb_net.run
@@ -0,0 +1,80 @@
+#
+# \brief Test for using the lwIP TCP/IP stack over USB
+# \author Sebastian Sumpf
+# \date 2012-07-06
+#
+# This test case executes a small HTTP server, it has been used on PandaBoard
+# hardware only, though it should execute but not do anything on other hardware
+#
+
+#
+# Build
+#
+
+build {
+ core init
+ drivers/pci drivers/timer drivers/usb
+ test/lwip/http_srv
+}
+
+create_boot_directory
+
+#
+# Generate config
+#
+
+set config {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+}
+
+install_config $config
+
+#
+# Boot modules
+#
+
+# generic modules
+set boot_modules {
+ core init timer
+ usb_drv
+ ld.lib.so libc.lib.so libc_log.lib.so lwip.lib.so test-lwip_httpsrv
+}
+
+build_boot_image $boot_modules
+
+# vi: set ft=tcl :
diff --git a/dde_linux/src/drivers/usb/arm/platform/platform.cc b/dde_linux/src/drivers/usb/arm/platform/platform.cc
index 26cbc024d..dbd8a061f 100644
--- a/dde_linux/src/drivers/usb/arm/platform/platform.cc
+++ b/dde_linux/src/drivers/usb/arm/platform/platform.cc
@@ -61,7 +61,6 @@ static struct ehci_hcd_omap_platform_data _ehci_data
};
-extern "C" void module_ehci_hcd_init();
/**
@@ -370,9 +369,22 @@ static void omap_ehci_init()
}
-void platform_hcd_init(void)
+extern "C" void module_ehci_hcd_init();
+extern "C" int module_usbnet_init();
+extern "C" int module_smsc95xx_init();
+
+void platform_hcd_init(Services *services)
{
+ /* register netowrk */
+ if (services->nic) {
+ module_usbnet_init();
+ module_smsc95xx_init();
+ }
+
+ /* register EHCI controller */
module_ehci_hcd_init();
+
+ /* initialize EHCI */
omap_ehci_init();
/* setup EHCI-controller platform device */
diff --git a/dde_linux/src/drivers/usb/arm/platform/platform.h b/dde_linux/src/drivers/usb/arm/platform/platform.h
index 638e2f440..d02f9901e 100644
--- a/dde_linux/src/drivers/usb/arm/platform/platform.h
+++ b/dde_linux/src/drivers/usb/arm/platform/platform.h
@@ -18,6 +18,8 @@
extern "C" {
#endif
+#include
+
static inline
void platform_execute(void *sp, void *func, void *arg)
{
@@ -28,9 +30,6 @@ void platform_execute(void *sp, void *func, void *arg)
: : "r"(sp), "r"(func), "r"(arg) : "r0");
}
-
-void platform_hcd_init(void);
-
#ifdef __cplusplus
}
#endif
diff --git a/dde_linux/src/drivers/usb/dummies.c b/dde_linux/src/drivers/usb/dummies.c
index 237eee533..f08e5f3c7 100644
--- a/dde_linux/src/drivers/usb/dummies.c
+++ b/dde_linux/src/drivers/usb/dummies.c
@@ -197,7 +197,6 @@ void mdelay(unsigned long msecs) { TRACE; }
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; }
@@ -236,6 +235,7 @@ int signal_pending(struct task_struct *p) { TRACE; return 0; }
void schedule(void) { TRACE; }
void yield(void) { TRACE; }
void cpu_relax(void) { TRACE; udelay(1); }
+signed long schedule_timeout(signed long timeout) { TRACE; return 0; }
struct task_struct *current;
@@ -521,7 +521,7 @@ void kunmap(struct page *page) { TRACE; }
** asm-generic/io.h **
**********************/
-void iounmap(volatile void *addr) { TRACE; }
+void iounmap(volatile void *addr) { TRACE; }
void native_io_delay(void) { TRACE; }
@@ -793,3 +793,90 @@ struct regulator *regulator_get(struct device *dev, const char *id) { TRACE; ret
int omap_usbhs_enable(struct device *dev) { TRACE; return 0; }
void omap_usbhs_disable(struct device *dev) { TRACE; }
+
+
+/********************
+ ** linux/skbuff.h **
+ ********************/
+
+unsigned char *__skb_put(struct sk_buff *skb, unsigned int len) { TRACE; return 0; }
+int skb_checksum_start_offset(const struct sk_buff *skb) { TRACE; return 0; }
+struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
+ int newheadroom, int newtailroom,
+ gfp_t gfp_mask) { TRACE; return 0; }
+int skb_tailroom(const struct sk_buff *skb) { TRACE; return 0; }
+
+int skb_queue_empty(const struct sk_buff_head *list) { TRACE; return 1; }
+void skb_queue_purge(struct sk_buff_head *list) { TRACE; }
+
+void skb_tx_timestamp(struct sk_buff *skb) { TRACE; }
+bool skb_defer_rx_timestamp(struct sk_buff *skb) { TRACE; return 0; }
+
+void dev_kfree_skb_any(struct sk_buff *skb) { TRACE; }
+
+
+/*********************
+ ** linux/ethtool.h **
+ *********************/
+
+__u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) { TRACE; return 0; }
+u32 ethtool_op_get_link(struct net_device *dev) { TRACE; return 0; }
+
+
+/***********************
+ ** linux/netdevice.h **
+ ***********************/
+
+u32 netif_msg_init(int debug_value, int default_msg_enable_bits) { TRACE; return 0; }
+
+void netif_start_queue(struct net_device *dev) { TRACE; }
+void netif_device_detach(struct net_device *dev) { TRACE; }
+void netif_stop_queue(struct net_device *dev) { TRACE; }
+void netif_wake_queue(struct net_device *dev) { TRACE; }
+void netif_device_attach(struct net_device *dev) { TRACE; }
+void unregister_netdev(struct net_device *dev) { TRACE; }
+void free_netdev(struct net_device *dev) { TRACE; }
+void netif_carrier_off(struct net_device *dev) { TRACE; }
+
+int netdev_mc_empty(struct net_device *dev) { TRACE; return 1; }
+
+/*****************
+ ** linux/mii.h **
+ *****************/
+
+unsigned int mii_check_media (struct mii_if_info *mii,
+ unsigned int ok_to_print,
+ unsigned int init_media) { TRACE; return 0; }
+int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) { TRACE; return 0; }
+int mii_link_ok (struct mii_if_info *mii) { TRACE; return 0; }
+
+int generic_mii_ioctl(struct mii_if_info *mii_if,
+ struct mii_ioctl_data *mii_data, int cmd,
+ unsigned int *duplex_changed) { TRACE; return 0; }
+struct mii_ioctl_data *if_mii(struct ifreq *rq) { TRACE; return 0; }
+
+
+/*************************
+ ** linux/etherdevice.h **
+ *************************/
+
+__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) { TRACE; return 0; }
+int eth_mac_addr(struct net_device *dev, void *p) { TRACE; return 0; }
+int eth_validate_addr(struct net_device *dev) { TRACE; return 0; }
+
+
+/**********************
+ ** linux/inerrupt.h **
+ **********************/
+
+void tasklet_kill(struct tasklet_struct *t) { TRACE; }
+
+
+/********************
+ ** asm/checksum.h **
+ ********************/
+
+__wsum csum_partial(const void *buff, int len, __wsum wsum) { TRACE; return 0; }
+__sum16 csum_fold(__wsum sum) { TRACE; return 0; }
+
+
diff --git a/dde_linux/src/drivers/usb/lx_emul.cc b/dde_linux/src/drivers/usb/lx_emul.cc
index 4b1eca730..177f27969 100644
--- a/dde_linux/src/drivers/usb/lx_emul.cc
+++ b/dde_linux/src/drivers/usb/lx_emul.cc
@@ -77,13 +77,15 @@ void mutex_unlock(struct mutex *m) { if (m->lock) dde_kit_lock_unlock( m->lock);
void *kmalloc(size_t size, gfp_t flags)
{
- return dde_kit_large_malloc(size);
+ /* align at least four byte alignment */
+ void *addr = Genode::Dma::pool()->alloc(size, 2);
+ return addr;
}
void *kzalloc(size_t size, gfp_t flags)
{
- void *addr = dde_kit_large_malloc(size);
+ void *addr = kmalloc(size, flags);
if (addr)
Genode::memset(addr, 0, size);
return addr;
@@ -100,7 +102,8 @@ void *kcalloc(size_t n, size_t size, gfp_t flags)
void kfree(const void *p)
{
- dde_kit_large_free((void *)p);
+ //dde_kit_large_free((void *)p);
+ Genode::Dma::pool()->free((void *)p);
}
@@ -396,6 +399,13 @@ class Driver : public Genode::List::Element
*/
bool match(struct device *dev)
{
+ /*
+ * Don't try if buses don't match, since drivers often use 'container_of'
+ * which might cast the device to non-matching type
+ */
+ if (_drv->bus != dev->bus)
+ return false;
+
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);
@@ -594,7 +604,7 @@ void dma_pool_destroy(struct dma_pool *d)
static void* _alloc(size_t size, int align, dma_addr_t *dma)
{
- void *addr = Genode::Dma::pool()->alloc(size, align);
+ void *addr = Genode::Dma::pool()->alloc(size, align < 2 ? 2 : align);
if (!addr)
return 0;
@@ -609,8 +619,6 @@ static void* _alloc(size_t size, int align, dma_addr_t *dma)
void *dma_pool_alloc(struct dma_pool *d, gfp_t f, dma_addr_t *dma)
{
return _alloc(d->size, d->align, dma);
-// return _alloc(0x1000, 12, dma);
-
}
@@ -646,7 +654,7 @@ dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
enum dma_data_direction dir,
struct dma_attrs *attrs)
{
- dma_addr_t phys = (dma_addr_t)dde_kit_pgtab_get_physaddr(ptr);
+ dma_addr_t phys = (dma_addr_t)Genode::Dma::pool()->phys_addr(ptr);
dde_kit_log(DEBUG_DMA, "virt: %p phys: %lx", ptr, phys);
return phys;
}
@@ -666,17 +674,6 @@ int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
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 **
*********************/
@@ -743,3 +740,115 @@ resource_size_t resource_size(const struct resource *res)
{
return res->end - res->start + 1;
}
+
+
+/****************
+ ** Networking **
+ ****************/
+
+
+/*************************
+ ** linux/etherdevice.h **
+ *************************/
+
+struct net_device *alloc_etherdev(int sizeof_priv)
+{
+ net_device *dev = (net_device *)kzalloc(sizeof(net_device), 0);
+
+ dev->mtu = 1500;
+ dev->hard_header_len = 0;
+ dev->priv = kzalloc(sizeof_priv, 0);
+ dev->dev_addr = dev->_dev_addr;
+ memset(dev->_dev_addr, 0, sizeof(dev->_dev_addr));
+
+ return dev;
+}
+
+
+int is_valid_ether_addr(const u8 *addr)
+{
+ /* is multicast */
+ if (!(addr[0] & 0x1))
+ return 0;
+
+ /* zero */
+ if (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]))
+ return 0;
+
+ return 1;
+}
+
+
+/*****************
+ ** linux/mii.h **
+ *****************/
+
+
+/**
+ * Restart NWay (autonegotiation) for this interface
+ */
+int mii_nway_restart (struct mii_if_info *mii)
+{
+ int bmcr;
+ int r = -EINVAL;
+ enum {
+ BMCR_ANENABLE = 0x1000, /* enable auto negotiation */
+ BMCR_ANRESTART = 0x200, /* auto negotation restart */
+ };
+
+ /* if autoneg is off, it's an error */
+ bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
+
+ if (bmcr & BMCR_ANENABLE) {
+ printk("Reanable\n");
+ bmcr |= BMCR_ANRESTART;
+ mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
+ r = 0;
+ }
+
+ return r;
+}
+
+
+int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
+{
+ ecmd->duplex = DUPLEX_FULL;
+ return 0;
+}
+
+
+u8 mii_resolve_flowctrl_fdx(u16 lcladv, u16 rmtadv)
+{
+ u8 cap = 0;
+
+ if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
+ cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
+ } else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
+ if (lcladv & ADVERTISE_PAUSE_CAP)
+ cap = FLOW_CTRL_RX;
+ else if (rmtadv & ADVERTISE_PAUSE_CAP)
+ cap = FLOW_CTRL_TX;
+ }
+
+ return cap;
+}
+
+
+/***********************
+ ** linux/netdevice.h **
+ ***********************/
+
+void *netdev_priv(const struct net_device *dev)
+{
+ return dev->priv;
+}
+
+
+/**********************
+ ** linux/inerrupt.h **
+ **********************/
+
+void tasklet_schedule(struct tasklet_struct *t)
+{
+ t->func(t->data);
+}
diff --git a/dde_linux/src/drivers/usb/lx_emul.h b/dde_linux/src/drivers/usb/lx_emul.h
index 897dbfc5e..7dc5a2071 100644
--- a/dde_linux/src/drivers/usb/lx_emul.h
+++ b/dde_linux/src/drivers/usb/lx_emul.h
@@ -39,10 +39,11 @@ extern "C" {
#if VERBOSE_LX_EMUL
#define DEBUG_COMPLETION 0
#define DEBUG_DMA 0
-#define DEBUG_DRIVER 0
+#define DEBUG_DRIVER 1
#define DEBUG_IRQ 0
#define DEBUG_KREF 0
#define DEBUG_PCI 0
+#define DEBUG_SKB 0
#define DEBUG_SLAB 0
#define DEBUG_TIMER 0
#define DEBUG_THREAD 0
@@ -52,8 +53,9 @@ extern "C" {
#define DEBUG_DMA 0
#define DEBUG_IRQ 0
#define DEBUG_KREF 0
-#define DEBUG_SLAB 0
#define DEBUG_PCI 0
+#define DEBUG_SKB 0
+#define DEBUG_SLAB 0
#define DEBUG_TIMER 0
#define DEBUG_THREAD 0
#endif
@@ -135,7 +137,8 @@ typedef dde_kit_size_t size_t;
typedef dde_kit_int64_t int64_t;
typedef dde_kit_uint64_t uint64_t;
-typedef uint32_t uint;
+typedef uint32_t uint;
+typedef unsigned long ulong;
typedef int8_t s8;
typedef uint8_t u8;
@@ -162,6 +165,9 @@ typedef __u16 __be16;
typedef __u32 __be32;
typedef __u64 __be64;
+typedef __u16 __sum16;
+typedef __u32 __wsum;
+
typedef u64 sector_t;
struct list_head {
@@ -229,6 +235,7 @@ typedef unsigned short mode_t;
#define rmb() mb()
#define wmb() asm volatile ("": : :"memory")
#define smp_wmb() wmb()
+#define smp_mb() mb()
static inline void barrier() { mb(); }
@@ -417,6 +424,8 @@ enum {
ETIME = 46,
EALREADY = 47,
EOPNOTSUPP = 48,
+ EDOM = 49,
+ ENOLINK = 50,
};
static inline bool IS_ERR(void *ptr) {
@@ -505,6 +514,8 @@ static inline size_t min(size_t a, size_t b) {
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#define BUILD_BUG_ON(condition)
+
void might_sleep();
#define INT_MAX ((int)(~0U>>1))
@@ -627,6 +638,7 @@ void *memscan(void *addr, int c, size_t size);
char *strcat(char *dest, const char *src);
int strcmp(const char *s1, const char *s2);
int strncmp(const char *cs, const char *ct, size_t count);
+char *strcpy(char *to, const char *from);
char *strncpy(char *, const char *, size_t);
char *strchr(const char *, int);
char *strrchr(const char *,int);
@@ -931,6 +943,8 @@ typedef struct wait_queue { int dummy; } wait_queue_t;
#define DECLARE_WAIT_QUEUE_HEAD(name) \
wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
+#define DECLARE_WAIT_QUEUE_HEAD_ONSTACK(name) DECLARE_WAIT_QUEUE_HEAD(name)
+
void __wake_up();
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
@@ -951,8 +965,8 @@ void __wait_event(void);
#define _wait_event(condition) \
while(!(condition)) { \
__wait_event(); \
- if (!(condition)) \
- msleep(1); \
+ if (!(condition)) \
+ msleep(1); \
} \
#define _wait_event_timeout(condition, timeout) \
@@ -1005,7 +1019,7 @@ void do_gettimeofday(struct timeval *tv);
** linux/sched.h **
*******************/
-enum { TASK_RUNNING = 0, TASK_INTERRUPTIBLE = 1, TASK_NORMAL = 3 };
+enum { TASK_RUNNING = 0, TASK_INTERRUPTIBLE = 1, TASK_UNINTERRUPTIBLE = 2, TASK_NORMAL = 3 };
enum { MAX_SCHEDULE_TIMEOUT = (~0U >> 1) };
@@ -1027,6 +1041,7 @@ void __set_current_state(int state);
#define set_current_state(state) __set_current_state(state)
int signal_pending(struct task_struct *p);
void schedule(void);
+signed long schedule_timeout(signed long);
signed long schedule_timeout_uninterruptible(signed long timeout);
void yield(void);
int wake_up_process(struct task_struct *tsk);
@@ -1200,6 +1215,7 @@ typedef struct pm_message { int event; } pm_message_t;
struct dev_pm_info { bool is_prepared; };
+#define PMSG_IS_AUTO(msg) 0
/************************
** linux/pm_runtime.h **
@@ -2595,6 +2611,396 @@ struct scsi_driver
};
+/************************
+ ** Networking support **
+ ************************/
+
+
+/********************
+ ** linux/skbuff.h **
+ ********************/
+
+enum {
+ NET_IP_ALIGN = 2,
+ CHECKSUM_COMPLETE = 2,
+ CHECKSUM_PARTIAL = 3,
+};
+
+struct skb_shared_info
+{
+ unsigned short nr_frags;
+};
+
+struct sk_buff
+{
+ struct sk_buff *next;
+ struct sk_buff *prev;
+
+ /*
+ * This is the control buffer. It is free to use for every
+ * layer. Please put your private variables there. If you
+ * want to keep them across layers you have to do a skb_clone()
+ * first. This is owned by whoever has the skb queued ATM.
+ */
+ char cb[48] __attribute__((aligned(8)));
+
+ unsigned int len;
+ union
+ {
+ __wsum csum;
+ struct
+ {
+ u16 csum_start;
+ u16 csum_offset;
+ };
+ };
+ u8 local_df:1,
+ cloned:1,
+ ip_summed:2,
+ nohdr:1,
+ nfctinfo:3;
+ __be16 protocol;
+ unsigned char *start;
+ unsigned char *end;
+ unsigned char *data;
+ unsigned char *tail;
+ unsigned int truesize;
+};
+
+struct sk_buff_head
+{
+ struct sk_buff *next;
+ struct sk_buff *prev;
+ u32 qlen;
+ spinlock_t lock;
+};
+
+
+#define skb_queue_walk_safe(queue, skb, tmp) \
+ for (skb = (queue)->next, tmp = skb->next; \
+ skb != (struct sk_buff *)(queue); \
+ skb = tmp, tmp = skb->next)
+
+struct skb_shared_info *skb_shinfo(struct sk_buff *);
+
+struct sk_buff *alloc_skb(unsigned int, gfp_t);
+unsigned char *skb_push(struct sk_buff *, unsigned int);
+unsigned char *skb_pull(struct sk_buff *, unsigned int);
+unsigned char *skb_put(struct sk_buff *, unsigned int);
+unsigned char *__skb_put(struct sk_buff *, unsigned int);
+void skb_trim(struct sk_buff *, unsigned int);
+unsigned int skb_headroom(const struct sk_buff *);
+int skb_checksum_start_offset(const struct sk_buff *);
+struct sk_buff *skb_copy_expand(const struct sk_buff *, int, int, gfp_t);
+unsigned char *skb_tail_pointer(const struct sk_buff *);
+int skb_tailroom(const struct sk_buff *);
+void skb_set_tail_pointer(struct sk_buff *, const int);
+struct sk_buff *skb_clone(struct sk_buff *, gfp_t);
+void skb_reserve(struct sk_buff *, int);
+
+struct sk_buff *skb_dequeue(struct sk_buff_head *);
+void skb_queue_head_init(struct sk_buff_head *);
+void skb_queue_tail(struct sk_buff_head *, struct sk_buff *);
+void __skb_queue_tail(struct sk_buff_head *, struct sk_buff *);
+int skb_queue_empty(const struct sk_buff_head *);
+void skb_queue_purge(struct sk_buff_head *);
+void __skb_unlink(struct sk_buff *, struct sk_buff_head *);
+
+void skb_tx_timestamp(struct sk_buff *);
+bool skb_defer_rx_timestamp(struct sk_buff *);
+
+void dev_kfree_skb(struct sk_buff *);
+void dev_kfree_skb_any(struct sk_buff *);
+
+
+/****************
+ ** linux/if.h **
+ ****************/
+
+enum {
+ IFF_PROMISC = 0x100, /* receive all packets */
+ IFF_ALLMULTI = 0x200, /* receive all multicast packets */
+ IFF_MULTICAST = 0x1000, /* supports multicast */
+ IFNAMSIZ = 16,
+};
+
+struct ifreq { };
+
+
+/**********************
+ ** linux/if_ether.h **
+ **********************/
+
+enum {
+ ETH_ALEN = 6, /* octets in one ethernet addr */
+ ETH_P_8021Q = 0x8100, /* 802.1Q VLAN Extended Header */
+
+ ETH_FRAME_LEN = 1514,
+};
+
+
+/*********************
+ ** linux/ethtool.h **
+ *********************/
+
+enum {
+ DUPLEX_FULL = 0x1,
+ ETHTOOL_GSET = 0x1,
+ ETHTOOL_FWVERS_LEN = 32,
+ ETHTOOL_BUSINFO_LEN = 32,
+};
+
+
+struct ethtool_cmd
+{
+ u32 cmd;
+ u8 duplex;
+};
+
+struct ethtool_eeprom
+{
+ u32 magic;
+ u32 offset;
+ u32 len;
+};
+
+struct ethtool_drvinfo
+{
+ char driver[32]; /* driver short name, "tulip", "eepro100" */
+ char version[32]; /* driver version string */
+ char fw_version[ETHTOOL_FWVERS_LEN]; /* firmware version string */
+ char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */
+ /* For PCI devices, use pci_name(pci_dev). */
+};
+
+struct ethhdr { };
+
+struct net_device;
+
+struct ethtool_ops
+{
+ int (*get_settings)(struct net_device *, struct ethtool_cmd *);
+ int (*set_settings)(struct net_device *, struct ethtool_cmd *);
+ void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
+ int (*nway_reset)(struct net_device *);
+ u32 (*get_link)(struct net_device *);
+ int (*get_eeprom_len)(struct net_device *);
+ int (*get_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *);
+ int (*set_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *);
+ u32 (*get_msglevel)(struct net_device *);
+ void (*set_msglevel)(struct net_device *, u32);
+};
+
+__u32 ethtool_cmd_speed(const struct ethtool_cmd *ep);
+u32 ethtool_op_get_link(struct net_device *);
+
+
+/***********************
+ ** linux/netdevice.h **
+ ***********************/
+
+#define netif_err(priv, type, dev, fmt, args...) dde_kit_printf("netif_err: " fmt, ## args);
+#define netif_info(priv, type, dev, fmt, args...) dde_kit_printf("netif_info: " fmt, ## args);
+
+#define netdev_err(dev, fmt, args...) dde_kit_printf("nedev_err: " fmt, ##args)
+#define netdev_warn(dev, fmt, args...) dde_kit_printf("nedev_warn: " fmt, ##args)
+#define netdev_info(dev, fmt, args...) dde_kit_printf("nedev_info: " fmt, ##args)
+
+#define netdev_for_each_mc_addr(a, b) if (0)
+
+#if VERBOSE_LX_EMUL
+#define netif_dbg(priv, type, dev, fmt, args...) dde_kit_printf("netif_dbg: " fmt, ## args)
+#define netdev_dbg(dev, fmt, args...) dde_kit_printf("nedev_dbg: " fmt, ##args)
+#else
+#define netif_dbg(priv, type, dev, fmt, args...)
+#define netdev_dbg(dev, fmt, args...)
+#endif
+
+#define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev))
+#define SET_NETDEV_DEVTYPE(net, devtype) ((net)->dev.type = (devtype))
+
+enum netdev_tx { NETDEV_TX_OK = 0 };
+typedef enum netdev_tx netdev_tx_t;
+
+enum {
+ MAX_ADDR_LEN = 32,
+
+ NETIF_F_HW_CSUM = 8,
+ NETIF_F_RXCSUM = (1 << 29),
+
+ NET_RX_SUCCESS = 0,
+
+ NETIF_MSG_DRV = 0x1,
+ NETIF_MSG_PROBE = 0x2,
+ NETIF_MSG_LINK = 0x4,
+};
+
+struct net_device_ops
+{
+ int (*ndo_open)(struct net_device *dev);
+ int (*ndo_stop)(struct net_device *dev);
+ netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev);
+ void (*ndo_set_rx_mode)(struct net_device *dev);
+ int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
+ int (*ndo_validate_addr)(struct net_device *dev);
+ int (*ndo_do_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
+ void (*ndo_tx_timeout) (struct net_device *dev);
+ int (*ndo_change_mtu)(struct net_device *dev, int new_mtu);
+ int (*ndo_set_features)(struct net_device *dev, u32 features);
+};
+
+struct net_device_stats
+{
+ unsigned long rx_packets;
+ unsigned long tx_packets;
+ unsigned long rx_bytes;
+ unsigned long tx_bytes;
+ unsigned long rx_errors;
+ unsigned long tx_errors;
+ unsigned long rx_dropped;
+ unsigned long tx_dropped;
+ unsigned long rx_length_errors;
+ unsigned long rx_over_errors;
+ unsigned long rx_crc_errors;
+ unsigned long rx_frame_errors;
+};
+
+/* NET_DEVICE */
+struct net_device
+{
+ char name[IFNAMSIZ];
+ u32 features;
+ u32 hw_features;
+
+ struct net_device_stats stats;
+ const struct net_device_ops *netdev_ops;
+ const struct ethtool_ops *ethtool_ops;
+
+ unsigned long state;
+
+ unsigned int flags;
+ unsigned short hard_header_len; /* hardware hdr length */
+ unsigned int mtu;
+ unsigned char *dev_addr;
+ unsigned char _dev_addr[ETH_ALEN];
+ unsigned long trans_start; /* Time (in jiffies) of last Tx */
+ int watchdog_timeo; /* used by dev_watchdog() */
+ struct device dev;
+ void *priv;
+};
+
+
+struct netdev_hw_addr
+{
+ unsigned char addr[MAX_ADDR_LEN];
+};
+
+#define netif_msg_tx_err(p) ({ printk("netif_msg_tx_err called not implemented\n"); 0; })
+#define netif_msg_rx_err(p) ({ printk("netif_msg_rx_err called not implemented\n"); 0; })
+#define netif_msg_tx_queued(p) ({ printk("netif_msg_tx_queued called not implemented\n"); 0; })
+
+u32 netif_msg_init(int, int);
+
+void *netdev_priv(const struct net_device *);
+int netif_running(const struct net_device *);
+int netif_device_present(struct net_device *);
+void netif_device_detach(struct net_device *);
+void netif_start_queue(struct net_device *);
+void netif_stop_queue(struct net_device *);
+void netif_wake_queue(struct net_device *);
+void netif_device_attach(struct net_device *);
+void unregister_netdev(struct net_device *);
+void free_netdev(struct net_device *);
+int netif_rx(struct sk_buff *);
+void netif_carrier_off(struct net_device *);
+
+int netdev_mc_empty(struct net_device *);
+int register_netdev(struct net_device *);
+
+/*****************
+ ** linux/mii.h **
+ *****************/
+
+enum {
+ FLOW_CTRL_TX = 0x1,
+ FLOW_CTRL_RX = 0x2,
+
+ MII_BMCR = 0x0,
+ MII_ADVERTISE = 0x4,
+ MII_LPA = 0x5,
+
+ BMCR_RESET = 0x8000, /* reset to default state */
+
+ ADVERTISE_PAUSE_CAP = 0x0400, /* try for pause */
+ ADVERTISE_CSMA = 0x0001, /* only selector supported */
+ ADVERTISE_PAUSE_ASYM = 0x0800, /* try for asymetric pause */
+ ADVERTISE_10HALF = 0x0020,
+ ADVERTISE_10FULL = 0x0040,
+ ADVERTISE_100HALF = 0x0080,
+ ADVERTISE_100FULL = 0x0100,
+ ADVERTISE_ALL = ADVERTISE_10HALF | ADVERTISE_10FULL |
+ ADVERTISE_100HALF | ADVERTISE_100FULL
+};
+
+struct mii_if_info
+{
+ int phy_id;
+ int phy_id_mask;
+ int reg_num_mask;
+ struct net_device *dev;
+ int (*mdio_read) (struct net_device *dev, int phy_id, int location);
+ void (*mdio_write) (struct net_device *dev, int phy_id, int location, int val);
+};
+
+
+unsigned int mii_check_media (struct mii_if_info *, unsigned int,
+ unsigned int);
+int mii_ethtool_gset(struct mii_if_info *, struct ethtool_cmd *);
+int mii_ethtool_sset(struct mii_if_info *, struct ethtool_cmd *);
+u8 mii_resolve_flowctrl_fdx(u16, u16);
+int mii_nway_restart (struct mii_if_info *);
+int mii_link_ok (struct mii_if_info *);
+
+struct mii_ioctl_data { };
+int generic_mii_ioctl(struct mii_if_info *,
+ struct mii_ioctl_data *, int,
+ unsigned int *);
+struct mii_ioctl_data *if_mii(struct ifreq *);
+
+
+/**********************
+ ** linux/inerrupt.h **
+ **********************/
+
+struct tasklet_struct
+{
+ void (*func)(unsigned long);
+ unsigned long data;
+};
+
+void tasklet_schedule(struct tasklet_struct *);
+void tasklet_kill(struct tasklet_struct *);
+
+/*************************
+ ** linux/etherdevice.h **
+ *************************/
+
+int eth_mac_addr(struct net_device *, void *);
+int eth_validate_addr(struct net_device *);
+__be16 eth_type_trans(struct sk_buff *, struct net_device *);
+int is_valid_ether_addr(const u8 *);
+
+void random_ether_addr(u8 *addr);
+
+struct net_device *alloc_etherdev(int);
+
+/********************
+ ** asm/checksum.h **
+ ********************/
+
+__wsum csum_partial(const void *, int, __wsum);
+__sum16 csum_fold(__wsum);
+
/**********************************
** Platform specific defintions **
*********************************/
diff --git a/dde_linux/src/drivers/usb/main.cc b/dde_linux/src/drivers/usb/main.cc
index c30cb1eb0..10db6a80f 100644
--- a/dde_linux/src/drivers/usb/main.cc
+++ b/dde_linux/src/drivers/usb/main.cc
@@ -21,6 +21,7 @@
#include
#include
+#include
/* Local */
#include "storage/component.h"
#include "routine.h"
@@ -50,7 +51,7 @@ bool Routine::_all = false;
void breakpoint() { PDBG("BREAK"); }
-static void init(bool hid, bool stor)
+static void init(Services *services)
{
/* start jiffies */
dde_kit_timer_init(0, 0);
@@ -59,7 +60,7 @@ static void init(bool hid, bool stor)
subsys_usb_init();
/* input + HID */
- if (hid) {
+ if (services->hid) {
subsys_input_init();
module_evdev_init();
@@ -73,10 +74,10 @@ static void init(bool hid, bool stor)
* Host controller.
*
*/
- platform_hcd_init();
+ platform_hcd_init(services);
/* storage */
- if (stor)
+ if (services->stor)
module_usb_stor_init();
}
@@ -91,13 +92,12 @@ int main(int, char **)
static Rpc_entrypoint ep_hid(&cap, STACK_SIZE, "usb_hid_ep");
static Signal_receiver recv;
- bool hid = false;
- bool stor = false;
+ Services services;
try {
config()->xml_node().sub_node("hid");
start_input_service(&ep_hid);
- hid = true;
+ services.hid = true;
} catch (Config::Invalid) {
PDBG("No node found - not starting any USB services");
return 0;
@@ -107,20 +107,28 @@ int main(int, char **)
try {
config()->xml_node().sub_node("storage");
- stor = true;
+ services.stor = true;
} catch (Xml_node::Nonexistent_sub_node) {
PDBG("No config node found - not starting the USB Storage (Block) service");
}
+ try {
+ config()->xml_node().sub_node("nic");
+ services.nic = true;
+ } catch (Xml_node::Nonexistent_sub_node) {
+ PDBG("No config node found - not starting the USB Nic (Network) service");
+ }
+
Timer::init(&recv);
Irq::init(&recv);
Event::init(&recv);
Service_handler::s()->receiver(&recv);
Storage::init(&recv);
+ Nic::init(&recv);
Routine::add(0, 0, "Main", true);
Routine::current_use_first();
- init(hid, stor);
+ init(&services);
Routine::remove();
diff --git a/dde_linux/src/drivers/usb/nic/component.h b/dde_linux/src/drivers/usb/nic/component.h
new file mode 100644
index 000000000..1ac5bb9a0
--- /dev/null
+++ b/dde_linux/src/drivers/usb/nic/component.h
@@ -0,0 +1,136 @@
+/*
+ * \brief Block-session implementation for network devices
+ * \author Sebastian Sumpf
+ * \date 2012-07-05
+ */
+
+/*
+ * 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 _NIC__COMPONENT_H_
+#define _NIC__COMPONENT_H_
+
+#include
+#include
+
+#include
+
+namespace Nic {
+
+ using namespace Genode;
+
+
+ class Session_component;
+
+ struct Device : ::Device
+ {
+ Session_component *_session;
+
+ /**
+ * Transmit data to driver
+ */
+ virtual void tx(addr_t virt, size_t size) = 0;
+
+ /**
+ * Return mac address of device
+ */
+ virtual Mac_address mac_address() = 0;
+
+ /**
+ * Set session belonging to this driver
+ */
+ void session(Session_component *s) { _session = s; }
+ };
+
+ class Session_component : public Genode::Allocator_avl,
+ public Packet_session_component
+ {
+ private:
+
+ Device *_device; /* device this session is using */
+ Tx::Sink *_tx_sink; /* client packet sink */
+
+ protected:
+
+ void _process_packets()
+ {
+ /* submit received packets to lower layer */
+ while (_tx_sink->packet_avail())
+ {
+ Packet_descriptor packet = _tx_sink->get_packet();
+ addr_t virt = (addr_t)_tx_sink->packet_content(packet);
+ /* send to driver */
+ _device->tx(virt, packet.size());
+
+ if (!_tx_sink->ready_to_ack())
+ PWRN("Wait for TX packet ack");
+
+ /* acknowledge to client */
+ _tx_sink->acknowledge_packet(packet);
+ }
+
+ /* release acknowledged packets */
+ while (_rx.source()->ack_avail())
+ {
+
+ Packet_descriptor packet = _rx.source()->get_acked_packet();
+
+ /* free packet buffer */
+ _rx.source()->release_packet(packet);
+ }
+ }
+
+ public:
+
+ /**
+ * Constructor
+ */
+ Session_component(Dataspace_capability tx_ds,
+ Dataspace_capability rx_ds,
+ Rpc_entrypoint &ep,
+ Signal_receiver *sig_rec,
+ ::Device *device)
+ :
+ Genode::Allocator_avl(Genode::env()->heap()),
+ Packet_session_component(tx_ds, rx_ds, this, ep, sig_rec),
+ _device(static_cast(device)),
+ _tx_sink(Session_rpc_object::_tx.sink()) { _device->session(this); }
+
+ Mac_address mac_address() { return _device->mac_address(); }
+
+ /**
+ * Send packet to client (called form driver)
+ */
+ void rx(addr_t virt, size_t size)
+ {
+ Packet_descriptor p =_rx.source()->alloc_packet(size);
+ memcpy(_rx.source()->packet_content(p), (void*)virt, size);
+ _rx.source()->submit_packet(p);
+ }
+ };
+
+ /*
+ * Shortcut for single-client root component
+ */
+ typedef Root_component Root_component;
+
+ /**
+ * Root component, handling new session requests
+ */
+ class Root : public Packet_root
+ {
+ public:
+
+ Root(Rpc_entrypoint *session_ep, Allocator *md_alloc,
+ Signal_receiver *sig_rec, Device *device)
+ :
+ Packet_root(session_ep, md_alloc, sig_rec, device) { }
+ };
+}
+
+
+#endif /* _NIC__COMPONENT_H_ */
diff --git a/dde_linux/src/drivers/usb/nic/nic.cc b/dde_linux/src/drivers/usb/nic/nic.cc
new file mode 100644
index 000000000..9597fcdf3
--- /dev/null
+++ b/dde_linux/src/drivers/usb/nic/nic.cc
@@ -0,0 +1,441 @@
+/*
+ * \brief Glue code for Linux network drivers
+ * \author Sebastian Sumpf
+ * \date 2012-07-05
+ */
+
+/*
+ * 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
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include "signal.h"
+
+static Signal_helper *_signal = 0;
+
+enum {
+ START = 0x1, /* device flag */
+ HEAD_ROOM = 32, /* head room in skb in bytes */
+ MAC_LEN = 17, /* 12 number and 6 colons */
+};
+
+class Nic_device : public Nic::Device
+{
+ private:
+
+ struct net_device *_ndev; /* Linux-net device */
+
+ public:
+
+ Nic_device(struct net_device *ndev) : _ndev(ndev) { }
+
+ /**
+ * Add device
+ */
+ static Nic_device *add(struct net_device *ndev) {
+ return new (Genode::env()->heap()) Nic_device(ndev); }
+
+
+ /**********************
+ ** Device interface **
+ **********************/
+
+ /**
+ * Submit packet to driver
+ */
+ void tx(Genode::addr_t virt, Genode::size_t size)
+ {
+ sk_buff *skb = alloc_skb(size + HEAD_ROOM, 0);
+ skb->len = size;
+ skb->data += HEAD_ROOM;
+
+ Genode::memcpy(skb->data, (void *)virt, skb->len);
+
+ _ndev->netdev_ops->ndo_start_xmit(skb, _ndev);
+ }
+
+ /**
+ * Submit packet for session
+ */
+ void rx(sk_buff *skb) { _session->rx((Genode::addr_t)skb->data, skb->len); }
+
+
+ /**
+ * Return mac address
+ */
+ Nic::Mac_address mac_address()
+ {
+ Nic::Mac_address m;
+ Genode::memcpy(&m, _ndev->_dev_addr, ETH_ALEN);
+ return m;
+ }
+};
+
+/* XXX support multiple devices */
+static Nic_device *_nic = 0;
+
+void Nic::init(Genode::Signal_receiver *recv) {
+ _signal = new (Genode::env()->heap()) Signal_helper(recv); }
+
+
+/***********************
+ ** linux/netdevice.h **
+ ***********************/
+
+int register_netdev(struct net_device *ndev)
+{
+ using namespace Genode;
+ static bool announce = false;
+
+ Nic_device *nic = Nic_device::add(ndev);
+
+ /* XXX: move to 'main' */
+ if (!announce) {
+ static Cap_connection cap_nic;
+ static Rpc_entrypoint ep_nic(&cap_nic, 4096, "usb_nic_ep");
+ static Nic::Root root(&ep_nic, env()->heap(), _signal->receiver(), nic);
+
+ announce = true;
+
+ ndev->state |= START;
+ int err = ndev->netdev_ops->ndo_open(ndev);
+ _nic = nic;
+ env()->parent()->announce(ep_nic.manage(&root));
+
+ return err;
+ }
+
+ return -ENODEV;
+}
+
+
+int netif_running(const struct net_device *dev) { return dev->state & START; }
+int netif_device_present(struct net_device *dev) { return 1; }
+
+
+int netif_rx(struct sk_buff *skb)
+{
+ if (_nic)
+ _nic->rx(skb);
+ dev_kfree_skb(skb);
+ return NET_RX_SUCCESS;
+}
+
+
+/********************
+ ** linux/skbuff.h **
+ ********************/
+
+struct sk_buff *alloc_skb(unsigned int size, gfp_t priority)
+{
+ sk_buff *skb = new (Genode::env()->heap()) sk_buff;
+ Genode::memset(skb, 0, sizeof(sk_buff));
+
+ size = (size + 3) & ~(0x3);
+
+ skb->start = skb->data = size ? (unsigned char*)kzalloc(size, 0) : 0;
+ skb->tail = skb->end = skb->start + size;
+ skb->truesize = size;
+
+ dde_kit_log(DEBUG_SKB, "alloc sbk: %p start: %p size: %u", skb, skb->start, size);
+ return skb;
+}
+
+
+void dev_kfree_skb(struct sk_buff *skb)
+{
+ dde_kit_log(DEBUG_SKB, "free skb: %p start: %p cloned: %d",
+ skb, skb->start, skb->cloned);
+
+ if (!skb->cloned)
+ kfree(skb->start);
+
+ destroy(Genode::env()->heap(), skb);
+}
+
+
+/**
+ * Reserve 'len'
+ */
+void skb_reserve(struct sk_buff *skb, int len)
+{
+ if ((skb->data + len) > skb->end) {
+ PERR("Error resevring SKB data: skb: %p data: %p end: %p len: %d",
+ skb, skb->data, skb->end, skb->len);
+ return;
+ }
+ skb->data += len;
+ dde_kit_log(DEBUG_SKB, "skb: %p slen: %u len: %d", skb, skb->len, len);
+}
+
+
+/**
+ * Prepend 'len'
+ */
+unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
+{
+ if((skb->data - len) < skb->start) {
+ PERR("Error SKB head room too small: %p data: %p start: %p len: %u",
+ skb, skb->data, skb->start, len);
+ return 0;
+ }
+
+ skb->len += len;
+ skb->data -= len;
+
+ dde_kit_log(DEBUG_SKB, "skb: %p slen: %u len: %u", skb, skb->len, len);
+ return skb->data;
+}
+
+
+/**
+ * Append 'len'
+ */
+unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
+{
+ if ((skb->data + len > skb->end)) {
+ PERR("Error increasing SKB length: skb: %p data: %p end: %p len: %u",
+ skb, skb->data, skb->end, len);
+ return 0;
+ }
+
+ unsigned char *old = skb_tail_pointer(skb);
+ skb->len += len;
+ skb->tail += len;
+ dde_kit_log(DEBUG_SKB, "skb: %p slen: %u len: %u", skb, skb->len, len);
+ return old;
+}
+
+
+/**
+ * Return current head room
+ */
+unsigned int skb_headroom(const struct sk_buff *skb)
+{
+ return skb->data - skb->start;
+}
+
+
+/**
+ * Take 'len' from front
+ */
+unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)
+{
+ if (len > skb->len) {
+ PERR("Error try to pull too much: skb: %p len: %u pull len: %u",
+ skb, skb->len, len);
+ return 0;
+ }
+ skb->len -= len;
+ dde_kit_log(DEBUG_SKB, "skb: %p slen: %u len: %u", skb, skb->len, len);
+ return skb->data += len;
+}
+
+
+/**
+ * Set 'len' and 'tail'
+ */
+void skb_trim(struct sk_buff *skb, unsigned int len)
+{
+ if (skb->len <= len) {
+ PERR("Error trimming skb: %p data: %p start: %p len %u ret: %p",
+ skb, skb->data, skb->start, len, __builtin_return_address((0)));
+ return;
+ }
+
+ skb->len = len;
+ skb_set_tail_pointer(skb, len);
+
+ dde_kit_log(DEBUG_SKB, "skb: %p slen: %u len: %u", skb, skb->len, len);
+}
+
+
+/**
+ * Clone skb
+ */
+struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
+{
+ sk_buff *c = alloc_skb(0, 0);
+ Genode::memcpy(c, skb, sizeof(sk_buff));
+ c->cloned = 1;
+ return c;
+}
+
+
+void skb_set_tail_pointer(struct sk_buff *skb, const int offset)
+{
+ skb->tail = skb->data + offset;
+}
+
+
+unsigned char *skb_tail_pointer(const struct sk_buff *skb)
+{
+ return skb->tail;
+}
+
+
+/**
+ * Dummy for shared info
+ */
+struct skb_shared_info *skb_shinfo(struct sk_buff * /* skb */)
+{
+ static skb_shared_info _s = { 0 };
+ return &_s;
+}
+
+
+/**
+ * Init list head
+ */
+void skb_queue_head_init(struct sk_buff_head *list)
+{
+ list->prev = list->next = (sk_buff *)list;
+ list->qlen = 0;
+}
+
+
+/**
+ * Add to tail of queue
+ */
+void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+ newsk->next = (sk_buff *)list;
+ newsk->prev = list->prev;
+ list->prev->next = newsk;
+ list->prev = newsk;
+ list->qlen++;
+}
+
+
+void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) {
+ __skb_queue_tail(list, newsk); }
+
+
+/**
+ * Remove skb from queue
+ */
+void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
+{
+ sk_buff *l = (sk_buff *)list;
+ while (l->next != l) {
+ l = l->next;
+
+ if (l == skb) {
+ l->prev->next = l->next;
+ l->next->prev = l->prev;
+ list->qlen--;
+ return;
+ }
+ }
+
+ PERR("SKB not found in __skb_unlink");
+}
+
+
+/**
+ * Remove from head of queue
+ */
+struct sk_buff *skb_dequeue(struct sk_buff_head *list)
+{
+ if (list->next == (sk_buff *)list)
+ return 0;
+
+ sk_buff *skb = list->next;
+ list->next = skb->next;
+ list->next->prev = (sk_buff *)list;
+ list->qlen--;
+
+ return skb;
+}
+
+
+/**********************
+ ** linux/inerrupt.h **
+ **********************/
+
+namespace Genode {
+
+ /**
+ * Convert ASCII string to mac address
+ */
+ template <>
+ inline size_t ascii_to(char const *s, Nic::Mac_address* mac, unsigned)
+ {
+ enum {
+ HEX = true,
+ };
+
+ if(strlen(s) < MAC_LEN)
+ throw -1;
+
+ char mac_str[6];
+ for (int i = 0; i < ETH_ALEN; i++) {
+ int hi = i * 3;
+ int lo = hi + 1;
+
+ if (!is_digit(s[hi], HEX) || !is_digit(s[lo], HEX))
+ throw -1;
+
+ mac_str[i] = (digit(s[hi], HEX) << 4) | digit(s[lo], HEX);
+ }
+
+ memcpy(mac->addr, mac_str, ETH_ALEN);
+
+ return MAC_LEN;
+ }
+}
+
+
+static void snprint_mac(char *buf, char *mac)
+{
+ for (int i = 0; i < ETH_ALEN; i++)
+ {
+ Genode::snprintf(&buf[i * 3], 3, "%02x", mac[i]);
+ if ((i * 3) < MAC_LEN)
+ buf[(i * 3) + 2] = ':';
+ }
+
+ buf[MAC_LEN] = 0;
+}
+
+
+void random_ether_addr(u8 *addr)
+{
+ using namespace Genode;
+ char str[MAC_LEN + 1];
+ char fallback[] = { 0x2e, 0x60, 0x90, 0x0c, 0x4e, 0x01 };
+ Nic::Mac_address mac;
+
+ /* try using configured mac */
+ try {
+ Xml_node nic_config = config()->xml_node().sub_node("nic");
+ Xml_node::Attribute mac_node = nic_config.attribute("mac");
+ mac_node.value(&mac);
+ } catch (...) {
+ /* use fallback mac */
+ snprint_mac(str, fallback);
+ PWRN("No mac address or wrong format attribute in - using fallback (%s)",
+ str);
+
+ Genode::memcpy(addr, fallback, ETH_ALEN);
+ return;
+ }
+
+ /* use configured mac*/
+ Genode::memcpy(addr, mac.addr, ETH_ALEN);
+ snprint_mac(str, mac.addr);
+ PINF("Using configured mac: %s", str);
+}
diff --git a/dde_linux/src/drivers/usb/platform.h b/dde_linux/src/drivers/usb/platform.h
new file mode 100644
index 000000000..ff5e3b441
--- /dev/null
+++ b/dde_linux/src/drivers/usb/platform.h
@@ -0,0 +1,30 @@
+/**
+ * \brief Platform specific definitions
+ * \author Sebastian Sumpf
+ * \date 2012-07-06
+ *
+ * These functions have to be implemented on all supported platforms.
+ */
+
+/*
+ * 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 _PLATFORM_H_
+#define _PLATFORM_H_
+
+struct Services
+{
+ bool hid;
+ bool stor;
+ bool nic;
+
+ Services() : hid(false), stor(false), nic(false) { }
+};
+
+void platform_hcd_init(Services *services);
+
+#endif /* _PLATFORM_H_ */
diff --git a/dde_linux/src/drivers/usb/routine.h b/dde_linux/src/drivers/usb/routine.h
index 10c8c7fac..c0260974a 100644
--- a/dde_linux/src/drivers/usb/routine.h
+++ b/dde_linux/src/drivers/usb/routine.h
@@ -136,8 +136,10 @@ class Routine : public Genode::List::Element
Routine *next = _next(all);
- if (next == _current)
+ if (next == _current) {
+ _check_dead();
return;
+ }
/* return when restored */
if (_current && _setjmp(_current->_env)) {
diff --git a/dde_linux/src/drivers/usb/signal.h b/dde_linux/src/drivers/usb/signal.h
index 1836f401c..34d46efda 100644
--- a/dde_linux/src/drivers/usb/signal.h
+++ b/dde_linux/src/drivers/usb/signal.h
@@ -120,4 +120,9 @@ namespace Storage
void init(Genode::Signal_receiver *recv);
}
+namespace Nic
+{
+ void init(Genode::Signal_receiver *recv);
+}
+
#endif /* _SIGNAL_H_ */
diff --git a/dde_linux/src/drivers/usb/signal/dispatch.h b/dde_linux/src/drivers/usb/signal/dispatch.h
new file mode 100644
index 000000000..3aa656594
--- /dev/null
+++ b/dde_linux/src/drivers/usb/signal/dispatch.h
@@ -0,0 +1,174 @@
+/**
+ * \brief Packet-stream-session components
+ * \author Sebastian Sumpf
+ * \author Norman Feske
+ * \date 2012-07-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 _SIGNAL__DISPATCHER_H_
+#define _SIGNAL__DISPATCHER_H_
+
+#include "signal.h"
+
+template
+class Signal_dispatcher : public Driver_context,
+ public Genode::Signal_context_capability
+{
+ private:
+
+ T &obj;
+ void (T::*member) ();
+ Genode::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(Genode::Signal_receiver *sig_rec,
+ T &obj, void (T::*member)())
+ :
+ Genode::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)(); }
+ char const *debug() { return "Signal_dispatcher"; }
+};
+
+
+/**
+ * Session components that overrides signal handlers
+ */
+template
+class Packet_session_component : public RPC
+{
+ private:
+
+ Signal_dispatcher _process_packet_dispatcher;
+
+ protected:
+
+ virtual void _process_packets() = 0;
+
+ public:
+
+ Packet_session_component(Genode::Dataspace_capability tx_ds,
+ Genode::Rpc_entrypoint &ep,
+ Genode::Signal_receiver *sig_rec)
+ :
+ RPC(tx_ds, ep),
+ _process_packet_dispatcher(sig_rec, *this,
+ &Packet_session_component::_process_packets)
+ {
+ /*
+ * Register '_process_packets' dispatch function as signal
+ * handler for packet-avail and ready-to-ack signals.
+ */
+ RPC::_tx.sigh_packet_avail(_process_packet_dispatcher);
+ RPC::_tx.sigh_ready_to_ack(_process_packet_dispatcher);
+ }
+
+ Packet_session_component(Genode::Dataspace_capability tx_ds,
+ Genode::Dataspace_capability rx_ds,
+ Genode::Range_allocator *rx_buffer_alloc,
+ Genode::Rpc_entrypoint &ep,
+ Genode::Signal_receiver *sig_rec)
+ :
+ RPC(tx_ds, rx_ds, rx_buffer_alloc, ep),
+ _process_packet_dispatcher(sig_rec, *this,
+ &Packet_session_component::_process_packets)
+ {
+ /*
+ * Register '_process_packets' dispatch function as signal
+ * handler for packet-avail and ready-to-ack signals.
+ */
+ RPC::_tx.sigh_packet_avail(_process_packet_dispatcher);
+ RPC::_tx.sigh_ready_to_ack(_process_packet_dispatcher);
+ }
+};
+
+
+ /**
+ * Abstract device
+ */
+ struct Device { };
+
+
+ /**
+ * Root component, handling new session requests
+ */
+ template
+ class Packet_root : public ROOT_COMPONENT
+ {
+ private:
+
+ Genode::Rpc_entrypoint &_ep;
+ Genode::Signal_receiver *_sig_rec;
+ Device *_device;
+
+ protected:
+
+ /**
+ * Always returns the singleton block-session component
+ */
+ SESSION_COMPONENT *_create_session(const char *args)
+ {
+ using namespace Genode;
+ 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);
+ size_t rx_buf_size =
+ Arg_string::find_arg(args, "rx_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 (ROOT_COMPONENT::md_alloc())
+ SESSION_COMPONENT(env()->ram_session()->alloc(tx_buf_size),
+ env()->ram_session()->alloc(rx_buf_size),
+ _ep, _sig_rec, _device);
+ }
+
+ public:
+
+ Packet_root(Genode::Rpc_entrypoint *session_ep, Genode::Allocator *md_alloc,
+ Genode::Signal_receiver *sig_rec, Device *device)
+ :
+ ROOT_COMPONENT(session_ep, md_alloc),
+ _ep(*session_ep), _sig_rec(sig_rec), _device(device)
+ { }
+ };
+
+#endif /* _SIGNAL__DISPATCHER_H_ */
diff --git a/dde_linux/src/drivers/usb/signal/event.cc b/dde_linux/src/drivers/usb/signal/event.cc
index 885c43349..0ce8fbe7a 100644
--- a/dde_linux/src/drivers/usb/signal/event.cc
+++ b/dde_linux/src/drivers/usb/signal/event.cc
@@ -52,6 +52,53 @@ void Event::init(Genode::Signal_receiver *recv) {
_signal = new (Genode::env()->heap()) Signal_helper(recv); }
+/**
+ * Delayed work
+ */
+class Work : public Genode::List::Element
+{
+ private:
+
+ void *_work;
+ bool _delayed;
+
+ static Genode::List *_list()
+ {
+ static Genode::List _l;
+ return &_l;
+ }
+
+ public:
+
+ Work(void *work, bool delayed) : _work(work), _delayed(delayed) { }
+
+ static void schedule(void *work, bool delayed)
+ {
+ Work *w = new (Genode::env()->heap()) Work(work, delayed);
+ _list()->insert(w);
+ }
+
+ static void exec()
+ {
+ while (_list()->first()) {
+ Work *w = _list()->first();
+ _list()->remove(w);
+
+ if (w->_delayed) {
+ delayed_work *work = static_cast(w->_work);
+ work->work.func(&(work)->work);
+ }
+ else {
+ work_struct *work = static_cast(w->_work);
+ work->func(work);
+ }
+ destroy(Genode::env()->heap(), w);
+ }
+ }
+
+};
+
+
/************************
** linux/completion.h **
************************/
@@ -59,8 +106,14 @@ void Event::init(Genode::Signal_receiver *recv) {
void __wake_up() { Routine::schedule_all(); }
-void __wait_event() {
- Service_handler::s()->process(); }
+void __wait_event()
+{
+ /* schedule work first */
+ Work::exec();
+
+ /* schedule other routines or wait for signals */
+ Service_handler::s()->process();
+}
void init_completion(struct completion *work)
@@ -167,3 +220,21 @@ int wake_up_process(struct task_struct *tsk)
return 0;
}
+
+/***********************
+ ** linux/workquque.h **
+ ***********************/
+
+int schedule_delayed_work(struct delayed_work *work, unsigned long delay)
+{
+ Work::schedule((void *)work, true);
+ //work->work.func(&(work)->work);
+ return 0;
+}
+
+
+int schedule_work(struct work_struct *work)
+{
+ Work::schedule((void *)work, false);
+ return 1;
+}
diff --git a/dde_linux/src/drivers/usb/signal/irq.cc b/dde_linux/src/drivers/usb/signal/irq.cc
index 688dc208a..9c2a5e048 100644
--- a/dde_linux/src/drivers/usb/signal/irq.cc
+++ b/dde_linux/src/drivers/usb/signal/irq.cc
@@ -93,9 +93,13 @@ class Irq_context : public Driver_context,
/* report IRQ to all clients */
for (Irq_handler *h = _handler_list.first(); h; h = h->next()) {
irqreturn_t rc;
- if ((rc = h->handler(_irq, h->dev)) == IRQ_HANDLED)
+
+ rc = h->handler(_irq, h->dev);
+ dde_kit_log(DEBUG_IRQ, "IRQ: %u ret: %u %p", _irq, rc, h->handler);
+ if (rc == IRQ_HANDLED) {
Routine::schedule_all();
- dde_kit_log(DEBUG_IRQ, "IRQ: %u ret: %u", _irq, rc);
+ return;
+ }
}
}
diff --git a/dde_linux/src/drivers/usb/storage/component.h b/dde_linux/src/drivers/usb/storage/component.h
index d290b671e..6e02a49e2 100644
--- a/dde_linux/src/drivers/usb/storage/component.h
+++ b/dde_linux/src/drivers/usb/storage/component.h
@@ -17,7 +17,7 @@
#include
#include
-#include "signal.h"
+#include
namespace Block {
@@ -25,7 +25,7 @@ namespace Block {
class Session_component;
- struct Device
+ struct Device : ::Device
{
/**
* Request block size for driver and medium
@@ -42,49 +42,14 @@ namespace Block {
};
- template
- 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)(); }
- char const *debug() { return "Block_context"; }
- };
-
-
- class Session_component : public Session_rpc_object
+ class Session_component : public Packet_session_component
{
private:
addr_t _rq_phys ; /* physical addr. of rq_ds */
Device *_device; /* device this session is using */
- Signal_dispatcher _process_packet_dispatcher;
+ protected:
void _process_packets()
{
@@ -105,23 +70,17 @@ namespace Block {
/**
* Constructor
*/
- Session_component(Dataspace_capability rq_ds,
+ Session_component(Dataspace_capability tx_ds,
+ Ram_dataspace_capability rx_ds,
Rpc_entrypoint &ep,
Signal_receiver *sig_rec,
- Device *device)
+ ::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);
+ Packet_session_component(tx_ds, ep, sig_rec),
+ _rq_phys(Dataspace_client(tx_ds).phys_addr()),
+ _device(static_cast(device))
+ {
+ env()->ram_session()->free(rx_ds);
}
void info(size_t *blk_count, size_t *blk_size,
@@ -148,58 +107,15 @@ namespace Block {
/**
* Root component, handling new session requests
*/
- class Root : public Root_component
+ class Root : public Packet_root
{
- 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,
+ 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)
- { }
+ Packet_root(session_ep, md_alloc, sig_rec, device) { }
};
-}
+}
#endif /* _STORAGE__COMPONENT_H_ */
diff --git a/dde_linux/src/drivers/usb/storage/storage.cc b/dde_linux/src/drivers/usb/storage/storage.cc
index c47d5aadf..9c3de0d4a 100644
--- a/dde_linux/src/drivers/usb/storage/storage.cc
+++ b/dde_linux/src/drivers/usb/storage/storage.cc
@@ -19,9 +19,9 @@
#include
-#include "component.h"
+#include
+#include
#include "signal.h"
-#include "scsi.h"
static Signal_helper *_signal = 0;
diff --git a/dde_linux/src/drivers/usb/target.mk b/dde_linux/src/drivers/usb/target.mk
index 802e18529..2e78da403 100644
--- a/dde_linux/src/drivers/usb/target.mk
+++ b/dde_linux/src/drivers/usb/target.mk
@@ -1,7 +1,7 @@
TARGET = usb_drv
LIBS = cxx env dde_kit server libc-setjmp signal
SRC_CC = main.cc lx_emul.cc irq.cc timer.cc event.cc storage.cc \
- input_component.cc
+ input_component.cc nic.cc
SRC_C = dummies.c scsi.c evdev.c
CONTRIB_DIR := $(REP_DIR)/contrib
@@ -82,12 +82,11 @@ else ifeq ($(filter-out $(SPECS),platform_panda),)
CC_OPT += -DCONFIG_USB_EHCI_HCD_OMAP -DCONFIG_USB_EHCI_TT_NEWSCHED -DVERBOSE_DEBUG
INC_DIR += $(PRG_DIR)/arm
INC_DIR += $(CONTRIB_DIR)/arch/arm/plat-omap/include
-SRC_C += platform_device.c
+SRC_C += platform_device.c usbnet.c smsc95xx.c
SRC_CC += platform.cc
-#SRC_C += $(CONTRIB_DIR)/arch/arm/mach-omap2/usb-host.c
-#SRC_C += $(DRIVERS_DIR)/mfd/omap-usb-host.c
vpath %.c $(PRG_DIR)/arm/platform
vpath %.cc $(PRG_DIR)/arm/platform
+vpath %.c $(CONTRIB_DIR)/drivers/net/usb
#
# Unsupported
@@ -135,6 +134,7 @@ vpath %.c $(PRG_DIR)/input
vpath %.cc $(PRG_DIR)/input
vpath %.cc $(PRG_DIR)/storage
vpath %.c $(PRG_DIR)/storage
+vpath %.cc $(PRG_DIR)/nic
clean cleanall:
$(VERBOSE) rm -r include
diff --git a/dde_linux/src/drivers/usb/x86_32/platform/platform.h b/dde_linux/src/drivers/usb/x86_32/platform/platform.h
index ce4a21bd7..687664e99 100644
--- a/dde_linux/src/drivers/usb/x86_32/platform/platform.h
+++ b/dde_linux/src/drivers/usb/x86_32/platform/platform.h
@@ -14,6 +14,8 @@
#ifndef _X86_32__PLATFORM_H_
#define _X86_32__PLATFORM_H_
+#include
+
static inline
void platform_execute(void *sp, void *func, void *arg)
{
@@ -27,7 +29,7 @@ void platform_execute(void *sp, void *func, void *arg)
extern "C" void module_ehci_hcd_init();
extern "C" void module_uhci_hcd_init();
-static inline void platform_hcd_init(void)
+inline void platform_hcd_init(Services *s)
{
/* ehci_hcd should always be loaded before uhci_hcd and ohci_hcd, not after */