1 /* 2 * Copyright 2007, Hugo Santos. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Hugo Santos, hugosantos@gmail.com 7 * 8 * Some of this code is based on previous work by Marcus Overhagen. 9 */ 10 11 #include "device.h" 12 13 #include <stdlib.h> 14 #include <Drivers.h> 15 #include <ether_driver.h> 16 17 #include <compat/sys/haiku-module.h> 18 19 #include <compat/sys/mbuf.h> 20 #include <compat/net/ethernet.h> 21 22 23 #define MAX_DEVICES 8 24 25 26 device_t gDevices[MAX_DEVICES]; 27 char *gDevNameList[MAX_DEVICES + 1]; 28 29 30 static device_probe_t *sDeviceProbe; 31 static device_attach_t *sDeviceAttach; 32 static device_detach_t *sDeviceDetach; 33 34 35 static device_t 36 allocate_device(driver_t *driver) 37 { 38 char semName[64]; 39 40 device_t dev = (device_t)malloc(sizeof(struct device)); 41 if (dev == NULL) 42 return NULL; 43 44 memset(dev, 0, sizeof(struct device)); 45 46 snprintf(semName, sizeof(semName), "%s rcv", gDriverName); 47 48 dev->softc = malloc(driver->softc_size); 49 if (dev->softc == NULL) { 50 free(dev); 51 return NULL; 52 } 53 54 dev->receive_sem = create_sem(0, semName); 55 if (dev->receive_sem < 0) { 56 free(dev->softc); 57 free(dev); 58 return NULL; 59 } 60 61 return dev; 62 } 63 64 65 static void 66 free_device(device_t dev) 67 { 68 delete_sem(dev->receive_sem); 69 free(dev->softc); 70 free(dev); 71 } 72 73 74 static device_method_signature_t 75 _resolve_method(driver_t *driver, const char *name) 76 { 77 device_method_signature_t method = NULL; 78 int i; 79 80 for (i = 0; method == NULL && driver->methods[i].name != NULL; i++) { 81 if (strcmp(driver->methods[i].name, name) == 0) 82 method = driver->methods[i].method; 83 } 84 85 return method; 86 } 87 88 89 static status_t 90 compat_open(const char *name, uint32 flags, void **cookie) 91 { 92 status_t status; 93 device_t dev; 94 int i; 95 96 dprintf("%s, compat_open(%s, 0x%lx)\n", gDriverName, name, flags); 97 98 for (i = 0; gDevNameList[i] != NULL; i++) { 99 if (strcmp(gDevNameList[i], name) == 0) 100 break; 101 } 102 103 if (gDevNameList[i] == NULL) 104 return B_ERROR; 105 106 dev = gDevices[i]; 107 108 if (atomic_or(&dev->flags, DEVICE_OPEN) & DEVICE_OPEN) 109 return B_BUSY; 110 111 status = sDeviceAttach(dev); 112 if (status != 0) 113 dev->flags = 0; 114 115 *cookie = dev; 116 117 return status; 118 } 119 120 121 static status_t 122 compat_close(void *cookie) 123 { 124 device_t dev = cookie; 125 126 dprintf("%s, compat_close(%p)\n", gDriverName, dev); 127 128 return B_ERROR; 129 } 130 131 132 static status_t 133 compat_free(void *cookie) 134 { 135 device_t dev = cookie; 136 137 dprintf("%s, compat_free(%p)\n", gDriverName, dev); 138 139 free_device(dev); 140 return B_ERROR; 141 } 142 143 144 static status_t 145 compat_read(void *cookie, off_t position, void *buf, size_t *numBytes) 146 { 147 uint32 semFlags = B_CAN_INTERRUPT; 148 device_t dev = cookie; 149 status_t status; 150 struct mbuf *mb; 151 size_t len; 152 153 if (dev->flags & DEVICE_CLOSED) 154 return B_INTERRUPTED; 155 156 if (dev->flags & DEVICE_NON_BLOCK) 157 semFlags |= B_RELATIVE_TIMEOUT; 158 159 do { 160 status = acquire_sem_etc(dev->receive_sem, 1, semFlags, 0); 161 if (dev->flags & DEVICE_CLOSED) 162 return B_INTERRUPTED; 163 164 if (status == B_WOULD_BLOCK) { 165 *numBytes = 0; 166 return B_OK; 167 } else if (status < B_OK) 168 return status; 169 170 IF_DEQUEUE(&dev->receive_queue, mb); 171 } while (mb == NULL); 172 173 len = min_c(max_c((size_t)mb->m_len, 0), *numBytes); 174 175 mb = m_defrag(mb, 0); 176 if (mb == NULL) { 177 *numBytes = 0; 178 return B_NO_MEMORY; 179 } 180 181 memcpy(buf, mtod(mb, const void *), len); 182 *numBytes = len; 183 184 m_freem(mb); 185 return B_OK; 186 } 187 188 189 static status_t 190 compat_write(void *cookie, off_t position, const void *buffer, 191 size_t *numBytes) 192 { 193 device_t dev = cookie; 194 struct mbuf *mb; 195 196 mb = m_getcl(0, MT_DATA, 0); 197 if (mb == NULL) 198 return ENOBUFS; 199 200 /* if we waited, check after if the ifp is still valid */ 201 202 mb->m_len = min_c(*numBytes, (size_t)MCLBYTES); 203 memcpy(mtod(mb, void *), buffer, mb->m_len); 204 205 return dev->ifp->if_output(dev->ifp, mb, NULL, NULL); 206 } 207 208 209 static status_t 210 compat_control(void *cookie, uint32 op, void *arg, size_t len) 211 { 212 device_t dev = cookie; 213 214 switch (op) { 215 case ETHER_INIT: 216 return B_OK; 217 218 case ETHER_GETADDR: 219 memcpy(arg, IF_LLADDR(dev->ifp), ETHER_ADDR_LEN); 220 return B_OK; 221 222 case ETHER_NONBLOCK: 223 if (*(int32 *)arg) 224 dev->flags |= DEVICE_NON_BLOCK; 225 else 226 dev->flags &= ~DEVICE_NON_BLOCK; 227 return B_OK; 228 229 case ETHER_SETPROMISC: 230 /* TODO */ 231 return B_ERROR; 232 233 case ETHER_GETFRAMESIZE: 234 *(uint32 *)arg = dev->ifp->if_mtu; 235 return B_OK; 236 237 case ETHER_ADDMULTI: 238 case ETHER_REMMULTI: 239 /* TODO */ 240 return B_ERROR; 241 242 case ETHER_GET_LINK_STATE: 243 /* TODO */ 244 return B_ERROR; 245 246 case ETHER_SET_LINK_STATE_SEM: 247 /* TODO */ 248 return B_OK; 249 } 250 251 return B_BAD_VALUE; 252 } 253 254 255 device_hooks gDeviceHooks = { 256 compat_open, 257 compat_close, 258 compat_free, 259 compat_control, 260 compat_read, 261 compat_write, 262 }; 263 264 265 status_t 266 _fbsd_init_hardware(driver_t *driver) 267 { 268 struct device fakeDevice; 269 device_probe_t *probe; 270 int i; 271 272 dprintf("%s: init_hardware(%p)\n", gDriverName, driver); 273 274 if (get_module(B_PCI_MODULE_NAME, (module_info **)&gPci) < B_OK) 275 return B_ERROR; 276 277 probe = (device_probe_t *)_resolve_method(driver, "device_probe"); 278 if (probe == NULL) { 279 dprintf("%s: driver has no device_probe method.\n", gDriverName); 280 return B_ERROR; 281 } 282 283 memset(&fakeDevice, 0, sizeof(struct device)); 284 285 for (i = 0; gPci->get_nth_pci_info(i, &fakeDevice.pci_info) == B_OK; i++) { 286 if (probe(&fakeDevice) >= 0) { 287 dprintf("%s, found %s at %d\n", gDriverName, 288 fakeDevice.description, i); 289 put_module(B_PCI_MODULE_NAME); 290 return B_OK; 291 } 292 } 293 294 dprintf("%s: no hardware found.\n", gDriverName); 295 put_module(B_PCI_MODULE_NAME); 296 297 return B_ERROR; 298 } 299 300 301 status_t 302 _fbsd_init_driver(driver_t *driver) 303 { 304 int i, ncards = 0; 305 status_t status; 306 device_t dev; 307 308 dprintf("%s: init_driver(%p)\n", gDriverName, driver); 309 310 sDeviceProbe = (device_probe_t *)_resolve_method(driver, "device_probe"); 311 sDeviceAttach = (device_attach_t *)_resolve_method(driver, "device_attach"); 312 sDeviceDetach = (device_detach_t *)_resolve_method(driver, "device_detach"); 313 314 dev = allocate_device(driver); 315 if (dev == NULL) 316 return B_NO_MEMORY; 317 318 status = init_mutexes(); 319 if (status < B_OK) { 320 free_device(dev); 321 return status; 322 } 323 324 status = init_mbufs(); 325 if (status < B_OK) { 326 uninit_mutexes(); 327 free_device(dev); 328 return status; 329 } 330 331 init_bounce_pages(); 332 333 for (i = 0; dev != NULL 334 && gPci->get_nth_pci_info(i, &dev->pci_info) == B_OK; i++) { 335 if (sDeviceProbe(dev) >= 0) { 336 snprintf(dev->dev_name, sizeof(dev->dev_name), "net/%s/%i", 337 gDriverName, ncards); 338 dprintf("%s, adding %s @%d -> /dev/%s\n", gDriverName, dev->description, 339 i, dev->dev_name); 340 341 gDevices[ncards] = dev; 342 gDevNameList[ncards] = dev->dev_name; 343 344 ncards++; 345 if (ncards < MAX_DEVICES) 346 dev = allocate_device(driver); 347 else 348 dev = NULL; 349 } 350 } 351 352 if (dev != NULL) 353 free_device(dev); 354 355 dprintf("%s, ... %d cards.\n", gDriverName, ncards); 356 357 gDevNameList[ncards + 1] = NULL; 358 359 return B_OK; 360 } 361 362 363 void 364 _fbsd_uninit_driver(driver_t *driver) 365 { 366 int i; 367 368 dprintf("%s: uninit_driver(%p)\n", gDriverName, driver); 369 370 for (i = 0; gDevNameList[i] != NULL; i++) { 371 free_device(gDevices[i]); 372 } 373 374 uninit_bounce_pages(); 375 uninit_mbufs(); 376 uninit_mutexes(); 377 } 378