if_ipheth.c revision 276701
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 276701 2015-01-05 15:04:17Z 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>
47257241Sglebius#include <sys/socket.h>
48213805Shselasky#include <sys/sysctl.h>
49213805Shselasky#include <sys/sx.h>
50213805Shselasky#include <sys/unistd.h>
51213805Shselasky#include <sys/callout.h>
52213805Shselasky#include <sys/malloc.h>
53213805Shselasky#include <sys/priv.h>
54213805Shselasky
55257241Sglebius#include <net/if.h>
56257241Sglebius#include <net/if_var.h>
57257241Sglebius
58213805Shselasky#include <dev/usb/usb.h>
59213805Shselasky#include <dev/usb/usbdi.h>
60213805Shselasky#include <dev/usb/usbdi_util.h>
61213805Shselasky#include "usbdevs.h"
62213805Shselasky
63213805Shselasky#define	USB_DEBUG_VAR ipheth_debug
64213805Shselasky#include <dev/usb/usb_debug.h>
65213805Shselasky#include <dev/usb/usb_process.h>
66213805Shselasky
67213805Shselasky#include <dev/usb/net/usb_ethernet.h>
68213805Shselasky#include <dev/usb/net/if_iphethvar.h>
69213805Shselasky
70213805Shselaskystatic device_probe_t ipheth_probe;
71213805Shselaskystatic device_attach_t ipheth_attach;
72213805Shselaskystatic device_detach_t ipheth_detach;
73213805Shselasky
74213805Shselaskystatic usb_callback_t ipheth_bulk_write_callback;
75213805Shselaskystatic usb_callback_t ipheth_bulk_read_callback;
76213805Shselasky
77213805Shselaskystatic uether_fn_t ipheth_attach_post;
78213805Shselaskystatic uether_fn_t ipheth_tick;
79213805Shselaskystatic uether_fn_t ipheth_init;
80213805Shselaskystatic uether_fn_t ipheth_stop;
81213805Shselaskystatic uether_fn_t ipheth_start;
82213805Shselaskystatic uether_fn_t ipheth_setmulti;
83213805Shselaskystatic uether_fn_t ipheth_setpromisc;
84213805Shselasky
85213805Shselasky#ifdef USB_DEBUG
86213805Shselaskystatic int ipheth_debug = 0;
87213805Shselasky
88227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet");
89276701ShselaskySYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RWTUN, &ipheth_debug, 0, "Debug level");
90213805Shselasky#endif
91213805Shselasky
92213805Shselaskystatic const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = {
93213805Shselasky
94213805Shselasky	[IPHETH_BULK_RX] = {
95213805Shselasky		.type = UE_BULK,
96213805Shselasky		.endpoint = UE_ADDR_ANY,
97213805Shselasky		.direction = UE_DIR_RX,
98213805Shselasky		.frames = IPHETH_RX_FRAMES_MAX,
99213805Shselasky		.bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES),
100213805Shselasky		.flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
101213805Shselasky		.callback = ipheth_bulk_read_callback,
102213805Shselasky		.timeout = 0,		/* no timeout */
103213805Shselasky	},
104213805Shselasky
105213805Shselasky	[IPHETH_BULK_TX] = {
106213805Shselasky		.type = UE_BULK,
107213805Shselasky		.endpoint = UE_ADDR_ANY,
108213805Shselasky		.direction = UE_DIR_TX,
109213805Shselasky		.frames = IPHETH_TX_FRAMES_MAX,
110213805Shselasky		.bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE),
111213805Shselasky		.flags = {.force_short_xfer = 1,},
112213805Shselasky		.callback = ipheth_bulk_write_callback,
113213805Shselasky		.timeout = IPHETH_TX_TIMEOUT,
114213805Shselasky	},
115213805Shselasky};
116213805Shselasky
117213805Shselaskystatic device_method_t ipheth_methods[] = {
118213805Shselasky	/* Device interface */
119213805Shselasky	DEVMETHOD(device_probe, ipheth_probe),
120213805Shselasky	DEVMETHOD(device_attach, ipheth_attach),
121213805Shselasky	DEVMETHOD(device_detach, ipheth_detach),
122213805Shselasky
123246128Ssbz	DEVMETHOD_END
124213805Shselasky};
125213805Shselasky
126213805Shselaskystatic driver_t ipheth_driver = {
127213805Shselasky	.name = "ipheth",
128213805Shselasky	.methods = ipheth_methods,
129213805Shselasky	.size = sizeof(struct ipheth_softc),
130213805Shselasky};
131213805Shselasky
132213805Shselaskystatic devclass_t ipheth_devclass;
133213805Shselasky
134213805ShselaskyDRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0);
135213805ShselaskyMODULE_VERSION(ipheth, 1);
136213805ShselaskyMODULE_DEPEND(ipheth, uether, 1, 1, 1);
137213805ShselaskyMODULE_DEPEND(ipheth, usb, 1, 1, 1);
138213805ShselaskyMODULE_DEPEND(ipheth, ether, 1, 1, 1);
139213805Shselasky
140213805Shselaskystatic const struct usb_ether_methods ipheth_ue_methods = {
141213805Shselasky	.ue_attach_post = ipheth_attach_post,
142213805Shselasky	.ue_start = ipheth_start,
143213805Shselasky	.ue_init = ipheth_init,
144213805Shselasky	.ue_tick = ipheth_tick,
145213805Shselasky	.ue_stop = ipheth_stop,
146213805Shselasky	.ue_setmulti = ipheth_setmulti,
147213805Shselasky	.ue_setpromisc = ipheth_setpromisc,
148213805Shselasky};
149213805Shselasky
150213805Shselasky#define	IPHETH_ID(v,p,c,sc,pt) \
151213805Shselasky    USB_VENDOR(v), USB_PRODUCT(p), \
152213805Shselasky    USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \
153213805Shselasky    USB_IFACE_PROTOCOL(pt)
154213805Shselasky
155223486Shselaskystatic const STRUCT_USB_HOST_ID ipheth_devs[] = {
156253670Shselasky#if 0
157213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE,
158213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
159213805Shselasky	    IPHETH_USBINTF_PROTO)},
160213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G,
161213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
162213805Shselasky	    IPHETH_USBINTF_PROTO)},
163213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS,
164213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
165213805Shselasky	    IPHETH_USBINTF_PROTO)},
166213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4,
167213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
168213805Shselasky	    IPHETH_USBINTF_PROTO)},
169251109Seadler	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S,
170251109Seadler	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
171251109Seadler	    IPHETH_USBINTF_PROTO)},
172241793Seadler	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5,
173241793Seadler	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
174241793Seadler	    IPHETH_USBINTF_PROTO)},
175253670Shselasky#else
176253670Shselasky	/* product agnostic interface match */
177253670Shselasky	{USB_VENDOR(USB_VENDOR_APPLE),
178253670Shselasky	 USB_IFACE_CLASS(IPHETH_USBINTF_CLASS),
179253670Shselasky	 USB_IFACE_SUBCLASS(IPHETH_USBINTF_SUBCLASS),
180253670Shselasky	 USB_IFACE_PROTOCOL(IPHETH_USBINTF_PROTO)},
181253670Shselasky#endif
182213805Shselasky};
183213805Shselasky
184213805Shselaskystatic int
185213805Shselaskyipheth_get_mac_addr(struct ipheth_softc *sc)
186213805Shselasky{
187213805Shselasky	struct usb_device_request req;
188213805Shselasky	int error;
189213805Shselasky
190213805Shselasky	req.bmRequestType = UT_READ_VENDOR_DEVICE;
191213805Shselasky	req.bRequest = IPHETH_CMD_GET_MACADDR;
192213805Shselasky	req.wValue[0] = 0;
193213805Shselasky	req.wValue[1] = 0;
194213805Shselasky	req.wIndex[0] = sc->sc_iface_no;
195213805Shselasky	req.wIndex[1] = 0;
196213805Shselasky	req.wLength[0] = ETHER_ADDR_LEN;
197213805Shselasky	req.wLength[1] = 0;
198213805Shselasky
199213805Shselasky	error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data);
200213805Shselasky
201213805Shselasky	if (error)
202213805Shselasky		return (error);
203213805Shselasky
204213805Shselasky	memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN);
205213805Shselasky
206213805Shselasky	return (0);
207213805Shselasky}
208213805Shselasky
209213805Shselaskystatic int
210213805Shselaskyipheth_probe(device_t dev)
211213805Shselasky{
212213805Shselasky	struct usb_attach_arg *uaa = device_get_ivars(dev);
213213805Shselasky
214213805Shselasky	if (uaa->usb_mode != USB_MODE_HOST)
215213805Shselasky		return (ENXIO);
216213805Shselasky
217213805Shselasky	return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa));
218213805Shselasky}
219213805Shselasky
220213805Shselaskystatic int
221213805Shselaskyipheth_attach(device_t dev)
222213805Shselasky{
223213805Shselasky	struct ipheth_softc *sc = device_get_softc(dev);
224213805Shselasky	struct usb_ether *ue = &sc->sc_ue;
225213805Shselasky	struct usb_attach_arg *uaa = device_get_ivars(dev);
226213805Shselasky	int error;
227213805Shselasky
228213805Shselasky	sc->sc_iface_no = uaa->info.bIfaceIndex;
229213805Shselasky
230213805Shselasky	device_set_usb_desc(dev);
231213805Shselasky
232213805Shselasky	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
233213805Shselasky
234213805Shselasky	error = usbd_set_alt_interface_index(uaa->device,
235213805Shselasky	    uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM);
236213805Shselasky	if (error) {
237213805Shselasky		device_printf(dev, "Cannot set alternate setting\n");
238213805Shselasky		goto detach;
239213805Shselasky	}
240213805Shselasky	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no,
241213805Shselasky	    sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx);
242213805Shselasky	if (error) {
243213805Shselasky		device_printf(dev, "Cannot setup USB transfers\n");
244213805Shselasky		goto detach;
245213805Shselasky	}
246213805Shselasky	ue->ue_sc = sc;
247213805Shselasky	ue->ue_dev = dev;
248213805Shselasky	ue->ue_udev = uaa->device;
249213805Shselasky	ue->ue_mtx = &sc->sc_mtx;
250213805Shselasky	ue->ue_methods = &ipheth_ue_methods;
251213805Shselasky
252213805Shselasky	error = ipheth_get_mac_addr(sc);
253213805Shselasky	if (error) {
254213805Shselasky		device_printf(dev, "Cannot get MAC address\n");
255213805Shselasky		goto detach;
256213805Shselasky	}
257213805Shselasky
258213805Shselasky	error = uether_ifattach(ue);
259213805Shselasky	if (error) {
260213805Shselasky		device_printf(dev, "could not attach interface\n");
261213805Shselasky		goto detach;
262213805Shselasky	}
263213805Shselasky	return (0);			/* success */
264213805Shselasky
265213805Shselaskydetach:
266213805Shselasky	ipheth_detach(dev);
267213805Shselasky	return (ENXIO);			/* failure */
268213805Shselasky}
269213805Shselasky
270213805Shselaskystatic int
271213805Shselaskyipheth_detach(device_t dev)
272213805Shselasky{
273213805Shselasky	struct ipheth_softc *sc = device_get_softc(dev);
274213805Shselasky	struct usb_ether *ue = &sc->sc_ue;
275213805Shselasky
276213805Shselasky	/* stop all USB transfers first */
277213805Shselasky	usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER);
278213805Shselasky
279213805Shselasky	uether_ifdetach(ue);
280213805Shselasky
281213805Shselasky	mtx_destroy(&sc->sc_mtx);
282213805Shselasky
283213805Shselasky	return (0);
284213805Shselasky}
285213805Shselasky
286213805Shselaskystatic void
287213805Shselaskyipheth_start(struct usb_ether *ue)
288213805Shselasky{
289213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
290213805Shselasky
291213805Shselasky	/*
292213805Shselasky	 * Start the USB transfers, if not already started:
293213805Shselasky	 */
294213805Shselasky	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]);
295213805Shselasky	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]);
296213805Shselasky}
297213805Shselasky
298213805Shselaskystatic void
299213805Shselaskyipheth_stop(struct usb_ether *ue)
300213805Shselasky{
301213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
302213805Shselasky
303213805Shselasky	/*
304213805Shselasky	 * Stop the USB transfers, if not already stopped:
305213805Shselasky	 */
306213805Shselasky	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]);
307213805Shselasky	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]);
308213805Shselasky}
309213805Shselasky
310213805Shselaskystatic void
311213805Shselaskyipheth_tick(struct usb_ether *ue)
312213805Shselasky{
313213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
314213805Shselasky	struct usb_device_request req;
315213805Shselasky	int error;
316213805Shselasky
317213805Shselasky	req.bmRequestType = UT_READ_VENDOR_DEVICE;
318213805Shselasky	req.bRequest = IPHETH_CMD_CARRIER_CHECK;
319213805Shselasky	req.wValue[0] = 0;
320213805Shselasky	req.wValue[1] = 0;
321213805Shselasky	req.wIndex[0] = sc->sc_iface_no;
322213805Shselasky	req.wIndex[1] = 0;
323213805Shselasky	req.wLength[0] = IPHETH_CTRL_BUF_SIZE;
324213805Shselasky	req.wLength[1] = 0;
325213805Shselasky
326213805Shselasky	error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT);
327213805Shselasky
328213805Shselasky	if (error)
329213805Shselasky		return;
330213805Shselasky
331213805Shselasky	sc->sc_carrier_on =
332213805Shselasky	    (sc->sc_data[0] == IPHETH_CARRIER_ON);
333213805Shselasky}
334213805Shselasky
335213805Shselaskystatic void
336213805Shselaskyipheth_attach_post(struct usb_ether *ue)
337213805Shselasky{
338213805Shselasky
339213805Shselasky}
340213805Shselasky
341213805Shselaskystatic void
342213805Shselaskyipheth_init(struct usb_ether *ue)
343213805Shselasky{
344213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
345213805Shselasky	struct ifnet *ifp = uether_getifp(ue);
346213805Shselasky
347213805Shselasky	IPHETH_LOCK_ASSERT(sc, MA_OWNED);
348213805Shselasky
349213805Shselasky	ifp->if_drv_flags |= IFF_DRV_RUNNING;
350213805Shselasky
351213805Shselasky	/* stall data write direction, which depends on USB mode */
352213805Shselasky	usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]);
353213805Shselasky
354213805Shselasky	/* start data transfers */
355213805Shselasky	ipheth_start(ue);
356213805Shselasky}
357213805Shselasky
358213805Shselaskystatic void
359213805Shselaskyipheth_setmulti(struct usb_ether *ue)
360213805Shselasky{
361213805Shselasky
362213805Shselasky}
363213805Shselasky
364213805Shselaskystatic void
365213805Shselaskyipheth_setpromisc(struct usb_ether *ue)
366213805Shselasky{
367213805Shselasky
368213805Shselasky}
369213805Shselasky
370213805Shselaskystatic void
371213805Shselaskyipheth_free_queue(struct mbuf **ppm, uint8_t n)
372213805Shselasky{
373213805Shselasky	uint8_t x;
374213805Shselasky
375213805Shselasky	for (x = 0; x != n; x++) {
376213805Shselasky		if (ppm[x] != NULL) {
377213805Shselasky			m_freem(ppm[x]);
378213805Shselasky			ppm[x] = NULL;
379213805Shselasky		}
380213805Shselasky	}
381213805Shselasky}
382213805Shselasky
383213805Shselaskystatic void
384213805Shselaskyipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
385213805Shselasky{
386213805Shselasky	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
387213805Shselasky	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
388213805Shselasky	struct usb_page_cache *pc;
389213805Shselasky	struct mbuf *m;
390213805Shselasky	uint8_t x;
391213805Shselasky	int actlen;
392213805Shselasky	int aframes;
393213805Shselasky
394213805Shselasky	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
395213805Shselasky
396213805Shselasky	DPRINTFN(1, "\n");
397213805Shselasky
398213805Shselasky	switch (USB_GET_STATE(xfer)) {
399213805Shselasky	case USB_ST_TRANSFERRED:
400213805Shselasky		DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
401213805Shselasky		    actlen, aframes);
402213805Shselasky
403271832Sglebius		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
404213805Shselasky
405213805Shselasky		/* free all previous TX buffers */
406213805Shselasky		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
407213805Shselasky
408213805Shselasky		/* FALLTHROUGH */
409213805Shselasky	case USB_ST_SETUP:
410213805Shselaskytr_setup:
411213805Shselasky		for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) {
412213805Shselasky
413213805Shselasky			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
414213805Shselasky
415213805Shselasky			if (m == NULL)
416213805Shselasky				break;
417213805Shselasky
418213805Shselasky			usbd_xfer_set_frame_offset(xfer,
419213805Shselasky			    x * IPHETH_BUF_SIZE, x);
420213805Shselasky
421213805Shselasky			pc = usbd_xfer_get_frame(xfer, x);
422213805Shselasky
423213805Shselasky			sc->sc_tx_buf[x] = m;
424213805Shselasky
425213805Shselasky			if (m->m_pkthdr.len > IPHETH_BUF_SIZE)
426213805Shselasky				m->m_pkthdr.len = IPHETH_BUF_SIZE;
427213805Shselasky
428213805Shselasky			usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
429213805Shselasky
430213805Shselasky			usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE);
431213805Shselasky
432213805Shselasky			if (IPHETH_BUF_SIZE != m->m_pkthdr.len) {
433213805Shselasky				usbd_frame_zero(pc, m->m_pkthdr.len,
434213805Shselasky					IPHETH_BUF_SIZE - m->m_pkthdr.len);
435213805Shselasky			}
436213805Shselasky
437213805Shselasky			/*
438213805Shselasky			 * If there's a BPF listener, bounce a copy of
439213805Shselasky			 * this frame to him:
440213805Shselasky			 */
441213805Shselasky			BPF_MTAP(ifp, m);
442213805Shselasky		}
443213805Shselasky		if (x != 0) {
444213805Shselasky			usbd_xfer_set_frames(xfer, x);
445213805Shselasky
446213805Shselasky			usbd_transfer_submit(xfer);
447213805Shselasky		}
448213805Shselasky		break;
449213805Shselasky
450213805Shselasky	default:			/* Error */
451213805Shselasky		DPRINTFN(11, "transfer error, %s\n",
452213805Shselasky		    usbd_errstr(error));
453213805Shselasky
454213805Shselasky		/* free all previous TX buffers */
455213805Shselasky		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
456213805Shselasky
457213805Shselasky		/* count output errors */
458271832Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
459213805Shselasky
460213805Shselasky		if (error != USB_ERR_CANCELLED) {
461213805Shselasky			/* try to clear stall first */
462213805Shselasky			usbd_xfer_set_stall(xfer);
463213805Shselasky			goto tr_setup;
464213805Shselasky		}
465213805Shselasky		break;
466213805Shselasky	}
467213805Shselasky}
468213805Shselasky
469213805Shselaskystatic void
470213805Shselaskyipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
471213805Shselasky{
472213805Shselasky	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
473213805Shselasky	struct mbuf *m;
474213805Shselasky	uint8_t x;
475213805Shselasky	int actlen;
476213805Shselasky	int aframes;
477213805Shselasky	int len;
478213805Shselasky
479213805Shselasky	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
480213805Shselasky
481213805Shselasky	switch (USB_GET_STATE(xfer)) {
482213805Shselasky	case USB_ST_TRANSFERRED:
483213805Shselasky
484213805Shselasky		DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
485213805Shselasky
486213805Shselasky		for (x = 0; x != aframes; x++) {
487213805Shselasky
488213805Shselasky			m = sc->sc_rx_buf[x];
489213805Shselasky			sc->sc_rx_buf[x] = NULL;
490213805Shselasky			len = usbd_xfer_frame_len(xfer, x);
491213805Shselasky
492233774Shselasky			if (len < (int)(sizeof(struct ether_header) +
493213805Shselasky			    IPHETH_RX_ADJ)) {
494213805Shselasky				m_freem(m);
495213805Shselasky				continue;
496213805Shselasky			}
497213805Shselasky
498213805Shselasky			m_adj(m, IPHETH_RX_ADJ);
499213805Shselasky
500213805Shselasky			/* queue up mbuf */
501213805Shselasky			uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ);
502213805Shselasky		}
503213805Shselasky
504213805Shselasky		/* FALLTHROUGH */
505213805Shselasky	case USB_ST_SETUP:
506213805Shselasky
507213805Shselasky		for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) {
508213805Shselasky			if (sc->sc_rx_buf[x] == NULL) {
509213805Shselasky				m = uether_newbuf();
510213805Shselasky				if (m == NULL)
511213805Shselasky					goto tr_stall;
512213805Shselasky
513213805Shselasky				/* cancel alignment for ethernet */
514213805Shselasky				m_adj(m, ETHER_ALIGN);
515213805Shselasky
516213805Shselasky				sc->sc_rx_buf[x] = m;
517213805Shselasky			} else {
518213805Shselasky				m = sc->sc_rx_buf[x];
519213805Shselasky			}
520213805Shselasky
521213805Shselasky			usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
522213805Shselasky		}
523213805Shselasky		/* set number of frames and start hardware */
524213805Shselasky		usbd_xfer_set_frames(xfer, x);
525213805Shselasky		usbd_transfer_submit(xfer);
526213805Shselasky		/* flush any received frames */
527213805Shselasky		uether_rxflush(&sc->sc_ue);
528213805Shselasky		break;
529213805Shselasky
530213805Shselasky	default:			/* Error */
531213805Shselasky		DPRINTF("error = %s\n", usbd_errstr(error));
532213805Shselasky
533213805Shselasky		if (error != USB_ERR_CANCELLED) {
534213805Shselasky	tr_stall:
535213805Shselasky			/* try to clear stall first */
536213805Shselasky			usbd_xfer_set_stall(xfer);
537213805Shselasky			usbd_xfer_set_frames(xfer, 0);
538213805Shselasky			usbd_transfer_submit(xfer);
539213805Shselasky			break;
540213805Shselasky		}
541213805Shselasky		/* need to free the RX-mbufs when we are cancelled */
542213805Shselasky		ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX);
543213805Shselasky		break;
544213805Shselasky	}
545213805Shselasky}
546