Adding upstream version 6.01~pre5+dfsg.
[syslinux-debian/hramrach.git] / efi / udp.c
blob301074d04c4ab6b7ffd2523788fd4f69b61018b4
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;
186 (void)socket;
188 b = udp_reader;
189 udp = (EFI_UDP4 *)b->this;
191 status = efi_setup_event(&token.Event, (EFI_EVENT_NOTIFY)udp4_cb,
192 &token);
193 if (status != EFI_SUCCESS)
194 return -1;
196 status = uefi_call_wrapper(udp->Receive, 2, udp, &token);
197 if (status != EFI_SUCCESS)
198 goto bail;
200 while (cb_status == -1)
201 uefi_call_wrapper(udp->Poll, 1, udp);
203 if (cb_status == 0)
204 rv = 0;
206 /* Reset */
207 cb_status = -1;
209 rxdata = token.Packet.RxData;
210 frag = &rxdata->FragmentTable[0];
212 size = min(frag->FragmentLength, *buf_len);
213 memcpy(buf, frag->FragmentBuffer, size);
214 *buf_len = size;
216 memcpy(src_port, &rxdata->UdpSession.SourcePort, sizeof(*src_port));
217 memcpy(src_ip, &rxdata->UdpSession.SourceAddress, sizeof(*src_ip));
219 uefi_call_wrapper(BS->SignalEvent, 1, rxdata->RecycleSignal);
221 bail:
222 uefi_call_wrapper(BS->CloseEvent, 1, token.Event);
223 return rv;
227 * Send a UDP packet.
229 * @param:socket, the open socket
230 * @param:data, data buffer to send
231 * @param:len, size of data bufer
233 void core_udp_send(struct pxe_pvt_inode *socket, const void *data, size_t len)
235 EFI_UDP4_COMPLETION_TOKEN *token;
236 EFI_UDP4_TRANSMIT_DATA *txdata;
237 EFI_UDP4_FRAGMENT_DATA *frag;
238 struct efi_binding *b = socket->net.efi.binding;
239 EFI_STATUS status;
240 EFI_UDP4 *udp = (EFI_UDP4 *)b->this;
242 token = zalloc(sizeof(*token));
243 if (!token)
244 return;
246 txdata = zalloc(sizeof(*txdata));
247 if (!txdata) {
248 free(token);
249 return;
252 status = efi_setup_event(&token->Event, (EFI_EVENT_NOTIFY)udp4_cb,
253 token);
254 if (status != EFI_SUCCESS)
255 goto bail;
257 txdata->UdpSessionData = NULL;
258 txdata->GatewayAddress = NULL;
259 txdata->DataLength = len;
260 txdata->FragmentCount = 1;
261 frag = &txdata->FragmentTable[0];
263 frag->FragmentLength = len;
264 frag->FragmentBuffer = (void *)data;
266 token->Packet.TxData = txdata;
268 status = uefi_call_wrapper(udp->Transmit, 2, udp, token);
269 if (status != EFI_SUCCESS)
270 goto close;
272 while (cb_status == -1)
273 uefi_call_wrapper(udp->Poll, 1, udp);
275 /* Reset */
276 cb_status = -1;
278 close:
279 uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
281 bail:
282 free(txdata);
283 free(token);