Make linux optional as all other configs.
[syslinux-debian/hramrach.git] / efi / udp.c
blob59bb42639b5b1285dd328b29d6d475b2c7ce5739
1 #include <string.h>
2 #include <minmax.h>
3 #include "efi.h"
4 #include "net.h"
5 #include "fs/pxe/pxe.h"
7 extern EFI_GUID Udp4ServiceBindingProtocol, Udp4Protocol;
9 /*
10 * This UDP binding is configured to operate in promiscuous mode. It is
11 * only used for reading packets. It has no associated state unlike
12 * socket->net.efi.binding, which has a remote IP address and port
13 * number.
15 static struct efi_binding *udp_reader;
17 /**
18 * Open a socket
20 * @param:socket, the socket to open
22 * @out: error code, 0 on success, -1 on failure
24 int core_udp_open(struct pxe_pvt_inode *socket)
26 EFI_UDP4_CONFIG_DATA udata;
27 struct efi_binding *b;
28 EFI_STATUS status;
29 EFI_UDP4 *udp;
31 (void)socket;
33 udp_reader = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
34 if (!udp_reader)
35 return -1;
37 b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
38 if (!b)
39 goto bail;
41 udp = (EFI_UDP4 *)udp_reader->this;
43 memset(&udata, 0, sizeof(udata));
44 udata.AcceptPromiscuous = TRUE;
45 udata.AcceptAnyPort = TRUE;
47 status = uefi_call_wrapper(udp->Configure, 2, udp, &udata);
48 if (status != EFI_SUCCESS)
49 goto bail;
51 socket->net.efi.binding = b;
53 return 0;
55 bail:
56 if (b)
57 efi_destroy_binding(b, &Udp4ServiceBindingProtocol);
59 efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
60 udp_reader = NULL;
62 return -1;
65 /**
66 * Close a socket
68 * @param:socket, the socket to open
70 void core_udp_close(struct pxe_pvt_inode *socket)
72 efi_destroy_binding(udp_reader, &Udp4ServiceBindingProtocol);
73 udp_reader = NULL;
75 if (!socket->net.efi.binding)
76 return;
78 efi_destroy_binding(socket->net.efi.binding, &Udp4ServiceBindingProtocol);
79 socket->net.efi.binding = NULL;
82 /**
83 * Establish a connection on an open socket
85 * @param:socket, the open socket
86 * @param:ip, the ip address
87 * @param:port, the port number, host-byte order
89 void core_udp_connect(struct pxe_pvt_inode *socket, uint32_t ip,
90 uint16_t port)
92 EFI_UDP4_CONFIG_DATA udata;
93 EFI_STATUS status;
94 EFI_UDP4 *udp;
96 udp = (EFI_UDP4 *)socket->net.efi.binding->this;
98 memset(&udata, 0, sizeof(udata));
100 /* Re-use the existing local port number */
101 udata.StationPort = socket->net.efi.localport;
103 memcpy(&udata.StationAddress, &IPInfo.myip, sizeof(IPInfo.myip));
104 memcpy(&udata.SubnetMask, &IPInfo.netmask, sizeof(IPInfo.netmask));
105 memcpy(&udata.RemoteAddress, &ip, sizeof(ip));
106 udata.RemotePort = port;
107 udata.AcceptPromiscuous = TRUE;
108 udata.TimeToLive = 64;
110 status = uefi_call_wrapper(udp->Configure, 2, udp, &udata);
111 if (status != EFI_SUCCESS) {
112 Print(L"Failed to configure UDP: %d\n", status);
113 return;
117 * If this is the first time connecting, save the random local port
118 * number that the UDPv4 Protocol Driver picked for us. The TFTP
119 * protocol uses the local port number as the TID, and it needs to
120 * be consistent across connect()/disconnect() calls.
122 if (!socket->net.efi.localport) {
123 status = uefi_call_wrapper(udp->GetModeData, 5, udp,
124 &udata, NULL, NULL, NULL);
125 if (status != EFI_SUCCESS)
126 Print(L"Failed to get UDP mode data: %d\n", status);
127 else
128 socket->net.efi.localport = udata.StationPort;
133 * Tear down a connection on an open socket
135 * @param:socket, the open socket
137 void core_udp_disconnect(struct pxe_pvt_inode *socket)
139 EFI_STATUS status;
140 EFI_UDP4 *udp;
142 udp = (EFI_UDP4 *)socket->net.efi.binding->this;
144 /* Reset */
145 status = uefi_call_wrapper(udp->Configure, 2, udp, NULL);
146 if (status != EFI_SUCCESS)
147 Print(L"Failed to reset UDP: %d\n", status);
151 static int volatile cb_status = -1;
152 static EFIAPI void udp4_cb(EFI_EVENT event, void *context)
154 (void)event;
156 EFI_UDP4_COMPLETION_TOKEN *token = context;
158 if (token->Status == EFI_SUCCESS)
159 cb_status = 0;
160 else
161 cb_status = 1;
165 * Read data from the network stack
167 * @param:socket, the open socket
168 * @param:buf, location of buffer to store data
169 * @param:buf_len, size of buffer
171 * @out: src_ip, ip address of the data source
172 * @out: src_port, port number of the data source, host-byte order
174 int core_udp_recv(struct pxe_pvt_inode *socket, void *buf, uint16_t *buf_len,
175 uint32_t *src_ip, uint16_t *src_port)
177 EFI_UDP4_COMPLETION_TOKEN token;
178 EFI_UDP4_FRAGMENT_DATA *frag;
179 EFI_UDP4_RECEIVE_DATA *rxdata;
180 struct efi_binding *b;
181 EFI_STATUS status;
182 EFI_UDP4 *udp;
183 size_t size;
184 int rv = -1;
185 jiffies_t start;
187 (void)socket;
189 b = udp_reader;
190 udp = (EFI_UDP4 *)b->this;
192 status = efi_setup_event(&token.Event, (EFI_EVENT_NOTIFY)udp4_cb,
193 &token);
194 if (status != EFI_SUCCESS)
195 return -1;
197 status = uefi_call_wrapper(udp->Receive, 2, udp, &token);
198 if (status != EFI_SUCCESS)
199 goto bail;
201 start = jiffies();
202 while (cb_status == -1) {
203 /* 15ms receive timeout... */
204 if (jiffies() - start >= 15) {
205 if (jiffies() - start >= 30)
206 dprintf("Failed to cancel UDP\n");
208 uefi_call_wrapper(udp->Cancel, 2, udp, &token);
209 dprintf("core_udp_recv: timed out\n");
212 uefi_call_wrapper(udp->Poll, 1, udp);
215 if (cb_status == 0)
216 rv = 0;
218 /* Reset */
219 cb_status = -1;
221 if (rv)
222 goto bail;
224 rxdata = token.Packet.RxData;
225 frag = &rxdata->FragmentTable[0];
227 size = min(frag->FragmentLength, *buf_len);
228 memcpy(buf, frag->FragmentBuffer, size);
229 *buf_len = size;
231 memcpy(src_port, &rxdata->UdpSession.SourcePort, sizeof(*src_port));
232 memcpy(src_ip, &rxdata->UdpSession.SourceAddress, sizeof(*src_ip));
234 uefi_call_wrapper(BS->SignalEvent, 1, rxdata->RecycleSignal);
236 bail:
237 uefi_call_wrapper(BS->CloseEvent, 1, token.Event);
238 return rv;
242 * Send a UDP packet.
244 * @param:socket, the open socket
245 * @param:data, data buffer to send
246 * @param:len, size of data bufer
248 void core_udp_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
250 EFI_UDP4_COMPLETION_TOKEN *token;
251 EFI_UDP4_TRANSMIT_DATA *txdata;
252 EFI_UDP4_FRAGMENT_DATA *frag;
253 struct efi_binding *b = socket->net.efi.binding;
254 EFI_STATUS status;
255 EFI_UDP4 *udp = (EFI_UDP4 *)b->this;
257 token = zalloc(sizeof(*token));
258 if (!token)
259 return;
261 txdata = zalloc(sizeof(*txdata));
262 if (!txdata) {
263 free(token);
264 return;
267 status = efi_setup_event(&token->Event, (EFI_EVENT_NOTIFY)udp4_cb,
268 token);
269 if (status != EFI_SUCCESS)
270 goto bail;
272 txdata->UdpSessionData = NULL;
273 txdata->GatewayAddress = NULL;
274 txdata->DataLength = len;
275 txdata->FragmentCount = 1;
276 frag = &txdata->FragmentTable[0];
278 frag->FragmentLength = len;
279 frag->FragmentBuffer = (void *)data;
281 token->Packet.TxData = txdata;
283 status = uefi_call_wrapper(udp->Transmit, 2, udp, token);
284 if (status != EFI_SUCCESS)
285 goto close;
287 while (cb_status == -1)
288 uefi_call_wrapper(udp->Poll, 1, udp);
290 /* Reset */
291 cb_status = -1;
293 close:
294 uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
296 bail:
297 free(txdata);
298 free(token);
302 * Send a UDP packet to a destination
304 * @param:socket, the open socket
305 * @param:data, data buffer to send
306 * @param:len, size of data bufer
307 * @param:ip, the ip address
308 * @param:port, the port number, host-byte order
310 void core_udp_sendto(struct pxe_pvt_inode *socket, const void *data,
311 size_t len, uint32_t ip, uint16_t port)
313 EFI_UDP4_COMPLETION_TOKEN *token;
314 EFI_UDP4_TRANSMIT_DATA *txdata;
315 EFI_UDP4_FRAGMENT_DATA *frag;
316 EFI_UDP4_CONFIG_DATA udata;
317 EFI_STATUS status;
318 struct efi_binding *b;
319 EFI_UDP4 *udp;
321 (void)socket;
323 b = efi_create_binding(&Udp4ServiceBindingProtocol, &Udp4Protocol);
324 if (!b)
325 return;
327 udp = (EFI_UDP4 *)b->this;
329 token = zalloc(sizeof(*token));
330 if (!token)
331 goto out;
333 txdata = zalloc(sizeof(*txdata));
334 if (!txdata)
335 goto bail;
337 memset(&udata, 0, sizeof(udata));
339 memcpy(&udata.StationAddress, &IPInfo.myip, sizeof(IPInfo.myip));
340 memcpy(&udata.SubnetMask, &IPInfo.netmask, sizeof(IPInfo.netmask));
341 memcpy(&udata.RemoteAddress, &ip, sizeof(ip));
342 udata.RemotePort = port;
343 udata.AcceptPromiscuous = TRUE;
344 udata.TimeToLive = 64;
346 status = uefi_call_wrapper(udp->Configure, 2, udp, &udata);
347 if (status != EFI_SUCCESS)
348 goto bail;
350 status = efi_setup_event(&token->Event, (EFI_EVENT_NOTIFY)udp4_cb,
351 token);
352 if (status != EFI_SUCCESS)
353 goto bail;
355 txdata->UdpSessionData = NULL;
356 txdata->GatewayAddress = NULL;
357 txdata->DataLength = len;
358 txdata->FragmentCount = 1;
359 frag = &txdata->FragmentTable[0];
361 frag->FragmentLength = len;
362 frag->FragmentBuffer = (void *)data;
364 token->Packet.TxData = txdata;
366 status = uefi_call_wrapper(udp->Transmit, 2, udp, token);
367 if (status != EFI_SUCCESS)
368 goto close;
370 while (cb_status == -1)
371 uefi_call_wrapper(udp->Poll, 1, udp);
373 /* Reset */
374 cb_status = -1;
376 close:
377 uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
379 bail:
380 free(txdata);
381 free(token);
382 out:
383 efi_destroy_binding(b, &Udp4ServiceBindingProtocol);