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:
parent
698b29d4d2
commit
0fce772df1
@ -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 common ;
|
||||
SubInclude OBOS_TOP src add-ons kernel drivers random ;
|
||||
SubInclude OBOS_TOP src add-ons kernel drivers net ;
|
||||
|
||||
|
23
src/add-ons/kernel/drivers/network/stack/Jamfile
Normal file
23
src/add-ons/kernel/drivers/network/stack/Jamfile
Normal file
@ -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 ;
|
||||
}
|
800
src/add-ons/kernel/drivers/network/stack/net_stack_driver.c
Normal file
800
src/add-ons/kernel/drivers/network/stack/net_stack_driver.c
Normal file
@ -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 */
|
852
src/add-ons/kernel/drivers/network/stack/net_userstack_driver.c
Normal file
852
src/add-ons/kernel/drivers/network/stack/net_userstack_driver.c
Normal file
@ -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, axeld@pinc-software.de
|
||||
** 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user