* 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;
|
uint8 num_wakeup_pattern_filters;
|
||||||
} _PACKED ethernet_functional_descriptor;
|
} _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 usb_module_info *gUSBModule;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
Distributed under the terms of the MIT license.
|
Distributed under the terms of the MIT license.
|
||||||
*/
|
*/
|
||||||
#include <ether_driver.h>
|
#include <ether_driver.h>
|
||||||
|
#include <net/if_media.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@@ -20,9 +21,17 @@ ECMDevice::ECMDevice(usb_device device)
|
|||||||
fDataInterfaceIndex(0),
|
fDataInterfaceIndex(0),
|
||||||
fMACAddressIndex(0),
|
fMACAddressIndex(0),
|
||||||
fMaxSegmentSize(0),
|
fMaxSegmentSize(0),
|
||||||
fControlEndpoint(0),
|
fNotifyEndpoint(0),
|
||||||
fReadEndpoint(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
|
const usb_device_descriptor *deviceDescriptor
|
||||||
= gUSBModule->get_device_descriptor(device);
|
= gUSBModule->get_device_descriptor(device);
|
||||||
@@ -50,6 +59,7 @@ ECMDevice::ECMDevice(usb_device device)
|
|||||||
&& interface->generic_count > 0) {
|
&& interface->generic_count > 0) {
|
||||||
// try to find and interpret the union and ethernet functional
|
// try to find and interpret the union and ethernet functional
|
||||||
// descriptors
|
// descriptors
|
||||||
|
foundUnionDescriptor = foundEthernetDescriptor = false;
|
||||||
for (size_t j = 0; j < interface->generic_count; j++) {
|
for (size_t j = 0; j < interface->generic_count; j++) {
|
||||||
usb_generic_descriptor *generic = &interface->generic[j]->generic;
|
usb_generic_descriptor *generic = &interface->generic[j]->generic;
|
||||||
if (generic->length >= 5
|
if (generic->length >= 5
|
||||||
@@ -103,7 +113,23 @@ ECMDevice::ECMDevice(usb_device device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fControlInterfaceIndex = controlIndex;
|
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) {
|
if (dataIndex >= config->interface_count) {
|
||||||
TRACE_ALWAYS("data interface index invalid\n");
|
TRACE_ALWAYS("data interface index invalid\n");
|
||||||
@@ -126,16 +152,15 @@ ECMDevice::ECMDevice(usb_device device)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fDataInterfaceIndex = dataIndex;
|
fDataInterfaceIndex = dataIndex;
|
||||||
fNotifyRead = create_sem(0, DRIVER_NAME"_notify_read");
|
fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read");
|
||||||
if (fNotifyRead < B_OK) {
|
if (fNotifyReadSem < B_OK) {
|
||||||
TRACE_ALWAYS("failed to create read notify sem\n");
|
TRACE_ALWAYS("failed to create read notify sem\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fNotifyWrite = create_sem(0, DRIVER_NAME"_notify_write");
|
fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write");
|
||||||
if (fNotifyWrite < B_OK) {
|
if (fNotifyWriteSem < B_OK) {
|
||||||
TRACE_ALWAYS("failed to create write notify sem\n");
|
TRACE_ALWAYS("failed to create write notify sem\n");
|
||||||
delete_sem(fNotifyRead);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,8 +170,13 @@ ECMDevice::ECMDevice(usb_device device)
|
|||||||
|
|
||||||
ECMDevice::~ECMDevice()
|
ECMDevice::~ECMDevice()
|
||||||
{
|
{
|
||||||
delete_sem(fNotifyRead);
|
if (fNotifyReadSem >= B_OK)
|
||||||
delete_sem(fNotifyWrite);
|
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;
|
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) {
|
if (result < B_OK) {
|
||||||
*numBytes = 0;
|
*numBytes = 0;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fStatusRead != B_OK) {
|
if (fStatusRead != B_OK && fStatusRead != B_CANCELED) {
|
||||||
TRACE_ALWAYS("device status error 0x%08lx\n", fStatusRead);
|
TRACE_ALWAYS("device status error 0x%08lx\n", fStatusRead);
|
||||||
result = gUSBModule->clear_feature(fReadEndpoint,
|
result = gUSBModule->clear_feature(fReadEndpoint,
|
||||||
USB_FEATURE_ENDPOINT_HALT);
|
USB_FEATURE_ENDPOINT_HALT);
|
||||||
@@ -281,13 +311,13 @@ ECMDevice::Write(const uint8 *buffer, size_t *numBytes)
|
|||||||
return result;
|
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) {
|
if (result < B_OK) {
|
||||||
*numBytes = 0;
|
*numBytes = 0;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fStatusWrite != B_OK) {
|
if (fStatusWrite != B_OK && fStatusWrite != B_CANCELED) {
|
||||||
TRACE_ALWAYS("device status error 0x%08lx\n", fStatusWrite);
|
TRACE_ALWAYS("device status error 0x%08lx\n", fStatusWrite);
|
||||||
result = gUSBModule->clear_feature(fWriteEndpoint,
|
result = gUSBModule->clear_feature(fWriteEndpoint,
|
||||||
USB_FEATURE_ENDPOINT_HALT);
|
USB_FEATURE_ENDPOINT_HALT);
|
||||||
@@ -318,6 +348,22 @@ ECMDevice::Control(uint32 op, void *buffer, size_t length)
|
|||||||
*(uint32 *)buffer = fMaxSegmentSize;
|
*(uint32 *)buffer = fMaxSegmentSize;
|
||||||
return B_OK;
|
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:
|
default:
|
||||||
TRACE_ALWAYS("unsupported ioctl %lu\n", op);
|
TRACE_ALWAYS("unsupported ioctl %lu\n", op);
|
||||||
}
|
}
|
||||||
@@ -367,7 +413,7 @@ ECMDevice::_ReadCallback(void *cookie, int32 status, void *data,
|
|||||||
ECMDevice *device = (ECMDevice *)cookie;
|
ECMDevice *device = (ECMDevice *)cookie;
|
||||||
device->fActualLengthRead = actualLength;
|
device->fActualLengthRead = actualLength;
|
||||||
device->fStatusRead = status;
|
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;
|
ECMDevice *device = (ECMDevice *)cookie;
|
||||||
device->fActualLengthWrite = actualLength;
|
device->fActualLengthWrite = actualLength;
|
||||||
device->fStatusWrite = status;
|
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);
|
void *data, uint32 actualLength);
|
||||||
static void _WriteCallback(void *cookie, int32 status,
|
static void _WriteCallback(void *cookie, int32 status,
|
||||||
void *data, uint32 actualLength);
|
void *data, uint32 actualLength);
|
||||||
|
static void _NotifyCallback(void *cookie, int32 status,
|
||||||
|
void *data, uint32 actualLength);
|
||||||
|
|
||||||
status_t _ReadMACAddress();
|
status_t _ReadMACAddress();
|
||||||
|
|
||||||
|
// state tracking
|
||||||
status_t fStatus;
|
status_t fStatus;
|
||||||
bool fOpen;
|
bool fOpen;
|
||||||
bool fRemoved;
|
bool fRemoved;
|
||||||
usb_device fDevice;
|
usb_device fDevice;
|
||||||
|
|
||||||
uint8 fMACAddress[6];
|
// interface and device infos
|
||||||
|
|
||||||
uint8 fControlInterfaceIndex;
|
uint8 fControlInterfaceIndex;
|
||||||
uint8 fDataInterfaceIndex;
|
uint8 fDataInterfaceIndex;
|
||||||
uint8 fMACAddressIndex;
|
uint8 fMACAddressIndex;
|
||||||
uint16 fMaxSegmentSize;
|
uint16 fMaxSegmentSize;
|
||||||
|
|
||||||
usb_pipe fControlEndpoint;
|
// pipes for notifications and data io
|
||||||
|
usb_pipe fNotifyEndpoint;
|
||||||
usb_pipe fReadEndpoint;
|
usb_pipe fReadEndpoint;
|
||||||
usb_pipe fWriteEndpoint;
|
usb_pipe fWriteEndpoint;
|
||||||
|
|
||||||
|
// data stores for async usb transfers
|
||||||
uint32 fActualLengthRead;
|
uint32 fActualLengthRead;
|
||||||
uint32 fActualLengthWrite;
|
uint32 fActualLengthWrite;
|
||||||
int32 fStatusRead;
|
int32 fStatusRead;
|
||||||
int32 fStatusWrite;
|
int32 fStatusWrite;
|
||||||
sem_id fNotifyRead;
|
sem_id fNotifyReadSem;
|
||||||
sem_id fNotifyWrite;
|
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_
|
#endif //_USB_ECM_DEVICE_H_
|
||||||
|
|||||||
Reference in New Issue
Block a user