fork of PCE focusing on macplus, supporting DaynaPort SCSI network emulation
at master 179 lines 5.3 kB view raw
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}