/* * Pegasus BeOS Driver * * Copyright 2006, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Jérôme Duval */ #include #include #include #include #include "driver.h" #include "usbdevs.h" typedef struct driver_cookie { struct driver_cookie *next; pegasus_dev *device; } driver_cookie; int32 api_version = B_CUR_DRIVER_API_VERSION; static status_t pegasus_device_added(const usb_device dev, void **cookie); static status_t pegasus_device_removed(void *cookie); static int sDeviceNumber = 0; static const char *kBaseName = "net/pegasus/"; static const char *kDriverName = DRIVER_NAME; usb_module_info *usb; usb_support_descriptor supported_devices[] = { { 0, 0, 0, USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10}, { 0, 0, 0, USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA}, { 0, 0, 0, USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC}, { 0, 0, 0, USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001}, { 0, 0, 0, USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS}, { 0, 0, 0, USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII}, { 0, 0, 0, USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2}, { 0, 0, 0, USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_3}, { 0, 0, 0, USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN}, { 0, 0, 0, USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100}, { 0, 0, 0, USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100}, { 0, 0, 0, USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100}, { 0, 0, 0, USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100}, { 0, 0, 0, USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX}, { 0, 0, 0, USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS}, { 0, 0, 0, USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4}, { 0, 0, 0, USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1}, { 0, 0, 0, USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX}, { 0, 0, 0, USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA}, { 0, 0, 0, USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3}, { 0, 0, 0, USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2}, { 0, 0, 0, USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650}, { 0, 0, 0, USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0}, { 0, 0, 0, USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1}, { 0, 0, 0, USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2}, { 0, 0, 0, USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3}, { 0, 0, 0, USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX}, { 0, 0, 0, USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET}, { 0, 0, 0, USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100}, { 0, 0, 0, USB_VENDOR_HP, USB_PRODUCT_HP_HN210E}, { 0, 0, 0, USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX}, { 0, 0, 0, USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS}, { 0, 0, 0, USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX}, { 0, 0, 0, USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1}, { 0, 0, 0, USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T}, { 0, 0, 0, USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX}, { 0, 0, 0, USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1}, { 0, 0, 0, USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA}, { 0, 0, 0, USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2}, { 0, 0, 0, USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110}, { 0, 0, 0, USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1}, { 0, 0, 0, USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5}, { 0, 0, 0, USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5}, { 0, 0, 0, USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA101}, { 0, 0, 0, USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM}, { 0, 0, 0, USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC}, { 0, 0, 0, USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB}, { 0, 0, 0, USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB}, { 0, 0, 0, USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100}, }; static const struct aue_type aue_devs[] = { {{ USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B}, PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1}, PNA|PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2}, PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000}, LSYS }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4}, PNA }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5}, PNA }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6}, PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7}, PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8}, PII }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9}, PNA }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10}, 0 }, {{ USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA}, 0 }, {{ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC}, 0 }, {{ USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001}, PII }, {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS}, PNA }, {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII}, PII }, {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2}, PII }, {{ USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_3}, PII }, {{ USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN}, PII }, {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100}, 0 }, {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100}, PNA }, {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100}, 0 }, {{ USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100}, PII }, {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX}, 0 }, {{ USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS},PII }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4}, LSYS|PII }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1}, LSYS }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX}, LSYS }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA}, PNA }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3}, LSYS|PII }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2}, LSYS|PII }, {{ USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650}, LSYS }, {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0}, 0 }, {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1}, LSYS }, {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2}, 0 }, {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3}, LSYS }, {{ USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX}, PII }, {{ USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET}, 0 }, {{ USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100}, PII }, {{ USB_VENDOR_HP, USB_PRODUCT_HP_HN210E}, PII }, {{ USB_VENDOR_IODATA, 0x092a /* USB_PRODUCT_IODATA_ETXUS2 */}, PII }, {{ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX}, 0 }, {{ USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS}, PII }, {{ USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX}, 0 }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1}, LSYS|PII }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T}, LSYS }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX}, LSYS }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1}, LSYS|PNA }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA}, LSYS }, {{ USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2}, LSYS|PII }, {{ USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110}, PII }, {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1}, 0 }, {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5}, 0 }, {{ USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5}, PII }, {{ USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA101}, PII }, {{ USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM}, PII }, {{ USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC},PII }, {{ USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB}, 0 }, {{ USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB}, PII }, {{ USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100}, 0 }, }; static status_t pegasus_checkdeviceinfo(pegasus_dev *dev) { if (!dev || dev->cookieMagic != PEGASUS_COOKIE_MAGIC) return EINVAL; return B_OK; } pegasus_dev * create_device(const usb_device dev, const usb_interface_info *ii, uint16 ifno) { pegasus_dev *device = NULL; sem_id sem; ASSERT(usb != NULL && dev != 0); device = malloc(sizeof(pegasus_dev)); if (device == NULL) return NULL; memset(device, 0, sizeof(pegasus_dev)); device->sem_lock = sem = create_sem(1, DRIVER_NAME "_lock"); if (sem < B_OK) { DPRINTF_ERR("create_sem() failed 0x%" B_PRIx32 "\n", sem); free(device); return NULL; } device->rx_sem = sem = create_sem(1, DRIVER_NAME"_receive"); if (sem < B_OK) { DPRINTF_ERR("create_sem() failed 0x%" B_PRIx32 "\n", sem); delete_sem(device->sem_lock); free(device); return NULL; } set_sem_owner(device->rx_sem, B_SYSTEM_TEAM); device->rx_sem_cb = sem = create_sem(0, DRIVER_NAME"_receive_cb"); if (sem < B_OK) { DPRINTF_ERR("create_sem() failed 0x%" B_PRIx32 "\n", sem); delete_sem(device->rx_sem); delete_sem(device->sem_lock); free(device); return NULL; } set_sem_owner(device->rx_sem_cb, B_SYSTEM_TEAM); device->tx_sem = sem = create_sem(1, DRIVER_NAME"_transmit"); if (sem < B_OK) { delete_sem(device->sem_lock); delete_sem(device->rx_sem); delete_sem(device->rx_sem_cb); free(device); return NULL; } set_sem_owner(device->tx_sem, B_SYSTEM_TEAM); device->tx_sem_cb = sem = create_sem(0, DRIVER_NAME"_transmit_cb"); if (sem < B_OK) { delete_sem(device->sem_lock); delete_sem(device->rx_sem); delete_sem(device->rx_sem_cb); delete_sem(device->tx_sem); free(device); return NULL; } set_sem_owner(device->tx_sem_cb, B_SYSTEM_TEAM); device->number = sDeviceNumber++; device->cookieMagic = PEGASUS_COOKIE_MAGIC; sprintf(device->name, "%s%d", kBaseName, device->number); device->dev = dev; device->ifno = ifno; device->open = 0; device->open_fds = NULL; device->aue_dying = false; device->flags = 0; device->maxframesize = 1514; // XXX is MAXIMUM_ETHERNET_FRAME_SIZE = 1518 too much? return device; } void remove_device(pegasus_dev *device) { ASSERT(device != NULL); delete_sem(device->rx_sem); delete_sem(device->tx_sem); delete_sem(device->rx_sem_cb); delete_sem(device->tx_sem_cb); delete_sem(device->sem_lock); free(device); } /** \fn: */ static status_t setup_endpoints(const usb_interface_info *uii, pegasus_dev *dev) { ssize_t epts[3] = { -1, -1, -1 }; size_t ep = 0; for(; ep < uii->endpoint_count; ep++){ usb_endpoint_descriptor *ed = uii->endpoint[ep].descr; DPRINTF_INFO("try endpoint:%ld %x %x %lx\n", ep, ed->attributes, ed->endpoint_address, uii->endpoint[ep].handle); if ((ed->attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_BULK) { if ((ed->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) == USB_ENDPOINT_ADDR_DIR_IN) { epts[0] = ep; } else epts[1] = ep; } else if ((ed->attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_INTERRUPT) { epts[2] = ep; } } dev->pipe_in = uii->endpoint[epts[0]].handle; dev->pipe_out = uii->endpoint[epts[1]].handle; dev->pipe_intr = uii->endpoint[epts[2]].handle; DPRINTF_INFO("endpoint:%lx %lx %lx\n", dev->pipe_in, dev->pipe_out, dev->pipe_intr); return ((epts[0] > -1) && (epts[1] > -1) && (epts[2] > -1)) ? B_OK : B_ENTRY_NOT_FOUND; } static void pegasus_rx_callback(void *cookie, status_t status, void *data, size_t actual_len) { pegasus_dev *dev = (pegasus_dev *)cookie; DPRINTF_INFO("pegasus_rx_callback() %ld %ld\n", status, actual_len); if (status == B_CANCELED) { /* cancelled: device is unplugged */ DPRINTF_ERR("pegasus_rx_callback() cancelled\n"); return; } ASSERT(cookie != NULL); dev->rx_actual_length = actual_len; dev->rx_status = status; /* B_USB_STATUS_* */ release_sem(dev->rx_sem_cb); DPRINTF_INFO("pegasus_rx_callback release sem %ld\n", dev->rx_sem_cb); } static void pegasus_tx_callback(void *cookie, status_t status, void *data, size_t actual_len) { pegasus_dev *dev = (pegasus_dev *)cookie; DPRINTF_INFO("pegasus_tx_callback() %ld %ld\n", status, actual_len); if (status == B_CANCELED) { /* cancelled: device is unplugged */ DPRINTF_ERR("pegasus_tx_callback() cancelled\n"); return; } ASSERT(cookie != NULL); dev->tx_actual_length = actual_len; dev->tx_status = status; /* B_USB_STATUS_* */ release_sem(dev->tx_sem_cb); DPRINTF_INFO("pegasus_tx_callback release sem %ld\n", dev->tx_sem_cb); } // #pragma mark - device hooks static status_t pegasus_device_added(const usb_device dev, void **cookie) { pegasus_dev *device; const usb_device_descriptor *dev_desc; const usb_configuration_info *conf; const usb_interface_info *intf; status_t status; uint16 ifno; unsigned int i; ASSERT(dev != 0 && cookie != NULL); DPRINTF_INFO("device_added()\n"); dev_desc = usb->get_device_descriptor(dev); DPRINTF_INFO("vendor ID 0x%04X, product ID 0x%04X\n", dev_desc->vendor_id, dev_desc->product_id); if ((conf = usb->get_nth_configuration(dev, DEFAULT_CONFIGURATION)) == NULL) { DPRINTF_ERR("cannot get default configuration\n"); return B_ERROR; } ifno = AUE_IFACE_IDX; intf = conf->interface [ifno].active; /* configuration */ if ((status = usb->set_configuration(dev, conf)) != B_OK) { DPRINTF_ERR("set_configuration() failed %s\n", strerror(status)); return B_ERROR; } if ((device = create_device(dev, intf, ifno)) == NULL) { DPRINTF_ERR("create_device() failed\n"); return B_ERROR; } device->aue_vendor = dev_desc->vendor_id; device->aue_product = dev_desc->product_id; for (i=0; i < sizeof(aue_devs) / sizeof(struct aue_type); i++) if (aue_devs[i].aue_dev.vendor == dev_desc->vendor_id && aue_devs[i].aue_dev.product == dev_desc->product_id) { device->aue_flags = aue_devs[i].aue_flags; break; } /* Find endpoints. */ setup_endpoints(intf, device); aue_attach(device); DPRINTF_INFO("MAC %02x:%02x:%02x:%02x:%02x:%02x\n", device->macaddr[0], device->macaddr[1], device->macaddr[2], device->macaddr[3], device->macaddr[4], device->macaddr[5]); aue_init(device); /* create a port */ add_device_info(device); *cookie = device; DPRINTF_INFO("added %s\n", device->name); return B_OK; } static status_t pegasus_device_removed(void *cookie) { pegasus_dev *device = cookie; ASSERT(cookie != NULL); DPRINTF_INFO("device_removed(%s)\n", device->name); aue_uninit(device); usb->cancel_queued_transfers(device->pipe_in); usb->cancel_queued_transfers(device->pipe_out); usb->cancel_queued_transfers(device->pipe_intr); remove_device_info(device); if (device->open == 0) { remove_device(device); } else { DPRINTF_INFO("%s still open\n", device->name); AUE_LOCK(device); device->aue_dying = true; AUE_UNLOCK(device); } return B_OK; } static status_t pegasus_device_open(const char *name, uint32 flags, driver_cookie **out_cookie) { driver_cookie *cookie; pegasus_dev *device; status_t err; ASSERT(name != NULL); ASSERT(out_cookie != NULL); DPRINTF_INFO("open(%s)\n", name); if ((device = search_device_info(name)) == NULL) return B_ENTRY_NOT_FOUND; if ((cookie = malloc(sizeof(driver_cookie))) == NULL) return B_NO_MEMORY; if ((err = acquire_sem(device->sem_lock)) != B_OK) { free(cookie); return err; } device->nonblocking = (flags & O_NONBLOCK) != 0; cookie->device = device; cookie->next = device->open_fds; device->open_fds = cookie; device->open++; release_sem(device->sem_lock); *out_cookie = cookie; DPRINTF_INFO("device %s open (%d)\n", name, device->open); return B_OK; } static status_t pegasus_device_read(driver_cookie *cookie, off_t position, void *buffer, size_t *_length) { pegasus_dev *dev; status_t status; int32 blockFlag; size_t size; DPRINTF_INFO("device %p read\n", cookie); if (pegasus_checkdeviceinfo(dev = cookie->device) != B_OK) { DPRINTF_ERR("EINVAL\n"); #ifndef __HAIKU__ *_length = 0; // net_server work-around; it obviously doesn't care about error conditions // For Haiku, this can be removed #endif return B_BAD_VALUE; } if (dev->aue_dying) return B_DEVICE_NOT_FOUND; /* already unplugged */ blockFlag = dev->nonblocking ? B_TIMEOUT : 0; // block until receive is available (if blocking is allowed) if ((status = acquire_sem_etc(dev->rx_sem, 1, B_CAN_INTERRUPT | blockFlag, 0)) != B_NO_ERROR) { DPRINTF_ERR("cannot acquire read sem: %" B_PRIx32 ", %s\n", status, strerror(status)); #ifndef __HAIKU__ *_length = 0; #endif return status; } // queue new request status = usb->queue_bulk(dev->pipe_in, dev->rx_buffer, MAX_FRAME_SIZE, &pegasus_rx_callback, dev); if (status != B_OK) { DPRINTF_ERR("queue_bulk:failed:%08" B_PRIx32 "\n", status); goto rx_done; } // block until data is available (if blocking is allowed) if ((status = acquire_sem_etc(dev->rx_sem_cb, 1, B_CAN_INTERRUPT | blockFlag, 0)) != B_NO_ERROR) { DPRINTF_ERR("cannot acquire read sem: %" B_PRIx32 ", %s\n", status, strerror(status)); #ifndef __HAIKU__ *_length = 0; #endif goto rx_done; } if (dev->rx_status != B_OK) { status = usb->clear_feature(dev->pipe_in, USB_FEATURE_ENDPOINT_HALT); if (status != B_OK) DPRINTF_ERR("clear_feature() error %s\n", strerror(status)); goto rx_done; } // copy buffer size = dev->rx_actual_length; if (size > MAX_FRAME_SIZE || (size - 2) > *_length) { DPRINTF_ERR("ERROR read: bad frame size %ld\n", size); size = *_length; } else if (size < *_length) *_length = size - 2; memcpy(buffer, dev->rx_buffer, size); DPRINTF_INFO("read done %ld\n", *_length); rx_done: release_sem(dev->rx_sem); return status; } static status_t pegasus_device_write(driver_cookie *cookie, off_t position, const void *buffer, size_t *_length) { pegasus_dev *dev; status_t status; uint16 frameSize; DPRINTF_INFO("device %p write %ld\n", cookie, *_length); if (pegasus_checkdeviceinfo(dev = cookie->device) != B_OK) { DPRINTF_ERR("EINVAL\n"); return EINVAL; } if (dev->aue_dying) return B_DEVICE_NOT_FOUND; /* already unplugged */ // block until a free tx descriptor is available if ((status = acquire_sem_etc(dev->tx_sem, 1, B_TIMEOUT, ETHER_TRANSMIT_TIMEOUT)) < B_NO_ERROR) { DPRINTF_ERR("write: acquiring sem failed: %" B_PRIx32 ", %s\n", status, strerror(status)); return status; } if (*_length > MAX_FRAME_SIZE) *_length = MAX_FRAME_SIZE; frameSize = *_length; /* Copy data to tx buffer */ memcpy(dev->tx_buffer+2, buffer, frameSize); /* * The ADMtek documentation says that the packet length is * supposed to be specified in the first two bytes of the * transfer, however it actually seems to ignore this info * and base the frame size on the bulk transfer length. */ dev->tx_buffer[0] = (uint8)frameSize; dev->tx_buffer[1] = (uint8)(frameSize >> 8); // queue new request, bulk length is one more if size is a multiple of 64 status = usb->queue_bulk(dev->pipe_out, dev->tx_buffer, ((frameSize + 2) & 0x3f) ? frameSize + 2 : frameSize + 3, &pegasus_tx_callback, dev); if (status != B_OK){ DPRINTF_ERR("queue_bulk:failed:%08" B_PRIx32 "\n", status); goto tx_done; } // block until data is sent (if blocking is allowed) if ((status = acquire_sem_etc(dev->tx_sem_cb, 1, B_CAN_INTERRUPT, 0)) != B_NO_ERROR) { DPRINTF_ERR("cannot acquire write done sem: %" B_PRIx32 ", %s\n", status, strerror(status)); #ifndef __HAIKU__ *_length = 0; #endif goto tx_done; } if (dev->tx_status != B_OK) { status = usb->clear_feature(dev->pipe_out, USB_FEATURE_ENDPOINT_HALT); if (status != B_OK) DPRINTF_ERR("clear_feature() error %s\n", strerror(status)); goto tx_done; } *_length = frameSize; tx_done: release_sem(dev->tx_sem); return status; } static status_t pegasus_device_control(driver_cookie *cookie, uint32 op, void *arg, size_t len) { status_t err = B_ERROR; pegasus_dev *device; ASSERT(cookie != NULL); device = cookie->device; ASSERT(device != NULL); DPRINTF_INFO("ioctl(0x%x)\n", (int)op); if (device->aue_dying) return B_DEVICE_NOT_FOUND; /* already unplugged */ switch (op) { case ETHER_INIT: DPRINTF_INFO("control() ETHER_INIT\n"); return B_OK; case ETHER_GETADDR: DPRINTF_INFO("control() ETHER_GETADDR\n"); memcpy(arg, &device->macaddr, sizeof(device->macaddr)); return B_OK; case ETHER_NONBLOCK: if (*(int32 *)arg) { DPRINTF_INFO("non blocking mode on\n"); device->nonblocking = true; } else { DPRINTF_INFO("non blocking mode off\n"); device->nonblocking = false; } return B_OK; case ETHER_ADDMULTI: DPRINTF_INFO("control() ETHER_ADDMULTI\n"); break; case ETHER_REMMULTI: DPRINTF_INFO("control() ETHER_REMMULTI\n"); return B_OK; case ETHER_SETPROMISC: if (*(int32 *)arg) { DPRINTF_INFO("control() ETHER_SETPROMISC on\n"); } else { DPRINTF_INFO("control() ETHER_SETPROMISC off\n"); } return B_OK; case ETHER_GETFRAMESIZE: DPRINTF_INFO("control() ETHER_GETFRAMESIZE, framesize = %ld (MTU = %ld)\n", device->maxframesize, device->maxframesize - ENET_HEADER_SIZE); *(uint32*)arg = device->maxframesize; return B_OK; default: DPRINTF_INFO("control() Invalid command\n"); break; } return err; } static status_t pegasus_device_close(driver_cookie *cookie) { pegasus_dev *device; ASSERT(cookie != NULL && cookie->device != NULL); device = cookie->device; DPRINTF_INFO("close(%s)\n", device->name); /* detach the cookie from list */ acquire_sem(device->sem_lock); if (device->open_fds == cookie) device->open_fds = cookie->next; else { driver_cookie *p; for (p = device->open_fds; p != NULL; p = p->next) { if (p->next == cookie) { p->next = cookie->next; break; } } } --device->open; release_sem(device->sem_lock); return B_OK; } static status_t pegasus_device_free(driver_cookie *cookie) { pegasus_dev *device; ASSERT(cookie != NULL && cookie->device != NULL); device = cookie->device; DPRINTF_INFO("free(%s)\n", device->name); free(cookie); if (device->open > 0) DPRINTF_INFO("%d opens left\n", device->open); else if (device->aue_dying) { DPRINTF_INFO("removed %s\n", device->name); remove_device(device); } return B_OK; } /* Driver Hooks --------------------------------------------------------- ** ** These functions provide the glue used by DevFS to load/unload ** the driver and also handle registering with the USB bus manager ** to receive device added and removed events */ static usb_notify_hooks notify_hooks = { &pegasus_device_added, &pegasus_device_removed }; status_t init_hardware(void) { return B_OK; } status_t init_driver(void) { DPRINTF_INFO("init_driver(), built %s %s\n", __DATE__, __TIME__); #if DEBUG_DRIVER if (load_driver_symbols(drivername) == B_OK) { DPRINTF_INFO("loaded symbols\n"); } else { DPRINTF_INFO("no symbols for you!\n"); } #endif if (get_module(B_USB_MODULE_NAME, (module_info**) &usb) != B_OK) { DPRINTF_INFO("cannot get module \"%s\"\n", B_USB_MODULE_NAME); return B_ERROR; } if ((gDeviceListLock = create_sem(1, "dev_list_lock")) < 0) { put_module(B_USB_MODULE_NAME); return gDeviceListLock; } usb->register_driver(kDriverName, supported_devices, sizeof(supported_devices)/sizeof(usb_support_descriptor), NULL); usb->install_notify(kDriverName, ¬ify_hooks); return B_OK; } void uninit_driver(void) { DPRINTF_INFO("uninit_driver()\n"); usb->uninstall_notify(kDriverName); delete_sem(gDeviceListLock); put_module(B_USB_MODULE_NAME); free_device_names(); } const char** publish_devices(void) { if (gDeviceListChanged) { free_device_names(); alloc_device_names(); if (gDeviceNames != NULL) rebuild_device_names(); gDeviceListChanged = false; } ASSERT(gDeviceNames != NULL); return (const char **) gDeviceNames; } device_hooks* find_device(const char* name) { static device_hooks hooks = { (device_open_hook)pegasus_device_open, (device_close_hook)pegasus_device_close, (device_free_hook)pegasus_device_free, (device_control_hook)pegasus_device_control, (device_read_hook)pegasus_device_read, (device_write_hook)pegasus_device_write, NULL }; if (search_device_info(name) != NULL) return &hooks; return NULL; }