Moving the network stack driver (/dev/net/stack) from old /net_kit/source/driver/
to /current repository git-svn-id: file:///srv/svn/repos/haiku/trunk/current@894 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -4,4 +4,5 @@ SubInclude OBOS_TOP src add-ons kernel drivers arch ;
|
|||||||
SubInclude OBOS_TOP src add-ons kernel drivers audio ;
|
SubInclude OBOS_TOP src add-ons kernel drivers audio ;
|
||||||
SubInclude OBOS_TOP src add-ons kernel drivers common ;
|
SubInclude OBOS_TOP src add-ons kernel drivers common ;
|
||||||
SubInclude OBOS_TOP src add-ons kernel drivers random ;
|
SubInclude OBOS_TOP src add-ons kernel drivers random ;
|
||||||
|
SubInclude OBOS_TOP src add-ons kernel drivers net ;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
SubDir OBOS_TOP src add-ons kernel drivers net stack ;
|
||||||
|
|
||||||
|
UsePrivateHeaders net ;
|
||||||
|
UsePublicHeaders posix ;
|
||||||
|
|
||||||
|
R5KernelAddon stack : kernel drivers bin :
|
||||||
|
net_stack_driver.c
|
||||||
|
;
|
||||||
|
|
||||||
|
# For OpenBeOS we should be building the driver objects this way.
|
||||||
|
#KernelObjects
|
||||||
|
# net_stack_driver.c
|
||||||
|
# :
|
||||||
|
# -fno-pic -D_KERNEL_MODE
|
||||||
|
# ;
|
||||||
|
|
||||||
|
# Link to kernel/drivers/dev/net
|
||||||
|
{
|
||||||
|
local dir = [ FDirName $(OBOS_ADDON_DIR) kernel drivers dev net ] ;
|
||||||
|
local instDriver = <kernel!drivers!dev!net>stack ;
|
||||||
|
MakeLocate $(instDriver) : $(dir) ;
|
||||||
|
RelSymLink $(instDriver) : stack ;
|
||||||
|
}
|
||||||
@@ -0,0 +1,800 @@
|
|||||||
|
/* net_stack_driver.c
|
||||||
|
*
|
||||||
|
* This file implements a very simple socket driver that is intended to
|
||||||
|
* act as an interface to the new networking stack.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _KERNEL_MODE
|
||||||
|
|
||||||
|
#error "This module MUST be built as a kernel driver!"
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
#include <Drivers.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <driver_settings.h>
|
||||||
|
#include <drivers/module.h> // For get_module()/put_module()
|
||||||
|
|
||||||
|
#include "netinet/in_var.h"
|
||||||
|
#include "sys/select.h"
|
||||||
|
|
||||||
|
#include "protosw.h"
|
||||||
|
#include "core_module.h"
|
||||||
|
#include "net_stack_driver.h"
|
||||||
|
|
||||||
|
/* these are missing from KernelExport.h ... */
|
||||||
|
#define B_SELECT_READ 1
|
||||||
|
#define B_SELECT_WRITE 2
|
||||||
|
#define B_SELECT_EXCEPTION 3
|
||||||
|
|
||||||
|
extern void notify_select_event(selectsync * sync, uint32 ref);
|
||||||
|
|
||||||
|
#define SHOW_INSANE_DEBUGGING (1)
|
||||||
|
#define SERIAL_DEBUGGING (0)
|
||||||
|
/* Force the driver to stay loaded in memory */
|
||||||
|
#define STAY_LOADED (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local definitions
|
||||||
|
* -----------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DRIVER_NAME
|
||||||
|
#define DRIVER_NAME "net_stack_driver"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LOGID
|
||||||
|
#define LOGID DRIVER_NAME ": "
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WARN
|
||||||
|
#define WARN "Warning: "
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ERR
|
||||||
|
#define ERR "ERROR: "
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef void (*notify_select_event_function)(selectsync * sync, uint32 ref);
|
||||||
|
|
||||||
|
// this struct will store one select() event to monitor per thread
|
||||||
|
typedef struct selecter {
|
||||||
|
struct selecter * next;
|
||||||
|
thread_id thread;
|
||||||
|
int event;
|
||||||
|
selectsync * sync;
|
||||||
|
uint32 ref;
|
||||||
|
} selecter;
|
||||||
|
|
||||||
|
// the cookie we attach to each file descriptor opened on our driver entry
|
||||||
|
typedef struct {
|
||||||
|
void * socket; // NULL before ioctl(fd, NET_STACK_SOCKET/_ACCEPT)
|
||||||
|
uint32 open_flags; // the open() flags (mostly for storing O_NONBLOCK mode)
|
||||||
|
sem_id selecters_lock; // protect the selecters linked-list
|
||||||
|
selecter * selecters; // the select()'ers lists (thread-aware)
|
||||||
|
} net_stack_cookie;
|
||||||
|
|
||||||
|
#if STAY_LOADED
|
||||||
|
/* to unload the driver, simply write UNLOAD_CMD to him:
|
||||||
|
* $ echo stop > /dev/net/stack
|
||||||
|
* As soon as last app, via libnet.so, stop using it, it will unload,
|
||||||
|
* and in turn stop the stack and unload all kernel modules...
|
||||||
|
*/
|
||||||
|
#define UNLOAD_CMD "stop"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Prototypes of device hooks functions */
|
||||||
|
static status_t net_stack_open(const char * name, uint32 flags, void ** cookie);
|
||||||
|
static status_t net_stack_close(void * cookie);
|
||||||
|
static status_t net_stack_free_cookie(void * cookie);
|
||||||
|
static status_t net_stack_control(void * cookie, uint32 msg,void * data, size_t datalen);
|
||||||
|
static status_t net_stack_read(void * cookie, off_t pos, void * data, size_t * datalen);
|
||||||
|
static status_t net_stack_write(void * cookie, off_t pos, const void * data, size_t * datalen);
|
||||||
|
static status_t net_stack_select(void *cookie, uint8 event, uint32 ref, selectsync *sync);
|
||||||
|
static status_t net_stack_deselect(void *cookie, uint8 event, selectsync *sync);
|
||||||
|
// static status_t net_stack_readv(void * cookie, off_t pos, const iovec * vec, size_t count, size_t * len);
|
||||||
|
// static status_t net_stack_writev(void * cookie, off_t pos, const iovec * vec, size_t count, size_t * len);
|
||||||
|
|
||||||
|
/* Privates prototypes */
|
||||||
|
static void on_socket_event(void * socket, uint32 event, void * cookie);
|
||||||
|
static void r5_notify_select_event(selectsync * sync, uint32 ref);
|
||||||
|
|
||||||
|
#if STAY_LOADED
|
||||||
|
static status_t keep_driver_loaded();
|
||||||
|
static status_t unload_driver();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global variables
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char * g_device_names_list[] = {
|
||||||
|
NET_STACK_DRIVER_PATH,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
device_hooks g_net_stack_driver_hooks =
|
||||||
|
{
|
||||||
|
net_stack_open, /* -> open entry point */
|
||||||
|
net_stack_close, /* -> close entry point */
|
||||||
|
net_stack_free_cookie, /* -> free entry point */
|
||||||
|
net_stack_control, /* -> control entry point */
|
||||||
|
net_stack_read, /* -> read entry point */
|
||||||
|
net_stack_write, /* -> write entry point */
|
||||||
|
net_stack_select, /* -> select entry point */
|
||||||
|
net_stack_deselect, /* -> deselect entry point */
|
||||||
|
NULL, /* -> readv entry pint */
|
||||||
|
NULL /* ->writev entry point */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct core_module_info * core = NULL;
|
||||||
|
|
||||||
|
// by default, assert we can use kernel select() support
|
||||||
|
notify_select_event_function g_nse = notify_select_event;
|
||||||
|
|
||||||
|
#if STAY_LOADED
|
||||||
|
int g_stay_loaded_fd = -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Driver API calls
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
_EXPORT int32 api_version = B_CUR_DRIVER_API_VERSION;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Do we init ourselves? If we're in safe mode we'll decline so if things
|
||||||
|
* screw up we can boot and delete the broken driver!
|
||||||
|
* After my experiences earlier - a good idea!
|
||||||
|
*
|
||||||
|
* Also we'll turn on the serial debugger to aid in our debugging
|
||||||
|
* experience.
|
||||||
|
*/
|
||||||
|
_EXPORT status_t init_hardware(void)
|
||||||
|
{
|
||||||
|
bool safemode = false;
|
||||||
|
void * sfmptr;
|
||||||
|
|
||||||
|
#if SERIAL_DEBUGGING
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
// XXX - switch on/off at top of file...
|
||||||
|
set_dprintf_enabled(true);
|
||||||
|
rv = load_driver_symbols(DRIVER_NAME);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// get a pointer to the driver settings...
|
||||||
|
sfmptr = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS);
|
||||||
|
|
||||||
|
// only use the pointer if it's valid
|
||||||
|
if (sfmptr != NULL) {
|
||||||
|
// we got a pointer, now get setting...
|
||||||
|
safemode = get_driver_boolean_parameter(sfmptr, B_SAFEMODE_SAFE_MODE, false, false);
|
||||||
|
// now get rid of settings
|
||||||
|
unload_driver_settings(sfmptr);
|
||||||
|
}
|
||||||
|
if (safemode) {
|
||||||
|
dprintf(LOGID WARN "init_hardware: declining offer to join the party.\n");
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "init_hardware done.\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* init_driver()
|
||||||
|
* called every time we're loaded.
|
||||||
|
*/
|
||||||
|
_EXPORT status_t init_driver(void)
|
||||||
|
{
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "init_driver, built %s %s\n", __DATE__, __TIME__);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rv = get_module(CORE_MODULE_PATH, (module_info **) &core);
|
||||||
|
if (rv < 0) {
|
||||||
|
dprintf(LOGID ERR "Argh, can't load " CORE_MODULE_PATH " module: %d\n", rv);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "init_driver: core = %p\n", core);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// start the network stack!
|
||||||
|
core->start();
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* uninit_driver()
|
||||||
|
* called every time the driver is unloaded
|
||||||
|
*/
|
||||||
|
_EXPORT void uninit_driver(void)
|
||||||
|
{
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "uninit_driver\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (core) {
|
||||||
|
#if STAY_LOADED
|
||||||
|
// shutdown the network stack
|
||||||
|
core->stop();
|
||||||
|
#endif
|
||||||
|
put_module(CORE_MODULE_PATH);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* publish_devices()
|
||||||
|
* called to publish our device.
|
||||||
|
*/
|
||||||
|
_EXPORT const char ** publish_devices()
|
||||||
|
{
|
||||||
|
return g_device_names_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_EXPORT device_hooks * find_device(const char* device_name)
|
||||||
|
{
|
||||||
|
return &g_net_stack_driver_hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Device hooks
|
||||||
|
* ------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
// the network stack functions - mainly just pass throughs...
|
||||||
|
static status_t net_stack_open(const char * name,
|
||||||
|
uint32 flags,
|
||||||
|
void ** cookie)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc;
|
||||||
|
|
||||||
|
nsc = (net_stack_cookie *) malloc(sizeof(*nsc));
|
||||||
|
if (!nsc)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
memset(nsc, 0, sizeof(*nsc));
|
||||||
|
nsc->socket = NULL; // the socket will be allocated in NET_STACK_SOCKET ioctl
|
||||||
|
nsc->open_flags = flags;
|
||||||
|
nsc->selecters_lock = create_sem(1, "socket_selecters_lock");
|
||||||
|
nsc->selecters = NULL;
|
||||||
|
|
||||||
|
// attach this new net_socket_cookie to file descriptor
|
||||||
|
*cookie = nsc;
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "net_stack_open(%s, %s%s) return this cookie: %p\n", name,
|
||||||
|
( ((flags & O_RWMASK) == O_RDONLY) ? "O_RDONLY" :
|
||||||
|
((flags & O_RWMASK) == O_WRONLY) ? "O_WRONLY" : "O_RDWR"),
|
||||||
|
(flags & O_NONBLOCK) ? " O_NONBLOCK" : "",
|
||||||
|
*cookie);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static status_t net_stack_close(void *cookie)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "net_stack_close(%p)\n", nsc);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (nsc->socket) {
|
||||||
|
// if a socket was opened on this fd, close it now.
|
||||||
|
rv = core->soclose(nsc->socket);
|
||||||
|
nsc->socket = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static status_t net_stack_free_cookie(void *cookie)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
selecter * s;
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "net_stack_free_cookie(%p)\n", cookie);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// free the selecters list
|
||||||
|
delete_sem(nsc->selecters_lock);
|
||||||
|
s = nsc->selecters;
|
||||||
|
while (s) {
|
||||||
|
selecter * tmp = s;
|
||||||
|
s = s->next;
|
||||||
|
free(tmp);
|
||||||
|
};
|
||||||
|
|
||||||
|
// free the cookie
|
||||||
|
free(cookie);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static status_t net_stack_control(void *cookie, uint32 op, void * data, size_t len)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "net_stack_control(%p, 0x%lX, %p, %ld)\n", cookie, op, data, len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STAY_LOADED
|
||||||
|
keep_driver_loaded();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case NET_STACK_SOCKET: {
|
||||||
|
struct socket_args * args = (struct socket_args *) data;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
// okay, now try to open a real socket behind this fd/net_stack_cookie pair
|
||||||
|
rv = core->initsocket(&nsc->socket);
|
||||||
|
if (rv == 0)
|
||||||
|
rv = core->socreate(args->family, nsc->socket, args->type, args->proto);
|
||||||
|
// TODO: This is where the open flags need to be addressed
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
case NET_STACK_CONNECT: {
|
||||||
|
struct sockaddr_args * args = (struct sockaddr_args *) data;
|
||||||
|
// args->addr == sockaddr to connect to
|
||||||
|
return core->soconnect(nsc->socket, (caddr_t) args->addr, args->addrlen);
|
||||||
|
}
|
||||||
|
case NET_STACK_BIND: {
|
||||||
|
struct sockaddr_args * args = (struct sockaddr_args *) data;
|
||||||
|
// args->addr == sockaddr to try and bind to
|
||||||
|
return core->sobind(nsc->socket, (caddr_t) args->addr, args->addrlen);
|
||||||
|
}
|
||||||
|
case NET_STACK_LISTEN: {
|
||||||
|
struct int_args * args = (struct int_args *) data;
|
||||||
|
// args->value == backlog to set
|
||||||
|
return core->solisten(nsc->socket, args->value);
|
||||||
|
}
|
||||||
|
case NET_STACK_GET_COOKIE: {
|
||||||
|
/* this is needed by accept() call, to be able to pass back
|
||||||
|
* in NET_STACK_ACCEPT opcode the cookie (aka the net_stack_cookie!)
|
||||||
|
* of the filedescriptor to use for the new accepted socket
|
||||||
|
*/
|
||||||
|
*((void **) data) = cookie;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
case NET_STACK_ACCEPT: {
|
||||||
|
struct accept_args * args = (struct accept_args *) data;
|
||||||
|
net_stack_cookie * ansc = (net_stack_cookie *) args->cookie;
|
||||||
|
/* args->cookie == net_stack_cookie * of the already opened fd to use to the
|
||||||
|
* newly accepted socket
|
||||||
|
*/
|
||||||
|
return core->soaccept(nsc->socket, &ansc->socket, (void *)args->addr, &args->addrlen);
|
||||||
|
}
|
||||||
|
case NET_STACK_SEND: {
|
||||||
|
struct data_xfer_args * args = (struct data_xfer_args *) data;
|
||||||
|
// TODO: flags gets ignored here...
|
||||||
|
return net_stack_write(cookie, 0, args->data, &args->datalen);
|
||||||
|
}
|
||||||
|
case NET_STACK_RECV: {
|
||||||
|
struct data_xfer_args * args = (struct data_xfer_args *) data;
|
||||||
|
// TODO: flags gets ignored here...
|
||||||
|
return net_stack_read(cookie, 0, args->data, &args->datalen);
|
||||||
|
}
|
||||||
|
case NET_STACK_RECVFROM: {
|
||||||
|
struct msghdr * mh = (struct msghdr *) data;
|
||||||
|
int retsize, error;
|
||||||
|
|
||||||
|
error = core->recvit(nsc->socket, mh, (caddr_t)&mh->msg_namelen,
|
||||||
|
&retsize);
|
||||||
|
if (error == 0)
|
||||||
|
return retsize;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
case NET_STACK_SENDTO: {
|
||||||
|
struct msghdr * mh = (struct msghdr *) data;
|
||||||
|
int retsize, error;
|
||||||
|
|
||||||
|
error = core->sendit(nsc->socket, mh, mh->msg_flags,
|
||||||
|
&retsize);
|
||||||
|
if (error == 0)
|
||||||
|
return retsize;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
case NET_STACK_SYSCTL: {
|
||||||
|
struct sysctl_args * args = (struct sysctl_args *) data;
|
||||||
|
return core->net_sysctl(args->name, args->namelen,
|
||||||
|
args->oldp, args->oldlenp,
|
||||||
|
args->newp, args->newlen);
|
||||||
|
}
|
||||||
|
case NET_STACK_GETSOCKOPT: {
|
||||||
|
struct sockopt_args * args = (struct sockopt_args *) data;
|
||||||
|
return core->sogetopt(nsc->socket, args->level, args->option,
|
||||||
|
args->optval, (size_t *) &args->optlen);
|
||||||
|
}
|
||||||
|
case NET_STACK_SETSOCKOPT: {
|
||||||
|
struct sockopt_args * args = (struct sockopt_args *)data;
|
||||||
|
|
||||||
|
return core->sosetopt(nsc->socket, args->level, args->option,
|
||||||
|
(const void *) args->optval, args->optlen);
|
||||||
|
}
|
||||||
|
case NET_STACK_GETSOCKNAME: {
|
||||||
|
struct sockaddr_args * args = (struct sockaddr_args *) data;
|
||||||
|
// args->addr == sockaddr to accept the sockname
|
||||||
|
return core->sogetsockname(nsc->socket, args->addr, &args->addrlen);
|
||||||
|
}
|
||||||
|
case NET_STACK_GETPEERNAME: {
|
||||||
|
struct sockaddr_args * args = (struct sockaddr_args *) data;
|
||||||
|
// args->addr == sockaddr to accept the peername
|
||||||
|
return core->sogetpeername(nsc->socket, args->addr, &args->addrlen);
|
||||||
|
}
|
||||||
|
case NET_STACK_STOP: {
|
||||||
|
core->stop();
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
case B_SET_BLOCKING_IO: {
|
||||||
|
nsc->open_flags &= ~O_NONBLOCK;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
case B_SET_NONBLOCKING_IO: {
|
||||||
|
nsc->open_flags |= O_NONBLOCK;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
case NET_STACK_SELECT: {
|
||||||
|
struct select_args *args = (struct select_args *) data;
|
||||||
|
|
||||||
|
/* if we get this opcode, we are using the r5 kernel select() call,
|
||||||
|
* so we can't use his notify_select_event(), but our own implementation! */
|
||||||
|
g_nse = r5_notify_select_event;
|
||||||
|
return net_stack_select(cookie, (args->ref & 0x0F), args->ref, args->sync);
|
||||||
|
}
|
||||||
|
case NET_STACK_DESELECT: {
|
||||||
|
struct select_args * args = (struct select_args *) data;
|
||||||
|
return net_stack_deselect(cookie, (args->ref & 0x0F), args->sync);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if (nsc->socket)
|
||||||
|
// pass any unhandled opcode to the stack
|
||||||
|
return core->soo_ioctl(nsc->socket, op, data);
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static status_t net_stack_read(void *cookie,
|
||||||
|
off_t position,
|
||||||
|
void *buffer,
|
||||||
|
size_t *readlen)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
struct iovec iov;
|
||||||
|
int error;
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "net_stack_read(%p, %Ld, %p, %ld)\n", cookie, position, buffer, *readlen);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STAY_LOADED
|
||||||
|
# if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "Calling keep_driver_loaded()...\n");
|
||||||
|
# endif
|
||||||
|
keep_driver_loaded();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (! nsc->socket)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
iov.iov_base = buffer;
|
||||||
|
iov.iov_len = *readlen;
|
||||||
|
|
||||||
|
error = core->readit(nsc->socket, &iov, &flags);
|
||||||
|
*readlen = error;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static status_t net_stack_write(void *cookie,
|
||||||
|
off_t position,
|
||||||
|
const void *buffer,
|
||||||
|
size_t *writelen)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
struct iovec iov;
|
||||||
|
int error;
|
||||||
|
int flags = 0;
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "net_stack_write(%p, %Ld, %p, %ld)\n", cookie, position, buffer, *writelen);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if STAY_LOADED
|
||||||
|
keep_driver_loaded();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (! nsc->socket) {
|
||||||
|
#if STAY_LOADED
|
||||||
|
if (*writelen >= strlen(UNLOAD_CMD) &&
|
||||||
|
strncmp(buffer, UNLOAD_CMD, strlen(UNLOAD_CMD)) == 0)
|
||||||
|
// someone write/send/tell us to unload this driver, so do it!
|
||||||
|
return unload_driver();
|
||||||
|
#endif
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
};
|
||||||
|
|
||||||
|
iov.iov_base = (void*)buffer;
|
||||||
|
iov.iov_len = *writelen;
|
||||||
|
|
||||||
|
error = core->writeit(nsc->socket, &iov, flags);
|
||||||
|
*writelen = error;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static status_t net_stack_select(void *cookie, uint8 event, uint32 ref, selectsync *sync)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
selecter * s;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "net_stack_select(%p, %d, %ld, %p)\n", cookie, event, ref, sync);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (! nsc->socket)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
s = (selecter *) malloc(sizeof(selecter));
|
||||||
|
if (! s)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
s->thread = find_thread(NULL); // current thread id
|
||||||
|
s->event = event;
|
||||||
|
s->sync = sync;
|
||||||
|
s->ref = ref;
|
||||||
|
|
||||||
|
// lock the selecters list
|
||||||
|
status = acquire_sem(nsc->selecters_lock);
|
||||||
|
if (status != B_OK) {
|
||||||
|
free(s);
|
||||||
|
return status;
|
||||||
|
};
|
||||||
|
|
||||||
|
// add it to selecters list
|
||||||
|
s->next = nsc->selecters;
|
||||||
|
nsc->selecters = s;
|
||||||
|
|
||||||
|
// unlock the selecters list
|
||||||
|
release_sem(nsc->selecters_lock);
|
||||||
|
|
||||||
|
// start (or continue) to monitor for socket event
|
||||||
|
return core->set_socket_event_callback(nsc->socket, on_socket_event, nsc, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static status_t net_stack_deselect(void *cookie, uint8 event, selectsync *sync)
|
||||||
|
{
|
||||||
|
net_stack_cookie *nsc = (net_stack_cookie *) cookie;
|
||||||
|
selecter * previous;
|
||||||
|
selecter * s;
|
||||||
|
thread_id current_thread;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "net_stack_deselect(%p, %d, %p)\n", cookie, event, sync);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!nsc || !nsc->socket)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
current_thread = find_thread(NULL);
|
||||||
|
|
||||||
|
// lock the selecters list
|
||||||
|
status = acquire_sem(nsc->selecters_lock);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
previous = NULL;
|
||||||
|
s = nsc->selecters;
|
||||||
|
while (s) {
|
||||||
|
if (s->thread == current_thread &&
|
||||||
|
s->event == event && s->sync == sync)
|
||||||
|
// selecter found!
|
||||||
|
break;
|
||||||
|
|
||||||
|
previous = s;
|
||||||
|
s = s->next;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (s != NULL) {
|
||||||
|
// remove it from selecters list
|
||||||
|
if (previous)
|
||||||
|
previous->next = s->next;
|
||||||
|
else
|
||||||
|
nsc->selecters = s->next;
|
||||||
|
free(s);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (nsc->selecters == NULL)
|
||||||
|
// selecters list is empty: no need to monitor socket events anymore
|
||||||
|
core->set_socket_event_callback(nsc->socket, NULL, NULL, event);
|
||||||
|
|
||||||
|
// unlock the selecters list
|
||||||
|
return release_sem(nsc->selecters_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
static void on_socket_event(void * socket, uint32 event, void * cookie)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
selecter * s;
|
||||||
|
|
||||||
|
if (!nsc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (nsc->socket != socket) {
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "on_socket_event(%p, %ld, %p): socket is higly suspect! Aborting.\n", socket, event, cookie);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "on_socket_event(%p, %ld, %p)\n", socket, event, cookie);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// lock the selecters list
|
||||||
|
if (acquire_sem(nsc->selecters_lock) != B_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
s = nsc->selecters;
|
||||||
|
while (s) {
|
||||||
|
if (s->event == event)
|
||||||
|
// notify this selecter (thread/event pair)
|
||||||
|
g_nse(s->sync, s->ref);
|
||||||
|
s = s->next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// unlock the selecters list
|
||||||
|
release_sem(nsc->selecters_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Under vanilla R5, we can't use the kernel notify_select_event(),
|
||||||
|
as select() kernel implementation is too buggy to be usefull.
|
||||||
|
So, here is our own notify_select_event() implementation, the driver-side pair
|
||||||
|
of the our libnet.so select() implementation...
|
||||||
|
*/
|
||||||
|
static void r5_notify_select_event(selectsync * sync, uint32 ref)
|
||||||
|
{
|
||||||
|
area_id area;
|
||||||
|
struct r5_selectsync * rss;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "r5_notify_select_event(%p, %ld)\n", sync, ref);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rss = NULL;
|
||||||
|
area = clone_area("r5_selectsync_area (driver)", (void **) &rss,
|
||||||
|
B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA, (area_id) sync);
|
||||||
|
if (area < B_OK) {
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "r5_notify_select_event: clone_area(%d) failed -> %d!\n", (area_id) sync, area);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "r5_selectsync at %p (area %ld, clone from %ld):\n"
|
||||||
|
"lock %ld\n"
|
||||||
|
"wakeup %ld\n", rss, area, (area_id) sync, rss->lock, rss->wakeup);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (acquire_sem(rss->lock) != B_OK)
|
||||||
|
// if we can't (anymore?) lock the shared r5_selectsync, select() party is done
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
fd = ref >> 8;
|
||||||
|
switch (ref & 0xFF) {
|
||||||
|
case B_SELECT_READ:
|
||||||
|
FD_SET(fd, &rss->rbits);
|
||||||
|
break;
|
||||||
|
case B_SELECT_WRITE:
|
||||||
|
FD_SET(fd, &rss->wbits);
|
||||||
|
break;
|
||||||
|
case B_SELECT_EXCEPTION:
|
||||||
|
FD_SET(fd, &rss->ebits);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
// wakeup select()
|
||||||
|
release_sem(rss->wakeup);
|
||||||
|
|
||||||
|
release_sem(rss->lock);
|
||||||
|
|
||||||
|
error:
|
||||||
|
delete_area(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
#if STAY_LOADED
|
||||||
|
static status_t keep_driver_loaded()
|
||||||
|
{
|
||||||
|
if ( g_stay_loaded_fd != -1 )
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
/* force the driver to stay loaded by opening himself */
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "keep_driver_loaded: internaly opening /dev/" NET_STACK_DRIVER_PATH " to stay loaded in memory...\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_stay_loaded_fd = open("/dev/" NET_STACK_DRIVER_PATH, 0);
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
if (g_stay_loaded_fd < 0)
|
||||||
|
dprintf(LOGID ERR "keep_driver_loaded: couldn't open(/dev/" NET_STACK_DRIVER_PATH")!\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static status_t unload_driver()
|
||||||
|
{
|
||||||
|
if ( g_stay_loaded_fd >= 0 )
|
||||||
|
{
|
||||||
|
int tmp_fd;
|
||||||
|
|
||||||
|
/* we need to set g_stay_loaded_fd to < 0 if we don't want
|
||||||
|
* the next close enter again in this case, and so on...
|
||||||
|
*/
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "unload_driver: unload requested.\n");
|
||||||
|
#endif
|
||||||
|
tmp_fd = g_stay_loaded_fd;
|
||||||
|
g_stay_loaded_fd = -1;
|
||||||
|
|
||||||
|
close(tmp_fd);
|
||||||
|
};
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
#endif /* STAY_LOADED */
|
||||||
|
|
||||||
|
#endif /* _KERNEL_MODE */
|
||||||
@@ -0,0 +1,852 @@
|
|||||||
|
/* net_userstack_driver - This file implements a very simple socket driver
|
||||||
|
** that is intended to act as an interface to the networking stack when
|
||||||
|
** it is loaded in userland.
|
||||||
|
** The communication is slow, and could probably be much better, but it's
|
||||||
|
** working, and that should be enough for now.
|
||||||
|
**
|
||||||
|
** Initial version by Axel Dörfler, [email protected]
|
||||||
|
** This file may be used under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//#include <OS.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <Drivers.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
#include "net_stack_driver.h"
|
||||||
|
#include "userland_ipc.h"
|
||||||
|
#include "lock.h"
|
||||||
|
#include "sys/sockio.h"
|
||||||
|
#include "sys/socket.h"
|
||||||
|
#include "net/if.h"
|
||||||
|
#include "sys/select.h"
|
||||||
|
|
||||||
|
/* these are missing from KernelExport.h ... */
|
||||||
|
#define B_SELECT_READ 1
|
||||||
|
#define B_SELECT_WRITE 2
|
||||||
|
#define B_SELECT_EXCEPTION 3
|
||||||
|
|
||||||
|
extern void notify_select_event(selectsync * sync, uint32 ref);
|
||||||
|
|
||||||
|
//*****************************************************/
|
||||||
|
// Debug output
|
||||||
|
//*****************************************************/
|
||||||
|
|
||||||
|
#define DEBUG_PREFIX "net_driver: "
|
||||||
|
#define __out dprintf
|
||||||
|
|
||||||
|
#define DEBUG 1
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define PRINT(x) { __out(DEBUG_PREFIX); __out x; }
|
||||||
|
#define REPORT_ERROR(status) __out(DEBUG_PREFIX "%s:%ld: %s\n",__FUNCTION__,__LINE__,strerror(status));
|
||||||
|
#define RETURN_ERROR(err) { status_t _status = err; if (_status < B_OK) REPORT_ERROR(_status); return _status;}
|
||||||
|
#define FATAL(x) { __out(DEBUG_PREFIX); __out x; }
|
||||||
|
#define INFORM(x) { __out(DEBUG_PREFIX); __out x; }
|
||||||
|
#define FUNCTION() __out(DEBUG_PREFIX "%s()\n",__FUNCTION__);
|
||||||
|
#define FUNCTION_START(x) { __out(DEBUG_PREFIX "%s() ",__FUNCTION__); __out x; }
|
||||||
|
// #define FUNCTION() ;
|
||||||
|
// #define FUNCTION_START(x) ;
|
||||||
|
#define D(x) {x;};
|
||||||
|
#else
|
||||||
|
#define PRINT(x) ;
|
||||||
|
#define REPORT_ERROR(status) ;
|
||||||
|
#define RETURN_ERROR(status) return status;
|
||||||
|
#define FATAL(x) { __out(DEBUG_PREFIX); __out x; }
|
||||||
|
#define INFORM(x) { __out(DEBUG_PREFIX); __out x; }
|
||||||
|
#define FUNCTION() ;
|
||||||
|
#define FUNCTION_START(x) ;
|
||||||
|
#define D(x) ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//*****************************************************/
|
||||||
|
// Structure definitions
|
||||||
|
//*****************************************************/
|
||||||
|
|
||||||
|
#ifndef DRIVER_NAME
|
||||||
|
# define DRIVER_NAME "net_stack_driver"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* wait one second when waiting on the stack */
|
||||||
|
#define STACK_TIMEOUT 1000000LL
|
||||||
|
|
||||||
|
typedef void (*notify_select_event_function)(selectsync * sync, uint32 ref);
|
||||||
|
|
||||||
|
// this struct will store one select() event to monitor per thread
|
||||||
|
typedef struct selecter {
|
||||||
|
struct selecter * next;
|
||||||
|
thread_id thread;
|
||||||
|
int event;
|
||||||
|
selectsync * sync;
|
||||||
|
uint32 ref;
|
||||||
|
} selecter;
|
||||||
|
|
||||||
|
|
||||||
|
// the cookie we attach to each file descriptor opened on our driver entry
|
||||||
|
typedef struct {
|
||||||
|
port_id local_port;
|
||||||
|
port_id remote_port;
|
||||||
|
area_id area;
|
||||||
|
|
||||||
|
sem_id command_sem;
|
||||||
|
net_command *commands;
|
||||||
|
int32 command_index;
|
||||||
|
int32 nb_commands;
|
||||||
|
|
||||||
|
sem_id selecters_lock; // protect the selecters linked-list
|
||||||
|
selecter * selecters; // the select()'ers lists (thread-aware)
|
||||||
|
} net_stack_cookie;
|
||||||
|
|
||||||
|
|
||||||
|
//*****************************************************/
|
||||||
|
// Prototypes
|
||||||
|
//*****************************************************/
|
||||||
|
|
||||||
|
/* device hooks */
|
||||||
|
static status_t net_stack_open(const char * name, uint32 flags, void ** cookie);
|
||||||
|
static status_t net_stack_close(void * cookie);
|
||||||
|
static status_t net_stack_free_cookie(void * cookie);
|
||||||
|
static status_t net_stack_control(void * cookie, uint32 msg,void * data, size_t datalen);
|
||||||
|
static status_t net_stack_read(void * cookie, off_t pos, void * data, size_t * datalen);
|
||||||
|
static status_t net_stack_write(void * cookie, off_t pos, const void * data, size_t * datalen);
|
||||||
|
static status_t net_stack_select(void *cookie, uint8 event, uint32 ref, selectsync *sync);
|
||||||
|
static status_t net_stack_deselect(void *cookie, uint8 event, selectsync *sync);
|
||||||
|
// static status_t net_stack_readv(void * cookie, off_t pos, const iovec * vec, size_t count, size_t * len);
|
||||||
|
// static status_t net_stack_writev(void * cookie, off_t pos, const iovec * vec, size_t count, size_t * len);
|
||||||
|
|
||||||
|
/* select() support */
|
||||||
|
static int32 socket_event_listener(void * data);
|
||||||
|
static void on_socket_event(void *socket, uint32 event, void *cookie);
|
||||||
|
static void r5_notify_select_event(selectsync *sync, uint32 ref);
|
||||||
|
|
||||||
|
/* command queue */
|
||||||
|
static status_t init_connection(void **cookie);
|
||||||
|
static void shutdown_connection(net_stack_cookie *nsc);
|
||||||
|
static net_command *get_command(net_stack_cookie *nsc, int32 *index);
|
||||||
|
static status_t execute_command(net_stack_cookie *nsc, int32 op, void *data, uint32 length);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Global variables
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const char * g_device_names_list[] = {
|
||||||
|
NET_STACK_DRIVER_PATH,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
device_hooks g_net_stack_driver_hooks =
|
||||||
|
{
|
||||||
|
net_stack_open, /* -> open entry point */
|
||||||
|
net_stack_close, /* -> close entry point */
|
||||||
|
net_stack_free_cookie, /* -> free entry point */
|
||||||
|
net_stack_control, /* -> control entry point */
|
||||||
|
net_stack_read, /* -> read entry point */
|
||||||
|
net_stack_write, /* -> write entry point */
|
||||||
|
net_stack_select, /* -> select entry point */
|
||||||
|
net_stack_deselect, /* -> deselect entry point */
|
||||||
|
NULL, /* -> readv entry pint */
|
||||||
|
NULL /* -> writev entry point */
|
||||||
|
};
|
||||||
|
|
||||||
|
// by default, assert we can use kernel select() support
|
||||||
|
notify_select_event_function g_nse = notify_select_event;
|
||||||
|
thread_id g_socket_event_listener_thread = -1;
|
||||||
|
port_id g_socket_event_listener_port = -1;
|
||||||
|
|
||||||
|
port_id g_stack_port = -1;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Driver API calls
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
_EXPORT int32 api_version = B_CUR_DRIVER_API_VERSION;
|
||||||
|
|
||||||
|
|
||||||
|
_EXPORT status_t
|
||||||
|
init_hardware(void)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_EXPORT status_t
|
||||||
|
init_driver (void)
|
||||||
|
{
|
||||||
|
thread_id thread;
|
||||||
|
port_id port;
|
||||||
|
|
||||||
|
FUNCTION();
|
||||||
|
|
||||||
|
port = create_port(32, "socket_event_listener");
|
||||||
|
if (port < B_OK)
|
||||||
|
return port;
|
||||||
|
set_port_owner(port, B_SYSTEM_TEAM);
|
||||||
|
|
||||||
|
thread = spawn_kernel_thread(socket_event_listener, "socket_event_listener",
|
||||||
|
B_NORMAL_PRIORITY, NULL);
|
||||||
|
if (thread < B_OK) {
|
||||||
|
delete_port(port);
|
||||||
|
return thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
g_socket_event_listener_thread = thread;
|
||||||
|
g_socket_event_listener_port = port;
|
||||||
|
|
||||||
|
return resume_thread(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_EXPORT void
|
||||||
|
uninit_driver (void)
|
||||||
|
{
|
||||||
|
status_t dummy;
|
||||||
|
|
||||||
|
FUNCTION();
|
||||||
|
|
||||||
|
delete_port(g_socket_event_listener_port);
|
||||||
|
wait_for_thread(g_socket_event_listener_thread, &dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_EXPORT const char **
|
||||||
|
publish_devices()
|
||||||
|
{
|
||||||
|
FUNCTION();
|
||||||
|
return g_device_names_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_EXPORT device_hooks *
|
||||||
|
find_device(const char *deviceName)
|
||||||
|
{
|
||||||
|
FUNCTION();
|
||||||
|
return &g_net_stack_driver_hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
//*****************************************************/
|
||||||
|
// Device hooks
|
||||||
|
//*****************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
net_stack_open(const char *name, uint32 flags, void ** cookie)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc;
|
||||||
|
|
||||||
|
status_t status = init_connection(cookie);
|
||||||
|
if (status < B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
nsc = *cookie;
|
||||||
|
|
||||||
|
status = execute_command(nsc, NET_STACK_OPEN, &flags, sizeof(uint32));
|
||||||
|
if (status < B_OK)
|
||||||
|
net_stack_free_cookie(nsc);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
net_stack_close(void *cookie)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
if (nsc == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
// we don't care here if the stack isn't alive anymore -
|
||||||
|
// from the kernel's point of view, the device can always
|
||||||
|
// be closed
|
||||||
|
execute_command(nsc, NET_STACK_CLOSE, NULL, 0);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
net_stack_free_cookie(void *cookie)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
if (nsc == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
shutdown_connection(nsc);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
net_stack_control(void *cookie, uint32 op, void *data, size_t length)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
|
||||||
|
//FUNCTION_START(("cookie = %p, op = %lx, data = %p, length = %ld\n", cookie, op, data, length));
|
||||||
|
|
||||||
|
if (nsc == NULL)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case NET_STACK_SELECT: {
|
||||||
|
struct select_args *args = (struct select_args *) data;
|
||||||
|
|
||||||
|
// if we get this call via ioctl() we are obviously called from an
|
||||||
|
// R5 compatible libnet: we are using the r5 kernel select() call,
|
||||||
|
// So, we can't use the kernel notify_select_event(), but our own implementation!
|
||||||
|
g_nse = r5_notify_select_event;
|
||||||
|
return net_stack_select(cookie, (args->ref & 0x0F), args->ref, args->sync);
|
||||||
|
}
|
||||||
|
case NET_STACK_DESELECT: {
|
||||||
|
struct select_args * args = (struct select_args *) data;
|
||||||
|
return net_stack_deselect(cookie, (args->ref & 0x0F), args->sync);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return execute_command(nsc, op, data, -1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
net_stack_read(void *cookie, off_t pos, void *buffer, size_t *length)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
struct data_xfer_args args;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
memset(&args, 0, sizeof(args));
|
||||||
|
args.data = buffer;
|
||||||
|
args.datalen = *length;
|
||||||
|
|
||||||
|
status = execute_command(nsc, NET_STACK_RECV, &args, sizeof(args));
|
||||||
|
if (status > 0) {
|
||||||
|
*length = status;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
net_stack_write(void *cookie, off_t pos, const void *buffer, size_t *length)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
struct data_xfer_args args;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
memset(&args, 0, sizeof(args));
|
||||||
|
args.data = (void *) buffer;
|
||||||
|
args.datalen = *length;
|
||||||
|
|
||||||
|
status = execute_command(nsc, NET_STACK_SEND, &args, sizeof(args));
|
||||||
|
if (status > 0) {
|
||||||
|
*length = status;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
net_stack_select(void *cookie, uint8 event, uint32 ref, selectsync *sync)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
struct notify_socket_event_args args;
|
||||||
|
selecter * s;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
FUNCTION_START(("cookie = %p, event = %d, ref = %lx, sync =%p\n", cookie, event, ref, sync));
|
||||||
|
|
||||||
|
s = (selecter *) malloc(sizeof(selecter));
|
||||||
|
if (!s)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
|
||||||
|
// lock the selecters list
|
||||||
|
status = acquire_sem(nsc->selecters_lock);
|
||||||
|
if (status != B_OK) {
|
||||||
|
free(s);
|
||||||
|
return status;
|
||||||
|
};
|
||||||
|
|
||||||
|
s->thread = find_thread(NULL); // current thread id
|
||||||
|
s->event = event;
|
||||||
|
s->sync = sync;
|
||||||
|
s->ref = ref;
|
||||||
|
|
||||||
|
// add it to selecters list
|
||||||
|
s->next = nsc->selecters;
|
||||||
|
nsc->selecters = s;
|
||||||
|
|
||||||
|
// unlock the selecters list
|
||||||
|
release_sem(nsc->selecters_lock);
|
||||||
|
|
||||||
|
// Tell the net_server to notify event(s) for this socket
|
||||||
|
memset(&args, 0, sizeof(args));
|
||||||
|
args.notify_port = g_socket_event_listener_port;
|
||||||
|
args.cookie = cookie;;
|
||||||
|
|
||||||
|
return execute_command(nsc, NET_STACK_NOTIFY_SOCKET_EVENT, &args, sizeof(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
net_stack_deselect(void *cookie, uint8 event, selectsync *sync)
|
||||||
|
{
|
||||||
|
net_stack_cookie *nsc = (net_stack_cookie *) cookie;
|
||||||
|
selecter * previous;
|
||||||
|
selecter * s;
|
||||||
|
thread_id current_thread;
|
||||||
|
status_t status;
|
||||||
|
|
||||||
|
FUNCTION_START(("cookie = %p, event = %d, sync =%p\n", cookie, event, sync));
|
||||||
|
|
||||||
|
if (!nsc)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
current_thread = find_thread(NULL);
|
||||||
|
|
||||||
|
// lock the selecters list
|
||||||
|
status = acquire_sem(nsc->selecters_lock);
|
||||||
|
if (status != B_OK)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
previous = NULL;
|
||||||
|
s = nsc->selecters;
|
||||||
|
while (s) {
|
||||||
|
if (s->thread == current_thread &&
|
||||||
|
s->event == event && s->sync == sync)
|
||||||
|
// selecter found!
|
||||||
|
break;
|
||||||
|
|
||||||
|
previous = s;
|
||||||
|
s = s->next;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (s != NULL) {
|
||||||
|
// remove it from selecters list
|
||||||
|
if (previous)
|
||||||
|
previous->next = s->next;
|
||||||
|
else
|
||||||
|
nsc->selecters = s->next;
|
||||||
|
free(s);
|
||||||
|
};
|
||||||
|
|
||||||
|
status = B_OK;
|
||||||
|
if (nsc->selecters == NULL) {
|
||||||
|
struct notify_socket_event_args args;
|
||||||
|
|
||||||
|
// Selecters list is empty: no need to monitor socket events anymore
|
||||||
|
// Tell the net_server to stop notifying event(s) for this socket
|
||||||
|
memset(&args, 0, sizeof(args));
|
||||||
|
args.notify_port = -1; // stop notifying
|
||||||
|
args.cookie = cookie; // for sanity check
|
||||||
|
status = execute_command(nsc, NET_STACK_NOTIFY_SOCKET_EVENT, NULL, -1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// unlock the selecters list
|
||||||
|
release_sem(nsc->selecters_lock);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
//*****************************************************/
|
||||||
|
// select() support
|
||||||
|
//*****************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is started as separate thread at init_driver() time to wait for
|
||||||
|
socket event notification.
|
||||||
|
|
||||||
|
One port to listen them all.
|
||||||
|
One port to find them.
|
||||||
|
One port to get them and in [r5_]notify_select_event() send them.
|
||||||
|
|
||||||
|
Okay, I should stop watch this movie *now*.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int32 socket_event_listener(void * data)
|
||||||
|
{
|
||||||
|
struct socket_event_data sed;
|
||||||
|
int32 msg;
|
||||||
|
ssize_t bytes;
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
bytes = read_port(g_socket_event_listener_port, &msg, &sed, sizeof(sed));
|
||||||
|
if (bytes < B_OK)
|
||||||
|
return bytes;
|
||||||
|
|
||||||
|
if (msg == NET_STACK_SOCKET_EVENT_NOTIFICATION)
|
||||||
|
// yep, we pass a NULL "socket" pointer here, but don't worry, we don't use anyway
|
||||||
|
on_socket_event(NULL, sed.event, sed.cookie);
|
||||||
|
};
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void on_socket_event(void * socket, uint32 event, void * cookie)
|
||||||
|
{
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) cookie;
|
||||||
|
selecter * s;
|
||||||
|
|
||||||
|
if (!nsc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FUNCTION_START(("socket = %p, event = %ld, cookie = %p\n", socket, event, cookie));
|
||||||
|
|
||||||
|
// lock the selecters list
|
||||||
|
if (acquire_sem(nsc->selecters_lock) != B_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
s = nsc->selecters;
|
||||||
|
while (s) {
|
||||||
|
if (s->event == event)
|
||||||
|
// notify this selecter (thread/event pair)
|
||||||
|
g_nse(s->sync, s->ref);
|
||||||
|
s = s->next;
|
||||||
|
};
|
||||||
|
|
||||||
|
// unlock the selecters list
|
||||||
|
release_sem(nsc->selecters_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Under vanilla R5, we can't use the kernel notify_select_event(),
|
||||||
|
as select() kernel implementation is too buggy to be usefull.
|
||||||
|
So, here is our own notify_select_event() implementation, the driver-side pair
|
||||||
|
of the our libnet.so select() implementation...
|
||||||
|
*/
|
||||||
|
static void r5_notify_select_event(selectsync * sync, uint32 ref)
|
||||||
|
{
|
||||||
|
area_id area;
|
||||||
|
struct r5_selectsync * rss;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
FUNCTION_START(("sync = %p, ref = %lx\n", sync, ref));
|
||||||
|
|
||||||
|
rss = NULL;
|
||||||
|
area = clone_area("r5_selectsync_area (driver)", (void **) &rss,
|
||||||
|
B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA, (area_id) sync);
|
||||||
|
if (area < B_OK) {
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "r5_notify_select_event: clone_area(%d) failed -> %d!\n", (area_id) sync, area);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if SHOW_INSANE_DEBUGGING
|
||||||
|
dprintf(LOGID "r5_selectsync at %p (area %ld, clone from %ld):\n"
|
||||||
|
"lock %ld\n"
|
||||||
|
"wakeup %ld\n", rss, area, (area_id) sync, rss->lock, rss->wakeup);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (acquire_sem(rss->lock) != B_OK)
|
||||||
|
// if we can't (anymore?) lock the shared r5_selectsync, select() party is done
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
fd = ref >> 8;
|
||||||
|
switch (ref & 0xFF) {
|
||||||
|
case B_SELECT_READ:
|
||||||
|
FD_SET(fd, &rss->rbits);
|
||||||
|
break;
|
||||||
|
case B_SELECT_WRITE:
|
||||||
|
FD_SET(fd, &rss->wbits);
|
||||||
|
break;
|
||||||
|
case B_SELECT_EXCEPTION:
|
||||||
|
FD_SET(fd, &rss->ebits);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
// wakeup select()
|
||||||
|
release_sem(rss->wakeup);
|
||||||
|
|
||||||
|
release_sem(rss->lock);
|
||||||
|
|
||||||
|
error:
|
||||||
|
delete_area(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
//*****************************************************/
|
||||||
|
// The Command Queue
|
||||||
|
//*****************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
static net_command *
|
||||||
|
get_command(net_stack_cookie *nsc, int32 *index)
|
||||||
|
{
|
||||||
|
int32 i, count = 0;
|
||||||
|
net_command *command;
|
||||||
|
|
||||||
|
while (count < nsc->nb_commands*2) {
|
||||||
|
i = atomic_add(&nsc->command_index,1) & (nsc->nb_commands - 1);
|
||||||
|
command = nsc->commands + i;
|
||||||
|
|
||||||
|
if (command->op == 0) {
|
||||||
|
// command is free to use
|
||||||
|
*index = i;
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
get_area_from_address(net_area_info *info, void *data)
|
||||||
|
{
|
||||||
|
area_info areaInfo;
|
||||||
|
|
||||||
|
if (data == NULL)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
info->id = area_for(data);
|
||||||
|
if (info->id < B_OK)
|
||||||
|
return info->id;
|
||||||
|
|
||||||
|
if (get_area_info(info->id,&areaInfo) != B_OK)
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
info->offset = areaInfo.address;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_command_areas(net_command *command)
|
||||||
|
{
|
||||||
|
void *data = command->data;
|
||||||
|
if (data == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (get_area_from_address(&command->area[0],data) < B_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (command->op) {
|
||||||
|
case NET_STACK_GETSOCKOPT:
|
||||||
|
case NET_STACK_SETSOCKOPT:
|
||||||
|
{
|
||||||
|
struct sockopt_args *sockopt = (struct sockopt_args *)data;
|
||||||
|
|
||||||
|
get_area_from_address(&command->area[1],sockopt->optval);
|
||||||
|
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;
|
||||||
|
|
||||||
|
get_area_from_address(&command->area[1],args->addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NET_STACK_RECV:
|
||||||
|
case NET_STACK_SEND:
|
||||||
|
{
|
||||||
|
struct data_xfer_args *args = (struct data_xfer_args *)data;
|
||||||
|
get_area_from_address(&command->area[1],args->data);
|
||||||
|
get_area_from_address(&command->area[2],args->addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NET_STACK_RECVFROM:
|
||||||
|
case NET_STACK_SENDTO:
|
||||||
|
{
|
||||||
|
struct msghdr *mh = (struct msghdr *)data;
|
||||||
|
get_area_from_address(&command->area[1],mh->msg_name);
|
||||||
|
get_area_from_address(&command->area[2],mh->msg_iov);
|
||||||
|
get_area_from_address(&command->area[3],mh->msg_control);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NET_STACK_ACCEPT:
|
||||||
|
{
|
||||||
|
struct accept_args *args = (struct accept_args *)data;
|
||||||
|
/* accept_args.cookie is always in the address space of the server */
|
||||||
|
get_area_from_address(&command->area[1],args->addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NET_STACK_SYSCTL:
|
||||||
|
{
|
||||||
|
struct sysctl_args *sysctl = (struct sysctl_args *)data;
|
||||||
|
|
||||||
|
get_area_from_address(&command->area[1],sysctl->name);
|
||||||
|
get_area_from_address(&command->area[2],sysctl->oldp);
|
||||||
|
get_area_from_address(&command->area[3],sysctl->oldlenp);
|
||||||
|
get_area_from_address(&command->area[4],sysctl->newp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OSIOCGIFCONF:
|
||||||
|
case SIOCGIFCONF:
|
||||||
|
{
|
||||||
|
struct ifconf *ifc = (struct ifconf *)data;
|
||||||
|
|
||||||
|
get_area_from_address(&command->area[1],ifc->ifc_buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
execute_command(net_stack_cookie *nsc, int32 op, void *data, uint32 length)
|
||||||
|
{
|
||||||
|
uint32 command_index;
|
||||||
|
net_command *command = get_command(nsc, (long *) &command_index);
|
||||||
|
int32 max_tries = 200;
|
||||||
|
status_t status;
|
||||||
|
ssize_t bytes;
|
||||||
|
|
||||||
|
if (command == NULL) {
|
||||||
|
FATAL(("execute: command queue is full\n"));
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(command, 0, sizeof(net_command));
|
||||||
|
|
||||||
|
command->op = op;
|
||||||
|
command->data = data;
|
||||||
|
set_command_areas(command);
|
||||||
|
|
||||||
|
bytes = write_port(nsc->remote_port, command_index, NULL, 0);
|
||||||
|
if (bytes < B_OK) {
|
||||||
|
FATAL(("execute %ld: couldn't contact stack (id = %ld): %s\n",op, nsc->remote_port, strerror(bytes)));
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're closing the connection, there is no need to
|
||||||
|
// wait for a result
|
||||||
|
if (op == NET_STACK_CLOSE)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// wait until we get the results back from our command
|
||||||
|
if ((status = acquire_sem(nsc->command_sem)) == B_OK) {
|
||||||
|
if (command->op != 0) {
|
||||||
|
if (--max_tries <= 0) {
|
||||||
|
FATAL(("command is not freed after 200 tries!\n"));
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
release_sem(nsc->command_sem);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return command->result;
|
||||||
|
}
|
||||||
|
FATAL(("command couldn't be executed: %s\n", strerror(status)));
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
init_connection(void **cookie)
|
||||||
|
{
|
||||||
|
net_connection connection;
|
||||||
|
ssize_t bytes;
|
||||||
|
int32 msg;
|
||||||
|
|
||||||
|
net_stack_cookie * nsc = (net_stack_cookie *) malloc(sizeof(net_stack_cookie));
|
||||||
|
if (nsc == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
// create a new port and get a connection from the stack
|
||||||
|
|
||||||
|
nsc->local_port = create_port(CONNECTION_QUEUE_LENGTH, "net_driver connection");
|
||||||
|
if (nsc->local_port < B_OK) {
|
||||||
|
FATAL(("open: couldn't create port: %s\n", strerror(nsc->local_port)));
|
||||||
|
free(cookie);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
set_port_owner(nsc->local_port, B_SYSTEM_TEAM);
|
||||||
|
|
||||||
|
bytes = write_port(g_stack_port, NET_STACK_NEW_CONNECTION, &nsc->local_port, sizeof(port_id));
|
||||||
|
if (bytes == B_BAD_PORT_ID) {
|
||||||
|
g_stack_port = find_port(NET_STACK_PORTNAME);
|
||||||
|
PRINT(("try to get net_server's port id: %ld\n", g_stack_port));
|
||||||
|
bytes = write_port(g_stack_port, NET_STACK_NEW_CONNECTION, &nsc->local_port, sizeof(port_id));
|
||||||
|
}
|
||||||
|
if (bytes < B_OK) {
|
||||||
|
FATAL(("open: couldn't contact stack: %s\n",strerror(bytes)));
|
||||||
|
delete_port(nsc->local_port);
|
||||||
|
free(nsc);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = read_port_etc(nsc->local_port, &msg, &connection, sizeof(net_connection), B_TIMEOUT,STACK_TIMEOUT);
|
||||||
|
if (bytes < B_OK) {
|
||||||
|
FATAL(("open: didn't hear back from stack: %s\n",strerror(bytes)));
|
||||||
|
delete_port(nsc->local_port);
|
||||||
|
free(nsc);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
if (msg != NET_STACK_NEW_CONNECTION) {
|
||||||
|
FATAL(("open: received wrong answer: %ld\n",msg));
|
||||||
|
delete_port(nsc->local_port);
|
||||||
|
free(nsc);
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up communication channels
|
||||||
|
|
||||||
|
// ToDo: close server connection if anything fails
|
||||||
|
// -> could also be done by the server, if it doesn't receive NET_STACK_OPEN
|
||||||
|
|
||||||
|
nsc->remote_port = connection.port;
|
||||||
|
nsc->command_sem = connection.commandSemaphore;
|
||||||
|
nsc->area = clone_area("net connection buffer", (void **) &nsc->commands,
|
||||||
|
B_CLONE_ADDRESS, B_READ_AREA | B_WRITE_AREA, connection.area);
|
||||||
|
if (nsc->area < B_OK) {
|
||||||
|
FATAL(("could clone command queue: %s\n", strerror(nsc->area)));
|
||||||
|
delete_port(nsc->local_port);
|
||||||
|
free(nsc);
|
||||||
|
return nsc->area;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsc->nb_commands = connection.numCommands;
|
||||||
|
nsc->command_index = 0;
|
||||||
|
|
||||||
|
nsc->selecters_lock = create_sem(1, "socket_selecters_lock");
|
||||||
|
nsc->selecters = NULL;
|
||||||
|
|
||||||
|
*cookie = nsc;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
shutdown_connection(net_stack_cookie *nsc)
|
||||||
|
{
|
||||||
|
selecter * s;
|
||||||
|
|
||||||
|
delete_area(nsc->area);
|
||||||
|
delete_port(nsc->local_port);
|
||||||
|
|
||||||
|
// free the selecters list
|
||||||
|
delete_sem(nsc->selecters_lock);
|
||||||
|
s = nsc->selecters;
|
||||||
|
while (s) {
|
||||||
|
selecter * tmp = s;
|
||||||
|
s = s->next;
|
||||||
|
free(tmp);
|
||||||
|
};
|
||||||
|
|
||||||
|
free(nsc);
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user