
//
// Name:	fpxCtrsAndRegs.c
// Purpose:	Simple plugin to demonstrate access to fpx counters and registers
// History:
// o Apr 3, 2006:	Ken Wong, changes to conform to standard format
// o June 20, 2005:	John DeHart, creation
//

// Notation:
//	- this:  struct rp_instance * (ptr to plugin instance)
// Post-Conditions:
//   fpxCtrsAndRegs_load
//	- Plugin class (fpxCtrsAndRegs_class) structure is initialized
//	- fpxCtrsAndRegs_get_class() is registered with PCU
//   fpxCtrsAndRegs_create_instance
//	- Plugin instance memory is allocated and initialized
//	  (base class and its extension):
//	  this->rpclass = &fpxCtrsAndRegs_class
//	  ... other members of base class ...
//   fpxCtrsAndRegs_bind_instance
//   fpxCtrsAndRegs_handle_packet
//   fpxCtrsAndRegs_unbind_instance
//   fpxCtrsAndRegs_free_instance
//	- Plugin instance memory is free
//   fpxCtrsAndRegs_unload
//	- All instances are free
//	- Class is not registered with PCU
//

#include "./stdinc.h"

#include "fpxCtrsAndRegs.h"

MOD_MISC("fpxCtrsAndRegs")

// Static plugin class structure.
// Each kernel module has one class structure that is initialized by
// the plugin and a pointer to it is returned. The class structure can then
// be used to create plugin instances. Note that this is a structure and 
// not a pointer.
static struct rp_class fpxCtrsAndRegs_class;

void
fpxCtrsAndRegs_init_class() {		// initialize plugin class
//
// Initialize class struct:
//   classid - user defined
//   itype   - RP_INTERFACE_TYPE_PKT - for read-only plugins. The handle packet
//             signature is <handle_packet(struct rp_instance *this, void *pkt)>
//             RP_INTERFACE_TYPE_HLIST for plugins that will modify pkts or want
//             to source or sink data. The signature is
//              <handle_packet(struct rp_instance *this, void *plist)>
//   create_instance = function pointer to your create instance method.
//
  fpxCtrsAndRegs_class.classid = fpxCtrsAndRegs_ID;
//  fpxCtrsAndRegs_class.itype   = RP_INTERFACE_TYPE_PKT;
  fpxCtrsAndRegs_class.itype   = RP_INTERFACE_TYPE_HLIST;
  fpxCtrsAndRegs_class.create_instance = fpxCtrsAndRegs_create_instance;
  return;
}

struct rp_class *
fpxCtrsAndRegs_get_class() {	// return class structure
  return &fpxCtrsAndRegs_class;
}

// --| create and initialize an instance of fpxCtrsAndRegs |--
struct rp_instance *
fpxCtrsAndRegs_create_instance(struct rp_class *myclass, u_int32_t instanceid) {
  struct fpxCtrsAndRegs_instance *myinst; 

  // allocate memory for local instance struct
  MSR_PLUGIN_MALLOC(myinst,struct fpxCtrsAndRegs_instance *, 
                    sizeof(struct fpxCtrsAndRegs_instance),
                    M_MSR, 
                    M_WAITOK);
  if (myinst == NULL)	return NULL;

  // fill in instance pointers to local methods
  myinst->rootinstance.rpclass         = &fpxCtrsAndRegs_class;
  myinst->rootinstance.handle_packet   = fpxCtrsAndRegs_handle_packet;
  myinst->rootinstance.free_instance   = fpxCtrsAndRegs_free_instance;
  myinst->rootinstance.bind_instance   = fpxCtrsAndRegs_bind_instance;
  myinst->rootinstance.unbind_instance = fpxCtrsAndRegs_unbind_instance;
  myinst->rootinstance.handle_msg      = fpxCtrsAndRegs_handle_msg;

  myinst->rootinstance.instanceid = instanceid;
  myinst->pkt_count = 0;	// initialization code

  return (struct rp_instance *)myinst;
}

void
fpxCtrsAndRegs_handle_packet(
	struct rp_instance *this,
	void *pkt
) {
  struct fpxCtrsAndRegs_instance *inst = (struct fpxCtrsAndRegs_instance *)this;

  inst->pkt_count++;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
  	  "fpxCtrsAndRegs_handle_packet: Instance ID = %d got pkt %d\n",
          this->instanceid, inst->pkt_count));
}

void
fpxCtrsAndRegs_free_instance(struct rp_instance *this) {
  if (this) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
          "fpxCtrsAndRegs_free_instance: Freeing instance id %d ((class id %d)\n",
          this->instanceid, this->rpclass->classid));
    MSR_PLUGIN_FREE(this, M_MSR);
  } else {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_WARNING,
          "fpxCtrsAndRegs_free_instance: Passing a NULL this pointer\n"));
  }
}

void
fpxCtrsAndRegs_bind_instance(struct rp_instance *this) {
  struct fpxCtrsAndRegs_instance *inst = (struct fpxCtrsAndRegs_instance *)this;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
	"fpxCtrsAndRegs_bind_instance: Binding instance id %d (class id %d, type %d) to filter id %d\n",
        this->instanceid, this->rpclass->classid,
	this->rpclass->itype, this->bound_fid));
  inst->pkt_count = 0;
}

void
fpxCtrsAndRegs_unbind_instance(struct rp_instance *this) {
  struct fpxCtrsAndRegs_instance *inst = (struct fpxCtrsAndRegs_instance *)this;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
	"fpxCtrsAndRegs_unbind_instance: Unbinding instance id %d (class id %d, Type %d) to filter id %d\n",
        this->instanceid, this->rpclass->classid,
	this->rpclass->itype, this->bound_fid));
  inst->pkt_count = 0;
}

// External kernel module entry point.
//
// This function must match the name of the .o file.
// It is called each time the module is loaded or unloaded.
// The stat information is not needed here, so we will 
// leave it lkm_nofunc().
//

#ifdef PLUGIN_DEBUG
int
fpxCtrsAndRegs  (struct lkm_table *lkmtp, int cmd, int ver) {
  struct kernel_plugin_fct_struct *fctPtr;
  extern struct kernel_plugin_fct_struct  MSR_kernel_plugin_fcts;
  fctPtr = &MSR_kernel_plugin_fcts;
#else
int
fpxCtrsAndRegs  (struct lkm_table *lkmtp, int cmd, int ver,
				struct kernel_plugin_fct_struct *fctPtr) {
#endif
  // Do NOT put anything before the kernel_plugin_fcts is set, especially if
  // it uses any of the MSR_ macros!!!
  if (kernel_plugin_fcts == NULL) {
    kernel_plugin_fcts = fctPtr;
    kernel_plugin_variables = fctPtr->pluginVariables;
  }

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
        "fpxCtrsAndRegs: Entry function -- initialized plugin_fcts pointers\n"));

  MSR_PLUGIN_DISPATCH(lkmtp, cmd, ver, fpxCtrsAndRegs_load, fpxCtrsAndRegs_unload,
							PLUGIN_LKM_NOFUNC_FCT);
}

// This function is called each time the module is loaded
int
fpxCtrsAndRegs_load(struct lkm_table *lkmtp, int cmd) {
  int err;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
        "fpxCtrsAndRegs_load: Loading fpxCtrsAndRegs ...\n"));

  if (PLUGIN_LKM_EXISTS_FCT(lkmtp)) {	// avoid loading twice
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
      "fpxCtrsAndRegs_load: plugin already exists! Returing EEXIST\n"));
    return (EEXIST);
  }
  fpxCtrsAndRegs_init_class();

  err = PLUGIN_PCU_REGISTER_CLASS_FCT(fpxCtrsAndRegs_get_class());
  if (err != RP_OK) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
	"fpxCtrsAndRegs_load: Error (err = %d) encountered registering class.\n",
	err));
    return -1;
  }

  return 0;
}

// This function is called each time the module is unloaded.
// Remove all existing instances and then remove class.
int
fpxCtrsAndRegs_unload(struct lkm_table *lkmtp, int cmd) {
  struct rp_class *rpclass;
  u_int32_t cid;

  rpclass = fpxCtrsAndRegs_get_class();
  cid = rpclass->classid;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
        "fpxCtrsAndRegs_unload: my rpclass id = %d, Struct Address = 0x%08x\n",
        cid, (u_int32_t)rpclass));

  if (PLUGIN_PCU_FREE_ALL_INSTANCES_FCT(rpclass) != RP_OK) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
          "fpxCtrsAndRegs_unload: Error freeing all instances for class %d\n", cid));
    return -1;
  }

  if (PLUGIN_PCU_DEREGISTER_CLASS_FCT(rpclass) != RP_OK) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
          "fpxCtrsAndRegs_unload: Error deregistering class %d\n", cid));
  }

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
        "fpxCtrsAndRegs_unload: Unloaded class %d\n", cid));

  return 0;
}

#define READ_REG 1
#define READ_CTR 2
#define READ_QID 3
#define READ_VOQ 4
//
// Request msg (4 32-bit words):  0) instance number, 1) data (not used),
//		2) result (not used)
// Reply msg (1 word):  0) pkt count
//
int
fpxCtrsAndRegs_handle_msg(
	struct rp_instance *this,
	void *buf,
	u_int8_t flags,
	u_int8_t seq,
	u_int8_t *len
) {
  struct fpxCtrsAndRegs_instance *inst = (struct fpxCtrsAndRegs_instance *)this;
  u_int32_t *vals = (u_int32_t *)buf;
  u_int32_t id	  = (u_int32_t)ntohl(*vals);
  u_int32_t cmd   = (u_int32_t)ntohl(*(vals + 1));	// Command(1: register, 2: counter)
  u_int32_t data1 = (u_int32_t)ntohl(*(vals + 2));	// which register or counter to read
  u_int32_t result, timestamp;
  u_int32_t voqResults[8], totalEgressQLength;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
    "fpxCtrsAndRegs_handle_msg: Flags 0x%x, Len %d, Seq %d, Instance %ud\n",
			flags, *len, seq, this->instanceid));
  switch (cmd) {
    case READ_REG:
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
    		"fpxCtrsAndRegs_handle_msg: Instance ID = %d -- Reading Register %d \n",
		id, data1));

      if (PLUGIN_MSR_NSP_FPX_READ_REGISTER(data1, &result, &timestamp) == 0) {
  	MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
    		"fpxCtrsAndRegs_handle_msg: Instance ID = %d -- Result of Reading Register %d = %d (0x%x)\n",
		id, data1, result, result));
      }
      break;
    case READ_CTR:
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
    		"fpxCtrsAndRegs_handle_msg: Instance ID = %d -- Reading Counter %d \n",
		id, data1));

      if (PLUGIN_MSR_NSP_FPX_READ_COUNTER(data1, &result, &timestamp) == 0) {
  	MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
    		"fpxCtrsAndRegs_handle_msg: Instance ID = %d -- Result of Reading Counter %d = %d (0x%x)\n",
				id, data1, result, result));
      }
      break;
    case READ_QID:
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
    		"fpxCtrsAndRegs_handle_msg: Instance ID = %d -- Reading QID %d \n",
				id, data1));

      if (PLUGIN_MSR_NSP_FPX_READ_QUEUE_LENGTH(data1, &result, &timestamp) == 0) {
  		MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
    		"fpxCtrsAndRegs_handle_msg: Instance ID = %d -- Result of Reading QID %d = %d (0x%x)\n",
				id, data1, result, result));
      }
      break;
    case READ_VOQ:
      MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
    		"fpxCtrsAndRegs_handle_msg: Instance ID = %d -- Reading VOQ %d \n",
				id));

      if (PLUGIN_MSR_NSP_FPX_READ_VOQ_LENGTH(voqResults, &totalEgressQLength, &timestamp) == 0) {
  		MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
    		"fpxCtrsAndRegs_handle_msg: Instance ID = %d -- Result of Reading VOQs %d %d,%d,%d,%d,%d,%d,%d,%d %d\n",
		id, voqResults[0], voqResults[1], voqResults[2], voqResults[3],
		voqResults[4], voqResults[5], voqResults[6], voqResults[7], totalEgressQLength));
      }
      *vals++ = (u_int32_t)voqResults[0];	// send result back
      *vals++ = (u_int32_t)voqResults[1];	// send result back
      *vals++ = (u_int32_t)voqResults[2];	// send result back
      *vals++ = (u_int32_t)voqResults[3];	// send result back
      *vals++ = (u_int32_t)voqResults[4];	// send result back
      *vals++ = (u_int32_t)voqResults[5];	// send result back
      *vals++ = (u_int32_t)voqResults[6];	// send result back
      *vals++ = (u_int32_t)voqResults[7];	// send result back
      *vals   = (u_int32_t)totalEgressQLength;// send result back
      *len    = 9*sizeof(u_int32_t);		// #bytes in buf
      return 0;
    break;
    default:
      break;
  }

  *vals = (u_int32_t)result;	// send result back
  *len = 1*sizeof(u_int32_t);			// #bytes in buf
  return 0;
}

