1/*
2 *	Davicom DM9601 USB 1.1 Ethernet Driver.
3 *	Copyright (c) 2008, 2011 Siarzhuk Zharski <imker@gmx.li>
4 *	Copyright (c) 2009 Adrien Destugues <pulkomandy@gmail.com>
5 *	Distributed under the terms of the MIT license.
6 *
7 *	Heavily based on code of the
8 *	Driver for USB Ethernet Control Model devices
9 *	Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
10 *	Distributed under the terms of the MIT license.
11 */
12
13
14#include "Driver.h"
15
16#include <stdio.h>
17
18#include <lock.h>
19#include <util/AutoLock.h>
20
21#include "DavicomDevice.h"
22#include "Settings.h"
23
24
25int32 api_version = B_CUR_DRIVER_API_VERSION;
26static const char *sDeviceBaseName = "net/usb_davicom/";
27DavicomDevice *gDavicomDevices[MAX_DEVICES];
28char *gDeviceNames[MAX_DEVICES + 1];
29usb_module_info *gUSBModule = NULL;
30mutex gDriverLock;
31
32
33// IMPORTANT: keep entries sorted by ids to let the
34// binary search lookup procedure work correctly !!!
35DeviceInfo gSupportedDevices[] = {
36	{ { 0x01e1, 0x9601 }, "Noname DM9601" },
37	{ { 0x07aa, 0x9601 }, "Corega FEther USB-TXC" },
38	{ { 0x0a46, 0x0268 }, "ShanTou ST268 USB NIC" },
39	{ { 0x0a46, 0x6688 }, "ZT6688 USB NIC" },
40	{ { 0x0a46, 0x8515 }, "ADMtek ADM8515 USB NIC" },
41	{ { 0x0a46, 0x9000 }, "DM9000E" },
42	{ { 0x0a46, 0x9601 }, "Davicom DM9601" },
43	{ { 0x0a47, 0x9601 }, "Hirose USB-100" },
44	{ { 0x0fe6, 0x8101 }, "Sunrising SR9600" },
45	{ { 0x0fe6, 0x9700 }, "Kontron DM9601" }
46};
47
48
49DavicomDevice *
50lookup_and_create_device(usb_device device)
51{
52	const usb_device_descriptor *deviceDescriptor
53		= gUSBModule->get_device_descriptor(device);
54
55	if (deviceDescriptor == NULL) {
56		TRACE_ALWAYS("Error of getting USB device descriptor.\n");
57		return NULL;
58	}
59
60	TRACE("trying %#06x:%#06x.\n",
61			deviceDescriptor->vendor_id, deviceDescriptor->product_id);
62
63	// use binary search to lookup device in table
64	uint32 id = deviceDescriptor->vendor_id << 16
65					| deviceDescriptor->product_id;
66	int left  = -1;
67	int right = B_COUNT_OF(gSupportedDevices);
68	while ((right - left) > 1) {
69		int i = (left + right) / 2;
70		((gSupportedDevices[i].Key() < id) ? left : right) = i;
71	}
72
73	if (gSupportedDevices[right].Key() == id)
74		return new DavicomDevice(device, gSupportedDevices[right]);
75
76	TRACE_ALWAYS("Search for %#x failed %d-%d.\n", id, left, right);
77	return NULL;
78}
79
80
81status_t
82usb_davicom_device_added(usb_device device, void **cookie)
83{
84	*cookie = NULL;
85
86	MutexLocker lock(gDriverLock); // released on exit
87
88	// check if this is a replug of an existing device first
89	for (int32 i = 0; i < MAX_DEVICES; i++) {
90		if (gDavicomDevices[i] == NULL)
91			continue;
92
93		if (gDavicomDevices[i]->CompareAndReattach(device) != B_OK)
94			continue;
95
96		TRACE("The device is plugged back. Use entry at %ld.\n", i);
97		*cookie = gDavicomDevices[i];
98		return B_OK;
99	}
100
101	// no such device yet, create a new one
102	DavicomDevice *davicomDevice = lookup_and_create_device(device);
103	if (davicomDevice == 0) {
104		return ENODEV;
105	}
106
107	status_t status = davicomDevice->InitCheck();
108	if (status < B_OK) {
109		delete davicomDevice;
110		return status;
111	}
112
113	status = davicomDevice->SetupDevice(false);
114	if (status < B_OK) {
115		delete davicomDevice;
116		return status;
117	}
118
119	for (int32 i = 0; i < MAX_DEVICES; i++) {
120		if (gDavicomDevices[i] != NULL)
121			continue;
122
123		gDavicomDevices[i] = davicomDevice;
124		*cookie = davicomDevice;
125
126		TRACE("New device is added at %ld.\n", i);
127		return B_OK;
128	}
129
130	// no space for the device
131	TRACE_ALWAYS("Error: no more device entries availble.\n");
132
133	delete davicomDevice;
134	return B_ERROR;
135}
136
137
138status_t
139usb_davicom_device_removed(void *cookie)
140{
141	MutexLocker lock(gDriverLock); // released on exit
142
143	DavicomDevice *device = (DavicomDevice *)cookie;
144	for (int32 i = 0; i < MAX_DEVICES; i++) {
145		if (gDavicomDevices[i] == device) {
146			if (device->IsOpen()) {
147				// the device will be deleted upon being freed
148				device->Removed();
149			} else {
150				gDavicomDevices[i] = NULL;
151				delete device;
152				TRACE("Device at %ld deleted.\n", i);
153			}
154			break;
155		}
156	}
157
158	return B_OK;
159}
160
161
162// #pragma mark -
163
164
165status_t
166init_hardware()
167{
168	return B_OK;
169}
170
171
172status_t
173init_driver()
174{
175	status_t status = get_module(B_USB_MODULE_NAME,
176		(module_info **)&gUSBModule);
177	if (status < B_OK)
178		return status;
179
180	load_settings();
181
182	TRACE_ALWAYS("%s\n", kVersion);
183
184	for (int32 i = 0; i < MAX_DEVICES; i++)
185		gDavicomDevices[i] = NULL;
186
187	gDeviceNames[0] = NULL;
188	mutex_init(&gDriverLock, DRIVER_NAME"_devices");
189
190	static usb_notify_hooks notifyHooks = {
191		&usb_davicom_device_added,
192		&usb_davicom_device_removed
193	};
194
195	const size_t count = B_COUNT_OF(gSupportedDevices);
196	static usb_support_descriptor sDescriptors[count] = {{ 0 }};
197
198	for (size_t i = 0; i < count; i++) {
199		sDescriptors[i].vendor  = gSupportedDevices[i].VendorId();
200		sDescriptors[i].product = gSupportedDevices[i].ProductId();
201	}
202
203	gUSBModule->register_driver(DRIVER_NAME, sDescriptors, count, NULL);
204	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
205	return B_OK;
206}
207
208
209void
210uninit_driver()
211{
212	gUSBModule->uninstall_notify(DRIVER_NAME);
213	mutex_lock(&gDriverLock);
214
215	for (int32 i = 0; i < MAX_DEVICES; i++) {
216		if (gDavicomDevices[i]) {
217			delete gDavicomDevices[i];
218			gDavicomDevices[i] = NULL;
219		}
220	}
221
222	for (int32 i = 0; gDeviceNames[i]; i++) {
223		free(gDeviceNames[i]);
224		gDeviceNames[i] = NULL;
225	}
226
227	mutex_destroy(&gDriverLock);
228	put_module(B_USB_MODULE_NAME);
229
230	release_settings();
231}
232
233
234static status_t
235usb_davicom_open(const char *name, uint32 flags, void **cookie)
236{
237	MutexLocker lock(gDriverLock); // released on exit
238
239	*cookie = NULL;
240	status_t status = ENODEV;
241	int32 index = strtol(name + strlen(sDeviceBaseName), NULL, 10);
242	if (index >= 0 && index < MAX_DEVICES && gDavicomDevices[index]) {
243		status = gDavicomDevices[index]->Open(flags);
244		*cookie = gDavicomDevices[index];
245	}
246
247	return status;
248}
249
250
251static status_t
252usb_davicom_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
253{
254	DavicomDevice *device = (DavicomDevice *)cookie;
255	return device->Read((uint8 *)buffer, numBytes);
256}
257
258
259static status_t
260usb_davicom_write(void *cookie, off_t position, const void *buffer,
261	size_t *numBytes)
262{
263	DavicomDevice *device = (DavicomDevice *)cookie;
264	return device->Write((const uint8 *)buffer, numBytes);
265}
266
267
268static status_t
269usb_davicom_control(void *cookie, uint32 op, void *buffer, size_t length)
270{
271	DavicomDevice *device = (DavicomDevice *)cookie;
272	return device->Control(op, buffer, length);
273}
274
275
276static status_t
277usb_davicom_close(void *cookie)
278{
279	DavicomDevice *device = (DavicomDevice *)cookie;
280	return device->Close();
281}
282
283
284static status_t
285usb_davicom_free(void *cookie)
286{
287	DavicomDevice *device = (DavicomDevice *)cookie;
288
289	MutexLocker lock(gDriverLock); // released on exit
290
291	status_t status = device->Free();
292	for (int32 i = 0; i < MAX_DEVICES; i++) {
293		if (gDavicomDevices[i] == device) {
294			// the device is removed already but as it was open the
295			// removed hook has not deleted the object
296			gDavicomDevices[i] = NULL;
297			delete device;
298			TRACE("Device at %ld deleted.\n", i);
299			break;
300		}
301	}
302
303	return status;
304}
305
306
307const char **
308publish_devices()
309{
310	for (int32 i = 0; gDeviceNames[i]; i++) {
311		free(gDeviceNames[i]);
312		gDeviceNames[i] = NULL;
313	}
314
315	MutexLocker lock(gDriverLock); // released on exit
316
317	int32 deviceCount = 0;
318	for (int32 i = 0; i < MAX_DEVICES; i++) {
319		if (gDavicomDevices[i] == NULL)
320			continue;
321
322		gDeviceNames[deviceCount] = (char *)malloc(strlen(sDeviceBaseName) + 4);
323		if (gDeviceNames[deviceCount]) {
324			sprintf(gDeviceNames[deviceCount], "%s%" B_PRId32, sDeviceBaseName, i);
325			TRACE("publishing %s\n", gDeviceNames[deviceCount]);
326			deviceCount++;
327		} else
328			TRACE_ALWAYS("Error: out of memory during allocating dev.name.\n");
329	}
330
331	gDeviceNames[deviceCount] = NULL;
332	return (const char **)&gDeviceNames[0];
333}
334
335
336device_hooks *
337find_device(const char *name)
338{
339	static device_hooks deviceHooks = {
340		usb_davicom_open,
341		usb_davicom_close,
342		usb_davicom_free,
343		usb_davicom_control,
344		usb_davicom_read,
345		usb_davicom_write,
346		NULL,				// select
347		NULL				// deselect
348	};
349
350	return &deviceHooks;
351}
352
353