1 /* 2 * Copyright 2007, Hugo Santos. All Rights Reserved. 3 * Copyright 2007, Axel Dörfler, axeld@pinc-software.de. 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; gDeviceNameList[i] != NULL; i++) { 32 if (strcmp(gDeviceNameList[i], name) == 0) 33 break; 34 } 35 36 if (gDeviceNameList[i] == NULL) 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_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 memcpy(buffer, mtod(mb, const void *), length); 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 mb = m_getcl(0, MT_DATA, M_PKTHDR); 157 if (mb == NULL) 158 return ENOBUFS; 159 160 /* if we waited, check after if the ifp is still valid */ 161 162 mb->m_len = min_c(*numBytes, (size_t)MCLBYTES); 163 memcpy(mtod(mb, void *), buffer, mb->m_len); 164 165 return ifp->if_output(ifp, mb, NULL, NULL); 166 } 167 168 169 static status_t 170 compat_control(void *cookie, uint32 op, void *arg, size_t length) 171 { 172 struct ifnet *ifp = cookie; 173 174 //if_printf(ifp, "compat_control(op %lu, %p, [%lu])\n", op, 175 // arg, length); 176 177 switch (op) { 178 case ETHER_INIT: 179 return B_OK; 180 181 case ETHER_GETADDR: 182 return user_memcpy(arg, IF_LLADDR(ifp), ETHER_ADDR_LEN); 183 184 case ETHER_NONBLOCK: 185 { 186 int32 value; 187 if (length < 4) 188 return B_BAD_VALUE; 189 if (user_memcpy(&value, arg, sizeof(int32)) < B_OK) 190 return B_BAD_ADDRESS; 191 if (value) 192 ifp->flags |= DEVICE_NON_BLOCK; 193 else 194 ifp->flags &= ~DEVICE_NON_BLOCK; 195 return B_OK; 196 } 197 198 case ETHER_SETPROMISC: 199 { 200 int32 value; 201 if (length < 4) 202 return B_BAD_VALUE; 203 if (user_memcpy(&value, arg, sizeof(int32)) < B_OK) 204 return B_BAD_ADDRESS; 205 if (value) 206 ifp->if_flags |= IFF_PROMISC; 207 else 208 ifp->if_flags &= ~IFF_PROMISC; 209 return ifp->if_ioctl(ifp, SIOCSIFFLAGS, NULL); 210 } 211 212 case ETHER_GETFRAMESIZE: 213 { 214 uint32 frameSize; 215 if (length < 4) 216 return B_BAD_VALUE; 217 218 frameSize = ifp->if_mtu + ETHER_HDR_LEN; 219 return user_memcpy(arg, &frameSize, 4); 220 } 221 222 case ETHER_ADDMULTI: 223 case ETHER_REMMULTI: 224 { 225 struct sockaddr_dl address; 226 227 if ((ifp->if_flags & IFF_MULTICAST) == 0) 228 return EOPNOTSUPP; 229 230 memset(&address, 0, sizeof(address)); 231 address.sdl_family = AF_LINK; 232 memcpy(LLADDR(&address), arg, ETHER_ADDR_LEN); 233 234 if (op == ETHER_ADDMULTI) 235 return if_addmulti(ifp, (struct sockaddr *)&address, NULL); 236 237 return if_delmulti(ifp, (struct sockaddr *)&address); 238 } 239 240 case ETHER_GET_LINK_STATE: 241 { 242 struct ifmediareq mediareq; 243 ether_link_state_t state; 244 status_t status; 245 246 if (length < sizeof(ether_link_state_t)) 247 return EINVAL; 248 249 memset(&mediareq, 0, sizeof(mediareq)); 250 status = ifp->if_ioctl(ifp, SIOCGIFMEDIA, (caddr_t)&mediareq); 251 if (status < B_OK) 252 return status; 253 254 state.media = mediareq.ifm_active; 255 if (mediareq.ifm_status & IFM_ACTIVE) 256 state.media |= IFM_ACTIVE; 257 if (mediareq.ifm_active & IFM_10_T) 258 state.speed = 10000; 259 else if (mediareq.ifm_active & IFM_100_TX) 260 state.speed = 100000; 261 else 262 state.speed = 1000000; 263 state.quality = 1000; 264 265 return user_memcpy(arg, &state, sizeof(ether_link_state_t)); 266 } 267 268 case ETHER_SET_LINK_STATE_SEM: 269 if (user_memcpy(&ifp->link_state_sem, arg, sizeof(sem_id)) < B_OK) { 270 ifp->link_state_sem = -1; 271 return B_BAD_ADDRESS; 272 } 273 return B_OK; 274 } 275 276 return B_BAD_VALUE; 277 } 278 279 280 device_hooks gDeviceHooks = { 281 compat_open, 282 compat_close, 283 compat_free, 284 compat_control, 285 compat_read, 286 compat_write, 287 }; 288 289