1 /* 2 * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3 * Copyright 2010, Andreas Faerber <andreas.faerber@web.de> 4 * Copyright 2002, Adrien Destugues <pulkomandy@pulkomandy.tk> 5 * All rights reserved. Distributed under the terms of the MIT License. 6 */ 7 8 9 #include <new> 10 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <OS.h> 16 17 #include <boot/platform.h> 18 #include <boot/net/Ethernet.h> 19 #include <boot/net/IP.h> 20 #include <boot/net/NetStack.h> 21 #include <platform/openfirmware/openfirmware.h> 22 23 24 //#define TRACE_NETWORK 25 #ifdef TRACE_NETWORK 26 # define TRACE(x) dprintf x 27 #else 28 # define TRACE(x) ; 29 #endif 30 31 32 class OFEthernetInterface : public EthernetInterface { 33 public: 34 OFEthernetInterface(); 35 virtual ~OFEthernetInterface(); 36 37 status_t Init(const char *device, const char *parameters); 38 39 virtual mac_addr_t MACAddress() const; 40 41 virtual void * AllocateSendReceiveBuffer(size_t size); 42 virtual void FreeSendReceiveBuffer(void *buffer); 43 44 virtual ssize_t Send(const void *buffer, size_t size); 45 virtual ssize_t Receive(void *buffer, size_t size); 46 47 private: 48 status_t FindMACAddress(); 49 50 private: 51 intptr_t fHandle; 52 mac_addr_t fMACAddress; 53 }; 54 55 56 #ifdef TRACE_NETWORK 57 58 static void 59 hex_dump(const void *_data, int length) 60 { 61 uint8 *data = (uint8*)_data; 62 for (int i = 0; i < length; i++) { 63 if (i % 4 == 0) { 64 if (i % 32 == 0) { 65 if (i != 0) 66 printf("\n"); 67 printf("%03x: ", i); 68 } else 69 printf(" "); 70 } 71 72 printf("%02x", data[i]); 73 } 74 printf("\n"); 75 } 76 77 #else // !TRACE_NETWORK 78 79 #define hex_dump(data, length) 80 81 #endif // !TRACE_NETWORK 82 83 84 // #pragma mark - 85 86 87 OFEthernetInterface::OFEthernetInterface() 88 : 89 EthernetInterface(), 90 fHandle(OF_FAILED), 91 fMACAddress(kNoMACAddress) 92 { 93 } 94 95 96 OFEthernetInterface::~OFEthernetInterface() 97 { 98 if (fHandle != OF_FAILED) 99 of_close(fHandle); 100 } 101 102 103 status_t 104 OFEthernetInterface::FindMACAddress() 105 { 106 intptr_t package = of_instance_to_package(fHandle); 107 108 // get MAC address 109 int bytesRead = of_getprop(package, "local-mac-address", &fMACAddress, 110 sizeof(fMACAddress)); 111 if (bytesRead == (int)sizeof(fMACAddress)) 112 return B_OK; 113 114 // Failed to get the MAC address of the network device. The system may 115 // have a global standard MAC address. 116 bytesRead = of_getprop(gChosen, "mac-address", &fMACAddress, 117 sizeof(fMACAddress)); 118 if (bytesRead == (int)sizeof(fMACAddress)) { 119 return B_OK; 120 } 121 122 // On Sun machines, there is a global word 'mac-address' which returns 123 // the size and a pointer to the MAC address 124 size_t size; 125 void* ptr; 126 if (of_interpret("mac-address", 0, 2, &size, &ptr) != OF_FAILED) { 127 if (size == sizeof(fMACAddress)) { 128 memcpy(&fMACAddress, ptr, size); 129 return B_OK; 130 } 131 } 132 133 return B_ERROR; 134 } 135 136 137 status_t 138 OFEthernetInterface::Init(const char *device, const char *parameters) 139 { 140 if (!device) 141 return B_BAD_VALUE; 142 143 // open device 144 fHandle = of_open(device); 145 if (fHandle == OF_FAILED) { 146 printf("opening ethernet device failed\n"); 147 return B_ERROR; 148 } 149 150 if (FindMACAddress() != B_OK) { 151 printf("Failed to get MAC address\n"); 152 return B_ERROR; 153 } 154 155 // get IP address 156 157 // Note: This is a non-standardized way. On my Mac mini the response of the 158 // DHCP server is stored as property of /chosen. We try to get it and use 159 // the IP address we find in there. 160 // TODO Sun machines may use bootp-response instead? 161 struct { 162 uint8 irrelevant[16]; 163 uint32 ip_address; 164 // ... 165 } dhcpResponse; 166 int bytesRead = of_getprop(gChosen, "dhcp-response", &dhcpResponse, 167 sizeof(dhcpResponse)); 168 if (bytesRead != OF_FAILED && bytesRead == (int)sizeof(dhcpResponse)) { 169 SetIPAddress(ntohl(dhcpResponse.ip_address)); 170 return B_OK; 171 } 172 173 // try to read manual client IP from boot path 174 if (parameters != NULL) { 175 char *comma = strrchr(parameters, ','); 176 if (comma != NULL && comma != strchr(parameters, ',')) { 177 SetIPAddress(ip_parse_address(comma + 1)); 178 return B_OK; 179 } 180 } 181 182 // try to read default-client-ip setting 183 char defaultClientIP[16]; 184 intptr_t package = of_finddevice("/options"); 185 bytesRead = of_getprop(package, "default-client-ip", 186 defaultClientIP, sizeof(defaultClientIP) - 1); 187 if (bytesRead != OF_FAILED && bytesRead > 1) { 188 defaultClientIP[bytesRead] = '\0'; 189 ip_addr_t address = ip_parse_address(defaultClientIP); 190 SetIPAddress(address); 191 return B_OK; 192 } 193 194 return B_ERROR; 195 } 196 197 198 mac_addr_t 199 OFEthernetInterface::MACAddress() const 200 { 201 return fMACAddress; 202 } 203 204 205 void * 206 OFEthernetInterface::AllocateSendReceiveBuffer(size_t size) 207 { 208 void *dmaMemory = NULL; 209 210 if (of_call_method(fHandle, "dma-alloc", 1, 1, size, &dmaMemory) 211 != OF_FAILED) { 212 return dmaMemory; 213 } 214 215 // The dma-alloc method could be on the parent node (PCI bus, for example), 216 // rather than the device itself 217 intptr_t parentPackage = of_parent(of_instance_to_package(fHandle)); 218 219 // FIXME surely there's a way to create an instance without going through 220 // the path? 221 char path[256]; 222 of_package_to_path(parentPackage, path, sizeof(path)); 223 intptr_t parentInstance = of_open(path); 224 225 if (of_call_method(parentInstance, "dma-alloc", 1, 1, size, &dmaMemory) 226 != OF_FAILED) { 227 of_close(parentInstance); 228 return dmaMemory; 229 } 230 231 of_close(parentInstance); 232 233 return NULL; 234 } 235 236 237 void 238 OFEthernetInterface::FreeSendReceiveBuffer(void *buffer) 239 { 240 if (buffer) 241 of_call_method(fHandle, "dma-free", 1, 0, buffer); 242 } 243 244 245 ssize_t 246 OFEthernetInterface::Send(const void *buffer, size_t size) 247 { 248 TRACE(("OFEthernetInterface::Send(%p, %lu)\n", buffer, size)); 249 250 if (!buffer) 251 return B_BAD_VALUE; 252 253 hex_dump(buffer, size); 254 255 int result = of_write(fHandle, buffer, size); 256 return (result == OF_FAILED ? B_ERROR : result); 257 } 258 259 260 ssize_t 261 OFEthernetInterface::Receive(void *buffer, size_t size) 262 { 263 if (!buffer) 264 return B_BAD_VALUE; 265 266 int result = of_read(fHandle, buffer, size); 267 268 if (result != OF_FAILED && result >= 0) { 269 TRACE(("OFEthernetInterface::Receive(%p, %lu): received %d bytes\n", 270 buffer, size, result)); 271 hex_dump(buffer, result); 272 } 273 274 return (result == OF_FAILED ? B_ERROR : result); 275 } 276 277 278 // #pragma mark - 279 280 281 status_t 282 platform_net_stack_init() 283 { 284 // Note: At the moment we only do networking at all, if the boot device 285 // is a network device. If it isn't, we simply fail here. For serious 286 // support we would want to iterate through the device tree and add all 287 // network devices. 288 289 // get boot path 290 char bootPath[192]; 291 int length = of_getprop(gChosen, "bootpath", bootPath, sizeof(bootPath)); 292 if (length <= 1) 293 return B_ERROR; 294 295 // we chop off parameters; otherwise opening the network device might have 296 // side effects 297 char *lastComponent = strrchr(bootPath, '/'); 298 char *parameters = strchr((lastComponent ? lastComponent : bootPath), ':'); 299 if (parameters) 300 *parameters = '\0'; 301 302 // get device node 303 intptr_t node = of_finddevice(bootPath); 304 if (node == OF_FAILED) 305 return B_ERROR; 306 307 // get device type 308 char type[16]; 309 if (of_getprop(node, "device_type", type, sizeof(type)) == OF_FAILED 310 || strcmp("network", type) != 0) { 311 return B_ERROR; 312 } 313 314 // create an EthernetInterface object for the device 315 OFEthernetInterface *interface = new(nothrow) OFEthernetInterface; 316 if (!interface) 317 return B_NO_MEMORY; 318 319 status_t error = interface->Init(bootPath, parameters + 1); 320 if (error != B_OK) { 321 delete interface; 322 return error; 323 } 324 325 // add it to the net stack 326 error = NetStack::Default()->AddEthernetInterface(interface); 327 if (error != B_OK) { 328 delete interface; 329 return error; 330 } 331 332 return B_OK; 333 } 334