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