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