diff --git a/qga/channel-posix.c b/qga/channel-posix.c
index 3f34465..d307cf4 100644
--- a/qga/channel-posix.c
+++ b/qga/channel-posix.c
@@ -295,3 +295,57 @@ void ga_channel_free(GAChannel *c)
     }
     g_free(c);
 }
+
+static bool is_serial_present(GAChannelMethod method, const gchar *path,
+    int *error_code)
+{
+    int fd = -1;
+    bool ret = true;
+
+    assert(error_code);
+    *error_code = 0;
+
+    switch (method) {
+    case GA_CHANNEL_VIRTIO_SERIAL:
+        fd = qemu_open(path, O_RDWR | O_NONBLOCK
+#ifndef CONFIG_SOLARIS
+            | O_ASYNC
+#endif
+        );
+        break;
+    case GA_CHANNEL_ISA_SERIAL:
+        fd = qemu_open(path, O_RDWR | O_NOCTTY | O_NONBLOCK);
+        break;
+    default:
+        ret = false;
+    }
+    if (fd < 0) {
+        *error_code = errno;
+        ret = false;
+    } else {
+        close(fd);
+    }
+    return ret;
+}
+
+bool ga_channel_serial_is_present(GAChannelMethod method, const gchar *path)
+{
+    int error_code = 0;
+    return is_serial_present(method, path, &error_code) ||
+        error_code == EBUSY;
+}
+
+bool ga_channel_was_serial_attached(GAChannelMethod method, const gchar *path,
+    bool is_serial_attached)
+{
+    int error_code = 0;
+    return !is_serial_attached &&
+        is_serial_present(method, path, &error_code);
+}
+bool ga_channel_was_serial_detached(GAChannelMethod method, const gchar *path,
+    bool is_serial_attached)
+{
+    int error_code = 0;
+    return is_serial_attached && !is_serial_present(method, path, &error_code)
+        && error_code == ENOENT;
+}
diff --git a/qga/channel-win32.c b/qga/channel-win32.c
index 7e6dc4d..2d51bee 100644
--- a/qga/channel-win32.c
+++ b/qga/channel-win32.c
@@ -354,3 +354,63 @@ void ga_channel_free(GAChannel *c)
     g_free(c->rstate.buf);
     g_free(c);
 }
+
+static bool is_serial_present(GAChannelMethod method, const gchar *path,
+    DWORD *err)
+{
+    gchar newpath[MAXPATHLEN] = { 0 };
+    bool ret = false;
+
+    assert(err);
+
+    if (method != GA_CHANNEL_VIRTIO_SERIAL && method != GA_CHANNEL_ISA_SERIAL) {
+        g_critical("unsupported communication method");
+        return false;
+    }
+
+    if (method == GA_CHANNEL_ISA_SERIAL) {
+        snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path);
+    } else {
+        g_strlcpy(newpath, path, sizeof(newpath));
+    }
+
+    HANDLE handle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+        OPEN_EXISTING,
+        FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
+
+    if (handle == INVALID_HANDLE_VALUE) {
+        *err = GetLastError();
+        ret = false;
+    } else {
+        ret = true;
+    }
+
+    CloseHandle(handle);
+    return ret;
+}
+
+bool ga_channel_serial_is_present(GAChannelMethod method, const gchar *path)
+{
+    DWORD err_code;
+    return is_serial_present(method, path, &err_code) ||
+        err_code == ERROR_ACCESS_DENIED;
+}
+
+bool ga_channel_was_serial_attached(GAChannelMethod method, const gchar *path,
+    bool is_serial_attached)
+{
+    DWORD err_code;
+    return !is_serial_attached && is_serial_present(method, path, &err_code);
+}
+
+bool ga_channel_was_serial_detached(GAChannelMethod method, const gchar *path,
+    bool is_serial_attached)
+{
+    DWORD err_code = NO_ERROR;
+        /* In order to make sure the serial that qemu-ga uses is the one that
+         * was detached. We'll get the error ERROR_FILE_NOT_FOUND when
+         * attempting to call CreateFile with the serial path.
+        */
+       return is_serial_attached && !is_serial_present(method, path, &err_code)
+           && err_code == ERROR_FILE_NOT_FOUND;
+}
diff --git a/qga/channel.h b/qga/channel.h
index 1778416..acb3d73 100644
--- a/qga/channel.h
+++ b/qga/channel.h
@@ -12,6 +12,10 @@
 #ifndef QGA_CHANNEL_H
 #define QGA_CHANNEL_H
 
+#ifndef _WIN32
+#define SUBSYSTEM_VIRTIO_SERIAL "virtio-ports";
+#define SUBSYSTEM_ISA_SERIAL "isa-serial";
+#endif
 
 typedef struct GAChannel GAChannel;
 
@@ -30,5 +34,10 @@ GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
 void ga_channel_free(GAChannel *c);
 GIOStatus ga_channel_read(GAChannel *c, gchar *buf, gsize size, gsize *count);
 GIOStatus ga_channel_write_all(GAChannel *c, const gchar *buf, gsize size);
+bool ga_channel_serial_is_present(GAChannelMethod method, const gchar *path);
+bool ga_channel_was_serial_attached(GAChannelMethod method, const gchar *path,
+    bool is_serial_attached);
+bool ga_channel_was_serial_detached(GAChannelMethod method, const gchar *path,
+    bool is_serial_attached);
 
 #endif
