Moblin: How to Write a Plugin for Your Driver

77
Article Source Moblin Documentation
July 1, 2009, 12:21 am

You may know there is not a standard driver subsystem for sensor such as HID or input system. Jonathan Cameron [
This e-mail address is being protected from spambots. You need JavaScript enabled to view it
] is fighting for checking his iio(industry I/O) into upstream, which is a driver system for sensor. I don’t know the date when kernel accepts it or when sensor driver writers will follow it, therefor our framework has to work with various drivers for a long time. This is why we need a driver plugin system.

Current pulgin system code is only an inital version or in other words a prototype. However,
the basic skeleton is ready, you may extend it for your business. Patches are always weclome.

Write a module is very simple in sensor framework, only two functions is required.

 

gint sf__init (SFModule* m);
void sf__exit (SFModule* m);

 

#SFModule struct defines fields required by sensor daemon, generally you should not directly
touch it. Anyway, some fields may contain useful info for your module, pls treate them as readonly.
see below:

 

/**
* SFModule:
* @name: name of module. It’s group name in module.conf.
* For example, in sample module.conf there is a [module-als-simulator]
* then module-als-simulator is treated as module name.
* @type: type of module. see #ModuleType
* @userdata: location for module save its private data. You can directly
* evaluate it.
* @gmodule: #GModule struct. Don’t touch it
* @core: #SFCore. Don’t touch it
* @keyfile: #GKeyFile contains parameters of module. Pls use key file function
* set of glib to get your parameter and don’t free it.
* @init: pointer to sf_init() of your module. Don’t touch
* @exit: pointer to sf_exit() of your module. Don’t touch
*/
typedef struct _SFModule {
    gchar* name;
    ModuleType type;
    gpointer userdata;
    GModule* gmodule;
    SFCore* core;
    guint index;
    GKeyFile* keyfile;
  
    SFModuleInit init;
    SFModuleExit exit;
} SFModule;

All works of initailization should be finished in sf__init() and return 0 to indicate a success or -1 to indicate a fail, sf__init() is responsible for freeing resource allocated by itself during initial period in its error handle. sf__exit() is called when module unloads, all resources allocated by module should be freed here.

 

Current module is called *driver moudle*, that means it should represent a sensor driver or represent a sensor in other words. Functional module will be introduced later if there is a real need. You may create a struct of you sensor, for example:

 

struct my_accelerometer {
      SFSensorDev parent;
¬†¬†¬†¬† ………..
};

 

#SFSensorDev is a parent object for all sensor structs, it must be always put as first element.
sf_sensordev_new () is the function that allocated memory and initialize #SFSensorDev for your struct, see
below:

 

/**
* sf_sensordev_new:
* @c: A #SFCore object.
* @type: #SFSensorType indicates type of sensor
* @ops: A set of functions that provided by module for
* management of framework. see #DevOps
* @size: size of #SFSensorDev to create
*
* Create a new #SFSensorDev struct. The size is specified to allow creating
* structure derived from #SFSensorDev that contain additional data The size
* passed in must be at least sizeof (SFSensorDev);
*
* Returns: A newly-created #SFSensorDev
*/                        
SFSensorDev* sf_sensordev_new (SFCore* c, SFSensorType type, DevOps* ops, guint size);

 

for @c, you can find #SFCore from #SFModule->core. #SFType defines as below:

 

typedef enum {s
    TYPE_INVAILD = -1,
    TYPE_MIN,
    TYPE_ACCEL = 1,
    TYPE_GRYO = 1 << 1,
    TYPE_THERMAL = 1 << 2, 
    TYPE_COMAPSS = 1 << 3,
    TYPE_ALS = 1 << 4,     
    TYPE_MAX,
} SFSensorType;

 

#DevOps only has two functions now:

 

struct _DevOps {
    gint (*dev_set_active) (SFSensorDev* se, DevActiveLevel level);
    void (*dev_msg_process) (SFSensorDev* se, gint code, gpointer data, guint datalen, gpointer ret_val);
};

 

dev_set_active() is used to enable/disable sensor. The action of enable/disable is module specific that I don’t care. Pls return 0 to indicate a success or -1 when an error happen, however, this function should not be failed. #DevActiveLevel defines as followed:

 

/**
* DevActiveLevel:
* @DEV_STOP: Stop to inject data/event
* @DEV_ACTIVE: Begin to inject data/event
*/
typedef enum {
    DEV_STOP,
    DEV_ACTIVE,
} DevActiveLevel;

 

Note that dev_set_active() is a mandatory requirment so pls always provide it since you may implement as a void function.

dev_msg_process() is a part of module communication mechanism. Framework implements a communication channel based on asynchronous queues of glib.

 

/*                                                      
     * dev_msg_process:                                     
     * @se: A #SFSensorDev object                           
     * @code: option code indicates what action should be executed.
     * @data: data passed in by sender                      
     * @datalen: length of data                             
     * @ret_val: a location to store return value. Note that memory
     * should be allocated by sender, so memcpy() returns to @ret_val
     * is ok.
     *
     * Handle message from framework or other modules.
     *
     * <note><para>
     * Sender may call sf_asyncmsg_send_sync() to send a message and block
     * until a signal of completion is received. This usually happens when
¬†¬†¬†¬† * sender waits for a return value. So don’t sleep in this function
     * otherwise whole framework may be blocked.
     * </para></note>
     */
    void (*dev_msg_process) (SFSensorDev* se, gint code, gpointer data, guint datalen, gpointer ret_val);

Here opiton code is defined in sensor.h as:

 

 

typedef enum {
    MSG_EXIT_THREAD,
    MSG_STREAM_REGISTER,
    MSG_STREAM_UNREGISTERED,

    /* ALS message */
    MSG_ALS_SET_RANGE,
    MSG_ALS_GET_RANGE,
} DevMsgCode;

 

To add your specific code, you have to extend this enumeration. Note that there are some messages for framework that module should not care about. Due to message queue will block when there is nothing to receive, it must be run in a individual thread. To simplify work, sf_sensordev_run_msg_process() will be behalf in you to create thread.

 

/**
* sf_sensordev_run_msg_process:
* @se: A #SFSensorDev object
*
* Create a thread to handle message
*/      
void sf_sensordev_run_msg_process (SFSensorDev* se);

 

this function should be called in sf__init() since your dev_msg_process () may be NULL. As mentioned before, there are some messages handled by framework itself, so whatever you should call sf_sensordev_run_msg_process() in your sf__init().

To stop message process, call sf_sensordev_stop_msg_process().

 

/**
* sf_sensordev_stop_msg_process:
* @se: A #SFSensorDev object
*
* Stop message process and reclaim thread.
*/
void sf_sensordev_stop_msg_process (SFSensorDev* se);

 

You may create the thread by youself if there are some stuff in your module need be handled in a single thread. In this case, you should directly invoke sf_sensordev_process_msg() to deal with message.

 

/**
* sf_sensordev_process_msg:
* @se: A #SFSensorDev object
* @timeout: period of time out. A -1 indicates to block until message
* become available.
*
* Returns: 0 indicates a MSG_EXIT_THREAD is gotten, that means you should
* end your thread. 1 means to go on handling message.
*
*/
gint sf_sensordev_process_msg (SFSensorDev* se, glong timeout);

 

Please refer to module-accel-simulator.c to see how to create a thread by yourself to process message.

To send a message to module, call sf_asyncmsg_send() or sf_asyncmsg_send_sync(). see defintions:

 

/**
* sf_asyncmsg_send:
* @q: A #GAsyncQueue object
* @code: A option code
* @data: data pass to receiver
* @len: length of data
*
* Send a message to owner of @q
*
* <note><para>
* This function send message in asynchronous mode.
* sf_asyncmsg_send_sync() is the synchronous version.
* </para></note>
*/
void sf_asyncmsg_send (GAsyncQueue* q, gint code, gpointer data, guint len);

/**
* sf_asyncmsg_send_sync:
* @q: A #GAsyncQueue object
* @code: A option code
* @data: data pass to receiver
* @len: length of data
* @ret_val: Location to store return value. Sender is responsible for
* allocating and freeing memory.
*
* Send message in synchronous mode and bring return value back to
* sender if there is any.
*/
void sf_asyncmsg_send_sync (GAsyncQueue* q, gint code, gpointer data,
                            guint len, gpointer ret_val);

For #SFSensorDev, the #GAsyncQueue is #SFSensorDev->inq.

 

The last thing, framework uses a auto-produced header file to resolve name conflict from sf__init()/sf__exit(). Add your header name in src/Makefile.am as below:

 

SYMDEF_FILES =
               modules/module-accel-simulator-symdef.h>-
               modules/module-als-simulator-symdef.h

 

Ok, till now you should have an overview of how to write a driver module. Some details are not
showed here, for example, the helper of polling data. As a chinese, writing in English is always
a big challenge. Anyway, refer to sample code is the fastest way to learn details. Please see
module-accel-simulator.c and module-als-simulator.c. And, patches are always welcome.