Subversion Repositories shark

Compare Revisions

Ignore whitespace Rev 261 → Rev 262

/shark/trunk/ports/tftp/include/tftp.h
0,0 → 1,178
#ifndef __TFTP_H
#define __TFTP_H 1
 
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ll/sys/types.h>
#include <drivers/udpip.h>
 
#include "modules/sem.h"
 
#define BASE_X (40)
 
/* Real-time constraints for the sender and receiver tasks */
#define TFTP_UPLOAD_SENDER_PERIOD (2000000)
#define TFTP_UPLOAD_SENDER_WCET (1000000)
#define TFTP_UPLOAD_SENDER_MET (500000)
 
/* opcodes options */
#define TFTP_READ_REQUEST (1)
#define TFTP_WRITE_REQUEST (2)
#define TFTP_DATA (3)
#define TFTP_ACK (4)
#define TFTP_ERROR (5)
 
/* Default TFTP protocol error codes */
#define TFTP_ERR_NOT_DEFINED (0) /* Not defined, see error message (if any) */
#define TFTP_ERR_FILE_NOR_FOUND (1) /* File not found */
#define TFTP_ERR_ACCESS_VIOLATION (2) /* Access violation */
#define TFTP_ERR_DISK_FULL (3) /* Disk full or allocation exceeded */
#define TFTP_ERR_ILLEGAL_OPERATION (4) /* Illegal TFTP operation */
#define TFTP_ERR_UNKNOWN_ID (5) /* Unknown transfer ID */
#define TFTP_ERR_FILE_EXISTS (6) /* File already exists */
#define TFTP_ERR_NO_USER (7) /* No such user */
 
/* Custom TFTP protocol error codes */
#define TFTP_NO_ERROR (-1)
#define TFTP_ERR_TIMEOUT (-2)
 
/* TFTP connection modes */
#define TFTP_NETASCII_MODE "netascii"
#define TFTP_OCTET_MODE "octet"
#define TFTP_MAIL_MODE "mail"
 
/* Status */
#define TFTP_NOT_CONNECTED (-1)
#define TFTP_OPEN (-2)
#define TFTP_ACTIVE (-3)
#define TFTP_CONNECTION_REQUESTING (-4)
#define TFTP_STREAMING (-5)
#define TFTP_FLUSHING (-6)
#define TFTP_ERR (-100)
 
/* Mode options */
//#define TFTP_UPLOAD (1)
//#define TFTP_DOWNLOAD (2)
 
/* Closing options */
#define TFTP_STOP_NOW (1)
#define TFTP_FLUSH_BUFFER (2)
 
/* Base port address for socket communication */
#define BASE_PORT (2000)
 
/* The standard size of the tftp data packet */
#define TFTP_DATA_SIZE (512)
 
typedef struct tftp_packet {
WORD opcode; /* This is the opcode that represents operation to perform. */
union {
struct {
BYTE filename[TFTP_DATA_SIZE]; /* This is the download data. */
} request;
struct {
WORD block; /* The number of this block. */
BYTE data[TFTP_DATA_SIZE]; /* This is the download data. */
} data;
struct {
WORD block; /* This is the block number of the last data packet received. */
} ack;
struct {
WORD errcode; /* This is the error code from the TFTP server. */
BYTE errmsg[TFTP_DATA_SIZE]; /* This is the error message from the server. */
} err;
} u;
} TFTP_PACKET;
 
#define MAX_CONCURRENT_STREAM (5)
#define MAX_BUFFER_SIZE (1000000)
#define TFTP_DEFAULT_TIMEOUT (6000000)
#define TFTP_DEFAULT_TIMEOUT_NUMBER (5)
 
typedef struct tftp_model {
int status;
int errcode;
char errmsg[200];
int handle;
BYTE filename[80];
PID sender_pid;
PID receiver_pid;
int socket;
UDP_ADDR host;
UDP_ADDR local;
unsigned int nblock;
TFTP_PACKET last_sent;
BYTE waiting_ack;
TIME timestamp; /* the time we sent the last packet (data or ack) */
TIME timeout; /* in microseconds */
int ntimeout; /* number of timeouts (when 0 -> error!) */
} TFTP_MODEL;
 
typedef struct tftp_buffer {
BYTE *data;
unsigned long size;
unsigned long nbytes;
} TFTP_BUFFER;
 
/* Mutex constant */
#define TFTP_PI (0)
#define TFTP_SRP (1)
#define TFTP_PC (2)
 
/* We need to know what kind of mutex the user wants to use to share data with the reading (writing) queue
*/
typedef struct tftp_mutex {
BYTE semtype; /* Semaphore type. */
union {
struct { /* SRP */
SRP_RES_MODEL r;
int pLevel; /* Preemption level */
} srp;
struct { /* Priority ceiling */
PC_RES_MODEL r;
int tPr; /* Task priority */
} pc;
struct { /* Priority inheritance */
PI_mutexattr_t a;
} pi;
} sem;
} TFTP_MUTEX;
 
/***** Tftp packet related routines *****/
int tftp_get_opcode(TFTP_PACKET *pkt);
WORD tftp_get_data(TFTP_PACKET *pkt, BYTE *data, int n);
int tftp_get_ack_block(TFTP_PACKET *pkt);
int tftp_get_error(TFTP_PACKET *pkt, char *errmsg);
int tftp_fill_request(TFTP_PACKET *pkt, WORD opcode, const BYTE *filename, const BYTE *mode);
int tftp_fill_data(TFTP_PACKET *pkt, WORD nblock, BYTE *rawdata, WORD datasize);
int tftp_fill_ack(TFTP_PACKET *pkt, WORD nblock);
 
/***** Initialization routines *****/
int tftp_init();
int tftp_net_start(char *local_ip, char *host_ip, int init_net);
 
/***** Parameter setting routines *****/
int tftp_set_timeout(int h, int sec);
int tftp_set_timeout_numbers(int h, int n);
 
/***** Connection routines *****/
int tftp_open(char *fname);
int tftp_upload(int i, unsigned long buffsize, sem_t *mtx);
int tftp_download(int i, unsigned long buffsize, sem_t *mtx);
int tftp_close(int h, int hardness);
 
/* Buffer I/O routines */
int tftp_put(int h, BYTE *rawdata, WORD n);
int tftp_get(int h, BYTE *rawdata, WORD n);
int tftp_getbuffersize(int h);
int tftp_usedbuffer(int h);
int tftp_freebuffer(int h);
 
int tftp_status(int h);
 
/* Debugger routines */
int debug_setbuffer(int h, int size);
void debug_freebuffer(int h);
 
#endif /* tftp.h */
/shark/trunk/ports/tftp/manual.txt
0,0 → 1,29
The Tftp protocol implementation under Shark
 
Initialization:
1. call the tftp_init() routine;
2. call the tftp_net_start(...) to initialize the network card; the third parameter specify if you wantto initialize
the IP address of the card (1) or if you simply pass an already initialized one (0).
3. call tftp_open(...) to get a free socket;
4. call tftp_upload(...) or tftp_download(...) (the last one is not yet implemented) to start the tftp deamons.
 
The tftp library allocate a new socket for each upload/download connection.
The base port is set to 2000 and the i-th connection uses the [base]+i port address.
 
To stop uploading use tftp_close(connection, hardness)
hardness could be TFTP_STOP_NOW or TFTP_FLUSH_BUFFER
with TFTP_STOP_NOW you:
- sends immediately a packet with 0 bytes of data (means stop transfer!);
- all data in the buffer are lost;
- deamon stops immediately.
 
with TFTP_FLUSH_BUFFER you:
- couldn't write no more data in the buffer;
- deamons stay alive to send all data in the buffer to the server;
- then they shut down the service.
 
To Do:
- Implement the download routines
- Improve the error checking (at now the ACK deamon don't check for the block number)
- Use the [Big-Little]-endian routines provided with Shark instead of the ad-hoc ones.
- Improving the QUEUE library (I'm developing it) and use it in the TFTP library (and provide it to all Shark users).
/shark/trunk/ports/tftp/makefile
8,11 → 8,11
 
LIBRARY = tftp
 
OBJS_PATH = $(BASE)/drivers/tftp
OBJS_PATH = $(BASE)/ports/tftp
 
OBJS = tftp.o endn.o
 
OTHERINCL += -I$(BASE)/drivers/tftp/include
OTHERINCL += -I$(BASE)/ports/tftp/include
 
include $(BASE)/config/lib.mk
 
/shark/trunk/ports/tftp/tftp.c
0,0 → 1,519
#include <kernel/func.h>
#include <kernel/kern.h>
#include <stdlib.h>
#include <stdio.h>
#include "string.h"
 
#include "drivers/udpip.h"
 
#include "tftp.h"
#include "endn.h"
 
/* */
#include "modules/sem.h"
 
char local_ip_addr[20];
char host_ip_addr[20];
 
/* The value is incremented when assigning a new port address to a new
* connection.
*/
int port_counter;
 
/* The fixed IP/port (=69) to submit the connection requesting */
UDP_ADDR connection_request;
 
TFTP_MODEL model[MAX_CONCURRENT_STREAM];
sem_t *model_sem[MAX_CONCURRENT_STREAM];
 
TFTP_BUFFER buffer[MAX_CONCURRENT_STREAM];
//QUEUE queue[MAX_CONCURRENT_STREAM];
sem_t *buffer_sem[MAX_CONCURRENT_STREAM];
 
WORD tftp_get_data(TFTP_PACKET *pkt, BYTE *data, int n) {
memcpy(data, pkt->u.data.data, n);
return(pkt->u.data.block);
}
 
int tftp_get_ack_block(TFTP_PACKET *pkt) {
return(pkt->u.ack.block);
}
 
int tftp_get_error(TFTP_PACKET *pkt, char *errmsg) {
strcpy(errmsg, pkt->u.err.errmsg);
return(pkt->u.err.errcode);
}
 
/* Returns the packet opcode.
*/
int tftp_get_opcode(TFTP_PACKET *pkt) {
WORD tmp;
tmp = pkt->opcode;
SWAP_SHORT(tmp); /* Swap endian!! */
return(tmp);
}
 
int tftp_fill_request(TFTP_PACKET *pkt, WORD opcode, const BYTE *filename, const BYTE *mode) {
int i, j;
 
pkt->opcode = opcode; /* Put the opcode in the right struct field */
SWAP_SHORT(pkt->opcode); /* Swap endian!! */
 
/* Reset the filename field */
memset(pkt->u.request.filename, 0, sizeof(pkt->u.request.filename));
 
/* Concats the containing filename and mode NULL terminatd strings in the filename field */
for (i = 0; i < strlen(filename); i++)
pkt->u.request.filename[i] = filename[i];
pkt->u.request.filename[i] = '\0';
for (j = 0, i = i + 1; j < strlen(mode); i++, j++)
pkt->u.request.filename[i] = mode[j];
pkt->u.request.filename[i] = '\0';
 
return(0);
}
 
int tftp_fill_data(TFTP_PACKET *pkt, WORD nblock, BYTE *rawdata, WORD datasize) {
if (datasize > TFTP_DATA_SIZE) { return(1); } /* Overflow checking */
 
pkt->opcode = TFTP_DATA; /* Put the DATA opcode in the opcode field */
SWAP_SHORT(pkt->opcode); /* Swap endian!! */
 
pkt->u.data.block = nblock;
SWAP_SHORT(pkt->u.data.block); /* Swap endian!! */
 
memcpy(pkt->u.data.data, rawdata, datasize); /* ??? Maybe some data manipulation required!!! */
return(0);
}
 
int tftp_fill_ack(TFTP_PACKET *pkt, WORD nblock) {
pkt->opcode = TFTP_ACK; /* Put the ACK opcode in the opcode field */
SWAP_SHORT(pkt->opcode); /* Swap endian!! */
 
pkt->u.ack.block = nblock;
return(0);
}
 
void tftp_reset_handle(int h) {
model[h].status = TFTP_NOT_CONNECTED;
model[h].errcode = TFTP_NO_ERROR;
model[h].handle = -1;
model[h].sender_pid = -1;
model[h].receiver_pid = -1;
model[h].nblock = 0;
model[h].waiting_ack = 0;
model[h].timestamp = 0;
model[h].timeout = TFTP_DEFAULT_TIMEOUT;
model[h].ntimeout = TFTP_DEFAULT_TIMEOUT_NUMBER;
 
buffer[h].data = NULL;
buffer[h].size = 0;
buffer[h].nbytes = 0;
 
model_sem[h] = NULL;
buffer_sem[h] = NULL;
}
 
int tftp_init() {
int i;
 
for (i = 0; i < MAX_CONCURRENT_STREAM; i++) {
tftp_reset_handle(i);
}
 
port_counter = 0;
 
return(0);
}
 
int tftp_net_start(char *local_ip, char *host_ip, int init_net) {
struct net_model m = net_base;
int netval;
 
/* Save IPs locally */
strcpy(local_ip_addr, local_ip);
strcpy(host_ip_addr, host_ip);
 
netval = 0;
 
if (init_net) {
net_setmode(m, TXTASK); /* We want a task for TX mutual exclusion */
net_setudpip(m, local_ip); /* We use UDP/IP stack */
 
/* OK: let's start the NetLib! */
netval = net_init(&m);
}
 
return(netval);
}
 
int tftp_setup_timeout(int h, int sec) {
if (model[h].handle != TFTP_NOT_CONNECTED) return(-1);
model[h].timeout = sec * 1000000;
return(0);
}
 
int tftp_set_timeout_numbers(int h, int n) {
if (model[h].handle != TFTP_NOT_CONNECTED) return(-1);
model[h].ntimeout = n;
return(0);
}
 
int tftp_open(char *fname) {
int i;
 
/* Finds the first free connection slot */
for (i = 0; i < MAX_CONCURRENT_STREAM; i++)
if (model[i].status == TFTP_NOT_CONNECTED) break;
if (i >= MAX_CONCURRENT_STREAM) return(-1); /* No connection slots available */
 
model[i].handle = i; /* Handle = index in the struct array */
strcpy(model[i].filename, fname); /* Save filename into struct */
model[i].status = TFTP_OPEN; /* Connection opened */
sem_init(model_sem[i], 0, 1);
 
return(i);
}
 
TASK upload_sender(int id) {
TFTP_PACKET pkt;
char data[TFTP_DATA_SIZE];
int mystatus;
int i, n;
 
i = 0;
while(1) {
sem_wait(model_sem[id]);
 
if (model[id].waiting_ack) { /* and status != error ??? */
if (sys_gettime(NULL) - model[id].timestamp >= model[id].timeout) { /* ??? check it!!! */
if (!model[id].ntimeout) {
model[id].status = TFTP_ERR;
model[id].errcode = TFTP_ERR_TIMEOUT;
sem_post(model_sem[id]);
} else {
model[id].ntimeout--;
model[id].timestamp = sys_gettime(NULL);
sem_post(model_sem[id]);
udp_sendto(model[id].socket, (char*)(&model[id].last_sent), sizeof(TFTP_PACKET), &model[id].host);
}
} else {
sem_post(model_sem[id]);
}
} else {
mystatus = model[id].status;
sem_post(model_sem[id]);
 
switch (mystatus) {
case TFTP_ACTIVE : {
 
/* Doesn't use mutex 'cause uses "static" model fields */
tftp_fill_request(&pkt, TFTP_WRITE_REQUEST, model[id].filename, TFTP_OCTET_MODE);
udp_sendto(model[id].socket, (char*)(&pkt), sizeof(TFTP_PACKET), &connection_request);
memcpy(&model[id].last_sent, &pkt, sizeof(TFTP_PACKET)); /* Save the last sent packet for retransmission */
 
sem_wait(model_sem[id]);
if (model[id].status != TFTP_ERR)
model[id].status = TFTP_CONNECTION_REQUESTING;
else {
sem_post(model_sem[id]);
break;
}
model[id].waiting_ack = 1;
model[id].timestamp = sys_gettime(NULL);
sem_post(model_sem[id]);
 
break;
}
case TFTP_CONNECTION_REQUESTING : {
}
case TFTP_STREAMING : {
if (tftp_usedbuffer(id) >= TFTP_DATA_SIZE) {
n = tftp_get(id, data, TFTP_DATA_SIZE);
tftp_fill_data(&pkt, model[id].nblock, data, n);
 
udp_sendto(model[id].socket, (char*)(&pkt), sizeof(TFTP_PACKET), &model[id].host);
memcpy(&model[id].last_sent, &pkt, sizeof(TFTP_PACKET)); /* Save the last sent packet for retransmission */
 
sem_wait(model_sem[id]);
model[id].waiting_ack = 1;
model[id].timestamp = sys_gettime(NULL);
sem_post(model_sem[id]);
}
break;
}
case TFTP_FLUSHING : {
n = tftp_usedbuffer(id);
if (n >= TFTP_DATA_SIZE) {
 
/* Get data for a full data packet */
n = tftp_get(id, data, TFTP_DATA_SIZE);
tftp_fill_data(&pkt, model[id].nblock, data, n);
 
udp_sendto(model[id].socket, (char*)(&pkt), sizeof(TFTP_PACKET), &model[id].host);
memcpy(&model[id].last_sent, &pkt, sizeof(TFTP_PACKET)); /* Save the last sent packet for retransmission */
 
sem_wait(model_sem[id]);
model[id].waiting_ack = 1;
model[id].timestamp = sys_gettime(NULL);
sem_post(model_sem[id]);
} else {
 
/* Get remaining data from buffer */
n = tftp_get(id, data, n);
tftp_fill_data(&pkt, model[id].nblock, data, n);
 
/* Sending 4 extra bytes for opcode and block number!! */
udp_sendto(model[id].socket, (char*)(&pkt), sizeof(n + 4), &model[id].host);
 
/* Don't wait for ack!! Maybe will be implemented later... */
task_kill(model[id].receiver_pid);
/* ..... */
task_abort(NULL);
}
break;
}
 
case TFTP_ERROR : {
break;
}
}
}
 
task_testcancel();
task_endcycle();
}
return(0);
}
 
/* This non real-time task reads UDP packets with ACK from the network
*/
TASK upload_receiver(int id) {
char msg[200];
int mystatus;
int n;
int i;
WORD opcode;
TFTP_PACKET pkt;
UDP_ADDR server;
 
i = 0;
while (1) {
sem_wait(model_sem[id]);
mystatus = model[id].status;
sem_post(model_sem[id]);
 
if (mystatus != TFTP_ERR) {
n = udp_recvfrom(model[id].socket, &pkt, &server);
opcode = tftp_get_opcode(&pkt);
 
if (opcode == TFTP_ERROR) {
n = tftp_get_error(&pkt, msg); // re-use n: not too orthodox...
 
sem_wait(model_sem[id]);
model[id].status = TFTP_ERR;
model[id].errcode = n;
strcpy(model[id].errmsg, msg);
sem_post(model_sem[id]);
 
} else {
switch (mystatus) {
case TFTP_NOT_CONNECTED : {
// discard the packet... set error??
break;
}
case TFTP_CONNECTION_REQUESTING : {
sem_wait(model_sem[id]);
memcpy(&model[id].host, &server, sizeof(model[id].host));
model[id].waiting_ack = 0;
model[id].status = TFTP_STREAMING;
model[id].nblock++;
sem_post(model_sem[id]);
 
break;
}
case TFTP_STREAMING : {
// check the nblock on the arrived packet
 
sem_wait(model_sem[id]);
model[id].waiting_ack = 0;
model[id].nblock++;
sem_post(model_sem[id]);
break;
}
}
}
}
i++;
}
 
return(0);
}
 
int tftp_upload(int i, unsigned long buffsize, sem_t *mtx) {
SOFT_TASK_MODEL soft_m;
NRT_TASK_MODEL nrt_m;
 
if ((buffer_sem[i] = mtx) == NULL) return(-3); /* ??? check assignment!!! */
 
if ((buffer[i].size = buffsize) > MAX_BUFFER_SIZE) return(-2); /* Buffer size too large */
if ((buffer[i].data = malloc(buffsize)) == NULL) return(-4); /* Buffer allocation error */
buffer[i].nbytes = 0;
 
/* Create a socket for transmission */
ip_str2addr(local_ip_addr, &(model[i].local.s_addr));
model[i].local.s_port = BASE_PORT + port_counter; /* Different port for each connection */
port_counter++;
 
ip_str2addr(host_ip_addr, &(connection_request.s_addr));
connection_request.s_port = 69; /* It is fixed for the connection request */
 
model[i].socket = udp_bind(&model[i].local, NULL);
 
/* First we set the sender's task properties... */
soft_task_default_model(soft_m);
soft_task_def_level(soft_m, 0);
soft_task_def_arg(soft_m, (void *)(i));
soft_task_def_group(soft_m, i);
soft_task_def_periodic(soft_m);
soft_task_def_wcet(soft_m, TFTP_UPLOAD_SENDER_WCET);
soft_task_def_period(soft_m, TFTP_UPLOAD_SENDER_PERIOD);
soft_task_def_met(soft_m, TFTP_UPLOAD_SENDER_MET);
 
model[i].sender_pid = task_create("upload_sender", upload_sender, &soft_m, NULL);
 
if (model[i].sender_pid == -1) {
free(buffer[i].data);
tftp_reset_handle(i);
return(-5);
}
 
nrt_task_default_model(nrt_m); /* Start the receiver task... */
nrt_task_def_arg(nrt_m, (void *)(i));
if ((model[i].receiver_pid = task_create("upload_receiver", upload_receiver, &nrt_m, NULL)) == NIL) {
free(buffer[i].data);
tftp_reset_handle(i);
return(-6);
}
 
model[i].status = TFTP_ACTIVE; /* Connection active */
if (task_activate(model[i].sender_pid) == -1) {
free(buffer[i].data);
tftp_reset_handle(i);
return(-7);
}
if (task_activate(model[i].receiver_pid) == -1) {
free(buffer[i].data); // Maybe not correct... sys_panic() may be better
tftp_reset_handle(i);
return(-8);
}
 
return(0);
}
 
int tftp_download(int i, unsigned long buffsize, sem_t *mtx) {
return(0);
}
 
int tftp_close(int h, int hardness) {
TFTP_PACKET pkt;
 
if (hardness == TFTP_STOP_NOW) {
task_kill(model[h].sender_pid);
task_kill(model[h].receiver_pid);
tftp_fill_data(&pkt, model[h].nblock, NULL, 0);
udp_sendto(model[h].socket, (char*)(&pkt), 4, &model[h].host);
tftp_reset_handle(h);
free(buffer[h].data);
sem_destroy(buffer_sem[h]);
sem_destroy(model_sem[h]);
} else {
sem_wait(model_sem[h]);
model[h].status = TFTP_FLUSHING;
sem_post(model_sem[h]);
}
 
return(0);
}
 
int tftp_put(int h, BYTE *rawdata, WORD n) {
sem_wait(buffer_sem[h]);
 
/* Buffer overflow checking */
if (buffer[h].nbytes + n > buffer[h].size) { /* Maybe ">"??? */
sem_post(buffer_sem[h]);
return(1);
}
 
/* Check this carefully!!! */
memcpy(buffer[h].data + buffer[h].nbytes, rawdata, n);
buffer[h].nbytes += n;
 
sem_post(buffer_sem[h]);
 
return(0);
}
 
int tftp_get(int h, BYTE *rawdata, WORD n) {
// cprintf("get mutex %d - use %d\n", buffer_sem[h]->mutexlevel, buffer_sem[h]->use);
sem_wait(buffer_sem[h]);
 
if (buffer[h].nbytes < 1) return(0);
if (buffer[h].nbytes < n) n = buffer[h].nbytes;
 
/* Check this carefully!!! */
memcpy(rawdata, buffer[h].data, n); /* Export data to calling function */
memcpy(buffer[h].data, buffer[h].data + n, n); /* Shift data into buffer */
buffer[h].nbytes -= n;
 
sem_post(buffer_sem[h]);
return(n);
}
 
int tftp_getbuffersize(int h) {
return(buffer[h].size); /* We on't use the mutex 'cause the size is read-only */
}
 
int tftp_usedbuffer(int h) {
int n;
 
// cprintf("used mutex %d - use %d\n", buffer_sem[h]->mutexlevel, buffer_sem[h]->use);
sem_wait(buffer_sem[h]);
n = buffer[h].nbytes;
sem_post(buffer_sem[h]);
return(n);
}
 
int tftp_freebuffer(int h) {
int n;
 
sem_wait(buffer_sem[h]);
n = buffer[h].size - buffer[h].nbytes;
sem_post(buffer_sem[h]);
return(n);
}
 
int tftp_status(int h) {
int n;
 
sem_wait(model_sem[h]);
n = model[h].status;
sem_post(model_sem[h]);
return(n);
}
 
///////////////////////////////////////////////////////////////////////////
 
int debug_setbuffer(int h, int size) {
if ((buffer[h].data = malloc(size)) == NULL) return(-1); /* Buffer allocation error */
buffer[h].size = size;
buffer[h].nbytes = 0;
return(0);
}
 
void debug_freebuffer(int h) {
free(buffer[h].data); /* Buffer allocation error */
buffer[h].size = 0;
buffer[h].nbytes = 0;
}