1/*
2	Driver for USB Ethernet Control Model devices
3	Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
4	Distributed under the terms of the MIT license.
5*/
6#include <ether_driver.h>
7#include <net/if_media.h>
8#include <string.h>
9#include <stdlib.h>
10
11#include "ECMDevice.h"
12#include "Driver.h"
13
14ECMDevice::ECMDevice(usb_device device)
15	:	fStatus(B_ERROR),
16		fOpen(false),
17		fRemoved(false),
18		fInsideNotify(0),
19		fDevice(device),
20		fControlInterfaceIndex(0),
21		fDataInterfaceIndex(0),
22		fMACAddressIndex(0),
23		fMaxSegmentSize(0),
24		fNotifyEndpoint(0),
25		fReadEndpoint(0),
26		fWriteEndpoint(0),
27		fNotifyReadSem(-1),
28		fNotifyWriteSem(-1),
29		fNotifyBuffer(NULL),
30		fNotifyBufferLength(0),
31		fLinkStateChangeSem(-1),
32		fHasConnection(false),
33		fDownstreamSpeed(0),
34		fUpstreamSpeed(0)
35{
36	const usb_device_descriptor *deviceDescriptor
37		= gUSBModule->get_device_descriptor(device);
38
39	if (deviceDescriptor == NULL) {
40		TRACE_ALWAYS("failed to get device descriptor\n");
41		return;
42	}
43
44	fVendorID = deviceDescriptor->vendor_id;
45	fProductID = deviceDescriptor->product_id;
46
47	fNotifyBufferLength = 64;
48	fNotifyBuffer = (uint8 *)malloc(fNotifyBufferLength);
49	if (fNotifyBuffer == NULL) {
50		TRACE_ALWAYS("out of memory for notify buffer allocation\n");
51		return;
52	}
53
54	fNotifyReadSem = create_sem(0, DRIVER_NAME"_notify_read");
55	if (fNotifyReadSem < B_OK) {
56		TRACE_ALWAYS("failed to create read notify sem\n");
57		return;
58	}
59
60	fNotifyWriteSem = create_sem(0, DRIVER_NAME"_notify_write");
61	if (fNotifyWriteSem < B_OK) {
62		TRACE_ALWAYS("failed to create write notify sem\n");
63		return;
64	}
65
66	if (_SetupDevice() != B_OK) {
67		TRACE_ALWAYS("failed to setup device\n");
68		return;
69	}
70
71	if (_ReadMACAddress(fDevice, fMACAddress) != B_OK) {
72		TRACE_ALWAYS("failed to read mac address\n");
73		return;
74	}
75
76	fStatus = B_OK;
77}
78
79
80ECMDevice::~ECMDevice()
81{
82	if (fNotifyReadSem >= B_OK)
83		delete_sem(fNotifyReadSem);
84	if (fNotifyWriteSem >= B_OK)
85		delete_sem(fNotifyWriteSem);
86
87	if (!fRemoved)
88		gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
89
90	free(fNotifyBuffer);
91}
92
93
94status_t
95ECMDevice::Open()
96{
97	if (fOpen)
98		return B_BUSY;
99	if (fRemoved)
100		return B_ERROR;
101
102	// reset the device by switching the data interface to the disabled first
103	// interface and then enable it by setting the second actual data interface
104	const usb_configuration_info *config
105		= gUSBModule->get_configuration(fDevice);
106
107	gUSBModule->set_alt_interface(fDevice,
108		&config->interface[fDataInterfaceIndex].alt[0]);
109
110	// update to the changed config
111	config = gUSBModule->get_configuration(fDevice);
112	gUSBModule->set_alt_interface(fDevice,
113		&config->interface[fDataInterfaceIndex].alt[1]);
114	gUSBModule->set_alt_interface(fDevice,
115		&config->interface[fControlInterfaceIndex].alt[0]);
116
117	// update again
118	config = gUSBModule->get_configuration(fDevice);
119	usb_interface_info *interface = config->interface[fDataInterfaceIndex].active;
120	if (interface->endpoint_count < 2) {
121		TRACE_ALWAYS("setting the data alternate interface failed\n");
122		return B_ERROR;
123	}
124
125	if (!(interface->endpoint[0].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN))
126		fWriteEndpoint = interface->endpoint[0].handle;
127	else
128		fReadEndpoint = interface->endpoint[0].handle;
129
130	if (interface->endpoint[1].descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
131		fReadEndpoint = interface->endpoint[1].handle;
132	else
133		fWriteEndpoint = interface->endpoint[1].handle;
134
135	if (fReadEndpoint == 0 || fWriteEndpoint == 0) {
136		TRACE_ALWAYS("no read and write endpoints found\n");
137		return B_ERROR;
138	}
139
140	if (gUSBModule->queue_interrupt(fNotifyEndpoint, fNotifyBuffer,
141		fNotifyBufferLength, _NotifyCallback, this) != B_OK) {
142		// we cannot use notifications - hardcode to active connection
143		fHasConnection = true;
144		fDownstreamSpeed = 1000 * 1000 * 10; // 10Mbps
145		fUpstreamSpeed = 1000 * 1000 * 10; // 10Mbps
146	}
147
148	// the device should now be ready
149	fOpen = true;
150	return B_OK;
151}
152
153
154status_t
155ECMDevice::Close()
156{
157	if (fRemoved) {
158		fOpen = false;
159		return B_OK;
160	}
161
162	gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
163	gUSBModule->cancel_queued_transfers(fReadEndpoint);
164	gUSBModule->cancel_queued_transfers(fWriteEndpoint);
165
166	// put the device into non-connected mode again by switching the data
167	// interface to the disabled alternate
168	const usb_configuration_info *config
169		= gUSBModule->get_configuration(fDevice);
170
171	gUSBModule->set_alt_interface(fDevice,
172		&config->interface[fDataInterfaceIndex].alt[0]);
173
174	fOpen = false;
175	return B_OK;
176}
177
178
179status_t
180ECMDevice::Free()
181{
182	return B_OK;
183}
184
185
186status_t
187ECMDevice::Read(uint8 *buffer, size_t *numBytes)
188{
189	if (fRemoved) {
190		*numBytes = 0;
191		return B_DEVICE_NOT_FOUND;
192	}
193
194	status_t result = gUSBModule->queue_bulk(fReadEndpoint, buffer, *numBytes,
195		_ReadCallback, this);
196	if (result != B_OK) {
197		*numBytes = 0;
198		return result;
199	}
200
201	result = acquire_sem_etc(fNotifyReadSem, 1, B_CAN_INTERRUPT, 0);
202	if (result < B_OK) {
203		*numBytes = 0;
204		return result;
205	}
206
207	if (fStatusRead != B_OK && fStatusRead != B_CANCELED && !fRemoved) {
208		TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusRead);
209		result = gUSBModule->clear_feature(fReadEndpoint,
210			USB_FEATURE_ENDPOINT_HALT);
211		if (result != B_OK) {
212			TRACE_ALWAYS("failed to clear halt state on read\n");
213			*numBytes = 0;
214			return result;
215		}
216	}
217
218	*numBytes = fActualLengthRead;
219	return B_OK;
220}
221
222
223status_t
224ECMDevice::Write(const uint8 *buffer, size_t *numBytes)
225{
226	if (fRemoved) {
227		*numBytes = 0;
228		return B_DEVICE_NOT_FOUND;
229	}
230
231	status_t result = gUSBModule->queue_bulk(fWriteEndpoint, (uint8 *)buffer,
232		*numBytes, _WriteCallback, this);
233	if (result != B_OK) {
234		*numBytes = 0;
235		return result;
236	}
237
238	result = acquire_sem_etc(fNotifyWriteSem, 1, B_CAN_INTERRUPT, 0);
239	if (result < B_OK) {
240		*numBytes = 0;
241		return result;
242	}
243
244	if (fStatusWrite != B_OK && fStatusWrite != B_CANCELED && !fRemoved) {
245		TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", fStatusWrite);
246		result = gUSBModule->clear_feature(fWriteEndpoint,
247			USB_FEATURE_ENDPOINT_HALT);
248		if (result != B_OK) {
249			TRACE_ALWAYS("failed to clear halt state on write\n");
250			*numBytes = 0;
251			return result;
252		}
253	}
254
255	*numBytes = fActualLengthWrite;
256	return B_OK;
257}
258
259
260status_t
261ECMDevice::Control(uint32 op, void *buffer, size_t length)
262{
263	switch (op) {
264		case ETHER_INIT:
265			return B_OK;
266
267		case ETHER_GETADDR:
268			memcpy(buffer, &fMACAddress, sizeof(fMACAddress));
269			return B_OK;
270
271		case ETHER_GETFRAMESIZE:
272			*(uint32 *)buffer = fMaxSegmentSize;
273			return B_OK;
274
275		case ETHER_SET_LINK_STATE_SEM:
276			fLinkStateChangeSem = *(sem_id *)buffer;
277			return B_OK;
278
279		case ETHER_GET_LINK_STATE:
280		{
281			ether_link_state *state = (ether_link_state *)buffer;
282			state->media = IFM_ETHER | IFM_FULL_DUPLEX
283				| (fHasConnection ? IFM_ACTIVE : 0);
284			state->quality = 1000;
285			state->speed = fDownstreamSpeed;
286			return B_OK;
287		}
288
289		default:
290			TRACE_ALWAYS("unsupported ioctl %" B_PRIu32 "\n", op);
291	}
292
293	return B_DEV_INVALID_IOCTL;
294}
295
296
297void
298ECMDevice::Removed()
299{
300	fRemoved = true;
301	fHasConnection = false;
302	fDownstreamSpeed = fUpstreamSpeed = 0;
303
304	// the notify hook is different from the read and write hooks as it does
305	// itself schedule traffic (while the other hooks only release a semaphore
306	// to notify another thread which in turn safly checks for the removed
307	// case) - so we must ensure that we are not inside the notify hook anymore
308	// before returning, as we would otherwise violate the promise not to use
309	// any of the pipes after returning from the removed hook
310	while (atomic_add(&fInsideNotify, 0) != 0)
311		snooze(100);
312
313	gUSBModule->cancel_queued_transfers(fNotifyEndpoint);
314	gUSBModule->cancel_queued_transfers(fReadEndpoint);
315	gUSBModule->cancel_queued_transfers(fWriteEndpoint);
316
317	if (fLinkStateChangeSem >= B_OK)
318		release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
319}
320
321
322status_t
323ECMDevice::CompareAndReattach(usb_device device)
324{
325	const usb_device_descriptor *deviceDescriptor
326		= gUSBModule->get_device_descriptor(device);
327
328	if (deviceDescriptor == NULL) {
329		TRACE_ALWAYS("failed to get device descriptor\n");
330		return B_ERROR;
331	}
332
333	if (deviceDescriptor->vendor_id != fVendorID
334		&& deviceDescriptor->product_id != fProductID) {
335		// this certainly isn't the same device
336		return B_BAD_VALUE;
337	}
338
339	// this might be the same device that was replugged - read the MAC address
340	// (which should be at the same index) to make sure
341	uint8 macBuffer[6];
342	if (_ReadMACAddress(device, macBuffer) != B_OK
343		|| memcmp(macBuffer, fMACAddress, sizeof(macBuffer)) != 0) {
344		// reading the MAC address failed or they are not the same
345		return B_BAD_VALUE;
346	}
347
348	// this is the same device that was replugged - clear the removed state,
349	// re-setup the endpoints and transfers and open the device if it was
350	// previously opened
351	fDevice = device;
352	fRemoved = false;
353	status_t result = _SetupDevice();
354	if (result != B_OK) {
355		fRemoved = true;
356		return result;
357	}
358
359	// in case notifications do not work we will have a hardcoded connection
360	// need to register that and notify the network stack ourselfs if this is
361	// the case as the open will not result in a corresponding notification
362	bool noNotifications = fHasConnection;
363
364	if (fOpen) {
365		fOpen = false;
366		result = Open();
367		if (result == B_OK && noNotifications && fLinkStateChangeSem >= B_OK)
368			release_sem_etc(fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
369	}
370
371	return B_OK;
372}
373
374
375status_t
376ECMDevice::_SetupDevice()
377{
378	const usb_device_descriptor *deviceDescriptor
379                = gUSBModule->get_device_descriptor(fDevice);
380
381	if (deviceDescriptor == NULL) {
382		TRACE_ALWAYS("failed to get device descriptor\n");
383		return B_ERROR;
384	}
385
386	uint8 controlIndex = 0;
387	uint8 dataIndex = 0;
388	bool foundUnionDescriptor = false;
389	bool foundEthernetDescriptor = false;
390	bool found = false;
391	const usb_configuration_info *config = NULL;
392	for (int i = 0; i < deviceDescriptor->num_configurations && !found; i++) {
393		config = gUSBModule->get_nth_configuration(fDevice, i);
394		if (config == NULL)
395			continue;
396
397		for (size_t j = 0; j < config->interface_count && !found; j++) {
398			const usb_interface_info *interface = config->interface[j].active;
399			usb_interface_descriptor *descriptor = interface->descr;
400			if (descriptor->interface_class != USB_INTERFACE_CLASS_CDC
401				|| descriptor->interface_subclass != USB_INTERFACE_SUBCLASS_ECM
402				|| interface->generic_count == 0) {
403				continue;
404			}
405
406			// try to find and interpret the union and ethernet functional
407			// descriptors
408			foundUnionDescriptor = foundEthernetDescriptor = false;
409			for (size_t k = 0; k < interface->generic_count; k++) {
410				usb_generic_descriptor *generic = &interface->generic[k]->generic;
411				if (generic->length >= 5
412					&& generic->data[0] == FUNCTIONAL_SUBTYPE_UNION) {
413					controlIndex = generic->data[1];
414					dataIndex = generic->data[2];
415					foundUnionDescriptor = true;
416				} else if (generic->length >= sizeof(ethernet_functional_descriptor)
417					&& generic->data[0] == FUNCTIONAL_SUBTYPE_ETHERNET) {
418					ethernet_functional_descriptor *ethernet
419						= (ethernet_functional_descriptor *)generic->data;
420					fMACAddressIndex = ethernet->mac_address_index;
421					fMaxSegmentSize = ethernet->max_segment_size;
422					foundEthernetDescriptor = true;
423				}
424
425				if (foundUnionDescriptor && foundEthernetDescriptor) {
426					found = true;
427					break;
428				}
429			}
430		}
431	}
432
433	if (!foundUnionDescriptor) {
434		TRACE_ALWAYS("did not find a union descriptor\n");
435		return B_ERROR;
436	}
437
438	if (!foundEthernetDescriptor) {
439		TRACE_ALWAYS("did not find an ethernet descriptor\n");
440		return B_ERROR;
441	}
442
443	// set the current configuration
444	gUSBModule->set_configuration(fDevice, config);
445	if (controlIndex >= config->interface_count) {
446		TRACE_ALWAYS("control interface index invalid\n");
447		return B_ERROR;
448	}
449
450	// check that the indicated control interface fits our needs
451	usb_interface_info *interface = config->interface[controlIndex].active;
452	usb_interface_descriptor *descriptor = interface->descr;
453	if ((descriptor->interface_class != USB_INTERFACE_CLASS_CDC
454		|| descriptor->interface_subclass != USB_INTERFACE_SUBCLASS_ECM)
455		|| interface->endpoint_count == 0) {
456		TRACE_ALWAYS("control interface invalid\n");
457		return B_ERROR;
458	}
459
460	fControlInterfaceIndex = controlIndex;
461	fNotifyEndpoint = interface->endpoint[0].handle;
462	fNotifyBufferLength = interface->endpoint[0].descr->max_packet_size;
463
464	if (dataIndex >= config->interface_count) {
465		TRACE_ALWAYS("data interface index invalid\n");
466		return B_ERROR;
467	}
468
469	// check that the indicated data interface fits our needs
470	if (config->interface[dataIndex].alt_count < 2) {
471		TRACE_ALWAYS("data interface does not provide two alternate interfaces\n");
472		return B_ERROR;
473	}
474
475	// alternate 0 is the disabled, endpoint-less default interface
476	interface = &config->interface[dataIndex].alt[1];
477	descriptor = interface->descr;
478	if (descriptor->interface_class != USB_INTERFACE_CLASS_CDC_DATA
479		|| interface->endpoint_count < 2) {
480		TRACE_ALWAYS("data interface invalid\n");
481		return B_ERROR;
482	}
483
484	fDataInterfaceIndex = dataIndex;
485	return B_OK;
486}
487
488
489status_t
490ECMDevice::_ReadMACAddress(usb_device device, uint8 *buffer)
491{
492	if (fMACAddressIndex == 0)
493		return B_BAD_VALUE;
494
495	size_t actualLength = 0;
496	size_t macStringLength = 26;
497	uint8 macString[macStringLength];
498	status_t result = gUSBModule->get_descriptor(device, USB_DESCRIPTOR_STRING,
499		fMACAddressIndex, 0, macString, macStringLength, &actualLength);
500	if (result != B_OK)
501		return result;
502
503	if (actualLength != macStringLength) {
504		TRACE_ALWAYS("did not retrieve full mac address\n");
505		return B_ERROR;
506	}
507
508	char macPart[3];
509	macPart[2] = 0;
510	for (int32 i = 0; i < 6; i++) {
511		macPart[0] = macString[2 + i * 4 + 0];
512		macPart[1] = macString[2 + i * 4 + 2];
513		buffer[i] = strtol(macPart, NULL, 16);
514	}
515
516	TRACE_ALWAYS("read mac address: %02x:%02x:%02x:%02x:%02x:%02x\n",
517		buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]);
518	return B_OK;
519}
520
521
522void
523ECMDevice::_ReadCallback(void *cookie, int32 status, void *data,
524	size_t actualLength)
525{
526	ECMDevice *device = (ECMDevice *)cookie;
527	device->fActualLengthRead = actualLength;
528	device->fStatusRead = status;
529	release_sem_etc(device->fNotifyReadSem, 1, B_DO_NOT_RESCHEDULE);
530}
531
532
533void
534ECMDevice::_WriteCallback(void *cookie, int32 status, void *data,
535	size_t actualLength)
536{
537	ECMDevice *device = (ECMDevice *)cookie;
538	device->fActualLengthWrite = actualLength;
539	device->fStatusWrite = status;
540	release_sem_etc(device->fNotifyWriteSem, 1, B_DO_NOT_RESCHEDULE);
541}
542
543
544void
545ECMDevice::_NotifyCallback(void *cookie, int32 status, void *data,
546	size_t actualLength)
547{
548	ECMDevice *device = (ECMDevice *)cookie;
549	atomic_add(&device->fInsideNotify, 1);
550	if (status == B_CANCELED || device->fRemoved) {
551		atomic_add(&device->fInsideNotify, -1);
552		return;
553	}
554
555	if (status == B_OK && actualLength >= sizeof(cdc_notification)) {
556		bool linkStateChange = false;
557		cdc_notification *notification
558			= (cdc_notification *)device->fNotifyBuffer;
559
560		switch (notification->notification_code) {
561			case CDC_NOTIFY_NETWORK_CONNECTION:
562				TRACE("connection state change to %d\n", notification->value);
563				device->fHasConnection = notification->value > 0;
564				linkStateChange = true;
565				break;
566
567			case CDC_NOTIFY_CONNECTION_SPEED_CHANGE:
568			{
569				if (notification->data_length < sizeof(cdc_connection_speed)
570					|| actualLength < sizeof(cdc_notification)
571					+ sizeof(cdc_connection_speed)) {
572					TRACE_ALWAYS("not enough data in connection speed change\n");
573					break;
574				}
575
576				cdc_connection_speed *speed;
577				speed = (cdc_connection_speed *)&notification->data[0];
578				device->fUpstreamSpeed = speed->upstream_speed;
579				device->fDownstreamSpeed = speed->downstream_speed;
580				device->fHasConnection = true;
581				TRACE("connection speed change to %" B_PRId32 "/%" B_PRId32 "\n",
582					speed->downstream_speed, speed->upstream_speed);
583				linkStateChange = true;
584				break;
585			}
586
587			default:
588				TRACE_ALWAYS("unsupported notification 0x%02x\n",
589					notification->notification_code);
590				break;
591		}
592
593		if (linkStateChange && device->fLinkStateChangeSem >= B_OK)
594			release_sem_etc(device->fLinkStateChangeSem, 1, B_DO_NOT_RESCHEDULE);
595	}
596
597	if (status != B_OK) {
598		TRACE_ALWAYS("device status error 0x%08" B_PRIx32 "\n", status);
599		if (gUSBModule->clear_feature(device->fNotifyEndpoint,
600			USB_FEATURE_ENDPOINT_HALT) != B_OK)
601			TRACE_ALWAYS("failed to clear halt state in notify hook\n");
602	}
603
604	// schedule next notification buffer
605	gUSBModule->queue_interrupt(device->fNotifyEndpoint, device->fNotifyBuffer,
606		device->fNotifyBufferLength, _NotifyCallback, device);
607	atomic_add(&device->fInsideNotify, -1);
608}
609