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