xref: /haiku/src/libs/compat/freebsd_network/device_hooks.c (revision 5c9dc91f64ff6baca3ba1d74b425ac501fe02332)
1 /*
2  * Copyright 2007-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2007, Hugo Santos. All Rights Reserved.
4  * Copyright 2004, Marcus Overhagen. All Rights Reserved.
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "device.h"
10 
11 #include <stdlib.h>
12 #include <sys/sockio.h>
13 
14 #include <Drivers.h>
15 #include <ether_driver.h>
16 #include <kernel.h>
17 #include <net_buffer.h>
18 
19 #include <compat/sys/haiku-module.h>
20 
21 #include <compat/sys/bus.h>
22 #include <compat/sys/mbuf.h>
23 #include <compat/net/ethernet.h>
24 #include <compat/net/if_media.h>
25 
26 
27 static status_t
compat_open(const char * name,uint32 flags,void ** cookie)28 compat_open(const char *name, uint32 flags, void **cookie)
29 {
30 	struct ifnet *ifp;
31 	struct ifreq ifr;
32 	int i;
33 	status_t status;
34 
35 	for (i = 0; i < MAX_DEVICES; i++) {
36 		if (gDevices[i] != NULL && !strcmp(gDevices[i]->device_name, name))
37 			break;
38 	}
39 
40 	if (i == MAX_DEVICES)
41 		return B_ERROR;
42 
43 	ifp = gDevices[i];
44 	if_printf(ifp, "compat_open(0x%" B_PRIx32 ")\n", flags);
45 
46 	if (atomic_or(&ifp->open_count, 1))
47 		return B_BUSY;
48 
49 	IFF_LOCKGIANT(ifp);
50 
51 	if (ifp->if_init != NULL)
52 		ifp->if_init(ifp->if_softc);
53 
54 	if (!HAIKU_DRIVER_REQUIRES(FBSD_WLAN_FEATURE)) {
55 		ifp->if_flags &= ~IFF_UP;
56 		ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
57 
58 		memset(&ifr, 0, sizeof(ifr));
59 		ifr.ifr_media = IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0);
60 		status = ifp->if_ioctl(ifp, SIOCSIFMEDIA, (caddr_t)&ifr);
61 		if (status != B_OK) {
62 			ifr.ifr_media = IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0);
63 			status = ifp->if_ioctl(ifp, SIOCSIFMEDIA, (caddr_t)&ifr);
64 		}
65 	}
66 
67 	ifp->if_flags |= IFF_UP;
68 	ifp->flags &= ~DEVICE_CLOSED;
69 	ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
70 
71 	IFF_UNLOCKGIANT(ifp);
72 
73 	*cookie = ifp;
74 	return B_OK;
75 }
76 
77 
78 static status_t
compat_close(void * cookie)79 compat_close(void *cookie)
80 {
81 	struct ifnet *ifp = cookie;
82 
83 	if_printf(ifp, "compat_close()\n");
84 	IFF_LOCKGIANT(ifp);
85 
86 	atomic_or(&ifp->flags, DEVICE_CLOSED);
87 
88 	wlan_close(cookie);
89 
90 	release_sem_etc(ifp->receive_sem, 1, B_RELEASE_ALL);
91 
92 	IFF_UNLOCKGIANT(ifp);
93 	return B_OK;
94 }
95 
96 
97 static status_t
compat_free(void * cookie)98 compat_free(void *cookie)
99 {
100 	struct ifnet *ifp = cookie;
101 
102 	if_printf(ifp, "compat_free()\n");
103 
104 	// TODO: empty out the send queue
105 
106 	atomic_and(&ifp->open_count, 0);
107 	return B_OK;
108 }
109 
110 
111 static status_t
compat_receive(void * cookie,net_buffer ** _buffer)112 compat_receive(void *cookie, net_buffer **_buffer)
113 {
114 	struct ifnet *ifp = cookie;
115 	uint32 semFlags = B_CAN_INTERRUPT;
116 	status_t status;
117 	struct mbuf *mb;
118 
119 	//if_printf(ifp, "compat_receive(%p)\n", _buffer);
120 
121 	if ((ifp->flags & DEVICE_CLOSED) != 0)
122 		return B_INTERRUPTED;
123 
124 	if (ifp->flags & DEVICE_NON_BLOCK)
125 		semFlags |= B_RELATIVE_TIMEOUT;
126 
127 	do {
128 		status = acquire_sem_etc(ifp->receive_sem, 1, semFlags, 0);
129 		if ((ifp->flags & DEVICE_CLOSED) != 0)
130 			return B_INTERRUPTED;
131 
132 		if (status != B_OK)
133 			return status;
134 
135 		IF_DEQUEUE(&ifp->receive_queue, mb);
136 	} while (mb == NULL);
137 
138 	net_buffer *buffer = gBufferModule->create(0);
139 	if (buffer == NULL) {
140 		m_freem(mb);
141 		return B_NO_MEMORY;
142 	}
143 
144 	for (struct mbuf *m = mb; m != NULL; m = m->m_next) {
145 		status = gBufferModule->append(buffer, mtod(m, void *), m->m_len);
146 		if (status != B_OK)
147 			break;
148 	}
149 	if (status != B_OK) {
150 		gBufferModule->free(buffer);
151 		m_freem(mb);
152 		return status;
153 	}
154 
155 	if ((mb->m_pkthdr.csum_flags & CSUM_L3_VALID) != 0)
156 		buffer->buffer_flags |= NET_BUFFER_L3_CHECKSUM_VALID;
157 	if ((mb->m_pkthdr.csum_flags & CSUM_L4_VALID) != 0)
158 		buffer->buffer_flags |= NET_BUFFER_L4_CHECKSUM_VALID;
159 
160 	*_buffer = buffer;
161 	m_freem(mb);
162 	return B_OK;
163 }
164 
165 
166 static status_t
compat_send(void * cookie,net_buffer * buffer)167 compat_send(void *cookie, net_buffer *buffer)
168 {
169 	struct ifnet *ifp = cookie;
170 	struct mbuf *mb;
171 	int length = buffer->size;
172 
173 	//if_printf(ifp, "compat_send(%p, [%lu])\n", buffer, length);
174 
175 	if (length <= MHLEN) {
176 		mb = m_gethdr(0, MT_DATA);
177 		if (mb == NULL)
178 			return ENOBUFS;
179 	} else {
180 		mb = m_get2(length, 0, MT_DATA, M_PKTHDR);
181 		if (mb == NULL)
182 			return E2BIG;
183 
184 		length = min_c(length, mb->m_ext.ext_size);
185 	}
186 
187 	status_t status = gBufferModule->read(buffer, 0, mtod(mb, void *), length);
188 	if (status != B_OK)
189 		return status;
190 	mb->m_pkthdr.len = mb->m_len = length;
191 
192 	if ((ifp->flags & DEVICE_CLOSED) != 0)
193 		return B_INTERRUPTED;
194 
195 	IFF_LOCKGIANT(ifp);
196 	int result = ifp->if_output(ifp, mb, NULL, NULL);
197 	IFF_UNLOCKGIANT(ifp);
198 
199 	if (result == 0)
200 		gBufferModule->free(buffer);
201 	return result;
202 }
203 
204 
205 static status_t
compat_control(void * cookie,uint32 op,void * arg,size_t length)206 compat_control(void *cookie, uint32 op, void *arg, size_t length)
207 {
208 	struct ifnet *ifp = cookie;
209 	status_t status;
210 
211 	//if_printf(ifp, "compat_control(op %lu, %p, [%lu])\n", op,
212 	//	arg, length);
213 
214 	switch (op) {
215 		case ETHER_INIT:
216 			return B_OK;
217 
218 		case ETHER_GETADDR:
219 			return user_memcpy(arg, IF_LLADDR(ifp), ETHER_ADDR_LEN);
220 
221 		case ETHER_NONBLOCK:
222 		{
223 			int32 value;
224 			if (length < 4)
225 				return B_BAD_VALUE;
226 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
227 				return B_BAD_ADDRESS;
228 			if (value)
229 				ifp->flags |= DEVICE_NON_BLOCK;
230 			else
231 				ifp->flags &= ~DEVICE_NON_BLOCK;
232 			return B_OK;
233 		}
234 
235 		case ETHER_SETPROMISC:
236 		{
237 			int32 value;
238 			if (length < 4)
239 				return B_BAD_VALUE;
240 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
241 				return B_BAD_ADDRESS;
242 			if (value)
243 				ifp->if_flags |= IFF_PROMISC;
244 			else
245 				ifp->if_flags &= ~IFF_PROMISC;
246 
247 			IFF_LOCKGIANT(ifp);
248 			status = ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
249 			IFF_UNLOCKGIANT(ifp);
250 			return status;
251 		}
252 
253 		case ETHER_GETFRAMESIZE:
254 		{
255 			uint32 frameSize;
256 			if (length < 4)
257 				return B_BAD_VALUE;
258 
259 			const int MTUs[] = {
260 				ETHERMTU_JUMBO,
261 				PAGESIZE - (ETHER_HDR_LEN + ETHER_CRC_LEN),
262 				2290, /* IEEE80211_MTU_MAX */
263 				0
264 			};
265 
266 			// This is (usually) only invoked during initialization to get the
267 			// maximum frame size. Thus we try a few common possible values,
268 			// as there is no way to determine what is supported (or required).
269 			for (int i = 0; MTUs[i] != 0; i++) {
270 				struct ifreq ifr;
271 				ifr.ifr_mtu = MTUs[i];
272 				if (compat_control(cookie, SIOCSIFMTU, &ifr, sizeof(ifr)) == 0)
273 					break;
274 			}
275 
276 			frameSize = ifp->if_mtu + ETHER_HDR_LEN;
277 			return user_memcpy(arg, &frameSize, 4);
278 		}
279 
280 		case ETHER_ADDMULTI:
281 		case ETHER_REMMULTI:
282 		{
283 			struct sockaddr_dl address;
284 
285 			if ((ifp->if_flags & IFF_MULTICAST) == 0)
286 				return B_NOT_SUPPORTED;
287 			if (length != ETHER_ADDR_LEN)
288 				return B_BAD_VALUE;
289 
290 			memset(&address, 0, sizeof(address));
291 			address.sdl_family = AF_LINK;
292 			if (user_memcpy(LLADDR(&address), arg, ETHER_ADDR_LEN) < B_OK)
293 				return B_BAD_ADDRESS;
294 
295 			IFF_LOCKGIANT(ifp);
296 			if (op == ETHER_ADDMULTI)
297 				status = if_addmulti(ifp, (struct sockaddr *)&address, NULL);
298 			else
299 				status = if_delmulti(ifp, (struct sockaddr *)&address);
300 			IFF_UNLOCKGIANT(ifp);
301 			return status;
302 		}
303 
304 		case ETHER_GET_LINK_STATE:
305 		{
306 			struct ifmediareq mediareq;
307 			ether_link_state_t state;
308 
309 			if (length < sizeof(ether_link_state_t))
310 				return EINVAL;
311 
312 			memset(&mediareq, 0, sizeof(mediareq));
313 			IFF_LOCKGIANT(ifp);
314 			status = ifp->if_ioctl(ifp, SIOCGIFMEDIA, (caddr_t)&mediareq);
315 			IFF_UNLOCKGIANT(ifp);
316 			if (status < B_OK)
317 				return status;
318 
319 			state.media = mediareq.ifm_active;
320 			if ((mediareq.ifm_status & IFM_ACTIVE) != 0)
321 				state.media |= IFM_ACTIVE;
322 			state.speed = ifmedia_baudrate(mediareq.ifm_active);
323 			state.quality = 1000;
324 
325 			return user_memcpy(arg, &state, sizeof(ether_link_state_t));
326 		}
327 
328 		case ETHER_SET_LINK_STATE_SEM:
329 			if (user_memcpy(&ifp->link_state_sem, arg, sizeof(sem_id)) < B_OK) {
330 				ifp->link_state_sem = -1;
331 				return B_BAD_ADDRESS;
332 			}
333 			return B_OK;
334 
335 		case ETHER_SEND_NET_BUFFER:
336 			if (arg == NULL || length == 0)
337 				return B_BAD_DATA;
338 			if (!IS_KERNEL_ADDRESS(arg))
339 				return B_BAD_ADDRESS;
340 			return compat_send(cookie, (net_buffer*)arg);
341 
342 		case ETHER_RECEIVE_NET_BUFFER:
343 			if (arg == NULL || length == 0)
344 				return B_BAD_DATA;
345 			if (!IS_KERNEL_ADDRESS(arg))
346 				return B_BAD_ADDRESS;
347 			return compat_receive(cookie, (net_buffer**)arg);
348 
349 		case SIOCGIFSTATS:
350 		{
351 			struct ifreq_stats stats;
352 			stats.receive.packets = ifp->if_data.ifi_ipackets;
353 			stats.receive.errors = ifp->if_data.ifi_ierrors;
354 			stats.receive.bytes = ifp->if_data.ifi_ibytes;
355 			stats.receive.multicast_packets = ifp->if_data.ifi_imcasts;
356 			stats.receive.dropped = ifp->if_data.ifi_iqdrops;
357 			stats.send.packets = ifp->if_data.ifi_opackets;
358 			stats.send.errors = ifp->if_data.ifi_oerrors;
359 			stats.send.bytes = ifp->if_data.ifi_obytes;
360 			stats.send.multicast_packets = ifp->if_data.ifi_omcasts;
361 			stats.send.dropped = ifp->if_data.ifi_oqdrops;
362 			stats.collisions = ifp->if_data.ifi_collisions;
363 			memcpy(arg, &stats, sizeof(stats));
364 			return B_OK;
365 		}
366 
367 		case SIOCSIFFLAGS:
368 		case SIOCSIFMEDIA:
369 		case SIOCSIFMTU:
370 		{
371 			IFF_LOCKGIANT(ifp);
372 			status = ifp->if_ioctl(ifp, op, (caddr_t)arg);
373 			IFF_UNLOCKGIANT(ifp);
374 			return status;
375 		}
376 	}
377 
378 	return wlan_control(cookie, op, arg, length);
379 }
380 
381 
382 device_hooks gDeviceHooks = {
383 	compat_open,
384 	compat_close,
385 	compat_free,
386 	compat_control,
387 	NULL,
388 	NULL,
389 };
390