#include <kernel/kernel.h>
#include <kernel/port.h>
#include <errno.h>
#include <wchar.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
SEMAPHORE(server_sem);
port_t *server_first, *server_last;
bool portRequest(device_t* dev, request_t* req);
device_t port_dev =
{
NULL,
portRequest,
NULL, NULL,
NULL
};
bool portRequest(device_t* dev, request_t* req)
{
port_t *port, *remote;
dword *params;
assert(dev == &port_dev);
switch (req->code)
{
case FS_WRITE:
port = (port_t*) req->params.fs_write.fd;
if ((port->state & PORT_CONNECTED) == 0 ||
port->u.client.remote == NULL)
{
wprintf(L"%s: not connected\n", port->name);
req->code = EINVALID;
return false;
}
//wprintf(L"port: write to %s->%s\n",
//port->name, port->u.client.remote->name);
remote = port->u.client.remote;
memcpy(remote->u.client.buffer + remote->u.client.buffer_length,
(void*) req->params.fs_write.buffer,
req->params.fs_write.length);
remote->u.client.buffer_length += req->params.fs_write.length;
hndSignal(remote, remote->u.client.buffer_length > 0);
hndSignal(req->event, true);
return true;
case FS_READ:
port = (port_t*) req->params.fs_read.fd;
if ((port->state & PORT_CONNECTED) == 0 ||
port->u.client.remote == NULL)
{
wprintf(L"%s: not connected\n", port->name);
req->code = EINVALID;
return false;
}
req->params.fs_read.length =
min(req->params.fs_read.length, port->u.client.buffer_length);
memcpy((void*) req->params.fs_read.buffer,
port->u.client.buffer,
req->params.fs_read.length);
port->u.client.buffer_length -= req->params.fs_read.length;
memmove(port->u.client.buffer,
port->u.client.buffer + req->params.fs_read.length,
port->u.client.buffer_length);
hndSignal(port, port->u.client.buffer_length > 0);
//wprintf(L"port: read from %s->%s\n",
//port->name, port->u.client.remote->name);
hndSignal(req->event, true);
return true;
case FS_IOCTL:
port = (port_t*) req->params.fs_ioctl.fd;
params = req->params.fs_ioctl.buffer;
switch (req->params.fs_ioctl.code)
{
case 0:
params[0] = port->u.client.buffer_length;
req->params.fs_ioctl.length = sizeof(dword);
hndSignal(req->event, true);
return true;
default:
req->params.fs_ioctl.length = 0;
break;
}
}
req->code = ENOTIMPL;
return false;
}
port_t* portFindServer(const wchar_t* name)
{
port_t *port;
for (port = server_first; port; port = port->next)
if (wcsicmp(port->name, name) == 0)
return port;
return NULL;
}
port_t* portCreate(process_t* owner, const wchar_t* name)
{
port_t* port;
port = hndAlloc(sizeof(port_t), owner);
assert(port != NULL);
port->file.fsd = &port_dev;
port->file.pos = 0;
port->owner = owner;
port->state = 0;
if (name)
port->name = wcsdup(name);
else
port->name = wcsdup(L"");
//wprintf(L"portCreate: created port %p = %s\n", port, name);
return port;
}
void portDelete(port_t* port)
{
port_t *server, *next;
for (server = server_first; server; server = next)
{
next = server->next;
if (server == port)
{
if (server->next)
server->next->prev = server->prev;
if (server->prev)
server->prev->next = server->next;
if (server_first == server)
server_first = server->next;
if (server_last == server)
server_last = server->prev;
break;
}
}
free(port->name);
hndFree(port);
}
bool portListen(port_t* port)
{
if (port->state != 0)
return false;
port->state |= PORT_LISTENING;
port->u.server.connect_first = port->u.server.connect_last = NULL;
port->u.server.num_clients = 0;
semInit(&port->u.server.connect);
//wprintf(L"portListen: %p = %s\n", port, port->name);
semAcquire(&server_sem);
if (server_last)
server_last->next = port;
if (server_first == NULL)
server_first = port;
port->next = NULL;
port->prev = server_last;
server_last = port;
semRelease(&server_sem);
return true;
}
bool portConnect(port_t* port, const wchar_t* remote)
{
port_t *remote_port;
if (port->state != 0)
return false;
remote_port = portFindServer(remote);
if (remote_port == NULL)
{
//wprintf(L"%s: remote port not found\n", remote);
errno = ENOTFOUND;
return false;
}
if ((remote_port->state & PORT_LISTENING) == 0)
{
wprintf(L"%s: remote port not listening\n", remote);
errno = EINVALID;
return false;
}
//wprintf(L"portConnect: %s to %s\n", port->name, remote_port->name);
port->u.client.remote = remote_port;
port->u.client.buffer_length = 0;
//wprintf(L"Acquiring sem...");
semAcquire(&port->u.server.connect);
//wprintf(L"done\n");
if (remote_port->u.server.connect_last)
remote_port->u.server.connect_last->next = port;
remote_port->u.server.connect_last = port;
if (remote_port->u.server.connect_first == NULL)
remote_port->u.server.connect_first = port;
port->next = NULL;
port->prev = remote_port->u.server.connect_last;
port->state = PORT_PENDING_CONNECT;
hndSignal(remote_port, true);
hndSignal(port, false);
semRelease(&port->u.server.connect);
//wprintf(L"portConnect: finished\n");
return true;
}
port_t* portAccept(port_t* server)
{
port_t *client, *port;
wchar_t name[50];
/* Server port must be listening and it must have a client waiting */
if ((server->state & PORT_LISTENING) == 0 ||
server->u.server.connect_first == NULL)
{
errno = EINVALID;
return NULL;
}
/* Get the first waiting client from the queue */
client = server->u.server.connect_first;
//wprintf(L"portAccept: %s, %s\n", server->name, client->name);
/* Acquire the server's connection semaphore */
//wprintf(L"Acquiring sem...");
semAcquire(&server->u.server.connect);
//wprintf(L"done\n");
/* Unlink the client from the server's queue */
if (client->next)
client->next->prev = NULL;
client->next = client->prev = NULL;
server->u.server.connect_first = client->next;
/* Create a new thread to connect with the client */
swprintf(name, L"%s_client%d", server->name, ++server->u.server.num_clients);
port = portCreate(server->owner, name);
/*
* Connect the new port with the client --
* we must do manually what portConnect does
*/
port->state = PORT_CONNECTED;
port->u.client.remote = client;
port->u.client.buffer_length = 0;
/* Connect the client with the new port */
client->state = PORT_CONNECTED;
client->u.client.remote = port;
/* Re-signal the server if it has another client waiting */
hndSignal(server, server->u.server.connect_first != NULL);
/* Signal the client because it is now connected */
hndSignal(client, true);
semRelease(&server->u.server.connect);
//wprintf(L"portAccept: finished\n");
/* Return the port connected to the client */
return port;
}