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 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 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 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 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 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 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 SIOCSIFFLAGS: 350 case SIOCSIFMEDIA: 351 case SIOCSIFMTU: 352 { 353 IFF_LOCKGIANT(ifp); 354 status = ifp->if_ioctl(ifp, op, (caddr_t)arg); 355 IFF_UNLOCKGIANT(ifp); 356 return status; 357 } 358 } 359 360 return wlan_control(cookie, op, arg, length); 361 } 362 363 364 device_hooks gDeviceHooks = { 365 compat_open, 366 compat_close, 367 compat_free, 368 compat_control, 369 NULL, 370 NULL, 371 }; 372