Adding USB ECM (Ethernet Control Model) driver. This driver should support
devices of the CDC class (2 - communication) with ECM subclass (6) interfaces. This type of device can be for example a USB to ethernet adapter or current UMTS cell phones that support the Wireless Mobile Communications Devices (WMC) standard. Note that there is also another, similar, thing called EEM (Ethernet Emulation Model) which we want to support too and is why I called this driver "usb_ecm" instead of just "usb_network". This driver was written in less than half a day and while it works nicely with my Sony Ericsson K850i (comitting over this very device/driver), it does not yet contain features like link state reporting and multicast and may obviously contain a few bugs. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25439 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
3560b757c2
commit
745c6cce89
@ -7,6 +7,7 @@ SubInclude HAIKU_TOP src add-ons kernel drivers network pegasus ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers network rtl8139 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers network rtl8169 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers network sis900 ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers network usb_ecm ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers network via_rhine ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers network vlance ;
|
||||
SubInclude HAIKU_TOP src add-ons kernel drivers network wb840 ;
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Driver for USB Ethernet Control Model devices
|
||||
Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
|
||||
Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
||||
#ifndef _BEOS_COMPATIBILITY_H_
|
||||
#define _BEOS_COMPATIBILITY_H_
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#define USB_ENDPOINT_ATTR_CONTROL 0x00
|
||||
#define USB_ENDPOINT_ATTR_ISOCHRONOUS 0x01
|
||||
#define USB_ENDPOINT_ATTR_BULK 0x02
|
||||
#define USB_ENDPOINT_ATTR_INTERRUPT 0x03
|
||||
#define USB_ENDPOINT_ATTR_MASK 0x03
|
||||
|
||||
#define USB_ENDPOINT_ADDR_DIR_IN 0x80
|
||||
#define USB_ENDPOINT_ADDR_DIR_OUT 0x00
|
||||
|
||||
typedef struct mutex {
|
||||
sem_id sem;
|
||||
int32 count;
|
||||
} mutex;
|
||||
|
||||
|
||||
static inline void
|
||||
mutex_init(mutex *lock, const char *name)
|
||||
{
|
||||
lock->sem = create_sem(0, name);
|
||||
lock->count = 0;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
mutex_destroy(mutex *lock)
|
||||
{
|
||||
delete_sem(lock->sem);
|
||||
}
|
||||
|
||||
|
||||
static inline status_t
|
||||
mutex_lock(mutex *lock)
|
||||
{
|
||||
if (atomic_add(&lock->count, -1) < 0)
|
||||
return acquire_sem(lock->sem);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
mutex_unlock(mutex *lock)
|
||||
{
|
||||
if (atomic_add(&lock->count, 1) < -1)
|
||||
release_sem(lock->sem);
|
||||
}
|
||||
|
||||
#endif /* !HAIKU_TARGET_PLATFORM_HAIKU */
|
||||
#endif /* _BEOS_COMPATIBILITY_H_ */
|
273
src/add-ons/kernel/drivers/network/usb_ecm/Driver.cpp
Normal file
273
src/add-ons/kernel/drivers/network/usb_ecm/Driver.cpp
Normal file
@ -0,0 +1,273 @@
|
||||
/*
|
||||
Driver for USB Ethernet Control Model devices
|
||||
Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
|
||||
Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
|
||||
#include <lock.h> // for mutex
|
||||
#else
|
||||
#include "BeOSCompatibility.h" // for pseudo mutex
|
||||
#endif
|
||||
|
||||
#include "Driver.h"
|
||||
#include "ECMDevice.h"
|
||||
|
||||
static const char *sDeviceBaseName = "net/usb_ecm/";
|
||||
ECMDevice *gECMDevices[MAX_DEVICES];
|
||||
char *gDeviceNames[MAX_DEVICES + 1];
|
||||
usb_module_info *gUSBModule = NULL;
|
||||
mutex gDriverLock;
|
||||
|
||||
|
||||
status_t
|
||||
usb_ecm_device_added(usb_device device, void **cookie)
|
||||
{
|
||||
*cookie = NULL;
|
||||
|
||||
ECMDevice *ecmDevice = new ECMDevice(device);
|
||||
status_t status = ecmDevice->InitCheck();
|
||||
if (status < B_OK) {
|
||||
delete ecmDevice;
|
||||
return status;
|
||||
}
|
||||
|
||||
mutex_lock(&gDriverLock);
|
||||
for (int32 i = 0; i < MAX_DEVICES; i++) {
|
||||
if (gECMDevices[i] != NULL)
|
||||
continue;
|
||||
|
||||
gECMDevices[i] = ecmDevice;
|
||||
*cookie = ecmDevice;
|
||||
|
||||
TRACE_ALWAYS("ecm device %ld added\n", i);
|
||||
mutex_unlock(&gDriverLock);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// no space for the device
|
||||
delete ecmDevice;
|
||||
mutex_unlock(&gDriverLock);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
usb_ecm_device_removed(void *cookie)
|
||||
{
|
||||
mutex_lock(&gDriverLock);
|
||||
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
for (int32 i = 0; i < MAX_DEVICES; i++) {
|
||||
if (gECMDevices[i] == device) {
|
||||
if (device->IsOpen()) {
|
||||
// the device will be deleted upon being freed
|
||||
device->Removed();
|
||||
} else {
|
||||
gECMDevices[i] = NULL;
|
||||
delete device;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&gDriverLock);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
//#pragma mark -
|
||||
|
||||
|
||||
status_t
|
||||
init_hardware()
|
||||
{
|
||||
TRACE("init_hardware()\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
init_driver()
|
||||
{
|
||||
TRACE("init_driver()\n");
|
||||
status_t status = get_module(B_USB_MODULE_NAME,
|
||||
(module_info **)&gUSBModule);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
for (int32 i = 0; i < MAX_DEVICES; i++)
|
||||
gECMDevices[i] = NULL;
|
||||
|
||||
gDeviceNames[0] = NULL;
|
||||
mutex_init(&gDriverLock, DRIVER_NAME"_devices");
|
||||
|
||||
static usb_notify_hooks notifyHooks = {
|
||||
&usb_ecm_device_added,
|
||||
&usb_ecm_device_removed
|
||||
};
|
||||
|
||||
static usb_support_descriptor supportDescriptor = {
|
||||
USB_INTERFACE_CLASS_CDC, /* CDC - Communication Device Class */
|
||||
USB_INTERFACE_SUBCLASS_ECM, /* ECM - Ethernet Control Model */
|
||||
0, 0, 0 /* no protocol, vendor or device */
|
||||
};
|
||||
|
||||
gUSBModule->register_driver(DRIVER_NAME, &supportDescriptor, 1, NULL);
|
||||
gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
uninit_driver()
|
||||
{
|
||||
TRACE("uninit_driver()\n");
|
||||
gUSBModule->uninstall_notify(DRIVER_NAME);
|
||||
mutex_lock(&gDriverLock);
|
||||
|
||||
for (int32 i = 0; i < MAX_DEVICES; i++) {
|
||||
if (gECMDevices[i]) {
|
||||
delete gECMDevices[i];
|
||||
gECMDevices[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32 i = 0; gDeviceNames[i]; i++) {
|
||||
free(gDeviceNames[i]);
|
||||
gDeviceNames[i] = NULL;
|
||||
}
|
||||
|
||||
mutex_destroy(&gDriverLock);
|
||||
put_module(B_USB_MODULE_NAME);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_ecm_open(const char *name, uint32 flags, void **cookie)
|
||||
{
|
||||
TRACE("open(%s, %lu, %p)\n", name, flags, cookie);
|
||||
mutex_lock(&gDriverLock);
|
||||
|
||||
*cookie = NULL;
|
||||
status_t status = ENODEV;
|
||||
int32 index = strtol(name + strlen(sDeviceBaseName), NULL, 10);
|
||||
if (index >= 0 && index < MAX_DEVICES && gECMDevices[index]) {
|
||||
status = gECMDevices[index]->Open(flags);
|
||||
*cookie = gECMDevices[index];
|
||||
}
|
||||
|
||||
mutex_unlock(&gDriverLock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_ecm_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
|
||||
{
|
||||
TRACE("read(%p, %Ld, %p, %lu)\n", cookie, position, buffer, *numBytes);
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
return device->Read((uint8 *)buffer, numBytes);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_ecm_write(void *cookie, off_t position, const void *buffer,
|
||||
size_t *numBytes)
|
||||
{
|
||||
TRACE("write(%p, %Ld, %p, %lu)\n", cookie, position, buffer, *numBytes);
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
return device->Write((const uint8 *)buffer, numBytes);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_ecm_control(void *cookie, uint32 op, void *buffer, size_t length)
|
||||
{
|
||||
TRACE("control(%p, %lu, %p, %lu)\n", cookie, op, buffer, length);
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
return device->Control(op, buffer, length);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_ecm_close(void *cookie)
|
||||
{
|
||||
TRACE("close(%p)\n", cookie);
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
return device->Close();
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_ecm_free(void *cookie)
|
||||
{
|
||||
TRACE("free(%p)\n", cookie);
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
mutex_lock(&gDriverLock);
|
||||
status_t status = device->Free();
|
||||
for (int32 i = 0; i < MAX_DEVICES; i++) {
|
||||
if (gECMDevices[i] == device) {
|
||||
// the device is removed already but as it was open the
|
||||
// removed hook has not deleted the object
|
||||
gECMDevices[i] = NULL;
|
||||
delete device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&gDriverLock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
const char **
|
||||
publish_devices()
|
||||
{
|
||||
TRACE("publish_devices()\n");
|
||||
for (int32 i = 0; gDeviceNames[i]; i++) {
|
||||
free(gDeviceNames[i]);
|
||||
gDeviceNames[i] = NULL;
|
||||
}
|
||||
|
||||
int32 deviceCount = 0;
|
||||
mutex_lock(&gDriverLock);
|
||||
for (int32 i = 0; i < MAX_DEVICES; i++) {
|
||||
if (gECMDevices[i] == NULL)
|
||||
continue;
|
||||
|
||||
gDeviceNames[deviceCount] = (char *)malloc(strlen(sDeviceBaseName) + 4);
|
||||
if (gDeviceNames[deviceCount]) {
|
||||
sprintf(gDeviceNames[deviceCount], "%s%ld", sDeviceBaseName, i);
|
||||
TRACE("publishing %s\n", gDeviceNames[deviceCount]);
|
||||
deviceCount++;
|
||||
} else
|
||||
TRACE_ALWAYS("publish_devices - no memory to allocate device name\n");
|
||||
}
|
||||
|
||||
gDeviceNames[deviceCount] = NULL;
|
||||
mutex_unlock(&gDriverLock);
|
||||
return (const char **)&gDeviceNames[0];
|
||||
}
|
||||
|
||||
|
||||
device_hooks *
|
||||
find_device(const char *name)
|
||||
{
|
||||
TRACE("find_device(%s)\n", name);
|
||||
static device_hooks deviceHooks = {
|
||||
usb_ecm_open,
|
||||
usb_ecm_close,
|
||||
usb_ecm_free,
|
||||
usb_ecm_control,
|
||||
usb_ecm_read,
|
||||
usb_ecm_write,
|
||||
NULL, /* select */
|
||||
NULL /* deselect */
|
||||
};
|
||||
|
||||
return &deviceHooks;
|
||||
}
|
61
src/add-ons/kernel/drivers/network/usb_ecm/Driver.h
Normal file
61
src/add-ons/kernel/drivers/network/usb_ecm/Driver.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
Driver for USB Ethernet Control Model devices
|
||||
Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
|
||||
Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef _USB_ECM_DRIVER_H_
|
||||
#define _USB_ECM_DRIVER_H_
|
||||
|
||||
#include <Drivers.h>
|
||||
#include <KernelExport.h>
|
||||
#include <OS.h>
|
||||
#include <USB3.h>
|
||||
|
||||
#include "kernel_cpp.h"
|
||||
|
||||
#define DRIVER_NAME "usb_ecm"
|
||||
#define MAX_DEVICES 8
|
||||
|
||||
/* class and subclass codes */
|
||||
#define USB_INTERFACE_CLASS_CDC 0x02
|
||||
#define USB_INTERFACE_SUBCLASS_ECM 0x06
|
||||
#define USB_INTERFACE_CLASS_CDC_DATA 0x0a
|
||||
#define USB_INTERFACE_SUBCLASS_DATA 0x00
|
||||
|
||||
/* communication device descriptor subtypes */
|
||||
#define FUNCTIONAL_SUBTYPE_UNION 0x06
|
||||
#define FUNCTIONAL_SUBTYPE_ETHERNET 0x0f
|
||||
|
||||
typedef struct ethernet_functional_descriptor_s {
|
||||
uint8 functional_descriptor_subtype;
|
||||
uint8 mac_address_index;
|
||||
uint32 ethernet_statistics;
|
||||
uint16 max_segment_size;
|
||||
uint16 num_multi_cast_filters;
|
||||
uint8 num_wakeup_pattern_filters;
|
||||
} _PACKED ethernet_functional_descriptor;
|
||||
|
||||
extern usb_module_info *gUSBModule;
|
||||
|
||||
extern "C" {
|
||||
status_t usb_ecm_device_added(usb_device device, void **cookie);
|
||||
status_t usb_ecm_device_removed(void *cookie);
|
||||
|
||||
status_t init_hardware();
|
||||
void uninit_driver();
|
||||
|
||||
status_t usb_ecm_open(const char *name, uint32 flags, void **cookie);
|
||||
status_t usb_ecm_read(void *cookie, off_t position, void *buffer, size_t *numBytes);
|
||||
status_t usb_ecm_write(void *cookie, off_t position, const void *buffer, size_t *numBytes);
|
||||
status_t usb_ecm_control(void *cookie, uint32 op, void *buffer, size_t length);
|
||||
status_t usb_ecm_close(void *cookie);
|
||||
status_t usb_ecm_free(void *cookie);
|
||||
|
||||
const char **publish_devices();
|
||||
device_hooks *find_device(const char *name);
|
||||
}
|
||||
|
||||
#define TRACE(x...) /*dprintf(DRIVER_NAME ": " x)*/
|
||||
#define TRACE_ALWAYS(x...) dprintf(DRIVER_NAME ": " x)
|
||||
|
||||
#endif //_USB_ECM_DRIVER_H_
|
382
src/add-ons/kernel/drivers/network/usb_ecm/ECMDevice.cpp
Normal file
382
src/add-ons/kernel/drivers/network/usb_ecm/ECMDevice.cpp
Normal file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
Driver for USB Ethernet Control Model devices
|
||||
Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
|
||||
Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include <ether_driver.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "BeOSCompatibility.h"
|
||||
#include "ECMDevice.h"
|
||||
#include "Driver.h"
|
||||
|
||||
ECMDevice::ECMDevice(usb_device device)
|
||||
: fStatus(B_ERROR),
|
||||
fOpen(false),
|
||||
fRemoved(false),
|
||||
fDevice(device),
|
||||
fControlInterfaceIndex(0),
|
||||
fDataInterfaceIndex(0),
|
||||
fMACAddressIndex(0),
|
||||
fMaxSegmentSize(0),
|
||||
fControlEndpoint(0),
|
||||
fReadEndpoint(0),
|
||||
fWriteEndpoint(0)
|
||||
{
|
||||
const usb_device_descriptor *deviceDescriptor
|
||||
= gUSBModule->get_device_descriptor(device);
|
||||
const usb_configuration_info *config
|
||||
= gUSBModule->get_nth_configuration(device, 0);
|
||||
|
||||
if (deviceDescriptor == NULL || config == NULL) {
|
||||
TRACE_ALWAYS("failed to get basic device info\n");
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE_ALWAYS("creating device: vendor: 0x%04x; device: 0x%04x\n",
|
||||
deviceDescriptor->vendor_id, deviceDescriptor->product_id);
|
||||
|
||||
uint8 controlIndex = 0;
|
||||
uint8 dataIndex = 0;
|
||||
bool foundUnionDescriptor = false;
|
||||
bool foundEthernetDescriptor = false;
|
||||
for (size_t i = 0; i < config->interface_count
|
||||
&& (!foundUnionDescriptor || !foundEthernetDescriptor); i++) {
|
||||
usb_interface_info *interface = config->interface[i].active;
|
||||
usb_interface_descriptor *descriptor = interface->descr;
|
||||
if (descriptor->interface_class == USB_INTERFACE_CLASS_CDC
|
||||
&& descriptor->interface_subclass == USB_INTERFACE_SUBCLASS_ECM
|
||||
&& interface->generic_count > 0) {
|
||||
// try to find and interpret the union and ethernet functional
|
||||
// descriptors
|
||||
for (size_t j = 0; j < interface->generic_count; j++) {
|
||||
usb_generic_descriptor *generic = &interface->generic[j]->generic;
|
||||
if (generic->length >= 5
|
||||
&& generic->data[0] == FUNCTIONAL_SUBTYPE_UNION) {
|
||||
controlIndex = generic->data[1];
|
||||
dataIndex = generic->data[2];
|
||||
foundUnionDescriptor = true;
|
||||
} else if (generic->length >= sizeof(ethernet_functional_descriptor)
|
||||
&& generic->data[0] == FUNCTIONAL_SUBTYPE_ETHERNET) {
|
||||
ethernet_functional_descriptor *ethernet
|
||||
= (ethernet_functional_descriptor *)generic->data;
|
||||
fMACAddressIndex = ethernet->mac_address_index;
|
||||
fMaxSegmentSize = ethernet->max_segment_size;
|
||||
foundEthernetDescriptor = true;
|
||||
}
|
||||
|
||||
if (foundUnionDescriptor && foundEthernetDescriptor)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundUnionDescriptor) {
|
||||
TRACE_ALWAYS("did not find a union descriptor\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!foundEthernetDescriptor) {
|
||||
TRACE_ALWAYS("did not find an ethernet descriptor\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_ReadMACAddress() != B_OK) {
|
||||
TRACE_ALWAYS("failed to read mac address\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (controlIndex >= config->interface_count) {
|
||||
TRACE_ALWAYS("control interface index invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// check that the indicated control interface fits our needs
|
||||
usb_interface_info *interface = config->interface[controlIndex].active;
|
||||
usb_interface_descriptor *descriptor = interface->descr;
|
||||
if ((descriptor->interface_class != USB_INTERFACE_CLASS_CDC
|
||||
|| descriptor->interface_subclass != USB_INTERFACE_SUBCLASS_ECM)
|
||||
|| interface->endpoint_count == 0) {
|
||||
TRACE_ALWAYS("control interface invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fControlInterfaceIndex = controlIndex;
|
||||
fControlEndpoint = interface->endpoint[0].handle;
|
||||
|
||||
if (dataIndex >= config->interface_count) {
|
||||
TRACE_ALWAYS("data interface index invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// check that the indicated data interface fits our needs
|
||||
if (config->interface[dataIndex].alt_count < 2) {
|
||||
TRACE_ALWAYS("data interface does not provide two alternate interfaces\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// alternate 0 is the disabled, endpoint-less default interface
|
||||
interface = &config->interface[dataIndex].alt[1];
|
||||
descriptor = interface->descr;
|
||||
if (descriptor->interface_class != USB_INTERFACE_CLASS_CDC_DATA
|
||||
|| interface->endpoint_count < 2) {
|
||||
TRACE_ALWAYS("data interface invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fDataInterfaceIndex = dataIndex;
|
||||
fNotifyRead = create_sem(0, DRIVER_NAME"_notify_read");
|
||||
if (fNotifyRead < B_OK) {
|
||||
TRACE_ALWAYS("failed to create read notify sem\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fNotifyWrite = create_sem(0, DRIVER_NAME"_notify_write");
|
||||
if (fNotifyWrite < B_OK) {
|
||||
TRACE_ALWAYS("failed to create write notify sem\n");
|
||||
delete_sem(fNotifyRead);
|
||||
return;
|
||||
}
|
||||
|
||||
fStatus = B_OK;
|
||||
}
|
||||
|
||||
|
||||
ECMDevice::~ECMDevice()
|
||||
{
|
||||
delete_sem(fNotifyRead);
|
||||
delete_sem(fNotifyWrite);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ECMDevice::Open(uint32 flags)
|
||||
{
|
||||
if (fOpen)
|
||||
return B_BUSY;
|
||||
|
||||
// reset the device by switching the data interface to the disabled first
|
||||
// interface and then enable it by setting the second actual data interface
|
||||
const usb_configuration_info *config
|
||||
= gUSBModule->get_nth_configuration(fDevice, 0);
|
||||
|
||||
gUSBModule->set_alt_interface(fDevice,
|
||||
&config->interface[fDataInterfaceIndex].alt[0]);
|
||||
|
||||
// update to the changed config
|
||||
config = gUSBModule->get_nth_configuration(fDevice, 0);
|
||||
gUSBModule->set_alt_interface(fDevice,
|
||||
&config->interface[fDataInterfaceIndex].alt[1]);
|
||||
|
||||
// update again
|
||||
config = gUSBModule->get_nth_configuration(fDevice, 0);
|
||||
usb_interface_info *interface = config->interface[fDataInterfaceIndex].active;
|
||||
if (interface->endpoint_count < 2) {
|
||||
TRACE_ALWAYS("setting the data alternate interface failed\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (!(interface->endpoint[0].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN))
|
||||
fWriteEndpoint = interface->endpoint[0].handle;
|
||||
else
|
||||
fReadEndpoint = interface->endpoint[0].handle;
|
||||
|
||||
if (interface->endpoint[1].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
|
||||
fReadEndpoint = interface->endpoint[1].handle;
|
||||
else
|
||||
fWriteEndpoint = interface->endpoint[1].handle;
|
||||
|
||||
if (fReadEndpoint == 0 || fWriteEndpoint == 0) {
|
||||
TRACE_ALWAYS("no read and write endpoints found\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// the device should now be ready
|
||||
fOpen = true;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ECMDevice::Close()
|
||||
{
|
||||
if (fRemoved) {
|
||||
fOpen = false;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
gUSBModule->cancel_queued_transfers(fReadEndpoint);
|
||||
gUSBModule->cancel_queued_transfers(fWriteEndpoint);
|
||||
|
||||
// put the device into non-connected mode again by switching the data
|
||||
// interface to the disabled alternate
|
||||
const usb_configuration_info *config
|
||||
= gUSBModule->get_nth_configuration(fDevice, 0);
|
||||
|
||||
gUSBModule->set_alt_interface(fDevice,
|
||||
&config->interface[fDataInterfaceIndex].alt[0]);
|
||||
|
||||
fOpen = false;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ECMDevice::Free()
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ECMDevice::Read(uint8 *buffer, size_t *numBytes)
|
||||
{
|
||||
if (fRemoved) {
|
||||
*numBytes = 0;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status_t result = gUSBModule->queue_bulk(fReadEndpoint, buffer, *numBytes,
|
||||
_ReadCallback, this);
|
||||
if (result != B_OK) {
|
||||
*numBytes = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
result = acquire_sem_etc(fNotifyRead, 1, B_CAN_INTERRUPT, 0);
|
||||
if (result < B_OK) {
|
||||
*numBytes = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fStatusRead != B_OK) {
|
||||
TRACE_ALWAYS("device status error 0x%08lx\n", fStatusRead);
|
||||
result = gUSBModule->clear_feature(fReadEndpoint,
|
||||
USB_FEATURE_ENDPOINT_HALT);
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("failed to clear halt state\n");
|
||||
*numBytes = 0;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
*numBytes = fActualLengthRead;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ECMDevice::Write(const uint8 *buffer, size_t *numBytes)
|
||||
{
|
||||
if (fRemoved) {
|
||||
*numBytes = 0;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status_t result = gUSBModule->queue_bulk(fWriteEndpoint, (uint8 *)buffer,
|
||||
*numBytes, _WriteCallback, this);
|
||||
if (result != B_OK) {
|
||||
*numBytes = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
result = acquire_sem_etc(fNotifyWrite, 1, B_CAN_INTERRUPT, 0);
|
||||
if (result < B_OK) {
|
||||
*numBytes = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fStatusWrite != B_OK) {
|
||||
TRACE_ALWAYS("device status error 0x%08lx\n", fStatusWrite);
|
||||
result = gUSBModule->clear_feature(fWriteEndpoint,
|
||||
USB_FEATURE_ENDPOINT_HALT);
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("failed to clear halt state\n");
|
||||
*numBytes = 0;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
*numBytes = fActualLengthWrite;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ECMDevice::Control(uint32 op, void *buffer, size_t length)
|
||||
{
|
||||
switch (op) {
|
||||
case ETHER_INIT:
|
||||
return B_OK;
|
||||
|
||||
case ETHER_GETADDR:
|
||||
memcpy(buffer, &fMACAddress, sizeof(fMACAddress));
|
||||
return B_OK;
|
||||
|
||||
case ETHER_GETFRAMESIZE:
|
||||
*(uint32 *)buffer = fMaxSegmentSize;
|
||||
return B_OK;
|
||||
|
||||
default:
|
||||
TRACE_ALWAYS("unsupported ioctl %lu\n", op);
|
||||
}
|
||||
|
||||
return B_DEV_INVALID_IOCTL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ECMDevice::_ReadMACAddress()
|
||||
{
|
||||
if (fMACAddressIndex == 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
size_t actualLength = 0;
|
||||
size_t macStringLength = 26;
|
||||
uint8 macString[macStringLength];
|
||||
status_t result = gUSBModule->get_descriptor(fDevice, USB_DESCRIPTOR_STRING,
|
||||
fMACAddressIndex, 0, macString, macStringLength, &actualLength);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
if (actualLength != macStringLength) {
|
||||
TRACE_ALWAYS("did not retrieve full mac address\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
char macPart[3];
|
||||
macPart[2] = 0;
|
||||
for (int32 i = 0; i < 6; i++) {
|
||||
macPart[0] = macString[2 + i * 4 + 0];
|
||||
macPart[1] = macString[2 + i * 4 + 2];
|
||||
fMACAddress[i] = strtol(macPart, NULL, 16);
|
||||
}
|
||||
|
||||
TRACE_ALWAYS("read mac address: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
fMACAddress[0], fMACAddress[1], fMACAddress[2], fMACAddress[3],
|
||||
fMACAddress[4], fMACAddress[5]);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ECMDevice::_ReadCallback(void *cookie, int32 status, void *data,
|
||||
uint32 actualLength)
|
||||
{
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
device->fActualLengthRead = actualLength;
|
||||
device->fStatusRead = status;
|
||||
release_sem_etc(device->fNotifyRead, 1, B_DO_NOT_RESCHEDULE);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ECMDevice::_WriteCallback(void *cookie, int32 status, void *data,
|
||||
uint32 actualLength)
|
||||
{
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
device->fActualLengthWrite = actualLength;
|
||||
device->fStatusWrite = status;
|
||||
release_sem_etc(device->fNotifyWrite, 1, B_DO_NOT_RESCHEDULE);
|
||||
}
|
63
src/add-ons/kernel/drivers/network/usb_ecm/ECMDevice.h
Normal file
63
src/add-ons/kernel/drivers/network/usb_ecm/ECMDevice.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Driver for USB Ethernet Control Model devices
|
||||
Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
|
||||
Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#ifndef _USB_ECM_DEVICE_H_
|
||||
#define _USB_ECM_DEVICE_H_
|
||||
|
||||
#include "Driver.h"
|
||||
|
||||
class ECMDevice {
|
||||
public:
|
||||
ECMDevice(usb_device device);
|
||||
~ECMDevice();
|
||||
|
||||
status_t InitCheck() { return fStatus; };
|
||||
|
||||
status_t Open(uint32 flags);
|
||||
bool IsOpen() { return fOpen; };
|
||||
|
||||
status_t Close();
|
||||
status_t Free();
|
||||
|
||||
status_t Read(uint8 *buffer, size_t *numBytes);
|
||||
status_t Write(const uint8 *buffer, size_t *numBytes);
|
||||
status_t Control(uint32 op, void *buffer, size_t length);
|
||||
|
||||
void Removed() { fRemoved = true; };
|
||||
bool IsRemoved() { return fRemoved; };
|
||||
|
||||
private:
|
||||
static void _ReadCallback(void *cookie, int32 status,
|
||||
void *data, uint32 actualLength);
|
||||
static void _WriteCallback(void *cookie, int32 status,
|
||||
void *data, uint32 actualLength);
|
||||
|
||||
status_t _ReadMACAddress();
|
||||
|
||||
status_t fStatus;
|
||||
bool fOpen;
|
||||
bool fRemoved;
|
||||
usb_device fDevice;
|
||||
|
||||
uint8 fMACAddress[6];
|
||||
|
||||
uint8 fControlInterfaceIndex;
|
||||
uint8 fDataInterfaceIndex;
|
||||
uint8 fMACAddressIndex;
|
||||
uint16 fMaxSegmentSize;
|
||||
|
||||
usb_pipe fControlEndpoint;
|
||||
usb_pipe fReadEndpoint;
|
||||
usb_pipe fWriteEndpoint;
|
||||
|
||||
uint32 fActualLengthRead;
|
||||
uint32 fActualLengthWrite;
|
||||
int32 fStatusRead;
|
||||
int32 fStatusWrite;
|
||||
sem_id fNotifyRead;
|
||||
sem_id fNotifyWrite;
|
||||
};
|
||||
|
||||
#endif //_USB_ECM_DEVICE_H_
|
10
src/add-ons/kernel/drivers/network/usb_ecm/Jamfile
Normal file
10
src/add-ons/kernel/drivers/network/usb_ecm/Jamfile
Normal file
@ -0,0 +1,10 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel drivers network usb_ecm ;
|
||||
|
||||
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||
|
||||
UsePrivateHeaders kernel net ;
|
||||
|
||||
KernelAddon usb_ecm :
|
||||
Driver.cpp
|
||||
ECMDevice.cpp
|
||||
;
|
45
src/add-ons/kernel/drivers/network/usb_ecm/kernel_cpp.h
Normal file
45
src/add-ons/kernel/drivers/network/usb_ecm/kernel_cpp.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef _KERNEL_CPP_H_
|
||||
#define _KERNEL_CPP_H_
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
inline void *
|
||||
operator new(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
|
||||
inline void *
|
||||
operator new[](size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
operator delete(void *pointer)
|
||||
{
|
||||
free(pointer);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
operator delete[](void *pointer)
|
||||
{
|
||||
free(pointer);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
terminate(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
__throw()
|
||||
{
|
||||
}
|
||||
|
||||
#endif // _KERNEL_CPP_H_
|
Loading…
Reference in New Issue
Block a user