librmnetctl: add uplink aggregation configuration API

Adds a public API to configure the parameters the RmNet driver will use
when aggregating uplink packets.

Change-Id: I318f32613042a0f54beb6a091a7261db0745b899
Signed-off-by: Sean Tranchetti <stranche@codeaurora.org>
diff --git a/rmnetctl/inc/librmnetctl.h b/rmnetctl/inc/librmnetctl.h
index 3dc8237..9ff5d4d 100644
--- a/rmnetctl/inc/librmnetctl.h
+++ b/rmnetctl/inc/librmnetctl.h
@@ -623,6 +623,30 @@
 int rtrmnet_ctl_bridgevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
 			  uint16_t *error_code);
 
+/* @brief Public API to configure the uplink aggregation parameters
+ * used by the RmNet driver
+ * @details Message type is RMN_NEWLINK
+ * @param hndl RmNet handle for the Netlink message
+ * @param devname Name of device node is connected to
+ * @param vndname Name of virtual device
+ * @param packet_count Maximum number of packets to aggregate
+ * @param byte_count Maximum number of bytes to aggregate
+ * @param time_limit Maximum time to aggregate
+ * @param error_code Status code of this operation returned from the kernel
+ * @return RMNETCTL_SUCCESS if successful
+ * @return RMENTCTL_LIB_ERR if there was a library error. Check error_code
+ * @return RMNETCTL_KERNEL_ERR if there was an error in the kernel.
+ * Check error_code
+ * @return RMNETCTL_INVALID_ARG if invalid arguments were passed to the API
+ */
+int rtrmnet_set_uplink_aggregation_params(rmnetctl_hndl_t *hndl,
+					  char *devname,
+					  char *vndname,
+					  uint8_t packet_count,
+					  uint16_t byte_count,
+					  uint32_t time_limit,
+					  uint16_t *error_code);
+
 int rtrmnet_activate_flow(rmnetctl_hndl_t *hndl,
 			  char *devname,
 			  char *vndname,
diff --git a/rmnetctl/src/librmnetctl.c b/rmnetctl/src/librmnetctl.c
index fcc4ed7..b640b9b 100644
--- a/rmnetctl/src/librmnetctl.c
+++ b/rmnetctl/src/librmnetctl.c
@@ -96,12 +96,19 @@
 	char data[NLMSG_DATA_SIZE];
 };
 
+struct rmnetctl_uplink_params {
+	uint16_t byte_count;
+	uint16_t packet_count;
+	uint32_t time_limit;
+};
+
 /* IFLA Attributes for the RT RmNet driver */
 enum {
 	RMNETCTL_IFLA_UNSPEC,
 	RMNETCTL_IFLA_MUX_ID,
 	RMNETCTL_IFLA_FLAGS,
 	RMNETCTL_IFLA_DFC_QOS,
+	RMNETCTL_IFLA_UPLINK_PARAMS,
 	__RMNETCTL_IFLA_MAX,
 };
 
@@ -1642,6 +1649,95 @@
 	return rmnet_get_ack(hndl, error_code);
 }
 
+int rtrmnet_set_uplink_aggregation_params(rmnetctl_hndl_t *hndl,
+					  char *devname,
+					  char *vndname,
+					  uint8_t packet_count,
+					  uint16_t byte_count,
+					  uint32_t time_limit,
+					  uint16_t *error_code)
+{
+	struct nlmsg req;
+	struct rmnetctl_uplink_params uplink_params;
+	struct rtattr *linkinfo, *datainfo;
+	unsigned int devindex = 0;
+	size_t reqsize;
+	int rc;
+
+	memset(&req, 0, sizeof(req));
+	memset(&uplink_params, 0, sizeof(uplink_params));
+
+	if (!hndl || !devname || !error_code ||_rmnetctl_check_dev_name(devname) ||
+		_rmnetctl_check_dev_name(vndname))
+		return RMNETCTL_INVALID_ARG;
+
+	reqsize = NLMSG_DATA_SIZE - sizeof(struct rtattr);
+	req.nl_addr.nlmsg_type = RTM_NEWLINK;
+	req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+	req.nl_addr.nlmsg_seq = hndl->transaction_id;
+	hndl->transaction_id++;
+
+	/* Get index of devname*/
+	devindex = if_nametoindex(devname);
+	if (devindex == 0) {
+		*error_code = errno;
+		return RMNETCTL_KERNEL_ERR;
+	}
+
+	/* Set up link attr with devindex as data */
+	rc = rta_put_u32(&req, &reqsize, IFLA_LINK, devindex);
+	if (rc != RMNETCTL_SUCCESS) {
+		*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
+		return rc;
+	}
+
+	rc = rta_put_string(&req, &reqsize, IFLA_IFNAME, vndname);
+	if (rc != RMNETCTL_SUCCESS) {
+		*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
+		return rc;
+	}
+
+	/* Set up IFLA info kind RMNET that has linkinfo and type */
+	rc = rta_nested_start(&req, &reqsize, IFLA_LINKINFO, &linkinfo);
+	if (rc != RMNETCTL_SUCCESS) {
+		*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
+		return rc;
+	}
+
+	rc = rta_put_string(&req, &reqsize, IFLA_INFO_KIND, "rmnet");
+	if (rc != RMNETCTL_SUCCESS) {
+		*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
+		return rc;
+	}
+
+	rc = rta_nested_start(&req, &reqsize, IFLA_INFO_DATA, &datainfo);
+	if (rc != RMNETCTL_SUCCESS) {
+		*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
+		return rc;
+	}
+
+	uplink_params.byte_count = byte_count;
+	uplink_params.packet_count = packet_count;
+	uplink_params.time_limit = time_limit;
+	rc = rta_put(&req, &reqsize, RMNETCTL_IFLA_UPLINK_PARAMS,
+		     sizeof(uplink_params), &uplink_params);
+	if (rc != RMNETCTL_SUCCESS) {
+		*error_code = RMNETCTL_API_ERR_RTA_FAILURE;
+		return rc;
+	}
+
+	rta_nested_end(&req, datainfo);
+	rta_nested_end(&req, linkinfo);
+
+	if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+		*error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+		return RMNETCTL_LIB_ERR;
+	}
+
+	return rmnet_get_ack(hndl, error_code);
+
+}
 
 int rtrmnet_activate_flow(rmnetctl_hndl_t *hndl,
 			  char *devname,