if_ipheth.c revision 253670
1193326Sed/*-
2193326Sed * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3193326Sed * Copyright (c) 2009 Diego Giagio. All rights reserved.
4193326Sed *
5193326Sed * Redistribution and use in source and binary forms, with or without
6193326Sed * modification, are permitted provided that the following conditions
7193326Sed * are met:
8193326Sed * 1. Redistributions of source code must retain the above copyright
9193326Sed *    notice, this list of conditions and the following disclaimer.
10193326Sed * 2. Redistributions in binary form must reproduce the above copyright
11193326Sed *    notice, this list of conditions and the following disclaimer in the
12193326Sed *    documentation and/or other materials provided with the distribution.
13193326Sed *
14212904Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15193326Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16193326Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17193326Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18224145Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19207619Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20249423Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21249423Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22249423Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23249423Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24249423Sdim * SUCH DAMAGE.
25249423Sdim */
26249423Sdim
27249423Sdim/*
28193326Sed * Thanks to Diego Giagio for figuring out the programming details for
29193326Sed * the Apple iPhone Ethernet driver.
30193326Sed */
31218893Sdim
32226633Sdim#include <sys/cdefs.h>
33193326Sed__FBSDID("$FreeBSD: head/sys/dev/usb/net/if_ipheth.c 253670 2013-07-26 09:58:56Z hselasky $");
34212904Sdim
35212904Sdim#include <sys/stdint.h>
36212904Sdim#include <sys/stddef.h>
37193326Sed#include <sys/param.h>
38193326Sed#include <sys/queue.h>
39193326Sed#include <sys/types.h>
40193326Sed#include <sys/systm.h>
41193326Sed#include <sys/kernel.h>
42193326Sed#include <sys/bus.h>
43193326Sed#include <sys/module.h>
44198092Srdivacky#include <sys/lock.h>
45193326Sed#include <sys/mutex.h>
46193326Sed#include <sys/condvar.h>
47193326Sed#include <sys/sysctl.h>
48234353Sdim#include <sys/sx.h>
49226633Sdim#include <sys/unistd.h>
50198092Srdivacky#include <sys/callout.h>
51193326Sed#include <sys/malloc.h>
52193326Sed#include <sys/priv.h>
53198092Srdivacky
54226633Sdim#include <dev/usb/usb.h>
55226633Sdim#include <dev/usb/usbdi.h>
56193326Sed#include <dev/usb/usbdi_util.h>
57193326Sed#include "usbdevs.h"
58193326Sed
59193326Sed#define	USB_DEBUG_VAR ipheth_debug
60198092Srdivacky#include <dev/usb/usb_debug.h>
61212904Sdim#include <dev/usb/usb_process.h>
62212904Sdim
63198092Srdivacky#include <dev/usb/net/usb_ethernet.h>
64193326Sed#include <dev/usb/net/if_iphethvar.h>
65193326Sed
66193326Sedstatic device_probe_t ipheth_probe;
67198092Srdivackystatic device_attach_t ipheth_attach;
68193326Sedstatic device_detach_t ipheth_detach;
69193326Sed
70224145Sdimstatic usb_callback_t ipheth_bulk_write_callback;
71226633Sdimstatic usb_callback_t ipheth_bulk_read_callback;
72193326Sed
73193326Sedstatic uether_fn_t ipheth_attach_post;
74193326Sedstatic uether_fn_t ipheth_tick;
75234353Sdimstatic uether_fn_t ipheth_init;
76234353Sdimstatic uether_fn_t ipheth_stop;
77234353Sdimstatic uether_fn_t ipheth_start;
78198092Srdivackystatic uether_fn_t ipheth_setmulti;
79234353Sdimstatic uether_fn_t ipheth_setpromisc;
80193326Sed
81193326Sed#ifdef USB_DEBUG
82193326Sedstatic int ipheth_debug = 0;
83193326Sed
84193326Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet");
85193326SedSYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RW, &ipheth_debug, 0, "Debug level");
86193326Sed#endif
87193326Sed
88193326Sedstatic const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = {
89193326Sed
90198092Srdivacky	[IPHETH_BULK_RX] = {
91234353Sdim		.type = UE_BULK,
92218893Sdim		.endpoint = UE_ADDR_ANY,
93234353Sdim		.direction = UE_DIR_RX,
94218893Sdim		.frames = IPHETH_RX_FRAMES_MAX,
95218893Sdim		.bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES),
96218893Sdim		.flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
97218893Sdim		.callback = ipheth_bulk_read_callback,
98218893Sdim		.timeout = 0,		/* no timeout */
99218893Sdim	},
100234353Sdim
101207619Srdivacky	[IPHETH_BULK_TX] = {
102207619Srdivacky		.type = UE_BULK,
103207619Srdivacky		.endpoint = UE_ADDR_ANY,
104207619Srdivacky		.direction = UE_DIR_TX,
105207619Srdivacky		.frames = IPHETH_TX_FRAMES_MAX,
106207619Srdivacky		.bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE),
107207619Srdivacky		.flags = {.force_short_xfer = 1,},
108207619Srdivacky		.callback = ipheth_bulk_write_callback,
109207619Srdivacky		.timeout = IPHETH_TX_TIMEOUT,
110207619Srdivacky	},
111207619Srdivacky};
112207619Srdivacky
113193326Sedstatic device_method_t ipheth_methods[] = {
114239462Sdim	/* Device interface */
115234353Sdim	DEVMETHOD(device_probe, ipheth_probe),
116207619Srdivacky	DEVMETHOD(device_attach, ipheth_attach),
117193326Sed	DEVMETHOD(device_detach, ipheth_detach),
118193326Sed
119193326Sed	DEVMETHOD_END
120198092Srdivacky};
121193326Sed
122234353Sdimstatic driver_t ipheth_driver = {
123234353Sdim	.name = "ipheth",
124234353Sdim	.methods = ipheth_methods,
125234353Sdim	.size = sizeof(struct ipheth_softc),
126234353Sdim};
127234353Sdim
128234353Sdimstatic devclass_t ipheth_devclass;
129234353Sdim
130234353SdimDRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0);
131234353SdimMODULE_VERSION(ipheth, 1);
132234353SdimMODULE_DEPEND(ipheth, uether, 1, 1, 1);
133234353SdimMODULE_DEPEND(ipheth, usb, 1, 1, 1);
134234353SdimMODULE_DEPEND(ipheth, ether, 1, 1, 1);
135234353Sdim
136234353Sdimstatic const struct usb_ether_methods ipheth_ue_methods = {
137193326Sed	.ue_attach_post = ipheth_attach_post,
138193326Sed	.ue_start = ipheth_start,
139198092Srdivacky	.ue_init = ipheth_init,
140234353Sdim	.ue_tick = ipheth_tick,
141193326Sed	.ue_stop = ipheth_stop,
142193326Sed	.ue_setmulti = ipheth_setmulti,
143239462Sdim	.ue_setpromisc = ipheth_setpromisc,
144239462Sdim};
145239462Sdim
146239462Sdim#define	IPHETH_ID(v,p,c,sc,pt) \
147239462Sdim    USB_VENDOR(v), USB_PRODUCT(p), \
148239462Sdim    USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \
149239462Sdim    USB_IFACE_PROTOCOL(pt)
150239462Sdim
151239462Sdimstatic const STRUCT_USB_HOST_ID ipheth_devs[] = {
152239462Sdim#if 0
153239462Sdim	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE,
154239462Sdim	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
155239462Sdim	    IPHETH_USBINTF_PROTO)},
156239462Sdim	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G,
157239462Sdim	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
158239462Sdim	    IPHETH_USBINTF_PROTO)},
159239462Sdim	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS,
160239462Sdim	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
161239462Sdim	    IPHETH_USBINTF_PROTO)},
162239462Sdim	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4,
163239462Sdim	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
164239462Sdim	    IPHETH_USBINTF_PROTO)},
165239462Sdim	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S,
166239462Sdim	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
167234353Sdim	    IPHETH_USBINTF_PROTO)},
168234353Sdim	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5,
169234353Sdim	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
170239462Sdim	    IPHETH_USBINTF_PROTO)},
171239462Sdim#else
172239462Sdim	/* product agnostic interface match */
173249423Sdim	{USB_VENDOR(USB_VENDOR_APPLE),
174249423Sdim	 USB_IFACE_CLASS(IPHETH_USBINTF_CLASS),
175249423Sdim	 USB_IFACE_SUBCLASS(IPHETH_USBINTF_SUBCLASS),
176234353Sdim	 USB_IFACE_PROTOCOL(IPHETH_USBINTF_PROTO)},
177239462Sdim#endif
178239462Sdim};
179239462Sdim
180239462Sdimstatic int
181234353Sdimipheth_get_mac_addr(struct ipheth_softc *sc)
182234353Sdim{
183239462Sdim	struct usb_device_request req;
184234353Sdim	int error;
185234353Sdim
186234353Sdim	req.bmRequestType = UT_READ_VENDOR_DEVICE;
187234353Sdim	req.bRequest = IPHETH_CMD_GET_MACADDR;
188234353Sdim	req.wValue[0] = 0;
189234353Sdim	req.wValue[1] = 0;
190234353Sdim	req.wIndex[0] = sc->sc_iface_no;
191239462Sdim	req.wIndex[1] = 0;
192239462Sdim	req.wLength[0] = ETHER_ADDR_LEN;
193239462Sdim	req.wLength[1] = 0;
194239462Sdim
195239462Sdim	error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data);
196239462Sdim
197239462Sdim	if (error)
198239462Sdim		return (error);
199239462Sdim
200239462Sdim	memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN);
201239462Sdim
202239462Sdim	return (0);
203239462Sdim}
204239462Sdim
205239462Sdimstatic int
206239462Sdimipheth_probe(device_t dev)
207239462Sdim{
208239462Sdim	struct usb_attach_arg *uaa = device_get_ivars(dev);
209239462Sdim
210239462Sdim	if (uaa->usb_mode != USB_MODE_HOST)
211239462Sdim		return (ENXIO);
212239462Sdim
213239462Sdim	return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa));
214239462Sdim}
215239462Sdim
216239462Sdimstatic int
217239462Sdimipheth_attach(device_t dev)
218239462Sdim{
219239462Sdim	struct ipheth_softc *sc = device_get_softc(dev);
220239462Sdim	struct usb_ether *ue = &sc->sc_ue;
221239462Sdim	struct usb_attach_arg *uaa = device_get_ivars(dev);
222239462Sdim	int error;
223234353Sdim
224239462Sdim	sc->sc_iface_no = uaa->info.bIfaceIndex;
225234353Sdim
226239462Sdim	device_set_usb_desc(dev);
227234353Sdim
228239462Sdim	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
229239462Sdim
230239462Sdim	error = usbd_set_alt_interface_index(uaa->device,
231239462Sdim	    uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM);
232243830Sdim	if (error) {
233239462Sdim		device_printf(dev, "Cannot set alternate setting\n");
234239462Sdim		goto detach;
235239462Sdim	}
236239462Sdim	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no,
237234353Sdim	    sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx);
238234353Sdim	if (error) {
239239462Sdim		device_printf(dev, "Cannot setup USB transfers\n");
240239462Sdim		goto detach;
241249423Sdim	}
242251662Sdim	ue->ue_sc = sc;
243234353Sdim	ue->ue_dev = dev;
244234353Sdim	ue->ue_udev = uaa->device;
245239462Sdim	ue->ue_mtx = &sc->sc_mtx;
246234353Sdim	ue->ue_methods = &ipheth_ue_methods;
247234353Sdim
248234353Sdim	error = ipheth_get_mac_addr(sc);
249234353Sdim	if (error) {
250234353Sdim		device_printf(dev, "Cannot get MAC address\n");
251234353Sdim		goto detach;
252234353Sdim	}
253234353Sdim
254234353Sdim	error = uether_ifattach(ue);
255239462Sdim	if (error) {
256239462Sdim		device_printf(dev, "could not attach interface\n");
257234353Sdim		goto detach;
258234353Sdim	}
259234353Sdim	return (0);			/* success */
260234353Sdim
261234353Sdimdetach:
262234353Sdim	ipheth_detach(dev);
263234353Sdim	return (ENXIO);			/* failure */
264234353Sdim}
265234353Sdim
266234353Sdimstatic int
267234353Sdimipheth_detach(device_t dev)
268234353Sdim{
269234353Sdim	struct ipheth_softc *sc = device_get_softc(dev);
270234353Sdim	struct usb_ether *ue = &sc->sc_ue;
271234353Sdim
272234353Sdim	/* stop all USB transfers first */
273234353Sdim	usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER);
274234353Sdim
275234353Sdim	uether_ifdetach(ue);
276234353Sdim
277234353Sdim	mtx_destroy(&sc->sc_mtx);
278234353Sdim
279234353Sdim	return (0);
280234353Sdim}
281234353Sdim
282234353Sdimstatic void
283234353Sdimipheth_start(struct usb_ether *ue)
284239462Sdim{
285239462Sdim	struct ipheth_softc *sc = uether_getsc(ue);
286239462Sdim
287234353Sdim	/*
288234353Sdim	 * Start the USB transfers, if not already started:
289234353Sdim	 */
290234353Sdim	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]);
291239462Sdim	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]);
292239462Sdim}
293239462Sdim
294239462Sdimstatic void
295239462Sdimipheth_stop(struct usb_ether *ue)
296239462Sdim{
297234353Sdim	struct ipheth_softc *sc = uether_getsc(ue);
298234353Sdim
299234353Sdim	/*
300234353Sdim	 * Stop the USB transfers, if not already stopped:
301239462Sdim	 */
302234353Sdim	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]);
303239462Sdim	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]);
304239462Sdim}
305234353Sdim
306234353Sdimstatic void
307234353Sdimipheth_tick(struct usb_ether *ue)
308234353Sdim{
309234353Sdim	struct ipheth_softc *sc = uether_getsc(ue);
310234353Sdim	struct usb_device_request req;
311234353Sdim	int error;
312234353Sdim
313234353Sdim	req.bmRequestType = UT_READ_VENDOR_DEVICE;
314234353Sdim	req.bRequest = IPHETH_CMD_CARRIER_CHECK;
315234353Sdim	req.wValue[0] = 0;
316234353Sdim	req.wValue[1] = 0;
317234353Sdim	req.wIndex[0] = sc->sc_iface_no;
318234353Sdim	req.wIndex[1] = 0;
319234353Sdim	req.wLength[0] = IPHETH_CTRL_BUF_SIZE;
320234353Sdim	req.wLength[1] = 0;
321234353Sdim
322234353Sdim	error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT);
323234353Sdim
324234353Sdim	if (error)
325234353Sdim		return;
326234353Sdim
327234353Sdim	sc->sc_carrier_on =
328234353Sdim	    (sc->sc_data[0] == IPHETH_CARRIER_ON);
329234353Sdim}
330234353Sdim
331234353Sdimstatic void
332234353Sdimipheth_attach_post(struct usb_ether *ue)
333234353Sdim{
334234353Sdim
335234353Sdim}
336234353Sdim
337234353Sdimstatic void
338234353Sdimipheth_init(struct usb_ether *ue)
339234353Sdim{
340234353Sdim	struct ipheth_softc *sc = uether_getsc(ue);
341239462Sdim	struct ifnet *ifp = uether_getifp(ue);
342239462Sdim
343234353Sdim	IPHETH_LOCK_ASSERT(sc, MA_OWNED);
344239462Sdim
345239462Sdim	ifp->if_drv_flags |= IFF_DRV_RUNNING;
346251662Sdim
347234353Sdim	/* stall data write direction, which depends on USB mode */
348243830Sdim	usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]);
349234353Sdim
350234353Sdim	/* start data transfers */
351234353Sdim	ipheth_start(ue);
352234353Sdim}
353234353Sdim
354234353Sdimstatic void
355234353Sdimipheth_setmulti(struct usb_ether *ue)
356234353Sdim{
357234353Sdim
358234353Sdim}
359234353Sdim
360234353Sdimstatic void
361234353Sdimipheth_setpromisc(struct usb_ether *ue)
362234353Sdim{
363234353Sdim
364234353Sdim}
365234353Sdim
366234353Sdimstatic void
367234353Sdimipheth_free_queue(struct mbuf **ppm, uint8_t n)
368234353Sdim{
369234353Sdim	uint8_t x;
370234353Sdim
371234353Sdim	for (x = 0; x != n; x++) {
372234353Sdim		if (ppm[x] != NULL) {
373234353Sdim			m_freem(ppm[x]);
374234353Sdim			ppm[x] = NULL;
375234353Sdim		}
376234353Sdim	}
377234353Sdim}
378234353Sdim
379234353Sdimstatic void
380234353Sdimipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
381234353Sdim{
382234353Sdim	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
383234353Sdim	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
384234353Sdim	struct usb_page_cache *pc;
385234353Sdim	struct mbuf *m;
386234353Sdim	uint8_t x;
387234353Sdim	int actlen;
388234353Sdim	int aframes;
389234353Sdim
390234353Sdim	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
391234353Sdim
392234353Sdim	DPRINTFN(1, "\n");
393234353Sdim
394234353Sdim	switch (USB_GET_STATE(xfer)) {
395234353Sdim	case USB_ST_TRANSFERRED:
396234353Sdim		DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
397234353Sdim		    actlen, aframes);
398234353Sdim
399234353Sdim		ifp->if_opackets++;
400234353Sdim
401234353Sdim		/* free all previous TX buffers */
402234353Sdim		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
403234353Sdim
404234353Sdim		/* FALLTHROUGH */
405234353Sdim	case USB_ST_SETUP:
406234353Sdimtr_setup:
407234353Sdim		for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) {
408234353Sdim
409234353Sdim			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
410234353Sdim
411234353Sdim			if (m == NULL)
412234353Sdim				break;
413234353Sdim
414234353Sdim			usbd_xfer_set_frame_offset(xfer,
415234353Sdim			    x * IPHETH_BUF_SIZE, x);
416234353Sdim
417234353Sdim			pc = usbd_xfer_get_frame(xfer, x);
418234353Sdim
419234353Sdim			sc->sc_tx_buf[x] = m;
420239462Sdim
421239462Sdim			if (m->m_pkthdr.len > IPHETH_BUF_SIZE)
422239462Sdim				m->m_pkthdr.len = IPHETH_BUF_SIZE;
423239462Sdim
424239462Sdim			usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
425239462Sdim
426239462Sdim			usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE);
427239462Sdim
428239462Sdim			if (IPHETH_BUF_SIZE != m->m_pkthdr.len) {
429239462Sdim				usbd_frame_zero(pc, m->m_pkthdr.len,
430239462Sdim					IPHETH_BUF_SIZE - m->m_pkthdr.len);
431239462Sdim			}
432239462Sdim
433239462Sdim			/*
434239462Sdim			 * If there's a BPF listener, bounce a copy of
435239462Sdim			 * this frame to him:
436239462Sdim			 */
437239462Sdim			BPF_MTAP(ifp, m);
438239462Sdim		}
439239462Sdim		if (x != 0) {
440239462Sdim			usbd_xfer_set_frames(xfer, x);
441239462Sdim
442239462Sdim			usbd_transfer_submit(xfer);
443239462Sdim		}
444239462Sdim		break;
445239462Sdim
446239462Sdim	default:			/* Error */
447239462Sdim		DPRINTFN(11, "transfer error, %s\n",
448239462Sdim		    usbd_errstr(error));
449239462Sdim
450239462Sdim		/* free all previous TX buffers */
451239462Sdim		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
452239462Sdim
453239462Sdim		/* count output errors */
454239462Sdim		ifp->if_oerrors++;
455239462Sdim
456239462Sdim		if (error != USB_ERR_CANCELLED) {
457239462Sdim			/* try to clear stall first */
458239462Sdim			usbd_xfer_set_stall(xfer);
459239462Sdim			goto tr_setup;
460239462Sdim		}
461239462Sdim		break;
462239462Sdim	}
463239462Sdim}
464239462Sdim
465239462Sdimstatic void
466239462Sdimipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
467239462Sdim{
468239462Sdim	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
469239462Sdim	struct mbuf *m;
470239462Sdim	uint8_t x;
471239462Sdim	int actlen;
472239462Sdim	int aframes;
473239462Sdim	int len;
474239462Sdim
475239462Sdim	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
476239462Sdim
477239462Sdim	switch (USB_GET_STATE(xfer)) {
478239462Sdim	case USB_ST_TRANSFERRED:
479239462Sdim
480243830Sdim		DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
481239462Sdim
482239462Sdim		for (x = 0; x != aframes; x++) {
483239462Sdim
484239462Sdim			m = sc->sc_rx_buf[x];
485239462Sdim			sc->sc_rx_buf[x] = NULL;
486239462Sdim			len = usbd_xfer_frame_len(xfer, x);
487239462Sdim
488239462Sdim			if (len < (int)(sizeof(struct ether_header) +
489239462Sdim			    IPHETH_RX_ADJ)) {
490239462Sdim				m_freem(m);
491239462Sdim				continue;
492249423Sdim			}
493251662Sdim
494239462Sdim			m_adj(m, IPHETH_RX_ADJ);
495239462Sdim
496239462Sdim			/* queue up mbuf */
497239462Sdim			uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ);
498239462Sdim		}
499239462Sdim
500239462Sdim		/* FALLTHROUGH */
501239462Sdim	case USB_ST_SETUP:
502239462Sdim
503239462Sdim		for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) {
504239462Sdim			if (sc->sc_rx_buf[x] == NULL) {
505239462Sdim				m = uether_newbuf();
506239462Sdim				if (m == NULL)
507239462Sdim					goto tr_stall;
508239462Sdim
509239462Sdim				/* cancel alignment for ethernet */
510239462Sdim				m_adj(m, ETHER_ALIGN);
511239462Sdim
512239462Sdim				sc->sc_rx_buf[x] = m;
513239462Sdim			} else {
514239462Sdim				m = sc->sc_rx_buf[x];
515239462Sdim			}
516239462Sdim
517239462Sdim			usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
518239462Sdim		}
519239462Sdim		/* set number of frames and start hardware */
520239462Sdim		usbd_xfer_set_frames(xfer, x);
521239462Sdim		usbd_transfer_submit(xfer);
522239462Sdim		/* flush any received frames */
523239462Sdim		uether_rxflush(&sc->sc_ue);
524239462Sdim		break;
525239462Sdim
526239462Sdim	default:			/* Error */
527239462Sdim		DPRINTF("error = %s\n", usbd_errstr(error));
528239462Sdim
529239462Sdim		if (error != USB_ERR_CANCELLED) {
530239462Sdim	tr_stall:
531239462Sdim			/* try to clear stall first */
532239462Sdim			usbd_xfer_set_stall(xfer);
533239462Sdim			usbd_xfer_set_frames(xfer, 0);
534239462Sdim			usbd_transfer_submit(xfer);
535239462Sdim			break;
536239462Sdim		}
537239462Sdim		/* need to free the RX-mbufs when we are cancelled */
538239462Sdim		ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX);
539239462Sdim		break;
540239462Sdim	}
541239462Sdim}
542239462Sdim