From e4ae66608bbf8b7be9162e5933a98905dcf52d6b Mon Sep 17 00:00:00 2001 From: Vitaly Kuzmichev Date: Fri, 11 Feb 2011 18:18:35 +0300 Subject: [PATCH] USB-RNDIS: Send RNDIS state on disconnecting Add waiting for receiving Ethernet gadget state on the Windows host side before dropping pullup, but keep it for debug. Signed-off-by: Vitaly Kuzmichev --- drivers/usb/gadget/ether.c | 23 +++++++++++++++++++++++ drivers/usb/gadget/rndis.c | 5 +++++ drivers/usb/gadget/rndis.h | 8 ++++++++ 3 files changed, 36 insertions(+) diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index f909267f65..9fb0e80ad1 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -1921,10 +1921,22 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) static int eth_stop(struct eth_dev *dev) { +#ifdef RNDIS_COMPLETE_SIGNAL_DISCONNECT + unsigned long ts; + unsigned long timeout = CONFIG_SYS_HZ; /* 1 sec to stop RNDIS */ +#endif + if (rndis_active(dev)) { rndis_set_param_medium(dev->rndis_config, NDIS_MEDIUM_802_3, 0); rndis_signal_disconnect(dev->rndis_config); +#ifdef RNDIS_COMPLETE_SIGNAL_DISCONNECT + /* Wait until host receives OID_GEN_MEDIA_CONNECT_STATUS */ + ts = get_timer(0); + while (get_timer(ts) < timeout) + usb_gadget_handle_interrupts(); +#endif + rndis_uninit(dev->rndis_config); dev->rndis = 0; } @@ -2486,6 +2498,17 @@ void usb_eth_halt(struct eth_device *netdev) if (!dev->gadget) return; + /* + * Some USB controllers may need additional deinitialization here + * before dropping pull-up (also due to hardware issues). + * For example: unhandled interrupt with status stage started may + * bring the controller to fully broken state (until board reset). + * There are some variants to debug and fix such cases: + * 1) In the case of RNDIS connection eth_stop can perform additional + * interrupt handling. See RNDIS_COMPLETE_SIGNAL_DISCONNECT definition. + * 2) 'pullup' callback in your UDC driver can be improved to perform + * this deinitialization. + */ eth_stop(dev); usb_gadget_disconnect(dev->gadget); diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 3e3f740ea5..886c0936e6 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -995,7 +995,12 @@ int rndis_signal_disconnect(int configNr) rndis_per_dev_params[configNr].media_state = NDIS_MEDIA_STATE_DISCONNECTED; +#ifdef RNDIS_COMPLETE_SIGNAL_DISCONNECT + return rndis_indicate_status_msg(configNr, + RNDIS_STATUS_MEDIA_DISCONNECT); +#else return 0; +#endif } void rndis_uninit(int configNr) diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 73a1204a03..d9e3a75287 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -17,6 +17,14 @@ #include "ndis.h" +/* + * By default rndis_signal_disconnect does not send status message about + * RNDIS disconnection to USB host (indicated as cable disconnected). + * Define RNDIS_COMPLETE_SIGNAL_DISCONNECT to send it. + * However, this will cause 1 sec delay on Ethernet device halt. + * Usually you do not need to define it. Mostly usable for debugging. + */ + #define RNDIS_MAXIMUM_FRAME_SIZE 1518 #define RNDIS_MAX_TOTAL_SIZE 1558 -- 2.25.1