
//
// Name:	fpxReads.c
// Purpose:	Simple plugin to demonstrate access to fpx counters and registers
// History:
// o June 20, 2005:	John DeHart, creation
//

// Notation:
//	- this:  struct rp_instance * (ptr to plugin instance)
// Post-Conditions:
//   fpxReads_load
//	- Plugin class (fpxReads_class) structure is initialized
//	- fpxReads_get_class() is registered with PCU
//   fpxReads_create_instance
//	- Plugin instance memory is allocated and initialized
//	  (base class and its extension):
//	  this->rpclass = &fpxReads_class
//	  ... other members of base class ...
//   fpxReads_bind_instance
//   fpxReads_handle_packet
//   fpxReads_unbind_instance
//   fpxReads_free_instance
//	- Plugin instance memory is free
//   fpxReads_unload
//	- All instances are free
//	- Class is not registered with PCU
//

#include "./stdinc.h"

#include "fpxReads.h"

MOD_MISC("fpxReads")

// 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 fpxReads_class;

void
fpxReads_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.
//
  fpxReads_class.classid = fpxReads_ID;
//  fpxReads_class.itype   = RP_INTERFACE_TYPE_PKT;
  fpxReads_class.itype   = RP_INTERFACE_TYPE_HLIST;
  fpxReads_class.create_instance = fpxReads_create_instance;
  return;
}

struct rp_class *
fpxReads_get_class() {	// return class structure
  return &fpxReads_class;
}

// --| create and initialize an instance of fpxReads |--
struct rp_instance *
fpxReads_create_instance(struct rp_class *myclass, u_int32_t instanceid) {
  struct fpxReads_instance *myinst; 

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

  // fill in instance pointers to local methods
  myinst->rootinstance.rpclass         = &fpxReads_class;
  myinst->rootinstance.handle_packet   = fpxReads_handle_packet;
  myinst->rootinstance.free_instance   = fpxReads_free_instance;
  myinst->rootinstance.bind_instance   = fpxReads_bind_instance;
  myinst->rootinstance.unbind_instance = fpxReads_unbind_instance;
  myinst->rootinstance.handle_msg      = fpxReads_handle_msg;

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

  return (struct rp_instance *)myinst;
}

void
fpxReads_handle_packet(struct rp_instance *this, void *pkt) {
  struct fpxReads_instance *inst = (struct fpxReads_instance *)this;

  inst->pkt_count++;

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

void
fpxReads_free_instance(struct rp_instance *this) {
  if (this) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
          "fpxReads_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,
          "fpxReads_free_instance: Passing a NULL this pointer\n"));
  }
}

void
fpxReads_bind_instance(struct rp_instance *this) {
  struct fpxReads_instance *inst = (struct fpxReads_instance *)this;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
	"fpxReads_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
fpxReads_unbind_instance(struct rp_instance *this) {
  struct fpxReads_instance *inst = (struct fpxReads_instance *)this;

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_INFO,
	"fpxReads_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
fpxReads  (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
fpxReads  (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,
        "fpxReads: Entry function -- initialized plugin_fcts pointers\n"));

  MSR_PLUGIN_DISPATCH(lkmtp, cmd, ver, fpxReads_load, fpxReads_unload,
							PLUGIN_LKM_NOFUNC_FCT);
}

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

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
        "fpxReads_load: Loading fpxReads ...\n"));

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

  err = PLUGIN_PCU_REGISTER_CLASS_FCT(fpxReads_get_class());
  if (err != RP_OK) {
    MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_ERROR,
	"fpxReads_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
fpxReads_unload(struct lkm_table *lkmtp, int cmd) {
  struct rp_class *rpclass;
  u_int32_t cid;

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

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
        "fpxReads_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,
          "fpxReads_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,
          "fpxReads_unload: Error deregistering class %d\n", cid));
  }

  MSR_DEBUG((MSR_DEBUG_PLUGIN | MSR_DEBUG_LEVEL_VERBOSE,
        "fpxReads_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
fpxReads_handle_msg(struct rp_instance *this, void *buf, u_int8_t flags,
						u_int8_t seq, u_int8_t *len) {
  struct fpxReads_instance *inst = (struct fpxReads_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,
    "fpxReads_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,
    			"fpxReads_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,
    				"fpxReads_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,
    			"fpxReads_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,
    				"fpxReads_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,
    			"fpxReads_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,
    				"fpxReads_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,
    			"fpxReads_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,
    				"fpxReads_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)htonl(result);	// send result back
  *vals = (u_int32_t)result;	// send result back
  *len = 1*sizeof(u_int32_t);			// #bytes in buf
  return 0;
}

