Beispiel #1
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 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(&eth->h_dest, &pGobiDev->mpNetDev->net->dev_addr[0], ETH_ALEN);
   memcpy(&eth->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(&eth->h_dest, &pGobiDev->mpNetDev->net->dev_addr[0], ETH_ALEN);
   memcpy(&eth->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;
}
Beispiel #2
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;
}
Beispiel #3
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;
}
Beispiel #4
0
/*===========================================================================
METHOD:
   QCUSBNetProbe (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 QCUSBNetProbe( 
   struct usb_interface *        pIntf, 
   const struct usb_device_id *  pVIDPIDs )
{
   int status;
   struct usbnet * pDev;
   sQCUSBNet * pQCDev;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION( 2,6,29 ))
   struct net_device_ops * pNetDevOps;
#endif
   int i;
   u8 *addr;

   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;
   }

   pQCDev = kmalloc( sizeof( sQCUSBNet ), GFP_KERNEL );
   if (pQCDev == NULL)
   {
      DBG( "falied to allocate device buffers" );
      usbnet_disconnect( pIntf );
      return -ENOMEM;
   }
   
   pDev->data[0] = (unsigned long)pQCDev;
   
   pQCDev->mpNetDev = pDev;

   // Overload PM related network functions
#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,29 ))
   pQCDev->mpUSBNetOpen = pDev->net->open;
   pDev->net->open = QCUSBNetOpen;
   pQCDev->mpUSBNetStop = pDev->net->stop;
   pDev->net->stop = QCUSBNetStop;
   pDev->net->hard_start_xmit = QCUSBNetStartXmit;
   pDev->net->tx_timeout = QCUSBNetTXTimeout;
#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 ) );
   
   pQCDev->mpUSBNetOpen = pNetDevOps->ndo_open;
   pNetDevOps->ndo_open = QCUSBNetOpen;
   pQCDev->mpUSBNetStop = pNetDevOps->ndo_stop;
   pNetDevOps->ndo_stop = QCUSBNetStop;
   pNetDevOps->ndo_start_xmit = QCUSBNetStartXmit;
   pNetDevOps->ndo_tx_timeout = QCUSBNetTXTimeout;

   pDev->net->netdev_ops = pNetDevOps;
#endif

#if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,31 ))
   memset( &(pQCDev->mpNetDev->stats), 0, sizeof( struct net_device_stats ) );
#else
   memset( &(pQCDev->mpNetDev->net->stats), 0, sizeof( struct net_device_stats ) );
#endif

   pQCDev->mpIntf = pIntf;
   memset( &(pQCDev->mMEID), '0', 14 );

   pQCDev->mbQMIValid = false;
   memset( &pQCDev->mQMIDev, 0, sizeof( sQMIDev ) );
   pQCDev->mQMIDev.mbCdevIsInitialized = false;

   pQCDev->mQMIDev.mpDevClass = gpClass;
   
   init_completion( &pQCDev->mAutoPM.mThreadDoWork );
   spin_lock_init( &pQCDev->mQMIDev.mClientMemLock );

   // Default to device down
   pQCDev->mDownReason = 0;
   QSetDownReason( pQCDev, NO_NDIS_CONNECTION );
   QSetDownReason( pQCDev, NET_IFACE_STOPPED );

   // Register QMI
   status = RegisterQMIDevice( pQCDev );
   if (status != 0)
   {
      // usbnet_disconnect() will call QCUSBNetUnbind() which will call
      // DeregisterQMIDevice() to clean up any partially created QMI device
      usbnet_disconnect( pIntf );
      return status;
   }

   // After calling RegisterQMIDevice mMEID is valid.  Although most
   // drivers set the MAC address in bind(), we need to wait until the
   // MEID is available.
   addr = &pDev->net->dev_addr[0];
   for (i = 0; i < 6; i++)
     addr[i] = (nibble(pQCDev->mMEID[i*2+2]) << 4) +
         nibble(pQCDev->mMEID[i*2+3]);
   addr [0] &= 0xfe;       /* clear multicast bit */
   addr [0] |= 0x02;       /* set local assignment bit (IEEE802) */

   DBG( "Mac Address:\n" );
   PrintHex( &pQCDev->mpNetDev->net->dev_addr[0], 6 );

   // Success
   return 0;
}