Maemo 3.x Connectivity Guide

This document is released under GPL license.

Maemo Connectivity Guide

Overview

This guide is written for maemo 3.x software release. This chapter briefly presents the clients and APIs of the maemo connectivity subsystem. Details on interfaces of connectivity are presented in the subsequent chapters.

The maemo Connectivity subsystem is implemented by using known Linux conventions. It resides in the user mode area of Linux and relies on Linux kernel through standard C libraries. Wireless LAN is the main channel to the Internet but dial-up connections through cellular networks are also supported. The only medium to the phone is Bluetooth. The Bluetooth software of maemo is based on BlueZ, which is known as the de-facto implementation of Bluetooth for Linux. D-Bus is used for internal application level message exchange.

Even though the connectivity device drivers are closely related to this subsystem they are considered to be outside of the scope.

Components of the maemo Connectivity Architecture:

maemo connectivity UI - User Interfaces parts of the connectivity. This includes Connection manager, Control panel applets and several different dialogs.

Maemo connectivity daemon (ICd) - LibConIC API works together with ICd which handles all Internet Access Points (IAPs). IC daemon handles both WLAN and Bluetooth connections.

OBEX wrapper - Interface to OBEX services. The primary target user of this library is the OBEX gnome-vfs module.

OpenOBEX - Open source implementation of the Object Exchange (OBEX) protocol. See more information about OpenOBEX from: http://triq.net/obex/

BlueZ Bluetooth stack - The de-facto implementation of Bluetooth for Linux. See more information about BlueZ from: http://www.bluez.org

BlueZ D-Bus API - BlueZ accepts commands via D-Bus.

WLAN connectivity daemon - The daemon which controls WLAN connections.

WLAN device driver - Device driver for Wireless LAN (IEEE 802.11g). Kernel driver is composed of two parts, a binary part (closed source) and an open source wrapper, which binds the binary to current Linux kernel.

Internet Access Points (IAP)

The central concept regarding Internet connections from maemo is the Internet Access Point (IAP). It represents a logical Internet (IP) connection, which the user will define according to her needs. An IAP is local to maemo and has a unique name. It defines the radio bearer (e.g. WLAN, CSD, GPRS) to be applied and usually the data transfer speed, username, password, proxy server, and the corresponding access point in the Internet or the telephone number of the service providers modem, among other characteristics.

Connectivity Subsystem

This section describes the system decomposition of the Connectivity subsystem. Maemo applications can open Internet connections by using the LicConIC API or its socket counterpart IC socket. The Internet Access subsystem will take care of the connection to the phone, if it is necessary, by using the services of the Phone Access subsystem. If an application needs to gain access to the phone's files, then the File selector will consult the Phone Access subsystem.

During the flight mode the WLAN and Bluetooth radios (at least the latter) must not be active. The Device System Management Entity (DSME) of maemo provides information about the transitions to and from the flight mode.

Name Connection Manager
Purpose Provides the UI for managing phone and Internet connections. Available as Control Panel applet and from Status Bar.
Responsibilities and additional requirements
  • Displays active connections with statistics
  • Updates status indicators
  • Provides dialogs for changing and disconnecting connections.
Concurrent usage (Not relevant)


Name Phone Access
Purpose Provides connections to phones with different Bluetooth profiles.
Responsibilities and additional requirements
  • Searches for phones and inquires their services.
  • Keeps phone register.
  • Provides status of the phone connections for Connection Manager
  • Binds RFCOMM devices to DUN and FTP services on the phone.
  • Provides easy access to OpenOBEX.
Concurrent usage Number of clients not limited by maemo. However, some phones may not support more than one Bluetooth profile at a time.
-->


Name Internet Access
Purpose Provides Internet connections over WLAN and cellular systems.
Responsibilities and additional requirements
  • Provides means for configuration and management of IAP settings
  • Provides API for Internet connections both with WLAN and with dial-up/PPP
  • Provides status of Internet connections for Connection Manager
Concurrent usage Number of clients not limited but only one connection to the Internet can exist at a time.



Phone and Internet connections are quite different from their nature and behavior. The next chapters will introduce these some more.

Phone access

Phone Access is the subsystem that handles connections to a mobile phone. It has a search utility for finding potential phones and inquiring the services they can offer. This is based on standard Bluetooth service discovery mechanism. Phone Access also keeps a record of phones having been connected to the device in GConf and provides a list of them for the user to choose from. Phone Access relies on the Linux Bluetooth implementation called BlueZ. BlueZ offers the Berkeley socket interface to the HCI and to the L2CAP protocol for the user space applications.

In principle, any mobile phone supporting Bluetooth Service Discovery Protocol (SDP), Dial-up Networking profile (DUN) and File Transfer Profile (FTP) can be connected to maemo. However, there is variation especially in the level of file transfer services and OBEX in different mobile phones. Some products limit the access to the Inbox (Object Push), whereas more sophisticated ones make the Gallery and the memory card available. The recent products support the OBEX Capability request, which can be used to get more specific information about the file system on the phone.

Maemo connects to a phone on-demand basis, that is, when an application requires a connection. As an example, when the Internet browser is about to open a URL, it will request the Phone Access to establish a connection to the phone. This makes Phone Access to bind an RFCOMM device to the requested service (in this case DUN) on the phone. In a similar fashion the File Selector can set up a file transfer connection to the phone using another RFCOMM device. After binding to a service the application in question can open the local RFCOMM device. Normal file selector access is done with GnomeVFS layer to get transparent access to phone same way as internal flash and MMC are accessed.

The Bluetooth SIM Access (SAP) profile is also needed in maemo to perform WLAN authentication using the EAP-SIM authentication method. In this case the EAP component will ask the BT sap component to get session keys from a GSM/UMTS phone.

Name Phone selection UI
Purpose User interface for managing phone operations. Supports Connection Manager.
Responsibilities and additional requirements
  • Keeps a list of phones
  • Stores the Bluetooth device addresses of phones to GConf
  • Invokes device search and capability query.
Concurrent usage N/A
Name General Bluetooth UI
Purpose User interface for managing all paired BT devices.
Responsibilities and additional requirements
  • Keeps a list of all paired devices (not only phones)
Concurrent usage N/A


Name BT search
Purpose Searches for available Bluetooth devices.
Responsibilities and additional requirements
  • Bluetooth inquiry
  • Checks on flight mode state
Concurrent usage N/A


Name BT service discovery
Purpose Checks if a found Bluetooth device is sufficient.
Responsibilities and additional requirements
  • Bluetooth service discovery (SDP)
Concurrent usage Not limited (but used by Phone selection UI and Phone connection daemon only).


Name GW OBEX library
Purpose Provides access to OpenOBEX library for the File selector (Gnome VFS) on a higher abstraction level than OpenOBEX itself supports.
Responsibilities and additional requirements
  • Handles OBEX requests.
Concurrent usage Not limited


Name BT SAP
Purpose Obtains session keys for EAP-SIM authentication from the phone.
Responsibilities and additional requirements
  • SIM/PIN entry
  • Connects to the phone with SIM Access Profile
  • Delivers the session keys to EAP.
Concurrent usage N/A

Maemo Bluetooth also supports HID (keyboard) and OPP (printer) profiles.

The BlueZ system also exports an D-Bus API which can be employed instead of OSSO Bluetooth tools. See the following reference for further reading on BlueZ D-Bus interface:

Internet access

The Internet Access subsystem manages connections to the Internet over both WLAN and cellular dial-up. It is also responsible for the configuration and management of Internet Access Points. The Internet Access provides applications with TCP/IP connections. They can be established with:

  • WLAN connection to some wireless access point.
  • Bluetooth connection through phone using Point-to-Point Protocol (PPP) and a cellular modem (in the phone).

In the latter case AT commands are applied to establish a PPP link to the cellular modem and the connection to the Internet.

Name IC daemon
Purpose IC daemon establishes Internet connections over WLAN and Bluetooth DUN.
Responsibilities and additional requirements
  • Controls that only one Internet connection (one active IAP) can exist at a time
  • Uses Phone Access for getting a character device for a dial-up connection and WLAN connection daemon for getting a network device and getting the connection authenticated.
  • Starts IP level services like PPP and DHCP
  • Provides statistics about the usage of IAPs to any application.
Concurrent usage Has only one client and limits the connections to one at a time.




Name Internet Connectivity GUI
Purpose This package has the GUI applications for configuring Internet Access Points and WLAN settings. Note that the Connection Manager is a separate application.
Responsibilities and additional requirements
  • Provides the UI for configuring an IAP
  • Provides the UI for WLAN settings
  • Scans for available WLAN networks (on request)
  • Saves IAP and WLAN settings (excluding EAP settings) to GConf
  • Opens up dialogs and displays notifications
Concurrent usage N/A


Name WLAN connection daemon
Purpose Manages WLAN network connections.
Responsibilities and additional requirements
  • Starts WLAN driver with the settings provided; stops WLAN
  • Requests authentication when necessary
  • Relays WLAN IAP settings from IC daemon to WLAN drivers and authentication requests to EAP
  • Relays WLAN events from WLAN drivers
  • Provides WLAN status information
Concurrent usage Not limited.


Name EAP UI
Purpose User interface for EAP authentication
Responsibilities and additional requirements
  • Invokes a dialog for an authentication password
  • Shows authentication status
Concurrent usage N/A


Name EAP
Purpose Provides WLAN security excluding basic WEP settings, which are in Wireless Extensions.
Responsibilities and additional requirements
  • Takes care of the authentication process for the active IAP
  • Delivers progress events to IC daemon
  • As a special case, controls the EAP-SIM authentication using the SIM Access Profile
  • Provides authentication status
Concurrent usage N/A

Internet Connectivity Daemon

This chapter describes how the Internet Connectivity daemon works internally. The following subchapters explain the behavior and the decomposition of this component in detail. It also covers the interfaces that this component realizes.

Decomposition

When the ICd receives a request to activate or deactivate an IAP, ICd will activate the IAP or show an UI requesting the user to choose one if no IAP has been selected as the default. Depending on the type of the IAP, ICd will activate or deactivate either a Bluetooth DUN or a WLAN interface.

ICd tracks the applications requesting IAPs by recording their D-Bus base service names. This allows ICd to detect situations where processes using an IAP has aborted or crashed. ICd also implements an idle timeout mechanism to shut down the active IAP if no packets have been sent in a configurable amount of time.

Maemo version 3.x introduced the automatic connection creation feature in the Internet Connectivity Daemon. In other words, the device will try to connect atomatically to saved IAPs and keep connected as long as possible, unless the idle timeout is set. With this feature, applications like E-mail and RSS reader, for example, will be always up to date. The device will also be always ready for online usage, for example, incoming VoIP call or IM chat. In former versions, the internet connection was automatically closed when there were no more applications using it or when the connection was idle for a given period of time defined by the configuration parameter idle timeout.

When not connected, the device scans for saved IAP's and tries to connect atomatically connect taking into account the value defined by the configuration parameter for search interval, which can be 5, 10, 30 or 60 minutes. All other values will be automatically mapped do "Never". This setup switches off the automatic connection feature. In this case, the device will behave just like the former versions: Connections will be created only when required by applications.

ICd is responsible for connection creation only, being of each application the responsability of keeping its data updated and then providing the always online feature.

While writing an application which makes use of the ICd system, you should keep the folowing points in mind:

  • Your application must always use the existing connection available.
  • As it was done in former versions, if device is not connected but a connection is required by user interaction, your application must require connection creation using LibConIC API.
  • Keep the user aware making visible when your data was last updated.
  • Your application must register via LibConIC and listen to signals emitted by ICd (Connection Created, Lost and Changed), and reacting as follows:
    • Connection Created: Use the connection and update all data.
    • Connection Lost: Go to an idle state silently and wait untill a new connection is created.
    • Connection Changed: Use this new connection.
  • Automatic data updates must run in background and silently:
    • Avoid alarming the user with unnecessary banners or bocking dialogs.
    • Save usernames and passwords so that automatic updates can be done without prompts.
    • In this case, all failures must not show error notification dialogs.
  • The connectivity infrastructure takes care of error situations in a centralized way.

Automatic connection creation feature can also be switched off using flight mode (offline mode). While in this mode, the configuration parameter for allowing WLAN in flight mode is checked. Depending on the state of this configuration parameter, WLAN IAPs are either enabled or disabled in flight mode. Also Bluetooth connections are normally disabled in flight mode.

Bluetooth Dial-up Networking

ICd uses PPP to establish IP connectivity over Bluetooth DUN interfaces. If there already is a different IAP active using Bluetooth DUN, the old IAP is first deactivated. The IAP is activated according the following action sequence:

WLAN

When connecting to a WLAN, ICd needs to associate with the network and enable EAP authentication and the DHCP client as needed. Independently of whether there is an IAP active using WLAN, the requested WLAN network will first be scanned to ensure that it is available. The current IAP will be deactivated if the requested network is found and the current IAP is using WLAN. WLAN is activated according to the following procedure:

  • If the network requires EAP authentication, the EAP authentication procedure is started. While performing the EAP authentication, the EAP software may show GUI dialogs relating to the EAP authentication procedure. When the EAP authentication has been completed, the EAP software sets security keys for the WLAN network, which will result in state change messages from wlancond. ICd will receive these messages but ignore them and wait for the reply from EAP authentication instead. If the EAP authentication fails, ICd aborts with a D-Bus error message.
  • After the EAP process has been started, ICd instructs wlancond to associate with the WLAN network. Any static security settings relating to pre-shared security keys are also supplied at this point. If a connection to the WLAN network cannot be established, ICd aborts with an error.
  • As the DHCP client is a standalone independent program, it is started using exec when the WLAN IAP requires dynamic IP address acquisition. When the DHCP client has obtained an IP address it configures IP related parameters and sends a D-Bus signal to ICd. If the IP address lease cannot be obtained, ICd will timeout, stop the DHCP client and abort with a D-Bus error message.

LibConIC library

The LibConIC library gives access to the maemo connection API.

LibConIC replaces the old maemo InternetConnect API, including tasks like gathering statistics and proxy configurations.

Connection (conicconnection.h)

ConIcConnection class can be used to request for Internet connections. Also it can be used retrieve current statistics, proxies and settings for Internet Access Points (IAPs).

ConIcConnection has these gobject signals:

  • connection-event
  • statistics

The different signals are listed below as function prototypes for informational purposes. They are implemented as gobject signals.

"connection-event" signal

void user_function(ConIcConnection *connection, ConIcConnectionEvent *event, gpointer user_data);

When there's a new connection event for the application (eg. a connection is opened as requested), it sent as connection-event signal. ConIcConnectionEvent contains the status, for example CON_IC_STATUS_CONNECTED or CON_IC_STATUS_DISCONNECTED.

"statistics" signal

void user_function(ConIcConnection *connection, ConIcStatisticsEvent *event, gpointer user_data);

All statistics are sent using this signal as ConIcStatistics event.

Gobject properties

ConIcConnection has one gobject property:

"automatic-connection-events" gboolean : Read / Write

If set to true, application will receive connection-events automatically as connections are established and tore down. Normally events are only sent when applications request for a connection, with this all events are received constantly. This makes it possible, for example, to create an application which executes something from the network every time a connection is established.

Note:
The automatic events are stopped by Internet Connectivity system when con_ic_connection_connect() is called and started again after DISCONNECT event is received.

#include <glib.h>
#include <glib-object.h>
#include <coniciap.h>

Typedefs

typedef _ConIcConnection  ConIcConnection (ConIcConnection object.)

The contents of the object are private, use only the functions provided by conicconnection.h.

Enumerations

enum  ConIcConnectFlags { CON_IC_CONNECT_FLAG_NONE = 0, CON_IC_CONNECT_FLAG_AUTOMATICALLY_TRIGGERED = 1 << 0, CON_IC_CONNECT_FLAG_UNMANAGED = 1 << 1 }

With these flags special connection requests can be made. The flags can be ORred. Normally just use CON_IC_CONNECT_FLAG_NONE.

Enumeration values:
CON_IC_CONNECT_FLAG_NONE  No flags set.
CON_IC_CONNECT_FLAG_AUTOMATICALLY_TRIGGERED  Connection establishment wasn't started by a user action.

Instead it was triggered by a timer, or something similar, event from the application. Using this flags means that if a connection isn't already established, the connection request will fail.

CON_IC_CONNECT_FLAG_UNMANAGED  Process requesting the connection won't be monitored for killing.

Normally if the process died, it will be automatically detached from the connection. If this flag is set and process dies, the connection won't closed automatically.

 Connection request flags.
enum  ConIcProxyMode { CON_IC_PROXY_MODE_NONE, CON_IC_PROXY_MODE_MANUAL, CON_IC_PROXY_MODE_AUTO }
Enumeration values:
CON_IC_PROXY_MODE_NONE  No proxies set.
CON_IC_PROXY_MODE_MANUAL  Manual proxies set.
CON_IC_PROXY_MODE_AUTO  Automatic proxy URL set.
 Proxy modes.
enum  ConIcProxyProtocol {
  CON_IC_PROXY_PROTOCOL_HTTP, CON_IC_PROXY_PROTOCOL_HTTPS, CON_IC_PROXY_PROTOCOL_FTP, CON_IC_PROXY_PROTOCOL_SOCKS,
  CON_IC_PROXY_PROTOCOL_RTSP
}
Enumeration values:
CON_IC_PROXY_PROTOCOL_HTTP  HTTP proxy protocol.
CON_IC_PROXY_PROTOCOL_HTTPS  HTTPS proxy protocol.
CON_IC_PROXY_PROTOCOL_FTP  FTP proxy protocol.
CON_IC_PROXY_PROTOCOL_SOCKS  SOCKS proxy protocol.
CON_IC_PROXY_PROTOCOL_RTSP  RTSP proxy protocol.

Functions

ConIcConnection * con_ic_connection_new (void)
 Creates new ConIcConnection.
gboolean con_ic_connection_connect (ConIcConnection *connection, ConIcConnectFlags flags)
 Request for a connection.
gboolean con_ic_connection_connect_by_id (ConIcConnection *connection, const gchar *id, ConIcConnectFlags flags)
 Request for a connection using the IAP id.
gboolean con_ic_connection_disconnect (ConIcConnection *connection)
 Disconnects all IAPs associated with the connection.
gboolean con_ic_connection_disconnect_by_id (ConIcConnection *connection, const gchar *id)
 Disconnects specific IAP associated with the application.
gboolean con_ic_connection_statistics (ConIcConnection *connection, const gchar *id)
 Requests statistics for a IAP.
ConIcProxyMode con_ic_connection_get_proxy_mode (ConIcConnection *connection)
 Get current proxy mode.
GSList * con_ic_connection_get_proxy_ignore_hosts (ConIcConnection *connection)
 Get a list of hosts to be ignored.
const gchar * con_ic_connection_get_proxy_autoconfig_url (ConIcConnection *connection)
 Get the URL of Auto-Config Proxy.
const gchar * con_ic_connection_get_proxy_host (ConIcConnection *connection, ConIcProxyProtocol protocol)
 Get proxy host.
gint con_ic_connection_get_proxy_port (ConIcConnection *connection, ConIcProxyProtocol protocol)
 Get proxy port.
GSList * con_ic_connection_get_all_iaps (ConIcConnection *connection)
 Get a list of all configured IAPs.
ConIcIap * con_ic_connection_get_iap (ConIcConnection *connection, const gchar *id)
 Retrieve an IAP by id.


gboolean con_ic_connection_connect (  ConIcConnection *  connection, ConIcConnectFlags  flags

Request for a connection.

The Internet Connectivity subsystem will choose the best connection based on user settings and input provided by the user.

Answer is sent using the connection-event signal from a ConIcConnection object. If the connection establishment succeeded, or there was already a connection established, a CON_IC_STATUS_CONNECTED inside ConIcConnectionEvent is sent. If connection establishment failed, a CON_IC_STATUS_DISCONNECTED inside ConIcConnectionEvent is sent and error is set accordingly.

Normally this one should be used.

Parameters:
connection ConIcConnection object.
flags Flags for the request.
Return values:
TRUE If no DBUS errors.
FALSE If a DBUS message couldn't be sent.


gboolean con_ic_connection_connect_by_id (  ConIcConnection *  connection, const gchar *  id, ConIcConnectFlags  flags

Request for a connection using the IAP id.

The Internet Connectivity subsystem will choose the best connection based on user settings and input provided by the user.

Answer is sent using the connection-event signal from a ConIcConnection object. If the connection establishment succeeded, or there was already a connection established, a CON_IC_STATUS_CONNECTED inside ConIcConnectionEvent is sent. If connection establishment failed, a CON_IC_STATUS_DISCONNECTED inside ConIcConnectionEvent is sent and error is set accordingly.

Normally con_ic_connection_connect() should be used. Use this one if you want to use a specific connection.

Parameters:
connection ConIcConnection object.
flags Flags for the request.
id Id of the requested IAP.
Return values:
TRUE If no DBUS errors.
FALSE If a DBUS message couldn't be sent.


gboolean con_ic_connection_disconnect (  ConIcConnection *  connection  ) 

Disconnects all IAPs associated with the connection.

Normally use this one.

Parameters:
connection ConIcConnection object.
Return values:
TRUE If no DBUS errors.
FALSE If a DBUS message couldn't be sent.


gboolean con_ic_connection_disconnect_by_id (  ConIcConnection *  connection, const gchar *  id

Disconnects specific IAP associated with the application.

Normally use con_ic_connection_disconnect().

Parameters:
connection ConIcConnection object.
id Id of the IAP to disconnected.
Return values:
TRUE If no DBUS errors.
FALSE If a DBUS message couldn't be sent.


GSList* con_ic_connection_get_all_iaps (  ConIcConnection *  connection  ) 

Get a list of all configured IAPs.

Parameters:
connection OssoIcConnection object
Returns:
A singly linked list of ConIcIaps or NULL for error. Caller should free all ConIcIaps with g_object_unref() and the list itself with g_slist_free().


ConIcIap* con_ic_connection_get_iap (  ConIcConnection *  connection, const gchar *  id

Retrieve an IAP by id.

Returns:
ConIcIap gobject which caller must free with g_object_unref().


const gchar* con_ic_connection_get_proxy_autoconfig_url (  ConIcConnection *  connection  ) 

Get the URL of Auto-Config Proxy.

It is guaranteed that this function always returns the current and up-to-date settings.

Parameters:
connection OssoIcConnection object
Returns:
URL as a string. NULL, if no URL specified.


const gchar* con_ic_connection_get_proxy_host (  ConIcConnection *  connection, ConIcProxyProtocol  protocol

Get proxy host.

Parameters:
connection OssoIcConnection object
Returns:
hostname of the proxy for the protocol


GSList* con_ic_connection_get_proxy_ignore_hosts (  ConIcConnection *  connection  ) 

Get a list of hosts to be ignored.

Connections to these hosts shouldn't use proxies.

It is guaranteed that this function always returns the current and up-to-date settings.

Parameters:
connection OssoIcConnection object
Returns:
A GSList of strings, which contain the hosts. Free all strings individually, and the list with g_slist_free(). NULL, if no hosts specified.


ConIcProxyMode con_ic_connection_get_proxy_mode (  ConIcConnection *  connection  ) 

Get current proxy mode.

If proxy mode is CON_IC_PROXY_MODE_NONE, do not use proxies at all.

If proxy mode is CON_IC_PROXY_MODE_MANUAL, use proxies and only use these functions to get the proxy settings:

  • con_ic_connection_get_proxy_host()
  • con_ic_connection_get_proxy_port()
  • con_ic_connection_get_proxy_ignore_hosts()

If proxy mode is CON_IC_PROXY_MODE_AUTO, then only use Proxy Auto-config file specified in http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html. To get the URL for the file, use this function:

  • con_ic_connection_get_proxy_autoconfig_url()


gint con_ic_connection_get_proxy_port (  ConIcConnection *  connection, ConIcProxyProtocol  protocol

Get proxy port.

Parameters:
connection OssoIcConnection object
Returns:
port number of the proxy host for the protocol


ConIcConnection* con_ic_connection_new (  void   ) 

Creates new ConIcConnection.

When not needed anymore, release with g_object_unref().

Creating the object does not yet open the connection. Use, for example, osso_ic_connection_connect() to do that.

Returns:
New ConIcConnection object. NULL, if the object creation failed.


gboolean con_ic_connection_statistics (  ConIcConnection *  connection, const gchar *  id

Requests statistics for a IAP.

The answer is sent as ConIcStatistics in statistics signal.

Parameters:
connection ConIcConnection object.
id Id of the IAP
Return values:
TRUE If no DBUS errors.
FALSE If a DBUS message couldn't be sent.

Connection Events (conicconnectionevent.h)

ConIcConnectionEvent class: ConIcConnectionEvent is sent whenever status of a connection changes. It's derived from ConIcEvent class.

#include <glib.h>
#include <glib-object.h>

Typedefs

typedef _ConIcConnectionEvent  ConIcConnectionEvent (ConIcConnectionEvent object.)

The contents of the object are private, to access the object use the functions provided by conicconnectionevent.h.

Enumerations

enum  ConIcConnectionStatus
 IAP connection status.
enum  ConIcConnectionError { CON_IC_CONNECTION_ERROR_NONE, CON_IC_CONNECTION_ERROR_INVALID_IAP, CON_IC_CONNECTION_ERROR_CONNECTION_FAILED, CON_IC_CONNECTION_ERROR_USER_CANCELED }
 Error codes for connection events.

Error codes for connection events are only set in DISCONNECTED events, otherwise set to CON_IC_CONNECTION_ERROR_NONE.

Note:
More error types might be added in the future.
Enumeration values:
CON_IC_CONNECTION_ERROR_NONE  No errors.
CON_IC_CONNECTION_ERROR_INVALID_IAP  Requested IAP was invalid (for example not found or incomplete settings).
CON_IC_CONNECTION_ERROR_CONNECTION_FAILED  Connections establishment failed for unknown reason.
CON_IC_CONNECTION_ERROR_USER_CANCELED  Connections establishment failed because of user cancellation.

Functions

ConIcConnectionStatus con_ic_connection_event_get_status (ConIcConnectionEvent *event)
 Get status from the event.
ConIcConnectionError con_ic_connection_event_get_error (ConIcConnectionEvent *event)
 Get error from the event.


ConIcConnectionError con_ic_connection_event_get_error (  ConIcConnectionEvent *  event  ) 

Get error from the event.

Parameters:
event OssIcConnectionEvent object
Returns:
error


>ConIcConnectionStatus con_ic_connection_event_get_status (  ConIcConnectionEvent *  event  ) 

Get status from the event.

Parameters:
event OssIcConnectionEvent object
Returns:
status

ConIC Event Class (conicevent.h)

ConIcEvent class: ConIcEvent is an abstract class. ConIcConnectionEvent and ConIcStatistcsEvent derive from this class.

#include <glib.h>
#include <glib-object.h>

Typedefs

typedef _ConIcEvent  ConIcEvent (ConIcEvent object.)

The contents of the object are private, to access the object use the functions provided by conicevent.h.

Functions

const gchar * con_ic_event_get_iap_id (ConIcEvent *event)
 Get the id of the IAP this event is associated with.
const gchar * con_ic_event_get_bearer_type (ConIcEvent *event)
 Get bearer type of the IAP this event is associated with.


const gchar* con_ic_event_get_bearer_type (  ConIcEvent *  event  ) 

Get bearer type of the IAP this event is associated with.

Different bearers are listed in coniciap.h.

Parameters:
event OssIcEvent object
Returns:
Bearer of the IAP.


const gchar* con_ic_event_get_iap_id (  ConIcEvent *  event  ) 

Get the id of the IAP this event is associated with.

Parameters:
event OssIcEvent object
Returns:
Id of the IAP.

IAP - Internet Connectivity API (coniciap.h)

ConIcIap class is for access IAPs.

#include <glib.h>
#include <glib-object.h>

Bearers

The bearer types returned by con_ic_iap_get_bearer_type() and con_ic_event_get_bearer_type(). Note: More bearer types might be added in the future.

#define CON_IC_BEARER_WLAN_INFRA   "WLAN_INFRA"
 WLAN infrastructure mode.
#define CON_IC_BEARER_WLAN_ADHOC   "WLAN_ADHOC"
 WLAN Ad-Hoc mode.
#define CON_IC_BEARER_DUN_GSM_CS   "DUN_GSM_CS"
 Bluetooth Dial-up Networking profile using GSM circuit-switched.
#define CON_IC_BEARER_DUN_GSM_PS   "DUN_GSM_PS"
 Bluetooth Dial-up Networking profile using GSM packet-switched.

Typedefs
typedef _ConIcIap ConIcIap
 ConIcConnectionEvent object.

Functions
const gchar * con_ic_iap_get_name (ConIcIap *iap)
 Returns the name of the IAP as string.
const gchar * con_ic_iap_get_id (ConIcIap *iap)
 Returns the ID of the IAP as string.
const gchar * con_ic_iap_get_bearer_type (ConIcIap *iap)
 Returns the bearer type of the IAP as string.

Typedefs

typedef struct _ConIcIap ConIcIap

ConIcConnectionEvent object: The contents of the object are private, to access the object use the functions provided by conicconnectionevent.h.

Functions


const gchar* con_ic_iap_get_bearer_type (  ConIcIap *  iap  ) 

Returns the bearer type of the IAP as string.

Parameters:
iap ConIcIap object
Returns:
Bearer type of the IAP.


const gchar* con_ic_iap_get_id (  ConIcIap * iap  ) 

Returns the ID of the IAP as string.

Parameters:
iap ConIcIap object
Returns:
ID of the IAP.


const gchar* con_ic_iap_get_name (  ConIcIap *  iap  ) 

Returns the name of the IAP as string.

Parameters:
iap ConIcIap object
Returns:
Name of the IAP.

Statistics (conicstatisticsevent.h)

ConIcStatisticsEvent is sent when an application has requested statistics. It's derived from ConIcEvent class.

#include <glib.h>
#include <glib-object.h>

Typedefs

typedef _ConIcStatisticsEvent ConIcStatisticsEvent ConIcStatisticsEvent object. The contents of the object are private, to access the object use the functions provided by conicstatisticsevent.h.

Functions

guint con_ic_statistics_event_get_time_active (ConIcStatisticsEvent *statistics)
 Get the active time from the event.
guint con_ic_statistics_event_get_signal_strength (ConIcStatisticsEvent *statistics)
 Get the signal strength from the event.
guint64 con_ic_statistics_event_get_rx_packets (ConIcStatisticsEvent *statistics)
 Get the number of received packets from the event.
guint64 con_ic_statistics_event_get_tx_packets (ConIcStatisticsEvent *statistics)
 Get the number of transmitted packets from the event.
guint64 con_ic_statistics_event_get_rx_bytes (ConIcStatisticsEvent *statistics)
 Get the received bytes from the event.
guint64 con_ic_statistics_event_get_tx_bytes (ConIcStatisticsEvent *statistics)
 Get the transfered bytes from the event.


guint64 con_ic_statistics_event_get_rx_bytes (  ConIcStatisticsEvent *  statistics  ) 

Get the received bytes from the event.

Parameters:
statistics ConIcStatisticsEvent object.
Returns:
Received bytes.


guint64 con_ic_statistics_event_get_rx_packets (  ConIcStatisticsEvent *  statistics  ) 

Get the number of received packets from the event.

Parameters:
statistics ConIcStatisticsEvent object.
Returns:
Received packets.


guint con_ic_statistics_event_get_signal_strength (  ConIcStatisticsEvent * statistics  ) 

Get the signal strength from the event.

Parameters:
statistics ConIcStatisticsEvent object.
Returns:
Signal strength.


guint con_ic_statistics_event_get_time_active (  ConIcStatisticsEvent *  statistics  ) 

Get the active time from the event.

Parameters:
statistics ConIcStatisticsEvent object.
Returns:
Active time.


guint64 con_ic_statistics_event_get_tx_bytes (  ConIcStatisticsEvent *  statistics  ) 

Get the transfered bytes from the event.

Parameters:
statistics ConIcStatisticsEvent object.
Returns:
Transfered bytes.


guint64 con_ic_statistics_event_get_tx_packets(  ConIcStatisticsEvent *  statistics  ) 

Get the number of transmitted packets from the event.

Parameters:
statistics ConIcStatisticsEvent object.
Returns:
Transmitted packets.

Bluetooth libraries

This chapter explains how maemo Bluetooth libraries work internally. The following subchapters explain the behavior and the decomposition of the Bluetooth library components in detail.

Libgwobex

Libgwobex provides access to libopenobex functionality by providing a helper/wrapper interface to it. Libopenobex is explained in detail in the following chapter.

The interface (doxygen generated documentation) to libgwobex can be found at GW OBEX.

Creating the connection

The connection with libgwobex is established using the gw_obex_setup_dev -function, which setups the connection.

#define OBEX_FTP_UUID \
	"\xF9\xEC\x7B\xC4\x95\x3C\x11\xD2\x98\x4E\x52\x54\x00\xDC\x9E\x09"
#define OBEX_FTP_UUID_LEN 16

GwObex* gw_obex_setup_dev (const gchar * device, const gchar * uuid, gint uuid_len, GMainContext * context, gint * error )
				

The following code snippet illustrates how to open a handle using gw_obex_setup_dev.

if (ctx->rfcomm_dev) {
	if (ctx->use_ftp)
		ctx->obex = gw_obex_setup_dev(ctx->rfcomm_dev,
			OBEX_FTP_UUID, OBEX_FTP_UUID_LEN,
			NULL, &err);
	else
		ctx->obex = gw_obex_setup_dev(ctx->rfcomm_dev, NULL,
			0, NULL, &err);

	if (ctx->obex == NULL)
		printf("OBEX setup failed: %s\n", response_to_string(err));
}
				

In this example ctx->rfcomm_dev points to a string containing the device node name (e.g. /dev/rfcomm0). ctx->use_ftp dictates whether standard folder browsing services should be set up. If use_ftp is untrue then we connect to INBOX.

Closing the connection

For closing a gwobex connection you can use the

void gw_obex_close ( GwObex * ctx )
				    

function. The following code demonstrates this usage.

if (ctx->obex) {
	gw_obex_close(ctx->obex);
	ctx->obex = NULL;
}
				    

If ctx->obex is not NULL we simply pass it as an argument to gw_obex_close().

Using the connection

The libgwobex library provides general file handling functionality including reading directory structure, browsing in different folders, getting files etc.

For reading entries from an opened directory you can use the function


gboolean gw_obex_read_dir (GwObex * ctx, const gchar * dir, gchar ** buf, gint * buf_size, gint * error )
				    

gw_obex_read_dir reads an entry from the selected folder and returns the result in the buf argument given to the function.

gboolean ret;
....
ret = gw_obex_read_dir(ctx->obex, dir, buf, buf_size, err);
				    

This reads an entry from the directory dir (char *) and returns it in buf (char **).

For changing the current directory, use function:

gboolean gw_obex_chdir (GwObex * ctx, const gchar * dir, gint * error )
				    

which changes the directory of the FTP connection. Below is a code example of using this function.

/* Ignore parent dir pointers */
if (g_str_equal(name, ".."))
	return TRUE;

if (!gw_obex_chdir(ctx->obex, name, err)) {
	printf("Could not chdir to %s\n", name);
	return FALSE;
}
				    

To retrieve files over the OBEX connection we can use the gw_obex_get_file function.

gboolean gw_obex_get_file (GwObex * ctx, const gchar * local, const gchar * remote, gint * error)
				    

gw_obex_get_file uses the ctx context for retriving the remote file to local file.

gboolean ret;

ret = gw_obex_get_file(ctx->obex, name, name, err);
				    

There are a lot more what can be done by using libgwobex wrapper directly, for full list of functions and their usage please see the GW OBEX API documentation.

Libopenobex

LibOpenOBEX library implements a generic OBEX Session Protocol. It does not implement the OBEX Application Framework. OBEX is a protocol designed to allow interchanging of data between different kinds of connections (for example Bluetooth, IrDA). Specific information about the OBEX protocol can be found at http://www.irda.org, by selecting the Developer->Specifications category. OBEX is similar to HTTP protocol expect for a few differences:

  • Transports: While HTTP is normally layered above a TCP/IP connection, OBEX is usually transported over IrLAP/IrLMP/Tiny TP (on IrDA) or over Baseband/Link Manager/L2CAP/RFCOMM (on Bluetooth).
  • Binary transmissions: OBEX communicates using binary transmissions as HTTP is transmitted in a human readable XML-based format.
  • Session support: HTTP is stateless while OBEX maintains the connection.

A pretty good overlook of OBEX can be found at http://en.wikipedia.org/wiki/OBEX.

Code examples for libopenobex can be obtained from http://openobex.triq.net/downloads from the example apps package.

Using BlueZ D-Bus API

The BlueZ system exports a D-Bus API which can be employed instead of OSSO Bluetooth tools. Refer to the following documents:

Connectivity UI

UI components

Connectivity UI contains different dialogs and other components used to control the connectivity. The different UI parts are:

  • Connection manager
  • Connectivity dialogs
  • Status bar applets
  • Control panel applet
  • Bluetooth UIs

The connectivity dialogs are invoked by D-Bus method calls so for example ICd is using these D-Bus method calls for showing dialogs when they are needed. Next chapter specifies the D-Bus API of maemo connectivity UI

D-Bus Connectivity UI interface

If we need some information from the user about the IAP we are about to connect we can use the below mentioned ones for this purpose.

Service:           com.nokia.icd_ui
Interfaces:        com.nokia.icd_ui
Object paths:      /com/nokia/icd_ui
			    

The Internet Connectivity UIs implements the following D-Bus API used by ICd and EAP.

Method:            show_conn_dlg
Parameters:        none
Return parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown
Description:       Shows the Connect Dialog where the user can choose an IAP.
			    
Method:            show_disconnect_dlg
Parameters:        none

Return Parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown

Description:       Show the disconnect dialog.
			    
Method:            show_retry_dlg
Parameters:        1. string  Bluetooth address of the device used with SAP
                   2. string  Name of the connection attempt error which
                               selects the retry dialog type.

Return Parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown

Description:       Shows the retry dialog.
			    
Method:            show_change_dlg
Parameters:        1. string  Name of the currently active IAP
                   2. string  Name of the IAP to be activated

Return Parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown

Description:       Shows the Change IAP Dialog
			    
Method:            show_passwd_dlg
Parameters:        1. string  Username supplied by ICd
                   2. string  Password supplied by ICd
                   3. string  Name of the IAP

Return Parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown

Description:       Shows the username/password dialog.
			    

Method:            show_gtc_dlg
Parameters:        1. string  GTC challenge string

Return Parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown

Description:       Show EAP GTC challenge dialog.
			    
Method:            show_mschap_change_dlg
Parameters:        1. string  Supplied username
                   2. string  Old password that is to be changed
                   3. string  Name of the IAP

Return Parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown

Description:       Show EAP MSCHAPv2 change password dialog.
			    
Method:            show_private_key_passwd_dlg
Parameters:        1. uint32  The private key ID

Return Parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown

Description:       Show EAP private key password dialog
			    
Method:            show_server_cert_dlg
Parameters:        1. string  Certificate name
                   2. string  Certificate serial
                   3. boolean  TRUE if certificate is expired, FALSE otherwise
                   4. boolean  TRUE if root CA is unknown or self-signed
                                certificate, FALSE otherwise

Return Parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown

Description:       Show server certificate error and expiration dialogs.
                   If both boolean arguments are false, the error dialog is
                   shown. If either or both boolean arguments are TRUE, the
                   expiration dialog is shown instead.
			    
Method:            strong_bt_req
Parameters:        1. string  Bluetooth address of the device to pair with
                   2. boolean  TRUE if strong authentication enabled, FALSE
                                if strong authentication is disabled

Return Parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown

Description:       Request strong (16 digit) BT PIN dialog for a BT device
                            
Method:            show_sim_pin_dlg
Parameters:        1. string  Bluetooth address of the device used with SAP
                   2. boolean  TRUE if PIN was incorrect and retry dialog
                                should be displayed before asking PIN. FALSE
                                if this is the first PIN request.

Return Parameters: none
Errors:            com.nokia.icd_ui.error.flight_mode:
                   Flight mode enabled, dialog not shown

Description:       Show SIM PIN dialog
                            

Example code for our application to show the connect dialog using show_conn_dlg is following. Please note the usage of macro for doing this.

#include <osso-ic-ui-dbus.h>

...
/* in our code somewhere, where we need the Connect Dialog*/
DBusMessage *uimsg;

/* construct the message for Connect Dialog request*/
uimsg =
  dbus_message_new_method_call(ICD_UI_DBUS_SERVICE,
  		           ICD_UI_DBUS_PATH,
		           ICD_UI_DBUS_INTERFACE,
			 /*macro for show_conn_dlg */
                               ICD_UI_SHOW_CONNDLG_REQ);

/* send the message */
reply =
  dbus_connection_send_with_reply_and_block(connection,
				    uimsg,
				   reply_timeout,
				   &error);

if (reply == NULL) {
    DLOG_ERR("Failed to show connect dialog: %s", uierror.message);
    dbus_error_free(&uierror);
}

dbus_message_unref(uimsg);
dbus_message_unref(reply);
...
                            

The signals emitted from com.nokia.icd_ui interface are listed below.

Signal:        disconnect
Parameters:    1. boolean  TRUE if "disconnect" pressed, FALSE if "cancel"

Description:   Signal emitted from UI when disconnect dialog has been closed.
                            
Signal:        retry
Parameters:    1. string  The IAP that is to be retried
               2. boolean  TRUE if "retry" pressed, FALSE if "cancel"

Description:   Signal emitted from UI when retry dialog has been closed.
                            

Signal:        change
Parameters:    1. string  Old IAP to change from
               2. string  New IAP to change to
               3. boolean  Change to the new IAP If TRUE, keep old if FALSE

Description:   Signal emitted from UI when change connection dialog has
                been closed.
                            
Signal:        passwd
Parameters:    1. string  Username supplied or modified by the user
               2. string  Password supplied or modified by the user
               3. string  IAP name
               4. boolean  TRUE if "ok" pressed, FALSE if "cancel"

Description:   Signal emitted from UI when the username/password dialog has
               been closed
                            

Signal:        gtc_response
Parameters:    1. string  Response to the given challenge or empty string
                           if cancelled
               2. boolean  TRUE if "ok" pressed, FALSE if "cancel"

Description:   Signal emitted from UI when the EAP GTC challenge dialog has
               been closed.
                            
Signal:        mschap_change
Parameters:    1. string  Supplied username
               2. string  The new password or empty string if cancelled
               3. string  IAP name
               4. boolean  TRUE if "ok" pressed, FALSE if "cancel"

Description:   Signal emitted from UI when the MSCHAPv2 password has been
               changed
                            
Signal:        private_key_passwd
Parameters:    1. uint32  The id of the private key
               2. string  Password for the private key or empty string if none
               3. boolean  TRUE if "ok" pressed, FALSE if "cancel"

Description:   Signal emitted from UI when the private key password dialog
               has been closed
                            

Signal:        server_cert
Parameters:    1. boolean  TRUE if strong PIN entered, FALSE if strong
                            PIN dialog was cancelled

Description:   Signal emitted from UI when the server certificate error
               dialog has been closed
                            
Signal:        strong_bt
Parameters:    1. boolean  TRUE if strong PIN entered, FALSE if strong
                            PIN dialog was cancelled

Description:   Signal emitted from UI when the strong (16 digit) BT PIN
               has been entered
                            
Signal:        sim_pin
Parameters:    1. string  SIM PIN code or empty string if  cancelled
               2. boolean  TRUE if "ok" pressed, FALSE if "cancel"

Description:   Signal emitted from UI when the SIM PIN has been entered.
                            

Bluetooth DBUS UI dialogs

  Example of use (command line):
  
  dbus-send --system --print-reply \
    --dest='com.nokia.icd_ui' /com/nokia/bt_ui \
    com.nokia.bt_ui.show_send_file_dlg \
    array:string:file:///home/user/MyDocs/.documents/testing.txt
    
  dbus-send --system --print-reply \
    --dest=com.nokia.bt_ui  /com/nokia/bt_ui 
    com.nokia.bt_ui.show_search_dlg \
    string: string: array:string: boolean:true
*/

#ifndef CONBTDIALOGS_DBUS_H
#define CONBTDIALOGS_DBUS_H

#ifdef __cplusplus
extern "C" {
#endif

/** Conbtdialogs service, resides in system dbus */
#define CONBTDIALOGS_DBUS_SERVICE		"com.nokia.bt_ui"

/** Conbtdialogs interface */
#define CONBTDIALOGS_DBUS_INTERFACE		"com.nokia.bt_ui"
/** Conbtdialogs path */
#define CONBTDIALOGS_DBUS_PATH		        "/com/nokia/bt_ui"

/** 
  Show send file dialog
 
  Arguments:
  
  uris: DBUS_TYPE_ARRAY	        Array of strings representing the URIs of the
                                files to send.
  
  Returns:
  
  DBUS_TYPE_BOOLEAN             TRUE, if dialog was shown succesfully.
 */
#define CONBTDIALOGS_SEND_FILE_REQ		"show_send_file_dlg"

/** 
  File sending result signal
 
  Arguments:
  
  success: DBUS_TYPE_BOOLEAN	TRUE, if all files were sent succesful or
                                FALSE, if error occured or sending was 
                                cancelled.
 */
#define CONBTDIALOGS_SEND_FILE_SIG	        "send_file"

/** 
  Show BT device search dialog
 
  Arguments:
  
  major_class: DBUS_TYPE_STRING To set filtering based on major_class or
                                "". Possible major class values are:
                                
                                "miscellaneous", "computer", "phone", 
                                "access point", "audio/video", 
                                "peripheral", "imaging", "wearable",
                                "toy" and "uncategorized".
  
  minor_class: DBUS_TYPE_STRING To set filtering based on minor_class or "".
                                Possible minor class values are:
                        
                                - Minor classes for "computer": 
                                  "uncategorized", "desktop", "server",
                                  "laptop", "handheld", "palm", "wearable"

                                - Minor classes for "phone": "uncategorized",
                                  "cellular", "cordless", "smart phone",
                                  "modem", "isdn"

  
  service_classes: DBUS_TYPE_ARRAY To set filtering based on service classes.
                                   Supported classes include "positioning", 
                                   "networking", "rendering", "capturing",
                                   "object transfer", "audio", "telephony", 
                                   "information". Can be empty list, when no
                                   service class filtering is performed.
  
  bonding: DBUS_TYPE_STRING     Bonding mode for found and selected device:
                                
                                "require" for requiring a bonding from a
                                selected device (i.e. bond device if it hasn't
                                been bond before). 
                                
                                "force" to always bond (i.e. device will be
                                bond even if bonded before).
                                
                                Any other string will allow to search and
                                select device without bonding it.
                                
  Returns:
  
  DBUS_TYPE_BOOLEAN             TRUE, if dialog was shown succesfully.
 */
#define CONBTDIALOGS_SEARCH_REQ                 "show_search_dlg"

/** 
  Bluetooth search result signal
 
  Arguments:
  
  address: DBUS_TYPE_STRING     Bluetooth address of the selected device or ""
                                if search dialog was cancelled.
  
  name: DBUS_TYPE_STRING        Name of the device.
  
  icon: DBUS_TYPE_STRING        Logical name for the icon describing the 
                                device.
  
  major_class: DBUS_TYPE_STRING Major class of the device.
  
  minor_class: DBUS_TYPE_STRING Minor class of the device.
  
  trusted: DBUS_TYPE_BOOLEAN    If device is marked as trusted device
  
  services: DBUS_TYPE_ARRAY     List of strings describing the service classes
                                and SDP based services provided by the device.
 */
#define CONBTDIALOGS_SEARCH_SIG                 "search_result"
/**
  Bluetooth UI Library for maemo

  Copyright (C) 2006 Nokia. All rights reserved.
  
  This sample demonstrates the use of conbtdialogs API and especially 
  send_file function. Compile the program with conbtdialogs and dbus:
  
  gcc -Wall `pkg-config --libs --cflags dbus-glib-1 conbtdialogs` -o send_file conbtdialogs_send_file.c
  
  Run with list of URLS:
  
  ./send_file file:///home/user/MyDocs/.sounds/Everyday.mp3
*/

#define DBUS_API_SUBJECT_TO_CHANGE

#include <glib.h>
#include <conbtdialogs-dbus.h>

#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>


DBusGConnection *connection = NULL;
GMainLoop *mainloop = NULL;


static gboolean initialize(void)
{
    GError *error = NULL;
    
    g_type_init ();
    
    /* Create main loop */
    mainloop = g_main_loop_new(NULL, TRUE);
    if ( mainloop == NULL ) return FALSE;
    
    /* Create DBUS connection */
    connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
  
    if (connection == NULL )
    {
        g_print ("Error: %s\n", error->message);
        g_clear_error (&error);
        return FALSE;
    }
    
    return TRUE;
}


static gboolean uninitialize(void)
{
    /* Quit main loop and unref it */
    if (mainloop != NULL)
    {
        g_main_loop_quit(mainloop);
        g_main_loop_unref(mainloop);
    }
    
    return TRUE;
}


static DBusHandlerResult file_sent_signal ( DBusConnection *connection,
                                            DBusMessage *message,
                                            void *data )
{
    gboolean success = FALSE;
    
    /* check signal */
    if (!dbus_message_is_signal(message,
                                CONBTDIALOGS_DBUS_INTERFACE,
                                CONBTDIALOGS_SEND_FILE_SIG))
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    
    /* get args */
    if ( !dbus_message_get_args ( message, NULL,
                                  DBUS_TYPE_BOOLEAN, &success,
                                  DBUS_TYPE_INVALID ) )
      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    
    /* print if file sending was success or failure */
    g_print ( "File sending was a " );
    
    if (success) g_print("success\n"); else g_print("failure\n");
    dbus_connection_close(connection);
    uninitialize();
    
    return DBUS_HANDLER_RESULT_HANDLED;
}


gint main(gint argc, gchar **argv)
{
    GError *error = NULL;
    gchar **files = NULL;
    gint idx = 0;
    DBusGProxy *proxy;
    DBusConnection *sys_conn;
    gchar *filter_string = NULL;

    if (argc < 2) return 1;
        
    if (initialize() == FALSE) {
        uninitialize();
        return 1;
    }
    
    /* Copy urls to GLib compatible char array */
    files = g_new0(gchar*, argc);
    
    for (idx = 1; idx < argc; idx++)
        files[idx-1] = g_strdup(argv[idx]);
    
    files[argc-1] = NULL;
    
    /* Open connection for btdialogs service */
    proxy = dbus_g_proxy_new_for_name(connection,
                                      CONBTDIALOGS_DBUS_SERVICE,
                                      CONBTDIALOGS_DBUS_PATH,
                                      CONBTDIALOGS_DBUS_INTERFACE);
    
    /* Send send file request to btdialogs service */
    if (!dbus_g_proxy_call(proxy, CONBTDIALOGS_SEND_FILE_REQ, 
                           &error, 
                           G_TYPE_STRV, files, G_TYPE_INVALID,
                           G_TYPE_INVALID))
    {
        g_print("Error: %s\n", error->message);
        g_clear_error(&error);
        g_strfreev (files);
        g_object_unref(G_OBJECT(proxy));
        uninitialize();
        return 1;
    }
    g_strfreev (files);
    files = NULL;
    
    g_object_unref(G_OBJECT(proxy));
    
    /* Now wait for file sent signal, use low level bindings as glib 
       bindings require signal marshaller registered */
    sys_conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
    g_assert(dbus_connection_add_filter(sys_conn,
                                        file_sent_signal,
                                        NULL,
                                        NULL ));
    filter_string =
       g_strdup_printf ("type='signal',interface='%s'", CONBTDIALOGS_DBUS_INTERFACE);
    
    dbus_bus_add_match(sys_conn, filter_string, NULL);
    dbus_connection_unref(sys_conn);
    
    /* Run mainloop */
    g_main_loop_run(mainloop);
    
    return 0;
}

GPS functionality

The device contains a GPS framework which consists of following parts:

  • gpsd (osso-gpsd package) for connecting to serial port GPS device
  • libgpsmgr for starting and stopping gps daemon and making sure that only one instance of gpsd is running in the system
  • libgpsbt for finding GPS devices and connecting gpsd to correct serial port

Note that gpsd is only running when there is an application that wishes to get position data from GPS device. The libgpsmgr makes sure that even if there is multiple programs running that need location information, only one instance of gpsd is running.

The libgpsbt selects GPS device using this formula:

  • if gpsbt_start() caller supplies BT address, then the library uses it directly
  • if not, then the library selects those BT devices that have positioning bit set (like Nokia LD-3W)
  • if no device with positioning bit is found, then the library uses some magic to select those devices that are likely GPS capable (like Nokia LD-1W, Holux GR-231, Insmat BT-GPS-3244C8 and Tomtom Wireless GPS MkII)

Geotagging example program

Geotagging is method where GPS data is attached to image's exif data. This means that if you take pictures using your digital camera and if your camera clock is set correctly, it is possible to add GPS position information to your pictures.

A geotagging application would consist of two parts:

  • GPS data collector that runs in your device
  • Post-processor which will run in your PC, matching the GPS data with the picture files' timestamps.

Follows the example source code of the GPS data collector, which uses services provided by GPS framework (mainly libgpsbt).

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <errno.h>
#include <pthread.h>
#include <signal.h>

#include "zlib.h"   /* for file compression */
#include "gps.h"    /* for reading GPS data from daemon */

#include "gpsbt.h"  /* GPS BT package */


#define VERSION "1.0"

#define MAX_BUF 255
#define MAX_ERROR_BUF_LEN 255

static int started_from_server = 0;
static int dont_quit = 1;
static int restart = 0;
static int radius = 10;  /* how much (in metres) the user has to move before
			  * we update the coordinate
			  */
static gzFile fp = NULL;  /* output file is compressed to save space */
static unsigned long data_id;
static struct gps_data_t *gpsd;
static gpsbt_t ctx = {0};  /* clearing the context is important! */
static pthread_mutex_t quit_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t query_gpsd_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t query_gpsd_cond = PTHREAD_COND_INITIALIZER;

#define MAX_GPSDEV_LEN 64
static char gpsdev[MAX_GPSDEV_LEN+1] = {0};

/* ----------------------------------------------------------------------- */
static int debug_level;

#ifdef DEBUG
#if (__GNUC__ > 2) && ((__GNUC__ > 3) || (__GNUC_MINOR__ > 2))
#define PDEBUG(fmt...) do {						\
		if (debug_level) {					\
			struct timeval tv;				\
			gettimeofday(&tv, 0);				\
			printf("DEBUG[%d]:%ld.%ld:%s:%s():%d: ",	\
			       getpid(),				\
			       tv.tv_sec, tv.tv_usec,			\
			       __FILE__, __FUNCTION__, __LINE__);	\
			printf(fmt);					\
			fflush(stdout);					\
		}							\
	}while(0)
#else
#define PDEBUG(fmt...) do {						\
		if (debug_level) {					\
			struct timeval tv;				\
			gettimeofday(&tv, 0);				\
			printf("DEBUG[%d]:%ld.%ld:%s:%s():%d: ",	\
			       getpid(),				\
			       tv.tv_sec, tv.tv_usec,			\
			       __FILE__, __FUNCTION__, __LINE__);	\
			printf(##fmt);					\
			fflush(stdout);					\
		}							\
	}while(0)
#endif
#else
#define PDEBUG(fmt...)
#endif



/* ----------------------------------------------------------------------- */
static void usage(char *prg, char *str)
{
	if (str)
		printf(str);

	printf("Usage: %s -f <log file> [-s] [-g <gps device>] [-r <distance>]\n", prg);
	printf("\t-f <log file>, the GPS data is saved in compressed form in this file.\n");
	printf("\t-s the program is started by server\n");
	printf("\t-g <gps device>, the GPS device id i.e., GPS BT address or rfcomm address\n");
	printf("\t-p <BT gps device to pair>, GPS BT address of the device to be paired with\n");
	printf("\t-r <distance>, in metres (not used at the moment)\n");
	printf("\t-D turn debugging on in this program\n");
	printf("\t-F <timeout>, flush gzipped output file after timeout seconds, default is not to flush\n");
	printf("\t-x <gpsmgr library debug level>\n");
	printf("\t-y <gpsbt library debug level>\n");
	printf("\t-z <gpsd debug level>\n");
	printf("\t-b daemonize i.e., run in background\n");
	printf("\t-i <PID file>, save the pid to this file\n");
	printf("\n");
	exit(-1);
}


/* ----------------------------------------------------------------------- */
/* Can be called from main thread or from signal handler thread. Used because
 * it is possible that connection to gpsd is blocked which would mean that
 * main thread is also blocked.
 */
static int quit(void)
{
	int st;

	/* note that the mutex is not unlocked anywhere! */
	st = pthread_mutex_trylock(&quit_mutex);
	if (st != 0) {
		/* already locked, just wait the exit */
		pause(); /* the other thread will do the cleanup */
		exit(0);
	}

	/* tell the beast to stop */
	gps_close(gpsd);

	st = gpsbt_stop(&ctx);
	if (st<0) {
		printf("gpsbt_stop(): error: %s, %d\n", strerror(errno), errno);
	}

	gzclose(fp);
	fp = NULL;

	return 0;
}


/* ----------------------------------------------------------------------- */
void *signal_handler(void *arg)
{
	sigset_t set;
	int sig, st;
	char *pidfile = (char *)arg;
	int dont_quit2 = 2;

	sigemptyset(&set);
	sigaddset(&set, SIGHUP);
	sigaddset(&set, SIGTERM);
	sigaddset(&set, SIGINT);

	if (pidfile) {
		pid_t pid = getpid();
		FILE *pidf;

		pidf = fopen(pidfile, "w");
		if (!pidf) {
			printf("Cannot write pid to pidfile (%s)\n", pidfile);
			perror(pidfile);
			exit(-1);
		}
		fprintf(pidf, "%lu", (unsigned long)pid);
		fclose(pidf);
	}


	/* If we get a second quit signal, then we quit the process because
	 * this condition probably means that the main thread is blocking
	 * and unable to stop.
	 */
	while (dont_quit2) {
		st = sigwait(&set, &sig);
		if (!st) {
			switch (sig) {
			case SIGHUP:
				printf("Restarting\n");
				restart = 1;
				break;
			case SIGTERM:
			case SIGINT:
				printf("Quitting\n");
				dont_quit = 0;
				dont_quit2--;
				break;
			default:
				printf("Got signal %d\n", sig);
				break;
			}
		}
	}

	if (!dont_quit2) {
		quit();
		exit(0);
	}

	pthread_exit(NULL);  /* we should not get here if quit() is called */
}


/* ----------------------------------------------------------------------- */
/* using only n. digits precision i.e., truncate to n digits */
static inline double round2(double val, int digits)
{
	unsigned long val2;
	double val3;
	int i;
	int multiple = 1;

	if (digits>6)
		return val;

	for (i=0; i<digits; i++)
		multiple = multiple * 10;

	val2 = (unsigned long)(val * (double)multiple);
	val3 = (double)val2 / (double)multiple;

#if 0
	printf("val=%f, val2=%ld, val3=%f\n", val, val2, val3);
#endif

	return val3;
}

/* ----------------------------------------------------------------------- */
/* This function is called periodically by gpsd library */
void gps_callback(struct gps_data_t *sentence, char *buf, size_t len, int level)
{
	if (!sentence) {
		PDEBUG("Sentence not set, len=%d\n", len);
		return;
	}

	if (!restart && fp) {

		/* data good for latitude, longitude & altitude */
		/* the libgps tells us if the position has changed */
		if (sentence->status &&

		    sentence->online &&
		    sentence->fix.mode>1 &&
		    (sentence->set & (LATLON_SET | ALTITUDE_SET))) {

			/* We only print data if it has been changed and
			 * if latitude and longitude are real numbers.
			 */
			static double prev_lat = 0.0;
			static double prev_lon = 0.0;
			static double prev_alt = 0.0;

			double lat, lon, alt;
			lat = round2(sentence->fix.latitude,4);
			lon = round2(sentence->fix.longitude,4);
			alt = round(sentence->fix.altitude);

			PDEBUG("recv %d bytes, level=%d, online=%f, status=%s (%d), fix.mode=%d, set=0x%X\n",
			       len, level, sentence->online,
			       sentence->status==0 ? "NO FIX" :
			        (sentence->status==1 ? "FIX" :
				 (sentence->status==2 ? "DGPS FIX" : "<unknown>")),
			       sentence->status,
			       sentence->fix.mode, sentence->set);

			if ((prev_lat != lat ||
			     prev_lon != lon ||
			     prev_alt != alt)) {
				prev_lat = lat;
				prev_lon = lon;
				prev_alt = alt;

				PDEBUG("fix: "

				       "T=%lu "
				       "M=%d "
				       "EPT=%.3f "
				       "LAT=%.6f "
				       "LON=%.6f "
				       "EPH=%.3f "

				       "ALT=%.3f "
				       "EPV=%.3f "
				       "TRK=%.3f "
				       "EPD=%.3f "
				       "SPD=%.3f "
				       "EPS=%.3f "

				       "CLMB=%.3f "
				       "EPC=%.3f "
				       "PTCH=%.3f "
				       "ROLL=%.3f "
				       "DIP=%.3f "
				       "SU=%d "

				       "S=%d "
				       "HS=%c\n"
				       ,
				       (unsigned long)sentence->fix.time,
				       sentence->fix.mode,
				       sentence->fix.ept,
				       sentence->fix.latitude,
				       sentence->fix.longitude,
				       sentence->fix.eph,
				       sentence->fix.altitude,
				       sentence->fix.epv,
				       sentence->fix.track,
				       sentence->fix.epd,
				       sentence->fix.speed,
				       sentence->fix.eps,
				       sentence->fix.climb,
				       sentence->fix.epc,
				       sentence->fix.pitch,
				       sentence->fix.roll,
				       sentence->fix.dip,
				       sentence->satellites_used,
				       sentence->satellites,
				       (sentence->headingStatus==' ' || sentence->headingStatus=='\0') ? '?' : sentence->headingStatus
					);
				
				gzprintf(fp,
					 "T=%lu "

					 "M=%d "
					 "EPT=%.3f "
					 "LAT=%.6f "
					 "LON=%.6f "
					 "EPH=%.3f "
					 "ALT=%.3f "

					 "EPV=%.3f "
					 "TRK=%.3f "
					 "EPD=%.3f "
					 "SPD=%.3f "
					 "EPS=%.3f "
					 "CLMB=%.3f "

					 "EPC=%.3f "
					 "PTCH=%.3f "
					 "ROLL=%.3f "
					 "DIP=%.3f "
					 "SU=%d "
					 "S=%d "

					 "HS=%c\n"
					 ,
					 (unsigned long)sentence->fix.time,
					 sentence->fix.mode,
					 sentence->fix.ept,
					 sentence->fix.latitude,
					 sentence->fix.longitude,
					 sentence->fix.eph,
					 sentence->fix.altitude,
					 sentence->fix.epv,
					 sentence->fix.track,
					 sentence->fix.epd,
					 sentence->fix.speed,
					 sentence->fix.eps,
					 sentence->fix.climb,
					 sentence->fix.epc,
					 sentence->fix.pitch,
					 sentence->fix.roll,
					 sentence->fix.dip,
					 sentence->satellites_used,
					 sentence->satellites,
					 (sentence->headingStatus==' ' || sentence->headingStatus=='\0') ? '?' : sentence->headingStatus
					);
			} else {
				PDEBUG("Same coordinate as before: lat=%f, lon=%f, alt=%f\n", lat, lon, alt);
			}
		} else {
#if 0   /* too much data so do not print */
			PDEBUG("Status (%d), Not online (%f), invalid fix (%d) or not changed (0x%X)\n",
			       sentence->status, sentence->online, sentence->fix.mode, sentence->set);
#else
			;
#endif
		}
	} else {
		PDEBUG("Restarting or fp not set, restart=%d\n", restart);
	}

	/* print data to gui server if it is used */
	if (started_from_server) {
		printf("%lu DATA "

		       "T=%lu "
		       "M=%d "
		       "EPT=%.3f "
		       "LAT=%.6f "
		       "LON=%.6f "
		       "EPH=%.3f "

		       "ALT=%.3f "
		       "EPV=%.3f "
		       "TRK=%.3f "
		       "EPD=%.3f "
		       "SPD=%.3f "
		       "EPS=%.3f "

		       "CLMB=%.3f "
		       "EPC=%.3f "
		       "PTCH=%.3f "
		       "ROLL=%.3f "
		       "DIP=%.3f "
		       "SU=%d "

		       "S=%d "
		       "HS=%c\n"
		       ,
		       data_id++,
		       (unsigned long)sentence->fix.time,
		       sentence->fix.mode,
		       sentence->fix.ept,
		       sentence->fix.latitude,
		       sentence->fix.longitude,
		       sentence->fix.eph,
		       sentence->fix.altitude,
		       sentence->fix.epv,
		       sentence->fix.track,
		       sentence->fix.epd,
		       sentence->fix.speed,
		       sentence->fix.eps,
		       sentence->fix.climb,
		       sentence->fix.epc,
		       sentence->fix.pitch,
		       sentence->fix.roll,
		       sentence->fix.dip,
		       sentence->satellites_used,
		       sentence->satellites,
		       (sentence->headingStatus==' ' || sentence->headingStatus=='\0') ? '?' : sentence->headingStatus
			);
	}
}


/* ----------------------------------------------------------------------- */
static int cmd_error(char *id, char *fmt, char *str)
{
	if (id)
		printf("%s ", id);
	printf("ERROR ");
	printf(fmt, str);
	printf("\n");

	return -1;
}


/* ----------------------------------------------------------------------- */
static int cmd_ok(char *id)
{
	if (id)
		printf("%s ", id);
	printf("OK\n");
	return 0;
}


/* ----------------------------------------------------------------------- */
static inline int cmd_version(char *id)
{
	printf("%s %s\n", id, VERSION);
	return 0;
}


/* ----------------------------------------------------------------------- */
static inline int cmd_start(char *id)
{
	return cmd_error(id, "Not implemented yet: %s", "START");
}


/* ----------------------------------------------------------------------- */
static inline int cmd_stop(char *id)
{
#if 1
	dont_quit = 0;
	return cmd_ok(id);
#else
	return cmd_error(id, "Not implemented yet: %s", "STOP");
#endif
}


/* ----------------------------------------------------------------------- */
static inline int cmd_restart(char *id)
{
#if 1
	restart = 1;
	return cmd_ok(id);
#else
	return cmd_error(id, "Not implemented yet: %s", "RESTART");
#endif
}


/* ----------------------------------------------------------------------- */
static inline int cmd_radius(char *id, char *params)
{
	return cmd_error(id, "Not implemented yet: %s", "RADIUS");
}


/* ----------------------------------------------------------------------- */
static inline int cmd_device(char *id, char *params)
{
#if 1
	strncpy(gpsdev, params, MAX_GPSDEV_LEN);
	restart = 1;
	return cmd_ok(id);
#else
	return cmd_error(id, "Not implemented yet: %s", "GPS");
#endif
}



/* ----------------------------------------------------------------------- */
static int call(char *line)
{
	char *id=NULL, *cmd, *params, *p;

	p = strstr(line, " ");
	if (!p) {
		return cmd_error(line, "Syntax error: %s", line);
	}
	*p = '\0';
	p++;
	id = line;
	cmd = p;
	p = strstr(p, " ");
	if (!p) {
                /* eol */
		int cmd_len = strlen(cmd);
		if (cmd_len > 0)
			cmd[cmd_len-1] = '\0';
		params = NULL;
		
	} else {
		int params_len;
		*p++ = '\0';
		params = p;
		params_len = strlen(params);
		if (params_len > 0)
			params[params_len-1] = '\0';
	}

	PDEBUG("id=%s, cmd=%s, params=%s\n", id, cmd, params==0 ? "<N/A>" : params);

	if (!strcasecmp(cmd, "VERSION")) {
		return cmd_version(id);
	}

	if (!strcasecmp(cmd, "GPS")) {
		return cmd_device(id, params);
	}

	if (!strcasecmp(cmd, "START")) {
		return cmd_start(id);
	}

	if (!strcasecmp(cmd, "STOP")) {
		return cmd_stop(id);
	}

	if (!strcasecmp(cmd, "RESTART")) {
		return cmd_restart(id);
	}

	if (!strcasecmp(cmd, "RADIUS")) {
		return cmd_radius(id, params);
	}

	return cmd_error(id, "Invalid command: %s", cmd);
}


/* ----------------------------------------------------------------------- */
/* Wait data from user interface process */
static void *command_handler(void *arg)
{
	char buf[MAX_BUF+1];
	int idx = 0, st;
	int fd = fileno(stdin);

	while (dont_quit) {
		st = read(fd, &buf[idx], MAX_BUF-idx);
		if (!st) {
			dont_quit = 0; /* quit on eof */
			break;
		}

		PDEBUG("cmd received: \"%s\"\n", &buf[idx]);

		call(&buf[idx]);
		memset(buf, 0, MAX_BUF);
	}

	pthread_exit(NULL);
}


/* ----------------------------------------------------------------------- */
/* Query GPS daemon for coordinate. This is done in separate thread so that
 * main thread is not blocked if gps_query() blocks.
 */
static void *gpsd_query_handler(void *arg)
{
	int st;

	while (dont_quit) {

		/* we wait until the main thread triggers us to do the query */
		st = pthread_cond_wait(&query_gpsd_cond, &query_gpsd_mutex);
		if (st) {
			; /* error, what to do now */
		}

		/* Note that gpsd timeouts the connection after one minute
		 * if there is no traffic to the client i.e., to this process,
		 * so ask position data every 30 seconds.
		 */
		gps_query(gpsd, "p");  /* current position */
	}

	pthread_exit(NULL);
}


/* ----------------------------------------------------------------------- */
static int daemon_init(void)
{
	pid_t pid;
	if ((pid=fork())<0)
		return -1;
	else if (pid!=0)
		exit(0); /* parent exists */

	setsid();
	chdir("/");
	umask(0);
	return(0);
}


/* ----------------------------------------------------------------------- */
int main(int argc, char **argv)
{
	int c;
	char *file = 0;
	int ret = 0, st;
	struct gps_data_t *gps;
	pthread_t th_sig, th_cmd, th_gps, th_ask;
	int radius_arg;
	sigset_t set;
	int daemonize = 0;
	char *pidfile = NULL;
	int do_some_testing = 0;
	unsigned int flush_timeout = 0;
	unsigned long count = 0;

	/* GPS BT vars */
	int gpsbt_debug_level = 0;
	int gpsmgr_debug_level = 0;
	int gpsd_debug_level = 0;
	char errbuf[MAX_ERROR_BUF_LEN+1] = {0};
	char *bda = NULL;

	srand(time(0));
	data_id = (unsigned long)rand();
	debug_level = 0;

	while ((c=getopt(argc, argv, "bDf:F:g:hi:p:r:sx:y:z:T"))>-1) {

		switch (c) {

		case 'b':
			daemonize = 1;
			break;

		case 'D':
			debug_level = 1;
			break;

		case 'f':
			file = optarg;
			break;

		case 'F':
			flush_timeout = atoi(optarg);
			break;

		case 'g':
			strncpy(gpsdev, optarg, MAX_GPSDEV_LEN);
			break;

		case 'i':
			pidfile = optarg;
			break;

		case 'p':
			bda = optarg;
			break;

		case 'r':
			radius_arg = atoi(optarg);
			if (radius_arg!=0)
				radius=radius_arg;
			break;

		case 's':
			started_from_server = 1;
			break;

		case 'x':
			gpsmgr_debug_level = atoi(optarg);
			break;

		case 'y':
			gpsbt_debug_level = atoi(optarg);
			break;

		case 'z':
			gpsd_debug_level = atoi(optarg);
			break;

		case 'T':
			do_some_testing=1;
			break;

		default:
			printf("Invalid option found.\n");
		case 'h':
			usage(argv[0], 0);
			break;
		}
	}

	if (do_some_testing) {
#if 1
		printf("-T parameter not in use at the moment\n");
#else
#define MAX_SENTENCE_LEN 64
#define R 1.23456789 /* some random value */
#define LAT 60.23456789
#define LON 24.23456789
#define ALT 10.23456789
		struct gps_data_t sentence = {0};
		char buf[MAX_SENTENCE_LEN+1] = {0};

		snprintf(buf, MAX_SENTENCE_LEN, "foo bar");

		restart = 0;
		fp = stdout;

		sentence.fix.time = (double)time(0);
		sentence.fix.mode = 2;
		sentence.fix.ept = R;
		sentence.fix.latitude = LAT;
		sentence.fix.longitude = LON;
		sentence.fix.eph = R;
		sentence.fix.altitude = ALT;
		sentence.fix.epv = R;
		sentence.fix.track = R;
		sentence.fix.speed = R;

		sentence.status = 1; /* fix */
		sentence.online = 1;
		sentence.set |= LATLON_SET | ALTITUDE_SET;
	
		gps_callback(&sentence, buf, MAX_SENTENCE_LEN, 1);
		printf("stop\n");
		exit(1);
#endif
	}

	/* Then use the libgpsbt library which starts the gps daemon */
	if (bda) {
		printf("Initiating pairing to %s, restart this program after pairing is finished.\n", bda);
		gpsbt_init_pairing(bda);
		exit(1); /* wait the pairing to finish and then restart this program */
	}

	if (!file) {
		usage(argv[0], "File name must be given.\n");
	}


	if (daemonize)
		daemon_init();

	/* Signal handler checks normal signals so we must block them now */
	sigemptyset(&set);
	sigaddset(&set, SIGHUP);
	sigaddset(&set, SIGTERM);
	sigaddset(&set, SIGINT);
	sigprocmask(SIG_BLOCK, &set, NULL);

	/* Following threads are started:
	 *    - command thread (only if started with -s option)
	 *    - signal handler
	 */
	pthread_create(&th_sig, NULL, signal_handler, (void *)pidfile);
	pthread_create(&th_ask, NULL, gpsd_query_handler, NULL);

	if (started_from_server)
		pthread_create(&th_cmd, NULL, command_handler, NULL);

	if (gpsmgr_debug_level)
		gpsmgr_set_debug_mode(gpsmgr_debug_level);

	/* Note that output file is not closed in restart case because gzip
	 * file will be truncated. Also "a" (=append) flag cannot be used
	 * because it seems to confuse gzip library (gunzip says the file is
	 * corrupted).
	 */
	fp = NULL;

	fp = gzopen(file, "w");
	if (fp == NULL) {
		perror("gzopen");
		exit(-1);
	}

RESTART:
	restart = 0;
	count = 0;

	st = gpsbt_start(gpsdev[0] ? gpsdev : NULL,
			 gpsbt_debug_level,
			 gpsd_debug_level,
			 0, /* port */
			 errbuf, MAX_ERROR_BUF_LEN,
			 0, /* timeout */
			 &ctx);
	if (st<0) {
		printf("gpsbt_start(): error: %s, %d [%s]\n", strerror(errno), errno, errbuf);
		goto OUT;
	}

	/* let the gpsd process start */
	sleep(1);

	gpsd = gps_open("127.0.0.1", DEFAULT_GPSD_PORT);  /* default port is 2947 */

	gps_set_callback(gpsd, gps_callback, &th_gps);

	/* Then just wait the data from gps device */
	while (dont_quit) {

		if (restart) {
			gps_close(gpsd);

			st = gpsbt_stop(&ctx);
			if (st<0) {
				printf("gpsbt_stop(): error: %s, %d\n", strerror(errno), errno);
			}

			goto RESTART;
		}

		if (!dont_quit)
			break;

		/* restart the beast if gpsd stops */
		if (!gpsmgr_is_gpsd_running(&ctx.mgr, NULL, GPSMGR_MODE_JUST_CHECK)) {
			PDEBUG("ERROR, gpsd is not running, restarting\n");
			restart = 1;
			continue;
		}

		/* Flush gzip file if necessary */
		if (flush_timeout && !(time(0) % flush_timeout)) {
			if (fp) {
				st = gzflush(fp, Z_SYNC_FLUSH);
				if (st != Z_OK) {
					int val = 0;
					printf("ERROR: %s\n", gzerror(fp, &val));
				} else {
					PDEBUG("Flushed %s\n", file);
				}
			}
		}

		if (!dont_quit)
			break;

		/* ask current position from gpsd */
		if (!(count % 30)) {
			pthread_cond_signal(&query_gpsd_cond);
		}

		sleep(1);
		count++;
	}

	quit();

OUT:
	exit(ret);
}



Improve this page