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
43197563Sthompsa/*
44197563Sthompsa * USB Network Control Model (NCM)
45197563Sthompsa * http://www.usb.org/developers/devclass_docs/NCM10.zip
46197563Sthompsa */
47197563Sthompsa
48184610Salfred#include <sys/cdefs.h>
49184610Salfred__FBSDID("$FreeBSD: releng/11.0/sys/dev/usb/net/if_cdce.c 292080 2015-12-11 05:28:00Z imp $");
50184610Salfred
51194677Sthompsa#include <sys/stdint.h>
52194677Sthompsa#include <sys/stddef.h>
53194677Sthompsa#include <sys/param.h>
54194677Sthompsa#include <sys/queue.h>
55194677Sthompsa#include <sys/types.h>
56194677Sthompsa#include <sys/systm.h>
57257176Sglebius#include <sys/socket.h>
58194677Sthompsa#include <sys/kernel.h>
59194677Sthompsa#include <sys/bus.h>
60194677Sthompsa#include <sys/module.h>
61194677Sthompsa#include <sys/lock.h>
62194677Sthompsa#include <sys/mutex.h>
63194677Sthompsa#include <sys/condvar.h>
64194677Sthompsa#include <sys/sysctl.h>
65194677Sthompsa#include <sys/sx.h>
66194677Sthompsa#include <sys/unistd.h>
67194677Sthompsa#include <sys/callout.h>
68194677Sthompsa#include <sys/malloc.h>
69194677Sthompsa#include <sys/priv.h>
70194677Sthompsa
71257176Sglebius#include <net/if.h>
72257176Sglebius#include <net/if_var.h>
73257176Sglebius
74188942Sthompsa#include <dev/usb/usb.h>
75194677Sthompsa#include <dev/usb/usbdi.h>
76194677Sthompsa#include <dev/usb/usbdi_util.h>
77188942Sthompsa#include <dev/usb/usb_cdc.h>
78194677Sthompsa#include "usbdevs.h"
79184610Salfred
80184610Salfred#define	USB_DEBUG_VAR cdce_debug
81194677Sthompsa#include <dev/usb/usb_debug.h>
82188942Sthompsa#include <dev/usb/usb_process.h>
83269584Sn_hibma#include <dev/usb/usb_msctest.h>
84194677Sthompsa#include "usb_if.h"
85184610Salfred
86188942Sthompsa#include <dev/usb/net/usb_ethernet.h>
87188942Sthompsa#include <dev/usb/net/if_cdcereg.h>
88184610Salfred
89184610Salfredstatic device_probe_t cdce_probe;
90184610Salfredstatic device_attach_t cdce_attach;
91184610Salfredstatic device_detach_t cdce_detach;
92184610Salfredstatic device_suspend_t cdce_suspend;
93184610Salfredstatic device_resume_t cdce_resume;
94188942Sthompsastatic usb_handle_request_t cdce_handle_request;
95184610Salfred
96193045Sthompsastatic usb_callback_t cdce_bulk_write_callback;
97193045Sthompsastatic usb_callback_t cdce_bulk_read_callback;
98193045Sthompsastatic usb_callback_t cdce_intr_read_callback;
99193045Sthompsastatic usb_callback_t cdce_intr_write_callback;
100184610Salfred
101197563Sthompsa#if CDCE_HAVE_NCM
102197563Sthompsastatic usb_callback_t cdce_ncm_bulk_write_callback;
103197563Sthompsastatic usb_callback_t cdce_ncm_bulk_read_callback;
104197563Sthompsa#endif
105197563Sthompsa
106193045Sthompsastatic uether_fn_t cdce_attach_post;
107193045Sthompsastatic uether_fn_t cdce_init;
108193045Sthompsastatic uether_fn_t cdce_stop;
109193045Sthompsastatic uether_fn_t cdce_start;
110193045Sthompsastatic uether_fn_t cdce_setmulti;
111193045Sthompsastatic uether_fn_t cdce_setpromisc;
112188412Sthompsa
113185948Sthompsastatic uint32_t	cdce_m_crc32(struct mbuf *, uint32_t, uint32_t);
114184610Salfred
115207077Sthompsa#ifdef USB_DEBUG
116184610Salfredstatic int cdce_debug = 0;
117213809Shselaskystatic int cdce_tx_interval = 0;
118184610Salfred
119227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet");
120276701ShselaskySYSCTL_INT(_hw_usb_cdce, OID_AUTO, debug, CTLFLAG_RWTUN, &cdce_debug, 0,
121188412Sthompsa    "Debug level");
122276701ShselaskySYSCTL_INT(_hw_usb_cdce, OID_AUTO, interval, CTLFLAG_RWTUN, &cdce_tx_interval, 0,
123213809Shselasky    "NCM transmit interval in ms");
124184610Salfred#endif
125184610Salfred
126192984Sthompsastatic const struct usb_config cdce_config[CDCE_N_TRANSFER] = {
127184610Salfred
128190734Sthompsa	[CDCE_BULK_RX] = {
129184610Salfred		.type = UE_BULK,
130184610Salfred		.endpoint = UE_ADDR_ANY,
131190734Sthompsa		.direction = UE_DIR_RX,
132184610Salfred		.if_index = 0,
133190734Sthompsa		.frames = CDCE_FRAMES_MAX,
134190734Sthompsa		.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
135190734Sthompsa		.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
136190734Sthompsa		.callback = cdce_bulk_read_callback,
137190734Sthompsa		.timeout = 0,	/* no timeout */
138192499Sthompsa		.usb_mode = USB_MODE_DUAL,	/* both modes */
139184610Salfred	},
140184610Salfred
141190734Sthompsa	[CDCE_BULK_TX] = {
142184610Salfred		.type = UE_BULK,
143184610Salfred		.endpoint = UE_ADDR_ANY,
144190734Sthompsa		.direction = UE_DIR_TX,
145184610Salfred		.if_index = 0,
146190734Sthompsa		.frames = CDCE_FRAMES_MAX,
147190734Sthompsa		.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
148190734Sthompsa		.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
149190734Sthompsa		.callback = cdce_bulk_write_callback,
150190734Sthompsa		.timeout = 10000,	/* 10 seconds */
151192499Sthompsa		.usb_mode = USB_MODE_DUAL,	/* both modes */
152184610Salfred	},
153184610Salfred
154190734Sthompsa	[CDCE_INTR_RX] = {
155184610Salfred		.type = UE_INTERRUPT,
156184610Salfred		.endpoint = UE_ADDR_ANY,
157190734Sthompsa		.direction = UE_DIR_RX,
158184610Salfred		.if_index = 1,
159190734Sthompsa		.bufsize = CDCE_IND_SIZE_MAX,
160190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
161190734Sthompsa		.callback = cdce_intr_read_callback,
162190734Sthompsa		.timeout = 0,
163190734Sthompsa		.usb_mode = USB_MODE_HOST,
164184610Salfred	},
165190734Sthompsa
166190734Sthompsa	[CDCE_INTR_TX] = {
167190734Sthompsa		.type = UE_INTERRUPT,
168190734Sthompsa		.endpoint = UE_ADDR_ANY,
169190734Sthompsa		.direction = UE_DIR_TX,
170190734Sthompsa		.if_index = 1,
171190734Sthompsa		.bufsize = CDCE_IND_SIZE_MAX,
172190734Sthompsa		.flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
173190734Sthompsa		.callback = cdce_intr_write_callback,
174190734Sthompsa		.timeout = 10000,	/* 10 seconds */
175190734Sthompsa		.usb_mode = USB_MODE_DEVICE,
176190734Sthompsa	},
177184610Salfred};
178184610Salfred
179197563Sthompsa#if CDCE_HAVE_NCM
180197563Sthompsastatic const struct usb_config cdce_ncm_config[CDCE_N_TRANSFER] = {
181197563Sthompsa
182197563Sthompsa	[CDCE_BULK_RX] = {
183197563Sthompsa		.type = UE_BULK,
184197563Sthompsa		.endpoint = UE_ADDR_ANY,
185197563Sthompsa		.direction = UE_DIR_RX,
186197563Sthompsa		.if_index = 0,
187197563Sthompsa		.frames = CDCE_NCM_RX_FRAMES_MAX,
188197563Sthompsa		.bufsize = (CDCE_NCM_RX_FRAMES_MAX * CDCE_NCM_RX_MAXLEN),
189197563Sthompsa		.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,},
190197563Sthompsa		.callback = cdce_ncm_bulk_read_callback,
191197563Sthompsa		.timeout = 0,	/* no timeout */
192197563Sthompsa		.usb_mode = USB_MODE_DUAL,	/* both modes */
193197563Sthompsa	},
194197563Sthompsa
195197563Sthompsa	[CDCE_BULK_TX] = {
196197563Sthompsa		.type = UE_BULK,
197197563Sthompsa		.endpoint = UE_ADDR_ANY,
198197563Sthompsa		.direction = UE_DIR_TX,
199197563Sthompsa		.if_index = 0,
200197563Sthompsa		.frames = CDCE_NCM_TX_FRAMES_MAX,
201197563Sthompsa		.bufsize = (CDCE_NCM_TX_FRAMES_MAX * CDCE_NCM_TX_MAXLEN),
202213809Shselasky		.flags = {.pipe_bof = 1,},
203197563Sthompsa		.callback = cdce_ncm_bulk_write_callback,
204197563Sthompsa		.timeout = 10000,	/* 10 seconds */
205197563Sthompsa		.usb_mode = USB_MODE_DUAL,	/* both modes */
206197563Sthompsa	},
207197563Sthompsa
208197563Sthompsa	[CDCE_INTR_RX] = {
209197563Sthompsa		.type = UE_INTERRUPT,
210197563Sthompsa		.endpoint = UE_ADDR_ANY,
211197563Sthompsa		.direction = UE_DIR_RX,
212197563Sthompsa		.if_index = 1,
213197563Sthompsa		.bufsize = CDCE_IND_SIZE_MAX,
214197563Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
215197563Sthompsa		.callback = cdce_intr_read_callback,
216197563Sthompsa		.timeout = 0,
217197563Sthompsa		.usb_mode = USB_MODE_HOST,
218197563Sthompsa	},
219197563Sthompsa
220197563Sthompsa	[CDCE_INTR_TX] = {
221197563Sthompsa		.type = UE_INTERRUPT,
222197563Sthompsa		.endpoint = UE_ADDR_ANY,
223197563Sthompsa		.direction = UE_DIR_TX,
224197563Sthompsa		.if_index = 1,
225197563Sthompsa		.bufsize = CDCE_IND_SIZE_MAX,
226197563Sthompsa		.flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
227197563Sthompsa		.callback = cdce_intr_write_callback,
228197563Sthompsa		.timeout = 10000,	/* 10 seconds */
229197563Sthompsa		.usb_mode = USB_MODE_DEVICE,
230197563Sthompsa	},
231197563Sthompsa};
232197563Sthompsa#endif
233197563Sthompsa
234184610Salfredstatic device_method_t cdce_methods[] = {
235184610Salfred	/* USB interface */
236188942Sthompsa	DEVMETHOD(usb_handle_request, cdce_handle_request),
237184610Salfred
238184610Salfred	/* Device interface */
239184610Salfred	DEVMETHOD(device_probe, cdce_probe),
240184610Salfred	DEVMETHOD(device_attach, cdce_attach),
241184610Salfred	DEVMETHOD(device_detach, cdce_detach),
242184610Salfred	DEVMETHOD(device_suspend, cdce_suspend),
243184610Salfred	DEVMETHOD(device_resume, cdce_resume),
244184610Salfred
245246128Ssbz	DEVMETHOD_END
246184610Salfred};
247184610Salfred
248184610Salfredstatic driver_t cdce_driver = {
249184610Salfred	.name = "cdce",
250184610Salfred	.methods = cdce_methods,
251184610Salfred	.size = sizeof(struct cdce_softc),
252184610Salfred};
253184610Salfred
254184610Salfredstatic devclass_t cdce_devclass;
255269584Sn_hibmastatic eventhandler_tag cdce_etag;
256184610Salfred
257269584Sn_hibmastatic int  cdce_driver_loaded(struct module *, int, void *);
258269584Sn_hibma
259269584Sn_hibmastatic const STRUCT_USB_HOST_ID cdce_switch_devs[] = {
260269584Sn_hibma	{USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E3272_INIT, MSC_EJECT_HUAWEI2)},
261269584Sn_hibma};
262269584Sn_hibma
263223486Shselaskystatic const STRUCT_USB_HOST_ID cdce_host_devs[] = {
264184610Salfred	{USB_VPI(USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632, CDCE_FLAG_NO_UNION)},
265184610Salfred	{USB_VPI(USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250, CDCE_FLAG_NO_UNION)},
266184610Salfred	{USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX, CDCE_FLAG_NO_UNION)},
267184610Salfred	{USB_VPI(USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00, CDCE_FLAG_NO_UNION)},
268184610Salfred	{USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
269184610Salfred	{USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
270184610Salfred	{USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET, CDCE_FLAG_NO_UNION)},
271184610Salfred	{USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501, CDCE_FLAG_NO_UNION)},
272184610Salfred	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500, CDCE_FLAG_ZAURUS)},
273184610Salfred	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
274184610Salfred	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
275184610Salfred	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
276184610Salfred	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
277269584Sn_hibma
278269584Sn_hibma	{USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
279269584Sn_hibma		USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x16),
280269584Sn_hibma		USB_DRIVER_INFO(0)},
281269584Sn_hibma	{USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
282269584Sn_hibma		USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x46),
283269584Sn_hibma		USB_DRIVER_INFO(0)},
284269584Sn_hibma	{USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR),
285269584Sn_hibma		USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x76),
286269584Sn_hibma		USB_DRIVER_INFO(0)},
287223486Shselasky};
288196492Salfred
289223486Shselaskystatic const STRUCT_USB_DUAL_ID cdce_dual_devs[] = {
290196492Salfred	{USB_IF_CSI(UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0)},
291196492Salfred	{USB_IF_CSI(UICLASS_CDC, UISUBCLASS_MOBILE_DIRECT_LINE_MODEL, 0)},
292197563Sthompsa	{USB_IF_CSI(UICLASS_CDC, UISUBCLASS_NETWORK_CONTROL_MODEL, 0)},
293184610Salfred};
294184610Salfred
295292080SimpDRIVER_MODULE(cdce, uhub, cdce_driver, cdce_devclass, cdce_driver_loaded, 0);
296292080SimpMODULE_VERSION(cdce, 1);
297292080SimpMODULE_DEPEND(cdce, uether, 1, 1, 1);
298292080SimpMODULE_DEPEND(cdce, usb, 1, 1, 1);
299292080SimpMODULE_DEPEND(cdce, ether, 1, 1, 1);
300292080SimpUSB_PNP_DEVICE_INFO(cdce_switch_devs);
301292080SimpUSB_PNP_HOST_INFO(cdce_host_devs);
302292080SimpUSB_PNP_DUAL_INFO(cdce_dual_devs);
303292080Simp
304292080Simpstatic const struct usb_ether_methods cdce_ue_methods = {
305292080Simp	.ue_attach_post = cdce_attach_post,
306292080Simp	.ue_start = cdce_start,
307292080Simp	.ue_init = cdce_init,
308292080Simp	.ue_stop = cdce_stop,
309292080Simp	.ue_setmulti = cdce_setmulti,
310292080Simp	.ue_setpromisc = cdce_setpromisc,
311292080Simp};
312292080Simp
313197563Sthompsa#if CDCE_HAVE_NCM
314197563Sthompsa/*------------------------------------------------------------------------*
315197563Sthompsa *	cdce_ncm_init
316197563Sthompsa *
317197563Sthompsa * Return values:
318197563Sthompsa * 0: Success
319197563Sthompsa * Else: Failure
320197563Sthompsa *------------------------------------------------------------------------*/
321197563Sthompsastatic uint8_t
322197563Sthompsacdce_ncm_init(struct cdce_softc *sc)
323197563Sthompsa{
324197563Sthompsa	struct usb_ncm_parameters temp;
325197563Sthompsa	struct usb_device_request req;
326213809Shselasky	struct usb_ncm_func_descriptor *ufd;
327213809Shselasky	uint8_t value[8];
328197563Sthompsa	int err;
329197563Sthompsa
330213809Shselasky	ufd = usbd_find_descriptor(sc->sc_ue.ue_udev, NULL,
331233774Shselasky	    sc->sc_ifaces_index[1], UDESC_CS_INTERFACE, 0xFF,
332233774Shselasky	    UCDC_NCM_FUNC_DESC_SUBTYPE, 0xFF);
333213809Shselasky
334213809Shselasky	/* verify length of NCM functional descriptor */
335213809Shselasky	if (ufd != NULL) {
336213809Shselasky		if (ufd->bLength < sizeof(*ufd))
337213809Shselasky			ufd = NULL;
338213809Shselasky		else
339213809Shselasky			DPRINTFN(1, "Found NCM functional descriptor.\n");
340213809Shselasky	}
341213809Shselasky
342197563Sthompsa	req.bmRequestType = UT_READ_CLASS_INTERFACE;
343197563Sthompsa	req.bRequest = UCDC_NCM_GET_NTB_PARAMETERS;
344197563Sthompsa	USETW(req.wValue, 0);
345197563Sthompsa	req.wIndex[0] = sc->sc_ifaces_index[1];
346197563Sthompsa	req.wIndex[1] = 0;
347197563Sthompsa	USETW(req.wLength, sizeof(temp));
348197563Sthompsa
349197563Sthompsa	err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req,
350197563Sthompsa	    &temp, 0, NULL, 1000 /* ms */);
351197563Sthompsa	if (err)
352197563Sthompsa		return (1);
353197563Sthompsa
354197563Sthompsa	/* Read correct set of parameters according to device mode */
355197563Sthompsa
356197563Sthompsa	if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
357212133Sthompsa		sc->sc_ncm.rx_max = UGETDW(temp.dwNtbInMaxSize);
358212133Sthompsa		sc->sc_ncm.tx_max = UGETDW(temp.dwNtbOutMaxSize);
359197563Sthompsa		sc->sc_ncm.tx_remainder = UGETW(temp.wNdpOutPayloadRemainder);
360197563Sthompsa		sc->sc_ncm.tx_modulus = UGETW(temp.wNdpOutDivisor);
361197563Sthompsa		sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpOutAlignment);
362213809Shselasky		sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams);
363197563Sthompsa	} else {
364212133Sthompsa		sc->sc_ncm.rx_max = UGETDW(temp.dwNtbOutMaxSize);
365212133Sthompsa		sc->sc_ncm.tx_max = UGETDW(temp.dwNtbInMaxSize);
366197563Sthompsa		sc->sc_ncm.tx_remainder = UGETW(temp.wNdpInPayloadRemainder);
367197563Sthompsa		sc->sc_ncm.tx_modulus = UGETW(temp.wNdpInDivisor);
368197563Sthompsa		sc->sc_ncm.tx_struct_align = UGETW(temp.wNdpInAlignment);
369213809Shselasky		sc->sc_ncm.tx_nframe = UGETW(temp.wNtbOutMaxDatagrams);
370197563Sthompsa	}
371197563Sthompsa
372197563Sthompsa	/* Verify maximum receive length */
373197563Sthompsa
374213809Shselasky	if ((sc->sc_ncm.rx_max < 32) ||
375197563Sthompsa	    (sc->sc_ncm.rx_max > CDCE_NCM_RX_MAXLEN)) {
376197563Sthompsa		DPRINTFN(1, "Using default maximum receive length\n");
377197563Sthompsa		sc->sc_ncm.rx_max = CDCE_NCM_RX_MAXLEN;
378197563Sthompsa	}
379197563Sthompsa
380197563Sthompsa	/* Verify maximum transmit length */
381197563Sthompsa
382213809Shselasky	if ((sc->sc_ncm.tx_max < 32) ||
383197563Sthompsa	    (sc->sc_ncm.tx_max > CDCE_NCM_TX_MAXLEN)) {
384197563Sthompsa		DPRINTFN(1, "Using default maximum transmit length\n");
385197563Sthompsa		sc->sc_ncm.tx_max = CDCE_NCM_TX_MAXLEN;
386197563Sthompsa	}
387197563Sthompsa
388197563Sthompsa	/*
389197563Sthompsa	 * Verify that the structure alignment is:
390197563Sthompsa	 * - power of two
391197563Sthompsa	 * - not greater than the maximum transmit length
392197563Sthompsa	 * - not less than four bytes
393197563Sthompsa	 */
394213809Shselasky	if ((sc->sc_ncm.tx_struct_align < 4) ||
395197563Sthompsa	    (sc->sc_ncm.tx_struct_align !=
396197563Sthompsa	     ((-sc->sc_ncm.tx_struct_align) & sc->sc_ncm.tx_struct_align)) ||
397197563Sthompsa	    (sc->sc_ncm.tx_struct_align >= sc->sc_ncm.tx_max)) {
398197563Sthompsa		DPRINTFN(1, "Using default other alignment: 4 bytes\n");
399197563Sthompsa		sc->sc_ncm.tx_struct_align = 4;
400197563Sthompsa	}
401197563Sthompsa
402197563Sthompsa	/*
403197563Sthompsa	 * Verify that the payload alignment is:
404197563Sthompsa	 * - power of two
405197563Sthompsa	 * - not greater than the maximum transmit length
406197563Sthompsa	 * - not less than four bytes
407197563Sthompsa	 */
408213809Shselasky	if ((sc->sc_ncm.tx_modulus < 4) ||
409197563Sthompsa	    (sc->sc_ncm.tx_modulus !=
410197563Sthompsa	     ((-sc->sc_ncm.tx_modulus) & sc->sc_ncm.tx_modulus)) ||
411197563Sthompsa	    (sc->sc_ncm.tx_modulus >= sc->sc_ncm.tx_max)) {
412197563Sthompsa		DPRINTFN(1, "Using default transmit modulus: 4 bytes\n");
413197563Sthompsa		sc->sc_ncm.tx_modulus = 4;
414197563Sthompsa	}
415197563Sthompsa
416197563Sthompsa	/* Verify that the payload remainder */
417197563Sthompsa
418213809Shselasky	if ((sc->sc_ncm.tx_remainder >= sc->sc_ncm.tx_modulus)) {
419197563Sthompsa		DPRINTFN(1, "Using default transmit remainder: 0 bytes\n");
420197563Sthompsa		sc->sc_ncm.tx_remainder = 0;
421197563Sthompsa	}
422197563Sthompsa
423213809Shselasky	/*
424213809Shselasky	 * Offset the TX remainder so that IP packet payload starts at
425213809Shselasky	 * the tx_modulus. This is not too clear in the specification.
426213809Shselasky	 */
427213809Shselasky
428213809Shselasky	sc->sc_ncm.tx_remainder =
429213809Shselasky	    (sc->sc_ncm.tx_remainder - ETHER_HDR_LEN) &
430213809Shselasky	    (sc->sc_ncm.tx_modulus - 1);
431213809Shselasky
432213809Shselasky	/* Verify max datagrams */
433213809Shselasky
434213809Shselasky	if (sc->sc_ncm.tx_nframe == 0 ||
435213809Shselasky	    sc->sc_ncm.tx_nframe > (CDCE_NCM_SUBFRAMES_MAX - 1)) {
436213809Shselasky		DPRINTFN(1, "Using default max "
437213809Shselasky		    "subframes: %u units\n", CDCE_NCM_SUBFRAMES_MAX - 1);
438213809Shselasky		/* need to reserve one entry for zero padding */
439213809Shselasky		sc->sc_ncm.tx_nframe = (CDCE_NCM_SUBFRAMES_MAX - 1);
440213809Shselasky	}
441213809Shselasky
442197563Sthompsa	/* Additional configuration, will fail in device side mode, which is OK. */
443197563Sthompsa
444197563Sthompsa	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
445197563Sthompsa	req.bRequest = UCDC_NCM_SET_NTB_INPUT_SIZE;
446197563Sthompsa	USETW(req.wValue, 0);
447197563Sthompsa	req.wIndex[0] = sc->sc_ifaces_index[1];
448197563Sthompsa	req.wIndex[1] = 0;
449197563Sthompsa
450213809Shselasky	if (ufd != NULL &&
451213809Shselasky	    (ufd->bmNetworkCapabilities & UCDC_NCM_CAP_MAX_DGRAM)) {
452213809Shselasky		USETW(req.wLength, 8);
453213809Shselasky		USETDW(value, sc->sc_ncm.rx_max);
454213809Shselasky		USETW(value + 4, (CDCE_NCM_SUBFRAMES_MAX - 1));
455213809Shselasky		USETW(value + 6, 0);
456213809Shselasky	} else {
457213809Shselasky		USETW(req.wLength, 4);
458213809Shselasky		USETDW(value, sc->sc_ncm.rx_max);
459213809Shselasky 	}
460213809Shselasky
461197563Sthompsa	err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req,
462197563Sthompsa	    &value, 0, NULL, 1000 /* ms */);
463197563Sthompsa	if (err) {
464197563Sthompsa		DPRINTFN(1, "Setting input size "
465197563Sthompsa		    "to %u failed.\n", sc->sc_ncm.rx_max);
466197563Sthompsa	}
467197563Sthompsa
468197563Sthompsa	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
469197563Sthompsa	req.bRequest = UCDC_NCM_SET_CRC_MODE;
470197563Sthompsa	USETW(req.wValue, 0);	/* no CRC */
471197563Sthompsa	req.wIndex[0] = sc->sc_ifaces_index[1];
472197563Sthompsa	req.wIndex[1] = 0;
473197563Sthompsa	USETW(req.wLength, 0);
474197563Sthompsa
475197563Sthompsa	err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req,
476197563Sthompsa	    NULL, 0, NULL, 1000 /* ms */);
477197563Sthompsa	if (err) {
478197563Sthompsa		DPRINTFN(1, "Setting CRC mode to off failed.\n");
479197563Sthompsa	}
480197563Sthompsa
481197563Sthompsa	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
482197563Sthompsa	req.bRequest = UCDC_NCM_SET_NTB_FORMAT;
483197563Sthompsa	USETW(req.wValue, 0);	/* NTB-16 */
484197563Sthompsa	req.wIndex[0] = sc->sc_ifaces_index[1];
485197563Sthompsa	req.wIndex[1] = 0;
486197563Sthompsa	USETW(req.wLength, 0);
487197563Sthompsa
488197563Sthompsa	err = usbd_do_request_flags(sc->sc_ue.ue_udev, NULL, &req,
489197563Sthompsa	    NULL, 0, NULL, 1000 /* ms */);
490197563Sthompsa	if (err) {
491197563Sthompsa		DPRINTFN(1, "Setting NTB format to 16-bit failed.\n");
492197563Sthompsa	}
493197563Sthompsa
494197563Sthompsa	return (0);		/* success */
495197563Sthompsa}
496197563Sthompsa#endif
497197563Sthompsa
498269584Sn_hibmastatic void
499269584Sn_hibmacdce_test_autoinst(void *arg, struct usb_device *udev,
500269584Sn_hibma    struct usb_attach_arg *uaa)
501269584Sn_hibma{
502269584Sn_hibma	struct usb_interface *iface;
503269584Sn_hibma	struct usb_interface_descriptor *id;
504269584Sn_hibma
505269584Sn_hibma	if (uaa->dev_state != UAA_DEV_READY)
506269584Sn_hibma		return;
507269584Sn_hibma
508269584Sn_hibma	iface = usbd_get_iface(udev, 0);
509269584Sn_hibma	if (iface == NULL)
510269584Sn_hibma		return;
511269584Sn_hibma	id = iface->idesc;
512269584Sn_hibma	if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
513269584Sn_hibma		return;
514269584Sn_hibma	if (usbd_lookup_id_by_uaa(cdce_switch_devs, sizeof(cdce_switch_devs), uaa))
515269584Sn_hibma		return;		/* no device match */
516269584Sn_hibma
517269584Sn_hibma	if (usb_msc_eject(udev, 0, USB_GET_DRIVER_INFO(uaa)) == 0) {
518269584Sn_hibma		/* success, mark the udev as disappearing */
519269584Sn_hibma		uaa->dev_state = UAA_DEV_EJECTING;
520269584Sn_hibma	}
521269584Sn_hibma}
522269584Sn_hibma
523184610Salfredstatic int
524269584Sn_hibmacdce_driver_loaded(struct module *mod, int what, void *arg)
525269584Sn_hibma{
526269584Sn_hibma	switch (what) {
527269584Sn_hibma	case MOD_LOAD:
528269584Sn_hibma		/* register our autoinstall handler */
529269584Sn_hibma		cdce_etag = EVENTHANDLER_REGISTER(usb_dev_configured,
530269584Sn_hibma		    cdce_test_autoinst, NULL, EVENTHANDLER_PRI_ANY);
531269584Sn_hibma		return (0);
532269584Sn_hibma	case MOD_UNLOAD:
533269584Sn_hibma		EVENTHANDLER_DEREGISTER(usb_dev_configured, cdce_etag);
534269584Sn_hibma		return (0);
535269584Sn_hibma	default:
536269584Sn_hibma		return (EOPNOTSUPP);
537269584Sn_hibma	}
538269584Sn_hibma}
539269584Sn_hibma
540269584Sn_hibmastatic int
541184610Salfredcdce_probe(device_t dev)
542184610Salfred{
543192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
544223486Shselasky	int error;
545184610Salfred
546223486Shselasky	error = usbd_lookup_id_by_uaa(cdce_host_devs, sizeof(cdce_host_devs), uaa);
547223486Shselasky	if (error)
548223486Shselasky		error = usbd_lookup_id_by_uaa(cdce_dual_devs, sizeof(cdce_dual_devs), uaa);
549223486Shselasky	return (error);
550184610Salfred}
551184610Salfred
552188412Sthompsastatic void
553192984Sthompsacdce_attach_post(struct usb_ether *ue)
554188412Sthompsa{
555188412Sthompsa	/* no-op */
556188412Sthompsa	return;
557188412Sthompsa}
558188412Sthompsa
559184610Salfredstatic int
560184610Salfredcdce_attach(device_t dev)
561184610Salfred{
562184610Salfred	struct cdce_softc *sc = device_get_softc(dev);
563192984Sthompsa	struct usb_ether *ue = &sc->sc_ue;
564192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
565192984Sthompsa	struct usb_interface *iface;
566192984Sthompsa	const struct usb_cdc_union_descriptor *ud;
567192984Sthompsa	const struct usb_interface_descriptor *id;
568192984Sthompsa	const struct usb_cdc_ethernet_descriptor *ued;
569197563Sthompsa	const struct usb_config *pcfg;
570246037Sjhb	uint32_t seed;
571184610Salfred	int error;
572184610Salfred	uint8_t i;
573197563Sthompsa	uint8_t data_iface_no;
574184610Salfred	char eaddr_str[5 * ETHER_ADDR_LEN];	/* approx */
575184610Salfred
576184610Salfred	sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
577197563Sthompsa	sc->sc_ue.ue_udev = uaa->device;
578184610Salfred
579194228Sthompsa	device_set_usb_desc(dev);
580184610Salfred
581188412Sthompsa	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
582184610Salfred
583194271Sthompsa	ud = usbd_find_descriptor
584184610Salfred	    (uaa->device, NULL, uaa->info.bIfaceIndex,
585233774Shselasky	    UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_UNION, 0xFF);
586184610Salfred
587197563Sthompsa	if ((ud == NULL) || (ud->bLength < sizeof(*ud)) ||
588197563Sthompsa	    (sc->sc_flags & CDCE_FLAG_NO_UNION)) {
589197563Sthompsa		DPRINTFN(1, "No union descriptor!\n");
590197563Sthompsa		sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex;
591197563Sthompsa		sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
592197563Sthompsa		goto alloc_transfers;
593184610Salfred	}
594197563Sthompsa	data_iface_no = ud->bSlaveInterface[0];
595184610Salfred
596184610Salfred	for (i = 0;; i++) {
597184610Salfred
598194228Sthompsa		iface = usbd_get_iface(uaa->device, i);
599184610Salfred
600184610Salfred		if (iface) {
601184610Salfred
602194228Sthompsa			id = usbd_get_interface_descriptor(iface);
603184610Salfred
604197563Sthompsa			if (id && (id->bInterfaceNumber == data_iface_no)) {
605184610Salfred				sc->sc_ifaces_index[0] = i;
606184610Salfred				sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
607194228Sthompsa				usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
608184610Salfred				break;
609184610Salfred			}
610184610Salfred		} else {
611199816Sthompsa			device_printf(dev, "no data interface found\n");
612184610Salfred			goto detach;
613184610Salfred		}
614184610Salfred	}
615184610Salfred
616184610Salfred	/*
617184610Salfred	 * <quote>
618184610Salfred	 *
619184610Salfred	 *  The Data Class interface of a networking device shall have
620184610Salfred	 *  a minimum of two interface settings. The first setting
621184610Salfred	 *  (the default interface setting) includes no endpoints and
622184610Salfred	 *  therefore no networking traffic is exchanged whenever the
623184610Salfred	 *  default interface setting is selected. One or more
624184610Salfred	 *  additional interface settings are used for normal
625184610Salfred	 *  operation, and therefore each includes a pair of endpoints
626184610Salfred	 *  (one IN, and one OUT) to exchange network traffic. Select
627184610Salfred	 *  an alternate interface setting to initialize the network
628184610Salfred	 *  aspects of the device and to enable the exchange of
629184610Salfred	 *  network traffic.
630184610Salfred	 *
631184610Salfred	 * </quote>
632184610Salfred	 *
633184610Salfred	 * Some devices, most notably cable modems, include interface
634184610Salfred	 * settings that have no IN or OUT endpoint, therefore loop
635184610Salfred	 * through the list of all available interface settings
636184610Salfred	 * looking for one with both IN and OUT endpoints.
637184610Salfred	 */
638184610Salfred
639184610Salfredalloc_transfers:
640184610Salfred
641197563Sthompsa	pcfg = cdce_config;	/* Default Configuration */
642197563Sthompsa
643188412Sthompsa	for (i = 0; i != 32; i++) {
644184610Salfred
645197563Sthompsa		error = usbd_set_alt_interface_index(uaa->device,
646197563Sthompsa		    sc->sc_ifaces_index[0], i);
647197563Sthompsa		if (error)
648197563Sthompsa			break;
649197563Sthompsa#if CDCE_HAVE_NCM
650197563Sthompsa		if ((i == 0) && (cdce_ncm_init(sc) == 0))
651197563Sthompsa			pcfg = cdce_ncm_config;
652197563Sthompsa#endif
653197563Sthompsa		error = usbd_transfer_setup(uaa->device,
654197563Sthompsa		    sc->sc_ifaces_index, sc->sc_xfer,
655197563Sthompsa		    pcfg, CDCE_N_TRANSFER, sc, &sc->sc_mtx);
656184610Salfred
657197563Sthompsa		if (error == 0)
658184610Salfred			break;
659184610Salfred	}
660184610Salfred
661197563Sthompsa	if (error || (i == 32)) {
662197563Sthompsa		device_printf(dev, "No valid alternate "
663199816Sthompsa		    "setting found\n");
664197563Sthompsa		goto detach;
665197563Sthompsa	}
666197563Sthompsa
667194271Sthompsa	ued = usbd_find_descriptor
668184610Salfred	    (uaa->device, NULL, uaa->info.bIfaceIndex,
669233774Shselasky	    UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_ENF, 0xFF);
670184610Salfred
671188412Sthompsa	if ((ued == NULL) || (ued->bLength < sizeof(*ued))) {
672184610Salfred		error = USB_ERR_INVAL;
673184610Salfred	} else {
674194228Sthompsa		error = usbd_req_get_string_any(uaa->device, NULL,
675188412Sthompsa		    eaddr_str, sizeof(eaddr_str), ued->iMacAddress);
676184610Salfred	}
677184610Salfred
678184610Salfred	if (error) {
679184610Salfred
680184610Salfred		/* fake MAC address */
681184610Salfred
682184610Salfred		device_printf(dev, "faking MAC address\n");
683246037Sjhb		seed = ticks;
684188412Sthompsa		sc->sc_ue.ue_eaddr[0] = 0x2a;
685246037Sjhb		memcpy(&sc->sc_ue.ue_eaddr[1], &seed, sizeof(uint32_t));
686188412Sthompsa		sc->sc_ue.ue_eaddr[5] = device_get_unit(dev);
687184610Salfred
688184610Salfred	} else {
689184610Salfred
690213809Shselasky		memset(sc->sc_ue.ue_eaddr, 0, sizeof(sc->sc_ue.ue_eaddr));
691184610Salfred
692188412Sthompsa		for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) {
693184610Salfred
694184610Salfred			char c = eaddr_str[i];
695184610Salfred
696188412Sthompsa			if ('0' <= c && c <= '9')
697184610Salfred				c -= '0';
698188412Sthompsa			else if (c != 0)
699184610Salfred				c -= 'A' - 10;
700188412Sthompsa			else
701184610Salfred				break;
702184610Salfred
703184610Salfred			c &= 0xf;
704184610Salfred
705188412Sthompsa			if ((i & 1) == 0)
706184610Salfred				c <<= 4;
707188412Sthompsa			sc->sc_ue.ue_eaddr[i / 2] |= c;
708184610Salfred		}
709184610Salfred
710192499Sthompsa		if (uaa->usb_mode == USB_MODE_DEVICE) {
711184610Salfred			/*
712184610Salfred			 * Do not use the same MAC address like the peer !
713184610Salfred			 */
714188412Sthompsa			sc->sc_ue.ue_eaddr[5] ^= 0xFF;
715184610Salfred		}
716184610Salfred	}
717184610Salfred
718188412Sthompsa	ue->ue_sc = sc;
719188412Sthompsa	ue->ue_dev = dev;
720188412Sthompsa	ue->ue_udev = uaa->device;
721188412Sthompsa	ue->ue_mtx = &sc->sc_mtx;
722188412Sthompsa	ue->ue_methods = &cdce_ue_methods;
723184610Salfred
724194228Sthompsa	error = uether_ifattach(ue);
725188412Sthompsa	if (error) {
726188412Sthompsa		device_printf(dev, "could not attach interface\n");
727184610Salfred		goto detach;
728184610Salfred	}
729184610Salfred	return (0);			/* success */
730184610Salfred
731184610Salfreddetach:
732184610Salfred	cdce_detach(dev);
733184610Salfred	return (ENXIO);			/* failure */
734184610Salfred}
735184610Salfred
736184610Salfredstatic int
737184610Salfredcdce_detach(device_t dev)
738184610Salfred{
739184610Salfred	struct cdce_softc *sc = device_get_softc(dev);
740192984Sthompsa	struct usb_ether *ue = &sc->sc_ue;
741184610Salfred
742184610Salfred	/* stop all USB transfers first */
743194228Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER);
744194228Sthompsa	uether_ifdetach(ue);
745184610Salfred	mtx_destroy(&sc->sc_mtx);
746184610Salfred
747184610Salfred	return (0);
748184610Salfred}
749184610Salfred
750184610Salfredstatic void
751192984Sthompsacdce_start(struct usb_ether *ue)
752184610Salfred{
753194228Sthompsa	struct cdce_softc *sc = uether_getsc(ue);
754184610Salfred
755188412Sthompsa	/*
756188412Sthompsa	 * Start the USB transfers, if not already started:
757188412Sthompsa	 */
758194228Sthompsa	usbd_transfer_start(sc->sc_xfer[CDCE_BULK_TX]);
759194228Sthompsa	usbd_transfer_start(sc->sc_xfer[CDCE_BULK_RX]);
760184610Salfred}
761184610Salfred
762184610Salfredstatic void
763188412Sthompsacdce_free_queue(struct mbuf **ppm, uint8_t n)
764184610Salfred{
765188412Sthompsa	uint8_t x;
766188412Sthompsa	for (x = 0; x != n; x++) {
767188412Sthompsa		if (ppm[x] != NULL) {
768188412Sthompsa			m_freem(ppm[x]);
769188412Sthompsa			ppm[x] = NULL;
770184610Salfred		}
771184610Salfred	}
772184610Salfred}
773184610Salfred
774184610Salfredstatic void
775194677Sthompsacdce_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
776184610Salfred{
777194677Sthompsa	struct cdce_softc *sc = usbd_xfer_softc(xfer);
778194228Sthompsa	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
779184610Salfred	struct mbuf *m;
780184610Salfred	struct mbuf *mt;
781188412Sthompsa	uint32_t crc;
782188412Sthompsa	uint8_t x;
783194677Sthompsa	int actlen, aframes;
784184610Salfred
785194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
786194677Sthompsa
787188412Sthompsa	DPRINTFN(1, "\n");
788188412Sthompsa
789184610Salfred	switch (USB_GET_STATE(xfer)) {
790184610Salfred	case USB_ST_TRANSFERRED:
791194677Sthompsa		DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
792194677Sthompsa		    actlen, aframes);
793184610Salfred
794271832Sglebius		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
795184610Salfred
796188412Sthompsa		/* free all previous TX buffers */
797188412Sthompsa		cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);
798184610Salfred
799188412Sthompsa		/* FALLTHROUGH */
800184610Salfred	case USB_ST_SETUP:
801184610Salfredtr_setup:
802188412Sthompsa		for (x = 0; x != CDCE_FRAMES_MAX; x++) {
803184610Salfred
804184610Salfred			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
805184610Salfred
806188412Sthompsa			if (m == NULL)
807184610Salfred				break;
808188412Sthompsa
809188412Sthompsa			if (sc->sc_flags & CDCE_FLAG_ZAURUS) {
810188412Sthompsa				/*
811188412Sthompsa				 * Zaurus wants a 32-bit CRC appended
812188412Sthompsa				 * to every frame
813188412Sthompsa				 */
814188412Sthompsa
815188412Sthompsa				crc = cdce_m_crc32(m, 0, m->m_pkthdr.len);
816188412Sthompsa				crc = htole32(crc);
817188412Sthompsa
818188412Sthompsa				if (!m_append(m, 4, (void *)&crc)) {
819188412Sthompsa					m_freem(m);
820271832Sglebius					if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
821188412Sthompsa					continue;
822188412Sthompsa				}
823184610Salfred			}
824188412Sthompsa			if (m->m_len != m->m_pkthdr.len) {
825243857Sglebius				mt = m_defrag(m, M_NOWAIT);
826184610Salfred				if (mt == NULL) {
827184610Salfred					m_freem(m);
828271832Sglebius					if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
829184610Salfred					continue;
830184610Salfred				}
831184610Salfred				m = mt;
832184610Salfred			}
833188412Sthompsa			if (m->m_pkthdr.len > MCLBYTES) {
834188412Sthompsa				m->m_pkthdr.len = MCLBYTES;
835188412Sthompsa			}
836188412Sthompsa			sc->sc_tx_buf[x] = m;
837194677Sthompsa			usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
838184610Salfred
839184610Salfred			/*
840188412Sthompsa			 * If there's a BPF listener, bounce a copy of
841188412Sthompsa			 * this frame to him:
842184610Salfred			 */
843184610Salfred			BPF_MTAP(ifp, m);
844184610Salfred		}
845188412Sthompsa		if (x != 0) {
846194677Sthompsa			usbd_xfer_set_frames(xfer, x);
847194677Sthompsa
848194228Sthompsa			usbd_transfer_submit(xfer);
849184610Salfred		}
850184610Salfred		break;
851184610Salfred
852184610Salfred	default:			/* Error */
853184610Salfred		DPRINTFN(11, "transfer error, %s\n",
854194677Sthompsa		    usbd_errstr(error));
855184610Salfred
856188412Sthompsa		/* free all previous TX buffers */
857188412Sthompsa		cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);
858184610Salfred
859188412Sthompsa		/* count output errors */
860271832Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
861184610Salfred
862194677Sthompsa		if (error != USB_ERR_CANCELLED) {
863277420Sbr			if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
864277420Sbr				/* try to clear stall first */
865277420Sbr				usbd_xfer_set_stall(xfer);
866277420Sbr			}
867277422Sbr			goto tr_setup;
868184610Salfred		}
869184610Salfred		break;
870184610Salfred	}
871184610Salfred}
872184610Salfred
873184610Salfredstatic int32_t
874184610Salfredcdce_m_crc32_cb(void *arg, void *src, uint32_t count)
875184610Salfred{
876188412Sthompsa	uint32_t *p_crc = arg;
877184610Salfred
878184610Salfred	*p_crc = crc32_raw(src, count, *p_crc);
879184610Salfred	return (0);
880184610Salfred}
881184610Salfred
882184610Salfredstatic uint32_t
883184610Salfredcdce_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len)
884184610Salfred{
885184610Salfred	uint32_t crc = 0xFFFFFFFF;
886188412Sthompsa	int error;
887184610Salfred
888188412Sthompsa	error = m_apply(m, src_offset, src_len, cdce_m_crc32_cb, &crc);
889184610Salfred	return (crc ^ 0xFFFFFFFF);
890184610Salfred}
891184610Salfred
892184610Salfredstatic void
893192984Sthompsacdce_init(struct usb_ether *ue)
894184610Salfred{
895194228Sthompsa	struct cdce_softc *sc = uether_getsc(ue);
896194228Sthompsa	struct ifnet *ifp = uether_getifp(ue);
897184610Salfred
898188412Sthompsa	CDCE_LOCK_ASSERT(sc, MA_OWNED);
899184610Salfred
900188412Sthompsa	ifp->if_drv_flags |= IFF_DRV_RUNNING;
901184610Salfred
902188412Sthompsa	/* start interrupt transfer */
903194228Sthompsa	usbd_transfer_start(sc->sc_xfer[CDCE_INTR_RX]);
904194228Sthompsa	usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]);
905188412Sthompsa
906277298Sbr	/*
907277298Sbr	 * Stall data write direction, which depends on USB mode.
908277298Sbr	 *
909277298Sbr	 * Some USB host stacks (e.g. Mac OS X) don't clears stall
910277298Sbr	 * bit as it should, so set it in our host mode only.
911277298Sbr	 */
912277298Sbr	if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST)
913277298Sbr		usbd_xfer_set_stall(sc->sc_xfer[CDCE_BULK_TX]);
914188412Sthompsa
915188412Sthompsa	/* start data transfers */
916188412Sthompsa	cdce_start(ue);
917188412Sthompsa}
918188412Sthompsa
919188412Sthompsastatic void
920192984Sthompsacdce_stop(struct usb_ether *ue)
921188412Sthompsa{
922194228Sthompsa	struct cdce_softc *sc = uether_getsc(ue);
923194228Sthompsa	struct ifnet *ifp = uether_getifp(ue);
924188412Sthompsa
925188412Sthompsa	CDCE_LOCK_ASSERT(sc, MA_OWNED);
926188412Sthompsa
927188412Sthompsa	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
928188412Sthompsa
929184610Salfred	/*
930184610Salfred	 * stop all the transfers, if not already stopped:
931184610Salfred	 */
932194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[CDCE_BULK_RX]);
933194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[CDCE_BULK_TX]);
934194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[CDCE_INTR_RX]);
935194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[CDCE_INTR_TX]);
936184610Salfred}
937184610Salfred
938188412Sthompsastatic void
939192984Sthompsacdce_setmulti(struct usb_ether *ue)
940188412Sthompsa{
941188412Sthompsa	/* no-op */
942188412Sthompsa	return;
943188412Sthompsa}
944188412Sthompsa
945188412Sthompsastatic void
946192984Sthompsacdce_setpromisc(struct usb_ether *ue)
947188412Sthompsa{
948188412Sthompsa	/* no-op */
949188412Sthompsa	return;
950188412Sthompsa}
951188412Sthompsa
952184610Salfredstatic int
953184610Salfredcdce_suspend(device_t dev)
954184610Salfred{
955184610Salfred	device_printf(dev, "Suspending\n");
956184610Salfred	return (0);
957184610Salfred}
958184610Salfred
959184610Salfredstatic int
960184610Salfredcdce_resume(device_t dev)
961184610Salfred{
962184610Salfred	device_printf(dev, "Resuming\n");
963184610Salfred	return (0);
964184610Salfred}
965184610Salfred
966184610Salfredstatic void
967194677Sthompsacdce_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
968184610Salfred{
969194677Sthompsa	struct cdce_softc *sc = usbd_xfer_softc(xfer);
970184610Salfred	struct mbuf *m;
971188412Sthompsa	uint8_t x;
972233774Shselasky	int actlen;
973233774Shselasky	int aframes;
974233774Shselasky	int len;
975184610Salfred
976194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
977194677Sthompsa
978184610Salfred	switch (USB_GET_STATE(xfer)) {
979184610Salfred	case USB_ST_TRANSFERRED:
980184610Salfred
981194677Sthompsa		DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
982184610Salfred
983194677Sthompsa		for (x = 0; x != aframes; x++) {
984184610Salfred
985188412Sthompsa			m = sc->sc_rx_buf[x];
986188412Sthompsa			sc->sc_rx_buf[x] = NULL;
987194682Sthompsa			len = usbd_xfer_frame_len(xfer, x);
988188412Sthompsa
989188412Sthompsa			/* Strip off CRC added by Zaurus, if any */
990194677Sthompsa			if ((sc->sc_flags & CDCE_FLAG_ZAURUS) && len >= 14)
991194677Sthompsa				len -= 4;
992188412Sthompsa
993233774Shselasky			if (len < (int)sizeof(struct ether_header)) {
994188412Sthompsa				m_freem(m);
995188412Sthompsa				continue;
996184610Salfred			}
997188412Sthompsa			/* queue up mbuf */
998194677Sthompsa			uether_rxmbuf(&sc->sc_ue, m, len);
999184610Salfred		}
1000184610Salfred
1001188412Sthompsa		/* FALLTHROUGH */
1002184610Salfred	case USB_ST_SETUP:
1003188412Sthompsa		/*
1004188412Sthompsa		 * TODO: Implement support for multi frame transfers,
1005188412Sthompsa		 * when the USB hardware supports it.
1006184610Salfred		 */
1007188412Sthompsa		for (x = 0; x != 1; x++) {
1008188412Sthompsa			if (sc->sc_rx_buf[x] == NULL) {
1009194228Sthompsa				m = uether_newbuf();
1010188412Sthompsa				if (m == NULL)
1011188412Sthompsa					goto tr_stall;
1012188412Sthompsa				sc->sc_rx_buf[x] = m;
1013184610Salfred			} else {
1014188412Sthompsa				m = sc->sc_rx_buf[x];
1015184610Salfred			}
1016184610Salfred
1017194677Sthompsa			usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
1018184610Salfred		}
1019188412Sthompsa		/* set number of frames and start hardware */
1020194677Sthompsa		usbd_xfer_set_frames(xfer, x);
1021194228Sthompsa		usbd_transfer_submit(xfer);
1022188412Sthompsa		/* flush any received frames */
1023194228Sthompsa		uether_rxflush(&sc->sc_ue);
1024184610Salfred		break;
1025184610Salfred
1026184610Salfred	default:			/* Error */
1027184610Salfred		DPRINTF("error = %s\n",
1028194677Sthompsa		    usbd_errstr(error));
1029184610Salfred
1030194677Sthompsa		if (error != USB_ERR_CANCELLED) {
1031188412Sthompsatr_stall:
1032277420Sbr			if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
1033277420Sbr				/* try to clear stall first */
1034277420Sbr				usbd_xfer_set_stall(xfer);
1035277420Sbr				usbd_xfer_set_frames(xfer, 0);
1036277420Sbr				usbd_transfer_submit(xfer);
1037277420Sbr			}
1038188412Sthompsa			break;
1039184610Salfred		}
1040184610Salfred
1041188412Sthompsa		/* need to free the RX-mbufs when we are cancelled */
1042188412Sthompsa		cdce_free_queue(sc->sc_rx_buf, CDCE_FRAMES_MAX);
1043184610Salfred		break;
1044184610Salfred	}
1045184610Salfred}
1046184610Salfred
1047184610Salfredstatic void
1048194677Sthompsacdce_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
1049184610Salfred{
1050277420Sbr	struct cdce_softc *sc = usbd_xfer_softc(xfer);
1051194677Sthompsa	int actlen;
1052194677Sthompsa
1053194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1054194677Sthompsa
1055184610Salfred	switch (USB_GET_STATE(xfer)) {
1056184610Salfred	case USB_ST_TRANSFERRED:
1057184610Salfred
1058194677Sthompsa		DPRINTF("Received %d bytes\n", actlen);
1059184610Salfred
1060184610Salfred		/* TODO: decode some indications */
1061184610Salfred
1062188412Sthompsa		/* FALLTHROUGH */
1063184610Salfred	case USB_ST_SETUP:
1064184610Salfredtr_setup:
1065194677Sthompsa		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
1066194228Sthompsa		usbd_transfer_submit(xfer);
1067184610Salfred		break;
1068184610Salfred
1069184610Salfred	default:			/* Error */
1070194677Sthompsa		if (error != USB_ERR_CANCELLED) {
1071184610Salfred			/* start clear stall */
1072277420Sbr			if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST)
1073277420Sbr				usbd_xfer_set_stall(xfer);
1074277422Sbr			goto tr_setup;
1075184610Salfred		}
1076184610Salfred		break;
1077184610Salfred	}
1078184610Salfred}
1079184610Salfred
1080184610Salfredstatic void
1081194677Sthompsacdce_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
1082184610Salfred{
1083277298Sbr	struct cdce_softc *sc = usbd_xfer_softc(xfer);
1084277298Sbr	struct usb_cdc_notification req;
1085277298Sbr	struct usb_page_cache *pc;
1086277298Sbr	uint32_t speed;
1087194677Sthompsa	int actlen;
1088194677Sthompsa
1089194677Sthompsa	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
1090194677Sthompsa
1091184610Salfred	switch (USB_GET_STATE(xfer)) {
1092184610Salfred	case USB_ST_TRANSFERRED:
1093184610Salfred
1094194677Sthompsa		DPRINTF("Transferred %d bytes\n", actlen);
1095184610Salfred
1096277420Sbr		switch (sc->sc_notify_state) {
1097277422Sbr		case CDCE_NOTIFY_NETWORK_CONNECTION:
1098277420Sbr			sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE;
1099277420Sbr			break;
1100277422Sbr		case CDCE_NOTIFY_SPEED_CHANGE:
1101277420Sbr			sc->sc_notify_state = CDCE_NOTIFY_DONE;
1102277420Sbr			break;
1103277420Sbr		default:
1104277420Sbr			break;
1105277420Sbr		}
1106277420Sbr
1107188412Sthompsa		/* FALLTHROUGH */
1108184610Salfred	case USB_ST_SETUP:
1109184610Salfredtr_setup:
1110277298Sbr		/*
1111277298Sbr		 * Inform host about connection. Required according to USB CDC
1112277298Sbr		 * specification and communicating to Mac OS X USB host stack.
1113277298Sbr		 * Some of the values seems ignored by Mac OS X though.
1114277298Sbr		 */
1115277298Sbr		if (sc->sc_notify_state == CDCE_NOTIFY_NETWORK_CONNECTION) {
1116277298Sbr			req.bmRequestType = UCDC_NOTIFICATION;
1117277298Sbr			req.bNotification = UCDC_N_NETWORK_CONNECTION;
1118277298Sbr			req.wIndex[0] = sc->sc_ifaces_index[1];
1119277298Sbr			req.wIndex[1] = 0;
1120277298Sbr			USETW(req.wValue, 1); /* Connected */
1121277298Sbr			USETW(req.wLength, 0);
1122277298Sbr
1123277298Sbr			pc = usbd_xfer_get_frame(xfer, 0);
1124277298Sbr			usbd_copy_in(pc, 0, &req, sizeof(req));
1125277298Sbr			usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
1126277298Sbr			usbd_xfer_set_frames(xfer, 1);
1127277298Sbr			usbd_transfer_submit(xfer);
1128277298Sbr
1129277298Sbr		} else if (sc->sc_notify_state == CDCE_NOTIFY_SPEED_CHANGE) {
1130277298Sbr			req.bmRequestType = UCDC_NOTIFICATION;
1131277298Sbr			req.bNotification = UCDC_N_CONNECTION_SPEED_CHANGE;
1132277298Sbr			req.wIndex[0] = sc->sc_ifaces_index[1];
1133277298Sbr			req.wIndex[1] = 0;
1134277298Sbr			USETW(req.wValue, 0);
1135277298Sbr			USETW(req.wLength, 8);
1136277298Sbr
1137277298Sbr			/* Peak theoretical bulk trasfer rate in bits/s */
1138277420Sbr			if (usbd_get_speed(sc->sc_ue.ue_udev) != USB_SPEED_FULL)
1139277298Sbr				speed = (13 * 512 * 8 * 1000 * 8);
1140277298Sbr			else
1141277298Sbr				speed = (19 * 64 * 1 * 1000 * 8);
1142277298Sbr
1143277298Sbr			USETDW(req.data + 0, speed); /* Upstream bit rate */
1144277298Sbr			USETDW(req.data + 4, speed); /* Downstream bit rate */
1145277298Sbr
1146277298Sbr			pc = usbd_xfer_get_frame(xfer, 0);
1147277298Sbr			usbd_copy_in(pc, 0, &req, sizeof(req));
1148277298Sbr			usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
1149277298Sbr			usbd_xfer_set_frames(xfer, 1);
1150277298Sbr			usbd_transfer_submit(xfer);
1151277298Sbr		}
1152184610Salfred		break;
1153184610Salfred
1154184610Salfred	default:			/* Error */
1155194677Sthompsa		if (error != USB_ERR_CANCELLED) {
1156277420Sbr			if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
1157277420Sbr				/* start clear stall */
1158277420Sbr				usbd_xfer_set_stall(xfer);
1159277420Sbr			}
1160277422Sbr			goto tr_setup;
1161184610Salfred		}
1162184610Salfred		break;
1163184610Salfred	}
1164184610Salfred}
1165184610Salfred
1166184610Salfredstatic int
1167184610Salfredcdce_handle_request(device_t dev,
1168277420Sbr    const void *preq, void **pptr, uint16_t *plen,
1169195121Sthompsa    uint16_t offset, uint8_t *pstate)
1170184610Salfred{
1171277420Sbr	struct cdce_softc *sc = device_get_softc(dev);
1172277420Sbr	const struct usb_device_request *req = preq;
1173277420Sbr	uint8_t is_complete = *pstate;
1174277420Sbr
1175277420Sbr	/*
1176277420Sbr	 * When Mac OS X resumes after suspending it expects
1177277420Sbr	 * to be notified again after this request.
1178277420Sbr	 */
1179277420Sbr	if (req->bmRequestType == UT_WRITE_CLASS_INTERFACE && \
1180277420Sbr	    req->bRequest == UCDC_NCM_SET_ETHERNET_PACKET_FILTER) {
1181277420Sbr
1182277420Sbr		if (is_complete == 1) {
1183277420Sbr			mtx_lock(&sc->sc_mtx);
1184277420Sbr			sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE;
1185277420Sbr			usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]);
1186277420Sbr			mtx_unlock(&sc->sc_mtx);
1187277420Sbr		}
1188277420Sbr
1189277420Sbr		return (0);
1190277420Sbr	}
1191277420Sbr
1192184610Salfred	return (ENXIO);			/* use builtin handler */
1193184610Salfred}
1194197563Sthompsa
1195197563Sthompsa#if CDCE_HAVE_NCM
1196213809Shselaskystatic void
1197213809Shselaskycdce_ncm_tx_zero(struct usb_page_cache *pc,
1198213809Shselasky    uint32_t start, uint32_t end)
1199213809Shselasky{
1200213809Shselasky	if (start >= CDCE_NCM_TX_MAXLEN)
1201213809Shselasky		return;
1202213809Shselasky	if (end > CDCE_NCM_TX_MAXLEN)
1203213809Shselasky		end = CDCE_NCM_TX_MAXLEN;
1204213809Shselasky
1205213809Shselasky	usbd_frame_zero(pc, start, end - start);
1206213809Shselasky}
1207213809Shselasky
1208197563Sthompsastatic uint8_t
1209197563Sthompsacdce_ncm_fill_tx_frames(struct usb_xfer *xfer, uint8_t index)
1210197563Sthompsa{
1211197563Sthompsa	struct cdce_softc *sc = usbd_xfer_softc(xfer);
1212197563Sthompsa	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
1213197563Sthompsa	struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, index);
1214197563Sthompsa	struct mbuf *m;
1215197563Sthompsa	uint32_t rem;
1216197563Sthompsa	uint32_t offset;
1217197563Sthompsa	uint32_t last_offset;
1218213809Shselasky	uint16_t n;
1219213809Shselasky	uint8_t retval;
1220197563Sthompsa
1221197563Sthompsa	usbd_xfer_set_frame_offset(xfer, index * CDCE_NCM_TX_MAXLEN, index);
1222197563Sthompsa
1223197563Sthompsa	offset = sizeof(sc->sc_ncm.hdr) +
1224197563Sthompsa	    sizeof(sc->sc_ncm.dpt) + sizeof(sc->sc_ncm.dp);
1225197563Sthompsa
1226197563Sthompsa	/* Store last valid offset before alignment */
1227197563Sthompsa	last_offset = offset;
1228197563Sthompsa
1229213809Shselasky	/* Align offset */
1230213809Shselasky	offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder,
1231213809Shselasky	    offset, sc->sc_ncm.tx_modulus);
1232197563Sthompsa
1233213809Shselasky	/* Zero pad */
1234213809Shselasky	cdce_ncm_tx_zero(pc, last_offset, offset);
1235197563Sthompsa
1236213809Shselasky	/* buffer full */
1237213809Shselasky	retval = 2;
1238213809Shselasky
1239213809Shselasky	for (n = 0; n != sc->sc_ncm.tx_nframe; n++) {
1240213809Shselasky
1241197563Sthompsa		/* check if end of transmit buffer is reached */
1242197563Sthompsa
1243197563Sthompsa		if (offset >= sc->sc_ncm.tx_max)
1244197563Sthompsa			break;
1245197563Sthompsa
1246197563Sthompsa		/* compute maximum buffer size */
1247197563Sthompsa
1248197563Sthompsa		rem = sc->sc_ncm.tx_max - offset;
1249197563Sthompsa
1250197563Sthompsa		IFQ_DRV_DEQUEUE(&(ifp->if_snd), m);
1251197563Sthompsa
1252213809Shselasky		if (m == NULL) {
1253213809Shselasky			/* buffer not full */
1254213809Shselasky			retval = 1;
1255197563Sthompsa			break;
1256213809Shselasky		}
1257197563Sthompsa
1258233774Shselasky		if (m->m_pkthdr.len > (int)rem) {
1259197563Sthompsa			if (n == 0) {
1260197563Sthompsa				/* The frame won't fit in our buffer */
1261197563Sthompsa				DPRINTFN(1, "Frame too big to be transmitted!\n");
1262197563Sthompsa				m_freem(m);
1263271832Sglebius				if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1264197563Sthompsa				n--;
1265197563Sthompsa				continue;
1266197563Sthompsa			}
1267197563Sthompsa			/* Wait till next buffer becomes ready */
1268197563Sthompsa			IFQ_DRV_PREPEND(&(ifp->if_snd), m);
1269197563Sthompsa			break;
1270197563Sthompsa		}
1271197563Sthompsa		usbd_m_copy_in(pc, offset, m, 0, m->m_pkthdr.len);
1272197563Sthompsa
1273197563Sthompsa		USETW(sc->sc_ncm.dp[n].wFrameLength, m->m_pkthdr.len);
1274197563Sthompsa		USETW(sc->sc_ncm.dp[n].wFrameIndex, offset);
1275197563Sthompsa
1276197563Sthompsa		/* Update offset */
1277197563Sthompsa		offset += m->m_pkthdr.len;
1278197563Sthompsa
1279197563Sthompsa		/* Store last valid offset before alignment */
1280197563Sthompsa		last_offset = offset;
1281197563Sthompsa
1282213809Shselasky		/* Align offset */
1283213809Shselasky		offset = CDCE_NCM_ALIGN(sc->sc_ncm.tx_remainder,
1284213809Shselasky		    offset, sc->sc_ncm.tx_modulus);
1285197563Sthompsa
1286213809Shselasky		/* Zero pad */
1287213809Shselasky		cdce_ncm_tx_zero(pc, last_offset, offset);
1288213809Shselasky
1289197563Sthompsa		/*
1290197563Sthompsa		 * If there's a BPF listener, bounce a copy
1291197563Sthompsa		 * of this frame to him:
1292197563Sthompsa		 */
1293197563Sthompsa		BPF_MTAP(ifp, m);
1294197563Sthompsa
1295197563Sthompsa		/* Free mbuf */
1296197563Sthompsa
1297197563Sthompsa		m_freem(m);
1298197563Sthompsa
1299197563Sthompsa		/* Pre-increment interface counter */
1300197563Sthompsa
1301271832Sglebius		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
1302197563Sthompsa	}
1303197563Sthompsa
1304197563Sthompsa	if (n == 0)
1305213809Shselasky		return (0);
1306197563Sthompsa
1307197563Sthompsa	rem = (sizeof(sc->sc_ncm.dpt) + (4 * n) + 4);
1308197563Sthompsa
1309197563Sthompsa	USETW(sc->sc_ncm.dpt.wLength, rem);
1310197563Sthompsa
1311197563Sthompsa	/* zero the rest of the data pointer entries */
1312197563Sthompsa	for (; n != CDCE_NCM_SUBFRAMES_MAX; n++) {
1313197563Sthompsa		USETW(sc->sc_ncm.dp[n].wFrameLength, 0);
1314197563Sthompsa		USETW(sc->sc_ncm.dp[n].wFrameIndex, 0);
1315197563Sthompsa	}
1316197563Sthompsa
1317213809Shselasky	offset = last_offset;
1318213809Shselasky
1319213809Shselasky	/* Align offset */
1320213809Shselasky	offset = CDCE_NCM_ALIGN(0, offset, CDCE_NCM_TX_MINLEN);
1321213809Shselasky
1322213809Shselasky	/* Optimise, save bandwidth and force short termination */
1323213809Shselasky	if (offset >= sc->sc_ncm.tx_max)
1324213809Shselasky		offset = sc->sc_ncm.tx_max;
1325213809Shselasky	else
1326213809Shselasky		offset ++;
1327213809Shselasky
1328213809Shselasky	/* Zero pad */
1329213809Shselasky	cdce_ncm_tx_zero(pc, last_offset, offset);
1330213809Shselasky
1331197563Sthompsa	/* set frame length */
1332213809Shselasky	usbd_xfer_set_frame_len(xfer, index, offset);
1333197563Sthompsa
1334197563Sthompsa	/* Fill out 16-bit header */
1335197563Sthompsa	sc->sc_ncm.hdr.dwSignature[0] = 'N';
1336197563Sthompsa	sc->sc_ncm.hdr.dwSignature[1] = 'C';
1337197563Sthompsa	sc->sc_ncm.hdr.dwSignature[2] = 'M';
1338197563Sthompsa	sc->sc_ncm.hdr.dwSignature[3] = 'H';
1339197563Sthompsa	USETW(sc->sc_ncm.hdr.wHeaderLength, sizeof(sc->sc_ncm.hdr));
1340213809Shselasky	USETW(sc->sc_ncm.hdr.wBlockLength, offset);
1341197563Sthompsa	USETW(sc->sc_ncm.hdr.wSequence, sc->sc_ncm.tx_seq);
1342197563Sthompsa	USETW(sc->sc_ncm.hdr.wDptIndex, sizeof(sc->sc_ncm.hdr));
1343197563Sthompsa
1344197563Sthompsa	sc->sc_ncm.tx_seq++;
1345197563Sthompsa
1346197563Sthompsa	/* Fill out 16-bit frame table header */
1347197563Sthompsa	sc->sc_ncm.dpt.dwSignature[0] = 'N';
1348197563Sthompsa	sc->sc_ncm.dpt.dwSignature[1] = 'C';
1349197563Sthompsa	sc->sc_ncm.dpt.dwSignature[2] = 'M';
1350200307Sthompsa	sc->sc_ncm.dpt.dwSignature[3] = '0';
1351197563Sthompsa	USETW(sc->sc_ncm.dpt.wNextNdpIndex, 0);		/* reserved */
1352197563Sthompsa
1353197563Sthompsa	usbd_copy_in(pc, 0, &(sc->sc_ncm.hdr), sizeof(sc->sc_ncm.hdr));
1354197563Sthompsa	usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr), &(sc->sc_ncm.dpt),
1355197563Sthompsa	    sizeof(sc->sc_ncm.dpt));
1356197563Sthompsa	usbd_copy_in(pc, sizeof(sc->sc_ncm.hdr) + sizeof(sc->sc_ncm.dpt),
1357197563Sthompsa	    &(sc->sc_ncm.dp), sizeof(sc->sc_ncm.dp));
1358213809Shselasky	return (retval);
1359197563Sthompsa}
1360197563Sthompsa
1361197563Sthompsastatic void
1362197563Sthompsacdce_ncm_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
1363197563Sthompsa{
1364197563Sthompsa	struct cdce_softc *sc = usbd_xfer_softc(xfer);
1365197563Sthompsa	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
1366197563Sthompsa	uint16_t x;
1367213809Shselasky	uint8_t temp;
1368197563Sthompsa	int actlen;
1369197563Sthompsa	int aframes;
1370197563Sthompsa
1371197563Sthompsa	switch (USB_GET_STATE(xfer)) {
1372197563Sthompsa	case USB_ST_TRANSFERRED:
1373197563Sthompsa
1374197563Sthompsa		usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
1375197563Sthompsa
1376197563Sthompsa		DPRINTFN(10, "transfer complete: "
1377197563Sthompsa		    "%u bytes in %u frames\n", actlen, aframes);
1378197563Sthompsa
1379197563Sthompsa	case USB_ST_SETUP:
1380197563Sthompsa		for (x = 0; x != CDCE_NCM_TX_FRAMES_MAX; x++) {
1381213809Shselasky			temp = cdce_ncm_fill_tx_frames(xfer, x);
1382213809Shselasky			if (temp == 0)
1383197563Sthompsa				break;
1384213809Shselasky			if (temp == 1) {
1385213809Shselasky				x++;
1386213809Shselasky				break;
1387213809Shselasky			}
1388197563Sthompsa		}
1389197563Sthompsa
1390197563Sthompsa		if (x != 0) {
1391213809Shselasky#ifdef USB_DEBUG
1392213809Shselasky			usbd_xfer_set_interval(xfer, cdce_tx_interval);
1393213809Shselasky#endif
1394197563Sthompsa			usbd_xfer_set_frames(xfer, x);
1395197563Sthompsa			usbd_transfer_submit(xfer);
1396197563Sthompsa		}
1397197563Sthompsa		break;
1398197563Sthompsa
1399197563Sthompsa	default:			/* Error */
1400197563Sthompsa		DPRINTFN(10, "Transfer error: %s\n",
1401197563Sthompsa		    usbd_errstr(error));
1402197563Sthompsa
1403197563Sthompsa		/* update error counter */
1404271832Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1405197563Sthompsa
1406197563Sthompsa		if (error != USB_ERR_CANCELLED) {
1407277420Sbr			if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
1408277420Sbr				/* try to clear stall first */
1409277420Sbr				usbd_xfer_set_stall(xfer);
1410277420Sbr				usbd_xfer_set_frames(xfer, 0);
1411277420Sbr				usbd_transfer_submit(xfer);
1412277420Sbr			}
1413197563Sthompsa		}
1414197563Sthompsa		break;
1415197563Sthompsa	}
1416197563Sthompsa}
1417197563Sthompsa
1418197563Sthompsastatic void
1419197563Sthompsacdce_ncm_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
1420197563Sthompsa{
1421197563Sthompsa	struct cdce_softc *sc = usbd_xfer_softc(xfer);
1422197563Sthompsa	struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, 0);
1423197563Sthompsa	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
1424197563Sthompsa	struct mbuf *m;
1425197563Sthompsa	int sumdata;
1426197563Sthompsa	int sumlen;
1427197563Sthompsa	int actlen;
1428197563Sthompsa	int aframes;
1429197563Sthompsa	int temp;
1430197563Sthompsa	int nframes;
1431197563Sthompsa	int x;
1432197563Sthompsa	int offset;
1433197563Sthompsa
1434197563Sthompsa	switch (USB_GET_STATE(xfer)) {
1435197563Sthompsa	case USB_ST_TRANSFERRED:
1436197563Sthompsa
1437197563Sthompsa		usbd_xfer_status(xfer, &actlen, &sumlen, &aframes, NULL);
1438197563Sthompsa
1439197563Sthompsa		DPRINTFN(1, "received %u bytes in %u frames\n",
1440197563Sthompsa		    actlen, aframes);
1441197563Sthompsa
1442233774Shselasky		if (actlen < (int)(sizeof(sc->sc_ncm.hdr) +
1443197563Sthompsa		    sizeof(sc->sc_ncm.dpt))) {
1444197563Sthompsa			DPRINTFN(1, "frame too short\n");
1445200307Sthompsa			goto tr_setup;
1446197563Sthompsa		}
1447197563Sthompsa		usbd_copy_out(pc, 0, &(sc->sc_ncm.hdr),
1448197563Sthompsa		    sizeof(sc->sc_ncm.hdr));
1449197563Sthompsa
1450197563Sthompsa		if ((sc->sc_ncm.hdr.dwSignature[0] != 'N') ||
1451197563Sthompsa		    (sc->sc_ncm.hdr.dwSignature[1] != 'C') ||
1452197563Sthompsa		    (sc->sc_ncm.hdr.dwSignature[2] != 'M') ||
1453197563Sthompsa		    (sc->sc_ncm.hdr.dwSignature[3] != 'H')) {
1454200307Sthompsa			DPRINTFN(1, "invalid HDR signature: "
1455200307Sthompsa			    "0x%02x:0x%02x:0x%02x:0x%02x\n",
1456200307Sthompsa			    sc->sc_ncm.hdr.dwSignature[0],
1457200307Sthompsa			    sc->sc_ncm.hdr.dwSignature[1],
1458200307Sthompsa			    sc->sc_ncm.hdr.dwSignature[2],
1459200307Sthompsa			    sc->sc_ncm.hdr.dwSignature[3]);
1460197563Sthompsa			goto tr_stall;
1461197563Sthompsa		}
1462197563Sthompsa		temp = UGETW(sc->sc_ncm.hdr.wBlockLength);
1463197563Sthompsa		if (temp > sumlen) {
1464197563Sthompsa			DPRINTFN(1, "unsupported block length %u/%u\n",
1465197563Sthompsa			    temp, sumlen);
1466197563Sthompsa			goto tr_stall;
1467197563Sthompsa		}
1468197563Sthompsa		temp = UGETW(sc->sc_ncm.hdr.wDptIndex);
1469233774Shselasky		if ((int)(temp + sizeof(sc->sc_ncm.dpt)) > actlen) {
1470200307Sthompsa			DPRINTFN(1, "invalid DPT index: 0x%04x\n", temp);
1471197563Sthompsa			goto tr_stall;
1472197563Sthompsa		}
1473197563Sthompsa		usbd_copy_out(pc, temp, &(sc->sc_ncm.dpt),
1474197563Sthompsa		    sizeof(sc->sc_ncm.dpt));
1475197563Sthompsa
1476197563Sthompsa		if ((sc->sc_ncm.dpt.dwSignature[0] != 'N') ||
1477197563Sthompsa		    (sc->sc_ncm.dpt.dwSignature[1] != 'C') ||
1478197563Sthompsa		    (sc->sc_ncm.dpt.dwSignature[2] != 'M') ||
1479200307Sthompsa		    (sc->sc_ncm.dpt.dwSignature[3] != '0')) {
1480200307Sthompsa			DPRINTFN(1, "invalid DPT signature"
1481200307Sthompsa			    "0x%02x:0x%02x:0x%02x:0x%02x\n",
1482200307Sthompsa			    sc->sc_ncm.dpt.dwSignature[0],
1483200307Sthompsa			    sc->sc_ncm.dpt.dwSignature[1],
1484200307Sthompsa			    sc->sc_ncm.dpt.dwSignature[2],
1485200307Sthompsa			    sc->sc_ncm.dpt.dwSignature[3]);
1486197563Sthompsa			goto tr_stall;
1487197563Sthompsa		}
1488197563Sthompsa		nframes = UGETW(sc->sc_ncm.dpt.wLength) / 4;
1489197563Sthompsa
1490197563Sthompsa		/* Subtract size of header and last zero padded entry */
1491197563Sthompsa		if (nframes >= (2 + 1))
1492197563Sthompsa			nframes -= (2 + 1);
1493197563Sthompsa		else
1494197563Sthompsa			nframes = 0;
1495197563Sthompsa
1496197563Sthompsa		DPRINTFN(1, "nframes = %u\n", nframes);
1497197563Sthompsa
1498197563Sthompsa		temp += sizeof(sc->sc_ncm.dpt);
1499197563Sthompsa
1500197563Sthompsa		if ((temp + (4 * nframes)) > actlen)
1501197563Sthompsa			goto tr_stall;
1502197563Sthompsa
1503197563Sthompsa		if (nframes > CDCE_NCM_SUBFRAMES_MAX) {
1504197563Sthompsa			DPRINTFN(1, "Truncating number of frames from %u to %u\n",
1505197563Sthompsa			    nframes, CDCE_NCM_SUBFRAMES_MAX);
1506197563Sthompsa			nframes = CDCE_NCM_SUBFRAMES_MAX;
1507197563Sthompsa		}
1508197563Sthompsa		usbd_copy_out(pc, temp, &(sc->sc_ncm.dp), (4 * nframes));
1509197563Sthompsa
1510197563Sthompsa		sumdata = 0;
1511197563Sthompsa
1512197563Sthompsa		for (x = 0; x != nframes; x++) {
1513197563Sthompsa
1514197563Sthompsa			offset = UGETW(sc->sc_ncm.dp[x].wFrameIndex);
1515197563Sthompsa			temp = UGETW(sc->sc_ncm.dp[x].wFrameLength);
1516198153Sthompsa
1517198153Sthompsa			if ((offset == 0) ||
1518233774Shselasky			    (temp < (int)sizeof(struct ether_header)) ||
1519198153Sthompsa			    (temp > (MCLBYTES - ETHER_ALIGN))) {
1520198153Sthompsa				DPRINTFN(1, "NULL frame detected at %d\n", x);
1521197563Sthompsa				m = NULL;
1522198153Sthompsa				/* silently ignore this frame */
1523198153Sthompsa				continue;
1524198153Sthompsa			} else if ((offset + temp) > actlen) {
1525198153Sthompsa				DPRINTFN(1, "invalid frame "
1526198153Sthompsa				    "detected at %d\n", x);
1527198153Sthompsa				m = NULL;
1528198153Sthompsa				/* silently ignore this frame */
1529198153Sthompsa				continue;
1530233774Shselasky			} else if (temp > (int)(MHLEN - ETHER_ALIGN)) {
1531243857Sglebius				m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1532197563Sthompsa			} else {
1533243857Sglebius				m = m_gethdr(M_NOWAIT, MT_DATA);
1534197563Sthompsa			}
1535197563Sthompsa
1536197563Sthompsa			DPRINTFN(16, "frame %u, offset = %u, length = %u \n",
1537197563Sthompsa			    x, offset, temp);
1538197563Sthompsa
1539197563Sthompsa			/* check if we have a buffer */
1540197563Sthompsa			if (m) {
1541290441Shselasky				m->m_len = m->m_pkthdr.len = temp + ETHER_ALIGN;
1542197563Sthompsa				m_adj(m, ETHER_ALIGN);
1543197563Sthompsa
1544197563Sthompsa				usbd_copy_out(pc, offset, m->m_data, temp);
1545197563Sthompsa
1546197563Sthompsa				/* enqueue */
1547197563Sthompsa				uether_rxmbuf(&sc->sc_ue, m, temp);
1548197563Sthompsa
1549197563Sthompsa				sumdata += temp;
1550197563Sthompsa			} else {
1551271832Sglebius				if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
1552197563Sthompsa			}
1553197563Sthompsa		}
1554197563Sthompsa
1555197563Sthompsa		DPRINTFN(1, "Efficiency: %u/%u bytes\n", sumdata, actlen);
1556197563Sthompsa
1557197563Sthompsa	case USB_ST_SETUP:
1558200307Sthompsatr_setup:
1559197563Sthompsa		usbd_xfer_set_frame_len(xfer, 0, sc->sc_ncm.rx_max);
1560197563Sthompsa		usbd_xfer_set_frames(xfer, 1);
1561197563Sthompsa		usbd_transfer_submit(xfer);
1562197563Sthompsa		uether_rxflush(&sc->sc_ue);	/* must be last */
1563197563Sthompsa		break;
1564197563Sthompsa
1565197563Sthompsa	default:			/* Error */
1566197563Sthompsa		DPRINTFN(1, "error = %s\n",
1567197563Sthompsa		    usbd_errstr(error));
1568197563Sthompsa
1569197563Sthompsa		if (error != USB_ERR_CANCELLED) {
1570197563Sthompsatr_stall:
1571277420Sbr			if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
1572277420Sbr				/* try to clear stall first */
1573277420Sbr				usbd_xfer_set_stall(xfer);
1574277420Sbr				usbd_xfer_set_frames(xfer, 0);
1575277420Sbr				usbd_transfer_submit(xfer);
1576277420Sbr			}
1577197563Sthompsa		}
1578197563Sthompsa		break;
1579197563Sthompsa	}
1580197563Sthompsa}
1581197563Sthompsa#endif
1582