docs: document interface and error YAMLs

Resolves openbmc/openbmc#477.

Change-Id: I07392ae4712206753399b58650e4658ef769e558
Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
diff --git a/README.md b/README.md
index d232426..878c611 100644
--- a/README.md
+++ b/README.md
@@ -1,31 +1,101 @@
 # sdbusplus
 
-sdbusplus is a library and a tool for generating C++ bindings to dbus.
+sdbusplus contains two parts:
+
+1. A C++ library (libsdbusplus) for interacting with D-Bus, built on top of
+   the sd-bus library from systemd.
+2. A tool (sdbus++) to generate C++ bindings to simplify the development of
+   D-Bus-based applications.
+
+## Dependencies
+
+The sdbusplus library requires sd-bus, which is contained in libsystemd.
+
+The sdbus++ application requires python and the python libraries mako
+and py-inflection.
+
+## C++ library
+
+The sdbusplus library builds on top of the
+[sd-bus](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html)
+library to create a modern C++ API for D-Bus.  The library attempts to be
+as lightweight as possible, usually compiling to exactly the sd-bus API
+calls that would have been necessary, while also providing compile-time
+type-safety and memory leak protection afforded by modern C++ practices.
+
+Consider the following code:
+```
+auto b = bus::new_system();
+auto m = b.new_method_call("org.freedesktop.login1",
+                           "/org/freedesktop/login1",
+                           "org.freedesktop.login1.Manager",
+                           "ListUsers");
+auto reply = b.call(m);
+
+std::vector<std::tuple<uint32_t, std::string, message::object_path>> users;
+reply.read(users);
+```
+
+In a few, relatively succinct, C++ lines this snippet will create a D-Bus
+connection to the system bus, and call the systemd login manager to get a
+list of active users.  The message and bus objects are automatically freed
+when they leave scope and the message format strings are generated at compile
+time based on the types being read.  Compare this to the corresponding server
+code within [logind](https://github.com/systemd/systemd/blob/d60c527009133a1ed3d69c14b8c837c790e78d10/src/login/logind-dbus.c#L496).
+
+In general, the library attempts to mimic the naming conventions of the sd-bus
+library: ex. `sd_bus_call` becomes `sdbusplus::bus::call`,
+`sd_bus_get_unique_name` becomes `sdbusplus::bus::get_unique_name`,
+`sd_bus_message_get_signature` becomes `sdbusplus::message::get_signature`,
+etc.  This allows a relatively straight-forward translation back to the sd-bus
+functions for looking up the manpage details.
+
+## Binding generation tool
+
+sdbusplus also contains a bindings generator tool: `sdbus++`.  The purpose of
+a bindings generator is to reduce the boilerplate associated with creating
+D-Bus server or client applications.  When creating a server application,
+rather than creating sd-bus vtables and writing C-style functions to handle
+each vtable callback, you can create a small YAML file to define your D-Bus
+interface and the `sdbus++` tool will create a C++ class that implements your
+D-Bus interface.  This class has a set of virtual functions for each method
+and property, which you can overload to create your own customized behavior
+for the interface.
+
+There are currently two types of YAML files: [interface](docs/interface.md) and
+[error](docs/error.md).  Interfaces are used to create server and client D-Bus
+interfaces.  Errors are used to define C++ exceptions which can be thrown and
+will automatically turn into D-Bus error responses.
+
+[[ D-Bus client bindings are not yet implemented.  See openbmc/openbmc#851. ]]
+
+### Generating bindings
 
 ## How to use tools/sdbus++
 
 The path of your file will be the interface name. For example, for an interface
-`xyz.openbmc_project.control.Chassis`, you would create the following file:
-`xyz/openbmc_project/control/Chassis.interface.yaml`. Similary, for errors, you
-would create `xyz/openbmc_project/control/Chassis.errors.yaml`.
-
-Generating all the files:
+`org.freedesktop.Example`, you would create the files
+`org/freedesktop/Example.interface.yaml` and
+`org/freedesktop/Example.errors.yaml]` for interfaces and errors respectively.
+These can then be used to generate the server and error bindings:
 ```
-root_dir=$(readlink -f ../phosphor-dbus-interfaces)
-desired_interface=xyz.openbmc_project.control.Chassis
-file_prefix=chassis_interface
-file_exp_prefix=chassis_interface_exceptions
-outdir=../phosphor-chassis-control/generated
-
-# Server bindings
-python tools/sdbus++ --templatedir=tools/sdbusplus/templates --rootdir=$root_dir interface server-header $desired_interface > $outdir/$file_prefix.hpp
-python tools/sdbus++ --templatedir=tools/sdbusplus/templates --rootdir=$root_dir interface server-cpp $desired_interface > $outdir/$file_prefix.cpp
-
-# Exception bindings
-python tools/sdbus++ --templatedir=tools/sdbusplus/templates --rootdir=$root_dir error exception-header $desired_interface > $outdir/$file_exp_prefix.hpp
-python tools/sdbus++ --templatedir=tools/sdbusplus/templates --rootdir=$root_dir error exception-cpp $desired_interface > $outdir/$file_exp_prefix.cpp
-
-# Docs
-python tools/sdbus++ --templatedir=tools/sdbusplus/templates --rootdir=$root_dir interface markdown $desired_interface > $outdir/$file_prefix.md
-python tools/sdbus++ --templatedir=tools/sdbusplus/templates --rootdir=$root_dir error markdown $desired_interface > $outdir/$file_exp_prefix.md
+sdbus++ interface server-header org.freedesktop.Example > \
+    org/freedesktop/Example/server.hpp
+sdbus++ interface server-cpp org.freedesktop.Example > \
+    org/freedesktop/Example/server.cpp
+sdbus++ error exception-header org.freedesktop.Example > \
+    org/freedesktop/Example/error.hpp \
+sdbus++ error exception-cpp org.freedesktop.Example > \
+    org/freedesktop/Example/error.cpp
 ```
+
+Markdown-based documentation can also be generated from the interface and
+exception files:
+```
+sdbus++ interface markdown org.freedesktop.Example > \
+    org/freedesktop/Example.md
+sdbus++ error markdown org.freedesktop.Example >> \
+    org/freedesktop/Example.md
+```
+
+See the `example/Makefile.am` for more details.
diff --git a/docs/error.md b/docs/error.md
new file mode 100644
index 0000000..62aa138
--- /dev/null
+++ b/docs/error.md
@@ -0,0 +1,22 @@
+# Error YAML
+
+D-Bus errors can be defined by creating a YAML file to describe the errors.
+From this YAML file, both documentation and binding code may be generated.
+The generated bindings are C++ exception types corresponding to the D-Bus
+error name.  Ex. `org.freedesktop.Example.Error.SomeError` will create a
+generated exception type of
+`sdbusplus::org::freedesktop::Example::Error::SomeError` which may be thrown
+or caught as appropriate.  If the error is thrown from an interface method
+which has specified it may return that error, then the bindings will generate
+a catch clause that returns a D-Bus error like
+"org.freedesktop.Example.Error.SomeError" to the method caller.
+
+The error YAML is simply a list of `name` and optional `description` properties.
+Example:
+```
+- name: SomeError
+- name: AnotherError
+  description: >
+    This is another error.
+```
+
diff --git a/docs/interface.md b/docs/interface.md
new file mode 100644
index 0000000..89820b1
--- /dev/null
+++ b/docs/interface.md
@@ -0,0 +1,173 @@
+# Interface YAML
+
+D-Bus interfaces can be defined by creating a YAML file to describe the
+methods, properties, and signals they contain.  From this YAML file,
+both documentation and binding code may be generated.
+
+## YAML sections
+
+An interface YAML may have the following sections:
+
+* description
+    - A small documentation section describing the purpose of the
+      interface.
+* methods
+    - A list of methods provided by this D-Bus interface.
+* properties
+    - A list of properties provided by this D-Bus interface.
+* signals
+    - A list of signals generated by this D-Bus interface.
+* enumerations
+    - A list of enumerations defined by this D-Bus interface.
+
+## Enumerations
+
+A common problem we have found with D-Bus interfaces is having a consistent
+way to define enumerations.  Two common practices are to either assign
+special meaning to integers, as a C compiler might do, or to have specific
+strings representing the enumeration name.  The [D-Bus API design guidelines](https://dbus.freedesktop.org/doc/dbus-api-design.html)
+specify both of these options:
+
+> For APIs being used in constrained situations, enumerated values should be
+> transmitted as unsigned integers. For APIs which need to be extended by
+> third parties or which are used in more loosely coupled systems, enumerated
+> values should be strings in some defined format.
+
+What we have done in `sdbus++` is to consider enumerations as a first-class
+type.  Within an interface you can define an enumeration and the bindings
+will have a C++ enumeration defined for it.  At a D-Bus level any property or
+method parameter will be a string, but the string will contain a
+fully-qualified name "interface.enum-name.enum-value" like
+"org.freedesktop.Example.Color.Red".  Within the generated bindings, an
+automatic conversion is done between strings and C++ enumeration values and
+clients will get an "xyz.openbmc_project.sdbusplus.Error.InvalidEnumString"
+error response if they attempt to use an invalid string value.
+
+An enumeration must have the YAML properties `name` and `values` and may
+optionally contain a `description`.  The `name` is a word corresponding to
+the desired "enum-name" portion of the fully-qualified name and the resulting
+C++ enum type.  The `values` are a list of enumeration values each containing
+their own `name` and optional `description`.
+
+Example:
+```
+enumerations:
+    - name: Suits
+      description: >
+        The suits found in a deck of cards.
+      values:
+        - name: Diamonds
+        - name: Hearts
+        - name: Clubs
+          description: >
+            This is the suit that looks like a clover.
+        - name: Spades
+```
+
+## Types
+
+### Base types
+Types are identified in YAML using their typename found in the
+[D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html),
+but listed using lowercases: `int64` instead of `INT64` or C++ `int64_t`.
+
+* byte
+* boolean
+* int16
+* uint16
+* ...
+* uint64
+* double
+* string
+* object_path
+* signature
+
+### Containers
+Container types can also be expressed, but the contained-type should be
+expressed within square-brackets `[]`.  The following containers are supported:
+
+* `array[type]`
+    - C++ type is `std::vector`
+* `struct[type0, type1, ...]`
+    - C++ type is `std::tuple`
+* `variant[type0, type1, ...]`
+    - C++ type is `std::variant`
+* `dict[keytype, valuetype]`
+    - C++ type is `std::map`
+
+It may seem odd that variants are required to list the types they may contain,
+but this is due to C++ being a strongly-typed language.  In order to generate
+bindings, to read from and append to a message, the binding generator must
+know all possible types the variant may contain.
+
+### Enumerations
+Enumerations are expressed like a container, but the contained-type is an
+identifier of the fully-qualified enum-name or a shortened `self.` identifier
+for locally defined types.
+
+* enum[org.freedesktop.Example.Suits]
+* enum[self.Suits]
+
+## Methods
+
+A method must have the YAML property `name` and may optionally have
+`parameters`, `returns`, `errors`, and `description`.  Each parameter must
+have a `name`, `type`, and optional `description`.  Each return must have a
+`type` and may optionally have a `name` and `description`.  Errors are a list
+of fully-qualified or shortened `self.` identifiers for errors the method may
+return, which must be defined in a corresponding errors YAML file.
+
+Example:
+```
+methods:
+    - name: Shuffle
+      errors:
+        - self.Error.TooTired
+    - name: Deal
+      description: >
+        Deals a new hand to each player.
+      errors:
+        - self.Error.OutOfCards
+    - name: LookAtTop
+      returns:
+        - name: Card
+          type: struct[enum[self.Suit], byte]
+    - name: MoveToTop
+      parameters:
+        - name: Card
+          type: struct[enum[self.Suit], byte]
+```
+
+## Properties
+
+A property must have the YAML property `name` and `type` and may optionally
+have `description` and `default`.  The `default` defines the default value of
+the property.
+
+Example:
+```
+properties:
+    - name: CardsRemaining
+      type: uint32
+      default: 52
+      description: >
+        The number of cards remaining in the deck.
+```
+
+## Signals
+
+A signal must have the YAML property `name` and may optionally have a
+`description` and list of `properties`.  Properties are specified the same
+as interface properties.
+
+Example:
+```
+signals:
+    - name: Shuffled
+      description: >
+        The deck has been shuffled.
+    - name: Cheated
+      properties:
+        - name: CardToTop
+          type: struct[enum[self.Suit], byte]
+```