Better checking these than lost them one day...

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3259 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Philippe Houdoin
2003-05-20 00:03:46 +00:00
parent 2dd47f63b5
commit 301e81d7a5
24 changed files with 4421 additions and 0 deletions
@@ -0,0 +1,293 @@
/* ethernet.c - ethernet devices interface module
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/dirent.h>
#include <sys/stat.h>
#include <Drivers.h>
#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
};
@@ -0,0 +1,121 @@
/* loopback.c - loopback device
*/
#include <stdio.h>
#include <stdlib.h>
#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
};
@@ -0,0 +1,38 @@
#ifndef OBOS_MEMORY_POOL_H
#define OBOS_MEMORY_POOL_H
#include <drivers/module.h>
#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 */
@@ -0,0 +1,295 @@
#include <stdio.h> // for the hack using malloc() / free()
#include <stdlib.h>
#include <SupportDefs.h>
#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
};
@@ -0,0 +1,124 @@
/* net_interface.h
* definitions of interface networking module API
*/
#ifndef OBOS_NET_INTERFACE_H
#define OBOS_NET_INTERFACE_H
#include <drivers/module.h>
#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 */
@@ -0,0 +1,17 @@
/* net_protocol.h
* definitions of protocol networking module API
*/
#ifndef OBOS_NET_PROTOCOL_H
#define OBOS_NET_PROTOCOL_H
#include <drivers/module.h>
struct net_protocol_module_info {
module_info module;
};
#define NET_PROTOCOL_MODULE_ROOT "network/protocols/"
#endif /* OBOS_NET_PROTOCOL_H */
+118
View File
@@ -0,0 +1,118 @@
/* Userland modules emulation support
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <app/Application.h>
#include <drivers/module.h>
#include "net_stack.h"
// #include <userland_ipc.h>
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;
}
Binary file not shown.
+109
View File
@@ -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 <drivers/module.h>
#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 */
@@ -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 <sys/select.h>
#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 */
+931
View File
@@ -0,0 +1,931 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <KernelExport.h>
#include <OS.h>
#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);
}
+49
View File
@@ -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 <SupportDefs.h>
#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
@@ -0,0 +1,231 @@
/* core.c */
/* This the heart of network stack
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #include <unistd.h>
#include <KernelExport.h>
#include <OS.h>
#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;
}
@@ -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 */
+150
View File
@@ -0,0 +1,150 @@
/* stack.c */
/* This the heart of network stack
*/
#include <stdio.h>
#include <drivers/module.h>
#include <drivers/KernelExport.h>
#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
};
+276
View File
@@ -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, [email protected]
**
** This file may be used under the terms of the OpenBeOS License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <KernelExport.h>
#include <OS.h>
#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
@@ -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, [email protected]
**
** This file may be used under the terms of the OpenBeOS License.
*/
#include <SupportDefs.h>
#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 */
+554
View File
@@ -0,0 +1,554 @@
/* userland_ipc - Communication between the network driver
** and the userland stack.
**
** Initial version by Axel Dörfler, [email protected]
** 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
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 <Return> to quit.");
fgets(buffer,sizeof(buffer),stdin);
shutdown_userland_ipc();
return 0;
}
#endif /* COMMUNICATION_TEST */
@@ -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, [email protected]
** This file may be used under the terms of the OpenBeOS License.
*/
#include <OS.h>
#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 */
@@ -0,0 +1,851 @@
/* Userland modules emulation support
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <drivers/KernelExport.h>
#include <drivers/module.h>
#include <app/Application.h>
#include <app/Roster.h>
#include <kernel/OS.h>
#include <kernel/image.h>
#include <storage/StorageDefs.h>
#include <storage/FindDirectory.h>
#include <storage/Path.h>
#include <storage/Directory.h>
#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 **) &ethernet);
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