| /* |
| * Copyright 2013-2014 Intel Corporation - All Rights Reserved |
| */ |
| |
| #include "efi.h" |
| #include "net.h" |
| #include "fs/pxe/pxe.h" |
| |
| extern EFI_GUID Tcp4ServiceBindingProtocol; |
| extern EFI_GUID Tcp4Protocol; |
| |
| |
| extern struct efi_binding *efi_create_binding(EFI_GUID *, EFI_GUID *); |
| extern void efi_destroy_binding(struct efi_binding *, EFI_GUID *); |
| int core_tcp_open(struct pxe_pvt_inode *socket) |
| { |
| struct efi_binding *b; |
| |
| b = efi_create_binding(&Tcp4ServiceBindingProtocol, &Tcp4Protocol); |
| if (!b) |
| return -1; |
| |
| socket->net.efi.binding = b; |
| |
| return 0; |
| } |
| |
| static EFIAPI void null_cb(EFI_EVENT ev, void *context) |
| { |
| EFI_TCP4_COMPLETION_TOKEN *token = context; |
| |
| (void)ev; |
| |
| uefi_call_wrapper(BS->CloseEvent, 1, token->Event); |
| } |
| |
| static int volatile cb_status = -1; |
| static EFIAPI void tcp_cb(EFI_EVENT ev, void *context) |
| { |
| EFI_TCP4_COMPLETION_TOKEN *token = context; |
| |
| (void)ev; |
| |
| if (token->Status == EFI_SUCCESS) |
| cb_status = 0; |
| else |
| cb_status = 1; |
| } |
| |
| int core_tcp_connect(struct pxe_pvt_inode *socket, uint32_t ip, uint16_t port) |
| { |
| EFI_TCP4_CONNECTION_TOKEN token; |
| EFI_TCP4_ACCESS_POINT *ap; |
| EFI_TCP4_CONFIG_DATA tdata; |
| struct efi_binding *b = socket->net.efi.binding; |
| EFI_STATUS status; |
| EFI_TCP4 *tcp = (EFI_TCP4 *)b->this; |
| int rv = -1; |
| int unmapped = 1; |
| jiffies_t start, last, cur; |
| |
| memset(&tdata, 0, sizeof(tdata)); |
| |
| ap = &tdata.AccessPoint; |
| ap->UseDefaultAddress = TRUE; |
| memcpy(&ap->RemoteAddress, &ip, sizeof(ip)); |
| ap->RemotePort = port; |
| ap->ActiveFlag = TRUE; /* Initiate active open */ |
| |
| tdata.TimeToLive = 64; |
| |
| last = start = jiffies(); |
| while (unmapped){ |
| status = uefi_call_wrapper(tcp->Configure, 2, tcp, &tdata); |
| if (status != EFI_NO_MAPPING) |
| unmapped = 0; |
| else { |
| cur = jiffies(); |
| if ( (cur - last) >= EFI_NOMAP_PRINT_DELAY ) { |
| last = cur; |
| Print(L"core_tcp_connect: stalling on configure with no mapping\n"); |
| } else if ( (cur - start) > EFI_NOMAP_PRINT_DELAY * EFI_NOMAP_PRINT_COUNT) { |
| Print(L"core_tcp_connect: aborting on no mapping\n"); |
| unmapped = 0; |
| } |
| } |
| } |
| if (status != EFI_SUCCESS) |
| return -1; |
| |
| status = efi_setup_event(&token.CompletionToken.Event, |
| (EFI_EVENT_NOTIFY)tcp_cb, &token.CompletionToken); |
| if (status != EFI_SUCCESS) |
| return -1; |
| |
| status = uefi_call_wrapper(tcp->Connect, 2, tcp, &token); |
| if (status != EFI_SUCCESS) { |
| Print(L"Failed to connect: %d\n", status); |
| goto out; |
| } |
| |
| while (cb_status == -1) |
| uefi_call_wrapper(tcp->Poll, 1, tcp); |
| |
| if (cb_status == 0) |
| rv = 0; |
| |
| /* Reset */ |
| cb_status = -1; |
| |
| out: |
| uefi_call_wrapper(BS->CloseEvent, 1, token.CompletionToken.Event); |
| return rv; |
| } |
| |
| bool core_tcp_is_connected(struct pxe_pvt_inode *socket) |
| { |
| if (socket->net.efi.binding) |
| return true; |
| |
| return false; |
| } |
| |
| int core_tcp_write(struct pxe_pvt_inode *socket, const void *data, |
| size_t len, bool copy) |
| { |
| EFI_TCP4_TRANSMIT_DATA txdata; |
| EFI_TCP4_FRAGMENT_DATA *frag; |
| struct efi_binding *b = socket->net.efi.binding; |
| EFI_TCP4_IO_TOKEN iotoken; |
| EFI_STATUS status; |
| EFI_TCP4 *tcp = (EFI_TCP4 *)b->this; |
| int rv = -1; |
| |
| (void)copy; |
| |
| memset(&iotoken, 0, sizeof(iotoken)); |
| memset(&txdata, 0, sizeof(txdata)); |
| |
| txdata.DataLength = len; |
| txdata.FragmentCount = 1; |
| |
| frag = &txdata.FragmentTable[0]; |
| frag->FragmentLength = len; |
| frag->FragmentBuffer = (void *)data; |
| |
| iotoken.Packet.TxData = &txdata; |
| |
| status = efi_setup_event(&iotoken.CompletionToken.Event, |
| (EFI_EVENT_NOTIFY)tcp_cb, &iotoken.CompletionToken); |
| if (status != EFI_SUCCESS) |
| return -1; |
| |
| status = uefi_call_wrapper(tcp->Transmit, 2, tcp, &iotoken); |
| if (status != EFI_SUCCESS) { |
| Print(L"tcp transmit failed, %d\n", status); |
| goto out; |
| } |
| |
| while (cb_status == -1) |
| uefi_call_wrapper(tcp->Poll, 1, tcp); |
| |
| if (cb_status == 0) |
| rv = 0; |
| |
| /* Reset */ |
| cb_status = -1; |
| |
| out: |
| uefi_call_wrapper(BS->CloseEvent, 1, iotoken.CompletionToken.Event); |
| return rv; |
| } |
| |
| void core_tcp_close_file(struct inode *inode) |
| { |
| struct pxe_pvt_inode *socket = PVT(inode); |
| struct efi_binding *b = socket->net.efi.binding; |
| EFI_TCP4_CLOSE_TOKEN token; |
| EFI_STATUS status; |
| EFI_TCP4 *tcp = (EFI_TCP4 *)b->this; |
| |
| if (!socket->tftp_goteof) { |
| memset(&token, 0, sizeof(token)); |
| |
| status = efi_setup_event(&token.CompletionToken.Event, |
| (EFI_EVENT_NOTIFY)null_cb, |
| &token.CompletionToken); |
| if (status != EFI_SUCCESS) |
| return; |
| |
| status = uefi_call_wrapper(tcp->Close, 2, tcp, &token); |
| if (status != EFI_SUCCESS) |
| Print(L"tcp close failed: %d\n", status); |
| } |
| |
| efi_destroy_binding(b, &Tcp4ServiceBindingProtocol); |
| socket->net.efi.binding = NULL; |
| } |
| |
| static char databuf[8192]; |
| |
| void core_tcp_fill_buffer(struct inode *inode) |
| { |
| struct pxe_pvt_inode *socket = PVT(inode); |
| struct efi_binding *b = socket->net.efi.binding; |
| EFI_TCP4_IO_TOKEN iotoken; |
| EFI_TCP4_RECEIVE_DATA rxdata; |
| EFI_TCP4_FRAGMENT_DATA *frag; |
| EFI_STATUS status; |
| EFI_TCP4 *tcp = (EFI_TCP4 *)b->this; |
| void *data; |
| size_t len; |
| |
| memset(&iotoken, 0, sizeof(iotoken)); |
| memset(&rxdata, 0, sizeof(rxdata)); |
| |
| status = efi_setup_event(&iotoken.CompletionToken.Event, |
| (EFI_EVENT_NOTIFY)tcp_cb, &iotoken.CompletionToken); |
| if (status != EFI_SUCCESS) |
| return; |
| |
| iotoken.Packet.RxData = &rxdata; |
| rxdata.FragmentCount = 1; |
| rxdata.DataLength = sizeof(databuf); |
| frag = &rxdata.FragmentTable[0]; |
| frag->FragmentBuffer = databuf; |
| frag->FragmentLength = sizeof(databuf); |
| |
| status = uefi_call_wrapper(tcp->Receive, 2, tcp, &iotoken); |
| if (status == EFI_CONNECTION_FIN) { |
| socket->tftp_goteof = 1; |
| if (inode->size == (uint64_t)-1) |
| inode->size = socket->tftp_filepos; |
| socket->ops->close(inode); |
| goto out; |
| } |
| |
| while (cb_status == -1) |
| uefi_call_wrapper(tcp->Poll, 1, tcp); |
| |
| /* Reset */ |
| cb_status = -1; |
| |
| len = frag->FragmentLength; |
| memcpy(databuf, frag->FragmentBuffer, len); |
| data = databuf; |
| |
| socket->tftp_dataptr = data; |
| socket->tftp_filepos += len; |
| socket->tftp_bytesleft = len; |
| |
| out: |
| uefi_call_wrapper(BS->CloseEvent, 1, iotoken.CompletionToken.Event); |
| } |