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