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