diff --git a/src/shared/radio/CC3135/CC3135.cpp b/src/shared/radio/CC3135/CC3135.cpp
index d3ff6ffd6b1118d4fef7a003c3cb93fbbe4f0885..761607147cd4f89f9782431447816f62d769379b 100644
--- a/src/shared/radio/CC3135/CC3135.cpp
+++ b/src/shared/radio/CC3135/CC3135.cpp
@@ -25,28 +25,39 @@
 #include <kernel/scheduler/scheduler.h>
 #include <utils/Debug.h>
 
-#define TRY(expr)                                            \
-    do                                                       \
-    {                                                        \
-        Boardcore::CC3135::Error result = (expr);            \
-        if (result != Boardcore::CC3135::Error::NO_ERROR)    \
-        {                                                    \
-            TRACE("[cc3135] NWP error@ %s\n", __FUNCTION__); \
-            return result;                                   \
-        }                                                    \
+#define TRY(expr)                                                      \
+    do                                                                 \
+    {                                                                  \
+        Boardcore::CC3135::Error result = (expr);                      \
+        if (result != Boardcore::CC3135::Error::NO_ERROR)              \
+        {                                                              \
+            TRACE("[cc3135] Error: %s @ %s:%d\n",                      \
+                  Boardcore::CC3135::errorToStr(result), __FUNCTION__, \
+                  __LINE__);                                           \
+            return result;                                             \
+        }                                                              \
     } while (0)
 
 using namespace Boardcore::CC3135Defs;
 using namespace miosix;
 
+constexpr uint8_t TEST_CHANNEL = 6;
+
 namespace Boardcore
 {
 
-CC3135::CC3135(std::unique_ptr<ICC3135Iface> &&iface) : iface(std::move(iface))
+CC3135::CC3135(std::unique_ptr<ICC3135Iface> &&iface)
+    : iface(std::move(iface)), sd(-1)
 {
     irq_wait_thread = thread;
 }
 
+CC3135::~CC3135()
+{
+    socketClose(sd);
+    sd = -1;
+}
+
 CC3135::Error CC3135::init(bool wait_for_init)
 {
     if (wait_for_init)
@@ -66,16 +77,76 @@ CC3135::Error CC3135::init(bool wait_for_init)
     // Start internal thread
     this->start();
 
+    // Setup transceiver mode
+    TRY(setupTransceiver());
+    TRY(startRecv());
+
+    return Error::NO_ERROR;
+}
+
+CC3135::Error CC3135::prepareForReset()
+{
+    // We need the device in station mode
+    TRY(wlanSetMode(ROLE_STA));
+
+    return Error::NO_ERROR;
+}
+
+CC3135::Error CC3135::startRecv() { return socketRecv(sd, Buffer::null(), 0); }
+
+CC3135::Error CC3135::dummyRecv() { return socketRecv(sd, Buffer::null(), 0); }
+
+CC3135::Error CC3135::dummySend()
+{
+    struct Packet
+    {
+        uint32_t a = 0xdeadbabe;
+        uint32_t b = 0xdeadbeef;
+        uint32_t c = 0xbabebabe;
+    };
+
+    Packet packet;
+
+    uint16_t flags =
+        makeWlanRawRfTxParams(TEST_CHANNEL, RATE_1M, 0, PREAMBLE_LONG);
+    TRY(socketSend2(sd, Buffer::from(&packet), flags));
+
+    return Error::NO_ERROR;
+}
+
+CC3135::Error CC3135::setupTransceiver()
+{
+    // TODO: This should really set the device in the correct mode and re-init
+
+    // Reset connection policies
+    TRY(wlanPolicySet(WLAN_POLICY_CONNECTION, 0, Buffer::null()));
+
+    Error result = wlanDisconnect();
+
+    // Check for not errors and continue
+    if (result != Error::NO_ERROR && result != Error::WLAN_ALREADY_DISCONNECTED)
+        return result;
+
+    // Open transceiver socket
+    // with respect of IEE802.11 medium access policies
+    TRY(socketOpen(AF_RF, SOCK_RAW, TEST_CHANNEL, sd));
+    TRACE("[cc3135] Socket opened: %d\n", sd);
+
+    // As a sanity check, verify the socket type
+    if (sdGetType(sd) != SdType::RAW_TRANSCEIVER)
+        return Error::GENERIC_ERROR;
+
     return Error::NO_ERROR;
 }
 
 CC3135::Error CC3135::getVersion(DeviceVersion &version)
 {
     version = {};
-    return devigeGet(1, 12, Buffer::from<DeviceVersion>(&version));
+    return devigeGet(DEVICE_GENERAL, DEVICE_GENERAL_VERSION,
+                     Buffer::from<DeviceVersion>(&version));
 }
 
-CC3135::Error CC3135::devigeGet(uint8_t set_id, uint8_t option, Buffer result)
+CC3135::Error CC3135::devigeGet(uint8_t set_id, uint8_t option, Buffer value)
 {
     DeviceSetGet tx_command  = {};
     tx_command.device_set_id = set_id;
@@ -83,31 +154,107 @@ CC3135::Error CC3135::devigeGet(uint8_t set_id, uint8_t option, Buffer result)
 
     DeviceSetGet rx_command = {};
 
-    return inoutPacketSync(OPCODE_DEVICE_DEVICEGET, Buffer::from(&tx_command),
-                           Buffer::null(), OPCODE_DEVICE_DEVICEGETRESPONSE,
-                           Buffer::from(&rx_command), result);
+    TRY(inoutPacketSync(OPCODE_DEVICE_DEVICEGET, Buffer::from(&tx_command),
+                        Buffer::null(), OPCODE_DEVICE_DEVICEGETRESPONSE,
+                        Buffer::from(&rx_command), value));
+
+    return errorFromStatus(rx_command.status);
 }
 
-CC3135::Error CC3135::dummyDeviceRead()
+CC3135::Error CC3135::wlanSetMode(CC3135Defs::Mode mode)
 {
-    ResponseHeader header;
-    TRY(readHeader(&header));
+    WlanSetMode tx_command = {};
+    tx_command.mode        = mode;
 
-    defaultPacketHandler(header);
+    BasicResponse rx_command = {};
 
-    return Error::NO_ERROR;
+    TRY(inoutPacketSync(OPCODE_WLAN_SET_MODE, Buffer::from(&tx_command),
+                        Buffer::null(), OPCODE_WLAN_SET_MODE_RESPONSE,
+                        Buffer::from(&rx_command), Buffer::null()));
+
+    return errorFromStatus(rx_command.status);
 }
 
-CC3135::Error CC3135::setMode(CC3135Defs::Mode mode)
+CC3135::Error CC3135::wlanPolicySet(uint8_t type, uint8_t option, Buffer value)
 {
-    WlanSetMode tx_command = {};
-    tx_command.mode        = mode;
+    WlanPolicySetGet tx_command  = {};
+    tx_command.policy_type       = type;
+    tx_command.policy_option     = option;
+    tx_command.policy_option_len = value.len;
 
     BasicResponse rx_command = {};
 
-    return inoutPacketSync(OPCODE_WLAN_SET_MODE, Buffer::from(&tx_command),
-                           Buffer::null(), OPCODE_WLAN_SET_MODE_RESPONSE,
-                           Buffer::from(&rx_command), Buffer::null());
+    TRY(inoutPacketSync(OPCODE_WLAN_POLICYSETCOMMAND, Buffer::from(&tx_command),
+                        value, OPCODE_WLAN_POLICYSETRESPONSE,
+                        Buffer::from(&rx_command), Buffer::null()));
+
+    return errorFromStatus(rx_command.status);
+}
+
+CC3135::Error CC3135::wlanDisconnect()
+{
+    BasicResponse rx_command = {};
+
+    TRY(inoutPacketSync(OPCODE_WLAN_WLANDISCONNECTCOMMAND, Buffer::null(),
+                        Buffer::null(), OPCODE_WLAN_WLANDISCONNECTRESPONSE,
+                        Buffer::from(&rx_command), Buffer::null()));
+
+    return errorFromStatus(rx_command.status);
+}
+
+CC3135::Error CC3135::socketOpen(uint8_t domain, uint8_t type, uint8_t protocol,
+                                 Sd &sd)
+{
+    SocketCommand tx_command = {};
+    tx_command.domain        = domain;
+    tx_command.type          = type;
+    tx_command.protocol      = protocol;
+
+    SocketResponse rx_command = {};
+
+    TRY(inoutPacketSync(OPCODE_SOCKET_SOCKET, Buffer::from(&tx_command),
+                        Buffer::null(), OPCODE_SOCKET_SOCKETRESPONSE,
+                        Buffer::from(&rx_command), Buffer::null()));
+
+    sd = rx_command.sd;
+    return errorFromStatus(rx_command.status_or_len);
+}
+
+CC3135::Error CC3135::socketClose(Sd sd)
+{
+    CloseCommand tx_command = {};
+    tx_command.sd           = sd;
+
+    SocketResponse rx_command = {};
+
+    TRY(inoutPacketSync(OPCODE_SOCKET_CLOSE, Buffer::from(&tx_command),
+                        Buffer::null(), OPCODE_SOCKET_CLOSERESPONSE,
+                        Buffer::from(&rx_command), Buffer::null()));
+
+    return errorFromStatus(rx_command.status_or_len);
+}
+
+CC3135::Error CC3135::socketRecv(Sd sd, Buffer buffer, uint16_t flags)
+{
+    SendRecvCommand tx_command  = {};
+    tx_command.sd               = sd;
+    tx_command.status_or_len    = 300;
+    tx_command.family_and_flags = flags & 0x0f;
+
+    return writePacketSync(OPCODE_SOCKET_RECV, Buffer::from(&tx_command),
+                           Buffer::null());
+}
+
+CC3135::Error CC3135::socketSend2(Sd sd, Buffer buffer, uint16_t flags)
+{
+    SendRecvCommand2 tx_command = {};
+    tx_command.sd               = sd;
+    tx_command.status_or_len    = buffer.len;
+    tx_command.family_and_flags = flags & 0x0f;
+    tx_command.flags            = flags;
+
+    return writePacketSync(OPCODE_SOCKET_SEND, Buffer::from(&tx_command),
+                           buffer);
 }
 
 void CC3135::handleIrq()
@@ -162,24 +309,6 @@ void CC3135::restoreDefaultServiceThread()
     }
 }
 
-void CC3135::defaultPacketHandler(CC3135Defs::ResponseHeader header)
-{
-    TRACE(
-        "[cc3135] Received packet:\n"
-        "- Opcode: %s (%4x)\n"
-        "- Status: %u\n"
-        "- Socket tx failures: %u\n"
-        "- Socket non blocking: %u\n",
-        opToStr(header.inner.opcode), header.inner.opcode, header.dev_status,
-        header.socket_tx_failure, header.socket_non_blocking);
-
-    size_t len = header.inner.len;
-
-    // Dummy read rest of the data
-    if (len > 0)
-        dummyRead(len);
-}
-
 void CC3135::run()
 {
     // TODO: Implement a way to stop this thread
@@ -188,22 +317,83 @@ void CC3135::run()
         waitForIrq();
         // Thread::sleep(500);
 
+        ResponseHeader header;
         {
             // Lock the device interface
             Lock<FastMutex> iface_lock(iface_mutex);
 
-            ResponseHeader header;
             Error result = readHeader(&header);
 
             if (result != Error::NO_ERROR)
             {
-                TRACE("[cc3135] NWP error!\n");
+                TRACE("[cc3135] Error: %s\n", errorToStr(result));
+                continue;
             }
-            else
+
+            defaultPacketHandler(header);
+        }
+    }
+}
+
+void CC3135::defaultPacketHandler(CC3135Defs::ResponseHeader header)
+{
+    TRACE(
+        "[cc3135] Received packet:\n"
+        "- Opcode: %s (%4x)\n"
+        "- Status: %u\n"
+        "- Socket tx failures: %u\n"
+        "- Socket non blocking: %u\n",
+        opToStr(header.inner.opcode), header.inner.opcode, header.dev_status,
+        header.socket_tx_failure, header.socket_non_blocking);
+
+    size_t len = alignSize(header.inner.len);
+
+    switch (header.inner.opcode)
+    {
+        case OPCODE_SOCKET_RECVASYNCRESPONSE:
+        {
+            SocketResponse rx_command = {};
+
+            safeRead(len, Buffer::from(&rx_command));
+
+            // 8 byte proprietary header, format information?
+            uint64_t header2;
+            safeRead(len, Buffer::from(&header2));
+            rx_command.status_or_len -= 8;
+
+            /*
+            notprintf("Header: %8x\n", header2);
+
+            if (rx_command.status_or_len >= 0)
             {
-                defaultPacketHandler(header);
+                notprintf("%d %d\n", len, rx_command.status_or_len);
+
+                uint8_t response[len];
+                safeRead(len, Buffer{response, len});
+
+                for (int i = 0; i < rx_command.status_or_len; i++)
+                {
+                    notprintf("%2x ", response[i]);
+                }
+                notprintf("\n");
             }
+            */
+
+            break;
         }
+        default:
+            if (len > 0)
+            {
+                dummyRead(len);
+                len = 0;
+            }
+    };
+
+    // Dummy read rest of the data
+    if (len > 0)
+    {
+        TRACE("[cc3135] %d bytes still in receive buffer!\n", len);
+        dummyRead(len);
     }
 }
 
@@ -268,7 +458,7 @@ CC3135::Error CC3135::readPacket(OpCode opcode, CC3135::Buffer command,
         else
         {
             // Read the rest of the packet
-            size_t len = header.inner.len;
+            size_t len = alignSize(header.inner.len);
 
             safeRead(len, command);
             safeRead(len, payload);
@@ -287,8 +477,8 @@ CC3135::Error CC3135::readPacket(OpCode opcode, CC3135::Buffer command,
 CC3135::Error CC3135::writePacket(OpCode opcode, CC3135::Buffer command,
                                   CC3135::Buffer payload)
 {
-    RequestHeader header{opcode,
-                         static_cast<uint16_t>(command.len + payload.len)};
+    size_t len = alignSize(command.len + payload.len);
+    RequestHeader header{opcode, static_cast<uint16_t>(len)};
 
     TRY(writeHeader(&header));
 
@@ -330,7 +520,7 @@ CC3135::Error CC3135::readHeader(ResponseHeader *header)
     while (true)
     {
         if ((getTick() - start) > TIMEOUT)
-            return Error::NWP_TIMEOUT;
+            return Error::SYNC_TIMEOUT;
 
         // Reads in SPI are always more than 4 bytes
         bool found = false;
@@ -368,7 +558,7 @@ CC3135::Error CC3135::readHeader(ResponseHeader *header)
     while (n2hSyncPatternMatch(sync, tx_seq_num))
     {
         if ((getTick() - start) > TIMEOUT)
-            return Error::NWP_TIMEOUT;
+            return Error::SYNC_TIMEOUT;
 
         memmove(&buf[0], &buf[4], 4);
         iface->read(&buf[4], 4);
@@ -415,13 +605,8 @@ CC3135::Error CC3135::writeHeader(RequestHeader *header)
 void CC3135::dummyRead(size_t n)
 {
     // Avoid HUGE stack allocations
-    uint8_t dummy[256];
-
-    while (n > 0)
-    {
-        iface->read(dummy, std::min(n, sizeof(dummy)));
-        n -= std::min(n, sizeof(dummy));
-    }
+    uint8_t dummy[n];
+    iface->read(dummy, n);
 }
 
 void CC3135::safeRead(size_t &len, Buffer buffer)
@@ -433,4 +618,38 @@ void CC3135::safeRead(size_t &len, Buffer buffer)
     }
 }
 
+CC3135::Error CC3135::errorFromStatus(int16_t status)
+{
+    if (status >= 0)
+    {
+        return Error::NO_ERROR;
+    }
+
+    TRACE("[cc3135] Non zero status: %d\n", status);
+    switch (status)
+    {
+        case -2071:
+            return Error::WLAN_ALREADY_DISCONNECTED;
+        default:
+            return Error::GENERIC_ERROR;
+    }
+}
+
+const char *CC3135::errorToStr(Error error)
+{
+    switch (error)
+    {
+        case Error::NO_ERROR:
+            return "NO_ERROR";
+        case Error::SYNC_TIMEOUT:
+            return "SYNC_TIMEOUT";
+        case Error::GENERIC_ERROR:
+            return "GENERIC_ERROR";
+        case Error::WLAN_ALREADY_DISCONNECTED:
+            return "WLAN_ALREADY_DISCONNECTED";
+        default:
+            return "<unknown>";
+    }
+}
+
 }  // namespace Boardcore
diff --git a/src/shared/radio/CC3135/CC3135.h b/src/shared/radio/CC3135/CC3135.h
index 531c44a0e9cbdcdf8ad8b545ebe5dbac620c892d..d60104533039edb337d3a8ca5ca3bc4b0a09d52a 100644
--- a/src/shared/radio/CC3135/CC3135.h
+++ b/src/shared/radio/CC3135/CC3135.h
@@ -43,14 +43,18 @@ namespace Boardcore
 class CC3135 : ActiveObject
 {
 public:
+    constexpr static size_t MTU = 1536;
+
     enum class Error
     {
-        NO_ERROR,             //< No error occured.
-        NWP_TIMEOUT,          //< The NWP did not respond.
-        NWP_STATUS_NON_ZERO,  //< The NWD returned a non-zero status code.
+        NO_ERROR,                   //< No error occured.
+        SYNC_TIMEOUT,               //< The NWP did not respond.
+        GENERIC_ERROR,              //< Generic error class.
+        WLAN_ALREADY_DISCONNECTED,  //< Wlan is already disconnected.
     };
 
     explicit CC3135(std::unique_ptr<ICC3135Iface> &&iface);
+    ~CC3135();
 
     CC3135::Error init(bool wait_for_init);
 
@@ -58,7 +62,14 @@ public:
 
     CC3135::Error getVersion(CC3135Defs::DeviceVersion &version);
 
-    CC3135::Error setMode(CC3135Defs::Mode mode);
+    CC3135::Error startRecv();
+
+    CC3135::Error dummyRecv();
+    CC3135::Error dummySend();
+
+    CC3135::Error prepareForReset();
+
+    static const char *errorToStr(Error error);
 
 private:
     //! Simple buffer for scatter/gather IO
@@ -79,7 +90,7 @@ private:
     class ServiceThreadLock
     {
     public:
-        ServiceThreadLock(CC3135 *parent) : parent(parent)
+        explicit ServiceThreadLock(CC3135 *parent) : parent(parent)
         {
             parent->installAsServiceThread();
         }
@@ -90,15 +101,34 @@ private:
         CC3135 *parent;
     };
 
+    CC3135::Error setupTransceiver();
+
     //! Function for servicing async messages.
     void run() override;
 
     void defaultPacketHandler(CC3135Defs::ResponseHeader header);
 
-    CC3135::Error devigeGet(uint8_t set_id, uint8_t option, Buffer result);
+    // Part of the device API
+
+    CC3135::Error devigeGet(uint8_t set_id, uint8_t option, Buffer value);
+
+    CC3135::Error wlanSetMode(CC3135Defs::Mode mode);
+
+    CC3135::Error wlanPolicySet(uint8_t type, uint8_t option, Buffer value);
 
-    //! Read something from the device, to wake it up?
-    CC3135::Error dummyDeviceRead();
+    CC3135::Error wlanDisconnect();
+
+    CC3135::Error socketOpen(uint8_t domain, uint8_t type, uint8_t protocol,
+                             CC3135Defs::Sd &sd);
+
+    CC3135::Error socketClose(CC3135Defs::Sd sd);
+
+    CC3135::Error socketRecv(CC3135Defs::Sd sd, Buffer buffer, uint16_t flags);
+
+    // CC3135::Error socketSend(CC3135Defs::Sd sd, Buffer buffer, uint8_t
+    // flags);
+
+    CC3135::Error socketSend2(CC3135Defs::Sd sd, Buffer buffer, uint16_t flags);
 
     // Functions dedicated to interrupt servicing
 
@@ -142,6 +172,8 @@ private:
     //! Safely read a buffer, does bound checking
     void safeRead(size_t &len, Buffer buffer);
 
+    static CC3135::Error errorFromStatus(int16_t status);
+
     miosix::Thread *irq_wait_thread = nullptr;  //< Thread waiting on IRQ
     size_t irq_count                = 0;        //< Number of interrupts
 
@@ -149,6 +181,8 @@ private:
     std::unique_ptr<ICC3135Iface> iface;
 
     uint8_t tx_seq_num = 0;
+
+    CC3135Defs::Sd sd;  //< Socket descriptor
 };
 
 }  // namespace Boardcore
diff --git a/src/shared/radio/CC3135/CC3135Defs.h b/src/shared/radio/CC3135/CC3135Defs.h
index 06c2fd3eaa64f39c7f250a21465d1049bb07cfd7..0738afaa3a96e46ad38cd7c9b3b428a736ff1646 100644
--- a/src/shared/radio/CC3135/CC3135Defs.h
+++ b/src/shared/radio/CC3135/CC3135Defs.h
@@ -37,15 +37,6 @@ namespace Boardcore
 namespace CC3135Defs
 {
 
-enum Mode : uint8_t
-{
-    ROLE_STA      = 0,
-    ROLE_RESERVED = 1,
-    ROLE_AP       = 2,
-    ROLE_P2P      = 3,
-    ROLE_TAG      = 4
-};
-
 //! Synchronous message mask.
 constexpr uint16_t OPCODE_SYNC = 1 << 10;
 
@@ -53,26 +44,92 @@ constexpr uint16_t OPCODE_SYNC = 1 << 10;
 enum OpCode : uint16_t
 {
     OPCODE_DEVICE_INITCOMPLETE                  = 0x0008,
+    OPCODE_DEVICE_ABORT                         = 0x000C,
     OPCODE_DEVICE_DEVICEASYNCDUMMY              = 0x0063,
     OPCODE_DEVICE_DEVICEGETRESPONSE             = 0x0466,
     OPCODE_DEVICE_DEVICESETRESPONSE             = 0x04B7,
     OPCODE_WLAN_PROVISIONING_STATUS_ASYNC_EVENT = 0x089A,  //< ????
+    OPCODE_WLAN_WLANDISCONNECTRESPONSE          = 0x0C81,
+    OPCODE_WLAN_POLICYSETRESPONSE               = 0x0C86,
     OPCODE_WLAN_SET_MODE_RESPONSE               = 0x0CB4,
+    OPCODE_SOCKET_RECVASYNCRESPONSE             = 0x100A,
+    OPCODE_SOCKET_SOCKETRESPONSE                = 0x1401,
+    OPCODE_SOCKET_CLOSERESPONSE                 = 0x1402,
     OPCODE_NETAPP_IPACQUIRED                    = 0x1825,  //< ????
     OPCODE_DEVICE_DEVICEGET                     = 0x8466,
     OPCODE_DEVICE_DEVICESET                     = 0x84B7,
+    OPCODE_WLAN_POLICYSETCOMMAND                = 0x8C86,
+    OPCODE_WLAN_WLANDISCONNECTCOMMAND           = 0x8C81,
     OPCODE_WLAN_SET_MODE                        = 0x8CB4,
+    OPCODE_SOCKET_SOCKET                        = 0x9401,
+    OPCODE_SOCKET_CLOSE                         = 0x9402,
+    OPCODE_SOCKET_RECV                          = 0x940A,
+    OPCODE_SOCKET_SEND                          = 0x940C,
 };
 
 //! Is this message synchronous?
 inline bool isSync(OpCode op) { return op & OPCODE_SYNC; }
 
-struct SyncPattern
+inline const char *opToStr(OpCode op)
 {
-    uint32_t long1;
-    uint16_t short1;
-    uint8_t byte1;
-    uint8_t byte2;
+    switch (op)
+    {
+        case OpCode::OPCODE_DEVICE_INITCOMPLETE:
+            return "OPCODE_DEVICE_INITCOMPLETE";
+        case OpCode::OPCODE_DEVICE_ABORT:
+            return "OPCODE_DEVICE_ABORT";
+        case OpCode::OPCODE_DEVICE_DEVICEASYNCDUMMY:
+            return "OPCODE_DEVICE_DEVICEASYNCDUMMY";
+        case OpCode::OPCODE_DEVICE_DEVICEGETRESPONSE:
+            return "OPCODE_DEVICE_DEVICEGETRESPONSE";
+        case OpCode::OPCODE_DEVICE_DEVICESETRESPONSE:
+            return "OPCODE_DEVICE_DEVICESETRESPONSE";
+        case OpCode::OPCODE_NETAPP_IPACQUIRED:
+            return "OPCODE_NETAPP_IPACQUIRED";
+        case OpCode::OPCODE_WLAN_PROVISIONING_STATUS_ASYNC_EVENT:
+            return "OPCODE_WLAN_PROVISIONING_STATUS_ASYNC_EVENT";
+        case OpCode::OPCODE_WLAN_WLANDISCONNECTRESPONSE:
+            return "OPCODE_WLAN_WLANDISCONNECTRESPONSE";
+        case OpCode::OPCODE_WLAN_POLICYSETRESPONSE:
+            return "OPCODE_WLAN_POLICYSETRESPONSE";
+        case OpCode::OPCODE_WLAN_SET_MODE_RESPONSE:
+            return "OPCODE_WLAN_SET_MODE_RESPONSE";
+        case OpCode::OPCODE_SOCKET_RECVASYNCRESPONSE:
+            return "OPCODE_SOCKET_RECVASYNCRESPONSE";
+        case OpCode::OPCODE_SOCKET_SOCKETRESPONSE:
+            return "OPCODE_SOCKET_SOCKETRESPONSE";
+        case OpCode::OPCODE_SOCKET_CLOSERESPONSE:
+            return "OPCODE_SOCKET_CLOSERESPONSE";
+        case OpCode::OPCODE_DEVICE_DEVICEGET:
+            return "OPCODE_DEVICE_DEVICEGET";
+        case OpCode::OPCODE_DEVICE_DEVICESET:
+            return "OPCODE_DEVICE_DEVICESET";
+        case OpCode::OPCODE_WLAN_POLICYSETCOMMAND:
+            return "OPCODE_WLAN_POLICYSETCOMMAND";
+        case OpCode::OPCODE_WLAN_WLANDISCONNECTCOMMAND:
+            return "OPCODE_WLAN_WLANDISCONNECTCOMMAND";
+        case OpCode::OPCODE_WLAN_SET_MODE:
+            return "OPCODE_WLAN_SET_MODE";
+        case OpCode::OPCODE_SOCKET_SOCKET:
+            return "OPCODE_SOCKET_SOCKET";
+        case OpCode::OPCODE_SOCKET_CLOSE:
+            return "OPCODE_SOCKET_CLOSE";
+        case OpCode::OPCODE_SOCKET_RECV:
+            return "OPCODE_SOCKET_RECV";
+        case OpCode::OPCODE_SOCKET_SEND:
+            return "OPCODE_SOCKET_SEND";
+        default:
+            return "<unknown>";
+    }
+}
+
+enum Mode : uint8_t
+{
+    ROLE_STA      = 0,
+    ROLE_RESERVED = 1,
+    ROLE_AP       = 2,
+    ROLE_P2P      = 3,
+    ROLE_TAG      = 4
 };
 
 struct DeviceVersion
@@ -82,7 +139,7 @@ struct DeviceVersion
     uint8_t phy_version[4];
     uint8_t nwp_version[4];
     uint16_t rom_version;
-    uint16_t _pad;
+    uint8_t _pad[2];
 };
 
 struct GenericHeader
@@ -124,12 +181,167 @@ struct DeviceSetGet
     uint16_t config_len;
 };
 
+constexpr uint16_t DEVICE_GENERAL         = 1;
+constexpr uint16_t DEVICE_GENERAL_VERSION = 12;
+
 struct WlanSetMode
 {
     Mode mode;
     uint8_t _pad[3];
 };
 
+struct WlanPolicySetGet
+{
+    uint8_t policy_type;
+    uint8_t _pad;
+    uint8_t policy_option;
+    uint8_t policy_option_len;
+};
+
+constexpr uint8_t WLAN_POLICY_CONNECTION = 16;
+
+/// Socket descriptor;
+using Sd = uint8_t;
+
+enum class SdType
+{
+    RAW_TRANSCEIVER,
+    GENERIC
+};
+
+inline SdType sdGetType(Sd sd)
+{
+    if ((sd & 0xf0) == 0x80)
+    {
+        return SdType::RAW_TRANSCEIVER;
+    }
+    else
+    {
+        return SdType::GENERIC;
+    }
+}
+
+struct SocketCommand
+{
+    uint8_t domain;
+    uint8_t type;
+    uint8_t protocol;
+    uint8_t padding;
+};
+
+constexpr uint8_t AF_INET  = 2;
+constexpr uint8_t AF_INET6 = 3;
+constexpr uint8_t AF_RF    = 6;
+
+constexpr uint8_t SOCK_STREAM = 1;
+constexpr uint8_t SOCK_DGRAM  = 2;
+constexpr uint8_t SOCK_RAW    = 3;
+constexpr uint8_t SOCK_RX_MTR = 4;
+
+constexpr uint8_t IPPROTO_TCP = 6;
+constexpr uint8_t IPPROTO_UDP = 17;
+
+struct CloseCommand
+{
+    Sd sd;
+    uint8_t _pad[3];
+};
+
+struct SendRecvCommand
+{
+    uint16_t status_or_len;
+    Sd sd;
+    uint8_t family_and_flags;
+};
+
+struct SendRecvCommand2
+{
+    uint16_t status_or_len;
+    Sd sd;
+    uint8_t family_and_flags;
+    uint16_t flags;
+    uint8_t _pad[2];
+};
+
+struct SocketResponse
+{
+    int16_t status_or_len;
+    Sd sd;
+    uint8_t _pad;
+};
+
+enum Rate : uint16_t
+{
+    RATE_1M    = 1,
+    RATE_2M    = 2,
+    RATE_5_5M  = 3,
+    RATE_11M   = 4,
+    RATE_6M    = 6,
+    RATE_9M    = 7,
+    RATE_12M   = 8,
+    RATE_18M   = 9,
+    RATE_24M   = 10,
+    RATE_36M   = 11,
+    RATE_48M   = 12,
+    RATE_54M   = 13,
+    RATE_MCS_0 = 14,
+    RATE_MCS_1 = 15,
+    RATE_MCS_2 = 16,
+    RATE_MCS_3 = 17,
+    RATE_MCS_4 = 18,
+    RATE_MCS_5 = 19,
+    RATE_MCS_6 = 20,
+    RATE_MCS_7 = 21,
+};
+
+enum Preable : uint16_t
+{
+    PREAMBLE_LONG  = 0,
+    PREAMBLE_SHORT = 1
+};
+
+inline uint16_t makeWlanRawRfTxParams(uint8_t channel, Rate rate, uint8_t power,
+                                      Preable preamble)
+{
+    constexpr uint8_t MAX_2_4G_CHANNEL = 14;
+    constexpr uint8_t BAND_2_4G        = 0;
+    constexpr uint8_t BAND_5_0G        = 1;
+
+    constexpr uint8_t CHANNEL_SHIFT  = 0;
+    constexpr uint8_t BAND_SHIFT     = 5;
+    constexpr uint8_t RATE_SHIFT     = 6;
+    constexpr uint8_t POWER_SHIFT    = 11;
+    constexpr uint8_t PREAMBLE_SHIFT = 15;
+
+    constexpr uint8_t CHANNEL_5G_SHIFT = 12;
+
+    if (channel <= MAX_2_4G_CHANNEL)
+    {
+        return ((channel << CHANNEL_SHIFT) | (BAND_2_4G << BAND_SHIFT) |
+                (rate << RATE_SHIFT) | (power << POWER_SHIFT) |
+                (preamble << PREAMBLE_SHIFT));
+    }
+    else
+    {
+        uint8_t channel_lo = channel & 0x1f;
+        uint8_t channel_hi = (channel & 0xe0) >> 5;
+
+        return ((channel_lo << CHANNEL_SHIFT) | (BAND_5_0G << BAND_SHIFT) |
+                (rate << RATE_SHIFT) | (channel_hi << CHANNEL_5G_SHIFT) |
+                (power << POWER_SHIFT) | (preamble << PREAMBLE_SHIFT));
+    }
+}
+
+// Stuff required for synchronization
+
+struct SyncPattern
+{
+    uint32_t long1;
+    uint16_t short1;
+    uint8_t byte1;
+    uint8_t byte2;
+};
+
 constexpr SyncPattern H2N_SYNC_PATTERN = {0xBBDDEEFF, 0x4321, 0x34, 0x12};
 constexpr SyncPattern H2N_CNYS_PATTERN = {0xBBDDEEFF, 0x8765, 0x78, 0x56};
 
@@ -151,35 +363,6 @@ inline bool n2hSyncPatternMatch(uint32_t sync, uint8_t seq_num)
 //! Align message size.
 inline size_t alignSize(size_t size) { return (size + 3) & (~3); }
 
-inline const char *opToStr(OpCode op)
-{
-    switch (op)
-    {
-        case OpCode::OPCODE_DEVICE_INITCOMPLETE:
-            return "OPCODE_DEVICE_INITCOMPLETE";
-        case OpCode::OPCODE_DEVICE_DEVICEASYNCDUMMY:
-            return "OPCODE_DEVICE_DEVICEASYNCDUMMY";
-        case OpCode::OPCODE_DEVICE_DEVICEGETRESPONSE:
-            return "OPCODE_DEVICE_DEVICEGETRESPONSE";
-        case OpCode::OPCODE_DEVICE_DEVICESETRESPONSE:
-            return "OPCODE_DEVICE_DEVICESETRESPONSE";
-        case OpCode::OPCODE_NETAPP_IPACQUIRED:
-            return "OPCODE_NETAPP_IPACQUIRED";
-        case OpCode::OPCODE_WLAN_PROVISIONING_STATUS_ASYNC_EVENT:
-            return "OPCODE_WLAN_PROVISIONING_STATUS_ASYNC_EVENT";
-        case OpCode::OPCODE_WLAN_SET_MODE_RESPONSE:
-            return "OPCODE_WLAN_SET_MODE_RESPONSE";
-        case OpCode::OPCODE_DEVICE_DEVICEGET:
-            return "OPCODE_DEVICE_DEVICEGET";
-        case OpCode::OPCODE_DEVICE_DEVICESET:
-            return "OPCODE_DEVICE_DEVICESET";
-        case OpCode::OPCODE_WLAN_SET_MODE:
-            return "OPCODE_WLAN_SET_MODE";
-        default:
-            return "<unknown>";
-    }
-}
-
 }  // namespace CC3135Defs
 
 }  // namespace Boardcore
diff --git a/src/tests/drivers/CC3135/test-cc3135.cpp b/src/tests/drivers/CC3135/test-cc3135.cpp
index f1c137c302572b74651f045f2f62e7278faff7ed..863a7862933d2f36fb4d2837d2c38e5d8e1790a7 100644
--- a/src/tests/drivers/CC3135/test-cc3135.cpp
+++ b/src/tests/drivers/CC3135/test-cc3135.cpp
@@ -86,7 +86,7 @@ using irq  = Gpio<GPIOA_BASE, 4>;
 using hib  = Gpio<GPIOA_BASE, 6>;
 
 #define CC3135_SPI SPI1
-// #define CC3135_HIB
+#define CC3135_HIB
 #endif
 
 #endif
@@ -178,6 +178,8 @@ int main()
 
     initBoard();
 
+    // For some reason, putting this before SPI peripheral activation makes it
+    // more reliable
 #ifdef CC3135_HIB
     // Reset CC3135
     hib::low();
@@ -190,7 +192,7 @@ int main()
     GpioPin cs_pin = cs::getPin();
 
     SPIBusConfig config = {};
-    config.clockDivider = SPI::ClockDivider::DIV_32;
+    config.clockDivider = SPI::ClockDivider::DIV_64;
     config.mode         = SPI::Mode::MODE_0;
     config.bitOrder     = SPI::BitOrder::MSB_FIRST;
 
@@ -204,22 +206,18 @@ int main()
     printf("[cc3135] Initializing...\n");
     cc3135 = new CC3135(std::move(iface));
 
-    // cc3135->handleIrq();
-
-    if (cc3135->init(false) != CC3135::Error::NO_ERROR)
+    auto result = cc3135->init(true);
+    if (result != CC3135::Error::NO_ERROR)
     {
-        printf("[cc3135] Failed to start cc3135, retrying...\n");
-        Thread::sleep(2000);
-        return 0;
+        printf("[cc3135] Failed to start cc3135 (error: %s), retrying...\n",
+               CC3135::errorToStr(result));
+        Thread::sleep(4000);
 
-        // miosix::reboot();
+        miosix::reboot();
     }
 
     printf("[cc3135] Initialization complete!\n");
 
-    // CHECK(cc3135->setMode(CC3135Defs::ROLE_STA));
-    // Thread::sleep(500);
-
     CC3135Defs::DeviceVersion version = {};
     CHECK(cc3135->getVersion(version));
     printf(
@@ -235,7 +233,29 @@ int main()
         version.nwp_version[3], version.rom_version);
 
     while (true)
-        ;
+    {
+        cc3135->startRecv();
+        Thread::sleep(1000);
+        printf("[cc3135] Looping\n");
+
+        cc3135->dummySend();
+
+        CHECK(cc3135->getVersion(version));
+        printf(
+            "[cc3135] Chip Id: %lx\n"
+            "[cc3135] Fw version: %u.%u.%u.%u\n"
+            "[cc3135] Phy version: %u.%u.%u.%u\n"
+            "[cc3135] Nwp version: %u.%u.%u.%u\n"
+            "[cc3135] Rom version: %x\n",
+            version.chip_id, version.fw_version[0], version.fw_version[1],
+            version.fw_version[2], version.fw_version[3],
+            version.phy_version[0], version.phy_version[1],
+            version.phy_version[2], version.phy_version[3],
+            version.nwp_version[0], version.nwp_version[1],
+            version.nwp_version[2], version.nwp_version[3],
+            version.rom_version);
+    }
+
     // Thread::sleep(3000);
     // miosix::reboot();
 }