[magma] Add connection and context

This change adds basic scaffolding for connection and context objects.

Bug: b/272307395

Test: m libgfxstream_backend
Change-Id: I8dd1aea47ab273ca2f592ef309519c4137c42a71
diff --git a/host/magma/Android.bp b/host/magma/Android.bp
index 26c92f8..1ce98fb 100644
--- a/host/magma/Android.bp
+++ b/host/magma/Android.bp
@@ -22,7 +22,9 @@
     target: {
         android: {
             srcs: [
+                "Connection.cpp",
                 "DrmBuffer.cpp",
+                "DrmContext.cpp",
                 "DrmDevice.cpp",
                 "IntelDrmDecoder.cpp",
             ],
@@ -35,7 +37,9 @@
         },
         not_windows: {
             srcs: [
+                "Connection.cpp",
                 "DrmBuffer.cpp",
+                "DrmContext.cpp",
                 "DrmDevice.cpp",
                 "IntelDrmDecoder.cpp",
             ],
diff --git a/host/magma/CMakeLists.txt b/host/magma/CMakeLists.txt
index c2c389a..32aece0 100644
--- a/host/magma/CMakeLists.txt
+++ b/host/magma/CMakeLists.txt
@@ -1,7 +1,9 @@
 if (LINUX AND DEPENDENCY_RESOLUTION STREQUAL "DOWNLOAD")
     set(
         gfxstream-magma-server-backend-sources
+        Connection.cpp
         DrmBuffer.cpp
+        DrmContext.cpp
         DrmDevice.cpp
         IntelDrmDecoder.cpp)
     set(
diff --git a/host/magma/Connection.cpp b/host/magma/Connection.cpp
new file mode 100644
index 0000000..e6e3a37
--- /dev/null
+++ b/host/magma/Connection.cpp
@@ -0,0 +1,57 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// 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.
+
+#include "host/magma/Connection.h"
+
+#include <i915_drm.h>
+#include <sys/mman.h>
+
+#include <cerrno>
+#include <cinttypes>
+
+#include "host-common/logging.h"
+#include "host/magma/DrmDevice.h"
+
+namespace gfxstream {
+namespace magma {
+
+Connection::Connection(DrmDevice& device) : mDevice(device) {}
+
+std::optional<uint32_t> Connection::createContext() {
+    auto context = DrmContext::create(*this);
+    if (!context) {
+        ERR("Failed to create context");
+        return std::nullopt;
+    }
+
+    auto id = context->getId();
+    auto [_, emplaced] = mContexts.emplace(id, std::move(*context));
+    if (!emplaced) {
+        ERR("GEM produced duplicate context ID %" PRIu32, id);
+        return std::nullopt;
+    }
+
+    return id;
+}
+
+DrmContext* Connection::getContext(uint32_t id) {
+    auto it = mContexts.find(id);
+    if (it == mContexts.end()) {
+        return nullptr;
+    }
+    return &it->second;
+}
+
+}  // namespace magma
+}  // namespace gfxstream
diff --git a/host/magma/Connection.h b/host/magma/Connection.h
new file mode 100644
index 0000000..429f16c
--- /dev/null
+++ b/host/magma/Connection.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License") override;
+// 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.
+
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <unordered_map>
+
+#include "DrmContext.h"
+
+namespace gfxstream {
+namespace magma {
+
+class DrmDevice;
+
+// A Connection represents an unique magma object ID namespace.
+// Magma objects from different connections may share the same ID.
+class Connection {
+   public:
+    Connection(DrmDevice& device);
+    ~Connection() = default;
+    DISALLOW_COPY_AND_ASSIGN(Connection);
+    Connection(Connection&&) noexcept = default;
+    Connection& operator=(Connection&&) = delete;
+
+    // Creates a new context and returns its ID. Returns nullopt on error.
+    std::optional<uint32_t> createContext();
+
+    // Returns the context for the given ID, or nullptr if invalid.
+    DrmContext* getContext(uint32_t id);
+
+   private:
+    friend class DrmContext;
+
+    DrmDevice& mDevice;
+
+    // Maps context IDs to contexts.
+    std::unordered_map<uint32_t, DrmContext> mContexts;
+};
+
+}  // namespace magma
+}  // namespace gfxstream
diff --git a/host/magma/DrmContext.cpp b/host/magma/DrmContext.cpp
new file mode 100644
index 0000000..47f648f
--- /dev/null
+++ b/host/magma/DrmContext.cpp
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// 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.
+
+#include "host/magma/DrmContext.h"
+
+#include <i915_drm.h>
+#include <sys/mman.h>
+
+#include <cerrno>
+#include <cinttypes>
+
+#include "host-common/logging.h"
+#include "host/magma/Connection.h"
+#include "host/magma/DrmDevice.h"
+
+namespace gfxstream {
+namespace magma {
+
+DrmContext::DrmContext(Connection& connection) : mConnection(connection) {}
+
+DrmContext::DrmContext(DrmContext&& other) noexcept
+    : mConnection(other.mConnection), mId(other.mId) {
+    other.mId = std::nullopt;
+}
+
+DrmContext::~DrmContext() {
+    if (!mId) {
+        return;
+    }
+    drm_i915_gem_context_destroy params{.ctx_id = mId.value()};
+    int result = mConnection.mDevice.ioctl(DRM_IOCTL_I915_GEM_CONTEXT_DESTROY, &params);
+    if (result) {
+        ERR("DRM_IOCTL_I915_GEM_CONTEXT_DESTROY(%d) failed: %d", mId.value(), errno);
+    }
+}
+
+std::unique_ptr<DrmContext> DrmContext::create(Connection& connection) {
+    // Create a new GEM context.
+    drm_i915_gem_context_create_ext params{};
+    int result = connection.mDevice.ioctl(DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT, &params);
+    if (result) {
+        ERR("DRM_IOCTL_I915_GEM_CONTEXT_CREATE_EXT failed: %d", errno);
+        return nullptr;
+    }
+
+    auto context = std::unique_ptr<DrmContext>(new DrmContext(connection));
+    context->mId = params.ctx_id;
+
+    INFO("Created DrmContext id %" PRIu32, context->mId);
+    return context;
+}
+
+uint32_t DrmContext::getId() { return mId.value(); }
+
+}  // namespace magma
+}  // namespace gfxstream
diff --git a/host/magma/DrmContext.h b/host/magma/DrmContext.h
new file mode 100644
index 0000000..db3ef45
--- /dev/null
+++ b/host/magma/DrmContext.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License") override;
+// 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.
+
+#pragma once
+
+#include <memory>
+#include <optional>
+
+#include "aemu/base/Compiler.h"
+
+namespace gfxstream {
+namespace magma {
+
+class Connection;
+
+class DrmContext {
+   public:
+    ~DrmContext();
+    DISALLOW_COPY_AND_ASSIGN(DrmContext);
+    DrmContext(DrmContext&&) noexcept;
+    DrmContext& operator=(DrmContext&&) = delete;
+
+    static std::unique_ptr<DrmContext> create(Connection& connection);
+    uint32_t getId();
+
+   private:
+    DrmContext(Connection& device);
+
+    Connection& mConnection;
+    std::optional<uint32_t> mId;
+};
+
+}  // namespace magma
+}  // namespace gfxstream
diff --git a/host/magma/DrmDevice.cpp b/host/magma/DrmDevice.cpp
index a898636..c9e5ee7 100644
--- a/host/magma/DrmDevice.cpp
+++ b/host/magma/DrmDevice.cpp
@@ -24,6 +24,7 @@
 #include <string>
 
 #include "host-common/logging.h"
+#include "magma/magma_common_defs.h"
 
 namespace gfxstream {
 namespace magma {
diff --git a/host/magma/DrmDevice.h b/host/magma/DrmDevice.h
index bce1dcb..3f74235 100644
--- a/host/magma/DrmDevice.h
+++ b/host/magma/DrmDevice.h
@@ -20,6 +20,7 @@
 #include <memory>
 
 #include "DrmBuffer.h"
+#include "DrmContext.h"
 #include "MonotonicMap.h"
 #include "aemu/base/Compiler.h"
 #include "aemu/base/ManagedDescriptor.hpp"
diff --git a/host/magma/IntelDrmDecoder.cpp b/host/magma/IntelDrmDecoder.cpp
index 3bffcda..83839d1 100644
--- a/host/magma/IntelDrmDecoder.cpp
+++ b/host/magma/IntelDrmDecoder.cpp
@@ -25,6 +25,7 @@
 #include <vector>
 
 #include "RenderThreadInfoMagma.h"
+#include "host/magma/Connection.h"
 #include "host/magma/DrmDevice.h"
 #include "magma/magma_common_defs.h"
 
@@ -320,12 +321,19 @@
 magma_status_t IntelDrmDecoder::magma_device_create_connection(magma_device_t device,
                                                                magma_connection_t* connection_out) {
     *connection_out = MAGMA_INVALID_OBJECT_ID;
-    WARN("%s not implemented", __FUNCTION__);
-    return MAGMA_STATUS_UNIMPLEMENTED;
+    auto dev = mDevices.get(device);
+    if (!dev) {
+        return MAGMA_STATUS_INVALID_ARGS;
+    }
+    *connection_out = mConnections.create(*dev);
+    return MAGMA_STATUS_OK;
 }
 
 void IntelDrmDecoder::magma_connection_release(magma_connection_t connection) {
-    WARN("%s not implemented", __FUNCTION__);
+    bool erased = mConnections.erase(connection);
+    if (!erased) {
+        WARN("invalid connection %" PRIu64, connection);
+    }
 }
 
 magma_status_t IntelDrmDecoder::magma_connection_create_buffer(magma_connection_t connection,
@@ -404,8 +412,17 @@
 magma_status_t IntelDrmDecoder::magma_connection_create_context(magma_connection_t connection,
                                                                 uint32_t* context_id_out) {
     *context_id_out = MAGMA_INVALID_OBJECT_ID;
-    WARN("%s not implemented", __FUNCTION__);
-    return MAGMA_STATUS_UNIMPLEMENTED;
+    auto con = mConnections.get(connection);
+    if (!con) {
+        return MAGMA_STATUS_INVALID_ARGS;
+    }
+    auto ctx = con->createContext();
+    if (!ctx) {
+        WARN("error creating context");
+        return MAGMA_STATUS_INTERNAL_ERROR;
+    }
+    *context_id_out = ctx.value();
+    return MAGMA_STATUS_OK;
 }
 
 void IntelDrmDecoder::magma_connection_release_context(magma_connection_t connection,
diff --git a/host/magma/IntelDrmDecoder.h b/host/magma/IntelDrmDecoder.h
index f4f5a32..d6f9316 100644
--- a/host/magma/IntelDrmDecoder.h
+++ b/host/magma/IntelDrmDecoder.h
@@ -17,6 +17,7 @@
 #include "Decoder.h"
 #include "MonotonicMap.h"
 #include "DrmDevice.h"
+#include "Connection.h"
 
 namespace gfxstream {
 namespace magma {
@@ -52,6 +53,7 @@
     uint32_t mContextId;
     MonotonicMap<magma_device_t, DrmDevice> mDevices;
     MonotonicMap<magma_buffer_t, DrmBuffer> mBuffers;
+    MonotonicMap<magma_connection_t, Connection> mConnections;
 
     // Maps GEM handles to Buffers.
     std::unordered_map<uint32_t, magma_buffer_t> mGemHandleToBuffer;