xref: /dragonfly/sys/bus/u4b/net/if_ipheth.c (revision 2b3f93ea)
112bd3c8bSSascha Wildner /*-
212bd3c8bSSascha Wildner  * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
312bd3c8bSSascha Wildner  * Copyright (c) 2009 Diego Giagio. All rights reserved.
412bd3c8bSSascha Wildner  *
512bd3c8bSSascha Wildner  * Redistribution and use in source and binary forms, with or without
612bd3c8bSSascha Wildner  * modification, are permitted provided that the following conditions
712bd3c8bSSascha Wildner  * are met:
812bd3c8bSSascha Wildner  * 1. Redistributions of source code must retain the above copyright
912bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer.
1012bd3c8bSSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1112bd3c8bSSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
1212bd3c8bSSascha Wildner  *    documentation and/or other materials provided with the distribution.
1312bd3c8bSSascha Wildner  *
1412bd3c8bSSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1512bd3c8bSSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1612bd3c8bSSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1712bd3c8bSSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1812bd3c8bSSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1912bd3c8bSSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2012bd3c8bSSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2112bd3c8bSSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2212bd3c8bSSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2312bd3c8bSSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2412bd3c8bSSascha Wildner  * SUCH DAMAGE.
2579be21c5SSascha Wildner  *
2679be21c5SSascha Wildner  * $FreeBSD: head/sys/dev/usb/net/if_ipheth.c 271832 2014-09-18 21:09:22Z glebius $
2712bd3c8bSSascha Wildner  */
2812bd3c8bSSascha Wildner 
2912bd3c8bSSascha Wildner /*
3012bd3c8bSSascha Wildner  * Thanks to Diego Giagio for figuring out the programming details for
3112bd3c8bSSascha Wildner  * the Apple iPhone Ethernet driver.
3212bd3c8bSSascha Wildner  */
3312bd3c8bSSascha Wildner 
3412bd3c8bSSascha Wildner #include <sys/stdint.h>
3512bd3c8bSSascha Wildner #include <sys/param.h>
3612bd3c8bSSascha Wildner #include <sys/queue.h>
3712bd3c8bSSascha Wildner #include <sys/types.h>
3812bd3c8bSSascha Wildner #include <sys/systm.h>
3912bd3c8bSSascha Wildner #include <sys/kernel.h>
4012bd3c8bSSascha Wildner #include <sys/bus.h>
4112bd3c8bSSascha Wildner #include <sys/module.h>
4212bd3c8bSSascha Wildner #include <sys/lock.h>
4312bd3c8bSSascha Wildner #include <sys/condvar.h>
4479be21c5SSascha Wildner #include <sys/socket.h>
4512bd3c8bSSascha Wildner #include <sys/sysctl.h>
4612bd3c8bSSascha Wildner #include <sys/unistd.h>
4712bd3c8bSSascha Wildner #include <sys/callout.h>
4812bd3c8bSSascha Wildner #include <sys/malloc.h>
49*2b3f93eaSMatthew Dillon #include <sys/caps.h>
5012bd3c8bSSascha Wildner 
5179be21c5SSascha Wildner #include <net/if.h>
5279be21c5SSascha Wildner #include <net/if_var.h>
5379be21c5SSascha Wildner #include <net/ifq_var.h>
5479be21c5SSascha Wildner 
5579be21c5SSascha Wildner #include <bus/u4b/usb.h>
5679be21c5SSascha Wildner #include <bus/u4b/usbdi.h>
5779be21c5SSascha Wildner #include <bus/u4b/usbdi_util.h>
5812bd3c8bSSascha Wildner #include "usbdevs.h"
5912bd3c8bSSascha Wildner 
6012bd3c8bSSascha Wildner #define	USB_DEBUG_VAR ipheth_debug
6179be21c5SSascha Wildner #include <bus/u4b/usb_debug.h>
6279be21c5SSascha Wildner #include <bus/u4b/usb_process.h>
6312bd3c8bSSascha Wildner 
6479be21c5SSascha Wildner #include <bus/u4b/net/usb_ethernet.h>
6579be21c5SSascha Wildner #include <bus/u4b/net/if_iphethvar.h>
6612bd3c8bSSascha Wildner 
6712bd3c8bSSascha Wildner static device_probe_t ipheth_probe;
6812bd3c8bSSascha Wildner static device_attach_t ipheth_attach;
6912bd3c8bSSascha Wildner static device_detach_t ipheth_detach;
7012bd3c8bSSascha Wildner 
7112bd3c8bSSascha Wildner static usb_callback_t ipheth_bulk_write_callback;
7212bd3c8bSSascha Wildner static usb_callback_t ipheth_bulk_read_callback;
7312bd3c8bSSascha Wildner 
7412bd3c8bSSascha Wildner static uether_fn_t ipheth_attach_post;
7512bd3c8bSSascha Wildner static uether_fn_t ipheth_tick;
7612bd3c8bSSascha Wildner static uether_fn_t ipheth_init;
7712bd3c8bSSascha Wildner static uether_fn_t ipheth_stop;
7812bd3c8bSSascha Wildner static uether_fn_t ipheth_start;
7912bd3c8bSSascha Wildner static uether_fn_t ipheth_setmulti;
8012bd3c8bSSascha Wildner static uether_fn_t ipheth_setpromisc;
8112bd3c8bSSascha Wildner 
8212bd3c8bSSascha Wildner #ifdef USB_DEBUG
8312bd3c8bSSascha Wildner static int ipheth_debug = 0;
8412bd3c8bSSascha Wildner 
8512bd3c8bSSascha Wildner static SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet");
86a9b765b7SSascha Wildner SYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RW, &ipheth_debug, 0, "Debug level");
8712bd3c8bSSascha Wildner #endif
8812bd3c8bSSascha Wildner 
8912bd3c8bSSascha Wildner static const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = {
9012bd3c8bSSascha Wildner 
9112bd3c8bSSascha Wildner 	[IPHETH_BULK_RX] = {
9212bd3c8bSSascha Wildner 		.type = UE_BULK,
9312bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
9412bd3c8bSSascha Wildner 		.direction = UE_DIR_RX,
9512bd3c8bSSascha Wildner 		.frames = IPHETH_RX_FRAMES_MAX,
9612bd3c8bSSascha Wildner 		.bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES),
9712bd3c8bSSascha Wildner 		.flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
9812bd3c8bSSascha Wildner 		.callback = ipheth_bulk_read_callback,
9912bd3c8bSSascha Wildner 		.timeout = 0,		/* no timeout */
10012bd3c8bSSascha Wildner 	},
10112bd3c8bSSascha Wildner 
10212bd3c8bSSascha Wildner 	[IPHETH_BULK_TX] = {
10312bd3c8bSSascha Wildner 		.type = UE_BULK,
10412bd3c8bSSascha Wildner 		.endpoint = UE_ADDR_ANY,
10512bd3c8bSSascha Wildner 		.direction = UE_DIR_TX,
10612bd3c8bSSascha Wildner 		.frames = IPHETH_TX_FRAMES_MAX,
10712bd3c8bSSascha Wildner 		.bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE),
10812bd3c8bSSascha Wildner 		.flags = {.force_short_xfer = 1,},
10912bd3c8bSSascha Wildner 		.callback = ipheth_bulk_write_callback,
11012bd3c8bSSascha Wildner 		.timeout = IPHETH_TX_TIMEOUT,
11112bd3c8bSSascha Wildner 	},
11212bd3c8bSSascha Wildner };
11312bd3c8bSSascha Wildner 
11412bd3c8bSSascha Wildner static device_method_t ipheth_methods[] = {
11512bd3c8bSSascha Wildner 	/* Device interface */
11612bd3c8bSSascha Wildner 	DEVMETHOD(device_probe, ipheth_probe),
11712bd3c8bSSascha Wildner 	DEVMETHOD(device_attach, ipheth_attach),
11812bd3c8bSSascha Wildner 	DEVMETHOD(device_detach, ipheth_detach),
11912bd3c8bSSascha Wildner 
120d3c9c58eSSascha Wildner 	DEVMETHOD_END
12112bd3c8bSSascha Wildner };
12212bd3c8bSSascha Wildner 
12312bd3c8bSSascha Wildner static driver_t ipheth_driver = {
12412bd3c8bSSascha Wildner 	.name = "ipheth",
12512bd3c8bSSascha Wildner 	.methods = ipheth_methods,
12612bd3c8bSSascha Wildner 	.size = sizeof(struct ipheth_softc),
12712bd3c8bSSascha Wildner };
12812bd3c8bSSascha Wildner 
12912bd3c8bSSascha Wildner static devclass_t ipheth_devclass;
13012bd3c8bSSascha Wildner 
1313a25be87SSascha Wildner DRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, NULL);
13212bd3c8bSSascha Wildner MODULE_VERSION(ipheth, 1);
13312bd3c8bSSascha Wildner MODULE_DEPEND(ipheth, uether, 1, 1, 1);
13412bd3c8bSSascha Wildner MODULE_DEPEND(ipheth, usb, 1, 1, 1);
13512bd3c8bSSascha Wildner MODULE_DEPEND(ipheth, ether, 1, 1, 1);
13612bd3c8bSSascha Wildner 
13712bd3c8bSSascha Wildner static const struct usb_ether_methods ipheth_ue_methods = {
13812bd3c8bSSascha Wildner 	.ue_attach_post = ipheth_attach_post,
13912bd3c8bSSascha Wildner 	.ue_start = ipheth_start,
14012bd3c8bSSascha Wildner 	.ue_init = ipheth_init,
14112bd3c8bSSascha Wildner 	.ue_tick = ipheth_tick,
14212bd3c8bSSascha Wildner 	.ue_stop = ipheth_stop,
14312bd3c8bSSascha Wildner 	.ue_setmulti = ipheth_setmulti,
14412bd3c8bSSascha Wildner 	.ue_setpromisc = ipheth_setpromisc,
14512bd3c8bSSascha Wildner };
14612bd3c8bSSascha Wildner 
14712bd3c8bSSascha Wildner #define	IPHETH_ID(v,p,c,sc,pt) \
14812bd3c8bSSascha Wildner     USB_VENDOR(v), USB_PRODUCT(p), \
14912bd3c8bSSascha Wildner     USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \
15012bd3c8bSSascha Wildner     USB_IFACE_PROTOCOL(pt)
15112bd3c8bSSascha Wildner 
15212bd3c8bSSascha Wildner static const STRUCT_USB_HOST_ID ipheth_devs[] = {
15379be21c5SSascha Wildner #if 0
15412bd3c8bSSascha Wildner 	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE,
15512bd3c8bSSascha Wildner 	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
15612bd3c8bSSascha Wildner 	    IPHETH_USBINTF_PROTO)},
15712bd3c8bSSascha Wildner 	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G,
15812bd3c8bSSascha Wildner 	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
15912bd3c8bSSascha Wildner 	    IPHETH_USBINTF_PROTO)},
16012bd3c8bSSascha Wildner 	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS,
16112bd3c8bSSascha Wildner 	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
16212bd3c8bSSascha Wildner 	    IPHETH_USBINTF_PROTO)},
16312bd3c8bSSascha Wildner 	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4,
16412bd3c8bSSascha Wildner 	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
16512bd3c8bSSascha Wildner 	    IPHETH_USBINTF_PROTO)},
16679be21c5SSascha Wildner 	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S,
16779be21c5SSascha Wildner 	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
16879be21c5SSascha Wildner 	    IPHETH_USBINTF_PROTO)},
16979be21c5SSascha Wildner 	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5,
17079be21c5SSascha Wildner 	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
17179be21c5SSascha Wildner 	    IPHETH_USBINTF_PROTO)},
17279be21c5SSascha Wildner #else
17379be21c5SSascha Wildner 	/* product agnostic interface match */
17479be21c5SSascha Wildner 	{USB_VENDOR(USB_VENDOR_APPLE),
17579be21c5SSascha Wildner 	 USB_IFACE_CLASS(IPHETH_USBINTF_CLASS),
17679be21c5SSascha Wildner 	 USB_IFACE_SUBCLASS(IPHETH_USBINTF_SUBCLASS),
17779be21c5SSascha Wildner 	 USB_IFACE_PROTOCOL(IPHETH_USBINTF_PROTO)},
17879be21c5SSascha Wildner #endif
17912bd3c8bSSascha Wildner };
18012bd3c8bSSascha Wildner 
18112bd3c8bSSascha Wildner static int
ipheth_get_mac_addr(struct ipheth_softc * sc)18212bd3c8bSSascha Wildner ipheth_get_mac_addr(struct ipheth_softc *sc)
18312bd3c8bSSascha Wildner {
18412bd3c8bSSascha Wildner 	struct usb_device_request req;
18512bd3c8bSSascha Wildner 	int error;
18612bd3c8bSSascha Wildner 
18712bd3c8bSSascha Wildner 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
18812bd3c8bSSascha Wildner 	req.bRequest = IPHETH_CMD_GET_MACADDR;
18912bd3c8bSSascha Wildner 	req.wValue[0] = 0;
19012bd3c8bSSascha Wildner 	req.wValue[1] = 0;
19112bd3c8bSSascha Wildner 	req.wIndex[0] = sc->sc_iface_no;
19212bd3c8bSSascha Wildner 	req.wIndex[1] = 0;
19312bd3c8bSSascha Wildner 	req.wLength[0] = ETHER_ADDR_LEN;
19412bd3c8bSSascha Wildner 	req.wLength[1] = 0;
19512bd3c8bSSascha Wildner 
19612bd3c8bSSascha Wildner 	error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data);
19712bd3c8bSSascha Wildner 
19812bd3c8bSSascha Wildner 	if (error)
19912bd3c8bSSascha Wildner 		return (error);
20012bd3c8bSSascha Wildner 
20112bd3c8bSSascha Wildner 	memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN);
20212bd3c8bSSascha Wildner 
20312bd3c8bSSascha Wildner 	return (0);
20412bd3c8bSSascha Wildner }
20512bd3c8bSSascha Wildner 
20612bd3c8bSSascha Wildner static int
ipheth_probe(device_t dev)20712bd3c8bSSascha Wildner ipheth_probe(device_t dev)
20812bd3c8bSSascha Wildner {
20912bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
21012bd3c8bSSascha Wildner 
21112bd3c8bSSascha Wildner 	if (uaa->usb_mode != USB_MODE_HOST)
21212bd3c8bSSascha Wildner 		return (ENXIO);
21312bd3c8bSSascha Wildner 
21412bd3c8bSSascha Wildner 	return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa));
21512bd3c8bSSascha Wildner }
21612bd3c8bSSascha Wildner 
21712bd3c8bSSascha Wildner static int
ipheth_attach(device_t dev)21812bd3c8bSSascha Wildner ipheth_attach(device_t dev)
21912bd3c8bSSascha Wildner {
22012bd3c8bSSascha Wildner 	struct ipheth_softc *sc = device_get_softc(dev);
22112bd3c8bSSascha Wildner 	struct usb_ether *ue = &sc->sc_ue;
22212bd3c8bSSascha Wildner 	struct usb_attach_arg *uaa = device_get_ivars(dev);
22312bd3c8bSSascha Wildner 	int error;
22412bd3c8bSSascha Wildner 
22512bd3c8bSSascha Wildner 	sc->sc_iface_no = uaa->info.bIfaceIndex;
22612bd3c8bSSascha Wildner 
22712bd3c8bSSascha Wildner 	device_set_usb_desc(dev);
22812bd3c8bSSascha Wildner 
22979be21c5SSascha Wildner 	lockinit(&sc->sc_lock, device_get_nameunit(dev), 0, LK_CANRECURSE);
23012bd3c8bSSascha Wildner 
23112bd3c8bSSascha Wildner 	error = usbd_set_alt_interface_index(uaa->device,
23212bd3c8bSSascha Wildner 	    uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM);
23312bd3c8bSSascha Wildner 	if (error) {
23412bd3c8bSSascha Wildner 		device_printf(dev, "Cannot set alternate setting\n");
23512bd3c8bSSascha Wildner 		goto detach;
23612bd3c8bSSascha Wildner 	}
23712bd3c8bSSascha Wildner 	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no,
23879be21c5SSascha Wildner 	    sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_lock);
23912bd3c8bSSascha Wildner 	if (error) {
24012bd3c8bSSascha Wildner 		device_printf(dev, "Cannot setup USB transfers\n");
24112bd3c8bSSascha Wildner 		goto detach;
24212bd3c8bSSascha Wildner 	}
24312bd3c8bSSascha Wildner 	ue->ue_sc = sc;
24412bd3c8bSSascha Wildner 	ue->ue_dev = dev;
24512bd3c8bSSascha Wildner 	ue->ue_udev = uaa->device;
24679be21c5SSascha Wildner 	ue->ue_lock = &sc->sc_lock;
24712bd3c8bSSascha Wildner 	ue->ue_methods = &ipheth_ue_methods;
24812bd3c8bSSascha Wildner 
24912bd3c8bSSascha Wildner 	error = ipheth_get_mac_addr(sc);
25012bd3c8bSSascha Wildner 	if (error) {
25112bd3c8bSSascha Wildner 		device_printf(dev, "Cannot get MAC address\n");
25212bd3c8bSSascha Wildner 		goto detach;
25312bd3c8bSSascha Wildner 	}
25412bd3c8bSSascha Wildner 
25512bd3c8bSSascha Wildner 	error = uether_ifattach(ue);
25612bd3c8bSSascha Wildner 	if (error) {
25712bd3c8bSSascha Wildner 		device_printf(dev, "could not attach interface\n");
25812bd3c8bSSascha Wildner 		goto detach;
25912bd3c8bSSascha Wildner 	}
26012bd3c8bSSascha Wildner 	return (0);			/* success */
26112bd3c8bSSascha Wildner 
26212bd3c8bSSascha Wildner detach:
26312bd3c8bSSascha Wildner 	ipheth_detach(dev);
26412bd3c8bSSascha Wildner 	return (ENXIO);			/* failure */
26512bd3c8bSSascha Wildner }
26612bd3c8bSSascha Wildner 
26712bd3c8bSSascha Wildner static int
ipheth_detach(device_t dev)26812bd3c8bSSascha Wildner ipheth_detach(device_t dev)
26912bd3c8bSSascha Wildner {
27012bd3c8bSSascha Wildner 	struct ipheth_softc *sc = device_get_softc(dev);
27112bd3c8bSSascha Wildner 	struct usb_ether *ue = &sc->sc_ue;
27212bd3c8bSSascha Wildner 
27312bd3c8bSSascha Wildner 	/* stop all USB transfers first */
27412bd3c8bSSascha Wildner 	usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER);
27512bd3c8bSSascha Wildner 
27612bd3c8bSSascha Wildner 	uether_ifdetach(ue);
27712bd3c8bSSascha Wildner 
27879be21c5SSascha Wildner 	lockuninit(&sc->sc_lock);
27912bd3c8bSSascha Wildner 
28012bd3c8bSSascha Wildner 	return (0);
28112bd3c8bSSascha Wildner }
28212bd3c8bSSascha Wildner 
28312bd3c8bSSascha Wildner static void
ipheth_start(struct usb_ether * ue)28412bd3c8bSSascha Wildner ipheth_start(struct usb_ether *ue)
28512bd3c8bSSascha Wildner {
28612bd3c8bSSascha Wildner 	struct ipheth_softc *sc = uether_getsc(ue);
28712bd3c8bSSascha Wildner 
28812bd3c8bSSascha Wildner 	/*
28912bd3c8bSSascha Wildner 	 * Start the USB transfers, if not already started:
29012bd3c8bSSascha Wildner 	 */
29112bd3c8bSSascha Wildner 	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]);
29212bd3c8bSSascha Wildner 	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]);
29312bd3c8bSSascha Wildner }
29412bd3c8bSSascha Wildner 
29512bd3c8bSSascha Wildner static void
ipheth_stop(struct usb_ether * ue)29612bd3c8bSSascha Wildner ipheth_stop(struct usb_ether *ue)
29712bd3c8bSSascha Wildner {
29812bd3c8bSSascha Wildner 	struct ipheth_softc *sc = uether_getsc(ue);
29912bd3c8bSSascha Wildner 
30012bd3c8bSSascha Wildner 	/*
30112bd3c8bSSascha Wildner 	 * Stop the USB transfers, if not already stopped:
30212bd3c8bSSascha Wildner 	 */
30312bd3c8bSSascha Wildner 	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]);
30412bd3c8bSSascha Wildner 	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]);
30512bd3c8bSSascha Wildner }
30612bd3c8bSSascha Wildner 
30712bd3c8bSSascha Wildner static void
ipheth_tick(struct usb_ether * ue)30812bd3c8bSSascha Wildner ipheth_tick(struct usb_ether *ue)
30912bd3c8bSSascha Wildner {
31012bd3c8bSSascha Wildner 	struct ipheth_softc *sc = uether_getsc(ue);
31112bd3c8bSSascha Wildner 	struct usb_device_request req;
31212bd3c8bSSascha Wildner 	int error;
31312bd3c8bSSascha Wildner 
31412bd3c8bSSascha Wildner 	req.bmRequestType = UT_READ_VENDOR_DEVICE;
31512bd3c8bSSascha Wildner 	req.bRequest = IPHETH_CMD_CARRIER_CHECK;
31612bd3c8bSSascha Wildner 	req.wValue[0] = 0;
31712bd3c8bSSascha Wildner 	req.wValue[1] = 0;
31812bd3c8bSSascha Wildner 	req.wIndex[0] = sc->sc_iface_no;
31912bd3c8bSSascha Wildner 	req.wIndex[1] = 0;
32012bd3c8bSSascha Wildner 	req.wLength[0] = IPHETH_CTRL_BUF_SIZE;
32112bd3c8bSSascha Wildner 	req.wLength[1] = 0;
32212bd3c8bSSascha Wildner 
32312bd3c8bSSascha Wildner 	error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT);
32412bd3c8bSSascha Wildner 
32512bd3c8bSSascha Wildner 	if (error)
32612bd3c8bSSascha Wildner 		return;
32712bd3c8bSSascha Wildner 
32812bd3c8bSSascha Wildner 	sc->sc_carrier_on =
32912bd3c8bSSascha Wildner 	    (sc->sc_data[0] == IPHETH_CARRIER_ON);
33012bd3c8bSSascha Wildner }
33112bd3c8bSSascha Wildner 
33212bd3c8bSSascha Wildner static void
ipheth_attach_post(struct usb_ether * ue)33312bd3c8bSSascha Wildner ipheth_attach_post(struct usb_ether *ue)
33412bd3c8bSSascha Wildner {
33512bd3c8bSSascha Wildner 
33612bd3c8bSSascha Wildner }
33712bd3c8bSSascha Wildner 
33812bd3c8bSSascha Wildner static void
ipheth_init(struct usb_ether * ue)33912bd3c8bSSascha Wildner ipheth_init(struct usb_ether *ue)
34012bd3c8bSSascha Wildner {
34112bd3c8bSSascha Wildner 	struct ipheth_softc *sc = uether_getsc(ue);
34212bd3c8bSSascha Wildner 	struct ifnet *ifp = uether_getifp(ue);
34312bd3c8bSSascha Wildner 
34479be21c5SSascha Wildner 	IPHETH_LOCK_ASSERT(sc);
34512bd3c8bSSascha Wildner 
34679be21c5SSascha Wildner 	ifp->if_flags |= IFF_RUNNING;
34712bd3c8bSSascha Wildner 
34812bd3c8bSSascha Wildner 	/* stall data write direction, which depends on USB mode */
34912bd3c8bSSascha Wildner 	usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]);
35012bd3c8bSSascha Wildner 
35112bd3c8bSSascha Wildner 	/* start data transfers */
35212bd3c8bSSascha Wildner 	ipheth_start(ue);
35312bd3c8bSSascha Wildner }
35412bd3c8bSSascha Wildner 
35512bd3c8bSSascha Wildner static void
ipheth_setmulti(struct usb_ether * ue)35612bd3c8bSSascha Wildner ipheth_setmulti(struct usb_ether *ue)
35712bd3c8bSSascha Wildner {
35812bd3c8bSSascha Wildner 
35912bd3c8bSSascha Wildner }
36012bd3c8bSSascha Wildner 
36112bd3c8bSSascha Wildner static void
ipheth_setpromisc(struct usb_ether * ue)36212bd3c8bSSascha Wildner ipheth_setpromisc(struct usb_ether *ue)
36312bd3c8bSSascha Wildner {
36412bd3c8bSSascha Wildner 
36512bd3c8bSSascha Wildner }
36612bd3c8bSSascha Wildner 
36712bd3c8bSSascha Wildner static void
ipheth_free_queue(struct mbuf ** ppm,uint8_t n)36812bd3c8bSSascha Wildner ipheth_free_queue(struct mbuf **ppm, uint8_t n)
36912bd3c8bSSascha Wildner {
37012bd3c8bSSascha Wildner 	uint8_t x;
37112bd3c8bSSascha Wildner 
37212bd3c8bSSascha Wildner 	for (x = 0; x != n; x++) {
37312bd3c8bSSascha Wildner 		if (ppm[x] != NULL) {
37412bd3c8bSSascha Wildner 			m_freem(ppm[x]);
37512bd3c8bSSascha Wildner 			ppm[x] = NULL;
37612bd3c8bSSascha Wildner 		}
37712bd3c8bSSascha Wildner 	}
37812bd3c8bSSascha Wildner }
37912bd3c8bSSascha Wildner 
38012bd3c8bSSascha Wildner static void
ipheth_bulk_write_callback(struct usb_xfer * xfer,usb_error_t error)38112bd3c8bSSascha Wildner ipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
38212bd3c8bSSascha Wildner {
38312bd3c8bSSascha Wildner 	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
38412bd3c8bSSascha Wildner 	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
38512bd3c8bSSascha Wildner 	struct usb_page_cache *pc;
38612bd3c8bSSascha Wildner 	struct mbuf *m;
38712bd3c8bSSascha Wildner 	uint8_t x;
38812bd3c8bSSascha Wildner 	int actlen;
38912bd3c8bSSascha Wildner 	int aframes;
39012bd3c8bSSascha Wildner 
39112bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
39212bd3c8bSSascha Wildner 
39312bd3c8bSSascha Wildner 	DPRINTFN(1, "\n");
39412bd3c8bSSascha Wildner 
39512bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
39612bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
39712bd3c8bSSascha Wildner 		DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
39812bd3c8bSSascha Wildner 		    actlen, aframes);
39912bd3c8bSSascha Wildner 
40079be21c5SSascha Wildner 		IFNET_STAT_INC(ifp, opackets, 1);
40112bd3c8bSSascha Wildner 
40212bd3c8bSSascha Wildner 		/* free all previous TX buffers */
40312bd3c8bSSascha Wildner 		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
40412bd3c8bSSascha Wildner 
40512bd3c8bSSascha Wildner 		/* FALLTHROUGH */
40612bd3c8bSSascha Wildner 	case USB_ST_SETUP:
40712bd3c8bSSascha Wildner tr_setup:
40812bd3c8bSSascha Wildner 		for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) {
40912bd3c8bSSascha Wildner 
41079be21c5SSascha Wildner 			m = ifq_dequeue(&ifp->if_snd);
41112bd3c8bSSascha Wildner 
41212bd3c8bSSascha Wildner 			if (m == NULL)
41312bd3c8bSSascha Wildner 				break;
41412bd3c8bSSascha Wildner 
41512bd3c8bSSascha Wildner 			usbd_xfer_set_frame_offset(xfer,
41612bd3c8bSSascha Wildner 			    x * IPHETH_BUF_SIZE, x);
41712bd3c8bSSascha Wildner 
41812bd3c8bSSascha Wildner 			pc = usbd_xfer_get_frame(xfer, x);
41912bd3c8bSSascha Wildner 
42012bd3c8bSSascha Wildner 			sc->sc_tx_buf[x] = m;
42112bd3c8bSSascha Wildner 
42212bd3c8bSSascha Wildner 			if (m->m_pkthdr.len > IPHETH_BUF_SIZE)
42312bd3c8bSSascha Wildner 				m->m_pkthdr.len = IPHETH_BUF_SIZE;
42412bd3c8bSSascha Wildner 
42512bd3c8bSSascha Wildner 			usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
42612bd3c8bSSascha Wildner 
42712bd3c8bSSascha Wildner 			usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE);
42812bd3c8bSSascha Wildner 
42912bd3c8bSSascha Wildner 			if (IPHETH_BUF_SIZE != m->m_pkthdr.len) {
43012bd3c8bSSascha Wildner 				usbd_frame_zero(pc, m->m_pkthdr.len,
43112bd3c8bSSascha Wildner 					IPHETH_BUF_SIZE - m->m_pkthdr.len);
43212bd3c8bSSascha Wildner 			}
43312bd3c8bSSascha Wildner 
43412bd3c8bSSascha Wildner 			/*
43512bd3c8bSSascha Wildner 			 * If there's a BPF listener, bounce a copy of
43612bd3c8bSSascha Wildner 			 * this frame to him:
43712bd3c8bSSascha Wildner 			 */
43812bd3c8bSSascha Wildner 			BPF_MTAP(ifp, m);
43912bd3c8bSSascha Wildner 		}
44012bd3c8bSSascha Wildner 		if (x != 0) {
44112bd3c8bSSascha Wildner 			usbd_xfer_set_frames(xfer, x);
44212bd3c8bSSascha Wildner 
44312bd3c8bSSascha Wildner 			usbd_transfer_submit(xfer);
44412bd3c8bSSascha Wildner 		}
44512bd3c8bSSascha Wildner 		break;
44612bd3c8bSSascha Wildner 
44712bd3c8bSSascha Wildner 	default:			/* Error */
44812bd3c8bSSascha Wildner 		DPRINTFN(11, "transfer error, %s\n",
44912bd3c8bSSascha Wildner 		    usbd_errstr(error));
45012bd3c8bSSascha Wildner 
45112bd3c8bSSascha Wildner 		/* free all previous TX buffers */
45212bd3c8bSSascha Wildner 		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
45312bd3c8bSSascha Wildner 
45412bd3c8bSSascha Wildner 		/* count output errors */
45579be21c5SSascha Wildner 		IFNET_STAT_INC(ifp, oerrors, 1);
45612bd3c8bSSascha Wildner 
45712bd3c8bSSascha Wildner 		if (error != USB_ERR_CANCELLED) {
45812bd3c8bSSascha Wildner 			/* try to clear stall first */
45912bd3c8bSSascha Wildner 			usbd_xfer_set_stall(xfer);
46012bd3c8bSSascha Wildner 			goto tr_setup;
46112bd3c8bSSascha Wildner 		}
46212bd3c8bSSascha Wildner 		break;
46312bd3c8bSSascha Wildner 	}
46412bd3c8bSSascha Wildner }
46512bd3c8bSSascha Wildner 
46612bd3c8bSSascha Wildner static void
ipheth_bulk_read_callback(struct usb_xfer * xfer,usb_error_t error)46712bd3c8bSSascha Wildner ipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
46812bd3c8bSSascha Wildner {
46912bd3c8bSSascha Wildner 	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
47012bd3c8bSSascha Wildner 	struct mbuf *m;
47112bd3c8bSSascha Wildner 	uint8_t x;
47212bd3c8bSSascha Wildner 	int actlen;
47312bd3c8bSSascha Wildner 	int aframes;
47412bd3c8bSSascha Wildner 	int len;
47512bd3c8bSSascha Wildner 
47612bd3c8bSSascha Wildner 	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
47712bd3c8bSSascha Wildner 
47812bd3c8bSSascha Wildner 	switch (USB_GET_STATE(xfer)) {
47912bd3c8bSSascha Wildner 	case USB_ST_TRANSFERRED:
48012bd3c8bSSascha Wildner 
48112bd3c8bSSascha Wildner 		DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
48212bd3c8bSSascha Wildner 
48312bd3c8bSSascha Wildner 		for (x = 0; x != aframes; x++) {
48412bd3c8bSSascha Wildner 
48512bd3c8bSSascha Wildner 			m = sc->sc_rx_buf[x];
48612bd3c8bSSascha Wildner 			sc->sc_rx_buf[x] = NULL;
48712bd3c8bSSascha Wildner 			len = usbd_xfer_frame_len(xfer, x);
48812bd3c8bSSascha Wildner 
48912bd3c8bSSascha Wildner 			if (len < (sizeof(struct ether_header) +
49012bd3c8bSSascha Wildner 			    IPHETH_RX_ADJ)) {
49112bd3c8bSSascha Wildner 				m_freem(m);
49212bd3c8bSSascha Wildner 				continue;
49312bd3c8bSSascha Wildner 			}
49412bd3c8bSSascha Wildner 
49512bd3c8bSSascha Wildner 			m_adj(m, IPHETH_RX_ADJ);
49612bd3c8bSSascha Wildner 
49712bd3c8bSSascha Wildner 			/* queue up mbuf */
49812bd3c8bSSascha Wildner 			uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ);
49912bd3c8bSSascha Wildner 		}
50012bd3c8bSSascha Wildner 
50112bd3c8bSSascha Wildner 		/* FALLTHROUGH */
50212bd3c8bSSascha Wildner 	case USB_ST_SETUP:
50312bd3c8bSSascha Wildner 
50412bd3c8bSSascha Wildner 		for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) {
50512bd3c8bSSascha Wildner 			if (sc->sc_rx_buf[x] == NULL) {
50612bd3c8bSSascha Wildner 				m = uether_newbuf();
50712bd3c8bSSascha Wildner 				if (m == NULL)
50812bd3c8bSSascha Wildner 					goto tr_stall;
50912bd3c8bSSascha Wildner 
51012bd3c8bSSascha Wildner 				/* cancel alignment for ethernet */
51112bd3c8bSSascha Wildner 				m_adj(m, ETHER_ALIGN);
51212bd3c8bSSascha Wildner 
51312bd3c8bSSascha Wildner 				sc->sc_rx_buf[x] = m;
51412bd3c8bSSascha Wildner 			} else {
51512bd3c8bSSascha Wildner 				m = sc->sc_rx_buf[x];
51612bd3c8bSSascha Wildner 			}
51712bd3c8bSSascha Wildner 
51812bd3c8bSSascha Wildner 			usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
51912bd3c8bSSascha Wildner 		}
52012bd3c8bSSascha Wildner 		/* set number of frames and start hardware */
52112bd3c8bSSascha Wildner 		usbd_xfer_set_frames(xfer, x);
52212bd3c8bSSascha Wildner 		usbd_transfer_submit(xfer);
52312bd3c8bSSascha Wildner 		/* flush any received frames */
52412bd3c8bSSascha Wildner 		uether_rxflush(&sc->sc_ue);
52512bd3c8bSSascha Wildner 		break;
52612bd3c8bSSascha Wildner 
52712bd3c8bSSascha Wildner 	default:			/* Error */
52812bd3c8bSSascha Wildner 		DPRINTF("error = %s\n", usbd_errstr(error));
52912bd3c8bSSascha Wildner 
53012bd3c8bSSascha Wildner 		if (error != USB_ERR_CANCELLED) {
53112bd3c8bSSascha Wildner 	tr_stall:
53212bd3c8bSSascha Wildner 			/* try to clear stall first */
53312bd3c8bSSascha Wildner 			usbd_xfer_set_stall(xfer);
53412bd3c8bSSascha Wildner 			usbd_xfer_set_frames(xfer, 0);
53512bd3c8bSSascha Wildner 			usbd_transfer_submit(xfer);
53612bd3c8bSSascha Wildner 			break;
53712bd3c8bSSascha Wildner 		}
53812bd3c8bSSascha Wildner 		/* need to free the RX-mbufs when we are cancelled */
53912bd3c8bSSascha Wildner 		ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX);
54012bd3c8bSSascha Wildner 		break;
54112bd3c8bSSascha Wildner 	}
54212bd3c8bSSascha Wildner }
543