if_ipheth.c revision 251109
1/*-
2 * Copyright (c) 2010 Hans Petter Selasky. All rights reserved.
3 * Copyright (c) 2009 Diego Giagio. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27/*
28 * Thanks to Diego Giagio for figuring out the programming details for
29 * the Apple iPhone Ethernet driver.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/sys/dev/usb/net/if_ipheth.c 251109 2013-05-29 20:36:51Z eadler $");
34
35#include <sys/stdint.h>
36#include <sys/stddef.h>
37#include <sys/param.h>
38#include <sys/queue.h>
39#include <sys/types.h>
40#include <sys/systm.h>
41#include <sys/kernel.h>
42#include <sys/bus.h>
43#include <sys/module.h>
44#include <sys/lock.h>
45#include <sys/mutex.h>
46#include <sys/condvar.h>
47#include <sys/sysctl.h>
48#include <sys/sx.h>
49#include <sys/unistd.h>
50#include <sys/callout.h>
51#include <sys/malloc.h>
52#include <sys/priv.h>
53
54#include <dev/usb/usb.h>
55#include <dev/usb/usbdi.h>
56#include <dev/usb/usbdi_util.h>
57#include "usbdevs.h"
58
59#define	USB_DEBUG_VAR ipheth_debug
60#include <dev/usb/usb_debug.h>
61#include <dev/usb/usb_process.h>
62
63#include <dev/usb/net/usb_ethernet.h>
64#include <dev/usb/net/if_iphethvar.h>
65
66static device_probe_t ipheth_probe;
67static device_attach_t ipheth_attach;
68static device_detach_t ipheth_detach;
69
70static usb_callback_t ipheth_bulk_write_callback;
71static usb_callback_t ipheth_bulk_read_callback;
72
73static uether_fn_t ipheth_attach_post;
74static uether_fn_t ipheth_tick;
75static uether_fn_t ipheth_init;
76static uether_fn_t ipheth_stop;
77static uether_fn_t ipheth_start;
78static uether_fn_t ipheth_setmulti;
79static uether_fn_t ipheth_setpromisc;
80
81#ifdef USB_DEBUG
82static int ipheth_debug = 0;
83
84static SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet");
85SYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RW, &ipheth_debug, 0, "Debug level");
86#endif
87
88static const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = {
89
90	[IPHETH_BULK_RX] = {
91		.type = UE_BULK,
92		.endpoint = UE_ADDR_ANY,
93		.direction = UE_DIR_RX,
94		.frames = IPHETH_RX_FRAMES_MAX,
95		.bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES),
96		.flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
97		.callback = ipheth_bulk_read_callback,
98		.timeout = 0,		/* no timeout */
99	},
100
101	[IPHETH_BULK_TX] = {
102		.type = UE_BULK,
103		.endpoint = UE_ADDR_ANY,
104		.direction = UE_DIR_TX,
105		.frames = IPHETH_TX_FRAMES_MAX,
106		.bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE),
107		.flags = {.force_short_xfer = 1,},
108		.callback = ipheth_bulk_write_callback,
109		.timeout = IPHETH_TX_TIMEOUT,
110	},
111};
112
113static device_method_t ipheth_methods[] = {
114	/* Device interface */
115	DEVMETHOD(device_probe, ipheth_probe),
116	DEVMETHOD(device_attach, ipheth_attach),
117	DEVMETHOD(device_detach, ipheth_detach),
118
119	DEVMETHOD_END
120};
121
122static driver_t ipheth_driver = {
123	.name = "ipheth",
124	.methods = ipheth_methods,
125	.size = sizeof(struct ipheth_softc),
126};
127
128static devclass_t ipheth_devclass;
129
130DRIVER_MODULE(ipheth, uhub, ipheth_driver, ipheth_devclass, NULL, 0);
131MODULE_VERSION(ipheth, 1);
132MODULE_DEPEND(ipheth, uether, 1, 1, 1);
133MODULE_DEPEND(ipheth, usb, 1, 1, 1);
134MODULE_DEPEND(ipheth, ether, 1, 1, 1);
135
136static const struct usb_ether_methods ipheth_ue_methods = {
137	.ue_attach_post = ipheth_attach_post,
138	.ue_start = ipheth_start,
139	.ue_init = ipheth_init,
140	.ue_tick = ipheth_tick,
141	.ue_stop = ipheth_stop,
142	.ue_setmulti = ipheth_setmulti,
143	.ue_setpromisc = ipheth_setpromisc,
144};
145
146#define	IPHETH_ID(v,p,c,sc,pt) \
147    USB_VENDOR(v), USB_PRODUCT(p), \
148    USB_IFACE_CLASS(c), USB_IFACE_SUBCLASS(sc), \
149    USB_IFACE_PROTOCOL(pt)
150
151static const STRUCT_USB_HOST_ID ipheth_devs[] = {
152	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE,
153	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
154	    IPHETH_USBINTF_PROTO)},
155	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G,
156	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
157	    IPHETH_USBINTF_PROTO)},
158	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3GS,
159	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
160	    IPHETH_USBINTF_PROTO)},
161	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4,
162	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
163	    IPHETH_USBINTF_PROTO)},
164	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_4S,
165	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
166	    IPHETH_USBINTF_PROTO)},
167	{IPHETH_ID(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_5,
168	    IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
169	    IPHETH_USBINTF_PROTO)},
170};
171
172static int
173ipheth_get_mac_addr(struct ipheth_softc *sc)
174{
175	struct usb_device_request req;
176	int error;
177
178	req.bmRequestType = UT_READ_VENDOR_DEVICE;
179	req.bRequest = IPHETH_CMD_GET_MACADDR;
180	req.wValue[0] = 0;
181	req.wValue[1] = 0;
182	req.wIndex[0] = sc->sc_iface_no;
183	req.wIndex[1] = 0;
184	req.wLength[0] = ETHER_ADDR_LEN;
185	req.wLength[1] = 0;
186
187	error = usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, sc->sc_data);
188
189	if (error)
190		return (error);
191
192	memcpy(sc->sc_ue.ue_eaddr, sc->sc_data, ETHER_ADDR_LEN);
193
194	return (0);
195}
196
197static int
198ipheth_probe(device_t dev)
199{
200	struct usb_attach_arg *uaa = device_get_ivars(dev);
201
202	if (uaa->usb_mode != USB_MODE_HOST)
203		return (ENXIO);
204
205	return (usbd_lookup_id_by_uaa(ipheth_devs, sizeof(ipheth_devs), uaa));
206}
207
208static int
209ipheth_attach(device_t dev)
210{
211	struct ipheth_softc *sc = device_get_softc(dev);
212	struct usb_ether *ue = &sc->sc_ue;
213	struct usb_attach_arg *uaa = device_get_ivars(dev);
214	int error;
215
216	sc->sc_iface_no = uaa->info.bIfaceIndex;
217
218	device_set_usb_desc(dev);
219
220	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
221
222	error = usbd_set_alt_interface_index(uaa->device,
223	    uaa->info.bIfaceIndex, IPHETH_ALT_INTFNUM);
224	if (error) {
225		device_printf(dev, "Cannot set alternate setting\n");
226		goto detach;
227	}
228	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no,
229	    sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx);
230	if (error) {
231		device_printf(dev, "Cannot setup USB transfers\n");
232		goto detach;
233	}
234	ue->ue_sc = sc;
235	ue->ue_dev = dev;
236	ue->ue_udev = uaa->device;
237	ue->ue_mtx = &sc->sc_mtx;
238	ue->ue_methods = &ipheth_ue_methods;
239
240	error = ipheth_get_mac_addr(sc);
241	if (error) {
242		device_printf(dev, "Cannot get MAC address\n");
243		goto detach;
244	}
245
246	error = uether_ifattach(ue);
247	if (error) {
248		device_printf(dev, "could not attach interface\n");
249		goto detach;
250	}
251	return (0);			/* success */
252
253detach:
254	ipheth_detach(dev);
255	return (ENXIO);			/* failure */
256}
257
258static int
259ipheth_detach(device_t dev)
260{
261	struct ipheth_softc *sc = device_get_softc(dev);
262	struct usb_ether *ue = &sc->sc_ue;
263
264	/* stop all USB transfers first */
265	usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER);
266
267	uether_ifdetach(ue);
268
269	mtx_destroy(&sc->sc_mtx);
270
271	return (0);
272}
273
274static void
275ipheth_start(struct usb_ether *ue)
276{
277	struct ipheth_softc *sc = uether_getsc(ue);
278
279	/*
280	 * Start the USB transfers, if not already started:
281	 */
282	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_TX]);
283	usbd_transfer_start(sc->sc_xfer[IPHETH_BULK_RX]);
284}
285
286static void
287ipheth_stop(struct usb_ether *ue)
288{
289	struct ipheth_softc *sc = uether_getsc(ue);
290
291	/*
292	 * Stop the USB transfers, if not already stopped:
293	 */
294	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_TX]);
295	usbd_transfer_stop(sc->sc_xfer[IPHETH_BULK_RX]);
296}
297
298static void
299ipheth_tick(struct usb_ether *ue)
300{
301	struct ipheth_softc *sc = uether_getsc(ue);
302	struct usb_device_request req;
303	int error;
304
305	req.bmRequestType = UT_READ_VENDOR_DEVICE;
306	req.bRequest = IPHETH_CMD_CARRIER_CHECK;
307	req.wValue[0] = 0;
308	req.wValue[1] = 0;
309	req.wIndex[0] = sc->sc_iface_no;
310	req.wIndex[1] = 0;
311	req.wLength[0] = IPHETH_CTRL_BUF_SIZE;
312	req.wLength[1] = 0;
313
314	error = uether_do_request(ue, &req, sc->sc_data, IPHETH_CTRL_TIMEOUT);
315
316	if (error)
317		return;
318
319	sc->sc_carrier_on =
320	    (sc->sc_data[0] == IPHETH_CARRIER_ON);
321}
322
323static void
324ipheth_attach_post(struct usb_ether *ue)
325{
326
327}
328
329static void
330ipheth_init(struct usb_ether *ue)
331{
332	struct ipheth_softc *sc = uether_getsc(ue);
333	struct ifnet *ifp = uether_getifp(ue);
334
335	IPHETH_LOCK_ASSERT(sc, MA_OWNED);
336
337	ifp->if_drv_flags |= IFF_DRV_RUNNING;
338
339	/* stall data write direction, which depends on USB mode */
340	usbd_xfer_set_stall(sc->sc_xfer[IPHETH_BULK_TX]);
341
342	/* start data transfers */
343	ipheth_start(ue);
344}
345
346static void
347ipheth_setmulti(struct usb_ether *ue)
348{
349
350}
351
352static void
353ipheth_setpromisc(struct usb_ether *ue)
354{
355
356}
357
358static void
359ipheth_free_queue(struct mbuf **ppm, uint8_t n)
360{
361	uint8_t x;
362
363	for (x = 0; x != n; x++) {
364		if (ppm[x] != NULL) {
365			m_freem(ppm[x]);
366			ppm[x] = NULL;
367		}
368	}
369}
370
371static void
372ipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
373{
374	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
375	struct ifnet *ifp = uether_getifp(&sc->sc_ue);
376	struct usb_page_cache *pc;
377	struct mbuf *m;
378	uint8_t x;
379	int actlen;
380	int aframes;
381
382	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
383
384	DPRINTFN(1, "\n");
385
386	switch (USB_GET_STATE(xfer)) {
387	case USB_ST_TRANSFERRED:
388		DPRINTFN(11, "transfer complete: %u bytes in %u frames\n",
389		    actlen, aframes);
390
391		ifp->if_opackets++;
392
393		/* free all previous TX buffers */
394		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
395
396		/* FALLTHROUGH */
397	case USB_ST_SETUP:
398tr_setup:
399		for (x = 0; x != IPHETH_TX_FRAMES_MAX; x++) {
400
401			IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
402
403			if (m == NULL)
404				break;
405
406			usbd_xfer_set_frame_offset(xfer,
407			    x * IPHETH_BUF_SIZE, x);
408
409			pc = usbd_xfer_get_frame(xfer, x);
410
411			sc->sc_tx_buf[x] = m;
412
413			if (m->m_pkthdr.len > IPHETH_BUF_SIZE)
414				m->m_pkthdr.len = IPHETH_BUF_SIZE;
415
416			usbd_m_copy_in(pc, 0, m, 0, m->m_pkthdr.len);
417
418			usbd_xfer_set_frame_len(xfer, x, IPHETH_BUF_SIZE);
419
420			if (IPHETH_BUF_SIZE != m->m_pkthdr.len) {
421				usbd_frame_zero(pc, m->m_pkthdr.len,
422					IPHETH_BUF_SIZE - m->m_pkthdr.len);
423			}
424
425			/*
426			 * If there's a BPF listener, bounce a copy of
427			 * this frame to him:
428			 */
429			BPF_MTAP(ifp, m);
430		}
431		if (x != 0) {
432			usbd_xfer_set_frames(xfer, x);
433
434			usbd_transfer_submit(xfer);
435		}
436		break;
437
438	default:			/* Error */
439		DPRINTFN(11, "transfer error, %s\n",
440		    usbd_errstr(error));
441
442		/* free all previous TX buffers */
443		ipheth_free_queue(sc->sc_tx_buf, IPHETH_TX_FRAMES_MAX);
444
445		/* count output errors */
446		ifp->if_oerrors++;
447
448		if (error != USB_ERR_CANCELLED) {
449			/* try to clear stall first */
450			usbd_xfer_set_stall(xfer);
451			goto tr_setup;
452		}
453		break;
454	}
455}
456
457static void
458ipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
459{
460	struct ipheth_softc *sc = usbd_xfer_softc(xfer);
461	struct mbuf *m;
462	uint8_t x;
463	int actlen;
464	int aframes;
465	int len;
466
467	usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL);
468
469	switch (USB_GET_STATE(xfer)) {
470	case USB_ST_TRANSFERRED:
471
472		DPRINTF("received %u bytes in %u frames\n", actlen, aframes);
473
474		for (x = 0; x != aframes; x++) {
475
476			m = sc->sc_rx_buf[x];
477			sc->sc_rx_buf[x] = NULL;
478			len = usbd_xfer_frame_len(xfer, x);
479
480			if (len < (int)(sizeof(struct ether_header) +
481			    IPHETH_RX_ADJ)) {
482				m_freem(m);
483				continue;
484			}
485
486			m_adj(m, IPHETH_RX_ADJ);
487
488			/* queue up mbuf */
489			uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ);
490		}
491
492		/* FALLTHROUGH */
493	case USB_ST_SETUP:
494
495		for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) {
496			if (sc->sc_rx_buf[x] == NULL) {
497				m = uether_newbuf();
498				if (m == NULL)
499					goto tr_stall;
500
501				/* cancel alignment for ethernet */
502				m_adj(m, ETHER_ALIGN);
503
504				sc->sc_rx_buf[x] = m;
505			} else {
506				m = sc->sc_rx_buf[x];
507			}
508
509			usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len);
510		}
511		/* set number of frames and start hardware */
512		usbd_xfer_set_frames(xfer, x);
513		usbd_transfer_submit(xfer);
514		/* flush any received frames */
515		uether_rxflush(&sc->sc_ue);
516		break;
517
518	default:			/* Error */
519		DPRINTF("error = %s\n", usbd_errstr(error));
520
521		if (error != USB_ERR_CANCELLED) {
522	tr_stall:
523			/* try to clear stall first */
524			usbd_xfer_set_stall(xfer);
525			usbd_xfer_set_frames(xfer, 0);
526			usbd_transfer_submit(xfer);
527			break;
528		}
529		/* need to free the RX-mbufs when we are cancelled */
530		ipheth_free_queue(sc->sc_rx_buf, IPHETH_RX_FRAMES_MAX);
531		break;
532	}
533}
534