1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "eth-client.h"
6 
7 #include <fuchsia/hardware/ethernet/c/fidl.h>
8 #include <zircon/syscalls.h>
9 
10 #include <limits.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 #if 0
16 #define IORING_TRACE(fmt...) fprintf(stderr, fmt)
17 #else
18 #define IORING_TRACE(fmt...) do {} while (0)
19 #endif
20 
eth_destroy(eth_client_t * eth)21 void eth_destroy(eth_client_t* eth) {
22     zx_handle_close(eth->rx_fifo);
23     zx_handle_close(eth->tx_fifo);
24     free(eth);
25 }
26 
eth_create(zx_handle_t svc,zx_handle_t io_vmo,void * io_mem,eth_client_t ** out)27 zx_status_t eth_create(zx_handle_t svc, zx_handle_t io_vmo, void* io_mem, eth_client_t** out) {
28     eth_client_t* eth;
29 
30     if ((eth = calloc(1, sizeof(*eth))) == NULL) {
31         return ZX_ERR_NO_MEMORY;
32     }
33 
34     fuchsia_hardware_ethernet_Fifos fifos;
35     zx_status_t status, call_status;
36 
37     status = fuchsia_hardware_ethernet_DeviceGetFifos(svc, &call_status, &fifos);
38     if (status != ZX_OK || call_status != ZX_OK) {
39         fprintf(stderr, "eth_create: failed to get fifos: %d, %d\n", status, call_status);
40         return status == ZX_OK ? call_status : status;
41     }
42 
43     zx_handle_t vmo;
44     if ((status = zx_handle_duplicate(io_vmo, ZX_RIGHT_SAME_RIGHTS, &vmo)) < 0) {
45         fprintf(stderr, "eth_create: failed to duplicate vmo\n");
46         goto fail;
47     }
48     status = fuchsia_hardware_ethernet_DeviceSetIOBuffer(svc, vmo, &call_status);
49     if (status != ZX_OK || call_status != ZX_OK) {
50         fprintf(stderr, "eth_create: failed to set iobuf: %d, %d\n", status, call_status);
51         if (status == ZX_OK) {
52             status = call_status;
53         }
54         goto fail;
55     }
56     status = fuchsia_hardware_ethernet_DeviceSetClientName(svc, "netsvc", 6, &call_status);
57     if (status != ZX_OK || call_status != ZX_OK) {
58         fprintf(stderr, "eth_create: failed to set client name %d, %d\n", status, call_status);
59     }
60 
61     eth->tx_fifo = fifos.tx;
62     eth->rx_fifo = fifos.rx;
63     eth->rx_size = fifos.rx_depth;
64     eth->tx_size = fifos.tx_depth;
65     eth->iobuf = io_mem;
66 
67     *out = eth;
68     return ZX_OK;
69 
70 fail:
71     zx_handle_close(fifos.tx);
72     zx_handle_close(fifos.rx);
73     eth_destroy(eth);
74     return status;
75 }
76 
eth_queue_tx(eth_client_t * eth,void * cookie,void * data,size_t len,uint32_t options)77 zx_status_t eth_queue_tx(eth_client_t* eth, void* cookie,
78                          void* data, size_t len, uint32_t options) {
79     fuchsia_hardware_ethernet_FifoEntry e = {
80         .offset = data - eth->iobuf,
81         .length = len,
82         .flags = options,
83         .cookie = (uint64_t)cookie,
84     };
85     IORING_TRACE("eth:tx+ c=%p o=%u l=%u f=%u\n",
86                  e.cookie, e.offset, e.length, e.flags);
87     return zx_fifo_write(eth->tx_fifo, sizeof(e), &e, 1, NULL);
88 }
89 
eth_queue_rx(eth_client_t * eth,void * cookie,void * data,size_t len,uint32_t options)90 zx_status_t eth_queue_rx(eth_client_t* eth, void* cookie,
91                          void* data, size_t len, uint32_t options) {
92     fuchsia_hardware_ethernet_FifoEntry e = {
93         .offset = data - eth->iobuf,
94         .length = len,
95         .flags = options,
96         .cookie = (uint64_t)cookie,
97     };
98     IORING_TRACE("eth:rx+ c=%p o=%u l=%u f=%u\n",
99                  e.cookie, e.offset, e.length, e.flags);
100     return zx_fifo_write(eth->rx_fifo, sizeof(e), &e, 1, NULL);
101 }
102 
eth_complete_tx(eth_client_t * eth,void * ctx,void (* func)(void * ctx,void * cookie))103 zx_status_t eth_complete_tx(eth_client_t* eth, void* ctx,
104                             void (*func)(void* ctx, void* cookie)) {
105     fuchsia_hardware_ethernet_FifoEntry entries[eth->tx_size];
106     zx_status_t status;
107     size_t count;
108     if ((status = zx_fifo_read(eth->tx_fifo, sizeof(entries[0]), entries, countof(entries), &count)) < 0) {
109         if (status == ZX_ERR_SHOULD_WAIT) {
110             return ZX_OK;
111         } else {
112             return status;
113         }
114     }
115 
116     for (fuchsia_hardware_ethernet_FifoEntry* e = entries; count-- > 0; e++) {
117         IORING_TRACE("eth:tx- c=%p o=%u l=%u f=%u\n",
118                      e->cookie, e->offset, e->length, e->flags);
119         func(ctx, (void*)e->cookie);
120     }
121     return ZX_OK;
122 }
123 
eth_complete_rx(eth_client_t * eth,void * ctx,void (* func)(void * ctx,void * cookie,size_t len,uint32_t flags))124 zx_status_t eth_complete_rx(eth_client_t* eth, void* ctx,
125                             void (*func)(void* ctx, void* cookie, size_t len, uint32_t flags)) {
126     fuchsia_hardware_ethernet_FifoEntry entries[eth->rx_size];
127     zx_status_t status;
128     size_t count;
129     if ((status = zx_fifo_read(eth->rx_fifo, sizeof(entries[0]), entries, countof(entries), &count)) < 0) {
130         if (status == ZX_ERR_SHOULD_WAIT) {
131             return ZX_OK;
132         } else {
133             return status;
134         }
135     }
136 
137     for (fuchsia_hardware_ethernet_FifoEntry* e = entries; count-- > 0; e++) {
138         IORING_TRACE("eth:rx- c=%p o=%u l=%u f=%u\n",
139                      e->cookie, e->offset, e->length, e->flags);
140         func(ctx, (void*)e->cookie, e->length, e->flags);
141     }
142     return ZX_OK;
143 }
144 
145 
146 // Wait for completed rx packets
147 // ZX_ERR_PEER_CLOSED - far side disconnected
148 // ZX_ERR_TIMED_OUT - deadline lapsed
149 // ZX_OK - completed packets are available
eth_wait_rx(eth_client_t * eth,zx_time_t deadline)150 zx_status_t eth_wait_rx(eth_client_t* eth, zx_time_t deadline) {
151     zx_status_t status;
152     zx_signals_t signals;
153 
154     if ((status = zx_object_wait_one(eth->rx_fifo,
155                                      ZX_FIFO_READABLE | ZX_FIFO_PEER_CLOSED,
156                                      deadline, &signals)) < 0) {
157         if (signals & ZX_FIFO_READABLE) {
158             return ZX_OK;
159         }
160         return status;
161     }
162     if (signals & ZX_FIFO_PEER_CLOSED) {
163         return ZX_ERR_PEER_CLOSED;
164     }
165     return ZX_OK;
166 }
167