/*=========================================================================== METHOD: GobiUSBNetStop (Public Method) DESCRIPTION: Wrapper to usbnet_stop, correctly handling autosuspend Stop AutoPM thread (if CONFIG_PM is defined) PARAMETERS pNet [ I ] - Pointer to net device RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ int GobiUSBNetStop( struct net_device * pNet ) { struct sGobiUSBNet * pGobiDev; struct usbnet * pDev = netdev_priv( pNet ); if (pDev == NULL || pDev->net == NULL) { DBG( "failed to get netdevice\n" ); return -ENXIO; } pGobiDev = (sGobiUSBNet *)pDev->data[0]; if (pGobiDev == NULL) { DBG( "failed to get QMIDevice\n" ); return -ENXIO; } // Stop traffic GobiSetDownReason( pGobiDev, NET_IFACE_STOPPED ); #ifdef CONFIG_PM // Tell traffic thread to exit pGobiDev->mAutoPM.mbExit = true; complete( &pGobiDev->mAutoPM.mThreadDoWork ); // Wait for it to exit while( pGobiDev->mAutoPM.mpThread != NULL ) { msleep( 100 ); } DBG( "thread stopped\n" ); #endif /* CONFIG_PM */ // Pass to usbnet_stop, if defined if (pGobiDev->mpUSBNetStop != NULL) { return pGobiDev->mpUSBNetStop( pNet ); } else { return 0; } }
/*=========================================================================== METHOD: GobiNetSuspend (Public Method) DESCRIPTION: Stops QMI traffic while device is suspended PARAMETERS pIntf [ I ] - Pointer to interface powerEvent [ I ] - Power management event RETURN VALUE: int - 0 for success negative errno for failure ===========================================================================*/ int GobiNetSuspend( struct usb_interface * pIntf, pm_message_t powerEvent ) { struct usbnet * pDev; sGobiUSBNet * pGobiDev; if (pIntf == 0) { return -ENOMEM; } #if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,23 )) pDev = usb_get_intfdata( pIntf ); #else pDev = (struct usbnet *)pIntf->dev.platform_data; #endif if (pDev == NULL || pDev->net == NULL) { DBG( "failed to get netdevice\n" ); return -ENXIO; } pGobiDev = (sGobiUSBNet *)pDev->data[0]; if (pGobiDev == NULL) { DBG( "failed to get QMIDevice\n" ); return -ENXIO; } // Is this autosuspend or system suspend? // do we allow remote wakeup? #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 )) if (pDev->udev->auto_pm == 0) #else if ((powerEvent.event & PM_EVENT_AUTO) == 0) #endif { DBG( "device suspended to power level %d\n", powerEvent.event ); GobiSetDownReason( pGobiDev, DRIVER_SUSPENDED ); } else { DBG( "device autosuspend\n" ); } if (powerEvent.event & PM_EVENT_SUSPEND) { // Stop QMI read callbacks KillRead( pGobiDev ); pDev->udev->reset_resume = 0; // Store power state to avoid duplicate resumes pIntf->dev.power.power_state.event = powerEvent.event; } else { // Other power modes cause QMI connection to be lost pDev->udev->reset_resume = 1; } // Run usbnet's suspend function return usbnet_suspend( pIntf, powerEvent ); }
/*=========================================================================== METHOD: GobiUSBNetProbe (Public Method) DESCRIPTION: Run usbnet_probe Setup QMI device PARAMETERS pIntf [ I ] - Pointer to interface pVIDPIDs [ I ] - Pointer to VID/PID table RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ int GobiUSBNetProbe( struct usb_interface * pIntf, const struct usb_device_id * pVIDPIDs ) { int is9x15 = 0; unsigned char ifacenum; int status; struct usbnet * pDev; sGobiUSBNet * pGobiDev; struct ethhdr *eth; #if 0 /* There exists a race condition in the firmware that sometimes results * in the absence of Ethernet framing of packets received from the device. * Therefore, a firmware work-around currently hard-codes the MAC address * to ensure valid Ethernet frames are sent to the host. We therefore * hard-code the network device MAC address to comply with the firmware */ const char default_addr[6] = {0x00, 0xa0, 0xc6, 0x00, 0x00, 0x00}; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,29 )) struct net_device_ops * pNetDevOps; #endif ifacenum = pIntf->cur_altsetting->desc.bInterfaceNumber; status = usbnet_probe( pIntf, pVIDPIDs ); if (status < 0) { DBG( "usbnet_probe failed %d\n", status ); return status; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,19 )) pIntf->needs_remote_wakeup = 1; #endif #if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,23 )) pDev = usb_get_intfdata( pIntf ); #else pDev = (struct usbnet *)pIntf->dev.platform_data; #endif if (pDev == NULL || pDev->net == NULL) { DBG( "failed to get netdevice\n" ); usbnet_disconnect( pIntf ); return -ENXIO; } pGobiDev = kmalloc( sizeof( sGobiUSBNet ), GFP_KERNEL ); if (pGobiDev == NULL) { DBG( "falied to allocate device buffers" ); usbnet_disconnect( pIntf ); return -ENOMEM; } pDev->data[0] = (unsigned long)pGobiDev; pGobiDev->mpNetDev = pDev; #ifdef DATA_MODE_RP pDev->net->flags |= IFF_NOARP; DBG( "RawIP mode\n" ); #endif // Clearing endpoint halt is a magic handshake that brings // the device out of low power (airplane) mode // NOTE: FCC verification should be done before this, if required usb_clear_halt( pGobiDev->mpNetDev->udev, pDev->out ); // Overload PM related network functions #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) pGobiDev->mpUSBNetOpen = pDev->net->open; pDev->net->open = GobiUSBNetOpen; pGobiDev->mpUSBNetStop = pDev->net->stop; pDev->net->stop = GobiUSBNetStop; pDev->net->hard_start_xmit = GobiUSBNetStartXmit; pDev->net->tx_timeout = GobiUSBNetTXTimeout; #else pNetDevOps = kmalloc( sizeof( struct net_device_ops ), GFP_KERNEL ); if (pNetDevOps == NULL) { DBG( "falied to allocate net device ops" ); usbnet_disconnect( pIntf ); return -ENOMEM; } memcpy( pNetDevOps, pDev->net->netdev_ops, sizeof( struct net_device_ops ) ); pGobiDev->mpUSBNetOpen = pNetDevOps->ndo_open; pNetDevOps->ndo_open = GobiUSBNetOpen; pGobiDev->mpUSBNetStop = pNetDevOps->ndo_stop; pNetDevOps->ndo_stop = GobiUSBNetStop; #ifdef CONFIG_PM pNetDevOps->ndo_start_xmit = GobiUSBNetStartXmit; pNetDevOps->ndo_tx_timeout = GobiUSBNetTXTimeout; #else pNetDevOps->ndo_start_xmit = usbnet_start_xmit; pNetDevOps->ndo_tx_timeout = usbnet_tx_timeout; #endif /* CONFIG_PM */ pDev->net->netdev_ops = pNetDevOps; #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,31 )) memset( &(pGobiDev->mpNetDev->stats), 0, sizeof( struct net_device_stats ) ); #else memset( &(pGobiDev->mpNetDev->net->stats), 0, sizeof( struct net_device_stats ) ); #endif pGobiDev->mpIntf = pIntf; memset( &(pGobiDev->mMEID), '0', 14 ); /* change MAC addr to include, ifacenum, and to be unique */ pGobiDev->mpNetDev->net->dev_addr[ETH_ALEN-2] = atomic_inc_return(&iface_counter); pGobiDev->mpNetDev->net->dev_addr[ETH_ALEN-1] = ifacenum; DBG( "Mac Address:\n" ); PrintHex( &pGobiDev->mpNetDev->net->dev_addr[0], 6 ); #if 0 /* interfers with multiple interface support and no longer appears to be necessary */ /* Hard-code the host MAC address to comply with the firmware workaround */ memcpy(&pGobiDev->mpNetDev->net->dev_addr[0], &default_addr[0], 6); DBG( "Default Mac Address:\n" ); PrintHex( &pGobiDev->mpNetDev->net->dev_addr[0], 6 ); #endif /* Create ethernet header for IPv4 packets */ eth = (struct ethhdr *)pGobiDev->eth_hdr_tmpl_ipv4; memcpy(ð->h_dest, &pGobiDev->mpNetDev->net->dev_addr[0], ETH_ALEN); memcpy(ð->h_source, &pGobiDev->mpNetDev->net->dev_addr[0], ETH_ALEN); eth->h_proto = cpu_to_be16(ETH_P_IP); /* Create ethernet header for IPv6 packets */ eth = (struct ethhdr *)pGobiDev->eth_hdr_tmpl_ipv6; memcpy(ð->h_dest, &pGobiDev->mpNetDev->net->dev_addr[0], ETH_ALEN); memcpy(ð->h_source, &pGobiDev->mpNetDev->net->dev_addr[0], ETH_ALEN); eth->h_proto = cpu_to_be16(ETH_P_IPV6); pGobiDev->mbQMIValid = false; memset( &pGobiDev->mQMIDev, 0, sizeof( sQMIDev ) ); pGobiDev->mQMIDev.mbCdevIsInitialized = false; pGobiDev->mQMIDev.mpDevClass = gpClass; #ifdef CONFIG_PM init_completion( &pGobiDev->mAutoPM.mThreadDoWork ); #endif /* CONFIG_PM */ spin_lock_init( &pGobiDev->mQMIDev.mClientMemLock ); // Default to device down pGobiDev->mDownReason = 0; GobiSetDownReason( pGobiDev, NO_NDIS_CONNECTION ); GobiSetDownReason( pGobiDev, NET_IFACE_STOPPED ); // Register QMI if (pDev->driver_info->data && test_bit(BIT_9X15, &pDev->driver_info->data)) { is9x15 = 1; } status = RegisterQMIDevice( pGobiDev, is9x15 ); if (status != 0) { // usbnet_disconnect() will call GobiNetDriverUnbind() which will call // DeregisterQMIDevice() to clean up any partially created QMI device usbnet_disconnect( pIntf ); return status; } // Success return 0; }
/*=========================================================================== METHOD: GobiUSBNetProbe (Public Method) DESCRIPTION: Run usbnet_probe Setup QMI device PARAMETERS pIntf [ I ] - Pointer to interface pVIDPIDs [ I ] - Pointer to VID/PID table RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ int GobiUSBNetProbe( struct usb_interface * pIntf, const struct usb_device_id * pVIDPIDs ) { int status; struct usbnet * pDev; sGobiUSBNet * pGobiDev; #if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,29 )) struct net_device_ops * pNetDevOps; #endif status = usbnet_probe( pIntf, pVIDPIDs ); if(status < 0 ) { DBG( "usbnet_probe failed %d\n", status ); return status; } #if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,23 )) pDev = usb_get_intfdata( pIntf ); #else pDev = (struct usbnet *)pIntf->dev.platform_data; #endif if (pDev == NULL || pDev->net == NULL) { DBG( "failed to get netdevice\n" ); usbnet_disconnect( pIntf ); return -ENXIO; } pGobiDev = kmalloc( sizeof( sGobiUSBNet ), GFP_KERNEL ); if (pGobiDev == NULL) { DBG( "falied to allocate device buffers" ); usbnet_disconnect( pIntf ); return -ENOMEM; } pDev->data[0] = (unsigned long)pGobiDev; pGobiDev->mpNetDev = pDev; // Overload PM related network functions #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) pGobiDev->mpUSBNetOpen = pDev->net->open; pDev->net->open = GobiUSBNetOpen; pGobiDev->mpUSBNetStop = pDev->net->stop; pDev->net->stop = GobiUSBNetStop; pDev->net->hard_start_xmit = GobiUSBNetStartXmit; pDev->net->tx_timeout = GobiUSBNetTXTimeout; #else pNetDevOps = kmalloc( sizeof( struct net_device_ops ), GFP_KERNEL ); if (pNetDevOps == NULL) { DBG( "falied to allocate net device ops" ); usbnet_disconnect( pIntf ); return -ENOMEM; } memcpy( pNetDevOps, pDev->net->netdev_ops, sizeof( struct net_device_ops ) ); pGobiDev->mpUSBNetOpen = pNetDevOps->ndo_open; pNetDevOps->ndo_open = GobiUSBNetOpen; pGobiDev->mpUSBNetStop = pNetDevOps->ndo_stop; pNetDevOps->ndo_stop = GobiUSBNetStop; pNetDevOps->ndo_start_xmit = GobiUSBNetStartXmit; pNetDevOps->ndo_tx_timeout = GobiUSBNetTXTimeout; pDev->net->netdev_ops = pNetDevOps; #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,31 )) memset( &(pGobiDev->mpNetDev->stats), 0, sizeof( struct net_device_stats ) ); #else memset( &(pGobiDev->mpNetDev->net->stats), 0, sizeof( struct net_device_stats ) ); #endif pGobiDev->mpIntf = pIntf; memset( &(pGobiDev->mMEID), '0', 14 ); DBG( "Mac Address:\n" ); PrintHex( &pGobiDev->mpNetDev->net->dev_addr[0], 6 ); pGobiDev->mbQMIValid = false; memset( &pGobiDev->mQMIDev, 0, sizeof( sQMIDev ) ); pGobiDev->mQMIDev.mbCdevIsInitialized = false; pGobiDev->mQMIDev.mpDevClass = gpClass; init_completion( &pGobiDev->mAutoPM.mThreadDoWork ); spin_lock_init( &pGobiDev->mQMIDev.mClientMemLock ); // Default to device down pGobiDev->mDownReason = 0; GobiSetDownReason( pGobiDev, NO_NDIS_CONNECTION ); GobiSetDownReason( pGobiDev, NET_IFACE_STOPPED ); // Register QMI status = RegisterQMIDevice( pGobiDev ); if (status != 0) { // usbnet_disconnect() will call GobiNetDriverUnbind() which will call // DeregisterQMIDevice() to clean up any partially created QMI device usbnet_disconnect( pIntf ); return status; } // Success return 0; }
/*=========================================================================== METHOD: GobiUSBNetProbe (Public Method) DESCRIPTION: Run usbnet_probe Setup QMI device PARAMETERS pIntf [ I ] - Pointer to interface pVIDPIDs [ I ] - Pointer to VID/PID table RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ int GobiUSBNetProbe( struct usb_interface * pIntf, const struct usb_device_id * pVIDPIDs ) { int status; struct usbnet * pDev; sGobiUSBNet * pGobiDev; sEndpoints * pEndpoints; int pipe; unsigned char my_mac_addr[ETH_ALEN]; struct sockaddr addr; #if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,29 )) struct net_device_ops * pNetDevOps; #endif pEndpoints = GatherEndpoints( pIntf ); if (pEndpoints == NULL) { return -ENODEV; } status = usbnet_probe( pIntf, pVIDPIDs ); if (status < 0) { DBG( "usbnet_probe failed %d\n", status ); return status; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,19 )) pIntf->needs_remote_wakeup = 1; #endif #if (LINUX_VERSION_CODE > KERNEL_VERSION( 2,6,23 )) pDev = usb_get_intfdata( pIntf ); #else pDev = (struct usbnet *)pIntf->dev.platform_data; #endif if (pDev == NULL || pDev->net == NULL) { DBG( "failed to get netdevice\n" ); usbnet_disconnect( pIntf ); kfree( pEndpoints ); return -ENXIO; } //modify Mac from param arthur 20140122 if(mac_addr != NULL){ str2mac(mac_addr,my_mac_addr); addr.sa_family = 1; memset(addr.sa_data,0,ETH_ALEN); memcpy(addr.sa_data, my_mac_addr, ETH_ALEN); #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) pDev->net->set_mac_address(pDev->net,&addr); #else pDev->net->netdev_ops->ndo_set_mac_address(pDev->net,&addr); #endif } //check the first 4 bits of MAC addr , for they can't be 4 or 6 arthur 20140122 if(((pDev->net->dev_addr[0] & 0xf0) == 0x40) || ((pDev->net->dev_addr[0] & 0xf0) == 0x60)) { printk("Need to Modify Mac\n" ); addr.sa_family = 1; memset(addr.sa_data,0,ETH_ALEN); memcpy(addr.sa_data, pDev->net->dev_addr, ETH_ALEN); addr.sa_data[0] |= 0x10; #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) pDev->net->set_mac_address(pDev->net,&addr); #else pDev->net->netdev_ops->ndo_set_mac_address(pDev->net,&addr); #endif } pGobiDev = kmalloc( sizeof( sGobiUSBNet ), GFP_KERNEL ); if (pGobiDev == NULL) { DBG( "falied to allocate device buffers" ); usbnet_disconnect( pIntf ); kfree( pEndpoints ); return -ENOMEM; } pDev->data[0] = (unsigned long)pGobiDev; pGobiDev->mpNetDev = pDev; pGobiDev->mpEndpoints = pEndpoints; // Clearing endpoint halt is a magic handshake that brings // the device out of low power (airplane) mode // NOTE: FCC verification should be done before this, if required pipe = usb_sndbulkpipe( pGobiDev->mpNetDev->udev, pGobiDev->mpEndpoints->mBlkOutEndp ); usb_clear_halt( pGobiDev->mpNetDev->udev, pipe ); // Overload PM related network functions #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 )) pGobiDev->mpUSBNetOpen = pDev->net->open; pDev->net->open = GobiUSBNetOpen; pGobiDev->mpUSBNetStop = pDev->net->stop; pDev->net->stop = GobiUSBNetStop; // pDev->net->hard_start_xmit = GobiUSBNetStartXmit; // pDev->net->tx_timeout = GobiUSBNetTXTimeout; #else pNetDevOps = kmalloc( sizeof( struct net_device_ops ), GFP_KERNEL ); if (pNetDevOps == NULL) { DBG( "falied to allocate net device ops" ); usbnet_disconnect( pIntf ); return -ENOMEM; } memcpy( pNetDevOps, pDev->net->netdev_ops, sizeof( struct net_device_ops ) ); pGobiDev->mpUSBNetOpen = pNetDevOps->ndo_open; pNetDevOps->ndo_open = GobiUSBNetOpen; pGobiDev->mpUSBNetStop = pNetDevOps->ndo_stop; pNetDevOps->ndo_stop = GobiUSBNetStop; // pNetDevOps->ndo_start_xmit = GobiUSBNetStartXmit; // pNetDevOps->ndo_tx_timeout = GobiUSBNetTXTimeout; pDev->net->netdev_ops = pNetDevOps; #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,31 )) memset( &(pGobiDev->mpNetDev->stats), 0, sizeof( struct net_device_stats ) ); #else memset( &(pGobiDev->mpNetDev->net->stats), 0, sizeof( struct net_device_stats ) ); #endif pGobiDev->mpIntf = pIntf; memset( &(pGobiDev->mMEID), '0', 14 ); DBG( "Mac Address:\n" ); PrintHex( &pGobiDev->mpNetDev->net->dev_addr[0], 6 ); pGobiDev->mbQMIValid = false; memset( &pGobiDev->mQMIDev, 0, sizeof( sQMIDev ) ); pGobiDev->mQMIDev.mbCdevIsInitialized = false; pGobiDev->mQMIDev.mpDevClass = gpClass; init_completion( &pGobiDev->mAutoPM.mThreadDoWork ); spin_lock_init( &pGobiDev->mQMIDev.mClientMemLock ); // Default to device down pGobiDev->mDownReason = 0; GobiSetDownReason( pGobiDev, NO_NDIS_CONNECTION ); GobiSetDownReason( pGobiDev, NET_IFACE_STOPPED ); // Register QMI status = RegisterQMIDevice( pGobiDev ); if (status != 0) { // usbnet_disconnect() will call GobiNetDriverUnbind() which will call // DeregisterQMIDevice() to clean up any partially created QMI device usbnet_disconnect( pIntf ); return status; } // Success return 0; }