diff --git a/config/common_bsdapp b/config/common_bsdapp
index 8fcc004..9c5e1e0 100644
--- a/config/common_bsdapp
+++ b/config/common_bsdapp
@@ -167,6 +167,14 @@ CONFIG_RTE_LIBRTE_QAT_DEBUG_DRIVER=y
 #
 CONFIG_RTE_MAX_QAT_SESSIONS=200
 
+
+#
+# Compile PMD for AESNI backed device
+#
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB=y
+CONFIG_RTE_LIBRTE_AESNI_MB_DEBUG=n
+
+#
 # Support NIC bypass logic
 #
 CONFIG_RTE_NIC_BYPASS=n
diff --git a/config/common_linuxapp b/config/common_linuxapp
index 7199c95..8e9e8fd 100644
--- a/config/common_linuxapp
+++ b/config/common_linuxapp
@@ -165,6 +165,13 @@ CONFIG_RTE_LIBRTE_PMD_QAT_DEBUG_DRIVER=y
 #
 CONFIG_RTE_LIBRTE_PMD_QAT_MAX_SESSIONS=4096
 
+# Compile PMD for AESNI backed device
+#
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB=y
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB_DEBUG=n
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB_MAX_SESSIONS=2048
+
+#
 # Support NIC bypass logic
 #
 CONFIG_RTE_NIC_BYPASS=n
diff --git a/doc/guides/cryptodevs/aesni_mb.rst b/doc/guides/cryptodevs/aesni_mb.rst
new file mode 100644
index 0000000..4d15b6b
--- /dev/null
+++ b/doc/guides/cryptodevs/aesni_mb.rst
@@ -0,0 +1,76 @@
+..  BSD LICENSE
+    Copyright(c) 2015 Intel Corporation. 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 Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "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 THE COPYRIGHT
+    OWNER OR CONTRIBUTORS 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.
+
+AESN-NI Multi Buffer Crytpo Poll Mode Driver
+============================================
+
+
+The AESNI MB PMD (**librte_pmd_aesni_mb**) provides poll mode crypto driver 
+support for utilising Intel multi buffer library, see the white paper
+`Fast Multi-buffer IPsec Implementations on Intel® Architecture Processors
+<https://www-ssl.intel.com/content/www/us/en/intelligent-systems/intel-technology/fast-multi-buffer-ipsec-implementations-ia-processors-paper.html?wapkw=multi+buffer>`_. 
+
+The AES-NI MB PMD has current only been tested on Fedora 21 64-bit with gcc.
+
+Features
+--------
+
+AESNI MB PMD has support for:
+
+Cipher algorithms:
+
+* RTE_CRYPTO_SYM_CIPHER_AES128_CBC
+* RTE_CRYPTO_SYM_CIPHER_AES256_CBC
+* RTE_CRYPTO_SYM_CIPHER_AES512_CBC
+
+Hash algorithms:
+
+* RTE_CRYPTO_SYM_HASH_SHA1_HMAC
+* RTE_CRYPTO_SYM_HASH_SHA256_HMAC
+* RTE_CRYPTO_SYM_HASH_SHA512_HMAC
+
+Limitations
+-----------
+
+* Chained mbufs are not supported.
+* Hash only is not supported.
+* Cipher only is not supported.
+* Only in-place is currently supported (destination address is the same as source address).
+* Only supports session-oriented API implementation (session-less APIs are not supported).
+*  Not performance tuned.
+
+Installation
+------------
+
+To build DPKD with the AESNI_MB_PMD the user is required to download the library
+from `here <https://downloadcenter.intel.com/download/22972>`_ and compile it on
+their user system before building DPDK. The environmental variable 
+AESNI_MULTI_BUFFER_LIB_PATH must be exported with the path where you extracted 
+and built the multi buffer library and finally set 
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB=y in config/common_linuxapp. 
\ No newline at end of file
diff --git a/doc/guides/cryptodevs/index.rst b/doc/guides/cryptodevs/index.rst
index 1c31697..8949fd0 100644
--- a/doc/guides/cryptodevs/index.rst
+++ b/doc/guides/cryptodevs/index.rst
@@ -39,4 +39,5 @@ Crypto Device Drivers
     :maxdepth: 2
     :numbered:
 
+    aesni_mb
     qat
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index eeb998e..26325b0 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -31,7 +31,7 @@
 
 include $(RTE_SDK)/mk/rte.vars.mk
 
-
+DIRS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB) += aesni_mb
 DIRS-$(CONFIG_RTE_LIBRTE_PMD_QAT) += qat
 
 include $(RTE_SDK)/mk/rte.sharelib.mk
diff --git a/drivers/crypto/aesni_mb/Makefile b/drivers/crypto/aesni_mb/Makefile
new file mode 100644
index 0000000..62f51ce
--- /dev/null
+++ b/drivers/crypto/aesni_mb/Makefile
@@ -0,0 +1,67 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2015 Intel Corporation. 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 Intel Corporation nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#   "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 THE COPYRIGHT
+#   OWNER OR CONTRIBUTORS 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.
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifeq ($(AESNI_MULTI_BUFFER_LIB_PATH),)
+$(error "Please define AESNI_MULTI_BUFFER_LIB_PATH environment variable")
+endif
+
+# library name
+LIB = librte_pmd_aesni_mb.a
+
+# build flags
+CFLAGS += -O3
+CFLAGS += $(WERROR_FLAGS)
+
+# library version
+LIBABIVER := 1
+
+# versioning export map
+EXPORT_MAP := rte_pmd_aesni_version.map
+
+# external library include paths
+CFLAGS += -I$(AESNI_MULTI_BUFFER_LIB_PATH)
+CFLAGS += -I$(AESNI_MULTI_BUFFER_LIB_PATH)/include
+
+# library source files
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB) += rte_aesni_mb_pmd.c
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB) += rte_aesni_mb_pmd_ops.c
+
+# export include files
+SYMLINK-y-include +=
+
+
+# library dependencies
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB) += lib/librte_eal
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB) += lib/librte_mbuf
+DEPDIRS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB) += lib/librte_cryptodev
+
+include $(RTE_SDK)/mk/rte.lib.mk
diff --git a/drivers/crypto/aesni_mb/aesni_mb_ops.h b/drivers/crypto/aesni_mb/aesni_mb_ops.h
new file mode 100644
index 0000000..ab96990
--- /dev/null
+++ b/drivers/crypto/aesni_mb/aesni_mb_ops.h
@@ -0,0 +1,206 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. 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 Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "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 THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS 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.
+ */
+
+#ifndef _AESNI_MB_OPS_H_
+#define _AESNI_MB_OPS_H_
+
+#ifndef LINUX
+#define LINUX
+#endif
+
+#include <mb_mgr.h>
+#include <aux_funcs.h>
+#include <gcm_defines.h>
+
+enum aesni_mb_vector_mode {
+	RTE_AESNI_MB_NOT_SUPPORTED = 0,
+	RTE_AESNI_MB_SSE,
+	RTE_AESNI_MB_AVX,
+	RTE_AESNI_MB_AVX2
+};
+
+typedef void (*md5_one_block_t)(void *data, void *digest);
+typedef void (*sha1_one_block_t)(void *data, void *digest);
+typedef void (*sha224_one_block_t)(void *data, void *digest);
+typedef void (*sha256_one_block_t)(void *data, void *digest);
+typedef void (*sha384_one_block_t)(void *data, void *digest);
+typedef void (*sha512_one_block_t)(void *data, void *digest);
+
+typedef void (*aes_keyexp_128_t)(void *key, void *enc_exp_keys, void *dec_exp_keys);
+typedef void (*aes_keyexp_192_t)(void *key, void *enc_exp_keys, void *dec_exp_keys);
+typedef void (*aes_keyexp_256_t)(void *key, void *enc_exp_keys, void *dec_exp_keys);
+
+typedef void (*aes_xcbc_expand_key_t)(void *key, void *k1_exp, void *k2, void *k3);
+
+typedef void (*aesni_gcm_t)(gcm_data *my_ctx_data, u8 *out, const u8 *in,
+		u64 plaintext_len, u8 *iv, const u8 *aad, u64 aad_len,
+		u8 *auth_tag, u64 auth_tag_len);
+
+typedef void (*aesni_gcm_precomp_t)(gcm_data *my_ctx_data, u8 *hash_subkey);
+
+/** Multi-buffer library function pointer table */
+struct aesni_mb_ops {
+	struct {
+		init_mb_mgr_t init_mgr;		/**< Initialise scheduler  */
+		get_next_job_t get_next;	/**< Get next free job structure */
+		submit_job_t submit;		/**< Submit job to scheduler */
+		get_completed_job_t get_completed_job; /**< Get completed job */
+		flush_job_t flush_job;		/**< flush jobs from manager */
+	} job; /**< multi buffer manager functions */
+	struct {
+		struct {
+			md5_one_block_t md5;		/**< MD5 one block hash */
+			sha1_one_block_t sha1;		/**< SHA1 one block hash */
+			sha224_one_block_t sha224;	/**< SHA224 one block hash */
+			sha256_one_block_t sha256;	/**< SHA256 one block hash */
+			sha384_one_block_t sha384;	/**< SHA384 one block hash */
+			sha512_one_block_t sha512;	/**< SHA512 one block hash */
+		} one_block; /**< one block hash functions */
+		struct {
+			aes_keyexp_128_t aes128;	/**< AES128 key expansions */
+			aes_keyexp_192_t aes192;	/**< AES192 key expansions */
+			aes_keyexp_256_t aes256;	/**< AES256 key expansions */
+			aes_xcbc_expand_key_t aes_xcbc;	/**< AES XCBC key expansions */
+		} keyexp;	/**< Key expansion functions */
+	} aux; /**< Auxiliary functions */
+	struct {
+		aesni_gcm_t enc;		/**< MD5 encode */
+		aesni_gcm_t dec;		/**< GCM decode */
+		aesni_gcm_precomp_t precomp;	/**< GCM pre-compute */
+	} gcm; /**< GCM functions */
+};
+
+
+static const struct aesni_mb_ops job_ops[] = {
+		[RTE_AESNI_MB_NOT_SUPPORTED] = {
+			.job = { NULL },
+			.aux = {
+				.one_block = { NULL },
+				.keyexp = { NULL }
+			},
+			.gcm = { NULL
+			}
+		},
+		[RTE_AESNI_MB_SSE] = {
+			.job = {
+				init_mb_mgr_sse,
+				get_next_job_sse,
+				submit_job_sse,
+				get_completed_job_sse,
+				flush_job_sse
+			},
+			.aux = {
+				.one_block = {
+					md5_one_block_sse,
+					sha1_one_block_sse,
+					sha224_one_block_sse,
+					sha256_one_block_sse,
+					sha384_one_block_sse,
+					sha512_one_block_sse
+				},
+				.keyexp = {
+					aes_keyexp_128_sse,
+					aes_keyexp_192_sse,
+					aes_keyexp_256_sse,
+					aes_xcbc_expand_key_sse
+				}
+			},
+			.gcm = {
+				aesni_gcm_enc_sse,
+				aesni_gcm_dec_sse,
+				aesni_gcm_precomp_sse
+			}
+		},
+		[RTE_AESNI_MB_AVX] = {
+				.job = {
+					init_mb_mgr_avx,
+					get_next_job_avx,
+					submit_job_avx,
+					get_completed_job_avx,
+					flush_job_avx
+				},
+				.aux = {
+					.one_block = {
+						md5_one_block_avx,
+						sha1_one_block_avx,
+						sha224_one_block_avx,
+						sha256_one_block_avx,
+						sha384_one_block_avx,
+						sha512_one_block_avx
+					},
+					.keyexp = {
+						aes_keyexp_128_avx,
+						aes_keyexp_192_avx,
+						aes_keyexp_256_avx,
+						aes_xcbc_expand_key_avx
+					}
+				},
+				.gcm = {
+					aesni_gcm_enc_avx_gen2,
+					aesni_gcm_dec_avx_gen2,
+					aesni_gcm_precomp_avx_gen2
+				}
+		},
+		[RTE_AESNI_MB_AVX2] = {
+				.job = {
+					init_mb_mgr_avx2,
+					get_next_job_avx2,
+					submit_job_avx2,
+					get_completed_job_avx2,
+					flush_job_avx2
+				},
+				.aux = {
+					.one_block = {
+						md5_one_block_avx2,
+						sha1_one_block_avx2,
+						sha224_one_block_avx2,
+						sha256_one_block_avx2,
+						sha384_one_block_avx2,
+						sha512_one_block_avx2
+					},
+					.keyexp = {
+						aes_keyexp_128_avx2,
+						aes_keyexp_192_avx2,
+						aes_keyexp_256_avx2,
+						aes_xcbc_expand_key_avx2
+					}
+				},
+				.gcm = {
+					aesni_gcm_enc_avx_gen4,
+					aesni_gcm_dec_avx_gen4,
+					aesni_gcm_precomp_avx_gen4
+				}
+		},
+};
+
+
+#endif /* _AESNI_MB_OPS_H_ */
diff --git a/drivers/crypto/aesni_mb/rte_aesni_mb_pmd.c b/drivers/crypto/aesni_mb/rte_aesni_mb_pmd.c
new file mode 100644
index 0000000..65a3731
--- /dev/null
+++ b/drivers/crypto/aesni_mb/rte_aesni_mb_pmd.c
@@ -0,0 +1,550 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. 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 Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "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 THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS 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.
+ */
+
+#include <rte_common.h>
+#include <rte_cryptodev.h>
+#include <rte_dev.h>
+#include <rte_malloc.h>
+#include <rte_cpuflags.h>
+
+#include "rte_aesni_mb_pmd_private.h"
+
+/**
+ * Global static parameter used to create a unique name for each AES-NI multi
+ * buffer crypto device.
+ */
+static unsigned unique_name_id = 0;
+
+static inline int
+create_unique_device_name(char *name, size_t size)
+{
+	int ret;
+
+	if (name == NULL)
+		return -EINVAL;
+
+	ret = snprintf(name, size, "%s_%u", CRYPTODEV_NAME_AESNI_MB_PMD,
+			unique_name_id++);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+typedef void (*hash_one_block_t)(void *data, void *digest);
+typedef void (*aes_keyexp_t)(void *key, void *enc_exp_keys, void *dec_exp_keys);
+
+/**
+ * Calculate the authentication pre-computes
+ *
+ * @param one_block_hash	Function pointer to calculate digest on ipad/opad
+ * @param ipad			Inner pad output byte array
+ * @param opad			Outer pad output byte array
+ * @param hkey			Authentication key
+ * @param hkey_len		Authentication key length
+ * @param blocksize		Block size of selected hash algo
+ */
+static void
+calculate_auth_precomputes(hash_one_block_t one_block_hash,
+		uint8_t *ipad, uint8_t *opad,
+		uint8_t *hkey, uint16_t hkey_len,
+		uint16_t blocksize)
+{
+	unsigned i, length;
+
+	uint8_t ipad_buf[blocksize] __rte_align(16);
+	uint8_t opad_buf[blocksize] __rte_align(16);
+
+	/* Setup inner and outer pads */
+	memset(ipad_buf, HMAC_IPAD_VALUE, blocksize);
+	memset(opad_buf, HMAC_OPAD_VALUE, blocksize);
+
+	/* XOR hash key with inner and outer pads */
+	length = hkey_len > blocksize ? blocksize : hkey_len;
+
+	for (i = 0; i < length; i++) {
+		ipad_buf[i] ^= hkey[i];
+		opad_buf[i] ^= hkey[i];
+	}
+
+	/* Compute partial hashes */
+	(*one_block_hash)(ipad_buf, ipad);
+	(*one_block_hash)(opad_buf, opad);
+
+	/* Clean up stack */
+	memset(ipad_buf, 0, blocksize);
+	memset(opad_buf, 0, blocksize);
+}
+
+int
+aesni_mb_set_session_parameters(const struct aesni_mb_ops *mb_ops,
+		struct aesni_mb_session *sess,
+		struct rte_crypto_cipher_params *cipher_params,
+		struct rte_crypto_hash_params *auth_params,
+		enum rte_crypto_operation_chain op_chain)
+{
+	aes_keyexp_t aes_keyexp_fn;
+	hash_one_block_t hash_oneblock_fn;
+
+	/* Select Crypto operation - hash then cipher / cipher then hash */
+	switch (op_chain) {
+	case RTE_CRYPTO_SYM_OPCHAIN_HASH_CIPHER:
+		sess->chain_order = HASH_CIPHER;
+		break;
+	case RTE_CRYPTO_SYM_OPCHAIN_CIPHER_HASH:
+		sess->chain_order = CIPHER_HASH;
+		break;
+	default:
+		printf("unsupported operation chain order parameter");
+		return -1;
+	}
+
+	/* Select cipher direction */
+	switch (cipher_params->op) {
+	case RTE_CRYPTO_SYM_CIPHER_OP_ENCRYPT:
+		sess->cipher.direction = ENCRYPT;
+		break;
+	case RTE_CRYPTO_SYM_CIPHER_OP_DECRYPT:
+		sess->cipher.direction = DECRYPT;
+		break;
+	default:
+		printf("unsupported cipher operation parameter");
+		return -1;
+	}
+
+	/* Select cipher mode */
+	switch (cipher_params->algo) {
+	case RTE_CRYPTO_SYM_CIPHER_AES_CBC:
+		sess->cipher.mode = CBC;
+		break;
+	default:
+		printf("unsupported cipher mode parameter");
+		return -1;
+	}
+
+	/* Check key length and choose key expansion function */
+	switch (cipher_params->key.length) {
+	case AES_128_BYTES:
+		sess->cipher.key_length_in_bytes = AES_128_BYTES;
+		aes_keyexp_fn = mb_ops->aux.keyexp.aes128;
+		break;
+	case AES_192_BYTES:
+		sess->cipher.key_length_in_bytes = AES_192_BYTES;
+		aes_keyexp_fn = mb_ops->aux.keyexp.aes192;
+		break;
+	case AES_256_BYTES:
+		sess->cipher.key_length_in_bytes = AES_256_BYTES;
+		aes_keyexp_fn = mb_ops->aux.keyexp.aes256;
+		break;
+	default:
+		printf("unsupported cipher key length");
+		return -1;
+	}
+
+	/* Expanded cipher keys */
+	(*aes_keyexp_fn)(cipher_params->key.data,
+			sess->cipher.expanded_aes_keys.encode,
+			sess->cipher.expanded_aes_keys.decode);
+
+	/* Set Authentication Parameters */
+	switch (auth_params->algo) {
+	case RTE_CRYPTO_SYM_HASH_MD5_HMAC:
+		sess->auth.algo = MD5;
+		hash_oneblock_fn = mb_ops->aux.one_block.md5;
+		break;
+	case RTE_CRYPTO_SYM_HASH_SHA1_HMAC:
+		sess->auth.algo = SHA1;
+		hash_oneblock_fn = mb_ops->aux.one_block.sha1;
+		break;
+	case RTE_CRYPTO_SYM_HASH_SHA224_HMAC:
+		sess->auth.algo = SHA_224;
+		hash_oneblock_fn = mb_ops->aux.one_block.sha224;
+		break;
+	case RTE_CRYPTO_SYM_HASH_SHA256_HMAC:
+		sess->auth.algo = SHA_256;
+		hash_oneblock_fn = mb_ops->aux.one_block.sha256;
+		break;
+	case RTE_CRYPTO_SYM_HASH_SHA384_HMAC:
+		sess->auth.algo = SHA_384;
+		hash_oneblock_fn = mb_ops->aux.one_block.sha384;
+		break;
+	case RTE_CRYPTO_SYM_HASH_SHA512_HMAC:
+		sess->auth.algo = SHA_512;
+		hash_oneblock_fn = mb_ops->aux.one_block.sha512;
+		break;
+	default:
+		printf("unsupported authentication algorithm selection");
+		return -1;
+	}
+
+	/* Calculate Authentication precomputes */
+	calculate_auth_precomputes(hash_oneblock_fn,
+			sess->auth.pads.inner, sess->auth.pads.outer,
+			auth_params->auth_key.data,
+			auth_params->auth_key.length,
+			get_auth_algo_blocksize(sess->auth.algo));
+
+	return 0;
+}
+
+/**
+ * Process a crypto operation and complete a JOB_AES_HMAC job structure for
+ * submission to the multi buffer library for processing.
+ *
+ * @param	qp	queue pair
+ * @param	job	JOB_AES_HMAC structure to fill
+ * @param	m	mbuf to process
+ *
+ * @return
+ * - Completed JOB_AES_HMAC structure pointer on success
+ * - NULL pointer if completion of JOB_AES_HMAC structure isn't possible
+ */
+static JOB_AES_HMAC *
+process_crypto_op(struct aesni_mb_qp *qp, JOB_AES_HMAC *job, struct rte_mbuf *m)
+{
+	struct rte_crypto_op_data *c_op = m->crypto_op;
+	struct aesni_mb_session *sess;
+
+	if (c_op->type == RTE_CRYPTO_OP_SESSIONLESS) {
+		sess = aesni_mb_get_session(qp->sess_mp);
+		if (unlikely(sess == NULL))
+			return NULL;
+
+		if (unlikely(aesni_mb_set_session_parameters(qp->mb_ops,
+				sess, &c_op->op_params.cipher,
+				&c_op->op_params.hash,
+				c_op->op_params.opchain) != 0))
+			return NULL;
+	} else {
+		sess = (struct aesni_mb_session *)c_op->session;
+	}
+
+	/* Set crypto operation */
+	job->chain_order = sess->chain_order;
+
+
+	/* Set cipher parameters */
+	job->cipher_direction = sess->cipher.direction;
+	job->cipher_mode = sess->cipher.mode;
+
+	job->aes_key_len_in_bytes = sess->cipher.key_length_in_bytes;
+	job->aes_enc_key_expanded = sess->cipher.expanded_aes_keys.encode;
+	job->aes_dec_key_expanded = sess->cipher.expanded_aes_keys.decode;
+
+
+	/* Set authentication parameters */
+	job->hash_alg = sess->auth.algo;
+	job->hashed_auth_key_xor_ipad = sess->auth.pads.inner;
+	job->hashed_auth_key_xor_opad = sess->auth.pads.outer;
+
+
+	/* Mutable crypto operation parameters */
+
+	/* Set digest output location */
+	if (job->cipher_direction == DECRYPT) {
+		job->auth_tag_output = (uint8_t *)rte_pktmbuf_append(m,
+				get_digest_byte_length(job->hash_alg));
+
+		if (job->auth_tag_output)
+			memset(job->auth_tag_output, 0,
+				sizeof(get_digest_byte_length(job->hash_alg)));
+		else
+			return NULL;
+	} else {
+		job->auth_tag_output = c_op->digest.data;
+	}
+
+	/* Set digest output length */
+#if 0
+	job->auth_tag_output_len_in_bytes = get_digest_byte_length(job->hash_alg);
+	job->auth_tag_output_len_in_bytes = job->auth_tag_output_len_in_bytes > c_op->digest.length ? c_op->digest.length : job->auth_tag_output_len_in_bytes;
+#else
+	job->auth_tag_output_len_in_bytes = get_truncated_digest_byte_length(job->hash_alg);
+#endif
+	/* Set IV parameters */
+	job->iv = c_op->iv.data;
+	job->iv_len_in_bytes = c_op->iv.length;
+
+	/* Data  Parameter */
+	job->src = rte_pktmbuf_mtod(m, uint8_t *);
+	job->dst = c_op->dst ? rte_pktmbuf_mtod(c_op->dst, uint8_t *) :
+			rte_pktmbuf_mtod(m, uint8_t *) + c_op->data.to_cipher.offset;
+
+	job->cipher_start_src_offset_in_bytes = c_op->data.to_cipher.offset;
+	job->msg_len_to_cipher_in_bytes = c_op->data.to_cipher.length;
+
+	job->hash_start_src_offset_in_bytes = c_op->data.to_hash.offset;
+	job->msg_len_to_hash_in_bytes = c_op->data.to_hash.length;
+
+	/* Set user data to be crypto operation data struct */
+	job->user_data = m;
+
+	return job;
+}
+
+/**
+ * Process a completed job and return rte_mbuf which job processed
+ *
+ * @param job	JOB_AES_HMAC job to process
+ *
+ * @return
+ * - Returns processed mbuf which is trimmed of output digest used in
+ * verification of supplied digest in the case of a HASH_CIPHER operation
+ * - Returns NULL on invalid job
+ */
+static struct rte_mbuf *
+post_process_job(JOB_AES_HMAC *job)
+{
+	struct rte_mbuf *m;
+
+	if (job == NULL  || job->user_data == NULL)
+		return NULL;
+
+	/* handled retrieved job */
+	m = (struct rte_mbuf *)job->user_data;
+
+	/* Verify digest if required */
+	if (job->chain_order == HASH_CIPHER) {
+		if (memcmp(job->auth_tag_output, m->crypto_op->digest.data,
+				job->auth_tag_output_len_in_bytes) != 0)
+			m->ol_flags |= PKT_RX_CRYPTO_DIGEST_BAD;
+		else
+			m->ol_flags &= ~PKT_RX_CRYPTO_DIGEST_BAD;
+
+		/* trim area used for digest from mbuf */
+		rte_pktmbuf_trim(m, get_digest_byte_length(job->hash_alg));
+	}
+
+	return m;
+}
+
+/**
+ * Process a completed JOB_AES_HMAC job and keep processing jobs until
+ * get_completed_job return NULL
+ *
+ * @param qp		Queue Pair to process
+ * @param job		JOB_AES_HMAC job
+ *
+ * @return
+ * - Number of processed jobs
+ */
+static unsigned
+handle_completed_jobs(struct aesni_mb_qp *qp, JOB_AES_HMAC *job)
+{
+	struct rte_mbuf *m = NULL;
+	unsigned processed_jobs = 0;
+
+	while (job) {
+		processed_jobs++;
+		m = post_process_job(job);
+		if (m)
+			rte_ring_enqueue(qp->processed_pkts, (void *)m);
+		else
+			qp->qp_stats.dequeue_err_count++;
+
+		job = (*qp->mb_ops->job.get_completed_job)(&qp->mb_mgr);
+	}
+
+	return processed_jobs;
+}
+
+
+static uint16_t
+aesni_mb_pmd_enqueue_burst(void *queue_pair,
+		struct rte_mbuf **bufs, uint16_t nb_bufs)
+{
+	struct aesni_mb_qp *qp = queue_pair;
+	JOB_AES_HMAC *job = NULL;
+
+	int i, processed_jobs = 0;
+
+	for (i = 0; i < nb_bufs; i++) {
+
+		if (unlikely(!(bufs[i]->ol_flags & PKT_TX_CRYPTO_OP))) {
+			qp->qp_stats.enqueue_err_count++;
+			goto flush_jobs;
+		}
+
+		job = (*qp->mb_ops->job.get_next)(&qp->mb_mgr);
+		if (unlikely(job == NULL)) {
+			qp->qp_stats.enqueue_err_count++;
+			goto flush_jobs;
+		}
+
+		job = process_crypto_op(qp, job, bufs[i]);
+		if (unlikely(job == NULL)) {
+			qp->qp_stats.enqueue_err_count++;
+			goto flush_jobs;
+		}
+
+		/* Submit Job */
+		job = (*qp->mb_ops->job.submit)(&qp->mb_mgr);
+		qp->qp_stats.enqueued_count++;
+
+		/* If submit return a processed job then handle it, before
+		 * submitting subsequent jobs */
+		if (job)
+			processed_jobs += handle_completed_jobs(qp, job);
+	}
+
+	if (processed_jobs == 0)
+		goto flush_jobs;
+	else
+		qp->qp_stats.dequeued_count += processed_jobs;
+		return i;
+
+flush_jobs:
+	/* if we haven't processed any jobs in submit loop, then flush jobs
+	 * queue to stop the output stalling */
+	job = (*qp->mb_ops->job.flush_job)(&qp->mb_mgr);
+	if (job)
+		qp->qp_stats.dequeued_count += handle_completed_jobs(qp, job);
+
+	return i;
+}
+
+static uint16_t
+aesni_mb_pmd_dequeue_burst(void *queue_pair,
+		struct rte_mbuf **bufs,	uint16_t nb_bufs)
+{
+	struct aesni_mb_qp *qp = queue_pair;
+	unsigned i, nb_dequeued;
+
+	nb_dequeued = rte_ring_dequeue_burst(qp->processed_pkts,
+			(void **)bufs, nb_bufs);
+
+	for (i = 0; i < nb_dequeued; i++) {
+		/* Free session if a session-less crypto op */
+		if (bufs[i]->crypto_op->type == RTE_CRYPTO_OP_SESSIONLESS) {
+			aesni_mb_free_session(qp->sess_mp,
+					(struct aesni_mb_session *)bufs[i]->crypto_op->session);
+			bufs[i]->crypto_op->session = NULL;
+		}
+	}
+
+	return nb_dequeued;
+}
+
+
+static int cryptodev_aesni_mb_uninit(const char *name);
+
+static int
+cryptodev_aesni_mb_create(const char *name, unsigned socket_id)
+{
+	struct rte_cryptodev *dev;
+	char crypto_dev_name[RTE_CRYPTODEV_NAME_MAX_LEN];
+	struct aesni_mb_private *internals;
+	enum aesni_mb_vector_mode vector_mode;
+
+	/* Check CPU for support for AES instruction set */
+	if (!rte_cpu_get_flag_enabled(RTE_CPUFLAG_AES)) {
+		MB_LOG_ERR("AES instructions not supported by CPU");
+		return -EFAULT;
+	}
+
+	/* Check CPU for supported vector instruction set */
+	if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2))
+		vector_mode = RTE_AESNI_MB_AVX2;
+	else if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX))
+		vector_mode = RTE_AESNI_MB_AVX;
+	else if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE4_1))
+		vector_mode = RTE_AESNI_MB_SSE;
+	else {
+		MB_LOG_ERR("Vector instructions are not supported by CPU");
+		return -EFAULT;
+	}
+
+	/* create a unique device name */
+	if (create_unique_device_name(crypto_dev_name,
+			RTE_CRYPTODEV_NAME_MAX_LEN) != 0) {
+		MB_LOG_ERR("failed to create unique cryptodev name");
+		return -EINVAL;
+	}
+
+
+	dev = rte_cryptodev_pmd_virtual_dev_init(crypto_dev_name,
+			sizeof(struct aesni_mb_private), socket_id);
+	if (dev == NULL) {
+		MB_LOG_ERR("failed to create cryptodev vdev");
+		goto init_error;
+	}
+
+	dev->dev_type = RTE_CRYPTODEV_AESNI_MB_PMD;
+	dev->dev_ops = rte_aesni_mb_pmd_ops;
+
+	/* register rx/tx burst functions for data path */
+	dev->dequeue_burst = aesni_mb_pmd_dequeue_burst;
+	dev->enqueue_burst = aesni_mb_pmd_enqueue_burst;
+
+	/* Set vector instructions mode supported */
+	internals = dev->data->dev_private;
+
+	internals->vector_mode = vector_mode;
+	internals->max_nb_qpairs = AESNI_MB_MAX_NB_QUEUE_PAIRS;
+
+	return dev->data->dev_id;
+init_error:
+	MB_LOG_ERR("driver %s: cryptodev_aesni_create failed", name);
+
+	cryptodev_aesni_mb_uninit(crypto_dev_name);
+	return -EFAULT;
+}
+
+
+static int
+cryptodev_aesni_mb_init(const char *name,
+		const char *params __rte_unused)
+{
+	RTE_LOG(INFO, PMD, "Initialising %s\n", name);
+
+	return cryptodev_aesni_mb_create(name, rte_socket_id());
+}
+
+static int
+cryptodev_aesni_mb_uninit(const char *name)
+{
+	if (name == NULL)
+		return -EINVAL;
+
+	RTE_LOG(INFO, PMD, "Closing AESNI crypto device %s on numa socket %u\n",
+			name, rte_socket_id());
+
+	return 0;
+}
+
+static struct rte_driver cryptodev_aesni_mb_pmd_drv = {
+	.name = CRYPTODEV_NAME_AESNI_MB_PMD,
+	.type = PMD_VDEV,
+	.init = cryptodev_aesni_mb_init,
+	.uninit = cryptodev_aesni_mb_uninit
+};
+
+PMD_REGISTER_DRIVER(cryptodev_aesni_mb_pmd_drv);
diff --git a/drivers/crypto/aesni_mb/rte_aesni_mb_pmd_ops.c b/drivers/crypto/aesni_mb/rte_aesni_mb_pmd_ops.c
new file mode 100644
index 0000000..fb57e7b
--- /dev/null
+++ b/drivers/crypto/aesni_mb/rte_aesni_mb_pmd_ops.c
@@ -0,0 +1,346 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. 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 Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "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 THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS 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.
+ */
+
+#include <rte_common.h>
+#include <rte_cryptodev.h>
+
+#include "rte_aesni_mb_pmd_private.h"
+
+/** Configure device */
+static int
+aesni_mb_pmd_config(__rte_unused struct rte_cryptodev *dev)
+{
+	return 0;
+}
+
+/** Start device */
+static int
+aesni_mb_pmd_start(__rte_unused struct rte_cryptodev *dev)
+{
+	return -ENOTSUP;
+}
+
+/** Stop device */
+static void
+aesni_mb_pmd_stop(__rte_unused struct rte_cryptodev *dev)
+{
+}
+
+/** Close device */
+static void
+aesni_mb_pmd_close(__rte_unused struct rte_cryptodev *dev)
+{
+}
+
+
+/** Get device statistics */
+static void
+aesni_mb_pmd_stats_get(struct rte_cryptodev *dev,
+		struct rte_cryptodev_stats *stats)
+{
+	int qp_id;
+
+	for (qp_id = 0; qp_id < dev->data->nb_queue_pairs; qp_id++) {
+		struct aesni_mb_qp *qp = dev->data->queue_pairs[qp_id];
+
+		stats->enqueued_count += qp->qp_stats.enqueued_count;
+		stats->dequeued_count += qp->qp_stats.dequeued_count;
+
+		stats->enqueue_err_count += qp->qp_stats.enqueue_err_count;
+		stats->dequeue_err_count += qp->qp_stats.dequeue_err_count;
+	}
+}
+
+/** Reset device statistics */
+static void
+aesni_mb_pmd_stats_reset(struct rte_cryptodev *dev)
+{
+	int qp_id;
+
+	for (qp_id = 0; qp_id < dev->data->nb_queue_pairs; qp_id++) {
+		struct aesni_mb_qp *qp = dev->data->queue_pairs[qp_id];
+
+		memset(&qp->qp_stats, 0, sizeof(qp->qp_stats));
+	}
+}
+
+
+/** Get device info */
+static void
+aesni_mb_pmd_info_get(struct rte_cryptodev *dev,
+		struct rte_cryptodev_info *dev_info)
+{
+	struct aesni_mb_private *internals = dev->data->dev_private;
+
+	if (dev_info != NULL) {
+		dev_info->dev_type = dev->dev_type;
+		dev_info->max_queue_pairs = internals->max_nb_qpairs;
+	}
+}
+
+/** Release queue pair */
+static void
+aesni_mb_pmd_qp_release(struct rte_cryptodev *dev, uint16_t qp_id)
+{
+	if (dev->data->queue_pairs[qp_id] != NULL) {
+		rte_free(dev->data->queue_pairs[qp_id]);
+		dev->data->queue_pairs[qp_id] = NULL;
+	}
+}
+
+/** set a unique name for the queue pair based on it's name, dev_id and qp_id */
+static int
+aesni_mb_pmd_qp_set_unique_name(struct rte_cryptodev *dev,
+		struct aesni_mb_qp *qp)
+{
+	unsigned n = snprintf(qp->name, sizeof(qp->name),
+			"aesni_mb_pmd_%u_qp_%u",
+			dev->data->dev_id, qp->id);
+
+	if (n > sizeof(qp->name))
+		return -1;
+
+	return 0;
+}
+
+/** Create a ring to place process packets on */
+static struct rte_ring *
+aesni_mb_pmd_qp_create_processed_pkts_ring(struct aesni_mb_qp *qp,
+		unsigned ring_size, int socket_id)
+{
+	struct rte_ring *r;
+
+	r = rte_ring_lookup(qp->name);
+	if (r) {
+		if (r->prod.size >= ring_size) {
+			MB_LOG_INFO("Reusing existing ring %s for processed packets", qp->name);
+			return r;
+		} else {
+			MB_LOG_ERR("Unable to reuse existing ring %s for processed packets", qp->name);
+			return NULL;
+		}
+	}
+
+	return rte_ring_create(qp->name, ring_size, socket_id,
+			RING_F_SP_ENQ | RING_F_SC_DEQ);
+}
+
+/** Setup a queue pair */
+static int
+aesni_mb_pmd_qp_setup(struct rte_cryptodev *dev, uint16_t qp_id,
+		const struct rte_cryptodev_qp_conf *qp_conf,
+		 int socket_id)
+{
+	struct aesni_mb_qp *qp = NULL;
+	struct aesni_mb_private *internals = dev->data->dev_private;
+
+	/* Free memory prior to re-allocation if needed. */
+	if (dev->data->queue_pairs[qp_id] != NULL)
+		aesni_mb_pmd_qp_release(dev, qp_id);
+
+	/* Allocate the queue pair data structure. */
+	qp = rte_zmalloc_socket("AES-NI PMD Queue Pair", sizeof(*qp),
+					RTE_CACHE_LINE_SIZE, socket_id);
+	if (qp == NULL)
+		return (-ENOMEM);
+
+	qp->id = qp_id;
+	dev->data->queue_pairs[qp_id] = qp;
+
+	if (aesni_mb_pmd_qp_set_unique_name(dev, qp))
+		goto qp_setup_cleanup;
+
+	qp->mb_ops = &job_ops[internals->vector_mode];
+
+	qp->processed_pkts = aesni_mb_pmd_qp_create_processed_pkts_ring(qp,
+			qp_conf->nb_descriptors, socket_id);
+
+	if (qp->processed_pkts == NULL)
+		goto qp_setup_cleanup;
+
+	qp->sess_mp = internals->sess_mp;
+
+	memset(&qp->qp_stats, 0, sizeof(qp->qp_stats));
+
+	/* Initialise multi-buffer manager */
+	(*qp->mb_ops->job.init_mgr)(&qp->mb_mgr);
+
+	return 0;
+
+qp_setup_cleanup:
+	if (qp)
+		rte_free(qp);
+
+	return -1;
+}
+
+/** Start queue pair */
+static int
+aesni_mb_pmd_qp_start(__rte_unused struct rte_cryptodev *dev,
+		__rte_unused uint16_t queue_pair_id)
+{
+	return -ENOTSUP;
+}
+
+/** Stop queue pair */
+static int
+aesni_mb_pmd_qp_stop(__rte_unused struct rte_cryptodev *dev,
+		__rte_unused uint16_t queue_pair_id)
+{
+	return -ENOTSUP;
+}
+
+/** Return the number of allocated queue pairs */
+static uint32_t
+aesni_mb_pmd_qp_count(struct rte_cryptodev *dev)
+{
+	return dev->data->nb_queue_pairs;
+}
+
+
+static void
+session_obj_init(__rte_unused struct rte_mempool *mp,
+		__rte_unused void *user_arg, __rte_unused void *element,
+		__rte_unused unsigned element_idx)
+{
+}
+
+static int
+aesni_mb_pmd_session_mempool_create(struct rte_cryptodev *dev,
+		unsigned nb_objs, unsigned obj_cache_size, int socket_id)
+{
+	struct aesni_mb_private *internals = dev->data->dev_private;
+
+	unsigned n = snprintf(internals->sess_mp_name,
+			sizeof(internals->sess_mp_name), "mb_cdev_%d_sess_mp",
+			dev->data->dev_id);
+
+	if (n > sizeof(internals->sess_mp_name)){
+		MB_LOG_ERR("Unable to create unique name for session mempool");
+		return -ENOMEM;
+	}
+	internals->sess_mp = rte_mempool_lookup(internals->sess_mp_name);
+	if (internals->sess_mp != NULL) {
+		if (internals->sess_mp->elt_size != sizeof(struct aesni_mb_session) ||
+				internals->sess_mp->cache_size < obj_cache_size ||
+				internals->sess_mp->size < nb_objs) {
+
+			MB_LOG_ERR("%s mempool already exists with different "
+					"initialisation parameters",
+					internals->sess_mp_name);
+			internals->sess_mp = NULL;
+			return -ENOMEM;
+		}
+	} else {
+		internals->sess_mp = rte_mempool_create(
+				internals->sess_mp_name,	/* mempool name */
+				nb_objs,			/* number of elements*/
+				sizeof(struct aesni_mb_session),/* element size*/
+				obj_cache_size, 		/* Cache size*/
+				0,				/* private data size */
+				NULL,				/* obj initialisation constructor */
+				NULL,				/* obj initialisation constructor argument */
+				session_obj_init,		/* obj constructor */
+				NULL,				/* obj constructor argument */
+				socket_id,			/* socket id */
+				0);				/* flags */
+
+		if (internals->sess_mp == NULL) {
+			MB_LOG_ERR("%s mempool allocation failed",
+					internals->sess_mp_name);
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+static struct rte_cryptodev_session *
+aesni_mb_pmd_create_session(struct rte_cryptodev *dev,
+		struct rte_crypto_cipher_params *cipher_setup_data,
+		struct rte_crypto_hash_params *hash_setup_data,
+		enum rte_crypto_operation_chain op_chain)
+{
+	struct aesni_mb_private *internals = dev->data->dev_private;
+	struct aesni_mb_session *sess  =
+			aesni_mb_get_session(internals->sess_mp);
+
+	if (unlikely(sess == NULL)) {
+		MB_LOG_ERR("failed to get session from mempool");
+		return NULL;
+	}
+
+	if (aesni_mb_set_session_parameters(
+			&job_ops[internals->vector_mode], sess,
+			cipher_setup_data, hash_setup_data, op_chain) != 0) {
+		aesni_mb_free_session(internals->sess_mp, sess);
+	}
+
+	return (struct rte_cryptodev_session *)sess;
+}
+
+static void
+aesni_mb_pmd_destroy_session(struct rte_cryptodev *dev,
+		struct rte_cryptodev_session *sess)
+{
+	struct aesni_mb_private *internals = dev->data->dev_private;
+
+	if (sess)
+		aesni_mb_free_session(internals->sess_mp,
+				(struct aesni_mb_session *)sess);
+}
+
+struct rte_cryptodev_ops aesni_mb_pmd_ops = {
+		.dev_configure		= aesni_mb_pmd_config,
+		.dev_start		= aesni_mb_pmd_start,
+		.dev_stop		= aesni_mb_pmd_stop,
+		.dev_close		= aesni_mb_pmd_close,
+
+		.stats_get		= aesni_mb_pmd_stats_get,
+		.stats_reset		= aesni_mb_pmd_stats_reset,
+
+		.dev_infos_get		= aesni_mb_pmd_info_get,
+
+		.queue_pair_setup	= aesni_mb_pmd_qp_setup,
+		.queue_pair_release	= aesni_mb_pmd_qp_release,
+		.queue_pair_start	= aesni_mb_pmd_qp_start,
+		.queue_pair_stop	= aesni_mb_pmd_qp_stop,
+		.queue_pair_count	= aesni_mb_pmd_qp_count,
+
+		.session_mp_create	= aesni_mb_pmd_session_mempool_create,
+
+		.session_create		= aesni_mb_pmd_create_session,
+		.session_destroy	= aesni_mb_pmd_destroy_session
+};
+
+struct rte_cryptodev_ops *rte_aesni_mb_pmd_ops = &aesni_mb_pmd_ops;
+
diff --git a/drivers/crypto/aesni_mb/rte_aesni_mb_pmd_private.h b/drivers/crypto/aesni_mb/rte_aesni_mb_pmd_private.h
new file mode 100644
index 0000000..c5c4a86
--- /dev/null
+++ b/drivers/crypto/aesni_mb/rte_aesni_mb_pmd_private.h
@@ -0,0 +1,224 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2015 Intel Corporation. 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 Intel Corporation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *   "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 THE COPYRIGHT
+ *   OWNER OR CONTRIBUTORS 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.
+ */
+
+#ifndef _RTE_AESNI_MB_PMD_PRIVATE_H_
+#define _RTE_AESNI_MB_PMD_PRIVATE_H_
+
+#include "aesni_mb_ops.h"
+
+#define MB_LOG_ERR(fmt, args...) do { \
+	RTE_LOG(ERR, CRYPTODEV, "[%s] %s() line %u: " fmt "\n",  \
+			CRYPTODEV_NAME_AESNI_MB_PMD, \
+			__func__, __LINE__, ## args); \
+	} while (0)
+
+#ifdef RTE_LIBRTE_AESNI_MB_DEBUG
+#define MB_LOG_INFO(fmt, args...) do { \
+	RTE_LOG(INFO, CRYPTODEV, "[%s] %s() line %u: " fmt "\n", \
+			CRYPTODEV_NAME_AESNI_MB_PMD, \
+			__func__, __LINE__, ## args); \
+	} while (0)
+
+#define MB_LOG_DBG(fmt, args...) do { \
+	RTE_LOG(DEBUG, CRYPTODEV, "[%s] %s() line %u: " fmt "\n", \
+			CRYPTODEV_NAME_AESNI_MB_PMD, \
+			__func__, __LINE__, ## args); \
+	} while (0)
+#else
+#define MB_LOG_INFO(fmt, args...)
+#define MB_LOG_DBG(fmt, args...)
+#endif
+
+#define AESNI_MB_NAME_MAX_LENGTH	(64)
+#define AESNI_MB_MAX_NB_QUEUE_PAIRS	(4)
+
+#define HMAC_IPAD_VALUE			(0x36)
+#define HMAC_OPAD_VALUE			(0x5C)
+
+static const unsigned auth_blocksize[] = {
+		[MD5]		= 64,
+		[SHA1]		= 64,
+		[SHA_224]	= 64,
+		[SHA_256]	= 64,
+		[SHA_384]	= 128,
+		[SHA_512]	= 128,
+		[AES_XCBC]	= 16,
+};
+
+/**
+ * Get the blocksize in bytes for a specified authentication algorithm
+ *
+ * @Note: this function will not return a valid value for a non-valid
+ * authentication algorithm
+ */
+static inline unsigned
+get_auth_algo_blocksize(JOB_HASH_ALG algo)
+{
+	return auth_blocksize[algo];
+}
+
+static const unsigned auth_truncated_digest_byte_lengths[] = {
+		[MD5]		= 12,
+		[SHA1]		= 12,
+		[SHA_224]	= 14,
+		[SHA_256]	= 16,
+		[SHA_384]	= 24,
+		[SHA_512]	= 32,
+		[AES_XCBC]	= 12,
+};
+
+/**
+ * Get the IPsec specified truncated length in bytes of the HMAC digest for a
+ * specified authentication algorithm
+ *
+ * @Note: this function will not return a valid value for a non-valid
+ * authentication algorithm
+ */
+static inline unsigned
+get_truncated_digest_byte_length(JOB_HASH_ALG algo)
+{
+	return auth_truncated_digest_byte_lengths[algo];
+}
+
+static const unsigned auth_digest_byte_lengths[] = {
+		[MD5]		= 16,
+		[SHA1]		= 20,
+		[SHA_224]	= 28,
+		[SHA_256]	= 32,
+		[SHA_384]	= 48,
+		[SHA_512]	= 64,
+		[AES_XCBC]	= 16,
+};
+
+/**
+ * Get the output digest size in bytes for a specified authentication algorithm
+ *
+ * @Note: this function will not return a valid value for a non-valid
+ * authentication algorithm
+ */
+static inline unsigned
+get_digest_byte_length(JOB_HASH_ALG algo)
+{
+	return auth_digest_byte_lengths[algo];
+}
+
+
+/** private data structure for each virtual AESNI device */
+struct aesni_mb_private {
+	enum aesni_mb_vector_mode vector_mode;
+
+	char sess_mp_name[AESNI_MB_NAME_MAX_LENGTH];
+	struct rte_mempool *sess_mp;
+
+	unsigned max_nb_qpairs;
+};
+
+struct aesni_mb_qp {
+	uint16_t id;				/**< Queue Pair Identifier */
+	char name[AESNI_MB_NAME_MAX_LENGTH];	/**< Unique Queue Pair Name */
+	const struct aesni_mb_ops *mb_ops;	/**<
+						 * Architecture dependent function
+						 * pointer table of the multi-buffer
+						 * APIs */
+	MB_MGR mb_mgr;				/**< Multi-buffer instance */
+	struct rte_ring *processed_pkts;	/**< Ring for placing process packets */
+	struct rte_mempool *sess_mp;		/**< Crypto Session mempool */
+
+	struct rte_cryptodev_stats qp_stats;	/**< Queue pair statistics */
+} __rte_cache_aligned;
+
+
+/** AES-NI Multi buffer session */
+struct aesni_mb_session {
+	JOB_CHAIN_ORDER chain_order;
+
+	struct {
+		JOB_CIPHER_DIRECTION direction;	/**< Cipher direction - encrypt / decrypt */
+		JOB_CIPHER_MODE mode;		/**< Cipher mode - CBC / Counter */
+
+		uint64_t key_length_in_bytes;
+
+		struct {
+			uint32_t encode[256] __rte_align(16);	/**< encode key */
+			uint32_t decode[256] __rte_align(16);	/**< decode key */
+		} expanded_aes_keys;
+		/**< Expanded AES keys - Allocating space to contain the
+		 * maximum expanded key size which is 240 bytes for 256 bit
+		 * AES, calculate by: ((key size (bytes)) * ((number of rounds) + 1)) */
+	} cipher;	/**< Cipher Parameters */
+
+	struct {
+		JOB_HASH_ALG algo;	/** Authentication Algorithm */
+
+		struct {
+			uint8_t inner[128] __rte_align(16);	/**< inner pad */
+			uint8_t outer[128] __rte_align(16);	/**< outer pad */
+		} pads;
+		/** HMAC Authentication pads - allocating space for the maximum
+		 * pad size supported which is 128 bytes for SHA512 */
+
+		uint8_t digest[64] __rte_align(16);
+	} auth;	/**< Authentication Parameters */
+} __rte_cache_aligned;
+
+
+static inline struct aesni_mb_session *
+aesni_mb_get_session(struct rte_mempool *mempool)
+{
+	struct aesni_mb_session *sess;
+
+	if (rte_mempool_get(mempool, (void **)&sess)) {
+		return NULL;
+	}
+	return sess;
+}
+
+static inline void
+aesni_mb_free_session(struct rte_mempool *mempool,
+		struct aesni_mb_session *sess)
+{
+	rte_mempool_put(mempool, (void *)sess);
+}
+
+extern int
+aesni_mb_set_session_parameters(const struct aesni_mb_ops *mb_ops,
+		struct aesni_mb_session *sess,
+		struct rte_crypto_cipher_params *cparams,
+		struct rte_crypto_hash_params *aparams,
+		enum rte_crypto_operation_chain op_chain);
+
+extern struct rte_cryptodev_ops *rte_aesni_mb_pmd_ops;
+
+
+
+#endif /* _RTE_AESNI_MB_PMD_PRIVATE_H_ */
+
diff --git a/drivers/crypto/aesni_mb/rte_pmd_aesni_version.map b/drivers/crypto/aesni_mb/rte_pmd_aesni_version.map
new file mode 100644
index 0000000..39cc84f
--- /dev/null
+++ b/drivers/crypto/aesni_mb/rte_pmd_aesni_version.map
@@ -0,0 +1,5 @@
+DPDK_2.2 {
+	global:
+
+	local: *;
+};
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 5502cc4..496cbeb 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -148,6 +148,10 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_NULL)       += -lrte_pmd_null
 # QAT PMD has a dependancy on libcrypto (from openssl) for calculating HMAC precomputes
 _LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_QAT)        += -lrte_pmd_qat -lcrypto
 
+# AESNI MULTI BUFFER is dependent on the IPSec_MB library
+_LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB)   += -lrte_pmd_aesni_mb
+_LDLIBS-$(CONFIG_RTE_LIBRTE_PMD_AESNI_MB)   += -L$(AESNI_MULTI_BUFFER_LIB_PATH) -lIPSec_MB
+
 endif # ! $(CONFIG_RTE_BUILD_SHARED_LIB)
 
 endif # ! CONFIG_RTE_BUILD_COMBINE_LIBS
