diff --git a/include/odp_crypto.h b/include/odp_crypto.h
new file mode 100644
index 0000000..725e8f1
--- /dev/null
+++ b/include/odp_crypto.h
@@ -0,0 +1,295 @@
+/* Copyright (c) 2013, Linaro Limited
Nit: date 2014
Should be like this for all the files.

[RK] good catch.
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+
+/**
+ * @file
+ *
+ * ODP crypto
+ */
+
+#ifndef ODP_CRYPTO_H_
+#define ODP_CRYPTO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp_std_types.h>
+#include <odp_buffer.h>
+#include <odp_buffer_pool.h>
+#include <odp_queue.h>
+#include <odp_packet.h>
+
+/** Invalid session handle */
+#define ODP_CRYPTO_SESSION_INVALID (-1ULL)
+
+/**
+ * Crypto API opaque session handle
+ */
+typedef uint64_t odp_crypto_session_t;
+
+/**
+ * Crypto API operation mode
+ */
+enum odp_crypto_op_mode {
+       ODP_CRYPTO_SYNC,    /**< Synchronous, return results immediately */
+       ODP_CRYPTO_ASYNC,   /**< Aynchronous, return results via posted event */
+};
+
+/**
+ * Crypto API operation type
+ */
+enum odp_crypto_op {
+       ODP_CRYPTO_OP_ENCODE, /**< Encrypt and/or compute authentication ICV */
+       ODP_CRYPTO_OP_DECODE  /**< Decrypt and/or verify authentication ICV */
+};
+
+/**
+ * Crypto API cipher algorithm
+ */
+enum  odp_cipher_alg {
+       ODP_CIPHER_ALG_NULL,     /**< No cipher algorithm specified */
+       ODP_CIPHER_ALG_DES,      /**< DES */
+       ODP_CIPHER_ALG_3DES_CBC, /**< Triple DES with cipher block chaining */
+};
+
+/**
+ * Crypto API authentication algorithm
+ */
+enum odp_auth_alg {
+       ODP_AUTH_ALG_NULL,   /**< No authentication algorithm specified */
+       ODP_AUTH_ALG_MD5_96, /**< HMAC-MD5 with 96 bit key */
+};
+
+/**
+ * Crypto API operation order
+ */
+enum odp_crypto_combination {
+       ODP_CRYPTO_CIPHER_ONLY,     /**< Only perform cipher */
+       ODP_CRYPTO_AUTH_ONLY,       /**< Only perform authentication */
+       ODP_CRYPTO_AUTH_CIPHERTEXT  /**< Cipher then authentication on encode */
+};
+
+/**
+ * Crypto API key
+ */
+typedef struct odp_key_s {
+       union  {
+               /** DES/3DES key definition (set all same for DES) */
+               struct {
+                       uint8_t k1[8]; /**< First key */
+                       uint8_t k2[8]; /**< Second key */
+                       uint8_t k3[8]; /**< Third key */
+               } des;
+               /** MD5 key */
+               struct {
+                       uint8_t key[16];  /**< Key up to 128 bits */
+               } md5;
+       };
+} odp_key_t;
+
+/**
+ * Crypto API data range specifier
+ */
+struct odp_data_range {
+       uint16_t offset;  /**< Offset from beginning of buffer (chain) */
+       uint16_t length;  /**< Length of data to operate on */
Why do we use uint16_t?
Can't the data be bigger than this?

[RK] this came from the ODP crypto spec, I think in general the idea
is packets will never be larger than an Ethernet jumbo (roughly 10K)
so no need to support larger than 64K.
So was that a reason for choosing uint16_t in the spec?
Or shall we change the spec?
+};
+
+/**
+ * Crypto API session creation paramters
+ *
+ * TODO: add "odp_session_proc_info_t"
+ */
+struct odp_crypto_session_params {
+       enum odp_crypto_op op;             /**< Encode versus decode */
+       enum odp_crypto_combination comb;  /**< Operation order */
+       enum odp_crypto_op_mode pref_mode; /**< Preferred sync vs async */
+       enum odp_cipher_alg cipher_alg;    /**< Cipher algorithm */
+       odp_key_t *cipher_key;             /**< Cipher key */
+       uint8_t *iv;                   /**< Cipher Initialization Vector (IV) */
Why do we use uint8_t and not uint_t?

[RK] This one also comes from the spec, my understanding is for crypto
the IV is always an opaque byte string that can be different lengths
for different ciphers.
OK, thanks.
+       size_t iv_len;                 /**< Cipher IV length */
+       enum odp_auth_alg auth_alg;    /**< Authentication algorithm */
+       odp_key_t *auth_key;           /**< Authentication key */
+       odp_queue_t compl_queue;       /**< Async mode completion event queue */
+};
+
+/**
+ * Crypto API per packet operation parameters
+ */
+struct odp_crypto_op_params {
+       odp_crypto_session_t session;   /**< Session handle from creation */
+       odp_packet_t pkt;               /**< Input packet buffer */
+       odp_packet_t out_pkt;           /**< Output packet buffer (optional) */
+       uint8_t *override_iv_ptr;       /**< Override session IV pointer */
+       unsigned hash_result_offset;    /**< Offset from start of packet buffer for hash result */
+       struct odp_data_range cipher_range;   /**< Data range to apply cipher */
+       struct odp_data_range auth_range;     /**< Data range to authenticate */
+};
+
+/**
+ * Crypto API session creation return code
+ *
+ * TODO: seems confusing, maybe _rc instead
+ */
+enum odp_crypto_ses_create_err {
+       ODP_CRYPTO_SES_CREATE_NONE,  /**< No session created? need to clarify */
+       ODP_CRYPTO_SES_CREATE_OK,    /**< Session created successfully */
+};
+
+/**
+ * Crypto API algorithm return code
+ */
+enum crypto_alg_err {
+       ODP_CRYPTO_ALG_ERR_NONE,      /**< Algorithm successful */
+       ODP_CRYPTO_ALG_ERR_DATA_SIZE, /**< Invalid data block size */
+       ODP_CRYPTO_ALG_ERR_KEY_SIZE,  /**< Key size invalid for algorithm */
+       ODP_CRYPTO_ALG_ERR_ICV_CHECK, /**< Computed ICV value mismatch */
+};
+
+/**
+ * Crypto API operation return code
+ */
+typedef enum odp_crypto_rc {
+       ODP_CRYPTO_OP_OK,      /**< Operation completed, results are valid */
+       ODP_CRYPTO_OP_POSTED,  /**< Operation was posted, results delivered via completion queue */
+       ODP_CRYPTO_OP_ERROR,   /**< Operation failed */
+} odp_crypto_rc_e;
+
+/**
+ * Crypto API hardware centric return code
+ */
+enum crypto_hw_err {
+       ODP_CRYPTO_HW_ERR_NONE,         /**< Operation completed successfully */
+       ODP_CRYPTO_HW_ERR_DMA,          /**< Error detected during DMA of data */
+       ODP_CRYPTO_HW_ERR_BP_DEPLETED,  /**< Operation failed due to buffer pool depletion */
+};
+
+/**
+ * Crypto API algorithm (cipher or authentication)
+ */
+typedef union odp_crypto_alg_u {
+       enum odp_cipher_alg cipher;   /**< Cipher algorithm */
+       enum odp_auth_alg   auth;     /**< Authentication algorithm */
+} odp_crypto_alg_t;
+
+/**
+ * Cryto API per packet operation completion status
+ */
+struct odp_crypto_compl_status {
+       odp_crypto_alg_t    alg;      /**< Requested algorithm */
+       enum crypto_alg_err alg_err;  /**< Algorithm specific return code */
+       enum crypto_hw_err  hw_err;   /**< Hardware specific return code */
+};
+
Is there a good reason why we are mixing enum name with typedef?

[RK] I agree, I would like to see us perhaps agree on typedef'ing
enums.  This one is a little kludgy anyway, I would vote for removing
the ALG from the completion status since it is simply a mirror of the
ALG (cipher or auth) which was specified during session creation.
I agree with you.
+
+/**
+ * Crypto session creation
+ *
+ * Create a crypto session.  Operation occurs asynchronously if a completion
+ * queue is specified else synchronously.
+ *
+ * @param params            Session parameters
+ * @param completion_event  Event by which the session creation results are
+ *                          delivered.
+ * @param completion_queue  Queue by which the completion event will be
+ *                          delivered.  Ignored if ODP_QUEUE_INVALID.
+ *
+ * @return Operation return code indicating success or failure for
+ *         when synchronous operation requested, else POSTED when
+ *         asynchronous operation is requested.
+ */
+odp_crypto_rc_e
+odp_crypto_session_create(struct odp_crypto_session_params *params,
+                         odp_buffer_t completion_event,
+                         odp_queue_t completion_queue);
+
+
+/**
+ * Crypto session creation completion status
+ *
+ * Accessor function for obtaining creation status from the completion event.
+ *
+ * @param completion_event  Event containing operation results
+ * @param status            Pointer to store creation return code
+ */
+void
+odp_crypto_get_ses_create_compl_status(odp_buffer_t completion_event,
+                                      enum odp_crypto_ses_create_err *status);
+
+/**
+ * Crypto session creation completion return value
+ *
+ * Accessor function for obtaining handle for newly created session.
+ *
+ * @param completion_event  Event containing operation results
+ * @param session           Pointer to store session handle
+ */
+void
+odp_crypto_get_ses_create_compl_session(odp_buffer_t completion_event,
+                                       odp_crypto_session_t *session);
+
+/**
+ * Crypto per packet operation
+ *
+ * Performs the cryptographic operations specified during session creation
+ * on the packet.
+ *
+ * @param params            Operation parameters
+ * @param completion_event  Event by which the session creation results are
+ *                          delivered.
+ *
+ * @return Operation return code indicating success or failure when session
+ *         indicates synchronous operation, else POSTED for asynchronous
+ *         operation.
+ */
+odp_crypto_rc_e
+odp_crypto_operation(struct odp_crypto_op_params *params,
+                    odp_buffer_t completion_event);
+
+
+/**
+ * Crypto per packet operation completion status
+ *
+ * Accessor function for obtaining operation status from the completion event.
+ *
+ * @param completion_event  Event containing operation results
+ * @param auth              Pointer to store authentication results
+ * @param cipher            Pointer to store cipher results
+ */
+void
+odp_crypto_get_operation_compl_status(odp_buffer_t completion_event,
+                                     struct odp_crypto_compl_status *auth,
+                                     struct odp_crypto_compl_status *cipher);
+
+/**
+ * Generate random byte string
+ *
+ * @param buf          Pointer to store result
+ * @param len          Pointer to input length value as well as return value
+ * @param use_entropy  (TODO: needs description)
+ *
+ * @return 0 if succesful
+ */
+int
+odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy);
+
+/**
+ * Initialize the crypto subsystem, called once from main thread
+ *
+ * @param max_sessions  Maximum number of sessions to support
+ *
+ * @return 0 if succesful
+ */
+int
+odp_crypto_init(uint32_t max_sessions);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/Makefile b/platform/linux-generic/Makefile
index ec5d4a7..57f6d18 100644
--- a/platform/linux-generic/Makefile
+++ b/platform/linux-generic/Makefile
@@ -72,6 +72,7 @@ OBJS    += $(OBJ_DIR)/odp_time.o
  OBJS    += $(OBJ_DIR)/odp_timer.o
  OBJS    += $(OBJ_DIR)/odp_ring.o
  OBJS    += $(OBJ_DIR)/odp_rwlock.o
+OBJS    += $(OBJ_DIR)/odp_crypto.o
  ifeq ($(ODP_HAVE_NETMAP),yes)
  OBJS    += $(OBJ_DIR)/odp_packet_netmap.o
  endif
diff --git a/platform/linux-generic/include/odp_crypto_internal.h b/platform/linux-generic/include/odp_crypto_internal.h
new file mode 100644
index 0000000..e558864
--- /dev/null
+++ b/platform/linux-generic/include/odp_crypto_internal.h
@@ -0,0 +1,86 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+
+
+#ifndef ODP_CRYPTO_INTERNAL_H_
+#define ODP_CRYPTO_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <openssl/des.h>
+
+#define OP_RESULT_MAGIC 0x91919191
+
+/** Forward declaration of session structure */
+struct odp_crypto_session_s;
+
+/**
+ * Algorithm handler function prototype
+ */
+typedef
+enum crypto_alg_err (*crypto_func_t)(struct odp_crypto_op_params *params,
+                                    struct odp_crypto_session_s *session);
+
+/**
+ * Per crypto session data structure
+ */
+struct odp_crypto_session_s {
+       uint32_t index;
Why not uint_t?

[RK] OK by me, since this data structure is internal to the
linux-generic implementation, and the plan is to support a
very small number of sessions this could be made as small as
uint8_t I think.
OK, I'm fine with that.
+       enum odp_crypto_op op;
+       enum odp_crypto_combination comb;
+       odp_queue_t compl_queue;
+       struct {
+               enum odp_cipher_alg   alg;
+               struct {
+                       uint8_t *data;
Why not uint_t?

[RK] Same reason as above for IV since it is byte data.
OK.
+                       size_t   len;
+               } iv;
+               union {
+                       struct {
+                               DES_key_schedule ks1;
+                               DES_key_schedule ks2;
+                               DES_key_schedule ks3;
+                       } des;
+               } data;
Is there a good reason to have a union with only one member?

[RK] The union is here with the intention that the linux-generic
implementation will add support for other ciphers (AES for example).
So the union is simply laying the ground work for expansion.
I understand, didn't think about that!
Thank you for your explanation.
+               crypto_func_t func;
+       } cipher;
+       struct {
+               enum odp_auth_alg  alg;
+               union {
+                       struct {
+                               uint8_t key[16];
Why are we limiting the key to 16?
I guess we want to limit the key up to 128 bits right?

[RK] 128 bit key is specific to MD5, other algorithems
would have their own entry in the union (again for future
expansion).
OK good.
Maybe comment that it is for keys up to 128 bits?
+                       } md5;
+               } data;
Is there a good reason to have a union with only one member?
+               crypto_func_t func;
+       } auth;
+
+};
+
+/**
+ * Per packet operation result
+ */
+struct odp_operation_result_s {
+       uint32_t magic;
+       struct odp_crypto_compl_status cipher;
+       struct odp_crypto_compl_status auth;
+};
+
+/**
+ * Per session creation operation result
+ */
+struct odp_session_result_s {
+       enum odp_crypto_ses_create_err rc;
+       odp_crypto_session_t           session;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/source/odp_crypto.c b/platform/linux-generic/source/odp_crypto.c
new file mode 100644
index 0000000..4ae3645
--- /dev/null
+++ b/platform/linux-generic/source/odp_crypto.c
@@ -0,0 +1,435 @@
+/* Copyright (c) 2013, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <odp_crypto.h>
+#include <odp_internal.h>
+#include <odp_atomic.h>
+#include <odp_spinlock.h>
+#include <odp_sync.h>
+#include <odp_debug.h>
+#include <odp_align.h>
+#include <odp_shared_memory.h>
+#include <odp_crypto_internal.h>
+
+#include <string.h>
+
+#include <openssl/des.h>
+#include <openssl/rand.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+
+#define MAX_SESSIONS 32
+
+typedef struct {
+       odp_atomic_u32_t next;
+       uint32_t         max;
+       struct odp_crypto_session_s sessions[0];
Pointer instead?

[RK] This is specifically for allocating memory where
the storage for sessions immediately follows the
structure, allowing an arbitrary number of sessions to
be supported at allocation time like shown yet accessed
as if they were statically allocated:

        /* Calculate the memory size we need */
        mem_size  = sizeof(*global);
        mem_size += (max_sessions * sizeof(struct odp_crypto_session_s));

and you can access individual sessions like this:

        session = &global->sessions[idx];

I've used this a lot over the years, but if it gives folks heartburn
I can allocate memory in multiple stages, etc.
I'm fine with either way as long as we do the same, what does others
think about this?
Cheers,
Anders
+} odp_crypto_global_t;
+
+static odp_crypto_global_t *global;
+
+/*
+ * TODO: This is a serious hack to allow us to use packet buffer to convey
+ *       crypto operation results by placing them at the very end of the
+ *       packet buffer.
+ */
+static
+struct odp_operation_result_s *get_op_result_from_buffer(odp_buffer_t buf)
+{
+       uint8_t   *temp;
+       struct odp_operation_result_s *result;
+
+       temp  = odp_buffer_addr(buf);
+       temp += odp_buffer_size(buf);
+       temp -= sizeof(*result);
+       result = (struct odp_operation_result_s *)(void *)temp;
+       return result;
+}
+
+static
+struct odp_crypto_session_s *alloc_session(void)
+{
+       uint32_t idx;
+       struct odp_crypto_session_s *session = NULL;
+
+       idx = odp_atomic_fetch_inc_u32(&global->next);
+       if (idx < global->max) {
+               session = &global->sessions[idx];
+               session->index = idx;
+       }
+       return session;
+}
+
+static
+enum crypto_alg_err null_crypto_routine(
+       struct odp_crypto_op_params *params ODP_UNUSED,
+       struct odp_crypto_session_s *session ODP_UNUSED)
+{
+       return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+enum crypto_alg_err md5_gen(struct odp_crypto_op_params *params,
+                           struct odp_crypto_session_s *session)
+{
+       uint8_t *data  = odp_packet_buf_addr(params->pkt);
+       uint8_t *icv   = data;
+       uint32_t len   = params->auth_range.length;
+       uint8_t  hash[EVP_MAX_MD_SIZE];
+       uint32_t hlen = 12;
+
+       /* Adjust pointer for beginning of area to auth */
+       data += params->auth_range.offset;
+       icv  += params->hash_result_offset;
+
+       /* Hash it */
+       HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, hash, &hlen);
+
+       /* Copy to the output location */
+       memcpy(icv, hash, 12);
+
+       return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+
+static
+enum crypto_alg_err md5_check(struct odp_crypto_op_params *params,
+                             struct odp_crypto_session_s *session)
+{
+       uint8_t *data  = odp_packet_buf_addr(params->pkt);
+       uint8_t *icv   = data;
+       uint32_t len   = params->auth_range.length;
+       uint8_t  hash[EVP_MAX_MD_SIZE];
+       uint32_t hlen = 12;
+
+       /* Adjust pointer for beginning of area to auth */
+       data += params->auth_range.offset;
+       icv  += params->hash_result_offset;
+
+       /* Copy current value out and clear it before authentication */
+       memcpy(hash, icv, hlen);
+       memset(icv, 0, hlen);
+
+       /* Hash it */
+       HMAC(EVP_md5(), session->auth.data.md5.key, 16, data, len, icv, &hlen);
+
+       /* Verify match */
+       if (0 != memcmp(icv, hash, 12))
+               return ODP_CRYPTO_ALG_ERR_ICV_CHECK;
+
+       /* Matched */
+       return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+enum crypto_alg_err des_encrypt(struct odp_crypto_op_params *params,
+                               struct odp_crypto_session_s *session)
+{
+       uint8_t *data  = odp_packet_buf_addr(params->pkt);
+       uint32_t len   = params->cipher_range.length;
+       DES_cblock *iv = (DES_cblock *)session->cipher.iv.data;
+
+       /* Adjust pointer for beginning of area to cipher */
+       data += params->cipher_range.offset;
+
+       /* Override IV if requested */
+       if (params->override_iv_ptr)
+               iv = (DES_cblock *)params->override_iv_ptr;
+
+       /* Encrypt it */
+       DES_ede3_cbc_encrypt(data,
+                            data,
+                            len,
+                            &session->cipher.data.des.ks1,
+                            &session->cipher.data.des.ks2,
+                            &session->cipher.data.des.ks3,
+                            iv,
+                            1);
+
+       return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+enum crypto_alg_err des_decrypt(struct odp_crypto_op_params *params,
+                               struct odp_crypto_session_s *session)
+{
+       uint8_t *data  = odp_packet_buf_addr(params->pkt);
+       uint32_t len   = params->cipher_range.length;
+       DES_cblock *iv = (DES_cblock *)session->cipher.iv.data;
+
+       /* Adjust pointer for beginning of area to cipher */
+       data += params->cipher_range.offset;
+
+       /* Override IV if requested */
+       if (params->override_iv_ptr)
+               iv = (DES_cblock *)params->override_iv_ptr;
+
+       /* Decrypt it */
+       DES_ede3_cbc_encrypt(data,
+                            data,
+                            len,
+                            &session->cipher.data.des.ks1,
+                            &session->cipher.data.des.ks2,
+                            &session->cipher.data.des.ks3,
+                            iv,
+                            0);
+
+       return ODP_CRYPTO_ALG_ERR_NONE;
+}
+
+static
+int process_des_params(struct odp_crypto_session_s *session,
+                      struct odp_crypto_session_params *params)
+{
+       /* Verify IV len is either 0 or 8 */
+       if (!((0 == params->iv_len) || (8 == params->iv_len)))
+               return -1;
+
+       /* Verify IV pointer */
+       if (params->iv_len && !params->iv)
+               return -1;
+
+       /* Set function */
+       if (ODP_CRYPTO_OP_ENCODE == params->op)
+               session->cipher.func = des_encrypt;
+       else
+               session->cipher.func = des_decrypt;
+
+       /* Convert keys */
+       DES_set_key(&params->cipher_key->des.k1, &session->cipher.data.des.ks1);
+       DES_set_key(&params->cipher_key->des.k2, &session->cipher.data.des.ks2);
+       DES_set_key(&params->cipher_key->des.k3, &session->cipher.data.des.ks3);
+
+       return 0;
+}
+
+static
+int process_md5_params(struct odp_crypto_session_s *session,
+                      struct odp_crypto_session_params *params)
+{
+       /* Set function */
+       if (ODP_CRYPTO_OP_ENCODE == params->op)
+               session->auth.func = md5_gen;
+       else
+               session->auth.func = md5_check;
+
+       /* Convert keys */
+       memcpy(session->auth.data.md5.key, params->auth_key->md5.key, 16);
+
+       return 0;
+}
+
+odp_crypto_rc_e
+odp_crypto_session_create(struct odp_crypto_session_params *params,
+                         odp_buffer_t completion_event,
+                         odp_queue_t completion_queue)
+{
+       int rc;
+       struct odp_crypto_session_s *session;
+       struct odp_session_result_s *result = odp_buffer_addr(completion_event);
+
+       /* Default to failure result */
+       result->rc = ODP_CRYPTO_SES_CREATE_NONE;
+       result->session = ODP_CRYPTO_SESSION_INVALID;
+
+       /* Allocate memory for this session */
+       session = alloc_session();
+       if (NULL == session)
+               return ODP_CRYPTO_OP_ERROR;
+
+       /* Copy stuff over */
+       session->op          = params->op;
+       session->comb        = params->comb;
+       session->compl_queue = params->compl_queue;
+       session->cipher.alg  = params->cipher_alg;
+       session->cipher.iv.data = params->iv;
+       session->cipher.iv.len  = params->iv_len;
+       session->auth.alg  = params->auth_alg;
+
+       /* Process based on cipher */
+       switch (params->cipher_alg) {
+       case ODP_CIPHER_ALG_NULL:
+               session->cipher.func = null_crypto_routine;
+               rc = 0;
+               break;
+       case ODP_CIPHER_ALG_DES:
+       case ODP_CIPHER_ALG_3DES_CBC:
+               rc = process_des_params(session, params);
+               break;
+       default:
+               rc = -1;
+       }
+
+       /* Check result */
+       if (rc)
+               return ODP_CRYPTO_OP_ERROR;
+
+       /* Process based on auth */
+       switch (params->auth_alg) {
+       case ODP_AUTH_ALG_NULL:
+               session->auth.func = null_crypto_routine;
+               rc = 0;
+               break;
+       case ODP_AUTH_ALG_MD5_96:
+               rc = process_md5_params(session, params);
+               break;
+       default:
+               rc = -1;
+       }
+
+       /* Check result */
+       if (rc)
+               return ODP_CRYPTO_OP_ERROR;
+
+       /* We're happy */
+       result->rc = ODP_CRYPTO_SES_CREATE_OK;
+       result->session = (intptr_t)session;
+
+       /* If there is a queue post else we're good */
+       if (ODP_QUEUE_INVALID != completion_queue) {
+               odp_queue_enq(completion_queue, completion_event);
+               return ODP_CRYPTO_OP_POSTED;
+       }
+
+       return ODP_CRYPTO_OP_OK;
+}
+
+
+odp_crypto_rc_e
+odp_crypto_operation(struct odp_crypto_op_params *params,
+                    odp_buffer_t completion_event)
+{
+       enum crypto_alg_err rc_cipher = ODP_CRYPTO_ALG_ERR_NONE;
+       enum crypto_alg_err rc_auth = ODP_CRYPTO_ALG_ERR_NONE;
+       struct odp_crypto_session_s *session;
+       struct odp_operation_result_s *result;
+
+       session = (struct odp_crypto_session_s *)(intptr_t)params->session;
+
+       /*
+        * robking: need to understand infrastructure for scattered packets
+        *          for now just don't support them
+        */
+       if (odp_buffer_is_scatter(odp_buffer_from_packet(params->pkt)))
+               return ODP_CRYPTO_OP_ERROR;
+
+       /*
+        * robking: for now we are only going to support in place
+        */
+       if (params->pkt != params->out_pkt)
+               return ODP_CRYPTO_OP_ERROR;
+
+       /* Invoke the functions */
+       switch (session->comb) {
+       case ODP_CRYPTO_CIPHER_ONLY:
+               rc_cipher = session->cipher.func(params, session);
+               break;
+       case ODP_CRYPTO_AUTH_ONLY:
+               rc_auth = session->auth.func(params, session);
+               break;
+       case ODP_CRYPTO_AUTH_CIPHERTEXT:
+               if (ODP_CRYPTO_OP_ENCODE == session->op) {
+                       rc_cipher = session->cipher.func(params, session);
+                       rc_auth = session->auth.func(params, session);
+               } else {
+                       rc_auth = session->auth.func(params, session);
+                       rc_cipher = session->cipher.func(params, session);
+               }
+               break;
+       }
+
+       /* Build Result (no HW so no errors) */
+       result = get_op_result_from_buffer(completion_event);
+       result->magic = OP_RESULT_MAGIC;
+       result->cipher.alg.cipher = session->cipher.alg;
+       result->cipher.alg_err = rc_cipher;
+       result->cipher.hw_err = ODP_CRYPTO_HW_ERR_NONE;
+       result->auth.alg.auth = session->auth.alg;
+       result->auth.alg_err = rc_auth;
+       result->auth.hw_err = ODP_CRYPTO_HW_ERR_NONE;
+
+       /*
+        * robking: a) the queue is supposed to come from session
+        *          b) ordering question asks whether we must
+        *             use the packet to return status
+        */
+       if (ODP_QUEUE_INVALID != session->compl_queue) {
+               odp_queue_enq(session->compl_queue, completion_event);
+               return ODP_CRYPTO_OP_POSTED;
+       }
+
+       return ODP_CRYPTO_OP_OK;
+}
+
+
+int
+odp_crypto_init(uint32_t max_sessions)
+{
+       size_t mem_size;
+
+       /* Force down to our limit */
+       if (MAX_SESSIONS < max_sessions)
+               max_sessions = MAX_SESSIONS;
+
+       /* Calculate the memory size we need */
+       mem_size  = sizeof(*global);
+       mem_size += (max_sessions * sizeof(struct odp_crypto_session_s));
+
+       /* Allocate our globally shared memory */
+       global = odp_shm_reserve("crypto_pool", mem_size, ODP_CACHE_LINE_SIZE);
+
+       /* Clear it out */
+       memset(global, 0, mem_size);
+
+       /* Initialize it */
+       global->max = max_sessions;
+
+       return 0;
+}
+
+int
+odp_hw_random_get(uint8_t *buf, size_t *len, bool use_entropy ODP_UNUSED)
+{
+       int rc;
+       rc = RAND_bytes(buf, *len);
+       return ((1 == rc) ? 0 : -1);
+}
+
+void
+odp_crypto_get_operation_compl_status(odp_buffer_t completion_event,
+                                     struct odp_crypto_compl_status *auth,
+                                     struct odp_crypto_compl_status *cipher)
+{
+       struct odp_operation_result_s *result;
+
+       result = get_op_result_from_buffer(completion_event);
+
+       if (OP_RESULT_MAGIC != result->magic)
+               abort();
+
+       memcpy(auth, &result->auth, sizeof(*auth));
