1/*
2 * Copyright 2013-2014 Intel Corporation - All Rights Reserved
3 */
4
5#include "efi.h"
6#include "net.h"
7#include "fs/pxe/pxe.h"
8
9extern EFI_GUID Tcp4ServiceBindingProtocol;
10extern EFI_GUID Tcp4Protocol;
11
12
13extern struct efi_binding *efi_create_binding(EFI_GUID *, EFI_GUID *);
14extern void efi_destroy_binding(struct efi_binding *, EFI_GUID *);
15int core_tcp_open(struct pxe_pvt_inode *socket)
16{
17    struct efi_binding *b;
18
19    b = efi_create_binding(&Tcp4ServiceBindingProtocol, &Tcp4Protocol);
20    if (!b)
21	return -1;
22
23    socket->net.efi.binding = b;
24
25    return 0;
26}
27
28static EFIAPI void null_cb(EFI_EVENT ev, void *context)
29{
30    EFI_TCP4_COMPLETION_TOKEN *token = context;
31
32    (void)ev;
33
34    uefi_call_wrapper(BS->CloseEvent, 1, token->Event);
35}
36
37static int volatile cb_status = -1;
38static EFIAPI void tcp_cb(EFI_EVENT ev, void *context)
39{
40    EFI_TCP4_COMPLETION_TOKEN *token = context;
41
42    (void)ev;
43
44    if (token->Status == EFI_SUCCESS)
45	cb_status = 0;
46    else
47	cb_status = 1;
48}
49
50int core_tcp_connect(struct pxe_pvt_inode *socket, uint32_t ip, uint16_t port)
51{
52    EFI_TCP4_CONNECTION_TOKEN token;
53    EFI_TCP4_ACCESS_POINT *ap;
54    EFI_TCP4_CONFIG_DATA tdata;
55    struct efi_binding *b = socket->net.efi.binding;
56    EFI_STATUS status;
57    EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
58    int rv = -1;
59    int unmapped = 1;
60    jiffies_t start, last, cur;
61
62    memset(&tdata, 0, sizeof(tdata));
63
64    ap = &tdata.AccessPoint;
65    ap->UseDefaultAddress = TRUE;
66    memcpy(&ap->RemoteAddress, &ip, sizeof(ip));
67    ap->RemotePort = port;
68    ap->ActiveFlag = TRUE; /* Initiate active open */
69
70    tdata.TimeToLive = 64;
71
72    last = start = jiffies();
73    while (unmapped){
74	status = uefi_call_wrapper(tcp->Configure, 2, tcp, &tdata);
75	if (status != EFI_NO_MAPPING)
76		unmapped = 0;
77	else {
78	    cur = jiffies();
79	    if ( (cur - last) >= EFI_NOMAP_PRINT_DELAY ) {
80		last = cur;
81		Print(L"core_tcp_connect: stalling on configure with no mapping\n");
82	    } else if ( (cur - start) > EFI_NOMAP_PRINT_DELAY * EFI_NOMAP_PRINT_COUNT) {
83		Print(L"core_tcp_connect: aborting on no mapping\n");
84		unmapped = 0;
85	    }
86	}
87    }
88    if (status != EFI_SUCCESS)
89	return -1;
90
91    status = efi_setup_event(&token.CompletionToken.Event,
92			    (EFI_EVENT_NOTIFY)tcp_cb, &token.CompletionToken);
93    if (status != EFI_SUCCESS)
94	return -1;
95
96    status = uefi_call_wrapper(tcp->Connect, 2, tcp, &token);
97    if (status != EFI_SUCCESS) {
98	Print(L"Failed to connect: %d\n", status);
99	goto out;
100    }
101
102    while (cb_status == -1)
103	uefi_call_wrapper(tcp->Poll, 1, tcp);
104
105    if (cb_status == 0)
106	rv = 0;
107
108    /* Reset */
109    cb_status = -1;
110
111out:
112    uefi_call_wrapper(BS->CloseEvent, 1, token.CompletionToken.Event);
113    return rv;
114}
115
116bool core_tcp_is_connected(struct pxe_pvt_inode *socket)
117{
118    if (socket->net.efi.binding)
119	return true;
120
121    return false;
122}
123
124int core_tcp_write(struct pxe_pvt_inode *socket, const void *data,
125		   size_t len, bool copy)
126{
127    EFI_TCP4_TRANSMIT_DATA txdata;
128    EFI_TCP4_FRAGMENT_DATA *frag;
129    struct efi_binding *b = socket->net.efi.binding;
130    EFI_TCP4_IO_TOKEN iotoken;
131    EFI_STATUS status;
132    EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
133    int rv = -1;
134
135    (void)copy;
136
137    memset(&iotoken, 0, sizeof(iotoken));
138    memset(&txdata, 0, sizeof(txdata));
139
140    txdata.DataLength = len;
141    txdata.FragmentCount = 1;
142
143    frag = &txdata.FragmentTable[0];
144    frag->FragmentLength = len;
145    frag->FragmentBuffer = (void *)data;
146
147    iotoken.Packet.TxData = &txdata;
148
149    status = efi_setup_event(&iotoken.CompletionToken.Event,
150			     (EFI_EVENT_NOTIFY)tcp_cb, &iotoken.CompletionToken);
151    if (status != EFI_SUCCESS)
152	return -1;
153
154    status = uefi_call_wrapper(tcp->Transmit, 2, tcp, &iotoken);
155    if (status != EFI_SUCCESS) {
156	Print(L"tcp transmit failed, %d\n", status);
157	goto out;
158    }
159
160    while (cb_status == -1)
161	uefi_call_wrapper(tcp->Poll, 1, tcp);
162
163    if (cb_status == 0)
164	rv = 0;
165
166    /* Reset */
167    cb_status = -1;
168
169out:
170    uefi_call_wrapper(BS->CloseEvent, 1, iotoken.CompletionToken.Event);
171    return rv;
172}
173
174void core_tcp_close_file(struct inode *inode)
175{
176    struct pxe_pvt_inode *socket = PVT(inode);
177    struct efi_binding *b = socket->net.efi.binding;
178    EFI_TCP4_CLOSE_TOKEN token;
179    EFI_STATUS status;
180    EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
181
182  if (!socket->tftp_goteof) {
183	memset(&token, 0, sizeof(token));
184
185	status = efi_setup_event(&token.CompletionToken.Event,
186				 (EFI_EVENT_NOTIFY)null_cb,
187				 &token.CompletionToken);
188	if (status != EFI_SUCCESS)
189	    return;
190
191	status = uefi_call_wrapper(tcp->Close, 2, tcp, &token);
192	if (status != EFI_SUCCESS)
193	    Print(L"tcp close failed: %d\n", status);
194    }
195
196    efi_destroy_binding(b, &Tcp4ServiceBindingProtocol);
197    socket->net.efi.binding = NULL;
198}
199
200static char databuf[8192];
201
202void core_tcp_fill_buffer(struct inode *inode)
203{
204    struct pxe_pvt_inode *socket = PVT(inode);
205    struct efi_binding *b = socket->net.efi.binding;
206    EFI_TCP4_IO_TOKEN iotoken;
207    EFI_TCP4_RECEIVE_DATA rxdata;
208    EFI_TCP4_FRAGMENT_DATA *frag;
209    EFI_STATUS status;
210    EFI_TCP4 *tcp = (EFI_TCP4 *)b->this;
211    void *data;
212    size_t len;
213
214    memset(&iotoken, 0, sizeof(iotoken));
215    memset(&rxdata, 0, sizeof(rxdata));
216
217    status = efi_setup_event(&iotoken.CompletionToken.Event,
218		      (EFI_EVENT_NOTIFY)tcp_cb, &iotoken.CompletionToken);
219    if (status != EFI_SUCCESS)
220	return;
221
222    iotoken.Packet.RxData = &rxdata;
223    rxdata.FragmentCount = 1;
224    rxdata.DataLength = sizeof(databuf);
225    frag = &rxdata.FragmentTable[0];
226    frag->FragmentBuffer = databuf;
227    frag->FragmentLength = sizeof(databuf);
228
229    status = uefi_call_wrapper(tcp->Receive, 2, tcp, &iotoken);
230    if (status == EFI_CONNECTION_FIN) {
231	socket->tftp_goteof = 1;
232	if (inode->size == (uint64_t)-1)
233	    inode->size = socket->tftp_filepos;
234	socket->ops->close(inode);
235	goto out;
236    }
237
238    while (cb_status == -1)
239	uefi_call_wrapper(tcp->Poll, 1, tcp);
240
241    /* Reset */
242    cb_status = -1;
243
244    len = frag->FragmentLength;
245    memcpy(databuf, frag->FragmentBuffer, len);
246    data = databuf;
247
248    socket->tftp_dataptr = data;
249    socket->tftp_filepos += len;
250    socket->tftp_bytesleft = len;
251
252out:
253    uefi_call_wrapper(BS->CloseEvent, 1, iotoken.CompletionToken.Event);
254}
255