You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by an...@apache.org on 2018/11/23 16:16:33 UTC

[mynewt-core] 01/26: hw/bus: Add initial implementation for generic bus driver

This is an automated email from the ASF dual-hosted git repository.

andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit cb29a50454297d115df1879f875c0b8fd8d5b5ba
Author: Andrzej Kaczmarek <an...@codecoup.pl>
AuthorDate: Mon Nov 5 16:39:27 2018 +0100

    hw/bus: Add initial implementation for generic bus driver
    
    This adds initial implementation of generic serial bus driver.
    
    General bus driver is an abstraction for using serial buses like I2C or
    SPI in a generic way where bus state is managed automatically to handle
    access form multiple threads and to multiple devices concurrently in a
    seamless way. This aims to replace direct calls to HAL in drivers etc.
    
    Internally bus driver works with two main object types:
    - bus_dev = bus device, a representation of I2C or SPI bus instance
    - bus_node = bus node, a representation of device connected to bus_dev
    
    In general, bus_dev object creation should be managed by MCU (or BSP)
    code and replace existing calls HAL init APIs since it will initialize
    these HALs internally. bus_node objects should be created by drivers
    or by an application in case application wants to access some "loose"
    device on the bus without dedicated driver.
    
    Externally both objects are passed around as simple os_dev so basically
    application needs to call os_dev_open() to get os_dev object and then
    just pass it to bus APIs.
---
 hw/bus/include/bus/bus.h        | 214 +++++++++++++++++++++++++++
 hw/bus/include/bus/bus_debug.h  |  47 ++++++
 hw/bus/include/bus/bus_driver.h | 156 ++++++++++++++++++++
 hw/bus/pkg.yml                  |  28 ++++
 hw/bus/src/bus.c                | 313 ++++++++++++++++++++++++++++++++++++++++
 hw/bus/syscfg.yml               |  32 ++++
 6 files changed, 790 insertions(+)

diff --git a/hw/bus/include/bus/bus.h b/hw/bus/include/bus/bus.h
new file mode 100644
index 0000000..7d9c04e
--- /dev/null
+++ b/hw/bus/include/bus/bus.h
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef HW_BUS_H_
+#define HW_BUS_H_
+
+#include <stdint.h>
+#include "os/os_dev.h"
+#include "os/os_mutex.h"
+#include "os/os_time.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Flags used for bus operations
+ */
+#define BUS_F_NONE          0
+#define BUS_F_NOSTOP        0x0001
+
+/**
+ * Read data from node
+ *
+ * Reads data from node. Bus is locked automatically for the duration of
+ * operation.
+ *
+ * The timeout parameter applies to complete transaction time, including
+ * locking the bus.
+ *
+ * @param node     Node device object
+ * @param buf      Buffer to read data into
+ * @param length   Length of data to be read
+ * @param timeout  Operation timeout
+ * @param flags    Flags
+ *
+ * @return 0 on success, SYS_xxx on error
+ */
+int
+bus_node_read(struct os_dev *node, uint8_t *buf, uint16_t length,
+              os_time_t timeout, uint16_t flags);
+
+/**
+ * Write data to node
+ *
+ * Writes data to node. Bus is locked automatically for the duration of
+ * operation.
+ *
+ * The timeout parameter applies to complete transaction time, including
+ * locking the bus.
+ *
+ * @param node     Node device object
+ * @param buf      Buffer with data to be written
+ * @param length   Length of data to be written
+ * @param timeout  Operation timeout
+ * @param flags    Flags
+ *
+ * @return 0 on success, SYS_xxx on error
+ */
+int
+bus_node_write(struct os_dev *node, uint8_t *buf, uint16_t length,
+               os_time_t timeout, uint16_t flags);
+
+/**
+ * Perform write and read transaction on node
+ *
+ * Writes data to node and automatically reads response afterwards. This is a
+ * convenient shortcut for a generic write-then-read operation used to read data
+ * from devices which is executed atomically (i.e. with bus lock held during
+ * entire transaction).
+ *
+ * The timeout parameter applies to complete transaction time.
+ *
+ * @param node     Node device object
+ * @param wbuf     Buffer with data to be written
+ * @param wlength  Length of data to be written
+ * @param rbuf     Buffer to read data into
+ * @param rlength  Length of data to be read
+ * @param timeout  Operation timeout
+ * @param flags    Flags
+ *
+ * @return 0 on success, SYS_xxx on error
+ */
+int
+bus_node_write_read_transact(struct os_dev *node, uint8_t *wbuf,
+                             uint16_t wlength, uint8_t *rbuf, uint16_t rlength,
+                             os_time_t timeout, uint16_t flags);
+
+/**
+ * Read data from node
+ *
+ * This is simple version of bus_node_read() with default timeout and no flags.
+ *
+ * @param node     Node device object
+ * @param buf      Buffer to read data into
+ * @param length   Length of data to be read
+ *
+ * @return 0 on success, SYS_xxx on error
+ */
+static inline int
+bus_node_simple_read(struct os_dev *node, uint8_t *buf, uint16_t length)
+{
+    return bus_node_read(node, buf, length, OS_TIMEOUT_NEVER, BUS_F_NONE);
+}
+
+/**
+ * Write data to node
+ *
+ * This is simple version of bus_node_write() with default timeout and no flags.
+ *
+ * @param node     Node device object
+ * @param buf      Buffer with data to be written
+ * @param length   Length of data to be written
+ *
+ * @return 0 on success, SYS_xxx on error
+ */
+static inline int
+bus_node_simple_write(struct os_dev *node, uint8_t *buf, uint16_t length)
+{
+    return bus_node_write(node, buf, length, OS_TIMEOUT_NEVER, BUS_F_NONE);
+}
+
+/**
+ * Perform write and read transaction on node
+ *
+ * This is simple version of bus_node_write_read_transact() with default timeout
+ * and no flags.
+ *
+ * @param node     Node device object
+ * @param wbuf     Buffer with data to be written
+ * @param wlength  Length of data to be written
+ * @param rbuf     Buffer to read data into
+ * @param rlength  Length of data to be read
+ *
+ * @return 0 on success, SYS_xxx on error
+ */
+static inline int
+bus_node_simple_write_read_transact(struct os_dev *node, uint8_t *wbuf,
+                                    uint16_t wlength, uint8_t *rbuf,
+                                    uint16_t rlength)
+{
+    return bus_node_write_read_transact(node, wbuf, wlength, rbuf, rlength,
+                                        OS_TIMEOUT_NEVER, BUS_F_NONE);
+}
+
+/**
+ * Get lock object for bus
+ *
+ * \deprecated
+ * This API is only provided for compatibility with legacy drivers where locking
+ * is provided by Sensors interface. To lock bus for compount transactions use
+ * bus_dev_lock() and bus_dev_unlock() instead.
+ *
+ * @param bus    Bus device object
+ * @param mutex  Mutex used for bus lock
+ *
+ * return 0 on success, SYS_xxx on error
+ */
+int
+bus_dev_get_lock(struct os_dev *bus, struct os_mutex **mutex);
+
+/**
+ * Lock bus for exclusive access
+ *
+ * Locks bus which given node is attached to for exclusive access. This should
+ * be only used for compound transactions where bus shall be locked for the
+ * duration of entire transaction. Simple operations like read, write or
+ * write-read (i.e. those with dedicated APIs) lock bus automatically.
+ *
+ * @param node     Node attached to bus to lock
+ * @param timeout  Timeout on locking attempt
+ *
+ * return 0 on success
+ *        SYS_ETIMEOUT on lock timeout
+ */
+int
+bus_dev_lock_by_node(struct os_dev *node, os_time_t timeout);
+
+/**
+ * Unlock bus
+ *
+ * Unlocks bus access. This shall be only used when bus was previously locked
+ * with bus_dev_lock_by_node(). API operations which lock bus will also unlock
+ * bus automatically.
+ *
+ * @param node  Node attached to bus to unlock
+ *
+ * return 0 on success
+ *        SYS_EACCESS when bus was not locked by current task
+ */
+int
+bus_dev_unlock_by_node(struct os_dev *node);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HW_BUS_H_ */
diff --git a/hw/bus/include/bus/bus_debug.h b/hw/bus/include/bus/bus_debug.h
new file mode 100644
index 0000000..034eec5
--- /dev/null
+++ b/hw/bus/include/bus/bus_debug.h
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef HW_BUS_DEBUG_H_
+#define HW_BUS_DEBUG_H_
+
+#include "syscfg/syscfg.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if MYNEWT_VAL(BUS_DEBUG)
+#define BUS_DEBUG_MAGIC_DEV             0xABADBABE
+#define BUS_DEBUG_MAGIC_NODE            0xABADCAFE
+#define BUS_DEBUG_POISON_DEV(_dev)      do { (_dev)->devmagic = (BUS_DEBUG_MAGIC_DEV); } while (0)
+#define BUS_DEBUG_POISON_NODE(_node)    do { (_node)->nodemagic = (BUS_DEBUG_MAGIC_NODE); } while (0)
+#define BUS_DEBUG_VERIFY_DEV(_dev)      assert((_dev)->devmagic == (BUS_DEBUG_MAGIC_DEV))
+#define BUS_DEBUG_VERIFY_NODE(_node)    assert((_node)->nodemagic == (BUS_DEBUG_MAGIC_NODE))
+#else
+#define BUS_DEBUG_POISON_DEV(_dev)      (void)(_dev)
+#define BUS_DEBUG_POISON_NODE(_node)    (void)(_node)
+#define BUS_DEBUG_VERIFY_DEV(_dev)      (void)(_dev)
+#define BUS_DEBUG_VERIFY_NODE(_node)    (void)(_node)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HW_BUS_DEBUG_H_ */
diff --git a/hw/bus/include/bus/bus_driver.h b/hw/bus/include/bus/bus_driver.h
new file mode 100644
index 0000000..a80f400
--- /dev/null
+++ b/hw/bus/include/bus/bus_driver.h
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+#ifndef HW_BUS_DRIVER_H_
+#define HW_BUS_DRIVER_H_
+
+#include <stdint.h>
+#include "os/mynewt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bus_dev;
+struct bus_node;
+
+/**
+ * Bus device operations
+ *
+ * These operations shall be defined by bus driver
+ */
+struct bus_dev_ops {
+    /* Enable bus device */
+    int (* enable)(struct bus_dev *bus);
+    /* Configure bus for node */
+    int (* configure)(struct bus_dev *bus, struct bus_node *node);
+    /* Read data from node */
+    int (* read)(struct bus_dev *dev, struct bus_node *node, uint8_t *buf,
+                 uint16_t length, os_time_t timeout, uint16_t flags);
+    /* Write data to node */
+    int (* write)(struct bus_dev *dev, struct bus_node *node, uint8_t *buf,
+                  uint16_t length, os_time_t timeout,  uint16_t flags);
+    /* Disable bus device */
+    int (* disable)(struct bus_dev *bus);
+};
+
+/**
+ * Bus node callbacks
+ *
+ * Node can use these callbacks to receive notifications from bus driver.
+ * All callbacks are optional.
+ */
+struct bus_node_callbacks {
+    /* Called when os_dev is initialized */
+    void (* init)(struct bus_node *node, void *arg);
+    /* Called when first reference to node object is opened */
+    void (* open)(struct bus_node *node);
+    /* Called when last reference to node object is closed */
+    void (* close)(struct bus_node *node);
+};
+
+/**
+ * Bus node configuration
+ *
+ * This can be wrapped in configuration structure defined by bus driver.
+ */
+struct bus_node_cfg {
+    /** Bus device name where node is attached */
+    const char *bus_name;
+};
+
+/**
+ * Bus device object state
+ *
+ * Contents of these objects are managed internally by bus driver and shall not
+ * be accessed directly.
+ */
+struct bus_dev {
+    struct os_dev odev;
+    const struct bus_dev_ops *dops;
+
+    struct os_mutex lock;
+    struct bus_node *configured_for;
+
+#if MYNEWT_VAL(BUS_DEBUG)
+    uint32_t devmagic;
+#endif
+};
+
+/**
+ * Bus node object state
+ *
+ * Contents of these objects are managed internally by bus driver and shall not
+ * be accessed directly.
+ */
+struct bus_node {
+    struct os_dev odev;
+    struct bus_node_callbacks callbacks;
+    struct bus_dev *parent_bus;
+
+#if MYNEWT_VAL(BUS_DEBUG)
+    uint32_t nodemagic;
+#endif
+};
+
+/**
+ * Initialize os_dev as bus device
+ *
+ * This can be passed as a parameter to os_dev_create() when creating os_dev
+ * object for bus, however it's recommended to create devices using specialized
+ * APIs provided by bus drivers.
+ *
+ * @param bus  Bus device object
+ * @param arg  Bus device operations struct (struct bus_dev_ops)
+ */
+int
+bus_dev_init_func(struct os_dev *bus, void *arg);
+
+/**
+ * Initialize os_dev as bus node
+ *
+ * This can be passed as a parameter to os_dev_create() when creating os_dev
+ * object for node, however it's recommended to create devices using specialized
+ * APIs provided by bus drivers.
+ *
+ * @param node  Node device object
+ * @param arg   Node configuration struct (struct bus_node_cfg)
+ */
+int
+bus_node_init_func(struct os_dev *node, void *arg);
+
+/**
+ * Set driver callbacks for node
+ *
+ * This should be used before node device is initialized to set callbacks for
+ * receiving notifications from bus driver. It shall be called no more than once
+ * on any bus_node object.
+ *
+ * @param node  Node device object;
+ * @param cbs   Callbacks
+ *
+ */
+void
+bus_node_set_callbacks(struct os_dev *node, struct bus_node_callbacks *cbs);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HW_BUS_DRIVER_H_ */
diff --git a/hw/bus/pkg.yml b/hw/bus/pkg.yml
new file mode 100644
index 0000000..7828e46
--- /dev/null
+++ b/hw/bus/pkg.yml
@@ -0,0 +1,28 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+#
+
+pkg.name: hw/bus
+pkg.description: Serial bus access layer
+pkg.author: "Apache Mynewt <de...@mynewt.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+pkg.deps:
+
+pkg.init:
+    bus_pkg_init: 100
diff --git a/hw/bus/src/bus.c b/hw/bus/src/bus.c
new file mode 100644
index 0000000..8f5a451
--- /dev/null
+++ b/hw/bus/src/bus.c
@@ -0,0 +1,313 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 <assert.h>
+#include <string.h>
+#include "defs/error.h"
+#include "bus/bus.h"
+#include "bus/bus_debug.h"
+#include "bus/bus_driver.h"
+
+static int
+bus_node_open_func(struct os_dev *odev, uint32_t wait, void *arg)
+{
+    struct bus_node *bnode = (struct bus_node *)odev;
+
+    BUS_DEBUG_VERIFY_NODE(bnode);
+
+    if (!bnode->callbacks.open) {
+        return 0;
+    }
+
+    /*
+     * XXX current os_dev implementation is prone to races since reference
+     * counting is done without any locking, we'll need to fix it there
+     */
+
+    /* Call open callback if opening first ref */
+    if (odev->od_open_ref == 0) {
+        bnode->callbacks.open(bnode);
+    }
+
+    return 0;
+}
+
+static int
+bus_node_close_func(struct os_dev *odev)
+{
+    struct bus_node *bnode = (struct bus_node *)odev;
+
+    BUS_DEBUG_VERIFY_NODE(bnode);
+
+    if (!bnode->callbacks.close) {
+        return 0;
+    }
+
+    /*
+     * XXX current os_dev implementation is prone to races since reference
+     * counting is done without any locking, we'll need to fix it there
+     */
+
+    /* Call close callback if closing last ref */
+    if (odev->od_open_ref == 1) {
+        bnode->callbacks.close(bnode);
+    }
+
+    return 0;
+}
+
+static inline int
+bus_dev_configure_for(struct bus_dev *bdev, struct bus_node *bnode)
+{
+    int rc;
+
+    assert((void *)bdev == (void *)bnode->parent_bus);
+
+    if (bdev->configured_for == bnode) {
+        return 0;
+    }
+
+    rc = bdev->dops->configure(bdev, bnode);
+    if (rc) {
+        bdev->configured_for = NULL;
+    } else {
+        bdev->configured_for = bnode;
+    }
+
+    return rc;
+}
+
+void
+bus_node_set_callbacks(struct os_dev *node, struct bus_node_callbacks *cbs)
+{
+    struct bus_node *bnode = (struct bus_node *)node;
+
+    /* This should be done only once so all callbacks should be NULL here */
+    assert(bnode->callbacks.init == NULL);
+    assert(bnode->callbacks.open == NULL);
+    assert(bnode->callbacks.close == NULL);
+
+    bnode->callbacks = *cbs;
+}
+
+int
+bus_dev_init_func(struct os_dev *odev, void *arg)
+{
+    struct bus_dev *bdev = (struct bus_dev *)odev;
+    struct bus_dev_ops *ops = arg;
+
+    BUS_DEBUG_POISON_DEV(bdev);
+
+    bdev->dops = ops;
+    bdev->configured_for = NULL;
+
+    os_mutex_init(&bdev->lock);
+
+    return 0;
+}
+
+int
+bus_node_init_func(struct os_dev *odev, void *arg)
+{
+    struct bus_node *bnode = (struct bus_node *)odev;
+    struct bus_node_cfg *node_cfg = arg;
+    struct os_dev *parent_odev;
+
+    parent_odev = os_dev_lookup(node_cfg->bus_name);
+    if (!parent_odev) {
+        return OS_EINVAL;
+    }
+
+    BUS_DEBUG_POISON_NODE(bnode);
+
+    bnode->parent_bus = (struct bus_dev *)parent_odev;
+
+    odev->od_handlers.od_open = bus_node_open_func;
+    odev->od_handlers.od_close = bus_node_close_func;
+
+    if (bnode->callbacks.init) {
+        bnode->callbacks.init(bnode, arg);
+    }
+
+    return 0;
+}
+
+int
+bus_node_read(struct os_dev *node, uint8_t *buf, uint16_t length,
+              os_time_t timeout, uint16_t flags)
+{
+    struct bus_node *bnode = (struct bus_node *)node;
+    struct bus_dev *bdev = bnode->parent_bus;
+    int rc;
+
+    BUS_DEBUG_VERIFY_DEV(bdev);
+    BUS_DEBUG_VERIFY_NODE(bnode);
+
+    if (!bdev->dops->read) {
+        return SYS_ENOTSUP;
+    }
+
+    rc = bus_dev_lock_by_node(node, timeout);
+    if (rc) {
+        return rc;
+    }
+
+    rc = bus_dev_configure_for(bdev, bnode);
+    if (rc) {
+        goto done;
+    }
+
+    rc = bdev->dops->read(bdev, bnode, buf, length, timeout, flags);
+
+done:
+    rc = bus_dev_unlock_by_node(node);
+    assert(rc == 0);
+
+    return rc;
+}
+
+int
+bus_node_write(struct os_dev *node, uint8_t *buf, uint16_t length,
+               os_time_t timeout, uint16_t flags)
+{
+    struct bus_node *bnode = (struct bus_node *)node;
+    struct bus_dev *bdev = bnode->parent_bus;
+    int rc;
+
+    BUS_DEBUG_VERIFY_DEV(bdev);
+    BUS_DEBUG_VERIFY_NODE(bnode);
+
+    if (!bdev->dops->write) {
+        return SYS_ENOTSUP;
+    }
+
+    rc = bus_dev_lock_by_node(node, timeout);
+    if (rc) {
+        return rc;
+    }
+
+    rc = bus_dev_configure_for(bdev, bnode);
+    if (rc) {
+        goto done;
+    }
+
+    rc = bdev->dops->write(bdev, bnode, buf, length, timeout, flags);
+
+done:
+    rc = bus_dev_unlock_by_node(node);
+    assert(rc == 0);
+
+    return rc;
+}
+
+int
+bus_node_write_read_transact(struct os_dev *node, uint8_t *wbuf,
+                             uint16_t wlength, uint8_t *rbuf, uint16_t rlength,
+                             os_time_t timeout, uint16_t flags)
+{
+    struct bus_node *bnode = (struct bus_node *)node;
+    struct bus_dev *bdev = bnode->parent_bus;
+    int rc;
+
+    BUS_DEBUG_VERIFY_DEV(bdev);
+    BUS_DEBUG_VERIFY_NODE(bnode);
+
+    if (!bdev->dops->write || !bdev->dops->read) {
+        return SYS_ENOTSUP;
+    }
+
+    rc = bus_dev_lock_by_node(node, timeout);
+    if (rc) {
+        return rc;
+    }
+
+    rc = bus_dev_configure_for(bdev, bnode);
+    if (rc) {
+        goto done;
+    }
+
+    /*
+     * XXX we probably should pass flags here but with some of them stripped,
+     * e.g. BUS_F_NOSTOP should not be present here, but since we do not have
+     * too many flags now (like we literally have only one flag) let's just pass
+     * no flags for now
+     */
+    rc = bdev->dops->write(bdev, bnode, wbuf, wlength, timeout, BUS_F_NOSTOP);
+    if (rc) {
+        goto done;
+    }
+
+    rc = bdev->dops->read(bdev, bnode, rbuf, rlength, timeout, flags);
+    if (rc) {
+        goto done;
+    }
+
+done:
+    rc = bus_dev_unlock_by_node(node);
+    assert(rc == 0);
+
+    return rc;
+}
+
+
+int
+bus_dev_lock_by_node(struct os_dev *node, os_time_t timeout)
+{
+    struct bus_node *bnode = (struct bus_node *)node;
+    struct bus_dev *bdev = bnode->parent_bus;
+    os_error_t err;
+
+    BUS_DEBUG_VERIFY_DEV(bdev);
+    BUS_DEBUG_VERIFY_NODE(bnode);
+
+    err = os_mutex_pend(&bdev->lock, timeout);
+    if (err == OS_TIMEOUT) {
+        return SYS_ETIMEOUT;
+    }
+
+    assert(err == OS_OK);
+
+    return 0;
+}
+
+int
+bus_dev_unlock_by_node(struct os_dev *node)
+{
+    struct bus_node *bnode = (struct bus_node *)node;
+    struct bus_dev *bdev = bnode->parent_bus;
+    os_error_t err;
+
+    BUS_DEBUG_VERIFY_DEV(bdev);
+    BUS_DEBUG_VERIFY_NODE(bnode);
+
+    err = os_mutex_release(&bdev->lock);
+    if (err == OS_BAD_MUTEX) {
+        return SYS_EACCES;
+    }
+
+    assert(err == OS_OK);
+
+    return 0;
+}
+
+void
+bus_pkg_init(void)
+{
+
+}
diff --git a/hw/bus/syscfg.yml b/hw/bus/syscfg.yml
new file mode 100644
index 0000000..62653bc
--- /dev/null
+++ b/hw/bus/syscfg.yml
@@ -0,0 +1,32 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+#
+
+syscfg.defs:
+    BUS_DRIVER_PRESENT:
+        description: >
+            This indicated that hw/bus package is present in build.
+            Do not override this settings.
+        value: 1
+
+    BUS_DEBUG_OS_DEV:
+        description: >
+            Enable additional debugging for os_dev objects.
+            Each os_dev object created by bus driver will be poisoned with
+            magic value which is then checked on each operation to ensure
+            proper objects are passed to APIs.
+        value: 0