1/*
2 * Pegasus BeOS Driver
3 *
4 * Copyright 2006, Haiku, Inc. All Rights Reserved.
5 * Distributed under the terms of the MIT License.
6 *
7 * Authors:
8 *		J��r��me Duval
9 */
10
11#include <inttypes.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include "driver.h"
16#include "usbdevs.h"
17
18typedef struct driver_cookie {
19	struct driver_cookie	*next;
20	pegasus_dev			*device;
21} driver_cookie;
22
23int32 api_version = B_CUR_DRIVER_API_VERSION;
24
25static status_t pegasus_device_added(const usb_device dev, void **cookie);
26static status_t pegasus_device_removed(void *cookie);
27
28static int sDeviceNumber = 0;
29static const char *kBaseName = "net/pegasus/";
30
31static const char *kDriverName = DRIVER_NAME;
32
33usb_module_info *usb;
34
35
36usb_support_descriptor supported_devices[] = {
37 { 0, 0, 0, USB_VENDOR_3COM,		USB_PRODUCT_3COM_3C460B},
38 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX1},
39 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX2},
40 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_UFE1000},
41 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX4},
42 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX5},
43 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX6},
44 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX7},
45 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX8},
46 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX9},
47 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX10},
48 { 0, 0, 0, USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_DSB650TX_PNA},
49 { 0, 0, 0, USB_VENDOR_ACCTON,		USB_PRODUCT_ACCTON_USB320_EC},
50 { 0, 0, 0, USB_VENDOR_ACCTON,		USB_PRODUCT_ACCTON_SS1001},
51 { 0, 0, 0, USB_VENDOR_ADMTEK,		USB_PRODUCT_ADMTEK_PEGASUS},
52 { 0, 0, 0, USB_VENDOR_ADMTEK,		USB_PRODUCT_ADMTEK_PEGASUSII},
53 { 0, 0, 0, USB_VENDOR_ADMTEK,		USB_PRODUCT_ADMTEK_PEGASUSII_2},
54 { 0, 0, 0, USB_VENDOR_ADMTEK,		USB_PRODUCT_ADMTEK_PEGASUSII_3},
55 { 0, 0, 0, USB_VENDOR_BELKIN,		USB_PRODUCT_BELKIN_USB2LAN},
56 { 0, 0, 0, USB_VENDOR_BILLIONTON,	USB_PRODUCT_BILLIONTON_USB100},
57 { 0, 0, 0, USB_VENDOR_BILLIONTON,	USB_PRODUCT_BILLIONTON_USBLP100},
58 { 0, 0, 0, USB_VENDOR_BILLIONTON,	USB_PRODUCT_BILLIONTON_USBEL100},
59 { 0, 0, 0, USB_VENDOR_BILLIONTON,	USB_PRODUCT_BILLIONTON_USBE100},
60 { 0, 0, 0, USB_VENDOR_COREGA,		USB_PRODUCT_COREGA_FETHER_USB_TX},
61 { 0, 0, 0, USB_VENDOR_COREGA,		USB_PRODUCT_COREGA_FETHER_USB_TXS},
62 { 0, 0, 0, USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX4},
63 { 0, 0, 0, USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX1},
64 { 0, 0, 0, USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX},
65 { 0, 0, 0, USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX_PNA},
66 { 0, 0, 0, USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX3},
67 { 0, 0, 0, USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX2},
68 { 0, 0, 0, USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650},
69 { 0, 0, 0, USB_VENDOR_ELECOM,		USB_PRODUCT_ELECOM_LDUSBTX0},
70 { 0, 0, 0, USB_VENDOR_ELECOM,		USB_PRODUCT_ELECOM_LDUSBTX1},
71 { 0, 0, 0, USB_VENDOR_ELECOM,		USB_PRODUCT_ELECOM_LDUSBTX2},
72 { 0, 0, 0, USB_VENDOR_ELECOM,		USB_PRODUCT_ELECOM_LDUSBTX3},
73 { 0, 0, 0, USB_VENDOR_ELECOM,		USB_PRODUCT_ELECOM_LDUSBLTX},
74 { 0, 0, 0, USB_VENDOR_ELSA,		USB_PRODUCT_ELSA_USB2ETHERNET},
75 { 0, 0, 0, USB_VENDOR_HAWKING,		USB_PRODUCT_HAWKING_UF100},
76 { 0, 0, 0, USB_VENDOR_HP,		USB_PRODUCT_HP_HN210E},
77 { 0, 0, 0, USB_VENDOR_IODATA,		USB_PRODUCT_IODATA_USBETTX},
78 { 0, 0, 0, USB_VENDOR_IODATA,		USB_PRODUCT_IODATA_USBETTXS},
79 { 0, 0, 0, USB_VENDOR_KINGSTON,	USB_PRODUCT_KINGSTON_KNU101TX},
80 { 0, 0, 0, USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB10TX1},
81 { 0, 0, 0, USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB10T},
82 { 0, 0, 0, USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB100TX},
83 { 0, 0, 0, USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB100H1},
84 { 0, 0, 0, USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB10TA},
85 { 0, 0, 0, USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB10TX2},
86 { 0, 0, 0, USB_VENDOR_MICROSOFT,	USB_PRODUCT_MICROSOFT_MN110},
87 { 0, 0, 0, USB_VENDOR_MELCO,		USB_PRODUCT_MELCO_LUATX1},
88 { 0, 0, 0, USB_VENDOR_MELCO,		USB_PRODUCT_MELCO_LUATX5},
89 { 0, 0, 0, USB_VENDOR_MELCO,		USB_PRODUCT_MELCO_LUA2TX5},
90 { 0, 0, 0, USB_VENDOR_NETGEAR,		USB_PRODUCT_NETGEAR_FA101},
91 { 0, 0, 0, USB_VENDOR_SIEMENS,		USB_PRODUCT_SIEMENS_SPEEDSTREAM},
92 { 0, 0, 0, USB_VENDOR_SMARTBRIDGES,	USB_PRODUCT_SMARTBRIDGES_SMARTNIC},
93 { 0, 0, 0, USB_VENDOR_SMC,		USB_PRODUCT_SMC_2202USB},
94 { 0, 0, 0, USB_VENDOR_SMC,		USB_PRODUCT_SMC_2206USB},
95 { 0, 0, 0, USB_VENDOR_SOHOWARE,	USB_PRODUCT_SOHOWARE_NUB100},
96};
97
98
99static const struct aue_type aue_devs[] = {
100 {{ USB_VENDOR_3COM,		USB_PRODUCT_3COM_3C460B},		 PII },
101 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX1},	  PNA|PII },
102 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX2},	  PII },
103 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_UFE1000},	  LSYS },
104 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX4},	  PNA },
105 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX5},	  PNA },
106 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX6},	  PII },
107 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX7},	  PII },
108 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX8},	  PII },
109 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX9},	  PNA },
110 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_XX10},	  0 },
111 {{ USB_VENDOR_ABOCOM,		USB_PRODUCT_ABOCOM_DSB650TX_PNA}, 0 },
112 {{ USB_VENDOR_ACCTON,		USB_PRODUCT_ACCTON_USB320_EC},	  0 },
113 {{ USB_VENDOR_ACCTON,		USB_PRODUCT_ACCTON_SS1001},	  PII },
114 {{ USB_VENDOR_ADMTEK,		USB_PRODUCT_ADMTEK_PEGASUS},	  PNA },
115 {{ USB_VENDOR_ADMTEK,		USB_PRODUCT_ADMTEK_PEGASUSII},	  PII },
116 {{ USB_VENDOR_ADMTEK,		USB_PRODUCT_ADMTEK_PEGASUSII_2},  PII },
117 {{ USB_VENDOR_ADMTEK,		USB_PRODUCT_ADMTEK_PEGASUSII_3},  PII },
118 {{ USB_VENDOR_BELKIN,		USB_PRODUCT_BELKIN_USB2LAN},	  PII },
119 {{ USB_VENDOR_BILLIONTON,	USB_PRODUCT_BILLIONTON_USB100},	  0 },
120 {{ USB_VENDOR_BILLIONTON,	USB_PRODUCT_BILLIONTON_USBLP100}, PNA },
121 {{ USB_VENDOR_BILLIONTON,	USB_PRODUCT_BILLIONTON_USBEL100}, 0 },
122 {{ USB_VENDOR_BILLIONTON,	USB_PRODUCT_BILLIONTON_USBE100},  PII },
123 {{ USB_VENDOR_COREGA,		USB_PRODUCT_COREGA_FETHER_USB_TX}, 0 },
124 {{ USB_VENDOR_COREGA,		USB_PRODUCT_COREGA_FETHER_USB_TXS},PII },
125 {{ USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX4},	  LSYS|PII },
126 {{ USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX1},	  LSYS },
127 {{ USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX},	  LSYS },
128 {{ USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX_PNA},  PNA },
129 {{ USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX3},	  LSYS|PII },
130 {{ USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650TX2},	  LSYS|PII },
131 {{ USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_DSB650},	  LSYS },
132 {{ USB_VENDOR_ELECOM,		USB_PRODUCT_ELECOM_LDUSBTX0},	  0 },
133 {{ USB_VENDOR_ELECOM,		USB_PRODUCT_ELECOM_LDUSBTX1},	  LSYS },
134 {{ USB_VENDOR_ELECOM,		USB_PRODUCT_ELECOM_LDUSBTX2},	  0 },
135 {{ USB_VENDOR_ELECOM,		USB_PRODUCT_ELECOM_LDUSBTX3},	  LSYS },
136 {{ USB_VENDOR_ELECOM,		USB_PRODUCT_ELECOM_LDUSBLTX},	  PII },
137 {{ USB_VENDOR_ELSA,		USB_PRODUCT_ELSA_USB2ETHERNET},	  0 },
138 {{ USB_VENDOR_HAWKING,		USB_PRODUCT_HAWKING_UF100},	   PII },
139 {{ USB_VENDOR_HP,			USB_PRODUCT_HP_HN210E},				PII },
140 {{ USB_VENDOR_IODATA,		0x092a /* USB_PRODUCT_IODATA_ETXUS2 */},		PII },
141 {{ USB_VENDOR_IODATA,		USB_PRODUCT_IODATA_USBETTX},	  0 },
142 {{ USB_VENDOR_IODATA,		USB_PRODUCT_IODATA_USBETTXS},	  PII },
143 {{ USB_VENDOR_KINGSTON,	USB_PRODUCT_KINGSTON_KNU101TX},   0 },
144 {{ USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB10TX1},	  LSYS|PII },
145 {{ USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB10T},	  LSYS },
146 {{ USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB100TX},	  LSYS },
147 {{ USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB100H1},	  LSYS|PNA },
148 {{ USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB10TA},	  LSYS },
149 {{ USB_VENDOR_LINKSYS,		USB_PRODUCT_LINKSYS_USB10TX2},	  LSYS|PII },
150 {{ USB_VENDOR_MICROSOFT,	USB_PRODUCT_MICROSOFT_MN110},	  PII },
151 {{ USB_VENDOR_MELCO,		USB_PRODUCT_MELCO_LUATX1},	  0 },
152 {{ USB_VENDOR_MELCO,		USB_PRODUCT_MELCO_LUATX5},	  0 },
153 {{ USB_VENDOR_MELCO,		USB_PRODUCT_MELCO_LUA2TX5},		PII },
154 {{ USB_VENDOR_NETGEAR,		USB_PRODUCT_NETGEAR_FA101},		PII },
155 {{ USB_VENDOR_SIEMENS,		USB_PRODUCT_SIEMENS_SPEEDSTREAM}, PII },
156 {{ USB_VENDOR_SMARTBRIDGES,	USB_PRODUCT_SMARTBRIDGES_SMARTNIC},PII },
157 {{ USB_VENDOR_SMC,			USB_PRODUCT_SMC_2202USB},		0 },
158 {{ USB_VENDOR_SMC,			USB_PRODUCT_SMC_2206USB},		PII },
159 {{ USB_VENDOR_SOHOWARE,	USB_PRODUCT_SOHOWARE_NUB100},	0 },
160};
161
162
163static status_t
164pegasus_checkdeviceinfo(pegasus_dev *dev)
165{
166	if (!dev || dev->cookieMagic != PEGASUS_COOKIE_MAGIC)
167		return EINVAL;
168
169	return B_OK;
170}
171
172
173pegasus_dev *
174create_device(const usb_device dev, const usb_interface_info *ii, uint16 ifno)
175{
176	pegasus_dev *device = NULL;
177	sem_id sem;
178
179	ASSERT(usb != NULL && dev != 0);
180
181	device = malloc(sizeof(pegasus_dev));
182	if (device == NULL)
183		return NULL;
184
185	memset(device, 0, sizeof(pegasus_dev));
186
187	device->sem_lock = sem = create_sem(1, DRIVER_NAME "_lock");
188	if (sem < B_OK) {
189		DPRINTF_ERR("create_sem() failed 0x%" B_PRIx32 "\n", sem);
190		free(device);
191		return NULL;
192	}
193
194	device->rx_sem = sem = create_sem(1, DRIVER_NAME"_receive");
195	if (sem < B_OK) {
196		DPRINTF_ERR("create_sem() failed 0x%" B_PRIx32 "\n", sem);
197		delete_sem(device->sem_lock);
198		free(device);
199		return NULL;
200	}
201	set_sem_owner(device->rx_sem, B_SYSTEM_TEAM);
202
203	device->rx_sem_cb = sem = create_sem(0, DRIVER_NAME"_receive_cb");
204	if (sem < B_OK) {
205		DPRINTF_ERR("create_sem() failed 0x%" B_PRIx32 "\n", sem);
206		delete_sem(device->rx_sem);
207		delete_sem(device->sem_lock);
208		free(device);
209		return NULL;
210	}
211	set_sem_owner(device->rx_sem_cb, B_SYSTEM_TEAM);
212
213	device->tx_sem = sem = create_sem(1, DRIVER_NAME"_transmit");
214	if (sem < B_OK) {
215		delete_sem(device->sem_lock);
216		delete_sem(device->rx_sem);
217		delete_sem(device->rx_sem_cb);
218		free(device);
219		return NULL;
220	}
221	set_sem_owner(device->tx_sem, B_SYSTEM_TEAM);
222
223	device->tx_sem_cb = sem = create_sem(0, DRIVER_NAME"_transmit_cb");
224	if (sem < B_OK) {
225		delete_sem(device->sem_lock);
226		delete_sem(device->rx_sem);
227		delete_sem(device->rx_sem_cb);
228		delete_sem(device->tx_sem);
229		free(device);
230		return NULL;
231	}
232	set_sem_owner(device->tx_sem_cb, B_SYSTEM_TEAM);
233
234	device->number = sDeviceNumber++;
235	device->cookieMagic = PEGASUS_COOKIE_MAGIC;
236	sprintf(device->name, "%s%d", kBaseName, device->number);
237	device->dev = dev;
238	device->ifno = ifno;
239	device->open = 0;
240	device->open_fds = NULL;
241	device->aue_dying = false;
242	device->flags = 0;
243	device->maxframesize = 1514; // XXX is MAXIMUM_ETHERNET_FRAME_SIZE = 1518 too much?
244
245	return device;
246}
247
248
249void
250remove_device(pegasus_dev *device)
251{
252	ASSERT(device != NULL);
253
254	delete_sem(device->rx_sem);
255	delete_sem(device->tx_sem);
256	delete_sem(device->rx_sem_cb);
257	delete_sem(device->tx_sem_cb);
258
259	delete_sem(device->sem_lock);
260
261	free(device);
262}
263
264
265/**
266	\fn:
267*/
268static status_t
269setup_endpoints(const usb_interface_info *uii, pegasus_dev *dev)
270{
271	ssize_t epts[3] = { -1, -1, -1 };
272	size_t ep = 0;
273	for(; ep < uii->endpoint_count; ep++){
274		usb_endpoint_descriptor *ed = uii->endpoint[ep].descr;
275		DPRINTF_INFO("try endpoint:%ld %x %x %lx\n", ep, ed->attributes,
276			ed->endpoint_address, uii->endpoint[ep].handle);
277		if ((ed->attributes & USB_ENDPOINT_ATTR_MASK) == USB_ENDPOINT_ATTR_BULK) {
278			if ((ed->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
279				== USB_ENDPOINT_ADDR_DIR_IN) {
280				epts[0]	= ep;
281			} else
282				epts[1] = ep;
283		} else if ((ed->attributes & USB_ENDPOINT_ATTR_MASK)
284				== USB_ENDPOINT_ATTR_INTERRUPT) {
285				epts[2] = ep;
286		}
287	}
288
289	dev->pipe_in = uii->endpoint[epts[0]].handle;
290	dev->pipe_out = uii->endpoint[epts[1]].handle;
291	dev->pipe_intr = uii->endpoint[epts[2]].handle;
292	DPRINTF_INFO("endpoint:%lx %lx %lx\n", dev->pipe_in, dev->pipe_out, dev->pipe_intr);
293
294	return ((epts[0] > -1) && (epts[1] > -1) && (epts[2] > -1)) ? B_OK : B_ENTRY_NOT_FOUND;
295}
296
297
298static void
299pegasus_rx_callback(void *cookie, status_t status, void *data, size_t actual_len)
300{
301	pegasus_dev *dev = (pegasus_dev *)cookie;
302
303	DPRINTF_INFO("pegasus_rx_callback() %ld %ld\n", status, actual_len);
304	if (status == B_CANCELED) {
305		/* cancelled: device is unplugged */
306		DPRINTF_ERR("pegasus_rx_callback() cancelled\n");
307		return;
308	}
309
310	ASSERT(cookie != NULL);
311	dev->rx_actual_length = actual_len;
312	dev->rx_status = status;	/* B_USB_STATUS_* */
313	release_sem(dev->rx_sem_cb);
314	DPRINTF_INFO("pegasus_rx_callback release sem %ld\n", dev->rx_sem_cb);
315}
316
317
318static void
319pegasus_tx_callback(void *cookie, status_t status, void *data, size_t actual_len)
320{
321	pegasus_dev *dev = (pegasus_dev *)cookie;
322
323	DPRINTF_INFO("pegasus_tx_callback() %ld %ld\n", status, actual_len);
324	if (status == B_CANCELED) {
325		/* cancelled: device is unplugged */
326		DPRINTF_ERR("pegasus_tx_callback() cancelled\n");
327		return;
328	}
329
330	ASSERT(cookie != NULL);
331	dev->tx_actual_length = actual_len;
332	dev->tx_status = status;	/* B_USB_STATUS_* */
333	release_sem(dev->tx_sem_cb);
334	DPRINTF_INFO("pegasus_tx_callback release sem %ld\n", dev->tx_sem_cb);
335}
336
337
338//	#pragma mark - device hooks
339
340
341static status_t
342pegasus_device_added(const usb_device dev, void **cookie)
343{
344	pegasus_dev *device;
345	const usb_device_descriptor *dev_desc;
346	const usb_configuration_info *conf;
347	const usb_interface_info *intf;
348	status_t status;
349	uint16 ifno;
350	unsigned int i;
351
352	ASSERT(dev != 0 && cookie != NULL);
353	DPRINTF_INFO("device_added()\n");
354
355	dev_desc = usb->get_device_descriptor(dev);
356
357	DPRINTF_INFO("vendor ID 0x%04X, product ID 0x%04X\n", dev_desc->vendor_id,
358		dev_desc->product_id);
359
360	if ((conf = usb->get_nth_configuration(dev, DEFAULT_CONFIGURATION))
361		== NULL) {
362		DPRINTF_ERR("cannot get default configuration\n");
363		return B_ERROR;
364	}
365
366	ifno = AUE_IFACE_IDX;
367	intf = conf->interface [ifno].active;
368
369	/* configuration */
370
371	if ((status = usb->set_configuration(dev, conf)) != B_OK) {
372		DPRINTF_ERR("set_configuration() failed %s\n", strerror(status));
373		return B_ERROR;
374	}
375
376	if ((device = create_device(dev, intf, ifno)) == NULL) {
377		DPRINTF_ERR("create_device() failed\n");
378		return B_ERROR;
379	}
380
381	device->aue_vendor = dev_desc->vendor_id;
382	device->aue_product = dev_desc->product_id;
383
384	for (i=0; i < sizeof(aue_devs) / sizeof(struct aue_type); i++)
385		if (aue_devs[i].aue_dev.vendor == dev_desc->vendor_id
386			&& aue_devs[i].aue_dev.product == dev_desc->product_id) {
387			device->aue_flags = aue_devs[i].aue_flags;
388			break;
389		}
390
391	/* Find endpoints. */
392	setup_endpoints(intf, device);
393
394	aue_attach(device);
395
396	DPRINTF_INFO("MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
397		device->macaddr[0], device->macaddr[1], device->macaddr[2],
398		device->macaddr[3], device->macaddr[4], device->macaddr[5]);
399
400	aue_init(device);
401
402	/* create a port */
403	add_device_info(device);
404
405	*cookie = device;
406	DPRINTF_INFO("added %s\n", device->name);
407	return B_OK;
408}
409
410
411static status_t
412pegasus_device_removed(void *cookie)
413{
414	pegasus_dev *device = cookie;
415
416	ASSERT(cookie != NULL);
417
418	DPRINTF_INFO("device_removed(%s)\n", device->name);
419
420	aue_uninit(device);
421
422	usb->cancel_queued_transfers(device->pipe_in);
423	usb->cancel_queued_transfers(device->pipe_out);
424	usb->cancel_queued_transfers(device->pipe_intr);
425	remove_device_info(device);
426
427	if (device->open == 0) {
428		remove_device(device);
429	} else {
430		DPRINTF_INFO("%s still open\n", device->name);
431		AUE_LOCK(device);
432		device->aue_dying = true;
433		AUE_UNLOCK(device);
434	}
435
436	return B_OK;
437}
438
439
440static status_t
441pegasus_device_open(const char *name, uint32 flags,
442	driver_cookie **out_cookie)
443{
444	driver_cookie *cookie;
445	pegasus_dev *device;
446	status_t err;
447
448	ASSERT(name != NULL);
449	ASSERT(out_cookie != NULL);
450	DPRINTF_INFO("open(%s)\n", name);
451
452	if ((device = search_device_info(name)) == NULL)
453		return B_ENTRY_NOT_FOUND;
454	if ((cookie = malloc(sizeof(driver_cookie))) == NULL)
455		return B_NO_MEMORY;
456
457	if ((err = acquire_sem(device->sem_lock)) != B_OK) {
458		free(cookie);
459		return err;
460	}
461	device->nonblocking = (flags & O_NONBLOCK) != 0;
462
463	cookie->device = device;
464	cookie->next = device->open_fds;
465	device->open_fds = cookie;
466	device->open++;
467	release_sem(device->sem_lock);
468
469	*out_cookie = cookie;
470	DPRINTF_INFO("device %s open (%d)\n", name, device->open);
471	return B_OK;
472}
473
474
475static status_t
476pegasus_device_read(driver_cookie *cookie, off_t position, void *buffer, size_t *_length)
477{
478	pegasus_dev *dev;
479	status_t status;
480	int32 blockFlag;
481	size_t size;
482
483	DPRINTF_INFO("device %p read\n", cookie);
484
485	if (pegasus_checkdeviceinfo(dev = cookie->device) != B_OK) {
486		DPRINTF_ERR("EINVAL\n");
487#ifndef __HAIKU__
488		*_length = 0;
489			// net_server work-around; it obviously doesn't care about error conditions
490			// For Haiku, this can be removed
491#endif
492		return B_BAD_VALUE;
493	}
494
495	if (dev->aue_dying)
496		return B_DEVICE_NOT_FOUND;		/* already unplugged */
497
498	blockFlag = dev->nonblocking ? B_TIMEOUT : 0;
499
500	// block until receive is available (if blocking is allowed)
501	if ((status = acquire_sem_etc(dev->rx_sem, 1, B_CAN_INTERRUPT | blockFlag, 0)) != B_NO_ERROR) {
502		DPRINTF_ERR("cannot acquire read sem: %" B_PRIx32 ", %s\n", status, strerror(status));
503#ifndef __HAIKU__
504		*_length = 0;
505#endif
506		return status;
507	}
508
509	// queue new request
510	status = usb->queue_bulk(dev->pipe_in, dev->rx_buffer, MAX_FRAME_SIZE, &pegasus_rx_callback, dev);
511
512	if (status != B_OK) {
513		DPRINTF_ERR("queue_bulk:failed:%08" B_PRIx32 "\n", status);
514		goto rx_done;
515	}
516
517	// block until data is available (if blocking is allowed)
518	if ((status = acquire_sem_etc(dev->rx_sem_cb, 1, B_CAN_INTERRUPT | blockFlag, 0)) != B_NO_ERROR) {
519		DPRINTF_ERR("cannot acquire read sem: %" B_PRIx32 ", %s\n", status, strerror(status));
520#ifndef __HAIKU__
521		*_length = 0;
522#endif
523		goto rx_done;
524	}
525
526	if (dev->rx_status != B_OK) {
527		status = usb->clear_feature(dev->pipe_in, USB_FEATURE_ENDPOINT_HALT);
528		if (status != B_OK)
529			DPRINTF_ERR("clear_feature() error %s\n", strerror(status));
530		goto rx_done;
531	}
532
533	// copy buffer
534	size = dev->rx_actual_length;
535	if (size > MAX_FRAME_SIZE || (size - 2) > *_length) {
536		DPRINTF_ERR("ERROR read: bad frame size %ld\n", size);
537		size = *_length;
538	} else if (size < *_length)
539		*_length = size - 2;
540
541	memcpy(buffer, dev->rx_buffer, size);
542
543	DPRINTF_INFO("read done %ld\n", *_length);
544
545rx_done:
546	release_sem(dev->rx_sem);
547	return status;
548}
549
550
551static status_t
552pegasus_device_write(driver_cookie *cookie, off_t position,	const void *buffer, size_t *_length)
553{
554	pegasus_dev *dev;
555	status_t status;
556	uint16 frameSize;
557
558	DPRINTF_INFO("device %p write %ld\n", cookie, *_length);
559
560	if (pegasus_checkdeviceinfo(dev = cookie->device) != B_OK) {
561		DPRINTF_ERR("EINVAL\n");
562		return EINVAL;
563	}
564
565	if (dev->aue_dying)
566		return B_DEVICE_NOT_FOUND;		/* already unplugged */
567
568		// block until a free tx descriptor is available
569	if ((status = acquire_sem_etc(dev->tx_sem, 1, B_TIMEOUT, ETHER_TRANSMIT_TIMEOUT)) < B_NO_ERROR) {
570		DPRINTF_ERR("write: acquiring sem failed: %" B_PRIx32 ", %s\n", status, strerror(status));
571		return status;
572	}
573
574
575	if (*_length > MAX_FRAME_SIZE)
576		*_length = MAX_FRAME_SIZE;
577
578	frameSize = *_length;
579
580	/* Copy data to tx buffer */
581	memcpy(dev->tx_buffer+2, buffer, frameSize);
582
583	/*
584	 * The ADMtek documentation says that the packet length is
585	 * supposed to be specified in the first two bytes of the
586	 * transfer, however it actually seems to ignore this info
587	 * and base the frame size on the bulk transfer length.
588	 */
589	dev->tx_buffer[0] = (uint8)frameSize;
590	dev->tx_buffer[1] = (uint8)(frameSize >> 8);
591
592	// queue new request, bulk length is one more if size is a multiple of 64
593	status = usb->queue_bulk(dev->pipe_out, dev->tx_buffer, ((frameSize + 2) & 0x3f) ? frameSize + 2 : frameSize + 3,
594		&pegasus_tx_callback, dev);
595
596	if (status != B_OK){
597		DPRINTF_ERR("queue_bulk:failed:%08" B_PRIx32 "\n", status);
598		goto tx_done;
599	}
600
601	// block until data is sent (if blocking is allowed)
602	if ((status = acquire_sem_etc(dev->tx_sem_cb, 1, B_CAN_INTERRUPT, 0)) != B_NO_ERROR) {
603		DPRINTF_ERR("cannot acquire write done sem: %" B_PRIx32 ", %s\n", status, strerror(status));
604#ifndef __HAIKU__
605		*_length = 0;
606#endif
607		goto tx_done;
608	}
609
610	if (dev->tx_status != B_OK) {
611		status = usb->clear_feature(dev->pipe_out, USB_FEATURE_ENDPOINT_HALT);
612		if (status != B_OK)
613			DPRINTF_ERR("clear_feature() error %s\n", strerror(status));
614		goto tx_done;
615	}
616
617	*_length = frameSize;
618
619tx_done:
620	release_sem(dev->tx_sem);
621	return status;
622}
623
624
625static status_t
626pegasus_device_control(driver_cookie *cookie, uint32 op,
627		void *arg, size_t len)
628{
629	status_t err = B_ERROR;
630	pegasus_dev *device;
631
632	ASSERT(cookie != NULL);
633	device = cookie->device;
634	ASSERT(device != NULL);
635	DPRINTF_INFO("ioctl(0x%x)\n", (int)op);
636
637	if (device->aue_dying)
638		return B_DEVICE_NOT_FOUND;		/* already unplugged */
639
640	switch (op) {
641		case ETHER_INIT:
642			DPRINTF_INFO("control() ETHER_INIT\n");
643			return B_OK;
644
645		case ETHER_GETADDR:
646			DPRINTF_INFO("control() ETHER_GETADDR\n");
647			memcpy(arg, &device->macaddr, sizeof(device->macaddr));
648			return B_OK;
649
650		case ETHER_NONBLOCK:
651			if (*(int32 *)arg) {
652				DPRINTF_INFO("non blocking mode on\n");
653				device->nonblocking = true;
654			} else {
655				DPRINTF_INFO("non blocking mode off\n");
656				device->nonblocking = false;
657			}
658			return B_OK;
659
660		case ETHER_ADDMULTI:
661			DPRINTF_INFO("control() ETHER_ADDMULTI\n");
662			break;
663
664		case ETHER_REMMULTI:
665			DPRINTF_INFO("control() ETHER_REMMULTI\n");
666			return B_OK;
667
668		case ETHER_SETPROMISC:
669			if (*(int32 *)arg) {
670				DPRINTF_INFO("control() ETHER_SETPROMISC on\n");
671			} else {
672				DPRINTF_INFO("control() ETHER_SETPROMISC off\n");
673			}
674			return B_OK;
675
676		case ETHER_GETFRAMESIZE:
677			DPRINTF_INFO("control() ETHER_GETFRAMESIZE, framesize = %ld (MTU = %ld)\n", device->maxframesize,  device->maxframesize - ENET_HEADER_SIZE);
678			*(uint32*)arg = device->maxframesize;
679			return B_OK;
680
681		default:
682			DPRINTF_INFO("control() Invalid command\n");
683			break;
684	}
685
686	return err;
687}
688
689
690static status_t
691pegasus_device_close(driver_cookie *cookie)
692{
693	pegasus_dev *device;
694
695	ASSERT(cookie != NULL && cookie->device != NULL);
696	device = cookie->device;
697	DPRINTF_INFO("close(%s)\n", device->name);
698
699	/* detach the cookie from list */
700
701	acquire_sem(device->sem_lock);
702	if (device->open_fds == cookie)
703		device->open_fds = cookie->next;
704	else {
705		driver_cookie *p;
706		for (p = device->open_fds; p != NULL; p = p->next) {
707			if (p->next == cookie) {
708				p->next = cookie->next;
709				break;
710			}
711		}
712	}
713	--device->open;
714	release_sem(device->sem_lock);
715
716	return B_OK;
717}
718
719
720static status_t
721pegasus_device_free(driver_cookie *cookie)
722{
723	pegasus_dev *device;
724
725	ASSERT(cookie != NULL && cookie->device != NULL);
726	device = cookie->device;
727	DPRINTF_INFO("free(%s)\n", device->name);
728
729	free(cookie);
730	if (device->open > 0)
731		DPRINTF_INFO("%d opens left\n", device->open);
732	else if (device->aue_dying) {
733		DPRINTF_INFO("removed %s\n", device->name);
734		remove_device(device);
735	}
736
737	return B_OK;
738}
739
740
741
742/* Driver Hooks ---------------------------------------------------------
743**
744** These functions provide the glue used by DevFS to load/unload
745** the driver and also handle registering with the USB bus manager
746** to receive device added and removed events
747*/
748
749static usb_notify_hooks notify_hooks =
750{
751	&pegasus_device_added,
752	&pegasus_device_removed
753};
754
755
756status_t
757init_hardware(void)
758{
759	return B_OK;
760}
761
762
763status_t
764init_driver(void)
765{
766	DPRINTF_INFO("init_driver(), built %s %s\n", __DATE__, __TIME__);
767
768#if DEBUG_DRIVER
769	if (load_driver_symbols(drivername) == B_OK) {
770		DPRINTF_INFO("loaded symbols\n");
771	} else {
772		DPRINTF_INFO("no symbols for you!\n");
773	}
774#endif
775
776	if (get_module(B_USB_MODULE_NAME, (module_info**) &usb) != B_OK) {
777		DPRINTF_INFO("cannot get module \"%s\"\n", B_USB_MODULE_NAME);
778		return B_ERROR;
779	}
780
781	if ((gDeviceListLock = create_sem(1, "dev_list_lock")) < 0) {
782		put_module(B_USB_MODULE_NAME);
783		return gDeviceListLock;
784	}
785
786	usb->register_driver(kDriverName, supported_devices,
787		sizeof(supported_devices)/sizeof(usb_support_descriptor), NULL);
788	usb->install_notify(kDriverName, &notify_hooks);
789
790	return B_OK;
791}
792
793
794void
795uninit_driver(void)
796{
797	DPRINTF_INFO("uninit_driver()\n");
798
799	usb->uninstall_notify(kDriverName);
800	delete_sem(gDeviceListLock);
801	put_module(B_USB_MODULE_NAME);
802	free_device_names();
803}
804
805
806const char**
807publish_devices(void)
808{
809	if (gDeviceListChanged) {
810		free_device_names();
811		alloc_device_names();
812		if (gDeviceNames != NULL)
813			rebuild_device_names();
814		gDeviceListChanged = false;
815	}
816	ASSERT(gDeviceNames != NULL);
817	return (const char **) gDeviceNames;
818}
819
820
821device_hooks*
822find_device(const char* name)
823{
824	static device_hooks hooks = {
825		(device_open_hook)pegasus_device_open,
826		(device_close_hook)pegasus_device_close,
827		(device_free_hook)pegasus_device_free,
828		(device_control_hook)pegasus_device_control,
829		(device_read_hook)pegasus_device_read,
830		(device_write_hook)pegasus_device_write,
831		NULL
832	};
833
834	if (search_device_info(name) != NULL)
835		return &hooks;
836	return NULL;
837}
838