xref: /haiku/src/system/boot/platform/openfirmware/network.cpp (revision 1e36cfc2721ef13a187c6f7354dc9cbc485e89d3)
1 /*
2  * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 #include <new>
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <OS.h>
13 
14 #include <boot/net/Ethernet.h>
15 #include <boot/net/NetStack.h>
16 #include <platform/openfirmware/openfirmware.h>
17 
18 
19 //#define TRACE_NETWORK
20 #ifdef TRACE_NETWORK
21 #	define TRACE(x) dprintf x
22 #else
23 #	define TRACE(x) ;
24 #endif
25 
26 
27 #ifdef TRACE_NETWORK
28 
29 static void
30 hex_dump(const void *_data, int length)
31 {
32 	uint8 *data = (uint8*)_data;
33 	for (int i = 0; i < length; i++) {
34 		if (i % 4 == 0) {
35 			if (i % 32 == 0) {
36 				if (i != 0)
37 					printf("\n");
38 				printf("%03x: ", i);
39 			} else
40 				printf(" ");
41 		}
42 
43 		printf("%02x", data[i]);
44 	}
45 	printf("\n");
46 }
47 
48 #else	// !TRACE_NETWORK
49 
50 #define hex_dump(data, length)
51 
52 #endif	// !TRACE_NETWORK
53 
54 
55 class OFEthernetInterface : public EthernetInterface {
56 public:
57 	OFEthernetInterface();
58 	virtual ~OFEthernetInterface();
59 
60 	status_t Init(const char *device);
61 
62 	virtual mac_addr_t MACAddress() const;
63 
64 	virtual	void *AllocateSendReceiveBuffer(size_t size);
65 	virtual	void FreeSendReceiveBuffer(void *buffer);
66 
67 	virtual ssize_t Send(const void *buffer, size_t size);
68 	virtual ssize_t Receive(void *buffer, size_t size);
69 
70 private:
71 	int			fHandle;
72 	mac_addr_t	fMACAddress;
73 };
74 
75 // constructor
76 OFEthernetInterface::OFEthernetInterface()
77 	: EthernetInterface(),
78 	  fHandle(OF_FAILED),
79 	  fMACAddress(kNoMACAddress)
80 {
81 }
82 
83 // destructor
84 OFEthernetInterface::~OFEthernetInterface()
85 {
86 	if (fHandle != OF_FAILED)
87 		of_close(fHandle);
88 }
89 
90 // Init
91 status_t
92 OFEthernetInterface::Init(const char *device)
93 {
94 	if (!device)
95 		return B_BAD_VALUE;
96 
97 	// open device
98 	fHandle = of_open(device);
99 	if (fHandle == OF_FAILED) {
100 		printf("opening ethernet device failed\n");
101 		return B_ERROR;
102 	}
103 
104 	int package = of_instance_to_package(fHandle);
105 
106 	// get MAC address
107 	int bytesRead = of_getprop(package, "local-mac-address", &fMACAddress,
108 		sizeof(fMACAddress));
109 	if (bytesRead == OF_FAILED || bytesRead < (int)sizeof(fMACAddress)) {
110 		// Failed to get the MAC address of the network device. The system may
111 		// have a global standard MAC address.
112 		bytesRead = of_getprop(gChosen, "mac-address", &fMACAddress,
113 			sizeof(fMACAddress));
114 		if (bytesRead == OF_FAILED || bytesRead < (int)sizeof(fMACAddress)) {
115 			printf("Failed to get MAC address\n");
116 			return B_ERROR;
117 		}
118 	}
119 
120 	// get IP address
121 	// Note: This is a non-standardized way. On my Mac mini the response of the
122 	// DHCP server is stored as property of /chosen. We try to get that it and
123 	// use the IP address we find in there.
124 	struct {
125 		uint8	irrelevant[16];
126 		uint32	ip_address;
127 		// ...
128 	} dhcpResponse;
129 	bytesRead = of_getprop(gChosen, "dhcp-response", &dhcpResponse,
130 		sizeof(dhcpResponse));
131 	if (bytesRead != OF_FAILED && bytesRead == (int)sizeof(dhcpResponse))
132 		SetIPAddress(ntohl(dhcpResponse.ip_address));
133 
134 	return B_OK;
135 }
136 
137 // MACAddress
138 mac_addr_t
139 OFEthernetInterface::MACAddress() const
140 {
141 	return fMACAddress;
142 }
143 
144 // AllocateSendReceiveBuffer
145 void *
146 OFEthernetInterface::AllocateSendReceiveBuffer(size_t size)
147 {
148 	void *dmaMemory;
149 	if (of_call_method(fHandle, "dma-alloc", 1, 1, size, &dmaMemory)
150 			== OF_FAILED) {
151 		return NULL;
152 	}
153 	return dmaMemory;
154 }
155 
156 // FreeSendReceiveBuffer
157 void
158 OFEthernetInterface::FreeSendReceiveBuffer(void *buffer)
159 {
160 	if (buffer)
161 		of_call_method(fHandle, "dma-free", 1, 0, buffer);
162 }
163 
164 // Send
165 ssize_t
166 OFEthernetInterface::Send(const void *buffer, size_t size)
167 {
168 	TRACE(("OFEthernetInterface::Send(%p, %lu)\n", buffer, size));
169 
170 	if (!buffer)
171 		return B_BAD_VALUE;
172 
173 	hex_dump(buffer, size);
174 
175 	int result = of_write(fHandle, buffer, size);
176 	return (result == OF_FAILED ? B_ERROR : result);
177 }
178 
179 // Receive
180 ssize_t
181 OFEthernetInterface::Receive(void *buffer, size_t size)
182 {
183 	if (!buffer)
184 		return B_BAD_VALUE;
185 
186 	int result = of_read(fHandle, buffer, size);
187 
188 	if (result != OF_FAILED && result >= 0) {
189 		TRACE(("OFEthernetInterface::Receive(%p, %lu): received %d bytes\n",
190 			buffer, size, result));
191 		hex_dump(buffer, result);
192 	}
193 
194 	return (result == OF_FAILED ? B_ERROR : result);
195 }
196 
197 
198 // #pragma mark -
199 
200 // platform_net_stack_init
201 status_t
202 platform_net_stack_init()
203 {
204 	// Note: At the moment we only do networking at all, if the boot device
205 	// is a network device. If it isn't, we simply fail here. For serious
206 	// support we would want to iterate through the device tree and add all
207 	// network devices.
208 
209 	// get boot path
210 	char bootPath[192];
211 	int length = of_getprop(gChosen, "bootpath", bootPath, sizeof(bootPath));
212 	if (length <= 1)
213 		return B_ERROR;
214 
215 	// we chop off parameters; otherwise opening the network device might have
216 	// side effects
217 	char *lastComponent = strrchr(bootPath, '/');
218 	char *parameters = strchr((lastComponent ? lastComponent : bootPath), ':');
219 	if (parameters)
220 		*parameters = '\0';
221 
222 	// get device node
223 	int node = of_finddevice(bootPath);
224 	if (node == OF_FAILED)
225 		return B_ERROR;
226 
227 	// get device type
228 	char type[16];
229 	if (of_getprop(node, "device_type", type, sizeof(type)) == OF_FAILED
230 		|| strcmp("network", type) != 0) {
231 		return B_ERROR;
232 	}
233 
234 	// create an EthernetInterface object for the device
235 	OFEthernetInterface *interface = new(nothrow) OFEthernetInterface;
236 	if (!interface)
237 		return B_NO_MEMORY;
238 
239 	status_t error = interface->Init(bootPath);
240 	if (error != B_OK) {
241 		delete interface;
242 		return error;
243 	}
244 
245 	// add it to the net stack
246 	error = NetStack::Default()->AddEthernetInterface(interface);
247 	if (error != B_OK) {
248 		delete interface;
249 		return error;
250 	}
251 
252 	return B_OK;
253 }
254