From 70b8fc2832c4f22cdb02ee638ec29b43179505cc Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Mon, 25 Jun 2012 16:31:04 +0200 Subject: [PATCH] USB: Support usbnet and smsc95xx for omap4 --- dde_linux/Makefile | 10 +- dde_linux/README | 30 +- dde_linux/patches/csum.patch | 17 + dde_linux/run/usb_net.run | 80 ++++ .../src/drivers/usb/arm/platform/platform.cc | 16 +- .../src/drivers/usb/arm/platform/platform.h | 5 +- dde_linux/src/drivers/usb/dummies.c | 91 +++- dde_linux/src/drivers/usb/lx_emul.cc | 145 +++++- dde_linux/src/drivers/usb/lx_emul.h | 418 ++++++++++++++++- dde_linux/src/drivers/usb/main.cc | 26 +- dde_linux/src/drivers/usb/nic/component.h | 136 ++++++ dde_linux/src/drivers/usb/nic/nic.cc | 441 ++++++++++++++++++ dde_linux/src/drivers/usb/platform.h | 30 ++ dde_linux/src/drivers/usb/routine.h | 4 +- dde_linux/src/drivers/usb/signal.h | 5 + dde_linux/src/drivers/usb/signal/dispatch.h | 174 +++++++ dde_linux/src/drivers/usb/signal/event.cc | 75 ++- dde_linux/src/drivers/usb/signal/irq.cc | 8 +- dde_linux/src/drivers/usb/storage/component.h | 116 +---- dde_linux/src/drivers/usb/storage/storage.cc | 4 +- dde_linux/src/drivers/usb/target.mk | 8 +- .../drivers/usb/x86_32/platform/platform.h | 4 +- 22 files changed, 1684 insertions(+), 159 deletions(-) create mode 100644 dde_linux/patches/csum.patch create mode 100644 dde_linux/run/usb_net.run create mode 100644 dde_linux/src/drivers/usb/nic/component.h create mode 100644 dde_linux/src/drivers/usb/nic/nic.cc create mode 100644 dde_linux/src/drivers/usb/platform.h create mode 100644 dde_linux/src/drivers/usb/signal/dispatch.h 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 */