* Added support for device state change notifications and implemented the
ETHER_GET_LINK_STATE and ETHER_SET_LINK_STATE_SEM to provide this information and notification. * Don't try to clear an endpoint halt in case of a B_CANCELED status in the callbacks as this is triggered by explicitly canceling transfers when the device is closed/removed. * Renamed the semaphore members to *Sem to distinguish them better. * Some minor other cleanup and some added comments. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25458 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -35,6 +35,24 @@ typedef struct ethernet_functional_descriptor_s {
|
||||
uint8 num_wakeup_pattern_filters;
|
||||
} _PACKED ethernet_functional_descriptor;
|
||||
|
||||
/* notification definitions */
|
||||
#define CDC_NOTIFY_NETWORK_CONNECTION 0x00
|
||||
#define CDC_NOTIFY_CONNECTION_SPEED_CHANGE 0x2a
|
||||
|
||||
typedef struct cdc_notification_s {
|
||||
uint8 request_type;
|
||||
uint8 notification_code;
|
||||
uint16 value;
|
||||
uint16 index;
|
||||
uint16 data_length;
|
||||
uint8 data[0];
|
||||
} _PACKED cdc_notification;
|
||||
|
||||
typedef struct cdc_connection_speed_s {
|
||||
uint32 upstream_speed; /* in bits/s */
|
||||
uint32 downstream_speed; /* in bits/s */
|
||||
} _PACKED cdc_connection_speed;
|
||||
|
||||
extern usb_module_info *gUSBModule;
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
Distributed under the terms of the MIT license.
|
||||
*/
|
||||
#include <ether_driver.h>
|
||||
#include <net/if_media.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -20,9 +21,17 @@ ECMDevice::ECMDevice(usb_device device)
|
||||
fDataInterfaceIndex(0),
|
||||
fMACAddressIndex(0),
|
||||
fMaxSegmentSize(0),
|
||||
fControlEndpoint(0),
|
||||
fNotifyEndpoint(0),
|
||||
fReadEndpoint(0),
|
||||
fWriteEndpoint(0)
|
||||
fWriteEndpoint(0),
|
||||
fNotifyReadSem(-1),
|
||||
fNotifyWriteSem(-1),
|
||||
fNotifyBuffer(NULL),
|
||||
fNotifyBufferLength(0),
|
||||
fLinkStateChangeSem(-1),
|
||||
fHasConnection(false),
|
||||
fDownstreamSpeed(0),
|
||||
fUpstreamSpeed(0)
|
||||
{
|
||||
const usb_device_descriptor *deviceDescriptor
|
||||
= gUSBModule->get_device_descriptor(device);
|
||||
@@ -50,6 +59,7 @@ ECMDevice::ECMDevice(usb_device device)
|
||||
&& interface->generic_count > 0) {
|
||||
// try to find and interpret the union and ethernet functional
|
||||
// descriptors
|
||||
foundUnionDescriptor = foundEthernetDescriptor = false;
|
||||
for (size_t j = 0; j < interface->generic_count; j++) {
|
||||
usb_generic_descriptor *generic = &interface->generic[j]->generic;
|
||||
if (generic->length >= 5
|
||||
@@ -103,7 +113,23 @@ ECMDevice::ECMDevice(usb_device device)
|
||||
}
|
||||
|
||||
fControlInterfaceIndex = controlIndex;
|
||||
fControlEndpoint = interface->endpoint[0].handle;
|
||||
fNotifyEndpoint = interface->endpoint[0].handle;
|
||||
|
||||
// setup notify buffer and try to schedule a notification transfer
|
||||
fNotifyBufferLength = 64;
|
||||
fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength);
|
||||
if (fNotifyBuffer == NULL) {
|
||||
TRACE_ALWAYS("out of memory for notify buffer allocation\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (gUSBModule->queue_interrupt(fNotifyEndpoint, fNotifyBuffer,
|
||||
fNotifyBufferLength, _NotifyCallback, this) != B_OK) {
|
||||
// we cannot use notifications - hardcode to active connection
|
||||
fHasConnection = true;
|
||||
fDownstreamSpeed = 1000 * 1000 * 10; // 10Mbps
|
||||
fUpstreamSpeed = 1000 * 1000 * 10; // 10Mbps
|
||||
}
|
||||
|
||||
if (dataIndex >= config->interface_count) {
|
||||
TRACE_ALWAYS("data interface index invalid\n");
|
||||
@@ -126,16 +152,15 @@ ECMDevice::ECMDevice(usb_device device)
|
||||
}
|
||||
|
||||
fDataInterfaceIndex = dataIndex;
|
||||
fNotifyRead = create_sem(0, DRIVER_NAME"_notify_read");
|
||||
if (fNotifyRead < B_OK) {
|
||||
fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read");
|
||||
if (fNotifyReadSem < B_OK) {
|
||||
TRACE_ALWAYS("failed to create read notify sem\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fNotifyWrite = create_sem(0, DRIVER_NAME"_notify_write");
|
||||
if (fNotifyWrite < B_OK) {
|
||||
fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write");
|
||||
if (fNotifyWriteSem < B_OK) {
|
||||
TRACE_ALWAYS("failed to create write notify sem\n");
|
||||
delete_sem(fNotifyRead);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -145,8 +170,13 @@ ECMDevice::ECMDevice(usb_device device)
|
||||
|
||||
ECMDevice::~ECMDevice()
|
||||
{
|
||||
delete_sem(fNotifyRead);
|
||||
delete_sem(fNotifyWrite);
|
||||
if (fNotifyReadSem >= B_OK)
|
||||
delete_sem(fNotifyReadSem);
|
||||
if (fNotifyWriteSem >= B_OK)
|
||||
delete_sem(fNotifyWriteSem);
|
||||
|
||||
gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
|
||||
free(fNotifyBuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -244,13 +274,13 @@ ECMDevice::Read(uint8 *buffer, size_t *numBytes)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = acquire_sem_etc(fNotifyRead, 1, B_CAN_INTERRUPT, 0);
|
||||
result = acquire_sem_etc(fNotifyReadSem, 1, B_CAN_INTERRUPT, 0);
|
||||
if (result < B_OK) {
|
||||
*numBytes = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fStatusRead != B_OK) {
|
||||
if (fStatusRead != B_OK && fStatusRead != B_CANCELED) {
|
||||
TRACE_ALWAYS("device status error 0x%08lx\n", fStatusRead);
|
||||
result = gUSBModule->clear_feature(fReadEndpoint,
|
||||
USB_FEATURE_ENDPOINT_HALT);
|
||||
@@ -281,13 +311,13 @@ ECMDevice::Write(const uint8 *buffer, size_t *numBytes)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = acquire_sem_etc(fNotifyWrite, 1, B_CAN_INTERRUPT, 0);
|
||||
result = acquire_sem_etc(fNotifyWriteSem, 1, B_CAN_INTERRUPT, 0);
|
||||
if (result < B_OK) {
|
||||
*numBytes = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fStatusWrite != B_OK) {
|
||||
if (fStatusWrite != B_OK && fStatusWrite != B_CANCELED) {
|
||||
TRACE_ALWAYS("device status error 0x%08lx\n", fStatusWrite);
|
||||
result = gUSBModule->clear_feature(fWriteEndpoint,
|
||||
USB_FEATURE_ENDPOINT_HALT);
|
||||
@@ -318,6 +348,22 @@ ECMDevice::Control(uint32 op, void *buffer, size_t length)
|
||||
*(uint32 *)buffer = fMaxSegmentSize;
|
||||
return B_OK;
|
||||
|
||||
#if HAIKU_TARGET_PLATFORM_HAIKU
|
||||
case ETHER_SET_LINK_STATE_SEM:
|
||||
fLinkStateChangeSem = *(sem_id *)buffer;
|
||||
return B_OK;
|
||||
|
||||
case ETHER_GET_LINK_STATE:
|
||||
{
|
||||
ether_link_state *state = (ether_link_state *)buffer;
|
||||
state->media = IFM_ETHER | IFM_FULL_DUPLEX
|
||||
| (fHasConnection ? IFM_ACTIVE : 0);
|
||||
state->quality = 1000;
|
||||
state->speed = fDownstreamSpeed / 1000;
|
||||
return B_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
TRACE_ALWAYS("unsupported ioctl %lu\n", op);
|
||||
}
|
||||
@@ -367,7 +413,7 @@ ECMDevice::_ReadCallback(void *cookie, int32 status, void *data,
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
device->fActualLengthRead = actualLength;
|
||||
device->fStatusRead = status;
|
||||
release_sem_etc(device->fNotifyRead, 1, B_DO_NOT_RESCHEDULE);
|
||||
release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE);
|
||||
}
|
||||
|
||||
|
||||
@@ -378,5 +424,68 @@ ECMDevice::_WriteCallback(void *cookie, int32 status, void *data,
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
device->fActualLengthWrite = actualLength;
|
||||
device->fStatusWrite = status;
|
||||
release_sem_etc(device->fNotifyWrite, 1, B_DO_NOT_RESCHEDULE);
|
||||
release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ECMDevice::_NotifyCallback(void *cookie, int32 status, void *data,
|
||||
uint32 actualLength)
|
||||
{
|
||||
if (status == B_CANCELED)
|
||||
return;
|
||||
|
||||
ECMDevice *device = (ECMDevice *)cookie;
|
||||
if (status == B_OK && actualLength >= sizeof(cdc_notification)) {
|
||||
bool linkStateChange = false;
|
||||
cdc_notification *notification
|
||||
= (cdc_notification *)device->fNotifyBuffer;
|
||||
|
||||
switch (notification->notification_code) {
|
||||
case CDC_NOTIFY_NETWORK_CONNECTION:
|
||||
TRACE("connection state change to %d\n", notification->value);
|
||||
device->fHasConnection = notification->value > 0;
|
||||
linkStateChange = true;
|
||||
break;
|
||||
|
||||
case CDC_NOTIFY_CONNECTION_SPEED_CHANGE:
|
||||
{
|
||||
if (notification->data_length < sizeof(cdc_connection_speed)
|
||||
|| actualLength < sizeof(cdc_notification)
|
||||
+ sizeof(cdc_connection_speed)) {
|
||||
TRACE_ALWAYS("not enough data in connection speed change\n");
|
||||
break;
|
||||
}
|
||||
|
||||
cdc_connection_speed *speed;
|
||||
speed = (cdc_connection_speed *)¬ification->data[0];
|
||||
device->fUpstreamSpeed = speed->upstream_speed;
|
||||
device->fDownstreamSpeed = speed->downstream_speed;
|
||||
device->fHasConnection = true;
|
||||
TRACE("connection speed change to %ld/%ld\n",
|
||||
speed->downstream_speed, speed->upstream_speed);
|
||||
linkStateChange = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
TRACE_ALWAYS("unsupported notification 0x%02x\n",
|
||||
notification->notification_code);
|
||||
break;
|
||||
}
|
||||
|
||||
if (linkStateChange && device->fLinkStateChangeSem >= B_OK)
|
||||
release_sem_etc(device->fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
|
||||
}
|
||||
|
||||
if (status != B_OK) {
|
||||
TRACE_ALWAYS("device status error 0x%08lx\n", status);
|
||||
if (gUSBModule->clear_feature(device->fNotifyEndpoint,
|
||||
USB_FEATURE_ENDPOINT_HALT) != B_OK)
|
||||
TRACE_ALWAYS("failed to clear halt state\n");
|
||||
}
|
||||
|
||||
// schedule next notification buffer
|
||||
gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyBuffer,
|
||||
device->fNotifyBufferLength, _NotifyCallback, device);
|
||||
}
|
||||
|
||||
@@ -33,31 +33,45 @@ static void _ReadCallback(void *cookie, int32 status,
|
||||
void *data, uint32 actualLength);
|
||||
static void _WriteCallback(void *cookie, int32 status,
|
||||
void *data, uint32 actualLength);
|
||||
static void _NotifyCallback(void *cookie, int32 status,
|
||||
void *data, uint32 actualLength);
|
||||
|
||||
status_t _ReadMACAddress();
|
||||
|
||||
// state tracking
|
||||
status_t fStatus;
|
||||
bool fOpen;
|
||||
bool fRemoved;
|
||||
usb_device fDevice;
|
||||
|
||||
uint8 fMACAddress[6];
|
||||
|
||||
// interface and device infos
|
||||
uint8 fControlInterfaceIndex;
|
||||
uint8 fDataInterfaceIndex;
|
||||
uint8 fMACAddressIndex;
|
||||
uint16 fMaxSegmentSize;
|
||||
|
||||
usb_pipe fControlEndpoint;
|
||||
// pipes for notifications and data io
|
||||
usb_pipe fNotifyEndpoint;
|
||||
usb_pipe fReadEndpoint;
|
||||
usb_pipe fWriteEndpoint;
|
||||
|
||||
// data stores for async usb transfers
|
||||
uint32 fActualLengthRead;
|
||||
uint32 fActualLengthWrite;
|
||||
int32 fStatusRead;
|
||||
int32 fStatusWrite;
|
||||
sem_id fNotifyRead;
|
||||
sem_id fNotifyWrite;
|
||||
sem_id fNotifyReadSem;
|
||||
sem_id fNotifyWriteSem;
|
||||
|
||||
uint8 * fNotifyBuffer;
|
||||
uint32 fNotifyBufferLength;
|
||||
|
||||
// connection data
|
||||
sem_id fLinkStateChangeSem;
|
||||
uint8 fMACAddress[6];
|
||||
bool fHasConnection;
|
||||
uint32 fDownstreamSpeed;
|
||||
uint32 fUpstreamSpeed;
|
||||
};
|
||||
|
||||
#endif //_USB_ECM_DEVICE_H_
|
||||
|
||||
Reference in New Issue
Block a user