Jump to main content | Jump to Primary Navigation | Jump to Sub Navigation


 

Drivers 2: A more complex device driver

Previous tutorial Next tutorial

Before reading this tutorial you should have covered Drivers 1: A new device driver.

Contents

  1. Introduction
  2. Private data
  3. Allocation function
  4. Initialization function
  5. Shutdown function
  6. Free memory function
  7. New boilerplate
  8. Bringing it all together

Introduction

In addition to the polling function specified in the previous tutorial, DevBot drivers may have initialization and shutdown functions, allocate memory and free memory functions, and private data.

In this tutorial we introduce a benchmark driver which creates integer arrays of a specified length, and in the polling function iterates through the array incrementing the value at each index. The benchmark driver's properties and behaviour is outlined in the Benchmark Driver entry in the driver database.

Private data

Private data for a driver may contain driver properties as well as other driver-specific values which need passing between the various driver functions. For example, the private data might have its memory allocated in the allocation function and its mandatory properties set in the initialization function. The private data might then be used in the polling function, then have its memory freed in the free function.

The private data for the benchmark driver the private data is called BenchmarkData and consists of 3 fields:

The benchmark driver allocates BenchmarkData memory in the allocate_device(), then sets up the fields in initialize_device(), allocating memory to the array buffer in the process. Interestingly the array property is dynamically created in initialize_device() rather than a statically defined property table, as the length of the property is itself a property. All three fields of BenchmarkData are used in poll_device() to copy the array property to the local array buffer, iterate through the array and copy the result back to the array property. The array buffer has its memory freed in shutdown_device() and BenchmarkData is freed in free_device().

typedef struct {
    BOT_TYPE_UINT_TYPE *buffer;
    uint32_t data_length;
    BotProperty *data;
} BenchmarkData;

Allocation function

This is used to allocate memory to the private data. Once the memory has been allocated, the function bot_device_set_data() is called to save the data to the device the function was called with.

The benchmark driver uses allocate_device() to allocate space for BenchmarkData.

static bool allocate_device(BotDevice *device)
{
    /* Allocate space for the private data */
    bot_device_set_data(device, g_new0(BenchmarkData, 1));
    return TRUE;
}

Initialization function

This is used to configure the properties with any initial values. If mandatory properties are not given values here, then an error is returned.

In the benchmark driver the mandatory length property is queried and used to dynamically create an array property of that length. If the mandatory length property was not specified by the device, then an error would be returned at this point. Finally the array buffer is allocated appropriate memory.

static bool initialize_device(BotDevice *device)
{
    BenchmarkData *benchmark_data;
    uint32_t length;

    /* Validate the requested length of Data */
    length = bot_property_get_uint(
        bot_device_lookup_property(device, "DataLength"));

    if (length < 1) {
        bot_warn("Invalid length (%d) for data", length);
        return FALSE;
    }

Shutdown function

The shutdown function exists to undo anything initialize_device() has done. For example if the device has opened a USB connection in initialization, then shutdown_device() would close that connection.

static void shutdown_device(BotDevice *device)
{
    /* Free the data buffer associated with this device */
    BenchmarkData *benchmark_data = bot_device_get_data(device);
    if (benchmark_data->buffer)
        g_free(benchmark_data->buffer);
}

Free memory function

This function is used to release the private data memory and any other memory assigned in allocate_device().

The benchmark driver uses free_device() to free the BenchmarkData memory which was assigned in the allocation function.

static void free_device(BotDevice *device)
{
    /* Get the private data associated with this device, and free it */
    BenchmarkData *benchmark_data = bot_device_get_data(device);
    g_free(benchmark_data);
}

New boilerplate

To ensure all the new methods of this driver are called, the boilerplate needs to be extended as below.

static const BotDriver boilerplate = {
    .name               = "Benchmark",
    .description        = "Driver used to create basic system benchmarks",
    .version            = "1.0",
    .allocate_device    = allocate_device,
    .initialize_device  = initialize_device,
    .shutdown_device    = shutdown_device,
    .free_device        = free_device,
    .poll_device        = poll_device,
    .driver_properties  = driver_properties
};

Bringing it all together

Bring all of the above together yields benchmark.c

/******************************************************************************
 * Benchmark driver used to generate basic performance statistics.
 * (c) Edinburgh Robotics 2007
 *
 * This driver also demonstrates many features of the API for driver authors.
 ******************************************************************************/

#include <devbot/driver/property.h>
#include <devbot/driver/driver.h>
#include <devbot/driver/device.h>

#include <devbot/type.h>
#include <devbot/bot.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <glib.h>

/* Private data */
typedef struct {
    BOT_TYPE_UINT_TYPE *buffer;
    uint32_t data_length;
    BotProperty *data;
} BenchmarkData;

static const BotDeviceProto driver_properties[] = {
    {
        .name = "DataLength",
        .description = "The length of the \"Data\" property "
                       "that will be created.",
        .type = BOT_TYPE_UINT_NAME,
        .flags = BOT_NO_DEFAULT,
    },
    END_PROPERTY_PROTO
};

static bool poll_device(BotDevice *device)
{
    BenchmarkData *benchmark_data = bot_device_get_data(device);
    uint32_t i;

    /* Update the data vector */
    bot_property_get_uint_all(benchmark_data->data,
                              benchmark_data->buffer);
    for (i = 0; i < benchmark_data->data_length; i++)
        benchmark_data->buffer[i]++;
    bot_property_set_uint_all(benchmark_data->data,
                              benchmark_data->buffer);
    return TRUE;
}

static bool allocate_device(BotDevice *device)
{
    /* Allocate space for the private data */
    bot_device_set_data(device, g_new0(BenchmarkData, 1));
    return TRUE;
}

static bool initialize_device(BotDevice *device)
{
    BenchmarkData *benchmark_data;
    uint32_t length;

    /* Validate the requested length of Data */
    length = bot_property_get_uint(
        bot_device_lookup_property(device, "DataLength"));

    if (length < 1) {
        bot_warn("Invalid length (%d) for data", length);
        return FALSE;
    }

    /* Get the private data associated with this device */
    benchmark_data = bot_device_get_data(device);

    /* Store our setup values for use later */
    benchmark_data->data_length = length;

    /* Dynamically create the Data property */
    const BotDeviceProto data_proto = {
        .name        = "Data",
        .description = "Dynamically created data array",
        .type        = BOT_TYPE_UINT_NAME,
        .length      = length,
    };
    benchmark_data->data =
        bot_device_add_property(device, &data_proto);

    /* Create a buffer that will be used to manipulate the data */
    benchmark_data->buffer =
        g_new0(uint32_t, benchmark_data->data_length);

    return TRUE;
}

static void shutdown_device(BotDevice *device)
{
    /* Free the data buffer associated with this device */
    BenchmarkData *benchmark_data = bot_device_get_data(device);
    if (benchmark_data->buffer)
        g_free(benchmark_data->buffer);
}

static void free_device(BotDevice *device)
{
    /* Get the private data associated with this device, and free it */
    BenchmarkData *benchmark_data = bot_device_get_data(device);
    g_free(benchmark_data);
}

static const BotDriver boilerplate = {
    .name               = "Benchmark",
    .description        = "Driver used to create basic system benchmarks",
    .version            = "1.0",
    .allocate_device    = allocate_device,
    .initialize_device  = initialize_device,
    .shutdown_device    = shutdown_device,
    .free_device        = free_device,
    .poll_device        = poll_device,
    .driver_properties  = driver_properties
};

BOT_DRIVER_BOILERPLATE(boilerplate);

Previous tutorial Next tutorial