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