1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
#include <stddef.h>
#include <string.h>
#include <vsprintf.h>
#include <assert.h>
#include <gpxe/async.h>
#include <gpxe/hello.h>
/** @file
*
* "Hello world" TCP protocol
*
* This file implements a trivial TCP-based protocol. It connects to
* the server specified in hello_request::server and transmits a
* single message (hello_request::message). Any data received from
* the server will be passed to the callback function,
* hello_request::callback(), and once the connection has been closed,
* the asynchronous operation associated with the request will be
* marked as complete.
*
* To use this code, do something like:
*
* @code
*
* static void my_callback ( char *data, size_t len ) {
* ... process data ...
* }
*
* struct hello_request hello = {
* .server = {
* ...
* },
* .message = "hello world!",
* .callback = my_callback,
* };
*
* rc = async_wait ( say_hello ( &hello ) );
*
* @endcode
*
* It's worth noting that this trivial protocol would be entirely
* adequate to implement a TCP-based version of TFTP; just use "RRQ
* <filename>" as the message. Now, if only an appropriate server
* existed...
*/
static inline struct hello_request *
tcp_to_hello ( struct tcp_application *app ) {
return container_of ( app, struct hello_request, tcp );
}
static void hello_closed ( struct tcp_application *app, int status ) {
struct hello_request *hello = tcp_to_hello ( app );
async_done ( &hello->aop, status );
}
static void hello_connected ( struct tcp_application *app ) {
struct hello_request *hello = tcp_to_hello ( app );
hello->remaining = strlen ( hello->message );
hello->state = HELLO_SENDING_MESSAGE;
}
static void hello_acked ( struct tcp_application *app, size_t len ) {
struct hello_request *hello = tcp_to_hello ( app );
hello->message += len;
hello->remaining -= len;
if ( hello->remaining == 0 ) {
switch ( hello->state ) {
case HELLO_SENDING_MESSAGE:
hello->message = "\r\n";
hello->remaining = 2;
hello->state = HELLO_SENDING_ENDL;
break;
case HELLO_SENDING_ENDL:
/* Nothing to do once we've finished sending
* the end-of-line indicator.
*/
break;
default:
assert ( 0 );
}
}
}
static void hello_newdata ( struct tcp_application *app, void *data,
size_t len ) {
struct hello_request *hello = tcp_to_hello ( app );
hello->callback ( data, len );
}
static void hello_senddata ( struct tcp_application *app,
void *buf __unused, size_t len __unused ) {
struct hello_request *hello = tcp_to_hello ( app );
tcp_send ( app, hello->message, hello->remaining );
}
static struct tcp_operations hello_tcp_operations = {
.closed = hello_closed,
.connected = hello_connected,
.acked = hello_acked,
.newdata = hello_newdata,
.senddata = hello_senddata,
};
/**
* Initiate a "hello world" connection
*
* @v hello "Hello world" request
*/
struct async_operation * say_hello ( struct hello_request *hello ) {
int rc;
hello->tcp.tcp_op = &hello_tcp_operations;
if ( ( rc = tcp_connect ( &hello->tcp, &hello->server, 0 ) ) != 0 )
async_done ( &hello->aop, rc );
return &hello->aop;
}
|