xref: /haiku/src/libs/compat/freebsd_network/device.c (revision 2510baa4685f8f570c607ceedfd73473d69342c4)
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 
32 	for (i = 0; i < MAX_DEVICES; i++) {
33 		if (gDevices[i] != NULL && !strcmp(gDevices[i]->device_name, name))
34 			break;
35 	}
36 
37 	if (i == MAX_DEVICES)
38 		return B_ERROR;
39 
40 	if (get_module(NET_STACK_MODULE_NAME, (module_info **)&gStack) != B_OK)
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 		put_module(NET_STACK_MODULE_NAME);
48 		return B_BUSY;
49 	}
50 
51 	ifp->if_init(ifp->if_softc);
52 
53 	if (!HAIKU_DRIVER_REQUIRES(FBSD_WLAN)) {
54 		ifp->if_flags &= ~IFF_UP;
55 		ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
56 	}
57 
58 	memset(&ifr, 0, sizeof(ifr));
59 	ifr.ifr_media = IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0);
60 	ifp->if_ioctl(ifp, SIOCSIFMEDIA, (caddr_t)&ifr);
61 
62 	ifp->if_flags |= IFF_UP;
63 	ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
64 
65 	*cookie = ifp;
66 	return B_OK;
67 }
68 
69 
70 static status_t
71 compat_close(void *cookie)
72 {
73 	struct ifnet *ifp = cookie;
74 
75 	if_printf(ifp, "compat_close()\n");
76 
77 	atomic_or(&ifp->flags, DEVICE_CLOSED);
78 
79 	wlan_close(cookie);
80 
81 	release_sem_etc(ifp->receive_sem, 1, B_RELEASE_ALL);
82 
83 	return B_OK;
84 }
85 
86 
87 static status_t
88 compat_free(void *cookie)
89 {
90 	struct ifnet *ifp = cookie;
91 
92 	if_printf(ifp, "compat_free()\n");
93 
94 	// TODO: empty out the send queue
95 
96 	atomic_and(&ifp->open_count, 0);
97 	put_module(NET_STACK_MODULE_NAME);
98 	return B_OK;
99 }
100 
101 
102 static status_t
103 compat_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
104 {
105 	struct ifnet *ifp = cookie;
106 	uint32 semFlags = B_CAN_INTERRUPT;
107 	status_t status;
108 	struct mbuf *mb;
109 	size_t length;
110 
111 	//if_printf(ifp, "compat_read(%lld, %p, [%lu])\n", position,
112 	//	buffer, *numBytes);
113 
114 	if (ifp->flags & DEVICE_CLOSED)
115 		return B_INTERRUPTED;
116 
117 	if (ifp->flags & DEVICE_NON_BLOCK)
118 		semFlags |= B_RELATIVE_TIMEOUT;
119 
120 	do {
121 		status = acquire_sem_etc(ifp->receive_sem, 1, semFlags, 0);
122 		if (ifp->flags & DEVICE_CLOSED)
123 			return B_INTERRUPTED;
124 
125 		if (status == B_WOULD_BLOCK) {
126 			*numBytes = 0;
127 			return B_OK;
128 		} else if (status < B_OK)
129 			return status;
130 
131 		IF_DEQUEUE(&ifp->receive_queue, mb);
132 	} while (mb == NULL);
133 
134 	length = min_c(max_c((size_t)mb->m_pkthdr.len, 0), *numBytes);
135 
136 #if 0
137 	mb = m_defrag(mb, 0);
138 	if (mb == NULL) {
139 		*numBytes = 0;
140 		return B_NO_MEMORY;
141 	}
142 #endif
143 
144 	m_copydata(mb, 0, length, buffer);
145 	*numBytes = length;
146 
147 	m_freem(mb);
148 	return B_OK;
149 }
150 
151 
152 static status_t
153 compat_write(void *cookie, off_t position, const void *buffer,
154 	size_t *numBytes)
155 {
156 	struct ifnet *ifp = cookie;
157 	struct mbuf *mb;
158 
159 	//if_printf(ifp, "compat_write(%lld, %p, [%lu])\n", position,
160 	//	buffer, *numBytes);
161 
162 	if (*numBytes > MHLEN)
163 		mb = m_getcl(0, MT_DATA, M_PKTHDR);
164 	else
165 		mb = m_gethdr(0, MT_DATA);
166 
167 	if (mb == NULL)
168 		return ENOBUFS;
169 
170 	// if we waited, check after if the ifp is still valid
171 
172 	mb->m_pkthdr.len = mb->m_len = min_c(*numBytes, (size_t)MCLBYTES);
173 	memcpy(mtod(mb, void *), buffer, mb->m_len);
174 
175 	return ifp->if_output(ifp, mb, NULL, NULL);
176 }
177 
178 
179 static status_t
180 compat_control(void *cookie, uint32 op, void *arg, size_t length)
181 {
182 	struct ifnet *ifp = cookie;
183 
184 	//if_printf(ifp, "compat_control(op %lu, %p, [%lu])\n", op,
185 	//	arg, length);
186 
187 	switch (op) {
188 		case ETHER_INIT:
189 			return B_OK;
190 
191 		case ETHER_GETADDR:
192 			return user_memcpy(arg, IF_LLADDR(ifp), ETHER_ADDR_LEN);
193 
194 		case ETHER_NONBLOCK:
195 		{
196 			int32 value;
197 			if (length < 4)
198 				return B_BAD_VALUE;
199 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
200 				return B_BAD_ADDRESS;
201 			if (value)
202 				ifp->flags |= DEVICE_NON_BLOCK;
203 			else
204 				ifp->flags &= ~DEVICE_NON_BLOCK;
205 			return B_OK;
206 		}
207 
208 		case ETHER_SETPROMISC:
209 		{
210 			int32 value;
211 			if (length < 4)
212 				return B_BAD_VALUE;
213 			if (user_memcpy(&value, arg, sizeof(int32)) < B_OK)
214 				return B_BAD_ADDRESS;
215 			if (value)
216 				ifp->if_flags |= IFF_PROMISC;
217 			else
218 				ifp->if_flags &= ~IFF_PROMISC;
219 			return ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL);
220 		}
221 
222 		case ETHER_GETFRAMESIZE:
223 		{
224 			uint32 frameSize;
225 			if (length < 4)
226 				return B_BAD_VALUE;
227 
228 			frameSize = ifp->if_mtu + ETHER_HDR_LEN;
229 			return user_memcpy(arg, &frameSize, 4);
230 		}
231 
232 		case ETHER_ADDMULTI:
233 		case ETHER_REMMULTI:
234 		{
235 			struct sockaddr_dl address;
236 
237 			if ((ifp->if_flags & IFF_MULTICAST) == 0)
238 				return B_NOT_SUPPORTED;
239 
240 			memset(&address, 0, sizeof(address));
241 			address.sdl_family = AF_LINK;
242 			memcpy(LLADDR(&address), arg, ETHER_ADDR_LEN);
243 
244 			if (op == ETHER_ADDMULTI)
245 				return if_addmulti(ifp, (struct sockaddr *)&address, NULL);
246 
247 			return if_delmulti(ifp, (struct sockaddr *)&address);
248 		}
249 
250 		case ETHER_GET_LINK_STATE:
251 		{
252 			struct ifmediareq mediareq;
253 			ether_link_state_t state;
254 			status_t status;
255 
256 			if (length < sizeof(ether_link_state_t))
257 				return EINVAL;
258 
259 			memset(&mediareq, 0, sizeof(mediareq));
260 			status = ifp->if_ioctl(ifp, SIOCGIFMEDIA, (caddr_t)&mediareq);
261 			if (status < B_OK)
262 				return status;
263 
264 			state.media = mediareq.ifm_active;
265 			if ((mediareq.ifm_status & IFM_ACTIVE) != 0)
266 				state.media |= IFM_ACTIVE;
267 			if ((mediareq.ifm_active & IFM_10_T) != 0)
268 				state.speed = 10000000;
269 			else if ((mediareq.ifm_active & IFM_100_TX) != 0)
270 				state.speed = 100000000;
271 			else
272 				state.speed = 1000000000;
273 			state.quality = 1000;
274 
275 			return user_memcpy(arg, &state, sizeof(ether_link_state_t));
276 		}
277 
278 		case ETHER_SET_LINK_STATE_SEM:
279 			if (user_memcpy(&ifp->link_state_sem, arg, sizeof(sem_id)) < B_OK) {
280 				ifp->link_state_sem = -1;
281 				return B_BAD_ADDRESS;
282 			}
283 			return B_OK;
284 	}
285 
286 	return wlan_control(cookie, op, arg, length);
287 }
288 
289 
290 device_hooks gDeviceHooks = {
291 	compat_open,
292 	compat_close,
293 	compat_free,
294 	compat_control,
295 	compat_read,
296 	compat_write,
297 };
298