aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/usb/net/if_cdce.c62
-rw-r--r--sys/dev/usb/net/if_cdcereg.h4
2 files changed, 60 insertions, 6 deletions
diff --git a/sys/dev/usb/net/if_cdce.c b/sys/dev/usb/net/if_cdce.c
index e42305ae801a..76134baa10a8 100644
--- a/sys/dev/usb/net/if_cdce.c
+++ b/sys/dev/usb/net/if_cdce.c
@@ -898,8 +898,14 @@ cdce_init(struct usb_ether *ue)
usbd_transfer_start(sc->sc_xfer[CDCE_INTR_RX]);
usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]);
- /* stall data write direction, which depends on USB mode */
- usbd_xfer_set_stall(sc->sc_xfer[CDCE_BULK_TX]);
+ /*
+ * Stall data write direction, which depends on USB mode.
+ *
+ * Some USB host stacks (e.g. Mac OS X) don't clears stall
+ * bit as it should, so set it in our host mode only.
+ */
+ if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST)
+ usbd_xfer_set_stall(sc->sc_xfer[CDCE_BULK_TX]);
/* start data transfers */
cdce_start(ue);
@@ -1065,6 +1071,10 @@ tr_setup:
static void
cdce_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
+ struct cdce_softc *sc = usbd_xfer_softc(xfer);
+ struct usb_cdc_notification req;
+ struct usb_page_cache *pc;
+ uint32_t speed;
int actlen;
usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
@@ -1077,10 +1087,50 @@ cdce_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
/* FALLTHROUGH */
case USB_ST_SETUP:
tr_setup:
-#if 0
- usbd_xfer_set_frame_len(xfer, 0, XXX);
- usbd_transfer_submit(xfer);
-#endif
+ /*
+ * Inform host about connection. Required according to USB CDC
+ * specification and communicating to Mac OS X USB host stack.
+ * Some of the values seems ignored by Mac OS X though.
+ */
+ if (sc->sc_notify_state == CDCE_NOTIFY_NETWORK_CONNECTION) {
+ req.bmRequestType = UCDC_NOTIFICATION;
+ req.bNotification = UCDC_N_NETWORK_CONNECTION;
+ req.wIndex[0] = sc->sc_ifaces_index[1];
+ req.wIndex[1] = 0;
+ USETW(req.wValue, 1); /* Connected */
+ USETW(req.wLength, 0);
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &req, sizeof(req));
+ usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+ usbd_xfer_set_frames(xfer, 1);
+ usbd_transfer_submit(xfer);
+ sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE;
+
+ } else if (sc->sc_notify_state == CDCE_NOTIFY_SPEED_CHANGE) {
+ req.bmRequestType = UCDC_NOTIFICATION;
+ req.bNotification = UCDC_N_CONNECTION_SPEED_CHANGE;
+ req.wIndex[0] = sc->sc_ifaces_index[1];
+ req.wIndex[1] = 0;
+ USETW(req.wValue, 0);
+ USETW(req.wLength, 8);
+
+ /* Peak theoretical bulk trasfer rate in bits/s */
+ if (usbd_get_speed(sc->sc_ue.ue_udev) == USB_SPEED_HIGH)
+ speed = (13 * 512 * 8 * 1000 * 8);
+ else
+ speed = (19 * 64 * 1 * 1000 * 8);
+
+ USETDW(req.data + 0, speed); /* Upstream bit rate */
+ USETDW(req.data + 4, speed); /* Downstream bit rate */
+
+ pc = usbd_xfer_get_frame(xfer, 0);
+ usbd_copy_in(pc, 0, &req, sizeof(req));
+ usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+ usbd_xfer_set_frames(xfer, 1);
+ usbd_transfer_submit(xfer);
+ sc->sc_notify_state = CDCE_NOTIFY_DONE;
+ }
break;
default: /* Error */
diff --git a/sys/dev/usb/net/if_cdcereg.h b/sys/dev/usb/net/if_cdcereg.h
index c470b368e066..1bc2d6039dc1 100644
--- a/sys/dev/usb/net/if_cdcereg.h
+++ b/sys/dev/usb/net/if_cdcereg.h
@@ -93,6 +93,10 @@ struct cdce_softc {
uint8_t sc_eaddr_str_index;
uint8_t sc_ifaces_index[2];
+ uint8_t sc_notify_state;
+#define CDCE_NOTIFY_NETWORK_CONNECTION 0
+#define CDCE_NOTIFY_SPEED_CHANGE 1
+#define CDCE_NOTIFY_DONE 2
};
#define CDCE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)