fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
1/*****************************************************************************
2 * pce *
3 *****************************************************************************/
4
5/*****************************************************************************
6 * File name: src/lib/vmnet.m *
7 * Created: 2022-09-18 by joshua stein <jcs@jcs.org> *
8 * Copyright: (C) 2022 joshua stein <jcs@jcs.org> *
9 *****************************************************************************/
10
11/*****************************************************************************
12 * This program is free software. You can redistribute it and / or modify it *
13 * under the terms of the GNU General Public License version 2 as published *
14 * by the Free Software Foundation. *
15 * *
16 * This program is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY, without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
19 * Public License for more details. *
20 *****************************************************************************/
21
22#include "log.h"
23
24#import "vmnet.h"
25
26struct pce_vmnet_interface * pce_vmnet_start (char *bridged_if)
27{
28 __block struct pce_vmnet_interface *ret;
29 __block vmnet_return_t if_status;
30
31 ret = malloc (sizeof(struct pce_vmnet_interface));
32 if (ret == NULL) {
33 return NULL;
34 }
35
36 memset (ret, 0, sizeof(struct pce_vmnet_interface));
37
38 ret->if_queue = dispatch_queue_create ("ch.hampa.pce.vmnet", DISPATCH_QUEUE_SERIAL);
39 dispatch_semaphore_t if_start_sem = dispatch_semaphore_create (0);
40
41 xpc_object_t if_desc = xpc_dictionary_create (NULL, NULL, 0);
42
43 if (bridged_if == NULL || bridged_if[0] == '\0') {
44 xpc_dictionary_set_uint64 (if_desc, vmnet_operation_mode_key, VMNET_SHARED_MODE);
45 xpc_dictionary_set_string (if_desc, vmnet_start_address_key, "10.10.10.1");
46 xpc_dictionary_set_string (if_desc, vmnet_end_address_key, "10.10.10.5");
47 xpc_dictionary_set_string (if_desc, vmnet_subnet_mask_key, "255.255.255.0");
48 pce_log_tag (MSG_INF,
49 "SCSI:",
50 "shared net ip=%s netmask=%s\n",
51 "10.10.10.1", "255.255.255.0"
52 );
53 } else {
54 xpc_dictionary_set_uint64 (if_desc, vmnet_operation_mode_key, VMNET_BRIDGED_MODE);
55 xpc_dictionary_set_string (if_desc, vmnet_shared_interface_name_key, bridged_if);
56 pce_log_tag (MSG_INF,
57 "SCSI:",
58 "bridged net if=%s\n",
59 bridged_if
60 );
61 }
62
63 ret->interface = vmnet_start_interface (if_desc, ret->if_queue,
64 ^(vmnet_return_t status, xpc_object_t if_param) {
65 if_status = status;
66
67 if (status != VMNET_SUCCESS) {
68 dispatch_semaphore_signal (if_start_sem);
69 if (status == VMNET_FAILURE) {
70 pce_log (MSG_ERR, "*** vmnet_start_interface failed, "
71 "try with root privileges?\n");
72 }
73 return;
74 }
75
76 dispatch_semaphore_signal (if_start_sem);
77 });
78
79 dispatch_semaphore_wait (if_start_sem, DISPATCH_TIME_FOREVER);
80 dispatch_release (if_start_sem);
81 xpc_release (if_desc);
82
83 if (if_status != VMNET_SUCCESS) {
84 dispatch_release (ret->if_queue);
85 free (ret);
86 return NULL;
87 }
88
89 vmnet_interface_set_event_callback (ret->interface,
90 VMNET_INTERFACE_PACKETS_AVAILABLE, ret->if_queue,
91 ^(interface_event_t event_id, xpc_object_t event) {
92 unsigned int navail = xpc_dictionary_get_uint64 (event,
93 vmnet_estimated_packets_available_key);
94 ret->packets_available = navail;
95 });
96
97 return ret;
98}
99
100void pce_vmnet_stop (struct pce_vmnet_interface *vi)
101{
102 if (vi == NULL || vi->interface == NULL) {
103 return;
104 }
105
106 dispatch_semaphore_t if_stop_sem = dispatch_semaphore_create (0);
107
108 vmnet_interface_set_event_callback(vi->interface,
109 VMNET_INTERFACE_PACKETS_AVAILABLE, NULL, NULL);
110
111 vmnet_stop_interface (vi->interface, vi->if_queue,
112 ^(vmnet_return_t status) {
113 dispatch_semaphore_signal (if_stop_sem);
114 });
115
116 dispatch_semaphore_wait (if_stop_sem, DISPATCH_TIME_FOREVER);
117 dispatch_release (vi->if_queue);
118
119 free (vi);
120}
121
122int pce_vmnet_write (struct pce_vmnet_interface *vi, void *buf, size_t size)
123{
124 struct iovec packets_iovec = {
125 .iov_base = buf,
126 .iov_len = size,
127 };
128
129 struct vmpktdesc packets = {
130 .vm_pkt_size = size,
131 .vm_pkt_iov = &packets_iovec,
132 .vm_pkt_iovcnt = 1,
133 .vm_flags = 0,
134 };
135
136 int count = packets.vm_pkt_iovcnt;
137
138 if (vi == NULL || vi->interface == NULL) {
139 return -1;
140 }
141
142 if (vmnet_write (vi->interface, &packets, &count) != VMNET_SUCCESS) {
143 return -1;
144 }
145
146 return 0;
147}
148
149size_t pce_vmnet_read (struct pce_vmnet_interface *vi, void *buf, size_t size)
150{
151 struct iovec packets_iovec = {
152 .iov_base = buf,
153 .iov_len = size,
154 };
155
156 struct vmpktdesc packets = {
157 .vm_pkt_size = size,
158 .vm_pkt_iov = &packets_iovec,
159 .vm_pkt_iovcnt = 1,
160 .vm_flags = 0,
161 };
162
163 int count = 1;
164
165 vmnet_return_t status = vmnet_read (vi->interface, &packets, &count);
166
167 if (status != VMNET_SUCCESS) {
168 return 0;
169 }
170
171 if (count == 0) {
172 return 0;
173 }
174
175 if (vi->packets_available > 0)
176 vi->packets_available--;
177
178 return packets.vm_pkt_size;
179}