xref: /haiku/src/libs/compat/freebsd_network/device_hooks.c (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 
17 #include <compat/sys/haiku-module.h>
18 
19 #include <compat/sys/bus.h>
20 #include <compat/sys/mbuf.h>
21 #include <compat/net/ethernet.h>
22 #include <compat/net/if_media.h>
23 
24 
25 static status_t
26 compat_open(const char *name, uint32 flags, void **cookie)
27 {
28 	struct ifnet *ifp;
29 	struct ifreq ifr;
30 	int i;
31 	status_t status;
32 
33 	for (i = 0; i < MAX_DEVICES; i++) {
34 		if (gDevices[i] != NULL && !strcmp(gDevices[i]->device_name, name))
35 			break;
36 	}
37 
38 	if (i == MAX_DEVICES)
39 		return B_ERROR;
40 
41 	if (get_module(NET_STACK_MODULE_NAME, (module_info **)&gStack) != B_OK)
42 		return B_ERROR;
43 
44 	ifp = gDevices[i];
45 	if_printf(ifp, "compat_open(0x%" B_PRIx32 ")\n", flags);
46 
47 	if (atomic_or(&ifp->open_count, 1)) {
48 		put_module(NET_STACK_MODULE_NAME);
49 		return B_BUSY;
50 	}
51 
52 	IFF_LOCKGIANT(ifp);
53 
54 	ifp->if_init(ifp->if_softc);
55 
56 	if (!HAIKU_DRIVER_REQUIRES(FBSD_WLAN_FEATURE)) {
57 		ifp->if_flags &= ~IFF_UP;
58 		ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
59 
60 		memset(&ifr, 0, sizeof(ifr));
61 		ifr.ifr_media = IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0);
62 		status = ifp->if_ioctl(ifp, SIOCSIFMEDIA, (caddr_t)&ifr);
63 		if (status != B_OK) {
64 			ifr.ifr_media = IFM_MAKEWORD(IFM_ETHER, IFM_10_T, 0, 0);
65 			status = ifp->if_ioctl(ifp, SIOCSIFMEDIA, (caddr_t)&ifr);
66 		}
67 	}
68 
69 	ifp->if_flags |= IFF_UP;
70 	ifp->flags &= ~DEVICE_CLOSED;
71 	ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
72 
73 	IFF_UNLOCKGIANT(ifp);
74 
75 	*cookie = ifp;
76 	return B_OK;
77 }
78 
79 
80 static status_t
81 compat_close(void *cookie)
82 {
83 	struct ifnet *ifp = cookie;
84 
85 	if_printf(ifp, "compat_close()\n");
86 
87 	atomic_or(&ifp->flags, DEVICE_CLOSED);
88 
89 	wlan_close(cookie);
90 
91 	release_sem_etc(ifp->receive_sem, 1, B_RELEASE_ALL);
92 
93 	return B_OK;
94 }
95 
96 
97 static status_t
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 	put_module(NET_STACK_MODULE_NAME);
108 	return B_OK;
109 }
110 
111 
112 static status_t
113 compat_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
114 {
115 	struct ifnet *ifp = cookie;
116 	uint32 semFlags = B_CAN_INTERRUPT;
117 	status_t status;
118 	struct mbuf *mb;
119 	size_t length = *numBytes;
120 
121 	//if_printf(ifp, "compat_read(%lld, %p, [%lu])\n", position,
122 	//	buffer, *numBytes);
123 
124 	if (ifp->flags & DEVICE_CLOSED)
125 		return B_INTERRUPTED;
126 
127 	if (ifp->flags & DEVICE_NON_BLOCK)
128 		semFlags |= B_RELATIVE_TIMEOUT;
129 
130 	do {
131 		status = acquire_sem_etc(ifp->receive_sem, 1, semFlags, 0);
132 		if (ifp->flags & DEVICE_CLOSED)
133 			return B_INTERRUPTED;
134 
135 		if (status == B_WOULD_BLOCK) {
136 			*numBytes = 0;
137 			return B_OK;
138 		} else if (status < B_OK)
139 			return status;
140 
141 		IF_DEQUEUE(&ifp->receive_queue, mb);
142 	} while (mb == NULL);
143 
144 	if (mb->m_pkthdr.len > length) {
145 		if_printf(ifp, "error reading packet: too large! (%d > %" B_PRIuSIZE ")\n",
146 			mb->m_pkthdr.len, length);
147 		m_freem(mb);
148 		*numBytes = 0;
149 		return E2BIG;
150 	}
151 
152 	length = min_c(max_c((size_t)mb->m_pkthdr.len, 0), length);
153 
154 	m_copydata(mb, 0, length, buffer);
155 	*numBytes = length;
156 
157 	m_freem(mb);
158 	return B_OK;
159 }
160 
161 
162 static status_t
163 compat_write(void *cookie, off_t position, const void *buffer,
164 	size_t *numBytes)
165 {
166 	struct ifnet *ifp = cookie;
167 	struct mbuf *mb;
168 	int length = *numBytes;
169 
170 	//if_printf(ifp, "compat_write(%lld, %p, [%lu])\n", position,
171 	//	buffer, *numBytes);
172 
173 	if (length <= MHLEN) {
174 		mb = m_gethdr(0, MT_DATA);
175 		if (mb == NULL)
176 			return ENOBUFS;
177 	} else {
178 		mb = m_get2(length, 0, MT_DATA, M_PKTHDR);
179 		if (mb == NULL)
180 			return E2BIG;
181 
182 		length = min_c(length, mb->m_ext.ext_size);
183 	}
184 
185 	// if we waited, check after if the ifp is still valid
186 
187 	mb->m_pkthdr.len = mb->m_len = length;
188 	memcpy(mtod(mb, void *), buffer, mb->m_len);
189 	*numBytes = length;
190 
191 	IFF_LOCKGIANT(ifp);
192 	int result = ifp->if_output(ifp, mb, NULL, NULL);
193 	IFF_UNLOCKGIANT(ifp);
194 
195 	return result;
196 }
197 
198 
199 static status_t
200 compat_control(void *cookie, uint32 op, void *arg, size_t length)
201 {
202 	struct ifnet *ifp = cookie;
203 	status_t status;
204 
205 	//if_printf(ifp, "compat_control(op %lu, %p, [%lu])\n", op,
206 	//	arg, length);
207 
208 	switch (op) {
209 		case ETHER_INIT:
210 			return B_OK;
211 
212 		case ETHER_GETADDR:
213 			return user_memcpy(arg, IF_LLADDR(ifp), ETHER_ADDR_LEN);
214 
215 		case ETHER_NONBLOCK:
216 		{
217 			int32 value;
218 			if (length < 4)
219 				return B_BAD_VALUE;
220 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
221 				return B_BAD_ADDRESS;
222 			if (value)
223 				ifp->flags |= DEVICE_NON_BLOCK;
224 			else
225 				ifp->flags &= ~DEVICE_NON_BLOCK;
226 			return B_OK;
227 		}
228 
229 		case ETHER_SETPROMISC:
230 		{
231 			int32 value;
232 			if (length < 4)
233 				return B_BAD_VALUE;
234 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
235 				return B_BAD_ADDRESS;
236 			if (value)
237 				ifp->if_flags |= IFF_PROMISC;
238 			else
239 				ifp->if_flags &= ~IFF_PROMISC;
240 
241 			IFF_LOCKGIANT(ifp);
242 			status = ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
243 			IFF_UNLOCKGIANT(ifp);
244 			return status;
245 		}
246 
247 		case ETHER_GETFRAMESIZE:
248 		{
249 			uint32 frameSize;
250 			if (length < 4)
251 				return B_BAD_VALUE;
252 
253 			const int MTUs[] = {
254 				ETHERMTU_JUMBO,
255 				PAGESIZE - (ETHER_HDR_LEN + ETHER_CRC_LEN),
256 				2290, /* IEEE80211_MTU_MAX */
257 				0
258 			};
259 
260 			// This is (usually) only invoked during initialization to get the
261 			// maximum frame size. Thus we try a few common possible values,
262 			// as there is no way to determine what is supported (or required).
263 			for (int i = 0; MTUs[i] != 0; i++) {
264 				struct ifreq ifr;
265 				ifr.ifr_mtu = MTUs[i];
266 				if (compat_control(cookie, SIOCSIFMTU, &ifr, sizeof(ifr)) == 0)
267 					break;
268 			}
269 
270 			frameSize = ifp->if_mtu + (ETHER_HDR_LEN + ETHER_CRC_LEN);
271 			return user_memcpy(arg, &frameSize, 4);
272 		}
273 
274 		case ETHER_ADDMULTI:
275 		case ETHER_REMMULTI:
276 		{
277 			struct sockaddr_dl address;
278 
279 			if ((ifp->if_flags & IFF_MULTICAST) == 0)
280 				return B_NOT_SUPPORTED;
281 			if (length != ETHER_ADDR_LEN)
282 				return B_BAD_VALUE;
283 
284 			memset(&address, 0, sizeof(address));
285 			address.sdl_family = AF_LINK;
286 			if (user_memcpy(LLADDR(&address), arg, ETHER_ADDR_LEN) < B_OK)
287 				return B_BAD_ADDRESS;
288 
289 			IFF_LOCKGIANT(ifp);
290 			if (op == ETHER_ADDMULTI)
291 				status = if_addmulti(ifp, (struct sockaddr *)&address, NULL);
292 			else
293 				status = if_delmulti(ifp, (struct sockaddr *)&address);
294 			IFF_UNLOCKGIANT(ifp);
295 			return status;
296 		}
297 
298 		case ETHER_GET_LINK_STATE:
299 		{
300 			struct ifmediareq mediareq;
301 			ether_link_state_t state;
302 
303 			if (length < sizeof(ether_link_state_t))
304 				return EINVAL;
305 
306 			memset(&mediareq, 0, sizeof(mediareq));
307 			IFF_LOCKGIANT(ifp);
308 			status = ifp->if_ioctl(ifp, SIOCGIFMEDIA, (caddr_t)&mediareq);
309 			IFF_UNLOCKGIANT(ifp);
310 			if (status < B_OK)
311 				return status;
312 
313 			state.media = mediareq.ifm_active;
314 			if ((mediareq.ifm_status & IFM_ACTIVE) != 0)
315 				state.media |= IFM_ACTIVE;
316 			state.speed = ifmedia_baudrate(mediareq.ifm_active);
317 			state.quality = 1000;
318 
319 			return user_memcpy(arg, &state, sizeof(ether_link_state_t));
320 		}
321 
322 		case ETHER_SET_LINK_STATE_SEM:
323 			if (user_memcpy(&ifp->link_state_sem, arg, sizeof(sem_id)) < B_OK) {
324 				ifp->link_state_sem = -1;
325 				return B_BAD_ADDRESS;
326 			}
327 			return B_OK;
328 
329 		case SIOCSIFFLAGS:
330 		case SIOCSIFMEDIA:
331 		case SIOCSIFMTU:
332 		{
333 			IFF_LOCKGIANT(ifp);
334 			status = ifp->if_ioctl(ifp, op, (caddr_t)arg);
335 			IFF_UNLOCKGIANT(ifp);
336 			return status;
337 		}
338 	}
339 
340 	return wlan_control(cookie, op, arg, length);
341 }
342 
343 
344 device_hooks gDeviceHooks = {
345 	compat_open,
346 	compat_close,
347 	compat_free,
348 	compat_control,
349 	compat_read,
350 	compat_write,
351 };
352