summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/freescale/dpa/capwap/dpaa_capwap_ioctl.c
diff options
context:
space:
mode:
authorPan Jiafei <Jiafei.Pan@freescale.com>2014-07-30 07:41:01 (GMT)
committerMatthew Weigel <Matthew.Weigel@freescale.com>2014-12-11 18:39:17 (GMT)
commitd3515fe84958bd0b20ddddc600de5ea198e597d3 (patch)
treed9044cf7c33ff4b70d6a626bfa668dd0bdebb495 /drivers/net/ethernet/freescale/dpa/capwap/dpaa_capwap_ioctl.c
parent2ae30dce496912131c5a5a401cf2ea13e8210b11 (diff)
downloadlinux-fsl-qoriq-d3515fe84958bd0b20ddddc600de5ea198e597d3.tar.xz
capwap: add capwap support
CAPWAP stands for Control and Provisioning of Wireless Access Points. CAPWAP is a standard, interoperable protocol that enables a controller to manage a collection of wireless access points. There are three drivers in Kernel Space: CAPWAP Domain driver, CAPWAP Bridge driver and CAPWAP Tunnel driver. CAPWAP Domain Driver implements configuration for CAPWAP Domain, including PCD configuration and FQs setup. User must configure and initialize CAPWAP domain before CAPWAP Tunnel and CAPWAP Bridge works. CAPWAP Tunnel Driver is a misc device driver, which registers four misc devices for four CAPWAP tunnels: fsl-capwap-ctrl-dtls, fsl-capwap-ctl-n-dtls, fsl-capwap-data-dtls, fsl-capwap-data-n-dtls. The misc device provides file-operation to transmit/receive DTLS/non-DTLS packets. For example, read from the device fsl-capwap-ctrl-dtls can receive DTLS control packet; Write to the device fsl-capwap-ctrl-dtls can transmit DTLS control packet. The CAPWAP Bridge Driver is also a misc device driver; it can bridge packets between PCIe NIC and CAPWAP data tunnels. To support capwap drivers, there should be a shared ethernet port in dts file to use as CAPWAP Ethernet port. Signed-off-by: Pan Jiafei <Jiafei.Pan@freescale.com> Change-Id: I7c99191156ebee5dd43673d20fb5c8469a780546 Reviewed-on: http://git.am.freescale.net:8181/21438 Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> Reviewed-by: Jianhua Xie <jianhua.xie@freescale.com> Reviewed-by: Shengzhou Liu <Shengzhou.Liu@freescale.com>
Diffstat (limited to 'drivers/net/ethernet/freescale/dpa/capwap/dpaa_capwap_ioctl.c')
-rw-r--r--drivers/net/ethernet/freescale/dpa/capwap/dpaa_capwap_ioctl.c588
1 files changed, 588 insertions, 0 deletions
diff --git a/drivers/net/ethernet/freescale/dpa/capwap/dpaa_capwap_ioctl.c b/drivers/net/ethernet/freescale/dpa/capwap/dpaa_capwap_ioctl.c
new file mode 100644
index 0000000..a8d9a1f
--- /dev/null
+++ b/drivers/net/ethernet/freescale/dpa/capwap/dpaa_capwap_ioctl.c
@@ -0,0 +1,588 @@
+/* Copyright (c) 2014 Freescale Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Freescale Semiconductor nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+
+#include "dpaa_capwap_ioctl.h"
+#include "dpaa_capwap_domain.h"
+
+#include <linux/fdtable.h>
+#include "lnxwrp_fm.h"
+
+#define DRV_VERSION "0.1"
+
+static int dpa_capwap_cdev_major = -1;
+static struct class *capwap_class;
+static struct device *capwap_dev;
+static struct list_head tunnel_list_head;
+
+struct tunnel_info {
+ struct list_head tunnel_list;
+ bool is_control;
+ bool is_dtls;
+ enum dpaa_capwap_domain_direction dir;
+ void *tunnel_id;
+};
+
+
+struct t_device {
+ uintptr_t id; /**< the device id */
+ int fd; /**< the device file descriptor */
+ t_Handle h_UserPriv;
+ uint32_t owners;
+};
+
+
+int wrp_dpa_capwap_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+
+int wrp_dpa_capwap_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+long wrp_dpa_capwap_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long args)
+{
+ long ret = 0;
+
+ switch (cmd) {
+ case DPA_CAPWAP_IOC_DOMAIN_GET_FQIDS: {
+ struct dpaa_capwap_domain_fqs *fqs;
+
+ fqs = get_domain_fqs();
+ if (fqs == NULL)
+ return -ENODEV;
+
+ if (copy_to_user((void *)args, fqs,
+ sizeof(struct dpaa_capwap_domain_fqs))) {
+ pr_err("Could not copy DPA CAPWAP FQID base to user\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ case DPA_CAPWAP_IOC_DOMAIN_INIT: {
+ struct dpaa_capwap_domain_params domain_params;
+ struct dpaa_capwap_domain *capwap_domain = NULL;
+ struct file *fm_pcd_file, *fm_port_file;
+ t_LnxWrpFmDev *fm_wrapper_dev;
+ t_LnxWrpFmPortDev *port_wrapper_dev;
+ struct t_device *dev;
+
+ /* Copy parameters from user-space */
+ if (copy_from_user(&domain_params, (void *)args,
+ sizeof(domain_params))) {
+ pr_err("Could not copy DPA CAPWAP init parameters\n");
+ return -EINVAL;
+ }
+
+ /* Translate FM_PCD file descriptor */
+ fm_pcd_file = fcheck((unsigned long)domain_params.h_fm_pcd);
+ if (!fm_pcd_file) {
+ pr_err("Could not acquire PCD handle\n");
+ return -EINVAL;
+ }
+ fm_wrapper_dev = ((t_LnxWrpFmDev *)fm_pcd_file->private_data);
+ domain_params.h_fm_pcd = (void *)fm_wrapper_dev->h_PcdDev;
+
+ /* Translate FM_Port file descriptor */
+ fm_port_file = fcheck((unsigned long)
+ domain_params.outbound_op.port_handle);
+ if (!fm_port_file) {
+ pr_err("Could not acquire FM Port handle\n");
+ return -EINVAL;
+ }
+ port_wrapper_dev = ((t_LnxWrpFmPortDev *)
+ fm_port_file->private_data);
+ domain_params.outbound_op.port_handle = (void *)
+ port_wrapper_dev->h_Dev;
+
+ /* Translate CCNode handle */
+ dev = domain_params.inbound_pre_params.h_Table;
+ domain_params.inbound_pre_params.h_Table = (void *)dev->id;
+
+ capwap_domain = (struct dpaa_capwap_domain *)
+ dpaa_capwap_domain_config(&domain_params);
+ if (!capwap_domain)
+ return -EINVAL;
+ ret = dpaa_capwap_domain_init(capwap_domain);
+ if (ret < 0)
+ return ret;
+
+ domain_params.id = capwap_domain;
+ if (copy_to_user((void *)args, &domain_params,
+ sizeof(domain_params))) {
+ pr_err("Could not copy DPA CAPWAP ID to user the ID\n");
+ return -EINVAL;
+ }
+
+ break;
+ }
+
+ case DPA_CAPWAP_IOC_DOMAIN_ADD_IN_TUNNEL: {
+ struct dpaa_capwap_tunnel *in_tunnel = NULL;
+ struct dpaa_capwap_domain *capwap_domain = NULL;
+ struct dpaa_capwap_domain_tunnel_in_params in_tunnel_params;
+ struct tunnel_info *tunnel_node, *new_tunnel;
+
+ /* Copy parameters from user-space */
+ if (copy_from_user(&in_tunnel_params, (void *)args,
+ sizeof(in_tunnel_params))) {
+ pr_err("Could not copy DPA CAPWAP Add-In-Tunnel parameters\n");
+ return -EINVAL;
+ }
+
+ capwap_domain = (struct dpaa_capwap_domain *)
+ in_tunnel_params.capwap_domain_id;
+ if (!capwap_domain)
+ return -EINVAL;
+
+ list_for_each_entry(tunnel_node, &tunnel_list_head,
+ tunnel_list) {
+ if (tunnel_node->is_dtls == in_tunnel_params.dtls &&
+ tunnel_node->is_control ==
+ in_tunnel_params.is_control &&
+ tunnel_node->dir ==
+ e_DPAA_CAPWAP_DOMAIN_DIR_INBOUND) {
+ pr_err("%s-%s inbound tunnel already exist, please remove it firstly\n",
+ tunnel_node->is_dtls ? "DTLS" :
+ "N-DTLS",
+ tunnel_node->is_control ?
+ "Control" : "Data");
+ return -EINVAL;
+ }
+ }
+
+ in_tunnel = dequeue_tunnel_obj(&capwap_domain->in_tunnel_list);
+ if (!in_tunnel) {
+ pr_err("You've reached the maximum number of inbound tunnels\n");
+ return -EINVAL;
+ }
+
+ if (copy_from_user(in_tunnel->auth_data.auth_key,
+ in_tunnel_params.dtls_params.auth_key,
+ in_tunnel_params.dtls_params
+ .auth_key_len / 8)) {
+ pr_err("Could not copy auth key from user space\n");
+ return -EINVAL;
+ }
+ if (copy_from_user(in_tunnel->cipher_data.cipher_key,
+ in_tunnel_params.dtls_params.cipher_key,
+ in_tunnel_params.dtls_params
+ .cipher_key_len / 8)) {
+ pr_err("Could not copy cipher key from user space\n");
+ return -EINVAL;
+ }
+
+ ret = add_in_tunnel(capwap_domain, in_tunnel,
+ &in_tunnel_params);
+ if (ret < 0)
+ return ret;
+ in_tunnel_params.tunnel_id = in_tunnel;
+
+ new_tunnel = kzalloc(sizeof(struct tunnel_info), GFP_KERNEL);
+ new_tunnel->is_control = in_tunnel_params.is_control;
+ new_tunnel->is_dtls = in_tunnel_params.dtls;
+ new_tunnel->tunnel_id = in_tunnel_params.tunnel_id;
+ new_tunnel->dir = e_DPAA_CAPWAP_DOMAIN_DIR_INBOUND;
+ list_add_tail(&new_tunnel->tunnel_list, &tunnel_list_head);
+
+ if (copy_to_user((void *)args, &in_tunnel_params,
+ sizeof(in_tunnel_params))) {
+ pr_err("Could not copy DPA CAPWAP ID to user the ID\n");
+ return -EINVAL;
+ }
+
+
+ break;
+ }
+
+ case DPA_CAPWAP_IOC_DOMAIN_ADD_OUT_TUNNEL: {
+ struct dpaa_capwap_tunnel *out_tunnel = NULL;
+ struct dpaa_capwap_domain *capwap_domain = NULL;
+ struct dpaa_capwap_domain_tunnel_out_params out_tunnel_params;
+ struct tunnel_info *tunnel_node, *new_tunnel;
+ uint8_t *buf;
+
+ /* Copy parameters from user-space */
+ if (copy_from_user(&out_tunnel_params, (void *)args,
+ sizeof(out_tunnel_params))) {
+ pr_err("Could not copy DPA CAPWAP Add-Out-Tunnel parameters\n");
+ return -EINVAL;
+ }
+
+ if (!out_tunnel_params.p_ether_header &&
+ out_tunnel_params.eth_header_size != 0) {
+ buf = kzalloc(out_tunnel_params.eth_header_size,
+ GFP_KERNEL);
+ if (buf == NULL) {
+ pr_err("No memory for ether header in %s\n",
+ __func__);
+ return -ENOMEM;
+ }
+ if (copy_from_user(buf,
+ out_tunnel_params.p_ether_header,
+ out_tunnel_params.eth_header_size)) {
+ pr_err("Could not copy ether header from user space:%s\n",
+ __func__);
+ kfree(buf);
+ return -EINVAL;
+ }
+ out_tunnel_params.p_ether_header = buf;
+ }
+ if (!out_tunnel_params.p_ip_header &&
+ out_tunnel_params.ip_header_size != 0) {
+ buf = kzalloc(out_tunnel_params.ip_header_size,
+ GFP_KERNEL);
+ if (buf == NULL) {
+ pr_err("No memory for IP header in %s\n",
+ __func__);
+ kfree(out_tunnel_params.p_ether_header);
+ return -ENOMEM;
+ }
+ if (copy_from_user(buf, out_tunnel_params.p_ip_header,
+ out_tunnel_params.ip_header_size)) {
+ pr_err("Could not copy IP header from user space:%s\n",
+ __func__);
+ kfree(out_tunnel_params.p_ether_header);
+ kfree(buf);
+ return -EINVAL;
+ }
+ out_tunnel_params.p_ip_header = buf;
+ }
+ if (!out_tunnel_params.p_udp_header) {
+ buf = kzalloc(UDP_HDR_SIZE, GFP_KERNEL);
+ if (buf == NULL) {
+ pr_err("No memory for UDP header in %s\n",
+ __func__);
+ kfree(out_tunnel_params.p_ether_header);
+ kfree(out_tunnel_params.p_ip_header);
+ return -ENOMEM;
+ }
+ if (copy_from_user(buf, out_tunnel_params.p_udp_header,
+ UDP_HDR_SIZE)) {
+ pr_err("Could not copy UDP header from user space:%s\n",
+ __func__);
+ kfree(out_tunnel_params.p_ether_header);
+ kfree(out_tunnel_params.p_ip_header);
+ kfree(buf);
+ return -EINVAL;
+ }
+ out_tunnel_params.p_udp_header = buf;
+ }
+ if (!out_tunnel_params.p_capwap_header &&
+ out_tunnel_params.capwap_header_size != 0) {
+ buf = kzalloc(out_tunnel_params.capwap_header_size,
+ GFP_KERNEL);
+ if (buf == NULL) {
+ pr_err("No memory for CAPWAP header in %s\n",
+ __func__);
+ kfree(out_tunnel_params.p_ether_header);
+ kfree(out_tunnel_params.p_ip_header);
+ kfree(out_tunnel_params.p_udp_header);
+ return -ENOMEM;
+ }
+ if (copy_from_user(buf,
+ out_tunnel_params.p_capwap_header,
+ out_tunnel_params.capwap_header_size)) {
+ pr_err("Could not copy CAPWAP header from user space:%s\n",
+ __func__);
+ kfree(out_tunnel_params.p_ether_header);
+ kfree(out_tunnel_params.p_ip_header);
+ kfree(out_tunnel_params.p_udp_header);
+ kfree(buf);
+ return -EINVAL;
+ }
+ out_tunnel_params.p_capwap_header = buf;
+ }
+
+ capwap_domain = (struct dpaa_capwap_domain *)
+ out_tunnel_params.capwap_domain_id;
+ if (!capwap_domain) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ list_for_each_entry(tunnel_node, &tunnel_list_head,
+ tunnel_list) {
+ if (tunnel_node->is_dtls == out_tunnel_params.dtls &&
+ tunnel_node->is_control ==
+ out_tunnel_params.is_control &&
+ tunnel_node->dir ==
+ e_DPAA_CAPWAP_DOMAIN_DIR_OUTBOUND) {
+ pr_err("%s-%s outbound tunnel already exist, please remove it firstly\n",
+ tunnel_node->is_dtls ?
+ "DTLS" : "N-DTLS",
+ tunnel_node->is_control ?
+ "Control" : "Data");
+ ret = -EINVAL;
+ goto err_out;
+ }
+ }
+
+ out_tunnel =
+ dequeue_tunnel_obj(&capwap_domain->out_tunnel_list);
+ if (!out_tunnel) {
+ pr_err("You've reached the maximum number of inbound tunnels\n");
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ if (copy_from_user(out_tunnel->auth_data.auth_key,
+ out_tunnel_params.dtls_params.auth_key,
+ out_tunnel_params.dtls_params
+ .auth_key_len / 8)) {
+ pr_err("Could not copy auth key from user space\n");
+ ret = -EINVAL;
+ goto err_out;
+ }
+ if (copy_from_user(out_tunnel->cipher_data.cipher_key,
+ out_tunnel_params.dtls_params.cipher_key,
+ out_tunnel_params.dtls_params
+ .cipher_key_len / 8)) {
+ pr_err("Could not copy cipher key from user space\n");
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ ret = add_out_tunnel(capwap_domain, out_tunnel,
+ &out_tunnel_params);
+ if (ret < 0)
+ goto err_out;
+ out_tunnel_params.tunnel_id = out_tunnel;
+
+ new_tunnel = kzalloc(sizeof(struct tunnel_info), GFP_KERNEL);
+ if (new_tunnel == NULL) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+ new_tunnel->is_control = out_tunnel_params.is_control;
+ new_tunnel->is_dtls = out_tunnel_params.dtls;
+ new_tunnel->tunnel_id = out_tunnel_params.tunnel_id;
+ new_tunnel->dir = e_DPAA_CAPWAP_DOMAIN_DIR_OUTBOUND;
+ list_add_tail(&new_tunnel->tunnel_list, &tunnel_list_head);
+
+ if (copy_to_user((void *)args, &out_tunnel_params,
+ sizeof(out_tunnel_params))) {
+ pr_err("Could not copy DPA CAPWAP ID to user the ID\n");
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+ break;
+err_out:
+ kfree(out_tunnel_params.p_ether_header);
+ kfree(out_tunnel_params.p_ip_header);
+ kfree(out_tunnel_params.p_udp_header);
+ kfree(out_tunnel_params.p_capwap_header);
+ kfree(new_tunnel);
+ return ret;
+
+ }
+
+ case DPA_CAPWAP_IOC_DOMAIN_REMOVE_TUNNEL: {
+ struct dpaa_capwap_tunnel *tunnel = NULL;
+ struct tunnel_info *tunnel_node;
+ int is_found = 0;
+
+ /* Copy parameters from user-space */
+ if (copy_from_user(&tunnel, (void *)args, sizeof(void *))) {
+ pr_err("Could not copy DPA CAPWAP Remove-Tunnel parameters\n");
+ return -EINVAL;
+ }
+ list_for_each_entry(tunnel_node, &tunnel_list_head,
+ tunnel_list) {
+ if (tunnel_node->tunnel_id == tunnel) {
+ is_found = 1;
+ break;
+ }
+ }
+ if (is_found) {
+ dpaa_capwap_domain_remove_tunnel(tunnel);
+ list_del(&tunnel_node->tunnel_list);
+ kfree(tunnel_node);
+ } else
+ return -EINVAL;
+
+ break;
+ }
+ case DPA_CAPWAP_IOC_DOMAIN_KERNAL_RX_CTL: {
+ struct capwap_domain_kernel_rx_ctl rx_ctl;
+
+ /* Copy parameters from user-space */
+ if (copy_from_user(&rx_ctl, (void *)args,
+ sizeof(struct capwap_domain_kernel_rx_ctl))) {
+ pr_err("Could not copy DPA CAPWAP Remove-Tunnel parameters\n");
+ return -EINVAL;
+ }
+
+ ret = capwap_kernel_rx_ctl(&rx_ctl);
+ if (ret)
+ return ret;
+
+ if (copy_to_user((void *)args, &rx_ctl,
+ sizeof(struct capwap_domain_kernel_rx_ctl))) {
+ pr_err("Could not copy DPA CAPWAP rx fqid to user space\n");
+ return -EINVAL;
+ }
+
+ break;
+ }
+ default:
+ pr_err("Invalid DPA CAPWAP ioctl (0x%x)\n", cmd);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+long wrp_dpa_capwap_ioctl_compat(struct file *filp, unsigned int cmd,
+ unsigned long args)
+{
+ long ret = 0;
+
+ return ret;
+}
+#endif
+
+static const struct file_operations dpa_capwap_fops = {
+ .owner = THIS_MODULE,
+ .open = wrp_dpa_capwap_open,
+ .read = NULL,
+ .write = NULL,
+ .unlocked_ioctl = wrp_dpa_capwap_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = wrp_dpa_capwap_ioctl_compat,
+#endif
+ .release = wrp_dpa_capwap_release
+};
+
+static ssize_t domain_show_statistic(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t bytes = 0;
+ struct tunnel_info *tunnel_node;
+ t_FmPcdManipStats manip_stats;
+ struct dpaa_capwap_tunnel *capwap_tunnel = NULL;
+ int ret;
+
+ list_for_each_entry(tunnel_node, &tunnel_list_head, tunnel_list) {
+ capwap_tunnel = (struct dpaa_capwap_tunnel *)tunnel_node->tunnel_id;
+ if (capwap_tunnel->h_capwap_frag) {
+ memset(&manip_stats, 0, sizeof(manip_stats));
+ ret = FM_PCD_ManipGetStatistics(capwap_tunnel->h_capwap_frag,
+ &manip_stats);
+ if (!ret) {
+ bytes += sprintf(buf + bytes, "%s-%s Tunnel:\n",
+ tunnel_node->is_dtls ?
+ "DTLS" : "N-DTLS",
+ tunnel_node->is_control ?
+ "Control" : "Data");
+ bytes += sprintf(buf + bytes,
+ "\tfrag-total-count: %u\n",
+ manip_stats.u.frag.u.capwapFrag.totalFrames);
+ }
+ }
+ }
+
+ return bytes;
+}
+
+static DEVICE_ATTR(domain_statistic, S_IRUGO, domain_show_statistic, NULL);
+
+static int __init wrp_dpa_capwap_init(void)
+{
+ /* Cannot initialize the wrapper twice */
+ if (dpa_capwap_cdev_major >= 0)
+ return -EBUSY;
+
+ dpa_capwap_cdev_major =
+ register_chrdev(0, DPA_CAPWAP_CDEV, &dpa_capwap_fops);
+ if (dpa_capwap_cdev_major < 0) {
+ pr_err("Could not register DPA CAPWAP character device\n");
+ return dpa_capwap_cdev_major;
+ }
+
+ capwap_class = class_create(THIS_MODULE, DPA_CAPWAP_CDEV);
+ if (IS_ERR(capwap_class)) {
+ pr_err("Cannot create DPA CAPWAP class device\n");
+ unregister_chrdev(dpa_capwap_cdev_major, DPA_CAPWAP_CDEV);
+ dpa_capwap_cdev_major = -1;
+ return PTR_ERR(capwap_class);
+ }
+
+ capwap_dev = device_create(capwap_class, NULL,
+ MKDEV(dpa_capwap_cdev_major, 0), NULL,
+ DPA_CAPWAP_CDEV);
+ if (IS_ERR(capwap_dev)) {
+ pr_err("Cannot create DPA CAPWAP device\n");
+ class_destroy(capwap_class);
+ unregister_chrdev(dpa_capwap_cdev_major, DPA_CAPWAP_CDEV);
+ dpa_capwap_cdev_major = -1;
+ return PTR_ERR(capwap_dev);
+ }
+ INIT_LIST_HEAD(&tunnel_list_head);
+
+ if (device_create_file(capwap_dev, &dev_attr_domain_statistic))
+ dev_err(capwap_dev, "Error creating sysfs file\n");
+
+ pr_info("DPAA CAPWAP Domain driver v%s", DRV_VERSION);
+
+ return 0;
+}
+
+
+static void __exit wrp_dpa_capwap_exit(void)
+{
+ device_destroy(capwap_class, MKDEV(dpa_capwap_cdev_major, 0));
+ class_destroy(capwap_class);
+ unregister_chrdev(dpa_capwap_cdev_major, DPA_CAPWAP_CDEV);
+ dpa_capwap_cdev_major = -1;
+}
+
+module_init(wrp_dpa_capwap_init);
+module_exit(wrp_dpa_capwap_exit);
+
+MODULE_AUTHOR("Freescale, <freescale.com>");
+MODULE_DESCRIPTION("DPA CAPWAP Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);