diff --git a/nRF51GattServer.cpp b/nRF51GattServer.cpp
index 09a9dcb..ee0140c 100644
--- a/nRF51GattServer.cpp
+++ b/nRF51GattServer.cpp
@@ -240,50 +240,51 @@ ble_error_t nRF51GattServer::getAppearance(uint16_t *appearanceP)
 /**************************************************************************/
 void nRF51GattServer::hwCallback(ble_evt_t *p_ble_evt)
 {
-    uint16_t                      handle_value;
-    GattServerEvents::gattEvent_t event;
+    uint16_t                       handle_value;
+    GattServerEvents::gattEvent_t  eventType;
+    const ble_gatts_evt_t         *gattsEventP = &p_ble_evt->evt.gatts_evt;
 
     switch (p_ble_evt->header.evt_id) {
         case BLE_GATTS_EVT_WRITE:
             /* There are 2 use case here: Values being updated & CCCD (indicate/notify) enabled */
 
             /* 1.) Handle CCCD changes */
-            handle_value = p_ble_evt->evt.gatts_evt.params.write.handle;
+            handle_value = gattsEventP->params.write.handle;
             for (uint8_t i = 0; i<characteristicCount; i++) {
                 if ((p_characteristics[i]->getProperties() & (GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY)) &&
                     (nrfCharacteristicHandles[i].cccd_handle == handle_value)) {
                     uint16_t cccd_value =
-                        (p_ble_evt->evt.gatts_evt.params.write.data[1] << 8) |
-                        p_ble_evt->evt.gatts_evt.params.write.data[0]; /* Little Endian but M0 may be mis-aligned */
+                        (gattsEventP->params.write.data[1] << 8) |
+                        gattsEventP->params.write.data[0]; /* Little Endian but M0 may be mis-aligned */
 
                     if (((p_characteristics[i]->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE) && (cccd_value & BLE_GATT_HVX_INDICATION)) ||
                         ((p_characteristics[i]->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) && (cccd_value & BLE_GATT_HVX_NOTIFICATION))) {
-                        event = GattServerEvents::GATT_EVENT_UPDATES_ENABLED;
+                        eventType = GattServerEvents::GATT_EVENT_UPDATES_ENABLED;
                     } else {
-                        event = GattServerEvents::GATT_EVENT_UPDATES_DISABLED;
+                        eventType = GattServerEvents::GATT_EVENT_UPDATES_DISABLED;
                     }
 
-                    handleEvent(event, i);
+                    handleEvent(eventType, i);
                     return;
                 }
             }
 
             /* 2.) Changes to the characteristic value will be handled with other events below */
-            event = GattServerEvents::GATT_EVENT_DATA_WRITTEN;
+            eventType = GattServerEvents::GATT_EVENT_DATA_WRITTEN;
             break;
 
-        case BLE_EVT_TX_COMPLETE:
-            handleEvent(GattServerEvents::GATT_EVENT_DATA_SENT);
-            return;
-
         case BLE_GATTS_EVT_HVC:
             /* Indication confirmation received */
-            event        = GattServerEvents::GATT_EVENT_CONFIRMATION_RECEIVED;
-            handle_value = p_ble_evt->evt.gatts_evt.params.hvc.handle;
+            eventType    = GattServerEvents::GATT_EVENT_CONFIRMATION_RECEIVED;
+            handle_value = gattsEventP->params.hvc.handle;
             break;
 
+        case BLE_EVT_TX_COMPLETE:
+            handleEvent(GattServerEvents::GATT_EVENT_DATA_SENT, NULL);
+            return;
+
         case BLE_GATTS_EVT_SYS_ATTR_MISSING:
-            sd_ble_gatts_sys_attr_set(p_ble_evt->evt.gatts_evt.conn_handle, NULL, 0);
+            sd_ble_gatts_sys_attr_set(gattsEventP->conn_handle, NULL, 0);
             return;
 
         default:
@@ -293,8 +294,21 @@ void nRF51GattServer::hwCallback(ble_evt_t *p_ble_evt)
     /* Find index (charHandle) in the pool */
     for (uint8_t i = 0; i<characteristicCount; i++) {
         if (nrfCharacteristicHandles[i].value_handle == handle_value) {
-            handleEvent(event, i);
-            break;
+            switch (eventType) {
+                case GattServerEvents::GATT_EVENT_DATA_WRITTEN: {
+                    GattCharacteristicWriteEvent eventData = {
+                        .op     = static_cast<GattCharacteristicWriteEvent::Type>(gattsEventP->params.write.op),
+                        .offset = gattsEventP->params.write.offset,
+                        .len    = gattsEventP->params.write.len,
+                        .data   = gattsEventP->params.write.data
+                    };
+                    handleDataWrittenEvent(i, &eventData);
+                    break;
+                }
+                default:
+                    handleEvent(eventType, i);
+                    break;
+            }
         }
     }
 }
diff --git a/public/BLEDevice.h b/public/BLEDevice.h
index 9f2e93f..fc496d6 100644
--- a/public/BLEDevice.h
+++ b/public/BLEDevice.h
@@ -214,7 +214,7 @@ public:
      * Setup a callback for when a characteristic has its value updated by a
      * client.
      */
-    void onDataWritten(GattServer::EventCallback_t callback);
+    void onDataWritten(GattServer::WriteEventCallback_t callback);
     void onUpdatesEnabled(GattServer::EventCallback_t callback);
     void onUpdatesDisabled(GattServer::EventCallback_t callback);
     void onConfirmationReceived(GattServer::EventCallback_t callback);
@@ -473,7 +473,7 @@ BLEDevice::onDataSent(GattServer::ServerEventCallback_t callback)
 }
 
 inline void
-BLEDevice::onDataWritten(GattServer::EventCallback_t callback)
+BLEDevice::onDataWritten(GattServer::WriteEventCallback_t callback)
 {
     transport->getGattServer().setOnDataWritten(callback);
 }
diff --git a/public/GattCharacteristicWriteEvent.h b/public/GattCharacteristicWriteEvent.h
new file mode 100644
index 0000000..ad7cc69
--- /dev/null
+++ b/public/GattCharacteristicWriteEvent.h
@@ -0,0 +1,35 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2013 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GATT_CHARACTERISTIC_WRITE_EVENT_H__
+#define __GATT_CHARACTERISTIC_WRITE_EVENT_H__
+
+struct GattCharacteristicWriteEvent {
+    enum Type {
+        GATTS_CHAR_OP_INVALID               = 0x00,  /**< Invalid Operation. */
+        GATTS_CHAR_OP_WRITE_REQ             = 0x01,  /**< Write Request. */
+        GATTS_CHAR_OP_WRITE_CMD             = 0x02,  /**< Write Command. */
+        GATTS_CHAR_OP_SIGN_WRITE_CMD        = 0x03,  /**< Signed Write Command. */
+        GATTS_CHAR_OP_PREP_WRITE_REQ        = 0x04,  /**< Prepare Write Request. */
+        GATTS_CHAR_OP_EXEC_WRITE_REQ_CANCEL = 0x05,  /**< Execute Write Request: Cancel all prepared writes. */
+        GATTS_CHAR_OP_EXEC_WRITE_REQ_NOW    = 0x06,  /**< Execute Write Request: Immediately execute all prepared writes. */
+    } op;                  /**< Type of write operation, */
+    uint16_t       offset; /**< Offset for the write operation. */
+    uint16_t       len;    /**< Length of the incoming data. */
+    const uint8_t *data;   /**< Incoming data, variable length. */
+};
+
+#endif /*__GATT_CHARACTERISTIC_WRITE_EVENT_H__*/
diff --git a/public/GattServer.h b/public/GattServer.h
index 74f43f4..3a06007 100644
--- a/public/GattServer.h
+++ b/public/GattServer.h
@@ -21,6 +21,7 @@
 #include "blecommon.h"
 #include "GattService.h"
 #include "GattServerEvents.h"
+#include "GattCharacteristicWriteEvent.h"
 
 /**************************************************************************/
 /*!
@@ -48,11 +49,12 @@ public:
 
     /* Event callback handlers. */
     typedef void (*EventCallback_t)(uint16_t attributeHandle);
+    typedef void (*WriteEventCallback_t)(uint16_t attributeHandle, const GattCharacteristicWriteEvent *eventDataP);
     typedef void (*ServerEventCallback_t)(void); /* independent of any particular attribute */
     void setOnDataSent(ServerEventCallback_t callback) {
         onDataSent = callback;
     }
-    void setOnDataWritten(EventCallback_t callback) {
+    void setOnDataWritten(WriteEventCallback_t callback) {
         onDataWritten = callback;
     }
     void setOnUpdatesEnabled(EventCallback_t callback) {
@@ -65,13 +67,14 @@ public:
         onConfirmationReceived = callback;
     }
 
+    void handleDataWrittenEvent(uint16_t charHandle, const GattCharacteristicWriteEvent *eventDataP) {
+        if (onDataWritten) {
+            onDataWritten(charHandle, eventDataP);
+        }
+    }
+
     void handleEvent(GattServerEvents::gattEvent_e type, uint16_t charHandle) {
         switch (type) {
-            case GattServerEvents::GATT_EVENT_DATA_WRITTEN:
-                if (onDataWritten) {
-                    onDataWritten(charHandle);
-                }
-                break;
             case GattServerEvents::GATT_EVENT_UPDATES_ENABLED:
                 if (onUpdatesEnabled) {
                     onUpdatesEnabled(charHandle);
@@ -113,7 +116,7 @@ protected:
 
 private:
     ServerEventCallback_t onDataSent;
-    EventCallback_t       onDataWritten;
+    WriteEventCallback_t  onDataWritten;
     EventCallback_t       onUpdatesEnabled;
     EventCallback_t       onUpdatesDisabled;
     EventCallback_t       onConfirmationReceived;
diff --git a/TransferService.cpp b/TransferService.cpp
index 9e82402..d8692a4 100644
--- a/TransferService.cpp
+++ b/TransferService.cpp
@@ -12,16 +12,16 @@ static const uint8_t transferFileInfoUUID[]    = transfer_UUID(0xACDF);
 static const uint8_t transferFileBlockUUID[16] = transfer_UUID(0xACE0);
 
 // Storage for the value of the characteristics
-struct fileInfo_t {
+typedef struct {
     uint16_t length;
     uint16_t crc16;
-};
-static struct fileInfo_t            fileInfo;
-struct fileBlock_t {
+} FileInfo_t;
+static FileInfo_t fileInfo;
+typedef struct {
     uint16_t blockNumber;
     uint8_t data[16];
-};
-static struct fileBlock_t           fileBlock;
+} FileBlock_t;
+static FileBlock_t fileBlock;
 
 // Other things needed for operation
 static BLEDevice*                   ble;
@@ -40,8 +40,8 @@ static GattCharacteristic transferFileInfo(transferFileInfoUUID,
 
 static GattCharacteristic transferFileBlock(transferFileBlockUUID,
                                             (uint8_t *)&fileBlock,
-                                            sizeof(fileBlock_t),
-                                            sizeof(fileBlock_t),
+                                            sizeof(FileBlock_t),
+                                            sizeof(FileBlock_t),
                                             GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE |
                                             GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);
 
@@ -71,15 +71,18 @@ void sendFileInfo();
 void refuseFile();
 void sendFileDownloadedSuccessfully();
 
-void handleDataWritten(uint16_t handle)
+void handleDataWritten(uint16_t handle, const GattCharacteristicWriteEvent *eventDataP)
 {
     if (!ble) {
         return;
     }
 
     if (handle == transferFileInfo.getHandle()) {
-        uint16_t len = sizeof(fileInfo);
-        ble->readCharacteristicValue(handle, (uint8_t *) &fileInfo, &len);
+        if (eventDataP->len != sizeof(FileInfo_t)) {
+            DEBUG("invalid write into fileInfo characteristic\r\n");
+            return;
+        }
+        memcpy(&fileInfo, eventDataP->data, eventDataP->len);
 
         if ((fileInfo.length == 0) && (fileInfo.crc16 == 0)) {
             // signal to cancel pending upload
@@ -103,8 +106,11 @@ void handleDataWritten(uint16_t handle)
             refuseFile();
         }
     } else if (handle == transferFileBlock.getHandle()) {
-        uint16_t len = sizeof(fileBlock);
-        ble->readCharacteristicValue(handle, (uint8_t *) &fileBlock, &len);
+        if (eventDataP->len != sizeof(FileBlock_t)) {
+            DEBUG("invalid write into fileInfo characteristic\r\n");
+            return;
+        }
+        memcpy(&fileBlock, eventDataP->data, eventDataP->len);
 
         if (fileBlock.blockNumber != expectingBlock) {
             DEBUG("Expected blk %u, not %u!\r\n", expectingBlock, fileBlock.blockNumber);
diff --git a/TransferService.h b/TransferService.h
index 84624f8..51652f7 100644
--- a/TransferService.h
+++ b/TransferService.h
@@ -2,11 +2,12 @@
 #define _H_TRANSFERSERVICE_H
 
 #include "BLEDevice.h"
+#include "GattCharacteristicWriteEvent.h"
 
 namespace Transfer {
     void init(BLEDevice &ble);
     void reset();
-    void handleDataWritten(uint16_t handle);
+    void handleDataWritten(uint16_t handle, const GattCharacteristicWriteEvent *eventDataP);
     const uint8_t* getServiceUUIDp();
 }
 
diff --git a/main.cpp b/main.cpp
index 7fb8b24..4ac87ca 100644
--- a/main.cpp
+++ b/main.cpp
@@ -57,10 +57,10 @@ void onUpdatesEnabled(Gap::Handle_t handle)
     DEBUG("Notifications enabled for %d\r\n", handle);
 }
 
-void onDataWritten(Gap::Handle_t handle)
+void onDataWritten(Gap::Handle_t handle, const GattCharacteristicWriteEvent *eventDataP)
 {
     // bubble up to services, they will emit callbacks if handle matches
-    Transfer::handleDataWritten(handle);
+    Transfer::handleDataWritten(handle, eventDataP);
 }
 
 void bluetoothInit()
