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