1104477Ssam/*-
2104477Ssam * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3104477Ssam * Copyright (c) 2009 Diego Giagio. All rights reserved.
4104477Ssam *
5104477Ssam * Redistribution and use in source and binary forms, with or without
6104477Ssam * modification, are permitted provided that the following conditions
7104477Ssam * are met:
8104477Ssam * 1. Redistributions of source code must retain the above copyright
9120915Ssam *    notice, this list of conditions and the following disclaimer.
10104477Ssam * 2. Redistributions in binary form must reproduce the above copyright
11104477Ssam *    notice, this list of conditions and the following disclaimer in the
12104477Ssam *    documentation and/or other materials provided with the distribution.
13104477Ssam *
14104477Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15104477Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16104477Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17104477Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18104477Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19104477Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20104477Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21104477Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22104477Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23104477Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24104477Ssam * SUCH DAMAGE.
25104477Ssam */
26104477Ssam
27104477Ssam/*
28104477Ssam * Thanks to Diego Giagio for figuring out the programming details for
29104477Ssam * the Apple iPhone Ethernet driver.
30104477Ssam */
31104477Ssam
32104477Ssam#include <sys/cdefs.h>
33104477Ssam__FBSDID("$FreeBSD$");
34104477Ssam
35104477Ssam#include <sys/stdint.h>
36104477Ssam#include <sys/stddef.h>
37104477Ssam#include <sys/param.h>
38104477Ssam#include <sys/queue.h>
39104477Ssam#include <sys/types.h>
40104477Ssam#include <sys/systm.h>
41104477Ssam#include <sys/kernel.h>
42104477Ssam#include <sys/bus.h>
43119418Sobrien#include <sys/module.h>
44119418Sobrien#include <sys/lock.h>
45119418Sobrien#include <sys/mutex.h>
46104477Ssam#include <sys/condvar.h>
47120915Ssam#include <sys/sysctl.h>
48104477Ssam#include <sys/sx.h>
49112124Ssam#include <sys/unistd.h>
50104477Ssam#include <sys/callout.h>
51104477Ssam#include <sys/malloc.h>
52104477Ssam#include <sys/priv.h>
53104477Ssam
54104477Ssam#include <dev/usb/usb.h>
55104477Ssam#include <dev/usb/usbdi.h>
56104477Ssam#include <dev/usb/usbdi_util.h>
57129879Sphk#include "usbdevs.h"
58104477Ssam
59104477Ssam#define	USB_DEBUG_VAR ipheth_debug
60104477Ssam#include <dev/usb/usb_debug.h>
61104477Ssam#include <dev/usb/usb_process.h>
62104477Ssam
63104477Ssam#include <dev/usb/net/usb_ethernet.h>
64104477Ssam#include <dev/usb/net/if_iphethvar.h>
65104477Ssam
66104477Ssamstatic device_probe_t ipheth_probe;
67104477Ssamstatic device_attach_t ipheth_attach;
68104477Ssamstatic device_detach_t ipheth_detach;
69104477Ssam
70104477Ssamstatic usb_callback_t ipheth_bulk_write_callback;
71104477Ssamstatic usb_callback_t ipheth_bulk_read_callback;
72104477Ssam
73104477Ssamstatic uether_fn_t ipheth_attach_post;
74104477Ssamstatic uether_fn_t ipheth_tick;
75119280Simpstatic uether_fn_t ipheth_init;
76119280Simpstatic uether_fn_t ipheth_stop;
77112124Ssamstatic uether_fn_t ipheth_start;
78112124Ssamstatic uether_fn_t ipheth_setmulti;
79112124Ssamstatic uether_fn_t ipheth_setpromisc;
80112124Ssam
81104477Ssam#ifdef USB_DEBUG
82104477Ssamstatic int ipheth_debug = 0;
83104477Ssam
84104477Ssamstatic SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet");
85104477SsamSYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RW, &ipheth_debug, 0, "Debug level");
86104477Ssam#endif
87104477Ssam
88104477Ssamstatic const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = {
89104477Ssam
90104477Ssam	[IPHETH_BULK_RX] = {
91104477Ssam		.type = UE_BULK,
92104477Ssam		.endpoint = UE_ADDR_ANY,
93104477Ssam		.direction = UE_DIR_RX,
94104477Ssam		.frames = IPHETH_RX_FRAMES_MAX,
95104477Ssam		.bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES),
96104477Ssam		.flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
97104477Ssam		.callback = ipheth_bulk_read_callback,
98104477Ssam		.timeout = 0,		/* no timeout */
99104477Ssam	},
100104477Ssam
101104477Ssam	[IPHETH_BULK_TX] = {
102104477Ssam		.type = UE_BULK,
103104477Ssam		.endpoint = UE_ADDR_ANY,
104104477Ssam		.direction = UE_DIR_TX,
105104477Ssam		.frames = IPHETH_TX_FRAMES_MAX,
106104477Ssam		.bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE),
107104477Ssam		.flags = {.force_short_xfer = 1,},
108104477Ssam		.callback = ipheth_bulk_write_callback,
109104477Ssam		.timeout = IPHETH_TX_TIMEOUT,
110104477Ssam	},
111104477Ssam};
112104477Ssam
113104477Ssamstatic device_method_t ipheth_methods[] = {
114104477Ssam	/* Device interface */
115104477Ssam	DEVMETHOD(device_probe, ipheth_probe),
116104477Ssam	DEVMETHOD(device_attach, ipheth_attach),
117105251Smarkm	DEVMETHOD(device_detach, ipheth_detach),
118112124Ssam
119112124Ssam	DEVMETHOD_END
120112124Ssam};
121104477Ssam
122104477Ssamstatic driver_t ipheth_driver = {
123104477Ssam	.name = "ipheth",
124104477Ssam	.methods = ipheth_methods,
125104477Ssam	.size = sizeof(struct ipheth_softc),
126104477Ssam};
127104477Ssam
128104477Ssamstatic devclass_t ipheth_devclass;
129104477Ssam
130104477SsamDRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0);
131104477SsamMODULE_VERSION(ipheth, 1);
132104477SsamMODULE_DEPEND(ipheth, uether, 1, 1, 1);
133104477SsamMODULE_DEPEND(ipheth, usb, 1, 1, 1);
134104477SsamMODULE_DEPEND(ipheth, ether, 1, 1, 1);
135104477Ssam
136104477Ssamstatic const struct usb_ether_methods ipheth_ue_methods = {
137104477Ssam	.ue_attach_post = ipheth_attach_post,
138104477Ssam	.ue_start = ipheth_start,
139104477Ssam	.ue_init = ipheth_init,
140104477Ssam	.ue_tick = ipheth_tick,
141104477Ssam	.ue_stop = ipheth_stop,
142104477Ssam	.ue_setmulti = ipheth_setmulti,
143104477Ssam	.ue_setpromisc = ipheth_setpromisc,
144104477Ssam};
145104477Ssam
146104477Ssam#define	IPHETH_ID(v,p,c,sc,pt) \
147104477Ssam    USB_VENDOR(v), USB_PRODUCT(p), \
148104477Ssam    USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \
149104477Ssam    USB_IFACE_PROTOCOL(pt)
150104477Ssam
151104477Ssamstatic const STRUCT_USB_HOST_ID ipheth_devs[] = {
152104477Ssam#if 0
153104477Ssam	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE,
154131575Sstefanf	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
155104477Ssam	    IPHETH_USBINTF_PROTO)},
156104477Ssam	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G,
157104477Ssam	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
158104477Ssam	    IPHETH_USBINTF_PROTO)},
159104477Ssam	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS,
160104477Ssam	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
161104477Ssam	    IPHETH_USBINTF_PROTO)},
162104477Ssam	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4,
163131575Sstefanf	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
164104477Ssam	    IPHETH_USBINTF_PROTO)},
165104477Ssam	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S,
166104477Ssam	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
167104477Ssam	    IPHETH_USBINTF_PROTO)},
168104477Ssam	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5,
169104477Ssam	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
170104477Ssam	    IPHETH_USBINTF_PROTO)},
171104477Ssam#else
172109596Ssam	/* product agnostic interface match */
173109596Ssam	{USB_VENDOR(USB_VENDOR_APPLE),
174104477Ssam	 USB_IFACE_CLASS(IPHETH_USBINTF_CLASS),
175104477Ssam	 USB_IFACE_SUBCLASS(IPHETH_USBINTF_SUBCLASS),
176109596Ssam	 USB_IFACE_PROTOCOL(IPHETH_USBINTF_PROTO)},
177109596Ssam#endif
178104477Ssam};
179104477Ssam
180104477Ssamstatic int
181109596Ssamipheth_get_mac_addr(struct ipheth_softc *sc)
182109596Ssam{
183112121Ssam	struct usb_device_request req;
184109596Ssam	int error;
185109596Ssam
186104477Ssam	req.bmRequestType = UT_READ_VENDOR_DEVICE;
187104477Ssam	req.bRequest = IPHETH_CMD_GET_MACADDR;
188104477Ssam	req.wValue[0] = 0;
189104477Ssam	req.wValue[1] = 0;
190104477Ssam	req.wIndex[0] = sc->sc_iface_no;
191104477Ssam	req.wIndex[1] = 0;
192104477Ssam	req.wLength[0] = ETHER_ADDR_LEN;
193104477Ssam	req.wLength[1] = 0;
194104477Ssam
195104477Ssam	error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data);
196104477Ssam
197104477Ssam	if (error)
198104477Ssam		return (error);
199104477Ssam
200120915Ssam	memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN);
201120915Ssam
202104477Ssam	return (0);
203104477Ssam}
204104477Ssam
205104477Ssamstatic int
206104477Ssamipheth_probe(device_t dev)
207104477Ssam{
208104477Ssam	struct usb_attach_arg *uaa = device_get_ivars(dev);
209104477Ssam
210104477Ssam	if (uaa->usb_mode != USB_MODE_HOST)
211104477Ssam		return (ENXIO);
212104477Ssam
213104477Ssam	return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa));
214104477Ssam}
215104477Ssam
216104477Ssamstatic int
217104477Ssamipheth_attach(device_t dev)
218104477Ssam{
219104477Ssam	struct ipheth_softc *sc = device_get_softc(dev);
220104477Ssam	struct usb_ether *ue = &sc->sc_ue;
221104477Ssam	struct usb_attach_arg *uaa = device_get_ivars(dev);
222104477Ssam	int error;
223104477Ssam
224104477Ssam	sc->sc_iface_no = uaa->info.bIfaceIndex;
225104477Ssam
226104477Ssam	device_set_usb_desc(dev);
227104477Ssam
228120915Ssam	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
229120915Ssam
230104477Ssam	error = usbd_set_alt_interface_index(uaa->device,
231104477Ssam	    uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM);
232104477Ssam	if (error) {
233104477Ssam		device_printf(dev, "Cannot set alternate setting\n");
234104477Ssam		goto detach;
235104477Ssam	}
236104477Ssam	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no,
237104477Ssam	    sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx);
238104477Ssam	if (error) {
239104477Ssam		device_printf(dev, "Cannot setup USB transfers\n");
240104477Ssam		goto detach;
241104477Ssam	}
242104477Ssam	ue->ue_sc = sc;
243104477Ssam	ue->ue_dev = dev;
244104477Ssam	ue->ue_udev = uaa->device;
245104477Ssam	ue->ue_mtx = &sc->sc_mtx;
246112124Ssam	ue->ue_methods = &ipheth_ue_methods;
247112124Ssam
248112124Ssam	error = ipheth_get_mac_addr(sc);
249112124Ssam	if (error) {
250112124Ssam		device_printf(dev, "Cannot get MAC address\n");
251112124Ssam		goto detach;
252104477Ssam	}
253104477Ssam
254104477Ssam	error = uether_ifattach(ue);
255104477Ssam	if (error) {
256104477Ssam		device_printf(dev, "could not attach interface\n");
257104477Ssam		goto detach;
258104477Ssam	}
259104477Ssam	return (0);			/* success */
260104477Ssam
261104477Ssamdetach:
262104477Ssam	ipheth_detach(dev);
263104477Ssam	return (ENXIO);			/* failure */
264104477Ssam}
265104477Ssam
266104477Ssamstatic int
267104477Ssamipheth_detach(device_t dev)
268104477Ssam{
269115748Ssam	struct ipheth_softc *sc = device_get_softc(dev);
270104477Ssam	struct usb_ether *ue = &sc->sc_ue;
271104477Ssam
272104477Ssam	/* stop all USB transfers first */
273104477Ssam	usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER);
274120915Ssam
275104477Ssam	uether_ifdetach(ue);
276104477Ssam
277104477Ssam	mtx_destroy(&sc->sc_mtx);
278120915Ssam
279120915Ssam	return (0);
280120915Ssam}
281104477Ssam
282104477Ssamstatic void
283104477Ssamipheth_start(struct usb_ether *ue)
284104477Ssam{
285104477Ssam	struct ipheth_softc *sc = uether_getsc(ue);
286104477Ssam
287104477Ssam	/*
288104477Ssam	 * Start the USB transfers, if not already started:
289104477Ssam	 */
290104477Ssam	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]);
291120915Ssam	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]);
292120915Ssam}
293120915Ssam
294120915Ssamstatic void
295120915Ssamipheth_stop(struct usb_ether *ue)
296120915Ssam{
297120915Ssam	struct ipheth_softc *sc = uether_getsc(ue);
298120915Ssam
299104477Ssam	/*
300104477Ssam	 * Stop the USB transfers, if not already stopped:
301104477Ssam	 */
302104477Ssam	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]);
303104477Ssam	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]);
304104477Ssam}
305104477Ssam
306104477Ssamstatic void
307104477Ssamipheth_tick(struct usb_ether *ue)
308104477Ssam{
309104477Ssam	struct ipheth_softc *sc = uether_getsc(ue);
310104477Ssam	struct usb_device_request req;
311104477Ssam	int error;
312104477Ssam
313104477Ssam	req.bmRequestType = UT_READ_VENDOR_DEVICE;
314104477Ssam	req.bRequest = IPHETH_CMD_CARRIER_CHECK;
315104477Ssam	req.wValue[0] = 0;
316104477Ssam	req.wValue[1] = 0;
317104477Ssam	req.wIndex[0] = sc->sc_iface_no;
318104477Ssam	req.wIndex[1] = 0;
319104477Ssam	req.wLength[0] = IPHETH_CTRL_BUF_SIZE;
320104477Ssam	req.wLength[1] = 0;
321104477Ssam
322104477Ssam	error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT);
323104477Ssam
324127135Snjl	if (error)
325127135Snjl		return;
326104477Ssam
327104477Ssam	sc->sc_carrier_on =
328104477Ssam	    (sc->sc_data[0] == IPHETH_CARRIER_ON);
329104477Ssam}
330104477Ssam
331104477Ssamstatic void
332104477Ssamipheth_attach_post(struct usb_ether *ue)
333104477Ssam{
334104477Ssam
335127135Snjl}
336127135Snjl
337104477Ssamstatic void
338104477Ssamipheth_init(struct usb_ether *ue)
339104477Ssam{
340104477Ssam	struct ipheth_softc *sc = uether_getsc(ue);
341104477Ssam	struct ifnet *ifp = uether_getifp(ue);
342104477Ssam
343104477Ssam	IPHETH_LOCK_ASSERT(sc, MA_OWNED);
344104477Ssam
345104477Ssam	ifp->if_drv_flags |= IFF_DRV_RUNNING;
346104477Ssam
347104477Ssam	/* stall data write direction, which depends on USB mode */
348104477Ssam	usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]);
349104477Ssam
350104477Ssam	/* start data transfers */
351104477Ssam	ipheth_start(ue);
352104477Ssam}
353104477Ssam
354104477Ssamstatic void
355104477Ssamipheth_setmulti(struct usb_ether *ue)
356104477Ssam{
357104477Ssam
358104477Ssam}
359104477Ssam
360117126Sscottlstatic void
361117126Sscottlipheth_setpromisc(struct usb_ether *ue)
362104477Ssam{
363104477Ssam
364104477Ssam}
365104477Ssam
366104477Ssamstatic void
367104477Ssamipheth_free_queue(struct mbuf **ppm, uint8_t n)
368104477Ssam{
369104477Ssam	uint8_t x;
370104477Ssam
371104477Ssam	for (x = 0; x != n; x++) {
372104477Ssam		if (ppm[x] != NULL) {
373104477Ssam			m_freem(ppm[x]);
374104477Ssam			ppm[x] = NULL;
375104477Ssam		}
376104477Ssam	}
377104477Ssam}
378104477Ssam
379104477Ssamstatic void
380104477Ssamipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
381104477Ssam{
382104477Ssam	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
383104477Ssam	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
384104477Ssam	struct usb_page_cache *pc;
385104477Ssam	struct mbuf *m;
386104477Ssam	uint8_t x;
387104477Ssam	int actlen;
388104477Ssam	int aframes;
389104477Ssam
390123824Ssam	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
391123824Ssam
392123824Ssam	DPRINTFN(1, "\n");
393123824Ssam
394104477Ssam	switch (USB_GET_STATE(xfer)) {
395104477Ssam	case USB_ST_TRANSFERRED:
396104477Ssam		DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
397104477Ssam		    actlen, aframes);
398104477Ssam
399104477Ssam		ifp->if_opackets++;
400104477Ssam
401104477Ssam		/* free all previous TX buffers */
402104477Ssam		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
403104477Ssam
404104477Ssam		/* FALLTHROUGH */
405104477Ssam	case USB_ST_SETUP:
406104477Ssamtr_setup:
407104477Ssam		for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) {
408104477Ssam
409104477Ssam			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
410104477Ssam
411104477Ssam			if (m == NULL)
412104477Ssam				break;
413104477Ssam
414120915Ssam			usbd_xfer_set_frame_offset(xfer,
415120915Ssam			    x * IPHETH_BUF_SIZE, x);
416120915Ssam
417120915Ssam			pc = usbd_xfer_get_frame(xfer, x);
418104477Ssam
419104477Ssam			sc->sc_tx_buf[x] = m;
420104477Ssam
421104477Ssam			if (m->m_pkthdr.len > IPHETH_BUF_SIZE)
422104477Ssam				m->m_pkthdr.len = IPHETH_BUF_SIZE;
423104477Ssam
424104477Ssam			usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
425104477Ssam
426104477Ssam			usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE);
427104477Ssam
428104477Ssam			if (IPHETH_BUF_SIZE != m->m_pkthdr.len) {
429104477Ssam				usbd_frame_zero(pc, m->m_pkthdr.len,
430104477Ssam					IPHETH_BUF_SIZE - m->m_pkthdr.len);
431104477Ssam			}
432104477Ssam
433104477Ssam			/*
434104477Ssam			 * If there's a BPF listener, bounce a copy of
435104477Ssam			 * this frame to him:
436104477Ssam			 */
437104477Ssam			BPF_MTAP(ifp, m);
438127135Snjl		}
439127135Snjl		if (x != 0) {
440104477Ssam			usbd_xfer_set_frames(xfer, x);
441104477Ssam
442104477Ssam			usbd_transfer_submit(xfer);
443104477Ssam		}
444104477Ssam		break;
445104477Ssam
446104477Ssam	default:			/* Error */
447104477Ssam		DPRINTFN(11, "transfer error, %s\n",
448115748Ssam		    usbd_errstr(error));
449104477Ssam
450104477Ssam		/* free all previous TX buffers */
451104477Ssam		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
452104477Ssam
453104477Ssam		/* count output errors */
454104477Ssam		ifp->if_oerrors++;
455104477Ssam
456104477Ssam		if (error != USB_ERR_CANCELLED) {
457104477Ssam			/* try to clear stall first */
458104477Ssam			usbd_xfer_set_stall(xfer);
459104477Ssam			goto tr_setup;
460104477Ssam		}
461104477Ssam		break;
462104477Ssam	}
463104477Ssam}
464104477Ssam
465104477Ssamstatic void
466104477Ssamipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
467104477Ssam{
468104477Ssam	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
469104477Ssam	struct mbuf *m;
470104477Ssam	uint8_t x;
471104477Ssam	int actlen;
472104477Ssam	int aframes;
473104477Ssam	int len;
474104477Ssam
475104477Ssam	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
476104477Ssam
477104477Ssam	switch (USB_GET_STATE(xfer)) {
478104477Ssam	case USB_ST_TRANSFERRED:
479104477Ssam
480104477Ssam		DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
481104477Ssam
482104477Ssam		for (x = 0; x != aframes; x++) {
483104477Ssam
484104477Ssam			m = sc->sc_rx_buf[x];
485104477Ssam			sc->sc_rx_buf[x] = NULL;
486104477Ssam			len = usbd_xfer_frame_len(xfer, x);
487104477Ssam
488104477Ssam			if (len < (int)(sizeof(struct ether_header) +
489120915Ssam			    IPHETH_RX_ADJ)) {
490120915Ssam				m_freem(m);
491120915Ssam				continue;
492120915Ssam			}
493104477Ssam
494104477Ssam			m_adj(m, IPHETH_RX_ADJ);
495104477Ssam
496104477Ssam			/* queue up mbuf */
497104477Ssam			uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ);
498104477Ssam		}
499104477Ssam
500104477Ssam		/* FALLTHROUGH */
501104477Ssam	case USB_ST_SETUP:
502104477Ssam
503104477Ssam		for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) {
504104477Ssam			if (sc->sc_rx_buf[x] == NULL) {
505104477Ssam				m = uether_newbuf();
506104477Ssam				if (m == NULL)
507104477Ssam					goto tr_stall;
508104477Ssam
509104477Ssam				/* cancel alignment for ethernet */
510104477Ssam				m_adj(m, ETHER_ALIGN);
511104477Ssam
512104477Ssam				sc->sc_rx_buf[x] = m;
513104477Ssam			} else {
514119137Ssam				m = sc->sc_rx_buf[x];
515104477Ssam			}
516104477Ssam
517104477Ssam			usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
518104477Ssam		}
519104477Ssam		/* set number of frames and start hardware */
520104477Ssam		usbd_xfer_set_frames(xfer, x);
521104477Ssam		usbd_transfer_submit(xfer);
522104477Ssam		/* flush any received frames */
523104477Ssam		uether_rxflush(&sc->sc_ue);
524104477Ssam		break;
525104477Ssam
526104477Ssam	default:			/* Error */
527104477Ssam		DPRINTF("error = %s\n", usbd_errstr(error));
528104477Ssam
529104477Ssam		if (error != USB_ERR_CANCELLED) {
530104477Ssam	tr_stall:
531104477Ssam			/* try to clear stall first */
532104477Ssam			usbd_xfer_set_stall(xfer);
533104477Ssam			usbd_xfer_set_frames(xfer, 0);
534104477Ssam			usbd_transfer_submit(xfer);
535104477Ssam			break;
536104477Ssam		}
537104477Ssam		/* need to free the RX-mbufs when we are cancelled */
538104477Ssam		ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX);
539104477Ssam		break;
540104477Ssam	}
541104477Ssam}
542104477Ssam