baaf8c76e7a8be54fc784c036f997882333c66ea
[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/hello.h>
6
7 /** @file
8  *
9  * "Hello world" TCP protocol
10  *
11  * This file implements a trivial TCP-based protocol.  It connects to
12  * the server specified in hello_request::tcp and transmits a single
13  * message (hello_request::message).  Any data received from the
14  * server will be passed to the callback function,
15  * hello_request::callback(), and once the connection has been closed,
16  * hello_request::complete will be set to a non-zero value.
17  *
18  * To use this code, do something like:
19  *
20  * @code
21  *
22  *   static void my_callback ( char *data, size_t len ) {
23  *     ... process data ...
24  *   }
25  *
26  *   struct hello_request hello = {
27  *     .message = "hello world!",
28  *     .callback = my_callback,
29  *   };
30  *
31  *   hello.sin.sin_addr.s_addr = ... server IP address ...
32  *   hello.sin.sin_port = ... server port ...
33  *
34  *   hello_connect ( &hello );
35  *   while ( ! hello.completed ) {
36  *     run_tcpip();
37  *   }
38  *
39  * @endcode
40  *
41  * It's worth noting that this trivial protocol would be entirely
42  * adequate to implement a TCP-based version of TFTP; just use "RRQ
43  * <filename>" as the message.  Now, if only an appropriate server
44  * existed...
45  */
46
47 static inline struct hello_request *
48 tcp_to_hello ( struct tcp_connection *conn ) {
49         return container_of ( conn, struct hello_request, tcp );
50 }
51
52 static void hello_closed ( struct tcp_connection *conn, int status ) {
53         struct hello_request *hello = tcp_to_hello ( conn );
54
55         hello->complete = ( status ? status : 1 );
56 }
57
58 static void hello_connected ( struct tcp_connection *conn ) {
59         struct hello_request *hello = tcp_to_hello ( conn );
60
61         hello->remaining = strlen ( hello->message );
62         hello->state = HELLO_SENDING_MESSAGE;
63 }
64
65 static void hello_acked ( struct tcp_connection *conn, size_t len ) {
66         struct hello_request *hello = tcp_to_hello ( conn );
67         
68         hello->message += len;
69         hello->remaining -= len;
70         if ( hello->remaining == 0 ) {
71                 switch ( hello->state ) {
72                 case HELLO_SENDING_MESSAGE:
73                         hello->message = "\r\n";
74                         hello->remaining = 2;
75                         hello->state = HELLO_SENDING_ENDL;
76                         break;
77                 case HELLO_SENDING_ENDL:
78                         /* Nothing to do once we've finished sending
79                          * the end-of-line indicator.
80                          */
81                         break;
82                 default:
83                         assert ( 0 );
84                 }
85         }
86 }
87
88 static void hello_newdata ( struct tcp_connection *conn, void *data,
89                             size_t len ) {
90         struct hello_request *hello = tcp_to_hello ( conn );
91
92         hello->callback ( data, len );
93 }
94
95 static void hello_senddata ( struct tcp_connection *conn ) {
96         struct hello_request *hello = tcp_to_hello ( conn );
97
98         tcp_send ( conn, 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 void hello_connect ( struct hello_request *hello ) {
115         hello->tcp.tcp_op = &hello_tcp_operations;
116         tcp_connect ( &hello->tcp );
117 }