Created net/tcp and moved hello.c there
[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 1.
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_aborted ( struct tcp_connection *conn ) {
53         struct hello_request *hello = tcp_to_hello ( conn );
54
55         printf ( "Connection aborted\n" );
56         hello->complete = 1;
57 }
58
59 static void hello_timedout ( struct tcp_connection *conn ) {
60         struct hello_request *hello = tcp_to_hello ( conn );
61
62         printf ( "Connection timed out\n" );
63         hello->complete = 1;
64 }
65
66 static void hello_closed ( struct tcp_connection *conn ) {
67         struct hello_request *hello = tcp_to_hello ( conn );
68
69         hello->complete = 1;
70 }
71
72 static void hello_connected ( struct tcp_connection *conn ) {
73         struct hello_request *hello = tcp_to_hello ( conn );
74
75         hello->remaining = strlen ( hello->message );
76         hello->state = HELLO_SENDING_MESSAGE;
77 }
78
79 static void hello_acked ( struct tcp_connection *conn, size_t len ) {
80         struct hello_request *hello = tcp_to_hello ( conn );
81         
82         hello->message += len;
83         hello->remaining -= len;
84         if ( hello->remaining == 0 ) {
85                 switch ( hello->state ) {
86                 case HELLO_SENDING_MESSAGE:
87                         hello->message = "\r\n";
88                         hello->remaining = 2;
89                         hello->state = HELLO_SENDING_ENDL;
90                         break;
91                 case HELLO_SENDING_ENDL:
92                         /* Nothing to do once we've finished sending
93                          * the end-of-line indicator.
94                          */
95                         break;
96                 default:
97                         assert ( 0 );
98                 }
99         }
100 }
101
102 static void hello_newdata ( struct tcp_connection *conn, void *data,
103                             size_t len ) {
104         struct hello_request *hello = tcp_to_hello ( conn );
105
106         hello->callback ( data, len );
107 }
108
109 static void hello_senddata ( struct tcp_connection *conn ) {
110         struct hello_request *hello = tcp_to_hello ( conn );
111
112         tcp_send ( conn, hello->message, hello->remaining );
113 }
114
115 static struct tcp_operations hello_tcp_operations = {
116         .aborted        = hello_aborted,
117         .timedout       = hello_timedout,
118         .closed         = hello_closed,
119         .connected      = hello_connected,
120         .acked          = hello_acked,
121         .newdata        = hello_newdata,
122         .senddata       = hello_senddata,
123 };
124
125 /**
126  * Initiate a "hello world" connection
127  *
128  * @v hello     "Hello world" request
129  */
130 void hello_connect ( struct hello_request *hello ) {
131         hello->tcp.tcp_op = &hello_tcp_operations;
132         tcp_connect ( &hello->tcp );
133 }