From 301e81d7a53aa814130caea7856c7d9f50b11cd0 Mon Sep 17 00:00:00 2001 From: Philippe Houdoin Date: Tue, 20 May 2003 00:03:46 +0000 Subject: [PATCH] Better checking these than lost them one day... git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3259 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kits/net/new_stack/ethernet/ethernet.c | 293 ++++++ .../ethernet/ethernet_module_x86.proj | Bin 0 -> 12017 bytes .../kits/net/new_stack/loopback/loopback.c | 121 +++ .../loopback/loopback_module_x86.proj | Bin 0 -> 12017 bytes src/tests/kits/net/new_stack/memory_pool.h | 38 + .../net/new_stack/memory_pool/memory_pool.c | 295 ++++++ .../memory_pool/memory_pool_module_x86.proj | Bin 0 -> 11936 bytes src/tests/kits/net/new_stack/net_interface.h | 124 +++ src/tests/kits/net/new_stack/net_protocol.h | 17 + src/tests/kits/net/new_stack/net_server.cpp | 118 +++ .../kits/net/new_stack/net_server_x86.proj | Bin 0 -> 11872 bytes src/tests/kits/net/new_stack/net_stack.h | 109 ++ .../kits/net/new_stack/net_stack_driver.h | 141 +++ src/tests/kits/net/new_stack/stack/data.c | 931 ++++++++++++++++++ src/tests/kits/net/new_stack/stack/data.h | 49 + src/tests/kits/net/new_stack/stack/datalink.c | 231 +++++ src/tests/kits/net/new_stack/stack/datalink.h | 24 + src/tests/kits/net/new_stack/stack/stack.c | 150 +++ .../net/new_stack/stack/stack_module_x86.proj | Bin 0 -> 13086 bytes src/tests/kits/net/new_stack/stack/timer.c | 276 ++++++ src/tests/kits/net/new_stack/stack/timer.h | 37 + src/tests/kits/net/new_stack/userland_ipc.c | 554 +++++++++++ src/tests/kits/net/new_stack/userland_ipc.h | 62 ++ .../kits/net/new_stack/userland_modules.cpp | 851 ++++++++++++++++ 24 files changed, 4421 insertions(+) create mode 100644 src/tests/kits/net/new_stack/ethernet/ethernet.c create mode 100644 src/tests/kits/net/new_stack/ethernet/ethernet_module_x86.proj create mode 100644 src/tests/kits/net/new_stack/loopback/loopback.c create mode 100644 src/tests/kits/net/new_stack/loopback/loopback_module_x86.proj create mode 100644 src/tests/kits/net/new_stack/memory_pool.h create mode 100644 src/tests/kits/net/new_stack/memory_pool/memory_pool.c create mode 100644 src/tests/kits/net/new_stack/memory_pool/memory_pool_module_x86.proj create mode 100644 src/tests/kits/net/new_stack/net_interface.h create mode 100644 src/tests/kits/net/new_stack/net_protocol.h create mode 100644 src/tests/kits/net/new_stack/net_server.cpp create mode 100644 src/tests/kits/net/new_stack/net_server_x86.proj create mode 100644 src/tests/kits/net/new_stack/net_stack.h create mode 100644 src/tests/kits/net/new_stack/net_stack_driver.h create mode 100644 src/tests/kits/net/new_stack/stack/data.c create mode 100644 src/tests/kits/net/new_stack/stack/data.h create mode 100644 src/tests/kits/net/new_stack/stack/datalink.c create mode 100644 src/tests/kits/net/new_stack/stack/datalink.h create mode 100644 src/tests/kits/net/new_stack/stack/stack.c create mode 100644 src/tests/kits/net/new_stack/stack/stack_module_x86.proj create mode 100644 src/tests/kits/net/new_stack/stack/timer.c create mode 100644 src/tests/kits/net/new_stack/stack/timer.h create mode 100644 src/tests/kits/net/new_stack/userland_ipc.c create mode 100644 src/tests/kits/net/new_stack/userland_ipc.h create mode 100644 src/tests/kits/net/new_stack/userland_modules.cpp diff --git a/src/tests/kits/net/new_stack/ethernet/ethernet.c b/src/tests/kits/net/new_stack/ethernet/ethernet.c new file mode 100644 index 0000000000..1a2e16c3f0 --- /dev/null +++ b/src/tests/kits/net/new_stack/ethernet/ethernet.c @@ -0,0 +1,293 @@ +/* ethernet.c - ethernet devices interface module + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "net_stack.h" +#include "net_interface.h" + +enum { + ETHER_GETADDR = B_DEVICE_OP_CODES_END, + ETHER_INIT, + ETHER_NONBLOCK, + ETHER_ADDMULTI, + ETHER_REMMULTI, + ETHER_SETPROMISC, + ETHER_GETFRAMESIZE +}; + +typedef struct ethernet_interface { + ifnet_t ifnet; + int fd; + int mtu; +} ethernet_interface; + +#define ETHER_ADDR_LEN 6 /* Ethernet address length */ +#define ETHER_TYPE_LEN 2 /* Ethernet type field length */ +#define ETHER_CRC_LEN 4 /* Ethernet CRC lenght */ +#define ETHER_HDR_LEN ((ETHER_ADDR_LEN * 2) + ETHER_TYPE_LEN) +#define ETHER_MIN_LEN 64 /* Minimum frame length, CRC included */ +#define ETHER_MAX_LEN 1518 /* Maximum frame length, CRC included */ + +#define ETHERMTU (ETHER_MAX_LEN - ETHER_HDR_LEN - ETHER_CRC_LEN) +#define ETHERMIN (ETHER_MIN_LEN - ETHER_HDR_LEN - ETHER_CRC_LEN) + +typedef struct ether_addr { + uint8 byte[ETHER_ADDR_LEN]; +} ether_addr_t; + + +status_t lookup_devices(char *root, void *cookie); +status_t std_ops(int32 op, ...); + +struct net_interface_module_info nimi; + +static struct net_stack_module_info *g_stack = NULL; + + +// ------------------- +status_t init(void * params) +{ + return lookup_devices("/dev/net", params); +} + +status_t uninit(ifnet_t *iface) +{ + ethernet_interface *ei = (ethernet_interface *) iface; + + printf("ethernet: uniniting %s interface\n", iface->if_name); + + free(iface->if_name); + close(ei->fd); + + return B_OK; +} + + +status_t up(ifnet_t *iface) +{ + return B_ERROR; +} + + +status_t down(ifnet_t *iface) +{ + return B_ERROR; +} + + +status_t send(ifnet_t *iface, net_data *data) +{ + if (!data) + return B_ERROR; + + + return B_OK; +} + + +status_t receive(ifnet_t *iface, net_data **data) +{ + ethernet_interface *ei = (ethernet_interface *) iface; + net_data *nd; + void *frame; + size_t len; + status_t status; + + len = iface->if_mtu; + frame = malloc(len); + if (!frame) + return B_NO_MEMORY; + + nd = g_stack->new_data(); + if (!nd) { + free(frame); + return B_NO_MEMORY; + }; + + status = read(ei->fd, frame, len); + if (status >= B_OK) { + g_stack->append_data(nd, frame, status, free); + *data = nd; + return status; + }; + + free(frame); + g_stack->delete_data(nd, false); + return status; +} + +status_t control(ifnet_t *iface, int opcode, void *arg) { return -1; } + +status_t get_hardware_address(ifnet_t *iface, net_interface_hwaddr *hwaddr) { return -1; } +status_t get_multicast_addresses(ifnet_t *iface, net_interface_hwaddr **hwaddr, int nb_addresses) { return -1; } +status_t set_multicast_addresses(ifnet_t *iface, net_interface_hwaddr *hwaddr, int nb_addresses) { return -1; } + +status_t set_mtu(ifnet_t *iface, uint32 mtu) { return -1; } +status_t set_promiscuous(ifnet_t *iface, bool enable) { return -1; } +status_t set_media(ifnet_t *iface, uint32 media) { return -1; } + +// #pragma mark - + +status_t lookup_devices(char *root, void *cookie) +{ + DIR *dir; + struct dirent *de; + struct stat st; + char path[1024]; + status_t status; + int fd; + + dir = opendir(root); + if (!dir) { + printf("Couldn't open the directory %s\n", root); + return B_ERROR; + }; + + status = B_OK; + + while ((de = readdir(dir)) != NULL) { + if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) + continue; // skip pseudo-directories + + sprintf(path, "%s/%s", root, de->d_name); + + if (stat(path, &st) < 0) { + printf("ethernet: Can't stat(%s) entry! Skipping...\n", path); + continue; + }; + + if (S_ISDIR(st.st_mode)) + status = lookup_devices(path, cookie); + else if (S_ISCHR(st.st_mode)) { // char device = driver! + ifnet_t *iface; + ethernet_interface *ei; + int frame_size; + ether_addr_t mac_address; + + if (strcmp(de->d_name, "stack") == 0 || // OBOS stack driver + strcmp(de->d_name, "api") == 0) // BONE stack driver + continue; // skip pseudo-entries + + fd = open(path, O_RDWR); + if (fd < B_OK) { + printf("ethernet: Unable to open(%s) -> %d [%s]. Skipping...\n", path, + fd, strerror(fd)); + status = fd; + continue; + }; + + // Init the network card + status = ioctl(fd, ETHER_INIT, NULL, 0); + if (status < B_OK) { + printf("ethernet: Failed to init %s: %ld [%s]. Skipping...\n", path, + status, strerror(status)); + close(fd); + continue; + }; + + // get the MAC address... + status = ioctl(fd, ETHER_GETADDR, &mac_address, 6); + if (status < B_OK) { + printf("ethernet: Failed to get %s MAC address: %ld [%s]. Skipping...\n", + path, status, strerror(status)); + close(fd); + continue; + }; + + + // Try to determine the MTU to use + status = ioctl(fd, ETHER_GETFRAMESIZE, &frame_size, sizeof(frame_size)); + if (status < B_OK) { + frame_size = ETHERMTU; + printf("ethernet: %s device don't support IF_GETFRAMESIZE; defaulting to %d\n", + path, frame_size); + }; + + printf("ethernet: interface '%s':\n" + "\tMAC address: %02x:%02x:%02x:%02x:%02x:%02x\n" + "\tMTU: %d bytes\n", + path, + mac_address.byte[0], mac_address.byte[1], + mac_address.byte[2], mac_address.byte[3], + mac_address.byte[4], mac_address.byte[5], frame_size); + + + + iface = (ifnet_t *) malloc(sizeof(*iface)); + if (!iface) + break; + + ei = (ethernet_interface *) iface; + + iface->if_name = strdup(path); + iface->if_flags = (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST); + iface->if_type = 0x20; + iface->if_mtu = frame_size; + + iface->module = &nimi; + ei->fd = fd; + + status = g_stack->register_interface(iface); + }; + }; + + closedir(dir); + + return status; +} + +// #pragma mark - + +status_t std_ops(int32 op, ...) +{ + switch(op) { + case B_MODULE_INIT: + printf("ethernet: B_MODULE_INIT\n"); + return get_module(NET_STACK_MODULE_NAME, (module_info **) &g_stack); + + case B_MODULE_UNINIT: + printf("ethernet: B_MODULE_UNINIT\n"); + put_module(NET_STACK_MODULE_NAME); + break; + + default: + return B_ERROR; + } + return B_OK; +} + +struct net_interface_module_info nimi = { + { + NET_INTERFACE_MODULE_ROOT "ethernet/v0", + 0, + std_ops + }, + + init, + uninit, + up, + down, + send, + receive, + control, + get_hardware_address, + get_multicast_addresses, + set_multicast_addresses, + set_mtu, + set_promiscuous, + set_media +}; + +_EXPORT module_info *modules[] = { + (module_info*) &nimi, // net_interface_module_info + NULL +}; diff --git a/src/tests/kits/net/new_stack/ethernet/ethernet_module_x86.proj b/src/tests/kits/net/new_stack/ethernet/ethernet_module_x86.proj new file mode 100644 index 0000000000000000000000000000000000000000..5f82df67c1b3e46e426db5f4f209b9e29ce2e51f GIT binary patch literal 12017 zcmeHN|8E?{6`##*le2N+3$bg*0df$M@FnL1p`y|t@Hyw0l-ODMT)wIFvfdlpTkp5- zu46}0rIm^*gy07xgwkIsB=`g1ClEpvKSa< zEC@bVM7kS!c4ywa_vXFN&CWi*orx3WF~)}fRo47j#*X|=^T!;u1%-y68{jd+-Jlqp4MJ`g+YS@0v7cs? zbDr=1o*UsG8+f%_s|gQ986GZ@|E1|tIYTe3-l^$HP3;bRqq<}|HFxQ)e=1QgXrtMz zVVTB4)e9N>pv2gxqrKykm5IUJw67Zr+<(mEOVjQGcdp>&_9xcG_Df)QiMzsXI6gKt zHK_W0HnwCp^`Xrd3(Upz+j6b;rzN7`jeCFUe$%-Bq7^*4e7fgMd48+^;rI zvJ-qy{o$dQ-J)J)$t!Y%^uBs)WF6{`sYx&3KGfyEo;yPQUx08wD$%C>xjDwVde9`B za@i}lMRSHxwM=IL{mm7!eT$0jc>rDyP1h+d7wn~iT*B9^KGWcA)vvX-oN;QyqrAYa zS-#AT#ZaHIekd^cKie{_`vu)M=FKxdWC$?X#ux=)RDes;*yJ_j%VF`ZO$w73*>d;Y zLcw)mi-hC4utmmp*M}{MF+aI2SSdBPwKXdi8!j!cUFhJ;TN8k9g)R8!X-mqM*b>^- zRv6Q=Eg8miX$aPCYwxyJ9fB?H^W3KI)7WYqi z6vzaO0;IyMXBzp!sr%6Glnw$N1lkJ$xlP<$vsSl>llZ4=?IzJ)kaWE5Akaae837rB z3{YwaboDo0O%J(-G@ghdJrSQQ|MGsp1A3&in!4cOJog=r?ANA? zv^AfEMXU#5(MjEzTh!-xYNyaG`3jP+FA1M)*Lhs`9n+Z;%L^6AHH5g6a6%LE!rkkQ++hR8XccOr7FESr2s*QRLWt zhyy8`cN5}3178c%OchAtKnoV0rT7c>x*fO_+s?;VJwEeM<3`HbmA4VBro~s z{`utIs4}nn&%Kn5)klEryDB3;1%D1n?B0C+J&e)GJEGr#{@Ldn{U^{r_&}E!Pcb4{ zYO~jeB2m##e;fLCOL&sL6kF1JTH?&U9E_;H{&g|4sDVp`rl7vR=z%vS(ygCoXdB;OIsjh zsQ;_M%nI?-IPC(djKVT25dE&7%`|MPwQIPJztwHk!Fzxm8THRQfs_1(qLba<8K|8b>iI%e2{eCnriXX2+asEr+Oa`MMn z(-z$F4g7{&80TeLYErk6x}r~KC|P_>OHcM}3Y3RY=n2)d(rBt+ZJKKar*p3LXilP` zg5(*gM8&g}6y-Pq0AVs?&wM-(UHtdKt907=(vf7mwsP?5hxX3M1AV%DAR-=W)96*kBc3u-yhSUL-7Kqw{9FY;L>$*{bKd*6@7FdHXs}+9Yzw^5K!e zLpQ4%yuq7a$BX@*^$;(fZesy_^z?&f3cjXGB-_%*!{{DZ`#WuTl1lnKlxmVb(}&jW i$%#2RAsshgQzF^h+=K#TT35{= +#include + +#include "net_stack.h" +#include "net_interface.h" + +status_t std_ops(int32 op, ...); + +struct net_interface_module_info nimi; + +struct net_stack_module_info *g_stack = NULL; + +status_t init(void * params) +{ + ifnet_t * iface; + + iface = (ifnet_t *) malloc(sizeof(*iface)); + if (!iface) + return B_ERROR; + + iface->if_name = "loopback"; + iface->if_flags = 0; + iface->module = &nimi; + + g_stack->register_interface(iface); + + return B_OK; +} + +status_t uninit(ifnet_t *iface) +{ + printf("loopback: uniniting %s interface\n", iface->if_name); + return B_OK; +} + +status_t up(ifnet_t *iface) +{ + return B_ERROR; +} + + +status_t down(ifnet_t *iface) +{ + return B_ERROR; +} + + +status_t send(ifnet_t *iface, net_data *data) +{ + if (!data) + return B_ERROR; + + return B_OK; +} + + +status_t receive(ifnet_t *iface, net_data **data) +{ + return B_ERROR; +} + +status_t control(ifnet_t *iface, int opcode, void *arg) { return -1; } + +status_t get_hardware_address(ifnet_t *iface, net_interface_hwaddr *hwaddr) { return -1; } +status_t get_multicast_addresses(ifnet_t *iface, net_interface_hwaddr **hwaddr, int nb_addresses) { return -1; } +status_t set_multicast_addresses(ifnet_t *iface, net_interface_hwaddr *hwaddr, int nb_addresses) { return -1; } + +status_t set_mtu(ifnet_t *iface, uint32 mtu) { return -1; } +status_t set_promiscuous(ifnet_t *iface, bool enable) { return -1; } +status_t set_media(ifnet_t *iface, uint32 media) { return -1; } + + +// #pragma mark - + +status_t std_ops(int32 op, ...) +{ + switch(op) { + case B_MODULE_INIT: + printf("loopback: B_MODULE_INIT\n"); + return get_module(NET_STACK_MODULE_NAME, (module_info **) &g_stack); + + case B_MODULE_UNINIT: + printf("loopback: B_MODULE_UNINIT\n"); + put_module(NET_STACK_MODULE_NAME); + break; + + default: + return B_ERROR; + } + return B_OK; +} + +struct net_interface_module_info nimi = { + { + NET_INTERFACE_MODULE_ROOT "loopback", + 0, + std_ops + }, + + init, + uninit, + up, + down, + send, + receive, + control, + get_hardware_address, + get_multicast_addresses, + set_multicast_addresses, + set_mtu, + set_promiscuous, + set_media +}; + +_EXPORT module_info *modules[] = { + (module_info*) &nimi, // net_interface_module_info + NULL +}; diff --git a/src/tests/kits/net/new_stack/loopback/loopback_module_x86.proj b/src/tests/kits/net/new_stack/loopback/loopback_module_x86.proj new file mode 100644 index 0000000000000000000000000000000000000000..4b1cc729df9030a90ed425604a5eecd6ba6ad37d GIT binary patch literal 12017 zcmeHN|8E?{6`##5$=NvZh1j*@069oW_>%L1P!Ti;e9k#0#dcOc7bv2=?Cp*1t@m4Z z*Rj*6(n>`YLg^2cP?i2tRVse~`~<2h#1B#1AEKxdzXX9$e?XNQEeIhb5W0wd-t4UJ zY%EB8u84X!^6bvMdGGCepPQY1elyc2%M*-^{-><@^NbyROYs5>fJxX747d`v0UTsJJ%`5A7xUU8A@b?yhn8mGYy zDXn<0&G9fiuS39qo7aJd=LY6#ry%kTcvxnwQ&4F7xd|RM?gqu+Y!Gt0*m0O>&Gj^+ zob!D5=iCT?)x@jQS}k}e%IIj3{4dRw${G4#_0P`DXlifZ8`TxlskQeA>V84@jYad!w;2LVb}&W(7!}}>G`4sR`D$3ae~ZE-Mz%cg zfKYH#*dpP$DQuCk-OXW3V$64L3ua2)ZEwzs$%aeYa~C@J>h=WS+hGg-dD@n;Ew+TV zwH?N^ZA*qRT^fS9+upaWU58+cd$ZW0GQ~%*)2>U~A`e^w3Cwr>8%>H_#(_^Kxib#m ziqp?(l*bq@dEP|b3|(KNFiylv6g6JCa^*SIQ1~v-O@#%i$G@~AeV_d>ekvaF@=A+)-MvB1xeT2E&^QyS`m;T z$PlH5Kv#e4)%B2TNaKkZLJq%buNai^K2HP4?}_+i_0dNJ59pE7ZtB8vU9W~rSF3F| z*p{~Dldy>OAuO8GorPt6fv0u~jgqe*`392k$#$J*b>A_a1;M;A!z6`{vPD}%{PP$6KZ5&!yGC4TpE)lIj%KH64_#*v=~Ii zg|5wTiXE0u&BDv1)wvQZx9aDRnWhP=i6Tn#56{HG5QBE7DM0Dj|~bs@d{az zmwa^paAtp0S=9ZPU&+SmQ$Y4@m64x2E`NQhB-yo57>y z@{NKy3hT)yXR zS^^_+& z+!$<8V4v~=`XB#vcNAIsqq(2Vl;}Mmo+(rf^}#m=Bk}J4Y$&2W(Mdj8Ez{ML{mWjl z>L6cAV%C-=oK|C{S`9+or%%?_N;TxZUG?4N&~$k9#&uLp#|+z$PyJNxOZ-%ZwTTlg zPTsgh|MwE|3;epA7#Cz(YDTw_x}vQ!lq`1B(vv+~0^~^)dP6m>G@dG0pXRFJbk6l2 z%}F#=kUS%msCc)MqMSegAWUZL#rKAyOXo(e(P`()N0agTFGsGuWABbU&|kQekBEob zH2N9Fjv@aB6%V2NK90H!;ZB?MLVtwD6K|cLnl^2UF=%X`*zH~|1n#nL@PgsiIh(GW zurQRKDPsgA`t&`yHOE?BVH+l0Lc(LBIKH|mOZ7hM0-hR+b!Pjz$WLx@p1l>dHf2U1PQc0gjQccol l+GyRGoS2gjq~qr6N+f$*d!PWB=G8io&6 + +#ifdef __cplusplus +extern "C" { +#endif + +// Pools of *nodes* kernel module +// Pools store any node of data, but they should be all the same node_size +// size (set at pool creation time). +// Very usefull for multiple same-sized structs storage... + +struct memory_pool; +typedef struct memory_pool memory_pool; + +// for_each_pool_node() callback prototype: +typedef status_t (*pool_iterate_func)(memory_pool * pool, void * node, void * cookie); + +struct memory_pool_module_info { + module_info module; + + memory_pool * (*new_pool)(size_t node_size, uint32 node_count); + status_t (*delete_pool)(memory_pool * pool); + void * (*new_pool_node)(memory_pool * pool); + status_t (*delete_pool_node)(memory_pool * pool, void * node); + status_t (*for_each_pool_node)(memory_pool * pool, pool_iterate_func iterate, void * cookie); +}; + +#define MEMORY_POOL_MODULE_NAME "generic/memory_pool/v1" + +#ifdef __cplusplus +} +#endif + +#endif /* OBOS_MEMORY_POOL_H */ + diff --git a/src/tests/kits/net/new_stack/memory_pool/memory_pool.c b/src/tests/kits/net/new_stack/memory_pool/memory_pool.c new file mode 100644 index 0000000000..ea913c0459 --- /dev/null +++ b/src/tests/kits/net/new_stack/memory_pool/memory_pool.c @@ -0,0 +1,295 @@ +#include // for the hack using malloc() / free() +#include +#include + +#include "memory_pool.h" + +// Keep in mind that pools size are rounded to B_PAGE_SIZE, and +// node_size are align to 32 bits boundaries... + +struct memory_pool { + struct memory_pool * next; // for dynamic pool expansion/shrinking... + size_t node_size; // in byte, rounded to 32 bits boundary + uint32 node_count; + // struct memory_pool_block blocks[1]; +}; + +typedef struct memory_pool_block { + uint32 nb_max; // in this pool only, may vary from pool to another for same pools chain + uint32 nb_free; // in this pool only, if 0 try 'next' one... + void * first; // address of first node, just next to freemap + void * last; // address of last node in this pool + // the uint32 freemap[] follow, aligned to 32 bits boundary + // then the nodes data follow... +} memory_pool_block; + +#define BITMAPSIZE(nb_nodes) (nb_nodes / 8) + +status_t std_ops(int32 op, ...); + +memory_pool * new_pool(size_t node_size, uint32 node_count); +status_t delete_pool(memory_pool * pool); +void * new_pool_node(memory_pool * pool); +status_t delete_pool_node(memory_pool * pool, void * node); +status_t for_each_pool_node(memory_pool * pool, pool_iterate_func iterate, void * cookie); + +// Keep in mind that pools size are rounded to B_PAGE_SIZE... + +#define ROUND(x, y) (((x) + (y) - 1) & ~((y) - 1)) + +#define DEBUG 1 + + + +#ifdef CODEWARRIOR + #pragma mark [Public functions] +#endif + +// ------------------------------- +memory_pool * new_pool(size_t node_size, uint32 node_count) +{ + // TODO! + return (memory_pool *) node_size; // quick hack +} + + +// ------------------------------- +status_t delete_pool(memory_pool * pool) +{ + // TODO! + + return 0; // return B_OK; +} + + +// ------------------------------- +void * new_pool_node(memory_pool * pool) +{ + // TODO! + + return malloc((size_t) pool); // quick hack +} + + +// ------------------------------- +status_t delete_pool_node(memory_pool * pool, void * node) +{ + // TODO! + + free(node); // quick hack + return 0; // return B_OK; +} + + +// ------------------------------- +status_t for_each_pool_node(memory_pool * pool, pool_iterate_func iterate, void * cookie) +{ + // TODO! + return 0; // return B_OK; +} + + +#if 0 + +// ------------------------------- +memory_pool * new_pool2(size_t node_size, uint32 node_count) +{ + memory_pool * pool; + size_t size; + size_t freemap_size; + uint8 * ptr; + + + node_size = ROUND(node_size, 4); // aligned to 32 bits + + freemap_size = ROUND(node_count, 32) / 8; // aligned to 32 bits + + size = sizeof(memory_pool); + + size += freemap_size; + size += node_size * node_count; + + pool = (memory_pool *) malloc(size); + + pool->next = NULL; // no secondary pool for the moment... + + pool->node_size = node_size; // aligned to 32 bits + pool->nb_max = node_count; + pool->nb_free = node_count; + + ptr = (uint8 *) (pool + 1); + ptr += freemap_size; + + pool->first = ptr; + + ptr += (node_count-1) * node_size; + + pool->last = ptr; + + return pool; +} + + + +// ------------------------------- +status_t delete_pool2(memory_pool * pool) +{ + memory_pool * next; + + while (pool) { + next = pool->next; + + my_free2("delete_pool: ", pool); + + pool = next; + }; + + return 0; // return B_OK; +} + + +// ------------------------------- +void * new_pool_node2(memory_pool * pool) +{ + memory_pool * p; + size_t slot; + sizet nb_slots; + uint32* freemap; + uint8 * ptr; + + // seaching pools list for one with at least one free node + p = pool; + while (p->nb_free == 0) { + p = p->next; + }; + + if (! p) { + // need to add a new pool + p = new_pool2(pool->node_size, pool->nb_max); + if (!p) + return NULL; // argh, no more memory!?! + + p->next = pool->next; + pool->next = p; + }; + + // okay, now find the first free slot of this pool + slot = 0; + nb_slots = ROUND(p->nb_max, 32); // aligned to 32 bits + freemap = (uint32 *) (p + 1); + + // fast lookup over 32 contiguous slots already in use (if any) + while (slot < nb_slots) { + if (*freemap != 0xFFFFFFFF) + break; + + freemap++; // all 32 contiguous slots used + slot += 32; + }; + + // find the first free slot of these next 32 ones + while(slot < nb_slots) { + if ( *freemap & (1 << (31 - (slot % 32))) == 0) + // free slot found :-) + break; + + slot++; + }; + + if (slot >= nb_slots) + // oh oh, should never happend! ;-) + return NULL; + + *freemap |= (1 << (31 - (slot % 32))); + p->nb_free--; + + ptr = (uint8 *) p->first; + ptr += slot * p->node_size; + + return ptr; +} + + +// ------------------------------- +status_t delete_pool_node2(memory_pool * pool, void * node) +{ + memory_pool * p; + size_t slot; + size_t nb_slots; + uint32* freemap; + uint8 * ptr; + + // seaching pools list for the one who host this node + p = pool; + while (pool) { + if (node >= pool->first && node <= pool->last) + break; // node's hosting pool found + pool = pool->next; + }; + + if (! pool) + return -1; // return B_BAD_VALUE; + + // find node slot number on this pool + slot = (node - pool->first); + if (slot % pool->node_size) + // oh oh, not a valid, starting node address value! + return -1; // return B_BAD_VALUE; + + nb_slots = ROUND(p->nb_max, 32); // aligned to 32 bits + slot /= pool->node_size; + if (slot >= nb_slots) + // oh oh, node slot out of range!!! + // something go wrong with pool->last value!?! + return -1; // return B_BAD_VALUE; + + freemap = (uint32 *) (pool + 1); + + pool->nb_free++; + if (pool->nb_free == pool->nb_max) + // free this pool_block + + return 0; // return B_OK; +} + +#endif + +// #pragma mark - + +struct memory_pool_module_info mpmi = { + { + MEMORY_POOL_MODULE_NAME, + 0, + std_ops + }, + + new_pool, + delete_pool, + new_pool_node, + delete_pool_node, + for_each_pool_node +}; + +status_t std_ops(int32 op, ...) +{ + switch(op) { + case B_MODULE_INIT: + printf("memory_pool: B_MODULE_INIT\n"); + break; + + case B_MODULE_UNINIT: + printf("memory_pool: B_MODULE_UNINIT\n"); + break; + + default: + return B_ERROR; + } + return B_OK; +} + + +_EXPORT module_info *modules[] = { + (module_info *) &mpmi, // memory_pool_module_info + NULL +}; + diff --git a/src/tests/kits/net/new_stack/memory_pool/memory_pool_module_x86.proj b/src/tests/kits/net/new_stack/memory_pool/memory_pool_module_x86.proj new file mode 100644 index 0000000000000000000000000000000000000000..a9e246e3363c1184241c87722f88e765e3804af2 GIT binary patch literal 11936 zcmeHNO>7)R7Ou|pCS#Mt6Jp1X6UabB!cWEnLZD@Xh{qYn1Y)P<@$#o-C+(iH-MIg@ zyB#}ob`58PcbFx)XSoU)jJSMpt6oXSi$n9dwVWKUsry1q6 z=etjEBYbihuU2a{;Grl314Z(`G*K#N=!4ZYJ~5`Loq=yu=S`>P&Y$^*5_N$#l+7BJ zY0Or=kg-onjD0!OH8NHi?aNL0x-rZBhfF>{;m&gB8+_cJVjXOc1a_;qE9{yh!{g(9 zs?Vo_YeCwCdSY9`Q!Qy)m4yr%j~`PvP0od(>jdLr$ZA_qzDYd1vJzRsAM^a1WhUHIGS1Ju6@gu79RHsMcCGuE*gO|mJs zxOI0lZ5UO{bY{_CTOr%mspy^u;PudSo#H~lo-fEp_=?qMmN}dCYo#qGo!Y<A$WG1`?fXf5NvU;7F$%N_y~5Ib!lDXflDBP`K^DW5s}N-_1y?}hTvOq z^kI$i7=0zr8?Kq5>uVIoiFk>k#tRoN{LqPUMZq5~iI}>_FbsrNstR`wyS8Uq+&}J7 zAQLbOkP1_tY2*vX??k&@+6c4}Xe|WfGI3qaT3RNK;h(ORi$rTd()PBEKpTNZ1Y`)( zOQ|8y)jxVQJ>(kFcp`?7!>`-(24%copaJCfM0~RF`@01X=#kQF>cZx>>z}H6uDj-l zUfP;Z!XmaAVbL+&nV!?9d1|NdT=EqpUw0Bd*{<`j?mMP4EtoIQFiD~P4ML6VLb13c zf|C7K$(|E))Hul87rCYtlaNdzq27{6sA{Q(ND#4{ZjBs1yjpm-JY~DpMxc#At05ql znEoWZV_916M0mHf*vTbje;NR;SW?=dV5>baZGYPc{2T&uLFpb3%GiWt>NpR|dZ?3# zBFFAT97x%`-G~DX>=ve(Dv-p18cI>IZTY&8VM2`zY?vcOA4y}fB*(QRNg``3loo@i zxX^VuoMQXrtT`F11*^|0-AlPQm(4)OUZ{;7H<^or{wz&E>LvHyRQHJGPU;p?ZviR72V+L*y4JSYJg zoWCKa_U8|7i|F21xEv$e^#?oRXW#9N2hraB*}nMe@AGl~t9>y&5Y%Tc_eNBMVaCpF z$=6f7dN(|mjmw~sO=6zWol$;kzW%R69q}OOq=)XLi|)j8 z_S$@1(82f8TiL`uw7+;JyEJy;#f|YGbfvsx2eqM&o!y?V_nLb|pXk}1e4X~&$tL0< zTZG;B?u@!XKfS9*_$+9o0CdLkb;5zV3i@X;zx|)?n8t`NMt>K^V(5GDo*qFbULi~J zl8^3RjqQvoGrIrW%h_1HAIN^7GV)XK=a9ti%GW={7@fQ$`pxK{dSSW$2>LhO(;>!F zj7XN+?5+MtRP@u|hW2g=PtuoSOL|X8oY}X15%n+sZP;V|(R-LH{cT83Do=M|GkCOI zzJByzPkg&%)c4J34?YV_Kz{`?51`UnkNZK# zjlmWL_9=ft|C`6RM3J>Kns{`qMDGFd97EMmZ-1vJ67T+>dn4)-o#d0%GF?5{-|cy; z2J*QiW^Gu)X*E`=)gaV;`ed!GR739DRo|TpO@~*1y^gBsm|+w0sh`R%iJwZJHhiSP z$zN~L|Gk9#0>3REj76E28q;m0u4wBFC5zp(^kmPP06Bz0Z>Xk~hEfG9(_E7{opYr} za}o^|B+p1CD&DQ6C`S+g2$LCm`t#oC+?zWt(P`)N2a@sH8#^w2YHx`=(BC_>Cn6qd z)9CjYJBa)nRNR5?^Qiwsy&08EXA$asuRVHZ)U+wa=tbQwcDok}fjj3LykNLB&PFRo zEDYU6z1jc( literal 0 HcmV?d00001 diff --git a/src/tests/kits/net/new_stack/net_interface.h b/src/tests/kits/net/new_stack/net_interface.h new file mode 100644 index 0000000000..198f7c16db --- /dev/null +++ b/src/tests/kits/net/new_stack/net_interface.h @@ -0,0 +1,124 @@ +/* net_interface.h + * definitions of interface networking module API + */ + +#ifndef OBOS_NET_INTERFACE_H +#define OBOS_NET_INTERFACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct net_data; + +typedef struct if_data { + /* generic interface information */ + // ifmedia_t ifi_media; /* media, see if_media.h */ + uint8 ifi_type; /* ethernet, tokenring, etc */ + uint8 ifi_addrlen; /* media address length */ + uint8 ifi_hdrlen; /* media header length */ + uint32 ifi_mtu; /* maximum transmission unit */ + /* this does not count framing; */ + /* e.g. ethernet would be 1500 */ + uint32 ifi_metric; /* routing metric (external only) */ + /* volatile statistics */ + uint32 ifi_ipackets; /* packets received on interface */ + uint32 ifi_ierrors; /* input errors on interface */ + uint32 ifi_opackets; /* packets sent on interface */ + uint32 ifi_oerrors; /* output errors on interface */ + uint32 ifi_collisions; /* collisions on csma interfaces */ + uint32 ifi_ibytes; /* total number of octets received */ + uint32 ifi_obytes; /* total number of octets sent */ + uint32 ifi_imcasts; /* packets received via multicast */ + uint32 ifi_omcasts; /* packets sent via multicast */ + uint32 ifi_iqdrops; /* dropped on input, this interface */ + uint32 ifi_noproto; /* destined for unsupported protocol */ + bigtime_t ifi_lastchange; /* time of last administrative change */ +} ifdata_t; + +#define if_mtu if_data.ifi_mtu +#define if_type if_data.ifi_type +#define if_media if_data.ifi_media +#define if_addrlen if_data.ifi_addrlen +#define if_hdrlen if_data.ifi_hdrlen +#define if_metric if_data.ifi_metric +#define if_baudrate if_data.ifi_baudrate +#define if_ipackets if_data.ifi_ipackets +#define if_ierrors if_data.ifi_ierrors +#define if_opackets if_data.ifi_opackets +#define if_oerrors if_data.ifi_oerrors +#define if_collisions if_data.ifi_collisions +#define if_ibytes if_data.ifi_ibytes +#define if_obytes if_data.ifi_obytes +#define if_imcasts if_data.ifi_imcasts +#define if_omcasts if_data.ifi_omcasts +#define if_noproto if_data.ifi_noproto +#define if_lastchange if_data.ifi_lastchange + + + +#define IFF_UP 0x1 /* interface is up */ +#define IFF_BROADCAST 0x2 /* broadcast address valid */ +#define IFF_DEBUG 0x4 /* turn on debugging */ +#define IFF_LOOPBACK 0x8 /* is a loopback net */ +#define IFF_POINTOPOINT 0x10 /* interface is point-to-point link */ +#define IFF_PTP IFF_POINTOPOINT +#define IFF_NOARP 0x40 /* don't arp */ +#define IFF_AUTOUP 0x80 /* for autodial ppp - anytime the interface is downed, it will immediately be re-upped by the datalink */ +#define IFF_PROMISC 0x100 /* receive all packets */ +#define IFF_ALLMULTI 0x200 /* receive all multicast packets */ +#define IFF_SIMPLEX 0x800 /* can't hear own transmissions */ +#define IFF_MULTICAST 0x8000 /* supports multicast */ + +/* flags set internally only: */ +#define IFF_CANTCHANGE (IFF_BROADCAST|IFF_LOOPBACK|IFF_POINTOPOINT| \ + IFF_NOARP|IFF_SIMPLEX|IFF_MULTICAST|IFF_ALLMULTI) + + + +typedef struct ifnet { + struct ifnet *if_next; + char *if_name; + uint32 if_flags; + struct net_interface_module_info *module; + struct if_data if_data; + volatile thread_id if_reader_thread; +} ifnet_t; + +typedef struct net_interface_hwaddr { + uint32 len; + uint8 hwaddr[256]; +} net_interface_hwaddr; + +struct net_interface_module_info { + module_info info; + + status_t (*init)(void * params); + status_t (*uninit)(ifnet_t *ifnet); + + status_t (*up)(ifnet_t *ifnet); + status_t (*down)(ifnet_t *ifnet); + + status_t (*send_data)(ifnet_t *ifnet, struct net_data *data); + status_t (*receive_data)(ifnet_t *ifnet, struct net_data **data); + + status_t (*control)(ifnet_t *ifnet, int opcode, void *arg); + + status_t (*get_hardware_address)(ifnet_t *ifnet, net_interface_hwaddr *hwaddr); + status_t (*get_multicast_addresses)(ifnet_t *ifnet, net_interface_hwaddr **hwaddr, int nb_addresses); + status_t (*set_multicast_addresses)(ifnet_t *ifnet, net_interface_hwaddr *hwaddr, int nb_addresses); + + status_t (*set_mtu)(ifnet_t *ifnet, uint32 mtu); + status_t (*set_promiscuous)(ifnet_t *ifnet, bool enable); + status_t (*set_media)(ifnet_t *ifnet, uint32 media); +}; + +#define NET_INTERFACE_MODULE_ROOT "network/interfaces/" + +#ifdef __cplusplus +} +#endif + +#endif /* OBOS_NET_INTERFACE_H */ diff --git a/src/tests/kits/net/new_stack/net_protocol.h b/src/tests/kits/net/new_stack/net_protocol.h new file mode 100644 index 0000000000..8409ae415e --- /dev/null +++ b/src/tests/kits/net/new_stack/net_protocol.h @@ -0,0 +1,17 @@ +/* net_protocol.h + * definitions of protocol networking module API + */ + +#ifndef OBOS_NET_PROTOCOL_H +#define OBOS_NET_PROTOCOL_H + +#include + +struct net_protocol_module_info { + module_info module; + +}; + +#define NET_PROTOCOL_MODULE_ROOT "network/protocols/" + +#endif /* OBOS_NET_PROTOCOL_H */ diff --git a/src/tests/kits/net/new_stack/net_server.cpp b/src/tests/kits/net/new_stack/net_server.cpp new file mode 100644 index 0000000000..66d7e6b770 --- /dev/null +++ b/src/tests/kits/net/new_stack/net_server.cpp @@ -0,0 +1,118 @@ +/* Userland modules emulation support +*/ + +#include +#include +#include + +#include +#include + +#include "net_stack.h" +// #include + +struct net_stack_module_info * g_stack = NULL; + + + +void my_free(void * ptr, ...) +{ + printf("my_free(%p)\n", ptr); + free(ptr); +} + +void my_free2(void * prompt, ...) // void * ptr) +{ + va_list args; + void * ptr; + + va_start(args, prompt); + ptr = va_arg(args, void *); + va_end(args); + + printf("%s: my_free2(%p)\n", (char *) prompt, ptr); + free(ptr); +} + +int test_data() +{ + net_data *data; + char buffer[128]; + size_t len; + char *tata; + char *titi; + char *toto; + char *tutu; + + puts("test_data():"); + + tata = (char *) malloc(16); + strcpy(tata, "0123456789"); + + titi = (char *) malloc(16); + strcpy(titi, "ABCDEF"); + + toto = (char *) malloc(48); + strcpy(toto, "abcdefghijklmnopqrstuvwxyz"); + + tutu = (char *) malloc(16); + strcpy(tutu, "hello there!"); + + data = g_stack->new_data(); + + g_stack->append_data(data, titi, 6, my_free); + g_stack->prepend_data(data, tata, 10, my_free); + g_stack->add_data_free_node(data, tutu, (void *) "hello there", my_free2); + g_stack->insert_data(data, 5, toto, 26, my_free); + g_stack->prepend_data(data, "this is a net_data test: ", 25, NULL); + + g_stack->dump_data(data); + + len = g_stack->copy_from_data(data, 2, buffer, 128); + buffer[len] = 0; + + printf("buffer = [%s]\n", buffer); + + g_stack->delete_data(data, false); + + return 0; +} + + +int main(int argc, char **argv) +{ + int ret = -1; + + new BApplication("application/x-vnd-OBOS-net_server"); + + // if (init_userland_ipc() < B_OK) + // goto exit0; + + if (get_module(NET_STACK_MODULE_NAME, (module_info **) &g_stack) != B_OK) + goto exit1; + + if (g_stack->start() == B_OK) { + + puts("Userland net stack (net_server) is running."); + puts("Press any key to quit."); + + fflush(stdin); + fgetc(stdin);; + + test_data(); + + g_stack->stop(); + }; + + put_module(NET_STACK_MODULE_NAME);; + + ret = 0; + + exit1:; + // shutdown_userland_ipc(); + +// exit0:; + delete be_app; + return ret; +} + diff --git a/src/tests/kits/net/new_stack/net_server_x86.proj b/src/tests/kits/net/new_stack/net_server_x86.proj new file mode 100644 index 0000000000000000000000000000000000000000..3c43e0dbfce2448dba7f763c7a0187f568dc1c27 GIT binary patch literal 11872 zcmeHNU2GIp6ux6uSV$|2u=WQk12rI!?LtV11_QLT6hd1jF8(FzGM&9`2WECQGt*K+ zOy3S;gBqil_+;=&`Q6C5|zSLlh59$LMjT#e;S)$*$bEms44dS*2!tPD) z%)RG+=iYP9-Z?#U<_;b1>u0R{@jlHRWo-Kgn%iGwj3xd?(cDK}YNIx3t~rT&5_P$P zOJ9#+;FdyNMtzh!QI(wLkK;x-resGP$Ilk{B)6PWcAV=4?t0k~PJ?w)ZonQ!sZ=M% zfSNa8kCNk=Q}tqzYJ0G`&x>V_)Qc4=dM;p(5$Wl`PZHebXssr!w}b zjQq)}AlGj;RfZR<*IZd9i-qo9p5fLgpW?=Zua8*&_K)~~mZe*FGrDVxo0I=02yAi( zV-#>ufnB0TjaQ#f`PpqXG7}rgvVFUl;Fgd@n&Xy`MaD9>hAfdVH(eI&l!CLkJu5aF zE{oo~n1fF(O~APnvfyQ5Ny?JQ;@j3z2(xHeni$iiAlSQ$``@-$g&>P_tH_dIitD*c z^B^sOT|8-+oBbONhWO%XUh8LOX8vA@ zaYex&E)iQ|gJBp5tr7{SbHK4nrp4W(B?@FbMgdZ0v}77;=IAc88>L}^h6U=&0@6)% z7OYt}F^HG0xlW?KOw!P{VS$DPsx2Txkk*0Wql2I)9w`-cpKC~4(Mz)oHQ0IkltCHZ zmuLX_ED@hfJ+NEE8K4Jjv8jt9_wyciCpiT|wH2v~8ihry8DY_&UL2dy$9OEKm{{U6 z61O%ApKPc2nC=$M;+O!>&oD`*Emcg_@QTC{ql9mh@RV4iz(MA&$TiJbgm@y3#MX!- zk?1KP5=1PgTLT9UEEL{VmTa^d7HC+Y-Yg(pOh**nu_U=W5#G%@JLyuk!~x(Om(mCY z>utj{^lez+dKQolW$nXWpATH(Vea+mzD_JkG^X5>8>cZPksIfhH9MLqI{0cfiUid4 z=D&T%q~&%YN~T=rdPK#S}l_y6ef%u2iQf)`gk0%Mj60aPGYIC zP|^^r;zHL-u*o*dRZ}vQ^%h=Lsu!c1`%F*wJZ|HAq|8)e{v<6x(@S($JokX*jOZ2; zlO8D`VAP-zK@)AZr{fG$A~D>P8>-G=5{43>-ldHpwJ#r89nihHa5+Y_d-tpeU-)EY zxF7BHUvCb7`dtmx?>C2}GWA)qEugx*pRtQAYB|QIG5+=!E5rGi<0m89>zIe;z2n7X z*asTPM18fhJy7pb%YW=!5$*?_=Ak>yMR&qX->sGf9k|ynBqRCI{^q&l?AXl9%fwtU zmgJx|reo7<)N-r2LG+29Osi!&;6*YKhGY?PuWS!ifqrIPTIeijGy&+0sb#VQbrtld zvA+G++K|Qw7oy*Zu@Lhec`z;LgcZERFL89AzP~-_<^6b3JME9-Z3#K+Zan-a)ExJH z^u4_1xa2=z^!c_Bdk=JNm0Iq7J{iXLpuUE_kePlg6yrxVWbd>EcSl{(UyXiCN-f8~ z=L?T^g^VgnO7hVfuM$o6cVAIM@>Sq{B0RIc_=bno& ze|9xpiM$=o&H+PMl8@*#$9nXCxDNKq!%bHRdkk2rue3&d@x1S$53!XUAEg6Y=dVNX`X9-3t%qojGh<5(>T5Hla8I} zvtQD|`}mgh5ZXy>xD<3aKOEVn8SVWaLsrn=0M8KWTGT_J_6QaP@m|;FI`9M9KW+qSp|?UMloxZlOlhk{WLnT!U`Rmu6|{=1SQa?f9(ngLv3RI9bvj<< znO?3-eq)tf*To5CS{X*$3G#O^2Qn($VXiezl>yT_lFE4QM%Mtt9&%6QI%Yw(QW z6gZO_ij@GFvr;7&*-b}%bFMJPCW4VvQYiYzbeGL-$32}dIgZsm9wYCYha}gSqDjpn zT+OI%@CsI)hfSg1ECld%<0NoWPaL~wCpWu8;Bg61_rBRad}DfUjoGB|?dYc`kZ`s1 OVj*ukg$Zmyq3u7hd7WDT literal 0 HcmV?d00001 diff --git a/src/tests/kits/net/new_stack/net_stack.h b/src/tests/kits/net/new_stack/net_stack.h new file mode 100644 index 0000000000..6bb9adf34a --- /dev/null +++ b/src/tests/kits/net/new_stack/net_stack.h @@ -0,0 +1,109 @@ +/* net_stack.h + * definitions needed by all network stack modules + */ + +#ifndef OBOS_NET_STACK_H +#define OBOS_NET_STACK_H + +#include + +#include "memory_pool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Networking data chunk(s) definition +typedef struct net_data net_data; +typedef struct net_data_queue net_data_queue; +typedef void (*data_node_free_func)(void * arg1, ...); // data node free callback prototype + +// Networking timers definitions +typedef struct net_timer net_timer; +typedef void (*net_timer_func)(net_timer *timer, void *cookie); // timer callback prototype + +// Generic lockers support + +typedef int benaphore; +#define create_benaphore(a, b) +#define delete_benaphore(a) +#define lock_benaphore(a) +#define unlock_benaphore(a) + +#include "net_interface.h" + + +// Network stack main module definition + +struct net_stack_module_info { + module_info module; + + status_t (*start)(void); + status_t (*stop)(void); + + /* + * Socket layer + */ + + /* + * Data-Link layer + */ + + status_t (*register_interface)(ifnet_t *ifnet); + status_t (*unregister_interface)(ifnet_t *ifnet); + + /* + * Data chunk(s) support + */ + + net_data * (*new_data)(void); + status_t (*delete_data)(net_data *nd, bool interrupt_safe); + + net_data * (*duplicate_data)(net_data *from); + net_data * (*clone_data)(net_data *from); + + status_t (*prepend_data)(net_data *nd, const void *data, uint32 bytes, data_node_free_func freethis); + status_t (*append_data)(net_data *nd, const void *data, uint32 bytes, data_node_free_func freethis); + status_t (*insert_data)(net_data *nd, uint32 offset, const void *data, uint32 bytes, data_node_free_func freethis); + status_t (*remove_data)(net_data *nd, uint32 offset, uint32 bytes); + + status_t (*add_data_free_node)(net_data *nd, void *arg1, void *arg2, data_node_free_func freethis); + + uint32 (*copy_from_data)(net_data *nd, uint32 offset, void *copyinto, uint32 bytes); + + void (*dump_data)(net_data *nd); + + // Data queues support + net_data_queue * (*new_data_queue)(size_t max_bytes); + status_t (*delete_data_queue)(net_data_queue *queue); + + status_t (*empty_data_queue)(net_data_queue *queue); + status_t (*enqueue_data)(net_data_queue *queue, net_data *data); + size_t (*dequeue_data)(net_data_queue *queue, net_data **data, bigtime_t timeout, bool peek); + + // Timers support + + net_timer * (*new_timer)(void); + status_t (*delete_timer)(net_timer *timer); + status_t (*start_timer)(net_timer *timer, net_timer_func func, void *cookie, bigtime_t period); + status_t (*cancel_timer)(net_timer *timer); + status_t (*get_timer_appointment)(net_timer *timer, bigtime_t *period, bigtime_t *when); + + // Lockers + + // Memory Pools support + memory_pool * (*new_pool)(size_t node_size, uint32 node_count); + status_t (*delete_pool)(memory_pool *pool); + void * (*new_pool_node)(memory_pool *pool); + status_t (*delete_pool_node)(memory_pool *pool, void *node); + status_t (*for_each_pool_node)(memory_pool *pool, pool_iterate_func iterate, void *cookie); + +}; + +#define NET_STACK_MODULE_NAME "network/stack/v1" + +#ifdef __cplusplus +} +#endif + +#endif /* OBOS_NET_STACK_H */ diff --git a/src/tests/kits/net/new_stack/net_stack_driver.h b/src/tests/kits/net/new_stack/net_stack_driver.h new file mode 100644 index 0000000000..c971637070 --- /dev/null +++ b/src/tests/kits/net/new_stack/net_stack_driver.h @@ -0,0 +1,141 @@ +/* net_stack_driver.h + * structures and defines to deal with the network stack pseudo-driver... + */ + +#ifndef NET_STACK_DRIVER_H +#define NET_STACK_DRIVER_H + +#include + +#define NET_STACK_DRIVER_DEV "net/stack" +#define NET_STACK_DRIVER_PATH "/dev/" ## NET_STACK_DRIVER_DEV + +enum { + // Paranoia mode: be far away of B_DEVICE_OP_CODES_END opcodes!!! + // You never know what another device driver ioctl() will do + // if think our NET_STACK_* is in fact his DO_RISKY_BUSINESS opcode, or whatever... + NET_IOCTL_BASE = 0xbe230000, + NET_STACK_IOCTL_BASE = NET_IOCTL_BASE + 0x200 +}; + +enum { + NET_STACK_SOCKET = NET_STACK_IOCTL_BASE, // socket_args * + NET_STACK_BIND, // sockaddr_args * + NET_STACK_RECVFROM, // struct msghdr * + NET_STACK_RECV, // data_xfer_args * + NET_STACK_SENDTO, // struct msghdr * + NET_STACK_SEND, // data_xfer_args * + NET_STACK_LISTEN, // int_args * (value = backlog) + NET_STACK_ACCEPT, // accept_args * + NET_STACK_CONNECT, // sockaddr_args * + NET_STACK_SHUTDOWN, // int_args * (value = how) + NET_STACK_GETSOCKOPT, // sockopt_args * + NET_STACK_SETSOCKOPT, // sockopt_args * + NET_STACK_GETSOCKNAME, // sockaddr_args * + NET_STACK_GETPEERNAME, // sockaddr_args * + + NET_STACK_SYSCTL, // sysctl_args * + NET_STACK_SELECT, // select_args * + NET_STACK_DESELECT, // select_args * + NET_STACK_GET_COOKIE, // void ** + + NET_STACK_STOP, + + NET_STACK_NOTIFY_SOCKET_EVENT, // notify_socket_event_args * (userland stack only) + + NET_STACK_IOCTL_MAX +}; + +struct int_args { // used by NET_STACK_LISTEN/_SHUTDOWN + int value; +}; + +struct sockaddr_args { // used by NET_STACK_CONNECT/_BIND/_GETSOCKNAME/_GETPEERNAME + struct sockaddr *addr; + int addrlen; +}; + +struct sockopt_args { // used by NET_STACK_SETSOCKOPT/_GETSOCKOPT + int level; + int option; + void *optval; + int optlen; +}; + +struct data_xfer_args { // used by NET_STACK_SEND/_RECV + void *data; + size_t datalen; + int flags; + struct sockaddr *addr; // unused in *_SEND and *_RECV cases + int addrlen; // unused in *_SEND and *_RECV cases +}; + +struct socket_args { // used by NET_STACK_SOCKET + int family; + int type; + int proto; +}; + +struct getcookie_args { // used by NET_STACK_GET_COOKIE + void **cookie; +}; + +struct accept_args { // used by NET_STACK_ACCEPT + void *cookie; + struct sockaddr *addr; + int addrlen; +}; + +struct sysctl_args { // used by NET_STACK_SYSCTL + int *name; + uint namelen; + void *oldp; + size_t *oldlenp; + void *newp; + size_t newlen; +}; + +/* + Userland stack driver on_socket_event() callback mecanism implementation: + the driver start a kernel thread waiting on a port for + a NET_STACK_SOCKET_EVENT_MSG message, which come with a socket_event_data block. + + The on_socket_event() mechanism stay in driver (kernelland) because it's + only there we can call kernel's notify_select_event() on BONE systems. + For non-BONE systems, we use our own r5_notify_select_event() + implementation, that could be moved into the userland net_server code, + but it'll have split (again!) the /dev/net/stack driver code... +*/ + +struct notify_socket_event_args { // used by NET_STACK_NOTIFY_SOCKET_EVENT + port_id notify_port; // port waiting for notification, -1 = stop notify + void * cookie; // this cookie value will be pass back in the socket_event_args +}; + +#define NET_STACK_SOCKET_EVENT_NOTIFICATION 'sevn' + +struct socket_event_data { + uint32 event; // B_SELECT_READ, B_SELECT_WRITE or B_SELECT_EXCEPTION + void * cookie; // The cookie as set in notify_socket_event_args for this socket +}; + +/* + R5.0.3 and before select() kernel support is too buggy to be used, so + here are the structures we used to support select() on sockets, and *only* on + sockets! +*/ + +struct select_args { // used by NET_STACK_SELECT and NET_STACK_DESELECT + struct selectsync * sync; // in fact, it's the area_id of a r5_selectsync struct!!! + uint32 ref; +}; + +struct r5_selectsync { + sem_id lock; // lock this r5_selectsync + sem_id wakeup; // sem to release to wakeup select() + struct fd_set rbits; // read event bits field + struct fd_set wbits; // write event bits field + struct fd_set ebits; // exception event bits field +}; + +#endif /* NET_STACK_DRIVER_H */ diff --git a/src/tests/kits/net/new_stack/stack/data.c b/src/tests/kits/net/new_stack/stack/data.c new file mode 100644 index 0000000000..f20444d813 --- /dev/null +++ b/src/tests/kits/net/new_stack/stack/data.c @@ -0,0 +1,931 @@ +#include +#include +#include +#include +#include + +#include "memory_pool.h" +#include "net_stack.h" +#include "data.h" + +#if 0 +typedef struct net_data_node_info +{ + uint32 ref_count; // this data is referenced by 'ref_count' net_data_node(s) + const void * data; // address of data + uint32 len; // in bytes (could be 0, with *guest* add_free_element() node + data_node_free_func free_func; // if NULL, don't free it! + void * free_cookie; // if != NULL, free_func(cookie, data) is called... otherwise, free_func(data) +} net_data_node_info; + + +typedef struct net_data_node +{ + struct net_data_node * next; + net_data_node_info * info; +} net_data_node; +#endif + +typedef struct net_data_node +{ + struct net_data_node *next; + struct net_data_node *next_data; + uint32 ref_count; // this data is referenced by 'ref_count' net_data_node(s) + const void *data; // address of data + uint32 len; // in bytes (could be 0, with *guest* add_data_free_element() node + data_node_free_func free_func; // if NULL, don't free it! + void * free_cookie; // if != NULL, free_func(cookie, data) is called... otherwise, free_func(data) +} net_data_node; + +struct net_data { + struct net_data *next; // for chained datas, like fifo'ed ones... + struct net_data_node *node_list; // unordored but ref counted net_data_node(s) linked list, to free on delete_data() + struct net_data_node *data_list; // ordered net_data_node(s) linked list + size_t len; // total bytes in this net_data + uint32 flags; + #define DATA_TO_FREE (1) + #define DATA_IS_URGENT (2) +}; + +struct net_data_queue +{ + benaphore lock; + sem_id sync; + volatile int32 waiting; + volatile int32 interrupt; + + size_t max_bytes; + size_t current_bytes; + + net_data *head; + net_data *tail; +}; + + +#define DPRINTF printf + +#define DATAS_PER_POOL (64) +#define DATA_NODES_PER_POOL (256) + +static memory_pool * g_datas_pool = NULL; +static memory_pool * g_datas_nodes_pool = NULL; + +static thread_id g_datas_purgatory_thread = -1; +static sem_id g_datas_purgatory_sync = -1; // use to notify data purgatory thread that some net_data nodes need to be deleted (interrupt safely...) + +#define DATA_QUEUES_PER_POOL 16 + +static memory_pool * g_datas_queues_pool = NULL; + +extern struct memory_pool_module_info *g_memory_pool; + +// Privates prototypes +// ------------------- + +static net_data_node * new_data_node(const void *data, uint32 len); +static net_data_node * find_data_node(net_data *data, uint32 offset, uint32 *offset_in_node, net_data_node **previous_node); + +static int32 datas_purgatory_thread(void * data); + +static status_t data_death(memory_pool * pool, void * node, void * cookie); +static status_t data_queue_death(memory_pool * pool, void * node, void * cookie); + + +// LET'S GO FOR IMPLEMENTATION +// ------------------------------------ + + +// #pragma mark [Start/Stop Service functions] + + +// -------------------------------------------------- +status_t start_data_service() +{ + g_datas_purgatory_sync = create_sem(0, "net data purgatory"); +#ifdef _KERNEL_MODE + set_sem_owner(g_datas_purgatory_sync, B_SYSTEM_TEAM); +#endif + + // fire data_purgatory_thread + g_datas_purgatory_thread = spawn_kernel_thread(datas_purgatory_thread, "net data serial killer", B_LOW_PRIORITY, 0); + if (g_datas_purgatory_thread < B_OK) + return g_datas_purgatory_thread; + + puts("net data service started."); + + return resume_thread(g_datas_purgatory_thread); +} + + +// -------------------------------------------------- +status_t stop_data_service() +{ + status_t status; + + // Free all data queues inner stuff (sem, locker, etc) + g_memory_pool->for_each_pool_node(g_datas_queues_pool, data_queue_death, NULL); + g_memory_pool->delete_pool(g_datas_queues_pool); + g_datas_queues_pool = NULL; + + // this would stop data_purgatory_thread + delete_sem(g_datas_purgatory_sync); + g_datas_purgatory_sync = -1; + wait_for_thread(g_datas_purgatory_thread, &status); + + // As the purgatory thread stop, some net_data(s) still could be flagged + // data_TO_FREE, but not deleted... yet. + g_memory_pool->for_each_pool_node(g_datas_pool, data_death, NULL); + + // free datas-related pools + g_memory_pool->delete_pool(g_datas_pool); + g_memory_pool->delete_pool(g_datas_nodes_pool); + + g_datas_pool = NULL; + g_datas_nodes_pool = NULL; + + puts("net data service stopped."); + + return B_OK; +} + + +// #pragma mark [Public functions] + + +// -------------------------------------------------- +net_data * new_data(void) +{ + net_data *nd; + + if (! g_datas_pool) + g_datas_pool = g_memory_pool->new_pool(sizeof(net_data), DATAS_PER_POOL); + + if (! g_datas_pool) + return NULL; + + nd = (net_data *) g_memory_pool->new_pool_node(g_datas_pool); + if (! nd) + return NULL; + + nd->next = NULL; + nd->data_list = NULL; + nd->node_list = NULL; + nd->len = 0; + nd->flags = 0; + + return nd; +} + + + +// -------------------------------------------------- +status_t delete_data(net_data *nd, bool interrupt_safe) +{ + net_data_node *node; + net_data_node *next_node; + + if (! nd) + return B_BAD_VALUE; + + if (! g_datas_pool) + // Uh? From where come this net_data!?! + return B_ERROR; + + if (interrupt_safe) { + // We're called from a interrupt handler. Don't use any blocking calls (delete_pool_node() is!) + // We flag this net_data as to be free by the data purgatory thread + // Notify him that he have a new data purgatory member + // (notice the B_DO_NOT_RESCHEDULE usage, the only release_sem_etc() interrupt-safe mode) + nd->flags |= DATA_TO_FREE; + // release_sem_etc(g_data_purgatory_sync, 1, B_DO_NOT_RESCHEDULE); + return B_OK; + }; + + // Okay, we can free each data node in the node_list right now! + node = nd->node_list; + while (node) { + // okay, one net_data_node less referencing this data + node->ref_count--; + next_node = node->next; + + if (node->ref_count == 0) { + // it was the last net_data_node to reference this data, + // so free it now! + + // do we have to call the free_func() on this data? + if (node->free_func) { + // yes, please!!! + // call the right free_func on this data + if (node->free_cookie) + node->free_func(node->free_cookie, (void *) node->data); + else + node->free_func((void *) node->data); + }; + }; + // delete this net_data_node + g_memory_pool->delete_pool_node(g_datas_nodes_pool, node); + + node = next_node; + }; + + // Last, delete the net_data itself + return g_memory_pool->delete_pool_node(g_datas_pool, nd); +} + + +// -------------------------------------------------- +net_data * duplicate_data(net_data *nd) +{ + return NULL; // B_UNSUPPORTED; +} + + +// -------------------------------------------------- +net_data * clone_data(net_data *nd) +{ + return NULL; // B_UNSUPPORTED; +} + + +// -------------------------------------------------- +status_t add_data_free_node(net_data *nd, void *arg1, void *arg2, data_node_free_func freethis) +{ + net_data_node *node; + + if (! nd) + return B_BAD_VALUE; + + node = new_data_node(arg1, 0); // unknown data length + if (! node) + return B_ERROR; + + node->next_data = NULL; // not a data_list member, just a *guest star* node_list member + node->free_func = freethis; + node->free_cookie = arg2; + + // add to node_list (nodes order don't matter in this list :-) + node->next = nd->node_list; + nd->node_list = node; + + return B_OK; +} + + +// -------------------------------------------------- +status_t prepend_data(net_data *nd, const void *data, uint32 bytes, data_node_free_func freethis) +{ + net_data_node *node; + + if (! nd) + return B_BAD_VALUE; + + if (bytes < 1) + return B_BAD_VALUE; + + // create a new node to host the prepending data + node = new_data_node(data, bytes); + if (! node) + return B_ERROR; + + node->free_func = freethis; // can be NULL = don't free this chunk + node->free_cookie = NULL; + + // add this node to node_list (nodes order don't matter in this list, so we do it the easy way :-) + node->next = nd->node_list; + nd->node_list = node; + + // prepend this node to the data_list: + node->next_data = nd->data_list; + nd->data_list = node; + + nd->len += bytes; + + return B_OK; +} + + +// -------------------------------------------------- +status_t append_data(net_data *nd, const void *data, uint32 bytes, data_node_free_func freethis) +{ + net_data_node *node; + + if (! nd) + return B_BAD_VALUE; + + if (bytes < 1) + return B_BAD_VALUE; + + // create a new node to host the appending data + node = new_data_node(data, bytes); + if (! node) + return B_ERROR; + + node->free_func = freethis; // can be NULL = don't free this chunk + node->free_cookie = NULL; + + // add this node to node_list (nodes order don't matter in this list, so we do it the easy way :-) + node->next = nd->node_list; + nd->node_list = node; + + // Add this node to the end of data_list + node->next_data = NULL; + if (nd->data_list) { + net_data_node *tmp; + + tmp = nd->data_list; + while(tmp->next_data) // search the last net_data_node in list + tmp = tmp->next_data; + tmp->next_data = node; + } else + nd->data_list = node; + + nd->len += bytes; + + return B_OK; +} + + +// -------------------------------------------------- +status_t insert_data(net_data *nd, uint32 offset, const void *data, uint32 bytes, data_node_free_func freethis) +{ + net_data_node *previous_node; + net_data_node *next_node; + net_data_node *split_node; + net_data_node *new_node; + uint32 offset_in_node; + + if (! nd) + return B_BAD_VALUE; + + if (bytes < 1) + return B_BAD_VALUE; + + if (offset == 0) + return prepend_data(nd, data, bytes, freethis); + + next_node = find_data_node(nd, offset, &offset_in_node, &previous_node); + if (! next_node) + return B_BAD_VALUE; + + split_node = NULL; + new_node = NULL; + + if (offset_in_node) { + // we must split next_node data chunk in two parts :-( + uint8 *split; + + split = (uint8 *) next_node->data; + split += offset_in_node; + split_node = new_data_node(split, next_node->len - offset_in_node); + if (! split_node) + goto error1; + + next_node->len = offset_in_node; // cut the data len of original node + + // as split_node data comes from 'next_node', we don't + // ask to free this node data, as it would be by the next_node node + split_node->free_func = NULL; + + // add the split_node to node_list (nodes order don't matter in this list, so we do it the easy way :-) + split_node->next = nd->node_list; + nd->node_list = split_node; + + // insert the split_node between the two part of the *splitted* next_node + split_node->next_data = next_node->next_data; + next_node->next_data = split_node; + + previous_node = next_node; + next_node = split_node; + }; + + // create a new node to host inserted data + new_node = new_data_node(data, bytes); + if (! new_node) + goto error2; + + new_node->free_func = freethis; // can be NULL = don't free this chunk + new_node->free_cookie = NULL; + + // add the new_node to node_list (nodes order don't matter in this list, so we do it the easy way :-) + new_node->next = nd->node_list; + nd->node_list = new_node; + + // Insert this new_node between previous and next node + previous_node->next_data = new_node; + new_node->next_data = next_node; + + nd->len += bytes; + + return B_OK; + +error2: + g_memory_pool->delete_pool_node(g_datas_nodes_pool, new_node); +error1: + g_memory_pool->delete_pool_node(g_datas_nodes_pool, split_node); + return B_ERROR; +} + + +// -------------------------------------------------- +status_t remove_data(net_data *nd, uint32 offset, uint32 bytes) +{ +#if 0 + // TODO!!! + + net_data_node * ndn; + net_data_node * start_node; + net_data_node * end_node; + uint32 start_node_offset; + uint32 end_node_offset; + net_data_node * start_split_node; + net_data_node * end_split_node; + uint8 * data; + + if (bytes < 1) + return B_BAD_VALUE; + + /* + Possibles cases: + [XXXXXXX|++++++].... Triming at start of node (start and/or end nodes) + [+++|XXXXXX|+++].... Triming in the middle of *one* node + [+++++|XXXXXXXX].... Triming at the end of start node + ...[XXXXXXXXXXXXXX].... Triming one full node + */ + + start_node = find_data_node(nd, offset, &start_node_offset, NULL); + if (! start_node) + return B_BAD_VALUE; + + end_node = find_data_node(nd, offset + bytes, &end_node_offset, NULL); + if (! end_node) + return B_BAD_VALUE; + + if ( (start_node == end_node) && + (start_node_offset > 0) && (end_node_offset != end_node->len) ) + // need to split one node into two parts, left & right, to be able to + // trim data in the middle: + // [++++|XXXXXX|+++++++] + return B_OK; + }; + + // if the trimmed part is in one node, we know now that it's: + // |++++++|XXXXXXXXXXXX|, or: + // |XXXXXXXXX|+++++++++| + + if (start_node_offset) + start_node->len = start_node_offset; + + if (end_node_offset) { + data = (uint8 *) end_node->data; + data += end_node_offset; + end_node->data = data; + end_node->len -= end_node_offset; + }; + + // start node must be split + split = (uint8 *) start_node->data; + split += start_node_offset; + start_split_node = new_data_node(split, start_node->len - start_node_offset); + if (! start_split_node) + goto error1; + }; + + if (end_node_offset) { + // end node must be split + split = (uint8 *) end_node->data; + split += end_node_offset; + end_split_node = new_data_node(split, end_node->len - end_node_offset); + if (! end_split_node) + goto error2; + }; + + if (start_split_node) { + }; + + ndn = start_node->next_data; + while (ndn->next_data != end_node) { + if (ndn->next_data == end_node) + break; + + ndn = ndn->next_data; + }; + != end_node + + if (end_split_node) { + }; + + + // split start_node data chunk in two parts + start_node->len = start_node_offset; + + start_split_node->free_func = NULL; + + }; + + + + ndn->len = offset_in_node; + + split_node->free_func = NULL; // as split_node data comes from 'ndn', we don't + // free this node data but only 'ndn' one... + + // create a new node to host inserted data + new_node = new_data_node(data, bytes); + if (! new_node) + goto error2; + + new_node->free_func = freethis; // can be NULL = don't free this chunk + new_node->free_cookie = NULL; + + // add these nodes to node_list (nodes order don't matter in this list, so we do it the easy way :-) + split_node->next = nd->node_list; + new_node->next = split_node; + nd->node_list = new_node; + + // Insert this new_node this node to the end of data_list + split_node->next_data = ndn->next_data; + ndn->next_data = new_node; + new_node->next_data = split_node; + + nd->len -= bytes; + + return B_OK; + +error2: + g_memory_pool->delete_pool_node(g_datas_nodes_pool, end_split_node); +error1: + if (start_split_node) + g_memory_pool->delete_pool_node(g_datas_nodes_pool, start_split_node); +#endif + return B_ERROR; +} + + +// -------------------------------------------------- +uint32 copy_from_data(net_data *data, uint32 offset, void *copyinto, uint32 bytes) +{ + net_data_node *node; + uint32 offset_in_node; + uint32 len; + uint32 chunk_len; + uint8 *from; + uint8 *to; + + to = (uint8 *) copyinto; + len = 0; + + node = find_data_node(data, offset, &offset_in_node, NULL); + while (node) { + from = (uint8 *) node->data; + from += offset_in_node; + chunk_len = min((node->len - offset_in_node), (bytes - len)); + + memcpy(to, from, chunk_len); + + len += chunk_len; + if (len >= bytes) + break; + + to += chunk_len; + offset_in_node = 0; // only the first node + + node = node->next_data; // next node + }; + + return len; +} + +// #pragma mark [data(s) queues functions] + +// -------------------------------------------------- +net_data_queue * new_data_queue(size_t max_bytes) +{ + net_data_queue *queue; + + if (! g_datas_queues_pool) + g_datas_queues_pool = g_memory_pool->new_pool(sizeof(net_data_queue), DATA_QUEUES_PER_POOL); + + if (! g_datas_queues_pool) + return NULL; + + queue = (net_data_queue *) g_memory_pool->new_pool_node(g_datas_queues_pool); + if (! queue) + return NULL; + + create_benaphore(&queue->lock, "net_data_queue lock"); + queue->sync = create_sem(0, "net_data_queue sem"); + + queue->max_bytes = max_bytes; + queue->current_bytes = 0; + + queue->head = queue->tail = NULL; + + return queue; +} + + +// -------------------------------------------------- +status_t delete_data_queue(net_data_queue *queue) +{ + status_t status; + + if (! queue) + return B_BAD_VALUE; + + if (! g_datas_queues_pool) + // Uh? From where come this queue then!?! + return B_ERROR; + + // free the net_data's (sill) in this queue + status = empty_data_queue(queue); + if (status != B_OK) + return status; + + delete_sem(queue->sync); + delete_benaphore(&queue->lock); + + return g_memory_pool->delete_pool_node(g_datas_queues_pool, queue); +} + + +// -------------------------------------------------- +status_t empty_data_queue(net_data_queue *queue) +{ + net_data *data; + net_data *tmp = NULL; + + if (! queue) + return B_BAD_VALUE; + + lock_benaphore(&queue->lock); + + data = queue->head; + while (data) { + tmp = data; + data = data->next; + delete_data(data, true); + }; + + queue->head = NULL; + queue->tail = NULL; + + delete_sem(queue->sync); + queue->sync = create_sem(0, "net_data_queue sem"); + + unlock_benaphore(&queue->lock); + return B_OK; +} + + +// -------------------------------------------------- +status_t enqueue_data(net_data_queue *queue, net_data *data) +{ + if (! queue) + return B_BAD_VALUE; + + if (! data) + return B_BAD_VALUE; + + data->next = NULL; + +/* + if (queue->current_bytes + data->len > queue->max_bytes) + // what to do? dump some enqueued data(s) to free space? drop the new net_data? + // TODO: dequeue enought net_data(s) to free space to queue this one... + NULL; +*/ + lock_benaphore(&queue->lock); + + if (! queue->head) + queue->head = data; + if (queue->tail) + queue->tail->next = data; + queue->tail = data; + + queue->current_bytes += data->len; + + unlock_benaphore(&queue->lock); + + return release_sem_etc(queue->sync, 1, B_DO_NOT_RESCHEDULE); +} + + +// -------------------------------------------------- +size_t dequeue_data(net_data_queue *queue, net_data **data, bigtime_t timeout, bool peek) +{ + status_t status; + net_data *head_data; + + if (! queue) + return B_BAD_VALUE; + + status = acquire_sem_etc(queue->sync, 1, B_RELATIVE_TIMEOUT, timeout); + if (status != B_OK) + return status; + + lock_benaphore(&queue->lock); + + head_data = queue->head; + + if (! peek) { + // detach the head net_data from this fifo + queue->head = head_data->next; + if (queue->tail == head_data) + queue->tail = queue->head; + + queue->current_bytes -= head_data->len; + + head_data->next = NULL; // we never know :-) + }; + + unlock_benaphore(&queue->lock); + + *data = head_data; + + return head_data->len; +} + + +// #pragma mark [Helper functions] + +// -------------------------------------------------- +static net_data_node * new_data_node(const void *data, uint32 len) +{ + net_data_node * node; + + if (! g_datas_nodes_pool) + g_datas_nodes_pool = g_memory_pool->new_pool(sizeof(net_data_node), DATA_NODES_PER_POOL); + + if (! g_datas_nodes_pool) + return NULL; + + node = (net_data_node *) g_memory_pool->new_pool_node(g_datas_nodes_pool); + + node->data = data; + node->len = len; + + node->ref_count = 1; + + node->free_cookie = NULL; + node->free_func = NULL; + + node->next = NULL; + node->next_data = NULL; + + return node; +} + + +// -------------------------------------------------- +static net_data_node * find_data_node(net_data *data, uint32 offset, uint32 *offset_in_node, net_data_node **previous_node) +{ + net_data_node *previous; + net_data_node *node; + uint32 len; + + if (! data) + return NULL; + + if (data->len <= offset) + // this net_data don't hold enough data to reach this offset! + return NULL; + + len = 0; + node = data->data_list; + previous = NULL; + while (node) { + len += node->len; + + if(offset < len) + break; + + previous = node; + node = node->next_data; + }; + + if (offset_in_node) + *offset_in_node = node->len - (len - offset); + if (previous_node) + *previous_node = previous; + + return node; +} + + +// -------------------------------------------------- +void dump_memory + ( + const char * prefix, + const void * data, + uint32 len + ) +{ + uint32 i,j; + char text[96]; // only 3*16 + 16 max by line needed + uint8 * byte; + char * ptr; + + byte = (uint8 *) data; + + for ( i = 0; i < len; i += 16 ) + { + ptr = text; + + for ( j = i; j < i+16 ; j++ ) + { + if ( j < len ) + sprintf(ptr, "%02x ",byte[j]); + else + sprintf(ptr, " "); + ptr += 3; + }; + + for (j = i; j < len && j < i+16;j++) + { + if ( byte[j] >= ' ' && byte[j] < 0x7f ) + *ptr = byte[j]; + else + *ptr = '.'; + + ptr++; + }; + *ptr = '\n'; + ptr++; + *ptr = '\0'; + + DPRINTF(prefix); + DPRINTF(text); + + // next line + }; +} + + +// -------------------------------------------------- +void dump_data(net_data *data) +{ + net_data_node * node; + + if (! data) + return; + + DPRINTF("---- net_data %p: total len %ld\n", data, data->len); + + DPRINTF("data_list:\n"); + node = data->data_list; + while (node) { + DPRINTF(" * node %p: data %p, len %ld\n", node, node->data, node->len); + dump_memory(" ", node->data, node->len); + DPRINTF(" next: %p\n", node->next_data); + + node = node->next_data; + }; + + DPRINTF("node_list:\n"); + node = data->node_list; + while (node) { + DPRINTF(" * node %p: data %p, len %ld, ref_count %ld\n", node, node->data, node->len, node->ref_count); + if (node->free_func) + DPRINTF(" free_func: %p, free_cookie %p\n", node->free_func, node->free_cookie); + DPRINTF(" next: %p\n", node->next); + + node = node->next; + }; + +} + +// #pragma mark [data Purgatory functions] + +// -------------------------------------------------- +static int32 datas_purgatory_thread(void *data) +{ + while (true) { + if (acquire_sem(g_datas_purgatory_sync) != B_OK) + break; + + // okay, time to cleanup some net_data(s) + g_memory_pool->for_each_pool_node(g_datas_pool, data_death, NULL); + }; + return 0; +} + + +// -------------------------------------------------- +static status_t data_death(memory_pool * pool, void * node, void * cookie) +{ + net_data *data; + + data = (net_data *) node; + if (data->flags & DATA_TO_FREE) + delete_data(data, false); + + return 0; // B_OK; +} + + +// -------------------------------------------------- +static status_t data_queue_death(memory_pool * pool, void * node, void * cookie) +{ + return delete_data_queue((net_data_queue *) node); +} + diff --git a/src/tests/kits/net/new_stack/stack/data.h b/src/tests/kits/net/new_stack/stack/data.h new file mode 100644 index 0000000000..375fee243a --- /dev/null +++ b/src/tests/kits/net/new_stack/stack/data.h @@ -0,0 +1,49 @@ +/* data.h + * private definitions for network data chunks support + */ + +#ifndef OBOS_NET_STACK_DATA_H +#define OBOS_NET_STACK_DATA_H + +#include + +#include "net_stack.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern status_t start_data_service(); +extern status_t stop_data_service(); + +// Network data chunk(s) +extern net_data * new_data(void); +extern status_t delete_data(net_data *nd, bool interrupt_safe); + +extern net_data * duplicate_data(net_data *from); +extern net_data * clone_data(net_data *from); + +extern status_t prepend_data(net_data *nd, const void *data, uint32 bytes, data_node_free_func freethis); +extern status_t append_data(net_data *nd, const void *data, uint32 bytes, data_node_free_func freethis); +extern status_t insert_data(net_data *nd, uint32 offset, const void *data, uint32 bytes, data_node_free_func freethis); +extern status_t remove_data(net_data *nd, uint32 offset, uint32 bytes); + +extern status_t add_data_free_node(net_data *nd, void *arg1, void *arg2, data_node_free_func freethis); + +extern uint32 copy_from_data(net_data *nd, uint32 offset, void * copyinto, uint32 bytes); + +extern void dump_data(net_data *data); + +// Network data(s) queue(s) +extern net_data_queue * new_data_queue(size_t max_bytes); +extern status_t delete_data_queue(net_data_queue *queue); + +extern status_t empty_data_queue(net_data_queue *queue); +extern status_t enqueue_data(net_data_queue *queue, net_data *data); +extern size_t dequeue_data(net_data_queue *queue, net_data **data, bigtime_t timeout, bool peek); + +#ifdef __cplusplus +} +#endif + +#endif // OBOS_NET_STACK_DATA_H diff --git a/src/tests/kits/net/new_stack/stack/datalink.c b/src/tests/kits/net/new_stack/stack/datalink.c new file mode 100644 index 0000000000..78b5297987 --- /dev/null +++ b/src/tests/kits/net/new_stack/stack/datalink.c @@ -0,0 +1,231 @@ +/* core.c */ + +/* This the heart of network stack + */ + +#include +#include +#include +// #include + +#include +#include + +#include "datalink.h" +#include "data.h" + +/* Defines we need */ +#define NETWORK_INTERFACES "network/interfaces" +#define NETWORK_PROTOCOLS "network/protocols" + +struct interfaces_list { + ifnet_t *first; + sem_id lock; +}; + +static struct interfaces_list g_interfaces; + +status_t enable_interface(ifnet_t *iface, bool enable); +status_t interface_reader(void *args); + + +// #pragma mark [Start/Stop functions] + + +// -------------------------------------------------- +status_t start_datalink_layer() +{ + void *module_list; + ifnet_t *iface; + + g_interfaces.first = NULL; + + g_interfaces.lock = create_sem(1, "net_interfaces list lock"); + if (g_interfaces.lock < B_OK) + return B_ERROR; + +#ifdef _KERNEL_MODE + set_sem_owner(g_interfaces.lock, B_SYSTEM_TEAM); +#endif + + // Load all network/interfaces/* modules and let them + // register any interface they may support by calling init() + module_list = open_module_list(NET_INTERFACE_MODULE_ROOT); + if (module_list) { + size_t sz; + char module_name[256]; + struct net_interface_module_info *nimi; + + sz = sizeof(module_name); + while(read_next_module_name(module_list, module_name, &sz) == B_OK) { + if (strlen(module_name) && get_module(module_name, (module_info **) &nimi) == B_OK) { + printf("datalink: initing %s interface module...\n", module_name); + // allow this module to register one or more interfaces + nimi->init(NULL); + }; + sz = sizeof(module_name); + }; + close_module_list(module_list); + }; + + acquire_sem(g_interfaces.lock); + iface = g_interfaces.first; + while (iface) { + enable_interface(iface, true); + iface = iface->if_next; + }; + release_sem(g_interfaces.lock); + + puts("net datalink layer started."); + return B_OK; +} + +// -------------------------------------------------- +status_t stop_datalink_layer() +{ + ifnet_t *iface, *next; + + delete_sem(g_interfaces.lock); + g_interfaces.lock = -1; + + // free the remaining interfaces entries + iface = g_interfaces.first; + while (iface) { + printf("datalink: uninit interface %s\n", iface->if_name); + // down the interface if currently up + enable_interface(iface, false); + + iface->module->uninit(iface); + iface = iface->if_next; + }; + + // unload all interface modules... and free iface structs + iface = g_interfaces.first; + while (iface) { + printf("datalink: unloading %s interface module\n", iface->module->info.name); + next = iface->if_next; + put_module(iface->module->info.name); + free(iface); + iface = next; + }; + + puts("net datalink layer stopped."); + + return B_OK; +} + + + +// #pragma mark [Public functions] + +// -------------------------------------------------- +status_t register_interface(ifnet_t *iface) +{ + status_t status; + + if (!iface) + return B_ERROR; + + iface->if_reader_thread = -1; + + status = acquire_sem(g_interfaces.lock); + if (status != B_OK) + return status; + + iface->if_next = g_interfaces.first; + g_interfaces.first = iface; + + release_sem(g_interfaces.lock); + + printf("datalink: register_interface(%s)\n", iface->if_name); + return B_OK; +} + +// -------------------------------------------------- +status_t unregister_interface(ifnet_t *iface) +{ + status_t status; + + if (!iface) + return B_ERROR; + + status = acquire_sem(g_interfaces.lock); + if (status != B_OK) + return status; + + if (g_interfaces.first == iface) + g_interfaces.first = iface->if_next; + else { + ifnet_t * p = g_interfaces.first; + while (p && p->if_next != iface) + p = p->if_next; + + if (!p) + printf("datalink: unregister_interface(): %p iface not found in list!\n", iface); + else + p->if_next = iface->if_next; + }; + + release_sem(g_interfaces.lock); + + printf("datalink: unregister_interface(%s)\n", iface->if_name); + return iface->module->uninit(iface); +} + + +// ---------------------------------------------------- +status_t enable_interface(ifnet_t *iface, bool enable) +{ + if (enable) { + thread_id tid; + + if (iface->if_flags & IFF_UP) + // already up + return B_OK; + + iface->module->up(iface); + + tid = spawn_kernel_thread(interface_reader, iface->if_name, + B_NORMAL_PRIORITY, iface); + if (tid < 0) { + printf("datalink: enable_interface(%s): failed to start reader thread -> %d [%s]\n", + iface->if_name, (int) tid, strerror(tid)); + return tid; + }; + + iface->if_reader_thread = tid; + iface->if_flags |= IFF_UP; + printf("datalink: starting interface %s...\n", iface->if_name); + return resume_thread(tid); + } else { + if (iface->if_reader_thread) { + kill_thread(iface->if_reader_thread); + iface->if_reader_thread = -1; + }; + iface->if_flags &= ~IFF_UP; + return iface->module->down(iface); + }; +} + + +// ---------------------------------------------------- +status_t interface_reader(void *args) +{ + ifnet_t *iface = args; + net_data *nd; + status_t status; + + if (!iface || iface->module == NULL) + return B_ERROR; + + while(iface->if_flags & IFF_UP) { + status = iface->module->receive_data(iface, &nd); + if (status < B_OK || nd == NULL) + continue; + dump_data(nd); + delete_data(nd, false); + }; + + return B_OK; +} + diff --git a/src/tests/kits/net/new_stack/stack/datalink.h b/src/tests/kits/net/new_stack/stack/datalink.h new file mode 100644 index 0000000000..fc8134f756 --- /dev/null +++ b/src/tests/kits/net/new_stack/stack/datalink.h @@ -0,0 +1,24 @@ +/* datalink.h + * private definitions for network datalink layer + */ + +#ifndef OBOS_NET_STACK_DATALINK_H +#define OBOS_NET_STACK_DATALINK_H + +#include "net_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern status_t start_datalink_layer(); +extern status_t stop_datalink_layer(); + +extern status_t register_interface(ifnet_t *ifnet); +extern status_t unregister_interface(ifnet_t *ifnet); + +#ifdef __cplusplus +} +#endif + +#endif /* OBOS_NET_STACK_DATALINK_H */ diff --git a/src/tests/kits/net/new_stack/stack/stack.c b/src/tests/kits/net/new_stack/stack/stack.c new file mode 100644 index 0000000000..506b339a3b --- /dev/null +++ b/src/tests/kits/net/new_stack/stack/stack.c @@ -0,0 +1,150 @@ +/* stack.c */ + +/* This the heart of network stack + */ + +#include + +#include +#include + +#include "net_stack.h" +#include "memory_pool.h" + +#include "datalink.h" +#include "data.h" +#include "timer.h" + +/* Defines we need */ +#define NETWORK_INTERFACES "network/interfaces" +#define NETWORK_PROTOCOLS "network/protocols" +#define PPP_DEVICES "ppp/devices" + +struct memory_pool_module_info *g_memory_pool = NULL; +static bool g_started = false; + +status_t std_ops(int32 op, ...); + +static status_t start(void); +static status_t stop(void); + + +static status_t start(void) +{ + if (g_started) + return B_OK; + + puts("stack: starting..."); + + start_data_service(); + start_timers_service(); + + start_datalink_layer(); + + puts("stack: started."); + g_started = true; + + return B_OK; +} + +static status_t stop(void) +{ + puts("stack: stopping..."); + + stop_datalink_layer(); + + stop_timers_service(); + stop_data_service(); + + + puts("stack: stopped."); + g_started = false; + + return 0; +} + +// #pragma mark - + +struct net_stack_module_info nsmi = { + { + NET_STACK_MODULE_NAME, + 0, // B_KEEP_LOADED, + std_ops + }, + + start, + stop, + + // Data-Link layer + register_interface, + unregister_interface, + + // net_data support + new_data, + delete_data, + duplicate_data, + clone_data, + + prepend_data, + append_data, + insert_data, + remove_data, + + add_data_free_node, + + copy_from_data, + + dump_data, + + // net_data_queue support + new_data_queue, + delete_data_queue, + empty_data_queue, + enqueue_data, + dequeue_data, + + // Timers support + new_net_timer, + delete_net_timer, + start_net_timer, + cancel_net_timer, + get_net_timer_appointment +}; + +status_t std_ops(int32 op, ...) +{ + status_t status; + + switch(op) { + case B_MODULE_INIT: + printf("stack: B_MODULE_INIT\n"); + load_driver_symbols("stack"); + status = get_module(MEMORY_POOL_MODULE_NAME, (module_info **) &g_memory_pool); + if (status != B_OK) + return status; + + // Re-publish memory_pool module api thru our + nsmi.new_pool = g_memory_pool->new_pool; + nsmi.delete_pool = g_memory_pool->delete_pool; + nsmi.new_pool_node = g_memory_pool->new_pool_node; + nsmi.delete_pool_node = g_memory_pool->delete_pool_node; + nsmi.for_each_pool_node = g_memory_pool->for_each_pool_node; + break; + + case B_MODULE_UNINIT: + // the stack is keeping loaded, so don't stop it + printf("stack: B_MODULE_UNINIT\n"); + put_module(MEMORY_POOL_MODULE_NAME); + break; + + default: + return B_ERROR; + } + return B_OK; +} + +_EXPORT module_info *modules[] = { + (module_info *) &nsmi, // net_stack_module_info + NULL +}; + diff --git a/src/tests/kits/net/new_stack/stack/stack_module_x86.proj b/src/tests/kits/net/new_stack/stack/stack_module_x86.proj new file mode 100644 index 0000000000000000000000000000000000000000..a7cf65b5d0fbbd2de44d95ceed87c24bcaa66512 GIT binary patch literal 13086 zcmeHNUu+yl8K2Ez9+p{Z7Ze(Pu7gL3J6_hi;Q73gZ97b~ z#(tVmE>>OVJKXTU-@vQgT1|K;%E(BT{Ljti@(Frj_0G&rYihUW8pUPPE<4Mw|4j*c zK^sdZ4a+nZi&dYo-{lzl^H}f1bYW^RHS6leB6puK`SPr@$n6jCa{B@6Vn-yfd&OO0 zyHAhL%nYh7pYv`9X&36beGyMFr)fpzGiW?JsqQK*`MzU&^pcF`*jIFU)w)ouaL?oR zJa>!Dlk5TC)8BnKWOu4JSoDe|l%nFe<<$YHac9^A$gP&lZJA zjBL60UZLQYutmagOV}b~yIaGS$e5dM3sy?mX>HAl#fD4EYZp5B%Ju}{+hGg-dD@n; zEw=a-Ydef-*_H%jx-MIN|#5}2F)j3z`b{B&m1To6-p|tj@_QmaS^4S%f(P_KX*G4>`ABm$ z8@Bnjv^5=tMXU#5(WGwAFX{6P5t7llt1$9__GZPT6?%xg1DQs`KdP&0eO z+DK5c-y_*mVvaHgnY$v_v}zKPi6qp!BMDW_l@SRdmeZ|?Q>WGo?;2BfS{(#B2(%jl za*G*?!aJ6v?M{Su_03LhDaYafaMhO52?g72!F2rXAn<7j$PJ}$#>?X)BvWU&m)Cur zL=-u80&yT^^9~~pH1M@B&QyUU4%ASJitS6+gbWjEWMIQ=Df(0#lSMhMx+IBgu~1qJ zqT)i=MmWWW<*X?gta|fTIETbt#NBS!2px5>;^{ckrK;1p!c=nxlklJf zsCV_QklOD(wlAQ2bK!D~Xm_6M3ZMOTcQ}gn;dh6_e|(e<)9(+5v>>R@UKj|ddgF{; z*^{ot{QEKf;%~ddwLIq*Bic`)hxG1vE*a)QBb&rLQwM|e-gNC>k938jppzcDlPK8r>8Yuvsv2=}apss@cWz1jsPhUu5#22D}2xB4iJ@#O~pcAi< zC3(q5_xGm{289LPeeQ*1s6Gm0-&7g-Dfm-JV)v$NZ()p1-Vy!F=wEog(SHj4FFn{L z#$${~mfGyqp+Hph<7Y$PZV6A)7h_9$&qqQ8A6asA&%6W3=!&!^H| zZ_*YB8R~y+FmavuX`FU}SVm!q>k$2<^y7HtHbZjp-cy4v1$GRYSe+SN(zb?SFe9pgz$_KH4qg-IM*L zvTT(>z7oZ(OUmUpc`^ui63i|QC z9t((v+BEtR#!e#tM!yVn%}Ang4PBb6?WX^-IR?Yi(4MC!05_)eSF{dhG_p#B|G zzJe+ajR$Bv{Lc4Ylim!VyI&j@Sjl+Kl56mc;gmU(a2>|5DAM{Kbdn;mIbyJaDDxEsA)NV)1G3o%P{vai7+Kp++i54pkig*BS!)Zg9;uQ=|7V%2f12Im*oaMD(t zvbm3q3tQiuZtwQ8yOs kW}d)JTC5U+cNt6k5iSg(BBa?Y1nd5;qqSbg;3hfvKR;Wo{{R30 literal 0 HcmV?d00001 diff --git a/src/tests/kits/net/new_stack/stack/timer.c b/src/tests/kits/net/new_stack/stack/timer.c new file mode 100644 index 0000000000..9cadf281c6 --- /dev/null +++ b/src/tests/kits/net/new_stack/stack/timer.c @@ -0,0 +1,276 @@ +/* timer.c - a small and more or less inaccurate timer for net modules. +** The registered hooks will be called in the thread of the timer. +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** +** This file may be used under the terms of the OpenBeOS License. +*/ + +#include +#include +#include +#include +#include + +#include "net_stack.h" + +struct net_timer { + struct net_timer *next; + net_timer_func func; + void *func_cookie; + bigtime_t period; + bigtime_t until; + bool pending; +}; + +struct timers_queue { + struct net_timer *first; + sem_id lock; + sem_id wait; + int32 counter; + volatile int32 in_use; +}; + +static struct timers_queue g_timers; +static thread_id g_timers_thread = -1; + +static int32 timers_thread(void *data); + +// #pragma mark [Start/Stop Service functions] + + +// -------------------------------------------------- +status_t start_timers_service() +{ + memset(&g_timers, 0, sizeof(g_timers)); + + g_timers.lock = create_sem(1, "net_timers lock"); + if (g_timers.lock < B_OK) + return B_ERROR; + + g_timers.wait = create_sem(0,"net_timers wait"); + if (g_timers.wait < B_OK) + return B_ERROR; + +#ifdef _KERNEL_MODE + set_sem_owner(g_timers.lock, B_SYSTEM_TEAM); + set_sem_owner(g_timers.wait, B_SYSTEM_TEAM); +#endif + + g_timers_thread = spawn_kernel_thread(timers_thread, "net timers runner", B_URGENT_DISPLAY_PRIORITY, &g_timers); + if (g_timers_thread < B_OK) + return g_timers_thread; + + puts("net timers service started."); + + return resume_thread(g_timers_thread); +} + + +// -------------------------------------------------- +status_t stop_timers_service() +{ + net_timer *nt, *next; + int32 tries = 20; + status_t status; + + delete_sem(g_timers.wait); + delete_sem(g_timers.lock); + g_timers.wait = -1; + g_timers.lock = -1; + + wait_for_thread(g_timers_thread, &status); + + // make sure the structure isn't used anymore + while (g_timers.in_use != 0 && tries-- > 0) + snooze(1000); + + // free the remaining timer entries + for (nt = g_timers.first; nt; nt = next) { + next = nt->next; + free(nt); + } + + puts("net timers service stopped."); + + return B_OK; +} + + +// #pragma mark [Public functions] + +// -------------------------------------------------- +net_timer * new_net_timer(void) +{ + return NULL; +} + + +// -------------------------------------------------- +status_t delete_net_timer(net_timer *nt) +{ + return B_ERROR; +} + + +// -------------------------------------------------- +status_t start_net_timer(net_timer *nt, net_timer_func func, void *cookie, bigtime_t period) +{ + return B_ERROR; +} + + +// -------------------------------------------------- +status_t cancel_net_timer(net_timer *nt) +{ + return B_ERROR; +} + + +// -------------------------------------------------- +status_t get_net_timer_appointment(net_timer *nt, bigtime_t *period, bigtime_t *when) +{ + return 0; +} + +// #pragma mark - + +// -------------------------------------------------- +static int32 timers_thread(void *data) +{ + struct timers_queue *timers = (struct timers_queue *) data; + status_t status = B_OK; + + do { + bigtime_t timeout = B_INFINITE_TIMEOUT; + net_timer *nt; + + // get access to the info structure + if (status == B_TIMED_OUT || status == B_OK) { + if (acquire_sem(timers->lock) == B_OK) { + for (nt = timers->first; nt; nt = nt->next) { + // new entry? + if (nt->until == -1) + nt->until = system_time() + nt->period; + + // execute timer? + if (nt->until < system_time()) { + nt->until += nt->period; + nt->func(nt, nt->func_cookie); + } + + // calculate new timeout + if (nt->until < timeout) + timeout = nt->until; + } + + release_sem(timers->lock); + } + } + + status = acquire_sem_etc(timers->wait, 1, B_ABSOLUTE_TIMEOUT, timeout); + // the wait sem normally can't be acquired, so we + // have to look at the status value the call returns: + // + // B_OK - someone wanted to notify us + // B_TIMED_OUT - look for timers to be executed + // B_BAD_SEM_ID - our sem got deleted + + } while (status != B_BAD_SEM_ID); + + return 0; +} + +#if 0 + + + +net_timer_id +net_add_timer(net_timer_hook hook,void *data,bigtime_t interval) +{ + struct timer_entry *te; + status_t status; + + if (interval < 100) + return B_BAD_VALUE; + + atomic_add(&gTimerInfo.ti_inUse,1); + + // get access to the timer info structure + status = acquire_sem(gTimerInfo.ti_lock); + if (status < B_OK) { + atomic_add(&gTimerInfo.ti_inUse,-1); + return status; + } + + te = (struct timer_entry *)malloc(sizeof(struct timer_entry)); + if (te == NULL) { + atomic_add(&gTimerInfo.ti_inUse,-1); + release_sem(gTimerInfo.ti_lock); + return B_NO_MEMORY; + } + + te->te_hook = hook; + te->te_data = data; + te->te_interval = interval; + te->te_until = -1; + te->te_id = ++gTimerInfo.ti_counter; + + // add the new entry + te->te_next = gTimerInfo.ti_first; + gTimerInfo.ti_first = te; + + atomic_add(&gTimerInfo.ti_inUse,-1); + release_sem(gTimerInfo.ti_lock); + + // notify timer about the change + release_sem(gTimerInfo.ti_wait); + + return te->te_id; +} + + +status_t +net_remove_timer(net_timer_id id) +{ + struct timer_entry *te,*last; + status_t status; + + if (id <= B_OK) + return B_BAD_VALUE; + + atomic_add(&gTimerInfo.ti_inUse,1); + + // get access to the timer info structure + status = acquire_sem(gTimerInfo.ti_lock); + if (status < B_OK) { + atomic_add(&gTimerInfo.ti_inUse,-1); + return status; + } + + // search the list for the right timer + + // little hack that relies on ti_first being on the same position + // in the structure as te_next + last = (struct timer_entry *)&gTimerInfo; + for (te = gTimerInfo.ti_first;te;te = te->te_next) { + if (te->te_id == id) { + last->te_next = te->te_next; + free(te); + break; + } + last = te; + } + atomic_add(&gTimerInfo.ti_inUse,-1); + release_sem(gTimerInfo.ti_lock); + + if (te == NULL) + return B_ENTRY_NOT_FOUND; + + // notify timer about the change + release_sem(gTimerInfo.ti_wait); + + return B_OK; +} + +#endif \ No newline at end of file diff --git a/src/tests/kits/net/new_stack/stack/timer.h b/src/tests/kits/net/new_stack/stack/timer.h new file mode 100644 index 0000000000..eae97c0e47 --- /dev/null +++ b/src/tests/kits/net/new_stack/stack/timer.h @@ -0,0 +1,37 @@ +/* timer.h + * private definitions for network timers support + */ + +#ifndef OBOS_NET_STACK_TIMER_H +#define OBOS_NET_STACK_TIMER_H + +/* timer.h - a small and more or less inaccurate timer for net modules. +** The registered hooks will be called in the thread of the timer. +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** +** This file may be used under the terms of the OpenBeOS License. +*/ + +#include + +#include "net_stack.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern status_t start_timers_service(); +extern status_t stop_timers_service(); + +extern net_timer * new_net_timer(void); +extern status_t delete_net_timer(net_timer *nt); +extern status_t start_net_timer(net_timer *nt, net_timer_func hook, void *cookie, bigtime_t period); +extern status_t cancel_net_timer(net_timer *nt); +extern status_t get_net_timer_appointment(net_timer *nt, bigtime_t *period, bigtime_t *when); + +#ifdef __cplusplus +} +#endif + +#endif /* OBOS_NET_STACK_TIMER_H */ diff --git a/src/tests/kits/net/new_stack/userland_ipc.c b/src/tests/kits/net/new_stack/userland_ipc.c new file mode 100644 index 0000000000..2253a0ffec --- /dev/null +++ b/src/tests/kits/net/new_stack/userland_ipc.c @@ -0,0 +1,554 @@ +/* userland_ipc - Communication between the network driver +** and the userland stack. +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include "userland_ipc.h" + +#include "sys/socket.h" +#include "net_misc.h" +#include "core_module.h" +#include "net_module.h" +#include "sys/sockio.h" + +#include +#include +#include + + +extern struct core_module_info *core; + +// installs a main() +//#define COMMUNICATION_TEST + +#define NUM_COMMANDS 32 +#define CONNECTION_BUFFER_SIZE (65536 + 4096 - CONNECTION_COMMAND_SIZE) + +#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1)) + +struct socket; /* forward declaration */ + +typedef struct { + port_id localPort,port; + area_id area; + struct socket * socket; + + uint8 *buffer; + net_command *commands; + sem_id commandSemaphore; + + int32 openFlags; + + thread_id runner; + + // for socket select events support + port_id socket_event_port; + void * notify_cookie; +} connection_cookie; + + +port_id gStackPort = -1; +thread_id gConnectionOpener = -1; + +// prototypes +static int32 connection_runner(void *_cookie); +static status_t init_connection(net_connection *connection, connection_cookie **_cookie); +static void shutdown_connection(connection_cookie *cookie); + + +static void +delete_cloned_areas(net_area_info *area) +{ + int32 i; + for (i = 0;i < MAX_NET_AREAS;i++) { + if (area[i].id == 0) + continue; + + delete_area(area[i].id); + } +} + + +static status_t +clone_command_areas(net_area_info *localArea,net_command *command) +{ + int32 i; + + memset(localArea,0,sizeof(net_area_info) * MAX_NET_AREAS); + + for (i = 0;i < MAX_NET_AREAS;i++) { + if (command->area[i].id <= 0) + continue; + + localArea[i].id = clone_area("net connection",(void **)&localArea[i].offset,B_ANY_ADDRESS, + B_READ_AREA | B_WRITE_AREA,command->area[i].id); + if (localArea[i].id < B_OK) + return localArea[i].id; + } + return B_OK; +} + + +static uint8 * +convert_address(net_area_info *fromArea,net_area_info *toArea,uint8 *data) +{ + if (data == NULL) + return NULL; + + if (data < fromArea->offset) { + printf("could not translate address: %p\n",data); + return data; + } + + return data - fromArea->offset + toArea->offset; +} + + +static inline void * +convert_to_local(net_area_info *foreignArea,net_area_info *localArea,void *data) +{ + return convert_address(foreignArea,localArea,data); +} + + +static void * +convert_to_foreign(net_area_info *foreignArea,net_area_info *localArea,void *data) +{ + return convert_address(localArea,foreignArea,data); +} + + +static void +on_socket_event(void * socket, uint32 event, void * cookie) +{ + connection_cookie * cc = (connection_cookie *) cookie; + struct socket_event_data sed; + status_t status; + + if (!cc) + return; + + if (cc->socket != socket) { + printf("on_socket_event(%p, %ld, %p): socket is higly suspect! Aborting.\n", socket, event, cookie); + return; + } + + printf("on_socket_event(%p, %ld, %p)\n", socket, event, cookie); + + sed.event = event; + sed.cookie = cc->notify_cookie; + + // TODO: don't block here => write_port_etc() ? + status = write_port(cc->socket_event_port, NET_STACK_SOCKET_EVENT_NOTIFICATION, + &sed, sizeof(sed)); + if (status != B_OK) + printf("write_port(NET_STACK_SOCKET_EVENT_NOTIFICATION) failure: %s\n", + strerror(status)); + return; +} + + + +static int32 +connection_runner(void *_cookie) +{ + connection_cookie *cookie = (connection_cookie *)_cookie; + bool run = true; + + while (run) { + net_area_info area[MAX_NET_AREAS]; + net_command *command; + status_t status = B_OK; + uint8 *data; + int32 index; + ssize_t bytes = read_port(cookie->localPort,&index,NULL,0); + if (bytes < B_OK) + break; + + if (index >= NUM_COMMANDS || index < 0) { + printf("got bad command index: %lx\n",index); + continue; + } + command = cookie->commands + index; + if (clone_command_areas(area,command) < B_OK) { + printf("could not clone command areas!\n"); + continue; + } + + data = convert_to_local(&command->area[0],&area[0],command->data); + printf("command %lx (index = %ld), buffer = %p, length = %ld, result = %ld\n",command->op,index,data,command->length,command->result); + + switch (command->op) { + case NET_STACK_OPEN: + { + struct int_args *args = (struct int_args *)data; + cookie->openFlags = args->value; + printf("opening socket, mode = %lx!\n",cookie->openFlags); + break; + } + case NET_STACK_CLOSE: + printf("closing socket...\n"); + run = false; + break; + + case NET_STACK_SOCKET: + { + struct socket_args *args = (struct socket_args *)data; + + printf("open a socket... family = %d, type = %d, proto = %d\n",args->family,args->type,args->proto); + status = core->socket_init(&cookie->socket); + if (status == 0) + status = core->socket_create(cookie->socket, args->family, args->type, args->proto); + break; + } + case NET_STACK_GETSOCKOPT: + case NET_STACK_SETSOCKOPT: + { + struct sockopt_args *sockopt = (struct sockopt_args *)data; + + if (command->op == NET_STACK_GETSOCKOPT) { + status = core->socket_getsockopt(cookie->socket,sockopt->level,sockopt->option, + convert_to_local(&command->area[1],&area[1],sockopt->optval), + (size_t *)&sockopt->optlen); + } else { + status = core->socket_setsockopt(cookie->socket,sockopt->level,sockopt->option, + (const void *)convert_to_local(&command->area[1],&area[1],sockopt->optval), + sockopt->optlen); + } + break; + } + case NET_STACK_CONNECT: + case NET_STACK_BIND: + case NET_STACK_GETSOCKNAME: + case NET_STACK_GETPEERNAME: + { + struct sockaddr_args *args = (struct sockaddr_args *)data; + caddr_t addr = (caddr_t)convert_to_local(&command->area[1],&area[1],args->addr); + + switch (command->op) { + case NET_STACK_CONNECT: + status = core->socket_connect(cookie->socket,addr,args->addrlen); + break; + case NET_STACK_BIND: + status = core->socket_bind(cookie->socket,addr,args->addrlen); + break; + case NET_STACK_GETSOCKNAME: + status = core->socket_getsockname(cookie->socket,(struct sockaddr *)addr,&args->addrlen); + break; + case NET_STACK_GETPEERNAME: + status = core->socket_getpeername(cookie->socket,(struct sockaddr *)addr,&args->addrlen); + break; + } + break; + } + case NET_STACK_LISTEN: + status = core->socket_listen(cookie->socket,((struct int_args *)data)->value); + break; + + case NET_STACK_GET_COOKIE: + /* this is needed by accept() call, to be able to pass back + * in NET_STACK_ACCEPT opcode the cookie of the filedescriptor to + * use for the new accepted socket + */ + *((void **)data) = cookie; + break; + + case NET_STACK_ACCEPT: + { + struct accept_args *args = (struct accept_args *)data; + connection_cookie *otherCookie = (connection_cookie *)args->cookie; + status = core->socket_accept(cookie->socket,&otherCookie->socket, + convert_to_local(&command->area[1],&area[1],args->addr), + &args->addrlen); + } + case NET_STACK_SEND: + { + struct data_xfer_args *args = (struct data_xfer_args *)data; + struct iovec iov; + int flags = 0; + + iov.iov_base = convert_to_local(&command->area[1],&area[1],args->data); + iov.iov_len = args->datalen; + + status = core->socket_writev(cookie->socket,&iov,flags); + break; + } + case NET_STACK_RECV: + { + struct data_xfer_args *args = (struct data_xfer_args *)data; + struct iovec iov; + int flags = 0; + + iov.iov_base = convert_to_local(&command->area[1],&area[1],args->data); + iov.iov_len = args->datalen; + + /* flags gets ignored here... */ + status = core->socket_readv(cookie->socket,&iov,&flags); + break; + } + case NET_STACK_RECVFROM: + { + struct msghdr *msg = (struct msghdr *)data; + int received; + + msg->msg_name = convert_to_local(&command->area[1],&area[1],msg->msg_name); + msg->msg_iov = convert_to_local(&command->area[2],&area[2],msg->msg_iov); + msg->msg_control = convert_to_local(&command->area[3],&area[3],msg->msg_control); + + status = core->socket_recv(cookie->socket, msg, (caddr_t)&msg->msg_namelen,&received); + if (status == 0) + status = received; + + msg->msg_name = convert_to_foreign(&command->area[1],&area[1],msg->msg_name); + msg->msg_iov = convert_to_foreign(&command->area[2],&area[2],msg->msg_iov); + msg->msg_control = convert_to_foreign(&command->area[3],&area[3],msg->msg_control); + break; + } + case NET_STACK_SENDTO: + { + struct msghdr *msg = (struct msghdr *)data; + int sent; + + msg->msg_name = convert_to_local(&command->area[1],&area[1],msg->msg_name); + msg->msg_iov = convert_to_local(&command->area[2],&area[2],msg->msg_iov); + msg->msg_control = convert_to_local(&command->area[3],&area[3],msg->msg_control); + + status = core->socket_send(cookie->socket,msg,msg->msg_flags,&sent); + if (status == 0) + status = sent; + + msg->msg_name = convert_to_foreign(&command->area[1],&area[1],msg->msg_name); + msg->msg_iov = convert_to_foreign(&command->area[2],&area[2],msg->msg_iov); + msg->msg_control = convert_to_foreign(&command->area[3],&area[3],msg->msg_control); + break; + } + + case NET_STACK_NOTIFY_SOCKET_EVENT: + { + struct notify_socket_event_args *args = (struct notify_socket_event_args *)data; + + cookie->socket_event_port = args->notify_port; + cookie->notify_cookie = args->cookie; + + if (cookie->socket_event_port != -1) + // start notify socket event + status = core->socket_set_event_callback(cookie->socket, on_socket_event, cookie, 0); + else + // stop notify socket event + status = core->socket_set_event_callback(cookie->socket, NULL, NULL, 0); + break; + } + + case NET_STACK_SYSCTL: + { + struct sysctl_args *args = (struct sysctl_args *)data; + + status = core->net_sysctl(convert_to_local(&command->area[1],&area[1],args->name), + args->namelen,convert_to_local(&command->area[2],&area[2],args->oldp), + convert_to_local(&command->area[3],&area[3],args->oldlenp), + convert_to_local(&command->area[4],&area[4],args->newp), + args->newlen); + break; + } + + case NET_STACK_STOP: + core->stop(); + break; + + case B_SET_BLOCKING_IO: + cookie->openFlags &= ~O_NONBLOCK; + break; + + case B_SET_NONBLOCKING_IO: + cookie->openFlags |= O_NONBLOCK; + break; + + case OSIOCGIFCONF: + case SIOCGIFCONF: + { + struct ifconf *ifc = (struct ifconf *)data; + ifc->ifc_buf = convert_to_local(&command->area[1],&area[1],ifc->ifc_buf); + + status = core->socket_ioctl(cookie->socket,command->op,(char *)data); + + ifc->ifc_buf = convert_to_foreign(&command->area[1],&area[1],ifc->ifc_buf); + break; + } + + default: + status = core->socket_ioctl(cookie->socket,command->op,(char *)data); + break; + } + // mark the command as done + command->result = status; + command->op = 0; + delete_cloned_areas(area); + + // notify the command pipeline that we're done with the command + release_sem(cookie->commandSemaphore); + } + + cookie->runner = -1; + shutdown_connection(cookie); + + return 0; +} + + +static status_t +init_connection(net_connection *connection,connection_cookie **_cookie) +{ + connection_cookie *cookie; + net_command *commands; + + cookie = (connection_cookie *)malloc(sizeof(connection_cookie)); + if (cookie == NULL) { + fprintf(stderr,"couldn't allocate memory for cookie.\n"); + return B_NO_MEMORY; + } + + connection->area = create_area("net connection",(void *)&commands,B_ANY_ADDRESS, + CONNECTION_BUFFER_SIZE + CONNECTION_COMMAND_SIZE, + B_NO_LOCK,B_READ_AREA | B_WRITE_AREA); + if (connection->area < B_OK) { + fprintf(stderr,"couldn't create area: %s.\n",strerror(connection->area)); + free(cookie); + return connection->area; + } + memset(commands,0,NUM_COMMANDS * sizeof(net_command)); + + connection->port = create_port(CONNECTION_QUEUE_LENGTH,"net stack connection"); + if (connection->port < B_OK) { + fprintf(stderr,"couldn't create port: %s.\n",strerror(connection->port)); + delete_area(connection->area); + free(cookie); + return connection->port; + } + + connection->commandSemaphore = create_sem(0,"net command queue"); + if (connection->commandSemaphore < B_OK) { + fprintf(stderr,"couldn't create semaphore: %s.\n",strerror(connection->commandSemaphore)); + delete_area(connection->area); + delete_port(connection->port); + free(cookie); + return connection->commandSemaphore; + } + + cookie->runner = spawn_thread(connection_runner,"connection runner",B_NORMAL_PRIORITY,cookie); + if (cookie->runner < B_OK) { + fprintf(stderr,"couldn't create thread: %s.\n",strerror(cookie->runner)); + delete_sem(connection->commandSemaphore); + delete_area(connection->area); + delete_port(connection->port); + free(cookie); + return B_ERROR; + } + + connection->numCommands = NUM_COMMANDS; + connection->bufferSize = CONNECTION_BUFFER_SIZE; + + // setup connection cookie + cookie->area = connection->area; + cookie->commands = commands; + cookie->buffer = (uint8 *)commands + CONNECTION_COMMAND_SIZE; + cookie->commandSemaphore = connection->commandSemaphore; + cookie->localPort = connection->port; + cookie->openFlags = 0; + + cookie->socket_event_port = -1; + cookie->notify_cookie = NULL; + + resume_thread(cookie->runner); + + *_cookie = cookie; + return B_OK; +} + + +static void +shutdown_connection(connection_cookie *cookie) +{ + printf("free cookie: %p\n",cookie); + kill_thread(cookie->runner); + + delete_port(cookie->localPort); + delete_sem(cookie->commandSemaphore); + delete_area(cookie->area); + + free(cookie); +} + + +static int32 +connection_opener(void *_unused) +{ + while(true) { + port_id port; + int32 msg; + ssize_t bytes = read_port(gStackPort,&msg,&port,sizeof(port_id)); + if (bytes < B_OK) + return bytes; + + if (msg == NET_STACK_NEW_CONNECTION) { + net_connection connection; + connection_cookie *cookie; + + printf("incoming connection...\n"); + if (init_connection(&connection,&cookie) == B_OK) + write_port(port,NET_STACK_NEW_CONNECTION,&connection,sizeof(net_connection)); + } else + fprintf(stderr,"connection_opener: received unknown command: %lx (expected = %lx)\n",msg,(int32)NET_STACK_NEW_CONNECTION); + } + return 0; +} + + +status_t +init_userland_ipc(void) +{ + gStackPort = create_port(CONNECTION_QUEUE_LENGTH,NET_STACK_PORTNAME); + if (gStackPort < B_OK) + return gStackPort; + + gConnectionOpener = spawn_thread(connection_opener,"connection opener",B_NORMAL_PRIORITY,NULL); + if (resume_thread(gConnectionOpener) < B_OK) { + delete_port(gStackPort); + if (gConnectionOpener >= B_OK) { + kill_thread(gConnectionOpener); + return B_BAD_THREAD_STATE; + } + return gConnectionOpener; + } + + return B_OK; +} + + +void +shutdown_userland_ipc(void) +{ + delete_port(gStackPort); + kill_thread(gConnectionOpener); +} + + +#ifdef COMMUNICATION_TEST +int +main(void) +{ + char buffer[8]; + + if (init_userland_ipc() < B_OK) + return -1; + + puts("Userland_ipc - test is running. Press to quit."); + fgets(buffer,sizeof(buffer),stdin); + + shutdown_userland_ipc(); + + return 0; +} +#endif /* COMMUNICATION_TEST */ diff --git a/src/tests/kits/net/new_stack/userland_ipc.h b/src/tests/kits/net/new_stack/userland_ipc.h new file mode 100644 index 0000000000..c3980481c9 --- /dev/null +++ b/src/tests/kits/net/new_stack/userland_ipc.h @@ -0,0 +1,62 @@ +#ifndef USERLAND_IPC_H +#define USERLAND_IPC_H +/* userland_ipc - Communication between the network driver +** and the userland stack. +** +** Initial version by Axel Dörfler, axeld@pinc-software.de +** This file may be used under the terms of the OpenBeOS License. +*/ + + +#include +#include "net_stack_driver.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NET_STACK_PORTNAME "net_server connection" + +enum { + NET_STACK_OPEN = NET_STACK_IOCTL_MAX, + NET_STACK_CLOSE, + NET_STACK_NEW_CONNECTION, +}; + +#define MAX_NET_AREAS 5 + +typedef struct { + area_id id; + uint8 *offset; +} net_area_info; + +typedef struct { + int32 op; +// int32 buffer; + uint8 *data; + int32 length; + int32 result; + net_area_info area[MAX_NET_AREAS]; +} net_command; + +#define CONNECTION_QUEUE_LENGTH 128 +#define CONNECTION_COMMAND_SIZE 2048 + +typedef struct { + port_id port; + area_id area; + + sem_id commandSemaphore; // command queue + uint32 numCommands,bufferSize; +} net_connection; + + +extern status_t init_userland_ipc(void); +extern void shutdown_userland_ipc(void); + + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif /* USERLAND_IPC_H */ diff --git a/src/tests/kits/net/new_stack/userland_modules.cpp b/src/tests/kits/net/new_stack/userland_modules.cpp new file mode 100644 index 0000000000..8fbd58643f --- /dev/null +++ b/src/tests/kits/net/new_stack/userland_modules.cpp @@ -0,0 +1,851 @@ +/* Userland modules emulation support +*/ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASSERT(condition) if (!(condition)) { debugger("Assertion failed!"); } + +typedef enum { + MODULE_LOADED = 0, + MODULE_INITING, + MODULE_READY, + MODULE_UNINITING, + MODULE_ERROR +} module_state; + +typedef struct module { + struct module * next; + uint32 id; + char * name; + module_info * info; + struct module_addon * addon; // the module addon this module live in + // if NULL, builtin module addon + int32 ref_count; // reference count of get_module() made on this module + bool keep_loaded; + module_state state; +} module; + +typedef struct module_addon { + struct module_addon * next; + int32 ref_count; // reference count of get_module() made using this addon + bool keep_loaded; + char * path; + image_id addon_image; // if -1, not loaded in memory currently + module_info ** infos; // valid only when addon_image != -1 +} module_addon; + +typedef struct module_list_cookie { + char * prefix; + char * search_paths; + char * search_path; + char * next_path_token; + BList * dir_stack; + module_addon * ma; // current module addon looked up + module_info ** mi; // current module addon module info +} module_list_cookie; + +#define LOCK_MODULES acquire_sem(g_modules_lock) +#define UNLOCK_MODULES release_sem(g_modules_lock) + +// local prototypes +// ------------------ + +static module * search_module(const char * name); +static status_t init_module(module * m); +static status_t uninit_module(module * m); +static module * find_loaded_module_by_name(const char * name); +static module * find_loaded_module_by_id(uint32 id); + +static module_addon * load_module_addon(const char * path); +static status_t unload_module_addon(module_addon * ma); + +// globals +// ------------------ + +static sem_id g_modules_lock = -1; // One lock for rule them all, etc... +static module * g_modules = NULL; +static module_addon * g_module_addons = NULL; +static int32 g_next_module_id = 1; + + +// Public routines +// --------------- + +_EXPORT status_t get_module(const char * name, module_info ** mi) +{ + status_t status; + module * m; + + printf("get_module(%s)\n", name); + + m = find_loaded_module_by_name(name); + if (!m) + m = search_module(name); + + if (!m) + return B_NAME_NOT_FOUND; + + *mi = m->info; + + status = B_OK; + + if (m->addon) // built-in modules don't comes from addon... + atomic_add(&m->addon->ref_count, 1); + + if (atomic_add(&m->ref_count, 1) == 0) { + // first time we reference this module, so let's init it: + status = init_module(m); + if (status != B_OK) { + printf("Failed to init module %s: %s.\n", m->name, strerror(status)); + unload_module_addon(m->addon); // unload the module addon... + }; + }; + + return status; +} + +_EXPORT status_t put_module(const char * name) +{ + module * m; + + printf("put_module(%s)\n", name); + + m = find_loaded_module_by_name(name); + if (!m) + // Hum??? Sorry, this module name was never get_module()'d + return B_NAME_NOT_FOUND; + + if (atomic_add(&m->ref_count, -1) <= 1) + // this module is no more used... + uninit_module(m); + + if (!m->addon) + // built-in modules are module addon less... + return B_OK; + + if (atomic_add(&m->addon->ref_count, -1) > 1) + // Still other module(s) using this module addon + return B_OK; + + // okay, this module addon is no more used + // let's free up some memory + return unload_module_addon(m->addon); +} + + +_EXPORT status_t get_next_loaded_module_name(uint32 *cookie, char *buf, size_t *bufsize) +{ + module * m; + status_t status; + + if (buf == NULL && bufsize == NULL) + return B_BAD_VALUE; + + LOCK_MODULES; + + if (*cookie == 0) + // first call expected value + m = g_modules; + else { + // find last loaded module returned, and seek to next one + m = (module *) find_loaded_module_by_id((int) *cookie); + if (m) + m = m->next; + }; + + // find next loaded module + while (m) { + if (m->ref_count) + break; + m = m->next; + }; + + status = B_OK; + if (m) { + ASSERT(m->info); + if (buf != NULL) + strncpy(buf, m->info->name, *bufsize); + else + *bufsize = strlen(m->info->name + 1); + *cookie = m->id; + } else + status = B_BAD_INDEX; + + UNLOCK_MODULES; + + return status; +} + + +_EXPORT void * open_module_list(const char *prefix) +{ + module_list_cookie * mlc; + char * addon_path; + + if (prefix == NULL) + return NULL; + + mlc = (module_list_cookie *) malloc(sizeof(*mlc)); + mlc->prefix = strdup(prefix); + + addon_path = getenv("ADDON_PATH"); + mlc->search_paths = (addon_path ? strdup(addon_path) : NULL); + mlc->search_path = strtok_r(mlc->search_paths, ":", &mlc->next_path_token); + mlc->dir_stack = new BList(); + mlc->ma = NULL; + mlc->mi = NULL; + + return mlc; +} + + +_EXPORT status_t read_next_module_name(void *cookie, char *buf, size_t *bufsize) +{ + module_list_cookie * mlc = (module_list_cookie *) cookie; + + if (!bufsize) + return B_BAD_VALUE; + + if (!mlc) + return B_BAD_VALUE; + + /* Okay, take some time to understand how this function works! + Basicly, we iterate thru: + - each searchable add-ons path root + - each (sub-)directory under the current add-ons path root + - each module add-on file in the current (sub-)directory + - each module name published by current module add-on + + As the iteration involve sub-directory walks, we use recursive calls. + Sorry if this code sounds too complex... + */ + + if (mlc->ma && mlc->mi) { + // we have a module addon still loaded from a last call + // so keep looking at his exported module names list + while (*mlc->mi) { + module_info * mi = *mlc->mi; + mlc->mi++; + if(strstr(mi->name, mlc->prefix)) { + // We find a matching module name. At least. Yeah!!! + if (buf) strncpy(buf, mi->name, *bufsize); + *bufsize = strlen(mi->name); + return B_OK; + }; + }; + + // We've iterate all module names of this module addon. Find another one... + unload_module_addon(mlc->ma); + mlc->ma = NULL; + mlc->mi = NULL; + }; + + // Iterate all searchable add-ons paths + while (mlc->search_path) { + BDirectory * dir; + BEntry entry; + BPath path; + status_t status; + + // Get current directory + dir = (BDirectory *) mlc->dir_stack->LastItem(); + if (!dir) { + // find add-ons root directory in this search path + if (strncmp(mlc->search_path, "%A/", 3) == 0) { + // resolve "%A/..." path + app_info ai; + + be_app->GetAppInfo(&ai); + entry.SetTo(&ai.ref); + entry.GetPath(&path); + path.GetParent(&path); + path.Append(mlc->search_path + 3); + } else { + path.SetTo(mlc->search_path); + }; + + // We look *only* under prefix-matching sub-path + path.Append(mlc->prefix); + + // printf("Looking module(s) in %s/%s...\n", mlc->search_path, mlc->prefix); + + dir = new BDirectory(path.Path()); + if (dir) + mlc->dir_stack->AddItem(dir); + }; + + // Iterate current directory content + if (dir) { + while (dir->GetNextEntry(&entry) == B_OK) { + entry.GetPath(&path); + // printf(" %s ?\n", path.Path()); + + if (entry.IsDirectory()) { + BDirectory * subdir; + // push this directory on dir_stack + subdir = new BDirectory(path.Path()); + if (!subdir) + continue; + + mlc->dir_stack->AddItem(subdir); + // recursivly search this sub-directory + return read_next_module_name(cookie, buf, bufsize); + }; + + if (entry.IsFile() || entry.IsSymLink()) { + mlc->ma = load_module_addon(path.Path()); + if (!mlc->ma) + // Oh-oh, not a loadable module addon!? + // WTF it's doing there?!? + continue; + + // call ourself to enter the module names list iteration at + // function begining code... + mlc->mi = mlc->ma->infos; + return read_next_module_name(cookie, buf, bufsize); + }; + }; + + // We walk thru all this directory content, go back to parent + status = mlc->dir_stack->RemoveItem(dir); + delete dir; + }; + + if (!mlc->dir_stack->IsEmpty()) + continue; + + // We walk thru all this search path content, next now + mlc->search_path = strtok_r(NULL, ":", &mlc->next_path_token); + }; + + // Module(s) list search done, ending... + return B_ERROR; +} + + +_EXPORT status_t close_module_list(void *cookie) +{ + module_list_cookie * mlc = (module_list_cookie *) cookie; + BDirectory * dir; + + ASSERT(mlc); + ASSERT(mlc->prefix); + + if (mlc->ma) + unload_module_addon(mlc->ma); + + while((dir = (BDirectory *) mlc->dir_stack->FirstItem())) { + mlc->dir_stack->RemoveItem(dir); + delete dir; + }; + + delete mlc->dir_stack; + + free(mlc->search_paths); + free(mlc->prefix); + free(mlc); + + return B_ERROR; +} + +// #pragma mark - +// Some KernelExport.h support from userland + +_EXPORT void dprintf(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + + +_EXPORT void kprintf(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + + +_EXPORT status_t load_driver_symbols(char *driver_name) +{ + // Userland debugger will extract symbols itself... + return B_OK; +} + + +_EXPORT thread_id spawn_kernel_thread(thread_entry func, const char *name, long priority, void *arg) +{ + return spawn_thread(func, name, priority, arg); +} + + +// #pragma mark - +// Private routines + +static module_addon * load_module_addon(const char * path) +{ + module_addon * ma; + image_id addon_id; + module_info ** mi; + status_t status; + + ASSERT(path); + + addon_id = load_add_on(path); + if (addon_id < 0) { + printf("Failed to load %s addon: %s.\n", path, strerror(addon_id)); + return NULL; + }; + + // printf("Addon %s loaded.\n", path); + + ma = NULL; + + status = get_image_symbol(addon_id, "modules", B_SYMBOL_TYPE_DATA, (void **) &mi); + if (status != B_OK) { + // No "modules" symbol found in this addon + printf("Symbol \"modules\" not found in %s addon: not a module addon!\n", path); + goto error; + }; + + ma = (module_addon *) malloc(sizeof(*ma)); + if (!ma) + // Gasp: not enough memory! + goto error; + + LOCK_MODULES; + + ma->ref_count = 0; + ma->keep_loaded = false; + ma->path = strdup(path); + ma->addon_image = addon_id; + ma->infos = mi; + + while(*mi) { + module * m; + + m = (module *) malloc(sizeof(*m)); + if (!m) + // Gasp, again: not enough memory! + goto error; + + m->ref_count = 0; + m->id = atomic_add(&g_next_module_id, 1); + m->info = (*mi); + m->name = strdup(m->info->name); + m->addon = ma; + m->keep_loaded = (m->info->flags & B_KEEP_LOADED) ? true : false; + + m->state = MODULE_LOADED; + + m->next = g_modules; + g_modules = m; + + mi++; + }; + + // add this module addon to the list + ma->next = g_module_addons; + g_module_addons = ma; + + UNLOCK_MODULES; + + return ma; + +error: + printf("Error while load_module_addon(%s)\n", path); + + if (ma) { + // remove any appended modules by this module addon until we got error... + module * prev; + module * m; + + prev = NULL; + m = g_modules; + while (m) { + if (m->addon == ma) { + module * tmp = m; + + m = tmp->next; + + if (prev) + prev->next = tmp->next; + else + g_modules = tmp->next; + + if (tmp->name) + free(tmp->name); + free(tmp); + continue; + }; + + prev = m; + m = m->next; + }; + + + UNLOCK_MODULES; + + if (ma->path) + free(ma->path); + free(ma); + }; + + unload_add_on(addon_id); + // printf("Addon %s unloaded.\n", path); + return NULL; +} + +static status_t unload_module_addon(module_addon * ma) +{ + module * m; + module * prev; + status_t status; + + if (!ma) + // built-in modules are addon-less, so nothing to do... + return B_OK; + + if (ma->keep_loaded) { + printf("B_KEEP_LOADED flag set for %s module addon. Will be *never* unloaded!\n", + ma->path); + return B_OK; + }; + + if (ma->ref_count) + // still someone needing this module addon, it seems? + return B_OK; + + if (ma->addon_image < 0) + // built-in addon, it seems... + return B_OK; + + status = unload_add_on(ma->addon_image); + if (status != B_OK) { + printf("Failed to unload %s addon: %s.\n", ma->path, strerror(status)); + return status; + }; + // printf("Addon %s unloaded.\n", ma->path); + + LOCK_MODULES; + + // remove the modules coming from this module addon from g_modules list + prev = NULL; + m = g_modules; + while (m) { + if (m->addon == ma) { + module * tmp = m; + + m = tmp->next; + + if (prev) + prev->next = tmp->next; + else + g_modules = tmp->next; + + if (tmp->name) + free(tmp->name); + free(tmp); + continue; + }; + + prev = m; + m = m->next; + }; + + // remove the module addon from g_module_addons list: + if (g_module_addons == ma) + g_module_addons = ma->next; + else { + module_addon * tmp; + tmp = g_module_addons; + while (tmp && tmp->next != ma) + tmp = tmp->next; + + ASSERT(tmp); + tmp->next = ma->next; + }; + + if (ma->path) + free(ma->path); + free(ma); + + UNLOCK_MODULES; + + return B_OK; +} + + +static module * search_module(const char * name) +{ + BPath path; + BPath addons_path; + BEntry entry; + module * found_module; + char * search_paths; + char * search_path; + char * next_path_token; + + // printf("search_module(%s):\n", name); + + search_paths = getenv("ADDON_PATH"); + if (!search_paths) + // Nowhere to search addons!!! + return NULL; + + search_paths = strdup(search_paths); + search_path = strtok_r(search_paths, ":", &next_path_token); + + found_module = NULL; + while (search_path && found_module == NULL) { + if (strncmp(search_path, "%A/", 3) == 0) { + // compute "%A/..." path + app_info ai; + + be_app->GetAppInfo(&ai); + entry.SetTo(&ai.ref); + entry.GetPath(&addons_path); + addons_path.GetParent(&addons_path); + addons_path.Append(search_path + 3); + } else { + addons_path.SetTo(search_path); + }; + + // printf("Looking into %s\n", search_path); + + path.SetTo(addons_path.Path()); + path.Append(name); + + while(path != addons_path) { + // printf(" %s ?\n", path.Path()); + entry.SetTo(path.Path()); + if (entry.IsFile() || entry.IsSymLink()) { + module_addon * ma; + + // try to load the module addon + ma = load_module_addon(path.Path()); + if (ma) { + found_module = find_loaded_module_by_name(name); + if (found_module) + break; + + unload_module_addon(ma); + }; // if (ma) + }; // if (entry.IsFile() || entry.IsSymLink()) + + // okay, remove the current path leaf and try again... + path.GetParent(&path); + }; + + search_path = strtok_r(NULL, ":", &next_path_token); + }; + + free(search_paths); + +/* + if (found_module) + printf(" Found it in %s addon module!\n", + found_module->addon ? found_module->addon->path : "BUILTIN"); +*/ + + return found_module; +} + + +static status_t init_module(module * m) +{ + status_t status; + + ASSERT(m); + + switch (m->state) { + case MODULE_LOADED: + m->state = MODULE_INITING; + ASSERT(m->info); + // printf("Initing module %s... ", m->name); + status = m->info->std_ops(B_MODULE_INIT); + // printf("done (%s).\n", strerror(status)); + m->state = (status == B_OK) ? MODULE_READY : MODULE_LOADED; + + if (m->state == MODULE_READY && m->keep_loaded && m->addon) { + // one module (at least) was inited and request to never being + // unload from memory, so keep the corresponding addon loaded + // printf("module %s set B_KEEP_LOADED flag:\nmodule addon %s will never be unloaded!\n", + // m->name, m->addon->path); + m->addon->keep_loaded = true; + }; + break; + + case MODULE_READY: + status = B_OK; + break; + + case MODULE_INITING: // circular reference!!! + case MODULE_UNINITING: // initing a module currently unloading... + case MODULE_ERROR: // module failed to unload previously... + default: // Unknown module state!!! + status = B_ERROR; + break; + }; + + return status; +} + + +static status_t uninit_module(module * m) +{ + status_t status; + + ASSERT(m); + + switch (m->state) { + case MODULE_READY: + m->state = MODULE_UNINITING; + ASSERT(m->info); + // printf("Uniniting module %s... ", m->name); + status = m->info->std_ops(B_MODULE_UNINIT); + // printf("done (%s).\n", strerror(status)); + m->state = (status == B_OK) ? MODULE_LOADED : MODULE_ERROR; + break; + + case MODULE_LOADED: + // No need to uninit it, all is fine so. + status = B_OK; + break; + + case MODULE_INITING: // uniniting while initializing + case MODULE_UNINITING: // uniniting already pending + case MODULE_ERROR: // module failed previously... + default: // Unknown module state!!! + status = B_ERROR; + break; + }; + + return status; +} + + +static module * find_loaded_module_by_name(const char * name) +{ + module * m; + + LOCK_MODULES; + + m = g_modules; + while (m) { + if (strcmp(name, m->name) == 0) + break; + m = m->next; + }; + + UNLOCK_MODULES; + return m; +} + + +static module * find_loaded_module_by_id(uint32 id) +{ + module * m; + + LOCK_MODULES; + + m = g_modules; + while (m) { + if (m->id == id) + break; + m = m->next; + }; + + UNLOCK_MODULES; + return m; +} + +#if 0 +// #pragma mark - + +#define NET_CORE_MODULE_NAME "network/core/v1" +#define NET_ETHERNET_MODULE_NAME "network/interfaces/ethernet" +#define NET_IPV4_MODULE_NAME "network/protocols/ipv4/v1" + +#define MODULE_LIST_PREFIX "network" + +int main(int argc, char **argv) +{ + module_info * core; + module_info * ethernet; + module_info * ipv4; + char module_name[256]; + uint32 cookie; + size_t sz; + void * ml_cookie; + + new BApplication("application/x-vnd-OBOS-net_server"); + + printf("open_module_list(%s):\n", MODULE_LIST_PREFIX); + ml_cookie = open_module_list(MODULE_LIST_PREFIX); + sz = sizeof(module_name); + while(read_next_module_name(ml_cookie, module_name, &sz) == B_OK) { + if (strlen(module_name)) + printf(" %s\n", module_name); + sz = sizeof(module_name); + }; + close_module_list(ml_cookie); + printf("close_module_list()\n"); + // return 0; + + core = NULL; + get_module(NET_CORE_MODULE_NAME, (module_info **) &core); + + ethernet = NULL; + get_module(NET_ETHERNET_MODULE_NAME, (module_info **) ðernet); + + ipv4 = NULL; + get_module(NET_IPV4_MODULE_NAME, (module_info **) &ipv4); + + printf("get_next_loaded_module_name() test:\n"); + cookie = 0; + sz = sizeof(module_name); + while (get_next_loaded_module_name(&cookie, module_name, &sz) == B_OK) + printf("%ld: %s\n", cookie, module_name); + + if (ipv4) + put_module(NET_IPV4_MODULE_NAME); + + if (ethernet) + put_module(NET_ETHERNET_MODULE_NAME); + + if (core) + put_module(NET_CORE_MODULE_NAME); + + printf("get_next_loaded_module_name() test:\n"); + cookie = 0; + sz = sizeof(module_name); + while (get_next_loaded_module_name(&cookie, module_name, &sz) == B_OK) + printf("%ld: %s\n", cookie, module_name); + + delete be_app; + return 0; +} +#endif + +