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$");
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
84227309Sedstatic SYSCTL_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
119246128Ssbz	DEVMETHOD_END
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[] = {
152253670Shselasky#if 0
153213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE,
154213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
155213805Shselasky	    IPHETH_USBINTF_PROTO)},
156213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G,
157213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
158213805Shselasky	    IPHETH_USBINTF_PROTO)},
159213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS,
160213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
161213805Shselasky	    IPHETH_USBINTF_PROTO)},
162213805Shselasky	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4,
163213805Shselasky	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
164213805Shselasky	    IPHETH_USBINTF_PROTO)},
165251109Seadler	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S,
166251109Seadler	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
167251109Seadler	    IPHETH_USBINTF_PROTO)},
168241793Seadler	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5,
169241793Seadler	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
170241793Seadler	    IPHETH_USBINTF_PROTO)},
171253670Shselasky#else
172253670Shselasky	/* product agnostic interface match */
173253670Shselasky	{USB_VENDOR(USB_VENDOR_APPLE),
174253670Shselasky	 USB_IFACE_CLASS(IPHETH_USBINTF_CLASS),
175253670Shselasky	 USB_IFACE_SUBCLASS(IPHETH_USBINTF_SUBCLASS),
176253670Shselasky	 USB_IFACE_PROTOCOL(IPHETH_USBINTF_PROTO)},
177253670Shselasky#endif
178213805Shselasky};
179213805Shselasky
180213805Shselaskystatic int
181213805Shselaskyipheth_get_mac_addr(struct ipheth_softc *sc)
182213805Shselasky{
183213805Shselasky	struct usb_device_request req;
184213805Shselasky	int error;
185213805Shselasky
186213805Shselasky	req.bmRequestType = UT_READ_VENDOR_DEVICE;
187213805Shselasky	req.bRequest = IPHETH_CMD_GET_MACADDR;
188213805Shselasky	req.wValue[0] = 0;
189213805Shselasky	req.wValue[1] = 0;
190213805Shselasky	req.wIndex[0] = sc->sc_iface_no;
191213805Shselasky	req.wIndex[1] = 0;
192213805Shselasky	req.wLength[0] = ETHER_ADDR_LEN;
193213805Shselasky	req.wLength[1] = 0;
194213805Shselasky
195213805Shselasky	error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data);
196213805Shselasky
197213805Shselasky	if (error)
198213805Shselasky		return (error);
199213805Shselasky
200213805Shselasky	memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN);
201213805Shselasky
202213805Shselasky	return (0);
203213805Shselasky}
204213805Shselasky
205213805Shselaskystatic int
206213805Shselaskyipheth_probe(device_t dev)
207213805Shselasky{
208213805Shselasky	struct usb_attach_arg *uaa = device_get_ivars(dev);
209213805Shselasky
210213805Shselasky	if (uaa->usb_mode != USB_MODE_HOST)
211213805Shselasky		return (ENXIO);
212213805Shselasky
213213805Shselasky	return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa));
214213805Shselasky}
215213805Shselasky
216213805Shselaskystatic int
217213805Shselaskyipheth_attach(device_t dev)
218213805Shselasky{
219213805Shselasky	struct ipheth_softc *sc = device_get_softc(dev);
220213805Shselasky	struct usb_ether *ue = &sc->sc_ue;
221213805Shselasky	struct usb_attach_arg *uaa = device_get_ivars(dev);
222213805Shselasky	int error;
223213805Shselasky
224213805Shselasky	sc->sc_iface_no = uaa->info.bIfaceIndex;
225213805Shselasky
226213805Shselasky	device_set_usb_desc(dev);
227213805Shselasky
228213805Shselasky	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
229213805Shselasky
230213805Shselasky	error = usbd_set_alt_interface_index(uaa->device,
231213805Shselasky	    uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM);
232213805Shselasky	if (error) {
233213805Shselasky		device_printf(dev, "Cannot set alternate setting\n");
234213805Shselasky		goto detach;
235213805Shselasky	}
236213805Shselasky	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no,
237213805Shselasky	    sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx);
238213805Shselasky	if (error) {
239213805Shselasky		device_printf(dev, "Cannot setup USB transfers\n");
240213805Shselasky		goto detach;
241213805Shselasky	}
242213805Shselasky	ue->ue_sc = sc;
243213805Shselasky	ue->ue_dev = dev;
244213805Shselasky	ue->ue_udev = uaa->device;
245213805Shselasky	ue->ue_mtx = &sc->sc_mtx;
246213805Shselasky	ue->ue_methods = &ipheth_ue_methods;
247213805Shselasky
248213805Shselasky	error = ipheth_get_mac_addr(sc);
249213805Shselasky	if (error) {
250213805Shselasky		device_printf(dev, "Cannot get MAC address\n");
251213805Shselasky		goto detach;
252213805Shselasky	}
253213805Shselasky
254213805Shselasky	error = uether_ifattach(ue);
255213805Shselasky	if (error) {
256213805Shselasky		device_printf(dev, "could not attach interface\n");
257213805Shselasky		goto detach;
258213805Shselasky	}
259213805Shselasky	return (0);			/* success */
260213805Shselasky
261213805Shselaskydetach:
262213805Shselasky	ipheth_detach(dev);
263213805Shselasky	return (ENXIO);			/* failure */
264213805Shselasky}
265213805Shselasky
266213805Shselaskystatic int
267213805Shselaskyipheth_detach(device_t dev)
268213805Shselasky{
269213805Shselasky	struct ipheth_softc *sc = device_get_softc(dev);
270213805Shselasky	struct usb_ether *ue = &sc->sc_ue;
271213805Shselasky
272213805Shselasky	/* stop all USB transfers first */
273213805Shselasky	usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER);
274213805Shselasky
275213805Shselasky	uether_ifdetach(ue);
276213805Shselasky
277213805Shselasky	mtx_destroy(&sc->sc_mtx);
278213805Shselasky
279213805Shselasky	return (0);
280213805Shselasky}
281213805Shselasky
282213805Shselaskystatic void
283213805Shselaskyipheth_start(struct usb_ether *ue)
284213805Shselasky{
285213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
286213805Shselasky
287213805Shselasky	/*
288213805Shselasky	 * Start the USB transfers, if not already started:
289213805Shselasky	 */
290213805Shselasky	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]);
291213805Shselasky	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]);
292213805Shselasky}
293213805Shselasky
294213805Shselaskystatic void
295213805Shselaskyipheth_stop(struct usb_ether *ue)
296213805Shselasky{
297213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
298213805Shselasky
299213805Shselasky	/*
300213805Shselasky	 * Stop the USB transfers, if not already stopped:
301213805Shselasky	 */
302213805Shselasky	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]);
303213805Shselasky	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]);
304213805Shselasky}
305213805Shselasky
306213805Shselaskystatic void
307213805Shselaskyipheth_tick(struct usb_ether *ue)
308213805Shselasky{
309213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
310213805Shselasky	struct usb_device_request req;
311213805Shselasky	int error;
312213805Shselasky
313213805Shselasky	req.bmRequestType = UT_READ_VENDOR_DEVICE;
314213805Shselasky	req.bRequest = IPHETH_CMD_CARRIER_CHECK;
315213805Shselasky	req.wValue[0] = 0;
316213805Shselasky	req.wValue[1] = 0;
317213805Shselasky	req.wIndex[0] = sc->sc_iface_no;
318213805Shselasky	req.wIndex[1] = 0;
319213805Shselasky	req.wLength[0] = IPHETH_CTRL_BUF_SIZE;
320213805Shselasky	req.wLength[1] = 0;
321213805Shselasky
322213805Shselasky	error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT);
323213805Shselasky
324213805Shselasky	if (error)
325213805Shselasky		return;
326213805Shselasky
327213805Shselasky	sc->sc_carrier_on =
328213805Shselasky	    (sc->sc_data[0] == IPHETH_CARRIER_ON);
329213805Shselasky}
330213805Shselasky
331213805Shselaskystatic void
332213805Shselaskyipheth_attach_post(struct usb_ether *ue)
333213805Shselasky{
334213805Shselasky
335213805Shselasky}
336213805Shselasky
337213805Shselaskystatic void
338213805Shselaskyipheth_init(struct usb_ether *ue)
339213805Shselasky{
340213805Shselasky	struct ipheth_softc *sc = uether_getsc(ue);
341213805Shselasky	struct ifnet *ifp = uether_getifp(ue);
342213805Shselasky
343213805Shselasky	IPHETH_LOCK_ASSERT(sc, MA_OWNED);
344213805Shselasky
345213805Shselasky	ifp->if_drv_flags |= IFF_DRV_RUNNING;
346213805Shselasky
347213805Shselasky	/* stall data write direction, which depends on USB mode */
348213805Shselasky	usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]);
349213805Shselasky
350213805Shselasky	/* start data transfers */
351213805Shselasky	ipheth_start(ue);
352213805Shselasky}
353213805Shselasky
354213805Shselaskystatic void
355213805Shselaskyipheth_setmulti(struct usb_ether *ue)
356213805Shselasky{
357213805Shselasky
358213805Shselasky}
359213805Shselasky
360213805Shselaskystatic void
361213805Shselaskyipheth_setpromisc(struct usb_ether *ue)
362213805Shselasky{
363213805Shselasky
364213805Shselasky}
365213805Shselasky
366213805Shselaskystatic void
367213805Shselaskyipheth_free_queue(struct mbuf **ppm, uint8_t n)
368213805Shselasky{
369213805Shselasky	uint8_t x;
370213805Shselasky
371213805Shselasky	for (x = 0; x != n; x++) {
372213805Shselasky		if (ppm[x] != NULL) {
373213805Shselasky			m_freem(ppm[x]);
374213805Shselasky			ppm[x] = NULL;
375213805Shselasky		}
376213805Shselasky	}
377213805Shselasky}
378213805Shselasky
379213805Shselaskystatic void
380213805Shselaskyipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
381213805Shselasky{
382213805Shselasky	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
383213805Shselasky	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
384213805Shselasky	struct usb_page_cache *pc;
385213805Shselasky	struct mbuf *m;
386213805Shselasky	uint8_t x;
387213805Shselasky	int actlen;
388213805Shselasky	int aframes;
389213805Shselasky
390213805Shselasky	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
391213805Shselasky
392213805Shselasky	DPRINTFN(1, "\n");
393213805Shselasky
394213805Shselasky	switch (USB_GET_STATE(xfer)) {
395213805Shselasky	case USB_ST_TRANSFERRED:
396213805Shselasky		DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
397213805Shselasky		    actlen, aframes);
398213805Shselasky
399213805Shselasky		ifp->if_opackets++;
400213805Shselasky
401213805Shselasky		/* free all previous TX buffers */
402213805Shselasky		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
403213805Shselasky
404213805Shselasky		/* FALLTHROUGH */
405213805Shselasky	case USB_ST_SETUP:
406213805Shselaskytr_setup:
407213805Shselasky		for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) {
408213805Shselasky
409213805Shselasky			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
410213805Shselasky
411213805Shselasky			if (m == NULL)
412213805Shselasky				break;
413213805Shselasky
414213805Shselasky			usbd_xfer_set_frame_offset(xfer,
415213805Shselasky			    x * IPHETH_BUF_SIZE, x);
416213805Shselasky
417213805Shselasky			pc = usbd_xfer_get_frame(xfer, x);
418213805Shselasky
419213805Shselasky			sc->sc_tx_buf[x] = m;
420213805Shselasky
421213805Shselasky			if (m->m_pkthdr.len > IPHETH_BUF_SIZE)
422213805Shselasky				m->m_pkthdr.len = IPHETH_BUF_SIZE;
423213805Shselasky
424213805Shselasky			usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
425213805Shselasky
426213805Shselasky			usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE);
427213805Shselasky
428213805Shselasky			if (IPHETH_BUF_SIZE != m->m_pkthdr.len) {
429213805Shselasky				usbd_frame_zero(pc, m->m_pkthdr.len,
430213805Shselasky					IPHETH_BUF_SIZE - m->m_pkthdr.len);
431213805Shselasky			}
432213805Shselasky
433213805Shselasky			/*
434213805Shselasky			 * If there's a BPF listener, bounce a copy of
435213805Shselasky			 * this frame to him:
436213805Shselasky			 */
437213805Shselasky			BPF_MTAP(ifp, m);
438213805Shselasky		}
439213805Shselasky		if (x != 0) {
440213805Shselasky			usbd_xfer_set_frames(xfer, x);
441213805Shselasky
442213805Shselasky			usbd_transfer_submit(xfer);
443213805Shselasky		}
444213805Shselasky		break;
445213805Shselasky
446213805Shselasky	default:			/* Error */
447213805Shselasky		DPRINTFN(11, "transfer error, %s\n",
448213805Shselasky		    usbd_errstr(error));
449213805Shselasky
450213805Shselasky		/* free all previous TX buffers */
451213805Shselasky		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
452213805Shselasky
453213805Shselasky		/* count output errors */
454213805Shselasky		ifp->if_oerrors++;
455213805Shselasky
456213805Shselasky		if (error != USB_ERR_CANCELLED) {
457213805Shselasky			/* try to clear stall first */
458213805Shselasky			usbd_xfer_set_stall(xfer);
459213805Shselasky			goto tr_setup;
460213805Shselasky		}
461213805Shselasky		break;
462213805Shselasky	}
463213805Shselasky}
464213805Shselasky
465213805Shselaskystatic void
466213805Shselaskyipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
467213805Shselasky{
468213805Shselasky	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
469213805Shselasky	struct mbuf *m;
470213805Shselasky	uint8_t x;
471213805Shselasky	int actlen;
472213805Shselasky	int aframes;
473213805Shselasky	int len;
474213805Shselasky
475213805Shselasky	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
476213805Shselasky
477213805Shselasky	switch (USB_GET_STATE(xfer)) {
478213805Shselasky	case USB_ST_TRANSFERRED:
479213805Shselasky
480213805Shselasky		DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
481213805Shselasky
482213805Shselasky		for (x = 0; x != aframes; x++) {
483213805Shselasky
484213805Shselasky			m = sc->sc_rx_buf[x];
485213805Shselasky			sc->sc_rx_buf[x] = NULL;
486213805Shselasky			len = usbd_xfer_frame_len(xfer, x);
487213805Shselasky
488233774Shselasky			if (len < (int)(sizeof(struct ether_header) +
489213805Shselasky			    IPHETH_RX_ADJ)) {
490213805Shselasky				m_freem(m);
491213805Shselasky				continue;
492213805Shselasky			}
493213805Shselasky
494213805Shselasky			m_adj(m, IPHETH_RX_ADJ);
495213805Shselasky
496213805Shselasky			/* queue up mbuf */
497213805Shselasky			uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ);
498213805Shselasky		}
499213805Shselasky
500213805Shselasky		/* FALLTHROUGH */
501213805Shselasky	case USB_ST_SETUP:
502213805Shselasky
503213805Shselasky		for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) {
504213805Shselasky			if (sc->sc_rx_buf[x] == NULL) {
505213805Shselasky				m = uether_newbuf();
506213805Shselasky				if (m == NULL)
507213805Shselasky					goto tr_stall;
508213805Shselasky
509213805Shselasky				/* cancel alignment for ethernet */
510213805Shselasky				m_adj(m, ETHER_ALIGN);
511213805Shselasky
512213805Shselasky				sc->sc_rx_buf[x] = m;
513213805Shselasky			} else {
514213805Shselasky				m = sc->sc_rx_buf[x];
515213805Shselasky			}
516213805Shselasky
517213805Shselasky			usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
518213805Shselasky		}
519213805Shselasky		/* set number of frames and start hardware */
520213805Shselasky		usbd_xfer_set_frames(xfer, x);
521213805Shselasky		usbd_transfer_submit(xfer);
522213805Shselasky		/* flush any received frames */
523213805Shselasky		uether_rxflush(&sc->sc_ue);
524213805Shselasky		break;
525213805Shselasky
526213805Shselasky	default:			/* Error */
527213805Shselasky		DPRINTF("error = %s\n", usbd_errstr(error));
528213805Shselasky
529213805Shselasky		if (error != USB_ERR_CANCELLED) {
530213805Shselasky	tr_stall:
531213805Shselasky			/* try to clear stall first */
532213805Shselasky			usbd_xfer_set_stall(xfer);
533213805Shselasky			usbd_xfer_set_frames(xfer, 0);
534213805Shselasky			usbd_transfer_submit(xfer);
535213805Shselasky			break;
536213805Shselasky		}
537213805Shselasky		/* need to free the RX-mbufs when we are cancelled */
538213805Shselasky		ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX);
539213805Shselasky		break;
540213805Shselasky	}
541213805Shselasky}
542