if_cdce.c revision 189528
1184610Salfred/*	$NetBSD: if_cdce.c,v 1.4 2004/10/24 12:50:54 augustss Exp $ */
2184610Salfred
3184610Salfred/*-
4184610Salfred * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul <wpaul@windriver.com>
5184610Salfred * Copyright (c) 2003-2005 Craig Boston
6184610Salfred * Copyright (c) 2004 Daniel Hartmeier
7188412Sthompsa * Copyright (c) 2009 Hans Petter Selasky
8184610Salfred * All rights reserved.
9184610Salfred *
10184610Salfred * Redistribution and use in source and binary forms, with or without
11184610Salfred * modification, are permitted provided that the following conditions
12184610Salfred * are met:
13184610Salfred * 1. Redistributions of source code must retain the above copyright
14184610Salfred *    notice, this list of conditions and the following disclaimer.
15184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
16184610Salfred *    notice, this list of conditions and the following disclaimer in the
17184610Salfred *    documentation and/or other materials provided with the distribution.
18184610Salfred * 3. All advertising materials mentioning features or use of this software
19184610Salfred *    must display the following acknowledgement:
20184610Salfred *	This product includes software developed by Bill Paul.
21184610Salfred * 4. Neither the name of the author nor the names of any co-contributors
22184610Salfred *    may be used to endorse or promote products derived from this software
23184610Salfred *    without specific prior written permission.
24184610Salfred *
25184610Salfred * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
26184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul, THE VOICES IN HIS HEAD OR
29184610Salfred * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30184610Salfred * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31184610Salfred * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
32184610Salfred * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33184610Salfred * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34184610Salfred * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35184610Salfred * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36184610Salfred */
37184610Salfred
38184610Salfred/*
39184610Salfred * USB Communication Device Class (Ethernet Networking Control Model)
40184610Salfred * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
41184610Salfred */
42184610Salfred
43184610Salfred#include <sys/cdefs.h>
44184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/net/if_cdce.c 189528 2009-03-08 06:56:13Z thompsa $");
45184610Salfred
46188746Sthompsa#include "usbdevs.h"
47188942Sthompsa#include <dev/usb/usb.h>
48188942Sthompsa#include <dev/usb/usb_mfunc.h>
49188942Sthompsa#include <dev/usb/usb_error.h>
50188942Sthompsa#include <dev/usb/usb_cdc.h>
51188942Sthompsa#include <dev/usb/usb_defs.h>
52184610Salfred
53184610Salfred#define	USB_DEBUG_VAR cdce_debug
54184610Salfred
55188942Sthompsa#include <dev/usb/usb_core.h>
56188942Sthompsa#include <dev/usb/usb_lookup.h>
57188942Sthompsa#include <dev/usb/usb_process.h>
58188942Sthompsa#include <dev/usb/usb_debug.h>
59188942Sthompsa#include <dev/usb/usb_request.h>
60188942Sthompsa#include <dev/usb/usb_busdma.h>
61188942Sthompsa#include <dev/usb/usb_util.h>
62188942Sthompsa#include <dev/usb/usb_parse.h>
63188942Sthompsa#include <dev/usb/usb_device.h>
64184610Salfred
65188942Sthompsa#include <dev/usb/net/usb_ethernet.h>
66188942Sthompsa#include <dev/usb/net/if_cdcereg.h>
67184610Salfred
68184610Salfredstatic device_probe_t cdce_probe;
69184610Salfredstatic device_attach_t cdce_attach;
70184610Salfredstatic device_detach_t cdce_detach;
71184610Salfredstatic device_shutdown_t cdce_shutdown;
72184610Salfredstatic device_suspend_t cdce_suspend;
73184610Salfredstatic device_resume_t cdce_resume;
74188942Sthompsastatic usb_handle_request_t cdce_handle_request;
75184610Salfred
76184610Salfredstatic usb2_callback_t cdce_bulk_write_callback;
77184610Salfredstatic usb2_callback_t cdce_bulk_read_callback;
78184610Salfredstatic usb2_callback_t cdce_intr_read_callback;
79184610Salfredstatic usb2_callback_t cdce_intr_write_callback;
80184610Salfred
81188412Sthompsastatic usb2_ether_fn_t cdce_attach_post;
82188412Sthompsastatic usb2_ether_fn_t cdce_init;
83188412Sthompsastatic usb2_ether_fn_t cdce_stop;
84188412Sthompsastatic usb2_ether_fn_t cdce_start;
85188412Sthompsastatic usb2_ether_fn_t cdce_setmulti;
86188412Sthompsastatic usb2_ether_fn_t cdce_setpromisc;
87188412Sthompsa
88185948Sthompsastatic uint32_t	cdce_m_crc32(struct mbuf *, uint32_t, uint32_t);
89184610Salfred
90184610Salfred#if USB_DEBUG
91184610Salfredstatic int cdce_debug = 0;
92184610Salfred
93188412SthompsaSYSCTL_NODE(_hw_usb2, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet");
94184610SalfredSYSCTL_INT(_hw_usb2_cdce, OID_AUTO, debug, CTLFLAG_RW, &cdce_debug, 0,
95188412Sthompsa    "Debug level");
96184610Salfred#endif
97184610Salfred
98184610Salfredstatic const struct usb2_config cdce_config[CDCE_N_TRANSFER] = {
99184610Salfred
100187259Sthompsa	[CDCE_BULK_A] = {
101184610Salfred		.type = UE_BULK,
102184610Salfred		.endpoint = UE_ADDR_ANY,
103184610Salfred		.direction = UE_DIR_OUT,
104184610Salfred		.if_index = 0,
105184610Salfred		/* Host Mode */
106188412Sthompsa		.mh.frames = CDCE_FRAMES_MAX,
107188412Sthompsa		.mh.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
108184610Salfred		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
109188412Sthompsa		.mh.callback = cdce_bulk_write_callback,
110184610Salfred		.mh.timeout = 10000,	/* 10 seconds */
111184610Salfred		/* Device Mode */
112188412Sthompsa		.md.frames = CDCE_FRAMES_MAX,
113188412Sthompsa		.md.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
114188412Sthompsa		.md.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
115188412Sthompsa		.md.callback = cdce_bulk_read_callback,
116184610Salfred		.md.timeout = 0,	/* no timeout */
117184610Salfred	},
118184610Salfred
119187259Sthompsa	[CDCE_BULK_B] = {
120184610Salfred		.type = UE_BULK,
121184610Salfred		.endpoint = UE_ADDR_ANY,
122184610Salfred		.direction = UE_DIR_IN,
123184610Salfred		.if_index = 0,
124184610Salfred		/* Host Mode */
125188412Sthompsa		.mh.frames = CDCE_FRAMES_MAX,
126188412Sthompsa		.mh.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
127188412Sthompsa		.mh.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
128188412Sthompsa		.mh.callback = cdce_bulk_read_callback,
129184610Salfred		.mh.timeout = 0,	/* no timeout */
130184610Salfred		/* Device Mode */
131188412Sthompsa		.md.frames = CDCE_FRAMES_MAX,
132188412Sthompsa		.md.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
133184610Salfred		.md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
134188412Sthompsa		.md.callback = cdce_bulk_write_callback,
135184610Salfred		.md.timeout = 10000,	/* 10 seconds */
136184610Salfred	},
137184610Salfred
138187259Sthompsa	[CDCE_INTR] = {
139184610Salfred		.type = UE_INTERRUPT,
140184610Salfred		.endpoint = UE_ADDR_ANY,
141184610Salfred		.direction = UE_DIR_IN,
142184610Salfred		.if_index = 1,
143184610Salfred		/* Host Mode */
144184610Salfred		.mh.bufsize = CDCE_IND_SIZE_MAX,
145184610Salfred		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
146188412Sthompsa		.mh.callback = cdce_intr_read_callback,
147184610Salfred		.mh.timeout = 0,
148184610Salfred		/* Device Mode */
149184610Salfred		.md.bufsize = CDCE_IND_SIZE_MAX,
150184610Salfred		.md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
151188412Sthompsa		.md.callback = cdce_intr_write_callback,
152184610Salfred		.md.timeout = 10000,	/* 10 seconds */
153184610Salfred	},
154184610Salfred};
155184610Salfred
156184610Salfredstatic device_method_t cdce_methods[] = {
157184610Salfred	/* USB interface */
158188942Sthompsa	DEVMETHOD(usb_handle_request, cdce_handle_request),
159184610Salfred
160184610Salfred	/* Device interface */
161184610Salfred	DEVMETHOD(device_probe, cdce_probe),
162184610Salfred	DEVMETHOD(device_attach, cdce_attach),
163184610Salfred	DEVMETHOD(device_detach, cdce_detach),
164184610Salfred	DEVMETHOD(device_suspend, cdce_suspend),
165184610Salfred	DEVMETHOD(device_resume, cdce_resume),
166184610Salfred	DEVMETHOD(device_shutdown, cdce_shutdown),
167184610Salfred
168184610Salfred	{0, 0}
169184610Salfred};
170184610Salfred
171184610Salfredstatic driver_t cdce_driver = {
172184610Salfred	.name = "cdce",
173184610Salfred	.methods = cdce_methods,
174184610Salfred	.size = sizeof(struct cdce_softc),
175184610Salfred};
176184610Salfred
177184610Salfredstatic devclass_t cdce_devclass;
178184610Salfred
179189275SthompsaDRIVER_MODULE(cdce, uhub, cdce_driver, cdce_devclass, NULL, 0);
180184610SalfredMODULE_VERSION(cdce, 1);
181188942SthompsaMODULE_DEPEND(cdce, uether, 1, 1, 1);
182188942SthompsaMODULE_DEPEND(cdce, usb, 1, 1, 1);
183184610SalfredMODULE_DEPEND(cdce, ether, 1, 1, 1);
184184610Salfred
185188412Sthompsastatic const struct usb2_ether_methods cdce_ue_methods = {
186188412Sthompsa	.ue_attach_post = cdce_attach_post,
187188412Sthompsa	.ue_start = cdce_start,
188188412Sthompsa	.ue_init = cdce_init,
189188412Sthompsa	.ue_stop = cdce_stop,
190188412Sthompsa	.ue_setmulti = cdce_setmulti,
191188412Sthompsa	.ue_setpromisc = cdce_setpromisc,
192188412Sthompsa};
193188412Sthompsa
194184610Salfredstatic const struct usb2_device_id cdce_devs[] = {
195184610Salfred	{USB_IF_CSI(UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0)},
196184610Salfred	{USB_IF_CSI(UICLASS_CDC, UISUBCLASS_MOBILE_DIRECT_LINE_MODEL, 0)},
197184610Salfred
198184610Salfred	{USB_VPI(USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632, CDCE_FLAG_NO_UNION)},
199184610Salfred	{USB_VPI(USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250, CDCE_FLAG_NO_UNION)},
200184610Salfred	{USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX, CDCE_FLAG_NO_UNION)},
201184610Salfred	{USB_VPI(USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00, CDCE_FLAG_NO_UNION)},
202184610Salfred	{USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
203184610Salfred	{USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
204184610Salfred	{USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET, CDCE_FLAG_NO_UNION)},
205184610Salfred	{USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501, CDCE_FLAG_NO_UNION)},
206184610Salfred	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500, CDCE_FLAG_ZAURUS)},
207184610Salfred	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
208184610Salfred	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
209184610Salfred	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
210184610Salfred	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
211184610Salfred};
212184610Salfred
213184610Salfredstatic int
214184610Salfredcdce_probe(device_t dev)
215184610Salfred{
216184610Salfred	struct usb2_attach_arg *uaa = device_get_ivars(dev);
217184610Salfred
218184610Salfred	return (usb2_lookup_id_by_uaa(cdce_devs, sizeof(cdce_devs), uaa));
219184610Salfred}
220184610Salfred
221188412Sthompsastatic void
222188412Sthompsacdce_attach_post(struct usb2_ether *ue)
223188412Sthompsa{
224188412Sthompsa	/* no-op */
225188412Sthompsa	return;
226188412Sthompsa}
227188412Sthompsa
228184610Salfredstatic int
229184610Salfredcdce_attach(device_t dev)
230184610Salfred{
231184610Salfred	struct cdce_softc *sc = device_get_softc(dev);
232188412Sthompsa	struct usb2_ether *ue = &sc->sc_ue;
233184610Salfred	struct usb2_attach_arg *uaa = device_get_ivars(dev);
234184610Salfred	struct usb2_interface *iface;
235184610Salfred	const struct usb2_cdc_union_descriptor *ud;
236184610Salfred	const struct usb2_interface_descriptor *id;
237188412Sthompsa	const struct usb2_cdc_ethernet_descriptor *ued;
238184610Salfred	int error;
239184610Salfred	uint8_t i;
240184610Salfred	char eaddr_str[5 * ETHER_ADDR_LEN];	/* approx */
241184610Salfred
242184610Salfred	sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
243184610Salfred
244184610Salfred	device_set_usb2_desc(dev);
245184610Salfred
246188412Sthompsa	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
247184610Salfred
248184610Salfred	if (sc->sc_flags & CDCE_FLAG_NO_UNION) {
249184610Salfred		sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex;
250184610Salfred		sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
251184610Salfred		sc->sc_data_iface_no = 0;	/* not used */
252184610Salfred		goto alloc_transfers;
253184610Salfred	}
254184610Salfred	ud = usb2_find_descriptor
255184610Salfred	    (uaa->device, NULL, uaa->info.bIfaceIndex,
256184610Salfred	    UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_UNION, 0 - 1);
257184610Salfred
258184610Salfred	if ((ud == NULL) || (ud->bLength < sizeof(*ud))) {
259184610Salfred		device_printf(dev, "no union descriptor!\n");
260184610Salfred		goto detach;
261184610Salfred	}
262184610Salfred	sc->sc_data_iface_no = ud->bSlaveInterface[0];
263184610Salfred
264184610Salfred	for (i = 0;; i++) {
265184610Salfred
266184610Salfred		iface = usb2_get_iface(uaa->device, i);
267184610Salfred
268184610Salfred		if (iface) {
269184610Salfred
270184610Salfred			id = usb2_get_interface_descriptor(iface);
271184610Salfred
272184610Salfred			if (id && (id->bInterfaceNumber ==
273184610Salfred			    sc->sc_data_iface_no)) {
274184610Salfred				sc->sc_ifaces_index[0] = i;
275184610Salfred				sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
276184610Salfred				usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
277184610Salfred				break;
278184610Salfred			}
279184610Salfred		} else {
280184610Salfred			device_printf(dev, "no data interface found!\n");
281184610Salfred			goto detach;
282184610Salfred		}
283184610Salfred	}
284184610Salfred
285184610Salfred	/*
286184610Salfred	 * <quote>
287184610Salfred	 *
288184610Salfred	 *  The Data Class interface of a networking device shall have
289184610Salfred	 *  a minimum of two interface settings. The first setting
290184610Salfred	 *  (the default interface setting) includes no endpoints and
291184610Salfred	 *  therefore no networking traffic is exchanged whenever the
292184610Salfred	 *  default interface setting is selected. One or more
293184610Salfred	 *  additional interface settings are used for normal
294184610Salfred	 *  operation, and therefore each includes a pair of endpoints
295184610Salfred	 *  (one IN, and one OUT) to exchange network traffic. Select
296184610Salfred	 *  an alternate interface setting to initialize the network
297184610Salfred	 *  aspects of the device and to enable the exchange of
298184610Salfred	 *  network traffic.
299184610Salfred	 *
300184610Salfred	 * </quote>
301184610Salfred	 *
302184610Salfred	 * Some devices, most notably cable modems, include interface
303184610Salfred	 * settings that have no IN or OUT endpoint, therefore loop
304184610Salfred	 * through the list of all available interface settings
305184610Salfred	 * looking for one with both IN and OUT endpoints.
306184610Salfred	 */
307184610Salfred
308184610Salfredalloc_transfers:
309184610Salfred
310188412Sthompsa	for (i = 0; i != 32; i++) {
311184610Salfred
312184610Salfred		error = usb2_set_alt_interface_index
313184610Salfred		    (uaa->device, sc->sc_ifaces_index[0], i);
314184610Salfred
315184610Salfred		if (error) {
316184610Salfred			device_printf(dev, "no valid alternate "
317184610Salfred			    "setting found!\n");
318184610Salfred			goto detach;
319184610Salfred		}
320184610Salfred		error = usb2_transfer_setup
321184610Salfred		    (uaa->device, sc->sc_ifaces_index,
322184610Salfred		    sc->sc_xfer, cdce_config, CDCE_N_TRANSFER,
323184610Salfred		    sc, &sc->sc_mtx);
324184610Salfred
325184610Salfred		if (error == 0) {
326184610Salfred			break;
327184610Salfred		}
328184610Salfred	}
329184610Salfred
330188412Sthompsa	ued = usb2_find_descriptor
331184610Salfred	    (uaa->device, NULL, uaa->info.bIfaceIndex,
332184610Salfred	    UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_ENF, 0 - 1);
333184610Salfred
334188412Sthompsa	if ((ued == NULL) || (ued->bLength < sizeof(*ued))) {
335184610Salfred		error = USB_ERR_INVAL;
336184610Salfred	} else {
337188412Sthompsa		error = usb2_req_get_string_any(uaa->device, NULL,
338188412Sthompsa		    eaddr_str, sizeof(eaddr_str), ued->iMacAddress);
339184610Salfred	}
340184610Salfred
341184610Salfred	if (error) {
342184610Salfred
343184610Salfred		/* fake MAC address */
344184610Salfred
345184610Salfred		device_printf(dev, "faking MAC address\n");
346188412Sthompsa		sc->sc_ue.ue_eaddr[0] = 0x2a;
347188412Sthompsa		memcpy(&sc->sc_ue.ue_eaddr[1], &ticks, sizeof(uint32_t));
348188412Sthompsa		sc->sc_ue.ue_eaddr[5] = device_get_unit(dev);
349184610Salfred
350184610Salfred	} else {
351184610Salfred
352188412Sthompsa		bzero(sc->sc_ue.ue_eaddr, sizeof(sc->sc_ue.ue_eaddr));
353184610Salfred
354188412Sthompsa		for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) {
355184610Salfred
356184610Salfred			char c = eaddr_str[i];
357184610Salfred
358188412Sthompsa			if ('0' <= c && c <= '9')
359184610Salfred				c -= '0';
360188412Sthompsa			else if (c != 0)
361184610Salfred				c -= 'A' - 10;
362188412Sthompsa			else
363184610Salfred				break;
364184610Salfred
365184610Salfred			c &= 0xf;
366184610Salfred
367188412Sthompsa			if ((i & 1) == 0)
368184610Salfred				c <<= 4;
369188412Sthompsa			sc->sc_ue.ue_eaddr[i / 2] |= c;
370184610Salfred		}
371184610Salfred
372184610Salfred		if (uaa->usb2_mode == USB_MODE_DEVICE) {
373184610Salfred			/*
374184610Salfred			 * Do not use the same MAC address like the peer !
375184610Salfred			 */
376188412Sthompsa			sc->sc_ue.ue_eaddr[5] ^= 0xFF;
377184610Salfred		}
378184610Salfred	}
379184610Salfred
380188412Sthompsa	ue->ue_sc = sc;
381188412Sthompsa	ue->ue_dev = dev;
382188412Sthompsa	ue->ue_udev = uaa->device;
383188412Sthompsa	ue->ue_mtx = &sc->sc_mtx;
384188412Sthompsa	ue->ue_methods = &cdce_ue_methods;
385184610Salfred
386188412Sthompsa	error = usb2_ether_ifattach(ue);
387188412Sthompsa	if (error) {
388188412Sthompsa		device_printf(dev, "could not attach interface\n");
389184610Salfred		goto detach;
390184610Salfred	}
391184610Salfred	return (0);			/* success */
392184610Salfred
393184610Salfreddetach:
394184610Salfred	cdce_detach(dev);
395184610Salfred	return (ENXIO);			/* failure */
396184610Salfred}
397184610Salfred
398184610Salfredstatic int
399184610Salfredcdce_detach(device_t dev)
400184610Salfred{
401184610Salfred	struct cdce_softc *sc = device_get_softc(dev);
402188412Sthompsa	struct usb2_ether *ue = &sc->sc_ue;
403184610Salfred
404184610Salfred	/* stop all USB transfers first */
405184610Salfred	usb2_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER);
406188412Sthompsa	usb2_ether_ifdetach(ue);
407184610Salfred	mtx_destroy(&sc->sc_mtx);
408184610Salfred
409184610Salfred	return (0);
410184610Salfred}
411184610Salfred
412184610Salfredstatic void
413188412Sthompsacdce_start(struct usb2_ether *ue)
414184610Salfred{
415188412Sthompsa	struct cdce_softc *sc = usb2_ether_getsc(ue);
416184610Salfred
417188412Sthompsa	/*
418188412Sthompsa	 * Start the USB transfers, if not already started:
419188412Sthompsa	 */
420188412Sthompsa	usb2_transfer_start(sc->sc_xfer[CDCE_BULK_B]);
421188412Sthompsa	usb2_transfer_start(sc->sc_xfer[CDCE_BULK_A]);
422184610Salfred}
423184610Salfred
424184610Salfredstatic void
425188412Sthompsacdce_free_queue(struct mbuf **ppm, uint8_t n)
426184610Salfred{
427188412Sthompsa	uint8_t x;
428188412Sthompsa	for (x = 0; x != n; x++) {
429188412Sthompsa		if (ppm[x] != NULL) {
430188412Sthompsa			m_freem(ppm[x]);
431188412Sthompsa			ppm[x] = NULL;
432184610Salfred		}
433184610Salfred	}
434184610Salfred}
435184610Salfred
436184610Salfredstatic void
437188412Sthompsacdce_bulk_write_callback(struct usb2_xfer *xfer)
438184610Salfred{
439184610Salfred	struct cdce_softc *sc = xfer->priv_sc;
440188412Sthompsa	struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
441184610Salfred	struct mbuf *m;
442184610Salfred	struct mbuf *mt;
443188412Sthompsa	uint32_t crc;
444188412Sthompsa	uint8_t x;
445184610Salfred
446188412Sthompsa	DPRINTFN(1, "\n");
447188412Sthompsa
448184610Salfred	switch (USB_GET_STATE(xfer)) {
449184610Salfred	case USB_ST_TRANSFERRED:
450184610Salfred		DPRINTFN(11, "transfer complete: "
451188412Sthompsa		    "%u bytes in %u frames\n", xfer->actlen,
452188412Sthompsa		    xfer->aframes);
453184610Salfred
454188412Sthompsa		ifp->if_opackets++;
455184610Salfred
456188412Sthompsa		/* free all previous TX buffers */
457188412Sthompsa		cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);
458184610Salfred
459188412Sthompsa		/* FALLTHROUGH */
460184610Salfred	case USB_ST_SETUP:
461184610Salfredtr_setup:
462188412Sthompsa		for (x = 0; x != CDCE_FRAMES_MAX; x++) {
463184610Salfred
464184610Salfred			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
465184610Salfred
466188412Sthompsa			if (m == NULL)
467184610Salfred				break;
468188412Sthompsa
469188412Sthompsa			if (sc->sc_flags & CDCE_FLAG_ZAURUS) {
470188412Sthompsa				/*
471188412Sthompsa				 * Zaurus wants a 32-bit CRC appended
472188412Sthompsa				 * to every frame
473188412Sthompsa				 */
474188412Sthompsa
475188412Sthompsa				crc = cdce_m_crc32(m, 0, m->m_pkthdr.len);
476188412Sthompsa				crc = htole32(crc);
477188412Sthompsa
478188412Sthompsa				if (!m_append(m, 4, (void *)&crc)) {
479188412Sthompsa					m_freem(m);
480188412Sthompsa					ifp->if_oerrors++;
481188412Sthompsa					continue;
482188412Sthompsa				}
483184610Salfred			}
484188412Sthompsa			if (m->m_len != m->m_pkthdr.len) {
485184610Salfred				mt = m_defrag(m, M_DONTWAIT);
486184610Salfred				if (mt == NULL) {
487184610Salfred					m_freem(m);
488184610Salfred					ifp->if_oerrors++;
489184610Salfred					continue;
490184610Salfred				}
491184610Salfred				m = mt;
492184610Salfred			}
493188412Sthompsa			if (m->m_pkthdr.len > MCLBYTES) {
494188412Sthompsa				m->m_pkthdr.len = MCLBYTES;
495188412Sthompsa			}
496188412Sthompsa			sc->sc_tx_buf[x] = m;
497188412Sthompsa			xfer->frlengths[x] = m->m_len;
498188412Sthompsa			usb2_set_frame_data(xfer, m->m_data, x);
499184610Salfred
500184610Salfred			/*
501188412Sthompsa			 * If there's a BPF listener, bounce a copy of
502188412Sthompsa			 * this frame to him:
503184610Salfred			 */
504184610Salfred			BPF_MTAP(ifp, m);
505184610Salfred		}
506188412Sthompsa		if (x != 0) {
507188412Sthompsa			xfer->nframes = x;
508188412Sthompsa			usb2_start_hardware(xfer);
509184610Salfred		}
510184610Salfred		break;
511184610Salfred
512184610Salfred	default:			/* Error */
513184610Salfred		DPRINTFN(11, "transfer error, %s\n",
514184610Salfred		    usb2_errstr(xfer->error));
515184610Salfred
516188412Sthompsa		/* free all previous TX buffers */
517188412Sthompsa		cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);
518184610Salfred
519188412Sthompsa		/* count output errors */
520184610Salfred		ifp->if_oerrors++;
521184610Salfred
522184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
523184610Salfred			/* try to clear stall first */
524184610Salfred			xfer->flags.stall_pipe = 1;
525184610Salfred			goto tr_setup;
526184610Salfred		}
527184610Salfred		break;
528184610Salfred	}
529184610Salfred}
530184610Salfred
531184610Salfredstatic int32_t
532184610Salfredcdce_m_crc32_cb(void *arg, void *src, uint32_t count)
533184610Salfred{
534188412Sthompsa	uint32_t *p_crc = arg;
535184610Salfred
536184610Salfred	*p_crc = crc32_raw(src, count, *p_crc);
537184610Salfred	return (0);
538184610Salfred}
539184610Salfred
540184610Salfredstatic uint32_t
541184610Salfredcdce_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len)
542184610Salfred{
543184610Salfred	uint32_t crc = 0xFFFFFFFF;
544188412Sthompsa	int error;
545184610Salfred
546188412Sthompsa	error = m_apply(m, src_offset, src_len, cdce_m_crc32_cb, &crc);
547184610Salfred	return (crc ^ 0xFFFFFFFF);
548184610Salfred}
549184610Salfred
550184610Salfredstatic void
551188412Sthompsacdce_init(struct usb2_ether *ue)
552184610Salfred{
553188412Sthompsa	struct cdce_softc *sc = usb2_ether_getsc(ue);
554188412Sthompsa	struct ifnet *ifp = usb2_ether_getifp(ue);
555184610Salfred
556188412Sthompsa	CDCE_LOCK_ASSERT(sc, MA_OWNED);
557184610Salfred
558188412Sthompsa	ifp->if_drv_flags |= IFF_DRV_RUNNING;
559184610Salfred
560188412Sthompsa	/* start interrupt transfer */
561188412Sthompsa	usb2_transfer_start(sc->sc_xfer[CDCE_INTR]);
562188412Sthompsa
563188412Sthompsa	/* stall data write direction, which depends on USB mode */
564188412Sthompsa	if (usb2_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST)
565188412Sthompsa		usb2_transfer_set_stall(sc->sc_xfer[CDCE_BULK_A]);
566188412Sthompsa	else
567188412Sthompsa		usb2_transfer_set_stall(sc->sc_xfer[CDCE_BULK_B]);
568188412Sthompsa
569188412Sthompsa	/* start data transfers */
570188412Sthompsa	cdce_start(ue);
571188412Sthompsa}
572188412Sthompsa
573188412Sthompsastatic void
574188412Sthompsacdce_stop(struct usb2_ether *ue)
575188412Sthompsa{
576188412Sthompsa	struct cdce_softc *sc = usb2_ether_getsc(ue);
577188412Sthompsa	struct ifnet *ifp = usb2_ether_getifp(ue);
578188412Sthompsa
579188412Sthompsa	CDCE_LOCK_ASSERT(sc, MA_OWNED);
580188412Sthompsa
581188412Sthompsa	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
582188412Sthompsa
583184610Salfred	/*
584184610Salfred	 * stop all the transfers, if not already stopped:
585184610Salfred	 */
586187259Sthompsa	usb2_transfer_stop(sc->sc_xfer[CDCE_BULK_A]);
587187259Sthompsa	usb2_transfer_stop(sc->sc_xfer[CDCE_BULK_B]);
588188412Sthompsa	usb2_transfer_stop(sc->sc_xfer[CDCE_INTR]);
589184610Salfred}
590184610Salfred
591188412Sthompsastatic void
592188412Sthompsacdce_setmulti(struct usb2_ether *ue)
593188412Sthompsa{
594188412Sthompsa	/* no-op */
595188412Sthompsa	return;
596188412Sthompsa}
597188412Sthompsa
598188412Sthompsastatic void
599188412Sthompsacdce_setpromisc(struct usb2_ether *ue)
600188412Sthompsa{
601188412Sthompsa	/* no-op */
602188412Sthompsa	return;
603188412Sthompsa}
604188412Sthompsa
605184610Salfredstatic int
606184610Salfredcdce_shutdown(device_t dev)
607184610Salfred{
608184610Salfred	struct cdce_softc *sc = device_get_softc(dev);
609184610Salfred
610188412Sthompsa	usb2_ether_ifshutdown(&sc->sc_ue);
611184610Salfred
612184610Salfred	return (0);
613184610Salfred}
614184610Salfred
615184610Salfredstatic int
616184610Salfredcdce_suspend(device_t dev)
617184610Salfred{
618184610Salfred	device_printf(dev, "Suspending\n");
619184610Salfred	return (0);
620184610Salfred}
621184610Salfred
622184610Salfredstatic int
623184610Salfredcdce_resume(device_t dev)
624184610Salfred{
625184610Salfred	device_printf(dev, "Resuming\n");
626184610Salfred	return (0);
627184610Salfred}
628184610Salfred
629184610Salfredstatic void
630188412Sthompsacdce_bulk_read_callback(struct usb2_xfer *xfer)
631184610Salfred{
632184610Salfred	struct cdce_softc *sc = xfer->priv_sc;
633184610Salfred	struct mbuf *m;
634188412Sthompsa	uint8_t x;
635184610Salfred
636184610Salfred	switch (USB_GET_STATE(xfer)) {
637184610Salfred	case USB_ST_TRANSFERRED:
638184610Salfred
639184610Salfred		DPRINTF("received %u bytes in %u frames\n",
640184610Salfred		    xfer->actlen, xfer->aframes);
641184610Salfred
642188412Sthompsa		for (x = 0; x != xfer->aframes; x++) {
643184610Salfred
644188412Sthompsa			m = sc->sc_rx_buf[x];
645188412Sthompsa			sc->sc_rx_buf[x] = NULL;
646188412Sthompsa
647188412Sthompsa			/* Strip off CRC added by Zaurus, if any */
648188412Sthompsa			if ((sc->sc_flags & CDCE_FLAG_ZAURUS) &&
649188412Sthompsa			    (xfer->frlengths[x] >= 14))
650188412Sthompsa				xfer->frlengths[x] -= 4;
651188412Sthompsa
652188412Sthompsa			if (xfer->frlengths[x] < sizeof(struct ether_header)) {
653188412Sthompsa				m_freem(m);
654188412Sthompsa				continue;
655184610Salfred			}
656188412Sthompsa			/* queue up mbuf */
657188412Sthompsa			usb2_ether_rxmbuf(&sc->sc_ue, m, xfer->frlengths[x]);
658184610Salfred		}
659184610Salfred
660188412Sthompsa		/* FALLTHROUGH */
661184610Salfred	case USB_ST_SETUP:
662188412Sthompsa		/*
663188412Sthompsa		 * TODO: Implement support for multi frame transfers,
664188412Sthompsa		 * when the USB hardware supports it.
665184610Salfred		 */
666188412Sthompsa		for (x = 0; x != 1; x++) {
667188412Sthompsa			if (sc->sc_rx_buf[x] == NULL) {
668189528Sthompsa				m = usb2_ether_newbuf();
669188412Sthompsa				if (m == NULL)
670188412Sthompsa					goto tr_stall;
671188412Sthompsa				sc->sc_rx_buf[x] = m;
672184610Salfred			} else {
673188412Sthompsa				m = sc->sc_rx_buf[x];
674184610Salfred			}
675184610Salfred
676188412Sthompsa			usb2_set_frame_data(xfer, m->m_data, x);
677188412Sthompsa			xfer->frlengths[x] = m->m_len;
678184610Salfred		}
679188412Sthompsa		/* set number of frames and start hardware */
680184610Salfred		xfer->nframes = x;
681184610Salfred		usb2_start_hardware(xfer);
682188412Sthompsa		/* flush any received frames */
683188412Sthompsa		usb2_ether_rxflush(&sc->sc_ue);
684184610Salfred		break;
685184610Salfred
686184610Salfred	default:			/* Error */
687184610Salfred		DPRINTF("error = %s\n",
688184610Salfred		    usb2_errstr(xfer->error));
689184610Salfred
690184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
691188412Sthompsatr_stall:
692184610Salfred			/* try to clear stall first */
693184610Salfred			xfer->flags.stall_pipe = 1;
694188412Sthompsa			xfer->nframes = 0;
695188412Sthompsa			usb2_start_hardware(xfer);
696188412Sthompsa			break;
697184610Salfred		}
698184610Salfred
699188412Sthompsa		/* need to free the RX-mbufs when we are cancelled */
700188412Sthompsa		cdce_free_queue(sc->sc_rx_buf, CDCE_FRAMES_MAX);
701184610Salfred		break;
702184610Salfred	}
703184610Salfred}
704184610Salfred
705184610Salfredstatic void
706184610Salfredcdce_intr_read_callback(struct usb2_xfer *xfer)
707184610Salfred{
708184610Salfred	;				/* style fix */
709184610Salfred	switch (USB_GET_STATE(xfer)) {
710184610Salfred	case USB_ST_TRANSFERRED:
711184610Salfred
712184610Salfred		DPRINTF("Received %d bytes\n",
713184610Salfred		    xfer->actlen);
714184610Salfred
715184610Salfred		/* TODO: decode some indications */
716184610Salfred
717188412Sthompsa		/* FALLTHROUGH */
718184610Salfred	case USB_ST_SETUP:
719184610Salfredtr_setup:
720184610Salfred		xfer->frlengths[0] = xfer->max_data_length;
721184610Salfred		usb2_start_hardware(xfer);
722184610Salfred		break;
723184610Salfred
724184610Salfred	default:			/* Error */
725184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
726184610Salfred			/* start clear stall */
727184610Salfred			xfer->flags.stall_pipe = 1;
728184610Salfred			goto tr_setup;
729184610Salfred		}
730184610Salfred		break;
731184610Salfred	}
732184610Salfred}
733184610Salfred
734184610Salfredstatic void
735184610Salfredcdce_intr_write_callback(struct usb2_xfer *xfer)
736184610Salfred{
737184610Salfred	;				/* style fix */
738184610Salfred	switch (USB_GET_STATE(xfer)) {
739184610Salfred	case USB_ST_TRANSFERRED:
740184610Salfred
741184610Salfred		DPRINTF("Transferred %d bytes\n", xfer->actlen);
742184610Salfred
743188412Sthompsa		/* FALLTHROUGH */
744184610Salfred	case USB_ST_SETUP:
745184610Salfredtr_setup:
746184610Salfred#if 0
747184610Salfred		xfer->frlengths[0] = XXX;
748184610Salfred		usb2_start_hardware(xfer);
749184610Salfred#endif
750184610Salfred		break;
751184610Salfred
752184610Salfred	default:			/* Error */
753184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
754184610Salfred			/* start clear stall */
755184610Salfred			xfer->flags.stall_pipe = 1;
756184610Salfred			goto tr_setup;
757184610Salfred		}
758184610Salfred		break;
759184610Salfred	}
760184610Salfred}
761184610Salfred
762184610Salfredstatic int
763184610Salfredcdce_handle_request(device_t dev,
764184610Salfred    const void *req, void **pptr, uint16_t *plen,
765184610Salfred    uint16_t offset, uint8_t is_complete)
766184610Salfred{
767184610Salfred	return (ENXIO);			/* use builtin handler */
768184610Salfred}
769