📄 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.13
*
* Last update : 14:58:04 - 00/06/19
*
* 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>
/*
* 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;
while (size > 0) {
Int s= send(writer_socket, buffer, size, 0);
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 + -