Rev 1063 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#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 "sem/sem/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
,"255.255.255.255"); /* 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;
}