📄 serialrpc.c
字号:
/*
*+-------------------------------------------------------------------+
*| Copyright (c) 1999,2000 TriMedia Technologies Inc. |
*| |
*| This software is furnished under a license and may only be used |
*| and copied in accordance with the terms and conditions of such a |
*| license and with the inclusion of this copyright notice. This |
*| software or any other copies of this software may not be provided |
*| or otherwise made available to any other person. The ownership |
*| and title of this software is not transferred. |
*| |
*| The information in this software is subject to change without |
*| any prior notice and should not be construed as a commitment by |
*| TriMedia Technologies. |
*| |
*| This code and information is provided "as is" without any |
*| warranty of any kind, either expressed or implied, including but |
*| not limited to the implied warranties of merchantability and/or |
*| fitness for any particular purpose. |
*+-------------------------------------------------------------------+
*
*
* Module name : SerialRPC.c 1.11
*
* Last update : 10:22:13 - 99/06/14
*
* Title : Serial communication driver for ANSI
* I/O, target side
*
* Description : This file is a serial driver that
* completes the generic serial host
* library that can be used for
* dispatching I/O via a serial form of
* communication to a corresponding rpc
* server. Such a server must have been
* completed with the host counterpar of
* this serial driver.
*
* This particular serial driver
* communicates with the serial TM-1
* application via tcp/ip, using a socket
* as serial connection. During
* initialization, this driver waits until
* a serving host connects to a server
* port. This port is currently hardcoded
* (see function reader_task_body, below),
* but this is easy to change.
*
* Starting a TM-1 application and its
* serial server is to be performed using
* the following steps:
*
* 1) start the TM-1 application. This
* application is to be linked with the
* target counterpart of this serial
* driver, and has to have network access.
*
* 2) start the serial server on a PC or
* Unix machine that can reach the TM-1
* application via tcp/ip. Stdin,
* stdout/stderr of the TM-1 application
* will be mapped to the stdin,
* stdout/stderr of the server process (on
* PC or Unix), and file I/O will be
* effectively performed by this same
* server process.
*
* This communication driver is
* implemented on top of the pSOS Network
* Architecture component (pNA), and hence
* can *only* be used for psos-based
* applications that have network access.
*
* Apart from this user visible aspect,
* using pNA introduces the following
* complications, which make the target
* driver somewhat more complex than its
* host counterpart:
*
* 1) I/O initialization, which is
* normally performed at startup of * the
* Trimedia runtime support, has to * be
* postponed until pNA is fully * active.
* That is, in the root task. * This
* postponing is achieved by * disabling
* the standard I/O support * in function
* SerialRPC_init, and * doing the actual
* initialization in a * new function
* SerialRPC_start. * SerialRPC_start is
* to be explicitly * called from the root
* task when it * wants to do I/O, as in
*
* void root()
* {
* SerialRPC_start();
* ...
*
* 2) In pSOS/pNA, a socket can only be
* used by the task that created it. For
* other tasks, synonyms have to be
* created by means of function
* shr_socket. For this reason, a reader-
* and a writer task is created to do the
* actual socket communication.
* Application tasks that want to do I/O
* communicate with these tasks using
* message queues.
*
* Some implementation notes:
*
* 1) The initialization routine creates
* both queues first, and then both tasks.
* The initialization routine awaits until
* the reader task starts serving; this
* reader task will await the writer task,
* and then create both sockets before it
* releases the initialization routine.
* This synchronization will prevent race
* conditions during startup.
*
* 2) Written data (to the host) is
* accumulated into packets of a specific
* size, when possible. This avoids the
* sending overhead of small data buffers.
* Buffering is done in a buffer
* 'mtu_buffer'. The packet size assumes
* ethernet here. Buffering output data
* also keeps the initial endian probe
* message to the host (which is sent
* during RTS initialization) until pNA is
* up and running.
*/
/*--------------------------- Includes ------------------------------*/
#include "errno.h"
#include <stdio.h>
#include "pna.h"
#include "psos.h"
#include <tmlib/tmtypes.h>
#include <assert.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <tmlib/AppSem.h>
#include <tmlib/dprintf.h>
/*
* Avoid redefinition in fcntl.h
* (clash with pna definition):
*/
#undef FREAD
#undef FWRITE
#include <fcntl.h>
#include "tmlib/HostCall.h"
#include "tmlib/AppModel.h"
/*--------------------------- Definitions ---------------------------*/
static ULONG reader_task;
static ULONG reader_queue; /* for sending read requests to reader task */
static Int reader_socket; /* for actual reading by reader task */
static ULONG writer_task;
static ULONG writer_queue; /* for sending write requests to writer task */
static Int writer_socket; /* for actual writing by writer task */
#define MTU 1500 /* maximum transmission unit (assumes ethernet connection) */
static Char mtu_buffer[ MTU ];
static Int mtu_pos;
static struct _AppSem_Semaphore buffer_lock= _AppSem_INITIALISED_SEM;
#define SYNC_BUFF 0
#define READ_BUFF 1
#define WRITE_BUFF 2
typedef struct {
Int kind;
AppModel_AppId sender;
Pointer buffer;
Int32 size;
} IOMsg;
/*------------------------------- Reading / Writing -----------------*/
/*
* Wait until the reader or the writer
* task (depending on the queue) comes up:
*/
static void await_active(ULONG queue)
{
ULONG m[4];
IOMsg iomsg;
iomsg.kind = SYNC_BUFF;
iomsg.sender = AppModel_current_thread;
m[0]= (ULONG)&iomsg;
q_send( queue, m );
AppModel_suspend_self();
}
/*
* Send a buffer to the writer task,
* for sending to the host:
*/
static void send_buffer(Pointer buffer, Int32 size)
{
ULONG m[4];
IOMsg iomsg;
iomsg.kind = WRITE_BUFF;
iomsg.sender = AppModel_current_thread;
iomsg.buffer = buffer;
iomsg.size = size;
m[0]= (ULONG)&iomsg;
q_send( writer_queue, m );
AppModel_suspend_self();
}
/*
* Send all pending output
* to the writer task,
* for sending to the host:
*/
static void sync_buffer()
{
send_buffer( mtu_buffer, mtu_pos );
mtu_pos = 0;
}
void SerialRPC_write_buffer(Pointer buffer, Int32 size)
{
AppSem_P(&buffer_lock);
if (mtu_pos+size <= MTU) {
memcpy( &mtu_buffer[mtu_pos], buffer, size);
mtu_pos += size;
} else if (mtu_pos > 0) {
Int buffer_space= MTU-mtu_pos;
memcpy( &mtu_buffer[mtu_pos], buffer, buffer_space);
send_buffer( mtu_buffer, MTU );
send_buffer( ((Address)buffer) + buffer_space, size - buffer_space);
mtu_pos = 0;
} else {
send_buffer( buffer, size );
}
AppSem_V(&buffer_lock);
}
void SerialRPC_read_buffer(Pointer buffer, Int32 size)
{
ULONG m[4];
IOMsg iomsg;
AppSem_P(&buffer_lock);
if (mtu_pos > 0) { sync_buffer(); }
AppSem_V(&buffer_lock);
iomsg.kind = READ_BUFF;
iomsg.sender = AppModel_current_thread;
iomsg.buffer = buffer;
iomsg.size = size;
m[0]= (ULONG)&iomsg;
q_send( reader_queue, m );
AppModel_suspend_self();
}
/*--------------------------- Initialisation / Termination ----------*/
extern Bool _bypass_rpc_init;
Bool SerialRPC_init( void )
{
_bypass_rpc_init= True;
return True;
}
static void reader_task_body()
{
Int listener;
struct sockaddr_in addr;
long addrlen = sizeof (struct sockaddr_in);
int port = 3357;
/*
* Wait for connection to the specified port
* from the serial host, and create reader-
* and writer socket:
*/
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
listener = socket(AF_INET, SOCK_STREAM, 0);
assert (listener != -1);
bind(listener, (Pointer)&addr, sizeof (struct sockaddr_in));
listen(listener, 5);
reader_socket = accept(listener, (Pointer)&addr, &addrlen);
assert (reader_socket != -1);
closesocket(listener);
await_active(writer_queue);
writer_socket= shr_socket( reader_socket, writer_task );
assert (writer_socket != -1);
/* --.-- */
/*
* Start serving messages:
*/
while (1) {
ULONG m[4];
IOMsg *iomsg;
q_receive(reader_queue, Q_WAIT, 0, m);
iomsg= (Pointer)m[0];
switch (iomsg->kind) {
case SYNC_BUFF :
{
AppModel_resume(iomsg->sender);
break;
}
case READ_BUFF :
{
Address buffer = iomsg->buffer;
UInt size = iomsg->size;
while (size > 0) {
Int s= recv(reader_socket, buffer, size, 0);
assert( s > 0 );
assert( s <= size );
buffer += s;
size -= s;
}
AppModel_resume(iomsg->sender);
break;
}
default : assert(False);
}
};
}
static void writer_task_body()
{
while (1) {
ULONG m[4];
IOMsg *iomsg;
q_receive(writer_queue, Q_WAIT, 0, m);
iomsg = (Pointer) m [0];
switch (iomsg->kind) {
case SYNC_BUFF:
AppModel_resume(iomsg->sender);
break;
case WRITE_BUFF:
{
Address buffer = iomsg->buffer;
UInt size = iomsg->size;
Int s;
while (size > 0) {
DP (("send (%d, 0x%X, %d, 0)\n", writer_socket,
buffer, size));
s = send(writer_socket, buffer, size, 0);
DP (("s %d\n", s));
assert( s > 0 );
assert( s <= size );
buffer += s;
size -= s;
}
AppModel_resume(iomsg->sender);
}
break;
default:
assert(False);
}
};
}
void SerialRPC_start()
{
if ( q_create("rdrq", 0, Q_LOCAL | Q_FIFO | Q_NOLIMIT, &reader_queue)
|| q_create("wrtq", 0, Q_LOCAL | Q_FIFO | Q_NOLIMIT, &writer_queue)
|| t_create("rdrt", 200, 0x10000, 0x10000, 0, &reader_task)
|| t_create("wrtt", 200, 0x10000, 0x10000, 0, &writer_task)
|| t_start(reader_task, T_PREEMPT | T_TSLICE | T_ASR | T_ISR, reader_task_body, 0)
|| t_start(writer_task, T_PREEMPT | T_TSLICE | T_ASR | T_ISR, writer_task_body, 0)
) {
/* trouble */
} else {
await_active(reader_queue);
_open(TCS_STDIN_NAME, O_RDONLY, 0666);
_open(TCS_STDOUT_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);
_open(TCS_STDERR_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -