#!/usr/bin/env python3
# Copyright (c) 2012 Arista Networks, Inc.  All rights reserved.
# Arista Networks, Inc. Confidential and Proprietary.
#
# fabric networking driver utility

import six
from sys import argv as argv_
from fcntl import ioctl
import ctypes, errno
import cpufabric
import os
import os.path
from linux_ioc import _IOWR

bufsize = 4096

platformToFabricNodes = {
   'sand' : ( '/dev/fabric', '/proc/sand-dma' ),
   'strata' : ( '/dev/fabric', '/proc/strata-dma' ),
   'xp' : ( '/dev/fabric', '/proc/xpdma' ),
   'bfn' : ( '/dev/fabric', '/proc/bfdma' ),
   'mako' : ( '/dev/mako_fabric', '/proc/makodma' )
}

def usage():
   print( 'usage: fab [ platform ] cmd [ values .. ]' )
   exit( -1 )

def dump( path, usePlugin=True ):
   if fabPlugin.isIoctlHandlerRegistered():
      fabPlugin.ioctl( "fabdump" )
   else:
      print( open( path ).read() )

def dumpAll():
   ret = -1

   if fabPlugin.isIoctlHandlerRegistered():
      fabPlugin.ioctl( "fabdump" )
      ret = 0
   else:
      for path in [ proc[ 1 ] for proc in sorted( platformToFabricNodes.values() ) ]:
         try:
            dump( path, usePlugin=False )
            ret = 0
         except IOError:
            pass

   return ret

def runCmd( argv, fabricDevice, cmdIndex ):
   # incrementally convert drivers to standard linux ioctl cmd encoding
   stdioc = os.path.exists( '/proc/xpdma' )
   cmd = _IOWR( 0, 0, bufsize ) if stdioc else bufsize

   with open( fabricDevice ) as f:
      buf = ctypes.create_string_buffer(
            six.ensure_binary( ' '.join( argv[ cmdIndex: ] ) ), bufsize )
      rc = -1
      try:
         rc = ioctl( f.fileno(), cmd, buf )
      except IOError as e:
         if e.errno in errno.errorcode:
            strerror = "{} {}".format( errno.errorcode[ e.errno ],
                                       os.strerror( e.errno ) )
         else:
            strerror = "unknown errno {}".format( e.errno )

         print( argv[ cmdIndex ], ':', strerror )
         exit( -1 )

      if rc:
         if argv[ cmdIndex ] == 'devstats':
            stats = ctypes.cast(
               buf, ctypes.POINTER( cpufabric.FabDevStats ) ).contents
            stats.__print__()
         else:
            print( six.ensure_str( buf.value ) )

if ( len( argv_ ) < 2 ) or ( argv_[ 1 ] == '-h' ) or ( argv_[ 1 ] == '--help' ):
   usage()


fabPlugin = cpufabric.FabPluginContext()

if argv_[ 1 ] == 'dump':
   exit( dumpAll() )
elif argv_[ 1 ] in platformToFabricNodes:
   platform = argv_[ 1 ]
   cmdIndex_ = 2
   if argv_[ cmdIndex_ ] == 'dump':
      # platform specific dump
      dump( platformToFabricNodes[ platform ][ 1 ] )
   else:
      fabricDevice_ = platformToFabricNodes[ platform ][ 0 ]
      runCmd( argv_, fabricDevice_, cmdIndex_ )
else:
   # This legacy shall continue despite no longer ideal as some 7130
   # platforms could have sand-dma and makodma coexisting.
   fabricDevice_ = '/dev/fabric'
   cmdIndex_ = 1
   runCmd( argv_, fabricDevice_, cmdIndex_ )
