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