if_ipheth.c revision 223486
1213805Shselasky/*-
2213805Shselasky * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3213805Shselasky * Copyright (c) 2009 Diego Giagio. All rights reserved.
4213805Shselasky *
5213805Shselasky * Redistribution and use in source and binary forms, with or without
6213805Shselasky * modification, are permitted provided that the following conditions
7213805Shselasky * are met:
8213805Shselasky * 1. Redistributions of source code must retain the above copyright
9213805Shselasky *    notice, this list of conditions and the following disclaimer.
10213805Shselasky * 2. Redistributions in binary form must reproduce the above copyright
11213805Shselasky *    notice, this list of conditions and the following disclaimer in the
12213805Shselasky *    documentation and/or other materials provided with the distribution.
13213805Shselasky *
14213805Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15213805Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16213805Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17213805Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18213805Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19213805Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20213805Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21213805Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22213805Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23213805Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24213805Shselasky * SUCH DAMAGE.
25213805Shselasky */
26213805Shselasky
27213805Shselasky/*
28213805Shselasky * Thanks to Diego Giagio for figuring out the programming details for
29213805Shselasky * the Apple iPhone Ethernet driver.
30213805Shselasky */
31213805Shselasky
32213805Shselasky#include <sys/cdefs.h>
33213805Shselasky__FBSDID("$FreeBSD: head/sys/dev/usb/net/if_ipheth.c 223486 2011-06-24 02:30:02Z hselasky $");
34213805Shselasky
35213805Shselasky#include <sys/stdint.h>
36213805Shselasky#include <sys/stddef.h>
37213805Shselasky#include <sys/param.h>
38213805Shselasky#include <sys/queue.h>
39213805Shselasky#include <sys/types.h>
40213805Shselasky#include <sys/systm.h>
41213805Shselasky#include <sys/kernel.h>
42213805Shselasky#include <sys/bus.h>
43213805Shselasky#include <sys/module.h>
44213805Shselasky#include <sys/lock.h>
45213805Shselasky#include <sys/mutex.h>
46213805Shselasky#include <sys/condvar.h>
47213805Shselasky#include <sys/sysctl.h>
48213805Shselasky#include <sys/sx.h>
49213805Shselasky#include <sys/unistd.h>
50213805Shselasky#include <sys/callout.h>
51213805Shselasky#include <sys/malloc.h>
52213805Shselasky#include <sys/priv.h>
53213805Shselasky
54213805Shselasky#include <dev/usb/usb.h>
55213805Shselasky#include <dev/usb/usbdi.h>
56213805Shselasky#include <dev/usb/usbdi_util.h>
57213805Shselasky#include "usbdevs.h"
58213805Shselasky
59213805Shselasky#define	USB_DEBUG_VAR ipheth_debug
60213805Shselasky#include <dev/usb/usb_debug.h>
61213805Shselasky#include <dev/usb/usb_process.h>
62213805Shselasky
63213805Shselasky#include <dev/usb/net/usb_ethernet.h>
64213805Shselasky#include <dev/usb/net/if_iphethvar.h>
65213805Shselasky
66213805Shselaskystatic device_probe_t ipheth_probe;
67213805Shselaskystatic device_attach_t ipheth_attach;
68213805Shselaskystatic device_detach_t ipheth_detach;
69213805Shselasky
70213805Shselaskystatic usb_callback_t ipheth_bulk_write_callback;
71213805Shselaskystatic usb_callback_t ipheth_bulk_read_callback;
72213805Shselasky
73213805Shselaskystatic uether_fn_t ipheth_attach_post;
74213805Shselaskystatic uether_fn_t ipheth_tick;
75213805Shselaskystatic uether_fn_t ipheth_init;
76213805Shselaskystatic uether_fn_t ipheth_stop;
77213805Shselaskystatic uether_fn_t ipheth_start;
78213805Shselaskystatic uether_fn_t ipheth_setmulti;
79213805Shselaskystatic uether_fn_t ipheth_setpromisc;
80213805Shselasky
81213805Shselasky#ifdef USB_DEBUG
82213805Shselaskystatic int ipheth_debug = 0;
83213805Shselasky
84213805ShselaskySYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet");
85213805ShselaskySYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RW, &ipheth_debug, 0, "Debug level");
86213805Shselasky#endif
87213805Shselasky
88213805Shselaskystatic const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = {
89213805Shselasky
90213805Shselasky	[IPHETH_BULK_RX] = {
91213805Shselasky		.type = UE_BULK,
92213805Shselasky		.endpoint = UE_ADDR_ANY,
93213805Shselasky		.direction = UE_DIR_RX,
94213805Shselasky		.frames = IPHETH_RX_FRAMES_MAX,
95213805Shselasky		.bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES),
96213805Shselasky		.flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
97213805Shselasky		.callback = ipheth_bulk_read_callback,
98213805Shselasky		.timeout = 0,		/* no timeout */
99213805Shselasky	},
100213805Shselasky
101213805Shselasky	[IPHETH_BULK_TX] = {
102213805Shselasky		.type = UE_BULK,
103213805Shselasky		.endpoint = UE_ADDR_ANY,
104213805Shselasky		.direction = UE_DIR_TX,
105213805Shselasky		.frames = IPHETH_TX_FRAMES_MAX,
106213805Shselasky		.bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE),
107213805Shselasky		.flags = {.force_short_xfer = 1,},
108213805Shselasky		.callback = ipheth_bulk_write_callback,
109213805Shselasky		.timeout = IPHETH_TX_TIMEOUT,
110213805Shselasky	},
111213805Shselasky};
112213805Shselasky
113213805Shselaskystatic device_method_t ipheth_methods[] = {
114213805Shselasky	/* Device interface */
115213805Shselasky	DEVMETHOD(device_probe, ipheth_probe),
116213805Shselasky	DEVMETHOD(device_attach, ipheth_attach),
117213805Shselasky	DEVMETHOD(device_detach, ipheth_detach),
118213805Shselasky
119213805Shselasky	{0, 0}
120213805Shselasky};
121213805Shselasky
122213805Shselaskystatic driver_t ipheth_driver = {
123213805Shselasky	.name = "ipheth",
124213805Shselasky	.methods = ipheth_methods,
125213805Shselasky	.size = sizeof(struct ipheth_softc),
126213805Shselasky};
127213805Shselasky
128213805Shselaskystatic devclass_t ipheth_devclass;
129213805Shselasky
130213805ShselaskyDRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0);
131213805ShselaskyMODULE_VERSION(ipheth, 1);
132213805ShselaskyMODULE_DEPEND(ipheth, uether, 1, 1, 1);
133213805ShselaskyMODULE_DEPEND(ipheth, usb, 1, 1, 1);
134213805ShselaskyMODULE_DEPEND(ipheth, ether, 1, 1, 1);
135213805Shselasky
136213805Shselaskystatic const struct usb_ether_methods ipheth_ue_methods = {
137213805Shselasky	.ue_attach_post = ipheth_attach_post,
138213805Shselasky	.ue_start = ipheth_start,
139213805Shselasky	.ue_init = ipheth_init,
140213805Shselasky	.ue_tick = ipheth_tick,
141213805Shselasky	.ue_stop = ipheth_stop,
142213805Shselasky	.ue_setmulti = ipheth_setmulti,
143213805Shselasky	.ue_setpromisc = ipheth_setpromisc,
144213805Shselasky};
145213805Shselasky
146213805Shselasky#define	IPHETH_ID(v,p,c,sc,pt) \
147213805Shselasky    USB_VENDOR(v), USB_PRODUCT(p), \
148213805Shselasky    USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \
149213805Shselasky    USB_IFACE_PROTOCOL(pt)
150213805Shselasky
151223486Shselaskystatic const STRUCT_USB_HOST_ID ipheth_devs[] = {
152213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE,
153213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
154213805Shselasky	    IPHETH_USBINTF_PROTO)},
155213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G,
156213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
157213805Shselasky	    IPHETH_USBINTF_PROTO)},
158213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS,
159213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
160213805Shselasky	    IPHETH_USBINTF_PROTO)},
161213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4,
162213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
163213805Shselasky	    IPHETH_USBINTF_PROTO)},
164213805Shselasky};
165213805Shselasky
166213805Shselaskystatic int
167213805Shselaskyipheth_get_mac_addr(struct ipheth_softc *sc)
168213805Shselasky{
169213805Shselasky	struct usb_device_request req;
170213805Shselasky	int error;
171213805Shselasky
172213805Shselasky	req.bmRequestType = UT_READ_VENDOR_DEVICE;
173213805Shselasky	req.bRequest = IPHETH_CMD_GET_MACADDR;
174213805Shselasky	req.wValue[0] = 0;
175213805Shselasky	req.wValue[1] = 0;
176213805Shselasky	req.wIndex[0] = sc->sc_iface_no;
177213805Shselasky	req.wIndex[1] = 0;
178213805Shselasky	req.wLength[0] = ETHER_ADDR_LEN;
179213805Shselasky	req.wLength[1] = 0;
180213805Shselasky
181213805Shselasky	error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data);
182213805Shselasky
183213805Shselasky	if (error)
184213805Shselasky		return (error);
185213805Shselasky
186213805Shselasky	memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN);
187213805Shselasky
188213805Shselasky	return (0);
189213805Shselasky}
190213805Shselasky
191213805Shselaskystatic int
192213805Shselaskyipheth_probe(device_t dev)
193213805Shselasky{
194213805Shselasky	struct usb_attach_arg *uaa = device_get_ivars(dev);
195213805Shselasky
196213805Shselasky	if (uaa->usb_mode != USB_MODE_HOST)
197213805Shselasky		return (ENXIO);
198213805Shselasky
199213805Shselasky	return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa));
200213805Shselasky}
201213805Shselasky
202213805Shselaskystatic int
203213805Shselaskyipheth_attach(device_t dev)
204213805Shselasky{
205213805Shselasky	struct ipheth_softc *sc = device_get_softc(dev);
206213805Shselasky	struct usb_ether *ue = &sc->sc_ue;
207213805Shselasky	struct usb_attach_arg *uaa = device_get_ivars(dev);
208213805Shselasky	int error;
209213805Shselasky
210213805Shselasky	sc->sc_iface_no = uaa->info.bIfaceIndex;
211213805Shselasky
212213805Shselasky	device_set_usb_desc(dev);
213213805Shselasky
214213805Shselasky	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
215213805Shselasky
216213805Shselasky	error = usbd_set_alt_interface_index(uaa->device,
217213805Shselasky	    uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM);
218213805Shselasky	if (error) {
219213805Shselasky		device_printf(dev, "Cannot set alternate setting\n");
220213805Shselasky		goto detach;
221213805Shselasky	}
222213805Shselasky	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no,
223213805Shselasky	    sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx);
224213805Shselasky	if (error) {
225213805Shselasky		device_printf(dev, "Cannot setup USB transfers\n");
226213805Shselasky		goto detach;
227213805Shselasky	}
228213805Shselasky	ue->ue_sc = sc;
229213805Shselasky	ue->ue_dev = dev;
230213805Shselasky	ue->ue_udev = uaa->device;
231213805Shselasky	ue->ue_mtx = &sc->sc_mtx;
232213805Shselasky	ue->ue_methods = &ipheth_ue_methods;
233213805Shselasky
234213805Shselasky	error = ipheth_get_mac_addr(sc);
235213805Shselasky	if (error) {
236213805Shselasky		device_printf(dev, "Cannot get MAC address\n");
237213805Shselasky		goto detach;
238213805Shselasky	}
239213805Shselasky
240213805Shselasky	error = uether_ifattach(ue);
241213805Shselasky	if (error) {
242213805Shselasky		device_printf(dev, "could not attach interface\n");
243213805Shselasky		goto detach;
244213805Shselasky	}
245213805Shselasky	return (0);			/* success */
246213805Shselasky
247213805Shselaskydetach:
248213805Shselasky	ipheth_detach(dev);
249213805Shselasky	return (ENXIO);			/* failure */
250213805Shselasky}
251213805Shselasky
252213805Shselaskystatic int
253213805Shselaskyipheth_detach(device_t dev)
254213805Shselasky{
255213805Shselasky	struct ipheth_softc *sc = device_get_softc(dev);
256213805Shselasky	struct usb_ether *ue = &sc->sc_ue;
257213805Shselasky
258213805Shselasky	/* stop all USB transfers first */
259213805Shselasky	usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER);
260213805Shselasky
261213805Shselasky	uether_ifdetach(ue);
262213805Shselasky
263213805Shselasky	mtx_destroy(&sc->sc_mtx);
264213805Shselasky
265213805Shselasky	return (0);
266213805Shselasky}
267213805Shselasky
268213805Shselaskystatic void
269213805Shselaskyipheth_start(struct usb_ether *ue)
270213805Shselasky{
271213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
272213805Shselasky
273213805Shselasky	/*
274213805Shselasky	 * Start the USB transfers, if not already started:
275213805Shselasky	 */
276213805Shselasky	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]);
277213805Shselasky	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]);
278213805Shselasky}
279213805Shselasky
280213805Shselaskystatic void
281213805Shselaskyipheth_stop(struct usb_ether *ue)
282213805Shselasky{
283213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
284213805Shselasky
285213805Shselasky	/*
286213805Shselasky	 * Stop the USB transfers, if not already stopped:
287213805Shselasky	 */
288213805Shselasky	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]);
289213805Shselasky	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]);
290213805Shselasky}
291213805Shselasky
292213805Shselaskystatic void
293213805Shselaskyipheth_tick(struct usb_ether *ue)
294213805Shselasky{
295213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
296213805Shselasky	struct usb_device_request req;
297213805Shselasky	int error;
298213805Shselasky
299213805Shselasky	req.bmRequestType = UT_READ_VENDOR_DEVICE;
300213805Shselasky	req.bRequest = IPHETH_CMD_CARRIER_CHECK;
301213805Shselasky	req.wValue[0] = 0;
302213805Shselasky	req.wValue[1] = 0;
303213805Shselasky	req.wIndex[0] = sc->sc_iface_no;
304213805Shselasky	req.wIndex[1] = 0;
305213805Shselasky	req.wLength[0] = IPHETH_CTRL_BUF_SIZE;
306213805Shselasky	req.wLength[1] = 0;
307213805Shselasky
308213805Shselasky	error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT);
309213805Shselasky
310213805Shselasky	if (error)
311213805Shselasky		return;
312213805Shselasky
313213805Shselasky	sc->sc_carrier_on =
314213805Shselasky	    (sc->sc_data[0] == IPHETH_CARRIER_ON);
315213805Shselasky}
316213805Shselasky
317213805Shselaskystatic void
318213805Shselaskyipheth_attach_post(struct usb_ether *ue)
319213805Shselasky{
320213805Shselasky
321213805Shselasky}
322213805Shselasky
323213805Shselaskystatic void
324213805Shselaskyipheth_init(struct usb_ether *ue)
325213805Shselasky{
326213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
327213805Shselasky	struct ifnet *ifp = uether_getifp(ue);
328213805Shselasky
329213805Shselasky	IPHETH_LOCK_ASSERT(sc, MA_OWNED);
330213805Shselasky
331213805Shselasky	ifp->if_drv_flags |= IFF_DRV_RUNNING;
332213805Shselasky
333213805Shselasky	/* stall data write direction, which depends on USB mode */
334213805Shselasky	usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]);
335213805Shselasky
336213805Shselasky	/* start data transfers */
337213805Shselasky	ipheth_start(ue);
338213805Shselasky}
339213805Shselasky
340213805Shselaskystatic void
341213805Shselaskyipheth_setmulti(struct usb_ether *ue)
342213805Shselasky{
343213805Shselasky
344213805Shselasky}
345213805Shselasky
346213805Shselaskystatic void
347213805Shselaskyipheth_setpromisc(struct usb_ether *ue)
348213805Shselasky{
349213805Shselasky
350213805Shselasky}
351213805Shselasky
352213805Shselaskystatic void
353213805Shselaskyipheth_free_queue(struct mbuf **ppm, uint8_t n)
354213805Shselasky{
355213805Shselasky	uint8_t x;
356213805Shselasky
357213805Shselasky	for (x = 0; x != n; x++) {
358213805Shselasky		if (ppm[x] != NULL) {
359213805Shselasky			m_freem(ppm[x]);
360213805Shselasky			ppm[x] = NULL;
361213805Shselasky		}
362213805Shselasky	}
363213805Shselasky}
364213805Shselasky
365213805Shselaskystatic void
366213805Shselaskyipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
367213805Shselasky{
368213805Shselasky	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
369213805Shselasky	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
370213805Shselasky	struct usb_page_cache *pc;
371213805Shselasky	struct mbuf *m;
372213805Shselasky	uint8_t x;
373213805Shselasky	int actlen;
374213805Shselasky	int aframes;
375213805Shselasky
376213805Shselasky	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
377213805Shselasky
378213805Shselasky	DPRINTFN(1, "\n");
379213805Shselasky
380213805Shselasky	switch (USB_GET_STATE(xfer)) {
381213805Shselasky	case USB_ST_TRANSFERRED:
382213805Shselasky		DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
383213805Shselasky		    actlen, aframes);
384213805Shselasky
385213805Shselasky		ifp->if_opackets++;
386213805Shselasky
387213805Shselasky		/* free all previous TX buffers */
388213805Shselasky		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
389213805Shselasky
390213805Shselasky		/* FALLTHROUGH */
391213805Shselasky	case USB_ST_SETUP:
392213805Shselaskytr_setup:
393213805Shselasky		for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) {
394213805Shselasky
395213805Shselasky			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
396213805Shselasky
397213805Shselasky			if (m == NULL)
398213805Shselasky				break;
399213805Shselasky
400213805Shselasky			usbd_xfer_set_frame_offset(xfer,
401213805Shselasky			    x * IPHETH_BUF_SIZE, x);
402213805Shselasky
403213805Shselasky			pc = usbd_xfer_get_frame(xfer, x);
404213805Shselasky
405213805Shselasky			sc->sc_tx_buf[x] = m;
406213805Shselasky
407213805Shselasky			if (m->m_pkthdr.len > IPHETH_BUF_SIZE)
408213805Shselasky				m->m_pkthdr.len = IPHETH_BUF_SIZE;
409213805Shselasky
410213805Shselasky			usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
411213805Shselasky
412213805Shselasky			usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE);
413213805Shselasky
414213805Shselasky			if (IPHETH_BUF_SIZE != m->m_pkthdr.len) {
415213805Shselasky				usbd_frame_zero(pc, m->m_pkthdr.len,
416213805Shselasky					IPHETH_BUF_SIZE - m->m_pkthdr.len);
417213805Shselasky			}
418213805Shselasky
419213805Shselasky			/*
420213805Shselasky			 * If there's a BPF listener, bounce a copy of
421213805Shselasky			 * this frame to him:
422213805Shselasky			 */
423213805Shselasky			BPF_MTAP(ifp, m);
424213805Shselasky		}
425213805Shselasky		if (x != 0) {
426213805Shselasky			usbd_xfer_set_frames(xfer, x);
427213805Shselasky
428213805Shselasky			usbd_transfer_submit(xfer);
429213805Shselasky		}
430213805Shselasky		break;
431213805Shselasky
432213805Shselasky	default:			/* Error */
433213805Shselasky		DPRINTFN(11, "transfer error, %s\n",
434213805Shselasky		    usbd_errstr(error));
435213805Shselasky
436213805Shselasky		/* free all previous TX buffers */
437213805Shselasky		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
438213805Shselasky
439213805Shselasky		/* count output errors */
440213805Shselasky		ifp->if_oerrors++;
441213805Shselasky
442213805Shselasky		if (error != USB_ERR_CANCELLED) {
443213805Shselasky			/* try to clear stall first */
444213805Shselasky			usbd_xfer_set_stall(xfer);
445213805Shselasky			goto tr_setup;
446213805Shselasky		}
447213805Shselasky		break;
448213805Shselasky	}
449213805Shselasky}
450213805Shselasky
451213805Shselaskystatic void
452213805Shselaskyipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
453213805Shselasky{
454213805Shselasky	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
455213805Shselasky	struct mbuf *m;
456213805Shselasky	uint8_t x;
457213805Shselasky	int actlen;
458213805Shselasky	int aframes;
459213805Shselasky	int len;
460213805Shselasky
461213805Shselasky	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
462213805Shselasky
463213805Shselasky	switch (USB_GET_STATE(xfer)) {
464213805Shselasky	case USB_ST_TRANSFERRED:
465213805Shselasky
466213805Shselasky		DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
467213805Shselasky
468213805Shselasky		for (x = 0; x != aframes; x++) {
469213805Shselasky
470213805Shselasky			m = sc->sc_rx_buf[x];
471213805Shselasky			sc->sc_rx_buf[x] = NULL;
472213805Shselasky			len = usbd_xfer_frame_len(xfer, x);
473213805Shselasky
474213805Shselasky			if (len < (sizeof(struct ether_header) +
475213805Shselasky			    IPHETH_RX_ADJ)) {
476213805Shselasky				m_freem(m);
477213805Shselasky				continue;
478213805Shselasky			}
479213805Shselasky
480213805Shselasky			m_adj(m, IPHETH_RX_ADJ);
481213805Shselasky
482213805Shselasky			/* queue up mbuf */
483213805Shselasky			uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ);
484213805Shselasky		}
485213805Shselasky
486213805Shselasky		/* FALLTHROUGH */
487213805Shselasky	case USB_ST_SETUP:
488213805Shselasky
489213805Shselasky		for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) {
490213805Shselasky			if (sc->sc_rx_buf[x] == NULL) {
491213805Shselasky				m = uether_newbuf();
492213805Shselasky				if (m == NULL)
493213805Shselasky					goto tr_stall;
494213805Shselasky
495213805Shselasky				/* cancel alignment for ethernet */
496213805Shselasky				m_adj(m, ETHER_ALIGN);
497213805Shselasky
498213805Shselasky				sc->sc_rx_buf[x] = m;
499213805Shselasky			} else {
500213805Shselasky				m = sc->sc_rx_buf[x];
501213805Shselasky			}
502213805Shselasky
503213805Shselasky			usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
504213805Shselasky		}
505213805Shselasky		/* set number of frames and start hardware */
506213805Shselasky		usbd_xfer_set_frames(xfer, x);
507213805Shselasky		usbd_transfer_submit(xfer);
508213805Shselasky		/* flush any received frames */
509213805Shselasky		uether_rxflush(&sc->sc_ue);
510213805Shselasky		break;
511213805Shselasky
512213805Shselasky	default:			/* Error */
513213805Shselasky		DPRINTF("error = %s\n", usbd_errstr(error));
514213805Shselasky
515213805Shselasky		if (error != USB_ERR_CANCELLED) {
516213805Shselasky	tr_stall:
517213805Shselasky			/* try to clear stall first */
518213805Shselasky			usbd_xfer_set_stall(xfer);
519213805Shselasky			usbd_xfer_set_frames(xfer, 0);
520213805Shselasky			usbd_transfer_submit(xfer);
521213805Shselasky			break;
522213805Shselasky		}
523213805Shselasky		/* need to free the RX-mbufs when we are cancelled */
524213805Shselasky		ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX);
525213805Shselasky		break;
526213805Shselasky	}
527213805Shselasky}
528