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