diff --git a/dserial-protocol/Makefile b/dserial-protocol/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..8999a8328f3bfff1142bb576139b07053b2cc9a5
--- /dev/null
+++ b/dserial-protocol/Makefile
@@ -0,0 +1,10 @@
+build-doc: info.skorepa.DSerial1-info.skorepa.DSerial1.port.pdf info.skorepa.DSerial1-info.skorepa.DSerial1.controller.pdf
+
+%.pdf: %.xml
+	docbook2pdf $<
+
+info.skorepa.DSerial1-info.skorepa.DSerial1.%.xml: %.xml
+	gdbus-codegen --generate-docbook info.skorepa.DSerial1 $<
+
+clean:
+	rm info.skorepa.DSerial1-info.skorepa.DSerial1.*
diff --git a/dserial-protocol/Readme.md b/dserial-protocol/Readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..8b53ef184ff03d9f19a776dd8d58c30f045ea4bc
--- /dev/null
+++ b/dserial-protocol/Readme.md
@@ -0,0 +1,5 @@
+# DSerial Protocol
+
+This repository contains specification DSerial D-Bus protocol interfaces.
+
+
diff --git a/dserial-protocol/controller.xml b/dserial-protocol/controller.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6399856e9c80e9c1e7ce42a394e06da54441312a
--- /dev/null
+++ b/dserial-protocol/controller.xml
@@ -0,0 +1,68 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+	"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!--
+This particular file is for interoperability purposes released under CC0
+https://creativecommons.org/publicdomain/zero/1.0/
+
+Rest of DSerial project is NOT released under CC0
+-->
+<node>
+	<!--
+		info.skorepa.DSerial.Controller:
+		@short_description: Interface to work with list of serial ports.
+
+		Interface to work with list of serial ports.
+	-->
+	<interface name='info.skorepa.DSerial1.controller'>
+		<!--
+		PortList:
+
+		List of ports.
+
+		Change signal is only emitted upon interaction with server.
+		Server does not actively check for port changes.
+		-->
+		<property name='PortList' type='as' access='read'/>
+
+		<!--
+		CreateFakePort:
+
+		Creates fake port = port without asociated physical device.
+
+		Not implemented
+		-->
+		<method name='CreateFakePort'>
+			<arg name='name' type='s' direction='in'/>
+		</method>
+
+		<!--
+		CreateFilePort:
+
+		Creates file port = port which writes (and reads) to/from
+		file but doesn't set settings.
+
+		Not implemented
+		-->
+		<method name='CreateFilePort'>
+			<arg name='filename' type='s' direction='in'/>
+		</method>
+
+		<!--
+		Autodetect:
+
+		Performs autodetection of physical serial ports. Creates new objects
+		and removes old ones as needed.
+		-->
+		<method name='Autodetect' />
+
+		<!--
+		GetPath:
+
+		Returns object path of autodetected port.
+		-->
+		<method name='GetPath'>
+			<arg name='filename' type='s' direction='in'/>
+			<arg name='path' type='o' direction='out'/>
+		</method>
+	</interface>
+</node>
diff --git a/dserial-protocol/port.xml b/dserial-protocol/port.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4c5d2cc88b9320675d77ef8454cfb6ad8ee70553
--- /dev/null
+++ b/dserial-protocol/port.xml
@@ -0,0 +1,231 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+	"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<!--
+This particular file is for interoperability purposes released under CC0
+https://creativecommons.org/publicdomain/zero/1.0/
+
+Rest of DSerial project is NOT release under CC0
+-->
+<node>
+	<!--
+		info.skorepa.DSerial.port1:
+		@short_description: Represents single serial port
+
+		Interface to work with serial ports on system.
+
+		<emphasis>Concepts</emphasis>
+
+		Most properties return 0 if port was removed after last autodetect.
+		If that happens port is automatically removed from port list.
+
+		Port has to be opened with info.skorepa.serial.port.Open() to
+		recieve any messages using #info.skorepa.serial.port::DataRecieved
+		signal.
+
+		Server automatically closes unused port connections. To avoid your
+		port closing you should listen for #info.skorepa.serial.port::Closing
+		signal and when you recieve it call info.skorepa.serial.port.Open() again.
+		See info.skorepa.serial.port.Open() and #info.skorepa.serial.port::Closing
+		for further details.
+
+		Port is always open as Read/Write.
+
+		<emphasis></emphasis>
+
+		<emphasis></emphasis>
+
+		<emphasis>Proposed client pseudo-code for read</emphasis>
+
+		@init: connect_signals && open
+
+		@on_Closing: open
+
+		@on_DataRecieved: do something with data
+	-->
+	<interface name='info.skorepa.DSerial1.port'>
+		<!--
+		FlowControl:
+
+		Possible values:
+
+		1 - NONE
+
+		2 - RTS/CTS - Hardware
+
+		3 - Xon/Xoff - Software
+
+		Can also return:
+
+		0 - Port was removed after last autodetect
+		-->
+		<property type='y' name='FlowControl' access='readwrite'/>
+		<!--
+		Write:
+
+		Sends data to serial port. Does <emphasis>not</emphasis>
+		require port to be open.
+
+		Also emits #info.skorepa.serial.port::DataSent signal
+
+		@data: Data to be sent
+		-->
+		<method name='Write'>
+			<arg type='ay' name='data' direction='in'/>
+		</method>
+		<!--
+		FakeReception:
+
+		Send fake data to clients listening to #info.skorepa.serial.port::DataRecieved signal.
+
+		Useful for debugging.
+
+		Port has to be open for this to work.
+
+		@data: Data to pretend we recieved
+		-->
+		<method name='FakeReception'>
+			<arg type='ay' name='data' direction='in'/>
+		</method>
+		<!--
+		Open:
+
+		Opens port for reading. Otherwise #info.skorepa.serial.port::DataRecieved signal does not work.
+
+		Server every two minutes (this may change in future) sends
+		#info.skorepa.serial.port::Closing signal
+		to check if there still are listening clients. If you still want to
+		recieve #info.skorepa.serial.port::DataRecieved signal you have to call
+		open again.
+
+		Calling this method resets autoclose timer to zero.
+		-->
+		<method name='Open'/>
+
+		<!--
+		Close:
+
+		Causes #info.skorepa.serial.port::Closing signal to be
+		emitted. See its documentation for more information about
+		what that means.
+
+		Every application which listens to #info.skorepa.serial.port::DataRecieved signal
+		and calls info.skorepa.serial.port.Open() should call this
+		method when it stops listening to #info.skorepa.serial.port::DataRecieved signal.
+
+		-->
+		<method name='Close'/>
+
+		<!--
+		DataRecieved:
+
+		Emited when we recieve data on serial port.
+
+		Only emitted when port is opened with info.skorepa.serial.port.Open()
+
+		@data: Data recieved from serial port
+		-->
+		<signal name='DataRecieved'>
+			<arg type='ay' name='data'/>
+		</signal>
+		<!--
+		Closing:
+
+		Announces that port will be closed within 5 seconds (this timeout may change in future).
+		If you still want to recieve data from #info.skorepa.serial.port::DataRecieved signal
+		you have to call info.skorepa.serial.port.Open() again.
+		-->
+		<signal name='Closing'/>
+		<!--
+		DataSent:
+
+		Emitted when user sends data with info.skorepa.serial.port.Write() method.
+
+		@data: Data sent
+		-->
+		<signal name='DataSent'>
+			<arg type='ay' name='data'/>
+		</signal>
+		<!--
+		Disappeared:
+
+		Emitted when port is no longer available
+		-->
+		<signal name='Disappeared' />
+		<!--
+			Name:
+
+			Name of port
+
+			Returns "REMOVED" if port was removed after last autodetect
+		-->
+		<property type='s' name='Name' access='read'/>
+		<!--
+			Baudrate:
+
+			Baudrate setting
+
+			Returns 0 if port was removed after last autodetect
+		-->
+		<property type='i' name='Baudrate' access='readwrite'/><!-- uint32 -->
+		<!--
+			DataBits:
+
+			Can be set to positive integer. If particular
+			databits setting is not supported it won't
+			perform operation = this setting will remain
+			unchanged.
+
+			Values: 5, 6, 7, 8
+
+			Returns 0 if port was removed after last autodetect
+		-->
+		<property type='y' name='DataBits' access='readwrite'/>
+		<!--
+			StopBits:
+
+			Can be set to positive integer. If particular
+			stopbits setting is not supported it won't
+			perform operation = this setting will remain
+			unchanged.
+
+			Values: 1, 2
+
+			Returns 0 if port was removed after last autodetect
+		-->
+		<property type='y' name='StopBits' access='readwrite'/>
+		<!--
+			Parity:
+
+			Can be set to:
+
+			1 - NONE
+
+			2 - EVEN
+
+			3 - ODD
+
+			4 - SPACE
+
+			5 - MARK
+
+			Can also return:
+
+			0 - Port was removed after last autodetect
+		-->
+		<property type='y' name='Parity' access='readwrite'/>
+		<!--
+			Type:
+
+			Indicates port type.
+
+			Current implementation only returns 0 - Autodetected
+
+			0 - Autodetected
+
+			1 - Fake port
+
+			2 - File interface
+		-->
+		<property type='y' name='Type' access='read'/>
+	</interface>
+</node>