From 69b17409a0176ea7947f16d36ba99f5e1c903be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Duval?= Date: Sun, 29 Oct 2006 12:09:52 +0000 Subject: [PATCH] Added driver for Pegasus-type USB ethernet devices Tested with a Admtek 8511 device to commit this revision git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19140 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/drivers/network/Jamfile | 1 + .../kernel/drivers/network/pegasus/Jamfile | 19 + .../kernel/drivers/network/pegasus/devlist.c | 126 +++ .../kernel/drivers/network/pegasus/driver.c | 820 ++++++++++++++++++ .../kernel/drivers/network/pegasus/driver.h | 166 ++++ .../kernel/drivers/network/pegasus/if_aue.c | 457 ++++++++++ .../drivers/network/pegasus/if_auereg.h | 276 ++++++ 7 files changed, 1865 insertions(+) create mode 100644 src/add-ons/kernel/drivers/network/pegasus/Jamfile create mode 100644 src/add-ons/kernel/drivers/network/pegasus/devlist.c create mode 100644 src/add-ons/kernel/drivers/network/pegasus/driver.c create mode 100644 src/add-ons/kernel/drivers/network/pegasus/driver.h create mode 100644 src/add-ons/kernel/drivers/network/pegasus/if_aue.c create mode 100644 src/add-ons/kernel/drivers/network/pegasus/if_auereg.h diff --git a/src/add-ons/kernel/drivers/network/Jamfile b/src/add-ons/kernel/drivers/network/Jamfile index 55339dce90..274a08cfca 100644 --- a/src/add-ons/kernel/drivers/network/Jamfile +++ b/src/add-ons/kernel/drivers/network/Jamfile @@ -9,6 +9,7 @@ SubInclude HAIKU_TOP src add-ons kernel drivers network rtl8169 ; SubInclude HAIKU_TOP src add-ons kernel drivers network ipro1000 ; SubInclude HAIKU_TOP src add-ons kernel drivers network ipw2100 ; SubInclude HAIKU_TOP src add-ons kernel drivers network etherpci ; +SubInclude HAIKU_TOP src add-ons kernel drivers network pegasus ; SubIncludeGPL HAIKU_TOP src add-ons kernel drivers network bcm440x ; SubIncludeGPL HAIKU_TOP src add-ons kernel drivers network bcm570x ; diff --git a/src/add-ons/kernel/drivers/network/pegasus/Jamfile b/src/add-ons/kernel/drivers/network/pegasus/Jamfile new file mode 100644 index 0000000000..b574994eda --- /dev/null +++ b/src/add-ons/kernel/drivers/network/pegasus/Jamfile @@ -0,0 +1,19 @@ +SubDir HAIKU_TOP src add-ons kernel drivers network pegasus ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +SubDirSysHdrs $(HAIKU_TOP) headers os drivers ; + +# For ether_driver.h +UsePrivateHeaders net ; + +KernelAddon pegasus : + driver.c + devlist.c + if_aue.c + ; + +# driver.c currently needs usbdevs.h so we make its path available and adds dependency +ObjectHdrs [ FGristFiles driver$(SUFOBJ) if_aue$(SUFOBJ) ] + : [ FDirName $(TARGET_COMMON_DEBUG_OBJECT_DIR) preferences devices ] ; +Includes [ FGristFiles driver.c if_aue.c ] : usbdevs.h ; diff --git a/src/add-ons/kernel/drivers/network/pegasus/devlist.c b/src/add-ons/kernel/drivers/network/pegasus/devlist.c new file mode 100644 index 0000000000..549095b964 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/pegasus/devlist.c @@ -0,0 +1,126 @@ +/* + * Copyright 2004-2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jérôme Duval + * + * Some portions of code are copyrighted by + * USB Joystick driver for BeOS R5 + * Copyright 2000 (C) ITO, Takayuki. All rights reserved + */ + + +#include "driver.h" + +#include +#include + + +sem_id gDeviceListLock = -1; +bool gDeviceListChanged = true; /* added or removed */ +/* dynamically generated */ +char **gDeviceNames = NULL; + + +static pegasus_dev *sDeviceList = NULL; +static int sDeviceCount = 0; + + +void +add_device_info(pegasus_dev *device) +{ + ASSERT(device != NULL); + + acquire_sem(gDeviceListLock); + device->next = sDeviceList; + sDeviceList = device; + sDeviceCount++; + gDeviceListChanged = true; + release_sem(gDeviceListLock); +} + + +void +remove_device_info(pegasus_dev *device) +{ + ASSERT(device != NULL); + + acquire_sem(gDeviceListLock); + + if (sDeviceList == device) + sDeviceList = device->next; + else { + pegasus_dev *previous; + for (previous = sDeviceList; previous != NULL; previous = previous->next) { + if (previous->next == device) { + previous->next = device->next; + --sDeviceCount; + gDeviceListChanged = true; + break; + } + } + ASSERT(previous != NULL); + } + release_sem(gDeviceListLock); +} + + +pegasus_dev * +search_device_info(const char* name) +{ + pegasus_dev *device; + + acquire_sem(gDeviceListLock); + for (device = sDeviceList; device != NULL; device = device->next) { + if (strcmp(device->name, name) == 0) + break; + } + + release_sem(gDeviceListLock); + return device; +} + + +// #pragma mark - device names + + +void +alloc_device_names(void) +{ + ASSERT(gDeviceNames == NULL); + gDeviceNames = malloc(sizeof(char *) * (sDeviceCount + 1)); +} + + +void +free_device_names(void) +{ + if (gDeviceNames != NULL) { + int i; + for (i = 0; gDeviceNames [i] != NULL; i++) { + free(gDeviceNames[i]); + } + + free(gDeviceNames); + gDeviceNames = NULL; + } +} + + +void +rebuild_device_names(void) +{ + int i; + pegasus_dev *device; + + ASSERT(gDeviceNames != NULL); + acquire_sem(gDeviceListLock); + for (i = 0, device = sDeviceList; device != NULL; device = device->next) { + gDeviceNames[i++] = strdup(device->name); + DPRINTF_INFO(MY_ID "publishing %s\n", device->name); + } + gDeviceNames[i] = NULL; + release_sem(gDeviceListLock); +} + diff --git a/src/add-ons/kernel/drivers/network/pegasus/driver.c b/src/add-ons/kernel/drivers/network/pegasus/driver.c new file mode 100644 index 0000000000..2c2914bf54 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/pegasus/driver.c @@ -0,0 +1,820 @@ +/* + * 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 "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_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_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_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, 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_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 %d\n", (int)sem); + free(device); + return NULL; + } + + device->rx_sem = sem = create_sem(1, DRIVER_NAME"_receive"); + if (sem < B_OK) { + DPRINTF_ERR("create_sem() failed %d\n", (int)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 %d\n", (int)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) +{ + size_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 %lx %lx %lx\n", ep, (int32)ed->attributes, (int32)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; + 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; ivendor_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) + return err; + device->nonblocking = (flags & O_NONBLOCK) ? true : false; + + 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; + } + + 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: %lx, %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:%08lx\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: %lx, %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; + } + + // 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: %lx, %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:%08lx\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: %lx, %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_ERROR; /* 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; +} diff --git a/src/add-ons/kernel/drivers/network/pegasus/driver.h b/src/add-ons/kernel/drivers/network/pegasus/driver.h new file mode 100644 index 0000000000..a0e916aef6 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/pegasus/driver.h @@ -0,0 +1,166 @@ +/* + * Pegasus BeOS Driver + * + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jérôme Duval + */ +#ifndef _DEV_PEGASUS_H_ +#define _DEV_PEGASUS_H_ + +#include +#include +#include +#include + +#include "ether_driver.h" + +#include "ByteOrder.h" + +// compat +#define u_uint8_t uint8 +#define u_uint16_t uint16 +#define u_int8_t int8 +#define u_int16_t int16 +#define DELAY snooze + +#include "if_auereg.h" + +#define VERSION "Version 0, Copyright (c) 2006 Jérôme Duval, compiled on " ## __DATE__ ## " " ## __TIME__ +#define DRIVER_NAME "pegasus" + +#define MY_ID "\033[34m" DRIVER_NAME ":\033[m " +#define MY_ERR "\033[31merror:\033[m " +#define MY_WARN "\033[31mwarning:\033[m " + +//#define DEBUG 1 +#if DEBUG + #define DPRINTF_INFO(x...) dprintf(MY_ID x) + #define DPRINTF_ERR(x...) dprintf(MY_ID x) +#else + #define DPRINTF_INFO(x...) + #define DPRINTF_ERR(x...) dprintf(MY_ID x) +#endif + +#define ASSERT(x) \ + ((x) ? 0 : dprintf (MY_ID "assertion failed at " __FILE__ ", line %d\n", __LINE__)) + +#define NUM_CARDS 3 +#define DEVNAME 32 + +#define DEFAULT_CONFIGURATION 0 + +#define ENET_HEADER_SIZE 14 +#define ETHER_ADDRESS_LENGTH 6 +#define MAX_FRAME_SIZE 1536 +#define ETHER_TRANSMIT_TIMEOUT ((bigtime_t)5000000) + /* five seconds */ + +/* + * Various supported device vendors/products. + */ +struct aue_type { + struct usb_devno { + uint16 vendor; + uint16 product; + } aue_dev; + uint16 aue_flags; +#define LSYS 0x0001 /* use Linksys reset */ +#define PNA 0x0002 /* has Home PNA */ +#define PII 0x0004 /* Pegasus II chip */ +}; + + +/* + * Devices + */ + +typedef struct _pegasus_dev { + /* list structure */ + struct _pegasus_dev *next; + + uint32 cookieMagic; + + /* maintain device */ + sem_id sem_lock; + + char name[DEVNAME]; /* used for resources */ + int type; + usb_device dev; + uint16 aue_vendor; + uint16 aue_product; + + uint16 ifno; + + int open; + struct driver_cookie *open_fds; + volatile bool aue_dying; + int number; + uint flags; + + int aue_unit; + uint16 aue_flags; + + volatile bool nonblocking; + + uint32 maxframesize; // 14 bytes header + MTU + uint8 macaddr[6]; + + usb_pipe pipe_in; /**/ + usb_pipe pipe_out; /**/ + usb_pipe pipe_intr; /**/ + + uint16 phy; + bool link; + + // receive data + + sem_id rx_sem; + sem_id rx_sem_cb; + char rx_buffer[MAX_FRAME_SIZE]; + status_t rx_status; + size_t rx_actual_length; + + // transmit data + + sem_id tx_sem; + sem_id tx_sem_cb; + char tx_buffer[MAX_FRAME_SIZE]; + status_t tx_status; + size_t tx_actual_length; + +} pegasus_dev; + +#define PEGASUS_COOKIE_MAGIC 'pega' + + +/* driver.c */ + +extern usb_module_info *usb; + +pegasus_dev *create_device(const usb_device dev, const usb_interface_info *ii, uint16 ifno); +void remove_device(pegasus_dev *device); + +/* devlist.c */ + +extern sem_id gDeviceListLock; +extern bool gDeviceListChanged; + +void add_device_info(pegasus_dev *device); +void remove_device_info(pegasus_dev *device); +pegasus_dev *search_device_info(const char *name); + +extern char **gDeviceNames; + +void alloc_device_names(void); +void free_device_names(void); +void rebuild_device_names(void); + +/* if_aue.c */ +void aue_attach(pegasus_dev *sc); +void aue_init(pegasus_dev *sc); +void aue_uninit(pegasus_dev *sc); + +#endif /* _DEV_PEGASUS_H_ */ diff --git a/src/add-ons/kernel/drivers/network/pegasus/if_aue.c b/src/add-ons/kernel/drivers/network/pegasus/if_aue.c new file mode 100644 index 0000000000..8022cb3e03 --- /dev/null +++ b/src/add-ons/kernel/drivers/network/pegasus/if_aue.c @@ -0,0 +1,457 @@ +/* + * Pegasus BeOS Driver + * + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jérôme Duval + */ + +/*- + * Copyright (c) 1997, 1998, 1999, 2000 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "driver.h" +#include "usbdevs.h" + +extern usb_module_info *usb; + +#define AUE_SETBIT(sc, reg, x) \ + aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x)) + +#define AUE_CLRBIT(sc, reg, x) \ + aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x)) + +static int +aue_csr_read_1(pegasus_dev *sc, int reg) +{ + status_t err; + uint8 val = 0; + size_t length; + + if (sc->aue_dying) + return (0); + + AUE_LOCK(sc); + + err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, + AUE_UR_READREG, 0, reg, 1, &val, &length); + AUE_UNLOCK(sc); + + if (err) { + return (0); + } + + return (val); +} + +static int +aue_csr_read_2(pegasus_dev *sc, int reg) +{ + status_t err; + uint16 val = 0; + size_t length; + + if (sc->aue_dying) + return (0); + + AUE_LOCK(sc); + + err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_IN, + AUE_UR_READREG, 0, reg, 2, &val, &length); + + AUE_UNLOCK(sc); + + if (err) { + return (0); + } + + return (val); +} + +static int +aue_csr_write_1(pegasus_dev *sc, int reg, int val) +{ + status_t err; + size_t length = 1; + + if (sc->aue_dying) + return (0); + + AUE_LOCK(sc); + + err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, + AUE_UR_WRITEREG, val, reg, 1, &val, &length); + + AUE_UNLOCK(sc); + + if (err) { + return (-1); + } + + return (0); +} + +static int +aue_csr_write_2(pegasus_dev *sc, int reg, int val) +{ + status_t err; + size_t length = 2; + + if (sc->aue_dying) + return (0); + + AUE_LOCK(sc); + + err = usb->send_request(sc->dev, USB_REQTYPE_VENDOR | USB_REQTYPE_DEVICE_OUT, + AUE_UR_WRITEREG, val, reg, 2, &val, &length); + + AUE_UNLOCK(sc); + + if (err) { + return (-1); + } + + return (0); +} + +/* + * Read a word of data stored in the EEPROM at address 'addr.' + */ +static void +aue_eeprom_getword(pegasus_dev *sc, int addr, u_int16_t *dest) +{ + int i; + u_int16_t word = 0; + + aue_csr_write_1(sc, AUE_EE_REG, addr); + aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ); + + for (i = 0; i < AUE_TIMEOUT; i++) { + if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE) + break; + } + + if (i == AUE_TIMEOUT) { + dprintf("aue%d: EEPROM read timed out\n", + sc->aue_unit); + } + + word = aue_csr_read_2(sc, AUE_EE_DATA); + *dest = word; + + return; +} + + + + +/* + * Read a sequence of words from the EEPROM. + */ +static void +aue_read_eeprom(pegasus_dev *sc, caddr_t dest, int off, int cnt, int swap) +{ + int i; + u_int16_t word = 0, *ptr; + + for (i = 0; i < cnt; i++) { + aue_eeprom_getword(sc, off + i, &word); + ptr = (u_int16_t *)(dest + (i * 2)); + if (swap) + *ptr = ntohs(word); + else + *ptr = word; + } + + return; +} + + +static int +aue_miibus_readreg(pegasus_dev *sc, int phy, int reg) +{ + int i; + u_int16_t val = 0; + + /* + * The Am79C901 HomePNA PHY actually contains + * two transceivers: a 1Mbps HomePNA PHY and a + * 10Mbps full/half duplex ethernet PHY with + * NWAY autoneg. However in the ADMtek adapter, + * only the 1Mbps PHY is actually connected to + * anything, so we ignore the 10Mbps one. It + * happens to be configured for MII address 3, + * so we filter that out. + */ + if (sc->aue_vendor == USB_VENDOR_ADMTEK && + sc->aue_product == USB_PRODUCT_ADMTEK_PEGASUS) { + if (phy == 3) + return (0); +#ifdef notdef + if (phy != 1) + return (0); +#endif + } + + aue_csr_write_1(sc, AUE_PHY_ADDR, phy); + aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ); + + for (i = 0; i < AUE_TIMEOUT; i++) { + if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) + break; + } + + if (i == AUE_TIMEOUT) { + dprintf("aue%d: MII read timed out\n", sc->aue_unit); + } + + val = aue_csr_read_2(sc, AUE_PHY_DATA); + + return (val); +} + + +static int +aue_miibus_writereg(pegasus_dev *sc, int phy, int reg, int data) +{ + int i; + + if (phy == 3) + return (0); + + aue_csr_write_2(sc, AUE_PHY_DATA, data); + aue_csr_write_1(sc, AUE_PHY_ADDR, phy); + aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE); + + for (i = 0; i < AUE_TIMEOUT; i++) { + if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE) + break; + } + + if (i == AUE_TIMEOUT) { + dprintf("aue%d: MII read timed out\n", + sc->aue_unit); + } + + return (0); +} + + +static uint16 +aue_miibus_read(pegasus_dev *dev, uint16 reg) +{ + return aue_miibus_readreg(dev, dev->phy, reg); +} + + +static void +aue_miibus_write(pegasus_dev *dev, uint16 reg, uint16 value) +{ + aue_miibus_writereg(dev, dev->phy, reg, value); +} + + +static uint16 +aue_miibus_status_from_phy(pegasus_dev *dev, uint16 phy) +{ + uint16 status; + int i = 0; + + // the status must be retrieved two times, because the first + // one may not work on some PHYs (notably ICS 1893) + while (i++ < 2) + status = aue_miibus_readreg(dev, phy, MII_STATUS); + + return status; +} + + +static uint16 +aue_miibus_status(pegasus_dev *dev) +{ + return aue_miibus_status_from_phy(dev, dev->phy); +} + + +static status_t +aue_init_phy(pegasus_dev *dev) +{ + uint16 phy, status; + int i; + + dev->phy = 255; + + // search for total of 32 possible MII PHY addresses + for (phy = 0; phy < 32; phy++) { + uint16 status; + + status = aue_miibus_status_from_phy(dev, phy); + if (status == 0xffff || status == 0x0000) + // this MII is not accessable + continue; + + dev->phy = phy; + } + + if (dev->phy == 255) { + DPRINTF_ERR("No MII PHY transceiver found!\n"); + return B_ENTRY_NOT_FOUND; + } + DPRINTF_INFO("aue_init_phy MII PHY found at %d\n", dev->phy); + + status = aue_miibus_read(dev, MII_CONTROL); + status &= ~MII_CONTROL_ISOLATE; + + aue_miibus_write(dev, MII_CONTROL, status); + + aue_miibus_write(dev, MII_CONTROL, MII_CONTROL_RESET); + for (i = 0; i < 100; i++) { + if ((aue_miibus_read(dev, MII_STATUS) & MII_CONTROL_RESET) == 0) + break; + DELAY(1000); + } + + dev->link = aue_miibus_status(dev) & MII_STATUS_LINK; + + return B_OK; +} + + +static void +aue_reset_pegasus_II(pegasus_dev *sc) +{ + /* Magic constants taken from Linux driver. */ + aue_csr_write_1(sc, AUE_REG_1D, 0); + aue_csr_write_1(sc, AUE_REG_7B, 2); +#if 0 + if ((sc->aue_flags & HAS_HOME_PNA) && mii_mode) + aue_csr_write_1(sc, AUE_REG_81, 6); + else +#endif + aue_csr_write_1(sc, AUE_REG_81, 2); +} + +static void +aue_reset(pegasus_dev *sc) +{ + int i; + + AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC); + + for (i = 0; i < AUE_TIMEOUT; i++) { + if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC)) + break; + } + + if (i == AUE_TIMEOUT) + dprintf("aue%d: reset failed\n", sc->aue_unit); + + /* + * The PHY(s) attached to the Pegasus chip may be held + * in reset until we flip on the GPIO outputs. Make sure + * to set the GPIO pins high so that the PHY(s) will + * be enabled. + * + * Note: We force all of the GPIO pins low first, *then* + * enable the ones we want. + */ + aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0); + aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0|AUE_GPIO_SEL1); + + if (sc->aue_flags & LSYS) { + /* Grrr. LinkSys has to be different from everyone else. */ + aue_csr_write_1(sc, AUE_GPIO0, + AUE_GPIO_SEL0 | AUE_GPIO_SEL1); + aue_csr_write_1(sc, AUE_GPIO0, + AUE_GPIO_SEL0 | AUE_GPIO_SEL1 | AUE_GPIO_OUT0); + } + + if (sc->aue_flags & PII) + aue_reset_pegasus_II(sc); + + /* Wait a little while for the chip to get its brains in order. */ + DELAY(10000); + + return; +} + + +/* + * Attach + */ +void +aue_attach(pegasus_dev *sc) +{ + u_char eaddr[ETHER_ADDRESS_LENGTH]; + + AUE_LOCK(sc); + + /* Reset the adapter. */ + aue_reset(sc); + + /* + * Get station address from the EEPROM. + */ + aue_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0); + + memcpy(sc->macaddr, eaddr, ETHER_ADDRESS_LENGTH); + + if (aue_init_phy(sc) != B_OK) + goto done; + + sc->aue_dying = 0; + +done: + AUE_UNLOCK(sc); +} + + +void +aue_init(pegasus_dev *sc) +{ + + /* Enable RX and TX */ + aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB); + AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB); + AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR); + +} + + +void +aue_uninit(pegasus_dev *sc) +{ + aue_csr_write_1(sc, AUE_CTL0, 0); + aue_csr_write_1(sc, AUE_CTL1, 0); + aue_reset(sc); +} diff --git a/src/add-ons/kernel/drivers/network/pegasus/if_auereg.h b/src/add-ons/kernel/drivers/network/pegasus/if_auereg.h new file mode 100644 index 0000000000..0e36ab682f --- /dev/null +++ b/src/add-ons/kernel/drivers/network/pegasus/if_auereg.h @@ -0,0 +1,276 @@ +/* + * Pegasus BeOS Driver + * + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Jérôme Duval + */ + +/*- + * Copyright (c) 1997, 1998, 1999 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: /repoman/r/ncvs/src/sys/dev/usb/if_auereg.h,v 1.22 2005/06/10 16:49:15 brooks Exp $ + */ + +/* + * Register definitions for ADMtek Pegasus AN986 USB to Ethernet + * chip. The Pegasus uses a total of four USB endpoints: the control + * endpoint (0), a bulk read endpoint for receiving packets (1), + * a bulk write endpoint for sending packets (2) and an interrupt + * endpoint for passing RX and TX status (3). Endpoint 0 is used + * to read and write the ethernet module's registers. All registers + * are 8 bits wide. + * + * Packet transfer is done in 64 byte chunks. The last chunk in a + * transfer is denoted by having a length less that 64 bytes. For + * the RX case, the data includes an optional RX status word. + */ + +#define AUE_UR_READREG 0xF0 +#define AUE_UR_WRITEREG 0xF1 + +#define AUE_CONFIG_NO 1 +#define AUE_IFACE_IDX 0 + +/* + * Note that while the ADMtek technically has four + * endpoints, the control endpoint (endpoint 0) is + * regarded as special by the USB code and drivers + * don't have direct access to it. (We access it + * using usbd_do_request() when reading/writing + * registers.) Consequently, our endpoint indexes + * don't match those in the ADMtek Pegasus manual: + * we consider the RX data endpoint to be index 0 + * and work up from there. + */ +#define AUE_ENDPT_RX 0x0 +#define AUE_ENDPT_TX 0x1 +#define AUE_ENDPT_INTR 0x2 +#define AUE_ENDPT_MAX 0x3 + +#define AUE_INTR_PKTLEN 0x8 + +#define AUE_CTL0 0x00 +#define AUE_CTL1 0x01 +#define AUE_CTL2 0x02 +#define AUE_MAR0 0x08 +#define AUE_MAR1 0x09 +#define AUE_MAR2 0x0A +#define AUE_MAR3 0x0B +#define AUE_MAR4 0x0C +#define AUE_MAR5 0x0D +#define AUE_MAR6 0x0E +#define AUE_MAR7 0x0F +#define AUE_MAR AUE_MAR0 +#define AUE_PAR0 0x10 +#define AUE_PAR1 0x11 +#define AUE_PAR2 0x12 +#define AUE_PAR3 0x13 +#define AUE_PAR4 0x14 +#define AUE_PAR5 0x15 +#define AUE_PAR AUE_PAR0 +#define AUE_PAUSE0 0x18 +#define AUE_PAUSE1 0x19 +#define AUE_PAUSE AUE_PAUSE0 +#define AUE_RX_FLOWCTL_CNT 0x1A +#define AUE_RX_FLOWCTL_FIFO 0x1B +#define AUE_REG_1D 0x1D +#define AUE_EE_REG 0x20 +#define AUE_EE_DATA0 0x21 +#define AUE_EE_DATA1 0x22 +#define AUE_EE_DATA AUE_EE_DATA0 +#define AUE_EE_CTL 0x23 +#define AUE_PHY_ADDR 0x25 +#define AUE_PHY_DATA0 0x26 +#define AUE_PHY_DATA1 0x27 +#define AUE_PHY_DATA AUE_PHY_DATA0 +#define AUE_PHY_CTL 0x28 +#define AUE_USB_STS 0x2A +#define AUE_TXSTAT0 0x2B +#define AUE_TXSTAT1 0x2C +#define AUE_TXSTAT AUE_TXSTAT0 +#define AUE_RXSTAT 0x2D +#define AUE_PKTLOST0 0x2E +#define AUE_PKTLOST1 0x2F +#define AUE_PKTLOST AUE_PKTLOST0 + +#define AUE_REG_7B 0x7B +#define AUE_GPIO0 0x7E +#define AUE_GPIO1 0x7F +#define AUE_REG_81 0x81 + +#define AUE_CTL0_INCLUDE_RXCRC 0x01 +#define AUE_CTL0_ALLMULTI 0x02 +#define AUE_CTL0_STOP_BACKOFF 0x04 +#define AUE_CTL0_RXSTAT_APPEND 0x08 +#define AUE_CTL0_WAKEON_ENB 0x10 +#define AUE_CTL0_RXPAUSE_ENB 0x20 +#define AUE_CTL0_RX_ENB 0x40 +#define AUE_CTL0_TX_ENB 0x80 + +#define AUE_CTL1_HOMELAN 0x04 +#define AUE_CTL1_RESETMAC 0x08 +#define AUE_CTL1_SPEEDSEL 0x10 /* 0 = 10mbps, 1 = 100mbps */ +#define AUE_CTL1_DUPLEX 0x20 /* 0 = half, 1 = full */ +#define AUE_CTL1_DELAYHOME 0x40 + +#define AUE_CTL2_EP3_CLR 0x01 /* reading EP3 clrs status regs */ +#define AUE_CTL2_RX_BADFRAMES 0x02 +#define AUE_CTL2_RX_PROMISC 0x04 +#define AUE_CTL2_LOOPBACK 0x08 +#define AUE_CTL2_EEPROMWR_ENB 0x10 +#define AUE_CTL2_EEPROM_LOAD 0x20 + +#define AUE_EECTL_WRITE 0x01 +#define AUE_EECTL_READ 0x02 +#define AUE_EECTL_DONE 0x04 + +#define AUE_PHYCTL_PHYREG 0x1F +#define AUE_PHYCTL_WRITE 0x20 +#define AUE_PHYCTL_READ 0x40 +#define AUE_PHYCTL_DONE 0x80 + +#define AUE_USBSTS_SUSPEND 0x01 +#define AUE_USBSTS_RESUME 0x02 + +#define AUE_TXSTAT0_JABTIMO 0x04 +#define AUE_TXSTAT0_CARLOSS 0x08 +#define AUE_TXSTAT0_NOCARRIER 0x10 +#define AUE_TXSTAT0_LATECOLL 0x20 +#define AUE_TXSTAT0_EXCESSCOLL 0x40 +#define AUE_TXSTAT0_UNDERRUN 0x80 + +#define AUE_TXSTAT1_PKTCNT 0x0F +#define AUE_TXSTAT1_FIFO_EMPTY 0x40 +#define AUE_TXSTAT1_FIFO_FULL 0x80 + +#define AUE_RXSTAT_OVERRUN 0x01 +#define AUE_RXSTAT_PAUSE 0x02 + +#define AUE_GPIO_IN0 0x01 +#define AUE_GPIO_OUT0 0x02 +#define AUE_GPIO_SEL0 0x04 +#define AUE_GPIO_IN1 0x08 +#define AUE_GPIO_OUT1 0x10 +#define AUE_GPIO_SEL1 0x20 + +struct aue_intrpkt { + u_int8_t aue_txstat0; + u_int8_t aue_txstat1; + u_int8_t aue_rxstat; + u_int8_t aue_rxlostpkt0; + u_int8_t aue_rxlostpkt1; + u_int8_t aue_wakeupstat; + u_int8_t aue_rsvd; +}; + +struct aue_rxpkt { + u_int16_t aue_pktlen; + u_int8_t aue_rxstat; +}; + +#define AUE_RXSTAT_MCAST 0x01 +#define AUE_RXSTAT_GIANT 0x02 +#define AUE_RXSTAT_RUNT 0x04 +#define AUE_RXSTAT_CRCERR 0x08 +#define AUE_RXSTAT_DRIBBLE 0x10 +#define AUE_RXSTAT_MASK 0x1E + +#define AUE_INC(x, y) (x) = (x + 1) % y + +#if 0 +#define AUE_LOCK(_sc) mtx_lock(&(_sc)->aue_mtx) +#define AUE_UNLOCK(_sc) mtx_unlock(&(_sc)->aue_mtx) +#else +#define AUE_LOCK(_sc) +#define AUE_UNLOCK(_sc) +#endif + +#define AUE_TIMEOUT 1000 +#define AUE_MIN_FRAMELEN 60 +#define AUE_TX_TIMEOUT 10000 /* ms */ +#define AUE_INTR_INTERVAL 100 /* ms */ +#define AUE_BUFSZ 1536 +#define ETHER_ALIGN 2 + + +/***************************** Media Independent Interface *****************************/ + + +enum MII_address { + // standard registers + MII_CONTROL = 0x00, + MII_STATUS = 0x01, + MII_PHY_ID0 = 0x02, + MII_PHY_ID1 = 0x03, + MII_AUTONEG_ADV = 0x04, + MII_AUTONEG_LINK_PARTNER = 0x05, + MII_AUTONEG_EXT = 0x06, +}; + +enum MII_control { + MII_CONTROL_RESET = 0x8000, + MII_CONTROL_RESET_AUTONEG = 0x0200, + MII_CONTROL_AUTO = 0x1000, + MII_CONTROL_FULL_DUPLEX = 0x0100, + MII_CONTROL_ISOLATE = 0x0400 +}; + +enum SiS900_MII_commands { + MII_CMD_READ = 0x6000, + MII_CMD_WRITE = 0x5002, + + MII_PHY_SHIFT = 7, + MII_REG_SHIFT = 2, +}; + +enum MII_status_bits { + MII_STATUS_EXT = 0x0001, + MII_STATUS_JAB = 0x0002, + MII_STATUS_LINK = 0x0004, + MII_STATUS_CAN_AUTO = 0x0008, + MII_STATUS_FAULT = 0x0010, + MII_STATUS_AUTO_DONE = 0x0020, + MII_STATUS_CAN_T = 0x0800, + MII_STATUS_CAN_T_FDX = 0x1000, + MII_STATUS_CAN_TX = 0x2000, + MII_STATUS_CAN_TX_FDX = 0x4000, + MII_STATUS_CAN_T4 = 0x8000 +}; + + +enum MII_link_status { + MII_LINK_FAIL = 0x4000, + MII_LINK_100_MBIT = 0x0080, + MII_LINK_FULL_DUPLEX = 0x0040 +};